#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; }
-#howto { position:fixed; top:3em; right:1em; color:#BFBFBF; }
+#notes { position:fixed; top:4em; right:1em; color:#BFBFBF; }
+ #notes ul, #notes ol, #notes li { list-style:circle ! important; }
+ #notes li { margin-left:1em; }
<link rel="stylesheet" href="css/lttl.css" type="text/css" media="screen">
</head>
<body class="lttl tanks">
- <h1>The Littlest Battletank</h1>
+<h1>The Littlest Battletank</h1>
+<ul id="notes" class="box">
+ <li>Press <code>enter</code> to start! (And to pause.)<br><br></li>
+
+ <li>Move around with <code>wasd</code> or the arrow keys.</li>
+ <li>Use the mouse to aim; left click or press <code>spacebar</code> to shoot.<br><br></li>
+
+ <li>For now, refresh the page to play again. :)</li>
+</ul>
<div id="config" class="box">
<h3>config</h3>
- <div><label for="bullets">bullets</label> <input id="bullets" name="bullets" value="10" type="text"></div>
+ <!--<div><label for="bullets">bullets</label> <input id="bullets" name="bullets" value="10" type="text"></div>-->
<div><label for="pathmap">overlay pathmap</label> <input id="pathmap" name="pathmap" value="1" type="checkbox"></div>
<div><label for="trajectories">trace trajectories</label> <input id="trajectories" name="trajectories" value="1" type="checkbox"></div>
</div>
# Bugs
-- collision short-circuiting, collision events
-- bullet collisions should explode both
-- 5-shot limit
-
# TODOs
, _Number = globals.Number
, slice = _Array.prototype.slice
-, isArray = _Array.isArray
, toString = _Object.prototype.toString
, hasOwn = _Object.prototype.hasOwnProperty
, getProto = _Object.getPrototypeOf
return set(o, key, value, def);
}
-function attr(o,key,value,def){
+function attr(o, key, value, def){
if ( !o || key === undefined ) return o;
if ( Y.isPlainObject(key) )
// 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;
- }, {});
+var class2name =
+ "Boolean Number String Function Array Date RegExp Object"
+ .split(" ")
+ .reduce(function(class2name, name) {
+ class2name[ "[object "+name+"]" ] = name.toLowerCase();
+ return class2name;
+ }, {})
+;
function type_of(obj){
return obj == null ?
String( obj ) :
- class2type[ toString.call(obj) ] || "object";
+ class2name[ toString.call(obj) ] || "object";
}
+function isArray(obj) { return type_of(obj) === "array" || obj instanceof Y.YArray; }
function isFunction(obj) { return type_of(obj) === "function"; }
function isString(obj) { return type_of(obj) === "string"; }
function isNumber(obj) { return type_of(obj) === "number"; }
-function isWindow( obj ) { return obj && typeof obj === "object" && "setInterval" in obj; }
+function isWindow(obj) { return obj && typeof obj === "object" && "setInterval" in obj; }
function isPlainObject( obj ){
// Must be an Object.
return key === undefined || hasOwn.call( obj, key );
}
-function isA(a, b){
- return (a instanceof b) || (type_of(a) === b);
+function type( o ) {
+ switch ( typeof(o) ) {
+ case "undefined" : return undefined;
+ case "string" : return String;
+ case "number" : return Number; // Note: NaN and Infinity are Number literals
+ case "boolean" : return Boolean;
+
+ case "function" :
+ // If the function has a user-specified prototype, we can probably assume
+ // it's meant to be a class constructor (and therefore, a type)
+ if ( o.prototype && o.prototype !== Function.prototype )
+ return o;
+ else
+ return Function;
+
+ case "object" :
+ default :
+ // Null is an object, obv
+ if ( o === null )
+ return null;
+
+ return KNOWN_CLASSES[o.className] || o.__class__
+ || (o.constructor && o.constructor !== Object) ? o.constructor : Object;
+ }
}
+function is( A, B ){
+ if ( isArray(B) )
+ return B.map( Y.is(A) ).any();
+ else {
+ var AT = type(A), BT = type(B);
+ return (A instanceof BT || B instanceof AT || AT === BT);
+ }
+}
Y.attr = dattr;
Y.extend = extend;
-Y.isA = isA;
+Y.type = type;
+Y.is = is;
Y.isString = isString;
Y.isNumber = isNumber;
Y.isFunction = isFunction;
// Cast `arguments` object to a real Array, optionally slicing at specified delimiters
if ( o.prototype === undefined
&& isNumber(o.length)
- && !isArray(o)
+ && !_Array.isArray(o)
&& o.constructor === _Object )
{
r = slice.call( o, A[1]||0, A[2]||o.length );
Y(Y.set);
Y(Y.attr);
Y(Y.extend);
-Y.isA = Y(Y.isA).curry();
+Y(Y.type);
+Y.is = Y(Y.is).curry();
+
Y.reduce(YFunction.prototype, function(_,fn,name){
YFunction(fn);
});
has: function(o,k){ return k in o; },
get: function(o,k){ return o[k] },
getdef: function(o,k,def){ return (k in o ? o[k] : def); },
- set: set,
- attr: attr,
+ set: set, // set(o, key, value, def)
+ attr: attr, // attr(o, key, value, def)
method: function(name){
var args = Y(arguments,1);
return function(obj){
// Curry all operators
Y.op = Y.reduce(Y.op, function(op, fn, k){
- op[k] = Y(fn).curry();
+ op[k] = Y( Y(fn).curry() );
return op;
}, {});
SECONDTH = ELAPSED / 1000;
SQUARETH = REF_SIZE * SECONDTH
+ this.active.invoke('updateCooldowns', NOW);
this.active.invoke('act');
this.draw();
LBT = new tanks.Game();
ctx = LBT.level.ctx;
- T = LBT.addUnit(new Tank(1), 1,2);
- new Player(LBT, T);
+ P = LBT.addUnit(new PlayerTank(1), 1,2);
+ E = LBT.addUnit(new Tank(2), 5,6);
setupUI();
- // barrel = T.barrel;
- // B = bullets.attr(0);
- // R = B.trajectory;
+ // toggleGame();
+ updateInfo();
}
function setupUI(){
LBT.loop.spark = new FpsSparkline(LBT.loop, '.fps-sparkline', 0,0);
- btank = new Tank(1);
- btank.act = function(){ return this; };
- btank.stats.shots = Infinity;
- LBT.addUnit(btank, 0,0);
- LBT.pathmap.removeBlocker(btank);
- btank.shape.hide();
+ // btank = new Tank(1);
+ // btank.act = function(){ return this; };
+ // btank.stats.shots = Infinity;
+ // LBT.addUnit(btank, 0,0);
+ // LBT.pathmap.removeBlocker(btank);
+ // btank.shape.hide();
initConfig();
+ $('#config').bind('mousedown', Y.op.K(false));
$('#config input').bind('change', updateConfig);
LBT.root.draw();
$(document).bind('keydown', 'return', toggleGame);
$(document).bind('keydown', 'ctrl+o', toggleOverlay);
- $('#bullets').bind('blur', function(evt){
- var n = parseInt($('#bullets').val() || 0);
- updateBullets(n);
- });
+ // $('#bullets').bind('blur', function(evt){
+ // var n = parseInt($('#bullets').val() || 0);
+ // updateBullets(n);
+ // });
LBT.root.draw();
setInterval(updateInfo, 1000);
- // Start the simulation!
- toggleGame();
- updateInfo();
-
// Fix grid-size on resize
// $(window).bind('resize', resizeGame);
}
$('#config [name=pathmap]').attr('checked', p.overlayPathmap);
$('#config [name=trajectories]').attr('checked', p.traceTrajectories);
- $('#config [name=bullets]').val(c.debug.projectiles);
- updateBullets( c.debug.projectiles );
+ // $('#config [name=bullets]').val(c.debug.projectiles);
+ // updateBullets( c.debug.projectiles );
}
function updateConfig(evt){
, n_projs = LBT.bullets.size()
;
+ $('#info #state').text( loop.running ? 'Running!' : ('Paused (tick '+TICKS+')') );
$('#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=active]').val( n_active );
$('#info [name=units]').val( n_units );
resetBound : function resetBound(){
var BOUND_SIZE_RATIO = 0.75
, abs = Math.abs
- , bb = this.owner.boundingBox;
- this.tBound = Math.min( abs(bb.width / this.pa) * BOUND_SIZE_RATIO,
- abs(bb.height / this.pb) * BOUND_SIZE_RATIO );
+ , w = this.owner.width, h = this.owner.height;
+ this.tBound = Math.min( abs(w / this.pa) * BOUND_SIZE_RATIO,
+ abs(h / this.pb) * BOUND_SIZE_RATIO );
return this;
},
},
createCooldowns : Y.op.nop,
+ updateCooldowns : Y.op.nop,
setTarget : function setTarget(x,y){
var loc = this.loc
var loc = this.loc;
this.shape = new Circle(3)
.position(loc.x, loc.y)
- .fill('#EC5B38')
+ .fill('#FFF6AE')
.appendTo( parent );
this.shape.layer.attr('title', ''+loc);
-Key = {
- fire : 32,
-
- _dirFromKey : {
- 37: "left", 38: "up", 39: "right", 40: "down",
- 65: "left", 87: "up", 68: "right", 83: "down"
- },
-
- _inverse : {
- left : "right", right : "left",
- up : "down", down : "up"
- },
-
- getDir : function getDir(key){
- return Key._dirFromKey[key+''];
- },
-
- getInverseDir : function getInverseDir(key){
- return Key._inverse[ Key._dirFromKey[key+''] ];
- }
-};
-
+(function(){
-
-Player = new Y.Class('Player', {
- activeKeys : null,
- shift : false, ctrl : false, meta : false, alt : false,
- leftMouse : false, middleMouse : false, rightMouse : false,
+PlayerTank = Tank.subclass('PlayerTank', {
+ bodyColor : '#E73075',
+ turretColor : '#A72F5B',
+ barrelColor : '#2E62C9',
- queue : null,
+ // Attributes
+ stats: {
+ hp : 1, // health
+
+ move : 1.0, // move speed (squares/sec)
+ rotate : HALF_PI, // rotation speed (radians/sec)
+
+ power : 1, // attack power
+ speed : 0.5, // attack cool (sec)