Creeps now drop l3wtz!
authordsc <david.schoonover@gmail.com>
Fri, 25 Feb 2011 11:14:57 +0000 (03:14 -0800)
committerdsc <david.schoonover@gmail.com>
Fri, 25 Feb 2011 11:14:57 +0000 (03:14 -0800)
14 files changed:
data/loadlist.yaml
data/types/levels.yaml
data/types/loots.yaml [new file with mode: 0644]
data/types/units.yaml
src/ezl/index.cjs
src/ezl/struct/index.cjs [new file with mode: 0644]
src/ezl/struct/namedtuple.cjs [new file with mode: 0644]
src/tanks/inventory/index.cjs
src/tanks/inventory/loot.cjs [new file with mode: 0644]
src/tanks/mixins/index.cjs
src/tanks/mixins/lootable.cjs [new file with mode: 0644]
src/tanks/mixins/meronomic.cjs
src/tanks/thing/item.cjs
src/tanks/thing/tank.cjs

index b5336e9..6fd0eed 100644 (file)
@@ -2,6 +2,7 @@
 - types/bullets
 - types/items
 # - types/weapons
+- types/loots
 - types/units
 - types/levels
 # - types/player
index 7aa014e..af501f8 100644 (file)
@@ -68,8 +68,8 @@ types:
           #   align: 2
           #   loc: [425,125]
         items:
-          - type: rockets
-            loc: [325,275]
+          # - type: rockets
+          #   loc: [325,275]
           - type: nitro
             loc: [325,25]
     
diff --git a/data/types/loots.yaml b/data/types/loots.yaml
new file mode 100644 (file)
index 0000000..ae7c9fe
--- /dev/null
@@ -0,0 +1,9 @@
+name: loots
+defaults:
+    symbol: tanks/inventory/loot.LootTable
+types:
+    rich:
+        items:
+            - 0.1 super_armor
+            - 0.2 rockets
+            - 0.3 nitro
index 11a9432..15c7596 100644 (file)
@@ -3,6 +3,7 @@ defaults:
     symbol: tanks/thing/thing.Thing
     level: 1
     projectile: normal
+    lootTable : ''
     stats:
         hp            : 1           # health
         move          : 1.0         # move speed (squares/sec)
@@ -70,6 +71,7 @@ types:
         desc: A very green tank.
         tags: [ 'tank' ]
         symbol: tanks/thing/tank.Tank
+        lootTable : rich
         stats:
             hp    : 1
             move  : 0.75
index bca3143..e79dc84 100644 (file)
@@ -3,12 +3,12 @@
 //#ensure "functional/to-function"
 //#ensure "jquery"
 //#ensure "Y"
