Adds Buff & Stat class
authordsc <david.schoonover@gmail.com>
Sat, 1 Jan 2011 03:29:59 +0000 (19:29 -0800)
committerdsc <david.schoonover@gmail.com>
Sat, 1 Jan 2011 03:29:59 +0000 (19:29 -0800)
26 files changed:
doc/notes.md
src/Y/class.cjs
src/Y/types/array.cjs
src/Y/types/string.cjs
src/Y/utils.cjs
src/ezl/layer/layer.cjs
src/tanks/constants.cjs [new file with mode: 0644]
src/tanks/effects/buff.cjs
src/tanks/effects/stat.cjs [new file with mode: 0644]
src/tanks/game.cjs
src/tanks/globals.js
src/tanks/map/map.cjs
src/tanks/map/pathmap.cjs
src/tanks/map/trajectory.cjs
src/tanks/map/traversal.cjs
src/tanks/mixins/configurable.cjs [copied from src/tanks/thing/zone.cjs with 100% similarity]
src/tanks/mixins/informative.cjs [copied from src/tanks/thing/zone.cjs with 100% similarity]
src/tanks/mixins/meronomic.cjs [copied from src/tanks/thing/zone.cjs with 100% similarity]
src/tanks/mixins/speciated.cjs [copied from src/tanks/thing/zone.cjs with 100% similarity]
src/tanks/mixins/usable.cjs [moved from src/tanks/thing/zone.cjs with 100% similarity]
src/tanks/thing/bullet.cjs
src/tanks/thing/tank.cjs
src/tanks/thing/thing.cjs
src/tanks/thing/wall.cjs
www/header.html
www/test/shapes/test-shapes.js

index 2c03c50..fc55836 100644 (file)
@@ -6,7 +6,6 @@
 - Config-driven unit-types (name, stats, properties; pointers to behavior scripts, assets)
 - Game scoring
 - Support touch events (for iPad?)
-- Migrate A* code into PathMap
 
 
 # Code Notes
index 73015eb..6848b76 100644 (file)
@@ -195,10 +195,33 @@ 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.
  */
