diff --git a/.DS_Store b/.DS_Store index 34a581f..1bffd0b 100644 Binary files a/.DS_Store and b/.DS_Store differ diff --git a/js/bullet.js b/js/bullet.js index 369394b..970a34f 100644 --- a/js/bullet.js +++ b/js/bullet.js @@ -2468,7 +2468,7 @@ const b = { Matter.Query.ray(map, this.position, mob[i].position).length === 0 && Matter.Query.ray(body, this.position, mob[i].position).length === 0 && !mob[i].isShielded) { - const SPEED = 45 + const SPEED = 40 const unit = Vector.normalise(Vector.sub(Vector.add(mob[i].position, Vector.mult(mob[i].velocity, Math.sqrt(dist) / 60)), this.position)) b.nail(this.position, Vector.mult(unit, SPEED), 0.4) this.force = Vector.mult(unit, -0.01 * this.mass) @@ -2611,8 +2611,8 @@ const b = { lookFrequency: 40 + Math.floor(7 * Math.random()) - 10 * tech.isLaserBotUpgrade, range: (700 + 400 * tech.isLaserBotUpgrade) * (1 + 0.1 * Math.random()), drainThreshold: tech.isEnergyHealth ? 0.6 : 0.4, - drain: 0.7 - 0.52 * tech.isLaserBotUpgrade, - laserDamage: 0.38 + 0.29 * tech.isLaserBotUpgrade, + drain: 0.56 - 0.42 * tech.isLaserBotUpgrade, + laserDamage: 0.5 + 0.35 * tech.isLaserBotUpgrade, endCycle: Infinity, classType: "bullet", collisionFilter: { @@ -3265,11 +3265,11 @@ const b = { if (m.crouch) { spread = 0.75 m.fireCDcycle = m.cycle + Math.floor(55 * b.fireCD); // cool down - if (tech.isShotgunImmune) m.immuneCycle = m.cycle + Math.floor(58 * b.fireCD); //player is immune to collision damage for 30 cycles + if (tech.isShotgunImmune && m.immuneCycle < m.cycle + Math.floor(58 * b.fireCD)) m.immuneCycle = m.cycle + Math.floor(58 * b.fireCD); //player is immune to collision damage for 30 cycles knock = 0.01 } else { m.fireCDcycle = m.cycle + Math.floor(45 * b.fireCD); // cool down - if (tech.isShotgunImmune) m.immuneCycle = m.cycle + Math.floor(47 * b.fireCD); //player is immune to collision damage for 30 cycles + if (tech.isShotgunImmune && m.immuneCycle < m.cycle + Math.floor(47 * b.fireCD)) m.immuneCycle = m.cycle + Math.floor(47 * b.fireCD); //player is immune to collision damage for 30 cycles spread = 1.3 knock = 0.1 } @@ -4514,7 +4514,7 @@ const b = { m.fireCDcycle = m.cycle + Math.floor(120 * b.fireCD); // cool down } else { m.energy -= DRAIN - m.immuneCycle = m.cycle + 30; //player is immune to collision damage for 5 cycles + if (m.immuneCycle < m.cycle + 30) m.immuneCycle = m.cycle + 30; //player is immune to collision damage for 5 cycles Matter.Body.setPosition(player, history.position); Matter.Body.setVelocity(player, { x: history.velocity.x, y: history.velocity.y }); if (m.health !== history.health) { diff --git a/js/engine.js b/js/engine.js index c209c14..52ecb36 100644 --- a/js/engine.js +++ b/js/engine.js @@ -108,7 +108,20 @@ function collisionChecks(event) { m.damage(dmg); return } - m.damage(dmg); + + if (tech.isAnthropicHarm) { + if (!tech.isAnthropicHarmImmune) { + tech.isAnthropicHarmImmune = true + if (document.getElementById("tech-flip-flop")) document.getElementById("tech-flip-flop").innerHTML = ` = on` + m.damage(dmg * 1.25); //damage triggers immune to next hit with extra 10% damage + } else { + tech.isAnthropicHarmImmune = false //immune to damage this hit, lose immunity for next hit + if (document.getElementById("tech-flip-flop")) document.getElementById("tech-flip-flop").innerHTML = ` = off` + } + } else { + m.damage(dmg); //normal damage + } + if (tech.isPiezo) m.energy += 20.48; if (tech.isBayesian) powerUps.ejectTech() if (mob[k].onHit) mob[k].onHit(k); diff --git a/js/level.js b/js/level.js index fbcc566..eaa6114 100644 --- a/js/level.js +++ b/js/level.js @@ -12,7 +12,7 @@ const level = { start() { if (level.levelsCleared === 0) { //this code only runs on the first level // simulation.enableConstructMode() //used to build maps in testing mode - // level.difficultyIncrease(40) + // level.difficultyIncrease(5) // simulation.zoomScale = 1000; // simulation.setZoom(); // m.setField("nano-scale manufacturing") @@ -792,7 +792,7 @@ const level = { } else { if (damage < 0.02) { m.damage(damage) - } else if (m.immuneCycle < m.cycle) { + } else if (m.immuneCycle < m.cycle + tech.collisionImmuneCycles) { m.immuneCycle = m.cycle + tech.collisionImmuneCycles; m.damage(damage) simulation.drawList.push({ //add dmg to draw queue @@ -1090,7 +1090,7 @@ const level = { // spawn.boost(1500, 0, 900); // spawn.starter(1900, -500, 200) //big boy - // spawn.starter(1900, -500) + spawn.starter(1900, -500) // spawn.historyBoss(1900, -500) // spawn.ghoster(2900, -500) // spawn.launcherBoss(1200, -500) @@ -1103,12 +1103,12 @@ const level = { // spawn.cellBossCulture(1600, -500) // spawn.cellBossCulture(1600, -500) // simulation.difficulty = 30 - spawn.orbitalBoss(1600, -500) + spawn.shieldingBoss(1600, -500) // spawn.beamer(1200, -500) // spawn.shield(mob[mob.length - 1], 1800, -120, 1); // spawn.nodeGroup(1200, -500, "launcher") - // spawn.snakeBoss(1200, -500) + spawn.snakeBoss(1200, -500) // spawn.powerUpBoss(2900, -500) // spawn.randomMob(1600, -500) }, @@ -1698,16 +1698,16 @@ const level = { spawn.randomMob(-75, -1475, 0); spawn.randomGroup(600, -2600, 0); } - if (simulation.difficulty < 25) { + if (simulation.difficulty < 20) { spawn.randomMob(700, -1650, 0); spawn.randomMob(600, -3500, 0.2); spawn.randomMob(-75, -1175, 0.2); powerUps.spawnBossPowerUp(-125, -1760); } else { if (Math.random() < 0.5) { - spawn.randomLevelBoss(700, -1550, ["shooterBoss", "launcherBoss", "laserTargetingBoss", "streamBoss"]); + spawn.randomLevelBoss(700, -1550, ["shooterBoss", "launcherBoss", "laserTargetingBoss", "streamBoss", "shieldingBoss"]); } else { - spawn.randomLevelBoss(675, -2775, ["shooterBoss", "launcherBoss", "laserTargetingBoss", "streamBoss"]); + spawn.randomLevelBoss(675, -2775, ["shooterBoss", "launcherBoss", "laserTargetingBoss", "streamBoss", "shieldingBoss"]); } } powerUps.addRerollToLevel() //needs to run after mobs are spawned @@ -1854,7 +1854,7 @@ const level = { spawn.randomMob(3600, 1725, 0.9); spawn.randomMob(4100, 1225, 0.9); spawn.randomMob(2825, 400, 0.9); - if (simulation.difficulty > 3) spawn.randomLevelBoss(6000, 2300, ["spiderBoss", "launcherBoss", "laserTargetingBoss", "streamBoss", "historyBoss", "orbitalBoss"]); + if (simulation.difficulty > 3) spawn.randomLevelBoss(6000, 2300, ["spiderBoss", "launcherBoss", "laserTargetingBoss", "streamBoss", "historyBoss", "orbitalBoss", "shieldingBoss"]); powerUps.addRerollToLevel() //needs to run after mobs are spawned if (tech.isDuplicateBoss && Math.random() < 2 * tech.duplicationChance()) spawn.randomLevelBoss(7725, 2275); }, @@ -2459,17 +2459,7 @@ const level = { if (simulation.difficulty > 3) { if (Math.random() < 0.1) { // tether ball const index = mob.length - spawn.tetherBoss(4250, 0) - cons[cons.length] = Constraint.create({ - pointA: { - x: 4250, - y: -675 - }, - bodyB: mob[index], - stiffness: 0.00007 - }); - World.add(engine.world, cons[cons.length - 1]); - + spawn.tetherBoss(4250, 0, { x: 4250, y: -675 }) if (simulation.difficulty > 4) spawn.nodeGroup(4250, 0, "spawns", 8, 20, 105); //chance to spawn a ring of exploding mobs around this boss } else if (Math.random() < 0.2) { spawn.randomLevelBoss(4250, -250); @@ -3214,16 +3204,7 @@ const level = { height: 525, color: "#ccc" }); - spawn.tetherBoss(2850, -80) - cons[cons.length] = Constraint.create({ - pointA: { - x: 2500, - y: -500 - }, - bodyB: mob[mob.length - 1], - stiffness: 0.00012 - }); - World.add(engine.world, cons[cons.length - 1]); + spawn.tetherBoss(2850, -80, { x: 2500, y: -500 }) //chance to spawn a ring of exploding mobs around this boss if (simulation.difficulty > 6) spawn.nodeGroup(2850, -80, "spawns", 8, 20, 105); } else { @@ -3690,16 +3671,7 @@ const level = { if (simulation.difficulty > 2) { if (Math.random() < 0.2) { // tether ball - spawn.tetherBoss(7000, -3300) - cons[cons.length] = Constraint.create({ - pointA: { - x: 7300, - y: -3300 - }, - bodyB: mob[mob.length - 1], - stiffness: 0.00006 - }); - World.add(engine.world, cons[cons.length - 1]); + spawn.tetherBoss(7000, -3300, { x: 7300, y: -3300 }) if (simulation.difficulty > 4) spawn.nodeGroup(7000, -3300, "spawns", 8, 20, 105); } else if (simulation.difficulty > 3) { spawn.randomLevelBoss(6100, -3600, ["shooterBoss", "launcherBoss", "laserTargetingBoss", "spiderBoss", "laserBoss"]); @@ -3709,16 +3681,7 @@ const level = { if (simulation.difficulty > 2) { if (Math.random() < 0.2) { // tether ball - spawn.tetherBoss(2300, -1300) - cons[cons.length] = Constraint.create({ - pointA: { - x: 2300, - y: -1750 - }, - bodyB: mob[mob.length - 1], - stiffness: 0.00036 - }); - World.add(engine.world, cons[cons.length - 1]); + spawn.tetherBoss(2300, -1300, { x: 2300, y: -1750 }) if (simulation.difficulty > 4) spawn.nodeGroup(2350, -1300, "spawns", 8, 20, 105); } else if (simulation.difficulty > 3) { spawn.randomLevelBoss(2300, -1400, ["shooterBoss", "launcherBoss", "laserTargetingBoss", "spiderBoss", "laserBoss", "snakeBoss"]); @@ -3785,16 +3748,7 @@ const level = { spawn.mapRect(3075, 1075, 375, 150); //Plafond salle trésor spawn.mapRect(3300, 1075, 1500, 1800); //Mur droite salle trésor // tether ball - spawn.tetherBoss(2330, 1850) - cons[cons.length] = Constraint.create({ - pointA: { - x: 2330, - y: 1425 - }, - bodyB: mob[mob.length - 1], - stiffness: 0.00017 - }); - World.add(engine.world, cons[cons.length - 1]); + spawn.tetherBoss(2330, 1850, { x: 2330, y: 1425 }) //chance to spawn a ring of exploding mobs around this boss if (simulation.difficulty > 4) spawn.nodeGroup(2330, 1850, "spawns", 8, 20, 105); powerUps.chooseRandomPowerUp(3100, 1630); @@ -4062,34 +4016,25 @@ const level = { spawn.randomGroup(8025, -845, 0.2); if (simulation.difficulty > 2) { - if (Math.random() < 0.2) { - // tether ball - spawn.tetherBoss(8000, 630) - let me = mob[mob.length - 1]; - me.onDeath = function() { - this.removeCons(); //remove constraint - spawnCouloirEnHaut() - doorSortieSalle.isOpen = false; - }; - cons[cons.length] = Constraint.create({ - pointA: { - x: 8550, - y: 680 - }, - bodyB: mob[mob.length - 1], - stiffness: 0.00015 - }); - World.add(engine.world, cons[cons.length - 1]); - if (simulation.difficulty > 4) spawn.nodeGroup(8000, 630, "spawns", 8, 20, 105); - } else { - spawn.randomLevelBoss(8000, 630, ["shooterBoss", "launcherBoss", "laserTargetingBoss", "spiderBoss", "laserBoss", "bomberBoss", "orbitalBoss"]); - let me = mob[mob.length - 1]; - me.onDeath = function() { - this.removeCons(); //remove constraint - spawnCouloirEnHaut() - doorSortieSalle.isOpen = false; - }; - } + // if (Math.random() < 0.2) { + // // tether ball + // spawn.tetherBoss(8000, 630, { x: 8550, y: 680 }) + // let me = mob[mob.length - 1]; + // me.onDeath = function() { + // this.removeCons(); //remove constraint + // spawnCouloirEnHaut() + // doorSortieSalle.isOpen = false; + // }; + // if (simulation.difficulty > 4) spawn.nodeGroup(8000, 630, "spawns", 8, 20, 105); + // } else { + spawn.randomLevelBoss(8000, 630, ["shooterBoss", "launcherBoss", "laserTargetingBoss", "spiderBoss", "laserBoss", "bomberBoss", "orbitalBoss"]); + let me = mob[mob.length - 1]; + me.onDeath = function() { + this.removeCons(); //remove constraint + spawnCouloirEnHaut() + doorSortieSalle.isOpen = false; + }; + // } } else { spawn.randomLevelBoss(8000, 630, ["shooterBoss"]); let me = mob[mob.length - 1]; @@ -4658,18 +4603,8 @@ const level = { if (simulation.difficulty > 3) { if (Math.random() < 0.16) { - spawn.tetherBoss(3380, -1775) - cons[cons.length] = Constraint.create({ - pointA: { - x: 3775, - y: -1775 - }, - bodyB: mob[mob.length - 1], - stiffness: 0.00018 + 0.000007 * level.levelsCleared - }); - World.add(engine.world, cons[cons.length - 1]); + spawn.tetherBoss(3380, -1775, { x: 3775, y: -1775 }) if (simulation.difficulty > 4) spawn.nodeGroup(3380, -1775, "spawns", 8, 20, 105); //chance to spawn a ring of exploding mobs around this boss - } else { spawn.randomLevelBoss(3100, -1850, ["shooterBoss", "spiderBoss", "launcherBoss", "laserTargetingBoss", "snakeBoss", "laserBoss"]); } diff --git a/js/mob.js b/js/mob.js index 28f3584..2e78b5c 100644 --- a/js/mob.js +++ b/js/mob.js @@ -651,25 +651,27 @@ const mobs = { //cause all mobs, and bodies to rotate in a circle applyCurl = function(center, array, isAntiGravity = true) { for (let i = 0; i < array.length; ++i) { - const sub = Vector.sub(center, array[i].position) - const radius2 = Vector.magnitudeSquared(sub); + if (!array[i].isNotHoldable) { + const sub = Vector.sub(center, array[i].position) + const radius2 = Vector.magnitudeSquared(sub); - //if too close, like center mob or shield, don't curl // if too far don't curl - if (radius2 < range * range && radius2 > 10000) { - const curlVector = Vector.mult(Vector.perp(Vector.normalise(sub)), mag) - //apply curl force - Matter.Body.setVelocity(array[i], { - x: array[i].velocity.x * 0.94 + curlVector.x * 0.06, - y: array[i].velocity.y * 0.94 + curlVector.y * 0.06 - }) - if (isAntiGravity) array[i].force.y -= 0.8 * simulation.g * array[i].mass - // //draw curl, for debugging - // ctx.beginPath(); - // ctx.moveTo(array[i].position.x, array[i].position.y); - // ctx.lineTo(array[i].position.x + curlVector.x * 10, array[i].position.y + curlVector.y * 10); - // ctx.lineWidth = 2; - // ctx.strokeStyle = "#000"; - // ctx.stroke(); + //if too close, like center mob or shield, don't curl // if too far don't curl + if (radius2 < range * range && radius2 > 10000) { + const curlVector = Vector.mult(Vector.perp(Vector.normalise(sub)), mag) + //apply curl force + Matter.Body.setVelocity(array[i], { + x: array[i].velocity.x * 0.94 + curlVector.x * 0.06, + y: array[i].velocity.y * 0.94 + curlVector.y * 0.06 + }) + if (isAntiGravity) array[i].force.y -= 0.8 * simulation.g * array[i].mass + // //draw curl, for debugging + // ctx.beginPath(); + // ctx.moveTo(array[i].position.x, array[i].position.y); + // ctx.lineTo(array[i].position.x + curlVector.x * 10, array[i].position.y + curlVector.y * 10); + // ctx.lineWidth = 2; + // ctx.strokeStyle = "#000"; + // ctx.stroke(); + } } } } @@ -1050,7 +1052,7 @@ const mobs = { bullet[bullet.length - 1].endCycle = simulation.cycle + 1000 + Math.floor(400 * Math.random()) this.leaveBody = false; // no body since it turned into the bot } - } else if (tech.isShieldAmmo && this.shield) { + } else if (tech.isShieldAmmo && this.shield && !this.isBonusShield) { let type = tech.isEnergyNoAmmo ? "heal" : "ammo" if (Math.random() < 0.4) { type = "heal" diff --git a/js/player.js b/js/player.js index 92556bb..7f1a602 100644 --- a/js/player.js +++ b/js/player.js @@ -570,7 +570,7 @@ const m = { } } m.energy = Math.max(m.energy - steps / 136, 0.01) - m.immuneCycle = m.cycle + 30; //player is immune to collision damage for 30 cycles + if (m.immuneCycle < m.cycle + tech.collisionImmuneCycles) m.immuneCycle = m.cycle + tech.collisionImmuneCycles; //player is immune to collision damage for 30 cycles let isDrawPlayer = true const shortPause = function() { @@ -642,7 +642,7 @@ const m = { simulation.makeTextLog(`m.research--
${powerUps.research.count}`) for (let i = 0; i < 6; i++) powerUps.spawn(m.pos.x, m.pos.y, "heal", false); m.energy = m.maxEnergy - m.immuneCycle = m.cycle + 360 //disable this.immuneCycle bonus seconds + if (m.immuneCycle < m.cycle + 360) m.immuneCycle = m.cycle + 360 //disable this.immuneCycle bonus seconds simulation.wipe = function() { //set wipe to have trails ctx.fillStyle = "rgba(255,255,255,0.03)"; ctx.fillRect(0, 0, canvas.width, canvas.height); @@ -671,7 +671,7 @@ const m = { simulation.makeTextLog(`m.research--
${powerUps.research.count}`) for (let i = 0; i < 6; i++) powerUps.spawn(m.pos.x + 10 * Math.random(), m.pos.y + 10 * Math.random(), "heal", false); - m.immuneCycle = m.cycle + 360 //disable this.immuneCycle bonus seconds + if (m.immuneCycle < m.cycle + 360) m.immuneCycle = m.cycle + 360 //disable this.immuneCycle bonus seconds simulation.wipe = function() { //set wipe to have trails ctx.fillStyle = "rgba(255,255,255,0.03)"; ctx.fillRect(0, 0, canvas.width, canvas.height); @@ -1777,7 +1777,7 @@ const m = { simulation.cycle--; //pause all functions that depend on game cycle increasing if (tech.isTimeSkip) { - m.immuneCycle = m.cycle + 10; + if (m.immuneCycle < m.cycle + 10) m.immuneCycle = m.cycle + 10; simulation.isTimeSkipping = true; m.cycle++; simulation.gravity(); @@ -2537,7 +2537,7 @@ const m = { x: velocity.x, y: velocity.y - 4 //an extra vertical kick so the player hangs in place longer }); - m.immuneCycle = m.cycle + 15; //player is immune to collision damage + if (m.immuneCycle < m.cycle + tech.collisionImmuneCycles) m.immuneCycle = m.cycle + tech.collisionImmuneCycles; //player is immune to collision damage // move bots to player for (let i = 0; i < bullet.length; i++) { if (bullet[i].botType) { @@ -2813,7 +2813,7 @@ const m = { if (tech.isPiezo) m.energy += 20.48; if (tech.isBayesian) powerUps.ejectTech() if (mob[k].onHit) mob[k].onHit(k); - m.immuneCycle = m.cycle + tech.collisionImmuneCycles; //player is immune to collision damage for 30 cycles + if (m.immuneCycle < m.cycle + tech.collisionImmuneCycles) m.immuneCycle = m.cycle + tech.collisionImmuneCycles; //player is immune to collision damage for 30 cycles //extra kick between player and mob //this section would be better with forces but they don't work... let angle = Math.atan2(player.position.y - mob[k].position.y, player.position.x - mob[k].position.x); Matter.Body.setVelocity(player, { diff --git a/js/powerup.js b/js/powerup.js index 92c7863..6166064 100644 --- a/js/powerup.js +++ b/js/powerup.js @@ -70,7 +70,7 @@ const powerUps = { document.body.style.overflow = "hidden" simulation.paused = false; simulation.isChoosing = false; //stops p from un pausing on key down - m.immuneCycle = m.cycle + 60; //player is immune to collision damage for 30 cycles + if (m.immuneCycle < m.cycle + tech.collisionImmuneCycles) m.immuneCycle = m.cycle + tech.collisionImmuneCycles; //player is immune to collision damage for 30 cycles build.unPauseGrid() requestAnimationFrame(cycle); }, diff --git a/js/simulation.js b/js/simulation.js index 9edb2ee..c783191 100644 --- a/js/simulation.js +++ b/js/simulation.js @@ -1002,7 +1002,7 @@ const simulation = { //ctx.fillText(bodies[i].id,bodies[i].position.x,bodies[i].position.y); //shows the id of every body let vertices = bodies[i].vertices; ctx.moveTo(vertices[0].x, vertices[0].y); - for (let j = 1; j < vertices.length; j += 1) { + for (let j = 1; j < vertices.length; j++) { ctx.lineTo(vertices[j].x, vertices[j].y); } ctx.lineTo(vertices[0].x, vertices[0].y); diff --git a/js/spawn.js b/js/spawn.js index 0d53a54..0dcda91 100644 --- a/js/spawn.js +++ b/js/spawn.js @@ -82,8 +82,8 @@ const spawn = { } } }, - //, "historyBoss", "shooterBoss", "cellBossCulture", "bomberBoss", "spiderBoss", "launcherBoss", "laserTargetingBoss", "powerUpBoss", "snakeBoss", "streamBoss" - randomLevelBoss(x, y, options = ["orbitalBoss"]) { + // + randomLevelBoss(x, y, options = ["shieldingBoss", "orbitalBoss", "historyBoss", "shooterBoss", "cellBossCulture", "bomberBoss", "spiderBoss", "launcherBoss", "laserTargetingBoss", "powerUpBoss", "snakeBoss", "streamBoss"]) { // other bosses: suckerBoss, laserBoss, tetherBoss, //these need a particular level to work so they are not included in the random pool spawn[options[Math.floor(Math.random() * options.length)]](x, y) }, @@ -437,7 +437,7 @@ const spawn = { vertexCollision(where, look, body); if (!m.isCloak) vertexCollision(where, look, [player]); if (best.who && best.who === player && m.immuneCycle < m.cycle) { - m.immuneCycle = m.cycle + 60 + tech.collisionImmuneCycles; //player is immune to collision damage for 30 cycles + if (m.immuneCycle < m.cycle + 60 + tech.collisionImmuneCycles) m.immuneCycle = m.cycle + 60 + tech.collisionImmuneCycles; //player is immune to collision damage extra time m.damage(dmg); simulation.drawList.push({ //add dmg to draw queue x: best.x, @@ -1623,7 +1623,7 @@ const spawn = { vertexCollision(where, look, body); if (!m.isCloak) vertexCollision(where, look, [player]); if (best.who && best.who === player && m.immuneCycle < m.cycle) { - m.immuneCycle = m.cycle + tech.collisionImmuneCycles; //player is immune to collision damage for 30 cycles + m.immuneCycle = m.cycle + tech.collisionImmuneCycles + 60; //player is immune to collision damage for an extra second const dmg = 0.14 * simulation.dmgScale; m.damage(dmg); simulation.drawList.push({ //add dmg to draw queue @@ -1776,9 +1776,8 @@ const spawn = { }; }, sneaker(x, y, radius = 15 + Math.ceil(Math.random() * 20)) { - let me; mobs.spawn(x, y, 5, radius, "transparent"); - me = mob[mob.length - 1]; + let me = mob[mob.length - 1]; Matter.Body.setDensity(me, 0.002); //extra dense //normal is 0.001 //makes effective life much larger me.accelMag = 0.001 * simulation.accelScale; me.frictionAir = 0.01; @@ -1828,9 +1827,8 @@ const spawn = { }; }, ghoster(x, y, radius = 40 + Math.ceil(Math.random() * 100)) { - let me; mobs.spawn(x, y, 7, radius, "transparent"); - me = mob[mob.length - 1]; + let me = mob[mob.length - 1]; me.seeAtDistance2 = 300000; me.accelMag = 0.00012 * simulation.accelScale; if (map.length) me.searchTarget = map[Math.floor(Math.random() * (map.length - 1))].position; //required for search @@ -2321,6 +2319,62 @@ const spawn = { } }; }, + shieldingBoss(x, y, radius = 210) { + mobs.spawn(x, y, 9, radius, "rgb(150, 150, 255)"); + let me = mob[mob.length - 1]; + Matter.Body.rotate(me, Math.random() * 2 * Math.PI) + // me.stroke = "rgb(220,220,255)" + me.isBoss = true; + me.cycle = 0 + me.maxCycles = 120; + me.frictionStatic = 0; + me.friction = 0; + me.frictionAir = 0.5; + spawn.shield(me, x, y, 1); + spawn.spawnOrbitals(me, radius + 50 + 200 * Math.random()) + + Matter.Body.setDensity(me, 0.03); //extra dense //normal is 0.001 //makes effective life much larger + me.onDeath = function() { + powerUps.spawnBossPowerUp(this.position.x, this.position.y) + // this.vertices = Matter.Vertices.hull(Matter.Vertices.clockwiseSort(this.vertices)) //helps collisions functions work better after vertex have been changed + }; + me.onDamage = function() {}; + me.do = function() { + this.checkStatus(); + + //draw cycle timer + ctx.beginPath(); + ctx.moveTo(this.vertices[this.vertices.length - 1].x, this.vertices[this.vertices.length - 1].y) + const phase = (this.vertices.length + 1) * this.cycle / this.maxCycles + if (phase > 1) ctx.lineTo(this.vertices[0].x, this.vertices[0].y) + for (let i = 1; i < phase - 1; i++) { + ctx.lineTo(this.vertices[i].x, this.vertices[i].y) + } + ctx.lineWidth = 5 + ctx.strokeStyle = "rgb(255,255,255)" + ctx.stroke(); + + if (!m.isBodiesAsleep) { + this.cycle++ + if (this.cycle > this.maxCycles) { + this.cycle = 0 + ctx.beginPath(); + for (let i = 0; i < mob.length; i++) { + if (!mob[i].isShielded && !mob[i].shield && mob[i].dropPowerUp) { + ctx.moveTo(this.position.x, this.position.y) + ctx.lineTo(mob[i].position.x, mob[i].position.y) + + spawn.shield(mob[i], mob[i].position.x, mob[i].position.y, 1, true); + } + } + ctx.lineWidth = 20 + // ctx.lineCap = "round"; + ctx.strokeStyle = "rgba(200,200,255,0.9)" + ctx.stroke(); + } + } + }; + }, streamBoss(x, y, radius = 110) { mobs.spawn(x, y, 5, radius, "rgb(245,180,255)"); let me = mob[mob.length - 1]; @@ -2492,7 +2546,6 @@ const spawn = { me.memory = 250; me.laserRange = 500; Matter.Body.setDensity(me, 0.0015 + 0.0005 * Math.sqrt(simulation.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) for (let i = 0; i < mob.length; i++) { //wake up tail mobs @@ -2532,9 +2585,10 @@ const spawn = { stiffness: 0.05 }); World.add(engine.world, consBB[consBB.length - 1]); + spawn.shield(me, x, y, 1); }, snakeBody(x, y, radius = 14) { - mobs.spawn(x, y, 8, radius, "transparent"); + mobs.spawn(x, y, 8, radius, "rgba(0,180,180,0.4)"); let me = mob[mob.length - 1]; // me.onHit = function() { // //run this function on hitting player @@ -2545,7 +2599,7 @@ const spawn = { me.leaveBody = false; me.frictionAir = 0.02; me.isSnakeTail = true; - me.stroke = "#099" + me.stroke = "transparent" me.onDeath = function() { if (this.isSnakeTail) { //wake up tail mobs @@ -2567,7 +2621,7 @@ const spawn = { this.attraction(); }; }, - tetherBoss(x, y, radius = 90) { + tetherBoss(x, y, constraint, radius = 90) { // constrained mob boss for the towers level // often has a ring of mobs around it mobs.spawn(x, y, 8, radius, "rgb(0,60,80)"); @@ -2577,8 +2631,18 @@ const spawn = { me.accelMag = 0.002 * simulation.accelScale; me.memory = 20; Matter.Body.setDensity(me, 0.001 + 0.0005 * Math.sqrt(simulation.difficulty)); //extra dense //normal is 0.001 //makes effective life much larger - spawn.shield(me, x, y, 1); + + cons[cons.length] = Constraint.create({ + pointA: { + x: constraint.x, + y: constraint.y + }, + bodyB: me, + stiffness: 0.00012 + }); + World.add(engine.world, cons[cons.length - 1]); + spawn.shield(me, x, y, 1); setTimeout(() => { spawn.spawnOrbitals(me, radius + 50 + 200 * Math.random()) }, 100); //have to wait a sec so the tether constraint doesn't attach to an orbital me.onDeath = function() { powerUps.spawnBossPowerUp(this.position.x, this.position.y) @@ -2591,13 +2655,14 @@ const spawn = { this.attraction(); }; }, - shield(target, x, y, chance = Math.min(0.02 + simulation.difficulty * 0.005, 0.2)) { + shield(target, x, y, chance = Math.min(0.02 + simulation.difficulty * 0.005, 0.2), isBonusShield = false) { if (this.allowShields && Math.random() < chance) { mobs.spawn(x, y, 9, target.radius + 30, "rgba(220,220,255,0.9)"); let me = mob[mob.length - 1]; me.stroke = "rgb(220,220,255)"; Matter.Body.setDensity(me, 0.00001) //very low density to not mess with the original mob's motion me.shield = true; + me.isBonusShield = isBonusShield //this prevents spamming with tech.isShieldAmmo me.collisionFilter.category = cat.mobShield me.collisionFilter.mask = cat.bullet; consBB[consBB.length] = Constraint.create({ @@ -2625,12 +2690,16 @@ const spawn = { if (mob[i].id === this.shieldTargetID) mob[i].isShielded = false; } }; - //swap order of shield and mob, so that mob is behind shield graphically - mob[mob.length - 1] = mob[mob.length - 2]; - mob[mob.length - 2] = me; me.do = function() { this.checkStatus(); }; + + //move shield to the front of the array, so that mob is behind shield graphically + mob.unshift(me); + + //swap order of shield and mob, so that mob is behind shield graphically + // mob[mob.length - 1] = mob[mob.length - 2]; + // mob[mob.length - 2] = me; } }, groupShield(targets, x, y, radius, stiffness = 0.4) { diff --git a/js/tech.js b/js/tech.js index 322c82b..1018090 100644 --- a/js/tech.js +++ b/js/tech.js @@ -95,13 +95,12 @@ haveGunCheck(name) { if ( !build.isExperimentSelection && - b.inventory.length > 2 && + b.inventory > 2 && name !== b.guns[b.activeGun].name && - Math.random() > 2 / (b.inventory.length + tech.isGunCycle * 3) //lower chance of tech specific to a gun if you have lots of guns + Math.random() > 2 - b.inventory.length * 0.5 ) { return false } - for (i = 0, len = b.inventory.length; i < len; i++) { if (b.guns[b.inventory[i]].name === name) return true } @@ -1164,10 +1163,10 @@ requires: "", effect() { tech.collisionImmuneCycles += 45; - m.immuneCycle = m.cycle + tech.collisionImmuneCycles; //player is immune to collision damage for 30 cycles + if (m.immuneCycle < m.cycle + tech.collisionImmuneCycles) m.immuneCycle = m.cycle + tech.collisionImmuneCycles; //player is immune to collision damage for 30 cycles }, remove() { - tech.collisionImmuneCycles = 25; + tech.collisionImmuneCycles = 30; } }, { @@ -1186,6 +1185,84 @@ tech.cyclicImmunity = 0; } }, + { + name: "flip-flop", + description: "after a collision take 25% more harm
but, on your next collision take 0 harm", + nameInfo: "", + addNameInfo() { + setTimeout(function() { + if (document.getElementById("tech-flip-flop")) { + if (tech.isAnthropicHarmImmune) { + document.getElementById("tech-flip-flop").innerHTML = ` = on` + } else { + document.getElementById("tech-flip-flop").innerHTML = ` = off` + } + } + }, 100); + }, + maxCount: 1, + count: 0, + allowed() { + return true + }, + requires: "", + effect() { + tech.isAnthropicHarm = true //do you have this tech + tech.isAnthropicHarmImmune = false //are you immune to next collision + }, + remove() { + tech.isAnthropicHarm = false + tech.isAnthropicHarmImmune = false + } + }, + { + name: "clock gating", + description: `slow time by 50% after receiving harm
reduce harm by 20%`, + maxCount: 1, + count: 0, + allowed() { + return simulation.fpsCapDefault > 45 && !tech.isRailTimeSlow + }, + requires: "FPS above 45", + effect() { + tech.isSlowFPS = true; + }, + remove() { + tech.isSlowFPS = false; + } + }, + { + name: "liquid cooling", + description: `freeze all mobs for 7 seconds
after receiving harm`, + maxCount: 1, + count: 0, + allowed() { + return tech.isSlowFPS + }, + requires: "clock gating", + effect() { + tech.isHarmFreeze = true; + }, + remove() { + tech.isHarmFreeze = false; + } + }, + { + name: "osmoprotectant", + description: `collisions with stunned or frozen mobs
cause you no harm`, + maxCount: 1, + count: 0, + allowed() { + return tech.isStunField || tech.isPulseStun || tech.oneSuperBall || tech.isHarmFreeze || tech.isIceField || tech.isIceCrystals || tech.isSporeFreeze || tech.isAoESlow || tech.isFreezeMobs || tech.isCloakStun || tech.orbitBotCount > 1 || tech.isWormholeDamage + }, + requires: "a freezing or stunning effect", + effect() { + tech.isFreezeHarmImmune = true; + }, + remove() { + tech.isFreezeHarmImmune = false; + } + }, { name: "ablative drones", description: "rebuild your broken parts as drones
chance to occur after receiving harm", @@ -1237,54 +1314,6 @@ tech.isHarmDamage = false; } }, - { - name: "liquid cooling", - description: `freeze all mobs for 7 seconds
after receiving harm`, - maxCount: 1, - count: 0, - allowed() { - return tech.isSlowFPS - }, - requires: "clock gating", - effect() { - tech.isHarmFreeze = true; - }, - remove() { - tech.isHarmFreeze = false; - } - }, - { - name: "osmoprotectant", - description: `collisions with stunned or frozen mobs
cause you no harm`, - maxCount: 1, - count: 0, - allowed() { - return tech.isStunField || tech.isPulseStun || tech.oneSuperBall || tech.isHarmFreeze || tech.isIceField || tech.isIceCrystals || tech.isSporeFreeze || tech.isAoESlow || tech.isFreezeMobs || tech.isCloakStun || tech.orbitBotCount > 1 || tech.isWormholeDamage - }, - requires: "a freezing or stunning effect", - effect() { - tech.isFreezeHarmImmune = true; - }, - remove() { - tech.isFreezeHarmImmune = false; - } - }, - { - name: "clock gating", - description: `slow time by 50% after receiving harm
reduce harm by 20%`, - maxCount: 1, - count: 0, - allowed() { - return simulation.fpsCapDefault > 45 && !tech.isRailTimeSlow - }, - requires: "FPS above 45", - effect() { - tech.isSlowFPS = true; - }, - remove() { - tech.isSlowFPS = false; - } - }, { name: "CPT reversal", description: "charge, parity, and time invert to undo harm
rewind (1.5—5) seconds for (66—220) energy", @@ -1339,7 +1368,7 @@ maxCount: 1, count: 0, allowed() { - return !tech.isEnergyHealth && m.harmReduction() < 1 + return !tech.isEnergyHealth && (m.harmReduction() < 1 || tech.isAnthropicHarm) }, requires: "not mass-energy equivalence, some harm reduction", effect() { @@ -1860,7 +1889,7 @@ }, { name: "decoherence", - description: "enter an alternate reality after you research
spawn 9 research", + description: "enter an alternate reality after you research
spawn 11 research", maxCount: 1, count: 0, allowed() { @@ -1869,7 +1898,7 @@ requires: "not quantum immortality, many-worlds", effect() { tech.isResearchReality = true; - for (let i = 0; i < 9; i++) powerUps.spawn(m.pos.x + Math.random() * 10, m.pos.y + Math.random() * 10, "research", false); + for (let i = 0; i < 11; i++) powerUps.spawn(m.pos.x + Math.random() * 10, m.pos.y + Math.random() * 10, "research", false); }, remove() { tech.isResearchReality = false; @@ -2011,7 +2040,7 @@ }, { name: "bubble fusion", - description: "after destroying a mob's shield
spawn 1-2 heals, ammo, or research", + description: "after destroying a mob's natural shield
spawn 1-2 heals, ammo, or research", maxCount: 1, count: 0, allowed() { @@ -4307,7 +4336,7 @@ }, { name: "ship", - description: "experimental mode: fly around with no legs
aim by rotating with keyboard", + description: "experiment: fly around with no legs
aim by rotating with keyboard", maxCount: 1, count: 0, isNonRefundable: true, @@ -4324,7 +4353,7 @@ }, { name: "quantum leap", - description: "experimental mode: every 20 seconds
become an alternate version of yourself", + description: "experiment: every 20 seconds
become an alternate version of yourself", maxCount: 1, count: 0, isNonRefundable: true, @@ -5373,5 +5402,7 @@ isHarmReduceAfterKill: null, isSwitchReality: null, isResearchReality: null, - isAnthropicDamage: null + isAnthropicDamage: null, + isAnthropicHarm: null, + isAnthropicHarmImmune: null } \ No newline at end of file diff --git a/todo.txt b/todo.txt index e9ef8f7..289f6ce 100644 --- a/todo.txt +++ b/todo.txt @@ -1,14 +1,19 @@ ******************************************************** NEXT PATCH ******************************************************** -mob orbitals can now be destroyed, but it takes a very large amount of damage +shieldingBoss - doesn't attack, but shields all mobs every 2.5s -laser-bot upgrade: gives 75% damage, range, and energy efficiency (was 400% damage, but they ran out of energy too fast) -boom-bots are now smart about not hurting the player with explosions while doing the most damage +reworked how tetherBoss's constraint work + please, let me know if the tether boss is buggy on any of the levels + the chance for tether boss was removed from level: detours + +tech: flip-flop - collisions do 25% more harm, but you become immune to harm for the next collision -tech: strong anthropic principle - after anthropic principle prevents your death do 137.03599 extra damage for the rest of the level ******************************************************** BUGS ******************************************************** +shields being put in front of mob array messes up some types of index = mob.length-1 uses +adding orbitals puts a random # of mobs in front of target, and shield behind + use the floor of portal sensor on the player? to unstuck player (only once on my computer) once every 7 second check isn't running code @@ -35,9 +40,6 @@ use the floor of portal sensor on the player? to unstuck player ******************************************************** TODO ******************************************************** -tech: after using anthropic principle do 200% more damage for the rest of the level - - use ship tech to make a mob mode differences from ship to mob graphics @@ -58,12 +60,6 @@ bosses should have 2x health, but only do about 50 health damage tech: spawn a bot after taking collision damage -tech: standing wave freezes the mobs it hits - -tech: health becomes drones - requires mass-energy? - junk tech? - map: laboratory rooms with switches that change physics gravity room @@ -373,7 +369,7 @@ possible names for tech perturbation theory holonomy - parallel transport of a vector leads to movement (applies to curved space) hypergolic - A hypergolic propellant combination used in a rocket engine is one whose components spontaneously ignite when they come into contact with each other. - + uncertainty principle chapter 1: bot can hear audio and learns testing mode bot uses testing mode to exit room