Levels are now config-driven.
authordsc <david.schoonover@gmail.com>
Sun, 9 Jan 2011 04:28:31 +0000 (20:28 -0800)
committerdsc <david.schoonover@gmail.com>
Sun, 9 Jan 2011 04:28:31 +0000 (20:28 -0800)
37 files changed:
data/config.yaml
data/types/buffs.yaml
data/types/bullets.yaml
data/types/items.yaml
data/types/levels.yaml
data/types/units.yaml
lib/jquery.masonry-1.3.2.js [new file with mode: 0644]
lib/jquery.masonry-1.3.2.min.js [new file with mode: 0644]
lib/jquery.masonry.js [new symlink]
lib/mustache-0.3.1.js [changed mode: 0755->0644]
pavement.py
src/Y/modules/y.config.cjs
src/Y/modules/y.scaffold.cjs
src/Y/types/object.cjs
src/ezl/util/data/datafile.cjs
src/ezl/util/data/loader.cjs
src/tanks/config.cjs
src/tanks/game.cjs
src/tanks/map/pathmap.cjs
src/tanks/map/traversal.cjs
src/tanks/mixins/speciated.cjs
src/tanks/thing/bullet.cjs
src/tanks/thing/customtank.cjs [deleted file]
src/tanks/thing/fence.cjs [new file with mode: 0644]
src/tanks/thing/item.cjs
src/tanks/thing/player.cjs
src/tanks/thing/tank.cjs
src/tanks/thing/thing.cjs
src/tanks/thing/wall.cjs
src/tanks/ui/configui.cjs
src/tanks/ui/grid.cjs
src/tanks/ui/index.cjs
src/tanks/ui/main.cjs
src/tanks/ui/pathmapui.cjs
www/css/lttl.css
www/debug.html
www/deps.html

index e69de29..6b4336d 100644 (file)
@@ -0,0 +1,18 @@
+game:
+    timeDilation       : 1.0
+    gameoverDelay      : 1000
+    refSize            : &ref_size 50
+debug:
+    showFpsGraph       : false
+    # createGridTable    : false
+    # showGridCoords     : false
+ui:
+    createGridCanvas   : true
+    overlayOnPause     : true
+    showAttackCooldown : false
+    showCountdown      : true
+pathing:
+    gridSquare        : *ref_size
+    overlayAiPaths    : false
+    overlayPathmap    : false
+    traceTrajectories : false
index 4757a85..9a555e6 100644 (file)
@@ -1,6 +1,4 @@
-# Buff definitions
-name: buff
-lookup: .tanks.data.buffs
+name: buffs
 defaults:
     symbol: tanks/effects/buff.Buff
     timeout: -1 # the Infinity literal is not valid JSON
index e69de29..728a8db 100644 (file)
@@ -0,0 +1,17 @@
+name: bullets
+defaults:
+    symbol: tanks/thing/bullet.Bullet
+    art:
+        map_icon: ''
+        inv_icon: ''
+types:
+    normal:
+        color: '#FFF6AE'
+        bounceLimit: 1
+        stats:
+            move: 2.25
+    player_normal:
+        color: '#F7ADA6'
+        bounceLimit: 1
+        stats:
+            move: 2.25
index 8565072..bdd80b5 100644 (file)
@@ -1,6 +1,4 @@
-# Item definitions
-name: item
-lookup: .tanks.data.items
+name: items
 defaults:
     symbol: tanks/thing/item.Item
 types:
index a31d9cd..e6321db 100644 (file)
@@ -1,6 +1,4 @@
-# Level definitions
-name: level
-lookup: .tanks.data.levels
+name: levels
 defaults:
     symbol: tanks/map/level.Level
 types:
index 5b725bb..d310033 100644 (file)
@@ -1,9 +1,8 @@
-# Unit definitions
-name: unit
-lookup: .tanks.data.units
+name: units
 defaults:
     symbol: tanks/thing/thing.Thing
     level: 1
+    projectile: normal
     stats:
         hp            : 1           # health
         move          : 1.0         # move speed (squares/sec)
@@ -28,6 +27,7 @@ types:
         desc: "Don't hate the Player, hate the--Wait. No, that's fine."
         tags: [ 'tank' ]
         symbol: tanks/thing/player.Player
+        # projectile: player_normal
         stats:
             hp    : 1
             move  : 0.90
