- types/bullets
- types/items
# - types/weapons
+- types/loots
- types/units
- types/levels
# - types/player
# align: 2
# loc: [425,125]
items:
- - type: rockets
- loc: [325,275]
+ # - type: rockets
+ # loc: [325,275]
- type: nitro
loc: [325,25]
--- /dev/null
+name: loots
+defaults:
+ symbol: tanks/inventory/loot.LootTable
+types:
+ rich:
+ items:
+ - 0.1 super_armor
+ - 0.2 rockets
+ - 0.3 nitro
symbol: tanks/thing/thing.Thing
level: 1
projectile: normal
+ lootTable : ''
stats:
hp : 1 # health
move : 1.0 # move speed (squares/sec)
desc: A very green tank.
tags: [ 'tank' ]
symbol: tanks/thing/tank.Tank
+ lootTable : rich
stats:
hp : 1
move : 0.75
//#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'),
--- /dev/null
+var Y = require('Y').Y
+, namedtuple = require('ezl/struct/namedtuple')
+;
+Y.core.extend(exports, {
+ 'namedtuple' : namedtuple,
+ 'NamedTuple' : namedtuple.NamedTuple
+});
--- /dev/null
+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.
+*/
, 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,
'EquipSlot' : equipslot.EquipSlot,
'belt' : belt,
- 'Belt' : belt.Belt
+ 'Belt' : belt.Belt,
+
+ 'loot' : loot,
+ 'LootTable' : loot.LootTable
});
--- /dev/null
+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();
+ });
+
.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
});
--- /dev/null
+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;
+ }
+ }
+
+});
exports['Meronomic'] =
Mixin.subclass('Meronomic', {
- onCreate : function initMeronomic(evt, self){
+ init : function initMeronomic(){
}
});
Item.on('speciate',
- function onSpeciate(evt){
+ function onItemSpeciate(evt){
var NewItem = evt.data.species
, proto = NewItem.fn
;
, 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
,
Y.core.descriptors(this, {
isCombatant : true,
+ lootTable : '',
colors : {
body : '#83BB32',
this.onBulletDeath = this.onBulletDeath.bind(this);
};
+ Lootable.mixInto(Tank);
+
this['onBulletDeath'] =
function onBulletDeath(evt){ this.nShots--; };