// Add YArray to the things that count as arrays
type.isArray.types.push(YArray);
+ var newYArray = YArray.instantiate.bind(YArray);
+
+ mixin(YArray, { donor:_Array,
+ names:'indexOf lastIndexOf shift pop join'.split(' ') });
mixin(YArray, { donor:_Array, chain:true,
names:'push unshift sort splice reverse'.split(' ') });
- mixin(YArray, { donor:_Array, wrap:YArray.instantiate.bind(YArray),
- names:'map forEach filter slice'.split(' ') });
- mixin(YArray, { donor:_Array,
- names:'reduce some every indexOf lastIndexOf shift pop join'.split(' ') });
+ mixin(YArray, { donor:_Array, 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'] });
+
this['init'] =
return new YArray(this._o.slice(0));
};
+ var size =
this['size'] =
function size(){
return this._o.length;
};
+ Object.defineProperty(this, 'length', { 'get':size });
this['toString'] =
function toString(){
return s;
}
+ function size(){ return this._o.length; }
+ Object.defineProperty(this, 'length', { 'get':size });
+
core.extend(this, {
'init' : function init(o){
if (!o) o = "";
return YString(this._o);
},
+ 'size' : size,
+
/**
* As Array.slice -- replaces `howMany` elements starting at `idx`
* with the concatenation of the rest of the arguments.
'names' : null,
'override' : true,
'wrap' : false,
- 'chain' : false
+ 'chain' : false,
+ 'fnFirst' : false
};
function mixin(o, options){
var opt = core.extend({}, defaults, options), Donor = opt.donor
var fn = proto[name];
if ( isFunction(fn) && (opt.override || !(name in target)) )
target[name] = function(){
- var r = fn.apply(this._o || target, arguments);
+ 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));
};
});
Class.__super__ = Object;
Class.__emitter__ = new Emitter(Class);
Class.fn = Class.prototype;
-Class.fn.__class__ = Class;
+Class.fn.__class__ = Class.fn.constructor = Class;
Class.className = Class.fn.className = "Class";
/* Class Methods */
, loc = require('ezl/loc')
, Loc = loc.Loc
, BoundingBox = loc.BoundingBox
+, Animation = require('ezl/loop/fx').Animation
,
CONTEXT_ATTRS = Y([
'globalAlpha', 'globalCompositeOperation',
Y.subclass('Layer', {
_cssClasses : 'ezl layer',
- canvas : null,
+ canvas : null,
- parent : null,
- children : null,
+ parent : null,
+ children : null,
- ctx : null,
- dirty : true,
+ dirty : true,
+ ctx : null,
+ animActive : null,
+ animQueue : null,
layerWidth : 0, canvasWidth : 0,
/// Setup ///
init : function init(){
- this.children = new Y.YArray();
+ this.children = new Y.YArray();
+ this.animActive = new Y.YArray();
+ this.animQueue = new Y.YArray();
this.loc = new Loc(0,0);
this.negBleed = new Loc(0,0);
this.layer[0].layer =
this.canvas[0].layer = this;
+
+ this.tick = this.tick.bind(this);
},
/// Scene Graph Heirarchy ///
return this;
},
- animate : function animate(props, options) {
+ /**
+ * @param {Number} duration Duration (ms) of animation.
+ * @param {Object} props Object whose keys are the property-names to animate
+ * paired with the target value. Animated properties can also be relative.
+ * If a value is supplied with a leading += or -= sequence of characters,
+ * then the target value is computed by adding or subtracting the given
+ * number from the current value of the property.
+ * @param {Object} [options] Animation options:
+ * * {Number} [delay=0] Milliseconds to wait before beginning animation.
+ * * {Boolean} [queue=true] Indicates whether to place the animation
+ * in the effects queue to run when other animations have finished.
+ * If false, the animation will begin after `delay` milliseconds;
+ * likewise, if `delay` is non-zero `queue` defaults to `false`
+ * instead.
+ * * {String|Function|Object} [easing='linear'] Name of easing function
+ * used to calculate each step value, or a function, or a map of
+ * properties to either of the above.
+ */
+ animate : function animate(duration, props, options) {
+ options = Y.extend({
+ 'delay' : 0,
+ 'queue' : undefined,
+ 'easing' : 'linear',
+ 'complete' : null,
+ 'step' : null
+ }, options);
+
+ if ( options.queue === undefined )
+ options.queue = !options.delay;
+
+ var A = new Animation(this, duration, props, options);
+ if ( options.queue && this.animActive.length )
+ this.animQueue.push(A);
+ else
+ this.animActive.push(A.start(NOW)); // XXX: Need another way to pass NOW in
+ return this;
+ },
+
+ tick : function tick(elapsed, now){
+ if (elapsed instanceof Event) {
+ var d = evt.data;
+ now = d.now;
+ elapsed = d.elapsed;
+ }
+ this.animActive = this.animActive.filter(function(anim){
+ return anim.tick(elapsed, now);
+ });
+ if ( !this.animActive.length && this.animQueue.length )
+ this.animActive.push( this.animQueue.shift().start(now) );
},
*/
Easing =
exports['Easing'] = {
+ 'lookup' : function lookupEasing(name, prop){
+ if ( Y.isFunction(name) )
+ return name;
+ if (name in Easing)
+ return Easing[name];
+ if ( !(Y.isPlainObject(name) && prop in name) )
+ throw new Error('Unknown easing function '+name+'!');
+ return lookupEasing(name[prop]);
+ },
+
'linear' : function linearEasing( p, elapsed, start, span, duration ) {
return start + span * p;
},
,
-lookupEasing =
-exports['lookupEasing'] =
-function lookupEasing(name, prop){
- if ( Y.isFunction(name) )
- return name;
- if (name in Easing)
- return Easing[name];
- if ( !(Y.isPlainObject(name) && prop in name) )
- throw new Error('Unknown easing function '+name+'!');
- return lookupEasing(name[prop]);
-}
-,
-
-
Fx =
exports['Fx'] =
Y.subclass('Fx', function setupFx(Fx){
this['start'] =
function start(now){
- if (this.running) return;
+ if (this.running) return this;
this.startTime = now || new Date().getTime();
this.running = true;
+ return this;
};
this['stop'] =
function stop(){
this.running = false;
+ return this;
};
this['tick'] =
if (!this.running)
return false;
- var p = (now - this.startTime) / this.duration
+ var p = Math.max(1, (now - this.startTime) / this.duration)
, v = this.cur = this.start + this.easing(p, 0, this.span, this.duration) ;
Y.attr(this.obj, this.prop, ( this.unit !== null ? v+this.unit : v ));
+ if (p === 1) {
+ this.running = false;
+ return false;
+ }
+
return true;
};
})
this.fx = Y(this.props)
.reduce(function(fx, val, prop){
- var easing = lookupEasing(options.easing, prop);
+ var easing = Easing.lookup(options.easing, prop);
return fx.push(new Fx(obj, duration, prop, val, easing));
}, Y([]) );
- // TODO: Handle Queuing -- should happen in target objects who provide the API
+ this.tick = this.tick.bind(this);
};
this['start'] =
function start(now){
- if (this.running) return;
+ if (this.running) return this;
this.started = now || new Date().getTime();
this.running = true;
this.waiting = !!this.options.delay;
+ return this;
+ };
+
+ this['stop'] =
+ function stop(){
+ this.running = false;
+ return this;
};
+ // Runs in the context of the Animation
+ function tickFx(fx, idx){
+ return fx.tick(this.elapsed, this.now);
+ }
+
this['tick'] =
function tick(elapsed, now){
+ this.now = now;
+ this.elapsed = elapsed;
+
if (this.waiting && (now < this.started+this.options.delay))
return true;
-
this.waiting = false;
- return true;
+ this.fx = this.fx.filter(tickFx, this);
+
+ return !!this.fx.length;
};
});
-var Y = require('Y').Y;
+var Y = require('Y').Y
+, fx = require('ezl/loop/fx')
+;
Y.extend(exports, {
'EventLoop' : require('ezl/loop/eventloop').EventLoop,
'Cooldown' : require('ezl/loop/cooldown').Cooldown,
- 'FpsSparkline' : require('ezl/loop/fps').FpsSparkline
+ 'FpsSparkline' : require('ezl/loop/fps').FpsSparkline,
+ 'Animation' : fx.Animation,
+ 'Easing' : fx.Easing,
+ 'Fx' : fx.Fx
});
var Y = require('Y').Y
, Event = require('Y/modules/y.event').Event
, EventLoop = require('ezl/loop').EventLoop
+, Circle = require('ezl/shape').Circle
, config = require('tanks/config')
, map = require('tanks/map')
this.active = new Y.YArray();
this.units = new Y.YArray();
this.bullets = new Y.YArray();
+ this.animations = new Y.YArray();
this.viewport = $(viewport || GRID_ELEMENT);
this.loop = new EventLoop(this, FRAME_RATE);
Thing.addEventListener('create', this.addUnit);
- Thing.addEventListener('destroy', this.killUnit);
+ Thing.addEventListener('destroy', this.explodeUnit);
this.addEventListener('tick', this.tick);
this.active.invoke('updateCooldowns', NOW);
this.active.invoke('act');
+ this.animations = this.animations.filter(this.tickAnimations, this);
this.draw();
},
+ tickAnimations : function tickAnimations(animation){
+ return animation.tick(ELAPSED, NOW);
+ },
return agent;
},
+ explodeUnit : function explodeUnit(unit){
+ if (unit instanceof Event)
+ unit = unit.instance;
+
+ this.killUnit(unit);
+ var loc = unit.loc
+ , size = Math.max(unit.width, unit.height)
+ , exp = new Circle(3)
+ .position(loc.x,loc.y)
+ .fill('#980011')
+ .animate(1000, { 'radius':size })
+ .appendTo(this.level);
+
+ this.animations.push(exp);
+
+ return unit;
+ },
+
/**
remove : function remove(){
if (this.shape) this.shape.remove();
if (this.tline) this.tline.remove();
- if (this.game) this.game.killUnit(this);
+ // if (this.game) this.game.killUnit(this);
return this;
},
remove : function remove(){
if (this.shape) this.shape.remove();
- if (this.game) this.game.killUnit(this);
+ // if (this.game) this.game.killUnit(this);
return this;
},
<script src="build/ezl/math/vec.js" type="text/javascript"></script>
<script src="build/ezl/loop/cooldown.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/loop.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/loop.js" type="text/javascript"></script>
<script src="build/ezl/loc/boundingbox.js" type="text/javascript"></script>
<script src="build/ezl/loc/square.js" type="text/javascript"></script>
<script src="build/ezl/loc.js" type="text/javascript"></script>
<script src="build/ezl.js" type="text/javascript"></script>
<script src="build/jquery.hotkeys.min.js" type="text/javascript"></script>
<script src="build/tanks/globals.js" type="text/javascript"></script>
-<script src="build/evt.js" type="text/javascript"></script>
<script src="build/ezl/util/binaryheap.js" type="text/javascript"></script>
<script src="build/ezl/util/astar.js" type="text/javascript"></script>
<script src="build/tanks/config.js" type="text/javascript"></script>