diff --git a/index.html b/index.html index 117c7d2..0e37d1e 100644 --- a/index.html +++ b/index.html @@ -99,10 +99,10 @@

diff --git a/js/bullet.js b/js/bullet.js index 26e62b0..49c8671 100644 --- a/js/bullet.js +++ b/js/bullet.js @@ -3227,153 +3227,148 @@ const b = { } }, firePulse() { - //calculate laser collision - let best, energy, explosionRange; - let 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] - }; - } - } - } - }; - //check for collisions - best = { - x: null, - y: null, - dist2: Infinity, - who: null, - v1: null, - v2: null - }; - if (mod.isPulseAim) { //find mobs in line of sight - let dist = 2200 - energy = 0.23 * Math.min(mech.energy, 1.5) - explosionRange = 1680 * energy - for (let i = 0, len = mob.length; i < len; i++) { - const newDist = Vector.magnitude(Vector.sub(path[0], mob[i].position)) - if (explosionRange < newDist && - newDist < dist && - Matter.Query.ray(map, path[0], mob[i].position).length === 0 && - Matter.Query.ray(body, path[0], mob[i].position).length === 0) { - dist = newDist - best.who = mob[i] - path[path.length - 1] = mob[i].position - } - } - } - if (!best.who) { - 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 - }; - } - } - if (mod.isPulseAim) { - mech.energy -= energy * mod.isLaserDiode - if (best.who) b.explosion(path[1], explosionRange, true) - mech.fireCDcycle = mech.cycle + Math.floor(25 * b.fireCD); // cool down - } else { - energy = 0.27 * Math.min(mech.energy, 1.5) - mech.energy -= energy * mod.isLaserDiode - explosionRange = 1560 * energy - if (best.who) b.explosion(path[1], explosionRange, true) - mech.fireCDcycle = mech.cycle + Math.floor(50 * b.fireCD); // cool down - } - if (mod.isPulseStun) { - const range = 100 + 2000 * energy - for (let i = 0, len = mob.length; i < len; ++i) { - if (mob[i].alive && !mob[i].isShielded) { - dist = Vector.magnitude(Vector.sub(path[1], mob[i].position)) - mob[i].radius; - if (dist < range) mobs.statusStun(mob[i], 30 + Math.floor(energy * 60)) - } - } - } - //draw laser beam - ctx.beginPath(); - ctx.moveTo(path[0].x, path[0].y); - ctx.lineTo(path[1].x, path[1].y); - ctx.strokeStyle = "rgba(255,0,0,0.13)" - ctx.lineWidth = 60 * energy / 0.2 - ctx.stroke(); - ctx.strokeStyle = "rgba(255,0,0,0.2)" - ctx.lineWidth = 18 - ctx.stroke(); - ctx.strokeStyle = "#f00"; - ctx.lineWidth = 4 - ctx.stroke(); + mech.fireCDcycle = mech.cycle + Math.floor((mod.isPulseAim ? 25 : 50) * b.fireCD); // cool down + let energy = 0.27 * Math.min(mech.energy, 1.5) + mech.energy -= energy * mod.isLaserDiode - //draw little dots along the laser path - const sub = Vector.sub(path[1], path[0]) - const mag = Vector.magnitude(sub) - for (let i = 0, len = Math.floor(mag * 0.03 * energy / 0.2); i < len; i++) { - const dist = Math.random() - game.drawList.push({ - x: path[0].x + sub.x * dist + 13 * (Math.random() - 0.5), - y: path[0].y + sub.y * dist + 13 * (Math.random() - 0.5), - radius: 1 + 4 * Math.random(), - color: "rgba(255,0,0,0.5)", - time: Math.floor(2 + 33 * Math.random() * Math.random()) - }); + if (mod.beamSplitter) { + energy *= 0.7 + b.pulse(energy, mech.angle) + for (let i = 1; i < 1 + mod.beamSplitter; i++) { + energy *= 0.9 + b.pulse(energy, mech.angle - i * 0.35) + b.pulse(energy, mech.angle + i * 0.35) + } + } else { + b.pulse(energy, mech.angle) } }, }, + ], + pulse(energy, angle = mech.angle) { + let best; + let explosionRange = 1560 * energy + let range = 3000 + const path = [{ + x: mech.pos.x + 20 * Math.cos(angle), + y: mech.pos.y + 20 * Math.sin(angle) + }, + { + x: mech.pos.x + range * Math.cos(angle), + y: mech.pos.y + range * Math.sin(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] + }; + } + } + } + }; + //check for collisions + best = { + x: null, + y: null, + dist2: Infinity, + who: null, + v1: null, + v2: null + }; + if (mod.isPulseAim) { //find mobs in line of sight + let dist = 2200 + for (let i = 0, len = mob.length; i < len; i++) { + const newDist = Vector.magnitude(Vector.sub(path[0], mob[i].position)) + if (explosionRange < newDist && + newDist < dist && + Matter.Query.ray(map, path[0], mob[i].position).length === 0 && + Matter.Query.ray(body, path[0], mob[i].position).length === 0) { + dist = newDist + best.who = mob[i] + path[path.length - 1] = mob[i].position + } + } + } + if (!best.who) { + 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 + }; + } + } + if (best.who) b.explosion(path[1], explosionRange, true) - // { - // name: "pulse", - // description: "convert 25% of your energy into a pulsed laser
instantly initiates a fusion explosion", - // ammo: 0, - // ammoPack: Infinity, - // have: false, - // fire() { + if (mod.isPulseStun) { + const range = 100 + 2000 * energy + for (let i = 0, len = mob.length; i < len; ++i) { + if (mob[i].alive && !mob[i].isShielded) { + dist = Vector.magnitude(Vector.sub(path[1], mob[i].position)) - mob[i].radius; + if (dist < range) mobs.statusStun(mob[i], 30 + Math.floor(energy * 60)) + } + } + } + //draw laser beam + ctx.beginPath(); + ctx.moveTo(path[0].x, path[0].y); + ctx.lineTo(path[1].x, path[1].y); + ctx.strokeStyle = "rgba(255,0,0,0.13)" + ctx.lineWidth = 60 * energy / 0.2 + ctx.stroke(); + ctx.strokeStyle = "rgba(255,0,0,0.2)" + ctx.lineWidth = 18 + ctx.stroke(); + ctx.strokeStyle = "#f00"; + ctx.lineWidth = 4 + ctx.stroke(); - // } - // }, - ] + //draw little dots along the laser path + const sub = Vector.sub(path[1], path[0]) + const mag = Vector.magnitude(sub) + for (let i = 0, len = Math.floor(mag * 0.03 * energy / 0.2); i < len; i++) { + const dist = Math.random() + game.drawList.push({ + x: path[0].x + sub.x * dist + 13 * (Math.random() - 0.5), + y: path[0].y + sub.y * dist + 13 * (Math.random() - 0.5), + radius: 1 + 4 * Math.random(), + color: "rgba(255,0,0,0.5)", + time: Math.floor(2 + 33 * Math.random() * Math.random()) + }); + } + } }; \ No newline at end of file diff --git a/js/game.js b/js/game.js index e8d8e6f..a4b1db0 100644 --- a/js/game.js +++ b/js/game.js @@ -127,8 +127,7 @@ const game = { delta: 1000 / 60, //speed of game engine //looks like it has to be 16 to match player input buttonCD: 0, levelsCleared: 0, - difficultyMode: 1, - isEasyMode: false, + difficultyMode: 2, //normal difficulty is 2 difficulty: 0, dmgScale: null, //set in levels.setDifficulty healScale: 1, @@ -485,20 +484,18 @@ const game = { level.levelsCleared = 0; //resetting difficulty - game.dmgScale = 1; - b.dmgScale = 0.7; + game.dmgScale = 0; //increases in level.difficultyIncrease + b.dmgScale = 1; //decreases in level.difficultyIncrease game.accelScale = 1; game.lookFreqScale = 1; game.CDScale = 1; game.difficulty = 0; game.difficultyMode = Number(document.getElementById("difficulty-select").value) build.isCustomSelection = false; - if (game.difficultyMode === 0) { - game.isEasyMode = true; - game.difficultyMode = 1 - level.difficultyDecrease(6); //if this stops being -6 change in build.calculateCustomDifficulty() - } - if (game.difficultyMode > 1) level.difficultyIncrease(3) + // if (game.difficultyMode > 2) { + // level.difficultyIncrease(game.difficultyMode) + // level.difficultyIncrease(game.difficultyMode) + // } game.clearNow = true; document.getElementById("text-log").style.opacity = 0; @@ -687,7 +684,7 @@ const game = { if (mech.energy > mech.maxEnergy) mech.energy = mech.maxEnergy + (mech.energy - mech.maxEnergy) * 0.75 if (mech.pos.y > game.fallHeight) { // if 4000px deep - if (game.difficultyMode > 2) { + if (game.difficultyMode > 4) { mech.death(); } else { @@ -726,8 +723,7 @@ const game = { }); } } - if (game.difficultyMode === 2) mech.damage(0.25); - if (game.difficultyMode === 1) mech.damage(0.1); + mech.damage(0.1 * game.difficultyMode); mech.energy = 0.01; } } @@ -753,7 +749,7 @@ const game = { let i = who.length; while (i--) { if (who[i].position.y > game.fallHeight) { - if (save && game.difficultyMode < 3) { + if (save && game.difficultyMode < 5) { Matter.Body.setVelocity(who[i], { x: 0, y: 0 diff --git a/js/index.js b/js/index.js index 3ca4d72..0c12c16 100644 --- a/js/index.js +++ b/js/index.js @@ -188,7 +188,7 @@ const build = {
position: (${player.position.x.toFixed(1)}, ${player.position.y.toFixed(1)})   velocity: (${player.velocity.x.toFixed(1)}, ${player.velocity.y.toFixed(1)})
mouse: (${game.mouseInGame.x.toFixed(1)}, ${game.mouseInGame.y.toFixed(1)})   mass: ${player.mass.toFixed(1)}
-
level: ${level.levelsCleared} - ${level.levels[level.onLevel]} (${level.difficultyText()})   ${mech.cycle} cycles +
level: ${level.levelsCleared} - ${level.levelsCleared===0?"intro":level.levels[level.onLevel]} (${level.difficultyText()})   ${mech.cycle} cycles
${mob.length} mobs,   ${body.length} blocks,   ${bullet.length} bullets,   ${powerUp.length} power ups
damage difficulty scale: ${(b.dmgScale*100).toFixed(2) }%
harm difficulty scale: ${(game.dmgScale*100).toFixed(0)}% @@ -318,10 +318,10 @@ const build = {
starting level:
` for (let i = 0, len = mech.fieldUpgrades.length; i < len; i++) { @@ -891,7 +891,7 @@ if (localSettings) { } else { localSettings = { isCommunityMaps: false, - difficultyMode: '1', + difficultyMode: '2', fpsCapDefault: 'max', runCount: 0, levelsClearedLastGame: 0, diff --git a/js/level.js b/js/level.js index 4af04de..d8af4d5 100644 --- a/js/level.js +++ b/js/level.js @@ -831,13 +831,13 @@ const level = { "Last time was a simulation. Is this one a simulation too?", ) } - if (game.difficultyMode < 2 && localSettings.levelsClearedLastGame > 10) { //too easy + if (game.difficultyMode < 4 && localSettings.levelsClearedLastGame > 10) { //too easy say.push( "That felt too easy.
Maybe I should increase the difficulty of the simulation.", "That was fun, but maybe I should increase the difficulty of the simulation.", "I should increase the difficulty of the simulation, that didn't feel realistic.", ) - } else if (game.difficultyMode > 1 && localSettings.levelsClearedLastGame > 10) { //great run on a hard or why + } else if (game.difficultyMode > 3 && localSettings.levelsClearedLastGame > 10) { //great run on a hard or why say.push( "What do I do after I escape?", "I'm almost ready to stop these simulations and actually escape.", @@ -3836,35 +3836,35 @@ const level = { difficultyIncrease(num = 1) { for (let i = 0; i < num; i++) { game.difficulty++ - game.dmgScale += 0.38; //damage done by mobs increases each level b.dmgScale *= 0.93; //damage done by player decreases each level if (game.accelScale < 5) game.accelScale *= 1.02 //mob acceleration increases each level if (game.lookFreqScale > 0.2) game.lookFreqScale *= 0.98 //mob cycles between looks decreases each level if (game.CDScale > 0.2) game.CDScale *= 0.97 //mob CD time decreases each level } + game.dmgScale = 0.38 * game.difficulty //damage done by mobs increases each level 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.38; //damage done by mobs increases each level - if (game.dmgScale < 0.1) game.dmgScale = 0.1; b.dmgScale /= 0.93; //damage done by player decreases each level if (game.accelScale > 0.2) game.accelScale /= 1.02 //mob acceleration increases each level if (game.lookFreqScale < 5) game.lookFreqScale /= 0.98 //mob cycles between looks decreases each level if (game.CDScale < 5) game.CDScale /= 0.97 //mob CD time decreases each level } if (game.difficulty < 1) game.difficulty = 0; + game.dmgScale = 0.38 * game.difficulty //damage done by mobs increases each level + if (game.dmgScale < 0.1) game.dmgScale = 0.1; game.healScale = 1 / (1 + game.difficulty * 0.06) }, - difficultyText(mode = document.getElementById("difficulty-select").value) { - if (mode === "0") { + difficultyText() { + if (game.difficultyMode === 1) { return "easy" - } else if (mode === "1") { + } else if (game.difficultyMode === 2) { return "normal" - } else if (mode === "2") { + } else if (game.difficultyMode === 4) { return "hard" - } else if (mode === "4") { + } else if (game.difficultyMode === 6) { return "why" } }, @@ -3887,7 +3887,6 @@ const level = { if (level.levelsCleared > level.levels.length * 1.25) level.difficultyIncrease(game.difficultyMode) if (level.levelsCleared > level.levels.length * 1.5) level.difficultyIncrease(game.difficultyMode) if (level.levelsCleared > level.levels.length * 2) level.difficultyIncrease(game.difficultyMode) - if (game.isEasyMode && level.levelsCleared % 2) level.difficultyDecrease(1); level.bossKilled = false; //reset lost mod display for (let i = 0; i < mod.mods.length; i++) { diff --git a/js/mods.js b/js/mods.js index b49d3e5..96340a9 100644 --- a/js/mods.js +++ b/js/mods.js @@ -2428,7 +2428,7 @@ const mod = { maxCount: 9, count: 0, allowed() { - return mod.haveGunCheck("laser") && !mod.isWideLaser && !mod.isPulseLaser + return mod.haveGunCheck("laser") && !mod.isWideLaser && !mod.isPulseAim }, requires: "laser, not specular reflection", effect() { @@ -2482,7 +2482,7 @@ const mod = { maxCount: 1, count: 0, allowed() { - return mod.haveGunCheck("laser") && mod.laserReflections < 3 && !mod.beamSplitter && !mod.isWideLaser + return mod.haveGunCheck("laser") && mod.laserReflections < 3 && !mod.isWideLaser }, requires: "laser, not specular reflection
not beam splitter, not diffuse", effect() { @@ -2520,7 +2520,7 @@ const mod = { maxCount: 1, count: 0, allowed() { - return mod.isPulseLaser + return mod.isPulseLaser && !mod.beamSplitter }, requires: "pulse", effect() { diff --git a/js/powerup.js b/js/powerup.js index 353d473..e6e52a4 100644 --- a/js/powerup.js +++ b/js/powerup.js @@ -434,7 +434,7 @@ const powerUps = { } }, spawnRandomPowerUp(x, y) { //mostly used after mob dies, doesn't always return a power up - if ((Math.random() * Math.random() - 0.3 > Math.sqrt(mech.health) && !mod.isEnergyHealth) || Math.random() < 0.035) { //spawn heal chance is higher at low health + if ((Math.random() * Math.random() - 0.3 > Math.sqrt(mech.health) && !mod.isEnergyHealth) || Math.random() < 0.04) { //spawn heal chance is higher at low health powerUps.spawn(x, y, "heal"); return; } @@ -446,7 +446,7 @@ const powerUps = { powerUps.spawn(x, y, "gun"); return; } - if (Math.random() < 0.0027 * (26 - mod.totalCount)) { //a new mod has a low chance for each not acquired mod up to 15 + if (Math.random() < 0.0027 * (25 - mod.totalCount)) { //a new mod has a low chance for each not acquired mod up to 15 powerUps.spawn(x, y, "mod"); return; } @@ -462,43 +462,30 @@ const powerUps = { randomPowerUpCounter: 0, spawnBossPowerUp(x, y) { //boss spawns field and gun mod upgrades level.bossKilled = true; - // if (game.difficultyMode === 4) powerUps.spawn(x, y, "mod") //why mode gets a free mod + powerUps.randomPowerUpCounter++; - const chanceToFail = Math.max(level.levelsCleared, 10) * 0.1 //1 until level 10, then 1.1, 1.2, 1.3, ... - if (Math.random() * chanceToFail < powerUps.randomPowerUpCounter) { - powerUps.randomPowerUpCounter = 0; - spawnPowerUps() - } else { - spawnHealthAmmo() - } - if (game.difficultyMode === 4) { - powerUps.randomPowerUpCounter + 0.6; - const chanceToFail = Math.max(level.levelsCleared, 6) * 0.1 //1 until level 8 + powerUpChance(Math.max(level.levelsCleared, 10) * 0.1) + powerUps.randomPowerUpCounter += 0.6; + powerUpChance(Math.max(level.levelsCleared, 6) * 0.1) + + function powerUpChance(chanceToFail) { if (Math.random() * chanceToFail < powerUps.randomPowerUpCounter) { powerUps.randomPowerUpCounter = 0; - spawnPowerUps() + if (mech.fieldMode === 0) { + powerUps.spawn(x, y, "field") + } else if (Math.random() < 0.95) { + powerUps.spawn(x, y, "mod") + } else { + powerUps.spawn(x, y, "gun") + } } else { - spawnHealthAmmo() - } - } - - function spawnHealthAmmo() { - if (mech.health < 0.65 && !mod.isEnergyHealth) { - powerUps.spawn(x, y, "heal"); - powerUps.spawn(x, y, "heal"); - } else { - powerUps.spawn(x, y, "ammo"); - powerUps.spawn(x, y, "ammo"); - } - } - - function spawnPowerUps() { - if (mech.fieldMode === 0) { - powerUps.spawn(x, y, "field") - } else if (Math.random() < 0.95) { - powerUps.spawn(x, y, "mod") - } else { - powerUps.spawn(x, y, "gun") + if (mech.health < 0.65 && !mod.isEnergyHealth) { + powerUps.spawn(x, y, "heal"); + powerUps.spawn(x, y, "heal"); + } else { + powerUps.spawn(x, y, "ammo"); + powerUps.spawn(x, y, "ammo"); + } } } }, @@ -517,7 +504,7 @@ const powerUps = { }, spawnStartingPowerUps(x, y) { //used for map specific power ups, mostly to give player a starting gun if (level.levelsCleared < 4) { //runs 4 times on all difficulty levels - if (game.difficultyMode > 1 && level.levelsCleared > 1) powerUps.spawn(x, y, "mod") + if (level.levelsCleared > 1) powerUps.spawn(x, y, "mod") //bonus power ups for clearing runs in the last game if (level.levelsCleared === 0 && !game.isCheating) { diff --git a/js/spawn.js b/js/spawn.js index 4aa5e20..6cce754 100644 --- a/js/spawn.js +++ b/js/spawn.js @@ -233,11 +233,11 @@ const spawn = { me.isBoss = true; me.frictionAir = 0.01 me.seeAtDistance2 = 9000000; - me.accelMag = 0.00062 * game.accelScale; + me.accelMag = 0.00065 * game.accelScale; Matter.Body.setDensity(me, 0.0006); //normal is 0.001 me.collisionFilter.mask = cat.bullet | cat.player me.memory = Infinity; - me.seePlayerFreq = 60 + me.seePlayerFreq = 30 me.lockedOn = null; if (vertices === 9) { diff --git a/todo.txt b/todo.txt index 636ebb6..d8ef188 100644 --- a/todo.txt +++ b/todo.txt @@ -1,17 +1,23 @@ gun: pulse is now a mod for laser + mod: beam splitter applies to pulse + +difficulty balancing + (this needs to be done as more mods are added to the game to prevent power creep) + all modes are a bit harder + if you used to play why, try the new hard + if you used to play hard try the new normal + power ups drop more often from bosses on all modes ************** TODO - n-gon ************** -optional setting for people with desktops that want double mobs +mod - explosions apply radiation damage over time + or spawn a neutron bomb with a timer -hard mode should match the pace of why, but with less difficulty - 2x power up chances, same as why for all difficulties - adjust difficulty levels - easy: 1 - normal: 2 - hard: 3 - why: 4 +mod self destruct - drones explode when they die + drones lose extra time on collisions + +optional setting for people with desktops that want double mobs combine more stuff? ____ mod for ____ @@ -20,11 +26,6 @@ combine more stuff? iceIX -> foam flechettes -> nail gun - -let beam splitter work with pulse - -neutron bomb as a mod for mines - mod: stealth field - mark a mob, it you next kill is that mob get a bonus power up run only at the start of a new level @@ -35,6 +36,8 @@ add an ending to the game around level 15 game never ends if you have used cheats +new status effect - push mob away from player for a time + new status effect - apply status effect to mobs that makes blocks attracted to them only lasts a few cycles @@ -56,7 +59,6 @@ look for mods that could update description text with count and mod.is informat can only use variables that change in effect() and remove() this.description = `8% chance to duplicate spawned power ups
chance to duplicate = ${mod.duplicateChance}` -mod self destruct - drones explode when they die, but they last 1/2 as long mouse event e.which is deprecated