diff --git a/lib/jquery.masonry-1.3.2.js b/lib/jquery.masonry-1.3.2.js
new file mode 100644 (file)
index 0000000..e127b3a
--- /dev/null
@@ -0,0 +1,298 @@
+/*************************************************
+**  jQuery Masonry version 1.3.2
+**  Copyright David DeSandro, licensed MIT
+**  http://desandro.com/resources/jquery-masonry
+**************************************************/
+;(function($){  
+
+  /*!
+   * smartresize: debounced resize event for jQuery
+   * http://github.com/lrbabe/jquery-smartresize
+   *
+   * Copyright (c) 2009 Louis-Remi Babe
+   * Licensed under the GPL license.
+   * http://docs.jquery.com/License
+   *
+   */
+  var event = $.event,
+      resizeTimeout;
+
+  event.special.smartresize = {
+    setup: function() {
+      $(this).bind( "resize", event.special.smartresize.handler );
+    },
+    teardown: function() {
+      $(this).unbind( "resize", event.special.smartresize.handler );
+    },
+    handler: function( event, execAsap ) {
+      // Save the context
+      var context = this,
+          args = arguments;
+
+      // set correct event type
+      event.type = "smartresize";
+
+      if (resizeTimeout) { clearTimeout(resizeTimeout); }
+      resizeTimeout = setTimeout(function() {
+        jQuery.event.handle.apply( context, args );
+      }, execAsap === "execAsap"? 0 : 100);
+    }
+  };
+
+  $.fn.smartresize = function( fn ) {
+    return fn ? this.bind( "smartresize", fn ) : this.trigger( "smartresize", ["execAsap"] );
+  };
+
+
+
+  // masonry code begin
+  $.fn.masonry = function(options, callback) { 
+
+    // all my sweet methods
+    var msnry = {
+      getBricks : function($wall, props, opts) {
+        var hasItemSelector = (opts.itemSelector === undefined);
+        if ( opts.appendedContent === undefined ) {
+          // if not appendedContent
+          props.$bricks = hasItemSelector ?
+                $wall.children() :
+                $wall.find(opts.itemSelector);
+        } else {
+         //  if appendedContent...
+         props.$bricks = hasItemSelector ?
+                opts.appendedContent : 
+                opts.appendedContent.filter( opts.itemSelector );
+        }
+      },
+      
+      placeBrick : function($brick, setCount, setY, props, opts) {
+            // get the minimum Y value from the columns...
+        var minimumY = Math.min.apply(Math, setY),
+            setHeight = minimumY + $brick.outerHeight(true),
+            i = setY.length,
+            shortCol = i,
+            setSpan = props.colCount + 1 - i;
+        // Which column has the minY value, closest to the left
+        while (i--) {
+          if ( setY[i] == minimumY ) {
+            shortCol = i;
+          }
+        }
+            
+        var position = {
+          left: props.colW * shortCol + props.posLeft,
+          top: minimumY
+        };
+            
+        // position the brick
+        $brick.applyStyle(position, $.extend(true,{},opts.animationOptions) );
+
+        // apply setHeight to necessary columns
+        for ( i=0; i < setSpan; i++ ) {
+          props.colY[ shortCol + i ] = setHeight;
+        }
+      },
+      
+      setup : function($wall, opts, props) {
+        msnry.getBricks($wall, props, opts);
+
+        if ( props.masoned ) {
+          props.previousData = $wall.data('masonry');
+        }
+
+        if ( opts.columnWidth === undefined) {
+          props.colW = props.masoned ?
+              props.previousData.colW :
+              props.$bricks.outerWidth(true);
+        } else {
+          props.colW = opts.columnWidth;
+        }
+
+        props.colCount = Math.floor( $wall.width() / props.colW ) ;
+        props.colCount = Math.max( props.colCount, 1 );
+      },
+      
+      arrange : function($wall, opts, props) {
+        var i;
+
+        if ( !props.masoned || opts.appendedContent !== undefined ) {
+          // just the new bricks
+          props.$bricks.css( 'position', 'absolute' );
+        }
+
+        // if masonry hasn't been called before
+        if ( !props.masoned ) { 
+          $wall.css( 'position', 'relative' );
+
+          // get top left position of where the bricks should be
+          var $cursor = $( document.createElement('div') );
+          $wall.prepend( $cursor );
+          props.posTop =  Math.round( $cursor.position().top );
+          props.posLeft = Math.round( $cursor.position().left );
+          $cursor.remove();
+        } else {
+          props.posTop =  props.previousData.posTop;
+          props.posLeft = props.previousData.posLeft;
+        }
+        
+        // set up column Y array
+        if ( props.masoned && opts.appendedContent !== undefined ) {
+          // if appendedContent is set, use colY from last call
+          props.colY = props.previousData.colY;
+
+          /*
+          *  in the case that the wall is not resizeable,
+          *  but the colCount has changed from the previous time
+          *  masonry has been called
+          */
+          for ( i = props.previousData.colCount; i < props.colCount; i++) {
+            props.colY[i] = props.posTop;
+          }
+
+        } else {
+          // start new colY array, with starting values set to posTop
+          props.colY = [];
+          i = props.colCount;
+          while (i--) {
+            props.colY.push(props.posTop);
+          }
+        }
+
+        // are we animating the rearrangement?
+        // use plugin-ish syntax for css or animate
+        $.fn.applyStyle = ( props.masoned && opts.animate ) ? $.fn.animate : $.fn.css;
+
+
+        // layout logic
+        if ( opts.singleMode ) {
+          props.$bricks.each(function(){
+            var $brick = $(this);
+            msnry.placeBrick($brick, props.colCount, props.colY, props, opts);
+          });      
+        } else {
+          props.$bricks.each(function() {
+            var $brick = $(this),
+                //how many columns does this brick span
+                colSpan = Math.ceil( $brick.outerWidth(true) / props.colW);
+            colSpan = Math.min( colSpan, props.colCount );
+
+            if ( colSpan === 1 ) {
+              // if brick spans only one column, just like singleMode
+              msnry.placeBrick($brick, props.colCount, props.colY, props, opts);
+            } else {
+              // brick spans more than one column
+
+              //how many different places could this brick fit horizontally
+              var groupCount = props.colCount + 1 - colSpan,
+                  groupY = [];
+
+              // for each group potential horizontal position
+              for ( i=0; i < groupCount; i++ ) {
+                // make an array of colY values for that one group
+                var groupColY = props.colY.slice(i, i+colSpan);
+                // and get the max value of the array
+                groupY[i] = Math.max.apply(Math, groupColY);
+              }
+
+              msnry.placeBrick($brick, groupCount, groupY, props, opts);
+            }
+          }); //    /props.bricks.each(function() {
+        }  //     /layout logic
+
+        // set the height of the wall to the tallest column
+        props.wallH = Math.max.apply(Math, props.colY);
+        var wallCSS = { height: props.wallH - props.posTop };
+        $wall.applyStyle( wallCSS, $.extend(true,[],opts.animationOptions) );
+
+        // add masoned class first time around
+        if ( !props.masoned ) { 
+          // wait 1 millisec for quell transitions
+          setTimeout(function(){
+            $wall.addClass('masoned'); 
+          }, 1);
+        }
+
+        // provide props.bricks as context for the callback
+        callback.call( props.$bricks );
+
+        // set all data so we can retrieve it for appended appendedContent
+        //    or anyone else's crazy jquery fun
+        $wall.data('masonry', props );
+        
+      }, // /msnry.arrange
+      
+      resize : function($wall, opts, props) {
+        props.masoned = !!$wall.data('masonry');
+        var prevColCount = $wall.data('masonry').colCount;
+        msnry.setup($wall, opts, props);
+        if ( props.colCount != prevColCount ) {
+          msnry.arrange($wall, opts, props);
+        }
+      }
+    };
+
+
+    /*
+    *  let's begin
+    *  IN A WORLD...
+    */
+    return this.each(function() {  
+
+      var $wall = $(this),
+          props = {};
+
+      // checks if masonry has been called before on this object
+      props.masoned = !!$wall.data('masonry');
+    
+      var previousOptions = props.masoned ? $wall.data('masonry').options : {},
+          opts =  $.extend(
+                    {},
+                    $.fn.masonry.defaults,
+                    previousOptions,
+                    options
+                  ),
+          resizeOn = previousOptions.resizeable;
+
+      // should we save these options for next time?
+      props.options = opts.saveOptions ? opts : previousOptions;
+
+      //picked up from Paul Irish
+      callback = callback || function(){};
+
+      msnry.getBricks($wall, props, opts);
+
+      // if brickParent is empty, do nothing, go back home and eat chips
+      if ( !props.$bricks.length ) { 
+        return this; 
+      }
+
+      // call masonry layout
+      msnry.setup($wall, opts, props);
+      msnry.arrange($wall, opts, props);
+    
+      // binding window resizing
+      if ( !resizeOn && opts.resizeable ) {
+        $(window).bind('smartresize.masonry', function() { msnry.resize($wall, opts, props); } );
+      }
+      if ( resizeOn && !opts.resizeable ) { 
+        $(window).unbind('smartresize.masonry'); 
+      }
+       
+
+    });    //    /return this.each(function()
+  };      //    /$.fn.masonry = function(options)
+
+
+  // Default plugin options
+  $.fn.masonry.defaults = {
+    singleMode: false,
+    columnWidth: undefined,
+    itemSelector: undefined,
+    appendedContent: undefined,
+    saveOptions: true,
+    resizeable: true,
+    animate: false,
+    animationOptions: {}
+  };
+
+})(jQuery);
\ No newline at end of file
diff --git a/lib/jquery.masonry-1.3.2.min.js b/lib/jquery.masonry-1.3.2.min.js
new file mode 100644 (file)
index 0000000..f6e8a82
--- /dev/null
@@ -0,0 +1,12 @@
+/*************************************************
+**  jQuery Masonry version 1.3.2
+**  Copyright David DeSandro, licensed MIT
+**  http://desandro.com/resources/jquery-masonry
+**************************************************/
+(function(e){var n=e.event,o;n.special.smartresize={setup:function(){e(this).bind("resize",n.special.smartresize.handler)},teardown:function(){e(this).unbind("resize",n.special.smartresize.handler)},handler:function(j,l){var g=this,d=arguments;j.type="smartresize";o&&clearTimeout(o);o=setTimeout(function(){jQuery.event.handle.apply(g,d)},l==="execAsap"?0:100)}};e.fn.smartresize=function(j){return j?this.bind("smartresize",j):this.trigger("smartresize",["execAsap"])};e.fn.masonry=function(j,l){var g=
+{getBricks:function(d,b,a){var c=a.itemSelector===undefined;b.$bricks=a.appendedContent===undefined?c?d.children():d.find(a.itemSelector):c?a.appendedContent:a.appendedContent.filter(a.itemSelector)},placeBrick:function(d,b,a,c,h){b=Math.min.apply(Math,a);for(var i=b+d.outerHeight(true),f=a.length,k=f,m=c.colCount+1-f;f--;)if(a[f]==b)k=f;d.applyStyle({left:c.colW*k+c.posLeft,top:b},e.extend(true,{},h.animationOptions));for(f=0;f<m;f++)c.colY[k+f]=i},setup:function(d,b,a){g.getBricks(d,a,b);if(a.masoned)a.previousData=
+d.data("masonry");a.colW=b.columnWidth===undefined?a.masoned?a.previousData.colW:a.$bricks.outerWidth(true):b.columnWidth;a.colCount=Math.floor(d.width()/a.colW);a.colCount=Math.max(a.colCount,1)},arrange:function(d,b,a){var c;if(!a.masoned||b.appendedContent!==undefined)a.$bricks.css("position","absolute");if(a.masoned){a.posTop=a.previousData.posTop;a.posLeft=a.previousData.posLeft}else{d.css("position","relative");var h=e(document.createElement("div"));d.prepend(h);a.posTop=Math.round(h.position().top);
+a.posLeft=Math.round(h.position().left);h.remove()}if(a.masoned&&b.appendedContent!==undefined){a.colY=a.previousData.colY;for(c=a.previousData.colCount;c<a.colCount;c++)a.colY[c]=a.posTop}else{a.colY=[];for(c=a.colCount;c--;)a.colY.push(a.posTop)}e.fn.applyStyle=a.masoned&&b.animate?e.fn.animate:e.fn.css;b.singleMode?a.$bricks.each(function(){var i=e(this);g.placeBrick(i,a.colCount,a.colY,a,b)}):a.$bricks.each(function(){var i=e(this),f=Math.ceil(i.outerWidth(true)/a.colW);f=Math.min(f,a.colCount);
+if(f===1)g.placeBrick(i,a.colCount,a.colY,a,b);else{var k=a.colCount+1-f,m=[];for(c=0;c<k;c++){var p=a.colY.slice(c,c+f);m[c]=Math.max.apply(Math,p)}g.placeBrick(i,k,m,a,b)}});a.wallH=Math.max.apply(Math,a.colY);d.applyStyle({height:a.wallH-a.posTop},e.extend(true,[],b.animationOptions));a.masoned||setTimeout(function(){d.addClass("masoned")},1);l.call(a.$bricks);d.data("masonry",a)},resize:function(d,b,a){a.masoned=!!d.data("masonry");var c=d.data("masonry").colCount;g.setup(d,b,a);a.colCount!=c&&
+g.arrange(d,b,a)}};return this.each(function(){var d=e(this),b={};b.masoned=!!d.data("masonry");var a=b.masoned?d.data("masonry").options:{},c=e.extend({},e.fn.masonry.defaults,a,j),h=a.resizeable;b.options=c.saveOptions?c:a;l=l||function(){};g.getBricks(d,b,c);if(!b.$bricks.length)return this;g.setup(d,c,b);g.arrange(d,c,b);!h&&c.resizeable&&e(window).bind("smartresize.masonry",function(){g.resize(d,c,b)});h&&!c.resizeable&&e(window).unbind("smartresize.masonry")})};e.fn.masonry.defaults={singleMode:false,
+columnWidth:undefined,itemSelector:undefined,appendedContent:undefined,saveOptions:true,resizeable:true,animate:false,animationOptions:{}}})(jQuery);
\ No newline at end of file
diff --git a/lib/jquery.masonry.js b/lib/jquery.masonry.js
new file mode 120000 (symlink)
index 0000000..a7b9106
--- /dev/null
@@ -0,0 +1 @@
+jquery.masonry-1.3.2.js
\ No newline at end of file
old mode 100755 (executable)
new mode 100644 (file)
index da4fbb4..c8acb1a 100755 (executable)
@@ -21,8 +21,8 @@ def commonjs(*args, **kw):
 
 
 @task
