Improves dodging and blocking AI.
authordsc <david.schoonover@gmail.com>
Wed, 24 Nov 2010 09:25:27 +0000 (01:25 -0800)
committerdsc <david.schoonover@gmail.com>
Wed, 24 Nov 2010 09:25:27 +0000 (01:25 -0800)
src/portal/math/line.js
src/tanks/config.js
src/tanks/map/level.js
src/tanks/map/pathmap.js
src/tanks/map/trajectory.js
src/tanks/thing/tank.js
src/tanks/ui/config.js
src/tanks/ui/main.js
src/tanks/ui/ui.js [new file with mode: 0644]
tanks.php

index 237aaac..3224e41 100644 (file)
@@ -66,8 +66,8 @@ math.Line = math.Vec.subclass('Line', {
     
     pointAtX : function pointAtX(x){ return new math.Vec(x, this.calcY(x)); },
     pointAtY : function pointAtY(y){ return new math.Vec(this.calcX(y), y); },
-    calcY : function calcY(x){ return (x === this.xint ? 0 : x*this.slope + this.yint); },
-    calcX : function calcX(y){ return (y === this.yint ? 0 : y/this.slope + this.xint); },
+    calcY : function calcY(x){ return (x !== this.xint ? x*this.slope + this.yint : 0); },
+    calcX : function calcX(y){ return (y !== this.yint ? y/this.slope + this.xint : 0); },
     
     near : function near(x,y){
         if ( !isFinite(this.slope) )
index fc710fd..6386908 100644 (file)
@@ -2,11 +2,11 @@
 tanks.config = {
     ui : {
         showGridCoords : false,
-        showCountdown  : true
+        showCountdown  : false
     },
     pathing : { 
+        overlayAIPaths    : true,
         overlayPathmap    : false,
-        overlayAIPaths    : false,
         traceTrajectories : false
     }
 };
index 17308f9..a3c6d2b 100644 (file)
@@ -5,9 +5,10 @@ Level = Rect.subclass('Level', {
         Rect.init.call(this, w,h);
         this.canvas.remove();
         
-        this.game    = game;
-        this.walls   = Y([]);
-        this.pathmap = new PathMap(this, 0,0, w, h, CAPACITY);
+        this.game     = game;
+        this.walls    = Y([]);
+        this.allWalls = Y([]);
+        this.pathmap  = new PathMap(this, 0,0, w, h, CAPACITY);
         
         game.addEventListener('ready', this.setup.bind(this));
     },
@@ -28,8 +29,8 @@ Level = Rect.subclass('Level', {
         
         E = 
         game.addUnit(new Tank(2), 0,1);
-        game.addUnit(new Tank(2), 1,0);
-        game.addUnit(new Tank(2), 8,1);
+        // game.addUnit(new Tank(2), 1,0);
+        // game.addUnit(new Tank(2), 8,1);
     },
     
     addWall : function addWall(x,y, w,h, isBoundary){
@@ -37,8 +38,9 @@ Level = Rect.subclass('Level', {
         this.pathmap.addBlocker(wall);
         this.append( wall.render(this).shape );
         
-        if (!isBoundary)
-            this.walls.push(wall);
+        this.allWalls.push(wall);
+        if (!isBoundary) this.walls.push(wall);
+        
         return wall;
     },
     
index 5ab4c17..5cdf075 100644 (file)
@@ -10,9 +10,11 @@ PathMap = QuadTree.subclass('PathMap', {
     init : function init(level, x1,y1, x2,y2, capacity) {
         x1 -= 1; y1 -= 1; x2 += 1; y2 += 1;
         QuadTree.init.call(this, x1,y1, x2,y2, capacity);
-        this.level = level;
-        this.game  = level.game;
-        this.walls = level.walls;
+        
+        this.level    = level;
+        this.game     = level.game;
+        this.walls    = level.walls;
+        this.allWalls = level.allWalls;
         
         this.game.addEventListener('ready', this.setup.bind(this, x1,y1, x2,y2));
     },
index 038864c..bc39f3d 100644 (file)
@@ -5,7 +5,7 @@ Trajectory = math.Line.subclass('Trajectory', {
     
     
     init : function initTrajectory(owner, x1,y1, x2,y2, tdist){
-        Y.bindAll(this, 'cmp', 'closer');
+        Y.bindAll(this, 'compare', 'closer', 'intersects');
         
         this.owner   = owner;
         this.game    = owner.game;
@@ -52,13 +52,26 @@ Trajectory = math.Line.subclass('Trajectory', {
         return this;
     },
     
+    
+    intersects : function intersects(x,y){
+        var o = x;
+        if (o instanceof Thing)
+            return o.boundingBox.intersects(this);
+        
+        if (o instanceof Loc.Rect)
+            return o.intersects(this);
+        
+        return math.Line.prototype.intersects.call(this, x,y);
+    },
+    
+    
     /**
      * Compares how distant in the future two objects are on this trajectory.
      * Objects that have been passed are always further away than those in the future,
      * but otherwise the comparison is performed by absolute distance.
      * @returns -1 if a closer b, 1 if a further b, 0 if a same as b
      */
-    cmp : function cmp(a, b){
+    compare : function compare(a, b){
         if (a instanceof Thing) a = a.midpoint;
         if (b instanceof Thing) b = b.midpoint;
         
@@ -90,11 +103,11 @@ Trajectory = math.Line.subclass('Trajectory', {
     },
     
     closer : function closer(o1, o2){
-        return this.cmp(o1,o2) === 1 ? o2 : o1;
+        return this.compare(o1,o2) === 1 ? o2 : o1;
     },
     
     closest : function closest(o1, o2){
-        return new Y(arguments).sort( this.cmp ).shift();
+        return new Y(arguments).sort( this.compare ).shift();
     },
     
     comesWithin : function comesWithin(pt, w,h){
@@ -121,10 +134,11 @@ Trajectory = math.Line.subclass('Trajectory', {
     
     pathBlocked : function pathBlocked(obj, ignore){
         var blockers =
-            this.pathmap.walls
-            .concat( this.game.units )
-            .apply('remove', Y(ignore || []).concat([this.owner]).end())
-            .sort( this.cmp )
+                this.pathmap.walls
+                .concat( this.game.units )
+                .apply('remove', Y(ignore || []).concat([this.owner]).end())
+                .filter( this.intersects )
+                .sort( this.compare )
         
         ,   blocker = blockers.shift()
         ;
index 42e7570..543d456 100644 (file)
@@ -1,3 +1,9 @@
+(function(){
+
+var BULLET_MOVE_PER_FRAME = MS_PER_FRAME * Bullet.prototype.stats.move*REF_SIZE/1000
+,   isBullet = Y.is(Bullet)
+;
+
 
 Tank = Thing.subclass('Tank', {
     projectile : Bullet,
@@ -46,7 +52,7 @@ Tank = Thing.subclass('Tank', {
     
     
     
-    act : function act(){
+    act : function act(allotment){
         
         // Check to see if we should obey our last decision, and not recalc
         if ( this.forceCurrentMove && this.forceCurrentMove() && this.currentMoveLimit > NOW ) {
@@ -56,42 +62,33 @@ Tank = Thing.subclass('Tank', {
             this.forceCurrentMove = false;
         
         
-        // Are we ready to fire?
+        // Try to shoot down nearby bullets
         if ( this.ableToShoot() ) {
-            
-            // Try to shoot down nearby bullets
-            var bs = this.willCollide( this.findNearLike(16, Y.is(Bullet)) );
+            var bs = this.willCollide( this.findNearLike(25, isBullet) );
+            // console.log('['+TICKS+':'+this.id, this, '] Shoot down bullets?', bs.size() && bs);
             if ( bs.size() ) {
                 var b = this.closestOf(bs);
-                
-                // console.log('Incoming! Shoot it down!', b);
+                // console.log('  --> Incoming! Shoot it down!', b);
                 this.shoot(b.loc.x, b.loc.y);
                 return this;
             }
-            
-            // Dodge incoming bullet
-            var bs = this.willCollide( this.findNearLike(50, Y.is(Bullet)), 10 );
-            if ( bs.size() ) {
-                var b   = this.closestOf(bs), bs = [b]
-                ,   mid = this.midpoint
-                ,   trj = b.trajectory.tangent(mid)
-                
-                ,   lvl = this.game.level, w = lvl.width, h = lvl.height
-                ,   x   = (mid.x > w/2 ? w : 0)
-                ,   y   = (mid.y > h/2 ? h : 0)
-                ,   to  = this.currentMove = trj.near(x,y);
-                
-                this.forceCurrentMove = this.willCollide.bind(this, bs, 10);
-                this.currentMoveLimit = NOW + 750;
-                
-                this.move(to.x, to.y);
-                return this;
-            }
-            
-            // Try to blow up nearby tanks
-            var t = this.findNearEnemies(66, true).shift();
+        }
+        
+        // Dodge incoming bullet
+        var bs = this.willCollide(this.findNearLike(71, isBullet), 5);
+        // console.log('['+TICKS+':'+this.id, this, '] Dodge bullets?', bs.size() && bs);
+        if ( bs.size() ) {
+            var bullet = this.closestOf(bs);
+            this.moveAwayFrom(bullet);
+            return this;
+        }
+        
+        // Try to blow up nearby tanks
+        if ( this.ableToShoot() ) {
+            var t = this.findNearEnemies(71, true).shift();
+            // console.log('['+TICKS+':'+this.id, this, '] Shoot at enemies?', t);
             if (t) {
-                // console.log('I gotcha!', t);
+                // console.log('  --> I gotcha!', t);
                 this.shoot(t.loc.x, t.loc.y);
                 return this;
             }
@@ -99,100 +96,32 @@ Tank = Thing.subclass('Tank', {
         
         // Nothing to shoot at? Move toward something
         this.continueMove();
-        
         return this;
     },
     
-    ableToShoot : function ableToShoot(){
-        return this.nShots < this.stats.shots && this.cooldowns.attack.ready;
-    },
     
-    closestOf : function closestOf(agents){
-        if ( !(agents && agents.size()) )
-            return null;
-        
-        var manhattan = math.Vec.manhattan
-        ,   bb = this.boundingBox, mid = this.midpoint ;
+    /**
+     * TODO: Get this.currentMove etc out of the calc methods.
+     */
+    moveAwayFrom : function moveAwayFrom(agent){
+        var mid = this.midpoint
+        ,   trj = agent.trajectory.tangent(mid)
         
-        agents.sort(function(a,b){
-            return Y.op.cmp(
-                manhattan(a.loc,mid),
-                manhattan(b.loc,mid) );
-        });
+        ,   wall = this.closestOf(this.game.pathmap.allWalls)
+        ,   wmid = wall.midpoint
         
-        return agents.attr(0);
-    },
-    
-    willCollide : function willCollide(bullets, wiggle){
-        wiggle = wiggle || 0;
-        var tank = this, bb = this.boundingBox
-        ,   w = (bb.width+wiggle)/2, h = (bb.height+wiggle)/2
-        ;
-        return bullets.filter(function(b){
-            var trj = b.trajectory;
-            return ( !b.dead
-                &&  trj.comesWithin(tank, w,h)
-                && !trj.pathBlocked(tank) );
-        });
-    },
-    
-    continueMove : function continueMove(){
-        if ( !this.currentMove || this.currentMoveLimit <= NOW ){
-            var t = this.findNearEnemies(10000).shift();
-            if (t) this.calculatePath(t.midpoint);
-        }
+        ,   lvl = this.game.level.boundingBox, w = lvl.width, h = lvl.height
+        ,   x   = ((mid.x - wmid.x) > 0 ? w : 0)
+        ,   y   = ((mid.y - wmid.y) > 0 ? h : 0)
+        ,   to  = this.currentMove = trj.near(x,y);
         
-        var to = this.currentMove;
-        if (!to) return;
+        this.forceCurrentMove = this.willCollide.bind(this, [agent], 5);
+        this.currentMoveLimit = NOW + 1000;
         
         this.move(to.x, to.y);
-        if ( this.midpoint.equals(to) ) {
-            this.currentMove = null;
-            this.forceCurrentMove = false;
-            this.currentMoveLimit = -1;
-        }
-    },
-    
-    calculatePath : function calculatePath(end){
-        if (!end) return;
-        
-        var pm = this.game.pathmap;
-        
-        // this.forceCurrentMove = Y.op.K(true);
-        // this.currentMoveLimit = NOW + 750;
-        this.forceCurrentMove = false;
-        this.currentMoveLimit = -1;
-        
-        // console.log(this, 'moving toward', t);
-        var bb = this.boundingBox
-        ,   mid = this.midpoint
-        ,   start = mid
-        
-        ,   path  = this.lastPath = pm.path(start, end, this.id)
-        ,   to = this.currentMove = path[0]
-        ;
-        
-        // VVV We only really need this code if we're going to recalculate before we reach the currentMove
-        
-        // we may need to move closer to start if we occupy multiple grid-squares
-        // var tosq = pm.vec2Square(to), manhattan = pm.manhattan
-        // ,   tl = pm.vec2Square(bb.x1,bb.y1), tr = pm.vec2Square(bb.x2,bb.y1)
-        // ,   bl = pm.vec2Square(bb.x1,bb.y2), br = pm.vec2Square(bb.x2,bb.y2)
-        // ,   straddles = [tl, tr, bl, br]
-        // ;
-        // 
-        // 
-        // if ( !tl.equals(br) ) {
-        //     tl.to = pm.vec2Square(bb.x1,bb.y1); tr.to = pm.vec2Square(mid.x,bb.y1);
-        //     bl.to = pm.vec2Square(bb.x1,mid.y); br.to = pm.vec2Square(mid.x,mid.y);
-        //     straddles.sort(function(a,b){
-        //         return Y.op.cmp(manhattan(a,tosq), manhattan(b,tosq));
-        //     });
-        //     to = pm.square2Vec(straddles[0].to).add(pm.gridSquareMidPt);
-        //     path.unshift(to);
-        // }
         
-        // console.log('start:', start, 'straddles:', straddles.map(pm.square2Vec.bind(pm)), 'end:', end, 'next:', to);
+        // console.log('  --> Dodge', agent, 'away from', wall, 'to', to);
+        return to;
     },
     
     find : function find(x1,y1, x2,y2){
@@ -201,27 +130,66 @@ Tank = Thing.subclass('Tank', {
     
     findNearLike : function findNearLike(ticks, fn){
         fn = (fn || Y.op.K(true)).toFunction();
-        var bMovePerTick = MS_PER_FRAME * Bullet.prototype.stats.move*REF_SIZE/1000
-        ,   within = bMovePerTick*ticks
+        
+        var within = BULLET_MOVE_PER_FRAME*ticks
         ,   bb = this.boundingBox
         ,   x1 = bb.x1 - within, y1 = bb.y1 - within
         ,   x2 = bb.x2 + within, y2 = bb.y2 + within
         ;
+        
         return this.game.pathmap.get(x1,y1, x2,y2).filter(fn, this);
     },
     
-    findNearEnemies : function findNearEnemies(ticks, wallObs){
-        var bb  = this.boundingBox
-        ,   mid = this.midpoint
-        ,   pm  = this.game.pathmap ;
+    findNearEnemies : function findNearEnemies(ticks, needLineOfSight){
         return this.findNearLike(ticks, function(agent){
             var am = agent.midpoint;
             return ( agent.align !== this.align
                   && Y.is(Tank, agent)
-                  && !(wallObs && new Trajectory(this,mid,am).pathBlocked(agent)) );
+                  && !(needLineOfSight 
+                        && new Trajectory(this,this.midpoint,am).pathBlocked(agent))
+                );
+        });
+    },
+    
+    closestOf : function closestOf(agents){
+        if ( !(agents && agents.size()) )
+            return null;
+        
+        var manhattan = math.Vec.manhattan
+        ,   bb = this.boundingBox, mid = this.midpoint ;
+        
+        agents.sort(function(a,b){
+            return Y.op.cmp(
+                manhattan(a.midpoint,mid),
+                manhattan(b.midpoint,mid) ); // FIXME: midpoint-to-midpoint is wrong -- should use pt on closest boundary-side of object
+        });
+        
+        return agents.attr(0);
+    },
+    
+    willCollide : function willCollide(bullets, wiggle){
+        bullets = ( !Y.isArray(bullets) ? [bullets] : bullets );
+        wiggle  = wiggle || 0;
+        
+        var tank = this, bb = this.boundingBox
+        ,   w = bb.width+wiggle, h = bb.height+wiggle ;
+        // ,   w = (bb.width+wiggle)/2, h = (bb.height+wiggle)/2 ;
+        
+        return bullets.filter(function(b){
+            var trj = b.trajectory;
+            return ( !b.dead
+                &&  trj.comesWithin(tank, w,h)
+                && !trj.pathBlocked(tank) );
         });
     },
     
+    /**
+     * Fires this agent's cannon. If a target location is omitted, the shot
+     * will be fired in the direction of the tank's current barrel rotation.
+     * 
+     * @param {Number} [x] Target X coordinate.
+     * @param {Number} [y] Target Y coordinate.
+     */
     shoot : function shoot(x,y){
         var WIGGLE = 4 // additional space which must be clear in front of the barrel
         ,   xydef = (x !== undefined && y !== undefined)
@@ -246,7 +214,8 @@ Tank = Thing.subclass('Tank', {
         ,   blockers = this.game.pathmap.get(x1-sz,y1-sz, x1+sz,y1+sz).remove(this)
         ;
         
-        if ( blockers.size() ) return; // console.log('squelch!', blockers);
+        if ( blockers.size() )
+            return null; // console.log('squelch!', blockers);
         
         if (!xydef) {
             x = x0 + REF_SIZE*cos;
@@ -263,21 +232,9 @@ Tank = Thing.subclass('Tank', {
         return p;
     },
     
-    getTurretLoc : function getTurretLoc(){
-        var loc    = this.loc, mid = this.midpoint
-        ,   barrel = this.barrel
-        
-        ,   theta  = barrel.transform.rotate
-        ,   sin = Math.sin(theta),  cos = Math.cos(theta)
-        ,   len = barrel.boundingBox.width
-        
-        ,   x = mid.x + len*cos
-        ,   y = mid.y + len*sin
-        ;
-        // console.log('getTurretLoc()', 'loc:', loc, 'bbb.(x2,y2):', [bbb.x2,bbb.y2], '(x,y):', [x,y]);
-        return new math.Vec(x,y);
-    },
-    
+    /**
+     * @return {this}
+     */
     move : function move(x,y){
         var bb = this.boundingBox
         ,   w2 = bb.width/2, h2 = bb.height/2
@@ -287,9 +244,7 @@ Tank = Thing.subclass('Tank', {
         return this.moveByAngle( this.angleTo(x,y), x2,y2 );
     },
     
-    /**
-     * @protected This method does not update this.trajectory -- call this.move(x,y) instead.
-     */
+    /** @protected This method does not update this.trajectory -- call this.move(x,y) instead. */
     moveByAngle : function moveByAngle(theta, targetX,targetY){
         var abs = Math.abs
         ,   bb = this.boundingBox, w2 = bb.width/2, h2 = bb.height/2
@@ -315,6 +270,84 @@ Tank = Thing.subclass('Tank', {
     },
     
     
+    continueMove : function continueMove(){
+        if ( !this.currentMove || this.currentMoveLimit <= NOW ){
+            var t = this.findNearEnemies(10000).shift();
+            if (t) this.calculatePath(t.midpoint);
+        }
+        
+        var to = this.currentMove;
+        if (!to) return;
+        
+        this.move(to.x, to.y);
+        if ( this.midpoint.equals(to) ) {
+            this.currentMove = null;
+            this.forceCurrentMove = false;
+            this.currentMoveLimit = -1;
+        }
+    },
+    
+    calculatePath : function calculatePath(end){
+        if (!end) return;
+        
+        // this.forceCurrentMove = Y.op.K(true);
+        // this.currentMoveLimit = NOW + 750;
+        this.forceCurrentMove = false;
+        this.currentMoveLimit = -1;
+        
+        // console.log(this, 'moving toward', t);
+        var pm = this.game.pathmap
+        ,   mid = this.midpoint, start = mid
+        
+        ,   path  = this.lastPath = pm.path(start, end, this.id)
+        ,   to = this.currentMove = path[0]
+        ;
+        
+        // VVV We only really need this code if we're going to recalculate before we reach the currentMove
+        
+        // we may need to move closer to start if we occupy multiple grid-squares
+        // var tosq = pm.vec2Square(to), manhattan = math.Vec.manhattan
+        // ,   bb = this.boundingBox
+        // ,   tl = pm.vec2Square(bb.x1,bb.y1), tr = pm.vec2Square(bb.x2,bb.y1)
+        // ,   bl = pm.vec2Square(bb.x1,bb.y2), br = pm.vec2Square(bb.x2,bb.y2)
+        // ,   straddles = [tl, tr, bl, br]
+        // ;
+        // 
+        // 
+        // if ( !tl.equals(br) ) {
+        //     tl.to = pm.vec2Square(bb.x1,bb.y1); tr.to = pm.vec2Square(mid.x,bb.y1);
+        //     bl.to = pm.vec2Square(bb.x1,mid.y); br.to = pm.vec2Square(mid.x,mid.y);
+        //     straddles.sort(function(a,b){
+        //         return Y.op.cmp(manhattan(a,tosq), manhattan(b,tosq));
+        //     });
+        //     to = pm.square2Vec(straddles[0].to).add(pm.gridSquareMidPt);
+        //     path.unshift(to);
+        // }
+        
+        // console.log('start:', start, 'straddles:', straddles.map(pm.square2Vec.bind(pm)), 'end:', end, 'next:', to);
+    },
+    
+    
+    ableToShoot : function ableToShoot(){
+        return this.nShots < this.stats.shots && this.cooldowns.attack.ready;
+    },
+    
+    getTurretLoc : function getTurretLoc(){
+        var loc    = this.loc, mid = this.midpoint
+        ,   barrel = this.barrel
+        
+        ,   theta  = barrel.transform.rotate
+        ,   sin = Math.sin(theta),  cos = Math.cos(theta)
+        ,   len = barrel.boundingBox.width
+        
+        ,   x = mid.x + len*cos
+        ,   y = mid.y + len*sin
+        ;
+        // console.log('getTurretLoc()', 'loc:', loc, 'bbb.(x2,y2):', [bbb.x2,bbb.y2], '(x,y):', [x,y]);
+        return new math.Vec(x,y);
+    },
+    
+    
     
     
     /// Rendering Methods ///
@@ -386,3 +419,6 @@ Tank = Thing.subclass('Tank', {
     }
     
 });
+
+
+})();
\ No newline at end of file
index d74e090..479fc9d 100644 (file)
@@ -1,8 +1,60 @@
 (function(){
 
-function splitCamel(s){
+var upperPat = /^[A-Z]+$/
+,   symbolPat = /^[^a-zA-Z]+$/
+;
+
+Y.YString.prototype.splitCamel =
+    function splitCamel(s){
+        return this.reduce(
+                function(acc, ch, i){
+                    return acc + (
+                        symbolPat.test(ch) ? '' : 
+                        (upperPat.test(ch) ? ' '+ch : ch) );
+                }, '')
+            .split(' ');
+    };
+
+
+// TODO
+// ====
+// - store at tanks.config.values, but clone at start to t.c.defaults
+// - need Config class to run triggers on changes
+// - button to clear saved config
+// 
+// init
+// - For each item in tanks.config (recursive):
+// - Create a UI box based on type, with a name based on its dotted path
+// - Check cookies -- fill it with the value from cookies or default
+// 
+// update
+// - For each config element:
+// - Extract value, typecast based on default's type
+// - If different from default, set cookie
+// - Update tanks.config.values
+
+var ns = tanks.ui.config = {}
+,   c = tanks.config
+,   p = c.pathing
+;
+
+ns.init = function initConfigUi(){
+    $('#config [name=pathmap]').attr('checked', p.overlayPathmap);
+    $('#config [name=aipaths]').attr('checked', p.overlayAIPaths);
+    $('#config [name=trajectories]').attr('checked', p.traceTrajectories);
     
+    $('#config [name=gridCoords]').attr('checked', c.ui.showGridCoords);
+    $('#viewport .layer.grid')[(c.ui.showGridCoords ? 'add' : 'remove')+'Class']('showGridCoords');
+};
+
+ns.update = function updateConfigUi(evt){
+    p.overlayPathmap    = $('#config [name=pathmap]').attr('checked');
+    p.overlayAIPaths    = $('#config [name=aipaths]').attr('checked');
+    p.traceTrajectories = $('#config [name=trajectories]').attr('checked');
     
-}
+    c.ui.showGridCoords = $('#config [name=gridCoords]').attr('checked');
+    $('#viewport .layer.grid')[(c.ui.showGridCoords ? 'add' : 'remove')+'Class']('showGridCoords');
+};
+
 
 })();
index d966db4..e6c3598 100644 (file)
@@ -1,5 +1,3 @@
-tanks.ui = {};
-
 (function(){
 
 jQuery(main);
@@ -37,7 +35,7 @@ function main(){
     $('#debug').bind('mousedown', Y.op.K(false));
     
     // Update config when changed
-    $('#debug input').bind('change', updateConfig);
+    $('#debug input').bind('change', tanks.ui.config.update);
     
     
     // Create #pause box
@@ -94,7 +92,7 @@ function setupUI(){
     
     if (!qkv.ai) $('#welcome').show();
     LBT.loop.spark = new FpsSparkline(LBT.loop, '.fps-sparkline', 0,0);
-    initConfig();
+    tanks.ui.config.init();
     LBT.root.draw();
     
     // Start button (click or return key)
@@ -167,54 +165,6 @@ function gameover(headline, button, body, evt){
 gameover = Y(gameover).curry();
 
 
-function initConfig(){
-    var c = tanks.config
-    ,   p = c.pathing
-    ;
-    
-    $('#config [name=pathmap]').attr('checked', p.overlayPathmap);
-    $('#config [name=aipaths]').attr('checked', p.overlayAIPaths);
-    $('#config [name=trajectories]').attr('checked', p.traceTrajectories);
-    
-    $('#config [name=gridCoords]').attr('checked', c.ui.showGridCoords);
-    $('#viewport .layer.grid')[(c.ui.showGridCoords ? 'add' : 'remove')+'Class']('showGridCoords');
-}
-
-function updateConfig(evt){
-    var c = tanks.config
-    ,   p = c.pathing
-    ;
-    
-    p.overlayPathmap    = $('#config [name=pathmap]').attr('checked');
-    p.overlayAIPaths    = $('#config [name=aipaths]').attr('checked');
-    p.traceTrajectories = $('#config [name=trajectories]').attr('checked');
-    
-    c.ui.showGridCoords = $('#config [name=gridCoords]').attr('checked');
-    $('#viewport .layer.grid')[(c.ui.showGridCoords ? 'add' : 'remove')+'Class']('showGridCoords');
-}
-
-// Update performance info periodically
-function updateInfo(){
-    var loop     = LBT.loop
-    ,   fps      = loop.fps()
-    ,   n_active = LBT.active.size()
-    ,   n_units  = LBT.units.size()
-    ,   n_projs  = LBT.bullets.size()
-    ;
-    
-    $('#info #state').text( loop.running ? 'Running!' : ('Paused (tick '+TICKS+')') );
-    $('#info [name=fps]').val( fps.toFixed(2) + " / " + loop.framerate );
-    $('#info [name=frame]').val( loop.frametime().toFixed(3)+" ms" );
-    
-    $('#info [name=active]').val( n_active );
-    $('#info [name=units]').val( n_units );
-    $('#info [name=bullets]').val( n_projs );
-    
-    loop.spark.drawTimes();
-    
-    return false;
-}
-
 function countdownToStart(n, fn){
     var el
     ,   showFor  = 750
@@ -272,6 +222,27 @@ function readyToStart(){
     toggleGame();
 }
 
+// Update performance info periodically
+function updateInfo(){
+    var loop     = LBT.loop
+    ,   fps      = loop.fps()
+    ,   n_active = LBT.active.size()
+    ,   n_units  = LBT.units.size()
+    ,   n_projs  = LBT.bullets.size()
+    ;
+    
+    $('#info #state').text( loop.running ? 'Running!' : ('Paused (tick '+TICKS+')') );
+    $('#info [name=fps]').val( fps.toFixed(2) + " / " + loop.framerate );
+    $('#info [name=frame]').val( loop.frametime().toFixed(3)+" ms" );
+    
+    $('#info [name=active]').val( n_active );
+    $('#info [name=units]').val( n_units );
+    $('#info [name=bullets]').val( n_projs );
+    
+    loop.spark.drawTimes();
+    
+    return false;
+}
 
 
 
diff --git a/src/tanks/ui/ui.js b/src/tanks/ui/ui.js
new file mode 100644 (file)
index 0000000..a1ca700
--- /dev/null
@@ -0,0 +1 @@
+tanks.ui = {};
index 40693fc..2a137d9 100644 (file)
--- a/tanks.php
+++ b/tanks.php
@@ -33,7 +33,9 @@ class Tanks {
         "src/tanks/map/level.js",
         "src/tanks/map/pathmap.js",
         
+        "src/tanks/ui/ui.js",
         "src/tanks/ui/grid.js",
+        "src/tanks/ui/config.js",
         
         "src/tanks/game.js"
     );