From ad29ced3f39d153d165dde40b3c94f6bed0042b6 Mon Sep 17 00:00:00 2001 From: dsc Date: Mon, 10 Jan 2011 19:25:26 -0800 Subject: [PATCH] Checkpoint in pathing refactor. --- data/config.yaml | 27 ++-- data/types/levels.yaml | 9 +- pavement.py | 2 +- src/ezl/layer/layer.cjs | 17 +- src/tanks/game.cjs | 17 +- src/tanks/globals.js | 14 -- src/tanks/map/index.cjs | 2 +- src/tanks/map/level.cjs | 4 +- src/tanks/map/map.cjs | 84 -------- src/tanks/map/pathing/index.cjs | 9 +- src/tanks/map/pathing/map-blockers.cjs | 70 +++++++ src/tanks/map/pathing/map-pathing.cjs | 318 ++++++++++++++++++++++++++++ src/tanks/map/pathing/map-searching.cjs | 109 ++++++++++ src/tanks/map/pathing/map.cjs | 57 +++++ src/tanks/map/pathing/pathmap.cjs | 343 ------------------------------- src/tanks/map/pathing/trajectory.cjs | 8 +- src/tanks/map/pathing/traversal.cjs | 4 +- src/tanks/thing/tank.cjs | 100 +--------- src/tanks/thing/thing.cjs | 4 +- src/tanks/ui/pathmapui.cjs | 22 +- src/tanks/util/utils.cjs | 2 +- www/data | 1 + 22 files changed, 630 insertions(+), 593 deletions(-) delete mode 100644 src/tanks/map/map.cjs create mode 100644 src/tanks/map/pathing/map-blockers.cjs create mode 100644 src/tanks/map/pathing/map-pathing.cjs create mode 100644 src/tanks/map/pathing/map-searching.cjs create mode 100644 src/tanks/map/pathing/map.cjs delete mode 100644 src/tanks/map/pathing/pathmap.cjs create mode 120000 www/data diff --git a/data/config.yaml b/data/config.yaml index a52c410..9c01445 100644 --- a/data/config.yaml +++ b/data/config.yaml @@ -1,20 +1,21 @@ game: - timeDilation : 1.0 - gameoverDelay : 1000 + timeDilation : 1.0 + # zoom : 1.0 + gameoverDelay : 1000 debug: - showFpsGraph : false + showFpsGraph : false # createGridTable : false # showGridCoords : false map: - refSize : &ref_size 50 - minSplitSize : 12.5 + refSize : &ref_size 50 + minSplitSize : 12.5 pathing: - pathSquare : *ref_size - overlayAiPaths : false - overlayPathmap : false - traceTrajectories : false + pathSquare : *ref_size + overlayAiPaths : false + overlayPathmap : false + traceTrajectories : false ui: - createGridCanvas : true - overlayOnPause : true - showAttackCooldown : false - showCountdown : true + createGridCanvas : true + overlayOnPause : true + showAttackCooldown : false + showCountdown : true diff --git a/data/types/levels.yaml b/data/types/levels.yaml index 3a6b7da..35f3bc3 100644 --- a/data/types/levels.yaml +++ b/data/types/levels.yaml @@ -57,6 +57,12 @@ types: - type: player align: 1 loc: [325,475] + - type: green + align: 2 + loc: [75,25] + - type: blue + align: 1 + loc: [75,475] - type: blue align: 1 loc: [175,475] @@ -65,9 +71,6 @@ types: loc: [25,375] - type: green align: 2 - loc: [75,25] - - type: green - align: 2 loc: [425,75] items: - type: nitro diff --git a/pavement.py b/pavement.py index d0ec9ed..d767048 100755 --- a/pavement.py +++ b/pavement.py @@ -95,7 +95,7 @@ def build_data(): def build_scripts(): "Builds js modules." info('Building scripts...') - sh('rm build/tanks/config.js') + sh('rm -f build/tanks/config.js') tags = sourceTags( commonjs(script_tags=True, capture=True) ) with path('build/deps.html').open('w') as f: f.write(tags) diff --git a/src/ezl/layer/layer.cjs b/src/ezl/layer/layer.cjs index 3cae1c7..55dca75 100644 --- a/src/ezl/layer/layer.cjs +++ b/src/ezl/layer/layer.cjs @@ -29,8 +29,8 @@ Y.subclass('Layer', { animQueue : null, _erased : null, - layerWidth : 0, canvasWidth : 0, - layerHeight : 0, canvasHeight : 0, + layerWidth : 0, canvasWidth : 0, realWidth : 0, + layerHeight : 0, canvasHeight : 0, realHeight : 0, x: 0, y: 0, loc : null, // Position relative to parent @@ -175,16 +175,19 @@ Y.subclass('Layer', { if (w === undefined && h === undefined) return new Vec(this.layerWidth,this.layerHeight); - if (w === null) w = this.layerWidth; - if (h === null) h = this.layerHeight; + if (w === null) w = this.realWidth; + if (h === null) h = this.realHeight; - this.layerWidth = w; - this.layerHeight = h; + this.realWidth = w; + this.realHeight = h; + + // HTMLElement.{width,height} is a long + this.layerWidth = Math.round(w); + this.layerHeight = Math.round(h); var bb = this.bbox.resize(w,h) , nb = this.negBleed, pb = this.posBleed - // HTMLCanvas.{width,height} is a long , cw = this.canvasWidth = Math.ceil(w + nb.x + pb.x) , ch = this.canvasHeight = Math.ceil(h + nb.y + pb.y) ; diff --git a/src/tanks/game.cjs b/src/tanks/game.cjs index 8dfac07..3a50a85 100644 --- a/src/tanks/game.cjs +++ b/src/tanks/game.cjs @@ -7,12 +7,11 @@ var Y = require('Y').Y , Circle = require('ezl/shape').Circle , config = require('tanks/config').config -, map = require('tanks/map') , thing = require('tanks/thing') +, Level = require('tanks/map/level').Level , Grid = require('tanks/ui/grid').Grid , PathMapUI = require('tanks/ui/pathmapui').PathMapUI -, Level = map.Level , Thing = thing.Thing , Tank = thing.Tank , Bullet = thing.Bullet @@ -51,9 +50,9 @@ Y.subclass('Game', { Level.create('test', this, CAPACITY, REF_SIZE) .appendTo(this.root); - this.pathmap = this.level.pathmap; - this.pathmap.ui = new PathMapUI(this, this.pathmap); - this.root.append(this.pathmap.ui); + this.map = this.level.map; + this.map.ui = new PathMapUI(this, this.map); + this.root.append(this.map.ui); // automatically keep track of units Thing.addEventListener('created', this.addThing); @@ -184,7 +183,7 @@ Y.subclass('Game', { unit.render( this.level ); } - this.pathmap.addBlocker(unit); + this.map.addBlocker(unit); return unit; }, @@ -195,7 +194,7 @@ Y.subclass('Game', { delete this.byId[unit.__id__]; this.active.remove(unit); - this.pathmap.removeBlocker(unit); + this.map.removeBlocker(unit); if (unit instanceof Bullet) this.bullets.remove(unit); @@ -206,10 +205,10 @@ Y.subclass('Game', { }, moveThingTo : function moveThingTo(agent, x,y){ - this.pathmap.removeBlocker(agent); + this.map.removeBlocker(agent); if (agent.dead) return agent; agent.position(x,y); - this.pathmap.addBlocker(agent); + this.map.addBlocker(agent); return agent; }, diff --git a/src/tanks/globals.js b/src/tanks/globals.js index b03d4be..24b2520 100644 --- a/src/tanks/globals.js +++ b/src/tanks/globals.js @@ -19,20 +19,6 @@ var undefined , ELAPSED = MS_PER_FRAME // Time (ms) since previous tick , TICKS = 0 // Ticks since start of game -/// Pathing Constants (from tanks/map/map) /// - -/** Does not obstruct other objects. */ -, PASSABLE = 0 - -/** Does not obstruct other objects, but still collides with them. */ -, ZONE = 1 - -/** Obstructs other blockers with its BoundingBox. */ -, BLOCKING = 2 - -/** Potentially obstructs other objects, but requires a special test once a BoundingBox collision has been detected. */ -, IRREGULAR = 3 - /// Common Components of Computation /// diff --git a/src/tanks/map/index.cjs b/src/tanks/map/index.cjs index f357fa0..ba5cba8 100644 --- a/src/tanks/map/index.cjs +++ b/src/tanks/map/index.cjs @@ -2,7 +2,7 @@ require('Y').Y .extend(exports, { 'pathing' : require('tanks/map/pathing'), - 'Map' : require('tanks/map/map').Map, + 'Map' : require('tanks/map/pathing').Map, 'Level' : require('tanks/map/level').Level, 'Wall' : require('tanks/map/wall').Wall, 'Drywall' : require('tanks/map/drywall').Drywall, diff --git a/src/tanks/map/level.cjs b/src/tanks/map/level.cjs index c31d975..393b6eb 100644 --- a/src/tanks/map/level.cjs +++ b/src/tanks/map/level.cjs @@ -5,7 +5,7 @@ var Y = require('Y').Y , Rect = require('ezl/shape').Rect , Buff = require('tanks/effects/buff').Buff -, PathMap = require('tanks/map/pathing/pathmap').PathMap +, Map = require('tanks/map/pathing/map').Map , Thing = require('tanks/thing/thing').Thing , Tank = require('tanks/thing/tank').Tank , Item = require('tanks/thing/item').Item @@ -36,7 +36,7 @@ new evt.Class('Level', { init : function init(game, capacity, buffer_size){ this.game = game; - this.pathmap = new PathMap(0,0, this.width, this.height, capacity, buffer_size); + this.map = new Map(0,0, this.width, this.height, capacity, buffer_size); var shape = this.shape = new Rect(this.width,this.height).fill('transparent'); shape.layer.attr('class', this._cssClasses); diff --git a/src/tanks/map/map.cjs b/src/tanks/map/map.cjs deleted file mode 100644 index 92461b4..0000000 --- a/src/tanks/map/map.cjs +++ /dev/null @@ -1,84 +0,0 @@ -// -*- mode: JavaScript; tab-width: 4; indent-tabs-mode: nil; -*- -var Y = require('Y').Y -, QuadTree = require('ezl/util/tree/quadtree').QuadTree - -, 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; - }, - - removeBlocker : function removeBlocker(obj){ - if (obj.region) { - this.remove(obj.region); - delete obj.region; - } - - if (obj.isWall) { - this.allWalls.remove(obj); - this.innerWalls.remove(obj); - this.denseWalls.remove(obj); - } - - return obj; - }, - - getBlockers : function getBlockers(x1,y1, x2,y2, ignore){ - if (x1 && x1.x1 !== undefined) { - ignore = y1; - y2 = x1.y2; x2 = x1.x2; - y1 = x1.y1; x1 = x1.x1; - } - 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 index 633d120..c9b122e 100644 --- a/src/tanks/map/pathing/index.cjs +++ b/src/tanks/map/pathing/index.cjs @@ -1,6 +1,13 @@ +// Load in order to ensure class is assembled the right way +require('tanks/map/pathing/map'); +require('tanks/map/pathing/map-blockers'); +require('tanks/map/pathing/map-pathing'); +require('tanks/map/pathing/map-searching'); +// + require('Y').Y .extend(exports, { - 'PathMap' : require('tanks/map/pathing/pathmap').PathMap, + 'Map' : require('tanks/map/pathing/map').Map, 'Traversal' : require('tanks/map/pathing/traversal').Traversal, 'Trajectory' : require('tanks/map/pathing/trajectory').Trajectory }); diff --git a/src/tanks/map/pathing/map-blockers.cjs b/src/tanks/map/pathing/map-blockers.cjs new file mode 100644 index 0000000..82bede1 --- /dev/null +++ b/src/tanks/map/pathing/map-blockers.cjs @@ -0,0 +1,70 @@ +// -*- mode: JavaScript; tab-width: 4; indent-tabs-mode: nil; -*- +var Y = require('Y').Y +, constants = require('tanks/constants') +, BoundsType = constants.BoundsType +, DensityType = constants.DensityType +, Map = require('tanks/map/pathing/map').Map +; + + +Y.core.extend(Map.fn, { + + 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; + }, + + removeBlocker : function removeBlocker(obj){ + if (obj.region) { + this.remove(obj.region); + delete obj.region; + } + + if (obj.isWall) { + this.allWalls.remove(obj); + this.innerWalls.remove(obj); + this.denseWalls.remove(obj); + } + + return obj; + }, + + getBlockers : function getBlockers(x1,y1, x2,y2, ignore){ + if (x1 && x1.x1 !== undefined) { + ignore = y1; + y2 = x1.y2; x2 = x1.x2; + y1 = x1.y1; x1 = x1.x1; + } + 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/map-pathing.cjs b/src/tanks/map/pathing/map-pathing.cjs new file mode 100644 index 0000000..36cdc97 --- /dev/null +++ b/src/tanks/map/pathing/map-pathing.cjs @@ -0,0 +1,318 @@ +// -*- mode: JavaScript; tab-width: 4; indent-tabs-mode: nil; -*- +var Y = require('Y').Y +, op = require('Y/op') + +, vec = require('ezl/math/vec') +, Vec = vec.Vec +, Line = require('ezl/math/line').Line +, QuadTree = require('ezl/util/tree/quadtree').QuadTree +, BinaryHeap = require('ezl/util/tree/binaryheap').BinaryHeap + +, config = require('tanks/config').config +, constants = require('tanks/constants') +, BoundsType = constants.BoundsType +, DensityType = constants.DensityType +, Map = require('tanks/map/pathing/map').Map +, Trajectory = require('tanks/map/pathing/trajectory').Trajectory +, Traversal = require('tanks/map/pathing/traversal').Traversal +, Thing = require('tanks/thing/thing').Thing +, Bullet = require('tanks/thing/bullet').Bullet + +, _X = 0, _Y = 1 +, BULLET_MOVE_PER_FRAME = MS_PER_FRAME * Bullet.fn.stats.move*REF_SIZE/1000 +, SQRT_TWO = Math.sqrt(2) + +, kTrue = op.K(true) +, isBullet = Y.is(Bullet) +; + +/** + * 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. + */ +Y.core.extend(Map.fn, { + + /** + * Finds the shortest path to a destination for the given agent. + * @param {Thing} agent Agent whose path is being generated. + * @param {Vec} end End destination. + */ + /** + * Finds the shortest path to a destination for the given agent. + * @param {Thing} agent Agent whose path is being generated. + * @param {Integer} x End destination x-coordinate. + * @param {Integer} y End destination y-coordinate. + */ + // * @param {Function} [passable] Function which tests whether a node is passable. + path : function path(agent, x,y){ + ++this._pathId; // Current pathId for Square calculation + this._agent = agent; // Current agent for Square calculation + + var heuristic = vec.manhattan + , start = this._getSquare(agent.loc) + , end = this._getSquare(x,y) + , open = new BinaryHeap('dist') // 'dist' is the scoring key on heap objects + + , iterations = 0 + ; + + open.push(start); + while (open.length) { + iterations++; + + // 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 = Y([]) + , mid = this.pathSquareMid + , node = current; + while( node.prev ) { + path.push( vec.sum(node,mid) ); + node = node.prev; + } + + if (!path.length) + return path; + + path = Y(path.reverse()); + + // Ensure we don't get stuck on a corner on our first step + var first = path.first() + , bb = agent.bbox.relocated( vec.lerp(0.5, agent.loc,first) ) + , blockers = this.getBlockers(bb, [agent]); + + if (blockers.length) + path.unshift( vec.sum(start,mid) ); + + return path; + } + + // Normal case -- move current from open to closed, process each of its neighbors + current.closed = true; + + var neighbors = current.getNeighbors(); + for (var i=0, il = neighbors.length; i < il; i++) { + var n = neighbors[i] + , node = n[0] + , weight = n[1] + ; + + // Move on if we've done this already, or if it's blocker (ignoring start) + if ( node.closed || (node.blocked && node !== start && node !== end) ) + continue; + + // startDist 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. + // --> In fact! Weight is determined by Square.getNeighbors() + var startDist = current.startDist + weight; + + if ( !node.visited || startDist < node.startDist ) { + + // Found an optimal (so far) path to this node. Take score for node to see how good it is. + node.prev = current; + node.endDist = node.endDist || heuristic(node, end); + node.startDist = startDist; + node.dist = startDist + node.endDist; + + // New? Add to the set of nodes to process + if ( !node.visited ) { + node.visited = true; + open.push(node); + + // Seen, but since it has been rescored we need to reorder it in the heap + } else + open.rescore(node); + } + var _node = node; + } + } + + // No result was found -- empty array signifies failure to find path + return Y([]); + }, + + /** + * @private + * Takes normal (not pathSquare-relative) coordinates. + */ + _getSquare : function getSquare(x,y){ + if (x instanceof Array) { y=x[1]; x=x[0]; } + var cache = this._squares + , SIZE = this.pathSquare ; + + x = Math.floor(x / SIZE) * SIZE; + y = Math.floor(y / SIZE) * SIZE; + var key = x+'_'+y + , sq = cache[key] ; + + if (sq) + sq.reset(); + else + sq = cache[key] = new Square(this, x,y); + + return sq; + }, + + vec2Square : function vec2Square(x,y){ + if (x instanceof Array){ y = x.y; x = x.x; } + var floor = Math.floor, SIZE = this.pathSquare; + 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.pathSquare; + 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(map, x,y){ + Vec.init.call(this, x,y); + this.map = map; + this.reset(); + }, + + reset : function reset(){ + var pathId = this.map._pathId; + if (this.pathId === pathId) + return this; + + this.pathId = pathId; + this.agent = this.map._agent; + 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.map + // , bb = this.agent.bbox + + // , origin = bb.relOrigin + // , left = origin.x, right = bb.width - left + // , top = origin.y, bottom = bb.height - top + + , SIZE = pm.pathSquare + , 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(this.agent) + .filter(this._filterBlocked, this) + ; + + return !!blockers.length; + }, + + /** + * @private + */ + _filterBlocked : function filterBlocked(v, r){ + return (v.blocking === BoundsType.BLOCKING && !v.isBoundary) + || (v.blocking === BoundsType.IRREGULAR && v.testCollide(this.agent,this,null) ); // FIXME: hm. calc bbox? + }, + + getNeighbors : function getNeighbors(){ + var neighbors = [] + , abs = Math.abs + , pm = this.map + , agent = pm._agent + , SIZE = pm.pathSquare + + , 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(){ + 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; + } + +}) +; + + +// Stay sync'd with config +config.updateOnChange( + ['pathing.pathSquare', 'pathing.pathSquareMid'], + Map.fn); + diff --git a/src/tanks/map/pathing/map-searching.cjs b/src/tanks/map/pathing/map-searching.cjs new file mode 100644 index 0000000..35d54ac --- /dev/null +++ b/src/tanks/map/pathing/map-searching.cjs @@ -0,0 +1,109 @@ +// -*- mode: JavaScript; tab-width: 4; indent-tabs-mode: nil; -*- +var Y = require('Y').Y +, op = require('Y/op') + +, vec = require('ezl/math/vec') +, Vec = vec.Vec +, Line = require('ezl/math/line').Line +, QuadTree = require('ezl/util/tree/quadtree').QuadTree +, BinaryHeap = require('ezl/util/tree/binaryheap').BinaryHeap + +, config = require('tanks/config').config +, constants = require('tanks/constants') +, BoundsType = constants.BoundsType +, DensityType = constants.DensityType +, Map = require('tanks/map/pathing/map').Map +, Trajectory = require('tanks/map/pathing/trajectory').Trajectory +, Traversal = require('tanks/map/pathing/traversal').Traversal +, Thing = require('tanks/thing/thing').Thing +, Bullet = require('tanks/thing/bullet').Bullet + +, _X = 0, _Y = 1 +, BULLET_MOVE_PER_FRAME = MS_PER_FRAME * Bullet.fn.stats.move*REF_SIZE/1000 +, SQRT_TWO = Math.sqrt(2) +; + + +Y.core.extend(Map.fn, { + + // moveAwayFrom : function moveAwayFrom(agent){ + // var mid = this.midpoint + // , trj = agent.trajectory.tangent(mid) + // + // , wall = this.closestOf(this.game.map.allWalls) + // , wmid = wall.midpoint + // + // , lvl = this.game.level.bbox, 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); + // + // this.forceCurrentMove = this.willCollide.bind(this, [agent], 5); + // this.currentMoveLimit = this.now + 1000; + // + // this.move(to.x, to.y); + // + // // console.log(' --> Dodge', agent, 'away from', wall, 'to', to); + // return to; + // }, + + findNearLike : function findNearLike(me, ticks, fn){ + if (fn) fn = fn.toFunction(); + + var within = BULLET_MOVE_PER_FRAME*ticks + , bb = me.bbox + , x1 = bb.x1 - within, y1 = bb.y1 - within + , x2 = bb.x2 + within, y2 = bb.y2 + within + , dudes = this.get(x1,y1, x2,y2) + ; + if (fn && dudes.length) + return dudes.filter(fn, me); + else + return dudes; + }, + + findNearEnemies : function findNearEnemies(me, ticks){ + return this.findNearLike(me, ticks, this._nearEnemyFilter); + }, + + _nearEnemyFilter : function nearEnemyFilter(agent){ + var me = this; // Runs in the context of the 'me' unit; @see this.findNearLike() + return agent.isCombatant && agent.align !== me.align; + }, + + findNearEnemiesInSight : function findNearEnemiesInSight(me, ticks){ + return this.findNearLike(me, ticks, this._nearEnemyInSightFilter); + }, + + _nearEnemyInSightFilter : function nearEnemyInSightFilter(agent){ + var me = this; // Runs in the context of the 'me' unit; @see this.findNearLike() + return ( agent.align !== me.align && agent.isCombatant && + new Trajectory(me, me.loc, agent.loc).pathBlocked(agent) ); + }, + + closestOf : function closestOf(me, agents){ + if ( !(agents && agents.length) ) + return null; + + return agents.sort(function(a,b){ + return op.cmp( + manhattan(a.midpoint, me.loc), + manhattan(b.midpoint, me.loc) ); + })[0]; + }, + + willCollide : function willCollide(me, bullets, wiggle){ + bullets = ( Y.isArray(bullets) ? bullets : [bullets] ); + wiggle = wiggle || 0; + + var bb = me.bbox + , w = bb.width+wiggle, h = bb.height+wiggle ; + return bullets.filter(function(agent){ + var trj = agent.trajectory; + return ( !agent.dead + && trj.comesWithin(me, w,h) + && !trj.pathBlocked(me) ); + }); + } + +}); diff --git a/src/tanks/map/pathing/map.cjs b/src/tanks/map/pathing/map.cjs new file mode 100644 index 0000000..3d7ce32 --- /dev/null +++ b/src/tanks/map/pathing/map.cjs @@ -0,0 +1,57 @@ +// -*- mode: JavaScript; tab-width: 4; indent-tabs-mode: nil; -*- +var Y = require('Y').Y +, QuadTree = require('ezl/util/tree/quadtree').QuadTree + +, constants = require('tanks/constants') +, BoundsType = constants.BoundsType +, DensityType = constants.DensityType +, + + +Map = +exports['Map'] = +QuadTree.subclass('Map', { + // Config + pathSquare : null, + pathSquareMid : null, + + // State + _squares : null, // Cache of Square objects + _pathId : -1, // Instance of A* being run, to track when to reset Square instances + + 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, buffer_size) { + this._squares = {}; + this.allWalls = Y([]); + this.innerWalls = Y([]); + this.denseWalls = Y([]); + + 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); + } + + +}); + +// Wrap QuadTree Methods to accept Rect-like objects +'overlaps get set removeAll leaves collect' + .split(' ') + .forEach(function(name){ + var method = Map.fn[name] || QuadTree.fn[name]; + Map.fn[name] = function mapHandleRect(o){ + var args = arguments, xy; + if (o.x1 !== undefined){ + xy = [o.x1,o.y1, o.x2,o.y2]; + args = (args.length === 1) ? xy : xy.concat(Y(args,1)); + } + return method.apply(this, args); + }; + }); diff --git a/src/tanks/map/pathing/pathmap.cjs b/src/tanks/map/pathing/pathmap.cjs deleted file mode 100644 index c81b110..0000000 --- a/src/tanks/map/pathing/pathmap.cjs +++ /dev/null @@ -1,343 +0,0 @@ -// -*- mode: JavaScript; tab-width: 4; indent-tabs-mode: nil; -*- -var Y = require('Y').Y - -, vec = require('ezl/math/vec') -, Vec = vec.Vec -, Line = require('ezl/math/line').Line -, QuadTree = require('ezl/util/tree/quadtree').QuadTree -, BinaryHeap = require('ezl/util/tree/binaryheap').BinaryHeap - -, config = require('tanks/config').config -, constants = require('tanks/constants') -, BoundsType = constants.BoundsType -, DensityType = constants.DensityType -, Map = require('tanks/map/map').Map - - -, SQRT_TWO = Math.sqrt(2) -, - -/** - * 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'] = -Map.subclass('PathMap', { - // Config - pathSquare : null, - pathSquareMid : null, - - // State - _squares : null, // Cache of Square objects - _pathId : -1, // Instance of A* being run, to track when to reset Square instances - - - - 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; - Map.init.call(this, x1,y1, x2,y2, capacity); - this._squares = {}; - }, - - /** - * Takes normal (not pathSquare-relative) coordinates. - */ - _getSquare : function getSquare(x,y){ - if (x instanceof Array) { y=x[1]; x=x[0]; } - var cache = this._squares - , SIZE = this.pathSquare ; - - x = Math.floor(x / SIZE) * SIZE; - y = Math.floor(y / SIZE) * SIZE; - var key = x+'_'+y - , sq = cache[key] ; - - if (sq) - sq.reset(); - else - sq = cache[key] = new Square(this, x,y); - - return sq; - }, - - /** - * Finds the shortest path to a destination for the given agent. - * @param {Thing} agent Agent whose path is being generated. - * @param {Vec} end End destination. - */ - /** - * Finds the shortest path to a destination for the given agent. - * @param {Thing} agent Agent whose path is being generated. - * @param {Integer} x End destination x-coordinate. - * @param {Integer} y End destination y-coordinate. - */ - // * @param {Function} [passable] Function which tests whether a node is passable. - path : function path(agent, x,y){ - ++this._pathId; // Current pathId for Square calculation - this._agent = agent; // Current agent for Square calculation - - var heuristic = vec.manhattan - , start = this._getSquare(agent.loc) - , end = this._getSquare(x,y) - , open = new BinaryHeap('dist') // 'dist' is the scoring key on heap objects - - , iterations = 0 - ; - - open.push(start); - while (open.length) { - iterations++; - - // 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 = Y([]) - , mid = this.pathSquareMid - , node = current; - while( node.prev ) { - path.push( vec.sum(node,mid) ); - node = node.prev; - } - - if (!path.length) - return path; - - path = Y(path.reverse()); - - // Ensure we don't get stuck on a corner on our first step - var first = path.first() - , bb = agent.bbox.relocated( vec.lerp(0.5, agent.loc,first) ) - , blockers = this.getBlockers(bb, [agent]); - - if (blockers.length) - path.unshift( vec.sum(start,mid) ); - - return path; - } - - // Normal case -- move current from open to closed, process each of its neighbors - current.closed = true; - - var neighbors = current.getNeighbors(); - for (var i=0, il = neighbors.length; i < il; i++) { - var n = neighbors[i] - , node = n[0] - , weight = n[1] - ; - - // Move on if we've done this already, or if it's blocker (ignoring start) - if ( node.closed || (node.blocked && node !== start && node !== end) ) - continue; - - // startDist 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. - // --> In fact! Weight is determined by Square.getNeighbors() - var startDist = current.startDist + weight; - - if ( !node.visited || startDist < node.startDist ) { - - // Found an optimal (so far) path to this node. Take score for node to see how good it is. - node.prev = current; - node.endDist = node.endDist || heuristic(node, end); - node.startDist = startDist; - node.dist = startDist + node.endDist; - - // New? Add to the set of nodes to process - if ( !node.visited ) { - node.visited = true; - open.push(node); - - // Seen, but since it has been rescored we need to reorder it in the heap - } else - open.rescore(node); - } - var _node = node; - } - } - - // No result was found -- empty array signifies failure to find path - return Y([]); - }, - - vec2Square : function vec2Square(x,y){ - if (x instanceof Array){ y = x.y; x = x.x; } - var floor = Math.floor, SIZE = this.pathSquare; - 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.pathSquare; - 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.agent = this.pathmap._agent; - 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 - // , bb = this.agent.bbox - - // , origin = bb.relOrigin - // , left = origin.x, right = bb.width - left - // , top = origin.y, bottom = bb.height - top - - , SIZE = pm.pathSquare - , 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(this.agent) - .filter(this._filterBlocked, this) - ; - - return !!blockers.length; - }, - - /** - * @private - */ - _filterBlocked : function filterBlocked(v, r){ - return (v.blocking === BoundsType.BLOCKING && !v.isBoundary) - || (v.blocking === BoundsType.IRREGULAR && v.testCollide(this.agent,this,null) ); // FIXME: hm. calc bbox? - }, - - getNeighbors : function getNeighbors(){ - var neighbors = [] - , abs = Math.abs - , pm = this.pathmap - , agent = pm._agent - , SIZE = pm.pathSquare - - , 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(){ - 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; - } - -}) -; - - - -// 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 pmHandleRect(o){ - var args = arguments, xy; - if (o.x1 !== undefined){ - xy = [o.x1,o.y1, o.x2,o.y2]; - args = (args.length === 1) ? xy : xy.concat(Y(args,1)); - } - return method.apply(this, args); - }; - }); - -// Stay sync'd with config -config.updateOnChange( - ['pathing.pathSquare', 'pathing.pathSquareMid'], - PathMap.fn); - diff --git a/src/tanks/map/pathing/trajectory.cjs b/src/tanks/map/pathing/trajectory.cjs index 6bd8add..5cc3892 100644 --- a/src/tanks/map/pathing/trajectory.cjs +++ b/src/tanks/map/pathing/trajectory.cjs @@ -22,7 +22,7 @@ Line.subclass('Trajectory', { this.owner = owner; this.game = owner.game; - this.pathmap = this.game.pathmap; + this.map = this.game.map; this.reset(x1,y1, x2,y2, tdist, tCurrent); }, @@ -38,7 +38,7 @@ Line.subclass('Trajectory', { Line.init.call(this, x1,y1, x2,y2, tdist || this.tdist); // Find appropriate edge in direction of line - var pm = this.pathmap + var pm = this.map , ex = (x1 > x2 ? -REF_SIZE : REF_SIZE+pm.width ) , ey = (y1 > y2 ? -REF_SIZE : REF_SIZE+pm.height) , edge = this.near(ex,ey) @@ -155,9 +155,9 @@ Line.subclass('Trajectory', { pathBlocked : function pathBlocked(obj, ignore){ var walls, blockers; if (this.owner.isProjectile) - walls = this.pathmap.denseWalls; + walls = this.map.denseWalls; else - walls = this.pathmap.innerWalls; // FIXME: won't filter out concave intersections with the bounds + walls = this.map.innerWalls; // FIXME: won't filter out concave intersections with the bounds blockers = walls .concat( this.game.units ) diff --git a/src/tanks/map/pathing/traversal.cjs b/src/tanks/map/pathing/traversal.cjs index 6f6beda..bc0ca25 100644 --- a/src/tanks/map/pathing/traversal.cjs +++ b/src/tanks/map/pathing/traversal.cjs @@ -22,7 +22,7 @@ Y.subclass('Traversal', { this.thing = thing; this.game = thing.game; - this.pathmap = thing.game.pathmap; + this.map = thing.game.map; this.bbox = thing.bbox.clone(); this.trajectory = trajectory || thing.trajectory; @@ -61,7 +61,7 @@ Y.subclass('Traversal', { this.bbox.relocate(to); // BoundingBox.relocate() is in-place this.blockers = - this.pathmap.get(this.bbox) + this.map.get(this.bbox) .remove(this.thing) .sort(tr.compare); diff --git a/src/tanks/thing/tank.cjs b/src/tanks/thing/tank.cjs index 4c318d5..2caad1c 100644 --- a/src/tanks/thing/tank.cjs +++ b/src/tanks/thing/tank.cjs @@ -29,6 +29,8 @@ exports['Tank'] = Thing.subclass('Tank', function(Tank){ Y.core.descriptors(this, { + isCombatant : true, + colors : { body : '#83BB32', turret : '#1C625B', @@ -92,9 +94,6 @@ Thing.subclass('Tank', function(Tank){ this.elapsed = elapsed; this.now = now; - // this.continueMove(); - // return; - // Check to see if we should obey our last decision, and not recalc if (this.forceCurrentMove && this.forceCurrentMove() && this.currentMoveLimit > now) { // console.log('forced!', this.currentMove); @@ -174,7 +173,7 @@ Thing.subclass('Tank', function(Tank){ , x1 = tx - pw2 - WIGGLE, y1 = ty - ph2 - WIGGLE , x2 = tx + pw2 + WIGGLE, y2 = ty + ph2 + WIGGLE - , blockers = this.game.pathmap.get(x1,y1, x2,y2).filter(filterShoot, this) + , blockers = this.game.map.get(x1,y1, x2,y2).filter(filterShoot, this) ; if ( blockers.size() ) @@ -254,7 +253,7 @@ Thing.subclass('Tank', function(Tank){ this.forceCurrentMove = false; this.currentMoveLimit = -1; - var pm = this.game.pathmap + var pm = this.game.map , start = this.loc , path = this.currentPath = pm.path(this, end) @@ -264,97 +263,6 @@ Thing.subclass('Tank', function(Tank){ }; - /** - * TODO: Get this.currentMove etc out of the calc methods. - */ - this['moveAwayFrom'] = - function moveAwayFrom(agent){ - var mid = this.midpoint - , trj = agent.trajectory.tangent(mid) - - , wall = this.closestOf(this.game.pathmap.allWalls) - , wmid = wall.midpoint - - , lvl = this.game.level.bbox, 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); - - this.forceCurrentMove = this.willCollide.bind(this, [agent], 5); - this.currentMoveLimit = this.now + 1000; - - this.move(to.x, to.y); - - // console.log(' --> Dodge', agent, 'away from', wall, 'to', to); - return to; - }; - - this['find'] = - function find(x1,y1, x2,y2){ - return this.pathmap.get(x1,y1, x2,y2); - }; - - - var kTrue = op.K(true); - - this['findNearLike'] = - function findNearLike(ticks, fn){ - fn = (fn || kTrue).toFunction(); - - var within = BULLET_MOVE_PER_FRAME*ticks - , bb = this.bbox - , 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); - }; - - this['findNearEnemies'] = - function findNearEnemies(ticks, needLineOfSight){ // TODO: Split off LOS version - return this.findNearLike(ticks, function nearEnemyFilter(agent){ - var aLoc = agent.loc; - return ( agent.align !== this.align - && (agent instanceof Tank) - && !(needLineOfSight - && new Trajectory(this,this.loc,aLoc).pathBlocked(agent)) - ); - }); - }; - - this['closestOf'] = - function closestOf(agents){ - if ( !(agents && agents.size()) ) - return null; - - var cmp = op.cmp - , loc = this.loc ; - - agents.sort(function(a,b){ - return cmp( - manhattan(a.midpoint,loc), - manhattan(b.midpoint,loc) ); - }); - - return agents.attr(0); - }; - - this['willCollide'] = - function willCollide(bullets, wiggle){ - bullets = ( Y.isArray(bullets) ? bullets : [bullets] ); - wiggle = wiggle || 0; - - var tank = this, bb = this.bbox - , w = bb.width+wiggle, h = bb.height+wiggle ; - - return bullets.filter(function(b){ - var trj = b.trajectory; - return ( !b.dead - && trj.comesWithin(tank, w,h) - && !trj.pathBlocked(tank) ); - }); - }; - this['getTurretLoc'] = function getTurretLoc(){ var WIGGLE = 2 diff --git a/src/tanks/thing/thing.cjs b/src/tanks/thing/thing.cjs index 5487ab5..e184dc3 100644 --- a/src/tanks/thing/thing.cjs +++ b/src/tanks/thing/thing.cjs @@ -53,9 +53,11 @@ new evt.Class('Thing', { dead : false, dirty : true, - // Properties + // Unit Flags blocking : BoundsType.BLOCKING, density : DensityType.DENSE, + isWall : false, + isCombatant : false, isProjectile : false, isRenderable : false, // Agent will present itself for rendering when ready // FIXME: stupid hack active : true, // Agent takes actions? diff --git a/src/tanks/ui/pathmapui.cjs b/src/tanks/ui/pathmapui.cjs index cf27951..aca2005 100644 --- a/src/tanks/ui/pathmapui.cjs +++ b/src/tanks/ui/pathmapui.cjs @@ -6,7 +6,7 @@ var Y = require('Y').Y , config = require('tanks/config').config , BoundsType = require('tanks/constants').BoundsType -, PathMap = require('tanks/map/pathing/pathmap').PathMap +, Map = require('tanks/map/pathing/map').Map , @@ -20,7 +20,7 @@ Rect.subclass('PathMapUI', { overlayAiPaths : null, // Shape Config - _cssClasses : 'pathmap rect shape layer ezl', + _cssClasses : 'map rect shape layer ezl', // Blocking objects @@ -36,11 +36,11 @@ Rect.subclass('PathMapUI', { - init : function initPathMapUI(game, pathmap){ + init : function initPathMapUI(game, map){ Y.bindAll(this, 'pathWithUI', 'cleanUpAgent', 'drawPathStep', 'overlayRegion'); var level = game.level - , b = this.buffer = pathmap.buffer_size; + , b = this.buffer = map.buffer_size; Rect.init.call(this, level.width, level.height); // this.posBleed.x = this.posBleed.y = // this.negBleed.x = this.negBleed.y = b; @@ -48,18 +48,18 @@ Rect.subclass('PathMapUI', { this.size(null,null); this.game = game; - this.pathmap = pathmap; + this.map = map; this.highlights = Y([]); // Store ref to original path function - this._path = pathmap.path; - pathmap.path = this.pathWithUI; + this._path = map.path; + map.path = this.pathWithUI; }, render : function render(ctx){ // nb. AI paths are automatically drawn (and cleaned up) as they are created (and destroyed). - // Redraw pathmap + // Redraw map if (this.overlayPathmap) this.overlay(ctx); @@ -69,7 +69,7 @@ Rect.subclass('PathMapUI', { overlay : function overlay(ctx){ // console.group('overlay'); - this.pathmap.reduce(this.overlayRegion, { 'ctx':ctx }); + this.map.reduce(this.overlayRegion, { 'ctx':ctx }); // console.groupEnd(); }, @@ -104,10 +104,10 @@ Rect.subclass('PathMapUI', { }, /** - * Wraps PathMap.path() to draw AI pathes. + * Wraps Map.path() to draw AI pathes. */ pathWithUI : function pathWithUI(agent, x,y){ - var pm = this.pathmap + var pm = this.map , path = this._path.call(pm, agent, x,y) , start = vec.sum(pm._getSquare(agent.loc), pm.pathSquareMid) ; diff --git a/src/tanks/util/utils.cjs b/src/tanks/util/utils.cjs index 86f52a1..6016604 100644 --- a/src/tanks/util/utils.cjs +++ b/src/tanks/util/utils.cjs @@ -6,7 +6,7 @@ Object.dump = function(o){ }; function dumpPathmap(){ - var pm = tanks.game.pathmap; + var pm = tanks.game.map; console.warn(new Date(), pm); pm.collect(pm.x1,pm.y1, pm.x2,pm.y2, function(acc, v, r){ console.log(r+''); diff --git a/www/data b/www/data new file mode 120000 index 0000000..4909e06 --- /dev/null +++ b/www/data @@ -0,0 +1 @@ +../data \ No newline at end of file -- 1.7.0.4