From: dsc Date: Sun, 9 Jan 2011 04:28:31 +0000 (-0800) Subject: Levels are now config-driven. X-Git-Url: http://git.less.ly:3516/?a=commitdiff_plain;h=02a65adeeda449ff0fae9acac258e9aba4c46293;p=tanks.git Levels are now config-driven. --- diff --git a/data/config.yaml b/data/config.yaml index e69de29..6b4336d 100644 --- a/data/config.yaml +++ b/data/config.yaml @@ -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 diff --git a/data/types/buffs.yaml b/data/types/buffs.yaml index 4757a85..9a555e6 100644 --- a/data/types/buffs.yaml +++ b/data/types/buffs.yaml @@ -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 diff --git a/data/types/bullets.yaml b/data/types/bullets.yaml index e69de29..728a8db 100644 --- a/data/types/bullets.yaml +++ b/data/types/bullets.yaml @@ -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 diff --git a/data/types/items.yaml b/data/types/items.yaml index 8565072..bdd80b5 100644 --- a/data/types/items.yaml +++ b/data/types/items.yaml @@ -1,6 +1,4 @@ -# Item definitions -name: item -lookup: .tanks.data.items +name: items defaults: symbol: tanks/thing/item.Item types: diff --git a/data/types/levels.yaml b/data/types/levels.yaml index a31d9cd..e6321db 100644 --- a/data/types/levels.yaml +++ b/data/types/levels.yaml @@ -1,6 +1,4 @@ -# Level definitions -name: level -lookup: .tanks.data.levels +name: levels defaults: symbol: tanks/map/level.Level types: diff --git a/data/types/units.yaml b/data/types/units.yaml index 5b725bb..d310033 100644 --- a/data/types/units.yaml +++ b/data/types/units.yaml @@ -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 index 0000000..e127b3a --- /dev/null +++ b/lib/jquery.masonry-1.3.2.js @@ -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 index 0000000..f6e8a82 --- /dev/null +++ b/lib/jquery.masonry-1.3.2.min.js @@ -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') + var g = + groups[chain] = + jQuery('
') .attr('id', chain) .addClass('group') .append( jQuery('').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; } ; diff --git a/src/Y/types/object.cjs b/src/Y/types/object.cjs index a383701..49ff70b 100644 --- a/src/Y/types/object.cjs +++ b/src/Y/types/object.cjs @@ -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; + } }); diff --git a/src/ezl/util/data/datafile.cjs b/src/ezl/util/data/datafile.cjs index 2bbf1f3..699ddf4 100644 --- a/src/ezl/util/data/datafile.cjs +++ b/src/ezl/util/data/datafile.cjs @@ -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); diff --git a/src/ezl/util/data/loader.cjs b/src/ezl/util/data/loader.cjs index 94e4186..b2f564a 100644 --- a/src/ezl/util/data/loader.cjs +++ b/src/ezl/util/data/loader.cjs @@ -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); diff --git a/src/tanks/config.cjs b/src/tanks/config.cjs index 23c4a96..b128344 100644 --- a/src/tanks/config.cjs +++ b/src/tanks/config.cjs @@ -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); diff --git a/src/tanks/game.cjs b/src/tanks/game.cjs index 5d8b883..b31c13d 100644 --- a/src/tanks/game.cjs +++ b/src/tanks/game.cjs @@ -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) diff --git a/src/tanks/map/pathmap.cjs b/src/tanks/map/pathmap.cjs index d4d72ee..3a09ae2 100644 --- a/src/tanks/map/pathmap.cjs +++ b/src/tanks/map/pathmap.cjs @@ -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(){ diff --git a/src/tanks/map/traversal.cjs b/src/tanks/map/traversal.cjs index f926a39..9d9237b 100644 --- a/src/tanks/map/traversal.cjs +++ b/src/tanks/map/traversal.cjs @@ -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; diff --git a/src/tanks/mixins/speciated.cjs b/src/tanks/mixins/speciated.cjs index 24bcf8c..544c3d6 100644 --- a/src/tanks/mixins/speciated.cjs +++ b/src/tanks/mixins/speciated.cjs @@ -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) diff --git a/src/tanks/thing/bullet.cjs b/src/tanks/thing/bullet.cjs index 22088e2..41be7dc 100644 --- a/src/tanks/thing/bullet.cjs +++ b/src/tanks/thing/bullet.cjs @@ -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 index 97dcecf..0000000 --- a/src/tanks/thing/customtank.cjs +++ /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 index 0000000..25e1c2b --- /dev/null +++ b/src/tanks/thing/fence.cjs @@ -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+')'; + } +}); + diff --git a/src/tanks/thing/item.cjs b/src/tanks/thing/item.cjs index 91f7e52..76dd7cc 100644 --- a/src/tanks/thing/item.cjs +++ b/src/tanks/thing/item.cjs @@ -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, diff --git a/src/tanks/thing/player.cjs b/src/tanks/thing/player.cjs index c81167c..617e498 100644 --- a/src/tanks/thing/player.cjs +++ b/src/tanks/thing/player.cjs @@ -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 diff --git a/src/tanks/thing/tank.cjs b/src/tanks/thing/tank.cjs index 868ef97..7dabe64 100644 --- a/src/tanks/thing/tank.cjs +++ b/src/tanks/thing/tank.cjs @@ -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); diff --git a/src/tanks/thing/thing.cjs b/src/tanks/thing/thing.cjs index 132c126..9a0ea6f 100644 --- a/src/tanks/thing/thing.cjs +++ b/src/tanks/thing/thing.cjs @@ -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? diff --git a/src/tanks/thing/wall.cjs b/src/tanks/thing/wall.cjs index ea84ef2..05cdf84 100644 --- a/src/tanks/thing/wall.cjs +++ b/src/tanks/thing/wall.cjs @@ -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, diff --git a/src/tanks/ui/configui.cjs b/src/tanks/ui/configui.cjs index e4cfe2b..f7846df 100644 --- a/src/tanks/ui/configui.cjs +++ b/src/tanks/ui/configui.cjs @@ -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('
') ); + // 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('
') ); + }); }; // 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); diff --git a/src/tanks/ui/grid.cjs b/src/tanks/ui/grid.cjs index 458a1e9..f40903a 100644 --- a/src/tanks/ui/grid.cjs +++ b/src/tanks/ui/grid.cjs @@ -36,26 +36,26 @@ Rect.subclass('Grid', { , h = this.canvasHeight ; - if ( this.createGridTable ) { - var tbody = $(''); - Y(0, rows).forEach(function(y){ - var row = $('').appendTo(tbody); - Y(0, cols).forEach(function(x){ - $(''+x+','+y+'') - .width(side).height(side) - .appendTo(row); - }, this); - }, this); - - this.table = - $('') - .width(this.layerWidth) - .height(this.layerHeight) - .append(tbody) - .prependTo( this.layer ); - - } else - this.table = null; + // if ( this.createGridTable ) { + // var tbody = $(''); + // Y(0, rows).forEach(function(y){ + // var row = $('').appendTo(tbody); + // Y(0, cols).forEach(function(x){ + // $('') + // .width(side).height(side) + // .appendTo(row); + // }, this); + // }, this); + // + // this.table = + // $('
'+x+','+y+'
') + // .width(this.layerWidth) + // .height(this.layerHeight) + // .append(tbody) + // .prependTo( this.layer ); + // + // } else + // this.table = null; if ( this.createGridCanvas ) { ctx.lineWidth = this.lineWidth; diff --git a/src/tanks/ui/index.cjs b/src/tanks/ui/index.cjs index 2a41016..a73ff83 100644 --- a/src/tanks/ui/index.cjs +++ b/src/tanks/ui/index.cjs @@ -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'); diff --git a/src/tanks/ui/main.cjs b/src/tanks/ui/main.cjs index 6497e3d..4a9e494 100644 --- a/src/tanks/ui/main.cjs +++ b/src/tanks/ui/main.cjs @@ -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; diff --git a/src/tanks/ui/pathmapui.cjs b/src/tanks/ui/pathmapui.cjs index 1e4e2b7..9bf2d6d 100644 --- a/src/tanks/ui/pathmapui.cjs +++ b/src/tanks/ui/pathmapui.cjs @@ -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; diff --git a/www/css/lttl.css b/www/css/lttl.css index 31f3557..0c5fb9b 100644 --- a/www/css/lttl.css +++ b/www/css/lttl.css @@ -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; } diff --git a/www/debug.html b/www/debug.html index 15fd901..7cdd17f 100644 --- a/www/debug.html +++ b/www/debug.html @@ -24,10 +24,5 @@ diff --git a/www/deps.html b/www/deps.html index 2de7617..817a88d 100644 --- a/www/deps.html +++ b/www/deps.html @@ -47,6 +47,7 @@ + @@ -55,12 +56,12 @@ + -