gameoverDelay : 1000
debug:
showFpsGraph : false
+ enableGameLogging : false
# createGridTable : false
# showGridCoords : false
map:
--- /dev/null
+strftime.min.js
\ No newline at end of file
--- /dev/null
+Number.prototype.pad=function(c,b){var a=""+this;b=b||"0";while(a.length<c){a=b+a}return a};Date.prototype.months=["January","February","March","April","May","June","July","August","September","October","November","December"];Date.prototype.weekdays=["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"];Date.prototype.dpm=[31,28,31,30,31,30,31,31,30,31,30,31];Date.prototype.strftime_f={A:function(a){return a.weekdays[a.getDay()]},a:function(a){return a.weekdays[a.getDay()].substring(0,3)},B:function(a){return a.months[a.getMonth()]},b:function(a){return a.months[a.getMonth()].substring(0,3)},C:function(a){return Math.floor(a.getFullYear()/100)},c:function(a){return a.toString()},D:function(a){return a.strftime_f.m(a)+"/"+a.strftime_f.d(a)+"/"+a.strftime_f.y(a)},d:function(a){return a.getDate().pad(2,"0")},e:function(a){return a.getDate().pad(2," ")},F:function(a){return a.strftime_f.Y(a)+"-"+a.strftime_f.m(a)+"-"+a.strftime_f.d(a)},H:function(a){return a.getHours().pad(2,"0")},I:function(a){return((a.getHours()%12||12).pad(2))},j:function(c){var b=c.getDate();var a=c.getMonth()-1;if(a>1){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<a.length){var d=a.substring(e,e+1);if(d=="%"){d=a.substring(++e,e+1);b+=(this.strftime_f[d])?this.strftime_f[d](this):d}else{b+=d}++e}return b};
\ No newline at end of file
return new this.__class__(this[_X],this[_Y]);
},
+ toObject : function toObject(){
+ return { 'x':this[_X], 'y':this[_Y] };
+ },
+
toString : function(){
var p = 2, x = this[_X], y = this[_Y];
x = ((x % 1 !== 0) ? x.toFixed(p) : x);
//#ensure "future"
//#ensure "jquery"
+//#ensure "strftime"
var Y = require('Y').Y
, Event = require('Y/modules/y.event').Event
// Config
gameoverDelay : null,
- // Defaults
+ // Instance
gameover : false,
+ isReplay : false,
-
- init : function initGame(viewport){
+ init : function initGame(replayFile){
Y.bindAll(this);
this.byId = {};
this.bullets = new Y.YArray();
this.animations = new Y.YArray();
- this.viewport = $(viewport || GRID_ELEMENT);
+ this.viewport = $(GRID_ELEMENT);
this.loop = new EventLoop(FRAME_RATE, this);
this.root =
this.addEventListener('tick', this.tick);
this.level.setup();
- this.fire('ready', this);
+
+ if (replayFile) {
+ this.isReplay = true;
+ this.loadLog(replayFile);
+ } else
+ this.fire('ready', this);
},
destroy : function destroy(){
},
+ saveLog : function saveLog(name){
+ var gameLog = this.player.gameLog;
+ if (!gameLog.length)
+ return this;
+
+ var url = new Date().strftime('/gamelogs/%Y%m%d-%H%M%S'+(name ? '-'+name : '')+'.json');
+ jQuery.ajax({
+ 'url' : url,
+ 'type' : 'PUT',
+ 'data' : JSON.stringify(gameLog),
+ 'success' : function(data, textStatus){
+ console.log('success!', data, textStatus);
+ },
+ 'error' : function(xhr, textStatus, errorThrown){
+ console.error('error!', xhr, textStatus, errorThrown);
+ }
+ });
+ console.log('Saving log to //'+(window.location.host)+url+' ...');
+
+ return this;
+ },
+
+ loadLog : function loadLog(file){
+ var self = this
+ , url = '/gamelogs/'+file;
+ jQuery.get(url, function(replayData){
+ if (!replayData)
+ return console.error('No replay data found!');
+ self.player.setReplay(replayData);
+ console.log('Replay loaded!');
+ self.fire('ready', self);
+ });
+ console.log('Loading replay log from //'+(window.location.host)+url+' ...');
+ return this;
+ },
+
/**
* @obsolete
this.bbox = shape.bbox;
},
- setup : function setup(){
+ setup : function setup(isReplay){
var game = this.game;
this.bounds =
var data = tanks.data.units;
this.units.map(function(x){
+ // if (isReplay && x.type === 'player')
+ // return null;
var obj = data[x.type].instantiate(x.align);
return game.addThing(obj, x.loc[0], x.loc[1]);
});
to = tr.pointAtY(B.y2 + 1 + bb.originBottom );
} else {
- console.error('Null reflection line!', 'to:', to, 'blocker:', blocker);
- // throw new Error('Null reflection line! to:'+to+', blocker:'+blocker);
+ console.warn('Null reflection line!', 'to:', to, 'blocker:', blocker);
}
return to;
// Reflection!
if ( unit.isReflective && this.bounceLimit >= ++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);
//#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
,
Tank.subclass('Player', {
__mixins__ : [ Inventoried ],
+ enableGameLogging : false,
+
// Attributes
stats: {
hp : 1, // health
/// 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,
queue : null,
-
-
+ first : true,
act : function act(elapsed, now){
this.elapsed = elapsed;
this.now = now;
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()
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;
},
},
keydown : function keydown(evt){
- if ( !this.game.loop.running ) return;
+ if ( !this.game.loop.running || this.replayMode ) return;
this.updateMeta(evt);
},
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;
},
mousemove : function mousemove(evt){
- if ( !this.game.loop.running ) return;
+ if ( !this.game.loop.running || this.replayMode ) return;
this.rotateBarrelRelPage(evt.pageX, evt.pageY);
}
};
+config.updateOnChange('debug.enableGameLogging', Player.fn);
+
+
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);
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;
}