Checkpoint with Mixins.
authordsc <david.schoonover@gmail.com>
Sat, 1 Jan 2011 14:44:45 +0000 (06:44 -0800)
committerdsc <david.schoonover@gmail.com>
Sat, 1 Jan 2011 14:44:45 +0000 (06:44 -0800)
29 files changed:
src/Y/class.cjs
src/Y/core.cjs
src/Y/delegate.cjs
src/Y/index.cjs
src/Y/modules/y.scaffold.cjs
src/Y/op.cjs
src/Y/types/array.cjs
src/Y/types/collection.cjs
src/evt.cjs
src/ezl/layer/layer.cjs
src/ezl/loc/loc.cjs
src/ezl/loop/eventloop.cjs
src/ezl/math/line.cjs
src/ezl/math/rect.cjs
src/ezl/math/vec.cjs
src/future.js
src/tanks/constants.cjs
src/tanks/effects/buff.cjs
src/tanks/effects/stat.cjs
src/tanks/map/level.cjs
src/tanks/map/pathmap.cjs
src/tanks/map/trajectory.cjs
src/tanks/map/traversal.cjs
src/tanks/thing/bullet.cjs
src/tanks/thing/item.cjs
src/tanks/thing/player.cjs
src/tanks/thing/tank.cjs
src/tanks/thing/thing.cjs
www/deps.html

