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;
     },