-var YBase = new Class("YBase", {
+YBase = new Class("YBase", {
     __y__ : true
 });
 
index 8b6fc0a..0f2700c 100644 (file)
@@ -2,7 +2,7 @@ var Y           = require('Y/y').Y
 ,   del         = require('Y/delegate')
 ,   type        = require('Y/type')
 ,   op          = require('Y/op')
-,   mixin       = require('Y/utils').mixin
+,   mixInto     = require('Y/utils').mixInto
 ,   YCollection = require('Y/types/collection').YCollection
 
 ,   _Array        = Array
@@ -19,15 +19,15 @@ YCollection.subclass('YArray', function(YArray){
     
     var newYArray = YArray.instantiate.bind(YArray);
     
-    mixin({ target:YArray, donor:_Array, context:'_o',
+    mixInto({ target:YArray, donor:_Array, context:'_o',
         names:'indexOf lastIndexOf shift pop join'.split(' ') });
-    mixin({ target:YArray, donor:_Array, context:'_o', chain:true,
+    mixInto({ target:YArray, donor:_Array, context:'_o', chain:true,
         names:'push unshift sort splice reverse'.split(' ') });
-    mixin({ target:YArray, donor:_Array, context:'_o', fnFirst:true, wrap:newYArray,
+    mixInto({ target:YArray, donor:_Array, context:'_o', fnFirst:true, wrap:newYArray,
         names:'map forEach filter'.split(' ') });
-    mixin({ target:YArray, donor:_Array, context:'_o', fnFirst:true,
+    mixInto({ target:YArray, donor:_Array, context:'_o', fnFirst:true,
         names:['reduce', 'some', 'every'] });
-    mixin({ target:YArray, donor:_Array, context:'_o', wrap:newYArray,
+    mixInto({ target:YArray, donor:_Array, context:'_o', wrap:newYArray,
         names:['slice'] });
     
     
index 4339f9a..5a82ff5 100644 (file)
@@ -1,5 +1,5 @@
 var YCollection = require('Y/types/collection').YCollection
-,   mixin = require('Y/utils').mixin
+,   mixInto = require('Y/utils').mixInto
 ,   op    = require('Y/op')
 ,   core  = require('Y/core')
 ,   del   = require('Y/delegate')
@@ -10,9 +10,9 @@ exports['YString'] =
 YCollection.subclass('YString', function(YString){
     
     var newYString = YString.instantiate.bind(YString);
-    mixin({ target:YString, donor:String, context:'_o', wrap:newYString,
+    mixInto({ target:YString, donor:String, context:'_o', wrap:newYString,
         names:'slice substr substring concat replace toLowerCase toUpperCase'.split(' ') });
-    mixin({ target:YString, donor:String, context:'_o',
+    mixInto({ target:YString, donor:String, context:'_o',
         names:'split indexOf lastIndexOf charAt charCodeAt'.split(' ') });
     
     function trim(val){
index a27df65..0f01984 100644 (file)
@@ -32,8 +32,8 @@ function bindAll(o, names){
 
 
 var 
-This   = mixin['This']   = {},
-Target = mixin['Target'] = {},
+This   = mixInto['This']   = {},
+Target = mixInto['Target'] = {},
 defaults = {
     /**
      * {Object|Function} target Required. Target object onto which methods will be added. If this a Function, it's prototype will be used.
@@ -51,10 +51,10 @@ defaults = {
     'names' : null,
     
     /**
-     * {mixin.This|mixin.Target|String} [context=mixin.This]
+     * {mixInto.This|mixInto.Target|String} [context=mixInto.This]
      * Sets the call context for the function.
-     *  - {mixin.This}: (Default) Use runtime `this` as context.
-     *  - {mixin.Target}: Use `target` as context.
+     *  - {mixInto.This}: (Default) Use runtime `this` as context.
+     *  - {mixInto.Target}: Use `target` as context.
      *  - {String}: Names a property on `this` to use. If specified and property is false-y, `this` is used.
      */
     'context' : This,
@@ -81,7 +81,7 @@ defaults = {
      */
     'fnFirst' : false
 };
-function mixin(options){
+function mixInto(options){
     var o = core.extend({}, defaults, options)
     ,   target = o.target
     ,   donor  = o.donor
@@ -123,4 +123,4 @@ function mixin(options){
 }
 
 exports['bindAll'] = bindAll;
-exports['mixin'] = mixin;
+exports['mixInto'] = mixInto;
index 75d5408..beaa2ac 100644 (file)
@@ -64,8 +64,8 @@ Y.subclass('Layer', {
         this.negBleed = new Loc(0,0);
         this.posBleed = new Loc(0,0);
         
-        this.boundingBox = new BoundingBox(0,0, 0,0, this.originX,this.originY);
-        this._origin = this.boundingBox.origin;
+        this.bbox = new BoundingBox(0,0, 0,0, this.originX,this.originY);
+        this._origin = this.bbox.origin;
         
         this.transform = {
             rotate    : 0,
@@ -172,7 +172,7 @@ Y.subclass('Layer', {
         this.layerWidth  = w;
         this.layerHeight = h;
         
-        var bb = this.boundingBox.resize(w,h)
+        var bb = this.bbox.resize(w,h)
         ,   nb = this.negBleed, pb = this.posBleed
         
         // HTMLCanvas.{width,height} is a long
@@ -204,7 +204,7 @@ Y.subclass('Layer', {
         
         this.layerWidth = w;
         
-        var bb = this.boundingBox.resize(w, this.layerHeight)
+        var bb = this.bbox.resize(w, this.layerHeight)
         // ,   ro = bb.relOrigin
         ,   nb = this.negBleed
         ,   cw = this.canvasWidth = Math.ceil(w + nb.x + this.posBleed.x); // HTMLCanvas.width is a long
@@ -230,7 +230,7 @@ Y.subclass('Layer', {
         
         this.layerHeight = h;
         
-        var bb = this.boundingBox.resize(this.layerWidth, h)
+        var bb = this.bbox.resize(this.layerWidth, h)
         // ,   ro = bb.relOrigin
         ,   nb = this.negBleed
         ,   ch = this.canvasHeight = Math.ceil(h + nb.y + this.posBleed.y); // HTMLCanvas.height is a long
@@ -279,7 +279,7 @@ Y.subclass('Layer', {
             x = ('left' in x ? x.left : x.x);
         }
         
-        var bbox = this.boundingBox.relocate(x,y);
+        var bbox = this.bbox.relocate(x,y);
         this.css({
             'left' : bbox.x1,
             'top'  : bbox.y1
diff --git a/src/tanks/constants.cjs b/src/tanks/constants.cjs
new file mode 100644 (file)
index 0000000..d6eba26
--- /dev/null
@@ -0,0 +1,39 @@
+//#exports PathingType StatInvariant
+require('Y').Y.extend(exports, {
+    
+    /// Pathing Constants ///
+    /** How this object interacts with the world. */
+    PathingType : {
+        
+        /** Does not obstruct other objects. */
+        PASSABLE : 0,
+        
+        /** Does not obstruct other objects, but still collides with them. */
+        ZONE : 1,
+        
+        /** Obstructs other blockers with its BoundingBox. */
+        BLOCKING : 2,
+        
+        /** Potentially obstructs other objects, but requires a special test once a BoundingBox collision has been detected. */
+        IRREGULAR : 3
+        
+    },
+    
+    /** 
+     * Invariant to restore for the current value when modifying the base or max of a Stat.
+     * @see {tanks.effects.stat.Stat} for the default behavior of its methods.
+     */
+    StatInvariant : {
+        
+        /** Do not change current value. */
+        NONE : 1,
+        
+        /** Keep current value at the same ratio it was before the change. */
+        RATIO : 2,
+        
+        /** Apply the full change to the current value. */
+        FULL : 3
+        
+    }
+    
+});
\ No newline at end of file
index e69de29..84375bb 100644 (file)
@@ -0,0 +1,60 @@
+var Y = require('Y').Y
+,
+
+
+// Events:
+//  - live.{affect,stack}
+//  - die.{expire,dismiss,dispel}
+Buff =
+exports['Buff'] =
+Y.subclass('Buff', {
+    // __mixins__: [ Informative, Speciated, Meronomic, Usable ], // Configurable?
+    
+    
+    /// Configurable ///
+    priority  : 0, // Order of stat and effect application (lower is earlier)
+    stats : {
+        timeout : -1,
+        stacked : 1,
+        threat  : 1
+    },
+    stat_mods : {}, // {stat : Number|Function} Modifications to stats
+    // 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
+    target : null, // Agent to which the buff applies
+    
+    
+    
+    init : function initBuff(owner, target){
+        this.owner = owner;
+        this.game = owner.game;
+        this.target = target;
+        this.created = this.game.NOW;
+        // XXX: Speciated mixin must clone objects
+    },
+    
+    applyStatMods : function applyStatMods(modifier){
+        modifier = modifier || 1;
+        Y(this.stat_mods).forEach(function(mod, name){
+            var keyparts = name.split('_')
+            ,   key = parts[0]
+            ,   part = parts[1] || 'val'
+            ;
+            this.target.stats[key].modifyPart(part, modifier * mod);
+        }, this);
+        return this;
+    },
+    
+    removeStatMods : function removeStatMods(){
+        return this.applyStatMods(-1);
+    }
+    
+    // XXX: toString implemented by Informative?
+    
+})
+
+
+
diff --git a/src/tanks/effects/stat.cjs b/src/tanks/effects/stat.cjs
new file mode 100644 (file)
index 0000000..3da9b06
--- /dev/null
@@ -0,0 +1,127 @@
+var Y = require('Y').Y
+
+,   StatInvariant = require('tanks/constants').StatInvariant
+
+,   NONE  = StatInvariant.NONE
+,   RATIO = StatInvariant.RATIO
+,   FULL  = StatInvariant.FULL
+,
+
+
+
+Stat =
+exports['Stat'] =
+Y.subclass('Stat', {
+    // __mixins__: [ Informative, Speciated, Meronomic, Usable ], // Configurable?
+    
+    integer : true, // if true, current value is rounded after a change
+    
+    base : 0,
+    val : 0,
+    max : 0,
+    
+    
+    init : function initStat(base, val, max, isFloat){
+        this.base = base;
+        this.val = val !== undefined ? val : base;
+        this.max = max !== undefined ? max : base;
+        this.integer = !isFloat;
+    },
+    
+    clone : function clone(){
+        return new Stat(this.base, this.val, this.max, !this.integer);
+    },
+    
+    /**
+     * Resets max value and current value to base value.
+     * @return {this}
+     */
+    reset : function reset(){
+        this.val = this.max = this.base;
+        return this;
+    },
+    
+    /**
+     * @param {Number} dv Amount to add to current value.
+     * @return {this}
+     */
+    modify : function modify(dv){
+        var v = Math.min(this.max, this.val+dv);
+        this.val = (this.integer ? Math.round(v) : v);
+        return this;
+    },
+    
+    /**
+     * @param {Number} dv Amount to add to base and max value.
+     * @param {StatInvariant} [inv=FULL] Invariant to restore for current value.
+     * @return {this}
+     */
+    modifyBase : function modifyBase(dv, inv){
+        this.base += dv;
+        return this.modifyMax(dv, inv || FULL);
+    },
+    
+    /**
+     * @param {Number} dv Amount to add to current value.
+     * @param {StatInvariant} [inv=FULL] Invariant to restore for current value.
+     * @return {this}
+     */
+    modifyMax : function modifyMax(dv, inv){
+        var v = this.val
+        ,   oldmax = this.max;
+        this.max += dv;
+        
+        switch ( inv ) {
+            case NONE: break;
+            
+            case RATIO:
+                v = (v / oldmax) * this.max;
+                break;
+            
+            case FULL:
+            default:
+                v += dv;
+                break;
+        }
+        this.val = (this.integer ? Math.round(v) : v);
+        
+        return this;
+    },
+    
+    modifyPart : function modifyPart(part, dv, inv){
+        var method =
+            (part === 'max'  ? 'modifyMax' : 
+            (part === 'base' ? 'modifyBase' : 'modify'));
+        return this[method](dv, inv);
+    },
+    
+    toString : function(){
+        return this.val+'/'+this.max+(this.max !== this.base ? ' ['+this.base+']' : '');
+    }
+    
+})
+;
+
+Stat.fn.modifyVal = Stat.fn.modify;
+
+
+Stat['from'] =
+function fromValue(v){
+    if ( v instanceof Stat )
+        return v.clone();
+    else if ( Y.isArray(v) )
+        return new Stat(v[0], v[1], v[2], v[3]);
+    else
+        return new Stat(v);
+};
+
+/**
+ * Accepts any number of objects which are combined to make the new map of names to stats.
+ */
+exports['createStats'] =
+function createStats(o){
+    var stats = Y({});
+    return stats
+        .extend.apply(stats, Y(arguments))
+        .map(Stat.from);
+};
index 3678225..b6d99ad 100644 (file)
@@ -86,11 +86,11 @@ Y.subclass('Game', {
         var d = evt.data
         ,   self = this;
         
-        NOW      = d.now;
-        ELAPSED  = d.elapsed;
-        TICKS    = d.ticks;
-        SECONDTH = ELAPSED / 1000;
-        SQUARETH = REF_SIZE * SECONDTH
+        NOW      = this.NOW      = d.now;
+        ELAPSED  = this.ELAPSED  = d.elapsed;
+        TICKS    = this.TICKS    = d.ticks;
+        SECONDTH = this.SECONDTH = ELAPSED / 1000;
+        SQUARETH = this.SQUARETH = REF_SIZE * SECONDTH
         
         this.active.invoke('updateCooldowns', ELAPSED, NOW);
         this.active.invoke('act', ELAPSED, NOW);
@@ -103,7 +103,7 @@ Y.subclass('Game', {
         
         if ( this.player.dead )
             this.gameover = 'lose';
-        else if ( !this.units.filter('_.align !== 1 && !_.dead'.lambda()).size() )
+        else if ( !this.units.filter('_.align !== 1 && !_.dead').size() )
             this.gameover = 'win';
         
         if ( this.gameover )
@@ -121,16 +121,16 @@ Y.subclass('Game', {
         return running;
     },
     
+    
+    
+    // *** Agent Management *** //
+    
     addAnimation : function addAnimation(animation){
         this.animations.push(animation);
         this.level.append(animation);
         return animation;
     },
     
-    
-    
-    // *** Agent Management *** //
-    
     addThing : function addThing(unit, col,row){
         if (unit instanceof Event)
             unit = unit.trigger;
index cb949e5..b3863ba 100644 (file)
@@ -19,6 +19,20 @@ var undefined
 ,   ELAPSED       = MS_PER_FRAME         // Time (ms) since previous tick
 ,   TICKS         = 0                    // Ticks since start of game
 
+/// Pathing Constants (from tanks/map/map) ///
+
+/** Does not obstruct other objects. */
+,   PASSABLE = 0
+
+/** Does not obstruct other objects, but still collides with them. */
+,   ZONE = 1
+
+/** Obstructs other blockers with its BoundingBox. */
+,   BLOCKING = 2
+
+/** Potentially obstructs other objects, but requires a special test once a BoundingBox collision has been detected. */
+,   IRREGULAR = 3
+
 
 /// Common Components of Computation ///
 
index 5860a7a..feec7a3 100644 (file)
@@ -23,8 +23,9 @@ QuadTree.subclass('Map', {
     
     addBlocker : function addBlocker(obj){
         this.removeBlocker(obj);
-        var bb = obj.boundingBox;
-        if (obj.blocking && bb)
+        var pt = obj.blocking
+        ,   bb = obj.bbox ;
+        if (pt !== undefined && bb)
             obj.region = this.set(bb.x1,bb.y1, bb.x2,bb.y2, obj);
         return obj;
     },
@@ -35,6 +36,20 @@ QuadTree.subclass('Map', {
             delete obj.region;
         }
         return obj;
+    },
+    
+    getBlockers : function getBlockers(x1,y1, x2,y2, ignore){
+        if (x1 && x1.x1 !== undefined) {
+            ignore = y1;
+            y2 = x1.y2;  x2 = x1.x2;
+            y1 = x1.y1;  x1 = x1.x1;
+        }
+        var bs = this.get(x1,y1, x2,y2)
+            .filter('_.blocking === BLOCKING');
+        if (ignore && bs.length)
+            return bs.remove.apply(bs, ignore || []);
+        else
+            return bs;
     }
     
     
index c82075b..ceac819 100644 (file)
@@ -40,15 +40,13 @@ Map.subclass('PathMap', {
     init : function init(level, x1,y1, x2,y2, capacity) {
         x1 -= 1; y1 -= 1; x2 += 1; y2 += 1;
         QuadTree.init.call(this, x1,y1, x2,y2, capacity);
-        
+        this._squares = {};
         
         this.level    = level;
         this.game     = level.game;
         this.walls    = level.walls;
         this.allWalls = level.allWalls;
         
-        this._squares = {};
-        
         this.game.addEventListener('ready', this.setup.bind(this, x1,y1, x2,y2));
     },
     
@@ -70,23 +68,9 @@ Map.subclass('PathMap', {
         this.boundaryWalls = Y.map(BWs, 'this.level.addWall.apply(this.level, _)', this);
     },
     
-    getBlockers : function getBlockers(x1,y1, x2,y2, ignore){
-        if (x1 && x1.x1 !== undefined) {
-            ignore = y1;
-            y2 = x1.y2;  x2 = x1.x2;
-            y1 = x1.y1;  x1 = x1.x1;
-        }
-        var bs = this.get(x1,y1, x2,y2)
-            .filter('_.blocking === 2'); // map.BLOCKING
-        if (ignore && bs.length)
-            return bs.remove.apply(bs, ignore || []);
-        else
-            return bs;
-    },
-    
     wallObstructs : function wallObstructs(line){
         return this.walls.some(function(wall){
-            return wall.boundingBox.intersects(line);
+            return wall.bbox.intersects(line);
         }, this);
     },
     
@@ -160,7 +144,7 @@ Map.subclass('PathMap', {
                 
                 // Ensure we don't get stuck on a corner on our first step
                 var first = path.first()
-                ,   bb = agent.boundingBox.relocated( vec.lerp(0.5, agent.loc,first) )
+                ,   bb = agent.bbox.relocated( vec.lerp(0.5, agent.loc,first) )
                 ,   blockers = this.getBlockers(bb, [agent]);
                 
                 if (blockers.length)
@@ -279,7 +263,7 @@ Y.subclass('Square', new Vec(0,0), {
     _blocked : function blocked(){
         var pm    = this.pathmap
         ,   agent = pm._agent
-        // ,   bb    = agent.boundingBox
+        // ,   bb    = agent.bbox
         
         // ,   origin = bb.relOrigin
         // ,   left = origin.x,  right  = bb.width - left
index 72b4f9a..dcd416a 100644 (file)
@@ -73,7 +73,7 @@ Line.subclass('Trajectory', {
     intersects : function intersects(x,y){
         var o = x;
         if (o instanceof Thing)
-            return o.boundingBox.intersects(this);
+            return o.bbox.intersects(this);
         
         if (o instanceof Rect)
             return o.intersects(this);
index c51d193..5f37d2a 100644 (file)
@@ -21,7 +21,7 @@ Y.subclass('Traversal', {
         this.thing   = thing;
         this.game    = thing.game;
         this.pathmap = thing.game.pathmap;
-        this.bbox    = thing.boundingBox.clone();
+        this.bbox    = thing.bbox.clone();
         
         this.trajectory = trajectory || thing.trajectory;
     },
@@ -119,7 +119,7 @@ Y.subclass('Traversal', {
     rewind : function rewind(blocker){
         var tr = this.trajectory
         ,   bb = this.bbox, st = this.start
-        ,   B = blocker.boundingBox
+        ,   B = blocker.bbox
         ,   to = this.to ;
         
         // Figure out which boundary of the blocker we crossed and calculate
index 22a02cc..be66bf0 100644 (file)
@@ -8,14 +8,14 @@ var Y          = require('Y').Y
 ,   Circle     = shape.Circle
 
 ,   config     = require('tanks/config').config
-,   thing      = require('tanks/thing/thing')
 ,   map        = require('tanks/map/map')
+,   stat       = require('tanks/effects/stat')
+
+,   Thing      = require('tanks/thing/thing').Thing
 ,   Wall       = require('tanks/thing/wall').Wall
 ,   Trajectory = require('tanks/map/trajectory').Trajectory
 ,   Traversal  = require('tanks/map/traversal').Traversal
 ,   Explosion  = require('tanks/fx/explosion').Explosion
-,   fillStats  = thing.fillStats
-,   Thing      = thing.Thing
 ,
 
 
@@ -67,7 +67,7 @@ Thing.subclass('Bullet', {
     },
     
     createStats : function createStats(){
-        this.stats = Y({}, fillStats(this.owner.stats), fillStats(this.stats) );
+        this.stats = stat.createStats(this.owner.stats, this.stats);
     },
     
     remove : function remove(){
index 9e69127..7dccbcc 100644 (file)
@@ -212,7 +212,7 @@ Thing.subclass('Tank', function(Tank){
         ,   wall = this.closestOf(this.game.pathmap.allWalls)
         ,   wmid = wall.midpoint
         
-        ,   lvl = this.game.level.boundingBox, w = lvl.width, h = lvl.height
+        ,   lvl = this.game.level.bbox, w = lvl.width, h = lvl.height
         ,   x   = ((mid.x - wmid.x) > 0 ? w : 0)
         ,   y   = ((mid.y - wmid.y) > 0 ? h : 0)
         ,   to  = this.currentMove = trj.near(x,y);
@@ -239,7 +239,7 @@ Thing.subclass('Tank', function(Tank){
         fn = (fn || kTrue).toFunction();
         
         var within = BULLET_MOVE_PER_FRAME*ticks
-        ,   bb = this.boundingBox
+        ,   bb = this.bbox
         ,   x1 = bb.x1 - within, y1 = bb.y1 - within
         ,   x2 = bb.x2 + within, y2 = bb.y2 + within
         ;
@@ -281,7 +281,7 @@ Thing.subclass('Tank', function(Tank){
         bullets = ( Y.isArray(bullets) ? bullets : [bullets] );
         wiggle  = wiggle || 0;
         
-        var tank = this, bb = this.boundingBox
+        var tank = this, bb = this.bbox
         ,   w = bb.width+wiggle, h = bb.height+wiggle ;
         // ,   w = (bb.width+wiggle)/2, h = (bb.height+wiggle)/2 ;
         
@@ -319,7 +319,7 @@ Thing.subclass('Tank', function(Tank){
         
         // var barrel = this.barrel
         // ,   loc = this.loc
-        // ,   bb = this.boundingBox
+        // ,   bb = this.bbox
         // ,   w2 = bb.width/2,    h2 = bb.height/2
         // ,   x0 = bb.x1+w2,      y0 = bb.y1+h2
         // 
@@ -327,7 +327,7 @@ Thing.subclass('Tank', function(Tank){
         // ,   sin = Math.sin(theta),  cos = Math.cos(theta)
         // 
         // ,   x1 = x0 + w2*cos, y1 = y0 + h2*sin
-        // ,   sz = (barrel.boundingBox.width - w2)/2 + WIGGLE
+        // ,   sz = (barrel.bbox.width - w2)/2 + WIGGLE
         // ,   blockers = this.game.pathmap.get(x1-sz,y1-sz, x1+sz,y1+sz).remove(this)
         ;
         
@@ -365,7 +365,7 @@ Thing.subclass('Tank', function(Tank){
         
         ,   theta  = barrel.transform.rotate
         ,   sin = Math.sin(theta),  cos = Math.cos(theta)
-        ,   len = barrel.boundingBox.width + WIGGLE
+        ,   len = barrel.bbox.width + WIGGLE
         
         ,   x = loc.x + len*cos
         ,   y = loc.y + len*sin
index 53945e3..6bcbc38 100644 (file)
@@ -10,6 +10,7 @@ var Y           = require('Y').Y
 
 ,   config      = require('tanks/config').config
 ,   map         = require('tanks/map/map')
+,   stat        = require('tanks/effects/stat')
 
 ,   THING_ID = 0
 ,
@@ -19,19 +20,24 @@ fillStats =
 exports['fillStats'] =
 function fillStats(stats){
     return Y.reduce(stats, function(_stats, v, k){
+        _stats[k] = v;
+        
         k = Y(k);
-        var k_    = k.rtrim('_max')
-        ,   k_max = k+'_max'
+        var k_     = k.rtrim('_max')
+        ,   k_max  = k_+'_max'
+        ,   k_base = k_+'_base'
         ;
-        if ( k.endsWith('_max') ) {
-            if ( _stats[k_] === undefined )
-                _stats[k_] = v;
+        if ( _stats[k_] === undefined )
+            _stats[k_] = v;
             
-        } else if ( _stats[k_max] === undefined )
+        if ( _stats[k_max] === undefined )
             _stats[k_max] = v;
         
+        if ( _stats[k_base] === undefined )
+            _stats[k_base] = _stats[k_max];
+        
         return _stats;
-    }, Y.extend({}, stats));
+    }, {});
 }
 ,
 
@@ -71,7 +77,7 @@ exports['Thing'] = new evt.Class('Thing', {
     
     // Location
     loc : null,
-    boundingBox : null,
+    bbox : null,
     
     // Rotation (rads)
     rotation : 0,
@@ -100,13 +106,33 @@ exports['Thing'] = new evt.Class('Thing', {
     
     
     createBoundingBox : function createBoundingBox(){
-        this.boundingBox = new BoundingBox(0,0, this.width,this.height, this.originX,this.originY);
+        this.bbox = new BoundingBox(0,0, this.width,this.height, this.originX,this.originY);
     },
     
     createStats : function createStats(){
-        this.stats = fillStats(this.stats);
+        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') )
+            
+    },
+    
+    _applyBuffStats : function _applyBuffStats(buff){
+        Y(buff.stat_mods).forEach(this._applyStatMod, this);
+    },
+    
+    _applyStatMod : function _applyStatMod(v, k){
+        this.target.modifyStat(k, v);
+    }
+    
+    
     createCooldowns : function createCooldowns(){
         this.cooldowns = {
             'attack': new Cooldown(1000 * this.stats.speed)
@@ -130,7 +156,7 @@ exports['Thing'] = new evt.Class('Thing', {
         if (x === undefined && y === undefined)
             return this.loc;
         
-        var bb  = this.boundingBox.relocate(x,y) // in-place
+        var bb  = this.bbox.relocate(x,y) // in-place
         ,   loc = this.loc = bb.absOrigin
         ,   mid = this.midpoint = bb.midpoint
         ;
index b710e38..2349ea8 100644 (file)
@@ -65,7 +65,7 @@ Thing.subclass('Wall', {
     },
     
     toString : function(){
-        var bb = this.boundingBox
+        var bb = this.bbox
         ,   x1,y1, x2,y2;
         if (bb){
             x1 = bb.x1; y1 = bb.y1;
index 5170b15..ea7a063 100644 (file)
@@ -2,6 +2,7 @@
 <html>
 <head>
 <title>The Littlest Battletank</title>
+<link rel="shortcut icon" href="/favicon.ico">
 <link rel="stylesheet" href="css/reset.css" type="text/css" media="screen">
 <link rel="stylesheet" href="css/lttl.css" type="text/css" media="screen">
 </head>
index 8fc9afa..746fc0a 100644 (file)
@@ -41,13 +41,13 @@ $(function(){
         .fromPoints(100,100, 150,150)
         .fill('#E73075')
         .appendTo(plot);
-    R.title(R.boundingBox+'');
+    R.title(R.bbox+'');
     
     C = new shape.Circle(25)
         .position(150,150)
         .fill('#2E62C9')
         .appendTo(plot);
-    C.title(C.boundingBox+'');
+    C.title(C.bbox+'');
     
     grid.draw();