.gitignore
authordsc <david.schoonover@gmail.com>
Fri, 24 Dec 2010 16:30:39 +0000 (08:30 -0800)
committerdsc <david.schoonover@gmail.com>
Fri, 24 Dec 2010 16:30:39 +0000 (08:30 -0800)
Moves asset files.

20 files changed:
.gitignore
assets/favicon-green.psd [moved from www/img/favicon-green.psd with 100% similarity]
assets/favicon-pink.psd [moved from www/img/favicon-pink.psd with 100% similarity]
assets/favicon-pink2.psd [moved from www/img/favicon-pink2.psd with 100% similarity]
src/tanks/map/index.cjs
src/tanks/map/level.cjs
src/tanks/map/map.cjs
src/tanks/map/pathmap.cjs
src/tanks/map/trajectory.cjs
src/tanks/map/traversal.cjs
src/tanks/map/wall.cjs [deleted file]
src/tanks/thing/bullet.cjs
src/tanks/thing/index.cjs
src/tanks/thing/item.cjs
src/tanks/thing/player.cjs
src/tanks/thing/tank.cjs
src/tanks/thing/thing.cjs
www/deps.html
www/img/tank-icon.png [deleted file]
www/img/viewport.png [deleted file]

index ceaa50f..0afc988 100644 (file)
@@ -4,3 +4,4 @@ build/
 *.md.html
 *.pyc
 www/versioned-deps.html
