Checkpoint on new inventory architecture and equipment UI.
authordsc <david.schoonover@gmail.com>
Mon, 31 Jan 2011 06:39:35 +0000 (22:39 -0800)
committerdsc <david.schoonover@gmail.com>
Mon, 31 Jan 2011 06:39:35 +0000 (22:39 -0800)
13 files changed:
src/Y/class.cjs
src/Y/modules/y.event.cjs
src/Y/types/array.cjs
src/Y/types/collection.cjs
src/evt.cjs
src/tanks/item/bag.cjs [new file with mode: 0644]
src/tanks/item/bagbag.cjs [new file with mode: 0644]
src/tanks/item/belt.cjs [new file with mode: 0644]
src/tanks/item/container.cjs
src/tanks/item/inventory.cjs [new file with mode: 0644]
src/tanks/mixins/inventoried.cjs
src/tanks/thing/item.cjs
src/tanks/ui/inventory/containerui.cjs [moved from src/tanks/ui/inventory/container.cjs with 100% similarity]

index b8bf9e9..65b7d9d 100644 (file)
@@ -99,12 +99,9 @@ function Class(className, Parent, members) {
     
     // Creates a new function with the appropriate name
     // based on the className.
-    var NewClass, 
-    constructor = [
-        'var '+className,
-        (''+_Class).replace(_Class.name, className),
-        'NewClass = '+className, ''
-    ].join(';\n');
+    var NewClass, constructor =
+        'var '+className+' = NewClass = '+
+        (''+_Class).replace(_Class.name, className) + ';';
     eval(constructor);
     
     // Copy Class statics
index 64859d9..2e15d46 100644 (file)
@@ -4,8 +4,7 @@ Y['event'] = exports;
 /**
  * A simple event.
  * TODO: Detect jQuery event objects.
- * TODO: If DOM event, wrap with consistent API.
- * TODO: use jQuery if present to wrap events.
+ * TODO: If DOM event, wrap with consistent API (use jQuery if present).
  */
 var Event =
 exports['Event'] =
index 37a181e..6b3a9eb 100644 (file)
@@ -40,6 +40,17 @@ YCollection.subclass('YArray', function(YArray){
             this._o = o || [];
     };
     
+    this['get'] =
+    function get(i){
+        if (arguments.length > 1) {
+            return this.slice.apply(this, arguments);
+        } else {
+            if (i < 0)
+                i += this._o.length;
+            return this._o[i];
+        }
+    };
+    
     this['clone'] =
     function clone(){
         return new YArray(this._o.slice(0));
@@ -59,13 +70,14 @@ YCollection.subclass('YArray', function(YArray){
         (function (j){
             Object.defineProperty(self, j+'', {
                 'get' : function(){  return this._o[j]; },
-                'set' : function(v){ return (this._o[j] = v); },
+                'set' : function(v){ return (this._o[j] = v); }
             });
         }).call(this, i);
     
     
     this['toString'] = function(){
-        return "Y[" + arrayToString.call(this._o||[]) + "]";
+        // return "Y[" + arrayToString.call(this._o||[]) + "]";
+        return "YArray(["+this._o+"])";
     };
     
     /**
@@ -93,11 +105,25 @@ YCollection.subclass('YArray', function(YArray){
                 return v;
     };
     
-    function _has(v){ return this._o.indexOf(v) !== -1; }
-    
+    /**
+     * Intersects this YArray with another collection, returning a new YArray.
+     * The membership test uses Y(a).has(), so it is possible to intersect collections of different types.
+     * For YArray and YObject, .has() uses strict equality (===) via .indexOf().
+     * 
+     * @param {Array|Object|YCollection} a Comparison collection.
+     * @return {YArray} A new YArray of all elements in {this} found in the supplied collection.
+     * 
+     *      var foo = /foo/;
+     *      var A = [foo, 'A', 1, 2, 3, 'C', /foo/];
+     *      var B = [foo, 'B', 3, 'A', 1, /foo/];
+     *      var I = Y(A).intersect(B);
+     *      I.toString() === "YArray([/foo/,A,1,3])"; // true
+     *      I.get(0) === foo; // true
+     */
     this['intersect'] =
     function intersect(a){
-        return Y(a).filter(_has, this);
+        var A = Y(a);
+        return this.filter(A.has, A);
     };
     
     this['clear'] =
index 7fbced3..46bbff5 100644 (file)
@@ -1,4 +1,5 @@
-var YBase  = require('Y/class').YBase
+var Y      = require('Y/y').Y
+,   YBase  = require('Y/class').YBase
 ,   type   = require('Y/type')
 ,   core   = require('Y/core')
 ,   del    = require('Y/delegate')
@@ -154,8 +155,29 @@ YBase.subclass('YCollection', {
     // FIXME: this._o[name].apply
     'apply' : function apply(name, args){
         return this[name].apply(this, args);
+    },
+    
+    /**
+     * Intersects this YArray with another collection, returning a new YArray.
+     * The membership test uses Y(a).has(), so it is possible to intersect collections of different types.
+     * For YArray and YObject, .has() uses strict equality (===) via .indexOf().
+     * 
+     * @param {Array|Object|YCollection} a Comparison collection.
+     * @return {YArray} A new YArray of all elements in {this} found in the supplied collection.
+     * 
+     *      var foo = /foo/;
+     *      var A = [foo, 'A', 1, 2, 3, 'C', /foo/];
+     *      var B = [foo, 'B', 3, 'A', 1, /foo/];
+     *      var I = Y(A).intersect(B);
+     *      I.toString() === "YArray([/foo/,A,1,3])"; // true
+     *      I.get(0) === foo; // true
+     */
+    'intersect' : function intersect(a){
+        var A = Y(a);
+        return this.filter(A.has, A);
     }
     
+    
 });
 
 
index b74b7d8..611ed6a 100644 (file)
@@ -310,14 +310,14 @@ function instantiate(cls){
     return new cls(Y(arguments,1)); // the magic is done by checking in the constructor for this specific caller
 }
 
-var CST = Class.__static__ = {};
-CST.instantiate = Class.instantiate = Y(instantiate).methodize();
-CST.fabricate   = Class.fabricate   = Y.Class.fabricate;
+var Cstatics = Class.__static__ = {};
+Cstatics.instantiate = Class.instantiate = Y(instantiate).methodize();
+Cstatics.fabricate   = Class.fabricate   = Y.Class.fabricate;
 
 /**
  * Class/Instance method of Classes, not to be confused with Evt.subclass, which is a static method.
  */
-CST.subclass =
+Cstatics.subclass =
 Class.subclass =
 Class.fn.subclass =
     Y(function subclass(className, members){
@@ -329,12 +329,11 @@ function lookupClass(name){
     return (typeof name === "function") ? name : KNOWN_CLASSES[name];
 }
 
-var
 
 /**
  * Everybody's favourite party metaclass!
  */
-Mixin =
+var Mixin =
 exports['Mixin'] =
 new Class('Mixin', Class, {
     
@@ -380,11 +379,11 @@ function mixinFilter(v, k){
  * Mixes a Mixin into another Class.
  */
 function mixin(cls, _mxn){
-    var proto = cls.fn
+    var proto   = cls.fn
     ,   bases   = cls.__bases__
     ,   cbinds  = proto.__bind__
     ,   cstatic = cls.__static__
-    ,   mxns = (Y.isArray(_mxn) ? _mxn.slice(0) : Y(arguments, 1))
+    ,   mxns    = (Y.isArray(_mxn) ? _mxn.slice(0) : Y(arguments, 1))
     ;
     mxns.reverse().forEach(function(mxn){
         mxn = (typeof mxn === "string") ? lookupClass(mxn) : mxn;
@@ -392,9 +391,9 @@ function mixin(cls, _mxn){
         if ( !mxn )
             throw new Error('Cannot mix in non-object! '+mxn);
         
-        var mproto = (typeof mxn === "function") ? mxn.fn : mxn
-        ,   statics = mxn.__static__
-        ,   onCreate  = mproto.onCreate
+        var mproto   = (typeof mxn === "function") ? mxn.fn : mxn
+        ,   statics  = mxn.__static__
+        ,   onCreate = mproto.onCreate
         ,   firstMix = (bases && bases.indexOf(mxn) === -1)
         ;
         
diff --git a/src/tanks/item/bag.cjs b/src/tanks/item/bag.cjs
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/src/tanks/item/bagbag.cjs b/src/tanks/item/bagbag.cjs
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/src/tanks/item/belt.cjs b/src/tanks/item/belt.cjs
new file mode 100644 (file)
index 0000000..e69de29
index 2e6b6d5..b728b43 100644 (file)
@@ -2,7 +2,7 @@ var Y        = require('Y').Y
 ,   op       = require('Y/op')
 ,   deepcopy = require('Y/types/object').deepcopy
 
-,   evt = require('evt')
+,   Mixin = require('evt').Mixin
 ,   Item  = require('tanks/thing/item').Item
 
 ,   kNull = op.K(null)
@@ -10,13 +10,13 @@ var Y        = require('Y').Y
 
 Container =
 exports['Container'] =
-evt.subclass('Container', {
+Mixin.subclass('Container', {
     __bind__   : [],
     
     name  : null,   // Name of this container
     max   : 1,      // Container capacity
     reqs  : null,   // Array of tag requirements to be stored here
-    equips : false, // Whether held items are equipped
+    equipContents : false, // Whether held items are equipped
     
     size  : 0,      // Number of items in container
     items : null,   // item id -> Item
@@ -39,32 +39,151 @@ evt.subclass('Container', {
     
     
     /**
+     * @param {Item} [item] If present, also check whether item matches this container's requirements.
      * @return {Boolean} Whether this container will accept this item.
      */
     canAddItem : function canAddItem(item){
-        return this.size < this.max && ( !item || !this.reqs || this.reqs.intersect(item.tags) );
+        var reqs = this.reqs;
+        return this.size < this.max && 
+            ( !item || !reqs || reqs.intersect(item.tags).length === reqs.length );
     },
     
     getItem : function getItem(idx){
-        if (idx === undefined || idx === null)
-            return idx;
-        if (idx instanceof Item)
+        if (idx === undefined || idx === null || idx instanceof Item)
             return idx;
         else
             return this.slots[idx];
     },
     
+    /**
+     * @param {Number|Item} id The Item or Item ID for which to test.
+     * @return {Boolean} Whether the item is in this container.
+     */
     hasItem : function hasItem(id){
         if (id instanceof Item) id = id.__id__;
         return !!this.items[id];
     },
     
+    /**
+     * Adds an item to this container.
+     * @param {Item} item
+     * @return {this}
+     */
+    addItem : function addItem(item){
+        if ( this.hasItem(item) )
+            return this;
+        else
+            return this.moveItem(item, this._getEmptySlot());
+    },
+    
+    
+    /**
+     * Changes the position of the item in this container.
+     * @param {Item} item Item to move.
+     * @param {Number} idx Index in the container to which to move item.
+     * @return {this}
+     */
+    moveItem : function moveItem(item, idx){
+        item = this.getItem(item);
+        if (!item || typeof idx != "number")
+            return this;
+        
+        // Make note of item we're displacing, if any
+        var displacedItem = this.slots[idx]
+        ,   oldIdx = item.ui.bpIdx
+        ,   newItem = !this.hasItem(item)
+        ,   evtName = 'item.move'
+        ;
+        
+        if ( !this._putItem(item, idx) )
+            return this;
+        
+        if (newItem)
+            if (this.equipContents)
+                evtName = 'item.equip'
+            else
+                evtName = 'item.move'
+        
+        idx = item.ui.bpIdx;
+        this._fireItemChange(evtName, item, { 'idx':idx, 'oldIdx':oldIdx });
+        
+        if ( displacedItem && displacedItem !== item ) {
+            displacedItem.ui.bpIdx = null;
+            this._putItem(displacedItem, oldIdx);
+            this._fireItemChange('item.move', displacedItem, { 'idx':displacedItem.ui.bpIdx, 'oldIdx':idx });
+        }
+        
+        return this;
+    },
+    
+    /**
+     * Removes the item from this container.
+     * @param {Item|Number} idx Item or container index to remove.
+     * @param {Boolean} [drop=false] If true, Item is dropped and not just removed from this container.
+     * @return {Item} The removed item.
+     */
+    removeItem : function removeItem(idx, drop){
+        var item = this.getItem(idx);
+        idx = item ? item.ui.bpIdx : -1; // ensures we have the index if given an Item
+        
+        // TODO: UI feedback on failure
+        if ( !this._removeItem(item) )
+            return null;
+        
+        this._fireItemChange('item.lose'+(drop ? '.drop' : ''), item, { 'idx':idx });
+        return item;
+    },
+    
+    equipItem : function equipItem(slot, item){
+        item = this.getItem(item);
+        
+        var inv = this.inventory
+        ,   eqs = inv.equipment;
+        
+        if ( !(slot in eqs) )
+            throw new Error('Unit '+this+' does not have an equipment slot '+slot+'!');
+        
+        if ( !(item instanceof Item) )
+            throw new Error('Unit '+this+' cannot equip item '+item+'!');
+        
+        // TODO: Ensure item has right tags for slot
+        var oldIdx = item.backpack;
+        this._removeItem(item);
+        
+        // TODO: UI feedback
+        if ( eqs[slot] )
+            this.unequipSlot(slot, oldIdx);
+        
+        this.items[item.__id__] = item;
+        eqs[slot] = item;
+        item.slot = slot;
+        
+        return this._fireItemChange('item.equip', item, { 'slot':slot });
+    },
+    
+    unequipSlot : function unequipSlot(slot, idx){
+        var inv  = this.inventory
+        ,   eqs  = inv.equipment
+        ,   item = eqs[slot]
+        ;
+        if (item) {
+            // TODO: UI feedback on failure
+            if ( !this._putItem(item, idx) )
+                return this;
+            
+            eqs[slot] = null;
+            item.slot = null;
+            this._fireItemChange('item.unequip', item, { 'slot':slot, 'idx':idx });
+        }
+        return this;
+    },
+    
     
     
     /**
      * @return {Integer} Index of first empty slot in this unit's backpack.
      */
-    getEmptySlot : function getEmptySlot(){
+    _getEmptySlot : function _getEmptySlot(){
         var slots = this.slots
         ,   max = this.max;
         
@@ -90,9 +209,7 @@ evt.subclass('Container', {
         if (!item) return false;
         
         // item = this.getItem(item);
-        var slots  = this.slots
-        ,   idx = ( (typeof idx == "number") ? idx : this.getEmptySlot() )
-        
+        var idx = ( (typeof idx == "number") ? idx : this._getEmptySlot() )
         ,   id = item.__id__
         ,   ui = item.ui
         ;
@@ -102,13 +219,14 @@ evt.subclass('Container', {
             return false;
         
         if ( this.items[id] )
-            delete slots[ui.bpIdx]; // Already in backpack? Remove from current slot
+            delete this.slots[ui.bpIdx]; // Already in backpack? Remove from current slot
         else
             this.size++;            // Otherwise increment size
         
-        slots[idx] = item;
-        ui.bpIdx = idx;
         this.items[id] = item;
+        this.slots[idx] = item;
+        ui.bpIdx = idx;
+        ui.container = this;
         
         return true;
     },
@@ -149,121 +267,8 @@ evt.subclass('Container', {
         this.fire(evt, item, data);
         item.fire(evt, this, data);
         return this;
-    },
-    
-    // TODO: update UI; UI to inform item on activation
-    /**
-     * @param {Item} item
-     * @return {this}
-     */
-    addItem : function addItem(item){
-        if ( this.hasItem(item) )
-            return this;
-        
-        item = this.getItem(item);
-        var idx = this.getEmptySlot();
-        
-        // TODO: UI feedback on failure
-        if ( !this._putItem(item, idx) )
-            return this;
-        
-        return this._fireItemChange('item.acquire', item, { 'idx':idx });
-    },
-    
-    moveItem : function moveItem(item, idx){
-        item = this.getItem(item);
-        if (!item || typeof idx != "number")
-            return this;
-        
-        // Make note of item we're displacing, if any
-        var displacedItem = this.slots[idx]
-        ,   oldIdx = item.ui.bpIdx
-        ;
-        
-        if ( !this._putItem(item, idx) )
-            return this;
-        
-        idx = item.ui.bpIdx;
-        this._fireItemChange('item.move', item, { 'idx':idx, 'oldIdx':oldIdx });
-        
-        if ( displacedItem && displacedItem !== item ) {
-            displacedItem.ui.bpIdx = null;
-            this._putItem(displacedItem, oldIdx);
-            this._fireItemChange('item.move', displacedItem, { 'idx':displacedItem.ui.bpIdx, 'oldIdx':idx });
-        }
-        
-        return true;
-    },
-    
-    removeItem : function removeItem(item){
-        item = this.getItem(item);
-        var idx = item && item.backpack;
-        
-        // TODO: UI feedback on failure
-        if ( this._removeItem(item) )
-            return this;
-        
-        this._fireItemChange('item.lose', item, { 'idx':idx });
-        return item;
-    },
-    
-    dropItem : function dropItem(idx){
-        item = this.getItem(item);
-        var idx = item ? item.ui.bpIdx : -1;
-        
-        // TODO: UI feedback on failure
-        if ( this._removeItem(item) )
-            return this;
-        
-        this._fireItemChange('item.lose.drop', item, { 'idx':idx });
-        return item;
-    },
-    
-    equipItem : function equipItem(slot, item){
-        item = this.getItem(item);
-        
-        var inv = this.inventory
-        ,   eqs = inv.equipment;
-        
-        if ( !(slot in eqs) )
-            throw new Error('Unit '+this+' does not have an equipment slot '+slot+'!');
-        
-        if ( !(item instanceof Item) )
-            throw new Error('Unit '+this+' cannot equip item '+item+'!');
-        
-        // TODO: Ensure item has right tags for slot
-        var oldIdx = item.backpack;
-        this._removeItem(item);
-        
-        // TODO: UI feedback
-        if ( eqs[slot] )
-            this.unequipSlot(slot, oldIdx);
-        
-        this.items[item.__id__] = item;
-        eqs[slot] = item;
-        item.slot = slot;
-        
-        return this._fireItemChange('item.equip', item, { 'slot':slot });
-    },
-    
-    unequipSlot : function unequipSlot(slot, idx){
-        var inv  = this.inventory
-        ,   eqs  = inv.equipment
-        ,   item = eqs[slot]
-        ;
-        if (item) {
-            // TODO: UI feedback on failure
-            if ( !this._putItem(item, idx) )
-                return this;
-            
-            eqs[slot] = null;
-            item.slot = null;
-            this._fireItemChange('item.unequip', item, { 'slot':slot, 'idx':idx });
-        }
-        return this;
     }
     
     
     
-    
 });
diff --git a/src/tanks/item/inventory.cjs b/src/tanks/item/inventory.cjs
new file mode 100644 (file)
index 0000000..e69de29
index 2831a52..6af5fa4 100644 (file)
@@ -48,6 +48,11 @@ Mixin.subclass('Inventoried', {
         return inv.size < inv.max;
     },
     
+    /**
+     * Looks up the Item at an index; returns undefined if that index is empty.
+     * @param {Number|Item} idx Index in backpack to retrieve. If an Item is passed, it is returned.
+     * @return {Item|undefined}
+     */
     getItem : function getItem(idx){
         if (idx instanceof Item)
             return idx;
@@ -60,109 +65,13 @@ Mixin.subclass('Inventoried', {
         return !!this.inventory.items[id];
     },
     
-    
-    
-    /**
-     * @return {Integer} Index of first empty slot in this unit's backpack.
-     */
-    getEmptySlot : function getEmptySlot(){
-        var inv = this.inventory
-        ,   bp  = inv.backpack
-        ,   len = inv.max;
-        
-        if (inv.size >= inv.max)
-            return -1;
-        
-        for (var i=0, v=bp[i]; i<len; v=bp[++i])
-            if (v === undefined)
-                return i;
-        
-        return -1;
-    },
-    
-    
-    /**
-     * Inserts item into backpack at index, updating metadata.
-     * @protected
-     * @param {Item} item
-     * @param {Number} [idx] Backpack position at which to insert item. If missing, first open slot will be used.
-     * @return {Boolean}
-     */
-    _putItem : function putItem(item, idx){
-        if (!item) return false;
-        
-        // item = this.getItem(item);
-        var inv = this.inventory
-        ,   bp  = inv.backpack
-        ,   idx = ( (typeof idx == "number") ? idx : this.getEmptySlot() )
-        
-        ,   id = item.__id__
-        ,   ui = item.ui
-        ;
-        
-        // Backpack bounds check
-        if ( idx < 0 || idx >= bp.length )
-            return false;
-        
-        if ( inv.items[id] )
-            delete bp[ui.bpIdx];    // Already in backpack? Remove from current slot
-        else
-            inv.size++;             // Otherwise increment size
-        
-        bp[idx] = item;
-        ui.bpIdx = idx;
-        inv.items[id] = item;
-        
-        return true;
-    },
-    
-    
-    /**
-     * Removes item from backpack, updating metadata.
-     * @param {Item} item
-     * @return {Boolean}
-     */
-    _removeItem : function removeItem(item){
-        if (!item) return false;
-        
-        // item = this.getItem(item);
-        var inv = this.inventory
-        ,   id  = item.__id__
-        ,   ui  = item.ui
-        ,   idx = ui.bpIdx
-        ,   slot = ui.equipSlot
-        ,   hasItem = !!inv.items[id]
-        ;
-        
-        delete inv.items[id];
-        
-        if (hasItem) {
-            // TODO: unequip slot
-            item.clean();
-            delete inv.backpack[idx];
-            inv.size--;
-            return true;
-        }
-        
-        return false;
-    },
-    
-    
-    _fireItemChange : function _fireItemChange(evt, item, data){
-        data = Y.extend({ 'unit':this, 'item':item }, data || {});
-        this.fire(evt, item, data);
-        item.fire(evt, this, data);
-        return this;
-    },
-    
-    // TODO: update UI; UI to inform item on activation
     /**
      * @param {Item} item
      * @return {this}
      */
     addItem : function addItem(item){
         item = this.getItem(item);
-        var idx = this.getEmptySlot();
+        var idx = this._getEmptySlot();
         
         // TODO: UI feedback on failure
         if ( !this._putItem(item, idx) )
@@ -262,9 +171,100 @@ Mixin.subclass('Inventoried', {
             this._fireItemChange('item.unequip', item, { 'slot':slot, 'idx':idx });
         }
         return this;
-    }
+    },
+    
+    
+        /**
+     * @return {Integer} Index of first empty slot in this unit's backpack.
+     */
+    _getEmptySlot : function _getEmptySlot(){
+        var inv = this.inventory
+        ,   bp  = inv.backpack
+        ,   len = inv.max;
+        
+        if (inv.size >= inv.max)
+            return -1;
+        
+        for (var i=0, v=bp[i]; i<len; v=bp[++i])
+            if (v === undefined)
+                return i;
+        
+        return -1;
+    },
+    
+    
+    /**
+     * Inserts item into backpack at index, updating metadata.
+     * @protected
+     * @param {Item} item
+     * @param {Number} [idx] Backpack position at which to insert item. If missing, first open slot will be used.
+     * @return {Boolean}
+     */
+    _putItem : function putItem(item, idx){
+        if (!item) return false;
+        
+        // item = this.getItem(item);
+        var inv = this.inventory
+        ,   bp  = inv.backpack
+        ,   idx = ( (typeof idx == "number") ? idx : this._getEmptySlot() )
+        
+        ,   id = item.__id__
+        ,   ui = item.ui
+        ;
+        
+        // Backpack bounds check
+        if ( idx < 0 || idx >= bp.length )
+            return false;
+        
+        if ( inv.items[id] )
+            delete bp[ui.bpIdx];    // Already in backpack? Remove from current slot
+        else
+            inv.size++;             // Otherwise increment size
+        
+        bp[idx] = item;
+        ui.bpIdx = idx;
+        inv.items[id] = item;
+        
+        return true;
+    },
+    
     
+    /**
+     * Removes item from backpack, updating metadata.
+     * @param {Item} item
+     * @return {Boolean}
+     */
+    _removeItem : function removeItem(item){
+        if (!item) return false;
+        
+        // item = this.getItem(item);
+        var inv = this.inventory
+        ,   id  = item.__id__
+        ,   ui  = item.ui
+        ,   idx = ui.bpIdx
+        ,   slot = ui.equipSlot
+        ,   hasItem = !!inv.items[id]
+        ;
+        
+        delete inv.items[id];
+        
+        if (hasItem) {
+            // TODO: unequip slot
+            item.clean();
+            delete inv.backpack[idx];
+            inv.size--;
+            return true;
+        }
+        
+        return false;
+    },
     
     
+    _fireItemChange : function _fireItemChange(evt, item, data){
+        data = Y.extend({ 'unit':this, 'item':item }, data || {});
+        this.fire(evt, item, data);
+        item.fire(evt, this, data);
+        return this;
+    },
     
 });
index af2fa5c..946c2df 100644 (file)
@@ -10,6 +10,7 @@ var Y         = require('Y').Y
 ,   Thing     = require('tanks/thing/thing').Thing
 
 ,   ITEM_SIZE = REF_SIZE * 0.5
+,   kNull = op.K(null)
 ,
 
 
@@ -48,9 +49,9 @@ Thing.subclass('Item', {
     currentBuffs : null,    // {Buff...} Buffs applied to owner
     
     // UI Bookkeeping
-    ui : {
-        bpIdx     : null,   // {Integer} Index in owner's backpack
-        equipSlot : null    // {String} Slotname if equipped on owner
+    inv : {
+        idx     : null,   // {Integer} Index in owner's backpack
+        container : null    // {String} Slotname if equipped on owner
     },
     
     
@@ -59,9 +60,12 @@ Thing.subclass('Item', {
         Thing.init.call(this, 0);
         this.clean();
         
-        this.currentBuffs = new Y.YArray();
-        this.passives = this.passives.clone();
         this.effects = this.effects.clone();
+        this.passives = this.passives.clone();
+        
+        this.activeBuffs = new Y.YArray();
+        this.passiveBuffs = new Y.YArray();
+        
         this.activateGauge = new CooldownGauge(this.cooldowns.activate, REF_SIZE+1,REF_SIZE+1);
         
         this.addEventListener('collide', this.onCollide);
@@ -74,7 +78,7 @@ Thing.subclass('Item', {
      * Resets UI bookkeeping.
      */
     clean : function clean(){
-        this.ui = { bpIdx:null, equipSlot:null };
+        this.inv = Y.core.map(this.inv, kNull);
         return this;
     },
     
@@ -118,6 +122,7 @@ Thing.subclass('Item', {
         // TODO: unequip buffs
     },
     
+    // TODO: Add to correct container
     onCollide : function onCollide(evt){
         var unit = evt.data.unit;
         if (unit.hasInventory && unit.canAddItem())