Adds barrel object and config.
authordsc <david.schoonover@gmail.com>
Fri, 25 Mar 2011 10:53:39 +0000 (03:53 -0700)
committerdsc <david.schoonover@gmail.com>
Fri, 25 Mar 2011 10:53:39 +0000 (03:53 -0700)
12 files changed:
data/types/units.yaml
src/Y/modules/y.polyevent.cjs
src/Y/op.cjs
src/Y/types/array.cjs
src/Y/types/collection.cjs
src/Y/utils.cjs
src/ezl/layer/layerable.cjs
src/ezl/struct/set.cjs [new file with mode: 0644]
src/ezl/util/tree/pointquadtree.cjs
src/tanks/inventory/inventory.cjs
src/tanks/thing/player.cjs
src/tanks/thing/tank.cjs

index c0bb747..d1913d0 100644 (file)
@@ -20,6 +20,13 @@ defaults:
         dodge         : 1.00        # dodge an incoming bullet
         shootIncoming : 0.25        # shoot down incoming bullet
         shootEnemy    : 0.75        # shoot at enemy tank if in range
+    barrel:
+        width         : unit.width * 0.75
+        height        : unit.height / 6
+        originX       : 2
+        originY       : 50%
+        x             : 50%
+        y             : 50%
     art:
         map_icon: ''
         inv_icon: ''
index 56638e8..312167a 100644 (file)
@@ -274,4 +274,6 @@ function setupRepeater(fromEmitter, toEmitter, evt){
 }
 
 Y.extend( exports, { 'repeat':repeat, 'setupRepeater':setupRepeater });
-Y.extend( Y['event'], exports );
+Y['polyevent'] = exports;
+
+Y.extend( Y['event'], { 'PolyEmitter':PolyEmitter, 'PolyEvent':PolyEvent, 'repeat':repeat, 'setupRepeater':setupRepeater });
index 5b7aeed..447e857 100644 (file)
@@ -43,6 +43,7 @@ var core = require('Y/core')
     K         : function(k){       return function(){ return k; }; },
     kObject   : function(){        return {}; },
     kArray    : function(){        return []; },
+    kThis     : function(){        return this; },
     val       : function(def,o){   return o !== undefined ? o : def; },
     ok        : function(o){       return o !== undefined && o !== null; },
     first     : function(a){       return a; },
index 011cb36..ab3a1fd 100644 (file)
@@ -159,12 +159,12 @@ YCollection.subclass('YArray', function(YArray){
     
     this['unique'] =
     function unique(){
-        return this.reduce(
+        return new YArray( this._o.reduce(
             function(acc, v, i){
                 if (acc.indexOf(v) === -1)
                     acc.push(v);
                 return acc;
-            }, new YArray(), this);
+            }, [], this) );
     };
     
     YArray.flatten = flatten;
index d30fbcf..8055012 100644 (file)
@@ -98,7 +98,7 @@ YBase.subclass('YCollection', {
         }, new this.__class__() );
     },
     