-var Y = require('Y').Y;
-
-Y.extend(exports, {
+require('Y').Y.core
+.extend(exports, {
     'math'   : require('ezl/math'),
     'loc'    : require('ezl/loc'),
     'mixins' : require('ezl/mixins'),
+    'struct' : require('ezl/struct'),
     'util'   : require('ezl/util'),
     
     'loop'   : require('ezl/loop'),
diff --git a/src/ezl/struct/index.cjs b/src/ezl/struct/index.cjs
new file mode 100644 (file)
index 0000000..a3427d5
--- /dev/null
@@ -0,0 +1,7 @@
+var Y = require('Y').Y
+,   namedtuple = require('ezl/struct/namedtuple')
+;
+Y.core.extend(exports, {
+    'namedtuple' : namedtuple,
+    'NamedTuple' : namedtuple.NamedTuple
+});
diff --git a/src/ezl/struct/namedtuple.cjs b/src/ezl/struct/namedtuple.cjs
new file mode 100644 (file)
index 0000000..a508ea4
--- /dev/null
@@ -0,0 +1,98 @@
+var Y    = require('Y').Y
+,
+
+NamedTuple =
+exports['NamedTuple'] =
+new Y.Class('NamedTuple', [], {
+    __fields__ : [],
+    
+    __static__ : {
+        fromObject : function tupleFromObject(o){
+            var NamedTupleType = this;
+            
+            if (!o)
+                return new NamedTupleType();
+            
+            var fields = NamedTupleType.prototype.__fields__
+            ,   values = fields.reduce(function(vals, name){
+                    vals.push( o[name] );
+                    return vals;
+                }, [])
+            ;
+            return NamedTupleType.instantiate(values);
+        }
+    },
+    
+    
+    /**
+     * @param {*} values... Values with which to populate the NamedTuple. They will also map to attribute names as specified by the call to create().
+     * @return {this}
+     */
+    init : function initNamedTuple(){
+        var args = arguments
+        ,   L    = args.length
+        ,   fL   = this.__fields__.length
+        ;
+        
+        if ( fL < L )
+            throw new Error(this.className+' only accepts '+fL+' arguments, but '+L+' given!');
+        
+        for (var i=0; i<L; i++)
+            this.push( args[i] );
+    },
+    
+    // TODO: iterate yielding (val, field), not (val, idx)
+    
+    toString : function(){
+        return (this.className +
+            '(' + this.__fields__.map(this._fieldToString, this).join(', ') + ')');
+    },
+    
+    _fieldToString : function _fieldToString(name, idx){
+        return name+'='+this[idx];
+    }
+})
+,
+
+createNamedTuple =
+exports['create'] =
+/**
+ * Creates a new Array subclass named `className`. The new subclass is used to create Array-like objects with
+ * fields accessible by attribute lookup as well as being indexable; the collection has YArray-compliant iteration methods (reduce,
+ * map, forEach, filter), and a helpful toString() which lists contents in order as name=value.
+ * 
+ * @param {String} className Subclass name.
+ * @param {String|Array} fields A single string with each fieldname separated by whitespace and/or commas,
+ *  for example `'x y'` or `'x, y'`. Alternatively, `fields` can be a sequence of strings such as `['x', 'y']`.
+ * @return {Class} New NamedTuple and Array subclass.
+ */
+function createNamedTuple(className, fields){
+    if ( typeof fields == 'string' )
+        fields = fields.split(/,?\s+/);
+    
+    var NewNamedTuple =
+        Y.subclass(className, new NamedTuple(), {
+            '__fields__' : fields
+        });
+    Y(fields).forEach(defineField, NewNamedTuple.prototype);
+    
+    return NewNamedTuple;
+};
+
+// `this` will be set to the NamedTuple subclass' prototype
+function defineField(name, idx){
+    Object.defineProperty( this, name+'', {
+        'get' : function(){  return this[idx]; },
+        'set' : function(v){ return (this[idx] = v); }
+    });
+}
+
+NamedTuple.create = createNamedTuple;
+
+
+
+// TODO: rename flag, a la python (lopri).
+/* From Python's docs:
+    If rename is true, invalid fieldnames are automatically replaced with positional names.
+    For example, ['abc', 'def', 'ghi', 'abc'] is converted to ['abc', '_1', 'ghi', '_3'], eliminating the keyword def and the duplicate fieldname abc.
+*/
index 9732c07..9e311d0 100644 (file)
@@ -5,6 +5,7 @@ var Y = require('Y').Y
 ,   bagbag    = require('tanks/inventory/bagbag')
 ,   equipslot = require('tanks/inventory/equipslot')
 ,   belt      = require('tanks/inventory/belt')
+,   loot      = require('tanks/inventory/loot')
 ;
 Y.core.extend(exports, {
     'inventory' : inventory,
@@ -23,5 +24,8 @@ Y.core.extend(exports, {
     'EquipSlot' : equipslot.EquipSlot,
     
     'belt'      : belt,
-    'Belt'      : belt.Belt
+    'Belt'      : belt.Belt,
+    
+    'loot'      : loot,
+    'LootTable' : loot.LootTable
 });
diff --git a/src/tanks/inventory/loot.cjs b/src/tanks/inventory/loot.cjs
new file mode 100644 (file)
index 0000000..c1dd74d
--- /dev/null
@@ -0,0 +1,59 @@
+var Y = require('Y').Y
+,   evt = require('evt')
+,   Speciated = require('ezl/mixins/speciated').Speciated
+,   Item = require('tanks/thing/item').Item
+,
+
+/**
+ * A list of (item, probability) tuples to be tested in order when a unit dies.
+ * The first to pass is dropped; if all fail, nothing is dropped.
+ */
+LootTable =
+exports['LootTable'] =
+evt.subclass('LootTable', {
+    __mixins__ : [ Speciated ],
+    items : null, // {LootEntry...}
+    
+    
+    init : function initLootTable(){
+        this.items = this.items || [];
+    },
+    
+    roll : function roll(){
+        return this.items.reduce(this._roll, null);
+    },
+    
+    _roll : function _roll(drop, o){
+        return drop || (Math.random() < o.p ? o.item : null);
+    }
+    
+})
+,
+
+LootTableEntry =
+exports['LootTableEntry'] =
+function LootTableEntry(p, item){
+    this.p = p;
+    this.item = item;
+};
+LootTableEntry.prototype.toString =
+    function(){ return "("+this.p+", "+this.item+")"; };
+
+LootTable.on('speciate',
+    function onLootTableSpeciate(evt){
+        var NewLootTable = evt.data.species
+        ,   proto        = NewLootTable.fn
+        ;
+        
+        proto.items = proto.items.map(
+            function(spec){
+                var sep = spec.indexOf(' ')
+                ,   p   = parseFloat(spec.slice(0, sep))
+                ,   id  = spec.slice(sep+1)
+                ;
+                return new LootTableEntry(p, Item.lookupOrSpeciate(id));
+            });
+        
+        NewLootTable.instance = new NewLootTable();
+    });
+
index efdab04..dfdfe63 100644 (file)
@@ -2,5 +2,6 @@ require('Y').Y.core
 .extend(exports, {
     'Meronomic'   : require('tanks/mixins/meronomic').Meronomic,
     'Quantified'  : require('tanks/mixins/quantified').Quantified,
-    'Inventoried' : require('tanks/mixins/inventoried').Inventoried
+    'Inventoried' : require('tanks/mixins/inventoried').Inventoried,
+    'Lootable'    : require('tanks/mixins/lootable').Lootable
 });
diff --git a/src/tanks/mixins/lootable.cjs b/src/tanks/mixins/lootable.cjs
new file mode 100644 (file)
index 0000000..0428621
--- /dev/null
@@ -0,0 +1,44 @@
+var Y = require('Y').Y
+,   Mixin = require('evt').Mixin
+,   LootTable = require('tanks/inventory/loot').LootTable
+,
+
+Lootable =
+exports['Lootable'] =
+Mixin.subclass('Lootable', {
+    __bind__   : [ 'onDeathDropLoot' ],
+    lootTable : '',
+    
+    
+    init : function initLootable(){
+        this.on('destroy', this.onDeathDropLoot);
+    },
+    
+    onDeathDropLoot : function onDeathDropLoot(evt){
+        if ( !this.lootTable ) return;
+        
+        var LootType = this.lootTable.roll()
+        ,   loc = this.loc
+        ;
+        if ( LootType && loc ) {
+            var loot = new LootType();
+            // console.log('l3wtz dropped! %s at %s (of %s)', loot, loc, LootType);
+            this.game.addUnit(loot, loc.x, loc.y);
+        }
+    },
+    
+    onMixin : function mixLootable(evt){
+        var cls = evt.data.cls
+        ,   mxn = evt.data.mixin
+        ,   proto = cls.fn
+        ,   table = proto.lootTable
+        ;
+        
+        cls.on('subclass', mixLootable); // ensure we update the loot table for any child-classes
+        
+        if ( table && !(table instanceof LootTable) ){
+            proto.lootTable = LootTable.lookupOrSpeciate(table).instance;
+        }
+    }
+    
+});
index cfec61c..acb9af5 100644 (file)
@@ -6,7 +6,7 @@ Meronomic =
 exports['Meronomic'] =
 Mixin.subclass('Meronomic', {
     
-    onCreate : function initMeronomic(evt, self){
+    init : function initMeronomic(){
         
     }
     
index 21cae60..8812b41 100644 (file)
@@ -217,7 +217,7 @@ Thing.subclass('Item', {
 });
 
 Item.on('speciate',
-    function onSpeciate(evt){
+    function onItemSpeciate(evt){
         var NewItem = evt.data.species
         ,   proto   = NewItem.fn
         ;
index 62b9ffa..d90dda4 100644 (file)
@@ -16,6 +16,7 @@ var Y  = require('Y').Y
 ,   Traversal   = require('tanks/map/pathing/traversal').Traversal
 ,   Thing       = require('tanks/thing/thing').Thing
 ,   Bullet      = require('tanks/thing/bullet').Bullet
+,   Lootable    = require('tanks/mixins/lootable').Lootable
 
 ,   _X = 0, _Y = 1
 ,
@@ -27,6 +28,7 @@ Thing.subclass('Tank', function(Tank){
     
     Y.core.descriptors(this, {
         isCombatant : true,
+        lootTable : '',
         
         colors : {
             body   : '#83BB32',
@@ -70,6 +72,8 @@ Thing.subclass('Tank', function(Tank){
         this.onBulletDeath = this.onBulletDeath.bind(this);
     };
     
+    Lootable.mixInto(Tank);
+    
     this['onBulletDeath'] =
     function onBulletDeath(evt){ this.nShots--; };