-def data():
-    "Convert all yaml files to json."
+def build_data():
+    "Convert all yaml files to json"
     info('Building data files...')
     for dirpath, dirs, files in os.walk(DATA_DIR):
         indir  = path(dirpath)
@@ -35,17 +35,34 @@ def data():
                 out = outdir / f.replace('.yaml', '.json')
                 with in_.open('rU') as infile, out.open('w') as outfile:
                     json.dump(yaml.load(infile), outfile, indent=4)
+    
+    info('Injecting config JSON...')
+    conf = path('build/tanks/config.js')
+    conf_json = path('build/data/config.json')
+    with conf.open('rU') as f:
+        conf_txt = f.read()
+    with conf_json.open('rU') as jf, conf.open('w') as out:
+        conf_data = jf.read()
+        out.write( conf_txt.replace('/*CONFIG_JSON*/', conf_data) )
 
 @task
-@needs('data')
-def build():
-    "Builds the Tanks project"
+def build_scripts():
+    "Builds js modules"
     info('Building scripts...')
+    sh('rm build/tanks/config.js')
     tags = commonjs(script_tags=True, capture=True)
     with path('www/deps.html').open('w') as f:
         f.write(tags)
 
 @task
+@needs('build_data')
+@needs('build_scripts')
+def build():
+    "Builds the Tanks project"
+    pass
+
+
+@task
 def clean():
     "Cleans dep cache and build files"
     commonjs(clean=True)
index f812fde..55ebb00 100644 (file)
@@ -132,6 +132,37 @@ Y.YObject.subclass('Config', function(Config){
                 acc, this);
         },
         
+        /**
+         * Iterates over both items and groups of the config object
+         * in order, sorted lexographically on key.
+         */
+        sorted : function sorted(groupFn, itemFn, acc, context){
+            context = context || this;
+            return this._sorted(new Y.YArray(), this._o, groupFn, itemFn, acc, context);
+        },
+        
+        _sorted : function _sorted(path, obj, groupFn, itemFn, acc, context){
+            var self = this
+            ,   keys = (Y.isFunction(obj.keys) ? obj.keys() : Object.keys(obj)).sort();
+            return keys.reduce(function __sorted(acc, k){
+                    var chain = path.push(k).join('.')
+                    ,   v = self.getNested(chain)
+                    ;
+                    
+                    // Nested object -- recurse
+                    if ( Y.isPlainObject(v) ){
+                        acc = groupFn.call(context, acc, v, chain, self);
+                        acc = self._sorted(path, v, groupFn, itemFn, acc, context);
+                        
+                    // Normal value -- invoke iterator
+                    } else
+                        acc = itemFn.call(context, acc, v, chain, self);
+                    
+                    path.pop();
+                    return acc;
+                }, acc);
+        },
+        
         getDefault : function getDefault(k, def){
             return getNested(this._defaults, k, def);
         },