index 6848b76..683f032 100644 (file)
@@ -198,27 +198,6 @@ Class.fn.subclass = YFunction(subclass);
 var
 
 /**
- * Everybody's favourite party metaclass!
- */
-Mixin =
-exports['Mixin'] =
-Y.subclass('Mixin', {
-    __mixin_skip__ : [ 'mixIntoClass' ],
-    
-    
-    
-    /**
-     * Mixes this Mixin into another Class.
-     */
-    mixIntoClass : function mixIntoClass(cls){
-        
-    },
-    
-})
-,
-
-
-/**
  * Root-class for all Y-objects.
  */
 YBase = new Class("YBase", {
index d400314..1c237cd 100644 (file)
@@ -131,7 +131,8 @@ function attrvk(o, v, k){   return attr(o, k, v, o[k]); }
  * @return {Object} A.
  */
 function accessors(A, B){
-    if ( !(A && typeof A === "object") )
+    var t = typeof A;
+    if ( !(A && (t === "object" || t === "function")) )
         return A;
     
     if (arguments.length < 2)
@@ -173,7 +174,8 @@ function _copyAccessors(desc, donor){
  * @return {Object} A.
  */
 function descriptors(A, B){
-    if ( !(A && typeof A === "object") )
+    var t = typeof A;
+    if ( !(A && (t === "object" || t === "function")) )
         return A;
     
     if (arguments.length < 2)
index 7a68061..b689067 100644 (file)
@@ -109,7 +109,8 @@ function extendall(A, donor){ return reduce(donor, attrvk, A); }
 function attrvk(o, v, k){     return attr(o, k, v, o[k]); }
 
 function accessors(A, B){
-    if ( !(A && typeof A === "object") )
+    var t = typeof A;
+    if ( !(A && (t === "object" || t === "function")) )
         return A;
     
     if ( notWrapped(A.accessors) )
@@ -119,7 +120,8 @@ function accessors(A, B){
 }
 
 function descriptors(A,B){
-    if ( !(A && typeof A === "object") )
+    var t = typeof A;
+    if ( !(A && (t === "object" || t === "function")) )
         return A;
     
     if ( notWrapped(A.descriptors) )
index cddbcd4..5dd82ff 100644 (file)
@@ -38,8 +38,7 @@ Y['is'] = YFunction(Y.is).curry();
 addNames('curry methodize genericize compose chain memoize', yfn);
 
 // Curry all operators
-var op = require('Y/op');
-Y['op'] =
+var op = Y['op'] = require('Y/op');
 op['curried'] = 
     core.reduce(op, function(Yop, fn, k){
         Yop[k] = YFunction(fn).curry();
@@ -56,9 +55,7 @@ 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;
+addNames('bindAll',       require('Y/utils'));
 
 function addName(name){ Y[name] = this[name]; }
 function addNames(names, ns){ names.split(' ').forEach(addName, ns); }
index 717b7c9..09973d7 100644 (file)
@@ -1,7 +1,7 @@
 //#ensure "jquery"
-var Y  = require('Y').Y
-,   Emitter   = require('Y/modules/y.event').Emitter
-,   op = Y.op
+var Y = require('Y').Y
+,   parseBool = require('Y/op').parseBool
+,   Emitter = require('Y/modules/y.event').Emitter
 
 
 ,   upperPat = /^[A-Z]+$/
@@ -20,7 +20,7 @@ function camelToSpaces(s){
 ,
 
 type2parser = {
-    'Boolean'  : op.parseBool,
+    'Boolean'  : parseBool,
     'Number'   : parseFloat,
     'String'   : String
 }
index 7a9e6ca..ab33442 100644 (file)
@@ -38,7 +38,7 @@ var core = require('Y/core')
     zrshift   : function(x,y){  return x >>> y; },
     
     // values
-    nop       : function(x){},
+    nop       : function(){},
     I         : function(x){       return x; },
     K         : function(k){       return function(){ return k; }; },
     nth       : function(n){       return function(){ return arguments[n]; }; },
index 0f2700c..9ffe267 100644 (file)
@@ -51,17 +51,16 @@ YCollection.subclass('YArray', function(YArray){
     Object.defineProperty(this, 'length', { 'get':size });
     
     // Convenience getters for index 0-9
-    var thisget   = op.thisget
-    ,   thiskvset = op.thiskvset ;
-    for (var i=0; i<10; i++)
-        Object.defineProperty(this, i, {
-            'get' : thisget.partial(i),
-            'set' : thiskvset.partial(i)
-        });
+    // var thisget   = op.thisget
+    // ,   thiskvset = op.thiskvset ;
+    // for (var i=0; i<10; i++)
+    //     Object.defineProperty(this, i, {
+    //         'get' : thisget.partial(i),
+    //         'set' : thiskvset.partial(i)
+    //     });
     
     
-    this['toString'] =
-    function toString(){
+    this['toString'] = function(){
         return "Y[" + arrayToString.call(this._o) + "]";
     };
     
index 8611eb7..fcb4875 100644 (file)
@@ -10,7 +10,7 @@ var YBase  = require('Y/class').YBase
 
 
 function extendY(){
-    del.extend.apply(this, [this._o].concat(slice.call(arguments)));
+    del.extend.apply(this, [this._o].concat(slice.call(arguments,0)));
     return this;
 }
 
@@ -49,6 +49,8 @@ YBase.subclass('YCollection', {
             return r;
     },
     
+    'extend' : extendY,
+    
     /**
      * Removes all instances of the given values from the collection.
      * 
index 61462cf..c4850d2 100644 (file)
@@ -24,6 +24,7 @@ Note: Metaprogramming events cannot depend on Class.
 */
 
 var Y       = require('Y').Y
+,   core    = require('Y/core')
 ,   Emitter = require('Y/modules/y.event').Emitter
 ,   unwrap  = require('Y/types/function').unwrap
 
@@ -39,34 +40,45 @@ var Y       = require('Y').Y
 
 ,   objToString   = _Object[P].toString
 ,   classToString = function toString(){ return this.className+"()"; }
+,   classMagic    = [ '__static__', '__mixins__', '__emitter__', '__bases__', '__initialise__' ]
+,   mixinSkip     = [ '__mixin_skip__', 'onMixin', 'onInit', 'init' ].concat(classMagic)
 ,
 
 
 /**
  * @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.
+ * new class to prevent shared state.
  * 
  * Fires `create` event prior to initialisation if not subclassing.
+ * 
+ * Fires `init` event after calling init(). nb. init() is fired
+ * for proper instances of the class, and instances of subclasses (but
+ * not for the instance created when subclassing).
  */
 ConstructorTemplate =
 exports['ConstructorTemplate'] =
 function ConstructorTemplate() {
-    var cls = arguments.callee
-    ,   instance = this;
+    var k, v
+    ,   cls = arguments.callee
+    ,   instance = this
+    ;
     
     // Not subclassing
     if ( unwrap(cls.caller) !== Y.fabricate ) {
         
+        // Perform actions that should only happen once in Constructor
+        instance.__emitter__ = new Emitter(instance, cls);
+        
         cls.fire('create', instance, {
             'instance' : instance,
             'cls'      : cls,
             'args'     : Y(arguments)
         });
         
-        if ( instance.initialise )
-            return instance.initialise.apply(instance, arguments);
+        var initialise = instance.__initialise__;
+        if ( isFunction(initialise) )
+            return initialise.apply(instance, arguments);
     }
     
     return instance;
@@ -74,37 +86,41 @@ function ConstructorTemplate() {
 ,
 
 /**
- * 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).
+ * Class must be captured in a closure so we can dispatch events correctly both
+ * on a new instance and off a call from a subclass by invoking
+ * MyClass.init.apply(instance, args).
  */
 createInitialise =
 exports['createInitialise'] =
-function createInitialise(NewClass){
+function createInitialise(cls){
     function initialise(){
         var instance = this
-        ,   init     = NewClass.prototype.init
+        ,   binds = cls.fn.__bind__ // list of names to bind
         ;
         
-        instance.__emitter__ = new Emitter(instance, NewClass);
+        if (binds)
+            Y.bindAll(instance, binds);
         
-        if (init) {
+        var init = cls.fn.init;
+        if ( isFunction(init) ) {
             var result = init.apply(instance, arguments);
-            if (result) instance = result;
+            if (result) instance = result; // XXX: I think this needs to go away
         }
         
         instance.fire('init', instance, {
             'instance' : instance,
-            'cls'      : NewClass,
+            'cls'      : cls,
             'args'     : Y(arguments)
         });
         
         return instance;
     }
     return Y(initialise);
-}
-;
+};
 
+function notClassMagic(v, k){
+    return classMagic.indexOf(k) === -1;
+}
 
 
 
@@ -192,28 +208,46 @@ function Class(className, Parent, members){
     NewClass.prototype = NewClass.fn = prototype;
     
     // Fix Constructors
-    NewClass.__super__    = SuperClass; // don't override NewClass.constructor -- it should be Function
+    NewClass.__super__ = SuperClass; // don't override NewClass.constructor -- it should be Function
+    NewClass.__bases__ = NewClass.fn.__bases__ = [SuperClass].concat( SuperClass.__bases__ || [ Class, Object ] );
     prototype.constructor = prototype.__class__ = NewClass;
     
-    NewClass.init = prototype.initialise = createInitialise(NewClass);
+    NewClass.init = prototype.__initialise__ = createInitialise(NewClass);
     
     // Add class emitter
     var ParentEmitter = (Parent.__emitter__ ? Parent : ClassFactory)
     ,   ClassEmitter  = NewClass.__emitter__ = new Emitter(NewClass, ParentEmitter)
     ;
     
+    // Record for metaprogramming
+    KNOWN_CLASSES[className] = NewClass;
+    
     // Either invoke body constructor...
     if ( isFunction(members) ) {
         members.call(prototype, NewClass);
     
     // Or add new instance methods
-    } else for (var k in members) {
-        if ( hasOwn.call(members, k) )
-            setDesc(prototype, k, getDesc(members,k));
+    } else {
+        var mixins  = members.__mixins__
+        ,   statics = members.__static__
+        ;
+        
+        if (mixins && hasOwn.call(members,'__mixins__'))
+            mixin(NewClass, mixins);
+        
+        if (statics)
+            core.descriptors( NewClass, core.filter(statics, notClassMagic) );
+        
+        core.descriptors( prototype, core.filter(members, notClassMagic) );
+        // for (var k in members) {
+        //     if ( hasOwn.call(members,k) && classMagic.indexOf(k) === -1 )
+        //         setDesc(prototype, k, getDesc(members,k));
+        // }
+        
+        NewClass.__super__    = SuperClass;
+        prototype.constructor = prototype.__class__ = NewClass;
     }
     
-    // Record for metaprogramming
-    KNOWN_CLASSES[className] = NewClass;
     
     // Notify parent of the subclass
     ParentEmitter.fire('subclass',
@@ -234,6 +268,7 @@ Class.__super__ = Object;
 Class.__emitter__ = new Emitter(Class);
 Class.fn = Class.prototype;
 Class.fn.__class__ = Class.fn.constructor = Class;
+Class.fn.__bases__ = Class.__bases__ = [ Object ];
 Class.className = Class.fn.className = "Class";
 
 /* Class Methods */
@@ -262,9 +297,97 @@ Class.fn.subclass =
     });
 
 
+function lookupClass(name){
+    return (typeof name === "function") ? name : KNOWN_CLASSES[name];
+}
+
+var
+
+/**
+ * Everybody's favourite party metaclass!
+ */
+Mixin =
+exports['Mixin'] =
+new Class('Mixin', Class, {
+    
+    /**
+     * Keys to be skipped by mixIntoClass()
+     */
+    __mixin_skip__ : mixinSkip,
+    
+    __mixin_skip : function __mixin_skip(k){
+        var skip = this.__mixin_skip__;
+        return !hasOwn.call(this,k) || (skip && skip.indexOf(k) !== -1);
+    },
+    
+    __static__ : {
+        mixInto : function mixIntoClass(cls){
+            var mxn = this;
+            return mixin(cls, mxn);
+        }
+    }
+    
+})
+,
+
+/**
+ * Mixes a Mixin into another Class.
+ */
+mixin =
+exports['mixin'] =
+function mixin(cls, mxn){
+    var proto = cls.fn
+    ,   mxns = (Y.isArray(mxn) ? mxn : Y(arguments, 1))
+    ;
+    mxns.forEach(function(mxn){
+        mxn = (typeof mxn === "string") ? lookupClass(mxn) : mxn;
+        
+        if ( !mxn )
+            throw new Error('Cannot mix in non-object! '+mxn);
+        
+        var mproto = (typeof mxn === "function") ? mxn.fn : mxn
+        ,   bases   = cls.__bases__
+        ,   statics = mproto.__static__
+        ,   onInit  = mproto.onInit
+        ;
+        
+        core.extend(proto, core.filter(mproto, mproto.__mixin_skip, mproto));
+        
+        if (bases)
+            bases.push(mxn);
+        
+        // Add mixin statics to class constructor
+        if (statics)
+            core.extend(cls, statics);
+        
+        // Register onInit to fire whenever a new instance is initialized
+        if ( hasOwn.call(mproto,'onInit') && isFunction(onInit) )
+            cls.addEventListener('init', onInit);
+        
+        // Fire the mixin event on this Mixin
+        if (mxn instanceof Mixin)
+            mxn.fire('mixin', cls, { 'mixin':mxn, 'into':cls });
+    });
+    return cls;
+}
+;
+
+Mixin.addEventListener('subclass',
+    function onMixinSubclass(evt){
+        var d       = evt.data
+        ,   mxn     = d.child
+        ,   members = d.members
+        ,   onMixin = members.onMixin
+        ;
+        
+        if ( hasOwn.call(members,'onMixin') && isFunction(onMixin) )
+            mxn.addEventListener('mixin', onMixin);
+    });
+
+
 // Expose
 exports['Class'] =
 exports['subclass']    = Class;
 exports['instantiate'] = instantiate;
 exports['fabricate']   = Y.fabricate;
-
+exports['lookupClass'] = lookupClass;
index beaa2ac..1f2bd73 100644 (file)
@@ -1,5 +1,6 @@
 //#ensure "jquery"
 var Y           = require('Y').Y
+,   op          = require('Y/op')
 ,   Vec         = require('ezl/math/vec').Vec
 ,   Loc         = require('ezl/loc/loc').Loc
 ,   BoundingBox = require('ezl/loc/boundingbox').BoundingBox
@@ -583,7 +584,7 @@ Y.subclass('Layer', {
             return anim.tick(elapsed, now);
         });
         
-        var childrenRunning = this.children.invoke('tick', elapsed, now).some(Y.op.I)
+        var childrenRunning = this.children.invoke('tick', elapsed, now).some(op.I)
         ,   running = !!this.animActive.length;
         if ( !running && this.animQueue.length )
             this.animActive.push( this.animQueue.shift().start(now) );
@@ -620,7 +621,7 @@ Y.subclass('Layer', {
     
     
     /// Misc ///
-    toString : function toString(){
+    toString : function(){
         var pos = ((this.layer && this.layer.parent()[0]) ? this.position() : {top:NaN, left:NaN});
         return this.className+'['+pos.left+','+pos.top+']( children='+this.children.size()+' )';
     }
index 67471d0..ddbb350 100644 (file)
@@ -57,7 +57,7 @@ Y.subclass('Loc', new Vec(0,0), {
                , 'y' : Y.isNumber(y) ? y+units : y };
     },
     
-    toString : function toString(){
+    toString : function(){
         var x = this.x, y = this.y;
         x = Y.isNumber(x) ? x.toFixed(2) : x;
         y = Y.isNumber(y) ? y.toFixed(2) : y;
index 5bc35dd..7ba9fc9 100644 (file)
@@ -1,6 +1,7 @@
 //#ensure "Y/modules/y.event"
 
 var Y = require('Y').Y
+,   op = require('Y/op')
 ,   Emitter = require('Y/modules/y.event').Emitter
 
 ,   MIN_TICK_INTERVAL = 0 // ms
@@ -122,11 +123,11 @@ Emitter.subclass('EventLoop', {
     },
     
     fps : function fps(){
-        return 1000 / (this.times.reduce(Y.op.add,0) / this.times.length);
+        return 1000 / (this.times.reduce(op.add,0) / this.times.length);
     },
     
     frametime : function frametime(){
-        return (this.realtimes.reduce(Y.op.add,0) / this.realtimes.length);
+        return (this.realtimes.reduce(op.add,0) / this.realtimes.length);
     }
     
 })
index e19ae19..a31eb95 100644 (file)
@@ -152,7 +152,7 @@ Vec.subclass('Line', {
         return new Line(x1,y1, x2,y2, this.tdist);
     },
     
-    toString : function toString(){
+    toString : function(){
         return '['+this.p1+', '+this.p2+', slope='+this.slope.toFixed(3)+']';
     }
     
index 0d665e4..f0d09c3 100644 (file)
@@ -86,7 +86,7 @@ Y.subclass('Rect', [], {
         return new this.__class__(this[X1],this[Y1], this[X2],this[Y2]);
     },
     
-    toString : function toString(){
+    toString : function(){
         return '['+this.p1+' '+this.p2+']';
     }
 });
index 00c88ff..3a9deb0 100644 (file)
@@ -95,7 +95,7 @@ new Y.Class('Vec', [], {
         return new this.__class__(this[_X],this[_Y]);
     },
     
-    toString : function toString(){
+    toString : function(){
         var p = 2, x = this[_X], y = this[_Y];
         x = ((x % 1 !== 0) ? x.toFixed(p) : x);
         y = ((y % 1 !== 0) ? y.toFixed(p) : y);
index c3270d1..561c791 100644 (file)
@@ -160,7 +160,8 @@ if ( !_Object.defineProperty ) {
     
     _Object['defineProperty'] =
         function defineProperty(o, prop, desc){
-            if (typeof o !== "object" || o === null)
+            var t = typeof o;
+            if (o === null || !(t === "object" || t === "function"))
                 throw new TypeError("Object.defineProperty called on non-object: "+o);
             
             desc = cleanDescriptor(desc);
@@ -177,7 +178,8 @@ if ( !_Object.defineProperty ) {
     
     _Object['defineProperties'] =
         function defineProperties(o, props) {
-            if (typeof o !== "object" || o === null)
+            var t = typeof o;
+            if (o === null || !(t === "object" || t === "function"))
                 throw new TypeError("Object.defineProperty called on non-object: "+o);
             
             props = Object(props);
index f35f003..b44aa33 100644 (file)
@@ -1,5 +1,6 @@
 //#exports PathingType.{PASSABLE,ZONE,BLOCKING,IRREGULAR}
 //#exports StatInvariant.{NONE,RATIO,FULL}
+
 require('Y').Y.extend(exports, {
     
     /// Pathing Constants ///
index 84375bb..83d276f 100644 (file)
@@ -18,9 +18,9 @@ Y.subclass('Buff', {
         stacked : 1,
         threat  : 1
     },
-    stat_mods : {}, // {stat : Number|Function} Modifications to stats
+    stat_mods : {}, // {stat : Number|Function} Modifications to stats // TODO: functions
+    // effects   : [], // {Function...} Effects to trigger on affect // XXX: not sure why i need this...
     // triggers  : {}, // {event : Effect} // maybe
-    // effects   : [], // {Function...} Effects to trigger on affect // not sure why i need this
     
     /// Instance ///
     owner : null, // Agent which originated the buff
@@ -52,7 +52,7 @@ Y.subclass('Buff', {
         return this.applyStatMods(-1);
     }
     
-    // XXX: toString implemented by Informative?
+    // XXX: toString implemented by Informative
     
 })
 
index 3da9b06..8b3ede7 100644 (file)
@@ -14,22 +14,22 @@ exports['Stat'] =
 Y.subclass('Stat', {
     // __mixins__: [ Informative, Speciated, Meronomic, Usable ], // Configurable?
     
-    integer : true, // if true, current value is rounded after a change
+    integer : false, // if true, current value is rounded after a change
     
     base : 0,
     val : 0,
     max : 0,
     
     
-    init : function initStat(base, val, max, isFloat){
+    init : function initStat(base, val, max, isInteger){
         this.base = base;
         this.val = val !== undefined ? val : base;
         this.max = max !== undefined ? max : base;
-        this.integer = !isFloat;
+        this.integer = !!isInteger;
     },
     
     clone : function clone(){
-        return new Stat(this.base, this.val, this.max, !this.integer);
+        return new Stat(this.base, this.val, this.max, this.integer);
     },
     
     /**
@@ -109,7 +109,7 @@ Stat['from'] =
 function fromValue(v){
     if ( v instanceof Stat )
         return v.clone();
-    else if ( Y.isArray(v) )
+    else if ( v instanceof Array )
         return new Stat(v[0], v[1], v[2], v[3]);
     else
         return new Stat(v);
index 147a7a1..4f3beaa 100644 (file)
@@ -1,5 +1,8 @@
 var Y          = require('Y').Y
+,   op         = require('Y/op')
+
 ,   Rect       = require('ezl/shape').Rect
+
 ,   Thing      = require('tanks/thing/thing').Thing
 ,   Tank       = require('tanks/thing/tank').Tank
 ,   Item       = require('tanks/thing/item').Item
@@ -60,6 +63,6 @@ Rect.subclass('Level', {
         return wall;
     },
     
-    render : Y.op.nop
+    render : op.nop
     
 });
\ No newline at end of file
index ceac819..ebcfbf8 100644 (file)
@@ -335,7 +335,7 @@ Y.subclass('Square', new Vec(0,0), {
         return neighbors;
     },
     
-    toString : function toString(){
+    toString : function(){
         var props = [];
         if (this.blocked) props.push('blocked');
         if (this.visited) props.push('dist='+this.dist);
index dcd416a..10fc848 100644 (file)
@@ -166,7 +166,7 @@ Line.subclass('Trajectory', {
         return (blocker === obj ? false : blocker);
     },
     
-    toString : function toString(){
+    toString : function(){
         return 'T['+this.p1+', '+this.p2+', slope='+this.slope.toFixed(3)+']';
     }
 });
index 5f37d2a..f926a39 100644 (file)
@@ -45,7 +45,7 @@ Y.subclass('Traversal', {
         var tr = this.trajectory
         ,   or = this.thing.loc
         ,   dt = Math.min(tr.tBound, this.remaining)
-        ,   to = this.to = tr.parametric(tr.tCurrent+dt) // calc how far down the tr we go
+        ,   to = tr.parametric(tr.tCurrent+dt) // calc how far down the tr we go
         ;
         
         // Don't overshoot the target
index be66bf0..c56da30 100644 (file)
@@ -129,7 +129,7 @@ Thing.subclass('Bullet', {
         // Reflection!
         if ( isReflective && this.bounceLimit >= ++this.bounces ) {
             if (!tvsl.side)
-                return console.error('Null reflection line!', 'to:', to, 'blockers:', tvsl.blockers);
+                return console.error('Null reflection line!', 'to:', to, 'blocker:', unit, 'blockers:', tvsl.blockers._o);
             
             ng = math.reflect(traj.p2, tvsl.side);
             traj.reset(to.x,to.y, ng.x,ng.y);
@@ -139,7 +139,7 @@ Thing.subclass('Bullet', {
             return;
         }
         
-        unit.dealDamage(this.stats.power, this);
+        unit.dealDamage(this.stats.power.val, this);
         this.destroy();
         
         // Trigger explosion
index 231baef..59da3b9 100644 (file)
@@ -21,7 +21,7 @@ Thing.subclass('Item', {
     height   : ITEM_SIZE,
     
     // indestructable
-    stats      : { hp:Infinity, move:0, power:0 },
+    stats      : { hp:Infinity, move:0, power:0, speed:0 },
     dealDamage : op.nop,
     
     // inactive
index 67a97cb..859f2eb 100644 (file)
@@ -96,7 +96,7 @@ Tank.subclass('PlayerTank', {
         if (!dir) return;
         
         // @see Tank.move()
-        this.move( this.loc.moveByDir(dir, this.stats.move * SQUARETH) );
+        this.move( this.loc.moveByDir(dir, this.stats.move.val * SQUARETH) );
     },
     
     
index 7dccbcc..d2d4163 100644 (file)
@@ -126,7 +126,7 @@ Thing.subclass('Tank', function(Tank){
         }
         
         // Try to blow up nearby tanks
-        if (ai.shootEnemy.ready && (this.stats.shots - this.nShots > 1)) {
+        if (ai.shootEnemy.ready && (this.stats.shots.val - this.nShots > 1)) {
             var t = this.findNearEnemies(71, true).shift();
             // console.log('['+TICKS+':'+this.id, this, '] Shoot at enemies?', t);
             if (t) {
@@ -232,7 +232,7 @@ Thing.subclass('Tank', function(Tank){
     };
     
     
-    var kTrue = Y.op.K(true);
+    var kTrue = op.K(true);
     
     this['findNearLike'] =
     function findNearLike(ticks, fn){
@@ -308,7 +308,7 @@ Thing.subclass('Tank', function(Tank){
         
         if (xydef) this.rotateBarrel(x,y);
         
-        if ( this.nShots >= this.stats.shots || !this.cooldowns.attack.activate(this.now) )
+        if ( this.nShots >= this.stats.shots.val || !this.cooldowns.attack.activate(this.now) )
             return null;
         
         var tloc = this.getTurretLoc()
@@ -354,7 +354,7 @@ Thing.subclass('Tank', function(Tank){
     
     this['ableToShoot'] =
     function ableToShoot(){
-        return this.nShots < this.stats.shots && this.cooldowns.attack.ready;
+        return this.nShots < this.stats.shots.val && this.cooldowns.attack.ready;
     };
     
     this['getTurretLoc'] =
@@ -426,7 +426,7 @@ Thing.subclass('Tank', function(Tank){
         var names = Y('bodyColor', 'turretColor', 'barrelColor');
         
         if (arguments.length === 0)
-            return names.generate( Y.op.get(this) );
+            return names.generate( Y.op.curried.get(this) );
         
         if (bodyColor)   this.bodyColor   = bodyColor;
         if (turretColor) this.turretColor = turretColor;
index 6bcbc38..e4c4525 100644 (file)
@@ -92,7 +92,10 @@ exports['Thing'] = new evt.Class('Thing', {
     'set'  : op.set.methodize(),
     'attr' : op.attr.methodize(),
     
-    get movePerMs(){ return this.stats.move*REF_SIZE/1000; },
+    get movePerMs(){
+        var stat = this.stats.move;
+        return (typeof stat === "number" ? stat : stat.val)*REF_SIZE/1000;
+    },
     
     
     
@@ -109,33 +112,18 @@ exports['Thing'] = new evt.Class('Thing', {
         this.bbox = new BoundingBox(0,0, this.width,this.height, this.originX,this.originY);
     },
     
-    createStats : function createStats(){
-        this.stats = stat.createStats(this.stats);
-    },
-    
-    recalculateStats : function recalculateStats(){
-        this.stats =
-            Y(this.buffs).reduce(this._applyBuffStats, Y(this.stats).map(this._resetStat, this), this);
-    },
-    
-    _resetStat : function _resetStat(v, k, stats){
-        k = Y(k);
-        if ( k.endsWith('_max') )
-            
+    stat : function stat(k){
+        var s = this.stats[k];
+        return s ? s.val : s;
     },
     
-    _applyBuffStats : function _applyBuffStats(buff){
-        Y(buff.stat_mods).forEach(this._applyStatMod, this);
+    createStats : function createStats(){
+        this.stats = stat.createStats(this.stats);
     },
     
-    _applyStatMod : function _applyStatMod(v, k){
-        this.target.modifyStat(k, v);
-    }
-    
-    
     createCooldowns : function createCooldowns(){
         this.cooldowns = {
-            'attack': new Cooldown(1000 * this.stats.speed)
+            'attack': new Cooldown(1000 * this.stats.speed.val)
         };
         this.ai = Y(this.ai).map(function(freq, k){
             return new Cooldown(1000 * freq);
@@ -176,8 +164,8 @@ exports['Thing'] = new evt.Class('Thing', {
     },
     
     dealDamage : function dealDamage(d, source){
-        this.stats.hp -= d;
-        if (this.stats.hp <= 0)
+        this.stats.hp.modify(-d);
+        if (this.stats.hp.val <= 0)
             this.destroy();
         return this;
     },
index 0387257..f1ca947 100644 (file)
@@ -24,8 +24,8 @@
 <script src="build/ezl/loop/fps.js" type="text/javascript"></script>
 <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/loop/eventloop.js" type="text/javascript"></script>
 <script src="build/ezl/loc/loc.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/Y/modules/y.kv.js" type="text/javascript"></script>
 <script src="build/ezl/util/tree/binaryheap.js" type="text/javascript"></script>
 <script src="build/ezl/util/tree/quadtree.js" type="text/javascript"></script>
+<script src="build/tanks/constants.js" type="text/javascript"></script>
 <script src="build/Y/modules/y.scaffold.js" type="text/javascript"></script>
 <script src="build/Y/modules/y.config.js" type="text/javascript"></script>
 <script src="build/Y/modules/y.cookies.js" type="text/javascript"></script>
 <script src="build/tanks/map/map.js" type="text/javascript"></script>
+<script src="build/tanks/effects/stat.js" type="text/javascript"></script>
 <script src="build/tanks/map/traversal.js" type="text/javascript"></script>
 <script src="build/tanks/config.js" type="text/javascript"></script>
 <script src="build/tanks/map/pathmap.js" type="text/javascript"></script>