Adds Fences; minor refactor to map-like stuff.
authordsc <david.schoonover@gmail.com>
Sun, 9 Jan 2011 10:26:50 +0000 (02:26 -0800)
committerdsc <david.schoonover@gmail.com>
Sun, 9 Jan 2011 10:26:50 +0000 (02:26 -0800)
19 files changed:
data/types/levels.yaml
src/tanks/constants.cjs
src/tanks/map/fence.cjs [new file with mode: 0644]
src/tanks/map/index.cjs
src/tanks/map/level.cjs
src/tanks/map/map.cjs
src/tanks/map/pathing/index.cjs [new file with mode: 0644]
src/tanks/map/pathing/pathmap.cjs [moved from src/tanks/map/pathmap.cjs with 89% similarity]
src/tanks/map/pathing/trajectory.cjs [moved from src/tanks/map/trajectory.cjs with 94% similarity]
src/tanks/map/pathing/traversal.cjs [moved from src/tanks/map/traversal.cjs with 87% similarity]
src/tanks/map/wall.cjs [moved from src/tanks/thing/wall.cjs with 58% similarity]
src/tanks/mixins/quantified.cjs
src/tanks/thing/bullet.cjs
src/tanks/thing/fence.cjs [deleted file]
src/tanks/thing/index.cjs
src/tanks/thing/item.cjs
src/tanks/thing/tank.cjs
src/tanks/thing/thing.cjs
src/tanks/ui/pathmapui.cjs

index df2c2f5..86b4a1f 100644 (file)
@@ -30,10 +30,18 @@ types:
             args: [150,300, 50,50]
           - type: wall
             args: [100,100, 50,50]
+          - type: fence
+            args: [360,210, 130,30]
+          - type: fence
+            args: [10,210, 30,30]
+          - type: fence
+            args: [110,210, 30,30]
+          - type: fence
+            args: [210,210, 30,30]
         units:
           - type: player
             align: 1
-            loc: [275,475]
+            loc: [325,475]
           - type: blue
             align: 1
             loc: [175,475]
