, type = require('Y/type')
;
+
+
function reduce(o, fn, acc){
if ( !o )
return acc;
return o[key];
}
+/**
+ * Copies all properties from all arguments after the first onto the first.
+ * Accepts any number of arguments greater than one.
+ *
+ * @param {Object} target Target object for the copied properties.
+ * @param {Object...} donors Donor object(s).
+ * @return {Object} A
+ *
+ * nb. "Properties" are only value descriptors. To copy accessor descriptors, use
+ * `core.accessors(A,B)`. To copy everything, use `core.descriptors(A,B)`.
+ *
+ */
function extend( A, B ){
var args = slice.call(arguments,1);
if ( type.isArray(A) )
return A.concat.apply(A, args);
else
- return args.reduce(extendall, A);
+ return args.reduce(_extend, A);
}
-function extendall(A, donor){ return reduce(donor, attrvk, A); }
-function attrvk(o, v, k){ return attr(o, k, v, o[k]); }
+function _extend(A, donor){ return reduce(donor, attrvk, A); }
+function attrvk(o, v, k){ return attr(o, k, v, o[k]); }
/**
* Fetches the descriptor for each property that is an accessor (getter/setter).
- * @param {Object} o Object for inspection.
- * @return {Object} A map of property to descriptor.
- * nb. An Array is safer, but:
- * 1) Object.defineProperties takes an Object
- * 2) That'd be much worse to work with
+ * @param {Object} target Object for inspection.
+ * @return {Object} Map from property to accessor descriptor.
*/
-function accessors(o){
- if ( !(o && typeof o === "object") )
- return {};
- return reduce(o, function(acc, v, k, obj){
-
- // Ignore inherited properties
- if ( !hasOwn.call(obj,k) )
- return acc;
-
- // Only yield accessors
- var desc = getDesc(obj,k);
- if ( !hasOwn.call(desc,'value') )
- acc[k] = desc;
-
+/**
+ * Copies descriptors from all arguments onto the first.
+ * Accepts any number of arguments greater than one.
+ *
+ * @param {Object} target Target object for the copied accessors.
+ * @param {Object...} donors Donor object(s).
+ * @return {Object} A.
+ */
+function accessors(A, B){
+ if ( !(A && typeof A === "object") )
+ return A;
+
+ if (arguments.length < 2)
+ return reduce(A, _getAccessors, {});
+
+ var desc = slice.call(arguments,1).reduce(_copyAccessors, {});
+ Object.defineProperties(A, desc);
+ return A;
+}
+function _getAccessors(acc, v, k, obj){
+
+ // Ignore inherited properties
+ if ( !hasOwn.call(obj,k) )
return acc;
- }, {});
+
+ // Only yield accessors
+ var desc = getDesc(obj,k);
+ if ( !hasOwn.call(desc,'value') )
+ acc[k] = desc;
+
+ return acc;
+}
+function _copyAccessors(desc, donor){
+ return reduce(donor, _getAccessors, desc);
}
+/**
+ * Fetches the descriptor for each own property of the object.
+ *
+ * @param {Object} target Object for inspection.
+ * @return {Object} Map from property to descriptor.
+ */
+/**
+ * Copies descriptors from all arguments onto the first.
+ * Accepts any number of arguments greater than one.
+ *
+ * @param {Object} target Target object for the copied accessors.
+ * @param {Object...} donors Donor object.
+ * @return {Object} A.
+ */
+function descriptors(A, B){
+ if ( !(A && typeof A === "object") )
+ return A;
+
+ if (arguments.length < 2)
+ return reduce(A, _getDescriptors, {});
+
+ var desc = slice.call(arguments,1).reduce(_copyDescriptors, {});
+ Object.defineProperties(A, desc);
+ return A;
+}
+function _getDescriptors(acc, v, k, obj){
+
+ // Ignore inherited properties
+ if ( !hasOwn.call(obj,k) )
+ return acc;
+
+ acc[k] = getDesc(obj,k);
+ return acc;
+}
+function _copyDescriptors(desc, donor){
+ return reduce(donor, _getDescriptors, desc);
+}
+
+
+
+exports['slice'] = slice;
+
+exports['reduce'] = reduce;
+exports['map'] = map;
+exports['forEach'] = forEach;
+exports['filter'] = filter;
-exports['reduce'] = reduce;
-exports['map'] = map;
-exports['forEach'] = forEach;
-exports['filter'] = filter;
+exports['set'] = set;
+exports['attr'] = attr;
+exports['extend'] = extend;
-exports['set'] = set;
-exports['attr'] = attr;
-exports['extend'] = extend;
+exports['accessors'] = accessors;
+exports['descriptors'] = descriptors;
-exports['accessors'] = accessors;
-exports['slice'] = slice;
\ No newline at end of file
function extendall(A, donor){ return reduce(donor, attrvk, A); }
function attrvk(o, v, k){ return attr(o, k, v, o[k]); }
-function accessors(o){
- if ( !(o && typeof o === "object") )
- return {};
+function accessors(A, B){
+ if ( !(A && typeof A === "object") )
+ return A;
- if ( notWrapped(o.accessors) )
- return o.accessors.apply(o, slice.call(arguments,1));
+ if ( notWrapped(A.accessors) )
+ return A.accessors.apply(A, slice.call(arguments,1));
- return core.accessors(o);
+ return core.accessors.apply(this, arguments);
}
-exports['reduce'] = reduce;
-exports['map'] = map;
-exports['forEach'] = forEach;
-exports['filter'] = filter;
+function descriptors(A,B){
+ if ( !(A && typeof A === "object") )
+ return A;
+
+ if ( notWrapped(A.descriptors) )
+ return A.descriptors.apply(A, slice.call(arguments,1));
+
+ return core.descriptors.apply(this, arguments);
+}
-exports['set'] = set;
-exports['attr'] = attr;
-exports['extend'] = extend;
+exports['reduce'] = reduce;
+exports['map'] = map;
+exports['forEach'] = forEach;
+exports['filter'] = filter;
-exports['accessors'] = accessors;
+exports['set'] = set;
+exports['attr'] = attr;
+exports['extend'] = extend;
+exports['accessors'] = accessors;
+exports['descriptors'] = descriptors;
--- /dev/null
+var
+Blank =
+exports['Blank'] =
+function Blank(v){ this.value = v; };
+
+exports['_'] = _ =
+exports['BLANK'] = BLANK = new Blank();
+
+exports['break_'] =
+function break_(v){ return new Blank(v); };
+
return ( L ? A[L-1] : undefined );
};
+ this['clear'] =
+ function clear(){
+ var A = this._o;
+ while (A.length) A.pop();
+ return this;
+ };
+
function unwrapY(o){
return (o instanceof YArray ? o.end() : o);
}
--- /dev/null
+var YCollection = require('Y/types/collection').YCollection
+, YFunction = require('Y/types/function').YFunction
+, core = require('Y/core')
+, del = require('Y/delegate')
+, op = require('Y/op')
+, type = require('Y/type')
+;
+
-/**
- * A generic collection (which also provides the majority of core functionality for YObject).
- * Specializations are provided by subclasses. All subclasses must implement reduce().
- */
-
var YBase = require('Y/class').YBase
// , type = require('Y/type')
, core = require('Y/core')
, del = require('Y/delegate')
, op = require('Y/op')
, slice = core.slice
+
+, internal = require('Y/internal')
;
return this;
}
+/**
+ * A generic collection (which also provides the majority of core functionality for YObject).
+ * Specializations are provided by subclasses.
+ *
+ * All subclasses must implement `reduce()`.
+ *
+ * There are several optional core methods for customization and optimization. By default:
+ * - `attr()` calls `Y.delegate.attr()` on the wrapped object, `this._o`. If the result is not the
+ * wrapped object, it is returned, otherwise `this` is returned.
+ * - `clone()` invokes the constructor with nothing, and then calls `extend()` on the new object passing itself.
+ * - `end()` returns this._o and does nothing else.
+ * - `remove()` must iterate the whole collection to ensure all values are removed.
+ * - `find()` is a variant of reduce that uses the break-wrapper `Y.internal.break_()`, as a sentinel to stop iteration.
+ */
+var YCollection =
exports['YCollection'] =
YBase.subclass('YCollection', {
- 'init' : function(o){
+ 'init' : function initYCollection(o){
this._o = o || {};
},
- 'attr' : function attr(k, v, def){
+ 'end' : function end(){ return this._o; },
+
+ 'clone' : function clone(){
+ return new this.constructor().extend(this);
+ },
+
+ 'attr' : function attr(k, v, def){
var r = del.attr(this._o, k, v, def);
if (r === this._o)
return this;
else
return r;
},
- 'extend' : extendY,
- 'merge' : extendY,
- 'concat' : extendY,
- 'end' : function end(){ return this._o; },
+ /**
+ * Removes all instances of the given values from the collection.
+ *
+ * @param {Any...} value Value(s) to be removed from the collection.
+ * @return {this}
+ */
+ 'remove' : function remove(v){
+ var o = this._o
+ , values = slice.call(arguments,0);
+ this.forEach(function(v, k){
+ if ( values.indexOf(v) !== -1 )
+ delete o[k];
+ });
+ return this;
+ },
+
'reduce' : function reduce( fn, acc, context ){
- var o = this._o || this,
- acc = acc || new o.constructor();
- for ( var name in o )
- acc = fn.call( context || this, acc, o[name], name, o );
- return acc;
+ throw new UnimplementedError("Reduce is unimplemented!");
},
'map' : function map( fn ){
- var o = this._o
- , cxt = arguments[1] || this
- , acc = new o.constructor()
- ;
- for ( var name in o )
- acc[name] = fn.call(cxt, o[name], name, o);
- return acc;
+ 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);
+ }, new this.__class__() );
},
'forEach' : function forEach( fn, context ){
- var o = this._o, cxt = arguments[1] || this;
- for ( var name in o )
- fn.call(cxt, o[name], name, o);
+ this.reduce(function(cxt, v, k, o){
+ fn.call(cxt, v, k, o);
+ }, arguments[1] || this);
return this;
},
'filter' : function filter( fn, context ){
- var o = this._o, acc = new o.constructor();
- for ( var name in o )
- if ( fn.call( context || this, o[name], name, o ) )
- acc[name] = o[name];
- return acc;
+ var cxt = arguments[1] || this;
+ return this.reduce(function(acc, v, k, o){
+ if ( fn.call(cxt, v, k, o) )
+ acc.attr(k, v);
+ return acc;
+ }, new this.__class__() );
},
+ // 'find' : function find( fn ){
+ // var cxt = arguments[1] || this;
+ //
+ // },
+
+ /**
+ * FIXME
+ */
'indexOf' : function indexOf( value ){
var o = this._o;
for ( var name in o )
return ( this.indexOf(value) !== -1 );
},
- 'clone' : function clone(){
- return new this.constructor().extend(this);
- },
-
- // Dependence on YArray closes the loop
- // 'remove' : function remove(v){
- // var o = this._o
- // , toRemove = new Y(arguments);
- //
- // for (var k in o) {
- // var v = o[k];
- // if ( toRemove.has(v) ) {
- // delete o[k];
- // toRemove.remove(v);
- // }
- // }
- // return this;
- // },
-
- 'remove' : function remove(v){
- var o = this._o
- , toRemove = slice.call(arguments);
-
- for (var k in o) {
- var v = o[k], idx = toRemove.indexOf(v);
- if ( idx !== -1 ) {
- delete o[k];
- toRemove.splice(idx,1);
- }
- if (!toRemove.length)
- break;
- }
- return this;
- },
-
'every' : function every( fn ){
var self = this, fn = fn || op.bool;
return this.reduce(function(acc, v, k, o){
},
'zip' : function zip(){
- var sequences = slice.call(arguments);
+ var sequences = slice.call(arguments,0);
sequences.unshift(this);
return this.map(function(_, k){
return sequences.map(op.curried.kget(k));
'pluck' : function pluck(key){
return this.map(function(v){
return del.attr(v, key);
- // return v && (type.isFunction(v.attr) ? v.attr(key) : v[key]);
});
},
+ // FIXME: del.map
'invoke' : function invoke(name){
var args = slice.call(arguments,1);
return del.map(this, function(o){
});
},
+ // FIXME: this._o[name].apply
'apply' : function apply(name, args){
return this[name].apply(this, args);
}
, slice = core.slice
, YF = YFunction
-, _ = YF._ = {}
, YFP = YF.prototype
;
// Export these last to avoid methodizing them
exports['YFunction'] = YF(YF);
-exports['_'] = _;
exports['YObject'] =
YCollection.subclass('YObject', {
- init : function initYObject(o){
+ 'init' : function initYObject(o){
this._o = o || {};
},
- clone : function clone(){
+ 'clone' : function clone(){
return new YObject( deepcopy(this._o) );
+ },
+
+ 'remove' : function remove(v){
+ var o = this._o
+ , values = slice.call(arguments,0);
+
+ for (var k in o) {
+ var v = o[k];
+ if ( values.indexOf(v) !== -1 )
+ delete o[k];
+ }
+ return this;
+ },
+
+
+ 'reduce' : function reduce( fn, acc, context ){
+ var o = this._o || this,
+ acc = acc || new o.constructor();
+ for ( var name in o )
+ acc = fn.call( context || this, acc, o[name], name, o );
+ return acc;
+ },
+
+ 'map' : function map( fn ){
+ var o = this._o
+ , cxt = arguments[1] || this
+ , acc = new o.constructor()
+ ;
+ for ( var name in o )
+ acc[name] = fn.call(cxt, o[name], name, o);
+ return acc;
+ },
+
+ 'forEach' : function forEach( fn, context ){
+ var o = this._o, cxt = arguments[1] || this;
+ for ( var name in o )
+ fn.call(cxt, o[name], name, o);
+ return this;
+ },
+
+ 'filter' : function filter( fn, context ){
+ var o = this._o, acc = new o.constructor();
+ for ( var name in o )
+ if ( fn.call( context || this, o[name], name, o ) )
+ acc[name] = o[name];
+ return acc;
+ },
+
+ 'indexOf' : function indexOf( value ){
+ var o = this._o;
+ for ( var name in o )
+ if ( o[name] === value )
+ return name;
+ return -1;
}
+
});
},
'clone' : function clone(){
- return YString(this._o);
+ return YString(this._o); // strings are immutable
},
'size' : size,
, slice = core.slice
, isFunction = type.isFunction
, isArray = type.isArray
-;
+, UE =
+exports['UnimplementedError'] =
+function UnimplementedError(){ Error.call(this, arguments); }
+;
+UE.prototype = new Error('Unimplemented!');
+
function bindName(k){
var v = this[k];
if ( isFunction(v) )
return false;
},
- 'tick' : function(elapsed){
+ 'tick' : function(elapsed, now){
if (this.ready) return true;
this.elapsed += elapsed || 0;
framerate : 0, // Target framerate
frametime : 0, // 1000 / framerate
- now : 0, // Last tick time (ms)
+ clock : 0, // Subjective clock
+ now : 0, // Last tick time (real ms)
fps : 0, // Effective framerate
ticks : 0, // Number of ticks since start
this.stop();
this.elapsedAtStop = 0;
this.ticks = 0;
+ this.clock = 0;
this.times = [];
this.realtimes = [];
},
this.now = new Date().getTime();
this.elapsed = this.now - lastTick;
this.perceived = this.elapsed/this.timeDilation;
+ this.clock += this.perceived;
clearTimeout(this.timer);
this.timer = null;
this.fire('tick', this, {
- 'now' : this.now,
+ 'now' : this.clock,
'elapsed' : this.perceived,
'ticks' : ++this.ticks
});
+
+
var Y = require('Y').Y
, CAPACITY = 8
, REGION_ID = 0
,
-
Region =
exports['Region'] =
Y.subclass('Region', {
this.value = value;
},
- // Expects caller will have ordered x1 < x2, y1 < y2
- overlaps : function overlaps(x1,y1, x2,y2){
- return !( x1 > this.x2 || y1 > this.y2
- || x2 <= this.x1 || y2 <= this.y1 );
- },
+ /**
+ * Determines if the given Rect overlaps with this tree,
+ * including the top/left edges, but excluding the bottom/right.
+ */
+ overlaps : orderThen('_overlaps'),
+
+ /**
+ * As overlaps, but presumes input is sorted x1 < x2 and y1 < y2.
+ */
+ _overlaps : overlaps,
toString : function toString(){
return this.className+'(id='+this.id+', rect='+rectToString(this)+', value='+this.value+')';
this.clear();
},
- // Expects caller will have ordered x1 < x2, y1 < y2
- overlaps : function overlaps(x1,y1, x2,y2){
- return !( x1 > this.x2 || y1 > this.y2
- || x2 <= this.x1 || y2 <= this.y1 );
- },
+ /**
+ * Determines if the given Rect overlaps with this tree,
+ * including the top/left edges, but excluding the bottom/right.
+ */
+ overlaps : orderThen('_overlaps'),
+
+ /**
+ * As overlaps, but presumes input is sorted x1 < x2 and y1 < y2.
+ */
+ _overlaps : overlaps,
// TODO: Add optional filter
get : function get(x1,y1, x2,y2){
// , x1 = Math.min(_x1,_x2), x2 = Math.max(_x1,_x2)
// , y1 = Math.min(_y1,_y2), y2 = Math.max(_y1,_y2) ;
//
- // if ( !self.overlaps(x1,y1, x2,y2) )
+ // if ( !self._overlaps(x1,y1, x2,y2) )
// return acc;
//
// // Implies this is a leaf: check its values
});
},
- 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)
- , _y1 = Math.min(y1,y2), _y2 = Math.max(y1,y2) ;
-
- if ( !self.overlaps(_x1,_y1, _x2,_y2) )
+ /**
+ * @protected
+ * Invokes iterator on each matching subtree (passing the accumulator, the tree's regions, the tree, and the Rect query)
+ * to acquire the new accumulator:
+ *
+ * acc = fn.call(context||tree, acc, tree.regions, tree, query);
+ *
+ * Where the Rect query is passed as an object with properties (x1,y1, x2,y2).
+ *
+ * For more friendly iterators, useful to public callers, @see this.collect(), @see this.reduce().
+ */
+ leaves : orderThen('_leaves'),
+
+ // leaves : function leaves(x1,y1, x2,y2, fn, acc, context){
+ // var _x1 = Math.min(x1,x2), _x2 = Math.max(x1,x2)
+ // , _y1 = Math.min(y1,y2), _y2 = Math.max(y1,y2);
+ // return this._leaves(_x1,_y1, _x2,_y2, fn, acc, context);
+ // },
+
+ _leaves : function _leaves(x1,y1, x2,y2, fn, acc, context){
+ if ( !this._overlaps(x1,y1, x2,y2) )
return acc;
// Implies this is a leaf: check its values
- if ( self.regions ) acc = fn.call(cxt, acc, self.regions, self, self);
+ if ( this.regions ) acc = fn.call(context || this, acc, this.regions, this);
// Recurse into any children -- Note we do not place this in an else-block,
// as mutation during the above call might cause the tree to split.
- if (self.nw) acc = self.nw.leaves(_x1,_y1, _x2,_y2, fn, acc, context);
- if (self.ne) acc = self.ne.leaves(_x1,_y1, _x2,_y2, fn, acc, context);
- if (self.sw) acc = self.sw.leaves(_x1,_y1, _x2,_y2, fn, acc, context);
- if (self.se) acc = self.se.leaves(_x1,_y1, _x2,_y2, fn, acc, context);
+ if (this.nw) acc = this.nw._leaves(x1,y1, x2,y2, fn, acc, context);
+ if (this.ne) acc = this.ne._leaves(x1,y1, x2,y2, fn, acc, context);
+ if (this.sw) acc = this.sw._leaves(x1,y1, x2,y2, fn, acc, context);
+ if (this.se) acc = this.se._leaves(x1,y1, x2,y2, fn, acc, context);
return acc;
},
- 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);
+ collect : orderThen('_collect'),
+
+ // 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._collect(_x1,_y1, _x2,_y2, fn, acc, context);
+ // },
+
+ _collect : function _collect(x1,y1, x2,y2, fn, acc, context){ // XXX: unique=false, limit=Infinity, depth=Infinity
return this.leaves(
- _x1,_y1, _x2,_y2,
+ x1,y1, x2,y2,
function(acc, rs, tree){
for (var i=0, L=rs.length, r=rs[i]; i<L; r=rs[++i])
- if ( r.overlaps(_x1,_y1, _x2,_y2) )
+ if ( r._overlaps(x1,y1, x2,y2) )
acc = fn.call(this, acc, r.value, r, tree);
return acc;
},
}
});
+/**
+ * Orders x1,y1, x2,y2 correctly then calls method.
+ */
+function orderThen(method){
+ return function _orderThen(x1,y1, x2,y2){
+ var _x1 = Math.min(x1,x2), _x2 = Math.max(x1,x2)
+ , _y1 = Math.min(y1,y2), _y2 = Math.max(y1,y2)
+ , args = [_x1,_y1, _x2,_y2];
+ if (arguments.length > 4)
+ args = args.concat(Y(arguments,4));
+ return this[method].apply(this, args);
+ };
+}
+
+function overlaps(x1,y1, x2,y2){
+ return !( x1 >= this.x2 || y1 >= this.y2
+ || x2 <= this.x1 || y2 <= this.y1 );
+}
+
function Rect(x1,y1, x2,y2){
return '['+x1+','+y1+', '+x2+','+y2+']';
}
+
+function moveArgs(args, st, by){
+ if (by === undefined) by = 1;
+ var i = (args.length += by)
+ , gap = st+by
+ ;
+ while (--i >= st) {
+ args[i] = (i < gap ? undefined : args[i-by]);
+ }
+ return args;
+}
// game.addThing(new Tank(1).colors('#4596FF', '#182B53', '#F25522'), 3,9);
E =
- game.addThing(new Tank(2), 0,0);
+ game.addThing(new Tank(2), 0,5);
// game.addThing(new Tank(2), 1,0);
// game.addThing(new Tank(2), 8,1);
_blocked : function blocked(){
var pm = this.pathmap
, agent = pm._agent
- , bb = agent.boundingBox
+ // , bb = agent.boundingBox
- , origin = bb.relOrigin
- , left = origin.x, right = bb.width - left
- , top = origin.y, bottom = bb.height - top
+ // , origin = bb.relOrigin
+ // , left = origin.x, right = bb.width - left
+ // , top = origin.y, bottom = bb.height - top
, SIZE = pm.gridSquare
, x = Math.floor(this.x)
path.push( vec.sum(node,mid) );
node = node.prev;
}
- return path.reverse();
+ return Y(path.reverse());
}
// Normal case -- move current from open to closed, process each of its neighbors
Trajectory =
exports['Trajectory'] =
Line.subclass('Trajectory', {
- elapsed : 0,
+ tCurrent : 0,
- init : function initTrajectory(owner, x1,y1, x2,y2, tdist, elapsed){
+ init : function initTrajectory(owner, x1,y1, x2,y2, tdist, tCurrent){
Y.bindAll(this, 'compare', 'closer', 'intersects');
this.owner = owner;
this.game = owner.game;
this.pathmap = this.game.pathmap;
- this.reset(x1,y1, x2,y2, tdist, elapsed);
+ this.reset(x1,y1, x2,y2, tdist, tCurrent);
},
- reset : function reset(x1,y1, x2,y2, tdist, elapsed){
+ reset : function reset(x1,y1, x2,y2, tdist, tCurrent){
if (x1 instanceof Array && y1 instanceof Array) {
tdist = x2;
y2 = y1[1]; x2 = y1[0];
this.p2 = new Vec(x2,y2);
Vec.init.call(this, x2-x1, y2-y1);
- this.elapsed = elapsed || 0;
+ this.tCurrent = tCurrent || 0;
this.resetBound();
return this;
},
},
clone : function clone(){
- return new Trajectory(this.owner, this.x1,this.y1, this.x2,this.y2, this.tdist, this.elapsed);
+ return new Trajectory(this.owner, this.x1,this.y1, this.x2,this.y2, this.tdist, this.tCurrent);
},
if (b instanceof Thing) b = b.loc;
var abs = Math.abs
- , t = this.elapsed
+ , t = this.tCurrent
, xa = this.calcX(a.y), ya = this.calcY(a.x)
, ta = this.iparametric(xa,ya) - t
return (blocker === obj ? false : blocker);
},
-
- // step : function step(dt){
- // this.halt = false;
- //
- // var _dt = dt = (dt === undefined ? ELAPSED : dt)
- // , _bb = bb = this.owner.boundingBox, bw = bb.width, bh = bb.height
- // , to, t, r;
- //
- // do {
- // t = Math.min(this.tBound, dt);
- // dt -= t;
- // this.elapsed += t;
- //
- // to = this.stepTo(t, bb);
- // if (this.halt) break;
- //
- // bb = new BoundingBox(to.x,to.y, to.x+bw,to.y+bh);
- //
- // } while (dt > 0);
- //
- // return to;
- // },
- //
- // stepTo : function stepTo(t, bb){
- // var ng, og = this.p2, owner = this.owner
- // , to = this.parametric(this.elapsed), _to = to
- // , test = this.pathmap.moveBlocked(owner, this, to, bb)
- // ;
- //
- // // Blocked! Reflect trajectory
- // if ( test ) {
- // to = test.to;
- // this.bounces++;
- //
- // var blocker = test.blockers[0];
- // owner.fire('collide', blocker);
- // blocker.fire('collide', owner);
- //
- // // Potentially set by responders to collision event
- // if (this.halt) return to;
- //
- // if (!test.side) {
- // console.error('Null reflection line!', 'to:', to, 'blockers:', test.blockers);
- // return to;
- // }
- //
- // if ( test.blockers.length > 1 ) {
- // to = bb.p1;
- // ng = this.p1; // XXX: recalculate?
- // console.log('corner!', this, 'to:',to, 'ng:',ng);
- // } else {
- // ng = math.reflect(this.p2, test.side);
- // }
- //
- // this.reset(to.x,to.y, ng.x,ng.y);
- // owner.render(this.game.level);
- //
- // // console.log([
- // // '['+TICKS+' ('+this.depth+')] '+owner+' reflected!',
- // // ' wanted: '+_to+' x ('+(_to.x+bb.width)+','+(_to.y+bb.height)+')',
- // // ' blocker: '+test.msg,
- // // ' old:',
- // // ' loc: '+bb.p1,
- // // ' goal: '+og,
- // // ' new:',
- // // ' loc: '+to,
- // // ' goal: '+ng,
- // // ' --> trajectory: '+this
- // // ].join('\n'));
- // }
- //
- // return to;
- // },
-
toString : function toString(){
return 'T['+this.p1+', '+this.p2+', slope='+this.slope.toFixed(3)+']';
}
Traversal =
exports['Traversal'] =
Y.subclass('Traversal', {
- elapsed : 0,
-
isBlocked : false,
isCorner : false,
remaining : 0,
this.bbox = thing.boundingBox.clone();
this.trajectory = trajectory || thing.trajectory;
- this.elapsed = this.trajectory.elapsed;
},
step : function step(dt, tx,ty){
var t, traj = this.trajectory;
+ // console.group('tvsl.step(dt='+dt+', tCurrent='+traj.tCurrent+', target=('+tx+','+ty+'))');
do {
t = this.stepTo( Math.min(traj.tBound,dt), tx,ty);
- this.elapsed += t;
- traj.elapsed += t;
- dt -= t;
+ traj.tCurrent += t;
+ dt = Math.max(0, dt-t);
if (this.isBlocked) {
this.remaining = dt;
} while (dt > 0);
+ // console.groupEnd();
return this.to;
},
, x2 = bb.x2, y2 = bb.y2
, o = bb.absOrigin
- , to = this.to = traj.parametric(this.elapsed+t)
+ , to = traj.parametric(traj.tCurrent+t)
;
// Don't overshoot the target
// nb: BoundingBox.relocate() is in-place, so this.bbox are updated
bb = bb.relocate(to);
+ var oto = this.to = to;
blockers = this.blockers = this.pathmap.get(bb).remove(thing).end();
- if (!blockers.length)
+ if (!blockers.length) {
+ // console.log('stepTo('+t+') --> '+to+' consuming '+t);
return t; // All time consumed, yay
+ }
this.isBlocked = true;
bb.relocate(to);
// Return how much time consumed
- return traj.timeToMove(to.x-o.x, to.y-o.y);
+ var consumed = traj.timeToMove(to.x-o.x, to.y-o.y);
+ // console.log('stepTo('+t+') --> wanted '+oto+' but BLOCKED! by '+this.blocker+', so ending at '+to+', consuming '+consumed);
+
+ return consumed;
}
})
this.move( this.loc.moveByDir(dir, this.stats.move * SQUARETH) );
},
- // _move : function _move(){
- // var dir = this.activeKeys
- // .unique()
- // .sort()
- // .join(' ');
- //
- // if (!dir) return;
- //
- // var toLoc = this.loc.moveByDir(dir, (this.stats.move * SQUARETH))
- //
- // , x = toLoc.x, y = toLoc.y
- // , bb = this.boundingBox.relocated(x,y)
- //
- // , blockers = this.game.pathmap.get(bb.x1,bb.y1, bb.x2,bb.y2).remove(this)
- // ;
- //
- // if ( !blockers.size() )
- // this.game.moveThingTo(this, x,y);
- // },
-
-
updateMeta : function updateMeta(evt){
exports['Tank'] =
Thing.subclass('Tank', function(Tank){
- Y.core.extend(this, {
+ Y.core.descriptors(this, {
projectile : Bullet,
blocking : true,
buffs : null,
nShots : 0,
- currentMove : null,
forceCurrentMove : false,
- currentMoveLimit : -1
+ currentMoveLimit : -1,
+
+ trajectory : null
});
-
this['init'] =
function initTank(align){
Thing.init.call(this, align);
var ai = this.ai;
+ this.continueMove();
+ return;
+
// Check to see if we should obey our last decision, and not recalc
- if (this.forceCurrentMove && this.forceCurrentMove() && this.currentMoveLimit > NOW) {
+ if (this.forceCurrentMove && this.forceCurrentMove() && this.currentMoveLimit > now) {
+ // console.log('forced!', this.currentMove);
this.continueMove();
return this;
} else
var bs = this.willCollide( this.findNearLike(25, isBullet) );
// console.log('['+TICKS+':'+this.id, this, '] Shoot down bullets?', bs.size() && bs);
if ( bs.size() ) {
- ai.shootIncoming.activate(NOW);
+ ai.shootIncoming.activate(now);
var b = this.closestOf(bs);
// console.log(' --> Incoming! Shoot it down!', b);
this.shoot(b.loc.x, b.loc.y);
var bs = this.willCollide(this.findNearLike(71, isBullet), 5);
// console.log('['+TICKS+':'+this.id, this, '] Dodge bullets?', bs.size() && bs);
if (bs.size()) {
- ai.dodge.activate(NOW);
+ ai.dodge.activate(now);
var bullet = this.closestOf(bs);
this.moveAwayFrom(bullet);
return this;
var t = this.findNearEnemies(71, true).shift();
// console.log('['+TICKS+':'+this.id, this, '] Shoot at enemies?', t);
if (t) {
- ai.shootEnemy.activate(NOW);
+ ai.shootEnemy.activate(now);
// console.log(' --> I gotcha!', t);
this.shoot(t.loc.x, t.loc.y);
return this;
/**
+ * @return {this}
+ */
+ this['move'] =
+ function move(x,y){
+ if (x instanceof Array) { y=x[_Y]; x=x[_X]; }
+
+ var loc = this.loc;
+ this.trajectory = new Trajectory(this, loc.x,loc.y, x,y, this.movePerMs);
+
+ var tvsl = new Traversal(this)
+ , to = tvsl.step(this.elapsed, x,y) ;
+
+ this.game.moveThingTo(this, to.x,to.y);
+
+ return this;
+ };
+
+ this['continueMove'] =
+ function continueMove(){
+ if ( !this.currentMove ){ //|| this.ai.path.ready ){
+ var target = this.findNearEnemies(10000).shift();
+ if (target)
+ this.calculatePath(target.loc);
+ else
+ this.currentMove = null;
+ }
+
+ var to = this.currentMove
+ , path = this.currentPath
+ if (!to) return;
+
+ this.move(to);
+ if ( this.loc.equals(to) ){
+ // console.log('--> destination reached!', 'to:', to, 'loc:', this.loc);
+ this.currentMove = ((path && path.length) ? path.shift() : null);
+ // console.log('--> next move:', this.currentMove, 'path: ['+path.invoke('toString')+']');
+ }
+ };
+
+ this['calculatePath'] =
+ function calculatePath(end){
+ if ( !end || !this.ai.path.activate() ) return;
+
+ // this.currentMove = null;
+ this.forceCurrentMove = false;
+ this.currentMoveLimit = -1;
+
+ // console.log('calculatePath() moving toward:', end);
+ var pm = this.game.pathmap
+ , start = this.loc
+
+ , path = this.currentPath = pm.path(this, end)
+ , to = this.currentMove = path.shift()
+ ;
+ // console.log('--> next move:', this.currentMove);
+ };
+
+
+ /**
* TODO: Get this.currentMove etc out of the calc methods.
*/
this['moveAwayFrom'] =
, to = this.currentMove = trj.near(x,y);
this.forceCurrentMove = this.willCollide.bind(this, [agent], 5);
- this.currentMoveLimit = NOW + 1000;
+ this.currentMoveLimit = this.now + 1000;
this.move(to.x, to.y);
if (xydef) this.rotateBarrel(x,y);
- if ( this.nShots >= this.stats.shots || !this.cooldowns.attack.activate(NOW) )
+ if ( this.nShots >= this.stats.shots || !this.cooldowns.attack.activate(this.now) )
return null;
var tloc = this.getTurretLoc()
return p;
};
- /**
- * @return {this}
- */
- this['move'] =
- function move(x,y){
- if (x instanceof Array) { y=x[_Y]; x=x[_X]; }
- var loc = this.loc
- , cur = this.currentMove
- ;
-
- if ( !(this.trajectory && cur && cur.x === x && cur.y === y) )
- this.trajectory = new Trajectory(this, loc.x,loc.y, x,y, this.movePerMs);
-
- var tvsl = new Traversal(this)
- , to = tvsl.step(this.elapsed, x,y)
- ;
-
- this.game.moveThingTo(this, to.x,to.y);
- return this;
- };
-
- /**
- * @protected
- * This method does not update this.trajectory -- call this.move(x,y) instead.
- */
- // this['moveByAngle'] =
- // function moveByAngle(theta, targetX,targetY){
- // var abs = Math.abs
- // , bb = this.boundingBox
- // , loc = this.loc
- // , to = loc.moveByAngle(theta, this.stats.move * SQUARETH)
- // , x = to.x, y = to.y
- // ;
- //
- // // Don't overshoot the target
- // if ( targetX !== undefined && abs(loc.x-x) > abs(loc.x-targetX) )
- // x = targetX;
- //
- // if ( targetY !== undefined && abs(loc.y-y) > abs(loc.y-targetY) )
- // y = targetY;
- //
- // var nbb = bb.relocated(x,y)
- // , blockers = this.game.pathmap.get(nbb.x1,nbb.y1, nbb.x2,nbb.y2).remove(this);
- //
- // if ( !blockers.size() )
- // this.game.moveThingTo(this, x,y);
- //
- // return this;
- // };
-
-
- this['continueMove'] =
- function continueMove(){
- if ( !this.currentMove || this.ai.path.ready ){
- var target = this.findNearEnemies(10000).shift();
- if (target) this.calculatePath(target.loc);
- }
-
- var to = this.currentMove;
- if (!to) return;
-
- this.move(to);
- if ( this.loc.equals(to) ) {
- this.forceCurrentMove = false;
- this.currentMoveLimit = -1;
- this.currentMove = (this.lastPath ? this.lastPath.shift() : null);
- }
- };
-
- this['calculatePath'] =
- function calculatePath(end){
- if ( !(end && this.ai.path.activate(NOW)) ) return;
-
- // this.forceCurrentMove = Y.op.K(true);
- // this.currentMoveLimit = NOW + 750;
- this.forceCurrentMove = false;
- this.currentMoveLimit = -1;
-
- // console.log(this, 'moving toward', t);
- var pm = this.game.pathmap
- , start = this.loc
-
- , path = this.lastPath = pm.path(this, end)
- , to = this.currentMove = path.shift()
- ;
- };
-
this['ableToShoot'] =
function ableToShoot(){
// Main method is only executed once, so we'll setup things
// that don't change between games.
function main(){
+ $('#welcome').center();
/// Debug ///
if (qkv.ai) {
$('#welcome').clone()
.attr('id', 'pause')
.hide()
+ .css({ 'top':'1em', 'left':'1em', 'margin':0, 'width':'auto' })
.appendTo( $('body') )
.find('.box').html('<h1>Paused!</h1>');
}
function pauseGame(evt){
- $('#overlay').toggle();
+ // $('#overlay').toggle();
$('#pause').toggle();
toggleGame();
}
return false;
}
+jQuery.fn.center = function centerJQ(){
+ var body = $('body');
+ this.css({
+ 'left' : '50%', 'top': '50%',
+ 'margin-left' : -0.5*this.width(),
+ 'margin-top' : -0.5*this.height(),
+ // 'left' : 0.5*(body.width() - this.width()),
+ // 'top' : 0.5*(body.height() - this.height()),
+ 'position' :'absolute'
+ });
+ return this;
+};
+
exports['main'] = main;
exports['gameExists'] = gameExists;
var Y = require('Y').Y
, Rect = require('ezl/shape').Rect
-, astar = require('ezl/util/astar')
+, vec = require('ezl/math/vec')
, PathMap = require('tanks/map/pathmap').PathMap
, config = require('tanks/config').config
,
init : function initPathMapUI(game, pathmap){
this.game = game;
this.pathmap = pathmap;
+ this.highlights = Y([]);
Rect.init.call(this, pathmap.width-2, pathmap.height-2);
- pathmap._gridPath = this.gridPathWithUI.bind(this);
- this.cleanUpAgent = this.cleanUpAgent.bind(this);
- this._drawPathStep = this._drawPathStep.bind(this);
+ // Store ref to original path function
+ this._path = pathmap.path;
+ pathmap.path = this.pathWithUI.bind(this);
+
+ this.cleanUpAgent = this.cleanUpAgent.bind(this);
+ this.drawPathStep = this.drawPathStep.bind(this);
},
render : function render(ctx){
}, {});
},
-
/**
- * Wraps PathMap.gridPath() to draw AI pathes.
+ * Wraps PathMap.path() to draw AI pathes.
*/
- gridPathWithUI : function gridPathWithUI(start, end, agent){
- var size = this.gridSquare, floor = Math.floor
- , grid = this.pathmap.grid()
-
- , startX = floor(start.x/size)
- , startY = floor(start.y/size)
- , startN = grid[startX][startY]
-
- , endX = floor(end.x/size)
- , endY = floor(end.y/size)
- , endN = grid[endX][endY]
-
- , path = Y(astar.search(grid, startN, endN))
+ pathWithUI : function pathWithUI(agent, x,y){
+ var pm = this.pathmap
+ , path = this._path.call(pm, agent, x,y)
+ , start = vec.sum(pm._getSquare(agent.loc), pm.gridSquareMid)
;
-
if (this.overlayAiPaths)
- this.drawPath(agent, startN, path);
+ this.drawPath(agent, start, Y(path));
return path;
},
ctx.clearRect(0,0, w,h);
ctx.closePath();
- // Draw the path
- ctx.fillStyle = 'rgba(0,0,0,0.1)';
- this._drawPathStep(ctx, start);
- path.reduce(this._drawPathStep, ctx);
-
- // Draw start (concentric ring)
- ctx.fillStyle = 'rgba(0,255,0,0.05)';
- this._drawPathStep(ctx, start, -1);
-
- // Draw finish
- ctx.fillStyle = 'rgba(0,0,255,0.05)';
- this._drawPathStep(ctx, path.last());
-
+ // Don't draw anything if no path exists
+ if ( path && path.length ) {
+
+ // Draw the path
+ ctx.fillStyle = 'rgba(0,0,0,0.1)';
+ this.drawPathStep(ctx, start);
+ path.reduce(this.drawPathStep, ctx);
+
+ // Draw start (concentric ring)
+ ctx.fillStyle = 'rgba(0,255,0,0.05)';
+ this.drawPathStep(ctx, start, -1);
+
+ // Draw finish
+ ctx.fillStyle = 'rgba(0,0,255,0.05)';
+ this.drawPathStep(ctx, path.last());
+ }
canvas.show();
},
- _drawPathStep : function drawPathStep(ctx, p, idx){
- var size = this.gridSquare
- , off = size*(idx === -1 ? 0.15 : 0.3)
- , r = size/2 - off
- , x = p.x*size + off + r
- , y = p.y*size + off + r ;
+ drawPathStep : function drawPathStep(ctx, p, idx){
+ var SIZE = this.gridSquare
+ , off = SIZE*(idx === -1 ? 0.15 : 0.3)
+ , r = SIZE/2 - off
+ ;
ctx.beginPath();
- ctx.arc(x,y, r, 0, Math.PI*2, false);
+ ctx.arc(p.x,p.y, r, 0, Math.PI*2, false);
ctx.fill();
ctx.closePath();
return ctx;
- }
+ },
+
+ hl : function highlight(x,y){
+ if (x instanceof Array) { y=x.y; x=x.x; }
+ var SIZE = REF_SIZE
+ , x1 = Math.floor(x/SIZE)*SIZE
+ , y1 = Math.floor(y/SIZE)*SIZE
+
+ , H = new Rect(SIZE,SIZE)
+ .position(x1,y1)
+ .fill('rgba(166,217,255, 0.1)')
+ .stroke('rgba(166,217,255, 0.75)', 1)
+ .appendTo(this)
+ ;
+ this.highlights.push(H);
+ this.draw();
+ return H;
+ },
+
+ clearhl : function clearHL(){
+ this.highlights.invoke('remove').clear();
+ return this.draw();
+ },
});
<script src="build/future.js" type="text/javascript"></script>
<script src="build/functional/to-function.js" type="text/javascript"></script>
<script src="build/Y/type.js" type="text/javascript"></script>
+<script src="build/Y/internal.js" type="text/javascript"></script>
<script src="build/Y/core.js" type="text/javascript"></script>
<script src="build/Y/delegate.js" type="text/javascript"></script>
<script src="build/Y/types/function.js" type="text/javascript"></script>
<script src="build/ezl/loop/eventloop.js" type="text/javascript"></script>
<script src="build/evt.js" type="text/javascript"></script>
<script src="build/ezl/loc/loc.js" type="text/javascript"></script>
-<script src="build/ezl/math/rect.js" type="text/javascript"></script>
<script src="build/ezl/math/line.js" type="text/javascript"></script>
+<script src="build/ezl/math/rect.js" type="text/javascript"></script>
<script src="build/ezl/math.js" type="text/javascript"></script>
<script src="build/ezl/loop/fx.js" type="text/javascript"></script>
<script src="build/ezl/loc/boundingbox.js" type="text/javascript"></script>