Adds tank AI!
authordsc <david.schoonover@gmail.com>
Thu, 18 Nov 2010 23:49:37 +0000 (15:49 -0800)
committerdsc <david.schoonover@gmail.com>
Thu, 18 Nov 2010 23:49:37 +0000 (15:49 -0800)
18 files changed:
css/lttl.css
index.php
notes.md
src/Y/alias.js
src/Y/core.js
src/Y/type.js
src/Y/y-core.js
src/Y/y-function.js
src/Y/y-op.js
src/tanks/game/game.js
src/tanks/main.js
src/tanks/map/trajectory.js
src/tanks/thing/bullet.js
src/tanks/thing/player.js [moved from src/tanks/ui/player.js with 59% similarity]
src/tanks/thing/tank.js
src/tanks/thing/thing.js
src/tanks/util/utils.js [new file with mode: 0644]
tanks.php

index fb3d778..72ee86a 100644 (file)
@@ -25,5 +25,7 @@ ul, ol, li { list-style: none ! important; margin:0; padding:0; }
     #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; }
 
index 9499a94..27ec53b 100644 (file)
--- a/index.php
+++ b/index.php
@@ -6,12 +6,20 @@
 <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>
index 04afe34..9b04c89 100644 (file)
--- a/notes.md
+++ b/notes.md
@@ -1,8 +1,4 @@
 # Bugs
-- collision short-circuiting, collision events
-- bullet collisions should explode both
-- 5-shot limit
-
 
 
 # TODOs
index a36af68..01249a8 100644 (file)
@@ -6,7 +6,6 @@ var globals   = this
 ,   _Number   = globals.Number
 
 ,   slice    = _Array.prototype.slice
-,   isArray  = _Array.isArray
 ,   toString = _Object.prototype.toString
 ,   hasOwn   = _Object.prototype.hasOwnProperty
 ,   getProto = _Object.getPrototypeOf
index 3718818..0ef4ca1 100644 (file)
@@ -68,7 +68,7 @@ function dset(o, key, value, def){
         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) )
index 7735fdf..a4be9df 100644 (file)
@@ -1,23 +1,26 @@
 // 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.
@@ -41,7 +44,37 @@ function isPlainObject( obj ){
     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);
+    }
+}
index a7abd34..2abb48b 100644 (file)
@@ -7,7 +7,8 @@ Y.set     = dset;
 Y.attr    = dattr;
 Y.extend  = extend;
 
-Y.isA           = isA;
+Y.type          = type;
+Y.is            = is;
 Y.isString      = isString;
 Y.isNumber      = isNumber;
 Y.isFunction    = isFunction;
@@ -38,7 +39,7 @@ function Y(o){
     // 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 );
index dde3287..fe1b668 100644 (file)
@@ -293,7 +293,9 @@ Y(Y.filter);
 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);
 });
index ca5064d..ab272b8 100644 (file)
@@ -52,8 +52,8 @@ Y.op = {
     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){
@@ -68,7 +68,7 @@ Y.op = {
 
 // 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;
 }, {});
 
index 5b7acfe..09a6a6a 100644 (file)
@@ -50,6 +50,7 @@ tanks.Game = new Y.Class('Game', {
         SECONDTH = ELAPSED / 1000;
         SQUARETH = REF_SIZE * SECONDTH
         
+        this.active.invoke('updateCooldowns', NOW);
         this.active.invoke('act');
         
         this.draw();
index ae192e0..00c78e9 100644 (file)
@@ -11,14 +11,13 @@ function main(){
     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();
 }
 
 
@@ -26,14 +25,15 @@ function main(){
 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();
@@ -42,18 +42,14 @@ function setupUI(){
     $(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);
 }
@@ -89,8 +85,8 @@ function initConfig(){
     $('#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){
@@ -149,9 +145,9 @@ function updateInfo(){
     ,   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 );
index d5433ae..344fe1b 100644 (file)
@@ -36,9 +36,9 @@ Trajectory = new Y.Class('Trajectory', math.Line, {
     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;
     },
     
index 5a05fc9..4e19d45 100644 (file)
@@ -49,6 +49,7 @@ Bullet = Thing.subclass('Bullet', {
     },
     
     createCooldowns : Y.op.nop,
+    updateCooldowns : Y.op.nop,
     
     setTarget : function setTarget(x,y){
         var loc = this.loc
@@ -100,7 +101,7 @@ Bullet = Thing.subclass('Bullet', {
         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);
         
similarity index 59%
rename from src/tanks/ui/player.js
rename to src/tanks/thing/player.js
index 7bb3b03..c684716 100644 (file)
@@ -1,84 +1,72 @@
-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)