index 3f85341..a17a77e 100644 (file)
@@ -57,8 +57,8 @@ Y.subclass('Field', {
         this.id    = chain;
         this.def   = def;
         this.val   = this.old = val === undefined ? def : val;
-        this.key   = chain.split('.').pop();
-        this.label = camelToSpaces(this.key);
+        this.key   = chain.replace('.', '_');
+        this.label = camelToSpaces(chain.split('.').pop());
         
         var T = Y.typeName(def);
         this.cast = options.cast || type2parser[T];
@@ -72,7 +72,7 @@ Y.subclass('Field', {
         this.build()
             .update(this.val);
         
-        this.elField.bind('change', this.onChange.bind(this));
+        jQuery(this.selector).live('change', this.onChange.bind(this));
     },
     
     build : function build(){
@@ -94,11 +94,12 @@ Y.subclass('Field', {
                 })
                 .val(this.val)
                 .appendTo(el);
+        this.selector = 'input[name='+this.key+']';
         return this;
     },
     
     update : function update(val){
-        var el = this.elField;
+        var el = jQuery(this.selector);
         if (val !== this.val) {
             this.old = this.val;
             this.val = val;
@@ -106,7 +107,7 @@ Y.subclass('Field', {
                 'key'    : this.id,
                 'oldval' : this.old,
                 'newval' : this.val,
-                'el'     : this.elField
+                'el'     : jQuery(this.selector)
             });
             el.val(val);
         }
@@ -117,10 +118,11 @@ Y.subclass('Field', {
     },
     
     onChange : function onChange(evt){
+        var el = jQuery(this.selector);
         if (this.type === 'checkbox')
-            this.update( this.elField.attr('checked') );
+            this.update( el.attr('checked') );
         else
-            this.update( this.cast(this.elField.val()) );
+            this.update( this.cast(el.val()) );
     }
     
 })
@@ -130,14 +132,19 @@ Y.subclass('Field', {
 create =
 exports['create'] =
 function create(config, el){
-    var fields = {};
-    config.parts(
+    var data = {}
+    ,   groups = data.groups = {}
+    ,   fields = data.fields = {};
+    config.sorted(
         function createGroup(oldGroup, value, chain){
-            return jQuery('<fieldset/>')
+            var g = 
+            groups[chain] = 
+                    jQuery('<fieldset/>')
                         .attr('id', chain)
                         .addClass('group')
                         .append( jQuery('<legend/>').text(chain.split('.').pop()) )
                         .appendTo(el);
+            return g;
         },
         function createField(group, value, chain){
             var def = config.getDefault(chain)
@@ -159,7 +166,7 @@ function create(config, el){
             return group;
         },
         el);
-    return fields;
+    return data;
 }
 ;
 
index a383701..49ff70b 100644 (file)
@@ -189,8 +189,23 @@ YCollection.subclass('YObject', {
             if ( o[name] === value )
                 return name;
         return -1;
-    }
+    },
+    
+    'unzipped' : function unzipped(){
+        return this.reduce(function(acc, v, k){
+            acc.keys.push(k);
+            acc.values.push(v);
+            return acc;
+        }, { 'keys':[], 'values':[] });
+    },
     
+    'keys' : function keys(){
+        return this.unzipped().keys;
+    },
+    
+    'values' : function values(){
+        return this.unzipped().values;
+    }
     
 });
 
index 2bbf1f3..699ddf4 100644 (file)
@@ -14,8 +14,9 @@ new evt.Class('DataFile', {
     path : '',
     
     
-    init : function initDataFile(path){
+    init : function initDataFile(path, datastore){
         this.path = path;
+        this.datastore = datastore;
     },
     
     load : function load(){
@@ -51,13 +52,11 @@ new evt.Class('DataFile', {
         
         var types = Y(data.types)
         ,   defaults = data.defaults || {}
-        ,   lookup = this.resolve(data.lookup)
         ;
         if (!(types && Y.isFunction(types.forEach)))
             this.die('Specified types are not iterable! '+data.types);
         
         types.forEach(function(kv, id){
-            // TODO: process 'inherits' key -- requires resolving data-type path
             var props = Y.extend({}, deepcopy(data.defaults), kv)
             ,   symbol = props.symbol;
             
@@ -71,7 +70,7 @@ new evt.Class('DataFile', {
                 this.die('Cannot create types from data (symbol-class '+base+' from "'+symbol+'" is not Speciated)!');
             
             var type = base.speciate(id, props);
-            lookup[id] = type;
+            this.datastore[id] = type;
             
             // console.log('Added type '+id+':', type);
         }, this);
index 94e4186..b2f564a 100644 (file)
@@ -100,6 +100,7 @@ new evt.Class('Loader', {
             return;
         
         var job = self.queue.shift();
+        // console.log(self+'.nextJob() --> '+job);
         job.addEventListener('complete', function onJobComplete(evt){
             // console.log(self+'.onJobComplete('+job+')');
             job.removeEventListener('complete', arguments.callee);
index 23c4a96..b128344 100644 (file)
@@ -1,5 +1,7 @@
 //  -*- mode: JavaScript; tab-width: 4; indent-tabs-mode: nil; -*-
+//#exports defaults config dataLoader
 var Y        = require('Y').Y
+,   ensure   = require('Y/types/object').ensure
 ,   Config   = require('Y/modules/y.config').Config
 
 ,   Vec      = require('ezl/math').Vec
@@ -9,28 +11,9 @@ var Y        = require('Y').Y
 
 /// Config (Inserted Here) ///
 
+
 defaults =
-exports['defaults'] = {
-    game : {
-        timeDilation       : 1.0,
-        gameoverDelay      : 1000
-    },
-    ui : {
-        createGridCanvas   : true,
-        createGridTable    : false,
-        overlayOnPause     : true,
-        showGridCoords     : false,
-        showAttackCooldown : false,
-        showCountdown      : (document.location.host.toString() !== 'tanks.woo')
-    },
-    pathing : {
-        gridSquare        : REF_SIZE,
-        gridSquareMid     : new Vec(REF_SIZE/2, REF_SIZE/2),
-        overlayAiPaths    : false,
-        overlayPathmap    : false,
-        traceTrajectories : false
-    }
-}
+exports['defaults'] = /*CONFIG_JSON*/
 ,
 
 config =
@@ -42,17 +25,25 @@ config.addEventListener('set:pathing.gridSquare', function(evt){
     var sq = evt.data.newval;
     config.set('pathing.gridSquareMid', new Vec(sq/2, sq/2));
 });
-
+config.set('pathing.gridSquare', config.get('pathing.gridSquare'));
 
 
 /// Load Data Files ///
 
-var files = 
-    'types/buffs types/items types/units types/levels' // levels game
-        .split(' ')
-        .map(function(type){
-            return new DataFile('build/data/'+type+'.json');
-        });
+var loader;
+exports['dataLoader'] = function dataLoader(){
+    if (!loader){
+        var files = 
+            'types/buffs types/items types/bullets types/units types/levels' // levels game
+                .split(' ')
+                .map(function(type){
+                    var name = type.split('/').pop();
+                    ensure(tanks.data, name);
+                    return new DataFile('build/data/'+type+'.json', tanks.data[name]);
+                });
+        loader = new Loader(files);
+    }
+    return loader;
+};
 
-exports['dataLoader'] = new Loader(files);
 
index 5d8b883..b31c13d 100644 (file)
@@ -121,6 +121,14 @@ Y.subclass('Game', {
         
     },
     
+    debugTick : function debugTick(evt){
+        try {
+            this.tick(evt);
+        } catch(ex) {
+            console.error('tick('+evt+') '+ex, ex);
+        }
+    },
+    
     tickAnimations : function tickAnimations(animation){
         var running = animation.tick(ELAPSED, NOW);
         if (!running)
index d4d72ee..3a09ae2 100644 (file)
@@ -9,8 +9,8 @@ var Y          = require('Y').Y
 ,   Line       = math.Line
 
 ,   config     = require('tanks/config').config
-,   map        = require('tanks/map/map')
-,   Map        = map.Map
+,   PathingType = require('tanks/constants').PathingType
+,   Map        = require('tanks/map/map').Map
 ,   Wall       = require('tanks/thing/wall').Wall
 
 
@@ -291,7 +291,7 @@ Y.subclass('Square', new Vec(0,0), {
      * @private
      */
     _filterBlocked : function filterBlocked(v, r){
-        return v.blocking === map.BLOCKING && !v.isBoundary;
+        return v.blocking === PathingType.BLOCKING && !v.isBoundary;
     },
     
     getNeighbors : function getNeighbors(){
index f926a39..9d9237b 100644 (file)
@@ -1,5 +1,5 @@
 var Y = require('Y').Y
-,   map = require('tanks/map/map')
+,   PathingType = require('tanks/constants').PathingType
 ,
 
 Traversal =
@@ -93,12 +93,12 @@ Y.subclass('Traversal', {
         var blocking = blocker.blocking;
         
         // All blockers after the first are ignored
-        if ( this.ignore.has(blocker) || blocking === map.PASSABLE || this.isBlocked )
+        if ( this.ignore.has(blocker) || blocking === PathingType.PASSABLE || this.isBlocked )
             return false;
         
         // Only fire collision with this zone once per traversal
         // XXX: Zone will have to manage enterance/exit and provide a method for testing, hm -- this would obviate the main need for this.ignore
-        if ( blocking === map.ZONE ) {
+        if ( blocking === PathingType.ZONE ) {
             this.ignore.push(blocker);
             this.collide(blocker);
             return false;
index 24bcf8c..544c3d6 100644 (file)
@@ -10,7 +10,11 @@ Mixin.subclass('Speciated', {
     tags : [],
     
     __static__ : {
+        __species_id__ : 0,
+        
         id2name : function id2name(id){
+            if (typeof id === 'number')
+                return this.className+id;
             var name = id+'';
             // TODO: all YString methods to return YString :P But it'll proally break shit
             name = Y(name).capitalize();
@@ -19,12 +23,16 @@ Mixin.subclass('Speciated', {
         },
         
         speciate : function speciate(id, props){
+            if ( arguments.length < 2 ) {
+                props = id;
+                id = this.__species_id__++;
+            }
+            
             if ( this.__known__[id] )
                 throw new Error('A species of '+this.className+' already exists with the id '+id+'!');
             
             props = props || {};
             props.__species__ = id;
-            // delete props.id; // reserved for instance id
             
             var cls = this
             ,   speciesName = this.id2name(id)
index 22088e2..41be7dc 100644 (file)
@@ -8,7 +8,7 @@ var Y          = require('Y').Y
 ,   Circle     = shape.Circle
 
 ,   config     = require('tanks/config').config
-,   map        = require('tanks/map/map')
+,   PathingType = require('tanks/constants').PathingType
 ,   stat       = require('tanks/effects/stat')
 
 ,   Thing      = require('tanks/thing/thing').Thing
@@ -33,7 +33,7 @@ Thing.subclass('Bullet', {
     },
     
     // Instance
-    blocking    : map.BLOCKING,
+    blocking : PathingType.BLOCKING,
     isRenderable : true,
     
     bounces     : 0,
@@ -42,6 +42,8 @@ Thing.subclass('Bullet', {
     width  : 6,
     height : 6,
     
+    color: '#FFF6AE',
+    
     stats : {
         move : 2.0 // move speed (squares/sec)
     },
@@ -58,9 +60,6 @@ Thing.subclass('Bullet', {
         
         Thing.init.call(this, owner.align);
         
-        // 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);
         
@@ -122,7 +121,7 @@ Thing.subclass('Bullet', {
         ;
         
         // Ignore collisions with zones
-        if (unit.blocking === map.ZONE)
+        if (unit.blocking === PathingType.ZONE)
             return;
         
         // Reflection!
@@ -188,7 +187,7 @@ Thing.subclass('Bullet', {
         var loc = this.loc;
         this.shape = new Circle(this.width/2)
             .position(loc.x, loc.y)
-            .fill('#FFF6AE')
+            .fill(this.color)
             .appendTo( parent );
         this.shape.layer.attr('title', ''+loc);
         
diff --git a/src/tanks/thing/customtank.cjs b/src/tanks/thing/customtank.cjs
deleted file mode 100644 (file)
index 97dcecf..0000000
+++ /dev/null
@@ -1,13 +0,0 @@
-var Tank  = require('tanks/thing/tank').Tank
-,
-
-CustomTank =
-exports['CustomTank'] =
-Tank.subclass('CustomTank', {
-    
-    init : function initCustomTank(align, script){
-        Tank.init.call(this, align);
-        this.act = eval('(function(){ with(this){'+script+'} })');
-    }
-    
-});
diff --git a/src/tanks/thing/fence.cjs b/src/tanks/thing/fence.cjs
new file mode 100644 (file)
index 0000000..25e1c2b
--- /dev/null
@@ -0,0 +1,79 @@
+var Y     = require('Y').Y
+,   op    = require('Y/op')
+,   Rect  = require('ezl/shape').Rect
+,   Wall  = require('tanks/thing/wall').Wall
+,
+
+
+Fence =
+exports['Fence'] =
+Wall.subclass('Fence', {
+    align : 0, // 0 reserved for neutral units
+    
+    originX : 0,
+    originY : 0,
+    
+    isReflective : true,
+    isRenderable : true,
+    
+    // inactive
+    active : false,
+    createCooldowns : op.nop,
+    
+    // indestructable
+    dealDamage : op.nop,
+    
+    stats : {
+        hp    : Infinity,
+        move  : 0,
+        power : 0,
+        speed : 0,
+        shots : 0
+    },
+    
+    // Instance 
+    
+    isBoundary : false,
+    
+    
+    
+    init : function initFence(x,y, w,h){
+        this.width = w;
+        this.height = h;
+        this.isBoundary = !!isBoundary;
+        Wall.init.call(this, x,y, w,h);
+        this.position(x,y);
+    },
+    
+    
+    
+    render : function render(parent){
+        if (this.isBoundary)
+            return this;
+        
+        if (this.shape)
+            this.shape.remove();
+        
+        this.shape =
+            new Rect(this.width, this.height)
+                .position(this.loc.x, this.loc.y)
+                .fill('rgba(0,0,0, 0.25)')
+                .stroke('transparent', 0)
+                .appendTo( parent );
+        
+        return this;
+    },
+    
+    toString : function(){
+        var bb = this.bbox
+        ,   x1,y1, x2,y2;
+        if (bb){
+            x1 = bb.x1; y1 = bb.y1;
+            x2 = bb.x2; y2 = bb.y2;
+        } else {
+            x1=y1=x2=y2=NaN;
+        }
+        return this.className+'['+x1+','+y1+', '+x2+','+y2+'](w='+this.width+', h='+this.height+')';
+    }
+});
+
index 91f7e52..76dd7cc 100644 (file)
@@ -1,12 +1,12 @@
-var Y     = require('Y').Y
-,   op    = require('Y/op')
+var Y         = require('Y').Y
+,   op        = require('Y/op')
 
-,   shape = require('ezl/shape')
-,   Rect  = shape.Rect
+,   shape     = require('ezl/shape')
+,   Rect      = shape.Rect
 
-,   map   = require('tanks/map/map')
-,   Buff  = require('tanks/effects/buff').Buff
-,   Thing = require('tanks/thing/thing').Thing
+,   PathingType = require('tanks/constants').PathingType
+,   Buff      = require('tanks/effects/buff').Buff
+,   Thing     = require('tanks/thing/thing').Thing
 
 ,   ITEM_SIZE = REF_SIZE * 0.5
 ,
@@ -20,7 +20,7 @@ Thing.subclass('Item', {
     
     align : 0, // 0 reserved for neutral units
     
-    blocking : map.ZONE,
+    blocking : PathingType.ZONE,
     width    : ITEM_SIZE,
     height   : ITEM_SIZE,
     
index c81167c..617e498 100644 (file)
@@ -1,9 +1,8 @@
 //#ensure "jquery"
 //#ensure "jquery.hotkeys"
 
-var Y    = require('Y').Y
-,   map  = require('tanks/map/map')
-,   Tank = require('tanks/thing/tank').Tank
+var Y           = require('Y').Y
+,   Tank        = require('tanks/thing/tank').Tank
 ,   Inventoried = require('tanks/mixins/inventoried').Inventoried
 ,
 
@@ -13,8 +12,6 @@ exports['Player'] =
 Tank.subclass('Player', {
     __mixins__ : [ Inventoried ],
     
-    blocking : map.BLOCKING,
-    
     // Attributes
     stats: {
         hp        : 1,          // health
index 868ef97..7dabe64 100644 (file)
@@ -10,7 +10,6 @@ var Y             = require('Y').Y
 ,   Rect          = shape.Rect
 ,   Circle        = shape.Circle
 
-,   map           = require('tanks/map/map')
 ,   Thing         = require('tanks/thing/thing').Thing
 ,   Bullet        = require('tanks/thing/bullet').Bullet
 ,   Trajectory    = require('tanks/map/trajectory').Trajectory
@@ -35,7 +34,6 @@ Thing.subclass('Tank', function(Tank){
         },
         
         // Bounding box
-        blocking : map.BLOCKING,
         width  : REF_SIZE*0.55,
         height : REF_SIZE*0.55,
         
@@ -56,7 +54,7 @@ Thing.subclass('Tank', function(Tank){
             shootEnemy    : 0.75    // shoot at enemy tank if in range
         },
         
-        projectile : Bullet,
+        projectile : 'bullet',
         
         /// Instance ///
         
@@ -75,6 +73,7 @@ Thing.subclass('Tank', function(Tank){
     this['init'] =
     function initTank(align){
         Thing.init.call(this, align);
+        this.projectile = Bullet.lookup(this.projectile);
         this.colors = Y.extend({}, this.colors);
         this.atkGauge = new CooldownGauge(this.cooldowns.attack, this.width+1,this.height+1);
         this.onBulletDeath = this.onBulletDeath.bind(this);
index 132c126..9a0ea6f 100644 (file)
@@ -9,7 +9,7 @@ var Y           = require('Y').Y
 ,   Cooldown    = require('ezl/loop').Cooldown
 
 ,   config      = require('tanks/config').config
-,   map         = require('tanks/map/map')
+,   PathingType = require('tanks/constants').PathingType
 ,   stat        = require('tanks/effects/stat')
 ,   Quantified  = require('tanks/mixins/quantified').Quantified
 ,   Speciated   = require('tanks/mixins/speciated').Speciated
@@ -52,7 +52,7 @@ new evt.Class('Thing', {
     dirty   : true,
     
     // Properties
-    blocking     : map.BLOCKING,
+    blocking     : PathingType.BLOCKING,
     isRenderable : false,       // Agent will present itself for rendering when ready // FIXME: stupid hack
     active       : true,        // Agent takes actions?
     isReflective : false,       // Projectiles bounce off agent rather than explode?
index ea84ef2..05cdf84 100644 (file)
@@ -1,8 +1,10 @@
-var Y     = require('Y').Y
-,   op    = require('Y/op')
-,   Rect  = require('ezl/shape').Rect
-,   Thing = require('tanks/thing/thing').Thing
-,   map   = require('tanks/map/map')
+var Y         = require('Y').Y
+,   op        = require('Y/op')
+
+,   Rect      = require('ezl/shape').Rect
+
+,   Thing     = require('tanks/thing/thing').Thing
+,   PathingType = require('tanks/constants').PathingType
 ,
 
 
@@ -14,7 +16,7 @@ Thing.subclass('Wall', {
     originX : 0,
     originY : 0,
     
-    blocking : map.BLOCKING,
+    blocking : PathingType.BLOCKING,
     isReflective : true,
     isRenderable : true,
     
index e4cfe2b..f7846df 100644 (file)
@@ -1,26 +1,60 @@
 //#ensure "jquery"
+//#ensure "jquery.masonry"
 var Y = require('Y').Y
 ,   cookies   = require('Y/modules/y.cookies')
 ,   scaffold  = require('Y/modules/y.scaffold')
 ,   EventLoop = require('ezl/loop/eventloop').EventLoop
 ,   config    = require('tanks/config').config
-,   configUI, fields
+
+,   ordering = 'game pathing ui debug'.split(' ')
+,   el   = exports['el']   = null
+,   data = exports['data'] = null
 ;
 
+
 exports['init'] =
 function initConfigUi(){
-    configUI = jQuery('#config .box');
-    fields   = scaffold.create(config, configUI);
-    Y.core.forEach(fields, function(field, key){
+    el   = exports['el']   = jQuery('#config .box');
+    data = exports['data'] = scaffold.create(config, el);
+    
+    Y.core.forEach(data.fields, function(field, key){
         var cookie = cookies.get(key);
         if (cookie) config.set(key, field.cast(cookie));
     });
     
-    configUI.append( jQuery('<div class="clearer"></div>') );
+    // Apply ordering
+    Y(data.groups).invoke('remove');
+    Y(ordering).forEach(function(k){
+        data.groups[k].appendTo(el);
+    });
+    // Attach leftovers
+    Y(data.groups).forEach(function(g, k){
+        if ( !g.parent().length )
+            g.appendTo(el);
+    });
+    
+    $('#config').one('show', function(evt){
+        el.masonry({
+            singleMode: true,
+            // Disables measuring the width of each floated element.
+            // Set to true if floated elements have the same width.
+            
+            itemSelector: 'fieldset.group',
+            // Additional selector to specify which elements inside
+            // the wrapping element will be rearranged.
+            // Required for Infinite Scroll with window resizing.
+            
+            resizeable: false
+            // Binds a Masonry call to window resizes
+            // so layout appears fluid.
+        });
+        el.append( jQuery('<div class="clearer"></div>') );
+    });
 };
 
 // Persist config via cookies
 config.addEventListener('set', function(evt){
+    // console.log('config.set', evt);
     var d = evt.data;
     if (d.newval !== d.defval)
         cookies.set(d.key, d.newval);
index 458a1e9..f40903a 100644 (file)
@@ -36,26 +36,26 @@ Rect.subclass('Grid', {
         ,   h    =  this.canvasHeight
         ;
         
-        if ( this.createGridTable ) {
-            var tbody = $('<tbody/>');
-            Y(0, rows).forEach(function(y){
-                var row = $('<tr class="row row'+y+'" />').appendTo(tbody);
-                Y(0, cols).forEach(function(x){
-                    $('<td class="cell cell'+x+'_'+y+' col'+x+'">'+x+','+y+'</td>')
-                        .width(side).height(side)
-                        .appendTo(row);
-                }, this);
-            }, this);
-            
-            this.table =
-                $('<table class="grid" cellspacing="0" cellpadding="0" />')
-                    .width(this.layerWidth)
-                    .height(this.layerHeight)
-                    .append(tbody)
-                    .prependTo( this.layer );
-            
-        } else
-            this.table = null;
+        // if ( this.createGridTable ) {
+        //     var tbody = $('<tbody/>');
+        //     Y(0, rows).forEach(function(y){
+        //         var row = $('<tr class="row row'+y+'" />').appendTo(tbody);
+        //         Y(0, cols).forEach(function(x){
+        //             $('<td class="cell cell'+x+'_'+y+' col'+x+'">'+x+','+y+'</td>')
+        //                 .width(side).height(side)
+        //                 .appendTo(row);
+        //         }, this);
+        //     }, this);
+        //     
+        //     this.table =
+        //         $('<table class="grid" cellspacing="0" cellpadding="0" />')
+        //             .width(this.layerWidth)
+        //             .height(this.layerHeight)
+        //             .append(tbody)
+        //             .prependTo( this.layer );
+        //     
+        // } else
+        //     this.table = null;
         
         if ( this.createGridCanvas ) {
             ctx.lineWidth   = this.lineWidth;
index 2a41016..a73ff83 100644 (file)
@@ -1,6 +1,5 @@
 var Y = require('Y').Y;
 
-Y.extend(exports, require('tanks/ui/configui'));
-
 exports['PathMapUI'] = require('tanks/ui/pathmapui').PathMapUI;
 exports['main'] = require('tanks/ui/main');
+exports['config'] = require('tanks/ui/configui');
index 6497e3d..4a9e494 100644 (file)
@@ -12,9 +12,12 @@ var Y            = require('Y').Y
 ,   Game         = require('tanks/game').Game
 ,   Tank         = require('tanks/thing').Tank
 
-,   config       = cfg.config
-,   updateTimer  = null
-,   LBT = null, overlay = null
+,   HIDE = 0, SHOW = 1, TOGGLE = 2
+
+,   config      = cfg.config
+,   updateTimer = null
+,   game        = null
+,   overlay     = null
 ;
 function stopProp(evt){ evt.stopPropagation(); }
 
@@ -25,6 +28,9 @@ qkv = Y(window.location.search.slice(1)).fromKV();
 function main(){
     $('#loading').center();
     
+    // Build and bind config
+    configui.init();
+    
     overlay = $('#overlay');
     updateOverlay( config.get('ui.overlayOnPause') );
     config.addEventListener('set:ui.overlayOnPause', function(evt){ updateOverlay(evt.newval); });
@@ -35,7 +41,7 @@ function main(){
         $('#ai').toggle();
         $('#ai textarea')[0].focus();
     }
-    if (qkv.debug) $('#info').toggle();
+    if (qkv.debug || config.get('debug.showFpsGraph')) updateUI('#info', TOGGLE);
     
     $('#ai .ready').bind('click', function(evt){
         try {
@@ -49,17 +55,14 @@ function main(){
     });
     
     // Show debug menus
-    $(document).bind('keydown', 'ctrl+d', function(evt){ $('.debug').toggle(); });
-    $(document).bind('keydown', 'ctrl+i', function(evt){ $('#info').toggle(); });
-    $(document).bind('keydown', 'ctrl+c', function(evt){ $('#config').toggle(); });
+    $(document).bind('keydown', 'ctrl+d', function(evt){ updateUI('.debug', TOGGLE); });
+    $(document).bind('keydown', 'ctrl+i', function(evt){ updateUI('#info', TOGGLE); });
+    $(document).bind('keydown', 'ctrl+c', function(evt){ updateUI('#config', TOGGLE); });
     
     // Don't fire on clicks in the debug menu
     $('.debug').bind('mousedown', stopProp);
     $('.debug').bind('click',     stopProp);
     
-    // Build and bind config
-    configui.init();
-    
     // Create #pause box
     $('#loading').clone()
         .attr('id', 'pause')
@@ -87,7 +90,7 @@ function main(){
         });
     
     
-    cfg.dataLoader
+    cfg.dataLoader()
         .addEventListener('complete', function(evt){
             $('#loading').hide();
             $('#welcome').show();
@@ -102,16 +105,16 @@ function gameExists(){ return !!tanks.game; }
 function setupGame(){
     if ( gameExists() ) teardownGame();
     
-    LBT = tanks.game = new Game();
+    game = tanks.game = new Game();
     
-    LBT.addEventListener('win',  gameover('You Win!', 'Play Again', ''));
-    LBT.addEventListener('lose', gameover('You Lose :(', 'Try Again', ''));
+    game.addEventListener('win',  gameover('You Win!', 'Play Again', ''));
+    game.addEventListener('lose', gameover('You Lose :(', 'Try Again', ''));
     
-    ctx = LBT.level.ctx;
+    ctx = game.level.ctx;
 }
 
 function teardownGame(){
-    LBT.destroy();
+    game.destroy();
     $('#viewport').empty();
 }
 
@@ -121,15 +124,15 @@ function setupUI(){
     if ( gameExists() ) teardownUI();
     
     if (!qkv.ai) $('#welcome').show();
-    LBT.loop.spark = new FpsSparkline(LBT.loop, '.fps-sparkline', 0,0);
-    LBT.root.draw();
+    game.loop.spark = new FpsSparkline(game.loop, '.fps-sparkline', 0,0);
+    game.root.draw();
     
     // Start button (click or return key)
     if (!qkv.ai) $(document).bind('click', startGame);
     $(document).bind('keydown', 'return', startGame);
     
     
-    LBT.root.draw();
+    game.root.draw();
     updateTimer = setInterval(updateInfo, 1000);
     
     updateInfo();
@@ -170,20 +173,20 @@ function pauseGame(evt){
 }
 
 function toggleGame(evt){
-    if (LBT.loop.running)
-        LBT.stop();
+    if (game.loop.running)
+        game.stop();
     else
-        LBT.start();
+        game.start();
     
     updateInfo();
 }
 
 function resizeGame(evt){
-    LBT.resize(evt);
+    game.resize(evt);
     
-    if (!LBT.loop.running) {
-        LBT.start();
-        LBT.stop();
+    if (!game.loop.running) {
+        game.start();
+        game.stop();
     }
 }
 
@@ -257,12 +260,12 @@ function readyToStart(){
 
 // Update performance info periodically
 function updateInfo(){
-    var loop     = LBT.loop
+    var loop     = game.loop
     ,   fps      = loop.fps()
-    ,   n_active = LBT.active.size()
-    ,   n_units  = LBT.units.size()
-    ,   n_projs  = LBT.bullets.size()
-    ,   n_anims  = LBT.animations.size()
+    ,   n_active = game.active.size()
+    ,   n_units  = game.units.size()
+    ,   n_projs  = game.bullets.size()
+    ,   n_anims  = game.animations.size()
     ;
     
     $('#info #state').text( loop.running ? 'Running!' : ('Paused (tick '+TICKS+')') );
@@ -299,6 +302,27 @@ function updateOverlay(show){
         overlay.remove();
 }
 
+function updateUI(selector, action){
+    var el = $(selector);
+    
+    if (action === TOGGLE){
+        if (el.length > 1)
+            return el.forEach(function(){ updateUI(this, action); });
+        action = (el.filter(':visible').length ? HIDE : SHOW);
+    }
+    
+    switch (action) {
+        case HIDE:
+            el.hide();
+            el.trigger('hide');
+        break;
+        case SHOW:
+            el.show();
+            el.trigger('show');
+        break;
+    }
+}
+
 
 exports['main']         = main;
 exports['gameExists']   = gameExists;
index 1e4e2b7..9bf2d6d 100644 (file)
@@ -1,12 +1,13 @@
 //#ensure "jquery"
-var Y       = require('Y').Y
+var Y         = require('Y').Y
 
-,   Rect    = require('ezl/shape').Rect
-,   vec     = require('ezl/math/vec')
+,   Rect      = require('ezl/shape').Rect
+,   vec       = require('ezl/math/vec')
+
+,   config    = require('tanks/config').config
+,   PathingType = require('tanks/constants').PathingType
+,   PathMap   = require('tanks/map/pathmap').PathMap
 
-,   map     = require('tanks/map/map')
-,   PathMap = require('tanks/map/pathmap').PathMap
-,   config  = require('tanks/config').config
 ,
 
 
@@ -80,7 +81,7 @@ Rect.subclass('PathMapUI', {
             return acc;
         acc[r.id] = r;
         
-        if (v.blocking === map.ZONE) {
+        if (v.blocking === PathingType.ZONE) {
             ctx.fillStyle   = this.zoneFillStyle;
             ctx.strokeStyle = this.zoneStrokeStyle;
             ctx.lineWidth   = this.zoneLineWidth;
index 31f3557..0c5fb9b 100644 (file)
@@ -67,16 +67,16 @@ td { text-align:center; vertical-align:middle; }
     #info .sep { opacity:0.1; background-color:#999; margin:5px 0; height:1px; }
     #info .fps-sparkline { width:100%; height:1.5em; margin-top:0.5em; }
 
-#config { position:absolute; bottom:1em; left:1em; }
+#config { position:absolute; bottom:1em; left:1em; max-width:900px; min-width:300px; width:900px; }
     #config .box { }
     #config .group { width:250px; float:left; margin-right:1em; }
     #config .group:last-of-type { margin-right:0; }
-    #config h3 { font-weight:bold; margin:0; position:absolute; bottom:1em; left:1em; }
+    #config h3 { font-weight:bold; margin:0; /* position:absolute; bottom:1em; left:1em; */ }
     #config fieldset { border:1px solid #999; padding:0.5em; }
     #config legend { padding:0.5em; text-transform:uppercase; color:#787878; }
     #config .field { position:relative; line-height:1.5em; }
     #config label { position:relative; width:69%; display:inline-block; text-transform:lowercase; }
-    #config input { position:absolute; width:30%; top:0; right:0; text-align:center;
+    #config input { position:absolute; width:30%; top:2px; right:0; text-align:center;
         border:0; background-color:rgba(0,0,0, 0.1) ! important; color:#5c5c5c; }
     #config input[type=checkbox] { top:0.33em; }
 
index 15fd901..7cdd17f 100644 (file)
 <div id="config" class="debug" style="display:none"><div class="inner hud">
     <h3>config</h3>
     <div class="box"></div>
-    <!--
-    <div><label for="pathmap">overlay pathmap</label> <input id="pathmap" name="pathmap" value="1" type="checkbox"></div>
-    <div><label for="aipaths">overlay ai paths</label> <input id="aipaths" name="aipaths" value="1" type="checkbox"></div>
-    <div><label for="trajectories">trace trajectories</label> <input id="trajectories" name="trajectories" value="1" type="checkbox"></div>
-    <div><label for="gridCoords">show coords on hover</label> <input id="gridCoords" name="gridCoords" value="1" type="checkbox"></div>-->
 </div></div>
 
index 2de7617..817a88d 100644 (file)
@@ -47,6 +47,7 @@
 <script src="build/ezl.js" type="text/javascript"></script>
 <script src="build/tanks/globals.js" type="text/javascript"></script>
 <script src="build/jquery.hotkeys.js" type="text/javascript"></script>
+<script src="build/jquery.masonry.js" type="text/javascript"></script>
 <script src="build/Y/modules/y.kv.js" type="text/javascript"></script>
 <script src="build/ezl/util/tree/binaryheap.js" type="text/javascript"></script>
 <script src="build/tanks/constants.js" type="text/javascript"></script>
 <script src="build/Y/modules/y.config.js" type="text/javascript"></script>
 <script src="build/tanks/effects/stat.js" type="text/javascript"></script>
 <script src="build/tanks/map/map.js" type="text/javascript"></script>
+<script src="build/tanks/map/traversal.js" type="text/javascript"></script>
 <script src="build/Y/modules/y.cookies.js" type="text/javascript"></script>
 <script src="build/ezl/util/data/datafile.js" type="text/javascript"></script>
 <script src="build/ezl/util/data/loader.js" type="text/javascript"></script>
 <script src="build/tanks/mixins/speciated.js" type="text/javascript"></script>
 <script src="build/tanks/mixins/meronomic.js" type="text/javascript"></script>
-<script src="build/tanks/map/traversal.js" type="text/javascript"></script>
 <script src="build/tanks/config.js" type="text/javascript"></script>
 <script src="build/tanks/ui/configui.js" type="text/javascript"></script>
 <script src="build/tanks/mixins/quantified.js" type="text/javascript"></script>