From f48cd896f2ce6313c1f968b237b38d4abcc98fee Mon Sep 17 00:00:00 2001 From: landgreen Date: Sat, 21 Dec 2019 10:01:38 -0800 Subject: [PATCH] explosions balance, added pulse gun, harmonic field balance --- js/bullets.js | 1152 +++++++++++++++++++++++++++++-------------------- js/level.js | 9 +- js/mobs.js | 18 +- js/player.js | 43 +- js/spawn.js | 4 +- 5 files changed, 718 insertions(+), 508 deletions(-) diff --git a/js/bullets.js b/js/bullets.js index eae6938..145993e 100644 --- a/js/bullets.js +++ b/js/bullets.js @@ -14,7 +14,7 @@ const b = { isModBulletsLastLonger: null, modIsImmortal: null, modSpores: null, - isModTempResist: null, + isModImmuneExplosion: null, isModDroneOnDamage: null, modExtraDmg: null, annihilation: null, @@ -30,7 +30,7 @@ const b = { b.modCount = 0; b.modFireRate = 1; b.modExplosionRadius = 1; - b.isModTempResist = false; + b.isModImmuneExplosion = false; b.modBulletSize = 1; b.isModDroneOnDamage = false; b.modEnergySiphon = 0; @@ -70,7 +70,7 @@ const b = { }, { name: "fluoroantimonic acid", - description: "each bullet does extra chemical damage
instant damage, unaffected by momentum", + description: "each bullet does extra chemical damage
instant damage, unaffected by momentum", have: false, //1 effect: () => { //good with guns that fire many bullets at low speeds, minigun, drones, junk-bots, shotgun, superballs, wavebeam b.modExtraDmg = 0.1 @@ -150,21 +150,21 @@ const b = { }, { name: "anti-matter cores", - description: "the radius of your explosions is doubled
be careful", + description: "your explosions are doubled in size
immune to damage from explosions", have: false, //11 - effect: () => { //at 1.4 gives a flat 40% increase, and increased range, balanced by limited guns and self damage - //testing at 1.3: grenade(+0.3), missiles, flak, M80 - b.modExplosionRadius = 1.8; //good for guns with explosions - } - }, - { - name: "ceramic plating", - description: "protection from to high temperatures
5x less damage from explosions and lasers", - have: false, //12 effect: () => { - b.isModTempResist = true; //good for guns with explosions + b.modExplosionRadius = 1.5; + b.isModImmuneExplosion = true; } }, + // { + // name: "ceramic plating", + // description: "protection from to high temperatures
5x less damage from explosions and lasers", + // have: false, //12 + // effect: () => { + // b.isModImmuneExplosion = true; //good for guns with explosions + // } + // }, { name: "entanglement", description: "using your first gun reduces damage taken
scales by 7% for each gun in your inventory", @@ -407,6 +407,99 @@ const b = { } } }, + explosion(where, radius) { + radius *= b.modExplosionRadius + // typically explode is used for some bullets with .onEnd + //add dmg to draw queue + game.drawList.push({ + x: where.x, + y: where.y, + radius: radius, + color: "rgba(255,25,0,0.6)", + time: game.drawTime + }); + let dist, sub, knock; + const dmg = b.dmgScale * radius * 0.009; + + const alertRange = 100 + radius * 2; //alert range + //add alert to draw queue + game.drawList.push({ + x: where.x, + y: where.y, + radius: alertRange, + color: "rgba(100,20,0,0.03)", + time: game.drawTime + }); + + //player damage and knock back + sub = Matter.Vector.sub(where, player.position); + dist = Matter.Vector.magnitude(sub); + if (dist < radius) { + if (!b.isModImmuneExplosion) mech.damage(radius * 0.0002); + knock = Matter.Vector.mult(Matter.Vector.normalise(sub), -Math.sqrt(dmg) * player.mass / 30); + player.force.x += knock.x; + player.force.y += knock.y; + mech.drop(); + } else if (dist < alertRange) { + knock = Matter.Vector.mult(Matter.Vector.normalise(sub), -Math.sqrt(dmg) * player.mass / 55); + player.force.x += knock.x; + player.force.y += knock.y; + mech.drop(); + } + + //body knock backs + for (let i = 0, len = body.length; i < len; ++i) { + sub = Matter.Vector.sub(where, body[i].position); + dist = Matter.Vector.magnitude(sub); + if (dist < radius) { + knock = Matter.Vector.mult(Matter.Vector.normalise(sub), (-Math.sqrt(dmg) * body[i].mass) / 18); + body[i].force.x += knock.x; + body[i].force.y += knock.y; + } else if (dist < alertRange) { + knock = Matter.Vector.mult(Matter.Vector.normalise(sub), (-Math.sqrt(dmg) * body[i].mass) / 40); + body[i].force.x += knock.x; + body[i].force.y += knock.y; + } + } + + //power up knock backs + for (let i = 0, len = powerUp.length; i < len; ++i) { + sub = Matter.Vector.sub(where, powerUp[i].position); + dist = Matter.Vector.magnitude(sub); + if (dist < radius) { + knock = Matter.Vector.mult(Matter.Vector.normalise(sub), (-Math.sqrt(dmg) * powerUp[i].mass) / 26); + powerUp[i].force.x += knock.x; + powerUp[i].force.y += knock.y; + } else if (dist < alertRange) { + knock = Matter.Vector.mult(Matter.Vector.normalise(sub), (-Math.sqrt(dmg) * powerUp[i].mass) / 40); + powerUp[i].force.x += knock.x; + powerUp[i].force.y += knock.y; + } + } + + //mob damage and knock back with alert + let damageScale = 1.5; // reduce dmg for each new target to limit total AOE damage + for (let i = 0, len = mob.length; i < len; ++i) { + if (mob[i].alive) { + sub = Matter.Vector.sub(where, mob[i].position); + dist = Matter.Vector.magnitude(sub) - mob[i].radius; + if (dist < radius) { + mob[i].damage(dmg * damageScale); + mob[i].locatePlayer(); + knock = Matter.Vector.mult(Matter.Vector.normalise(sub), (-Math.sqrt(dmg * damageScale) * mob[i].mass) / 18); + mob[i].force.x += knock.x; + mob[i].force.y += knock.y; + radius *= 0.9 //reduced range for each additional explosion target + damageScale *= 0.75 //reduced damage for each additional explosion target + } else if (!mob[i].seePlayer.recall && dist < alertRange) { + mob[i].locatePlayer(); + knock = Matter.Vector.mult(Matter.Vector.normalise(sub), (-Math.sqrt(dmg * damageScale) * mob[i].mass) / 35); + mob[i].force.x += knock.x; + mob[i].force.y += knock.y; + } + } + } + }, explode(me) { // typically explode is used for some bullets with .onEnd let radius = bullet[me].explodeRad * b.modExplosionRadius @@ -435,11 +528,7 @@ const b = { sub = Matter.Vector.sub(bullet[me].position, player.position); dist = Matter.Vector.magnitude(sub); if (dist < radius) { - if (b.isModTempResist) { - mech.damage(radius * 0.00004); - } else { - mech.damage(radius * 0.0002); - } + if (!b.isModImmuneExplosion) mech.damage(radius * 0.0002); knock = Matter.Vector.mult(Matter.Vector.normalise(sub), -Math.sqrt(dmg) * player.mass / 30); player.force.x += knock.x; player.force.y += knock.y; @@ -482,7 +571,7 @@ const b = { } //mob damage and knock back with alert - let damageScale = 1; // reduce dmg for each new target to limit total AOE damage + let damageScale = 1.5; // reduce dmg for each new target to limit total AOE damage for (let i = 0, len = mob.length; i < len; ++i) { if (mob[i].alive) { sub = Matter.Vector.sub(bullet[me].position, mob[i].position); @@ -494,7 +583,7 @@ const b = { mob[i].force.x += knock.x; mob[i].force.y += knock.y; radius *= 0.9 //reduced range for each additional explosion target - damageScale *= 0.9 //reduced damage for each additional explosion target + damageScale *= 0.75 //reduced damage for each additional explosion target } else if (!mob[i].seePlayer.recall && dist < alertRange) { mob[i].locatePlayer(); knock = Matter.Vector.mult(Matter.Vector.normalise(sub), (-Math.sqrt(dmg * damageScale) * mob[i].mass) / 35); @@ -567,170 +656,290 @@ const b = { }); World.add(engine.world, bullet[bIndex]); //add bullet to world }, - guns: [{ - name: "laser", //0 - description: "emit a beam of collimation coherent light
uses energy instead of ammunition", - ammo: 0, - // ammoPack: 350, - ammoPack: Infinity, - have: false, - isStarterGun: true, - fire() { - // mech.fireCDcycle = mech.cycle + 1 - //laser drains energy as well as bullets - const FIELD_DRAIN = 0.002 - const damage = 0.05 - if (mech.fieldMeter < FIELD_DRAIN) { - mech.fireCDcycle = mech.cycle + 100; // cool down if out of energy + drone(speed = 1) { + const me = bullet.length; + const THRUST = 0.0015 + const dir = mech.angle + 0.2 * (Math.random() - 0.5); + const RADIUS = (4.5 + 3 * Math.random()) * b.modBulletSize + bullet[me] = Bodies.circle(mech.pos.x + 30 * Math.cos(mech.angle), mech.pos.y + 30 * Math.sin(mech.angle), RADIUS, { + angle: dir, + inertia: Infinity, + friction: 0.05, + frictionAir: 0.0005, + restitution: 1, + dmg: 0.13 + b.modExtraDmg, //damage done in addition to the damage from momentum + lookFrequency: 83 + Math.floor(41 * Math.random()), + endCycle: game.cycle + Math.floor((1080 + 360 * Math.random()) * b.isModBulletsLastLonger), + classType: "bullet", + collisionFilter: { + category: cat.bullet, + mask: cat.map | cat.body | cat.bullet | cat.mob | cat.mobBullet | cat.mobShield //self collide + }, + minDmgSpeed: 0, + lockedOn: null, + isFollowMouse: true, + onDmg() { + this.lockedOn = null + if (this.endCycle > game.cycle + 180) { + this.endCycle -= 60 + if (game.cycle + 180 > this.endCycle) this.endCycle = game.cycle + 180 + } + }, + onEnd() {}, + do() { + if (game.cycle + 180 > this.endCycle) { //fall and die + this.force.y += this.mass * 0.0012; + this.restitution = 0.2; } else { - mech.fieldMeter -= mech.fieldRegen + FIELD_DRAIN - let best; - const color = "#f00"; - const range = 3000; - const path = [{ - x: mech.pos.x + 20 * Math.cos(mech.angle), - y: mech.pos.y + 20 * Math.sin(mech.angle) - }, - { - x: mech.pos.x + range * Math.cos(mech.angle), - y: mech.pos.y + range * Math.sin(mech.angle) + this.force.y += this.mass * 0.0002; + //find mob targets + if (!(game.cycle % this.lookFrequency)) { + this.lockedOn = null; + let closeDist = Infinity; + for (let i = 0, len = mob.length; i < len; ++i) { + if ( + Matter.Query.ray(map, this.position, mob[i].position).length === 0 && + Matter.Query.ray(body, this.position, mob[i].position).length === 0 + ) { + const TARGET_VECTOR = Matter.Vector.sub(this.position, mob[i].position) + const DIST = Matter.Vector.magnitude(TARGET_VECTOR); + if (DIST < closeDist) { + closeDist = DIST; + this.lockedOn = mob[i] + } + } } - ]; - const vertexCollision = function (v1, v1End, domain) { - for (let i = 0; i < domain.length; ++i) { - let vertices = domain[i].vertices; - const len = vertices.length - 1; - for (let j = 0; j < len; j++) { - results = game.checkLineIntersection(v1, v1End, vertices[j], vertices[j + 1]); - if (results.onLine1 && results.onLine2) { - const dx = v1.x - results.x; - const dy = v1.y - results.y; - const dist2 = dx * dx + dy * dy; - if (dist2 < best.dist2 && (!domain[i].mob || domain[i].alive)) { - best = { - x: results.x, - y: results.y, - dist2: dist2, - who: domain[i], - v1: vertices[j], - v2: vertices[j + 1] - }; + if (!this.lockedOn) { + //grab a power up if it is (ammo) or (a heal when player is low) + let closeDist = Infinity; + for (let i = 0, len = powerUp.length; i < len; ++i) { + if ( + ((powerUp[i].name !== "field" && powerUp[i].name !== "heal") || (powerUp[i].name === "heal" && mech.health < 0.8)) && + Matter.Query.ray(map, this.position, powerUp[i].position).length === 0 && + Matter.Query.ray(body, this.position, powerUp[i].position).length === 0 + ) { + const TARGET_VECTOR = Matter.Vector.sub(this.position, powerUp[i].position) + const DIST = Matter.Vector.magnitude(TARGET_VECTOR); + if (DIST < closeDist) { + if (DIST < 50) { //eat the power up if close enough + powerUp[i].effect(); + Matter.World.remove(engine.world, powerUp[i]); + powerUp.splice(i, 1); + break; + } + closeDist = DIST; + this.lockedOn = powerUp[i] } } } - results = game.checkLineIntersection(v1, v1End, vertices[0], vertices[len]); - if (results.onLine1 && results.onLine2) { - const dx = v1.x - results.x; - const dy = v1.y - results.y; - const dist2 = dx * dx + dy * dy; - if (dist2 < best.dist2 && (!domain[i].mob || domain[i].alive)) { - best = { - x: results.x, - y: results.y, - dist2: dist2, - who: domain[i], - v1: vertices[0], - v2: vertices[len] - }; - } - } - } - }; - const checkForCollisions = function () { - best = { - x: null, - y: null, - dist2: Infinity, - who: null, - v1: null, - v2: null - }; - vertexCollision(path[path.length - 2], path[path.length - 1], mob); - vertexCollision(path[path.length - 2], path[path.length - 1], map); - vertexCollision(path[path.length - 2], path[path.length - 1], body); - }; - const laserHitMob = function (dmg) { - if (best.who.alive) { - dmg *= b.dmgScale * damage; - best.who.damage(dmg); - best.who.locatePlayer(); - //draw mob damage circle - ctx.fillStyle = color; - ctx.beginPath(); - ctx.arc(path[path.length - 1].x, path[path.length - 1].y, Math.sqrt(dmg) * 100, 0, 2 * Math.PI); - ctx.fill(); - } - }; - - const reflection = function () { - // https://math.stackexchange.com/questions/13261/how-to-get-a-reflection-vector - const n = Matter.Vector.perp(Matter.Vector.normalise(Matter.Vector.sub(best.v1, best.v2))); - const d = Matter.Vector.sub(path[path.length - 1], path[path.length - 2]); - const nn = Matter.Vector.mult(n, 2 * Matter.Vector.dot(d, n)); - const r = Matter.Vector.normalise(Matter.Vector.sub(d, nn)); - path[path.length] = Matter.Vector.add(Matter.Vector.mult(r, range), path[path.length - 1]); - }; - //beam before reflection - checkForCollisions(); - if (best.dist2 != Infinity) { - //if hitting something - path[path.length - 1] = { - x: best.x, - y: best.y - }; - laserHitMob(1); - - //1st reflection beam - reflection(); - //ugly bug fix: this stops the reflection on a bug where the beam gets trapped inside a body - let who = best.who; - checkForCollisions(); - if (best.dist2 != Infinity) { - //if hitting something - path[path.length - 1] = { - x: best.x, - y: best.y - }; - laserHitMob(0.75); - - //2nd reflection beam - //ugly bug fix: this stops the reflection on a bug where the beam gets trapped inside a body - if (who !== best.who) { - reflection(); - checkForCollisions(); - if (best.dist2 != Infinity) { - //if hitting something - path[path.length - 1] = { - x: best.x, - y: best.y - }; - laserHitMob(0.5); - } - } } } - ctx.fillStyle = color; - ctx.strokeStyle = color; - ctx.lineWidth = 2; - ctx.lineDashOffset = 300 * Math.random() - // ctx.setLineDash([200 * Math.random(), 250 * Math.random()]); - - ctx.setLineDash([50 + 120 * Math.random(), 50 * Math.random()]); - for (let i = 1, len = path.length; i < len; ++i) { - ctx.beginPath(); - ctx.moveTo(path[i - 1].x, path[i - 1].y); - ctx.lineTo(path[i].x, path[i].y); - ctx.stroke(); - ctx.globalAlpha *= 0.5; //reflections are less intense - // ctx.globalAlpha -= 0.1; //reflections are less intense + if (this.lockedOn) { //accelerate towards mobs + this.force = Matter.Vector.mult(Matter.Vector.normalise(Matter.Vector.sub(this.position, this.lockedOn.position)), -this.mass * THRUST) + } else { //accelerate towards mouse + this.force = Matter.Vector.mult(Matter.Vector.normalise(Matter.Vector.sub(this.position, game.mouseInGame)), -this.mass * THRUST) + } + // speed cap instead of friction to give more agility + if (this.speed > 6) { + Matter.Body.setVelocity(this, { + x: this.velocity.x * 0.97, + y: this.velocity.y * 0.97 + }); + } + } + } + }) + World.add(engine.world, bullet[me]); //add bullet to world + Matter.Body.setVelocity(bullet[me], { + x: speed * Math.cos(dir), + y: speed * Math.sin(dir) + }); + }, + guns: [{ + name: "minigun", //0 + description: "rapidly fire a stream of small bullets", + ammo: 0, + ammoPack: 115, + have: false, + isStarterGun: true, + fire() { + const me = bullet.length; + b.muzzleFlash(15); + // if (Math.random() > 0.2) mobs.alert(500); + const dir = mech.angle + (Math.random() - 0.5) * ((mech.crouch) ? 0.03 : 0.14); + bullet[me] = Bodies.rectangle(mech.pos.x + 30 * Math.cos(mech.angle), mech.pos.y + 30 * Math.sin(mech.angle), 17 * b.modBulletSize, 5 * b.modBulletSize, b.fireAttributes(dir)); + b.fireProps(mech.crouch ? 11 : 5, mech.crouch ? 44 : 36, dir, me); //cd , speed + bullet[me].endCycle = game.cycle + Math.floor(65 * b.isModBulletsLastLonger); + bullet[me].frictionAir = mech.crouch ? 0.007 : 0.01; + bullet[me].do = function () { + this.force.y += this.mass * 0.0005; + }; + } + }, + { + name: "shotgun", //1 + description: "fire a burst of short range bullets
crouch to reduce recoil", + ammo: 0, + ammoPack: 10, + have: false, + isStarterGun: true, + fire() { + mech.fireCDcycle = mech.cycle + Math.floor((mech.crouch ? 65 : 45) * b.modFireRate); // cool down + + b.muzzleFlash(35); + // mobs.alert(650); + const side = 11 * b.modBulletSize + for (let i = 0; i < 9; i++) { + const me = bullet.length; + const dir = mech.angle + (Math.random() - 0.5) * (mech.crouch ? 0.22 : 0.7) + bullet[me] = Bodies.rectangle(mech.pos.x + 35 * Math.cos(mech.angle) + 15 * (Math.random() - 0.5), mech.pos.y + 35 * Math.sin(mech.angle) + 15 * (Math.random() - 0.5), side, side, b.fireAttributes(dir)); + World.add(engine.world, bullet[me]); //add bullet to world + const SPEED = 40 + Math.random() * 11 + Matter.Body.setVelocity(bullet[me], { + x: SPEED * Math.cos(dir), + y: SPEED * Math.sin(dir) + }); + bullet[me].endCycle = game.cycle + Math.floor(55 * b.isModBulletsLastLonger); + bullet[me].frictionAir = 0.03; + bullet[me].do = function () { + this.force.y += this.mass * 0.001; + }; + } + + //knock back + const KNOCK = ((mech.crouch) ? 0.017 : 0.17) * b.modBulletSize * b.modBulletSize + player.force.x -= KNOCK * Math.cos(mech.angle) + player.force.y -= KNOCK * Math.sin(mech.angle) * 0.3 //reduce knock back in vertical direction to stop super jumps + } + }, + { + name: "super balls", //2 + description: "fire balls that bounce with no momentum loss", + ammo: 0, + ammoPack: 15, + have: false, + isStarterGun: true, + fire() { + mech.fireCDcycle = mech.cycle + Math.floor((mech.crouch ? 25 : 15) * b.modFireRate); // cool down + b.muzzleFlash(20); + // mobs.alert(450); + const SPEED = mech.crouch ? 45 : 28 + const SPREAD = mech.crouch ? 0.04 : 0.14 + let dir = mech.angle - SPREAD; + for (let i = 0; i < 3; i++) { + const me = bullet.length; + bullet[me] = Bodies.circle(mech.pos.x + 30 * Math.cos(mech.angle), mech.pos.y + 30 * Math.sin(mech.angle), 7 * b.modBulletSize, b.fireAttributes(dir, false)); + World.add(engine.world, bullet[me]); //add bullet to world + Matter.Body.setVelocity(bullet[me], { + x: SPEED * Math.cos(dir), + y: SPEED * Math.sin(dir) + }); + // Matter.Body.setDensity(bullet[me], 0.0001); + bullet[me].endCycle = game.cycle + Math.floor(360 * b.isModBulletsLastLonger); + bullet[me].dmg = b.modExtraDmg; + bullet[me].minDmgSpeed = 0; + bullet[me].restitution = 0.99; + bullet[me].friction = 0; + bullet[me].do = function () { + this.force.y += this.mass * 0.001; + }; + dir += SPREAD; + } + } + }, + + { + name: "fléchettes", //3 + description: "fire a flight of long range needles", + ammo: 0, + ammoPack: 16, + have: false, + isStarterGun: true, + fire() { + mech.fireCDcycle = mech.cycle + Math.floor(40 * b.modFireRate); // cool down + + function spawnFlechette(dir = mech.angle, speed, size = 1) { + const me = bullet.length; + bullet[me] = Bodies.rectangle(mech.pos.x + 40 * Math.cos(dir), mech.pos.y + 40 * Math.sin(dir), 32 * size * b.modBulletSize, 0.8 * size * b.modBulletSize, b.fireAttributes(dir)); + bullet[me].endCycle = game.cycle + Math.floor(180 * b.isModBulletsLastLonger); + bullet[me].dmg = 0.15 * size + b.modExtraDmg; + bullet[me].do = function () { + this.force.y += this.mass * 0.0002; //low gravity + }; + Matter.Body.setVelocity(bullet[me], { + x: mech.Vx / 2 + speed * Math.cos(dir), + y: mech.Vy / 2 + speed * Math.sin(dir) + }); + World.add(engine.world, bullet[me]); //add bullet to world + } + + if (mech.crouch) { + for (let i = 0; i < 3; i++) { + spawnFlechette(mech.angle + 0.02 * (Math.random() - 0.5), 35 + 4 * i, 1.55) + } + } else { + for (let i = 0; i < 9; i++) { + spawnFlechette(mech.angle + 0.12 * (Math.random() - 0.5), 30 + 8 * Math.random()) } - ctx.setLineDash([0, 0]); - ctx.globalAlpha = 1; } } }, { - name: "rail gun", //1 + name: "wave beam", //4 + description: "emit a sine wave of oscillating particles
particles propagate through walls", + ammo: 0, + ammoPack: 85, + have: false, + isStarterGun: true, + fire() { + const me = bullet.length; + const dir = mech.angle + const SCALE = (mech.crouch ? 0.963 : 0.95) + const wiggleMag = ((mech.flipLegs === 1) ? 1 : -1) * ((mech.crouch) ? 0.004 : 0.005) + bullet[me] = Bodies.circle(mech.pos.x + 25 * Math.cos(dir), mech.pos.y + 25 * Math.sin(dir), 10 * b.modBulletSize, { + angle: dir, + cycle: -0.43, //adjust this number until the bullets line up with the cross hairs + endCycle: game.cycle + Math.floor((mech.crouch ? 155 : 120) * b.isModBulletsLastLonger), + inertia: Infinity, + frictionAir: 0, + minDmgSpeed: 0, + dmg: 0.13 + b.modExtraDmg, //damage done in addition to the damage from momentum + classType: "bullet", + collisionFilter: { + category: cat.bullet, + mask: cat.mob | cat.mobBullet | cat.mobShield + }, + onDmg() {}, + onEnd() {}, + do() { + if (!mech.isBodiesAsleep) { + this.cycle++ + const THRUST = wiggleMag * Math.cos(this.cycle * 0.3) + this.force = Matter.Vector.mult(Matter.Vector.normalise(this.direction), this.mass * THRUST) //wiggle + + if (this.cycle > 0 && !(Math.floor(this.cycle) % 6)) Matter.Body.scale(this, SCALE, SCALE); //shrink + } + } + }); + World.add(engine.world, bullet[me]); //add bullet to world + mech.fireCDcycle = mech.cycle + Math.floor((mech.crouch ? 8 : 4) * b.modFireRate); // cool down + const SPEED = mech.crouch ? 5.2 : 4.5; + Matter.Body.setVelocity(bullet[me], { + x: SPEED * Math.cos(dir), + y: SPEED * Math.sin(dir) + }); + bullet[me].direction = Matter.Vector.perp(bullet[me].velocity) + // if (mech.angle + Math.PI / 2 > 0) { + // bullet[me].direction = Matter.Vector.perp(bullet[me].velocity, true) + // } else { + // bullet[me].direction = Matter.Vector.perp(bullet[me].velocity) + // } + + World.add(engine.world, bullet[me]); //add bullet to world + } + }, + { + name: "rail gun", //5 description: "electro-magnetically launch a dense rod
hold left mouse to charge, release to fire", //and repel enemies ammo: 0, ammoPack: 7, @@ -962,174 +1171,10 @@ const b = { } }, { - name: "minigun", //2 - description: "rapidly fire a stream of small bullets", - ammo: 0, - ammoPack: 105, - have: false, - isStarterGun: true, - fire() { - const me = bullet.length; - b.muzzleFlash(15); - // if (Math.random() > 0.2) mobs.alert(500); - const dir = mech.angle + (Math.random() - 0.5) * ((mech.crouch) ? 0.03 : 0.14); - bullet[me] = Bodies.rectangle(mech.pos.x + 30 * Math.cos(mech.angle), mech.pos.y + 30 * Math.sin(mech.angle), 17 * b.modBulletSize, 5 * b.modBulletSize, b.fireAttributes(dir)); - b.fireProps(mech.crouch ? 11 : 5, mech.crouch ? 44 : 36, dir, me); //cd , speed - bullet[me].endCycle = game.cycle + Math.floor(65 * b.isModBulletsLastLonger); - bullet[me].frictionAir = mech.crouch ? 0.007 : 0.01; - bullet[me].do = function () { - this.force.y += this.mass * 0.0005; - }; - } - }, - { - name: "wave beam", //3 - description: "emit a sine wave of oscillating particles
particles propagate through walls", - ammo: 0, - ammoPack: 85, - have: false, - isStarterGun: true, - fire() { - const me = bullet.length; - const dir = mech.angle - const SCALE = (mech.crouch ? 0.963 : 0.95) - const wiggleMag = ((mech.flipLegs === 1) ? 1 : -1) * ((mech.crouch) ? 0.004 : 0.005) - bullet[me] = Bodies.circle(mech.pos.x + 25 * Math.cos(dir), mech.pos.y + 25 * Math.sin(dir), 10 * b.modBulletSize, { - angle: dir, - cycle: -0.43, //adjust this number until the bullets line up with the cross hairs - endCycle: game.cycle + Math.floor((mech.crouch ? 155 : 120) * b.isModBulletsLastLonger), - inertia: Infinity, - frictionAir: 0, - minDmgSpeed: 0, - dmg: 0.13 + b.modExtraDmg, //damage done in addition to the damage from momentum - classType: "bullet", - collisionFilter: { - category: cat.bullet, - mask: cat.mob | cat.mobBullet | cat.mobShield - }, - onDmg() {}, - onEnd() {}, - do() { - if (!mech.isBodiesAsleep) { - this.cycle++ - const THRUST = wiggleMag * Math.cos(this.cycle * 0.3) - this.force = Matter.Vector.mult(Matter.Vector.normalise(this.direction), this.mass * THRUST) //wiggle - - if (this.cycle > 0 && !(Math.floor(this.cycle) % 6)) Matter.Body.scale(this, SCALE, SCALE); //shrink - } - } - }); - World.add(engine.world, bullet[me]); //add bullet to world - mech.fireCDcycle = mech.cycle + Math.floor((mech.crouch ? 8 : 4) * b.modFireRate); // cool down - const SPEED = mech.crouch ? 5.2 : 4.5; - Matter.Body.setVelocity(bullet[me], { - x: SPEED * Math.cos(dir), - y: SPEED * Math.sin(dir) - }); - bullet[me].direction = Matter.Vector.perp(bullet[me].velocity) - // if (mech.angle + Math.PI / 2 > 0) { - // bullet[me].direction = Matter.Vector.perp(bullet[me].velocity, true) - // } else { - // bullet[me].direction = Matter.Vector.perp(bullet[me].velocity) - // } - - World.add(engine.world, bullet[me]); //add bullet to world - } - }, { - name: "super balls", //4 - description: "fire balls that bounce with no momentum loss", - ammo: 0, - ammoPack: 14, - have: false, - isStarterGun: true, - fire() { - b.muzzleFlash(20); - // mobs.alert(450); - const SPREAD = mech.crouch ? 0.04 : 0.14 - let dir = mech.angle - SPREAD; - for (let i = 0; i < 3; i++) { - const me = bullet.length; - bullet[me] = Bodies.circle(mech.pos.x + 30 * Math.cos(mech.angle), mech.pos.y + 30 * Math.sin(mech.angle), 7 * b.modBulletSize, b.fireAttributes(dir, false)); - b.fireProps(mech.crouch ? 30 : 12, mech.crouch ? 38 : 30, dir, me); //cd , speed - Matter.Body.setDensity(bullet[me], 0.0001); - bullet[me].endCycle = game.cycle + Math.floor(300 * b.isModBulletsLastLonger); - bullet[me].dmg = 0.25 + b.modExtraDmg; - bullet[me].minDmgSpeed = 0; - bullet[me].restitution = 0.98; - bullet[me].friction = 0; - bullet[me].do = function () { - this.force.y += this.mass * 0.001; - }; - dir += SPREAD; - } - } - }, { - name: "shotgun", //5 - description: "fire a burst of short range bullets
crouch to reduce recoil", - ammo: 0, - ammoPack: 9, - have: false, - isStarterGun: true, - fire() { - b.muzzleFlash(35); - // mobs.alert(650); - const side = 11 * b.modBulletSize - for (let i = 0; i < 9; i++) { - const me = bullet.length; - const dir = mech.angle + (Math.random() - 0.5) * (mech.crouch ? 0.22 : 0.7) - bullet[me] = Bodies.rectangle(mech.pos.x + 35 * Math.cos(mech.angle) + 15 * (Math.random() - 0.5), mech.pos.y + 35 * Math.sin(mech.angle) + 15 * (Math.random() - 0.5), side, side, b.fireAttributes(dir)); - b.fireProps(mech.crouch ? 65 : 45, 40 + Math.random() * 11, dir, me); //cd , speed - bullet[me].endCycle = game.cycle + Math.floor(55 * b.isModBulletsLastLonger); - bullet[me].frictionAir = 0.03; - bullet[me].do = function () { - this.force.y += this.mass * 0.001; - }; - } - - //knock back - const KNOCK = ((mech.crouch) ? 0.015 : 0.15) * b.modBulletSize * b.modBulletSize - player.force.x -= KNOCK * Math.cos(mech.angle) - player.force.y -= KNOCK * Math.sin(mech.angle) * 0.3 //reduce knock back in vertical direction to stop super jumps - } - }, { - name: "fléchettes", //6 - description: "fire a flight of long range needles", - ammo: 0, - ammoPack: 16, - have: false, - isStarterGun: true, - fire() { - function spawnFlechette(dir = mech.angle, speed, size = 1) { - const me = bullet.length; - bullet[me] = Bodies.rectangle(mech.pos.x + 40 * Math.cos(dir), mech.pos.y + 40 * Math.sin(dir), 32 * size * b.modBulletSize, 0.8 * size * b.modBulletSize, b.fireAttributes(dir)); - bullet[me].endCycle = game.cycle + Math.floor(180 * b.isModBulletsLastLonger); - bullet[me].dmg = 0.15 * size + b.modExtraDmg; - bullet[me].do = function () { - this.force.y += this.mass * 0.0002; //low gravity - }; - Matter.Body.setVelocity(bullet[me], { - x: mech.Vx / 2 + speed * Math.cos(dir), - y: mech.Vy / 2 + speed * Math.sin(dir) - }); - World.add(engine.world, bullet[me]); //add bullet to world - } - - if (mech.crouch) { - for (let i = 0; i < 3; i++) { - spawnFlechette(mech.angle + 0.02 * (Math.random() - 0.5), 35 + 4 * i, 1.55) - } - } else { - for (let i = 0; i < 9; i++) { - spawnFlechette(mech.angle + 0.12 * (Math.random() - 0.5), 30 + 8 * Math.random()) - } - } - mech.fireCDcycle = mech.cycle + Math.floor(40 * b.modFireRate); // cool down - } - }, { - name: "missiles", //7 + name: "missiles", //6 description: "fire missiles that accelerate towards enemies
explodes when near target", ammo: 0, - ammoPack: 10, + ammoPack: 8, have: false, isStarterGun: false, fireCycle: 0, @@ -1145,8 +1190,8 @@ const b = { bullet[me].force.y += 0.0005; //a small push down at first to make it seem like the missile is briefly falling bullet[me].frictionAir = 0.023 bullet[me].endCycle = game.cycle + Math.floor((280 + 40 * Math.random()) * b.isModBulletsLastLonger); - bullet[me].explodeRad = 170 + 70 * Math.random(); - bullet[me].lookFrequency = Math.floor(8 + Math.random() * 7); + bullet[me].explodeRad = 160 + 60 * Math.random(); + bullet[me].lookFrequency = Math.floor(15 + Math.random() * 5); bullet[me].onEnd = b.explode; //makes bullet do explosive damage at end bullet[me].onDmg = function () { this.endCycle = 0; //bullet ends cycle after doing damage // also triggers explosion @@ -1176,11 +1221,9 @@ const b = { } //explode when bullet is close enough to target if (this.lockedOn && closeDist < this.explodeRad * 0.6) { - this.endCycle = 0; //bullet ends cycle after doing damage //this also triggers explosion - //does extra damage to target - - const dmg = b.dmgScale * 4; - this.lockedOn.damage(dmg); + this.endCycle = 0; //bullet ends cycle after doing damage //also triggers explosion + const dmg = b.dmgScale * 3; + this.lockedOn.damage(dmg); //does extra damage to target } } @@ -1219,33 +1262,36 @@ const b = { } } }, { - name: "flak", //8 + name: "flak", //7 description: "fire a cluster of short range projectiles
explodes on contact or after half a second", ammo: 0, - ammoPack: 22, + ammoPack: 14, have: false, isStarterGun: true, fire() { + mech.fireCDcycle = mech.cycle + Math.floor((mech.crouch ? 25 : 10) * b.modFireRate); // cool down b.muzzleFlash(30); - const totalBullets = 5 - const angleStep = (mech.crouch ? 0.06 : 0.25) / totalBullets const SPEED = mech.crouch ? 29 : 25 - const CD = mech.crouch ? 30 : 11 const END = Math.floor((mech.crouch ? 30 : 18) * b.isModBulletsLastLonger); - let dir = mech.angle - angleStep * totalBullets / 2; const side1 = 17 * b.modBulletSize const side2 = 4 * b.modBulletSize + const totalBullets = 5 + const angleStep = (mech.crouch ? 0.06 : 0.25) / totalBullets + let dir = mech.angle - angleStep * totalBullets / 2; for (let i = 0; i < totalBullets; i++) { //5 -> 7 dir += angleStep const me = bullet.length; bullet[me] = Bodies.rectangle(mech.pos.x + 50 * Math.cos(mech.angle), mech.pos.y + 50 * Math.sin(mech.angle), side1, side2, b.fireAttributes(dir)); - b.fireProps(CD, SPEED + 15 * Math.random() - 2 * i, dir, me); //cd , speed - // Matter.Body.setDensity(bullet[me], 0.005); + World.add(engine.world, bullet[me]); //add bullet to world + Matter.Body.setVelocity(bullet[me], { + x: (SPEED + 15 * Math.random() - 2 * i) * Math.cos(dir), + y: (SPEED + 15 * Math.random() - 2 * i) * Math.sin(dir) + }); + bullet[me].endCycle = 2 * i + game.cycle + END bullet[me].restitution = 0; bullet[me].friction = 1; - // bullet[me].dmg = 0.15; bullet[me].explodeRad = (mech.crouch ? 70 : 50) + (Math.random() - 0.5) * 50; bullet[me].onEnd = b.explode; bullet[me].onDmg = function () { @@ -1253,19 +1299,14 @@ const b = { }; bullet[me].do = function () { this.force.y += this.mass * 0.0004; - // if (this.speed < 10) { //if slow explode - // for (let i = 0, len = bullet.length; i < len; i++) { - // bullet[i].endCycle = 0 //all other bullets explode - // } - // } } } } }, { - name: "grenades", //9 + name: "grenades", //8 description: "lob a single bouncy projectile
explodes on contact or after one second", ammo: 0, - ammoPack: 16, + ammoPack: 11, have: false, isStarterGun: false, fire() { @@ -1290,7 +1331,7 @@ const b = { }; } }, { - name: "vacuum bomb", //10 + name: "vacuum bomb", //9 description: "fire a bomb that sucks before exploding
click left mouse again to detonate", ammo: 0, ammoPack: 5, @@ -1399,8 +1440,8 @@ const b = { } } }, { - name: "ferro frag", //11 - description: "fire a grenade that ejects magnetized nails
nails are attracted to enemies", + name: "ferro frag", //10 + description: "fire a grenade that ejects nails
nails are magnetically attracted to enemies", ammo: 0, ammoPack: 8, have: false, @@ -1469,17 +1510,17 @@ const b = { } } }, { - name: "spores", //12 + name: "spores", //11 description: "fire orbs that discharge spores
spores seek out enemies", ammo: 0, - ammoPack: 5, + ammoPack: 6, have: false, isStarterGun: false, fire() { const me = bullet.length; const dir = mech.angle; bullet[me] = Bodies.polygon(mech.pos.x + 30 * Math.cos(mech.angle), mech.pos.y + 30 * Math.sin(mech.angle), 20, 4.5, b.fireAttributes(dir, false)); - b.fireProps(mech.crouch ? 75 : 55, mech.crouch ? 25 : 14, dir, me); //cd , speed + b.fireProps(mech.crouch ? 65 : 45, mech.crouch ? 28 : 14, dir, me); //cd , speed Matter.Body.setDensity(bullet[me], 0.000001); bullet[me].endCycle = game.cycle + 100; bullet[me].frictionAir = 0; @@ -1514,111 +1555,20 @@ const b = { } }, { - name: "drones", //13 + name: "drones", //12 description: "deploy drones that seek out enemies
collisions reduce drone cycles by 1 second", ammo: 0, ammoPack: 17, have: false, isStarterGun: true, fire() { - const THRUST = 0.0015 - const dir = mech.angle + 0.2 * (Math.random() - 0.5); - const me = bullet.length; - const RADIUS = (4.5 + 3 * Math.random()) * b.modBulletSize - bullet[me] = Bodies.circle(mech.pos.x + 30 * Math.cos(mech.angle), mech.pos.y + 30 * Math.sin(mech.angle), RADIUS, { - angle: dir, - inertia: Infinity, - friction: 0.05, - frictionAir: 0.0005, - restitution: 1, - dmg: 0.13 + b.modExtraDmg, //damage done in addition to the damage from momentum - lookFrequency: 83 + Math.floor(41 * Math.random()), - endCycle: game.cycle + Math.floor((1080 + 360 * Math.random()) * b.isModBulletsLastLonger), - classType: "bullet", - collisionFilter: { - category: cat.bullet, - mask: cat.map | cat.body | cat.bullet | cat.mob | cat.mobBullet | cat.mobShield //self collide - }, - minDmgSpeed: 0, - lockedOn: null, - isFollowMouse: true, - onDmg() { - this.lockedOn = null - if (this.endCycle > game.cycle + 180) { - this.endCycle -= 60 - if (game.cycle + 180 > this.endCycle) this.endCycle = game.cycle + 180 - } - }, - onEnd() {}, - do() { - if (game.cycle + 180 > this.endCycle) { //fall and die - this.force.y += this.mass * 0.0012; - this.restitution = 0.2; - } else { - this.force.y += this.mass * 0.0002; - //find mob targets - if (!(game.cycle % this.lookFrequency)) { - this.lockedOn = null; - let closeDist = Infinity; - for (let i = 0, len = mob.length; i < len; ++i) { - if ( - Matter.Query.ray(map, this.position, mob[i].position).length === 0 && - Matter.Query.ray(body, this.position, mob[i].position).length === 0 - ) { - const TARGET_VECTOR = Matter.Vector.sub(this.position, mob[i].position) - const DIST = Matter.Vector.magnitude(TARGET_VECTOR); - if (DIST < closeDist) { - closeDist = DIST; - this.lockedOn = mob[i] - } - } - } - if (!this.lockedOn) { - //grab a power up if it is (ammo) or (a heal when player is low) - let closeDist = Infinity; - for (let i = 0, len = powerUp.length; i < len; ++i) { - if ( - ((powerUp[i].name !== "field" && powerUp[i].name !== "heal") || (powerUp[i].name === "heal" && mech.health < 0.8)) && - Matter.Query.ray(map, this.position, powerUp[i].position).length === 0 && - Matter.Query.ray(body, this.position, powerUp[i].position).length === 0 - ) { - const TARGET_VECTOR = Matter.Vector.sub(this.position, powerUp[i].position) - const DIST = Matter.Vector.magnitude(TARGET_VECTOR); - if (DIST < closeDist) { - if (DIST < 50) { //eat the power up if close enough - powerUp[i].effect(); - Matter.World.remove(engine.world, powerUp[i]); - powerUp.splice(i, 1); - break; - } - closeDist = DIST; - this.lockedOn = powerUp[i] - } - } - } - } - } - if (this.lockedOn) { //accelerate towards mobs - this.force = Matter.Vector.mult(Matter.Vector.normalise(Matter.Vector.sub(this.position, this.lockedOn.position)), -this.mass * THRUST) - } else { //accelerate towards mouse - this.force = Matter.Vector.mult(Matter.Vector.normalise(Matter.Vector.sub(this.position, game.mouseInGame)), -this.mass * THRUST) - } - // speed cap instead of friction to give more agility - if (this.speed > 6) { - Matter.Body.setVelocity(this, { - x: this.velocity.x * 0.97, - y: this.velocity.y * 0.97 - }); - } - } - } - }) - b.fireProps(mech.crouch ? 14 : 10, mech.crouch ? 40 : 1, dir, me); //cd , speed + b.drone(mech.crouch ? 45 : 1) + mech.fireCDcycle = mech.cycle + Math.floor((mech.crouch ? 25 : 10) * b.modFireRate); // cool down } }, { - name: "laser-bot", //14 - description: "deploy bots that defend against close threats
lasts one level, but drains energy", + name: "guardian", //13 + description: "deploy a bot that protect you for one level
uses a short range laser that drains energy", ammo: 0, ammoPack: 1, have: false, @@ -1713,17 +1663,286 @@ const b = { } } }) - b.fireProps(mech.crouch ? 60 : 30, 15, dir, me); //cd , speed + b.fireProps(mech.crouch ? 40 : 30, mech.crouch ? 50 : 15, dir, me); //cd , speed } }, { - name: "foam", //15 + name: "laser", //14 + description: "emit a beam of collimation coherent light
uses energy instead of ammunition", + ammo: 0, + // ammoPack: 350, + ammoPack: Infinity, + have: false, + isStarterGun: true, + fire() { + // mech.fireCDcycle = mech.cycle + 1 + //laser drains energy as well as bullets + const FIELD_DRAIN = 0.002 + const damage = 0.05 + if (mech.fieldMeter < FIELD_DRAIN) { + mech.fireCDcycle = mech.cycle + 100; // cool down if out of energy + } else { + mech.fieldMeter -= mech.fieldRegen + FIELD_DRAIN + let best; + const color = "#f00"; + const range = 3000; + const path = [{ + x: mech.pos.x + 20 * Math.cos(mech.angle), + y: mech.pos.y + 20 * Math.sin(mech.angle) + }, + { + x: mech.pos.x + range * Math.cos(mech.angle), + y: mech.pos.y + range * Math.sin(mech.angle) + } + ]; + const vertexCollision = function (v1, v1End, domain) { + for (let i = 0; i < domain.length; ++i) { + let vertices = domain[i].vertices; + const len = vertices.length - 1; + for (let j = 0; j < len; j++) { + results = game.checkLineIntersection(v1, v1End, vertices[j], vertices[j + 1]); + if (results.onLine1 && results.onLine2) { + const dx = v1.x - results.x; + const dy = v1.y - results.y; + const dist2 = dx * dx + dy * dy; + if (dist2 < best.dist2 && (!domain[i].mob || domain[i].alive)) { + best = { + x: results.x, + y: results.y, + dist2: dist2, + who: domain[i], + v1: vertices[j], + v2: vertices[j + 1] + }; + } + } + } + results = game.checkLineIntersection(v1, v1End, vertices[0], vertices[len]); + if (results.onLine1 && results.onLine2) { + const dx = v1.x - results.x; + const dy = v1.y - results.y; + const dist2 = dx * dx + dy * dy; + if (dist2 < best.dist2 && (!domain[i].mob || domain[i].alive)) { + best = { + x: results.x, + y: results.y, + dist2: dist2, + who: domain[i], + v1: vertices[0], + v2: vertices[len] + }; + } + } + } + }; + const checkForCollisions = function () { + best = { + x: null, + y: null, + dist2: Infinity, + who: null, + v1: null, + v2: null + }; + vertexCollision(path[path.length - 2], path[path.length - 1], mob); + vertexCollision(path[path.length - 2], path[path.length - 1], map); + vertexCollision(path[path.length - 2], path[path.length - 1], body); + }; + const laserHitMob = function (dmg) { + if (best.who.alive) { + dmg *= b.dmgScale * damage; + best.who.damage(dmg); + best.who.locatePlayer(); + //draw mob damage circle + ctx.fillStyle = color; + ctx.beginPath(); + ctx.arc(path[path.length - 1].x, path[path.length - 1].y, Math.sqrt(dmg) * 100, 0, 2 * Math.PI); + ctx.fill(); + } + }; + + const reflection = function () { + // https://math.stackexchange.com/questions/13261/how-to-get-a-reflection-vector + const n = Matter.Vector.perp(Matter.Vector.normalise(Matter.Vector.sub(best.v1, best.v2))); + const d = Matter.Vector.sub(path[path.length - 1], path[path.length - 2]); + const nn = Matter.Vector.mult(n, 2 * Matter.Vector.dot(d, n)); + const r = Matter.Vector.normalise(Matter.Vector.sub(d, nn)); + path[path.length] = Matter.Vector.add(Matter.Vector.mult(r, range), path[path.length - 1]); + }; + //beam before reflection + checkForCollisions(); + if (best.dist2 != Infinity) { + //if hitting something + path[path.length - 1] = { + x: best.x, + y: best.y + }; + laserHitMob(1); + + //1st reflection beam + reflection(); + //ugly bug fix: this stops the reflection on a bug where the beam gets trapped inside a body + let who = best.who; + checkForCollisions(); + if (best.dist2 != Infinity) { + //if hitting something + path[path.length - 1] = { + x: best.x, + y: best.y + }; + laserHitMob(0.75); + + //2nd reflection beam + //ugly bug fix: this stops the reflection on a bug where the beam gets trapped inside a body + if (who !== best.who) { + reflection(); + checkForCollisions(); + if (best.dist2 != Infinity) { + //if hitting something + path[path.length - 1] = { + x: best.x, + y: best.y + }; + laserHitMob(0.5); + } + } + } + } + ctx.fillStyle = color; + ctx.strokeStyle = color; + ctx.lineWidth = 2; + ctx.lineDashOffset = 300 * Math.random() + // ctx.setLineDash([200 * Math.random(), 250 * Math.random()]); + + ctx.setLineDash([50 + 120 * Math.random(), 50 * Math.random()]); + for (let i = 1, len = path.length; i < len; ++i) { + ctx.beginPath(); + ctx.moveTo(path[i - 1].x, path[i - 1].y); + ctx.lineTo(path[i].x, path[i].y); + ctx.stroke(); + ctx.globalAlpha *= 0.5; //reflections are less intense + // ctx.globalAlpha -= 0.1; //reflections are less intense + } + ctx.setLineDash([0, 0]); + ctx.globalAlpha = 1; + } + } + }, + { + name: "pulse", //15 + description: "emit a pulse of collimation coherent light
use energy to induce an explosion", + ammo: 0, + ammoPack: Infinity, + have: false, + isStarterGun: true, + count: 0, + fire() { + //calculate laser collision + let best; + let range = 6000 + const dir = mech.angle // + 0.04 * (Math.random() - 0.5) + const path = [{ + x: mech.pos.x + 20 * Math.cos(dir), + y: mech.pos.y + 20 * Math.sin(dir) + }, + { + x: mech.pos.x + range * Math.cos(dir), + y: mech.pos.y + range * Math.sin(dir) + } + ]; + const vertexCollision = function (v1, v1End, domain) { + for (let i = 0; i < domain.length; ++i) { + let vertices = domain[i].vertices; + const len = vertices.length - 1; + for (let j = 0; j < len; j++) { + results = game.checkLineIntersection(v1, v1End, vertices[j], vertices[j + 1]); + if (results.onLine1 && results.onLine2) { + const dx = v1.x - results.x; + const dy = v1.y - results.y; + const dist2 = dx * dx + dy * dy; + if (dist2 < best.dist2 && (!domain[i].mob || domain[i].alive)) { + best = { + x: results.x, + y: results.y, + dist2: dist2, + who: domain[i], + v1: vertices[j], + v2: vertices[j + 1] + }; + } + } + } + results = game.checkLineIntersection(v1, v1End, vertices[0], vertices[len]); + if (results.onLine1 && results.onLine2) { + const dx = v1.x - results.x; + const dy = v1.y - results.y; + const dist2 = dx * dx + dy * dy; + if (dist2 < best.dist2 && (!domain[i].mob || domain[i].alive)) { + best = { + x: results.x, + y: results.y, + dist2: dist2, + who: domain[i], + v1: vertices[0], + v2: vertices[len] + }; + } + } + } + }; + + //check for collisions + best = { + x: null, + y: null, + dist2: Infinity, + who: null, + v1: null, + v2: null + }; + vertexCollision(path[0], path[1], mob); + vertexCollision(path[0], path[1], map); + vertexCollision(path[0], path[1], body); + if (best.dist2 != Infinity) { //if hitting something + path[path.length - 1] = { + x: best.x, + y: best.y + }; + } + + //draw blowtorch laser beam + ctx.strokeStyle = "rgba(255,0,0,0.15)" + ctx.lineWidth = 16 + ctx.beginPath(); + ctx.moveTo(path[0].x, path[0].y); + ctx.lineTo(path[1].x, path[1].y); + ctx.stroke(); + ctx.strokeStyle = "#f00"; + ctx.lineWidth = 2.5 + ctx.beginPath(); + ctx.moveTo(path[0].x, path[0].y); + ctx.lineTo(path[1].x, path[1].y); + ctx.stroke(); + + this.count++ + if (this.count > 2) { + this.count = 0; + mech.fireCDcycle = mech.cycle + Math.floor(30 * b.modFireRate); // cool down + const energy = Math.min(0.2, mech.fieldMeter * 0.3) + if (best.who) b.explosion(path[1], 1300 * energy) + mech.fieldMeter -= energy + } + } + }, + { + name: "foam", //16 description: "spray bubbly foam that sticks to enemies
does damage over time and slows movement", ammo: 0, ammoPack: 90, have: false, isStarterGun: true, fire() { + mech.fireCDcycle = mech.cycle + Math.floor((mech.crouch ? 12 : 4) * b.modFireRate); // cool down const me = bullet.length; const dir = mech.angle + 0.2 * (Math.random() - 0.5) const RADIUS = (8 + 16 * Math.random()) * b.modBulletSize @@ -1802,7 +2021,6 @@ const b = { } }); World.add(engine.world, bullet[me]); //add bullet to world - mech.fireCDcycle = mech.cycle + Math.floor((mech.crouch ? 12 : 4) * b.modFireRate); // cool down const SPEED = mech.crouch ? 22 : 12 - RADIUS * 0.25; Matter.Body.setVelocity(bullet[me], { x: SPEED * Math.cos(dir), diff --git a/js/level.js b/js/level.js index 5b0a897..21c7b71 100644 --- a/js/level.js +++ b/js/level.js @@ -14,8 +14,8 @@ const level = { start() { if (level.levelsCleared === 0) { // game.difficulty = 6; //for testing to simulate possible mobs spawns - // b.giveGuns(7) - // mech.fieldUpgrades[1].effect(); + b.giveGuns(15) + // mech.fieldUpgrades[2].effect(); // b.giveMod(21) this.intro(); //starting level @@ -109,10 +109,7 @@ const level = { // powerUps.spawn(450, -400, "mod", false, 6); // powerUps.spawn(450, -400, "mod", false); // spawn.bodyRect(-45, -100, 40, 50); - spawn.starter(800, -1050); - spawn.starter(800, -1050); - spawn.starter(800, -1050); - spawn.starter(800, -1050); + spawn.shooter(800, -1050); // spawn.groupBoss(-600, -550); // spawn.hopper(800, -150); // spawn.beamer(800, -150); diff --git a/js/mobs.js b/js/mobs.js index fed150d..ddce5aa 100644 --- a/js/mobs.js +++ b/js/mobs.js @@ -268,12 +268,7 @@ const mobs = { ctx.setLineDash([125 * Math.random(), 125 * Math.random()]); // ctx.lineDashOffset = 6*(game.cycle % 215); if (this.distanceToPlayer() < this.laserRange && !mech.isStealth) { - //if (Math.random()>0.2 && this.seePlayer.yes && this.distanceToPlayer2()<800000) { - if (b.isModTempResist) { - mech.damage(0.00006 * game.dmgScale); - } else { - mech.damage(0.0003 * game.dmgScale); - } + mech.damage(0.0003 * game.dmgScale); if (mech.fieldMeter > 0.1) mech.fieldMeter -= 0.004 ctx.beginPath(); ctx.moveTo(this.position.x, this.position.y); @@ -361,12 +356,7 @@ const mobs = { if (!mech.isStealth) vertexCollision(this.position, look, [player]); // hitting player if (best.who === player) { - if (b.isModTempResist) { - dmg = 0.0004 * game.dmgScale; - } else { - dmg = 0.002 * game.dmgScale; - } - + dmg = 0.002 * game.dmgScale; mech.damage(dmg); //draw damage ctx.fillStyle = color; @@ -915,8 +905,8 @@ const mobs = { const angle = Math.atan2(unitVector.y, unitVector.x); Matter.Body.setAngle(this, angle - Math.PI); }, - explode() { - mech.damage(Math.min(Math.max(0.02 * Math.sqrt(this.mass), 0.01), 0.35) * game.dmgScale); + explode(mass = this.mass) { + mech.damage(Math.min(Math.max(0.02 * Math.sqrt(mass), 0.01), 0.35) * game.dmgScale); this.dropPowerUp = false; this.death(); //death with no power up or body }, diff --git a/js/player.js b/js/player.js index 7c26880..955c2c9 100644 --- a/js/player.js +++ b/js/player.js @@ -458,7 +458,7 @@ const mech = { if (b.isModDroneOnDamage) { const len = (dmg - 0.08 + 0.05 * Math.random()) / 0.05 for (let i = 0; i < len; i++) { - if (Math.random() < 0.6) b.guns[13].fire() //spawn drone + if (Math.random() < 0.6) b.drone() //spawn drone } } @@ -886,20 +886,20 @@ const mech = { mech.holdingTarget = null //knock backs const unit = Matter.Vector.normalise(Matter.Vector.sub(player.position, who.position)) - const mass = Math.min(Math.sqrt(who.mass), 3.5); //large masses above 4*4 can start to overcome the push back + const massRoot = Math.sqrt(Math.min(12, Math.max(0.15, who.mass))); // masses above 12 can start to overcome the push back Matter.Body.setVelocity(who, { - x: player.velocity.x - (15 * unit.x) / mass, - y: player.velocity.y - (15 * unit.y) / mass + x: player.velocity.x - (15 * unit.x) / massRoot, + y: player.velocity.y - (15 * unit.y) / massRoot }); if (mech.crouch) { Matter.Body.setVelocity(player, { - x: player.velocity.x + 0.4 * unit.x * mass, - y: player.velocity.y + 0.4 * unit.y * mass + x: player.velocity.x + 0.4 * unit.x * massRoot, + y: player.velocity.y + 0.4 * unit.y * massRoot }); } else { Matter.Body.setVelocity(player, { - x: player.velocity.x + 5 * unit.x * mass, - y: player.velocity.y + 5 * unit.y * mass + x: player.velocity.x + 5 * unit.x * massRoot, + y: player.velocity.y + 5 * unit.y * massRoot }); } @@ -1086,7 +1086,7 @@ const mech = { mech.holding(); mech.throw(); } else if ((keys[32] || game.mouseDownRight) && mech.fieldCDcycle < mech.cycle) { - const DRAIN = 0.0027 + const DRAIN = 0.0023 if (mech.fieldMeter > DRAIN) { mech.fieldMeter -= DRAIN; @@ -1140,7 +1140,7 @@ const mech = { }, { name: "plasma torch", - description: "use energy to emit damaging plasma
decreased shield range and efficiency", + description: "use energy to emit damaging plasma
effective at close range", effect: () => { mech.fieldMode = 2; mech.fieldText(); @@ -1289,10 +1289,16 @@ const mech = { } ctx.lineWidth = 2 * Math.random(); ctx.stroke(); + //draw shield around player + const pushRange = 110; + ctx.beginPath(); + ctx.arc(mech.pos.x, mech.pos.y, pushRange, 0, 2 * Math.PI); + ctx.fillStyle = "rgba(255,0,255,0.05)" + ctx.fill(); mech.grabPowerUp(); mech.lookForPickUp(); - mech.pushMobs360(110); + mech.pushMobs360(pushRange); // mech.pushBody360(100); //disabled because doesn't work at short range } else { mech.fieldCDcycle = mech.cycle + 120; //if out of energy @@ -1395,13 +1401,13 @@ const mech = { }, { name: "standing wave harmonics", - description: "oscillating shields surround you constantly
decreased energy regeneration", + description: "three oscillating shields are perminantly active
energy regenerates at normal rate", effect: () => { mech.fieldMode = 4; mech.fieldText(); mech.setHoldDefaults(); - mech.fieldRegen *= 0.6; - mech.fieldShieldingScale = 1.5; + // mech.fieldRegen *= 0.6; + mech.fieldShieldingScale = 1.33; mech.hold = function () { if (mech.isHolding) { @@ -1440,17 +1446,16 @@ const mech = { }, { name: "nano-scale manufacturing", - description: "excess energy used to build drones
3x energy regeneration", + description: "excess energy used to build drones
2x energy regeneration", effect: () => { - let gunIndex = 13 //Math.random() < 0.5 ? 13 : 14 mech.fieldMode = 5; mech.fieldText(); mech.setHoldDefaults(); - mech.fieldRegen *= 3; + mech.fieldRegen *= 2; mech.hold = function () { if (mech.fieldMeter > mech.fieldEnergyMax - 0.02) { - mech.fieldMeter -= 0.43; - b.guns[gunIndex].fire() //spawn drone + mech.fieldMeter -= 0.32; + b.drone(1) mech.fireCDcycle = mech.cycle + 25; // set fire cool down to prevent +energy from making huge numbers of drones } if (mech.isHolding) { diff --git a/js/spawn.js b/js/spawn.js index 22f3818..bf76203 100644 --- a/js/spawn.js +++ b/js/spawn.js @@ -900,9 +900,9 @@ const spawn = { let me = mob[mob.length - 1]; me.stroke = "transparent"; me.onHit = function () { - this.explode(); + this.explode(this.mass * 10); }; - Matter.Body.setDensity(me, 0.001); //normal is 0.001 + Matter.Body.setDensity(me, 0.0001); //normal is 0.001 me.timeLeft = 240; me.g = 0.001; //required if using 'gravity' me.frictionAir = 0;