Shield mostly works!
authordsc <david.schoonover@gmail.com>
Sat, 5 Mar 2011 01:22:48 +0000 (17:22 -0800)
committerdsc <david.schoonover@gmail.com>
Sat, 5 Mar 2011 01:22:48 +0000 (17:22 -0800)
26 files changed:
data/config.yaml
data/types/units.yaml
pavement.py
src/Y/class.cjs
src/evt.cjs
src/ezl/math/circle.cjs [new file with mode: 0644]
src/tanks/constants.cjs
src/tanks/game.cjs
src/tanks/globals.js
src/tanks/map/pathing/circular-trajectory.cjs [new file with mode: 0644]
src/tanks/map/pathing/index.cjs
src/tanks/map/pathing/linear-trajectory.cjs [moved from src/tanks/map/pathing/trajectory.cjs with 96% similarity]
src/tanks/map/pathing/map-blockers.cjs
src/tanks/map/pathing/map-pathing.cjs
src/tanks/map/pathing/map-searching.cjs
src/tanks/map/pathing/traversal.cjs
src/tanks/thing/bullet.cjs
src/tanks/thing/component.cjs [moved from src/tanks/thing/accessory.cjs with 54% similarity]
src/tanks/thing/index.cjs
src/tanks/thing/player.cjs
src/tanks/thing/shield.cjs [new file with mode: 0644]
src/tanks/thing/tank.cjs
src/tanks/thing/thing.cjs
src/tanks/ui/main.cjs
www/css/lttl.css
www/scripts.html

index 22f7b01..ce7027f 100644 (file)
@@ -7,6 +7,7 @@ debug:
     createGridTable     : false
     showGridCoords      : false
     showFpsGraph        : false
+    allowViewportScroll : false
 map:
     refSize             : &ref_size 50
     rockSizeMin         : 12.5
index 899fba6..4fbbada 100644 (file)
@@ -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'
+    
+    
     
index dd1cb39..21ca152 100755 (executable)
@@ -118,7 +118,7 @@ def update_version():
     with path('build/versioned-build.html').open('w') as f:
         f.write('<script>BUILD="build/{}";</script>\n'.format(git_version))
     with path('build/versioned-app.html').open('w') as f:
