From: dsc Date: Thu, 18 Nov 2010 23:49:37 +0000 (-0800) Subject: Adds tank AI! X-Git-Url: http://git.less.ly:3516/?a=commitdiff_plain;h=f66ab3325058c405fa5479f0a9a5fa76e524deb4;p=tanks.git Adds tank AI! --- diff --git a/css/lttl.css b/css/lttl.css index fb3d778..72ee86a 100644 --- a/css/lttl.css +++ b/css/lttl.css @@ -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; } diff --git a/index.php b/index.php index 9499a94..27ec53b 100644 --- a/index.php +++ b/index.php @@ -6,12 +6,20 @@ -

The Littlest Battletank

+

The Littlest Battletank

+

config

-
+
diff --git a/notes.md b/notes.md index 04afe34..9b04c89 100644 --- 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 diff --git a/src/Y/alias.js b/src/Y/alias.js index a36af68..01249a8 100644 --- a/src/Y/alias.js +++ b/src/Y/alias.js @@ -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 diff --git a/src/Y/core.js b/src/Y/core.js index 3718818..0ef4ca1 100644 --- a/src/Y/core.js +++ b/src/Y/core.js @@ -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) ) diff --git a/src/Y/type.js b/src/Y/type.js index 7735fdf..a4be9df 100644 --- a/src/Y/type.js +++ b/src/Y/type.js @@ -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); + } +} diff --git a/src/Y/y-core.js b/src/Y/y-core.js index a7abd34..2abb48b 100644 --- a/src/Y/y-core.js +++ b/src/Y/y-core.js @@ -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 ); diff --git a/src/Y/y-function.js b/src/Y/y-function.js index dde3287..fe1b668 100644 --- a/src/Y/y-function.js +++ b/src/Y/y-function.js @@ -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); }); diff --git a/src/Y/y-op.js b/src/Y/y-op.js index ca5064d..ab272b8 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, - 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; }, {}); diff --git a/src/tanks/game/game.js b/src/tanks/game/game.js index 5b7acfe..09a6a6a 100644 --- a/src/tanks/game/game.js +++ b/src/tanks/game/game.js @@ -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(); diff --git a/src/tanks/main.js b/src/tanks/main.js index ae192e0..00c78e9 100644 --- a/src/tanks/main.js +++ b/src/tanks/main.js @@ -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 ); diff --git a/src/tanks/map/trajectory.js b/src/tanks/map/trajectory.js index d5433ae..344fe1b 100644 --- a/src/tanks/map/trajectory.js +++ b/src/tanks/map/trajectory.js @@ -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; }, diff --git a/src/tanks/thing/bullet.js b/src/tanks/thing/bullet.js index 5a05fc9..4e19d45 100644 --- a/src/tanks/thing/bullet.js +++ b/src/tanks/thing/bullet.js @@ -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); diff --git a/src/tanks/ui/player.js b/src/tanks/thing/player.js similarity index 59% rename from src/tanks/ui/player.js rename to src/tanks/thing/player.js index 7bb3b03..c684716 100644 --- a/src/tanks/ui/player.js +++ b/src/tanks/thing/player.js @@ -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) + shots : 5 // max projectiles in the air at once + }, - init : function init(game, tank){ - Y.bindAll(this); + init : function init(align){ + Tank.init.call(this, align); this.activeKeys = new Y.YArray(); this.queue = []; - this.tank = tank; - this.game = game; - this.pathmap = game.pathmap; - tank.act = this.act; // Override tank actions with player control - tank.move = this.move; + // tank.act = this.act; // Override tank actions with player control + // tank.move = this.move; $(window) - .bind('keydown', this.keydown) - .bind('keyup', this.keyup) - .bind('mousedown', this.mousedown) - .bind('mouseup', this.mouseup) - .bind('mousemove', this.mousemove) + .bind('keydown', this.keydown.bind(this)) + .bind('keyup', this.keyup.bind(this)) + .bind('mousedown', this.mousedown.bind(this)) + .bind('mouseup', this.mouseup.bind(this)) + .bind('mousemove', this.mousemove.bind(this)) ; }, + activeKeys : null, + shift : false, ctrl : false, meta : false, alt : false, + leftMouse : false, middleMouse : false, rightMouse : false, + + queue : null, + + act : function act(){ - if (this.tank.dead) - return this.tank; - - this.tank.cooldowns.invoke('update', NOW); + if (this.dead) + return this; var action = this.queue.shift(); - if (action && action.type === 'fire') { - this.tank.shoot(action.x, action.y); - } else if ( this.activeKeys.size() ) + if (action && action.type === 'fire') + this.shoot(action.x, action.y); + + else if ( this.activeKeys.size() ) this.move(); - return this.tank; + return this; }, - fire : function fire(){ - var WIGGLE = 4 - , tank = this.tank - , cooldown = tank.cooldowns.attr('attack') - ; + attack : function attack(){ + var WIGGLE = 4; - if ( !cooldown.activate(NOW) ) + if ( !this.cooldowns.attack.ready ) return; - var barrel = tank.barrel - , bb = tank.boundingBox + var barrel = this.barrel + , bb = this.boundingBox , w2 = bb.width/2, h2 = bb.height/2 , x0 = bb.x1+w2, y0 = bb.y1+h2 @@ -87,7 +75,7 @@ Player = new Y.Class('Player', { , x1 = x0 + w2*cos, y1 = y0 + h2*sin , sz = (barrel.boundingBox.width - w2)/2 + WIGGLE - , blockers = this.pathmap.get(x1-sz,y1-sz, x1+sz,y1+sz).remove(tank) + , blockers = this.game.pathmap.get(x1-sz,y1-sz, x1+sz,y1+sz).remove(this) ; if ( blockers.size() ) return; // console.log('squelch!', blockers); @@ -107,17 +95,16 @@ Player = new Y.Class('Player', { if (!dir) return; - var tank = this.tank - , toLoc = tank.loc.moveByDir(dir, (tank.stats.move * SQUARETH)) + var toLoc = this.loc.moveByDir(dir, (this.stats.move * SQUARETH)) , x = toLoc.x, y = toLoc.y - , bb = tank.boundingBox.add(x,y) + , bb = this.boundingBox.add(x,y) - , blockers = this.pathmap.get(bb.x1,bb.y1, bb.x2,bb.y2).remove(tank) + , blockers = this.game.pathmap.get(bb.x1,bb.y1, bb.x2,bb.y2).remove(this) ; if ( !blockers.size() ) - this.game.moveAgentTo(tank, x,y); + this.game.moveAgentTo(this, x,y); }, @@ -131,10 +118,12 @@ Player = new Y.Class('Player', { }, keydown : function keydown(evt){ + if ( !this.game.loop.running ) return; + this.updateMeta(evt); if (evt.which === Key.fire) { - this.fire(); + this.attack(); return; } @@ -148,14 +137,18 @@ Player = new Y.Class('Player', { }, keyup : function keyup(evt){ + // if ( !this.game.loop.running ) return; + var dir = Key.getDir(evt.which); if (dir) this.activeKeys.remove(dir); this.updateMeta(evt); }, mousedown : function mousedown(evt){ + if ( !this.game.loop.running ) return; + switch (evt.which) { - case 1: evt.leftMouse = true; this.fire(); break; + case 1: evt.leftMouse = true; this.attack(); break; case 2: evt.rightMouse = true; break; case 3: evt.middleMouse = true; break; } @@ -163,6 +156,8 @@ Player = new Y.Class('Player', { }, mouseup : function mouseup(evt){ + // if ( !this.game.loop.running ) return; + switch (evt.which) { case 1: evt.leftMouse = false; break; case 2: evt.rightMouse = false; break; @@ -172,16 +167,36 @@ Player = new Y.Class('Player', { }, mousemove : function mousemove(evt){ - var shape = this.tank.shape - , cannon = this.tank.cannon - , barrel = this.tank.barrel - , off = shape.offset() - , w = shape.width(), h = shape.height() - , x = off.left + w/2 - evt.pageX - , y = off.top + h/2 - evt.pageY - , theta = Math.atan2(-y,-x); - - barrel.rotate(theta); + if ( !this.game.loop.running ) return; + + this.rotateBarrelRelPage(evt.pageX, evt.pageY); } -}); \ No newline at end of file +}); + + +var 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+''] ]; + } +}; + + + +})(); diff --git a/src/tanks/thing/tank.js b/src/tanks/thing/tank.js index ea3666a..064ff18 100644 --- a/src/tanks/thing/tank.js +++ b/src/tanks/thing/tank.js @@ -3,6 +3,11 @@ Tank = Thing.subclass('Tank', { projectile : Bullet, blocking : true, + bodyColor : '#83BB32', + turretColor : '#1C625B', + barrelColor : '#D43B24', + + // Bounding box size width : REF_SIZE*0.55, height : REF_SIZE*0.55, @@ -11,11 +16,11 @@ Tank = Thing.subclass('Tank', { stats: { hp : 1, // health - move : 1.0, // move speed (squares/sec) + move : 0.75, // move speed (squares/sec) rotate : HALF_PI, // rotation speed (radians/sec) power : 1, // attack power - speed : 0.5, // attack cool (sec) + speed : 0.75, // attack cool (sec) shots : 5 // max projectiles in the air at once }, @@ -27,20 +32,54 @@ Tank = Thing.subclass('Tank', { this.onBulletDeath = this.onBulletDeath.bind(this); }, - attack : function attack(unit){ - var atk_cool = this.cooldowns.attr('attack'); - if ( atk_cool.activate(NOW) ) - this.doAttack(unit); - return this; + cannonReady : function cannonReady(){ + return this.nShots < this.stats.shots && this.cooldowns.attack.ready; }, - doAttack : function doAttack(x,y){ - this.shoot(x,y); - return this; + act : function act(){ + // Are we ready to fire? + if ( this.cannonReady() ) { + // Try to blow up nearby tanks + var t = this.nearLike(66, 'Y.is(Tank, _) && _.align !== '+this.align).shift(); + if (t) { + // console.log('I gotcha!', t); + this.shoot(t.loc.x, t.loc.y); + return this; + } + + // Try to shoot down nearby bullets + var b = this.nearLike(15, Y.is(Bullet)).shift(); + if (b) { + // console.log('Incoming! Shoot it down!', b); + this.shoot(b.loc.x, b.loc.y); + return this; + } + + } + + // Nothing to shoot at? Move toward something + var t = this.nearLike(10000, 'Y.is(Tank, _) && _.align !== '+this.align).shift(); + if (t) { + this.move(t.loc.x, t.loc.y); + return this; + } + }, + + nearLike : function nearLike(ticks, fn){ + fn = fn.toFunction(); + var bMovePerTick = MS_PER_FRAME * Bullet.prototype.stats.move*REF_SIZE/1000 + , within = bMovePerTick*ticks + , bb = this.boundingBox + , x1 = bb.x1 - within, y1 = bb.y1 - within + , x2 = bb.x2 + within, y2 = bb.y2 + within + ; + return this.game.pathmap.get(x1,y1, x2,y2).filter(fn || Y.op.K(true)); }, shoot : function shoot(x,y){ - if (this.nShots >= this.stats.shots) + this.rotateBarrel(x,y); + + if ( this.nShots >= this.stats.shots || !this.cooldowns.attack.activate(NOW) ) return null; var ProjectileType = this.projectile @@ -61,9 +100,9 @@ Tank = Thing.subclass('Tank', { var loc = this.loc , barrel = this.barrel, bb = barrel.boundingBox , theta = barrel.transform.rotate, sin = Math.sin(theta), cos = Math.cos(theta) - , x0 = bb.x2-bb.x1, y0 = (bb.y2-bb.y1)/2 - , x = loc.x + bb.x1 + x0*cos - y0*sin + 3 - , y = loc.y + bb.y1 + x0*sin + y0*cos + 3 + , x0 = 3+bb.x2-bb.x1, y0 = (bb.y2-bb.y1)/2 + , x = loc.x + bb.x1 + x0*cos - y0*sin + , y = loc.y + bb.y1 + x0*sin + y0*cos ; // console.log('getTurretLoc()', 'loc:', loc, 'bb.(x2,y2):', [bb.x2,bb.y2], '(x,y):', [x,y]); return new math.Vec(x,y); @@ -91,23 +130,56 @@ Tank = Thing.subclass('Tank', { this.shape = new Rect(w,h) .position(this.loc.x, this.loc.y) - .fill('#E73075') + .fill(this.bodyColor) .appendTo( parent ); - this.cannon = new Circle(r, true) + this.turret = new Circle(r, true) .position(w2-r, h2-r) - .fill('#A72F5B') + .fill(this.turretColor) .appendTo( this.shape ) ; this.barrel = new Rect(cw,ch) .position(w2-2, h2-ch/2) .origin(2, ch/2) - .fill('#2E62C9') + .fill(this.barrelColor) .appendTo( this.shape ) ; return this; + }, + + colors : function colors(bodyColor, turretColor, barrelColor){ + var names = Y('bodyColor', 'turretColor', 'barrelColor'); + + if (arguments.length === 0) + return names.generate( Y.op.get(this) ); + + if (bodyColor) this.bodyColor = bodyColor; + if (turretColor) this.turretColor = turretColor; + if (barrelColor) this.barrelColor = barrelColor; + return this; + }, + + rotateBarrel : function rotateBarrel(x,y){ + var bb = this.boundingBox + , w = this.width, h = this.height + , x0 = x - bb.x1 - w/2 + , y0 = y - bb.y1 - h/2 + , theta = Math.atan2(y0,x0) + ; + this.barrel.rotate(theta); + }, + + rotateBarrelRelPage : function rotateBarrelRelPage(pageX, pageY){ + var shape = this.shape + , off = shape.offset() + , w = this.width, h = this.height + , x = off.left + w/2 - pageX + , y = off.top + h/2 - pageY + , theta = Math.atan2(-y,-x) + ; + this.barrel.rotate(theta); } }); diff --git a/src/tanks/thing/thing.js b/src/tanks/thing/thing.js index c8482e0..bbc6218 100644 --- a/src/tanks/thing/thing.js +++ b/src/tanks/thing/thing.js @@ -42,6 +42,9 @@ Thing = new Evt.Class('Thing', { width : REF_SIZE*0.7, height : REF_SIZE*0.6, + set : Y.op.set.methodize(), + attr : Y.op.attr.methodize(), + dealDamage : function dealDamage(d, source){ this.stats.hp -= d; @@ -93,9 +96,10 @@ Thing = new Evt.Class('Thing', { }, createCooldowns : function createCooldowns(){ - this.cooldowns = Y({ + var cs = this.cooldowns = { attack: new Cooldown(1000 * this.stats.speed) - }); + }; + this._cooldowns = Y(cs); }, /** @@ -108,6 +112,11 @@ Thing = new Evt.Class('Thing', { // *** Gameplay Methods *** // + updateCooldowns : function updateCooldowns(){ + this._cooldowns.invoke('update', NOW); + return this; + }, + /** * Determines what the creep should do -- move, attack, etc. */ @@ -115,9 +124,19 @@ Thing = new Evt.Class('Thing', { return this; }, - move : function move(){ - var to = this.loc.moveByAngle(this.rotation, this.stats.move * SQUARETH); - this.game.moveAgentTo(this, to.x, to.y); + move : function move(x,y){ + return this.moveByAngle(Math.atan2(-y,-x)); + }, + + moveByAngle : function moveByAngle(theta){ + var to = this.loc.moveByAngle(theta, this.stats.move * SQUARETH) + , bb = this.boundingBox.add(to.x,to.y) + , blockers = this.game.pathmap.get(bb.x1,bb.y1, bb.x2,bb.y2).remove(this) + ; + + if ( !blockers.size() ) + this.game.moveAgentTo(this, to.x, to.y); + return this; }, diff --git a/src/tanks/util/utils.js b/src/tanks/util/utils.js new file mode 100644 index 0000000..9399beb --- /dev/null +++ b/src/tanks/util/utils.js @@ -0,0 +1,7 @@ +Object.dump = function(o){ + var out = []; + for (var k in o) + out.push(k+': '+o[k]); + return '{ '+out.join(', ')+' }'; +}; + diff --git a/tanks.php b/tanks.php index 1ae6e32..ff16226 100644 --- a/tanks.php +++ b/tanks.php @@ -24,6 +24,7 @@ class Tanks { "src/tanks/thing/thing.js", "src/tanks/thing/bullet.js", "src/tanks/thing/tank.js", + "src/tanks/thing/player.js", "src/tanks/map/loc.js", "src/tanks/map/trajectory.js", @@ -31,7 +32,6 @@ class Tanks { "src/tanks/map/pathmap.js", "src/tanks/ui/grid.js", - "src/tanks/ui/player.js", "src/tanks/game/game.js" );