/// Now start assembling the normal sub-modules ///
-addNames('YCollection', require('Y/types/collection'));
-addNames('YArray', require('Y/types/array'));
-addNames('YObject deepcopy', require('Y/types/object'));
-addNames('YString', require('Y/types/string'));
-addNames('YNumber range', require('Y/types/number'));
+addNames('YCollection', require('Y/types/collection'));
+addNames('YArray', require('Y/types/array'));
+addNames('YObject getNested setNested deepcopy',
+ require('Y/types/object'));
+addNames('YString', require('Y/types/string'));
+addNames('YNumber range', require('Y/types/number'));
var utils = require('Y/utils');
Y['bindAll'] = utils.bindAll;
var Y = require('Y').Y
-, getNested = require('Y/types/object').getNested
+, getNested = Y.getNested
, Emitter = require('Y/modules/y.event').Emitter
,
*/
updateOnChange : function updateOnChange(events, obj){
if ( !Y.isArray(events) )
- events = events.split();
+ events = events.split(/\s+/);
events = events.map(function events(key){
obj[key.split('.').pop()] = this.get(key);
if ( !Y(key).startsWith('set:') )
addEventListener : function addEventListener(evts, fn){
fn = fn.toFunction();
if ( !Y.isArray(evts) )
- evts = evts.split();
+ evts = evts.split(/\s+/);
evts.forEach(function addEvent(evt){
this.getQueue(evt).push(fn);
}, this);
return evt;
},
- // XXX: does not handle degenerate or recursive event dispatch
+ /**
+ * Dispatches the given event to all listeners in the appropriate queue.
+ * Listeners are invoked with the event, and with context set to the target.
+ * XXX: does not handle degenerate or recursive event dispatch
+ */
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);
,
+// addTypeSupport =
+// exports['addTypeSupport'] =
+// function addTypeSupport(type, parser, builder){
+// var T = Y.typeName(type);
+// type2parser[T] = parser;
+// }
+// ,
+
Field =
exports['Field'] =
this.key = chain.split('.').pop();
this.label = camelToSpaces(this.key);
- var T = Y(Y.type(val)).getName();
+ var T = Y.typeName(def);
this.cast = options.cast || type2parser[T];
this.type = options.type || type2el[T];
},
function createField(group, value, chain){
var def = config.getDefault(chain)
- , field = new Field(chain, def, value);
+ , T = Y.typeName(def);
+
+ if (!type2parser[T])
+ return group;
+ var field = new Field(chain, def, value);
fields[chain] = field;
group.append(field.el);
config.addEventListener('set:'+chain, function onConfigSet(evt){
"Boolean Number String Function Array Date RegExp Object"
.split(" ")
.reduce(function(class2name, name) {
- class2name[ "[object "+name+"]" ] = name.toLowerCase();
+ class2name[ "[object "+name+"]" ] = name;
return class2name;
}, {});
-function type_of(obj){
- return obj == null ?
- String( obj ) :
- class2name[ toString.call(obj) ] || "object";
+function basicTypeName(o){
+ return o == null ?
+ String(o) :
+ class2name[ toString.call(o) ] || "Object";
+}
+
+function typeName(o){
+ if (o === undefined || o === null)
+ return String(o);
+ var T = type(o);
+ return T.className || (T !== Function ? T.name : basicTypeName(o));
}
var arrayLike = isArray.types = [];
-function isArray(obj) { return type_of(obj) === "array" || (arrayLike.indexOf(type(obj)) !== -1); }
-function isFunction(obj) { return type_of(obj) === "function"; }
-function isString(obj) { return type_of(obj) === "string"; }
-function isNumber(obj) { return type_of(obj) === "number"; }
-function isWindow(obj) { return obj && typeof obj === "object" && "setInterval" in obj; }
+function isArray(o) { return basicTypeName(o) === "Array" || (arrayLike.indexOf(type(o)) !== -1); }
+function isFunction(o) { return basicTypeName(o) === "Function"; }
+function isString(o) { return basicTypeName(o) === "String"; }
+function isNumber(o) { return basicTypeName(o) === "Number"; }
+function isWindow(o) { return o && typeof o === "object" && "setInterval" in o; }
-function isPlainObject( obj ){
+function isPlainObject(o){
// Must be an Object.
// Because of IE, we also have to check the presence of the constructor property.
// Make sure that DOM nodes and window objects don't pass through, as well
- if ( !obj || type_of(obj) !== "object" || obj.nodeType || isWindow(obj) )
+ if ( !o || basicTypeName(o) !== "Object" || o.nodeType || isWindow(o) )
return false;
// Not own constructor property must be Object
- if ( obj[FN] &&
- !hasOwn.call(obj, FN) &&
- !hasOwn.call(obj[FN][PT], "isPrototypeOf") )
+ if ( o[FN] &&
+ !hasOwn.call(o, FN) &&
+ !hasOwn.call(o[FN][PT], "isPrototypeOf") )
return false;
// Own properties are enumerated firstly, so to speed up,
// if last one is own, then all properties are own.
var key;
- for ( key in obj ) {}
+ for ( key in o ) {}
- return key === undefined || hasOwn.call( obj, key );
+ return key === undefined || hasOwn.call( o, key );
}
-function type( o ) {
+function type(o) {
if ( o === null || o === undefined )
return o;
}
}
-function is( A, B ){
+function is(A, B){
if ( isArray(B) )
return B.some( is.bind(this,A) );
else {
exports['is'] = is;
exports['type'] = type;
-exports['type_of'] = type_of;
+exports['basicTypeName'] = basicTypeName;
+exports['typeName'] = typeName;
exports['isArray'] = isArray;
exports['isFunction'] = isFunction;
exports['isWindow'] = isWindow;
exports['isPlainObject'] = isPlainObject;
-type['KNOWN_CLASSES'] = KNOWN_CLASSES;
+type['KNOWN_CLASSES'] = KNOWN_CLASSES;
// Export these last to avoid methodizing them
exports['YFunction'] = YF(YF);
-exports._ = _;
+exports['_'] = _;
}
// Do we have a type-specific wrapper?
- var name = type.type_of(o)
- , yname = 'Y' + name.charAt(0).toUpperCase() + name.slice(1)
+ var yname = 'Y' + type.basicTypeName(o)
, YType = Y[yname]
;
, objToString = _Object[P].toString
, classToString = function toString(){ return this.className+"()"; }
-;
+,
+
-// Private delegating constructor -- must be defined for every
-// new class to prevent shared state. All construction is
-// actually done in the init method.
-exports['ConstructorTemplate'] = ConstructorTemplate;
+/**
+ * @private
+ * Private delegating constructor. This function must be redefined for every
+ * new class to prevent shared state. All construction is actually done in
+ * the methods initialise and init.
+ *
+ * Fires `create` event prior to initialisation if not subclassing.
+ */
+ConstructorTemplate =
+exports['ConstructorTemplate'] =
function ConstructorTemplate() {
var cls = arguments.callee
, instance = this;
return instance;
}
+,
+/**
+ * Fires `init` event after calling init(). Note that events are fired
+ * for proper instances of the class and instances of subclasses (but
+ * not for the instance created when subclassing).
+ */
+createInitialise =
+exports['createInitialise'] =
+function createInitialise(NewClass){
+ function initialise(){
+ var instance = this
+ , init = NewClass.prototype.init
+ ;
+
+ instance.__emitter__ = new Emitter(instance, NewClass);
+
+ if (init) {
+ var result = init.apply(instance, arguments);
+ if (result) instance = result;
+ }
+
+ instance.fire('init', instance, {
+ 'instance' : instance,
+ 'cls' : NewClass,
+ 'args' : Y(arguments)
+ });
+
+ return instance;
+ }
+ return Y(initialise);
+}
+;
NewClass.__super__ = SuperClass; // don't override NewClass.constructor -- it should be Function
prototype.constructor = prototype.__class__ = NewClass;
- NewClass.init = // XXX: This means subclasses will fire events.
- prototype.initialise =
- Y(function initialise(){
- var instance = this
- , init = NewClass.prototype.init
- ;
-
- instance.__emitter__ = new Emitter(instance, NewClass);
-
- if (init) {
- var result = init.apply(instance, arguments);
- if (result) instance = result;
- }
-
- instance.fire('init', instance, {
- 'instance' : instance,
- 'cls' : NewClass,
- 'args' : Y(arguments)
- });
-
- return instance;
- });
+ NewClass.init = prototype.initialise = createInitialise(NewClass);
// Add class emitter
var ParentEmitter = (Parent.__emitter__ ? Parent : ClassFactory)
_cssClasses : 'ezl layer',
canvas : null,
-
parent : null,
children : null,
// Transforms
_origin : null, // rotational origin
transform : null, // Object
- useCanvasScaling : false, // default to CSS3 scaling
/// Defaults ///
+ useCanvasScaling : false, // Default to CSS3 scaling
+ alwaysClearDrawing : true, // Whether to clear the canvas content before redraw
+ alwaysClearAttrs : false, // Whether to remove all canvas attributes (CONTEXT_ATTRS)
+ // and transforms (scale, rotate, translate) and reset defaults before redraw
+
originX : 0,
originY : 0,
*/
empty : function empty(ctx){
this.children.invoke('remove');
- this.clear();
+ this.clear(ctx);
return this;
},
* @param {Boolean} [force=false] Forces redraw.
*/
draw : function draw(ctx, force){
+ var _ctx = ctx || this.ctx;
+
if ( this.dirty || force ){
- var _ctx = ctx || this.ctx;
+ this.dirty = false;
this._openPath(_ctx);
- this.drawShape(_ctx);
+ this.render(_ctx);
this._closePath(_ctx);
}
- this.dirty = false;
this.children.invoke('draw', ctx, force);
return this;
},
_openPath : function _openPath(ctx){
var self = this
- , alwaysClear = this.alwaysClear
, neg = this.negBleed
, t = this.transform
, w = this.canvasWidth, h = this.canvasHeight ;
ctx.beginPath();
- ctx.setTransform(1,0,0,1,0,0);
- ctx.clearRect(-w,-h, 2*w,2*h);
+ // TODO: only alwaysClearAttrs should reset transforms?
+ // if (this.alwaysClearAttrs)
+ ctx.setTransform(1,0,0,1,0,0);
+
+ if (this.alwaysClearDrawing)
+ ctx.clearRect(-w,-h, 2*w,2*h);
+
+ // if (this.alwaysClearAttrs) {
if (this.useCanvasScaling && (t.scale.x !== 1 || t.scale.y !== 1))
ctx.scale(t.scale.x,t.scale.y);
ctx.translate(neg.x, neg.y);
ctx.translate(t.translate.x, t.translate.y);
+ // }
// Set context attributes
CONTEXT_ATTRS.forEach(function(name){
- if (self[name] !== undefined)
- ctx[name] = self[name];
- else if (alwaysClear)
+ if (this[name] !== undefined)
+ ctx[name] = this[name];
+ else if (this.alwaysClearAttrs)
delete ctx[name];
- });
+ }, this);
return this;
},
/** To be implemented by subclasses. */
- drawShape : function drawShape(ctx){ return this; },
+ render : function render(ctx){ return this; },
_closePath : function _closePath(ctx){
ctx.closePath();
},
/**
- * Clears this layer and all children.
+ * Clears this layer and optionally all children.
*/
- clear : function clear(ctx){
+ clear : function clear(ctx, clearChildren){
var w = this.canvas.width()
, h = this.canvas.height();
ctx = ctx || this.ctx;
ctx.setTransform(1,0,0,1,0,0);
ctx.clearRect(-w,-h, 2*w,2*h);
ctx.closePath();
- this.children.invoke('clear');
+ if (clearChildren)
+ this.children.invoke('clear');
return this;
},
get origin(){ return this._origin; },
relocate : function relocate(x,y){
+ if (x instanceof Array) { y=x[1]; x=x[0]; }
var _x1 = this[X1], _y1 = this[Y1]
, _x2 = this[X2], _y2 = this[Y2]
},
relocated : function relocated(x,y){
- var bb = this.clone();
- return bb.relocate(x,y);
+ return this.clone().relocate(x,y);
},
resize : function resize(w,h){
+ if (w instanceof Array) { h=w[1]; w=w[0]; }
var x1 = this[X1], y1 = this[Y1]
, x2 = this[X2], y2 = this[Y2]
, wOld = x2-x1, hOld = y2-y1
},
resized : function resized(w,h){
- var bb = this.clone();
- return bb.resize(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);
+ }
}
});
EventLoop =
exports['EventLoop'] =
Emitter.subclass('EventLoop', {
- samples : NUM_SAMPLES, // Number of frames to track for effective fps
- dilation : 1.0,
+ samples : NUM_SAMPLES, // Number of frames to track for effective fps
+ timeDilation : 1.0,
framerate : 0, // Target framerate
frametime : 0, // 1000 / framerate
if (samples !== undefined)
this.samples = samples;
if (dilation !== undefined)
- this.dilation = dilation;
+ this.timeDilation = dilation;
this.reset();
},
return delegate;
}
+
return this;
},
+ /**
+ * @return {Vec} Delta-(x,y) on this line after given elapsed parametric time.
+ */
+ wouldMove : function wouldMove(t){
+ return new Vec( t*this.pa, t*this.pb );
+ },
+
+ /**
+ * @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;
+ },
+
parametric : function parametric(t){
+ // return this.wouldMove(t).add(this.p1);
return new Vec( this.x1 + t*this.pa ,
this.y1 + t*this.pb );
},
iparametric : function iparametric(x,y){
- return new Vec( (x-this.x1)/this.pa ,
- (y-this.y1)/this.pa );
+ var tx = (x - this.x1)/this.pa
+ , ty = (y - this.y1)/this.pb;
+ // tx and ty should be the same number provided (x,y) is on the line
+ // we average to provide a reasonable answer when that's not true,
+ // rather than failing outright or requiring the user to deal with a Vec.
+ return (tx + ty) * 0.5;
},
pointAtX : function pointAtX(x){ return new Vec(x, this.calcY(x)); },
return this;
},
- drawShape : function drawShape(ctx){
+ render : function render(ctx){
var r = this._radius;
ctx.arc(r,r, r, 0, Math.PI*2, false);
ctx.fill();
return this;
},
- drawShape : function drawShape(ctx){
+ render : function render(ctx){
this._fixSize();
var x1,y1, x2,y2
, line = this.line, p1 = line.p1, p2 = line.p2
ctx.closePath();
// } catch(e) {
// if (window.console) {
- // console.error(this+'.drawShape()');
+ // console.error(this+'.render()');
// console.log(' points:', x1,y1, ' ', x2,y2);
// console.log(' bounds:', minW,minH, ' ', maxW,maxH);
// console.log(' ::', this, line);
return values.map(function(v, i){ return (self[which+i] = v); });
},
- drawShape : function drawShape(ctx){
+ render : function render(ctx){
this.points.forEach(function(loc, i){
ctx.lineTo(loc.x, loc.y);
});
this.size(w,h);
},
- drawShape : function drawShape(ctx){
+ render : function render(ctx){
ctx.rect(0,0, this.canvasWidth,this.canvasHeight);
ctx.fill();
+ ctx.stroke();
},
toString : function(){
+++ /dev/null
-var Y = require('Y').Y
-, getNested = require('Y/types/object').getNested
-, Emitter = require('Y/modules/y.event').Emitter
-,
-
-
-Config =
-exports['Config'] =
-Y.YObject.subclass('Config', function(Config){
-
- Y.core.extend(this, {
-
- init : function initConfig(defaults){
- this._defaults = defaults || {};
- this._o = Y({}, this._defaults);
- this.__emitter__ = new Emitter(this);
- },
-
- clone : function clone(){
- var c = new Config();
- c._defaults = this._defaults;
- c._o = c._o.clone();
- return c;
- },
-
- set : function set(key, value, def){
- if ( key !== undefined ){
- var meta = this.ensure(key).getNested(key, def, true)
- , old = meta.value ;
- def = (def === undefined ? old : def);
- value = (value === undefined ? def : value);
-
- meta.obj[meta.key] = value;
- this._fireMutation('set', key, old, value, meta.obj);
- }
- return this;
- },
-
- remove : function remove(key){
- if ( key !== undefined ){
- var sentinel = {}
- , meta = this.getNested(key, sentinel, true)
- , old = (meta.value === sentinel ? undefined : old);
-
- if ( meta.obj ) {
- delete meta.obj[meta.key];
- this._fireMutation('remove', key, old, undefined, meta.obj);
- }
- }
- return this;
- },
-
- /**
- * @private
- */
- _fireMutation : function _fireMutation(evt, key, oldval, newval, obj){
- if (newval !== oldval){
- var data = {
- 'key' : key,
- 'oldval' : oldval,
- 'newval' : newval,
- 'obj' : obj,
- 'root' : this
- };
- this.fire(evt+':'+key, this, data);
- this.fire(evt, this, data);
- }
- return this;
- },
-
- /**
- * Recursively iterates the hierarchy of the Config, depth-first.
- */
- reduce : function reduce(fn, acc, context){
- context = context || this;
-
- var ret = Y.reduce(this._o, this._reducer, {
- 'acc' : acc,
- 'path' : new Y.YArray(),
- 'cxt' : context
- }, this);
-
- return ret.acc;
- },
-
- /**
- * @private
- * XXX: Not going to call the iterator on root objects. ok?
- * XXX: Always passing the Config object as obj to iterator. ok?
- */
- _reducer : function _reducer(state, v, k, o){
- var chain = state.path.push(k).join('.');
-
- // Nested object -- recurse
- if ( Y.isPlainObject(v) )
- state = Y.reduce(v, this._reducer, state, this);
-
- // Normal value -- invoke iterator
- else
- state.acc = fn.call(state.cxt, state.acc, v, chain, this);
-
- state.path.pop();
- return state;
- },
-
- getDefault : function getDefault(k, def){
- return getNested(this._defaults, k, def);
- }
-
- });
-
- this['get'] = this.getNested;
-
-});
return Layer.fn.draw.apply(this, arguments);
};
- this['drawShape'] =
- function drawShape(ctx){
+ this['render'] =
+ function render(ctx){
var cool = this.cooldown;
if (cool.ready)
// -*- mode: JavaScript; tab-width: 4; indent-tabs-mode: nil; -*-
-var Y = require('Y').Y
+var Y = require('Y').Y
, Config = require('Y/modules/y.config').Config
+, Vec = require('ezl/math').Vec
,
defaults =
showAttackCooldown : false,
showCountdown : (document.location.host.toString() !== 'tanks.woo')
},
- pathing : {
- overlayAiPaths : false,
- overlayPathmap : false,
- traceTrajectories : false
+ pathing : {
+ gridSquare : REF_SIZE,
+ gridSquareMid : new Vec(REF_SIZE/2, REF_SIZE/2),
+ overlayAiPaths : false,
+ overlayPathmap : false,
+ traceTrajectories : false
}
-};
+}
+,
+
+config =
+exports['config'] = new Config(defaults)
+;
+
+config.addEventListener('set:pathing.gridSquare', function(evt){
+ var sq = evt.data.newval;
+ config.set('pathing.gridSquareMid', new Vec(sq/2, sq/2));
+});
-exports['values'] = new Config(defaults);
, EventLoop = require('ezl/loop').EventLoop
, Circle = require('ezl/shape').Circle
-, config = require('tanks/config').values
-, map = require('tanks/map')
-, thing = require('tanks/thing')
-, Grid = require('tanks/ui/grid').Grid
+, config = require('tanks/config').config
+, map = require('tanks/map')
+, thing = require('tanks/thing')
+, Grid = require('tanks/ui/grid').Grid
+, PathMapUI = require('tanks/ui/pathmapui').PathMapUI
, Level = map.Level
, Thing = thing.Thing, Tank = thing.Tank, Bullet = thing.Bullet
Game =
exports['Game'] =
Y.subclass('Game', {
- overlayPathmap : null,
- overlayAiPaths : null,
- gameoverDelay : null,
+ // Config
+ gameoverDelay : null,
+
+ // Defaults
+ gameover : false,
- gameover :