From 17e418922bc8b1a62aafe7635887e10a2e85e1cd Mon Sep 17 00:00:00 2001 From: dsc Date: Mon, 20 Dec 2010 16:49:46 -0800 Subject: [PATCH] Movement and pathing sorta work again! AI tanks are a bit off though. --- src/ezl/loop/eventloop.cjs | 2 +- src/ezl/math/rect.cjs | 6 +++- src/tanks/map/level.cjs | 4 +- src/tanks/map/pathmap.cjs | 55 +++++++++++++++++++++++++++---------- src/tanks/map/traversal.cjs | 33 +++++++++++++--------- src/tanks/thing/bullet.cjs | 4 +- src/tanks/thing/player.cjs | 5 +++- src/tanks/thing/tank.cjs | 63 ++++++++++++++++++++++-------------------- 8 files changed, 105 insertions(+), 67 deletions(-) diff --git a/src/ezl/loop/eventloop.cjs b/src/ezl/loop/eventloop.cjs index c93e2e5..6d2895f 100644 --- a/src/ezl/loop/eventloop.cjs +++ b/src/ezl/loop/eventloop.cjs @@ -96,7 +96,7 @@ Emitter.subclass('EventLoop', { var lastTick = this.now; this.now = new Date().getTime(); this.elapsed = this.now - lastTick; - this.perceived = this.elapsed/this.dilation; + this.perceived = this.elapsed/this.timeDilation; clearTimeout(this.timer); this.timer = null; diff --git a/src/ezl/math/rect.cjs b/src/ezl/math/rect.cjs index 7cf09e0..0d665e4 100644 --- a/src/ezl/math/rect.cjs +++ b/src/ezl/math/rect.cjs @@ -1,5 +1,7 @@ -var Y = require('Y').Y -, Vec = require('ezl/math/vec').Vec +var Y = require('Y').Y +, Vec = require('ezl/math/vec').Vec +, Line = require('ezl/math/line').Line + , X1 = 0, Y1 = 1 , X2 = 2, Y2 = 3 , diff --git a/src/tanks/map/level.cjs b/src/tanks/map/level.cjs index a3cc320..3da05b1 100644 --- a/src/tanks/map/level.cjs +++ b/src/tanks/map/level.cjs @@ -42,8 +42,8 @@ Rect.subclass('Level', { // game.addThing(new Tank(1).colors('#4596FF', '#182B53', '#F25522'), 3,9); E = - game.addThing(new Tank(2), 0,1); - game.addThing(new Tank(2), 1,0); + game.addThing(new Tank(2), 0,0); + // game.addThing(new Tank(2), 1,0); // game.addThing(new Tank(2), 8,1); I = game.addThing(new Item(), 8,8); diff --git a/src/tanks/map/pathmap.cjs b/src/tanks/map/pathmap.cjs index 14f2ad5..7c6cc28 100644 --- a/src/tanks/map/pathmap.cjs +++ b/src/tanks/map/pathmap.cjs @@ -23,7 +23,7 @@ var Y = require('Y').Y // Would be nice if I could make this an inner class (for once) Square = exports['Square'] = -Vec.subclass('Square', { +Y.subclass('Square', new Vec(0,0), { pathId : null, // instance of A* being run, to track when to reset // resetable: @@ -51,7 +51,7 @@ Vec.subclass('Square', { return this; this.pathId = pathId; - this._blocked(); + this.blocked = this._blocked(); this.dist = 0; this.startDist = 0; @@ -64,7 +64,7 @@ Vec.subclass('Square', { }, /** - * @private Caches this.blocked value. Should only be called by reset(). + * @private Calculates this.blocked value. Should only be called by reset() to cache the result. */ _blocked : function blocked(){ var pm = this.pathmap @@ -78,15 +78,17 @@ Vec.subclass('Square', { , SIZE = pm.gridSquare , 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 - 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(agent) .filter(this._filterBlocked) ; - this.blocked = !!blockers.length; + return !!blockers.length; }, /** @@ -104,23 +106,39 @@ Vec.subclass('Square', { , SIZE = pm.gridSquare , x = this.x, y = this.y - , sq, cost, ix, iy + , sq, cost, ix, iy, x1,y1, x2,y2 ; for (ix=-1; ix<2; ix++) { for (iy=-1; iy<2; iy++) { - // if (ix === 0 && iy === 0) // skipping diagonals - if ( (ix === 0 && iy === 0) || (abs(ix) === 1 && abs(iy) === 1) ) + // if (ix === 0 && iy === 0) + if ( (ix === 0 && iy === 0) || (abs(ix) === 1 && abs(iy) === 1) ) // skipping diagonals + continue; + x1 = x + ix*SIZE; y1 = y + iy*SIZE; + x2 = x1+SIZE; y2 = y1+SIZE; + if ( pm.x1 >= x1 || pm.y1 >= y1 || + pm.x2 <= x2 || pm.y2 <= y2 ) continue; // TODO: handle diagonals: need to ensure we don't try to move through a corner // cost = ( abs(ix) === 1 && abs(iy) === 1 ) ? SQRT_TWO : 1; cost = 1; - sq = pm._getSquare(x + ix*SIZE, y + iy*SIZE); - if ( !sq.blocked ) neighbors.push([ sq, cost*SIZE ]); + sq = pm._getSquare(x1,y1); + // if ( !sq.blocked ) neighbors.push([ sq, cost*SIZE ]); + neighbors.push([ sq, cost*SIZE ]); } } return neighbors; + }, + + toString : function toString(){ + 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; } }) @@ -193,10 +211,12 @@ Map.subclass('PathMap', { */ _getSquare : function getSquare(x,y){ if (x instanceof Array) { y=x[1]; x=x[0]; } - x = Math.floor(x); y = Math.floor(y); - var cache = this._squares - , key = x+"_"+y + , SIZE = this.gridSquare ; + + x = Math.floor(x / SIZE) * SIZE; + y = Math.floor(y / SIZE) * SIZE; + var key = x+'_'+y , sq = cache[key] ; if (sq) @@ -227,10 +247,13 @@ Map.subclass('PathMap', { , 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(); @@ -238,9 +261,10 @@ Map.subclass('PathMap', { // End case -- result has been found, return the traced path if (current === end) { var path = [] + , mid = this.gridSquareMid , node = current; while( node.prev ) { - path.push( node ); + path.push( vec.sum(node,mid) ); node = node.prev; } return path.reverse(); @@ -283,6 +307,7 @@ Map.subclass('PathMap', { } else open.rescore(node); } + var _node = node; } } diff --git a/src/tanks/map/traversal.cjs b/src/tanks/map/traversal.cjs index 29a5471..7fe1e34 100644 --- a/src/tanks/map/traversal.cjs +++ b/src/tanks/map/traversal.cjs @@ -18,7 +18,7 @@ Y.subclass('Traversal', { init : function initTraversal(thing, trajectory){ this.thing = thing; this.game = thing.game; - this.pathmap = thing.pathmap; + this.pathmap = thing.game.pathmap; this.bbox = thing.boundingBox.clone(); this.trajectory = trajectory || thing.trajectory; @@ -36,8 +36,9 @@ Y.subclass('Traversal', { if (this.isBlocked) { this.remaining = dt; - this.thing.fire('collide', this.blocker); - this.blocker.fire('collide', this.thing); + var data = { 'traversal':this }; + this.thing.fire('collide', this.blocker, data); + this.blocker.fire('collide', this.thing, data); break; } @@ -53,6 +54,7 @@ Y.subclass('Traversal', { */ stepTo : function stepTo(t, tx,ty){ var blockers, blocker, B + , abs = Math.abs , traj = this.trajectory, thing = this.thing , bb = this.bbox @@ -82,9 +84,10 @@ Y.subclass('Traversal', { // TODO: Either: this never happens (current belief) OR it needs better corner-detection // Literal corner case :P if (blockers.length > 1) { - console.error('Corner! WHO KNEW.', 'to:', to, 'blockers:', blockers); + console.error('Corner!', 'to:', to, 'blockers:', blockers); + // throw new Error('Corner! to:'+to+', blockers:'+blockers); this.isCorner = true; - // to = trj.p1; // Turn around, go back whence you came + // to = traj.p1; // Turn around, go back whence you came } // blocker = traj.closest(blockers) @@ -93,30 +96,32 @@ Y.subclass('Traversal', { // Figure out which boundary of the blocker we crossed and calculate // the furthest non-overlapping point. - if (bb.x2 <= B.x1 && x2 >= B.x1) { - this.to = to = trj.pointAtX(B.x1 - 1 - bb.originRight); + if (x2 <= B.x1 && bb.x2 >= B.x1) { + this.to = to = traj.pointAtX(B.x1 - 1 - bb.originRight); this.side = B.leftSide; - } else if (bb.x1 >= B.x2 && x1 <= B.x2) { - this.to = to = trj.pointAtX(B.x2 + 1 + bb.originLeft); + } else if (x1 >= B.x2 && bb.x1 <= B.x2) { + this.to = to = traj.pointAtX(B.x2 + 1 + bb.originLeft); this.side = B.rightSide; - } else if (bb.y2 <= B.y1 && y2 >= B.y1) { - this.to = to = trj.pointAtY(B.y1 - 1 - bb.originTop); + } else if (y2 <= B.y1 && bb.y2 >= B.y1) { + this.to = to = traj.pointAtY(B.y1 - 1 - bb.originTop); this.side = B.topSide; - } else if (bb.y1 >= B.y2 && y1 <= B.y2) { - this.to = to = trj.pointAtY(B.y2 + 1 + bb.originBottom ); + } else if (y1 >= B.y2 && bb.y1 <= B.y2) { + this.to = to = traj.pointAtY(B.y2 + 1 + bb.originBottom ); this.side = B.bottomSide; + } else { console.error('Null reflection line!', 'to:', to, 'blockers:', blockers); + // throw new Error('Null reflection line! to:'+to+', blockers:'+blockers); } // Move bounding box to furthest non-overlapping point bb.relocate(to); // Return how much time consumed - return trj.timeToMove(to.x-o.x, to.y-o.y); + return traj.timeToMove(to.x-o.x, to.y-o.y); } }) diff --git a/src/tanks/thing/bullet.cjs b/src/tanks/thing/bullet.cjs index 08de2ac..22d1b94 100644 --- a/src/tanks/thing/bullet.cjs +++ b/src/tanks/thing/bullet.cjs @@ -91,7 +91,7 @@ Thing.subclass('Bullet', { move : function move(elapsed, now){ if (this.dead) return this; - var tvsl = new Traversal(this, this.trajectory) + var tvsl = new Traversal(this) , to = tvsl.step(elapsed) ; @@ -110,7 +110,7 @@ Thing.subclass('Bullet', { if ( this.dead ) return; var ng - , tvsl = evt.target + , tvsl = evt.data.traversal , traj = this.trajectory , to = tvsl.to diff --git a/src/tanks/thing/player.cjs b/src/tanks/thing/player.cjs index 8455342..619cf4b 100644 --- a/src/tanks/thing/player.cjs +++ b/src/tanks/thing/player.cjs @@ -56,6 +56,9 @@ Tank.subclass('PlayerTank', { act : function act(elapsed, now){ + this.elapsed = elapsed; + this.now = now; + if (this.dead) return this; @@ -86,7 +89,7 @@ Tank.subclass('PlayerTank', { if (!dir) return; // @see Tank.move() - this.move(this.loc.moveByDir(dir, this.stats.move * SQUARETH)); + this.move( this.loc.moveByDir(dir, this.stats.move * SQUARETH) ); }, // _move : function _move(){ diff --git a/src/tanks/thing/tank.cjs b/src/tanks/thing/tank.cjs index 40238a5..fbbb7fd 100644 --- a/src/tanks/thing/tank.cjs +++ b/src/tanks/thing/tank.cjs @@ -80,6 +80,9 @@ Thing.subclass('Tank', function(Tank){ this['act'] = function act(elapsed, now){ + this.elapsed = elapsed; + this.now = now; + var ai = this.ai; // Check to see if we should obey our last decision, and not recalc @@ -293,11 +296,11 @@ Thing.subclass('Tank', function(Tank){ , cur = this.currentMove ; - if (cur.x !== x || cur.y !== y) + if ( !(this.trajectory && cur && cur.x === x && cur.y === y) ) this.trajectory = new Trajectory(this, loc.x,loc.y, x,y, this.movePerMs); - var tvsl = new Traversal(this, this.trajectory) - , to = tvsl.step(elapsed, x,y) + var tvsl = new Traversal(this) + , to = tvsl.step(this.elapsed, x,y) ; this.game.moveThingTo(this, to.x,to.y); @@ -308,35 +311,35 @@ Thing.subclass('Tank', function(Tank){ * @protected * This method does not update this.trajectory -- call this.move(x,y) instead. */ - this['moveByAngle'] = - function moveByAngle(theta, targetX,targetY){ - var abs = Math.abs - , bb = this.boundingBox - , loc = this.loc - , to = loc.moveByAngle(theta, this.stats.move * SQUARETH) - , x = to.x, y = to.y - ; - - // Don't overshoot the target - if ( targetX !== undefined && abs(loc.x-x) > abs(loc.x-targetX) ) - x = targetX; - - if ( targetY !== undefined && abs(loc.y-y) > abs(loc.y-targetY) ) - y = targetY; - - var nbb = bb.relocated(x,y) - , blockers = this.game.pathmap.get(nbb.x1,nbb.y1, nbb.x2,nbb.y2).remove(this); - - if ( !blockers.size() ) - this.game.moveThingTo(this, x,y); - - return this; - }; + // this['moveByAngle'] = + // function moveByAngle(theta, targetX,targetY){ + // var abs = Math.abs + // , bb = this.boundingBox + // , loc = this.loc + // , to = loc.moveByAngle(theta, this.stats.move * SQUARETH) + // , x = to.x, y = to.y + // ; + // + // // Don't overshoot the target + // if ( targetX !== undefined && abs(loc.x-x) > abs(loc.x-targetX) ) + // x = targetX; + // + // if ( targetY !== undefined && abs(loc.y-y) > abs(loc.y-targetY) ) + // y = targetY; + // + // var nbb = bb.relocated(x,y) + // , blockers = this.game.pathmap.get(nbb.x1,nbb.y1, nbb.x2,nbb.y2).remove(this); + // + // if ( !blockers.size() ) + // this.game.moveThingTo(this, x,y); + // + // return this; + // }; this['continueMove'] = function continueMove(){ - if ( !this.currentMove || this.currentMoveLimit <= NOW ){ + if ( !this.currentMove || this.ai.path.ready ){ var target = this.findNearEnemies(10000).shift(); if (target) this.calculatePath(target.loc); } @@ -344,8 +347,8 @@ Thing.subclass('Tank', function(Tank){ var to = this.currentMove; if (!to) return; - this.move(to.x, to.y); - if ( this.midpoint.equals(to) ) { + this.move(to); + if ( this.loc.equals(to) ) { this.forceCurrentMove = false; this.currentMoveLimit = -1; this.currentMove = (this.lastPath ? this.lastPath.shift() : null); -- 1.7.0.4