From 28ea4a26af902a9095ebb7f0cebf8172c96977b3 Mon Sep 17 00:00:00 2001 From: dsc Date: Tue, 11 Jan 2011 03:25:59 -0800 Subject: [PATCH] Adds alpha replay support. --- data/config.yaml | 1 + lib/strftime.js | 1 + lib/strftime.min.js | 1 + src/ezl/math/vec.cjs | 4 + src/tanks/game.cjs | 52 +++++++++++++-- src/tanks/map/level.cjs | 4 +- src/tanks/map/pathing/traversal.cjs | 3 +- src/tanks/thing/bullet.cjs | 2 +- src/tanks/thing/player.cjs | 127 ++++++++++++++++++++++++++--------- src/tanks/thing/tank.cjs | 1 + src/tanks/ui/main.cjs | 15 +++-- 11 files changed, 165 insertions(+), 46 deletions(-) create mode 120000 lib/strftime.js create mode 100644 lib/strftime.min.js diff --git a/data/config.yaml b/data/config.yaml index 52788cd..e32760c 100644 --- a/data/config.yaml +++ b/data/config.yaml @@ -4,6 +4,7 @@ game: gameoverDelay : 1000 debug: showFpsGraph : false + enableGameLogging : false # createGridTable : false # showGridCoords : false map: diff --git a/lib/strftime.js b/lib/strftime.js new file mode 120000 index 0000000..996f99b --- /dev/null +++ b/lib/strftime.js @@ -0,0 +1 @@ +strftime.min.js \ No newline at end of file diff --git a/lib/strftime.min.js b/lib/strftime.min.js new file mode 100644 index 0000000..50a9444 --- /dev/null +++ b/lib/strftime.min.js @@ -0,0 +1 @@ +Number.prototype.pad=function(c,b){var a=""+this;b=b||"0";while(a.length1){var e=c.getYear();if(((e%100)==0)&&((e%400)==0)){++b}else{if((e%4)==0){++b}}}while(a>-1){b+=c.dpm[a--]}return b.pad(3,"0")},k:function(a){return a.getHours().pad(2," ")},l:function(a){return((a.getHours()%12||12).pad(2," "))},M:function(a){return a.getMinutes().pad(2,"0")},m:function(a){return(a.getMonth()+1).pad(2,"0")},n:function(a){return"\n"},p:function(a){return(a.getHours()>11)?"PM":"AM"},R:function(a){return a.strftime_f.H(a)+":"+a.strftime_f.M(a)},r:function(a){return a.strftime_f.I(a)+":"+a.strftime_f.M(a)+":"+a.strftime_f.S(a)+" "+a.strftime_f.p(a)},S:function(a){return a.getSeconds().pad(2,"0")},s:function(a){return Math.floor(a.getTime()/1000)},T:function(a){return a.strftime_f.H(a)+":"+a.strftime_f.M(a)+":"+a.strftime_f.S(a)},t:function(a){return"\t"},u:function(a){return(a.getDay()||7)},v:function(a){return a.strftime_f.e(a)+"-"+a.strftime_f.b(a)+"-"+a.strftime_f.Y(a)},w:function(a){return a.getDay()},X:function(a){return a.toTimeString()},x:function(a){return a.toDateString()},Y:function(a){return a.getFullYear()},y:function(a){return(a.getYear()%100).pad(2)},"%":function(a){return"%"}};Date.prototype.strftime_f["+"]=Date.prototype.strftime_f.c;Date.prototype.strftime_f.h=Date.prototype.strftime_f.b;Date.prototype.strftime=function(a){var b="";var e=0;while(e= ++this.bounces ) { if (!tvsl.side) - return console.error('Null reflection line!', 'to:', to, 'blocker:', unit, 'blockers:', tvsl.blockers._o); + return console.warn('Null reflection line!', 'to:', to, 'blocker:', unit, 'blockers:', tvsl.blockers._o); ng = vec.reflect(traj.p2, tvsl.side); traj.reset(to.x,to.y, ng.x,ng.y); diff --git a/src/tanks/thing/player.cjs b/src/tanks/thing/player.cjs index 78586c0..c12bba2 100644 --- a/src/tanks/thing/player.cjs +++ b/src/tanks/thing/player.cjs @@ -2,6 +2,9 @@ //#ensure "jquery.hotkeys" var Y = require('Y').Y +, Vec = require('ezl/math/vec').Vec + +, config = require('tanks/config').config , Tank = require('tanks/thing/tank').Tank , Inventoried = require('tanks/mixins/inventoried').Inventoried , @@ -12,6 +15,8 @@ exports['Player'] = Tank.subclass('Player', { __mixins__ : [ Inventoried ], + enableGameLogging : false, + // Attributes stats: { hp : 1, // health @@ -25,28 +30,44 @@ Tank.subclass('Player', { /// Instance /// align : null, + replayMode : false, - init : function init(align){ + init : function init(align, replay){ Tank.init.call(this, align); - this.activeKeys = new Y.YArray(); + this.gameLog = []; this.queue = []; + this.activeKeys = new Y.YArray(); + + this.replayMode = !!replay; - this.keydown = this.keydown.bind(this); - this.keyup = this.keyup.bind(this); - this.mousedown = this.mousedown.bind(this); - this.mouseup = this.mouseup.bind(this); - this.mousemove = this.mousemove.bind(this); - - $(document) - .bind('keydown', this.keydown) - .bind('keyup', this.keyup) - .bind('mousedown', this.mousedown) - .bind('mouseup', this.mouseup) - .bind('mousemove', this.mousemove) - ; + if (this.replayMode) { + this.replay = replay + this.nextMove = replay.shift(); + + } else { + this.keydown = this.keydown.bind(this); + this.keyup = this.keyup.bind(this); + this.mousedown = this.mousedown.bind(this); + this.mouseup = this.mouseup.bind(this); + this.mousemove = this.mousemove.bind(this); + + $(document) + .bind('keydown', this.keydown) + .bind('keyup', this.keyup) + .bind('mousedown', this.mousedown) + .bind('mouseup', this.mouseup) + .bind('mousemove', this.mousemove) + ; + } + }, + + setReplay : function setReplay(replay){ + this.replayMode = true; + this.replay = replay + this.nextMove = replay.shift(); }, activeKeys : null, @@ -55,8 +76,7 @@ Tank.subclass('Player', { queue : null, - - + first : true, act : function act(elapsed, now){ this.elapsed = elapsed; this.now = now; @@ -64,25 +84,61 @@ Tank.subclass('Player', { if (this.dead) return this; - var action = this.queue.shift(); - if (action && action.type === 'fire') - this.shoot(action.x, action.y); + var action; + if (this.replayMode) { + action = this.nextMove; + + if (!action || TICKS < action.ticks) + return this; + + // var drift = TICKS - action.ticks; + // console.log('replay drift:', (drift > 0 ? '+' : '')+drift); + this.nextMove = this.replay.shift(); + + } else { + action = this.queue.shift(); + + if ( !action && this.activeKeys.size() ) { + var to = this.getPlayerMove(); + if (to) action = { 'type':'move', 'data':to }; + } + } - else if ( this.activeKeys.size() ) - this.playerMove(); + if ( !action ) + return this; + + var data = action.data; + switch (action.type) { + case 'shoot': + if (!this.replayMode && this.enableGameLogging) + this.gameLog.push({ 'type':'shoot', 'ticks':TICKS, 'data':data.toObject() }); + this.shoot(data.x, data.y); + break; + + case 'move': + this.move(data.x, data.y); + break; + + default: + console.error('wtf unknown player action "'+action.type+'"', action); + break; + } return this; }, attack : function attack(x,y){ - this.queue.push({ - type : 'fire', - x : x, - y : y - }); + if ( x === undefined || y === undefined ) { + var theta = this.barrel.transform.rotate + , sin = Math.sin(theta), cos = Math.cos(theta) + , t = this.getTurretLoc(); + x = t.x + REF_SIZE*cos; + y = t.y + REF_SIZE*sin; + } + this.queue.push({ type:'shoot', data:new Vec(x,y) }); }, - playerMove : function playerMove(){ + getPlayerMove : function getPlayerMove(){ var dir = this.activeKeys .unique() .sort() @@ -91,7 +147,11 @@ Tank.subclass('Player', { if (!dir) return; // @see Tank.move() - this.move( this.loc.moveByDir(dir, this.stats.move.val * SQUARETH) ); + var to = this.loc.moveByDir(dir, this.stats.move.val * SQUARETH); + if (!this.replayMode && this.enableGameLogging) + this.gameLog.push({ 'type':'move', 'ticks':TICKS, 'data':to.toObject() }); + + return to; }, @@ -104,7 +164,7 @@ Tank.subclass('Player', { }, keydown : function keydown(evt){ - if ( !this.game.loop.running ) return; + if ( !this.game.loop.running || this.replayMode ) return; this.updateMeta(evt); @@ -131,7 +191,7 @@ Tank.subclass('Player', { }, mousedown : function mousedown(evt){ - if ( !this.game.loop.running ) return; + if ( !this.game.loop.running || this.replayMode ) return; switch (evt.which) { case 1: evt.leftMouse = true; @@ -168,7 +228,7 @@ Tank.subclass('Player', { }, mousemove : function mousemove(evt){ - if ( !this.game.loop.running ) return; + if ( !this.game.loop.running || this.replayMode ) return; this.rotateBarrelRelPage(evt.pageX, evt.pageY); } @@ -200,3 +260,6 @@ exports['Key'] = { }; +config.updateOnChange('debug.enableGameLogging', Player.fn); + + diff --git a/src/tanks/thing/tank.cjs b/src/tanks/thing/tank.cjs index dfbcbed..17c7c29 100644 --- a/src/tanks/thing/tank.cjs +++ b/src/tanks/thing/tank.cjs @@ -155,6 +155,7 @@ Thing.subclass('Tank', function(Tank){ if ( this.nShots >= this.stats.shots.val || !this.cooldowns.attack.ready ) return null; + if (x instanceof Array) { y = x[_Y]; x = x[_X]; } var xydef = (x !== undefined && y !== undefined); if (xydef) this.rotateBarrel(x,y); diff --git a/src/tanks/ui/main.cjs b/src/tanks/ui/main.cjs index 2fdd6d9..b28f224 100644 --- a/src/tanks/ui/main.cjs +++ b/src/tanks/ui/main.cjs @@ -93,11 +93,16 @@ function gameExists(){ return !!tanks.game; } function setupGame(){ if ( gameExists() ) teardownGame(); - game = tanks.game = new Game(); - - game.addEventListener('win', gameover('You Win!', 'Play Again', '')); - game.addEventListener('lose', gameover('You Lose :(', 'Try Again', '')); - + var replayFile = qkv.replay; + game = tanks.game = new Game(replayFile); + + if (replayFile) { + game.addEventListener('win', gameover('You Won the Replay!', 'Watch Again', '')); + game.addEventListener('lose', gameover('You Lost the Replay :(', 'Watch Again', '')); + } else { + game.addEventListener('win', gameover('You Win!', 'Play Again', '')); + game.addEventListener('lose', gameover('You Lose :(', 'Try Again', '')); + } ctx = game.level.ctx; } -- 1.7.0.4