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