diff --git a/.DS_Store b/.DS_Store index addbf77..4cbfe6b 100644 Binary files a/.DS_Store and b/.DS_Store differ diff --git a/js/bullet.js b/js/bullet.js index 9969664..612b75d 100644 --- a/js/bullet.js +++ b/js/bullet.js @@ -231,7 +231,7 @@ const b = { fireCD: 1, setFireCD() { b.fireCD = tech.fireRate * tech.slowFire * tech.researchHaste * tech.aimDamage / tech.fastTime - if (tech.isFireRateForGuns) b.fireCD *= Math.pow(0.85, b.inventory.length) + if (tech.isFireRateForGuns) b.fireCD *= Math.pow(0.83, b.inventory.length) if (tech.isFireNotMove) b.fireCD *= 0.33 }, fireAttributes(dir, rotate = true) { @@ -2227,21 +2227,21 @@ const b = { // ************************************************************************************************** // ************************************************************************************************** respawnBots() { - for (let i = 0; i < tech.dynamoBotCount; i++) b.dynamoBot({ x: m.pos.x + 50 * (Math.random() - 0.5), y: m.pos.y + 50 * (Math.random() - 0.5) }, false) - for (let i = 0; i < tech.laserBotCount; i++) b.laserBot({ x: m.pos.x + 50 * (Math.random() - 0.5), y: m.pos.y + 50 * (Math.random() - 0.5) }, false) - for (let i = 0; i < tech.nailBotCount; i++) b.nailBot({ x: m.pos.x + 50 * (Math.random() - 0.5), y: m.pos.y + 50 * (Math.random() - 0.5) }, false) - for (let i = 0; i < tech.foamBotCount; i++) b.foamBot({ x: m.pos.x + 50 * (Math.random() - 0.5), y: m.pos.y + 50 * (Math.random() - 0.5) }, false) - for (let i = 0; i < tech.boomBotCount; i++) b.boomBot({ x: m.pos.x + 50 * (Math.random() - 0.5), y: m.pos.y + 50 * (Math.random() - 0.5) }, false) - for (let i = 0; i < tech.orbitBotCount; i++) b.orbitBot({ x: m.pos.x + 50 * (Math.random() - 0.5), y: m.pos.y + 50 * (Math.random() - 0.5) }, false) - for (let i = 0; i < tech.plasmaBotCount; i++) b.plasmaBot({ x: m.pos.x + 50 * (Math.random() - 0.5), y: m.pos.y + 50 * (Math.random() - 0.5) }, false) - for (let i = 0; i < tech.missileBotCount; i++) b.missileBot({ x: m.pos.x + 50 * (Math.random() - 0.5), y: m.pos.y + 50 * (Math.random() - 0.5) }, false) + for (let i = 0; i < tech.dynamoBotCount; i++) b.dynamoBot({ x: player.position.x + 50 * (Math.random() - 0.5), y: player.position.y + 50 * (Math.random() - 0.5) }, false) + for (let i = 0; i < tech.laserBotCount; i++) b.laserBot({ x: player.position.x + 50 * (Math.random() - 0.5), y: player.position.y + 50 * (Math.random() - 0.5) }, false) + for (let i = 0; i < tech.nailBotCount; i++) b.nailBot({ x: player.position.x + 50 * (Math.random() - 0.5), y: player.position.y + 50 * (Math.random() - 0.5) }, false) + for (let i = 0; i < tech.foamBotCount; i++) b.foamBot({ x: player.position.x + 50 * (Math.random() - 0.5), y: player.position.y + 50 * (Math.random() - 0.5) }, false) + for (let i = 0; i < tech.boomBotCount; i++) b.boomBot({ x: player.position.x + 50 * (Math.random() - 0.5), y: player.position.y + 50 * (Math.random() - 0.5) }, false) + for (let i = 0; i < tech.orbitBotCount; i++) b.orbitBot({ x: player.position.x + 50 * (Math.random() - 0.5), y: player.position.y + 50 * (Math.random() - 0.5) }, false) + for (let i = 0; i < tech.plasmaBotCount; i++) b.plasmaBot({ x: player.position.x + 50 * (Math.random() - 0.5), y: player.position.y + 50 * (Math.random() - 0.5) }, false) + for (let i = 0; i < tech.missileBotCount; i++) b.missileBot({ x: player.position.x + 50 * (Math.random() - 0.5), y: player.position.y + 50 * (Math.random() - 0.5) }, false) if (tech.isIntangible && m.isCloak) { for (let i = 0; i < bullet.length; i++) { if (bullet[i].botType) bullet[i].collisionFilter.mask = cat.map | cat.bullet | cat.mobBullet | cat.mobShield } } }, - randomBot(where = m.pos, isKeep = true, isAll = true) { + randomBot(where = player.position, isKeep = true, isAll = true) { if (Math.random() < 0.167 && isAll) { b.dynamoBot(where) if (isKeep) tech.dynamoBotCount++; @@ -2277,7 +2277,7 @@ const b = { } } }, - dynamoBot(position = m.pos, isConsole = true) { + dynamoBot(position = player.position, isConsole = true) { if (isConsole) simulation.makeTextLog(`b.dynamoBot()`); const me = bullet.length; bullet[me] = Bodies.polygon(position.x, position.y, 5, 10, { @@ -2305,17 +2305,17 @@ const b = { followDelay: 0, phase: Math.floor(60 * Math.random()), do() { - // if (Vector.magnitude(Vector.sub(this.position, m.pos)) < 150) { + // if (Vector.magnitude(Vector.sub(this.position, player.position)) < 150) { // ctx.fillStyle = "rgba(0,0,0,0.06)"; // ctx.beginPath(); // ctx.arc(this.position.x, this.position.y, 150, 0, 2 * Math.PI); // ctx.fill(); // } if (!((m.cycle + this.phase) % 30)) { //twice a second - if (Vector.magnitude(Vector.sub(this.position, m.pos)) < 250) { //give energy + if (Vector.magnitude(Vector.sub(this.position, player.position)) < 250) { //give energy Matter.Body.setAngularVelocity(this, this.spin) if (this.isUpgraded) { - m.energy += 0.06 + m.energy += 0.12 simulation.drawList.push({ //add dmg to draw queue x: this.position.x, y: this.position.y, @@ -2324,7 +2324,7 @@ const b = { time: simulation.drawTime }); } else { - m.energy += 0.02 + m.energy += 0.03 simulation.drawList.push({ //add dmg to draw queue x: this.position.x, y: this.position.y, @@ -2371,7 +2371,7 @@ const b = { World.add(engine.world, bullet[me]); //add bullet to world b.setDynamoBotDelay() }, - nailBot(position = { x: m.pos.x + 50 * (Math.random() - 0.5), y: m.pos.y + 50 * (Math.random() - 0.5) }, isConsole = true) { + nailBot(position = { x: player.position.x + 50 * (Math.random() - 0.5), y: player.position.y + 50 * (Math.random() - 0.5) }, isConsole = true) { if (isConsole) simulation.makeTextLog(`b.nailBot()`); const me = bullet.length; const dir = m.angle; @@ -2406,13 +2406,14 @@ const b = { Matter.Body.setVelocity(this, Vector.add(Vector.mult(this.velocity, 0.90), Vector.mult(player.velocity, 0.17))); //add player's velocity if (this.lastLookCycle < simulation.cycle && !m.isCloak) { - this.lastLookCycle = simulation.cycle + (this.isUpgraded ? 15 : 80) + this.lastLookCycle = simulation.cycle + (this.isUpgraded ? 13 : 80) let target for (let i = 0, len = mob.length; i < len; i++) { const dist = Vector.magnitudeSquared(Vector.sub(this.position, mob[i].position)); if (dist < 3000000 && //1400*1400 Matter.Query.ray(map, this.position, mob[i].position).length === 0 && - Matter.Query.ray(body, this.position, mob[i].position).length === 0) { + Matter.Query.ray(body, this.position, mob[i].position).length === 0 && + !mob[i].isShielded) { target = Vector.add(mob[i].position, Vector.mult(mob[i].velocity, Math.sqrt(dist) / 60)) const SPEED = 50 const unit = Vector.normalise(Vector.sub(target, this.position)) @@ -2427,7 +2428,7 @@ const b = { }) World.add(engine.world, bullet[me]); //add bullet to world }, - missileBot(position = { x: m.pos.x + 50 * (Math.random() - 0.5), y: m.pos.y + 50 * (Math.random() - 0.5) }, isConsole = true) { + missileBot(position = { x: player.position.x + 50 * (Math.random() - 0.5), y: player.position.y + 50 * (Math.random() - 0.5) }, isConsole = true) { if (isConsole) simulation.makeTextLog(`b.missileBot()`); const me = bullet.length; bullet[me] = Bodies.rectangle(position.x, position.y, 28, 11, { @@ -2478,7 +2479,7 @@ const b = { }) World.add(engine.world, bullet[me]); //add bullet to world }, - foamBot(position = { x: m.pos.x + 50 * (Math.random() - 0.5), y: m.pos.y + 50 * (Math.random() - 0.5) }, isConsole = true) { + foamBot(position = { x: player.position.x + 50 * (Math.random() - 0.5), y: player.position.y + 50 * (Math.random() - 0.5) }, isConsole = true) { if (isConsole) simulation.makeTextLog(`b.foamBot()`); const me = bullet.length; const dir = m.angle; @@ -2493,7 +2494,7 @@ const b = { restitution: 0.6 * (1 + 0.5 * Math.random()), dmg: 0, // 0.14 //damage done in addition to the damage from momentum minDmgSpeed: 2, - lookFrequency: 60 + Math.floor(17 * Math.random()) - 30 * tech.isFoamBotUpgrade, + lookFrequency: 60 + Math.floor(17 * Math.random()) - 35 * tech.isFoamBotUpgrade, cd: 0, delay: 100, acceleration: 0.005 * (1 + 0.5 * Math.random()), @@ -2523,7 +2524,7 @@ const b = { const radius = 6 + 7 * Math.random() const SPEED = 29 - radius * 0.5; //(m.crouch ? 32 : 20) - radius * 0.7; const velocity = Vector.mult(Vector.normalise(Vector.sub(target, this.position)), SPEED) - b.foam(this.position, velocity, radius + 7 * this.isUpgraded) + b.foam(this.position, velocity, radius + 8 * this.isUpgraded) break; } } @@ -2533,7 +2534,7 @@ const b = { }) World.add(engine.world, bullet[me]); //add bullet to world }, - laserBot(position = { x: m.pos.x + 50 * (Math.random() - 0.5), y: m.pos.y + 50 * (Math.random() - 0.5) }, isConsole = true) { + laserBot(position = { x: player.position.x + 50 * (Math.random() - 0.5), y: player.position.y + 50 * (Math.random() - 0.5) }, isConsole = true) { if (isConsole) simulation.makeTextLog(`b.laserBot()`); const me = bullet.length; const dir = m.angle; @@ -2605,7 +2606,7 @@ const b = { //hit target with laser if (this.lockedOn && this.lockedOn.alive && m.energy > this.drainThreshold) { m.energy -= tech.laserFieldDrain * tech.isLaserDiode - b.laser(this.vertices[0], this.lockedOn.position, b.dmgScale * (0.38 * tech.laserDamage + this.isUpgraded * 0.21), tech.laserReflections, false, 0.4) //tech.laserDamage = 0.16 + b.laser(this.vertices[0], this.lockedOn.position, b.dmgScale * (0.38 * tech.laserDamage + this.isUpgraded * 0.25), tech.laserReflections, false, 0.4) //tech.laserDamage = 0.16 // laser(where = { // x: m.pos.x + 20 * Math.cos(m.angle), // y: m.pos.y + 20 * Math.sin(m.angle) @@ -2618,7 +2619,7 @@ const b = { }) World.add(engine.world, bullet[me]); //add bullet to world }, - boomBot(position = { x: m.pos.x + 50 * (Math.random() - 0.5), y: m.pos.y + 50 * (Math.random() - 0.5) }, isConsole = true) { + boomBot(position = { x: player.position.x + 50 * (Math.random() - 0.5), y: player.position.y + 50 * (Math.random() - 0.5) }, isConsole = true) { if (isConsole) simulation.makeTextLog(`b.boomBot()`); const me = bullet.length; const dir = m.angle; @@ -2633,9 +2634,10 @@ const b = { restitution: 1, dmg: 0, minDmgSpeed: 0, - lookFrequency: 43 + Math.floor(7 * Math.random()), + lookFrequency: 43 + Math.floor(7 * Math.random()) - 10 * tech.isBoomBotUpgrade, acceleration: 0.005 * (1 + 0.5 * Math.random()), - range: 500 * (1 + 0.1 * Math.random()) + 350 * tech.isBoomBotUpgrade, + attackAcceleration: 0.012 + 0.005 * tech.isBoomBotUpgrade, + range: 500 * (1 + 0.1 * Math.random()) + 400 * tech.isBoomBotUpgrade, endCycle: Infinity, classType: "bullet", collisionFilter: { @@ -2646,7 +2648,7 @@ const b = { explode: 0, beforeDmg() { if (this.lockedOn) { - const explosionRadius = Math.min(170 + 200 * this.isUpgraded, Vector.magnitude(Vector.sub(this.position, m.pos)) - 30) + const explosionRadius = Math.min(170 + 220 * this.isUpgraded, Vector.magnitude(Vector.sub(this.position, m.pos)) - 30) if (explosionRadius > 60) { this.explode = explosionRadius // @@ -2690,14 +2692,14 @@ const b = { if (DIST - this.lockedOn.radius < this.range && Matter.Query.ray(map, this.position, this.lockedOn.position).length === 0) { //move towards the target - this.force = Vector.add(this.force, Vector.mult(Vector.normalise(Vector.sub(this.lockedOn.position, this.position)), 0.012 * this.mass)) + this.force = Vector.add(this.force, Vector.mult(Vector.normalise(Vector.sub(this.lockedOn.position, this.position)), this.attackAcceleration * this.mass)) } } } }) World.add(engine.world, bullet[me]); //add bullet to world }, - plasmaBot(position = { x: m.pos.x + 50 * (Math.random() - 0.5), y: m.pos.y + 50 * (Math.random() - 0.5) }, isConsole = true) { + plasmaBot(position = { x: player.position.x + 50 * (Math.random() - 0.5), y: player.position.y + 50 * (Math.random() - 0.5) }, isConsole = true) { if (isConsole) simulation.makeTextLog(`b.plasmaBot()`); const me = bullet.length; const dir = m.angle; @@ -2882,7 +2884,7 @@ const b = { }) World.add(engine.world, bullet[me]); //add bullet to world }, - orbitBot(position = m.pos, isConsole = true) { + orbitBot(position = player.position, isConsole = true) { if (isConsole) simulation.makeTextLog(`b.orbitBot()`); const me = bullet.length; bullet[me] = Bodies.polygon(position.x, position.y, 9, 12, { @@ -2921,6 +2923,7 @@ const b = { orbitalSpeed: 0, phase: 2 * Math.PI * Math.random(), do() { + //check for damage if (!m.isCloak && !m.isBodiesAsleep) { //if time dilation isn't active // q = Matter.Query.point(mob, this.position) @@ -2938,7 +2941,7 @@ const b = { }) for (let i = 0; i < q.length; i++) { mobs.statusStun(q[i], 180) - const dmg = 0.5 * b.dmgScale * (this.isUpgraded ? 2.5 : 1) * (tech.isCrit ? 4 : 1) + const dmg = 0.5 * b.dmgScale * (this.isUpgraded ? 3 : 1) * (tech.isCrit ? 4 : 1) q[i].damage(dmg); q[i].foundPlayer(); simulation.drawList.push({ //add dmg to draw queue diff --git a/js/index.js b/js/index.js index a40ce79..5716862 100644 --- a/js/index.js +++ b/js/index.js @@ -297,7 +297,7 @@ const build = { //update tech text //disable not allowed tech for (let i = 0, len = tech.tech.length; i < len; i++) { const techID = document.getElementById("tech-" + i) - if (!tech.tech[i].isCustomHide) { + if (!tech.tech[i].isExperimentHide) { if (tech.tech[i].allowed() || isAllowed || tech.tech[i].count > 0) { const isCount = tech.tech[i].count > 1 ? `(${tech.tech[i].count}x)` : ""; if (tech.tech[i].isFieldTech) { @@ -384,7 +384,7 @@ const build = { } for (let i = 0, len = tech.tech.length; i < len; i++) { - if (!tech.tech[i].isCustomHide) { + if (!tech.tech[i].isExperimentHide) { if (!tech.tech[i].allowed()) { // || tech.tech[i].name === "+1 cardinality") { //|| tech.tech[i].name === "leveraged investment" text += `
${tech.tech[i].name}
requires: ${tech.tech[i].requires}
` // } else if (tech.tech[i].count > 1) { @@ -754,7 +754,9 @@ window.addEventListener("keydown", function(event) { simulation.testing = false; simulation.loop = simulation.normalLoop if (simulation.isConstructionMode) document.getElementById("construct").style.display = 'none' - simulation.makeTextLog(`exiting testing mode`); + // document.getElementById("text-log").innerHTML = "" + simulation.lastLogTime = 0 //clear text log + // simulation.makeTextLog(`exiting testing mode`); } else { //if (keys[191]) simulation.testing = true; simulation.loop = simulation.testingLoop @@ -783,7 +785,7 @@ window.addEventListener("keydown", function(event) { H - infinite health + brief harm immunity N @@ -872,10 +874,11 @@ window.addEventListener("keydown", function(event) { b.giveGuns("all", 1000) break case "h": - m.health = Infinity + // m.health = Infinity + m.immuneCycle = Infinity // m.energy = Infinity - document.getElementById("health").style.display = "none" - document.getElementById("health-bg").style.display = "none" + // document.getElementById("health").style.display = "none" + // document.getElementById("health-bg").style.display = "none" break case "n": m.addHealth(Infinity) diff --git a/js/level.js b/js/level.js index f938918..51e1d06 100644 --- a/js/level.js +++ b/js/level.js @@ -29,10 +29,13 @@ const level = { // tech.isMineSentry = true // for (let i = 0; i < 60; i++) tech.giveTech("rivet diameter") // tech.giveTech("missile-bot") + // for (let i = 0; i < 5; i++) // tech.giveTech("nail-bot") + // for (let i = 0; i < 2; i++) tech.giveTech("foam-bot") // for (let i = 0; i < 15; i++) tech.giveTech("plasma jet") // tech.isBlockPowerUps = true; // m.shipMode() + // tech.isBotSwap = true; level.intro(); //starting level // level.testing(); //not in rotation @@ -80,7 +83,7 @@ const level = { b.respawnBots(); m.resetHistory(); if (tech.isArmorFromPowerUps) { - tech.armorFromPowerUps += Math.min(0.03 * powerUps.totalPowerUps, 0.42) + tech.armorFromPowerUps += Math.min(0.03 * powerUps.totalPowerUps, 0.51) m.setMaxHealth(); } if (tech.isHealLowHealth) { @@ -1090,7 +1093,7 @@ const level = { // spawn.streamBoss(1600, -500) // spawn.cellBossCulture(1600, -500) // spawn.cellBossCulture(1600, -500) - // spawn.bomberBoss(1600, -500) + spawn.orbitalBoss(1600, -500) // spawn.beamer(1200, -500) // spawn.shield(mob[mob.length - 1], 1800, -120, 1); @@ -1420,7 +1423,7 @@ const level = { powerUps.spawn(2050, -150, "heal", false); //starting gun // powerUps.spawn(2050, -150, "field", false); //starting gun if (localSettings.levelsClearedLastGame < 6) { - if (!simulation.isCheating) { + if (!simulation.isCheating && !m.isShipMode) { spawn.wireFoot(); spawn.wireFootLeft(); spawn.wireKnee(); @@ -1896,7 +1899,7 @@ const level = { spawn.randomMob(3600, 1725, 0.9); spawn.randomMob(4100, 1225, 0.9); spawn.randomMob(2825, 400, 0.9); - if (simulation.difficulty > 3) spawn.randomLevelBoss(6000, 2300, ["spiderBoss", "launcherBoss", "laserTargetingBoss", "streamBoss", "historyBoss"]); + if (simulation.difficulty > 3) spawn.randomLevelBoss(6000, 2300, ["spiderBoss", "launcherBoss", "laserTargetingBoss", "streamBoss", "historyBoss", "orbitalBoss"]); powerUps.addRerollToLevel() //needs to run after mobs are spawned if (tech.isDuplicateBoss && Math.random() < 2 * tech.duplicationChance()) spawn.randomLevelBoss(7725, 2275); }, @@ -4123,7 +4126,7 @@ const level = { World.add(engine.world, cons[cons.length - 1]); if (simulation.difficulty > 4) spawn.nodeGroup(8000, 630, "spawns", 8, 20, 105); } else { - spawn.randomLevelBoss(8000, 630, ["shooterBoss", "launcherBoss", "laserTargetingBoss", "spiderBoss", "laserBoss", "bomberBoss"]); + spawn.randomLevelBoss(8000, 630, ["shooterBoss", "launcherBoss", "laserTargetingBoss", "spiderBoss", "laserBoss", "bomberBoss", "orbitalBoss"]); let me = mob[mob.length - 1]; me.onDeath = function() { this.removeCons(); //remove constraint diff --git a/js/player.js b/js/player.js index f953fac..c4f1ca6 100644 --- a/js/player.js +++ b/js/player.js @@ -40,17 +40,6 @@ const m = { }); Matter.Body.setMass(player, m.mass); World.add(engine.world, [player]); - - m.holdConstraint = Constraint.create({ - //holding body constraint - pointA: { - x: 0, - y: 0 - }, - bodyB: jumpSensor, //setting constraint to jump sensor because it has to be on something until the player picks up things - stiffness: 0.4 - }); - World.add(engine.world, m.holdConstraint); }, cycle: 600, //starts at 600 cycles instead of 0 to prevent bugs with m.history lastKillCycle: 0, @@ -484,15 +473,15 @@ const m = { harmReduction() { let dmg = 1 dmg *= m.fieldHarmReduction - if (tech.isBlockHarm && m.isHolding) dmg *= 0.25 + if (tech.isBlockHarm && m.isHolding) dmg *= 0.2 if (tech.squirrelFx !== 1) dmg *= 1 + (tech.squirrelFx - 1) / 5 //cause more damage if (tech.isSpeedHarm) dmg *= 1 - Math.min(player.speed * 0.0185, 0.55) if (tech.isSlowFPS) dmg *= 0.8 if (tech.isPiezo) dmg *= 0.85 - if (tech.isHarmReduce && m.fieldUpgrades[m.fieldMode].name === "negative mass field" && m.isFieldActive) dmg *= 0.6 - if (tech.isBotArmor) dmg *= 0.96 ** tech.totalBots() + if (tech.isHarmReduce && m.fieldUpgrades[m.fieldMode].name === "negative mass field" && m.isFieldActive) dmg *= 0.5 + if (tech.isBotArmor) dmg *= 0.94 ** tech.totalBots() if (tech.isHarmArmor && m.lastHarmCycle + 600 > m.cycle) dmg *= 0.33; - if (tech.isNoFireDefense && m.cycle > m.fireCDcycle + 120) dmg *= 0.6 + if (tech.isNoFireDefense && m.cycle > m.fireCDcycle + 120) dmg *= 0.34 if (tech.energyRegen === 0) dmg *= 0.34 if (tech.isTurret && m.crouch) dmg *= 0.5; if (tech.isFireMoveLock && input.fire) dmg *= 0.4; @@ -2575,8 +2564,12 @@ const m = { }, ], isShipMode: false, - shipMode() { + shipMode(thrust = 0.03, drag = 0.99, torque = 1.15, rotationDrag = 0.92) { // m.shipMode() //thrust = 0.03, drag = 0.99, torque = 1.15, rotationDrag = 0.92 if (!m.isShipMode) { + //if wires remove them + for (let i = 0; i < mob.length; i++) { + if (!mob[i].freeOfWires) mob[i].freeOfWires = true + } m.isShipMode = true simulation.isCheating = true const points = [ @@ -2611,7 +2604,8 @@ const m = { // Matter.Body.setDensity(player, 0.01); //extra dense //normal is 0.001 //makes effective life much larger m.defaultMass = 30 Matter.Body.setMass(player, m.defaultMass); - player.friction = 0.07 + player.friction = 0.05 + player.restitution = 0.2 // player.frictionStatic = 0.1 // Matter.Body.setInertia(player, Infinity); //disable rotation @@ -2620,14 +2614,40 @@ const m = { // console.log(player.parts[0]) // Matter.Body.setVertices(player.parts[0], Matter.Vertices.create(points, player.parts[0])) // console.log(player.parts[0].vertices) + m.spin = 0 + // m.groundControl = () => {} //disable entering ground + m.onGround = false + playerOnGroundCheck = () => {} + m.airControl = () => { //tank controls + player.force.y -= player.mass * simulation.g; //undo gravity + Matter.Body.setVelocity(player, { + x: drag * player.velocity.x, + y: drag * player.velocity.y + }); + if (input.up) { //forward thrust + player.force.x += thrust * Math.cos(m.angle) * tech.squirrelJump + player.force.y += thrust * Math.sin(m.angle) * tech.squirrelJump + } else if (input.down) { + player.force.x -= 0.6 * thrust * Math.cos(m.angle) + player.force.y -= 0.6 * thrust * Math.sin(m.angle) + } + //rotation + Matter.Body.setAngularVelocity(player, player.angularVelocity * rotationDrag) + if (input.right) { + player.torque += torque + } else if (input.left) { + player.torque -= torque + } + m.angle += m.spin + m.angle = player.angle + } level.playerExitCheck = () => { if ( player.position.x > level.exit.x && player.position.x < level.exit.x + 100 && player.position.y > level.exit.y - 150 && - player.position.y < level.exit.y - 40 && - player.speed < 4 + player.position.y < level.exit.y - 40 ) { level.nextLevel() } @@ -2694,6 +2714,34 @@ const m = { ctx.translate(player.position.x, player.position.y); ctx.rotate(player.angle); + //thrust + if (input.up) { + var grd2 = ctx.createLinearGradient(0, 0, -150, 0); + // grd2.addColorStop(0, 'rgba(255, 255, 155, 0.8)'); + // grd2.addColorStop(1, 'rgba(255, 200, 0, 0.1)'); + grd2.addColorStop(0, 'rgba(150, 200, 255, 0.7)'); + grd2.addColorStop(1, 'rgba(150, 200, 255, 0)'); + ctx.fillStyle = grd2; + ctx.beginPath(); + ctx.moveTo(-18, -25); + //10 * (Math.random() - 0.5), 10 * (Math.random() - 0.5) + ctx.lineTo(-18, 25); + ctx.lineTo(-50 - 100 * Math.random(), 0); + ctx.fill(); + } else if (input.down) { + var grd2 = ctx.createLinearGradient(0, 0, 80, 0); + grd2.addColorStop(0, 'rgba(150, 200, 255, 0.7)'); + grd2.addColorStop(1, 'rgba(150, 200, 255, 0)'); + ctx.fillStyle = grd2; + ctx.beginPath(); + ctx.moveTo(20, -16); + //10 * (Math.random() - 0.5), 10 * (Math.random() - 0.5) + ctx.lineTo(20, 16); + ctx.lineTo(35 + 43 * Math.random(), 0); + ctx.fill(); + } + + //body ctx.beginPath(); ctx.arc(0, 0, 30, 0, 2 * Math.PI); let grd = ctx.createLinearGradient(-30, 0, 30, 0); @@ -2705,50 +2753,10 @@ const m = { ctx.strokeStyle = "#333"; ctx.lineWidth = 2; ctx.stroke(); + ctx.restore(); } - m.spin = 0 - // m.groundControl = () => {} //disable entering ground - m.onGround = false - playerOnGroundCheck = () => {} - m.airControl = () => { //tank controls - player.force.y -= player.mass * simulation.g; //undo gravity - const thrust = 0.03 * tech.squirrelJump //Math.max(0.1 / (0.01 + player.speed), 0.03) * tech.squirrelJump - // console.log(player.speed, thrust) - if (input.up) { //thrust - player.force.x += thrust * Math.cos(m.angle) - player.force.y += thrust * Math.sin(m.angle) - - const friction = 0.99 - Matter.Body.setVelocity(player, { - x: friction * player.velocity.x, - y: friction * player.velocity.y - }); - } else if (input.down) { - player.force.x -= 0.7 * thrust * Math.cos(m.angle) - player.force.y -= 0.7 * thrust * Math.sin(m.angle) - - const friction = 0.96 - Matter.Body.setVelocity(player, { - x: friction * player.velocity.x, - y: friction * player.velocity.y - }); - } - const spinChange = 1.1 - if (input.right) { - player.torque += spinChange - // m.spin += spinChange - } else if (input.left) { - // m.spin -= spinChange - player.torque -= spinChange - } - Matter.Body.setAngularVelocity(player, player.angularVelocity * 0.9) - // m.spin *= 0.88 //spin friction - m.angle += m.spin // - m.angle = player.angle - } //fix collisions - collisionChecks = (event) => { const pairs = event.pairs; for (let i = 0, j = pairs.length; i != j; i++) { diff --git a/js/powerup.js b/js/powerup.js index 040680e..0115435 100644 --- a/js/powerup.js +++ b/js/powerup.js @@ -155,7 +155,7 @@ const powerUps = { } } if (tech.healGiveMaxEnergy) { - tech.healMaxEnergyBonus += 0.04 + tech.healMaxEnergyBonus += 0.05 m.setMaxEnergy(); } }, diff --git a/js/simulation.js b/js/simulation.js index 4295a5d..cc60030 100644 --- a/js/simulation.js +++ b/js/simulation.js @@ -362,7 +362,26 @@ const simulation = { b.activeGun = b.inventory[b.inventoryGun]; simulation.updateGunHUD(); simulation.boldActiveGunHUD(); - // m.drop(); + if (tech.isBotSwap) { + //get total count + const countPermanent = tech.dynamoBotCount + tech.laserBotCount + tech.nailBotCount + tech.foamBotCount + tech.boomBotCount + tech.orbitBotCount + //remove all bots + tech.dynamoBotCount = 0 + tech.laserBotCount = 0 + tech.nailBotCount = 0 + tech.foamBotCount = 0 + tech.boomBotCount = 0 + tech.orbitBotCount = 0 + for (let i = 0; i < bullet.length; i++) { + if (bullet[i].botType && bullet[i].endCycle === Infinity) bullet[i].endCycle = 0 //don't remove temp bots + } + //set to a new type + options = [() => { tech.laserBotCount = countPermanent }, () => { tech.nailBotCount = countPermanent }, () => { tech.foamBotCount = countPermanent }, () => { tech.boomBotCount = countPermanent }, () => { tech.orbitBotCount = countPermanent }, () => { tech.dynamoBotCount = countPermanent }, ] + options[tech.botSwapCycleIndex]() + tech.botSwapCycleIndex++ + if (tech.botSwapCycleIndex > options.length - 1) tech.botSwapCycleIndex = 0 + b.respawnBots() + } }, zoom: null, zoomScale: 1000, @@ -492,7 +511,11 @@ const simulation = { document.getElementById("splash").style.display = "none"; //hides the element that spawned the function document.getElementById("dmg").style.display = "inline"; document.getElementById("health-bg").style.display = "inline"; - m.spawn(); //spawns the player + if (!m.isShipMode) { + m.spawn(); //spawns the player + } else { + World.add(engine.world, [player]) + } level.levels = level.playableLevels.slice(0) //copy array, not by just by assignment if (simulation.isCommunityMaps) { diff --git a/js/spawn.js b/js/spawn.js index be151aa..185f152 100644 --- a/js/spawn.js +++ b/js/spawn.js @@ -68,6 +68,7 @@ const spawn = { } else { if (Math.random() < 0.07) { this[pick](x, y, 90 + Math.random() * 40); //one extra large mob + spawn.spawnOrbitals(mob[mob.length - 1], radius + 50 + 200 * Math.random(), 1) } else if (Math.random() < 0.35) { this.blockGroup(x, y) //hidden grouping blocks } else { @@ -81,8 +82,8 @@ const spawn = { } } }, - randomLevelBoss(x, y, options = ["historyBoss", "shooterBoss", "cellBossCulture", "bomberBoss", "spiderBoss", "launcherBoss", "laserTargetingBoss", "powerUpBoss", "snakeBoss", "streamBoss"]) { - // other bosses: suckerBoss, laserBoss, tetherBoss, //all need a particular level to work so they are not included + randomLevelBoss(x, y, options = ["orbitalBoss", "historyBoss", "shooterBoss", "cellBossCulture", "bomberBoss", "spiderBoss", "launcherBoss", "laserTargetingBoss", "powerUpBoss", "snakeBoss", "streamBoss"]) { + // other bosses: suckerBoss, laserBoss, tetherBoss, //these need a particular level to work so they are not included in the random pool spawn[options[Math.floor(Math.random() * options.length)]](x, y) }, //mob templates ********************************************************************************************* @@ -159,7 +160,7 @@ const spawn = { const pushUp = Vector.add(velocity, { x: 0, y: -0.5 }) Matter.Body.setVelocity(body[i], Vector.add(body[i].velocity, pushUp)); } - //push away mobs + //damage all mobs for (let i = 0, len = mob.length; i < len; ++i) { if (mob[i] !== this) { mob[i].damage(Infinity, true); @@ -522,9 +523,9 @@ const spawn = { } }, cellBoss(x, y, radius = 20, cellID) { - mobs.spawn(x + Math.random(), y + Math.random(), 20, radius * (1 + 1.2 * Math.random()), "rgba(0,100,105,0)"); + mobs.spawn(x + Math.random(), y + Math.random(), 20, radius * (1 + 1.2 * Math.random()), "rgba(0,100,105,0.4)"); let me = mob[mob.length - 1]; - me.stroke = "#099" + me.stroke = "transparent" me.isBoss = true; me.isCell = true; me.cellID = cellID @@ -1198,7 +1199,7 @@ const spawn = { }, historyBoss(x, y, radius = 30) { if (tech.dynamoBotCount > 0) { - spawn.randomLevelBoss(x, y, ["cellBossCulture", "bomberBoss", "powerUpBoss"]) + spawn.randomLevelBoss(x, y, ["cellBossCulture", "bomberBoss", "powerUpBoss", "orbitalBoss"]) return } mobs.spawn(x, y, 0, radius, "transparent"); @@ -1370,6 +1371,7 @@ const spawn = { } Matter.Body.setDensity(me, 0.023); //extra dense //normal is 0.001 //makes effective life much larger spawn.shield(me, x, y, 1); + spawn.spawnOrbitals(me, radius + 100 + 100 * Math.random()) me.onHit = function() { //run this function on hitting player // this.explode(); @@ -1517,6 +1519,7 @@ const spawn = { me.count = 0; me.frictionAir = 0.03; // me.torque -= me.inertia * 0.002 + spawn.spawnOrbitals(me, radius + 50 + 200 * Math.random()) Matter.Body.setDensity(me, 0.05); //extra dense //normal is 0.001 //makes effective life much larger // spawn.shield(me, x, y, 1); //not working, not sure why me.onDeath = function() { @@ -1875,8 +1878,8 @@ const spawn = { } ctx.lineTo(vertices[0].x, vertices[0].y); ctx.lineWidth = 1; - ctx.strokeStyle = `rgba(0,0,0,${this.alpha * this.alpha})`; - ctx.stroke(); + ctx.fillStyle = `rgba(255,255,255,${this.alpha * this.alpha})`; + ctx.fill(); } else if (this.canTouchPlayer) { this.canTouchPlayer = false; this.collisionFilter.mask = cat.bullet; //can't touch player or walls @@ -1929,14 +1932,14 @@ const spawn = { // }, bomberBoss(x, y, radius = 88) { //boss that drops bombs from above and holds a set distance from player - mobs.spawn(x, y, 3, radius, "transparent"); + mobs.spawn(x, y, 3, radius, "rgba(255,0,200,0.5)"); let me = mob[mob.length - 1]; me.isBoss = true; - Matter.Body.setDensity(me, 0.0014 + 0.0003 * Math.sqrt(simulation.difficulty)); //extra dense //normal is 0.001 //makes effective life much larger + Matter.Body.setDensity(me, 0.004 + 0.00035 * Math.sqrt(simulation.difficulty)); //extra dense //normal is 0.001 //makes effective life much larger - me.stroke = "rgba(255,0,200)"; //used for drawGhost + me.stroke = "transparent"; //used for drawGhost me.seeAtDistance2 = 1500000; - me.fireFreq = Math.floor(120 * simulation.CDScale); + me.fireFreq = Math.floor(100 * simulation.CDScale); me.searchTarget = map[Math.floor(Math.random() * (map.length - 1))].position; //required for search me.hoverElevation = 460 + (Math.random() - 0.5) * 200; //squared me.hoverXOff = (Math.random() - 0.5) * 100; @@ -1949,6 +1952,7 @@ const spawn = { // Matter.Body.setDensity(me, 0.0015); //extra dense //normal is 0.001 me.collisionFilter.mask = cat.player | cat.bullet spawn.shield(me, x, y, 1); + spawn.spawnOrbitals(me, radius + 150 + 250 * Math.random(), 1) me.onDeath = function() { powerUps.spawnBossPowerUp(this.position.x, this.position.y) }; @@ -2062,34 +2066,32 @@ const spawn = { this.explode(this.mass * 120); }; me.onDeath = function() { - if (simulation.difficulty > 4) { - 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); - const mag = 8 - const v1 = Vector.rotate({ - x: 1, - y: 1 - }, 2 * Math.PI * Math.random()) - const v2 = Vector.rotate({ - x: 1, - y: 1 - }, 2 * Math.PI * Math.random()) - const v3 = Vector.normalise(Vector.add(v1, v2)) //last vector is opposite the sum of the other two to look a bit like momentum is conserved + 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); + const mag = 8 + const v1 = Vector.rotate({ + x: 1, + y: 1 + }, 2 * Math.PI * Math.random()) + const v2 = Vector.rotate({ + x: 1, + y: 1 + }, 2 * Math.PI * Math.random()) + const v3 = Vector.normalise(Vector.add(v1, v2)) //last vector is opposite the sum of the other two to look a bit like momentum is conserved - Matter.Body.setVelocity(mob[mob.length - 1], { - x: mag * v1.x, - y: mag * v1.y - }); - Matter.Body.setVelocity(mob[mob.length - 2], { - x: mag * v2.x, - y: mag * v2.y - }); - Matter.Body.setVelocity(mob[mob.length - 3], { - x: -mag * v3.x, - y: -mag * v3.y - }); - } + Matter.Body.setVelocity(mob[mob.length - 1], { + x: mag * v1.x, + y: mag * v1.y + }); + Matter.Body.setVelocity(mob[mob.length - 2], { + x: mag * v2.x, + y: mag * v2.y + }); + Matter.Body.setVelocity(mob[mob.length - 3], { + x: -mag * v3.x, + y: -mag * v3.y + }); } Matter.Body.setDensity(me, 0.00005); //normal is 0.001 me.timeLeft = 140 + Math.floor(Math.random() * 30); @@ -2283,6 +2285,8 @@ const spawn = { me.memory = 420; me.repulsionRange = 1200000; //squared spawn.shield(me, x, y, 1); + spawn.spawnOrbitals(me, radius + 50 + 200 * Math.random()) + Matter.Body.setDensity(me, 0.004 + 0.0005 * Math.sqrt(simulation.difficulty)); //extra dense //normal is 0.001 //makes effective life much larger me.onDeath = function() { powerUps.spawnBossPowerUp(this.position.x, this.position.y) @@ -2326,6 +2330,8 @@ const spawn = { me.memory = 240; me.repulsionRange = 1200000; //squared spawn.shield(me, x, y, 1); + spawn.spawnOrbitals(me, radius + 50 + 200 * Math.random()) + Matter.Body.setDensity(me, 0.025); //extra dense //normal is 0.001 //makes effective life much larger me.onDeath = function() { powerUps.spawnBossPowerUp(this.position.x, this.position.y) @@ -2564,6 +2570,8 @@ const spawn = { me.memory = 20; Matter.Body.setDensity(me, 0.001 + 0.0005 * Math.sqrt(simulation.difficulty)); //extra dense //normal is 0.001 //makes effective life much larger spawn.shield(me, x, y, 1); + spawn.spawnOrbitals(me, radius + 50 + 200 * Math.random()) + me.onDeath = function() { powerUps.spawnBossPowerUp(this.position.x, this.position.y) this.removeCons(); //remove constraint @@ -2659,8 +2667,83 @@ const spawn = { this.checkStatus(); }; }, - //fan made mobs ***************************************************************************************** - //******************************************************************************************************* + spawnOrbitals(who, radius, chance = Math.min(0.25 + simulation.difficulty * 0.005)) { + if (Math.random() < chance) { + // simulation.difficulty = 50 + const len = Math.floor(Math.min(15, 3 + Math.sqrt(simulation.difficulty))) // simulation.difficulty = 40 on hard mode level 10 + const speed = (0.007 + 0.003 * Math.random() + 0.004 * Math.sqrt(simulation.difficulty)) * ((Math.random() < 0.5) ? 1 : -1) + for (let i = 0; i < len; i++) spawn.orbital(who, radius, i / len * 2 * Math.PI, speed) + } + }, + orbital(who, radius, phase, speed) { + // for (let i = 0, len = 7; i < len; i++) spawn.orbital(me, radius + 250, 2 * Math.PI / len * i) + mobs.spawn(0, 0, 8, 12, "rgb(255,0,150)"); + let me = mob[mob.length - 1]; + me.stroke = "transparent"; + // Matter.Body.setDensity(me, 0.00004); //normal is 0.001 + me.leaveBody = false; + me.dropPowerUp = false; + me.showHealthBar = false; + me.isShielded = true + me.collisionFilter.category = cat.mobBullet; + me.collisionFilter.mask = cat.bullet; //cat.player | cat.map | cat.body + me.do = function() { + //if host is gone + if (!who || !who.alive) { + this.death(); + return + } + //set orbit + const time = simulation.cycle * speed + phase + const orbit = { + x: Math.cos(time), + y: Math.sin(time) + } + Matter.Body.setPosition(this, Vector.add(who.position, Vector.mult(orbit, radius))) //bullets move with player + //damage player + if (Matter.Query.collides(this, [player]).length > 0) { + m.damage(0.1 * simulation.dmgScale); + this.death(); + } + + }; + }, + orbitalBoss(x, y, radius = 88) { + const nodes = Math.floor(3 + 1 * Math.sqrt(simulation.difficulty)) + mobs.spawn(x, y, nodes, radius, "rgb(255,0,150)"); + let me = mob[mob.length - 1]; + me.isBoss = true; + Matter.Body.setDensity(me, 0.004 + 0.00035 * Math.sqrt(simulation.difficulty)); //extra dense //normal is 0.001 //makes effective life much larger + + me.stroke = "transparent"; //used for drawGhost + me.seeAtDistance2 = 2000000; + me.accelMag = Math.floor(10 * (Math.random() + 4.5)) * 0.00001 * simulation.accelScale; + me.frictionAir = 0.005; + me.accelMag = 0.00017 * simulation.accelScale; + me.memory = Infinity; + me.collisionFilter.mask = cat.player | cat.bullet + spawn.shield(me, x, y, 1); + + let speed = (0.006 + 0.002 * Math.sqrt(simulation.difficulty)) * ((Math.random() < 0.5) ? 1 : -1) + let range = radius + 125 + 250 * Math.random() + nodes * 5 + for (let i = 0; i < nodes; i++) spawn.orbital(me, range, i / nodes * 2 * Math.PI, speed) + const orbitalIndexes = [] //find indexes for all the current nodes + for (let i = 0; i < nodes; i++) orbitalIndexes.push(mob.length - 1 - i) + // add orbitals for each orbital + range = 70 + Math.max(0, 140 * Math.random() - nodes * 3) + speed = speed * (1.5 + 2 * Math.random()) + for (let j = 0; j < nodes; j++) { + for (let i = 0, len = nodes - 1; i < len; i++) spawn.orbital(mob[orbitalIndexes[j]], range, i / len * 2 * Math.PI, speed) + } + me.onDeath = function() { + powerUps.spawnBossPowerUp(this.position.x, this.position.y) + }; + me.do = function() { + this.seePlayerCheckByDistance(); + this.checkStatus(); + this.attraction(); + }; + }, //complex constrained mob templates********************************************************************** //******************************************************************************************************* allowShields: true, diff --git a/js/tech.js b/js/tech.js index 1121144..c1d4404 100644 --- a/js/tech.js +++ b/js/tech.js @@ -1,5151 +1,5179 @@ -const tech = { - totalCount: null, - setupAllTech() { - for (let i = 0, len = tech.tech.length; i < len; i++) { - tech.tech[i].remove(); - tech.tech[i].isLost = false - tech.tech[i].count = 0 - } - lore.techCount = 0; - tech.removeJunkTechFromPool(); - tech.removeLoreTechFromPool(); - tech.addLoreTechToPool(); - tech.armorFromPowerUps = 0; - tech.totalCount = 0; - simulation.updateTechHUD(); - }, - removeTech(index) { - tech.tech[index].remove(); - tech.tech[index].count = 0; - simulation.updateTechHUD(); - }, - // 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(num = 1) { - for (let i = 0; i < num; i++) { - // find an index that doesn't have dups first - let index = null - for (let i = 0; i < tech.junk.length; i++) { - if (tech.junk[i].numberInPool === 0) { - index = i - break - } + const tech = { + totalCount: null, + setupAllTech() { + for (let i = 0, len = tech.tech.length; i < len; i++) { + tech.tech[i].remove(); + tech.tech[i].isLost = false + tech.tech[i].count = 0 } - if (index === null) index = Math.floor(Math.random() * tech.junk.length) //or just pick a random junk tech to add - - tech.junk[index].numberInPool++ - tech.tech.push(Object.assign({}, tech.junk[index])) // push a "clone" of the tech.junk into the pool - if (tech.junk[index].numberInPool > 1) tech.tech[tech.tech.length - 1].name += `(${tech.junk[index].numberInPool})` //give it a unique name so it can be found - } - }, - removeJunkTechFromPool() { - for (let i = tech.tech.length - 1; i > 0; i--) { - if (tech.tech[i].isJunk && tech.tech[i].count === 0) tech.tech.splice(i, 1) - } - }, - 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()) 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) - } - } 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.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 + lore.techCount = 0; + tech.removeJunkTechFromPool(); + tech.removeLoreTechFromPool(); + tech.addLoreTechToPool(); + tech.armorFromPowerUps = 0; + tech.totalCount = 0; simulation.updateTechHUD(); - } - }, - setTechoNonRefundable(name) { - for (let i = 0; i < tech.tech.length; i++) { - if (tech.tech.name === name) { - tech.tech[i].isNonRefundable = true; - return - } - } - }, - haveGunCheck(name) { - if ( - !build.isExperimentSelection && - b.inventory.length > 2 && - name !== b.guns[b.activeGun].name && - Math.random() > 2 / (b.inventory.length + tech.isGunCycle * 3) //lower chance of tech specific to a gun if you have lots of guns - ) { - return false - } - - for (i = 0, len = b.inventory.length; i < len; i++) { - if (b.guns[b.inventory[i]].name === name) return true - } - return false - }, - damageFromTech() { - let dmg = m.fieldDamage - if (tech.isTechDamage) dmg *= 2 - 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.4 - if (tech.isEnergyNoAmmo) dmg *= 1.5 - if (tech.isDamageForGuns) dmg *= 1 + 0.15 * b.inventory.length - if (tech.isLowHealthDmg) dmg *= 1 + 0.6 * Math.max(0, 1 - m.health) - if (tech.isHarmDamage && m.lastHarmCycle + 600 > m.cycle) dmg *= 3; - if (tech.isEnergyLoss) dmg *= 1.5; - if (tech.isAcidDmg && m.health > 1) dmg *= 1.4; - if (tech.restDamage > 1 && player.speed < 1) dmg *= tech.restDamage - if (tech.isEnergyDamage) dmg *= 1 + m.energy / 9; - if (tech.isDamageFromBulletCount) dmg *= 1 + bullet.length * 0.0038 - if (tech.isRerollDamage) dmg *= 1 + 0.035 * powerUps.research.count - if (tech.isOneGun && b.inventory.length < 2) dmg *= 1.25 - if (tech.isNoFireDamage && m.cycle > m.fireCDcycle + 120) dmg *= 1.66 - if (tech.isSpeedDamage) dmg *= 1 + Math.min(0.4, player.speed * 0.013) - if (tech.isBotDamage) dmg *= 1 + 0.04 * tech.totalBots() - return dmg * tech.slowFire * tech.aimDamage - }, - duplicationChance() { - return (tech.isBayesian ? 0.2 : 0) + tech.cancelCount * 0.045 + tech.duplicateChance + m.duplicateChance - }, - maxDuplicationEvent() { - if (tech.is100Duplicate && tech.duplicationChance() > 0.99) { - tech.is100Duplicate = false - const range = 1000 - const bossOptions = ["historyBoss", "cellBossCulture", "bomberBoss", "powerUpBoss", "suckerBoss"] - spawn.randomLevelBoss(m.pos.x + range, m.pos.y, bossOptions); - spawn.randomLevelBoss(m.pos.x, m.pos.y + range, bossOptions); - spawn.randomLevelBoss(m.pos.x - range, m.pos.y, bossOptions); - spawn.randomLevelBoss(m.pos.x, m.pos.y - range, bossOptions); - } - }, - totalBots() { - return tech.dynamoBotCount + tech.foamBotCount + tech.nailBotCount + tech.laserBotCount + tech.boomBotCount + tech.orbitBotCount + tech.plasmaBotCount + tech.missileBotCount - }, - tech: [{ - name: "integrated armament", - description: `increase damage by 25%
your inventory can only hold 1 gun`, - maxCount: 1, - count: 0, - allowed() { - return b.inventory.length < 2 //&& !tech.haveGunCheck("CPT gun") - }, - requires: "no more than 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, - allowed() { - return b.inventory.length > 1 && !tech.isEnergyHealth - }, - requires: "at least 2 guns", - effect() { - tech.isEntanglement = true - setTimeout(function() { - simulation.boldActiveGunHUD(); - }, 1000); - - }, - remove() { - tech.isEntanglement = false; - } + removeTech(index) { + tech.tech[index].remove(); + tech.tech[index].count = 0; + simulation.updateTechHUD(); }, - { - name: "arsenal", - description: "increase damage by 15%
for each gun in your inventory", - maxCount: 1, - count: 0, - allowed() { - return b.inventory.length > 1 - }, - requires: "at least 2 guns", - effect() { - tech.isDamageForGuns = true; - }, - remove() { - tech.isDamageForGuns = false; - } - }, - { - name: "active cooling", - description: "15% decreased delay after firing
for each gun in your inventory", - maxCount: 1, - count: 0, - 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 6 guns, but you can't switch guns
guns cycle automatically with each new level", - maxCount: 1, - count: 0, - isNonRefundable: true, - allowed() { - return (tech.isDamageForGuns || tech.isFireRateForGuns) && (b.inventory.length + 5) < b.guns.length - }, - requires: "arsenal or cyclic rate boost", - effect() { - tech.isGunCycle = true; - for (let i = 0; i < 6; i++) powerUps.spawn(m.pos.x + 10 * Math.random(), m.pos.y + 10 * Math.random(), "gun"); - }, - remove() { - tech.isGunCycle = false; - } - }, - { - name: "specialist", - description: "for every gun in your inventory spawn a
heal, research, field, ammo, or tech", - maxCount: 1, //random power up - count: 0, - isNonRefundable: true, - isCustomHide: true, - allowed() { - return tech.isGunCycle - }, - requires: "generalist", - 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: "logistics", - description: "ammo power ups give 200% ammo
but ammo is only added to your current gun", - maxCount: 1, - count: 0, - allowed() { - return !tech.isEnergyNoAmmo - }, - requires: "not exciton-lattice", - effect() { - tech.isAmmoForGun = true; - }, - remove() { - tech.isAmmoForGun = false; - } - }, - { - name: "supply chain", - description: "double your current ammo for all guns", - maxCount: 9, - count: 0, - isNonRefundable: true, - allowed() { - return tech.isAmmoForGun - }, - requires: "logistics", - 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(); - }, - remove() {} - }, - { - name: "catabolism", - description: "when you fire while out of ammo
gain 3 ammo, but lose 5 health", - maxCount: 1, - count: 0, - allowed() { - return !tech.isEnergyHealth && !tech.isEnergyNoAmmo - }, - requires: "not mass-energy equivalence
not exciton-lattice", - effect: () => { - tech.isAmmoFromHealth = true; - }, - remove() { - tech.isAmmoFromHealth = false; - } - }, - { - name: "perpetual ammo", - description: "find 2 ammo at the start of each level", - maxCount: 1, - count: 0, - allowed() { - return !tech.isPerpetualReroll && !tech.isPerpetualHeal && !tech.isPerpetualReroll && !tech.isPerpetualStun && !tech.isEnergyNoAmmo - }, - requires: "only 1 perpetual effect, not exciton lattice", - effect() { - tech.isPerpetualAmmo = true - }, - remove() { - tech.isPerpetualAmmo = false - } - }, - { - name: "desublimated ammunition", - description: "use 50% less ammo when crouching", - maxCount: 1, - count: 0, - allowed() { - return true - }, - requires: "", - effect() { - tech.isCrouchAmmo = true - }, - remove() { - tech.isCrouchAmmo = false; - } - }, - { - name: "gun turret", - description: "reduce harm by 50% when crouching", - maxCount: 1, - count: 0, - allowed() { - return tech.isCrouchAmmo && !tech.isEnergyHealth - }, - requires: "desublimated ammunition
not mass-energy equivalence", - effect() { - tech.isTurret = true - }, - remove() { - tech.isTurret = false; - } - }, - { - name: "inertial frame", - description: "66% decreased delay after firing
you can only fire when at rest", - maxCount: 1, - count: 0, - allowed() { - return true - }, - requires: "", - effect: () => { - tech.isFireNotMove = true; - b.setFireCD(); - b.setFireMethod(); - }, - remove() { - if (tech.isFireNotMove) { - tech.isFireNotMove = false - b.setFireCD(); - b.setFireMethod(); - } - } - }, - { - name: "dead reckoning", - description: "increase damage by 33% when at rest", - maxCount: 9, - count: 0, - allowed() { - return tech.isFireNotMove - }, - requires: "inertial frame", - effect: () => { - tech.restDamage += 0.33 - }, - remove() { - tech.restDamage = 1; - } - }, - { - name: "Higgs mechanism", - description: "while firing your position is locked
and harm is reduced by 60%", - maxCount: 1, - count: 0, - allowed() { - return !tech.isEnergyHealth - }, - requires: "not mass energy", - effect: () => { - tech.isFireMoveLock = true; - b.setFireMethod(); - }, - remove() { - if (tech.isFireMoveLock) { - tech.isFireMoveLock = false - b.setFireMethod(); - } - } - }, - { - name: "squirrel-cage rotor", - description: "move and jump about 30% faster
but you take 5% more harm", - maxCount: 9, - count: 0, - 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 50%", - maxCount: 1, - count: 0, - allowed() { - return m.Fx > 0.016 && !tech.isEnergyHealth - }, - requires: "speed increase, not mass-energy equivalence", - effect() { - tech.isSpeedHarm = true - }, - remove() { - tech.isSpeedHarm = false - } - }, - { - name: "Newton's 2nd law", - description: "moving at high speeds increases damage
by up to 33%", - maxCount: 1, - count: 0, - allowed() { - return m.Fx > 0.016 - }, - requires: "speed increase", - effect() { - tech.isSpeedDamage = true - }, - remove() { - tech.isSpeedDamage = false - } - }, - // { - // name: "Galilean group", - // description: "reduce harm by 50% when at rest", - // maxCount: 1, - // count: 0, - // allowed() { - // return tech.isFireNotMove || tech.isFireMoveLock - // }, - // requires: "inertial frame or Higgs manism", - // effect() { - // tech.isRestHarm = true - // }, - // remove() { - // tech.isRestHarm = false; - // } + // 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" // }, - { - name: "kinetic bombardment", - description: "increase damage by up to 33%
at a distance of 40 steps from the target", - maxCount: 1, - count: 0, - allowed() { - return true - }, - requires: "", - effect() { - tech.isFarAwayDmg = true; //used in mob.damage() - }, - remove() { - tech.isFarAwayDmg = false; - } - }, - { - name: "electrostatic discharge", - description: "increase damage by 20%
20% increased delay after firing", - maxCount: 1, - count: 0, - allowed() { - return true - }, - effect() { - tech.slowFire = 1.2 - b.setFireCD(); - }, - remove() { - tech.slowFire = 1; - b.setFireCD(); - } - }, - { - name: "auto-loading heuristics", - description: "30% decreased delay after firing", - maxCount: 9, - count: 0, - allowed() { - return true - }, - requires: "", - effect() { - tech.fireRate *= 0.7 - b.setFireCD(); - }, - remove() { - tech.fireRate = 1; - b.setFireCD(); - } - }, - { - name: "iridium-192", - description: "explosions release gamma radiation
100% more damage, but over 4 seconds", - maxCount: 1, - count: 0, - allowed() { - return tech.explosiveRadius === 1 && !tech.isSmallExplosion && (tech.haveGunCheck("missiles") || tech.isIncendiary || (tech.haveGunCheck("grenades") && !tech.isNeutronBomb) || tech.haveGunCheck("vacuum bomb") || tech.isPulseLaser || tech.isMissileField || tech.boomBotCount > 1) - }, - requires: "an explosive damage source, not ammonium nitrate or nitroglycerin", - effect: () => { - tech.isExplodeRadio = true; - }, - remove() { - tech.isExplodeRadio = false; - } - }, - { - name: "ammonium nitrate", - description: "increase explosive damage by 20%
increase explosive radius by 20%", - maxCount: 9, - count: 0, - allowed() { - return !tech.isExplodeRadio && (tech.haveGunCheck("missiles") || tech.isIncendiary || (tech.haveGunCheck("grenades") && !tech.isNeutronBomb) || tech.haveGunCheck("vacuum bomb") || tech.isPulseLaser || tech.isMissileField || tech.boomBotCount > 1) - }, - requires: "an explosive damage source, not iridium-192", - effect: () => { - tech.explosiveRadius += 0.2; - }, - remove() { - tech.explosiveRadius = 1; - } - }, - { - name: "nitroglycerin", - description: "increase explosive damage by 60%
decrease explosive radius by 20%", - maxCount: 1, - count: 0, - allowed() { - return !tech.isExplodeRadio && (tech.haveGunCheck("missiles") || tech.isIncendiary || (tech.haveGunCheck("grenades") && !tech.isNeutronBomb) || tech.haveGunCheck("vacuum bomb") || tech.isPulseLaser || tech.isMissileField || tech.boomBotCount > 1) - }, - 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 400% more harm from explosions", - maxCount: 1, - count: 0, - allowed() { - return tech.haveGunCheck("missiles") || tech.isIncendiary || (tech.haveGunCheck("grenades") && !tech.isNeutronBomb) || tech.haveGunCheck("vacuum bomb") || tech.isPulseLaser || tech.isMissileField - }, - requires: "an explosive damage source", - effect: () => { - tech.isExplosionHarm = true; - }, - remove() { - tech.isExplosionHarm = false; - } - }, - { - name: "electric reactive armor", - // description: "explosions do no harm
while your energy is above 98%", - description: "harm from explosions is passively reduced
by 7% for every 10 stored energy", - maxCount: 1, - count: 0, - allowed() { - return tech.haveGunCheck("missiles") || tech.isIncendiary || (tech.haveGunCheck("grenades") && !tech.isNeutronBomb) || tech.haveGunCheck("vacuum bomb") || tech.isMissileField || tech.isExplodeMob || tech.isPulseLaser - }, - requires: "an explosive damage source", - effect: () => { - tech.isImmuneExplosion = true; - }, - remove() { - tech.isImmuneExplosion = false; - } - }, - { - name: "thermal runaway", - description: "mobs explode when they die
be careful", - maxCount: 1, - count: 0, - allowed() { - return (tech.haveGunCheck("missiles") || tech.isIncendiary || (tech.haveGunCheck("grenades") && !tech.isNeutronBomb) || tech.haveGunCheck("vacuum bomb") || tech.isPulseLaser || tech.isMissileField || tech.boomBotCount > 1) && !tech.sporesOnDeath && !tech.nailsDeathMob && !tech.isBotSpawner - }, - requires: "an explosive damage source, no other mob death tech", - effect: () => { - tech.isExplodeMob = true; - }, - remove() { - tech.isExplodeMob = false; - } - }, - { - name: "zoospore vector", - description: "mobs produce spores when they die
9% chance", - maxCount: 9, - count: 0, - allowed() { - return !tech.nailsDeathMob && !tech.isExplodeMob && !tech.isBotSpawner - }, - requires: "no other mob death tech", - effect() { - tech.sporesOnDeath += 0.09; - for (let i = 0; i < 8; i++) { - b.spore(m.pos) - } - }, - remove() { - tech.sporesOnDeath = 0; - } - }, - { - name: "impact shear", - description: "mobs release a nail when they die
nails target nearby mobs", - maxCount: 9, - count: 0, - allowed() { - return !tech.sporesOnDeath && !tech.isExplodeMob && !tech.isBotSpawner - }, - requires: "no other mob death tech", - effect: () => { - tech.nailsDeathMob++ - }, - remove() { - tech.nailsDeathMob = 0; - } - }, - { - name: "reaction inhibitor", - description: "mobs spawn with 11% less health", - maxCount: 3, - count: 0, - allowed() { - return tech.nailsDeathMob || tech.sporesOnDeath || tech.isExplodeMob || tech.isBotSpawner - }, - 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 40%
after not using your gun or field for 2 seconds", - maxCount: 1, - count: 0, - allowed() { - return (tech.totalBots() > 1 || tech.haveGunCheck("drones") || tech.haveGunCheck("mine") || tech.haveGunCheck("spores") || m.fieldUpgrades[m.fieldMode].name === "nano-scale manufacturing") && !tech.isEnergyHealth - }, - requires: "drones, spores, mines, or bots", - effect() { - tech.isNoFireDefense = true - }, - remove() { - tech.isNoFireDefense = false - } - }, - { - name: "anticorrelation", - description: "increase damage by 66%
after not using your gun or field for 2 seconds", - maxCount: 1, - count: 0, - allowed() { - return tech.isNoFireDefense - }, - requires: "decorrelation", - effect() { - tech.isNoFireDamage = true - }, - remove() { - tech.isNoFireDamage = false - } - }, - { - name: "scrap bots", - description: "20% chance to build a bot after killing a mob
the bot lasts for about 20 seconds", - maxCount: 3, - count: 0, - allowed() { - return tech.totalBots() > 0 && !tech.sporesOnDeath && !tech.nailsDeathMob && !tech.isExplodeMob - }, - requires: "a bot and no other mob death tech", - effect() { - tech.isBotSpawner += 0.20; - }, - remove() { - tech.isBotSpawner = 0; - } - }, - { - name: "nail-bot", - description: "a bot fires nails at mobs in line of sight", - maxCount: 9, - count: 0, - allowed() { - return true - }, - requires: "", - effect() { - tech.nailBotCount++; - b.nailBot(); - }, - remove() { - tech.nailBotCount -= this.count; - } - }, - { - name: "nail-bot upgrade", - description: "500% increased fire rate
applies to all current and future nail-bots", - maxCount: 1, - count: 0, - allowed() { - return tech.nailBotCount > 1 - }, - requires: "2 or more nail bots", - effect() { - tech.isNailBotUpgrade = true - for (let i = 0; i < bullet.length; i++) { - if (bullet[i].botType === 'nail') bullet[i].isUpgraded = true - } - }, - remove() { - tech.isNailBotUpgrade = false - for (let i = 0; i < bullet.length; i++) { - if (bullet[i].botType === 'nail') bullet[i].isUpgraded = false - } - } - }, - { - name: "foam-bot", - description: "a bot fires foam at nearby mobs", - maxCount: 9, - count: 0, - allowed() { - return true - }, - requires: "", - effect() { - tech.foamBotCount++; - b.foamBot(); - }, - remove() { - tech.foamBotCount -= this.count; - } - }, - { - name: "foam-bot upgrade", - description: "200% increased foam size and fire rate
applies to all current and future foam-bots", - maxCount: 1, - count: 0, - allowed() { - return tech.foamBotCount > 1 - }, - requires: "2 or more foam bots", - effect() { - tech.isFoamBotUpgrade = true - for (let i = 0; i < bullet.length; i++) { - if (bullet[i].botType === 'foam') bullet[i].isUpgraded = true - } - }, - remove() { - tech.isFoamBotUpgrade = false - for (let i = 0; i < bullet.length; i++) { - if (bullet[i].botType === 'foam') bullet[i].isUpgraded = false - } - } - }, - { - name: "boom-bot", - description: "a bot defends the space around you
ignites an explosion after hitting a mob", - maxCount: 9, - count: 0, - allowed() { - return true - }, - requires: "", - effect() { - tech.boomBotCount++; - b.boomBot(); - }, - remove() { - tech.boomBotCount -= this.count; - } - }, - { - name: "boom-bot upgrade", - description: "250% increased explosion damage and size
applies to all current and future boom-bots", - maxCount: 1, - count: 0, - allowed() { - return tech.boomBotCount > 1 - }, - requires: "2 or more boom bots", - effect() { - tech.isBoomBotUpgrade = true - for (let i = 0; i < bullet.length; i++) { - if (bullet[i].botType === 'boom') bullet[i].isUpgraded = true - } - }, - remove() { - tech.isBoomBotUpgrade = false - for (let i = 0; i < bullet.length; i++) { - if (bullet[i].botType === 'boom') bullet[i].isUpgraded = false - } - } - }, - { - name: "laser-bot", - description: "a bot uses energy to emit a laser beam
that targets nearby mobs", - maxCount: 9, - count: 0, - allowed() { - return m.maxEnergy > 0.5 - }, - requires: "maximum energy above 50%", - effect() { - tech.laserBotCount++; - b.laserBot(); - }, - remove() { - tech.laserBotCount -= this.count; - } - }, - { - name: "laser-bot upgrade", - description: "350% increased laser damage
applies to all current and future laser-bots", - maxCount: 1, - count: 0, - allowed() { - return tech.laserBotCount > 1 - }, - requires: "2 or more laser bots", - effect() { - tech.isLaserBotUpgrade = true - for (let i = 0; i < bullet.length; i++) { - if (bullet[i].botType === 'laser') bullet[i].isUpgraded = true - } - }, - remove() { - tech.isLaserBotUpgrade = false - for (let i = 0; i < bullet.length; i++) { - if (bullet[i].botType === 'laser') bullet[i].isUpgraded = false - } - } - }, - { - name: "orbital-bot", - description: "a bot is locked in orbit around you
stuns and damages mobs on contact", - maxCount: 9, - count: 0, - allowed() { - return true - }, - requires: "", - effect() { - b.orbitBot(); - tech.orbitBotCount++; - }, - remove() { - tech.orbitBotCount -= this.count; - } - }, - { - name: "orbital-bot upgrade", - description: "increase damage by 150% and radius by 30%
applies to all current and future orbit-bots", - maxCount: 1, - count: 0, - allowed() { - return tech.orbitBotCount > 1 - }, - requires: "2 or more orbital bots", - effect() { - tech.isOrbitBotUpgrade = true - const range = 190 + 60 * 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) - } - } - - }, - remove() { - tech.isOrbitBotUpgrade = false - const range = 190 + 60 * 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) - } - } - } - }, - { - name: "dynamo-bot", - description: "a bot damages mobs while it traces your path
regen 4 energy per second when it's near", - maxCount: 9, - count: 0, - allowed() { - return true - }, - requires: "", - effect() { - tech.dynamoBotCount++; - b.dynamoBot(); - }, - remove() { - tech.dynamoBotCount -= this.count; - } - }, - { - name: "dynamo-bot upgrade", - description: "dynamo-bots regen 12 energy per second
applies to all current and future dynamo-bots", - maxCount: 1, - count: 0, - allowed() { - return tech.dynamoBotCount > 1 - }, - requires: "2 or more dynamo bots", - effect() { - tech.isDynamoBotUpgrade = true - for (let i = 0; i < bullet.length; i++) { - if (bullet[i].botType === 'dynamo') bullet[i].isUpgraded = true - } - }, - remove() { - tech.isDynamoBotUpgrade = false - for (let i = 0; i < bullet.length; i++) { - if (bullet[i].botType === 'dynamo') bullet[i].isUpgraded = false - } - } - }, - { - name: "bot fabrication", - description: "anytime you collect 5 research
use them to build a random bot", - maxCount: 1, - count: 0, - allowed() { - return powerUps.research.count > 5 || build.isExperimentSelection - }, - requires: "at least 6 research", - effect() { - tech.isRerollBots = true; - powerUps.research.changeRerolls(0) - simulation.makeTextLog(`m.research = 0`) - }, - remove() { - tech.isRerollBots = false; - } - }, - { - name: "perimeter defense", - description: "reduce harm by 4%
for each of your permanent bots", - maxCount: 1, - count: 0, - allowed() { - return tech.totalBots() > 5 && !tech.isEnergyHealth - }, - requires: "5 or more bots", - effect() { - tech.isBotArmor = true - }, - remove() { - tech.isBotArmor = false - } - }, { - name: "network effect", - description: "increase damage by 4%
for each of your permanent bots", - maxCount: 1, - count: 0, - allowed() { - return tech.totalBots() > 6 - }, - requires: "6 or more bots", - effect() { - tech.isBotDamage = true - }, - remove() { - tech.isBotDamage = false - } - }, - { - name: "bot replication", - description: "duplicate your permanent bots
remove all of your guns", - maxCount: 1, - count: 0, - isNonRefundable: true, - isCustomHide: true, - allowed() { - return tech.totalBots() > 3 - }, - requires: "at least 3 bots", - effect() { - 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.plasmaBotCount; i++) { - b.plasmaBot(); - } - tech.plasmaBotCount *= 2 - for (let i = 0; i < tech.missileBotCount; i++) { - b.missileBot(); - } - tech.missileBotCount *= 2 - }, - remove() {} - }, - { - name: "mass driver", - description: "increase block collision damage by 100%
charge throws more quickly for less energy", - maxCount: 1, - count: 0, - allowed() { - return m.fieldUpgrades[m.fieldMode].name !== "wormhole" - }, - requires: "not wormhole", - effect() { - tech.throwChargeRate = 2 - }, - remove() { - tech.throwChargeRate = 1 - } - }, - { - name: "restitution", - description: "mobs killed by collisions with blocks
spawn a heal, ammo, or research", - maxCount: 1, - count: 0, - allowed() { - return tech.throwChargeRate > 1 - }, - requires: "mass driver", - effect() { - tech.isBlockPowerUps = true - }, - remove() { - tech.isBlockPowerUps = false - } - }, - { - name: "inelastic collision", - description: "while you are holding a block
reduce harm by 75%", - maxCount: 1, - count: 0, - allowed() { - return tech.throwChargeRate > 1 - }, - requires: "mass driver", - effect() { - tech.isBlockHarm = true - }, - remove() { - tech.isBlockHarm = false - } - }, - { - name: "perpetual stun", - description: "stun all mobs for up to 12 seconds
at the start of each level", - maxCount: 1, - count: 0, - allowed() { - return !tech.isPerpetualReroll && !tech.isPerpetualHeal && !tech.isPerpetualAmmo - }, - requires: "only 1 perpetual effect", - effect() { - tech.isPerpetualStun = true - }, - remove() { - tech.isPerpetualStun = false - } - }, - { - name: "Pauli exclusion", - description: `after receiving harm from a collision become
immune to harm for an extra 0.75 seconds`, - maxCount: 9, - count: 0, - allowed() { - return true - }, - requires: "", - effect() { - tech.collisionImmuneCycles += 45; - m.immuneCycle = m.cycle + tech.collisionImmuneCycles; //player is immune to collision damage for 30 cycles - }, - remove() { - tech.collisionImmuneCycles = 25; - } - }, - { - name: "complex spin-statistics", - description: `become immune to harm for +1 second
once every 7 seconds`, - maxCount: 3, - count: 0, - allowed() { - return true //tech.collisionImmuneCycles > 30 - }, - requires: "", - effect() { - tech.cyclicImmunity += 60; - }, - remove() { - tech.cyclicImmunity = 0; - } - }, - { - name: "ablative drones", - description: "rebuild your broken parts as drones
chance to occur after receiving harm", - maxCount: 1, - count: 0, - 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", - description: "for 10 seconds after receiving harm
reduce harm by 66%", - maxCount: 1, - count: 0, - 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, - allowed() { - return m.harmReduction() < 1 - }, - requires: "some harm reduction", - effect() { - tech.isHarmDamage = true; - }, - remove() { - tech.isHarmDamage = false; - } - }, - { - name: "liquid cooling", - description: `freeze all mobs for 5 seconds
after receiving harm`, - maxCount: 1, - count: 0, - allowed() { - return tech.isSlowFPS - }, - requires: "clock gating", - effect() { - tech.isHarmFreeze = true; - }, - remove() { - tech.isHarmFreeze = false; - } - }, - { - name: "osmoprotectant", - description: `collisions with stunned or frozen mobs
cause you no harm`, - maxCount: 1, - count: 0, - allowed() { - return tech.isStunField || tech.isPulseStun || tech.oneSuperBall || tech.isHarmFreeze || tech.isIceField || tech.isIceCrystals || tech.isSporeFreeze || tech.isAoESlow || tech.isFreezeMobs || tech.isCloakStun || tech.orbitBotCount > 1 || tech.isWormholeDamage - }, - requires: "a freezing or stunning effect", - effect() { - tech.isFreezeHarmImmune = true; - }, - remove() { - tech.isFreezeHarmImmune = false; - } - }, - { - name: "clock gating", - description: `slow time by 50% after receiving harm
reduce harm by 20%`, - maxCount: 1, - count: 0, - allowed() { - return simulation.fpsCapDefault > 45 && !tech.isRailTimeSlow - }, - requires: "FPS above 45", - effect() { - tech.isSlowFPS = true; - }, - remove() { - tech.isSlowFPS = false; - } - }, - { - name: "CPT reversal", - description: "charge, parity, and time invert to undo harm
rewind (1.5—5) seconds for (66—220) energy", - maxCount: 1, - count: 0, - allowed() { //&& (m.fieldUpgrades[m.fieldMode].name !== "nano-scale manufacturing" || m.maxEnergy > 1) - return m.maxEnergy > 0.99 && m.fieldUpgrades[m.fieldMode].name !== "standing wave harmonics" && !tech.isEnergyHealth && !tech.isRewindGun - }, - requires: "not standing wave, mass-energy, piezo, max energy reduction, CPT gun", - effect() { - tech.isRewindAvoidDeath = true; - }, - remove() { - tech.isRewindAvoidDeath = false; - } - }, - { - name: "causality bots", - description: "when you rewind, build several bots
that protect you for about 9 seconds", - maxCount: 3, - count: 0, - allowed() { - return tech.isRewindAvoidDeath || tech.isRewindEnergy - }, - requires: "CPT", - effect() { - tech.isRewindBot++; - }, - remove() { - tech.isRewindBot = 0; - } - }, - { - name: "causality bombs", - description: "before you rewind drop several grenades", - maxCount: 1, - count: 0, - allowed() { - return tech.isRewindAvoidDeath - }, - requires: "CPT", - effect() { - tech.isRewindGrenade = true; - }, - remove() { - tech.isRewindGrenade = false; - } - }, - { - name: "piezoelectricity", - description: "colliding with mobs gives you 400 energy
reduce harm by 15%", - maxCount: 1, - count: 0, - allowed() { - return !tech.isEnergyHealth - }, - requires: "not mass-energy equivalence", - effect() { - tech.isPiezo = true; - m.energy += 4; - }, - remove() { - tech.isPiezo = false; - } - }, - { - name: "ground state", - description: "reduce harm by 60%
you no longer passively regenerate energy", - maxCount: 1, - count: 0, - allowed() { - return (tech.iceEnergy || tech.isWormholeEnergy || tech.isPiezo || tech.isRailEnergyGain) && tech.energyRegen !== 0.004 && !tech.isEnergyHealth - }, - requires: "piezoelectricity, Penrose, half-wave, or thermoelectric, but 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, - allowed() { - return !tech.isEnergyLoss && !tech.isPiezo && !tech.isRewindAvoidDeath && !tech.isRewindGun && !tech.isSpeedHarm && m.fieldUpgrades[m.fieldMode].name !== "negative mass field" && !tech.isHealLowHealth && !tech.isTechDamage - }, - requires: "not exothermic process, piezoelectricity, CPT, 1st law, negative mass , ...", - effect: () => { - m.health = 0 - // m.displayHealth(); - document.getElementById("health").style.display = "none" - document.getElementById("health-bg").style.display = "none" - document.getElementById("dmg").style.backgroundColor = "#0cf"; - tech.isEnergyHealth = true; - m.displayHealth(); - }, - remove() { - 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); - m.displayHealth(); - } - }, - { - name: "1st ionization energy", - description: "each heal power up you collect
increases your maximum energy by 4", - maxCount: 1, - count: 0, - 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: "electrolytes", - description: "increase damage by 1%
for every 9 stored energy", - maxCount: 1, - count: 0, - allowed() { - return m.maxEnergy > 1 || tech.isEnergyRecovery || tech.isPiezo || tech.energySiphon > 0 - }, - requires: "increased energy regen or max energy", - effect: () => { - tech.isEnergyDamage = true - }, - remove() { - tech.isEnergyDamage = false; - } - }, - { - name: "exciton-lattice", - description: `increase damage by 50%, but
ammo will no longer spawn`, - maxCount: 1, - count: 0, - allowed() { - return (tech.haveGunCheck("nail gun") && tech.isIceCrystals) || tech.haveGunCheck("laser") || m.fieldUpgrades[m.fieldMode].name === "plasma torch" || m.fieldUpgrades[m.fieldMode].name === "nano-scale manufacturing" || m.fieldUpgrades[m.fieldMode].name === "pilot wave" - }, - requires: "energy based damage", - 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, - allowed() { - return !tech.isEnergyHealth - }, - requires: "not mass-energy equivalence", - effect() { - tech.isEnergyLoss = true; - }, - remove() { - tech.isEnergyLoss = false; - } - }, - { - name: "heat engine", - description: `increase damage by 40%, but
reduce maximum energy by 50`, - maxCount: 1, - count: 0, - allowed() { - return tech.isEnergyLoss && m.maxEnergy < 1.1 && !tech.isSporeField && !tech.isRewindAvoidDeath - }, - requires: "exothermic process, not max energy increase, CPT, or spore nano-scale", - 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, - allowed() { - return tech.isEnergyLoss && m.maxEnergy < 1.1 - }, - requires: "exothermic process, not max energy increase", - effect() { - tech.isLowEnergyDamage = true; - }, - remove() { - tech.isLowEnergyDamage = false; - } - }, - { - name: "overcharge", - description: "increase your maximum energy by 50", - maxCount: 9, - count: 0, - allowed() { - return m.maxEnergy > 0.99 - }, - requires: "max energy >= 1", - effect() { - // m.maxEnergy += 0.5 - // m.energy += 0.5 - tech.bonusEnergy += 0.5 - m.setMaxEnergy() - }, - remove() { - tech.bonusEnergy = 0; - m.setMaxEnergy() - } - }, - { - name: "supercapacitor", - description: "energy above your max decays 60% slower", - maxCount: 1, - count: 0, - allowed() { - return tech.isEnergyRecovery || tech.isPiezo || tech.energySiphon > 0 || tech.isRailEnergyGain || tech.isWormholeEnergy || tech.iceEnergy > 0 - }, - requires: "a source of overfilled energy", - effect() { - tech.overfillDrain = 0.85 - }, - remove() { - tech.overfillDrain = 0.75 - } - }, - { - name: "energy conservation", - description: "6% of damage done recovered as energy", - maxCount: 9, - count: 0, - allowed() { - return tech.damageFromTech() > 1 - }, - requires: "some increased damage", - effect() { - tech.energySiphon += 0.06; - }, - remove() { - tech.energySiphon = 0; - } - }, - { - name: "waste energy recovery", - description: "if a mob has died in the last 5 seconds
regen 5% of max energy every second", - maxCount: 1, - count: 0, - allowed() { - return m.maxEnergy > 0.99 - }, - requires: "max energy >= 1", - effect() { - tech.isEnergyRecovery = true; - }, - remove() { - tech.isEnergyRecovery = false; - } - }, - { - name: "scrap recycling", - description: "if a mob has died in the last 5 seconds
regain 1% of max health every second", - maxCount: 1, - count: 0, - allowed() { - return !tech.isEnergyHealth - }, - requires: "not mass-energy equivalence", - effect() { - tech.isHealthRecovery = true; - }, - remove() { - tech.isHealthRecovery = false; - } - }, - { - name: "negative feedback", - description: "increase damage by 6%
for every 10 health below 100", - maxCount: 1, - count: 0, - allowed() { - return m.health < 0.5 || build.isExperimentSelection - }, - requires: "health below 60", - effect() { - tech.isLowHealthDmg = true; //used in mob.damage() - }, - remove() { - tech.isLowHealthDmg = false; - } - }, { - name: "antiscience", - description: "increase damage by 100%
lose 11 health when you pick up a tech", - maxCount: 1, - count: 0, - allowed() { - return (m.harmReduction() < 1 || tech.healthDrain || tech.isLowHealthDmg || tech.isHealthRecovery || tech.isHealLowHealth || tech.largerHeals > 1 || tech.isPerpetualHeal) && !tech.isEnergyHealth - }, - requires: "negative feedback or extra healing tech or harm reduction, not mass-energy", - effect() { - tech.isTechDamage = true; - }, - remove() { - tech.isTechDamage = false; - } - }, - { - name: "entropy exchange", - description: "heal for 1% of damage done", - maxCount: 9, - count: 0, - allowed() { - return !tech.isEnergyHealth && tech.damageFromTech() > 1 - }, - requires: "some increased damage, not mass-energy equivalence", - effect() { - tech.healthDrain += 0.01; - }, - remove() { - tech.healthDrain = 0; - } - }, - { - name: "fluoroantimonic acid", - description: "increase damage by 40%
when your health is above 100", - maxCount: 1, - count: 0, - allowed() { - return m.maxHealth > 1; - }, - requires: "health above 100", - effect() { - tech.isAcidDmg = true; - }, - remove() { - tech.isAcidDmg = false; - } - }, - { - name: "supersaturation", - description: "increase your maximum health by 50", - maxCount: 9, - count: 0, - allowed() { - return !tech.isEnergyHealth - }, - requires: "not mass-energy equivalence", - effect() { - tech.bonusHealth += 0.5 - m.addHealth(0.50) - m.setMaxHealth(); - }, - remove() { - tech.bonusHealth = 0 - m.setMaxHealth(); - - } - }, - { - name: "inductive coupling", - description: "for each unused power up at the end of a level
add 3 max health (up to 42 health per level)", - maxCount: 1, - count: 0, - allowed() { - return !tech.isEnergyHealth - }, - requires: "not mass-energy equivalence", - effect() { - tech.isArmorFromPowerUps = true; //tracked by tech.armorFromPowerUps - }, - remove() { - tech.isArmorFromPowerUps = false; - // tech.armorFromPowerUps = 0; //this is now reset in tech.setupAllTech(); - m.setMaxHealth(); - } - }, - { - name: "transceiver chip", - description: "unused power ups at the end of each level
are still activated (selections are random)", - maxCount: 1, - count: 0, - allowed() { - return tech.isArmorFromPowerUps - }, - requires: "inductive coupling", - effect() { - tech.isEndLevelPowerUp = true; - }, - remove() { - tech.isEndLevelPowerUp = false; - } - }, - { - name: "negentropy", - description: `at the start of each level
spawn a heal for every 50 missing health`, - maxCount: 1, - count: 0, - allowed() { - return m.maxHealth > 1 || tech.isArmorFromPowerUps - }, - requires: "increased max health", - effect() { - tech.isHealLowHealth = true; - }, - remove() { - tech.isHealLowHealth = false; - } - }, - { - name: "adiabatic healing", - description: "heal power ups are 100% more effective", - maxCount: 3, - count: 0, - allowed() { - return (m.health < 0.7 || build.isExperimentSelection) && !tech.isEnergyHealth - }, - requires: "not mass-energy equivalence", - effect() { - tech.largerHeals++; - }, - remove() { - tech.largerHeals = 1; - } - }, - { - name: "perpetual heals", - description: "find 2 heals at the start of each level", - maxCount: 1, - count: 0, - allowed() { - return !tech.isPerpetualReroll && !tech.isPerpetualAmmo && !tech.isPerpetualStun - }, - requires: "only 1 perpetual effect", - effect() { - tech.isPerpetualHeal = true - }, - remove() { - tech.isPerpetualHeal = false - } - }, - { - name: "anthropic principle", - nameInfo: "", - addNameInfo() { - setTimeout(function() { - powerUps.research.changeRerolls(0) - }, 1000); - }, - description: "once per level use 1 research to avoid dying
and spawn 6 heal power ups", - maxCount: 1, - count: 0, - 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: "quantum immortality", - description: "after dying, continue in an alternate reality
spawn 4 research", - maxCount: 1, - count: 0, - allowed() { - return powerUps.research.count > 1 || build.isExperimentSelection - }, - requires: "at least 2 research", - effect() { - tech.isImmortal = true; - for (let i = 0; i < 4; i++) powerUps.spawn(m.pos.x + Math.random() * 10, m.pos.y + Math.random() * 10, "research", false); - }, - remove() { - tech.isImmortal = false; - } - }, - { - name: "bubble fusion", - description: "after destroying a mob's shield
spawn 1-2 heals, ammo, or research", - maxCount: 1, - count: 0, - allowed() { - return true - }, - requires: "", - effect() { - tech.isShieldAmmo = true; - }, - remove() { - tech.isShieldAmmo = false; - } - }, - { - name: "stimulated emission", - description: "20% chance to duplicate spawned power ups
after a collision, eject 1 tech", - maxCount: 1, - count: 0, - allowed() { - return tech.duplicationChance() < 1 - }, - requires: "below 100% duplication chance", - effect: () => { - tech.isBayesian = true - simulation.draw.powerUp = simulation.draw.powerUpBonus //change power up draw - tech.maxDuplicationEvent() - }, - remove() { - tech.isBayesian = false - if (tech.duplicationChance() === 0) simulation.draw.powerUp = simulation.draw.powerUpNormal - } - }, - { - name: "replication", - description: "7% chance to duplicate spawned power ups
add 11 junk tech to the potential pool", - maxCount: 9, - count: 0, - allowed() { - return tech.duplicationChance() < 1 - }, - requires: "below 100% duplication chance", - effect() { - tech.duplicateChance += 0.075 - simulation.draw.powerUp = simulation.draw.powerUpBonus //change power up draw - tech.addJunkTechToPool(11) - tech.maxDuplicationEvent() - }, - remove() { - tech.duplicateChance = 0 - if (tech.duplicationChance() === 0) simulation.draw.powerUp = simulation.draw.powerUpNormal - } - }, - { - name: "futures exchange", - description: "clicking × to cancel a field, tech, or gun
adds 4.5% power up duplication chance", - maxCount: 1, - count: 0, - allowed() { - return tech.duplicationChance() < 1 && !tech.isDeterminism && (level.levelsCleared < 5 || Math.random() < 0.5) - }, - requires: "below 100% duplication chance, not determinism", - effect() { - tech.isCancelDuplication = true - tech.cancelCount = 0 - simulation.draw.powerUp = simulation.draw.powerUpBonus //change power up draw - }, - remove() { - tech.isCancelDuplication = false - // tech.cancelCount = 0 - if (tech.duplicationChance() === 0) simulation.draw.powerUp = simulation.draw.powerUpNormal - } - }, - { - name: "commodities exchange", - description: "clicking × to cancel a field, tech, or gun
spawns 8 heals, ammo, and research", - maxCount: 1, - count: 0, - allowed() { - return tech.isCancelDuplication - }, - requires: "futures exchange", - 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, - allowed() { - return tech.duplicationChance() > 0.15 - }, - requires: "some duplication chance", - effect() { - tech.isDupDamage = true; - }, - remove() { - tech.isDupDamage = false; - } - }, - { - name: "parthenogenesis", - description: "each level has a chance to spawn a level boss
equal to double your duplication chance", - maxCount: 1, - count: 0, - allowed() { - return tech.duplicationChance() > 0 - }, - requires: "some duplication chance", - effect() { - tech.isDuplicateBoss = true; - }, - remove() { - tech.isDuplicateBoss = false; - } - }, - { - name: "apomixis", - description: "after reaching 100% duplication chance
immediately spawn 4 level bosses", - maxCount: 1, - count: 0, - allowed() { - return tech.isDuplicateBoss - }, - requires: "parthenogenesis", - effect() { - tech.is100Duplicate = true; - }, - remove() { - tech.is100Duplicate = false; - } - }, - { - name: "exchange symmetry", - description: "convert 1 a random tech into 3 new guns
recursive tech lose all stacks", - maxCount: 1, - count: 0, - isNonRefundable: true, - isCustomHide: true, - allowed() { - return (tech.totalCount > 3) && !tech.isSuperDeterminism - }, - requires: "at least 1 tech, a chance to duplicate power ups", - 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.remove("${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, - isNonRefundable: true, - isCustomHide: true, - allowed() { - return (tech.totalCount > 3) && !tech.isSuperDeterminism && tech.duplicationChance() > 0 - }, - requires: "at least 1 tech, a chance to duplicate power ups", - 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.remove("${tech.tech[choose].name}")`) - for (let i = 0; i < tech.tech[choose].count; i++) powerUps.spawn(m.pos.x, m.pos.y, "tech"); - powerUps.spawn(m.pos.x, m.pos.y, "tech"); - 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: "strange attractor", - description: `use 2 research to spawn 1 tech
with double your duplication chance`, - maxCount: 1, - count: 0, - isNonRefundable: true, - isCustomHide: true, - allowed() { - return !tech.isSuperDeterminism && tech.duplicationChance() > 0 && powerUps.research.count > 1 - }, - requires: "at least 1 tech and 1 research, a chance to duplicate power ups", - effect: () => { - powerUps.research.changeRerolls(-2) - simulation.makeTextLog(`m.research -= 2 -
${powerUps.research.count}`) - const chanceStore = tech.duplicateChance - tech.duplicateChance = (tech.isBayesian ? 0.2 : 0) + tech.cancelCount * 0.045 + m.duplicateChance + tech.duplicateChance * 2 //increase duplication chance to simulate doubling all 3 sources of duplication chance - powerUps.spawn(m.pos.x, m.pos.y, "tech"); - tech.duplicateChance = chanceStore - }, - remove() {} - }, - { - name: "mine synthesis", - description: "drop a mine after picking up a power up", - maxCount: 1, - count: 0, - allowed() { - return tech.duplicationChance() > 0 - }, - requires: "some power up duplication", - effect() { - tech.isMineDrop = true; - if (tech.isMineDrop) b.mine(m.pos, { x: 0, y: 0 }, 0, tech.isMineAmmoBack) - }, - remove() { - tech.isMineDrop = false; - } - }, - { - name: "unified field theory", - description: `in the pause menu, change your field
by clicking on your field's box`, - maxCount: 1, - count: 0, - allowed() { - return (b.inventory.length > 1) || build.isExperimentSelection && !tech.isSuperDeterminism - }, - requires: "at least 2 guns, not superdeterminism", - effect() { - tech.isGunSwitchField = true; - }, - remove() { - tech.isGunSwitchField = false; - } - }, - { - name: "dark patterns", - description: "reduce combat difficulty by 1 level
add 16 junk tech to the potential pool", - maxCount: 1, - isNonRefundable: true, - isCustomHide: true, - count: 0, - allowed() { - return powerUps.research.count === 0 && level.onLevel < 6 - }, - requires: "no research, and in the first 5 levels", - effect() { - level.difficultyDecrease(simulation.difficultyMode) - simulation.makeTextLog(`simulation.difficultyMode--`) - tech.addJunkTechToPool(16) - // for (let i = 0; i < tech.junk.length; i++) tech.tech.push(tech.junk[i]) - }, - remove() {} - }, - { - name: "cardinality", - description: "tech, fields, and guns have 5 choices", - maxCount: 1, - count: 0, - allowed() { - return !tech.isDeterminism - }, - requires: "not determinism", - effect: () => { - tech.isExtraChoice = true; - }, - remove() { - tech.isExtraChoice = false; - } - }, - { - name: "determinism", - description: "spawn 5 tech
tech, fields, and guns have only 1 choice", - maxCount: 1, - count: 0, - isNonRefundable: true, - allowed() { - return !tech.isExtraChoice && !tech.isCancelDuplication && !tech.isCancelRerolls - }, - requires: "not cardinality, not futures or commodities exchanges", - effect: () => { - tech.isDeterminism = true; - //if you change the six also change it in Born rule - for (let i = 0; i < 5; i++) powerUps.spawn(m.pos.x, m.pos.y, "tech"); - }, - remove() { - tech.isDeterminism = false; - } - }, - { - name: "superdeterminism", - description: "spawn 7 tech
research, guns, and fields no longer spawn", - maxCount: 1, - count: 0, - isNonRefundable: true, - allowed() { - return tech.isDeterminism && !tech.manyWorlds && !tech.isGunSwitchField - }, - requires: "determinism, not unified field theory", - effect: () => { - tech.isSuperDeterminism = true; - for (let i = 0; i < 7; i++) { //if you change the six also change it in Born rule - powerUps.spawn(m.pos.x, m.pos.y, "tech"); - } - }, - remove() { - tech.isSuperDeterminism = false; - } - }, - { - name: "Ψ(t) collapse", - description: "66% decreased delay after firing
when you have no research in your inventory", - maxCount: 1, - count: 0, - allowed() { - return powerUps.research.count === 0 && !tech.manyWorlds - }, - requires: "no research", - effect() { - tech.isRerollHaste = true; - tech.researchHaste = 0.33; - b.setFireCD(); - }, - remove() { - tech.isRerollHaste = false; - tech.researchHaste = 1; - b.setFireCD(); - } - }, - { - name: "many-worlds", - description: "after choosing a field, tech, or gun
if you have no research spawn 2", - maxCount: 1, - count: 0, - allowed() { - return powerUps.research.count === 0 && !tech.isSuperDeterminism && !tech.isRerollHaste - }, - requires: "not superdeterminism or Ψ(t) collapse
no research", - effect: () => { - tech.manyWorlds = true; - }, - remove() { - tech.manyWorlds = false; - } - }, - { - name: "renormalization", - description: "using a research for any purpose
has a 37% chance to spawn a research", - maxCount: 1, - count: 0, - allowed() { - return (powerUps.research.count > 1 || build.isExperimentSelection) && !tech.isSuperDeterminism && !tech.isRerollHaste - }, - requires: "not superdeterminism or Ψ(t) collapse
at least 2 research", - effect() { - tech.renormalization = true; - }, - remove() { - tech.renormalization = false; - } - }, - { - name: "erase", - description: "researched or canceled tech won't reoccur
spawn 4 research", - maxCount: 1, - count: 0, - 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 < 4; i++) powerUps.spawn(m.pos.x, m.pos.y, "research", false); - }, - remove() { - tech.isBanish = false - powerUps.tech.banishLog = [] //reset banish log - } - }, - { - name: "perturbation theory", - description: "increase damage by 3.5%
for each research in your inventory", - maxCount: 1, - count: 0, - allowed() { - return powerUps.research.count > 4 || build.isExperimentSelection - }, - requires: "at least 5 research", - effect() { - tech.isRerollDamage = true; - }, - remove() { - tech.isRerollDamage = false; - } - }, - { - name: "Born rule", - description: "remove all current tech
spawn new tech to replace them", - maxCount: 1, - count: 0, - // isNonRefundable: true, - isCustomHide: true, - allowed() { - return (tech.totalCount > 6) - }, - requires: "more than 6 tech", - effect: () => { - //remove active bullets //to get rid of bots - for (let i = 0; i < bullet.length; ++i) Matter.World.remove(engine.world, bullet[i]); - bullet = []; - let count = 0 //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 -= 3 //remove the bonus tech - if (tech.isSuperDeterminism) count -= 2 //remove the bonus tech - - tech.setupAllTech(); // remove all tech - tech.addLoreTechToPool(); - for (let i = 0; i < count; i++) { // spawn new tech power ups - powerUps.spawn(m.pos.x, m.pos.y, "tech"); - } - //have state is checked in m.death() - }, - remove() {} - }, - { - name: "perpetual research", - description: "find 1 research at the start of each level", - maxCount: 1, - count: 0, - allowed() { - return !tech.isSuperDeterminism && !tech.isPerpetualHeal && !tech.isPerpetualAmmo && !tech.isPerpetualStun - }, - requires: "only 1 perpetual effect, not superdeterminism", - effect() { - tech.isPerpetualReroll = true - }, - remove() { - tech.isPerpetualReroll = false - } - }, - //************************************************** - //************************************************** gun - //************************************************** tech - //************************************************** - { - name: "CPT gun", - description: `adds the CPT gun to your inventory
it rewinds your health, velocity, and position`, - isGunTech: true, - maxCount: 1, - count: 0, - allowed() { - return (tech.totalBots() > 5 || m.fieldUpgrades[m.fieldMode].name === "nano-scale manufacturing" || m.fieldUpgrades[m.fieldMode].name === "plasma torch" || m.fieldUpgrades[m.fieldMode].name === "pilot wave") && !tech.isEnergyHealth && !tech.isRewindAvoidDeath //build.isExperimentSelection || - }, - requires: "bots > 5, plasma torch, nano-scale, 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: "incendiary ammunition", - description: "shotgun, super balls, and drones
are loaded with explosives", - isGunTech: true, - maxCount: 1, - count: 0, - allowed() { - return ((m.fieldUpgrades[m.fieldMode].name === "nano-scale manufacturing" && !(tech.isSporeField || tech.isMissileField || tech.isIceField)) || tech.haveGunCheck("drones") || tech.haveGunCheck("super balls") || tech.haveGunCheck("shotgun")) && !tech.isNailShot - }, - requires: "drones, super balls, shotgun", - effect() { - tech.isIncendiary = true - }, - remove() { - tech.isIncendiary = false; - } - }, - { - name: "fragmentation", - description: "some detonations and collisions eject nails
blocks, rail gun, grenades, missiles, shotgun slugs", - isGunTech: true, - maxCount: 9, - count: 0, - allowed() { - return (tech.haveGunCheck("grenades") && !tech.isNeutronBomb) || tech.haveGunCheck("missiles") || tech.haveGunCheck("rail gun") || (tech.haveGunCheck("shotgun") && tech.isSlugShot) || tech.throwChargeRate > 1 - }, - requires: "grenades, missiles, rail gun, shotgun slugs, or mass driver", - effect() { - tech.fragments++ - }, - remove() { - tech.fragments = 0 - } - }, - { - name: "superfluidity", - description: "freeze effects are applied to a small area", - isGunTech: true, - maxCount: 1, - count: 0, - allowed() { - return tech.isIceCrystals || tech.isSporeFreeze || tech.isIceField - }, - requires: "a freeze effect", - effect() { - tech.isAoESlow = true - }, - remove() { - tech.isAoESlow = false - } - }, - { - name: "radioactive contamination", - description: "after a mob or shield dies,
leftover radiation spreads to a nearby mob", - isGunTech: true, - maxCount: 1, - count: 0, - allowed() { - return tech.isNailRadiation || tech.isWormholeDamage || tech.isNeutronBomb || tech.isExplodeRadio - }, - requires: "radiation damage source", - effect() { - tech.isRadioactive = true - }, - remove() { - tech.isRadioactive = false - } - }, - { - name: "anti-shear topology", - description: "some bullets last 30% longer
drones, spores, missiles, foam, wave, neutron", - isGunTech: true, - maxCount: 3, - count: 0, - allowed() { - return m.fieldUpgrades[m.fieldMode].name === "nano-scale manufacturing" || tech.haveGunCheck("spores") || tech.haveGunCheck("drones") || tech.haveGunCheck("missiles") || tech.haveGunCheck("foam") || tech.haveGunCheck("wave beam") || tech.isNeutronBomb - }, - requires: "drones, spores, missiles, foam
wave beam, neutron bomb", - effect() { - tech.isBulletsLastLonger += 0.3 - }, - remove() { - tech.isBulletsLastLonger = 1; - } - }, - { - name: "microstates", - description: "increase damage by 4%
for every 10 active bullets", - isGunTech: true, - maxCount: 1, - count: 0, - allowed() { - return tech.isBulletsLastLonger > 1 - }, - requires: "Lorentzian topology", - effect() { - tech.isDamageFromBulletCount = true - }, - remove() { - tech.isDamageFromBulletCount = false - } - }, - { - name: "needle gun", - description: "nail gun fires 3 mob piercing needles
requires 3 times more ammo", - isGunTech: true, - maxCount: 1, - count: 0, - allowed() { - return tech.haveGunCheck("nail gun") && !tech.nailFireRate && !tech.isIceCrystals && !tech.isRivets && !tech.isNailRadiation - }, - requires: "nail gun, not ice crystal, rivets, 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(); + 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(num = 1) { + for (let i = 0; i < num; i++) { + // find an index that doesn't have dups first + let index = null + for (let i = 0; i < tech.junk.length; i++) { + if (tech.junk[i].numberInPool === 0) { + index = i 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 - } - } - } + if (index === null) index = Math.floor(Math.random() * tech.junk.length) //or just pick a random junk tech to add + + tech.junk[index].numberInPool++ + tech.tech.push(Object.assign({}, tech.junk[index])) // push a "clone" of the tech.junk into the pool + if (tech.junk[index].numberInPool > 1) tech.tech[tech.tech.length - 1].name += `(${tech.junk[index].numberInPool})` //give it a unique name so it can be found } }, - { - name: "ceramic needle", - description: `your needles pierce shields
directly damaging shielded mobs`, - isGunTech: true, - maxCount: 1, - count: 0, - allowed() { - return tech.isNeedles && !tech.isNailRadiation - }, - requires: "needle gun, not irradiated nails", - effect() { - tech.isNeedleShieldPierce = true - }, - remove() { - tech.isNeedleShieldPierce = false + removeJunkTechFromPool() { + for (let i = tech.tech.length - 1; i > 0; i--) { + if (tech.tech[i].isJunk && tech.tech[i].count === 0) tech.tech.splice(i, 1) } }, - { - name: "rivet gun", - description: "nail gun slowly fires a heavy rivet", - isGunTech: true, - maxCount: 1, - count: 0, - allowed() { - return tech.haveGunCheck("nail gun") && !tech.nailFireRate && !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 - } + 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()) options.push(i); } - }, - 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 - } - } + // 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) } - } - }, - { - name: "rivet diameter", - description: `your rivets are 20% larger
increases mass and physical damage`, - isGunTech: true, - maxCount: 9, - count: 0, - allowed() { - return tech.isRivets - }, - requires: "rivet gun", - effect() { - tech.rivetSize += 0.2 - }, - remove() { - tech.rivetSize = 1; - } - }, - { - name: "ice crystal nucleation", - description: "the nail gun uses energy to condense
unlimited freezing ice shards", - isGunTech: true, - maxCount: 1, - count: 0, - allowed() { - return tech.haveGunCheck("nail gun") && !tech.nailInstantFireRate && !tech.isRivets && !tech.isNeedles && !tech.isNailRadiation && !tech.isNailCrit - }, - requires: "nail gun, not powder-actuated, rivets, needles, irradiated, or fission", - 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(); + } 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.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 } } }, - { - name: "pneumatic actuator", - description: "nail gun takes 45% less time to ramp up
to it's shortest delay after firing", - isGunTech: true, - maxCount: 1, - count: 0, - allowed() { - return tech.haveGunCheck("nail gun") && !tech.isRivets && !tech.isNeedles - }, - requires: "nail gun, not rivets or needles", - effect() { - tech.nailFireRate = 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.nailFireRate) { - tech.nailFireRate = 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() - } - } + haveGunCheck(name) { + if ( + !build.isExperimentSelection && + b.inventory.length > 2 && + name !== b.guns[b.activeGun].name && + Math.random() > 2 / (b.inventory.length + tech.isGunCycle * 3) //lower chance of tech specific to a gun if you have lots of guns + ) { + return false } - }, - { - name: "powder-actuated", - description: "nail gun takes no time to ramp up
nails have a 30% faster muzzle speed", - isGunTech: true, - maxCount: 1, - count: 0, - allowed() { - return tech.haveGunCheck("nail gun") && tech.nailFireRate && !tech.isIceCrystals - }, - requires: "nail gun and pneumatic actuator", - 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: "supercritical fission", - description: "nails, needles, and rivets can explode
if they strike mobs near their center", - isGunTech: true, - maxCount: 1, - count: 0, - allowed() { - return (tech.isNailShot || tech.nailBotCount > 1 || tech.haveGunCheck("nail gun")) && !tech.isIceCrystals - }, - requires: "nails", - effect() { - tech.isNailCrit = true - }, - remove() { - tech.isNailCrit = false - } - }, - { - name: "irradiated nails", - description: "nails and rivets are radioactive
about 90% more damage over 2 seconds", - isGunTech: true, - maxCount: 1, - count: 0, - allowed() { - return (tech.isMineDrop + tech.nailBotCount + tech.fragments + tech.nailsDeathMob / 2 + ((tech.haveGunCheck("mine") && !tech.isLaserMine) + tech.isNailShot + (tech.haveGunCheck("nail gun") && !tech.isNeedleShieldPierce)) * 2 > 1) && !tech.isIceCrystals - }, - requires: "nails, rivets, nonceramic needles, not ice crystals", - effect() { - tech.isNailRadiation = true; - }, - remove() { - tech.isNailRadiation = false; - } - }, - { - name: "4s half-life", - description: "nails are made of plutonium-238
increase damage by 100% over 6 seconds", - isGunTech: true, - maxCount: 1, - count: 0, - allowed() { - return tech.isNailRadiation && !tech.isFastRadiation - }, - requires: "irradiated nails", - effect() { - tech.isSlowRadiation = true; - }, - remove() { - tech.isSlowRadiation = false; - } - }, - { - name: "1/2s half-life", - description: "nails are made of lithium-8
damage occurs after 1/2 a second", - isGunTech: true, - maxCount: 1, - count: 0, - allowed() { - return tech.isNailRadiation && !tech.isSlowRadiation - }, - requires: "irradiated nails", - effect() { - tech.isFastRadiation = true; - }, - remove() { - tech.isFastRadiation = false; - } - }, - { - name: "shotgun spin-statistics", - description: "immune to harm while firing the shotgun
ammo costs are doubled", - isGunTech: true, - maxCount: 1, - count: 0, - allowed() { - return tech.haveGunCheck("shotgun") - }, - requires: "shotgun", - effect() { - tech.isShotgunImmune = true; - //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); - break; - } - } - simulation.updateGunHUD(); - - 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 * 0.5 - break; - } - } - }, - remove() { - 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; - break; - } - } - } - }, - { - name: "nailshot", - description: "the shotgun fires a burst of nails", - isGunTech: true, - maxCount: 1, - count: 0, - allowed() { - return tech.haveGunCheck("shotgun") && !tech.isIncendiary && !tech.isSlugShot - }, - requires: "shotgun", - effect() { - tech.isNailShot = true; - }, - remove() { - tech.isNailShot = false; - } - }, - { - name: "shotgun slug", - description: "the shotgun fires 1 large bullet", - isGunTech: true, - maxCount: 1, - count: 0, - allowed() { - return tech.haveGunCheck("shotgun") && !tech.isNailShot - }, - requires: "shotgun", - effect() { - tech.isSlugShot = true; - }, - remove() { - tech.isSlugShot = false; - } - }, - { - name: "Newton's 3rd law", - description: "shotgun recoil is greatly increased
and has a 66% decreased delay after firing", - isGunTech: true, - maxCount: 1, - count: 0, - allowed() { - return tech.haveGunCheck("shotgun") - }, - requires: "shotgun", - effect() { - tech.isShotgunRecoil = true; - }, - remove() { - tech.isShotgunRecoil = false; - } - }, - { - name: "super duper", - description: "fire 1 additional super ball", - isGunTech: true, - maxCount: 9, - count: 0, - allowed() { - return tech.haveGunCheck("super balls") && !tech.oneSuperBall - }, - requires: "super balls, but not the tech super ball", - effect() { - tech.superBallNumber++ - }, - remove() { - tech.superBallNumber = 4; - } - }, - { - name: "super ball", - description: "fire just 1 large super ball
that stuns mobs for 3 second", - isGunTech: true, - maxCount: 1, - count: 0, - allowed() { - return tech.haveGunCheck("super balls") && tech.superBallNumber === 4 - }, - requires: "super balls, but not super duper", - effect() { - tech.oneSuperBall = true; - }, - remove() { - tech.oneSuperBall = false; - } - }, - { - name: "super sized", - description: `your super balls are 20% larger
increases mass and physical damage`, - isGunTech: true, - maxCount: 9, - count: 0, - allowed() { - return tech.haveGunCheck("super balls") - }, - requires: "super balls", - effect() { - tech.bulletSize += 0.15 - }, - remove() { - tech.bulletSize = 1; - } - }, - { - name: "wave packet", - description: "wave beam emits two oscillating particles
decrease wave damage by 20%", - isGunTech: true, - maxCount: 1, - count: 0, - allowed() { - return tech.haveGunCheck("wave beam") - }, - requires: "wave beam", - effect() { - tech.waveHelix = 2 - }, - remove() { - tech.waveHelix = 1 - } - }, - { - name: "phase velocity", - description: "the wave beam propagates faster in solids", - isGunTech: true, - maxCount: 1, - count: 0, - allowed() { - return tech.haveGunCheck("wave beam") && !tech.isWaveReflect - }, - requires: "wave beam", - effect() { - tech.waveSpeedMap = 3 //needs to be 3 to stop bound state require check - tech.waveSpeedBody = 1.9 - }, - remove() { - tech.waveSpeedMap = 0.08 - tech.waveSpeedBody = 0.25 - } - }, - { - name: "bound state", - description: "wave beam bullets last 5x longer
bullets are bound to a region around player", - isGunTech: true, - maxCount: 1, - count: 0, - allowed() { - return tech.haveGunCheck("wave beam") && tech.waveSpeedMap !== 3 - }, - requires: "wave beam", - effect() { - tech.isWaveReflect = true - }, - remove() { - tech.isWaveReflect = false - } - }, - { - name: "cruise missile", - description: "missiles travel 50% slower,
but have a 50% larger explosive payload", - isGunTech: true, - maxCount: 1, - count: 0, - allowed() { - return tech.haveGunCheck("missiles") || tech.isMissileField - }, - requires: "missiles", - effect() { - tech.missileSize = true - }, - remove() { - tech.missileSize = false - } - }, - { - name: "MIRV", - description: "launch +1 missile at a time
decrease size and fire rate by 10%", - isGunTech: true, - maxCount: 9, - count: 0, - allowed() { - return tech.haveGunCheck("missiles") - }, - requires: "missiles", - effect() { - tech.missileCount++; - }, - remove() { - tech.missileCount = 1; - } - }, - { - name: "missile-bot", - description: "a bot fires missiles at far away mobs", - isGunTech: true, - maxCount: 1, - count: 0, - allowed() { - return tech.haveGunCheck("missiles") - }, - requires: "missiles", - effect() { - tech.missileBotCount++; - b.missileBot(); - }, - remove() { - tech.missileBotCount = 0; - } - }, - { - name: "rocket-propelled grenade", - description: "grenades rapidly accelerate forward
map collisions trigger an explosion", - isGunTech: true, - maxCount: 1, - count: 0, - 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, - 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: "neutron bomb", - description: "grenades are irradiated with Cf-252
does damage, harm, and drains energy", - isGunTech: true, - maxCount: 1, - count: 0, - allowed() { - return tech.haveGunCheck("grenades") && !tech.fragments && !tech.isVacuumBomb - }, - requires: "grenades, not fragmentation", - effect() { - tech.isNeutronBomb = true; - b.setGrenadeMode() - }, - remove() { - tech.isNeutronBomb = false; - b.setGrenadeMode() - } - }, - { - name: "water shielding", - description: "increase neutron bomb's range by 20%
player is immune to its harmful effects", - isGunTech: true, - maxCount: 1, - count: 0, - allowed() { - return tech.isNeutronBomb - }, - requires: "neutron bomb", - effect() { - tech.isNeutronImmune = true - }, - remove() { - tech.isNeutronImmune = false - } - }, - { - name: "vacuum permittivity", - description: "increase neutron bomb's range by 20%
objects in range of the bomb are slowed", - isGunTech: true, - maxCount: 1, - count: 0, - allowed() { - return tech.isNeutronBomb - }, - requires: "neutron bomb", - effect() { - tech.isNeutronSlow = true - }, - remove() { - tech.isNeutronSlow = false - } - }, - { - name: "laser-mines", - description: "mines hover in place until mobs get in range
mines use energy to emit 3 unaimed lasers", - isGunTech: true, - maxCount: 1, - count: 0, - allowed() { - return (tech.haveGunCheck("mine") || tech.isMineDrop) && !tech.isMineSentry - }, - requires: "mines, not sentry", - effect() { - tech.isLaserMine = true; - }, - remove() { - tech.isLaserMine = false; - } - }, - { - name: "mine reclamation", - description: "retrieve ammo from all undetonated mines
and 20% of mines after detonation", - isGunTech: true, - maxCount: 1, - count: 0, - allowed() { - return tech.haveGunCheck("mine") && !tech.isMineSentry - }, - requires: "mine, not sentry", - effect() { - tech.isMineAmmoBack = true; - }, - remove() { - tech.isMineAmmoBack = false; - } - }, - { - name: "sentry", - description: "mines target mobs with nails over time
mines last about 12 seconds", - isGunTech: true, - maxCount: 1, - count: 0, - allowed() { - return (tech.haveGunCheck("mine") || tech.isMineDrop) && !tech.isMineAmmoBack && !tech.isLaserMine - }, - requires: "mines, not mine reclamation, laser-mines", - effect() { - tech.isMineSentry = true; - }, - remove() { - tech.isMineSentry = false; - } - }, - { - name: "mycelial fragmentation", - description: "sporangium release an extra spore
once a second during their growth phase", - isGunTech: true, - maxCount: 1, - count: 0, - allowed() { - return tech.haveGunCheck("spores") - }, - requires: "spores", - effect() { - tech.isSporeGrowth = true - }, - remove() { - tech.isSporeGrowth = false - } - }, - { - name: "tinsellated flagella", - description: "sporangium release 2 more spores
spores accelerate 50% faster", - isGunTech: true, - maxCount: 1, - count: 0, - allowed() { - return tech.haveGunCheck("spores") || tech.sporesOnDeath > 0 || tech.isSporeField - }, - requires: "spores", - effect() { - tech.isFastSpores = true - }, - remove() { - tech.isFastSpores = false - } - }, - { - name: "cryodesiccation", - description: "sporangium release 2 more spores
spores freeze mobs for 1 second", - //
spores do 1/3 damage - isGunTech: true, - maxCount: 1, - count: 0, - allowed() { - return tech.haveGunCheck("spores") || tech.sporesOnDeath > 0 || tech.isSporeField - }, - requires: "spores", - effect() { - tech.isSporeFreeze = true - }, - remove() { - tech.isSporeFreeze = false - } - }, - { - name: "diplochory", - description: "spores use the player for dispersal
until they locate a viable host", - isGunTech: true, - maxCount: 1, - count: 0, - allowed() { - return tech.haveGunCheck("spores") || tech.sporesOnDeath > 0 || tech.isSporeField - }, - requires: "spores", - 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, - allowed() { - return (tech.haveGunCheck("spores") || tech.sporesOnDeath > 0 || tech.isSporeField) && !tech.isEnergyHealth - }, - requires: "spores", - effect() { - tech.isMutualism = true - }, - remove() { - tech.isMutualism = false - } - }, - { - name: "brushless motor", - description: "drones accelerate 50% faster", - isGunTech: true, - maxCount: 1, - count: 0, - allowed() { - return tech.haveGunCheck("drones") || (m.fieldUpgrades[m.fieldMode].name === "nano-scale manufacturing" && !(tech.isSporeField || tech.isMissileField || tech.isIceField)) - }, - requires: "drones", - effect() { - tech.isFastDrones = true - }, - remove() { - tech.isFastDrones = false - } - }, - { - name: "harvester", - description: "after a drone picks up a power up,
it's larger, faster, and very durable", - isGunTech: true, - maxCount: 1, - count: 0, - allowed() { - return !tech.isArmorFromPowerUps && (tech.haveGunCheck("drones") || (m.fieldUpgrades[m.fieldMode].name === "nano-scale manufacturing" && !(tech.isSporeField || tech.isMissileField || tech.isIceField))) - }, - requires: "drones", - effect() { - tech.isDroneGrab = true - }, - remove() { - tech.isDroneGrab = 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, - allowed() { - return tech.haveGunCheck("foam") || tech.foamBotCount > 1 - }, - requires: "foam", - effect() { - tech.isFoamGrowOnDeath = true - }, - remove() { - tech.isFoamGrowOnDeath = false; - } - }, - { - name: "colloidal foam", - description: "foam bubbles dissipate 40% faster
increase foam damage per second by 300%", - isGunTech: true, - maxCount: 1, - count: 0, - allowed() { - return tech.haveGunCheck("foam") || tech.foamBotCount > 2 - }, - requires: "foam", - effect() { - tech.isFastFoam = true - }, - remove() { - tech.isFastFoam = false; - } - }, - { - name: "foam fractionation", - description: "foam gun bubbles are 100% larger
when you have below 300 ammo", - isGunTech: true, - maxCount: 1, - count: 0, - allowed() { - return tech.haveGunCheck("foam") - }, - requires: "foam", - effect() { - tech.isAmmoFoamSize = true - }, - remove() { - tech.isAmmoFoamSize = false; - } - }, - { - name: "quantum foam", - description: "foam gun fires 0.3 seconds into the future
increase foam gun damage by 153%", - isGunTech: true, - maxCount: 9, - count: 0, - allowed() { - return tech.haveGunCheck("foam") - }, - requires: "foam", - effect() { - tech.foamFutureFire++ - }, - remove() { - tech.foamFutureFire = 0; - } - }, - - // { - // name: "foam size", - // description: "increase foam damage by 200%
foam dissipates 50% faster", - // maxCount: 1, - // count: 0, - // allowed() { - // return tech.haveGunCheck("foam") || tech.foamBotCount > 2 - // }, - // requires: "foam", - // effect() { - // tech.isLargeFoam = true - // }, - // remove() { - // tech.isLargeFoam = false; - // } - // }, - // { - // name: "frame-dragging", - // description: "slow time while charging the rail gun
charging no longer drains energy", - // maxCount: 1, - // count: 0, - // allowed() { - // return simulation.fpsCapDefault > 45 && tech.haveGunCheck("rail gun") && !tech.isSlowFPS && !tech.isCapacitor - // }, - // requires: "rail gun and FPS above 45", - // effect() { - // tech.isRailTimeSlow = true; - // }, - // remove() { - // tech.isRailTimeSlow = false; - // simulation.fpsCap = simulation.fpsCapDefault - // simulation.fpsInterval = 1000 / simulation.fpsCap; - // } - // }, - { - name: "half-wave rectifier", - description: "charging the rail gun gives you energy
instead of draining it", - isGunTech: true, - maxCount: 1, - count: 0, - allowed() { - return tech.haveGunCheck("rail gun") - }, - requires: "rail gun", - effect() { - tech.isRailEnergyGain = true; - }, - remove() { - tech.isRailEnergyGain = false; - } - }, - { - name: "dielectric polarization", - description: "firing the rail gun damages nearby mobs", - isGunTech: true, - maxCount: 1, - count: 0, - allowed() { - return tech.haveGunCheck("rail gun") - }, - requires: "rail gun", - effect() { - tech.isRailAreaDamage = true; - }, - remove() { - tech.isRailAreaDamage = false; - } - }, - { - name: "capacitor bank", - description: "the rail gun no longer takes time to charge
rail gun rods are 66% less massive", - isGunTech: true, - maxCount: 1, - count: 0, - allowed() { - return tech.haveGunCheck("rail gun") - }, - requires: "rail gun", - effect() { - tech.isCapacitor = true; - }, - remove() { - tech.isCapacitor = false; - } - }, - { - name: "laser diodes", - description: "all lasers drain 37% less energy
effects laser-gun, laser-bot, and laser-mines", - isGunTech: true, - maxCount: 1, - count: 0, - allowed() { - return tech.haveGunCheck("laser") || tech.laserBotCount > 1 || tech.isLaserMine - }, - requires: "laser", - effect() { - tech.isLaserDiode = 0.63; //100%-37% - }, - remove() { - tech.isLaserDiode = 1; - } - }, - { - name: "relativistic momentum", - description: "all lasers push mobs away
effects laser-gun, laser-bot, and laser-mines", - isGunTech: true, - maxCount: 1, - count: 0, - allowed() { - return tech.haveGunCheck("laser") || tech.laserBotCount > 1 - }, - requires: "laser", - effect() { - tech.isLaserPush = true; - }, - remove() { - tech.isLaserPush = false; - } - }, - - { - name: "specular reflection", - description: "increase damage and energy drain by 50%
and +1 reflection for all lasers (gun, bot, mine)", - isGunTech: true, - maxCount: 9, - count: 0, - allowed() { - return (tech.haveGunCheck("laser") || tech.isLaserMine || tech.laserBotCount > 1) && !tech.isWideLaser && !tech.isPulseLaser && !tech.historyLaser - }, - requires: "laser, not wide beam", - effect() { - tech.laserReflections++; - tech.laserDamage += 0.08; //base is 0.12 - tech.laserFieldDrain += 0.0009 //base is 0.002 - }, - remove() { - tech.laserReflections = 2; - tech.laserDamage = 0.16; - tech.laserFieldDrain = 0.0018; - } - }, - { - name: "diffraction grating", - description: `your laser gains 2 diverging beams
decrease individual beam damage by 10%`, - isGunTech: true, - maxCount: 9, - count: 0, - allowed() { - return tech.haveGunCheck("laser") && !tech.isWideLaser && !tech.isPulseAim && !tech.historyLaser - }, - requires: "laser, not specular reflection", - 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", - description: "laser beam is wider and doesn't reflect
increase full beam damage by 175%", - isGunTech: true, - maxCount: 1, - count: 0, - allowed() { - return tech.haveGunCheck("laser") && tech.laserReflections < 3 && !tech.beamSplitter && !tech.isPulseLaser && !tech.historyLaser - }, - requires: "laser, not specular reflection, diffraction grating, slow light", - 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, - allowed() { - return tech.haveGunCheck("laser") && tech.isWideLaser - }, - requires: "laser, not specular reflection
not diffraction grating", - 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 propagation", - description: "laser beam is spread into your recent past
increase total beam damage by 300%", - isGunTech: true, - maxCount: 9, - count: 0, - allowed() { - return tech.haveGunCheck("laser") && tech.laserReflections < 3 && !tech.beamSplitter && !tech.isPulseLaser && !tech.isWideLaser - }, - requires: "laser, 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: "use 25% of your energy in a pulsed laser
that instantly initiates a fusion explosion", - isGunTech: true, - maxCount: 1, - count: 0, - allowed() { - return tech.haveGunCheck("laser") && tech.laserReflections < 3 && !tech.isWideLaser && !tech.historyLaser - }, - requires: "laser, not specular reflection, not diffuse", - 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: "shock wave", - description: "mobs caught in pulse's explosion are stunned
for up to 2 seconds", - isGunTech: true, - maxCount: 1, - count: 0, - allowed() { - return tech.isPulseLaser - }, - requires: "pulse", - effect() { - tech.isPulseStun = true; - }, - remove() { - tech.isPulseStun = false; - } - }, - { - name: "neocognitron", - description: "pulse automatically aims at a nearby mob
50% decreased delay after firing", - isGunTech: true, - maxCount: 1, - count: 0, - allowed() { - return tech.isPulseLaser && !tech.beamSplitter - }, - requires: "pulse", - effect() { - tech.isPulseAim = true; - }, - remove() { - tech.isPulseAim = false; - } - }, - //************************************************** - //************************************************** field - //************************************************** tech - //************************************************** - { - name: "bremsstrahlung radiation", - description: "blocking with standing wave harmonics
does damage to mobs", - isFieldTech: true, - maxCount: 9, - count: 0, - allowed() { - return m.fieldUpgrades[m.fieldMode].name === "standing wave harmonics" - }, - requires: "standing wave harmonics", - effect() { - tech.blockDmg += 0.75 //if you change this value also update the for loop in the electricity graphics in m.pushMass - }, - remove() { - tech.blockDmg = 0; - } - }, - { - name: "frequency resonance", - description: "standing wave harmonics shield is retuned
increase size and blocking efficiency by 40%", - isFieldTech: true, - maxCount: 9, - count: 0, - allowed() { - return m.fieldUpgrades[m.fieldMode].name === "standing wave harmonics" - }, - requires: "standing wave harmonics", - effect() { - m.fieldRange += 175 * 0.2 - m.fieldShieldingScale *= 0.55 - }, - remove() { - m.fieldRange = 175; - m.fieldShieldingScale = 1; - } - }, - { - name: "flux pinning", - description: "blocking with perfect diamagnetism
stuns mobs for +1 second", - isFieldTech: true, - maxCount: 9, - count: 0, - allowed() { - return m.fieldUpgrades[m.fieldMode].name === "perfect diamagnetism" - }, - requires: "perfect diamagnetism", - effect() { - tech.isStunField += 60; - }, - remove() { - tech.isStunField = 0; - } - }, - { - name: "eddy current brake", - description: "your stored energy projects a field that
limits the top speed of mobs", - isFieldTech: true, - maxCount: 1, - count: 0, - allowed() { - return m.fieldUpgrades[m.fieldMode].name === "perfect diamagnetism" - }, - requires: "perfect diamagnetism", - effect() { - tech.isPerfectBrake = true; - }, - remove() { - tech.isPerfectBrake = false; - } - }, - { - name: "fracture analysis", - description: "bullet impacts do 400% damage
to stunned mobs", - isFieldTech: true, - maxCount: 1, - count: 0, - allowed() { - return tech.isStunField || tech.oneSuperBall || tech.isCloakStun || tech.orbitBotCount > 1 || tech.isPerpetualStun - }, - requires: "a stun effect", - effect() { - tech.isCrit = true; - }, - remove() { - tech.isCrit = false; - } - }, - { - name: "pair production", - description: "picking up a power up gives you 250 energy", - isFieldTech: true, - maxCount: 1, - count: 0, - allowed() { - return m.fieldUpgrades[m.fieldMode].name === "nano-scale manufacturing" || m.fieldUpgrades[m.fieldMode].name === "pilot wave" - }, - requires: "nano-scale manufacturing", - effect: () => { - tech.isMassEnergy = true // used in m.grabPowerUp - m.energy += 3 - }, - remove() { - tech.isMassEnergy = false; - } - }, - { - name: "bot manufacturing", - description: "use nano-scale manufacturing
to build 3 random bots", - isFieldTech: true, - maxCount: 1, - count: 0, - isNonRefundable: true, - isCustomHide: true, - allowed() { - return m.fieldUpgrades[m.fieldMode].name === "nano-scale manufacturing" - }, - requires: "nano-scale manufacturing", - effect: () => { - m.energy = 0.01; - b.randomBot() - b.randomBot() - b.randomBot() - }, - remove() {} - }, - { - name: "bot prototypes", - description: "use nano-scale manufacturing to upgrade
all bots of a random type and build 2 of that bot", - isFieldTech: true, - maxCount: 1, - count: 0, - isNonRefundable: true, - isCustomHide: true, - allowed() { - return m.fieldUpgrades[m.fieldMode].name === "nano-scale manufacturing" && !(tech.isNailBotUpgrade && tech.isFoamBotUpgrade && tech.isBoomBotUpgrade && tech.isLaserBotUpgrade && tech.isOrbitBotUpgrade) - }, - requires: "nano-scale manufacturing", - effect: () => { - m.energy = 0.01; - //fill array of available bots - const notUpgradedBots = [] - if (!tech.isNailBotUpgrade) notUpgradedBots.push(() => { - tech.giveTech("nail-bot upgrade") - tech.setTechoNonRefundable("nail-bot upgrade") - for (let i = 0; i < 2; i++) { - b.nailBot() - tech.nailBotCount++; - } - simulation.makeTextLog(`tech.isNailBotUpgrade = true`) - }) - if (!tech.isFoamBotUpgrade) notUpgradedBots.push(() => { - tech.giveTech("foam-bot upgrade") - tech.setTechoNonRefundable("foam-bot upgrade") - for (let i = 0; i < 2; i++) { - b.foamBot() - tech.foamBotCount++; - } - simulation.makeTextLog(`tech.isFoamBotUpgrade = true`) - }) - if (!tech.isBoomBotUpgrade) notUpgradedBots.push(() => { - tech.giveTech("boom-bot upgrade") - tech.setTechoNonRefundable("boom-bot upgrade") - for (let i = 0; i < 2; i++) { - b.boomBot() - tech.boomBotCount++; - } - simulation.makeTextLog(`tech.isBoomBotUpgrade = true`) - }) - if (!tech.isLaserBotUpgrade) notUpgradedBots.push(() => { - tech.giveTech("laser-bot upgrade") - tech.setTechoNonRefundable("laser-bot upgrade") - for (let i = 0; i < 2; i++) { - b.laserBot() - tech.laserBotCount++; - } - simulation.makeTextLog(`tech.isLaserBotUpgrade = true`) - }) - if (!tech.isOrbitBotUpgrade) notUpgradedBots.push(() => { - tech.giveTech("orbital-bot upgrade") - tech.setTechoNonRefundable("orbital-bot upgrade") - for (let i = 0; i < 2; i++) { - b.orbitBot() - tech.orbitBotCount++; - } - simulation.makeTextLog(`tech.isOrbitalBotUpgrade = true`) - }) - if (!tech.isDynamoBotUpgrade) notUpgradedBots.push(() => { - tech.giveTech("dynamo-bot upgrade") - tech.setTechoNonRefundable("dynamo-bot upgrade") - for (let i = 0; i < 2; i++) { - b.orbitBot() - tech.dynamoBotCount++; - } - simulation.makeTextLog(`tech.isDynamoBotUpgrade = true`) - }) - //double chance for dynamo-bot, since it's very good for nano-scale - if (!tech.isDynamoBotUpgrade) notUpgradedBots.push(() => { - tech.giveTech("dynamo-bot upgrade") - tech.setTechoNonRefundable("dynamo-bot upgrade") - for (let i = 0; i < 2; i++) { - b.orbitBot() - tech.dynamoBotCount++; - } - simulation.makeTextLog(`tech.isDynamoBotUpgrade = true`) - }) - //choose random function from the array and run it - notUpgradedBots[Math.floor(Math.random() * notUpgradedBots.length)]() - }, - remove() {} - }, - { - name: "mycelium manufacturing", - description: "nano-scale manufacturing is repurposed
excess energy used to grow spores", - isFieldTech: true, - maxCount: 1, - count: 0, - allowed() { - return m.maxEnergy > 0.99 && m.fieldUpgrades[m.fieldMode].name === "nano-scale manufacturing" && !(tech.isMissileField || tech.isIceField || tech.isFastDrones || tech.isDroneGrab) - }, - requires: "nano-scale manufacturing", - effect() { - tech.isSporeField = true; - }, - remove() { - tech.isSporeField = false; - } - }, - { - name: "missile manufacturing", - description: "nano-scale manufacturing is repurposed
excess energy used to construct missiles", - isFieldTech: true, - maxCount: 1, - count: 0, - allowed() { - return m.maxEnergy > 0.5 && m.fieldUpgrades[m.fieldMode].name === "nano-scale manufacturing" && !(tech.isSporeField || tech.isIceField || tech.isFastDrones || tech.isDroneGrab) - }, - requires: "nano-scale manufacturing", - effect() { - tech.isMissileField = true; - }, - remove() { - tech.isMissileField = false; - } - }, - { - name: "ice IX manufacturing", - description: "nano-scale manufacturing is repurposed
excess energy used to synthesize ice IX", - isFieldTech: true, - maxCount: 1, - count: 0, - allowed() { - return m.fieldUpgrades[m.fieldMode].name === "nano-scale manufacturing" && !(tech.isSporeField || tech.isMissileField || tech.isFastDrones || tech.isDroneGrab) - }, - requires: "nano-scale manufacturing", - effect() { - tech.isIceField = true; - }, - remove() { - tech.isIceField = false; - } - }, - { - name: "thermoelectric effect", - description: "killing mobs with ice IX gives 4 health
and 80 energy", - isFieldTech: true, - maxCount: 9, - count: 0, - allowed() { - return tech.isIceField - }, - requires: "ice IX", - effect() { - tech.iceEnergy++ - }, - remove() { - tech.iceEnergy = 0; - } - }, - { - name: "degenerate matter", - description: "reduce harm by 40%
while negative mass field is active", - isFieldTech: true, - maxCount: 1, - count: 0, - allowed() { - return m.fieldUpgrades[m.fieldMode].name === "negative mass field" - }, - requires: "negative mass field", - effect() { - tech.isHarmReduce = true - }, - remove() { - tech.isHarmReduce = false; - } - }, - { - name: "annihilation", - description: "after touching mobs, they are annihilated
drains 33% of maximum energy", - isFieldTech: true, - maxCount: 1, - count: 0, - allowed() { - return m.fieldUpgrades[m.fieldMode].name === "negative mass field" || m.fieldUpgrades[m.fieldMode].name === "pilot wave" - }, - requires: "negative mass field", - effect() { - tech.isAnnihilation = true - }, - remove() { - tech.isAnnihilation = false; - } - }, - { - name: "Bose Einstein condensate", - description: "mobs inside your field are frozen
pilot wave, negative mass, time dilation", - isFieldTech: true, - maxCount: 1, - count: 0, - allowed() { - return m.fieldUpgrades[m.fieldMode].name === "pilot wave" || m.fieldUpgrades[m.fieldMode].name === "negative mass field" || m.fieldUpgrades[m.fieldMode].name === "time dilation field" - }, - requires: "pilot wave, negative mass field, time dilation field", - effect() { - tech.isFreezeMobs = true - }, - remove() { - tech.isFreezeMobs = false - } - }, - // { - // name: "thermal reservoir", - // description: "increase your plasma damage by 100%
plasma temporarily lowers health not energy", - // isFieldTech: true, - // maxCount: 1, - // count: 0, - // 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: "plasma jet", - description: "increase plasma torch's range by 27%", - isFieldTech: true, - maxCount: 9, - count: 0, - allowed() { - return m.fieldUpgrades[m.fieldMode].name === "plasma torch" - }, - requires: "plasma torch", - effect() { - tech.isPlasmaRange += 0.27; - }, - remove() { - tech.isPlasmaRange = 1; - } - }, - { - name: "plasma-bot", - description: "a bot uses energy to emit plasma
that damages and pushes mobs", - isFieldTech: true, - maxCount: 1, - count: 0, - allowed() { - return m.fieldUpgrades[m.fieldMode].name === "plasma torch" - }, - requires: "plasma torch", - effect() { - tech.plasmaBotCount++; - b.plasmaBot(); - }, - remove() { - tech.plasmaBotCount = 0; - } - }, - { - name: "micro-extruder", - description: "plasma torch extrudes a thin hot wire
increases damage, and energy drain", - isFieldTech: true, - maxCount: 1, - count: 0, - allowed() { - return m.fieldUpgrades[m.fieldMode].name === "plasma torch" - }, - requires: "plasma torch", - effect() { - tech.isExtruder = true; - }, - remove() { - tech.isExtruder = false; - } - }, - { - name: "timelike world line", - description: "time dilation doubles your relative time rate
and makes you immune to harm", - isFieldTech: true, - maxCount: 1, - count: 0, - allowed() { - return m.fieldUpgrades[m.fieldMode].name === "time dilation field" - }, - requires: "time dilation field", - effect() { - tech.isTimeSkip = true; - b.setFireCD(); - }, - remove() { - tech.isTimeSkip = false; - b.setFireCD(); - } - }, - { - name: "Lorentz transformation", - description: "permanently increase your relative time rate
move, jump, and shoot 40% faster", - isFieldTech: true, - maxCount: 1, - count: 0, - allowed() { - return m.fieldUpgrades[m.fieldMode].name === "time dilation field" || m.fieldUpgrades[m.fieldMode].name === "pilot wave" - }, - requires: "time dilation field", - effect() { - tech.fastTime = 1.40; - tech.fastTimeJump = 1.11; - m.setMovement(); - b.setFireCD(); - }, - remove() { - tech.fastTime = 1; - tech.fastTimeJump = 1; - m.setMovement(); - b.setFireCD(); - } - }, - { - name: "time crystals", - description: "quadruple your default energy regeneration", - isFieldTech: true, - maxCount: 1, - count: 0, - allowed() { - return (m.fieldUpgrades[m.fieldMode].name === "time dilation field" || m.fieldUpgrades[m.fieldMode].name === "pilot wave") && tech.energyRegen !== 0; - }, - requires: "time dilation field", - effect: () => { - tech.energyRegen = 0.004; - m.fieldRegen = tech.energyRegen; - }, - remove() { - tech.energyRegen = 0.001; - m.fieldRegen = tech.energyRegen; - } - }, - { - name: "phase decoherence", - description: "intangible to blocks and mobs while cloaked
passing through mobs drains your energy", - isFieldTech: true, - maxCount: 1, - count: 0, - allowed() { - return m.fieldUpgrades[m.fieldMode].name === "metamaterial cloaking" - }, - requires: "metamaterial cloaking", - effect() { - tech.isIntangible = true; - }, - remove() { - tech.isIntangible = false; - } - }, - { - name: "dazzler", - description: "decloaking stuns nearby mobs
drains 30% of your stored energy", - isFieldTech: true, - maxCount: 1, - count: 0, - allowed() { - return m.fieldUpgrades[m.fieldMode].name === "metamaterial cloaking" - }, - requires: "metamaterial cloaking", - effect() { - tech.isCloakStun = true; - }, - remove() { - tech.isCloakStun = false; - } - }, - { - name: "discrete optimization", - description: "increase damage by 66%
50% increased delay after firing", - isFieldTech: true, - maxCount: 1, - count: 0, - allowed() { - return m.fieldUpgrades[m.fieldMode].name === "metamaterial cloaking" || m.fieldUpgrades[m.fieldMode].name === "pilot wave" - }, - requires: "metamaterial cloaking", - effect() { - tech.aimDamage = 1.66 - b.setFireCD(); - }, - remove() { - tech.aimDamage = 1 - b.setFireCD(); - } - }, - { - 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, - allowed() { - return m.fieldUpgrades[m.fieldMode].name === "wormhole" - }, - requires: "wormhole", - effect() { - tech.isWormholeDamage = true - }, - remove() { - tech.isWormholeDamage = false - } - }, - { - name: "Penrose process", - description: "after a block falls into a wormhole
you gain 50 energy", - isFieldTech: true, - maxCount: 1, - count: 0, - allowed() { - return m.fieldUpgrades[m.fieldMode].name === "wormhole" - }, - requires: "wormhole", - effect() { - tech.isWormholeEnergy = true - }, - remove() { - tech.isWormholeEnergy = false - } - }, - { - name: "transdimensional spores", - description: "when blocks fall into a wormhole
higher dimension spores are summoned", - isFieldTech: true, - maxCount: 1, - count: 0, - allowed() { - return m.fieldUpgrades[m.fieldMode].name === "wormhole" - }, - requires: "wormhole", - effect() { - tech.isWormSpores = true - }, - remove() { - tech.isWormSpores = false - } - }, - { - name: "traversable geodesics", - description: "your bullets can traverse wormholes
spawn a gun and ammo", - isFieldTech: true, - maxCount: 1, - count: 0, - allowed() { - return m.fieldUpgrades[m.fieldMode].name === "wormhole" - }, - requires: "wormhole", - effect() { - tech.isWormBullets = true - powerUps.spawn(m.pos.x, m.pos.y, "gun"); - powerUps.spawn(m.pos.x, m.pos.y, "ammo"); - }, - remove() { - tech.isWormBullets = false - } - }, - //************************************************** - //************************************************** spawn power up - //************************************************** tech - //************************************************** - { - name: "heals", - description: "spawn 6 heals", - maxCount: 9, - count: 0, - isNonRefundable: true, - isCustomHide: true, - allowed() { - return true - }, - requires: "", - effect() { - for (let i = 0; i < 6; i++) powerUps.spawn(m.pos.x, m.pos.y, "heal"); - this.count-- - }, - remove() {} - }, - { - name: "ammo", - description: "spawn 6 ammo", - maxCount: 9, - count: 0, - isNonRefundable: true, - isCustomHide: true, - allowed() { - return !tech.isEnergyNoAmmo - }, - requires: "not exciton lattice", - effect() { - for (let i = 0; i < 6; i++) powerUps.spawn(m.pos.x, m.pos.y, "ammo"); - this.count-- - }, - remove() {} - }, - { - name: "research", - description: "spawn 4 research", - maxCount: 9, - count: 0, - isNonRefundable: true, - isCustomHide: true, - allowed() { - return !tech.isSuperDeterminism - }, - requires: "not superdeterminism", - effect() { - for (let i = 0; i < 4; i++) powerUps.spawn(m.pos.x, m.pos.y, "research"); - this.count-- - }, - remove() {} - }, - { - name: "gun", - description: "spawn a gun", - maxCount: 9, - count: 0, - isNonRefundable: true, - isCustomHide: true, - allowed() { - return !tech.isSuperDeterminism - }, - requires: "not superdeterminism", - effect() { - powerUps.spawn(m.pos.x, m.pos.y, "gun"); - this.count-- - }, - remove() {} - }, - { - name: "field", - description: "spawn a field", - maxCount: 9, - count: 0, - isNonRefundable: true, - isCustomHide: true, - allowed() { - return !tech.isSuperDeterminism - }, - requires: "not superdeterminism", - effect() { - powerUps.spawn(m.pos.x, m.pos.y, "field"); - this.count-- - }, - remove() {} - }, - ], - addLoreTechToPool() { //adds lore tech to tech pool - if (!simulation.isCheating) { - tech.tech.push({ - name: `undefined`, - description: `${lore.techCount+1}/10
add copies of this to the potential tech pool`, + for (i = 0, len = b.inventory.length; i < len; i++) { + if (b.guns[b.inventory[i]].name === name) return true + } + return false + }, + damageFromTech() { + let dmg = m.fieldDamage + if (tech.isTechDamage) dmg *= 2 + 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.4 + if (tech.isEnergyNoAmmo) dmg *= 1.5 + if (tech.isDamageForGuns) dmg *= 1 + 0.17 * b.inventory.length + if (tech.isLowHealthDmg) dmg *= 1 + 0.6 * Math.max(0, 1 - m.health) + if (tech.isHarmDamage && m.lastHarmCycle + 600 > m.cycle) dmg *= 3; + if (tech.isEnergyLoss) dmg *= 1.5; + if (tech.isAcidDmg && m.health > 1) dmg *= 1.4; + if (tech.restDamage > 1 && player.speed < 1) dmg *= tech.restDamage + if (tech.isEnergyDamage) dmg *= 1 + m.energy / 9; + if (tech.isDamageFromBulletCount) dmg *= 1 + bullet.length * 0.0038 + if (tech.isRerollDamage) dmg *= 1 + 0.039 * powerUps.research.count + if (tech.isOneGun && b.inventory.length < 2) dmg *= 1.25 + if (tech.isNoFireDamage && m.cycle > m.fireCDcycle + 120) dmg *= 2 + if (tech.isSpeedDamage) dmg *= 1 + Math.min(0.4, player.speed * 0.013) + if (tech.isBotDamage) dmg *= 1 + 0.06 * tech.totalBots() + return dmg * tech.slowFire * tech.aimDamage + }, + duplicationChance() { + return (tech.isBayesian ? 0.2 : 0) + tech.cancelCount * 0.045 + tech.duplicateChance + m.duplicateChance + }, + maxDuplicationEvent() { + if (tech.is100Duplicate && tech.duplicationChance() > 0.99) { + tech.is100Duplicate = false + const range = 1000 + const bossOptions = ["historyBoss", "cellBossCulture", "bomberBoss", "powerUpBoss", "orbitalBoss"] + spawn.randomLevelBoss(m.pos.x + range, m.pos.y, bossOptions); + spawn.randomLevelBoss(m.pos.x, m.pos.y + range, bossOptions); + spawn.randomLevelBoss(m.pos.x - range, m.pos.y, bossOptions); + spawn.randomLevelBoss(m.pos.x, m.pos.y - range, bossOptions); + } + }, + totalBots() { + return tech.dynamoBotCount + tech.foamBotCount + tech.nailBotCount + tech.laserBotCount + tech.boomBotCount + tech.orbitBotCount + tech.plasmaBotCount + tech.missileBotCount + }, + tech: [{ + name: "integrated armament", + description: `increase damage by 25%
your inventory can only hold 1 gun`, + maxCount: 1, + count: 0, + allowed() { + return b.inventory.length < 2 //&& !tech.haveGunCheck("CPT gun") + }, + requires: "no more than 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, + allowed() { + return b.inventory.length > 1 && !tech.isEnergyHealth + }, + requires: "at least 2 guns", + effect() { + tech.isEntanglement = true + setTimeout(function() { + simulation.boldActiveGunHUD(); + }, 1000); + + }, + remove() { + tech.isEntanglement = false; + } + }, + { + name: "arsenal", + description: "increase damage by 17%
for each gun in your inventory", + maxCount: 1, + count: 0, + allowed() { + return b.inventory.length > 1 + }, + requires: "at least 2 guns", + effect() { + tech.isDamageForGuns = true; + }, + remove() { + tech.isDamageForGuns = false; + } + }, + { + name: "active cooling", + description: "17% decreased delay after firing
for each gun in your inventory", + maxCount: 1, + count: 0, + 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 6 guns, but you can't switch guns
guns cycle automatically with each new level", maxCount: 1, count: 0, - isLore: true, isNonRefundable: true, - isCustomHide: true, + allowed() { + return (tech.isDamageForGuns || tech.isFireRateForGuns) && (b.inventory.length + 5) < b.guns.length + }, + requires: "arsenal or cyclic rate boost", + effect() { + tech.isGunCycle = true; + for (let i = 0; i < 6; i++) powerUps.spawn(m.pos.x + 10 * Math.random(), m.pos.y + 10 * Math.random(), "gun"); + }, + remove() { + tech.isGunCycle = false; + } + }, + { + name: "specialist", + description: "for every gun in your inventory spawn a
heal, research, field, ammo, or tech", + maxCount: 1, //random power up + count: 0, + isNonRefundable: true, + isExperimentHide: true, + allowed() { + return b.inventory.length > 2 + }, + requires: "at least 3 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: "logistics", + description: "ammo power ups give 200% ammo
but ammo is only added to your current gun", + maxCount: 1, + count: 0, + allowed() { + return !tech.isEnergyNoAmmo + }, + requires: "not exciton-lattice", + effect() { + tech.isAmmoForGun = true; + }, + remove() { + tech.isAmmoForGun = false; + } + }, + { + name: "supply chain", + description: "double your current ammo for all guns", + maxCount: 9, + count: 0, + isNonRefundable: true, + allowed() { + return tech.isAmmoForGun + }, + requires: "logistics", + 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(); + }, + remove() {} + }, + { + name: "catabolism", + description: "when you fire while out of ammo
gain 3 ammo, but lose 5 health", + maxCount: 1, + count: 0, + allowed() { + return !tech.isEnergyHealth && !tech.isEnergyNoAmmo + }, + requires: "not mass-energy equivalence
not exciton-lattice", + effect: () => { + tech.isAmmoFromHealth = true; + }, + remove() { + tech.isAmmoFromHealth = false; + } + }, + { + name: "perpetual ammo", + description: "find 2 ammo at the start of each level", + maxCount: 1, + count: 0, + allowed() { + return !tech.isPerpetualReroll && !tech.isPerpetualHeal && !tech.isPerpetualReroll && !tech.isPerpetualStun && !tech.isEnergyNoAmmo + }, + requires: "only 1 perpetual effect, not exciton lattice", + effect() { + tech.isPerpetualAmmo = true + }, + remove() { + tech.isPerpetualAmmo = false + } + }, + { + name: "desublimated ammunition", + description: "use 50% less ammo when crouching", + maxCount: 1, + count: 0, allowed() { return true }, requires: "", effect() { - setTimeout(() => { //a short delay, I can't remember why - lore.techCount++ - if (lore.techCount > 9) { - tech.removeLoreTechFromPool(); - } 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}/10
add copies of this to the potential tech pool` - } - for (let i = 0, len = 10; i < len; i++) tech.addLoreTechToPool() + tech.isCrouchAmmo = true + }, + remove() { + tech.isCrouchAmmo = false; + } + }, + { + name: "gun turret", + description: "reduce harm by 50% when crouching", + maxCount: 1, + count: 0, + allowed() { + return tech.isCrouchAmmo && !tech.isEnergyHealth + }, + requires: "desublimated ammunition
not mass-energy equivalence", + effect() { + tech.isTurret = true + }, + remove() { + tech.isTurret = false; + } + }, + { + name: "inertial frame", + description: "66% decreased delay after firing
you can only fire when at rest", + maxCount: 1, + count: 0, + allowed() { + return true + }, + requires: "", + effect: () => { + tech.isFireNotMove = true; + b.setFireCD(); + b.setFireMethod(); + }, + remove() { + if (tech.isFireNotMove) { + tech.isFireNotMove = false + b.setFireCD(); + b.setFireMethod(); + } + } + }, + { + name: "dead reckoning", + description: "increase damage by 33% when at rest", + maxCount: 9, + count: 0, + allowed() { + return tech.isFireNotMove + }, + requires: "inertial frame", + effect: () => { + tech.restDamage += 0.33 + }, + remove() { + tech.restDamage = 1; + } + }, + { + name: "Higgs mechanism", + description: "while firing your position is locked
and harm is reduced by 60%", + maxCount: 1, + count: 0, + allowed() { + return !tech.isEnergyHealth + }, + requires: "not mass energy", + effect: () => { + tech.isFireMoveLock = true; + b.setFireMethod(); + }, + remove() { + if (tech.isFireMoveLock) { + tech.isFireMoveLock = false + b.setFireMethod(); + } + } + }, + { + name: "squirrel-cage rotor", + description: "move and jump about 30% faster
but you take 5% more harm", + maxCount: 9, + count: 0, + 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 50%", + maxCount: 1, + count: 0, + allowed() { + return m.Fx > 0.016 && !tech.isEnergyHealth + }, + requires: "speed increase, not mass-energy equivalence", + effect() { + tech.isSpeedHarm = true + }, + remove() { + tech.isSpeedHarm = false + } + }, + { + name: "Newton's 2nd law", + description: "moving at high speeds increases damage
by up to 33%", + maxCount: 1, + count: 0, + allowed() { + return m.Fx > 0.016 + }, + requires: "speed increase", + effect() { + tech.isSpeedDamage = true + }, + remove() { + tech.isSpeedDamage = false + } + }, + // { + // name: "Galilean group", + // description: "reduce harm by 50% when at rest", + // maxCount: 1, + // count: 0, + // allowed() { + // return tech.isFireNotMove || tech.isFireMoveLock + // }, + // requires: "inertial frame or Higgs manism", + // effect() { + // tech.isRestHarm = true + // }, + // remove() { + // tech.isRestHarm = false; + // } + // }, + { + name: "kinetic bombardment", + description: "increase damage by up to 33%
at a distance of 40 steps from the target", + maxCount: 1, + count: 0, + allowed() { + return true + }, + requires: "", + effect() { + tech.isFarAwayDmg = true; //used in mob.damage() + }, + remove() { + tech.isFarAwayDmg = false; + } + }, + { + name: "electrostatic discharge", + description: "increase damage by 20%
20% increased delay after firing", + maxCount: 1, + count: 0, + allowed() { + return true + }, + effect() { + tech.slowFire = 1.2 + b.setFireCD(); + }, + remove() { + tech.slowFire = 1; + b.setFireCD(); + } + }, + { + name: "auto-loading heuristics", + description: "30% decreased delay after firing", + maxCount: 9, + count: 0, + allowed() { + return true + }, + requires: "", + effect() { + tech.fireRate *= 0.7 + b.setFireCD(); + }, + remove() { + tech.fireRate = 1; + b.setFireCD(); + } + }, + { + name: "iridium-192", + description: "explosions release gamma radiation
100% more damage, but over 4 seconds", + maxCount: 1, + count: 0, + allowed() { + return tech.explosiveRadius === 1 && !tech.isSmallExplosion && (tech.haveGunCheck("missiles") || tech.isIncendiary || (tech.haveGunCheck("grenades") && !tech.isNeutronBomb) || tech.haveGunCheck("vacuum bomb") || tech.isPulseLaser || tech.isMissileField || tech.boomBotCount > 1) + }, + requires: "an explosive damage source, not ammonium nitrate or nitroglycerin", + effect: () => { + tech.isExplodeRadio = true; + }, + remove() { + tech.isExplodeRadio = false; + } + }, + { + name: "ammonium nitrate", + description: "increase explosive damage by 20%
increase explosive radius by 20%", + maxCount: 9, + count: 0, + allowed() { + return !tech.isExplodeRadio && (tech.haveGunCheck("missiles") || tech.isIncendiary || (tech.haveGunCheck("grenades") && !tech.isNeutronBomb) || tech.haveGunCheck("vacuum bomb") || tech.isPulseLaser || tech.isMissileField || tech.boomBotCount > 1) + }, + requires: "an explosive damage source, not iridium-192", + effect: () => { + tech.explosiveRadius += 0.2; + }, + remove() { + tech.explosiveRadius = 1; + } + }, + { + name: "nitroglycerin", + description: "increase explosive damage by 60%
decrease explosive radius by 20%", + maxCount: 1, + count: 0, + allowed() { + return !tech.isExplodeRadio && (tech.haveGunCheck("missiles") || tech.isIncendiary || (tech.haveGunCheck("grenades") && !tech.isNeutronBomb) || tech.haveGunCheck("vacuum bomb") || tech.isPulseLaser || tech.isMissileField || tech.boomBotCount > 1) + }, + 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 400% more harm from explosions", + maxCount: 1, + count: 0, + allowed() { + return tech.haveGunCheck("missiles") || tech.isIncendiary || (tech.haveGunCheck("grenades") && !tech.isNeutronBomb) || tech.haveGunCheck("vacuum bomb") || tech.isPulseLaser || tech.isMissileField + }, + requires: "an explosive damage source", + effect: () => { + tech.isExplosionHarm = true; + }, + remove() { + tech.isExplosionHarm = false; + } + }, + { + name: "electric reactive armor", + // description: "explosions do no harm
while your energy is above 98%", + description: "harm from explosions is passively reduced
by 7% for every 10 stored energy", + maxCount: 1, + count: 0, + allowed() { + return tech.haveGunCheck("missiles") || tech.isIncendiary || (tech.haveGunCheck("grenades") && !tech.isNeutronBomb) || tech.haveGunCheck("vacuum bomb") || tech.isMissileField || tech.isExplodeMob || tech.isPulseLaser + }, + requires: "an explosive damage source", + effect: () => { + tech.isImmuneExplosion = true; + }, + remove() { + tech.isImmuneExplosion = false; + } + }, + { + name: "thermal runaway", + description: "mobs explode when they die
be careful", + maxCount: 1, + count: 0, + allowed() { + return (tech.haveGunCheck("missiles") || tech.isIncendiary || (tech.haveGunCheck("grenades") && !tech.isNeutronBomb) || tech.haveGunCheck("vacuum bomb") || tech.isPulseLaser || tech.isMissileField || tech.boomBotCount > 1) && !tech.sporesOnDeath && !tech.nailsDeathMob && !tech.isBotSpawner + }, + requires: "an explosive damage source, no other mob death tech", + effect: () => { + tech.isExplodeMob = true; + }, + remove() { + tech.isExplodeMob = false; + } + }, + { + name: "zoospore vector", + description: "mobs produce spores when they die
9% chance", + maxCount: 9, + count: 0, + allowed() { + return !tech.nailsDeathMob && !tech.isExplodeMob && !tech.isBotSpawner + }, + requires: "no other mob death tech", + effect() { + tech.sporesOnDeath += 0.09; + for (let i = 0; i < 8; i++) { + b.spore(m.pos) + } + }, + remove() { + tech.sporesOnDeath = 0; + } + }, + { + name: "impact shear", + description: "mobs release a nail when they die
nails target nearby mobs", + maxCount: 9, + count: 0, + allowed() { + return !tech.sporesOnDeath && !tech.isExplodeMob && !tech.isBotSpawner + }, + requires: "no other mob death tech", + effect: () => { + tech.nailsDeathMob++ + }, + remove() { + tech.nailsDeathMob = 0; + } + }, + { + name: "reaction inhibitor", + description: "mobs spawn with 11% less health", + maxCount: 3, + count: 0, + allowed() { + return tech.nailsDeathMob || tech.sporesOnDeath || tech.isExplodeMob || tech.isBotSpawner + }, + 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 66%
after not using your gun or field for 2 seconds", + maxCount: 1, + count: 0, + allowed() { + return (tech.totalBots() > 1 || tech.haveGunCheck("drones") || tech.haveGunCheck("mine") || tech.haveGunCheck("spores") || m.fieldUpgrades[m.fieldMode].name === "nano-scale manufacturing") && !tech.isEnergyHealth + }, + requires: "drones, spores, mines, or bots", + 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, + allowed() { + return tech.isNoFireDefense + }, + requires: "decorrelation", + effect() { + tech.isNoFireDamage = true + }, + remove() { + tech.isNoFireDamage = false + } + }, + { + name: "scrap bots", + description: "20% chance to build a bot after killing a mob
the bot lasts for about 20 seconds", + maxCount: 3, + count: 0, + allowed() { + return tech.totalBots() > 0 && !tech.sporesOnDeath && !tech.nailsDeathMob && !tech.isExplodeMob + }, + requires: "a bot and no other mob death tech", + effect() { + tech.isBotSpawner += 0.20; + }, + remove() { + tech.isBotSpawner = 0; + } + }, + { + name: "nail-bot", + description: "a bot fires nails at mobs in line of sight", + maxCount: 9, + count: 0, + allowed() { + return true + }, + requires: "", + effect() { + tech.nailBotCount++; + b.nailBot(); + }, + remove() { + tech.nailBotCount -= this.count; + } + }, + { + name: "nail-bot upgrade", + description: "500% increased fire rate
applies to all current and future nail-bots", + maxCount: 1, + count: 0, + allowed() { + return tech.nailBotCount > 1 + }, + requires: "2 or more nail bots", + effect() { + tech.isNailBotUpgrade = true + for (let i = 0; i < bullet.length; i++) { + if (bullet[i].botType === 'nail') bullet[i].isUpgraded = true + } + }, + remove() { + tech.isNailBotUpgrade = false + for (let i = 0; i < bullet.length; i++) { + if (bullet[i].botType === 'nail') bullet[i].isUpgraded = false + } + } + }, + { + name: "foam-bot", + description: "a bot fires foam at nearby mobs", + maxCount: 9, + count: 0, + allowed() { + return true + }, + requires: "", + effect() { + tech.foamBotCount++; + b.foamBot(); + }, + remove() { + tech.foamBotCount -= this.count; + } + }, + { + name: "foam-bot upgrade", + description: "250% increased foam size and fire rate
applies to all current and future foam-bots", + maxCount: 1, + count: 0, + allowed() { + return tech.foamBotCount > 1 + }, + requires: "2 or more foam bots", + effect() { + tech.isFoamBotUpgrade = true + for (let i = 0; i < bullet.length; i++) { + if (bullet[i].botType === 'foam') bullet[i].isUpgraded = true + } + }, + remove() { + tech.isFoamBotUpgrade = false + for (let i = 0; i < bullet.length; i++) { + if (bullet[i].botType === 'foam') bullet[i].isUpgraded = false + } + } + }, + { + name: "boom-bot", + description: "a bot defends the space around you
ignites an explosion after hitting a mob", + maxCount: 9, + count: 0, + allowed() { + return true + }, + requires: "", + effect() { + tech.boomBotCount++; + b.boomBot(); + }, + remove() { + tech.boomBotCount -= this.count; + } + }, + { + name: "boom-bot upgrade", + description: "250% increased explosion damage and size
applies to all current and future boom-bots", + maxCount: 1, + count: 0, + allowed() { + return tech.boomBotCount > 1 + }, + requires: "2 or more boom bots", + effect() { + tech.isBoomBotUpgrade = true + for (let i = 0; i < bullet.length; i++) { + if (bullet[i].botType === 'boom') bullet[i].isUpgraded = true + } + }, + remove() { + tech.isBoomBotUpgrade = false + for (let i = 0; i < bullet.length; i++) { + if (bullet[i].botType === 'boom') bullet[i].isUpgraded = false + } + } + }, + { + name: "laser-bot", + description: "a bot uses energy to emit a laser beam
that targets nearby mobs", + maxCount: 9, + count: 0, + allowed() { + return m.maxEnergy > 0.5 + }, + requires: "maximum energy above 50%", + effect() { + tech.laserBotCount++; + b.laserBot(); + }, + remove() { + tech.laserBotCount -= this.count; + } + }, + { + name: "laser-bot upgrade", + description: "400% increased laser damage
applies to all current and future laser-bots", + maxCount: 1, + count: 0, + allowed() { + return tech.laserBotCount > 1 + }, + requires: "2 or more laser bots", + effect() { + tech.isLaserBotUpgrade = true + for (let i = 0; i < bullet.length; i++) { + if (bullet[i].botType === 'laser') bullet[i].isUpgraded = true + } + }, + remove() { + tech.isLaserBotUpgrade = false + for (let i = 0; i < bullet.length; i++) { + if (bullet[i].botType === 'laser') bullet[i].isUpgraded = false + } + } + }, + { + name: "orbital-bot", + description: "a bot is locked in orbit around you
stuns and damages mobs on contact", + maxCount: 9, + count: 0, + allowed() { + return true + }, + requires: "", + effect() { + b.orbitBot(); + tech.orbitBotCount++; + }, + remove() { + tech.orbitBotCount -= this.count; + } + }, + { + name: "orbital-bot upgrade", + description: "increase damage by 200% and radius by 30%
applies to all current and future orbit-bots", + maxCount: 1, + count: 0, + allowed() { + return tech.orbitBotCount > 1 + }, + requires: "2 or more orbital bots", + effect() { + tech.isOrbitBotUpgrade = true + const range = 190 + 60 * 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) } - }, 1); + } + + }, + remove() { + tech.isOrbitBotUpgrade = false + const range = 190 + 60 * 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) + } + } + } + }, + { + name: "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, + allowed() { + return true + }, + requires: "", + effect() { + tech.dynamoBotCount++; + b.dynamoBot(); + }, + remove() { + tech.dynamoBotCount -= this.count; + } + }, + { + name: "dynamo-bot upgrade", + description: "dynamo-bots regen 24 energy per second
applies to all current and future dynamo-bots", + maxCount: 1, + count: 0, + allowed() { + return tech.dynamoBotCount > 1 + }, + requires: "2 or more dynamo bots", + effect() { + tech.isDynamoBotUpgrade = true + for (let i = 0; i < bullet.length; i++) { + if (bullet[i].botType === 'dynamo') bullet[i].isUpgraded = true + } + }, + remove() { + tech.isDynamoBotUpgrade = false + for (let i = 0; i < bullet.length; i++) { + if (bullet[i].botType === 'dynamo') bullet[i].isUpgraded = false + } + } + }, + { + name: "bot fabrication", + description: "anytime you collect 4 research
use them to build a random bot", + maxCount: 1, + count: 0, + allowed() { + return powerUps.research.count > 3 || build.isExperimentSelection + }, + requires: "at least 4 research", + effect() { + tech.isRerollBots = true; + powerUps.research.changeRerolls(0) + simulation.makeTextLog(`m.research = 0`) + }, + remove() { + tech.isRerollBots = false; + } + }, + { + name: "electroactive polymers", + description: "build 2 random bots
switching guns cycles bots to the same type", + maxCount: 1, + isNonRefundable: true, + count: 0, + allowed() { + return tech.totalBots() > 2 && b.inventory.length > 1 + }, + requires: "at least 3 bots and 2 guns", + effect() { + tech.isBotSwap = true + b.randomBot() + b.randomBot() + }, + remove() { + tech.isBotSwap = false + tech.botSwapCycleIndex = 0 + } + }, + { + name: "perimeter defense", + description: "reduce harm by 6%
for each of your permanent bots", + maxCount: 1, + count: 0, + allowed() { + return tech.totalBots() > 3 && !tech.isEnergyHealth + }, + requires: "at least 4 bots", + effect() { + tech.isBotArmor = true + }, + remove() { + tech.isBotArmor = false + } + }, { + name: "network effect", + description: "increase damage by 6%
for each of your permanent bots", + maxCount: 1, + count: 0, + allowed() { + return tech.totalBots() > 3 + }, + requires: "at least 4 bots", + effect() { + tech.isBotDamage = true + }, + remove() { + tech.isBotDamage = false + } + }, + { + name: "bot replication", + description: "duplicate your permanent bots
remove all of your guns", + maxCount: 1, + count: 0, + isNonRefundable: true, + isExperimentHide: true, + allowed() { + return tech.totalBots() > 3 + }, + requires: "at least 4 bots", + effect() { + 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() {} - }) - } - }, - junk: [ - // { - // name: "junk", - // description: "", - // maxCount: 9, - // count: 0, - // numberInPool: 0, - // isNonRefundable: true, - // isCustomHide: true, - // isJunk: true, - // allowed() { - // return true - // }, - // requires: "", - // effect() { + }, + { + name: "mass driver", + description: "increase block collision damage by 100%
charge throws more quickly for less energy", + maxCount: 1, + count: 0, + allowed() { + return m.fieldUpgrades[m.fieldMode].name !== "wormhole" + }, + requires: "not wormhole", + effect() { + tech.throwChargeRate = 2 + }, + remove() { + tech.throwChargeRate = 1 + } + }, + { + name: "restitution", + description: "mobs killed by collisions with blocks
spawn a heal, ammo, or research", + maxCount: 1, + count: 0, + allowed() { + return tech.throwChargeRate > 1 + }, + requires: "mass driver", + effect() { + tech.isBlockPowerUps = true + }, + remove() { + tech.isBlockPowerUps = false + } + }, + { + name: "inelastic collision", + description: "while you are holding a block
reduce harm by 80%", + maxCount: 1, + count: 0, + allowed() { + return tech.throwChargeRate > 1 + }, + requires: "mass driver", + effect() { + tech.isBlockHarm = true + }, + remove() { + tech.isBlockHarm = false + } + }, + { + name: "perpetual stun", + description: "stun all mobs for up to 12 seconds
at the start of each level", + maxCount: 1, + count: 0, + allowed() { + return !tech.isPerpetualReroll && !tech.isPerpetualHeal && !tech.isPerpetualAmmo + }, + requires: "only 1 perpetual effect", + effect() { + tech.isPerpetualStun = true + }, + remove() { + tech.isPerpetualStun = false + } + }, + { + name: "Pauli exclusion", + description: `after receiving harm from a collision become
immune to harm for an extra 0.75 seconds`, + maxCount: 9, + count: 0, + allowed() { + return true + }, + requires: "", + effect() { + tech.collisionImmuneCycles += 45; + m.immuneCycle = m.cycle + tech.collisionImmuneCycles; //player is immune to collision damage for 30 cycles + }, + remove() { + tech.collisionImmuneCycles = 25; + } + }, + { + name: "complex spin-statistics", + description: `become immune to harm for +1 second
once every 7 seconds`, + maxCount: 3, + count: 0, + allowed() { + return true //tech.collisionImmuneCycles > 30 + }, + requires: "", + effect() { + tech.cyclicImmunity += 60; + }, + remove() { + tech.cyclicImmunity = 0; + } + }, + { + name: "ablative drones", + description: "rebuild your broken parts as drones
chance to occur after receiving harm", + maxCount: 1, + count: 0, + 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", + description: "for 10 seconds after receiving harm
reduce harm by 66%", + maxCount: 1, + count: 0, + 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, + allowed() { + return m.harmReduction() < 1 + }, + requires: "some harm reduction", + effect() { + tech.isHarmDamage = true; + }, + remove() { + tech.isHarmDamage = false; + } + }, + { + name: "liquid cooling", + description: `freeze all mobs for 5 seconds
after receiving harm`, + maxCount: 1, + count: 0, + allowed() { + return tech.isSlowFPS + }, + requires: "clock gating", + effect() { + tech.isHarmFreeze = true; + }, + remove() { + tech.isHarmFreeze = false; + } + }, + { + name: "osmoprotectant", + description: `collisions with stunned or frozen mobs
cause you no harm`, + maxCount: 1, + count: 0, + allowed() { + return tech.isStunField || tech.isPulseStun || tech.oneSuperBall || tech.isHarmFreeze || tech.isIceField || tech.isIceCrystals || tech.isSporeFreeze || tech.isAoESlow || tech.isFreezeMobs || tech.isCloakStun || tech.orbitBotCount > 1 || tech.isWormholeDamage + }, + requires: "a freezing or stunning effect", + effect() { + tech.isFreezeHarmImmune = true; + }, + remove() { + tech.isFreezeHarmImmune = false; + } + }, + { + name: "clock gating", + description: `slow time by 50% after receiving harm
reduce harm by 20%`, + maxCount: 1, + count: 0, + allowed() { + return simulation.fpsCapDefault > 45 && !tech.isRailTimeSlow + }, + requires: "FPS above 45", + effect() { + tech.isSlowFPS = true; + }, + remove() { + tech.isSlowFPS = false; + } + }, + { + name: "CPT reversal", + description: "charge, parity, and time invert to undo harm
rewind (1.5—5) seconds for (66—220) energy", + maxCount: 1, + count: 0, + allowed() { //&& (m.fieldUpgrades[m.fieldMode].name !== "nano-scale manufacturing" || m.maxEnergy > 1) + return m.maxEnergy > 0.99 && m.fieldUpgrades[m.fieldMode].name !== "standing wave harmonics" && !tech.isEnergyHealth && !tech.isRewindGun + }, + requires: "not standing wave, mass-energy, piezo, max energy reduction, CPT gun", + effect() { + tech.isRewindAvoidDeath = true; + }, + remove() { + tech.isRewindAvoidDeath = false; + } + }, + { + name: "causality bots", + description: "when you rewind, build several bots
that protect you for about 9 seconds", + maxCount: 3, + count: 0, + allowed() { + return tech.isRewindAvoidDeath || tech.isRewindEnergy + }, + requires: "CPT", + effect() { + tech.isRewindBot++; + }, + remove() { + tech.isRewindBot = 0; + } + }, + { + name: "causality bombs", + description: "before you rewind drop several grenades", + maxCount: 1, + count: 0, + allowed() { + return tech.isRewindAvoidDeath + }, + requires: "CPT", + effect() { + tech.isRewindGrenade = true; + }, + remove() { + tech.isRewindGrenade = false; + } + }, + { + name: "piezoelectricity", + description: "colliding with mobs gives you 400 energy
reduce harm by 15%", + maxCount: 1, + count: 0, + allowed() { + return !tech.isEnergyHealth + }, + requires: "not mass-energy equivalence", + effect() { + tech.isPiezo = true; + m.energy += 4; + }, + remove() { + tech.isPiezo = false; + } + }, + { + name: "ground state", + description: "reduce harm by 66%
you no longer passively regenerate energy", + maxCount: 1, + count: 0, + allowed() { + return (tech.iceEnergy || tech.isWormholeEnergy || tech.isPiezo || tech.isRailEnergyGain) && tech.energyRegen !== 0.004 && !tech.isEnergyHealth + }, + requires: "piezoelectricity, Penrose, half-wave, or thermoelectric, but 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, + allowed() { + return !tech.isEnergyLoss && !tech.isPiezo && !tech.isRewindAvoidDeath && !tech.isRewindGun && !tech.isSpeedHarm && m.fieldUpgrades[m.fieldMode].name !== "negative mass field" && !tech.isHealLowHealth && !tech.isTechDamage + }, + requires: "not exothermic process, piezoelectricity, CPT, 1st law, negative mass , ...", + effect: () => { + m.health = 0 + // m.displayHealth(); + document.getElementById("health").style.display = "none" + document.getElementById("health-bg").style.display = "none" + document.getElementById("dmg").style.backgroundColor = "#0cf"; + tech.isEnergyHealth = true; + m.displayHealth(); + }, + remove() { + 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); + m.displayHealth(); + } + }, + { + name: "1st ionization energy", + description: "each heal power up you collect
increases your maximum energy by 5", + maxCount: 1, + count: 0, + 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: "electrolytes", + description: "increase damage by 1%
for every 9 stored energy", + maxCount: 1, + count: 0, + allowed() { + return m.maxEnergy > 1 || tech.isEnergyRecovery || tech.isPiezo || tech.energySiphon > 0 + }, + requires: "increased energy regen or max energy", + effect: () => { + tech.isEnergyDamage = true + }, + remove() { + tech.isEnergyDamage = false; + } + }, + { + name: "exciton-lattice", + description: `increase damage by 50%, but
ammo will no longer spawn`, + maxCount: 1, + count: 0, + allowed() { + return (tech.haveGunCheck("nail gun") && tech.isIceCrystals) || tech.haveGunCheck("laser") || m.fieldUpgrades[m.fieldMode].name === "plasma torch" || m.fieldUpgrades[m.fieldMode].name === "nano-scale manufacturing" || m.fieldUpgrades[m.fieldMode].name === "pilot wave" + }, + requires: "energy based damage", + 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, + allowed() { + return !tech.isEnergyHealth + }, + requires: "not mass-energy equivalence", + effect() { + tech.isEnergyLoss = true; + }, + remove() { + tech.isEnergyLoss = false; + } + }, + { + name: "heat engine", + description: `increase damage by 40%, but
reduce maximum energy by 50`, + maxCount: 1, + count: 0, + allowed() { + return tech.isEnergyLoss && m.maxEnergy < 1.1 && !tech.isSporeField && !tech.isRewindAvoidDeath + }, + requires: "exothermic process, not max energy increase, CPT, or spore nano-scale", + 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, + allowed() { + return tech.isEnergyLoss && m.maxEnergy < 1.1 + }, + requires: "exothermic process, not max energy increase", + effect() { + tech.isLowEnergyDamage = true; + }, + remove() { + tech.isLowEnergyDamage = false; + } + }, + { + name: "overcharge", + description: "increase your maximum energy by 50", + maxCount: 9, + count: 0, + allowed() { + return m.maxEnergy > 0.99 + }, + requires: "max energy >= 1", + effect() { + // m.maxEnergy += 0.5 + // m.energy += 0.5 + tech.bonusEnergy += 0.5 + m.setMaxEnergy() + }, + remove() { + tech.bonusEnergy = 0; + m.setMaxEnergy() + } + }, + { + name: "supercapacitor", + description: "energy above your max decays 60% slower", + maxCount: 1, + count: 0, + allowed() { + return tech.isEnergyRecovery || tech.isPiezo || tech.energySiphon > 0 || tech.isRailEnergyGain || tech.isWormholeEnergy || tech.iceEnergy > 0 + }, + requires: "a source of overfilled energy", + effect() { + tech.overfillDrain = 0.85 + }, + remove() { + tech.overfillDrain = 0.75 + } + }, + { + name: "energy conservation", + description: "6% of damage done recovered as energy", + maxCount: 9, + count: 0, + allowed() { + return tech.damageFromTech() > 1 + }, + requires: "some increased damage", + effect() { + tech.energySiphon += 0.06; + }, + remove() { + tech.energySiphon = 0; + } + }, + { + name: "waste energy recovery", + description: "if a mob has died in the last 5 seconds
regen 5% of max energy every second", + maxCount: 1, + count: 0, + allowed() { + return m.maxEnergy > 0.99 + }, + requires: "max energy >= 1", + effect() { + tech.isEnergyRecovery = true; + }, + remove() { + tech.isEnergyRecovery = false; + } + }, + { + name: "scrap recycling", + description: "if a mob has died in the last 5 seconds
regain 1% of max health every second", + maxCount: 1, + count: 0, + allowed() { + return !tech.isEnergyHealth + }, + requires: "not mass-energy equivalence", + effect() { + tech.isHealthRecovery = true; + }, + remove() { + tech.isHealthRecovery = false; + } + }, + { + name: "negative feedback", + description: "increase damage by 6%
for every 10 health below 100", + maxCount: 1, + count: 0, + allowed() { + return m.health < 0.5 || build.isExperimentSelection + }, + requires: "health below 60", + effect() { + tech.isLowHealthDmg = true; //used in mob.damage() + }, + remove() { + tech.isLowHealthDmg = false; + } + }, { + name: "antiscience", + description: "increase damage by 100%
lose 11 health when you pick up a tech", + maxCount: 1, + count: 0, + allowed() { + return (m.harmReduction() < 1 || tech.healthDrain || tech.isLowHealthDmg || tech.isHealthRecovery || tech.isHealLowHealth || tech.largerHeals > 1 || tech.isPerpetualHeal) && !tech.isEnergyHealth + }, + requires: "negative feedback or extra healing tech or harm reduction, not mass-energy", + effect() { + tech.isTechDamage = true; + }, + remove() { + tech.isTechDamage = false; + } + }, + { + name: "entropy exchange", + description: "heal for 1% of damage done", + maxCount: 9, + count: 0, + allowed() { + return !tech.isEnergyHealth && tech.damageFromTech() > 1 + }, + requires: "some increased damage, not mass-energy equivalence", + effect() { + tech.healthDrain += 0.01; + }, + remove() { + tech.healthDrain = 0; + } + }, + { + name: "fluoroantimonic acid", + description: "increase damage by 40%
when your health is above 100", + maxCount: 1, + count: 0, + allowed() { + return m.maxHealth > 1; + }, + requires: "health above 100", + effect() { + tech.isAcidDmg = true; + }, + remove() { + tech.isAcidDmg = false; + } + }, + { + name: "supersaturation", + description: "increase your maximum health by 50", + maxCount: 9, + count: 0, + allowed() { + return !tech.isEnergyHealth + }, + requires: "not mass-energy equivalence", + effect() { + tech.bonusHealth += 0.5 + m.addHealth(0.50) + m.setMaxHealth(); + }, + remove() { + tech.bonusHealth = 0 + m.setMaxHealth(); - // }, - // remove() {} - // }, - { - name: "banish", - description: "erase all junk tech from the possible pool
probably...", - maxCount: 1, - count: 0, - numberInPool: 0, - isNonRefundable: true, - isCustomHide: true, - isJunk: true, - allowed() { - return true - }, - requires: "", - effect() { - tech.removeJunkTechFromPool() - }, - remove() {} - }, - { - name: "ship", - description: "fly around with no legs
reduce combat difficulty by 1 level", - maxCount: 1, - count: 0, - numberInPool: 0, - isNonRefundable: true, - isCustomHide: true, - isJunk: true, - allowed() { - return !m.isShipMode - }, - requires: "", - effect() { - m.shipMode() - level.difficultyDecrease(simulation.difficultyMode) - }, - remove() {} - }, - { - name: "lubrication", - description: "reduce block density and friction for this level", - maxCount: 9, - count: 0, - numberInPool: 0, - isNonRefundable: true, - isCustomHide: 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, - numberInPool: 0, - isNonRefundable: true, - isCustomHide: 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, - numberInPool: 0, - isNonRefundable: true, - isCustomHide: 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, - numberInPool: 0, - isNonRefundable: true, - isCustomHide: true, - isJunk: true, - allowed() { - return m.fieldUpgrades[m.fieldMode].name !== "negative mass field" - }, - requires: "", - effect() { - ctx.globalCompositeOperation = "lighter"; - }, - remove() {} - }, - { - name: "rewind", - description: "every 5 seconds rewind 2 seconds
lasts 120 seconds", - maxCount: 9, - count: 0, - numberInPool: 0, - isNonRefundable: true, - isCustomHide: true, - isJunk: true, - allowed() { - return true - }, - requires: "", - effect() { - for (let i = 0; i < 24; i++) { - setTimeout(() => { m.rewind(120) }, i * 5000); + { + name: "inductive coupling", + description: "for each unused power up at the end of a level
add 3 max health (up to 51 health per level)", + maxCount: 1, + count: 0, + allowed() { + return !tech.isEnergyHealth + }, + requires: "not mass-energy equivalence", + effect() { + tech.isArmorFromPowerUps = true; //tracked by tech.armorFromPowerUps + }, + remove() { + tech.isArmorFromPowerUps = false; + // tech.armorFromPowerUps = 0; //this is now reset in tech.setupAllTech(); + m.setMaxHealth(); } }, - remove() {} - }, - { - name: "energy to mass conversion", - description: "convert your energy into blocks", - maxCount: 9, - count: 0, - numberInPool: 0, - isNonRefundable: true, - isCustomHide: true, - isJunk: true, - allowed() { - return true - }, - requires: "", - effect() { - for (let i = 0, len = Math.floor(m.energy * 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"; - World.add(engine.world, body[index]); //add to world - }, i * 100); + { + name: "transceiver chip", + description: "unused power ups at the end of each level
are still activated (selections are random)", + maxCount: 1, + count: 0, + allowed() { + return tech.isArmorFromPowerUps + }, + requires: "inductive coupling", + effect() { + tech.isEndLevelPowerUp = true; + }, + remove() { + tech.isEndLevelPowerUp = false; } + }, + { + name: "negentropy", + description: `at the start of each level
spawn a heal for every 50 missing health`, + maxCount: 1, + count: 0, + allowed() { + return m.maxHealth > 1 || tech.isArmorFromPowerUps + }, + requires: "increased max health", + effect() { + tech.isHealLowHealth = true; + }, + remove() { + tech.isHealLowHealth = false; + } + }, + { + name: "adiabatic healing", + description: "heal power ups are 100% more effective", + maxCount: 3, + count: 0, + allowed() { + return (m.health < 0.7 || build.isExperimentSelection) && !tech.isEnergyHealth + }, + requires: "not mass-energy equivalence", + effect() { + tech.largerHeals++; + }, + remove() { + tech.largerHeals = 1; + } + }, + { + name: "perpetual heals", + description: "find 2 heals at the start of each level", + maxCount: 1, + count: 0, + allowed() { + return !tech.isPerpetualReroll && !tech.isPerpetualAmmo && !tech.isPerpetualStun + }, + requires: "only 1 perpetual effect", + effect() { + tech.isPerpetualHeal = true + }, + remove() { + tech.isPerpetualHeal = false + } + }, + { + name: "anthropic principle", + nameInfo: "", + addNameInfo() { + setTimeout(function() { + powerUps.research.changeRerolls(0) + }, 1000); + }, + description: "once per level use 1 research to avoid dying
and spawn 6 heal power ups", + maxCount: 1, + count: 0, + 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: "quantum immortality", + description: "after dying, continue in an alternate reality
spawn 4 research", + maxCount: 1, + count: 0, + allowed() { + return powerUps.research.count > 1 || build.isExperimentSelection + }, + requires: "at least 2 research", + effect() { + tech.isImmortal = true; + for (let i = 0; i < 4; i++) powerUps.spawn(m.pos.x + Math.random() * 10, m.pos.y + Math.random() * 10, "research", false); + }, + remove() { + tech.isImmortal = false; + } + }, + { + name: "bubble fusion", + description: "after destroying a mob's shield
spawn 1-2 heals, ammo, or research", + maxCount: 1, + count: 0, + allowed() { + return true + }, + requires: "", + effect() { + tech.isShieldAmmo = true; + }, + remove() { + tech.isShieldAmmo = false; + } + }, + { + name: "stimulated emission", + description: "20% chance to duplicate spawned power ups
after a collision, eject 1 tech", + maxCount: 1, + count: 0, + allowed() { + return tech.duplicationChance() < 1 + }, + requires: "below 100% duplication chance", + effect: () => { + tech.isBayesian = true + simulation.draw.powerUp = simulation.draw.powerUpBonus //change power up draw + tech.maxDuplicationEvent() + }, + remove() { + tech.isBayesian = false + if (tech.duplicationChance() === 0) simulation.draw.powerUp = simulation.draw.powerUpNormal + } + }, + { + name: "replication", + description: "7% chance to duplicate spawned power ups
add 11 junk tech to the potential pool", + maxCount: 9, + count: 0, + allowed() { + return tech.duplicationChance() < 1 + }, + requires: "below 100% duplication chance", + effect() { + tech.duplicateChance += 0.075 + simulation.draw.powerUp = simulation.draw.powerUpBonus //change power up draw + tech.addJunkTechToPool(11) + tech.maxDuplicationEvent() + }, + remove() { + tech.duplicateChance = 0 + if (tech.duplicationChance() === 0) simulation.draw.powerUp = simulation.draw.powerUpNormal + } + }, + { + name: "futures exchange", + description: "clicking × to cancel a field, tech, or gun
adds 4.5% power up duplication chance", + maxCount: 1, + count: 0, + allowed() { + return tech.duplicationChance() < 1 && !tech.isDeterminism && (level.levelsCleared < 5 || Math.random() < 0.5) + }, + requires: "below 100% duplication chance, not determinism", + effect() { + tech.isCancelDuplication = true + tech.cancelCount = 0 + simulation.draw.powerUp = simulation.draw.powerUpBonus //change power up draw + }, + remove() { + tech.isCancelDuplication = false + // tech.cancelCount = 0 + if (tech.duplicationChance() === 0) simulation.draw.powerUp = simulation.draw.powerUpNormal + } + }, + { + name: "commodities exchange", + description: "clicking × to cancel a field, tech, or gun
spawns 8 heals, ammo, and research", + maxCount: 1, + count: 0, + allowed() { + return tech.isCancelDuplication + }, + requires: "futures exchange", + 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, + allowed() { + return tech.duplicationChance() > 0.15 + }, + requires: "some duplication chance", + effect() { + tech.isDupDamage = true; + }, + remove() { + tech.isDupDamage = false; + } + }, + { + name: "parthenogenesis", + description: "each level has a chance to spawn a level boss
equal to double your duplication chance", + maxCount: 1, + count: 0, + allowed() { + return tech.duplicationChance() > 0 + }, + requires: "some duplication chance", + effect() { + tech.isDuplicateBoss = true; + }, + remove() { + tech.isDuplicateBoss = false; + } + }, + { + name: "apomixis", + description: "after reaching 100% duplication chance
immediately spawn 4 level bosses", + maxCount: 1, + count: 0, + allowed() { + return tech.isDuplicateBoss + }, + requires: "parthenogenesis", + effect() { + tech.is100Duplicate = true; + tech.maxDuplicationEvent() + }, + remove() { + tech.is100Duplicate = false; + } + }, + { + name: "exchange symmetry", + description: "convert 1 a random tech into 3 new guns
recursive tech lose all stacks", + maxCount: 1, + count: 0, + isNonRefundable: true, + isExperimentHide: true, + allowed() { + return (tech.totalCount > 3) && !tech.isSuperDeterminism + }, + requires: "at least 1 tech, a chance to duplicate power ups", + 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.remove("${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, + isNonRefundable: true, + isExperimentHide: true, + allowed() { + return (tech.totalCount > 3) && !tech.isSuperDeterminism && tech.duplicationChance() > 0 + }, + requires: "at least 1 tech, a chance to duplicate power ups", + 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.remove("${tech.tech[choose].name}")`) + for (let i = 0; i < tech.tech[choose].count; i++) powerUps.spawn(m.pos.x, m.pos.y, "tech"); + powerUps.spawn(m.pos.x, m.pos.y, "tech"); + 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: "strange attractor", + description: `use 2 research to spawn 1 tech
with double your duplication chance`, + maxCount: 1, + count: 0, + isNonRefundable: true, + isExperimentHide: true, + allowed() { + return !tech.isSuperDeterminism && tech.duplicationChance() > 0 && powerUps.research.count > 1 + }, + requires: "at least 1 tech and 1 research, a chance to duplicate power ups", + effect: () => { + powerUps.research.changeRerolls(-2) + simulation.makeTextLog(`m.research -= 2 +
${powerUps.research.count}`) + const chanceStore = tech.duplicateChance + tech.duplicateChance = (tech.isBayesian ? 0.2 : 0) + tech.cancelCount * 0.045 + m.duplicateChance + tech.duplicateChance * 2 //increase duplication chance to simulate doubling all 3 sources of duplication chance + powerUps.spawn(m.pos.x, m.pos.y, "tech"); + tech.duplicateChance = chanceStore + }, + remove() {} + }, + { + name: "mine synthesis", + description: "drop a mine after picking up a power up", + maxCount: 1, + count: 0, + allowed() { + return tech.duplicationChance() > 0 + }, + requires: "some power up duplication", + effect() { + tech.isMineDrop = true; + if (tech.isMineDrop) b.mine(m.pos, { x: 0, y: 0 }, 0, tech.isMineAmmoBack) + }, + remove() { + tech.isMineDrop = false; + } + }, + { + name: "unified field theory", + description: `in the pause menu, change your field
by clicking on your field's box`, + maxCount: 1, + count: 0, + allowed() { + return (b.inventory.length > 1) || build.isExperimentSelection && !tech.isSuperDeterminism + }, + requires: "at least 2 guns, not superdeterminism", + effect() { + tech.isGunSwitchField = true; + }, + remove() { + tech.isGunSwitchField = false; + } + }, + { + name: "dark patterns", + description: "reduce combat difficulty by 1 level
add 16 junk tech to the potential pool", + maxCount: 1, + isNonRefundable: true, + isExperimentHide: true, + count: 0, + allowed() { + return powerUps.research.count === 0 && level.onLevel < 6 + }, + requires: "no research, and in the first 5 levels", + effect() { + level.difficultyDecrease(simulation.difficultyMode) + simulation.makeTextLog(`simulation.difficultyMode--`) + tech.addJunkTechToPool(16) + // for (let i = 0; i < tech.junk.length; i++) tech.tech.push(tech.junk[i]) + }, + remove() {} + }, + { + name: "cardinality", + description: "tech, fields, and guns have 5 choices", + maxCount: 1, + count: 0, + allowed() { + return !tech.isDeterminism + }, + requires: "not determinism", + effect: () => { + tech.isExtraChoice = true; + }, + remove() { + tech.isExtraChoice = false; + } + }, + { + name: "determinism", + description: "spawn 5 tech
tech, fields, and guns have only 1 choice", + maxCount: 1, + count: 0, + isNonRefundable: true, + allowed() { + return !tech.isExtraChoice && !tech.isCancelDuplication && !tech.isCancelRerolls + }, + requires: "not cardinality, not futures or commodities exchanges", + effect: () => { + tech.isDeterminism = true; + //if you change the six also change it in Born rule + for (let i = 0; i < 5; i++) powerUps.spawn(m.pos.x, m.pos.y, "tech"); + }, + remove() { + tech.isDeterminism = false; + } + }, + { + name: "superdeterminism", + description: "spawn 7 tech
research, guns, and fields no longer spawn", + maxCount: 1, + count: 0, + isNonRefundable: true, + allowed() { + return tech.isDeterminism && !tech.manyWorlds && !tech.isGunSwitchField + }, + requires: "determinism, not unified field theory", + effect: () => { + tech.isSuperDeterminism = true; + for (let i = 0; i < 7; i++) { //if you change the six also change it in Born rule + powerUps.spawn(m.pos.x, m.pos.y, "tech"); + } + }, + remove() { + tech.isSuperDeterminism = false; + } + }, + { + name: "Ψ(t) collapse", + description: "66% decreased delay after firing
when you have no research in your inventory", + maxCount: 1, + count: 0, + allowed() { + return powerUps.research.count === 0 && !tech.manyWorlds + }, + requires: "no research", + effect() { + tech.isRerollHaste = true; + tech.researchHaste = 0.33; + b.setFireCD(); + }, + remove() { + tech.isRerollHaste = false; + tech.researchHaste = 1; + b.setFireCD(); + } + }, + { + name: "many-worlds", + description: "after choosing a field, tech, or gun
if you have no research spawn 2", + maxCount: 1, + count: 0, + allowed() { + return powerUps.research.count === 0 && !tech.isSuperDeterminism && !tech.isRerollHaste + }, + requires: "not superdeterminism or Ψ(t) collapse
no research", + effect: () => { + tech.manyWorlds = true; + }, + remove() { + tech.manyWorlds = false; + } + }, + { + name: "renormalization", + description: "using a research for any purpose
has a 37% chance to spawn a research", + maxCount: 1, + count: 0, + allowed() { + return (powerUps.research.count > 1 || build.isExperimentSelection) && !tech.isSuperDeterminism && !tech.isRerollHaste + }, + requires: "not superdeterminism or Ψ(t) collapse
at least 2 research", + effect() { + tech.renormalization = true; + }, + remove() { + tech.renormalization = false; + } + }, + { + name: "erase", + description: "researched or canceled tech won't reoccur
spawn 4 research", + maxCount: 1, + count: 0, + 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 < 4; i++) powerUps.spawn(m.pos.x, m.pos.y, "research", false); + }, + remove() { + tech.isBanish = false + powerUps.tech.banishLog = [] //reset banish log + } + }, + { + name: "Bayesian statistics", + description: "increase damage by 3.9%
for each research in your inventory", + maxCount: 1, + count: 0, + allowed() { + return powerUps.research.count > 4 || build.isExperimentSelection + }, + requires: "at least 5 research", + effect() { + tech.isRerollDamage = true; + }, + remove() { + tech.isRerollDamage = false; + } + }, + { + name: "Born rule", + description: "remove all current tech
spawn new tech to replace them", + maxCount: 1, + count: 0, + // isNonRefundable: true, + isExperimentHide: true, + allowed() { + return (tech.totalCount > 6) + }, + requires: "more than 6 tech", + effect: () => { + //remove active bullets //to get rid of bots + for (let i = 0; i < bullet.length; ++i) Matter.World.remove(engine.world, bullet[i]); + bullet = []; + let count = 0 //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 -= 3 //remove the bonus tech + if (tech.isSuperDeterminism) count -= 2 //remove the bonus tech + tech.setupAllTech(); // remove all tech + tech.addLoreTechToPool(); + for (let i = 0; i < count; i++) { // spawn new tech power ups + powerUps.spawn(m.pos.x, m.pos.y, "tech"); + } + //have state is checked in m.death() + }, + remove() {} }, - remove() {} - }, - { - name: "level.nextLevel()", - description: "advance to the next level", - maxCount: 9, - count: 0, - numberInPool: 0, - isNonRefundable: true, - isCustomHide: true, - isJunk: true, - allowed() { - return true - }, - requires: "", - effect() { - level.nextLevel(); - }, - remove() {} - }, - { - name: "expert system", - description: "spawn a tech power up
add 64 junk tech to the potential pool", - maxCount: 9, - count: 0, - numberInPool: 0, - isNonRefundable: true, - isCustomHide: true, - isJunk: true, - allowed() { - return true - }, - requires: "", - effect() { - powerUps.spawn(m.pos.x, m.pos.y, "tech"); - tech.addJunkTechToPool(64) - }, - remove() {} - }, - { - name: "energy investment", - description: "every 10 seconds drain your energy and return it doubled 10 seconds later
lasts 180 seconds", - maxCount: 9, - count: 0, - numberInPool: 0, - isNonRefundable: true, - isCustomHide: 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); + { + name: "perpetual research", + description: "find 1 research at the start of each level", + maxCount: 1, + count: 0, + allowed() { + return !tech.isSuperDeterminism && !tech.isPerpetualHeal && !tech.isPerpetualAmmo && !tech.isPerpetualStun + }, + requires: "only 1 perpetual effect, not superdeterminism", + effect() { + tech.isPerpetualReroll = true + }, + remove() { + tech.isPerpetualReroll = false } }, - remove() {} - }, - { - name: "missile Launching System", - description: "fire missiles for the next 60 seconds", - maxCount: 9, - count: 0, - numberInPool: 0, - isNonRefundable: true, - isCustomHide: true, - isJunk: true, - allowed() { - return true + //************************************************** + //************************************************** gun + //************************************************** tech + //************************************************** + { + name: "CPT gun", + description: `adds the CPT gun to your inventory
it rewinds your health, velocity, and position`, + isGunTech: true, + maxCount: 1, + count: 0, + allowed() { + return (tech.totalBots() > 5 || m.fieldUpgrades[m.fieldMode].name === "nano-scale manufacturing" || m.fieldUpgrades[m.fieldMode].name === "plasma torch" || m.fieldUpgrades[m.fieldMode].name === "pilot wave") && !tech.isEnergyHealth && !tech.isRewindAvoidDeath //build.isExperimentSelection || + }, + requires: "bots > 5, plasma torch, nano-scale, 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 + } + } }, - requires: "", - effect() { - for (let i = 0; i < 60; i++) { - setTimeout(() => { - const where = { - x: m.pos.x, - y: m.pos.y - 40 + { + name: "incendiary ammunition", + description: "shotgun, super balls, and drones
are loaded with explosives", + isGunTech: true, + maxCount: 1, + count: 0, + allowed() { + return ((m.fieldUpgrades[m.fieldMode].name === "nano-scale manufacturing" && !(tech.isSporeField || tech.isMissileField || tech.isIceField)) || tech.haveGunCheck("drones") || tech.haveGunCheck("super balls") || tech.haveGunCheck("shotgun")) && !tech.isNailShot + }, + requires: "drones, super balls, shotgun", + effect() { + tech.isIncendiary = true + }, + remove() { + tech.isIncendiary = false; + } + }, + { + name: "fragmentation", + description: "some detonations and collisions eject nails
blocks, rail gun, grenades, missiles, shotgun slugs", + isGunTech: true, + maxCount: 9, + count: 0, + allowed() { + return (tech.haveGunCheck("grenades") && !tech.isNeutronBomb) || tech.haveGunCheck("missiles") || tech.haveGunCheck("rail gun") || (tech.haveGunCheck("shotgun") && tech.isSlugShot) || tech.throwChargeRate > 1 + }, + requires: "grenades, missiles, rail gun, shotgun slugs, or mass driver", + effect() { + tech.fragments++ + }, + remove() { + tech.fragments = 0 + } + }, + { + name: "superfluidity", + description: "freeze effects are applied to a small area", + isGunTech: true, + maxCount: 1, + count: 0, + allowed() { + return tech.isIceCrystals || tech.isSporeFreeze || tech.isIceField + }, + requires: "a freeze effect", + effect() { + tech.isAoESlow = true + }, + remove() { + tech.isAoESlow = false + } + }, + { + name: "radioactive contamination", + description: "after a mob or shield dies,
leftover radiation spreads to a nearby mob", + isGunTech: true, + maxCount: 1, + count: 0, + allowed() { + return tech.isNailRadiation || tech.isWormholeDamage || tech.isNeutronBomb || tech.isExplodeRadio + }, + requires: "radiation damage source", + effect() { + tech.isRadioactive = true + }, + remove() { + tech.isRadioactive = false + } + }, + { + name: "anti-shear topology", + description: "some bullets last 30% longer
drones, spores, missiles, foam, wave, neutron", + isGunTech: true, + maxCount: 3, + count: 0, + allowed() { + return m.fieldUpgrades[m.fieldMode].name === "nano-scale manufacturing" || tech.haveGunCheck("spores") || tech.haveGunCheck("drones") || tech.haveGunCheck("missiles") || tech.haveGunCheck("foam") || tech.haveGunCheck("wave beam") || tech.isNeutronBomb + }, + requires: "drones, spores, missiles, foam
wave beam, neutron bomb", + effect() { + tech.isBulletsLastLonger += 0.3 + }, + remove() { + tech.isBulletsLastLonger = 1; + } + }, + { + name: "microstates", + description: "increase damage by 4%
for every 10 active bullets", + isGunTech: true, + maxCount: 1, + count: 0, + allowed() { + return tech.isBulletsLastLonger > 1 + }, + requires: "Lorentzian topology", + effect() { + tech.isDamageFromBulletCount = true + }, + remove() { + tech.isDamageFromBulletCount = false + } + }, + { + name: "needle gun", + description: "nail gun fires 3 mob piercing needles
requires 3 times more ammo", + isGunTech: true, + maxCount: 1, + count: 0, + allowed() { + return tech.haveGunCheck("nail gun") && !tech.nailFireRate && !tech.isIceCrystals && !tech.isRivets && !tech.isNailRadiation + }, + requires: "nail gun, not ice crystal, rivets, 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 } - 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, - numberInPool: 0, - isNonRefundable: true, - isCustomHide: 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, - numberInPool: 0, - isNonRefundable: true, - isCustomHide: 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, - numberInPool: 0, - isNonRefundable: true, - isCustomHide: 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); - let grd = ctx.createLinearGradient(-30, 0, 30, 0); - grd.addColorStop(0, m.fillColorDark); - grd.addColorStop(1, m.fillColor); - ctx.fillStyle = grd; - 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, - numberInPool: 0, - isNonRefundable: true, - isCustomHide: 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); - let grd = ctx.createLinearGradient(-30, 0, 30, 0); - grd.addColorStop(0, m.fillColorDark); - grd.addColorStop(1, m.fillColor); - ctx.fillStyle = grd; - 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, - numberInPool: 0, - isNonRefundable: true, - isCustomHide: 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); - let grd = ctx.createLinearGradient(-30, 0, 30, 0); - grd.addColorStop(0, m.fillColorDark); - grd.addColorStop(1, m.fillColor); - ctx.fillStyle = grd; - 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 = grd; - 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, - numberInPool: 0, - isNonRefundable: true, - isCustomHide: 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, - numberInPool: 0, - isNonRefundable: true, - isCustomHide: true, - isJunk: true, - allowed() { - return tech.totalBots() > 2 - }, - requires: "at least 3 bots", - effect() { - const total = tech.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++ + }, + 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 + } + } } - ] - 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, - numberInPool: 0, - isNonRefundable: true, - isCustomHide: true, - isJunk: true, - allowed() { - return true + { + name: "ceramic needle", + description: `your needles pierce shields
directly damaging shielded mobs`, + isGunTech: true, + maxCount: 1, + count: 0, + allowed() { + return tech.isNeedles && !tech.isNailRadiation + }, + requires: "needle gun, not irradiated nails", + effect() { + tech.isNeedleShieldPierce = true + }, + remove() { + tech.isNeedleShieldPierce = false + } }, - requires: "", - effect() { - level.difficultyIncrease(simulation.difficultyMode) + { + name: "rivet gun", + description: "nail gun slowly fires a heavy rivet", + isGunTech: true, + maxCount: 1, + count: 0, + allowed() { + return tech.haveGunCheck("nail gun") && !tech.nailFireRate && !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 + } + } + } + } }, - remove() {} - }, - { - name: "stun", - description: "stun all mobs for up to 8 seconds", - maxCount: 9, - count: 0, - numberInPool: 0, - isNonRefundable: true, - isCustomHide: true, - isJunk: true, - allowed() { - return true + { + name: "rivet diameter", + description: `your rivets are 20% larger
increases mass and physical damage`, + isGunTech: true, + maxCount: 9, + count: 0, + allowed() { + return tech.isRivets + }, + requires: "rivet gun", + effect() { + tech.rivetSize += 0.2 + }, + remove() { + tech.rivetSize = 1; + } }, - requires: "", - effect() { - for (let i = 0; i < mob.length; i++) mobs.statusStun(mob[i], 480) + { + name: "ice crystal nucleation", + description: "the nail gun uses energy to condense
unlimited freezing ice shards", + isGunTech: true, + maxCount: 1, + count: 0, + allowed() { + return tech.haveGunCheck("nail gun") && !tech.nailInstantFireRate && !tech.isRivets && !tech.isNeedles && !tech.isNailRadiation && !tech.isNailCrit + }, + requires: "nail gun, not powder-actuated, rivets, needles, irradiated, or fission", + 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; + } + } + } + } }, - remove() {} - }, - { - name: "re-arm", - description: "eject all your guns", - maxCount: 9, - count: 0, - numberInPool: 0, - isNonRefundable: true, - isCustomHide: true, - isJunk: true, - allowed() { - return b.inventory.length > 0 + { + name: "pneumatic actuator", + description: "nail gun takes 45% less time to ramp up
to it's shortest delay after firing", + isGunTech: true, + maxCount: 1, + count: 0, + allowed() { + return tech.haveGunCheck("nail gun") && !tech.isRivets && !tech.isNeedles + }, + requires: "nail gun, not rivets or needles", + effect() { + tech.nailFireRate = 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.nailFireRate) { + tech.nailFireRate = 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() + } + } + } }, - 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"); + { + name: "powder-actuated", + description: "nail gun takes no time to ramp up
nails have a 30% faster muzzle speed", + isGunTech: true, + maxCount: 1, + count: 0, + allowed() { + return tech.haveGunCheck("nail gun") && tech.nailFireRate && !tech.isIceCrystals + }, + requires: "nail gun and pneumatic actuator", + 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: "supercritical fission", + description: "nails, needles, and rivets can explode
if they strike mobs near their center", + isGunTech: true, + maxCount: 1, + count: 0, + allowed() { + return (tech.isNailShot || tech.nailBotCount > 1 || tech.haveGunCheck("nail gun")) && !tech.isIceCrystals + }, + requires: "nails", + effect() { + tech.isNailCrit = true + }, + remove() { + tech.isNailCrit = false + } + }, + { + name: "irradiated nails", + description: "nails and rivets are radioactive
about 90% more damage over 2 seconds", + isGunTech: true, + maxCount: 1, + count: 0, + allowed() { + return (tech.isMineDrop + tech.nailBotCount + tech.fragments + tech.nailsDeathMob / 2 + ((tech.haveGunCheck("mine") && !tech.isLaserMine) + tech.isNailShot + (tech.haveGunCheck("nail gun") && !tech.isNeedleShieldPierce)) * 2 > 1) && !tech.isIceCrystals + }, + requires: "nails, rivets, nonceramic needles, not ice crystals", + effect() { + tech.isNailRadiation = true; + }, + remove() { + tech.isNailRadiation = false; + } + }, + { + name: "4s half-life", + description: "nails are made of plutonium-238
increase damage by 100% over 6 seconds", + isGunTech: true, + maxCount: 1, + count: 0, + allowed() { + return tech.isNailRadiation && !tech.isFastRadiation + }, + requires: "irradiated nails", + effect() { + tech.isSlowRadiation = true; + }, + remove() { + tech.isSlowRadiation = false; + } + }, + { + name: "1/2s half-life", + description: "nails are made of lithium-8
damage occurs after 1/2 a second", + isGunTech: true, + maxCount: 1, + count: 0, + allowed() { + return tech.isNailRadiation && !tech.isSlowRadiation + }, + requires: "irradiated nails", + effect() { + tech.isFastRadiation = true; + }, + remove() { + tech.isFastRadiation = false; + } + }, + { + name: "shotgun spin-statistics", + description: "immune to harm while firing the shotgun
ammo costs are doubled", + isGunTech: true, + maxCount: 1, + count: 0, + allowed() { + return tech.haveGunCheck("shotgun") + }, + requires: "shotgun", + effect() { + tech.isShotgunImmune = true; - //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 research", - maxCount: 9, - count: 0, - numberInPool: 0, - isNonRefundable: true, - isCustomHide: true, - isJunk: true, - allowed() { - return powerUps.research.count > 3 - }, - requires: "at least 4 research", - effect() { - for (let i = 0; i < powerUps.research.count; i++) powerUps.spawn(m.pos.x + 60 * (Math.random() - 0.5), m.pos.y + 60 * (Math.random() - 0.5), "research"); - powerUps.research.count = 0 - }, - remove() {} - }, - { - name: "quantum black hole", - description: "use all your energy to spawn inside the event horizon of a huge black hole", - maxCount: 9, - count: 0, - numberInPool: 0, - isNonRefundable: true, - isCustomHide: true, - isJunk: true, - allowed() { - return true - }, - requires: "", - effect() { - m.energy = 0 - spawn.suckerBoss(m.pos.x, m.pos.y - 1000) - }, - remove() {} - }, - { - name: "black hole cluster", - description: "spawn 2 research
spawn 40 nearby black holes", - maxCount: 9, - count: 0, - numberInPool: 0, - isNonRefundable: true, - isCustomHide: true, - isJunk: true, - allowed() { - return true - }, - requires: "", - effect() { - for (let i = 0; i < 2; i++) powerUps.spawn(m.pos.x, m.pos.y, "research"); - const unit = { - x: 1, - y: 0 - } - for (let i = 0; i < 40; i++) { - const where = Vector.add(m.pos, Vector.mult(Vector.rotate(unit, Math.random() * 2 * Math.PI), 600 + 800 * Math.random())) - spawn.sucker(where.x, where.y) + //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); + break; + } + } + simulation.updateGunHUD(); + + 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 * 0.5 + break; + } + } + }, + remove() { + 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; + break; + } + } } }, - remove() {} + { + name: "nailshot", + description: "the shotgun fires a burst of nails", + isGunTech: true, + maxCount: 1, + count: 0, + allowed() { + return tech.haveGunCheck("shotgun") && !tech.isIncendiary && !tech.isSlugShot + }, + requires: "shotgun", + effect() { + tech.isNailShot = true; + }, + remove() { + tech.isNailShot = false; + } + }, + { + name: "shotgun slug", + description: "the shotgun fires 1 large bullet", + isGunTech: true, + maxCount: 1, + count: 0, + allowed() { + return tech.haveGunCheck("shotgun") && !tech.isNailShot + }, + requires: "shotgun", + effect() { + tech.isSlugShot = true; + }, + remove() { + tech.isSlugShot = false; + } + }, + { + name: "Newton's 3rd law", + description: "shotgun recoil is greatly increased
and has a 66% decreased delay after firing", + isGunTech: true, + maxCount: 1, + count: 0, + allowed() { + return tech.haveGunCheck("shotgun") + }, + requires: "shotgun", + effect() { + tech.isShotgunRecoil = true; + }, + remove() { + tech.isShotgunRecoil = false; + } + }, + { + name: "super duper", + description: "fire 1 additional super ball", + isGunTech: true, + maxCount: 9, + count: 0, + allowed() { + return tech.haveGunCheck("super balls") && !tech.oneSuperBall + }, + requires: "super balls, but not the tech super ball", + effect() { + tech.superBallNumber++ + }, + remove() { + tech.superBallNumber = 4; + } + }, + { + name: "super ball", + description: "fire just 1 large super ball
that stuns mobs for 3 second", + isGunTech: true, + maxCount: 1, + count: 0, + allowed() { + return tech.haveGunCheck("super balls") && tech.superBallNumber === 4 + }, + requires: "super balls, but not super duper", + effect() { + tech.oneSuperBall = true; + }, + remove() { + tech.oneSuperBall = false; + } + }, + { + name: "super sized", + description: `your super balls are 20% larger
increases mass and physical damage`, + isGunTech: true, + maxCount: 9, + count: 0, + allowed() { + return tech.haveGunCheck("super balls") + }, + requires: "super balls", + effect() { + tech.bulletSize += 0.15 + }, + remove() { + tech.bulletSize = 1; + } + }, + { + name: "wave packet", + description: "wave beam emits two oscillating particles
decrease wave damage by 20%", + isGunTech: true, + maxCount: 1, + count: 0, + allowed() { + return tech.haveGunCheck("wave beam") + }, + requires: "wave beam", + effect() { + tech.waveHelix = 2 + }, + remove() { + tech.waveHelix = 1 + } + }, + { + name: "phase velocity", + description: "the wave beam propagates faster in solids", + isGunTech: true, + maxCount: 1, + count: 0, + allowed() { + return tech.haveGunCheck("wave beam") && !tech.isWaveReflect + }, + requires: "wave beam", + effect() { + tech.waveSpeedMap = 3 //needs to be 3 to stop bound state require check + tech.waveSpeedBody = 1.9 + }, + remove() { + tech.waveSpeedMap = 0.08 + tech.waveSpeedBody = 0.25 + } + }, + { + name: "bound state", + description: "wave beam bullets last 5x longer
bullets are bound to a region around player", + isGunTech: true, + maxCount: 1, + count: 0, + allowed() { + return tech.haveGunCheck("wave beam") && tech.waveSpeedMap !== 3 + }, + requires: "wave beam", + effect() { + tech.isWaveReflect = true + }, + remove() { + tech.isWaveReflect = false + } + }, + { + name: "cruise missile", + description: "missiles travel 50% slower,
but have a 50% larger explosive payload", + isGunTech: true, + maxCount: 1, + count: 0, + allowed() { + return tech.haveGunCheck("missiles") || tech.isMissileField + }, + requires: "missiles", + effect() { + tech.missileSize = true + }, + remove() { + tech.missileSize = false + } + }, + { + name: "MIRV", + description: "launch +1 missile at a time
decrease size and fire rate by 10%", + isGunTech: true, + maxCount: 9, + count: 0, + allowed() { + return tech.haveGunCheck("missiles") + }, + requires: "missiles", + effect() { + tech.missileCount++; + }, + remove() { + tech.missileCount = 1; + } + }, + { + name: "missile-bot", + description: "a bot fires missiles at far away mobs", + isGunTech: true, + maxCount: 1, + count: 0, + allowed() { + return tech.haveGunCheck("missiles") + }, + requires: "missiles", + effect() { + tech.missileBotCount++; + b.missileBot(); + }, + remove() { + tech.missileBotCount = 0; + } + }, + { + name: "rocket-propelled grenade", + description: "grenades rapidly accelerate forward
map collisions trigger an explosion", + isGunTech: true, + maxCount: 1, + count: 0, + 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, + 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: "neutron bomb", + description: "grenades are irradiated with Cf-252
does damage, harm, and drains energy", + isGunTech: true, + maxCount: 1, + count: 0, + allowed() { + return tech.haveGunCheck("grenades") && !tech.fragments && !tech.isVacuumBomb + }, + requires: "grenades, not fragmentation", + effect() { + tech.isNeutronBomb = true; + b.setGrenadeMode() + }, + remove() { + tech.isNeutronBomb = false; + b.setGrenadeMode() + } + }, + { + name: "water shielding", + description: "increase neutron bomb's range by 20%
player is immune to its harmful effects", + isGunTech: true, + maxCount: 1, + count: 0, + allowed() { + return tech.isNeutronBomb + }, + requires: "neutron bomb", + effect() { + tech.isNeutronImmune = true + }, + remove() { + tech.isNeutronImmune = false + } + }, + { + name: "vacuum permittivity", + description: "increase neutron bomb's range by 20%
objects in range of the bomb are slowed", + isGunTech: true, + maxCount: 1, + count: 0, + allowed() { + return tech.isNeutronBomb + }, + requires: "neutron bomb", + effect() { + tech.isNeutronSlow = true + }, + remove() { + tech.isNeutronSlow = false + } + }, + { + name: "laser-mines", + description: "mines hover in place until mobs get in range
mines use energy to emit 3 unaimed lasers", + isGunTech: true, + maxCount: 1, + count: 0, + allowed() { + return (tech.haveGunCheck("mine") || tech.isMineDrop) && !tech.isMineSentry + }, + requires: "mines, not sentry", + effect() { + tech.isLaserMine = true; + }, + remove() { + tech.isLaserMine = false; + } + }, + { + name: "mine reclamation", + description: "retrieve ammo from all undetonated mines
and 20% of mines after detonation", + isGunTech: true, + maxCount: 1, + count: 0, + allowed() { + return tech.haveGunCheck("mine") && !tech.isMineSentry + }, + requires: "mine, not sentry", + effect() { + tech.isMineAmmoBack = true; + }, + remove() { + tech.isMineAmmoBack = false; + } + }, + { + name: "sentry", + description: "mines target mobs with nails over time
mines last about 12 seconds", + isGunTech: true, + maxCount: 1, + count: 0, + allowed() { + return (tech.haveGunCheck("mine") || tech.isMineDrop) && !tech.isMineAmmoBack && !tech.isLaserMine + }, + requires: "mines, not mine reclamation, laser-mines", + effect() { + tech.isMineSentry = true; + }, + remove() { + tech.isMineSentry = false; + } + }, + { + name: "mycelial fragmentation", + description: "sporangium release an extra spore
once a second during their growth phase", + isGunTech: true, + maxCount: 1, + count: 0, + allowed() { + return tech.haveGunCheck("spores") + }, + requires: "spores", + effect() { + tech.isSporeGrowth = true + }, + remove() { + tech.isSporeGrowth = false + } + }, + { + name: "tinsellated flagella", + description: "sporangium release 2 more spores
spores accelerate 50% faster", + isGunTech: true, + maxCount: 1, + count: 0, + allowed() { + return tech.haveGunCheck("spores") || tech.sporesOnDeath > 0 || tech.isSporeField + }, + requires: "spores", + effect() { + tech.isFastSpores = true + }, + remove() { + tech.isFastSpores = false + } + }, + { + name: "cryodesiccation", + description: "sporangium release 2 more spores
spores freeze mobs for 1 second", + //
spores do 1/3 damage + isGunTech: true, + maxCount: 1, + count: 0, + allowed() { + return tech.haveGunCheck("spores") || tech.sporesOnDeath > 0 || tech.isSporeField + }, + requires: "spores", + effect() { + tech.isSporeFreeze = true + }, + remove() { + tech.isSporeFreeze = false + } + }, + { + name: "diplochory", + description: "spores use the player for dispersal
until they locate a viable host", + isGunTech: true, + maxCount: 1, + count: 0, + allowed() { + return tech.haveGunCheck("spores") || tech.sporesOnDeath > 0 || tech.isSporeField + }, + requires: "spores", + 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, + allowed() { + return (tech.haveGunCheck("spores") || tech.sporesOnDeath > 0 || tech.isSporeField) && !tech.isEnergyHealth + }, + requires: "spores", + effect() { + tech.isMutualism = true + }, + remove() { + tech.isMutualism = false + } + }, + { + name: "brushless motor", + description: "drones accelerate 50% faster", + isGunTech: true, + maxCount: 1, + count: 0, + allowed() { + return tech.haveGunCheck("drones") || (m.fieldUpgrades[m.fieldMode].name === "nano-scale manufacturing" && !(tech.isSporeField || tech.isMissileField || tech.isIceField)) + }, + requires: "drones", + effect() { + tech.isFastDrones = true + }, + remove() { + tech.isFastDrones = false + } + }, + { + name: "harvester", + description: "after a drone picks up a power up,
it's larger, faster, and very durable", + isGunTech: true, + maxCount: 1, + count: 0, + allowed() { + return !tech.isArmorFromPowerUps && (tech.haveGunCheck("drones") || (m.fieldUpgrades[m.fieldMode].name === "nano-scale manufacturing" && !(tech.isSporeField || tech.isMissileField || tech.isIceField))) + }, + requires: "drones", + effect() { + tech.isDroneGrab = true + }, + remove() { + tech.isDroneGrab = 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, + allowed() { + return tech.haveGunCheck("foam") || tech.foamBotCount > 1 + }, + requires: "foam", + effect() { + tech.isFoamGrowOnDeath = true + }, + remove() { + tech.isFoamGrowOnDeath = false; + } + }, + { + name: "colloidal foam", + description: "foam bubbles dissipate 40% faster
increase foam damage per second by 300%", + isGunTech: true, + maxCount: 1, + count: 0, + allowed() { + return tech.haveGunCheck("foam") || tech.foamBotCount > 2 + }, + requires: "foam", + effect() { + tech.isFastFoam = true + }, + remove() { + tech.isFastFoam = false; + } + }, + { + name: "foam fractionation", + description: "foam gun bubbles are 100% larger
when you have below 300 ammo", + isGunTech: true, + maxCount: 1, + count: 0, + allowed() { + return tech.haveGunCheck("foam") + }, + requires: "foam", + effect() { + tech.isAmmoFoamSize = true + }, + remove() { + tech.isAmmoFoamSize = false; + } + }, + { + name: "quantum foam", + description: "foam gun fires 0.3 seconds into the future
increase foam gun damage by 153%", + isGunTech: true, + maxCount: 9, + count: 0, + allowed() { + return tech.haveGunCheck("foam") + }, + requires: "foam", + effect() { + tech.foamFutureFire++ + }, + remove() { + tech.foamFutureFire = 0; + } + }, + + // { + // name: "foam size", + // description: "increase foam damage by 200%
foam dissipates 50% faster", + // maxCount: 1, + // count: 0, + // allowed() { + // return tech.haveGunCheck("foam") || tech.foamBotCount > 2 + // }, + // requires: "foam", + // effect() { + // tech.isLargeFoam = true + // }, + // remove() { + // tech.isLargeFoam = false; + // } + // }, + // { + // name: "frame-dragging", + // description: "slow time while charging the rail gun
charging no longer drains energy", + // maxCount: 1, + // count: 0, + // allowed() { + // return simulation.fpsCapDefault > 45 && tech.haveGunCheck("rail gun") && !tech.isSlowFPS && !tech.isCapacitor + // }, + // requires: "rail gun and FPS above 45", + // effect() { + // tech.isRailTimeSlow = true; + // }, + // remove() { + // tech.isRailTimeSlow = false; + // simulation.fpsCap = simulation.fpsCapDefault + // simulation.fpsInterval = 1000 / simulation.fpsCap; + // } + // }, + { + name: "half-wave rectifier", + description: "charging the rail gun gives you energy
instead of draining it", + isGunTech: true, + maxCount: 1, + count: 0, + allowed() { + return tech.haveGunCheck("rail gun") + }, + requires: "rail gun", + effect() { + tech.isRailEnergyGain = true; + }, + remove() { + tech.isRailEnergyGain = false; + } + }, + { + name: "dielectric polarization", + description: "firing the rail gun damages nearby mobs", + isGunTech: true, + maxCount: 1, + count: 0, + allowed() { + return tech.haveGunCheck("rail gun") + }, + requires: "rail gun", + effect() { + tech.isRailAreaDamage = true; + }, + remove() { + tech.isRailAreaDamage = false; + } + }, + { + name: "capacitor bank", + description: "the rail gun no longer takes time to charge
rail gun rods are 66% less massive", + isGunTech: true, + maxCount: 1, + count: 0, + allowed() { + return tech.haveGunCheck("rail gun") + }, + requires: "rail gun", + effect() { + tech.isCapacitor = true; + }, + remove() { + tech.isCapacitor = false; + } + }, + { + name: "laser diodes", + description: "all lasers drain 37% less energy
effects laser-gun, laser-bot, and laser-mines", + isGunTech: true, + maxCount: 1, + count: 0, + allowed() { + return tech.haveGunCheck("laser") || tech.laserBotCount > 1 || tech.isLaserMine + }, + requires: "laser", + effect() { + tech.isLaserDiode = 0.63; //100%-37% + }, + remove() { + tech.isLaserDiode = 1; + } + }, + { + name: "relativistic momentum", + description: "all lasers push mobs away
effects laser-gun, laser-bot, and laser-mines", + isGunTech: true, + maxCount: 1, + count: 0, + allowed() { + return tech.haveGunCheck("laser") || tech.laserBotCount > 1 + }, + requires: "laser", + effect() { + tech.isLaserPush = true; + }, + remove() { + tech.isLaserPush = false; + } + }, + + { + name: "specular reflection", + description: "increase damage and energy drain by 50%
and +1 reflection for all lasers (gun, bot, mine)", + isGunTech: true, + maxCount: 9, + count: 0, + allowed() { + return (tech.haveGunCheck("laser") || tech.isLaserMine || tech.laserBotCount > 1) && !tech.isWideLaser && !tech.isPulseLaser && !tech.historyLaser + }, + requires: "laser, not wide beam", + effect() { + tech.laserReflections++; + tech.laserDamage += 0.08; //base is 0.12 + tech.laserFieldDrain += 0.0009 //base is 0.002 + }, + remove() { + tech.laserReflections = 2; + tech.laserDamage = 0.16; + tech.laserFieldDrain = 0.0018; + } + }, + { + name: "diffraction grating", + description: `your laser gains 2 diverging beams
decrease individual beam damage by 10%`, + isGunTech: true, + maxCount: 9, + count: 0, + allowed() { + return tech.haveGunCheck("laser") && !tech.isWideLaser && !tech.isPulseAim && !tech.historyLaser + }, + requires: "laser, not specular reflection", + 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", + description: "laser beam is wider and doesn't reflect
increase full beam damage by 175%", + isGunTech: true, + maxCount: 1, + count: 0, + allowed() { + return tech.haveGunCheck("laser") && tech.laserReflections < 3 && !tech.beamSplitter && !tech.isPulseLaser && !tech.historyLaser + }, + requires: "laser, not specular reflection, diffraction grating, slow light", + 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, + allowed() { + return tech.haveGunCheck("laser") && tech.isWideLaser + }, + requires: "laser, not specular reflection
not diffraction grating", + 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 propagation", + description: "laser beam is spread into your recent past
increase total beam damage by 300%", + isGunTech: true, + maxCount: 9, + count: 0, + allowed() { + return tech.haveGunCheck("laser") && tech.laserReflections < 3 && !tech.beamSplitter && !tech.isPulseLaser && !tech.isWideLaser + }, + requires: "laser, 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: "use 25% of your energy in a pulsed laser
that instantly initiates a fusion explosion", + isGunTech: true, + maxCount: 1, + count: 0, + allowed() { + return tech.haveGunCheck("laser") && tech.laserReflections < 3 && !tech.isWideLaser && !tech.historyLaser + }, + requires: "laser, not specular reflection, not diffuse", + 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: "shock wave", + description: "mobs caught in pulse's explosion are stunned
for up to 2 seconds", + isGunTech: true, + maxCount: 1, + count: 0, + allowed() { + return tech.isPulseLaser + }, + requires: "pulse", + effect() { + tech.isPulseStun = true; + }, + remove() { + tech.isPulseStun = false; + } + }, + { + name: "neocognitron", + description: "pulse automatically aims at a nearby mob
50% decreased delay after firing", + isGunTech: true, + maxCount: 1, + count: 0, + allowed() { + return tech.isPulseLaser && !tech.beamSplitter + }, + requires: "pulse", + effect() { + tech.isPulseAim = true; + }, + remove() { + tech.isPulseAim = false; + } + }, + //************************************************** + //************************************************** field + //************************************************** tech + //************************************************** + { + name: "bremsstrahlung radiation", + description: "blocking with standing wave harmonics
does damage to mobs", + isFieldTech: true, + maxCount: 9, + count: 0, + allowed() { + return m.fieldUpgrades[m.fieldMode].name === "standing wave harmonics" + }, + requires: "standing wave harmonics", + effect() { + tech.blockDmg += 0.75 //if you change this value also update the for loop in the electricity graphics in m.pushMass + }, + remove() { + tech.blockDmg = 0; + } + }, + { + name: "frequency resonance", + description: "standing wave harmonics shield is retuned
increase size and blocking efficiency by 40%", + isFieldTech: true, + maxCount: 9, + count: 0, + allowed() { + return m.fieldUpgrades[m.fieldMode].name === "standing wave harmonics" + }, + requires: "standing wave harmonics", + effect() { + m.fieldRange += 175 * 0.2 + m.fieldShieldingScale *= 0.55 + }, + remove() { + m.fieldRange = 175; + m.fieldShieldingScale = 1; + } + }, + { + name: "flux pinning", + description: "blocking with perfect diamagnetism
stuns mobs for +1 second", + isFieldTech: true, + maxCount: 9, + count: 0, + allowed() { + return m.fieldUpgrades[m.fieldMode].name === "perfect diamagnetism" + }, + requires: "perfect diamagnetism", + effect() { + tech.isStunField += 60; + }, + remove() { + tech.isStunField = 0; + } + }, + { + name: "eddy current brake", + description: "your stored energy projects a field that
limits the top speed of mobs", + isFieldTech: true, + maxCount: 1, + count: 0, + allowed() { + return m.fieldUpgrades[m.fieldMode].name === "perfect diamagnetism" + }, + requires: "perfect diamagnetism", + effect() { + tech.isPerfectBrake = true; + }, + remove() { + tech.isPerfectBrake = false; + } + }, + { + name: "fracture analysis", + description: "bullet impacts do 400% damage
to stunned mobs", + isFieldTech: true, + maxCount: 1, + count: 0, + allowed() { + return tech.isStunField || tech.oneSuperBall || tech.isCloakStun || tech.orbitBotCount > 1 || tech.isPerpetualStun + }, + requires: "a stun effect", + effect() { + tech.isCrit = true; + }, + remove() { + tech.isCrit = false; + } + }, + { + name: "pair production", + description: "picking up a power up gives you 250 energy", + isFieldTech: true, + maxCount: 1, + count: 0, + allowed() { + return m.fieldUpgrades[m.fieldMode].name === "nano-scale manufacturing" || m.fieldUpgrades[m.fieldMode].name === "pilot wave" + }, + requires: "nano-scale manufacturing", + effect: () => { + tech.isMassEnergy = true // used in m.grabPowerUp + m.energy += 3 + }, + remove() { + tech.isMassEnergy = false; + } + }, + { + name: "bot manufacturing", + description: "use nano-scale manufacturing
to build 3 random bots", + isFieldTech: true, + maxCount: 1, + count: 0, + isNonRefundable: true, + isExperimentHide: true, + allowed() { + return m.fieldUpgrades[m.fieldMode].name === "nano-scale manufacturing" + }, + requires: "nano-scale manufacturing", + effect: () => { + m.energy = 0.01; + b.randomBot() + b.randomBot() + b.randomBot() + }, + remove() {} + }, + { + name: "bot prototypes", + description: "use nano-scale manufacturing to upgrade
all bots of a random type and build 2 of that bot", + isFieldTech: true, + maxCount: 1, + count: 0, + isNonRefundable: true, + isExperimentHide: true, + allowed() { + return m.fieldUpgrades[m.fieldMode].name === "nano-scale manufacturing" && !(tech.isNailBotUpgrade && tech.isFoamBotUpgrade && tech.isBoomBotUpgrade && tech.isLaserBotUpgrade && tech.isOrbitBotUpgrade) + }, + requires: "nano-scale manufacturing", + effect: () => { + m.energy = 0.01; + //fill array of available bots + const notUpgradedBots = [] + if (!tech.isNailBotUpgrade) notUpgradedBots.push(() => { + tech.giveTech("nail-bot upgrade") + tech.setTechoNonRefundable("nail-bot upgrade") + for (let i = 0; i < 2; i++) { + b.nailBot() + tech.nailBotCount++; + } + simulation.makeTextLog(`tech.isNailBotUpgrade = true`) + }) + if (!tech.isFoamBotUpgrade) notUpgradedBots.push(() => { + tech.giveTech("foam-bot upgrade") + tech.setTechoNonRefundable("foam-bot upgrade") + for (let i = 0; i < 2; i++) { + b.foamBot() + tech.foamBotCount++; + } + simulation.makeTextLog(`tech.isFoamBotUpgrade = true`) + }) + if (!tech.isBoomBotUpgrade) notUpgradedBots.push(() => { + tech.giveTech("boom-bot upgrade") + tech.setTechoNonRefundable("boom-bot upgrade") + for (let i = 0; i < 2; i++) { + b.boomBot() + tech.boomBotCount++; + } + simulation.makeTextLog(`tech.isBoomBotUpgrade = true`) + }) + if (!tech.isLaserBotUpgrade) notUpgradedBots.push(() => { + tech.giveTech("laser-bot upgrade") + tech.setTechoNonRefundable("laser-bot upgrade") + for (let i = 0; i < 2; i++) { + b.laserBot() + tech.laserBotCount++; + } + simulation.makeTextLog(`tech.isLaserBotUpgrade = true`) + }) + if (!tech.isOrbitBotUpgrade) notUpgradedBots.push(() => { + tech.giveTech("orbital-bot upgrade") + tech.setTechoNonRefundable("orbital-bot upgrade") + for (let i = 0; i < 2; i++) { + b.orbitBot() + tech.orbitBotCount++; + } + simulation.makeTextLog(`tech.isOrbitalBotUpgrade = true`) + }) + if (!tech.isDynamoBotUpgrade) notUpgradedBots.push(() => { + tech.giveTech("dynamo-bot upgrade") + tech.setTechoNonRefundable("dynamo-bot upgrade") + for (let i = 0; i < 2; i++) { + b.orbitBot() + tech.dynamoBotCount++; + } + simulation.makeTextLog(`tech.isDynamoBotUpgrade = true`) + }) + //double chance for dynamo-bot, since it's very good for nano-scale + if (!tech.isDynamoBotUpgrade) notUpgradedBots.push(() => { + tech.giveTech("dynamo-bot upgrade") + tech.setTechoNonRefundable("dynamo-bot upgrade") + for (let i = 0; i < 2; i++) { + b.orbitBot() + tech.dynamoBotCount++; + } + simulation.makeTextLog(`tech.isDynamoBotUpgrade = true`) + }) + //choose random function from the array and run it + notUpgradedBots[Math.floor(Math.random() * notUpgradedBots.length)]() + }, + remove() {} + }, + { + name: "mycelium manufacturing", + description: "nano-scale manufacturing is repurposed
excess energy used to grow spores", + isFieldTech: true, + maxCount: 1, + count: 0, + allowed() { + return m.maxEnergy > 0.99 && m.fieldUpgrades[m.fieldMode].name === "nano-scale manufacturing" && !(tech.isMissileField || tech.isIceField || tech.isFastDrones || tech.isDroneGrab) + }, + requires: "nano-scale manufacturing", + effect() { + tech.isSporeField = true; + }, + remove() { + tech.isSporeField = false; + } + }, + { + name: "missile manufacturing", + description: "nano-scale manufacturing is repurposed
excess energy used to construct missiles", + isFieldTech: true, + maxCount: 1, + count: 0, + allowed() { + return m.maxEnergy > 0.5 && m.fieldUpgrades[m.fieldMode].name === "nano-scale manufacturing" && !(tech.isSporeField || tech.isIceField || tech.isFastDrones || tech.isDroneGrab) + }, + requires: "nano-scale manufacturing", + effect() { + tech.isMissileField = true; + }, + remove() { + tech.isMissileField = false; + } + }, + { + name: "ice IX manufacturing", + description: "nano-scale manufacturing is repurposed
excess energy used to synthesize ice IX", + isFieldTech: true, + maxCount: 1, + count: 0, + allowed() { + return m.fieldUpgrades[m.fieldMode].name === "nano-scale manufacturing" && !(tech.isSporeField || tech.isMissileField || tech.isFastDrones || tech.isDroneGrab) + }, + requires: "nano-scale manufacturing", + effect() { + tech.isIceField = true; + }, + remove() { + tech.isIceField = false; + } + }, + { + name: "thermoelectric effect", + description: "killing mobs with ice IX gives 4 health
and 80 energy", + isFieldTech: true, + maxCount: 9, + count: 0, + allowed() { + return tech.isIceField + }, + requires: "ice IX", + effect() { + tech.iceEnergy++ + }, + remove() { + tech.iceEnergy = 0; + } + }, + { + name: "degenerate matter", + description: "reduce harm by 50%
while negative mass field is active", + isFieldTech: true, + maxCount: 1, + count: 0, + allowed() { + return m.fieldUpgrades[m.fieldMode].name === "negative mass field" + }, + requires: "negative mass field", + effect() { + tech.isHarmReduce = true + }, + remove() { + tech.isHarmReduce = false; + } + }, + { + name: "annihilation", + description: "after touching mobs, they are annihilated
drains 33% of maximum energy", + isFieldTech: true, + maxCount: 1, + count: 0, + allowed() { + return m.fieldUpgrades[m.fieldMode].name === "negative mass field" || m.fieldUpgrades[m.fieldMode].name === "pilot wave" + }, + requires: "negative mass field", + effect() { + tech.isAnnihilation = true + }, + remove() { + tech.isAnnihilation = false; + } + }, + { + name: "Bose Einstein condensate", + description: "mobs inside your field are frozen
pilot wave, negative mass, time dilation", + isFieldTech: true, + maxCount: 1, + count: 0, + allowed() { + return m.fieldUpgrades[m.fieldMode].name === "pilot wave" || m.fieldUpgrades[m.fieldMode].name === "negative mass field" || m.fieldUpgrades[m.fieldMode].name === "time dilation field" + }, + requires: "pilot wave, negative mass field, time dilation field", + effect() { + tech.isFreezeMobs = true + }, + remove() { + tech.isFreezeMobs = false + } + }, + // { + // name: "thermal reservoir", + // description: "increase your plasma damage by 100%
plasma temporarily lowers health not energy", + // isFieldTech: true, + // maxCount: 1, + // count: 0, + // 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: "plasma jet", + description: "increase plasma torch's range by 27%", + isFieldTech: true, + maxCount: 9, + count: 0, + allowed() { + return m.fieldUpgrades[m.fieldMode].name === "plasma torch" + }, + requires: "plasma torch", + effect() { + tech.isPlasmaRange += 0.27; + }, + remove() { + tech.isPlasmaRange = 1; + } + }, + { + name: "plasma-bot", + description: "a bot uses energy to emit plasma
that damages and pushes mobs", + isFieldTech: true, + maxCount: 1, + count: 0, + allowed() { + return m.fieldUpgrades[m.fieldMode].name === "plasma torch" + }, + requires: "plasma torch", + effect() { + tech.plasmaBotCount++; + b.plasmaBot(); + }, + remove() { + tech.plasmaBotCount = 0; + } + }, + { + name: "micro-extruder", + description: "plasma torch extrudes a thin hot wire
increases damage, and energy drain", + isFieldTech: true, + maxCount: 1, + count: 0, + allowed() { + return m.fieldUpgrades[m.fieldMode].name === "plasma torch" + }, + requires: "plasma torch", + effect() { + tech.isExtruder = true; + }, + remove() { + tech.isExtruder = false; + } + }, + { + name: "timelike world line", + description: "time dilation doubles your relative time rate
and makes you immune to harm", + isFieldTech: true, + maxCount: 1, + count: 0, + allowed() { + return m.fieldUpgrades[m.fieldMode].name === "time dilation field" + }, + requires: "time dilation field", + effect() { + tech.isTimeSkip = true; + b.setFireCD(); + }, + remove() { + tech.isTimeSkip = false; + b.setFireCD(); + } + }, + { + name: "Lorentz transformation", + description: "permanently increase your relative time rate
move, jump, and shoot 40% faster", + isFieldTech: true, + maxCount: 1, + count: 0, + allowed() { + return m.fieldUpgrades[m.fieldMode].name === "time dilation field" || m.fieldUpgrades[m.fieldMode].name === "pilot wave" + }, + requires: "time dilation field", + effect() { + tech.fastTime = 1.40; + tech.fastTimeJump = 1.11; + m.setMovement(); + b.setFireCD(); + }, + remove() { + tech.fastTime = 1; + tech.fastTimeJump = 1; + m.setMovement(); + b.setFireCD(); + } + }, + { + name: "time crystals", + description: "quadruple your default energy regeneration", + isFieldTech: true, + maxCount: 1, + count: 0, + allowed() { + return (m.fieldUpgrades[m.fieldMode].name === "time dilation field" || m.fieldUpgrades[m.fieldMode].name === "pilot wave") && tech.energyRegen !== 0; + }, + requires: "time dilation field", + effect: () => { + tech.energyRegen = 0.004; + m.fieldRegen = tech.energyRegen; + }, + remove() { + tech.energyRegen = 0.001; + m.fieldRegen = tech.energyRegen; + } + }, + { + name: "phase decoherence", + description: "intangible to blocks and mobs while cloaked
passing through mobs drains your energy", + isFieldTech: true, + maxCount: 1, + count: 0, + allowed() { + return m.fieldUpgrades[m.fieldMode].name === "metamaterial cloaking" + }, + requires: "metamaterial cloaking", + effect() { + tech.isIntangible = true; + }, + remove() { + tech.isIntangible = false; + } + }, + { + name: "dazzler", + description: "decloaking stuns nearby mobs
drains 30% of your stored energy", + isFieldTech: true, + maxCount: 1, + count: 0, + allowed() { + return m.fieldUpgrades[m.fieldMode].name === "metamaterial cloaking" + }, + requires: "metamaterial cloaking", + effect() { + tech.isCloakStun = true; + }, + remove() { + tech.isCloakStun = false; + } + }, + { + name: "discrete optimization", + description: "increase damage by 66%
50% increased delay after firing", + isFieldTech: true, + maxCount: 1, + count: 0, + allowed() { + return m.fieldUpgrades[m.fieldMode].name === "metamaterial cloaking" || m.fieldUpgrades[m.fieldMode].name === "pilot wave" + }, + requires: "metamaterial cloaking", + effect() { + tech.aimDamage = 1.66 + b.setFireCD(); + }, + remove() { + tech.aimDamage = 1 + b.setFireCD(); + } + }, + { + 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, + allowed() { + return m.fieldUpgrades[m.fieldMode].name === "wormhole" + }, + requires: "wormhole", + effect() { + tech.isWormholeDamage = true + }, + remove() { + tech.isWormholeDamage = false + } + }, + { + name: "Penrose process", + description: "after a block falls into a wormhole
you gain 50 energy", + isFieldTech: true, + maxCount: 1, + count: 0, + allowed() { + return m.fieldUpgrades[m.fieldMode].name === "wormhole" + }, + requires: "wormhole", + effect() { + tech.isWormholeEnergy = true + }, + remove() { + tech.isWormholeEnergy = false + } + }, + { + name: "transdimensional spores", + description: "when blocks fall into a wormhole
higher dimension spores are summoned", + isFieldTech: true, + maxCount: 1, + count: 0, + allowed() { + return m.fieldUpgrades[m.fieldMode].name === "wormhole" + }, + requires: "wormhole", + effect() { + tech.isWormSpores = true + }, + remove() { + tech.isWormSpores = false + } + }, + { + name: "traversable geodesics", + description: "your bullets can traverse wormholes
spawn a gun and ammo", + isFieldTech: true, + maxCount: 1, + count: 0, + allowed() { + return m.fieldUpgrades[m.fieldMode].name === "wormhole" + }, + requires: "wormhole", + effect() { + tech.isWormBullets = true + powerUps.spawn(m.pos.x, m.pos.y, "gun"); + powerUps.spawn(m.pos.x, m.pos.y, "ammo"); + }, + remove() { + tech.isWormBullets = false + } + }, + //************************************************** + //************************************************** spawn power up + //************************************************** tech + //************************************************** + { + name: "heals", + description: "spawn 6 heals", + maxCount: 9, + count: 0, + isNonRefundable: true, + isExperimentHide: true, + allowed() { + return true + }, + requires: "", + effect() { + for (let i = 0; i < 6; i++) powerUps.spawn(m.pos.x, m.pos.y, "heal"); + this.count-- + }, + remove() {} + }, + { + name: "ammo", + description: "spawn 6 ammo", + maxCount: 9, + count: 0, + isNonRefundable: true, + isExperimentHide: true, + allowed() { + return !tech.isEnergyNoAmmo + }, + requires: "not exciton lattice", + effect() { + for (let i = 0; i < 6; i++) powerUps.spawn(m.pos.x, m.pos.y, "ammo"); + this.count-- + }, + remove() {} + }, + { + name: "research", + description: "spawn 4 research", + maxCount: 9, + count: 0, + isNonRefundable: true, + isExperimentHide: true, + allowed() { + return !tech.isSuperDeterminism + }, + requires: "not superdeterminism", + effect() { + for (let i = 0; i < 4; i++) powerUps.spawn(m.pos.x, m.pos.y, "research"); + this.count-- + }, + remove() {} + }, + { + name: "gun", + description: "spawn a gun", + maxCount: 9, + count: 0, + isNonRefundable: true, + isExperimentHide: true, + allowed() { + return !tech.isSuperDeterminism + }, + requires: "not superdeterminism", + effect() { + powerUps.spawn(m.pos.x, m.pos.y, "gun"); + this.count-- + }, + remove() {} + }, + { + name: "field", + description: "spawn a field", + maxCount: 9, + count: 0, + isNonRefundable: true, + isExperimentHide: true, + allowed() { + return !tech.isSuperDeterminism + }, + requires: "not superdeterminism", + effect() { + powerUps.spawn(m.pos.x, m.pos.y, "field"); + this.count-- + }, + remove() {} + }, + { + name: "ship", + description: "experimental mode: fly around with no legs", + maxCount: 1, + count: 0, + isNonRefundable: true, + allowed() { + return !m.isShipMode && m.fieldUpgrades[m.fieldMode].name !== "negative mass field" && build.isExperimentSelection + }, + requires: "", + effect() { + m.shipMode() + }, + remove() {} + }, + ], + addLoreTechToPool() { //adds lore tech to tech pool + if (!simulation.isCheating) { + tech.tech.push({ + name: `undefined`, + description: `${lore.techCount+1}/10
add copies of this to the potential tech pool`, + maxCount: 1, + count: 0, + 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 > 9) { + tech.removeLoreTechFromPool(); + } 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}/10
add copies of this to the potential tech pool` + } + for (let i = 0, len = 10; i < len; i++) tech.addLoreTechToPool() + } + }, 1); + }, + remove() {} + }) + } }, - ], - //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, - superBallNumber: null, - oneSuperBall: null, - laserReflections: null, - laserDamage: null, - laserFieldDrain: null, - isAmmoFromHealth: null, - mobSpawnWithHealth: null, - isEnergyRecovery: null, - isHealthRecovery: null, - isEnergyLoss: null, - isDeathAvoid: null, - isDeathAvoidedThisLevel: null, - waveSpeedMap: null, - waveSpeedBody: null, - isSporeField: null, - isMissileField: null, - isIceField: null, - isMineAmmoBack: null, - isPlasmaRange: null, - isFreezeMobs: null, - isIceCrystals: null, - throwChargeRate: null, - isBlockStun: null, - isStunField: null, - isHarmDamage: null, - energyRegen: null, - isVacuumBomb: null, - renormalization: null, - fragments: null, - isEnergyDamage: null, - isBotSpawner: null, - waveHelix: null, - isSporeFollow: null, - isNailRadiation: null, - isEnergyHealth: null, - isPulseStun: null, - restDamage: null, - isRPG: null, - missileCount: null, - isDeterminism: null, - isSuperDeterminism: null, - isHarmReduce: null, - nailsDeathMob: null, - isSlowFPS: null, - isNeutronStun: null, - manyWorlds: null, - isDamageFromBulletCount: null, - isLaserDiode: null, - isNailShot: null, - slowFire: null, - fastTime: null, - squirrelJump: null, - fastTimeJump: null, - isFastRadiation: null, - isArmorFromPowerUps: 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, - isRailTimeSlow: null, - isNailBotUpgrade: null, - isFoamBotUpgrade: null, - isLaserBotUpgrade: null, - isBoomBotUpgrade: null, - isOrbitBotUpgrade: null, - isDroneGrab: null, - isOneGun: null, - isDamageForGuns: null, - isGunCycle: null, - isFastFoam: null, - isSporeGrowth: null, - isBayesian: null, - nailGun: null, - nailInstantFireRate: null, - isCapacitor: null, - isEnergyNoAmmo: null, - isFreezeHarmImmune: null, - isSmallExplosion: null, - isExplosionHarm: null, - armorFromPowerUps: null, - bonusHealth: null, - isIntangible: null, - isCloakStun: null, - bonusEnergy: null, - healGiveMaxEnergy: null, - healMaxEnergyBonus: 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, - isWormSpores: 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, - isPerpetualReroll: null, - isPerpetualAmmo: null, - isPerpetualHeal: null, - isPerpetualStun: null, - isCancelDuplication: null, - cancelCount: null, - isCancelRerolls: null, - isBotDamage: null, - isBanish: null, - isMaxEnergyTech: null, - isLowEnergyDamage: null, - isRewindBot: null, - isRewindGrenade: null, - isExtruder: null, - isEndLevelPowerUp: null, - isRewindGun: null, - missileSize: null, - isLaserMine: null, - isAmmoFoamSize: null, - isIceIX: null, - isDupDamage: null, - isFireRateForGuns: null, - cyclicImmunity: null, - isTechDamage: null, - isFireNotMove: null, - isRestHarm: null, - isFireMoveLock: null, - isRivets: null, - isNeedles: null, - isExplodeRadio: null, - isGunSwitchField: null, - isNeedleShieldPierce: null, - isDuplicateBoss: null, - is100Duplicate: null, - isDynamoBotUpgrade: null, - isBlockPowerUps: null, - isBlockHarm: null, - foamFutureFire: null -} \ No newline at end of file + junk: [ + // { + // name: "junk", + // description: "", + // maxCount: 9, + // count: 0, + // numberInPool: 0, + // isNonRefundable: true, + // isExperimentHide: true, + // isJunk: true, + // allowed() { + // return true + // }, + // requires: "", + // effect() { + + // }, + // remove() {} + // }, + { + name: "banish", + description: "erase all junk tech from the possible pool
probably...", + maxCount: 1, + count: 0, + numberInPool: 0, + isNonRefundable: true, + isExperimentHide: true, + isJunk: true, + allowed() { + return true + }, + requires: "", + effect() { + setTimeout(() => { //have to wait a sec so that the tech that is this doesn't get removed before it is done running + for (let i = tech.tech.length - 1; i > 0; i--) { + if (tech.tech[i].isJunk && tech.tech[i] !== this) tech.tech.splice(i, 1) + } + }, 1000); + }, + remove() {} + }, + { + name: "ship", + description: "fly around with no legs
reduce combat difficulty by 1 level", + maxCount: 1, + count: 0, + numberInPool: 0, + isNonRefundable: true, + isExperimentHide: true, + isJunk: true, + allowed() { + return !m.isShipMode && m.fieldUpgrades[m.fieldMode].name !== "negative mass field" + }, + requires: "", + effect() { + m.shipMode() + level.difficultyDecrease(simulation.difficultyMode) + }, + remove() {} + }, + { + name: "lubrication", + description: "reduce block density and friction for this level", + maxCount: 9, + count: 0, + numberInPool: 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, + numberInPool: 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, + numberInPool: 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, + numberInPool: 0, + isNonRefundable: true, + isExperimentHide: true, + isJunk: true, + allowed() { + return m.fieldUpgrades[m.fieldMode].name !== "negative mass field" + }, + requires: "", + effect() { + ctx.globalCompositeOperation = "lighter"; + }, + remove() {} + }, + { + name: "rewind", + description: "every 5 seconds rewind 2 seconds
lasts 120 seconds", + maxCount: 9, + count: 0, + numberInPool: 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, + numberInPool: 0, + isNonRefundable: true, + isExperimentHide: true, + isJunk: true, + allowed() { + return true + }, + requires: "", + effect() { + for (let i = 0, len = Math.floor(m.energy * 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"; + World.add(engine.world, body[index]); //add to world + }, i * 100); + } + + }, + remove() {} + }, + { + name: "level.nextLevel()", + description: "advance to the next level", + maxCount: 9, + count: 0, + numberInPool: 0, + isNonRefundable: true, + isExperimentHide: true, + isJunk: true, + allowed() { + return true + }, + requires: "", + effect() { + level.nextLevel(); + }, + remove() {} + }, + { + name: "expert system", + description: "spawn a tech power up
add 64 junk tech to the potential pool", + maxCount: 9, + count: 0, + numberInPool: 0, + isNonRefundable: true, + isExperimentHide: true, + isJunk: true, + allowed() { + return true + }, + requires: "", + effect() { + powerUps.spawn(m.pos.x, m.pos.y, "tech"); + tech.addJunkTechToPool(64) + }, + remove() {} + }, + { + name: "energy investment", + description: "every 10 seconds drain your energy and return it doubled 10 seconds later
lasts 180 seconds", + maxCount: 9, + count: 0, + numberInPool: 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, + numberInPool: 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, + numberInPool: 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, + numberInPool: 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, + numberInPool: 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); + let grd = ctx.createLinearGradient(-30, 0, 30, 0); + grd.addColorStop(0, m.fillColorDark); + grd.addColorStop(1, m.fillColor); + ctx.fillStyle = grd; + 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, + numberInPool: 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); + let grd = ctx.createLinearGradient(-30, 0, 30, 0); + grd.addColorStop(0, m.fillColorDark); + grd.addColorStop(1, m.fillColor); + ctx.fillStyle = grd; + 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, + numberInPool: 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); + let grd = ctx.createLinearGradient(-30, 0, 30, 0); + grd.addColorStop(0, m.fillColorDark); + grd.addColorStop(1, m.fillColor); + ctx.fillStyle = grd; + 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 = grd; + 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, + numberInPool: 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, + numberInPool: 0, + isNonRefundable: true, + isExperimentHide: true, + isJunk: true, + allowed() { + return tech.totalBots() > 2 + }, + requires: "at least 3 bots", + effect() { + const total = tech.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, + numberInPool: 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, + numberInPool: 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: "eject all your guns", + maxCount: 9, + count: 0, + numberInPool: 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 research", + maxCount: 9, + count: 0, + numberInPool: 0, + isNonRefundable: true, + isExperimentHide: true, + isJunk: true, + allowed() { + return powerUps.research.count > 3 + }, + requires: "at least 4 research", + effect() { + for (let i = 0; i < powerUps.research.count; i++) powerUps.spawn(m.pos.x + 60 * (Math.random() - 0.5), m.pos.y + 60 * (Math.random() - 0.5), "research"); + powerUps.research.count = 0 + }, + remove() {} + }, + { + name: "quantum black hole", + description: "use all your energy to spawn inside the event horizon of a huge black hole", + maxCount: 9, + count: 0, + numberInPool: 0, + isNonRefundable: true, + isExperimentHide: true, + isJunk: true, + allowed() { + return true + }, + requires: "", + effect() { + m.energy = 0 + spawn.suckerBoss(m.pos.x, m.pos.y - 1000) + }, + remove() {} + }, + { + name: "black hole cluster", + description: "spawn 2 research
spawn 40 nearby black holes", + maxCount: 9, + count: 0, + numberInPool: 0, + isNonRefundable: true, + isExperimentHide: true, + isJunk: true, + allowed() { + return true + }, + requires: "", + effect() { + for (let i = 0; i < 2; i++) powerUps.spawn(m.pos.x, m.pos.y, "research"); + const unit = { + x: 1, + y: 0 + } + for (let i = 0; i < 40; i++) { + const where = Vector.add(m.pos, Vector.mult(Vector.rotate(unit, Math.random() * 2 * Math.PI), 600 + 800 * Math.random())) + spawn.sucker(where.x, where.y) + } + }, + remove() {} + }, + ], + //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, + superBallNumber: null, + oneSuperBall: null, + laserReflections: null, + laserDamage: null, + laserFieldDrain: null, + isAmmoFromHealth: null, + mobSpawnWithHealth: null, + isEnergyRecovery: null, + isHealthRecovery: null, + isEnergyLoss: null, + isDeathAvoid: null, + isDeathAvoidedThisLevel: null, + waveSpeedMap: null, + waveSpeedBody: null, + isSporeField: null, + isMissileField: null, + isIceField: null, + isMineAmmoBack: null, + isPlasmaRange: null, + isFreezeMobs: null, + isIceCrystals: null, + throwChargeRate: null, + isBlockStun: null, + isStunField: null, + isHarmDamage: null, + energyRegen: null, + isVacuumBomb: null, + renormalization: null, + fragments: null, + isEnergyDamage: null, + isBotSpawner: null, + waveHelix: null, + isSporeFollow: null, + isNailRadiation: null, + isEnergyHealth: null, + isPulseStun: null, + restDamage: null, + isRPG: null, + missileCount: null, + isDeterminism: null, + isSuperDeterminism: null, + isHarmReduce: null, + nailsDeathMob: null, + isSlowFPS: null, + isNeutronStun: null, + manyWorlds: null, + isDamageFromBulletCount: null, + isLaserDiode: null, + isNailShot: null, + slowFire: null, + fastTime: null, + squirrelJump: null, + fastTimeJump: null, + isFastRadiation: null, + isArmorFromPowerUps: 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, + isRailTimeSlow: null, + isNailBotUpgrade: null, + isFoamBotUpgrade: null, + isLaserBotUpgrade: null, + isBoomBotUpgrade: null, + isOrbitBotUpgrade: null, + isDroneGrab: null, + isOneGun: null, + isDamageForGuns: null, + isGunCycle: null, + isFastFoam: null, + isSporeGrowth: null, + isBayesian: null, + nailGun: null, + nailInstantFireRate: null, + isCapacitor: null, + isEnergyNoAmmo: null, + isFreezeHarmImmune: null, + isSmallExplosion: null, + isExplosionHarm: null, + armorFromPowerUps: null, + bonusHealth: null, + isIntangible: null, + isCloakStun: null, + bonusEnergy: null, + healGiveMaxEnergy: null, + healMaxEnergyBonus: 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, + isWormSpores: 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, + isPerpetualReroll: null, + isPerpetualAmmo: null, + isPerpetualHeal: null, + isPerpetualStun: null, + isCancelDuplication: null, + cancelCount: null, + isCancelRerolls: null, + isBotDamage: null, + isBanish: null, + isMaxEnergyTech: null, + isLowEnergyDamage: null, + isRewindBot: null, + isRewindGrenade: null, + isExtruder: null, + isEndLevelPowerUp: null, + isRewindGun: null, + missileSize: null, + isLaserMine: null, + isAmmoFoamSize: null, + isIceIX: null, + isDupDamage: null, + isFireRateForGuns: null, + cyclicImmunity: null, + isTechDamage: null, + isFireNotMove: null, + isRestHarm: null, + isFireMoveLock: null, + isRivets: null, + isNeedles: null, + isExplodeRadio: null, + isGunSwitchField: null, + isNeedleShieldPierce: null, + isDuplicateBoss: null, + is100Duplicate: null, + isDynamoBotUpgrade: null, + isBlockPowerUps: null, + isBlockHarm: null, + foamFutureFire: null, + isBotSwap: null, + botSwapCycleIndex: null + } \ No newline at end of file diff --git a/style.css b/style.css index 8ff6cf7..798ac2d 100644 --- a/style.css +++ b/style.css @@ -552,6 +552,20 @@ summary { /* text-decoration: underline; */ } +.color-bot { + /* background-color: #eee; */ + /* color: #555; */ + /* letter-spacing: -1.5px; */ + /* text-transform: uppercase; */ + /* font-variant: all-caps; */ + /* text-decoration: underline solid; */ + /* border: 1px solid #000; */ + /* padding: 10px; */ + /* border-radius: 10%; */ + /* font-family: Lucida Console, Courier, monospace; */ + /* color: #777; */ +} + .color-cloaked { opacity: 0.25; letter-spacing: 1px; diff --git a/todo.txt b/todo.txt index f7c6f6c..0e7fb96 100644 --- a/todo.txt +++ b/todo.txt @@ -1,8 +1,12 @@ ******************************************************** NEXT PATCH ******************************************************** -added 2 new testing keys: J = clear mobs, H = infinity health -added junk tech: ship (it's hard to control, but you get better with practice) - m.shipMode() in console +ship mode can be found in the experimental menu + +some mobs now have orbitals at random +new level boss: orbitalBoss + +most late game bot tech has been buffed +tech: get 2 random bots, also when you switch guns cycle all bots to the same type ******************************************************** BUGS ******************************************************** @@ -32,14 +36,16 @@ use the floor of portal sensor on the player? to unstuck player ******************************************************** TODO ******************************************************** +add a shipMode tech that only shows up in experimental mode + +tech: spawn a bot after taking collision damage + +tech: standing wave freezes the mobs it hits + tech: health becomes drones requires mass-energy? junk tech? -tech: 1/3 of the time foam fires 1 extra backwards foam - -make a secret only accessible to the ship (very small + flying) - map: laboratory rooms with switches that change physics gravity room @@ -66,7 +72,6 @@ copy time-like foam to other guns? shotgun nail gun -tech: when you switch guns switch all bots to a different bot lore: a tutorial / lore intro needs to be optional so it doesn't slow experienced players @@ -360,7 +365,7 @@ n-gon outreach ideas ******************************************************** LORE ******************************************************** cool names for tech - strange loop, ansatz, Bayesian statistics + strange loop, ansatz, perturbation theory voice singing with pitch?