index b44aa33..2404b79 100644 (file)
@@ -1,11 +1,11 @@
-//#exports PathingType.{PASSABLE,ZONE,BLOCKING,IRREGULAR}
+//#exports BoundsType.{PASSABLE,ZONE,BLOCKING,IRREGULAR}
+//#exports DensityType.{PASSABLE,BOUNDARY,DENSE,SPARSE,IRREGULAR}
 //#exports StatInvariant.{NONE,RATIO,FULL}
 
 require('Y').Y.extend(exports, {
     
-    /// Pathing Constants ///
-    /** How this object interacts with the world. */
-    PathingType : {
+    /** How to determine the bounds of this object for pathing. */
+    BoundsType : {
         
         /** Does not obstruct other objects. */
         PASSABLE : 0,
@@ -21,6 +21,30 @@ require('Y').Y.extend(exports, {
         
     },
     
+    /**
+     * Governs the kinds of objects this object obstructs.
+     */
+    DensityType : {
+        
+        /** Not an obstruction. */
+        PASSABLE : 0,
+        
+        /** Blocks absolutely everything. No exceptions. */
+        BOUNDARY : 1,
+        
+        /** Blocks everything material (eg, units). */
+        DENSE : 2,
+        
+        /** Does not block small things (eg, projectiles). */
+        SPARSE : 3,
+        
+        /** Blocks based on custom rules. */
+        IRREGULAR : 4
+        
+    },
+    
+    
+    
     /** 
      * Invariant to restore for the current value when modifying the base or max of a Stat.
      * @see {tanks.effects.stat.Stat} for the default behavior of its methods.
diff --git a/src/tanks/map/fence.cjs b/src/tanks/map/fence.cjs
new file mode 100644 (file)
index 0000000..cb1e79b
--- /dev/null
@@ -0,0 +1,31 @@
+var Y = require('Y').Y
+
+,   constants   = require('tanks/constants')
+,   BoundsType  = constants.BoundsType
+,   DensityType = constants.DensityType
+,   Wall        = require('tanks/map/wall').Wall
+,
+
+
+Fence =
+exports['Fence'] =
+Wall.subclass('Fence', {
+    
+    isReflective : false,
+    fillStyle    : 'rgba(0,0,0, 0.25)',
+    strokeStyle  : 'transparent',
+    lineWidth    : 0,
+    
+    blocking : BoundsType.BLOCKING,
+    density  : DensityType.SPARSE,
+    isBoundary : false,
+    
+    
+    
+    init : function initFence(x,y, w,h){
+        Wall.init.call(this, x,y, w,h, this.isBoundary);
+    }
+    
+});
+
+Wall.register('fence', Fence);
index d143552..0b2364f 100644 (file)
@@ -1,9 +1,9 @@
-var Y = require('Y').Y;
-
-Y.extend(exports, {
+require('Y').Y
+.extend(exports, {
+    'pathing'    : require('tanks/map/pathing'),
+    
     'Map'        : require('tanks/map/map').Map,
-    'PathMap'    : require('tanks/map/pathmap').PathMap,
     'Level'      : require('tanks/map/level').Level,
-    'Traversal'  : require('tanks/map/traversal').Traversal,
-    'Trajectory' : require('tanks/map/trajectory').Trajectory
+    'Wall'       : require('tanks/map/wall').Wall,
+    'Fence'      : require('tanks/map/fence').Fence
 });
index 328de4a..c31d975 100644 (file)
@@ -5,12 +5,12 @@ var Y          = require('Y').Y
 ,   Rect       = require('ezl/shape').Rect
 
 ,   Buff       = require('tanks/effects/buff').Buff
-,   PathMap    = require('tanks/map/pathmap').PathMap
+,   PathMap    = require('tanks/map/pathing/pathmap').PathMap
 ,   Thing      = require('tanks/thing/thing').Thing
 ,   Tank       = require('tanks/thing/tank').Tank
 ,   Item       = require('tanks/thing/item').Item
 ,   Player = require('tanks/thing/player').Player
-,   Wall       = require('tanks/thing/wall').Wall
+,   Wall       = require('tanks/map/wall').Wall
 ,   Speciated  = require('tanks/mixins/speciated').Speciated
 
 ,   min = Y(Math.min).limit(2)
index feec7a3..92461b4 100644 (file)
@@ -1,32 +1,45 @@
 //  -*- mode: JavaScript; tab-width: 4; indent-tabs-mode: nil; -*-
 var Y        = require('Y').Y
-,   QuadTree = require('ezl/util/tree/quadtree').QuadTree
+,   QuadTree    = require('ezl/util/tree/quadtree').QuadTree
 
-
-/** 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
+,   constants   = require('tanks/constants')
+,   BoundsType  = constants.BoundsType
+,   DensityType = constants.DensityType
 ,
 
 
 Map =
 exports['Map'] =
 QuadTree.subclass('Map', {
+    allWalls   : null, // All walls in this map
+    innerWalls : null, // Non-boundary walls
+    denseWalls : null, // Non-boundary non-Fences
+    
+    
+    init : function initMap(x1,y1, x2,y2, capacity){
+        this.allWalls   = Y([]);
+        this.innerWalls = Y([]);
+        this.denseWalls = Y([]);
+        QuadTree.init.call(this, x1,y1, x2,y2, capacity);
+    },
     
     addBlocker : function addBlocker(obj){
         this.removeBlocker(obj);
+        
         var pt = obj.blocking
         ,   bb = obj.bbox ;
         if (pt !== undefined && bb)
             obj.region = this.set(bb.x1,bb.y1, bb.x2,bb.y2, obj);
+        
+        if (obj.isWall) {
+            if ( !this.allWalls.has(obj) )
+                this.allWalls.push(obj);
+            if ( !(obj.isBoundary || this.innerWalls.has(obj)) )
+                this.innerWalls.push(obj);
+            if ( obj.density === DensityType.DENSE || obj.density === DensityType.BOUNDARY )
+                this.denseWalls.push(obj);
+        }
+        
         return obj;
     },
     
@@ -35,6 +48,13 @@ QuadTree.subclass('Map', {
             this.remove(obj.region);
             delete obj.region;
         }
+        
+        if (obj.isWall) {
+            this.allWalls.remove(obj);
+            this.innerWalls.remove(obj);
+            this.denseWalls.remove(obj);
+        }
+        
         return obj;
     },
     
@@ -44,13 +64,21 @@ QuadTree.subclass('Map', {
             y2 = x1.y2;  x2 = x1.x2;
             y1 = x1.y1;  x1 = x1.x1;
         }
-        var bs = this.get(x1,y1, x2,y2)
-            .filter('_.blocking === BLOCKING');
+        var bs = this.get(x1,y1, x2,y2).filter(this._filterBlockers, this);
         if (ignore && bs.length)
             return bs.remove.apply(bs, ignore || []);
         else
             return bs;
-    }
+    },
     
+    _filterBlockers : function _filterBlockers(v){
+        return (v.blocking === BoundsType.BLOCKING || v.blocking === BoundsType.IRREGULAR);
+    },
+    
+    wallObstructs : function wallObstructs(line){
+        return this.innerWalls.some(function(wall){
+            return wall.bbox.intersects(line);
+        }, this);
+    }
     
 });
diff --git a/src/tanks/map/pathing/index.cjs b/src/tanks/map/pathing/index.cjs
new file mode 100644 (file)
index 0000000..633d120
--- /dev/null
@@ -0,0 +1,6 @@
+require('Y').Y
+.extend(exports, {
+    'PathMap'    : require('tanks/map/pathing/pathmap').PathMap,
+    'Traversal'  : require('tanks/map/pathing/traversal').Traversal,
+    'Trajectory' : require('tanks/map/pathing/trajectory').Trajectory
+});
similarity index 89%
rename from src/tanks/map/pathmap.cjs
rename to src/tanks/map/pathing/pathmap.cjs
index 3a09ae2..6dc6504 100644 (file)
@@ -9,9 +9,10 @@ var Y          = require('Y').Y
 ,   Line       = math.Line
 
 ,   config     = require('tanks/config').config
-,   PathingType = require('tanks/constants').PathingType
+,   constants   = require('tanks/constants')
+,   BoundsType = constants.BoundsType
+,   DensityType = constants.DensityType
 ,   Map        = require('tanks/map/map').Map
-,   Wall       = require('tanks/thing/wall').Wall
 
 
 ,   SQRT_TWO = Math.sqrt(2)
@@ -37,44 +38,12 @@ Map.subclass('PathMap', {
     
     
     
-    
     init : function init(x1,y1, x2,y2, capacity, buffer_size) {
         this.buffer_size = buffer_size;
         x1 -= buffer_size; y1 -= buffer_size;
         x2 += buffer_size; y2 += buffer_size;
-        QuadTree.init.call(this, x1,y1, x2,y2, capacity);
+        Map.init.call(this, x1,y1, x2,y2, capacity);
         this._squares = {};
-        
-        this.innerWalls = Y([]);
-        this.allWalls = Y([]);
-        
-        // this.game.addEventListener('ready', this.setup.bind(this, x1,y1, x2,y2));
-    },
-    
-    addBlocker : function addBlocker(obj){
-        Map.fn.addBlocker.call(this, obj);
-        if (obj instanceof Wall) {
-            if ( !this.allWalls.has(obj) )
-                this.allWalls.push(obj);
-            if ( !(obj.isBoundary || this.innerWalls.has(obj)) )
-                this.innerWalls.push(obj);
-        }
-        return obj;
-    },
-    
-    removeBlocker : function removeBlocker(obj){
-        Map.fn.removeBlocker.call(this, obj);
-        if (obj instanceof Wall) {
-            this.innerWalls.remove(obj);
-            this.allWalls.remove(obj);
-        }
-        return obj;
-    },
-    
-    wallObstructs : function wallObstructs(line){
-        return this.innerWalls.some(function(wall){
-            return wall.bbox.intersects(line);
-        }, this);
     },
     
     /**
@@ -246,7 +215,8 @@ Y.subclass('Square', new Vec(0,0), {
         if (this.pathId === pathId)
             return this;
         
-        this.pathId = pathId;
+        this.pathId  = pathId;
+        this.agent   = this.pathmap._agent;
         this.blocked = this._blocked();
         
         this.dist      = 0;
@@ -264,8 +234,7 @@ Y.subclass('Square', new Vec(0,0), {
      */
     _blocked : function blocked(){
         var pm    = this.pathmap
-        ,   agent = pm._agent
-        // ,   bb    = agent.bbox
+        // ,   bb    = this.agent.bbox
         
         // ,   origin = bb.relOrigin
         // ,   left = origin.x,  right  = bb.width - left
@@ -280,7 +249,7 @@ Y.subclass('Square', new Vec(0,0), {
         ,   y1 = y,  y2 = y + SIZE
         
         ,   blockers = pm.get(x1,y1, x2,y2)
-                .remove(agent)
+                .remove(this.agent)
                 .filter(this._filterBlocked, this)
         ;
         
@@ -291,7 +260,8 @@ Y.subclass('Square', new Vec(0,0), {
      * @private
      */
     _filterBlocked : function filterBlocked(v, r){
-        return v.blocking === PathingType.BLOCKING && !v.isBoundary;
+        return (v.blocking === BoundsType.BLOCKING  && !v.isBoundary)
+            || (v.blocking === BoundsType.IRREGULAR && v.testCollide(this.agent,this,null) ); // FIXME: hm. calc bbox?
     },
     
     getNeighbors : function getNeighbors(){
similarity index 94%
rename from src/tanks/map/trajectory.cjs
rename to src/tanks/map/pathing/trajectory.cjs
index 9653053..837c758 100644 (file)
@@ -5,7 +5,8 @@ var Y     = require('Y').Y
 ,   Line  = math.Line
 ,   Rect  = math.Rect
 ,   BoundingBox = require('ezl/loc').BoundingBox
-,   Thing = require('tanks/thing/thing').Thing
+
+,   Thing  = require('tanks/thing/thing').Thing
 
 ,   BOUND_SIZE_RATIO = 0.75
 ,
@@ -153,8 +154,13 @@ Line.subclass('Trajectory', {
     },
     
     pathBlocked : function pathBlocked(obj, ignore){
-        var blockers =
-                this.pathmap.innerWalls
+        var walls, blockers;
+        if (this.owner.isProjectile)
+            walls = this.pathmap.denseWalls;
+        else
+            walls = this.pathmap.innerWalls; // FIXME: won't filter out concave intersections with the bounds
+        
+        blockers = walls
                     .concat( this.game.units )
                     .apply('remove', [this.owner].concat(ignore || []) )
                     .filter( this.intersects )
similarity index 87%
rename from src/tanks/map/traversal.cjs
rename to src/tanks/map/pathing/traversal.cjs
index 9d9237b..6f6beda 100644 (file)
@@ -1,5 +1,7 @@
 var Y = require('Y').Y
-,   PathingType = require('tanks/constants').PathingType
+,   constants   = require('tanks/constants')
+,   BoundsType = constants.BoundsType
+,   DensityType = constants.DensityType
 ,
 
 Traversal =
@@ -90,15 +92,26 @@ Y.subclass('Traversal', {
      * Filters found obstructions to keep only those not ignored and blocking.
      */
     checkBlocker : function checkBlocker(blocker){
-        var blocking = blocker.blocking;
+        var blocking = blocker.blocking
+        ,   density  = blocker.density;
         
         // All blockers after the first are ignored
-        if ( this.ignore.has(blocker) || blocking === PathingType.PASSABLE || this.isBlocked )
+        if ( this.isBlocked || this.ignore.has(blocker)
+                // Skip passable objects
+                || (blocking === BoundsType.PASSABLE)
+                
+                // Ask irregular objects if we hit
+                || (blocking === BoundsType.IRREGULAR && !blocker.testCollide(this.thing,this.to,this.bbox))
+                
+                // Filter out Sparse objects if bullet
+                || (density === DensityType.SPARSE && this.thing.isProjectile) )
+        {
             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 === PathingType.ZONE ) {
+        if ( blocking === BoundsType.ZONE ) {
             this.ignore.push(blocker);
             this.collide(blocker);
             return false;
@@ -111,7 +124,6 @@ Y.subclass('Traversal', {
     
     collide : function collide(blocker){
         var thing = this.thing;
-        
         thing.fire('collide', blocker, { 'traversal':this, 'unit':blocker });
         blocker.fire('collide', thing, { 'traversal':this, 'unit':thing });
     },
similarity index 58%
rename from src/tanks/thing/wall.cjs
rename to src/tanks/map/wall.cjs
index f59831f..a814c75 100644 (file)
@@ -1,10 +1,12 @@
-var Y         = require('Y').Y
-,   op        = require('Y/op')
+var Y  = require('Y').Y
+,   op = require('Y/op')
 
-,   Rect      = require('ezl/shape').Rect
+,   Rect = require('ezl/shape').Rect
 
-,   Thing     = require('tanks/thing/thing').Thing
-,   PathingType = require('tanks/constants').PathingType
+,   Thing       = require('tanks/thing/thing').Thing
+,   constants   = require('tanks/constants')
+,   BoundsType  = constants.BoundsType
+,   DensityType = constants.DensityType
 ,
 
 
@@ -16,9 +18,27 @@ Thing.subclass('Wall', {
     originX : 0,
     originY : 0,
     
-    blocking : PathingType.BLOCKING,
+    width : 0,
+    height : 0,
+    
+    
+    blocking : BoundsType.BLOCKING,
+    density  : DensityType.DENSE,
+    isWall : true,
     isReflective : true,
+    
+    // rendering
+    
     isRenderable : true,
+    fillStyle    : 'rgba(255,255,255, 0.25)',
+    strokeStyle  : 'transparent',
+    lineWidth    : 0,
+    
+    renderWidth   : 0,
+    renderHeight  : 0,
+    renderOffsetX : 0,
+    renderOffsetY : 0,
+    
     
     // inactive
     active : false,
@@ -45,6 +65,8 @@ Thing.subclass('Wall', {
         this.width = w;
         this.height = h;
         this.isBoundary = !!isBoundary;
+        if (isBoundary)
+            this.density = DensityType.BOUNDARY;
         Thing.init.call(this);
         this.position(x,y);
     },
@@ -59,9 +81,10 @@ Thing.subclass('Wall', {
             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)')
+            new Rect( this.renderWidth || this.width, this.renderHeight || this.height )
+                .position(this.loc.x + this.renderOffsetX, this.loc.y + this.renderOffsetY)
+                .fill(this.fillStyle)
+                .stroke(this.strokeStyle, this.lineWidth)
                 .appendTo( parent );
         
         return this;
index 05f2e4a..0889a07 100644 (file)
@@ -53,14 +53,14 @@ Mixin.subclass('Quantified', {
     onBuffAcquired : function onBuffAcquired(evt){
         var buff = evt.data.buff;
         this.buffs.push(buff);
-        console.log('Buff '+buff+' acquired by '+this);
+        // console.log('Buff '+buff+' acquired by '+this);
         // TODO: update UI
     },
     
     onBuffLost : function onBuffLost(evt){
         var buff = evt.data.buff;
         this.buffs.remove(buff);
-        console.log('Buff '+buff+' lost by '+this);
+        // console.log('Buff '+buff+' lost by '+this);
         // TODO: update UI
     },
     
index 41be7dc..1a48969 100644 (file)
@@ -8,14 +8,12 @@ var Y          = require('Y').Y
 ,   Circle     = shape.Circle
 
 ,   config     = require('tanks/config').config
-,   PathingType = require('tanks/constants').PathingType
+,   BoundsType = require('tanks/constants').BoundsType
 ,   stat       = require('tanks/effects/stat')
-
-,   Thing      = require('tanks/thing/thing').Thing
-,   Wall       = require('tanks/thing/wall').Wall
-,   Trajectory = require('tanks/map/trajectory').Trajectory
-,   Traversal  = require('tanks/map/traversal').Traversal
 ,   Explosion  = require('tanks/fx/explosion').Explosion
+,   Thing      = require('tanks/thing/thing').Thing
+,   Trajectory = require('tanks/map/pathing/trajectory').Trajectory
+,   Traversal  = require('tanks/map/pathing/traversal').Traversal
 ,
 
 
@@ -33,8 +31,9 @@ Thing.subclass('Bullet', {
     },
     
     // Instance
-    blocking : PathingType.BLOCKING,
+    blocking : BoundsType.BLOCKING,
     isRenderable : true,
+    isProjectile : true,
     
     bounces     : 0,
     bounceLimit : 1,
@@ -121,7 +120,7 @@ Thing.subclass('Bullet', {
         ;
         
         // Ignore collisions with zones
-        if (unit.blocking === PathingType.ZONE)
+        if (unit.blocking === BoundsType.ZONE)
             return;
         
         // Reflection!
diff --git a/src/tanks/thing/fence.cjs b/src/tanks/thing/fence.cjs
deleted file mode 100644 (file)
index b08b7b0..0000000
+++ /dev/null
@@ -1,80 +0,0 @@
-var Y     = require('Y').Y
-,   op    = require('Y/op')
-,   Rect  = require('ezl/shape').Rect
-,   Wall  = require('tanks/thing/wall').Wall
-,
-
-
-Fence =
-exports['Fence'] =
-Wall.subclass('Fence', {
-    align : 0, // 0 reserved for neutral units
-    
-    originX : 0,
-    originY : 0,
-    
-    isReflective : true,
-    isRenderable : true,
-    
-    // inactive
-    active : false,
-    createCooldowns : op.nop,
-    
-    // indestructable
-    dealDamage : op.nop,
-    
-    stats : {
-        hp    : Infinity,
-        move  : 0,
-        power : 0,
-        speed : 0,
-        shots : 0
-    },
-    
-    // Instance 
-    
-    isBoundary : false,
-    
-    
-    
-    init : function initFence(x,y, w,h){
-        this.width = w;
-        this.height = h;
-        this.isBoundary = !!isBoundary;
-        Wall.init.call(this, x,y, w,h);
-        this.position(x,y);
-    },
-    
-    
-    
-    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(0,0,0, 0.25)')
-                .stroke('transparent', 0)
-                .appendTo( parent );
-        
-        return this;
-    },
-    
-    toString : function(){
-        var bb = this.bbox
-        ,   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+')';
-    }
-});
-
-Wall.register('fence', Fence);