From: dsc Date: Fri, 19 Nov 2010 11:45:51 +0000 (-0800) Subject: Adds A* pathing for AI tanks. X-Git-Url: http://git.less.ly:3516/?a=commitdiff_plain;h=ae3b1107003e12d275108942a71f6e9dccb7f2b2;p=tanks.git Adds A* pathing for AI tanks. --- diff --git a/index.php b/index.php index 27ec53b..54d1905 100644 --- a/index.php +++ b/index.php @@ -21,6 +21,7 @@

config

+
diff --git a/src/portal/math/vec.js b/src/portal/math/vec.js index 7debadf..4154495 100644 --- a/src/portal/math/vec.js +++ b/src/portal/math/vec.js @@ -26,12 +26,18 @@ math.Vec = new Y.Class('Vec', [], { return this; }, - add : function add(b){ - return this.setXY(this.x+b.x, this.y+b.y); + add : function add(x,y){ + if ( x instanceof Array ) { + y = x[1]; x = x[0]; + } + return this.setXY(this.x+x, this.y+y); }, - subtract : function subtract(b){ - return this.setXY(this.x-b.x, this.y-b.y); + subtract : function subtract(x,y){ + if ( x instanceof Array ) { + y = x[1]; x = x[0]; + } + return this.setXY(this.x-x, this.y-y); }, scale : function scale(s){ diff --git a/src/portal/util/astar.js b/src/portal/util/astar.js new file mode 100644 index 0000000..78f89ed --- /dev/null +++ b/src/portal/util/astar.js @@ -0,0 +1,140 @@ +/* astar.js http://github.com/bgrins/javascript-astar + MIT License + + Implements the astar search algorithm in javascript using a binary heap + **Requires graph.js** + + Example Usage: + var graph = new Graph([ + [0,0,0,0], + [1,0,0,1], + [1,1,0,0] + ]); + var start = graph.nodes[0][0]; + var end = graph.nodes[1][2]; + astar.search(graph.nodes, start, end); +*/ + +var astar = { + + init: function initAStar(grid) { + // var copy = []; + for(var x = 0, xl = grid.length; x < xl; x++) { + var yl = grid[x].length; + // copy[x] = new Array(yl); + for(var y = 0; y < yl; y++) { + // var node = grid[x][y].clone(); + var node = grid[x][y]; + node.f = 0; + node.g = 0; + node.h = 0; + node.visited = false; + node.closed = false; + node.parent = null; + // copy[x][y] = node; + } + } + // return copy; + }, + + search: function search(grid, start, end, heuristic, ignore) { + astar.init(grid); + heuristic = heuristic || astar.manhattan; + + ignore = ignore || []; + ignore.push(start.value); + ignore.push(end.value); + + var open = new BinaryHeap('f'); + open.push(start); + + while( open.length > 0 ) { + + // Grab the lowest f(x) to process next. Heap keeps this sorted for us. + var current = open.pop(); + + // End case -- result has been found, return the traced path + if(current === end) { + var path = [] + , node = current; + while( node.parent ) { + path.push( node.clone() ); + node = node.parent; + } + return path.reverse(); + } + + // Normal case -- move current from open to closed, process each of its neighbors + current.closed = true; + + var neighbors = astar.neighbors(grid, current); + for(var i=0, il = neighbors.length; i < il; i++) { + var neighbor = neighbors[i]; + + // blocker + if( neighbor.closed || (neighbor.blocked && ignore.indexOf(neighbor.value) === -1) ) + continue; + + // g score is the shortest distance from start to current node, we need to check if + // the path we have arrived at this neighbor is the shortest one we have seen yet. + // We define the distance from a node to it's neighbor as 1, but this could be variable for weighted paths. + var g = current.g + 1; + + if( !neighbor.visited || g < neighbor.g ) { + + // Found an optimal (so far) path to this node. Take score for node to see how good it is. + neighbor.parent = current; + neighbor.h = neighbor.h || heuristic(neighbor, end); + neighbor.g = g; + neighbor.f = g + neighbor.h; + + // New? Add to the set of nodes to process + if ( !neighbor.visited ) { + neighbor.visited = true; + open.push(neighbor); + + // Seen, but since it has been rescored we need to reorder it in the heap + } else + open.rescore(neighbor); + } + } + } + + // No result was found -- empty array signifies failure to find path + return []; + }, + + manhattan: function manhattan(p1, p2) { + // See list of heuristics: http://theory.stanford.edu/~amitp/GameProgramming/Heuristics.html + var d1 = Math.abs(p2.x - p1.x) + , d2 = Math.abs(p2.y - p1.y) ; + return d1 + d2; + }, + + manhattan4: function manhattan(x1,y1, x2,y2) { + // See list of heuristics: http://theory.stanford.edu/~amitp/GameProgramming/Heuristics.html + var d1 = Math.abs(x2 - x1) + , d2 = Math.abs(y2 - y1) ; + return d1 + d2; + }, + + neighbors: function neighbors(grid, node) { + var ret = []; + var x = node.x; + var y = node.y; + + if(grid[x-1] && grid[x-1][y]) { + ret.push(grid[x-1][y]); + } + if(grid[x+1] && grid[x+1][y]) { + ret.push(grid[x+1][y]); + } + if(grid[x] && grid[x][y-1]) { + ret.push(grid[x][y-1]); + } + if(grid[x] && grid[x][y+1]) { + ret.push(grid[x][y+1]); + } + return ret; + } +}; diff --git a/src/portal/util/binaryheap.js b/src/portal/util/binaryheap.js new file mode 100644 index 0000000..cfecb3a --- /dev/null +++ b/src/portal/util/binaryheap.js @@ -0,0 +1,146 @@ +(function(){ + +function BinaryHeap(score){ + if (typeof score === 'string') + this.key = score; + else + this.score = score; +} + +var AP = Array.prototype +, proto = BinaryHeap.prototype = [] +, methods = { + + _push : AP.push, + _pop: AP.pop, + + // Replaced by score function if supplied + score : function score(el){ + return el[this.key]; + }, + + push: function(el) { + // Add the new element to the end of the array. + this._push(el); + + // Allow it to sink down. + this.heapDown(this.length-1); + + return this; + }, + + pop: function() { + // Store the first element so we can return it later. + var result = this[0] + + // Get the element at the end of the array. + , end = this._pop(); + + // If there are any elements left, put the end element at the + // start, and let it bubble up. + if ( this.length > 0 ) { + this[0] = end; + this.heapUp(0); + } + return result; + }, + + remove: function(node) { + var key = this.key + , i = this.indexOf(node) + + // When it is found, the process seen in 'pop' is repeated + // to fill up the hole. + , end = this._pop(); + + if ( i !== this.length-1 ) { + this[i] = end; + var endScore = (key ? end[key] : this.score(end)) + , nodeScore = (key ? node[key] : this.score(node)) ; + if ( endScore < nodeScore ) + this.heapDown(i); + else + this.heapUp(i); + } + return this; + }, + + rescore: function(node) { + this.heapDown(this.indexOf(node)); + return this; + }, + + heapDown: function(n) { + var key = this.key + , el = this[n] + , elScore = (key ? el[key] : this.score(el)) + ; + + // When at 0, an element can not sink any further. + while ( n > 0 ) { + + // Compute the parent element's index and score + var parentN = ((n + 1) >> 1) - 1 + , parent = this[parentN] + , parentScore = (key ? parent[key] : this.score(parent)) + ; + + // Swap the elements if the parent is greater + if ( elScore < parentScore ) { + this[parentN] = el; + this[n] = parent; + + // Update 'n' to continue at the new position. + n = parentN; + + // Found a parent that is less, no need to sink any further. + } else break; + } + }, + + heapUp: function(n) { + var key = this.key + , len = this.length + , el = this[n] + , elScore = this.score(el) + ; + + while( true ) { + var child2N = (n+1) << 1 // Compute children + , child1N = child2N - 1 + , swap = null // Swap index + ; + + if ( child1N < len ) { + var child1 = this[child1N] + , child1Score = (key ? child1[key] : this.score(child1)) + ; + if (child1Score < elScore) + swap = child1N; + } + + if ( child2N < len ) { + var child2 = this[child2N] + , child2Score = (key ? child2[key] : this.score(child2)) + ; + if (child2Score < (swap === null ? elScore : child1Score)) + swap = child2N; + } + + // Perform swap if necessary + if ( swap !== null ) { + this[n] = this[swap]; + this[swap] = el; + n = swap; + + } else break; + } + } +}; + +// Add methods to prototype +for (var k in methods) proto[k] = methods[k]; + +this.BinaryHeap = BinaryHeap; + +})(); \ No newline at end of file diff --git a/src/portal/util/graph.js b/src/portal/util/graph.js new file mode 100644 index 0000000..e85dc73 --- /dev/null +++ b/src/portal/util/graph.js @@ -0,0 +1,16 @@ +function Graph(grid) { + this.grid = grid; + this.nodes = []; + + for (var x = 0; x < grid.length; x++) { + var row = grid[x]; + this.nodes[x] = []; + for (var y = 0; y < row.length; y++) { + var n = new math.Vec(x,y); + n.blocked = !!row[y]; + this.nodes[x].push(n); + } + } +} + + diff --git a/src/portal/util/tree/quadtree.js b/src/portal/util/tree/quadtree.js index d1ac1c8..b7a5afb 100644 --- a/src/portal/util/tree/quadtree.js +++ b/src/portal/util/tree/quadtree.js @@ -34,8 +34,8 @@ Region = new Y.Class('Region', { // Expects caller will have ordered x1 < x2, y1 < y2 overlaps : function overlaps(x1,y1, x2,y2){ - return !( x1 > this.x2 || y1 > this.y2 - || x2 < this.x1 || y2 < this.y1 ); + return !( x1 > this.x2 || y1 > this.y2 + || x2 <= this.x1 || y2 <= this.y1 ); }, toString : function toString(){ @@ -64,8 +64,8 @@ QuadTree = new Y.Class('QuadTree', { // Expects caller will have ordered x1 < x2, y1 < y2 overlaps : function overlaps(x1,y1, x2,y2){ - return !( x1 > this.x2 || y1 > this.y2 - || x2 < this.x1 || y2 < this.y1 ); + return !( x1 > this.x2 || y1 > this.y2 + || x2 <= this.x1 || y2 <= this.y1 ); }, diff --git a/src/tanks/config.js b/src/tanks/config.js index 0588ebb..46503eb 100644 --- a/src/tanks/config.js +++ b/src/tanks/config.js @@ -2,6 +2,7 @@ tanks.config = { pathing : { overlayPathmap : false, + overlayAIPaths : true, traceTrajectories : false }, debug : { diff --git a/src/tanks/game/game.js b/src/tanks/game/game.js index 09a6a6a..ba148a9 100644 --- a/src/tanks/game/game.js +++ b/src/tanks/game/game.js @@ -33,6 +33,7 @@ tanks.Game = new Y.Class('Game', { draw : function draw(){ this.root.draw(); + this.pathmap.removeOverlay(this.viewport); if (tanks.config.pathing.overlayPathmap) this.pathmap.overlay(this.viewport); @@ -50,6 +51,9 @@ tanks.Game = new Y.Class('Game', { SECONDTH = ELAPSED / 1000; SQUARETH = REF_SIZE * SECONDTH + if (!tanks.config.pathing.overlayAIPaths) + this.pathmap.clearPath(); + this.active.invoke('updateCooldowns', NOW); this.active.invoke('act'); diff --git a/src/tanks/main.js b/src/tanks/main.js index 00c78e9..233dc98 100644 --- a/src/tanks/main.js +++ b/src/tanks/main.js @@ -11,13 +11,15 @@ function main(){ LBT = new tanks.Game(); ctx = LBT.level.ctx; - P = LBT.addUnit(new PlayerTank(1), 1,2); - E = LBT.addUnit(new Tank(2), 5,6); + P = LBT.addUnit(new PlayerTank(1), 8,9); + E = LBT.addUnit(new Tank(2), 1,2); setupUI(); // toggleGame(); updateInfo(); + + pm = LBT.pathmap; } @@ -83,6 +85,7 @@ 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=bullets]').val(c.debug.projectiles); @@ -91,7 +94,8 @@ function initConfig(){ function updateConfig(evt){ var p = tanks.config.pathing; - p.overlayPathmap = $('#config [name=pathmap]').attr('checked'); + p.overlayPathmap = $('#config [name=pathmap]').attr('checked'); + p.overlayAIPaths = $('#config [name=aipaths]').attr('checked'); p.traceTrajectories = $('#config [name=trajectories]').attr('checked'); } diff --git a/src/tanks/map/pathmap.js b/src/tanks/map/pathmap.js index bce6aca..e113de3 100644 --- a/src/tanks/map/pathmap.js +++ b/src/tanks/map/pathmap.js @@ -1,25 +1,29 @@ // -*- mode: JavaScript; tab-width: 4; indent-tabs-mode: nil; -*- +(function(){ + PathMap = new Y.Class('PathMap', QuadTree, { + gridSquareSize : REF_SIZE, + gridSquareMidPt : new math.Vec(REF_SIZE/2, REF_SIZE/2), + + - init : function init(game, x1,y1, x2,y2, capacity) { + 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.game = game; + this.level = level; + this.game = level.game; var w = this.width, h = this.height; - // this.walls = { - // top : new math.Line(0,0, w,0), - // bottom : new math.Line(0,h, w,h), - // left : new math.Line(0,0, 0,h), - // right : new math.Line(w,0, w,h) - // }; - this.walls = { - top : new Level.Wall(0,0, w,1), - bottom : new Level.Wall(0,h-1, w,1), - left : new Level.Wall(0,0, 1,h), - right : new Level.Wall(w-1,0, 1,h) + top : new Level.Wall(x1,y1, w,1), + bottom : new Level.Wall(x1,y2-1, w,1), + left : new Level.Wall(x1,y1, 1,h), + right : new Level.Wall(x2-1,y1, 1,h) }; - Y(this.walls).forEach(this.addBlocker, this); + Y(this.walls).forEach(function(wall){ + wall.isBoundary = true; + this.addBlocker(wall); + }, this); }, addBlocker : function addBlocker(obj){ @@ -38,6 +42,8 @@ PathMap = new Y.Class('PathMap', QuadTree, { return obj; }, + + moveBlocked : function moveBlocked(agent, trj, to, bb){ bb = bb || agent.boundingBox; var blockers, blocker, msg @@ -96,10 +102,193 @@ PathMap = new Y.Class('PathMap', QuadTree, { + grid : function grid(){ + if ( !this._grid ) { + var size = this.gridSquareSize + , floor = Math.floor, ceil = Math.ceil + , cols = ceil((this.width-2) /size) + , rows = ceil((this.height-2)/size) + , grid = new Array(cols); + ; + + for (var x=0; x').appendTo(el)[0]; + $(canvas).width(w).height(h); + canvas.width = w; + canvas.height = h; + } + + var ctx = canvas.getContext('2d'); + ctx.lineWidth = 0; + + // Clear the canvas + ctx.beginPath(); + ctx.clearRect(this.x,this.y, w,h); + ctx.closePath(); + + + // Draw blockers + // var s = size/2; + // ctx.fillStyle = 'rgba(255,0,0,0.2)'; + // + // Y(grid).invoke('forEach', function(node){ + // if ( !node.blocked ) return; + // + // var x = node.x*size + s + // , y = node.y*size + s ; + // + // ctx.beginPath(); + // ctx.moveTo( x, y-s ); + // ctx.lineTo( x+s, y ); + // ctx.lineTo( x, y+s ); + // ctx.lineTo( x-s, y ); + // ctx.fill(); + // ctx.closePath(); + // }); + + + // Draw path + var r = size/2 - off; + + function drawStep(p, i){ + var x = p.x*size + off + r + , y = p.y*size + off + r ; + + ctx.beginPath(); + ctx.arc(x,y, r, 0, Math.PI*2, false); + ctx.fill(); + ctx.closePath(); + } + this.drawStep = drawStep; + + ctx.fillStyle = 'rgba(0,0,0,0.2)'; + path.forEach(drawStep); + + + // Draw start + ctx.fillStyle = 'rgba(0,255,0,0.2)'; + drawStep(start, 0); + + // Draw finish + ctx.fillStyle = 'rgba(0,0,255,0.2)'; + drawStep( path.last() ); + + $(canvas).show(); + }, + + clearPath : function clearPath(){ + $('.path', this.game.viewport).hide(); + }, + + + _overlayBG : $('')[0], overlay : function overlay(gridEl){ - var w = this.width *SCALE - , h = this.height *SCALE + var w = this.width-2 + , h = this.height-2 , canvas = $('.overlay', gridEl)[0]; if (!canvas) { @@ -110,7 +299,7 @@ PathMap = new Y.Class('PathMap', QuadTree, { } var ctx = canvas.getContext('2d'); - ctx.scale(SCALE, SCALE); + // ctx.scale(SCALE, SCALE); // Clear the canvas ctx.beginPath(); @@ -124,8 +313,9 @@ PathMap = new Y.Class('PathMap', QuadTree, { // Draw regions - this.reduce(function(acc, value, r, tree){ - if ( acc[r.id] ) return acc; + this.reduce(function(acc, v, r, tree){ + if ( acc[r.id] || v.isBoundary ) + return acc; acc[r.id] = r; @@ -150,3 +340,16 @@ PathMap = new Y.Class('PathMap', QuadTree, { }); +var QT = QuadTree.prototype; + +'set remove removeAll clear' + .split(' ') + .forEach(function(name){ + PathMap.prototype[name] = function(){ + delete this._grid; + return QT[name].apply(this, arguments); + }; + }); + + +})(); diff --git a/src/tanks/thing/player.js b/src/tanks/thing/player.js index c684716..b4a2af0 100644 --- a/src/tanks/thing/player.js +++ b/src/tanks/thing/player.js @@ -59,31 +59,11 @@ PlayerTank = Tank.subclass('PlayerTank', { return this; }, - attack : function attack(){ - var WIGGLE = 4; - - if ( !this.cooldowns.attack.ready ) - return; - - var barrel = this.barrel - , bb = this.boundingBox - , w2 = bb.width/2, h2 = bb.height/2 - , x0 = bb.x1+w2, y0 = bb.y1+h2 - - , theta = barrel.transform.rotate - , sin = Math.sin(theta), cos = Math.cos(theta) - - , x1 = x0 + w2*cos, y1 = y0 + h2*sin - , sz = (barrel.boundingBox.width - w2)/2 + WIGGLE - , blockers = this.game.pathmap.get(x1-sz,y1-sz, x1+sz,y1+sz).remove(this) - ; - - if ( blockers.size() ) return; // console.log('squelch!', blockers); - + attack : function attack(x,y){ this.queue.push({ type : 'fire', - x : x0 + REF_SIZE*cos, - y : y0 + REF_SIZE*sin + x : x, + y : y }); }, diff --git a/src/tanks/thing/tank.js b/src/tanks/thing/tank.js index 0add167..3b9974c 100644 --- a/src/tanks/thing/tank.js +++ b/src/tanks/thing/tank.js @@ -30,6 +30,11 @@ Tank = Thing.subclass('Tank', { init : function init(align){ Thing.init.call(this, align); this.onBulletDeath = this.onBulletDeath.bind(this); + + var self = this; + this.addEventListener('destroy', function(){ + this.game.pathmap.clearPath(); + }); }, cannonReady : function cannonReady(){ @@ -54,18 +59,66 @@ Tank = Thing.subclass('Tank', { this.shoot(b.loc.x, b.loc.y); return this; } - } // Nothing to shoot at? Move toward something - var t = this.nearLike(10000, 'Y.is(Tank, _) && _.align !== '+this.align) - .remove(this) - .shift(); - if (t) { - // console.log(this, 'moving toward', t); - this.move(t.loc.x, t.loc.y); - return this; - } + this.continueMove(); + + return this; + }, + + continueMove : function continueMove(){ + if (!this.currentMove) + this.recalculatePath(); + + var to = this.currentMove; + if (!to) return; + + this.move(to.x, to.y); + if ( this.boundingBox.midpoint().equals(to) ) + this.currentMove = null; + }, + + recalculatePath : function recalculatePath(){ + var t = this.nearLike(10000, 'Y.is(Tank, _) && _.align !== '+this.align).shift() + , pm = this.game.pathmap + ; + + if (!t) return; + + pm.clearPath(); + + // console.log(this, 'moving toward', t); + var end = t.boundingBox.midpoint() + , bb = this.boundingBox + , mid = bb.midpoint() + , start = mid + + , path = this.lastPath = pm.path(start, end) + , 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); }, nearLike : function nearLike(ticks, fn){ @@ -80,11 +133,35 @@ Tank = Thing.subclass('Tank', { }, shoot : function shoot(x,y){ - this.rotateBarrel(x,y); + var WIGGLE = 4 + , xydef = (x !== undefined && y !== undefined) + ; + + if (xydef) this.rotateBarrel(x,y); if ( this.nShots >= this.stats.shots || !this.cooldowns.attack.activate(NOW) ) return null; + var barrel = this.barrel + , bb = this.boundingBox + , w2 = bb.width/2, h2 = bb.height/2 + , x0 = bb.x1+w2, y0 = bb.y1+h2 + + , theta = barrel.transform.rotate + , sin = Math.sin(theta), cos = Math.cos(theta) + + , x1 = x0 + w2*cos, y1 = y0 + h2*sin + , sz = (barrel.boundingBox.width - w2)/2 + WIGGLE + , blockers = this.game.pathmap.get(x1-sz,y1-sz, x1+sz,y1+sz).remove(this) + ; + + if ( blockers.size() ) return; // console.log('squelch!', blockers); + + if (!xydef) { + x = x0 + REF_SIZE*cos; + y = y0 + REF_SIZE*sin; + } + var ProjectileType = this.projectile , p = new ProjectileType(this, x,y); @@ -111,6 +188,38 @@ Tank = Thing.subclass('Tank', { return new math.Vec(x,y); }, + move : function move(x,y){ + var bb = this.boundingBox + , w2 = bb.width/2, h2 = bb.height/2 + ; + return this.moveByAngle( this.angleTo(x,y), x-w2,y-h2 ); + }, + + moveByAngle : function moveByAngle(theta, targetX,targetY){ + var abs = Math.abs + , bb = this.boundingBox, w2 = bb.width/2, h2 = bb.height/2 + , loc = this.loc, mid = bb.midpoint() + , to = loc.moveByAngle(theta, this.stats.move * SQUARETH) + ; + + // Don't overshoot the target + if ( targetX !== undefined && abs(loc.x-to.x) > abs(loc.x-targetX) ) + to.setXY(targetX, to.y); + + if ( targetY !== undefined && abs(loc.y-to.y) > abs(loc.y-targetY) ) + to.setXY(to.x, targetY); + + var nbb = bb.add(to.x,to.y) + , blockers = this.game.pathmap.get(nbb.x1,nbb.y1, nbb.x2,nbb.y2).remove(this) + ; + + if ( !blockers.size() ) + this.game.moveAgentTo(this, to.x, to.y); + + return this; + }, + + /// Rendering Methods /// diff --git a/src/tanks/thing/thing.js b/src/tanks/thing/thing.js index 0177d53..66bbd78 100644 --- a/src/tanks/thing/thing.js +++ b/src/tanks/thing/thing.js @@ -132,22 +132,6 @@ Thing = new Evt.Class('Thing', { return this; }, - move : function move(x,y){ - return this.moveByAngle( this.angleTo(x,y) ); - }, - - moveByAngle : function moveByAngle(theta){ - var to = this.loc.moveByAngle(theta, this.stats.move * SQUARETH) - , bb = this.boundingBox.add(to.x,to.y) - , blockers = this.game.pathmap.get(bb.x1,bb.y1, bb.x2,bb.y2).remove(this) - ; - - if ( !blockers.size() ) - this.game.moveAgentTo(this, to.x, to.y); - - return this; - }, - diff --git a/tanks.php b/tanks.php index ff16226..82bba0a 100644 --- a/tanks.php +++ b/tanks.php @@ -64,6 +64,10 @@ class Tanks { "src/portal/shape/line.js", "src/portal/shape/polygon.js", + "src/portal/util/binaryheap.js", + "src/portal/util/graph.js", + "src/portal/util/astar.js", + "src/portal/util/tree/quadtree.js", "src/portal/loop/eventloop.js",