-        f.write('<script src="build/{}/{}";</script>\n'.format(git_version, APP_FILE))
+        f.write('<script src="build/{}/{}"></script>\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)
index c70ec20..86f8d0f 100644 (file)
@@ -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<L; ++i) {
@@ -122,10 +119,10 @@ function Class(className, Parent, members) {
         prototype.toString = classToString;
     
     // Fix Constructors, prototypes
-    NewClass[P] = NewClass.fn = prototype;
-    prototype.constructor = prototype.__class__ = NewClass;
-    NewClass.__super__ = SuperClass; // don't override NewClass.constructor -- it should be Function
-    NewClass.className = prototype.className = className;
+    NewClass[P] = NewClass['fn'] = prototype;
+    prototype['constructor'] = prototype['__class__'] = NewClass;
+    NewClass['__super__'] = SuperClass; // don't override NewClass.constructor -- it should be Function
+    NewClass['className'] = prototype['className'] = className;
     
     // Either invoke body constructor...
     if ( isFunction(members) ) {
@@ -137,7 +134,7 @@ function Class(className, Parent, members) {
             setDesc(prototype, k, getDesc(members,k));
     }
     
-    if (prototype.init) NewClass.init = YFunction(prototype.init);
+    if (prototype.init) NewClass['init'] = YFunction(prototype.init);
     
     KNOWN_CLASSES[className] = NewClass;
     
@@ -145,10 +142,10 @@ function Class(className, Parent, members) {
 }
 
 // Add metaprogramming data to Class object
-Class.__super__ = Object;
-Class.fn = Class[P];
-Class.fn.__class__ = Class;
-Class.className = Class.fn.className = "Class";
+Class['__super__'] = Object;
+Class['fn'] = Class[P];
+Class.fn['__class__'] = Class;
+Class['className'] = Class.fn['className'] = "Class";
 
 
 /* Class Methods */
@@ -183,8 +180,8 @@ function subclass(className, members){
     return new Class(className, this, members);
 }
 
-Class.subclass =
-Class.fn.subclass = YFunction(subclass);
+Class['subclass'] =
+Class.fn['subclass'] = YFunction(subclass);
 
 
 [ fabricate, instantiate
@@ -202,13 +199,13 @@ var
  * Root-class for all Y-objects.
  */
 YBase = new Class("YBase", {
-    __y__ : true
+    '__y__' : true
 });
 
 
 exports['Class'] =
-exports['subclass']    = Class;
-exports['YBase']       = YBase;
+exports['subclass'] = Class;
+exports['YBase']    = YBase;
 // exports['instantiate'] = instantiate;
 // exports['fabricate']   = fabricate;
 
index aad3cd0..3e58b78 100644 (file)
@@ -200,14 +200,11 @@ function Class(className, Parent, members){
     
     // Creates a new function with the appropriate name
     // based on the className.
-    var NewClass,
-    constructor = [
-        'var '+className,
-        (''+ConstructorTemplate).replace(ConstructorTemplate.name, className),
-        'NewClass = '+className, ''
-    ].join(';\n');
-    
-    eval(constructor);
+    var constructor = ('('+ConstructorTemplate+')').replace(ConstructorTemplate.name, className);
+    // var NewClass, constructor =
+    //     'var NewClass, '+className+' = NewClass = '+
+    //     (''+_Class).replace(_Class.name, className) + ';';
+    var NewClass = eval(constructor);
     
     // Copy parent methods, then add new instance methods
     for (var k in parentMembers) {
diff --git a/src/ezl/math/circle.cjs b/src/ezl/math/circle.cjs
new file mode 100644 (file)
index 0000000..d5b3227
--- /dev/null
@@ -0,0 +1,49 @@
+var Y = require('Y').Y
+,   Vec = require('ezl/math/vec').Vec
+,   _X = 0, _Y = 1
+,
+
+Circle =
+exports['Circle'] =
+Y.subclass('Circle', {
+    isCircle : true,
+    
+    init : function initCircle(x,y, radius){
+        if (x instanceof Array) { radius=y; y=x[_Y]; x=x[_X]; }
+        this.x = x; // center of circle
+        this.y = y;
+        this.radius = radius;
+    },
+    
+    get loc(){ return new Vec(this.x,this.y); },
+    set loc(v){ this.x = v.x; this.y = v.y; },
+    
+    parametric : function parametric(t){
+        var r = this.radius
+        ,   x = this.x + r*Math.cos(t)
+        ,   y = this.y + r*Math.sin(t)
+        ;
+        return new Vec(x,y);
+    },
+    
+    iparametric : function iparametric(x,y){
+        if (x instanceof Array) { y=x[_Y]; x=x[_X]; }
+        var r = this.radius
+        ,   tx = Math.acos((x - this.x) / r)
+        ,   ty = Math.asin((y - this.y) / r)
+        ;
+        // tx and ty should be the same number provided (x,y) is on the trajectory
+        return (tx+ty) * 0.5;
+    },
+    
+    timeToMove : function timeToMove(dx,dy){
+        if (dx instanceof Array) { dy=dx[1]; dx=dx[0]; }
+        return Math.abs( Math.acos(dx/this.radius) + Math.asin(dy/this.radius) ) * 0.5; // see note at iparametric
+    },
+    
+    pointAtX : function pointAtX(x){ return this.parametric(Math.acos((x - this.x)/this.radius)); },
+    pointAtY : function pointAtY(y){ return this.parametric(Math.asin((y - this.y)/this.radius)); }
+    
+    
+})
+;
index 225b20e..865fdf4 100644 (file)
@@ -1,4 +1,4 @@
-//#exports BoundsType.{PASSABLE,ZONE,BLOCKING,IRREGULAR,COMPLEX}
+//#exports BoundsType.{PASSABLE,ZONE,BLOCKING,IRREGULAR}
 //#exports DensityType.{PASSABLE,BOUNDARY,DENSE,SPARSE,IRREGULAR}
 //#exports StatInvariant.{NONE,RATIO,FULL}
 
@@ -16,11 +16,12 @@ require('Y').Y.extend(exports, {
         /** Obstructs other blockers with its BoundingBox. */
         BLOCKING : 2,
         
-        /** Potentially obstructs other objects, but requires a special test once a BoundingBox collision has been detected. */
-        IRREGULAR : 3,
-        
-        /** Potentially obstructs other objects, but requires a special test, and may instruct a different object become the target of the collision. */
-        COMPLEX : 4
+        /**
+         * Potentially obstructs other objects, but requires a special test once a 
+         * BoundingBox collision has been detected, and may instruct a different 
+         * object become the target of the collision. 
+         */
+        IRREGULAR : 3
         
     },
     
index e51b63a..7c6b93d 100644 (file)
@@ -33,6 +33,7 @@ evt.subclass('Game', {
     
     // Config
     gameoverDelay : null,
+    allowViewportScroll : false,
     
     // Instance
     levelId : 'test',
@@ -127,7 +128,8 @@ evt.subclass('Game', {
         this.animations = this.animations.filter(this.tickAnimations, this);
         
         this.draw();
-        this.viewport.centerOn( this.player.loc );
+        if ( !this.allowViewportScroll )
+            this.viewport.centerOn( this.player.loc );
         
         if ( this.gameover )
             return;
@@ -350,5 +352,5 @@ evt.subclass('Game', {
     
 });
 
-config.updateOnChange('game.gameoverDelay', Game.fn);
+config.updateOnChange(['game.gameoverDelay', 'debug.allowViewportScroll'], Game.fn);
 
index 98aa278..75b0cdd 100644 (file)
@@ -26,10 +26,4 @@ var undefined
 ,   SECONDTH       = ELAPSED / 1000       // Amount of second completed in this tick
 ,   SQUARETH       = REF_SIZE * SECONDTH  // Amount of square/second covered in this tick
 
-,   PI             = Math.PI
-,   QUARTER_PI     = PI/4
-,   HALF_PI        = PI/2
-,   PI_AND_HALF    = PI + HALF_PI
-,   TWO_PI         = PI*2
-
 ;
diff --git a/src/tanks/map/pathing/circular-trajectory.cjs b/src/tanks/map/pathing/circular-trajectory.cjs
new file mode 100644 (file)
index 0000000..495c40e
--- /dev/null
@@ -0,0 +1,59 @@
+var Y = require('Y').Y
+,   op = require('Y/op')
+,   Circle = require('ezl/math/circle').Circle
+,   BOUND_SIZE_RATIO = 0.75
+,
+
+CircularTrajectory =
+exports['CircularTrajectory'] =
+Circle.subclass('CircularTrajectory', {
+    
+    init : function initCircularTrajectory(owner, x,y, radius, radsPerTick, tCurrent){
+        Y.bindAll(this, 'compare'); //, 'closer', 'intersects');
+        this.owner = owner;
+        Circle.init.call(this, x,y, radius);
+        this.radsPerTick = radsPerTick;
+        this.tCurrent = tCurrent || 0;
+        this.tBound = BOUND_SIZE_RATIO * Math.min(owner.width, owner.height);
+    },
+    
+    /**
+     * Linear approximation of distance so we don't have to figure out the intersection point.
+     */
+    compare : function compare(a, b){
+        var oa = a, ob = b; 
+        if ( a.loc ) a = a.loc;
+        if ( b.loc ) b = b.loc;
+        
+        var abs = Math.abs
+        ,   loc = this.owner.loc
+        ,   ax = a.x-this.x, ay = a.y-this.y
+        ,   bx = b.x-this.x, by = b.y-this.y
+        ,   ta = ax*ax + ay*ay
+        ,   tb = bx*bx + by*by
+        ;
+        
+        return op.cmp(ta, tb);
+    },
+    
+    _compare : function compare(a, b){
+        var oa = a, ob = b; 
+        if ( a.loc ) a = a.loc;
+        if ( b.loc ) b = b.loc;
+        
+        var abs = Math.abs
+        ,   t   = this.tCurrent
+        
+        ,   ta = this.iparametric(a) - t
+        ,   tb = this.iparametric(b) - t
+        ;
+        
+        if (ta < 0 && tb >= 0)
+            return 1;
+        if (tb < 0 && ta >= 0)
+            return -1;
+        return op.cmp(abs(ta), abs(tb));
+    },
+    
+})
+;
index c9b122e..4eb559e 100644 (file)
@@ -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
 });
similarity index 96%
rename from src/tanks/map/pathing/trajectory.cjs
rename to src/tanks/map/pathing/linear-trajectory.cjs
index fc3f14f..4e439b0 100644 (file)
@@ -11,13 +11,13 @@ var Y     = require('Y').Y
 ,
 
 
-Trajectory =
-exports['Trajectory'] =
-Line.subclass('Trajectory', {
+LinearTrajectory =
+exports['LinearTrajectory'] =
+Line.subclass('LinearTrajectory', {
     tCurrent : 0,
     
     
-    init : function initTrajectory(owner, x1,y1, x2,y2, tdist, tCurrent){
+    init : function initLinearTrajectory(owner, x1,y1, x2,y2, tdist, tCurrent){
         Y.bindAll(this, 'compare', 'closer', 'intersects');
         
         this.owner = owner;
@@ -66,7 +66,7 @@ Line.subclass('Trajectory', {
     },
     
     clone : function clone(){
-        return new Trajectory(this.owner, this.x1,this.y1, this.x2,this.y2, this.tdist, this.tCurrent);
+        return new LinearTrajectory(this.owner, this.x1,this.y1, this.x2,this.y2, this.tdist, this.tCurrent);
     },
     
     
index 680fb2e..9a80595 100644 (file)
@@ -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){
index c107c46..0ee31c7 100644 (file)
@@ -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(){
index 8be836a..1650944 100644 (file)
@@ -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) );
 }
index 9f8ab9e..45b185e 100644 (file)
@@ -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;
     },
     
index 03d35e3..ed2cd84 100644 (file)
@@ -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);
     },
similarity index 54%
rename from src/tanks/thing/accessory.cjs
rename to src/tanks/thing/component.cjs
index b67e717..b2323c4 100644 (file)
@@ -5,17 +5,19 @@ var Y = require('Y').Y
 ,   Thing       = require('tanks/thing/thing').Thing
 ,
 
-Accessory =
-exports['Accessory'] =
-Thing.subclass('Accessory', {
-    __bind__ : [ 'onCollide' ],
+/**
+ * Units that need to be activated in order after another unit has acted.
+ */
+Component =
+exports['Component'] =
+Thing.subclass('Component', {
     
     // Instance
-    blocking : BoundsType.BLOCKING,
+    blocking : BoundsType.IRREGULAR, // Need to allow shields etc to passthrough like-aligned units
     
-    isAccessory  : true,
+    isComponent  : true,
     
-    active       : true,        // Agent takes actions?
+    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?
@@ -30,32 +32,12 @@ Thing.subclass('Accessory', {
     
     
     
-    init : function initAccessory(owner){
+    init : function initComponent(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;
     }
+    
 })
 ;
index 980882f..004c378 100644 (file)
@@ -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
 });
index 471cea1..d438aa9 100644 (file)
@@ -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 (file)
index 0000000..ff7d62c
--- /dev/null
@@ -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;
+    }
+})
+;
index c6dc5a7..7eae81e 100644 (file)
@@ -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) ;
index 81c71b0..9bc1ecc 100644 (file)
@@ -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;
index 8c9d2e6..462e403 100644 (file)
@@ -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'))
index 710c0b4..9fb10fc 100644 (file)
@@ -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; }
index d71deaf..2222c8b 100644 (file)
@@ -1,5 +1,5 @@
 <div id="scripts">
 <!--# include file="build/versioned-build.html" -->
-<!--# include file="build/versioned-deps.html" -->
+<!--# include file="build/versioned-app.html" -->
 <!--# include file="ga.html" -->
 </div>