diff --git a/.gitignore b/.gitignore index 73bad25..97e094b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ .jsbeautifyrc .DS_Store +.DS_Store diff --git a/js/bullet.js b/js/bullet.js index 3b2e7fc..d3090bb 100644 --- a/js/bullet.js +++ b/js/bullet.js @@ -852,7 +852,7 @@ const b = { if (mod.iceEnergy && !who.shield && !who.isShielded && who.dropPowerUp && who.alive) { setTimeout(function() { if (!who.alive) { - mech.energy += mod.iceEnergy * 0.66 * mech.maxEnergy + mech.energy += mod.iceEnergy * 0.5 * mech.maxEnergy mech.addHealth(mod.iceEnergy * 0.04) } }, 10); @@ -2066,7 +2066,7 @@ const b = { y: SPEED * Math.sin(dir) }); // Matter.Body.setDensity(bullet[me], 0.0001); - bullet[me].endCycle = game.cycle + Math.floor((300 + 60 * Math.random()) * mod.isBulletsLastLonger); + bullet[me].endCycle = game.cycle + Math.floor(300 + 60 * Math.random()); bullet[me].minDmgSpeed = 0; bullet[me].restitution = 1; bullet[me].friction = 0; @@ -2363,12 +2363,13 @@ const b = { //missile(where, dir, speed, size = 1, spawn = 0) { if (mod.is3Missiles) { if (mech.crouch) { - mech.fireCDcycle = mech.cycle + 18 * b.fireCD; // cool down + mech.fireCDcycle = mech.cycle + 17 * b.fireCD; // cool down for (let i = 0; i < 3; i++) { b.missile({ x: mech.pos.x, y: mech.pos.y - 40 - }, -Math.PI / 2 + 0.08 * (1 - i), 0, 0.7, mod.recursiveMissiles) + }, -Math.PI / 2 + 0.08 * (1 - i) + 0.3 * (Math.random() - 0.5), 0, 0.7, mod.recursiveMissiles) + bullet[bullet.length - 1].force.x -= 0.015 * (i - 1); } } else { mech.fireCDcycle = mech.cycle + 55 * b.fireCD; // cool down @@ -2381,19 +2382,22 @@ const b = { b.missile({ x: mech.pos.x + 40 * direction.x, y: mech.pos.y + 40 * direction.y - }, mech.angle + 0.1 * (Math.random() - 0.5), 5, 0.7, mod.recursiveMissiles) + }, mech.angle + 0.06 * (Math.random() - 0.5), 5, 0.7, mod.recursiveMissiles) bullet[bullet.length - 1].force.x += push.x * (i - 1); bullet[bullet.length - 1].force.y += push.y * (i - 1); } } } else { if (mech.crouch) { - mech.fireCDcycle = mech.cycle + 18 * b.fireCD; // cool down + mech.fireCDcycle = mech.cycle + 17 * b.fireCD; // cool down + const off = Math.random() - 0.5 b.missile({ x: mech.pos.x, y: mech.pos.y - 40 }, - -Math.PI / 2, 0, 1, mod.recursiveMissiles) + -Math.PI / 2 + 0.15 * off, 0, 1, mod.recursiveMissiles) + bullet[bullet.length - 1].force.x += off * 0.03; + // bullet[bullet.length - 1].force.y += push.y * (i - 1); } else { mech.fireCDcycle = mech.cycle + 55 * b.fireCD; // cool down b.missile({ @@ -2501,7 +2505,7 @@ const b = { const me = bullet.length; const dir = mech.angle; bullet[me] = Bodies.polygon(mech.pos.x + 30 * Math.cos(mech.angle), mech.pos.y + 30 * Math.sin(mech.angle), 10, 4, b.fireAttributes(dir, false)); - b.fireProps(mech.crouch ? 45 : 25, mech.crouch ? 30 : 20, dir, me); //cd , speed + b.fireProps(mech.crouch ? 45 : 25, mech.crouch ? 35 : 20, dir, me); //cd , speed Matter.Body.setDensity(bullet[me], 0.000001); bullet[me].endCycle = Infinity; bullet[me].frictionAir = 0; @@ -2510,9 +2514,11 @@ const b = { bullet[me].restitution = 0; bullet[me].minDmgSpeed = 0; bullet[me].damageRadius = 100; - bullet[me].maxDamageRadius = (435 + 150 * Math.random()) * (mod.isNeutronImmune ? 1.2 : 1) + bullet[me].maxDamageRadius = 450 + 130 * mod.isNeutronSlow + 130 * mod.isNeutronImmune //+ 150 * Math.random() + bullet[me].radiusDecay = (0.81 + 0.15 * mod.isNeutronSlow + 0.15 * mod.isNeutronImmune) / mod.isBulletsLastLonger bullet[me].stuckTo = null; bullet[me].stuckToRelativePosition = null; + bullet[me].vacuumSlow = 0.97; bullet[me].beforeDmg = function() {}; bullet[me].stuck = function() {}; bullet[me].do = function() { @@ -2581,12 +2587,11 @@ const b = { } } } - bullet[me].radiationMode = function() { + bullet[me].radiationMode = function() { //the do code after the bullet is stuck on something, projects a damaging radiation field this.stuck(); //runs different code based on what the bullet is stuck to if (!mech.isBodiesAsleep) { this.damageRadius = this.damageRadius * 0.85 + 0.15 * this.maxDamageRadius //smooth radius towards max - this.maxDamageRadius -= 0.8 / mod.isBulletsLastLonger //+ 0.5 * Math.sin(game.cycle * 0.1) //slowly shrink max radius - + this.maxDamageRadius -= this.radiusDecay if (this.damageRadius < 15) { this.endCycle = 0; } else { @@ -2603,11 +2608,17 @@ 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) { - let dmg = b.dmgScale * 0.08 + let dmg = b.dmgScale * 0.082 if (Matter.Query.ray(map, mob[i].position, this.position).length > 0) dmg *= 0.25 //reduce damage if a wall is in the way if (mob[i].shield) dmg *= 4 //x5 to make up for the /5 that shields normally take mob[i].damage(dmg); mob[i].locatePlayer(); + if (mod.isNeutronSlow) { + Matter.Body.setVelocity(mob[i], { + x: mob[i].velocity.x * this.vacuumSlow, + y: mob[i].velocity.y * this.vacuumSlow + }); + } } } ctx.beginPath(); @@ -2616,6 +2627,24 @@ const b = { ctx.fillStyle = `rgba(25,139,170,${0.2+0.06*Math.random()})`; ctx.fill(); ctx.globalCompositeOperation = "source-over" + if (mod.isNeutronSlow) { + const that = this + + function slow(who, radius = that.explodeRad * 3.2) { + for (i = 0, len = who.length; i < len; i++) { + const sub = Vector.sub(that.position, who[i].position); + const dist = Vector.magnitude(sub); + if (dist < radius) { + Matter.Body.setVelocity(who[i], { + x: who[i].velocity.x * that.vacuumSlow, + y: who[i].velocity.y * that.vacuumSlow + }); + } + } + } + slow(body, this.damageRadius) + slow([player], this.damageRadius) + } } } } diff --git a/js/game.js b/js/game.js index 5a101b3..26733a1 100644 --- a/js/game.js +++ b/js/game.js @@ -459,13 +459,9 @@ const game = { addGravity(body, game.g); player.force.y += player.mass * game.g; }, - // reset() { //run on first run, and each later run after you die - - // }, firstRun: true, splashReturn() { game.onTitlePage = true; - // document.getElementById('splash').onclick = 'run(this)'; document.getElementById("splash").onclick = function() { game.startGame(); }; diff --git a/js/level.js b/js/level.js index f7034fc..b7c70e1 100644 --- a/js/level.js +++ b/js/level.js @@ -13,16 +13,15 @@ const level = { start() { if (level.levelsCleared === 0) { //this code only runs on the first level // game.enableConstructMode() //used to build maps in testing mode - // level.difficultyIncrease(8) + // level.difficultyIncrease(26) // game.zoomScale = 1000; // game.setZoom(); // mech.setField("wormhole") // b.giveGuns("missiles") // mod.is3Missiles = true - // mod.giveMod("incendiary ammunition") + // mod.giveMod("neutron bomb") // mod.giveMod("super ball") - level.intro(); //starting level // level.testing(); //not in rotation // level.finalBoss() //final boss level @@ -147,16 +146,16 @@ const level = { // spawn.launcherBoss(1200, -500) // spawn.laserTargetingBoss(1600, -400) // spawn.spawner(1600, -500) - spawn.shooter(1700, -120) + // spawn.shooter(1700, -120) // spawn.bomberBoss(1400, -500) - spawn.sniper(1800, -120) + // spawn.sniper(1800, -120) // spawn.cellBossCulture(1600, -500) // spawn.spiderBoss(1600, -500) - spawn.launcher(1200, -500) + // spawn.launcher(1200, -500) // spawn.shield(mob[mob.length - 1], 1800, -120, 1); // spawn.nodeBoss(1200, -500, "launcher") - // spawn.snakeBoss(1200, -500) + spawn.snakeBoss(1200, -500) // spawn.timeSkipBoss(2900, -500) // spawn.randomMob(1600, -500) }, diff --git a/js/mob.js b/js/mob.js index 225b123..27a1463 100644 --- a/js/mob.js +++ b/js/mob.js @@ -145,7 +145,7 @@ const mobs = { who.isStunned = false }, type: "stun", - endCycle: game.cycle + cycles, + endCycle: game.cycle + cycles * (who.isBoss ? 0.2 : 1), }) } }, diff --git a/js/mods.js b/js/mods.js index 612a164..31bfd48 100644 --- a/js/mods.js +++ b/js/mods.js @@ -1596,7 +1596,7 @@ const mod = { maxCount: 1, count: 0, allowed() { - return (mech.fieldUpgrades[mech.fieldMode].name === "nano-scale manufacturing" && !(mod.isSporeField || mod.isMissileField || mod.isIceField)) || mod.haveGunCheck("drones") || mod.haveGunCheck("super balls") || (mod.haveGunCheck("nail gun") && !mod.isIceCrystals && !mod.isNailCrit) || (mod.haveGunCheck("shotgun") && !mod.isNailShot) + return (mech.fieldUpgrades[mech.fieldMode].name === "nano-scale manufacturing" && !(mod.isSporeField || mod.isMissileField || mod.isIceField)) || mod.haveGunCheck("drones") || mod.haveGunCheck("super balls") || (mod.haveGunCheck("nail gun")) || (mod.haveGunCheck("shotgun")) && !mod.isIceCrystals && !mod.isNailCrit && !mod.isNailShot && !mod.isNailPoison }, requires: "drones, super balls, nail gun, shotgun", effect() { @@ -1608,15 +1608,15 @@ const mod = { }, { name: "Lorentzian topology", - description: "bullets last 33% longer
drones, spores, super balls, foam, wave, ice IX, neutron", + description: "bullets last 30% longer
drones, spores, missiles, foam, wave, ice IX, neutron", maxCount: 3, count: 0, allowed() { - return mech.fieldUpgrades[mech.fieldMode].name === "nano-scale manufacturing" || mod.haveGunCheck("spores") || mod.haveGunCheck("drones") || mod.haveGunCheck("super balls") || mod.haveGunCheck("foam") || mod.haveGunCheck("wave beam") || mod.haveGunCheck("ice IX") || mod.haveGunCheck("neutron bomb") + return mech.fieldUpgrades[mech.fieldMode].name === "nano-scale manufacturing" || mod.haveGunCheck("spores") || mod.haveGunCheck("drones") || mod.haveGunCheck("missiles") || mod.haveGunCheck("foam") || mod.haveGunCheck("wave beam") || mod.haveGunCheck("ice IX") || mod.isNeutronBomb }, - requires: "drones, spores, super balls, foam
wave beam, ice IX, neutron bomb", + requires: "drones, spores, missiles, foam
wave beam, ice IX, neutron bomb", effect() { - mod.isBulletsLastLonger += 0.33 + mod.isBulletsLastLonger += 0.3 }, remove() { mod.isBulletsLastLonger = 1; @@ -2071,7 +2071,7 @@ const mod = { allowed() { return mod.haveGunCheck("grenades") && !mod.isRPG && !mod.isNeutronBomb }, - requires: "grenades, not rocket-propelled or neutron bomb", + requires: "grenades, not rocket-propelled", effect() { mod.isVacuumBomb = true; for (i = 0, len = b.guns.length; i < len; i++) { //find which gun @@ -2080,8 +2080,10 @@ const mod = { }, remove() { mod.isVacuumBomb = false; - for (i = 0, len = b.guns.length; i < len; i++) { //find which gun - if (b.guns[i].name === "grenades") b.guns[i].fire = b.guns[i].fireNormal + if (!mod.isNeutronBomb) { + for (i = 0, len = b.guns.length; i < len; i++) { //find which gun + if (b.guns[i].name === "grenades") b.guns[i].fire = (mod.isNeutronBomb) ? b.guns[i].fireNeutron : b.guns[i].fireNormal + } } } }, @@ -2091,9 +2093,9 @@ const mod = { maxCount: 1, count: 0, allowed() { - return mod.haveGunCheck("grenades") && !mod.isRPG && !mod.isVacuumBomb && !mod.grenadeFragments + return mod.haveGunCheck("grenades") && !mod.isRPG && !mod.grenadeFragments && !mod.isVacuumBomb }, - requires: "grenades, not rocket-propelled, vacuum bomb, or fragmentation", + requires: "grenades, not rocket-propelled or fragmentation", effect() { mod.isNeutronBomb = true; for (i = 0, len = b.guns.length; i < len; i++) { //find which gun @@ -2103,7 +2105,7 @@ const mod = { remove() { mod.isNeutronBomb = false; for (i = 0, len = b.guns.length; i < len; i++) { //find which gun - if (b.guns[i].name === "grenades") b.guns[i].fire = b.guns[i].fireNormal + if (b.guns[i].name === "grenades") b.guns[i].fire = (mod.isVacuumBomb) ? b.guns[i].fireVacuum : b.guns[i].fireNormal } } }, @@ -2123,6 +2125,22 @@ const mod = { mod.isNeutronImmune = false } }, + { + name: "vacuum permittivity", + description: "increase neutron bomb's range by 20%
objects in range of the bomb are slowed", + maxCount: 1, + count: 0, + allowed() { + return mod.isNeutronBomb + }, + requires: "neutron bomb", + effect() { + mod.isNeutronSlow = true + }, + remove() { + mod.isNeutronSlow = false + } + }, { name: "mine reclamation", description: "retrieve ammo from all undetonated mines
and 20% of mines after detonation", @@ -2334,7 +2352,7 @@ const mod = { }, { name: "thermoelectric effect", - description: "killing mobs with ice IX gives 4% health
and overloads energy by 166% of your max", + description: "killing mobs with ice IX gives 4% health
and overloads energy by 50% of your max", maxCount: 9, count: 0, allowed() { @@ -3279,5 +3297,6 @@ const mod = { isRailEnergyGain: null, isMineSentry: null, isIncendiary: null, - overfillDrain: null + overfillDrain: null, + isNeutronSlow: null } \ No newline at end of file diff --git a/js/player.js b/js/player.js index 1bdddc7..4e5d05a 100644 --- a/js/player.js +++ b/js/player.js @@ -390,6 +390,8 @@ const mech = { document.getElementById("text-log").style.opacity = 0; //fade out any active text logs document.getElementById("fade-out").style.opacity = 1; //slowly fades out setTimeout(function() { + World.clear(engine.world); + Engine.clear(engine); game.splashReturn(); }, 3000); } @@ -462,6 +464,7 @@ const mech = { if (Math.random() < 0.5) b.drone() //spawn drone } } + if (mod.isEnergyHealth) { mech.energy -= dmg; if (mech.energy < 0 || isNaN(mech.energy)) { //taking deadly damage @@ -1339,7 +1342,7 @@ const mech = { description: "use energy to block mobs
excess energy used to build drones
double your default energy regeneration", effect: () => { mech.hold = function() { - if (mech.energy > mech.maxEnergy - 0.02 && mech.fieldCDcycle < mech.cycle) { + if (mech.energy > mech.maxEnergy - 0.02 && mech.fieldCDcycle < mech.cycle && input.field) { if (mod.isSporeField) { // mech.fieldCDcycle = mech.cycle + 10; // set cool down to prevent +energy from making huge numbers of drones const len = Math.floor(6 + 5 * Math.random()) diff --git a/js/spawn.js b/js/spawn.js index 91ea90c..d82e596 100644 --- a/js/spawn.js +++ b/js/spawn.js @@ -2192,16 +2192,23 @@ const spawn = { }; }, snakeBoss(x, y, radius = 75) { //snake boss with a laser head - mobs.spawn(x, y, 8, radius, "rgb(255,50,130)"); + mobs.spawn(x, y, 8, radius, "rgb(55,170,170)"); let me = mob[mob.length - 1]; me.isBoss = true; - me.accelMag = 0.0011 * game.accelScale; + me.accelMag = 0.0008 * game.accelScale; me.memory = 250; me.laserRange = 500; - Matter.Body.setDensity(me, 0.0013 + 0.0005 * Math.sqrt(game.difficulty)); //extra dense //normal is 0.001 //makes effective life much larger + Matter.Body.setDensity(me, 0.0015 + 0.0005 * 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) + //wake up tail mobs + for (let i = 0; i < mob.length; i++) { + if (mob[i].isSnakeTail && mob[i].alive) { + mob[i].do = mob[i].doActive + mob[i].removeConsBB(); + } + } }; me.do = function() { this.seePlayerCheck(); @@ -2211,7 +2218,7 @@ const spawn = { }; //snake tail - const nodes = 2 + Math.min(3 + Math.ceil(Math.random() * game.difficulty + 2), 8) + const nodes = Math.min(8 + Math.ceil(0.5 * game.difficulty), 40) spawn.lineBoss(x + 105, y, "snakeBody", nodes); //constraint boss with first 3 mobs in lineboss consBB[consBB.length] = Constraint.create({ @@ -2235,7 +2242,7 @@ const spawn = { }, snakeBody(x, y, radius = 20) { - mobs.spawn(x, y, 4, radius, "rgb(255,0,0)"); + mobs.spawn(x, y, 4, radius, "rgb(55,170,170)"); let me = mob[mob.length - 1]; me.onHit = function() { //run this function on hitting player @@ -2243,16 +2250,18 @@ const spawn = { }; me.collisionFilter.mask = cat.bullet | cat.player // me.g = 0.0002; //required if using 'gravity' - // me.accelMag = 0 //0.001 * game.accelScale; - // me.memory = 0; + me.accelMag = 0.001 * game.accelScale; me.leaveBody = false; - // me.seePlayerFreq = Math.round((80 + 50 * Math.random()) * game.lookFreqScale); + me.seePlayerFreq = Math.round((80 + 50 * Math.random()) * game.lookFreqScale); me.frictionAir = 0.02; + me.isSnakeTail = true; me.do = function() { - // this.gravity(); - // this.seePlayerCheck(); this.checkStatus(); - // this.attraction(); + }; + me.doActive = function() { + this.seePlayerCheck(); + this.checkStatus(); + this.attraction(); }; }, tetherBoss(x, y, radius = 90) { diff --git a/todo.txt b/todo.txt index e95a331..f4290e9 100644 --- a/todo.txt +++ b/todo.txt @@ -1,8 +1,11 @@ *********** NEXT PATCH *********** -some changes to snake boss -supercapacitor - overfill energy decays 66% slower - (thanks to NoHaxJustPi for coding this) +snake boss is updated + -fear the snake + +mod neutron bomb: vacuum permittivity - gets a larger radius and a slow effect + +bosses are only stunned for 1/5 of the normal stun time ************** BUGS ************** @@ -25,7 +28,10 @@ supercapacitor - overfill energy decays 66% slower ************** TODO ************** -vacuum and neutron bomb could merge +mod - railgun's push effect is increased, and it does some damage to nearby mobs + maybe only triggers at max energy + +Laser mod: For each reflection of laser, damage increases by 10% new power up - increase damage and fire speed, for 15 seconds named boost? @@ -34,43 +40,27 @@ new power up - increase damage and fire speed, for 15 seconds how to indicate effect duration or just give the effect after picking up a reroll -mod - overfilled energy decays slower +mod - bot very slowly follows you and gives you a bonus when it's in range + it moves through walls + effect: damage bonus?, damage reduction?, push away mobs, limit top speed of mobs/blocks/player? -mod - remove max health, but you take 60% more harm - -mod - taking damage fires your current weapon at the nearest mob - requires? some harm reduction - -mod - a 4th mod selection option that is always a bot - -mod - railgun's push effect is increased, and it does some damage to nearby mobs - maybe only triggers at max energy - -mod - nano scale field could be a mod - excess energy is converted to bullets - run this code in the energy overfill code check - possible issues - worm hole, Penrose process - nerf a bit - pair production - nerf and give it to anyone - cloaking field - maybe just don't spawn bullets when cloaked +add an ending to the game + maybe the game ending should ask you to open the console and type in some commands like in the end of doki doki + mirror ending (if no cheats) + level after final boss battle is the intro level, but flipped left right, with a fake player + damage the fake player to end the game + message about go outside + no ending (if cheats) + game goes on forever + also game goes on if player attacks, the fake player + game never ends if you have used cheats Mod: "Solar Power": Energy regeneration is doubled while standing still run in the 1 second check -mod - a bot that eats up health and ammo, but poops out a mod (10 power ups = 1 mod?) - or it poops a reroll after picking up 2 heal/ammo - requires the reroll -> bot mod - 4 rerolls can convert to a bot, also 1 rerolls can convert to 5% damage - the mods that do those effects could be required before you see this bot - it passes through walls - moves slower then the player so you can get to it before the bot if you hurry - disable crystalized armor? - mod: take less harm if you are in the air require squirrel cage rotor -Laser mod: For each reflection of laser, damage increases by 10% - mechanic - remove a random mod as a condition for picking up a really good mod mechanic - do something for 2 seconds after firing @@ -80,10 +70,6 @@ mod - do 50% more damage in close, but 50% less at a distance code it like mod.isFarAwayDmg have these mods disable each other -mod - bot very slowly follows you and gives you a bonus when it's in range - it moves through walls - effect: damage bonus?, damage reduction?, push away mobs, limit top speed of mobs/blocks/player? - mod - foam is attracted to mobs use a gravitational attraction model? could foam be attracted to other foam bullets too? @@ -105,21 +91,11 @@ repeat map in vertical and horizontal space or at least vertical space camera looks strange when you teleport player with a high velocity -add an ending to the game - maybe the game ending should ask you to open the console and type in some commands like in the end of doki doki - mirror ending (if no cheats) - level after final boss battle is the intro level, but flipped left right, with a fake player - damage the fake player to end the game - message about go outside - no ending (if cheats) - game goes on forever - also game goes on if player attacks, the fake player - game never ends if you have used cheats - new status effect: fear - 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 + or zero cycles and it doesn't need to be a status have some mobs spawn in later in the level (in hard and why modes) where