+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
-# 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
+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
-# Item definitions
-name: item
-lookup: .tanks.data.items
+name: items
defaults:
symbol: tanks/thing/item.Item
types:
-# Level definitions
-name: level
-lookup: .tanks.data.levels
+name: levels
defaults:
symbol: tanks/map/level.Level
types:
-# 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)
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
--- /dev/null
+/*************************************************
+** 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
--- /dev/null
+/*************************************************
+** 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
--- /dev/null
+jquery.masonry-1.3.2.js
\ No newline at end of file
@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)
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)
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);
},
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];
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(){
})
.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;
'key' : this.id,
'oldval' : this.old,
'newval' : this.val,
- 'el' : this.elField
+ 'el' : jQuery(this.selector)
});
el.val(val);
}
},
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()) );
}
})
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)
return group;
},
el);
- return fields;
+ return data;
}
;
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;
+ }
});
path : '',
- init : function initDataFile(path){
+ init : function initDataFile(path, datastore){
this.path = path;
+ this.datastore = datastore;
},
load : function load(){
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;
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);
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);
// -*- 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
/// 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 =
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);
},
+ 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)
, 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
* @private
*/
_filterBlocked : function filterBlocked(v, r){
- return v.blocking === map.BLOCKING && !v.isBoundary;
+ return v.blocking === PathingType.BLOCKING && !v.isBoundary;
},
getNeighbors : function getNeighbors(){
var Y = require('Y').Y
-, map = require('tanks/map/map')
+, PathingType = require('tanks/constants').PathingType
,
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;
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();
},
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)
, 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
},
// Instance
- blocking : map.BLOCKING,
+ blocking : PathingType.BLOCKING,
isRenderable : true,
bounces : 0,
width : 6,
height : 6,
+ color: '#FFF6AE',
+
stats : {
move : 2.0 // move speed (squares/sec)
},
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);
;
// Ignore collisions with zones
- if (unit.blocking === map.ZONE)
+ if (unit.blocking === PathingType.ZONE)
return;
// Reflection!
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);
+++ /dev/null
-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+'} })');
- }
-
-});
--- /dev/null
+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+')';
+ }
+});
+
-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
,
align : 0, // 0 reserved for neutral units
- blocking : map.ZONE,
+ blocking : PathingType.ZONE,
width : ITEM_SIZE,
height : ITEM_SIZE,
//#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
,
Tank.subclass('Player', {
__mixins__ : [ Inventoried ],
- blocking : map.BLOCKING,
-
// Attributes
stats: {
hp : 1, // health
, 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
},
// Bounding box
- blocking : map.BLOCKING,
width : REF_SIZE*0.55,
height : REF_SIZE*0.55,
shootEnemy : 0.75 // shoot at enemy tank if in range
},
- projectile : Bullet,
+ projectile : 'bullet',
/// Instance ///
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);
, 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
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?
-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
,
originX : 0,
originY : 0,
- blocking : map.BLOCKING,
+ blocking : PathingType.BLOCKING,
isReflective : true,
isRenderable : true,
//#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);
, 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;
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');
, 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(); }
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); });
$('#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 {
});
// 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')
});
- cfg.dataLoader
+ cfg.dataLoader()
.addEventListener('complete', function(evt){
$('#loading').hide();
$('#welcome').show();
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();
}
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();
}
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();
}
}
// 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+')') );
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;
//#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
,
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;
#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; }
<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>
<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>