From: dsc Date: Fri, 24 Dec 2010 16:30:39 +0000 (-0800) Subject: .gitignore X-Git-Url: http://git.less.ly:3516/?a=commitdiff_plain;h=e83bf476a4785d228e76a6119752fd59f17f6331;p=tanks.git .gitignore Moves asset files. --- diff --git a/.gitignore b/.gitignore index ceaa50f..0afc988 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,4 @@ build/ *.md.html *.pyc www/versioned-deps.html +google74dcf785afbc60e0.html diff --git a/src/tanks/map/index.cjs b/src/tanks/map/index.cjs index a041760..d143552 100644 --- a/src/tanks/map/index.cjs +++ b/src/tanks/map/index.cjs @@ -3,7 +3,6 @@ var Y = require('Y').Y; Y.extend(exports, { 'Map' : require('tanks/map/map').Map, 'PathMap' : require('tanks/map/pathmap').PathMap, - 'Wall' : require('tanks/map/wall').Wall, 'Level' : require('tanks/map/level').Level, 'Traversal' : require('tanks/map/traversal').Traversal, 'Trajectory' : require('tanks/map/trajectory').Trajectory diff --git a/src/tanks/map/level.cjs b/src/tanks/map/level.cjs index 3e7025a..6736b4f 100644 --- a/src/tanks/map/level.cjs +++ b/src/tanks/map/level.cjs @@ -5,7 +5,7 @@ var Y = require('Y').Y , Item = require('tanks/thing/item').Item , PlayerTank = require('tanks/thing/player').PlayerTank , PathMap = require('tanks/map/pathmap').PathMap -, Wall = require('tanks/map/wall').Wall +, Wall = require('tanks/thing/wall').Wall , @@ -42,11 +42,11 @@ Rect.subclass('Level', { // game.addThing(new Tank(1).colors('#4596FF', '#182B53', '#F25522'), 3,9); E = - game.addThing(new Tank(2), 0,5); - game.addThing(new Tank(2), 1,0); - game.addThing(new Tank(2), 8,1); + game.addThing(new Tank(2), 0,7); + // game.addThing(new Tank(2), 1,0); + // game.addThing(new Tank(2), 8,1); - I = game.addThing(new Item(), 8,8); + // I = game.addThing(new Item(), 8,8); }, addWall : function addWall(x,y, w,h, isBoundary){ diff --git a/src/tanks/map/map.cjs b/src/tanks/map/map.cjs index 939494b..5860a7a 100644 --- a/src/tanks/map/map.cjs +++ b/src/tanks/map/map.cjs @@ -1,16 +1,19 @@ // -*- mode: JavaScript; tab-width: 4; indent-tabs-mode: nil; -*- var Y = require('Y').Y - -, math = require('ezl/math') -, vec = require('ezl/math/vec') , QuadTree = require('ezl/util/tree/quadtree').QuadTree -, astar = require('ezl/util/astar') -, config = require('tanks/config').config -, Bullet = require('tanks/thing/bullet').Bullet -, Vec = vec.Vec -, Line = math.Line +/** Does not obstruct other objects. */ +, PASSABLE = exports['PASSABLE'] = 0 + +/** Does not obstruct other objects, but still collides with them. */ +, ZONE = exports['ZONE'] = 1 + +/** Obstructs other blockers with its BoundingBox. */ +, BLOCKING = exports['BLOCKING'] = 2 + +/** Potentially obstructs other objects, but requires a special test once a BoundingBox collision has been detected. */ +, IRREGULAR = exports['IRREGULAR'] = 3 , @@ -32,68 +35,7 @@ QuadTree.subclass('Map', { delete obj.region; } return obj; - }, - - // 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 }; - // }, - + } }); diff --git a/src/tanks/map/pathmap.cjs b/src/tanks/map/pathmap.cjs index 2e439f9..1d515ee 100644 --- a/src/tanks/map/pathmap.cjs +++ b/src/tanks/map/pathmap.cjs @@ -5,157 +5,17 @@ var Y = require('Y').Y , vec = require('ezl/math/vec') , QuadTree = require('ezl/util/tree/quadtree').QuadTree , BinaryHeap = require('ezl/util/binaryheap').BinaryHeap - -, config = require('tanks/config').config -, Bullet = require('tanks/thing/bullet').Bullet -, Map = require('tanks/map/map').Map , Vec = vec.Vec , Line = math.Line +, config = require('tanks/config').config +, map = require('tanks/map/map') +, Map = map.Map -, SQRT_TWO = Math.sqrt(2) -, PASSABLE = 1 // Does not obstruct other objects -, BLOCKING = 2 // Obstructs other blockers -, ZONE = 3 // Does not obstruct other objects, but still collides with them +, SQRT_TWO = Math.sqrt(2) , - -// 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.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 - , agent = pm._agent - // , bb = agent.boundingBox - - // , origin = bb.relOrigin - // , left = origin.x, right = bb.width - left - // , top = origin.y, bottom = bb.height - top - - , 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, x2 = x + SIZE - , y1 = y, y2 = y + SIZE - - , blockers = pm.get(x1,y1, x2,y2) - .remove(agent) - .filter(this._filterBlocked) - ; - - return !!blockers.length; - }, - - /** - * @private - */ - _filterBlocked : function filterBlocked(v, r){ - return !(v.isBoundary || v instanceof Bullet); // XXX: use PASSABLE - }, - - getNeighbors : function getNeighbors(){ - var neighbors = [] - , abs = Math.abs - , pm = this.pathmap - , agent = pm._agent - , SIZE = pm.gridSquare - - , 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 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; - } - -}) - - - /** * A QuadTree which aids in pathing for AI. * - QuadTree methods which took rect coords (x1,y1, x2,y2) will accept @@ -338,7 +198,143 @@ Map.subclass('PathMap', { 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.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 + , agent = pm._agent + // , bb = agent.boundingBox + + // , origin = bb.relOrigin + // , left = origin.x, right = bb.width - left + // , top = origin.y, bottom = bb.height - top + + , 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, x2 = x + SIZE + , y1 = y, y2 = y + SIZE + + , blockers = pm.get(x1,y1, x2,y2) + .remove(agent) + .filter(this._filterBlocked, this) + ; + + return !!blockers.length; + }, + + /** + * @private + */ + _filterBlocked : function filterBlocked(v, r){ + return v.blocking === map.BLOCKING && !v.isBoundary; + }, + + getNeighbors : function getNeighbors(){ + var neighbors = [] + , abs = Math.abs + , pm = this.pathmap + , agent = pm._agent + , SIZE = pm.gridSquare + + , 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 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; + } + +}) +; diff --git a/src/tanks/map/trajectory.cjs b/src/tanks/map/trajectory.cjs index f11de2a..72b4f9a 100644 --- a/src/tanks/map/trajectory.cjs +++ b/src/tanks/map/trajectory.cjs @@ -58,10 +58,10 @@ Line.subclass('Trajectory', { // Determine how much time can pass before we risk teleporting // We'll need to reset this whenever the bounding box changes size resetBound : function resetBound(){ - var p = BOUND_SIZE_RATIO - , abs = Math.abs - , w = this.owner.width, h = this.owner.height; - this.tBound = Math.min(abs(w/this.pa) * p, abs(h/this.pb) * p); + this.tBound = + BOUND_SIZE_RATIO * Math.min( + Math.abs(this.owner.width / this.pa), + Math.abs(this.owner.height / this.pb) ); return this; }, @@ -115,7 +115,7 @@ Line.subclass('Trajectory', { return this.compare(o1,o2) === 1 ? o2 : o1; }, - closest : function closest(o1, o2){ + sortClosest : function sortClosest(o1, o2){ var things; if (o1 instanceof Y.YArray) things = o1; @@ -124,7 +124,11 @@ Line.subclass('Trajectory', { else things = new Y(arguments); - return things.sort( this.compare ).shift(); + return things.sort( this.compare ); + }, + + closest : function closest(o1, o2){ + return this.sortClosest.apply(this, arguments).shift(); }, comesWithin : function comesWithin(pt, w,h){ diff --git a/src/tanks/map/traversal.cjs b/src/tanks/map/traversal.cjs index c449437..40001c8 100644 --- a/src/tanks/map/traversal.cjs +++ b/src/tanks/map/traversal.cjs @@ -1,131 +1,156 @@ var Y = require('Y').Y +, map = require('tanks/map/map') , Traversal = exports['Traversal'] = Y.subclass('Traversal', { + ignore : null, // objects to ignore when determining blockers + isBlocked : false, - isCorner : false, - remaining : 0, - blocker : null, - to : null, - side : null, + to : null, // furthest point reached + remaining : 0, // time left unconsumed due to blocker + blocker : null, // blocking object + side : null, // collision side of blocker (Line) - init : function initTraversal(thing, trajectory){ - this.thing = thing; - this.game = thing.game; - this.pathmap = thing.game.pathmap; - this.bbox = thing.boundingBox.clone(); + init : function initTraversal(thing, trajectory, ignore){ + this.ignore = Y(ignore || []); + + this.thing = thing; + this.game = thing.game; + this.pathmap = thing.game.pathmap; + this.bbox = thing.boundingBox.clone(); this.trajectory = trajectory || thing.trajectory; }, - step : function step(dt, tx,ty){ - var t, traj = this.trajectory; - // console.group('tvsl.step(dt='+dt+', tCurrent='+traj.tCurrent+', target=('+tx+','+ty+'))'); + traverse : function traverse(t, tx,ty){ + var tr = this.trajectory; + this.remaining = t; do { - t = this.stepTo( Math.min(traj.tBound,dt), tx,ty); - traj.tCurrent += t; - dt = Math.max(0, dt-t); - - if (this.isBlocked) { - this.remaining = dt; - var data = { 'traversal':this }; - this.thing.fire('collide', this.blocker, data); - this.blocker.fire('collide', this.thing, data); - break; - } - - } while (dt > 0); + this.step(tx,ty); + } while (!this.isBlocked && this.remaining > 0); - // console.groupEnd(); return this.to; }, /** * Checks for blockers and moves traversal bounds forward as much as possible. - * @param {Number} t Time units to move this traversal forward. - * @return {Number} Time units consumed by this step. */ - stepTo : function stepTo(t, tx,ty){ - var blockers, blocker, B - , abs = Math.abs - , traj = this.trajectory, thing = this.thing - - , bb = this.bbox - , x1 = bb.x1, y1 = bb.y1 - , x2 = bb.x2, y2 = bb.y2 - , o = bb.absOrigin - - , to = traj.parametric(traj.tCurrent+t) + step : function step(tx,ty){ + var tr = this.trajectory + , or = this.thing.loc + , dt = Math.min(tr.tBound, this.remaining) + , to = this.to = tr.parametric(tr.tCurrent+dt) // calc how far down the tr we go ; // Don't overshoot the target - if ( tx !== undefined && abs(o.x-to.x) > abs(o.x-tx) ) + if ( tx !== undefined && Math.abs(or.x-to.x) > Math.abs(or.x-tx) ) to.x = tx; - if ( ty !== undefined && abs(o.y-to.y) > abs(o.y-ty) ) + if ( ty !== undefined && Math.abs(or.y-to.y) > Math.abs(or.y-ty) ) to.y = ty; - // nb: BoundingBox.relocate() is in-place, so this.bbox are updated - bb = bb.relocate(to); - var oto = this.to = to; - blockers = this.blockers = this.pathmap.get(bb).remove(thing).end(); + this.to = to; + this.bbox.relocate(to); // BoundingBox.relocate() is in-place + + this.blockers = + this.pathmap.get(this.bbox) + .remove(this.thing) + .sort(tr.compare); - if (!blockers.length) { + // this.blocker = this.blockers.filter(this.checkBlocker, this).shift(); + this.blockers.filter(this.checkBlocker, this); + + if (this.blocker) { + + // Move bounding box to furthest non-overlapping point + this.to = this.rewind(this.blocker); + this.bbox.relocate(this.to); + + // Calculate how much time we actually used + var consumed = tr.timeToMove(to.x-or.x, to.y-or.y); + this.consumeTime(consumed); + // console.log('stepTo('+dt+') --> wanted '+oto+' but BLOCKED! by '+this.blocker+', so ending at '+to+', consuming '+consumed); + + // Fire events + this.collide(this.blocker); + + } else { + this.consumeTime(dt); // console.log('stepTo('+t+') --> '+to+' consuming '+t); - return t; // All time consumed, yay + } + }, + + /** + * Filters found obstructions to keep only those not ignored and blocking. + */ + checkBlocker : function checkBlocker(blocker){ + var blocking = blocker.blocking; + + // All blockers after the first are ignored + if ( this.ignore.has(blocker) || blocking === map.PASSABLE || this.isBlocked ) + return false; + + // Only fire collision with this zone once per traversal + // XXX: Zone will have to manage enterance/exit and provide a method for testing, hm -- this would obviate the main need for this.ignore + if ( blocking === map.ZONE ) { + this.ignore.push(blocker); + this.collide(blocker); + return false; } this.isBlocked = true; + this.blocker = blocker; + return true; + }, + + collide : function collide(blocker){ + var thing = this.thing + , data = { 'traversal':this, 'thing':thing, 'blocker':blocker }; - // TODO: Either: this never happens (current belief) OR it needs better corner-detection - // Literal corner case :P - if (blockers.length > 1) { - console.error('Corner!', 'to:', to, 'blockers:', blockers); - // throw new Error('Corner! to:'+to+', blockers:'+blockers); - this.isCorner = true; - // to = traj.p1; // Turn around, go back whence you came - } - - // blocker = traj.closest(blockers) - blocker = this.blocker = blockers[0]; - B = blocker.boundingBox; + thing.fire('collide', blocker, data); + blocker.fire('collide', thing, data); + }, + + rewind : function rewind(blocker){ + var tr = this.trajectory, bb = this.bbox + , B = blocker.boundingBox + , to = this.to ; // Figure out which boundary of the blocker we crossed and calculate // the furthest non-overlapping point. - if (x2 <= B.x1 && bb.x2 >= B.x1) { - this.to = to = traj.pointAtX(B.x1 - 1 - bb.originRight); + if (bb.x2 <= B.x1 && bb.x2 >= B.x1) { this.side = B.leftSide; + to = tr.pointAtX(B.x1 - 1 - bb.originRight); - } else if (x1 >= B.x2 && bb.x1 <= B.x2) { - this.to = to = traj.pointAtX(B.x2 + 1 + bb.originLeft); + } else if (bb.x1 >= B.x2 && bb.x1 <= B.x2) { this.side = B.rightSide; + to = tr.pointAtX(B.x2 + 1 + bb.originLeft); - } else if (y2 <= B.y1 && bb.y2 >= B.y1) { - this.to = to = traj.pointAtY(B.y1 - 1 - bb.originTop); + } else if (bb.y2 <= B.y1 && bb.y2 >= B.y1) { this.side = B.topSide; + to = tr.pointAtY(B.y1 - 1 - bb.originTop); - } else if (y1 >= B.y2 && bb.y1 <= B.y2) { - this.to = to = traj.pointAtY(B.y2 + 1 + bb.originBottom ); + } else if (bb.y1 >= B.y2 && bb.y1 <= B.y2) { this.side = B.bottomSide; + to = tr.pointAtY(B.y2 + 1 + bb.originBottom ); } else { - console.error('Null reflection line!', 'to:', to, 'blockers:', blockers); - // throw new Error('Null reflection line! to:'+to+', blockers:'+blockers); + console.error('Null reflection line!', 'to:', to, 'blocker:', blocker); + // throw new Error('Null reflection line! to:'+to+', blocker:'+blocker); } - // Move bounding box to furthest non-overlapping point - bb.relocate(to); - - // Return how much time consumed - var consumed = traj.timeToMove(to.x-o.x, to.y-o.y); - // console.log('stepTo('+t+') --> wanted '+oto+' but BLOCKED! by '+this.blocker+', so ending at '+to+', consuming '+consumed); - - return consumed; + return to; + }, + + consumeTime : function consumeTime(t){ + this.trajectory.tCurrent += t; + this.remaining = Math.max(0, this.remaining-t); + return this; } }) diff --git a/src/tanks/map/wall.cjs b/src/tanks/map/wall.cjs deleted file mode 100644 index 03c5b3f..0000000 --- a/src/tanks/map/wall.cjs +++ /dev/null @@ -1,70 +0,0 @@ -var Y = require('Y').Y -, op = require('Y/op') -, Rect = require('ezl/shape').Rect -, Thing = require('tanks/thing/thing').Thing -, - - -Wall = -exports['Wall'] = -Thing.subclass('Wall', { - isBoundary : false, - blocking : true, - active : false, - - originX : 0, - originY : 0, - - stats : { - hp : Infinity, - move : 0, - power : 0, - speed : 0, - shots : 0 - }, - - - init : function initWall(x,y, w,h, isBoundary){ - this.width = w; - this.height = h; - this.isBoundary = !!isBoundary; - Thing.init.call(this); - this.position(x,y); - }, - - // inactive - createCooldowns : op.nop, - - // indestructable - dealDamage : op.nop, - - - render : function render(parent){ - if (this.isBoundary) - return this; - - if (this.shape) - this.shape.remove(); - - this.shape = - new Rect(this.width, this.height) - .position(this.loc.x, this.loc.y) - .fill('rgba(255,255,255, 0.25)') - .appendTo( parent ); - - return this; - }, - - toString : function(){ - var bb = this.boundingBox - , x1,y1, x2,y2; - if (bb){ - x1 = bb.x1; y1 = bb.y1; - x2 = bb.x2; y2 = bb.y2; - } else { - x1=y1=x2=y2=NaN; - } - return this.className+'['+x1+','+y1+', '+x2+','+y2+'](w='+this.width+', h='+this.height+')'; - } -}); - diff --git a/src/tanks/thing/bullet.cjs b/src/tanks/thing/bullet.cjs index 22d1b94..b2a7e9c 100644 --- a/src/tanks/thing/bullet.cjs +++ b/src/tanks/thing/bullet.cjs @@ -1,19 +1,21 @@ // -*- mode: JavaScript; tab-width: 4; indent-tabs-mode: nil; -*- var Y = require('Y').Y , op = require('Y/op') + , math = require('ezl/math') , shape = require('ezl/shape') +, Line = shape.Line +, Circle = shape.Circle + , config = require('tanks/config').config , thing = require('tanks/thing/thing') -, Wall = require('tanks/map/wall').Wall +, map = require('tanks/map/map') +, Wall = require('tanks/thing/wall').Wall , Trajectory = require('tanks/map/trajectory').Trajectory , Traversal = require('tanks/map/traversal').Traversal , Explosion = require('tanks/fx/explosion').Explosion - , fillStats = thing.fillStats , Thing = thing.Thing -, Line = shape.Line -, Circle = shape.Circle , @@ -31,7 +33,8 @@ Thing.subclass('Bullet', { }, // Instance - blocking : true, + blocking : map.BLOCKING, + bounces : 0, bounceLimit : 1, @@ -92,12 +95,12 @@ Thing.subclass('Bullet', { if (this.dead) return this; var tvsl = new Traversal(this) - , to = tvsl.step(elapsed) + , to = tvsl.traverse(elapsed) ; if (tvsl.isBlocked && !this.dead) { tvsl = new Traversal(this, this.trajectory); - to = tvsl.step(tvsl.remaining); + to = tvsl.traverse(tvsl.remaining); } if (!this.dead) @@ -109,25 +112,30 @@ Thing.subclass('Bullet', { onCollide : function onCollide(evt){ if ( this.dead ) return; - var ng - , tvsl = evt.data.traversal - , traj = this.trajectory + var traj = this.trajectory + + , ng, d = evt.data + , tvsl = d.traversal , to = tvsl.to + , unit = d.blocker - , unit = evt.trigger - , hitAWall = unit instanceof Wall + , isReflective = unit instanceof Wall // XXX: unit.reflective ; + // Ignore collisions with zones + if (unit.blocking === map.ZONE) + return; + // Reflection! - if ( hitAWall && this.bounceLimit >= ++this.bounces ) { - if ( tvsl.isCorner ) { - ng = to; - } else { - if (!tvsl.side) return console.error('Null reflection line!', 'to:', to, 'blockers:', tvsl.blockers); - ng = math.reflect(traj.p2, tvsl.side); - } + if ( isReflective && this.bounceLimit >= ++this.bounces ) { + if (!tvsl.side) + return console.error('Null reflection line!', 'to:', to, 'blockers:', tvsl.blockers); + + ng = math.reflect(traj.p2, tvsl.side); traj.reset(to.x,to.y, ng.x,ng.y); - this.render(this.game.level); // to render the new reflection line + + this.dirty = true; // to render the new reflection line, if it's visible + return; } @@ -143,7 +151,7 @@ Thing.subclass('Bullet', { , start = bsize, end = bsize ; - if (hitAWall) { + if (isReflective) { x = loc.x; y = loc.y; } else { diff --git a/src/tanks/thing/index.cjs b/src/tanks/thing/index.cjs index 93d9be7..8aa096a 100644 --- a/src/tanks/thing/index.cjs +++ b/src/tanks/thing/index.cjs @@ -1,4 +1,5 @@ exports['Thing'] = require('tanks/thing/thing').Thing; +exports['Wall'] = require('tanks/thing/wall').Wall; exports['Bullet'] = require('tanks/thing/bullet').Bullet; exports['Tank'] = require('tanks/thing/tank').Tank; exports['PlayerTank'] = require('tanks/thing/player').PlayerTank; diff --git a/src/tanks/thing/item.cjs b/src/tanks/thing/item.cjs index 7e218ff..704708a 100644 --- a/src/tanks/thing/item.cjs +++ b/src/tanks/thing/item.cjs @@ -1,9 +1,12 @@ var Y = require('Y').Y , op = require('Y/op') -, Thing = require('tanks/thing/thing').Thing + , shape = require('ezl/shape') , Rect = shape.Rect +, map = require('tanks/map/map') +, Thing = require('tanks/thing/thing').Thing + , SIZE = REF_SIZE * 0.5 , @@ -11,7 +14,7 @@ var Y = require('Y').Y Item = exports['Item'] = Thing.subclass('Item', { - blocking : true, + blocking : map.ZONE, active : false, width : SIZE, diff --git a/src/tanks/thing/player.cjs b/src/tanks/thing/player.cjs index fdd177a..d59b3ce 100644 --- a/src/tanks/thing/player.cjs +++ b/src/tanks/thing/player.cjs @@ -2,6 +2,7 @@ //#ensure "jquery.hotkeys" var Y = require('Y').Y +, map = require('tanks/map/map') , Tank = require('tanks/thing/tank').Tank , @@ -9,6 +10,7 @@ var Y = require('Y').Y PlayerTank = exports['PlayerTank'] = Tank.subclass('PlayerTank', { + blocking : map.BLOCKING, bodyColor : '#E73075', turretColor : '#A72F5B', diff --git a/src/tanks/thing/tank.cjs b/src/tanks/thing/tank.cjs index cc38b24..2c60493 100644 --- a/src/tanks/thing/tank.cjs +++ b/src/tanks/thing/tank.cjs @@ -5,17 +5,17 @@ var Y = require('Y').Y , shape = require('ezl/shape') , Cooldown = require('ezl/loop').Cooldown , CooldownGauge = require('ezl/widget').CooldownGauge +, Vec = vec.Vec +, manhattan = vec.manhattan +, Rect = shape.Rect +, Circle = shape.Circle +, map = require('tanks/map/map') , Thing = require('tanks/thing/thing').Thing , Bullet = require('tanks/thing/bullet').Bullet , Trajectory = require('tanks/map/trajectory').Trajectory , Traversal = require('tanks/map/traversal').Traversal -, Vec = vec.Vec -, manhattan = vec.manhattan -, Rect = shape.Rect -, Circle = shape.Circle - , isBullet = Y.is(Bullet) , BULLET_MOVE_PER_FRAME = MS_PER_FRAME * Bullet.prototype.stats.move*REF_SIZE/1000 , _X = 0, _Y = 1 @@ -27,8 +27,7 @@ exports['Tank'] = Thing.subclass('Tank', function(Tank){ Y.core.descriptors(this, { - projectile : Bullet, - blocking : true, + blocking : map.BLOCKING, bodyColor : '#83BB32', turretColor : '#1C625B', @@ -57,6 +56,7 @@ Thing.subclass('Tank', function(Tank){ shootEnemy : 0.75 // shoot at enemy tank if in range }, + projectile : Bullet, buffs : null, nShots : 0, @@ -84,8 +84,8 @@ Thing.subclass('Tank', function(Tank){ this.elapsed = elapsed; this.now = now; - // this.continueMove(); - // return; + this.continueMove(); + return; // Check to see if we should obey our last decision, and not recalc if (this.forceCurrentMove && this.forceCurrentMove() && this.currentMoveLimit > now) { @@ -150,7 +150,7 @@ Thing.subclass('Tank', function(Tank){ this.trajectory = new Trajectory(this, loc.x,loc.y, x,y, this.movePerMs); var tvsl = new Traversal(this) - , to = tvsl.step(this.elapsed, x,y) ; + , to = tvsl.traverse(this.elapsed, x,y) ; this.game.moveThingTo(this, to.x,to.y); @@ -187,14 +187,13 @@ Thing.subclass('Tank', function(Tank){ this.forceCurrentMove = false; this.currentMoveLimit = -1; - // console.log('calculatePath() moving toward:', end); var pm = this.game.pathmap , start = this.loc , path = this.currentPath = pm.path(this, end) , to = this.currentMove = path.shift() ; - // console.log('--> next move:', this.currentMove); + console.log('calculatePath()', start, 'toward:', end, 'next:', this.currentMove, 'path: ['+path.invoke('toString')+']'); }; @@ -381,7 +380,7 @@ Thing.subclass('Tank', function(Tank){ * or when the world needs to be redrawn from scratch. */ this['render'] = - function render( parent ){ + function render(parent){ if (this.shape) this.shape.remove(); var w = this.width, w2 = w/2 diff --git a/src/tanks/thing/thing.cjs b/src/tanks/thing/thing.cjs index 4c9b01a..3315dc9 100644 --- a/src/tanks/thing/thing.cjs +++ b/src/tanks/thing/thing.cjs @@ -1,15 +1,20 @@ //#ensure "evt" var Y = require('Y').Y , op = require('Y/op') + , evt = require('evt') + , Loc = require('ezl/loc/loc').Loc , BoundingBox = require('ezl/loc/boundingbox').BoundingBox , Cooldown = require('ezl/loop').Cooldown + , config = require('tanks/config').config +, map = require('tanks/map/map') , THING_ID = 0 , + fillStats = exports['fillStats'] = function fillStats(stats){ @@ -32,8 +37,7 @@ function fillStats(stats){ Thing = -exports['Thing'] = -new evt.Class('Thing', { +exports['Thing'] = new evt.Class('Thing', { // Config showAttackCooldown : null, @@ -50,14 +54,15 @@ new evt.Class('Thing', { // AI "Cooldowns" (max frequency of each action per sec) ai : {}, // see Tank + // *** Bookkeeping *** // id : 0, align : 0, // 0 reserved for neutral units dead : false, + dirty : true, - pathing : null, - blocking : true, // Whether the agent obstructs pathing + blocking : map.BLOCKING, // Pathing type @see {map} for constant definitions active : true, // Whether the agent takes actions // Location @@ -74,8 +79,8 @@ new evt.Class('Thing', { height : REF_SIZE, // Accessors - set : op.set.methodize(), - attr : op.attr.methodize(), + 'set' : op.set.methodize(), + 'attr' : op.attr.methodize(), get movePerMs(){ return this.stats.move*REF_SIZE/1000; }, @@ -184,15 +189,19 @@ new evt.Class('Thing', { * Sets up unit appearance for minimal updates. Called once at start, * or when the world needs to be redrawn from scratch. */ - render : function render(){ + render : function render(parent){ return this; }, draw : function draw(){ - if (this.dead) - this.shape.hide(); - else + if (!this.dead) { + if (this.dirty) { + this.dirty = false; + this.render(this.game.level); + } this.shape.draw(); + } else + this.shape.hide(); }, toString : function toString(){ diff --git a/www/deps.html b/www/deps.html index 7b1d73f..b943aaf 100644 --- a/www/deps.html +++ b/www/deps.html @@ -48,27 +48,26 @@ - - + + + - + + - - - diff --git a/www/img/tank-icon.png b/www/img/tank-icon.png deleted file mode 100644 index ef4a61d..0000000 Binary files a/www/img/tank-icon.png and /dev/null differ diff --git a/www/img/viewport.png b/www/img/viewport.png deleted file mode 100644 index b731914..0000000 Binary files a/www/img/viewport.png and /dev/null differ