+google74dcf785afbc60e0.html
index a041760472f7aec38f322c98363fc75a3bbc0988..d143552b0028bbbc5e3f1e8f7e666115f06de7ec 100644 (file)
@@ -3,7 +3,6 @@ var Y = require('Y').Y;
 Y.extend(exports, {
     'Map'        : require('tanks/map/map').Map,
     'PathMap'    : require('tanks/map/pathmap').PathMap,
-    'Wall'       : require('tanks/map/wall').Wall,
     'Level'      : require('tanks/map/level').Level,
     'Traversal'  : require('tanks/map/traversal').Traversal,
     'Trajectory' : require('tanks/map/trajectory').Trajectory
index 3e7025a2454470d53fdebc8440bf0e12199d1561..6736b4f6d95909fcf85373db5fd712ee1f252977 100644 (file)
@@ -5,7 +5,7 @@ var Y          = require('Y').Y
 ,   Item       = require('tanks/thing/item').Item
 ,   PlayerTank = require('tanks/thing/player').PlayerTank
 ,   PathMap    = require('tanks/map/pathmap').PathMap
-,   Wall       = require('tanks/map/wall').Wall
+,   Wall       = require('tanks/thing/wall').Wall
 ,
 
 
@@ -42,11 +42,11 @@ Rect.subclass('Level', {
         // game.addThing(new Tank(1).colors('#4596FF', '#182B53', '#F25522'), 3,9);
         
         E = 
-        game.addThing(new Tank(2), 0,5);
-        game.addThing(new Tank(2), 1,0);
-        game.addThing(new Tank(2), 8,1);
+        game.addThing(new Tank(2), 0,7);
+        // game.addThing(new Tank(2), 1,0);
+        // game.addThing(new Tank(2), 8,1);
         
-        I = game.addThing(new Item(), 8,8);
+        // I = game.addThing(new Item(), 8,8);
     },
     
     addWall : function addWall(x,y, w,h, isBoundary){
index 939494b8365cc51b078b875c0817503b50dbb7e9..5860a7a279c166a8cb3894da33f412011ac4f6e5 100644 (file)
@@ -1,16 +1,19 @@
 //  -*- mode: JavaScript; tab-width: 4; indent-tabs-mode: nil; -*-
 var Y        = require('Y').Y
-
-,   math     = require('ezl/math')
-,   vec      = require('ezl/math/vec')
 ,   QuadTree = require('ezl/util/tree/quadtree').QuadTree
-,   astar    = require('ezl/util/astar')
 
-,   config   = require('tanks/config').config
-,   Bullet   = require('tanks/thing/bullet').Bullet
 
-,   Vec      = vec.Vec
-,   Line     = math.Line
+/** Does not obstruct other objects. */
+,   PASSABLE = exports['PASSABLE'] = 0
+
+/** Does not obstruct other objects, but still collides with them. */
+,   ZONE = exports['ZONE'] = 1
+
+/** Obstructs other blockers with its BoundingBox. */
+,   BLOCKING = exports['BLOCKING'] = 2
+
+/** Potentially obstructs other objects, but requires a special test once a BoundingBox collision has been detected. */
+,   IRREGULAR = exports['IRREGULAR'] = 3
 ,
 
 
@@ -32,68 +35,7 @@ QuadTree.subclass('Map', {
             delete obj.region;
         }
         return obj;
-    },
-    
-    // moveBlocked : function moveBlocked(agent, trj, to, bb){
-    //     bb = bb || agent.boundingBox;
-    //     var blockers, blocker, msg
-    //     ,   side = null
-    //     ,   tobb = bb.relocated(to.x,to.y)
-    //     ,   x1 = tobb.x1, y1 = tobb.y1
-    //     ,   x2 = tobb.x2, y2 = tobb.y2
-    //     
-    //     ,   ro = bb.relOrigin
-    //     ,   bw = bb.width,          bh = bb.height
-    //     ,   offX = bw - ro.x,       offY = bh = ro.y
-    //     
-    //     // ,   x1 = to.x+offX,         y1 = to.y+offY
-    //     // ,   x2 = x1+bw,             y2 = y1+bh
-    //     ;
-    //     
-    //     blockers = this.get(x1,y1, x2,y2).remove(agent).end();
-    //     blocker = blockers[0];
-    //     
-    //     // Not blocked
-    //     if (!blocker) return false;
-    //     
-    //     // Literal corner case :P
-    //     // XXX: needs better detection of corner
-    //     if (blockers.length > 1) {
-    //         msg = 'corner of '+blockers.slice(0);
-    //         to  = trj.p1;
-    //         
-    //     // Normal reflection against one line
-    //     } else {
-    //         msg = blocker+' on the ';
-    //         
-    //         var B = blocker.boundingBox;
-    //         if (bb.x2 <= B.x1 && x2 >= B.x1) {
-    //             msg += 'left';
-    //             side = B.leftSide;
-    //             to   = trj.pointAtX(B.x1-offX-1);
-    //             
-    //         } else if (bb.x1 >= B.x2 && x1 <= B.x2) {
-    //             msg += 'right';
-    //             side = B.rightSide;
-    //             to   = trj.pointAtX(B.x2+ro.x+1);
-    //             
-    //         } else if (bb.y2 <= B.y1 && y2 >= B.y1) {
-    //             msg += 'top';
-    //             side = B.topSide;
-    //             to   = trj.pointAtY(B.y1-offY-1);
-    //             
-    //         } else if (bb.y1 >= B.y2 && y1 <= B.y2) {
-    //             msg += 'bottom';
-    //             side = B.bottomSide;
-    //             to   = trj.pointAtY(B.y2+ro.y+1);
-    //         }
-    //         
-    //         msg += ' side';
-    //     }
-    //     
-    //     return { 'msg':msg, 'blockers':blockers, 'side':side, 'to':to };
-    // },
-    
+    }
     
     
 });
index 2e439f9da9b48e6cf4d8fecfa988ebca6eaf4ee1..1d515eef8ca2c337b2f1661fc927686b6341b20c 100644 (file)
@@ -5,157 +5,17 @@ var Y          = require('Y').Y
 ,   vec        = require('ezl/math/vec')
 ,   QuadTree   = require('ezl/util/tree/quadtree').QuadTree
 ,   BinaryHeap = require('ezl/util/binaryheap').BinaryHeap
-
-,   config     = require('tanks/config').config
-,   Bullet     = require('tanks/thing/bullet').Bullet
-,   Map        = require('tanks/map/map').Map
 ,   Vec        = vec.Vec
 ,   Line       = math.Line
 
+,   config     = require('tanks/config').config
+,   map        = require('tanks/map/map')
+,   Map        = map.Map
 
-,   SQRT_TWO = Math.sqrt(2)
 
-,   PASSABLE = 1 // Does not obstruct other objects
-,   BLOCKING = 2 // Obstructs other blockers
-,   ZONE     = 3 // Does not obstruct other objects, but still collides with them
+,   SQRT_TWO = Math.sqrt(2)
 ,
 
-
-// Would be nice if I could make this an inner class (for once)
-Square =
-exports['Square'] =
-Y.subclass('Square', new Vec(0,0), {
-    pathId : null, // instance of A* being run, to track when to reset
-    
-    // resetable:
-    dist      : 0, // estimated distance
-    startDist : 0, // distance from start
-    endDist   : 0, // estimated distance to end
-    visited   : false,
-    closed    : false,
-    prev      : null,
-    
-    
-    /**
-     * @param x Left coord of square.
-     * @param y Top coord of square.
-     */
-    init : function initSquare(pathmap, x,y){
-        Vec.init.call(this, x,y);
-        this.pathmap = pathmap;
-        this.reset();
-    },
-    
-    reset : function reset(){
-        var pathId = this.pathmap._pathId;
-        if (this.pathId === pathId)
-            return this;
-        
-        this.pathId = pathId;
-        this.blocked = this._blocked();
-        
-        this.dist      = 0;
-        this.startDist = 0;
-        this.endDist   = 0;
-        this.visited   = false;
-        this.closed    = false;
-        this.prev      = null;
-        
-        return this;
-    },
-    
-    /**
-     * @private Calculates this.blocked value. Should only be called by reset() to cache the result.
-     */
-    _blocked : function blocked(){
-        var pm    = this.pathmap
-        ,   agent = pm._agent
-        // ,   bb    = agent.boundingBox
-        
-        // ,   origin = bb.relOrigin
-        // ,   left = origin.x,  right  = bb.width - left
-        // ,   top  = origin.y,  bottom = bb.height - top
-        
-        ,   SIZE = pm.gridSquare
-        ,   x  = Math.floor(this.x)
-        ,   y  = Math.floor(this.y)
-        // ,   x1 = x - left,  x2 = x + SIZE + right
-        // ,   y1 = y - top,   y2 = y + SIZE + bottom
-        ,   x1 = x,  x2 = x + SIZE
-        ,   y1 = y,  y2 = y + SIZE
-        
-        ,   blockers = pm.get(x1,y1, x2,y2)
-                .remove(agent)
-                .filter(this._filterBlocked)
-        ;
-        
-        return !!blockers.length;
-    },
-    
-    /**
-     * @private
-     */
-    _filterBlocked : function filterBlocked(v, r){
-        return !(v.isBoundary || v instanceof Bullet); // XXX: use PASSABLE
-    },
-    
-    getNeighbors : function getNeighbors(){
-        var neighbors = []
-        ,   abs   = Math.abs
-        ,   pm    = this.pathmap
-        ,   agent = pm._agent
-        ,   SIZE  = pm.gridSquare
-        
-        ,   x = this.x,  y = this.y
-        ,   sq, cost, ix, iy, x1,y1, x2,y2
-        ;
-        
-        for (ix=-1; ix<2; ix++) {
-            for (iy=-1; iy<2; iy++) {
-                // Skip self
-                if (ix === 0 && iy === 0)
-                    continue;
-                
-                cost = 1;
-                x1 = x + ix*SIZE;  y1 = y + iy*SIZE;
-                x2 = x1+SIZE;      y2 = y1+SIZE;
-                
-                // filter squares outside bounds
-                if (    pm.x1 >= x1 || pm.y1 >= y1 ||
-                        pm.x2 <= x2 || pm.y2 <= y2 )
-                    continue;
-                
-                // Diagonal square
-                if ( abs(ix) === 1 && abs(iy) === 1 ) {
-                    cost = SQRT_TWO;
-                    
-                    // Ensure we don't cut a blocked corner
-                    if ( pm._getSquare(x1,y).blocked || pm._getSquare(x,y1).blocked )
-                        continue;
-                }
-                
-                sq = pm._getSquare(x1,y1);
-                neighbors.push([ sq, cost*SIZE ]);
-            }
-        }
-        
-        return neighbors;
-    },
-    
-    toString : function toString(){
-        var props = [];
-        if (this.blocked) props.push('blocked');
-        if (this.visited) props.push('dist='+this.dist);
-        if (this.closed) props.push('closed');
-        props = props.join(', ');
-        if (props) props = '('+props+')';
-        return '['+this.x+' '+this.y+']'+props;
-    }
-    
-})
-
-
-
 /**
  * A QuadTree which aids in pathing for AI.
  *  - QuadTree methods which took rect coords (x1,y1, x2,y2) will accept
@@ -338,7 +198,143 @@ Map.subclass('PathMap', {
         return new Vec(floor(x)*size, floor(y)*size);
     }
     
-});
+})
+,
+
+
+// Would be nice if I could make this an inner class (for once)
+Square =
+exports['Square'] =
+Y.subclass('Square', new Vec(0,0), {
+    pathId : null, // instance of A* being run, to track when to reset
+    
+    // resetable:
+    dist      : 0, // estimated distance
+    startDist : 0, // distance from start
+    endDist   : 0, // estimated distance to end
+    visited   : false,
+    closed    : false,
+    prev      : null,
+    
+    
+    /**
+     * @param x Left coord of square.
+     * @param y Top coord of square.
+     */
+    init : function initSquare(pathmap, x,y){
+        Vec.init.call(this, x,y);
+        this.pathmap = pathmap;
+        this.reset();
+    },
+    
+    reset : function reset(){
+        var pathId = this.pathmap._pathId;
+        if (this.pathId === pathId)
+            return this;
+        
+        this.pathId = pathId;
+        this.blocked = this._blocked();
+        
+        this.dist      = 0;
+        this.startDist = 0;
+        this.endDist   = 0;
+        this.visited   = false;
+        this.closed    = false;
+        this.prev      = null;
+        
+        return this;
+    },
+    
+    /**
+     * @private Calculates this.blocked value. Should only be called by reset() to cache the result.
+     */
+    _blocked : function blocked(){
+        var pm    = this.pathmap
+        ,   agent = pm._agent
+        // ,   bb    = agent.boundingBox
+        
+        // ,   origin = bb.relOrigin
+        // ,   left = origin.x,  right  = bb.width - left
+        // ,   top  = origin.y,  bottom = bb.height - top
+        
+        ,   SIZE = pm.gridSquare
+        ,   x  = Math.floor(this.x)
+        ,   y  = Math.floor(this.y)
+        // ,   x1 = x - left,  x2 = x + SIZE + right
+        // ,   y1 = y - top,   y2 = y + SIZE + bottom
+        ,   x1 = x,  x2 = x + SIZE
+        ,   y1 = y,  y2 = y + SIZE
+        
+        ,   blockers = pm.get(x1,y1, x2,y2)
+                .remove(agent)
+                .filter(this._filterBlocked, this)
+        ;
+        
+        return !!blockers.length;
+    },
+    
+    /**
+     * @private
+     */
+    _filterBlocked : function filterBlocked(v, r){
+        return v.blocking === map.BLOCKING && !v.isBoundary;
+    },
+    
+    getNeighbors : function getNeighbors(){
+        var neighbors = []
+        ,   abs   = Math.abs
+        ,   pm    = this.pathmap
+        ,   agent = pm._agent
+        ,   SIZE  = pm.gridSquare
+        
+        ,   x = this.x,  y = this.y
+        ,   sq, cost, ix, iy, x1,y1, x2,y2
+        ;
+        
+        for (ix=-1; ix<2; ix++) {
+            for (iy=-1; iy<2; iy++) {
+                // Skip self
+                if (ix === 0 && iy === 0)
+                    continue;
+                
+                cost = 1;
+                x1 = x + ix*SIZE;  y1 = y + iy*SIZE;
+                x2 = x1+SIZE;      y2 = y1+SIZE;
+                
+                // filter squares outside bounds
+                if (    pm.x1 >= x1 || pm.y1 >= y1 ||
+                        pm.x2 <= x2 || pm.y2 <= y2 )
+                    continue;
+                
+                // Diagonal square
+                if ( abs(ix) === 1 && abs(iy) === 1 ) {
+                    cost = SQRT_TWO;
+                    
+                    // Ensure we don't cut a blocked corner
+                    if ( pm._getSquare(x1,y).blocked || pm._getSquare(x,y1).blocked )
+                        continue;
+                }
+                
+                sq = pm._getSquare(x1,y1);
+                neighbors.push([ sq, cost*SIZE ]);
+            }
+        }
+        
+        return neighbors;
+    },
+    
+    toString : function toString(){
+        var props = [];
+        if (this.blocked) props.push('blocked');
+        if (this.visited) props.push('dist='+this.dist);
+        if (this.closed) props.push('closed');
+        props = props.join(', ');
+        if (props) props = '('+props+')';
+        return '['+this.x+' '+this.y+']'+props;
+    }
+    
+})
+;
 
 
 
