Adds Barrel class to pull it apart from a unit's sprite.
authordsc <david.schoonover@gmail.com>
Sat, 12 Mar 2011 20:00:23 +0000 (12:00 -0800)
committerdsc <david.schoonover@gmail.com>
Sat, 12 Mar 2011 20:00:23 +0000 (12:00 -0800)
data/types/units.yaml
src/ezl/layer/layerable.cjs
src/ezl/shape/rect.cjs
src/tanks/fx/barrel.cjs [new file with mode: 0644]
src/tanks/fx/index.cjs
src/tanks/map/pit.cjs
src/tanks/thing/shield.cjs

index 4fbbada..c0bb747 100644 (file)
@@ -122,10 +122,11 @@ types:
             hp    : 1
             move  : 0.5
             power : 1
-        orbit : 9
-        width  : 9
-        height : 9
-        color: '#0A9CFF'
-    
+        orbit  : 8
+        width  : 8
+        height : 8
+        colors:
+            body  : '#0A9CFF'
+            shine : '#195FBC'
     
     
index 40af4a6..3d19d0e 100644 (file)
@@ -249,7 +249,6 @@ Mixin.subclass('Layerable', {
             return new Vec(this.layerWidth,this.layerHeight);
         
         var bb = this.bbox
-        ,   ox1 = bb.x1, oy1 = bb.y1
         ,   ow = this.realWidth,  lw, olw = lw = this.layerWidth,  cw
         ,   oh = this.realHeight, lh, olh = lh = this.layerHeight, ch
         ,   changes = {}, cchanges = {}
@@ -263,7 +262,7 @@ Mixin.subclass('Layerable', {
             this.realWidth = w;
             lw = Math.round(w); // HTMLElement.{width,height} is a long
             if (lw !== olw) {
-                this.layerWidth = changes.width = lw;
+                this.layerWidth  = changes.width  = lw;
                 this.canvasWidth = cchanges.width = cw = Math.ceil(w);
                 dirtied = true;
             }
@@ -273,7 +272,7 @@ Mixin.subclass('Layerable', {
             this.realHeight = h;
             lh = Math.round(h); // HTMLElement.{width,height} is a long
             if (lh !== olh) {
-                this.layerHeight = changes.height = lh;
+                this.layerHeight  = changes.height  = lh;
                 this.canvasHeight = cchanges.height = ch = Math.ceil(h);
                 dirtied = true;
             }
@@ -284,10 +283,10 @@ Mixin.subclass('Layerable', {
             
             var x1 = bb.x1 | 0
             ,   y1 = bb.y1 | 0;
-            if (ox1 !== x1)
-                changes.left = x1;
-            if (oy1 !== y1)
-                changes.top = y1;
+            if (x1 !== this.layerX)
+                changes.left = this.layerX = x1;
+            if (y1 !== this.layerY)
+                changes.top = this.layerY = y1;
             
             this.layer.css(changes);
             
@@ -420,6 +419,20 @@ Mixin.subclass('Layerable', {
     scrollWidth  : function scrollWidth(v){  return this.layer.attr('scrollWidth',  v); },
     scrollHeight : function scrollHeight(v){ return this.layer.attr('scrollHeight', v); },
     
+    /**
+     * Calculates angle in radians from the Layer's origin to (x,y).
+     * @param {Number} x
+     * @param {Number} y
+     * @return {Number} Angle in radians.
+     */
+    angleTo : function angleTo(x,y){
+        var loc = this.loc
+        ,   x0 = x - loc.x
+        ,   y0 = y - loc.y
+        ;
+        return Math.atan2(y0,x0);
+    },
+    
     
     /// Transformations ///
     
@@ -441,14 +454,25 @@ Mixin.subclass('Layerable', {
     },
     
     /**
+     * @return {Number} The layer's current angle of rotation in radians.
+     */
+    /**
      * Rotates this layer by r radians.
+     * @param {Number} r Radians to rotate.
+     * @return {this}
+     */
+    /**
+     * Rotates this layer by the angle between the layer's origin and (x,y).
+     * @param {Number} x
+     * @param {Number} y
+     * @return {this}
      */
-    rotate : function rotate(r){
+    rotate : function rotate(x,y){
         var t = this.transform;
-        if (r === undefined)
+        if (x === undefined && y === undefined)
             return t.rotate;
         
-        t.rotate = r;
+        t.rotate = (y === undefined ? x : this.angleTo(x,y));
         return this._applyTransforms();
     },
     
@@ -630,24 +654,58 @@ Mixin.subclass('Layerable', {
         return this;
     },
     
-    erase : function erase(x,y, w,h, alsoChildren){
-        this._erased.push({ 'x':x,'y':y, 'w':w,'h':h, 'alsoChildren':alsoChildren });
+    eraseRect : function eraseRect(x,y, w,h, alsoChildren){
+        return this._addErasedPath('rect', x,y, { 'w':w,'h':h }, alsoChildren);
+    },
+    
+    eraseCircle : function eraseCircle(x,y, r, alsoChildren){
+        return this._addErasedPath('circle', x,y, { 'r':r }, alsoChildren);
+    },
+    
+    _addErasedPath : function _addErasedPath(type, x,y, args, alsoChildren){
+        this._erased.push( Y.extend({ 'type':type, 'x':x,'y':y, 'alsoChildren':alsoChildren }, args||{}) );
         this.dirty = true;
         if (alsoChildren)
-            this.children.invoke('erase', x,y, w,h, alsoChildren);
+            this.children.invoke('_addErasedPath', x,y, args, alsoChildren);
         return this;
     },
     
     _erase : function _erase(args){
-        var x = args.x, y = args.y
-        ,   w = args.w, h = args.h;
+        var ctx = this.ctx
+        ,   gco = ctx.globalCompositeOperation
+        
+        ,   pw = this.canvas.width()
+        ,   ph = this.canvas.height()
+        
+        ,   type = args.type
+        ,   x = args.x, y = args.y
+        ;
         
-        if (w < 0) w = this.canvas.width()  + w;
-        if (h < 0) h = this.canvas.height() + h;
+        ctx.beginPath();
+        ctx.fillStyle = '#000000';
+        ctx.lineWidth = 0;
+        
+        switch (args.type) {
+            case 'rect':
+                var w = args.w, h = args.h;
+                if (w < 0) w = pw + w;
+                if (h < 0) h = ph + h;
+                ctx.clearRect(x,y, w,h);
+            break;
+            
+            case 'circle':
+                var r = args.r;
+                if (r <= 0) r = Math.min(pw-x,ph-y)/2 + (r || 0);
+                ctx.globalCompositeOperation = 'destination-out';
+                ctx.arc(x,y, r, 0, Math.PI*2, false);
+                ctx.fill();
+            break;
+            
+            default: break;
+        }
         
-        this.ctx.beginPath();
-        this.ctx.clearRect(x,y, w,h);
-        this.ctx.closePath();
+        ctx.closePath();
+        ctx.globalCompositeOperation = gco;
     },
     
     /**
@@ -757,7 +815,7 @@ Mixin.subclass('Layerable', {
         ;
         // Check we're in the DOM, otherwise jQuery vomits
         if ( el && el.attr('parentNode') )
-            pos = this.position();
+            pos = el.position();
         return this.className+'['+pos.left+','+pos.top+']( children='+kids+' )';
     }
 });
index 2918a2b..3cce462 100644 (file)
@@ -20,10 +20,10 @@ Shape.subclass('Rect', {
     },
     
     toString : function(){
-        var loc = this.loc
-        ,   x1 = loc.x, y1 = loc.y
-        ,   x2 = x1+this.layerWidth, y2 = y1 + this.layerHeight;
-        return this.className+'['+x1+','+y1+', '+x2+','+y2+'](w='+this.layerWidth+', h='+this.layerHeight+')';
+        // var loc = this.loc
+        // ,   x1 = loc.x, y1 = loc.y
+        // ,   x2 = x1+this.layerWidth, y2 = y1 + this.layerHeight;
+        return this.className+this.bbox+'(w='+this.layerWidth+', h='+this.layerHeight+')';
     }
     
 });
diff --git a/src/tanks/fx/barrel.cjs b/src/tanks/fx/barrel.cjs
new file mode 100644 (file)
index 0000000..339969f
--- /dev/null
@@ -0,0 +1,77 @@
+var Y = require('Y').Y
+,   Vec  = require('ezl/math/vec').Vec
+,   Rect = require('ezl/shape').Rect
+
+// ,   ORIGIN = new Vec(0,0)
+// ,   FACING_RIGHT = exports['FACING_RIGHT'] = 'right'
+// ,   FACING_LEFT  = exports['FACING_LEFT']  = 'left'
+,
+
+Barrel =
+exports['Barrel'] =
+Rect.subclass('Barrel', {
+    originX : 0,
+    originY : '50%',
+    // facing  : FACING_RIGHT,
+    
+    
+    /**
+     * @constructor
+     * @param {Number} width
+     * @param {Number} height
+     * @param {Number|String} [pivotX=0] x coordinate of pivot point (in pixels or as a percentage of width).
+     * @param {Number} [pivotY='50%'] y coordinate of pivot point (in pixels or as a percentage of height).
+     * @return {this}
+     */
+    init : function initBarrel(w,h, pivotX,pivotY){
+        this.originX = pivotX || 0;
+        this.originY = pivotY || '50%';
+        // this.facing  = facing || FACING_RIGHT;
+        Rect.init.call(this, w,h);
+    },
+    
+    
+    /**
+     * Converts (pageX,pageY) from global to local coordinates (x,y), and then
+     * rotates this layer by the angle between the layer's origin and (x,y).
+     * @param {Number} pageX
+     * @param {Number} pageY
+     * @return {this}
+     */
+    rotateRelPage : function rotateRelPage(pageX,pageY){
+        var off = this.offset()
+        ,   w = this.realWidth, h = this.realHeight
+        ,   x = off.left + w/2 - pageX
+        ,   y = off.top  + h/2 - pageY
+        ,   theta = Math.atan2(-y,-x)
+        ;
+        return this.rotate(theta);
+    },
+    
+    /**
+     * Calculates the coordinates of the barrel tip based on barrel facing and rotation.
+     * @param {Number} [wiggle=0] Optional offset distance in front of the barrel tip.
+     * @param {Boolean} [relParent=false] If true, coordinates will be relative to parent's origin.
+     * @return {Vec} Coordinates of barrel tip (with offset).
+     */
+    barrelTipLoc : function barrelTipLoc(wiggle, relParent){
+        var theta = this.transform.rotate
+        ,   sin = Math.sin(theta),  cos = Math.cos(theta)
+        
+        // (sqrt(2)/2 * wiggle) is max diagonal to ensure projectile doesn't overlap with firing unit
+        ,   len = this.bbox.originRight + (wiggle || 0)*0.707
+        
+        // ,   len = this.bbox.originDist(this.facing) + (wiggle || 0)*0.707
+        // ,   olen = (this.facing === FACING_LEFT ? this.bbox.originLeft : this.bbox.originRight)
+        // ,   len  = olen + (wiggle || 0)*0.707
+        
+        ,   o = ((relParent && this.parent) ? this.parent.loc : ORIGIN)
+        ,   x = o.x + this.loc.x + len*cos
+        ,   y = o.y + this.loc.y + len*sin
+        ;
+        return new Vec(x,y);
+    }
+    
+    
+})
+;
index aa388b6..698054c 100644 (file)
@@ -1,5 +1,8 @@
-var Y = require('Y').Y;
-
-Y.extend(exports, {
-    'Explosion' : require('tanks/fx/explosion').Explosion
-});
\ No newline at end of file
+var Y = require('Y').Y
+,   barrel    = require('tanks/fx/barrel')
+,   explosion = require('tanks/fx/explosion')
+;
+Y.core.extend(exports, {
+       'Barrel'    : barrel.Barrel,
+       'Explosion' : explosion.Explosion
+});
index c562065..271f686 100644 (file)
@@ -56,7 +56,7 @@ Wall.subclass('Pit', {
                 .position(0,0)
                 .fill(this.shadowFillStyle)
                 .stroke(this.strokeStyle, this.lineWidth)
-                .erase(ss,ss, -ss,-ss)
+                .eraseRect(ss,ss, -ss,-ss)
                 .appendTo( this.shape );
         
         return this;
index 43a2820..3a74f7d 100644 (file)
@@ -28,11 +28,14 @@ Component.subclass('Shield', {
     isReflective : false,       // Projectiles bounce off agent rather than explode?
     hasInventory : false,       // Agent can acquire items?
     
-    orbit  : 9,
-    width  : 9,
-    height : 9,
+    orbit  : 8,
+    width  : 8,
+    height : 8,
     
-    color : '#0A9CFF',
+    colors: {
+        body : '#0A9CFF',
+        shine : '#195FBC'
+    },
     
     stats : {}, // radial speed in revolutions/sec
     
@@ -52,6 +55,7 @@ Component.subclass('Shield', {
         this.radsPerTick = TWO_PI * this.stats.move.val / 1000 * MS_PER_FRAME;
         
         this.trajectory = new CircularTrajectory(this, oloc.x,oloc.y, this.radius, this.radsPerTick, this.radialPosition);
+        this.colors = Y.extend({}, this.colors);
         this.act(0);
     },
     
@@ -70,26 +74,26 @@ Component.subclass('Shield', {
         ,   to = tvsl.traverse(this.radsPerTick);
         
         this.game.moveThingTo(this, to.x,to.y);
-        
-        // var oloc = this.owner.loc
-        // ,   t = (this.radialPosition += this.radsPerTick)
-        // ,   r = this.radius
-        // ,   x = oloc.x + r*Math.cos(t)
-        // ,   y = oloc.y + r*Math.sin(t)
-        // ;
-        // this.game.moveThingTo(this, x,y);
         return this;
     },
     
     render : function render(parent){
         this.remove();
         
-        var loc = this.loc;
-        this.shape = new Circle(this.width/2)
+        var loc = this.loc
+        ,   d = this.width, r = d/2;
+        
+        this.shape = new Circle(r)
             .position(loc.x, loc.y)
-            .fill(this.color)
+            .fill(this.colors.body)
             .appendTo( parent );
         
+        this.shine = new Circle(r)
+            .position(r,r)
+            .fill(this.colors.shine)
+            // .eraseCircle(-r,-r, d*SQRT_TWO)
+            .eraseCircle(r*0.2,-r*1.5, 1.1*d*SQRT_TWO)
+            .appendTo( this.shape );
         return this;
     }
 })