From 7ef99c13e915d5b41869400cefa0d55d19eb6bab Mon Sep 17 00:00:00 2001 From: landgreen Date: Sun, 5 Jan 2020 17:31:30 -0800 Subject: [PATCH] shield rebalance, difficulty rebalance, piezo and static mods --- js/bullets.js | 298 +++++++++++++++++++++---------------------------- js/engine.js | 5 +- js/game.js | 89 ++++++--------- js/index.js | 8 +- js/level.js | 37 +++--- js/mobs.js | 21 ++-- js/player.js | 18 +-- js/powerups.js | 2 +- js/spawn.js | 133 +++++++++++----------- 9 files changed, 278 insertions(+), 333 deletions(-) diff --git a/js/bullets.js b/js/bullets.js index e2c6880..571a6fb 100644 --- a/js/bullets.js +++ b/js/bullets.js @@ -29,6 +29,8 @@ const b = { isModFourOptions: null, modGuardianCount: null, modCollisionImmuneCycles: null, + modBlockDmg: null, + modPiezo: null, setModDefaults() { b.modCount = 0; b.modFireRate = 1; @@ -55,6 +57,8 @@ const b = { b.isModMassEnergy = false; b.modGuardianCount = 0; b.modCollisionImmuneCycles = 30; + b.modBlockDmg = 0; + b.modPiezo = 0; mech.Fx = 0.015; mech.jumpForce = 0.38; mech.maxHealth = 1; @@ -64,9 +68,8 @@ const b = { } }, mods: [{ - name: "depleted uranium rounds", + name: "depleted uranium rounds", //0 description: `your bullets are +11% larger
increased mass and physical damage`, - //0 count: 0, maxCount: 4, effect() { @@ -74,131 +77,127 @@ const b = { } }, { - name: "fluoroantimonic acid", + name: "fluoroantimonic acid", //1 description: "each bullet does extra chemical damage
instant damage, unaffected by momentum", - //1 maxCount: 4, count: 0, - effect() { //good with guns that fire many bullets at low speeds, minigun, drones, junk-bots, shotgun, superballs, wavebeam + effect() { b.modExtraDmg += 0.25 game.playerDmgColor = "rgba(0,80,80,0.9)" } }, { - name: "fracture analysis", + name: "fracture analysis", //2 description: "5x physical damage to unaware enemies
unaware enemies don't have a health bar", - //2 maxCount: 1, count: 0, - effect() { // good with high damage guns that strike from a distance: rail gun, drones, flechettes, spores, grenade, vacuum bomb + effect() { b.isModCrit = true; } }, { - name: "kinetic bombardment", + name: "kinetic bombardment", //3 description: "do extra damage from a distance
up to 50% increase at about 30 steps away", - //3 maxCount: 1, count: 0, - effect() { // good with annihilation, melee builds + effect() { b.isModFarAwayDmg = true; //used in mob.damage() } }, { - name: "quasistatic equilibrium", + name: "quasistatic equilibrium", //4 description: "do extra damage at low health
up to 50% increase when near death", - //4 maxCount: 1, count: 0, - effect() { // good with annihilation, melee builds + effect() { b.isModLowHealthDmg = true; //used in mob.damage() } }, { - name: "auto-loading heuristics", + name: "auto-loading heuristics", //5 description: "your delay after firing is +12% shorter", - //5 maxCount: 4, count: 0, - effect() { //good for guns with extra ammo: needles, M80, rapid fire, flak, super balls + effect() { b.modFireRate *= 0.88 } }, { - name: "desublimated ammunition", + name: "desublimated ammunition", //6 description: "use 50% less ammo when crouching", - //6 maxCount: 1, count: 0, - effect() { //good with guns that have less ammo: one shot, grenades, missiles, super balls, spray + effect() { b.modNoAmmo = 1 } }, { - name: "Lorentzian topology", + name: "Lorentzian topology", //7 description: "your bullets last +33% longer", - //7 maxCount: 4, count: 0, - effect() { //good with: drones, super balls, spore, missiles, wave beam(range), rapid fire(range), flak(range) + effect() { b.isModBulletsLastLonger += 0.33 } }, { - name: "zoospore vector", + name: "zoospore vector", //8 description: "enemies discharge spores on death
+11% chance", - //8 maxCount: 4, count: 0, - effect() { //good late game maybe? + effect() { b.modSpores += 0.11; } }, { - name: "ablative synthesis", + name: "ablative synthesis", //9 description: "rebuild your broken parts as drones
chance to occur after being harmed", - //9 maxCount: 1, count: 0, - effect() { //makes dangerous situations more survivable + effect() { b.isModDroneOnDamage = true; } }, { - name: "guardian", + name: "guardian", //10 description: "a bot protects the space around you
uses a short range laser that drains energy", - //10 maxCount: 4, count: 0, - effect() { // good with melee builds, content skipping builds + effect() { b.modGuardianCount++; b.guardian(); } }, { - name: "piezoelectric plating", + name: "electrostatic repulsion", //11 description: "immune to harm from collisions for +2 seconds
activates after being harmed from a collision", - //11 maxCount: 1, count: 0, - effect() { // good with melee builds, content skipping builds + effect() { b.modCollisionImmuneCycles += 120; } }, { - name: "annihilation", + name: "bremsstrahlung radiation", //12 + description: "when your field blocks it also does damage", + maxCount: 4, + count: 0, + effect() { + b.modBlockDmg += 0.7 + } + }, + { + name: "annihilation", //13 description: "after touching enemies, they are annihilated", - //12 maxCount: 1, count: 0, - effect() { //good with mods that heal: superconductive healing, entropy transfer + effect() { b.isModAnnihilation = true } }, { - name: "high explosives", + name: "high explosives", //14 description: "the radius of explosions are +20% larger
immune to harm from explosions", - //13 maxCount: 4, count: 0, effect: () => { @@ -207,39 +206,44 @@ const b = { } }, { - name: "entanglement", + name: "entanglement", //15 description: "using your first gun reduces harm
scales by 7% for each gun in your inventory", - //14 maxCount: 1, count: 0, - effect() { // good with laser-bots + effect() { b.isModEntanglement = true } }, { - name: "energy transfer", - description: "gain energy proportional to damage done", - //15 + name: "piezoelectricity", //16 + description: "gain energy proportional to harm received", maxCount: 4, count: 0, - effect() { //good with laser, and all fields + effect() { + b.modPiezo += 2 + } + }, + { + name: "energy conservation", //17 + description: "gain energy proportional to damage done", + maxCount: 4, + count: 0, + effect() { b.modEnergySiphon += 0.18; } }, { - name: "entropy transfer", + name: "entropy exchange", //18 description: "heal proportional to damage done", - //16 maxCount: 4, count: 0, - effect() { //good with guns that overkill: one shot, grenade + effect() { b.modHealthDrain += 0.015; } }, { - name: "overcharge", + name: "overcharge", //19 description: "charge energy +33% beyond your maximum", - //17 maxCount: 4, count: 0, effect() { @@ -247,9 +251,8 @@ const b = { } }, { - name: "supersaturation", + name: "supersaturation", //20 description: "heal +33% beyond your max health", - //18 maxCount: 4, count: 0, effect() { @@ -257,19 +260,17 @@ const b = { } }, { - name: "recursive healing", + name: "recursive healing", //21 description: "healing power ups trigger an extra time.", - //19 maxCount: 4, count: 0, - effect() { // good with ablative synthesis, melee builds + effect() { b.modRecursiveHealing += 1 } }, { - name: "mass-energy equivalence", + name: "mass-energy equivalence", //22 description: "convert the mass of power ups into energy
power ups fill your energy and heal for +5%", - //20 maxCount: 1, count: 0, effect: () => { @@ -277,9 +278,8 @@ const b = { } }, { - name: "+1 cardinality", + name: "+1 cardinality", //23 description: "one extra choice when selecting power ups", - //21 maxCount: 1, count: 0, effect: () => { @@ -287,31 +287,17 @@ const b = { } }, { - name: "Bayesian inference", + name: "Bayesian inference", //24 description: "20% chance for double power ups to drop
one fewer choice when selecting power ups", - //22 maxCount: 1, count: 0, effect: () => { b.isModBayesian = 0.20; } }, - // { - // name: "Gauss rifle", - // description: "launch blocks at much higher speeds
hold onto larger blocks even after getting hit", - // //23 - // maxCount: 1, - // count: 0, - // effect() { // good with guns that run out of ammo - // mech.throwChargeRate = 4; - // mech.throwChargeMax = 150; - // mech.holdingMassScale = 0.05; //can hold heavier blocks with lower cost to jumping - // } - // }, { - name: "squirrel-cage rotor", + name: "squirrel-cage rotor", //25 description: "jump higher and move faster
reduced harm from falling ", - //23 maxCount: 1, count: 0, effect() { // good with melee builds, content skipping builds @@ -321,9 +307,8 @@ const b = { } }, { - name: "quantum immortality", + name: "quantum immortality", //26 description: "after dying, continue in an alternate reality
guns, ammo, and field are randomized", - //24 maxCount: 1, count: 0, effect() { @@ -488,7 +473,7 @@ const b = { time: game.drawTime }); let dist, sub, knock; - const dmg = b.dmgScale * radius * 0.009; + let dmg = b.dmgScale * radius * 0.009; const alertRange = 100 + radius * 2; //alert range //add alert to draw queue @@ -549,20 +534,21 @@ const b = { //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) { + if (mob[i].alive && !mob[i].isShielded) { sub = Vector.sub(where, mob[i].position); dist = Vector.magnitude(sub) - mob[i].radius; if (dist < radius) { + if (mob[i].shield) dmg *= 3 //balancing explosion dmg to shields mob[i].damage(dmg * damageScale); mob[i].locatePlayer(); - knock = Vector.mult(Vector.normalise(sub), (-Math.sqrt(dmg * damageScale) * mob[i].mass) / 30); + knock = Vector.mult(Vector.normalise(sub), (-Math.sqrt(dmg * damageScale) * mob[i].mass) / 50); mob[i].force.x += knock.x; mob[i].force.y += knock.y; radius *= 0.93 //reduced range for each additional explosion target damageScale *= 0.8 //reduced damage for each additional explosion target } else if (!mob[i].seePlayer.recall && dist < alertRange) { mob[i].locatePlayer(); - knock = Vector.mult(Vector.normalise(sub), (-Math.sqrt(dmg * damageScale) * mob[i].mass) / 50); + knock = Vector.mult(Vector.normalise(sub), (-Math.sqrt(dmg * damageScale) * mob[i].mass) / 80); mob[i].force.x += knock.x; mob[i].force.y += knock.y; } @@ -581,7 +567,7 @@ const b = { time: game.drawTime }); let dist, sub, knock; - const dmg = b.dmgScale * radius * 0.009; + let dmg = b.dmgScale * radius * 0.009; const alertRange = 100 + radius * 2; //alert range //add alert to draw queue @@ -642,20 +628,21 @@ const b = { //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) { + if (mob[i].alive && !mob[i].isShielded) { sub = Vector.sub(bullet[me].position, mob[i].position); dist = Vector.magnitude(sub) - mob[i].radius; if (dist < radius) { + if (mob[i].shield) dmg *= 3 //balancing explosion dmg to shields mob[i].damage(dmg * damageScale); mob[i].locatePlayer(); - knock = Vector.mult(Vector.normalise(sub), (-Math.sqrt(dmg * damageScale) * mob[i].mass) / 30); + knock = Vector.mult(Vector.normalise(sub), (-Math.sqrt(dmg * damageScale) * mob[i].mass) / 50); mob[i].force.x += knock.x; mob[i].force.y += knock.y; radius *= 0.93 //reduced range for each additional explosion target damageScale *= 0.8 //reduced damage for each additional explosion target } else if (!mob[i].seePlayer.recall && dist < alertRange) { mob[i].locatePlayer(); - knock = Vector.mult(Vector.normalise(sub), (-Math.sqrt(dmg * damageScale) * mob[i].mass) / 50); + knock = Vector.mult(Vector.normalise(sub), (-Math.sqrt(dmg * damageScale) * mob[i].mass) / 80); mob[i].force.x += knock.x; mob[i].force.y += knock.y; } @@ -837,7 +824,7 @@ const b = { restitution: 0.5 + 0.5 * Math.random(), dmg: 0, // 0.14 //damage done in addition to the damage from momentum minDmgSpeed: 2, - lookFrequency: 37 + Math.floor(17 * Math.random()), + lookFrequency: 31 + Math.floor(17 * Math.random()), acceleration: 0.0015 + 0.0013 * Math.random(), range: 500 + Math.floor(200 * Math.random()), endCycle: Infinity, @@ -858,6 +845,7 @@ const b = { for (let i = 0, len = mob.length; i < len; ++i) { const DIST = Vector.magnitude(Vector.sub(this.vertices[0], mob[i].position)); if (DIST - mob[i].radius < closeDist && + !mob[i].isShielded && Matter.Query.ray(map, this.vertices[0], mob[i].position).length === 0 && Matter.Query.ray(body, this.vertices[0], mob[i].position).length === 0) { closeDist = DIST; @@ -951,17 +939,18 @@ const b = { name: "minigun", //0 description: "rapidly fire a stream of small bullets", ammo: 0, - ammoPack: 50, + ammoPack: 55, 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.04 : 0.12); - 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 ? 10 : 5, mech.crouch ? 50 : 36, dir, me); //cd , speed - bullet[me].endCycle = game.cycle + 65; + const dir = mech.angle + (Math.random() - 0.5) * ((mech.crouch) ? 0.03 : 0.1); + bullet[me] = Bodies.rectangle(mech.pos.x + 30 * Math.cos(mech.angle), mech.pos.y + 30 * Math.sin(mech.angle), 20 * b.modBulletSize, 6 * b.modBulletSize, b.fireAttributes(dir)); + b.fireProps(mech.crouch ? 8 : 4, mech.crouch ? 52 : 38, dir, me); //cd , speed + bullet[me].endCycle = game.cycle + 70; + bullet[me].dmg = 0.07; bullet[me].frictionAir = mech.crouch ? 0.007 : 0.01; bullet[me].do = function () { this.force.y += this.mass * 0.0005; @@ -972,7 +961,7 @@ const b = { name: "shotgun", //1 description: "fire a burst of short range bullets
crouch to reduce recoil", ammo: 0, - ammoPack: 4, + ammoPack: 5, have: false, isStarterGun: true, fire() { @@ -999,7 +988,7 @@ const b = { } //knock back - const KNOCK = ((mech.crouch) ? 0.017 : 0.17) * b.modBulletSize * b.modBulletSize + const KNOCK = ((mech.crouch) ? 0.013 : 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 } @@ -1043,7 +1032,7 @@ const b = { name: "fléchettes", //3 description: "fire a volley of precise high velocity needles", ammo: 0, - ammoPack: 22, + ammoPack: 20, have: false, isStarterGun: true, count: 0, //used to track how many shots are in a volley before a big CD @@ -1095,7 +1084,7 @@ const b = { inertia: Infinity, frictionAir: 0, minDmgSpeed: 0, - dmg: 0.13, //damage done in addition to the damage from momentum + dmg: 0.2, //damage done in addition to the damage from momentum classType: "bullet", collisionFilter: { category: cat.bullet, @@ -1134,7 +1123,7 @@ const b = { 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: 2, + ammoPack: 1, have: false, isStarterGun: false, fire() { @@ -1208,18 +1197,6 @@ const b = { mob[i].force.y += 1.5 * FORCE.y; } } - //push mobs around player when firing - // range = 600 * this.charge - // for (let i = 0, len = mob.length; i < len; ++i) { - // const SUB = Vector.sub(mob[i].position, mech.pos) - // const DISTANCE = Vector.magnitude(SUB) - // if (DISTANCE < range) { - // const DEPTH = range - DISTANCE - // const FORCE = Vector.mult(Vector.normalise(SUB), 0.00000001 * DEPTH * DEPTH * DEPTH * Math.sqrt(mob[i].mass)) - // mob[i].force.x += FORCE.x - // mob[i].force.y += FORCE.y - // } - // } } else { // charging on mouse down mech.fireCDcycle = Infinity //can't fire until mouse is released if (mech.crouch) { @@ -1227,25 +1204,6 @@ const b = { } else { this.charge = this.charge * 0.985 + 0.015 // this.charge converges to 1 } - - //gently push away mobs while charging - // const RANGE = 270 * this.charge - // for (let i = 0, len = mob.length; i < len; ++i) { - // const SUB = Vector.sub(mob[i].position, mech.pos) - // const DISTANCE = Vector.magnitude(SUB) - // // if (DISTANCE < RANGE) { - // // Matter.Body.setVelocity(mob[i], Vector.rotate(mob[i].velocity, 0.1)) - // // } - // // const DRAIN = 0.0002 //&& mech.fieldMeter > DRAIN - // if (DISTANCE < RANGE) { - // // mech.fieldMeter -= DRAIN + mech.fieldRegen; - // const DEPTH = RANGE - DISTANCE - // const FORCE = Vector.mult(Vector.normalise(SUB), 0.000000001 * DEPTH * DEPTH * DEPTH * Math.sqrt(mob[i].mass)) - // mob[i].force.x += FORCE.x - // mob[i].force.y += FORCE.y - // } - // } - //draw laser targeting let best; let range = 3000 @@ -1377,50 +1335,51 @@ const b = { 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)); b.fireProps(mech.crouch ? 55 : 30, -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 = 160 + 60 * Math.random(); - bullet[me].lookFrequency = Math.floor(15 + Math.random() * 5); + bullet[me].explodeRad = 170 + 60 * Math.random(); + bullet[me].lookFrequency = Math.floor(31 + Math.random() * 11); 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 + 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 * 0.95) { + // 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.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)) - // ctx.beginPath(); //draw future pos - // ctx.arc(futurePos.x, futurePos.y, 20, 0, 2 * Math.PI); - // ctx.fillStyle = "rgba(0,0,0,0.5)"; - // ctx.fill(); - 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 * 0.7) { - 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 - } + this.tryToLockOn(); } //rotate missile towards the target @@ -1754,12 +1713,12 @@ const b = { name: "drones", //12 description: "deploy drones that crash into enemies
collisions reduce drone cycles by 1 second", ammo: 0, - ammoPack: 6, + ammoPack: 5, have: false, isStarterGun: true, fire() { b.drone(mech.crouch ? 45 : 1) - mech.fireCDcycle = mech.cycle + Math.floor((mech.crouch ? 25 : 10) * b.modFireRate); // cool down + mech.fireCDcycle = mech.cycle + Math.floor((mech.crouch ? 25 : 5) * b.modFireRate); // cool down } }, // { @@ -2155,7 +2114,7 @@ const b = { const RADIUS = (8 + 16 * Math.random()) * b.modBulletSize bullet[me] = Bodies.polygon(mech.pos.x + 30 * Math.cos(mech.angle), mech.pos.y + 30 * Math.sin(mech.angle), 25, RADIUS, { angle: dir, - density: 0.00004, // 0.001 is normal density + density: 0.00005, // 0.001 is normal density inertia: Infinity, frictionAir: 0.003, friction: 0.2, @@ -2173,7 +2132,7 @@ const b = { target: null, targetVertex: null, onDmg(who) { - if (!this.target && who.alive && who.dropPowerUp) { + if (!this.target && who.alive && who.dropPowerUp && !who.isShielded) { this.target = who; this.collisionFilter.category = cat.body; this.collisionFilter.mask = null; @@ -2216,9 +2175,9 @@ const b = { if (this.target && this.target.alive) { //if stuck to a target Matter.Body.setPosition(this, this.target.vertices[this.targetVertex]) - Matter.Body.setVelocity(this.target, Vector.mult(this.target.velocity, 0.94)) - Matter.Body.setAngularVelocity(this.target, this.target.angularVelocity * 0.94) - this.target.damage(b.dmgScale * 0.0045); + Matter.Body.setVelocity(this.target, Vector.mult(this.target.velocity, 0.9)) + Matter.Body.setAngularVelocity(this.target, this.target.angularVelocity * 0.9) + this.target.damage(b.dmgScale * 0.005); } else if (this.target !== null) { //look for a new target this.target = null this.collisionFilter.category = cat.bullet; @@ -2301,6 +2260,7 @@ const b = { } } }; + const checkForCollisions = function () { best = { x: null, diff --git a/js/engine.js b/js/engine.js index 40cfbe5..8fc27cd 100644 --- a/js/engine.js +++ b/js/engine.js @@ -126,7 +126,7 @@ function mobCollisionChecks(event) { let dmg = Math.min(Math.max(0.025 * Math.sqrt(mob[k].mass), 0.05), 0.3) * game.dmgScale; //player damage is capped at 0.3*dmgScale of 1.0 mech.damage(dmg); if (mob[k].onHit) mob[k].onHit(k); - if (b.isModAnnihilation && mob[k].dropPowerUp) { + if (b.isModAnnihilation && mob[k].dropPowerUp && !mob[k].isShielded) { mob[k].death(); game.drawList.push({ //add dmg to draw queue @@ -164,8 +164,7 @@ function mobCollisionChecks(event) { if (obj.classType === "bullet" && obj.speed > obj.minDmgSpeed) { // const dmg = b.dmgScale * (obj.dmg + 0.15 * obj.mass * Vector.magnitude(Vector.sub(mob[k].velocity, obj.velocity))); let dmg = b.dmgScale * (obj.dmg + b.modExtraDmg + 0.15 * obj.mass * Vector.magnitude(Vector.sub(mob[k].velocity, obj.velocity))) - if (mob[k].shield) dmg *= 0.3 - if (b.isModCrit && !mob[k].seePlayer.recall) dmg *= 5 + if (b.isModCrit && !mob[k].seePlayer.recall && !mob[k].shield) dmg *= 5 mob[k].foundPlayer(); mob[k].damage(dmg); obj.onDmg(mob[k]); //some bullets do actions when they hits things, like despawn diff --git a/js/game.js b/js/game.js index 7820286..c14aeb6 100644 --- a/js/game.js +++ b/js/game.js @@ -81,7 +81,8 @@ const game = { isBodyDamage: true, levelsCleared: 0, difficultyMode: 1, - difficulty: 1, + isEasyMode: false, + difficulty: 0, dmgScale: null, //set in levels.setDifficulty healScale: 1, accelScale: null, //set in levels.setDifficulty @@ -416,24 +417,6 @@ const game = { }, wipe() { ctx.clearRect(0, 0, canvas.width, canvas.height); - - // ctx.fillStyle = "rgba(255,255,255,1)"; - // ctx.globalCompositeOperation = "difference"; - // ctx.fillRect(0, 0, canvas.width, canvas.height); - // ctx.globalCompositeOperation = "source-over"; - - // ctx.globalAlpha = (mech.health < 0.7) ? (mech.health+0.3)*(mech.health+0.3) : 1 - // if (mech.health < 0.7) { - // ctx.globalAlpha= 0.3 + mech.health - // ctx.fillStyle = document.body.style.backgroundColor - // ctx.fillRect(0, 0, canvas.width, canvas.height); - // ctx.globalAlpha=1; - // } else { - // ctx.clearRect(0, 0, canvas.width, canvas.height); - // } - //ctx.fillStyle = "rgba(255,255,255," + (1 - Math.sqrt(player.speed)*0.1) + ")"; - //ctx.fillStyle = "rgba(255,255,255,0.4)"; - //ctx.fillRect(0, 0, canvas.width, canvas.height); }, gravity() { function addGravity(bodies, magnitude) { @@ -474,8 +457,10 @@ const game = { game.accelScale = 1; game.lookFreqScale = 1; game.CDScale = 1; + game.difficulty = 0; game.difficultyMode = Number(document.getElementById("difficulty-select").value) if (game.difficultyMode === 0) { + game.isEasyMode = true; game.difficultyMode = 1 level.difficultyDecrease(6); } @@ -693,7 +678,7 @@ const game = { testingOutput() { ctx.textAlign = "right"; ctx.fillStyle = "#000"; - let line = 140; + let line = 500; const x = canvas.width - 5; ctx.fillText("T: exit testing mode", x, line); line += 20; @@ -712,38 +697,38 @@ const game = { ctx.fillText("1-7: spawn things", x, line); line += 30; - ctx.fillText("cycle: " + game.cycle, x, line); - line += 20; - ctx.fillText("player cycle: " + mech.cycle, x, line); - line += 20; - ctx.fillText("x: " + player.position.x.toFixed(0), x, line); - line += 20; - ctx.fillText("y: " + player.position.y.toFixed(0), x, line); - line += 20; - ctx.fillText("Vx: " + mech.Vx.toFixed(2), x, line); - line += 20; - ctx.fillText("Vy: " + mech.Vy.toFixed(2), x, line); - line += 20; - ctx.fillText("Fx: " + player.force.x.toFixed(3), x, line); - line += 20; - ctx.fillText("Fy: " + player.force.y.toFixed(3), x, line); - line += 20; - ctx.fillText("yOff: " + mech.yOff.toFixed(1), x, line); - line += 20; - ctx.fillText("mass: " + player.mass.toFixed(1), x, line); - line += 20; - ctx.fillText("onGround: " + mech.onGround, x, line); - line += 20; - ctx.fillText("crouch: " + mech.crouch, x, line); - line += 20; - ctx.fillText("isHeadClear: " + mech.isHeadClear, x, line); - line += 20; - ctx.fillText("frictionAir: " + player.frictionAir.toFixed(3), x, line); - line += 20; - ctx.fillText("stepSize: " + mech.stepSize.toFixed(2), x, line); - line += 20; - ctx.fillText("zoom: " + game.zoom.toFixed(4), x, line); - line += 20; + // ctx.fillText("cycle: " + game.cycle, x, line); + // line += 20; + // ctx.fillText("player cycle: " + mech.cycle, x, line); + // line += 20; + // ctx.fillText("x: " + player.position.x.toFixed(0), x, line); + // line += 20; + // ctx.fillText("y: " + player.position.y.toFixed(0), x, line); + // line += 20; + // ctx.fillText("Vx: " + mech.Vx.toFixed(2), x, line); + // line += 20; + // ctx.fillText("Vy: " + mech.Vy.toFixed(2), x, line); + // line += 20; + // ctx.fillText("Fx: " + player.force.x.toFixed(3), x, line); + // line += 20; + // ctx.fillText("Fy: " + player.force.y.toFixed(3), x, line); + // line += 20; + // ctx.fillText("yOff: " + mech.yOff.toFixed(1), x, line); + // line += 20; + // ctx.fillText("mass: " + player.mass.toFixed(1), x, line); + // line += 20; + // ctx.fillText("onGround: " + mech.onGround, x, line); + // line += 20; + // ctx.fillText("crouch: " + mech.crouch, x, line); + // line += 20; + // ctx.fillText("isHeadClear: " + mech.isHeadClear, x, line); + // line += 20; + // ctx.fillText("frictionAir: " + player.frictionAir.toFixed(3), x, line); + // line += 20; + // ctx.fillText("stepSize: " + mech.stepSize.toFixed(2), x, line); + // line += 20; + // ctx.fillText("zoom: " + game.zoom.toFixed(4), x, line); + // line += 20; ctx.textAlign = "center"; ctx.fillText(`(${game.mouseInGame.x.toFixed(1)}, ${game.mouseInGame.y.toFixed(1)})`, game.mouse.x, game.mouse.y - 20); }, diff --git a/js/index.js b/js/index.js index 82515cd..f1c25f2 100644 --- a/js/index.js +++ b/js/index.js @@ -2,8 +2,12 @@ /* TODO: ******************************************* ***************************************************** -mod: fields do damage on blocking - name: something about radiation? +add new power up class: rerolls + let you repopulate a power up selection menu + +weekly random challenge where everyone playing each week gets the same random setup. + The randomness would be determined by the date so it would sync everyone. + powerups still drop, but the drops are determined by a preset list that changes each week. mod: do something at the end of each level heal to full diff --git a/js/level.js b/js/level.js index c7d0c3a..80cb180 100644 --- a/js/level.js +++ b/js/level.js @@ -16,7 +16,7 @@ const level = { // game.difficulty = 6; //for testing to simulate possible mobs spawns // b.giveGuns(15) // mech.setField(3) - // b.giveMod(10); + b.giveMod(16); level.intro(); //starting level // level.testingMap(); @@ -45,18 +45,18 @@ const level = { // if (level.isBuildRun) num++ for (let i = 0; i < num; i++) { game.difficulty++ - game.dmgScale += 0.1; //damage done by mobs increases each level + game.dmgScale += 0.11; //damage done by mobs increases each level b.dmgScale *= 0.94; //damage done by player decreases each level game.accelScale *= 1.03 //mob acceleration increases each level game.lookFreqScale *= 0.97 //mob cycles between looks decreases each level game.CDScale *= 0.97 //mob CD time decreases each level } - game.healScale = 1 / (1 + game.difficulty * 0.05) + game.healScale = 1 / (1 + game.difficulty * 0.06) //a higher denominator makes for lower heals // mech.health += heal * game.healScale; }, difficultyDecrease(num = 1) { //used in easy mode for game.reset() for (let i = 0; i < num; i++) { game.difficulty-- - game.dmgScale -= 0.1; //damage done by mobs increases each level + game.dmgScale -= 0.11; //damage done by mobs increases each level if (game.dmgScale < 0.1) game.dmgScale = 0.1; b.dmgScale /= 0.94; //damage done by player decreases each level game.accelScale /= 1.03 //mob acceleration increases each level @@ -64,14 +64,14 @@ const level = { game.CDScale /= 0.97 //mob CD time decreases each level } if (game.difficulty < 1) game.difficulty = 1; - game.healScale = 1 / (1 + game.difficulty * 0.05) + game.healScale = 1 / (1 + game.difficulty * 0.06) }, //****************************************************************************************************************** //****************************************************************************************************************** testingMap() { //start with all guns - level.difficultyIncrease(8) - game.zoomScale = 1400 //1400 is normal + level.difficultyIncrease(9) //level 7 on normal, level 4 on hard, level 1.2 on why? + game.zoomScale = 1700 //1400 is normal spawn.setSpawnList(); mech.setPosToSpawn(-75, -60); //normal spawn level.enter.x = mech.spawnPos.x - 50; @@ -114,17 +114,17 @@ const level = { // spawn.lineBoss(-500, -600, spawn.allowedBossList[Math.floor(Math.random() * spawn.allowedBossList.length)]); // spawn.bodyRect(-135, -50, 50, 50); // spawn.bodyRect(-140, -100, 50, 50); - powerUps.spawn(420, -400, "gun", false); + // powerUps.spawn(420, -400, "gun", false); // powerUps.spawn(420, -400, "field", false); // powerUps.spawn(420, -400, "field", false); // powerUps.spawn(420, -400, "field", false); // powerUps.spawn(450, -400, "mod", false, 6); // powerUps.spawn(450, -400, "mod", false); // spawn.bodyRect(-45, -100, 40, 50); - // spawn.groupBoss(800, -1050); - // spawn.starter(400, -1050); + // spawn.bomber(800, -450); + spawn.hopper(400, -1050); // spawn.starter(1200, -1050); - // spawn.groupBoss(-600, -550); + // spawn.nodeBoss(-600, -550, "starter"); // spawn.starter(800, -150); // spawn.beamer(800, -150); // spawn.grower(800, -250); @@ -203,14 +203,14 @@ const level = { spawn.mapRect(6700, -1800, 800, 2600); //right wall spawn.mapRect(level.exit.x, level.exit.y + 20, 100, 100); //exit bump - for (let i = 0; i < 5; ++i) { - if (game.difficulty * Math.random() > 3 * i) { + for (let i = 0; i < 3; ++i) { + if (game.difficulty * Math.random() > 15 * i) { spawn.randomBoss(2000 + 500 * (Math.random() - 0.5), -800 + 200 * (Math.random() - 0.5), Infinity); } - if (game.difficulty * Math.random() > 2.6 * i) { + if (game.difficulty * Math.random() > 10 * i) { spawn.randomBoss(3500 + 500 * (Math.random() - 0.5), -800 + 200 * (Math.random() - 0.5), Infinity); } - if (game.difficulty * Math.random() > 2.4 * i) { + if (game.difficulty * Math.random() > 7 * i) { spawn.randomBoss(5000 + 500 * (Math.random() - 0.5), -800 + 200 * (Math.random() - 0.5), Infinity); } } @@ -1468,13 +1468,12 @@ const level = { nextLevel() { //enter when player isn't falling if (player.velocity.y < 0.1) { - //increase difficulty based on modes - level.difficultyIncrease(game.difficultyMode + level.isBuildRun) - //cycles map to next level level.levelsCleared++; - level.onLevel++; + level.onLevel++; //cycles map to next level if (level.onLevel > level.levels.length - 1) level.onLevel = 0; + level.difficultyIncrease(game.difficultyMode + level.isBuildRun) //increase difficulty based on modes + if (game.isEasyMode && level.levelsCleared % 2) level.difficultyDecrease(1); game.clearNow = true; //triggers in game.clearMap to remove all physics bodies and setup for new map } }, diff --git a/js/mobs.js b/js/mobs.js index 3d3f034..f09fcb4 100644 --- a/js/mobs.js +++ b/js/mobs.js @@ -932,17 +932,18 @@ const mobs = { } }, damage(dmg) { - dmg /= Math.sqrt(this.mass) - if (b.isModLowHealthDmg) dmg *= (3 / (2 + mech.health)) //up to 50% dmg at zero player health - if (b.isModFarAwayDmg) dmg *= 1 + Math.sqrt(Math.max(1000, Math.min(3500, this.distanceToPlayer())) - 1000) * 0.01 //up to 50% dmg at max range of 3500 - if (dmg !== Infinity) { - if (b.modEnergySiphon) mech.fieldMeter += Math.min(this.health, dmg) * b.modEnergySiphon - if (b.modHealthDrain) mech.addHealth(Math.min(this.health, dmg) * b.modHealthDrain) + if (!this.isShielded) { + dmg /= Math.sqrt(this.mass) + if (this.shield) dmg *= 0.04 + if (b.isModLowHealthDmg) dmg *= (3 / (2 + mech.health)) //up to 50% dmg at zero player health + if (b.isModFarAwayDmg) dmg *= 1 + Math.sqrt(Math.max(1000, Math.min(3500, this.distanceToPlayer())) - 1000) * 0.01 //up to 50% dmg at max range of 3500 + if (b.modEnergySiphon && dmg !== Infinity) mech.fieldMeter += Math.min(this.health, dmg) * b.modEnergySiphon + if (b.modHealthDrain && dmg !== Infinity) mech.addHealth(Math.min(this.health, dmg) * b.modHealthDrain) + this.health -= dmg + //this.fill = this.color + this.health + ')'; + this.onDamage(this); //custom damage effects + if (this.health < 0.05) this.death(); } - this.health -= dmg - //this.fill = this.color + this.health + ')'; - if (this.health < 0.05) this.death(); - this.onDamage(this); //custom damage effects }, onDamage() { // a placeholder for custom effects on mob damage diff --git a/js/player.js b/js/player.js index 69ab13b..d6f78b2 100644 --- a/js/player.js +++ b/js/player.js @@ -326,11 +326,7 @@ const mech = { } function randomizeField() { - if (game.difficulty * (Math.random() + 0.27) > 2) { - mech.setField(Math.floor(Math.random() * (mech.fieldUpgrades.length))) - } else { - mech.setField(0) - } + mech.setField(Math.floor(Math.random() * (mech.fieldUpgrades.length))) } function randomizeHealth() { @@ -446,6 +442,9 @@ const mech = { dmg *= 0.93 } } + if (b.modPiezo) { + mech.fieldMeter += dmg * b.modPiezo + } mech.health -= dmg; if (mech.health < 0) { mech.health = 0; @@ -792,8 +791,8 @@ const mech = { mech.fieldCDcycle = mech.cycle + 15; mech.isHolding = false; //bullet-like collisions - mech.holdingTarget.collisionFilter.category = cat.body; - mech.holdingTarget.collisionFilter.mask = cat.map | cat.body | cat.bullet | cat.mob | cat.mobBullet; + mech.holdingTarget.collisionFilter.category = cat.bullet; + mech.holdingTarget.collisionFilter.mask = cat.map | cat.body | cat.bullet | cat.mob | cat.mobBullet | cat.mobShield; //check every second to see if player is away from thrown body, and make solid const solid = function (that) { const dx = that.position.x - player.position.x; @@ -900,6 +899,9 @@ const mech = { mech.drawHold(who); mech.fieldCDcycle = mech.cycle + 10; mech.holdingTarget = null + if (b.modBlockDmg) { + who.damage(b.modBlockDmg) + } //knock backs const unit = Vector.normalise(Vector.sub(player.position, who.position)) const massRoot = Math.sqrt(Math.min(12, Math.max(0.15, who.mass))); // masses above 12 can start to overcome the push back @@ -1327,7 +1329,7 @@ const mech = { mech.holding(); mech.throwBlock(); } else if ((keys[32] || game.mouseDownRight) && mech.fieldCDcycle < mech.cycle) { //push away - const DRAIN = 0.00025 + const DRAIN = 0.00035 if (mech.fieldMeter > DRAIN) { mech.grabPowerUp(); mech.lookForPickUp(150); diff --git a/js/powerups.js b/js/powerups.js index 09cfeac..c912fd7 100644 --- a/js/powerups.js +++ b/js/powerups.js @@ -300,7 +300,7 @@ const powerUps = { size: size }); if (mode) { - console.log(mode) + // console.log(mode) powerUp[index].mode = mode } if (moving) { diff --git a/js/spawn.js b/js/spawn.js index 161fd16..1025812 100644 --- a/js/spawn.js +++ b/js/spawn.js @@ -19,23 +19,25 @@ const spawn = { "sneaker", ], allowedBossList: ["chaser", "spinner", "striker", "springer", "laser", "focuser", "beamer", "exploder", "spawner", "shooter"], - setSpawnList() { - //this is run at the start of each new level to determine the possible mobs for the level + setSpawnList() { //this is run at the start of each new level to determine the possible mobs for the level //each level has 2 mobs: one new mob and one from the last level spawn.pickList.splice(0, 1); spawn.pickList.push(spawn.fullPickList[Math.floor(Math.random() * spawn.fullPickList.length)]); }, + spawnChance(chance) { + return Math.random() < chance + 0.07 * game.difficulty && mob.length < -1 + 16 * Math.log10(game.difficulty + 1) + }, randomMob(x, y, chance = 1) { - if (Math.random() < chance + 0.09 * (game.difficulty - 1) && mob.length < 4 + game.difficulty * 1.7) { + if (spawn.spawnChance(chance)) { const pick = this.pickList[Math.floor(Math.random() * this.pickList.length)]; this[pick](x, y); } }, randomSmallMob(x, y, - num = Math.max(Math.min(Math.round(Math.random() * (game.difficulty - 1) * 0.45 - 0.4), 4), 0), + num = Math.max(Math.min(Math.round(Math.random() * game.difficulty * 0.2), 4), 0), size = 16 + Math.ceil(Math.random() * 15), chance = 1) { - if (Math.random() < chance + (game.difficulty - 1) * 0.03 && mob.length < 4 + game.difficulty * 1.7) { + if (spawn.spawnChance(chance)) { for (let i = 0; i < num; ++i) { const pick = this.pickList[Math.floor(Math.random() * this.pickList.length)]; this[pick](x + Math.round((Math.random() - 0.5) * 20) + i * size * 2.5, y + Math.round((Math.random() - 0.5) * 20), size); @@ -43,7 +45,7 @@ const spawn = { } }, randomBoss(x, y, chance = 1) { - if (Math.random() < chance + (game.difficulty - 1) * 0.14 && game.difficulty !== 1 && mob.length < 4 + game.difficulty * 2 || chance == Infinity) { + if (spawn.spawnChance(chance) && game.difficulty > 2 || chance == Infinity) { //choose from the possible picklist let pick = this.pickList[Math.floor(Math.random() * this.pickList.length)]; //is the pick able to be a boss? @@ -233,7 +235,7 @@ const spawn = { me.accelMag = 0.001 * game.accelScale;; me.g = me.accelMag * 0.6; //required if using 'gravity' me.memory = 50; - if (Math.random() < Math.min((game.difficulty - 1) * 0.05, 0.3)) spawn.shield(me, x, y); + spawn.shield(me, x, y); me.do = function () { this.gravity(); this.seePlayerCheck(); @@ -293,32 +295,12 @@ const spawn = { me.onDeath = function () { this.removeCons(); }; - if (Math.random() < Math.min((game.difficulty - 1) * 0.05, 0.3)) spawn.shield(me, x, y); + spawn.shield(me, x, y); me.do = function () { this.gravity(); this.searchSpring(); }; }, - // zoomer(x, y, radius = 20 + Math.ceil(Math.random() * 30)) { - // mobs.spawn(x, y, 6, radius, "#ffe2fd"); - // let me = mob[mob.length - 1]; - // me.trailLength = 20; //required for trails - // me.setupTrail(); //fill trails array up with the current position of mob - // me.trailFill = "#ff00f0"; - // me.g = 0.001; //required if using 'gravity' - // me.frictionAir = 0.02; - // me.accelMag = 0.004 * game.accelScale; - // me.memory = 30; - // me.zoomMode = 150; - // me.onHit = function () { - // this.zoomMode = 150; - // }; - // me.do = function () { - // this.seePlayerByDistAndLOS(); - // this.zoom(); - // this.gravity(); - // }; - // }, hopper(x, y, radius = 30 + Math.ceil(Math.random() * 30)) { mobs.spawn(x, y, 5, radius, "rgb(0,200,180)"); let me = mob[mob.length - 1]; @@ -329,6 +311,7 @@ const spawn = { me.delay = 110; me.randomHopFrequency = 50 + Math.floor(Math.random() * 1000); me.randomHopCD = game.cycle + me.randomHopFrequency; + spawn.shield(me, x, y); me.do = function () { this.gravity(); this.seePlayerCheck(); @@ -361,6 +344,7 @@ const spawn = { me.frictionAir = 0.022; me.lookTorque = 0.0000014; me.restitution = 0; + spawn.shield(me, x, y); me.do = function () { this.seePlayerByLookingAt(); //accelerate towards the player after a delay @@ -464,7 +448,7 @@ const spawn = { } } }, - suckerBoss(x, y, radius = 20) { + suckerBoss(x, y, radius = 25) { mobs.spawn(x, y, 12, radius, "#000"); let me = mob[mob.length - 1]; me.stroke = "transparent"; //used for drawSneaker @@ -493,16 +477,11 @@ const spawn = { toMe(mob, this.position, this.eventHorizon) toMe(bullet, this.position, this.eventHorizon) - - - //push everything away - // function push(who, pos, range) { // for (let i = 0, len = who.length; i < len; ++i) { // const SUB = Vector.sub(who[i].position, pos) // const DISTANCE = Vector.magnitude(SUB) - // if (DISTANCE < range) { // const depth = range - DISTANCE // const force = Vector.mult(Vector.normalise(SUB), 30 * who[i].mass / depth) @@ -515,8 +494,6 @@ const spawn = { // push(mob, this.position, this.eventHorizon) // push(bullet, this.position, this.eventHorizon) // push([player], this.position, this.eventHorizon) - - // for (let i = 0; i < (game.difficulty - 3); ++i) { // spawn.sucker(this.position.x + (Math.random() - 0.5) * radius * 2, this.position.y + (Math.random() - 0.5) * radius * 2, 70 * Math.random()); // Matter.Body.setVelocity(mob[mob.length - 1], { @@ -600,7 +577,7 @@ const spawn = { me.accelMag = 0.0005 * game.accelScale; me.frictionStatic = 0; me.friction = 0; - if (Math.random() < Math.min(0.2 + (game.difficulty - 1) * 0.05, 0.3)) spawn.shield(me, x, y); + spawn.shield(me, x, y); me.do = function () { this.seePlayerByLookingAt(); this.attraction(); @@ -623,7 +600,7 @@ const spawn = { me.onDamage = function () { this.laserPos = this.position; }; - // if (Math.random() < Math.min(0.2 + game.difficulty * 0.1, 0.7)) spawn.shield(me, x, y); + spawn.shield(me, x, y); me.do = function () { if (!mech.isBodiesAsleep) { this.seePlayerByLookingAt(); @@ -698,6 +675,7 @@ const spawn = { me.friction = 0; me.delay = 100; Matter.Body.rotate(me, Math.PI * 0.1); + spawn.shield(me, x, y); me.onDamage = function () { this.cd = game.cycle + this.delay; }; @@ -858,7 +836,7 @@ const spawn = { //boss that drops bombs from above and holds a set distance from player mobs.spawn(x, y, 3, radius, "transparent"); let me = mob[mob.length - 1]; - Matter.Body.setDensity(me, 0.0015 + 0.0005 * Math.sqrt(game.difficulty)); //extra dense //normal is 0.001 //makes effective life much larger + Matter.Body.setDensity(me, 0.0015 + 0.0004 * Math.sqrt(game.difficulty)); //extra dense //normal is 0.001 //makes effective life much larger me.stroke = "rgba(255,0,200)"; //used for drawGhost me.seeAtDistance2 = 1500000; @@ -874,7 +852,7 @@ const spawn = { // me.memory = 300; // Matter.Body.setDensity(me, 0.0015); //extra dense //normal is 0.001 me.collisionFilter.mask = cat.player | cat.bullet - spawn.shield(me, x, y); + spawn.shield(me, x, y, 1); me.onDeath = function () { powerUps.spawnBossPowerUp(this.position.x, this.position.y) }; @@ -900,30 +878,30 @@ const spawn = { x: 0, y: 0 }; - if (Math.random() < Math.min(0.15 + (game.difficulty - 1) * 0.05, 0.3)) spawn.shield(me, x, y); + // spawn.shield(me, x, y); me.do = function () { this.seePlayerByLookingAt(); this.fire(); }; }, - shooterBoss(x, y, radius = 85 + Math.ceil(Math.random() * 50)) { + shooterBoss(x, y, radius = 130) { //boss spawns on skyscraper level mobs.spawn(x, y, 3, radius, "rgb(255,70,180)"); let me = mob[mob.length - 1]; me.vertices = Matter.Vertices.rotate(me.vertices, Math.PI, me.position); //make the pointy side of triangle the front me.memory = 240; - me.fireFreq = 0.02; + me.fireFreq = 0.025; me.noseLength = 0; me.fireAngle = 0; me.accelMag = 0.005 * game.accelScale; me.frictionAir = 0.1; - me.lookTorque = 0.000005 * (Math.random() > 0.5 ? -1 : 1); + me.lookTorque = 0.000007 * (Math.random() > 0.5 ? -1 : 1); me.fireDir = { x: 0, y: 0 }; - Matter.Body.setDensity(me, 0.001 + 0.0005 * Math.sqrt(game.difficulty)); //extra dense //normal is 0.001 //makes effective life much larger - spawn.shield(me, x, y); + Matter.Body.setDensity(me, 0.02 + 0.001 * Math.sqrt(game.difficulty)); //extra dense //normal is 0.001 //makes effective life much larger + // spawn.shield(me, x, y, 1); me.onDeath = function () { powerUps.spawnBossPowerUp(this.position.x, this.position.y) }; @@ -970,7 +948,7 @@ const spawn = { }); } }; - if (Math.random() < Math.min((game.difficulty - 1) * 0.04, 0.2)) spawn.shield(me, x, y); + spawn.shield(me, x, y); me.do = function () { this.gravity(); this.seePlayerCheck(); @@ -1018,8 +996,7 @@ const spawn = { me.memory = 200; me.laserRange = 500; Matter.Body.setDensity(me, 0.001 + 0.0005 * Math.sqrt(game.difficulty)); //extra dense //normal is 0.001 //makes effective life much larger - spawn.shield(me, x, y); - if (Math.random() < Math.min((game.difficulty - 1) * 0.05, 0.3)) spawn.shield(me, x, y); + spawn.shield(me, x, y, 1); me.onDeath = function () { powerUps.spawnBossPowerUp(this.position.x, this.position.y) }; @@ -1059,9 +1036,7 @@ const spawn = { me.accelMag = 0.002 * game.accelScale; me.memory = 20; Matter.Body.setDensity(me, 0.001 + 0.0005 * Math.sqrt(game.difficulty)); //extra dense //normal is 0.001 //makes effective life much larger - spawn.shield(me, x, y); - if (Math.random() < Math.min((game.difficulty - 1) * 0.05, 0.3)) spawn.shield(me, x, y); - + spawn.shield(me, x, y, 1); me.onDeath = function () { powerUps.spawnBossPowerUp(this.position.x, this.position.y) this.removeCons(); //remove constraint @@ -1072,46 +1047,57 @@ const spawn = { this.attraction(); }; }, - shield(target, x, y, stiffness = 0.4) { - if (this.allowShields) { - mobs.spawn(x, y, 9, target.radius + 20, "rgba(220,220,255,0.6)"); + shield(target, x, y, chance = Math.min(0.01 + game.difficulty * 0.01, 0.3)) { + if (this.allowShields && Math.random() < chance) { + mobs.spawn(x, y, 9, target.radius + 30, "rgba(220,220,255,0.9)"); let me = mob[mob.length - 1]; me.stroke = "rgb(220,220,255)"; - Matter.Body.setDensity(me, 0.0001) //very low density to not mess with the original mob's motion + Matter.Body.setDensity(me, 0.00001) //very low density to not mess with the original mob's motion me.shield = true; me.collisionFilter.category = cat.mobShield me.collisionFilter.mask = cat.bullet; consBB[consBB.length] = Constraint.create({ - //attach shield to last spawned mob bodyA: me, - bodyB: target, - stiffness: stiffness, + bodyB: target, //attach shield to target + stiffness: 0.4, damping: 0.1 }); me.onDamage = function () { //make sure the mob that owns the shield can tell when damage is done this.alertNearByMobs(); + this.fill = `rgba(220,220,255,${0.3 + 0.6 *this.health})` }; me.leaveBody = false; me.dropPowerUp = false; me.showHealthBar = false; + + me.shieldTargetID = target.id + target.isShielded = true; + me.onDeath = function () { + //clear isShielded status from target + for (let i = 0, len = mob.length; i < len; i++) { + if (mob[i].id === this.shieldTargetID) mob[i].isShielded = false; + } + }; //swap order of shield and mob, so that mob is behind shield graphically mob[mob.length - 1] = mob[mob.length - 2]; mob[mob.length - 2] = me; me.do = function () {}; } }, - bossShield(nodes, x, y, radius) { - mobs.spawn(x, y, 9, radius, "rgba(220,220,255,0.65)"); + bossShield(targets, x, y, radius) { + const nodes = targets.length + mobs.spawn(x, y, 9, radius, "rgba(220,220,255,0.9)"); let me = mob[mob.length - 1]; me.stroke = "rgb(220,220,255)"; - Matter.Body.setDensity(me, 0.00005) //very low density to not mess with the original mob's motion + Matter.Body.setDensity(me, 0.00001) //very low density to not mess with the original mob's motion me.frictionAir = 0; me.shield = true; me.collisionFilter.category = cat.mobShield me.collisionFilter.mask = cat.bullet; - //constrain to all mob nodes in boss for (let i = 0; i < nodes; ++i) { + mob[mob.length - i - 2].isShielded = true; + //constrain to all mob nodes in boss consBB[consBB.length] = Constraint.create({ bodyA: me, bodyB: mob[mob.length - i - 2], @@ -1120,8 +1106,16 @@ const spawn = { }); } me.onDamage = function () { - //make sure the mob that owns the shield can tell when damage is done - this.alertNearByMobs(); + this.alertNearByMobs(); //makes sure the mob that owns the shield can tell when damage is done + this.fill = `rgba(220,220,255,${0.3 + 0.6 *this.health})` + }; + me.onDeath = function () { + //clear isShielded status from target + for (let j = 0; j < targets.length; j++) { + for (let i = 0, len = mob.length; i < len; i++) { + if (mob[i].id === targets[j]) mob[i].isShielded = false; + } + } }; me.leaveBody = false; me.dropPowerUp = false; @@ -1143,8 +1137,9 @@ const spawn = { sideLength = Math.ceil(Math.random() * 100) + 70, // distance between each node mob stiffness = Math.random() * 0.03 + 0.005 ) { - this.allowShields = false; //don't want shields on boss mobs + this.allowShields = false; //don't want shields on individual boss mobs const angle = 2 * Math.PI / nodes + let targets = [] for (let i = 0; i < nodes; ++i) { let whoSpawn = spawn; if (spawn === "random") { @@ -1153,6 +1148,7 @@ const spawn = { whoSpawn = this.pickList[Math.floor(Math.random() * this.pickList.length)]; } this[whoSpawn](x + sideLength * Math.sin(i * angle), y + sideLength * Math.cos(i * angle), radius); + targets.push(mob[mob.length - 1].id) //track who is in the node boss, for shields } if (Math.random() < 0.3) { this.constrain2AdjacentMobs(nodes, stiffness * 2, true); @@ -1161,8 +1157,7 @@ const spawn = { } //spawn shield for entire boss if (nodes > 2 && Math.random() < 0.998) { - this.bossShield(nodes, x, y, sideLength + 2.5 * radius + nodes * 6 - 25); - // this.bossShield(nodes, x, y, sideLength / (2 * Math.sin(Math.PI / nodes))); + this.bossShield(targets, x, y, sideLength + 2.5 * radius + nodes * 6 - 25); } this.allowShields = true; }, @@ -1176,7 +1171,7 @@ const spawn = { l = Math.ceil(Math.random() * 80) + 30, stiffness = Math.random() * 0.06 + 0.01 ) { - this.allowShields = false; //don't want shields on boss mobs + this.allowShields = false; //don't want shields on individual boss mobs for (let i = 0; i < nodes; ++i) { let whoSpawn = spawn; if (spawn === "random") {