From 5c466b4e0989b2599b74646299631ff96cb299a9 Mon Sep 17 00:00:00 2001 From: landgreen Date: Fri, 26 Feb 2021 05:10:25 -0800 Subject: [PATCH] shieldingBoss shieldingBoss - doesn't attack, but shields all mobs every 2.5s 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 --- .DS_Store | Bin 6148 -> 6148 bytes js/bullet.js | 12 ++-- js/engine.js | 15 ++++- js/level.js | 133 +++++++++++------------------------------ js/mob.js | 40 +++++++------ js/player.js | 12 ++-- js/powerup.js | 2 +- js/simulation.js | 2 +- js/spawn.js | 103 ++++++++++++++++++++++++++------ js/tech.js | 151 ++++++++++++++++++++++++++++------------------- todo.txt | 24 ++++---- 11 files changed, 270 insertions(+), 224 deletions(-) diff --git a/.DS_Store b/.DS_Store index 34a581f45448754239e06f6d165836684d372d86..1bffd0b0367b94ada224b070292da800077514e2 100644 GIT binary patch delta 22 dcmZoMXffEJ#mtnXJXwd?mr3Zr=4$3n5dcdZ27Ukl delta 22 dcmZoMXffEJ#mp2DKUs&_mr2-Qb2W3P2mne$1|a|d 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