From 0e0b2fd5a29bb0188f85d09661505d2254ed17af Mon Sep 17 00:00:00 2001 From: landgreen Date: Sat, 29 Feb 2020 08:32:07 -0800 Subject: [PATCH] missile mod, and missile field --- js/bullets.js | 251 ++++++++++++++++++++++++++++++-------------------- js/level.js | 6 +- js/player.js | 44 +++++++-- todo.txt | 8 ++ 4 files changed, 200 insertions(+), 109 deletions(-) diff --git a/js/bullets.js b/js/bullets.js index f32d5d7..f69054f 100644 --- a/js/bullets.js +++ b/js/bullets.js @@ -54,17 +54,19 @@ const b = { modWaveSpeedBody: null, modFieldEfficiency: null, isModSporeField: null, + isModMissileField: null, isModFlechetteMultiShot: null, isModMineAmmoBack: null, isModRailNails: null, isModHawking: null, + modBabyMissiles: null, modOnHealthChange() { //used with acid mod if (b.isModAcidDmg && mech.health > 0.8) { game.playerDmgColor = "rgba(0,80,80,0.9)" - b.modAcidDmg = 1.5 + b.modAcidDmg = 0.7 } else { game.playerDmgColor = "rgba(0,0,0,0.7)" - b.modAcidDmg = 1 + b.modAcidDmg = 0 } }, mods: [{ @@ -85,7 +87,7 @@ const b = { }, { name: "fluoroantimonic acid", - description: "each bullet does extra chemical damage
only active when you are above 80% health", + description: "each bullet does extra chemical damage
active when you are above 80% base health", maxCount: 1, count: 0, allowed() { @@ -156,7 +158,7 @@ const b = { maxCount: 3, count: 0, allowed() { - return b.haveGunCheck("missiles") || b.haveGunCheck("flak") || b.haveGunCheck("grenades") || b.haveGunCheck("vacuum bomb") || b.haveGunCheck("pulse"); + return b.haveGunCheck("missiles") || b.haveGunCheck("flak") || b.haveGunCheck("grenades") || b.haveGunCheck("vacuum bomb") || b.haveGunCheck("pulse") || b.isModMissileField; }, requires: "an explosive gun", effect: () => { @@ -526,7 +528,7 @@ const b = { maxCount: 9, count: 0, allowed() { - return mech.fieldUpgrades[mech.fieldMode].name !== "time dilation field" && mech.fieldUpgrades[mech.fieldMode].name !== "phase decoherence field" + return mech.fieldUpgrades[mech.fieldMode].name !== "time dilation field" && mech.fieldUpgrades[mech.fieldMode].name !== "phase decoherence field" && !(b.isModHawking && mech.fieldUpgrades[mech.fieldMode].name === "negative mass field") }, requires: "not time dilation field
requires not phase decoherence field", effect() { @@ -861,6 +863,22 @@ const b = { b.modWaveSpeedBody = 0.25 } }, + { + name: "self-replication", + description: "when your missiles explode
they fire +1 smaller missiles", + maxCount: 9, + count: 0, + allowed() { + return b.haveGunCheck("missiles") || b.isModMissileField + }, + requires: "missiles", + effect() { + b.modBabyMissiles++ + }, + remove() { + b.modBabyMissiles = 0; + } + }, { name: "optimized shell packing", description: "flak ammo drops contain 2x more shells", @@ -919,7 +937,7 @@ const b = { maxCount: 1, count: 0, allowed() { - return b.haveGunCheck("drones") || (mech.fieldUpgrades[mech.fieldMode].name === "nano-scale manufacturing" && !b.isModSporeField) + return b.haveGunCheck("drones") || (mech.fieldUpgrades[mech.fieldMode].name === "nano-scale manufacturing" && !(b.isModSporeField || b.isModMissileField)) }, requires: "drones", effect() { @@ -963,7 +981,7 @@ const b = { }, { name: "specular reflection", - description: "your laser gains +1 reflection
+30% laser damage and energy drain", + description: "your laser gains +1 reflection
+33% laser damage and energy drain", maxCount: 9, count: 0, allowed() { @@ -972,12 +990,12 @@ const b = { requires: "laser", effect() { b.modLaserReflections++; - b.modLaserDamage += 0.015; //base is 0.05 + b.modLaserDamage += 0.02; //base is 0.05 b.modLaserFieldDrain += 0.0006 //base is 0.002 }, remove() { b.modLaserReflections = 2; - b.modLaserDamage = 0.05; + b.modLaserDamage = 0.06; b.modLaserFieldDrain = 0.002; } }, @@ -1001,11 +1019,11 @@ const b = { }, { name: "mycelium manufacturing", - description: "nano-scale manufacturing is modified to
grow spores instead of drones", + description: "nano-scale manufacturing is repurposed
excess energy used to grow spores", maxCount: 1, count: 0, allowed() { - return mech.fieldUpgrades[mech.fieldMode].name === "nano-scale manufacturing" + return mech.fieldUpgrades[mech.fieldMode].name === "nano-scale manufacturing" && !b.isModMissileField }, requires: "nano-scale manufacturing", effect() { @@ -1015,9 +1033,25 @@ const b = { b.isModSporeField = false; } }, + { + name: "missile manufacturing", + description: "nano-scale manufacturing is repurposed
excess energy used to construct missiles", + maxCount: 1, + count: 0, + allowed() { + return mech.fieldUpgrades[mech.fieldMode].name === "nano-scale manufacturing" && !b.isModSporeField + }, + requires: "nano-scale manufacturing", + effect() { + b.isModMissileField = true; + }, + remove() { + b.isModMissileField = false; + } + }, { name: "hawking radiation", - description: "negative mass field releases virtual particles that
damage mobs within range", + description: "negative mass field can no longer block
instead it damages mobs within range", maxCount: 1, count: 0, allowed() { @@ -1337,6 +1371,101 @@ const b = { } } }, + missile(where, dir, speed, size = 1, spawn = 0) { + const me = bullet.length; + bullet[me] = Bodies.rectangle(where.x, where.y, 30 * b.modBulletSize * size, 4 * b.modBulletSize * size, b.fireAttributes(dir)); + const thrust = 0.00417 * bullet[me].mass; + 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 + bullet[me].frictionAir = 0.023 + bullet[me].endCycle = game.cycle + Math.floor((280 + 40 * Math.random()) * b.isModBulletsLastLonger); + bullet[me].explodeRad = 170 + 60 * Math.random(); + bullet[me].lookFrequency = Math.floor(21 + Math.random() * 7); + bullet[me].onEnd = function () { + b.explosion(this.position, this.explodeRad * size); //makes bullet do explosive damage at end + for (let i = 0; i < spawn; i++) { + b.missile(this.position, 2 * Math.PI * Math.random(), 0, 0.75) + } + } + bullet[me].onDmg = function () { + this.tryToLockOn(); + // this.endCycle = 0; //bullet ends cycle after doing damage // also triggers explosion + }; + bullet[me].lockedOn = null; + bullet[me].tryToLockOn = function () { + this.lockedOn = null; + let closeDist = Infinity; + + //look for closest target to where the missile will be in 30 cycles + const futurePos = Vector.add(this.position, Vector.mult(this.velocity, 30)) + for (let i = 0, len = mob.length; i < len; ++i) { + if ( + mob[i].alive && mob[i].dropPowerUp && + Matter.Query.ray(map, this.position, mob[i].position).length === 0 && + Matter.Query.ray(body, this.position, mob[i].position).length === 0 + ) { + const futureDist = Vector.magnitude(Vector.sub(futurePos, mob[i].position)); + if (futureDist < closeDist) { + closeDist = futureDist; + this.lockedOn = mob[i]; + this.frictionAir = 0.05; //extra friction once a target it locked + } + } + } + //explode when bullet is close enough to target + if (this.lockedOn && Vector.magnitude(Vector.sub(this.position, this.lockedOn.position)) < this.explodeRad) { + // console.log('hit') + this.endCycle = 0; //bullet ends cycle after doing damage //also triggers explosion + this.lockedOn.damage(b.dmgScale * 5 * size); //does extra damage to target + } + }; + bullet[me].do = function () { + if (!mech.isBodiesAsleep) { + if (!(mech.cycle % this.lookFrequency)) { + this.tryToLockOn(); + } + + //rotate missile towards the target + if (this.lockedOn) { + const face = { + x: Math.cos(this.angle), + y: Math.sin(this.angle) + }; + const target = Vector.normalise(Vector.sub(this.position, this.lockedOn.position)); + if (Vector.dot(target, face) > -0.98) { + if (Vector.cross(target, face) > 0) { + Matter.Body.rotate(this, 0.08); + } else { + Matter.Body.rotate(this, -0.08); + } + } + } + //accelerate in direction bullet is facing + const dir = this.angle; // + (Math.random() - 0.5); + this.force.x += Math.cos(dir) * thrust; + this.force.y += Math.sin(dir) * thrust; + + //draw rocket + ctx.beginPath(); + ctx.arc(this.position.x - Math.cos(this.angle) * (30 * size - 3) + (Math.random() - 0.5) * 4, + this.position.y - Math.sin(this.angle) * (30 * size - 3) + (Math.random() - 0.5) * 4, + 11 * size, 0, 2 * Math.PI); + ctx.fillStyle = "rgba(255,155,0,0.5)"; + ctx.fill(); + } else { + //draw rocket with time stop + ctx.beginPath(); + ctx.arc(this.position.x - Math.cos(this.angle) * (30 * size - 3) + (Math.random() - 0.5) * 4, + this.position.y - Math.sin(this.angle) * (30 * size - 3) + (Math.random() - 0.5) * 4, + 11 * size, 0, 2 * Math.PI); + ctx.fillStyle = "rgba(255,155,0,0.5)"; + ctx.fill(); + } + } + }, mine(where, velocity, angle = 0, isAmmoBack = false) { const bIndex = bullet.length; bullet[bIndex] = Bodies.rectangle(where.x, where.y, 45 * b.modBulletSize, 16 * b.modBulletSize, { @@ -2090,93 +2219,15 @@ const b = { fireCycle: 0, ammoLoaded: 0, fire() { - let dir = mech.angle + (0.5 - Math.random()) * (mech.crouch ? 0 : 0.2); - const me = bullet.length; - bullet[me] = Bodies.rectangle(mech.pos.x + 40 * Math.cos(mech.angle), mech.pos.y + 40 * Math.sin(mech.angle) - 3, 30 * b.modBulletSize, 4 * b.modBulletSize, b.fireAttributes(dir)); - const thrust = 0.00417 * bullet[me].mass; - b.fireProps(mech.crouch ? 50 : 25, -3 * (0.5 - Math.random()) + (mech.crouch ? 25 : -8), dir, me); //cd , speed - // bullet[me].collisionFilter.mask = cat.map | cat.body | cat.mobBullet - // Matter.Body.setDensity(bullet[me], 0.01) //doesn't help with reducing explosion knock backs - 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 + 60 * Math.random(); - bullet[me].lookFrequency = Math.floor(21 + Math.random() * 7); - bullet[me].onEnd = function () { - b.explosion(this.position, this.explodeRad); //makes bullet do explosive damage at end - } - bullet[me].onDmg = function () { - this.tryToLockOn(); - // this.endCycle = 0; //bullet ends cycle after doing damage // also triggers explosion - }; - bullet[me].lockedOn = null; - bullet[me].tryToLockOn = function () { - this.lockedOn = null; - let closeDist = Infinity; - - //look for closest target to where the missile will be in 30 cycles - const futurePos = Vector.add(this.position, Vector.mult(this.velocity, 30)) - for (let i = 0, len = mob.length; i < len; ++i) { - if ( - mob[i].alive && mob[i].dropPowerUp && - Matter.Query.ray(map, this.position, mob[i].position).length === 0 && - Matter.Query.ray(body, this.position, mob[i].position).length === 0 - ) { - const futureDist = Vector.magnitude(Vector.sub(futurePos, mob[i].position)); - if (futureDist < closeDist) { - closeDist = futureDist; - this.lockedOn = mob[i]; - this.frictionAir = 0.05; //extra friction once a target it locked - } - } - } - //explode when bullet is close enough to target - if (this.lockedOn && Vector.magnitude(Vector.sub(this.position, this.lockedOn.position)) < this.explodeRad) { - // console.log('hit') - this.endCycle = 0; //bullet ends cycle after doing damage //also triggers explosion - const dmg = b.dmgScale * 5; - this.lockedOn.damage(dmg); //does extra damage to target - } - }; - bullet[me].do = function () { - if (!mech.isBodiesAsleep) { - if (!(mech.cycle % this.lookFrequency)) { - this.tryToLockOn(); - } - - //rotate missile towards the target - if (this.lockedOn) { - const face = { - x: Math.cos(this.angle), - y: Math.sin(this.angle) - }; - const target = Vector.normalise(Vector.sub(this.position, this.lockedOn.position)); - if (Vector.dot(target, face) > -0.98) { - if (Vector.cross(target, face) > 0) { - Matter.Body.rotate(this, 0.08); - } else { - Matter.Body.rotate(this, -0.08); - } - } - } - //accelerate in direction bullet is facing - const dir = this.angle; // + (Math.random() - 0.5); - this.force.x += Math.cos(dir) * thrust; - this.force.y += Math.sin(dir) * thrust; - - //draw rocket - ctx.beginPath(); - ctx.arc(this.position.x - Math.cos(this.angle) * 27 + (Math.random() - 0.5) * 4, this.position.y - Math.sin(this.angle) * 27 + (Math.random() - 0.5) * 4, 11, 0, 2 * Math.PI); - ctx.fillStyle = "rgba(255,155,0,0.5)"; - ctx.fill(); - } else { - //draw rocket with time stop - ctx.beginPath(); - ctx.arc(this.position.x - Math.cos(this.angle) * 27, this.position.y - Math.sin(this.angle) * 27, 11, 0, 2 * Math.PI); - ctx.fillStyle = "rgba(255,155,0,0.5)"; - ctx.fill(); - } - } + mech.fireCDcycle = mech.cycle + Math.floor(mech.crouch ? 50 : 25); // cool down + b.missile({ + x: mech.pos.x + 40 * Math.cos(mech.angle), + y: mech.pos.y + 40 * Math.sin(mech.angle) - 3 + }, + mech.angle + (0.5 - Math.random()) * (mech.crouch ? 0 : 0.2), + -3 * (0.5 - Math.random()) + (mech.crouch ? 25 : -8) * b.modFireRate, + 1, b.modBabyMissiles) + bullet[bullet.length - 1].force.y += 0.0006; //a small push down at first to make it seem like the missile is briefly falling } }, { @@ -2429,7 +2480,7 @@ const b = { name: "drones", //11 description: "deploy drones that crash into mobs
collisions reduce their lifespan by 1 second", ammo: 0, - ammoPack: (game.difficultyMode > 3) ? 8 : 10, + ammoPack: 10, have: false, isStarterGun: true, fire() { diff --git a/js/level.js b/js/level.js index 17ac7fd..15a1bf3 100644 --- a/js/level.js +++ b/js/level.js @@ -14,9 +14,9 @@ const level = { start() { if (level.levelsCleared === 0) { // level.difficultyIncrease(9) - // b.giveGuns("rail gun") - // mech.setField("nano-scale manufacturing") - // b.giveMod("fléchettes multishot"); + // b.giveGuns("missiles") + // mech.setField("negative mass field") + // b.giveMod("self-replication"); level.intro(); //starting level // level.testingMap(); diff --git a/js/player.js b/js/player.js index ebae8a3..1fee99e 100644 --- a/js/player.js +++ b/js/player.js @@ -1389,7 +1389,6 @@ const mech = { if (mech.energy > DRAIN) { mech.grabPowerUp(); mech.lookForPickUp(); - mech.pushMobs360(); //look for nearby objects to make zero-g function zeroG(who, range, mag = 1.06) { for (let i = 0, len = who.length; i < len; ++i) { @@ -1441,15 +1440,38 @@ const mech = { ctx.fillStyle = "#f5f5ff"; ctx.globalCompositeOperation = "difference"; ctx.fill(); - ctx.globalCompositeOperation = "source-over"; - if (b.isModHawking) { for (let i = 0, len = mob.length; i < len; i++) { - if (Vector.magnitude(Vector.sub(mob[i].position, mech.pos)) < this.fieldDrawRadius) { + if (mob[i].distanceToPlayer2() < this.fieldDrawRadius * this.fieldDrawRadius && Matter.Query.ray(map, mech.pos, mob[i].position).length === 0 && Matter.Query.ray(body, mech.pos, mob[i].position).length === 0) { + mob[i].damage(b.dmgScale * 0.09); + mob[i].locatePlayer(); + + //draw electricity + const sub = Vector.sub(mob[i].position, mech.pos) + const unit = Vector.normalise(sub); + const steps = 6 + const step = Vector.magnitude(sub) / steps; + ctx.beginPath(); + let x = mech.pos.x + 30 * unit.x; + let y = mech.pos.y + 30 * unit.y; + ctx.moveTo(x, y); + for (let i = 0; i < steps; i++) { + x += step * (unit.x + 0.7 * (Math.random() - 0.5)) + y += step * (unit.y + 0.7 * (Math.random() - 0.5)) + ctx.lineTo(x, y); + } + ctx.lineWidth = 1; + ctx.strokeStyle = "rgba(0,255,0,0.5)" //"#fff"; + ctx.stroke(); } } + } else { + mech.pushMobs360(); } + ctx.globalCompositeOperation = "source-over"; + + } else { //trigger cool down @@ -1514,11 +1536,21 @@ const mech = { if (mech.energy > mech.fieldEnergyMax - 0.02 && mech.fieldCDcycle < mech.cycle) { mech.fieldCDcycle = mech.cycle + 17; // set cool down to prevent +energy from making huge numbers of drones if (b.isModSporeField) { - const len = Math.floor(6 + 3 * Math.random()) - mech.energy -= len * 0.12; + const len = Math.floor(7 + 3 * Math.random()) + mech.energy -= len * 0.1; for (let i = 0; i < len; i++) { b.spore(player) } + } + if (b.isModMissileField) { + mech.energy -= 0.55; + b.missile({ + x: mech.pos.x + 40 * Math.cos(mech.angle), + y: mech.pos.y + 40 * Math.sin(mech.angle) - 3 + }, + mech.angle + (0.5 - Math.random()) * (mech.crouch ? 0 : 0.2), + -3 * (0.5 - Math.random()) + (mech.crouch ? 25 : -8) * b.modFireRate, + 1, b.modBabyMissiles) } else { mech.energy -= 0.33; b.drone(1) diff --git a/todo.txt b/todo.txt index aa10058..13eaa1d 100644 --- a/todo.txt +++ b/todo.txt @@ -1,5 +1,13 @@ ************** TODO - n-gon ************** +mod - missiles release smaller missiles after 2s hits + +mod - grenades release smaller explosions + +mod - rocket propelled grenades + grenades do +20% damage and move in an accelerating path + maybe explode on collision with map? + mod - negative mass field does damage to mobs in zone mod - flechettes mod for poison damage