From 64a166b9851909343fd5f2e98fd498fa1a3aaa86 Mon Sep 17 00:00:00 2001 From: dsc Date: Sun, 19 Dec 2010 21:47:50 -0800 Subject: [PATCH] Methods now accept Rect-like objects. --- src/tanks/map/pathmap.cjs | 198 ++++++++++++++++++++++++++------------------- 1 files changed, 113 insertions(+), 85 deletions(-) diff --git a/src/tanks/map/pathmap.cjs b/src/tanks/map/pathmap.cjs index b1957a9..ac0fb83 100644 --- a/src/tanks/map/pathmap.cjs +++ b/src/tanks/map/pathmap.cjs @@ -14,7 +14,13 @@ var Y = require('Y').Y , - +/** + * A QuadTree which aids in pathing for AI. + * - QuadTree methods which took rect coords (x1,y1, x2,y2) will accept + * anything Rect-like (having properties x1,y1, x2,y2). + * - Pathing methods will account for the unit's size when calculating paths, + * intersections, blocking, and similar tasks. + */ PathMap = exports['PathMap'] = QuadTree.subclass('PathMap', { @@ -36,6 +42,10 @@ QuadTree.subclass('PathMap', { this.game.addEventListener('ready', this.setup.bind(this, x1,y1, x2,y2)); }, + /** + * Adds boundary walls and notifies the level. + * TODO: I hate this. The level shouldn't have shit to do with pathing. + */ setup : function setup(x1,y1, x2,y2){ var w = this.width, h = this.height , level = this.level @@ -51,12 +61,6 @@ QuadTree.subclass('PathMap', { }, wallObstructs : function wallObstructs(line){ - // var walls = this.walls; - // for (var i=0, L=walls.length, w=walls[i]; i 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 }; - }, - + // 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 }; + // }, + // TODO: Fold A* into this class. + // TODO: Deal with bbox /** * @protected Creates a pathing grid suitable for A* search. - * TODO: Fold A* into this class. + * @param {Thing|BoundingBox} bbox Denotes the bounds of object under pathing: + * - An object with a `boundingBox` property + * - A BoundingBox-like object (has properties relOrigin, width, height) */ - grid : function grid(){ + grid : function grid(bbox){ if ( !this._grid ) { var size = this.gridSquare , floor = Math.floor, ceil = Math.ceil @@ -168,6 +175,7 @@ QuadTree.subclass('PathMap', { } } + // cache result this._grid = this.reduce(function(grid, v, r){ // Ignore bullets and boundary-walls in pathing @@ -195,19 +203,7 @@ QuadTree.subclass('PathMap', { return this._grid; }, - vec2Square : function vec2Square(x,y){ - if (x instanceof Array){ y = x.y; x = x.x; } - var floor = Math.floor, size = this.gridSquare; - return new Vec(floor(x/size), floor(y/size)); - }, - - square2Vec : function square2Vec(x,y){ - if (x instanceof Array){ y = x.y; x = x.x; } - var floor = Math.floor, size = this.gridSquare; - return new Vec(floor(x)*size, floor(y)*size); - }, - - path : function path(start, end, agent){ + path : function path(agent, start, end){ return this.gridPath(start, end, agent) .invoke('scale', this.gridSquare) .invoke('add', this.gridSquareMid) @@ -218,7 +214,7 @@ QuadTree.subclass('PathMap', { * @protected * Generates grid-sized path from start to end. */ - gridPath : function gridPath(start, end, agent){ + _gridPath : function gridPath(agent, start, end){ var size = this.gridSquare, floor = Math.floor , grid = this.grid() @@ -231,20 +227,52 @@ QuadTree.subclass('PathMap', { , endN = grid[endX][endY] ; return Y(astar.search(grid, startN, endN)) - } + }, + + vec2Square : function vec2Square(x,y){ + if (x instanceof Array){ y = x.y; x = x.x; } + var floor = Math.floor, size = this.gridSquare; + return new Vec(floor(x/size), floor(y/size)); + }, + + square2Vec : function square2Vec(x,y){ + if (x instanceof Array){ y = x.y; x = x.x; } + var floor = Math.floor, size = this.gridSquare; + return new Vec(floor(x)*size, floor(y)*size); + }, + + }); +// Wrap QuadTree Methods to accept Rect-like objects +'overlaps get set removeAll leaves collect' + .split(' ') + .forEach(function(name){ + var method = PathMap.fn[name] || QuadTree.fn[name]; + PathMap.fn[name] = function(o){ + var args = arguments, xy; + if ('x1' in o){ + xy = [o.x1,o.y1, o.x2,o.y2]; + args = (args.length === 1) ? xy : xy.concat(Y(args,1)); + } + return method.apply(this, args); + }; + }); +// Invalidate grid cache +// TODO: Goes away when I fix A* 'set remove removeAll clear' .split(' ') .forEach(function(name){ + var method = PathMap.fn[name] || QuadTree.fn[name]; PathMap.fn[name] = function(){ delete this._grid; - return QuadTree.fn[name].apply(this, arguments); + return method.apply(this, arguments); }; }); +// Stay sync'd with config config.updateOnChange( ['pathing.gridSquare', 'pathing.gridSquareMid'], PathMap.fn); -- 1.7.0.4