From 82fec55babffa889872d901b900112f9dce4d2d1 Mon Sep 17 00:00:00 2001 From: dsc Date: Sat, 6 Nov 2010 23:59:37 -0700 Subject: [PATCH] Adding bullets now. --- index.php | 10 ++- notes.md | 10 +++- src/portal/math/line.js | 14 +++-- src/portal/math/math.js | 10 +++ src/portal/math/vec.js | 11 +--- src/portal/shape.js | 1 + src/tanks/bullet/bullet.js | 5 -- src/tanks/globals.js | 10 ++-- src/tanks/thing/bullet.js | 61 ++++++++++++++++++ src/tanks/thing/tank.js | 77 ++++++++++++++++++++++ src/tanks/thing/thing.js | 152 ++++++++++++++++++++++++++++++++++++++++++++ src/tanks/unit/tank.js | 77 ---------------------- src/tanks/unit/thing.js | 152 -------------------------------------------- test/math/math.test.js | 29 +++++--- 14 files changed, 350 insertions(+), 269 deletions(-) delete mode 100644 src/tanks/bullet/bullet.js create mode 100644 src/tanks/thing/bullet.js create mode 100644 src/tanks/thing/tank.js create mode 100644 src/tanks/thing/thing.js delete mode 100644 src/tanks/unit/tank.js delete mode 100644 src/tanks/unit/thing.js diff --git a/index.php b/index.php index f22a776..40593b9 100644 --- a/index.php +++ b/index.php @@ -52,13 +52,17 @@ $scripts = array( "src/tanks/globals.js", "src/tanks/util/calc.js", - "src/tanks/util/pathmap.js", "src/tanks/util/grid.js", + "src/tanks/util/pathmap.js", + "src/tanks/game/game.js", "src/tanks/game/map.js", - "src/tanks/unit/thing.js", - "src/tanks/unit/tank.js", + + "src/tanks/thing/thing.js", + "src/tanks/thing/bullet.js", + "src/tanks/thing/tank.js", "src/tanks/game/player.js", + "src/tanks/lttl.js", "src/tanks/ui.js" diff --git a/notes.md b/notes.md index dc5d10b..b8e3471 100644 --- a/notes.md +++ b/notes.md @@ -1,2 +1,10 @@ +# Bugs + +# TODOs +- Move game objects into namespace `tanks` +- Move portal into namespace (ideas: `portal`, canvas tools... `easel`, or `ezl`) + + +# Notes - Clipping is going to suck -- TODO Replace *2 and /2 with shifts at compile-time \ No newline at end of file +- TODO Replace *2 and /2 with shifts at compile-time diff --git a/src/portal/math/line.js b/src/portal/math/line.js index 2d84eb2..8ffb956 100644 --- a/src/portal/math/line.js +++ b/src/portal/math/line.js @@ -1,10 +1,9 @@ /** - * A line. + * A line in the cartesian plane. */ math.Line = new Y.Class('Line', math.Vec, { init : function init(x1,y1, x2,y2, t){ - this.t = 1/(t || 1); this.x1 = x1; this.y1 = y1; this.p1 = new math.Vec(x1,y1); this.x2 = x2; this.y2 = y2; this.p2 = new math.Vec(x2,y2); @@ -13,10 +12,15 @@ math.Line = new Y.Class('Line', math.Vec, { , yi = this.yint = -x1*m + y1 , xi = this.xint = -y1/m + x1 ; - - this.pa = this.t*xdelta; - this.pb = this.t*ydelta math.Vec.init.call(this, xdelta, ydelta); + this.setT(t); + }, + + setT : function setT(t){ + this.t = 1/(t || 1); + this.pa = this.t*this.x; + this.pb = this.t*this.y; + return this; }, pcalc : function parametric(t){ diff --git a/src/portal/math/math.js b/src/portal/math/math.js index 072bd28..99b71fd 100644 --- a/src/portal/math/math.js +++ b/src/portal/math/math.js @@ -5,5 +5,15 @@ math = { lerp : function lerp(x, a, b) { return a + x*(b - a); + }, + + reflect : function reflect(v, line){ + var dot = math.Vec.dot + , basev = math.Vec.difference(v, line.p1); + return line.clone() + .scale(2 * dot(basev,line) / dot(line,line)) + .subtract(basev) + .add(line.p1); } + }; \ No newline at end of file diff --git a/src/portal/math/vec.js b/src/portal/math/vec.js index 7b042a8..ce387ee 100644 --- a/src/portal/math/vec.js +++ b/src/portal/math/vec.js @@ -82,16 +82,7 @@ Y.extend(math.Vec, { lerp : function lerp(x, a, b) { return new math.Vec( math.lerp(a.x, b.x, x), - math.lerp(a.y, b.y, x) ); - }, - - reflect : function reflect(v, line){ - var dot = math.Vec.dot - , basev = math.Vec.difference(v, line.p1); - return line.clone() - .scale(2 * dot(basev,line) / dot(line,line)) - .subtract(basev) - .add(line.p1); + math.lerp(a.y, b.y, x) ); } }); diff --git a/src/portal/shape.js b/src/portal/shape.js index 2b269de..1eb20bf 100644 --- a/src/portal/shape.js +++ b/src/portal/shape.js @@ -157,4 +157,5 @@ Quad = new Y.Class('Quad', Polygon, { init : function init(x1,y1, x2,y2, x3,y3){ Polygon.init.call(this, [x1,x2,x3], [y1,y2,y3]); } + }); diff --git a/src/tanks/bullet/bullet.js b/src/tanks/bullet/bullet.js deleted file mode 100644 index ce139c9..0000000 --- a/src/tanks/bullet/bullet.js +++ /dev/null @@ -1,5 +0,0 @@ -Bullet = new Y.Class('Bullet', Thing, { - - - -}); \ No newline at end of file diff --git a/src/tanks/globals.js b/src/tanks/globals.js index 7d16030..4d2d098 100644 --- a/src/tanks/globals.js +++ b/src/tanks/globals.js @@ -21,12 +21,12 @@ var undefined /// Common Components of Computation /// , SECONDTH = ELAPSED / 1000 // Amount of second completed in this tick -, SQUARETH = REF_SIZE * SECONDTH // Amount of square/second covered in this tick +, SQUARETH = REF_SIZE * SECONDTH // Amount of square/second covered in this tick -, PI = Math.PI -, HALF_PI = PI/2 -, PI_AND_HALF = PI + HALF_PI -, TWO_PI = PI*2 +, PI = Math.PI +, HALF_PI = PI/2 +, PI_AND_HALF = PI + HALF_PI +, TWO_PI = PI*2 ; diff --git a/src/tanks/thing/bullet.js b/src/tanks/thing/bullet.js new file mode 100644 index 0000000..b5bb707 --- /dev/null +++ b/src/tanks/thing/bullet.js @@ -0,0 +1,61 @@ +Bullet = new Y.Class('Bullet', Thing, { + + /* + t = time from creation + current_t = now - creation_time + */ + /** + * @param {tanks.Unit} owner + * @param {math.Line} trajectory + */ + init : function initBullet(owner, trajectory){ + Thing.init.call(this, owner.game, owner.align); + + this.owner = owner; + this.trajectory = trajectory; + this.creationTime = NOW; + + this.setLocation(trajectory.x1, trajectory.y1); + // this.trajectory.setT(this.stats.move*SQUARETH); + }, + blocking : false, + creationTime : 0, + + stats : { + move : 2.0, // move speed (squares/sec) + range : 0.1 // attack range (squares) + }, + + fillStats : function(){ + this.stats = Y({}, + Thing.fillStats(this.owner.stats), + Thing.fillStats(this.stats) ).end(); + }, + + createCooldowns : Y.op.nop, + + act : function act(){ + if (!this.dead) + this.move(); + return this; + }, + + move : function move(){ + var to = this.trajectory.pcalc( (NOW-this.creationTime) * REF_SIZE/1000 ); + this.game.moveAgentTo(this, to.x, to.y); + return this; + } + +}); + +Y(Bullet).extend({ + + fireAt : function fireAt(ProjectileType, owner, target){ + var oloc = owner.loc + , x1 = oloc.x, y1 = oloc.y + , x2 = target.x, y2 = target.y + ; + return new ProjectileType(owner, new math.Line(x1,y1, x2,y2)); + } + +}); \ No newline at end of file diff --git a/src/tanks/thing/tank.js b/src/tanks/thing/tank.js new file mode 100644 index 0000000..ff2c931 --- /dev/null +++ b/src/tanks/thing/tank.js @@ -0,0 +1,77 @@ + +Tank = new Y.Class('Tank', Thing, { + projectile : Bullet, + + // Bounding box size + width : REF_SIZE*0.7, + height : REF_SIZE*0.6, + + // Attributes + stats: { + move : 0.5, // move speed (squares/sec) + rotate : HALF_PI, // rotation speed (radians/sec) + + power : 1, // attack power + speed : 1.0, // attacks/sec + shots : 5 // max projectiles in the air at once + }, + + + init : function init(align){ + Thing.init.call(this, align); + this.bullets = new Y.YArray(); + }, + + filterTarget : function filterTarget(unit){ return unit.align !== this.align; }, + + attack : function attack(unit){ + var atk_cool = this.cooldowns.attr('attack'); + if ( atk_cool.activate(NOW) ) + this.doAttack(unit); + return this; + }, + + doAttack : function doAttack(target){ + this.fireProjectile(target); + }, + + fireProjectile : function fireProjectile(target){ + var p = Bullet.fireAt(this.projectile, this, target); + this.bullets.push(p); + this.game.addAgent(p); + return p; + }, + + + + /// Rendering Methods /// + + /** + * Sets up unit appearance for minimal updates. Called once at start, + * or when the world needs to be redrawn from scratch. + */ + render : function render( parent ){ + if (this.shape) this.shape.remove(); + + var w = this.width + , h = this.height + , tw = w / 4 + , th = h * 0.9 + , sw = w * 0.05 + ; + + this.shape = + new Rect(w,h) + .position(this.loc.x, this.loc.y) + .attr('fillStyle', '#E73075') + .append( + new Triangle(0,th, tw,th/2) + .position(w-tw-sw, (h-th)/2) + .attr('fillStyle', '#980011') + ) + .appendTo( parent ); + + return this; + } + +}); diff --git a/src/tanks/thing/thing.js b/src/tanks/thing/thing.js new file mode 100644 index 0000000..49e6252 --- /dev/null +++ b/src/tanks/thing/thing.js @@ -0,0 +1,152 @@ +Thing = new Evt.Class('Thing', { + + init : function init(align){ + this.id = Thing.THING_ID++; + this.align = align || 0; + + this.fillStats(); + this.createCooldowns(); + }, + + stats: { + move : 0.5, // move speed (squares/sec) + rotate : HALF_PI, // rotation speed (radians/sec) + + power : 1, // attack power + speed : 1.0, // attacks/sec + shots : 5 // max projectiles in the air at once + }, + + + + // *** Bookkeeping *** // + + id : 0, + align : 0, + dead : false, + + loc : null, + boundingBox : null, + + // Rotation (rads) + rotation : 0, + + // Bounding box dimensions + width : REF_SIZE*0.7, + height : REF_SIZE*0.6, + + + destroy : function destroy(){ + if (this.dead) return; + + this.dead = true; + this.fire('destroy', this); + }, + + setLocation : function setLocation(x,y){ + var loc = this.loc; + if (loc && loc.x === x && loc.y === y) + return loc; + loc = this.loc = new Loc(x,y); + if (this.shape) this.shape.position(x,y); + this.boundingBox = new Loc.Rect(x,y, x+this.width,y+this.height); + return loc; + }, + + + + + // *** Gameplay Methods *** // + + fillStats : function fillStats(){ + this.stats = Thing.fillStats(this.stats); + }, + + createCooldowns : function createCooldowns(){ + this.cooldowns = Y({ + attack: new Cooldown(1000 * this.stats.speed) + }); + }, + + /** + * Determines what the creep should do -- move, attack, etc. + */ + act : function act(){ + if (this.dead) return this; + + this.cooldowns.invoke('update', NOW); + + var bb = this.boundingBox + , x1 = Calc.rangeX(this), x2 = bb.left.x + , y1 = bb.top.y, y2 = bb.bottom.y + , units = this.game + .getUnitsAt(x1,y1, x2,y2) + .filter(this.filterTarget, this) + , target = this.closest(units) + ; + if (target) + this.attack(target); + else + this.move(); + + if (bb.x1 <= MIN_X_DIST || bb.x2 >= MAX_X_DIST) + this.destroy(); + + return this; + }, + + move : function move(){ + var to = this.loc.moveByAngle(this.rotation, this.stats.move * SQUARETH); + this.game.moveAgentTo(this, to.x, to.y); + return this; + }, + + + /// Rendering Methods /// + + /** + * Sets up unit appearance for minimal updates. Called once at start, + * or when the world needs to be redrawn from scratch. + */ + render : function render( parent ){ + return this; + }, + + draw : function draw(){ + if (this.dead) + this.shape.hide(); + else + this.shape.draw(); + }, + + toString : function toString(){ + return this.className+'(id='+this.id+', loc='+this.loc+')'; + } + +}); + +Y(Thing).extend({ + THING_ID : 0, + + fillStats : function fillStats(stats){ + var st = Y(stats) + , stats = st.clone().end() + ; + + st.forEach(function(v, k){ + var k = Y(k) + , k_ = k.rtrim('_max') + ; + + if ( k.endsWith('_max') ) { + if ( stats[k_] === undefined ) + stats[k_] = v; + + } else if ( stats[k+'_max'] === undefined ) + stats[k+'_max'] = v; + + }); + + return stats; + } +}); diff --git a/src/tanks/unit/tank.js b/src/tanks/unit/tank.js deleted file mode 100644 index 4d8b390..0000000 --- a/src/tanks/unit/tank.js +++ /dev/null @@ -1,77 +0,0 @@ - -Tank = new Y.Class('Tank', Thing, { - - // Bounding box size - width : REF_SIZE*0.7, - height : REF_SIZE*0.6, - - // Attributes - stats: { - move : 0.5, // move speed (squares/sec) - rotate : HALF_PI, // rotation speed (radians/sec) - - power : 1, // attack power - speed : 1.0, // attacks/sec - shots : 5 // max projectiles in the air at once - }, - - - init : function init(align){ - Thing.init.call(this, align); - this.bullets = new Y.YArray(); - }, - - filterTarget : function filterTarget(unit){ return unit.align !== this.align; }, - - attack : function attack(unit){ - var atk_cool = this.cooldowns.attr('attack'); - if ( atk_cool.activate(NOW) ) - this.doAttack(unit); - return this; - }, - - doAttack : function doAttack(target){ - this.fireProjectile(target); - }, - - fireProjectile : function fireProjectile(target){ - var AbilityType = this.projectile - , p = new AbilityType(this, target); - this.bullets.push(p); - this.game.addAgent(p); - return p; - }, - - - - /// Rendering Methods /// - - /** - * Sets up unit appearance for minimal updates. Called once at start, - * or when the world needs to be redrawn from scratch. - */ - render : function render( parent ){ - if (this.shape) this.shape.remove(); - - var w = this.width - , h = this.height - , tw = w / 4 - , th = h * 0.9 - , sw = w * 0.05 - ; - - this.shape = - new Rect(w,h) - .position(this.loc.x, this.loc.y) - .attr('fillStyle', '#E73075') - .append( - new Triangle(0,th, tw,th/2) - .position(w-tw-sw, (h-th)/2) - .attr('fillStyle', '#980011') - ) - .appendTo( parent ); - - return this; - } - -}); diff --git a/src/tanks/unit/thing.js b/src/tanks/unit/thing.js deleted file mode 100644 index c1e8ee7..0000000 --- a/src/tanks/unit/thing.js +++ /dev/null @@ -1,152 +0,0 @@ -Thing = new Evt.Class('Thing', { - - init : function init(align){ - this.id = Thing.THING_ID++; - this.align = align || 0; - - this.fillStats(); - this.createCooldowns(); - }, - - stats: { - move : 0.5, // move speed (squares/sec) - rotate : HALF_PI, // rotation speed (radians/sec) - - power : 1, // attack power - speed : 1.0, // attacks/sec - shots : 5 // max projectiles in the air at once - }, - - - - // *** Bookkeeping *** // - - id : 0, - align : 0, - dead : false, - - loc : null, - boundingBox : null, - - // Rotation (rads) - rotation : 0, - - // Bounding box dimensions - width : REF_SIZE*0.7, - height : REF_SIZE*0.6, - - - destroy : function destroy(){ - if (this.dead) return; - - this.dead = true; - this.fire('destroy', this); - }, - - setLocation : function setLocation(x,y){ - var loc = this.loc; - if (loc && loc.x === x && loc.y === y) - return loc; - loc = this.loc = new Loc(x,y); - if (this.shape) this.shape.position(x,y); - this.boundingBox = new Loc.Rect(x,y, x+this.width,y+this.height); - return loc; - }, - - - - - // *** Gameplay Methods *** // - - fillStats : function fillStats(){ - this.stats = Thing.fillStats(this.stats); - }, - - createCooldowns : function createCooldowns(){ - this.cooldowns = Y({ - attack: new Cooldown(1000 * this.stats.speed) - }); - }, - - /** - * Determines what the creep should do -- move, attack, etc. - */ - act : function act(){ - if (this.dead) return this; - - this.cooldowns.invoke('update', NOW); - - var bb = this.boundingBox - , x1 = Calc.rangeX(this), x2 = bb.left.x - , y1 = bb.top.y, y2 = bb.bottom.y - , units = this.game - .getUnitsAt(x1,y1, x2,y2) - .filter(this.filterTarget, this) - , target = this.closest(units) - ; - if (target) - this.attack(target); - else - this.move(); - - if (bb.x1 <= MIN_X_DIST || bb.x2 >= MAX_X_DIST) - this.destroy(); - - return this; - }, - - move : function move(){ - var toLoc = this.loc.moveByAngle(this.rotation, this.stats.move * SQUARETH); - this.game.moveAgentTo(this, toLoc.x, toLoc.y); - return this; - }, - - - /// Rendering Methods /// - - /** - * Sets up unit appearance for minimal updates. Called once at start, - * or when the world needs to be redrawn from scratch. - */ - render : function render( parent ){ - return this; - }, - - draw : function draw(){ - if (this.dead) - this.shape.hide(); - else - this.shape.draw(); - }, - - toString : function toString(){ - return this.className+'(id='+this.id+', loc='+this.loc+')'; - } - -}); - -Y(Thing).extend({ - THING_ID : 0, - - fillStats : function fillStats(stats){ - var st = Y(stats) - , stats = st.clone().end() - ; - - st.forEach(function(v, k){ - var k = Y(k) - , k_ = k.rtrim('_max') - ; - - if ( k.endsWith('_max') ) { - if ( stats[k_] === undefined ) - stats[k_] = v; - - } else if ( stats[k+'_max'] === undefined ) - stats[k+'_max'] = v; - - }); - - return stats; - } -}); diff --git a/test/math/math.test.js b/test/math/math.test.js index 2dc338d..db23df8 100644 --- a/test/math/math.test.js +++ b/test/math/math.test.js @@ -31,7 +31,7 @@ ctx.translate(w2, h2); ctx.scale(PPU, PPU); points = Y([]); -line = mkLine(-3,2, 4,7); +line = mkLine(5,8, 4,7); addPoint(-5,2); dragging = false; @@ -47,7 +47,7 @@ P.layer.bind('click', function(evt){ function convertLoc(evt){ var off = P.layer.offset() - , x = (evt.pageX - off.left - w2)/(PPU) + , x = (evt.pageX - off.left - w2)/PPU , y = (evt.pageY - off.top - h2)/PPU * -1; return new math.Vec(x,y); } @@ -93,7 +93,7 @@ function addPoint(x,y){ , c = drawPoint(v, null, '#552F74'); } - var rv = math.Vec.reflect(c.vec,line) + var rv = math.reflect(c.vec,line) , rc = c.reflected = drawPoint(rv, null, '#F25522'); points.push(c); } @@ -102,7 +102,7 @@ function mkLine(x1,y1, x2,y2, color, pcolor){ if (window.p1) p1.remove(); if (window.p2) p2.remove(); - color = color || 'rgba(231,48,117, 0.5)'; + color = color || 'rgba(231,48,117, 0.5)'; pcolor = pcolor || 'rgba(69,150,255, 1)'; var line = new math.Line(x1,y1, x2,y2, 10); drawLine(-W/2, line.calcY(-W2), line.calcX(H2), H2, color); @@ -135,13 +135,20 @@ function mkLine(x1,y1, x2,y2, color, pcolor){ } function drawLine(x1,y1, x2,y2, color, width){ - ctx.beginPath(); - ctx.lineWidth = width || PX; - ctx.strokeStyle = color || '#000000'; - ctx.moveTo(x1, -y1); - ctx.lineTo(x2, -y2); - ctx.stroke(); - ctx.closePath(); + if ([x1,y1, x2,y2].some(isNaN)) { + throw new Error('Value is NaN!'+'('+x1+','+y1+') ('+x2+','+y2+')'); + } + try { + ctx.beginPath(); + ctx.lineWidth = width || PX; + ctx.strokeStyle = color || '#000000'; + ctx.moveTo(x1, -y1); + ctx.lineTo(x2, -y2); + ctx.stroke(); + ctx.closePath(); + } catch (e) { + console.log('drawLine error:', e, '(',x1,y1,')', '(',x2,y2,')'); + } } function drawPoint(x,y, color, r){ -- 1.7.0.4