Resolves issue where attack cooldown would be consumed despite inability to fire...
authordsc <david.schoonover@gmail.com>
Tue, 4 Jan 2011 11:29:16 +0000 (03:29 -0800)
committerdsc <david.schoonover@gmail.com>
Tue, 4 Jan 2011 11:29:16 +0000 (03:29 -0800)
15 files changed:
data/types/buffs.yaml
data/types/units.yaml
pavement.py
src/ezl/util/data/datafile.cjs
src/ezl/util/data/loader.cjs
src/tanks/config.cjs
src/tanks/effects/buff.cjs
src/tanks/map/level.cjs
src/tanks/mixins/speciated.cjs
src/tanks/thing/bullet.cjs
src/tanks/thing/item.cjs
src/tanks/thing/tank.cjs
src/tanks/thing/thing.cjs
src/tanks/ui/main.cjs
www/commonjs.php

index 231d445..9109b53 100644 (file)
@@ -10,6 +10,7 @@ types:
         name: Speed Up
         desc: Speeds up your tank temporarily.
         tags: [ 'movement' ]
+        timeout: 5.0
         stats:
             move: 2.0
         effects: []
index 8016198..f62f730 100644 (file)
@@ -28,7 +28,7 @@ types:
         tags: [ 'tank' ]
         symbol: tanks/thing/player.PlayerTank
         stats:
-            hp    : 1
+            hp    : 10
             move  : 0.90
             power : 1
             speed : 0.5
index d0a9f0a..cfc0082 100755 (executable)
@@ -1,6 +1,6 @@
 #!/usr/bin/env paver
 from paver.easy import *
-import yaml, json, os
+import yaml, json, os, sys
 
 BUILD_DIR = path('build')
 SRC_DIRS = [ path('src')/d for d in ('Y', 'ezl', 'tanks') ]
@@ -22,6 +22,7 @@ def commonjs(*args, **kw):
 @task
 def data():
     "Convert all yaml files to json."
+    info('Building data files...')
     for dirpath, dirs, files in os.walk(DATA_DIR):
         indir  = path(dirpath)
         outdir = BUILD_DIR/dirpath
@@ -34,11 +35,11 @@ def data():
                 with in_.open('rU') as infile, out.open('w') as outfile:
                     json.dump(yaml.load(infile), outfile, indent=4)
 
-
 @task
 @needs('data')
 def build():
     "Builds the Tanks project"
+    info('Building scripts...')
     tags = commonjs(script_tags=True, capture=True)
     with path('www/deps.html').open('w') as f:
         f.write(tags)
@@ -49,3 +50,4 @@ def clean():
     commonjs(clean=True)
     build()
 