index f11de2aacefd78503e90af36aa5eeb924bd8b332..72b4f9a14c7e6172aa1955c4c0a987349a3f88c3 100644 (file)
@@ -58,10 +58,10 @@ Line.subclass('Trajectory', {
     // Determine how much time can pass before we risk teleporting
     // We'll need to reset this whenever the bounding box changes size
     resetBound : function resetBound(){
-        var p = BOUND_SIZE_RATIO
-        ,   abs = Math.abs
-        ,   w = this.owner.width, h = this.owner.height;
-        this.tBound = Math.min(abs(w/this.pa) * p, abs(h/this.pb) * p);
+        this.tBound = 
+            BOUND_SIZE_RATIO * Math.min(
+                Math.abs(this.owner.width  / this.pa),
+                Math.abs(this.owner.height / this.pb)  );
         return this;
     },
     
@@ -115,7 +115,7 @@ Line.subclass('Trajectory', {
         return this.compare(o1,o2) === 1 ? o2 : o1;
     },
     
-    closest : function closest(o1, o2){
+    sortClosest : function sortClosest(o1, o2){
         var things;
         if (o1 instanceof Y.YArray)
             things = o1;
@@ -124,7 +124,11 @@ Line.subclass('Trajectory', {
         else
             things = new Y(arguments);
         
-        return things.sort( this.compare ).shift();
+        return things.sort( this.compare );
+    },
+    
+    closest : function closest(o1, o2){
+        return this.sortClosest.apply(this, arguments).shift();
     },
     
     comesWithin : function comesWithin(pt, w,h){
index c4494371450bbc53eeea6388ed4f7043cfc7b69c..40001c8456898684fac4367b1a0568d5d5d5d0fa 100644 (file)
 var Y = require('Y').Y
+,   map = require('tanks/map/map')
 ,
 
 Traversal =
 exports['Traversal'] =
 Y.subclass('Traversal', {
+    ignore    : null, // objects to ignore when determining blockers
+    
     isBlocked : false,
-    isCorner  : false,
-    remaining : 0,
-    blocker   : null,
-    to        : null,
-    side      : null,
+    to        : null, // furthest point reached
+    remaining : 0,    // time left unconsumed due to blocker
+    blocker   : null, // blocking object
+    side      : null, // collision side of blocker (Line)
     
     
     
-    init : function initTraversal(thing, trajectory){
-        this.thing      = thing;
-        this.game       = thing.game;
-        this.pathmap    = thing.game.pathmap;
-        this.bbox       = thing.boundingBox.clone();
+    init : function initTraversal(thing, trajectory, ignore){
+        this.ignore  = Y(ignore || []);
+        
+        this.thing   = thing;
+        this.game    = thing.game;
+        this.pathmap = thing.game.pathmap;
+        this.bbox    = thing.boundingBox.clone();
         
         this.trajectory = trajectory || thing.trajectory;
     },
     
-    step : function step(dt, tx,ty){
-        var t, traj = this.trajectory;
-        // console.group('tvsl.step(dt='+dt+', tCurrent='+traj.tCurrent+', target=('+tx+','+ty+'))');
+    traverse : function traverse(t, tx,ty){
+        var tr = this.trajectory;
+        this.remaining = t;
         
         do {
-            t = this.stepTo( Math.min(traj.tBound,dt), tx,ty);
-            traj.tCurrent += t;
-            dt = Math.max(0, dt-t);
-            
-            if (this.isBlocked) {
-                this.remaining = dt;
-                var data = { 'traversal':this };
-                this.thing.fire('collide', this.blocker, data);
-                this.blocker.fire('collide', this.thing, data);
-                break;
-            }
-            
-        } while (dt > 0);
+            this.step(tx,ty);
+        } while (!this.isBlocked && this.remaining > 0);
         
-        // console.groupEnd();
         return this.to;
     },
     
     /**
      * Checks for blockers and moves traversal bounds forward as much as possible.
-     * @param {Number} t Time units to move this traversal forward.
-     * @return {Number} Time units consumed by this step.
      */
-    stepTo : function stepTo(t, tx,ty){
-        var blockers, blocker, B
-        ,   abs = Math.abs
-        ,   traj = this.trajectory, thing = this.thing
-        
-        ,   bb = this.bbox
-        ,   x1 = bb.x1,  y1 = bb.y1
-        ,   x2 = bb.x2,  y2 = bb.y2
-        ,   o  = bb.absOrigin
-        
-        ,   to = traj.parametric(traj.tCurrent+t)
+    step : function step(tx,ty){
+        var tr = this.trajectory
+        ,   or = this.thing.loc
+        ,   dt = Math.min(tr.tBound, this.remaining)
+        ,   to = this.to = tr.parametric(tr.tCurrent+dt) // calc how far down the tr we go
         ;
         
         // Don't overshoot the target
-        if ( tx !== undefined && abs(o.x-to.x) > abs(o.x-tx) )
+        if ( tx !== undefined && Math.abs(or.x-to.x) > Math.abs(or.x-tx) )
             to.x = tx;
         
-        if ( ty !== undefined && abs(o.y-to.y) > abs(o.y-ty) )
+        if ( ty !== undefined && Math.abs(or.y-to.y) > Math.abs(or.y-ty) )
             to.y = ty;
         
-        // nb: BoundingBox.relocate() is in-place, so this.bbox are updated
-        bb = bb.relocate(to);
-        var oto = this.to = to;
-        blockers = this.blockers = this.pathmap.get(bb).remove(thing).end();
+        this.to = to;
+        this.bbox.relocate(to); // BoundingBox.relocate() is in-place
+        
+        this.blockers =
+            this.pathmap.get(this.bbox)
+                .remove(this.thing)
+                .sort(tr.compare);
         
-        if (!blockers.length) {
+        // this.blocker = this.blockers.filter(this.checkBlocker, this).shift();
+        this.blockers.filter(this.checkBlocker, this);
+        
+        if (this.blocker) {
+            
+            // Move bounding box to furthest non-overlapping point
+            this.to = this.rewind(this.blocker);
+            this.bbox.relocate(this.to);
+            
+            // Calculate how much time we actually used
+            var consumed = tr.timeToMove(to.x-or.x, to.y-or.y);
+            this.consumeTime(consumed);
+            // console.log('stepTo('+dt+') --> wanted '+oto+' but BLOCKED! by '+this.blocker+', so ending at '+to+', consuming '+consumed);
+            
+            // Fire events
+            this.collide(this.blocker);
+            
+        } else {
+            this.consumeTime(dt);
             // console.log('stepTo('+t+') --> '+to+' consuming '+t);
-            return t; // All time consumed, yay
+        }
+    },
+    
+    /**
+     * Filters found obstructions to keep only those not ignored and blocking.
+     */
+    checkBlocker : function checkBlocker(blocker){
+        var blocking = blocker.blocking;
+        
+        // All blockers after the first are ignored
+        if ( this.ignore.has(blocker) || blocking === map.PASSABLE || this.isBlocked )
+            return false;
+        
+        // Only fire collision with this zone once per traversal
+        // XXX: Zone will have to manage enterance/exit and provide a method for testing, hm -- this would obviate the main need for this.ignore
+        if ( blocking === map.ZONE ) {
+            this.ignore.push(blocker);
+            this.collide(blocker);
+            return false;
         }
         
         this.isBlocked = true;
+        this.blocker = blocker;
+        return true;
+    },
+    
+    collide : function collide(blocker){
+        var thing = this.thing
+        ,   data  = { 'traversal':this, 'thing':thing, 'blocker':blocker };
         
-        // TODO: Either: this never happens (current belief) OR it needs better corner-detection
-        // Literal corner case :P
-        if (blockers.length > 1) {
-            console.error('Corner!', 'to:', to, 'blockers:', blockers);
-            // throw new Error('Corner! to:'+to+', blockers:'+blockers);
-            this.isCorner = true;
-            // to = traj.p1; // Turn around, go back whence you came
-        }
-        
-        // blocker = traj.closest(blockers)
-        blocker = this.blocker = blockers[0];
-        B = blocker.boundingBox;
+        thing.fire('collide', blocker, data);
+        blocker.fire('collide', thing, data);
+    },
+    
+    rewind : function rewind(blocker){
+        var tr = this.trajectory, bb = this.bbox
+        ,   B = blocker.boundingBox
+        ,   to = this.to ;
         
         // Figure out which boundary of the blocker we crossed and calculate
         // the furthest non-overlapping point.
-        if (x2 <= B.x1 && bb.x2 >= B.x1) {
-            this.to = to = traj.pointAtX(B.x1 - 1 - bb.originRight);
+        if (bb.x2 <= B.x1 && bb.x2 >= B.x1) {
             this.side = B.leftSide;
+            to = tr.pointAtX(B.x1 - 1 - bb.originRight);
             
-        } else if (x1 >= B.x2 && bb.x1 <= B.x2) {
-            this.to = to = traj.pointAtX(B.x2 + 1 + bb.originLeft);
+        } else if (bb.x1 >= B.x2 && bb.x1 <= B.x2) {
             this.side = B.rightSide;
+            to = tr.pointAtX(B.x2 + 1 + bb.originLeft);
             
-        } else if (y2 <= B.y1 && bb.y2 >= B.y1) {
-            this.to = to = traj.pointAtY(B.y1 - 1 - bb.originTop);
+        } else if (bb.y2 <= B.y1 && bb.y2 >= B.y1) {
             this.side = B.topSide;
+            to = tr.pointAtY(B.y1 - 1 - bb.originTop);
             
-        } else if (y1 >= B.y2 && bb.y1 <= B.y2) {
-            this.to = to = traj.pointAtY(B.y2 + 1 + bb.originBottom );
+        } else if (bb.y1 >= B.y2 && bb.y1 <= B.y2) {
             this.side = B.bottomSide;
+            to = tr.pointAtY(B.y2 + 1 + bb.originBottom );
             
         } else {
-            console.error('Null reflection line!', 'to:', to, 'blockers:', blockers);
-            // throw new Error('Null reflection line! to:'+to+', blockers:'+blockers);
+            console.error('Null reflection line!', 'to:', to, 'blocker:', blocker);
+            // throw new Error('Null reflection line! to:'+to+', blocker:'+blocker);
         }
         
-        // Move bounding box to furthest non-overlapping point
-        bb.relocate(to);
-        
-        // Return how much time consumed
-        var consumed = traj.timeToMove(to.x-o.x, to.y-o.y);
-        // console.log('stepTo('+t+') --> wanted '+oto+' but BLOCKED! by '+this.blocker+', so ending at '+to+', consuming '+consumed);
-        
-        return consumed;
+        return to;
+    },
+    
+    consumeTime : function consumeTime(t){
+        this.trajectory.tCurrent += t;
+        this.remaining = Math.max(0, this.remaining-t);
+        return this;
     }
     
 })
deleted file mode 100644 (file)
index 03c5b3fa961b758ea496b7a3cb6594c17fe30a9f..0000000000000000000000000000000000000000
+++ /dev/null
@@ -1,70 +0,0 @@
-var Y     = require('Y').Y
-,   op    = require('Y/op')
-,   Rect  = require('ezl/shape').Rect
-,   Thing = require('tanks/thing/thing').Thing
-,
-
-
-Wall =
-exports['Wall'] =
-Thing.subclass('Wall', {
-    isBoundary : false,
-    blocking : true,
-    active   : false,
-    
-    originX : 0,
-    originY : 0,
-    
-    stats : {
-        hp    : Infinity,
-        move  : 0,
-        power : 0,
-        speed : 0,
-        shots : 0
-    },
-    
-    
-    init : function initWall(x,y, w,h, isBoundary){
-        this.width = w;
-        this.height = h;
-        this.isBoundary = !!isBoundary;
-        Thing.init.call(this);
-        this.position(x,y);
-    },
-    
-    // inactive
-    createCooldowns : op.nop,
-    
-    // indestructable
-    dealDamage : op.nop,
-    
-    
-    render : function render(parent){
-        if (this.isBoundary)
-            return this;
-        
-        if (this.shape)
-            this.shape.remove();
-        
-        this.shape =
-            new Rect(this.width, this.height)
-                .position(this.loc.x, this.loc.y)
-                .fill('rgba(255,255,255, 0.25)')
-                .appendTo( parent );
-        
-        return this;
-    },
-    
-    toString : function(){
-        var bb = this.boundingBox
-        ,   x1,y1, x2,y2;
-        if (bb){
-            x1 = bb.x1; y1 = bb.y1;
-            x2 = bb.x2; y2 = bb.y2;
-        } else {
-            x1=y1=x2=y2=NaN;
-        }
-        return this.className+'['+x1+','+y1+', '+x2+','+y2+'](w='+this.width+', h='+this.height+')';
-    }
-});
-
index 22d1b94a04fc9887b4114e8a459a1279ef3e1806..b2a7e9c646b2d972e64001d92a0bc49e16691bd9 100644 (file)
@@ -1,19 +1,21 @@
 //  -*- mode: JavaScript; tab-width: 4; indent-tabs-mode: nil; -*-
 var Y          = require('Y').Y
 ,   op         = require('Y/op')
+
 ,   math       = require('ezl/math')
 ,   shape      = require('ezl/shape')
+,   Line       = shape.Line
+,   Circle     = shape.Circle
+
 ,   config     = require('tanks/config').config
 ,   thing      = require('tanks/thing/thing')
-,   Wall       = require('tanks/map/wall').Wall
+,   map        = require('tanks/map/map')
+,   Wall       = require('tanks/thing/wall').Wall
 ,   Trajectory = require('tanks/map/trajectory').Trajectory
 ,   Traversal  = require('tanks/map/traversal').Traversal
 ,   Explosion  = require('tanks/fx/explosion').Explosion
-
 ,   fillStats  = thing.fillStats
 ,   Thing      = thing.Thing
-,   Line       = shape.Line
-,   Circle     = shape.Circle
 ,
 
 
@@ -31,7 +33,8 @@ Thing.subclass('Bullet', {
     },
     
     // Instance
-    blocking    : true,
+    blocking    : map.BLOCKING,
+    
     bounces     : 0,
     bounceLimit : 1,
     
@@ -92,12 +95,12 @@ Thing.subclass('Bullet', {
         if (this.dead) return this;
         
         var tvsl = new Traversal(this)
-        ,   to = tvsl.step(elapsed)
+        ,   to = tvsl.traverse(elapsed)
         ;
         
         if (tvsl.isBlocked && !this.dead) {
             tvsl = new Traversal(this, this.trajectory);
-            to = tvsl.step(tvsl.remaining);
+            to = tvsl.traverse(tvsl.remaining);
         }
         
         if (!this.dead)
@@ -109,25 +112,30 @@ Thing.subclass('Bullet', {
     onCollide : function onCollide(evt){
         if ( this.dead ) return;
         
-        var ng
-        ,   tvsl = evt.data.traversal
-        ,   traj = this.trajectory
+        var traj = this.trajectory
+        
+        ,   ng, d = evt.data
+        ,   tvsl = d.traversal
         ,   to   = tvsl.to
+        ,   unit = d.blocker
         
-        ,   unit = evt.trigger
-        ,   hitAWall = unit instanceof Wall
+        ,   isReflective = unit instanceof Wall // XXX: unit.reflective
         ;
         
+        // Ignore collisions with zones
+        if (unit.blocking === map.ZONE)
+            return;
+        
         // Reflection!
-        if ( hitAWall && this.bounceLimit >= ++this.bounces ) {
-            if ( tvsl.isCorner ) {
-                ng = to;
-            } else {
-                if (!tvsl.side) return console.error('Null reflection line!', 'to:', to, 'blockers:', tvsl.blockers);
-                ng = math.reflect(traj.p2, tvsl.side);
-            }
+        if ( isReflective && this.bounceLimit >= ++this.bounces ) {
+            if (!tvsl.side)
+                return console.error('Null reflection line!', 'to:', to, 'blockers:', tvsl.blockers);
+            
+            ng = math.reflect(traj.p2, tvsl.side);
             traj.reset(to.x,to.y, ng.x,ng.y);
-            this.render(this.game.level); // to render the new reflection line
+            
+            this.dirty = true; // to render the new reflection line, if it's visible
+            
             return;
         }
         
@@ -143,7 +151,7 @@ Thing.subclass('Bullet', {
         ,   start = bsize, end = bsize
         ;
         
-        if (hitAWall) {
+        if (isReflective) {
             x = loc.x;
             y = loc.y;
         } else {
index 93d9be7f241b77d6b92f73fdc200fe50fe748920..8aa096ae776322cca313c0474ff9bac6cbbdd7a3 100644 (file)
@@ -1,4 +1,5 @@
 exports['Thing']      = require('tanks/thing/thing').Thing;
+exports['Wall']       = require('tanks/thing/wall').Wall;
 exports['Bullet']     = require('tanks/thing/bullet').Bullet;
 exports['Tank']       = require('tanks/thing/tank').Tank;
 exports['PlayerTank'] = require('tanks/thing/player').PlayerTank;
diff --git a/src/tanks/map/wall.cjs b/src/tanks/map/wall.cjs
index 7e218ff8900bdea0a9f5db6623d0f311e3c5779a..704708abd4baaa7fb2bc1ca340faee285361c52c 100644 (file)
+++ b/src/tanks/map/wall.cjs
@@ -1,9 +1,12 @@
 var Y     = require('Y').Y
 ,   op    = require('Y/op')
-,   Thing = require('tanks/thing/thing').Thing
+
 ,   shape = require('ezl/shape')
 ,   Rect  = shape.Rect
 
+,   map   = require('tanks/map/map')
+,   Thing = require('tanks/thing/thing').Thing
+
 ,   SIZE = REF_SIZE * 0.5
 ,
 
@@ -11,7 +14,7 @@ var Y     = require('Y').Y
 Item =
 exports['Item'] =
 Thing.subclass('Item', {
-    blocking : true,
+    blocking : map.ZONE,
     active   : false,
     
     width  : SIZE,
index fdd177ac60a76ca79f479d8bf0b665348f12b3e4..d59b3ce6349449938a5b7b0ca8ad4e1655d6f9c8 100644 (file)
@@ -2,6 +2,7 @@
 //#ensure "jquery.hotkeys"
 
 var Y    = require('Y').Y
+,   map  = require('tanks/map/map')
 ,   Tank = require('tanks/thing/tank').Tank
 ,
 
@@ -9,6 +10,7 @@ var Y    = require('Y').Y
 PlayerTank =
 exports['PlayerTank'] =
 Tank.subclass('PlayerTank', {
+    blocking : map.BLOCKING,
     
     bodyColor   : '#E73075',
     turretColor : '#A72F5B',
index cc38b244e9bf345e37bdc2305f37eff30fe40bc0..2c60493bf56748c73295af71dd5317652669fb08 100644 (file)
@@ -5,17 +5,17 @@ var Y             = require('Y').Y
 ,   shape         = require('ezl/shape')
 ,   Cooldown      = require('ezl/loop').Cooldown
 ,   CooldownGauge = require('ezl/widget').CooldownGauge
+,   Vec           = vec.Vec
+,   manhattan     = vec.manhattan
+,   Rect          = shape.Rect
+,   Circle        = shape.Circle
 
+,   map           = require('tanks/map/map')
 ,   Thing         = require('tanks/thing/thing').Thing
 ,   Bullet        = require('tanks/thing/bullet').Bullet
 ,   Trajectory    = require('tanks/map/trajectory').Trajectory
 ,   Traversal     = require('tanks/map/traversal').Traversal
 
-,   Vec           = vec.Vec
-,   manhattan     = vec.manhattan
-,   Rect          = shape.Rect
-,   Circle        = shape.Circle
-
 ,   isBullet = Y.is(Bullet)
 ,   BULLET_MOVE_PER_FRAME = MS_PER_FRAME * Bullet.prototype.stats.move*REF_SIZE/1000
 ,   _X = 0, _Y = 1
@@ -27,8 +27,7 @@ exports['Tank'] =
 Thing.subclass('Tank', function(Tank){
     
     Y.core.descriptors(this, {
-        projectile : Bullet,
-        blocking   : true,
+        blocking : map.BLOCKING,
         
         bodyColor   : '#83BB32',
         turretColor : '#1C625B',
@@ -57,6 +56,7 @@ Thing.subclass('Tank', function(Tank){
             shootEnemy    : 0.75    // shoot at enemy tank if in range
         },
         
+        projectile : Bullet,
         buffs : null,
         
         nShots : 0,
@@ -84,8 +84,8 @@ Thing.subclass('Tank', function(Tank){
         this.elapsed = elapsed;
         this.now = now;
         
-        // this.continueMove();
-        // return;
+        this.continueMove();
+        return;
         
         // Check to see if we should obey our last decision, and not recalc
         if (this.forceCurrentMove && this.forceCurrentMove() && this.currentMoveLimit > now) {
@@ -150,7 +150,7 @@ Thing.subclass('Tank', function(Tank){
         this.trajectory = new Trajectory(this, loc.x,loc.y, x,y, this.movePerMs);
         
         var tvsl = new Traversal(this)
-        ,   to = tvsl.step(this.elapsed, x,y) ;
+        ,   to = tvsl.traverse(this.elapsed, x,y) ;
         
         this.game.moveThingTo(this, to.x,to.y);
         
@@ -187,14 +187,13 @@ Thing.subclass('Tank', function(Tank){
         this.forceCurrentMove = false;
         this.currentMoveLimit = -1;
         
-        // console.log('calculatePath() moving toward:', end);
         var pm    = this.game.pathmap
         ,   start = this.loc
         
         ,   path  = this.currentPath = pm.path(this, end)
         ,   to    = this.currentMove = path.shift()
         ;
-        // console.log('--> next move:', this.currentMove);
+        console.log('calculatePath()', start, 'toward:', end, 'next:', this.currentMove, 'path: ['+path.invoke('toString')+']');
     };
     
     
@@ -381,7 +380,7 @@ Thing.subclass('Tank', function(Tank){
      * or when the world needs to be redrawn from scratch.
      */
     this['render'] =
-    function render( parent ){
+    function render(parent){
         if (this.shape) this.shape.remove();
         
         var w = this.width,  w2 = w/2
index 4c9b01ae217f3a703f1b278db1db5d1265ce1c30..3315dc9a23d70ba78811c0c0c805d3f1ac536de6 100644 (file)
@@ -1,15 +1,20 @@
 //#ensure "evt"
 var Y           = require('Y').Y
 ,   op          = require('Y/op')
+
 ,   evt         = require('evt')
+
 ,   Loc         = require('ezl/loc/loc').Loc
 ,   BoundingBox = require('ezl/loc/boundingbox').BoundingBox
 ,   Cooldown    = require('ezl/loop').Cooldown
+
 ,   config      = require('tanks/config').config
+,   map         = require('tanks/map/map')
 
 ,   THING_ID = 0
 ,
 
+
 fillStats =
 exports['fillStats'] =
 function fillStats(stats){
@@ -32,8 +37,7 @@ function fillStats(stats){
 
 
 Thing =
-exports['Thing'] =
-new evt.Class('Thing', {
+exports['Thing'] = new evt.Class('Thing', {
     // Config
     showAttackCooldown : null,
     
@@ -50,14 +54,15 @@ new evt.Class('Thing', {
     // AI "Cooldowns" (max frequency of each action per sec)
     ai : {}, // see Tank
     
+    
     // *** Bookkeeping *** //
     
     id      : 0,
     align   : 0, // 0 reserved for neutral units
     dead    : false,
+    dirty   : true,
     
-    pathing  : null, 
-    blocking : true, // Whether the agent obstructs pathing
+    blocking : map.BLOCKING, // Pathing type @see {map} for constant definitions
     active   : true, // Whether the agent takes actions
     
     // Location
@@ -74,8 +79,8 @@ new evt.Class('Thing', {
     height : REF_SIZE,
     
     // Accessors
-    set  : op.set.methodize(),
-    attr : op.attr.methodize(),
+    'set'  : op.set.methodize(),
+    'attr' : op.attr.methodize(),
     
     get movePerMs(){ return this.stats.move*REF_SIZE/1000; },
     
@@ -184,15 +189,19 @@ new evt.Class('Thing', {
      * Sets up unit appearance for minimal updates. Called once at start,
      * or when the world needs to be redrawn from scratch.
      */
-    render : function render(){
+    render : function render(parent){
         return this;
     },
     
     draw : function draw(){
-        if (this.dead) 
-            this.shape.hide();
-        else
+        if (!this.dead) {
+            if (this.dirty) {
+                this.dirty = false;
+                this.render(this.game.level);
+            }
             this.shape.draw();
+        } else
+            this.shape.hide();
     },
     
     toString : function toString(){
index 7b1d73f3cc0e7dd82ddcfe8690973e5a86efdda4..b943aaf43a77d83a69c040ad8aa113638d0d86d3 100644 (file)
 <script src="build/jquery.hotkeys.js" type="text/javascript"></script>
 <script src="build/Y/modules/y.kv.js" type="text/javascript"></script>
 <script src="build/ezl/util/binaryheap.js" type="text/javascript"></script>
-<script src="build/ezl/util/astar.js" type="text/javascript"></script>
-<script src="build/tanks/map/traversal.js" type="text/javascript"></script>
 <script src="build/ezl/util/tree/quadtree.js" type="text/javascript"></script>
 <script src="build/Y/modules/y.scaffold.js" type="text/javascript"></script>
 <script src="build/Y/modules/y.config.js" type="text/javascript"></script>
 <script src="build/Y/modules/y.cookies.js" type="text/javascript"></script>
+<script src="build/tanks/map/map.js" type="text/javascript"></script>
+<script src="build/tanks/map/traversal.js" type="text/javascript"></script>
 <script src="build/tanks/config.js" type="text/javascript"></script>
+<script src="build/tanks/map/pathmap.js" type="text/javascript"></script>
 <script src="build/tanks/ui/configui.js" type="text/javascript"></script>
 <script src="build/tanks/thing/thing.js" type="text/javascript"></script>
 <script src="build/tanks/map/trajectory.js" type="text/javascript"></script>
-<script src="build/tanks/map/wall.js" type="text/javascript"></script>
+<script src="build/tanks/ui/pathmapui.js" type="text/javascript"></script>
+<script src="build/tanks/thing/wall.js" type="text/javascript"></script>
 <script src="build/tanks/thing/item.js" type="text/javascript"></script>
 <script src="build/tanks/ui/grid.js" type="text/javascript"></script>
 <script src="build/tanks/fx/explosion.js" type="text/javascript"></script>
 <script src="build/tanks/thing/bullet.js" type="text/javascript"></script>
 <script src="build/tanks/thing/tank.js" type="text/javascript"></script>
-<script src="build/tanks/map/map.js" type="text/javascript"></script>
-<script src="build/tanks/map/pathmap.js" type="text/javascript"></script>
 <script src="build/tanks/thing/player.js" type="text/javascript"></script>
 <script src="build/tanks/map/level.js" type="text/javascript"></script>
-<script src="build/tanks/ui/pathmapui.js" type="text/javascript"></script>
 <script src="build/tanks/thing.js" type="text/javascript"></script>
 <script src="build/tanks/map.js" type="text/javascript"></script>
 <script src="build/tanks/game.js" type="text/javascript"></script>
deleted file mode 100644 (file)
index ef4a61d4dce005d3854678c687739cfa996a7cba..0000000000000000000000000000000000000000
Binary files a/www/img/tank-icon.png and /dev/null differ
deleted file mode 100644 (file)
index b7319141e60e52c58fd8f56ab6bbd1d14c7aa4b1..0000000000000000000000000000000000000000
Binary files a/www/img/viewport.png and /dev/null differ
diff --cc www/deps.html
Simple merge
diff --cc www/img/tank-icon.png
Simple merge
diff --cc www/img/viewport.png
Simple merge