From f8b18c7772f8964386cad61260e1a7ffc726d48f Mon Sep 17 00:00:00 2001 From: landgreen Date: Sun, 28 Nov 2021 05:28:01 -0800 Subject: [PATCH] affine connection tech: affine connection - wormholes can now tunnel through the map at 200% increased energy cost tech: regression - after bullets hit a mob, the mob takes 5% more future damage (0.5% for bosses) tech: axion - while inside the MACHO halo, 75% of your total harm reduction is added as damage mob: launcherOne - launches 1 big seeker bullet that chases you black holes boss and final boss spawn big seeker bullets that chases you during the black hole phase applied science no longer gives research (just a random tech for every gun you have) bot fabrication increase cost every 5 -> x6 bots average console time to disappear is 3 -> 4 seconds ammo power ups no longer log ammo to in game console for performance reasons JUNK tech: catabolysis - set max health to 1; double your current ammo 10 times (2^10 = 1024x ammo) --- .gitignore | 1 + js/engine.js | 7 +- js/level.js | 65 +- js/lore.js | 25 +- js/mob.js | 35 +- js/player.js | 3548 +++++---- js/powerup.js | 14 +- js/simulation.js | 2 +- js/spawn.js | 423 +- js/tech.js | 17741 +++++++++++++++++++++++---------------------- todo.txt | 54 +- 11 files changed, 11005 insertions(+), 10910 deletions(-) diff --git a/.gitignore b/.gitignore index e9de866..ef23d46 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ .DS_Store .DS_Store .DS_Store +js/workspace.code-workspace diff --git a/js/engine.js b/js/engine.js index 4591db2..2a0033f 100644 --- a/js/engine.js +++ b/js/engine.js @@ -186,6 +186,7 @@ function collisionChecks(event) { color: simulation.playerDmgColor, time: simulation.drawTime }); + if (tech.isLessDamageReduction && !mob[k].shield) mob[k].damageReduction *= mob[k].isBoss ? 1.005 : 1.05 return; } //mob + body collisions @@ -231,15 +232,15 @@ function collisionChecks(event) { } //determine if player is on the ground -Events.on(engine, "collisionStart", function(event) { +Events.on(engine, "collisionStart", function (event) { playerOnGroundCheck(event); // playerHeadCheck(event); if (m.alive) collisionChecks(event); }); -Events.on(engine, "collisionActive", function(event) { +Events.on(engine, "collisionActive", function (event) { playerOnGroundCheck(event); // playerHeadCheck(event); }); -Events.on(engine, "collisionEnd", function(event) { +Events.on(engine, "collisionEnd", function (event) { playerOffGroundCheck(event); }); \ No newline at end of file diff --git a/js/level.js b/js/level.js index e2485e0..a2b1f69 100644 --- a/js/level.js +++ b/js/level.js @@ -16,18 +16,17 @@ 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("molecular assembler") + // m.setField("wormhole") // b.giveGuns("laser") // b.giveGuns("nail gun") // b.giveGuns("harpoon") - // tech.giveTech("darts") - // tech.giveTech("incendiary ammunition") + // tech.giveTech("affine connection") + // tech.giveTech("regression") // tech.giveTech("relativistic momentum") // for (let i = 0; i < 2; i++) tech.giveTech("refractory metal") // tech.giveTech("antiscience") // for (let i = 0; i < 1; i++) tech.giveTech("reticulum") // for (let i = 0; i < 2; i++) tech.giveTech("laser-bot") - // tech.isCancelDuplication = true level.intro(); //starting level // level.testing(); //not in rotation, used for testing @@ -132,8 +131,8 @@ const level = { // for (let i = 0; i < 2; i++) powerUps.spawn(player.position.x + 90 * (Math.random() - 0.5), player.position.y + 90 * (Math.random() - 0.5), "tech", false); //start } }, - custom() {}, - customTopLayer() {}, + custom() { }, + customTopLayer() { }, setDifficulty() { simulation.difficulty = 0 b.dmgScale = 1; //damage done by player decreases each level @@ -575,7 +574,7 @@ const level = { body[body.length] = rotor1 body[body.length] = rotor2 - setTimeout(function() { + setTimeout(function () { rotor.collisionFilter.category = cat.body; rotor.collisionFilter.mask = cat.body | cat.player | cat.bullet | cat.mob | cat.mobBullet //| cat.map }, 1000); @@ -590,7 +589,7 @@ const level = { Composite.add(engine.world, constraint); if (rotate) { - rotor.rotate = function() { + rotor.rotate = function () { if (!m.isBodiesAsleep) { Matter.Body.applyForce(rotor, { x: rotor.position.x + 100, @@ -793,7 +792,7 @@ const level = { y: 0 }, angleB) - draw = function() { + draw = function () { ctx.beginPath(); //portal let v = this.vertices; ctx.moveTo(v[0].x, v[0].y); @@ -803,7 +802,7 @@ const level = { ctx.fillStyle = this.color ctx.fill(); } - query = function(isRemoveBlocks = false) { + query = function (isRemoveBlocks = false) { if (Matter.Query.collides(this, [player]).length === 0) { //not touching player if (player.isInPortal === this) player.isInPortal = null } else if (player.isInPortal !== this) { //touching player @@ -998,7 +997,7 @@ const level = { opticalQuery() { if (this.isOn) { //draw - ctx.fillStyle = `hsla(0, 100%, 50%,${0.6+0.4*Math.random()})` + ctx.fillStyle = `hsla(0, 100%, 50%,${0.6 + 0.4 * Math.random()})` ctx.fillRect(this.min.x, this.min.y, this.width, this.height) //collision with player if (this.height > 0 && Matter.Query.region([player], this).length && !(m.isCloak)) { @@ -1418,7 +1417,7 @@ const level = { button.isReadyToFire = true } else if (button.isReadyToFire && !button.isUp) { button.isReadyToFire = false - fireBlock = function(xPos, yPos) { + fireBlock = function (xPos, yPos) { const index = body.length spawn.bodyRect(xPos, yPos, 35 + 50 * Math.random(), 35 + 50 * Math.random()); const bodyBullet = body[body.length - 1] @@ -1474,7 +1473,7 @@ const level = { button.isReadyToFire = true } else if (button.isReadyToFire && !button.isUp) { button.isReadyToFire = false - fireBlock = function(xPos, yPos) { + fireBlock = function (xPos, yPos) { const index = body.length spawn.bodyRect(xPos, yPos, 35 + 50 * Math.random(), 35 + 50 * Math.random()); const bodyBullet = body[body.length - 1] @@ -1569,7 +1568,7 @@ const level = { y: -5 }); } - ctx.fillStyle = `rgba(255,0,255,${0.2+0.7*Math.random()})` + ctx.fillStyle = `rgba(255,0,255,${0.2 + 0.7 * Math.random()})` ctx.fillRect(bounds.min.x, y - 185, 38, 70); } } @@ -1636,7 +1635,7 @@ const level = { y: -5 }); } - ctx.fillStyle = `rgba(255,0,255,${0.2+0.7*Math.random()})` + ctx.fillStyle = `rgba(255,0,255,${0.2 + 0.7 * Math.random()})` ctx.fillRect(bounds.min.x, y - 185, 38, 70); } } @@ -1888,7 +1887,7 @@ const level = { Composite.add(engine.world, who); //add to world } let r = 150 - let hexagon = `${r} 0 ${r*Math.cos(5.236)} ${r*Math.sin(5.236)} ${r*Math.cos(4.189)} ${r*Math.sin(4.189)} ${-r} 0 ${r*Math.cos(2.0944)} ${r*Math.sin(2.0944)} ${r*Math.cos(1.0472)} ${r*Math.sin(1.0472)} ` + let hexagon = `${r} 0 ${r * Math.cos(5.236)} ${r * Math.sin(5.236)} ${r * Math.cos(4.189)} ${r * Math.sin(4.189)} ${-r} 0 ${r * Math.cos(2.0944)} ${r * Math.sin(2.0944)} ${r * Math.cos(1.0472)} ${r * Math.sin(1.0472)} ` //450 horizontal spread // -130-130-130 = 390 vertical if (Math.random() < 0.5) { spawn.mapVertex(x + 775, y + -260, hexagon); @@ -1977,13 +1976,13 @@ const level = { } //right side hexagons let r = 300 - let hexagon = `${r} 0 ${r*Math.cos(5.236)} ${r*Math.sin(5.236)} ${r*Math.cos(4.189)} ${r*Math.sin(4.189)} ${-r} 0 ${r*Math.cos(2.0944)} ${r*Math.sin(2.0944)} ${r*Math.cos(1.0472)} ${r*Math.sin(1.0472)} ` + let hexagon = `${r} 0 ${r * Math.cos(5.236)} ${r * Math.sin(5.236)} ${r * Math.cos(4.189)} ${r * Math.sin(4.189)} ${-r} 0 ${r * Math.cos(2.0944)} ${r * Math.sin(2.0944)} ${r * Math.cos(1.0472)} ${r * Math.sin(1.0472)} ` spawn.mapVertex(x + 1640, y + -365, hexagon); // r = 275 // let hexagonHalf = `${r} 0 ${r*Math.cos(5.236)} ${r*Math.sin(5.236)} ${r*Math.cos(4.189)} ${r*Math.sin(4.189)} ${-r} 0 ` // spawn.mapVertex(x + 2300, y + -75, hexagonHalf); r = 150 - const hexagon150 = `${r} 0 ${r*Math.cos(5.236)} ${r*Math.sin(5.236)} ${r*Math.cos(4.189)} ${r*Math.sin(4.189)} ${-r} 0 ${r*Math.cos(2.0944)} ${r*Math.sin(2.0944)} ${r*Math.cos(1.0472)} ${r*Math.sin(1.0472)} ` + const hexagon150 = `${r} 0 ${r * Math.cos(5.236)} ${r * Math.sin(5.236)} ${r * Math.cos(4.189)} ${r * Math.sin(4.189)} ${-r} 0 ${r * Math.cos(2.0944)} ${r * Math.sin(2.0944)} ${r * Math.cos(1.0472)} ${r * Math.sin(1.0472)} ` // spawn.mapVertex(x + 1750, y + -550, hexagon150); spawn.mapVertex(x + 1750, y + -1100, hexagon150); spawn.mapVertex(x + 1750, y + -1650, hexagon150); @@ -1991,7 +1990,7 @@ const level = { //left side r = 350 - let hexagonHalf = `${r} 0 ${r*Math.cos(5.236)} ${r*Math.sin(5.236)} ${r*Math.cos(4.189)} ${r*Math.sin(4.189)} ${-r} 0 ` + let hexagonHalf = `${r} 0 ${r * Math.cos(5.236)} ${r * Math.sin(5.236)} ${r * Math.cos(4.189)} ${r * Math.sin(4.189)} ${-r} 0 ` spawn.mapVertex(x + 425, y + -90, hexagonHalf); spawn.mapVertex(x + 850, y + -500, hexagon150); @@ -2338,10 +2337,10 @@ const level = { spawn.mapRect(4850, -275, 50, 175); //??? - level.difficultyIncrease(40) //30 is near max on hard //60 is near max on why - // spawn.starter(1900, -500, 200) //big boy + level.difficultyIncrease(30) //30 is near max on hard //60 is near max on why + spawn.starter(1900, -500, 200) //big boy - // spawn.spiderBoss(1700, -500) + // spawn.launcherOne(1700, -500) // spawn.launcherBoss(3200, -500) // spawn.laserTargetingBoss(1700, -500) // spawn.powerUpBoss(3200, -500) @@ -2371,7 +2370,7 @@ const level = { // spawn.blinkBoss(1600, -500) // spawn.laserTargetingBoss(1700, -120) // spawn.bomberBoss(1400, -500) - spawn.laser(1800, -320) + // spawn.laser(1800, -320) // spawn.laserBombingBoss(1600, -500) // spawn.laserTargetingBoss(1600, -500) // spawn.laserBoss(1600, -500) @@ -2389,7 +2388,7 @@ const level = { level.exit.draw(); level.enter.draw(); }; - level.customTopLayer = () => {}; + level.customTopLayer = () => { }; level.setPosToSpawn(0, -50); //normal spawn level.exit.x = 1500; level.exit.y = -1875; @@ -5519,7 +5518,7 @@ const level = { if (mob[i].isBoss) me = mob[i] } if (me) { - me.onDeath = function() { //please don't edit the onDeath function this causes serious bugs + me.onDeath = function () { //please don't edit the onDeath function this causes serious bugs spawnCouloirEnHaut() doorSortieSalle.isOpen = false; }; @@ -5535,7 +5534,7 @@ const level = { if (mob[i].isBoss) me = mob[i] } if (me) { - me.onDeath = function() { //please don't edit the onDeath function this causes serious bugs + me.onDeath = function () { //please don't edit the onDeath function this causes serious bugs spawnCouloirEnHaut() doorSortieSalle.isOpen = false; }; @@ -5760,11 +5759,11 @@ const level = { body[body.length] = part4; body[body.length] = part5; body[body.length] = part6; - setTimeout(function() { + setTimeout(function () { chair.collisionFilter.category = cat.body; chair.collisionFilter.mask = cat.body | cat.player | cat.bullet | cat.mob | cat.mobBullet | cat.map }, 1000); - setTimeout(function() { + setTimeout(function () { chair2.collisionFilter.category = cat.body; chair2.collisionFilter.mask = cat.body | cat.player | cat.bullet | cat.mob | cat.mobBullet | cat.map }, 1000); @@ -5819,7 +5818,7 @@ const level = { body[body.length] = rightUpperLeg body[body.length] = rightLowerArm body[body.length] = rightUpperArm - setTimeout(function() { + setTimeout(function () { person.collisionFilter.category = cat.body; person.collisionFilter.mask = cat.body | cat.player | cat.bullet | cat.mob | cat.mobBullet | cat.map }, 1000); @@ -6211,7 +6210,7 @@ const level = { level.exit.draw(); level.enter.draw(); }; - level.customTopLayer = () => {}; + level.customTopLayer = () => { }; level.defaultZoom = 1800 simulation.zoomTransition(level.defaultZoom) document.body.style.backgroundColor = "#dcdcde"; @@ -7270,7 +7269,7 @@ const level = { body[body.length] = part1; body[body.length] = part2; body[body.length] = part3; - setTimeout(function() { + setTimeout(function () { compoundParts.collisionFilter.category = cat.body; compoundParts.collisionFilter.mask = cat.body | cat.player | cat.bullet | cat.mob | cat.mobBullet | cat.map }, 1000); @@ -7389,8 +7388,8 @@ const level = { // prevent the user from getting into the secreter room without defeating all mobs if (m.pos.x > 1500 && m.pos.x < 2500 && m.pos.y > -4000 && m.pos.y < -3500 && mob.reduce((a, i) => { - return a || ((Math.sqrt((i.position.x - 3600) * (i.position.x - 3600) + (i.position.y + 3600) * (i.position.y + 3600)) < 20000) && i.isDropPowerUp); - }, false) && !emergencyActivated) { + return a || ((Math.sqrt((i.position.x - 3600) * (i.position.x - 3600) + (i.position.y + 3600) * (i.position.y + 3600)) < 20000) && i.isDropPowerUp); + }, false) && !emergencyActivated) { Matter.Body.setPosition(player, { x: 2800, y: m.pos.y diff --git a/js/lore.js b/js/lore.js index 6c0eca0..4d3e28d 100644 --- a/js/lore.js +++ b/js/lore.js @@ -11,7 +11,6 @@ const lore = { } else if (simulation.difficultyMode === 6) { this.techGoal = 1 } - }, talkingColor: "#dff", //set color of graphic on level.null isSpeech: false, @@ -42,6 +41,17 @@ const lore = { lore.conversation[lore.chapter][lore.sentence]() //go to next sentence in the chapter and play it } }, + unlockTesting() { + if (localSettings.loreCount < 1) localSettings.loreCount = 1 + localStorage.setItem("localSettings", JSON.stringify(localSettings)); //update local storage + document.getElementById("control-testing").style.visibility = (localSettings.loreCount === 0) ? "hidden" : "visible" + document.getElementById("experiment-button").style.visibility = (localSettings.loreCount === 0) ? "hidden" : "visible" + simulation.makeTextLog(`lore.unlockTesting()`, Infinity); + + sound.portamento(50) + sound.portamento(83.333) + sound.portamento(166.666) + }, anand: { color: "#e0c", voice: undefined, @@ -490,19 +500,6 @@ const lore = { // () => { lore.miriam.text("And that is why you keep running these fighting simulations.") }, // () => { lore.miriam.text("You haven't been researching new technology.") }, // () => { lore.miriam.text("You've are planning how to escape.") }, - - - unlockTesting() { - if (localSettings.loreCount < 1) localSettings.loreCount = 1 - localStorage.setItem("localSettings", JSON.stringify(localSettings)); //update local storage - document.getElementById("control-testing").style.visibility = (localSettings.loreCount === 0) ? "hidden" : "visible" - document.getElementById("experiment-button").style.visibility = (localSettings.loreCount === 0) ? "hidden" : "visible" - simulation.makeTextLog(`lore.unlockTesting()`, Infinity); - - sound.portamento(50) - sound.portamento(83.333) - sound.portamento(166.666) - }, } diff --git a/js/mob.js b/js/mob.js index 1b1c331..bcf8dd6 100644 --- a/js/mob.js +++ b/js/mob.js @@ -128,7 +128,7 @@ const mobs = { const y = who.position.y - w * 0.7; ctx.fillStyle = "rgba(100, 100, 100, 0.3)"; ctx.fillRect(x, y, w, h); - ctx.fillStyle = `rgba(${Math.floor(255*Math.random())},${Math.floor(255*Math.random())},${Math.floor(255*Math.random())},0.5)` + ctx.fillStyle = `rgba(${Math.floor(255 * Math.random())},${Math.floor(255 * Math.random())},${Math.floor(255 * Math.random())},0.5)` ctx.fillRect(x, y, w * who.health, h); //draw fill inside mob @@ -165,7 +165,7 @@ const mobs = { }); } }, - endEffect() {}, + endEffect() { }, dmg: tickDamage, type: "dot", endCycle: simulation.cycle + cycles, @@ -472,7 +472,7 @@ const mobs = { } }, laser() { - const vertexCollision = function(v1, v1End, domain) { + const vertexCollision = function (v1, v1End, domain) { for (let i = 0; i < domain.length; ++i) { let vertices = domain[i].vertices; const len = vertices.length - 1; @@ -609,7 +609,7 @@ const mobs = { ctx.fillStyle = "rgba(0,0,0,0.07)"; ctx.fill(); //spring to random place on map - const vertexCollision = function(v1, v1End, domain) { + const vertexCollision = function (v1, v1End, domain) { for (let i = 0; i < domain.length; ++i) { let vertices = domain[i].vertices; const len = vertices.length - 1; @@ -676,7 +676,7 @@ const mobs = { }, curl(range = 1000, mag = -10) { //cause all mobs, and bodies to rotate in a circle - applyCurl = function(center, array, isAntiGravity = true) { + applyCurl = function (center, array, isAntiGravity = true) { for (let i = 0; i < array.length; ++i) { if (!array[i].isNotHoldable) { const sub = Vector.sub(center, array[i].position) @@ -686,10 +686,17 @@ const mobs = { 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 (array[i].isMobBullet) { + Matter.Body.setVelocity(array[i], { + x: array[i].velocity.x * 0.97 + curlVector.x * 0.06, + y: array[i].velocity.y * 0.97 + curlVector.y * 0.06 + }) + } else { + 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(); @@ -818,7 +825,7 @@ const mobs = { //be sure to declare searchTarget in mob spawn //accelerate towards the searchTarget if (!this.seePlayer.recall) { - const newTarget = function(that) { + const newTarget = function (that) { if (Math.random() < 0.0005) { that.searchTarget = player.position; //chance to target player } else { @@ -1221,7 +1228,7 @@ const mobs = { for (let i = 0, len = consBB.length; i < len; ++i) { if (consBB[i].bodyA === this) { if (consBB[i].bodyB.shield) { - consBB[i].bodyB.do = function() { + consBB[i].bodyB.do = function () { this.death(); }; } @@ -1231,7 +1238,7 @@ const mobs = { break; } else if (consBB[i].bodyB === this) { if (consBB[i].bodyA.shield) { - consBB[i].bodyA.do = function() { + consBB[i].bodyA.do = function () { this.death(); }; } @@ -1290,7 +1297,7 @@ const mobs = { //large mobs shrink so they don't block paths if (body[len].mass + body[len2].mass > 16) { const massLimit = 8 + 6 * Math.random() - const shrink = function(that1, that2) { + const shrink = function (that1, that2) { if (that1.mass + that2.mass > massLimit) { const scale = 0.95; Matter.Body.scale(that1, scale, scale); @@ -1313,7 +1320,7 @@ const mobs = { //large mobs shrink so they don't block paths if (body[len].mass > 9) { const massLimit = 7 + 4 * Math.random() - const shrink = function(that) { + const shrink = function (that) { if (that.mass > massLimit) { const scale = 0.95; Matter.Body.scale(that, scale, scale); diff --git a/js/player.js b/js/player.js index 160dc52..3dc5255 100644 --- a/js/player.js +++ b/js/player.js @@ -57,7 +57,7 @@ const m = { }, setFillColors() { this.fillColor = `hsl(${m.color.hue},${m.color.sat}%,${m.color.light}%)` - this.fillColorDark = `hsl(${m.color.hue},${m.color.sat}%,${m.color.light-25}%)` + this.fillColorDark = `hsl(${m.color.hue},${m.color.sat}%,${m.color.light - 25}%)` let grd = ctx.createLinearGradient(-30, 0, 30, 0); grd.addColorStop(0, m.fillColorDark); grd.addColorStop(1, m.fillColor); @@ -183,7 +183,7 @@ const m = { lastGroundedPositionY: 0, // mouseZoom: 0, lookSmoothing: 0.07, //1 is instant jerky, 0.001 is slow smooth zoom, 0.07 is standard - look() {}, //set to lookDefault() + look() { }, //set to lookDefault() lookDefault() { //always on mouse look m.angle = Math.atan2( @@ -405,7 +405,7 @@ const m = { m.health = 1; // m.addHealth(1) - simulation.wipe = function() { //set wipe to have trails + simulation.wipe = function () { //set wipe to have trails ctx.fillStyle = "rgba(255,255,255,0)"; ctx.fillRect(0, 0, canvas.width, canvas.height); } @@ -415,8 +415,8 @@ const m = { m.switchWorlds() const swapPeriod = 1000 for (let i = 0, len = 5; i < len; i++) { - setTimeout(function() { - simulation.wipe = function() { //set wipe to have trails + setTimeout(function () { + simulation.wipe = function () { //set wipe to have trails ctx.fillStyle = "rgba(255,255,255,0)"; ctx.fillRect(0, 0, canvas.width, canvas.height); } @@ -424,16 +424,16 @@ const m = { simulation.clearNow = true; //triggers a map reset m.switchWorlds() simulation.isTextLogOpen = true; - simulation.makeTextLog(`simulation.amplitude = 0.${len-i-1}`, swapPeriod); + simulation.makeTextLog(`simulation.amplitude = 0.${len - i - 1}`, swapPeriod); simulation.isTextLogOpen = false; - simulation.wipe = function() { //set wipe to have trails - ctx.fillStyle = `rgba(255,255,255,${(i+1)*(i+1)*0.006})`; + simulation.wipe = function () { //set wipe to have trails + ctx.fillStyle = `rgba(255,255,255,${(i + 1) * (i + 1) * 0.006})`; ctx.fillRect(0, 0, canvas.width, canvas.height); } }, (i + 1) * swapPeriod); } - setTimeout(function() { - simulation.wipe = function() { //set wipe to normal + setTimeout(function () { + simulation.wipe = function () { //set wipe to normal ctx.clearRect(0, 0, canvas.width, canvas.height); } simulation.isTextLogOpen = true; @@ -448,7 +448,7 @@ const m = { document.getElementById("text-log").style.opacity = 0; //fade out any active text logs document.getElementById("fade-out").style.opacity = 0.9; //slowly fade to 90% white on top of canvas // build.shareURL(false) - setTimeout(function() { + setTimeout(function () { Composite.clear(engine.world); Engine.clear(engine); simulation.splashReturn(); @@ -491,7 +491,7 @@ const m = { baseHealth: 1, setMaxHealth() { m.maxHealth = m.baseHealth + tech.extraMaxHealth + tech.isFallingDamage //+ tech.bonusHealth - document.getElementById("health-bg").style.width = `${Math.floor(300*m.maxHealth)}px` + document.getElementById("health-bg").style.width = `${Math.floor(300 * m.maxHealth)}px` simulation.makeTextLog(`m.maxHealth = ${m.maxHealth.toFixed(2)}`) if (m.health > m.maxHealth) m.health = m.maxHealth; m.displayHealth(); @@ -588,7 +588,7 @@ const m = { if (m.immuneCycle < m.cycle + tech.collisionImmuneCycles) m.immuneCycle = m.cycle + tech.collisionImmuneCycles; //player is immune to damage for 30 cycles let isDrawPlayer = true - const shortPause = function() { + const shortPause = function () { if (m.defaultFPSCycle < m.cycle) { //back to default values simulation.fpsCap = simulation.fpsCapDefault simulation.fpsInterval = 1000 / simulation.fpsCap; @@ -657,13 +657,13 @@ const m = { for (let i = 0; i < 5; i++) powerUps.spawn(m.pos.x + 100 * (Math.random() - 0.5), m.pos.y + 100 * (Math.random() - 0.5), "heal", false); m.energy = m.maxEnergy if (m.immuneCycle < m.cycle + 300) m.immuneCycle = m.cycle + 300 //disable this.immuneCycle bonus seconds - simulation.wipe = function() { //set wipe to have trails + simulation.wipe = function () { //set wipe to have trails ctx.fillStyle = "rgba(255,255,255,0.03)"; ctx.fillRect(0, 0, canvas.width, canvas.height); } - setTimeout(function() { + setTimeout(function () { tech.maxDuplicationEvent() - simulation.wipe = function() { //set wipe to normal + simulation.wipe = function () { //set wipe to normal ctx.clearRect(0, 0, canvas.width, canvas.height); } }, 3000); @@ -686,13 +686,13 @@ const m = {
${powerUps.research.count}`) for (let i = 0; i < 5; i++) powerUps.spawn(m.pos.x + 100 * (Math.random() - 0.5), m.pos.y + 100 * (Math.random() - 0.5), "heal", false); if (m.immuneCycle < m.cycle + 300) m.immuneCycle = m.cycle + 300 //disable this.immuneCycle bonus seconds - simulation.wipe = function() { //set wipe to have trails + simulation.wipe = function () { //set wipe to have trails ctx.fillStyle = "rgba(255,255,255,0.03)"; ctx.fillRect(0, 0, canvas.width, canvas.height); } - setTimeout(function() { + setTimeout(function () { tech.maxDuplicationEvent() - simulation.wipe = function() { //set wipe to normal + simulation.wipe = function () { //set wipe to normal ctx.clearRect(0, 0, canvas.width, canvas.height); } }, 3000); @@ -709,7 +709,7 @@ const m = { } if (dmg > 0.06 / m.holdingMassScale) m.drop(); //drop block if holding - const normalFPS = function() { + const normalFPS = function () { if (m.defaultFPSCycle < m.cycle) { //back to default values simulation.fpsCap = simulation.fpsCapDefault simulation.fpsInterval = 1000 / simulation.fpsCap; @@ -808,38 +808,7 @@ const m = { m.knee.x = (l / d) * (m.foot.x - m.hip.x) - (h / d) * (m.foot.y - m.hip.y) + m.hip.x + offset; m.knee.y = (l / d) * (m.foot.y - m.hip.y) + (h / d) * (m.foot.x - m.hip.x) + m.hip.y; }, - draw() { - ctx.fillStyle = m.fillColor; - m.walk_cycle += m.flipLegs * m.Vx; - - //draw body - ctx.save(); - ctx.globalAlpha = (m.immuneCycle < m.cycle) ? 1 : 0.5 - ctx.translate(m.pos.x, m.pos.y); - - m.calcLeg(Math.PI, -3); - m.drawLeg("#4a4a4a"); - m.calcLeg(0, 0); - m.drawLeg("#333"); - - ctx.rotate(m.angle); - ctx.beginPath(); - ctx.arc(0, 0, 30, 0, 2 * Math.PI); - ctx.fillStyle = this.bodyGradient; - ctx.fill(); - ctx.arc(15, 0, 4, 0, 2 * Math.PI); - ctx.strokeStyle = "#333"; - ctx.lineWidth = 2; - ctx.stroke(); - // draw eye; used in flip-flop - // ctx.beginPath(); - // ctx.arc(15, 0, 3.5, 0, 2 * Math.PI); - // ctx.fillStyle = m.eyeFillColor; - // ctx.fill() - - ctx.restore(); - m.yOff = m.yOff * 0.85 + m.yOffGoal * 0.15; //smoothly move leg height towards height goal - }, + draw() { }, drawFlipFlop() { ctx.fillStyle = m.fillColor; m.walk_cycle += m.flipLegs * m.Vx; @@ -875,17 +844,13 @@ const m = { drawDefault() { ctx.fillStyle = m.fillColor; m.walk_cycle += m.flipLegs * m.Vx; - - //draw body ctx.save(); ctx.globalAlpha = (m.immuneCycle < m.cycle) ? 1 : 0.5 ctx.translate(m.pos.x, m.pos.y); - m.calcLeg(Math.PI, -3); m.drawLeg("#4a4a4a"); m.calcLeg(0, 0); m.drawLeg("#333"); - ctx.rotate(m.angle); ctx.beginPath(); ctx.arc(0, 0, 30, 0, 2 * Math.PI); @@ -895,12 +860,6 @@ const m = { ctx.strokeStyle = "#333"; ctx.lineWidth = 2; ctx.stroke(); - // draw eye; used in flip-flop - // ctx.beginPath(); - // ctx.arc(15, 0, 3.5, 0, 2 * Math.PI); - // ctx.fillStyle = m.eyeFillColor; - // ctx.fill() - ctx.restore(); m.yOff = m.yOff * 0.85 + m.yOffGoal * 0.15; //smoothly move leg height towards height goal }, @@ -1022,7 +981,7 @@ const m = { ctx.fillRect(xOff, yOff, range * m.energy, 10); } }, - drawFieldMeterCloaking: function() { + drawFieldMeterCloaking: function () { if (m.energy < m.maxEnergy) { // replaces m.drawFieldMeter() with custom code m.regenEnergy(); const xOff = m.pos.x - m.radius * m.maxEnergy @@ -1038,11 +997,11 @@ const m = { ctx.stroke(); } }, - regenEnergy: function() { //used in drawFieldMeter // rewritten by some tech + regenEnergy: function () { //used in drawFieldMeter // rewritten by some tech if (m.immuneCycle < m.cycle) m.energy += m.fieldRegen; if (m.energy < 0) m.energy = 0 }, - regenEnergyDefault: function() { + regenEnergyDefault: function () { if (m.immuneCycle < m.cycle) m.energy += m.fieldRegen; if (m.energy < 0) m.energy = 0 }, @@ -1230,7 +1189,7 @@ const m = { m.holdingTarget.friction = m.holdingTarget.frictionStatic = m.holdingTarget.frictionAir = 0.001 } //check every second to see if player is away from thrown body, and make solid - const solid = function(that) { + const solid = function (that) { const dx = that.position.x - player.position.x; const dy = that.position.y - player.position.y; if (that.speed < 3 && dx * dx + dy * dy > 10000 && that !== m.holdingTarget) { @@ -1262,7 +1221,7 @@ const m = { m.definePlayerMass() //return to normal player mass if (tech.isAddBlockMass) { - const expand = function(that, massLimit) { + const expand = function (that, massLimit) { if (that.mass < massLimit) { const scale = 1.05; Matter.Body.scale(that, scale, scale); @@ -1507,7 +1466,7 @@ const m = { // wake(powerUp); } }, - hold() {}, + hold() { }, setField(index) { if (isNaN(index)) { //find index by name let found = false @@ -1527,1534 +1486,1501 @@ const m = { simulation.makeTextLog(`m.setField("${m.fieldUpgrades[m.fieldMode].name}")`); }, fieldUpgrades: [{ - name: "field emitter", - description: "regen 6 energy per second
use it to deflect mobs and throw blocks
energy regen disabled if immune to harm", - // description: "use energy to deflect mobs,
grab power ups, and throw blocks
regen 6 energy/s, when not immune to harm", - effect: () => { - m.hold = function() { - if (m.isHolding) { - m.drawHold(m.holdingTarget); - m.holding(); - m.throwBlock(); - } else if ((input.field && m.fieldCDcycle < m.cycle)) { //not hold but field button is pressed - m.grabPowerUp(); - m.lookForPickUp(); - if (m.energy > 0.05) { - m.drawField(); - m.pushMobsFacing(); - } - } else if (m.holdingTarget && m.fieldCDcycle < m.cycle) { //holding, but field button is released - m.pickUp(); - } else { - m.holdingTarget = null; //clears holding target (this is so you only pick up right after the field button is released and a hold target exists) + name: "field emitter", + description: "regen 6 energy per second
use it to deflect mobs and throw blocks
energy regen disabled if immune to harm", + // description: "use energy to deflect mobs,
grab power ups, and throw blocks
regen 6 energy/s, when not immune to harm", + effect: () => { + m.hold = function () { + if (m.isHolding) { + m.drawHold(m.holdingTarget); + m.holding(); + m.throwBlock(); + } else if ((input.field && m.fieldCDcycle < m.cycle)) { //not hold but field button is pressed + m.grabPowerUp(); + m.lookForPickUp(); + if (m.energy > 0.05) { + m.drawField(); + m.pushMobsFacing(); } - m.drawFieldMeter() - } - } - }, - { - name: "standing wave", - description: "3 oscillating shields are permanently active
deflecting protects you in every direction
deflecting has 50% less recoil", //drains energy - drainCD: 0, - effect: () => { - m.fieldBlockCD = 0; - m.blockingRecoil = 2 //4 is normal - m.fieldRange = 175 - m.fieldShieldingScale = 1.3 * Math.pow(0.6, (tech.harmonics - 2)) - - m.harmonic3Phase = () => { //normal standard 3 different 2-d circles - const fieldRange1 = (0.7 + 0.3 * Math.sin(m.cycle / 23)) * m.fieldRange * m.harmonicRadius - const fieldRange2 = (0.63 + 0.37 * Math.sin(m.cycle / 37)) * m.fieldRange * m.harmonicRadius - const fieldRange3 = (0.65 + 0.35 * Math.sin(m.cycle / 47)) * m.fieldRange * m.harmonicRadius - const netfieldRange = Math.max(fieldRange1, fieldRange2, fieldRange3) - ctx.fillStyle = "rgba(110,170,200," + Math.min(0.73, (0.04 + m.energy * (0.11 + 0.13 * Math.random()))) + ")"; - ctx.beginPath(); - ctx.arc(m.pos.x, m.pos.y, fieldRange1, 0, 2 * Math.PI); - ctx.fill(); - ctx.beginPath(); - ctx.arc(m.pos.x, m.pos.y, fieldRange2, 0, 2 * Math.PI); - ctx.fill(); - ctx.beginPath(); - ctx.arc(m.pos.x, m.pos.y, fieldRange3, 0, 2 * Math.PI); - ctx.fill(); - //360 block - for (let i = 0, len = mob.length; i < len; ++i) { - if (Vector.magnitude(Vector.sub(mob[i].position, m.pos)) - mob[i].radius < netfieldRange && !mob[i].isUnblockable) { // && Matter.Query.ray(map, mob[i].position, m.pos).length === 0 - mob[i].locatePlayer(); - if (this.drainCD > m.cycle) { - m.pushMass(mob[i], 0); - } else { - m.pushMass(mob[i]); - this.drainCD = m.cycle + 10 - } - if (mob[i].isShielded) m.fieldCDcycle = m.cycle + 45 - } - } - } - m.harmonicRadius = 1 //for smoothing function when player holds mouse (for harmonicAtomic) - m.harmonicAtomic = () => { //several ellipses spinning about different axises - const rotation = simulation.cycle * 0.0031 - const phase = simulation.cycle * 0.023 - const radius = m.fieldRange * m.harmonicRadius - ctx.lineWidth = 1; - ctx.strokeStyle = "rgba(110,170,200,0.8)" - ctx.fillStyle = "rgba(110,170,200," + Math.min(0.7, m.energy * (0.13 + 0.1 * Math.random()) * (3 / tech.harmonics)) + ")"; - // ctx.fillStyle = "rgba(110,170,200," + Math.min(0.7, m.energy * (0.22 - 0.01 * tech.harmonics) * (0.5 + 0.5 * Math.random())) + ")"; - for (let i = 0; i < tech.harmonics; i++) { - ctx.beginPath(); - ctx.ellipse(m.pos.x, m.pos.y, radius * Math.abs(Math.sin(phase + i / tech.harmonics * Math.PI)), radius, rotation + i / tech.harmonics * Math.PI, 0, 2 * Math.PI); - ctx.fill(); - ctx.stroke(); - } - //360 block - for (let i = 0, len = mob.length; i < len; ++i) { - if (Vector.magnitude(Vector.sub(mob[i].position, m.pos)) - mob[i].radius < radius && !mob[i].isUnblockable) { // && Matter.Query.ray(map, mob[i].position, m.pos).length === 0 - mob[i].locatePlayer(); - if (this.drainCD > m.cycle) { - m.pushMass(mob[i], 0); - } else { - m.pushMass(mob[i]); - this.drainCD = m.cycle + 10 - } - if (mob[i].isShielded) m.fieldCDcycle = m.cycle + 45 - } - } - } - if (tech.harmonics === 2) { - m.harmonicShield = m.harmonic3Phase + } else if (m.holdingTarget && m.fieldCDcycle < m.cycle) { //holding, but field button is released + m.pickUp(); } else { - m.harmonicShield = m.harmonicAtomic + m.holdingTarget = null; //clears holding target (this is so you only pick up right after the field button is released and a hold target exists) } - m.hold = function() { - if (m.isHolding) { - m.drawHold(m.holdingTarget); - m.holding(); - m.throwBlock(); - } else if ((input.field) && m.fieldCDcycle < m.cycle) { //not hold but field button is pressed - m.grabPowerUp(); - m.lookForPickUp(); - } else if (m.holdingTarget && m.fieldCDcycle < m.cycle) { //holding, but field button is released - m.pickUp(); - } else { - m.holdingTarget = null; //clears holding target (this is so you only pick up right after the field button is released and a hold target exists) - } - if (m.energy > 0.1 && m.fieldCDcycle < m.cycle) { - if (tech.isStandingWaveExpand) { - if (input.field) { - // const oldHarmonicRadius = m.harmonicRadius - m.harmonicRadius = 0.985 * m.harmonicRadius + 0.015 * 2.5 - // m.energy -= 0.1 * (m.harmonicRadius - oldHarmonicRadius) - } else { - m.harmonicRadius = 0.995 * m.harmonicRadius + 0.005 - } + m.drawFieldMeter() + } + } + }, + { + name: "standing wave", + description: "3 oscillating shields are permanently active
deflecting protects you in every direction
deflecting has 50% less recoil", //drains energy + drainCD: 0, + effect: () => { + m.fieldBlockCD = 0; + m.blockingRecoil = 2 //4 is normal + m.fieldRange = 175 + m.fieldShieldingScale = 1.3 * Math.pow(0.6, (tech.harmonics - 2)) + + m.harmonic3Phase = () => { //normal standard 3 different 2-d circles + const fieldRange1 = (0.7 + 0.3 * Math.sin(m.cycle / 23)) * m.fieldRange * m.harmonicRadius + const fieldRange2 = (0.63 + 0.37 * Math.sin(m.cycle / 37)) * m.fieldRange * m.harmonicRadius + const fieldRange3 = (0.65 + 0.35 * Math.sin(m.cycle / 47)) * m.fieldRange * m.harmonicRadius + const netfieldRange = Math.max(fieldRange1, fieldRange2, fieldRange3) + ctx.fillStyle = "rgba(110,170,200," + Math.min(0.73, (0.04 + m.energy * (0.11 + 0.13 * Math.random()))) + ")"; + ctx.beginPath(); + ctx.arc(m.pos.x, m.pos.y, fieldRange1, 0, 2 * Math.PI); + ctx.fill(); + ctx.beginPath(); + ctx.arc(m.pos.x, m.pos.y, fieldRange2, 0, 2 * Math.PI); + ctx.fill(); + ctx.beginPath(); + ctx.arc(m.pos.x, m.pos.y, fieldRange3, 0, 2 * Math.PI); + ctx.fill(); + //360 block + for (let i = 0, len = mob.length; i < len; ++i) { + if (Vector.magnitude(Vector.sub(mob[i].position, m.pos)) - mob[i].radius < netfieldRange && !mob[i].isUnblockable) { // && Matter.Query.ray(map, mob[i].position, m.pos).length === 0 + mob[i].locatePlayer(); + if (this.drainCD > m.cycle) { + m.pushMass(mob[i], 0); + } else { + m.pushMass(mob[i]); + this.drainCD = m.cycle + 10 } - m.harmonicShield() + if (mob[i].isShielded) m.fieldCDcycle = m.cycle + 45 } - m.drawFieldMeter() } } - }, - { - name: "perfect diamagnetism", - description: "attract power ups from far away
deflecting does not drain energy
maintains functionality while inactive", - // description: "attract power ups from far away
deflecting doesn't drain energy
thrown blocks have", - // description: "gain energy when blocking
no recoil when blocking", - effect: () => { - m.fieldShieldingScale = 0; - m.fieldBlockCD = 3; - m.grabPowerUpRange2 = 10000000 - m.fieldPosition = { x: m.pos.x, y: m.pos.y } - m.fieldAngle = m.angle - m.perfectPush = (isFree = false) => { - if (m.fieldCDcycle < m.cycle) { - for (let i = 0, len = mob.length; i < len; ++i) { - if ( - Vector.magnitude(Vector.sub(mob[i].position, m.fieldPosition)) - mob[i].radius < m.fieldRange && - !mob[i].isUnblockable && - Vector.dot({ x: Math.cos(m.fieldAngle), y: Math.sin(m.fieldAngle) }, Vector.normalise(Vector.sub(mob[i].position, m.fieldPosition))) > m.fieldThreshold && - Matter.Query.ray(map, mob[i].position, m.fieldPosition).length === 0 - ) { - mob[i].locatePlayer(); - const unit = Vector.normalise(Vector.sub(m.fieldPosition, mob[i].position)) - m.fieldCDcycle = m.cycle + m.fieldBlockCD + (mob[i].isShielded ? 15 : 0); - if (tech.blockingIce) { - for (let i = 0; i < 2 * tech.blockingIce; i++) { - const angle = m.fieldAngle + 1.55 * (Math.random() - 0.5) - b.iceIX(10, angle, Vector.add(m.fieldPosition, { x: m.fieldRange * Math.cos(angle), y: m.fieldRange * Math.sin(angle) })) + m.harmonicRadius = 1 //for smoothing function when player holds mouse (for harmonicAtomic) + m.harmonicAtomic = () => { //several ellipses spinning about different axises + const rotation = simulation.cycle * 0.0031 + const phase = simulation.cycle * 0.023 + const radius = m.fieldRange * m.harmonicRadius + ctx.lineWidth = 1; + ctx.strokeStyle = "rgba(110,170,200,0.8)" + ctx.fillStyle = "rgba(110,170,200," + Math.min(0.7, m.energy * (0.13 + 0.1 * Math.random()) * (3 / tech.harmonics)) + ")"; + // ctx.fillStyle = "rgba(110,170,200," + Math.min(0.7, m.energy * (0.22 - 0.01 * tech.harmonics) * (0.5 + 0.5 * Math.random())) + ")"; + for (let i = 0; i < tech.harmonics; i++) { + ctx.beginPath(); + ctx.ellipse(m.pos.x, m.pos.y, radius * Math.abs(Math.sin(phase + i / tech.harmonics * Math.PI)), radius, rotation + i / tech.harmonics * Math.PI, 0, 2 * Math.PI); + ctx.fill(); + ctx.stroke(); + } + //360 block + for (let i = 0, len = mob.length; i < len; ++i) { + if (Vector.magnitude(Vector.sub(mob[i].position, m.pos)) - mob[i].radius < radius && !mob[i].isUnblockable) { // && Matter.Query.ray(map, mob[i].position, m.pos).length === 0 + mob[i].locatePlayer(); + if (this.drainCD > m.cycle) { + m.pushMass(mob[i], 0); + } else { + m.pushMass(mob[i]); + this.drainCD = m.cycle + 10 + } + if (mob[i].isShielded) m.fieldCDcycle = m.cycle + 45 + } + } + } + if (tech.harmonics === 2) { + m.harmonicShield = m.harmonic3Phase + } else { + m.harmonicShield = m.harmonicAtomic + } + m.hold = function () { + if (m.isHolding) { + m.drawHold(m.holdingTarget); + m.holding(); + m.throwBlock(); + } else if ((input.field) && m.fieldCDcycle < m.cycle) { //not hold but field button is pressed + m.grabPowerUp(); + m.lookForPickUp(); + } else if (m.holdingTarget && m.fieldCDcycle < m.cycle) { //holding, but field button is released + m.pickUp(); + } else { + m.holdingTarget = null; //clears holding target (this is so you only pick up right after the field button is released and a hold target exists) + } + if (m.energy > 0.1 && m.fieldCDcycle < m.cycle) { + if (tech.isStandingWaveExpand) { + if (input.field) { + // const oldHarmonicRadius = m.harmonicRadius + m.harmonicRadius = 0.985 * m.harmonicRadius + 0.015 * 2.5 + // m.energy -= 0.1 * (m.harmonicRadius - oldHarmonicRadius) + } else { + m.harmonicRadius = 0.995 * m.harmonicRadius + 0.005 + } + } + m.harmonicShield() + } + m.drawFieldMeter() + } + } + }, + { + name: "perfect diamagnetism", + description: "attract power ups from far away
deflecting does not drain energy
maintains functionality while inactive", + // description: "attract power ups from far away
deflecting doesn't drain energy
thrown blocks have", + // description: "gain energy when blocking
no recoil when blocking", + effect: () => { + m.fieldShieldingScale = 0; + m.fieldBlockCD = 3; + m.grabPowerUpRange2 = 10000000 + m.fieldPosition = { x: m.pos.x, y: m.pos.y } + m.fieldAngle = m.angle + m.perfectPush = (isFree = false) => { + if (m.fieldCDcycle < m.cycle) { + for (let i = 0, len = mob.length; i < len; ++i) { + if ( + Vector.magnitude(Vector.sub(mob[i].position, m.fieldPosition)) - mob[i].radius < m.fieldRange && + !mob[i].isUnblockable && + Vector.dot({ x: Math.cos(m.fieldAngle), y: Math.sin(m.fieldAngle) }, Vector.normalise(Vector.sub(mob[i].position, m.fieldPosition))) > m.fieldThreshold && + Matter.Query.ray(map, mob[i].position, m.fieldPosition).length === 0 + ) { + mob[i].locatePlayer(); + const unit = Vector.normalise(Vector.sub(m.fieldPosition, mob[i].position)) + m.fieldCDcycle = m.cycle + m.fieldBlockCD + (mob[i].isShielded ? 15 : 0); + if (tech.blockingIce) { + for (let i = 0; i < 2 * tech.blockingIce; i++) { + const angle = m.fieldAngle + 1.55 * (Math.random() - 0.5) + b.iceIX(10, angle, Vector.add(m.fieldPosition, { x: m.fieldRange * Math.cos(angle), y: m.fieldRange * Math.sin(angle) })) + } + } + if (tech.blockDmg) { //electricity + mob[i].damage(tech.blockDmg * b.dmgScale) + const step = 40 + ctx.beginPath(); + for (let i = 0, len = 1.5 * tech.blockDmg; i < len; i++) { + let x = m.fieldPosition.x - 20 * unit.x; + let y = m.fieldPosition.y - 20 * unit.y; + ctx.moveTo(x, y); + for (let i = 0; i < 8; i++) { + x += step * (-unit.x + 1.5 * (Math.random() - 0.5)) + y += step * (-unit.y + 1.5 * (Math.random() - 0.5)) + ctx.lineTo(x, y); } } - if (tech.blockDmg) { //electricity - mob[i].damage(tech.blockDmg * b.dmgScale) - const step = 40 - ctx.beginPath(); - for (let i = 0, len = 1.5 * tech.blockDmg; i < len; i++) { - let x = m.fieldPosition.x - 20 * unit.x; - let y = m.fieldPosition.y - 20 * unit.y; - ctx.moveTo(x, y); - for (let i = 0; i < 8; i++) { - x += step * (-unit.x + 1.5 * (Math.random() - 0.5)) - y += step * (-unit.y + 1.5 * (Math.random() - 0.5)) - ctx.lineTo(x, y); - } - } - ctx.lineWidth = 3; - ctx.strokeStyle = "#f0f"; - ctx.stroke(); - } else if (isFree) { - //when blocking draw this graphic - ctx.fillStyle = "rgba(110,170,200," + (0.2 + 0.4 * Math.random()) + ")"; - ctx.lineWidth = 2; - ctx.strokeStyle = "#000"; - const len = mob[i].vertices.length - 1; - const mag = mob[i].radius - ctx.beginPath(); - ctx.moveTo(mob[i].vertices[len].x + mag * (Math.random() - 0.5), mob[i].vertices[len].y + mag * (Math.random() - 0.5)) - for (let j = 0; j < len; j++) { - ctx.lineTo(mob[i].vertices[j].x + mag * (Math.random() - 0.5), mob[i].vertices[j].y + mag * (Math.random() - 0.5)); - } - ctx.lineTo(mob[i].vertices[len].x + mag * (Math.random() - 0.5), mob[i].vertices[len].y + mag * (Math.random() - 0.5)) - ctx.fill(); - ctx.stroke(); - } else { - //when blocking draw this graphic - const eye = 15; - const len = mob[i].vertices.length - 1; - ctx.fillStyle = "rgba(110,170,200," + (0.2 + 0.4 * Math.random()) + ")"; - ctx.lineWidth = 1; - ctx.strokeStyle = "#000"; + ctx.lineWidth = 3; + ctx.strokeStyle = "#f0f"; + ctx.stroke(); + } else if (isFree) { + //when blocking draw this graphic + ctx.fillStyle = "rgba(110,170,200," + (0.2 + 0.4 * Math.random()) + ")"; + ctx.lineWidth = 2; + ctx.strokeStyle = "#000"; + const len = mob[i].vertices.length - 1; + const mag = mob[i].radius + ctx.beginPath(); + ctx.moveTo(mob[i].vertices[len].x + mag * (Math.random() - 0.5), mob[i].vertices[len].y + mag * (Math.random() - 0.5)) + for (let j = 0; j < len; j++) { + ctx.lineTo(mob[i].vertices[j].x + mag * (Math.random() - 0.5), mob[i].vertices[j].y + mag * (Math.random() - 0.5)); + } + ctx.lineTo(mob[i].vertices[len].x + mag * (Math.random() - 0.5), mob[i].vertices[len].y + mag * (Math.random() - 0.5)) + ctx.fill(); + ctx.stroke(); + } else { + //when blocking draw this graphic + const eye = 15; + const len = mob[i].vertices.length - 1; + ctx.fillStyle = "rgba(110,170,200," + (0.2 + 0.4 * Math.random()) + ")"; + ctx.lineWidth = 1; + ctx.strokeStyle = "#000"; + ctx.beginPath(); + ctx.moveTo(m.fieldPosition.x + eye * Math.cos(m.fieldAngle), m.fieldPosition.y + eye * Math.sin(m.fieldAngle)); + ctx.lineTo(mob[i].vertices[len].x, mob[i].vertices[len].y); + ctx.lineTo(mob[i].vertices[0].x, mob[i].vertices[0].y); + ctx.fill(); + ctx.stroke(); + for (let j = 0; j < len; j++) { ctx.beginPath(); ctx.moveTo(m.fieldPosition.x + eye * Math.cos(m.fieldAngle), m.fieldPosition.y + eye * Math.sin(m.fieldAngle)); - ctx.lineTo(mob[i].vertices[len].x, mob[i].vertices[len].y); - ctx.lineTo(mob[i].vertices[0].x, mob[i].vertices[0].y); + ctx.lineTo(mob[i].vertices[j].x, mob[i].vertices[j].y); + ctx.lineTo(mob[i].vertices[j + 1].x, mob[i].vertices[j + 1].y); ctx.fill(); ctx.stroke(); - for (let j = 0; j < len; j++) { - ctx.beginPath(); - ctx.moveTo(m.fieldPosition.x + eye * Math.cos(m.fieldAngle), m.fieldPosition.y + eye * Math.sin(m.fieldAngle)); - ctx.lineTo(mob[i].vertices[j].x, mob[i].vertices[j].y); - ctx.lineTo(mob[i].vertices[j + 1].x, mob[i].vertices[j + 1].y); - ctx.fill(); - ctx.stroke(); - } } - if (tech.isStunField) mobs.statusStun(mob[i], tech.isStunField) - //mob knock backs - const massRoot = Math.sqrt(Math.max(1, mob[i].mass)); - Matter.Body.setVelocity(mob[i], { - x: player.velocity.x - (30 * unit.x) / massRoot, - y: player.velocity.y - (30 * unit.y) / massRoot - }); - if (mob[i].isOrbital) Matter.Body.setVelocity(mob[i], { x: 0, y: 0 }); - if (!isFree) { //player knock backs - if (mob[i].isDropPowerUp && player.speed < 12) { - const massRootCap = Math.sqrt(Math.min(10, Math.max(0.2, mob[i].mass))); - Matter.Body.setVelocity(player, { - x: 0.9 * player.velocity.x + 0.6 * unit.x * massRootCap, - y: 0.9 * player.velocity.y + 0.6 * unit.y * massRootCap - }); - } + } + if (tech.isStunField) mobs.statusStun(mob[i], tech.isStunField) + //mob knock backs + const massRoot = Math.sqrt(Math.max(1, mob[i].mass)); + Matter.Body.setVelocity(mob[i], { + x: player.velocity.x - (30 * unit.x) / massRoot, + y: player.velocity.y - (30 * unit.y) / massRoot + }); + if (mob[i].isOrbital) Matter.Body.setVelocity(mob[i], { x: 0, y: 0 }); + if (!isFree) { //player knock backs + if (mob[i].isDropPowerUp && player.speed < 12) { + const massRootCap = Math.sqrt(Math.min(10, Math.max(0.2, mob[i].mass))); + Matter.Body.setVelocity(player, { + x: 0.9 * player.velocity.x + 0.6 * unit.x * massRootCap, + y: 0.9 * player.velocity.y + 0.6 * unit.y * massRootCap + }); } } } } } - m.hold = function() { - const wave = Math.sin(m.cycle * 0.022); - m.fieldRange = 160 + 12 * wave + 100 * tech.isBigField - m.fieldArc = 0.34 + 0.04 * wave + 0.065 * tech.isBigField //run calculateFieldThreshold after setting fieldArc, used for powerUp grab and mobPush with lookingAt(mob) - m.calculateFieldThreshold(); - if (m.isHolding) { - m.drawHold(m.holdingTarget); - m.holding(); - m.throwBlock(); - } else if (input.field) { //not hold but field button is pressed - m.grabPowerUp(); - m.lookForPickUp(); - m.fieldPosition = { x: m.pos.x, y: m.pos.y } - m.fieldAngle = m.angle - //draw field attached to player - if (m.holdingTarget) { - ctx.fillStyle = "rgba(110,170,200," + (0.06 + 0.03 * Math.random()) + ")"; - ctx.strokeStyle = "rgba(110, 200, 235, " + (0.35 + 0.05 * Math.random()) + ")" - } else { - ctx.fillStyle = "rgba(110,170,200," + (0.27 + 0.2 * Math.random() - 0.1 * wave) + ")"; - ctx.strokeStyle = "rgba(110, 200, 235, " + (0.4 + 0.5 * Math.random()) + ")" - } + } + m.hold = function () { + const wave = Math.sin(m.cycle * 0.022); + m.fieldRange = 160 + 12 * wave + 100 * tech.isBigField + m.fieldArc = 0.34 + 0.04 * wave + 0.065 * tech.isBigField //run calculateFieldThreshold after setting fieldArc, used for powerUp grab and mobPush with lookingAt(mob) + m.calculateFieldThreshold(); + if (m.isHolding) { + m.drawHold(m.holdingTarget); + m.holding(); + m.throwBlock(); + } else if (input.field) { //not hold but field button is pressed + m.grabPowerUp(); + m.lookForPickUp(); + m.fieldPosition = { x: m.pos.x, y: m.pos.y } + m.fieldAngle = m.angle + //draw field attached to player + if (m.holdingTarget) { + ctx.fillStyle = "rgba(110,170,200," + (0.06 + 0.03 * Math.random()) + ")"; + ctx.strokeStyle = "rgba(110, 200, 235, " + (0.35 + 0.05 * Math.random()) + ")" + } else { + ctx.fillStyle = "rgba(110,170,200," + (0.27 + 0.2 * Math.random() - 0.1 * wave) + ")"; + ctx.strokeStyle = "rgba(110, 200, 235, " + (0.4 + 0.5 * Math.random()) + ")" + } + ctx.beginPath(); + ctx.arc(m.pos.x, m.pos.y, m.fieldRange, m.angle - Math.PI * m.fieldArc, m.angle + Math.PI * m.fieldArc, false); + ctx.lineWidth = 2.5 - 1.5 * wave; + ctx.lineCap = "butt" + ctx.stroke(); + const curve = 0.57 + 0.04 * wave + const aMag = (1 - curve * 1.2) * Math.PI * m.fieldArc + let a = m.angle + aMag + let cp1x = m.pos.x + curve * m.fieldRange * Math.cos(a) + let cp1y = m.pos.y + curve * m.fieldRange * Math.sin(a) + ctx.quadraticCurveTo(cp1x, cp1y, m.pos.x + 30 * Math.cos(m.angle), m.pos.y + 30 * Math.sin(m.angle)) + a = m.angle - aMag + cp1x = m.pos.x + curve * m.fieldRange * Math.cos(a) + cp1y = m.pos.y + curve * m.fieldRange * Math.sin(a) + ctx.quadraticCurveTo(cp1x, cp1y, m.pos.x + 1 * m.fieldRange * Math.cos(m.angle - Math.PI * m.fieldArc), m.pos.y + 1 * m.fieldRange * Math.sin(m.angle - Math.PI * m.fieldArc)) + ctx.fill(); + m.perfectPush(); + } else if (m.holdingTarget && m.fieldCDcycle < m.cycle) { //holding, but field button is released + m.pickUp(); + } else { + m.holdingTarget = null; //clears holding target (this is so you only pick up right after the field button is released and a hold target exists) + if (!input.field) { //&& tech.isFieldFree + //draw field free of player + ctx.fillStyle = "rgba(110,170,200," + (0.27 + 0.2 * Math.random() - 0.1 * wave) + ")"; + ctx.strokeStyle = "rgba(110, 200, 235, " + (0.4 + 0.5 * Math.random()) + ")" ctx.beginPath(); - ctx.arc(m.pos.x, m.pos.y, m.fieldRange, m.angle - Math.PI * m.fieldArc, m.angle + Math.PI * m.fieldArc, false); + ctx.arc(m.fieldPosition.x, m.fieldPosition.y, m.fieldRange, m.fieldAngle - Math.PI * m.fieldArc, m.fieldAngle + Math.PI * m.fieldArc, false); ctx.lineWidth = 2.5 - 1.5 * wave; ctx.lineCap = "butt" ctx.stroke(); - const curve = 0.57 + 0.04 * wave + const curve = 0.8 + 0.06 * wave const aMag = (1 - curve * 1.2) * Math.PI * m.fieldArc - let a = m.angle + aMag - let cp1x = m.pos.x + curve * m.fieldRange * Math.cos(a) - let cp1y = m.pos.y + curve * m.fieldRange * Math.sin(a) - ctx.quadraticCurveTo(cp1x, cp1y, m.pos.x + 30 * Math.cos(m.angle), m.pos.y + 30 * Math.sin(m.angle)) - a = m.angle - aMag - cp1x = m.pos.x + curve * m.fieldRange * Math.cos(a) - cp1y = m.pos.y + curve * m.fieldRange * Math.sin(a) - ctx.quadraticCurveTo(cp1x, cp1y, m.pos.x + 1 * m.fieldRange * Math.cos(m.angle - Math.PI * m.fieldArc), m.pos.y + 1 * m.fieldRange * Math.sin(m.angle - Math.PI * m.fieldArc)) - ctx.fill(); - m.perfectPush(); - } else if (m.holdingTarget && m.fieldCDcycle < m.cycle) { //holding, but field button is released - m.pickUp(); - } else { - m.holdingTarget = null; //clears holding target (this is so you only pick up right after the field button is released and a hold target exists) - if (!input.field) { //&& tech.isFieldFree - //draw field free of player - ctx.fillStyle = "rgba(110,170,200," + (0.27 + 0.2 * Math.random() - 0.1 * wave) + ")"; - ctx.strokeStyle = "rgba(110, 200, 235, " + (0.4 + 0.5 * Math.random()) + ")" - ctx.beginPath(); - ctx.arc(m.fieldPosition.x, m.fieldPosition.y, m.fieldRange, m.fieldAngle - Math.PI * m.fieldArc, m.fieldAngle + Math.PI * m.fieldArc, false); - ctx.lineWidth = 2.5 - 1.5 * wave; - ctx.lineCap = "butt" - ctx.stroke(); - const curve = 0.8 + 0.06 * wave - const aMag = (1 - curve * 1.2) * Math.PI * m.fieldArc - let a = m.fieldAngle + aMag - ctx.quadraticCurveTo(m.fieldPosition.x + curve * m.fieldRange * Math.cos(a), m.fieldPosition.y + curve * m.fieldRange * Math.sin(a), m.fieldPosition.x + 1 * m.fieldRange * Math.cos(m.fieldAngle - Math.PI * m.fieldArc), m.fieldPosition.y + 1 * m.fieldRange * Math.sin(m.fieldAngle - Math.PI * m.fieldArc)) - ctx.fill(); - m.perfectPush(true); - } - } - m.drawFieldMeter() - if (tech.isPerfectBrake) { //cap mob speed around player - const range = 200 + 140 * wave + 150 * m.energy - for (let i = 0; i < mob.length; i++) { - const distance = Vector.magnitude(Vector.sub(m.pos, mob[i].position)) - if (distance < range) { - const cap = mob[i].isShielded ? 8 : 4 - if (mob[i].speed > cap && Vector.dot(mob[i].velocity, Vector.sub(m.pos, mob[i].position)) > 0) { // if velocity is directed towards player - Matter.Body.setVelocity(mob[i], Vector.mult(Vector.normalise(mob[i].velocity), cap)); //set velocity to cap, but keep the direction - } - } - } - ctx.beginPath(); - ctx.arc(m.pos.x, m.pos.y, range, 0, 2 * Math.PI); - ctx.fillStyle = "hsla(200,50%,61%,0.08)"; + let a = m.fieldAngle + aMag + ctx.quadraticCurveTo(m.fieldPosition.x + curve * m.fieldRange * Math.cos(a), m.fieldPosition.y + curve * m.fieldRange * Math.sin(a), m.fieldPosition.x + 1 * m.fieldRange * Math.cos(m.fieldAngle - Math.PI * m.fieldArc), m.fieldPosition.y + 1 * m.fieldRange * Math.sin(m.fieldAngle - Math.PI * m.fieldArc)) ctx.fill(); + m.perfectPush(true); } } - } - }, - { - name: "negative mass", - description: "use energy to nullify  gravity
reduce harm by 55%
hold blocks as if they have a lower mass", - fieldDrawRadius: 0, - effect: () => { - m.fieldFire = true; - m.holdingMassScale = 0.01; //can hold heavier blocks with lower cost to jumping - m.fieldMeterColor = "#333" - m.eyeFillColor = m.fieldMeterColor - m.fieldHarmReduction = 0.45; //55% reduction - m.fieldDrawRadius = 0; - - m.hold = function() { - m.airSpeedLimit = 125 //5 * player.mass * player.mass - m.FxAir = 0.016 - if (m.isHolding) { - m.drawHold(m.holdingTarget); - m.holding(); - m.throwBlock(); - } else if (input.field && m.fieldCDcycle < m.cycle) { //push away - m.grabPowerUp(); - m.lookForPickUp(); - const DRAIN = 0.00035 - if (m.energy > DRAIN) { - if (tech.isFlyFaster) { - //look for nearby objects to make zero-g - function moveThis(who, range, mag = 1.06) { - for (let i = 0, len = who.length; i < len; ++i) { - sub = Vector.sub(who[i].position, m.pos); - dist = Vector.magnitude(sub); - if (dist < range) { - who[i].force.y -= who[i].mass * (simulation.g * mag); //add a bit more then standard gravity - if (input.left) { //blocks move horizontally with the same force as the player - who[i].force.x -= m.FxAir * who[i].mass / 10; // move player left / a - } else if (input.right) { - who[i].force.x += m.FxAir * who[i].mass / 10; //move player right / d - } - //loose attraction to player - // const sub = Vector.sub(m.pos, body[i].position) - // const unit = Vector.mult(Vector.normalise(sub), who[i].mass * 0.0000002 * Vector.magnitude(sub)) - // body[i].force.x += unit.x - // body[i].force.y += unit.y - } - } - } - //control horizontal acceleration - m.airSpeedLimit = 1000 // 7* player.mass * player.mass - m.FxAir = 0.01 - //control vertical acceleration - if (input.down) { //down - player.force.y += 0.5 * player.mass * simulation.g; - this.fieldDrawRadius = this.fieldDrawRadius * 0.97 + 500 * 0.03; - moveThis(powerUp, this.fieldDrawRadius, 0); - moveThis(body, this.fieldDrawRadius, 0); - } else if (input.up) { //up - m.energy -= 5 * DRAIN; - this.fieldDrawRadius = this.fieldDrawRadius * 0.97 + 1100 * 0.03; - player.force.y -= 2.25 * player.mass * simulation.g; - moveThis(powerUp, this.fieldDrawRadius, 1.8); - moveThis(body, this.fieldDrawRadius, 1.8); - } else { - m.energy -= DRAIN; - this.fieldDrawRadius = this.fieldDrawRadius * 0.97 + 800 * 0.03; - player.force.y -= 1.07 * player.mass * simulation.g; // slow upward drift - moveThis(powerUp, this.fieldDrawRadius); - moveThis(body, this.fieldDrawRadius); - } - } else { - //look for nearby objects to make zero-g - function verticalForce(who, range, mag = 1.06) { - for (let i = 0, len = who.length; i < len; ++i) { - sub = Vector.sub(who[i].position, m.pos); - dist = Vector.magnitude(sub); - if (dist < range) who[i].force.y -= who[i].mass * (simulation.g * mag); - } - } - //control horizontal acceleration - m.airSpeedLimit = 400 // 7* player.mass * player.mass - m.FxAir = 0.005 - //control vertical acceleration - if (input.down) { //down - player.force.y -= 0.5 * player.mass * simulation.g; - this.fieldDrawRadius = this.fieldDrawRadius * 0.97 + 400 * 0.03; - verticalForce(powerUp, this.fieldDrawRadius, 0.7); - verticalForce(body, this.fieldDrawRadius, 0.7); - } else if (input.up) { //up - m.energy -= 5 * DRAIN; - this.fieldDrawRadius = this.fieldDrawRadius * 0.97 + 850 * 0.03; - player.force.y -= 1.45 * player.mass * simulation.g; - verticalForce(powerUp, this.fieldDrawRadius, 1.38); - verticalForce(body, this.fieldDrawRadius, 1.38); - } else { - m.energy -= DRAIN; - this.fieldDrawRadius = this.fieldDrawRadius * 0.97 + 650 * 0.03; - player.force.y -= 1.07 * player.mass * simulation.g; // slow upward drift - verticalForce(powerUp, this.fieldDrawRadius); - verticalForce(body, this.fieldDrawRadius); - } + m.drawFieldMeter() + if (tech.isPerfectBrake) { //cap mob speed around player + const range = 200 + 140 * wave + 150 * m.energy + for (let i = 0; i < mob.length; i++) { + const distance = Vector.magnitude(Vector.sub(m.pos, mob[i].position)) + if (distance < range) { + const cap = mob[i].isShielded ? 8 : 4 + if (mob[i].speed > cap && Vector.dot(mob[i].velocity, Vector.sub(m.pos, mob[i].position)) > 0) { // if velocity is directed towards player + Matter.Body.setVelocity(mob[i], Vector.mult(Vector.normalise(mob[i].velocity), cap)); //set velocity to cap, but keep the direction } - - if (m.energy < 0.001) { - m.fieldCDcycle = m.cycle + 120; - m.energy = 0; - } - //add extra friction for horizontal motion - if (input.down || input.up || input.left || input.right) { - Matter.Body.setVelocity(player, { - x: player.velocity.x * 0.99, - y: player.velocity.y * 0.98 - }); - } else { //slow rise and fall - Matter.Body.setVelocity(player, { - x: player.velocity.x * 0.99, - y: player.velocity.y * 0.98 - }); - } - if (tech.isFreezeMobs) { - const ICE_DRAIN = 0.0002 - for (let i = 0, len = mob.length; i < len; i++) { - if (((mob[i].distanceToPlayer() + mob[i].radius) < this.fieldDrawRadius) && !mob[i].shield && !mob[i].isShielded) { - if (m.energy > ICE_DRAIN * 2) { - m.energy -= ICE_DRAIN; - this.fieldDrawRadius -= 2; - mobs.statusSlow(mob[i], 60) - } else { - break; - } - } - } - } - - //draw zero-G range - ctx.beginPath(); - ctx.arc(m.pos.x, m.pos.y, this.fieldDrawRadius, 0, 2 * Math.PI); - ctx.fillStyle = "#f5f5ff"; - ctx.globalCompositeOperation = "difference"; - ctx.fill(); - ctx.globalCompositeOperation = "source-over"; } - } else if (m.holdingTarget && m.fieldCDcycle < m.cycle) { //holding, but field button is released - m.pickUp(); - this.fieldDrawRadius = 0 - } else { - m.holdingTarget = null; //clears holding target (this is so you only pick up right after the field button is released and a hold target exists) - this.fieldDrawRadius = 0 } - m.drawFieldMeter("rgba(0,0,0,0.2)") + ctx.beginPath(); + ctx.arc(m.pos.x, m.pos.y, range, 0, 2 * Math.PI); + ctx.fillStyle = "hsla(200,50%,61%,0.08)"; + ctx.fill(); } } - }, - { - name: "molecular assembler", - description: "excess energy used to build drones
use energy to deflect mobs
double your default energy regeneration", - effect: () => { - // m.fieldMeterColor = "#0c5" - // m.eyeFillColor = m.fieldMeterColor - m.hold = function() { - if (m.energy > m.maxEnergy - 0.02 && m.fieldCDcycle < m.cycle && !input.field && bullet.length < 200 && (m.cycle % 2)) { - if (tech.isSporeField) { - if (tech.isSporeWorm) { - if (m.energy > 0.16) { - m.energy -= 0.16 - b.worm({ x: m.pos.x + 35 * Math.cos(m.angle), y: m.pos.y + 35 * Math.sin(m.angle) }) - const SPEED = 2 + 1 * Math.random(); - Matter.Body.setVelocity(bullet[bullet.length - 1], { - x: SPEED * Math.cos(m.angle), - y: SPEED * Math.sin(m.angle) - }); + } + }, + { + name: "negative mass", + description: "use energy to nullify  gravity
reduce harm by 55%
hold blocks as if they have a lower mass", + fieldDrawRadius: 0, + effect: () => { + m.fieldFire = true; + m.holdingMassScale = 0.01; //can hold heavier blocks with lower cost to jumping + m.fieldMeterColor = "#333" + m.eyeFillColor = m.fieldMeterColor + m.fieldHarmReduction = 0.45; //55% reduction + m.fieldDrawRadius = 0; + + m.hold = function () { + m.airSpeedLimit = 125 //5 * player.mass * player.mass + m.FxAir = 0.016 + if (m.isHolding) { + m.drawHold(m.holdingTarget); + m.holding(); + m.throwBlock(); + } else if (input.field && m.fieldCDcycle < m.cycle) { //push away + m.grabPowerUp(); + m.lookForPickUp(); + const DRAIN = 0.00035 + if (m.energy > DRAIN) { + if (tech.isFlyFaster) { + //look for nearby objects to make zero-g + function moveThis(who, range, mag = 1.06) { + for (let i = 0, len = who.length; i < len; ++i) { + sub = Vector.sub(who[i].position, m.pos); + dist = Vector.magnitude(sub); + if (dist < range) { + who[i].force.y -= who[i].mass * (simulation.g * mag); //add a bit more then standard gravity + if (input.left) { //blocks move horizontally with the same force as the player + who[i].force.x -= m.FxAir * who[i].mass / 10; // move player left / a + } else if (input.right) { + who[i].force.x += m.FxAir * who[i].mass / 10; //move player right / d + } + //loose attraction to player + // const sub = Vector.sub(m.pos, body[i].position) + // const unit = Vector.mult(Vector.normalise(sub), who[i].mass * 0.0000002 * Vector.magnitude(sub)) + // body[i].force.x += unit.x + // body[i].force.y += unit.y + } } + } + //control horizontal acceleration + m.airSpeedLimit = 1000 // 7* player.mass * player.mass + m.FxAir = 0.01 + //control vertical acceleration + if (input.down) { //down + player.force.y += 0.5 * player.mass * simulation.g; + this.fieldDrawRadius = this.fieldDrawRadius * 0.97 + 500 * 0.03; + moveThis(powerUp, this.fieldDrawRadius, 0); + moveThis(body, this.fieldDrawRadius, 0); + } else if (input.up) { //up + m.energy -= 5 * DRAIN; + this.fieldDrawRadius = this.fieldDrawRadius * 0.97 + 1100 * 0.03; + player.force.y -= 2.25 * player.mass * simulation.g; + moveThis(powerUp, this.fieldDrawRadius, 1.8); + moveThis(body, this.fieldDrawRadius, 1.8); } else { - for (let i = 0, len = Math.random() * 20; i < len; i++) { - m.energy -= 0.08 - if (m.energy > 0) { - b.spore(m.pos) + m.energy -= DRAIN; + this.fieldDrawRadius = this.fieldDrawRadius * 0.97 + 800 * 0.03; + player.force.y -= 1.07 * player.mass * simulation.g; // slow upward drift + moveThis(powerUp, this.fieldDrawRadius); + moveThis(body, this.fieldDrawRadius); + } + } else { + //look for nearby objects to make zero-g + function verticalForce(who, range, mag = 1.06) { + for (let i = 0, len = who.length; i < len; ++i) { + sub = Vector.sub(who[i].position, m.pos); + dist = Vector.magnitude(sub); + if (dist < range) who[i].force.y -= who[i].mass * (simulation.g * mag); + } + } + //control horizontal acceleration + m.airSpeedLimit = 400 // 7* player.mass * player.mass + m.FxAir = 0.005 + //control vertical acceleration + if (input.down) { //down + player.force.y -= 0.5 * player.mass * simulation.g; + this.fieldDrawRadius = this.fieldDrawRadius * 0.97 + 400 * 0.03; + verticalForce(powerUp, this.fieldDrawRadius, 0.7); + verticalForce(body, this.fieldDrawRadius, 0.7); + } else if (input.up) { //up + m.energy -= 5 * DRAIN; + this.fieldDrawRadius = this.fieldDrawRadius * 0.97 + 850 * 0.03; + player.force.y -= 1.45 * player.mass * simulation.g; + verticalForce(powerUp, this.fieldDrawRadius, 1.38); + verticalForce(body, this.fieldDrawRadius, 1.38); + } else { + m.energy -= DRAIN; + this.fieldDrawRadius = this.fieldDrawRadius * 0.97 + 650 * 0.03; + player.force.y -= 1.07 * player.mass * simulation.g; // slow upward drift + verticalForce(powerUp, this.fieldDrawRadius); + verticalForce(body, this.fieldDrawRadius); + } + } + + if (m.energy < 0.001) { + m.fieldCDcycle = m.cycle + 120; + m.energy = 0; + } + //add extra friction for horizontal motion + if (input.down || input.up || input.left || input.right) { + Matter.Body.setVelocity(player, { + x: player.velocity.x * 0.99, + y: player.velocity.y * 0.98 + }); + } else { //slow rise and fall + Matter.Body.setVelocity(player, { + x: player.velocity.x * 0.99, + y: player.velocity.y * 0.98 + }); + } + if (tech.isFreezeMobs) { + const ICE_DRAIN = 0.0002 + for (let i = 0, len = mob.length; i < len; i++) { + if (((mob[i].distanceToPlayer() + mob[i].radius) < this.fieldDrawRadius) && !mob[i].shield && !mob[i].isShielded) { + if (m.energy > ICE_DRAIN * 2) { + m.energy -= ICE_DRAIN; + this.fieldDrawRadius -= 2; + mobs.statusSlow(mob[i], 60) } else { - m.energy = 0.001 break; } } } - } else if (tech.isMissileField) { - m.energy -= 0.3; - b.missile({ x: m.pos.x, y: m.pos.y - 40 }, -Math.PI / 2 + 0.5 * (Math.random() - 0.5), 0, 1) - } else if (tech.isIceField) { - m.energy -= 0.04; - b.iceIX(1) - } else if (tech.isDroneRadioactive) { - m.energy -= 0.8; - b.droneRadioactive({ x: m.pos.x + 30 * Math.cos(m.angle) + 10 * (Math.random() - 0.5), y: m.pos.y + 30 * Math.sin(m.angle) + 10 * (Math.random() - 0.5) }, 25) - } else { - m.energy -= 0.45 * tech.droneEnergyReduction; - b.drone() } - } + //draw zero-G range + ctx.beginPath(); + ctx.arc(m.pos.x, m.pos.y, this.fieldDrawRadius, 0, 2 * Math.PI); + ctx.fillStyle = "#f5f5ff"; + ctx.globalCompositeOperation = "difference"; + ctx.fill(); + ctx.globalCompositeOperation = "source-over"; + } + } else if (m.holdingTarget && m.fieldCDcycle < m.cycle) { //holding, but field button is released + m.pickUp(); + this.fieldDrawRadius = 0 + } else { + m.holdingTarget = null; //clears holding target (this is so you only pick up right after the field button is released and a hold target exists) + this.fieldDrawRadius = 0 + } + m.drawFieldMeter("rgba(0,0,0,0.2)") + } + } + }, + { + name: "molecular assembler", + description: "excess energy used to build drones
use energy to deflect mobs
double your default energy regeneration", + effect: () => { + // m.fieldMeterColor = "#0c5" + // m.eyeFillColor = m.fieldMeterColor + m.hold = function () { + if (m.energy > m.maxEnergy - 0.02 && m.fieldCDcycle < m.cycle && !input.field && bullet.length < 200 && (m.cycle % 2)) { + if (tech.isSporeField) { + if (tech.isSporeWorm) { + if (m.energy > 0.16) { + m.energy -= 0.16 + b.worm({ x: m.pos.x + 35 * Math.cos(m.angle), y: m.pos.y + 35 * Math.sin(m.angle) }) + const SPEED = 2 + 1 * Math.random(); + Matter.Body.setVelocity(bullet[bullet.length - 1], { + x: SPEED * Math.cos(m.angle), + y: SPEED * Math.sin(m.angle) + }); + } + } else { + for (let i = 0, len = Math.random() * 20; i < len; i++) { + m.energy -= 0.08 + if (m.energy > 0) { + b.spore(m.pos) + } else { + m.energy = 0.001 + break; + } + } + } + } else if (tech.isMissileField) { + m.energy -= 0.3; + b.missile({ x: m.pos.x, y: m.pos.y - 40 }, -Math.PI / 2 + 0.5 * (Math.random() - 0.5), 0, 1) + } else if (tech.isIceField) { + m.energy -= 0.04; + b.iceIX(1) + } else if (tech.isDroneRadioactive) { + m.energy -= 0.8; + b.droneRadioactive({ x: m.pos.x + 30 * Math.cos(m.angle) + 10 * (Math.random() - 0.5), y: m.pos.y + 30 * Math.sin(m.angle) + 10 * (Math.random() - 0.5) }, 25) + } else { + m.energy -= 0.45 * tech.droneEnergyReduction; + b.drone() + } + } + + if (m.isHolding) { + m.drawHold(m.holdingTarget); + m.holding(); + m.throwBlock(); + } else if ((input.field && m.fieldCDcycle < m.cycle)) { //not hold but field button is pressed + m.grabPowerUp(); + m.lookForPickUp(); + if (m.energy > 0.05) { + m.drawField(); + m.pushMobsFacing(); + } + } else if (m.holdingTarget && m.fieldCDcycle < m.cycle) { //holding, but field button is released + m.pickUp(); + } else { + m.holdingTarget = null; //clears holding target (this is so you only pick up right after the field button is released and a hold target exists) + } + m.regenEnergy() + m.drawFieldMeter() + } + } + }, + // { + // name: "plasma torch", + // description: "use energy to emit short range plasma
damages and pushes mobs away", + // effect() { + // m.fieldMeterColor = "#f0f" + // m.eyeFillColor = m.fieldMeterColor + // m.hold = function() { + // b.isExtruderOn = false + // if (m.isHolding) { + // m.drawHold(m.holdingTarget); + // m.holding(); + // m.throwBlock(); + // } else if (input.field && m.fieldCDcycle < m.cycle) { //not hold but field button is pressed + // m.grabPowerUp(); + // m.lookForPickUp(); + // if (tech.isExtruder) { + // b.extruder(); + // } else { + // b.plasma(); + // } + // } else if (m.holdingTarget && m.fieldCDcycle < m.cycle) { //holding, but field button is released + // m.pickUp(); + // } else { + // m.holdingTarget = null; //clears holding target (this is so you only pick up right after the field button is released and a hold target exists) + // } + // m.drawFieldMeter("rgba(0, 0, 0, 0.2)") + + // if (tech.isExtruder) { + // if (input.field) { + // b.wasExtruderOn = true + // } else { + // b.wasExtruderOn = false + // b.canExtruderFire = true + // } + // ctx.beginPath(); //draw all the wave bullets + // for (let i = 0, len = bullet.length; i < len; i++) { + // if (bullet[i].isWave) { + // if (bullet[i].isBranch) { + // ctx.moveTo(bullet[i].position.x, bullet[i].position.y) + // } else { + // ctx.lineTo(bullet[i].position.x, bullet[i].position.y) + // } + // } + // } + // if (b.wasExtruderOn && b.isExtruderOn) ctx.lineTo(m.pos.x + 15 * Math.cos(m.angle), m.pos.y + 15 * Math.sin(m.angle)) + // ctx.lineWidth = 4; + // ctx.strokeStyle = "#f07" + // ctx.stroke(); + // ctx.lineWidth = tech.extruderRange; + // ctx.strokeStyle = "rgba(255,0,110,0.05)" + // ctx.stroke(); + // } + // } + // } + // }, + { + name: "plasma torch", + description: "use energy to emit short range plasma
damages and pushes mobs away", + set() { + b.isExtruderOn = false + if (tech.isExtruder) { + m.hold = function () { + b.isExtruderOn = false if (m.isHolding) { m.drawHold(m.holdingTarget); m.holding(); m.throwBlock(); - } else if ((input.field && m.fieldCDcycle < m.cycle)) { //not hold but field button is pressed + } else if (input.field && m.fieldCDcycle < m.cycle) { //not hold but field button is pressed m.grabPowerUp(); m.lookForPickUp(); - if (m.energy > 0.05) { - m.drawField(); - m.pushMobsFacing(); - } + b.extruder(); } else if (m.holdingTarget && m.fieldCDcycle < m.cycle) { //holding, but field button is released m.pickUp(); } else { m.holdingTarget = null; //clears holding target (this is so you only pick up right after the field button is released and a hold target exists) } - m.regenEnergy() - m.drawFieldMeter() + m.drawFieldMeter("rgba(0, 0, 0, 0.2)") + if (input.field) { + b.wasExtruderOn = true + } else { + b.wasExtruderOn = false + b.canExtruderFire = true + } + ctx.beginPath(); //draw all the wave bullets + for (let i = 1, len = bullet.length; i < len; i++) { //skip the first bullet (which is is oldest bullet) + if (bullet[i].isWave) { + if (bullet[i].isBranch || bullet[i - 1].isBranch) { + ctx.moveTo(bullet[i].position.x, bullet[i].position.y) + } else { + ctx.lineTo(bullet[i].position.x, bullet[i].position.y) + } + } + } + if (b.wasExtruderOn && b.isExtruderOn) ctx.lineTo(m.pos.x + 15 * Math.cos(m.angle), m.pos.y + 15 * Math.sin(m.angle)) + ctx.lineWidth = 4; + ctx.strokeStyle = "#f07" + ctx.stroke(); + ctx.lineWidth = tech.extruderRange; + ctx.strokeStyle = "rgba(255,0,110,0.06)" + ctx.stroke(); + } + } else { + m.hold = function () { + if (m.isHolding) { + m.drawHold(m.holdingTarget); + m.holding(); + m.throwBlock(); + } else if (input.field && m.fieldCDcycle < m.cycle) { //not hold but field button is pressed + m.grabPowerUp(); + m.lookForPickUp(); + b.plasma(); + } else if (m.holdingTarget && m.fieldCDcycle < m.cycle) { //holding, but field button is released + m.pickUp(); + } else { + m.holdingTarget = null; //clears holding target (this is so you only pick up right after the field button is released and a hold target exists) + } + m.drawFieldMeter("rgba(0, 0, 0, 0.2)") } } }, - // { - // name: "plasma torch", - // description: "use energy to emit short range plasma
damages and pushes mobs away", - // effect() { - // m.fieldMeterColor = "#f0f" - // m.eyeFillColor = m.fieldMeterColor - // m.hold = function() { - // b.isExtruderOn = false - // if (m.isHolding) { - // m.drawHold(m.holdingTarget); - // m.holding(); - // m.throwBlock(); - // } else if (input.field && m.fieldCDcycle < m.cycle) { //not hold but field button is pressed - // m.grabPowerUp(); - // m.lookForPickUp(); - // if (tech.isExtruder) { - // b.extruder(); - // } else { - // b.plasma(); - // } - // } else if (m.holdingTarget && m.fieldCDcycle < m.cycle) { //holding, but field button is released - // m.pickUp(); - // } else { - // m.holdingTarget = null; //clears holding target (this is so you only pick up right after the field button is released and a hold target exists) - // } - // m.drawFieldMeter("rgba(0, 0, 0, 0.2)") + effect() { + m.fieldMeterColor = "#f0f" + m.eyeFillColor = m.fieldMeterColor + this.set(); + } + }, + { + name: "time dilation", + // description: "use energy to stop time
while time is stopped you can move and fire
and collisions do 50% less harm", + description: "use energy to stop time
move and fire while time is stopped
but, collisions still do harm", + set() { + if (tech.isRewindField) { + this.rewindCount = 0 + m.grabPowerUpRange2 = 300000 + m.hold = function () { + if (m.isHolding) { + m.drawHold(m.holdingTarget); + m.holding(); + m.throwBlock(); + } else if (input.field && m.fieldCDcycle < m.cycle) { //not hold but field button is pressed + m.grabPowerUp(); + if (this.rewindCount === 0) m.lookForPickUp(); - // if (tech.isExtruder) { - // if (input.field) { - // b.wasExtruderOn = true - // } else { - // b.wasExtruderOn = false - // b.canExtruderFire = true - // } - // ctx.beginPath(); //draw all the wave bullets - // for (let i = 0, len = bullet.length; i < len; i++) { - // if (bullet[i].isWave) { - // if (bullet[i].isBranch) { - // ctx.moveTo(bullet[i].position.x, bullet[i].position.y) - // } else { - // ctx.lineTo(bullet[i].position.x, bullet[i].position.y) - // } - // } - // } - // if (b.wasExtruderOn && b.isExtruderOn) ctx.lineTo(m.pos.x + 15 * Math.cos(m.angle), m.pos.y + 15 * Math.sin(m.angle)) - // ctx.lineWidth = 4; - // ctx.strokeStyle = "#f07" - // ctx.stroke(); - // ctx.lineWidth = tech.extruderRange; - // ctx.strokeStyle = "rgba(255,0,110,0.05)" - // ctx.stroke(); - // } - // } - // } - // }, - { - name: "plasma torch", - description: "use energy to emit short range plasma
damages and pushes mobs away", - set() { - b.isExtruderOn = false - if (tech.isExtruder) { - m.hold = function() { - b.isExtruderOn = false - if (m.isHolding) { - m.drawHold(m.holdingTarget); - m.holding(); - m.throwBlock(); - } else if (input.field && m.fieldCDcycle < m.cycle) { //not hold but field button is pressed - m.grabPowerUp(); - m.lookForPickUp(); - b.extruder(); - } else if (m.holdingTarget && m.fieldCDcycle < m.cycle) { //holding, but field button is released - m.pickUp(); - } else { - m.holdingTarget = null; //clears holding target (this is so you only pick up right after the field button is released and a hold target exists) - } - m.drawFieldMeter("rgba(0, 0, 0, 0.2)") - if (input.field) { - b.wasExtruderOn = true - } else { - b.wasExtruderOn = false - b.canExtruderFire = true - } - ctx.beginPath(); //draw all the wave bullets - for (let i = 1, len = bullet.length; i < len; i++) { //skip the first bullet (which is is oldest bullet) - if (bullet[i].isWave) { - if (bullet[i].isBranch || bullet[i - 1].isBranch) { - ctx.moveTo(bullet[i].position.x, bullet[i].position.y) - } else { - ctx.lineTo(bullet[i].position.x, bullet[i].position.y) - } - } - } - if (b.wasExtruderOn && b.isExtruderOn) ctx.lineTo(m.pos.x + 15 * Math.cos(m.angle), m.pos.y + 15 * Math.sin(m.angle)) - ctx.lineWidth = 4; - ctx.strokeStyle = "#f07" - ctx.stroke(); - ctx.lineWidth = tech.extruderRange; - ctx.strokeStyle = "rgba(255,0,110,0.06)" - ctx.stroke(); - } - } else { - m.hold = function() { - if (m.isHolding) { - m.drawHold(m.holdingTarget); - m.holding(); - m.throwBlock(); - } else if (input.field && m.fieldCDcycle < m.cycle) { //not hold but field button is pressed - m.grabPowerUp(); - m.lookForPickUp(); - b.plasma(); - } else if (m.holdingTarget && m.fieldCDcycle < m.cycle) { //holding, but field button is released - m.pickUp(); - } else { - m.holdingTarget = null; //clears holding target (this is so you only pick up right after the field button is released and a hold target exists) - } - m.drawFieldMeter("rgba(0, 0, 0, 0.2)") - } - } - }, - effect() { - m.fieldMeterColor = "#f0f" - m.eyeFillColor = m.fieldMeterColor - this.set(); - } - }, - { - name: "time dilation", - // description: "use energy to stop time
while time is stopped you can move and fire
and collisions do 50% less harm", - description: "use energy to stop time
move and fire while time is stopped
but, collisions still do harm", - set() { - if (tech.isRewindField) { - this.rewindCount = 0 - m.grabPowerUpRange2 = 300000 - m.hold = function() { - if (m.isHolding) { - m.drawHold(m.holdingTarget); - m.holding(); - m.throwBlock(); - } else if (input.field && m.fieldCDcycle < m.cycle) { //not hold but field button is pressed - m.grabPowerUp(); - if (this.rewindCount === 0) m.lookForPickUp(); - - if (!m.holdingTarget) { - this.rewindCount += 6; - const DRAIN = 0.001 - let history = m.history[(m.cycle - this.rewindCount) % 600] - if (this.rewindCount > 599 || m.energy < DRAIN) { - this.rewindCount = 0; - m.resetHistory(); - if (m.fireCDcycle < m.cycle + 60) m.fieldCDcycle = m.cycle + 60 - m.immuneCycle = m.cycle //if you reach the end of the history disable harm immunity - } else { - //draw field everywhere - ctx.globalCompositeOperation = "saturation" - ctx.fillStyle = "#ccc"; - ctx.fillRect(-100000, -100000, 200000, 200000) - ctx.globalCompositeOperation = "source-over" - // m.grabPowerUp(); //a second grab power up to make the power ups easier to grab, and they more fast which matches the time theme - m.energy -= DRAIN - if (m.immuneCycle < m.cycle + 60) m.immuneCycle = m.cycle + 60; //player is immune to damage for __ cycles - Matter.Body.setPosition(player, history.position); - Matter.Body.setVelocity(player, { x: history.velocity.x, y: history.velocity.y }); - if (m.health < history.health) { - m.health = history.health - if (m.health > m.maxHealth) m.health = m.maxHealth - m.displayHealth(); - } - m.yOff = history.yOff - if (m.yOff < 48) { - m.doCrouch() - } else { - m.undoCrouch() - } - //grab power ups - for (let i = 0, len = powerUp.length; i < len; ++i) { - if ( - Vector.magnitudeSquared(Vector.sub(m.pos, powerUp[i].position)) < 100000 && - !simulation.isChoosing && - (powerUp[i].name !== "heal" || m.health !== m.maxHealth || tech.isOverHeal) - ) { - powerUps.onPickUp(powerUp[i]); - powerUp[i].effect(); - Matter.Composite.remove(engine.world, powerUp[i]); - powerUp.splice(i, 1); - break; //because the array order is messed up after splice - } - } - if (!(this.rewindCount % 30)) { - if (tech.isRewindBot) { - for (let i = 0; i < tech.isRewindBot; i++) { - b.randomBot(m.pos, false, false) - bullet[bullet.length - 1].endCycle = simulation.cycle + 480 + Math.floor(120 * Math.random()) //8-9 seconds - } - } - - if (tech.isRewindGrenade) { - b.grenade(m.pos, this.rewindCount) //Math.PI / 2 - const who = bullet[bullet.length - 1] - // Matter.Body.setVelocity(who, { - // x: 0, - // y: 0 - // }); - who.endCycle = simulation.cycle + 60 - // if (tech.isVacuumBomb) { - // Matter.Body.setVelocity(who, { - // x: who.velocity.x * 0.5, - // y: who.velocity.y * 0.5 - // }); - // } else if (tech.isRPG) { - // who.endCycle = simulation.cycle + 10 - // } else if (tech.isNeutronBomb) { - // Matter.Body.setVelocity(who, { - // x: who.velocity.x * 0.3, - // y: who.velocity.y * 0.3 - // }); - // } else { - // Matter.Body.setVelocity(who, { - // x: who.velocity.x * 0.5, - // y: who.velocity.y * 0.5 - // }); - // who.endCycle = simulation.cycle + 30 - // } - } - - - } - } - } - } else if (m.holdingTarget && m.fieldCDcycle < m.cycle) { //holding, but field button is released - m.pickUp(); - this.rewindCount = 0; - } else { - m.holdingTarget = null; //clears holding target (this is so you only pick up right after the field button is released and a hold target exists) - this.rewindCount = 0; - } - m.drawFieldMeter() - - - - - // // console.log(this.rewindCount) - // if (input.field && m.fieldCDcycle < m.cycle) { //button has been held down - // if (m.isHolding) { - // m.drawHold(m.holdingTarget); - // m.holding(); - // m.throwBlock(); - // } else { - // m.grabPowerUp(); - // m.lookForPickUp(); - // if (!m.holdingTarget) { - // this.rewindCount += 8; - // const DRAIN = 0.001 - // let history = m.history[(m.cycle - this.rewindCount) % 600] - // if (this.rewindCount > 599 || m.energy < DRAIN) { - // this.rewindCount = 0; - // m.resetHistory(); - // } else { - // // m.grabPowerUp(); //a second grab power up to make the power ups easier to grab, and they more fast which matches the time theme - // m.energy -= DRAIN - // if (m.immuneCycle < m.cycle + 30) m.immuneCycle = m.cycle + 30; //player is immune to damage for 30 cycles - // Matter.Body.setPosition(player, history.position); - // Matter.Body.setVelocity(player, { x: history.velocity.x, y: history.velocity.y }); - // if (m.health < history.health) { - // m.health = history.health - // m.displayHealth(); - // } - // m.yOff = history.yOff - // if (m.yOff < 48) { - // m.doCrouch() - // } else { - // m.undoCrouch() - // } - // //grab power ups - // for (let i = 0, len = powerUp.length; i < len; ++i) { - // if ( - // Vector.magnitudeSquared(Vector.sub(m.pos, powerUp[i].position)) < 100000 && - // !simulation.isChoosing && - // (powerUp[i].name !== "heal" || m.health !== m.maxHealth || tech.isOverHeal) - // ) { - // powerUps.onPickUp(powerUp[i]); - // powerUp[i].effect(); - // Matter.Composite.remove(engine.world, powerUp[i]); - // powerUp.splice(i, 1); - // break; //because the array order is messed up after splice - // } - // } - // } - // } - // } - // } else { //button is held the first time - // this.rewindCount = 0; - // if (m.holdingTarget && m.fieldCDcycle < m.cycle) { - // m.pickUp(); - // } else { - // m.holdingTarget = null; //clears holding target (this is so you only pick up right after the field button is released and a hold target exists) - // } - // } - - } - } else { - m.fieldFire = true; - m.isBodiesAsleep = false; - m.drain = 0.0005 - m.hold = function() { - if (m.isHolding) { - m.wakeCheck(); - m.drawHold(m.holdingTarget); - m.holding(); - m.throwBlock(); - } else if (input.field && m.fieldCDcycle < m.cycle) { - m.grabPowerUp(); - m.lookForPickUp(); - - m.drain += 0.0000025 //also increases inside tech.isTimeSkip - if (m.energy > m.drain) { - m.energy -= m.drain; - if (m.energy < m.drain) { - m.fieldCDcycle = m.cycle + 120; - m.energy = 0; - m.wakeCheck(); - } + if (!m.holdingTarget) { + this.rewindCount += 6; + const DRAIN = 0.001 + let history = m.history[(m.cycle - this.rewindCount) % 600] + if (this.rewindCount > 599 || m.energy < DRAIN) { + this.rewindCount = 0; + m.resetHistory(); + if (m.fireCDcycle < m.cycle + 60) m.fieldCDcycle = m.cycle + 60 + m.immuneCycle = m.cycle //if you reach the end of the history disable harm immunity + } else { //draw field everywhere ctx.globalCompositeOperation = "saturation" ctx.fillStyle = "#ccc"; ctx.fillRect(-100000, -100000, 200000, 200000) ctx.globalCompositeOperation = "source-over" - //stop time - m.isBodiesAsleep = true; - - function sleep(who) { - for (let i = 0, len = who.length; i < len; ++i) { - if (!who[i].isSleeping) { - who[i].storeVelocity = who[i].velocity - who[i].storeAngularVelocity = who[i].angularVelocity - } - Matter.Sleeping.set(who[i], true) - } + // m.grabPowerUp(); //a second grab power up to make the power ups easier to grab, and they more fast which matches the time theme + m.energy -= DRAIN + if (m.immuneCycle < m.cycle + 60) m.immuneCycle = m.cycle + 60; //player is immune to damage for __ cycles + Matter.Body.setPosition(player, history.position); + Matter.Body.setVelocity(player, { x: history.velocity.x, y: history.velocity.y }); + if (m.health < history.health) { + m.health = history.health + if (m.health > m.maxHealth) m.health = m.maxHealth + m.displayHealth(); } - sleep(mob); - sleep(body); - sleep(bullet); - - simulation.cycle--; //pause all functions that depend on game cycle increasing - if (tech.isTimeSkip) { - m.immuneCycle = 0; - m.drain += 0.0000025 - m.regenEnergy(); //immunity disables normal regen, so turn off immunity for just this function - m.immuneCycle = m.cycle + 10; - simulation.isTimeSkipping = true; - m.cycle++; - simulation.gravity(); - if (tech.isFireMoveLock && input.fire) { - player.force.x = 0 - player.force.y = 0 - } - Engine.update(engine, simulation.delta); - m.move(); - simulation.checks(); - m.walk_cycle += m.flipLegs * m.Vx; - b.fire(); - b.bulletDo(); - simulation.isTimeSkipping = false; + m.yOff = history.yOff + if (m.yOff < 48) { + m.doCrouch() + } else { + m.undoCrouch() } - } else { //holding, but field button is released - m.wakeCheck(); - } - } else if (m.holdingTarget && m.fieldCDcycle < m.cycle) { //holding, but field button is released - m.wakeCheck(); - m.pickUp(); - } else { - if (m.drain > 0.0005) m.drain -= 0.000005 //return drain to base level - m.wakeCheck(); - m.holdingTarget = null; //clears holding target (this is so you only pick up right after the field button is released and a hold target exists) - } - // console.log(m.drain.toFixed(6)) - m.drawFieldMeter() - } - } - }, - effect() { - // m.fieldMeterColor = "#000" - this.set(); - } - }, - { - name: "metamaterial cloaking", //"weak photonic coupling" "electromagnetically induced transparency" "optical non-coupling" "slow light field" "electro-optic transparency" - description: "when not firing activate a cloaking effect
+333% damage if a mob hasn't recently died
collisions do 50% less harm when cloaked", - effect: () => { - m.fieldFire = true; - m.fieldMeterColor = "#333"; - m.eyeFillColor = m.fieldMeterColor - // m.eyeFillColor = '#333' - m.fieldPhase = 0; - m.isCloak = false - // m.fieldDamage = 2.46 // 1 + 146/100 - m.fieldDrawRadius = 0 - m.isSneakAttack = true; - const drawRadius = 900 - - m.hold = function() { - // console.log(m.holdingTarget) - if (m.isHolding) { - m.drawHold(m.holdingTarget); - m.holding(); - m.throwBlock(); - } else if (input.field && m.fieldCDcycle < m.cycle) { //not hold and field button is pressed - m.grabPowerUp(); - m.lookForPickUp(); - } else if (m.holdingTarget && m.fieldCDcycle < m.cycle) { //holding target exists, and field button is not pressed - m.pickUp(); - } else { - m.holdingTarget = null; //clears holding target (this is so you only pick up right after the field button is released and a hold target exists) - } - - //not shooting (or using field) enable cloak - if (m.energy < 0.05 && m.fireCDcycle < m.cycle && !input.fire) m.fireCDcycle = m.cycle - if (m.fireCDcycle + 30 < m.cycle && !input.fire) { //automatically cloak if not firing - if (!m.isCloak) { - m.isCloak = true //enter cloak - if (tech.isIntangible) { - for (let i = 0; i < bullet.length; i++) { - if (bullet[i].botType && bullet[i].botType !== "orbit") bullet[i].collisionFilter.mask = cat.map | cat.bullet | cat.mobBullet | cat.mobShield - } - } - } - } else if (m.isCloak) { //exit cloak - m.isCloak = false - if (tech.isIntangible) { - for (let i = 0; i < bullet.length; i++) { - if (bullet[i].botType && bullet[i].botType !== "orbit") bullet[i].collisionFilter.mask = cat.map | cat.body | cat.bullet | cat.mob | cat.mobBullet | cat.mobShield - } - } - if (tech.isCloakStun) { //stun nearby mobs after exiting cloak - let isMobsAround = false - const stunRange = m.fieldDrawRadius * 1.5 - const drain = 0.15 - const stunTime = 240 - if (m.energy > drain) { - for (let i = 0, len = mob.length; i < len; ++i) { - if (Vector.magnitude(Vector.sub(mob[i].position, m.pos)) < stunRange && Matter.Query.ray(map, mob[i].position, m.pos).length === 0 && !mob[i].isBadTarget) { - isMobsAround = true - mobs.statusStun(mob[i], stunTime) - } - } - if (isMobsAround) { - m.energy -= drain - simulation.drawList.push({ - x: m.pos.x, - y: m.pos.y, - radius: stunRange, - color: "hsla(0,50%,100%,0.7)", - time: 7 - }); - } - } - } - } - - function drawField() { - m.fieldPhase += 0.007 - const wiggle = 0.15 * Math.sin(m.fieldPhase * 0.5) - ctx.beginPath(); - ctx.ellipse(m.pos.x, m.pos.y, m.fieldDrawRadius * (1 - wiggle), m.fieldDrawRadius * (1 + wiggle), m.fieldPhase, 0, 2 * Math.PI); - // if (m.fireCDcycle > m.cycle && (input.field)) {} - ctx.fillStyle = "#fff" - ctx.lineWidth = 2; - ctx.strokeStyle = "#000" - ctx.stroke() - // ctx.fillStyle = "#fff" //`rgba(0,0,0,${0.5+0.5*m.energy})`; - ctx.globalCompositeOperation = "destination-in"; - ctx.fill(); - ctx.globalCompositeOperation = "source-over"; - ctx.clip(); - } - - // const energy = Math.max(0.01, Math.min(m.energy, 1)) - if (m.isCloak) { - this.fieldRange = this.fieldRange * 0.9 + 0.1 * drawRadius - m.fieldDrawRadius = this.fieldRange * 0.88 //* Math.min(1, 0.3 + 0.5 * Math.min(1, energy * energy)); - drawField() - } else if (this.fieldRange < 3000) { - this.fieldRange += 50 - m.fieldDrawRadius = this.fieldRange //* Math.min(1, 0.3 + 0.5 * Math.min(1, energy * energy)); - drawField() - } - if (tech.isIntangible) { - if (m.isCloak) { - player.collisionFilter.mask = cat.map - let inPlayer = Matter.Query.region(mob, player.bounds) - if (inPlayer.length > 0) { - for (let i = 0; i < inPlayer.length; i++) { - if (m.energy > 0 && inPlayer[i].shield) m.energy -= 0.014; - } - } - } else { - player.collisionFilter.mask = cat.body | cat.map | cat.mob | cat.mobBullet | cat.mobShield //normal collisions - } - } - - this.drawFieldMeterCloaking() - //show sneak attack status - - if (m.cycle > m.lastKillCycle + 240) { - ctx.strokeStyle = "rgba(0,0,0,0.4)" //m.fieldMeterColor; //"rgba(255,255,0,0.2)" //ctx.strokeStyle = `rgba(0,0,255,${0.5+0.5*Math.random()})` - ctx.beginPath(); - ctx.arc(m.pos.x, m.pos.y, 28, 0, 2 * Math.PI); - ctx.lineWidth = 2 - ctx.stroke(); - } - } - } - }, - // { - // name: "phase decoherence field", - // description: "use energy to become intangible
firing and touching shields drains energy
unable to see and be seen by mobs", - // effect: () => { - // m.fieldFire = true; - // m.fieldMeterColor = "#fff"; - // m.fieldPhase = 0; - - // m.hold = function () { - // function drawField(radius) { - // radius *= Math.min(4, 0.9 + 2.2 * m.energy * m.energy); - // const rotate = m.cycle * 0.005; - // m.fieldPhase += 0.5 - 0.5 * Math.sqrt(Math.max(0.01, Math.min(m.energy, 1))); - // const off1 = 1 + 0.06 * Math.sin(m.fieldPhase); - // const off2 = 1 - 0.06 * Math.sin(m.fieldPhase); - // ctx.beginPath(); - // ctx.ellipse(m.pos.x, m.pos.y, radius * off1, radius * off2, rotate, 0, 2 * Math.PI); - // if (m.fireCDcycle > m.cycle && (input.field)) { - // ctx.lineWidth = 5; - // ctx.strokeStyle = `rgba(0, 204, 255,1)` - // ctx.stroke() - // } - // ctx.fillStyle = "#fff" //`rgba(0,0,0,${0.5+0.5*m.energy})`; - // ctx.globalCompositeOperation = "destination-in"; //in or atop - // ctx.fill(); - // ctx.globalCompositeOperation = "source-over"; - // ctx.clip(); - // } - - // m.isCloak = false //isCloak disables most uses of foundPlayer() - // player.collisionFilter.mask = cat.body | cat.map | cat.mob | cat.mobBullet | cat.mobShield //normal collisions - // if (m.isHolding) { - // if (this.fieldRange < 2000) { - // this.fieldRange += 100 - // drawField(this.fieldRange) - // } - // m.drawHold(m.holdingTarget); - // m.holding(); - // m.throwBlock(); - // } else if (input.field) { - // m.grabPowerUp(); - // m.lookForPickUp(); - - // if (m.fieldCDcycle < m.cycle) { - // // simulation.draw.bodyFill = "transparent" - // // simulation.draw.bodyStroke = "transparent" - - // const DRAIN = 0.00013 + (m.fireCDcycle > m.cycle ? 0.005 : 0) - // if (m.energy > DRAIN) { - // m.energy -= DRAIN; - // // if (m.energy < 0.001) { - // // m.fieldCDcycle = m.cycle + 120; - // // m.energy = 0; - // // m.holdingTarget = null; //clears holding target (this is so you only pick up right after the field button is released and a hold target exists) - // // } - // this.fieldRange = this.fieldRange * 0.8 + 0.2 * 160 - // drawField(this.fieldRange) - - // m.isCloak = true //isCloak disables most uses of foundPlayer() - // player.collisionFilter.mask = cat.map - - - // let inPlayer = Matter.Query.region(mob, player.bounds) - // if (inPlayer.length > 0) { - // for (let i = 0; i < inPlayer.length; i++) { - // if (inPlayer[i].shield) { - // m.energy -= 0.005; //shields drain player energy - // //draw outline of shield - // ctx.fillStyle = `rgba(140,217,255,0.5)` - // ctx.fill() - // } else if (tech.superposition && inPlayer[i].isDropPowerUp) { - // // inPlayer[i].damage(0.4 * b.dmgScale); //damage mobs inside the player - // // m.energy += 0.005; - - // mobs.statusStun(inPlayer[i], 300) - // //draw outline of mob in a few random locations to show blurriness - // const vertices = inPlayer[i].vertices; - // const off = 30 - // for (let k = 0; k < 3; k++) { - // const xOff = off * (Math.random() - 0.5) - // const yOff = off * (Math.random() - 0.5) - // ctx.beginPath(); - // ctx.moveTo(xOff + vertices[0].x, yOff + vertices[0].y); - // for (let j = 1, len = vertices.length; j < len; ++j) { - // ctx.lineTo(xOff + vertices[j].x, yOff + vertices[j].y); - // } - // ctx.lineTo(xOff + vertices[0].x, yOff + vertices[0].y); - // ctx.fillStyle = "rgba(0,0,0,0.1)" - // ctx.fill() - // } - // break; - // } - // } - // } - // } else { - // m.fieldCDcycle = m.cycle + 120; - // m.energy = 0; - // m.holdingTarget = null; //clears holding target (this is so you only pick up right after the field button is released and a hold target exists) - // drawField(this.fieldRange) - // } - // } - // } else if (m.holdingTarget && m.fieldCDcycle < m.cycle) { //holding, but field button is released - // m.pickUp(); - // if (this.fieldRange < 2000) { - // this.fieldRange += 100 - // drawField(this.fieldRange) - // } - // } else { - // // this.fieldRange = 3000 - // if (this.fieldRange < 2000 && m.holdingTarget === null) { - // this.fieldRange += 100 - // drawField(this.fieldRange) - // } - // m.holdingTarget = null; //clears holding target (this is so you only pick up right after the field button is released and a hold target exists) - // } - - // if (m.energy < m.maxEnergy) { - // m.energy += m.fieldRegen; - // const xOff = m.pos.x - m.radius * m.maxEnergy - // const yOff = m.pos.y - 50 - // ctx.fillStyle = "rgba(0, 0, 0, 0.3)"; - // ctx.fillRect(xOff, yOff, 60 * m.maxEnergy, 10); - // ctx.fillStyle = m.fieldMeterColor; - // ctx.fillRect(xOff, yOff, 60 * m.energy, 10); - // ctx.beginPath() - // ctx.rect(xOff, yOff, 60 * m.maxEnergy, 10); - // ctx.strokeStyle = "rgb(0, 0, 0)"; - // ctx.lineWidth = 1; - // ctx.stroke(); - // } - // if (m.energy < 0) m.energy = 0 - // } - // } - // }, - { - name: "pilot wave", - description: "use energy to push blocks with your mouse
blocks can't collide with intangible mobs
field radius decreases out of line of sight", - effect: () => { - m.fieldPhase = 0; - m.fieldPosition = { - x: simulation.mouseInGame.x, - y: simulation.mouseInGame.y - } - m.lastFieldPosition = { - x: simulation.mouseInGame.x, - y: simulation.mouseInGame.y - } - m.fieldOn = false; - m.fieldRadius = 0; - m.drop(); - m.hold = function() { - if (input.field) { - if (m.fieldCDcycle < m.cycle) { - const scale = 25 - const bounds = { - min: { - x: m.fieldPosition.x - scale, - y: m.fieldPosition.y - scale - }, - max: { - x: m.fieldPosition.x + scale, - y: m.fieldPosition.y + scale - } - } - const isInMap = Matter.Query.region(map, bounds).length - // const isInMap = Matter.Query.point(map, m.fieldPosition).length - - if (!m.fieldOn) { // if field was off, and it starting up, teleport to new mouse location - m.fieldOn = true; - // m.fieldPosition = { //smooth the mouse position, set to starting at player - // x: m.pos.x, - // y: m.pos.y - // } - m.fieldPosition = { //smooth the mouse position, set to mouse's current location - x: simulation.mouseInGame.x, - y: simulation.mouseInGame.y - } - m.lastFieldPosition = { //used to find velocity of field changes - x: m.fieldPosition.x, - y: m.fieldPosition.y - } - } else { //when field is on it smoothly moves towards the mouse - m.lastFieldPosition = { //used to find velocity of field changes - x: m.fieldPosition.x, - y: m.fieldPosition.y - } - const smooth = isInMap ? 0.985 : 0.96; - m.fieldPosition = { //smooth the mouse position - x: m.fieldPosition.x * smooth + simulation.mouseInGame.x * (1 - smooth), - y: m.fieldPosition.y * smooth + simulation.mouseInGame.y * (1 - smooth), - } - } - - //grab power ups into the field - for (let i = 0, len = powerUp.length; i < len; ++i) { - const dxP = m.fieldPosition.x - powerUp[i].position.x; - const dyP = m.fieldPosition.y - powerUp[i].position.y; - const dist2 = dxP * dxP + dyP * dyP + 200; - // float towards field if looking at and in range or if very close to player - if ( - dist2 < m.fieldRadius * m.fieldRadius && - (m.lookingAt(powerUp[i]) || dist2 < 16000) - ) { - powerUp[i].force.x += 0.05 * (dxP / Math.sqrt(dist2)) * powerUp[i].mass; - powerUp[i].force.y += 0.05 * (dyP / Math.sqrt(dist2)) * powerUp[i].mass - powerUp[i].mass * simulation.g; //negate gravity - //extra friction - Matter.Body.setVelocity(powerUp[i], { - x: powerUp[i].velocity.x * 0.11, - y: powerUp[i].velocity.y * 0.11 - }); + //grab power ups + for (let i = 0, len = powerUp.length; i < len; ++i) { if ( - dist2 < 5000 && + Vector.magnitudeSquared(Vector.sub(m.pos, powerUp[i].position)) < 100000 && !simulation.isChoosing && (powerUp[i].name !== "heal" || m.health !== m.maxHealth || tech.isOverHeal) - // (powerUp[i].name !== "heal" || m.health < 0.94 * m.maxHealth) - // (powerUp[i].name !== "ammo" || b.guns[b.activeGun].ammo !== Infinity) - ) { //use power up if it is close enough + ) { powerUps.onPickUp(powerUp[i]); powerUp[i].effect(); Matter.Composite.remove(engine.world, powerUp[i]); powerUp.splice(i, 1); - // m.fieldRadius += 50 break; //because the array order is messed up after splice } } - } - //grab power ups normally too - m.grabPowerUp(); - - if (m.energy > 0.01) { - //find mouse velocity - const diff = Vector.sub(m.fieldPosition, m.lastFieldPosition) - const speed = Vector.magnitude(diff) - const velocity = Vector.mult(Vector.normalise(diff), Math.min(speed, 60)) //limit velocity - let radius, radiusSmooth - if (Matter.Query.ray(map, m.fieldPosition, player.position).length) { //is there something block the player's view of the field - radius = 0 - radiusSmooth = Math.max(0, isInMap ? 0.96 - 0.02 * speed : 0.995); //0.99 - } else { - radius = Math.max(50, 250 - 2 * speed) - radiusSmooth = 0.97 - } - m.fieldRadius = m.fieldRadius * radiusSmooth + radius * (1 - radiusSmooth) - - for (let i = 0, len = body.length; i < len; ++i) { - if (Vector.magnitude(Vector.sub(body[i].position, m.fieldPosition)) < m.fieldRadius && !body[i].isNotHoldable) { - const DRAIN = speed * body[i].mass * 0.000006 // * (1 + m.energy * m.energy) //drain more energy when you have more energy - if (m.energy > DRAIN) { - m.energy -= DRAIN; - Matter.Body.setVelocity(body[i], velocity); //give block mouse velocity - Matter.Body.setAngularVelocity(body[i], body[i].angularVelocity * 0.8) - // body[i].force.y -= body[i].mass * simulation.g; //remove gravity effects - //blocks drift towards center of pilot wave - const sub = Vector.sub(m.fieldPosition, body[i].position) - const push = Vector.mult(Vector.normalise(sub), 0.0001 * body[i].mass * Vector.magnitude(sub)) - body[i].force.x += push.x - body[i].force.y += push.y - body[i].mass * simulation.g //remove gravity effects - // if (body[i].collisionFilter.category !== cat.bullet) { - // body[i].collisionFilter.category = cat.bullet; - // } - } else { - m.fieldCDcycle = m.cycle + 120; - m.fieldOn = false - m.fieldRadius = 0 - break + if (!(this.rewindCount % 30)) { + if (tech.isRewindBot) { + for (let i = 0; i < tech.isRewindBot; i++) { + b.randomBot(m.pos, false, false) + bullet[bullet.length - 1].endCycle = simulation.cycle + 480 + Math.floor(120 * Math.random()) //8-9 seconds } } - } - - // m.holdingTarget.collisionFilter.category = cat.bullet; - // m.holdingTarget.collisionFilter.mask = cat.map | cat.body | cat.bullet | cat.mob | cat.mobBullet | cat.mobShield; - // //check every second to see if player is away from thrown body, and make solid - // const solid = function(that) { - // const dx = that.position.x - player.position.x; - // const dy = that.position.y - player.position.y; - // if (that.speed < 3 && dx * dx + dy * dy > 10000 && that !== m.holdingTarget) { - // that.collisionFilter.category = cat.body; //make solid - // that.collisionFilter.mask = cat.player | cat.map | cat.body | cat.bullet | cat.mob | cat.mobBullet; //can hit player now - // } else { - // setTimeout(solid, 40, that); - // } - // }; - // setTimeout(solid, 200, m.holdingTarget); - - - - if (tech.isFreezeMobs) { - for (let i = 0, len = mob.length; i < len; ++i) { - if (Vector.magnitude(Vector.sub(mob[i].position, m.fieldPosition)) < m.fieldRadius) { - mobs.statusSlow(mob[i], 180) - } + if (tech.isRewindGrenade) { + b.grenade(m.pos, this.rewindCount) //Math.PI / 2 + const who = bullet[bullet.length - 1] + // Matter.Body.setVelocity(who, { + // x: 0, + // y: 0 + // }); + who.endCycle = simulation.cycle + 60 + // if (tech.isVacuumBomb) { + // Matter.Body.setVelocity(who, { + // x: who.velocity.x * 0.5, + // y: who.velocity.y * 0.5 + // }); + // } else if (tech.isRPG) { + // who.endCycle = simulation.cycle + 10 + // } else if (tech.isNeutronBomb) { + // Matter.Body.setVelocity(who, { + // x: who.velocity.x * 0.3, + // y: who.velocity.y * 0.3 + // }); + // } else { + // Matter.Body.setVelocity(who, { + // x: who.velocity.x * 0.5, + // y: who.velocity.y * 0.5 + // }); + // who.endCycle = simulation.cycle + 30 + // } } - } - ctx.beginPath(); - const rotate = m.cycle * 0.008; - m.fieldPhase += 0.2 // - 0.5 * Math.sqrt(Math.min(m.energy, 1)); - const off1 = 1 + 0.06 * Math.sin(m.fieldPhase); - const off2 = 1 - 0.06 * Math.sin(m.fieldPhase); - ctx.beginPath(); - ctx.ellipse(m.fieldPosition.x, m.fieldPosition.y, 1.2 * m.fieldRadius * off1, 1.2 * m.fieldRadius * off2, rotate, 0, 2 * Math.PI); - ctx.globalCompositeOperation = "exclusion"; //"exclusion" "difference"; - ctx.fillStyle = "#fff"; //"#eef"; - ctx.fill(); - ctx.globalCompositeOperation = "source-over"; - ctx.beginPath(); - ctx.ellipse(m.fieldPosition.x, m.fieldPosition.y, 1.2 * m.fieldRadius * off1, 1.2 * m.fieldRadius * off2, rotate, 0, 2 * Math.PI * m.energy / m.maxEnergy); - ctx.strokeStyle = "#000"; - ctx.lineWidth = 4; - ctx.stroke(); - } else { - m.fieldCDcycle = m.cycle + 120; - m.fieldOn = false - m.fieldRadius = 0 + + } } - } else { - m.grabPowerUp(); } + } else if (m.holdingTarget && m.fieldCDcycle < m.cycle) { //holding, but field button is released + m.pickUp(); + this.rewindCount = 0; } else { - m.fieldOn = false - m.fieldRadius = 0 + m.holdingTarget = null; //clears holding target (this is so you only pick up right after the field button is released and a hold target exists) + this.rewindCount = 0; } m.drawFieldMeter() + + + + + // // console.log(this.rewindCount) + // if (input.field && m.fieldCDcycle < m.cycle) { //button has been held down + // if (m.isHolding) { + // m.drawHold(m.holdingTarget); + // m.holding(); + // m.throwBlock(); + // } else { + // m.grabPowerUp(); + // m.lookForPickUp(); + // if (!m.holdingTarget) { + // this.rewindCount += 8; + // const DRAIN = 0.001 + // let history = m.history[(m.cycle - this.rewindCount) % 600] + // if (this.rewindCount > 599 || m.energy < DRAIN) { + // this.rewindCount = 0; + // m.resetHistory(); + // } else { + // // m.grabPowerUp(); //a second grab power up to make the power ups easier to grab, and they more fast which matches the time theme + // m.energy -= DRAIN + // if (m.immuneCycle < m.cycle + 30) m.immuneCycle = m.cycle + 30; //player is immune to damage for 30 cycles + // Matter.Body.setPosition(player, history.position); + // Matter.Body.setVelocity(player, { x: history.velocity.x, y: history.velocity.y }); + // if (m.health < history.health) { + // m.health = history.health + // m.displayHealth(); + // } + // m.yOff = history.yOff + // if (m.yOff < 48) { + // m.doCrouch() + // } else { + // m.undoCrouch() + // } + // //grab power ups + // for (let i = 0, len = powerUp.length; i < len; ++i) { + // if ( + // Vector.magnitudeSquared(Vector.sub(m.pos, powerUp[i].position)) < 100000 && + // !simulation.isChoosing && + // (powerUp[i].name !== "heal" || m.health !== m.maxHealth || tech.isOverHeal) + // ) { + // powerUps.onPickUp(powerUp[i]); + // powerUp[i].effect(); + // Matter.Composite.remove(engine.world, powerUp[i]); + // powerUp.splice(i, 1); + // break; //because the array order is messed up after splice + // } + // } + // } + // } + // } + // } else { //button is held the first time + // this.rewindCount = 0; + // if (m.holdingTarget && m.fieldCDcycle < m.cycle) { + // m.pickUp(); + // } else { + // m.holdingTarget = null; //clears holding target (this is so you only pick up right after the field button is released and a hold target exists) + // } + // } + + } + } else { + m.fieldFire = true; + m.isBodiesAsleep = false; + m.drain = 0.0005 + m.hold = function () { + if (m.isHolding) { + m.wakeCheck(); + m.drawHold(m.holdingTarget); + m.holding(); + m.throwBlock(); + } else if (input.field && m.fieldCDcycle < m.cycle) { + m.grabPowerUp(); + m.lookForPickUp(); + + m.drain += 0.0000025 //also increases inside tech.isTimeSkip + if (m.energy > m.drain) { + m.energy -= m.drain; + if (m.energy < m.drain) { + m.fieldCDcycle = m.cycle + 120; + m.energy = 0; + m.wakeCheck(); + } + //draw field everywhere + ctx.globalCompositeOperation = "saturation" + ctx.fillStyle = "#ccc"; + ctx.fillRect(-100000, -100000, 200000, 200000) + ctx.globalCompositeOperation = "source-over" + //stop time + m.isBodiesAsleep = true; + + function sleep(who) { + for (let i = 0, len = who.length; i < len; ++i) { + if (!who[i].isSleeping) { + who[i].storeVelocity = who[i].velocity + who[i].storeAngularVelocity = who[i].angularVelocity + } + Matter.Sleeping.set(who[i], true) + } + } + sleep(mob); + sleep(body); + sleep(bullet); + + simulation.cycle--; //pause all functions that depend on game cycle increasing + if (tech.isTimeSkip) { + m.immuneCycle = 0; + m.drain += 0.0000025 + m.regenEnergy(); //immunity disables normal regen, so turn off immunity for just this function + m.immuneCycle = m.cycle + 10; + simulation.isTimeSkipping = true; + m.cycle++; + simulation.gravity(); + if (tech.isFireMoveLock && input.fire) { + player.force.x = 0 + player.force.y = 0 + } + Engine.update(engine, simulation.delta); + m.move(); + simulation.checks(); + m.walk_cycle += m.flipLegs * m.Vx; + b.fire(); + b.bulletDo(); + simulation.isTimeSkipping = false; + } + } else { //holding, but field button is released + m.wakeCheck(); + } + } else if (m.holdingTarget && m.fieldCDcycle < m.cycle) { //holding, but field button is released + m.wakeCheck(); + m.pickUp(); + } else { + if (m.drain > 0.0005) m.drain -= 0.000005 //return drain to base level + m.wakeCheck(); + m.holdingTarget = null; //clears holding target (this is so you only pick up right after the field button is released and a hold target exists) + } + // console.log(m.drain.toFixed(6)) + m.drawFieldMeter() } } }, - { - name: "wormhole", - description: "use energy to tunnel through a wormhole
wormholes attract blocks and power ups
7% chance to duplicate spawned power ups", //
bullets may also traverse wormholes - effect: function() { - m.duplicateChance = 0.07 - m.fieldRange = 0 - powerUps.setDupChance(); //needed after adjusting duplication chance + effect() { + // m.fieldMeterColor = "#000" + this.set(); + } + }, + { + name: "metamaterial cloaking", //"weak photonic coupling" "electromagnetically induced transparency" "optical non-coupling" "slow light field" "electro-optic transparency" + description: "when not firing activate a cloaking effect
+333% damage if a mob hasn't recently died
collisions do 50% less harm when cloaked", + effect: () => { + m.fieldFire = true; + m.fieldMeterColor = "#333"; + m.eyeFillColor = m.fieldMeterColor + // m.eyeFillColor = '#333' + m.fieldPhase = 0; + m.isCloak = false + // m.fieldDamage = 2.46 // 1 + 146/100 + m.fieldDrawRadius = 0 + m.isSneakAttack = true; + const drawRadius = 900 - m.hold = function() { - // m.hole = { //this is reset with each new field, but I'm leaving it here for reference - // isOn: false, - // isReady: true, - // pos1: {x: 0,y: 0}, - // pos2: {x: 0,y: 0}, - // angle: 0, - // unit:{x:0,y:0}, - // } - if (m.hole.isOn) { - // draw holes - m.fieldRange = 0.97 * m.fieldRange + 0.03 * (50 + 10 * Math.sin(simulation.cycle * 0.025)) - const semiMajorAxis = m.fieldRange + 30 - const edge1a = Vector.add(Vector.mult(m.hole.unit, semiMajorAxis), m.hole.pos1) - const edge1b = Vector.add(Vector.mult(m.hole.unit, -semiMajorAxis), m.hole.pos1) - const edge2a = Vector.add(Vector.mult(m.hole.unit, semiMajorAxis), m.hole.pos2) - const edge2b = Vector.add(Vector.mult(m.hole.unit, -semiMajorAxis), m.hole.pos2) - ctx.beginPath(); - ctx.moveTo(edge1a.x, edge1a.y) - ctx.bezierCurveTo(m.hole.pos1.x, m.hole.pos1.y, m.hole.pos2.x, m.hole.pos2.y, edge2a.x, edge2a.y); - ctx.lineTo(edge2b.x, edge2b.y) - ctx.bezierCurveTo(m.hole.pos2.x, m.hole.pos2.y, m.hole.pos1.x, m.hole.pos1.y, edge1b.x, edge1b.y); - ctx.fillStyle = `rgba(255,255,255,${200 / m.fieldRange / m.fieldRange})` //"rgba(0,0,0,0.1)" - ctx.fill(); - ctx.beginPath(); - ctx.ellipse(m.hole.pos1.x, m.hole.pos1.y, m.fieldRange, semiMajorAxis, m.hole.angle, 0, 2 * Math.PI) - ctx.ellipse(m.hole.pos2.x, m.hole.pos2.y, m.fieldRange, semiMajorAxis, m.hole.angle, 0, 2 * Math.PI) - ctx.fillStyle = `rgba(255,255,255,${32 / m.fieldRange})` - ctx.fill(); + m.hold = function () { + // console.log(m.holdingTarget) + if (m.isHolding) { + m.drawHold(m.holdingTarget); + m.holding(); + m.throwBlock(); + } else if (input.field && m.fieldCDcycle < m.cycle) { //not hold and field button is pressed + m.grabPowerUp(); + m.lookForPickUp(); + } else if (m.holdingTarget && m.fieldCDcycle < m.cycle) { //holding target exists, and field button is not pressed + m.pickUp(); + } else { + m.holdingTarget = null; //clears holding target (this is so you only pick up right after the field button is released and a hold target exists) + } - //suck power ups - for (let i = 0, len = powerUp.length; i < len; ++i) { - //which hole is closer - const dxP1 = m.hole.pos1.x - powerUp[i].position.x; - const dyP1 = m.hole.pos1.y - powerUp[i].position.y; - const dxP2 = m.hole.pos2.x - powerUp[i].position.x; - const dyP2 = m.hole.pos2.y - powerUp[i].position.y; - let dxP, dyP, dist2 - if (dxP1 * dxP1 + dyP1 * dyP1 < dxP2 * dxP2 + dyP2 * dyP2) { - dxP = dxP1 - dyP = dyP1 - } else { - dxP = dxP2 - dyP = dyP2 + //not shooting (or using field) enable cloak + if (m.energy < 0.05 && m.fireCDcycle < m.cycle && !input.fire) m.fireCDcycle = m.cycle + if (m.fireCDcycle + 30 < m.cycle && !input.fire) { //automatically cloak if not firing + if (!m.isCloak) { + m.isCloak = true //enter cloak + if (tech.isIntangible) { + for (let i = 0; i < bullet.length; i++) { + if (bullet[i].botType && bullet[i].botType !== "orbit") bullet[i].collisionFilter.mask = cat.map | cat.bullet | cat.mobBullet | cat.mobShield } - dist2 = dxP * dxP + dyP * dyP; - if (dist2 < 600000) { //&& !(m.health === m.maxHealth && powerUp[i].name === "heal") - powerUp[i].force.x += 4 * (dxP / dist2) * powerUp[i].mass; // float towards hole - powerUp[i].force.y += 4 * (dyP / dist2) * powerUp[i].mass - powerUp[i].mass * simulation.g; //negate gravity - Matter.Body.setVelocity(powerUp[i], { //extra friction - x: powerUp[i].velocity.x * 0.05, - y: powerUp[i].velocity.y * 0.05 + } + } + } else if (m.isCloak) { //exit cloak + m.isCloak = false + if (tech.isIntangible) { + for (let i = 0; i < bullet.length; i++) { + if (bullet[i].botType && bullet[i].botType !== "orbit") bullet[i].collisionFilter.mask = cat.map | cat.body | cat.bullet | cat.mob | cat.mobBullet | cat.mobShield + } + } + if (tech.isCloakStun) { //stun nearby mobs after exiting cloak + let isMobsAround = false + const stunRange = m.fieldDrawRadius * 1.5 + const drain = 0.15 + const stunTime = 240 + if (m.energy > drain) { + for (let i = 0, len = mob.length; i < len; ++i) { + if (Vector.magnitude(Vector.sub(mob[i].position, m.pos)) < stunRange && Matter.Query.ray(map, mob[i].position, m.pos).length === 0 && !mob[i].isBadTarget) { + isMobsAround = true + mobs.statusStun(mob[i], stunTime) + } + } + if (isMobsAround) { + m.energy -= drain + simulation.drawList.push({ + x: m.pos.x, + y: m.pos.y, + radius: stunRange, + color: "hsla(0,50%,100%,0.7)", + time: 7 }); - if (dist2 < 1000 && !simulation.isChoosing) { //use power up if it is close enough + } + } + } + } - // if (true) { //AoE radiation effect - // const range = 800 + function drawField() { + m.fieldPhase += 0.007 + const wiggle = 0.15 * Math.sin(m.fieldPhase * 0.5) + ctx.beginPath(); + ctx.ellipse(m.pos.x, m.pos.y, m.fieldDrawRadius * (1 - wiggle), m.fieldDrawRadius * (1 + wiggle), m.fieldPhase, 0, 2 * Math.PI); + // if (m.fireCDcycle > m.cycle && (input.field)) {} + ctx.fillStyle = "#fff" + ctx.lineWidth = 2; + ctx.strokeStyle = "#000" + ctx.stroke() + // ctx.fillStyle = "#fff" //`rgba(0,0,0,${0.5+0.5*m.energy})`; + ctx.globalCompositeOperation = "destination-in"; + ctx.fill(); + ctx.globalCompositeOperation = "source-over"; + ctx.clip(); + } - // for (let i = 0, len = mob.length; i < len; ++i) { - // if (mob[i].alive && !mob[i].isShielded) { - // dist = Vector.magnitude(Vector.sub(powerUp[i].position, mob[i].position)) - mob[i].radius; - // if (dist < range) mobs.statusDoT(mob[i], 0.5) //apply radiation damage status effect on direct hits - // } - // } + // const energy = Math.max(0.01, Math.min(m.energy, 1)) + if (m.isCloak) { + this.fieldRange = this.fieldRange * 0.9 + 0.1 * drawRadius + m.fieldDrawRadius = this.fieldRange * 0.88 //* Math.min(1, 0.3 + 0.5 * Math.min(1, energy * energy)); + drawField() + } else if (this.fieldRange < 3000) { + this.fieldRange += 50 + m.fieldDrawRadius = this.fieldRange //* Math.min(1, 0.3 + 0.5 * Math.min(1, energy * energy)); + drawField() + } + if (tech.isIntangible) { + if (m.isCloak) { + player.collisionFilter.mask = cat.map + let inPlayer = Matter.Query.region(mob, player.bounds) + if (inPlayer.length > 0) { + for (let i = 0; i < inPlayer.length; i++) { + if (m.energy > 0 && inPlayer[i].shield) m.energy -= 0.014; + } + } + } else { + player.collisionFilter.mask = cat.body | cat.map | cat.mob | cat.mobBullet | cat.mobShield //normal collisions + } + } - // simulation.drawList.push({ - // x: powerUp[i].position.x, - // y: powerUp[i].position.y, - // radius: range, - // color: "rgba(0,150,200,0.3)", - // time: 4 - // }); - // } + this.drawFieldMeterCloaking() + //show sneak attack status - m.fieldRange *= 0.8 + if (m.cycle > m.lastKillCycle + 240) { + ctx.strokeStyle = "rgba(0,0,0,0.4)" //m.fieldMeterColor; //"rgba(255,255,0,0.2)" //ctx.strokeStyle = `rgba(0,0,255,${0.5+0.5*Math.random()})` + ctx.beginPath(); + ctx.arc(m.pos.x, m.pos.y, 28, 0, 2 * Math.PI); + ctx.lineWidth = 2 + ctx.stroke(); + } + } + } + }, + // { + // name: "phase decoherence field", + // description: "use energy to become intangible
firing and touching shields drains energy
unable to see and be seen by mobs", + // effect: () => { + // m.fieldFire = true; + // m.fieldMeterColor = "#fff"; + // m.fieldPhase = 0; + + // m.hold = function () { + // function drawField(radius) { + // radius *= Math.min(4, 0.9 + 2.2 * m.energy * m.energy); + // const rotate = m.cycle * 0.005; + // m.fieldPhase += 0.5 - 0.5 * Math.sqrt(Math.max(0.01, Math.min(m.energy, 1))); + // const off1 = 1 + 0.06 * Math.sin(m.fieldPhase); + // const off2 = 1 - 0.06 * Math.sin(m.fieldPhase); + // ctx.beginPath(); + // ctx.ellipse(m.pos.x, m.pos.y, radius * off1, radius * off2, rotate, 0, 2 * Math.PI); + // if (m.fireCDcycle > m.cycle && (input.field)) { + // ctx.lineWidth = 5; + // ctx.strokeStyle = `rgba(0, 204, 255,1)` + // ctx.stroke() + // } + // ctx.fillStyle = "#fff" //`rgba(0,0,0,${0.5+0.5*m.energy})`; + // ctx.globalCompositeOperation = "destination-in"; //in or atop + // ctx.fill(); + // ctx.globalCompositeOperation = "source-over"; + // ctx.clip(); + // } + + // m.isCloak = false //isCloak disables most uses of foundPlayer() + // player.collisionFilter.mask = cat.body | cat.map | cat.mob | cat.mobBullet | cat.mobShield //normal collisions + // if (m.isHolding) { + // if (this.fieldRange < 2000) { + // this.fieldRange += 100 + // drawField(this.fieldRange) + // } + // m.drawHold(m.holdingTarget); + // m.holding(); + // m.throwBlock(); + // } else if (input.field) { + // m.grabPowerUp(); + // m.lookForPickUp(); + + // if (m.fieldCDcycle < m.cycle) { + // // simulation.draw.bodyFill = "transparent" + // // simulation.draw.bodyStroke = "transparent" + + // const DRAIN = 0.00013 + (m.fireCDcycle > m.cycle ? 0.005 : 0) + // if (m.energy > DRAIN) { + // m.energy -= DRAIN; + // // if (m.energy < 0.001) { + // // m.fieldCDcycle = m.cycle + 120; + // // m.energy = 0; + // // m.holdingTarget = null; //clears holding target (this is so you only pick up right after the field button is released and a hold target exists) + // // } + // this.fieldRange = this.fieldRange * 0.8 + 0.2 * 160 + // drawField(this.fieldRange) + + // m.isCloak = true //isCloak disables most uses of foundPlayer() + // player.collisionFilter.mask = cat.map + + + // let inPlayer = Matter.Query.region(mob, player.bounds) + // if (inPlayer.length > 0) { + // for (let i = 0; i < inPlayer.length; i++) { + // if (inPlayer[i].shield) { + // m.energy -= 0.005; //shields drain player energy + // //draw outline of shield + // ctx.fillStyle = `rgba(140,217,255,0.5)` + // ctx.fill() + // } else if (tech.superposition && inPlayer[i].isDropPowerUp) { + // // inPlayer[i].damage(0.4 * b.dmgScale); //damage mobs inside the player + // // m.energy += 0.005; + + // mobs.statusStun(inPlayer[i], 300) + // //draw outline of mob in a few random locations to show blurriness + // const vertices = inPlayer[i].vertices; + // const off = 30 + // for (let k = 0; k < 3; k++) { + // const xOff = off * (Math.random() - 0.5) + // const yOff = off * (Math.random() - 0.5) + // ctx.beginPath(); + // ctx.moveTo(xOff + vertices[0].x, yOff + vertices[0].y); + // for (let j = 1, len = vertices.length; j < len; ++j) { + // ctx.lineTo(xOff + vertices[j].x, yOff + vertices[j].y); + // } + // ctx.lineTo(xOff + vertices[0].x, yOff + vertices[0].y); + // ctx.fillStyle = "rgba(0,0,0,0.1)" + // ctx.fill() + // } + // break; + // } + // } + // } + // } else { + // m.fieldCDcycle = m.cycle + 120; + // m.energy = 0; + // m.holdingTarget = null; //clears holding target (this is so you only pick up right after the field button is released and a hold target exists) + // drawField(this.fieldRange) + // } + // } + // } else if (m.holdingTarget && m.fieldCDcycle < m.cycle) { //holding, but field button is released + // m.pickUp(); + // if (this.fieldRange < 2000) { + // this.fieldRange += 100 + // drawField(this.fieldRange) + // } + // } else { + // // this.fieldRange = 3000 + // if (this.fieldRange < 2000 && m.holdingTarget === null) { + // this.fieldRange += 100 + // drawField(this.fieldRange) + // } + // m.holdingTarget = null; //clears holding target (this is so you only pick up right after the field button is released and a hold target exists) + // } + + // if (m.energy < m.maxEnergy) { + // m.energy += m.fieldRegen; + // const xOff = m.pos.x - m.radius * m.maxEnergy + // const yOff = m.pos.y - 50 + // ctx.fillStyle = "rgba(0, 0, 0, 0.3)"; + // ctx.fillRect(xOff, yOff, 60 * m.maxEnergy, 10); + // ctx.fillStyle = m.fieldMeterColor; + // ctx.fillRect(xOff, yOff, 60 * m.energy, 10); + // ctx.beginPath() + // ctx.rect(xOff, yOff, 60 * m.maxEnergy, 10); + // ctx.strokeStyle = "rgb(0, 0, 0)"; + // ctx.lineWidth = 1; + // ctx.stroke(); + // } + // if (m.energy < 0) m.energy = 0 + // } + // } + // }, + { + name: "pilot wave", + description: "use energy to push blocks with your mouse
blocks can't collide with intangible mobs
field radius decreases out of line of sight", + effect: () => { + m.fieldPhase = 0; + m.fieldPosition = { + x: simulation.mouseInGame.x, + y: simulation.mouseInGame.y + } + m.lastFieldPosition = { + x: simulation.mouseInGame.x, + y: simulation.mouseInGame.y + } + m.fieldOn = false; + m.fieldRadius = 0; + m.drop(); + m.hold = function () { + if (input.field) { + if (m.fieldCDcycle < m.cycle) { + const scale = 25 + const bounds = { + min: { + x: m.fieldPosition.x - scale, + y: m.fieldPosition.y - scale + }, + max: { + x: m.fieldPosition.x + scale, + y: m.fieldPosition.y + scale + } + } + const isInMap = Matter.Query.region(map, bounds).length + // const isInMap = Matter.Query.point(map, m.fieldPosition).length + + if (!m.fieldOn) { // if field was off, and it starting up, teleport to new mouse location + m.fieldOn = true; + // m.fieldPosition = { //smooth the mouse position, set to starting at player + // x: m.pos.x, + // y: m.pos.y + // } + m.fieldPosition = { //smooth the mouse position, set to mouse's current location + x: simulation.mouseInGame.x, + y: simulation.mouseInGame.y + } + m.lastFieldPosition = { //used to find velocity of field changes + x: m.fieldPosition.x, + y: m.fieldPosition.y + } + } else { //when field is on it smoothly moves towards the mouse + m.lastFieldPosition = { //used to find velocity of field changes + x: m.fieldPosition.x, + y: m.fieldPosition.y + } + const smooth = isInMap ? 0.985 : 0.96; + m.fieldPosition = { //smooth the mouse position + x: m.fieldPosition.x * smooth + simulation.mouseInGame.x * (1 - smooth), + y: m.fieldPosition.y * smooth + simulation.mouseInGame.y * (1 - smooth), + } + } + + //grab power ups into the field + for (let i = 0, len = powerUp.length; i < len; ++i) { + const dxP = m.fieldPosition.x - powerUp[i].position.x; + const dyP = m.fieldPosition.y - powerUp[i].position.y; + const dist2 = dxP * dxP + dyP * dyP + 200; + // float towards field if looking at and in range or if very close to player + if ( + dist2 < m.fieldRadius * m.fieldRadius && + (m.lookingAt(powerUp[i]) || dist2 < 16000) + ) { + powerUp[i].force.x += 0.05 * (dxP / Math.sqrt(dist2)) * powerUp[i].mass; + powerUp[i].force.y += 0.05 * (dyP / Math.sqrt(dist2)) * powerUp[i].mass - powerUp[i].mass * simulation.g; //negate gravity + //extra friction + Matter.Body.setVelocity(powerUp[i], { + x: powerUp[i].velocity.x * 0.11, + y: powerUp[i].velocity.y * 0.11 + }); + if ( + dist2 < 5000 && + !simulation.isChoosing && + (powerUp[i].name !== "heal" || m.health !== m.maxHealth || tech.isOverHeal) + // (powerUp[i].name !== "heal" || m.health < 0.94 * m.maxHealth) + // (powerUp[i].name !== "ammo" || b.guns[b.activeGun].ammo !== Infinity) + ) { //use power up if it is close enough powerUps.onPickUp(powerUp[i]); powerUp[i].effect(); Matter.Composite.remove(engine.world, powerUp[i]); powerUp.splice(i, 1); + // m.fieldRadius += 50 break; //because the array order is messed up after splice } } } - //suck and shrink blocks - const suckRange = 500 - const shrinkRange = 100 - const shrinkScale = 0.97; - const slowScale = 0.9 - for (let i = 0, len = body.length; i < len; i++) { - if (!body[i].isNotHoldable) { - const dist1 = Vector.magnitude(Vector.sub(m.hole.pos1, body[i].position)) - const dist2 = Vector.magnitude(Vector.sub(m.hole.pos2, body[i].position)) - if (dist1 < dist2) { - if (dist1 < suckRange) { - const pull = Vector.mult(Vector.normalise(Vector.sub(m.hole.pos1, body[i].position)), 1) - const slow = Vector.mult(body[i].velocity, slowScale) - Matter.Body.setVelocity(body[i], Vector.add(slow, pull)); - //shrink - if (Vector.magnitude(Vector.sub(m.hole.pos1, body[i].position)) < shrinkRange) { - Matter.Body.scale(body[i], shrinkScale, shrinkScale); - if (body[i].mass < 0.05) { - Matter.Composite.remove(engine.world, body[i]); - body.splice(i, 1); - m.fieldRange *= 0.8 - if (tech.isWormholeEnergy) m.energy += 0.63 - if (tech.isWormholeSpores) { //pandimensional spermia - for (let i = 0, len = Math.ceil(3 * (tech.isSporeWorm ? 0.5 : 1) * Math.random()); i < len; i++) { - if (tech.isSporeWorm) { - b.worm(Vector.add(m.hole.pos2, Vector.rotate({ - x: m.fieldRange * 0.4, - y: 0 - }, 2 * Math.PI * Math.random()))) - Matter.Body.setVelocity(bullet[bullet.length - 1], Vector.mult(Vector.rotate(m.hole.unit, Math.PI / 2), -5)); - } else { - b.spore(Vector.add(m.hole.pos2, Vector.rotate({ - x: m.fieldRange * 0.4, - y: 0 - }, 2 * Math.PI * Math.random()))) - Matter.Body.setVelocity(bullet[bullet.length - 1], Vector.mult(Vector.rotate(m.hole.unit, Math.PI / 2), -15)); - } - } - } - break - } - } + //grab power ups normally too + m.grabPowerUp(); + + if (m.energy > 0.01) { + //find mouse velocity + const diff = Vector.sub(m.fieldPosition, m.lastFieldPosition) + const speed = Vector.magnitude(diff) + const velocity = Vector.mult(Vector.normalise(diff), Math.min(speed, 60)) //limit velocity + let radius, radiusSmooth + if (Matter.Query.ray(map, m.fieldPosition, player.position).length) { //is there something block the player's view of the field + radius = 0 + radiusSmooth = Math.max(0, isInMap ? 0.96 - 0.02 * speed : 0.995); //0.99 + } else { + radius = Math.max(50, 250 - 2 * speed) + radiusSmooth = 0.97 + } + m.fieldRadius = m.fieldRadius * radiusSmooth + radius * (1 - radiusSmooth) + + for (let i = 0, len = body.length; i < len; ++i) { + if (Vector.magnitude(Vector.sub(body[i].position, m.fieldPosition)) < m.fieldRadius && !body[i].isNotHoldable) { + const DRAIN = speed * body[i].mass * 0.000006 // * (1 + m.energy * m.energy) //drain more energy when you have more energy + if (m.energy > DRAIN) { + m.energy -= DRAIN; + Matter.Body.setVelocity(body[i], velocity); //give block mouse velocity + Matter.Body.setAngularVelocity(body[i], body[i].angularVelocity * 0.8) + // body[i].force.y -= body[i].mass * simulation.g; //remove gravity effects + //blocks drift towards center of pilot wave + const sub = Vector.sub(m.fieldPosition, body[i].position) + const push = Vector.mult(Vector.normalise(sub), 0.0001 * body[i].mass * Vector.magnitude(sub)) + body[i].force.x += push.x + body[i].force.y += push.y - body[i].mass * simulation.g //remove gravity effects + // if (body[i].collisionFilter.category !== cat.bullet) { + // body[i].collisionFilter.category = cat.bullet; + // } + } else { + m.fieldCDcycle = m.cycle + 120; + m.fieldOn = false + m.fieldRadius = 0 + break } - } else if (dist2 < suckRange) { - const pull = Vector.mult(Vector.normalise(Vector.sub(m.hole.pos2, body[i].position)), 1) + } + } + + + // m.holdingTarget.collisionFilter.category = cat.bullet; + // m.holdingTarget.collisionFilter.mask = cat.map | cat.body | cat.bullet | cat.mob | cat.mobBullet | cat.mobShield; + // //check every second to see if player is away from thrown body, and make solid + // const solid = function(that) { + // const dx = that.position.x - player.position.x; + // const dy = that.position.y - player.position.y; + // if (that.speed < 3 && dx * dx + dy * dy > 10000 && that !== m.holdingTarget) { + // that.collisionFilter.category = cat.body; //make solid + // that.collisionFilter.mask = cat.player | cat.map | cat.body | cat.bullet | cat.mob | cat.mobBullet; //can hit player now + // } else { + // setTimeout(solid, 40, that); + // } + // }; + // setTimeout(solid, 200, m.holdingTarget); + + + + if (tech.isFreezeMobs) { + for (let i = 0, len = mob.length; i < len; ++i) { + if (Vector.magnitude(Vector.sub(mob[i].position, m.fieldPosition)) < m.fieldRadius) { + mobs.statusSlow(mob[i], 180) + } + } + } + + ctx.beginPath(); + const rotate = m.cycle * 0.008; + m.fieldPhase += 0.2 // - 0.5 * Math.sqrt(Math.min(m.energy, 1)); + const off1 = 1 + 0.06 * Math.sin(m.fieldPhase); + const off2 = 1 - 0.06 * Math.sin(m.fieldPhase); + ctx.beginPath(); + ctx.ellipse(m.fieldPosition.x, m.fieldPosition.y, 1.2 * m.fieldRadius * off1, 1.2 * m.fieldRadius * off2, rotate, 0, 2 * Math.PI); + ctx.globalCompositeOperation = "exclusion"; //"exclusion" "difference"; + ctx.fillStyle = "#fff"; //"#eef"; + ctx.fill(); + ctx.globalCompositeOperation = "source-over"; + ctx.beginPath(); + ctx.ellipse(m.fieldPosition.x, m.fieldPosition.y, 1.2 * m.fieldRadius * off1, 1.2 * m.fieldRadius * off2, rotate, 0, 2 * Math.PI * m.energy / m.maxEnergy); + ctx.strokeStyle = "#000"; + ctx.lineWidth = 4; + ctx.stroke(); + } else { + m.fieldCDcycle = m.cycle + 120; + m.fieldOn = false + m.fieldRadius = 0 + } + } else { + m.grabPowerUp(); + } + } else { + m.fieldOn = false + m.fieldRadius = 0 + } + m.drawFieldMeter() + } + } + }, + { + name: "wormhole", + description: "use energy to tunnel through a wormhole
wormholes attract blocks and power ups
7% chance to duplicate spawned power ups", //
bullets may also traverse wormholes + drain: 0, + effect: function () { + m.duplicateChance = 0.07 + m.fieldRange = 0 + powerUps.setDupChance(); //needed after adjusting duplication chance + + m.hold = function () { + // m.hole = { //this is reset with each new field, but I'm leaving it here for reference + // isOn: false, + // isReady: true, + // pos1: {x: 0,y: 0}, + // pos2: {x: 0,y: 0}, + // angle: 0, + // unit:{x:0,y:0}, + // } + if (m.hole.isOn) { + // draw holes + m.fieldRange = 0.97 * m.fieldRange + 0.03 * (50 + 10 * Math.sin(simulation.cycle * 0.025)) + const semiMajorAxis = m.fieldRange + 30 + const edge1a = Vector.add(Vector.mult(m.hole.unit, semiMajorAxis), m.hole.pos1) + const edge1b = Vector.add(Vector.mult(m.hole.unit, -semiMajorAxis), m.hole.pos1) + const edge2a = Vector.add(Vector.mult(m.hole.unit, semiMajorAxis), m.hole.pos2) + const edge2b = Vector.add(Vector.mult(m.hole.unit, -semiMajorAxis), m.hole.pos2) + ctx.beginPath(); + ctx.moveTo(edge1a.x, edge1a.y) + ctx.bezierCurveTo(m.hole.pos1.x, m.hole.pos1.y, m.hole.pos2.x, m.hole.pos2.y, edge2a.x, edge2a.y); + ctx.lineTo(edge2b.x, edge2b.y) + ctx.bezierCurveTo(m.hole.pos2.x, m.hole.pos2.y, m.hole.pos1.x, m.hole.pos1.y, edge1b.x, edge1b.y); + ctx.fillStyle = `rgba(255,255,255,${200 / m.fieldRange / m.fieldRange})` //"rgba(0,0,0,0.1)" + ctx.fill(); + ctx.beginPath(); + ctx.ellipse(m.hole.pos1.x, m.hole.pos1.y, m.fieldRange, semiMajorAxis, m.hole.angle, 0, 2 * Math.PI) + ctx.ellipse(m.hole.pos2.x, m.hole.pos2.y, m.fieldRange, semiMajorAxis, m.hole.angle, 0, 2 * Math.PI) + ctx.fillStyle = `rgba(255,255,255,${32 / m.fieldRange})` + ctx.fill(); + + //suck power ups + for (let i = 0, len = powerUp.length; i < len; ++i) { + //which hole is closer + const dxP1 = m.hole.pos1.x - powerUp[i].position.x; + const dyP1 = m.hole.pos1.y - powerUp[i].position.y; + const dxP2 = m.hole.pos2.x - powerUp[i].position.x; + const dyP2 = m.hole.pos2.y - powerUp[i].position.y; + let dxP, dyP, dist2 + if (dxP1 * dxP1 + dyP1 * dyP1 < dxP2 * dxP2 + dyP2 * dyP2) { + dxP = dxP1 + dyP = dyP1 + } else { + dxP = dxP2 + dyP = dyP2 + } + dist2 = dxP * dxP + dyP * dyP; + if (dist2 < 600000) { //&& !(m.health === m.maxHealth && powerUp[i].name === "heal") + powerUp[i].force.x += 4 * (dxP / dist2) * powerUp[i].mass; // float towards hole + powerUp[i].force.y += 4 * (dyP / dist2) * powerUp[i].mass - powerUp[i].mass * simulation.g; //negate gravity + Matter.Body.setVelocity(powerUp[i], { //extra friction + x: powerUp[i].velocity.x * 0.05, + y: powerUp[i].velocity.y * 0.05 + }); + if (dist2 < 1000 && !simulation.isChoosing) { //use power up if it is close enough + + // if (true) { //AoE radiation effect + // const range = 800 + + // for (let i = 0, len = mob.length; i < len; ++i) { + // if (mob[i].alive && !mob[i].isShielded) { + // dist = Vector.magnitude(Vector.sub(powerUp[i].position, mob[i].position)) - mob[i].radius; + // if (dist < range) mobs.statusDoT(mob[i], 0.5) //apply radiation damage status effect on direct hits + // } + // } + + // simulation.drawList.push({ + // x: powerUp[i].position.x, + // y: powerUp[i].position.y, + // radius: range, + // color: "rgba(0,150,200,0.3)", + // time: 4 + // }); + // } + + m.fieldRange *= 0.8 + powerUps.onPickUp(powerUp[i]); + powerUp[i].effect(); + Matter.Composite.remove(engine.world, powerUp[i]); + powerUp.splice(i, 1); + break; //because the array order is messed up after splice + } + } + } + //suck and shrink blocks + const suckRange = 500 + const shrinkRange = 100 + const shrinkScale = 0.97; + const slowScale = 0.9 + for (let i = 0, len = body.length; i < len; i++) { + if (!body[i].isNotHoldable) { + const dist1 = Vector.magnitude(Vector.sub(m.hole.pos1, body[i].position)) + const dist2 = Vector.magnitude(Vector.sub(m.hole.pos2, body[i].position)) + if (dist1 < dist2) { + if (dist1 < suckRange) { + const pull = Vector.mult(Vector.normalise(Vector.sub(m.hole.pos1, body[i].position)), 1) const slow = Vector.mult(body[i].velocity, slowScale) Matter.Body.setVelocity(body[i], Vector.add(slow, pull)); //shrink - if (Vector.magnitude(Vector.sub(m.hole.pos2, body[i].position)) < shrinkRange) { + if (Vector.magnitude(Vector.sub(m.hole.pos1, body[i].position)) < shrinkRange) { Matter.Body.scale(body[i], shrinkScale, shrinkScale); if (body[i].mass < 0.05) { Matter.Composite.remove(engine.world, body[i]); body.splice(i, 1); m.fieldRange *= 0.8 - // if (tech.isWormholeEnergy && m.energy < m.maxEnergy * 2) m.energy = m.maxEnergy * 2 - if (tech.isWormholeEnergy && m.immuneCycle < m.cycle) m.energy += 0.63 + if (tech.isWormholeEnergy) m.energy += 0.63 if (tech.isWormholeSpores) { //pandimensional spermia for (let i = 0, len = Math.ceil(3 * (tech.isSporeWorm ? 0.5 : 1) * Math.random()); i < len; i++) { if (tech.isSporeWorm) { - b.worm(Vector.add(m.hole.pos1, Vector.rotate({ + b.worm(Vector.add(m.hole.pos2, Vector.rotate({ x: m.fieldRange * 0.4, y: 0 }, 2 * Math.PI * Math.random()))) - Matter.Body.setVelocity(bullet[bullet.length - 1], Vector.mult(Vector.rotate(m.hole.unit, Math.PI / 2), 5)); + Matter.Body.setVelocity(bullet[bullet.length - 1], Vector.mult(Vector.rotate(m.hole.unit, Math.PI / 2), -5)); } else { - b.spore(Vector.add(m.hole.pos1, Vector.rotate({ + b.spore(Vector.add(m.hole.pos2, Vector.rotate({ x: m.fieldRange * 0.4, y: 0 }, 2 * Math.PI * Math.random()))) - Matter.Body.setVelocity(bullet[bullet.length - 1], Vector.mult(Vector.rotate(m.hole.unit, Math.PI / 2), 15)); + Matter.Body.setVelocity(bullet[bullet.length - 1], Vector.mult(Vector.rotate(m.hole.unit, Math.PI / 2), -15)); } } } @@ -3062,338 +2988,372 @@ const m = { } } } - } - } - if (tech.isWormBullets) { - //teleport bullets - for (let i = 0, len = bullet.length; i < len; ++i) { //teleport bullets from hole1 to hole2 - if (!bullet[i].botType && !bullet[i].isInHole) { //don't teleport bots - if (Vector.magnitude(Vector.sub(m.hole.pos1, bullet[i].position)) < m.fieldRange) { //find if bullet is touching hole1 - Matter.Body.setPosition(bullet[i], Vector.add(m.hole.pos2, Vector.sub(m.hole.pos1, bullet[i].position))); - m.fieldRange += 5 - bullet[i].isInHole = true - } else if (Vector.magnitude(Vector.sub(m.hole.pos2, bullet[i].position)) < m.fieldRange) { //find if bullet is touching hole1 - Matter.Body.setPosition(bullet[i], Vector.add(m.hole.pos1, Vector.sub(m.hole.pos2, bullet[i].position))); - m.fieldRange += 5 - bullet[i].isInHole = true - } - } - } - // mobs get pushed away - for (let i = 0, len = mob.length; i < len; i++) { - if (Vector.magnitude(Vector.sub(m.hole.pos1, mob[i].position)) < 200) { - const pull = Vector.mult(Vector.normalise(Vector.sub(m.hole.pos1, mob[i].position)), -0.07) - Matter.Body.setVelocity(mob[i], Vector.add(mob[i].velocity, pull)); - } - if (Vector.magnitude(Vector.sub(m.hole.pos2, mob[i].position)) < 200) { - const pull = Vector.mult(Vector.normalise(Vector.sub(m.hole.pos2, mob[i].position)), -0.07) - Matter.Body.setVelocity(mob[i], Vector.add(mob[i].velocity, pull)); - } - } - } - } - - if (m.fieldCDcycle < m.cycle) { - const scale = 60 - const justPastMouse = Vector.add(Vector.mult(Vector.normalise(Vector.sub(simulation.mouseInGame, m.pos)), 50), simulation.mouseInGame) - const sub = Vector.sub(simulation.mouseInGame, m.pos) - const mag = Vector.magnitude(sub) - const drain = tech.isFreeWormHole ? 0 : 0.06 + 0.006 * Math.sqrt(mag) - if (input.field) { - m.grabPowerUp(); - - //draw possible wormhole - if (mag > 250 && m.energy > drain) { - const unit = Vector.perp(Vector.normalise(sub)) - const where = { x: m.pos.x + 30 * Math.cos(m.angle), y: m.pos.y + 30 * Math.sin(m.angle) } - m.fieldRange = 0.97 * m.fieldRange + 0.03 * (50 + 10 * Math.sin(simulation.cycle * 0.025)) - const edge2a = Vector.add(Vector.mult(unit, 1.5 * m.fieldRange), simulation.mouseInGame) - const edge2b = Vector.add(Vector.mult(unit, -1.5 * m.fieldRange), simulation.mouseInGame) - ctx.beginPath(); - ctx.moveTo(where.x, where.y) - ctx.bezierCurveTo(where.x, where.y, simulation.mouseInGame.x, simulation.mouseInGame.y, edge2a.x, edge2a.y); - ctx.moveTo(where.x, where.y) - ctx.bezierCurveTo(where.x, where.y, simulation.mouseInGame.x, simulation.mouseInGame.y, edge2b.x, edge2b.y); - if ( - Matter.Query.region(map, { - min: { - x: simulation.mouseInGame.x - scale, - y: simulation.mouseInGame.y - scale - }, - max: { - x: simulation.mouseInGame.x + scale, - y: simulation.mouseInGame.y + scale - } - }).length === 0 && - Matter.Query.ray(map, m.pos, justPastMouse).length === 0 - ) { - m.hole.isReady = true; - // ctx.fillStyle = "rgba(255,255,255,0.5)" - // ctx.fill(); - ctx.lineWidth = 1 - ctx.strokeStyle = "#000" - ctx.stroke(); - } else { - m.hole.isReady = false; - ctx.lineWidth = 1 - ctx.strokeStyle = "#000" - ctx.lineDashOffset = 30 * Math.random() - ctx.setLineDash([20, 40]); - ctx.stroke(); - ctx.setLineDash([]); - } - } else { - m.hole.isReady = false; - } - } else { - //make new wormhole - if ( - m.hole.isReady && mag > 250 && m.energy > drain && - Matter.Query.region(map, { - min: { - x: simulation.mouseInGame.x - scale, - y: simulation.mouseInGame.y - scale - }, - max: { - x: simulation.mouseInGame.x + scale, - y: simulation.mouseInGame.y + scale - } - }).length === 0 && - Matter.Query.ray(map, m.pos, justPastMouse).length === 0 - ) { - m.energy -= drain - m.hole.isReady = false; - m.fieldRange = 0 - Matter.Body.setPosition(player, simulation.mouseInGame); - m.buttonCD_jump = 0 //this might fix a bug with jumping - const velocity = Vector.mult(Vector.normalise(sub), 20) - Matter.Body.setVelocity(player, { - x: velocity.x, - y: velocity.y - 4 //an extra vertical kick so the player hangs in place longer - }); - if (m.immuneCycle < m.cycle + 15) m.immuneCycle = m.cycle + 15; //player is immune to damage for 1/4 seconds - // move bots to player - for (let i = 0; i < bullet.length; i++) { - if (bullet[i].botType) { - Matter.Body.setPosition(bullet[i], Vector.add(player.position, { - x: 250 * (Math.random() - 0.5), - y: 250 * (Math.random() - 0.5) - })); - Matter.Body.setVelocity(bullet[i], { - x: 0, - y: 0 - }); - } - } - - //set holes - m.hole.isOn = true; - m.hole.pos1.x = m.pos.x - m.hole.pos1.y = m.pos.y - m.hole.pos2.x = player.position.x - m.hole.pos2.y = player.position.y - m.hole.angle = Math.atan2(sub.y, sub.x) - m.hole.unit = Vector.perp(Vector.normalise(sub)) - - if (tech.isWormholeDamage) { - who = Matter.Query.ray(mob, m.pos, simulation.mouseInGame, 100) - for (let i = 0; i < who.length; i++) { - if (who[i].body.alive) { - mobs.statusDoT(who[i].body, 1, 420) - mobs.statusStun(who[i].body, 360) + } else if (dist2 < suckRange) { + const pull = Vector.mult(Vector.normalise(Vector.sub(m.hole.pos2, body[i].position)), 1) + const slow = Vector.mult(body[i].velocity, slowScale) + Matter.Body.setVelocity(body[i], Vector.add(slow, pull)); + //shrink + if (Vector.magnitude(Vector.sub(m.hole.pos2, body[i].position)) < shrinkRange) { + Matter.Body.scale(body[i], shrinkScale, shrinkScale); + if (body[i].mass < 0.05) { + Matter.Composite.remove(engine.world, body[i]); + body.splice(i, 1); + m.fieldRange *= 0.8 + // if (tech.isWormholeEnergy && m.energy < m.maxEnergy * 2) m.energy = m.maxEnergy * 2 + if (tech.isWormholeEnergy && m.immuneCycle < m.cycle) m.energy += 0.63 + if (tech.isWormholeSpores) { //pandimensional spermia + for (let i = 0, len = Math.ceil(3 * (tech.isSporeWorm ? 0.5 : 1) * Math.random()); i < len; i++) { + if (tech.isSporeWorm) { + b.worm(Vector.add(m.hole.pos1, Vector.rotate({ + x: m.fieldRange * 0.4, + y: 0 + }, 2 * Math.PI * Math.random()))) + Matter.Body.setVelocity(bullet[bullet.length - 1], Vector.mult(Vector.rotate(m.hole.unit, Math.PI / 2), 5)); + } else { + b.spore(Vector.add(m.hole.pos1, Vector.rotate({ + x: m.fieldRange * 0.4, + y: 0 + }, 2 * Math.PI * Math.random()))) + Matter.Body.setVelocity(bullet[bullet.length - 1], Vector.mult(Vector.rotate(m.hole.unit, Math.PI / 2), 15)); + } + } } + break } } } } } - - // if (input.field && m.fieldCDcycle < m.cycle) { //not hold but field button is pressed - // const justPastMouse = Vector.add(Vector.mult(Vector.normalise(Vector.sub(simulation.mouseInGame, m.pos)), 50), simulation.mouseInGame) - // const scale = 60 - // // console.log(Matter.Query.region(map, bounds)) - // const sub = Vector.sub(simulation.mouseInGame, m.pos) - // const mag = Vector.magnitude(sub) - // const drain = tech.isFreeWormHole ? 0 : 0.06 + 0.006 * Math.sqrt(mag) - // if (m.hole.isReady && mag > 250 && m.energy > drain) { - // if ( - // Matter.Query.region(map, { - // min: { - // x: simulation.mouseInGame.x - scale, - // y: simulation.mouseInGame.y - scale - // }, - // max: { - // x: simulation.mouseInGame.x + scale, - // y: simulation.mouseInGame.y + scale - // } - // }).length === 0 && - // Matter.Query.ray(map, m.pos, justPastMouse).length === 0 - // // Matter.Query.ray(map, m.pos, simulation.mouseInGame).length === 0 && - // // Matter.Query.ray(map, player.position, simulation.mouseInGame).length === 0 && - // // Matter.Query.ray(map, player.position, justPastMouse).length === 0 - // ) { - // m.energy -= drain - // m.hole.isReady = false; - // m.fieldRange = 0 - // Matter.Body.setPosition(player, simulation.mouseInGame); - // m.buttonCD_jump = 0 //this might fix a bug with jumping - // const velocity = Vector.mult(Vector.normalise(sub), 20) - // Matter.Body.setVelocity(player, { - // x: velocity.x, - // y: velocity.y - 4 //an extra vertical kick so the player hangs in place longer - // }); - // if (m.immuneCycle < m.cycle + 15) m.immuneCycle = m.cycle + 15; //player is immune to damage for 1/4 seconds - // // move bots to player - // for (let i = 0; i < bullet.length; i++) { - // if (bullet[i].botType) { - // Matter.Body.setPosition(bullet[i], Vector.add(player.position, { - // x: 250 * (Math.random() - 0.5), - // y: 250 * (Math.random() - 0.5) - // })); - // Matter.Body.setVelocity(bullet[i], { - // x: 0, - // y: 0 - // }); - // } - // } - - // //set holes - // m.hole.isOn = true; - // m.hole.pos1.x = m.pos.x - // m.hole.pos1.y = m.pos.y - // m.hole.pos2.x = player.position.x - // m.hole.pos2.y = player.position.y - // m.hole.angle = Math.atan2(sub.y, sub.x) - // m.hole.unit = Vector.perp(Vector.normalise(sub)) - - // if (tech.isWormholeDamage) { - // who = Matter.Query.ray(mob, m.pos, simulation.mouseInGame, 100) - // for (let i = 0; i < who.length; i++) { - // if (who[i].body.alive) { - // mobs.statusDoT(who[i].body, 1, 420) - // mobs.statusStun(who[i].body, 360) - // } - // } - // } - // } else { - // //draw failed wormhole - // const unit = Vector.perp(Vector.normalise(Vector.sub(simulation.mouseInGame, m.pos))) - // const where = { x: m.pos.x + 30 * Math.cos(m.angle), y: m.pos.y + 30 * Math.sin(m.angle), } - // m.fieldRange = 0.97 * m.fieldRange + 0.03 * (50 + 10 * Math.sin(simulation.cycle * 0.025)) - // const edge2a = Vector.add(Vector.mult(unit, 1.5 * m.fieldRange), simulation.mouseInGame) - // const edge2b = Vector.add(Vector.mult(unit, -1.5 * m.fieldRange), simulation.mouseInGame) - // ctx.beginPath(); - // ctx.moveTo(where.x, where.y) - // ctx.bezierCurveTo(where.x, where.y, simulation.mouseInGame.x, simulation.mouseInGame.y, edge2a.x, edge2a.y); - // ctx.lineTo(edge2b.x, edge2b.y) - // ctx.bezierCurveTo(simulation.mouseInGame.x, simulation.mouseInGame.y, where.x, where.y, where.x, where.y); - // // ctx.fillStyle = "rgba(255,255,255,0.5)" - // // ctx.fill(); - // ctx.lineWidth = 1 - // ctx.strokeStyle = "#000" - // ctx.lineDashOffset = 30 * Math.random() - // ctx.setLineDash([20, 40]); - // ctx.stroke(); - // ctx.setLineDash([]); - // } - // } - // m.grabPowerUp(); - // } else { - // m.hole.isReady = true; - // } - - - - - m.drawFieldMeter() + if (tech.isWormBullets) { + //teleport bullets + for (let i = 0, len = bullet.length; i < len; ++i) { //teleport bullets from hole1 to hole2 + if (!bullet[i].botType && !bullet[i].isInHole) { //don't teleport bots + if (Vector.magnitude(Vector.sub(m.hole.pos1, bullet[i].position)) < m.fieldRange) { //find if bullet is touching hole1 + Matter.Body.setPosition(bullet[i], Vector.add(m.hole.pos2, Vector.sub(m.hole.pos1, bullet[i].position))); + m.fieldRange += 5 + bullet[i].isInHole = true + } else if (Vector.magnitude(Vector.sub(m.hole.pos2, bullet[i].position)) < m.fieldRange) { //find if bullet is touching hole1 + Matter.Body.setPosition(bullet[i], Vector.add(m.hole.pos1, Vector.sub(m.hole.pos2, bullet[i].position))); + m.fieldRange += 5 + bullet[i].isInHole = true + } + } + } + // mobs get pushed away + for (let i = 0, len = mob.length; i < len; i++) { + if (Vector.magnitude(Vector.sub(m.hole.pos1, mob[i].position)) < 200) { + const pull = Vector.mult(Vector.normalise(Vector.sub(m.hole.pos1, mob[i].position)), -0.07) + Matter.Body.setVelocity(mob[i], Vector.add(mob[i].velocity, pull)); + } + if (Vector.magnitude(Vector.sub(m.hole.pos2, mob[i].position)) < 200) { + const pull = Vector.mult(Vector.normalise(Vector.sub(m.hole.pos2, mob[i].position)), -0.07) + Matter.Body.setVelocity(mob[i], Vector.add(mob[i].velocity, pull)); + } + } + } } - }, - // rewind: function() { - // if (input.down) { - // if (input.field && m.fieldCDcycle < m.cycle) { //not hold but field button is pressed - // const DRAIN = 0.01 - // if (this.rewindCount < 289 && m.energy > DRAIN) { - // m.energy -= DRAIN + if (m.fieldCDcycle < m.cycle) { + const scale = 60 + const justPastMouse = Vector.add(Vector.mult(Vector.normalise(Vector.sub(simulation.mouseInGame, m.pos)), 50), simulation.mouseInGame) + const sub = Vector.sub(simulation.mouseInGame, m.pos) + const mag = Vector.magnitude(sub) + + if (input.field) { + m.grabPowerUp(); + //draw possible wormhole + if (tech.isWormholeMapIgnore && Matter.Query.ray(map, m.pos, justPastMouse).length !== 0) { + this.drain = (0.06 + 0.006 * Math.sqrt(mag)) * 2 + } else { + this.drain = tech.isFreeWormHole ? 0 : 0.06 + 0.006 * Math.sqrt(mag) + } + const unit = Vector.perp(Vector.normalise(sub)) + const where = { x: m.pos.x + 30 * Math.cos(m.angle), y: m.pos.y + 30 * Math.sin(m.angle) } + m.fieldRange = 0.97 * m.fieldRange + 0.03 * (50 + 10 * Math.sin(simulation.cycle * 0.025)) + const edge2a = Vector.add(Vector.mult(unit, 1.5 * m.fieldRange), simulation.mouseInGame) + const edge2b = Vector.add(Vector.mult(unit, -1.5 * m.fieldRange), simulation.mouseInGame) + ctx.beginPath(); + ctx.moveTo(where.x, where.y) + ctx.bezierCurveTo(where.x, where.y, simulation.mouseInGame.x, simulation.mouseInGame.y, edge2a.x, edge2a.y); + ctx.moveTo(where.x, where.y) + ctx.bezierCurveTo(where.x, where.y, simulation.mouseInGame.x, simulation.mouseInGame.y, edge2b.x, edge2b.y); + if ( + mag > 250 && m.energy > this.drain && + (tech.isWormholeMapIgnore || Matter.Query.ray(map, m.pos, justPastMouse).length === 0) && + Matter.Query.region(map, { + min: { + x: simulation.mouseInGame.x - scale, + y: simulation.mouseInGame.y - scale + }, + max: { + x: simulation.mouseInGame.x + scale, + y: simulation.mouseInGame.y + scale + } + }).length === 0 + ) { + m.hole.isReady = true; + // ctx.fillStyle = "rgba(255,255,255,0.5)" + // ctx.fill(); + ctx.lineWidth = 1 + ctx.strokeStyle = "#000" + ctx.stroke(); + } else { + m.hole.isReady = false; + ctx.lineWidth = 1 + ctx.strokeStyle = "#000" + ctx.lineDashOffset = 30 * Math.random() + ctx.setLineDash([20, 40]); + ctx.stroke(); + ctx.setLineDash([]); + } + } else { + //make new wormhole + if ( + m.hole.isReady && mag > 250 && m.energy > this.drain && + (tech.isWormholeMapIgnore || Matter.Query.ray(map, m.pos, justPastMouse).length === 0) && + Matter.Query.region(map, { + min: { + x: simulation.mouseInGame.x - scale, + y: simulation.mouseInGame.y - scale + }, + max: { + x: simulation.mouseInGame.x + scale, + y: simulation.mouseInGame.y + scale + } + }).length === 0 + ) { + m.energy -= this.drain + m.hole.isReady = false; + m.fieldRange = 0 + Matter.Body.setPosition(player, simulation.mouseInGame); + m.buttonCD_jump = 0 //this might fix a bug with jumping + const velocity = Vector.mult(Vector.normalise(sub), 20) + Matter.Body.setVelocity(player, { + x: velocity.x, + y: velocity.y - 4 //an extra vertical kick so the player hangs in place longer + }); + if (m.immuneCycle < m.cycle + 15) m.immuneCycle = m.cycle + 15; //player is immune to damage for 1/4 seconds + // move bots to player + for (let i = 0; i < bullet.length; i++) { + if (bullet[i].botType) { + Matter.Body.setPosition(bullet[i], Vector.add(player.position, { + x: 250 * (Math.random() - 0.5), + y: 250 * (Math.random() - 0.5) + })); + Matter.Body.setVelocity(bullet[i], { + x: 0, + y: 0 + }); + } + } + + //set holes + m.hole.isOn = true; + m.hole.pos1.x = m.pos.x + m.hole.pos1.y = m.pos.y + m.hole.pos2.x = player.position.x + m.hole.pos2.y = player.position.y + m.hole.angle = Math.atan2(sub.y, sub.x) + m.hole.unit = Vector.perp(Vector.normalise(sub)) + + if (tech.isWormholeDamage) { + who = Matter.Query.ray(mob, m.pos, simulation.mouseInGame, 100) + for (let i = 0; i < who.length; i++) { + if (who[i].body.alive) { + mobs.statusDoT(who[i].body, 1, 420) + mobs.statusStun(who[i].body, 360) + } + } + } + } + } + } + // if (input.field && m.fieldCDcycle < m.cycle) { //not hold but field button is pressed + // const justPastMouse = Vector.add(Vector.mult(Vector.normalise(Vector.sub(simulation.mouseInGame, m.pos)), 50), simulation.mouseInGame) + // const scale = 60 + // // console.log(Matter.Query.region(map, bounds)) + // const sub = Vector.sub(simulation.mouseInGame, m.pos) + // const mag = Vector.magnitude(sub) + // const drain = tech.isFreeWormHole ? 0 : 0.06 + 0.006 * Math.sqrt(mag) + // if (m.hole.isReady && mag > 250 && m.energy > drain) { + // if ( + // Matter.Query.region(map, { + // min: { + // x: simulation.mouseInGame.x - scale, + // y: simulation.mouseInGame.y - scale + // }, + // max: { + // x: simulation.mouseInGame.x + scale, + // y: simulation.mouseInGame.y + scale + // } + // }).length === 0 && + // Matter.Query.ray(map, m.pos, justPastMouse).length === 0 + // // Matter.Query.ray(map, m.pos, simulation.mouseInGame).length === 0 && + // // Matter.Query.ray(map, player.position, simulation.mouseInGame).length === 0 && + // // Matter.Query.ray(map, player.position, justPastMouse).length === 0 + // ) { + // m.energy -= drain + // m.hole.isReady = false; + // m.fieldRange = 0 + // Matter.Body.setPosition(player, simulation.mouseInGame); + // m.buttonCD_jump = 0 //this might fix a bug with jumping + // const velocity = Vector.mult(Vector.normalise(sub), 20) + // Matter.Body.setVelocity(player, { + // x: velocity.x, + // y: velocity.y - 4 //an extra vertical kick so the player hangs in place longer + // }); + // if (m.immuneCycle < m.cycle + 15) m.immuneCycle = m.cycle + 15; //player is immune to damage for 1/4 seconds + // // move bots to player + // for (let i = 0; i < bullet.length; i++) { + // if (bullet[i].botType) { + // Matter.Body.setPosition(bullet[i], Vector.add(player.position, { + // x: 250 * (Math.random() - 0.5), + // y: 250 * (Math.random() - 0.5) + // })); + // Matter.Body.setVelocity(bullet[i], { + // x: 0, + // y: 0 + // }); + // } + // } + + // //set holes + // m.hole.isOn = true; + // m.hole.pos1.x = m.pos.x + // m.hole.pos1.y = m.pos.y + // m.hole.pos2.x = player.position.x + // m.hole.pos2.y = player.position.y + // m.hole.angle = Math.atan2(sub.y, sub.x) + // m.hole.unit = Vector.perp(Vector.normalise(sub)) + + // if (tech.isWormholeDamage) { + // who = Matter.Query.ray(mob, m.pos, simulation.mouseInGame, 100) + // for (let i = 0; i < who.length; i++) { + // if (who[i].body.alive) { + // mobs.statusDoT(who[i].body, 1, 420) + // mobs.statusStun(who[i].body, 360) + // } + // } + // } + // } else { + // //draw failed wormhole + // const unit = Vector.perp(Vector.normalise(Vector.sub(simulation.mouseInGame, m.pos))) + // const where = { x: m.pos.x + 30 * Math.cos(m.angle), y: m.pos.y + 30 * Math.sin(m.angle), } + // m.fieldRange = 0.97 * m.fieldRange + 0.03 * (50 + 10 * Math.sin(simulation.cycle * 0.025)) + // const edge2a = Vector.add(Vector.mult(unit, 1.5 * m.fieldRange), simulation.mouseInGame) + // const edge2b = Vector.add(Vector.mult(unit, -1.5 * m.fieldRange), simulation.mouseInGame) + // ctx.beginPath(); + // ctx.moveTo(where.x, where.y) + // ctx.bezierCurveTo(where.x, where.y, simulation.mouseInGame.x, simulation.mouseInGame.y, edge2a.x, edge2a.y); + // ctx.lineTo(edge2b.x, edge2b.y) + // ctx.bezierCurveTo(simulation.mouseInGame.x, simulation.mouseInGame.y, where.x, where.y, where.x, where.y); + // // ctx.fillStyle = "rgba(255,255,255,0.5)" + // // ctx.fill(); + // ctx.lineWidth = 1 + // ctx.strokeStyle = "#000" + // ctx.lineDashOffset = 30 * Math.random() + // ctx.setLineDash([20, 40]); + // ctx.stroke(); + // ctx.setLineDash([]); + // } + // } + // m.grabPowerUp(); + // } else { + // m.hole.isReady = true; + // } - // if (this.rewindCount === 0) { - // const shortPause = function() { - // if (m.defaultFPSCycle < m.cycle) { //back to default values - // simulation.fpsCap = simulation.fpsCapDefault - // simulation.fpsInterval = 1000 / simulation.fpsCap; - // // document.getElementById("dmg").style.transition = "opacity 1s"; - // // document.getElementById("dmg").style.opacity = "0"; - // } else { - // requestAnimationFrame(shortPause); - // } - // }; - // if (m.defaultFPSCycle < m.cycle) requestAnimationFrame(shortPause); - // simulation.fpsCap = 4 //1 is longest pause, 4 is standard - // simulation.fpsInterval = 1000 / simulation.fpsCap; - // m.defaultFPSCycle = m.cycle - // } - // this.rewindCount += 10; - // simulation.wipe = function() { //set wipe to have trails - // // ctx.fillStyle = "rgba(255,255,255,0)"; - // ctx.fillStyle = `rgba(221,221,221,${0.004})`; - // ctx.fillRect(0, 0, canvas.width, canvas.height); - // } - // let history = m.history[(m.cycle - this.rewindCount) % 300] - // Matter.Body.setPosition(player, history.position); - // Matter.Body.setVelocity(player, { x: history.velocity.x, y: history.velocity.y }); - // if (history.health > m.health) { - // m.health = history.health - // m.displayHealth(); - // } - // //grab power ups - // for (let i = 0, len = powerUp.length; i < len; ++i) { - // const dxP = player.position.x - powerUp[i].position.x; - // const dyP = player.position.y - powerUp[i].position.y; - // if (dxP * dxP + dyP * dyP < 50000 && !simulation.isChoosing && !(m.health === m.maxHealth && powerUp[i].name === "heal")) { - // powerUps.onPickUp(player.position); - // powerUp[i].effect(); - // Matter.Composite.remove(engine.world, powerUp[i]); - // powerUp.splice(i, 1); - // const shortPause = function() { - // if (m.defaultFPSCycle < m.cycle) { //back to default values - // simulation.fpsCap = simulation.fpsCapDefault - // simulation.fpsInterval = 1000 / simulation.fpsCap; - // // document.getElementById("dmg").style.transition = "opacity 1s"; - // // document.getElementById("dmg").style.opacity = "0"; - // } else { - // requestAnimationFrame(shortPause); - // } - // }; - // if (m.defaultFPSCycle < m.cycle) requestAnimationFrame(shortPause); - // simulation.fpsCap = 3 //1 is longest pause, 4 is standard - // simulation.fpsInterval = 1000 / simulation.fpsCap; - // m.defaultFPSCycle = m.cycle - // break; //because the array order is messed up after splice - // } - // } - // m.immuneCycle = m.cycle + 5; //player is immune to damage for 30 cycles - // } else { - // m.fieldCDcycle = m.cycle + 30; - // // m.resetHistory(); - // } - // } else { - // if (this.rewindCount !== 0) { - // m.fieldCDcycle = m.cycle + 30; - // m.resetHistory(); - // this.rewindCount = 0; - // simulation.wipe = function() { //set wipe to normal - // ctx.clearRect(0, 0, canvas.width, canvas.height); - // } - // } - // m.holdingTarget = null; //clears holding target (this is so you only pick up right after the field button is released and a hold target exists) - // } - // } - // m.drawFieldMeter() - // }, + m.drawFieldMeter() + } }, + + // rewind: function() { + // if (input.down) { + // if (input.field && m.fieldCDcycle < m.cycle) { //not hold but field button is pressed + // const DRAIN = 0.01 + // if (this.rewindCount < 289 && m.energy > DRAIN) { + // m.energy -= DRAIN + + + // if (this.rewindCount === 0) { + // const shortPause = function() { + // if (m.defaultFPSCycle < m.cycle) { //back to default values + // simulation.fpsCap = simulation.fpsCapDefault + // simulation.fpsInterval = 1000 / simulation.fpsCap; + // // document.getElementById("dmg").style.transition = "opacity 1s"; + // // document.getElementById("dmg").style.opacity = "0"; + // } else { + // requestAnimationFrame(shortPause); + // } + // }; + // if (m.defaultFPSCycle < m.cycle) requestAnimationFrame(shortPause); + // simulation.fpsCap = 4 //1 is longest pause, 4 is standard + // simulation.fpsInterval = 1000 / simulation.fpsCap; + // m.defaultFPSCycle = m.cycle + // } + + + // this.rewindCount += 10; + // simulation.wipe = function() { //set wipe to have trails + // // ctx.fillStyle = "rgba(255,255,255,0)"; + // ctx.fillStyle = `rgba(221,221,221,${0.004})`; + // ctx.fillRect(0, 0, canvas.width, canvas.height); + // } + // let history = m.history[(m.cycle - this.rewindCount) % 300] + // Matter.Body.setPosition(player, history.position); + // Matter.Body.setVelocity(player, { x: history.velocity.x, y: history.velocity.y }); + // if (history.health > m.health) { + // m.health = history.health + // m.displayHealth(); + // } + // //grab power ups + // for (let i = 0, len = powerUp.length; i < len; ++i) { + // const dxP = player.position.x - powerUp[i].position.x; + // const dyP = player.position.y - powerUp[i].position.y; + // if (dxP * dxP + dyP * dyP < 50000 && !simulation.isChoosing && !(m.health === m.maxHealth && powerUp[i].name === "heal")) { + // powerUps.onPickUp(player.position); + // powerUp[i].effect(); + // Matter.Composite.remove(engine.world, powerUp[i]); + // powerUp.splice(i, 1); + // const shortPause = function() { + // if (m.defaultFPSCycle < m.cycle) { //back to default values + // simulation.fpsCap = simulation.fpsCapDefault + // simulation.fpsInterval = 1000 / simulation.fpsCap; + // // document.getElementById("dmg").style.transition = "opacity 1s"; + // // document.getElementById("dmg").style.opacity = "0"; + // } else { + // requestAnimationFrame(shortPause); + // } + // }; + // if (m.defaultFPSCycle < m.cycle) requestAnimationFrame(shortPause); + // simulation.fpsCap = 3 //1 is longest pause, 4 is standard + // simulation.fpsInterval = 1000 / simulation.fpsCap; + // m.defaultFPSCycle = m.cycle + // break; //because the array order is messed up after splice + // } + // } + // m.immuneCycle = m.cycle + 5; //player is immune to damage for 30 cycles + // } else { + // m.fieldCDcycle = m.cycle + 30; + // // m.resetHistory(); + // } + // } else { + // if (this.rewindCount !== 0) { + // m.fieldCDcycle = m.cycle + 30; + // m.resetHistory(); + // this.rewindCount = 0; + // simulation.wipe = function() { //set wipe to normal + // ctx.clearRect(0, 0, canvas.width, canvas.height); + // } + // } + // m.holdingTarget = null; //clears holding target (this is so you only pick up right after the field button is released and a hold target exists) + // } + // } + // m.drawFieldMeter() + // }, + }, ], //************************************************************************************ //************************************************************************************ diff --git a/js/powerup.js b/js/powerup.js index 1f5c936..09087dd 100644 --- a/js/powerup.js +++ b/js/powerup.js @@ -321,7 +321,7 @@ const powerUps = { } if (tech.isRerollBots) { let delay = 0 - for (const cost = 2 + Math.floor(0.2 * b.totalBots()); powerUps.research.count > cost - 1; powerUps.research.count -= cost) { + for (const cost = 2 + Math.floor(0.1666 * b.totalBots()); powerUps.research.count > cost - 1; powerUps.research.count -= cost) { delay += 500 setTimeout(() => { b.randomBot() @@ -469,30 +469,30 @@ const powerUps = { if (tech.ammoCap) { const ammoAdded = Math.ceil(target.ammoPack * 0.7 * tech.ammoCap) //0.7 is average target.ammo = ammoAdded - simulation.makeTextLog(`${target.name}.ammo = ${ammoAdded}`) + // simulation.makeTextLog(`${target.name}.ammo = ${ammoAdded}`) } else { const ammoAdded = Math.ceil((0.7 * Math.random() + 0.7 * Math.random()) * target.ammoPack) target.ammo += ammoAdded - simulation.makeTextLog(`${target.name}.ammo += ${ammoAdded}`) + // simulation.makeTextLog(`${target.name}.ammo += ${ammoAdded}`) } } } else { //give ammo to all guns in inventory - let textLog = "" + // let textLog = "" for (let i = 0, len = b.inventory.length; i < len; i++) { const target = b.guns[b.inventory[i]] if (target.ammo !== Infinity) { if (tech.ammoCap) { const ammoAdded = Math.ceil(target.ammoPack * 0.45 * tech.ammoCap) //0.45 is average target.ammo = ammoAdded - textLog += `${target.name}.ammo = ${ammoAdded}
` + // textLog += `${target.name}.ammo = ${ammoAdded}
` } else { const ammoAdded = Math.ceil((0.45 * Math.random() + 0.45 * Math.random()) * target.ammoPack) //Math.ceil(Math.random() * target.ammoPack) target.ammo += ammoAdded - textLog += `${target.name}.ammo += ${ammoAdded}
` + // textLog += `${target.name}.ammo += ${ammoAdded}
` } } } - simulation.makeTextLog(textLog) + // simulation.makeTextLog(textLog) } // } else { //give ammo to all guns in inventory // for (let i = 0, len = b.inventory.length; i < len; i++) { diff --git a/js/simulation.js b/js/simulation.js index 56b5269..809c09f 100644 --- a/js/simulation.js +++ b/js/simulation.js @@ -354,7 +354,7 @@ const simulation = { // --> // SVGleftMouse: ' ', // SVGrightMouse: ' ', - makeTextLog(text, time = 180) { + makeTextLog(text, time = 240) { if (simulation.isTextLogOpen && !build.isExperimentSelection) { if (simulation.lastLogTime > m.cycle) { //if there is an older message document.getElementById("text-log").innerHTML = document.getElementById("text-log").innerHTML + '
' + text; diff --git a/js/spawn.js b/js/spawn.js index 8471eb1..1034c97 100644 --- a/js/spawn.js +++ b/js/spawn.js @@ -13,12 +13,13 @@ const spawn = { "grenadier", "grenadier", "striker", "striker", "laser", "laser", - "exploder", "exploder", "stabber", "stabber", - "launcher", "launcher", "springer", "springer", "pulsar", "pulsar", - "sneaker", "sneaker", + "exploder", + "sneaker", + "launcher", + "launcherOne", "sucker", "sniper", "spinner", @@ -28,7 +29,7 @@ const spawn = { "spawner", "ghoster", ], - allowedGroupList: ["spinner", "striker", "springer", "laser", "focuser", "beamer", "exploder", "spawner", "shooter", "launcher", "stabber", "sniper", "pulsar", "grenadier", "slasher"], + allowedGroupList: ["spinner", "striker", "springer", "laser", "focuser", "beamer", "exploder", "spawner", "shooter", "launcher", "launcherOne", "stabber", "sniper", "pulsar", "grenadier", "slasher"], setSpawnList() { //this is run at the start of each new level to determine the possible mobs for the level //each level has 2 mobs: one new mob and one from the last level spawn.pickList.splice(0, 1); @@ -136,7 +137,7 @@ const spawn = { me.isMACHO = true; me.frictionAir = 0.006 - me.do = function() { + me.do = function () { const sine = Math.sin(simulation.cycle * 0.015) this.radius = 370 * (1 + 0.1 * sine) //chase player @@ -155,6 +156,7 @@ const spawn = { if (mag < this.radius) { //buff to player when inside radius tech.isHarmMACHO = true; + //draw halo ctx.strokeStyle = "rgba(80,120,200,0.2)" //"rgba(255,255,0,0.2)" //ctx.strokeStyle = `rgba(0,0,255,${0.5+0.5*Math.random()})` ctx.beginPath(); @@ -191,7 +193,7 @@ const spawn = { me.collisionFilter.mask = 0; //cat.player //| cat.body me.chaseSpeed = 1 + 1.5 * Math.random() - me.awake = function() { + me.awake = function () { //chase player const sub = Vector.sub(player.position, this.position) const where = Vector.add(this.position, Vector.mult(Vector.normalise(sub), this.chaseSpeed)) @@ -241,7 +243,7 @@ const spawn = { ctx.arc(this.position.x, this.position.y, this.radius, 0, 2 * Math.PI); // ctx.fillStyle = "hsla(160, 100%, 35%,0.75)" //"rgba(255,0,255,0.2)"; // ctx.globalCompositeOperation = "lighter" - ctx.fillStyle = `rgba(25,139,170,${0.2+0.12*Math.random()})`; + ctx.fillStyle = `rgba(25,139,170,${0.2 + 0.12 * Math.random()})`; ctx.fill(); this.radius = 100 * (1 + 0.25 * Math.sin(simulation.cycle * 0.03)) // ctx.fillStyle = "#fff"; @@ -249,7 +251,7 @@ const spawn = { // ctx.fill(); // ctx.globalCompositeOperation = "source-over" } - me.do = function() { //wake up 2 seconds after the player moves + me.do = function () { //wake up 2 seconds after the player moves if (player.speed > 1 && !m.isCloak) { setTimeout(() => { this.do = this.awake; }, 2000); } @@ -271,10 +273,7 @@ const spawn = { }); Composite.add(engine.world, me.constraint); }, 2000); //add in a delay in case the level gets flipped left right - me.isBoss = true; - - me.frictionAir = 0.01; me.memory = Infinity; me.hasRunDeathScript = false @@ -282,7 +281,7 @@ const spawn = { const density = 0.2 Matter.Body.setDensity(me, density); //extra dense //normal is 0.001 //makes effective life much larger // spawn.shield(me, x, y, 1); - me.onDeath = function() { + me.onDeath = function () { if (!this.hasRunDeathScript) { this.hasRunDeathScript = true //make a block body to replace this one @@ -296,7 +295,7 @@ const spawn = { body[len].collisionFilter.mask = cat.player | cat.map | cat.body | cat.bullet | cat.mob | cat.mobBullet; body[len].classType = "body"; Composite.add(engine.world, body[len]); //add to world - const expand = function(that, massLimit) { + const expand = function (that, massLimit) { const scale = 1.05; Matter.Body.scale(that, scale, scale); if (that.mass < massLimit) setTimeout(expand, 20, that, massLimit); @@ -318,7 +317,7 @@ const spawn = { //add lore level as next level if player took lore tech earlier in the game if (lore.techCount > (lore.techGoal - 1) && !simulation.isCheating) { simulation.makeTextLog(`undefined = ${lore.techCount}/${lore.techGoal}`, 360); - setTimeout(function() { + setTimeout(function () { simulation.makeTextLog(`level.levels.push("null")`, 720); unlockExit() level.levels.push("null") @@ -332,7 +331,7 @@ const spawn = { count++ if (count < 660) { if (count === 1) simulation.makeTextLog(`//enter testing mode to set level.levels.length to Infinite`); - if (!(count % 60)) simulation.makeTextLog(`simulation.analysis = ${((count/60- Math.random())*0.1 ).toFixed(3)}`); + if (!(count % 60)) simulation.makeTextLog(`simulation.analysis = ${((count / 60 - Math.random()) * 0.1).toFixed(3)}`); } else if (count === 660) { simulation.makeTextLog(`simulation.analysis = 1 //analysis complete`); } else if (count === 780) { @@ -351,7 +350,7 @@ const spawn = { document.getElementById("text-log").style.opacity = 0; //fade out any active text logs document.getElementById("fade-out").style.opacity = 1; //slowly fades out // build.shareURL(false) - setTimeout(function() { + setTimeout(function () { simulation.paused = true; // simulation.clearMap(); // Matter.Composite.clear(composite, keepStatic, [deep = false]) @@ -365,7 +364,7 @@ const spawn = { } if (simulation.testing) { unlockExit() - setTimeout(function() { + setTimeout(function () { simulation.makeTextLog(`level.levels.length = Infinite`); }, 1500); } else { @@ -408,13 +407,13 @@ const spawn = { } } }; - me.onDamage = function() {}; + me.onDamage = function () { }; me.cycle = 420; me.endCycle = 780; me.totalCycles = 0 me.mode = 0; - me.damageReduction = 0.25 - me.do = function() { + me.damageReduction = 0.25 //reset on each new mode + me.do = function () { // this.armor(); // Matter.Body.setPosition(this, { // x: x, @@ -433,6 +432,7 @@ const spawn = { if (this.cycle > this.endCycle) { this.cycle = 0; this.mode++ + this.damageReduction = 0.25 if (this.mode > 2) { this.mode = 0; this.fill = "#50f"; @@ -477,14 +477,14 @@ const spawn = { } // } }; - me.modeDo = function() {} - me.modeAll = function() { + me.modeDo = function () { } + me.modeAll = function () { this.modeSpawns() this.modeSuck() this.modeLasers() } me.spawnInterval = 395 - me.modeSpawns = function() { + me.modeSpawns = function () { if (!(this.cycle % this.spawnInterval) && !m.isBodiesAsleep && mob.length < 40) { if (this.mode !== 3) Matter.Body.setAngularVelocity(this, 0.1) //fire a bullet from each vertex @@ -508,7 +508,22 @@ const spawn = { } me.eventHorizon = 1300 me.eventHorizonCycleRate = 4 * Math.PI / me.endCycle - me.modeSuck = function() { + me.modeSuck = function () { + if (!(this.cycle % 60)) { + const index = Math.floor((this.cycle % 360) / 60) + spawn.seeker(this.vertices[index].x, this.vertices[index].y, 20 * (0.5 + Math.random()), 9); //give the bullet a rotational velocity as if they were attached to a vertex + const who = mob[mob.length - 1] + Matter.Body.setDensity(who, 0.00003); //normal is 0.001 + who.timeLeft = 760 //* (0.8 + 0.4 * Math.random()); + who.accelMag = 0.0003 * simulation.accelScale; //* (0.8 + 0.4 * Math.random()) + who.frictionAir = 0.01 //* (0.8 + 0.4 * Math.random()); + const velocity = Vector.mult(Vector.perp(Vector.normalise(Vector.sub(this.position, this.vertices[index]))), -7) + Matter.Body.setVelocity(who, { + x: this.velocity.x + velocity.x, + y: this.velocity.y + velocity.y + }); + } + //eventHorizon waves in and out const eventHorizon = this.eventHorizon * (1 - 0.25 * Math.cos(simulation.cycle * this.eventHorizonCycleRate)) //0.014 //draw darkness @@ -557,8 +572,8 @@ const spawn = { } me.rotateVelocity = 0.0025 me.rotateCount = 0; - me.lasers = function(where, angle, dmg = 0.14 * simulation.dmgScale) { - const vertexCollision = function(v1, v1End, domain) { + me.lasers = function (where, angle, dmg = 0.14 * simulation.dmgScale) { + const vertexCollision = function (v1, v1End, domain) { for (let i = 0; i < domain.length; ++i) { let vertices = domain[i].vertices; const len = vertices.length - 1; @@ -628,7 +643,7 @@ const spawn = { ctx.moveTo(where.x, where.y); ctx.lineTo(best.x, best.y); } - me.modeLasers = function() { + me.modeLasers = function () { if (!m.isBodiesAsleep && !this.isStunned) { let slowed = false //check if slowed for (let i = 0; i < this.status.length; i++) { @@ -660,7 +675,7 @@ const spawn = { ctx.stroke(); // Draw it ctx.setLineDash([]); ctx.lineWidth = 20; - ctx.strokeStyle = `rgba(80,0,255,${0.07*scale})`; + ctx.strokeStyle = `rgba(80,0,255,${0.07 * scale})`; ctx.stroke(); // Draw it } else { ctx.beginPath(); @@ -690,7 +705,7 @@ const spawn = { // me.memory = 120; me.seeAtDistance2 = 2000000 //1400 vision range Matter.Body.setDensity(me, 0.0005) // normal density is 0.001 // this reduces life by half and decreases knockback - me.do = function() { + me.do = function () { this.seePlayerByLookingAt(); this.attraction(); this.repulsion(); @@ -715,7 +730,7 @@ const spawn = { me.isGrouper = true; me.seeAtDistance2 = 600 * 600 me.seePlayerFreq = Math.floor(50 + 50 * Math.random()) - me.do = function() { + me.do = function () { this.gravity(); this.checkStatus(); this.seePlayerCheck(); @@ -755,7 +770,7 @@ const spawn = { me.damageReduction = 0.04 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1); //extra reduction for a boss, because normal density me.frictionAir = 0.01; me.accelMag = 0.0002; - me.onDeath = function() { + me.onDeath = function () { powerUps.spawnBossPowerUp(this.position.x, this.position.y); for (const who of mob) { if (who.isNecroMob) { //blockMobs leave their body, and die @@ -765,7 +780,7 @@ const spawn = { } } me.target = player; // the target to lock on. Usually a block, but will be the player under certain conditions - me.do = function() { + me.do = function () { this.checkStatus(); this.seePlayerCheck(); if (this.target) { //(this.target === player && this.seePlayer.yes) || this.target !== player @@ -875,7 +890,7 @@ const spawn = { me.isDropPowerUp = false; // me.showHealthBar = false; me.cycle = 0 - me.do = function() { //grow phase only occurs for growCycles + me.do = function () { //grow phase only occurs for growCycles this.checkStatus(); this.seePlayerCheck(); @@ -891,7 +906,7 @@ const spawn = { } } } - me.normalDo = function() { + me.normalDo = function () { this.gravity(); this.checkStatus(); this.seePlayerCheck(); @@ -921,21 +936,21 @@ const spawn = { me.collisionFilter.mask = cat.player | cat.bullet //| cat.body | cat.map Matter.Body.setDensity(me, 0.00035) // normal density is 0.001 // this reduces life by half and decreases knockback const k = 642 //k=r^2/m - me.split = function() { + me.split = function () { Matter.Body.scale(this, 0.45, 0.45); this.radius = Math.sqrt(this.mass * k / Math.PI) spawn.cellBoss(this.position.x, this.position.y, this.radius, this.cellID); mob[mob.length - 1].health = this.health } - me.onHit = function() { //run this function on hitting player + me.onHit = function () { //run this function on hitting player this.health = 1; this.split(); }; - me.onDamage = function(dmg) { + me.onDamage = function (dmg) { if (Math.random() < 0.34 * dmg * Math.sqrt(this.mass) && this.health > dmg) this.split(); } me.damageReduction = 0.17 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1); //me.damageReductionGoal - me.do = function() { + me.do = function () { // // this.armor(); if (!m.isBodiesAsleep) { this.seePlayerByDistOrLOS(); @@ -964,7 +979,7 @@ const spawn = { } } }; - me.onDeath = function() { + me.onDeath = function () { this.isCell = false; let count = 0 //count other cells by id // console.log(this.cellID) @@ -1004,11 +1019,11 @@ const spawn = { Matter.Body.setAngularVelocity(me, 0.12 * (Math.random() - 0.5)) // spawn.shield(me, x, y, 1); - me.onHit = function() { //run this function on hitting player + me.onHit = function () { //run this function on hitting player this.explode(); }; me.damageReduction = 0.2 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1); - me.doAwake = function() { + me.doAwake = function () { if (!m.isBodiesAsleep) { // this.armor(); this.alwaysSeePlayer(); @@ -1032,7 +1047,7 @@ const spawn = { } } } - me.do = function() { + me.do = function () { this.checkStatus(); if (this.seePlayer.recall) { this.do = this.doAwake @@ -1042,7 +1057,7 @@ const spawn = { } } }; - me.onDeath = function() { + me.onDeath = function () { this.isSpawnBoss = false; let count = 0 //count other cells by id // console.log(this.spawnID) @@ -1097,18 +1112,18 @@ const spawn = { me.buffCount = 0 me.accelMag = 0.00005 //* simulation.accelScale; - me.setBuffed = function() { + me.setBuffed = function () { this.buffCount++ this.accelMag += 0.000035 //* Math.sqrt(simulation.accelScale) // Matter.Body.setDensity(this, 0.001 + 0.0003 * this.buffCount) // normal density is 0.001 //+ 0.0005 * Math.sqrt(simulation.difficulty) - this.fill = `hsl(144, ${5+10*this.buffCount}%, 50%)` + this.fill = `hsl(144, ${5 + 10 * this.buffCount}%, 50%)` const scale = 1.132; Matter.Body.scale(this, scale, scale); this.radius *= scale; // this.health += 0.03 // if (this.health > 1) this.health = 1 } - me.onDeath = function() { + me.onDeath = function () { this.isBuffBoss = false; let count = 0 //count other cells by id for (let i = 0, len = mob.length; i < len; i++) { @@ -1126,7 +1141,7 @@ const spawn = { } } me.damageReduction = 0.18 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1) - me.do = function() { + me.do = function () { // this.armor(); this.alwaysSeePlayer(); this.checkStatus(); @@ -1169,12 +1184,12 @@ const spawn = { } else if (!m.isCloak) { me.foundPlayer(); } - me.onHit = function() { //run this function on hitting player + 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"); }; - me.onDeath = function() { + me.onDeath = function () { this.leaveBody = false; if (vertices > 3) { this.isDropPowerUp = false; @@ -1187,9 +1202,9 @@ const spawn = { for (let i = 0; i < powerUp.length; i++) powerUp[i].collisionFilter.mask = cat.map | cat.powerUp }; me.damageReduction = 0.25 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1) - me.do = function() { + me.do = function () { // this.armor(); - this.stroke = `hsl(0,0%,${80+25*Math.sin(simulation.cycle*0.01)}%)` + 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++) { @@ -1233,7 +1248,7 @@ const spawn = { // me.onDeath = function () { //helps collisions functions work better after vertex have been changed // this.vertices = Matter.Vertices.hull(Matter.Vertices.clockwiseSort(this.vertices)) // } - me.do = function() { + me.do = function () { this.seePlayerByLookingAt(); this.checkStatus(); this.attraction(); @@ -1281,14 +1296,14 @@ const spawn = { Composite.add(engine.world, cons[cons.length - 1]); cons[len2].length = 100 + 1.5 * radius; me.cons2 = cons[len2]; - me.do = function() { + me.do = function () { this.gravity(); this.searchSpring(); this.checkStatus(); this.springAttack(); }; - me.onDeath = function() { + me.onDeath = function () { this.removeCons(); }; spawn.shield(me, x, y); @@ -1306,7 +1321,7 @@ const spawn = { me.randomHopFrequency = 200 + Math.floor(Math.random() * 150); me.randomHopCD = simulation.cycle + me.randomHopFrequency; spawn.shield(me, x, y); - me.do = function() { + me.do = function () { this.gravity(); this.seePlayerCheck(); this.checkStatus(); @@ -1351,12 +1366,12 @@ const spawn = { Matter.Body.setDensity(me, 0.03); //extra dense //normal is 0.001 //makes effective life much larger spawn.shield(me, x, y, 1); spawn.spawnOrbitals(me, radius + 60, 1) - me.onDeath = function() { + me.onDeath = function () { powerUps.spawnBossPowerUp(this.position.x, this.position.y) }; me.lastSpeed = me.speed me.damageReduction = 0.25 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1) - me.do = function() { + me.do = function () { // this.armor(); this.gravity(); this.seePlayerCheck(); @@ -1439,7 +1454,7 @@ const spawn = { me.lookTorque = 0.0000014; me.restitution = 0; spawn.shield(me, x, y); - me.look = function() { + me.look = function () { this.seePlayerByLookingAt(); this.checkStatus(); if (this.seePlayer.recall && this.cd < simulation.cycle) { @@ -1449,7 +1464,7 @@ const spawn = { } } me.do = me.look - me.spin = function() { + me.spin = function () { this.checkStatus(); this.torque += 0.000035 * this.inertia; this.fill = randomColor({ @@ -1486,7 +1501,7 @@ const spawn = { me.collisionFilter.mask = cat.player | cat.bullet //| cat.body me.memory = Infinity; Matter.Body.setDensity(me, 0.008); //extra dense //normal is 0.001 //makes effective life much larger - me.do = function() { + me.do = function () { //keep it slow, to stop issues from explosion knock backs if (this.speed > 5) { Matter.Body.setVelocity(this, { @@ -1565,7 +1580,7 @@ const spawn = { // me.frictionAir = 0.005; me.memory = 1600; Matter.Body.setDensity(me, 0.03); //extra dense //normal is 0.001 //makes effective life much larger - me.onDeath = function() { + me.onDeath = function () { //applying forces to player doesn't seem to work inside this method, not sure why powerUps.spawnBossPowerUp(this.position.x, this.position.y) if (simulation.difficulty > 5) { @@ -1583,12 +1598,11 @@ const spawn = { } toMe(body, this.position, this.eventHorizon) toMe(mob, this.position, this.eventHorizon) - // toMe(bullet, this.position, this.eventHorizon) + // toMe(bullet, this.position, this.eventHorizon)) } }; me.damageReduction = 0.25 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1) - me.do = function() { - // this.armor(); + me.do = function () { //keep it slow, to stop issues from explosion knock backs if (this.speed > 1) { Matter.Body.setVelocity(this, { @@ -1606,6 +1620,21 @@ const spawn = { } this.checkStatus(); if (this.seePlayer.recall) { + //throw large seekers + if (!(simulation.cycle % 240) && !m.isBodiesAsleep) { + spawn.seeker(this.position.x, this.position.y, 15 * (0.7 + 0.5 * Math.random()), 7); //give the bullet a rotational velocity as if they were attached to a vertex + const who = mob[mob.length - 1] + Matter.Body.setDensity(who, 0.00001); //normal is 0.001 + who.timeLeft = 600 + who.accelMag = 0.0002 * simulation.accelScale; //* (0.8 + 0.4 * Math.random()) + who.frictionAir = 0.01 //* (0. + const velocity = Vector.mult(Vector.normalise(Vector.sub(m.pos, this.position)), -20); //set direction to turn to fire //Vector.mult(Vector.perp(Vector.normalise(Vector.sub(this.position, this.vertices[index]))), -35) + Matter.Body.setVelocity(who, { + x: this.velocity.x + velocity.x, + y: this.velocity.y + velocity.y + }); + } + //accelerate towards the player const forceMag = this.accelMag * this.mass; const dx = this.seePlayer.position.x - this.position.x @@ -1711,7 +1740,7 @@ const spawn = { cons[len2].length = 100 + 1.5 * radius; me.cons2 = cons[len2]; me.damageReduction = 0.2 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1) //normal is 1, most bosses have 0.25 - me.do = function() { + me.do = function () { // this.armor(); this.gravity(); this.searchSpring(); @@ -1719,7 +1748,7 @@ const spawn = { this.springAttack(); }; - me.onDeath = function() { + me.onDeath = function () { this.removeCons(); powerUps.spawnBossPowerUp(this.position.x, this.position.y) }; @@ -1845,7 +1874,7 @@ const spawn = { me.frictionStatic = 0; me.friction = 0; spawn.shield(me, x, y); - me.do = function() { + me.do = function () { this.seePlayerByLookingAt(); this.checkStatus(); this.attraction(); @@ -1871,11 +1900,11 @@ const spawn = { me.stroke = "transparent"; //used for drawGhost me.collisionFilter.mask = cat.bullet | cat.body me.memory = Infinity - me.onDeath = function() { + me.onDeath = function () { powerUps.spawnBossPowerUp(this.position.x, this.position.y) }; me.damageReduction = 0.35 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1) // me.damageReductionGoal - me.awake = function() { + me.awake = function () { // this.armor(); this.checkStatus(); //health bar needs to be here because the position is being set @@ -1950,7 +1979,7 @@ const spawn = { Matter.Body.setPosition(this, { x: history.position.x, y: history.position.y - history.yOff + 24.2859 }) //bullets move with player } } - me.do = function() { + me.do = function () { if (this.seePlayer.recall || (!(simulation.cycle % this.seePlayerFreq) && this.distanceToPlayer2() < this.seeAtDistance2 && !m.isCloak)) { setTimeout(() => { this.do = this.awake @@ -1973,11 +2002,11 @@ const spawn = { me.accelMag = 0.00009 * simulation.accelScale; me.frictionStatic = 0; me.friction = 0; - me.onDamage = function() { + me.onDamage = function () { this.laserPos = this.position; }; spawn.shield(me, x, y); - me.do = function() { + me.do = function () { if (!m.isBodiesAsleep) { this.seePlayerByLookingAt(); this.checkStatus(); @@ -2018,7 +2047,7 @@ const spawn = { sub = Vector.normalise(Vector.sub(laserOffL, this.position)); laserOffL = Vector.add(laserOffL, Vector.mult(sub, rangeWidth)); ctx.lineTo(laserOffL.x, laserOffL.y); - ctx.fillStyle = `rgba(0,0,255,${Math.max(0,0.3*r/targetDist)})` + ctx.fillStyle = `rgba(0,0,255,${Math.max(0, 0.3 * r / targetDist)})` ctx.fill(); } } else { @@ -2046,16 +2075,16 @@ const spawn = { Matter.Body.setDensity(me, 0.008); //extra dense //normal is 0.001 //makes effective life much larger spawn.shield(me, x, y, 1); spawn.spawnOrbitals(me, radius + 200 + 300 * Math.random()) - me.onHit = function() { + me.onHit = function () { //run this function on hitting player // this.explode(); }; // spawn.shield(me, x, y, 1); //not working, not sure why - me.onDeath = function() { + me.onDeath = function () { powerUps.spawnBossPowerUp(this.position.x, this.position.y) }; me.damageReduction = 0.25 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1) - me.do = function() { + me.do = function () { // this.armor(); this.seePlayerByLookingAt(); this.checkStatus(); @@ -2083,7 +2112,7 @@ const spawn = { // this.force.y += mag * Math.sin(this.angle) // } - const vertexCollision = function(v1, v1End, domain) { + const vertexCollision = function (v1, v1End, domain) { for (let i = 0; i < domain.length; ++i) { let vertices = domain[i].vertices; const len = vertices.length - 1; @@ -2186,14 +2215,14 @@ const spawn = { Matter.Body.setDensity(me, 0.01); //extra dense //normal is 0.001 //makes effective life much larger spawn.shield(me, x, y, 1); spawn.spawnOrbitals(me, radius + 200 + 300 * Math.random()) - me.onHit = function() {}; - me.onDeath = function() { + me.onHit = function () { }; + me.onDeath = function () { powerUps.spawnBossPowerUp(this.position.x, this.position.y) }; me.damageReduction = 0.25 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1) me.targetingCount = 0; me.targetingTime = 60 - Math.min(58, 3 * simulation.difficulty) - me.do = function() { + me.do = function () { // this.armor(); this.seePlayerByLookingAt(); this.checkStatus(); @@ -2212,7 +2241,7 @@ const spawn = { } else if (c < -threshold) { this.torque -= 0.000004 * this.inertia; } - const vertexCollision = function(v1, v1End, domain) { + const vertexCollision = function (v1, v1End, domain) { for (let i = 0; i < domain.length; ++i) { let vertices = domain[i].vertices; const len = vertices.length - 1; @@ -2337,10 +2366,10 @@ const spawn = { me.delay = 30 + 35 * simulation.CDScale; me.nextBlinkCycle = me.delay; spawn.shield(me, x, y, 1); - me.onDamage = function() { + me.onDamage = function () { // this.cd = simulation.cycle + this.delay; }; - me.onDeath = function() { + me.onDeath = function () { const offAngle = Math.PI * Math.random() for (let i = 0, len = 3; i < len; i++) { spawn.grenade(this.position.x, this.position.y, this.grenadeDelay); @@ -2355,7 +2384,7 @@ const spawn = { powerUps.spawnBossPowerUp(this.position.x, this.position.y) } me.damageReduction = 0.25 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1) - me.do = function() { + me.do = function () { // this.armor(); this.seePlayerByHistory() if (this.nextBlinkCycle < simulation.cycle && this.seePlayer.yes) { //teleport towards the player @@ -2413,15 +2442,15 @@ const spawn = { spawn.shield(me, x, y, 1); spawn.spawnOrbitals(me, radius + 200 + 300 * Math.random(), 1) - me.onDeath = function() { + me.onDeath = function () { powerUps.spawnBossPowerUp(this.position.x, this.position.y) }; - me.onHit = function() {}; - me.do = function() { + me.onHit = function () { }; + me.do = function () { if (player.speed > 5) this.do = this.fire //don't attack until player moves } me.damageReduction = 0.25 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1) - me.fire = function() { + me.fire = function () { // this.armor(); this.checkStatus(); if (!m.isBodiesAsleep) { @@ -2521,8 +2550,8 @@ const spawn = { me.pulseRadius = Math.min(400, 170 + simulation.difficulty * 3) me.fireDelay = Math.max(75, 140 - simulation.difficulty * 0.5) me.isFiring = false - me.onHit = function() {}; - me.canSeeTarget = function() { + me.onHit = function () { }; + me.canSeeTarget = function () { const angle = this.angle + Math.PI / 2; const dot = Vector.dot({ x: Math.cos(angle), @@ -2540,7 +2569,7 @@ const spawn = { return true } } - me.do = function() { + me.do = function () { this.seePlayerByLookingAt(); this.checkStatus(); if (!m.isBodiesAsleep) { @@ -2636,11 +2665,11 @@ const spawn = { me.vertices = Matter.Vertices.rotate(me.vertices, Math.PI, me.position); //make the pointy side of triangle the front Matter.Body.rotate(me, Math.random() * Math.PI * 2); me.accelMag = 0.0001 * simulation.accelScale; - me.onHit = function() { + me.onHit = function () { //run this function on hitting player this.explode(); }; - me.do = function() { + me.do = function () { this.seePlayerByLookingAt(); this.checkStatus(); this.attraction(); @@ -2679,12 +2708,12 @@ const spawn = { me.isBoss = true; // spawn.shield(me, x, y, 1); //not working, not sure why - me.onDeath = function() { + me.onDeath = function () { powerUps.spawnBossPowerUp(this.position.x, this.position.y) }; me.rotateVelocity = Math.min(0.0045, 0.0015 * simulation.accelScale * simulation.accelScale) * (level.levelsCleared > 8 ? 1 : -1) * (simulation.isHorizontalFlipped ? -1 : 1) - me.do = function() { + me.do = function () { // this.armor(); this.fill = '#' + Math.random().toString(16).substr(-6); //flash colors this.checkStatus(); @@ -2727,8 +2756,8 @@ const spawn = { // Matter.Body.setPosition(this, this.startingPosition); }; - me.lasers = function(where, angle) { - const vertexCollision = function(v1, v1End, domain) { + me.lasers = function (where, angle) { + const vertexCollision = function (v1, v1End, domain) { for (let i = 0; i < domain.length; ++i) { let vertices = domain[i].vertices; const len = vertices.length - 1; @@ -2816,7 +2845,7 @@ const spawn = { Matter.Body.rotate(me, Math.PI * 0.1); spawn.shield(me, x, y); // me.onDamage = function () {}; - me.onDeath = function() { + me.onDeath = function () { if (this.spikeLength > 4) { this.spikeLength = 4 const spike = Vector.mult(Vector.normalise(Vector.sub(this.vertices[this.spikeVertex], this.position)), this.radius * this.spikeLength) @@ -2825,7 +2854,7 @@ const spawn = { // this.vertices = Matter.Vertices.hull(Matter.Vertices.clockwiseSort(this.vertices)) } }; - me.do = function() { + me.do = function () { if (!m.isBodiesAsleep) { // this.gravity(); this.seePlayerByLookingAt(); @@ -2891,10 +2920,10 @@ const spawn = { me.cd = Infinity; Matter.Body.rotate(me, Math.PI * 0.1); spawn.shield(me, x, y); - me.onDamage = function() { + me.onDamage = function () { this.cd = simulation.cycle + this.delay; }; - me.do = function() { + me.do = function () { this.gravity(); if (!(simulation.cycle % this.seePlayerFreq)) { // this.seePlayerCheck(); from mobs if ( @@ -2951,11 +2980,11 @@ const spawn = { 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; - me.onDamage = function() {}; - me.onDeath = function() { + me.onDamage = function () { }; + me.onDeath = function () { powerUps.spawnBossPowerUp(this.position.x, this.position.y) }; - me.do = function() { + me.do = function () { this.checkStatus(); this.seePlayerByHistory(60); this.attraction(); @@ -2968,8 +2997,8 @@ const spawn = { if (long > 0) this.laserSword(this.vertices[i], bend + this.angle + (i + 0.5) / sides * 2 * Math.PI, Math.abs(long)); } }; - me.laserSword = function(where, angle, length) { - const vertexCollision = function(v1, v1End, domain) { + me.laserSword = function (where, angle, length) { + const vertexCollision = function (v1, v1End, domain) { for (let i = 0; i < domain.length; ++i) { let vertices = domain[i].vertices; const len = vertices.length - 1; @@ -3042,14 +3071,14 @@ const spawn = { me.laserAngle = 3 * Math.PI / 5 const seeDistance2 = 200000 spawn.shield(me, x, y); - me.onDamage = function() {}; - me.do = function() { + me.onDamage = function () { }; + me.do = function () { this.checkStatus(); this.seePlayerByHistory(15); this.attraction(); if (!m.isBodiesAsleep) this.sword() //does various things depending on what stage of the sword swing }; - me.swordWaiting = function() { + me.swordWaiting = function () { if ( this.seePlayer.recall && this.cd < simulation.cycle && @@ -3074,7 +3103,7 @@ const spawn = { } } me.sword = me.swordWaiting //base function that changes during different aspects of the sword swing - me.swordGrow = function() { + me.swordGrow = function () { this.laserSword(this.vertices[this.swordVertex], this.angle + this.laserAngle); this.swordRadius += this.swordRadiusGrowRate if (this.swordRadius > this.swordRadiusMax) { @@ -3082,7 +3111,7 @@ const spawn = { this.spinCount = 0 } } - me.swordSlash = function() { + me.swordSlash = function () { this.laserSword(this.vertices[this.swordVertex], this.angle + this.laserAngle); this.torque += this.torqueMagnitude; this.spinCount++ @@ -3093,8 +3122,8 @@ const spawn = { this.cd = simulation.cycle + this.delay; } } - me.laserSword = function(where, angle) { - const vertexCollision = function(v1, v1End, domain) { + me.laserSword = function (where, angle) { + const vertexCollision = function (v1, v1End, domain) { for (let i = 0; i < domain.length; ++i) { let v = domain[i].vertices; const len = v.length - 1; @@ -3160,7 +3189,7 @@ const spawn = { me.collisionFilter.mask = cat.map | cat.body | cat.bullet | cat.mob //can't touch player me.showHealthBar = false; me.memory = 240; - me.do = function() { + me.do = function () { this.gravity(); this.seePlayerByHistory(15); this.checkStatus(); @@ -3211,7 +3240,7 @@ const spawn = { me.collisionFilter.mask = cat.bullet //| cat.body me.showHealthBar = false; me.memory = 480; - me.do = function() { + me.do = function () { //cap max speed if (this.speed > 5) { Matter.Body.setVelocity(this, { @@ -3330,11 +3359,11 @@ const spawn = { radiusOrbitals = radius + 125 + 350 * Math.random() for (let i = 0; i < len; i++) spawn.orbital(me, radiusOrbitals, i / len * 2 * Math.PI, -speed) - me.onDeath = function() { + me.onDeath = function () { powerUps.spawnBossPowerUp(this.position.x, this.position.y) }; me.damageReduction = 0.2 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1) - me.do = function() { + me.do = function () { // this.armor(); this.seePlayerCheckByDistance(); this.checkStatus(); @@ -3366,11 +3395,11 @@ const spawn = { x: 0, y: 0 }; - me.onDeath = function() { //helps collisions functions work better after vertex have been changed + me.onDeath = function () { //helps collisions functions work better after vertex have been changed // this.vertices = Matter.Vertices.hull(Matter.Vertices.clockwiseSort(this.vertices)) } // spawn.shield(me, x, y); - me.do = function() { + me.do = function () { this.seePlayerByLookingAt(); this.checkStatus(); this.fire(); @@ -3416,13 +3445,13 @@ const spawn = { spawn.spawnOrbitals(me, radius + 75, 1); }, 100); //have to wait a sec so the tether constraint doesn't attach to an orbital Matter.Body.setDensity(me, 0.008 + 0.0003 * Math.sqrt(simulation.difficulty)); //extra dense //normal is 0.001 //makes effective life much larger - me.onDeath = function() { + 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.damageReduction = 0.25 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1) - me.do = function() { + me.do = function () { // this.armor(); this.seePlayerByLookingAt(); this.checkStatus(); @@ -3438,7 +3467,7 @@ const spawn = { mobs.spawn(x, y, sides, radius, "rgb(255,0,0)"); let me = mob[mob.length - 1]; me.stroke = "transparent"; - me.onHit = function() { + me.onHit = function () { this.explode(this.mass * 20); }; Matter.Body.setDensity(me, 0.00004); //normal is 0.001 @@ -3453,7 +3482,7 @@ const spawn = { me.showHealthBar = false; me.collisionFilter.category = cat.mobBullet; me.collisionFilter.mask = cat.player | cat.map | cat.body | cat.bullet; - me.do = function() { + me.do = function () { this.gravity(); this.timeLimit(); }; @@ -3462,10 +3491,10 @@ const spawn = { mobs.spawn(x, y, sides, radius, "rgb(255,0,0)"); let me = mob[mob.length - 1]; me.stroke = "transparent"; - me.onHit = function() { + me.onHit = function () { this.explode(this.mass * 120); }; - me.onDeath = function() { + me.onDeath = function () { spawn.bullet(this.position.x, this.position.y, this.radius / 3, 5); spawn.bullet(this.position.x, this.position.y, this.radius / 3, 5); spawn.bullet(this.position.x, this.position.y, this.radius / 3, 5); @@ -3505,7 +3534,7 @@ const spawn = { me.showHealthBar = false; me.collisionFilter.category = cat.mobBullet; me.collisionFilter.mask = cat.player | cat.map | cat.body | cat.bullet; - me.do = function() { + me.do = function () { this.gravity(); this.timeLimit(); }; @@ -3535,11 +3564,11 @@ const spawn = { x: 0, y: 0 }; - me.onDeath = function() { //helps collisions functions work better after vertex have been changed + me.onDeath = function () { //helps collisions functions work better after vertex have been changed // this.vertices = Matter.Vertices.hull(Matter.Vertices.clockwiseSort(this.vertices)) } // spawn.shield(me, x, y); - me.do = function() { + me.do = function () { // this.seePlayerByLookingAt(); this.seePlayerCheck(); this.checkStatus(); @@ -3630,7 +3659,7 @@ const spawn = { mobs.spawn(x, y, sides, radius, "rgb(255,0,155)"); let me = mob[mob.length - 1]; me.stroke = "transparent"; - me.onHit = function() { + me.onHit = function () { this.explode(this.mass * 20); }; Matter.Body.setDensity(me, 0.00005); //normal is 0.001 @@ -3645,7 +3674,7 @@ const spawn = { me.showHealthBar = false; me.collisionFilter.category = cat.mobBullet; me.collisionFilter.mask = cat.player | cat.map | cat.body | cat.bullet; - me.do = function() { + me.do = function () { // this.gravity(); this.timeLimit(); if (Matter.Query.collides(this, map).length > 0 || Matter.Query.collides(this, body).length > 0 && this.speed < 3) { @@ -3654,6 +3683,36 @@ const spawn = { } }; }, + launcherOne(x, y, radius = 30 + Math.ceil(Math.random() * 40)) { + mobs.spawn(x, y, 3, radius, "rgb(150,150,255)"); + let me = mob[mob.length - 1]; + me.accelMag = 0.00004 * simulation.accelScale; + me.fireFreq = Math.floor(420 + 90 * Math.random() * simulation.CDScale) + me.frictionStatic = 0; + me.friction = 0; + me.frictionAir = 0.015; + spawn.shield(me, x, y); + me.onDamage = function () { }; + me.do = function () { + this.seePlayerCheck(); + this.checkStatus(); + this.attraction(); + if (this.seePlayer.recall && !(simulation.cycle % this.fireFreq) && !m.isBodiesAsleep) { + Matter.Body.setAngularVelocity(this, 0.14) + spawn.seeker(this.vertices[0].x, this.vertices[0].y, 20, 9); //give the bullet a rotational velocity as if they were attached to a vertex + const who = mob[mob.length - 1] + Matter.Body.setDensity(who, 0.00003); //normal is 0.001 + who.timeLeft = 840 //* (0.8 + 0.4 * Math.random()); + who.accelMag = 0.00035 * simulation.accelScale; //* (0.8 + 0.4 * Math.random()) + who.frictionAir = 0.01 //* (0.8 + 0.4 * Math.random()); + const velocity = Vector.mult(Vector.perp(Vector.normalise(Vector.sub(this.position, this.vertices[0]))), -6) + Matter.Body.setVelocity(who, { + x: this.velocity.x + velocity.x, + y: this.velocity.y + velocity.y + }); + } + }; + }, launcher(x, y, radius = 30 + Math.ceil(Math.random() * 40)) { mobs.spawn(x, y, 3, radius, "rgb(150,150,255)"); let me = mob[mob.length - 1]; @@ -3663,8 +3722,8 @@ const spawn = { me.friction = 0; me.frictionAir = 0.02; spawn.shield(me, x, y); - me.onDamage = function() {}; - me.do = function() { + me.onDamage = function () { }; + me.do = function () { this.seePlayerCheck(); this.checkStatus(); this.attraction(); @@ -3699,13 +3758,13 @@ const spawn = { spawn.spawnOrbitals(me, radius + 50 + 200 * Math.random()) Matter.Body.setDensity(me, 0.0022 + 0.0002 * Math.sqrt(simulation.difficulty)); //extra dense //normal is 0.001 //makes effective life much larger - me.onDeath = function() { + 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.onDamage = function () { }; me.damageReduction = 0.25 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1) - me.do = function() { + me.do = function () { // this.armor(); this.seePlayerCheck(); this.checkStatus(); @@ -3743,7 +3802,7 @@ const spawn = { spawn.spawnOrbitals(me, radius + 125, 1); spawn.spawnOrbitals(me, radius + 200, 1); Matter.Body.setDensity(me, 0.004 + 0.0002 * Math.sqrt(simulation.difficulty)); //extra dense //normal is 0.001 //makes effective life much larger - me.onDeath = function() { //helps collisions functions work better after vertex have been changed + me.onDeath = function () { //helps collisions functions work better after vertex have been changed for (let i = 0; i < 6; i++) { spawn.grenade(this.position.x, this.position.y, 75 * simulation.CDScale); const who = mob[mob.length - 1] @@ -3757,7 +3816,7 @@ const spawn = { powerUps.spawnBossPowerUp(this.position.x, this.position.y) } me.grenadeLimiter = 0 - me.onDamage = function() { + me.onDamage = function () { if (this.grenadeLimiter < 240) { this.grenadeLimiter += 60 spawn.grenade(this.position.x, this.position.y, 80 + Math.floor(60 * Math.random())); @@ -3770,7 +3829,7 @@ const spawn = { } }; me.damageReduction = 0.25 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1) - me.do = function() { + me.do = function () { // this.armor(); if (this.grenadeLimiter > 1) this.grenadeLimiter-- this.seePlayerCheck(); @@ -3798,13 +3857,13 @@ const spawn = { x: 0, y: 0 }; - me.onDeath = function() { //helps collisions functions work better after vertex have been changed + me.onDeath = function () { //helps collisions functions work better after vertex have been changed spawn.grenade(this.position.x, this.position.y, 75 * simulation.CDScale); // mob[mob.length - 1].collisionFilter.category = 0 mob[mob.length - 1].collisionFilter.mask = cat.player | cat.map; } // spawn.shield(me, x, y); - me.do = function() { + me.do = function () { this.seePlayerCheck(); this.checkStatus(); @@ -3860,7 +3919,7 @@ const spawn = { mobs.spawn(x, y, 4, size, "rgb(215,0,190)"); //rgb(215,80,190) let me = mob[mob.length - 1]; me.stroke = "transparent"; - me.onHit = function() { + me.onHit = function () { this.explode(this.mass * 20); }; Matter.Body.setDensity(me, 0.00004); //normal is 0.001 @@ -3874,7 +3933,7 @@ const spawn = { me.isDropPowerUp = false; me.isBadTarget = true; me.isMobBullet = true; - me.onDeath = function() { + me.onDeath = function () { //damage player if in range if (Vector.magnitude(Vector.sub(player.position, this.position)) < pulseRadius && m.immuneCycle < m.cycle) { m.immuneCycle = m.cycle + tech.collisionImmuneCycles; //player is immune to damage @@ -3892,7 +3951,7 @@ const spawn = { me.collisionFilter.category = cat.mobBullet; me.collisionFilter.mask = cat.map | cat.body | cat.player // me.collisionFilter.mask = 0 - me.do = function() { + me.do = function () { this.timeLimit(); ctx.beginPath(); //draw explosion outline ctx.arc(this.position.x, this.position.y, pulseRadius * (1.01 - this.timeLeft / this.lifeSpan), 0, 2 * Math.PI); //* this.fireCycle / this.fireDelay @@ -3929,15 +3988,15 @@ const spawn = { spawn.spawnOrbitals(me, radius + 50 + 200 * Math.random()) Matter.Body.setDensity(me, 0.0045); //extra dense //normal is 0.001 //makes effective life much larger - me.onDeath = function() { + 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.onDamage = function () { this.cycle = 0 }; me.damageReduction = 0.25 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1) - me.do = function() { + me.do = function () { // this.armor(); this.checkStatus(); ctx.beginPath(); //draw cycle timer @@ -3991,13 +4050,13 @@ const spawn = { spawn.spawnOrbitals(me, radius + 50 + 200 * Math.random()) Matter.Body.setDensity(me, 0.01); //extra dense //normal is 0.001 //makes effective life much larger - me.onDeath = function() { + 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.onDamage = function () { }; me.damageReduction = 0.25 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1) - me.do = function() { + me.do = function () { // this.armor(); this.seePlayerCheck(); this.checkStatus(); @@ -4057,7 +4116,7 @@ const spawn = { mobs.spawn(x, y, sides, radius, "rgb(255,0,255)"); let me = mob[mob.length - 1]; me.stroke = "transparent"; - me.onHit = function() { + me.onHit = function () { this.explode(this.mass * 20); }; Matter.Body.setDensity(me, 0.000015); //normal is 0.001 @@ -4072,7 +4131,7 @@ const spawn = { me.showHealthBar = false; me.collisionFilter.category = cat.mobBullet; me.collisionFilter.mask = cat.player | cat.map | cat.body | cat.bullet; - me.do = function() { + me.do = function () { // this.seePlayer.yes = false; this.alwaysSeePlayer() this.attraction(); @@ -4085,7 +4144,7 @@ const spawn = { me.g = 0.0004; //required if using this.gravity me.leaveBody = false; // me.isDropPowerUp = false; - me.onDeath = function() { //run this function on death + me.onDeath = function () { //run this function on death for (let i = 0; i < Math.ceil(this.mass * 0.15 + Math.random() * 2.5); ++i) { spawn.spawns(this.position.x + (Math.random() - 0.5) * radius * 2.5, this.position.y + (Math.random() - 0.5) * radius * 2.5); Matter.Body.setVelocity(mob[mob.length - 1], { @@ -4095,7 +4154,7 @@ const spawn = { } }; spawn.shield(me, x, y); - me.do = function() { + me.do = function () { this.gravity(); this.seePlayerCheck(); this.checkStatus(); @@ -4105,7 +4164,7 @@ const spawn = { spawns(x, y, radius = 15) { mobs.spawn(x, y, 4, radius, "rgb(255,0,0)"); let me = mob[mob.length - 1]; - me.onHit = function() { //run this function on hitting player + me.onHit = function () { //run this function on hitting player this.explode(); }; // me.stroke = "transparent" @@ -4119,7 +4178,7 @@ const spawn = { me.leaveBody = false; me.seePlayerFreq = Math.floor((80 + 50 * Math.random())); me.frictionAir = 0.004; - me.do = function() { + me.do = function () { this.gravity(); this.seePlayerCheck(); this.checkStatus(); @@ -4176,12 +4235,12 @@ const spawn = { exploder(x, y, radius = 40 + Math.ceil(Math.random() * 50)) { mobs.spawn(x, y, 4, radius, "rgb(255,0,0)"); let me = mob[mob.length - 1]; - me.onHit = function() { + me.onHit = function () { //run this function on hitting player this.explode(); }; me.g = 0.0004; //required if using this.gravity - me.do = function() { + me.do = function () { this.gravity(); this.seePlayerCheck(); this.checkStatus(); @@ -4201,7 +4260,7 @@ const spawn = { me.memory = 250; me.laserRange = 500; Matter.Body.setDensity(me, 0.0022 + 0.00022 * Math.sqrt(simulation.difficulty)); //extra dense //normal is 0.001 //makes effective life much larger - me.onDeath = function() { + me.onDeath = function () { powerUps.spawnBossPowerUp(this.position.x, this.position.y) for (let i = 0; i < mob.length; i++) { //wake up tail mobs if (mob[i].isSnakeTail && mob[i].alive) { @@ -4216,7 +4275,7 @@ const spawn = { // me.closestVertex2 = 1; me.cycle = 0 me.damageReduction = 0.2 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1) - me.do = function() { + me.do = function () { // this.armor(); this.seePlayerByHistory() this.checkStatus(); @@ -4319,7 +4378,7 @@ const spawn = { me.memory = 250; me.laserRange = 500; Matter.Body.setDensity(me, 0.00165 + 0.00011 * Math.sqrt(simulation.difficulty)); //extra dense //normal is 0.001 //makes effective life much larger - me.onDeath = function() { + me.onDeath = function () { powerUps.spawnBossPowerUp(this.position.x, this.position.y) for (let i = 0; i < mob.length; i++) { //wake up tail mobs if (mob[i].isSnakeTail && mob[i].alive) { @@ -4330,7 +4389,7 @@ const spawn = { } }; me.damageReduction = 0.25 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1) - me.do = function() { + me.do = function () { // this.armor(); this.seePlayerByHistory() this.checkStatus(); @@ -4387,7 +4446,7 @@ const spawn = { me.frictionAir = 0.02; me.isSnakeTail = true; me.stroke = "transparent" - me.onDeath = function() { + me.onDeath = function () { if (this.isSnakeTail) { //wake up tail mobs for (let i = 0; i < mob.length; i++) { if (mob[i].isSnakeTail && mob[i].alive) { @@ -4398,10 +4457,10 @@ const spawn = { } } }; - me.do = function() { + me.do = function () { this.checkStatus(); }; - me.doActive = function() { + me.doActive = function () { this.checkStatus(); this.alwaysSeePlayer(); this.attraction(); @@ -4431,12 +4490,12 @@ const spawn = { 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() { + me.onDeath = function () { powerUps.spawnBossPowerUp(this.position.x, this.position.y) this.removeCons(); //remove constraint }; me.damageReduction = 0.25 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1) - me.do = function() { + me.do = function () { // this.armor(); this.gravity(); this.seePlayerCheck(); @@ -4464,10 +4523,10 @@ const spawn = { }); Composite.add(engine.world, consBB[consBB.length - 1]); - me.onDamage = function() { + me.onDamage = function () { //make sure the mob that owns the shield can tell when damage is done this.alertNearByMobs(); - this.fill = `rgba(220,220,255,${0.3 + 0.6 *this.health})` + this.fill = `rgba(220,220,255,${0.3 + 0.6 * this.health})` }; me.leaveBody = false; me.isDropPowerUp = false; @@ -4475,13 +4534,13 @@ const spawn = { me.shieldTargetID = target.id target.isShielded = true; - me.onDeath = function() { + me.onDeath = function () { //clear isShielded status from target for (let i = 0, len = mob.length; i < len; i++) { if (mob[i].id === this.shieldTargetID) mob[i].isShielded = false; } }; - me.do = function() { + me.do = function () { this.checkStatus(); }; @@ -4514,11 +4573,11 @@ const spawn = { }); Composite.add(engine.world, consBB[consBB.length - 1]); } - me.onDamage = function() { + me.onDamage = function () { this.alertNearByMobs(); //makes sure the mob that owns the shield can tell when damage is done - this.fill = `rgba(220,220,255,${0.3 + 0.6 *this.health})` + this.fill = `rgba(220,220,255,${0.3 + 0.6 * this.health})` }; - me.onDeath = function() { + me.onDeath = function () { //clear isShielded status from target for (let j = 0; j < targets.length; j++) { for (let i = 0, len = mob.length; i < len; i++) { @@ -4531,7 +4590,7 @@ const spawn = { me.showHealthBar = false; mob[mob.length - 1] = mob[mob.length - 1 - nodes]; mob[mob.length - 1 - nodes] = me; - me.do = function() { + me.do = function () { this.checkStatus(); }; }, @@ -4558,7 +4617,7 @@ const spawn = { // me.isShielded = true me.collisionFilter.category = cat.mobBullet; me.collisionFilter.mask = cat.bullet; //cat.player | cat.map | cat.body - me.do = function() { + me.do = function () { //if host is gone if (!who || !who.alive) { this.death(); @@ -4617,11 +4676,11 @@ const spawn = { for (let j = 0; j < nodes; j++) { for (let i = 0, len = subNodes; i < len; i++) spawn.orbital(mob[orbitalIndexes[j]], range, i / len * 2 * Math.PI, speed) } - me.onDeath = function() { + me.onDeath = function () { powerUps.spawnBossPowerUp(this.position.x, this.position.y) }; me.damageReduction = 0.25 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1) - me.do = function() { + me.do = function () { // this.armor(); this.seePlayerCheckByDistance(); this.checkStatus(); @@ -4786,7 +4845,7 @@ const spawn = { me.isBadTarget = true; me.isUnblockable = true; - me.do = function() { + me.do = function () { let wireX = -50; let wireY = -1000; if (this.freeOfWires) { @@ -4855,7 +4914,7 @@ const spawn = { me.isBadTarget = true; me.isUnblockable = true; - me.do = function() { + me.do = function () { let wireX = -50 - 20; let wireY = -1000; @@ -4907,7 +4966,7 @@ const spawn = { me.isBadTarget = true; me.isUnblockable = true; - me.do = function() { + me.do = function () { let wireX = -50 - 35; let wireY = -1000; @@ -4958,7 +5017,7 @@ const spawn = { me.isBadTarget = true; me.isUnblockable = true; - me.do = function() { + me.do = function () { let wireX = -50 + 16; let wireY = -1000; @@ -5009,7 +5068,7 @@ const spawn = { me.isBadTarget = true; me.isUnblockable = true; - me.do = function() { + me.do = function () { let wireX = -50 + 26; let wireY = -1000; diff --git a/js/tech.js b/js/tech.js index 8f23f10..89aa23d 100644 --- a/js/tech.js +++ b/js/tech.js @@ -1,46 +1,138 @@ - const tech = { - totalCount: null, - setupAllTech() { +const tech = { + totalCount: null, + setupAllTech() { + for (let i = 0, len = tech.tech.length; i < len; i++) { + tech.tech[i].count = 0 + tech.tech[i].isLost = false + tech.tech[i].remove(); + if (tech.tech[i].isJunk) { + tech.tech[i].frequency = 0 + } else if (tech.tech[i].frequencyDefault) { + tech.tech[i].frequency = tech.tech[i].frequencyDefault + } else { + tech.tech[i].frequency = 2 + } + } + lore.techCount = 0; + if (simulation.isCheating) { //simulation.isCommunityMaps || for (let i = 0, len = tech.tech.length; i < len; i++) { - tech.tech[i].count = 0 - tech.tech[i].isLost = false - tech.tech[i].remove(); - if (tech.tech[i].isJunk) { - tech.tech[i].frequency = 0 - } else if (tech.tech[i].frequencyDefault) { - tech.tech[i].frequency = tech.tech[i].frequencyDefault - } else { - tech.tech[i].frequency = 2 + if (tech.tech[i].isLore) { + tech.tech[i].frequency = 0; + tech.tech[i].count = 0; } } - lore.techCount = 0; - if (simulation.isCheating) { //simulation.isCommunityMaps || - for (let i = 0, len = tech.tech.length; i < len; i++) { - if (tech.tech[i].isLore) { - tech.tech[i].frequency = 0; - tech.tech[i].count = 0; - } + } + // tech.removeJunkTechFromPool(); + // tech.removeLoreTechFromPool(); + // tech.addLoreTechToPool(); + tech.extraMaxHealth = 0; + tech.totalCount = 0; + simulation.updateTechHUD(); + }, + removeTech(index = 'random') { + if (index === 'random') { + const have = [] //find which tech you have + for (let i = 0; i < tech.tech.length; i++) { + if (tech.tech[i].count > 0 && !tech.tech[i].isNonRefundable) have.push(i) + } + if (have.length) { + index = have[Math.floor(Math.random() * have.length)] + } else { + return 0 //if none found don't remove any tech + } + } else if (isNaN(index)) { //find index by name + let found = false; + for (let i = 0; i < tech.tech.length; i++) { + if (index === tech.tech[i].name) { + index = i; + found = true; + break; } } - // tech.removeJunkTechFromPool(); - // tech.removeLoreTechFromPool(); - // tech.addLoreTechToPool(); - tech.extraMaxHealth = 0; - tech.totalCount = 0; - simulation.updateTechHUD(); - }, - removeTech(index = 'random') { - if (index === 'random') { - const have = [] //find which tech you have - for (let i = 0; i < tech.tech.length; i++) { - if (tech.tech[i].count > 0 && !tech.tech[i].isNonRefundable) have.push(i) + if (!found) return 0 //if name not found don't remove any tech + } + if (tech.tech[index].count === 0) return 0 + const totalRemoved = tech.tech[index].count + simulation.makeTextLog(`tech.removeTech("${tech.tech[index].name}")`) + tech.tech[index].remove(); + tech.tech[index].count = 0; + simulation.updateTechHUD(); + tech.tech[index].isLost = true + simulation.updateTechHUD(); + return totalRemoved //return the total number of tech removed + }, + // onclick="tech.removeTechPaused(${i}, this)" //add this to tech elements in pause menu + // removeTechPaused(index, who) { + // tech.tech[index].remove(); + // tech.tech[index].count = 0; + // simulation.updateTechHUD(); + // who.innerHTML = "removed" + // // who.style.display = "none" + // }, + // removeLoreTechFromPool() { + // for (let i = tech.tech.length - 1; i > 0; i--) { + // if (tech.tech[i].isLore && tech.tech[i].count === 0) tech.tech.splice(i, 1) + // } + // }, + addJunkTechToPool(chance) { //chance is number between 0-1 + // { //count JUNK + // let count = 0 + // for (let i = 0, len = tech.tech.length; i < len; i++) { + // if (tech.tech[i].count < tech.tech[i].maxCount && tech.tech[i].allowed() && tech.tech[i].isJunk && tech.tech[i].frequency > 0) count += tech.tech[i].frequency + // } + // console.log(count) + // } + // { //count not JUNK + // let count = 0 + // for (let i = 0, len = tech.tech.length; i < len; i++) { + // if (tech.tech[i].count < tech.tech[i].maxCount && tech.tech[i].allowed() && !tech.tech[i].isJunk && tech.tech[i].frequency > 0) count++ + // } + // console.log(count) + // } + // count total non junk tech + let count = 0 + for (let i = 0, len = tech.tech.length; i < len; i++) { + if (tech.tech[i].count < tech.tech[i].maxCount && tech.tech[i].allowed() && !tech.tech[i].isJunk) count += tech.tech[i].frequency + } + //make an array for possible junk tech to add + let options = []; + for (let i = 0; i < tech.tech.length; i++) { + if (tech.tech[i].count < tech.tech[i].maxCount && tech.tech[i].isJunk) options.push(i); + } + //add random array options to tech pool + if (options.length) { + const num = chance * count //scale number added + for (let i = 0; i < num; i++) tech.tech[options[Math.floor(Math.random() * options.length)]].frequency++ + simulation.makeTextLog(`tech.tech.push(${num} JUNK)`) + return num + } else { + return 0 + } + }, + removeJunkTechFromPool(num = 1) { + for (let j = 0; j < num; j++) { + for (let i = 0; i < tech.tech.length; i++) { + if (tech.tech[i].isJunk && tech.tech[i].frequency > 0 && tech.tech[i].count < tech.tech[i].maxCount) { + tech.tech[i].frequency-- + break } - if (have.length) { - index = have[Math.floor(Math.random() * have.length)] - } else { - return 0 //if none found don't remove any tech - } - } else if (isNaN(index)) { //find index by name + } + } + }, + giveTech(index = 'random') { + if (index === 'random') { + let options = []; + for (let i = 0; i < tech.tech.length; i++) { + if (tech.tech[i].count < tech.tech[i].maxCount && tech.tech[i].allowed() && !tech.tech[i].isJunk && !tech.tech[i].isLore && !tech.tech[i].isBadRandomOption) options.push(i); + } + // give a random tech from the tech I don't have + if (options.length > 0) { + let newTech = options[Math.floor(Math.random() * options.length)] + tech.giveTech(newTech) + simulation.makeTextLog(`tech.giveTech("${tech.tech[newTech].name}") //random tech`); + } + } else { + if (isNaN(index)) { //find index by name let found = false; for (let i = 0; i < tech.tech.length; i++) { if (index === tech.tech[i].name) { @@ -49,7415 +141,7402 @@ break; } } - if (!found) return 0 //if name not found don't remove any tech + if (!found) return //if name not found don't give any tech } - if (tech.tech[index].count === 0) return 0 - const totalRemoved = tech.tech[index].count - simulation.makeTextLog(`tech.removeTech("${tech.tech[index].name}")`) - tech.tech[index].remove(); - tech.tech[index].count = 0; + if (tech.isMetaAnalysis && tech.tech[index].isJunk) { + simulation.makeTextLog(`//tech: meta-analysis replaced junk tech with random tech`); + tech.giveTech('random') + for (let i = 0; i < 3; i++) powerUps.spawn(m.pos.x + 40 * Math.random(), m.pos.y + 40 * Math.random(), "research"); + return + } + + if (tech.tech[index].isLost) tech.tech[index].isLost = false; //give specific tech + tech.tech[index].effect(); //give specific tech + tech.tech[index].count++ + tech.totalCount++ //used in power up randomization simulation.updateTechHUD(); - tech.tech[index].isLost = true - simulation.updateTechHUD(); - return totalRemoved //return the total number of tech removed - }, - // onclick="tech.removeTechPaused(${i}, this)" //add this to tech elements in pause menu - // removeTechPaused(index, who) { - // tech.tech[index].remove(); - // tech.tech[index].count = 0; - // simulation.updateTechHUD(); - // who.innerHTML = "removed" - // // who.style.display = "none" - // }, - // removeLoreTechFromPool() { - // for (let i = tech.tech.length - 1; i > 0; i--) { - // if (tech.tech[i].isLore && tech.tech[i].count === 0) tech.tech.splice(i, 1) - // } - // }, - addJunkTechToPool(chance) { //chance is number between 0-1 - // { //count JUNK - // let count = 0 - // for (let i = 0, len = tech.tech.length; i < len; i++) { - // if (tech.tech[i].count < tech.tech[i].maxCount && tech.tech[i].allowed() && tech.tech[i].isJunk && tech.tech[i].frequency > 0) count += tech.tech[i].frequency - // } - // console.log(count) - // } - // { //count not JUNK - // let count = 0 - // for (let i = 0, len = tech.tech.length; i < len; i++) { - // if (tech.tech[i].count < tech.tech[i].maxCount && tech.tech[i].allowed() && !tech.tech[i].isJunk && tech.tech[i].frequency > 0) count++ - // } - // console.log(count) - // } - // count total non junk tech - let count = 0 + } + }, + // setTechoNonRefundable(name) { + // for (let i = 0; i < tech.tech.length; i++) { + // if (tech.tech.name === name) { + // tech.tech[i].isNonRefundable = true; + // return + // } + // } + // }, + setCheating() { + if (!simulation.isCheating) { + simulation.isCheating = true; + level.levelAnnounce(); + lore.techCount = 0; for (let i = 0, len = tech.tech.length; i < len; i++) { - if (tech.tech[i].count < tech.tech[i].maxCount && tech.tech[i].allowed() && !tech.tech[i].isJunk) count += tech.tech[i].frequency + if (tech.tech[i].isLore) { + tech.tech[i].frequency = 0; + tech.tech[i].count = 0; + } } - //make an array for possible junk tech to add - let options = []; + console.log('cheating') + sound.tone(250) + sound.tone(300) + sound.tone(375) + } + }, + haveGunCheck(name, needActive = true) { + // if ( + // !build.isExperimentSelection && + // b.inventory.length > 2 && + // name !== b.guns[b.activeGun].name && + // 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 + // } + // return false + if (build.isExperimentSelection || !needActive) { + for (i = 0, len = b.inventory.length; i < len; i++) { + if (b.guns[b.inventory[i]].name === name) return true + } + return false + } else { //must be holding gun, this is the standard while playing + return b.inventory.length > 0 && b.guns[b.activeGun].name === name + } + }, + hasExplosiveDamageCheck() { + return tech.haveGunCheck("missiles") || tech.isMissileField || tech.missileBotCount > 0 || tech.boomBotCount > 1 || tech.isIncendiary || tech.isPulseLaser || tech.isTokamak || (tech.haveGunCheck("grenades") && !tech.isNeutronBomb) + }, + damageFromTech() { + let dmg = 1 //m.fieldDamage + if (tech.isAxion && tech.isHarmMACHO) dmg *= 1 + 0.75 * (1 - m.harmReduction()) + if (tech.OccamDamage) dmg *= tech.OccamDamage + if (tech.isCloakingDamage) dmg *= 1.35 + if (tech.isFlipFlopDamage && tech.isFlipFlopOn) dmg *= 1.5 + if (tech.isAnthropicDamage && tech.isDeathAvoidedThisLevel) dmg *= 2.3703599 + if (m.isSneakAttack && m.cycle > m.lastKillCycle + 240) dmg *= tech.sneakAttackDmg + if (tech.isTechDamage) dmg *= 1.9 + if (tech.isDupDamage) dmg *= 1 + Math.min(1, tech.duplicationChance()) + if (tech.isLowEnergyDamage) dmg *= 1 + Math.max(0, 1 - m.energy) * 0.5 + if (tech.isMaxEnergyTech) dmg *= 1.5 + if (tech.isEnergyNoAmmo) dmg *= 1.6 + if (tech.isDamageForGuns) dmg *= 1 + 0.14 * b.inventory.length + if (tech.isLowHealthDmg) dmg *= 1 + Math.max(0, 1 - m.health) * 0.5 + if (tech.isHarmDamage && m.lastHarmCycle + 600 > m.cycle) dmg *= 3; + if (tech.isEnergyLoss) dmg *= 1.55; + if (tech.isAcidDmg && m.health > 1) dmg *= 1.35; + if (tech.restDamage > 1 && player.speed < 1) dmg *= tech.restDamage + if (tech.isEnergyDamage) dmg *= 1 + m.energy / 11; + if (tech.isDamageFromBulletCount) dmg *= 1 + bullet.length * 0.007 + if (tech.isRerollDamage) dmg *= 1 + 0.037 * powerUps.research.count + if (tech.isOneGun && b.inventory.length < 2) dmg *= 1.1995 + if (tech.isNoFireDamage && m.cycle > m.fireCDcycle + 120) dmg *= 2 + if (tech.isSpeedDamage) dmg *= 1 + Math.min(0.66, player.speed * 0.0165) + if (tech.isBotDamage) dmg *= 1 + 0.07 * b.totalBots() + if (tech.isDamageAfterKillNoRegen && m.lastKillCycle + 300 > m.cycle) dmg *= 1.5 + return dmg * tech.slowFire * tech.aimDamage + }, + duplicationChance() { + return Math.max(0, (tech.isPowerUpsVanish ? 0.12 : 0) + (tech.isStimulatedEmission ? 0.15 : 0) + tech.cancelCount * 0.04 + tech.duplicateChance + m.duplicateChance + tech.wormDuplicate + tech.cloakDuplication + (tech.isAnthropicTech && tech.isDeathAvoidedThisLevel ? 0.45 : 0)) + }, + isScaleMobsWithDuplication: false, + maxDuplicationEvent() { + if (tech.is111Duplicate && tech.duplicationChance() > 1.11) { + tech.is111Duplicate = false + const range = 1300 + tech.isScaleMobsWithDuplication = true + for (let i = 0, len = 9; i < len; i++) { + const angle = 2 * Math.PI * i / len + spawn.randomLevelBoss(m.pos.x + range * Math.cos(angle), m.pos.y + range * Math.sin(angle), spawn.nonCollideBossList); + } + spawn.historyBoss(0, 0) + spawn.pulsarBoss(level.exit.x, level.exit.y, 70, true) + spawn.blockBoss(level.enter.x, level.enter.y) + tech.isScaleMobsWithDuplication = false + } + }, + setTechFrequency(name, frequency) { + for (let i = 0, len = tech.tech.length; i < len; i++) { + if (tech.tech[i].name === name) tech.tech[i].frequency = frequency + } + }, + setBotTechFrequency(f = 0) { + for (let i = 0, len = tech.tech.length; i < len; i++) { + if (tech.tech[i].isBotTech) { + switch (tech.tech[i].name) { + case "dynamo-bot": + tech.tech[i].frequency = f + break; + case "orbital-bot": + tech.tech[i].frequency = f + break; + case "laser-bot": + tech.tech[i].frequency = f + break; + case "boom-bot": + tech.tech[i].frequency = f + break; + case "foam-bot": + tech.tech[i].frequency = f + break; + case "nail-bot": + tech.tech[i].frequency = f + break; + } + } + } + }, + tech: [{ + name: "integrated armament", + link: `integrated armament`, + description: `increase damage by 19.95%
your inventory can only hold 1 gun`, + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return b.inventory.length === 1 //&& !tech.haveGunCheck("CPT gun") + }, + requires: "only 1 gun", + effect() { + tech.isOneGun = true; for (let i = 0; i < tech.tech.length; i++) { - if (tech.tech[i].count < tech.tech[i].maxCount && tech.tech[i].isJunk) options.push(i); - } - //add random array options to tech pool - if (options.length) { - const num = chance * count //scale number added - for (let i = 0; i < num; i++) tech.tech[options[Math.floor(Math.random() * options.length)]].frequency++ - simulation.makeTextLog(`tech.tech.push(${num} JUNK)`) - return num - } else { - return 0 + if (tech.tech[i].name === "CPT gun") tech.tech[i].description = `adds the CPT gun to your inventory
it rewinds your health, velocity, and position
replaces your current gun
` } }, - removeJunkTechFromPool(num = 1) { - for (let j = 0; j < num; j++) { - for (let i = 0; i < tech.tech.length; i++) { - if (tech.tech[i].isJunk && tech.tech[i].frequency > 0 && tech.tech[i].count < tech.tech[i].maxCount) { - tech.tech[i].frequency-- - break - } - } + remove() { + tech.isOneGun = false; + for (let i = 0; i < tech.tech.length; i++) { + if (tech.tech[i].name === "CPT gun") tech.tech[i].description = `adds the CPT gun to your inventory
it rewinds your health, velocity, and position` } + } + }, + { + name: "entanglement", + nameInfo: "", + addNameInfo() { + setTimeout(function () { + simulation.boldActiveGunHUD(); + }, 1000); }, - giveTech(index = 'random') { - if (index === 'random') { - let options = []; - for (let i = 0; i < tech.tech.length; i++) { - if (tech.tech[i].count < tech.tech[i].maxCount && tech.tech[i].allowed() && !tech.tech[i].isJunk && !tech.tech[i].isLore && !tech.tech[i].isBadRandomOption) options.push(i); - } - // give a random tech from the tech I don't have - if (options.length > 0) { - let newTech = options[Math.floor(Math.random() * options.length)] - tech.giveTech(newTech) - simulation.makeTextLog(`tech.giveTech("${tech.tech[newTech].name}") //random tech`); - } - } else { - if (isNaN(index)) { //find index by name - let found = false; - for (let i = 0; i < tech.tech.length; i++) { - if (index === tech.tech[i].name) { - index = i; - found = true; - break; - } - } - if (!found) return //if name not found don't give any tech - } - if (tech.isMetaAnalysis && tech.tech[index].isJunk) { - simulation.makeTextLog(`//tech: meta-analysis replaced junk tech with random tech`); - tech.giveTech('random') - for (let i = 0; i < 3; i++) powerUps.spawn(m.pos.x + 40 * Math.random(), m.pos.y + 40 * Math.random(), "research"); - return - } + description: "while your first gun is equipped
reduce harm by 13% for each of your guns", + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return b.inventory.length > 1 && !tech.isEnergyHealth + }, + requires: "at least 2 guns, not mass-energy", + effect() { + tech.isEntanglement = true + setTimeout(function () { + simulation.boldActiveGunHUD(); + }, 1000); - if (tech.tech[index].isLost) tech.tech[index].isLost = false; //give specific tech - tech.tech[index].effect(); //give specific tech - tech.tech[index].count++ - tech.totalCount++ //used in power up randomization - simulation.updateTechHUD(); - } }, - // setTechoNonRefundable(name) { - // for (let i = 0; i < tech.tech.length; i++) { - // if (tech.tech.name === name) { - // tech.tech[i].isNonRefundable = true; - // return - // } - // } + remove() { + tech.isEntanglement = false; + } + }, + { + name: "arsenal", + // descriptionFunction() { + // return `increase damage by ${14 * b.inventory.length}%
14% for each gun in your inventory` // }, - setCheating() { - if (!simulation.isCheating) { - simulation.isCheating = true; - level.levelAnnounce(); - lore.techCount = 0; + description: "increase damage by 14%
for each gun in your inventory", + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return b.inventory.length > 2 + }, + requires: "at least 3 guns", + effect() { + tech.isDamageForGuns = true; + }, + remove() { + tech.isDamageForGuns = false; + } + }, + { + name: "active cooling", + description: "18% decreased delay after firing
for each gun in your inventory", + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return b.inventory.length > 1 + }, + requires: "at least 2 guns", + effect() { + tech.isFireRateForGuns = true; + b.setFireCD(); + }, + remove() { + tech.isFireRateForGuns = false; + b.setFireCD(); + } + }, + { + name: "generalist", + description: "spawn 8 guns, but you can't switch guns
guns cycle automatically with each new level", + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return (tech.isDamageForGuns || tech.isFireRateForGuns) && b.inventory.length < b.guns.length - 5 //12-5 guns total + }, + requires: "arsenal or active cooling and less than 7 guns", + effect() { + tech.isGunCycle = true; + for (let i = 0; i < 8; i++) powerUps.spawn(m.pos.x + 10 * Math.random(), m.pos.y + 10 * Math.random(), "gun"); + }, + remove() { + if (tech.isGunCycle) { + for (let i = 0; i < 8; i++) { + if (b.inventory.length) b.removeGun(b.guns[b.inventory[b.inventory.length - 1]].name) //remove your last gun + } + tech.isGunCycle = false; + } + } + }, + { + name: "gun sciences", + description: "spawn a gun and double the frequency
of finding tech for your guns", + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + isNonRefundable: true, + // isExperimentHide: true, + isBadRandomOption: true, + allowed() { + return !tech.isSuperDeterminism + }, + requires: "NOT EXPERIMENT MODE, not superdeterminism", + effect() { + powerUps.spawn(m.pos.x, m.pos.y, "gun"); + // this.count-- + for (let i = 0, len = tech.tech.length; i < len; i++) { + if (tech.tech[i].isGunTech) tech.tech[i].frequency *= 2 + } + }, + remove() { } + }, + { + name: "ad hoc", + description: `for every gun in your inventory spawn a
${powerUps.orb.heal()}, ${powerUps.orb.research(1)}, field, ${powerUps.orb.ammo(1)}, or tech`, + maxCount: 1, //random power up + count: 0, + frequency: 1, + frequencyDefault: 1, + isNonRefundable: true, + allowed() { + return b.inventory.length > 1 + }, + requires: "NOT EXPERIMENT MODE, at least 2 guns", + effect() { + for (let i = 0; i < b.inventory.length; i++) { + if (Math.random() < 0.2) { + powerUps.spawn(m.pos.x + 10 * Math.random(), m.pos.y + 10 * Math.random(), "tech"); + } else if (Math.random() < 0.25) { + powerUps.spawn(m.pos.x + 10 * Math.random(), m.pos.y + 10 * Math.random(), "field"); + } else if (Math.random() < 0.33) { + powerUps.spawn(m.pos.x + 10 * Math.random(), m.pos.y + 10 * Math.random(), "heal"); + } else if (Math.random() < 0.5) { + powerUps.spawn(m.pos.x + 10 * Math.random(), m.pos.y + 10 * Math.random(), "ammo"); + } else { + powerUps.spawn(m.pos.x + 10 * Math.random(), m.pos.y + 10 * Math.random(), "research"); + } + } + }, + remove() { } + }, + { + name: "applied science", + description: `get a random tech
for each gun in your inventory`, //spawn ${powerUps.orb.research(1)} and + maxCount: 9, + count: 0, + isNonRefundable: true, + frequency: 2, + frequencyDefault: 2, + allowed() { + return b.inventory.length > 1 + }, + requires: "NOT EXPERIMENT MODE, at least 2 guns", + effect() { + for (let i = b.inventory.length - 1; i > -1; i--) { + //spawn a research for each gun + // powerUps.spawn(m.pos.x + 40 * (Math.random() - 0.5), m.pos.y + 40 * (Math.random() - 0.5), "research", false); + //find a gun tech for this gun + const gunTechPool = [] + for (let j = 0, len = tech.tech.length; j < len; j++) { + // console.log(j, tech.tech[j].isGunTech, tech.tech[j].allowed(), !tech.tech[j].isJunk, !tech.tech[j].isBadRandomOption, tech.tech[j].count < tech.tech[j].maxCount) + //set current gun to active so allowed works + const originalActiveGunIndex = b.activeGun + b.activeGun = b.inventory[i] //to make the .allowed work for guns that aren't active + if (tech.tech[j].isGunTech && tech.tech[j].allowed() && !tech.tech[j].isJunk && !tech.tech[j].isBadRandomOption && tech.tech[j].count < tech.tech[j].maxCount) { + const regex = tech.tech[j].requires.search(b.guns[b.inventory[i]].name) //get string index of gun name + const not = tech.tech[j].requires.search(' not ') //get string index of ' not ' + //look for the gun name in the requirements, but the gun name needs to show up before the word ' not ' + if (regex !== -1 && (not === -1 || not > regex)) { + gunTechPool.push(j) + } + } + b.activeGun = originalActiveGunIndex + } + if (gunTechPool.length) { + const index = Math.floor(Math.random() * gunTechPool.length) + tech.giveTech(gunTechPool[index]) // choose from the gun pool + simulation.makeTextLog(`tech.giveTech("${tech.tech[gunTechPool[index]].name}")`) + } + } + simulation.boldActiveGunHUD(); + }, + remove() { } + }, + { + name: "logistics", + description: `${powerUps.orb.ammo()} give 80% more ammo, but
it's only added to your current gun`, + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return !tech.isEnergyNoAmmo + }, + requires: "not exciton", + effect() { + tech.isAmmoForGun = true; + }, + remove() { + tech.isAmmoForGun = false; + } + }, + { + name: "supply chain", + junk: 0.05, + descriptionFunction() { return `double your current ammo for all guns
+${this.junk * 100}% JUNK to the potential tech pool` }, + maxCount: 9, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { return true }, + requires: "", + effect() { + for (let i = 0; i < b.guns.length; i++) { + if (b.guns[i].have) b.guns[i].ammo = Math.floor(2 * b.guns[i].ammo) + } + simulation.makeGunHUD(); + this.refundAmount += tech.addJunkTechToPool(this.junk) + }, + refundAmount: 0, + remove() { + for (let j = 0; j < this.count; j++) { + for (let i = 0; i < b.guns.length; i++) { + if (b.guns[i].have) b.guns[i].ammo = Math.floor(0.5 * b.guns[i].ammo) + } + } + simulation.makeGunHUD(); + if (this.count > 0 && this.refundAmount > 0) { + tech.removeJunkTechFromPool(this.refundAmount) + this.refundAmount = 0 + } + } + }, + { + name: "cache", + link: `cache`, + description: `${powerUps.orb.ammo()} give 13x more ammo, but
you can't store any more ammo than that`, + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return !tech.isEnergyNoAmmo + }, + requires: "not exciton", + effect() { + tech.ammoCap = 13; + powerUps.ammo.effect() + }, + remove() { + tech.ammoCap = 0; + } + }, + { + name: "catabolism", + description: `firing while out of ammo spawns ${powerUps.orb.ammo(4)}
but it reduces your maximum health by 1`, + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return !tech.isEnergyNoAmmo && !tech.isEnergyHealth + }, + requires: "not exciton, mass-energy", + effect: () => { + tech.isAmmoFromHealth = true; + }, + remove() { + tech.isAmmoFromHealth = false; + } + }, + { + name: "desublimated ammunition", + link: `desublimated ammunition`, + description: `produce bullets from air molecules
every other crouched shot uses no ammo`, + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return build.isExperimentSelection + }, + requires: "", + effect() { + tech.isCrouchAmmo = true + }, + remove() { + tech.isCrouchAmmo = false; + } + }, + { + name: "gun turret", + description: "reduce harm by 60% when crouching", + maxCount: 1, + count: 0, + frequency: 3, + frequencyDefault: 3, + allowed() { + return (tech.isCrouchAmmo && !tech.isEnergyHealth) || tech.isCrouchRegen + }, + requires: "inductive coupling, desublimated ammunition, not mass-energy", + effect() { + tech.isTurret = true + }, + remove() { + tech.isTurret = false; + } + }, + { + name: "dead reckoning", + description: "increase damage by 36% when at rest", + maxCount: 9, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { return true }, + requires: "", + effect: () => { + tech.restDamage += 0.36 + }, + remove() { + tech.restDamage = 1; + } + }, + { + name: "Higgs mechanism", + description: "while firing your position is locked
50% decreased delay after firing", + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return !m.isShipMode && !tech.isAlwaysFire + }, + requires: "not ship mode, not automatic", + effect: () => { + tech.isFireMoveLock = true; + b.setFireCD(); + b.setFireMethod(); + }, + remove() { + if (tech.isFireMoveLock) { + tech.isFireMoveLock = false + b.setFireCD(); + b.setFireMethod(); + } + } + }, + { + name: "squirrel-cage rotor", + description: "move and jump 30% faster
take 5% more harm", + maxCount: 9, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { return true }, + requires: "", + effect() { // good with melee builds, content skipping builds + tech.squirrelFx += 0.25; + tech.squirrelJump += 0.1; + m.setMovement() + }, + remove() { + tech.squirrelFx = 1; + tech.squirrelJump = 1; + m.setMovement() + } + }, + { + name: "Newton's 1st law", + description: "moving at high speeds
reduces harm by up to 66%", + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return !tech.isEnergyHealth + }, + requires: "not mass-energy equivalence", + effect() { + tech.isSpeedHarm = true //max at speed = 40 + }, + remove() { + tech.isSpeedHarm = false + } + }, + { + name: "Newton's 2nd law", + description: "moving at high speeds
increases damage by up to 66%", + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { return true }, + requires: "", + effect() { + tech.isSpeedDamage = true //max at speed = 40 + }, + remove() { + tech.isSpeedDamage = false + } + }, + { + name: "kinetic bombardment", + description: "increase damage by up to 33% at a distance
of up to 50 player widths from the target", + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { return true }, + requires: "", + effect() { + tech.isFarAwayDmg = true; //used in mob.damage() + }, + remove() { + tech.isFarAwayDmg = false; + } + }, + { + name: "regression", + description: "bullet collisions increase vulnerability to
damage by 5% for mobs (0.5% for bosses)", + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { return true }, + requires: "", + effect() { + tech.isLessDamageReduction = true + }, + remove() { + tech.isLessDamageReduction = false + } + }, + + { + name: "simulated annealing", + description: "increase damage by 20%
20% increased delay after firing", + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { return true }, + effect() { + tech.slowFire = 1.2 + b.setFireCD(); + }, + remove() { + tech.slowFire = 1; + b.setFireCD(); + } + }, + { + name: "heuristics", + description: "30% decreased delay after firing", + maxCount: 9, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { return true }, + requires: "", + effect() { + tech.fireRate *= 0.7 + b.setFireCD(); + }, + remove() { + tech.fireRate = 1; + b.setFireCD(); + } + }, + + { + name: "fracture analysis", + description: "bullet impacts do 400% damage
to stunned mobs", + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.isStunField || tech.oneSuperBall || tech.isCloakStun || tech.orbitBotCount > 1 || tech.isExplosionStun || tech.isMineStun + }, + requires: "a stun effect", + effect() { + tech.isCrit = true; + }, + remove() { + tech.isCrit = false; + } + }, + { + name: "microstates", + link: `microstates`, + description: "increase damage by 7%
for every 10 active projectiles", + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { return true }, + requires: "", + effect() { + tech.isDamageFromBulletCount = true + }, + remove() { + tech.isDamageFromBulletCount = false + } + }, + { + name: "anti-shear topology", + link: `anti-shear topology`, + description: "some projectiles last 30% longer
drone, spore, missile, foam, wave, neutron, ice", + + // isGunTech: true, + maxCount: 3, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return m.fieldUpgrades[m.fieldMode].name === "molecular assembler" || tech.haveGunCheck("spores") || tech.haveGunCheck("drones") || tech.haveGunCheck("missiles") || tech.haveGunCheck("foam") || tech.haveGunCheck("matter wave") || tech.isNeutronBomb || tech.isIceField || tech.isIceShot || tech.relayIce || tech.isNeedleIce || tech.blockingIce > 1 || tech.isWormShot || tech.foamBotCount > 1 + }, + requires: "drones, spores, missiles, foam, matter wave, neutron bomb, ice IX", + effect() { + tech.isBulletsLastLonger += 0.3 + }, + remove() { + tech.isBulletsLastLonger = 1; + } + }, + { + name: "radioactive contamination", + description: "after a mob or shield dies,
leftover radiation spreads to a nearby mob", + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.isNailRadiation || tech.isWormholeDamage || tech.isNeutronBomb || tech.isExplodeRadio + }, + requires: "radiation damage source", + effect() { + tech.isRadioactive = true + }, + remove() { + tech.isRadioactive = false + } + }, + { + name: "water shielding", + link: `water shielding`, + description: "radioactive effects on you are reduced by 75%
neutron bomb, drones, explosions, slime", + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.isNeutronBomb || tech.isDroneRadioactive || tech.isExplodeRadio + }, + requires: "neutron bomb or irradiated drones or iridium-192", + effect() { + tech.isRadioactiveResistance = true + }, + remove() { + tech.isRadioactiveResistance = false + } + }, + { + name: "iridium-192", + description: "explosions release gamma radiation
100% more damage, but over 4 seconds", + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.explosiveRadius === 1 && !tech.isSmallExplosion && (tech.haveGunCheck("missiles") || tech.isIncendiary || (tech.haveGunCheck("grenades") && !tech.isNeutronBomb) || tech.isPulseLaser || tech.isMissileField || tech.boomBotCount > 1 || tech.isTokamak) + }, + requires: "an explosive damage source, not ammonium nitrate or nitroglycerin", + effect: () => { + tech.isExplodeRadio = true; //iridium-192 + }, + remove() { + tech.isExplodeRadio = false; + } + }, + { + name: "ammonium nitrate", + description: "increase explosive damage by 30%
increase explosive radius by 30%", + maxCount: 9, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return !tech.isExplodeRadio && tech.hasExplosiveDamageCheck() + }, + requires: "an explosive damage source, not iridium-192", + effect: () => { + tech.explosiveRadius += 0.3; + }, + remove() { + tech.explosiveRadius = 1; + } + }, + { + name: "nitroglycerin", + description: "increase explosive damage by 66%
decrease explosive radius by 33%", + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return !tech.isExplodeRadio && tech.hasExplosiveDamageCheck() + }, + requires: "an explosive damage source, not iridium-192", + effect: () => { + tech.isSmallExplosion = true; + }, + remove() { + tech.isSmallExplosion = false; + } + }, + { + name: "acetone peroxide", + description: "increase explosive radius by 80%, but
you take 300% more harm from explosions", + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + isBadRandomOption: true, + allowed() { + return tech.hasExplosiveDamageCheck() + }, + requires: "an explosive damage source", + effect: () => { + tech.isExplosionHarm = true; + }, + remove() { + tech.isExplosionHarm = false; + } + }, + { + name: "shock wave", + description: "explosions stun mobs for 1-2 seconds
decrease explosive damage by 30%", + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return !tech.isExplodeRadio && tech.hasExplosiveDamageCheck() + }, + requires: "an explosive damage source, not iridium-192", + effect() { + tech.isExplosionStun = true; + }, + remove() { + tech.isExplosionStun = false; + } + }, + { + name: "controlled explosion", + description: `use ${powerUps.orb.research(3)} to dynamically reduce all
explosions until they do no harm`, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return !tech.isImmuneExplosion && (build.isExperimentSelection || powerUps.research.count > 2) && (tech.haveGunCheck("missiles") || tech.isMissileField || tech.missileBotCount > 0 || tech.isIncendiary || tech.isPulseLaser || tech.isTokamak || (tech.haveGunCheck("grenades") && !tech.isNeutronBomb)) + }, + requires: "an explosive damage source, not electric reactive armor", + effect: () => { + tech.isSmartRadius = true; + for (let i = 0; i < 3; i++) { + if (powerUps.research.count > 0) powerUps.research.changeRerolls(-1) + } + }, + remove() { + tech.isSmartRadius = false; + if (this.count > 0) powerUps.research.changeRerolls(3) + } + }, + { + name: "electric reactive armor", + // description: "explosions do no harm
while your energy is above 98%", + description: "harm from explosions is passively reduced
by 5% for every 10 stored energy", + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return !tech.isSmartRadius && !tech.isExplodeRadio && tech.hasExplosiveDamageCheck() + }, + requires: "an explosive damage source, not iridium-192", + effect: () => { + tech.isImmuneExplosion = true; + }, + remove() { + tech.isImmuneExplosion = false; + } + }, + { + name: "incendiary ammunition", + description: "shotgun, super balls, and drones
are loaded with explosives", + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return ((m.fieldUpgrades[m.fieldMode].name === "molecular assembler" && !(tech.isDroneTeleport || tech.isDroneRadioactive || tech.isSporeField || tech.isMissileField || tech.isIceField)) || (tech.haveGunCheck("drones") && !tech.isForeverDrones && !tech.isDroneRadioactive && !tech.isDroneTeleport) || tech.haveGunCheck("super balls") || tech.haveGunCheck("shotgun")) && !tech.isNailShot && !tech.isIceShot && !tech.isFoamShot && !tech.isWormShot && !tech.isNeedleShot + }, + requires: "super balls, basic or slug shotgun, drones, not irradiated drones or burst drones", + effect() { + tech.isIncendiary = true + }, + remove() { + tech.isIncendiary = false; + } + }, + { + name: "fragmentation", + description: "some detonations and collisions eject nails
blocks, grenades, missiles, slugs, harpoon", + maxCount: 9, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return tech.haveGunCheck("harpoon") || (tech.haveGunCheck("grenades") && !tech.isNeutronBomb) || tech.haveGunCheck("missiles") || tech.missileBotCount || (tech.haveGunCheck("shotgun") && tech.isSlugShot) || tech.blockDamage > 0.075 + }, + requires: "grenades, missiles, shotgun slugs, harpoon, or mass driver", + effect() { + tech.fragments++ + }, + remove() { + tech.fragments = 0 + } + }, + { + name: "thermal runaway", + description: "mobs explode when they die", + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return !tech.sporesOnDeath && !tech.nailsDeathMob && !tech.botSpawner && !tech.isMobBlockFling && !tech.iceIXOnDeath + }, + requires: "no other mob death tech", + effect: () => { + tech.isExplodeMob = true; + }, + remove() { + tech.isExplodeMob = false; + } + }, + { + name: "shear stress", + description: "mobs release a nail when they die
nails target nearby mobs", + maxCount: 9, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return !tech.sporesOnDeath && !tech.isExplodeMob && !tech.botSpawner && !tech.isMobBlockFling && !tech.iceIXOnDeath + }, + requires: "no other mob death tech", + effect: () => { + tech.nailsDeathMob++ + }, + remove() { + tech.nailsDeathMob = 0; + } + }, + { + name: "zoospore vector", + link: `zoospore vector`, + description: "mobs produce spores when they die
11% chance", + maxCount: 9, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return !tech.nailsDeathMob && !tech.isExplodeMob && !tech.botSpawner && !tech.isMobBlockFling && !tech.iceIXOnDeath + }, + requires: "no other mob death tech", + effect() { + tech.sporesOnDeath += 0.11; + // if (tech.isSporeWorm) { + // for (let i = 0; i < 4; i++) b.worm(m.pos) + // } else { + // for (let i = 0; i < 8; i++) b.spore(m.pos) + // } + }, + remove() { + tech.sporesOnDeath = 0; + } + }, + { + name: "reaction inhibitor", + description: "mobs spawn with 11% less health", + maxCount: 3, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return tech.nailsDeathMob || tech.sporesOnDeath || tech.isExplodeMob || tech.botSpawner || tech.isMobBlockFling || tech.iceIXOnDeath + }, + requires: "any mob death tech", + effect: () => { + tech.mobSpawnWithHealth *= 0.89 + + //set all mobs at full health to 0.85 + for (let i = 0; i < mob.length; i++) { + if (mob.health > tech.mobSpawnWithHealth) mob.health = tech.mobSpawnWithHealth + } + }, + remove() { + tech.mobSpawnWithHealth = 1; + } + }, + { + name: "decorrelation", + description: "reduce harm by 70% after not activating
your gun or field for 2 seconds", + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return !tech.isEnergyHealth //((m.fieldUpgrades[m.fieldMode].name === "standing wave" && (tech.blockingIce !== 0 || tech.blockDmg !== 0)) || b.totalBots() > 1 || tech.haveGunCheck("mine") || tech.haveGunCheck("spores") || m.fieldUpgrades[m.fieldMode].name === "molecular assembler") && + }, + requires: "not mass-energy", + effect() { + tech.isNoFireDefense = true + }, + remove() { + tech.isNoFireDefense = false + } + }, + { + name: "anticorrelation", + description: "increase damage by 100%
after not using your gun or field for 2 seconds", + maxCount: 1, + count: 0, + frequency: 3, + frequencyDefault: 3, + allowed() { + return tech.isNoFireDefense + }, + requires: "decorrelation", + effect() { + tech.isNoFireDamage = true + }, + remove() { + tech.isNoFireDamage = false + } + }, + { + name: "scrap bots", + link: `scrap bots`, + description: "33% chance after killing a mob to build
a scrap bot that operates for 14 seconds", + maxCount: 3, + count: 0, + frequency: 1, + frequencyDefault: 1, + isBotTech: true, + allowed() { + return !tech.sporesOnDeath && !tech.nailsDeathMob && !tech.isExplodeMob && !tech.isMobBlockFling && !tech.iceIXOnDeath + }, + requires: "no other mob death tech", + effect() { + tech.botSpawner += 0.33; + }, + remove() { + tech.botSpawner = 0; + } + }, + { + name: "scrap refit", + link: `scrap refit`, + description: "killing a mob resets your functional scrap bots
to 14 seconds of operation", + maxCount: 1, + count: 0, + frequency: 3, + frequencyDefault: 3, + isBotTech: true, + allowed() { + return tech.botSpawner + }, + requires: "scrap bots", + effect() { + tech.isBotSpawnerReset = true; + }, + remove() { + tech.isBotSpawnerReset = false; + } + }, + { + name: "nail-bot", + link: `nail-bot`, + description: "a bot fires nails at mobs in line of sight", + maxCount: 9, + count: 0, + frequency: 1, + frequencyDefault: 1, + isBot: true, + isBotTech: true, + allowed() { return true }, + requires: "", + effect() { + tech.nailBotCount++; + b.nailBot(); + }, + remove() { + if (this.count) { + tech.nailBotCount -= this.count; + b.clearPermanentBots(); + b.respawnBots(); + } + } + }, + { + name: "nail-bot upgrade", + link: `nail-bot upgrade`, + description: "convert your current bots to nail-bots
+500% fire rate and +40% nail velocity", + maxCount: 1, + count: 0, + frequency: 3, + frequencyDefault: 3, + isBotTech: true, + allowed() { + return tech.nailBotCount > 1 && !b.hasBotUpgrade() + }, + requires: "2 or more nail bots and no other bot upgrade", + effect() { + tech.isNailBotUpgrade = true + b.convertBotsTo("nail-bot") + for (let i = 0; i < bullet.length; i++) { + if (bullet[i].botType === 'nail') bullet[i].isUpgraded = true + } + tech.setBotTechFrequency() + tech.setTechFrequency("nail-bot", 5) + }, + remove() { + if (this.count) { + for (let i = 0; i < bullet.length; i++) { + if (bullet[i].botType === 'nail') bullet[i].isUpgraded = false + } + tech.setBotTechFrequency(1) + } + tech.isNailBotUpgrade = false + } + }, + { + name: "foam-bot", + link: `foam-bot`, + description: "a bot fires foam at nearby mobs", + maxCount: 9, + count: 0, + frequency: 1, + frequencyDefault: 1, + isBot: true, + isBotTech: true, + allowed() { return true }, + requires: "", + effect() { + tech.foamBotCount++; + b.foamBot(); + }, + remove() { + if (this.count) { + tech.foamBotCount -= this.count; + b.clearPermanentBots(); + b.respawnBots(); + } + } + }, + { + name: "foam-bot upgrade", + link: `foam-bot upgrade`, + description: "convert your current bots to foam-bots
300% increased foam size and fire rate", + maxCount: 1, + count: 0, + frequency: 3, + frequencyDefault: 3, + isBotTech: true, + allowed() { + return tech.foamBotCount > 1 && !b.hasBotUpgrade() + }, + requires: "2 or more foam bots and no other bot upgrade", + effect() { + tech.isFoamBotUpgrade = true + b.convertBotsTo("foam-bot") + for (let i = 0; i < bullet.length; i++) { + if (bullet[i].botType === 'foam') bullet[i].isUpgraded = true + } + tech.setBotTechFrequency() + tech.setTechFrequency("foam-bot", 5) + }, + remove() { + if (this.count) { + for (let i = 0; i < bullet.length; i++) { + if (bullet[i].botType === 'foam') bullet[i].isUpgraded = false + } + tech.setBotTechFrequency(1) + } + tech.isFoamBotUpgrade = false + } + }, + { + name: "boom-bot", + link: `boom-bot`, + description: "a bot defends the space around you
ignites an explosion after hitting a mob", + maxCount: 9, + count: 0, + frequency: 1, + frequencyDefault: 1, + isBot: true, + isBotTech: true, + allowed() { return true }, + requires: "", + effect() { + tech.boomBotCount++; + b.boomBot(); + }, + remove() { + if (this.count) { + tech.boomBotCount -= this.count; + b.clearPermanentBots(); + b.respawnBots(); + } + } + }, + { + name: "boom-bot upgrade", + link: `boom-bot upgrade`, + description: "convert your current bots to boom-bots
300% increased explosion damage and size", + maxCount: 1, + count: 0, + frequency: 3, + frequencyDefault: 3, + isBotTech: true, + allowed() { + return tech.boomBotCount > 1 && !b.hasBotUpgrade() + }, + requires: "2 or more boom bots and no other bot upgrade", + effect() { + tech.isBoomBotUpgrade = true + b.convertBotsTo("boom-bot") + for (let i = 0; i < bullet.length; i++) { + if (bullet[i].botType === 'boom') bullet[i].isUpgraded = true + } + tech.setBotTechFrequency() + tech.setTechFrequency("boom-bot", 5) + }, + remove() { + if (this.count) { + for (let i = 0; i < bullet.length; i++) { + if (bullet[i].botType === 'boom') bullet[i].isUpgraded = false + } + tech.setBotTechFrequency(1) + } + tech.isBoomBotUpgrade = false + } + }, + { + name: "laser-bot", + link: `laser-bot`, + description: "a bot uses energy to emit a laser beam
that targets nearby mobs", + maxCount: 9, + count: 0, + frequency: 1, + frequencyDefault: 1, + isBot: true, + isBotTech: true, + allowed() { + return m.maxEnergy > 0.5 + }, + requires: "maximum energy above 50", + effect() { + tech.laserBotCount++; + b.laserBot(); + }, + remove() { + if (this.count) { + tech.laserBotCount -= this.count; + b.clearPermanentBots(); + b.respawnBots(); + } + } + }, + { + name: "laser-bot upgrade", + link: `laser-bot upgrade`, + description: "convert your current bots to laser-bots
100% improved damage, efficiency, and range", // 400% increased laser-bot laser damage", + maxCount: 1, + count: 0, + frequency: 3, + frequencyDefault: 3, + isBotTech: true, + allowed() { + return tech.laserBotCount > 1 && !b.hasBotUpgrade() + }, + requires: "2 or more laser bots and no other bot upgrade", + effect() { + tech.isLaserBotUpgrade = true + b.convertBotsTo("laser-bot") + for (let i = 0; i < bullet.length; i++) { + if (bullet[i].botType === 'laser') bullet[i].isUpgraded = true + } + tech.setBotTechFrequency() + tech.setTechFrequency("laser-bot", 5) + }, + remove() { + if (this.count) { + for (let i = 0; i < bullet.length; i++) { + if (bullet[i].botType === 'laser') bullet[i].isUpgraded = false + } + tech.setBotTechFrequency(1) + } + tech.isLaserBotUpgrade = false + } + }, + { + name: "orbital-bot", + link: `orbital-bot`, + description: "a bot is locked in orbit around you
stuns and damages mobs on contact", + maxCount: 9, + count: 0, + frequency: 1, + frequencyDefault: 1, + isBot: true, + isBotTech: true, + allowed() { return true }, + requires: "", + effect() { + b.orbitBot(); + tech.orbitBotCount++; + }, + remove() { + if (this.count) { + tech.orbitBotCount -= this.count; + b.clearPermanentBots(); + b.respawnBots(); + } + } + }, + { + name: "orbital-bot upgrade", + link: `orbital-bot upgrade`, + description: "convert your current bots to orbital-bots
increase damage by 300% and radius by 50%", + maxCount: 1, + count: 0, + frequency: 3, + frequencyDefault: 3, + isBotTech: true, + allowed() { + return tech.orbitBotCount > 1 && !b.hasBotUpgrade() + }, + requires: "2 or more orbital bots and no other bot upgrade", + effect() { + tech.isOrbitBotUpgrade = true + b.convertBotsTo("orbital-bot") + const range = 190 + 120 * tech.isOrbitBotUpgrade + for (let i = 0; i < bullet.length; i++) { + if (bullet[i].botType === 'orbit') { + bullet[i].isUpgraded = true + bullet[i].range = range + bullet[i].orbitalSpeed = Math.sqrt(0.25 / range) + } + } + tech.setBotTechFrequency() + tech.setTechFrequency("orbital-bot", 5) + }, + remove() { + if (this.count) { + const range = 190 + 100 * tech.isOrbitBotUpgrade + for (let i = 0; i < bullet.length; i++) { + if (bullet[i].botType === 'orbit') { + bullet[i].range = range + bullet[i].orbitalSpeed = Math.sqrt(0.25 / range) + } + } + tech.setBotTechFrequency(1) + } + tech.isOrbitBotUpgrade = false + } + }, + { + name: "dynamo-bot", + link: `dynamo-bot`, + description: "a bot damages mobs while it traces your path
regen 6 energy per second when it's near", + maxCount: 9, + count: 0, + frequency: 1, + frequencyDefault: 1, + isBot: true, + isBotTech: true, + allowed() { return true }, + requires: "", + effect() { + tech.dynamoBotCount++; + b.dynamoBot(); + }, + remove() { + if (this.count) { + tech.dynamoBotCount -= this.count; + b.clearPermanentBots(); + b.respawnBots(); + } + } + }, + { + name: "dynamo-bot upgrade", + link: `dynamo-bot upgrade`, + description: "convert your current bots to dynamo-bots
increase regen to 20 energy per second", + maxCount: 1, + count: 0, + frequency: 3, + frequencyDefault: 3, + isBotTech: true, + allowed() { + return tech.dynamoBotCount > 1 && !b.hasBotUpgrade() + }, + requires: "2 or more dynamo bots and no other bot upgrade", + effect() { + tech.isDynamoBotUpgrade = true + b.convertBotsTo("dynamo-bot") + for (let i = 0; i < bullet.length; i++) { + if (bullet[i].botType === 'dynamo') bullet[i].isUpgraded = true + } + tech.setBotTechFrequency() + tech.setTechFrequency("dynamo-bot", 5) + }, + remove() { + if (this.count) { + for (let i = 0; i < bullet.length; i++) { + if (bullet[i].botType === 'dynamo') bullet[i].isUpgraded = false + } + tech.setBotTechFrequency(1) + } + tech.isDynamoBotUpgrade = false + } + }, + { + name: "bot fabrication", + link: `bot fabrication`, + descriptionFunction() { + return `after you collect ${powerUps.orb.research(2 + Math.floor(0.1666 * b.totalBots()))}use them to build a
random bot (+1 cost every 6 bots)` + }, + // description: `if you collect ${powerUps.orb.research(2)}use them to build a
random bot (+1 cost every 5 bots)`, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + isBotTech: true, + allowed() { + return powerUps.research.count > 1 || build.isExperimentSelection + }, + requires: "at least 2 research", + effect() { + tech.isRerollBots = true; + powerUps.research.changeRerolls(0) + simulation.makeTextLog(`m.research = 0`) + }, + remove() { + tech.isRerollBots = false; + // this.description = `if you collect ${powerUps.orb.research(2 + Math.floor(0.2 * b.totalBots()))}use them to build a
random bot (+1 cost every 5 bots)` + } + }, + { + name: "robotics", + description: `spawn 2 random bots
quadruple the frequency of finding bot tech`, + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + isBotTech: true, + allowed() { + return b.totalBots() > 1 || build.isExperimentSelection + }, + requires: "at least 2 bots", + effect: () => { + b.randomBot() + b.randomBot() + for (let i = 0, len = tech.tech.length; i < len; i++) { + if (tech.tech[i].isBotTech) tech.tech[i].frequency *= 4 + } + }, + remove() { + if (this.count > 0) { + b.removeBot() + b.removeBot() + b.clearPermanentBots(); + b.respawnBots(); for (let i = 0, len = tech.tech.length; i < len; i++) { - if (tech.tech[i].isLore) { - tech.tech[i].frequency = 0; - tech.tech[i].count = 0; + if (tech.tech[i].isBotTech) tech.tech[i].frequency = Math.ceil(tech.tech[i].frequency / 4) + } + } + } + }, + { + name: "perimeter defense", + description: "reduce harm by 8%
for each of your permanent bots", + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + isBotTech: true, + allowed() { + return b.totalBots() > 2 && !tech.isEnergyHealth + }, + requires: "at least 3 bots", + effect() { + tech.isBotArmor = true + }, + remove() { + tech.isBotArmor = false + } + }, + { + name: "network effect", + description: "increase damage by 7%
for each of your permanent bots", + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + isBotTech: true, + allowed() { + return b.totalBots() > 2 + }, + requires: "at least 3 bots", + effect() { + tech.isBotDamage = true + }, + remove() { + tech.isBotDamage = false + } + }, + { + name: "ersatz bots", + link: `ersatz bots`, + description: "double your current permanent bots
remove all guns in your inventory", + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + isBotTech: true, + // isNonRefundable: true, + isBadRandomOption: true, + numberOfGunsLost: 0, + allowed() { + return b.totalBots() > 3 && !build.isExperimentSelection + }, + requires: "NOT EXPERIMENT MODE, at least 4 bots", + effect() { + this.numberOfGunsLost = b.inventory.length + b.removeAllGuns(); + simulation.makeGunHUD(); + //double bots + for (let i = 0; i < tech.nailBotCount; i++) b.nailBot(); + tech.nailBotCount *= 2 + for (let i = 0; i < tech.laserBotCount; i++) b.laserBot(); + tech.laserBotCount *= 2 + for (let i = 0; i < tech.foamBotCount; i++) b.foamBot(); + tech.foamBotCount *= 2 + for (let i = 0; i < tech.boomBotCount; i++) b.boomBot(); + tech.boomBotCount *= 2 + for (let i = 0; i < tech.orbitBotCount; i++) b.orbitBot(); + tech.orbitBotCount *= 2 + for (let i = 0; i < tech.dynamoBotCount; i++) b.dynamoBot(); + tech.dynamoBotCount *= 2 + for (let i = 0; i < tech.plasmaBotCount; i++) b.plasmaBot(); + tech.plasmaBotCount *= 2 + for (let i = 0; i < tech.missileBotCount; i++) b.missileBot(); + tech.missileBotCount *= 2 + }, + remove() { + if (this.count) { + //return guns + for (let i = 0; i < this.numberOfGunsLost; i++) powerUps.spawn(m.pos.x + 60 * (Math.random() - 0.5), m.pos.y + 60 * (Math.random() - 0.5), "gun"); + this.numberOfGunsLost = 0; + + //half all current guns + tech.nailBotCount = Math.round(tech.nailBotCount / 2) + tech.laserBotCount = Math.round(tech.laserBotCount / 2) + tech.foamBotCount = Math.round(tech.foamBotCount / 2) + tech.boomBotCount = Math.round(tech.boomBotCount / 2) + tech.orbitBotCount = Math.round(tech.orbitBotCount / 2) + tech.dynamoBotCount = Math.round(tech.dynamoBotCount / 2) + tech.plasmaBotCount = Math.round(tech.plasmaBotCount / 2) + tech.missileBotCount = Math.round(tech.missileBotCount / 2) + b.clearPermanentBots(); + b.respawnBots(); + } + } + }, + { + name: "capacitor bank", + // description: "charge effects build up almost instantly
throwing blocks, foam, railgun, pulse, tokamak", + descriptionFunction() { return `charge effects build up almost instantly
throwing blocks, ${tech.haveGunCheck("foam", false) ? "foam" : "foam"}, ${tech.isRailGun ? "railgun" : "railgun"}, ${tech.isPulseLaser ? "pulse" : "pulse"}, ${tech.isTokamak ? "tokamak" : "tokamak"}` }, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.blockDamage > 0.075 || tech.haveGunCheck("foam") || tech.isRailGun || tech.isTokamak || tech.isPulseLaser + }, + requires: "throwing blocks, foam, railgun, pulse, tokamak", + effect() { + tech.isCapacitor = true; + }, + remove() { + tech.isCapacitor = false; + } + }, + { + name: "mass driver", + description: "increase block collision damage by 300%", + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return m.fieldUpgrades[m.fieldMode].name !== "wormhole" + }, + requires: "not wormhole", + effect() { + tech.blockDamage = 0.3 + }, + remove() { + tech.blockDamage = 0.075 + } + }, + { + name: "inflation", + link: `inflation`, + description: "throwing a block expands it by 300%
holding a block reduces harm by 85%", + maxCount: 1, + count: 0, + frequency: 3, + frequencyDefault: 3, + allowed() { + return tech.blockDamage > 0.075 && m.fieldUpgrades[m.fieldMode].name !== "pilot wave" && m.fieldUpgrades[m.fieldMode].name !== "wormhole" && !tech.isTokamak + }, + requires: "mass driver, not pilot wave, tokamak, wormhole", + effect() { + tech.isAddBlockMass = true + }, + remove() { + tech.isAddBlockMass = false + } + }, + { + name: "restitution", + description: "throwing a block makes it very bouncy
increase block collision damage by 150%", + maxCount: 1, + count: 0, + frequency: 3, + frequencyDefault: 3, + allowed() { + return tech.blockDamage > 0.075 && m.fieldUpgrades[m.fieldMode].name !== "pilot wave" && m.fieldUpgrades[m.fieldMode].name !== "wormhole" && !tech.isTokamak + }, + requires: "mass driver, not pilot wave not tokamak, wormhole", + effect() { + tech.isBlockRestitution = true + }, + remove() { + tech.isBlockRestitution = false + } + }, + { + name: "flywheel", + description: "after a mob dies its block is flung at mobs
increase block collision damage by 150%", + maxCount: 1, + count: 0, + frequency: 3, + frequencyDefault: 3, + allowed() { + return tech.blockDamage > 0.075 && !tech.nailsDeathMob && !tech.sporesOnDeath && !tech.isExplodeMob && !tech.botSpawner && !tech.iceIXOnDeath + }, + requires: "mass driver, no other mob death tech", + effect() { + tech.isMobBlockFling = true + }, + remove() { + tech.isMobBlockFling = false + } + }, + // { + // name: "fermions", + // description: "blocks thrown by you or pilot wave will
collide with intangible mobs, but not you", + // maxCount: 1, + // count: 0, + // frequency: 2, + // frequencyDefault: 2, + // allowed() { + // return (tech.blockDamage > 0.075 || m.fieldUpgrades[m.fieldMode].name === "pilot wave") && !tech.isTokamak + // }, + // requires: "mass driver or pilot wave, not tokamak", + // effect() { + // tech.isBlockBullets = true + // }, + // remove() { + // tech.isBlockBullets = false + // } + // }, + // { + // name: "inelastic collision", + // description: "holding a block reduces harm by 85%
increase block collision damage by 150%", + // maxCount: 1, + // count: 0, + // frequency: 3, + // frequencyDefault: 3, + // allowed() { + // return tech.blockDamage > 0.075 && m.fieldUpgrades[m.fieldMode].name !== "pilot wave" && m.fieldUpgrades[m.fieldMode].name !== "wormhole" && !tech.isEnergyHealth + // }, + // requires: "mass driver, a field that can hold things, not mass-energy", + // effect() { + // tech.isBlockHarm = true + // }, + // remove() { + // tech.isBlockHarm = false + // } + // }, + { + name: "buckling", + description: `if a block you threw kills a mob
spawn 1 ${powerUps.orb.heal()}, ${powerUps.orb.ammo()}, or ${powerUps.orb.research(1)}`, + maxCount: 1, + count: 0, + frequency: 3, + frequencyDefault: 3, + allowed() { + return tech.blockDamage > 0.075 && m.fieldUpgrades[m.fieldMode].name !== "pilot wave" && !tech.isTokamak + }, + requires: "mass driver, not pilot wave, tokamak", + effect() { + tech.isBlockPowerUps = true + }, + remove() { + tech.isBlockPowerUps = false + } + }, + { + name: "Pauli exclusion", + description: `after receiving harm from a collision become
immune to harm for 1 extra second`, + maxCount: 9, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { return true }, + requires: "", + effect() { + tech.collisionImmuneCycles += 60; + if (m.immuneCycle < m.cycle + tech.collisionImmuneCycles) m.immuneCycle = m.cycle + tech.collisionImmuneCycles; //player is immune to damage for 30 cycles + }, + remove() { + tech.collisionImmuneCycles = 30; + } + }, + { + name: "complex spin-statistics", + description: `become immune to harm for 1.5 seconds
once every 7 seconds`, + maxCount: 3, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return true //tech.collisionImmuneCycles > 30 + }, + requires: "", + effect() { + tech.cyclicImmunity += 90; + }, + remove() { + tech.cyclicImmunity = 0; + } + }, + { + name: "NOR gate", + description: "if flip-flop is in the ON state
take 0 harm from collisions with mobs", + maxCount: 1, + count: 0, + frequency: 4, + frequencyDefault: 4, + allowed() { + return tech.isFlipFlop + }, + requires: "flip-flop", + effect() { + tech.isFlipFlopHarm = true //do you have this tech + }, + remove() { + tech.isFlipFlopHarm = false + } + }, + { + name: "flip-flop", + link: `flip-flop`, + description: `toggle ON and OFF after a collision
unlock advanced tech that runs if ON`, + nameInfo: "", + addNameInfo() { + setTimeout(function () { + if (document.getElementById("tech-flip-flop")) { + if (tech.isFlipFlopOn) { + document.getElementById("tech-flip-flop").innerHTML = ` = ON` + m.eyeFillColor = m.fieldMeterColor //'#5af' + } else { + document.getElementById("tech-flip-flop").innerHTML = ` = OFF` + m.eyeFillColor = "transparent" } } - console.log('cheating') - sound.tone(250) - sound.tone(300) - sound.tone(375) + }, 100); + }, + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return !tech.isRelay + }, + requires: "not relay switch", + effect() { + tech.isFlipFlop = true //do you have this tech? + tech.isFlipFlopOn = true //what is the state of flip-Flop? + if (!m.isShipMode) { + m.draw = m.drawFlipFlop } }, - haveGunCheck(name, needActive = true) { - // if ( - // !build.isExperimentSelection && - // b.inventory.length > 2 && - // name !== b.guns[b.activeGun].name && - // 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 - // } - // return false - if (build.isExperimentSelection || !needActive) { - for (i = 0, len = b.inventory.length; i < len; i++) { - if (b.guns[b.inventory[i]].name === name) return true + remove() { + tech.isFlipFlop = false + tech.isFlipFlopOn = false + m.eyeFillColor = 'transparent' + } + }, + { + name: "relay switch", + description: `toggle ON and OFF after picking up a power up
unlock advanced tech that runs if ON`, + nameInfo: "", + addNameInfo() { + setTimeout(function () { + if (document.getElementById("tech-switch")) { + if (tech.isFlipFlopOn) { + document.getElementById("tech-switch").innerHTML = ` = ON` + m.eyeFillColor = m.fieldMeterColor //'#5af' + } else { + document.getElementById("tech-switch").innerHTML = ` = OFF` + m.eyeFillColor = "transparent" + } } - return false - } else { //must be holding gun, this is the standard while playing - return b.inventory.length > 0 && b.guns[b.activeGun].name === name + }, 100); + }, + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return !tech.isFlipFlop + }, + requires: "not flip-flop", + effect() { + tech.isRelay = true //do you have this tech? + tech.isFlipFlopOn = true //what is the state of flip-Flop? + if (!m.isShipMode) { + m.draw = m.drawFlipFlop } }, - hasExplosiveDamageCheck() { - return tech.haveGunCheck("missiles") || tech.isMissileField || tech.missileBotCount > 0 || tech.boomBotCount > 1 || tech.isIncendiary || tech.isPulseLaser || tech.isTokamak || (tech.haveGunCheck("grenades") && !tech.isNeutronBomb) + remove() { + tech.isRelay = false + tech.isFlipFlopOn = false + m.eyeFillColor = 'transparent' + } + }, + { + name: "NAND gate", + description: "if in the ON state
do 50% more damage", + maxCount: 1, + count: 0, + frequency: 4, + frequencyDefault: 4, + allowed() { + return tech.isFlipFlop || tech.isRelay }, - damageFromTech() { - let dmg = 1 //m.fieldDamage - if (tech.OccamDamage) dmg *= tech.OccamDamage - if (tech.isCloakingDamage) dmg *= 1.35 - if (tech.isFlipFlopDamage && tech.isFlipFlopOn) dmg *= 1.5 - if (tech.isAnthropicDamage && tech.isDeathAvoidedThisLevel) dmg *= 2.3703599 - if (m.isSneakAttack && m.cycle > m.lastKillCycle + 240) dmg *= tech.sneakAttackDmg - if (tech.isTechDamage) dmg *= 1.9 - if (tech.isDupDamage) dmg *= 1 + Math.min(1, tech.duplicationChance()) - if (tech.isLowEnergyDamage) dmg *= 1 + Math.max(0, 1 - m.energy) * 0.5 - if (tech.isMaxEnergyTech) dmg *= 1.5 - if (tech.isEnergyNoAmmo) dmg *= 1.6 - if (tech.isDamageForGuns) dmg *= 1 + 0.14 * b.inventory.length - if (tech.isLowHealthDmg) dmg *= 1 + Math.max(0, 1 - m.health) * 0.5 - if (tech.isHarmDamage && m.lastHarmCycle + 600 > m.cycle) dmg *= 3; - if (tech.isEnergyLoss) dmg *= 1.55; - if (tech.isAcidDmg && m.health > 1) dmg *= 1.35; - if (tech.restDamage > 1 && player.speed < 1) dmg *= tech.restDamage - if (tech.isEnergyDamage) dmg *= 1 + m.energy / 11; - if (tech.isDamageFromBulletCount) dmg *= 1 + bullet.length * 0.007 - if (tech.isRerollDamage) dmg *= 1 + 0.037 * powerUps.research.count - if (tech.isOneGun && b.inventory.length < 2) dmg *= 1.1995 - if (tech.isNoFireDamage && m.cycle > m.fireCDcycle + 120) dmg *= 2 - if (tech.isSpeedDamage) dmg *= 1 + Math.min(0.66, player.speed * 0.0165) - if (tech.isBotDamage) dmg *= 1 + 0.07 * b.totalBots() - if (tech.isDamageAfterKillNoRegen && m.lastKillCycle + 300 > m.cycle) dmg *= 1.5 - return dmg * tech.slowFire * tech.aimDamage + requires: "ON/OFF tech", + effect() { + tech.isFlipFlopDamage = true; }, - duplicationChance() { - return Math.max(0, (tech.isPowerUpsVanish ? 0.12 : 0) + (tech.isStimulatedEmission ? 0.15 : 0) + tech.cancelCount * 0.04 + tech.duplicateChance + m.duplicateChance + tech.wormDuplicate + tech.cloakDuplication + (tech.isAnthropicTech && tech.isDeathAvoidedThisLevel ? 0.45 : 0)) + remove() { + tech.isFlipFlopDamage = false; + } + }, + { + name: "transistor", + description: "if ON regen 20 energy per second
if OFF drain 1 energy per second", + maxCount: 1, + count: 0, + frequency: 4, + frequencyDefault: 4, + allowed() { + return tech.isFlipFlop || tech.isRelay }, - isScaleMobsWithDuplication: false, - maxDuplicationEvent() { - if (tech.is111Duplicate && tech.duplicationChance() > 1.11) { - tech.is111Duplicate = false - const range = 1300 - tech.isScaleMobsWithDuplication = true - for (let i = 0, len = 9; i < len; i++) { - const angle = 2 * Math.PI * i / len - spawn.randomLevelBoss(m.pos.x + range * Math.cos(angle), m.pos.y + range * Math.sin(angle), spawn.nonCollideBossList); - } - spawn.historyBoss(0, 0) - spawn.pulsarBoss(level.exit.x, level.exit.y, 70, true) - spawn.blockBoss(level.enter.x, level.enter.y) - tech.isScaleMobsWithDuplication = false + requires: "ON/OFF tech", + effect() { + tech.isFlipFlopEnergy = true; + }, + remove() { + tech.isFlipFlopEnergy = false; + } + }, + { + name: "shift registers", + description: "set to the ON state
at the start of a level", + maxCount: 1, + count: 0, + frequency: 4, + frequencyDefault: 4, + allowed() { + return tech.isFlipFlopEnergy || tech.isFlipFlopDamage || tech.isFlipFlopHarm || tech.relayIce + }, + requires: "2 ON/OFF techs", + effect() { + tech.isFlipFlopLevelReset = true; + }, + remove() { + tech.isFlipFlopLevelReset = false; + } + }, + { + name: "thermocouple", + description: "if relay switch is in the ON state
condense 1-9 ice IX crystals every second", + maxCount: 9, + count: 0, + frequency: 4, + frequencyDefault: 4, + allowed() { + return tech.isRelay + }, + requires: "relay switch", + effect() { + tech.relayIce++ + }, + remove() { + tech.relayIce = 0 + } + }, + { + name: "crystallizer", + description: "after frozen mobs die they
shatter into ice IX crystals", + maxCount: 9, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return (tech.isIceCrystals || tech.isSporeFreeze || tech.isIceField || tech.isIceShot || tech.relayIce || tech.isNeedleIce || tech.blockingIce > 1) && !tech.sporesOnDeath && !tech.isExplodeMob && !tech.botSpawner && !tech.isMobBlockFling && !tech.nailsDeathMob + }, + requires: "a localized freeze effect, no other mob death tech", + effect() { + tech.iceIXOnDeath++ + }, + remove() { + tech.iceIXOnDeath = 0 + } + }, + { + name: "thermoelectric effect", + description: "killing mobs with ice IX
generates 100 energy", + maxCount: 9, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.isIceField || tech.relayIce || tech.isNeedleIce || tech.blockingIce || tech.iceIXOnDeath || tech.isIceShot + }, + requires: "ice IX", + effect() { + tech.iceEnergy++ + }, + remove() { + tech.iceEnergy = 0; + } + }, + { + name: "superfluidity", + description: "freeze effects are applied to a small area", + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.isIceCrystals || tech.isSporeFreeze || tech.isIceField || tech.relayIce || tech.isNeedleIce || tech.blockingIce > 1 || tech.iceIXOnDeath || tech.isIceShot + }, + requires: "a localized freeze effect", + effect() { + tech.isAoESlow = true + }, + remove() { + tech.isAoESlow = false + } + }, + { + name: "osmoprotectant", + description: `collisions with stunned or frozen mobs
cause you no harm`, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.isStunField || tech.isExplosionStun || tech.isMineStun || tech.oneSuperBall || tech.isHarmFreeze || tech.isIceField || tech.relayIce || tech.isNeedleIce || tech.isIceCrystals || tech.isSporeFreeze || tech.isAoESlow || tech.isFreezeMobs || tech.isCloakStun || tech.orbitBotCount > 1 || tech.isWormholeDamage || tech.blockingIce > 1 || tech.iceIXOnDeath || tech.isIceShot + }, + requires: "a freezing or stunning effect", + effect() { + tech.isFreezeHarmImmune = true; + }, + remove() { + tech.isFreezeHarmImmune = false; + } + }, + { + name: "liquid cooling", + description: `freeze all mobs for 7 seconds
after receiving harm`, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.isSlowFPS + }, + requires: "clock gating", + effect() { + tech.isHarmFreeze = true; + }, + remove() { + tech.isHarmFreeze = false; + } + }, + { + name: "clock gating", + description: `slow time by 50% after receiving harm
reduce harm by 20%`, + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return simulation.fpsCapDefault > 45 + }, + requires: "FPS above 45", + effect() { + tech.isSlowFPS = true; + }, + remove() { + tech.isSlowFPS = false; + } + }, + { + name: "quantum immortality", + description: "reduce harm by 33%
after dying, continue in an alternate reality", + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { return true }, + requires: "", + effect() { + tech.isImmortal = true; + }, + remove() { + tech.isImmortal = false; + } + }, + { + name: "MACHO", + description: "a massive but compact object slowly follows you
take 66% less harm inside it's halo", + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return !tech.isEnergyHealth + }, + requires: "not mass-energy", + effect: () => { + tech.isMACHO = true; //this harm reduction comes from the particle toggling tech.isHarmMACHO + spawn.MACHO() + }, + remove() { + tech.isMACHO = false; + for (let i = 0, len = mob.length; i < len; i++) { + if (mob[i].isMACHO) mob[i].alive = false; + } + } + }, + { + name: "axion", + description: "while inside the MACHO 75% of your total
harm reduction is added to your damage", + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.isMACHO + }, + requires: "MACHO", + effect: () => { + tech.isAxion = true + }, + remove() { + tech.isAxion = false + } + }, + { + name: "ablative drones", + description: "rebuild your broken parts as drones
chance to occur after receiving harm", + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return m.harmReduction() < 1 + }, + requires: "some harm reduction", + effect() { + tech.isDroneOnDamage = true; + for (let i = 0; i < 4; i++) { + b.drone() //spawn drone } }, - setTechFrequency(name, frequency) { + remove() { + tech.isDroneOnDamage = false; + } + }, + { + name: "non-Newtonian armor", + link: `non-Newtonian armor`, + description: "for 10 seconds after receiving harm
reduce harm by 66%", + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return !tech.isEnergyHealth && m.harmReduction() < 1 + }, + requires: "some harm reduction", + effect() { + tech.isHarmArmor = true; + }, + remove() { + tech.isHarmArmor = false; + } + }, + { + name: "radiative equilibrium", + description: "for 10 seconds after receiving harm
increase damage by 200%", + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return m.harmReduction() < 1 + }, + requires: "some harm reduction", + effect() { + tech.isHarmDamage = true; + }, + remove() { + tech.isHarmDamage = false; + } + }, + { + name: "CPT symmetry", + description: "charge, parity, and time invert to undo harm
rewind (1.5—5) seconds for (66—220) energy", + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { //&& (m.fieldUpgrades[m.fieldMode].name !== "molecular assembler" || m.maxEnergy > 1) + return m.maxEnergy > 0.99 && m.fieldUpgrades[m.fieldMode].name !== "standing wave" && !tech.isEnergyHealth && !tech.isRewindField //&& !tech.isRewindGun + }, + requires: "not standing wave, mass-energy, max energy reduction", + effect() { + tech.isRewindAvoidDeath = true; + }, + remove() { + tech.isRewindAvoidDeath = false; + } + }, + { + name: "causality bots", + link: `causality bots`, + description: "when you rewind, build several bots
that protect you for about 9 seconds", + maxCount: 3, + count: 0, + frequency: 2, + frequencyDefault: 2, + isBotTech: true, + allowed() { + return tech.isRewindAvoidDeath || tech.isRewindField + }, + requires: "CPT, retrocausality", + effect() { + tech.isRewindBot++; + }, + remove() { + tech.isRewindBot = 0; + } + }, + { + name: "causality bombs", + link: `causality bombs`, + description: "when you rewind drop several grenades
become immune to harm until they explode", + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.isRewindAvoidDeath || tech.isRewindField + }, + requires: "CPT, retrocausality", + effect() { + tech.isRewindGrenade = true; + }, + remove() { + tech.isRewindGrenade = false; + } + }, + { + name: "piezoelectricity", + description: "colliding with mobs gives you 2048 energy", //
reduce harm by 15% + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return !tech.isEnergyHealth + }, + requires: "not mass-energy", + effect() { + tech.isPiezo = true; + if (simulation.isTextLogOpen) m.energy += 20.48; + }, + remove() { + tech.isPiezo = false; + } + }, + { + name: "ground state", + description: "reduce harm by 66%
you no longer passively regenerate energy", + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return (tech.iceEnergy || tech.isWormholeEnergy || tech.isPiezo || tech.isRailEnergyGain || tech.energySiphon || tech.isEnergyRecovery || tech.dynamoBotCount || tech.isFlipFlopEnergy || tech.isTokamak) && tech.energyRegen !== 0.004 && !tech.isEnergyHealth && !tech.isCrouchRegen + }, + requires: "a way to regen extra energy, not time crystals", + effect: () => { + tech.energyRegen = 0; + m.fieldRegen = tech.energyRegen; + }, + remove() { + tech.energyRegen = 0.001; + m.fieldRegen = tech.energyRegen; + } + }, + { + name: "mass-energy equivalence", + description: "energy protects you instead of health
harm reduction effects provide no benefit", + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return !tech.isZeno && !tech.isNoHeals && !tech.isPiezo && !tech.isRewindAvoidDeath && !tech.isTechDamage && !tech.isMutualism //&& !tech.isAmmoFromHealth && !tech.isRewindGun + }, + requires: "not Zeno, ergodicity, piezoelectricity, CPT, antiscience, mutualism", + effect: () => { + m.health = 0 + document.getElementById("health").style.display = "none" + document.getElementById("health-bg").style.display = "none" + document.getElementById("dmg").style.backgroundColor = "#0cf"; + tech.isEnergyHealth = true; + simulation.mobDmgColor = "rgba(14, 190, 235,0.7)" //"#0cf" + m.displayHealth(); + }, + remove() { + if (tech.isEnergyHealth) { + tech.isEnergyHealth = false; + document.getElementById("health").style.display = "inline" + document.getElementById("health-bg").style.display = "inline" + document.getElementById("dmg").style.backgroundColor = "#f67"; + m.health = Math.max(Math.min(m.maxHealth, m.energy), 0.1); + simulation.mobDmgColor = "rgba(255,0,0,0.7)" + m.displayHealth(); + } + tech.isEnergyHealth = false; + } + }, + { + name: "1st ionization energy", + link: `1st ionization energy`, + description: `each ${powerUps.orb.heal()} you collect
increases your maximum energy by 8`, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.isEnergyHealth + }, + requires: "mass-energy equivalence", + effect() { + tech.healGiveMaxEnergy = true; //tech.healMaxEnergyBonus given from heal power up + powerUps.heal.color = "#0ae" + for (let i = 0; i < powerUp.length; i++) { //find active heal power ups and adjust color live + if (powerUp[i].name === "heal") powerUp[i].color = powerUps.heal.color + } + }, + remove() { + tech.healGiveMaxEnergy = false; + // tech.healMaxEnergyBonus = 0 + powerUps.heal.color = "#0eb" + for (let i = 0; i < powerUp.length; i++) { //find active heal power ups and adjust color live + if (powerUp[i].name === "heal") powerUp[i].color = powerUps.heal.color + } + } + }, + { + name: "weak interaction", + description: "each unused power up at the end of a level
adds 5 maximum energy", // (up to 51 health per level)", + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return !tech.isDroneGrab + }, + requires: "not delivery drone", + effect() { + tech.isExtraMaxEnergy = true; //tracked by tech.extraMaxHealth + }, + remove() { + tech.isExtraMaxEnergy = false; + } + }, + { + name: "electroweak interaction", + description: "unused power ups at the end of each level
are still activated (selections are random)", + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.isExtraMaxEnergy + }, + requires: "weak interaction", + effect() { + tech.isEndLevelPowerUp = true; + }, + remove() { + tech.isEndLevelPowerUp = false; + } + }, + { + name: "electronegativity", + description: "increase damage by 1%
for every 11 stored energy", + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { return true }, + requires: "", + effect: () => { + tech.isEnergyDamage = true + }, + remove() { + tech.isEnergyDamage = false; + } + }, + { + name: "exciton", + description: `increase damage by 60%, but
${powerUps.orb.ammo()} will no longer spawn`, + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { return true }, + requires: "", + effect() { + tech.isEnergyNoAmmo = true; + }, + remove() { + tech.isEnergyNoAmmo = false; + } + }, + { + name: "exothermic process", + description: "increase damage by 50%
if a mob dies drain energy by 25%", + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { return true }, + requires: "", + effect() { + tech.isEnergyLoss = true; + }, + remove() { + tech.isEnergyLoss = false; + } + }, + { + name: "heat engine", + description: `increase damage by 50%, but
reduce maximum energy by 50`, + maxCount: 1, + count: 0, + frequency: 3, + frequencyDefault: 3, + allowed() { + return tech.isEnergyLoss && !tech.isRewindAvoidDeath + }, + requires: "exothermic process, not CPT", + effect() { + tech.isMaxEnergyTech = true; + m.setMaxEnergy() + }, + remove() { + tech.isMaxEnergyTech = false; + m.setMaxEnergy() + } + }, + { + name: "Gibbs free energy", + description: `increase damage by 5%
for every 10 energy below 100`, + maxCount: 1, + count: 0, + frequency: 3, + frequencyDefault: 3, + allowed() { + return tech.isEnergyLoss && m.maxEnergy < 1.01 + }, + requires: "exothermic process, not max energy increase", + effect() { + tech.isLowEnergyDamage = true; + }, + remove() { + tech.isLowEnergyDamage = false; + } + }, + { + name: "overcharge", + description: "increase your maximum energy by 60
+10% JUNK to the potential tech pool", + maxCount: 9, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { return true }, + requires: "", + effect() { + tech.bonusEnergy += 0.6 + m.setMaxEnergy() + this.refundAmount += tech.addJunkTechToPool(0.1) + }, + refundAmount: 0, + remove() { + tech.bonusEnergy = 0; + m.setMaxEnergy() + if (this.count > 0 && this.refundAmount > 0) { + tech.removeJunkTechFromPool(this.refundAmount) + this.refundAmount = 0 + } + } + }, + { + name: "Maxwell's demon", + description: "energy above your max decays 95% slower
+10% JUNK to the potential tech pool", + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return m.energy > m.maxEnergy || build.isExperimentSelection + }, + requires: "energy above your max", + effect() { + tech.overfillDrain = 0.92 //70% = 1-(1-0.75)/(1-0.15) //92% = 1-(1-0.75)/(1-0.87) + this.refundAmount += tech.addJunkTechToPool(0.1) + }, + refundAmount: 0, + remove() { + tech.overfillDrain = 0.7 + if (this.count > 0 && this.refundAmount > 0) { + tech.removeJunkTechFromPool(this.refundAmount) + this.refundAmount = 0 + } + } + }, + { + name: "inductive coupling", + description: "passive energy regen is increased by 700%
but you only regen when crouched", + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return tech.energyRegen !== 0 && !tech.isDamageAfterKillNoRegen + }, + requires: "not ground state, apex predator", + effect() { + tech.isCrouchRegen = true; //only used to check for requirements + m.regenEnergy = function () { + if (m.immuneCycle < m.cycle && m.crouch) m.energy += 7 * m.fieldRegen; //m.fieldRegen = 0.001 + if (m.energy < 0) m.energy = 0 + } + }, + remove() { + tech.isCrouchRegen = false; + m.regenEnergy = m.regenEnergyDefault + } + }, + { + name: "energy conservation", + description: "5% of damage done recovered as energy", + maxCount: 9, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { return true }, + requires: "", + effect() { + tech.energySiphon += 0.05; + }, + remove() { + tech.energySiphon = 0; + } + }, + { + name: "waste heat recovery", + description: "if a mob has died in the last 5 seconds
regen 5% of max energy every second", + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { return true }, + requires: "", + effect() { + tech.isEnergyRecovery = true; + }, + remove() { + tech.isEnergyRecovery = false; + } + }, + { + name: "recycling", + description: "if a mob has died in the last 5 seconds
regain 1% of max health every second", + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + isHealTech: true, + allowed() { + return !tech.isEnergyHealth + }, + requires: "not mass-energy equivalence", + effect() { + tech.isHealthRecovery = true; + }, + remove() { + tech.isHealthRecovery = false; + } + }, + { + name: "predator", + description: "if a mob has died in the last 5 seconds
increase damage 50% and inhibit energy regen", + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { return !tech.isCrouchRegen }, + requires: "not inductive coupling", + effect() { + tech.isDamageAfterKillNoRegen = true; + m.regenEnergy = function () { + if (m.immuneCycle < m.cycle && (m.lastKillCycle + 300 < m.cycle)) m.energy += m.fieldRegen; //m.fieldRegen = 0.001 + if (m.energy < 0) m.energy = 0 + } + }, + remove() { + if (this.count) m.regenEnergy = m.regenEnergyDefault + tech.isDamageAfterKillNoRegen = false; + } + }, + { + name: "torpor", + description: "if a mob has not died in the last 5 seconds
reduce harm by 66%", + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return !tech.isEnergyHealth + }, + requires: "not mass-energy", + effect() { + tech.isHarmReduceNoKill = true; + }, + remove() { + tech.isHarmReduceNoKill = false; + } + }, + { + name: "Zeno's paradox", + description: "reduce harm by 83%, but every 5 seconds
remove 1/10 of your current health", + // description: "every 5 seconds remove 1/10 of your health
reduce harm by 90%", + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { return !tech.isEnergyHealth }, + requires: "not mass-energy", + effect() { + tech.isZeno = true; + }, + remove() { + tech.isZeno = false; + } + }, + { + name: "negative feedback", + description: "increase damage by 5%
for every 10 health below 100", + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return m.health < 0.6 || build.isExperimentSelection + }, + requires: "health below 60", + effect() { + tech.isLowHealthDmg = true; //used in mob.damage() + }, + remove() { + tech.isLowHealthDmg = false; + } + }, + { + name: "antiscience", + description: "increase damage by 90%
lose 11 health when you pick up a tech", + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return !tech.isEnergyHealth + }, + requires: "not mass-energy", + effect() { + tech.isTechDamage = true; + }, + remove() { + tech.isTechDamage = false; + } + }, + { + name: "entropy exchange", + description: "heal for 3% of damage done
take 10% more harm", + maxCount: 9, + count: 0, + frequency: 2, + frequencyDefault: 2, + isHealTech: true, + allowed() { + return !tech.isEnergyHealth + }, + requires: "not mass-energy equivalence", + effect() { + tech.healthDrain += 0.03; + }, + remove() { + tech.healthDrain = 0; + } + }, + { + name: "fluoroantimonic acid", + description: "increase damage by 35%
when your health is above 100", + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return m.maxHealth > 1; + }, + requires: "max health above 100", + effect() { + tech.isAcidDmg = true; + }, + remove() { + tech.isAcidDmg = false; + } + }, + { + name: "tungsten carbide", + description: "increase your maximum health by 100
landings that force you to crouch cause harm", + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return !tech.isEnergyHealth + }, + requires: "not mass-energy equivalence", + effect() { + tech.isFallingDamage = true; + m.setMaxHealth(); + m.addHealth(1 / simulation.healScale) + }, + remove() { + tech.isFallingDamage = false; + m.setMaxHealth(); + } + }, + { + name: "quenching", + description: `over healing from ${powerUps.orb.heal()} does harm
but it also increase your maximum health`, + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return !tech.isEnergyHealth && !tech.isNoHeals + }, + requires: "not mass-energy equivalence, ergodicity", + effect() { + tech.isOverHeal = true; + }, + remove() { + tech.isOverHeal = false; + } + }, + { + name: "negative entropy", + description: `at the start of each level
spawn ${powerUps.orb.heal()} for every 26 missing health`, + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + isHealTech: true, + allowed() { + return m.health > 0.1 && !tech.isNoHeals + }, + requires: "has some health, not ergodicity", + effect() { + tech.isHealLowHealth = true; + }, + remove() { + tech.isHealLowHealth = false; + } + }, + { + name: "adiabatic healing", + description: `${powerUps.orb.heal()} are 100% more effective
+5% JUNK to the potential tech pool`, + maxCount: 3, + count: 0, + frequency: 1, + frequencyDefault: 1, + isHealTech: true, + allowed() { + return ((m.health / m.maxHealth) < 0.7 || build.isExperimentSelection) && !tech.isEnergyHealth && !tech.isNoHeals + }, + requires: "under 70% health, not mass-energy equivalence, ergodicity", + effect() { + tech.largerHeals++; + this.refundAmount += tech.addJunkTechToPool(0.05) + }, + refundAmount: 0, + remove() { + tech.largerHeals = 1; + if (this.count > 0 && this.refundAmount > 0) { + tech.removeJunkTechFromPool(this.refundAmount) + this.refundAmount = 0 + } + } + }, + { + name: "maintenance", + description: `double the frequency of finding healing tech
spawn ${powerUps.orb.heal(11)}`, + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + isNonRefundable: true, + isBadRandomOption: true, + allowed() { return true }, + requires: "NOT EXPERIMENT MODE", + effect() { + for (let i = 0; i < 11; i++) powerUps.spawn(m.pos.x + 60 * (Math.random() - 0.5), m.pos.y + 60 * (Math.random() - 0.5), "heal"); for (let i = 0, len = tech.tech.length; i < len; i++) { - if (tech.tech[i].name === name) tech.tech[i].frequency = frequency + if (tech.tech[i].isHealTech) tech.tech[i].frequency *= 2 } }, - setBotTechFrequency(f = 0) { + remove() { } + }, + { + name: "anthropic principle", + nameInfo: "", + addNameInfo() { + setTimeout(function () { + powerUps.research.changeRerolls(0) + }, 1000); + }, + description: `once per level, instead of dying
use ${powerUps.orb.research(1)} and spawn ${powerUps.orb.heal(5)}`, + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + isHealTech: true, + allowed() { + return powerUps.research.count > 0 || build.isExperimentSelection + }, + requires: "at least 1 research", + effect() { + tech.isDeathAvoid = true; + tech.isDeathAvoidedThisLevel = false; + setTimeout(function () { + powerUps.research.changeRerolls(0) + }, 1000); + }, + remove() { + tech.isDeathAvoid = false; + } + }, + { + name: "weak anthropic principle", + description: "after anthropic principle prevents your death
add 45% duplication chance for that level", + maxCount: 1, + count: 0, + frequency: 3, + frequencyDefault: 3, + allowed() { + return tech.isDeathAvoid + }, + requires: "anthropic principle", + effect() { + tech.isAnthropicTech = true + powerUps.setDupChance(); //needed after adjusting duplication chance + }, + remove() { + tech.isAnthropicTech = false + powerUps.setDupChance(); //needed after adjusting duplication chance + } + }, + { + name: "strong anthropic principle", + description: "after anthropic principle prevents your death
increase damage by 137.03599% for that level", + maxCount: 1, + count: 0, + frequency: 3, + frequencyDefault: 3, + allowed() { + return tech.isDeathAvoid + }, + requires: "anthropic principle", + effect() { + tech.isAnthropicDamage = true + }, + remove() { + tech.isAnthropicDamage = false + } + }, + { + name: "non-unitary operator", + link: `non-unitary operator`, + description: "reduce combat difficulty by 2 levels, but
after a collision enter an alternate reality", + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return !tech.isResearchReality && !tech.isSwitchReality + }, + requires: "not Ψ(t) collapse, many-worlds", + effect() { + tech.isCollisionRealitySwitch = true; + level.difficultyDecrease(simulation.difficultyMode * 2) + }, + remove() { + tech.isCollisionRealitySwitch = false; + if (this.count > 0) { + level.difficultyIncrease(simulation.difficultyMode * 2) + } + } + }, + { + name: "many-worlds", + // description: "each level is an alternate reality, where you
find a tech at the start of each level", + description: `on each new level use ${powerUps.orb.research(1)} to enter an
alternate reality and spawn a tech power up`, + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return !tech.isResearchReality && !tech.isCollisionRealitySwitch + }, + requires: "not Ψ(t) collapse, non-unitary", + effect() { + tech.isSwitchReality = true; + }, + remove() { + tech.isSwitchReality = false; + } + }, + { + name: "Ψ(t) collapse", + link: `Ψ(t) collapse`, + description: `enter an alternate reality after you research
spawn ${powerUps.orb.research(21)}`, + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return !tech.isSwitchReality && !tech.isCollisionRealitySwitch && !tech.isJunkResearch + }, + requires: "not many-worlds, non-unitary, pseudoscience", + effect() { + tech.isResearchReality = true; + for (let i = 0; i < 16; i++) powerUps.spawn(m.pos.x + Math.random() * 60, m.pos.y + Math.random() * 60, "research", false); + }, + remove() { + tech.isResearchReality = false; + } + }, + { + name: "decoherence", + description: `researched or canceled tech won't reoccur
spawn ${powerUps.orb.research(9)}`, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return (powerUps.research.count > 2 || build.isExperimentSelection) && !tech.isDeterminism + }, + requires: "not determinism, at least 3 research", + effect() { + tech.isBanish = true + for (let i = 0; i < 9; i++) powerUps.spawn(m.pos.x + 40 * (Math.random() - 0.5), m.pos.y + 40 * (Math.random() - 0.5), "research", false); + }, + remove() { + if (tech.isBanish) { + tech.isBanish = false + powerUps.tech.banishLog = [] //reset banish log + powerUps.research.changeRerolls(-10) + } + } + }, + { + name: "renormalization", + description: `using ${powerUps.orb.research(1)} for any purpose
has a 40% chance to spawn ${powerUps.orb.research(1)}`, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return (powerUps.research.count > 2 || build.isExperimentSelection) && !tech.isSuperDeterminism + }, + requires: "at least 3 research and not superdeterminism", + effect() { + tech.renormalization = true; + }, + remove() { + tech.renormalization = false; + } + }, + { + name: "perturbation theory", + description: `66% decreased delay after firing
when you have no ${powerUps.orb.research(1)} in your inventory`, + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return powerUps.research.count === 0 + }, + requires: "no research", + effect() { + tech.isRerollHaste = true; + tech.researchHaste = 0.33; + b.setFireCD(); + }, + remove() { + tech.isRerollHaste = false; + tech.researchHaste = 1; + b.setFireCD(); + } + }, + { + name: "ansatz", + description: `after choosing a field, tech, or gun
spawn ${powerUps.orb.research(2)}if you have no ${powerUps.orb.research(1)} in your inventory`, + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return powerUps.research.count === 0 && !tech.isSuperDeterminism && !tech.isRerollHaste && !tech.isResearchReality + }, + requires: "no research, not superdeterminism, Ψ(t) collapse, perturbation theory", + effect: () => { + tech.isAnsatz = true; + }, + remove() { + tech.isAnsatz = false; + } + }, + { + name: "Bayesian statistics", + description: `increase damage by 3.7%
for each ${powerUps.orb.research(1)} in your inventory`, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return powerUps.research.count > 3 || build.isExperimentSelection + }, + requires: "at least 4 research", + effect() { + tech.isRerollDamage = true; + }, + remove() { + tech.isRerollDamage = false; + } + }, + { + name: "pseudoscience", + description: "when selecting a power up, research 3 times
for free, but add 0-3% JUNK to the tech pool", + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return !tech.isResearchReality //tech.isResearchBoss || tech.isMetaAnalysis || tech.isRerollBots || tech.isDeathAvoid || tech.isRerollDamage || build.isExperimentSelection + }, + requires: "not Ψ(t) collapse", //"abiogenesis, meta-analysis, bot fabrication, anthropic principle, or Bayesian statistics, not Ψ(t) collapse", + effect() { + tech.isJunkResearch = true; + }, + remove() { + tech.isJunkResearch = false; + } + }, + { + name: "Born rule", + description: "remove all current tech
spawn new tech to replace them", + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + isNonRefundable: true, + isBadRandomOption: true, + allowed() { + return (tech.totalCount > 6) + }, + requires: "NOT EXPERIMENT MODE, more than 6 tech", + effect: () => { + //remove active bullets //to get rid of bots + for (let i = 0; i < bullet.length; ++i) Matter.Composite.remove(engine.world, bullet[i]); + bullet = []; + let count = 1 //count tech + for (let i = 0, len = tech.tech.length; i < len; i++) { // spawn new tech power ups + if (!tech.tech[i].isNonRefundable) count += tech.tech[i].count + } + if (tech.isDeterminism) count -= 4 //remove the bonus tech + if (tech.isSuperDeterminism) count -= 4 //remove the bonus tech + + tech.setupAllTech(); // remove all tech + if (simulation.isCheating) tech.setCheating(); + lore.techCount = 0; + // tech.addLoreTechToPool(); + for (let i = 0; i < count; i++) powerUps.spawn(m.pos.x + 100 * (Math.random() - 0.5), m.pos.y + 100 * (Math.random() - 0.5), "tech"); // spawn new tech power ups + //have state is checked in m.death() + }, + remove() { } + }, + { + name: "Occam's razor", + descriptionFunction() { + return `randomly remove ${this.removePercent * 100}% of your tech
for each removed gain ${this.damagePerRemoved * 100}% damage` + }, + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + isNonRefundable: true, + isBadRandomOption: true, + allowed() { + return (tech.totalCount > 6) + }, + requires: "NOT EXPERIMENT MODE, more than 6 tech", + removePercent: 0.5, + damagePerRemoved: 0.36, + effect() { + let pool = [] + for (let i = 0, len = tech.tech.length; i < len; i++) { // spawn new tech power ups + if (tech.tech[i].count && !tech.tech[i].isNonRefundable) pool.push(i) + } + pool = shuffle(pool); //shuffles order of maps + let removeCount = 0 + for (let i = 0, len = pool.length * this.removePercent; i < len; i++) removeCount += tech.removeTech(pool[i]) + tech.OccamDamage = 1 + this.damagePerRemoved * removeCount + }, + remove() { + tech.OccamDamage = 0; + } + }, + { + name: "abiogenesis", + description: `at the start of a level spawn a 2nd boss
use ${powerUps.orb.research(4)}or add 49% JUNK to the tech pool`, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return (build.isExperimentSelection || powerUps.research.count > 3) && !tech.isDuplicateBoss + }, + requires: "at least 4 research and not parthenogenesis", + effect() { + tech.isResearchBoss = true; //abiogenesis + }, + remove() { + tech.isResearchBoss = false; + } + }, + { + name: "bubble fusion", + description: `after destroying a mob's natural shield
spawn 1-2 ${powerUps.orb.heal()}, ${powerUps.orb.ammo()}, or ${powerUps.orb.research(1)}`, + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { return true }, + requires: "", + effect() { + tech.isShieldAmmo = true; + }, + remove() { + tech.isShieldAmmo = false; + } + }, + { + name: "meta-analysis", + description: `if you choose a JUNK tech you instead get a
random normal tech and ${powerUps.orb.research(3)}`, + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { return true }, + requires: "", + effect() { + tech.isMetaAnalysis = true + }, + remove() { + tech.isMetaAnalysis = false + } + }, + { + name: "replication", + description: "10% chance to duplicate spawned power ups
+40% JUNK to the potential tech pool", + maxCount: 9, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return tech.duplicationChance() < 1 + }, + requires: "below 100% duplication chance", + effect() { + tech.duplicateChance += 0.1 + powerUps.setDupChance(); //needed after adjusting duplication chance + if (!build.isExperimentSelection && !simulation.isTextLogOpen) simulation.circleFlare(0.1); + this.refundAmount += tech.addJunkTechToPool(0.4) + }, + refundAmount: 0, + remove() { + tech.duplicateChance = 0 + powerUps.setDupChance(); //needed after adjusting duplication chance + if (this.count > 0 && this.refundAmount > 0) { + tech.removeJunkTechFromPool(this.refundAmount) + this.refundAmount = 0 + } + } + }, + { + name: "stimulated emission", + description: "15% chance to duplicate spawned power ups
but, after a collision eject 1 tech", + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return tech.duplicationChance() < 1 + }, + requires: "below 100% duplication chance", + effect: () => { + tech.isStimulatedEmission = true + powerUps.setDupChance(); //needed after adjusting duplication chance + if (!build.isExperimentSelection && !simulation.isTextLogOpen) simulation.circleFlare(0.15); + }, + remove() { + tech.isStimulatedEmission = false + powerUps.setDupChance(); //needed after adjusting duplication chance + } + }, + { + name: "metastability", + description: "12% chance to duplicate spawned power ups
duplicates explode with a 3 second half-life ", + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return tech.duplicationChance() < 1 + }, + requires: "below 100% duplication chance", + effect: () => { + tech.isPowerUpsVanish = true + powerUps.setDupChance(); //needed after adjusting duplication chance + if (!build.isExperimentSelection && !simulation.isTextLogOpen) simulation.circleFlare(0.12); + }, + remove() { + tech.isPowerUpsVanish = false + powerUps.setDupChance(); //needed after adjusting duplication chance + } + }, + { + name: "futures exchange", + description: "clicking Ă— to cancel a field, tech, or gun
adds 4% power up duplication chance", + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return tech.duplicationChance() < 1 && !tech.isDeterminism + }, + requires: "below 100% duplication chance, not determinism", + effect() { + tech.isCancelDuplication = true //search for tech.cancelCount to balance + powerUps.setDupChance(); //needed after adjusting duplication chance + }, + remove() { + tech.isCancelDuplication = false + powerUps.setDupChance(); //needed after adjusting duplication chance + } + }, + { + name: "commodities exchange", + description: `clicking Ă— to cancel a field, tech, or gun
spawns 5-10 ${powerUps.orb.heal()}, ${powerUps.orb.ammo()}, or ${powerUps.orb.research(1)}`, + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return !tech.isDeterminism + }, + requires: "not determinism", + effect() { + tech.isCancelRerolls = true + }, + remove() { + tech.isCancelRerolls = false + } + }, + { + name: "correlated damage", + description: "your chance to duplicate power ups
increases your damage by the same percent", + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return tech.duplicationChance() > 0.15 + }, + requires: "duplication chance > 15%", + effect() { + tech.isDupDamage = true; + }, + remove() { + tech.isDupDamage = false; + } + }, + { + name: "parthenogenesis", + description: " bosses have a 2x chance to be duplicated, but their
health is increased by your duplication chance
", + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return tech.duplicationChance() > 0 && !tech.isResearchBoss + }, + requires: "some duplication chance, not abiogenesis", + effect() { + tech.isDuplicateBoss = true; + }, + remove() { + tech.isDuplicateBoss = false; + } + }, + { + name: "apomixis", + description: `when you reach 111% duplication
spawn 11 bosses with 111% more health`, + maxCount: 1, + count: 0, + frequency: 10, + frequencyDefault: 10, + allowed() { + return tech.duplicationChance() > 0.99 + }, + requires: "duplication chance above 99%", + effect() { + tech.is111Duplicate = true; + tech.maxDuplicationEvent() + }, + remove() { + tech.is111Duplicate = false; + } + }, + { + name: "exchange symmetry", + description: "convert 1 random tech into 2 new guns
recursive tech lose all stacks", + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + isNonRefundable: true, + isBadRandomOption: true, + allowed() { + return (tech.totalCount > 3) && !tech.isSuperDeterminism + }, + requires: "NOT EXPERIMENT MODE, at least 4 tech, not superdeterminism", + effect: () => { + const have = [] //find which tech you have + for (let i = 0; i < tech.tech.length; i++) { + if (tech.tech[i].count > 0) have.push(i) + } + const choose = have[Math.floor(Math.random() * have.length)] + simulation.makeTextLog(`tech.removeTech("${tech.tech[choose].name}")`) + for (let i = 0; i < tech.tech[choose].count; i++) { + powerUps.spawn(m.pos.x, m.pos.y, "gun"); + } + powerUps.spawn(m.pos.x, m.pos.y, "gun"); + // powerUps.spawn(m.pos.x, m.pos.y, "gun"); + tech.tech[choose].count = 0; + tech.tech[choose].remove(); // remove a random tech form the list of tech you have + tech.tech[choose].isLost = true + simulation.updateTechHUD(); + }, + remove() { } + }, + { + name: "monte carlo experiment", + description: "spawn 2 tech
remove 1 random tech", + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + isNonRefundable: true, + isBadRandomOption: true, + allowed() { + return (tech.totalCount > 3) && !tech.isSuperDeterminism + }, + requires: "NOT EXPERIMENT MODE, at least 4 tech, not superdeterminism", + effect: () => { + const removeTotal = tech.removeTech() + for (let i = 0; i < removeTotal + 1; i++) powerUps.spawn(m.pos.x + 60 * (Math.random() - 0.5), m.pos.y + 60 * (Math.random() - 0.5), "tech"); + }, + remove() { } + }, + { + name: "strange attractor", + description: `use ${powerUps.orb.research(2)} to spawn 1 tech
with double your duplication chance`, + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + isNonRefundable: true, + isBadRandomOption: true, + allowed() { + return !tech.isSuperDeterminism && tech.duplicationChance() > 0 && powerUps.research.count > 1 + }, + requires: "NOT EXPERIMENT MODE, some duplication, not super determinism", + effect: () => { + powerUps.research.changeRerolls(-2) + simulation.makeTextLog(`m.research -= 2
${powerUps.research.count}`) + powerUps.directSpawn(m.pos.x, m.pos.y, "tech"); + if (Math.random() < tech.duplicationChance() * 2) powerUps.directSpawn(m.pos.x + 10, m.pos.y + 5, "tech"); + }, + remove() { } + }, + { + name: "vector fields", + description: "double the frequency of finding field tech
spawn a field", + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + isNonRefundable: true, + isBadRandomOption: true, + allowed() { + return !tech.isSuperDeterminism + }, + requires: "NOT EXPERIMENT MODE, not superdeterminism", + effect() { + powerUps.spawn(m.pos.x, m.pos.y, "field"); for (let i = 0, len = tech.tech.length; i < len; i++) { - if (tech.tech[i].isBotTech) { - switch (tech.tech[i].name) { - case "dynamo-bot": - tech.tech[i].frequency = f - break; - case "orbital-bot": - tech.tech[i].frequency = f - break; - case "laser-bot": - tech.tech[i].frequency = f - break; - case "boom-bot": - tech.tech[i].frequency = f - break; - case "foam-bot": - tech.tech[i].frequency = f - break; - case "nail-bot": - tech.tech[i].frequency = f - break; - } + if (tech.tech[i].isFieldTech) tech.tech[i].frequency *= 2 + } + }, + remove() { + // if (this.count > 1) { + // for (let i = 0, len = tech.tech.length; i < len; i++) { + // if (tech.tech[i].isFieldTech) tech.tech[i].frequency /= 2 + // } + // } + } + }, + { + name: "reinforcement learning", + description: "increase the frequency of finding copies of
recursive tech you already have by 1000%", + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return tech.totalCount > 9 + }, + requires: "at least 10 tech", + effect: () => { + for (let i = 0, len = tech.tech.length; i < len; i++) { + if (tech.tech[i].count > 0) tech.tech[i].frequency *= 10 + } + }, + remove() { + for (let i = 0, len = tech.tech.length; i < len; i++) { + if (tech.tech[i].count > 0 && tech.tech[i].frequency > 1) tech.tech[i].frequency /= 10 + } + } + }, + { + name: "backward induction", + description: `use ${powerUps.orb.research(2)} to choose all the unchosen
tech from your previous tech selection`, + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + isNonRefundable: true, + isBadRandomOption: true, + allowed() { + return powerUps.tech.choiceLog.length > 10 && !tech.isDeterminism && powerUps.research.count > 1 + }, + requires: "NOT EXPERIMENT MODE, rejected an option in the last tech selection, at least 2 research, not determinism", + effect: () => { + powerUps.research.changeRerolls(-2) + let num = 3 + if (tech.isExtraChoice) num = 5 + if (tech.isDeterminism) num = 1 + for (let i = 0; i < num; i++) { + const index = powerUps.tech.choiceLog[powerUps.tech.choiceLog.length - i - 1] + if (index !== powerUps.lastTechIndex && tech.tech[index].count < tech.tech[index].maxCount && tech.tech[index].allowed() && tech.tech[index].name !== "backward induction") { + tech.giveTech(index) + simulation.makeTextLog(`tech.giveTech("${tech.tech[index].name}") //backward induction`); } } }, - tech: [{ - name: "integrated armament", - link: `integrated armament`, - description: `increase damage by 19.95%
your inventory can only hold 1 gun`, - maxCount: 1, - count: 0, - frequency: 1, - frequencyDefault: 1, - allowed() { - return b.inventory.length === 1 //&& !tech.haveGunCheck("CPT gun") - }, - requires: "only 1 gun", - effect() { - tech.isOneGun = true; - for (let i = 0; i < tech.tech.length; i++) { - if (tech.tech[i].name === "CPT gun") tech.tech[i].description = `adds the CPT gun to your inventory
it rewinds your health, velocity, and position
replaces your current gun
` - } - }, - remove() { - tech.isOneGun = false; - for (let i = 0; i < tech.tech.length; i++) { - if (tech.tech[i].name === "CPT gun") tech.tech[i].description = `adds the CPT gun to your inventory
it rewinds your health, velocity, and position` - } - } - }, - { - name: "entanglement", - nameInfo: "", - addNameInfo() { - setTimeout(function() { - simulation.boldActiveGunHUD(); - }, 1000); - }, - description: "while your first gun is equipped
reduce harm by 13% for each of your guns", - maxCount: 1, - count: 0, - frequency: 1, - frequencyDefault: 1, - allowed() { - return b.inventory.length > 1 && !tech.isEnergyHealth - }, - requires: "at least 2 guns, not mass-energy", - effect() { - tech.isEntanglement = true - setTimeout(function() { - simulation.boldActiveGunHUD(); - }, 1000); + remove() { } + }, + { + name: "unified field theory", + description: `spawn ${powerUps.orb.research(6)}and when paused
clicking the field box switches your field`, + // description: `in the pause menu, change your field
by clicking on your field's box`, + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return !tech.isSuperDeterminism + }, + requires: "not superdeterminism", + effect() { + tech.isGunSwitchField = true; + for (let i = 0; i < 6; i++) powerUps.spawn(m.pos.x + 40 * (Math.random() - 0.5), m.pos.y + 40 * (Math.random() - 0.5), "research", false); + }, + remove() { + if (tech.isGunSwitchField) { + tech.isGunSwitchField = false; + powerUps.research.changeRerolls(-6) + } + } + }, + { + name: "cross disciplinary", + description: "tech have an extra field or gun choice", + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return !tech.isDeterminism + }, + requires: "not determinism", + effect: () => { + tech.isExtraGunField = true; + // for (let i = 0; i < 2; i++) powerUps.spawn(m.pos.x + 40 * (Math.random() - 0.5), m.pos.y + 40 * (Math.random() - 0.5), "research", false); - }, - remove() { - tech.isEntanglement = false; - } - }, - { - name: "arsenal", - // descriptionFunction() { - // return `increase damage by ${14 * b.inventory.length}%
14% for each gun in your inventory` - // }, - description: "increase damage by 14%
for each gun in your inventory", - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return b.inventory.length > 2 - }, - requires: "at least 3 guns", - effect() { - tech.isDamageForGuns = true; - }, - remove() { - tech.isDamageForGuns = false; - } - }, - { - name: "active cooling", - description: "18% decreased delay after firing
for each gun in your inventory", - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return b.inventory.length > 1 - }, - requires: "at least 2 guns", - effect() { - tech.isFireRateForGuns = true; - b.setFireCD(); - }, - remove() { - tech.isFireRateForGuns = false; - b.setFireCD(); - } - }, - { - name: "generalist", - description: "spawn 8 guns, but you can't switch guns
guns cycle automatically with each new level", - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return (tech.isDamageForGuns || tech.isFireRateForGuns) && b.inventory.length < b.guns.length - 5 //12-5 guns total - }, - requires: "arsenal or active cooling and less than 7 guns", - effect() { - tech.isGunCycle = true; - for (let i = 0; i < 8; i++) powerUps.spawn(m.pos.x + 10 * Math.random(), m.pos.y + 10 * Math.random(), "gun"); - }, - remove() { - if (tech.isGunCycle) { - for (let i = 0; i < 8; i++) { - if (b.inventory.length) b.removeGun(b.guns[b.inventory[b.inventory.length - 1]].name) //remove your last gun - } - tech.isGunCycle = false; - } - } - }, - { - name: "gun sciences", - description: "spawn a gun and double the frequency
of finding tech for your guns", - maxCount: 1, - count: 0, - frequency: 1, - frequencyDefault: 1, - isNonRefundable: true, - // isExperimentHide: true, - isBadRandomOption: true, - allowed() { - return !tech.isSuperDeterminism - }, - requires: "NOT EXPERIMENT MODE, not superdeterminism", - effect() { - powerUps.spawn(m.pos.x, m.pos.y, "gun"); - // this.count-- - for (let i = 0, len = tech.tech.length; i < len; i++) { - if (tech.tech[i].isGunTech) tech.tech[i].frequency *= 2 - } - }, - remove() {} - }, - { - name: "ad hoc", - description: `for every gun in your inventory spawn a
${powerUps.orb.heal()}, ${powerUps.orb.research(1)}, field, ${powerUps.orb.ammo(1)}, or tech`, - maxCount: 1, //random power up - count: 0, - frequency: 1, - frequencyDefault: 1, - isNonRefundable: true, - allowed() { - return b.inventory.length > 1 - }, - requires: "NOT EXPERIMENT MODE, at least 2 guns", - effect() { - for (let i = 0; i < b.inventory.length; i++) { - if (Math.random() < 0.2) { - powerUps.spawn(m.pos.x + 10 * Math.random(), m.pos.y + 10 * Math.random(), "tech"); - } else if (Math.random() < 0.25) { - powerUps.spawn(m.pos.x + 10 * Math.random(), m.pos.y + 10 * Math.random(), "field"); - } else if (Math.random() < 0.33) { - powerUps.spawn(m.pos.x + 10 * Math.random(), m.pos.y + 10 * Math.random(), "heal"); - } else if (Math.random() < 0.5) { - powerUps.spawn(m.pos.x + 10 * Math.random(), m.pos.y + 10 * Math.random(), "ammo"); - } else { - powerUps.spawn(m.pos.x + 10 * Math.random(), m.pos.y + 10 * Math.random(), "research"); - } - } - }, - remove() {} - }, - { - name: "applied science", - description: `spawn ${powerUps.orb.research(1)} and get a random tech
for each gun in your inventory`, - maxCount: 9, - count: 0, - isNonRefundable: true, - frequency: 2, - frequencyDefault: 2, - allowed() { - return b.inventory.length > 1 - }, - requires: "NOT EXPERIMENT MODE, at least 2 guns", - effect() { - for (let i = b.inventory.length - 1; i > -1; i--) { - //spawn a research for each gun - powerUps.spawn(m.pos.x + 40 * (Math.random() - 0.5), m.pos.y + 40 * (Math.random() - 0.5), "research", false); - //find a gun tech for this gun - const gunTechPool = [] - for (let j = 0, len = tech.tech.length; j < len; j++) { - // console.log(j, tech.tech[j].isGunTech, tech.tech[j].allowed(), !tech.tech[j].isJunk, !tech.tech[j].isBadRandomOption, tech.tech[j].count < tech.tech[j].maxCount) - //set current gun to active so allowed works - const originalActiveGunIndex = b.activeGun - b.activeGun = b.inventory[i] //to make the .allowed work for guns that aren't active - if (tech.tech[j].isGunTech && tech.tech[j].allowed() && !tech.tech[j].isJunk && !tech.tech[j].isBadRandomOption && tech.tech[j].count < tech.tech[j].maxCount) { - const regex = tech.tech[j].requires.search(b.guns[b.inventory[i]].name) //get string index of gun name - const not = tech.tech[j].requires.search(' not ') //get string index of ' not ' - //look for the gun name in the requirements, but the gun name needs to show up before the word ' not ' - if (regex !== -1 && (not === -1 || not > regex)) { - gunTechPool.push(j) - } - } - b.activeGun = originalActiveGunIndex - } - if (gunTechPool.length) { - const index = Math.floor(Math.random() * gunTechPool.length) - tech.giveTech(gunTechPool[index]) // choose from the gun pool - simulation.makeTextLog(`tech.giveTech("${tech.tech[gunTechPool[index]].name}")`) - } - } - simulation.boldActiveGunHUD(); - }, - remove() {} - }, - { - name: "logistics", - description: `${powerUps.orb.ammo()} give 80% more ammo, but
it's only added to your current gun`, - maxCount: 1, - count: 0, - frequency: 1, - frequencyDefault: 1, - allowed() { - return !tech.isEnergyNoAmmo - }, - requires: "not exciton", - effect() { - tech.isAmmoForGun = true; - }, - remove() { - tech.isAmmoForGun = false; - } - }, - { - name: "supply chain", - junk: 0.05, - descriptionFunction() { return `double your current ammo for all guns
+${this.junk*100}% JUNK to the potential tech pool` }, - maxCount: 9, - count: 0, - frequency: 1, - frequencyDefault: 1, - allowed() { return true }, - requires: "", - effect() { - for (let i = 0; i < b.guns.length; i++) { - if (b.guns[i].have) b.guns[i].ammo = Math.floor(2 * b.guns[i].ammo) - } - simulation.makeGunHUD(); - this.refundAmount += tech.addJunkTechToPool(this.junk) - }, - refundAmount: 0, - remove() { - for (let j = 0; j < this.count; j++) { - for (let i = 0; i < b.guns.length; i++) { - if (b.guns[i].have) b.guns[i].ammo = Math.floor(0.5 * b.guns[i].ammo) - } - } - simulation.makeGunHUD(); - if (this.count > 0 && this.refundAmount > 0) { - tech.removeJunkTechFromPool(this.refundAmount) - this.refundAmount = 0 - } - } - }, - { - name: "cache", - link: `cache`, - description: `${powerUps.orb.ammo()} give 13x more ammo, but
you can't store any more ammo than that`, - maxCount: 1, - count: 0, - frequency: 1, - frequencyDefault: 1, - allowed() { - return !tech.isEnergyNoAmmo - }, - requires: "not exciton", - effect() { - tech.ammoCap = 13; - powerUps.ammo.effect() - }, - remove() { - tech.ammoCap = 0; - } - }, - { - name: "catabolism", - description: `firing while out of ammo spawns ${powerUps.orb.ammo(4)}
but it reduces your maximum health by 1`, - maxCount: 1, - count: 0, - frequency: 1, - frequencyDefault: 1, - allowed() { - return !tech.isEnergyNoAmmo && !tech.isEnergyHealth - }, - requires: "not exciton, mass-energy", - effect: () => { - tech.isAmmoFromHealth = true; - }, - remove() { - tech.isAmmoFromHealth = false; - } - }, - { - name: "desublimated ammunition", - link: `desublimated ammunition`, - description: `produce bullets from air molecules
every other crouched shot uses no ammo`, - maxCount: 1, - count: 0, - frequency: 1, - frequencyDefault: 1, - allowed() { - return build.isExperimentSelection - }, - requires: "", - effect() { - tech.isCrouchAmmo = true - }, - remove() { - tech.isCrouchAmmo = false; - } - }, - { - name: "gun turret", - description: "reduce harm by 60% when crouching", - maxCount: 1, - count: 0, - frequency: 3, - frequencyDefault: 3, - allowed() { - return (tech.isCrouchAmmo && !tech.isEnergyHealth) || tech.isCrouchRegen - }, - requires: "inductive coupling, desublimated ammunition, not mass-energy", - effect() { - tech.isTurret = true - }, - remove() { - tech.isTurret = false; - } - }, - { - name: "dead reckoning", - description: "increase damage by 36% when at rest", - maxCount: 9, - count: 0, - frequency: 1, - frequencyDefault: 1, - allowed() { return true }, - requires: "", - effect: () => { - tech.restDamage += 0.36 - }, - remove() { - tech.restDamage = 1; - } - }, - { - name: "Higgs mechanism", - description: "while firing your position is locked
50% decreased delay after firing", - maxCount: 1, - count: 0, - frequency: 1, - frequencyDefault: 1, - allowed() { - return !m.isShipMode && !tech.isAlwaysFire - }, - requires: "not ship mode, not automatic", - effect: () => { - tech.isFireMoveLock = true; - b.setFireCD(); - b.setFireMethod(); - }, - remove() { - if (tech.isFireMoveLock) { - tech.isFireMoveLock = false - b.setFireCD(); - b.setFireMethod(); - } - } - }, - { - name: "squirrel-cage rotor", - description: "move and jump 30% faster
take 5% more harm", - maxCount: 9, - count: 0, - frequency: 1, - frequencyDefault: 1, - allowed() { return true }, - requires: "", - effect() { // good with melee builds, content skipping builds - tech.squirrelFx += 0.25; - tech.squirrelJump += 0.1; - m.setMovement() - }, - remove() { - tech.squirrelFx = 1; - tech.squirrelJump = 1; - m.setMovement() - } - }, - { - name: "Newton's 1st law", - description: "moving at high speeds
reduces harm by up to 66%", - maxCount: 1, - count: 0, - frequency: 1, - frequencyDefault: 1, - allowed() { - return !tech.isEnergyHealth - }, - requires: "not mass-energy equivalence", - effect() { - tech.isSpeedHarm = true //max at speed = 40 - }, - remove() { - tech.isSpeedHarm = false - } - }, - { - name: "Newton's 2nd law", - description: "moving at high speeds
increases damage by up to 66%", - maxCount: 1, - count: 0, - frequency: 1, - frequencyDefault: 1, - allowed() { return true }, - requires: "", - effect() { - tech.isSpeedDamage = true //max at speed = 40 - }, - remove() { - tech.isSpeedDamage = false - } - }, - { - name: "kinetic bombardment", - description: "increase damage by up to 33% at a distance
of up to 50 player widths from the target", - maxCount: 1, - count: 0, - frequency: 1, - frequencyDefault: 1, - allowed() { return true }, - requires: "", - effect() { - tech.isFarAwayDmg = true; //used in mob.damage() - }, - remove() { - tech.isFarAwayDmg = false; - } - }, - { - name: "simulated annealing", - description: "increase damage by 20%
20% increased delay after firing", - maxCount: 1, - count: 0, - frequency: 1, - frequencyDefault: 1, - allowed() { return true }, - effect() { - tech.slowFire = 1.2 - b.setFireCD(); - }, - remove() { - tech.slowFire = 1; - b.setFireCD(); - } - }, - { - name: "heuristics", - description: "30% decreased delay after firing", - maxCount: 9, - count: 0, - frequency: 1, - frequencyDefault: 1, - allowed() { return true }, - requires: "", - effect() { - tech.fireRate *= 0.7 - b.setFireCD(); - }, - remove() { - tech.fireRate = 1; - b.setFireCD(); - } - }, - - { - name: "fracture analysis", - description: "bullet impacts do 400% damage
to stunned mobs", - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return tech.isStunField || tech.oneSuperBall || tech.isCloakStun || tech.orbitBotCount > 1 || tech.isExplosionStun || tech.isMineStun - }, - requires: "a stun effect", - effect() { - tech.isCrit = true; - }, - remove() { - tech.isCrit = false; - } - }, - { - name: "microstates", - link: `microstates`, - description: "increase damage by 7%
for every 10 active projectiles", - maxCount: 1, - count: 0, - frequency: 1, - frequencyDefault: 1, - allowed() { return true }, - requires: "", - effect() { - tech.isDamageFromBulletCount = true - }, - remove() { - tech.isDamageFromBulletCount = false - } - }, - { - name: "anti-shear topology", - link: `anti-shear topology`, - description: "some projectiles last 30% longer
drone, spore, missile, foam, wave, neutron, ice", - - // isGunTech: true, - maxCount: 3, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return m.fieldUpgrades[m.fieldMode].name === "molecular assembler" || tech.haveGunCheck("spores") || tech.haveGunCheck("drones") || tech.haveGunCheck("missiles") || tech.haveGunCheck("foam") || tech.haveGunCheck("matter wave") || tech.isNeutronBomb || tech.isIceField || tech.isIceShot || tech.relayIce || tech.isNeedleIce || tech.blockingIce > 1 || tech.isWormShot || tech.foamBotCount > 1 - }, - requires: "drones, spores, missiles, foam, matter wave, neutron bomb, ice IX", - effect() { - tech.isBulletsLastLonger += 0.3 - }, - remove() { - tech.isBulletsLastLonger = 1; - } - }, - { - name: "radioactive contamination", - description: "after a mob or shield dies,
leftover radiation spreads to a nearby mob", - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return tech.isNailRadiation || tech.isWormholeDamage || tech.isNeutronBomb || tech.isExplodeRadio - }, - requires: "radiation damage source", - effect() { - tech.isRadioactive = true - }, - remove() { - tech.isRadioactive = false - } - }, - { - name: "water shielding", - link: `water shielding`, - description: "radioactive effects on you are reduced by 75%
neutron bomb, drones, explosions, slime", - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return tech.isNeutronBomb || tech.isDroneRadioactive || tech.isExplodeRadio - }, - requires: "neutron bomb or irradiated drones or iridium-192", - effect() { - tech.isRadioactiveResistance = true - }, - remove() { - tech.isRadioactiveResistance = false - } - }, - { - name: "iridium-192", - description: "explosions release gamma radiation
100% more damage, but over 4 seconds", - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return tech.explosiveRadius === 1 && !tech.isSmallExplosion && (tech.haveGunCheck("missiles") || tech.isIncendiary || (tech.haveGunCheck("grenades") && !tech.isNeutronBomb) || tech.isPulseLaser || tech.isMissileField || tech.boomBotCount > 1 || tech.isTokamak) - }, - requires: "an explosive damage source, not ammonium nitrate or nitroglycerin", - effect: () => { - tech.isExplodeRadio = true; //iridium-192 - }, - remove() { - tech.isExplodeRadio = false; - } - }, - { - name: "ammonium nitrate", - description: "increase explosive damage by 30%
increase explosive radius by 30%", - maxCount: 9, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return !tech.isExplodeRadio && tech.hasExplosiveDamageCheck() - }, - requires: "an explosive damage source, not iridium-192", - effect: () => { - tech.explosiveRadius += 0.3; - }, - remove() { - tech.explosiveRadius = 1; - } - }, - { - name: "nitroglycerin", - description: "increase explosive damage by 66%
decrease explosive radius by 33%", - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return !tech.isExplodeRadio && tech.hasExplosiveDamageCheck() - }, - requires: "an explosive damage source, not iridium-192", - effect: () => { - tech.isSmallExplosion = true; - }, - remove() { - tech.isSmallExplosion = false; - } - }, - { - name: "acetone peroxide", - description: "increase explosive radius by 80%, but
you take 300% more harm from explosions", - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - isBadRandomOption: true, - allowed() { - return tech.hasExplosiveDamageCheck() - }, - requires: "an explosive damage source", - effect: () => { - tech.isExplosionHarm = true; - }, - remove() { - tech.isExplosionHarm = false; - } - }, - { - name: "shock wave", - description: "explosions stun mobs for 1-2 seconds
decrease explosive damage by 30%", - maxCount: 1, - count: 0, - frequency: 1, - frequencyDefault: 1, - allowed() { - return !tech.isExplodeRadio && tech.hasExplosiveDamageCheck() - }, - requires: "an explosive damage source, not iridium-192", - effect() { - tech.isExplosionStun = true; - }, - remove() { - tech.isExplosionStun = false; - } - }, - { - name: "controlled explosion", - description: `use ${powerUps.orb.research(3)} to dynamically reduce all
explosions until they do no harm`, - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return !tech.isImmuneExplosion && (build.isExperimentSelection || powerUps.research.count > 2) && (tech.haveGunCheck("missiles") || tech.isMissileField || tech.missileBotCount > 0 || tech.isIncendiary || tech.isPulseLaser || tech.isTokamak || (tech.haveGunCheck("grenades") && !tech.isNeutronBomb)) - }, - requires: "an explosive damage source, not electric reactive armor", - effect: () => { - tech.isSmartRadius = true; - for (let i = 0; i < 3; i++) { - if (powerUps.research.count > 0) powerUps.research.changeRerolls(-1) - } - }, - remove() { - tech.isSmartRadius = false; - if (this.count > 0) powerUps.research.changeRerolls(3) - } - }, - { - name: "electric reactive armor", - // description: "explosions do no harm
while your energy is above 98%", - description: "harm from explosions is passively reduced
by 5% for every 10 stored energy", - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return !tech.isSmartRadius && !tech.isExplodeRadio && tech.hasExplosiveDamageCheck() - }, - requires: "an explosive damage source, not iridium-192", - effect: () => { - tech.isImmuneExplosion = true; - }, - remove() { - tech.isImmuneExplosion = false; - } - }, - { - name: "incendiary ammunition", - description: "shotgun, super balls, and drones
are loaded with explosives", - maxCount: 1, - count: 0, - frequency: 1, - frequencyDefault: 1, - allowed() { - return ((m.fieldUpgrades[m.fieldMode].name === "molecular assembler" && !(tech.isDroneTeleport || tech.isDroneRadioactive || tech.isSporeField || tech.isMissileField || tech.isIceField)) || (tech.haveGunCheck("drones") && !tech.isForeverDrones && !tech.isDroneRadioactive && !tech.isDroneTeleport) || tech.haveGunCheck("super balls") || tech.haveGunCheck("shotgun")) && !tech.isNailShot && !tech.isIceShot && !tech.isFoamShot && !tech.isWormShot && !tech.isNeedleShot - }, - requires: "super balls, basic or slug shotgun, drones, not irradiated drones or burst drones", - effect() { - tech.isIncendiary = true - }, - remove() { - tech.isIncendiary = false; - } - }, - { - name: "fragmentation", - description: "some detonations and collisions eject nails
blocks, grenades, missiles, slugs, harpoon", - maxCount: 9, - count: 0, - frequency: 1, - frequencyDefault: 1, - allowed() { - return tech.haveGunCheck("harpoon") || (tech.haveGunCheck("grenades") && !tech.isNeutronBomb) || tech.haveGunCheck("missiles") || tech.missileBotCount || (tech.haveGunCheck("shotgun") && tech.isSlugShot) || tech.blockDamage > 0.075 - }, - requires: "grenades, missiles, shotgun slugs, harpoon, or mass driver", - effect() { - tech.fragments++ - }, - remove() { - tech.fragments = 0 - } - }, - { - name: "thermal runaway", - description: "mobs explode when they die", - maxCount: 1, - count: 0, - frequency: 1, - frequencyDefault: 1, - allowed() { - return !tech.sporesOnDeath && !tech.nailsDeathMob && !tech.botSpawner && !tech.isMobBlockFling && !tech.iceIXOnDeath - }, - requires: "no other mob death tech", - effect: () => { - tech.isExplodeMob = true; - }, - remove() { - tech.isExplodeMob = false; - } - }, - { - name: "shear stress", - description: "mobs release a nail when they die
nails target nearby mobs", - maxCount: 9, - count: 0, - frequency: 1, - frequencyDefault: 1, - allowed() { - return !tech.sporesOnDeath && !tech.isExplodeMob && !tech.botSpawner && !tech.isMobBlockFling && !tech.iceIXOnDeath - }, - requires: "no other mob death tech", - effect: () => { - tech.nailsDeathMob++ - }, - remove() { - tech.nailsDeathMob = 0; - } - }, - { - name: "zoospore vector", - link: `zoospore vector`, - description: "mobs produce spores when they die
11% chance", - maxCount: 9, - count: 0, - frequency: 1, - frequencyDefault: 1, - allowed() { - return !tech.nailsDeathMob && !tech.isExplodeMob && !tech.botSpawner && !tech.isMobBlockFling && !tech.iceIXOnDeath - }, - requires: "no other mob death tech", - effect() { - tech.sporesOnDeath += 0.11; - // if (tech.isSporeWorm) { - // for (let i = 0; i < 4; i++) b.worm(m.pos) - // } else { - // for (let i = 0; i < 8; i++) b.spore(m.pos) - // } - }, - remove() { - tech.sporesOnDeath = 0; - } - }, - { - name: "reaction inhibitor", - description: "mobs spawn with 11% less health", - maxCount: 3, - count: 0, - frequency: 1, - frequencyDefault: 1, - allowed() { - return tech.nailsDeathMob || tech.sporesOnDeath || tech.isExplodeMob || tech.botSpawner || tech.isMobBlockFling || tech.iceIXOnDeath - }, - requires: "any mob death tech", - effect: () => { - tech.mobSpawnWithHealth *= 0.89 - - //set all mobs at full health to 0.85 - for (let i = 0; i < mob.length; i++) { - if (mob.health > tech.mobSpawnWithHealth) mob.health = tech.mobSpawnWithHealth - } - }, - remove() { - tech.mobSpawnWithHealth = 1; - } - }, - { - name: "decorrelation", - description: "reduce harm by 70% after not activating
your gun or field for 2 seconds", - maxCount: 1, - count: 0, - frequency: 1, - frequencyDefault: 1, - allowed() { - return !tech.isEnergyHealth //((m.fieldUpgrades[m.fieldMode].name === "standing wave" && (tech.blockingIce !== 0 || tech.blockDmg !== 0)) || b.totalBots() > 1 || tech.haveGunCheck("mine") || tech.haveGunCheck("spores") || m.fieldUpgrades[m.fieldMode].name === "molecular assembler") && - }, - requires: "not mass-energy", - effect() { - tech.isNoFireDefense = true - }, - remove() { - tech.isNoFireDefense = false - } - }, - { - name: "anticorrelation", - description: "increase damage by 100%
after not using your gun or field for 2 seconds", - maxCount: 1, - count: 0, - frequency: 3, - frequencyDefault: 3, - allowed() { - return tech.isNoFireDefense - }, - requires: "decorrelation", - effect() { - tech.isNoFireDamage = true - }, - remove() { - tech.isNoFireDamage = false - } - }, - { - name: "scrap bots", - link: `scrap bots`, - description: "33% chance after killing a mob to build
a scrap bot that operates for 14 seconds", - maxCount: 3, - count: 0, - frequency: 1, - frequencyDefault: 1, - isBotTech: true, - allowed() { - return !tech.sporesOnDeath && !tech.nailsDeathMob && !tech.isExplodeMob && !tech.isMobBlockFling && !tech.iceIXOnDeath - }, - requires: "no other mob death tech", - effect() { - tech.botSpawner += 0.33; - }, - remove() { - tech.botSpawner = 0; - } - }, - { - name: "scrap refit", - link: `scrap refit`, - description: "killing a mob resets your functional scrap bots
to 14 seconds of operation", - maxCount: 1, - count: 0, - frequency: 3, - frequencyDefault: 3, - isBotTech: true, - allowed() { - return tech.botSpawner - }, - requires: "scrap bots", - effect() { - tech.isBotSpawnerReset = true; - }, - remove() { - tech.isBotSpawnerReset = false; - } - }, - { - name: "nail-bot", - link: `nail-bot`, - description: "a bot fires nails at mobs in line of sight", - maxCount: 9, - count: 0, - frequency: 1, - frequencyDefault: 1, - isBot: true, - isBotTech: true, - allowed() { return true }, - requires: "", - effect() { - tech.nailBotCount++; - b.nailBot(); - }, - remove() { - if (this.count) { - tech.nailBotCount -= this.count; - b.clearPermanentBots(); - b.respawnBots(); - } - } - }, - { - name: "nail-bot upgrade", - link: `nail-bot upgrade`, - description: "convert your current bots to nail-bots
+500% fire rate and +40% nail velocity", - maxCount: 1, - count: 0, - frequency: 3, - frequencyDefault: 3, - isBotTech: true, - allowed() { - return tech.nailBotCount > 1 && !b.hasBotUpgrade() - }, - requires: "2 or more nail bots and no other bot upgrade", - effect() { - tech.isNailBotUpgrade = true - b.convertBotsTo("nail-bot") - for (let i = 0; i < bullet.length; i++) { - if (bullet[i].botType === 'nail') bullet[i].isUpgraded = true - } - tech.setBotTechFrequency() - tech.setTechFrequency("nail-bot", 5) - }, - remove() { - if (this.count) { - for (let i = 0; i < bullet.length; i++) { - if (bullet[i].botType === 'nail') bullet[i].isUpgraded = false - } - tech.setBotTechFrequency(1) - } - tech.isNailBotUpgrade = false - } - }, - { - name: "foam-bot", - link: `foam-bot`, - description: "a bot fires foam at nearby mobs", - maxCount: 9, - count: 0, - frequency: 1, - frequencyDefault: 1, - isBot: true, - isBotTech: true, - allowed() { return true }, - requires: "", - effect() { - tech.foamBotCount++; - b.foamBot(); - }, - remove() { - if (this.count) { - tech.foamBotCount -= this.count; - b.clearPermanentBots(); - b.respawnBots(); - } - } - }, - { - name: "foam-bot upgrade", - link: `foam-bot upgrade`, - description: "convert your current bots to foam-bots
300% increased foam size and fire rate", - maxCount: 1, - count: 0, - frequency: 3, - frequencyDefault: 3, - isBotTech: true, - allowed() { - return tech.foamBotCount > 1 && !b.hasBotUpgrade() - }, - requires: "2 or more foam bots and no other bot upgrade", - effect() { - tech.isFoamBotUpgrade = true - b.convertBotsTo("foam-bot") - for (let i = 0; i < bullet.length; i++) { - if (bullet[i].botType === 'foam') bullet[i].isUpgraded = true - } - tech.setBotTechFrequency() - tech.setTechFrequency("foam-bot", 5) - }, - remove() { - if (this.count) { - for (let i = 0; i < bullet.length; i++) { - if (bullet[i].botType === 'foam') bullet[i].isUpgraded = false - } - tech.setBotTechFrequency(1) - } - tech.isFoamBotUpgrade = false - } - }, - { - name: "boom-bot", - link: `boom-bot`, - description: "a bot defends the space around you
ignites an explosion after hitting a mob", - maxCount: 9, - count: 0, - frequency: 1, - frequencyDefault: 1, - isBot: true, - isBotTech: true, - allowed() { return true }, - requires: "", - effect() { - tech.boomBotCount++; - b.boomBot(); - }, - remove() { - if (this.count) { - tech.boomBotCount -= this.count; - b.clearPermanentBots(); - b.respawnBots(); - } - } - }, - { - name: "boom-bot upgrade", - link: `boom-bot upgrade`, - description: "convert your current bots to boom-bots
300% increased explosion damage and size", - maxCount: 1, - count: 0, - frequency: 3, - frequencyDefault: 3, - isBotTech: true, - allowed() { - return tech.boomBotCount > 1 && !b.hasBotUpgrade() - }, - requires: "2 or more boom bots and no other bot upgrade", - effect() { - tech.isBoomBotUpgrade = true - b.convertBotsTo("boom-bot") - for (let i = 0; i < bullet.length; i++) { - if (bullet[i].botType === 'boom') bullet[i].isUpgraded = true - } - tech.setBotTechFrequency() - tech.setTechFrequency("boom-bot", 5) - }, - remove() { - if (this.count) { - for (let i = 0; i < bullet.length; i++) { - if (bullet[i].botType === 'boom') bullet[i].isUpgraded = false - } - tech.setBotTechFrequency(1) - } - tech.isBoomBotUpgrade = false - } - }, - { - name: "laser-bot", - link: `laser-bot`, - description: "a bot uses energy to emit a laser beam
that targets nearby mobs", - maxCount: 9, - count: 0, - frequency: 1, - frequencyDefault: 1, - isBot: true, - isBotTech: true, - allowed() { - return m.maxEnergy > 0.5 - }, - requires: "maximum energy above 50", - effect() { - tech.laserBotCount++; - b.laserBot(); - }, - remove() { - if (this.count) { - tech.laserBotCount -= this.count; - b.clearPermanentBots(); - b.respawnBots(); - } - } - }, - { - name: "laser-bot upgrade", - link: `laser-bot upgrade`, - description: "convert your current bots to laser-bots
100% improved damage, efficiency, and range", // 400% increased laser-bot laser damage", - maxCount: 1, - count: 0, - frequency: 3, - frequencyDefault: 3, - isBotTech: true, - allowed() { - return tech.laserBotCount > 1 && !b.hasBotUpgrade() - }, - requires: "2 or more laser bots and no other bot upgrade", - effect() { - tech.isLaserBotUpgrade = true - b.convertBotsTo("laser-bot") - for (let i = 0; i < bullet.length; i++) { - if (bullet[i].botType === 'laser') bullet[i].isUpgraded = true - } - tech.setBotTechFrequency() - tech.setTechFrequency("laser-bot", 5) - }, - remove() { - if (this.count) { - for (let i = 0; i < bullet.length; i++) { - if (bullet[i].botType === 'laser') bullet[i].isUpgraded = false - } - tech.setBotTechFrequency(1) - } - tech.isLaserBotUpgrade = false - } - }, - { - name: "orbital-bot", - link: `orbital-bot`, - description: "a bot is locked in orbit around you
stuns and damages mobs on contact", - maxCount: 9, - count: 0, - frequency: 1, - frequencyDefault: 1, - isBot: true, - isBotTech: true, - allowed() { return true }, - requires: "", - effect() { - b.orbitBot(); - tech.orbitBotCount++; - }, - remove() { - if (this.count) { - tech.orbitBotCount -= this.count; - b.clearPermanentBots(); - b.respawnBots(); - } - } - }, - { - name: "orbital-bot upgrade", - link: `orbital-bot upgrade`, - description: "convert your current bots to orbital-bots
increase damage by 300% and radius by 50%", - maxCount: 1, - count: 0, - frequency: 3, - frequencyDefault: 3, - isBotTech: true, - allowed() { - return tech.orbitBotCount > 1 && !b.hasBotUpgrade() - }, - requires: "2 or more orbital bots and no other bot upgrade", - effect() { - tech.isOrbitBotUpgrade = true - b.convertBotsTo("orbital-bot") - const range = 190 + 120 * tech.isOrbitBotUpgrade - for (let i = 0; i < bullet.length; i++) { - if (bullet[i].botType === 'orbit') { - bullet[i].isUpgraded = true - bullet[i].range = range - bullet[i].orbitalSpeed = Math.sqrt(0.25 / range) - } - } - tech.setBotTechFrequency() - tech.setTechFrequency("orbital-bot", 5) - }, - remove() { - if (this.count) { - const range = 190 + 100 * tech.isOrbitBotUpgrade - for (let i = 0; i < bullet.length; i++) { - if (bullet[i].botType === 'orbit') { - bullet[i].range = range - bullet[i].orbitalSpeed = Math.sqrt(0.25 / range) - } - } - tech.setBotTechFrequency(1) - } - tech.isOrbitBotUpgrade = false - } - }, - { - name: "dynamo-bot", - link: `dynamo-bot`, - description: "a bot damages mobs while it traces your path
regen 6 energy per second when it's near", - maxCount: 9, - count: 0, - frequency: 1, - frequencyDefault: 1, - isBot: true, - isBotTech: true, - allowed() { return true }, - requires: "", - effect() { - tech.dynamoBotCount++; - b.dynamoBot(); - }, - remove() { - if (this.count) { - tech.dynamoBotCount -= this.count; - b.clearPermanentBots(); - b.respawnBots(); - } - } - }, - { - name: "dynamo-bot upgrade", - link: `dynamo-bot upgrade`, - description: "convert your current bots to dynamo-bots
increase regen to 20 energy per second", - maxCount: 1, - count: 0, - frequency: 3, - frequencyDefault: 3, - isBotTech: true, - allowed() { - return tech.dynamoBotCount > 1 && !b.hasBotUpgrade() - }, - requires: "2 or more dynamo bots and no other bot upgrade", - effect() { - tech.isDynamoBotUpgrade = true - b.convertBotsTo("dynamo-bot") - for (let i = 0; i < bullet.length; i++) { - if (bullet[i].botType === 'dynamo') bullet[i].isUpgraded = true - } - tech.setBotTechFrequency() - tech.setTechFrequency("dynamo-bot", 5) - }, - remove() { - if (this.count) { - for (let i = 0; i < bullet.length; i++) { - if (bullet[i].botType === 'dynamo') bullet[i].isUpgraded = false - } - tech.setBotTechFrequency(1) - } - tech.isDynamoBotUpgrade = false - } - }, - { - name: "bot fabrication", - link: `bot fabrication`, - descriptionFunction() { - return `if you collect ${powerUps.orb.research(2 + Math.floor(0.2 * b.totalBots()))}use them to build a
random bot (+1 cost every 5 bots)` - }, - // description: `if you collect ${powerUps.orb.research(2)}use them to build a
random bot (+1 cost every 5 bots)`, - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - isBotTech: true, - allowed() { - return powerUps.research.count > 1 || build.isExperimentSelection - }, - requires: "at least 2 research", - effect() { - tech.isRerollBots = true; - powerUps.research.changeRerolls(0) - simulation.makeTextLog(`m.research = 0`) - }, - remove() { - tech.isRerollBots = false; - // this.description = `if you collect ${powerUps.orb.research(2 + Math.floor(0.2 * b.totalBots()))}use them to build a
random bot (+1 cost every 5 bots)` - } - }, - { - name: "robotics", - description: `spawn 2 random bots
quadruple the frequency of finding bot tech`, - maxCount: 1, - count: 0, - frequency: 1, - frequencyDefault: 1, - isBotTech: true, - allowed() { - return b.totalBots() > 1 || build.isExperimentSelection - }, - requires: "at least 2 bots", - effect: () => { - b.randomBot() - b.randomBot() - for (let i = 0, len = tech.tech.length; i < len; i++) { - if (tech.tech[i].isBotTech) tech.tech[i].frequency *= 4 - } - }, - remove() { - if (this.count > 0) { - b.removeBot() - b.removeBot() - b.clearPermanentBots(); - b.respawnBots(); - for (let i = 0, len = tech.tech.length; i < len; i++) { - if (tech.tech[i].isBotTech) tech.tech[i].frequency = Math.ceil(tech.tech[i].frequency / 4) - } - } - } - }, - { - name: "perimeter defense", - description: "reduce harm by 8%
for each of your permanent bots", - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - isBotTech: true, - allowed() { - return b.totalBots() > 2 && !tech.isEnergyHealth - }, - requires: "at least 3 bots", - effect() { - tech.isBotArmor = true - }, - remove() { - tech.isBotArmor = false - } - }, - { - name: "network effect", - description: "increase damage by 7%
for each of your permanent bots", - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - isBotTech: true, - allowed() { - return b.totalBots() > 2 - }, - requires: "at least 3 bots", - effect() { - tech.isBotDamage = true - }, - remove() { - tech.isBotDamage = false - } - }, - { - name: "ersatz bots", - link: `ersatz bots`, - description: "double your current permanent bots
remove all guns in your inventory", - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - isBotTech: true, - // isNonRefundable: true, - isBadRandomOption: true, - numberOfGunsLost: 0, - allowed() { - return b.totalBots() > 3 && !build.isExperimentSelection - }, - requires: "NOT EXPERIMENT MODE, at least 4 bots", - effect() { - this.numberOfGunsLost = b.inventory.length - b.removeAllGuns(); - simulation.makeGunHUD(); - //double bots - for (let i = 0; i < tech.nailBotCount; i++) b.nailBot(); - tech.nailBotCount *= 2 - for (let i = 0; i < tech.laserBotCount; i++) b.laserBot(); - tech.laserBotCount *= 2 - for (let i = 0; i < tech.foamBotCount; i++) b.foamBot(); - tech.foamBotCount *= 2 - for (let i = 0; i < tech.boomBotCount; i++) b.boomBot(); - tech.boomBotCount *= 2 - for (let i = 0; i < tech.orbitBotCount; i++) b.orbitBot(); - tech.orbitBotCount *= 2 - for (let i = 0; i < tech.dynamoBotCount; i++) b.dynamoBot(); - tech.dynamoBotCount *= 2 - for (let i = 0; i < tech.plasmaBotCount; i++) b.plasmaBot(); - tech.plasmaBotCount *= 2 - for (let i = 0; i < tech.missileBotCount; i++) b.missileBot(); - tech.missileBotCount *= 2 - }, - remove() { - if (this.count) { - //return guns - for (let i = 0; i < this.numberOfGunsLost; i++) powerUps.spawn(m.pos.x + 60 * (Math.random() - 0.5), m.pos.y + 60 * (Math.random() - 0.5), "gun"); - this.numberOfGunsLost = 0; - - //half all current guns - tech.nailBotCount = Math.round(tech.nailBotCount / 2) - tech.laserBotCount = Math.round(tech.laserBotCount / 2) - tech.foamBotCount = Math.round(tech.foamBotCount / 2) - tech.boomBotCount = Math.round(tech.boomBotCount / 2) - tech.orbitBotCount = Math.round(tech.orbitBotCount / 2) - tech.dynamoBotCount = Math.round(tech.dynamoBotCount / 2) - tech.plasmaBotCount = Math.round(tech.plasmaBotCount / 2) - tech.missileBotCount = Math.round(tech.missileBotCount / 2) - b.clearPermanentBots(); - b.respawnBots(); - } - } - }, - { - name: "capacitor bank", - // description: "charge effects build up almost instantly
throwing blocks, foam, railgun, pulse, tokamak", - descriptionFunction() { return `charge effects build up almost instantly
throwing blocks, ${tech.haveGunCheck("foam", false)?"foam" :"foam"}, ${tech.isRailGun?"railgun" :"railgun"}, ${tech.isPulseLaser?"pulse" :"pulse"}, ${tech.isTokamak?"tokamak" :"tokamak"}` }, - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return tech.blockDamage > 0.075 || tech.haveGunCheck("foam") || tech.isRailGun || tech.isTokamak || tech.isPulseLaser - }, - requires: "throwing blocks, foam, railgun, pulse, tokamak", - effect() { - tech.isCapacitor = true; - }, - remove() { - tech.isCapacitor = false; - } - }, - { - name: "mass driver", - description: "increase block collision damage by 300%", - maxCount: 1, - count: 0, - frequency: 1, - frequencyDefault: 1, - allowed() { - return m.fieldUpgrades[m.fieldMode].name !== "wormhole" - }, - requires: "not wormhole", - effect() { - tech.blockDamage = 0.3 - }, - remove() { - tech.blockDamage = 0.075 - } - }, - { - name: "inflation", - link: `inflation`, - description: "throwing a block expands it by 300%
holding a block reduces harm by 85%", - maxCount: 1, - count: 0, - frequency: 3, - frequencyDefault: 3, - allowed() { - return tech.blockDamage > 0.075 && m.fieldUpgrades[m.fieldMode].name !== "pilot wave" && m.fieldUpgrades[m.fieldMode].name !== "wormhole" && !tech.isTokamak - }, - requires: "mass driver, not pilot wave, tokamak, wormhole", - effect() { - tech.isAddBlockMass = true - }, - remove() { - tech.isAddBlockMass = false - } - }, - { - name: "restitution", - description: "throwing a block makes it very bouncy
increase block collision damage by 150%", - maxCount: 1, - count: 0, - frequency: 3, - frequencyDefault: 3, - allowed() { - return tech.blockDamage > 0.075 && m.fieldUpgrades[m.fieldMode].name !== "pilot wave" && m.fieldUpgrades[m.fieldMode].name !== "wormhole" && !tech.isTokamak - }, - requires: "mass driver, not pilot wave not tokamak, wormhole", - effect() { - tech.isBlockRestitution = true - }, - remove() { - tech.isBlockRestitution = false - } - }, - { - name: "flywheel", - description: "after a mob dies its block is flung at mobs
increase block collision damage by 150%", - maxCount: 1, - count: 0, - frequency: 3, - frequencyDefault: 3, - allowed() { - return tech.blockDamage > 0.075 && !tech.nailsDeathMob && !tech.sporesOnDeath && !tech.isExplodeMob && !tech.botSpawner && !tech.iceIXOnDeath - }, - requires: "mass driver, no other mob death tech", - effect() { - tech.isMobBlockFling = true - }, - remove() { - tech.isMobBlockFling = false - } - }, - // { - // name: "fermions", - // description: "blocks thrown by you or pilot wave will
collide with intangible mobs, but not you", - // maxCount: 1, - // count: 0, - // frequency: 2, - // frequencyDefault: 2, - // allowed() { - // return (tech.blockDamage > 0.075 || m.fieldUpgrades[m.fieldMode].name === "pilot wave") && !tech.isTokamak - // }, - // requires: "mass driver or pilot wave, not tokamak", - // effect() { - // tech.isBlockBullets = true - // }, - // remove() { - // tech.isBlockBullets = false - // } - // }, - // { - // name: "inelastic collision", - // description: "holding a block reduces harm by 85%
increase block collision damage by 150%", - // maxCount: 1, - // count: 0, - // frequency: 3, - // frequencyDefault: 3, - // allowed() { - // return tech.blockDamage > 0.075 && m.fieldUpgrades[m.fieldMode].name !== "pilot wave" && m.fieldUpgrades[m.fieldMode].name !== "wormhole" && !tech.isEnergyHealth - // }, - // requires: "mass driver, a field that can hold things, not mass-energy", - // effect() { - // tech.isBlockHarm = true - // }, - // remove() { - // tech.isBlockHarm = false - // } - // }, - { - name: "buckling", - description: `if a block you threw kills a mob
spawn 1 ${powerUps.orb.heal()}, ${powerUps.orb.ammo()}, or ${powerUps.orb.research(1)}`, - maxCount: 1, - count: 0, - frequency: 3, - frequencyDefault: 3, - allowed() { - return tech.blockDamage > 0.075 && m.fieldUpgrades[m.fieldMode].name !== "pilot wave" && !tech.isTokamak - }, - requires: "mass driver, not pilot wave, tokamak", - effect() { - tech.isBlockPowerUps = true - }, - remove() { - tech.isBlockPowerUps = false - } - }, - { - name: "Pauli exclusion", - description: `after receiving harm from a collision become
immune to harm for 1 extra second`, - maxCount: 9, - count: 0, - frequency: 1, - frequencyDefault: 1, - allowed() { return true }, - requires: "", - effect() { - tech.collisionImmuneCycles += 60; - if (m.immuneCycle < m.cycle + tech.collisionImmuneCycles) m.immuneCycle = m.cycle + tech.collisionImmuneCycles; //player is immune to damage for 30 cycles - }, - remove() { - tech.collisionImmuneCycles = 30; - } - }, - { - name: "complex spin-statistics", - description: `become immune to harm for 1.5 seconds
once every 7 seconds`, - maxCount: 3, - count: 0, - frequency: 1, - frequencyDefault: 1, - allowed() { - return true //tech.collisionImmuneCycles > 30 - }, - requires: "", - effect() { - tech.cyclicImmunity += 90; - }, - remove() { - tech.cyclicImmunity = 0; - } - }, - { - name: "NOR gate", - description: "if flip-flop is in the ON state
take 0 harm from collisions with mobs", - maxCount: 1, - count: 0, - frequency: 4, - frequencyDefault: 4, - allowed() { - return tech.isFlipFlop - }, - requires: "flip-flop", - effect() { - tech.isFlipFlopHarm = true //do you have this tech - }, - remove() { - tech.isFlipFlopHarm = false - } - }, - { - name: "flip-flop", - link: `flip-flop`, - description: `toggle ON and OFF after a collision
unlock advanced tech that runs if ON`, - nameInfo: "", - addNameInfo() { - setTimeout(function() { - if (document.getElementById("tech-flip-flop")) { - if (tech.isFlipFlopOn) { - document.getElementById("tech-flip-flop").innerHTML = ` = ON` - m.eyeFillColor = m.fieldMeterColor //'#5af' - } else { - document.getElementById("tech-flip-flop").innerHTML = ` = OFF` - m.eyeFillColor = "transparent" - } - } - }, 100); - }, - maxCount: 1, - count: 0, - frequency: 1, - frequencyDefault: 1, - allowed() { - return !tech.isRelay - }, - requires: "not relay switch", - effect() { - tech.isFlipFlop = true //do you have this tech? - tech.isFlipFlopOn = true //what is the state of flip-Flop? - if (!m.isShipMode) { - m.draw = m.drawFlipFlop - } - }, - remove() { - tech.isFlipFlop = false - tech.isFlipFlopOn = false - m.eyeFillColor = 'transparent' - } - }, - { - name: "relay switch", - description: `toggle ON and OFF after picking up a power up
unlock advanced tech that runs if ON`, - nameInfo: "", - addNameInfo() { - setTimeout(function() { - if (document.getElementById("tech-switch")) { - if (tech.isFlipFlopOn) { - document.getElementById("tech-switch").innerHTML = ` = ON` - m.eyeFillColor = m.fieldMeterColor //'#5af' - } else { - document.getElementById("tech-switch").innerHTML = ` = OFF` - m.eyeFillColor = "transparent" - } - } - }, 100); - }, - maxCount: 1, - count: 0, - frequency: 1, - frequencyDefault: 1, - allowed() { - return !tech.isFlipFlop - }, - requires: "not flip-flop", - effect() { - tech.isRelay = true //do you have this tech? - tech.isFlipFlopOn = true //what is the state of flip-Flop? - if (!m.isShipMode) { - m.draw = m.drawFlipFlop - } - }, - remove() { - tech.isRelay = false - tech.isFlipFlopOn = false - m.eyeFillColor = 'transparent' - } - }, - { - name: "NAND gate", - description: "if in the ON state
do 50% more damage", - maxCount: 1, - count: 0, - frequency: 4, - frequencyDefault: 4, - allowed() { - return tech.isFlipFlop || tech.isRelay - }, - requires: "ON/OFF tech", - effect() { - tech.isFlipFlopDamage = true; - }, - remove() { - tech.isFlipFlopDamage = false; - } - }, - { - name: "transistor", - description: "if ON regen 20 energy per second
if OFF drain 1 energy per second", - maxCount: 1, - count: 0, - frequency: 4, - frequencyDefault: 4, - allowed() { - return tech.isFlipFlop || tech.isRelay - }, - requires: "ON/OFF tech", - effect() { - tech.isFlipFlopEnergy = true; - }, - remove() { - tech.isFlipFlopEnergy = false; - } - }, - { - name: "shift registers", - description: "set to the ON state
at the start of a level", - maxCount: 1, - count: 0, - frequency: 4, - frequencyDefault: 4, - allowed() { - return tech.isFlipFlopEnergy || tech.isFlipFlopDamage || tech.isFlipFlopHarm || tech.relayIce - }, - requires: "2 ON/OFF techs", - effect() { - tech.isFlipFlopLevelReset = true; - }, - remove() { - tech.isFlipFlopLevelReset = false; - } - }, - { - name: "thermocouple", - description: "if relay switch is in the ON state
condense 1-9 ice IX crystals every second", - maxCount: 9, - count: 0, - frequency: 4, - frequencyDefault: 4, - allowed() { - return tech.isRelay - }, - requires: "relay switch", - effect() { - tech.relayIce++ - }, - remove() { - tech.relayIce = 0 - } - }, - { - name: "crystallizer", - description: "after frozen mobs die they
shatter into ice IX crystals", - maxCount: 9, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return (tech.isIceCrystals || tech.isSporeFreeze || tech.isIceField || tech.isIceShot || tech.relayIce || tech.isNeedleIce || tech.blockingIce > 1) && !tech.sporesOnDeath && !tech.isExplodeMob && !tech.botSpawner && !tech.isMobBlockFling && !tech.nailsDeathMob - }, - requires: "a localized freeze effect, no other mob death tech", - effect() { - tech.iceIXOnDeath++ - }, - remove() { - tech.iceIXOnDeath = 0 - } - }, - { - name: "thermoelectric effect", - description: "killing mobs with ice IX
generates 100 energy", - maxCount: 9, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return tech.isIceField || tech.relayIce || tech.isNeedleIce || tech.blockingIce || tech.iceIXOnDeath || tech.isIceShot - }, - requires: "ice IX", - effect() { - tech.iceEnergy++ - }, - remove() { - tech.iceEnergy = 0; - } - }, - { - name: "superfluidity", - description: "freeze effects are applied to a small area", - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return tech.isIceCrystals || tech.isSporeFreeze || tech.isIceField || tech.relayIce || tech.isNeedleIce || tech.blockingIce > 1 || tech.iceIXOnDeath || tech.isIceShot - }, - requires: "a localized freeze effect", - effect() { - tech.isAoESlow = true - }, - remove() { - tech.isAoESlow = false - } - }, - { - name: "osmoprotectant", - description: `collisions with stunned or frozen mobs
cause you no harm`, - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return tech.isStunField || tech.isExplosionStun || tech.isMineStun || tech.oneSuperBall || tech.isHarmFreeze || tech.isIceField || tech.relayIce || tech.isNeedleIce || tech.isIceCrystals || tech.isSporeFreeze || tech.isAoESlow || tech.isFreezeMobs || tech.isCloakStun || tech.orbitBotCount > 1 || tech.isWormholeDamage || tech.blockingIce > 1 || tech.iceIXOnDeath || tech.isIceShot - }, - requires: "a freezing or stunning effect", - effect() { - tech.isFreezeHarmImmune = true; - }, - remove() { - tech.isFreezeHarmImmune = false; - } - }, - { - name: "liquid cooling", - description: `freeze all mobs for 7 seconds
after receiving harm`, - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return tech.isSlowFPS - }, - requires: "clock gating", - effect() { - tech.isHarmFreeze = true; - }, - remove() { - tech.isHarmFreeze = false; - } - }, - { - name: "clock gating", - description: `slow time by 50% after receiving harm
reduce harm by 20%`, - maxCount: 1, - count: 0, - frequency: 1, - frequencyDefault: 1, - allowed() { - return simulation.fpsCapDefault > 45 - }, - requires: "FPS above 45", - effect() { - tech.isSlowFPS = true; - }, - remove() { - tech.isSlowFPS = false; - } - }, - { - name: "quantum immortality", - description: "reduce harm by 33%
after dying, continue in an alternate reality", - maxCount: 1, - count: 0, - frequency: 1, - frequencyDefault: 1, - allowed() { return true }, - requires: "", - effect() { - tech.isImmortal = true; - }, - remove() { - tech.isImmortal = false; - } - }, - { - name: "MACHO", - description: "a massive but compact object slowly follows you
take 66% less harm inside it's halo", - maxCount: 1, - count: 0, - frequency: 1, - frequencyDefault: 1, - allowed() { - return !tech.isEnergyHealth - }, - requires: "not mass-energy", - effect: () => { - tech.isMACHO = true; //this harm reduction comes from the particle toggling tech.isHarmMACHO - spawn.MACHO() - }, - remove() { - tech.isMACHO = false; - for (let i = 0, len = mob.length; i < len; i++) { - if (mob[i].isMACHO) mob[i].alive = false; - } - } - }, - { - name: "ablative drones", - description: "rebuild your broken parts as drones
chance to occur after receiving harm", - maxCount: 1, - count: 0, - frequency: 1, - frequencyDefault: 1, - allowed() { - return m.harmReduction() < 1 - }, - requires: "some harm reduction", - effect() { - tech.isDroneOnDamage = true; - for (let i = 0; i < 4; i++) { - b.drone() //spawn drone - } - }, - remove() { - tech.isDroneOnDamage = false; - } - }, - { - name: "non-Newtonian armor", - link: `non-Newtonian armor`, - description: "for 10 seconds after receiving harm
reduce harm by 66%", - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return !tech.isEnergyHealth && m.harmReduction() < 1 - }, - requires: "some harm reduction", - effect() { - tech.isHarmArmor = true; - }, - remove() { - tech.isHarmArmor = false; - } - }, - { - name: "radiative equilibrium", - description: "for 10 seconds after receiving harm
increase damage by 200%", - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return m.harmReduction() < 1 - }, - requires: "some harm reduction", - effect() { - tech.isHarmDamage = true; - }, - remove() { - tech.isHarmDamage = false; - } - }, - { - name: "CPT symmetry", - description: "charge, parity, and time invert to undo harm
rewind (1.5—5) seconds for (66—220) energy", - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { //&& (m.fieldUpgrades[m.fieldMode].name !== "molecular assembler" || m.maxEnergy > 1) - return m.maxEnergy > 0.99 && m.fieldUpgrades[m.fieldMode].name !== "standing wave" && !tech.isEnergyHealth && !tech.isRewindField //&& !tech.isRewindGun - }, - requires: "not standing wave, mass-energy, max energy reduction", - effect() { - tech.isRewindAvoidDeath = true; - }, - remove() { - tech.isRewindAvoidDeath = false; - } - }, - { - name: "causality bots", - link: `causality bots`, - description: "when you rewind, build several bots
that protect you for about 9 seconds", - maxCount: 3, - count: 0, - frequency: 2, - frequencyDefault: 2, - isBotTech: true, - allowed() { - return tech.isRewindAvoidDeath || tech.isRewindField - }, - requires: "CPT, retrocausality", - effect() { - tech.isRewindBot++; - }, - remove() { - tech.isRewindBot = 0; - } - }, - { - name: "causality bombs", - link: `causality bombs`, - description: "when you rewind drop several grenades
become immune to harm until they explode", - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return tech.isRewindAvoidDeath || tech.isRewindField - }, - requires: "CPT, retrocausality", - effect() { - tech.isRewindGrenade = true; - }, - remove() { - tech.isRewindGrenade = false; - } - }, - { - name: "piezoelectricity", - description: "colliding with mobs gives you 2048 energy", //
reduce harm by 15% - maxCount: 1, - count: 0, - frequency: 1, - frequencyDefault: 1, - allowed() { - return !tech.isEnergyHealth - }, - requires: "not mass-energy", - effect() { - tech.isPiezo = true; - if (simulation.isTextLogOpen) m.energy += 20.48; - }, - remove() { - tech.isPiezo = false; - } - }, - { - name: "ground state", - description: "reduce harm by 66%
you no longer passively regenerate energy", - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return (tech.iceEnergy || tech.isWormholeEnergy || tech.isPiezo || tech.isRailEnergyGain || tech.energySiphon || tech.isEnergyRecovery || tech.dynamoBotCount || tech.isFlipFlopEnergy || tech.isTokamak) && tech.energyRegen !== 0.004 && !tech.isEnergyHealth && !tech.isCrouchRegen - }, - requires: "a way to regen extra energy, not time crystals", - effect: () => { - tech.energyRegen = 0; - m.fieldRegen = tech.energyRegen; - }, - remove() { - tech.energyRegen = 0.001; - m.fieldRegen = tech.energyRegen; - } - }, - { - name: "mass-energy equivalence", - description: "energy protects you instead of health
harm reduction effects provide no benefit", - maxCount: 1, - count: 0, - frequency: 1, - frequencyDefault: 1, - allowed() { - return !tech.isZeno && !tech.isNoHeals && !tech.isPiezo && !tech.isRewindAvoidDeath && !tech.isTechDamage && !tech.isMutualism //&& !tech.isAmmoFromHealth && !tech.isRewindGun - }, - requires: "not Zeno, ergodicity, piezoelectricity, CPT, antiscience, mutualism", - effect: () => { - m.health = 0 - document.getElementById("health").style.display = "none" - document.getElementById("health-bg").style.display = "none" - document.getElementById("dmg").style.backgroundColor = "#0cf"; - tech.isEnergyHealth = true; - simulation.mobDmgColor = "rgba(14, 190, 235,0.7)" //"#0cf" - m.displayHealth(); - }, - remove() { - if (tech.isEnergyHealth) { - tech.isEnergyHealth = false; - document.getElementById("health").style.display = "inline" - document.getElementById("health-bg").style.display = "inline" - document.getElementById("dmg").style.backgroundColor = "#f67"; - m.health = Math.max(Math.min(m.maxHealth, m.energy), 0.1); - simulation.mobDmgColor = "rgba(255,0,0,0.7)" - m.displayHealth(); - } - tech.isEnergyHealth = false; - } - }, - { - name: "1st ionization energy", - link: `1st ionization energy`, - description: `each ${powerUps.orb.heal()} you collect
increases your maximum energy by 8`, - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return tech.isEnergyHealth - }, - requires: "mass-energy equivalence", - effect() { - tech.healGiveMaxEnergy = true; //tech.healMaxEnergyBonus given from heal power up - powerUps.heal.color = "#0ae" - for (let i = 0; i < powerUp.length; i++) { //find active heal power ups and adjust color live - if (powerUp[i].name === "heal") powerUp[i].color = powerUps.heal.color - } - }, - remove() { - tech.healGiveMaxEnergy = false; - // tech.healMaxEnergyBonus = 0 - powerUps.heal.color = "#0eb" - for (let i = 0; i < powerUp.length; i++) { //find active heal power ups and adjust color live - if (powerUp[i].name === "heal") powerUp[i].color = powerUps.heal.color - } - } - }, - { - name: "weak interaction", - description: "each unused power up at the end of a level
adds 5 maximum energy", // (up to 51 health per level)", - maxCount: 1, - count: 0, - frequency: 1, - frequencyDefault: 1, - allowed() { - return !tech.isDroneGrab - }, - requires: "not delivery drone", - effect() { - tech.isExtraMaxEnergy = true; //tracked by tech.extraMaxHealth - }, - remove() { - tech.isExtraMaxEnergy = false; - } - }, - { - name: "electroweak interaction", - description: "unused power ups at the end of each level
are still activated (selections are random)", - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return tech.isExtraMaxEnergy - }, - requires: "weak interaction", - effect() { - tech.isEndLevelPowerUp = true; - }, - remove() { - tech.isEndLevelPowerUp = false; - } - }, - { - name: "electronegativity", - description: "increase damage by 1%
for every 11 stored energy", - maxCount: 1, - count: 0, - frequency: 1, - frequencyDefault: 1, - allowed() { return true }, - requires: "", - effect: () => { - tech.isEnergyDamage = true - }, - remove() { - tech.isEnergyDamage = false; - } - }, - { - name: "exciton", - description: `increase damage by 60%, but
${powerUps.orb.ammo()} will no longer spawn`, - maxCount: 1, - count: 0, - frequency: 1, - frequencyDefault: 1, - allowed() { return true }, - requires: "", - effect() { - tech.isEnergyNoAmmo = true; - }, - remove() { - tech.isEnergyNoAmmo = false; - } - }, - { - name: "exothermic process", - description: "increase damage by 50%
if a mob dies drain energy by 25%", - maxCount: 1, - count: 0, - frequency: 1, - frequencyDefault: 1, - allowed() { return true }, - requires: "", - effect() { - tech.isEnergyLoss = true; - }, - remove() { - tech.isEnergyLoss = false; - } - }, - { - name: "heat engine", - description: `increase damage by 50%, but
reduce maximum energy by 50`, - maxCount: 1, - count: 0, - frequency: 3, - frequencyDefault: 3, - allowed() { - return tech.isEnergyLoss && !tech.isRewindAvoidDeath - }, - requires: "exothermic process, not CPT", - effect() { - tech.isMaxEnergyTech = true; - m.setMaxEnergy() - }, - remove() { - tech.isMaxEnergyTech = false; - m.setMaxEnergy() - } - }, - { - name: "Gibbs free energy", - description: `increase damage by 5%
for every 10 energy below 100`, - maxCount: 1, - count: 0, - frequency: 3, - frequencyDefault: 3, - allowed() { - return tech.isEnergyLoss && m.maxEnergy < 1.01 - }, - requires: "exothermic process, not max energy increase", - effect() { - tech.isLowEnergyDamage = true; - }, - remove() { - tech.isLowEnergyDamage = false; - } - }, - { - name: "overcharge", - description: "increase your maximum energy by 60
+10% JUNK to the potential tech pool", - maxCount: 9, - count: 0, - frequency: 1, - frequencyDefault: 1, - allowed() { return true }, - requires: "", - effect() { - tech.bonusEnergy += 0.6 - m.setMaxEnergy() - this.refundAmount += tech.addJunkTechToPool(0.1) - }, - refundAmount: 0, - remove() { - tech.bonusEnergy = 0; - m.setMaxEnergy() - if (this.count > 0 && this.refundAmount > 0) { - tech.removeJunkTechFromPool(this.refundAmount) - this.refundAmount = 0 - } - } - }, - { - name: "Maxwell's demon", - description: "energy above your max decays 95% slower
+10% JUNK to the potential tech pool", - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return m.energy > m.maxEnergy || build.isExperimentSelection - }, - requires: "energy above your max", - effect() { - tech.overfillDrain = 0.92 //70% = 1-(1-0.75)/(1-0.15) //92% = 1-(1-0.75)/(1-0.87) - this.refundAmount += tech.addJunkTechToPool(0.1) - }, - refundAmount: 0, - remove() { - tech.overfillDrain = 0.7 - if (this.count > 0 && this.refundAmount > 0) { - tech.removeJunkTechFromPool(this.refundAmount) - this.refundAmount = 0 - } - } - }, - { - name: "inductive coupling", - description: "passive energy regen is increased by 700%
but you only regen when crouched", - maxCount: 1, - count: 0, - frequency: 1, - frequencyDefault: 1, - allowed() { - return tech.energyRegen !== 0 && !tech.isDamageAfterKillNoRegen - }, - requires: "not ground state, apex predator", - effect() { - tech.isCrouchRegen = true; //only used to check for requirements - m.regenEnergy = function() { - if (m.immuneCycle < m.cycle && m.crouch) m.energy += 7 * m.fieldRegen; //m.fieldRegen = 0.001 - if (m.energy < 0) m.energy = 0 - } - }, - remove() { - tech.isCrouchRegen = false; - m.regenEnergy = m.regenEnergyDefault - } - }, - { - name: "energy conservation", - description: "5% of damage done recovered as energy", - maxCount: 9, - count: 0, - frequency: 1, - frequencyDefault: 1, - allowed() { return true }, - requires: "", - effect() { - tech.energySiphon += 0.05; - }, - remove() { - tech.energySiphon = 0; - } - }, - { - name: "waste heat recovery", - description: "if a mob has died in the last 5 seconds
regen 5% of max energy every second", - maxCount: 1, - count: 0, - frequency: 1, - frequencyDefault: 1, - allowed() { return true }, - requires: "", - effect() { - tech.isEnergyRecovery = true; - }, - remove() { - tech.isEnergyRecovery = false; - } - }, - { - name: "recycling", - description: "if a mob has died in the last 5 seconds
regain 1% of max health every second", - maxCount: 1, - count: 0, - frequency: 1, - frequencyDefault: 1, - isHealTech: true, - allowed() { - return !tech.isEnergyHealth - }, - requires: "not mass-energy equivalence", - effect() { - tech.isHealthRecovery = true; - }, - remove() { - tech.isHealthRecovery = false; - } - }, - { - name: "predator", - description: "if a mob has died in the last 5 seconds
increase damage 50% and inhibit energy regen", - maxCount: 1, - count: 0, - frequency: 1, - frequencyDefault: 1, - allowed() { return !tech.isCrouchRegen }, - requires: "not inductive coupling", - effect() { - tech.isDamageAfterKillNoRegen = true; - m.regenEnergy = function() { - if (m.immuneCycle < m.cycle && (m.lastKillCycle + 300 < m.cycle)) m.energy += m.fieldRegen; //m.fieldRegen = 0.001 - if (m.energy < 0) m.energy = 0 - } - }, - remove() { - if (this.count) m.regenEnergy = m.regenEnergyDefault - tech.isDamageAfterKillNoRegen = false; - } - }, - { - name: "torpor", - description: "if a mob has not died in the last 5 seconds
reduce harm by 66%", - maxCount: 1, - count: 0, - frequency: 1, - frequencyDefault: 1, - allowed() { - return !tech.isEnergyHealth - }, - requires: "not mass-energy", - effect() { - tech.isHarmReduceNoKill = true; - }, - remove() { - tech.isHarmReduceNoKill = false; - } - }, - { - name: "Zeno's paradox", - description: "reduce harm by 83%, but every 5 seconds
remove 1/10 of your current health", - // description: "every 5 seconds remove 1/10 of your health
reduce harm by 90%", - maxCount: 1, - count: 0, - frequency: 1, - frequencyDefault: 1, - allowed() { return !tech.isEnergyHealth }, - requires: "not mass-energy", - effect() { - tech.isZeno = true; - }, - remove() { - tech.isZeno = false; - } - }, - { - name: "negative feedback", - description: "increase damage by 5%
for every 10 health below 100", - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return m.health < 0.6 || build.isExperimentSelection - }, - requires: "health below 60", - effect() { - tech.isLowHealthDmg = true; //used in mob.damage() - }, - remove() { - tech.isLowHealthDmg = false; - } - }, - { - name: "antiscience", - description: "increase damage by 90%
lose 11 health when you pick up a tech", - maxCount: 1, - count: 0, - frequency: 1, - frequencyDefault: 1, - allowed() { - return !tech.isEnergyHealth - }, - requires: "not mass-energy", - effect() { - tech.isTechDamage = true; - }, - remove() { - tech.isTechDamage = false; - } - }, - { - name: "entropy exchange", - description: "heal for 3% of damage done
take 10% more harm", - maxCount: 9, - count: 0, - frequency: 2, - frequencyDefault: 2, - isHealTech: true, - allowed() { - return !tech.isEnergyHealth - }, - requires: "not mass-energy equivalence", - effect() { - tech.healthDrain += 0.03; - }, - remove() { - tech.healthDrain = 0; - } - }, - { - name: "fluoroantimonic acid", - description: "increase damage by 35%
when your health is above 100", - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return m.maxHealth > 1; - }, - requires: "max health above 100", - effect() { - tech.isAcidDmg = true; - }, - remove() { - tech.isAcidDmg = false; - } - }, - { - name: "tungsten carbide", - description: "increase your maximum health by 100
landings that force you to crouch cause harm", - maxCount: 1, - count: 0, - frequency: 1, - frequencyDefault: 1, - allowed() { - return !tech.isEnergyHealth - }, - requires: "not mass-energy equivalence", - effect() { - tech.isFallingDamage = true; - m.setMaxHealth(); - m.addHealth(1 / simulation.healScale) - }, - remove() { - tech.isFallingDamage = false; - m.setMaxHealth(); - } - }, - { - name: "quenching", - description: `over healing from ${powerUps.orb.heal()} does harm
but it also increase your maximum health`, - maxCount: 1, - count: 0, - frequency: 1, - frequencyDefault: 1, - allowed() { - return !tech.isEnergyHealth && !tech.isNoHeals - }, - requires: "not mass-energy equivalence, ergodicity", - effect() { - tech.isOverHeal = true; - }, - remove() { - tech.isOverHeal = false; - } - }, - { - name: "negative entropy", - description: `at the start of each level
spawn ${powerUps.orb.heal()} for every 26 missing health`, - maxCount: 1, - count: 0, - frequency: 1, - frequencyDefault: 1, - isHealTech: true, - allowed() { - return m.health > 0.1 && !tech.isNoHeals - }, - requires: "has some health, not ergodicity", - effect() { - tech.isHealLowHealth = true; - }, - remove() { - tech.isHealLowHealth = false; - } - }, - { - name: "adiabatic healing", - description: `${powerUps.orb.heal()} are 100% more effective
+5% JUNK to the potential tech pool`, - maxCount: 3, - count: 0, - frequency: 1, - frequencyDefault: 1, - isHealTech: true, - allowed() { - return ((m.health / m.maxHealth) < 0.7 || build.isExperimentSelection) && !tech.isEnergyHealth && !tech.isNoHeals - }, - requires: "under 70% health, not mass-energy equivalence, ergodicity", - effect() { - tech.largerHeals++; - this.refundAmount += tech.addJunkTechToPool(0.05) - }, - refundAmount: 0, - remove() { - tech.largerHeals = 1; - if (this.count > 0 && this.refundAmount > 0) { - tech.removeJunkTechFromPool(this.refundAmount) - this.refundAmount = 0 - } - } - }, - { - name: "maintenance", - description: `double the frequency of finding healing tech
spawn ${powerUps.orb.heal(11)}`, - maxCount: 1, - count: 0, - frequency: 1, - frequencyDefault: 1, - isNonRefundable: true, - isBadRandomOption: true, - allowed() { return true }, - requires: "NOT EXPERIMENT MODE", - effect() { - for (let i = 0; i < 11; i++) powerUps.spawn(m.pos.x + 60 * (Math.random() - 0.5), m.pos.y + 60 * (Math.random() - 0.5), "heal"); - for (let i = 0, len = tech.tech.length; i < len; i++) { - if (tech.tech[i].isHealTech) tech.tech[i].frequency *= 2 - } - }, - remove() {} - }, - { - name: "anthropic principle", - nameInfo: "", - addNameInfo() { - setTimeout(function() { - powerUps.research.changeRerolls(0) - }, 1000); - }, - description: `once per level, instead of dying
use ${powerUps.orb.research(1)} and spawn ${powerUps.orb.heal(5)}`, - maxCount: 1, - count: 0, - frequency: 1, - frequencyDefault: 1, - isHealTech: true, - allowed() { - return powerUps.research.count > 0 || build.isExperimentSelection - }, - requires: "at least 1 research", - effect() { - tech.isDeathAvoid = true; - tech.isDeathAvoidedThisLevel = false; - setTimeout(function() { - powerUps.research.changeRerolls(0) - }, 1000); - }, - remove() { - tech.isDeathAvoid = false; - } - }, - { - name: "weak anthropic principle", - description: "after anthropic principle prevents your death
add 45% duplication chance for that level", - maxCount: 1, - count: 0, - frequency: 3, - frequencyDefault: 3, - allowed() { - return tech.isDeathAvoid - }, - requires: "anthropic principle", - effect() { - tech.isAnthropicTech = true - powerUps.setDupChance(); //needed after adjusting duplication chance - }, - remove() { - tech.isAnthropicTech = false - powerUps.setDupChance(); //needed after adjusting duplication chance - } - }, - { - name: "strong anthropic principle", - description: "after anthropic principle prevents your death
increase damage by 137.03599% for that level", - maxCount: 1, - count: 0, - frequency: 3, - frequencyDefault: 3, - allowed() { - return tech.isDeathAvoid - }, - requires: "anthropic principle", - effect() { - tech.isAnthropicDamage = true - }, - remove() { - tech.isAnthropicDamage = false - } - }, - { - name: "non-unitary operator", - link: `non-unitary operator`, - description: "reduce combat difficulty by 2 levels, but
after a collision enter an alternate reality", - maxCount: 1, - count: 0, - frequency: 1, - frequencyDefault: 1, - allowed() { - return !tech.isResearchReality && !tech.isSwitchReality - }, - requires: "not Ψ(t) collapse, many-worlds", - effect() { - tech.isCollisionRealitySwitch = true; - level.difficultyDecrease(simulation.difficultyMode * 2) - }, - remove() { - tech.isCollisionRealitySwitch = false; - if (this.count > 0) { - level.difficultyIncrease(simulation.difficultyMode * 2) - } - } - }, - { - name: "many-worlds", - // description: "each level is an alternate reality, where you
find a tech at the start of each level", - description: `on each new level use ${powerUps.orb.research(1)} to enter an
alternate reality and spawn a tech power up`, - maxCount: 1, - count: 0, - frequency: 1, - frequencyDefault: 1, - allowed() { - return !tech.isResearchReality && !tech.isCollisionRealitySwitch - }, - requires: "not Ψ(t) collapse, non-unitary", - effect() { - tech.isSwitchReality = true; - }, - remove() { - tech.isSwitchReality = false; - } - }, - { - name: "Ψ(t) collapse", - link: `Ψ(t) collapse`, - description: `enter an alternate reality after you research
spawn ${powerUps.orb.research(21)}`, - maxCount: 1, - count: 0, - frequency: 1, - frequencyDefault: 1, - allowed() { - return !tech.isSwitchReality && !tech.isCollisionRealitySwitch && !tech.isJunkResearch - }, - requires: "not many-worlds, non-unitary, pseudoscience", - effect() { - tech.isResearchReality = true; - for (let i = 0; i < 16; i++) powerUps.spawn(m.pos.x + Math.random() * 60, m.pos.y + Math.random() * 60, "research", false); - }, - remove() { - tech.isResearchReality = false; - } - }, - { - name: "decoherence", - description: `researched or canceled tech won't reoccur
spawn ${powerUps.orb.research(9)}`, - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return (powerUps.research.count > 2 || build.isExperimentSelection) && !tech.isDeterminism - }, - requires: "not determinism, at least 3 research", - effect() { - tech.isBanish = true - for (let i = 0; i < 9; i++) powerUps.spawn(m.pos.x + 40 * (Math.random() - 0.5), m.pos.y + 40 * (Math.random() - 0.5), "research", false); - }, - remove() { - if (tech.isBanish) { - tech.isBanish = false - powerUps.tech.banishLog = [] //reset banish log - powerUps.research.changeRerolls(-10) - } - } - }, - { - name: "renormalization", - description: `using ${powerUps.orb.research(1)} for any purpose
has a 40% chance to spawn ${powerUps.orb.research(1)}`, - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return (powerUps.research.count > 2 || build.isExperimentSelection) && !tech.isSuperDeterminism - }, - requires: "at least 3 research and not superdeterminism", - effect() { - tech.renormalization = true; - }, - remove() { - tech.renormalization = false; - } - }, - { - name: "perturbation theory", - description: `66% decreased delay after firing
when you have no ${powerUps.orb.research(1)} in your inventory`, - maxCount: 1, - count: 0, - frequency: 1, - frequencyDefault: 1, - allowed() { - return powerUps.research.count === 0 - }, - requires: "no research", - effect() { - tech.isRerollHaste = true; - tech.researchHaste = 0.33; - b.setFireCD(); - }, - remove() { - tech.isRerollHaste = false; - tech.researchHaste = 1; - b.setFireCD(); - } - }, - { - name: "ansatz", - description: `after choosing a field, tech, or gun
spawn ${powerUps.orb.research(2)}if you have no ${powerUps.orb.research(1)} in your inventory`, - maxCount: 1, - count: 0, - frequency: 1, - frequencyDefault: 1, - allowed() { - return powerUps.research.count === 0 && !tech.isSuperDeterminism && !tech.isRerollHaste && !tech.isResearchReality - }, - requires: "no research, not superdeterminism, Ψ(t) collapse, perturbation theory", - effect: () => { - tech.isAnsatz = true; - }, - remove() { - tech.isAnsatz = false; - } - }, - { - name: "Bayesian statistics", - description: `increase damage by 3.7%
for each ${powerUps.orb.research(1)} in your inventory`, - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return powerUps.research.count > 3 || build.isExperimentSelection - }, - requires: "at least 4 research", - effect() { - tech.isRerollDamage = true; - }, - remove() { - tech.isRerollDamage = false; - } - }, - { - name: "pseudoscience", - description: "when selecting a power up, research 3 times
for free, but add 0-3% JUNK to the tech pool", - maxCount: 1, - count: 0, - frequency: 1, - frequencyDefault: 1, - allowed() { - return !tech.isResearchReality //tech.isResearchBoss || tech.isMetaAnalysis || tech.isRerollBots || tech.isDeathAvoid || tech.isRerollDamage || build.isExperimentSelection - }, - requires: "not Ψ(t) collapse", //"abiogenesis, meta-analysis, bot fabrication, anthropic principle, or Bayesian statistics, not Ψ(t) collapse", - effect() { - tech.isJunkResearch = true; - }, - remove() { - tech.isJunkResearch = false; - } - }, - { - name: "Born rule", - description: "remove all current tech
spawn new tech to replace them", - maxCount: 1, - count: 0, - frequency: 1, - frequencyDefault: 1, - isNonRefundable: true, - isBadRandomOption: true, - allowed() { - return (tech.totalCount > 6) - }, - requires: "NOT EXPERIMENT MODE, more than 6 tech", - effect: () => { - //remove active bullets //to get rid of bots - for (let i = 0; i < bullet.length; ++i) Matter.Composite.remove(engine.world, bullet[i]); - bullet = []; - let count = 1 //count tech - for (let i = 0, len = tech.tech.length; i < len; i++) { // spawn new tech power ups - if (!tech.tech[i].isNonRefundable) count += tech.tech[i].count - } - if (tech.isDeterminism) count -= 4 //remove the bonus tech - if (tech.isSuperDeterminism) count -= 4 //remove the bonus tech - - tech.setupAllTech(); // remove all tech - if (simulation.isCheating) tech.setCheating(); - lore.techCount = 0; - // tech.addLoreTechToPool(); - for (let i = 0; i < count; i++) powerUps.spawn(m.pos.x + 100 * (Math.random() - 0.5), m.pos.y + 100 * (Math.random() - 0.5), "tech"); // spawn new tech power ups - //have state is checked in m.death() - }, - remove() {} - }, - { - name: "Occam's razor", - descriptionFunction() { - return `randomly remove ${this.removePercent*100}% of your tech
for each removed gain ${this.damagePerRemoved*100}% damage` - }, - maxCount: 1, - count: 0, - frequency: 1, - frequencyDefault: 1, - isNonRefundable: true, - isBadRandomOption: true, - allowed() { - return (tech.totalCount > 6) - }, - requires: "NOT EXPERIMENT MODE, more than 6 tech", - removePercent: 0.5, - damagePerRemoved: 0.36, - effect() { - let pool = [] - for (let i = 0, len = tech.tech.length; i < len; i++) { // spawn new tech power ups - if (tech.tech[i].count && !tech.tech[i].isNonRefundable) pool.push(i) - } - pool = shuffle(pool); //shuffles order of maps - let removeCount = 0 - for (let i = 0, len = pool.length * this.removePercent; i < len; i++) removeCount += tech.removeTech(pool[i]) - tech.OccamDamage = 1 + this.damagePerRemoved * removeCount - }, - remove() { - tech.OccamDamage = 0; - } - }, - { - name: "abiogenesis", - description: `at the start of a level spawn a 2nd boss
use ${powerUps.orb.research(4)}or add 49% JUNK to the tech pool`, - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return (build.isExperimentSelection || powerUps.research.count > 3) && !tech.isDuplicateBoss - }, - requires: "at least 4 research and not parthenogenesis", - effect() { - tech.isResearchBoss = true; //abiogenesis - }, - remove() { - tech.isResearchBoss = false; - } - }, - { - name: "bubble fusion", - description: `after destroying a mob's natural shield
spawn 1-2 ${powerUps.orb.heal()}, ${powerUps.orb.ammo()}, or ${powerUps.orb.research(1)}`, - maxCount: 1, - count: 0, - frequency: 1, - frequencyDefault: 1, - allowed() { return true }, - requires: "", - effect() { - tech.isShieldAmmo = true; - }, - remove() { - tech.isShieldAmmo = false; - } - }, - { - name: "meta-analysis", - description: `if you choose a JUNK tech you instead get a
random normal tech and ${powerUps.orb.research(3)}`, - maxCount: 1, - count: 0, - frequency: 1, - frequencyDefault: 1, - allowed() { return true }, - requires: "", - effect() { - tech.isMetaAnalysis = true - }, - remove() { - tech.isMetaAnalysis = false - } - }, - { - name: "replication", - description: "10% chance to duplicate spawned power ups
+40% JUNK to the potential tech pool", - maxCount: 9, - count: 0, - frequency: 1, - frequencyDefault: 1, - allowed() { - return tech.duplicationChance() < 1 - }, - requires: "below 100% duplication chance", - effect() { - tech.duplicateChance += 0.1 - powerUps.setDupChance(); //needed after adjusting duplication chance - if (!build.isExperimentSelection && !simulation.isTextLogOpen) simulation.circleFlare(0.1); - this.refundAmount += tech.addJunkTechToPool(0.4) - }, - refundAmount: 0, - remove() { - tech.duplicateChance = 0 - powerUps.setDupChance(); //needed after adjusting duplication chance - if (this.count > 0 && this.refundAmount > 0) { - tech.removeJunkTechFromPool(this.refundAmount) - this.refundAmount = 0 - } - } - }, - { - name: "stimulated emission", - description: "15% chance to duplicate spawned power ups
but, after a collision eject 1 tech", - maxCount: 1, - count: 0, - frequency: 1, - frequencyDefault: 1, - allowed() { - return tech.duplicationChance() < 1 - }, - requires: "below 100% duplication chance", - effect: () => { - tech.isStimulatedEmission = true - powerUps.setDupChance(); //needed after adjusting duplication chance - if (!build.isExperimentSelection && !simulation.isTextLogOpen) simulation.circleFlare(0.15); - }, - remove() { - tech.isStimulatedEmission = false - powerUps.setDupChance(); //needed after adjusting duplication chance - } - }, - { - name: "metastability", - description: "12% chance to duplicate spawned power ups
duplicates explode with a 3 second half-life ", - maxCount: 1, - count: 0, - frequency: 1, - frequencyDefault: 1, - allowed() { - return tech.duplicationChance() < 1 - }, - requires: "below 100% duplication chance", - effect: () => { - tech.isPowerUpsVanish = true - powerUps.setDupChance(); //needed after adjusting duplication chance - if (!build.isExperimentSelection && !simulation.isTextLogOpen) simulation.circleFlare(0.12); - }, - remove() { - tech.isPowerUpsVanish = false - powerUps.setDupChance(); //needed after adjusting duplication chance - } - }, - { - name: "futures exchange", - description: "clicking Ă— to cancel a field, tech, or gun
adds 4% power up duplication chance", - maxCount: 1, - count: 0, - frequency: 1, - frequencyDefault: 1, - allowed() { - return tech.duplicationChance() < 1 && !tech.isDeterminism - }, - requires: "below 100% duplication chance, not determinism", - effect() { - tech.isCancelDuplication = true //search for tech.cancelCount to balance - powerUps.setDupChance(); //needed after adjusting duplication chance - }, - remove() { - tech.isCancelDuplication = false - powerUps.setDupChance(); //needed after adjusting duplication chance - } - }, - { - name: "commodities exchange", - description: `clicking Ă— to cancel a field, tech, or gun
spawns 5-10 ${powerUps.orb.heal()}, ${powerUps.orb.ammo()}, or ${powerUps.orb.research(1)}`, - maxCount: 1, - count: 0, - frequency: 1, - frequencyDefault: 1, - allowed() { - return !tech.isDeterminism - }, - requires: "not determinism", - effect() { - tech.isCancelRerolls = true - }, - remove() { - tech.isCancelRerolls = false - } - }, - { - name: "correlated damage", - description: "your chance to duplicate power ups
increases your damage by the same percent", - maxCount: 1, - count: 0, - frequency: 1, - frequencyDefault: 1, - allowed() { - return tech.duplicationChance() > 0.15 - }, - requires: "duplication chance > 15%", - effect() { - tech.isDupDamage = true; - }, - remove() { - tech.isDupDamage = false; - } - }, - { - name: "parthenogenesis", - description: " bosses have a 2x chance to be duplicated, but their
health is increased by your duplication chance
", - maxCount: 1, - count: 0, - frequency: 1, - frequencyDefault: 1, - allowed() { - return tech.duplicationChance() > 0 && !tech.isResearchBoss - }, - requires: "some duplication chance, not abiogenesis", - effect() { - tech.isDuplicateBoss = true; - }, - remove() { - tech.isDuplicateBoss = false; - } - }, - { - name: "apomixis", - description: `when you reach 111% duplication
spawn 11 bosses with 111% more health`, - maxCount: 1, - count: 0, - frequency: 10, - frequencyDefault: 10, - allowed() { - return tech.duplicationChance() > 0.99 - }, - requires: "duplication chance above 99%", - effect() { - tech.is111Duplicate = true; - tech.maxDuplicationEvent() - }, - remove() { - tech.is111Duplicate = false; - } - }, - { - name: "exchange symmetry", - description: "convert 1 random tech into 2 new guns
recursive tech lose all stacks", - maxCount: 1, - count: 0, - frequency: 1, - frequencyDefault: 1, - isNonRefundable: true, - isBadRandomOption: true, - allowed() { - return (tech.totalCount > 3) && !tech.isSuperDeterminism - }, - requires: "NOT EXPERIMENT MODE, at least 4 tech, not superdeterminism", - effect: () => { - const have = [] //find which tech you have - for (let i = 0; i < tech.tech.length; i++) { - if (tech.tech[i].count > 0) have.push(i) - } - const choose = have[Math.floor(Math.random() * have.length)] - simulation.makeTextLog(`tech.removeTech("${tech.tech[choose].name}")`) - for (let i = 0; i < tech.tech[choose].count; i++) { - powerUps.spawn(m.pos.x, m.pos.y, "gun"); - } - powerUps.spawn(m.pos.x, m.pos.y, "gun"); - // powerUps.spawn(m.pos.x, m.pos.y, "gun"); - tech.tech[choose].count = 0; - tech.tech[choose].remove(); // remove a random tech form the list of tech you have - tech.tech[choose].isLost = true - simulation.updateTechHUD(); - }, - remove() {} - }, - { - name: "monte carlo experiment", - description: "spawn 2 tech
remove 1 random tech", - maxCount: 1, - count: 0, - frequency: 1, - frequencyDefault: 1, - isNonRefundable: true, - isBadRandomOption: true, - allowed() { - return (tech.totalCount > 3) && !tech.isSuperDeterminism - }, - requires: "NOT EXPERIMENT MODE, at least 4 tech, not superdeterminism", - effect: () => { - const removeTotal = tech.removeTech() - for (let i = 0; i < removeTotal + 1; i++) powerUps.spawn(m.pos.x + 60 * (Math.random() - 0.5), m.pos.y + 60 * (Math.random() - 0.5), "tech"); - }, - remove() {} - }, - { - name: "strange attractor", - description: `use ${powerUps.orb.research(2)} to spawn 1 tech
with double your duplication chance`, - maxCount: 1, - count: 0, - frequency: 1, - frequencyDefault: 1, - isNonRefundable: true, - isBadRandomOption: true, - allowed() { - return !tech.isSuperDeterminism && tech.duplicationChance() > 0 && powerUps.research.count > 1 - }, - requires: "NOT EXPERIMENT MODE, some duplication, not super determinism", - effect: () => { - powerUps.research.changeRerolls(-2) - simulation.makeTextLog(`m.research -= 2
${powerUps.research.count}`) - powerUps.directSpawn(m.pos.x, m.pos.y, "tech"); - if (Math.random() < tech.duplicationChance() * 2) powerUps.directSpawn(m.pos.x + 10, m.pos.y + 5, "tech"); - }, - remove() {} - }, - { - name: "vector fields", - description: "double the frequency of finding field tech
spawn a field", - maxCount: 1, - count: 0, - frequency: 1, - frequencyDefault: 1, - isNonRefundable: true, - isBadRandomOption: true, - allowed() { - return !tech.isSuperDeterminism - }, - requires: "NOT EXPERIMENT MODE, not superdeterminism", - effect() { - powerUps.spawn(m.pos.x, m.pos.y, "field"); - for (let i = 0, len = tech.tech.length; i < len; i++) { - if (tech.tech[i].isFieldTech) tech.tech[i].frequency *= 2 - } - }, - remove() { - // if (this.count > 1) { - // for (let i = 0, len = tech.tech.length; i < len; i++) { - // if (tech.tech[i].isFieldTech) tech.tech[i].frequency /= 2 - // } - // } - } - }, - { - name: "reinforcement learning", - description: "increase the frequency of finding copies of
recursive tech you already have by 1000%", - maxCount: 1, - count: 0, - frequency: 1, - frequencyDefault: 1, - allowed() { - return tech.totalCount > 9 - }, - requires: "at least 10 tech", - effect: () => { - for (let i = 0, len = tech.tech.length; i < len; i++) { - if (tech.tech[i].count > 0) tech.tech[i].frequency *= 10 - } - }, - remove() { - for (let i = 0, len = tech.tech.length; i < len; i++) { - if (tech.tech[i].count > 0 && tech.tech[i].frequency > 1) tech.tech[i].frequency /= 10 - } - } - }, - { - name: "backward induction", - description: `use ${powerUps.orb.research(2)} to choose all the unchosen
tech from your previous tech selection`, - maxCount: 1, - count: 0, - frequency: 1, - frequencyDefault: 1, - isNonRefundable: true, - isBadRandomOption: true, - allowed() { - return powerUps.tech.choiceLog.length > 10 && !tech.isDeterminism && powerUps.research.count > 1 - }, - requires: "NOT EXPERIMENT MODE, rejected an option in the last tech selection, at least 2 research, not determinism", - effect: () => { - powerUps.research.changeRerolls(-2) - let num = 3 - if (tech.isExtraChoice) num = 5 - if (tech.isDeterminism) num = 1 - for (let i = 0; i < num; i++) { - const index = powerUps.tech.choiceLog[powerUps.tech.choiceLog.length - i - 1] - if (index !== powerUps.lastTechIndex && tech.tech[index].count < tech.tech[index].maxCount && tech.tech[index].allowed() && tech.tech[index].name !== "backward induction") { - tech.giveTech(index) - simulation.makeTextLog(`tech.giveTech("${tech.tech[index].name}") //backward induction`); - } - } - }, - remove() {} - }, - { - name: "unified field theory", - description: `spawn ${powerUps.orb.research(6)}and when paused
clicking the field box switches your field`, - // description: `in the pause menu, change your field
by clicking on your field's box`, - maxCount: 1, - count: 0, - frequency: 1, - frequencyDefault: 1, - allowed() { - return !tech.isSuperDeterminism - }, - requires: "not superdeterminism", - effect() { - tech.isGunSwitchField = true; - for (let i = 0; i < 6; i++) powerUps.spawn(m.pos.x + 40 * (Math.random() - 0.5), m.pos.y + 40 * (Math.random() - 0.5), "research", false); - }, - remove() { - if (tech.isGunSwitchField) { - tech.isGunSwitchField = false; - powerUps.research.changeRerolls(-6) - } - } - }, - { - name: "cross disciplinary", - description: "tech have an extra field or gun choice", - maxCount: 1, - count: 0, - frequency: 1, - frequencyDefault: 1, - allowed() { - return !tech.isDeterminism - }, - requires: "not determinism", - effect: () => { - tech.isExtraGunField = true; - // for (let i = 0; i < 2; i++) powerUps.spawn(m.pos.x + 40 * (Math.random() - 0.5), m.pos.y + 40 * (Math.random() - 0.5), "research", false); - - }, - remove() { - tech.isExtraGunField = false; - // if (this.count > 0) powerUps.research.changeRerolls(-2) - } - }, - { - name: "emergence", - description: "tech, fields, and guns have 5 choices
+5% JUNK to the potential tech pool", - maxCount: 1, - count: 0, - frequency: 1, - frequencyDefault: 1, - allowed() { - return !tech.isDeterminism - }, - requires: "not determinism", - effect: () => { - tech.isExtraChoice = true; - this.refundAmount += tech.addJunkTechToPool(0.05) - }, - refundAmount: 0, - remove() { - tech.isExtraChoice = false; - if (this.count > 0 && this.refundAmount > 0) { - tech.removeJunkTechFromPool(this.refundAmount) - this.refundAmount = 0 - } - } - }, - { - name: "determinism", - description: "spawn 5 tech, but you have no cancel
and 1 choice for tech, fields, and guns", - maxCount: 1, - count: 0, - frequency: 1, - frequencyDefault: 1, - isBadRandomOption: true, - isNonRefundable: true, - allowed() { - return !tech.isExtraChoice && !tech.isCancelDuplication && !tech.isCancelRerolls - }, - requires: "not emergence, not futures or commodities exchanges", - effect: () => { - tech.isDeterminism = true; - //if you change the number spawned also change it in Born rule - for (let i = 0; i < 5; i++) powerUps.spawn(m.pos.x + 60 * (Math.random() - 0.5), m.pos.y + 60 * (Math.random() - 0.5), "tech"); - }, - remove() { - tech.isDeterminism = false; - // if (this.count > 0) { - // for (let i = 0; i < 5; i++) { - // const numberRemoved = tech.removeTech() - // console.log(numberRemoved) - // if (numberRemoved === 0) { //if the player didn't remove a power up then remove 1 tech for the map - // for (let j = powerUp.length - 1; j > -1; j--) { - // if (powerUp[j].name === "tech") { - // Matter.Composite.remove(engine.world, powerUp[j]); - // powerUp.splice(j, 1); - // break; - // } - // } - // } - // } - // } - } - }, - { - name: "superdeterminism", - description: `spawn 5 tech
${powerUps.orb.research(1)}, guns, and fields no longer spawn`, - maxCount: 1, - count: 0, - frequency: 3, - frequencyDefault: 3, - isBadRandomOption: true, - isNonRefundable: true, - allowed() { - return tech.isDeterminism && !tech.isAnsatz && !tech.isGunSwitchField - }, - requires: "determinism, not unified field theory, not ansatz", - effect: () => { - tech.isSuperDeterminism = true; - //if you change the number spawned also change it in Born rule - for (let i = 0; i < 5; i++) powerUps.spawn(m.pos.x + 60 * (Math.random() - 0.5), m.pos.y + 60 * (Math.random() - 0.5), "tech"); - }, - remove() { - tech.isSuperDeterminism = false; - // tech.isSuperDeterminism = false; - // if (this.count) { - // for (let i = 0; i < 5; i++) tech.removeTech() - // } - } - }, - { - name: "dark patterns", - description: "reduce combat difficulty by 1 level
+31% JUNK to the potential tech pool", - maxCount: 1, - count: 0, - frequency: 1, - frequencyDefault: 1, - allowed() { - return level.onLevel < 8 && level.onLevel > 0 - }, - requires: "on levels 1 through 7", - effect() { - level.difficultyDecrease(simulation.difficultyMode) - // simulation.difficulty-= - simulation.makeTextLog(`level.difficultyDecrease(simulation.difficultyMode)`) - this.refundAmount += tech.addJunkTechToPool(0.31) - // for (let i = 0; i < tech.junk.length; i++) tech.tech.push(tech.junk[i]) - }, - refundAmount: 0, - remove() { - if (this.count > 0) { - if (this.refundAmount > 0) tech.removeJunkTechFromPool(this.refundAmount) - level.difficultyIncrease(simulation.difficultyMode) - } - } - }, - { - name: "ergodicity", - description: `reduce combat difficulty by 2 levels
${powerUps.orb.heal()} have no effect`, - maxCount: 1, - count: 0, - frequency: 1, - frequencyDefault: 1, - allowed() { - return level.onLevel > 1 && !tech.isEnergyHealth - }, - requires: "past levels 1, not mass-energy", - effect() { - tech.isNoHeals = true; - level.difficultyDecrease(simulation.difficultyMode * 2) - simulation.makeTextLog(`level.difficultyDecrease(simulation.difficultyMode * 2)`) - powerUps.heal.color = "#abb" - for (let i = 0; i < powerUp.length; i++) { //find active heal power ups and adjust color live - if (powerUp[i].name === "heal") powerUp[i].color = powerUps.heal.color - } - }, - remove() { - if (tech.isNoHeals) { - powerUps.heal.color = "#0eb" - for (let i = 0; i < powerUp.length; i++) { //find active heal power ups and adjust color live - if (powerUp[i].name === "heal") powerUp[i].color = powerUps.heal.color - } - } - tech.isNoHeals = false; - if (this.count > 0) level.difficultyIncrease(simulation.difficultyMode * 2) - } - }, - //************************************************** - //************************************************** gun - //************************************************** tech - //************************************************** - // { - // name: "CPT gun", - // link: `CPT gun`, - // description: `adds the CPT gun to your inventory
it rewinds your health, velocity, and position`, - // isGunTech: true, - // maxCount: 1, - // count: 0, - // frequency: 2, - // frequencyDefault: 2, - // allowed() { - // return (b.totalBots() > 3 || m.fieldUpgrades[m.fieldMode].name === "molecular assembler" || m.fieldUpgrades[m.fieldMode].name === "plasma torch" || m.fieldUpgrades[m.fieldMode].name === "pilot wave") && !tech.isEnergyHealth && !tech.isRewindAvoidDeath //build.isExperimentSelection || - // }, - // requires: "bots > 3, plasma torch, assembler, pilot wave, not mass-energy equivalence, CPT", - // effect() { - // tech.isRewindGun = true - // b.guns.push(b.gunRewind) - // b.giveGuns("CPT gun"); - // }, - // remove() { - // if (tech.isRewindGun) { - // b.removeGun("CPT gun", true) - // // for (let i = 0; i < b.guns.length; i++) { - // // if (b.guns[i].name === "CPT gun") { - // // b.guns[i].have = false - // // for (let j = 0; j < b.inventory.length; j++) { - // // if (b.inventory[j] === i) { - // // b.inventory.splice(j, 1) - // // break - // // } - // // } - // // if (b.inventory.length) { - // // b.activeGun = b.inventory[0]; - // // } else { - // // b.activeGun = null; - // // } - // // simulation.makeGunHUD(); - - // // b.guns.splice(i, 1) //also remove CPT gun from gun pool array - // // break - // // } - // // } - // tech.isRewindGun = false - // } - // } - // }, - { - name: "needle ice", - description: `when needles impact walls
they chip off 1-2 freezing ice IX crystals`, - isGunTech: true, - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return tech.isNeedles || tech.isNeedleShot - }, - requires: "nail gun, needle gun, needle-shot", - effect() { - tech.isNeedleIce = true - }, - remove() { - tech.isNeedleIce = false - } - }, - { - name: "ceramics", - description: `needles and harpoons pierce shields
directly damaging shielded mobs`, - isGunTech: true, - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return tech.haveGunCheck("harpoon") || (tech.isNeedles || tech.isNeedleShot) - }, - requires: "nail gun, needle gun, needle-shot, harpoon", - effect() { - tech.isShieldPierce = true - }, - remove() { - tech.isShieldPierce = false - } - }, - { - name: "needle gun", - description: "nail gun fires 3 mob piercing needles
requires 3 times more bullets", - isGunTech: true, - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return tech.haveGunCheck("nail gun") && !tech.nailInstantFireRate && !tech.isIceCrystals && !tech.isRivets && !tech.nailRecoil - }, - requires: "nail gun, not ice crystal, rivets, rotary cannon, or pneumatic actuator", - effect() { - tech.isNeedles = true - for (i = 0, len = b.guns.length; i < len; i++) { //find which gun - if (b.guns[i].name === "nail gun") { - b.guns[i].ammo = Math.ceil(b.guns[i].ammo / 3); - b.guns[i].ammoPack = Math.ceil(b.guns[i].defaultAmmoPack / 3); - b.guns[i].chooseFireMethod() - simulation.updateGunHUD(); - break - } - } - }, - remove() { - if (tech.isNeedles) { - tech.isNeedles = false - for (i = 0, len = b.guns.length; i < len; i++) { //find which gun - if (b.guns[i].name === "nail gun") { - b.guns[i].chooseFireMethod() - b.guns[i].ammo = Math.ceil(b.guns[i].ammo * 3); - b.guns[i].ammoPack = b.guns[i].defaultAmmoPack; - simulation.updateGunHUD(); - break - } - } - } - } - }, - // { - // name: "darts", - // description: "nail gun fires firing several self-steering darts", - // isGunTech: true, - // maxCount: 1, - // count: 0, - // frequency: 2, - // frequencyDefault: 2, - // allowed() { - // return tech.haveGunCheck("nail gun") - // }, - // requires: "nail gun", - // effect() { - // tech.isDarts = true; - // for (i = 0, len = b.guns.length; i < len; i++) { //find which gun - // if (b.guns[i].name === "nail gun") { - // b.guns[i].chooseFireMethod() - // break - // } - // } - // }, - // remove() { - // if (tech.isDarts) { - // tech.isDarts = false; - // for (i = 0, len = b.guns.length; i < len; i++) { //find which gun - // if (b.guns[i].name === "nail gun") { - // b.guns[i].chooseFireMethod() - // break + }, + remove() { + tech.isExtraGunField = false; + // if (this.count > 0) powerUps.research.changeRerolls(-2) + } + }, + { + name: "emergence", + description: "tech, fields, and guns have 5 choices
+5% JUNK to the potential tech pool", + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return !tech.isDeterminism + }, + requires: "not determinism", + effect: () => { + tech.isExtraChoice = true; + this.refundAmount += tech.addJunkTechToPool(0.05) + }, + refundAmount: 0, + remove() { + tech.isExtraChoice = false; + if (this.count > 0 && this.refundAmount > 0) { + tech.removeJunkTechFromPool(this.refundAmount) + this.refundAmount = 0 + } + } + }, + { + name: "determinism", + description: "spawn 5 tech, but you have no cancel
and 1 choice for tech, fields, and guns", + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + isBadRandomOption: true, + isNonRefundable: true, + allowed() { + return !tech.isExtraChoice && !tech.isCancelDuplication && !tech.isCancelRerolls + }, + requires: "not emergence, not futures or commodities exchanges", + effect: () => { + tech.isDeterminism = true; + //if you change the number spawned also change it in Born rule + for (let i = 0; i < 5; i++) powerUps.spawn(m.pos.x + 60 * (Math.random() - 0.5), m.pos.y + 60 * (Math.random() - 0.5), "tech"); + }, + remove() { + tech.isDeterminism = false; + // if (this.count > 0) { + // for (let i = 0; i < 5; i++) { + // const numberRemoved = tech.removeTech() + // console.log(numberRemoved) + // if (numberRemoved === 0) { //if the player didn't remove a power up then remove 1 tech for the map + // for (let j = powerUp.length - 1; j > -1; j--) { + // if (powerUp[j].name === "tech") { + // Matter.Composite.remove(engine.world, powerUp[j]); + // powerUp.splice(j, 1); + // break; // } // } // } // } - // }, - { - name: "rivet gun", - description: "nail gun slowly fires a heavy rivet", - isGunTech: true, - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return tech.haveGunCheck("nail gun") && !tech.nailInstantFireRate && !tech.isIceCrystals && !tech.isNeedles - }, - requires: "nail gun, not ice crystal, needles, or pneumatic actuator", - effect() { - tech.isRivets = true - for (i = 0, len = b.guns.length; i < len; i++) { //find which gun - if (b.guns[i].name === "nail gun") { - b.guns[i].chooseFireMethod() - break - } - } - }, - remove() { - if (tech.isRivets) { - tech.isRivets = false - for (i = 0, len = b.guns.length; i < len; i++) { //find which gun - if (b.guns[i].name === "nail gun") { - b.guns[i].chooseFireMethod() - break - } - } - } + // } + } + }, + { + name: "superdeterminism", + description: `spawn 5 tech
${powerUps.orb.research(1)}, guns, and fields no longer spawn`, + maxCount: 1, + count: 0, + frequency: 3, + frequencyDefault: 3, + isBadRandomOption: true, + isNonRefundable: true, + allowed() { + return tech.isDeterminism && !tech.isAnsatz && !tech.isGunSwitchField + }, + requires: "determinism, not unified field theory, not ansatz", + effect: () => { + tech.isSuperDeterminism = true; + //if you change the number spawned also change it in Born rule + for (let i = 0; i < 5; i++) powerUps.spawn(m.pos.x + 60 * (Math.random() - 0.5), m.pos.y + 60 * (Math.random() - 0.5), "tech"); + }, + remove() { + tech.isSuperDeterminism = false; + // tech.isSuperDeterminism = false; + // if (this.count) { + // for (let i = 0; i < 5; i++) tech.removeTech() + // } + } + }, + { + name: "dark patterns", + description: "reduce combat difficulty by 1 level
+31% JUNK to the potential tech pool", + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return level.onLevel < 8 && level.onLevel > 0 + }, + requires: "on levels 1 through 7", + effect() { + level.difficultyDecrease(simulation.difficultyMode) + // simulation.difficulty-= + simulation.makeTextLog(`level.difficultyDecrease(simulation.difficultyMode)`) + this.refundAmount += tech.addJunkTechToPool(0.31) + // for (let i = 0; i < tech.junk.length; i++) tech.tech.push(tech.junk[i]) + }, + refundAmount: 0, + remove() { + if (this.count > 0) { + if (this.refundAmount > 0) tech.removeJunkTechFromPool(this.refundAmount) + level.difficultyIncrease(simulation.difficultyMode) + } + } + }, + { + name: "ergodicity", + description: `reduce combat difficulty by 2 levels
${powerUps.orb.heal()} have no effect`, + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return level.onLevel > 1 && !tech.isEnergyHealth + }, + requires: "past levels 1, not mass-energy", + effect() { + tech.isNoHeals = true; + level.difficultyDecrease(simulation.difficultyMode * 2) + simulation.makeTextLog(`level.difficultyDecrease(simulation.difficultyMode * 2)`) + powerUps.heal.color = "#abb" + for (let i = 0; i < powerUp.length; i++) { //find active heal power ups and adjust color live + if (powerUp[i].name === "heal") powerUp[i].color = powerUps.heal.color + } + }, + remove() { + if (tech.isNoHeals) { + powerUps.heal.color = "#0eb" + for (let i = 0; i < powerUp.length; i++) { //find active heal power ups and adjust color live + if (powerUp[i].name === "heal") powerUp[i].color = powerUps.heal.color } - }, - { - name: "pneumatic hammer", - description: `rivets are 20% larger
increases mass and physical damage`, - isGunTech: true, - maxCount: 9, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return tech.isRivets - }, - requires: "nail gun, rivet gun", - effect() { - tech.rivetSize += 0.2 - }, - remove() { - tech.rivetSize = 1; - } - }, - { - name: "ice crystal nucleation", - link: `ice crystal nucleation`, - description: "the nail gun uses energy to condense
unlimited freezing ice shards", - isGunTech: true, - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return tech.haveGunCheck("nail gun") && !tech.isRivets && !tech.isNeedles // && !tech.isNailRadiation && !tech.isNailCrit - }, - requires: "nail gun, not rivets, needles", - effect() { - tech.isIceCrystals = true; - for (i = 0, len = b.guns.length; i < len; i++) { //find which gun - if (b.guns[i].name === "nail gun") { - b.guns[i].ammoPack = Infinity - b.guns[i].recordedAmmo = b.guns[i].ammo - b.guns[i].ammo = Infinity - simulation.updateGunHUD(); - break; - } - } - }, - remove() { - if (tech.isIceCrystals) { - tech.isIceCrystals = false; - for (i = 0, len = b.guns.length; i < len; i++) { //find which gun - if (b.guns[i].name === "nail gun") { - b.guns[i].ammoPack = b.guns[i].defaultAmmoPack; - if (b.guns[i].recordedAmmo) b.guns[i].ammo = b.guns[i].recordedAmmo - simulation.updateGunHUD(); - break; - } - } - } - } - }, - { - name: "pneumatic actuator", - description: "nail gun takes no time to ramp up
to it's shortest delay after firing", - isGunTech: true, - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return tech.haveGunCheck("nail gun") && !tech.isRivets && !tech.isNeedles && !tech.nailRecoil - }, - requires: "nail gun, not rotary cannon, rivets, or needles", - effect() { - tech.nailInstantFireRate = true - for (i = 0, len = b.guns.length; i < len; i++) { //find which gun - if (b.guns[i].name === "nail gun") b.guns[i].chooseFireMethod() - } - }, - remove() { - if (tech.nailInstantFireRate) { - tech.nailInstantFireRate = false - for (i = 0, len = b.guns.length; i < len; i++) { //find which gun - if (b.guns[i].name === "nail gun") b.guns[i].chooseFireMethod() - } - } - } - }, - { - name: "rotary cannon", - description: "nail gun has increased muzzle speed,
maximum fire rate, accuracy, and recoil", - isGunTech: true, - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return tech.haveGunCheck("nail gun") && !tech.nailInstantFireRate && !tech.isNeedles - }, - requires: "nail gun, not pneumatic actuator, needle gun", - effect() { - tech.nailRecoil = true - for (i = 0, len = b.guns.length; i < len; i++) { //find which gun - if (b.guns[i].name === "nail gun") b.guns[i].chooseFireMethod() - } - }, - remove() { - if (tech.nailRecoil) { - tech.nailRecoil = false - for (i = 0, len = b.guns.length; i < len; i++) { //find which gun - if (b.guns[i].name === "nail gun") b.guns[i].chooseFireMethod() - } - } - } - }, - { - name: "supercritical fission", - description: "nails, needles, and rivets can explode
if they strike mobs near their center", - isGunTech: true, - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return (tech.isNailShot || tech.isNeedleShot || tech.nailBotCount > 1 || tech.haveGunCheck("nail gun")) - }, - requires: "nail gun, nails", - effect() { - tech.isNailCrit = true - }, - remove() { - tech.isNailCrit = false - } - }, - { - name: "irradiated nails", - link: `irradiated nails`, - description: "nails, needles, and rivets are radioactive
about 90% more damage over 3 seconds", - isGunTech: true, - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return tech.isMineDrop + tech.nailBotCount + tech.fragments + tech.nailsDeathMob / 2 + ((tech.haveGunCheck("mine") && !tech.isLaserMine) + (tech.haveGunCheck("nail gun") && !tech.isShieldPierce) + tech.isNeedleShot + tech.isNailShot) * 2 > 1 - }, - requires: "nail gun, nails, rivets, not ceramic needles", - effect() { - tech.isNailRadiation = true; - }, - remove() { - tech.isNailRadiation = false; - } - }, - { - name: "6s half-life", - link: `6s half-life`, - description: "nails are made of plutonium-238
increase damage by 100% over 6 seconds", - isGunTech: true, - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return tech.isNailRadiation && !tech.isFastRadiation - }, - requires: "irradiated nails, not 1s half-life", - effect() { - tech.isSlowRadiation = true; - }, - remove() { - tech.isSlowRadiation = false; - } - }, - { - name: "1s half-life", - link: `1s half-life`, - description: "nails are made of lithium-8
damage occurs after 1 second", - isGunTech: true, - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return tech.isNailRadiation && !tech.isSlowRadiation - }, - requires: "irradiated nails, not 6s half-life", - effect() { - tech.isFastRadiation = true; - }, - remove() { - tech.isFastRadiation = false; - } - }, - { - name: "shotgun spin-statistics", - link: `shotgun spin-statistics`, - description: "immune to harm while firing the shotgun
shotgun has 50% fewer shots", - isGunTech: true, - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return tech.haveGunCheck("shotgun") - }, - requires: "shotgun", - effect() { - tech.isShotgunImmune = true; + } + tech.isNoHeals = false; + if (this.count > 0) level.difficultyIncrease(simulation.difficultyMode * 2) + } + }, + //************************************************** + //************************************************** gun + //************************************************** tech + //************************************************** + // { + // name: "CPT gun", + // link: `CPT gun`, + // description: `adds the CPT gun to your inventory
it rewinds your health, velocity, and position`, + // isGunTech: true, + // maxCount: 1, + // count: 0, + // frequency: 2, + // frequencyDefault: 2, + // allowed() { + // return (b.totalBots() > 3 || m.fieldUpgrades[m.fieldMode].name === "molecular assembler" || m.fieldUpgrades[m.fieldMode].name === "plasma torch" || m.fieldUpgrades[m.fieldMode].name === "pilot wave") && !tech.isEnergyHealth && !tech.isRewindAvoidDeath //build.isExperimentSelection || + // }, + // requires: "bots > 3, plasma torch, assembler, pilot wave, not mass-energy equivalence, CPT", + // effect() { + // tech.isRewindGun = true + // b.guns.push(b.gunRewind) + // b.giveGuns("CPT gun"); + // }, + // remove() { + // if (tech.isRewindGun) { + // b.removeGun("CPT gun", true) + // // for (let i = 0; i < b.guns.length; i++) { + // // if (b.guns[i].name === "CPT gun") { + // // b.guns[i].have = false + // // for (let j = 0; j < b.inventory.length; j++) { + // // if (b.inventory[j] === i) { + // // b.inventory.splice(j, 1) + // // break + // // } + // // } + // // if (b.inventory.length) { + // // b.activeGun = b.inventory[0]; + // // } else { + // // b.activeGun = null; + // // } + // // simulation.makeGunHUD(); - //cut current ammo by 1/2 - for (i = 0, len = b.guns.length; i < len; i++) { //find which gun - if (b.guns[i].name === "shotgun") { - b.guns[i].ammo = Math.ceil(b.guns[i].ammo * 0.5); - b.guns[i].ammoPack = b.guns[i].defaultAmmoPack * 0.5 - break; - } - } + // // b.guns.splice(i, 1) //also remove CPT gun from gun pool array + // // break + // // } + // // } + // tech.isRewindGun = false + // } + // } + // }, + { + name: "needle ice", + description: `when needles impact walls
they chip off 1-2 freezing ice IX crystals`, + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.isNeedles || tech.isNeedleShot + }, + requires: "nail gun, needle gun, needle-shot", + effect() { + tech.isNeedleIce = true + }, + remove() { + tech.isNeedleIce = false + } + }, + { + name: "ceramics", + description: `needles and harpoons pierce shields
directly damaging shielded mobs`, + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.haveGunCheck("harpoon") || (tech.isNeedles || tech.isNeedleShot) + }, + requires: "nail gun, needle gun, needle-shot, harpoon", + effect() { + tech.isShieldPierce = true + }, + remove() { + tech.isShieldPierce = false + } + }, + { + name: "needle gun", + description: "nail gun fires 3 mob piercing needles
requires 3 times more bullets", + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.haveGunCheck("nail gun") && !tech.nailInstantFireRate && !tech.isIceCrystals && !tech.isRivets && !tech.nailRecoil + }, + requires: "nail gun, not ice crystal, rivets, rotary cannon, or pneumatic actuator", + effect() { + tech.isNeedles = true + for (i = 0, len = b.guns.length; i < len; i++) { //find which gun + if (b.guns[i].name === "nail gun") { + b.guns[i].ammo = Math.ceil(b.guns[i].ammo / 3); + b.guns[i].ammoPack = Math.ceil(b.guns[i].defaultAmmoPack / 3); + b.guns[i].chooseFireMethod() simulation.updateGunHUD(); - }, - remove() { - if (tech.isShotgunImmune) { - tech.isShotgunImmune = false; - for (i = 0, len = b.guns.length; i < len; i++) { //find which gun - if (b.guns[i].name === "shotgun") { - b.guns[i].ammoPack = b.guns[i].defaultAmmoPack; - b.guns[i].ammo = Math.ceil(b.guns[i].ammo * 2); - break; - } - } + break + } + } + }, + remove() { + if (tech.isNeedles) { + tech.isNeedles = false + for (i = 0, len = b.guns.length; i < len; i++) { //find which gun + if (b.guns[i].name === "nail gun") { + b.guns[i].chooseFireMethod() + b.guns[i].ammo = Math.ceil(b.guns[i].ammo * 3); + b.guns[i].ammoPack = b.guns[i].defaultAmmoPack; simulation.updateGunHUD(); + break } } - }, - { - name: "Newton's 3rd law", - description: "shotgun recoil is increased
decrease shotgun delay after firing by 66%", - isGunTech: true, - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return tech.haveGunCheck("shotgun") && !tech.isShotgunReversed - }, - requires: "shotgun, not Noether violation", - effect() { - tech.isShotgunRecoil = true; - }, - remove() { - tech.isShotgunRecoil = false; + } + } + }, + // { + // name: "darts", + // description: "nail gun fires firing several self-steering darts", + // isGunTech: true, + // maxCount: 1, + // count: 0, + // frequency: 2, + // frequencyDefault: 2, + // allowed() { + // return tech.haveGunCheck("nail gun") + // }, + // requires: "nail gun", + // effect() { + // tech.isDarts = true; + // for (i = 0, len = b.guns.length; i < len; i++) { //find which gun + // if (b.guns[i].name === "nail gun") { + // b.guns[i].chooseFireMethod() + // break + // } + // } + // }, + // remove() { + // if (tech.isDarts) { + // tech.isDarts = false; + // for (i = 0, len = b.guns.length; i < len; i++) { //find which gun + // if (b.guns[i].name === "nail gun") { + // b.guns[i].chooseFireMethod() + // break + // } + // } + // } + // } + // }, + { + name: "rivet gun", + description: "nail gun slowly fires a heavy rivet", + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.haveGunCheck("nail gun") && !tech.nailInstantFireRate && !tech.isIceCrystals && !tech.isNeedles + }, + requires: "nail gun, not ice crystal, needles, or pneumatic actuator", + effect() { + tech.isRivets = true + for (i = 0, len = b.guns.length; i < len; i++) { //find which gun + if (b.guns[i].name === "nail gun") { + b.guns[i].chooseFireMethod() + break } - }, - { - name: "Noether violation", - link: `Noether violation`, - description: "increase shotgun damage 60%
their recoil is increased and reversed", - isGunTech: true, - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return (tech.haveGunCheck("shotgun")) && !tech.isShotgunRecoil - }, - requires: "shotgun, not Newton's 3rd law", - effect() { - tech.isShotgunReversed = true; - }, - remove() { - tech.isShotgunReversed = false; - } - }, - { - name: "shotgun slug", - description: "shotgun lobs 1 huge bullet", - isGunTech: true, - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return tech.haveGunCheck("shotgun") && !tech.isNailShot && !tech.isIncendiary && !tech.isIceShot && !tech.isFoamShot && !tech.isWormShot && !tech.isNeedleShot - }, - requires: "shotgun, not nail-shot, foam-shot, worm-shot, ice-shot, needle-shot", - effect() { - tech.isSlugShot = true; - }, - remove() { - tech.isSlugShot = false; - } - }, - { - name: "nail-shot", - link: `nail-shot`, - description: "shotgun fires 17 nails", - isGunTech: true, - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return tech.haveGunCheck("shotgun") && !tech.isIncendiary && !tech.isSlugShot && !tech.isIceShot && !tech.isFoamShot && !tech.isWormShot && !tech.isNeedleShot - }, - requires: "shotgun, not incendiary, slug, foam-shot, worm-shot, ice-shot, needle-shot", - effect() { - tech.isNailShot = true; - }, - remove() { - tech.isNailShot = false; - } - }, - { - name: "needle-shot", - link: `needle-shot`, - description: "shotgun propels 11 mob piercing needles", - isGunTech: true, - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return tech.haveGunCheck("shotgun") && !tech.isNailShot && !tech.isIncendiary && !tech.isSlugShot && !tech.isFoamShot && !tech.isWormShot && !tech.isIceShot - }, - requires: "shotgun, not incendiary, nail-shot, slug, foam-shot, worm-shot, ice-shot", - effect() { - tech.isNeedleShot = true; - }, - remove() { - tech.isNeedleShot = false; - } - }, - { - name: "worm-shot", - link: `worm-shot`, - description: "shotgun hatches 3-4 mob seeking worms
worms benefit from spore technology", //
worms seek out nearby mobs - isGunTech: true, - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return tech.haveGunCheck("shotgun") && !tech.isNailShot && !tech.isIncendiary && !tech.isSlugShot && !tech.isIceShot && !tech.isFoamShot && !tech.isNeedleShot - }, - requires: "shotgun, not incendiary, nail-shot, slug, foam-shot, ice-shot, needle-shot", - effect() { - tech.isWormShot = true; - }, - remove() { - tech.isWormShot = false; - } - }, - { - name: "foam-shot", - link: `foam-shot`, - description: "shotgun sprays 13 sticky foam bubbles", - isGunTech: true, - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return tech.haveGunCheck("shotgun") && !tech.isNailShot && !tech.isIncendiary && !tech.isSlugShot && !tech.isIceShot && !tech.isWormShot && !tech.isNeedleShot - }, - requires: "shotgun, not incendiary, nail-shot, slug, worm-shot, ice-shot, needle-shot", - effect() { - tech.isFoamShot = true; - }, - remove() { - tech.isFoamShot = false; - } - }, - { - name: "ice-shot", - link: `ice-shot`, - description: "shotgun grows 15 freezing ice IX crystals", - isGunTech: true, - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return tech.haveGunCheck("shotgun") && !tech.isNailShot && !tech.isIncendiary && !tech.isSlugShot && !tech.isFoamShot && !tech.isWormShot && !tech.isNeedleShot - }, - requires: "shotgun, not incendiary, nail-shot, slug, foam-shot, worm-shot", - effect() { - tech.isIceShot = true; - }, - remove() { - tech.isIceShot = false; - } - }, - { - name: "supertemporal", - link: `supertemporal`, - description: "fire super ball from the same point in space
but separated by 0.1 seconds in time", - isGunTech: true, - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return tech.haveGunCheck("super balls") && !tech.oneSuperBall - }, - requires: "super balls, but not the tech super ball", - effect() { - tech.superBallDelay = true - for (i = 0, len = b.guns.length; i < len; i++) { //find which gun - if (b.guns[i].name === "super balls") b.guns[i].chooseFireMethod() - } - }, - remove() { - if (tech.superBallDelay) { - tech.superBallDelay = false; - for (i = 0, len = b.guns.length; i < len; i++) { //find which gun - if (b.guns[i].name === "super balls") b.guns[i].chooseFireMethod() - } + } + }, + remove() { + if (tech.isRivets) { + tech.isRivets = false + for (i = 0, len = b.guns.length; i < len; i++) { //find which gun + if (b.guns[i].name === "nail gun") { + b.guns[i].chooseFireMethod() + break } } - }, - { - name: "super ball", - description: "fire just 1 large super ball
that stuns mobs for 3 second", - isGunTech: true, - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return tech.haveGunCheck("super balls") && tech.missileCount === 1 && !tech.superBallDelay - }, - requires: "super balls, but not MIRV or supertemporal", - effect() { - tech.oneSuperBall = true; - for (i = 0, len = b.guns.length; i < len; i++) { //find which gun - if (b.guns[i].name === "super balls") b.guns[i].chooseFireMethod() - } - }, - remove() { - if (tech.oneSuperBall) { - tech.oneSuperBall = false; - for (i = 0, len = b.guns.length; i < len; i++) { //find which gun - if (b.guns[i].name === "super balls") b.guns[i].chooseFireMethod() - } + } + } + }, + { + name: "pneumatic hammer", + description: `rivets are 20% larger
increases mass and physical damage`, + isGunTech: true, + maxCount: 9, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.isRivets + }, + requires: "nail gun, rivet gun", + effect() { + tech.rivetSize += 0.2 + }, + remove() { + tech.rivetSize = 1; + } + }, + { + name: "ice crystal nucleation", + link: `ice crystal nucleation`, + description: "the nail gun uses energy to condense
unlimited freezing ice shards", + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.haveGunCheck("nail gun") && !tech.isRivets && !tech.isNeedles // && !tech.isNailRadiation && !tech.isNailCrit + }, + requires: "nail gun, not rivets, needles", + effect() { + tech.isIceCrystals = true; + for (i = 0, len = b.guns.length; i < len; i++) { //find which gun + if (b.guns[i].name === "nail gun") { + b.guns[i].ammoPack = Infinity + b.guns[i].recordedAmmo = b.guns[i].ammo + b.guns[i].ammo = Infinity + simulation.updateGunHUD(); + break; + } + } + }, + remove() { + if (tech.isIceCrystals) { + tech.isIceCrystals = false; + for (i = 0, len = b.guns.length; i < len; i++) { //find which gun + if (b.guns[i].name === "nail gun") { + b.guns[i].ammoPack = b.guns[i].defaultAmmoPack; + if (b.guns[i].recordedAmmo) b.guns[i].ammo = b.guns[i].recordedAmmo + simulation.updateGunHUD(); + break; } } - }, - { - name: "super sized", - description: `increase super ball radius by 14%
increases damage by about 27%`, - isGunTech: true, - maxCount: 9, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return tech.haveGunCheck("super balls") - }, - requires: "super balls", - effect() { - tech.bulletSize += 0.14 - }, - remove() { - tech.bulletSize = 1; - } - }, - { - name: "phase velocity", - description: "matter wave propagates faster through solids
increase matter wave damage by 15%", - // description: "matter wave propagates faster through solids
up by 3000% in the map and 760% in blocks", - isGunTech: true, - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return tech.haveGunCheck("matter wave") && !tech.isLongitudinal - }, - requires: "matter wave, not phonon", - effect() { - tech.isPhaseVelocity = true; - }, - remove() { - tech.isPhaseVelocity = false; - } - }, - { - name: "bound state", - description: "wave packets reflect backwards 2 times
range is reduced by 25%", - isGunTech: true, - maxCount: 9, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return tech.haveGunCheck("matter wave") - }, - requires: "matter wave", - effect() { - tech.waveReflections += 2 - }, - remove() { - tech.waveReflections = 1 - } - }, - { - name: "amplitude", - description: "wave packet amplitude is 33% higher
wave damage is increased by 50%", - isGunTech: true, - maxCount: 3, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return tech.haveGunCheck("matter wave") - }, - requires: "matter wave", - effect() { - tech.waveFrequency *= 0.66 - tech.wavePacketDamage *= 1.5 - }, - remove() { - tech.waveFrequency = 0.2 - tech.wavePacketDamage = 1 - } - }, - { - name: "propagation", - description: "wave packet propagation speed is 20% slower
wave damage is increased by 50%", - isGunTech: true, - maxCount: 9, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return tech.haveGunCheck("matter wave") - }, - requires: "matter wave", - effect() { - tech.waveBeamSpeed *= 0.8; - tech.waveBeamDamage += 1.5 * 0.5 //this sets base matter wave damage, not used by arcs or circles - }, - remove() { - tech.waveBeamSpeed = 10; - tech.waveBeamDamage = 1.5 //this sets base matter wave damage, not used by arcs or circles - } - }, - { - name: "phonon", //longitudinal //gravitational wave? - description: "matter wave emits low frequency, high damage
expanding arcs that propagate through solids", - isGunTech: true, - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return tech.haveGunCheck("matter wave") && !tech.isPhaseVelocity && !tech.isBulletTeleport - }, - requires: "matter wave, not phase velocity, uncertainty principle", - effect() { - tech.isLongitudinal = true; - for (i = 0, len = b.guns.length; i < len; i++) { //find which gun - if (b.guns[i].name === "matter wave") { - b.guns[i].chooseFireMethod() - b.guns[i].ammoPack = b.guns[i].defaultAmmoPack / 8 - b.guns[i].ammo = Math.ceil(b.guns[i].ammo / 8); - simulation.updateGunHUD(); - break - } - } - }, - remove() { - if (tech.isLongitudinal) { - for (i = 0, len = b.guns.length; i < len; i++) { //find which gun - if (b.guns[i].name === "matter wave") { - tech.isLongitudinal = false; - b.guns[i].chooseFireMethod() - b.guns[i].ammoPack = b.guns[i].defaultAmmoPack - b.guns[i].ammo = Math.ceil(b.guns[i].ammo * 8); - simulation.updateGunHUD(); - break - } - } - } - tech.isLongitudinal = false; - } - }, - { - name: "isotropic radiator", - description: "matter wave expands in all directions
range reduced 40% and damage increased 50%", - isGunTech: true, - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return tech.isLongitudinal - }, - requires: "matter wave, phonon", - effect() { - tech.is360Longitudinal = true; - for (i = 0, len = b.guns.length; i < len; i++) { //find which gun - if (b.guns[i].name === "matter wave") { - b.guns[i].chooseFireMethod() - break - } - } - }, - remove() { - tech.is360Longitudinal = false; - for (i = 0, len = b.guns.length; i < len; i++) { //find which gun - if (b.guns[i].name === "matter wave") { - b.guns[i].chooseFireMethod() - break - } - } - } - }, - { - name: "cruise missile", - description: "missiles travel 63% slower,
but have a 50% larger explosive payload", - isGunTech: true, - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return tech.haveGunCheck("missiles") || tech.isMissileField || tech.missileBotCount - }, - requires: "missiles", - effect() { - tech.missileSize = true - }, - remove() { - tech.missileSize = false - } - }, - { - name: "missile-bot", - link: `missile-bot`, - description: "gain a bot that fires missiles at mobs
remove your missile gun", - isGunTech: true, - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - isBot: true, - isBotTech: true, - allowed() { - return tech.haveGunCheck("missiles", false) - }, - requires: "missiles", - effect() { - tech.missileBotCount++; - b.missileBot(); - if (tech.haveGunCheck("missiles", false)) b.removeGun("missiles") //remove your last gun - }, - remove() { - if (this.count) { - tech.missileBotCount = 0; - b.clearPermanentBots(); - b.respawnBots(); - if (!tech.haveGunCheck("missiles", false)) b.giveGuns("missiles") - } - } - }, - { - name: "MIRV", - description: "fire +1 missile, grenade, and super ball
decrease explosion radius up to 10%", - isGunTech: true, - maxCount: 9, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return tech.haveGunCheck("missiles") || tech.missileBotCount || tech.haveGunCheck("grenades") || (tech.haveGunCheck("super balls") && !tech.oneSuperBall) - }, - requires: "missiles, grenades, super balls, not super ball", - effect() { - tech.missileCount++; - }, - remove() { - tech.missileCount = 1; - } - }, - { - name: "rocket-propelled grenade", - description: "grenades rapidly accelerate forward
map collisions trigger an explosion", - isGunTech: true, - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return tech.haveGunCheck("grenades") - }, - requires: "grenades", - effect() { - tech.isRPG = true; - b.setGrenadeMode() - }, - remove() { - tech.isRPG = false; - b.setGrenadeMode() - } - }, - { - name: "vacuum bomb", - description: "grenades fire slower, explode bigger
and, suck everything towards them", - isGunTech: true, - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return tech.haveGunCheck("grenades") && !tech.isNeutronBomb - }, - requires: "grenades, not neutron bomb", - effect() { - tech.isVacuumBomb = true; - b.setGrenadeMode() - }, - remove() { - tech.isVacuumBomb = false; - b.setGrenadeMode() - } - }, - { - name: "chain reaction", - description: "increase grenade radius and damage 33%
blocks caught in explosions also explode", - isGunTech: true, - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return tech.isVacuumBomb && !tech.isExplodeRadio - }, - requires: "grenades, vacuum bomb, not iridium-192", - effect() { - tech.isBlockExplode = true; //chain reaction - }, - remove() { - tech.isBlockExplode = false; - } - }, - { - name: "neutron bomb", - description: "grenades are irradiated with Cf-252
does damage, harm, and drains energy", - isGunTech: true, - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return tech.haveGunCheck("grenades") && !tech.fragments && !tech.isVacuumBomb - }, - requires: "grenades, not fragmentation, vacuum bomb", - effect() { - tech.isNeutronBomb = true; - b.setGrenadeMode() - }, - remove() { - tech.isNeutronBomb = false; - b.setGrenadeMode() - } - }, - { - name: "vacuum permittivity", - description: "increase radioactive range by 20%
objects in range of the bomb are slowed", - isGunTech: true, - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return tech.isNeutronBomb - }, - requires: "grenades, neutron bomb", - effect() { - tech.isNeutronSlow = true - }, - remove() { - tech.isNeutronSlow = false - } - }, - { - name: "booby trap", - description: "drop a mine after picking up a power up
+53% JUNK to the potential tech pool", - isGunTech: true, - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return tech.haveGunCheck("mine") - }, - requires: "mines", - effect() { - tech.isMineDrop = true; - if (tech.isMineDrop) b.mine(m.pos, { x: 0, y: 0 }, 0) - this.refundAmount += tech.addJunkTechToPool(0.53) - }, - refundAmount: 0, - remove() { - tech.isMineDrop = false; - if (this.count > 0 && this.refundAmount > 0) { - tech.removeJunkTechFromPool(this.refundAmount) - this.refundAmount = 0 - } - } - }, - { - name: "laser-mines", - link: `laser-mines`, - description: "mines laid while you are crouched
use energy to emit 3 unaimed lasers", - isGunTech: true, - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return tech.haveGunCheck("mine") - }, - requires: "mines", - effect() { - tech.isLaserMine = true; - }, - remove() { - tech.isLaserMine = false; - } - }, - { - name: "sentry", - description: "instead of detonating, mines target mobs
with a stream of nails for about 17 seconds", - isGunTech: true, - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return tech.haveGunCheck("mine") - }, - requires: "mines", - effect() { - tech.isMineSentry = true; - }, - remove() { - tech.isMineSentry = false; - } - }, - { - name: "blast mines", - link: `blast mines`, - description: "when a mine activates
it stuns nearby mobs for 2-4 seconds", - isGunTech: true, - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return tech.haveGunCheck("mine") - }, - requires: "mines", - effect() { - tech.isMineStun = true; - }, - remove() { - tech.isMineStun = false; - } - }, - { - name: "mycelial fragmentation", - link: `tinsellated flagella`, - description: "sporangium release 6 extra spores
during their growth phase", - isGunTech: true, - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return tech.haveGunCheck("spores") - }, - requires: "spore gun", - effect() { - tech.isSporeGrowth = true - }, - remove() { - tech.isSporeGrowth = false - } - }, - { - name: "tinsellated flagella", - link: `tinsellated flagella`, - description: "sporangium release 2 more spores
spores accelerate 40% faster", - isGunTech: true, - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return tech.haveGunCheck("spores") || tech.sporesOnDeath > 0 || tech.isSporeField - }, - requires: "spore gun, spores", - effect() { - tech.isFastSpores = true - }, - remove() { - tech.isFastSpores = false - } - }, - { - name: "cryodesiccation", - description: "sporangium release 2 more spores
spores freeze mobs for 1.5 second", - //
spores do 1/3 damage - isGunTech: true, - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return tech.haveGunCheck("spores") || tech.sporesOnDeath > 0 || tech.isSporeField || tech.isWormShot - }, - requires: "spore gun, spores or worms", - effect() { - tech.isSporeFreeze = true - }, - remove() { - tech.isSporeFreeze = false - } - }, - { - name: "diplochory", - description: "spores use you for dispersal
until they locate a viable host", - isGunTech: true, - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return tech.haveGunCheck("spores") || tech.sporesOnDeath > 0 || tech.isSporeField || tech.isWormShot - }, - requires: "spore gun, spores or worms", - effect() { - tech.isSporeFollow = true - }, - remove() { - tech.isSporeFollow = false - } - }, - { - name: "mutualism", - description: "increase spore damage by 150%
spores borrow 0.5 health until they die", - isGunTech: true, - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return (tech.haveGunCheck("spores") || tech.sporesOnDeath > 0 || tech.isSporeField) && !tech.isEnergyHealth || tech.isWormShot - }, - requires: "spore gun, spores, worms, not mass-energy", - effect() { - tech.isMutualism = true - }, - remove() { - tech.isMutualism = false - } - }, - { - name: "nematodes", - description: "spores develop into 1/2 as many worms
worms do 250% more damage", - isGunTech: true, - maxCount: 1, - count: 0, - frequency: 4, - frequencyDefault: 4, - allowed() { - return tech.haveGunCheck("spores") || tech.sporesOnDeath > 0 || tech.isSporeField || tech.isWormholeSpores - }, - requires: "spore gun, spores", - effect() { - tech.isSporeWorm = true - }, - remove() { - tech.isSporeWorm = false - } - }, - { - name: "necrophage", - description: "if worms kill their target
they reset their lifespan", - isGunTech: true, - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return tech.isSporeWorm || tech.isWormShot - }, - requires: "spore gun, worms", - effect() { - tech.wormSurviveDmg = true - }, - remove() { - tech.wormSurviveDmg = false - } - }, - { - name: "fault tolerance", - description: "spawn 8 drones that last forever
remove your drone gun", - isGunTech: true, - maxCount: 1, - count: 0, - frequency: 1, - frequencyDefault: 1, - allowed() { - return tech.haveGunCheck("drones", false) - }, - requires: "drones", - effect() { - const num = 8 - tech.isForeverDrones += num - if (tech.haveGunCheck("drones", false)) b.removeGun("drones") - //spawn drones - if (tech.isDroneRadioactive) { - for (let i = 0; i < num * 0.25; i++) { - b.droneRadioactive({ x: m.pos.x + 30 * (Math.random() - 0.5), y: m.pos.y + 30 * (Math.random() - 0.5) }, 5) - bullet[bullet.length - 1].endCycle = Infinity - } - } else { - for (let i = 0; i < num; i++) { - b.drone({ x: m.pos.x + 30 * (Math.random() - 0.5), y: m.pos.y + 30 * (Math.random() - 0.5) }, 5) - bullet[bullet.length - 1].endCycle = Infinity - } - } - }, - remove() { - tech.isForeverDrones = 0 - if (this.count && !tech.haveGunCheck("drones", false)) b.giveGuns("drones") - } - }, - { - name: "reduced tolerances", - link: `reduced tolerances`, - description: `increase drones per ${powerUps.orb.ammo()} or energy by 66%
reduce the average drone lifetime by 40%`, - isGunTech: true, - maxCount: 3, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return !tech.isDroneRadioactive && (tech.haveGunCheck("drones") || (m.fieldUpgrades[m.fieldMode].name === "molecular assembler" && !(tech.isSporeField || tech.isMissileField || tech.isIceField))) - }, - requires: "drones, not irradiated drones", - effect() { - tech.droneCycleReduction = Math.pow(0.6, 1 + this.count) - tech.droneEnergyReduction = Math.pow(0.333, 1 + this.count) - for (i = 0, len = b.guns.length; i < len; i++) { //find which gun - if (b.guns[i].name === "drones") { - const scale = Math.pow(3, this.count + 1) - b.guns[i].ammoPack = b.guns[i].defaultAmmoPack * scale - } - } - }, - remove() { - tech.droneCycleReduction = 1 - tech.droneEnergyReduction = 1 - for (i = 0, len = b.guns.length; i < len; i++) { //find which gun - if (b.guns[i].name === "drones") b.guns[i].ammoPack = b.guns[i].defaultAmmoPack - } - } - }, - { - name: "delivery drone", - description: "if a drone picks up a power up,
it becomes larger, faster, and more durable", - isGunTech: true, - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return !tech.isExtraMaxEnergy && (tech.haveGunCheck("drones") || tech.isForeverDrones || (m.fieldUpgrades[m.fieldMode].name === "molecular assembler" && !(tech.isSporeField || tech.isMissileField || tech.isIceField))) - }, - requires: "drones, not permittivity", - effect() { - tech.isDroneGrab = true - }, - remove() { - tech.isDroneGrab = false - } - }, - { - name: "drone repair", - link: `drone repair`, - description: "after a drone expires it redeploys
for a 25% chance to use 1 drone ammo", - // description: "broken drones repair if the drone gun is active
repairing has a 25% chance to use 1 drone", - isGunTech: true, - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return tech.haveGunCheck("drones") - }, - requires: "drones", - effect() { - tech.isDroneRespawn = true - }, - remove() { - tech.isDroneRespawn = false - } - }, - { - name: "brushless motor", - description: "drones rapidly rush towards their target
increase drone collision damage by 33%", - isGunTech: true, - maxCount: 1, - count: 0, - frequency: 3, - frequencyDefault: 3, - allowed() { - return (tech.haveGunCheck("drones") || tech.isForeverDrones || (m.fieldUpgrades[m.fieldMode].name === "molecular assembler" && !(tech.isSporeField || tech.isMissileField || tech.isIceField))) && !tech.isDroneRadioactive && !tech.isIncendiary - }, - requires: "drones, molecular assembler, not irradiated drones, incendiary", - effect() { - tech.isDroneTeleport = true - }, - remove() { - tech.isDroneTeleport = false - } - }, - { - name: "axial flux motor", - description: "drones can rush 66% more often
increase drone collision damage by 44%", - isGunTech: true, - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return tech.isDroneTeleport - }, - requires: "drones, brushless motor", - effect() { - tech.isDroneFastLook = true - }, - remove() { - tech.isDroneFastLook = false - } - }, - { - name: "irradiated drones", - link: `irradiated drones`, - description: `the space around drones is irradiated
reduce drones per ${powerUps.orb.ammo()} or energy 75%`, - isGunTech: true, - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return tech.droneCycleReduction === 1 && !tech.isIncendiary && !tech.isDroneTeleport && (tech.haveGunCheck("drones") || tech.isForeverDrones || (m.fieldUpgrades[m.fieldMode].name === "molecular assembler" && !(tech.isSporeField || tech.isMissileField || tech.isIceField))) - }, - requires: "drones, not reduced tolerances, incendiary, torque bursts", - effect() { - tech.isDroneRadioactive = true - for (i = 0, len = b.guns.length; i < len; i++) { //find which gun - if (b.guns[i].name === "drones") { - b.guns[i].ammoPack = b.guns[i].defaultAmmoPack * 0.25 - b.guns[i].ammo = Math.ceil(b.guns[i].ammo * 0.25) - simulation.makeGunHUD(); - } - } - }, - remove() { - if (tech.isDroneRadioactive) { - tech.isDroneRadioactive = false - for (i = 0, len = b.guns.length; i < len; i++) { //find which gun - if (b.guns[i].name === "drones") { - b.guns[i].ammoPack = b.guns[i].defaultAmmoPack - b.guns[i].ammo = b.guns[i].ammo * 4 - simulation.makeGunHUD(); - } - } - } - } - }, - { - name: "beta radiation", //"control rod ejection", - description: "reduce the average drone lifetime by 50%
increase radiation damage by 100%", - isGunTech: true, - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return tech.isDroneRadioactive - }, - requires: "drones irradiated drones", - effect() { - tech.droneRadioDamage = 2 - }, - remove() { - tech.droneRadioDamage = 1 - } - }, - { - name: "orthocyclic winding", - link: `orthocyclic winding`, - description: "drones accelerate 66% faster
increase radiation damage by 33%", - isGunTech: true, - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return tech.isDroneRadioactive - }, - requires: "drones, irradiated drones", - effect() { - tech.isFastDrones = true - }, - remove() { - tech.isFastDrones = false - } - }, - { - name: "electrostatic induction", - description: "foam bubbles are electrically charged
causing attraction to nearby mobs", - isGunTech: true, - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return !tech.isBulletTeleport && (tech.haveGunCheck("foam") || tech.foamBotCount > 1 || tech.isFoamShot) - }, - requires: "foam, not uncertainty", - effect() { - tech.isFoamAttract = true - }, - remove() { - tech.isFoamAttract = false - } - }, - { - name: "uncertainty principle", - description: "foam and wave particle positions are random
increase their damage by 43%", - isGunTech: true, - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return (!tech.isFoamAttract && (tech.haveGunCheck("foam") || tech.foamBotCount > 1 || tech.isFoamShot)) || (tech.haveGunCheck("matter wave") && !tech.isLongitudinal) - }, - requires: "foam, not electrostatic induction, matter wave, not phonon", - effect() { - tech.isBulletTeleport = true - }, - remove() { - tech.isBulletTeleport = false; - } - }, - { - name: "necrophoresis", - description: "foam bubbles grow and split into 3 copies
when the mob they are stuck to dies", - isGunTech: true, - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return tech.haveGunCheck("foam") || tech.foamBotCount > 1 || tech.isFoamShot - }, - requires: "foam", - effect() { - tech.isFoamGrowOnDeath = true - }, - remove() { - tech.isFoamGrowOnDeath = false; - } - }, - { - name: "aerogel", - description: "foam bubbles float and dissipate 50% faster
increase foam damage per second by 150%", - isGunTech: true, - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return tech.haveGunCheck("foam") || tech.foamBotCount > 1 || tech.isFoamShot - }, - requires: "foam", - effect() { - tech.isFastFoam = true - tech.foamGravity = -0.0003 - }, - remove() { - tech.isFastFoam = false; - tech.foamGravity = 0.00008 - } - }, - { - name: "quantum foam", - description: "foam gun fires 0.25 seconds into the future
increase foam gun damage by 66%", - isGunTech: true, - maxCount: 9, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return tech.haveGunCheck("foam") - }, - requires: "foam", - effect() { - tech.foamFutureFire++ - }, - remove() { - tech.foamFutureFire = 0; - } - }, - { - name: "foam fractionation", - description: "foam gun bubbles are 100% larger
when you have below 300 foam", - isGunTech: true, - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return tech.haveGunCheck("foam") - }, - requires: "foam", - effect() { - tech.isAmmoFoamSize = true - }, - remove() { - tech.isAmmoFoamSize = false; - } - }, - { - name: "surfactant", - description: "trade your foam gun for 2 foam-bots
and upgrade all bots to foam
", - isGunTech: true, - maxCount: 1, - count: 0, - frequency: 1, - frequencyDefault: 1, - isBot: true, - isBotTech: true, - isNonRefundable: true, - requires: "foam gun, not bot upgrades NOT EXPERIMENT MODE,", - allowed() { - return tech.haveGunCheck("foam", false) && !b.hasBotUpgrade() - }, - effect() { - tech.giveTech("foam-bot upgrade") - for (let i = 0; i < 2; i++) { - b.foamBot() - tech.foamBotCount++; - } - simulation.makeTextLog(`tech.isFoamBotUpgrade = true`) - if (tech.haveGunCheck("foam", false)) b.removeGun("foam") - }, - remove() { - // if (this.count) { - // b.clearPermanentBots(); - // b.respawnBots(); - // if (!tech.haveGunCheck("foam")) b.giveGuns("foam") - // } - } - }, - { - name: "filament", - description: "increase the length of your harpoon's rope
by 1% per harpoon ammo", - isGunTech: true, - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return tech.haveGunCheck("harpoon") - }, - requires: "harpoon", - effect() { - tech.isFilament = true; - }, - remove() { - tech.isFilament = false; - } - }, - { - name: "unaaq", - link: `unaaq`, //https://en.wikipedia.org/wiki/Weapon - description: "increase the size of your harpoon
by 10% of the square root of its ammo", - isGunTech: true, - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return tech.haveGunCheck("harpoon") - }, - requires: "harpoon", - effect() { - tech.isLargeHarpoon = true; - }, - remove() { - tech.isLargeHarpoon = false; - } - }, - { - name: "toggling harpoon", - description: "increase the damage of your next harpoon
by 800% after using it to collect a power up", - isGunTech: true, - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return tech.haveGunCheck("harpoon") - }, - requires: "harpoon", - effect() { - tech.isHarpoonPowerUp = true - }, - remove() { - tech.isHarpoonPowerUp = false - tech.harpoonDensity = 0.008 - } - }, - { - name: "reticulum", - description: "fire +1 harpoon, but energy cost
to retract also increases", - isGunTech: true, - maxCount: 9, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return tech.haveGunCheck("harpoon") - }, - requires: "harpoon", - effect() { - tech.extraHarpoons++; - }, - remove() { - tech.extraHarpoons = 0; - } - }, - { - name: "railgun", - description: "firing the harpoon while crouched launches
a rod that is faster, larger, and more dense", - isGunTech: true, - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return tech.haveGunCheck("harpoon") - }, - requires: "railgun", - effect() { - tech.isRailGun = true; - }, - remove() { - tech.isRailGun = false; - } - }, - { - name: "half-wave rectifier", - description: "charging the railgun gives you energy
instead of draining it", - isGunTech: true, - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return tech.isRailGun - }, - requires: "harpoon, railgun", - effect() { - tech.isRailEnergyGain = true; - }, - remove() { - tech.isRailEnergyGain = false; - } - }, - // { - // name: "dielectric polarization", - // description: "firing the railgun damages nearby mobs", - // isGunTech: true, - // maxCount: 1, - // count: 0, - // frequency: 2, - // frequencyDefault: 2, - // allowed() { - // return tech.haveGunCheck("railgun") - // }, - // requires: "railgun", - // effect() { - // tech.isRailAreaDamage = true; - // }, - // remove() { - // tech.isRailAreaDamage = false; - // } - // }, - // { - // name: "aerodynamic heating", - // description: "railgun rod damage nearby mobs", - // isGunTech: true, - // maxCount: 1, - // count: 0, - // frequency: 2, - // frequencyDefault: 2, - // allowed() { - // return tech.haveGunCheck("railgun") - // }, - // requires: "railgun", - // effect() { - // tech.isRodAreaDamage = true; - // }, - // remove() { - // tech.isRodAreaDamage = false; - // } - // }, - { - name: "laser diode", - description: "all lasers drain 30% less energy
affects laser-gun, laser-bot, laser-mines, pulse", - isGunTech: true, - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return (tech.haveGunCheck("laser") || tech.laserBotCount > 1 || tech.isLaserMine) && tech.laserDamage === 0.17 - }, - requires: "laser, not free-electron", - effect() { - tech.isLaserDiode = 0.70; //100%-37% - tech.laserColor = "rgb(0, 11, 255)" - tech.laserColorAlpha = "rgba(0, 11, 255,0.5)" - }, - remove() { - tech.isLaserDiode = 1; - tech.laserColor = "#f02" - tech.laserColorAlpha = "rgba(255, 0, 0, 0.5)" - } - }, - { - name: "free-electron laser", - description: "increase all laser damage by 200%
increase all laser energy drain by 250%", - isGunTech: true, - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return (tech.haveGunCheck("laser") || tech.isLaserMine || tech.laserBotCount > 1) && !tech.isPulseLaser && tech.isLaserDiode === 1 - }, - requires: "laser, not pulse, diodes", - effect() { - tech.laserFieldDrain = 0.007 //base is 0.002 - tech.laserDamage = 0.51; //base is 0.16 - tech.laserColor = "#83f" - tech.laserColorAlpha = "rgba(136, 51, 255,0.5)" - }, - remove() { - tech.laserFieldDrain = 0.002; - tech.laserDamage = 0.17; //used in check on pulse and diode: tech.laserDamage === 0.16 - tech.laserColor = "#f00" - tech.laserColorAlpha = "rgba(255, 0, 0, 0.5)" - } - }, - { - name: "relativistic momentum", - description: "all lasers push mobs and blocks away
affects laser-gun, laser-bot, and laser-mines", - isGunTech: true, - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return (tech.haveGunCheck("laser") && !tech.isPulseLaser) || tech.laserBotCount > 1 - }, - requires: "laser, not pulse", - effect() { - tech.isLaserPush = true; - }, - remove() { - tech.isLaserPush = false; - } - }, - { - name: "specular reflection", - description: "+2 reflection for all lasers
affects laser-gun, laser-bot, and laser-mines", - isGunTech: true, - maxCount: 3, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return (tech.haveGunCheck("laser") || tech.isLaserMine || tech.laserBotCount > 1) && !tech.isWideLaser && !tech.isPulseLaser && !tech.historyLaser - }, - requires: "laser, not diffuse beam, pulse, or slow light", - effect() { - tech.laserReflections += 2; - }, - remove() { - tech.laserReflections = 2; - } - }, - { - name: "diffraction grating", - description: `laser gains a diverging beam`, - isGunTech: true, - maxCount: 9, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return tech.haveGunCheck("laser") && !tech.isWideLaser && !tech.isPulseAim && !tech.historyLaser - }, - requires: "laser gun, not neocognitron, diffuse beam, or slow light", - effect() { - tech.beamSplitter++ - for (i = 0, len = b.guns.length; i < len; i++) { //find which gun - if (b.guns[i].name === "laser") b.guns[i].chooseFireMethod() - } - }, - remove() { - if (tech.beamSplitter !== 0) { - tech.beamSplitter = 0 - for (i = 0, len = b.guns.length; i < len; i++) { //find which gun - if (b.guns[i].name === "laser") b.guns[i].chooseFireMethod() - } - } - } - }, - { - name: "diffuse beam", - link: `diffuse beam`, - description: "laser beam is wider and doesn't reflect
increase full beam damage by 200%", - isGunTech: true, - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return tech.haveGunCheck("laser") && tech.laserReflections < 3 && !tech.beamSplitter && !tech.isPulseLaser && !tech.historyLaser - }, - requires: "laser gun, not specular reflection, diffraction grating, slow light, pulse", - effect() { - if (tech.wideLaser === 0) tech.wideLaser = 3 - tech.isWideLaser = true; - for (i = 0, len = b.guns.length; i < len; i++) { //find which gun - if (b.guns[i].name === "laser") b.guns[i].chooseFireMethod() - } - }, - remove() { - if (tech.isWideLaser) { - // tech.wideLaser = 0 - tech.isWideLaser = false; - for (i = 0, len = b.guns.length; i < len; i++) { //find which gun - if (b.guns[i].name === "laser") b.guns[i].chooseFireMethod() - } - } - } - }, - { - name: "output coupler", - description: "widen diffuse laser beam by 40%
increase full beam damage by 40%", - isGunTech: true, - maxCount: 9, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return tech.isWideLaser - }, - requires: "laser gun, diffuse beam", - effect() { - tech.wideLaser += 2 - for (i = 0, len = b.guns.length; i < len; i++) { //find which gun - if (b.guns[i].name === "laser") b.guns[i].chooseFireMethod() - } - }, - remove() { - if (tech.isWideLaser) { - tech.wideLaser = 3 - } else { - tech.wideLaser = 0 - } - for (i = 0, len = b.guns.length; i < len; i++) { //find which gun - if (b.guns[i].name === "laser") b.guns[i].chooseFireMethod() - } - } - }, - { - name: "slow light", - description: "laser beam is spread into your recent past
increase total beam damage by 300%", - isGunTech: true, - maxCount: 9, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return tech.haveGunCheck("laser") && tech.laserReflections < 3 && !tech.beamSplitter && !tech.isWideLaser - }, - requires: "laser gun, not specular reflection, diffraction grating, diffuse beam", - effect() { - // this.description = `add 5 more laser beams into into your past` - tech.historyLaser++ - for (i = 0, len = b.guns.length; i < len; i++) { //find which gun - if (b.guns[i].name === "laser") b.guns[i].chooseFireMethod() - } - }, - remove() { - // this.description = "laser beam is spread into your recent past
increase total beam damage by 300%" - if (tech.historyLaser) { - tech.historyLaser = 0 - for (i = 0, len = b.guns.length; i < len; i++) { //find which gun - if (b.guns[i].name === "laser") b.guns[i].chooseFireMethod() - } - } - } - }, - { - name: "pulse", - description: "charge your energy and release it as a
laser pulse that initiates an explosion cluster", - isGunTech: true, - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return tech.haveGunCheck("laser") && tech.laserReflections < 3 && !tech.isWideLaser && tech.laserDamage === 0.17 - }, - requires: "laser gun, not specular reflection, diffuse, free-electron laser", - effect() { - tech.isPulseLaser = true; - for (i = 0, len = b.guns.length; i < len; i++) { //find which gun - if (b.guns[i].name === "laser") b.guns[i].chooseFireMethod() - } - }, - remove() { - if (tech.isPulseLaser) { - tech.isPulseLaser = false; - for (i = 0, len = b.guns.length; i < len; i++) { //find which gun - if (b.guns[i].name === "laser") b.guns[i].chooseFireMethod() - } - } - } - }, - { - name: "neocognitron", - description: "pulse automatically aims at a nearby mob", - isGunTech: true, - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return tech.isPulseLaser && !tech.beamSplitter - }, - requires: "laser gun, pulse, not diffraction grating", - effect() { - tech.isPulseAim = true; - }, - remove() { - tech.isPulseAim = false; - } - }, - //************************************************** - //************************************************** field - //************************************************** tech - //************************************************** - { - name: "zero point energy", - description: `use ${powerUps.orb.research(2)}to increase your max energy by 100`, - // description: "use 2 research to
increase your maximum energy by 74", - isFieldTech: true, - maxCount: 1, - count: 0, - frequency: 3, - frequencyDefault: 3, - allowed() { - return (m.fieldUpgrades[m.fieldMode].name === "standing wave" || m.fieldUpgrades[m.fieldMode].name === "pilot wave") && (build.isExperimentSelection || powerUps.research.count > 1) - }, - requires: "standing wave or pilot wave", - effect() { - tech.harmonicEnergy = 1 - m.setMaxEnergy() - for (let i = 0; i < 2; i++) { - if (powerUps.research.count > 0) powerUps.research.changeRerolls(-1) - } - }, - remove() { - tech.harmonicEnergy = 0; - m.setMaxEnergy() - if (this.count > 0) powerUps.research.changeRerolls(2) - } - }, - { - name: "spherical harmonics", - description: "standing wave oscillates in a 3rd dimension
increasing deflecting efficiency by 40%", - isFieldTech: true, - maxCount: 9, - count: 0, - frequency: 3, - frequencyDefault: 3, - allowed() { - return m.fieldUpgrades[m.fieldMode].name === "standing wave" - }, - requires: "standing wave", - effect() { - tech.harmonics++ - m.fieldShieldingScale = 1.3 * Math.pow(0.6, (tech.harmonics - 2)) - m.harmonicShield = m.harmonicAtomic - }, - remove() { - tech.harmonics = 2 - m.fieldShieldingScale = 1.3 * Math.pow(0.6, (tech.harmonics - 2)) - m.harmonicShield = m.harmonic3Phase - } - }, - { - name: "expansion", - description: "using standing wave field temporarily
expands its radius", - // description: "use energy to expand standing wave
the field slowly contracts when not used", - isFieldTech: true, - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return m.fieldUpgrades[m.fieldMode].name === "standing wave" - }, - requires: "standing wave", - effect() { - tech.isStandingWaveExpand = true - }, - remove() { - tech.isStandingWaveExpand = false - m.harmonicRadius = 1 - } - }, - { - name: "bremsstrahlung", - description: "deflecting does damage to mobs", - isFieldTech: true, - maxCount: 9, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return m.fieldUpgrades[m.fieldMode].name === "standing wave" || m.fieldUpgrades[m.fieldMode].name === "perfect diamagnetism" - }, - requires: "standing wave, perfect diamagnetism", - effect() { - tech.blockDmg += 1.75 //if you change this value also update the for loop in the electricity graphics in m.pushMass - }, - remove() { - tech.blockDmg = 0; - } - }, - { - name: "triple point", - description: "the pressure from deflecting is used
to condense ice IX crystals", - isFieldTech: true, - maxCount: 9, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return m.fieldUpgrades[m.fieldMode].name === "standing wave" || m.fieldUpgrades[m.fieldMode].name === "perfect diamagnetism" - }, - requires: "standing wave, perfect diamagnetism", - effect() { - tech.blockingIce++ - }, - remove() { - tech.blockingIce = 0; - } - }, - { - name: "flux pinning", - description: "deflecting mobs with your field
stuns them for 4 seconds", - isFieldTech: true, - maxCount: 9, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return m.fieldUpgrades[m.fieldMode].name === "perfect diamagnetism" || m.fieldUpgrades[m.fieldMode].name === "standing wave" || m.fieldUpgrades[m.fieldMode].name === "molecular assembler" - }, - requires: "a field that can block", - effect() { - tech.isStunField += 240; - }, - remove() { - tech.isStunField = 0; - } - }, - { - name: "eddy current brake", - description: "project a field that limits the top speed of mobs
field radius scales with stored energy", - isFieldTech: true, - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return m.fieldUpgrades[m.fieldMode].name === "perfect diamagnetism" - }, - requires: "perfect diamagnetism", - effect() { - tech.isPerfectBrake = true; - }, - remove() { - tech.isPerfectBrake = false; - } - }, - { - name: "Meissner effect", - description: "increase perfect diamagnetism field
radius by 55% and circular arc by 22°", - isFieldTech: true, - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return m.fieldUpgrades[m.fieldMode].name === "perfect diamagnetism" - }, - requires: "perfect diamagnetism", - effect() { - tech.isBigField = true; - }, - remove() { - tech.isBigField = false; - } - }, - { - name: "tessellation", - description: `use ${powerUps.orb.research(2)}to reduce harm by 50%`, - // description: "use 4 research
reduce harm by 50%", - isFieldTech: true, - maxCount: 1, - count: 0, - frequency: 3, - frequencyDefault: 3, - allowed() { - return (m.fieldUpgrades[m.fieldMode].name === "pilot wave" || m.fieldUpgrades[m.fieldMode].name === "perfect diamagnetism" || m.fieldUpgrades[m.fieldMode].name === "negative mass") && (build.isExperimentSelection || powerUps.research.count > 3) - }, - requires: "perfect diamagnetism, negative mass, pilot wave", - effect() { - tech.isFieldHarmReduction = true - for (let i = 0; i < 2; i++) { - if (powerUps.research.count > 0) powerUps.research.changeRerolls(-1) - } - }, - remove() { - tech.isFieldHarmReduction = false - if (this.count > 0) powerUps.research.changeRerolls(2) - } - }, - { - name: "neutronium", - description: `reduce harm by 90% when your field is active
move and jump 33% slower`, - isFieldTech: true, - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return m.fieldUpgrades[m.fieldMode].name === "negative mass" && !tech.isEnergyHealth - }, - requires: "negative mass, not mass-energy", - effect() { - tech.isNeutronium = true - tech.baseFx *= 0.66 - tech.baseJumpForce *= 0.66 - m.setMovement() - }, - //also removed in m.setHoldDefaults() if player switches into a bad field - remove() { - tech.isNeutronium = false - if (!tech.isFreeWormHole) { - tech.baseFx = 0.08 - tech.baseJumpForce = 10.5 - m.setMovement() - } - } - }, - { - name: "annihilation", - description: "touching normal mobs annihilates them
but drains 33% of your maximum energy", - isFieldTech: true, - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return m.fieldUpgrades[m.fieldMode].name === "negative mass" - }, - requires: "negative mass", - effect() { - tech.isAnnihilation = true - }, - remove() { - tech.isAnnihilation = false; - } - }, - { - name: "inertial mass", - description: "negative mass is larger and faster
blocks also move horizontally with the field", - isFieldTech: true, - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return m.fieldUpgrades[m.fieldMode].name === "negative mass" - }, - requires: "negative mass", - effect() { - tech.isFlyFaster = true - }, - remove() { - tech.isFlyFaster = false; - } - }, - { - name: "Bose Einstein condensate", - description: "mobs inside your field are frozen
pilot wave, negative mass, time dilation", - isFieldTech: true, - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return m.fieldUpgrades[m.fieldMode].name === "pilot wave" || m.fieldUpgrades[m.fieldMode].name === "negative mass" || (m.fieldUpgrades[m.fieldMode].name === "time dilation" && !tech.isRewindField) - }, - requires: "pilot wave, negative mass, time dilation, not retrocausality", - effect() { - tech.isFreezeMobs = true - }, - remove() { - tech.isFreezeMobs = false - } - }, - { - name: "bot manufacturing", - description: `use molecular assembler and ${powerUps.orb.research(2)}
to build 3 random bots`, - isFieldTech: true, - maxCount: 1, - count: 0, - frequency: 1, - frequencyDefault: 1, - isBotTech: true, - isNonRefundable: true, - // isExperimentHide: true, - allowed() { - return powerUps.research.count > 1 && m.fieldUpgrades[m.fieldMode].name === "molecular assembler" - }, - requires: "NOT EXPERIMENT MODE, molecular assembler", - effect: () => { - for (let i = 0; i < 2; i++) { - if (powerUps.research.count > 0) powerUps.research.changeRerolls(-1) - } - m.energy = 0.01; - b.randomBot() - b.randomBot() - b.randomBot() - }, - remove() {} - }, - { - name: "bot prototypes", - description: `use ${powerUps.orb.research(3)}to build
2 random bots and upgrade all bots to that type`, - isFieldTech: true, - maxCount: 1, - count: 0, - frequency: 1, - frequencyDefault: 1, - isBotTech: true, - isNonRefundable: true, - // isExperimentHide: true, - allowed() { - return powerUps.research.count > 2 && m.fieldUpgrades[m.fieldMode].name === "molecular assembler" - }, - requires: "NOT EXPERIMENT MODE, molecular assembler", - effect: () => { - for (let i = 0; i < 3; i++) { - if (powerUps.research.count > 0) powerUps.research.changeRerolls(-1) - } + } + } + }, + { + name: "pneumatic actuator", + description: "nail gun takes no time to ramp up
to it's shortest delay after firing", + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.haveGunCheck("nail gun") && !tech.isRivets && !tech.isNeedles && !tech.nailRecoil + }, + requires: "nail gun, not rotary cannon, rivets, or needles", + effect() { + tech.nailInstantFireRate = true + for (i = 0, len = b.guns.length; i < len; i++) { //find which gun + if (b.guns[i].name === "nail gun") b.guns[i].chooseFireMethod() + } + }, + remove() { + if (tech.nailInstantFireRate) { + tech.nailInstantFireRate = false + for (i = 0, len = b.guns.length; i < len; i++) { //find which gun + if (b.guns[i].name === "nail gun") b.guns[i].chooseFireMethod() + } + } + } + }, + { + name: "rotary cannon", + description: "nail gun has increased muzzle speed,
maximum fire rate, accuracy, and recoil", + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.haveGunCheck("nail gun") && !tech.nailInstantFireRate && !tech.isNeedles + }, + requires: "nail gun, not pneumatic actuator, needle gun", + effect() { + tech.nailRecoil = true + for (i = 0, len = b.guns.length; i < len; i++) { //find which gun + if (b.guns[i].name === "nail gun") b.guns[i].chooseFireMethod() + } + }, + remove() { + if (tech.nailRecoil) { + tech.nailRecoil = false + for (i = 0, len = b.guns.length; i < len; i++) { //find which gun + if (b.guns[i].name === "nail gun") b.guns[i].chooseFireMethod() + } + } + } + }, + { + name: "supercritical fission", + description: "nails, needles, and rivets can explode
if they strike mobs near their center", + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return (tech.isNailShot || tech.isNeedleShot || tech.nailBotCount > 1 || tech.haveGunCheck("nail gun")) + }, + requires: "nail gun, nails", + effect() { + tech.isNailCrit = true + }, + remove() { + tech.isNailCrit = false + } + }, + { + name: "irradiated nails", + link: `irradiated nails`, + description: "nails, needles, and rivets are radioactive
about 90% more damage over 3 seconds", + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.isMineDrop + tech.nailBotCount + tech.fragments + tech.nailsDeathMob / 2 + ((tech.haveGunCheck("mine") && !tech.isLaserMine) + (tech.haveGunCheck("nail gun") && !tech.isShieldPierce) + tech.isNeedleShot + tech.isNailShot) * 2 > 1 + }, + requires: "nail gun, nails, rivets, not ceramic needles", + effect() { + tech.isNailRadiation = true; + }, + remove() { + tech.isNailRadiation = false; + } + }, + { + name: "6s half-life", + link: `6s half-life`, + description: "nails are made of plutonium-238
increase damage by 100% over 6 seconds", + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.isNailRadiation && !tech.isFastRadiation + }, + requires: "irradiated nails, not 1s half-life", + effect() { + tech.isSlowRadiation = true; + }, + remove() { + tech.isSlowRadiation = false; + } + }, + { + name: "1s half-life", + link: `1s half-life`, + description: "nails are made of lithium-8
damage occurs after 1 second", + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.isNailRadiation && !tech.isSlowRadiation + }, + requires: "irradiated nails, not 6s half-life", + effect() { + tech.isFastRadiation = true; + }, + remove() { + tech.isFastRadiation = false; + } + }, + { + name: "shotgun spin-statistics", + link: `shotgun spin-statistics`, + description: "immune to harm while firing the shotgun
shotgun has 50% fewer shots", + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.haveGunCheck("shotgun") + }, + requires: "shotgun", + effect() { + tech.isShotgunImmune = true; - //fill array of available bots - const notUpgradedBots = [] - const num = 2 - notUpgradedBots.push(() => { - tech.giveTech("nail-bot upgrade") - for (let i = 0; i < num; i++) { - b.nailBot() - tech.nailBotCount++; - } - simulation.makeTextLog(`tech.isNailBotUpgrade = true`) - }) - notUpgradedBots.push(() => { - tech.giveTech("foam-bot upgrade") - for (let i = 0; i < num; i++) { - b.foamBot() - tech.foamBotCount++; - } - simulation.makeTextLog(`tech.isFoamBotUpgrade = true`) - }) - notUpgradedBots.push(() => { - tech.giveTech("boom-bot upgrade") - for (let i = 0; i < num; i++) { - b.boomBot() - tech.boomBotCount++; - } - simulation.makeTextLog(`tech.isBoomBotUpgrade = true`) - }) - notUpgradedBots.push(() => { - tech.giveTech("laser-bot upgrade") - for (let i = 0; i < num; i++) { - b.laserBot() - tech.laserBotCount++; - } - simulation.makeTextLog(`tech.isLaserBotUpgrade = true`) - }) - notUpgradedBots.push(() => { - tech.giveTech("orbital-bot upgrade") - for (let i = 0; i < num; i++) { - b.orbitBot() - tech.orbitBotCount++; - } - simulation.makeTextLog(`tech.isOrbitalBotUpgrade = true`) - }) - for (let i = 0; i < 2; i++) { //double chance for dynamo-bot, since it's very good for assembler - notUpgradedBots.push(() => { - tech.giveTech("dynamo-bot upgrade") - for (let i = 0; i < num; i++) { - b.dynamoBot() - tech.dynamoBotCount++; - } - simulation.makeTextLog(`tech.isDynamoBotUpgrade = true`) - }) - } - notUpgradedBots[Math.floor(Math.random() * notUpgradedBots.length)]() //choose random function from the array and run it - }, - remove() {} - }, - { - name: "mycelium manufacturing", - link: `mycelium manufacturing`, - description: `use ${powerUps.orb.research(1)}to repurpose molecular assembler
excess energy used to grow spores`, - // description: "use 3 research to repurpose assembler
excess energy used to grow spores", - isFieldTech: true, - maxCount: 1, - count: 0, - frequency: 3, - frequencyDefault: 3, - allowed() { - return (build.isExperimentSelection || powerUps.research.count > 0) && m.fieldUpgrades[m.fieldMode].name === "molecular assembler" && !(tech.isMissileField || tech.isIceField || tech.isFastDrones || tech.isDroneGrab || tech.isDroneRadioactive || tech.isDroneTeleport) - }, - requires: "molecular assembler, no other manufacturing, no drone tech", - effect() { - if (!build.isExperimentSelection) { - for (let i = 0; i < 1; i++) { - if (powerUps.research.count > 0) powerUps.research.changeRerolls(-1) - } - } - tech.isSporeField = true; - }, - remove() { - tech.isSporeField = false; - if (this.count > 0) powerUps.research.changeRerolls(1) + //cut current ammo by 1/2 + for (i = 0, len = b.guns.length; i < len; i++) { //find which gun + if (b.guns[i].name === "shotgun") { + b.guns[i].ammo = Math.ceil(b.guns[i].ammo * 0.5); + b.guns[i].ammoPack = b.guns[i].defaultAmmoPack * 0.5 + break; } - }, - { - name: "missile manufacturing", - link: `missile manufacturing`, - description: `use ${powerUps.orb.research(1)}to repurpose molecular assembler
excess energy used to construct missiles`, - // description: "use 3 research to repurpose assembler
excess energy used to construct missiles", - isFieldTech: true, - maxCount: 1, - count: 0, - frequency: 3, - frequencyDefault: 3, - allowed() { - return (build.isExperimentSelection || powerUps.research.count > 0) && m.maxEnergy > 0.5 && m.fieldUpgrades[m.fieldMode].name === "molecular assembler" && !(tech.isSporeField || tech.isIceField || tech.isFastDrones || tech.isDroneGrab || tech.isDroneRadioactive || tech.isDroneTeleport) - }, - requires: "molecular assembler, no other manufacturing, no drone tech", - effect() { - if (!build.isExperimentSelection) { - for (let i = 0; i < 1; i++) { - if (powerUps.research.count > 0) powerUps.research.changeRerolls(-1) - } - } - tech.isMissileField = true; - }, - remove() { - tech.isMissileField = false; - if (this.count > 0) powerUps.research.changeRerolls(1) - } - }, - { - name: "ice IX manufacturing", - link: `ice IX manufacturing`, - description: `use ${powerUps.orb.research(1)}to repurpose molecular assembler
excess energy used to condense ice IX`, - // description: "use 3 research to repurpose assembler
excess energy used to condense ice IX", - isFieldTech: true, - maxCount: 1, - count: 0, - frequency: 3, - frequencyDefault: 3, - allowed() { - return (build.isExperimentSelection || powerUps.research.count > 0) && m.fieldUpgrades[m.fieldMode].name === "molecular assembler" && !(tech.isSporeField || tech.isMissileField || tech.isFastDrones || tech.isDroneGrab || tech.isDroneRadioactive || tech.isDroneTeleport) - }, - requires: "molecular assembler, no other manufacturing, no drone tech", - effect() { - if (!build.isExperimentSelection) { - for (let i = 0; i < 1; i++) { - if (powerUps.research.count > 0) powerUps.research.changeRerolls(-1) - } - } - tech.isIceField = true; - }, - remove() { - tech.isIceField = false; - if (this.count > 0) powerUps.research.changeRerolls(1) - } - }, - { - name: "pair production", - description: "picking up a power up gives you 200 energy", - isFieldTech: true, - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return m.fieldUpgrades[m.fieldMode].name === "molecular assembler" || m.fieldUpgrades[m.fieldMode].name === "pilot wave" - }, - requires: "molecular assembler or pilot wave", - effect: () => { - tech.isMassEnergy = true // used in m.grabPowerUp - m.energy += 2 - }, - remove() { - tech.isMassEnergy = false; - } - }, - // { - // name: "thermal reservoir", - // description: "increase your plasma damage by 100%
plasma temporarily lowers health not energy", - // isFieldTech: true, - // maxCount: 1, - // count: 0, - // frequency: 2, - // allowed() { - // return m.fieldUpgrades[m.fieldMode].name === "plasma torch" && !tech.isEnergyHealth - // }, - // requires: "plasma torch, not mass-energy equivalence", - // effect() { - // tech.isPlasmaRange += 0.27; - // }, - // remove() { - // tech.isPlasmaRange = 1; - // } - // }, - { - name: "degenerate matter", - description: "reduce harm by 60% while your field is active", - isFieldTech: true, - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return (m.fieldUpgrades[m.fieldMode].name === "plasma torch" || m.fieldUpgrades[m.fieldMode].name === "perfect diamagnetism" || m.fieldUpgrades[m.fieldMode].name === "pilot wave") && !tech.isEnergyHealth - }, - requires: "perfect diamagnetism, pilot wave, plasma, not mass-energy", - effect() { - tech.isHarmReduce = true - }, - remove() { - tech.isHarmReduce = false; - } - }, - { - name: "tokamak", - description: "throwing a block converts it into energy
and a pulsed fusion explosion", - isFieldTech: true, - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return m.fieldUpgrades[m.fieldMode].name === "plasma torch" || m.fieldUpgrades[m.fieldMode].name === "molecular assembler" - }, - requires: "plasma torch or molecular assembler", - effect() { - tech.isTokamak = true; - }, - remove() { - tech.isTokamak = false; - } - }, - { - name: "plasma-bot", - link: `plasma-bot`, - description: "remove your field to build a bot
that uses energy to emit plasma", - isFieldTech: true, - maxCount: 1, - count: 0, - frequency: 3, - frequencyDefault: 3, - isBot: true, - isBotTech: true, - allowed() { - return m.fieldUpgrades[m.fieldMode].name === "plasma torch" && (build.isExperimentSelection || powerUps.research.count > 0) - }, - requires: "plasma torch", - effect() { - tech.plasmaBotCount++; - b.plasmaBot(); - if (build.isExperimentSelection) { - document.getElementById("field-" + m.fieldMode).classList.remove("build-field-selected"); - document.getElementById("field-0").classList.add("build-field-selected"); - } - m.setField("field emitter") - }, - remove() { - if (this.count > 0) { - tech.plasmaBotCount = 0; - b.clearPermanentBots(); - b.respawnBots(); - if (m.fieldMode === 0) { - m.setField("plasma torch") - if (build.isExperimentSelection) { - document.getElementById("field-0").classList.remove("build-field-selected"); - document.getElementById("field-" + m.fieldMode).classList.add("build-field-selected"); - } - } + } + simulation.updateGunHUD(); + }, + remove() { + if (tech.isShotgunImmune) { + tech.isShotgunImmune = false; + for (i = 0, len = b.guns.length; i < len; i++) { //find which gun + if (b.guns[i].name === "shotgun") { + b.guns[i].ammoPack = b.guns[i].defaultAmmoPack; + b.guns[i].ammo = Math.ceil(b.guns[i].ammo * 2); + break; } } - }, - { - name: "plasma jet", - link: `plasma jet`, - description: `use ${powerUps.orb.research(1)} to increase plasma torch range 50%`, - // description: "use 1 research to
increase plasma torch's range by 50%", - isFieldTech: true, - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return (tech.plasmaBotCount || m.fieldUpgrades[m.fieldMode].name === "plasma torch") && (build.isExperimentSelection || powerUps.research.count > 0) - }, - requires: "plasma torch", - effect() { - tech.isPlasmaRange += 0.5; + simulation.updateGunHUD(); + } + } + }, + { + name: "Newton's 3rd law", + description: "shotgun recoil is increased
decrease shotgun delay after firing by 66%", + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.haveGunCheck("shotgun") && !tech.isShotgunReversed + }, + requires: "shotgun, not Noether violation", + effect() { + tech.isShotgunRecoil = true; + }, + remove() { + tech.isShotgunRecoil = false; + } + }, + { + name: "Noether violation", + link: `Noether violation`, + description: "increase shotgun damage 60%
their recoil is increased and reversed", + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return (tech.haveGunCheck("shotgun")) && !tech.isShotgunRecoil + }, + requires: "shotgun, not Newton's 3rd law", + effect() { + tech.isShotgunReversed = true; + }, + remove() { + tech.isShotgunReversed = false; + } + }, + { + name: "shotgun slug", + description: "shotgun lobs 1 huge bullet", + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.haveGunCheck("shotgun") && !tech.isNailShot && !tech.isIncendiary && !tech.isIceShot && !tech.isFoamShot && !tech.isWormShot && !tech.isNeedleShot + }, + requires: "shotgun, not nail-shot, foam-shot, worm-shot, ice-shot, needle-shot", + effect() { + tech.isSlugShot = true; + }, + remove() { + tech.isSlugShot = false; + } + }, + { + name: "nail-shot", + link: `nail-shot`, + description: "shotgun fires 17 nails", + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.haveGunCheck("shotgun") && !tech.isIncendiary && !tech.isSlugShot && !tech.isIceShot && !tech.isFoamShot && !tech.isWormShot && !tech.isNeedleShot + }, + requires: "shotgun, not incendiary, slug, foam-shot, worm-shot, ice-shot, needle-shot", + effect() { + tech.isNailShot = true; + }, + remove() { + tech.isNailShot = false; + } + }, + { + name: "needle-shot", + link: `needle-shot`, + description: "shotgun propels 11 mob piercing needles", + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.haveGunCheck("shotgun") && !tech.isNailShot && !tech.isIncendiary && !tech.isSlugShot && !tech.isFoamShot && !tech.isWormShot && !tech.isIceShot + }, + requires: "shotgun, not incendiary, nail-shot, slug, foam-shot, worm-shot, ice-shot", + effect() { + tech.isNeedleShot = true; + }, + remove() { + tech.isNeedleShot = false; + } + }, + { + name: "worm-shot", + link: `worm-shot`, + description: "shotgun hatches 3-4 mob seeking worms
worms benefit from spore technology", //
worms seek out nearby mobs + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.haveGunCheck("shotgun") && !tech.isNailShot && !tech.isIncendiary && !tech.isSlugShot && !tech.isIceShot && !tech.isFoamShot && !tech.isNeedleShot + }, + requires: "shotgun, not incendiary, nail-shot, slug, foam-shot, ice-shot, needle-shot", + effect() { + tech.isWormShot = true; + }, + remove() { + tech.isWormShot = false; + } + }, + { + name: "foam-shot", + link: `foam-shot`, + description: "shotgun sprays 13 sticky foam bubbles", + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.haveGunCheck("shotgun") && !tech.isNailShot && !tech.isIncendiary && !tech.isSlugShot && !tech.isIceShot && !tech.isWormShot && !tech.isNeedleShot + }, + requires: "shotgun, not incendiary, nail-shot, slug, worm-shot, ice-shot, needle-shot", + effect() { + tech.isFoamShot = true; + }, + remove() { + tech.isFoamShot = false; + } + }, + { + name: "ice-shot", + link: `ice-shot`, + description: "shotgun grows 15 freezing ice IX crystals", + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.haveGunCheck("shotgun") && !tech.isNailShot && !tech.isIncendiary && !tech.isSlugShot && !tech.isFoamShot && !tech.isWormShot && !tech.isNeedleShot + }, + requires: "shotgun, not incendiary, nail-shot, slug, foam-shot, worm-shot", + effect() { + tech.isIceShot = true; + }, + remove() { + tech.isIceShot = false; + } + }, + { + name: "supertemporal", + link: `supertemporal`, + description: "fire super ball from the same point in space
but separated by 0.1 seconds in time", + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.haveGunCheck("super balls") && !tech.oneSuperBall + }, + requires: "super balls, but not the tech super ball", + effect() { + tech.superBallDelay = true + for (i = 0, len = b.guns.length; i < len; i++) { //find which gun + if (b.guns[i].name === "super balls") b.guns[i].chooseFireMethod() + } + }, + remove() { + if (tech.superBallDelay) { + tech.superBallDelay = false; + for (i = 0, len = b.guns.length; i < len; i++) { //find which gun + if (b.guns[i].name === "super balls") b.guns[i].chooseFireMethod() + } + } + } + }, + { + name: "super ball", + description: "fire just 1 large super ball
that stuns mobs for 3 second", + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.haveGunCheck("super balls") && tech.missileCount === 1 && !tech.superBallDelay + }, + requires: "super balls, but not MIRV or supertemporal", + effect() { + tech.oneSuperBall = true; + for (i = 0, len = b.guns.length; i < len; i++) { //find which gun + if (b.guns[i].name === "super balls") b.guns[i].chooseFireMethod() + } + }, + remove() { + if (tech.oneSuperBall) { + tech.oneSuperBall = false; + for (i = 0, len = b.guns.length; i < len; i++) { //find which gun + if (b.guns[i].name === "super balls") b.guns[i].chooseFireMethod() + } + } + } + }, + { + name: "super sized", + description: `increase super ball radius by 14%
increases damage by about 27%`, + isGunTech: true, + maxCount: 9, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.haveGunCheck("super balls") + }, + requires: "super balls", + effect() { + tech.bulletSize += 0.14 + }, + remove() { + tech.bulletSize = 1; + } + }, + { + name: "phase velocity", + description: "matter wave propagates faster through solids
increase matter wave damage by 15%", + // description: "matter wave propagates faster through solids
up by 3000% in the map and 760% in blocks", + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.haveGunCheck("matter wave") && !tech.isLongitudinal + }, + requires: "matter wave, not phonon", + effect() { + tech.isPhaseVelocity = true; + }, + remove() { + tech.isPhaseVelocity = false; + } + }, + { + name: "bound state", + description: "wave packets reflect backwards 2 times
range is reduced by 25%", + isGunTech: true, + maxCount: 9, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.haveGunCheck("matter wave") + }, + requires: "matter wave", + effect() { + tech.waveReflections += 2 + }, + remove() { + tech.waveReflections = 1 + } + }, + { + name: "amplitude", + description: "wave packet amplitude is 33% higher
wave damage is increased by 50%", + isGunTech: true, + maxCount: 3, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.haveGunCheck("matter wave") + }, + requires: "matter wave", + effect() { + tech.waveFrequency *= 0.66 + tech.wavePacketDamage *= 1.5 + }, + remove() { + tech.waveFrequency = 0.2 + tech.wavePacketDamage = 1 + } + }, + { + name: "propagation", + description: "wave packet propagation speed is 20% slower
wave damage is increased by 50%", + isGunTech: true, + maxCount: 9, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.haveGunCheck("matter wave") + }, + requires: "matter wave", + effect() { + tech.waveBeamSpeed *= 0.8; + tech.waveBeamDamage += 1.5 * 0.5 //this sets base matter wave damage, not used by arcs or circles + }, + remove() { + tech.waveBeamSpeed = 10; + tech.waveBeamDamage = 1.5 //this sets base matter wave damage, not used by arcs or circles + } + }, + { + name: "phonon", //longitudinal //gravitational wave? + description: "matter wave emits low frequency, high damage
expanding arcs that propagate through solids", + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.haveGunCheck("matter wave") && !tech.isPhaseVelocity && !tech.isBulletTeleport + }, + requires: "matter wave, not phase velocity, uncertainty principle", + effect() { + tech.isLongitudinal = true; + for (i = 0, len = b.guns.length; i < len; i++) { //find which gun + if (b.guns[i].name === "matter wave") { + b.guns[i].chooseFireMethod() + b.guns[i].ammoPack = b.guns[i].defaultAmmoPack / 8 + b.guns[i].ammo = Math.ceil(b.guns[i].ammo / 8); + simulation.updateGunHUD(); + break + } + } + }, + remove() { + if (tech.isLongitudinal) { + for (i = 0, len = b.guns.length; i < len; i++) { //find which gun + if (b.guns[i].name === "matter wave") { + tech.isLongitudinal = false; + b.guns[i].chooseFireMethod() + b.guns[i].ammoPack = b.guns[i].defaultAmmoPack + b.guns[i].ammo = Math.ceil(b.guns[i].ammo * 8); + simulation.updateGunHUD(); + break + } + } + } + tech.isLongitudinal = false; + } + }, + { + name: "isotropic radiator", + description: "matter wave expands in all directions
range reduced 40% and damage increased 50%", + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.isLongitudinal + }, + requires: "matter wave, phonon", + effect() { + tech.is360Longitudinal = true; + for (i = 0, len = b.guns.length; i < len; i++) { //find which gun + if (b.guns[i].name === "matter wave") { + b.guns[i].chooseFireMethod() + break + } + } + }, + remove() { + tech.is360Longitudinal = false; + for (i = 0, len = b.guns.length; i < len; i++) { //find which gun + if (b.guns[i].name === "matter wave") { + b.guns[i].chooseFireMethod() + break + } + } + } + }, + { + name: "cruise missile", + description: "missiles travel 63% slower,
but have a 50% larger explosive payload", + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.haveGunCheck("missiles") || tech.isMissileField || tech.missileBotCount + }, + requires: "missiles", + effect() { + tech.missileSize = true + }, + remove() { + tech.missileSize = false + } + }, + { + name: "missile-bot", + link: `missile-bot`, + description: "gain a bot that fires missiles at mobs
remove your missile gun", + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + isBot: true, + isBotTech: true, + allowed() { + return tech.haveGunCheck("missiles", false) + }, + requires: "missiles", + effect() { + tech.missileBotCount++; + b.missileBot(); + if (tech.haveGunCheck("missiles", false)) b.removeGun("missiles") //remove your last gun + }, + remove() { + if (this.count) { + tech.missileBotCount = 0; + b.clearPermanentBots(); + b.respawnBots(); + if (!tech.haveGunCheck("missiles", false)) b.giveGuns("missiles") + } + } + }, + { + name: "MIRV", + description: "fire +1 missile, grenade, and super ball
decrease explosion radius up to 10%", + isGunTech: true, + maxCount: 9, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.haveGunCheck("missiles") || tech.missileBotCount || tech.haveGunCheck("grenades") || (tech.haveGunCheck("super balls") && !tech.oneSuperBall) + }, + requires: "missiles, grenades, super balls, not super ball", + effect() { + tech.missileCount++; + }, + remove() { + tech.missileCount = 1; + } + }, + { + name: "rocket-propelled grenade", + description: "grenades rapidly accelerate forward
map collisions trigger an explosion", + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.haveGunCheck("grenades") + }, + requires: "grenades", + effect() { + tech.isRPG = true; + b.setGrenadeMode() + }, + remove() { + tech.isRPG = false; + b.setGrenadeMode() + } + }, + { + name: "vacuum bomb", + description: "grenades fire slower, explode bigger
and, suck everything towards them", + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.haveGunCheck("grenades") && !tech.isNeutronBomb + }, + requires: "grenades, not neutron bomb", + effect() { + tech.isVacuumBomb = true; + b.setGrenadeMode() + }, + remove() { + tech.isVacuumBomb = false; + b.setGrenadeMode() + } + }, + { + name: "chain reaction", + description: "increase grenade radius and damage 33%
blocks caught in explosions also explode", + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.isVacuumBomb && !tech.isExplodeRadio + }, + requires: "grenades, vacuum bomb, not iridium-192", + effect() { + tech.isBlockExplode = true; //chain reaction + }, + remove() { + tech.isBlockExplode = false; + } + }, + { + name: "neutron bomb", + description: "grenades are irradiated with Cf-252
does damage, harm, and drains energy", + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.haveGunCheck("grenades") && !tech.fragments && !tech.isVacuumBomb + }, + requires: "grenades, not fragmentation, vacuum bomb", + effect() { + tech.isNeutronBomb = true; + b.setGrenadeMode() + }, + remove() { + tech.isNeutronBomb = false; + b.setGrenadeMode() + } + }, + { + name: "vacuum permittivity", + description: "increase radioactive range by 20%
objects in range of the bomb are slowed", + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.isNeutronBomb + }, + requires: "grenades, neutron bomb", + effect() { + tech.isNeutronSlow = true + }, + remove() { + tech.isNeutronSlow = false + } + }, + { + name: "booby trap", + description: "drop a mine after picking up a power up
+53% JUNK to the potential tech pool", + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.haveGunCheck("mine") + }, + requires: "mines", + effect() { + tech.isMineDrop = true; + if (tech.isMineDrop) b.mine(m.pos, { x: 0, y: 0 }, 0) + this.refundAmount += tech.addJunkTechToPool(0.53) + }, + refundAmount: 0, + remove() { + tech.isMineDrop = false; + if (this.count > 0 && this.refundAmount > 0) { + tech.removeJunkTechFromPool(this.refundAmount) + this.refundAmount = 0 + } + } + }, + { + name: "laser-mines", + link: `laser-mines`, + description: "mines laid while you are crouched
use energy to emit 3 unaimed lasers", + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.haveGunCheck("mine") + }, + requires: "mines", + effect() { + tech.isLaserMine = true; + }, + remove() { + tech.isLaserMine = false; + } + }, + { + name: "sentry", + description: "instead of detonating, mines target mobs
with a stream of nails for about 17 seconds", + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.haveGunCheck("mine") + }, + requires: "mines", + effect() { + tech.isMineSentry = true; + }, + remove() { + tech.isMineSentry = false; + } + }, + { + name: "blast mines", + link: `blast mines`, + description: "when a mine activates
it stuns nearby mobs for 2-4 seconds", + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.haveGunCheck("mine") + }, + requires: "mines", + effect() { + tech.isMineStun = true; + }, + remove() { + tech.isMineStun = false; + } + }, + { + name: "mycelial fragmentation", + link: `tinsellated flagella`, + description: "sporangium release 6 extra spores
during their growth phase", + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.haveGunCheck("spores") + }, + requires: "spore gun", + effect() { + tech.isSporeGrowth = true + }, + remove() { + tech.isSporeGrowth = false + } + }, + { + name: "tinsellated flagella", + link: `tinsellated flagella`, + description: "sporangium release 2 more spores
spores accelerate 40% faster", + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.haveGunCheck("spores") || tech.sporesOnDeath > 0 || tech.isSporeField + }, + requires: "spore gun, spores", + effect() { + tech.isFastSpores = true + }, + remove() { + tech.isFastSpores = false + } + }, + { + name: "cryodesiccation", + description: "sporangium release 2 more spores
spores freeze mobs for 1.5 second", + //
spores do 1/3 damage + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.haveGunCheck("spores") || tech.sporesOnDeath > 0 || tech.isSporeField || tech.isWormShot + }, + requires: "spore gun, spores or worms", + effect() { + tech.isSporeFreeze = true + }, + remove() { + tech.isSporeFreeze = false + } + }, + { + name: "diplochory", + description: "spores use you for dispersal
until they locate a viable host", + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.haveGunCheck("spores") || tech.sporesOnDeath > 0 || tech.isSporeField || tech.isWormShot + }, + requires: "spore gun, spores or worms", + effect() { + tech.isSporeFollow = true + }, + remove() { + tech.isSporeFollow = false + } + }, + { + name: "mutualism", + description: "increase spore damage by 150%
spores borrow 0.5 health until they die", + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return (tech.haveGunCheck("spores") || tech.sporesOnDeath > 0 || tech.isSporeField) && !tech.isEnergyHealth || tech.isWormShot + }, + requires: "spore gun, spores, worms, not mass-energy", + effect() { + tech.isMutualism = true + }, + remove() { + tech.isMutualism = false + } + }, + { + name: "nematodes", + description: "spores develop into 1/2 as many worms
worms do 250% more damage", + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 4, + frequencyDefault: 4, + allowed() { + return tech.haveGunCheck("spores") || tech.sporesOnDeath > 0 || tech.isSporeField || tech.isWormholeSpores + }, + requires: "spore gun, spores", + effect() { + tech.isSporeWorm = true + }, + remove() { + tech.isSporeWorm = false + } + }, + { + name: "necrophage", + description: "if worms kill their target
they reset their lifespan", + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.isSporeWorm || tech.isWormShot + }, + requires: "spore gun, worms", + effect() { + tech.wormSurviveDmg = true + }, + remove() { + tech.wormSurviveDmg = false + } + }, + { + name: "fault tolerance", + description: "spawn 8 drones that last forever
remove your drone gun", + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return tech.haveGunCheck("drones", false) + }, + requires: "drones", + effect() { + const num = 8 + tech.isForeverDrones += num + if (tech.haveGunCheck("drones", false)) b.removeGun("drones") + //spawn drones + if (tech.isDroneRadioactive) { + for (let i = 0; i < num * 0.25; i++) { + b.droneRadioactive({ x: m.pos.x + 30 * (Math.random() - 0.5), y: m.pos.y + 30 * (Math.random() - 0.5) }, 5) + bullet[bullet.length - 1].endCycle = Infinity + } + } else { + for (let i = 0; i < num; i++) { + b.drone({ x: m.pos.x + 30 * (Math.random() - 0.5), y: m.pos.y + 30 * (Math.random() - 0.5) }, 5) + bullet[bullet.length - 1].endCycle = Infinity + } + } + }, + remove() { + tech.isForeverDrones = 0 + if (this.count && !tech.haveGunCheck("drones", false)) b.giveGuns("drones") + } + }, + { + name: "reduced tolerances", + link: `reduced tolerances`, + description: `increase drones per ${powerUps.orb.ammo()} or energy by 66%
reduce the average drone lifetime by 40%`, + isGunTech: true, + maxCount: 3, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return !tech.isDroneRadioactive && (tech.haveGunCheck("drones") || (m.fieldUpgrades[m.fieldMode].name === "molecular assembler" && !(tech.isSporeField || tech.isMissileField || tech.isIceField))) + }, + requires: "drones, not irradiated drones", + effect() { + tech.droneCycleReduction = Math.pow(0.6, 1 + this.count) + tech.droneEnergyReduction = Math.pow(0.333, 1 + this.count) + for (i = 0, len = b.guns.length; i < len; i++) { //find which gun + if (b.guns[i].name === "drones") { + const scale = Math.pow(3, this.count + 1) + b.guns[i].ammoPack = b.guns[i].defaultAmmoPack * scale + } + } + }, + remove() { + tech.droneCycleReduction = 1 + tech.droneEnergyReduction = 1 + for (i = 0, len = b.guns.length; i < len; i++) { //find which gun + if (b.guns[i].name === "drones") b.guns[i].ammoPack = b.guns[i].defaultAmmoPack + } + } + }, + { + name: "delivery drone", + description: "if a drone picks up a power up,
it becomes larger, faster, and more durable", + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return !tech.isExtraMaxEnergy && (tech.haveGunCheck("drones") || tech.isForeverDrones || (m.fieldUpgrades[m.fieldMode].name === "molecular assembler" && !(tech.isSporeField || tech.isMissileField || tech.isIceField))) + }, + requires: "drones, not permittivity", + effect() { + tech.isDroneGrab = true + }, + remove() { + tech.isDroneGrab = false + } + }, + { + name: "drone repair", + link: `drone repair`, + description: "after a drone expires it redeploys
for a 25% chance to use 1 drone ammo", + // description: "broken drones repair if the drone gun is active
repairing has a 25% chance to use 1 drone", + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.haveGunCheck("drones") + }, + requires: "drones", + effect() { + tech.isDroneRespawn = true + }, + remove() { + tech.isDroneRespawn = false + } + }, + { + name: "brushless motor", + description: "drones rapidly rush towards their target
increase drone collision damage by 33%", + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 3, + frequencyDefault: 3, + allowed() { + return (tech.haveGunCheck("drones") || tech.isForeverDrones || (m.fieldUpgrades[m.fieldMode].name === "molecular assembler" && !(tech.isSporeField || tech.isMissileField || tech.isIceField))) && !tech.isDroneRadioactive && !tech.isIncendiary + }, + requires: "drones, molecular assembler, not irradiated drones, incendiary", + effect() { + tech.isDroneTeleport = true + }, + remove() { + tech.isDroneTeleport = false + } + }, + { + name: "axial flux motor", + description: "drones can rush 66% more often
increase drone collision damage by 44%", + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.isDroneTeleport + }, + requires: "drones, brushless motor", + effect() { + tech.isDroneFastLook = true + }, + remove() { + tech.isDroneFastLook = false + } + }, + { + name: "irradiated drones", + link: `irradiated drones`, + description: `the space around drones is irradiated
reduce drones per ${powerUps.orb.ammo()} or energy 75%`, + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.droneCycleReduction === 1 && !tech.isIncendiary && !tech.isDroneTeleport && (tech.haveGunCheck("drones") || tech.isForeverDrones || (m.fieldUpgrades[m.fieldMode].name === "molecular assembler" && !(tech.isSporeField || tech.isMissileField || tech.isIceField))) + }, + requires: "drones, not reduced tolerances, incendiary, torque bursts", + effect() { + tech.isDroneRadioactive = true + for (i = 0, len = b.guns.length; i < len; i++) { //find which gun + if (b.guns[i].name === "drones") { + b.guns[i].ammoPack = b.guns[i].defaultAmmoPack * 0.25 + b.guns[i].ammo = Math.ceil(b.guns[i].ammo * 0.25) + simulation.makeGunHUD(); + } + } + }, + remove() { + if (tech.isDroneRadioactive) { + tech.isDroneRadioactive = false + for (i = 0, len = b.guns.length; i < len; i++) { //find which gun + if (b.guns[i].name === "drones") { + b.guns[i].ammoPack = b.guns[i].defaultAmmoPack + b.guns[i].ammo = b.guns[i].ammo * 4 + simulation.makeGunHUD(); + } + } + } + } + }, + { + name: "beta radiation", //"control rod ejection", + description: "reduce the average drone lifetime by 50%
increase radiation damage by 100%", + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.isDroneRadioactive + }, + requires: "drones irradiated drones", + effect() { + tech.droneRadioDamage = 2 + }, + remove() { + tech.droneRadioDamage = 1 + } + }, + { + name: "orthocyclic winding", + link: `orthocyclic winding`, + description: "drones accelerate 66% faster
increase radiation damage by 33%", + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.isDroneRadioactive + }, + requires: "drones, irradiated drones", + effect() { + tech.isFastDrones = true + }, + remove() { + tech.isFastDrones = false + } + }, + { + name: "electrostatic induction", + description: "foam bubbles are electrically charged
causing attraction to nearby mobs", + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return !tech.isBulletTeleport && (tech.haveGunCheck("foam") || tech.foamBotCount > 1 || tech.isFoamShot) + }, + requires: "foam, not uncertainty", + effect() { + tech.isFoamAttract = true + }, + remove() { + tech.isFoamAttract = false + } + }, + { + name: "uncertainty principle", + description: "foam and wave particle positions are random
increase their damage by 43%", + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return (!tech.isFoamAttract && (tech.haveGunCheck("foam") || tech.foamBotCount > 1 || tech.isFoamShot)) || (tech.haveGunCheck("matter wave") && !tech.isLongitudinal) + }, + requires: "foam, not electrostatic induction, matter wave, not phonon", + effect() { + tech.isBulletTeleport = true + }, + remove() { + tech.isBulletTeleport = false; + } + }, + { + name: "necrophoresis", + description: "foam bubbles grow and split into 3 copies
when the mob they are stuck to dies", + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.haveGunCheck("foam") || tech.foamBotCount > 1 || tech.isFoamShot + }, + requires: "foam", + effect() { + tech.isFoamGrowOnDeath = true + }, + remove() { + tech.isFoamGrowOnDeath = false; + } + }, + { + name: "aerogel", + description: "foam bubbles float and dissipate 50% faster
increase foam damage per second by 150%", + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.haveGunCheck("foam") || tech.foamBotCount > 1 || tech.isFoamShot + }, + requires: "foam", + effect() { + tech.isFastFoam = true + tech.foamGravity = -0.0003 + }, + remove() { + tech.isFastFoam = false; + tech.foamGravity = 0.00008 + } + }, + { + name: "quantum foam", + description: "foam gun fires 0.25 seconds into the future
increase foam gun damage by 66%", + isGunTech: true, + maxCount: 9, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.haveGunCheck("foam") + }, + requires: "foam", + effect() { + tech.foamFutureFire++ + }, + remove() { + tech.foamFutureFire = 0; + } + }, + { + name: "foam fractionation", + description: "foam gun bubbles are 100% larger
when you have below 300 foam", + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.haveGunCheck("foam") + }, + requires: "foam", + effect() { + tech.isAmmoFoamSize = true + }, + remove() { + tech.isAmmoFoamSize = false; + } + }, + { + name: "surfactant", + description: "trade your foam gun for 2 foam-bots
and upgrade all bots to foam
", + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + isBot: true, + isBotTech: true, + isNonRefundable: true, + requires: "foam gun, not bot upgrades NOT EXPERIMENT MODE,", + allowed() { + return tech.haveGunCheck("foam", false) && !b.hasBotUpgrade() + }, + effect() { + tech.giveTech("foam-bot upgrade") + for (let i = 0; i < 2; i++) { + b.foamBot() + tech.foamBotCount++; + } + simulation.makeTextLog(`tech.isFoamBotUpgrade = true`) + if (tech.haveGunCheck("foam", false)) b.removeGun("foam") + }, + remove() { + // if (this.count) { + // b.clearPermanentBots(); + // b.respawnBots(); + // if (!tech.haveGunCheck("foam")) b.giveGuns("foam") + // } + } + }, + { + name: "filament", + description: "increase the length of your harpoon's rope
by 1% per harpoon ammo", + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.haveGunCheck("harpoon") + }, + requires: "harpoon", + effect() { + tech.isFilament = true; + }, + remove() { + tech.isFilament = false; + } + }, + { + name: "unaaq", + link: `unaaq`, //https://en.wikipedia.org/wiki/Weapon + description: "increase the size of your harpoon
by 10% of the square root of its ammo", + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.haveGunCheck("harpoon") + }, + requires: "harpoon", + effect() { + tech.isLargeHarpoon = true; + }, + remove() { + tech.isLargeHarpoon = false; + } + }, + { + name: "toggling harpoon", + description: "increase the damage of your next harpoon
by 800% after using it to collect a power up", + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.haveGunCheck("harpoon") + }, + requires: "harpoon", + effect() { + tech.isHarpoonPowerUp = true + }, + remove() { + tech.isHarpoonPowerUp = false + tech.harpoonDensity = 0.008 + } + }, + { + name: "reticulum", + description: "fire +1 harpoon, but energy cost
to retract also increases", + isGunTech: true, + maxCount: 9, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.haveGunCheck("harpoon") + }, + requires: "harpoon", + effect() { + tech.extraHarpoons++; + }, + remove() { + tech.extraHarpoons = 0; + } + }, + { + name: "railgun", + description: "firing the harpoon while crouched launches
a rod that is faster, larger, and more dense", + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.haveGunCheck("harpoon") + }, + requires: "railgun", + effect() { + tech.isRailGun = true; + }, + remove() { + tech.isRailGun = false; + } + }, + { + name: "half-wave rectifier", + description: "charging the railgun gives you energy
instead of draining it", + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.isRailGun + }, + requires: "harpoon, railgun", + effect() { + tech.isRailEnergyGain = true; + }, + remove() { + tech.isRailEnergyGain = false; + } + }, + // { + // name: "dielectric polarization", + // description: "firing the railgun damages nearby mobs", + // isGunTech: true, + // maxCount: 1, + // count: 0, + // frequency: 2, + // frequencyDefault: 2, + // allowed() { + // return tech.haveGunCheck("railgun") + // }, + // requires: "railgun", + // effect() { + // tech.isRailAreaDamage = true; + // }, + // remove() { + // tech.isRailAreaDamage = false; + // } + // }, + // { + // name: "aerodynamic heating", + // description: "railgun rod damage nearby mobs", + // isGunTech: true, + // maxCount: 1, + // count: 0, + // frequency: 2, + // frequencyDefault: 2, + // allowed() { + // return tech.haveGunCheck("railgun") + // }, + // requires: "railgun", + // effect() { + // tech.isRodAreaDamage = true; + // }, + // remove() { + // tech.isRodAreaDamage = false; + // } + // }, + { + name: "laser diode", + description: "all lasers drain 30% less energy
affects laser-gun, laser-bot, laser-mines, pulse", + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return (tech.haveGunCheck("laser") || tech.laserBotCount > 1 || tech.isLaserMine) && tech.laserDamage === 0.17 + }, + requires: "laser, not free-electron", + effect() { + tech.isLaserDiode = 0.70; //100%-37% + tech.laserColor = "rgb(0, 11, 255)" + tech.laserColorAlpha = "rgba(0, 11, 255,0.5)" + }, + remove() { + tech.isLaserDiode = 1; + tech.laserColor = "#f02" + tech.laserColorAlpha = "rgba(255, 0, 0, 0.5)" + } + }, + { + name: "free-electron laser", + description: "increase all laser damage by 200%
increase all laser energy drain by 250%", + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return (tech.haveGunCheck("laser") || tech.isLaserMine || tech.laserBotCount > 1) && !tech.isPulseLaser && tech.isLaserDiode === 1 + }, + requires: "laser, not pulse, diodes", + effect() { + tech.laserFieldDrain = 0.007 //base is 0.002 + tech.laserDamage = 0.51; //base is 0.16 + tech.laserColor = "#83f" + tech.laserColorAlpha = "rgba(136, 51, 255,0.5)" + }, + remove() { + tech.laserFieldDrain = 0.002; + tech.laserDamage = 0.17; //used in check on pulse and diode: tech.laserDamage === 0.16 + tech.laserColor = "#f00" + tech.laserColorAlpha = "rgba(255, 0, 0, 0.5)" + } + }, + { + name: "relativistic momentum", + description: "all lasers push mobs and blocks away
affects laser-gun, laser-bot, and laser-mines", + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return (tech.haveGunCheck("laser") && !tech.isPulseLaser) || tech.laserBotCount > 1 + }, + requires: "laser, not pulse", + effect() { + tech.isLaserPush = true; + }, + remove() { + tech.isLaserPush = false; + } + }, + { + name: "specular reflection", + description: "+2 reflection for all lasers
affects laser-gun, laser-bot, and laser-mines", + isGunTech: true, + maxCount: 3, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return (tech.haveGunCheck("laser") || tech.isLaserMine || tech.laserBotCount > 1) && !tech.isWideLaser && !tech.isPulseLaser && !tech.historyLaser + }, + requires: "laser, not diffuse beam, pulse, or slow light", + effect() { + tech.laserReflections += 2; + }, + remove() { + tech.laserReflections = 2; + } + }, + { + name: "diffraction grating", + description: `laser gains a diverging beam`, + isGunTech: true, + maxCount: 9, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.haveGunCheck("laser") && !tech.isWideLaser && !tech.isPulseAim && !tech.historyLaser + }, + requires: "laser gun, not neocognitron, diffuse beam, or slow light", + effect() { + tech.beamSplitter++ + for (i = 0, len = b.guns.length; i < len; i++) { //find which gun + if (b.guns[i].name === "laser") b.guns[i].chooseFireMethod() + } + }, + remove() { + if (tech.beamSplitter !== 0) { + tech.beamSplitter = 0 + for (i = 0, len = b.guns.length; i < len; i++) { //find which gun + if (b.guns[i].name === "laser") b.guns[i].chooseFireMethod() + } + } + } + }, + { + name: "diffuse beam", + link: `diffuse beam`, + description: "laser beam is wider and doesn't reflect
increase full beam damage by 200%", + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.haveGunCheck("laser") && tech.laserReflections < 3 && !tech.beamSplitter && !tech.isPulseLaser && !tech.historyLaser + }, + requires: "laser gun, not specular reflection, diffraction grating, slow light, pulse", + effect() { + if (tech.wideLaser === 0) tech.wideLaser = 3 + tech.isWideLaser = true; + for (i = 0, len = b.guns.length; i < len; i++) { //find which gun + if (b.guns[i].name === "laser") b.guns[i].chooseFireMethod() + } + }, + remove() { + if (tech.isWideLaser) { + // tech.wideLaser = 0 + tech.isWideLaser = false; + for (i = 0, len = b.guns.length; i < len; i++) { //find which gun + if (b.guns[i].name === "laser") b.guns[i].chooseFireMethod() + } + } + } + }, + { + name: "output coupler", + description: "widen diffuse laser beam by 40%
increase full beam damage by 40%", + isGunTech: true, + maxCount: 9, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.isWideLaser + }, + requires: "laser gun, diffuse beam", + effect() { + tech.wideLaser += 2 + for (i = 0, len = b.guns.length; i < len; i++) { //find which gun + if (b.guns[i].name === "laser") b.guns[i].chooseFireMethod() + } + }, + remove() { + if (tech.isWideLaser) { + tech.wideLaser = 3 + } else { + tech.wideLaser = 0 + } + for (i = 0, len = b.guns.length; i < len; i++) { //find which gun + if (b.guns[i].name === "laser") b.guns[i].chooseFireMethod() + } + } + }, + { + name: "slow light", + description: "laser beam is spread into your recent past
increase total beam damage by 300%", + isGunTech: true, + maxCount: 9, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.haveGunCheck("laser") && tech.laserReflections < 3 && !tech.beamSplitter && !tech.isWideLaser + }, + requires: "laser gun, not specular reflection, diffraction grating, diffuse beam", + effect() { + // this.description = `add 5 more laser beams into into your past` + tech.historyLaser++ + for (i = 0, len = b.guns.length; i < len; i++) { //find which gun + if (b.guns[i].name === "laser") b.guns[i].chooseFireMethod() + } + }, + remove() { + // this.description = "laser beam is spread into your recent past
increase total beam damage by 300%" + if (tech.historyLaser) { + tech.historyLaser = 0 + for (i = 0, len = b.guns.length; i < len; i++) { //find which gun + if (b.guns[i].name === "laser") b.guns[i].chooseFireMethod() + } + } + } + }, + { + name: "pulse", + description: "charge your energy and release it as a
laser pulse that initiates an explosion cluster", + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.haveGunCheck("laser") && tech.laserReflections < 3 && !tech.isWideLaser && tech.laserDamage === 0.17 + }, + requires: "laser gun, not specular reflection, diffuse, free-electron laser", + effect() { + tech.isPulseLaser = true; + for (i = 0, len = b.guns.length; i < len; i++) { //find which gun + if (b.guns[i].name === "laser") b.guns[i].chooseFireMethod() + } + }, + remove() { + if (tech.isPulseLaser) { + tech.isPulseLaser = false; + for (i = 0, len = b.guns.length; i < len; i++) { //find which gun + if (b.guns[i].name === "laser") b.guns[i].chooseFireMethod() + } + } + } + }, + { + name: "neocognitron", + description: "pulse automatically aims at a nearby mob", + isGunTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.isPulseLaser && !tech.beamSplitter + }, + requires: "laser gun, pulse, not diffraction grating", + effect() { + tech.isPulseAim = true; + }, + remove() { + tech.isPulseAim = false; + } + }, + //************************************************** + //************************************************** field + //************************************************** tech + //************************************************** + { + name: "zero point energy", + description: `use ${powerUps.orb.research(2)}to increase your max energy by 100`, + // description: "use 2 research to
increase your maximum energy by 74", + isFieldTech: true, + maxCount: 1, + count: 0, + frequency: 3, + frequencyDefault: 3, + allowed() { + return (m.fieldUpgrades[m.fieldMode].name === "standing wave" || m.fieldUpgrades[m.fieldMode].name === "pilot wave") && (build.isExperimentSelection || powerUps.research.count > 1) + }, + requires: "standing wave or pilot wave", + effect() { + tech.harmonicEnergy = 1 + m.setMaxEnergy() + for (let i = 0; i < 2; i++) { + if (powerUps.research.count > 0) powerUps.research.changeRerolls(-1) + } + }, + remove() { + tech.harmonicEnergy = 0; + m.setMaxEnergy() + if (this.count > 0) powerUps.research.changeRerolls(2) + } + }, + { + name: "spherical harmonics", + description: "standing wave oscillates in a 3rd dimension
increasing deflecting efficiency by 40%", + isFieldTech: true, + maxCount: 9, + count: 0, + frequency: 3, + frequencyDefault: 3, + allowed() { + return m.fieldUpgrades[m.fieldMode].name === "standing wave" + }, + requires: "standing wave", + effect() { + tech.harmonics++ + m.fieldShieldingScale = 1.3 * Math.pow(0.6, (tech.harmonics - 2)) + m.harmonicShield = m.harmonicAtomic + }, + remove() { + tech.harmonics = 2 + m.fieldShieldingScale = 1.3 * Math.pow(0.6, (tech.harmonics - 2)) + m.harmonicShield = m.harmonic3Phase + } + }, + { + name: "expansion", + description: "using standing wave field temporarily
expands its radius", + // description: "use energy to expand standing wave
the field slowly contracts when not used", + isFieldTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return m.fieldUpgrades[m.fieldMode].name === "standing wave" + }, + requires: "standing wave", + effect() { + tech.isStandingWaveExpand = true + }, + remove() { + tech.isStandingWaveExpand = false + m.harmonicRadius = 1 + } + }, + { + name: "bremsstrahlung", + description: "deflecting does damage to mobs", + isFieldTech: true, + maxCount: 9, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return m.fieldUpgrades[m.fieldMode].name === "standing wave" || m.fieldUpgrades[m.fieldMode].name === "perfect diamagnetism" + }, + requires: "standing wave, perfect diamagnetism", + effect() { + tech.blockDmg += 1.75 //if you change this value also update the for loop in the electricity graphics in m.pushMass + }, + remove() { + tech.blockDmg = 0; + } + }, + { + name: "triple point", + description: "the pressure from deflecting is used
to condense ice IX crystals", + isFieldTech: true, + maxCount: 9, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return m.fieldUpgrades[m.fieldMode].name === "standing wave" || m.fieldUpgrades[m.fieldMode].name === "perfect diamagnetism" + }, + requires: "standing wave, perfect diamagnetism", + effect() { + tech.blockingIce++ + }, + remove() { + tech.blockingIce = 0; + } + }, + { + name: "flux pinning", + description: "deflecting mobs with your field
stuns them for 4 seconds", + isFieldTech: true, + maxCount: 9, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return m.fieldUpgrades[m.fieldMode].name === "perfect diamagnetism" || m.fieldUpgrades[m.fieldMode].name === "standing wave" || m.fieldUpgrades[m.fieldMode].name === "molecular assembler" + }, + requires: "a field that can block", + effect() { + tech.isStunField += 240; + }, + remove() { + tech.isStunField = 0; + } + }, + { + name: "eddy current brake", + description: "project a field that limits the top speed of mobs
field radius scales with stored energy", + isFieldTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return m.fieldUpgrades[m.fieldMode].name === "perfect diamagnetism" + }, + requires: "perfect diamagnetism", + effect() { + tech.isPerfectBrake = true; + }, + remove() { + tech.isPerfectBrake = false; + } + }, + { + name: "Meissner effect", + description: "increase perfect diamagnetism field
radius by 55% and circular arc by 22°", + isFieldTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return m.fieldUpgrades[m.fieldMode].name === "perfect diamagnetism" + }, + requires: "perfect diamagnetism", + effect() { + tech.isBigField = true; + }, + remove() { + tech.isBigField = false; + } + }, + { + name: "tessellation", + description: `use ${powerUps.orb.research(2)}to reduce harm by 50%`, + // description: "use 4 research
reduce harm by 50%", + isFieldTech: true, + maxCount: 1, + count: 0, + frequency: 3, + frequencyDefault: 3, + allowed() { + return (m.fieldUpgrades[m.fieldMode].name === "pilot wave" || m.fieldUpgrades[m.fieldMode].name === "perfect diamagnetism" || m.fieldUpgrades[m.fieldMode].name === "negative mass") && (build.isExperimentSelection || powerUps.research.count > 3) + }, + requires: "perfect diamagnetism, negative mass, pilot wave", + effect() { + tech.isFieldHarmReduction = true + for (let i = 0; i < 2; i++) { + if (powerUps.research.count > 0) powerUps.research.changeRerolls(-1) + } + }, + remove() { + tech.isFieldHarmReduction = false + if (this.count > 0) powerUps.research.changeRerolls(2) + } + }, + { + name: "neutronium", + description: `reduce harm by 90% when your field is active
move and jump 33% slower`, + isFieldTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return m.fieldUpgrades[m.fieldMode].name === "negative mass" && !tech.isEnergyHealth + }, + requires: "negative mass, not mass-energy", + effect() { + tech.isNeutronium = true + tech.baseFx *= 0.66 + tech.baseJumpForce *= 0.66 + m.setMovement() + }, + //also removed in m.setHoldDefaults() if player switches into a bad field + remove() { + tech.isNeutronium = false + if (!tech.isFreeWormHole) { + tech.baseFx = 0.08 + tech.baseJumpForce = 10.5 + m.setMovement() + } + } + }, + { + name: "annihilation", + description: "touching normal mobs annihilates them
but drains 33% of your maximum energy", + isFieldTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return m.fieldUpgrades[m.fieldMode].name === "negative mass" + }, + requires: "negative mass", + effect() { + tech.isAnnihilation = true + }, + remove() { + tech.isAnnihilation = false; + } + }, + { + name: "inertial mass", + description: "negative mass is larger and faster
blocks also move horizontally with the field", + isFieldTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return m.fieldUpgrades[m.fieldMode].name === "negative mass" + }, + requires: "negative mass", + effect() { + tech.isFlyFaster = true + }, + remove() { + tech.isFlyFaster = false; + } + }, + { + name: "Bose Einstein condensate", + description: "mobs inside your field are frozen
pilot wave, negative mass, time dilation", + isFieldTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return m.fieldUpgrades[m.fieldMode].name === "pilot wave" || m.fieldUpgrades[m.fieldMode].name === "negative mass" || (m.fieldUpgrades[m.fieldMode].name === "time dilation" && !tech.isRewindField) + }, + requires: "pilot wave, negative mass, time dilation, not retrocausality", + effect() { + tech.isFreezeMobs = true + }, + remove() { + tech.isFreezeMobs = false + } + }, + { + name: "bot manufacturing", + description: `use molecular assembler and ${powerUps.orb.research(2)}
to build 3 random bots`, + isFieldTech: true, + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + isBotTech: true, + isNonRefundable: true, + // isExperimentHide: true, + allowed() { + return powerUps.research.count > 1 && m.fieldUpgrades[m.fieldMode].name === "molecular assembler" + }, + requires: "NOT EXPERIMENT MODE, molecular assembler", + effect: () => { + for (let i = 0; i < 2; i++) { + if (powerUps.research.count > 0) powerUps.research.changeRerolls(-1) + } + m.energy = 0.01; + b.randomBot() + b.randomBot() + b.randomBot() + }, + remove() { } + }, + { + name: "bot prototypes", + description: `use ${powerUps.orb.research(3)}to build
2 random bots and upgrade all bots to that type`, + isFieldTech: true, + maxCount: 1, + count: 0, + frequency: 1, + frequencyDefault: 1, + isBotTech: true, + isNonRefundable: true, + // isExperimentHide: true, + allowed() { + return powerUps.research.count > 2 && m.fieldUpgrades[m.fieldMode].name === "molecular assembler" + }, + requires: "NOT EXPERIMENT MODE, molecular assembler", + effect: () => { + for (let i = 0; i < 3; i++) { + if (powerUps.research.count > 0) powerUps.research.changeRerolls(-1) + } + + //fill array of available bots + const notUpgradedBots = [] + const num = 2 + notUpgradedBots.push(() => { + tech.giveTech("nail-bot upgrade") + for (let i = 0; i < num; i++) { + b.nailBot() + tech.nailBotCount++; + } + simulation.makeTextLog(`tech.isNailBotUpgrade = true`) + }) + notUpgradedBots.push(() => { + tech.giveTech("foam-bot upgrade") + for (let i = 0; i < num; i++) { + b.foamBot() + tech.foamBotCount++; + } + simulation.makeTextLog(`tech.isFoamBotUpgrade = true`) + }) + notUpgradedBots.push(() => { + tech.giveTech("boom-bot upgrade") + for (let i = 0; i < num; i++) { + b.boomBot() + tech.boomBotCount++; + } + simulation.makeTextLog(`tech.isBoomBotUpgrade = true`) + }) + notUpgradedBots.push(() => { + tech.giveTech("laser-bot upgrade") + for (let i = 0; i < num; i++) { + b.laserBot() + tech.laserBotCount++; + } + simulation.makeTextLog(`tech.isLaserBotUpgrade = true`) + }) + notUpgradedBots.push(() => { + tech.giveTech("orbital-bot upgrade") + for (let i = 0; i < num; i++) { + b.orbitBot() + tech.orbitBotCount++; + } + simulation.makeTextLog(`tech.isOrbitalBotUpgrade = true`) + }) + for (let i = 0; i < 2; i++) { //double chance for dynamo-bot, since it's very good for assembler + notUpgradedBots.push(() => { + tech.giveTech("dynamo-bot upgrade") + for (let i = 0; i < num; i++) { + b.dynamoBot() + tech.dynamoBotCount++; + } + simulation.makeTextLog(`tech.isDynamoBotUpgrade = true`) + }) + } + notUpgradedBots[Math.floor(Math.random() * notUpgradedBots.length)]() //choose random function from the array and run it + }, + remove() { } + }, + { + name: "mycelium manufacturing", + link: `mycelium manufacturing`, + description: `use ${powerUps.orb.research(1)}to repurpose molecular assembler
excess energy used to grow spores`, + // description: "use 3 research to repurpose assembler
excess energy used to grow spores", + isFieldTech: true, + maxCount: 1, + count: 0, + frequency: 3, + frequencyDefault: 3, + allowed() { + return (build.isExperimentSelection || powerUps.research.count > 0) && m.fieldUpgrades[m.fieldMode].name === "molecular assembler" && !(tech.isMissileField || tech.isIceField || tech.isFastDrones || tech.isDroneGrab || tech.isDroneRadioactive || tech.isDroneTeleport) + }, + requires: "molecular assembler, no other manufacturing, no drone tech", + effect() { + if (!build.isExperimentSelection) { + for (let i = 0; i < 1; i++) { if (powerUps.research.count > 0) powerUps.research.changeRerolls(-1) - }, - remove() { - tech.isPlasmaRange = 1; - if (this.count > 0) powerUps.research.changeRerolls(this.count) } - }, - { - name: "extruder", - description: "plasma torch extrudes a thin hot wire
increases damage and energy drain", - isFieldTech: true, - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return m.fieldUpgrades[m.fieldMode].name === "plasma torch" - }, - requires: "plasma torch", - effect() { - tech.isExtruder = true; - m.fieldUpgrades[m.fieldMode].set() - }, - remove() { - tech.isExtruder = false; - if (this.count) m.fieldUpgrades[m.fieldMode].set() + } + tech.isSporeField = true; + }, + remove() { + tech.isSporeField = false; + if (this.count > 0) powerUps.research.changeRerolls(1) + } + }, + { + name: "missile manufacturing", + link: `missile manufacturing`, + description: `use ${powerUps.orb.research(1)}to repurpose molecular assembler
excess energy used to construct missiles`, + // description: "use 3 research to repurpose assembler
excess energy used to construct missiles", + isFieldTech: true, + maxCount: 1, + count: 0, + frequency: 3, + frequencyDefault: 3, + allowed() { + return (build.isExperimentSelection || powerUps.research.count > 0) && m.maxEnergy > 0.5 && m.fieldUpgrades[m.fieldMode].name === "molecular assembler" && !(tech.isSporeField || tech.isIceField || tech.isFastDrones || tech.isDroneGrab || tech.isDroneRadioactive || tech.isDroneTeleport) + }, + requires: "molecular assembler, no other manufacturing, no drone tech", + effect() { + if (!build.isExperimentSelection) { + for (let i = 0; i < 1; i++) { + if (powerUps.research.count > 0) powerUps.research.changeRerolls(-1) } - }, - { - name: "refractory metal", - description: "extrude metals at a higher temperature
increases effective radius and damage", - isFieldTech: true, - maxCount: 9, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return tech.isExtruder - }, - requires: "extruder", - effect() { - tech.extruderRange += 50 - }, - remove() { - tech.extruderRange = 15 + } + tech.isMissileField = true; + }, + remove() { + tech.isMissileField = false; + if (this.count > 0) powerUps.research.changeRerolls(1) + } + }, + { + name: "ice IX manufacturing", + link: `ice IX manufacturing`, + description: `use ${powerUps.orb.research(1)}to repurpose molecular assembler
excess energy used to condense ice IX`, + // description: "use 3 research to repurpose assembler
excess energy used to condense ice IX", + isFieldTech: true, + maxCount: 1, + count: 0, + frequency: 3, + frequencyDefault: 3, + allowed() { + return (build.isExperimentSelection || powerUps.research.count > 0) && m.fieldUpgrades[m.fieldMode].name === "molecular assembler" && !(tech.isSporeField || tech.isMissileField || tech.isFastDrones || tech.isDroneGrab || tech.isDroneRadioactive || tech.isDroneTeleport) + }, + requires: "molecular assembler, no other manufacturing, no drone tech", + effect() { + if (!build.isExperimentSelection) { + for (let i = 0; i < 1; i++) { + if (powerUps.research.count > 0) powerUps.research.changeRerolls(-1) } - }, - // { - // name: "CPT gun", - // link: `CPT gun`, - // description: `adds the CPT gun to your inventory
it rewinds your health, velocity, and position`, - // isGunTech: true, - // maxCount: 1, - // count: 0, - // frequency: 2, - // frequencyDefault: 2, - // allowed() { - // return (b.totalBots() > 3 || m.fieldUpgrades[m.fieldMode].name === "molecular assembler" || m.fieldUpgrades[m.fieldMode].name === "plasma torch" || m.fieldUpgrades[m.fieldMode].name === "pilot wave") && !tech.isEnergyHealth && !tech.isRewindAvoidDeath //build.isExperimentSelection || - // }, - // requires: "bots > 3, plasma torch, assembler, pilot wave, not mass-energy equivalence, CPT", - // effect() { - // tech.isRewindGun = true - // b.guns.push(b.gunRewind) - // b.giveGuns("CPT gun"); - // }, - // remove() { - // if (tech.isRewindGun) { - // b.removeGun("CPT gun", true) - // tech.isRewindGun = false - // } - // } - // }, - { - name: "retrocausality", - description: "time dilation uses energy to rewind your
health, velocity, and position up to 10 s", - isFieldTech: true, - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return m.fieldUpgrades[m.fieldMode].name === "time dilation" && !m.isShipMode && !tech.isRewindAvoidDeath && !tech.isEnergyHealth && !tech.isTimeSkip && !tech.isFreezeMobs - }, - requires: "time dilation, not CPT symmetry, mass-energy, timelike, Bose Einstein condensate", - effect() { - tech.isRewindField = true; - m.fieldUpgrades[m.fieldMode].set() - m.wakeCheck(); - }, - remove() { - tech.isRewindField = false; - if (this.count) m.fieldUpgrades[m.fieldMode].set() - } - }, - { - name: "timelike", - description: "time dilation doubles your relative time rate
and makes you immune to harm", - isFieldTech: true, - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return m.fieldUpgrades[m.fieldMode].name === "time dilation" && !m.isShipMode && !tech.isRewindField - }, - requires: "time dilation, not retrocausality", - effect() { - tech.isTimeSkip = true; - }, - remove() { - tech.isTimeSkip = false; - } - }, - { - name: "Lorentz transformation", - description: `use ${powerUps.orb.research(3)}to increase your time rate
move, jump, and shoot 50% faster`, - isFieldTech: true, - maxCount: 1, - count: 0, - frequency: 3, - frequencyDefault: 3, - allowed() { - return (m.fieldUpgrades[m.fieldMode].name === "time dilation") && (build.isExperimentSelection || powerUps.research.count > 2) - }, - requires: "time dilation", - effect() { - tech.isFastTime = true - m.setMovement(); - b.setFireCD(); - for (let i = 0; i < 3; i++) { - if (powerUps.research.count > 0) powerUps.research.changeRerolls(-1) - } - }, - remove() { - tech.isFastTime = false - m.setMovement(); - b.setFireCD(); - if (this.count > 0) powerUps.research.changeRerolls(3) - } - }, - { - name: "time crystals", - description: "quadruple your default energy regeneration", - isFieldTech: true, - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return (m.fieldUpgrades[m.fieldMode].name === "time dilation" || m.fieldUpgrades[m.fieldMode].name === "pilot wave") && tech.energyRegen !== 0 - }, - requires: "time dilation or pilot wave, not ground state", - effect: () => { - tech.energyRegen = 0.004; - m.fieldRegen = tech.energyRegen; - }, - remove() { - tech.energyRegen = 0.001; - m.fieldRegen = tech.energyRegen; - } - }, - { - name: "no-cloning theorem", - description: `40% chance to duplicate spawned power ups
after a mob dies, lose 2% duplication chance`, - isFieldTech: true, - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return (m.fieldUpgrades[m.fieldMode].name === "time dilation" || m.fieldUpgrades[m.fieldMode].name === "metamaterial cloaking") - }, - requires: "cloaking, time dilation", - effect() { - tech.cloakDuplication = 0.4 - powerUps.setDupChance(); //needed after adjusting duplication chance - if (!build.isExperimentSelection && !simulation.isTextLogOpen) simulation.circleFlare(0.4); - }, - remove() { - tech.cloakDuplication = 0 - powerUps.setDupChance(); //needed after adjusting duplication chance - } - }, - { - name: "symbiosis", - description: "after a mob dies, lose 1 max health
bosses spawn 1 extra tech after they die", - isFieldTech: true, - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return m.fieldUpgrades[m.fieldMode].name === "metamaterial cloaking" || m.fieldUpgrades[m.fieldMode].name === "time dilation" - }, - requires: "cloaking or time dilation", - effect() { - tech.isAddRemoveMaxHealth = true - }, - remove() { - tech.isAddRemoveMaxHealth = false - } - }, - // { - // name: "symbiosis", - // description: "after a mob dies, lose 0.5 max health
after picking up tech gain 10 max health", - // isFieldTech: true, - // maxCount: 1, - // count: 0, - // frequency: 2, - // frequencyDefault: 2, - // allowed() { - // return (m.fieldUpgrades[m.fieldMode].name === "time dilation" || m.fieldUpgrades[m.fieldMode].name === "metamaterial cloaking") - // }, - // requires: "metamaterial cloaking", - // effect() { - // tech.isAddRemoveMaxHealth = true - // tech.extraMaxHealth += 0.1 //increase max health - // m.setMaxHealth(); - // }, - // remove() { - // tech.isAddRemoveMaxHealth = false - // } - // }, - // { - // name: "symbiosis", - // description: "if a mob dies, lose 1% max health
at the end of each level spawn 2 tech", - // isFieldTech: true, - // maxCount: 1, - // count: 0, - // frequency: 2, - // frequencyDefault: 2, - // allowed() { - // return (m.fieldUpgrades[m.fieldMode].name === "time dilation" || m.fieldUpgrades[m.fieldMode].name === "metamaterial cloaking") && !tech.removeMaxHealthOnKill - // }, - // requires: "metamaterial cloaking, not -symbiosis-", - // effect() { - // tech.removeMaxHealthOnKill = 0.01 - // tech.isSpawnExitTech = true - // // for (let i = 0; i < 2; i++) powerUps.spawn(player.position.x + 90 * (Math.random() - 0.5), player.position.y + 90 * (Math.random() - 0.5), "tech", false); //start - // for (let i = 0; i < 2; i++) powerUps.spawn(level.exit.x + 10 * (Math.random() - 0.5), level.exit.y - 100 + 10 * (Math.random() - 0.5), "tech", false) //exit - // }, - // remove() { - // tech.removeMaxHealthOnKill = 0 - // tech.isSpawnExitTech = false - // } - // }, - { - name: "boson composite", - link: `boson composite`, - description: "intangible to blocks and mobs while cloaked
passing through shields drains your energy", - isFieldTech: true, - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return m.fieldUpgrades[m.fieldMode].name === "metamaterial cloaking" - }, - requires: "metamaterial cloaking", - effect() { - tech.isIntangible = true; - }, - remove() { - if (tech.isIntangible) { - tech.isIntangible = false; - player.collisionFilter.mask = cat.body | cat.map | cat.mob | cat.mobBullet | cat.mobShield //normal collisions + } + tech.isIceField = true; + }, + remove() { + tech.isIceField = false; + if (this.count > 0) powerUps.research.changeRerolls(1) + } + }, + { + name: "pair production", + description: "picking up a power up gives you 200 energy", + isFieldTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return m.fieldUpgrades[m.fieldMode].name === "molecular assembler" || m.fieldUpgrades[m.fieldMode].name === "pilot wave" + }, + requires: "molecular assembler or pilot wave", + effect: () => { + tech.isMassEnergy = true // used in m.grabPowerUp + m.energy += 2 + }, + remove() { + tech.isMassEnergy = false; + } + }, + // { + // name: "thermal reservoir", + // description: "increase your plasma damage by 100%
plasma temporarily lowers health not energy", + // isFieldTech: true, + // maxCount: 1, + // count: 0, + // frequency: 2, + // allowed() { + // return m.fieldUpgrades[m.fieldMode].name === "plasma torch" && !tech.isEnergyHealth + // }, + // requires: "plasma torch, not mass-energy equivalence", + // effect() { + // tech.isPlasmaRange += 0.27; + // }, + // remove() { + // tech.isPlasmaRange = 1; + // } + // }, + { + name: "degenerate matter", + description: "reduce harm by 60% while your field is active", + isFieldTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return (m.fieldUpgrades[m.fieldMode].name === "plasma torch" || m.fieldUpgrades[m.fieldMode].name === "perfect diamagnetism" || m.fieldUpgrades[m.fieldMode].name === "pilot wave") && !tech.isEnergyHealth + }, + requires: "perfect diamagnetism, pilot wave, plasma, not mass-energy", + effect() { + tech.isHarmReduce = true + }, + remove() { + tech.isHarmReduce = false; + } + }, + { + name: "tokamak", + description: "throwing a block converts it into energy
and a pulsed fusion explosion", + isFieldTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return m.fieldUpgrades[m.fieldMode].name === "plasma torch" || m.fieldUpgrades[m.fieldMode].name === "molecular assembler" + }, + requires: "plasma torch or molecular assembler", + effect() { + tech.isTokamak = true; + }, + remove() { + tech.isTokamak = false; + } + }, + { + name: "plasma-bot", + link: `plasma-bot`, + description: "remove your field to build a bot
that uses energy to emit plasma", + isFieldTech: true, + maxCount: 1, + count: 0, + frequency: 3, + frequencyDefault: 3, + isBot: true, + isBotTech: true, + allowed() { + return m.fieldUpgrades[m.fieldMode].name === "plasma torch" && (build.isExperimentSelection || powerUps.research.count > 0) + }, + requires: "plasma torch", + effect() { + tech.plasmaBotCount++; + b.plasmaBot(); + if (build.isExperimentSelection) { + document.getElementById("field-" + m.fieldMode).classList.remove("build-field-selected"); + document.getElementById("field-0").classList.add("build-field-selected"); + } + m.setField("field emitter") + }, + remove() { + if (this.count > 0) { + tech.plasmaBotCount = 0; + b.clearPermanentBots(); + b.respawnBots(); + if (m.fieldMode === 0) { + m.setField("plasma torch") + if (build.isExperimentSelection) { + document.getElementById("field-0").classList.remove("build-field-selected"); + document.getElementById("field-" + m.fieldMode).classList.add("build-field-selected"); } } - }, - { - name: "dazzler", - description: "decloaking stuns nearby mobs
and drains 10 energy", - isFieldTech: true, - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return m.fieldUpgrades[m.fieldMode].name === "metamaterial cloaking" - }, - requires: "metamaterial cloaking", - effect() { - tech.isCloakStun = true; - }, - remove() { - tech.isCloakStun = false; + } + } + }, + { + name: "plasma jet", + link: `plasma jet`, + description: `use ${powerUps.orb.research(1)} to increase plasma torch range 50%`, + // description: "use 1 research to
increase plasma torch's range by 50%", + isFieldTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return (tech.plasmaBotCount || m.fieldUpgrades[m.fieldMode].name === "plasma torch") && (build.isExperimentSelection || powerUps.research.count > 0) + }, + requires: "plasma torch", + effect() { + tech.isPlasmaRange += 0.5; + if (powerUps.research.count > 0) powerUps.research.changeRerolls(-1) + }, + remove() { + tech.isPlasmaRange = 1; + if (this.count > 0) powerUps.research.changeRerolls(this.count) + } + }, + { + name: "extruder", + description: "plasma torch extrudes a thin hot wire
increases damage and energy drain", + isFieldTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return m.fieldUpgrades[m.fieldMode].name === "plasma torch" + }, + requires: "plasma torch", + effect() { + tech.isExtruder = true; + m.fieldUpgrades[m.fieldMode].set() + }, + remove() { + tech.isExtruder = false; + if (this.count) m.fieldUpgrades[m.fieldMode].set() + } + }, + { + name: "refractory metal", + description: "extrude metals at a higher temperature
increases effective radius and damage", + isFieldTech: true, + maxCount: 9, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return tech.isExtruder + }, + requires: "extruder", + effect() { + tech.extruderRange += 50 + }, + remove() { + tech.extruderRange = 15 + } + }, + // { + // name: "CPT gun", + // link: `CPT gun`, + // description: `adds the CPT gun to your inventory
it rewinds your health, velocity, and position`, + // isGunTech: true, + // maxCount: 1, + // count: 0, + // frequency: 2, + // frequencyDefault: 2, + // allowed() { + // return (b.totalBots() > 3 || m.fieldUpgrades[m.fieldMode].name === "molecular assembler" || m.fieldUpgrades[m.fieldMode].name === "plasma torch" || m.fieldUpgrades[m.fieldMode].name === "pilot wave") && !tech.isEnergyHealth && !tech.isRewindAvoidDeath //build.isExperimentSelection || + // }, + // requires: "bots > 3, plasma torch, assembler, pilot wave, not mass-energy equivalence, CPT", + // effect() { + // tech.isRewindGun = true + // b.guns.push(b.gunRewind) + // b.giveGuns("CPT gun"); + // }, + // remove() { + // if (tech.isRewindGun) { + // b.removeGun("CPT gun", true) + // tech.isRewindGun = false + // } + // } + // }, + { + name: "retrocausality", + description: "time dilation uses energy to rewind your
health, velocity, and position up to 10 s", + isFieldTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return m.fieldUpgrades[m.fieldMode].name === "time dilation" && !m.isShipMode && !tech.isRewindAvoidDeath && !tech.isEnergyHealth && !tech.isTimeSkip && !tech.isFreezeMobs + }, + requires: "time dilation, not CPT symmetry, mass-energy, timelike, Bose Einstein condensate", + effect() { + tech.isRewindField = true; + m.fieldUpgrades[m.fieldMode].set() + m.wakeCheck(); + }, + remove() { + tech.isRewindField = false; + if (this.count) m.fieldUpgrades[m.fieldMode].set() + } + }, + { + name: "timelike", + description: "time dilation doubles your relative time rate
and makes you immune to harm", + isFieldTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return m.fieldUpgrades[m.fieldMode].name === "time dilation" && !m.isShipMode && !tech.isRewindField + }, + requires: "time dilation, not retrocausality", + effect() { + tech.isTimeSkip = true; + }, + remove() { + tech.isTimeSkip = false; + } + }, + { + name: "Lorentz transformation", + description: `use ${powerUps.orb.research(3)}to increase your time rate
move, jump, and shoot 50% faster`, + isFieldTech: true, + maxCount: 1, + count: 0, + frequency: 3, + frequencyDefault: 3, + allowed() { + return (m.fieldUpgrades[m.fieldMode].name === "time dilation") && (build.isExperimentSelection || powerUps.research.count > 2) + }, + requires: "time dilation", + effect() { + tech.isFastTime = true + m.setMovement(); + b.setFireCD(); + for (let i = 0; i < 3; i++) { + if (powerUps.research.count > 0) powerUps.research.changeRerolls(-1) + } + }, + remove() { + tech.isFastTime = false + m.setMovement(); + b.setFireCD(); + if (this.count > 0) powerUps.research.changeRerolls(3) + } + }, + { + name: "time crystals", + description: "quadruple your default energy regeneration", + isFieldTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return (m.fieldUpgrades[m.fieldMode].name === "time dilation" || m.fieldUpgrades[m.fieldMode].name === "pilot wave") && tech.energyRegen !== 0 + }, + requires: "time dilation or pilot wave, not ground state", + effect: () => { + tech.energyRegen = 0.004; + m.fieldRegen = tech.energyRegen; + }, + remove() { + tech.energyRegen = 0.001; + m.fieldRegen = tech.energyRegen; + } + }, + { + name: "no-cloning theorem", + description: `40% chance to duplicate spawned power ups
after a mob dies, lose 2% duplication chance`, + isFieldTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return (m.fieldUpgrades[m.fieldMode].name === "time dilation" || m.fieldUpgrades[m.fieldMode].name === "metamaterial cloaking") + }, + requires: "cloaking, time dilation", + effect() { + tech.cloakDuplication = 0.4 + powerUps.setDupChance(); //needed after adjusting duplication chance + if (!build.isExperimentSelection && !simulation.isTextLogOpen) simulation.circleFlare(0.4); + }, + remove() { + tech.cloakDuplication = 0 + powerUps.setDupChance(); //needed after adjusting duplication chance + } + }, + { + name: "symbiosis", + description: "after a mob dies, lose 1 max health
bosses spawn 1 extra tech after they die", + isFieldTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return m.fieldUpgrades[m.fieldMode].name === "metamaterial cloaking" || m.fieldUpgrades[m.fieldMode].name === "time dilation" + }, + requires: "cloaking or time dilation", + effect() { + tech.isAddRemoveMaxHealth = true + }, + remove() { + tech.isAddRemoveMaxHealth = false + } + }, + // { + // name: "symbiosis", + // description: "after a mob dies, lose 0.5 max health
after picking up tech gain 10 max health", + // isFieldTech: true, + // maxCount: 1, + // count: 0, + // frequency: 2, + // frequencyDefault: 2, + // allowed() { + // return (m.fieldUpgrades[m.fieldMode].name === "time dilation" || m.fieldUpgrades[m.fieldMode].name === "metamaterial cloaking") + // }, + // requires: "metamaterial cloaking", + // effect() { + // tech.isAddRemoveMaxHealth = true + // tech.extraMaxHealth += 0.1 //increase max health + // m.setMaxHealth(); + // }, + // remove() { + // tech.isAddRemoveMaxHealth = false + // } + // }, + // { + // name: "symbiosis", + // description: "if a mob dies, lose 1% max health
at the end of each level spawn 2 tech", + // isFieldTech: true, + // maxCount: 1, + // count: 0, + // frequency: 2, + // frequencyDefault: 2, + // allowed() { + // return (m.fieldUpgrades[m.fieldMode].name === "time dilation" || m.fieldUpgrades[m.fieldMode].name === "metamaterial cloaking") && !tech.removeMaxHealthOnKill + // }, + // requires: "metamaterial cloaking, not -symbiosis-", + // effect() { + // tech.removeMaxHealthOnKill = 0.01 + // tech.isSpawnExitTech = true + // // for (let i = 0; i < 2; i++) powerUps.spawn(player.position.x + 90 * (Math.random() - 0.5), player.position.y + 90 * (Math.random() - 0.5), "tech", false); //start + // for (let i = 0; i < 2; i++) powerUps.spawn(level.exit.x + 10 * (Math.random() - 0.5), level.exit.y - 100 + 10 * (Math.random() - 0.5), "tech", false) //exit + // }, + // remove() { + // tech.removeMaxHealthOnKill = 0 + // tech.isSpawnExitTech = false + // } + // }, + { + name: "boson composite", + link: `boson composite`, + description: "intangible to blocks and mobs while cloaked
passing through shields drains your energy", + isFieldTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return m.fieldUpgrades[m.fieldMode].name === "metamaterial cloaking" + }, + requires: "metamaterial cloaking", + effect() { + tech.isIntangible = true; + }, + remove() { + if (tech.isIntangible) { + tech.isIntangible = false; + player.collisionFilter.mask = cat.body | cat.map | cat.mob | cat.mobBullet | cat.mobShield //normal collisions + } + } + }, + { + name: "dazzler", + description: "decloaking stuns nearby mobs
and drains 10 energy", + isFieldTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return m.fieldUpgrades[m.fieldMode].name === "metamaterial cloaking" + }, + requires: "metamaterial cloaking", + effect() { + tech.isCloakStun = true; + }, + remove() { + tech.isCloakStun = false; + } + }, + { + name: "ambush", + description: "metamaterial cloaking field damage effect
is increased from 333% to 666%", + isFieldTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return m.fieldUpgrades[m.fieldMode].name === "metamaterial cloaking" + }, + requires: "metamaterial cloaking", + effect() { + tech.sneakAttackDmg = 7.66 + }, + remove() { + tech.sneakAttackDmg = 4.33 + } + }, + { + name: "dynamical systems", + description: `use ${powerUps.orb.research(2)}to increase your damage by 35%`, + // description: "use 1 research
increase your damage by 35%", + isFieldTech: true, + maxCount: 1, + count: 0, + frequency: 3, + frequencyDefault: 3, + allowed() { + return (m.fieldUpgrades[m.fieldMode].name === "plasma torch" || m.fieldUpgrades[m.fieldMode].name === "metamaterial cloaking" || m.fieldUpgrades[m.fieldMode].name === "pilot wave") && (build.isExperimentSelection || powerUps.research.count > 1) + }, + requires: "cloaking, pilot wave, or plasma torch", + effect() { + tech.isCloakingDamage = true + for (let i = 0; i < 2; i++) { + if (powerUps.research.count > 0) powerUps.research.changeRerolls(-1) + } + }, + remove() { + tech.isCloakingDamage = false + if (this.count > 0) powerUps.research.changeRerolls(2) + } + }, + { + name: "discrete optimization", + description: "increase damage by 40%
40% increased delay after firing", + isFieldTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return m.fieldUpgrades[m.fieldMode].name === "plasma torch" || m.fieldUpgrades[m.fieldMode].name === "metamaterial cloaking" || m.fieldUpgrades[m.fieldMode].name === "pilot wave" || m.fieldUpgrades[m.fieldMode].name === "molecular assembler" + }, + requires: "metamaterial cloaking, molecular assembler, plasma torch or pilot wave", + effect() { + tech.aimDamage = 1.40 + b.setFireCD(); + }, + remove() { + tech.aimDamage = 1 + b.setFireCD(); + } + }, + // { + // name: "potential well", + // description: "the force that pilot wave generates
to trap blocks is greatly increased", + // isFieldTech: true, + // maxCount: 1, + // count: 0, + // frequency: 2, + // frequencyDefault: 2, + // allowed() { + // return m.fieldUpgrades[m.fieldMode].name === "pilot wave" + // }, + // requires: "pilot wave", + // effect() { + // tech.pilotForce = 0.0006 + // }, + // remove() { + // tech.pilotForce = 0.00002 + // } + // }, + { + name: "WIMPs", + description: `at the end of each level spawn ${powerUps.orb.research(5)}
and a harmful particle that slowly chases you`, + isFieldTech: true, + maxCount: 9, + count: 0, + frequency: 1, + frequencyDefault: 1, + allowed() { + return m.fieldUpgrades[m.fieldMode].name === "wormhole" || m.fieldUpgrades[m.fieldMode].name === "pilot wave" + }, + requires: "wormhole or pilot wave", + effect: () => { + tech.wimpCount++ + spawn.WIMP() + for (let j = 0, len = 1 + 5 * Math.random(); j < len; j++) powerUps.spawn(level.exit.x + 100 * (Math.random() - 0.5), level.exit.y - 100 + 100 * (Math.random() - 0.5), "research", false) + }, + remove() { + tech.wimpCount = 0 + } + }, + { + name: "cosmic string", + description: "stun and do radioactive damage to mobs
if you tunnel through them with a wormhole", + isFieldTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return m.fieldUpgrades[m.fieldMode].name === "wormhole" + }, + requires: "wormhole", + effect() { + tech.isWormholeDamage = true + }, + remove() { + tech.isWormholeDamage = false + } + }, + { + name: "virtual particles", + description: `use ${powerUps.orb.research(4)}to exploit your wormhole for a
13% chance to duplicate spawned power ups`, + isFieldTech: true, + maxCount: 1, + count: 0, + frequency: 3, + frequencyDefault: 3, + allowed() { + return m.fieldUpgrades[m.fieldMode].name === "wormhole" && (build.isExperimentSelection || powerUps.research.count > 3) + }, + requires: "wormhole", + effect() { + tech.wormDuplicate = 0.13 + powerUps.setDupChance(); //needed after adjusting duplication chance + if (!build.isExperimentSelection && !simulation.isTextLogOpen) simulation.circleFlare(0.13); + for (let i = 0; i < 4; i++) { + if (powerUps.research.count > 0) powerUps.research.changeRerolls(-1) + } + }, + remove() { + tech.wormDuplicate = 0 + powerUps.setDupChance(); //needed after adjusting duplication chance + if (this.count > 0) powerUps.research.changeRerolls(4) + } + }, + { + name: "Penrose process", + description: "after a block falls into a wormhole
you gain 63 energy", + isFieldTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return m.fieldUpgrades[m.fieldMode].name === "wormhole" + }, + requires: "wormhole", + effect() { + tech.isWormholeEnergy = true + }, + remove() { + tech.isWormholeEnergy = false + } + }, + { + name: "transdimensional spores", + link: `transdimensional spores`, + description: "when blocks fall into a wormhole
higher dimension spores are summoned", + isFieldTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return m.fieldUpgrades[m.fieldMode].name === "wormhole" + }, + requires: "wormhole", + effect() { + tech.isWormholeSpores = true + }, + remove() { + tech.isWormholeSpores = false + } + }, + { + name: "geodesics", + description: `your projectiles can traverse wormholes
spawn 2 guns and ${powerUps.orb.ammo(2)}`, + isFieldTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return m.fieldUpgrades[m.fieldMode].name === "wormhole" + }, + requires: "wormhole", + effect() { + tech.isWormBullets = true + for (let i = 0; i < 2; i++) { + powerUps.spawn(m.pos.x, m.pos.y, "gun"); + powerUps.spawn(m.pos.x, m.pos.y, "ammo"); + } + }, + remove() { + if (tech.isWormBullets) { + for (let i = 0; i < 2; i++) { + if (b.inventory.length) b.removeGun(b.guns[b.inventory[b.inventory.length - 1]].name) //remove your last gun } - }, - { - name: "ambush", - description: "metamaterial cloaking field damage effect
is increased from 333% to 666%", - isFieldTech: true, - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return m.fieldUpgrades[m.fieldMode].name === "metamaterial cloaking" - }, - requires: "metamaterial cloaking", - effect() { - tech.sneakAttackDmg = 7.66 - }, - remove() { - tech.sneakAttackDmg = 4.33 + tech.isWormBullets = false; + } + } + }, + { + name: "charmed baryons", + description: `wormholes require zero energy
move and jump 33% slower`, + isFieldTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return m.fieldUpgrades[m.fieldMode].name === "wormhole" && !tech.isWormholeMapIgnore + }, + requires: "wormhole, not affine connection", + effect() { + tech.isFreeWormHole = true + tech.baseFx *= 0.66 + tech.baseJumpForce *= 0.66 + m.setMovement() + }, + //also removed in m.setHoldDefaults() if player switches into a bad field + remove() { + tech.isFreeWormHole = false + if (!tech.isNeutronium) { + tech.baseFx = 0.08 + tech.baseJumpForce = 10.5 + m.setMovement() + } + } + }, + { + name: "affine connection", + description: "wormholes can tunnel through the map
at 200% increased energy cost", + isFieldTech: true, + maxCount: 1, + count: 0, + frequency: 2, + frequencyDefault: 2, + allowed() { + return m.fieldUpgrades[m.fieldMode].name === "wormhole" && !tech.isFreeWormHole + }, + requires: "wormhole, not charmed baryons", + effect() { + tech.isWormholeMapIgnore = true + }, + remove() { + tech.isWormholeMapIgnore = false + } + }, + //************************************************** + //************************************************** experimental + //************************************************** modes + //************************************************** + { + name: "-ship-", + description: "experiment: fly around with no legs
aim with the keyboard", + maxCount: 1, + count: 0, + frequency: 0, + isNonRefundable: true, + isBadRandomOption: true, + isExperimentalMode: true, + allowed() { + return build.isExperimentSelection && !m.isShipMode && m.fieldUpgrades[m.fieldMode].name !== "negative mass" + }, + requires: "", + effect() { + m.shipMode() + }, + remove() { } + }, + { + name: "-quantum leap-", + description: "experiment: every 20 seconds
become an alternate version of yourself", + maxCount: 1, + count: 0, + frequency: 0, + isBadRandomOption: true, + isExperimentalMode: true, + allowed() { + return build.isExperimentSelection + }, + requires: "", + interval: undefined, + effect() { + this.interval = setInterval(() => { + if (!build.isExperimentSelection) { + m.switchWorlds() + simulation.trails() } - }, - { - name: "dynamical systems", - description: `use ${powerUps.orb.research(2)}to increase your damage by 35%`, - // description: "use 1 research
increase your damage by 35%", - isFieldTech: true, - maxCount: 1, - count: 0, - frequency: 3, - frequencyDefault: 3, - allowed() { - return (m.fieldUpgrades[m.fieldMode].name === "plasma torch" || m.fieldUpgrades[m.fieldMode].name === "metamaterial cloaking" || m.fieldUpgrades[m.fieldMode].name === "pilot wave") && (build.isExperimentSelection || powerUps.research.count > 1) - }, - requires: "cloaking, pilot wave, or plasma torch", - effect() { - tech.isCloakingDamage = true - for (let i = 0; i < 2; i++) { - if (powerUps.research.count > 0) powerUps.research.changeRerolls(-1) - } - }, - remove() { - tech.isCloakingDamage = false - if (this.count > 0) powerUps.research.changeRerolls(2) - } - }, - { - name: "discrete optimization", - description: "increase damage by 40%
40% increased delay after firing", - isFieldTech: true, - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return m.fieldUpgrades[m.fieldMode].name === "plasma torch" || m.fieldUpgrades[m.fieldMode].name === "metamaterial cloaking" || m.fieldUpgrades[m.fieldMode].name === "pilot wave" || m.fieldUpgrades[m.fieldMode].name === "molecular assembler" - }, - requires: "metamaterial cloaking, molecular assembler, plasma torch or pilot wave", - effect() { - tech.aimDamage = 1.40 - b.setFireCD(); - }, - remove() { - tech.aimDamage = 1 - b.setFireCD(); - } - }, - // { - // name: "potential well", - // description: "the force that pilot wave generates
to trap blocks is greatly increased", - // isFieldTech: true, - // maxCount: 1, - // count: 0, - // frequency: 2, - // frequencyDefault: 2, - // allowed() { - // return m.fieldUpgrades[m.fieldMode].name === "pilot wave" - // }, - // requires: "pilot wave", - // effect() { - // tech.pilotForce = 0.0006 - // }, - // remove() { - // tech.pilotForce = 0.00002 - // } - // }, - { - name: "WIMPs", - description: `at the end of each level spawn ${powerUps.orb.research(5)}
and a harmful particle that slowly chases you`, - isFieldTech: true, - maxCount: 9, - count: 0, - frequency: 1, - frequencyDefault: 1, - allowed() { - return m.fieldUpgrades[m.fieldMode].name === "wormhole" || m.fieldUpgrades[m.fieldMode].name === "pilot wave" - }, - requires: "wormhole or pilot wave", - effect: () => { - tech.wimpCount++ - spawn.WIMP() - for (let j = 0, len = 1 + 5 * Math.random(); j < len; j++) powerUps.spawn(level.exit.x + 100 * (Math.random() - 0.5), level.exit.y - 100 + 100 * (Math.random() - 0.5), "research", false) - }, - remove() { - tech.wimpCount = 0 - } - }, - { - name: "cosmic string", - description: "stun and do radioactive damage to mobs
if you tunnel through them with a wormhole", - isFieldTech: true, - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return m.fieldUpgrades[m.fieldMode].name === "wormhole" - }, - requires: "wormhole", - effect() { - tech.isWormholeDamage = true - }, - remove() { - tech.isWormholeDamage = false - } - }, - { - name: "virtual particles", - description: `use ${powerUps.orb.research(4)}to exploit your wormhole for a
13% chance to duplicate spawned power ups`, - isFieldTech: true, - maxCount: 1, - count: 0, - frequency: 3, - frequencyDefault: 3, - allowed() { - return m.fieldUpgrades[m.fieldMode].name === "wormhole" && (build.isExperimentSelection || powerUps.research.count > 3) - }, - requires: "wormhole", - effect() { - tech.wormDuplicate = 0.13 - powerUps.setDupChance(); //needed after adjusting duplication chance - if (!build.isExperimentSelection && !simulation.isTextLogOpen) simulation.circleFlare(0.13); - for (let i = 0; i < 4; i++) { - if (powerUps.research.count > 0) powerUps.research.changeRerolls(-1) - } - }, - remove() { - tech.wormDuplicate = 0 - powerUps.setDupChance(); //needed after adjusting duplication chance - if (this.count > 0) powerUps.research.changeRerolls(4) - } - }, - { - name: "Penrose process", - description: "after a block falls into a wormhole
you gain 63 energy", - isFieldTech: true, - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return m.fieldUpgrades[m.fieldMode].name === "wormhole" - }, - requires: "wormhole", - effect() { - tech.isWormholeEnergy = true - }, - remove() { - tech.isWormholeEnergy = false - } - }, - { - name: "transdimensional spores", - link: `transdimensional spores`, - description: "when blocks fall into a wormhole
higher dimension spores are summoned", - isFieldTech: true, - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return m.fieldUpgrades[m.fieldMode].name === "wormhole" - }, - requires: "wormhole", - effect() { - tech.isWormholeSpores = true - }, - remove() { - tech.isWormholeSpores = false - } - }, - { - name: "geodesics", - description: `your projectiles can traverse wormholes
spawn 2 guns and ${powerUps.orb.ammo(2)}`, - isFieldTech: true, - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return m.fieldUpgrades[m.fieldMode].name === "wormhole" - }, - requires: "wormhole", - effect() { - tech.isWormBullets = true - for (let i = 0; i < 2; i++) { - powerUps.spawn(m.pos.x, m.pos.y, "gun"); - powerUps.spawn(m.pos.x, m.pos.y, "ammo"); - } - }, - remove() { - if (tech.isWormBullets) { - for (let i = 0; i < 2; i++) { - if (b.inventory.length) b.removeGun(b.guns[b.inventory[b.inventory.length - 1]].name) //remove your last gun - } - tech.isWormBullets = false; - } - } - }, - { - name: "charmed baryons", - description: `wormhole field uses no energy
move and jump 33% slower`, - isFieldTech: true, - maxCount: 1, - count: 0, - frequency: 2, - frequencyDefault: 2, - allowed() { - return m.fieldUpgrades[m.fieldMode].name === "wormhole" - }, - requires: "wormhole", - effect() { - tech.isFreeWormHole = true - tech.baseFx *= 0.66 - tech.baseJumpForce *= 0.66 - m.setMovement() - }, - //also removed in m.setHoldDefaults() if player switches into a bad field - remove() { - tech.isFreeWormHole = false - if (!tech.isNeutronium) { - tech.baseFx = 0.08 - tech.baseJumpForce = 10.5 - m.setMovement() - } - } - }, - //************************************************** - //************************************************** experimental - //************************************************** modes - //************************************************** - { - name: "-ship-", - description: "experiment: fly around with no legs
aim with the keyboard", - maxCount: 1, - count: 0, - frequency: 0, - isNonRefundable: true, - isBadRandomOption: true, - isExperimentalMode: true, - allowed() { - return build.isExperimentSelection && !m.isShipMode && m.fieldUpgrades[m.fieldMode].name !== "negative mass" - }, - requires: "", - effect() { - m.shipMode() - }, - remove() {} - }, - { - name: "-quantum leap-", - description: "experiment: every 20 seconds
become an alternate version of yourself", - maxCount: 1, - count: 0, - frequency: 0, - isBadRandomOption: true, - isExperimentalMode: true, - allowed() { - return build.isExperimentSelection - }, - requires: "", - interval: undefined, - effect() { - this.interval = setInterval(() => { - if (!build.isExperimentSelection) { - m.switchWorlds() - simulation.trails() - } - }, 20000); //every 20 seconds + }, 20000); //every 20 seconds - }, - remove() { - if (this.count > 0) clearTimeout(this.interval); - } - }, - { - name: "-shields-", - description: "experiment: every 5 seconds
all mobs gain a shield", - maxCount: 1, - count: 0, - frequency: 0, - isBadRandomOption: true, - isExperimentalMode: true, - allowed() { - return build.isExperimentSelection - }, - requires: "", - effect() { - this.interval = setInterval(() => { - if (!build.isExperimentSelection) { - for (let i = 0; i < mob.length; i++) { - if (!mob[i].isShielded && !mob[i].shield && mob[i].isDropPowerUp) spawn.shield(mob[i], mob[i].position.x, mob[i].position.y, 1, true); - } - } - }, 5000); //every 5 seconds - }, - interval: undefined, - remove() { - if (this.count > 0) clearTimeout(this.interval); - } - }, - { - name: "-Fourier analysis-", - description: "experiment: your aiming is random", - maxCount: 1, - count: 0, - frequency: 0, - isBadRandomOption: true, - isExperimentalMode: true, - allowed() { - return build.isExperimentSelection && !m.isShipMode - }, - requires: "not ship", - effect() { - m.look = () => { - m.angle = 2 * Math.sin(m.cycle * 0.0133) + Math.sin(m.cycle * 0.013) + 0.5 * Math.sin(m.cycle * 0.031) + 0.33 * Math.sin(m.cycle * 0.03) - const scale = 0.8; - m.transSmoothX = canvas.width2 - m.pos.x - (simulation.mouse.x - canvas.width2) * scale; - m.transSmoothY = canvas.height2 - m.pos.y - (simulation.mouse.y - canvas.height2) * scale; - m.transX += (m.transSmoothX - m.transX) * 0.07; - m.transY += (m.transSmoothY - m.transY) * 0.07; + }, + remove() { + if (this.count > 0) clearTimeout(this.interval); + } + }, + { + name: "-shields-", + description: "experiment: every 5 seconds
all mobs gain a shield", + maxCount: 1, + count: 0, + frequency: 0, + isBadRandomOption: true, + isExperimentalMode: true, + allowed() { + return build.isExperimentSelection + }, + requires: "", + effect() { + this.interval = setInterval(() => { + if (!build.isExperimentSelection) { + for (let i = 0; i < mob.length; i++) { + if (!mob[i].isShielded && !mob[i].shield && mob[i].isDropPowerUp) spawn.shield(mob[i], mob[i].position.x, mob[i].position.y, 1, true); } - }, - remove() { - if (this.count > 0) m.look = m.lookDefault() } - }, - { - name: "-panopticon-", - description: "experiment: mobs can always see you", - maxCount: 1, - count: 0, - frequency: 0, - isBadRandomOption: true, - isExperimentalMode: true, - allowed() { - return build.isExperimentSelection - }, - requires: "", - effect() { - this.interval = setInterval(() => { - if (!build.isExperimentSelection) { - for (let i = 0; i < mob.length; i++) { - if (!mob[i].shield && mob[i].isDropPowerUp) { - mob[i].locatePlayer() - mob[i].seePlayer.yes = true; - } - } + }, 5000); //every 5 seconds + }, + interval: undefined, + remove() { + if (this.count > 0) clearTimeout(this.interval); + } + }, + { + name: "-Fourier analysis-", + description: "experiment: your aiming is random", + maxCount: 1, + count: 0, + frequency: 0, + isBadRandomOption: true, + isExperimentalMode: true, + allowed() { + return build.isExperimentSelection && !m.isShipMode + }, + requires: "not ship", + effect() { + m.look = () => { + m.angle = 2 * Math.sin(m.cycle * 0.0133) + Math.sin(m.cycle * 0.013) + 0.5 * Math.sin(m.cycle * 0.031) + 0.33 * Math.sin(m.cycle * 0.03) + const scale = 0.8; + m.transSmoothX = canvas.width2 - m.pos.x - (simulation.mouse.x - canvas.width2) * scale; + m.transSmoothY = canvas.height2 - m.pos.y - (simulation.mouse.y - canvas.height2) * scale; + m.transX += (m.transSmoothX - m.transX) * 0.07; + m.transY += (m.transSmoothY - m.transY) * 0.07; + } + }, + remove() { + if (this.count > 0) m.look = m.lookDefault() + } + }, + { + name: "-panopticon-", + description: "experiment: mobs can always see you", + maxCount: 1, + count: 0, + frequency: 0, + isBadRandomOption: true, + isExperimentalMode: true, + allowed() { + return build.isExperimentSelection + }, + requires: "", + effect() { + this.interval = setInterval(() => { + if (!build.isExperimentSelection) { + for (let i = 0; i < mob.length; i++) { + if (!mob[i].shield && mob[i].isDropPowerUp) { + mob[i].locatePlayer() + mob[i].seePlayer.yes = true; } - }, 1000); //every 1 seconds - }, - interval: undefined, - remove() { - if (this.count > 0) clearTimeout(this.interval); + } } - }, - { - name: "-decomposers-", - description: "experiment: after they die
mobs leave behind spawns", - maxCount: 1, - count: 0, - frequency: 0, - isBadRandomOption: true, - isExperimentalMode: true, - allowed() { - return build.isExperimentSelection - }, - requires: "", - effect() { - tech.deathSpawns = 0.2 - }, - remove() { - tech.deathSpawns = 0 - } - }, - { - name: "-WIMP-", - description: "experiment: harmful particles slowly chase you", - maxCount: 1, - count: 0, - frequency: 0, - isBadRandomOption: true, - isExperimentalMode: true, - allowed() { - return build.isExperimentSelection - }, - requires: "", - effect() { - tech.wimpExperiment = 5 - }, - remove() { - tech.wimpExperiment = 0 - } - }, - { - name: "-symbiosis-", - description: "experiment: if you kill a mob
lose 0.2 max health", - maxCount: 1, - count: 0, - frequency: 0, - isBadRandomOption: true, - isExperimentalMode: true, - allowed() { - return build.isExperimentSelection - }, - requires: "", - effect() { - tech.removeMaxHealthOnKill = 0.002 - }, - remove() { - tech.removeMaxHealthOnKill = 0 - } - }, - { - name: "-parthenocarpy-", - description: "experiment: spawn about 50% more mobs", - maxCount: 1, - count: 1, - frequency: 0, - isBadRandomOption: true, - isExperimentalMode: true, - allowed() { - return build.isExperimentSelection - }, - requires: "", - effect() { - tech.isMoreMobs = true - }, - remove() { - tech.isMoreMobs = false - } - }, - //************************************************** - //************************************************** JUNK - //************************************************** tech - //************************************************** - // { - // name: "junk", - // description: "", - // maxCount: 9, - // count: 0, - // frequency: 0, - // isNonRefundable: true, - // isExperimentHide: true, - // isJunk: true, - // allowed() { - // return true - // }, - // requires: "", - // effect() { + }, 1000); //every 1 seconds + }, + interval: undefined, + remove() { + if (this.count > 0) clearTimeout(this.interval); + } + }, + { + name: "-decomposers-", + description: "experiment: after they die
mobs leave behind spawns", + maxCount: 1, + count: 0, + frequency: 0, + isBadRandomOption: true, + isExperimentalMode: true, + allowed() { + return build.isExperimentSelection + }, + requires: "", + effect() { + tech.deathSpawns = 0.2 + }, + remove() { + tech.deathSpawns = 0 + } + }, + { + name: "-WIMP-", + description: "experiment: harmful particles slowly chase you", + maxCount: 1, + count: 0, + frequency: 0, + isBadRandomOption: true, + isExperimentalMode: true, + allowed() { + return build.isExperimentSelection + }, + requires: "", + effect() { + tech.wimpExperiment = 5 + }, + remove() { + tech.wimpExperiment = 0 + } + }, + { + name: "-symbiosis-", + description: "experiment: if you kill a mob
lose 0.2 max health", + maxCount: 1, + count: 0, + frequency: 0, + isBadRandomOption: true, + isExperimentalMode: true, + allowed() { + return build.isExperimentSelection + }, + requires: "", + effect() { + tech.removeMaxHealthOnKill = 0.002 + }, + remove() { + tech.removeMaxHealthOnKill = 0 + } + }, + { + name: "-parthenocarpy-", + description: "experiment: spawn about 50% more mobs", + maxCount: 1, + count: 1, + frequency: 0, + isBadRandomOption: true, + isExperimentalMode: true, + allowed() { + return build.isExperimentSelection + }, + requires: "", + effect() { + tech.isMoreMobs = true + }, + remove() { + tech.isMoreMobs = false + } + }, + //************************************************** + //************************************************** JUNK + //************************************************** tech + //************************************************** + // { + // name: "junk", + // description: "", + // maxCount: 9, + // count: 0, + // frequency: 0, + // isNonRefundable: true, + // isExperimentHide: true, + // isJunk: true, + // allowed() { + // return true + // }, + // requires: "", + // effect() { - // }, - // remove() {} - // }, - { - name: "density", - description: `block are 100 times less dense`, - maxCount: 1, - count: 0, - frequency: 0, - isNonRefundable: true, - isExperimentHide: true, - isJunk: true, - allowed() { return true }, - requires: "", - effect() { - for (let i = 0; i < body.length; i++) Matter.Body.setDensity(body[i], 0.00001) //set current blocks to low density - level.addToWorld = () => { - for (let i = 0; i < body.length; i++) { - if (body[i] !== m.holdingTarget && !body[i].isNoSetCollision) { - body[i].collisionFilter.category = cat.body; - body[i].collisionFilter.mask = cat.player | cat.map | cat.body | cat.bullet | cat.mob | cat.mobBullet - } - Matter.Body.setDensity(body[i], 0.00001) //THIS IS THE ONLY ADDED LINE OF CODE - body[i].classType = "body"; - Composite.add(engine.world, body[i]); //add to world - } - for (let i = 0; i < map.length; i++) { - map[i].collisionFilter.category = cat.map; - map[i].collisionFilter.mask = cat.player | cat.map | cat.body | cat.bullet | cat.powerUp | cat.mob | cat.mobBullet; - Matter.Body.setStatic(map[i], true); //make static - Composite.add(engine.world, map[i]); //add to world - } + // }, + // remove() {} + // }, + { + name: "catabolysis", + description: `set your maximum health to 1
double your current ammo 10 times`, + maxCount: 1, + count: 0, + frequency: 0, + isNonRefundable: true, + isExperimentHide: true, + isJunk: true, + allowed() { return !tech.isFallingDamage && !tech.isOverHeal && !tech.isEnergyHealth }, + requires: "not quenching, tungsten carbide, mass-energy", + effect() { + m.baseHealth = 0.01 + m.setMaxHealth(); + for (let i = 0; i < b.guns.length; i++) b.guns[i].ammo = b.guns[i].ammo * Math.pow(2, 10) + simulation.updateGunHUD(); + }, + remove() { } + }, + { + name: "density", + description: `block are 100 times less dense`, + maxCount: 1, + count: 0, + frequency: 0, + isNonRefundable: true, + isExperimentHide: true, + isJunk: true, + allowed() { return true }, + requires: "", + effect() { + for (let i = 0; i < body.length; i++) Matter.Body.setDensity(body[i], 0.00001) //set current blocks to low density + level.addToWorld = () => { + for (let i = 0; i < body.length; i++) { + if (body[i] !== m.holdingTarget && !body[i].isNoSetCollision) { + body[i].collisionFilter.category = cat.body; + body[i].collisionFilter.mask = cat.player | cat.map | cat.body | cat.bullet | cat.mob | cat.mobBullet } - }, - remove() { - if (this.count) m.look = m.lookDefault + Matter.Body.setDensity(body[i], 0.00001) //THIS IS THE ONLY ADDED LINE OF CODE + body[i].classType = "body"; + Composite.add(engine.world, body[i]); //add to world } - }, - { - name: "palantĂ­r", - description: `see far away lands`, - maxCount: 1, - count: 0, - frequency: 0, - // isNonRefundable: true, - isExperimentHide: true, - isJunk: true, - allowed() { return true }, - requires: "", - effect() { - m.look = () => { - //always on mouse look - m.angle = Math.atan2( - simulation.mouseInGame.y - m.pos.y, - simulation.mouseInGame.x - m.pos.x - ); - //smoothed mouse look translations - const scale = 2; - m.transSmoothX = canvas.width2 - m.pos.x - (simulation.mouse.x - canvas.width2) * scale; - m.transSmoothY = canvas.height2 - m.pos.y - (simulation.mouse.y - canvas.height2) * scale; - m.transX += (m.transSmoothX - m.transX) * m.lookSmoothing; - m.transY += (m.transSmoothY - m.transY) * m.lookSmoothing; - } - }, - remove() { - if (this.count) m.look = m.lookDefault + for (let i = 0; i < map.length; i++) { + map[i].collisionFilter.category = cat.map; + map[i].collisionFilter.mask = cat.player | cat.map | cat.body | cat.bullet | cat.powerUp | cat.mob | cat.mobBullet; + Matter.Body.setStatic(map[i], true); //make static + Composite.add(engine.world, map[i]); //add to world } - }, - { - name: "motion sickness", - description: `disable camera smoothing`, - maxCount: 1, - count: 0, - frequency: 0, - // isNonRefundable: true, - isExperimentHide: true, - isJunk: true, - allowed() { return true }, - requires: "", - effect() { - m.look = () => { - //always on mouse look - m.angle = Math.atan2( - simulation.mouseInGame.y - m.pos.y, - simulation.mouseInGame.x - m.pos.x - ); - //smoothed mouse look translations - const scale = 1.2; - m.transSmoothX = canvas.width2 - m.pos.x - (simulation.mouse.x - canvas.width2) * scale; - m.transSmoothY = canvas.height2 - m.pos.y - (simulation.mouse.y - canvas.height2) * scale; - m.transX = canvas.width2 - m.pos.x - (simulation.mouse.x - canvas.width2) * scale; - m.transY = canvas.height2 - m.pos.y - (simulation.mouse.y - canvas.height2) * scale; - // m.transX += (m.transSmoothX - m.transX) * m.lookSmoothing; - // m.transY += (m.transSmoothY - m.transY) * m.lookSmoothing; - } - }, - remove() { - if (this.count) m.look = m.lookDefault - } - }, - { - name: "planetesimals", - description: `play planetesimals
(an annoying asteroids game with Newtonian physics)
clearing a level in planetesimals spawns a tech in n-gon
but, if you die in planetesimals you die in n-gon`, - maxCount: 1, - count: 0, - frequency: 100, - isNonRefundable: true, - isExperimentHide: true, - isJunk: true, - allowed() { return true }, - requires: "", - effect() { - window.open('../../planetesimals/index.html', '_blank') - // powerUps.spawn(m.pos.x, m.pos.y, "tech"); + } + }, + remove() { + if (this.count) m.look = m.lookDefault + } + }, + { + name: "palantĂ­r", + description: `see far away lands`, + maxCount: 1, + count: 0, + frequency: 0, + // isNonRefundable: true, + isExperimentHide: true, + isJunk: true, + allowed() { return true }, + requires: "", + effect() { + m.look = () => { + //always on mouse look + m.angle = Math.atan2( + simulation.mouseInGame.y - m.pos.y, + simulation.mouseInGame.x - m.pos.x + ); + //smoothed mouse look translations + const scale = 2; + m.transSmoothX = canvas.width2 - m.pos.x - (simulation.mouse.x - canvas.width2) * scale; + m.transSmoothY = canvas.height2 - m.pos.y - (simulation.mouse.y - canvas.height2) * scale; + m.transX += (m.transSmoothX - m.transX) * m.lookSmoothing; + m.transY += (m.transSmoothY - m.transY) * m.lookSmoothing; + } + }, + remove() { + if (this.count) m.look = m.lookDefault + } + }, + { + name: "motion sickness", + description: `disable camera smoothing`, + maxCount: 1, + count: 0, + frequency: 0, + // isNonRefundable: true, + isExperimentHide: true, + isJunk: true, + allowed() { return true }, + requires: "", + effect() { + m.look = () => { + //always on mouse look + m.angle = Math.atan2( + simulation.mouseInGame.y - m.pos.y, + simulation.mouseInGame.x - m.pos.x + ); + //smoothed mouse look translations + const scale = 1.2; + m.transSmoothX = canvas.width2 - m.pos.x - (simulation.mouse.x - canvas.width2) * scale; + m.transSmoothY = canvas.height2 - m.pos.y - (simulation.mouse.y - canvas.height2) * scale; + m.transX = canvas.width2 - m.pos.x - (simulation.mouse.x - canvas.width2) * scale; + m.transY = canvas.height2 - m.pos.y - (simulation.mouse.y - canvas.height2) * scale; + // m.transX += (m.transSmoothX - m.transX) * m.lookSmoothing; + // m.transY += (m.transSmoothY - m.transY) * m.lookSmoothing; + } + }, + remove() { + if (this.count) m.look = m.lookDefault + } + }, + { + name: "planetesimals", + description: `play planetesimals
(an annoying asteroids game with Newtonian physics)
clearing a level in planetesimals spawns a tech in n-gon
but, if you die in planetesimals you die in n-gon`, + maxCount: 1, + count: 0, + frequency: 100, + isNonRefundable: true, + isExperimentHide: true, + isJunk: true, + allowed() { return true }, + requires: "", + effect() { + window.open('../../planetesimals/index.html', '_blank') + // powerUps.spawn(m.pos.x, m.pos.y, "tech"); - // for communicating to other tabs, like planetesimals - // Connection to a broadcast channel - const bc = new BroadcastChannel('planetesimals'); - bc.activated = false + // for communicating to other tabs, like planetesimals + // Connection to a broadcast channel + const bc = new BroadcastChannel('planetesimals'); + bc.activated = false - bc.onmessage = function(ev) { - if (ev.data === 'tech') powerUps.directSpawn(m.pos.x, m.pos.y, "tech"); - if (ev.data === 'death') { - m.death() - bc.close(); //end session - } - if (ev.data === 'ready' && !bc.activated) { - bc.activated = true //prevents n-gon from activating multiple copies of planetesimals - bc.postMessage("activate"); - } - } - }, - remove() {} - }, - { - name: "facsimile", - description: `inserts a copy of your current level into the level list`, - maxCount: 1, - count: 0, - frequency: 0, - isNonRefundable: true, - isExperimentHide: true, - isJunk: true, - allowed() { return true }, - requires: "", - effect() { - level.levels.splice(level.onLevel, 0, level.levels[level.onLevel]); - }, - remove() {} - }, - { - name: "negative friction", - description: "when you touch walls you speed up instead of slowing down. It's kinda fun.", - maxCount: 1, - count: 0, - frequency: 0, - isExperimentHide: true, - isJunk: true, - allowed() { return true }, - requires: "", - effect() { - player.friction = -0.4 - }, - remove() { - if (this.count) player.friction = 0.002 + bc.onmessage = function (ev) { + if (ev.data === 'tech') powerUps.directSpawn(m.pos.x, m.pos.y, "tech"); + if (ev.data === 'death') { + m.death() + bc.close(); //end session } - }, - { - name: "bounce", - description: "you bounce off things. It's annoying, but not that bad.", - maxCount: 1, - count: 0, - frequency: 0, - isExperimentHide: true, - isJunk: true, - allowed() { return true }, - requires: "", - effect() { - player.restitution = 0.9 - }, - remove() { - if (this.count) player.restitution = 0 + if (ev.data === 'ready' && !bc.activated) { + bc.activated = true //prevents n-gon from activating multiple copies of planetesimals + bc.postMessage("activate"); } - }, - { - name: "mouth", - description: "mobs have a non functional mouth", - maxCount: 1, - count: 0, - frequency: 0, - isExperimentHide: true, - isJunk: true, - allowed() { return true }, - requires: "", - effect() { - mobs.draw = () => { - ctx.lineWidth = 2; - let i = mob.length; - while (i--) { - ctx.beginPath(); - const vertices = mob[i].vertices; - ctx.moveTo(vertices[0].x, vertices[0].y); - for (let j = 1, len = vertices.length; j < len; ++j) ctx.lineTo(vertices[j].x, vertices[j].y); - ctx.quadraticCurveTo(mob[i].position.x, mob[i].position.y, vertices[0].x, vertices[0].y); - ctx.fillStyle = mob[i].fill; - ctx.strokeStyle = mob[i].stroke; - ctx.fill(); - ctx.stroke(); - } - } - }, - remove() { - mobs.draw = () => { - ctx.lineWidth = 2; - let i = mob.length; - while (i--) { - ctx.beginPath(); - const vertices = mob[i].vertices; - ctx.moveTo(vertices[0].x, vertices[0].y); - for (let j = 1, len = vertices.length; j < len; ++j) ctx.lineTo(vertices[j].x, vertices[j].y); - ctx.lineTo(vertices[0].x, vertices[0].y); - ctx.fillStyle = mob[i].fill; - ctx.strokeStyle = mob[i].stroke; - ctx.fill(); - ctx.stroke(); - } - } + } + }, + remove() { } + }, + { + name: "facsimile", + description: `inserts a copy of your current level into the level list`, + maxCount: 1, + count: 0, + frequency: 0, + isNonRefundable: true, + isExperimentHide: true, + isJunk: true, + allowed() { return true }, + requires: "", + effect() { + level.levels.splice(level.onLevel, 0, level.levels[level.onLevel]); + }, + remove() { } + }, + { + name: "negative friction", + description: "when you touch walls you speed up instead of slowing down. It's kinda fun.", + maxCount: 1, + count: 0, + frequency: 0, + isExperimentHide: true, + isJunk: true, + allowed() { return true }, + requires: "", + effect() { + player.friction = -0.4 + }, + remove() { + if (this.count) player.friction = 0.002 + } + }, + { + name: "bounce", + description: "you bounce off things. It's annoying, but not that bad.", + maxCount: 1, + count: 0, + frequency: 0, + isExperimentHide: true, + isJunk: true, + allowed() { return true }, + requires: "", + effect() { + player.restitution = 0.9 + }, + remove() { + if (this.count) player.restitution = 0 + } + }, + { + name: "mouth", + description: "mobs have a non functional mouth", + maxCount: 1, + count: 0, + frequency: 0, + isExperimentHide: true, + isJunk: true, + allowed() { return true }, + requires: "", + effect() { + mobs.draw = () => { + ctx.lineWidth = 2; + let i = mob.length; + while (i--) { + ctx.beginPath(); + const vertices = mob[i].vertices; + ctx.moveTo(vertices[0].x, vertices[0].y); + for (let j = 1, len = vertices.length; j < len; ++j) ctx.lineTo(vertices[j].x, vertices[j].y); + ctx.quadraticCurveTo(mob[i].position.x, mob[i].position.y, vertices[0].x, vertices[0].y); + ctx.fillStyle = mob[i].fill; + ctx.strokeStyle = mob[i].stroke; + ctx.fill(); + ctx.stroke(); } - }, - { - name: "all-stars", - description: "make all mobs look like stars", - maxCount: 1, - count: 0, - frequency: 0, - isExperimentHide: true, - isJunk: true, - allowed() { return true }, - requires: "", - effect() { - mobs.draw = () => { - ctx.lineWidth = 2; - let i = mob.length; - while (i--) { - ctx.beginPath(); - const vertices = mob[i].vertices; - ctx.moveTo(vertices[0].x, vertices[0].y); - for (let j = 1, len = vertices.length; j < len; ++j) ctx.quadraticCurveTo(mob[i].position.x, mob[i].position.y, vertices[j].x, vertices[j].y); - ctx.quadraticCurveTo(mob[i].position.x, mob[i].position.y, vertices[0].x, vertices[0].y); - ctx.fillStyle = mob[i].fill; - ctx.strokeStyle = mob[i].stroke; - ctx.fill(); - ctx.stroke(); - } - } - }, - remove() { - mobs.draw = () => { - ctx.lineWidth = 2; - let i = mob.length; - while (i--) { - ctx.beginPath(); - const vertices = mob[i].vertices; - ctx.moveTo(vertices[0].x, vertices[0].y); - for (let j = 1, len = vertices.length; j < len; ++j) ctx.lineTo(vertices[j].x, vertices[j].y); - ctx.lineTo(vertices[0].x, vertices[0].y); - ctx.fillStyle = mob[i].fill; - ctx.strokeStyle = mob[i].stroke; - ctx.fill(); - ctx.stroke(); - } - } + } + }, + remove() { + mobs.draw = () => { + ctx.lineWidth = 2; + let i = mob.length; + while (i--) { + ctx.beginPath(); + const vertices = mob[i].vertices; + ctx.moveTo(vertices[0].x, vertices[0].y); + for (let j = 1, len = vertices.length; j < len; ++j) ctx.lineTo(vertices[j].x, vertices[j].y); + ctx.lineTo(vertices[0].x, vertices[0].y); + ctx.fillStyle = mob[i].fill; + ctx.strokeStyle = mob[i].stroke; + ctx.fill(); + ctx.stroke(); } - }, - // draw() { - // ctx.lineWidth = 2; - // let i = mob.length; - // while (i--) { - // ctx.beginPath(); - // const vertices = mob[i].vertices; - // ctx.moveTo(vertices[0].x, vertices[0].y); - // for (let j = 1, len = vertices.length; j < len; ++j) ctx.lineTo(vertices[j].x, vertices[j].y); - // ctx.lineTo(vertices[0].x, vertices[0].y); - // ctx.fillStyle = mob[i].fill; - // ctx.strokeStyle = mob[i].stroke; - // ctx.fill(); - // ctx.stroke(); - // } - // }, - { - name: "true colors", - description: `set all power ups to their real world colors`, - maxCount: 1, - count: 0, - frequency: 0, - isExperimentHide: true, - isJunk: true, - allowed() { return true }, - requires: "", - effect() { - // const colors = shuffle(["#f7b", "#0eb", "#467", "#0cf", "hsl(246,100%,77%)", "#26a"]) - const colors = shuffle([powerUps.research.color, powerUps.heal.color, powerUps.ammo.color, powerUps.ammo.color, powerUps.field.color, powerUps.gun.color]) - powerUps.research.color = colors[0] - powerUps.heal.color = colors[1] - powerUps.ammo.color = colors[2] - powerUps.field.color = colors[3] - powerUps.tech.color = colors[4] - powerUps.gun.color = colors[5] - for (let i = 0; i < powerUp.length; i++) { - switch (powerUp[i].name) { - case "research": - powerUp[i].color = colors[0] - break; - case "heal": - powerUp[i].color = colors[1] - break; - case "ammo": - powerUp[i].color = colors[2] - break; - case "field": - powerUp[i].color = colors[3] - break; - case "tech": - powerUp[i].color = colors[4] - break; - case "gun": - powerUp[i].color = colors[5] - break; - } - } - }, - remove() { - const colors = ["#f7b", "#0eb", "#467", "#0cf", "hsl(246,100%,77%)", "#26a"] //no shuffle - powerUps.research.color = colors[0] - powerUps.heal.color = colors[1] - powerUps.ammo.color = colors[2] - powerUps.field.color = colors[3] - powerUps.tech.color = colors[4] - powerUps.gun.color = colors[5] - for (let i = 0; i < powerUp.length; i++) { - switch (powerUp[i].name) { - case "research": - powerUp[i].color = colors[0] - break; - case "heal": - powerUp[i].color = colors[1] - break; - case "ammo": - powerUp[i].color = colors[2] - break; - case "field": - powerUp[i].color = colors[3] - break; - case "tech": - powerUp[i].color = colors[4] - break; - case "gun": - powerUp[i].color = colors[5] - break; - } - } + } + } + }, + { + name: "all-stars", + description: "make all mobs look like stars", + maxCount: 1, + count: 0, + frequency: 0, + isExperimentHide: true, + isJunk: true, + allowed() { return true }, + requires: "", + effect() { + mobs.draw = () => { + ctx.lineWidth = 2; + let i = mob.length; + while (i--) { + ctx.beginPath(); + const vertices = mob[i].vertices; + ctx.moveTo(vertices[0].x, vertices[0].y); + for (let j = 1, len = vertices.length; j < len; ++j) ctx.quadraticCurveTo(mob[i].position.x, mob[i].position.y, vertices[j].x, vertices[j].y); + ctx.quadraticCurveTo(mob[i].position.x, mob[i].position.y, vertices[0].x, vertices[0].y); + ctx.fillStyle = mob[i].fill; + ctx.strokeStyle = mob[i].stroke; + ctx.fill(); + ctx.stroke(); } - }, - { - name: "emergency broadcasting", - description: "emit 2 sound sine waveforms at 853 Hz and 960 Hz
lower your volume", - maxCount: 1, - count: 0, - frequency: 0, - isExperimentHide: true, - isJunk: true, - isNonRefundable: true, - allowed() { return true }, - requires: "", - effect: () => { - //setup audio context - function tone(frequency) { - const audioCtx = new(window.AudioContext || window.webkitAudioContext)(); - const oscillator1 = audioCtx.createOscillator(); - const gainNode1 = audioCtx.createGain(); - gainNode1.gain.value = 0.5; //controls volume - oscillator1.connect(gainNode1); - gainNode1.connect(audioCtx.destination); - oscillator1.type = "sine"; // 'sine' 'square', 'sawtooth', 'triangle' and 'custom' - oscillator1.frequency.value = frequency; // value in hertz - oscillator1.start(); - return audioCtx - } - // let sound = tone(1050) + } + }, + remove() { + mobs.draw = () => { + ctx.lineWidth = 2; + let i = mob.length; + while (i--) { + ctx.beginPath(); + const vertices = mob[i].vertices; + ctx.moveTo(vertices[0].x, vertices[0].y); + for (let j = 1, len = vertices.length; j < len; ++j) ctx.lineTo(vertices[j].x, vertices[j].y); + ctx.lineTo(vertices[0].x, vertices[0].y); + ctx.fillStyle = mob[i].fill; + ctx.strokeStyle = mob[i].stroke; + ctx.fill(); + ctx.stroke(); + } + } + } + }, + // draw() { + // ctx.lineWidth = 2; + // let i = mob.length; + // while (i--) { + // ctx.beginPath(); + // const vertices = mob[i].vertices; + // ctx.moveTo(vertices[0].x, vertices[0].y); + // for (let j = 1, len = vertices.length; j < len; ++j) ctx.lineTo(vertices[j].x, vertices[j].y); + // ctx.lineTo(vertices[0].x, vertices[0].y); + // ctx.fillStyle = mob[i].fill; + // ctx.strokeStyle = mob[i].stroke; + // ctx.fill(); + // ctx.stroke(); + // } + // }, + { + name: "true colors", + description: `set all power ups to their real world colors`, + maxCount: 1, + count: 0, + frequency: 0, + isExperimentHide: true, + isJunk: true, + allowed() { return true }, + requires: "", + effect() { + // const colors = shuffle(["#f7b", "#0eb", "#467", "#0cf", "hsl(246,100%,77%)", "#26a"]) + const colors = shuffle([powerUps.research.color, powerUps.heal.color, powerUps.ammo.color, powerUps.ammo.color, powerUps.field.color, powerUps.gun.color]) + powerUps.research.color = colors[0] + powerUps.heal.color = colors[1] + powerUps.ammo.color = colors[2] + powerUps.field.color = colors[3] + powerUps.tech.color = colors[4] + powerUps.gun.color = colors[5] + for (let i = 0; i < powerUp.length; i++) { + switch (powerUp[i].name) { + case "research": + powerUp[i].color = colors[0] + break; + case "heal": + powerUp[i].color = colors[1] + break; + case "ammo": + powerUp[i].color = colors[2] + break; + case "field": + powerUp[i].color = colors[3] + break; + case "tech": + powerUp[i].color = colors[4] + break; + case "gun": + powerUp[i].color = colors[5] + break; + } + } + }, + remove() { + const colors = ["#f7b", "#0eb", "#467", "#0cf", "hsl(246,100%,77%)", "#26a"] //no shuffle + powerUps.research.color = colors[0] + powerUps.heal.color = colors[1] + powerUps.ammo.color = colors[2] + powerUps.field.color = colors[3] + powerUps.tech.color = colors[4] + powerUps.gun.color = colors[5] + for (let i = 0; i < powerUp.length; i++) { + switch (powerUp[i].name) { + case "research": + powerUp[i].color = colors[0] + break; + case "heal": + powerUp[i].color = colors[1] + break; + case "ammo": + powerUp[i].color = colors[2] + break; + case "field": + powerUp[i].color = colors[3] + break; + case "tech": + powerUp[i].color = colors[4] + break; + case "gun": + powerUp[i].color = colors[5] + break; + } + } + } + }, + { + name: "emergency broadcasting", + description: "emit 2 sound sine waveforms at 853 Hz and 960 Hz
lower your volume", + maxCount: 1, + count: 0, + frequency: 0, + isExperimentHide: true, + isJunk: true, + isNonRefundable: true, + allowed() { return true }, + requires: "", + effect: () => { + //setup audio context + function tone(frequency) { + const audioCtx = new (window.AudioContext || window.webkitAudioContext)(); + const oscillator1 = audioCtx.createOscillator(); + const gainNode1 = audioCtx.createGain(); + gainNode1.gain.value = 0.5; //controls volume + oscillator1.connect(gainNode1); + gainNode1.connect(audioCtx.destination); + oscillator1.type = "sine"; // 'sine' 'square', 'sawtooth', 'triangle' and 'custom' + oscillator1.frequency.value = frequency; // value in hertz + oscillator1.start(); + return audioCtx + } + // let sound = tone(1050) - function EBS() { - const audioCtx = new(window.AudioContext || window.webkitAudioContext)(); + function EBS() { + const audioCtx = new (window.AudioContext || window.webkitAudioContext)(); - const oscillator1 = audioCtx.createOscillator(); - const gainNode1 = audioCtx.createGain(); - gainNode1.gain.value = 0.3; //controls volume - oscillator1.connect(gainNode1); - gainNode1.connect(audioCtx.destination); - oscillator1.type = "sine"; // 'sine' 'square', 'sawtooth', 'triangle' and 'custom' - oscillator1.frequency.value = 853; // value in hertz - oscillator1.start(); + const oscillator1 = audioCtx.createOscillator(); + const gainNode1 = audioCtx.createGain(); + gainNode1.gain.value = 0.3; //controls volume + oscillator1.connect(gainNode1); + gainNode1.connect(audioCtx.destination); + oscillator1.type = "sine"; // 'sine' 'square', 'sawtooth', 'triangle' and 'custom' + oscillator1.frequency.value = 853; // value in hertz + oscillator1.start(); - const oscillator2 = audioCtx.createOscillator(); - const gainNode2 = audioCtx.createGain(); - gainNode2.gain.value = 0.3; //controls volume - oscillator2.connect(gainNode2); - gainNode2.connect(audioCtx.destination); - oscillator2.type = "sine"; // 'sine' 'square', 'sawtooth', 'triangle' and 'custom' - oscillator2.frequency.value = 960; // value in hertz - oscillator2.start(); - return audioCtx - } - let sound = EBS() + const oscillator2 = audioCtx.createOscillator(); + const gainNode2 = audioCtx.createGain(); + gainNode2.gain.value = 0.3; //controls volume + oscillator2.connect(gainNode2); + gainNode2.connect(audioCtx.destination); + oscillator2.type = "sine"; // 'sine' 'square', 'sawtooth', 'triangle' and 'custom' + oscillator2.frequency.value = 960; // value in hertz + oscillator2.start(); + return audioCtx + } + let sound = EBS() - delay = 1000 + delay = 1000 + setTimeout(() => { + sound.suspend() + powerUps.spawn(m.pos.x + 160 * (Math.random() - 0.5), m.pos.y + 160 * (Math.random() - 0.5), "heal"); + setTimeout(() => { + sound.resume() setTimeout(() => { sound.suspend() powerUps.spawn(m.pos.x + 160 * (Math.random() - 0.5), m.pos.y + 160 * (Math.random() - 0.5), "heal"); @@ -7480,15 +7559,8 @@ sound.resume() setTimeout(() => { sound.suspend() + sound.close() powerUps.spawn(m.pos.x + 160 * (Math.random() - 0.5), m.pos.y + 160 * (Math.random() - 0.5), "heal"); - setTimeout(() => { - sound.resume() - setTimeout(() => { - sound.suspend() - sound.close() - powerUps.spawn(m.pos.x + 160 * (Math.random() - 0.5), m.pos.y + 160 * (Math.random() - 0.5), "heal"); - }, delay); - }, delay); }, delay); }, delay); }, delay); @@ -7498,1466 +7570,1471 @@ }, delay); }, delay); }, delay); - }, - remove() {} - }, - { - name: "automatic", - description: "you can't fire when moving
always fire when at rest", - maxCount: 1, - count: 0, - frequency: 0, - isExperimentHide: true, - isJunk: true, - allowed() { - return !tech.isFireMoveLock - }, - requires: "not Higgs mechanism", - effect: () => { - tech.isAlwaysFire = true; - b.setFireMethod(); - }, - remove() { - if (tech.isAlwaysFire) { - tech.isAlwaysFire = false - b.setFireMethod(); + }, delay); + }, delay); + }, + remove() { } + }, + { + name: "automatic", + description: "you can't fire when moving
always fire when at rest", + maxCount: 1, + count: 0, + frequency: 0, + isExperimentHide: true, + isJunk: true, + allowed() { + return !tech.isFireMoveLock + }, + requires: "not Higgs mechanism", + effect: () => { + tech.isAlwaysFire = true; + b.setFireMethod(); + }, + remove() { + if (tech.isAlwaysFire) { + tech.isAlwaysFire = false + b.setFireMethod(); + } + } + }, + { + name: "hidden variable", + description: `spawn ${powerUps.orb.heal(15)}
but hide your health bar`, + maxCount: 1, + count: 0, + frequency: 0, + isNonRefundable: true, + isExperimentHide: true, + isJunk: true, + allowed() { + return !tech.isEnergyHealth + }, + requires: "not mass-energy", + effect() { + document.getElementById("health").style.display = "none" + document.getElementById("health-bg").style.display = "none" + for (let i = 0; i < 15; i++) powerUps.spawn(m.pos.x + 160 * (Math.random() - 0.5), m.pos.y + 160 * (Math.random() - 0.5), "heal"); + }, + remove() { } + }, + { + name: "not a bug", + description: "initiate a totally safe game crash for 5 seconds", + maxCount: 1, + count: 0, + frequency: 0, + isNonRefundable: true, + isExperimentHide: true, + isJunk: true, + allowed() { return true }, + requires: "", + effect() { + const savedfunction = simulation.drawCircle + simulation.drawCircle = () => { + const a = mob[Infinity].position //crashed the game in a visually interesting way, because of the ctx.translate command is never reverted in the main game loop + } + setTimeout(() => { + simulation.drawCircle = savedfunction + canvas.width = canvas.width //clears the canvas // works on chrome at least + }, 5000); + + // for (;;) {} //freezes the tab + }, + remove() { } + }, + { + name: "posture", + description: "stand a bit taller", + maxCount: 1, + count: 0, + frequency: 0, + isExperimentHide: true, + isJunk: true, + allowed() { + return !m.isShipMode + }, + requires: "", + effect() { + m.yOffWhen.stand = 70 + }, + remove() { + m.yOffWhen.stand = 49 + } + }, + { + name: "rhythm", + description: "you oscillate up and down", + maxCount: 1, + count: 0, + frequency: 0, + isExperimentHide: true, + isJunk: true, + isNonRefundable: true, + allowed() { + return !m.isShipMode + }, + requires: "", + effect() { + setInterval(() => { + m.yOffWhen.stand = 53 + 28 * Math.sin(simulation.cycle * 0.2) + if (m.onGround && !m.crouch) m.yOffGoal = m.yOffWhen.stand + }, 100); + }, + remove() { } + }, + { + name: "spinor", + description: "the direction you aim is determined by your position", + maxCount: 1, + count: 0, + frequency: 0, + isExperimentHide: true, + isNonRefundable: true, + isJunk: true, + allowed() { + return !m.isShipMode + }, + requires: "", + effect() { + m.look = function () { + //always on mouse look + m.angle = (((m.pos.x + m.pos.y) / 100 + Math.PI) % Math.PI * 2) - Math.PI + //smoothed mouse look translations + const scale = 0.8; + m.transSmoothX = canvas.width2 - m.pos.x - (simulation.mouse.x - canvas.width2) * scale; + m.transSmoothY = canvas.height2 - m.pos.y - (simulation.mouse.y - canvas.height2) * scale; + + m.transX += (m.transSmoothX - m.transX) * 0.07; + m.transY += (m.transSmoothY - m.transY) * 0.07; + } + }, + remove() { + if (this.count) m.look = m.lookDefault + } + }, + { + name: "decomposers", + description: "after they die mobs leave behind spawns", + maxCount: 1, + count: 0, + frequency: 0, + isExperimentHide: true, + isNonRefundable: true, + isJunk: true, + allowed() { + return tech.deathSpawns === 0 + }, + requires: "", + effect() { + tech.deathSpawns = 0.2 + }, + remove() { + tech.deathSpawns = 0 + } + }, + { + name: "panopticon", + description: "mobs can always see you", + maxCount: 1, + count: 0, + frequency: 0, + isExperimentHide: true, + isNonRefundable: true, + isJunk: true, + allowed() { return true }, + requires: "", + effect() { + setInterval(() => { + for (let i = 0; i < mob.length; i++) { + if (!mob[i].shield && mob[i].isDropPowerUp) { + mob[i].locatePlayer() + mob[i].seePlayer.yes = true; } } - }, - { - name: "hidden variable", - description: `spawn ${powerUps.orb.heal(15)}
but hide your health bar`, - maxCount: 1, - count: 0, - frequency: 0, - isNonRefundable: true, - isExperimentHide: true, - isJunk: true, - allowed() { - return !tech.isEnergyHealth - }, - requires: "not mass-energy", - effect() { - document.getElementById("health").style.display = "none" - document.getElementById("health-bg").style.display = "none" - for (let i = 0; i < 15; i++) powerUps.spawn(m.pos.x + 160 * (Math.random() - 0.5), m.pos.y + 160 * (Math.random() - 0.5), "heal"); - }, - remove() {} - }, - { - name: "not a bug", - description: "initiate a totally safe game crash for 5 seconds", - maxCount: 1, - count: 0, - frequency: 0, - isNonRefundable: true, - isExperimentHide: true, - isJunk: true, - allowed() { return true }, - requires: "", - effect() { - const savedfunction = simulation.drawCircle - simulation.drawCircle = () => { - const a = mob[Infinity].position //crashed the game in a visually interesting way, because of the ctx.translate command is never reverted in the main game loop - } - setTimeout(() => { - simulation.drawCircle = savedfunction - canvas.width = canvas.width //clears the canvas // works on chrome at least - }, 5000); - - // for (;;) {} //freezes the tab - }, - remove() {} - }, - { - name: "posture", - description: "stand a bit taller", - maxCount: 1, - count: 0, - frequency: 0, - isExperimentHide: true, - isJunk: true, - allowed() { - return !m.isShipMode - }, - requires: "", - effect() { - m.yOffWhen.stand = 70 - }, - remove() { - m.yOffWhen.stand = 49 - } - }, - { - name: "rhythm", - description: "you oscillate up and down", - maxCount: 1, - count: 0, - frequency: 0, - isExperimentHide: true, - isJunk: true, - isNonRefundable: true, - allowed() { - return !m.isShipMode - }, - requires: "", - effect() { - setInterval(() => { - m.yOffWhen.stand = 53 + 28 * Math.sin(simulation.cycle * 0.2) - if (m.onGround && !m.crouch) m.yOffGoal = m.yOffWhen.stand - }, 100); - }, - remove() {} - }, - { - name: "spinor", - description: "the direction you aim is determined by your position", - maxCount: 1, - count: 0, - frequency: 0, - isExperimentHide: true, - isNonRefundable: true, - isJunk: true, - allowed() { - return !m.isShipMode - }, - requires: "", - effect() { - m.look = function() { - //always on mouse look - m.angle = (((m.pos.x + m.pos.y) / 100 + Math.PI) % Math.PI * 2) - Math.PI - //smoothed mouse look translations - const scale = 0.8; - m.transSmoothX = canvas.width2 - m.pos.x - (simulation.mouse.x - canvas.width2) * scale; - m.transSmoothY = canvas.height2 - m.pos.y - (simulation.mouse.y - canvas.height2) * scale; - - m.transX += (m.transSmoothX - m.transX) * 0.07; - m.transY += (m.transSmoothY - m.transY) * 0.07; - } - }, - remove() { - if (this.count) m.look = m.lookDefault - } - }, - { - name: "decomposers", - description: "after they die mobs leave behind spawns", - maxCount: 1, - count: 0, - frequency: 0, - isExperimentHide: true, - isNonRefundable: true, - isJunk: true, - allowed() { - return tech.deathSpawns === 0 - }, - requires: "", - effect() { - tech.deathSpawns = 0.2 - }, - remove() { - tech.deathSpawns = 0 - } - }, - { - name: "panopticon", - description: "mobs can always see you", - maxCount: 1, - count: 0, - frequency: 0, - isExperimentHide: true, - isNonRefundable: true, - isJunk: true, - allowed() { return true }, - requires: "", - effect() { - setInterval(() => { - for (let i = 0; i < mob.length; i++) { - if (!mob[i].shield && mob[i].isDropPowerUp) { - mob[i].locatePlayer() - mob[i].seePlayer.yes = true; - } - } - }, 1000); //every 1 seconds - }, - remove() {} - }, - // { - // name: "inverted mouse", - // description: "your mouse is scrambled
it's fine, just rotate it 90 degrees", - // maxCount: 1, - // count: 0, - // frequency: 0, - // isExperimentHide: true, - // isNonRefundable: true, - // isJunk: true, - // allowed() { - // return !m.isShipMode - // }, - // requires: "not ship", - // effect() { - // document.body.addEventListener("mousemove", (e) => { - // const ratio = window.innerWidth / window.innerHeight - // simulation.mouse.x = e.clientY * ratio - // simulation.mouse.y = e.clientX / ratio; - // }); - // }, - // remove() { - // // m.look = m.lookDefault - // } - // }, - { - name: "Fourier analysis", - description: "your aiming is now controlled by this equation:
2sin(0.0133t) + sin(0.013t) + 0.5sin(0.031t)+ 0.33sin(0.03t)", - maxCount: 1, - count: 0, - frequency: 0, - isExperimentHide: true, - isJunk: true, - allowed() { - return !m.isShipMode - }, - requires: "not ship", - effect() { - m.look = () => { - m.angle = 2 * Math.sin(m.cycle * 0.0133) + Math.sin(m.cycle * 0.013) + 0.5 * Math.sin(m.cycle * 0.031) + 0.33 * Math.sin(m.cycle * 0.03) - const scale = 0.8; - simulation.mouse.y - m.transSmoothX = canvas.width2 - m.pos.x - (simulation.mouse.x - canvas.width2) * scale; - m.transSmoothY = canvas.height2 - m.pos.y - (simulation.mouse.y - canvas.height2) * scale; - m.transX += (m.transSmoothX - m.transX) * 0.07; - m.transY += (m.transSmoothY - m.transY) * 0.07; - } - }, - remove() { - if (this.count) m.look = m.lookDefault - } - }, - { - name: "disintegrated armament", - description: "spawn a gun
remove your active gun", - maxCount: 1, - count: 0, - frequency: 0, - isNonRefundable: true, - isExperimentHide: true, - isJunk: true, - allowed() { - return b.inventory.length > 0 - }, - requires: "at least 1 gun", - effect() { - if (b.inventory.length > 0) b.removeGun(b.guns[b.activeGun].name) - simulation.makeGunHUD() - powerUps.spawn(m.pos.x + 60 * (Math.random() - 0.5), m.pos.y + 60 * (Math.random() - 0.5), "gun"); - }, - remove() {} - }, - { - name: "probability", - description: "increase the frequency
of one random tech by 100", - maxCount: 1, - count: 0, - frequency: 0, - isNonRefundable: true, - isExperimentHide: true, - isJunk: true, - allowed() { return true }, - requires: "", - effect() { - let options = []; //find what tech I could get - for (let i = 0, len = tech.tech.length; i < len; i++) { - if ( - tech.tech[i].count < tech.tech[i].maxCount && - tech.tech[i].allowed() && - !tech.tech[i].isJunk && - !tech.tech.isLore - ) { - options.push(i); - } - } - if (options.length) { - const index = options[Math.floor(Math.random() * options.length)] - tech.tech[index].frequency = 100 - } - }, - remove() {} - }, - { - name: "encryption", - description: "secure tech information", - maxCount: 1, - count: 0, - frequency: 0, - isNonRefundable: true, - isExperimentHide: true, - isJunk: true, - allowed() { return true }, - requires: "", - effect() { - String.prototype.shuffle = function() { - var a = this.split(""), - n = a.length; - - for (var i = n - 1; i > 0; i--) { - var j = Math.floor(Math.random() * (i + 1)); - var tmp = a[i]; - a[i] = a[j]; - a[j] = tmp; - } - return a.join(""); - } - - for (let i = 0, len = tech.tech.length; i < len; i++) tech.tech[i].name = tech.tech[i].name.shuffle() - }, - remove() {} - }, - { - name: "transparency", - description: "become invisible to yourself
mobs can still see you", - maxCount: 1, - count: 0, - frequency: 0, - isNonRefundable: true, - isExperimentHide: true, - isJunk: true, - allowed() { return true }, - requires: "", - effect() { - m.draw = () => {} - }, - remove() {} - }, - { - name: "quantum leap", - description: "become an alternate version of yourself
every 20 seconds", - maxCount: 1, - count: 0, - frequency: 0, - isNonRefundable: true, - isExperimentHide: true, - isJunk: true, - allowed() { return true }, - requires: "", - effect() { - setInterval(() => { - m.switchWorlds() - simulation.trails() - }, 20000); //every 30 seconds - }, - remove() {} - }, - { - name: "score", - description: "Add a score to n-gon!", - maxCount: 1, - count: 0, - frequency: 0, - isNonRefundable: true, - isExperimentHide: true, - isJunk: true, - allowed() { return true }, - requires: "", - effect() { - setInterval(() => { - let score = Math.ceil(1000 * Math.random() * Math.random() * Math.random() * Math.random() * Math.random()) - simulation.makeTextLog(`simulation.score = ${score.toFixed(0)}`); - }, 10000); //every 10 seconds - }, - remove() {} - }, - { - name: "pop-ups", - description: "sign up to learn endless easy ways to win n-gon
that Landgreen doesn't want you to know!!!1!!", - maxCount: 1, - count: 0, - frequency: 0, - isNonRefundable: true, - isExperimentHide: true, - isJunk: true, - allowed() { return true }, - requires: "", - effect() { - setInterval(() => { - alert(`The best combo is ${tech.tech[Math.floor(Math.random() * tech.tech.length)].name} with ${tech.tech[Math.floor(Math.random() * tech.tech.length)].name}!`); - }, 30000); //every 30 seconds - }, - remove() {} - }, - { - name: "music", - description: "add music to n-gon", - maxCount: 1, - count: 0, - frequency: 0, - isNonRefundable: true, - isExperimentHide: true, - isJunk: true, - allowed() { return true }, - requires: "", - effect() { - window.open('https://www.youtube.com/results?search_query=music', '_blank') - }, - remove() {} - }, - { - name: "performance", - description: "display performance stats to n-gon", - maxCount: 1, - count: 0, - frequency: 0, - isNonRefundable: true, - isExperimentHide: true, - isJunk: true, - allowed() { return true }, - requires: "", - effect() { - (function() { - var script = document.createElement('script'); - script.onload = function() { - var stats = new Stats(); - document.body.appendChild(stats.dom); - requestAnimationFrame(function loop() { - stats.update(); - requestAnimationFrame(loop) - }); - }; - script.src = 'https://unpkg.com/stats.js@0.17.0/build/stats.min.js'; - document.head.appendChild(script); - })() - //move health to the right - document.getElementById("health").style.left = "86px" - document.getElementById("health-bg").style.left = "86px" - }, - remove() {} - }, - { - name: "repartitioning", - description: "set the frequency of finding normal tech to 0
spawn 5 tech", - maxCount: 1, - count: 0, - frequency: 0, - isNonRefundable: true, - isExperimentHide: true, - isJunk: true, - allowed() { return true }, - requires: "", - effect() { - for (let i = 0, len = tech.tech.length; i < len; i++) { - if (tech.tech[i].isJunk) { - tech.tech[i].frequency = 2 - } else { - tech.tech[i].frequency = 0 - } - } - for (let i = 0; i < 5; i++) powerUps.spawn(m.pos.x, m.pos.y, "tech"); - }, - remove() {} - }, - { - name: "defragment", - description: "set the frequency of finding JUNK tech to zero", - maxCount: 1, - count: 0, - frequency: 0, - isNonRefundable: true, - isExperimentHide: true, - isJunk: true, - allowed() { return true }, - requires: "", - effect() { - for (let i = tech.tech.length - 1; i > 0; i--) { - if (tech.tech[i].isJunk) tech.tech[i].frequency = 0 - } - }, - remove() {} - }, - { - name: "ship", - description: "fly around with no legs
reduce combat difficulty by 1 level", - maxCount: 1, - count: 0, - frequency: 0, - isNonRefundable: true, - isExperimentHide: true, - isJunk: true, - allowed() { - return !m.isShipMode && m.fieldUpgrades[m.fieldMode].name !== "negative mass" - }, - requires: "", - effect() { - m.shipMode() - level.difficultyDecrease(simulation.difficultyMode) - }, - remove() {} - }, - // { - // name: "lubrication", - // description: "reduce block density and friction for this level", - // maxCount: 9, - // count: 0, - // frequency: 0, - // isNonRefundable: true, - // isExperimentHide: true, - // isJunk: true, - // allowed() { - // return true - // }, - // requires: "", - // effect() { - // for (let i = 0; i < body.length; i++) { - // Matter.Body.setDensity(body[i], 0.0001) // 0.001 is normal - // body[i].friction = 0.01 - // } - // }, - // remove() {} - // }, - { - name: "pitch", - description: "oscillate the pitch of your world", - maxCount: 1, - count: 0, - frequency: 0, - isNonRefundable: true, - isExperimentHide: true, - isJunk: true, - allowed() { return true }, - requires: "", - effect() { - setInterval(() => { if (!simulation.paused) ctx.rotate(0.001 * Math.sin(simulation.cycle * 0.01)) }, 16); - }, - remove() {} - }, - { - name: "umbra", - description: "produce a blue glow around everything
and probably some simulation lag", - maxCount: 1, - count: 0, - frequency: 0, - isNonRefundable: true, - isExperimentHide: true, - isJunk: true, - allowed() { return true }, - requires: "", - effect() { - ctx.shadowColor = '#06f'; - ctx.shadowBlur = 25; - }, - remove() {} - }, - { - name: "lighter", - description: `ctx.globalCompositeOperation = "lighter"`, - maxCount: 1, - count: 0, - frequency: 0, - isNonRefundable: true, - isExperimentHide: true, - isJunk: true, - allowed() { - return m.fieldUpgrades[m.fieldMode].name !== "negative mass" - }, - requires: "", - effect() { - ctx.globalCompositeOperation = "lighter"; - }, - remove() {} - }, - { - name: "rewind", - description: "every 5 seconds rewind 2 seconds
lasts 120 seconds", - maxCount: 9, - count: 0, - frequency: 0, - isNonRefundable: true, - isExperimentHide: true, - isJunk: true, - allowed() { return true }, - requires: "", - effect() { - for (let i = 0; i < 24; i++) { - setTimeout(() => { m.rewind(120) }, i * 5000); - } - }, - remove() {} - }, - { - name: "energy to mass conversion", - description: "convert your energy into blocks", - maxCount: 9, - count: 0, - frequency: 0, - isNonRefundable: true, - isExperimentHide: true, - isJunk: true, - allowed() { return true }, - requires: "", - effect() { - for (let i = 0, len = 40; i < len; i++) { - setTimeout(() => { - m.energy -= 1 / len - const index = body.length - where = Vector.add(m.pos, { x: 400 * (Math.random() - 0.5), y: 400 * (Math.random() - 0.5) }) - spawn.bodyRect(where.x, where.y, Math.floor(15 + 100 * Math.random()), Math.floor(15 + 100 * Math.random())); - body[index].collisionFilter.category = cat.body; - body[index].collisionFilter.mask = cat.player | cat.map | cat.body | cat.bullet | cat.mob | cat.mobBullet - body[index].classType = "body"; - Composite.add(engine.world, body[index]); //add to world - }, i * 100); - } - - }, - remove() {} - }, - { - name: "level.nextLevel()", - description: "advance to the next level", - maxCount: 9, - count: 0, - frequency: 0, - isNonRefundable: true, - isExperimentHide: true, - isJunk: true, - allowed() { return true }, - requires: "", - effect() { - level.nextLevel(); - }, - remove() {} - }, - { - name: "expert system", - description: "spawn a tech power up
+64% JUNK to the potential tech pool", - maxCount: 9, - count: 0, - frequency: 0, - isNonRefundable: true, - isExperimentHide: true, - isJunk: true, - allowed() { return true }, - requires: "", - effect() { - powerUps.spawn(m.pos.x, m.pos.y, "tech"); - tech.addJunkTechToPool(0.64) - }, - remove() {} - }, - { - name: "energy investment", - description: "every 10 seconds drain your energy
return it doubled 10 seconds later
lasts 180 seconds", - maxCount: 9, - count: 0, - frequency: 0, - isNonRefundable: true, - isExperimentHide: true, - isJunk: true, - allowed() { return true }, - requires: "", - effect() { - for (let i = 0; i < 18; i++) { - setTimeout(() => { //drain energy - const energy = m.energy - m.energy = 0 - setTimeout(() => { //return energy - m.energy += 2 * energy - }, 5000); - }, i * 10000); - } - }, - remove() {} - }, - { - name: "missile Launching System", - description: "fire missiles for the next 60 seconds", - maxCount: 9, - count: 0, - frequency: 0, - isNonRefundable: true, - isExperimentHide: true, - isJunk: true, - allowed() { return true }, - requires: "", - effect() { - for (let i = 0; i < 60; i++) { - setTimeout(() => { - const where = { - x: m.pos.x, - y: m.pos.y - 40 - } - b.missile(where, -Math.PI / 2 + 0.2 * (Math.random() - 0.5) * Math.sqrt(tech.missileCount), -2) - }, i * 1000); - } - }, - remove() {} - }, - { - name: "grenade production", - description: "drop grenades for the next 120 seconds", - maxCount: 9, - count: 0, - frequency: 0, - isNonRefundable: true, - isExperimentHide: true, - isJunk: true, - allowed() { return true }, - requires: "", - effect() { - for (let i = 0; i < 120; i++) { - setTimeout(() => { - b.grenade(Vector.add(m.pos, { x: 10 * (Math.random() - 0.5), y: 10 * (Math.random() - 0.5) }), -Math.PI / 2) //fire different angles for each grenade - const who = bullet[bullet.length - 1] - Matter.Body.setVelocity(who, { - x: who.velocity.x * 0.1, - y: who.velocity.y * 0.1 - }); - }, i * 1000); - } - }, - remove() {} - }, - // { - // name: "inverted input", - // description: "left input becomes right and up input becomes down", - // maxCount: 9, - // count: 0, - // frequency: 0, - // isNonRefundable: true, - // isExperimentHide: true, - // isJunk: true, - // allowed() { - // return true - // }, - // requires: "", - // effect() { - // const left = input.key.left - // input.key.left = input.key.right - // input.key.right = left - - // const up = input.key.up - // input.key.up = input.key.down - // input.key.down = up - // }, - // remove() {} - // }, - { - name: "Sleipnir", - description: "grow more legs", - maxCount: 1, - count: 0, - frequency: 0, - isNonRefundable: true, - isExperimentHide: true, - isJunk: true, - allowed() { - return !m.isShipMode - }, - requires: "", - effect() { - m.draw = function() { - ctx.fillStyle = m.fillColor; - m.walk_cycle += m.flipLegs * m.Vx; - - //draw body - ctx.save(); - ctx.globalAlpha = (m.immuneCycle < m.cycle) ? 1 : 0.5 - ctx.translate(m.pos.x, m.pos.y); - for (let i = 0; i < 16; i++) { - m.calcLeg(Math.PI * i / 8, -3 * i / 16) - m.drawLeg("#444") - } - ctx.rotate(m.angle); - - ctx.beginPath(); - ctx.arc(0, 0, 30, 0, 2 * Math.PI); - ctx.fillStyle = this.bodyGradient - ctx.fill(); - ctx.arc(15, 0, 4, 0, 2 * Math.PI); - ctx.strokeStyle = "#333"; - ctx.lineWidth = 2; - ctx.stroke(); - // ctx.beginPath(); - // ctx.arc(15, 0, 3, 0, 2 * Math.PI); - // ctx.fillStyle = '#0cf'; - // ctx.fill() - ctx.restore(); - m.yOff = m.yOff * 0.85 + m.yOffGoal * 0.15; //smoothly move leg height towards height goal - } - }, - remove() {} - }, - { - name: "diegesis", - description: "indicate gun fire delay
through a rotation of your head", - maxCount: 1, - count: 0, - frequency: 0, - isNonRefundable: true, - isExperimentHide: true, - isJunk: true, - allowed() { - return !m.isShipMode - }, - requires: "", - effect() { - m.draw = function() { - ctx.fillStyle = m.fillColor; - m.walk_cycle += m.flipLegs * m.Vx; - - ctx.save(); - ctx.globalAlpha = (m.immuneCycle < m.cycle) ? 1 : 0.5 - ctx.translate(m.pos.x, m.pos.y); - m.calcLeg(Math.PI, -3); - m.drawLeg("#4a4a4a"); - m.calcLeg(0, 0); - m.drawLeg("#333"); - ctx.rotate(m.angle - (m.fireCDcycle != Infinity ? m.flipLegs * 0.25 * Math.pow(Math.max(m.fireCDcycle - m.cycle, 0), 0.5) : 0)); - - ctx.beginPath(); - ctx.arc(0, 0, 30, 0, 2 * Math.PI); - ctx.fillStyle = this.bodyGradient - ctx.fill(); - ctx.arc(15, 0, 4, 0, 2 * Math.PI); - ctx.strokeStyle = "#333"; - ctx.lineWidth = 2; - ctx.stroke(); - ctx.restore(); - m.yOff = m.yOff * 0.85 + m.yOffGoal * 0.15; //smoothly move leg height towards height goal - } - }, - remove() {} - }, - { - name: "pareidolia", - description: "don't", - maxCount: 1, - count: 0, - frequency: 0, - isNonRefundable: true, - isExperimentHide: true, - isJunk: true, - allowed() { - return !m.isShipMode - }, - requires: "", - effect() { - m.draw = function() { - ctx.fillStyle = m.fillColor; - m.walk_cycle += m.flipLegs * m.Vx; - ctx.save(); - ctx.globalAlpha = (m.immuneCycle < m.cycle) ? 1 : 0.7 - ctx.translate(m.pos.x, m.pos.y); - m.calcLeg(Math.PI, -3); - m.drawLeg("#4a4a4a"); - m.calcLeg(0, 0); - m.drawLeg("#333"); - ctx.rotate(m.angle); - ctx.beginPath(); - ctx.arc(0, 0, 30, 0, 2 * Math.PI); - ctx.fillStyle = this.bodyGradient - ctx.fill(); - ctx.strokeStyle = "#333"; - ctx.lineWidth = 2; - if (!(m.angle > -Math.PI / 2 && m.angle < Math.PI / 2)) ctx.scale(1, -1); //here is the flip - ctx.stroke(); - ctx.beginPath(); - ctx.arc(2, -6, 7, 0, 2 * Math.PI); - ctx.stroke(); - ctx.beginPath(); - ctx.arc(25, -6, 7, 0.25 * Math.PI, 1.6 * Math.PI); - ctx.stroke(); - ctx.beginPath(); - ctx.arc(2, -10, 9, 1.25 * Math.PI, 1.75 * Math.PI); - ctx.stroke(); - ctx.beginPath(); - ctx.arc(25, -10, 9, 1.25 * Math.PI, 1.4 * Math.PI); - ctx.stroke(); - ctx.beginPath(); - ctx.arc(18, 13, 10, 0, 2 * Math.PI); - ctx.fillStyle = this.bodyGradient; - ctx.fill(); - ctx.stroke(); - ctx.beginPath(); - ctx.arc(18, 13, 6, 0, 2 * Math.PI); - ctx.fillStyle = "#555"; - ctx.fill(); - ctx.stroke(); - ctx.beginPath(); - ctx.arc(3, -6, 3, 0, 2 * Math.PI); - ctx.fill(); - ctx.stroke(); - ctx.beginPath(); - ctx.arc(26, -6, 3, 0, 2 * Math.PI); - ctx.fill(); - ctx.stroke(); - ctx.restore(); - m.yOff = m.yOff * 0.85 + m.yOffGoal * 0.15; - } - }, - remove() {} - }, - { - name: "prism", - description: "you cycle through different colors", - maxCount: 1, - count: 0, - frequency: 0, - isNonRefundable: true, - isExperimentHide: true, - isJunk: true, - allowed() { return true }, - requires: "", - effect() { - m.color = { - hue: 0, - sat: 100, - light: 50 - } - setInterval(function() { - m.color.hue++ - m.setFillColors() - }, 10); - }, - remove() {} - }, - { - name: "assimilation", - description: "all your bots are converted to the same random model", - maxCount: 1, - count: 0, - frequency: 0, - isBotTech: true, - isNonRefundable: true, - isExperimentHide: true, - isJunk: true, - allowed() { - return b.totalBots() > 2 - }, - requires: "at least 3 bots", - effect() { - const total = b.totalBots(); - tech.dynamoBotCount = 0; - tech.nailBotCount = 0; - tech.laserBotCount = 0; - tech.orbitBotCount = 0; - tech.foamBotCount = 0; - tech.boomBotCount = 0; - tech.plasmaBotCount = 0; - tech.missileBotCount = 0; - for (let i = 0; i < bullet.length; i++) { - if (bullet[i].botType) bullet[i].endCycle = 0 - } - - const bots = [ - () => { - b.nailBot(); - tech.nailBotCount++; - }, - () => { - b.foamBot(); - tech.foamBotCount++; - }, - () => { - b.boomBot(); - tech.boomBotCount++; - }, - () => { - b.laserBot(); - tech.laserBotCount++; - }, - () => { - b.orbitBot(); - tech.orbitBotCount++ - }, - () => { - b.dynamoBot(); - tech.dynamoBotCount++ - } - ] - const index = Math.floor(Math.random() * bots.length) - for (let i = 0; i < total; i++) bots[index]() - }, - remove() {} - }, - { - name: "growth hacking", - description: "increase combat difficulty by 1 level", - maxCount: 1, - count: 0, - frequency: 0, - isNonRefundable: true, - isExperimentHide: true, - isJunk: true, - allowed() { return true }, - requires: "", - effect() { - level.difficultyIncrease(simulation.difficultyMode) - }, - remove() {} - }, - { - name: "stun", - description: "stun all mobs for up to 8 seconds", - maxCount: 9, - count: 0, - frequency: 0, - isNonRefundable: true, - isExperimentHide: true, - isJunk: true, - allowed() { return true }, - requires: "", - effect() { - for (let i = 0; i < mob.length; i++) mobs.statusStun(mob[i], 480) - }, - remove() {} - }, - { - name: "re-arm", - description: "remove all your guns,
and spawn new ones", - maxCount: 9, - count: 0, - frequency: 0, - isNonRefundable: true, - isExperimentHide: true, - isJunk: true, - allowed() { - return b.inventory.length > 0 - }, - requires: "at least 1 gun", - effect() { - for (let i = 0; i < b.inventory.length; i++) powerUps.spawn(m.pos.x + 60 * (Math.random() - 0.5), m.pos.y + 60 * (Math.random() - 0.5), "gun"); - - //removes guns and ammo - b.inventory = []; - b.activeGun = null; - b.inventoryGun = 0; - for (let i = 0, len = b.guns.length; i < len; ++i) { - b.guns[i].have = false; - if (b.guns[i].ammo !== Infinity) b.guns[i].ammo = 0; - } - simulation.makeGunHUD(); //update gun HUD - }, - remove() {} - }, - { - name: "re-research", - description: `eject all your ${powerUps.orb.research(1)}`, - maxCount: 9, - count: 0, - frequency: 0, - isNonRefundable: true, - isExperimentHide: true, - isJunk: true, - allowed() { - return powerUps.research.count > 3 - }, - requires: "at least 4 research", - effect() { - const dist = 10 * powerUps.research.count + 100 - for (let i = 0; i < powerUps.research.count; i++) { - powerUps.directSpawn(m.pos.x + dist * (Math.random() - 0.5), m.pos.y + dist * (Math.random() - 0.5), "research"); - } - powerUps.research.count = 0 - }, - remove() {} - }, - { - name: "quantum black hole", - description: `use your energy and ${powerUps.orb.research(4)} to spawn
inside the event horizon of a huge black hole`, - maxCount: 9, - count: 0, - frequency: 0, - isNonRefundable: true, - isExperimentHide: true, - isJunk: true, - allowed() { - return powerUps.research.count > 3 - }, - requires: "at least 4 research", - effect() { - m.energy = 0 - spawn.suckerBoss(m.pos.x, m.pos.y - 700) - powerUps.research.changeRerolls(-4) - simulation.makeTextLog(`m.research --
${powerUps.research.count}`) - }, - remove() {} - }, - { - name: "black hole cluster", - description: `spawn 100 nearby black holes`, - maxCount: 9, - count: 0, - frequency: 0, - isNonRefundable: true, - isExperimentHide: true, - isJunk: true, - allowed() { return true }, - requires: "", - effect() { - const unit = { x: 1, y: 0 } - for (let i = 0; i < 100; i++) { - const where = Vector.add(m.pos, Vector.mult(Vector.rotate(unit, Math.random() * 2 * Math.PI), 500 + 200 * Math.random())) - spawn.sucker(where.x, where.y) - } - }, - remove() {} - }, - //************************************************** - //************************************************** undefined / lore - //************************************************** tech - //************************************************** - { - name: `undefined`, - // description: `${lore.techCount+1}/${lore.techGoal}
add copies of this to the potential tech pool`, - description: `this`, - maxCount: 1, - count: 0, - frequency: 3, - frequencyDefault: 3, - isLore: true, - // isNonRefundable: true, - isExperimentHide: true, - allowed() { return true }, - requires: "", - effect() { - setTimeout(() => { //a short delay, I can't remember why - lore.techCount++ - if (lore.techCount === lore.techGoal) { - // tech.removeLoreTechFromPool(); - this.frequency = 0; - this.description = `null is open at level.final()` - } else { - this.frequency += lore.techGoal - // for (let i = 0; i < tech.tech.length; i++) { //set name for all unchosen copies of this tech - // if (tech.tech[i].isLore && tech.tech[i].count === 0) tech.tech[i].description = `${lore.techCount+1}/${lore.techGoal}
add copies of this to the potential tech pool` - // } - // for (let i = 0, len = 10; i < len; i++) tech.addLoreTechToPool() - this.description = `uncaught error:
${Math.max(0,lore.techGoal-lore.techCount)} more required for access to null` - } - }, 1); - }, - remove() { - lore.techCount = 0; - this.maxCount = lore.techGoal; - this.description = `this` + }, 1000); //every 1 seconds + }, + remove() { } + }, + // { + // name: "inverted mouse", + // description: "your mouse is scrambled
it's fine, just rotate it 90 degrees", + // maxCount: 1, + // count: 0, + // frequency: 0, + // isExperimentHide: true, + // isNonRefundable: true, + // isJunk: true, + // allowed() { + // return !m.isShipMode + // }, + // requires: "not ship", + // effect() { + // document.body.addEventListener("mousemove", (e) => { + // const ratio = window.innerWidth / window.innerHeight + // simulation.mouse.x = e.clientY * ratio + // simulation.mouse.y = e.clientX / ratio; + // }); + // }, + // remove() { + // // m.look = m.lookDefault + // } + // }, + { + name: "Fourier analysis", + description: "your aiming is now controlled by this equation:
2sin(0.0133t) + sin(0.013t) + 0.5sin(0.031t)+ 0.33sin(0.03t)", + maxCount: 1, + count: 0, + frequency: 0, + isExperimentHide: true, + isJunk: true, + allowed() { + return !m.isShipMode + }, + requires: "not ship", + effect() { + m.look = () => { + m.angle = 2 * Math.sin(m.cycle * 0.0133) + Math.sin(m.cycle * 0.013) + 0.5 * Math.sin(m.cycle * 0.031) + 0.33 * Math.sin(m.cycle * 0.03) + const scale = 0.8; + simulation.mouse.y + m.transSmoothX = canvas.width2 - m.pos.x - (simulation.mouse.x - canvas.width2) * scale; + m.transSmoothY = canvas.height2 - m.pos.y - (simulation.mouse.y - canvas.height2) * scale; + m.transX += (m.transSmoothX - m.transX) * 0.07; + m.transY += (m.transSmoothY - m.transY) * 0.07; + } + }, + remove() { + if (this.count) m.look = m.lookDefault + } + }, + { + name: "disintegrated armament", + description: "spawn a gun
remove your active gun", + maxCount: 1, + count: 0, + frequency: 0, + isNonRefundable: true, + isExperimentHide: true, + isJunk: true, + allowed() { + return b.inventory.length > 0 + }, + requires: "at least 1 gun", + effect() { + if (b.inventory.length > 0) b.removeGun(b.guns[b.activeGun].name) + simulation.makeGunHUD() + powerUps.spawn(m.pos.x + 60 * (Math.random() - 0.5), m.pos.y + 60 * (Math.random() - 0.5), "gun"); + }, + remove() { } + }, + { + name: "probability", + description: "increase the frequency
of one random tech by 100", + maxCount: 1, + count: 0, + frequency: 0, + isNonRefundable: true, + isExperimentHide: true, + isJunk: true, + allowed() { return true }, + requires: "", + effect() { + let options = []; //find what tech I could get + for (let i = 0, len = tech.tech.length; i < len; i++) { + if ( + tech.tech[i].count < tech.tech[i].maxCount && + tech.tech[i].allowed() && + !tech.tech[i].isJunk && + !tech.tech.isLore + ) { + options.push(i); } } - ], - // addLoreTechToPool() { //adds lore tech to tech pool - // if (!simulation.isCheating) { - // tech.tech.push({ - // name: `undefined`, - // description: `${lore.techCount+1}/${lore.techGoal}
add copies of this to the potential tech pool`, - // maxCount: 1, - // count: 0, - // frequency: 2, - // isLore: true, - // isNonRefundable: true, - // isExperimentHide: true, - // allowed() { - // return true - // }, - // requires: "", - // effect() { - // setTimeout(() => { //a short delay, I can't remember why - // lore.techCount++ - // if (lore.techCount > lore.techGoal - 1) { - // // tech.removeLoreTechFromPool(); - // for (let i = tech.tech.length - 1; i > 0; i--) { - // if (tech.tech[i].isLore && tech.tech[i].count === 0) tech.tech.splice(i, 1) - // } - // } else { - // for (let i = 0; i < tech.tech.length; i++) { //set name for all unchosen copies of this tech - // if (tech.tech[i].isLore && tech.tech[i].count === 0) tech.tech[i].description = `${lore.techCount+1}/${lore.techGoal}
add copies of this to the potential tech pool` - // } - // for (let i = 0, len = 10; i < len; i++) tech.addLoreTechToPool() - // } - // }, 1); - // }, - // remove() {} - // }) - // } - // }, - // junk: [ + if (options.length) { + const index = options[Math.floor(Math.random() * options.length)] + tech.tech[index].frequency = 100 + } + }, + remove() { } + }, + { + name: "encryption", + description: "secure tech information", + maxCount: 1, + count: 0, + frequency: 0, + isNonRefundable: true, + isExperimentHide: true, + isJunk: true, + allowed() { return true }, + requires: "", + effect() { + String.prototype.shuffle = function () { + var a = this.split(""), + n = a.length; - // ], - //variables use for gun tech upgrades - fireRate: null, - bulletSize: null, - energySiphon: null, - healthDrain: null, - isCrouchAmmo: null, - isBulletsLastLonger: null, - isImmortal: null, - sporesOnDeath: null, - isImmuneExplosion: null, - isExplodeMob: null, - isDroneOnDamage: null, - isAcidDmg: null, - isAnnihilation: null, - largerHeals: null, - squirrelFx: null, - isCrit: null, - isLowHealthDmg: null, - isFarAwayDmg: null, - isEntanglement: null, - isMassEnergy: null, - isExtraChoice: null, - laserBotCount: null, - dynamoBotCount: null, - nailBotCount: null, - foamBotCount: null, - boomBotCount: null, - plasmaBotCount: null, - missileBotCount: null, - orbitBotCount: null, - collisionImmuneCycles: null, - blockDmg: null, - isPiezo: null, - isFastDrones: null, - isFastSpores: null, - oneSuperBall: null, - laserReflections: null, - laserDamage: null, - laserFieldDrain: null, - isAmmoFromHealth: null, - mobSpawnWithHealth: null, - isEnergyRecovery: null, - isHealthRecovery: null, - isEnergyLoss: null, - isDeathAvoid: null, - isDeathAvoidedThisLevel: null, - isSporeField: null, - isMissileField: null, - isIceField: null, - isPlasmaRange: null, - isFreezeMobs: null, - isIceCrystals: null, - blockDamage: null, - isBlockStun: null, - isStunField: null, - isHarmDamage: null, - energyRegen: null, - isVacuumBomb: null, - renormalization: null, - fragments: null, - isEnergyDamage: null, - botSpawner: null, - isBotSpawnerReset: null, - isSporeFollow: null, - isNailRadiation: null, - isEnergyHealth: null, - isExplosionStun: null, - restDamage: null, - isRPG: null, - missileCount: null, - isDeterminism: null, - isSuperDeterminism: null, - isHarmReduce: null, - nailsDeathMob: null, - isSlowFPS: null, - isNeutronStun: null, - isAnsatz: null, - isDamageFromBulletCount: null, - isLaserDiode: null, - isNailShot: null, - slowFire: null, - fastTime: null, - squirrelJump: null, - isFastRadiation: null, - isExtraMaxEnergy: null, - isAmmoForGun: null, - isRapidPulse: null, - isPulseAim: null, - isSporeFreeze: null, - isShotgunRecoil: null, - isHealLowHealth: null, - isAoESlow: null, - isHarmArmor: null, - isTurret: null, - isRerollDamage: null, - isHarmFreeze: null, - isBotArmor: null, - isRerollHaste: null, - researchHaste: null, - isMineDrop: null, - isRerollBots: null, - isNailBotUpgrade: null, - isFoamBotUpgrade: null, - isLaserBotUpgrade: null, - isBoomBotUpgrade: null, - isOrbitBotUpgrade: null, - isDroneGrab: null, - isOneGun: null, - isDamageForGuns: null, - isGunCycle: null, - isFastFoam: null, - isSporeGrowth: null, - isStimulatedEmission: null, - nailGun: null, - nailInstantFireRate: null, - isCapacitor: null, - isEnergyNoAmmo: null, - isFreezeHarmImmune: null, - isSmallExplosion: null, - isExplosionHarm: null, - extraMaxHealth: null, - // bonusHealth: null, - isIntangible: null, - isCloakStun: null, - bonusEnergy: null, - healGiveMaxEnergy: null, - healMaxEnergyBonus: 0, //not null - aimDamage: null, - isNoFireDefense: null, - isNoFireDamage: null, - duplicateChance: null, - beamSplitter: null, - iceEnergy: null, - isPerfectBrake: null, - explosiveRadius: null, - isWormholeEnergy: null, - isWormholeDamage: null, - isNailCrit: null, - isFlechetteExplode: null, - isWormholeSpores: null, - isWormBullets: null, - isWideLaser: null, - wideLaser: null, - isPulseLaser: null, - isRadioactive: null, - isRailEnergyGain: null, - isMineSentry: null, - isIncendiary: null, - overfillDrain: null, - isNeutronSlow: null, - // isRailAreaDamage: null, - historyLaser: null, - isSpeedHarm: null, - isSpeedDamage: null, - isTimeSkip: null, - isCancelDuplication: null, - cancelCount: null, - isCancelRerolls: null, - isBotDamage: null, - isBanish: null, - isMaxEnergyTech: null, - isLowEnergyDamage: null, - isRewindBot: null, - isRewindGrenade: null, - isExtruder: null, - isEndLevelPowerUp: null, - missileSize: null, - isLaserMine: null, - isAmmoFoamSize: null, - isIceIX: null, - isDupDamage: null, - isFireRateForGuns: null, - cyclicImmunity: null, - isTechDamage: null, - isRestHarm: null, - isFireMoveLock: null, - isRivets: null, - isNeedles: null, - isExplodeRadio: null, - isGunSwitchField: null, - isShieldPierce: null, - isDuplicateBoss: null, - is111Duplicate: null, - isDynamoBotUpgrade: null, - isBlockPowerUps: null, - foamFutureFire: null, - isDamageAfterKillNoRegen: null, - isHarmReduceNoKill: null, - isSwitchReality: null, - isResearchReality: null, - isAnthropicDamage: null, - isFlipFlop: null, - isFlipFlopHarm: null, - isFlipFlopOn: null, - isFlipFlopLevelReset: null, - isFlipFlopDamage: null, - isFlipFlopEnergy: null, - isRelay: null, - relayIce: null, - isMetaAnalysis: null, - isFoamAttract: null, - droneCycleReduction: null, - droneEnergyReduction: null, - isNoHeals: null, - isAlwaysFire: null, - isDroneRespawn: null, - deathSpawns: null, - isMobBlockFling: null, - blockingIce: null, - isPhaseVelocity: null, - waveBeamSpeed: null, - wavePacketAmplitude: null, - isCollisionRealitySwitch: null, - iceIXOnDeath: null, - wimpCount: null, - isAddBlockMass: null, - isMACHO: null, - isHarmMACHO: null, - isSneakAttack: null, - isFallingDamage: null, - harmonics: null, - isStandingWaveExpand: null, - isTokamak: null, - superBallDelay: null, - isBlockExplode: null, - isOverHeal: null, - isDroneRadioactive: null, - droneRadioDamage: null, - isDroneTeleport: null, - isDroneFastLook: null, - isBulletTeleport: null, - isResearchBoss: null, - isJunkResearch: null, - junkResearchNumber: null, - laserColor: null, - laserColorAlpha: null, - isLongitudinal: null, - is360Longitudinal: null, - isShotgunReversed: null, - wormDuplicate: null, - isCloakingDamage: null, - harmonicEnergy: null, - isFieldHarmReduction: null, - isFastTime: null, - isAnthropicTech: null, - isSporeWorm: null, - isWormShot: null, - isFoamShot: null, - isIceShot: null, - isNeedleShot: null, - isBlockRestitution: null, - isZeno: null, - isFieldFree: null, - wormSurviveDmg: null, - isExtraGunField: null, - isBigField: null, - isMineStun: null, - isSmartRadius: null, - isFilament: null, - isLargeHarpoon: null, - extraHarpoons: null, - ammoCap: null, - isHarpoonPowerUp: null, - harpoonDensity: null, - isAddRemoveMaxHealth: null, - removeMaxHealthOnKill: null, - isSpawnExitTech: null, - cloakDuplication: null, - extruderRange: null, - isForeverDrones: null, - isMoreMobs: null, - nailRecoil: null, - baseJumpForce: null, - baseFx: null, - isNeutronium: null, - isFreeWormHole: null, - isRewindField: null, - isCrouchRegen: null, - isDarts: null, - OccamDamage: null, - } \ No newline at end of file + for (var i = n - 1; i > 0; i--) { + var j = Math.floor(Math.random() * (i + 1)); + var tmp = a[i]; + a[i] = a[j]; + a[j] = tmp; + } + return a.join(""); + } + + for (let i = 0, len = tech.tech.length; i < len; i++) tech.tech[i].name = tech.tech[i].name.shuffle() + }, + remove() { } + }, + { + name: "transparency", + description: "become invisible to yourself
mobs can still see you", + maxCount: 1, + count: 0, + frequency: 0, + isNonRefundable: true, + isExperimentHide: true, + isJunk: true, + allowed() { return true }, + requires: "", + effect() { + m.draw = () => { } + }, + remove() { } + }, + { + name: "quantum leap", + description: "become an alternate version of yourself
every 20 seconds", + maxCount: 1, + count: 0, + frequency: 0, + isNonRefundable: true, + isExperimentHide: true, + isJunk: true, + allowed() { return true }, + requires: "", + effect() { + setInterval(() => { + m.switchWorlds() + simulation.trails() + }, 20000); //every 30 seconds + }, + remove() { } + }, + { + name: "score", + description: "Add a score to n-gon!", + maxCount: 1, + count: 0, + frequency: 0, + isNonRefundable: true, + isExperimentHide: true, + isJunk: true, + allowed() { return true }, + requires: "", + effect() { + setInterval(() => { + let score = Math.ceil(1000 * Math.random() * Math.random() * Math.random() * Math.random() * Math.random()) + simulation.makeTextLog(`simulation.score = ${score.toFixed(0)}`); + }, 10000); //every 10 seconds + }, + remove() { } + }, + { + name: "pop-ups", + description: "sign up to learn endless easy ways to win n-gon
that Landgreen doesn't want you to know!!!1!!", + maxCount: 1, + count: 0, + frequency: 0, + isNonRefundable: true, + isExperimentHide: true, + isJunk: true, + allowed() { return true }, + requires: "", + effect() { + setInterval(() => { + alert(`The best combo is ${tech.tech[Math.floor(Math.random() * tech.tech.length)].name} with ${tech.tech[Math.floor(Math.random() * tech.tech.length)].name}!`); + }, 30000); //every 30 seconds + }, + remove() { } + }, + { + name: "music", + description: "add music to n-gon", + maxCount: 1, + count: 0, + frequency: 0, + isNonRefundable: true, + isExperimentHide: true, + isJunk: true, + allowed() { return true }, + requires: "", + effect() { + window.open('https://www.youtube.com/results?search_query=music', '_blank') + }, + remove() { } + }, + { + name: "performance", + description: "display performance stats to n-gon", + maxCount: 1, + count: 0, + frequency: 0, + isNonRefundable: true, + isExperimentHide: true, + isJunk: true, + allowed() { return true }, + requires: "", + effect() { + (function () { + var script = document.createElement('script'); + script.onload = function () { + var stats = new Stats(); + document.body.appendChild(stats.dom); + requestAnimationFrame(function loop() { + stats.update(); + requestAnimationFrame(loop) + }); + }; + script.src = 'https://unpkg.com/stats.js@0.17.0/build/stats.min.js'; + document.head.appendChild(script); + })() + //move health to the right + document.getElementById("health").style.left = "86px" + document.getElementById("health-bg").style.left = "86px" + }, + remove() { } + }, + { + name: "repartitioning", + description: "set the frequency of finding normal tech to 0
spawn 5 tech", + maxCount: 1, + count: 0, + frequency: 0, + isNonRefundable: true, + isExperimentHide: true, + isJunk: true, + allowed() { return true }, + requires: "", + effect() { + for (let i = 0, len = tech.tech.length; i < len; i++) { + if (tech.tech[i].isJunk) { + tech.tech[i].frequency = 2 + } else { + tech.tech[i].frequency = 0 + } + } + for (let i = 0; i < 5; i++) powerUps.spawn(m.pos.x, m.pos.y, "tech"); + }, + remove() { } + }, + { + name: "defragment", + description: "set the frequency of finding JUNK tech to zero", + maxCount: 1, + count: 0, + frequency: 0, + isNonRefundable: true, + isExperimentHide: true, + isJunk: true, + allowed() { return true }, + requires: "", + effect() { + for (let i = tech.tech.length - 1; i > 0; i--) { + if (tech.tech[i].isJunk) tech.tech[i].frequency = 0 + } + }, + remove() { } + }, + { + name: "ship", + description: "fly around with no legs
reduce combat difficulty by 1 level", + maxCount: 1, + count: 0, + frequency: 0, + isNonRefundable: true, + isExperimentHide: true, + isJunk: true, + allowed() { + return !m.isShipMode && m.fieldUpgrades[m.fieldMode].name !== "negative mass" + }, + requires: "", + effect() { + m.shipMode() + level.difficultyDecrease(simulation.difficultyMode) + }, + remove() { } + }, + // { + // name: "lubrication", + // description: "reduce block density and friction for this level", + // maxCount: 9, + // count: 0, + // frequency: 0, + // isNonRefundable: true, + // isExperimentHide: true, + // isJunk: true, + // allowed() { + // return true + // }, + // requires: "", + // effect() { + // for (let i = 0; i < body.length; i++) { + // Matter.Body.setDensity(body[i], 0.0001) // 0.001 is normal + // body[i].friction = 0.01 + // } + // }, + // remove() {} + // }, + { + name: "pitch", + description: "oscillate the pitch of your world", + maxCount: 1, + count: 0, + frequency: 0, + isNonRefundable: true, + isExperimentHide: true, + isJunk: true, + allowed() { return true }, + requires: "", + effect() { + setInterval(() => { if (!simulation.paused) ctx.rotate(0.001 * Math.sin(simulation.cycle * 0.01)) }, 16); + }, + remove() { } + }, + { + name: "umbra", + description: "produce a blue glow around everything
and probably some simulation lag", + maxCount: 1, + count: 0, + frequency: 0, + isNonRefundable: true, + isExperimentHide: true, + isJunk: true, + allowed() { return true }, + requires: "", + effect() { + ctx.shadowColor = '#06f'; + ctx.shadowBlur = 25; + }, + remove() { } + }, + { + name: "lighter", + description: `ctx.globalCompositeOperation = "lighter"`, + maxCount: 1, + count: 0, + frequency: 0, + isNonRefundable: true, + isExperimentHide: true, + isJunk: true, + allowed() { + return m.fieldUpgrades[m.fieldMode].name !== "negative mass" + }, + requires: "", + effect() { + ctx.globalCompositeOperation = "lighter"; + }, + remove() { } + }, + { + name: "rewind", + description: "every 5 seconds rewind 2 seconds
lasts 120 seconds", + maxCount: 9, + count: 0, + frequency: 0, + isNonRefundable: true, + isExperimentHide: true, + isJunk: true, + allowed() { return true }, + requires: "", + effect() { + for (let i = 0; i < 24; i++) { + setTimeout(() => { m.rewind(120) }, i * 5000); + } + }, + remove() { } + }, + { + name: "energy to mass conversion", + description: "convert your energy into blocks", + maxCount: 9, + count: 0, + frequency: 0, + isNonRefundable: true, + isExperimentHide: true, + isJunk: true, + allowed() { return true }, + requires: "", + effect() { + for (let i = 0, len = 40; i < len; i++) { + setTimeout(() => { + m.energy -= 1 / len + const index = body.length + where = Vector.add(m.pos, { x: 400 * (Math.random() - 0.5), y: 400 * (Math.random() - 0.5) }) + spawn.bodyRect(where.x, where.y, Math.floor(15 + 100 * Math.random()), Math.floor(15 + 100 * Math.random())); + body[index].collisionFilter.category = cat.body; + body[index].collisionFilter.mask = cat.player | cat.map | cat.body | cat.bullet | cat.mob | cat.mobBullet + body[index].classType = "body"; + Composite.add(engine.world, body[index]); //add to world + }, i * 100); + } + + }, + remove() { } + }, + { + name: "level.nextLevel()", + description: "advance to the next level", + maxCount: 9, + count: 0, + frequency: 0, + isNonRefundable: true, + isExperimentHide: true, + isJunk: true, + allowed() { return true }, + requires: "", + effect() { + level.nextLevel(); + }, + remove() { } + }, + { + name: "expert system", + description: "spawn a tech power up
+64% JUNK to the potential tech pool", + maxCount: 9, + count: 0, + frequency: 0, + isNonRefundable: true, + isExperimentHide: true, + isJunk: true, + allowed() { return true }, + requires: "", + effect() { + powerUps.spawn(m.pos.x, m.pos.y, "tech"); + tech.addJunkTechToPool(0.64) + }, + remove() { } + }, + { + name: "energy investment", + description: "every 10 seconds drain your energy
return it doubled 10 seconds later
lasts 180 seconds", + maxCount: 9, + count: 0, + frequency: 0, + isNonRefundable: true, + isExperimentHide: true, + isJunk: true, + allowed() { return true }, + requires: "", + effect() { + for (let i = 0; i < 18; i++) { + setTimeout(() => { //drain energy + const energy = m.energy + m.energy = 0 + setTimeout(() => { //return energy + m.energy += 2 * energy + }, 5000); + }, i * 10000); + } + }, + remove() { } + }, + { + name: "missile Launching System", + description: "fire missiles for the next 60 seconds", + maxCount: 9, + count: 0, + frequency: 0, + isNonRefundable: true, + isExperimentHide: true, + isJunk: true, + allowed() { return true }, + requires: "", + effect() { + for (let i = 0; i < 60; i++) { + setTimeout(() => { + const where = { + x: m.pos.x, + y: m.pos.y - 40 + } + b.missile(where, -Math.PI / 2 + 0.2 * (Math.random() - 0.5) * Math.sqrt(tech.missileCount), -2) + }, i * 1000); + } + }, + remove() { } + }, + { + name: "grenade production", + description: "drop grenades for the next 120 seconds", + maxCount: 9, + count: 0, + frequency: 0, + isNonRefundable: true, + isExperimentHide: true, + isJunk: true, + allowed() { return true }, + requires: "", + effect() { + for (let i = 0; i < 120; i++) { + setTimeout(() => { + b.grenade(Vector.add(m.pos, { x: 10 * (Math.random() - 0.5), y: 10 * (Math.random() - 0.5) }), -Math.PI / 2) //fire different angles for each grenade + const who = bullet[bullet.length - 1] + Matter.Body.setVelocity(who, { + x: who.velocity.x * 0.1, + y: who.velocity.y * 0.1 + }); + }, i * 1000); + } + }, + remove() { } + }, + // { + // name: "inverted input", + // description: "left input becomes right and up input becomes down", + // maxCount: 9, + // count: 0, + // frequency: 0, + // isNonRefundable: true, + // isExperimentHide: true, + // isJunk: true, + // allowed() { + // return true + // }, + // requires: "", + // effect() { + // const left = input.key.left + // input.key.left = input.key.right + // input.key.right = left + + // const up = input.key.up + // input.key.up = input.key.down + // input.key.down = up + // }, + // remove() {} + // }, + { + name: "Sleipnir", + description: "grow more legs", + maxCount: 1, + count: 0, + frequency: 0, + isNonRefundable: true, + isExperimentHide: true, + isJunk: true, + allowed() { + return !m.isShipMode + }, + requires: "", + effect() { + m.draw = function () { + ctx.fillStyle = m.fillColor; + m.walk_cycle += m.flipLegs * m.Vx; + + //draw body + ctx.save(); + ctx.globalAlpha = (m.immuneCycle < m.cycle) ? 1 : 0.5 + ctx.translate(m.pos.x, m.pos.y); + for (let i = 0; i < 16; i++) { + m.calcLeg(Math.PI * i / 8, -3 * i / 16) + m.drawLeg("#444") + } + ctx.rotate(m.angle); + + ctx.beginPath(); + ctx.arc(0, 0, 30, 0, 2 * Math.PI); + ctx.fillStyle = this.bodyGradient + ctx.fill(); + ctx.arc(15, 0, 4, 0, 2 * Math.PI); + ctx.strokeStyle = "#333"; + ctx.lineWidth = 2; + ctx.stroke(); + // ctx.beginPath(); + // ctx.arc(15, 0, 3, 0, 2 * Math.PI); + // ctx.fillStyle = '#0cf'; + // ctx.fill() + ctx.restore(); + m.yOff = m.yOff * 0.85 + m.yOffGoal * 0.15; //smoothly move leg height towards height goal + } + }, + remove() { } + }, + { + name: "diegesis", + description: "indicate gun fire delay
through a rotation of your head", + maxCount: 1, + count: 0, + frequency: 0, + isNonRefundable: true, + isExperimentHide: true, + isJunk: true, + allowed() { + return !m.isShipMode + }, + requires: "", + effect() { + m.draw = function () { + ctx.fillStyle = m.fillColor; + m.walk_cycle += m.flipLegs * m.Vx; + + ctx.save(); + ctx.globalAlpha = (m.immuneCycle < m.cycle) ? 1 : 0.5 + ctx.translate(m.pos.x, m.pos.y); + m.calcLeg(Math.PI, -3); + m.drawLeg("#4a4a4a"); + m.calcLeg(0, 0); + m.drawLeg("#333"); + ctx.rotate(m.angle - (m.fireCDcycle != Infinity ? m.flipLegs * 0.25 * Math.pow(Math.max(m.fireCDcycle - m.cycle, 0), 0.5) : 0)); + + ctx.beginPath(); + ctx.arc(0, 0, 30, 0, 2 * Math.PI); + ctx.fillStyle = this.bodyGradient + ctx.fill(); + ctx.arc(15, 0, 4, 0, 2 * Math.PI); + ctx.strokeStyle = "#333"; + ctx.lineWidth = 2; + ctx.stroke(); + ctx.restore(); + m.yOff = m.yOff * 0.85 + m.yOffGoal * 0.15; //smoothly move leg height towards height goal + } + }, + remove() { } + }, + { + name: "pareidolia", + description: "don't", + maxCount: 1, + count: 0, + frequency: 0, + isNonRefundable: true, + isExperimentHide: true, + isJunk: true, + allowed() { + return !m.isShipMode + }, + requires: "", + effect() { + m.draw = function () { + ctx.fillStyle = m.fillColor; + m.walk_cycle += m.flipLegs * m.Vx; + ctx.save(); + ctx.globalAlpha = (m.immuneCycle < m.cycle) ? 1 : 0.7 + ctx.translate(m.pos.x, m.pos.y); + m.calcLeg(Math.PI, -3); + m.drawLeg("#4a4a4a"); + m.calcLeg(0, 0); + m.drawLeg("#333"); + ctx.rotate(m.angle); + ctx.beginPath(); + ctx.arc(0, 0, 30, 0, 2 * Math.PI); + ctx.fillStyle = this.bodyGradient + ctx.fill(); + ctx.strokeStyle = "#333"; + ctx.lineWidth = 2; + if (!(m.angle > -Math.PI / 2 && m.angle < Math.PI / 2)) ctx.scale(1, -1); //here is the flip + ctx.stroke(); + ctx.beginPath(); + ctx.arc(2, -6, 7, 0, 2 * Math.PI); + ctx.stroke(); + ctx.beginPath(); + ctx.arc(25, -6, 7, 0.25 * Math.PI, 1.6 * Math.PI); + ctx.stroke(); + ctx.beginPath(); + ctx.arc(2, -10, 9, 1.25 * Math.PI, 1.75 * Math.PI); + ctx.stroke(); + ctx.beginPath(); + ctx.arc(25, -10, 9, 1.25 * Math.PI, 1.4 * Math.PI); + ctx.stroke(); + ctx.beginPath(); + ctx.arc(18, 13, 10, 0, 2 * Math.PI); + ctx.fillStyle = this.bodyGradient; + ctx.fill(); + ctx.stroke(); + ctx.beginPath(); + ctx.arc(18, 13, 6, 0, 2 * Math.PI); + ctx.fillStyle = "#555"; + ctx.fill(); + ctx.stroke(); + ctx.beginPath(); + ctx.arc(3, -6, 3, 0, 2 * Math.PI); + ctx.fill(); + ctx.stroke(); + ctx.beginPath(); + ctx.arc(26, -6, 3, 0, 2 * Math.PI); + ctx.fill(); + ctx.stroke(); + ctx.restore(); + m.yOff = m.yOff * 0.85 + m.yOffGoal * 0.15; + } + }, + remove() { } + }, + { + name: "prism", + description: "you cycle through different colors", + maxCount: 1, + count: 0, + frequency: 0, + isNonRefundable: true, + isExperimentHide: true, + isJunk: true, + allowed() { return true }, + requires: "", + effect() { + m.color = { + hue: 0, + sat: 100, + light: 50 + } + setInterval(function () { + m.color.hue++ + m.setFillColors() + }, 10); + }, + remove() { } + }, + { + name: "assimilation", + description: "all your bots are converted to the same random model", + maxCount: 1, + count: 0, + frequency: 0, + isBotTech: true, + isNonRefundable: true, + isExperimentHide: true, + isJunk: true, + allowed() { + return b.totalBots() > 2 + }, + requires: "at least 3 bots", + effect() { + const total = b.totalBots(); + tech.dynamoBotCount = 0; + tech.nailBotCount = 0; + tech.laserBotCount = 0; + tech.orbitBotCount = 0; + tech.foamBotCount = 0; + tech.boomBotCount = 0; + tech.plasmaBotCount = 0; + tech.missileBotCount = 0; + for (let i = 0; i < bullet.length; i++) { + if (bullet[i].botType) bullet[i].endCycle = 0 + } + + const bots = [ + () => { + b.nailBot(); + tech.nailBotCount++; + }, + () => { + b.foamBot(); + tech.foamBotCount++; + }, + () => { + b.boomBot(); + tech.boomBotCount++; + }, + () => { + b.laserBot(); + tech.laserBotCount++; + }, + () => { + b.orbitBot(); + tech.orbitBotCount++ + }, + () => { + b.dynamoBot(); + tech.dynamoBotCount++ + } + ] + const index = Math.floor(Math.random() * bots.length) + for (let i = 0; i < total; i++) bots[index]() + }, + remove() { } + }, + { + name: "growth hacking", + description: "increase combat difficulty by 1 level", + maxCount: 1, + count: 0, + frequency: 0, + isNonRefundable: true, + isExperimentHide: true, + isJunk: true, + allowed() { return true }, + requires: "", + effect() { + level.difficultyIncrease(simulation.difficultyMode) + }, + remove() { } + }, + { + name: "stun", + description: "stun all mobs for up to 8 seconds", + maxCount: 9, + count: 0, + frequency: 0, + isNonRefundable: true, + isExperimentHide: true, + isJunk: true, + allowed() { return true }, + requires: "", + effect() { + for (let i = 0; i < mob.length; i++) mobs.statusStun(mob[i], 480) + }, + remove() { } + }, + { + name: "re-arm", + description: "remove all your guns,
and spawn new ones", + maxCount: 9, + count: 0, + frequency: 0, + isNonRefundable: true, + isExperimentHide: true, + isJunk: true, + allowed() { + return b.inventory.length > 0 + }, + requires: "at least 1 gun", + effect() { + for (let i = 0; i < b.inventory.length; i++) powerUps.spawn(m.pos.x + 60 * (Math.random() - 0.5), m.pos.y + 60 * (Math.random() - 0.5), "gun"); + + //removes guns and ammo + b.inventory = []; + b.activeGun = null; + b.inventoryGun = 0; + for (let i = 0, len = b.guns.length; i < len; ++i) { + b.guns[i].have = false; + if (b.guns[i].ammo !== Infinity) b.guns[i].ammo = 0; + } + simulation.makeGunHUD(); //update gun HUD + }, + remove() { } + }, + { + name: "re-research", + description: `eject all your ${powerUps.orb.research(1)}`, + maxCount: 9, + count: 0, + frequency: 0, + isNonRefundable: true, + isExperimentHide: true, + isJunk: true, + allowed() { + return powerUps.research.count > 3 + }, + requires: "at least 4 research", + effect() { + const dist = 10 * powerUps.research.count + 100 + for (let i = 0; i < powerUps.research.count; i++) { + powerUps.directSpawn(m.pos.x + dist * (Math.random() - 0.5), m.pos.y + dist * (Math.random() - 0.5), "research"); + } + powerUps.research.count = 0 + }, + remove() { } + }, + { + name: "quantum black hole", + description: `use your energy and ${powerUps.orb.research(4)} to spawn
inside the event horizon of a huge black hole`, + maxCount: 9, + count: 0, + frequency: 0, + isNonRefundable: true, + isExperimentHide: true, + isJunk: true, + allowed() { + return powerUps.research.count > 3 + }, + requires: "at least 4 research", + effect() { + m.energy = 0 + spawn.suckerBoss(m.pos.x, m.pos.y - 700) + powerUps.research.changeRerolls(-4) + simulation.makeTextLog(`m.research --
${powerUps.research.count}`) + }, + remove() { } + }, + { + name: "black hole cluster", + description: `spawn 100 nearby black holes`, + maxCount: 9, + count: 0, + frequency: 0, + isNonRefundable: true, + isExperimentHide: true, + isJunk: true, + allowed() { return true }, + requires: "", + effect() { + const unit = { x: 1, y: 0 } + for (let i = 0; i < 100; i++) { + const where = Vector.add(m.pos, Vector.mult(Vector.rotate(unit, Math.random() * 2 * Math.PI), 500 + 200 * Math.random())) + spawn.sucker(where.x, where.y) + } + }, + remove() { } + }, + //************************************************** + //************************************************** undefined / lore + //************************************************** tech + //************************************************** + { + name: `undefined`, + // description: `${lore.techCount+1}/${lore.techGoal}
add copies of this to the potential tech pool`, + description: `this`, + maxCount: 1, + count: 0, + frequency: 3, + frequencyDefault: 3, + isLore: true, + // isNonRefundable: true, + isExperimentHide: true, + allowed() { return true }, + requires: "", + effect() { + setTimeout(() => { //a short delay, I can't remember why + lore.techCount++ + if (lore.techCount === lore.techGoal) { + // tech.removeLoreTechFromPool(); + this.frequency = 0; + this.description = `null is open at level.final()` + } else { + this.frequency += lore.techGoal + // for (let i = 0; i < tech.tech.length; i++) { //set name for all unchosen copies of this tech + // if (tech.tech[i].isLore && tech.tech[i].count === 0) tech.tech[i].description = `${lore.techCount+1}/${lore.techGoal}
add copies of this to the potential tech pool` + // } + // for (let i = 0, len = 10; i < len; i++) tech.addLoreTechToPool() + this.description = `uncaught error:
${Math.max(0, lore.techGoal - lore.techCount)} more required for access to null` + } + }, 1); + }, + remove() { + lore.techCount = 0; + this.maxCount = lore.techGoal; + this.description = `this` + } + } + ], + // addLoreTechToPool() { //adds lore tech to tech pool + // if (!simulation.isCheating) { + // tech.tech.push({ + // name: `undefined`, + // description: `${lore.techCount+1}/${lore.techGoal}
add copies of this to the potential tech pool`, + // maxCount: 1, + // count: 0, + // frequency: 2, + // isLore: true, + // isNonRefundable: true, + // isExperimentHide: true, + // allowed() { + // return true + // }, + // requires: "", + // effect() { + // setTimeout(() => { //a short delay, I can't remember why + // lore.techCount++ + // if (lore.techCount > lore.techGoal - 1) { + // // tech.removeLoreTechFromPool(); + // for (let i = tech.tech.length - 1; i > 0; i--) { + // if (tech.tech[i].isLore && tech.tech[i].count === 0) tech.tech.splice(i, 1) + // } + // } else { + // for (let i = 0; i < tech.tech.length; i++) { //set name for all unchosen copies of this tech + // if (tech.tech[i].isLore && tech.tech[i].count === 0) tech.tech[i].description = `${lore.techCount+1}/${lore.techGoal}
add copies of this to the potential tech pool` + // } + // for (let i = 0, len = 10; i < len; i++) tech.addLoreTechToPool() + // } + // }, 1); + // }, + // remove() {} + // }) + // } + // }, + // junk: [ + + // ], + //variables use for gun tech upgrades + fireRate: null, + bulletSize: null, + energySiphon: null, + healthDrain: null, + isCrouchAmmo: null, + isBulletsLastLonger: null, + isImmortal: null, + sporesOnDeath: null, + isImmuneExplosion: null, + isExplodeMob: null, + isDroneOnDamage: null, + isAcidDmg: null, + isAnnihilation: null, + largerHeals: null, + squirrelFx: null, + isCrit: null, + isLowHealthDmg: null, + isFarAwayDmg: null, + isEntanglement: null, + isMassEnergy: null, + isExtraChoice: null, + laserBotCount: null, + dynamoBotCount: null, + nailBotCount: null, + foamBotCount: null, + boomBotCount: null, + plasmaBotCount: null, + missileBotCount: null, + orbitBotCount: null, + collisionImmuneCycles: null, + blockDmg: null, + isPiezo: null, + isFastDrones: null, + isFastSpores: null, + oneSuperBall: null, + laserReflections: null, + laserDamage: null, + laserFieldDrain: null, + isAmmoFromHealth: null, + mobSpawnWithHealth: null, + isEnergyRecovery: null, + isHealthRecovery: null, + isEnergyLoss: null, + isDeathAvoid: null, + isDeathAvoidedThisLevel: null, + isSporeField: null, + isMissileField: null, + isIceField: null, + isPlasmaRange: null, + isFreezeMobs: null, + isIceCrystals: null, + blockDamage: null, + isBlockStun: null, + isStunField: null, + isHarmDamage: null, + energyRegen: null, + isVacuumBomb: null, + renormalization: null, + fragments: null, + isEnergyDamage: null, + botSpawner: null, + isBotSpawnerReset: null, + isSporeFollow: null, + isNailRadiation: null, + isEnergyHealth: null, + isExplosionStun: null, + restDamage: null, + isRPG: null, + missileCount: null, + isDeterminism: null, + isSuperDeterminism: null, + isHarmReduce: null, + nailsDeathMob: null, + isSlowFPS: null, + isNeutronStun: null, + isAnsatz: null, + isDamageFromBulletCount: null, + isLaserDiode: null, + isNailShot: null, + slowFire: null, + fastTime: null, + squirrelJump: null, + isFastRadiation: null, + isExtraMaxEnergy: null, + isAmmoForGun: null, + isRapidPulse: null, + isPulseAim: null, + isSporeFreeze: null, + isShotgunRecoil: null, + isHealLowHealth: null, + isAoESlow: null, + isHarmArmor: null, + isTurret: null, + isRerollDamage: null, + isHarmFreeze: null, + isBotArmor: null, + isRerollHaste: null, + researchHaste: null, + isMineDrop: null, + isRerollBots: null, + isNailBotUpgrade: null, + isFoamBotUpgrade: null, + isLaserBotUpgrade: null, + isBoomBotUpgrade: null, + isOrbitBotUpgrade: null, + isDroneGrab: null, + isOneGun: null, + isDamageForGuns: null, + isGunCycle: null, + isFastFoam: null, + isSporeGrowth: null, + isStimulatedEmission: null, + nailGun: null, + nailInstantFireRate: null, + isCapacitor: null, + isEnergyNoAmmo: null, + isFreezeHarmImmune: null, + isSmallExplosion: null, + isExplosionHarm: null, + extraMaxHealth: null, + // bonusHealth: null, + isIntangible: null, + isCloakStun: null, + bonusEnergy: null, + healGiveMaxEnergy: null, + healMaxEnergyBonus: 0, //not null + aimDamage: null, + isNoFireDefense: null, + isNoFireDamage: null, + duplicateChance: null, + beamSplitter: null, + iceEnergy: null, + isPerfectBrake: null, + explosiveRadius: null, + isWormholeEnergy: null, + isWormholeDamage: null, + isNailCrit: null, + isFlechetteExplode: null, + isWormholeSpores: null, + isWormBullets: null, + isWideLaser: null, + wideLaser: null, + isPulseLaser: null, + isRadioactive: null, + isRailEnergyGain: null, + isMineSentry: null, + isIncendiary: null, + overfillDrain: null, + isNeutronSlow: null, + // isRailAreaDamage: null, + historyLaser: null, + isSpeedHarm: null, + isSpeedDamage: null, + isTimeSkip: null, + isCancelDuplication: null, + cancelCount: null, + isCancelRerolls: null, + isBotDamage: null, + isBanish: null, + isMaxEnergyTech: null, + isLowEnergyDamage: null, + isRewindBot: null, + isRewindGrenade: null, + isExtruder: null, + isEndLevelPowerUp: null, + missileSize: null, + isLaserMine: null, + isAmmoFoamSize: null, + isIceIX: null, + isDupDamage: null, + isFireRateForGuns: null, + cyclicImmunity: null, + isTechDamage: null, + isRestHarm: null, + isFireMoveLock: null, + isRivets: null, + isNeedles: null, + isExplodeRadio: null, + isGunSwitchField: null, + isShieldPierce: null, + isDuplicateBoss: null, + is111Duplicate: null, + isDynamoBotUpgrade: null, + isBlockPowerUps: null, + foamFutureFire: null, + isDamageAfterKillNoRegen: null, + isHarmReduceNoKill: null, + isSwitchReality: null, + isResearchReality: null, + isAnthropicDamage: null, + isFlipFlop: null, + isFlipFlopHarm: null, + isFlipFlopOn: null, + isFlipFlopLevelReset: null, + isFlipFlopDamage: null, + isFlipFlopEnergy: null, + isRelay: null, + relayIce: null, + isMetaAnalysis: null, + isFoamAttract: null, + droneCycleReduction: null, + droneEnergyReduction: null, + isNoHeals: null, + isAlwaysFire: null, + isDroneRespawn: null, + deathSpawns: null, + isMobBlockFling: null, + blockingIce: null, + isPhaseVelocity: null, + waveBeamSpeed: null, + wavePacketAmplitude: null, + isCollisionRealitySwitch: null, + iceIXOnDeath: null, + wimpCount: null, + isAddBlockMass: null, + isMACHO: null, + isHarmMACHO: null, + isSneakAttack: null, + isFallingDamage: null, + harmonics: null, + isStandingWaveExpand: null, + isTokamak: null, + superBallDelay: null, + isBlockExplode: null, + isOverHeal: null, + isDroneRadioactive: null, + droneRadioDamage: null, + isDroneTeleport: null, + isDroneFastLook: null, + isBulletTeleport: null, + isResearchBoss: null, + isJunkResearch: null, + junkResearchNumber: null, + laserColor: null, + laserColorAlpha: null, + isLongitudinal: null, + is360Longitudinal: null, + isShotgunReversed: null, + wormDuplicate: null, + isCloakingDamage: null, + harmonicEnergy: null, + isFieldHarmReduction: null, + isFastTime: null, + isAnthropicTech: null, + isSporeWorm: null, + isWormShot: null, + isFoamShot: null, + isIceShot: null, + isNeedleShot: null, + isBlockRestitution: null, + isZeno: null, + isFieldFree: null, + wormSurviveDmg: null, + isExtraGunField: null, + isBigField: null, + isMineStun: null, + isSmartRadius: null, + isFilament: null, + isLargeHarpoon: null, + extraHarpoons: null, + ammoCap: null, + isHarpoonPowerUp: null, + harpoonDensity: null, + isAddRemoveMaxHealth: null, + removeMaxHealthOnKill: null, + isSpawnExitTech: null, + cloakDuplication: null, + extruderRange: null, + isForeverDrones: null, + isMoreMobs: null, + nailRecoil: null, + baseJumpForce: null, + baseFx: null, + isNeutronium: null, + isFreeWormHole: null, + isRewindField: null, + isCrouchRegen: null, + isDarts: null, + OccamDamage: null, + isAxion: null, + isWormholeMapIgnore: null, + isLessDamageReduction: null +} \ No newline at end of file diff --git a/todo.txt b/todo.txt index 1bc343b..5dcbf72 100644 --- a/todo.txt +++ b/todo.txt @@ -1,50 +1,46 @@ ******************************************************** NEXT PATCH ************************************************** -change: torpor - if you have NOT killed a mob in the last 5 seconds reduce harm by 66% (no negative effect though) +tech: affine connection - wormholes can now tunnel through the map at 200% increased energy cost +tech: regression - after bullets hit a mob, the mob takes 5% more future damage (0.5% for bosses) +tech: axion - while inside the MACHO halo, 75% of your total harm reduction is added as damage +mob: launcherOne - launches 1 big seeker bullet that chases you +black holes boss and final boss spawn big seeker bullets that chases you during the black hole phase + +applied science no longer gives research (just a random tech for every gun you have) +bot fabrication increase cost every 5 -> x6 bots + +average console time to disappear is 3 -> 4 seconds +ammo power ups no longer log ammo to in game console for performance reasons + +JUNK tech: catabolysis - set max health to 1; double your current ammo 10 times (2^10 = 1024x ammo) ******************************************************** TODO ******************************************************** -reduce some common in game console triggers, but lengthen default console time - don't log ammo? - don't log health - cap length of console? +new late game level that is easier if you can: platform well, jump high, immune to slime, wormhole through walls, fly fast + climb vertically to avoid rising slime -tech if you are inside the MACHO halo get a benefit +field tech - disable blocking, but does high damage to mobs inside field + and maybe slows mobs it damages + +mob/boss that fires a laser at player, but give player time to avoid + laser isn't always on + they target where player was 1 second ago + they turn to face player? dart: a new bullet type for string-less harpoons can turn harder + dart, draw quick line to indicate targeting can get new targets? - -convert tech descriptions into a method() - this means the text would generate when you: press pause, or display options, or open experiment mode - this would allow the description to reference variables inside it, like this.count - who could use this: - Occam's razor - tech: dart - alt fire several small harpoons, with guidance requires not railgun -tech: after bullets hit a mob, the mob takes 1% more damage - this.damageReduction *= 1.01 - only for drones? - only for drones, spores, worms, ice-IX? - - -make it easier to push blocks around with your body - part of negative mass? - link to other block tech - -tech: instead of throwing a block, give bots that last for 20 seconds - tech: open a new tab for n-gon, spawn things in the original game based on events in new game if you die in new die in original? new is n-gon classic? make a JUNK tech? if you die in original open a tab with a new n-gon that starts on a random level with a random load out. if you clear the level you come back to life in the original? -tech or field aspect - Firing now doesn't alert any mob but the mob you hit - bug - death while paused crashes game? tech rocket jump - jumping produces an explosion at your feet that lets you jump extra high, but does some damage @@ -448,7 +444,6 @@ level boss: fires a line intersection in a random direction every few seconds. ******************************************************** LORE ******************************************************** possible names for tech - astrophage strange loop homeostasis holonomy - parallel transport of a vector leads to movement (applies to curved space) @@ -461,7 +456,6 @@ possible names for tech Gödel's incompleteness quantum zeno effect (perturbation of a system prevents some systems from evolving because it scrambles coherence) (apply to lasers, fields) counterfactual - something false - axion - maybe a 3rd dark matter type tech Pigeonhole principle - if there are several things that are matched up regression to the mean phlogiston theory is a superseded scientific theory that postulated the existence of a fire-like element called phlogiston @@ -469,13 +463,13 @@ possible names for tech evolutionary cosmology eternal inflation hypergraph - gnarl SQUID (for superconducting quantum interference device) is a very sensitive magnetometer used to measure extremely subtle magnetic fields, based on superconducting loops containing Josephson junctions. nuclear pasta - hard matter in neutron star nonlocal fine-tuned universe eternalism https://en.wikipedia.org/wiki/Eternalism_(philosophy_of_time) axial motor + hall effect thrusters a tutorial / lore intro needs to be optional so it doesn't slow experienced players