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