diff --git a/js/bullets.js b/js/bullets.js index 1530335..8ac9824 100644 --- a/js/bullets.js +++ b/js/bullets.js @@ -83,6 +83,8 @@ const b = { isModRPG: null, isMod3Missiles: null, isModDeterminism: null, + isModHarmReduce: null, + modNailsDeathMob: null, modOnHealthChange() { //used with acid mod if (b.isModAcidDmg && mech.health > 0.8) { b.modAcidDmg = 0.5 @@ -407,6 +409,22 @@ const b = { b.isModExplodeMob = false; } }, + { + name: "impact shear", + description: "mobs release +2 nails when they die
nails target nearby mobs", + maxCount: 9, + count: 0, + allowed() { + return true + }, + requires: "", + effect: () => { + b.modNailsDeathMob += 2 + }, + remove() { + b.modNailsDeathMob = 0; + } + }, { name: "reaction inhibitor", description: "mobs die if their life goes below 12%", @@ -1405,6 +1423,22 @@ const b = { b.isModPlasmaRange = 1; } }, + { + name: "degenerate matter", + description: "2x energy drain for negative mass field
increase harm reduction to 90%", + maxCount: 1, + count: 0, + allowed() { + return mech.fieldUpgrades[mech.fieldMode].name === "negative mass field" + }, + requires: "negative mass field", + effect() { + b.isModHarmReduce = true + }, + remove() { + b.isModHarmReduce = false; + } + }, { name: "annihilation", description: "after touching mobs, they are annihilated", @@ -2081,35 +2115,7 @@ const b = { }, onEnd() { if (this.isArmed) { - const targets = [] //target nearby mobs - for (let i = 0, len = mob.length; i < len; i++) { - if (mob[i].dropPowerUp) { - const dist = Vector.magnitudeSquared(Vector.sub(this.position, mob[i].position)); - if (dist < 1440000 && //1200*1200 - Matter.Query.ray(map, this.position, mob[i].position).length === 0 && - Matter.Query.ray(body, this.position, mob[i].position).length === 0) { - targets.push(Vector.add(mob[i].position, Vector.mult(mob[i].velocity, Math.sqrt(dist) / 60))) //predict where the mob will be in a few cycles - } - } - } - for (let i = 0; i < 14; i++) { - const speed = 53 + 10 * Math.random() - if (targets.length > 0) { // aim near a random target in array - const index = Math.floor(Math.random() * targets.length) - const SPREAD = 150 / targets.length - const WHERE = { - x: targets[index].x + SPREAD * (Math.random() - 0.5), - y: targets[index].y + SPREAD * (Math.random() - 0.5) - } - b.nail(this.position, Vector.mult(Vector.normalise(Vector.sub(WHERE, this.position)), speed), 1.1) - } else { // aim in random direction - const ANGLE = 2 * Math.PI * Math.random() - b.nail(this.position, { - x: speed * Math.cos(ANGLE), - y: speed * Math.sin(ANGLE) - }) - } - } + b.targetedNail(this.position, 14) } if (isAmmoBack) { //get ammo back from b.isModMineAmmoBack for (i = 0, len = b.guns.length; i < len; i++) { //find which gun @@ -2371,6 +2377,36 @@ const b = { y: speed * Math.sin(dir) }); }, + targetedNail(position, num = 1, speed = 50 + 10 * Math.random(), range = 1200) { + const targets = [] //target nearby mobs + for (let i = 0, len = mob.length; i < len; i++) { + if (mob[i].dropPowerUp) { + const dist = Vector.magnitude(Vector.sub(position, mob[i].position)); + if (dist < range && + Matter.Query.ray(map, position, mob[i].position).length === 0 && + Matter.Query.ray(body, position, mob[i].position).length === 0) { + targets.push(Vector.add(mob[i].position, Vector.mult(mob[i].velocity, dist / 60))) //predict where the mob will be in a few cycles + } + } + } + for (let i = 0; i < num; i++) { + if (targets.length > 0) { // aim near a random target in array + const index = Math.floor(Math.random() * targets.length) + const SPREAD = 150 / targets.length + const WHERE = { + x: targets[index].x + SPREAD * (Math.random() - 0.5), + y: targets[index].y + SPREAD * (Math.random() - 0.5) + } + b.nail(position, Vector.mult(Vector.normalise(Vector.sub(WHERE, position)), speed), 1.1) + } else { // aim in random direction + const ANGLE = 2 * Math.PI * Math.random() + b.nail(position, { + x: speed * Math.cos(ANGLE), + y: speed * Math.sin(ANGLE) + }) + } + } + }, nail(pos, velocity, dmg = 0) { const me = bullet.length; bullet[me] = Bodies.rectangle(pos.x, pos.y, 25, 2, b.fireAttributes(Math.atan2(velocity.y, velocity.x))); @@ -2939,7 +2975,7 @@ const b = { fire() { if (b.isMod3Missiles) { if (mech.crouch) { - mech.fireCDcycle = mech.cycle + 80; // cool down + mech.fireCDcycle = mech.cycle + 80 * b.modFireRate; // cool down const direction = { x: Math.cos(mech.angle), y: Math.sin(mech.angle) @@ -2955,7 +2991,7 @@ const b = { bullet[bullet.length - 1].force.y += push.y * (i - 1); } } else { - mech.fireCDcycle = mech.cycle + 60; // cool down + mech.fireCDcycle = mech.cycle + 60 * b.modFireRate; // cool down const direction = { x: Math.cos(mech.angle), y: Math.sin(mech.angle) @@ -2972,7 +3008,7 @@ const b = { } } } else { - mech.fireCDcycle = mech.cycle + Math.floor(mech.crouch ? 50 : 30); // cool down + mech.fireCDcycle = mech.cycle + Math.floor(mech.crouch ? 50 : 30) * b.modFireRate; // cool down b.missile({ x: mech.pos.x + 40 * Math.cos(mech.angle), y: mech.pos.y + 40 * Math.sin(mech.angle) - 3 @@ -3046,37 +3082,7 @@ const b = { bullet[me].explodeRad = 275; bullet[me].onEnd = function () { b.explosion(this.position, this.explodeRad); //makes bullet do explosive damage at end - if (b.modGrenadeFragments) { - const targets = [] //target nearby mobs - for (let i = 0, len = mob.length; i < len; i++) { - if (mob[i].dropPowerUp) { - const dist = Vector.magnitudeSquared(Vector.sub(this.position, mob[i].position)); - if (dist < 1440000 && //1200*1200 - Matter.Query.ray(map, this.position, mob[i].position).length === 0 && - Matter.Query.ray(body, this.position, mob[i].position).length === 0) { - targets.push(Vector.add(mob[i].position, Vector.mult(mob[i].velocity, Math.sqrt(dist) / 60))) //predict where the mob will be in a few cycles - } - } - } - for (let i = 0; i < b.modGrenadeFragments; i++) { - const speed = 53 + 10 * Math.random() - if (targets.length > 0) { // aim near a random target in array - const index = Math.floor(Math.random() * targets.length) - const SPREAD = 150 / targets.length - const WHERE = { - x: targets[index].x + SPREAD * (Math.random() - 0.5), - y: targets[index].y + SPREAD * (Math.random() - 0.5) - } - b.nail(this.position, Vector.mult(Vector.normalise(Vector.sub(WHERE, this.position)), speed), 1.1) - } else { // aim in random direction - const ANGLE = 2 * Math.PI * Math.random() - b.nail(this.position, { - x: speed * Math.cos(ANGLE), - y: speed * Math.sin(ANGLE) - }) - } - } - } + if (b.modGrenadeFragments) b.targetedNail(this.position, b.modGrenadeFragments) } bullet[me].minDmgSpeed = 1; bullet[me].onDmg = function () { @@ -3305,7 +3311,7 @@ const b = { name: "drones", description: "deploy drones that crash into mobs
collisions reduce their lifespan by 1 second", ammo: 0, - ammoPack: 13, + ammoPack: 14, have: false, isStarterGun: true, isEasyToAim: true, @@ -3476,13 +3482,7 @@ const b = { if (who.shield) { for (let i = 0, len = mob.length; i < len; i++) { if (mob[i].id === who.shieldTargetID) { //apply some knock back to shield mob before shield breaks - Matter.Body.setVelocity(mob[i], Matter.Vector.mult(Matter.Vector.normalise(this.velocity), 10)); - - // const force = Matter.Vector.mult(this.velocity, 4 / mob[i].mass) - // const velocity = Matter.Vector.add(force, mob[i].velocity) - // Matter.Body.setVelocity(mob[i], velocity); - break } } @@ -3493,35 +3493,7 @@ const b = { Matter.Body.setDensity(this, 0.001); } if (b.isModRailNails && this.speed > 10) { - const targets = [] //target nearby mobs - for (let i = 0, len = mob.length; i < len; i++) { - if (mob[i].dropPowerUp) { - const dist = Vector.magnitudeSquared(Vector.sub(this.position, mob[i].position)); - if (dist < 1000000 && //1000*1000 - Matter.Query.ray(map, this.position, mob[i].position).length === 0 && - Matter.Query.ray(body, this.position, mob[i].position).length === 0) { - targets.push(Vector.add(mob[i].position, Vector.mult(mob[i].velocity, Math.sqrt(dist) / 60))) //predict where the mob will be in a few cycles - } - } - } - for (let i = 0; i < this.speed - 10; i++) { - const speed = 50 + 10 * Math.random() - if (targets.length > 0) { // aim near a random target in array - const index = Math.floor(Math.random() * targets.length) - const SPREAD = 150 / targets.length - const WHERE = { - x: targets[index].x + SPREAD * (Math.random() - 0.5), - y: targets[index].y + SPREAD * (Math.random() - 0.5) - } - b.nail(this.position, Vector.mult(Vector.normalise(Vector.sub(WHERE, this.position)), speed), 1.1) - } else { // aim in random direction - const ANGLE = 2 * Math.PI * Math.random() - b.nail(this.position, { - x: speed * Math.cos(ANGLE), - y: speed * Math.sin(ANGLE) - }) - } - } + b.targetedNail(this.position, Math.min(40, this.speed) - 10) this.endCycle = 0 //triggers despawn } }, diff --git a/js/game.js b/js/game.js index 0b0a515..814a995 100644 --- a/js/game.js +++ b/js/game.js @@ -67,6 +67,8 @@ const game = { mech.cycle++; game.gravity(); Engine.update(engine, game.delta); + mech.keyMove(); + level.checkZones(); level.checkQuery(); mech.move(); diff --git a/js/level.js b/js/level.js index 1ff5b3d..0d88890 100644 --- a/js/level.js +++ b/js/level.js @@ -19,13 +19,14 @@ const level = { // b.giveGuns("foam") // mech.setField("time dilation field") // b.giveMod("renormalization"); - // b.giveMod("quantum tunneling"); - // b.giveGuns("rail gun") + // b.giveMod("impact shear"); + // b.giveMod("nail bot"); + // b.giveGuns("mine") // mech.setField("pilot wave") // mech.setField("perfect diamagnetism") - // level.intro(); //starting level - level.testing(); + level.intro(); //starting level + // level.testing(); // level.stronghold() // level.bosses(); // level.satellite(); @@ -157,11 +158,11 @@ const level = { // spawn.bomberBoss(2900, -500) // spawn.suckerBoss(1200, -500) // spawn.hopper(1200, -500, 70) - // spawn.hopper(1200, -500, 100) + spawn.spinner(1200, -500) // spawn.shield(mob[mob.length - 1], 1200, -500, 1); // spawn.nodeBoss(1200, -500, "spiker") - spawn.spiderBoss(1200, -500) + // spawn.spiderBoss(1200, -500) // spawn.timeSkipBoss(2900, -500) // spawn.randomMob(1600, -500) }, @@ -987,8 +988,7 @@ const level = { stiffness: 0.00007 }); if (game.difficulty > 4) spawn.nodeBoss(4250, 0, "spawns", 8, 20, 105); //chance to spawn a ring of exploding mobs around this boss - } - if (Math.random() < 0.08) { + } else if (Math.random() < 0.08) { spawn.randomLevelBoss(4250, -250); } else { //floor below right tall tower @@ -1721,7 +1721,7 @@ const level = { } } }, - stronghold() { // player made level by Francois 👑 from discord + stronghold() { // player made level by Francois 👑 from discord level.defaultZoom = 1400 game.zoomTransition(level.defaultZoom) @@ -1768,6 +1768,13 @@ const level = { height: 1700, color: "rgba(0,0,0,0.1)" }); + level.fillBG.push({ + x: 1100, + y: -1700, + width: 50, + height: 450, + color: "rgba(0,0,0,0.1)" + }); level.fillBG.push({ x: 1050, y: -1200, @@ -1792,7 +1799,7 @@ const level = { level.fillBG.push({ x: 2530, y: -550, - width: 400, + width: 430, height: -1450, color: "rgba(0,0,0,0.1)" }); @@ -1873,15 +1880,15 @@ const level = { spawn.bodyRect(3400, -1470, 110, 70); //Moyen bloc dans la cuve spawn.mapRect(3270, -1750, 80, 50); // Rebord gauche cuve - spawn.mapRect(2530, -2000, 400, 50); //First Plateforme + spawn.mapRect(2530, -2000, 430, 50); //First Plateforme spawn.mapRect(1600, -1750, 600, 50); // Middle plateforme - spawn.mapRect(1150, -1750, 250, 50); //Derniere plateforme // Toit petite boite en [ + spawn.mapRect(1100, -1750, 300, 50); //Derniere plateforme // Toit petite boite en [ spawn.bodyRect(1830, -1980, 190, 230); // Fat bloc plateforme middle spawn.bodyRect(1380, -1770, 250, 20) // Pont last plateforme spawn.mapRect(1000, -1250, 400, 50); //Sol de la petite boite en [ - spawn.mapRect(1100, -1750, 50, 380); //Mur gauche petite boite en [ - spawn.bodyRect(1100, -1380, 48, 119); //Bloc-porte petite boite en [ + spawn.mapRect(1100, -1550, 50, 190); //Mur gauche petite boite en [ + spawn.bodyRect(1100, -1380, 48, 109); //Bloc-porte petite boite en [ spawn.mapRect(-100, -750, 1100, 50); //Sol last salle spawn.mapRect(1000, -1200, 50, 500) // Mur droit last salle @@ -1897,7 +1904,9 @@ const level = { spawn.bodyRect(-503, -1250, 30, 30); // Petit bloc exit room spawn.mapRect(500, -700, 100, 590); //Bloc noir un dessous last salle - spawn.mapRect(1400, -250, 200, 250); //Black Block left from the spawn + spawn.mapRect(1350, -250, 250, 250); //Black Block left from the spawn + spawn.boost(1470, -250, 1080); + spawn.boost(-370, 0, 800); map[map.length] = Bodies.polygon(2325, -205, 0, 15); //circle above door @@ -1920,12 +1929,19 @@ const level = { spawn.bodyRect(2545, -50, 70, 50); spawn.bodyRect(2550, 0, 100, 30); + spawn.randomSmallMob(200, -1300, 0.5); + spawn.randomSmallMob(300, -1300, 0.9); + spawn.randomSmallMob(470, -650, 1); spawn.randomSmallMob(1000, -400, 1); spawn.randomSmallMob(2550, -560, 1); spawn.randomSmallMob(3350, -900, 1); spawn.randomSmallMob(3600, -1210, 1); spawn.randomSmallMob(700, -1950, 0.2); spawn.randomSmallMob(5050, -550); + spawn.randomMob(-250, -250, 0.8); + spawn.randomMob(-300, -600, 0.6); + spawn.randomMob(350, -900, 0.5); + spawn.randomMob(770, -950, 0.8) spawn.randomMob(900, -160, 1); spawn.randomMob(2360, -820, 0.8); spawn.randomMob(2700, -2020, 0.8); @@ -1935,6 +1951,7 @@ const level = { spawn.randomBoss(1500, -1900, 0.5); spawn.randomBoss(2350, -850, 1); spawn.randomBoss(100, -450, 0.9); + if (game.difficulty > 3) spawn.randomLevelBoss(1850, -1400, 1); }, //***************************************************************************************************************** diff --git a/js/mobs.js b/js/mobs.js index 6c2a3f4..4054ff3 100644 --- a/js/mobs.js +++ b/js/mobs.js @@ -996,6 +996,7 @@ const mobs = { if (mech.energy > 0.33) mech.energy -= 0.33 } if (b.isModExplodeMob) b.explosion(this.position, Math.min(450, Math.sqrt(this.mass + 3) * 80)) + if (b.modNailsDeathMob) b.targetedNail(this.position, b.modNailsDeathMob) } }, removeConsBB() { diff --git a/js/player.js b/js/player.js index 2ea2776..f885eb5 100644 --- a/js/player.js +++ b/js/player.js @@ -1461,9 +1461,16 @@ const mech = { } else if ((keys[32] || game.mouseDownRight) && mech.fieldCDcycle < mech.cycle) { //push away mech.grabPowerUp(); mech.lookForPickUp(); - const DRAIN = 0.00035 + let DRAIN = 0.00105; if (mech.energy > DRAIN) { - mech.fieldDamageResistance = 0.2; // 1 - 0.8 + if (b.isModHarmReduce) { + mech.fieldDamageResistance = 0.1; // 1 - 0.9 + DRAIN = 0.0007 //2x energy drain + } else { + mech.fieldDamageResistance = 0.2; // 1 - 0.8 + DRAIN = 0.00035 + } + // mech.pushMobs360(); //repulse mobs diff --git a/js/spawn.js b/js/spawn.js index eb6aad6..76c0e39 100644 --- a/js/spawn.js +++ b/js/spawn.js @@ -81,8 +81,7 @@ const spawn = { }, randomLevelBoss(x, y) { // suckerBoss, laserBoss, tetherBoss, snakeBoss all need a particular level to work so they are not included - const options = ["spiderBoss"] //, "timeSkipBoss" //"shooterBoss", "cellBossCulture", "bomberBoss", - // const options = ["timeSkipBoss"] + const options = ["shooterBoss", "cellBossCulture", "bomberBoss", "spiderBoss"] // , "timeSkipBoss" spawn[options[Math.floor(Math.random() * options.length)]](x, y) }, //mob templates ********************************************************************************************* @@ -420,55 +419,49 @@ const spawn = { let me = mob[mob.length - 1]; me.fill = "#28b"; me.rememberFill = me.fill; - me.cdBurst1 = 0; //must add for burstAttraction - me.cdBurst2 = 0; //must add for burstAttraction - me.delay = 0; + me.cd = 0; me.burstDir = { x: 0, y: 0 }; - me.accelMag = 0.16 * game.accelScale; me.frictionAir = 0.022; me.lookTorque = 0.0000014; me.restitution = 0; spawn.shield(me, x, y); - me.do = function () { + me.look = function () { this.seePlayerByLookingAt(); this.checkStatus(); - //accelerate towards the player after a delay - if (this.seePlayer.recall) { - if (this.cdBurst2 < game.cycle && this.angularSpeed < 0.01) { - this.cdBurst2 = Infinity; - this.cdBurst1 = game.cycle + 40; - this.burstDir = Vector.normalise(Vector.sub(this.seePlayer.position, this.position)); - } else if (this.cdBurst1 < game.cycle) { - this.cdBurst2 = game.cycle + this.delay; - this.cdBurst1 = Infinity; - this.force = Vector.mult(this.burstDir, this.mass * 0.25); - this.fill = this.rememberFill; - } else if (this.cdBurst1 != Infinity) { - this.torque += 0.000035 * this.inertia; - this.fill = randomColor({ - hue: "blue" - }); - //draw attack vector - const mag = this.radius * 2.5 + 50; - ctx.strokeStyle = "rgba(0,0,0,0.2)"; - ctx.lineWidth = 3; - ctx.setLineDash([10, 20]); //30 - const dir = Vector.add(this.position, Vector.mult(this.burstDir, mag)); - ctx.beginPath(); - ctx.moveTo(this.position.x, this.position.y); - ctx.lineTo(dir.x, dir.y); - ctx.stroke(); - ctx.setLineDash([]); - } else { - this.fill = this.rememberFill; - } - } else { - this.cdBurst2 = 0; + if (this.seePlayer.recall && this.cd < game.cycle) { + this.burstDir = Vector.normalise(Vector.sub(this.seePlayer.position, this.position)); + this.cd = game.cycle + 40; + this.do = this.spin } - }; + } + me.do = me.look + me.spin = function () { + this.checkStatus(); + this.torque += 0.000035 * this.inertia; + this.fill = randomColor({ + hue: "blue" + }); + //draw attack vector + const mag = this.radius * 2.5 + 50; + ctx.strokeStyle = "rgba(0,0,0,0.2)"; + ctx.lineWidth = 3; + ctx.setLineDash([10, 20]); //30 + const dir = Vector.add(this.position, Vector.mult(this.burstDir, mag)); + ctx.beginPath(); + ctx.moveTo(this.position.x, this.position.y); + ctx.lineTo(dir.x, dir.y); + ctx.stroke(); + ctx.setLineDash([]); + if (this.cd < game.cycle) { + this.fill = this.rememberFill; + this.cd = game.cycle + 180 * game.CDScale + this.do = this.look + this.force = Vector.mult(this.burstDir, this.mass * 0.25); + } + } }, sucker(x, y, radius = 30 + Math.ceil(Math.random() * 70)) { radius = 9 + radius / 8; //extra small @@ -714,18 +707,18 @@ const spawn = { }); } }, - timeSkipBoss(x, y, radius = 70) { + timeSkipBoss(x, y, radius = 55) { mobs.spawn(x, y, 6, radius, '#000'); let me = mob[mob.length - 1]; // me.stroke = "transparent"; //used for drawSneaker me.timeSkipLastCycle = 0 - me.eventHorizon = 1600; //required for black hole + me.eventHorizon = 1800; //required for black hole me.seeAtDistance2 = (me.eventHorizon + 2000) * (me.eventHorizon + 2000); //vision limit is event horizon + 2000 me.accelMag = 0.0004 * game.accelScale; // me.frictionAir = 0.005; // me.memory = 1600; // Matter.Body.setDensity(me, 0.02); //extra dense //normal is 0.001 //makes effective life much larger - Matter.Body.setDensity(me, 0.0025 + 0.0007 * 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); diff --git a/todo.txt b/todo.txt index be95822..9eeb0d2 100644 --- a/todo.txt +++ b/todo.txt @@ -1,16 +1,24 @@ - -large mods shrink on death instead of disappearing -new boss mob: spiderBoss -mod - Bayesian interference -> determinism - spawn 4 mods and 2 heals, future power ups are limited to one choice -mod - Leveraged investments -> Bayesian interference - 33% chance for double power ups to drop, remove all future ammo power ups - -(serious progress on my level construction tools, but it's not ready yet) +mod - negative mass field: 80%->90% harm reduction while active, but 2x energy drain +mod - mobs fire nails when they die (by Francois 👑 from discord) ************** TODO - n-gon ************** +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. + +have a mob apply a positive status effect on other mobs, + heal? + make it yellow + 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 @@ -18,25 +26,6 @@ construct display current output text in a box live update it - -mod - blocking with perfect diamagnetism fires your gun - maybe doesn't trigger cooldown? - does use ammo - -mod - annihilation might be unbalanced? - -boss mob - let it die multiple times and come back to life - on death event spawns a new version of self, but with a decrementing counter - -foam - check for touching map / blocks and slow foam down rather then bounce off walls -quantum foam should just skip the shield - -red flashes when you take damage were replaced with the color of your energy bar - When you have mass energy equivalence - -mod - missiles: fire 3 small missiles - disables missile replication mod - lore - a robot (the player) gains self awareness each mod/gun/field is a new tech all the technology leads to the singularity @@ -57,9 +46,12 @@ atmosphere levels: change the pace, give the user a rest between combat simple puzzles cool looking stuff large rotating fan that the player has to move through - nonaggressive mobs in the final level you see your self at the starting level, with the wires you shoot your self to wake up? + nonaggressive mobs + +boss mob - let it die multiple times and come back to life + on death event spawns a new version of self, but with a decrementing counter bullets cost 5 life instead of ammo, but return 5 life when they hit a mob replace life with energy or ammo?