-    amap : function amap( fn ){
+    'amap' : function amap( fn ){
         var cxt = arguments[1] || this;
         return this.reduce(function(acc, v, k, o){
             return acc.push(fn.call(cxt, v, k, o));
@@ -121,6 +121,15 @@ YBase.subclass('YCollection', {
         return -1;
     },
     
+    'lastIndexOf' : function lastIndexOf( value ){
+        var o = this._o
+        ,   idx = -1;
+        for ( var name in o )
+            if ( o[name] === value )
+                idx = name;
+        return idx;
+    },
+    
     'has' : function has( value ){
         return ( this.indexOf(value) !== -1 );
     },
@@ -132,7 +141,7 @@ YBase.subclass('YCollection', {
         }, true);
     },
     
-    'any' : function any( fn ){
+    'some' : function some( fn ){
         var self = this, fn = fn || op.bool;
         return this.reduce(function(acc, v, k, o){
             return acc || fn.call(self, v, k, o);
@@ -180,7 +189,31 @@ YBase.subclass('YCollection', {
     'intersect' : function intersect(a){
         var A = Y(a);
         return this.filter(A.has, A);
-    }
+    },
+    
+    // /**
+    //  * Set Union (A v B)
+    //  * @param {Array|Object|YCollection} a Comparison collection.
+    //  * @return {YArray} A new YArray of all elements in both collections, but without duplicates.
+    //  */
+    // 'union' : function union(a){
+    //     return this.clone().extend(a).unique();
+    // },
+    // 
+    // /**
+    //  * Set Difference (A - B)
+    //  * @param {Array|Object|YCollection} a Comparison collection.
+    //  * @return {YArray} A new YArray of only elements in this not in supplied collection.
+    //  */
+    // 'difference' : function difference(a){
+    //     var A = Y(a);
+    //     return this.filter(Y(A.has).compose(op.not), A);
+    // },
+    // 
+    // // Symmetric Difference
+    // 'xor' : function xor(a){
+    //     return this.difference(a).extend( Y(a).difference(this) );
+    // }
     
 });
 
index d3b94a1..52d1de7 100644 (file)
@@ -92,17 +92,17 @@ function proxy(options){
     
     // TODO: Throw errors on invalid values
     
-    if (isFunction(target)) target = target.prototype;
-    if (isFunction(donor))  donor  = donor.prototype;
-    if (!isArray(names))    names  = [names];
+    if (typeof target == 'function') target = target.prototype;
+    if (typeof donor  == 'function') donor  = donor.prototype;
+    if (!isArray(names))             names  = [names];
     
     // Need a closure to capture the name
     names.forEach(function(name){
         var fn = donor[name];
-        if ( !(isFunction(fn) && (o.override || !(name in target))) )
+        if ( !(typeof fn == 'function' && (o.override || !(name in target))) )
             return;
         
-        target[name] = function(){
+        var wrapper = target[name] = function(){
             var A = arguments, r, context;
             
             if (o.fnFirst)
@@ -118,6 +118,7 @@ function proxy(options){
             r = fn.apply(context, A);
             return (o.chain ? this : (o.wrap ? o.wrap(r) : r));
         };
+        wrapper.name = name; // works in webkit
     });
     
     return o;
index 3d19d0e..83cea06 100644 (file)
@@ -13,6 +13,7 @@ var Y           = require('Y').Y
 CONTEXT_ATTRS  = Y('globalAlpha globalCompositeOperation strokeStyle fillStyle lineWidth lineCap lineJoin miterLimit shadowOffsetX shadowOffsetY shadowBlur shadowColor'.split(' ')),
 FAUX_ACCESSORS = Y('width height position stroke fill origin rotate scale translate title'.split(' '))
 , _X = 0, _Y = 1
+, CALC_LOC = new Loc(0,0)
 ,
 
 
@@ -365,7 +366,18 @@ Mixin.subclass('Layerable', {
         if (x === null || x === undefined) x = bb.x1;
         if (y === null || y === undefined) y = bb.y1;
         
-        var bb = this.bbox;
+        var p  = this.parent
+        ,   bb = this.bbox
+        ,   sx = (typeof x == 'string') // TODO: store and recalc on w/h change
+        ,   sy = (typeof y == 'string')
+        ;
+        if (sx || sy) {
+            if (!p) return this;
+            var xy = CALC_LOC.setXY(x,y).absolute(p.realWidth, p.realHeight);
+            x = xy.x;
+            y = xy.y;
+        }
+        
         if (x === bb.x1 && y === bb.y1)
             return this;
         
diff --git a/src/ezl/struct/set.cjs b/src/ezl/struct/set.cjs
new file mode 100644 (file)
index 0000000..0488796
--- /dev/null
@@ -0,0 +1,226 @@
+var Y           = require('Y').Y
+,   op          = require('Y/op')
+,   proxy       = require('Y/utils').proxy
+,   YCollection = require('Y/types/collection').YCollection
+,   slice       = Array.prototype.slice
+,
+
+
+Set =
+exports['Set'] =
+YCollection.subclass('Set', null, function setupSet(Set){
+    Y.extend(this, {
+        _byId : {},
+    });
+    
+    this['init'] =
+    function initSet(){
+        this._o = new Y.YArray();
+        this._byId = {};
+        if ( arguments.length )
+            this.update( arguments );
+    };
+    
+    var size =
+    this['size'] =
+    function size(){
+        return this._o.length;
+    };
+    
+    this['attr'] = op.attr.methodize();
+    Object.defineProperty(this, 'length', { 'get':size });
+    
+    this['clone'] =
+    function clone(){
+        return new Set(this._o);
+    };
+    
+    this['clear'] =
+    function clear(){
+        this._o = new Y.YArray();
+        this._byId = {};
+        return this;
+    };
+    
+    this['indexOf'] =
+    this['lastIndexOf'] =
+    function unsupported(){
+        throw new Error(arguments.callee.name+' is unsupported for type '+this.className+'!');
+    };
+    
+    function _getIdSafe(v){
+        switch (typeof v) {
+            case 'undefined':
+                return undefined;
+            break;
+            
+            case 'boolean':
+            case 'string':
+            case 'number':
+                return v;
+            break;
+            
+            case 'object':
+            case 'function':
+            default:
+                if ( !('__id__' in v) )
+                    return undefined;
+                else
+                    return v.__id__;
+            break;
+        }
+    }
+    
+    this['_getId'] =
+    function _getId(v){
+        var id = _getIdSafe(v);
+        if (id === undefined)
+            throw new Error('All Set elements must be hashable! v='+v);
+        else
+            return id;
+    };
+    
+    this['has'] =
+    function has(v){
+        return (_getIdSafe(v) in this._byId);
+    };
+    
+    function addIt(v){
+        var id = this._getId(v);
+        
+        if (!(id in this._byId)) {
+            this._byId[id] = v;
+            this._o.push(v);
+        }
+        
+        return this;
+    }
+    
+    this['add'] =
+    this['push'] =
+    this['unshift'] =
+    function add(v){
+        var L = arguments.length;
+        
+        if (L === 1)
+            addIt.call(this, v);
+        else if (L > 1)
+            slice.call(arguments,0).forEach(addIt, this);
+        
+        return this;
+    };
+    
+    this['update'] =
+    this['extend'] =
+    function update(vs){
+        Y(vs).forEach(addIt, this);
+        return this;
+    };
+    
+    this['join'] =
+    function join(vs){
+        return this.clone().update(vs);
+    };
+    
+    function removeIt(v){
+        var id = this._getId(v);
+        
+        if ( id in this._byId ) {
+            delete this._byId[id];
+            this._o.splice(this._o.indexOf(v), 1);
+        }
+        
+        return this;
+    }
+    
+    this['remove'] =
+    this['discard'] =
+    function remove(v){
+        var L = arguments.length;
+        
+        if (L === 1)
+            removeIt.call(this, v);
+        else if (L > 1)
+            slice.call(arguments,0).forEach(removeIt, this);
+        
+        return this;
+    };
+    
+    this['shift'] =
+    this['pop'] =
+    function shift(){
+        if (!this._o.length) return;
+        
+        var v = this._o.shift()
+        ,   id = this._getId(v);
+        delete this._byId[id];
+        return v;
+    };
+    
+    this['unique'] = op.kThis;
+    
+    /**
+     * Tests if `a` is a Collection and has all elements in common with the set.
+     * Sets are equal if and only if their intersection has the same size as both sets.
+     * @param {Collection} a
+     * @return {Boolean}
+     */
+    this['equals'] =
+    function equals(a){
+        if (!a) return false;
+        var L = this._o.length;
+        return (L === a.length) && (L === this.intersect(a).length);
+    };
+    
+    /**
+     * Tests if the set has no elements in common with `a`.
+     * Sets are disjoint if and only if their intersection is the empty set.
+     * @param {Collection} a
+     * @return {Boolean}
+     */
+    this['isDisjoint'] =
+    function isDisjoint(a){
+        if (!a) return true;
+        return !Y(a).some(this.has, this);
+    };
+    
+    /**
+     * Test whether every element in the set is in `a`.
+     * @param {Collection} a
+     * @return {Boolean}
+     */
+    this['isSubset'] =
+    function isSubset(a){
+        if (!a) return false;
+        var A = Y(a);
+        return this.every(A.has, A);
+    };
+    
+    /**
+     * Test whether every element in `a` is in the set.
+     * @param {Collection} a
+     * @return {Boolean}
+     */
+    this['isSuperset'] =
+    function isSuperset(a){
+        if (!a) return false;
+        return Y(a).every(this.has, this);
+    };
+    
+    this['toString'] = function(){
+        return "Set(["+this._o+"])";
+    };
+    
+});
+
+var YArray = Y.YArray
+,   newSet = Set.instantiate;
+
+proxy({ target:Set, donor:YArray, context:'_o',
+    names:['sort', 'zip'] });
+proxy({ target:Set, donor:YArray, context:'_o', wrap:newSet,
+    names:['concat'] });
+proxy({ target:Set, donor:YArray, context:'_o', fnFirst:true, wrap:newSet,
+    names:'map forEach filter intersect union difference xor'.split(' ') });
+proxy({ target:Set, donor:YArray, context:'_o', fnFirst:true,
+    names:'reduce some every'.split(' ') });
index 38b7222..19745fd 100644 (file)
@@ -212,9 +212,9 @@ PointQuadTree = Y.subclass('PointQuadTree', {
     
     _inSubrange : function _inSubrange(x1,y1, x2,y2){
         if (arguments.length === 4)
-            return this._ranges.invoke('containsRange', x1,y1, x2,y2).any();
+            return this._ranges.invoke('containsRange', x1,y1, x2,y2).some();
         else
-            return this._ranges.invoke('contains', x1,y1).any();
+            return this._ranges.invoke('contains', x1,y1).some();
     },
     
     // XXX: Hm. Should this be contains ALL or contains ANY for the range query?
index d8cd401..c772d34 100644 (file)
@@ -94,7 +94,7 @@ evt.subclass('Inventory', {
      */
     canAddItem : function canAddItem(item, bag){
         if (bag === undefined)
-            return this._bags.invoke('canAddItem', item).any();
+            return this._bags.invoke('canAddItem', item).some();
         bag = this.getBag(bag);
         return bag && bag.canAddItem(item);
     },
index 0805a3d..aefeea3 100644 (file)
@@ -140,7 +140,7 @@ Tank.subclass('Player', {
     
     attack : function attack(x,y){
         if ( x === undefined || y === undefined ) {
-            var theta  = this.barrel.transform.rotate
+            var theta  = this.barrelShape.transform.rotate
             ,   sin = Math.sin(theta),  cos = Math.cos(theta)
             ,   t = this.getTurretLoc();
             x = t.x + REF_SIZE*cos;
index 7eae81e..ffe8c34 100644 (file)
@@ -12,11 +12,12 @@ var Y  = require('Y').Y
 ,   constants   = require('tanks/constants')
 ,   BoundsType  = constants.BoundsType
 ,   DensityType = constants.DensityType
-,   LinearTrajectory  = require('tanks/map/pathing/linear-trajectory').LinearTrajectory
+,   LinearTrajectory = require('tanks/map/pathing/linear-trajectory').LinearTrajectory
 ,   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
+,   Barrel      = require('tanks/fx/barrel').Barrel
 
 ,   _X = 0, _Y = 1
 ,
@@ -36,6 +37,15 @@ Thing.subclass('Tank', function(Tank){
             barrel : '#D43B24'
         },
         
+        barrel: {
+            width   : 'unit.width * 0.75',
+            height  : 'unit.height / 6',
+            originX : 2,
+            originY : '50%',
+            x       : '50%',
+            y       : '50%'
+        },
+        
         // Bounding box
         width  : REF_SIZE*0.55,
         height : REF_SIZE*0.55,
@@ -173,7 +183,7 @@ Thing.subclass('Tank', function(Tank){
             return null; // console.log('squelch!', blockers);
         
         if (!xydef) {
-            var theta  = this.barrel.transform.rotate
+            var theta  = this.barrelShape.transform.rotate
             ,   sin = Math.sin(theta),  cos = Math.cos(theta);
             x = tx + REF_SIZE*cos;
             y = ty + REF_SIZE*sin;
@@ -284,7 +294,7 @@ Thing.subclass('Tank', function(Tank){
     function getTurretLoc(){
         var WIGGLE = 2
         ,   loc    = this.loc
-        ,   barrel = this.barrel
+        ,   barrel = this.barrelShape
         
         ,   theta  = barrel.transform.rotate
         ,   sin = Math.sin(theta),  cos = Math.cos(theta)
@@ -316,10 +326,7 @@ Thing.subclass('Tank', function(Tank){
         var colors = this.colors
         ,   w = this.width,  w2 = w/2
         ,   h = this.height, h2 = h/2
-        
         ,   r  = w / 4
-        ,   bw = w * 0.75
-        ,   bh = h / 6
         ;
         
         this.shape = 
@@ -338,12 +345,15 @@ Thing.subclass('Tank', function(Tank){
                 .fill(colors.turret)
                 .appendTo( this.shape ) ;
         
-        this.barrel =
-            new Rect(bw,bh)
-                .position(w2-2, h2-bh/2)
-                .origin(2, bh/2)
-                .fill(colors.barrel)
-                .appendTo( this.shape ) ;
+        var b = Y.map(this.barrel, calcValue, this);
+        
+        this.barrelShape =
+            new Barrel(b.width,b.height, b.originX,b.originY)
+                .appendTo( this.shape ) // have to append early to avoid problems with relative positioning
+                .position(b.x, b.y)
+                // .position(w2-2, h2-bh/2)
+                // .origin(2, bh/2)
+                .fill( colors.barrel ) ;
         
         return this;
     };
@@ -362,7 +372,7 @@ Thing.subclass('Tank', function(Tank){
     
     this['rotateBarrel'] =
     function rotateBarrel(x,y){
-        this.barrel.rotate(this.angleTo(x,y));
+        this.barrelShape.rotate(this.angleTo(x,y));
         return this;
     };
     
@@ -375,9 +385,15 @@ Thing.subclass('Tank', function(Tank){
         ,   y = off.top  + h/2 - pageY
         ,   theta = Math.atan2(-y,-x)
         ;
-        this.barrel.rotate(theta);
+        this.barrelShape.rotate(theta);
         return this;
     };
     
 });
 
+var NUM_OR_PERCENT = /^\s*[\d\.]+\s*%?\s*$/;
+function calcValue(v){
+    var unit = this;
+    return ( NUM_OR_PERCENT.test(v) ? v : eval(v) );
+}
+