From 498f867738badf536bf455935b3934efc72f96fa Mon Sep 17 00:00:00 2001 From: landgreen Date: Mon, 13 Dec 2021 19:11:46 -0800 Subject: [PATCH] mantisBoss mantisBoss: 3 punching mobs, eject your ammo when you get punched invulnerable except for 1 second after moving powerUpBossBaby: smaller, cuter, faster, can't move through walls slashBoss renamed revolutionBoss invulnerable every other revolution of it's blades new mob state: invulnerable - immune to damage and status effects (stun, dots, freeze) damage and harm circle graphic size have been made more consistent bug fixes --- .DS_Store | Bin 6148 -> 6148 bytes js/bullet.js | 22 +-- js/engine.js | 8 +- js/index.js | 48 +++-- js/level.js | 33 ++-- js/mob.js | 31 +++- js/player.js | 1 - js/powerup.js | 38 +++- js/simulation.js | 1 + js/spawn.js | 443 ++++++++++++++++++++++++++++++++++++++++++----- js/tech.js | 12 +- style.css | 2 +- todo.txt | 41 ++++- 13 files changed, 555 insertions(+), 125 deletions(-) diff --git a/.DS_Store b/.DS_Store index 38c8f0f308521b3e8b0d71d1ecbaa661ae7ce8a6..b231e2cbff2e35e373b0da454bafd94ee7ff3f30 100644 GIT binary patch delta 23 ecmZoMXffEJ$;>Qkr8ZfI*_TOn!{%z{P7wf1`37+S delta 23 ecmZoMXffEJ$;>SCrfRYdvoDjJ!{%z{P7wfDQ3nSA diff --git a/js/bullet.js b/js/bullet.js index 409e23b..f863958 100644 --- a/js/bullet.js +++ b/js/bullet.js @@ -1932,7 +1932,7 @@ const b = { simulation.drawList.push({ x: path[1].x, y: path[1].y, - radius: Math.sqrt(dmg) * 50, + radius: 600 * dmg * best.who.damageReduction, color: "rgba(255,0,255,0.2)", time: simulation.drawTime * 4 }); @@ -2058,7 +2058,8 @@ const b = { simulation.drawList.push({ //add dmg to draw queue x: path[path.length - 1].x, y: path[path.length - 1].y, - radius: Math.sqrt(damage) * 100, + // radius: Math.sqrt(damage) * 100 * mob[k].damageReduction, + radius: 600 * damage * best.who.damageReduction, color: tech.laserColorAlpha, time: simulation.drawTime }); @@ -2705,7 +2706,7 @@ const b = { } }, onEnd() { - if (tech.isDroneRespawn) { + if (tech.isDroneRespawn && b.inventory.length) { const who = b.guns[b.activeGun] if (who.name === "drones" && who.ammo > 0 && mob.length) { b.drone({ x: this.position.x, y: this.position.y }, 0) @@ -3178,7 +3179,7 @@ const b = { for (let i = 0; i < len; i++) { if (targets.length - i > 0) { const index = Math.floor(Math.random() * targets.length) - const speed = 10 + 10 * Math.random() + const speed = 6 + 6 * Math.random() const velocity = Vector.mult(Vector.normalise(Vector.sub(targets[index].position, this.position)), speed) b.foam(this.position, Vector.rotate(velocity, 0.5 * (Math.random() - 0.5)), radius) } else { @@ -3342,7 +3343,7 @@ const b = { simulation.drawList.push({ //add dmg to draw queue x: this.position.x, y: this.position.y, - radius: Math.log(2 * dmg + 1.1) * 40, + radius: Math.log(2 * dmg + 1.1) * 40 * who.damageReduction, color: simulation.playerDmgColor, time: simulation.drawTime }); @@ -3395,7 +3396,7 @@ const b = { simulation.drawList.push({ //add dmg to draw queue x: this.position.x, y: this.position.y, - radius: Math.log(2 * dmg + 1.1) * 40, + radius: Math.log(2 * dmg + 1.1) * 40 * who.damageReduction, color: simulation.playerDmgColor, time: simulation.drawTime }); @@ -3651,7 +3652,7 @@ const b = { simulation.drawList.push({ //add dmg to draw queue x: this.position.x, y: this.position.y, - radius: Math.log(2 * dmg + 1.1) * 40, + radius: 600 * dmg * q[i].damageReduction, color: 'rgba(0,0,0,0.4)', time: simulation.drawTime }); @@ -4204,7 +4205,8 @@ const b = { simulation.drawList.push({ x: path[1].x, y: path[1].y, - radius: Math.sqrt(dmg) * 50, + // radius: Math.sqrt(dmg) * 50 * mob[k].damageReduction, + radius: 600 * dmg * best.who.damageReduction, color: "rgba(255,0,255,0.2)", time: simulation.drawTime * 4 }); @@ -4297,7 +4299,7 @@ const b = { simulation.drawList.push({ //add dmg to draw queue x: this.position.x, y: this.position.y, - radius: Math.log(2 * dmg + 1.1) * 40, + radius: 600 * dmg * q[i].damageReduction, color: 'rgba(0,0,0,0.4)', time: simulation.drawTime }); @@ -5211,7 +5213,7 @@ const b = { simulation.drawList.push({ //add dmg to draw queue x: this.position.x, y: this.position.y, - radius: Math.log(2 * dmg + 1.1) * 40, + radius: Math.log(2 * dmg + 1.1) * 40 * q[i].damageReduction, color: 'rgba(0,0,0,0.4)', time: simulation.drawTime }); diff --git a/js/engine.js b/js/engine.js index 8d55fe2..e84772d 100644 --- a/js/engine.js +++ b/js/engine.js @@ -155,7 +155,7 @@ function collisionChecks(event) { simulation.drawList.push({ //add dmg to draw queue x: pairs[i].activeContacts[0].vertex.x, y: pairs[i].activeContacts[0].vertex.y, - radius: dmg * 2000, + radius: Math.sqrt(dmg) * 500, color: "rgba(255,0,255,0.2)", time: simulation.drawTime }); @@ -163,7 +163,7 @@ function collisionChecks(event) { simulation.drawList.push({ //add dmg to draw queue x: pairs[i].activeContacts[0].vertex.x, y: pairs[i].activeContacts[0].vertex.y, - radius: dmg * 500, + radius: Math.sqrt(dmg) * 200, color: simulation.mobDmgColor, time: simulation.drawTime }); @@ -182,7 +182,7 @@ function collisionChecks(event) { simulation.drawList.push({ //add dmg to draw queue x: pairs[i].activeContacts[0].vertex.x, y: pairs[i].activeContacts[0].vertex.y, - radius: Math.log(2 * dmg + 1.1) * 40, + radius: Math.log(2 * dmg + 1.1) * 40 * mob[k].damageReduction, color: simulation.playerDmgColor, time: simulation.drawTime }); @@ -217,7 +217,7 @@ function collisionChecks(event) { simulation.drawList.push({ x: pairs[i].activeContacts[0].vertex.x, y: pairs[i].activeContacts[0].vertex.y, - radius: Math.log(2 * dmg + 1.1) * 40, + radius: Math.log(2 * dmg + 1.1) * 40 * mob[k].damageReduction, color: simulation.playerDmgColor, time: simulation.drawTime }); diff --git a/js/index.js b/js/index.js index bfe77c6..0e68cca 100644 --- a/js/index.js +++ b/js/index.js @@ -215,32 +215,30 @@ const build = { if (tech.missileBotCount) botText += `
missile-bots: ${tech.missileBotCount}` const harm = (1 - m.harmReduction()) * 100 - let text = "" - if (!simulation.isChoosing) text += `
- PAUSED               press P to resume
` - text += `
- ${simulation.isCheating? "lore disabled

": ""} - damage increase: ${((tech.damageFromTech()-1)*100).toFixed(0)}% -
harm reduction: ${harm.toFixed(harm > 90 ? 2 : 0)}% -
fire delay decrease: ${((1-b.fireCDscale)*100).toFixed(b.fireCDscale < 0.1 ? 2 : 0)}% -
duplication chance: ${(tech.duplicationChance()*100).toFixed(0)}% - ${botText} -
-
tech: ${tech.totalCount}   research: ${powerUps.research.count} -
health: (${(m.health*100).toFixed(0)} / ${(m.maxHealth*100).toFixed(0)})   energy: (${(m.energy*100).toFixed(0)} / ${(m.maxEnergy*100).toFixed(0)}) -
position: (${player.position.x.toFixed(1)}, ${player.position.y.toFixed(1)})   velocity: (${player.velocity.x.toFixed(1)}, ${player.velocity.y.toFixed(1)}) -
mouse: (${simulation.mouseInGame.x.toFixed(1)}, ${simulation.mouseInGame.y.toFixed(1)})   mass: ${player.mass.toFixed(1)} -
-
level: ${level.levels[level.onLevel]} (${level.difficultyText()})   ${m.cycle} cycles -
${mob.length} mobs,   ${body.length} blocks,   ${bullet.length} bullets,   ${powerUp.length} power ups + let text = `
` + if (!simulation.isChoosing) text += `
PAUSED               press P to resume +

+ copy build url +
` + text += `damage increase: ${((tech.damageFromTech()-1)*100).toFixed(0)}% +
harm reduction: ${harm.toFixed(harm > 90 ? 2 : 0)}% +
fire delay decrease: ${((1-b.fireCDscale)*100).toFixed(b.fireCDscale < 0.1 ? 2 : 0)}% +
duplication chance: ${(tech.duplicationChance()*100).toFixed(0)}% +${botText} +
+
tech: ${tech.totalCount}   research: ${powerUps.research.count} +
health: (${(m.health*100).toFixed(0)} / ${(m.maxHealth*100).toFixed(0)})   energy: (${(m.energy*100).toFixed(0)} / ${(m.maxEnergy*100).toFixed(0)}) +
gun: ${b.activeGun !== null ? b.guns[b.activeGun].name: "null"}   ammo: ${b.activeGun !== null ? b.guns[b.activeGun].ammo: "0"} +
position: (${player.position.x.toFixed(1)}, ${player.position.y.toFixed(1)})   velocity: (${player.velocity.x.toFixed(1)}, ${player.velocity.y.toFixed(1)}) +
mouse: (${simulation.mouseInGame.x.toFixed(1)}, ${simulation.mouseInGame.y.toFixed(1)})   mass: ${player.mass.toFixed(1)} +
+
level: ${level.levels[level.onLevel]} (${level.difficultyText()})   ${m.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: ${(simulation.dmgScale*100).toFixed(0)}% -
heal difficulty scale: ${(simulation.healScale*100).toFixed(1)}% -
- - copy build url - +
damage difficulty scale: ${(b.dmgScale*100).toFixed(2) }% +
harm difficulty scale: ${(simulation.dmgScale*100).toFixed(0)}% +
heal difficulty scale: ${(simulation.healScale*100).toFixed(1)}% +${simulation.isCheating ? "

lore disabled": ""}
`; for (let i = 0, len = b.inventory.length; i < len; i++) { text += `
  ${build.nameLink(b.guns[b.inventory[i]].name)} - ${b.guns[b.inventory[i]].ammo}
${b.guns[b.inventory[i]].description}
` diff --git a/js/level.js b/js/level.js index 9c3b8bc..1d8ccb7 100644 --- a/js/level.js +++ b/js/level.js @@ -16,8 +16,8 @@ const level = { // localSettings.levelsClearedLastGame = 10 // level.difficultyIncrease(30) //30 is near max on hard //60 is near max on why // simulation.isHorizontalFlipped = true - // m.setField("wormhole") - // b.giveGuns("shotgun") + // m.setField("plasma torch") + // b.giveGuns("drones") // b.giveGuns("nail gun") // b.giveGuns("harpoon") // tech.giveTech("needle gun") @@ -2337,13 +2337,15 @@ const level = { spawn.mapRect(4850, -275, 50, 175); //??? - // level.difficultyIncrease(30) //30 is near max on hard //60 is near max on why + level.difficultyIncrease(1) //30 is near max on hard //60 is near max on why + m.addHealth(Infinity) + // spawn.starter(1900, -500, 200) //big boy - // spawn.launcherOne(1700, -500) // spawn.launcherBoss(3200, -500) // spawn.laserTargetingBoss(1700, -500) // spawn.powerUpBoss(3200, -500) + // spawn.powerUpBossBaby(3200, -500) // spawn.snakeBoss(1700, -500) // spawn.streamBoss(3200, -500) // spawn.pulsarBoss(1700, -500) @@ -2355,28 +2357,19 @@ const level = { // spawn.laserBombingBoss(1700, -500) // spawn.launcherBoss(3200, -500) // spawn.blockBoss(1700, -500) - // spawn.slashBoss(3200, -500) - // spawn.spiderBoss(3200, -500) + // spawn.blinkBoss(3200, -500) + // spawn.mantisBoss(1700, -500) // spawn.tetherBoss(1700, -500) //go to actual level? + spawn.revolutionBoss(1900, -500) + // spawn.bomberBoss(1400, -500) + // spawn.cellBossCulture(1600, -500) + // spawn.launcherOne(1700, -500) // for (let i = 0; i < 10; ++i) spawn.bodyRect(1600 + 5, -500, 30, 40); // for (let i = 0; i < 5; i++) spawn.focuser(1900, -500) - // spawn.slashBoss(1900, -500) - spawn.pulsar(1900, -500) + // spawn.pulsar(1900, -500) // spawn.shield(mob[mob.length - 1], 1900, -500, 1); // mob[mob.length - 1].isShielded = true - // spawn.growBossCulture(1200, -500) - // spawn.laserTargetingBoss(1600, -400) - // spawn.blinkBoss(1600, -500) - // spawn.laserTargetingBoss(1700, -120) - // spawn.bomberBoss(1400, -500) - // spawn.laser(1800, -320) - // spawn.laserBombingBoss(1600, -500) - // spawn.laserTargetingBoss(1600, -500) - // spawn.laserBoss(1600, -500) - // spawn.cellBossCulture(1600, -500) - // spawn.nodeGroup(1200, -500, "grenadier") - // spawn.nodeGroup(1800, -500, "grenadier") // spawn.nodeGroup(1200, 0, "grenadier") // spawn.blinkBoss(1200, -500) // spawn.suckerBoss(2900, -500) diff --git a/js/mob.js b/js/mob.js index f0f9a6a..a17c0dc 100644 --- a/js/mob.js +++ b/js/mob.js @@ -59,7 +59,7 @@ const mobs = { } function applySlow(whom) { - if (!whom.shield && !whom.isShielded && !m.isBodiesAsleep) { + if (!whom.shield && !whom.isShielded && !m.isBodiesAsleep && who.damageReduction > 0) { if (whom.isBoss) cycles = Math.floor(cycles * 0.25) let i = whom.status.length while (i--) { @@ -98,7 +98,7 @@ const mobs = { } }, statusStun(who, cycles = 180) { - if (!who.shield && !who.isShielded && !m.isBodiesAsleep) { + if (!who.shield && !who.isShielded && !m.isBodiesAsleep && who.damageReduction > 0) { Matter.Body.setVelocity(who, { x: who.velocity.x * 0.8, y: who.velocity.y * 0.8 @@ -150,7 +150,7 @@ const mobs = { } }, statusDoT(who, tickDamage, cycles = 180) { - if (!who.isShielded && who.alive) { + if (!who.isShielded && who.alive && who.damageReduction > 0) { who.status.push({ effect() { if ((simulation.cycle - this.startCycle) % 30 === 0 && !m.isBodiesAsleep) { @@ -579,7 +579,7 @@ const mobs = { springAttack() { // set new values of the ends of the spring constraints const stepRange = 600 - if (this.seePlayer.recall && Matter.Query.ray(map, this.position, player.position).length === 0) { + if (this.seePlayer.recall && Matter.Query.ray(map, this.position, this.seePlayer.position).length === 0) { if (!(simulation.cycle % (this.seePlayerFreq * 2))) { const unit = Vector.normalise(Vector.sub(this.seePlayer.position, this.position)) const goal = Vector.add(this.position, Vector.mult(unit, stepRange)) @@ -650,7 +650,7 @@ const mobs = { } }; //move to a random location - if (!(simulation.cycle % (this.seePlayerFreq * 5))) { + if (!(simulation.cycle % (this.seePlayerFreq * 4))) { best = { x: null, y: null, @@ -797,6 +797,27 @@ const mobs = { } } }, + invulnerability() { + if (this.isInvulnerable) { + if (this.invulnerabilityCountDown > 0) { + this.invulnerabilityCountDown-- + //graphics //draw a super shield? + ctx.beginPath(); + let vertices = this.vertices; + ctx.moveTo(vertices[0].x, vertices[0].y); + for (let j = 1; j < vertices.length; j++) ctx.lineTo(vertices[j].x, vertices[j].y); + ctx.lineTo(vertices[0].x, vertices[0].y); + ctx.lineWidth = 20; + // ctx.fillStyle = `rgba(${Math.floor(255 * Math.random())},${Math.floor(255 * Math.random())},${Math.floor(255 * Math.random())},0.5)` + // ctx.fill(); + ctx.strokeStyle = "rgba(255,255,255,0.4)"; + ctx.stroke(); + } else { + this.isInvulnerable = false + this.damageReduction = this.startingDamageReduction + } + } + }, grow() { if (!m.isBodiesAsleep) { if (this.seePlayer.recall) { diff --git a/js/player.js b/js/player.js index 374ef67..d94ef01 100644 --- a/js/player.js +++ b/js/player.js @@ -327,7 +327,6 @@ const m = { tech.tech[i].name !== "Ψ(t) collapse" && tech.tech[i].name !== "non-unitary operator" && tech.tech[i].name !== "-quantum leap-" - ) { totalTech += tech.tech[i].count tech.tech[i].remove(); diff --git a/js/powerup.js b/js/powerup.js index 55ce58f..a4113cf 100644 --- a/js/powerup.js +++ b/js/powerup.js @@ -1,6 +1,36 @@ let powerUp = []; const powerUps = { + ejectGraphic(color = "68, 102, 119") { + simulation.drawList.push({ + x: m.pos.x, + y: m.pos.y, + radius: 100, + color: `rgba(${color}, 0.8)`, + time: 4 + }); + simulation.drawList.push({ + x: m.pos.x, + y: m.pos.y, + radius: 75, + color: `rgba(${color}, 0.6)`, + time: 8 + }); + simulation.drawList.push({ + x: m.pos.x, + y: m.pos.y, + radius: 50, + color: `rgba(${color}, 0.3)`, + time: 12 + }); + simulation.drawList.push({ + x: m.pos.x, + y: m.pos.y, + radius: 25, + color: `rgba(${color}, 0.15)`, + time: 16 + }); + }, orb: { research(num = 1) { switch (num) { @@ -963,8 +993,11 @@ const powerUps = { tech.tech[choose].isLost = true; simulation.updateTechHUD(); m.fieldCDcycle = m.cycle + 30; //disable field so you can't pick up the ejected tech + return true + } else { + return false } - } else { + } else if (tech.tech[choose].count) { // simulation.makeTextLog(`
  ${tech.tech[choose].name} was ejected`, 600) //message about what tech was lost simulation.makeTextLog(`tech.remove("${tech.tech[choose].name}")`) @@ -978,6 +1011,9 @@ const powerUps = { tech.tech[choose].isLost = true; simulation.updateTechHUD(); m.fieldCDcycle = m.cycle + 30; //disable field so you can't pick up the ejected tech + return true + } else { + return false } }, // removeRandomTech() { diff --git a/js/simulation.js b/js/simulation.js index 809c09f..9245e9c 100644 --- a/js/simulation.js +++ b/js/simulation.js @@ -397,6 +397,7 @@ const simulation = { } } if (tech.isCrouchAmmo) tech.isCrouchAmmo = 1 //this prevents hacking the tech by switching guns + b.activeGun = b.inventory[b.inventoryGun]; if (b.guns[b.activeGun].charge) b.guns[b.activeGun].charge = 0; //if switching into foam set charge to 0 simulation.updateGunHUD(); diff --git a/js/spawn.js b/js/spawn.js index b82c0f9..53a8564 100644 --- a/js/spawn.js +++ b/js/spawn.js @@ -1,9 +1,12 @@ //main object for spawning things in a level const spawn = { - nonCollideBossList: ["snakeBoss"], //"cellBossCulture", "bomberBoss", "powerUpBoss", "orbitalBoss", "spawnerBossCulture", "growBossCulture" + nonCollideBossList: ["cellBossCulture", "bomberBoss", "powerUpBoss", "orbitalBoss", "spawnerBossCulture", "growBossCulture"], // other bosses: suckerBoss, laserBoss, tetherBoss, //these need a particular level to work so they are not included in the random pool - // "shieldingBoss", "orbitalBoss", "historyBoss", "shooterBoss", "cellBossCulture", "bomberBoss", "spiderBoss", "launcherBoss", "laserTargetingBoss",, , "streamBoss", "pulsarBoss", "spawnerBossCulture", "grenadierBoss", "growBossCulture", "blinkBoss", "snakeSpitBoss", "laserBombingBoss", , "slashBoss" - randomLevelBoss(x, y, options = ["snakeBoss", "powerUpBoss", "blockBoss"]) { + randomLevelBoss(x, y, options = [ + "shieldingBoss", "orbitalBoss", "historyBoss", "shooterBoss", "cellBossCulture", "bomberBoss", "spiderBoss", "launcherBoss", "laserTargetingBoss", + "powerUpBoss", "powerUpBossBaby", "snakeBoss", "streamBoss", "pulsarBoss", "spawnerBossCulture", "grenadierBoss", "growBossCulture", "blinkBoss", + "snakeSpitBoss", "laserBombingBoss", "blockBoss", "revolutionBoss", "mantisBoss" + ]) { spawn[options[Math.floor(Math.random() * options.length)]](x, y) }, pickList: ["starter", "starter"], @@ -1165,6 +1168,64 @@ const spawn = { // } } }, + powerUpBossBaby(x, y, vertices = 9, radius = 70) { + mobs.spawn(x, y, vertices, radius, "transparent"); + let me = mob[mob.length - 1]; + me.isBoss = true; + me.frictionAir = 0.006 + me.seeAtDistance2 = 1000000; + me.accelMag = 0.0007 * simulation.accelScale; + Matter.Body.setDensity(me, 0.0008); //normal is 0.001 + me.collisionFilter.mask = cat.bullet | cat.player | cat.body | cat.map + me.memory = Infinity; + me.seePlayerFreq = 30 + me.lockedOn = null; + if (vertices === 9) { + //on primary spawn + powerUps.spawnBossPowerUp(me.position.x, me.position.y) + powerUps.spawn(me.position.x, me.position.y, "heal"); + powerUps.spawn(me.position.x, me.position.y, "ammo"); + } else if (!m.isCloak) { + me.foundPlayer(); + } + me.onHit = function() { //run this function on hitting player + if (powerUps.ejectTech()) { + powerUps.ejectGraphic("150, 138, 255"); + powerUps.spawn(m.pos.x + 60 * (Math.random() - 0.5), m.pos.y + 60 * (Math.random() - 0.5), "ammo"); + powerUps.spawn(m.pos.x + 60 * (Math.random() - 0.5), m.pos.y + 60 * (Math.random() - 0.5), "research"); + } + }; + me.onDeath = function() { + this.leaveBody = false; + if (vertices > 3) { + this.isDropPowerUp = false; + spawn.powerUpBossBaby(this.position.x, this.position.y, vertices - 1) + Matter.Body.setVelocity(mob[mob.length - 1], { + x: this.velocity.x, + y: this.velocity.y + }) + } + for (let i = 0; i < powerUp.length; i++) powerUp[i].collisionFilter.mask = cat.map | cat.powerUp + }; + me.damageReduction = 0.15 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1) + me.do = function() { + // this.armor(); + this.stroke = `hsl(0,0%,${80 + 25 * Math.sin(simulation.cycle * 0.01)}%)` + + //steal all power ups + for (let i = 0; i < Math.min(powerUp.length, this.vertices.length); i++) { + powerUp[i].collisionFilter.mask = 0 + Matter.Body.setPosition(powerUp[i], this.vertices[i]) + Matter.Body.setVelocity(powerUp[i], { + x: 0, + y: 0 + }) + } + this.seePlayerByHistory(); + this.attraction(); + this.checkStatus(); + }; + }, powerUpBoss(x, y, vertices = 9, radius = 130) { mobs.spawn(x, y, vertices, radius, "transparent"); let me = mob[mob.length - 1]; @@ -1186,9 +1247,11 @@ const spawn = { me.foundPlayer(); } me.onHit = function() { //run this function on hitting player - powerUps.ejectTech() - powerUps.spawn(m.pos.x + 60 * (Math.random() - 0.5), m.pos.y + 60 * (Math.random() - 0.5), "ammo"); - powerUps.spawn(m.pos.x + 60 * (Math.random() - 0.5), m.pos.y + 60 * (Math.random() - 0.5), "research"); + if (powerUps.ejectTech()) { + powerUps.ejectGraphic("150, 138, 255"); + powerUps.spawn(m.pos.x + 60 * (Math.random() - 0.5), m.pos.y + 60 * (Math.random() - 0.5), "ammo"); + powerUps.spawn(m.pos.x + 60 * (Math.random() - 0.5), m.pos.y + 60 * (Math.random() - 0.5), "research"); + } }; me.onDeath = function() { this.leaveBody = false; @@ -1222,6 +1285,7 @@ const spawn = { this.checkStatus(); }; }, + // chaser(x, y, radius = 35 + Math.ceil(Math.random() * 40)) { // mobs.spawn(x, y, 8, radius, "rgb(255,150,100)"); //"#2c9790" // let me = mob[mob.length - 1]; @@ -1784,6 +1848,277 @@ const spawn = { spawn.groupShield(targets, x, y, sideLength + 1 * radius + nodes * 5 - 25); spawn.allowShields = true; }, + mantisBoss(x, y, radius = 35) { + mobs.spawn(x, y, 5, radius, "#6ba"); + let me = mob[mob.length - 1]; + me.babyList = [] //list of mobs that are apart of this boss + Matter.Body.setDensity(me, 0.002); //extra dense //normal is 0.001 //makes effective life much larger and damage on collision + me.damageReduction = 0.1 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1) //normal is 1, most bosses have 0.25 + me.isBoss = true; + me.friction = 0; + me.frictionAir = 0.0067; + me.g = 0.0002; //required if using this.gravity + me.seePlayerFreq = 300; + const springStiffness = 0.0004; //simulation.difficulty + const springDampening = 0.005; + + me.springTarget = { + x: me.position.x, + y: me.position.y + }; + const len = cons.length; + cons[len] = Constraint.create({ + pointA: me.springTarget, + bodyB: me, + stiffness: springStiffness, + damping: springDampening + }); + Composite.add(engine.world, cons[cons.length - 1]); + cons[len].length = 100 + 1.5 * radius; + me.cons = cons[len]; + me.springTarget2 = { x: me.position.x, y: me.position.y }; + const len2 = cons.length; + cons[len2] = Constraint.create({ + pointA: me.springTarget2, + bodyB: me, + stiffness: springStiffness, + damping: springDampening, + length: 0 + }); + Composite.add(engine.world, cons[cons.length - 1]); + cons[len2].length = 100 + 1.5 * radius; + me.cons2 = cons[len2]; + + me.startingDamageReduction = me.damageReduction + me.isInvulnerable = false + me.invulnerabilityCountDown = 0 + + me.do = function() { + this.checkStatus(); + this.gravity(); + //draw the two dots on the end of the springs + ctx.beginPath(); + ctx.arc(this.cons.pointA.x, this.cons.pointA.y, 6, 0, 2 * Math.PI); + ctx.arc(this.cons2.pointA.x, this.cons2.pointA.y, 6, 0, 2 * Math.PI); + ctx.fillStyle = "#222"; + ctx.fill(); + + this.seePlayerCheck() + // this.seePlayerByHistory() + if (this.isInvulnerable) { + ctx.beginPath(); + let vertices = this.vertices; + ctx.moveTo(vertices[0].x, vertices[0].y); + for (let j = 1; j < vertices.length; j++) ctx.lineTo(vertices[j].x, vertices[j].y); + ctx.lineTo(vertices[0].x, vertices[0].y); + for (let i = 0; i < this.babyList.length; i++) { + if (this.babyList[i].alive) { + let vertices = this.babyList[i].vertices; + ctx.moveTo(vertices[0].x, vertices[0].y); + for (let j = 1; j < vertices.length; j++) ctx.lineTo(vertices[j].x, vertices[j].y); + ctx.lineTo(vertices[0].x, vertices[0].y); + } + } + ctx.lineWidth = 20; + ctx.strokeStyle = "rgba(255,255,255,0.7)"; + ctx.stroke(); + } else if (this.invulnerabilityCountDown > 0) { + this.invulnerabilityCountDown-- + } else { + this.isInvulnerable = true + this.startingDamageReduction = this.damageReduction + this.damageReduction = 0 + for (let i = 0; i < this.babyList.length; i++) { + if (this.babyList[i].alive) { + this.babyList[i].isInvulnerable = true + this.babyList[i].damageReduction = 0 + } + } + } + // set new values of the ends of the spring constraints + const stepRange = 1200 + if (this.seePlayer.recall && Matter.Query.ray(map, this.position, this.seePlayer.position).length === 0) { + if (!(simulation.cycle % (this.seePlayerFreq * 2))) { + const unit = Vector.normalise(Vector.sub(this.seePlayer.position, this.position)) + const goal = Vector.add(this.position, Vector.mult(unit, stepRange)) + this.springTarget.x = goal.x; + this.springTarget.y = goal.y; + this.cons.length = -200; + this.cons2.length = 100 + 1.5 * this.radius; + + this.isInvulnerable = false + this.invulnerabilityCountDown = 40 + Math.max(0, 70 - simulation.difficulty * 2) + this.damageReduction = this.startingDamageReduction + for (let i = 0; i < this.babyList.length; i++) { + if (this.babyList[i].alive) this.babyList[i].damageReduction = this.startingDamageReduction + } + } else if (!(simulation.cycle % this.seePlayerFreq)) { + const unit = Vector.normalise(Vector.sub(this.seePlayer.position, this.position)) + const goal = Vector.add(this.position, Vector.mult(unit, stepRange)) + this.springTarget2.x = goal.x; + this.springTarget2.y = goal.y; + this.cons.length = 100 + 1.5 * this.radius; + this.cons2.length = -200; + + this.isInvulnerable = false + this.invulnerabilityCountDown = 40 + Math.max(0, 70 - simulation.difficulty * 2) + this.damageReduction = this.startingDamageReduction + for (let i = 0; i < this.babyList.length; i++) { + if (this.babyList[i].alive) this.babyList[i].damageReduction = this.startingDamageReduction + } + } + } else { + this.torque = this.lookTorque * this.inertia; + //spring to random place on map + 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 = simulation.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 = simulation.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) { + best = { + x: results.x, + y: results.y, + dist2: dist2, + who: domain[i], + v1: vertices[0], + v2: vertices[len] + }; + } + } + } + }; + //move to a random location + if (!(simulation.cycle % (this.seePlayerFreq))) { + best = { + x: null, + y: null, + dist2: Infinity, + who: null, + v1: null, + v2: null + }; + const seeRange = 3000; + const look = { + x: this.position.x + seeRange * Math.cos(this.angle), + y: this.position.y + seeRange * Math.sin(this.angle) + }; + vertexCollision(this.position, look, map); + if (best.dist2 != Infinity) { + this.springTarget.x = best.x; + this.springTarget.y = best.y; + this.cons.length = 100 + 1.5 * this.radius; + this.cons2.length = 100 + 1.5 * this.radius; + } + } + } + }; + + me.onDeath = function() { + this.removeCons(); + powerUps.spawnBossPowerUp(this.position.x, this.position.y) + for (let i = 0; i < this.babyList.length; i++) { + if (this.babyList[i].alive) { + this.babyList[i].isInvulnerable = false + this.babyList[i].damageReduction = this.startingDamageReduction + this.babyList[i].collisionFilter.mask = cat.bullet | cat.player | cat.map | cat.body + } + } + }; + + const sideLength = 120 // distance between each node mob + const nodes = 3 + const angle = 2 * Math.PI / nodes + spawn.allowShields = false; //don't want shields on individual mobs, it messes with the constraints + for (let i = 0; i < nodes; ++i) { + spawn.striker(x + sideLength * Math.sin(i * angle), y + sideLength * Math.cos(i * angle), 20, 12); + const babyMob = mob[mob.length - 1] + me.babyList.push(babyMob) + babyMob.fill = "rgb(68, 102, 119)" + babyMob.isBoss = true; + // Matter.Body.setDensity(babyMob, 0.001); //extra dense //normal is 0.001 //makes effective life much larger and increases damage + babyMob.damageReduction = 0.1 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1) + babyMob.collisionFilter.mask = cat.bullet | cat.player //can't touch other mobs //cat.map | cat.body | + babyMob.delay = 60 + 50 * simulation.CDScale; + babyMob.strikeRange = 600 + babyMob.onHit = function() { + this.cd = simulation.cycle + this.delay; + //dislodge ammo + if (b.inventory.length) { + let isRemovedAmmo = false + const numRemoved = 3 + for (let j = 0; j < numRemoved; j++) { + for (let i = 0; i < b.inventory.length; i++) { + const gun = b.guns[b.inventory[i]] + if (gun.ammo > 0 && gun.ammo !== Infinity) { + gun.ammo -= Math.ceil((0.45 * Math.random() + 0.45 * Math.random()) * gun.ammoPack) //Math.ceil(Math.random() * target.ammoPack) + if (gun.ammo < 0) gun.ammo = 0 + isRemovedAmmo = true + } + } + } + if (isRemovedAmmo) { + simulation.updateGunHUD(); + for (let j = 0; j < numRemoved; j++) powerUps.directSpawn(this.position.x + 10 * Math.random(), this.position.y + 10 * Math.random(), "ammo"); + powerUps.ejectGraphic(); + } + } + }; + } + + const stiffness = 0.01 + const damping = 0.1 + for (let i = 1; i < nodes; ++i) { //attach to center mob + consBB[consBB.length] = Constraint.create({ + bodyA: mob[mob.length - i], + bodyB: mob[mob.length - i - 1], + stiffness: stiffness, + damping: damping + }); + Composite.add(engine.world, consBB[consBB.length - 1]); + } + consBB[consBB.length] = Constraint.create({ + bodyA: mob[mob.length - 1], + bodyB: mob[mob.length - nodes], + stiffness: stiffness, + damping: damping + }); + Composite.add(engine.world, consBB[consBB.length - 1]); + + + for (let i = 0; i < nodes; ++i) { //attach to center mob + consBB[consBB.length] = Constraint.create({ + bodyA: me, + bodyB: mob[mob.length - i - 1], + stiffness: stiffness, + damping: damping + }); + Composite.add(engine.world, consBB[consBB.length - 1]); + } + spawn.allowShields = true; + }, // timeSkipBoss(x, y, radius = 55) { // mobs.spawn(x, y, 6, radius, '#000'); // let me = mob[mob.length - 1]; @@ -2358,12 +2693,13 @@ const spawn = { me.memory = 240 me.seePlayerFreq = 60 me.blinkRange = 235 - if (false && 0.5 < Math.random()) { + if (0.5 < Math.random()) { me.grenadeDelay = 260 + me.blinkRange *= 1.5 } else { me.grenadeDelay = 100 } - me.pulseRadius = 1.4 * Math.min(550, 220 + simulation.difficulty * 4) + me.pulseRadius = 1.5 * Math.min(550, 200 + simulation.difficulty * 2) me.delay = 30 + 35 * simulation.CDScale; me.nextBlinkCycle = me.delay; spawn.shield(me, x, y, 1); @@ -2919,8 +3255,9 @@ const spawn = { me.g = 0.00015; //required if using this.gravity me.frictionStatic = 0; me.friction = 0; - me.delay = 90 * simulation.CDScale; + me.delay = 30 + 60 * simulation.CDScale; me.cd = Infinity; + me.strikeRange = 300 Matter.Body.rotate(me, Math.PI * 0.1); spawn.shield(me, x, y); me.onDamage = function() { @@ -2953,7 +3290,7 @@ const spawn = { if (distMag < 400) { Matter.Body.translate(this, Vector.mult(Vector.normalise(dist), distMag - 20 - radius)); } else { - Matter.Body.translate(this, Vector.mult(Vector.normalise(dist), 300)); + Matter.Body.translate(this, Vector.mult(Vector.normalise(dist), this.strikeRange)); } ctx.lineTo(this.position.x, this.position.y); ctx.lineWidth = radius * 2.1; @@ -2966,7 +3303,7 @@ const spawn = { } }; }, - slashBoss(x, y, radius = 70) { + revolutionBoss(x, y, radius = 70) { const sides = 9 + Math.floor(Math.min(12, 0.2 * simulation.difficulty)) const coolBends = [-1.8, 0, 0, 0.9, 1.2] const bendFactor = coolBends[Math.floor(Math.random() * coolBends.length)]; @@ -2979,7 +3316,7 @@ const spawn = { me.laserAngle = 0; me.swordDamage = 0.0025 * simulation.dmgScale - spawn.shield(me, x, y, 1); + // spawn.shield(me, x, y, 1); Matter.Body.setDensity(me, 0.005); //extra dense //normal is 0.001 //makes effective life much larger me.damageReduction = 0.11 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1) me.isBoss = true; @@ -2989,42 +3326,62 @@ const spawn = { }; + //invulnerability every 1/4 fraction of life lost + //required setup for invulnerable + // me.isInvulnerable = false + // me.isNextInvulnerability = 0.75 + // me.invulnerabilityCountDown = 0 + // me.invulnerable = function() { + // if (this.health < this.isNextInvulnerability) { + // this.isNextInvulnerability = Math.floor(this.health * 4) / 4 //0.75,0.5,0.25 + // this.isInvulnerable = true + // this.startingDamageReduction = this.damageReduction + // this.damageReduction = 0 + // this.invulnerabilityCountDown = 240 + // } + // if (this.isInvulnerable) { + // if (this.invulnerabilityCountDown > 0) { + // this.invulnerabilityCountDown-- + // //graphics //draw a super shield? + // ctx.beginPath(); + // let vertices = this.vertices; + // ctx.moveTo(vertices[0].x, vertices[0].y); + // for (let j = 1; j < vertices.length; j++) ctx.lineTo(vertices[j].x, vertices[j].y); + // ctx.lineTo(vertices[0].x, vertices[0].y); + // ctx.lineWidth = 20; + // // ctx.fillStyle = `rgba(${Math.floor(255 * Math.random())},${Math.floor(255 * Math.random())},${Math.floor(255 * Math.random())},0.5)` + // // ctx.fill(); + // ctx.strokeStyle = "rgba(255,255,255,0.7)"; + // ctx.stroke(); + // } else { + // this.isInvulnerable = false + // this.damageReduction = this.startingDamageReduction + // } + // } + // } - me.startingDamageReduction = me.damageReduction + //invulnerable every other revolution me.isInvulnerable = false - me.isNextInvulnerability = 0.75 - me.invulnerabilityCountDown = 0 me.invulnerable = function() { - if (this.health < this.isNextInvulnerability) { - this.isNextInvulnerability = Math.floor(this.health * 4) / 4 //0.75,0.5,0.25 - this.isInvulnerable = true - this.damageReduction = 0 - this.invulnerabilityCountDown = 180 + if (this.laserAngle % (4 * Math.PI) > 2 * Math.PI) { + if (!this.isInvulnerable) { + this.isInvulnerable = true + this.startingDamageReduction = this.damageReduction + this.damageReduction = 0 + } + } else if (this.isInvulnerable) { + this.isInvulnerable = false + this.damageReduction = this.startingDamageReduction } if (this.isInvulnerable) { - if (this.invulnerabilityCountDown > 0) { - this.invulnerabilityCountDown-- - //graphics //draw a super shield? - - ctx.beginPath(); - - let vertices = this.vertices; - ctx.moveTo(vertices[0].x, vertices[0].y); - for (let j = 1; j < vertices.length; j++) ctx.lineTo(vertices[j].x, vertices[j].y); - ctx.lineTo(vertices[0].x, vertices[0].y); - ctx.lineWidth = 20; - // ctx.fillStyle = `rgba(${Math.floor(255 * Math.random())},${Math.floor(255 * Math.random())},${Math.floor(255 * Math.random())},0.5)` - // ctx.fill(); - ctx.strokeStyle = "rgba(255,255,255,0.4)"; - ctx.stroke(); - - - - - } else { - this.isInvulnerable = false - this.damageReduction = this.startingDamageReduction - } + ctx.beginPath(); + let vertices = this.vertices; + ctx.moveTo(vertices[0].x, vertices[0].y); + for (let j = 1; j < vertices.length; j++) ctx.lineTo(vertices[j].x, vertices[j].y); + ctx.lineTo(vertices[0].x, vertices[0].y); + ctx.lineWidth = 20; + ctx.strokeStyle = "rgba(255,255,255,0.7)"; + ctx.stroke(); } } diff --git a/js/tech.js b/js/tech.js index ec3a2e9..ff549b1 100644 --- a/js/tech.js +++ b/js/tech.js @@ -1980,9 +1980,7 @@ const tech = { requires: "", effect() { tech.isDroneOnDamage = true; - for (let i = 0; i < 4; i++) { - b.drone() //spawn drone - } + // for (let i = 0; i < 4; i++) b.drone() }, remove() { tech.isDroneOnDamage = false; @@ -5028,9 +5026,9 @@ const tech = { frequency: 1, frequencyDefault: 1, allowed() { - return tech.haveGunCheck("drones", false) + return tech.haveGunCheck("drones", false) && !tech.isDroneRespawn && tech.isBulletsLastLonger === 1 }, - requires: "drones", + requires: "drones, not drone repair, anti-shear topology", effect() { const num = 8 tech.isForeverDrones += num @@ -5385,9 +5383,9 @@ const tech = { isBot: true, isBotTech: true, isNonRefundable: true, - requires: "foam gun, not bot upgrades NOT EXPERIMENT MODE,", + requires: "foam gun, NOT EXPERIMENT MODE, bot upgrades, fractionation, quantum foam", allowed() { - return tech.haveGunCheck("foam", false) && !b.hasBotUpgrade() + return tech.haveGunCheck("foam", false) && !b.hasBotUpgrade() && !tech.isAmmoFoamSize && !tech.foamFutureFire }, effect() { tech.giveTech("foam-bot upgrade") diff --git a/style.css b/style.css index 68f48c7..301d9e9 100644 --- a/style.css +++ b/style.css @@ -435,7 +435,7 @@ summary { z-index: 2; width: 100%; height: 100%; - background-color: #ecf0f3; + background-color: #e8edf0; opacity: 1; transition: opacity 3s; pointer-events: none; diff --git a/todo.txt b/todo.txt index f5a876e..3b14237 100644 --- a/todo.txt +++ b/todo.txt @@ -1,15 +1,43 @@ ******************************************************** NEXT PATCH ************************************************** -several tech now have fewer requirements to unlock -some normal tech is now considered gun tech (mostly explosion stuff) -snakeBoss tails, stay attached to each other -"snakeBoss", "blockBoss", "powerupBoss" are now the only bosses + +mantisBoss: 3 punching mobs, eject your ammo when you get punched + invulnerable except for 1 second after moving + +powerUpBossBaby: smaller, cuter, faster, can't move through walls + +slashBoss renamed revolutionBoss + invulnerable every other revolution of it's blades + +new mob state: invulnerable - immune to damage and status effects (stun, dots, freeze) +damage and harm circle graphic size have been made more consistent bug fixes + ******************************************************** TODO ******************************************************** -suggestion: quasarBoss: inverted pulsar boss that hits everything except where its aiming +history vision messes up when drones hit boss? + for babypowerupboss + +some boss mobs need invulnerability phases + powerUpBoss after death? + nerf powerup boss health or acceleration to balance + growboss + after one dies + +electric motors: increases movement speed and jump height, but jumping and moving costs energy + overwrite the key event listeners? + JUNK tech? + +make the shields that shieldBoss adds twice as damage resistant + +mob that fires bullets in 4,5,6,7 different directions at once, no aiming + +bug once: switching from shotgun to harpoon somehow set b.activeGun to not defined + https://discord.com/channels/645222059647172618/646505973610971165/919116288008290324 + +quasarBoss: inverted pulsar boss that hits everything except where its aiming pulsar mobs retarget too easily also they drift around too much @@ -18,9 +46,6 @@ intro map: diegeticly draw a mouse with field highlighted also indicate space? dynamically adjust drawing after picking up a gun -some boss mobs need invulnerability phases - maybe trigger immunity to damage for 5-10s after taking the first hit? - increase mass and movement speed at the same time increase jump differently because it scales extra with mass