Adds game restart.
authordsc <david.schoonover@gmail.com>
Mon, 22 Nov 2010 05:22:53 +0000 (21:22 -0800)
committerdsc <david.schoonover@gmail.com>
Mon, 22 Nov 2010 05:22:53 +0000 (21:22 -0800)
css/lttl.css
notes.md
src/Y/core.js
src/Y/modules/y.kv.js
src/Y/y-op.js
src/Y/y-string.js
src/tanks/game/game.js
src/tanks/main.js
src/tanks/map/level.js
src/tanks/util/utils.js
tanks.php

index 1cb208d..4a925ee 100644 (file)
@@ -39,6 +39,10 @@ table.grid td { /* outline:1px solid rgba(255,255,255,0.1); */
         #notes ul, #notes ol, #notes li { list-style:circle ! important; }
         #notes li { margin-left:1em; }
 
+.pinkbutton { width:50%; margin:0 auto; cursor:pointer;
+    font-size:1.5em; padding:0.5em; background-color:#E73075; color:#fff; text-align:center; text-transform:uppercase; }
+
+#welcome { cursor:pointer; }
 
 /* 
 #welcome fieldset { border:1px solid #182B53; padding:1em; }
index 9596a07..3285ae5 100644 (file)
--- a/notes.md
+++ b/notes.md
@@ -15,6 +15,7 @@
 - Move game objects into namespace `tanks`
 - Move portal into namespace (ideas: `portal`, canvas tools... `easel`, or `ezl`)
 - Support touch events (for iPad?)
+- Migrate A* code into PathMap
 
 
 # Notes
index 0ef4ca1..9f70f78 100644 (file)
@@ -56,8 +56,11 @@ function filter(o, fn){
 }
 
 function set(o, key, value, def){
-    if ( o && key !== undefined )
+    if ( o && key !== undefined ){
+        if (def === undefined)
+            def = o[key];
         o[key] = (value !== undefined ? value : def);
+    }
     return o;
 }
 
index 4bf85f8..76873b3 100644 (file)
@@ -3,24 +3,25 @@
 var enc = encodeURIComponent, 
     dec = decodeURIComponent;
 
-Y.YObject.prototype.toKV = toKV;
+Y.YObject.prototype.toKV =
 function toKV(del){
     return this.reduce(function(acc, v, k){
         return acc.push( enc(k) + '=' + enc(v) );
     }, Y([]))
     .join(del !== undefined ? del : "&");
-}
+};
 
 Y.YString.prototype.toObject =
-Y.YString.prototype.fromKV = fromKV;
+Y.YString.prototype.fromKV =
 function fromKV(del){
     return Y(this.split(del || '&'))
         .reduce(function(acc, pair){
-            var kv = pair.split('='), k = kv[0], v = kv[1];
-            if (k) acc[k] = dec(v);
+            var idx = pair.indexOf('=')
+            ,   k = dec(pair.slice(0,idx))
+            ,   v = dec(pair.slice(idx+1)) ;
+            if (k) acc[k] = v;
             return acc;
         }, {});
-}
-
+};
 
 })(this.Y);
index ab272b8..7d295ea 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, // set(o, key, value, def)
-    attr:    attr, // attr(o, key, value, def)
+    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){
@@ -62,10 +62,14 @@ Y.op = {
             else
                 return obj;
         };
+    },
+    extend : function extend(A,B){
+        return slice.call(arguments,1).reduce('A donor -> Y.reduce(donor, Y.op.vkset, A)'.lambda(), A);
     }
     
 };
 
+
 // Curry all operators
 Y.op = Y.reduce(Y.op, function(op, fn, k){
     op[k] = Y( Y(fn).curry() );
index 572d443..906d701 100644 (file)
@@ -1,66 +1,71 @@
 
-function strip(){ 
-    return this._o.replace(/(^\s+|\s+$)/g, '');
-}
-
-function trim(val){
-    var s = this._o+'';
-    val = val+'';
-    if (s.length < val.length)
-        return s;
-    else if ( s.indexOf(val) === 0 )
-        return s.slice(val.length);
-    else
-        return s;
-}
-
-function rtrim(val){
-    var s = this._o+'';
-    val = val+'';
-    
-    var idx = (s.length - val.length);
-    if (idx < 0)
-        return s;
-    else if ( s.lastIndexOf(val) === idx )
-        return s.slice(0, idx);
-    else
-        return s;
-}
-
-function startsWith(val){
-    return this._o.indexOf(val) === 0;
-}
-
-function endsWith(val){
-    var s = this._o;
-    return s.lastIndexOf(val) === (s.length - val.length);
-}
-
 var
 YString =
 Y.YString =
-YCollection.subclass('YString', {
-    init : function init(o){
-        if (!o) o = "";
-        this._o = o;
-        YCollection.init.call(this, o);
-    },
-    strip: strip,
-    trim: trim,
-    ltrim: trim,
-    rtrim: rtrim,
-    startsWith: startsWith,
-    endsWith: endsWith,
+YCollection.subclass('YString', function(YString){
+    mixinNames(YString, String, [
+            'slice', 'split',
+            'substr', 'substring',
+            'concat', 'replace',
+            'toLowerCase', 'toUpperCase'
+        ], true, true);
+    mixinNames(YString, String, [ 'indexOf', 'lastIndexOf', 'charAt', 'charCodeAt' ], true, false);
     
-    compare : function compare(n){
-        var m = this._o;
-        return (m > n ?  1 :
-               (m < n ? -1 : 0 ));
-    },
-    
-    toString: function(){
-        // return 'Y"'+this._o+'"';
-        return this._o;
+    function trim(val){
+        var s = this._o+'';
+        val = val+'';
+        if (s.length < val.length)
+            return s;
+        else if ( s.indexOf(val) === 0 )
+            return s.slice(val.length);
+        else
+            return s;
     }
+    
+    Y.op.extend(this, {
+        init : function init(o){
+            if (!o) o = "";
+            this._o = o;
+            YCollection.init.call(this, o);
+        },
+        
+        strip: function strip(){ 
+            return this._o.replace(/(^\s+|\s+$)/g, '');
+        },
+        
+        trim: trim,
+        ltrim: trim,
+        
+        rtrim: function rtrim(val){
+            var s = this._o+'';
+            val = val+'';
+            
+            var idx = (s.length - val.length);
+            if (idx < 0)
+                return s;
+            else if ( s.lastIndexOf(val) === idx )
+                return s.slice(0, idx);
+            else
+                return s;
+        },
+        
+        startsWith: function startsWith(val){
+            return this._o.indexOf(val) === 0;
+        },
+        endsWith: function endsWith(val){
+            var s = this._o;
+            return s.lastIndexOf(val) === (s.length - val.length);
+        },
+        
+        compare : function compare(n){
+            var m = this._o;
+            return (m > n ?  1 :
+                   (m < n ? -1 : 0 ));
+        },
+        
+        toString: function(){
+            return this._o;
+        }
+    });
 });
 
index 08f57cb..78e25d7 100644 (file)
@@ -27,6 +27,8 @@ tanks.Game = new Y.Class('Game', {
         Thing.addEventListener('destroy', this.killUnit);
         
         this.addEventListener('tick', this.tick);
+        
+        this.fire('ready', this);
     },
     
     
@@ -58,6 +60,16 @@ tanks.Game = new Y.Class('Game', {
         this.active.invoke('act');
         
         this.draw();
+        
+        if (this.player.dead) {
+            this.fire('lose', this.player);
+            this.stop();
+            
+        } else if ( !this.units.filter('!_.dead'.lambda()).remove(this.player).size() ) {
+            this.fire('win', this.player);
+            this.stop();
+        }
+        
     },
     
     
index 0779141..1211d25 100644 (file)
 jQuery(main);
 
 this.main = main;
+
+// Main method is only executed once, so we'll setup things
+// that don't change between games.
 function main(){
-    var v = $('#viewport');
+    qkv = Y(window.location.search).fromKV();
     
-    if (window.LBT) LBT.stop();
-    v.empty();
+    /// Debug ///
+    if (qkv.debug) $('#debug').toggle();
     
-    LBT = new tanks.Game();
-    ctx = LBT.level.ctx;
+    // Show debug
+    $(document).bind('keydown', 'ctrl+c', function(evt){ $('#debug').toggle(); });
+    
+    // Don't fire on clicks in the debug menu
+    $('#debug').bind('mousedown', Y.op.K(false));
     
-    P = LBT.addUnit(new PlayerTank(1), 5,9);
-    E = 
-    LBT.addUnit(new Tank(2), 0,1);
-    LBT.addUnit(new Tank(2), 1,0);
-    LBT.addUnit(new Tank(2), 8,1);
+    // Update config when changed
+    $('#debug input').bind('change', updateConfig);
+    
+    
+    // Create #pause box
+    $('#welcome').clone()
+        .attr('id', 'pause')
+        .hide()
+        .appendTo( $('body') )
+        .find('.box').html('<h1>Paused!</h1>');
     
+    // Create Victory/Defeat box
+    $('#welcome').clone()
+        .attr('id', 'gameover')
+        .hide()
+        .appendTo( $('body') )
+            .find('.box')
+            .html('<h1>You Win!</h1><p></p><div class="restart pinkbutton rounded">Play Again</div>');
+    
+    
+    // Bind to all future restart buttons
+    $('#gameover .restart')
+        .live('click', function(evt){
+            setupGame();
+            setupUI();
+            
+            startGame();
+        });
+    
+    setupGame();
     setupUI();
+}
+
+function gameExists(){ return !!tanks.currentGame; }
+
+function setupGame(){
+    if ( gameExists() ) teardownGame();
     
-    // toggleGame();
-    updateInfo();
+    LBT = tanks.currentGame = new tanks.Game();
     
-    pm = LBT.pathmap;
+    LBT.addEventListener('win',  gameover('You Win!', 'Play Again', ''));
+    LBT.addEventListener('lose', gameover('You Lose :(', 'Try Again', ''));
+    
+    ctx = LBT.level.ctx;
+}
+
+function teardownGame(){
+    LBT.stop();
+    $('#viewport').empty();
 }
 
 
 // Set up UI listeners
 function setupUI(){
-    LBT.loop.spark = new FpsSparkline(LBT.loop, '.fps-sparkline', 0,0);
+    if ( gameExists() ) teardownUI();
     
+    $('#welcome').show();
+    LBT.loop.spark = new FpsSparkline(LBT.loop, '.fps-sparkline', 0,0);
     initConfig();
-    $('#config').bind('mousedown', Y.op.K(false));
-    $('#config input').bind('change', updateConfig);
-    
     LBT.root.draw();
     
-    $(document).bind('keydown', 'ctrl+c', toggleConfig);
-    
     // Start button (click or return key)
-    $(document).bind('keydown', 'return', startGame);
     $(document).bind('click', startGame);
-    
-    $('#welcome').clone()
-        .attr('id', 'pause')
-        .hide()
-        .appendTo( $('body') )
-        .find('.box').html('<h1>Paused!</h1>');
+    $(document).bind('keydown', 'return', startGame);
     
     
     LBT.root.draw();
     setInterval(updateInfo, 1000);
     
+    updateInfo();
+    
     // Fix grid-size on resize
     // $(window).bind('resize', resizeGame);
 }
 
-
-function startGame(evt){
-    $(document).unbind('click', startGame);
+function teardownUI(){
+    $(document).unbind('click',   startGame);
     $(document).unbind('keydown', startGame);
+    $(document).unbind('keydown', pauseGame);
     
-    $('#welcome').hide();
-    countdownToStart(3);
+    $('#welcome, #pause, #gameover').hide();
 }
 
-function onGameStart(){
-    $('#overlay').hide();
-    toggleGame();
-    $(document).bind('keydown', 'return', pauseGame);
-}
 
-function countdownToStart(n){
-    var el
-    ,   showFor  = 750
-    ,   pauseFor = 500
-    
-    ,   body = $('body')
-    ,   sizeRatio = 0.6
-    ,   values = new Y(1,n+1).unshift('GO').end()
-    ,   colors = [ '#E73075', '#2992C5', '#2992C5', '#2992C5' ]
-    ;
-    
-    function tickDown(){
-        var bw = body.width()
-        ,   bh = body.height()
-        ,   size = bh*sizeRatio
-        
-        ,   el = $('<div/>')
-            .width(bh*0.75).height(bh*0.75)
-            .text( values.pop() )
-            .css({
-                'position'   : 'fixed',
-                'top'        : ((bh - size)/2)+'px',
-                'left'       : (bw/2 - bh*0.375)+'px',
-                'overflow'   : 'hidden',
-                'z-index'    : 1000,
-                
-                'text-align' : 'center',
-                'font-size'  : size+'px',
-                'color'      : '#000000',
-                'background-color' : colors.pop(),
-            })
-        ;
-        
-        ' -moz- -webkit-'
-            .split(' ')
-            .forEach(function(prefix){
-                // el.css(prefix+'border-radius', (size*0.25)+'px');
-                el.css(prefix+'border-radius', (bh/2)+'px');
-            });
-        
-        body.append(el);
-        
-        setTimeout(function(){
-            el.remove();
-            
-            if ( values.length ) {
-                setTimeout(tickDown, pauseFor);
-            } else {
-                onGameStart();
-            }
-        }, showFor);
-    }
+function startGame(evt){
+    $(document).unbind('click',   startGame);
+    $(document).unbind('keydown', startGame);
     
-    tickDown();
+    $('#welcome').hide();
+    countdownToStart(3, function(){
+        $('#overlay').hide();
+        $(document).bind('keydown', 'return', pauseGame);
+        toggleGame();
+    });
 }
 
 function pauseGame(evt){
-    toggleOverlay();
+    $('#overlay').toggle();
     $('#pause').toggle();
     toggleGame();
 }
 
-
-
-
 function toggleGame(evt){
     if (LBT.loop.running)
         LBT.stop();
@@ -145,10 +128,6 @@ function toggleGame(evt){
     updateInfo();
 }
 
-function toggleConfig(evt){ $('#debug').toggle(); }
-function toggleOverlay(evt){ $('#overlay').toggle(); }
-function togglePathingOverlay(evt){ LBT.showOverlay = !(LBT.showOverlay); }
-
 function resizeGame(evt){
     LBT.resize(evt);
     
@@ -158,72 +137,43 @@ function resizeGame(evt){
     }
 }
 
+function gameover(headline, button, body, evt){
+    $('#overlay').toggle();
+    $('#gameover')
+        .find('h1').text(headline).end()
+        .find('.pinkbutton').text(button).end()
+        .find('p').text(body).end()
+        .show();
+}
+gameover = Y(gameover).curry();
+
 
 function initConfig(){
-    var c = tanks.config, p = c.pathing;
+    var c = tanks.config
+    ,   p = c.pathing
+    ;
     
     $('#config [name=pathmap]').attr('checked', p.overlayPathmap);
     $('#config [name=aipaths]').attr('checked', p.overlayAIPaths);
     $('#config [name=trajectories]').attr('checked', p.traceTrajectories);
     
-    $('#config [name=gridCoords]').attr('checked', tanks.config.debug.gridShowCoords);
-    $('#viewport .layer.grid')[(tanks.config.debug.gridShowCoords ? 'add' : 'remove')+'Class']('gridShowCoords');
-    
-    // $('#config [name=bullets]').val(c.debug.projectiles);
-    // updateBullets( c.debug.projectiles );
+    $('#config [name=gridCoords]').attr('checked', c.debug.gridShowCoords);
+    $('#viewport .layer.grid')[(c.debug.gridShowCoords ? 'add' : 'remove')+'Class']('gridShowCoords');
 }
 
 function updateConfig(evt){
-    var p = tanks.config.pathing;
+    var c = tanks.config
+    ,   p = c.pathing
+    ;
+    
     p.overlayPathmap    = $('#config [name=pathmap]').attr('checked');
     p.overlayAIPaths    = $('#config [name=aipaths]').attr('checked');
     p.traceTrajectories = $('#config [name=trajectories]').attr('checked');
-    tanks.config.debug.gridShowCoords = $('#config [name=gridCoords]').attr('checked');
     
-    $('#viewport .layer.grid')[(tanks.config.debug.gridShowCoords ? 'add' : 'remove')+'Class']('gridShowCoords');
+    c.debug.gridShowCoords = $('#config [name=gridCoords]').attr('checked');
+    $('#viewport .layer.grid')[(c.debug.gridShowCoords ? 'add' : 'remove')+'Class']('gridShowCoords');
 }
 
-function spawnBullet(x,y, atX,atY){
-    if (x === undefined && y === undefined) {
-        var loc = randOpenLoc();
-        x = loc.x; y = loc.y;
-    }
-    if (atX === undefined && atY === undefined) do {
-        atX = rand(0,COLUMNS*REF_SIZE);
-        atY = rand(0,ROWS*REF_SIZE);
-    } while ( atX === x && atY === y);
-    
-    btank.setLocation(x,y);
-    return btank.shoot(atX,atY);
-}
-
-function updateBullets(n){
-    if ( !Y.isNumber(n) || isNaN(n) || n === bullets.size() ) return;
-    
-    while (n < bullets.size()) {
-        var i = Math.round(rand(0, bullets.size()-1));
-        bullets.remove( bullets.attr(i).remove() );
-    }
-    
-    while (n > bullets.size())
-        bullets.push(spawnBullet());
-}
-
-function rand(a,b){ return a + Math.random()*(b-a); }
-function randOpenLoc(){
-    var BP = Bullet.prototype
-    ,   w  = BP.width, h = BP.height
-    ,   offX = BP.offsetX, offY = BP.offsetY
-    ;
-    do {
-        var x = rand(-offX, COLUMNS*REF_SIZE-w)
-        ,   y = rand(-offY, ROWS*REF_SIZE-h);
-    } while ( LBT.pathmap.get(x+offX,y+offY, x+w,y+h).size() );
-    return new math.Vec(x,y);
-}
-
-
-
 // Update performance info periodically
 function updateInfo(){
     var loop     = LBT.loop
@@ -246,32 +196,62 @@ function updateInfo(){
     return false;
 }
 
+function countdownToStart(n, fn){
+    var el
+    ,   showFor  = 750
+    ,   pauseFor = 500
+    
+    ,   body = $('body')
+    ,   sizeRatio = 0.6
+    ,   values = new Y(1,n+1).unshift('GO').end()
+    ,   colors = [ '#E73075', '#2992C5', '#2992C5', '#2992C5' ]
+    ;
+    
+    function tickDown(){
+        var bw = body.width()
+        ,   bh = body.height()
+        ,   size = bh*sizeRatio
+        
+        ,   el = $('<div/>')
+            .width(bh*0.75).height(bh*0.75)
+            .text( values.pop() )
+            .css({
+                'position'   : 'fixed',
+                'top'        : ((bh - size)/2)+'px',
+                'left'       : (bw/2 - bh*0.375)+'px',
+                'overflow'   : 'hidden',
+                'z-index'    : 1000,
+                
+                'text-align' : 'center',
+                'font-size'  : size+'px',
+                'color'      : '#000000',
+                'background-color' : colors.pop(),
+            })
+        ;
+        
+        ' -moz- -webkit-'
+            .split(' ')
+            .forEach(function(prefix){
+                // el.css(prefix+'border-radius', (size*0.25)+'px');
+                el.css(prefix+'border-radius', (bh/2)+'px');
+            });
+        
+        body.append(el);
+        
+        setTimeout(function(){
+            el.remove();
+            
+            if ( values.length ) {
+                setTimeout(tickDown, pauseFor);
+            } else {
+                fn();
+            }
+        }, showFor);
+    }
+    
+    tickDown();
+}
 
-// function initUki(){
-//     uki({ view:'Box', rect:'0 0 250 500', anchor:'top width', className:'box', childViews:[
-//         { view:'Box', rect:'8 8 250 0', anchor:'top width height', childViews:[
-//             { view:'HFlow', rect:'250 25', anchor:'width', childViews:[
-//                 { view:'Label', rect:'200 25', anchor:'top left', text:'Bullets' },
-//                 { view:'TextField', rect:'50 25', anchor:'top right', name:'bullets', value:'10', background:'' }
-//             ]}
-//         ]}
-//     ]})
-//     .attachTo(
-//         $('<div/>').css({
-//             'position':'relative',
-//             'width':'100%',
-//             'top':0, 'left':0
-//         }).appendTo('#controls')[0], '0 0');
-//     // .attachTo(window, '10 10');
-// }
 
 })();
 
-function dumpPathmap(){
-    var pm = LBT.pathmap;
-    console.warn(new Date(), pm);
-    pm.collect(pm.x1,pm.y1, pm.x2,pm.y2, function(acc, v, r){
-        console.log(r+'');
-    });
-    console.log(' ');
-}
index efef86b..413110b 100644 (file)
@@ -7,11 +7,14 @@ Level = Rect.subclass('Level', {
         this.pathmap = new PathMap(this, 0,0, w, h, CAPACITY);
         this.walls   = [];
         
-        this.setup();
         this.canvas.remove();
+        
+        game.addEventListener('ready', this.setup.bind(this));
     },
     
     setup : function setup(){
+        var game = this.game;
+        
         Y.map([ [6,1, 1,4],
                 [6,7, 1,2],
                 [1,7, 2,2],
@@ -19,6 +22,14 @@ Level = Rect.subclass('Level', {
                 [2,2, 1,1]  ],
             "this.addWall.apply(this, Y.map(_, '*50'))",
             this );
+        
+        P = 
+        game.player = game.addUnit(new PlayerTank(1), 5,9);
+        
+        E = 
+        game.addUnit(new Tank(2), 0,1);
+        game.addUnit(new Tank(2), 1,0);
+        game.addUnit(new Tank(2), 8,1);
     },
     
     addWall : function addWall(x,y, w,h){
index 9399beb..04c1494 100644 (file)
@@ -5,3 +5,11 @@ Object.dump = function(o){
     return '{ '+out.join(', ')+' }';
 };
 
+function dumpPathmap(){
+    var pm = LBT.pathmap;
+    console.warn(new Date(), pm);
+    pm.collect(pm.x1,pm.y1, pm.x2,pm.y2, function(acc, v, r){
+        console.log(r+'');
+    });
+    console.log(' ');
+}
index 82bba0a..8230724 100644 (file)
--- a/tanks.php
+++ b/tanks.php
@@ -50,6 +50,7 @@ class Tanks {
         
         "src/Y/y.js.php",
         "src/Y/modules/y.event.js",
+        "src/Y/modules/y.kv.js",
         
         "src/evt/evt.class.js",