diff --git a/js/bullets.js b/js/bullets.js index 8ecf456..c1441db 100644 --- a/js/bullets.js +++ b/js/bullets.js @@ -87,6 +87,7 @@ const b = { modNailsDeathMob: null, isModSlowFPS: null, isModNeutronStun: null, + manyWorlds: null, modOnHealthChange() { //used with acid mod if (b.isModAcidDmg && mech.health > 0.8) { b.modAcidDmg = 0.5 @@ -762,7 +763,7 @@ const b = { }, { name: "pair production", - description: "power ups overfill your energy
temporarily gain twice your maximum", + description: "power ups overfill your energy
temporarily gain twice your max energy", maxCount: 1, count: 0, allowed() { @@ -850,31 +851,23 @@ const b = { } }, { - name: "reallocation", - description: "convert 1 random mod into 2 new guns
recursive mods lose all stacks", + name: "many worlds", + description: "spawn a reroll after choosing a power up", maxCount: 1, count: 0, allowed() { - return (b.modCount > 0) && !build.isCustomSelection + return true }, - requires: "at least 1 mod", + requires: "", effect: () => { - const have = [] //find which mods you have - for (let i = 0; i < b.mods.length; i++) { - if (b.mods[i].count > 0) have.push(i) - } - const choose = have[Math.floor(Math.random() * have.length)] - b.mods[choose].remove(); // remove a random mod form the list of mods you have - b.mods[choose].count = 0; - game.updateModHUD(); - - for (let i = 0; i < 2; i++) { - powerUps.spawn(mech.pos.x, mech.pos.y, "gun"); - if (Math.random() < b.modBayesian) powerUps.spawn(mech.pos.x, mech.pos.y, "gun"); - } + b.manyWorlds = true; + // for (let i = 0; i < 9; i++) { + // powerUps.spawn(mech.pos.x, mech.pos.y, "reroll"); + // if (Math.random() < b.modBayesian) powerUps.spawn(mech.pos.x, mech.pos.y, "reroll"); + // } }, remove() { - //nothing to remove + b.manyWorlds = false; } }, { @@ -903,6 +896,34 @@ const b = { //nothing to undo } }, + { + name: "reallocation", + description: "convert 1 random mod into 2 new guns
recursive mods lose all stacks", + maxCount: 1, + count: 0, + allowed() { + return (b.modCount > 0) && !build.isCustomSelection + }, + requires: "at least 1 mod", + effect: () => { + const have = [] //find which mods you have + for (let i = 0; i < b.mods.length; i++) { + if (b.mods[i].count > 0) have.push(i) + } + const choose = have[Math.floor(Math.random() * have.length)] + b.mods[choose].remove(); // remove a random mod form the list of mods you have + b.mods[choose].count = 0; + game.updateModHUD(); + + for (let i = 0; i < 2; i++) { + powerUps.spawn(mech.pos.x, mech.pos.y, "gun"); + if (Math.random() < b.modBayesian) powerUps.spawn(mech.pos.x, mech.pos.y, "gun"); + } + }, + remove() { + //nothing to remove + } + }, { name: "Lorentzian topology", description: "your bullets last +33% longer", @@ -1973,6 +1994,7 @@ const b = { dist = Vector.magnitude(sub) - mob[i].radius; if (dist < radius) { if (mob[i].shield) dmg *= 3 //balancing explosion dmg to shields + if (Matter.Query.ray(map, mob[i].position, where).length > 0) dmg *= 0.5 //reduce damage if a wall is in the way mob[i].damage(dmg * damageScale); mob[i].locatePlayer(); knock = Vector.mult(Vector.normalise(sub), (-Math.sqrt(dmg * damageScale) * mob[i].mass) / 50); @@ -2353,7 +2375,7 @@ const b = { friction: 0.05, frictionAir: 0.0005, restitution: 1, - dmg: 0.17, //damage done in addition to the damage from momentum + dmg: 0.23, //damage done in addition to the damage from momentum lookFrequency: 107 + Math.floor(47 * Math.random()), endCycle: game.cycle + Math.floor((1200 + 420 * Math.random()) * b.isModBulletsLastLonger), classType: "bullet", @@ -2365,7 +2387,14 @@ const b = { lockedOn: null, isFollowMouse: true, deathCycles: 110 + RADIUS * 5, - onDmg() { + onDmg(who) { + //move away from target after hitting + const unit = Vector.mult(Vector.normalise(Vector.sub(this.position, who.position)), -20) + Matter.Body.setVelocity(this, { + x: unit.x, + y: unit.y + }); + this.lockedOn = null if (this.endCycle > game.cycle + this.deathCycles && b.isModDroneCollide) { this.endCycle -= 60 @@ -3119,7 +3148,7 @@ const b = { // check if inside a mob q = Matter.Query.point(mob, this.position) for (let i = 0; i < q.length; i++) { - let dmg = b.dmgScale * 0.43 / Math.sqrt(q[i].mass) * (b.modWaveHelix === 1 ? 1 : 0.6) //1 - 0.4 = 0.6 for helix mod 40% damage reduction + let dmg = b.dmgScale * 0.40 / Math.sqrt(q[i].mass) * (b.modWaveHelix === 1 ? 1 : 0.6) //1 - 0.4 = 0.6 for helix mod 40% damage reduction q[i].damage(dmg); q[i].foundPlayer(); game.drawList.push({ //add dmg to draw queue @@ -3631,11 +3660,10 @@ const b = { //aoe damage to mobs for (let i = 0, len = mob.length; i < len; i++) { if (Vector.magnitude(Vector.sub(mob[i].position, this.position)) < this.damageRadius) { - if (mob[i].shield) { - mob[i].damage(5 * b.dmgScale * 0.023); - } else { - mob[i].damage(b.dmgScale * 0.023); - } + let dmg = b.dmgScale * 0.023 + if (Matter.Query.ray(map, mob[i].position, this.position).length > 0) dmg *= 0.5 //reduce damage if a wall is in the way + if (mob[i].shield) dmg *= 5 //x5 to make up for the /5 that shields normally take + mob[i].damage(dmg); mob[i].locatePlayer(); } } @@ -3859,6 +3887,7 @@ const b = { //frictionAir: 0.01, //restitution: 0, // angle: 0, // friction: 0.5, + restitution: 0, frictionAir: 0, dmg: 0, //damage done in addition to the damage from momentum classType: "bullet", diff --git a/js/game.js b/js/game.js index 608c0c1..dc65ec2 100644 --- a/js/game.js +++ b/js/game.js @@ -56,6 +56,7 @@ const game = { b.bulletDraw(); b.bulletDo(); game.drawCircle(); + game.clip(); ctx.restore(); game.drawCursor(); }, @@ -132,6 +133,9 @@ const game = { // }; // requestAnimationFrame(normalFPS); // }, + clip() { + + }, drawCursor() { const size = 10; ctx.beginPath(); @@ -481,6 +485,7 @@ const game = { b.setupAllMods(); //sets mods to default values game.updateModHUD(); + powerUps.reroll.rerolls = 0; mech.maxHealth = 1 mech.maxEnergy = 1 game.paused = false; diff --git a/js/index.js b/js/index.js index f64b893..f510696 100644 --- a/js/index.js +++ b/js/index.js @@ -87,8 +87,6 @@ const build = {
global damage increase: ${((b.damageFromMods()-1)*100).toFixed(0)}%
global harm reduction: ${((1-mech.harmReduction())*100).toFixed(0)}% `; - - let countGuns = 0 let countMods = 0 for (let i = 0, len = b.guns.length; i < len; i++) { @@ -269,10 +267,6 @@ const build = { document.getElementById("build-grid").style.display = "grid" }, shareURL() { - game.copyToClipBoard(build.generateURL()) - alert('n-gon build URL copied to clipboard.\nPaste into browser address bar.') - }, - generateURL() { let url = "https://landgreen.github.io/sidescroller/index.html?" let count = 0; for (let i = 0; i < b.guns.length; i++) { @@ -292,7 +286,8 @@ const build = { url += `&difficulty=${game.difficultyMode}` url += `&level=${Number(document.getElementById("starting-level").value)}` console.log(url) - return url + game.copyToClipBoard(url) + alert('n-gon build URL copied to clipboard.\nPaste into browser address bar.') }, startBuildRun() { build.isCustomSelection = false; diff --git a/js/level.js b/js/level.js index d6e73a5..79126b2 100644 --- a/js/level.js +++ b/js/level.js @@ -16,11 +16,8 @@ const level = { if (level.levelsCleared === 0) { //this code only runs on the first level // game.enableConstructMode() //used to build maps in testing mode // level.difficultyIncrease(9) - // b.giveGuns("foam") // mech.setField("time dilation field") - // b.giveMod("necrophoresis"); - // b.giveMod("impact shear"); - // b.giveMod("foam-bot"); + // b.giveMod("many worlds"); // b.giveGuns("neutron bomb") // b.giveGuns("foam") // mech.setField("pilot wave") @@ -162,10 +159,11 @@ const level = { // spawn.bomberBoss(2900, -500) // spawn.shooterBoss(1200, -500) - spawn.spinner(1200, -500, 40) + // spawn.starter(2500, -40, 40) // spawn.stabber(1600, -500) // spawn.cellBossCulture(1600, -500) // spawn.shooter(1600, -500) + spawn.focuser(1600, -500) // spawn.shield(mob[mob.length - 1], 1200, -500, 1); // spawn.nodeBoss(1200, -500, "spiker") diff --git a/js/mobs.js b/js/mobs.js index b0a89ef..9c1736b 100644 --- a/js/mobs.js +++ b/js/mobs.js @@ -507,7 +507,7 @@ const mobs = { }, springAttack() { // set new values of the ends of the spring constraints - if (this.seePlayer.recall) { + if (this.seePlayer.recall && Matter.Query.ray(map, this.position, player.position).length === 0) { if (!(game.cycle % (this.seePlayerFreq * 2))) { this.springTarget.x = this.seePlayer.position.x; this.springTarget.y = this.seePlayer.position.y; @@ -850,7 +850,6 @@ const mobs = { if (!(game.cycle % this.seePlayerFreq)) { this.fireDir = Vector.normalise(Vector.sub(this.seePlayer.position, this.position)); this.fireDir.y -= Math.abs(this.seePlayer.position.x - this.position.x) / 1600; //gives the bullet an arc - this.fireAngle = Math.atan2(this.fireDir.y, this.fireDir.x); } //rotate towards fireAngle const angle = this.angle + Math.PI / 2; @@ -885,6 +884,52 @@ const mobs = { // } } }, + launch() { + if (!mech.isBodiesAsleep) { + const setNoseShape = () => { + const mag = this.radius + this.radius * this.noseLength; + this.vertices[1].x = this.position.x + Math.cos(this.angle) * mag; + this.vertices[1].y = this.position.y + Math.sin(this.angle) * mag; + }; + //throw a mob/bullet at player + if (this.seePlayer.recall) { + //set direction to turn to fire + if (!(game.cycle % this.seePlayerFreq)) { + this.fireDir = Vector.normalise(Vector.sub(this.seePlayer.position, this.position)); + } + //rotate towards fireAngle + const angle = this.angle + Math.PI / 2; + c = Math.cos(angle) * this.fireDir.x + Math.sin(angle) * this.fireDir.y; + const threshold = 0.1; + if (c > threshold) { + this.torque += 0.0000045 * this.inertia; + } else if (c < -threshold) { + this.torque -= 0.0000045 * this.inertia; + } else if (this.noseLength > 1.5) { + //fire + spawn.seeker(this.vertices[1].x, this.vertices[1].y, 5 + Math.ceil(this.radius / 15), 5); + const v = 15; + Matter.Body.setVelocity(mob[mob.length - 1], { + x: this.velocity.x + this.fireDir.x * v + Math.random(), + y: this.velocity.y + this.fireDir.y * v + Math.random() + }); + this.noseLength = 0; + // recoil + this.force.x -= 0.005 * this.fireDir.x * this.mass; + this.force.y -= 0.005 * this.fireDir.y * this.mass; + } + if (this.noseLength < 1.5) this.noseLength += this.fireFreq; + setNoseShape(); + } else if (this.noseLength > 0.1) { + this.noseLength -= this.fireFreq / 2; + setNoseShape(); + } + // else if (this.noseLength < -0.1) { + // this.noseLength += this.fireFreq / 4; + // setNoseShape(); + // } + } + }, turnToFacePlayer() { //turn to face player const dx = player.position.x - this.position.x; diff --git a/js/player.js b/js/player.js index 9f8088c..60a195d 100644 --- a/js/player.js +++ b/js/player.js @@ -363,6 +363,7 @@ const mech = { if (b.isModImmortal) { //if player has the immortality buff, spawn on the same level with randomized stats spawn.setSpawnList(); //new mob types game.clearNow = true; //triggers a map reset + powerUps.reroll.rerolls = Math.floor(Math.random() * Math.random() * 8) //count mods let totalMods = 0; @@ -380,6 +381,7 @@ const mech = { b.mods[i].name !== "Born rule" && b.mods[i].name !== "determinism" && b.mods[i].name !== "reallocation" && + b.mods[i].name !== "many worlds" && b.mods[i].allowed() ) options.push(i); } @@ -1045,7 +1047,7 @@ const mech = { } if (mech.energy > mech.maxEnergy) mech.energy = mech.maxEnergy; - if (b.modBlockDmg) { + if (b.modBlockDmg && mech.fieldUpgrades[mech.fieldMode].name === "standing wave harmonics") { who.damage(b.modBlockDmg) //draw electricity const step = 40 @@ -1087,7 +1089,7 @@ const mech = { }); } } else { - if (b.isModStunField) mobs.statusStun(who, b.isModStunField) + if (b.isModStunField && mech.fieldUpgrades[mech.fieldMode].name === "perfect diamagnetism") mobs.statusStun(who, b.isModStunField) // mobs.statusSlow(who, b.isModStunField) const massRoot = Math.sqrt(Math.max(0.15, who.mass)); // masses above 12 can start to overcome the push back Matter.Body.setVelocity(who, { @@ -1434,7 +1436,7 @@ const mech = { }, { name: "negative mass field", - description: "use energy to nullify   gravity
reduce harm by 80% while field is active", //
launch larger blocks at much higher speeds + description: "use energy to nullify   gravity
reduce harm by 80% while field is active", fieldDrawRadius: 0, isEasyToAim: true, effect: () => { @@ -1569,10 +1571,11 @@ const mech = { }, { name: "plasma torch", - description: "use energy to emit damaging plasma
effective at close range", + description: "use energy to emit damaging plasma
reduce harm by 33%", isEasyToAim: false, effect: () => { mech.fieldMeterColor = "#f0f" + mech.fieldDamageResistance = 0.67; //reduce harm by 33% mech.hold = function () { if (mech.isHolding) { @@ -1582,7 +1585,7 @@ const mech = { } else if ((keys[32] || game.mouseDownRight) && mech.fieldCDcycle < mech.cycle) { //not hold but field button is pressed mech.grabPowerUp(); mech.lookForPickUp(); - const DRAIN = 0.00065 + const DRAIN = 0.0006 if (mech.energy > DRAIN) { mech.energy -= DRAIN; if (mech.energy < 0) { @@ -1666,8 +1669,12 @@ const mech = { best.who.locatePlayer(); //push mobs away - const force = Vector.mult(Vector.normalise(Vector.sub(mech.pos, path[1])), -0.02 * Math.sqrt(best.who.mass)) + const force = Vector.mult(Vector.normalise(Vector.sub(mech.pos, path[1])), -0.01 * Math.min(5, best.who.mass)) Matter.Body.applyForce(best.who, path[1], force) + Matter.Body.setVelocity(best.who, { //friction + x: best.who.velocity.x * 0.6, + y: best.who.velocity.y * 0.6 + }); // const angle = Math.atan2(player.position.y - best.who.position.y, player.position.x - best.who.position.x); // const mass = Math.min(Math.sqrt(best.who.mass), 6); // Matter.Body.setVelocity(best.who, { @@ -1685,7 +1692,7 @@ const mech = { }); } else if (!best.who.isStatic) { //push blocks away - const force = Vector.mult(Vector.normalise(Vector.sub(mech.pos, path[1])), -0.006 * Math.sqrt(Math.sqrt(best.who.mass))) + const force = Vector.mult(Vector.normalise(Vector.sub(mech.pos, path[1])), -0.007 * Math.sqrt(Math.sqrt(best.who.mass))) Matter.Body.applyForce(best.who, path[1], force) } } diff --git a/js/powerups.js b/js/powerups.js index d7623c4..e0b0b9e 100644 --- a/js/powerups.js +++ b/js/powerups.js @@ -21,6 +21,10 @@ const powerUps = { powerUps.endDraft(); }, endDraft() { + if (b.manyWorlds) { + powerUps.spawn(mech.pos.x, mech.pos.y, "reroll"); + if (Math.random() < b.modBayesian) powerUps.spawn(mech.pos.x, mech.pos.y, "reroll"); + } document.body.style.cursor = "none"; document.getElementById("choose-grid").style.display = "none" document.getElementById("choose-background").style.display = "none" @@ -162,7 +166,7 @@ const powerUps = { let choice2 = -1 let choice3 = -1 if (choice1 > -1) { - let text = `

choose a field

` + let text = `

choose a field

` text += `
  ${mech.fieldUpgrades[choice1].name}
${mech.fieldUpgrades[choice1].description}
` if (!b.isModDeterminism) { choice2 = pick(mech.fieldUpgrades, choice1) @@ -207,7 +211,7 @@ const powerUps = { let choice2 = -1 let choice3 = -1 if (choice1 > -1) { - let text = `

choose a mod

` + let text = `

choose a mod

` text += `
  ${b.mods[choice1].name}
${b.mods[choice1].description}
` if (!b.isModDeterminism) { choice2 = pick(choice1) @@ -249,7 +253,7 @@ const powerUps = { let choice2 = -1 let choice3 = -1 if (choice1 > -1) { - let text = `

choose a gun

` + let text = `

choose a gun

` text += `
  ${b.guns[choice1].name}
${b.guns[choice1].description}
` if (!b.isModDeterminism) { choice2 = pick(b.guns, choice1) @@ -312,7 +316,7 @@ const powerUps = { if (Math.random() < b.modBayesian) powerUps.spawn(x, y, "field"); return; } - if (Math.random() < 0.01) { + if (Math.random() < 0.005) { powerUps.spawn(x, y, "reroll"); if (Math.random() < b.modBayesian) powerUps.spawn(x, y, "reroll"); return; @@ -352,7 +356,7 @@ const powerUps = { } }, chooseRandomPowerUp(x, y) { //100% chance to drop a random power up //used in spawn.debris - if (Math.random() < 0.02) { + if (Math.random() < 0.05) { powerUps.spawn(x, y, "reroll"); } else if (Math.random() < 0.5) { powerUps.spawn(x, y, "heal", false); diff --git a/js/spawn.js b/js/spawn.js index 665e762..8c75c8c 100644 --- a/js/spawn.js +++ b/js/spawn.js @@ -2,13 +2,14 @@ const spawn = { pickList: ["starter", "starter"], fullPickList: [ - "shooter", "shooter", "shooter", "shooter", "shooter", + "shooter", "shooter", "shooter", "shooter", "hopper", "hopper", "hopper", "hopper", "chaser", "chaser", "chaser", "striker", "striker", "laser", "laser", "exploder", "exploder", "stabber", "stabber", + "launcher", "launcher", "spinner", "grower", "springer", @@ -19,7 +20,7 @@ const spawn = { "ghoster", "sneaker", ], - allowedBossList: ["chaser", "spinner", "striker", "springer", "laser", "focuser", "beamer", "exploder", "spawner", "shooter", "stabber"], + allowedBossList: ["chaser", "spinner", "striker", "springer", "laser", "focuser", "beamer", "exploder", "spawner", "shooter", "launcher", "stabber"], 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); @@ -845,30 +846,30 @@ const spawn = { if (!mech.isBodiesAsleep) { this.seePlayerByLookingAt(); this.checkStatus(); + this.attraction(); const dist2 = this.distanceToPlayer2(); //laser Tracking - if (this.seePlayer.yes && dist2 < 4000000 && !mech.isStealth) { - this.attraction(); + if (this.seePlayer.yes && dist2 < 4000000) { const rangeWidth = 2000; //this is sqrt of 4000000 from above if() //targeting laser will slowly move from the mob to the player's position this.laserPos = Vector.add(this.laserPos, Vector.mult(Vector.sub(player.position, this.laserPos), 0.1)); let targetDist = Vector.magnitude(Vector.sub(this.laserPos, mech.pos)); - const r = 10; + const r = 12; ctx.beginPath(); ctx.moveTo(this.position.x, this.position.y); - if (targetDist < r + 15) { + if (targetDist < r + 16) { targetDist = r + 10; //charge at player - const forceMag = this.accelMag * 40 * this.mass; + const forceMag = this.accelMag * 30 * this.mass; const angle = Math.atan2(this.seePlayer.position.y - this.position.y, this.seePlayer.position.x - this.position.x); this.force.x += forceMag * Math.cos(angle); this.force.y += forceMag * Math.sin(angle); } else { //high friction if can't lock onto player - Matter.Body.setVelocity(this, { - x: this.velocity.x * 0.96, - y: this.velocity.y * 0.96 - }); + // Matter.Body.setVelocity(this, { + // x: this.velocity.x * 0.98, + // y: this.velocity.y * 0.98 + // }); } if (dist2 > 80000) { const laserWidth = 0.002; @@ -1436,6 +1437,58 @@ const spawn = { this.timeLimit(); }; }, + launcher(x, y, radius = 25 + Math.ceil(Math.random() * 50)) { + mobs.spawn(x, y, 3, radius, "rgb(150,150,255)"); + let me = mob[mob.length - 1]; + me.vertices = Matter.Vertices.clockwiseSort(Matter.Vertices.rotate(me.vertices, Math.PI, me.position)); //make the pointy side of triangle the front + me.isVerticesChange = true + // Matter.Body.rotate(me, Math.PI) + + me.memory = 120; + me.fireFreq = 0.006 + Math.random() * 0.003; + me.noseLength = 0; + me.fireAngle = 0; + me.accelMag = 0.0005 * game.accelScale; + me.frictionAir = 0.05; + me.lookTorque = 0.0000028 * (Math.random() > 0.5 ? -1 : 1); + me.fireDir = { + x: 0, + y: 0 + }; + me.onDeath = function () { //helps collisions functions work better after vertex have been changed + // this.vertices = Matter.Vertices.hull(Matter.Vertices.clockwiseSort(this.vertices)) + } + // spawn.shield(me, x, y); + me.do = function () { + this.seePlayerByLookingAt(); + this.checkStatus(); + this.launch(); + }; + }, + seeker(x, y, radius = 6, sides = 0) { + //bullets + mobs.spawn(x, y, sides, radius, "rgb(0,0,255)"); + let me = mob[mob.length - 1]; + me.stroke = "transparent"; + me.onHit = function () { + this.explode(this.mass * 10); + }; + Matter.Body.setDensity(me, 0.00005); //normal is 0.001 + me.timeLeft = 420; + me.accelMag = 0.0004 * game.accelScale; + me.frictionAir = 0.035; + me.restitution = 0.5; + me.leaveBody = false; + me.dropPowerUp = false; + me.showHealthBar = false; + me.collisionFilter.category = cat.mobBullet; + me.collisionFilter.mask = cat.player | cat.map | cat.body | cat.bullet; + me.do = function () { + this.seePlayerCheck(); + this.attraction(); + this.timeLimit(); + }; + }, spawner(x, y, radius = 55 + Math.ceil(Math.random() * 50)) { mobs.spawn(x, y, 4, radius, "rgb(255,150,0)"); let me = mob[mob.length - 1]; diff --git a/style.css b/style.css index a69d8d3..f0fdf70 100644 --- a/style.css +++ b/style.css @@ -466,6 +466,11 @@ em { letter-spacing: 1px; } +.color-r { + color: #f7b; + letter-spacing: 1px; +} + .faded { opacity: 0.7; font-size: 90%; @@ -511,7 +516,6 @@ em { } .reroll { - /* #f84 #f99*/ background: #f7b; } diff --git a/todo.txt b/todo.txt index 6d1354b..05c6f6a 100644 --- a/todo.txt +++ b/todo.txt @@ -1,20 +1,21 @@ -foam bullets are faster, smaller, shrink slower, stick to mobs better -foam can now stick to shielded mobs, but shrinks faster -mod: foam-bots +mod - many worlds: spawn a reroll after choosing (or canceling) a power up +new mob type - launcher: similar to the shooter, but fires bullets that chase you +plasma torch - reduces harm to player by 1/3 and has more reliable stopping power against charging mobs +explosions and neutron bomb do 50% less damage through walls +drone bullets bounce off mobs more predictably ************** TODO - n-gon ************** -power up reroll - color yellow? +what about a neutron bomb mod, that cause the bomb to activate right after you fire and slowly move forward with no gravity +Drop a reroll when the player exits the selection screen without picking one of the choices + +redblobgames.com/articles/visibility + https://github.com/Silverwolf90/2d-visibility/tree/master/src + could apply to explosions, neutron bomb, player LOS sticking bullets don't always gain the correct speed from mobs after they die -mod - frozen mobs take +33% damage - -mod heal to full at the end of each level - heal mods no longer drop? - possible names for mods Hypergolic - A hypergolic propellant combination used in a rocket engine is one whose components spontaneously ignite when they come into contact with each other. @@ -24,19 +25,6 @@ have a mob apply a positive status effect on other mobs, damage bonus, but how? possible balance issues -mobs that fire grenades -mobs that fire bullets - bullets that chase the player a bit (blue color) - short lived bullets that fire in every direction - or fire in a spiral over time - -construct - display outline of map to be draw while mouse is down - toggle between maps and bodies - ?highlight map/body mouse is over and be able to remove it? - display current output text in a box - live update it - boss levels - small levels just a boss, and maybe a few mobs boss level for timeSkipBoss because of game instability for boss on normal levels this might not fix issues @@ -65,10 +53,6 @@ lore - a robot (the player) gains self awareness the next level is the final level when you die with Quantum Immortality there is a chance of lore text -scrap drops that can be used to buy things in a shop - scrap could be yellow - shop could appear every 4 levels - mob - stuns, or slows player boss mob - let it die multiple times and come back to life @@ -90,16 +74,11 @@ uranium reactor core add player Scent Trails for mob navigation https://abitawake.com/news/articles/enemy-ai-chasing-a-player-without-navigation2d-or-a-star-pathfinding -mod - killing a stunned mob gives something - ammo or heal power up? - mod - scale squirrel cage rotor with current energy is variable speed going to be hard to deal with? mod - you can no longer see your current health -mob sniper - targeting laser, then a high speed, no gravity bullet - mod - increase laser bot range, and reduce energy drain mod - mines become a turret that fires nails