table.grid td { /* outline:1px solid rgba(255,255,255,0.1); */
color:transparent; font: 18pt monospace;
margin:0; padding:0; white-space:nowrap; overflow:hidden; }
-.gridShowCoords table.grid td:hover { color:rgba(255,255,255,0.1); }
+.showGridCoords table.grid td:hover { color:rgba(255,255,255,0.1); }
.bigblue { position:fixed; width:100%; top:50%; margin-top:-200px; z-index:101; }
.bigblue .box { width:400px; margin:0 auto; padding:1em; color:#000; background-color:#2992C5;
Y.YArray =
YCollection.subclass('YArray', function(YArray){
this.init = function(o){
- YCollection.init.call(this, o || []);
+ // YCollection.init.call(this, o || []);
+ this._o = o || [];
};
this.merge =
function concat( donor ){
var A = this._o;
new Y(arguments).forEach(function( donor ){
- A = A.concat(donor);
+ A = A.concat(donor instanceof Y.YArray ? donor.end() : donor);
});
return Y(A);
};
this.remove =
function remove(v){
- var idx = this.indexOf(v);
- if ( idx != -1 )
- this.splice(idx, 1);
+ if (arguments.length > 1)
+ Y(arguments).forEach(this.remove, this);
+ else {
+ var idx = this.indexOf(v);
+ if ( idx != -1 )
+ this.splice(idx, 1);
+ }
return this;
};
/// Other Class Utilities ///
+function bindName(v, k){
+ if ( isFunction(v) )
+ this[k] = Y(this[k]).bind(this);
+}
+// bindName = Y(bindName).curry();
+
Y.bindAll = bindAll;
-function bindAll(o){
- for (var k in o)
- if (isFunction(o[k])) o[k] = o[k].bind(o);
+function bindAll(o, names){
+ var names = new Y(arguments, 1);
+ Y(names.size() ? names.generate(Y.op.get(o)) : o).forEach( bindName, o );
+
+ // if ( names.size() ){
+ // names.forEach(binder);
+ // } else
+ // for (var k in o) binder(k);
+
return o;
}
this._o = o;
},
- 'attr' : function(k, v, def){
+ 'attr' : function attr(k, v, def){
var r = Y.attr(this._o, k, v, def);
if (r === this._o)
return this;
'merge' : extendY,
'concat' : extendY,
- 'end' : function(){ return this._o; },
+ 'end' : function end(){ return this._o; },
- 'reduce' : function( fn, acc, context ){
+ 'reduce' : function reduce( fn, acc, context ){
var o = this._o || this,
acc = acc || new o.constructor();
for ( var name in o )
return acc;
},
- 'map' : function( fn, context ){
+ 'map' : function map( fn, context ){
var o = this._o, acc = new o.constructor();
for ( var name in o )
acc[name] = fn.call( context || this, o[name], name, o );
return acc;
},
- 'forEach' : function( fn, context ){
+ 'forEach' : function forEach( fn, context ){
var o = this._o;
for ( var name in o )
fn.call( context || this, o[name], name, o );
},
- 'filter' : function( fn, context ){
+ 'filter' : function filter( fn, context ){
var o = this._o, acc = new o.constructor();
for ( var name in o )
if ( fn.call( context || this, o[name], name, o ) )
return acc;
},
- 'indexOf' : function( value ){
+ 'indexOf' : function indexOf( value ){
var o = this._o;
for ( var name in o )
if ( o[name] === value )
return -1;
},
- 'has' : function( value ){
+ 'has' : function has( value ){
return ( this.indexOf(value) !== -1 );
},
- 'clone' : function(){
+ 'clone' : function clone(){
return Y({}).extend(this);
},
- 'remove' : function(v){
- var o = this._o;
+ 'remove' : function remove(v){
+ var o = this._o
+ , toRemove = new Y(arguments);
+
for (var k in o) {
- if (o[k] !== v) continue;
- delete o[k];
- break;
+ var v = o[k];
+ if ( toRemove.has(v) ) {
+ delete o[k];
+ toRemove.remove(v);
+ }
}
return this;
},
- 'every' : function( fn ){
+ 'every' : function every( fn ){
var self = this, fn = fn || bool;
return this.reduce(function(acc, v, k, o){
return acc && fn.call(self, v, k, o);
}, true);
},
- 'any' : function( fn ){
+ 'any' : function any( fn ){
var self = this, fn = fn || bool;
return this.reduce(function(acc, v, k, o){
return acc || fn.call(self, v, k, o);
}, false);
},
- 'zip' : function(){
+ 'zip' : function zip(){
var sequences = new Y(arguments).map( Y.limit(1) ).unshift(this);
return this.map(function(_, k){
return sequences.invoke('attr', k);
}).invoke('end');
},
- 'pluck' : function(key){
+ 'pluck' : function pluck(key){
return this.map(function(v){
return v && (isFunction(v.attr) ? v.attr(key) : v[key]);
});
},
- 'invoke' : function(name){
+ 'invoke' : function invoke(name){
var args = Y(arguments),
name = args.shift();
return this.map(function(o){
return o && o[name].apply(o, args);
});
+ },
+
+ 'apply' : function apply(name, args){
+ return this[name].apply(this, args);
}
});
var _bind = _Function.prototype.bind;
YFunction.prototype.bind =
-function bind(context, args){
- var bound = _bind.apply(this, arguments);
- bound[WRAPS] = this;
- return Y(bound);
-};
+ function bind(context, args){
+ var bound = _bind.apply(this, arguments);
+ bound[WRAPS] = this;
+ return Y(bound);
+ };
// Remembers arguments but obeys current context
YFunction.prototype.partial =
-function partial(){
- var fn = this
- , args = Y(arguments)
- , partially = function(){
- return fn.apply( this, args.concat(Y(arguments)) );
- };
- partially[WRAPS] = fn;
- return Y(partially);
-};
+ function partial(){
+ var fn = this
+ , args = Y(arguments)
+ , partially = function(){
+ return fn.apply( this, args.concat(Y(arguments)) );
+ };
+ partially[WRAPS] = fn;
+ return Y(partially);
+ };
// Only works for arguments whose toString is unique and stateless (for example, primitives, but not closures).
// },
manhattan: function manhattan(x1,y1, x2,y2) {
- if (x1 instanceof math.Vec && y1 instanceof math.Vec) {
- y2 = y1.y; x2 = y1.x;
- y1 = x1.y; x1 = x1.x;
+ if (x1 instanceof Array && y1 instanceof Array) {
+ y2 = y1[1]; x2 = y1[0];
+ y1 = x1[1]; x1 = x1[0];
}
var d1 = Math.abs(x2 - x1)
, d2 = Math.abs(y2 - y1) ;
// -*- mode: JavaScript; tab-width: 4; indent-tabs-mode: nil; -*-
tanks.config = {
- debug : {
- gridShowCoords : false
+ ui : {
+ showGridCoords : false,
+ showCountdown : false
},
pathing : {
overlayPathmap : false,
tanks.Game = new Y.Class('Game', {
+ overlayPathmap : false,
+
init : function init(viewport){
Y.bindAll(this);
-
tanks.resetGlobals();
this.byId = {};
this.fire('ready', this);
},
-
+ destroy : function destroy(){
+ this.stop();
+ tanks.resetGlobals();
+ },
draw : function draw(){
this.root.draw();
return unit;
},
- moveAgentTo : function moveAgentTo(agent, x,y){
+ moveUnitTo : function moveUnitTo(agent, x,y){
this.pathmap.removeBlocker(agent);
agent.setLocation(x,y);
this.pathmap.addBlocker(agent);
+ /**
+ * @obsolete
+ */
resize : function resize(){
var ratio = COLUMNS / ROWS
, el = this.el
this.canvas.remove();
this.game = game;
- this.walls = [];
+ this.walls = Y([]);
this.pathmap = new PathMap(this, 0,0, w, h, CAPACITY);
game.addEventListener('ready', this.setup.bind(this));
addWall : function addWall(x,y, w,h, isBoundary){
var wall = new Level.Wall(x,y, w,h, isBoundary);
- this.walls.push(wall);
this.pathmap.addBlocker(wall);
-
this.append( wall.render(this).shape );
+
+ if (!isBoundary)
+ this.walls.push(wall);
return wall;
},
Loc.Rect = new Y.Class('Rect', [], {
init : function initRect(x1,y1, x2,y2){
- if (x1 instanceof Loc && y1 instanceof Loc) {
- y2 = y1.y; x2 = y1.x;
- y1 = x1.y; x1 = x1.x;
+ if (x1 instanceof Array && y1 instanceof Array) {
+ y2 = y1[1]; x2 = y1[0];
+ y1 = x1[1]; x1 = x1[0];
}
// init : function initRect(_x1,_y1, _x2,_y2){
- // if (_x1 instanceof Loc && _y1 instanceof Loc) {
- // _y2 = _y1.y; _x2 = _y1.x;
- // _y1 = _x1.y; _x1 = _x1.x;
+ // if (_x1 instanceof Array && _y1 instanceof Array) {
+ // _y2 = _y1[1]; _x2 = _y1[0];
+ // _y1 = _x1[1]; _x1 = _x1[0];
// }
// var x1 = Math.min(_x1,_x2), x2 = Math.max(_x1,_x2)
// , y1 = Math.min(_y1,_y2), y2 = Math.max(_y1,_y2);
}
});
-Loc.BoundingBox = new Y.Class('BoundingBox', Loc.Rect, {
+Loc.BoundingBox = Loc.Rect.subclass('BoundingBox', {
init : function initBoundingBox(x1,y1, x2,y2){
Loc.Rect.init.call(this, x1,y1, x2,y2);
this.boundaryWalls = Y.map(BWs, 'this.level.addWall.apply(this.level, _)', this);
},
- wallObstructs : function wallObstructs(line, w,h){
+ wallObstructs : function wallObstructs(line){
+ // var walls = this.walls;
+ // for (var i=0, L=walls.length, w=walls[i]; i<L; w=walls[++i]) {
+ // if ( w.boundingBox.intersects(line) )
+ // return w;
+ // }
+ // return false;
return this.walls.some(function(wall){
- var bb = wall.boundingBox
- , ss = bb.sides ;
- return ( line.calcY(bb.x1) >= bb.y1 && line.calcY(bb.x2) <= bb.y2 )
- || ( line.calcX(bb.y1) >= bb.x1 && line.calcX(bb.y2) <= bb.x2 ) ;
+ return wall.boundingBox.intersects(line);
}, this);
},
init : function initTrajectory(owner, x1,y1, x2,y2, tdist){
+ Y.bindAll(this, 'cmp', 'closer');
+
this.owner = owner;
this.game = owner.game;
this.pathmap = this.game.pathmap;
},
reset : function reset(x1,y1, x2,y2, tdist){
+ if (x1 instanceof Array && y1 instanceof Array) {
+ tdist = x2;
+ y2 = y1[1]; x2 = y1[0];
+ y1 = x1[1]; x1 = x1[0];
+ }
+
// init with raw numbers to do calculations
math.Line.init.call(this, x1,y1, x2,y2, tdist || this.tdist);
return this;
},
+ /**
+ * Compares how distant in the future two objects are on this trajectory.
+ * Objects that have been passed are always further away than those in the future,
+ * but otherwise the comparison is performed by absolute distance.
+ * @returns -1 if a closer b, 1 if a further b, 0 if a same as b
+ */
+ cmp : function cmp(a, b){
+ if (a instanceof Thing) a = a.midpoint;
+ if (b instanceof Thing) b = b.midpoint;
+
+ var abs = Math.abs
+ // , cur = this.owner.midpoint
+ // , t = this.iparametric(cur.x, cur.y)
+ , t = this.elapsed
+
+ , xa = this.calcX(a.y), ya = this.calcY(a.x)
+ , ta = this.iparametric(xa,ya)
+ , da = (ta.x + ta.y)/2 - t
+ // , da = (ta.x - t.x + ta.y - t.y)/2
+ // , dxa = ta.x - t.x, dya = ta.y - t.y
+
+ , xb = this.calcX(b.y), yb = this.calcY(b.x)
+ , tb = this.iparametric(xb,yb)
+ , db = (tb.x + tb.y)/2 - t
+ // , db = (tb.x - t.x + tb.y - t.y)/2
+ // , dxb = tb.x - t.x, dyb = tb.y - t.y
+ ;
+
+ // If one has passed, return the other
+ if ( da < 0 && db >= 0 )
+ return 1;
+ if ( db < 0 && da >= 0 )
+ return -1;
+
+ return Y.op.cmp(abs(da), abs(db));
+ },
+
+ closer : function closer(o1, o2){
+ return this.cmp(o1,o2) === 1 ? o2 : o1;
+ },
+
+ closest : function closest(o1, o2){
+ return new Y(arguments).sort( this.cmp ).shift();
+ },
+
+ comesWithin : function comesWithin(pt, w,h){
+ if ( !this.owner.midpoint )
+ return false;
+
+ if (pt instanceof Thing) pt = pt.midpoint;
+
+ if ( w === undefined ){
+ w = 0; h = 0;
+ } else if ( Y.isNumber(w.width) ){
+ h = w.height; w = w.width;
+ }
+
+ var cur = this.owner.midpoint
+ , fx = this.calcX(pt.y), fy = this.calcY(pt.x)
+ , t = this.iparametric(cur.x, cur.y)
+ , ft = this.iparametric(fx,fy)
+ , dw = Math.abs(fx - pt.x), dh = Math.abs(fy - pt.y)
+ ;
+ return ( t.x <= ft.x && t.y <= ft.y
+ && ( dw <= w || dh <= h ) );
+ },
+
+ pathBlocked : function pathBlocked(obj, ignore){
+ var blockers =
+ this.pathmap.walls
+ .concat( this.game.units )
+ .apply('remove', Y(ignore || []).concat([this.owner]).end())
+ .sort( this.cmp )
+
+ , blocker = blockers.shift()
+ ;
+
+ return (blocker === obj ? false : blocker);
+ },
+
+
step : function step(dt){
this.halt = false;
return to;
},
- willComeWithin : function willComeWithin(pt, w,h){
- if ( !this.owner.midpoint )
- return false;
-
- if ( !Y.isNumber(w) ){
- h = w.height; w = w.width;
- }
-
- var cur = this.owner.midpoint
- , fx = this.calcX(pt.y), fy = this.calcY(pt.x)
- , t = this.iparametric(cur.x, cur.y)
- , ft = this.iparametric(fx,fy)
- , dw = Math.abs(fx - pt.x), dh = Math.abs(fy - pt.y)
- ;
- return ( t.x <= ft.x && t.y <= ft.y
- && ( dw <= w || dh <= h )
- && !this.pathmap.wallObstructs(this, w,h) );
- },
-
toString : function toString(){
return 'T['+this.p1+', '+this.p2+', slope='+this.slope.toFixed(3)+']';
}
var to = this.trajectory.step( ELAPSED );
if (!this.dead)
- this.game.moveAgentTo(this, to.x, to.y);
+ this.game.moveUnitTo(this, to.x, to.y);
return this;
},
;
if ( !blockers.size() )
- this.game.moveAgentTo(this, x,y);
+ this.game.moveUnitTo(this, x,y);
},
willCollide : function willCollide(bullets, wiggle){
wiggle = wiggle || 0;
- var bb = this.boundingBox, mid = this.midpoint
+ var tank = this, bb = this.boundingBox
, w = (bb.width+wiggle)/2, h = (bb.height+wiggle)/2
;
return bullets.filter(function(b){
- return !b.dead && b.willComeWithin(mid, w,h);
+ var trj = b.trajectory;
+ return ( !b.dead
+ && trj.comesWithin(tank, w,h)
+ && !trj.pathBlocked(tank) );
});
},
continueMove : function continueMove(){
if ( !this.currentMove || this.currentMoveLimit <= NOW ){
var t = this.findNearEnemies(10000).shift();
- this.calculatePath(t.midpoint);
+ if (t) this.calculatePath(t.midpoint);
}
var to = this.currentMove;
, pm = this.game.pathmap ;
return this.findNearLike(ticks, function(agent){
var am = agent.midpoint;
- return agent.align !== this.align
- && Y.is(Tank, agent)
- && !(wallObs
- && pm.wallObstructs(new Trajectory(this, mid.x,mid.y, am.x,am.y), 6,6));
+ return ( agent.align !== this.align
+ && Y.is(Tank, agent)
+ && !(wallObs && new Trajectory(this,mid,am).pathBlocked(agent)) );
});
},
;
if ( !blockers.size() )
- this.game.moveAgentTo(this, to.x, to.y);
+ this.game.moveUnitTo(this, to.x, to.y);
return this;
},
return Math.atan2(y0,x0);
},
- willComeWithin : function willComeWithin(pt, w,h){
+ comesWithin : function comesWithin(pt, w,h){
if ( !(this.trajectory && this.midpoint) )
return false;
-
- if ( !Y.isNumber(w) ){
- h = w.height; w = w.width;
- }
- var trj = this.trajectory
- , cur = this.midpoint
- , fx = trj.calcX(pt.y), fy = trj.calcY(pt.x)
- , t = trj.iparametric(cur.x, cur.y)
- , ft = trj.iparametric(fx,fy)
- , dw = Math.abs(fx - pt.x), dh = Math.abs(fy - pt.y)
- ;
- return ( t.x <= ft.x && t.y <= ft.y
- && ( dw <= w || dh <= h ) );
+ return this.trajectory.comesWithin(pt, w,h);
},
+tanks.ui = {};
+
(function(){
+
jQuery(main);
this.main = main;
}
function teardownGame(){
- LBT.stop();
+ LBT.destroy();
$('#viewport').empty();
}
$(document).unbind('keydown', startGame);
$('#welcome').hide();
- countdownToStart(3, function(){
- $('#overlay').hide();
- $(document).bind('keydown', 'return', pauseGame);
- toggleGame();
- });
+
+ if ( tanks.config.ui.showCountdown )
+ countdownToStart(3, readyToStart);
+ else
+ readyToStart();
}
function pauseGame(evt){
$('#config [name=aipaths]').attr('checked', p.overlayAIPaths);
$('#config [name=trajectories]').attr('checked', p.traceTrajectories);
- $('#config [name=gridCoords]').attr('checked', c.debug.gridShowCoords);
- $('#viewport .layer.grid')[(c.debug.gridShowCoords ? 'add' : 'remove')+'Class']('gridShowCoords');
+ $('#config [name=gridCoords]').attr('checked', c.ui.showGridCoords);
+ $('#viewport .layer.grid')[(c.ui.showGridCoords ? 'add' : 'remove')+'Class']('showGridCoords');
}
function updateConfig(evt){
p.overlayAIPaths = $('#config [name=aipaths]').attr('checked');
p.traceTrajectories = $('#config [name=trajectories]').attr('checked');
- c.debug.gridShowCoords = $('#config [name=gridCoords]').attr('checked');
- $('#viewport .layer.grid')[(c.debug.gridShowCoords ? 'add' : 'remove')+'Class']('gridShowCoords');
+ c.ui.showGridCoords = $('#config [name=gridCoords]').attr('checked');
+ $('#viewport .layer.grid')[(c.ui.showGridCoords ? 'add' : 'remove')+'Class']('showGridCoords');
}
// Update performance info periodically
tickDown();
}
+function readyToStart(){
+ $('#overlay').hide();
+ $(document).bind('keydown', 'return', pauseGame);
+ toggleGame();
+}
+
+
+
-})();
+}).call(tanks.ui);
static $mainScripts = array(
- "src/tanks/main.js"
- // "src/tanks/main-ui.js"
+ "src/tanks/ui/main.js"
);
+
+
static $srcScripts = array(
"src/tanks/tanks.js",
"src/tanks/globals.js",
"src/tanks/ui/grid.js",
- "src/tanks/game/game.js"
+ "src/tanks/game.js"
);
+
+
static $libScripts = array(
"lib/jquery-1.4.3.js",
"lib/jquery.sparkline.min.js",