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?