Bullet reflections now work!
authordsc <david.schoonover@gmail.com>
Mon, 8 Nov 2010 08:53:07 +0000 (00:53 -0800)
committerdsc <david.schoonover@gmail.com>
Mon, 8 Nov 2010 08:53:07 +0000 (00:53 -0800)
18 files changed:
index.php
src/Y/core.js
src/Y/y-core.js
src/Y/y-op.js
src/portal/layer.js
src/portal/math/line.js
src/portal/math/vec.js
src/portal/shape.js
src/portal/util/eventloop.js
src/portal/util/fps.js
src/tanks/game/map.js
src/tanks/game/player.js
src/tanks/lttl.js
src/tanks/thing/bullet.js
src/tanks/thing/tank.js
src/tanks/ui.js
src/tanks/util/grid.js
test/math/math.test.js

index 40593b9..2a76380 100644 (file)
--- a/index.php
+++ b/index.php
@@ -43,6 +43,11 @@ $scripts = array(
     
     "src/portal/layer.js",
     "src/portal/shape.js",
+    
+    "src/portal/math/math.js",
+    "src/portal/math/vec.js",
+    "src/portal/math/line.js",
+    
     "src/portal/util/quadtree.js",
     "src/portal/util/rbtree.js",
     "src/portal/util/eventloop.js",
index 19e33ae..493e4bf 100644 (file)
@@ -1,6 +1,6 @@
 // Generic Collection Functions
 
-function notSelfOrWrapped(fn){
+function notWrapped(fn){
     var self = arguments.callee.caller;
     return fn && fn !== self && fn.__wraps__ !== self;
 }
@@ -10,7 +10,7 @@ function reduce(o, fn, acc, cxt){
         return acc;
     
     // fn = Function.toFunction(fn);
-    if ( notSelfOrWrapped(o.reduce) )
+    if ( notWrapped(o.reduce) )
         return o.reduce.apply(o, [fn, acc, cxt]);
     
     cxt = cxt || o;
@@ -20,31 +20,39 @@ function reduce(o, fn, acc, cxt){
     return acc;
 }
 
-function set(o, key, value, def){
-    if ( o && notSelfOrWrapped(o.set) )
-        return o.set.apply(o, slice.call(arguments,1));
     
+function set(o, key, value, def){
     if ( o && key !== undefined )
         o[key] = (value !== undefined ? value : def);
-    
     return o;
 }
 
-function attr(o, key, value, def){
-    if ( o && notSelfOrWrapped(o.attr) )
-        return o.attr.apply(o, slice.call(arguments,1));
-    
+function dset(o, key, value, def){
+    if ( o && notWrapped(o.set) )
+        return o.set.apply(o, slice.call(arguments,1));
+    else
+        return set(o, key, value, def);
+}
+
+function attr(o,key,value,def){
     if ( !o || key === undefined ) return o;
     
-    if ( isPlainObject(key) )
+    if ( Y.isPlainObject(key) )
         return extend(o, key);
     
     if ( value !== undefined || def !== undefined ){
-        return set(o, key, value, def);
+        return dset(o, key, value, def);
     } else
         return o[key];
 }
 
+function dattr(o, key, value, def){
+    if ( o && notWrapped(o.attr) )
+        return o.attr.apply(o, slice.call(arguments,1));
+    else
+        return attr(o, key, value, def);
+}
+
 function extend( A, B ){
     return slice.call(arguments,1).reduce(extend._extendall, A);
 }
@@ -52,6 +60,6 @@ extend._extendall = function _extendall(A, donor){
     return reduce(donor, extend._set, A);
 };
 extend._set = function _set(o, v, k){
-    return attr(o, k, v, o[k]);
+    return dattr(o, k, v, o[k]);
 };
 
index 995751b..dced427 100644 (file)
@@ -1,7 +1,7 @@
 
 Y.reduce = reduce;
-Y.set    = set;
-Y.attr   = attr;
+Y.set    = dset;
+Y.attr   = dattr;
 Y.extend = extend;
 
 Y.isFunction = isFunction;
index 5017606..ca5064d 100644 (file)
@@ -36,17 +36,24 @@ Y.op = {
     zrshift: function(x,y){ return x >>> y; },
     
     // values
-    nop:    function(){},
+    nop:    function(x){},
     I:      function(x){       return x; },
     K:      function(k){       return function(){ return k; }; },
     val:    function(def,o){   return o !== undefined ? o : def; },
     ok:     function(o){       return o !== undefined && o !== null; },
     
-    // values & accessors
-    has:    function(k,o){     return k in o; },
-    get:    function(k,o){     return o[k] },
-    getdef: function(def,k,o){ return (k in o ? o[k] : def); },
-    set:    function(o,k,v){   if (o && k !== undefined) o[k] = v; return o; },
+    // reduce-ordered values & accessors
+    khas:    function(k,o){     return k in o; },
+    kget:    function(k,o){     return o[k] },
+    defkget: function(def,k,o){ return (k in o ? o[k] : def); },
+    vkset:   function(o,v,k){   if (o && k !== undefined) o[k] = v; return o; },
+    
+    // curry-ordered values & accessors
+    has:     function(o,k){     return k in o; },
+    get:     function(o,k){     return o[k] },
+    getdef:  function(o,k,def){ return (k in o ? o[k] : def); },
+    set:     set,
+    attr:    attr,
     method: function(name){
         var args = Y(arguments,1);
         return function(obj){
index f88500a..db019dd 100644 (file)
@@ -1,5 +1,14 @@
 (function($, undefined){
 
+var CONTEXT_ATTRS = Y([
+    'globalAlpha', 'globalCompositeOperation',
+    'strokeStyle', 'fillStyle',
+    'lineWidth', 'lineCap', 'lineJoin', 'miterLimit',
+    'shadowOffsetX', 'shadowOffsetY', 'shadowBlur', 'shadowColor'
+]);
+
+
+
 Layer = new Y.Class('Layer', {
     _cssClasses : 'portal layer',
     
@@ -118,6 +127,29 @@ Layer = new Y.Class('Layer', {
     
     /// Attributes ///
     
+    
+    attr : function attr(key, value, def){
+        if (!key) return this;
+        
+        if ( Y.isPlainObject(key) ) {
+            for (var k in key)
+                this.attr(k, key[k]);
+            return this;
+            
+        // } else if ( CONTEXT_ATTRS.has(key) ) {
+        //     var r = Y.attr(this.ctx, key, value, def);
+        //     
+        //     if (r === this.ctx) {
+        //         // This implies we set a property
+        //         this.dirty = true;
+        //         return this;
+        //     } else
+        //         return r;
+        //     
+        } else
+            return Y.op.attr(this, key, value, def);
+    },
+    
     /**
      * Changes the layer's width and then updates the canvas.
      */
@@ -291,19 +323,6 @@ Layer = new Y.Class('Layer', {
     
     /// Drawing Functions ///
     
-    // for debugging
-    point : function point(x,y, color){
-        var ctx = this.ctx;
-        this._openPath(ctx);
-        
-        var r = 2;
-        ctx.arc(x+r,y+r, r, 0, Math.PI*2, false);
-        ctx.fillStyle = color || '#FFFFFF';
-        ctx.fill();
-        
-        this._closePath(ctx);
-        return this;
-    },
     /**
      * @param {CanvasDrawingContext2D} [ctx=this.ctx] Forces context to use rather than the layer's own.
      * @param {Boolean} [force=false] Forces redraw.
@@ -322,7 +341,8 @@ Layer = new Y.Class('Layer', {
     },
     
     _openPath : function _openPath(ctx){
-        var w = this.canvasWidth
+        var self = this
+        ,   w = this.canvasWidth
         ,   h = this.canvasHeight
         ,   neg = this.negBleed
         ;
@@ -337,6 +357,14 @@ Layer = new Y.Class('Layer', {
         
         // ctx.scale(this.absScaleX, this.absScaleY);
         
+        // Set context attributes
+        CONTEXT_ATTRS.forEach(function(name){
+            if (self[name] === undefined)
+                delete ctx[name];
+            else
+                ctx[name] = self[name];
+        });
+        
         return this;
     },
     
@@ -365,6 +393,22 @@ Layer = new Y.Class('Layer', {
         return this;
     },
     
+    // for debugging
+    point : function point(x,y, color){
+        var ctx = this.ctx;
+        // this._openPath(ctx);
+        
+        var r = 2;
+        ctx.beginPath();
+        ctx.arc(x,y, r, 0, Math.PI*2, false);
+        ctx.fillStyle = color || '#FFFFFF';
+        ctx.fill();
+        ctx.closePath();
+        
+        // this._closePath(ctx);
+        return this;
+    },
+    
     
     
     
@@ -439,6 +483,7 @@ $(function(){
     $('<style />')
         .text([
             '.portal.layer { position:absolute; z-index:1; top:0; left:0; line-height:0; }',
+            // '.portal.layer { outline:1px solid #000000; }',
             '.portal.layer canvas { z-index:0; }'
         ].join('\n'))
         .appendTo('head');
index 8ffb956..b774a96 100644 (file)
@@ -1,9 +1,17 @@
+(function(){
+
+var 
+HALF_PI = Math.PI/2,
+SIN_HALF_PI = Math.sin(HALF_PI),
+COS_HALF_PI = Math.cos(HALF_PI);
+
+
 /**
  * A line in the cartesian plane.
  */
 math.Line = new Y.Class('Line', math.Vec, {
     
-    init : function init(x1,y1, x2,y2, t){
+    init : function init(x1,y1, x2,y2, tdist){
         this.x1 = x1; this.y1 = y1; this.p1 = new math.Vec(x1,y1);
         this.x2 = x2; this.y2 = y2; this.p2 = new math.Vec(x2,y2);
         
@@ -13,19 +21,41 @@ math.Line = new Y.Class('Line', math.Vec, {
         ,   xi = this.xint   = -y1/m + x1
         ;
         math.Vec.init.call(this, xdelta, ydelta);
-        this.setT(t);
+        
+        this.theta = Math.atan2(ydelta, xdelta);
+        this._cos  = Math.cos(this.theta);
+        this._sin  = Math.sin(this.theta);
+        this.setTScale(tdist);
+    },
+    
+    clone : function clone(){
+        return new math.Line(this.x1,this.y1, this.x2,this.y2, this.tdist);
     },
     
-    setT : function setT(t){
-        this.t  = 1/(t || 1);
-        this.pa = this.t*this.x;
-        this.pb = this.t*this.y;
+    setTScale : function setTScale(tdist){
+        if (tdist) {
+            this.tdist = tdist;
+            this.pa = tdist * this._cos;
+            this.pb = tdist * this._sin;
+        } else {
+            this.tdist = this.y / this._sin;
+            this.pa = this.x;
+            this.pb = this.y;
+        }
         return this;
     },
     
     pcalc : function parametric(t){
-        return new math.Vec( this.x1 + this.pa*t ,
-                             this.y1 + this.pb*t );
+        return new math.Vec( this.x1 + t*this.pa ,
+                             this.y1 + t*this.pb );
+    },
+    
+    pointX : function pointX(x){
+        return new math.Vec(x, this.calcY(x));
+    },
+    
+    pointY : function pointY(y){
+        return new math.Vec(this.calcX(y), y);
     },
     
     calcY : function calcY(x){
@@ -37,11 +67,26 @@ math.Line = new Y.Class('Line', math.Vec, {
     },
     
     base : function base(){
-        return new math.Line(0,0, this.x,this.y);
+        if (!this._base)
+            this._base = new math.Line(0,0, this.x,this.y);
+        return this._base;
+    },
+    
+    tangent : function tangent(at){
+        var _theta = Math.PI/2 + this.theta
+        ,   x  = (at.x !== this.x1 ? this.x1 : this.x2) - at.x
+        ,   y  = (at.y !== this.y1 ? this.y1 : this.y2) - at.y
+        ,   _x = at.x + y
+        ,   _y = at.y - x
+        return new math.Line(at.x,at.y, _x,_y, this.tdist);
     },
     
     toString : function toString(){
-        return 'Line('+this.x1+','+this.y1+', '+this.x2+','+this.y2+', slope='+this.slope+')';
+        return 'Line('+this.x1.toFixed(2)+','+this.y1.toFixed(2)+', '+
+                       this.x2.toFixed(2)+','+this.y2.toFixed(2)+', '+
+                       'slope='+this.slope.toFixed(3)+')';
     }
     
-});
\ No newline at end of file
+});
+
+})();
index ce387ee..995f1c1 100644 (file)
@@ -62,7 +62,7 @@ math.Vec = new Y.Class('Vec', {
     },
     
     toString : function toString(){
-        return 'Vec('+this.x+','+this.y+')';
+        return 'Vec('+this.x.toFixed(3)+','+this.y.toFixed(3)+')';
     }
     
 });
index 1eb20bf..1f1b109 100644 (file)
@@ -1,35 +1,6 @@
-CONTEXT_ATTRS = Y([
-    'globalAlpha', 'globalCompositeOperation', 'strokeStyle', 'fillStyle',
-    'lineWidth', 'lineCap', 'lineJoin', 'miterLimit', 'shadowOffsetX',
-    'shadowOffsetY', 'shadowBlur', 'shadowColor'
-]);
-
-
 Shape = new Y.Class('Shape', Layer, {
     _cssClasses : 'portal layer shape',
     
-    attr : function attr(key, value, def){
-        if (!key) return this;
-        
-        if ( Y.isPlainObject(key) ) {
-            for (var k in key)
-                this.attr(k, key[k]);
-            return this;
-            
-        } else if ( CONTEXT_ATTRS.has(key) ) {
-            var r = Y.attr(this.ctx, key, value, def);
-            
-            if (r === this.ctx) {
-                // This implies we set a property
-                this.dirty = true;
-                return this;
-            } else
-                return r;
-            
-        } else
-            return Y.attr(this, key, value, def);
-    },
-    
     _calcDimension : function _calcDimension(which, values){
         values.unshift(0);
         var self = this
@@ -50,12 +21,12 @@ Shape = new Y.Class('Shape', Layer, {
 Rect = new Y.Class('Rect', Shape, {
     _cssClasses : 'portal layer shape rect',
     
-    init : function init(w, h){
+    init : function initRect(w, h){
         Layer.init.call(this);
         
         this.width(w)
-            .height(h)
-            .origin(w/2, h/2);
+            .height(h);
+            // .origin(w/2, h/2);
     },
     
     drawShape : function drawShape(ctx){
@@ -68,18 +39,18 @@ Rect = new Y.Class('Rect', Shape, {
 Circle = new Y.Class('Circle', Shape, {
     _cssClasses : 'portal layer shape circle',
     
-    init : function init(radius){
+    init : function initCircle(radius){
         Layer.init.call(this);
         
         var d = radius * 2;
-        this.radius = radius;
-        this.width(d).height(d)
-            .origin(radius,radius);
+        this.radius = this.negBleed.x = this.negBleed.y = radius;
+        this.width(d).height(d);
+            // .origin(radius,radius);
     },
     
     drawShape : function drawShape(ctx){
         var r  = this.radius;
-        ctx.arc(r,r, r, 0, Math.PI*2, false);
+        ctx.arc(0,0, r, 0, Math.PI*2, false);
         ctx.fill();
     }
     
@@ -93,7 +64,7 @@ Polygon = new Y.Class('Polygon', Shape, {
      * together to make the numbered coordinates.
      * x0 and y0 will always be 0.
      */
-    init : function init(xs, ys){
+    init : function initPolygon(xs, ys){
         Layer.init.call(this);
         
         var xs = this._calcDimension('x', xs)
@@ -104,8 +75,8 @@ Polygon = new Y.Class('Polygon', Shape, {
         
         this.points = Y(xs).zip(ys).map(Loc.instantiate, Loc);
         this.width(w)
-            .height(h)
-            .origin(w/2, h/2);
+            .height(h);
+            // .origin(w/2, h/2);
     },
     
     drawShape : function drawShape(ctx){
@@ -117,12 +88,90 @@ Polygon = new Y.Class('Polygon', Shape, {
 });
 
 // Er, this won't do. It's only a line-segment.
-Line = new Y.Class('Line', Polygon, {
+Line = new Y.Class('Line', Shape, {
+    _cssClasses : 'portal layer shape line',
+    
+    init : function initLine(x,y){
+        Layer.init.call(this);
+        
+        this.sublayer = jQuery('<div style="overflow:hidden" />')
+            .append(this.canvas)
+            .appendTo(this.layer);
+        
+        this.x2 = x; this.y2 = y;
+        this.position(0,0);
+    },
+    
+    position : function position(left, top){
+        if (top === undefined && left === undefined)
+            return this.line.p1;
+        
+        if (top && Y.isPlainObject(top))
+            var pos = top;
+        else
+            var pos = { 'top': top, 'left':left };
+        
+        this.x1 = pos.left; this.x2 += this.x1;
+        this.y1 = pos.top;  this.y2 += this.y1;
+        this.line = new math.Line(this.x1,this.y1, this.x2,this.y2);
+        
+        return this;
+    },
     
-    init : function init(x,y){
-        Polygon.init.call(this, [x], [y]);
+    appendTo : function appendTo(parent){
+        var r = Layer.prototype.appendTo.call(this, parent);
+        this.fixSize();
+        return r;
+    },
+    
+    fixSize : function fixSize(){
+        var p = this.parent
+        ,   w = this.canvasWidth, h = this.canvasHeight
+        ,   pw = p.canvasWidth,   ph = p.canvasHeight ;
+        
+        if (w !== pw) {
+            this.width(pw);
+            this.sublayer.width(pw);
+        }
+        if (h !== ph) {
+            this.height(ph);
+            this.sublayer.height(ph);
+        }
+        return this;
+    },
+    
+    drawShape : function drawShape(ctx){
+        this.fixSize();
+        var x1,y1, x2,y2
+        ,   line = this.line, p1 = line.p1, p2 = line.p2
+        ,   w = this.canvasWidth, h = this.canvasHeight;
+        
+        x1 = 0; y1 = line.calcY(x1);
+        if (y1 < 0) {
+            y1 = 0; x1 = line.calcX(y1);
+        }
+        
+        x2 = w; y2 = line.calcY(x2);
+        if (y2 > h) {
+            y2 = h; x2 = line.calcX(y2);
+        }
+        
+        ctx.moveTo(x1,y1);
+        ctx.lineTo(x2,y2);
+        ctx.stroke();
+        ctx.closePath();
+        
+        // Show definition points
+        this.point(p1.x,p1.y, '#4596FF');
+        this.point(p2.x,p2.y, '#4596FF');
+    },
+    
+    
+    toString : function toString(){
+        return this.className+'['+this.x1+','+this.y1+', '+this.x2+','+this.y2+']';
     }
     
+    
 });
 Line.fromPoints = function fromPoints(x1,y1, x2,y2){
     return new Line(x2-x1, y2-y1).position(x1,y1);
@@ -132,7 +181,7 @@ Line.fromPoints = function fromPoints(x1,y1, x2,y2){
 Triangle = new Y.Class('Triangle', Polygon, {
     _cssClasses : 'portal layer shape polygon triangle',
     
-    init : function init(x1,y1, x2,y2){
+    init : function initTriangle(x1,y1, x2,y2){
         Polygon.init.call(this, [x1,x2], [y1,y2]);
     },
     
@@ -154,7 +203,7 @@ Triangle = new Y.Class('Triangle', Polygon, {
 Quad = new Y.Class('Quad', Polygon, {
     _cssClasses : 'portal layer shape polygon quad',
     
-    init : function init(x1,y1, x2,y2, x3,y3){
+    init : function initQuad(x1,y1, x2,y2, x3,y3){
         Polygon.init.call(this, [x1,x2,x3], [y1,y2,y3]);
     }
     
index 815ab89..466e822 100644 (file)
@@ -81,8 +81,8 @@ methods = {
         this.timer = setTimeout(this.tick, tickInt);
     },
     
-    tick : function tick(lastTick){
-        lastTick     = lastTick || this.now;
+    tick : function tick(){
+        var lastTick = this.now;
         this.now     = new Date().getTime();
         this.elapsed = this.now - lastTick;
         
index 947c7c0..5dc506a 100644 (file)
@@ -34,11 +34,11 @@ FpsSparkline = new Y.Class('FpsSparkline', {
             this.maxBuffer = Math.floor(w / 3);
     },
     
-    tickTime : function tickTime(lastTick){
+    tickTime : function tickTime(){
         var loop = this.loop
         ,   buf  = this.buffer
         ;
-        this.loopTick.call(loop, lastTick);
+        this.loopTick.call(loop);
         
         this.ticks++;
         this.tickSum += loop.tFrame;
index a154714..3abf13c 100644 (file)
@@ -55,14 +55,16 @@ Y(Game.prototype).extend({
     addUnit : function addUnit(unit, col,row){
         unit.game = this;
         
-        // Center unit in square
-        var sqX = (col || 0) * REF_SIZE
-        ,   sqY = (row || 0) * REF_SIZE
-        ,   x = sqX + (REF_SIZE-unit.width)/2
-        ,   y = sqY + (REF_SIZE-unit.height)/2 ;
-        
-        unit.setLocation(x,y);
-        unit.render( this.level );
+        if (col !== undefined) {
+            // Center unit in square
+            var sqX = (col || 0) * REF_SIZE
+            ,   sqY = (row || 0) * REF_SIZE
+            ,   x = sqX + (REF_SIZE-unit.width) /2
+            ,   y = sqY + (REF_SIZE-unit.height)/2 ;
+            
+            unit.setLocation(x,y);
+            unit.render( this.level );
+        }
         
         this.addBlocker(unit);
         
index e4295b6..aecc829 100644 (file)
@@ -83,7 +83,7 @@ Player = new Y.Class('Player', {
         
         var r = TWO_PI * (TICKS % 30)/30;
         // console.log('tank.shape.rotate(', r, ')');
-        t.shape.rotate(r);
+        // t.shape.rotate(r);
     },
     
     act_ : function act_(){
index 8578913..6cf21de 100644 (file)
@@ -4,30 +4,18 @@ v = $('#viewport');
 LBT = new Game();
 
 T = new Tank(0);
-LBT.addUnit(T, 1,1);
+LBT.addUnit(T, 1,2);
+
+B = new Bullet(T, 3*REF_SIZE,3*REF_SIZE);
+LBT.addUnit(B);
+B.render( LBT.level );
+
 P = new Player(LBT, T);
 
 
-// L = new Layer(v)
-//     .width(v.width())
-//     .height(v.height())
-//     .append(
-//         new Rect(250,250)
-//             .position(150,50)
-//             .attr({
-//                 fillStyle : '#E2EEF5'
-//             }),
-//         new Triangle(25,-25, 50,0)
-//             .position(150,300)
-//             .attr('fillStyle', '#E73075'),
-//         new Quad(10,50, 50,50, 60,0)
-//             .position(50,50)
-//             .attr('fillStyle', '#E73075'),
-//         new Circle(30)
-//             .position(30,330)
-//             .attr('fillStyle', '#8DCDA1')
-//     ).appendTo(v)
-//     .draw()
-// ;
+ctx = LBT.level.ctx;
+tr = B.trajectory;
+
+
 
 });
\ No newline at end of file
index b5bb707..4855582 100644 (file)
@@ -1,25 +1,23 @@
 Bullet = new Y.Class('Bullet', Thing, {
     
-    /*
-       t = time from creation
-       current_t = now - creation_time
-     */
     /**
      * @param {tanks.Unit} owner
      * @param {math.Line} trajectory
      */
-    init : function initBullet(owner, trajectory){
-        Thing.init.call(this, owner.game, owner.align);
+    init : function initBullet(owner, x,y){
+        this.owner = owner;
         
-        this.owner        = owner;
-        this.trajectory   = trajectory;
-        this.creationTime = NOW;
+        var loc = owner.loc
+        ,   trj = this.trajectory = new math.Line(loc.x,loc.y, x,y);
+        Thing.init.call(this, owner.game, owner.align);
         
-        this.setLocation(trajectory.x1, trajectory.y1);
-        // this.trajectory.setT(this.stats.move*SQUARETH);
+        this.setLocation(trj.x1, trj.y1);
+        trj.setTScale(this.stats.move * REF_SIZE/1000);
     },
     blocking : false,
-    creationTime : 0,
+    elapsed : 0,
+    width  : 5,
+    height : 5,
     
     stats : {
         move      : 2.0, // move speed (squares/sec)
@@ -29,33 +27,93 @@ Bullet = new Y.Class('Bullet', Thing, {
     fillStats : function(){
         this.stats = Y({},
             Thing.fillStats(this.owner.stats),
-            Thing.fillStats(this.stats) ).end();
+            Thing.fillStats(this.stats) );
     },
     
     createCooldowns : Y.op.nop,
     
     act : function act(){
+        this.elapsed += ELAPSED;
         if (!this.dead)
             this.move();
         return this;
     },
     
     move : function move(){
-        var to = this.trajectory.pcalc( (NOW-this.creationTime) * REF_SIZE/1000 );
+        var p, wall, p2 = this.trajectory.p2
+        ,   level = this.game.grid, walls = level.walls
+        ,   w = level.layerWidth, h = level.layerHeight
+        ,   trj = this.trajectory
+        ,   to  = this.trajectory.pcalc(this.elapsed);
+        
+        // Do we need to reflect because we've hit a wall?
+        if (to.x <= 0 || to.x >= w || to.y <= 0 || to.y >= h) {
+            if (to.x <= 0) {
+                wall = walls.left;
+                to = trj.pointX(0);
+            
+            } else if (to.x >= w) {
+                wall = walls.right;
+                to = trj.pointX(w);
+            
+            } else if (to.y <= 0) {
+                wall = walls.top;
+                to = trj.pointY(0);
+            
+            } else if (to.y >= h) {
+                wall = walls.bottom;
+                to = trj.pointY(h);
+            }
+            
+            p = math.reflect(p2, wall.tangent(to));
+            
+            // Are we pointed the wrong direction? Rotate 180 degrees around the bullet
+            if (   (p.x <= 0 && to.x <= 0) || (p.y <= 0 && to.y <= 0)
+                || (p.x >= w && to.x >= w) || (p.y >= h && to.y >= h)
+            ){
+                var x = to.x - (p.x - to.x)
+                ,   y = to.y - (p.y - to.y);
+                p = new math.Vec(x,y);
+            }
+            
+            this.trajectory = new math.Line(
+                    math.clamp(to.x, 0.1, w-0.1), math.clamp(to.y, 0.1, h-0.1),
+                    p.x,p.y,
+                    this.stats.move * REF_SIZE/1000 );
+            
+            // console.log('p2='+p2+' reflected in '+wall.tangent(to)+' --> '+this.trajectory);
+            
+            this.render(this.game.level);
+            this.elapsed = 0;
+            this.dirty = true;
+            return this.move();
+        }
+        
         this.game.moveAgentTo(this, to.x, to.y);
         return this;
+    },
+    
+    render : function render(parent){
+        if (this.tline) this.tline.remove();
+        if (this.shape) this.shape.remove();
+        
+        var t = this.trajectory;
+        this.tline = Line.fromPoints(t.x1,t.y1, t.x2,t.y2)
+            .attr('strokeStyle', 'rgba(255,246,174, 0.5)')
+            .appendTo( parent );
+        
+        this.shape = new Circle(2.5)
+            .position(this.loc.x, this.loc.y)
+            .attr('fillStyle', '#FFF6AE')
+            .appendTo( parent );
+        
+        return this;
+    },
+    
+    toString : function toString(){
+        
+        return this.className+'(loc='+this.loc+', traj='+this.trajectory+', owner='+this.owner+')';
     }
     
 });
 
-Y(Bullet).extend({
-    
-    fireAt : function fireAt(ProjectileType, owner, target){
-        var oloc = owner.loc
-        ,   x1 = oloc.x,   y1 = oloc.y
-        ,   x2 = target.x, y2 = target.y
-        ;
-        return new ProjectileType(owner, new math.Line(x1,y1, x2,y2));
-    }
-    
-});
\ No newline at end of file
index ff2c931..670190f 100644 (file)
@@ -35,10 +35,11 @@ Tank = new Y.Class('Tank', Thing, {
         this.fireProjectile(target);
     },
     
-    fireProjectile : function fireProjectile(target){
-        var p = Bullet.fireAt(this.projectile, this, target);
+    fireProjectile : function fireProjectile(x,y){
+        var ProjectileType = this.projectile
+        ,   p = new ProjectileType(this, x,y);
         this.bullets.push(p);
-        this.game.addAgent(p);
+        this.game.addUnit(p);
         return p;
     },
     
index 5b67993..88c3ddb 100644 (file)
@@ -47,9 +47,12 @@ jQuery(function($){
     spark = new FpsSparkline(LBT.loop, '.fps-sparkline', 0,0);
     
     // Tick once to draw grid, initial units
-    LBT.start();
-    LBT.stop();
-    LBT.loop.elapsedAtStop = 0;
+    // LBT.start();
+    // LBT.stop();
+    // LBT.loop.elapsedAtStop = 1;
+    
+    LBT.root.draw();
+    
     
     setInterval(updateInfo, 1000);
     updateInfo();
index 22cf5c1..c3b6862 100644 (file)
@@ -9,6 +9,15 @@ Grid = new Y.Class('Grid', Rect, {
         this.rows = rows;
         this.size = size;
         Rect.init.call(this, cols*size, rows*size);
+        
+        var w = this.canvasWidth
+        ,   h = this.canvasHeight;
+        this.walls = {
+            top    : new math.Line(0,0, w,0),
+            bottom : new math.Line(0,h, w,h),
+            left   : new math.Line(0,0, 0,h),
+            right  : new math.Line(w,0, w,h)
+        };
     },
     
     drawShape : function drawShape(ctx){
index db23df8..0fbf015 100644 (file)
@@ -21,7 +21,6 @@ drawLine(-w,-h2, w,-h2, '#CCCCCC', 2.0);
 drawLine(w2,-h,  w2,h, '#CCCCCC', 2.0);
 
 
-
 P = new Layer()
     .width(w).height(h)
     .appendTo(grid);
@@ -31,9 +30,14 @@ ctx.translate(w2, h2);
 ctx.scale(PPU, PPU);
 
 points = Y([]);
-line = mkLine(5,8, 4,7);
+line = mkLine(0,0, 2.3125,1);
 addPoint(-5,2);
 
+drawAngle(line.theta);
+
+tanLine = line.tangent(line.p2);
+drawLine(-W, tanLine.calcY(-W), W, tanLine.calcY(W), 'rgba(226,127,8, 0.5)');
+
 dragging = false;
 P.layer.bind('click', function(evt){
     if (!dragging) {
@@ -72,7 +76,7 @@ function redraw(color){
     color = color || 'rgba(231,48,117, 0.5)';
     var pts = points.clone();
     clear();
-    drawLine(-W/2, line.calcY(-W2), line.calcX(H2), H2, color);
+    drawLine(-W, line.calcY(-W), W, line.calcY(W), color);
     P.append(p1, p2);
     pts.forEach(addPoint);
 }
@@ -104,8 +108,8 @@ function mkLine(x1,y1, x2,y2, color, pcolor){
     
     color  = color  || 'rgba(231,48,117, 0.5)';
     pcolor = pcolor || 'rgba(69,150,255, 1)';
-    var line = new math.Line(x1,y1, x2,y2, 10);
-    drawLine(-W/2, line.calcY(-W2), line.calcX(H2), H2, color);
+    var line = new math.Line(x1,y1, x2,y2);
+    drawLine(-W, line.calcY(-W), W, line.calcY(W), color);
     
     p1 = drawPoint(line.x1, line.y1, pcolor);
     p1.layer.bind('mousedown', function(){
@@ -135,9 +139,6 @@ function mkLine(x1,y1, x2,y2, color, pcolor){
 }
 
 function drawLine(x1,y1, x2,y2, color, width){
-    if ([x1,y1, x2,y2].some(isNaN)) {
-        throw new Error('Value is NaN!'+'('+x1+','+y1+') ('+x2+','+y2+')');
-    }
     try {
         ctx.beginPath();
         ctx.lineWidth = width || PX;
@@ -170,3 +171,15 @@ function drawPoint(x,y, color, r){
     return c;
 }
 
+function drawAngle(theta, radius){
+    radius = radius || 3;
+    ctx.beginPath();
+    ctx.fillStyle = 'rgba(36,71,146, 0.25)';
+    ctx.moveTo(0,0);
+    ctx.lineTo(radius,0);
+    // ctx.arc(0,0, radius, 0, -(Math.PI/2-theta), true);
+    ctx.arc(0,0, radius, 0, -theta, true);
+    ctx.lineTo(0,0);
+    ctx.fill();
+    ctx.closePath();
+}