From a346c817fe56b5c5c79c25aa2be8cddac9df5ba8 Mon Sep 17 00:00:00 2001 From: dsc Date: Fri, 4 Mar 2011 17:22:48 -0800 Subject: [PATCH] Shield mostly works! --- data/config.yaml | 1 + data/types/units.yaml | 19 ++- pavement.py | 35 ++++- src/Y/class.cjs | 37 ++-- src/evt.cjs | 13 +- src/ezl/math/circle.cjs | 49 +++++ src/tanks/constants.cjs | 13 +- src/tanks/game.cjs | 6 +- src/tanks/globals.js | 6 - src/tanks/map/pathing/circular-trajectory.cjs | 59 ++++++ src/tanks/map/pathing/index.cjs | 7 +- src/tanks/map/pathing/linear-trajectory.cjs | 246 +++++++++++++++++++++++++ src/tanks/map/pathing/map-blockers.cjs | 3 +- src/tanks/map/pathing/map-pathing.cjs | 4 +- src/tanks/map/pathing/map-searching.cjs | 4 +- src/tanks/map/pathing/trajectory.cjs | 246 ------------------------- src/tanks/map/pathing/traversal.cjs | 29 ++-- src/tanks/thing/accessory.cjs | 61 ------ src/tanks/thing/bullet.cjs | 4 +- src/tanks/thing/component.cjs | 43 +++++ src/tanks/thing/index.cjs | 18 +- src/tanks/thing/player.cjs | 38 ++-- src/tanks/thing/shield.cjs | 94 ++++++++++ src/tanks/thing/tank.cjs | 5 +- src/tanks/thing/thing.cjs | 12 +- src/tanks/ui/main.cjs | 14 +- www/css/lttl.css | 2 +- www/scripts.html | 2 +- 28 files changed, 656 insertions(+), 414 deletions(-) create mode 100644 src/ezl/math/circle.cjs create mode 100644 src/tanks/map/pathing/circular-trajectory.cjs create mode 100644 src/tanks/map/pathing/linear-trajectory.cjs delete mode 100644 src/tanks/map/pathing/trajectory.cjs delete mode 100644 src/tanks/thing/accessory.cjs create mode 100644 src/tanks/thing/component.cjs create mode 100644 src/tanks/thing/shield.cjs diff --git a/data/config.yaml b/data/config.yaml index 22f7b01..ce7027f 100644 --- a/data/config.yaml +++ b/data/config.yaml @@ -7,6 +7,7 @@ debug: createGridTable : false showGridCoords : false showFpsGraph : false + allowViewportScroll : false map: refSize : &ref_size 50 rockSizeMin : 12.5 diff --git a/data/types/units.yaml b/data/types/units.yaml index 899fba6..4fbbada 100644 --- a/data/types/units.yaml +++ b/data/types/units.yaml @@ -6,7 +6,8 @@ defaults: lootTable : '' stats: hp : 1 # health - move : 1.0 # move speed (squares/sec) + move : 1.0 # move speed (squares/sec or orbits/sec) + rotate : 1.0 # rotation speed (full-turns/sec) power : 1 # attack power speed : 0.5 # attack cool (sec) accuracy : 1.0 # chance of shooting where aiming @@ -112,5 +113,19 @@ types: turret : '#C13B00' barrel : '#244792' - + shield: + name: Shield Sphere + desc: A protective sphere of energy. + tags: [ 'shield' ] + symbol: tanks/thing/shield.Shield + stats: + hp : 1 + move : 0.5 + power : 1 + orbit : 9 + width : 9 + height : 9 + color: '#0A9CFF' + + diff --git a/pavement.py b/pavement.py index dd1cb39..21ca152 100755 --- a/pavement.py +++ b/pavement.py @@ -118,7 +118,7 @@ def update_version(): with path('build/versioned-build.html').open('w') as f: f.write('\n'.format(git_version)) with path('build/versioned-app.html').open('w') as f: - f.write('\n'.format(git_version, APP_FILE)) @task @@ -134,6 +134,8 @@ def build(): "Builds the Tanks project" pass + + @task @needs('build') def compress(outfile='build/'+APP_FILE, yuic_jar='~/bin/yuic.jar'): @@ -143,6 +145,37 @@ def compress(outfile='build/'+APP_FILE, yuic_jar='~/bin/yuic.jar'): sh('cat %s | java -jar %s --type js -o %s' % (mods.replace('\n', ' '), yuic_jar, outfile)) @task +@needs('build') +def compress_each(yuic_jar='~/bin/yuic.jar'): + yuic_jar = path(yuic_jar).expand() + info('Compressing Each Source File (YUI Compressor=%s)' % yuic_jar) + for dirpath, dirs, files in os.walk(BUILD_DIR): + dirpath = path(dirpath) + for f in files: + if not f.endswith('.js') or f.endswith('.min.js'): + continue + in_ = dirpath / f + out = dirpath / f[:-3] + '.min.js' + sh('java -jar %s --type js -o %s %s' % (yuic_jar, out, in_)) + +@task +def combine_all(outfile='build/'+APP_FILE, minified=True, add_newlines=True): + info('Concatenating Source Files') + mods = commonjs(file_list=True, capture=True) + if minified: + mods = mods.replace('.js\n', '.min.js\n') + if add_newlines: + sh('echo "" > .NL') + modlist = mods.replace('\n', ' .NL ') + else: + modlist = mods.replace('\n', ' ') + + sh('cat %s > %s' % (modlist, outfile)) + + if add_newlines: + sh('rm .NL') + +@task def clean(): "Cleans dep cache and build files" commonjs(clean=True) diff --git a/src/Y/class.cjs b/src/Y/class.cjs index c70ec20..86f8d0f 100644 --- a/src/Y/class.cjs +++ b/src/Y/class.cjs @@ -97,12 +97,9 @@ function Class(className, Parent, members) { parentMembers = Parent[P] || {}; } - // Creates a new function with the appropriate name - // based on the className. - var NewClass, constructor = - 'var '+className+' = NewClass = '+ - (''+_Class).replace(_Class.name, className) + ';'; - eval(constructor); + // Creates a new function with the appropriate name based on the className. + var constructor = ('('+_Class+')').replace(_Class.name, className) + , NewClass = eval(constructor); // Copy Class statics for (var i=0, L=classStatics.length; i= 0) + return 1; + if (tb < 0 && ta >= 0) + return -1; + return op.cmp(abs(ta), abs(tb)); + }, + +}) +; diff --git a/src/tanks/map/pathing/index.cjs b/src/tanks/map/pathing/index.cjs index c9b122e..4eb559e 100644 --- a/src/tanks/map/pathing/index.cjs +++ b/src/tanks/map/pathing/index.cjs @@ -7,7 +7,8 @@ require('tanks/map/pathing/map-searching'); require('Y').Y .extend(exports, { - 'Map' : require('tanks/map/pathing/map').Map, - 'Traversal' : require('tanks/map/pathing/traversal').Traversal, - 'Trajectory' : require('tanks/map/pathing/trajectory').Trajectory + 'Map' : require('tanks/map/pathing/map').Map, + 'Traversal' : require('tanks/map/pathing/traversal').Traversal, + 'LinearTrajectory' : require('tanks/map/pathing/linear-trajectory').LinearTrajectory, + 'CircularTrajectory' : require('tanks/map/pathing/circular-trajectory').CircularTrajectory }); diff --git a/src/tanks/map/pathing/linear-trajectory.cjs b/src/tanks/map/pathing/linear-trajectory.cjs new file mode 100644 index 0000000..4e439b0 --- /dev/null +++ b/src/tanks/map/pathing/linear-trajectory.cjs @@ -0,0 +1,246 @@ +var Y = require('Y').Y +, op = require('Y/op') +, Vec = require('ezl/math/vec').Vec +, Line = require('ezl/math/line').Line +, Rect = require('ezl/math/rect').Rect +, BoundingBox = require('ezl/loc').BoundingBox + +, Thing = require('tanks/thing/thing').Thing + +, BOUND_SIZE_RATIO = 0.75 +, + + +LinearTrajectory = +exports['LinearTrajectory'] = +Line.subclass('LinearTrajectory', { + tCurrent : 0, + + + init : function initLinearTrajectory(owner, x1,y1, x2,y2, tdist, tCurrent){ + Y.bindAll(this, 'compare', 'closer', 'intersects'); + + this.owner = owner; + this.game = owner.game; + this.map = this.game.map; + + this.reset(x1,y1, x2,y2, tdist, tCurrent); + }, + + reset : function reset(x1,y1, x2,y2, tdist, tCurrent){ + if (x1 instanceof Array && y1 instanceof Array) { + tdist = x2; + y2 = y1[1]; x2 = y1[0]; + y1 = x1[1]; x1 = x1[0]; + } + + // init with raw numbers to do calculations + Line.init.call(this, x1,y1, x2,y2, tdist || this.tdist); + + // Find appropriate edge in direction of line + 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) + ; + + // Move goal point beyond far wall to avoid rotations post-reflection + x2 = this.x2 = edge.x; + y2 = this.y2 = edge.y; + this.p2 = new Vec(x2,y2); + Vec.init.call(this, x2-x1, y2-y1); + + this.tCurrent = tCurrent || 0; + this.resetBound(); + return this; + }, + + // 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(){ + this.tBound = + BOUND_SIZE_RATIO * Math.min( + Math.abs(this.owner.width / this.pa), + Math.abs(this.owner.height / this.pb) ); + return this; + }, + + clone : function clone(){ + return new LinearTrajectory(this.owner, this.x1,this.y1, this.x2,this.y2, this.tdist, this.tCurrent); + }, + + + intersects : function intersects(x,y){ + var o = x; + if (o.bbox) + o = o.bbox; + if (o.isRect || o instanceof Rect) + return o.intersects(this); + return Line.fn.intersects.call(this, x,y); + }, + + intersection : function intersection(line){ + if (!line) return null; + + // Line, Vec + if (line instanceof Line || line instanceof Vec) + return Line.fn.intersection.call(this, line); + + // Rect + var o = line; + if (o.bbox) o = o.bbox; + + var hits = [], hit + , sides = o.sides; + for (var n=0, L=sides.length, side=sides[n]; n 1 ) + hits.sort(this.compare); + return hits[0]; + }, + + /** + * Compares how distant in the future two objects are on this trajectory. + * Objects that have been passed are always further away than those in the future, + * but otherwise the comparison is performed by absolute distance. + * @return -1 if a closer b, 1 if a further b, 0 if a same as b + */ + compare : function compare(a, b){ + var _a = a, _b = b; + + if ( !(a instanceof Array) ) a = this.intersection(a); + if ( !(b instanceof Array) ) b = this.intersection(b); + + // if (a.bbox || a.intersection || a instanceof Rect) a = this.intersection(a); + // if (b.bbox || b.intersection || b instanceof Rect) b = this.intersection(b); + + if ( !(a || b) ) return 0; + if ( !a ) return 1; + if ( !b ) return -1; + + var abs = Math.abs + , t = this.tCurrent + + , xa = this.calcX(a.y), ya = this.calcY(a.x) + , ta = this.iparametric(xa,ya) - t + + , xb = this.calcX(b.y), yb = this.calcY(b.x) + , tb = this.iparametric(xb,yb) - t + ; + + // If one has passed, return the other + if ( isNaN(ta) || !isFinite(ta) || (ta < 0 && tb >= 0) ) + return 1; + if ( isNaN(tb) || !isFinite(tb) || (tb < 0 && ta >= 0) ) + return -1; + + return op.cmp(abs(ta), abs(tb)); + }, + + closer : function closer(o1, o2){ + return this.compare(o1,o2) === 1 ? o2 : o1; + }, + + sortClosest : function sortClosest(o1, o2){ + var things; + if (o1 instanceof Y.YArray) + things = o1; + else if (o1 instanceof Array) + things = Y(o1); + else + things = new Y(arguments); + + return things.sort( this.compare ); + }, + + closest : function closest(o1, o2){ + return this.sortClosest.apply(this, arguments).shift(); + }, + + comesWithin : function comesWithin(pt, w,h){ + if ( !this.owner.loc ) + return false; + + if (pt instanceof Thing) pt = pt.loc; + + if ( w === undefined ){ + w = 0; h = 0; + } else if ( Y.isNumber(w.width) ){ + h = w.height; w = w.width; + } + + var cur = this.owner.loc + , fx = this.calcX(pt.y), fy = this.calcY(pt.x) + , t = this.iparametric(cur.x, cur.y) + , ft = this.iparametric(fx,fy) + , dw = Math.abs(fx - pt.x), dh = Math.abs(fy - pt.y) + ; + return ( t <= ft && (dw <= w || dh <= h) ); + }, + + canSee : function canSee(obj, ignore){ + ignore = [this.owner].concat(ignore || []); + // var blockers = this.map.denseWalls + // .apply('remove', ignore) + // .filter(this.intersects) + // .sort( this.compare ) + var blockers = this.map.denseWalls + .apply('remove', ignore).concat([ obj ]) + .filter(this.intersects) + .sort(this.compare) + , blocker = blockers.first() + ; + return !!(blocker === obj); + }, + + pathBlocked : function pathBlocked(obj, ignore){ + // var walls, blockers + // if (this.owner.isProjectile) + // walls = this.map.denseWalls; + // else + // walls = this.map.innerWalls; // FIXME: won't filter out concave intersections with the bounds + + var blockers = this.map.innerWalls + .concat( this.game.units ) + .apply('remove', [this.owner].concat(ignore || []) ) + .filter( this.intersects ) + .sort( this.compare ) + + , blocker = blockers.first() + ; + + return (blocker === obj ? false : blocker); + }, + + calcIntersectTraj : function calcIntersectTraj(fireLoc, speed){ + // targetVelocityVector = math.Vector3(0, 0, 0) * aTargetSpeed + // interceptVelocityVector = math.Vector3(0, 0, 0) * aBulletSpeed + // + // tDelta = aTargetPosition - aFiringPosition + // + // Vr = targetVelocityVector - interceptVelocityVector + // a = Vr.Dot( Vr ) - aBulletSpeed * aBulletSpeed + // b = 2 * Vr.Dot( tDelta ) + // c = tDelta.Dot ( tDelta ) + // + // deter = b * b - 4 * ( a * c ) + // + // if deter < 0: + // return -1 + // else: + // ETN = 2 * ( c / pdmath.sqrt(deter) - b ) + // return ETN + + }, + + + toString : function(){ + return 'T['+this.p1+', '+this.p2+', slope='+this.slope.toFixed(3)+']'; + } +}); diff --git a/src/tanks/map/pathing/map-blockers.cjs b/src/tanks/map/pathing/map-blockers.cjs index 680fb2e..9a80595 100644 --- a/src/tanks/map/pathing/map-blockers.cjs +++ b/src/tanks/map/pathing/map-blockers.cjs @@ -59,8 +59,7 @@ Y.core.extend(Map.fn, { _filterBlockers : function _filterBlockers(v){ return (v.blocking === BoundsType.BLOCKING - || v.blocking === BoundsType.IRREGULAR - || v.blocking === BoundsType.COMPLEX ); + || v.blocking === BoundsType.IRREGULAR ); }, wallObstructs : function wallObstructs(line){ diff --git a/src/tanks/map/pathing/map-pathing.cjs b/src/tanks/map/pathing/map-pathing.cjs index c107c46..0ee31c7 100644 --- a/src/tanks/map/pathing/map-pathing.cjs +++ b/src/tanks/map/pathing/map-pathing.cjs @@ -235,8 +235,8 @@ Y.subclass('Square', new Vec(0,0), { _filterBlocked : function filterBlocked(v, r){ // FIXME: calc bbox for new loc to testCollide() var bbox = null; - return ( v.blocking === BoundsType.BLOCKING && !(v.isBoundary || v.isProjectile) ) - || ((v.blocking === BoundsType.IRREGULAR || v.blocking === BoundsType.COMPLEX) && v.testCollide(this.agent,this,bbox)); + return (v.blocking === BoundsType.BLOCKING && !(v.isBoundary || v.isProjectile) ) + || (v.blocking === BoundsType.IRREGULAR && v.testCollide(this.agent,this,bbox)); }, getNeighbors : function getNeighbors(){ diff --git a/src/tanks/map/pathing/map-searching.cjs b/src/tanks/map/pathing/map-searching.cjs index 8be836a..1650944 100644 --- a/src/tanks/map/pathing/map-searching.cjs +++ b/src/tanks/map/pathing/map-searching.cjs @@ -7,7 +7,7 @@ var Y = require('Y').Y , manhattan = vec.manhattan , Map = require('tanks/map/pathing/map').Map -, Trajectory = require('tanks/map/pathing/trajectory').Trajectory +, LinearTrajectory = require('tanks/map/pathing/linear-trajectory').LinearTrajectory , Bullet = require('tanks/thing/bullet').Bullet , _X = 0, _Y = 1 @@ -92,5 +92,5 @@ function nearEnemyFilter(agent){ function nearEnemyInSightFilter(agent){ var me = this; // Runs in the context of the 'me' unit; @see this.findNearLike() return ( agent.isCombatant && agent.align !== me.align && - new Trajectory(me, me.loc, agent.loc).canSee(agent) ); + new LinearTrajectory(me, me.loc, agent.loc).canSee(agent) ); } diff --git a/src/tanks/map/pathing/trajectory.cjs b/src/tanks/map/pathing/trajectory.cjs deleted file mode 100644 index fc3f14f..0000000 --- a/src/tanks/map/pathing/trajectory.cjs +++ /dev/null @@ -1,246 +0,0 @@ -var Y = require('Y').Y -, op = require('Y/op') -, Vec = require('ezl/math/vec').Vec -, Line = require('ezl/math/line').Line -, Rect = require('ezl/math/rect').Rect -, BoundingBox = require('ezl/loc').BoundingBox - -, Thing = require('tanks/thing/thing').Thing - -, BOUND_SIZE_RATIO = 0.75 -, - - -Trajectory = -exports['Trajectory'] = -Line.subclass('Trajectory', { - tCurrent : 0, - - - init : function initTrajectory(owner, x1,y1, x2,y2, tdist, tCurrent){ - Y.bindAll(this, 'compare', 'closer', 'intersects'); - - this.owner = owner; - this.game = owner.game; - this.map = this.game.map; - - this.reset(x1,y1, x2,y2, tdist, tCurrent); - }, - - reset : function reset(x1,y1, x2,y2, tdist, tCurrent){ - if (x1 instanceof Array && y1 instanceof Array) { - tdist = x2; - y2 = y1[1]; x2 = y1[0]; - y1 = x1[1]; x1 = x1[0]; - } - - // init with raw numbers to do calculations - Line.init.call(this, x1,y1, x2,y2, tdist || this.tdist); - - // Find appropriate edge in direction of line - 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) - ; - - // Move goal point beyond far wall to avoid rotations post-reflection - x2 = this.x2 = edge.x; - y2 = this.y2 = edge.y; - this.p2 = new Vec(x2,y2); - Vec.init.call(this, x2-x1, y2-y1); - - this.tCurrent = tCurrent || 0; - this.resetBound(); - return this; - }, - - // 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(){ - this.tBound = - BOUND_SIZE_RATIO * Math.min( - Math.abs(this.owner.width / this.pa), - Math.abs(this.owner.height / this.pb) ); - return this; - }, - - clone : function clone(){ - return new Trajectory(this.owner, this.x1,this.y1, this.x2,this.y2, this.tdist, this.tCurrent); - }, - - - intersects : function intersects(x,y){ - var o = x; - if (o.bbox) - o = o.bbox; - if (o.isRect || o instanceof Rect) - return o.intersects(this); - return Line.fn.intersects.call(this, x,y); - }, - - intersection : function intersection(line){ - if (!line) return null; - - // Line, Vec - if (line instanceof Line || line instanceof Vec) - return Line.fn.intersection.call(this, line); - - // Rect - var o = line; - if (o.bbox) o = o.bbox; - - var hits = [], hit - , sides = o.sides; - for (var n=0, L=sides.length, side=sides[n]; n 1 ) - hits.sort(this.compare); - return hits[0]; - }, - - /** - * Compares how distant in the future two objects are on this trajectory. - * Objects that have been passed are always further away than those in the future, - * but otherwise the comparison is performed by absolute distance. - * @return -1 if a closer b, 1 if a further b, 0 if a same as b - */ - compare : function compare(a, b){ - var _a = a, _b = b; - - if ( !(a instanceof Array) ) a = this.intersection(a); - if ( !(b instanceof Array) ) b = this.intersection(b); - - // if (a.bbox || a.intersection || a instanceof Rect) a = this.intersection(a); - // if (b.bbox || b.intersection || b instanceof Rect) b = this.intersection(b); - - if ( !(a || b) ) return 0; - if ( !a ) return 1; - if ( !b ) return -1; - - var abs = Math.abs - , t = this.tCurrent - - , xa = this.calcX(a.y), ya = this.calcY(a.x) - , ta = this.iparametric(xa,ya) - t - - , xb = this.calcX(b.y), yb = this.calcY(b.x) - , tb = this.iparametric(xb,yb) - t - ; - - // If one has passed, return the other - if ( isNaN(ta) || !isFinite(ta) || (ta < 0 && tb >= 0) ) - return 1; - if ( isNaN(tb) || !isFinite(tb) || (tb < 0 && ta >= 0) ) - return -1; - - return op.cmp(abs(ta), abs(tb)); - }, - - closer : function closer(o1, o2){ - return this.compare(o1,o2) === 1 ? o2 : o1; - }, - - sortClosest : function sortClosest(o1, o2){ - var things; - if (o1 instanceof Y.YArray) - things = o1; - else if (o1 instanceof Array) - things = Y(o1); - else - things = new Y(arguments); - - return things.sort( this.compare ); - }, - - closest : function closest(o1, o2){ - return this.sortClosest.apply(this, arguments).shift(); - }, - - comesWithin : function comesWithin(pt, w,h){ - if ( !this.owner.loc ) - return false; - - if (pt instanceof Thing) pt = pt.loc; - - if ( w === undefined ){ - w = 0; h = 0; - } else if ( Y.isNumber(w.width) ){ - h = w.height; w = w.width; - } - - var cur = this.owner.loc - , fx = this.calcX(pt.y), fy = this.calcY(pt.x) - , t = this.iparametric(cur.x, cur.y) - , ft = this.iparametric(fx,fy) - , dw = Math.abs(fx - pt.x), dh = Math.abs(fy - pt.y) - ; - return ( t <= ft && (dw <= w || dh <= h) ); - }, - - canSee : function canSee(obj, ignore){ - ignore = [this.owner].concat(ignore || []); - // var blockers = this.map.denseWalls - // .apply('remove', ignore) - // .filter(this.intersects) - // .sort( this.compare ) - var blockers = this.map.denseWalls - .apply('remove', ignore).concat([ obj ]) - .filter(this.intersects) - .sort(this.compare) - , blocker = blockers.first() - ; - return !!(blocker === obj); - }, - - pathBlocked : function pathBlocked(obj, ignore){ - // var walls, blockers - // if (this.owner.isProjectile) - // walls = this.map.denseWalls; - // else - // walls = this.map.innerWalls; // FIXME: won't filter out concave intersections with the bounds - - var blockers = this.map.innerWalls - .concat( this.game.units ) - .apply('remove', [this.owner].concat(ignore || []) ) - .filter( this.intersects ) - .sort( this.compare ) - - , blocker = blockers.first() - ; - - return (blocker === obj ? false : blocker); - }, - - calcIntersectTraj : function calcIntersectTraj(fireLoc, speed){ - // targetVelocityVector = math.Vector3(0, 0, 0) * aTargetSpeed - // interceptVelocityVector = math.Vector3(0, 0, 0) * aBulletSpeed - // - // tDelta = aTargetPosition - aFiringPosition - // - // Vr = targetVelocityVector - interceptVelocityVector - // a = Vr.Dot( Vr ) - aBulletSpeed * aBulletSpeed - // b = 2 * Vr.Dot( tDelta ) - // c = tDelta.Dot ( tDelta ) - // - // deter = b * b - 4 * ( a * c ) - // - // if deter < 0: - // return -1 - // else: - // ETN = 2 * ( c / pdmath.sqrt(deter) - b ) - // return ETN - - }, - - - toString : function(){ - return 'T['+this.p1+', '+this.p2+', slope='+this.slope.toFixed(3)+']'; - } -}); diff --git a/src/tanks/map/pathing/traversal.cjs b/src/tanks/map/pathing/traversal.cjs index 9f8ab9e..45b185e 100644 --- a/src/tanks/map/pathing/traversal.cjs +++ b/src/tanks/map/pathing/traversal.cjs @@ -7,10 +7,15 @@ var Y = require('Y').Y Traversal = exports['Traversal'] = Y.subclass('Traversal', { - ignore : null, // objects to ignore when determining blockers + thing : null, // agent and its various state-objects + game : null, + map : null, + trajectory : null, // movement path of the agent + ignore : null, // objects to ignore when determining blockers isBlocked : false, to : null, // furthest point reached + bbox : null, // current agent position (cloned) remaining : 0, // time left unconsumed due to blocker blocker : null, // blocking object side : null, // collision side of blocker (Line) @@ -20,16 +25,15 @@ Y.subclass('Traversal', { init : function initTraversal(thing, trajectory, ignore){ this.ignore = Y(ignore || []); - this.thing = thing; - this.game = thing.game; - this.map = thing.game.map; - this.bbox = thing.bbox.clone(); + this.thing = thing; + this.game = thing.game; + this.map = thing.game.map; + this.bbox = thing.bbox.clone(); this.trajectory = trajectory || thing.trajectory; }, traverse : function traverse(t, tx,ty){ - var tr = this.trajectory; this.remaining = t; do { @@ -92,7 +96,8 @@ Y.subclass('Traversal', { * Filters found obstructions to keep only those not ignored and blocking. */ checkBlocker : function checkBlocker(blocker){ - var blocking = blocker.blocking + var oBlocking = this.thing.blocking + , blocking = blocker.blocking , density = blocker.density; // All blockers after the first are ignored @@ -100,11 +105,9 @@ Y.subclass('Traversal', { // Skip passable objects || (blocking === BoundsType.PASSABLE) - // Ask irregular objects if we hit - || (blocking === BoundsType.IRREGULAR && !blocker.testCollide(this.thing,this.to,this.bbox,this)) - - // Ask complex objects if we hit, and recall they might go busta on us - || blocking === BoundsType.COMPLEX && !blocker.testCollide(this.thing,this.to,this.bbox,this) + // Ask irregular objects if we hit (and recall it might change this.ignore or this.blocker) + || (oBlocking === BoundsType.IRREGULAR && !this.thing.testCollide(blocker,this.to,null,this)) + || (blocking === BoundsType.IRREGULAR && !blocker.testCollide(this.thing,this.to,this.bbox,this)) // Filter out Sparse objects if bullet || (density === DensityType.SPARSE && this.thing.isProjectile) ) @@ -121,7 +124,7 @@ Y.subclass('Traversal', { } this.isBlocked = true; - if (!this.blocker) this.blocker = blocker; // BoundsType.COMPLEX might switch blockers on us + if (!this.blocker) this.blocker = blocker; // BoundsType.IRREGULAR might switch blockers on us return true; }, diff --git a/src/tanks/thing/accessory.cjs b/src/tanks/thing/accessory.cjs deleted file mode 100644 index b67e717..0000000 --- a/src/tanks/thing/accessory.cjs +++ /dev/null @@ -1,61 +0,0 @@ -var Y = require('Y').Y -, constants = require('tanks/constants') -, BoundsType = constants.BoundsType -, DensityType = constants.DensityType -, Thing = require('tanks/thing/thing').Thing -, - -Accessory = -exports['Accessory'] = -Thing.subclass('Accessory', { - __bind__ : [ 'onCollide' ], - - // Instance - blocking : BoundsType.BLOCKING, - - isAccessory : true, - - active : true, // Agent takes actions? - isRenderable : true, // Agent will present itself for rendering when ready // FIXME: stupid hack - isReflective : false, // Projectiles bounce off agent rather than explode? - hasInventory : false, // Agent can acquire items? - dropOnDeath : false, // Agent drops all items in inventory on death? - - width : 6, - height : 6, - - color : '#0A9CFF', - - stats : {}, // radial speed in revolutions/sec - - - - init : function initAccessory(owner){ - this.owner = owner; - this.game = owner.game; - - Thing.init.call(this, owner.align); - - }, - - testCollide : function testCollide(agent, to, bbox, traversal){ - return false; - }, - - onCollide : function onCollide(evt){ - - }, - - render : function render(parent){ - this.remove(); - - var loc = this.loc; - this.shape = new Circle(this.width/2) - .position(loc.x, loc.y) - .fill(this.color) - .appendTo( parent ); - - return this; - } -}) -; diff --git a/src/tanks/thing/bullet.cjs b/src/tanks/thing/bullet.cjs index 03d35e3..ed2cd84 100644 --- a/src/tanks/thing/bullet.cjs +++ b/src/tanks/thing/bullet.cjs @@ -13,7 +13,7 @@ var Y = require('Y').Y , stat = require('tanks/effects/stat') , Explosion = require('tanks/fx/explosion').Explosion , Thing = require('tanks/thing/thing').Thing -, Trajectory = require('tanks/map/pathing/trajectory').Trajectory +, LinearTrajectory = require('tanks/map/pathing/linear-trajectory').LinearTrajectory , Traversal = require('tanks/map/pathing/traversal').Traversal , @@ -63,7 +63,7 @@ Thing.subclass('Bullet', { Thing.init.call(this, owner.align); this.position(x1,y1); - this.trajectory = new Trajectory(this, x1,y1, x2,y2, this.movePerMs); + this.trajectory = new LinearTrajectory(this, x1,y1, x2,y2, this.movePerMs); this.on('collide', this.onCollide); }, diff --git a/src/tanks/thing/component.cjs b/src/tanks/thing/component.cjs new file mode 100644 index 0000000..b2323c4 --- /dev/null +++ b/src/tanks/thing/component.cjs @@ -0,0 +1,43 @@ +var Y = require('Y').Y +, constants = require('tanks/constants') +, BoundsType = constants.BoundsType +, DensityType = constants.DensityType +, Thing = require('tanks/thing/thing').Thing +, + +/** + * Units that need to be activated in order after another unit has acted. + */ +Component = +exports['Component'] = +Thing.subclass('Component', { + + // Instance + blocking : BoundsType.IRREGULAR, // Need to allow shields etc to passthrough like-aligned units + + isComponent : true, + + active : false, // Owner will notify us to ensure we go at the right time + isRenderable : true, // Agent will present itself for rendering when ready // FIXME: stupid hack + isReflective : false, // Projectiles bounce off agent rather than explode? + hasInventory : false, // Agent can acquire items? + dropOnDeath : false, // Agent drops all items in inventory on death? + + width : 6, + height : 6, + + color : '#0A9CFF', + + stats : {}, // radial speed in revolutions/sec + + + + init : function initComponent(owner){ + this.owner = owner; + this.game = owner.game; + + Thing.init.call(this, owner.align); + } + +}) +; diff --git a/src/tanks/thing/index.cjs b/src/tanks/thing/index.cjs index 980882f..004c378 100644 --- a/src/tanks/thing/index.cjs +++ b/src/tanks/thing/index.cjs @@ -1,16 +1,18 @@ var Y = require('Y').Y -, tank = require('tanks/thing/tank') , bullet = require('tanks/thing/bullet') +, component = require('tanks/thing/component') , item = require('tanks/thing/item') -, accessory = require('tanks/thing/accessory') , player = require('tanks/thing/player') +, shield = require('tanks/thing/shield') +, tank = require('tanks/thing/tank') , thing = require('tanks/thing/thing') ; Y.core.extend(exports, { - 'Tank' : tank.Tank, - 'Bullet' : bullet.Bullet, - 'Item' : item.Item, - 'Accessory' : accessory.Accessory, - 'Player' : player.Player, - 'Thing' : thing.Thing + 'Bullet' : bullet.Bullet, + 'Component' : component.Component, + 'Item' : item.Item, + 'Player' : player.Player, + 'Shield' : shield.Shield, + 'Tank' : tank.Tank, + 'Thing' : thing.Thing }); diff --git a/src/tanks/thing/player.cjs b/src/tanks/thing/player.cjs index 471cea1..d438aa9 100644 --- a/src/tanks/thing/player.cjs +++ b/src/tanks/thing/player.cjs @@ -8,6 +8,7 @@ var Y = require('Y').Y , Tank = require('tanks/thing/tank').Tank , Inventory = require('tanks/inventory/inventory').Inventory , Inventoried = require('tanks/mixins/inventoried').Inventoried +, Shield = require('tanks/thing/shield').Shield , @@ -23,7 +24,7 @@ Tank.subclass('Player', { stats: { hp : 1, // health move : 0.90, // move speed (squares/sec) - rotate : HALF_PI, // rotation speed (radians/sec) + rotate : 1.0, // rotation speed (rotations/sec) power : 1, // attack power speed : 0.5, // attack cool (sec) shots : 5 // max projectiles in the air at once @@ -32,11 +33,10 @@ Tank.subclass('Player', { /// Instance /// align : null, - replayMode : false, - init : function init(align, replay){ + init : function init(align){ Tank.init.call(this, align); this.gameLog = []; @@ -44,19 +44,20 @@ Tank.subclass('Player', { this.activeKeys = new Y.YArray(); this.inventory = new Inventory(this); - if (this.replayMode) { - this.replay = replay - this.nextMove = replay.shift(); - - } else { - $(document) - .bind('keydown', this.keydown) - .bind('keyup', this.keyup) - .bind('mousemove', this.mousemove); - $('#viewport') - .bind('mousedown', this.mousedown) - .bind('mouseup', this.mouseup); - } + $(document) + .bind('keydown', this.keydown) + .bind('keyup', this.keyup) + .bind('mousemove', this.mousemove); + $('#viewport') + .bind('mousedown', this.mousedown) + .bind('mouseup', this.mouseup); + + var self = this; + this.game.on('ready', function(){ + for (var i=0; i<1; i += 0.25) { + self.components.push( Shield.create('shield', self, i * 2*Math.PI) ); + } + }); }, destroy : function destroy(){ @@ -110,8 +111,10 @@ Tank.subclass('Player', { } } - if ( !action ) + if ( !action ) { + this.components.invoke('act', elapsed, now); return this; + } var data = action.data; switch (action.type) { @@ -130,6 +133,7 @@ Tank.subclass('Player', { break; } + this.components.invoke('act', elapsed, now); return this; }, diff --git a/src/tanks/thing/shield.cjs b/src/tanks/thing/shield.cjs new file mode 100644 index 0000000..ff7d62c --- /dev/null +++ b/src/tanks/thing/shield.cjs @@ -0,0 +1,94 @@ +var Y = require('Y').Y +, Circle = require('ezl/shape/circle').Circle + +, constants = require('tanks/constants') +, BoundsType = constants.BoundsType +, Thing = require('tanks/thing/thing').Thing +, Component = require('tanks/thing/component').Component +, Traversal = require('tanks/map/pathing/traversal').Traversal +, CircularTrajectory = require('tanks/map/pathing/circular-trajectory').CircularTrajectory + +, SQRT_TWO = Math.sqrt(2) +, TWO_PI = 2 * Math.PI +, + +Shield = +exports['Shield'] = +Component.subclass('Shield', { + // __bind__ : [ 'onCollide' ], + + // Instance + blocking : BoundsType.IRREGULAR, // Need to allow shields etc to passthrough like-aligned units + + isComponent : true, + isShield : true, + + active : false, // Owner will notify us to ensure we go at the right time + isRenderable : true, // Agent will present itself for rendering when ready // FIXME: stupid hack + isReflective : false, // Projectiles bounce off agent rather than explode? + hasInventory : false, // Agent can acquire items? + + orbit : 9, + width : 9, + height : 9, + + color : '#0A9CFF', + + stats : {}, // radial speed in revolutions/sec + + // state + radius : 0, + radsPerTick : 0, + radialPosition : 0, + + + + init : function initShield(owner, radialPosition){ + Component.init.call(this, owner); + + var obb = owner.bbox, oloc = owner.loc; + this.radialPosition = radialPosition || 0; + this.radius = this.orbit + SQRT_TWO * Math.max(obb.width, obb.height) / 2; + this.radsPerTick = TWO_PI * this.stats.move.val / 1000 * MS_PER_FRAME; + + this.trajectory = new CircularTrajectory(this, oloc.x,oloc.y, this.radius, this.radsPerTick, this.radialPosition); + this.act(0); + }, + + testCollide : function testCollide(agent, bbox, to, traversal){ + return agent.isProjectile && (agent.align !== this.align); + }, + + act : function act(elapsed, now){ + var oloc = this.owner.loc + , tr = this.trajectory; + tr.x = oloc.x; tr.y = oloc.y; + + var tvsl = new Traversal(this) + , to = tvsl.traverse(this.radsPerTick); + + this.game.moveThingTo(this, to.x,to.y); + + // var oloc = this.owner.loc + // , t = (this.radialPosition += this.radsPerTick) + // , r = this.radius + // , x = oloc.x + r*Math.cos(t) + // , y = oloc.y + r*Math.sin(t) + // ; + // this.game.moveThingTo(this, x,y); + return this; + }, + + render : function render(parent){ + this.remove(); + + var loc = this.loc; + this.shape = new Circle(this.width/2) + .position(loc.x, loc.y) + .fill(this.color) + .appendTo( parent ); + + return this; + } +}) +; diff --git a/src/tanks/thing/tank.cjs b/src/tanks/thing/tank.cjs index c6dc5a7..7eae81e 100644 --- a/src/tanks/thing/tank.cjs +++ b/src/tanks/thing/tank.cjs @@ -12,7 +12,7 @@ var Y = require('Y').Y , constants = require('tanks/constants') , BoundsType = constants.BoundsType , DensityType = constants.DensityType -, Trajectory = require('tanks/map/pathing/trajectory').Trajectory +, LinearTrajectory = require('tanks/map/pathing/linear-trajectory').LinearTrajectory , Traversal = require('tanks/map/pathing/traversal').Traversal , Thing = require('tanks/thing/thing').Thing , Bullet = require('tanks/thing/bullet').Bullet @@ -133,6 +133,7 @@ Thing.subclass('Tank', function(Tank){ // Nothing to shoot at? Move toward something this.continueMove(); + this.components.invoke('act', elapsed, now); return this; }; @@ -204,7 +205,7 @@ Thing.subclass('Tank', function(Tank){ if (x instanceof Array) { y=x[_Y]; x=x[_X]; } var loc = this.loc; - this.trajectory = new Trajectory(this, loc.x,loc.y, x,y, this.movePerMs); + this.trajectory = new LinearTrajectory(this, loc.x,loc.y, x,y, this.movePerMs); var tvsl = new Traversal(this) , to = tvsl.traverse(this.elapsed, x,y) ; diff --git a/src/tanks/thing/thing.cjs b/src/tanks/thing/thing.cjs index 81c71b0..9bc1ecc 100644 --- a/src/tanks/thing/thing.cjs +++ b/src/tanks/thing/thing.cjs @@ -29,7 +29,6 @@ new evt.Class('Thing', { // Attributes stats: { - // rotate : HALF_PI, // rotation speed (radians/sec) [UNUSED] hp : 1, // health move : 1.0, // move speed (squares/sec) power : 1, // attack power @@ -45,6 +44,8 @@ new evt.Class('Thing', { // AI "Cooldowns" (max frequency of each action per sec) ai : {}, // see Tank + components : [], + // *** Bookkeeping *** // @@ -62,6 +63,7 @@ new evt.Class('Thing', { isWall : false, isProjectile : false, isAccessory : false, + isComponent : false, active : true, // Agent takes actions? isRenderable : false, // Agent will present itself for rendering when ready // FIXME: stupid hack @@ -72,9 +74,7 @@ new evt.Class('Thing', { // Location loc : null, bbox : null, - - // subclasses with BoundsType.{IRREGULAR,COMPLEX} should override: - testCollision : function testCollide(agent, to, bbox, traversal){ return false; }, + ownBbox : null, // Rotation (rads) rotation : 0, @@ -98,6 +98,7 @@ new evt.Class('Thing', { init : function init(align){ this.align = align || this.align; + this.components = new Y.YArray(); this.createBoundingBox(); this.createStats(); this.createCooldowns(); @@ -108,6 +109,9 @@ new evt.Class('Thing', { this.bbox = new BoundingBox(0,0, this.width,this.height, this.originX,this.originY); }, + // subclasses with BoundsType.{IRREGULAR,COMPLEX} should override but probably delegate on false + testCollision : function testCollide(agent, bbox, to, traversal){ return true; }, + position : function position(x,y){ if (x === undefined && y === undefined) return this.loc; diff --git a/src/tanks/ui/main.cjs b/src/tanks/ui/main.cjs index 8c9d2e6..462e403 100644 --- a/src/tanks/ui/main.cjs +++ b/src/tanks/ui/main.cjs @@ -35,11 +35,15 @@ function main(){ overlay = $('#overlay'); updateOverlay( config.get('ui.overlayOnPause') ); - config.on('set:ui.overlayOnPause', function(evt){ updateOverlay(evt.newval); }); - config.on('set:debug.showFpsGraph', function(evt){ updateUI('#info', evt.newval); }); - config.on('set:debug.showGridCoords', function(evt){ $('body')[(evt.newval ? 'add' : 'remove' )+'Class']('showGridCoords'); }); - - config.set('debug.showGridCoords', config.get('debug.showGridCoords')); + Y.forEach({ + 'ui.overlayOnPause' : function(evt){ updateOverlay(evt.newval); }, + 'debug.showFpsGraph' : function(evt){ updateUI('#info', evt.newval); }, + 'debug.showGridCoords' : function(evt){ $('body')[(evt.newval ? 'add' : 'remove' )+'Class']('showGridCoords'); }, + 'debug.allowViewportScroll' : function(evt){ $('body')[(evt.newval ? 'add' : 'remove' )+'Class']('allowViewportScroll'); } + }, function(fn, key){ + config.on('set:'+key, fn); + config.set(key, config.get(key)); + }); /// Debug /// if (qkv.debug || config.get('debug.showFpsGraph')) diff --git a/www/css/lttl.css b/www/css/lttl.css index 710c0b4..9fb10fc 100644 --- a/www/css/lttl.css +++ b/www/css/lttl.css @@ -58,7 +58,7 @@ td { text-align:center; vertical-align:middle; } #viewport .layer.grid { outline:1px solid rgba(255,255,255, 0.1); } #viewport .ezl.layer.map { } #viewport .ezl.layer.pathmap { z-index:50; } - +.allowViewportScroll #viewport { overflow:scroll; } .item-container { position:absolute; } diff --git a/www/scripts.html b/www/scripts.html index d71deaf..2222c8b 100644 --- a/www/scripts.html +++ b/www/scripts.html @@ -1,5 +1,5 @@
- +
-- 1.7.0.4