From: dsc Date: Mon, 22 Nov 2010 05:22:53 +0000 (-0800) Subject: Adds game restart. X-Git-Url: http://git.less.ly:3516/?a=commitdiff_plain;h=c00315128c4009b7f119a9e533f011f2b1b342f8;p=tanks.git Adds game restart. --- diff --git a/css/lttl.css b/css/lttl.css index 1cb208d..4a925ee 100644 --- a/css/lttl.css +++ b/css/lttl.css @@ -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; } diff --git a/notes.md b/notes.md index 9596a07..3285ae5 100644 --- 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 diff --git a/src/Y/core.js b/src/Y/core.js index 0ef4ca1..9f70f78 100644 --- a/src/Y/core.js +++ b/src/Y/core.js @@ -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; } diff --git a/src/Y/modules/y.kv.js b/src/Y/modules/y.kv.js index 4bf85f8..76873b3 100644 --- a/src/Y/modules/y.kv.js +++ b/src/Y/modules/y.kv.js @@ -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); diff --git a/src/Y/y-op.js b/src/Y/y-op.js index ab272b8..7d295ea 100644 --- a/src/Y/y-op.js +++ b/src/Y/y-op.js @@ -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() ); diff --git a/src/Y/y-string.js b/src/Y/y-string.js index 572d443..906d701 100644 --- a/src/Y/y-string.js +++ b/src/Y/y-string.js @@ -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; + } + }); }); diff --git a/src/tanks/game/game.js b/src/tanks/game/game.js index 08f57cb..78e25d7 100644 --- a/src/tanks/game/game.js +++ b/src/tanks/game/game.js @@ -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(); + } + }, diff --git a/src/tanks/main.js b/src/tanks/main.js index 0779141..1211d25 100644 --- a/src/tanks/main.js +++ b/src/tanks/main.js @@ -2,140 +2,123 @@ 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('

Paused!

'); + // Create Victory/Defeat box + $('#welcome').clone() + .attr('id', 'gameover') + .hide() + .appendTo( $('body') ) + .find('.box') + .html('

You Win!

Play Again
'); + + + // 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('

Paused!

'); + $(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 = $('
') - .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 = $('
') + .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( -// $('
').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(' '); -} diff --git a/src/tanks/map/level.js b/src/tanks/map/level.js index efef86b..413110b 100644 --- a/src/tanks/map/level.js +++ b/src/tanks/map/level.js @@ -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){ diff --git a/src/tanks/util/utils.js b/src/tanks/util/utils.js index 9399beb..04c1494 100644 --- a/src/tanks/util/utils.js +++ b/src/tanks/util/utils.js @@ -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(' '); +} diff --git a/tanks.php b/tanks.php index 82bba0a..8230724 100644 --- 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",