From e83bf476a4785d228e76a6119752fd59f17f6331 Mon Sep 17 00:00:00 2001 From: dsc Date: Fri, 24 Dec 2010 08:30:39 -0800 Subject: [PATCH] .gitignore Moves asset files. --- .gitignore | 1 + assets/favicon-green.psd | Bin 0 -> 24308 bytes assets/favicon-pink.psd | Bin 0 -> 23408 bytes assets/favicon-pink2.psd | Bin 0 -> 24260 bytes src/tanks/map/index.cjs | 1 - src/tanks/map/level.cjs | 10 +- src/tanks/map/map.cjs | 82 ++---------- src/tanks/map/pathmap.cjs | 286 +++++++++++++++++++++--------------------- src/tanks/map/trajectory.cjs | 16 ++- src/tanks/map/traversal.cjs | 181 +++++++++++++++------------ src/tanks/map/wall.cjs | 70 ---------- src/tanks/thing/bullet.cjs | 50 +++++--- src/tanks/thing/index.cjs | 1 + src/tanks/thing/item.cjs | 7 +- src/tanks/thing/player.cjs | 2 + src/tanks/thing/tank.cjs | 25 ++-- src/tanks/thing/thing.cjs | 29 +++-- www/deps.html | 11 +- www/img/favicon-green.psd | Bin 24308 -> 0 bytes www/img/favicon-pink.psd | Bin 23408 -> 0 bytes www/img/favicon-pink2.psd | Bin 24260 -> 0 bytes www/img/tank-icon.png | Bin 6405 -> 0 bytes www/img/viewport.png | Bin 18170 -> 0 bytes 23 files changed, 345 insertions(+), 427 deletions(-) create mode 100644 assets/favicon-green.psd create mode 100644 assets/favicon-pink.psd create mode 100644 assets/favicon-pink2.psd delete mode 100644 src/tanks/map/wall.cjs delete mode 100644 www/img/favicon-green.psd delete mode 100644 www/img/favicon-pink.psd delete mode 100644 www/img/favicon-pink2.psd delete mode 100644 www/img/tank-icon.png delete mode 100644 www/img/viewport.png 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/assets/favicon-green.psd b/assets/favicon-green.psd new file mode 100644 index 0000000000000000000000000000000000000000..6c45e856ecc56792c096a1c54ef2a54faee53ca3 GIT binary patch literal 24308 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 Nf`2CcPSf9e{~LeH1pxp6 literal 0 HcmV?d00001 diff --git a/assets/favicon-pink.psd b/assets/favicon-pink.psd new file mode 100644 index 0000000000000000000000000000000000000000..c18399b131facd7c7d21e9c5419b2425ff957e84 GIT binary patch literal 23408 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 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/favicon-green.psd b/www/img/favicon-green.psd deleted file mode 100644 index 6c45e856ecc56792c096a1c54ef2a54faee53ca3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 24308 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 Nf`2CcPSf9e{~LeH1pxp6 diff --git a/www/img/favicon-pink.psd b/www/img/favicon-pink.psd deleted file mode 100644 index c18399b131facd7c7d21e9c5419b2425ff957e84..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 23408 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 hn$D}4{H-uzm68?b4<4|5hCEeU9pty z!WONAWiKZndtIZyA%(G%8Q#%f5u|I?9#*nmy=s!{+%@%A&*z>*g)+uUN=^$bmWSH!dXj>pdEJn7B(t@ji< zE>6tc`+afeN^ZU;6hTq|r_IO$Ld>}J#^7~w8Z&5+P-m~8+>DB7ww{P~T{g#n_-CRxj zwRyn4h4M>(SK-n76zmn&*c1ul-jwppOt0goV=q!GkMrw8sMviwC-1Bc?()I)ZClvM z7U_f1yXMP^lo#Wb#t>tq_{547|^)94C=&&i5`cW>YD ze*XO`QxyhfhQs!gCS%Z~Q1QlPd{-R!51!<1XX80ym=dqlQUe~Q_HANIFCF0;m|Zy| z2})TriCox=_3=1I067k#S+ka9X6weJ0c;lOf8jRZS)257)K8IhAoTN#qY`%97#o%E zwU&jS5R`s&6wRWHD{=0*EofVgy7y4;lr*%pI58(GdF{{CGt}}_mRTLn7kt95penKC zoRiRQstVEDW4HXzMPo85t2MYtvd!^J7^`Z7@k~`;{ zhw;{TcaG0hxh~mRc+1nr0gmdN_qrz?5dX(oe$vHhHa0{kKsTsSm$?+jSTX(;rfWU& zoa-oZew)m_yv%Z*+1pNahP(^$^CQ>sfGbIc#NK_6uEv)#`)(Qv7quIYS*BDO<|XlF4PX!K20f0Eem^-P)_(Q)%PCj`Re1uS=YLvxBI^<>7sT=u1 zgVhTI-n)TSk0fj5014b+9}lyNR;z&bVX6uW;33QoR?gD-Z8|4NR90q&xetcroPp`r zV*aua*_JtygkQLEMx0ef$8%YY^o~_eCtm-dXji9P%W66Tc;iS=N$+~K(X5|Wt^Iv4 z!k-UPuvEX+zi*!_fp9o)^t5LQT)xj}_PX6c48IvBE~J4xXqmX5+FkWtjILJStvt^J zJfBwap2&kV84#3}@!NIxjpFBCU6Z;U$DAxQI(BMY%#QQ$v>NKDL(()fQIw~e^_4}B zRLgJ8mcbugHs_rnA7{&i;fnr@ygmWSfTzjC5#_4P^%v#CbHA??mJ-zEH|fC zOuTXyH#d3hF9hzfguUuWr_OgzNoGW7-#bojm`F{6s|eXNrJsF9ut?RlP$Bd6p!M$d zaMbaup0ABM>a4>>kRL6xq0-JpMP&mPwHc_rb4TN^^MQSmIrIrSQ(Z1zM4f^Ijr-t=GDPb35iTbniz;a18YvYT(aNFxs! z>_I=&-ml!^ysyT|W%=M87nC5zpF&|K1gNdnNn^Fa>H(8)b(B>uZEKnX9d2Uz1?cv& zp~R@T#Mm{>V2ksF3BG1|hP}na*tev7HG`ff8psQUz;013BqxWb*c8Z9tLOTa>Y0ec z*pJ?NVug7UylO$t{?U5cXR~M5n-sL=2>zcC3MSkbRefB0el!dpdLYmIjxH|lHtU$% zc|`@yVsv^{tEPasWDPzTOT2S#smqlFwh*x){sy{!1#&et#O7Zc|I`p)bMvL4!Wph4 zpSY+gBUtD+{vTN=vh#rJFO9yI9pQVUPFBvQ!Fh{TVzn`vg%W_Ox6ueTt=DJ>lH@p( zGp{~q#7F_!tX)))2%9b~Ad&B~(?Xi5H#c*-Z0Ii4Vyj% z6*f3iZw1>b7B{oX1P{ZK<`JMFdzLV~?etPYMk{qaGO88|#uHOEzp7bc0C#OLg2Udr z@&TP