.rounded { border-radius:1em; -moz-border-radius:1em; -webkit-border-radius:1em; }
#viewport { position:relative; top:1em; width:500px; height:500px; margin:0 auto;
- outline:1px solid #ccc; }
+ /* outline:1px solid #ccc; */ }
#howto { position:fixed; top:3em; right:1em; color:#BFBFBF; }
#info { position:fixed; bottom:10px; right:10px; padding:0.5em; background-color:rgba(0,0,0, 0.1); color:#787878; }
#info label { display:block; float:left; width:3em; margin-right:0.5em; color:#787878; }
#info input { border:0; background-color:transparent; min-width:5em; width:5em; color:#5c5c5c; }
-
-#log { position:fixed; top:auto; bottom:0; left:0; width:100%; height:30%; border-top:1px solid #bbb; }
+ #info .sep { opacity:0.1; background-color:#999; margin:5px 0; height:1px; }
<li id="state"></li>
<li><label for="fps">fps</label> <input id="fps" name="fps" value="" type="text"></li>
<li><label for="frame">frame</label> <input id="frame" name="frame" value="" type="text"></li>
- <li><label for="agents">agents</label> <input id="agents" name="agents" value="" type="text"></li>
+ <li><div class="sep"></div></li>
+ <li><label for="objects">objects</label> <input id="objects" name="objects" value="" type="text"></li>
<li><label for="units">units</label> <input id="units" name="units" value="" type="text"></li>
<li><label for="bullets">bullets</label> <input id="bullets" name="bullets" value="" type="text"></li>
</ul>
-<div id="log" style="display:none"></div>
-
<div id="scripts">
<!--[if IE]><script type="text/javascript" src="lib/excanvas.min.js"></script><![endif]-->
<?php
"src/portal/layer.js",
"src/portal/shape.js",
+ "src/portal/util/quadtree.js",
+ "src/portal/util/rbtree.js",
+ "src/portal/util/eventloop.js",
+ "src/portal/util/cooldown.js",
+ "src/portal/util/loc.js",
+
+ "src/tanks/util/pathmap.js",
+ "src/tanks/util/grid.js",
+
+ "src/tanks/game/game.js",
+ "src/tanks/game/map.js",
+
+ "src/tanks/unit/tank.js",
- // "src/portal/util/quadtree.js",
- // "src/portal/util/rbtree.js",
- // "src/portal/util/eventloop.js",
- // "src/portal/util/cooldown.js",
- // "src/portal/util/loc.js",
+ "src/tanks/game/player.js",
- "src/tanks/map.js",
- "src/tanks/tank.js",
- "src/tanks/game.js",
- "src/tanks/ui.js",
+ "src/tanks/lttl.js",
+ "src/tanks/ui.js"
- "src/tanks/lttl.js"
);
function js($src) {
if ( !o )
return acc;
+ fn = Function.toFunction(fn);
if ( notSelfOrWrapped(o.reduce) )
return o.reduce.apply(o, slice.call(arguments,1));
if (fn.__curried__)
return fn.apply(this, Y(arguments,1));
+ fn = Function.toFunction(fn);
var args = Y(arguments, 1)
, L = unwrap(fn).length;
YFunction.prototype.compose = methodize(compose);
function _composer(x,fn){ return fn.call(this, x); }
function compose(f,g){
- var fns = Y(arguments);
+ var fns = Y(arguments).map(Function.toFunction);
return function(){
return fns.reduce(_composer, Y(arguments), this);
};
Y.chain = chain;
YFunction.prototype.chain = methodize(chain);
function chain(f,g){
- var fns = Y(arguments);
+ var fns = Y(arguments).map(Function.toFunction);
if ( g.__sequence__ )
fns = g.__sequence__.concat( fns.slice(1) );
}
$y_files = array(
+ 'to-function',
'alias',
'type',
'core',
+++ /dev/null
-(function(){
-
-var undefined,
-globals = this,
-_toString = _Object.prototype.toString,
-_hasOwn = _Object.prototype.hasOwnProperty,
-_isArray = _Array.isArray;
+++ /dev/null
-
-globals.reduce = reduce;
-globals.extend = extend;
-globals.attr = attr;
-
-})();
+++ /dev/null
-// Generic Collection Functions
-
-function notSelfOrWrapped(fn){
- var self = arguments.callee.caller;
- return fn && fn !== self && fn.__wraps !== self;
-}
-
-function reduce(o, fn, acc, cxt){
- if ( !o )
- return acc;
-
- if ( notSelfOrWrapped(o.reduce) )
- return o.reduce.apply(o, slice.call(arguments,1));
-
- cxt = cxt || o;
- for ( var name in o )
- acc = fn.call(cxt, acc, o[name], name, o);
-
- return acc;
-}
-
-function attr(o, key, value, def){
- if ( o && notSelfOrWrapped(o.attr) )
- return o.attr.apply(o, slice.call(arguments,1));
-
- if ( value !== undefined || def !== undefined ){
- o[key] = (value !== undefined ? value : def);
- return o;
- } else
- return o[key];
-}
-
-function extend( A, B ){
- return slice.call(arguments,1).reduce(function(A, donor){
- return reduce(donor, function(o, v, k){
- return attr(o, k, v, o[k]);
- }, A);
- }, A);
-}
+++ /dev/null
-var WR_P = "__wraps__";
-
-
-function unwrap(fn){
- return ( fn && isFunction(fn) ) ? unwrap(fn[WR_P]) || fn : fn;
-}
-
-Function.prototype.curry = curry;
-function curry(){
- var fn = this
- , args = Array.slice(arguments,0)
- , L = unwrap(fn).length;
-
- function curried(){
- var _args = args.concat(Array.slice(arguments,0));
- if ( _args.length >= L )
- return fn.apply(this, _args);
- else
- return curry.apply(fn, _args);
- }
- curried[WR_P] = fn;
-
- return curried;
-}
-
-Function.prototype.methodize = methodize;
-function methodize() {
- var fn = this;
- if ( fn.__methodized__ )
- return fn.__methodized__;
-
- var m = fn.__methodized__ =
- function(){
- return fn.apply(this, [this].concat(Array.slice(arguments, 0)));
- };
- m[WR_P] = fn;
- return m;
-}
-
-/** Returns the declared name of a function. */
-Function.prototype.getName = getName;
-function getName(){
- var fn = this;
- return fn.className || fn.name || (fn+'').match( /function\s*([^\(]*)\(/ )[1] || '';
-}
-
+++ /dev/null
-<?php
-function dump_file($path, $add_newline=true){
- $size = filesize($path);
- if ($size > 0) {
- $f = fopen($path, "r");
- echo fread($f, $size);
- fclose($f);
- }
- if ($add_newline) echo "\n";
-}
-
-$jsjs_files = array(
- 'type',
- 'core',
- 'function',
- 'object',
- 'array',
- 'string',
- 'number'
-);
-
-function jsjs_list($path='') {
- global $jsjs_files;
- $path = $path ? $path : dirname($_SERVER["REQUEST_URI"]);
- // echo $path;
- foreach ($jsjs_files as $f) {
- echo "<script src='$path/$f.js' type='text/javascript'></script>\n";
- }
-}
-
-function jsjs_dump($expose=false) {
- global $jsjs_files;
- if (!$expose)
- dump_file("./_intro.js");
- foreach ($jsjs_files as $f)
- dump_file("./$f.js");
- if (!$expose)
- dump_file("./_outro.js");
-}
-
-if ( basename($_SERVER["SCRIPT_FILENAME"]) == basename(__FILE__) ) {
- if ( $_REQUEST["list"] )
- jsjs_list();
- else
- jsjs_dump();
-}
\ No newline at end of file
+++ /dev/null
-// Type Utilities //
-// Much borrowed from jQuery
-
-var class2type = "Boolean Number String Function Array Date RegExp Object"
- .split(" ")
- .reduce(function(class2type, name) {
- class2type[ "[object "+name+"]" ] = name.toLowerCase();
- return class2type;
- }, {});
-
-function type_of(obj){
- return obj == null ?
- String( obj ) :
- class2type[ toString.call(obj) ] || "object";
-}
-
-function isFunction(obj) { return type_of(obj) === "function"; }
-function isString(obj) { return type_of(obj) === "string"; }
-function isNumber(obj) { return type_of(obj) === "number"; }
-
-// A crude way of determining if an object is a window
-function isWindow( obj ) {
- return obj && typeof obj === "object" && "setInterval" in obj;
-}
-
-function isPlainObject( obj ){
- // Must be an Object.
- // Because of IE, we also have to check the presence of the constructor property.
- // Make sure that DOM nodes and window objects don't pass through, as well
- if ( !obj || type_of(obj) !== "object" || obj.nodeType || isWindow(obj) )
- return false;
-
- // Not own constructor property must be Object
- if ( obj.constructor &&
- !hasOwn.call(obj, "constructor") &&
- !hasOwn.call(obj.constructor.prototype, "isPrototypeOf") )
- return false;
-
-
- // Own properties are enumerated firstly, so to speed up,
- // if last one is own, then all properties are own.
-
- var key;
- for ( key in obj ) {}
-
- return key === undefined || hasOwn.call( obj, key );
-}
-
-
$(function(){
$('<style />')
.text([
- '.portal.layer { position:absolute; z-index:1; overflow:hidden; }',
+ '.portal.layer { position:absolute; z-index:1; overflow:hidden; top:0; left:0; }',
'.portal.layer canvas { z-index:0; }'
].join('\n'))
.appendTo('head');
return this;
var _ctx = ctx || this.ctx;
- this._openPath(_ctx)
- .drawShape(_ctx)
- ._closePath(_ctx);
+ this._openPath(_ctx);
+ this.drawShape(_ctx);
+ this._closePath(_ctx);
this.dirty = false;
this.children.invoke('draw', ctx);
drawShape : function(ctx){
ctx.rect(0,0, this._width,this._height);
ctx.fill();
- return this;
}
});
Polygon = new Y.Class('Polygon', Shape, {
+ _cssClasses : 'portal layer shape polygon',
/**
* Expects two arrays of coordinate-halfs, which could be zipped
ctx.lineTo.apply(ctx, pair);
});
ctx.fill();
- return this;
}
});
Triangle = new Y.Class('Triangle', Polygon, {
+ _cssClasses : 'portal layer shape polygon triangle',
+
init : function(x1,y1, x2,y2){
Polygon.init.call(this, [x1,x2], [y1,y2]);
}
});
Quad = new Y.Class('Quad', Polygon, {
+ _cssClasses : 'portal layer shape polygon quad',
+
init : function(x1,y1, x2,y2, x3,y3){
Polygon.init.call(this, [x1,x2,x3], [y1,y2,y3]);
}
});
+Circle = new Y.Class('Circle', Shape, {
+ _cssClasses : 'portal layer shape circle',
+
+ init : function(radius){
+ Layer.init.call(this);
+
+ var d = radius * 2;
+ this.width(d).height(d);
+ this.radius = radius;
+ },
+
+ drawShape : function(ctx){
+ var r = this.radius;
+ ctx.arc(r,r, r, 0, Math.PI*2, false);
+ ctx.fill();
+ }
+
+});
\ No newline at end of file
this.y = Math.max(y,0);
},
+ moveBy : function(dir, amount){
+ var mod = 1;
+ switch (dir) {
+ case Loc.LEFT: mod = -1;
+ case Loc.RIGHT:
+ return new Loc(this.x+amount*mod, this.y);
+
+ case Loc.UP: mod = -1;
+ case Loc.DOWN:
+ return new Loc(this.x, this.y+amount*mod);
+ }
+ },
+
clone : function(){
return new Loc(this.x, this.y);
},
toSquare : function(){
- return Square.fromLoc(this.x, this.y);
+ return Loc.Square.fromLoc(this.x, this.y);
},
toString : function(){
});
Y(Loc).extend({
+ UP : 'up', DOWN : 'down',
+ RIGHT : 'right', LEFT : 'left',
fromSquare : function(col, row){
return new Loc(
});
-Rect = new Y.Class('Rect', {
+Loc.Rect = new Y.Class('Rect', {
init : function(x1,y1, x2,y2){
if (x1 instanceof Loc && y1 instanceof Loc) {
var top = x1,
},
clone : function(){
- return new Rect(
+ return new Loc.Rect(
this.top.clone(),
this.bottom.clone() );
},
});
-Square = new Y.Class('Square', Rect, {
+Loc.Square = new Y.Class('Square', Rect, {
init : function(col, row){
col = this.col = Math.max(col,0);
row = this.row = Math.max(row,0);
, x2 = x1 + REF_SIZE
, y2 = y1 + REF_SIZE;
- Rect.init.call(this, x1,y1, x2,y2);
+ Loc.Rect.init.call(this, x1,y1, x2,y2);
},
clone : function(){
}
});
-Square.fromLoc = function(x, y){
- return new Square(
+Loc.Square.fromLoc = function(x, y){
+ return new Loc.Square(
Math.floor(x / REF_SIZE),
Math.floor(y / REF_SIZE) );
};
}
});
-this['QuadTree'] = QuadTree;
QuadTree['Region'] = Region;
+this['QuadTree'] = QuadTree;
})();
--- /dev/null
+var undefined
+, GRID_ELEMENT = '#viewport'
+
+, REF_SIZE = 50
+, CELL_SIZE = REF_SIZE
+, SCALE = CELL_SIZE / REF_SIZE
+, GRID_OFFSET = 0
+
+, COLUMNS = 10
+, ROWS = 10
+, CAPACITY = 32
+
+, FRAME_RATE = 30
+, MS_PER_FRAME = 1000 / FRAME_RATE
+
+, NOW = new Date().getTime() // Current tick's timestamp (ms)
+, ELAPSED = MS_PER_FRAME // Time (ms) since previous tick
+, FRAMETH = ELAPSED / 1000 // Ratio of second completed
+, TICKS = 0 // Ticks since start of game
+
+, PI = Math.PI
+, TWO_PI = PI*2
+, HALF_PI = PI/2
+
+;
+
+
+
+Game = new Y.Class('Game', {
+
+ init : function(viewport){
+ this.loop = new EventLoop(this, FRAME_RATE);
+
+ // Seal all methods
+ // Y.bindAll(this);
+ this.resize = this.resize.bind(this);
+ this.tick = this.tick.bind(this);
+
+ this.viewport = $(viewport || GRID_ELEMENT);
+
+ this.initMap();
+
+ this.addEventListener('tick', this.tick);
+ },
+ showOverlay : false,
+
+ /**
+ * Main Event Loop.
+ */
+ tick : function(evt){
+ var d = evt.data;
+
+ NOW = d.now;
+ ELAPSED = d.elapsed;
+ TICKS = d.ticks;
+
+ this.root.draw();
+
+ this.units.invoke('act');
+ // XXX: Collect the dead
+
+ // this.grid.removeOverlay(this.el);
+ // if (this.showOverlay) this.grid.overlay(this.el);
+ }
+
+});
+
+
--- /dev/null
+
+Y(Game.prototype).extend({
+
+ initMap : function(){
+ var self = this;
+
+ this.pathmap = new PathMap(0,0, COLUMNS*REF_SIZE, ROWS*REF_SIZE, CAPACITY);
+
+ var root = this.root = this.grid =
+ new Grid(COLUMNS,ROWS, CELL_SIZE)
+ .appendTo(this.viewport);
+ this.level =
+ new Layer()
+ .width( root._width )
+ .height( root._height)
+ .appendTo(this.root);
+
+ // this.root.ctx.scale(1.0, 1.0);
+
+ this.byId = {};
+ this.units = new Y.YArray();
+ this.bullets = new Y.YArray();
+ this.blockers = new Y.YArray();
+
+ // Agent.addEventListener('create', function(evt){
+ // self.addAgent(evt.instance);
+ // });
+ // Agent.addEventListener('destroy', function(evt){
+ // self.killAgent(evt.instance);
+ // });
+ },
+
+
+ // *** Path Map Management *** //
+
+ addBlocker : function(agent){
+ var bb = agent.boundingBox;
+ if (agent.blocking && bb)
+ agent.region = this.grid.set(bb.x1,bb.y1, bb.x2,bb.y2, agent);
+ return agent;
+ },
+
+ removeBlocker : function(agent){
+ if (agent.region)
+ this.grid.remove(agent.region);
+ return agent;
+ },
+
+ updateBlocker : function(agent){
+ this.removeBlocker(agent);
+ this.addBlocker(agent);
+ },
+
+
+ // *** Agent Management *** //
+
+ addUnit : function(unit, col,row){
+ unit.game = this;
+
+ // Center unit in square
+ var sqX = (col || 0) * REF_SIZE
+ , sqY = (row || 0) * REF_SIZE
+ , x = sqX + (REF_SIZE-unit.width)/2
+ , y = sqY + (REF_SIZE-unit.height)/2 ;
+
+ unit.setLocation(x,y);
+ unit.render( this.level );
+
+ this.addBlocker(unit);
+
+ if ( !this.byId[unit.id] ) {
+ this.byId[unit.id] = unit;
+ this.units.push(unit);
+ }
+
+ return unit;
+ },
+
+ addAgent : function(agent){
+ agent.game = this;
+ if (agent.id === undefined) return agent;
+
+ this.addBlocker(agent);
+
+ if ( !this.byId[agent.id] ) {
+ this.byId[agent.id] = agent;
+ if (agent instanceof Ability)
+ this.abilities.push(agent);
+ else
+ this.units.push(agent);
+ }
+
+ return agent;
+ },
+
+ killAgent : function(agent){
+ delete this.byId[agent.id];
+ if (agent instanceof Ability)
+ this.abilities.remove(agent);
+ else
+ this.units.remove(agent);
+
+ this.removeBlocker(agent);
+ return agent;
+ },
+
+ moveAgentTo : function(agent, x,y){
+ this.removeBlocker(agent);
+ agent.setLocation(x,y);
+ this.addBlocker(agent);
+ return agent;
+ },
+
+ getUnitAt : function(x,y){
+ return this.grid.get(x,y);
+ },
+
+ getUnitsAt : function(x1,y1, x2,y2){
+ return this.grid.get(x1,y1, x2,y2);
+ }
+
+});
+
+Y(Game.prototype).extend({
+
+ // Ehh.
+ resize : function(){
+ var ratio = COLUMNS / ROWS
+ , el = this.el
+ , p = el.parent()
+ , pw = p.width(), ph = p.height()
+ , pRatio = pw / ph
+ ;
+
+ if ( ratio > pRatio )
+ CELL_SIZE = Math.floor((pw-GRID_OFFSET*2) / COLUMNS);
+ else
+ CELL_SIZE = Math.floor((ph-GRID_OFFSET*2) / ROWS);
+
+ SCALE = CELL_SIZE/REF_SIZE;
+
+ var w = COLUMNS*CELL_SIZE
+ , h = ROWS*CELL_SIZE
+ , canvas = this.canvas[0];
+
+ this.el.width(w).height(h);
+ this.canvas.width(w).height(h);
+ canvas.width = w;
+ canvas.height = h;
+
+ this.el.offset({
+ top : (ph - h) / 2,
+ left : (pw - w) / 2
+ });
+
+ this.ctx.scale(SCALE,SCALE);
+ }
+
+});
+
+
--- /dev/null
+Action = {
+ MOVE : 'move',
+ ATTACK : 'attack',
+
+ moveDirFromKey : {
+ 37: "left", 38: "up", 39: "right", 40: "down",
+ 65: "left", 87: "up", 83: "right", 68: "down"
+ }
+};
+
+Player = new Y.Class('Player', {
+ activeKeys : null,
+ shift : false, ctrl : false, meta : false, alt : false,
+ leftMouse : false, middleMouse : false, rightMouse : false,
+
+ action : null,
+ target : null,
+
+
+ init : function(game, tank){
+ Y.bindAll(this);
+
+ this.activeKeys = new Y.YArray();
+
+ this.game = game;
+ this.tank = tank;
+ tank.act = this.act;
+
+ $(window)
+ .bind('keydown', this.keydown)
+ .bind('keyup', this.keyup)
+ .bind('mousedown', this.mousedown)
+ .bind('mouseup', this.mouseup);
+ },
+
+ queueMove : function(dir){
+ var self = this;
+ return function(evt){
+ self.action = Actions.MOVE;
+ self.target = dir;
+ return false;
+ };
+ },
+
+ updateMeta : function(evt){
+ this.shift = evt.shiftKey;
+ this.alt = evt.altKey;
+ this.meta = evt.metaKey;
+ this.ctrl = evt.ctrlKey;
+ },
+
+ keydown : function(evt){
+ this.activeKeys.push(evt.which+'');
+ this.updateMeta(evt);
+ },
+
+ keyup : function(evt){
+ this.activeKeys.remove(evt.which+'');
+ this.updateMeta(evt);
+ },
+
+ mousedown : function(evt){
+ switch (evt.which) {
+ case 1: evt.leftMouse = true; break;
+ case 2: evt.rightMouse = true; break;
+ case 3: evt.middleMouse = true; break;
+ }
+ this.updateMeta(evt);
+ },
+
+ mouseup : function(evt){
+ switch (evt.which) {
+ case 1: evt.leftMouse = false; break;
+ case 2: evt.rightMouse = false; break;
+ case 3: evt.middleMouse = false; break;
+ }
+ this.updateMeta(evt);
+ },
+
+ act : function(){
+ if (this.tank.dead) return this;
+
+ var active = this.activeKeys;
+ for (var key in Action.moveDirFromKey)
+ if ( active.has(key) ) {
+ this.move( Action.moveDirFromKey[key] );
+ return this;
+ }
+
+ return this;
+ },
+
+ move : function(dir){
+ var tank = this.tank
+ , toLoc = tank.loc.moveBy(dir, (tank.stats.move * REF_SIZE * FRAMETH));
+ this.game.moveAgentTo(tank, toLoc.x, toLoc.y);
+ }
+
+});
\ No newline at end of file
jQuery(function($){
v = $('#viewport');
+LBT = new Game();
-L = new Layer(v)
- .width(v.width())
- .height(v.height())
- .append(
- new Rect(50,50)
- .position(50,50)
- .attr({
- fillStyle : '#E73075'
- })
- ).append(
- R = new Rect(250,250)
- .position(150,50)
- .attr({
- fillStyle : '#E2EEF5'
- })
- ).append(
- T = new Triangle(25,-25, 50,0)
- .position(150,300)
- .css('z-index', 2)
- .attr({
- fillStyle : '#E73075'
- })
- ).appendTo(v)
- .draw()
-;
+T = new Tank(0);
+LBT.addUnit(T, 1,1);
+P = new Player(LBT, T);
+
+
+// L = new Layer(v)
+// .width(v.width())
+// .height(v.height())
+// .append(
+// new Rect(250,250)
+// .position(150,50)
+// .attr({
+// fillStyle : '#E2EEF5'
+// }),
+// new Triangle(25,-25, 50,0)
+// .position(150,300)
+// .attr('fillStyle', '#E73075'),
+// new Quad(10,50, 50,50, 60,0)
+// .position(50,50)
+// .attr('fillStyle', '#E73075'),
+// new Circle(30)
+// .position(30,330)
+// .attr('fillStyle', '#8DCDA1')
+// ).appendTo(v)
+// .draw()
+// ;
});
\ No newline at end of file
+function toggleGame(evt){
+ if (LBT.loop.running)
+ LBT.stop();
+ else
+ LBT.start();
+
+ updateInfo();
+ return false;
+}
+
+// Update performance info periodically
+function updateInfo(){
+ var loop = LBT.loop
+ , fps = loop.fps()
+ , n_units = LBT.units.size()
+ , n_projs = LBT.bullets.size()
+ ;
+
+ $('#info [name=fps]').val( fps.toFixed(2) + " / " + loop.framerate );
+ $('#info [name=frame]').val( loop.frametime().toFixed(3)+" ms" );
+ $('#info #state').text( loop.running ? 'Running!' : ('Paused (tick '+TICKS+')') );
+
+ $('#info [name=objects]').val( n_units+n_projs );
+ $('#info [name=units]').val( n_units );
+ $('#info [name=bullets]').val( n_projs );
+
+ return false;
+}
+
+function fixStartText(){
+ var txt = (LBT.loop.running ? 'pause' : 'start');
+ $('.start_btn').text(txt).attr('title', txt);
+ return false;
+}
+function fixOverlayText(){
+ var txt = (LBT.showOverlay ? 'hide' : 'show') + ' bounds';
+ $('.overlay_btn').text(txt).attr('title', txt);
+ return false;
+}
+
+
+
+jQuery(function($){
+ // Tick once to draw grid, initial units
+ LBT.start();
+ LBT.stop();
+
+ // Add Buttons
+ // logger.menu.prepend('<a class="start_btn" />', ' | ', '<a class="overlay_btn"></a>', ' || ');
+
+ setInterval(updateInfo, 1000);
+ updateInfo();
+
+ // Update Text on Click
+ LBT.addEventListener('start', fixStartText);
+ LBT.addEventListener('stop', fixStartText);
+
+ // Start button (click or return key)
+ $('.start_btn').bind('click', toggleGame);
+ $(document).bind('keydown', 'return', toggleGame);
+
+ $(document).bind('keydown', 'ctrl+o', function(evt){ LBT.showOverlay = !(LBT.showOverlay); });
+
+ // Show Overlays
+ $('.overlay_btn').bind('click', function(evt){
+ LBT.showOverlay = !(LBT.showOverlay);
+ fixOverlayText();
+ return false;
+ });
+
+ // Fix Starting text
+ fixStartText();
+ fixOverlayText();
+
+ // Fix grid-size on resize
+ $(window).bind('resize', function(evt){
+ // LBT.resize(evt);
+
+ if (!LBT.loop.running) {
+ LBT.start();
+ LBT.stop();
+ }
+ });
+
+ //
+});
\ No newline at end of file
--- /dev/null
+(function(){
+var uid = 0;
+
+
+Tank = new Y.Class('Tank', {
+
+ init : function(align){
+ this.id = uid++;
+ this.align = align || 0;
+
+ this.bullets = new Y.YArray();
+
+ this.fillStats();
+ this.createCooldowns();
+ },
+
+ // Attributes
+ stats: {
+ move : 0.5, // move speed (squares/sec)
+ rotate : HALF_PI, // rotation speed (radians/sec)
+
+ power : 1, // attack power
+ speed : 1.0, // attacks/sec
+ shots : 5 // max projectiles in the air at once
+ },
+
+ // *** Bookkeeping *** //
+
+ id : 0,
+ bullets : null,
+ align : 0,
+ dead : false,
+
+ destroy : function(){
+ if (this.dead) return;
+
+ this.dead = true;
+ this.fire('destroy', this);
+ },
+
+
+ loc : null,
+ boundingBox : null,
+ vec : 0,
+
+ // Bounding box size
+ width : REF_SIZE*0.7,
+ height : REF_SIZE*0.6,
+
+
+ setLocation : function(x,y){
+ var loc = this.loc;
+ if (loc && loc.x === x && loc.y === y)
+ return loc;
+ loc = this.loc = new Loc(x,y);
+ if (this.shape) this.shape.position(x,y);
+ this.boundingBox = new Loc.Rect(x,y, x+this.width,y+this.height);
+ return loc;
+ },
+
+ // *** Gameplay Methods *** //
+
+ fillStats : function(){
+ this.stats = Tank.fillStats(this.stats);
+ },
+
+ createCooldowns : function(){
+ this.cooldowns = Y({
+ attack: new Cooldown(1000 * this.stats.speed)
+ });
+ },
+
+ /**
+ * Determines what the creep should do -- move, attack, etc.
+ */
+ act : function(){
+ if (this.dead) return this;
+
+ this.cooldowns.invoke('update', NOW);
+
+ var bb = this.boundingBox
+ , x1 = Calc.rangeX(this), x2 = bb.left.x
+ , y1 = bb.top.y, y2 = bb.bottom.y
+ , units = this.game
+ .getUnitsAt(x1,y1, x2,y2)
+ .filter(this.filterTarget, this)
+ , target = this.closest(units)
+ ;
+ if (target)
+ this.attack(target);
+ else
+ this.move();
+
+ if (bb.x1 <= MIN_X_DIST || bb.x2 >= MAX_X_DIST)
+ this.destroy();
+
+ return this;
+ },
+
+ filterTarget : function(unit){ return unit.align !== this.align; },
+
+ move : function(dir){
+ var toLoc = this.loc.moveBy(dir, (this.stats.move * REF_SIZE * FRAMETH));
+ this.game.moveAgentTo(this, toLoc.x, toLoc.y);
+ return this;
+ },
+
+ attack : function(unit){
+ var atk_cool = this.cooldowns.attr('attack');
+ if ( atk_cool.activate(NOW) )
+ this.doAttack(unit);
+ return this;
+ },
+
+ doAttack : function(target){
+ this.fireProjectile(target);
+ },
+
+ fireProjectile : function(target){
+ var AbilityType = this.projectile
+ , p = new AbilityType(this, target);
+ this.bullets.push(p);
+ this.game.addAgent(p);
+ return p;
+ },
+
+
+
+ /// Rendering Methods ///
+
+ /**
+ * Sets up unit appearance for minimal updates. Called once at start,
+ * or when the world needs to be redrawn from scratch.
+ */
+ render : function( parent ){
+ if (this.shape) this.shape.remove();
+
+ var w = this.width
+ , h = this.height
+ , tw = w / 4
+ , th = h * 0.9
+ , sw = w * 0.05
+ ;
+
+ this.shape =
+ new Rect(w,h)
+ .position(this.loc.x, this.loc.y)
+ .attr('fillStyle', '#E73075')
+ .append(
+ new Triangle(0,th, tw,th/2)
+ .position(w-tw-sw, (h-th)/2)
+ .attr('fillStyle', '#980011')
+ )
+ .appendTo( parent );
+
+ return this;
+ },
+
+ draw : function(){
+ if (this.dead)
+ this.shape.hide();
+ else
+ this.shape.draw();
+ },
+
+ toString : function(){
+ return this.className+'(id='+this.id+', loc='+this.loc+')';
+ }
+});
+
+
+
+Y(Tank).extend({
+
+ fillStats : function(stats){
+ var st = Y(stats)
+ , stats = st.clone().end()
+ ;
+
+ st.forEach(function(v, k){
+ var k = Y(k)
+ , k_ = k.rtrim('_max')
+ ;
+
+ if ( k.endsWith('_max') ) {
+ if ( stats[k_] === undefined )
+ stats[k_] = v;
+
+ } else if ( stats[k+'_max'] === undefined )
+ stats[k+'_max'] = v;
+
+ });
+
+ return stats;
+ }
+});
+
+})();
--- /dev/null
+Grid = new Y.Class('Grid', Rect, {
+ _cssClasses : 'portal layer shape rect grid',
+
+ init : function(cols,rows, size){
+ this.cols = cols;
+ this.rows = rows;
+ this.size = size;
+ Rect.init.call(this, cols*size, rows*size);
+ },
+
+ drawShape : function(ctx){
+ var size = this.size
+ , rows = this.rows
+ , cols = this.cols
+ , w = this._width
+ , h = this._height;
+
+ ctx.lineWidth = 0.5;
+ ctx.strokeStyle = '#6E6E6E';
+
+ for (var row=0, y=0; row<=rows; y = (++row) * size){
+ ctx.moveTo(0,y);
+ ctx.lineTo(w,y);
+ }
+
+ for (var col=0, x=0; col<=cols; x = (++col) * size){
+ ctx.moveTo(x,0);
+ ctx.lineTo(x,h);
+ }
+
+ ctx.stroke();
+ }
+
+});
\ No newline at end of file
-Grid = new Y.Class('Grid', QuadTree, {
+PathMap = new Y.Class('PathMap', QuadTree, {
overlay : function(gridEl){
var w = this.width *SCALE