+default = build
index 0623e60..87c40a3 100644 (file)
@@ -9,7 +9,7 @@ var Y = require('Y').Y
 DataFile =
 exports['DataFile'] =
 new evt.Class('DataFile', {
-    __bind__ : [ 'onLoad' ],
+    __bind__ : [ 'process' ],
     
     path : '',
     
@@ -19,8 +19,8 @@ new evt.Class('DataFile', {
     },
     
     load : function load(){
-        this.fire('load', this);
-        jQuery.getJSON(this.path, this.onLoad);
+        this.fire('load');
+        jQuery.getJSON(this.path, this.process);
         return this;
     },
     
@@ -46,7 +46,7 @@ new evt.Class('DataFile', {
     process : function process(data){
         this.data = data;
         this.fire('process', data);
-        console.group('DataFile: processing '+this.path);
+        // console.group('DataFile: processing '+this.path);
         
         var types = Y(data.types)
         ,   defaults = data.defaults || {};
@@ -64,24 +64,19 @@ new evt.Class('DataFile', {
             delete props.symbol;
             var base = this.resolve(symbol)
             ,   type = base.speciate(id, props);
-            console.log('Added type '+id+':', type);
+            // console.log('Added type '+id+':', type);
         }, this);
         
-        console.groupEnd();
-        this.fire('complete', this);
+        // console.groupEnd();
+        this.fire('complete');
         return this;
     },
     
-    // TODO: repeat other jq events
-    onLoad : function onLoad(data){
-        console.log(this+'.onLoad()');
-        this.process(data);
-    },
-    
     /**
      * @private
      */
     die : function die(reason){
+        this.fire('error', this, { 'reason':reason });
         throw new Error(this+' Error: '+reason);
     },
     
index c5cd6d3..94e4186 100644 (file)
@@ -85,9 +85,18 @@ new evt.Class('Loader', {
     nextJob : function nextJob(){
         var self = this;
         // console.log(self+'.nextJob()');
+        
+        if (self.running
+                && self.queue.length === 0 
+                && self.inFlight.length === 0 ) {
+            self.running = false;
+            self.fire('complete');
+            return;
+        }
+        
         if ( !self.running
                 || self.inFlight.length >= self.max
-                || !self.queue.length )
+                || self.queue.length === 0 )
             return;
         
         var job = self.queue.shift();
@@ -101,11 +110,7 @@ new evt.Class('Loader', {
         job.load(self);
         
         // Recurse until we've got enough in-flight, or we're out of queued jobs
-        if (!self.queue.length){
-            this.running = false;
-            self.fire('complete');
-        } else
-            self.nextJob();
+        self.nextJob();
     },
     
     toString : function(){
index 65becea..91c40d3 100644 (file)
@@ -46,14 +46,12 @@ config.addEventListener('set:pathing.gridSquare', function(evt){
 
 /// Load Data Files ///
 
-exports['loadDataFiles'] =
-    function loadDataFiles(){
-        var files = 
-            'types/buffs types/items types/units' // types/level levels game
-                .split(' ')
-                .map(function(type){
-                    return new DataFile('build/data/'+type+'.json');
-                });
-        return new Loader(files).start();
-    };
+var files = 
+    'types/buffs types/items types/units' // types/level levels game
+        .split(' ')
+        .map(function(type){
+            return new DataFile('build/data/'+type+'.json');
+        });
+
+exports['dataLoader'] = new Loader(files);
 
index 3d0b346..0841194 100644 (file)
@@ -18,24 +18,20 @@ new evt.Class('Buff', {
     
     
     /// Configurable ///
-    priority  : 0, // Order of stat and effect application (lower is earlier) // TODO: doesn't do anything
-    timeout : Infinity,
-    threat  : 1, // TODO
+    priority : 0,           // Order of stat and effect application (lower is earlier) // TODO: doesn't do anything
+    timeout  : Infinity,    // Duration until expires (seconds)
+    threat   : 1, // TODO
     
-    // TODO: functions
-    // {stat : Number|Function} Modifications to stats
-    stat_mods : {
-        move : 2
-    },
-    effects   : [], // {Function...} Effects to trigger on affect // XXX: not sure why i need this...
-    triggers  : {}, // {event : Effect} // maybe
+    stats    : {},          // {stat : Number|Function} Modifications to stats // TODO: functions
+    effects  : [],          // {Function...} Effects to trigger on affect // XXX: not sure why i need this...
+    triggers : {},          // {event : Effect} // maybe
     
     /// Instance ///
     name    : '',
     owner   : null, // Agent which originated the buff
     target  : null, // Agent to which the buff applies
     game    : null,
-    created : 0,
+    created : 0,    // Creation timestamp
     
     
     init : function initBuff(target, owner){
@@ -44,7 +40,7 @@ new evt.Class('Buff', {
         this.owner = owner || target;
         
         this.created = this.game.NOW;
-        this.expires = this.created + this.timeout;
+        this.expires = this.created + this.timeout*1000;
         
         this.applyStatMods();
         
@@ -82,7 +78,7 @@ new evt.Class('Buff', {
     
     applyStatMods : function applyStatMods(modifier){
         modifier = (modifier || 1);
-        Y(this.stat_mods).map(mul(modifier)).forEach(this._applyStatMod, this);
+        Y(this.stats).map(mul(modifier)).forEach(this._applyStatMod, this);
         return this;
     },
     
@@ -97,7 +93,13 @@ new evt.Class('Buff', {
         return this.applyStatMods(-1);
     }
     
-})
+});
 
+Buff.addEventListener('speciate',
+    function onSpeciate(evt){
+        var NewBuff = evt.data.species;
+        if (NewBuff.fn.timeout === -1)
+            NewBuff.fn.timeout = Infinity;
+    });
 
 
index 7524739..e5f56e9 100644 (file)
@@ -41,16 +41,18 @@ Rect.subclass('Level', {
             "this.addWall.apply(this, Y.map(_, '*REF_SIZE'))",
             this );
         
-        P = 
-        game.player = game.addThing(new PlayerTank(1), 3,9);
-        // game.addThing(new Tank(1).colors('#4596FF', '#182B53', '#F25522'), 3,9);
+        P =
+        game.player = game.addThing(PlayerTank.create('player', 1), 3,9);
+        // game.addThing(Tank.create('blue', 1).colors('#4596FF', '#182B53', '#F25522'), 3,9);
         
-        E = 
-        game.addThing(new Tank(2), 0,7);
-        game.addThing(new Tank(2), 1,0);
-        game.addThing(new Tank(2), 8,1);
+        E1 =
+        game.addThing(Tank.create('pink', 2), 0,7);
+        E2 =
+        game.addThing(Tank.create('pink', 2), 1,0);
+        E3 =
+        game.addThing(Tank.create('pink', 2), 8,1);
         
-        I = game.addThing(new Item(), 8,8);
+        I = game.addThing(Item.create('nitro'), 8,8);
     },
     
     addWall : function addWall(x,y, w,h, isBoundary){
index 9a4ec24..b34d414 100644 (file)
@@ -31,14 +31,16 @@ Mixin.subclass('Speciated', {
             ,   Species = this.__known__[id] = cls.subclass(speciesName, props)
             ;
             Species.__species_props__ = props;
+            
+            cls.fire('speciate', Species, { 'id':id, 'species':Species, 'cls':cls });
             return Species;
         },
         
-        lookupSpecies : function lookupSpecies(id){
+        lookup : function lookup(id){
             return this.__known__[id];
         },
         
-        createSpeciesInstance : function createSpeciesInstance(id){
+        create : function create(id){
             var args = Y(arguments,1)
             ,   Species = this.__known__[id];
             return Species.instantiate.apply(Species, args);
@@ -51,21 +53,8 @@ Mixin.subclass('Speciated', {
         cls.__known__ = {};
     },
     
-    // onCreate : function initSpeciated(evt){
-    //     var d = evt.data
-    //     ,   instance = d.instance
-    //     ,   Species = d.cls
-    //     ,   props = Species.__species_props__
-    //     ;
-    //     // for (var k in props) {
-    //     //     var v = props[k];
-    //     //     if ( Y.isArray(v) || typeof v === "object" )
-    //     //         instance[k] = Y(v).clone();
-    //     // }
-    // },
-    
     toString : function(){
-        return this.className+'(name='+this.name+', id='+this.id+', tags=['+this.tags+'])';
+        return this.className+'(name='+this.name+', id='+this.__id__+', tags=['+this.tags+'])';
     }
     
 });
index c56da30..9797b73 100644 (file)
@@ -51,14 +51,14 @@ Thing.subclass('Bullet', {
      * @param {tanks.Unit} owner
      * @param {math.Line} trajectory
      */
-    init : function initBullet(owner, x2,y2){
+    init : function initBullet(owner, x1,y1, x2,y2){
         this.owner = owner;
         this.game  = owner.game;
         
         Thing.init.call(this, owner.align);
         
-        var loc = owner.getTurretLoc()
-        ,   x1  = loc.x, y1  = loc.y;
+        // var loc = owner.getTurretLoc()
+        // ,   x1  = loc.x, y1  = loc.y;
         
         this.position(x1,y1);
         this.trajectory = new Trajectory(this, x1,y1, x2,y2, this.movePerMs);
@@ -118,8 +118,6 @@ Thing.subclass('Bullet', {
         ,   unit = d.unit
         ,   tvsl = d.traversal
         ,   to   = tvsl.to
-        
-        ,   isReflective = unit.isReflective
         ;
         
         // Ignore collisions with zones
@@ -127,7 +125,7 @@ Thing.subclass('Bullet', {
             return;
         
         // Reflection!
-        if ( isReflective && this.bounceLimit >= ++this.bounces ) {
+        if ( unit.isReflective && this.bounceLimit >= ++this.bounces ) {
             if (!tvsl.side)
                 return console.error('Null reflection line!', 'to:', to, 'blocker:', unit, 'blockers:', tvsl.blockers._o);
             
@@ -151,13 +149,20 @@ Thing.subclass('Bullet', {
         ,   start = bsize, end = bsize
         ;
         
-        if (isReflective) {
-            x = loc.x;
-            y = loc.y;
-        } else {
+        if (unit instanceof Bullet) {
             x = math.lerp(0.5,loc.x,uloc.x);
             y = math.lerp(0.5,loc.y,uloc.y);
+        } else {
+            x = loc.x;
+            y = loc.y;
         }
+        // if (isReflective) {
+        //     x = loc.x;
+        //     y = loc.y;
+        // } else {
+        //     x = math.lerp(0.5,loc.x,uloc.x);
+        //     y = math.lerp(0.5,loc.y,uloc.y);
+        // }
         
         if ( unit.dead && asize >= bsize )
             end = asize;
index 02fbcf9..91f7e52 100644 (file)
@@ -33,15 +33,12 @@ Thing.subclass('Item', {
     
     
     /// Instance Properties ///
-    type     : 'dummy',     // {String} Item type identifier (unique, for config lookup)
     name     : 'Dummy',     // {String} Display name
     icon_inv : '',          // {String} URL to inventory icon file (TODO)
     icon_map : '',          // {String} URL to map icon file (TODO)
     
     // {Buff...} Passive effects (triggered on pickup)
-    itemBuffs : Y([
-        Buff
-    ]),
+    itemBuffs : Y([]),
     
     // {Function...} Active effects (triggered on activation)
     effects : Y([]),
@@ -94,7 +91,7 @@ Thing.subclass('Item', {
     
     onAcquired : function onAcquired(evt){
         this.owner = evt.data.unit;
-        console.log(this.owner+' acquired '+this+'!');
+        console.log(this.owner+' acquired '+this+' ('+this.desc+')!');
         this.currentBuffs = this.itemBuffs.invoke('instantiate', this.owner);
         this.destroy(); // removes map object
     },
@@ -125,7 +122,19 @@ Thing.subclass('Item', {
     },
     
     toString : function(){
-        return this.type+'(id='+this.id+', owner='+this.owner+')';
+        return this.className+'(id='+this.id+', owner='+this.owner+')';
     }
-})
-;
+});
+
+Item.addEventListener('speciate',
+    function onSpeciate(evt){
+        var NewItem = evt.data.species;
+        NewItem.fn.itemBuffs =
+            Y(NewItem.fn.buffs.map(function(buff){
+                if (typeof buff == "string")
+                    return Buff.lookup(buff);
+                else
+                    return buff;
+            }));
+    });
+
index 9a6094c..1762a12 100644 (file)
@@ -25,6 +25,7 @@ var Y             = require('Y').Y
 Tank =
 exports['Tank'] =
 Thing.subclass('Tank', function(Tank){
+    // TODO: lookup projectile on Bullet
     
     Y.core.descriptors(this, {
         bodyColor   : '#83BB32',
@@ -301,33 +302,25 @@ Thing.subclass('Tank', function(Tank){
      */
     this['shoot'] =
     function shoot(x,y){
-        var WIGGLE = 3 // additional space which must be clear in front of the barrel
-        ,   xydef = (x !== undefined && y !== undefined)
-        ;
+        if ( this.nShots >= this.stats.shots.val || !this.cooldowns.attack.ready )
+            return null;
         
-        if (xydef) this.rotateBarrel(x,y);
+        var xydef = (x !== undefined && y !== undefined);
+        if (xydef)
+            this.rotateBarrel(x,y);
         
-        if ( this.nShots >= this.stats.shots.val || !this.cooldowns.attack.activate(this.now) )
-            return null;
+        // Additional space on each side which must be clear around the
+        // shot to ensure we don't shoot ourself in the foot (literally)
+        var WIGGLE = 1
+        ,   Projectile = this.projectile
+        ,   pw2 = Projectile.fn.width/2, ph2 = Projectile.fn.height/2
         
-        var tloc = this.getTurretLoc()
+        ,   tloc = this.getTurretLoc()
         ,   tx = tloc.x, ty = tloc.y
-        ,   x1 = tx - WIGGLE, y1 = ty - WIGGLE
-        ,   x2 = tx + WIGGLE, y2 = ty + WIGGLE
-        ,   blockers = this.game.pathmap.get(x1,y1, x1,y1).remove(this)
-        
-        // var barrel = this.barrel
-        // ,   loc = this.loc
-        // ,   bb = this.bbox
-        // ,   w2 = bb.width/2,    h2 = bb.height/2
-        // ,   x0 = bb.x1+w2,      y0 = bb.y1+h2
-        // 
-        // ,   theta  = barrel.transform.rotate
-        // ,   sin = Math.sin(theta),  cos = Math.cos(theta)
-        // 
-        // ,   x1 = x0 + w2*cos, y1 = y0 + h2*sin
-        // ,   sz = (barrel.bbox.width - w2)/2 + WIGGLE
-        // ,   blockers = this.game.pathmap.get(x1-sz,y1-sz, x1+sz,y1+sz).remove(this)
+        
+        ,   x1 = tx - pw2 - WIGGLE, y1 = ty - ph2 - WIGGLE
+        ,   x2 = tx + pw2 + WIGGLE, y2 = ty + ph2 + WIGGLE
+        ,   blockers = this.game.pathmap.get(x1,y1, x2,y2).remove(this)
         ;
         
         if ( blockers.size() )
@@ -340,12 +333,11 @@ Thing.subclass('Tank', function(Tank){
             y = ty + REF_SIZE*sin;
         }
         
-        var ProjectileType = this.projectile
-        ,   p = new ProjectileType(this, x,y);
-        
+        this.cooldowns.attack.activate(this.now);
         this.nShots++;
-        p.addEventListener('destroy', this.onBulletDeath);
         
+        var p = new Projectile(this, tx,ty, x,y);
+        p.addEventListener('destroy', this.onBulletDeath);
         this.game.addThing(p).render(this.game.level);
         return p;
     };
@@ -358,13 +350,17 @@ Thing.subclass('Tank', function(Tank){
     
     this['getTurretLoc'] =
     function getTurretLoc(){
-        var WIGGLE = 9 // 1.4 * 6 for max diagonal
+        var WIGGLE = 2
         ,   loc    = this.loc
         ,   barrel = this.barrel
         
         ,   theta  = barrel.transform.rotate
         ,   sin = Math.sin(theta),  cos = Math.cos(theta)
-        ,   len = barrel.bbox.width + WIGGLE
+        
+        // sqrt(2)/2 * (P.width + WIGGLE)
+        // is max diagonal to ensure we don't overlap with the firing unit
+        ,   pw  = this.projectile.fn.width
+        ,   len = barrel.bbox.width + 0.707*(pw+WIGGLE)
         
         ,   x = loc.x + len*cos
         ,   y = loc.y + len*sin
index bc537f6..f8c2305 100644 (file)
@@ -47,7 +47,6 @@ new evt.Class('Thing', {
     
     // *** Bookkeeping *** //
     
-    id      : 0,
     align   : 0, // 0 reserved for neutral units
     dead    : false,
     dirty   : true,
index 5b8972a..7f42c80 100644 (file)
@@ -23,12 +23,7 @@ qkv = Y(window.location.search.slice(1)).fromKV();
 // Main method is only executed once, so we'll setup things
 // that don't change between games.
 function main(){
-    
-    // TODO: wait until completed to allow start
-    // TODO: loading screen
-    var configLoader = cfg.loadDataFiles();
-    
-    $('#welcome').center();
+    $('#welcome').center().hide();
     
     /// Debug ///
     if (qkv.ai) {
@@ -61,6 +56,13 @@ function main(){
     // Build and bind config
     configui.init();
     
+    // Create #loading box
+    $('#welcome').clone()
+        .attr('id', 'loading')
+        // .hide()
+        // .css({ 'top':'1em', 'left':'1em', 'margin':0, 'width':'auto' })
+        .appendTo( $('body') )
+        .find('.box').html('<h1>Loading...</h1>');
     
     // Create #pause box
     $('#welcome').clone()
@@ -88,11 +90,14 @@ function main(){
             startGame();
         });
     
-    setupGame();
-    setupUI();
-    
-    // $('#welcome').hide();
-    // $('#overlay').hide();
+    cfg.dataLoader
+        .addEventListener('complete', function(evt){
+            $('#loading').hide();
+            $('#welcome').show();
+            setupGame();
+            setupUI();
+        })
+        .start();
 }
 
 function gameExists(){ return !!tanks.game; }
index e9af8be..9a1601f 100644 (file)
@@ -21,5 +21,5 @@ $PYTHONPATH = implode(':', array_unique(array_reduce($PYTHON_SITE_PATHS, 'pyVers
 
 function commonjs($src, $root="..") {
     global $PYTHONPATH;
-    return shell_exec("cd $root && PYTHONPATH=$PYTHONPATH commonjs $src 2>&1");
+    return shell_exec("cd $root && PYTHONPATH=$PYTHONPATH paver -q build 2>&1");
 }