if a mob has died in the last 5 seconds inhibit
passive energy regen and increase damage 50%",
maxCount: 1,
count: 0,
frequency: 1,
frequencyDefault: 1,
allowed() {
return !tech.isCrouchRegen
},
requires: "not inductive coupling",
effect() {
tech.isDamageAfterKillNoRegen = true;
m.regenEnergy = function() {
if (m.immuneCycle < m.cycle && (m.lastKillCycle + 300 < m.cycle)) m.energy += m.fieldRegen; //m.fieldRegen = 0.001
if (m.energy < 0) m.energy = 0
}
},
remove() {
if (this.count) m.regenEnergy = m.regenEnergyDefault
tech.isDamageAfterKillNoRegen = false;
}
},
{
name: "torpor",
description: "if a mob has not died in the last 5 seconds
reduce harm by 66%",
maxCount: 1,
count: 0,
frequency: 1,
frequencyDefault: 1,
allowed() {
return !tech.isEnergyHealth
},
requires: "not mass-energy",
effect() {
tech.isHarmReduceNoKill = true;
},
remove() {
tech.isHarmReduceNoKill = false;
}
},
{
name: "Zeno's paradox",
description: "reduce harm by 85%, but every 5 seconds
remove 6% of your current health",
// description: "every 5 seconds remove 1/10 of your health
reduce harm by 90%",
maxCount: 1,
count: 0,
frequency: 1,
frequencyDefault: 1,
allowed() { return !tech.isEnergyHealth },
requires: "not mass-energy",
effect() {
tech.isZeno = true;
},
remove() {
tech.isZeno = false;
}
},
{
name: "negative feedback",
description: "increase damage by 5%
for every 10 health below 100",
maxCount: 1,
count: 0,
frequency: 1,
frequencyDefault: 1,
allowed() {
return m.health < 0.6 || build.isExperimentSelection
},
requires: "health below 60",
effect() {
tech.isLowHealthDmg = true; //used in mob.damage()
},
remove() {
tech.isLowHealthDmg = false;
}
},
{
name: "antiscience",
description: "increase damage by 90%
lose 11 health when you pick up a tech",
maxCount: 1,
count: 0,
frequency: 1,
frequencyDefault: 1,
allowed() {
return !tech.isEnergyHealth
},
requires: "not mass-energy",
effect() {
tech.isTechDamage = true;
},
remove() {
tech.isTechDamage = false;
}
},
{
name: "fluoroantimonic acid",
description: "increase damage by 35%
when your health is above 100",
maxCount: 1,
count: 0,
frequency: 2,
frequencyDefault: 2,
allowed() {
return m.maxHealth > 1;
},
requires: "max health above 100",
effect() {
tech.isAcidDmg = true;
},
remove() {
tech.isAcidDmg = false;
}
},
{
name: "tungsten carbide",
description: "increase your maximum health by 100
landings that force you to crouch cause harm",
maxCount: 1,
count: 0,
frequency: 1,
frequencyDefault: 1,
allowed() {
return !tech.isEnergyHealth
},
requires: "not mass-energy equivalence",
effect() {
tech.isFallingDamage = true;
m.setMaxHealth();
m.addHealth(1 / simulation.healScale)
},
remove() {
tech.isFallingDamage = false;
m.setMaxHealth();
}
},
{
name: "quenching",
description: `over healing from ${powerUps.orb.heal()} does harm
but it also increase your maximum health`,
maxCount: 1,
count: 0,
frequency: 1,
frequencyDefault: 1,
allowed() {
return !tech.isEnergyHealth && !tech.isNoHeals
},
requires: "not mass-energy equivalence, ergodicity",
effect() {
tech.isOverHeal = true;
},
remove() {
tech.isOverHeal = false;
}
},
{
name: "negative entropy",
description: `at the start of each level
spawn ${powerUps.orb.heal()} for every 26 missing health`,
maxCount: 1,
count: 0,
frequency: 1,
frequencyDefault: 1,
isHealTech: true,
allowed() {
return m.health > 0.1 && !tech.isNoHeals
},
requires: "has some health, not ergodicity",
effect() {
tech.isHealLowHealth = true;
},
remove() {
tech.isHealLowHealth = false;
}
},
{
name: "adiabatic healing",
description: `${powerUps.orb.heal()} are 100% more effective
+5% JUNK to the potential tech pool`,
maxCount: 3,
count: 0,
frequency: 1,
frequencyDefault: 1,
isHealTech: true,
allowed() {
return ((m.health / m.maxHealth) < 0.7 || build.isExperimentSelection) && !tech.isEnergyHealth && !tech.isNoHeals
},
requires: "under 70% health, not mass-energy equivalence, ergodicity",
effect() {
tech.largerHeals++;
this.refundAmount += tech.addJunkTechToPool(0.05)
//update current heals
for (let i = 0; i < powerUp.length; i++) {
if (powerUp[i].name === "heal") powerUp[i].size = powerUps.heal.size()
}
},
refundAmount: 0,
remove() {
tech.largerHeals = 1;
if (this.count > 0 && this.refundAmount > 0) {
tech.removeJunkTechFromPool(this.refundAmount)
this.refundAmount = 0
}
}
},
{
name: "enthalpy",
description: `doing damage can spawn ${powerUps.orb.heal(1)}
take 10% more harm`,
maxCount: 9,
count: 0,
frequency: 1,
frequencyDefault: 1,
isHealTech: true,
allowed() {
return !tech.isNoHeals
},
requires: "not ergodicity",
effect() {
tech.healthDrain += 0.02;
},
remove() {
tech.healthDrain = 0;
}
},
{
name: "maintenance",
description: `double the frequency of finding healing tech
spawn ${powerUps.orb.heal(13)}`,
maxCount: 1,
count: 0,
frequency: 1,
frequencyDefault: 1,
isNonRefundable: true,
isBadRandomOption: true,
allowed() { return true },
requires: "NOT EXPERIMENT MODE",
effect() {
for (let i = 0; i < 13; i++) powerUps.spawn(m.pos.x + 60 * (Math.random() - 0.5), m.pos.y + 60 * (Math.random() - 0.5), "heal");
for (let i = 0, len = tech.tech.length; i < len; i++) {
if (tech.tech[i].isHealTech) tech.tech[i].frequency *= 2
}
},
remove() {}
},
{
name: "anthropic principle",
nameInfo: "",
addNameInfo() {
setTimeout(function() {
powerUps.research.changeRerolls(0)
}, 1000);
},
description: `once per level, instead of dying
use ${powerUps.orb.research(1)} and spawn ${powerUps.orb.heal(5)}`,
maxCount: 1,
count: 0,
frequency: 1,
frequencyDefault: 1,
isHealTech: true,
allowed() {
return powerUps.research.count > 0 || build.isExperimentSelection
},
requires: "at least 1 research",
effect() {
tech.isDeathAvoid = true;
tech.isDeathAvoidedThisLevel = false;
setTimeout(function() {
powerUps.research.changeRerolls(0)
}, 1000);
},
remove() {
tech.isDeathAvoid = false;
}
},
{
name: "weak anthropic principle",
description: "after anthropic principle prevents your death
add 50% duplication chance for that level",
maxCount: 1,
count: 0,
frequency: 3,
frequencyDefault: 3,
allowed() {
return tech.isDeathAvoid
},
requires: "anthropic principle",
effect() {
tech.isAnthropicTech = true
powerUps.setDupChance(); //needed after adjusting duplication chance
},
remove() {
tech.isAnthropicTech = false
powerUps.setDupChance(); //needed after adjusting duplication chance
}
},
{
name: "strong anthropic principle",
description: "after anthropic principle prevents your death
increase damage by 137.03599% for that level",
maxCount: 1,
count: 0,
frequency: 3,
frequencyDefault: 3,
allowed() {
return tech.isDeathAvoid
},
requires: "anthropic principle",
effect() {
tech.isAnthropicDamage = true
},
remove() {
tech.isAnthropicDamage = false
}
},
{
name: "quantum immortality",
description: "reduce harm by 33%
after dying, continue in an alternate reality",
maxCount: 1,
count: 0,
frequency: 1,
frequencyDefault: 1,
allowed() { return true },
requires: "",
effect() {
tech.isImmortal = true;
},
remove() {
tech.isImmortal = false;
}
},
{
name: "non-unitary operator",
link: `non-unitary operator`,
description: "reduce combat difficulty by 2 levels, but
after a collision enter an alternate reality",
maxCount: 1,
count: 0,
frequency: 1,
frequencyDefault: 1,
allowed() {
return !tech.isResearchReality && !tech.isSwitchReality
},
requires: "not Ψ(t) collapse, many-worlds",
effect() {
tech.isCollisionRealitySwitch = true;
level.difficultyDecrease(simulation.difficultyMode * 2)
},
remove() {
tech.isCollisionRealitySwitch = false;
if (this.count > 0) {
level.difficultyIncrease(simulation.difficultyMode * 2)
}
}
},
{
name: "many-worlds",
// description: "each level is an alternate reality, where you
find a tech at the start of each level",
description: `on each new level use ${powerUps.orb.research(1)} to enter an
alternate reality and spawn a tech power up`,
maxCount: 1,
count: 0,
frequency: 1,
frequencyDefault: 1,
allowed() {
return !tech.isResearchReality && !tech.isCollisionRealitySwitch
},
requires: "not Ψ(t) collapse, non-unitary",
effect() {
tech.isSwitchReality = true;
},
remove() {
tech.isSwitchReality = false;
}
},
{
name: "Ψ(t) collapse",
link: `Ψ(t) collapse`,
description: `enter an alternate reality after you research
spawn ${powerUps.orb.research(14)}`,
maxCount: 1,
count: 0,
frequency: 1,
frequencyDefault: 1,
allowed() {
return !tech.isSwitchReality && !tech.isCollisionRealitySwitch && !tech.isJunkResearch
},
requires: "not many-worlds, non-unitary, pseudoscience",
bonusResearch: 14,
effect() {
tech.isResearchReality = true;
for (let i = 0; i < this.bonusResearch; i++) powerUps.spawn(m.pos.x + Math.random() * 60, m.pos.y + Math.random() * 60, "research", false);
},
remove() {
tech.isResearchReality = false;
if (this.count > 0) powerUps.research.changeRerolls(-this.bonusResearch)
}
},
{
name: "decoherence",
description: `researched or canceled tech won't reoccur
spawn ${powerUps.orb.research(7)}`,
maxCount: 1,
count: 0,
frequency: 1,
frequencyDefault: 1,
allowed() {
return !tech.isSuperDeterminism
},
requires: "not superdeterminism",
bonusResearch: 7,
effect() {
tech.isBanish = true
for (let i = 0; i < this.bonusResearch; i++) powerUps.spawn(m.pos.x + 40 * (Math.random() - 0.5), m.pos.y + 40 * (Math.random() - 0.5), "research", false);
},
remove() {
if (tech.isBanish) {
tech.isBanish = false
//reset banish list
for (let i = 0; i < tech.tech.length; i++) {
if (tech.tech[i].isBanished) tech.tech[i].isBanished = false
}
powerUps.research.changeRerolls(-this.bonusResearch)
}
tech.isBanish = false
}
},
{
name: "renormalization",
description: `using ${powerUps.orb.research(1)} for any purpose
has a 40% chance to spawn ${powerUps.orb.research(1)}`,
maxCount: 1,
count: 0,
frequency: 2,
frequencyDefault: 2,
allowed() {
return (powerUps.research.count > 3 || build.isExperimentSelection) && !tech.isSuperDeterminism
},
requires: "at least 4 research, not superdeterminism",
effect() {
tech.renormalization = true;
},
remove() {
tech.renormalization = false;
}
},
{
name: "perturbation theory",
description: `66% decreased delay after firing
when you have no ${powerUps.orb.research(1)} in your inventory`,
maxCount: 1,
count: 0,
frequency: 1,
frequencyDefault: 1,
allowed() {
return powerUps.research.count === 0
},
requires: "no research",
effect() {
tech.isRerollHaste = true;
tech.researchHaste = 0.33;
b.setFireCD();
},
remove() {
tech.isRerollHaste = false;
tech.researchHaste = 1;
b.setFireCD();
}
},
{
name: "ansatz",
description: `after choosing a field, tech, or gun
spawn ${powerUps.orb.research(2)}if you have no ${powerUps.orb.research(1)} in your inventory`,
maxCount: 1,
count: 0,
frequency: 1,
frequencyDefault: 1,
allowed() {
return powerUps.research.count === 0 && !tech.isSuperDeterminism && !tech.isRerollHaste && !tech.isResearchReality
},
requires: "no research, not superdeterminism, Ψ(t) collapse, perturbation theory",
effect: () => {
tech.isAnsatz = true;
},
remove() {
tech.isAnsatz = false;
}
},
{
name: "Bayesian statistics",
description: `increase damage by 3.8%
for each ${powerUps.orb.research(1)} in your inventory`,
maxCount: 1,
count: 0,
frequency: 2,
frequencyDefault: 2,
allowed() {
return powerUps.research.count > 5 || build.isExperimentSelection
},
requires: "at least 6 research",
effect() {
tech.isRerollDamage = true;
},
remove() {
tech.isRerollDamage = false;
}
},
{
name: "pseudoscience",
description: "when selecting a power up, research 3 times
for free, but add 0-3% JUNK to the tech pool",
maxCount: 1,
count: 0,
frequency: 1,
frequencyDefault: 1,
allowed() {
return !tech.isResearchReality //tech.isResearchBoss || tech.isMetaAnalysis || tech.isRerollBots || tech.isDeathAvoid || tech.isRerollDamage || build.isExperimentSelection
},
requires: "not Ψ(t) collapse", //"abiogenesis, meta-analysis, bot fabrication, anthropic principle, or Bayesian statistics, not Ψ(t) collapse",
effect() {
tech.isJunkResearch = true;
},
remove() {
tech.isJunkResearch = false;
}
},
{
name: "brainstorming",
description: "tech choices randomize
every 2 seconds for 10 seconds",
maxCount: 1,
count: 0,
frequency: 1,
frequencyDefault: 1,
allowed() {
return true
},
requires: "",
effect() {
tech.isBrainstorm = true
tech.isBrainstormActive = false
tech.brainStormDelay = 145 - simulation.difficultyMode * 10
},
remove() {
tech.isBrainstorm = false
tech.isBrainstormActive = false
}
},
{
name: "cross disciplinary",
description: "tech have an extra field or gun choice",
maxCount: 1,
count: 0,
frequency: 1,
frequencyDefault: 1,
allowed() {
return !tech.isDeterminism
},
requires: "not determinism",
effect: () => {
tech.isExtraGunField = true;
},
remove() {
tech.isExtraGunField = false;
}
},
{
name: "emergence",
description: "tech, fields, and guns have 5 choices
+5% JUNK to the potential tech pool",
maxCount: 1,
count: 0,
frequency: 1,
frequencyDefault: 1,
allowed() {
return !tech.isDeterminism
},
requires: "not determinism",
effect: () => {
tech.isExtraChoice = true;
this.refundAmount += tech.addJunkTechToPool(0.05)
},
refundAmount: 0,
remove() {
tech.isExtraChoice = false;
if (this.count > 0 && this.refundAmount > 0) {
tech.removeJunkTechFromPool(this.refundAmount)
this.refundAmount = 0
}
}
},
{
name: "determinism",
description: "spawn 5 tech, but you have only
1 choice for tech, fields, and guns",
maxCount: 1,
count: 0,
frequency: 1,
frequencyDefault: 1,
isBadRandomOption: true,
isNonRefundable: true,
allowed() {
return !tech.isExtraChoice && !tech.isExtraGunField
},
requires: "NOT EXPERIMENT MODE, not emergence, cross disciplinary",
effect: () => {
tech.isDeterminism = true;
//if you change the number spawned also change it in Born rule
for (let i = 0; i < 5; i++) powerUps.spawn(m.pos.x + 60 * (Math.random() - 0.5), m.pos.y + 60 * (Math.random() - 0.5), "tech");
},
remove() {
tech.isDeterminism = false;
// if (this.count > 0) {
// for (let i = 0; i < 5; i++) {
// const numberRemoved = tech.removeTech()
// console.log(numberRemoved)
// if (numberRemoved === 0) { //if the player didn't remove a power up then remove 1 tech for the map
// for (let j = powerUp.length - 1; j > -1; j--) {
// if (powerUp[j].name === "tech") {
// Matter.Composite.remove(engine.world, powerUp[j]);
// powerUp.splice(j, 1);
// break;
// }
// }
// }
// }
// }
}
},
{
name: "superdeterminism",
description: `spawn 5 tech, but you have no cancel
and ${powerUps.orb.research(1)}, no longer spawn`,
maxCount: 1,
count: 0,
frequency: 4,
frequencyDefault: 4,
isBadRandomOption: true,
isNonRefundable: true,
allowed() {
return tech.isDeterminism && !tech.isAnsatz
},
requires: "NOT EXPERIMENT MODE, determinism, not ansatz",
effect: () => {
tech.isSuperDeterminism = true;
//if you change the number spawned also change it in Born rule
for (let i = 0; i < 5; i++) powerUps.spawn(m.pos.x + 60 * (Math.random() - 0.5), m.pos.y + 60 * (Math.random() - 0.5), "tech");
},
remove() {
tech.isSuperDeterminism = false;
// tech.isSuperDeterminism = false;
// if (this.count) {
// for (let i = 0; i < 5; i++) tech.removeTech()
// }
}
},
{
name: "unified field theory",
description: `clicking the field box when paused cycles your field
triple the frequency of finding fieldtech`,
maxCount: 1,
count: 0,
frequency: 1,
frequencyDefault: 1,
allowed() {
return !tech.isSuperDeterminism && !tech.isNoDraftPause
},
requires: "not superdeterminism, eternalism",
effect() {
tech.isPauseSwitchField = true;
for (let i = 0, len = tech.tech.length; i < len; i++) {
if (tech.tech[i].isFieldTech) tech.tech[i].frequency *= 3
}
},
remove() {
tech.isPauseSwitchField = false;
if (this.count > 1) {
for (let i = 0, len = tech.tech.length; i < len; i++) {
if (tech.tech[i].isFieldTech) tech.tech[i].frequency /= 3
}
}
}
},
// for (let i = 0, len = tech.tech.length; i < len; i++) {
// if (tech.tech[i].isFieldTech) tech.tech[i].frequency *= 3
// }
// },
// remove() {
// // powerUps.research.changeRerolls(-6)
// // if (this.count > 1) {
// // for (let i = 0, len = tech.tech.length; i < len; i++) {
// // if (tech.tech[i].isFieldTech) tech.tech[i].frequency /= 3
// // }
// // }
{
name: "paradigm shift",
description: `clicking tech while paused ejects them
16% chance to convert that tech into ${powerUps.orb.research(1)}`,
maxCount: 1,
count: 0,
frequency: 1,
frequencyDefault: 1,
allowed() {
return !tech.isSuperDeterminism && !tech.isNoDraftPause
},
requires: "not superdeterminism, eternalism",
effect() {
tech.isPauseEjectTech = true;
},
remove() {
tech.isPauseEjectTech = false;
}
},
{
name: "eternalism",
// description: `increase damage by 60%, but time doesn't pause
while choosing a choosing a field, tech, or gun`, //${powerUps.orb.heal()} or
description: "increase damage by 40%, but
time can't be paused (time dilation still works)",
maxCount: 1,
count: 0,
frequency: 1,
frequencyDefault: 1,
allowed() {
return !tech.isPauseSwitchField && !tech.isPauseEjectTech && !tech.isWormHolePause
},
requires: "not unified field theory, paradigm shift, invariant",
effect() {
tech.isNoDraftPause = true
},
remove() {
tech.isNoDraftPause = false
}
},
{
name: "technical debt", // overengineering
// description: `increase damage by 300% minus 10% for tech you have learned(${4 - 0.1 * tech.totalCount})`,
// description: `increase damage by 300%, but reduce damage
by 10% for tech you have learned`,
descriptionFunction() {
// return `increase damage by 300% minus 15%
for each tech you have learned (${Math.floor(100*(4 - 0.14 * tech.totalCount))-100}%)`
return `increase damage by 300% minus 15%
for each tech you have learned (${Math.floor(100*(Math.max(41 / (tech.totalCount + 21), 4 - 0.15 * tech.totalCount) ))-100}%)`
},
maxCount: 1,
count: 0,
frequency: 1,
frequencyDefault: 1,
allowed() { return true },
requires: "",
effect() {
tech.isTechDebt = true;
},
remove() {
tech.isTechDebt = false;
}
},
{
name: "abiogenesis",
description: `at the start of a level spawn a 2nd boss
use ${powerUps.orb.research(4)}or add 49% JUNK to the tech pool`,
maxCount: 1,
count: 0,
frequency: 2,
frequencyDefault: 2,
allowed() {
return (build.isExperimentSelection || powerUps.research.count > 3) && !tech.isDuplicateBoss
},
requires: "at least 4 research, not parthenogenesis",
effect() {
tech.isResearchBoss = true; //abiogenesis
},
remove() {
tech.isResearchBoss = false;
}
},
{
name: "meta-analysis",
description: `if you choose a JUNK tech you instead get a
random normal tech and ${powerUps.orb.research(1)}`,
maxCount: 1,
count: 0,
frequency: 1,
frequencyDefault: 1,
allowed() { return true },
requires: "",
effect() {
tech.isMetaAnalysis = true
},
remove() {
tech.isMetaAnalysis = false
}
},
{
name: "dark patterns",
description: "reduce combat difficulty by 1 level
+31% JUNK to the potential tech pool",
maxCount: 1,
count: 0,
frequency: 1,
frequencyDefault: 1,
allowed() {
return level.onLevel < 8 && level.onLevel > 0
},
requires: "on levels 1 through 7",
effect() {
level.difficultyDecrease(simulation.difficultyMode)
// simulation.difficulty-=
simulation.makeTextLog(`level.difficultyDecrease(simulation.difficultyMode)`)
this.refundAmount += tech.addJunkTechToPool(0.31)
// for (let i = 0; i < tech.junk.length; i++) tech.tech.push(tech.junk[i])
},
refundAmount: 0,
remove() {
if (this.count > 0) {
if (this.refundAmount > 0) tech.removeJunkTechFromPool(this.refundAmount)
level.difficultyIncrease(simulation.difficultyMode)
}
}
},
{
name: "ergodicity",
description: `reduce combat difficulty by 2 levels
${powerUps.orb.heal()} have no effect`,
maxCount: 1,
count: 0,
frequency: 1,
frequencyDefault: 1,
allowed() {
return level.onLevel > 1 && !tech.isEnergyHealth && !tech.healthDrain
},
requires: "past level 1, not mass-energy, enthalpy",
effect() {
tech.isNoHeals = true;
level.difficultyDecrease(simulation.difficultyMode * 2)
simulation.makeTextLog(`level.difficultyDecrease(simulation.difficultyMode * 2)`)
powerUps.heal.color = "#abb"
for (let i = 0; i < powerUp.length; i++) { //find active heal power ups and adjust color live
if (powerUp[i].name === "heal") powerUp[i].color = powerUps.heal.color
}
},
remove() {
if (tech.isNoHeals) {
powerUps.heal.color = "#0eb"
for (let i = 0; i < powerUp.length; i++) { //find active heal power ups and adjust color live
if (powerUp[i].name === "heal") powerUp[i].color = powerUps.heal.color
}
}
tech.isNoHeals = false;
if (this.count > 0) level.difficultyIncrease(simulation.difficultyMode * 2)
}
},
{
name: "bubble fusion",
description: `after destroying a mob's natural shield
spawn 1-2 ${powerUps.orb.heal()}, ${powerUps.orb.ammo()}, or ${powerUps.orb.research(1)}`,
maxCount: 1,
count: 0,
frequency: 1,
frequencyDefault: 1,
allowed() { return true },
requires: "",
effect() {
tech.isShieldAmmo = true;
},
remove() {
tech.isShieldAmmo = false;
}
},
{
name: "options exchange",
link: `options exchange`,
description: `clicking × for a field, tech, or gun has a 94%
chance to randomize choices and not cancel`,
maxCount: 1,
count: 0,
frequency: 1,
frequencyDefault: 1,
allowed() {
return !tech.isSuperDeterminism //&& (tech.isCancelRerolls || tech.isCancelDuplication)
},
requires: "not superdeterminism", //futures exchange, commodities exchange,
effect() {
tech.isCancelTech = true
},
remove() {
tech.isCancelTech = false
}
},
{
name: "commodities exchange",
description: `clicking × to cancel a field, tech, or gun
spawns 5-10 ${powerUps.orb.heal()}, ${powerUps.orb.ammo()}, or ${powerUps.orb.research(1)}`,
maxCount: 1,
count: 0,
frequency: 1,
frequencyDefault: 1,
allowed() {
return !tech.isSuperDeterminism
},
requires: "not superdeterminism",
effect() {
tech.isCancelRerolls = true
},
remove() {
tech.isCancelRerolls = false
}
},
{
name: "futures exchange",
description: "clicking × to cancel a field, tech, or gun
adds 4.5% power up duplication chance",
maxCount: 1,
count: 0,
frequency: 1,
frequencyDefault: 1,
allowed() {
return tech.duplicationChance() < 1 && !tech.isSuperDeterminism
},
requires: "below 100% duplication chance, not superdeterminism",
effect() {
tech.isCancelDuplication = true //search for tech.cancelCount to balance
powerUps.setDupChance(); //needed after adjusting duplication chance
},
remove() {
tech.isCancelDuplication = false
powerUps.setDupChance(); //needed after adjusting duplication chance
}
},
{
name: "replication",
description: "10% chance to duplicate spawned power ups
+30% JUNK to the potential tech pool",
maxCount: 9,
count: 0,
frequency: 1,
frequencyDefault: 1,
allowed() {
return tech.duplicationChance() < 1
},
requires: "below 100% duplication chance",
effect() {
tech.duplicateChance += 0.1
powerUps.setDupChance(); //needed after adjusting duplication chance
if (!build.isExperimentSelection && !simulation.isTextLogOpen) simulation.circleFlare(0.1);
this.refundAmount += tech.addJunkTechToPool(0.3)
},
refundAmount: 0,
remove() {
tech.duplicateChance = 0
powerUps.setDupChance(); //needed after adjusting duplication chance
if (this.count > 0 && this.refundAmount > 0) {
tech.removeJunkTechFromPool(this.refundAmount)
this.refundAmount = 0
}
}
},
{
name: "stimulated emission",
description: "15% chance to duplicate spawned power ups
but, after a collision eject 1 tech",
maxCount: 1,
count: 0,
frequency: 1,
frequencyDefault: 1,
allowed() {
return tech.duplicationChance() < 1
},
requires: "below 100% duplication chance",
effect: () => {
tech.isStimulatedEmission = true
powerUps.setDupChance(); //needed after adjusting duplication chance
if (!build.isExperimentSelection && !simulation.isTextLogOpen) simulation.circleFlare(0.15);
},
remove() {
tech.isStimulatedEmission = false
powerUps.setDupChance(); //needed after adjusting duplication chance
}
},
{
name: "metastability",
description: "12% chance to duplicate spawned power ups
duplicates explode with a 3 second half-life",
maxCount: 1,
count: 0,
frequency: 1,
frequencyDefault: 1,
allowed() {
return tech.duplicationChance() < 1
},
requires: "below 100% duplication chance",
effect: () => {
tech.isPowerUpsVanish = true
powerUps.setDupChance(); //needed after adjusting duplication chance
if (!build.isExperimentSelection && !simulation.isTextLogOpen) simulation.circleFlare(0.12);
},
remove() {
tech.isPowerUpsVanish = false
powerUps.setDupChance(); //needed after adjusting duplication chance
}
},
{
name: "correlated damage",
description: "your chance to duplicate power ups
increases your damage by the same percent",
maxCount: 1,
count: 0,
frequency: 1,
frequencyDefault: 1,
allowed() {
return tech.duplicationChance() > 0.15
},
requires: "duplication chance > 15%",
effect() {
tech.isDupDamage = true;
},
remove() {
tech.isDupDamage = false;
}
},
{
name: "parthenogenesis",
description: " bosses have a 2x chance to be duplicated, but their
health is increased by your duplication chance",
maxCount: 1,
count: 0,
frequency: 1,
frequencyDefault: 1,
allowed() {
return tech.duplicationChance() > 0 && !tech.isResearchBoss
},
requires: "some duplication chance, not abiogenesis",
effect() {
tech.isDuplicateBoss = true;
},
remove() {
tech.isDuplicateBoss = false;
}
},
{
name: "apomixis",
description: `when you reach 111% duplication
spawn 11 bosses with 111% more health`,
maxCount: 1,
count: 0,
frequency: 6,
frequencyDefault: 6,
isNonRefundable: true,
allowed() {
return tech.duplicationChance() > 0.6
},
requires: "NOT EXPERIMENT MODE, duplication chance above 60%",
effect() {
tech.is111Duplicate = true;
tech.maxDuplicationEvent()
},
remove() {
tech.is111Duplicate = false;
}
},
{
name: "Born rule",
description: "remove all current tech
spawn new tech to replace them",
maxCount: 1,
count: 0,
frequency: 1,
frequencyDefault: 1,
isNonRefundable: true,
isBadRandomOption: true,
allowed() {
return (tech.totalCount > 6)
},
requires: "NOT EXPERIMENT MODE, more than 6 tech",
effect: () => {
//remove active bullets //to get rid of bots
for (let i = 0; i < bullet.length; ++i) Matter.Composite.remove(engine.world, bullet[i]);
bullet = [];
let count = 1 //count tech
for (let i = 0, len = tech.tech.length; i < len; i++) { // spawn new tech power ups
if (!tech.tech[i].isNonRefundable) count += tech.tech[i].count
}
if (tech.isDeterminism) count -= 4 //remove the bonus tech
if (tech.isSuperDeterminism) count -= 4 //remove the bonus tech
tech.setupAllTech(); // remove all tech
if (simulation.isCheating) tech.setCheating();
lore.techCount = 0;
// tech.addLoreTechToPool();
for (let i = 0; i < count; i++) powerUps.spawn(m.pos.x + 100 * (Math.random() - 0.5), m.pos.y + 100 * (Math.random() - 0.5), "tech"); // spawn new tech power ups
//have state is checked in m.death()
},
remove() {}
},
{
name: "Occam's razor",
// descriptionFunction() {
// return `randomly remove ${this.removePercent * 100}% of your tech
for each removed gain ${this.damagePerRemoved * 100}% damage`
// },
descriptionFunction() {
return `randomly remove half your tech
for each removed gain ${this.damagePerRemoved * 100 }% damage (~${(this.count === 0) ? this.damagePerRemoved * 50 * tech.totalCount : tech.OccamDamage*100}%)`
},
maxCount: 1,
count: 0,
frequency: 199,
frequencyDefault: 1,
isNonRefundable: true,
isBadRandomOption: true,
allowed() {
return (tech.totalCount > 6)
},
requires: "more than 6 tech",
// removePercent: 0.5,
damagePerRemoved: 0.5,
effect() {
let pool = []
for (let i = 0, len = tech.tech.length; i < len; i++) { // spawn new tech power ups
if (tech.tech[i].count && !tech.tech[i].isNonRefundable && !tech.tech[i].isFromAppliedScience) pool.push(i)
}
pool = shuffle(pool); //shuffles order of maps
let removeCount = 0
for (let i = 0, len = pool.length * this.damagePerRemoved; i < len; i++) removeCount += tech.removeTech(pool[i])
tech.OccamDamage = 1 + this.damagePerRemoved * removeCount
// tech.OccamDamage = Math.pow(1.25, removeCount)
},
remove() {
tech.OccamDamage = 0;
}
},
{
name: "exchange symmetry",
description: "remove 1 random tech
spawn 2 new guns",
maxCount: 1,
count: 0,
frequency: 1,
frequencyDefault: 1,
isNonRefundable: true,
isBadRandomOption: true,
allowed() {
return (tech.totalCount > 3) && !tech.isSuperDeterminism
},
requires: "NOT EXPERIMENT MODE, at least 4 tech, not superdeterminism",
effect: () => {
const have = [] //find which tech you have
for (let i = 0; i < tech.tech.length; i++) {
if (tech.tech[i].count > 0 && !tech.tech[i].isNonRefundable) have.push(i)
}
const choose = have[Math.floor(Math.random() * have.length)]
simulation.makeTextLog(`tech.removeTech("${tech.tech[choose].name}")`)
for (let i = 0; i < tech.tech[choose].count; i++) {
powerUps.spawn(m.pos.x, m.pos.y, "gun");
}
powerUps.spawn(m.pos.x, m.pos.y, "gun");
// powerUps.spawn(m.pos.x, m.pos.y, "gun");
tech.tech[choose].count = 0;
tech.tech[choose].remove(); // remove a random tech form the list of tech you have
tech.tech[choose].isLost = true
simulation.updateTechHUD();
},
remove() {}
},
{
name: "monte carlo experiment",
description: "remove 1 random tech
spawn 2 tech",
maxCount: 1,
count: 0,
frequency: 1,
frequencyDefault: 1,
isNonRefundable: true,
isBadRandomOption: true,
allowed() {
return (tech.totalCount > 3) && tech.duplicationChance() > 0 && !tech.isSuperDeterminism
},
requires: "NOT EXPERIMENT MODE, some duplication, at least 4 tech, not superdeterminism",
effect: () => {
const removeTotal = tech.removeTech()
for (let i = 0; i < removeTotal + 1; i++) powerUps.spawn(m.pos.x + 60 * (Math.random() - 0.5), m.pos.y + 60 * (Math.random() - 0.5), "tech");
},
remove() {}
},
{
name: "strange attractor",
descriptionFunction() { return `use ${powerUps.orb.research(2)} to spawn 1 tech with double
your duplication chance (${(2*tech.duplicationChance()*100).toFixed(0)}%)` },
// description: `use ${powerUps.orb.research(2)} to spawn 1 tech with double
your duplication chance (${(2*tech.duplicationChance()*100).toFixed(0)}%)`,
maxCount: 1,
count: 0,
frequency: 1000,
frequencyDefault: 1000,
isNonRefundable: true,
isBadRandomOption: true,
allowed() {
return !tech.isSuperDeterminism && tech.duplicationChance() > 0 && powerUps.research.count > 1
},
requires: "NOT EXPERIMENT MODE, some duplication, not super determinism",
effect: () => {
powerUps.research.changeRerolls(-2)
simulation.makeTextLog(`m.research -= 2`)
powerUps.directSpawn(m.pos.x, m.pos.y, "tech");
if (Math.random() < tech.duplicationChance() * 2) powerUps.directSpawn(m.pos.x + 10, m.pos.y + 5, "tech");
},
remove() {}
},
{
name: "reinforcement learning",
description: "increase the frequency of finding copies of
your current recursive tech by 1000%",
maxCount: 1,
count: 0,
frequency: 1,
frequencyDefault: 1,
allowed() {
return tech.totalCount > 9
},
requires: "at least 10 tech",
effect: () => {
for (let i = 0, len = tech.tech.length; i < len; i++) {
if (tech.tech[i].count > 0) tech.tech[i].frequency *= 10
}
},
remove() {
for (let i = 0, len = tech.tech.length; i < len; i++) {
if (tech.tech[i].count > 0 && tech.tech[i].frequency > 1) tech.tech[i].frequency /= 10
}
}
},
// {
// name: "backward induction",
// descriptionFunction() {
// if (build.isExperimentSelection || powerUps.tech.choiceLog.length < 10) return `use ${powerUps.orb.research(2)} to choose all the unchosen tech
from your last selection`
// text = ``
// let num = 3
// if (tech.isExtraChoice) num = 5
// if (tech.isDeterminism) num = 1
// for (let i = 0; i < num; i++) {
// const index = powerUps.tech.choiceLog[powerUps.tech.choiceLog.length - i - 1]
// if (index !== powerUps.lastTechIndex && tech.tech[index].count < tech.tech[index].maxCount && tech.tech[index].allowed() && tech.tech[index].name !== "backward induction") {
// text += `${tech.tech[index].name}, `
// }
// }
// text = text.slice(0, -2);
// return `use ${powerUps.orb.research(2)}to choose the unchosen
tech from your previous selection:
${text}`
// },
// // description: `use ${powerUps.orb.research(2)}to choose all the unchosen
tech from your previous tech selection`,
// maxCount: 1,
// count: 0,
// frequency: 100,
// frequencyDefault: 100,
// isNonRefundable: true,
// isBadRandomOption: true,
// allowed() {
// return powerUps.tech.choiceLog.length > 10 && !tech.isDeterminism && powerUps.research.count > 1
// },
// requires: "NOT EXPERIMENT MODE, rejected an option in the last tech selection, at least 2 research, not determinism",
// effect: () => {
// powerUps.research.changeRerolls(-2)
// let num = 3
// if (tech.isExtraChoice) num = 5
// if (tech.isDeterminism) num = 1
// for (let i = 0; i < num; i++) {
// const index = powerUps.tech.choiceLog[powerUps.tech.choiceLog.length - i - 1]
// if (index !== powerUps.lastTechIndex && tech.tech[index].count < tech.tech[index].maxCount && tech.tech[index].allowed() && tech.tech[index].name !== "backward induction") {
// tech.giveTech(index)
// simulation.makeTextLog(`tech.giveTech("${tech.tech[index].name}") //backward induction`);
// }
// }
// },
// remove() {}
// },
//**************************************************
//************************************************** gun
//************************************************** tech
//**************************************************
// {
// name: "CPT gun",
// link: `CPT gun`,
// description: `adds the CPT gun to your inventory
it rewinds your health, velocity, and position`,
// isGunTech: true,
// maxCount: 1,
// count: 0,
// frequency: 2,
// frequencyDefault: 2,
// allowed() {
// return (b.totalBots() > 3 || m.fieldUpgrades[m.fieldMode].name === "molecular assembler" || m.fieldUpgrades[m.fieldMode].name === "plasma torch" || m.fieldUpgrades[m.fieldMode].name === "pilot wave") && !tech.isEnergyHealth && !tech.isRewindAvoidDeath //build.isExperimentSelection ||
// },
// requires: "bots > 3, plasma torch, assembler, pilot wave, not mass-energy equivalence, CPT",
// effect() {
// tech.isRewindGun = true
// b.guns.push(b.gunRewind)
// b.giveGuns("CPT gun");
// },
// remove() {
// if (tech.isRewindGun) {
// b.removeGun("CPT gun", true)
// // for (let i = 0; i < b.guns.length; i++) {
// // if (b.guns[i].name === "CPT gun") {
// // b.guns[i].have = false
// // for (let j = 0; j < b.inventory.length; j++) {
// // if (b.inventory[j] === i) {
// // b.inventory.splice(j, 1)
// // break
// // }
// // }
// // if (b.inventory.length) {
// // b.activeGun = b.inventory[0];
// // } else {
// // b.activeGun = null;
// // }
// // simulation.makeGunHUD();
// // b.guns.splice(i, 1) //also remove CPT gun from gun pool array
// // break
// // }
// // }
// tech.isRewindGun = false
// }
// }
// },
{
name: "needle ice",
description: `when needles impact walls
they chip off 1-2 freezing ice IX crystals`,
isGunTech: true,
maxCount: 1,
count: 0,
frequency: 2,
frequencyDefault: 2,
allowed() {
return (tech.isNeedles || tech.isNeedles) && !tech.needleTunnel
},
requires: "nail gun, needle gun, not nanowires",
effect() {
tech.isNeedleIce = true
},
remove() {
tech.isNeedleIce = false
}
},
{
name: "nanowires",
description: `needles tunnel through blocks and map
increase needle damage by 20%`,
isGunTech: true,
maxCount: 1,
count: 0,
frequency: 2,
frequencyDefault: 2,
allowed() {
return ((tech.haveGunCheck("nail gun") && tech.isNeedles) || (tech.isNeedles && tech.haveGunCheck("shotgun"))) && !tech.isNeedleIce
},
requires: "nail gun, needle gun, not needle ice",
effect() {
tech.needleTunnel = true
},
remove() {
tech.needleTunnel = false
}
},
{
name: "needle gun",
description: "nail gun and shotgun fire mob piercing needles",
isGunTech: true,
maxCount: 1,
count: 0,
frequency: 2,
frequencyDefault: 2,
allowed() {
return ((tech.haveGunCheck("nail gun") && !tech.nailInstantFireRate && !tech.nailRecoil) || (tech.haveGunCheck("shotgun") && !tech.isNailShot && !tech.isFoamShot && !tech.isSporeWorm)) && !tech.isRivets && !tech.isIncendiary && !tech.isIceCrystals && !tech.isIceShot
},
requires: "nail gun, shotgun, not ice crystal, rivets, rotary cannon, or pneumatic, incendiary, nail-shot, rivets, foam-shot, worm-shot, ice-shot",
effect() {
tech.isNeedles = true
for (i = 0, len = b.guns.length; i < len; i++) { //find which gun
if (b.guns[i].name === "nail gun") {
b.guns[i].ammo = Math.ceil(b.guns[i].ammo / 3);
b.guns[i].ammoPack = Math.ceil(b.guns[i].defaultAmmoPack / 3);
b.guns[i].chooseFireMethod()
simulation.updateGunHUD();
break
}
}
},
remove() {
if (tech.isNeedles) {
tech.isNeedles = false
for (i = 0, len = b.guns.length; i < len; i++) { //find which gun
if (b.guns[i].name === "nail gun") {
b.guns[i].chooseFireMethod()
b.guns[i].ammo = Math.ceil(b.guns[i].ammo * 3);
b.guns[i].ammoPack = b.guns[i].defaultAmmoPack;
simulation.updateGunHUD();
break
}
}
}
}
},
{
name: "rivet gun",
description: "nail gun and shotgun slowly lob a heavy rivet",
isGunTech: true,
maxCount: 1,
count: 0,
frequency: 2,
frequencyDefault: 2,
allowed() {
return ((tech.haveGunCheck("nail gun") && !tech.nailInstantFireRate) || (tech.haveGunCheck("shotgun") && !tech.isNailShot && !tech.isFoamShot && !tech.isSporeWorm)) && !tech.isNeedles && !tech.isIceCrystals && !tech.isIceShot
},
requires: "nail gun, shotgun, 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
}
}
}
tech.isRivets = false
}
},
// {
// name: "slug",
// description: "shotgun lobs 1 huge bullet",
// isGunTech: true,
// maxCount: 1,
// count: 0,
// frequency: 2,
// frequencyDefault: 2,
// allowed() {
// return tech.haveGunCheck("shotgun") && !tech.isNailShot && !tech.isIncendiary && !tech.isIceShot && !tech.isFoamShot && !tech.isSporeWorm && !tech.isNeedles
// },
// requires: "shotgun, not nail-shot, foam-shot, worm-shot, ice-shot, needle-shot",
// effect() {
// tech.isSlugShot = true;
// for (i = 0, len = b.guns.length; i < len; i++) { //find which gun
// if (b.guns[i].name === "shotgun") {
// b.guns[i].do = function() {
// if (!input.field && input.down) {
// ctx.beginPath()
// const speed = input.down ? 212 : 160
// const v = { x: speed * Math.cos(m.angle), y: speed * Math.sin(m.angle) } //m.Vy / 2 + removed to make the path less jerky
// const where = { x: m.pos.x, y: m.pos.y }
// for (let i = 0; i < 20; i++) {
// v.x *= 0.9712
// v.y = v.y * 0.977 + 9.87
// where.x += v.x
// where.y += v.y
// ctx.lineTo(where.x, where.y)
// }
// ctx.strokeStyle = "rgba(68, 68, 68, 0.2)" //color.map
// ctx.lineWidth = 2
// ctx.stroke()
// }
// }
// break
// }
// }
// },
// remove() {
// if (tech.isSlugShot) {
// for (i = 0, len = b.guns.length; i < len; i++) { //find which gun
// if (b.guns[i].name === "shotgun") {
// b.guns[i].do = function() {}
// break
// }
// }
// }
// tech.isSlugShot = false;
// }
// },
// {
// name: "super sized",
// description: `increase super ball radius by 14%
increases damage by about 27%`,
// isGunTech: true,
// maxCount: 9,
// count: 0,
// frequency: 2,
// frequencyDefault: 2,
// allowed() {
// return tech.haveGunCheck("super balls")
// },
// requires: "super balls",
// effect() {
// tech.bulletSize += 0.14
// },
// remove() {
// tech.bulletSize = 1;
// }
// },
{
name: "caliber",
description: `rivets, needles, super balls, and nails
have 16% increased mass and physical damage`,
isGunTech: true,
maxCount: 9,
count: 0,
frequency: 2,
frequencyDefault: 2,
allowed() {
return tech.isMineDrop + tech.isNailBotUpgrade + tech.fragments + tech.nailsDeathMob + (tech.haveGunCheck("super balls") + (tech.haveGunCheck("mine") && !tech.isLaserMine) + (tech.haveGunCheck("nail gun")) + tech.isNeedles + tech.isNailShot + tech.isRivets) * 2 > 1
},
requires: "nails, nail gun, rivets, shotgun",
effect() {
tech.bulletSize += 0.16
},
remove() {
tech.bulletSize = 1;
}
},
{
name: "pneumatic actuator",
description: "nail gun takes no time to ramp up
to its shortest delay after firing",
isGunTech: true,
maxCount: 1,
count: 0,
frequency: 2,
frequencyDefault: 2,
allowed() {
return tech.haveGunCheck("nail gun") && !tech.isRivets && !tech.isNeedles && !tech.nailRecoil
},
requires: "nail gun, not rotary cannon, rivets, or needles",
effect() {
tech.nailInstantFireRate = true
for (i = 0, len = b.guns.length; i < len; i++) { //find which gun
if (b.guns[i].name === "nail gun") b.guns[i].chooseFireMethod()
}
},
remove() {
if (tech.nailInstantFireRate) {
tech.nailInstantFireRate = false
for (i = 0, len = b.guns.length; i < len; i++) { //find which gun
if (b.guns[i].name === "nail gun") b.guns[i].chooseFireMethod()
}
}
}
},
{
name: "ice crystal nucleation",
link: `ice crystal nucleation`,
description: "the nail gun uses energy to condense
unlimited freezing ice shards",
isGunTech: true,
maxCount: 1,
count: 0,
frequency: 2,
frequencyDefault: 2,
allowed() {
return tech.haveGunCheck("nail gun") && !tech.isRivets && !tech.isNeedles // && !tech.isNailRadiation && !tech.isNailCrit
},
requires: "nail gun, not rivets, needles",
effect() {
tech.isIceCrystals = true;
for (i = 0, len = b.guns.length; i < len; i++) { //find which gun
if (b.guns[i].name === "nail gun") {
b.guns[i].ammoPack = Infinity
b.guns[i].recordedAmmo = b.guns[i].ammo
b.guns[i].ammo = Infinity
simulation.updateGunHUD();
break;
}
}
},
remove() {
if (tech.isIceCrystals) {
tech.isIceCrystals = false;
for (i = 0, len = b.guns.length; i < len; i++) { //find which gun
if (b.guns[i].name === "nail gun") {
b.guns[i].ammoPack = b.guns[i].defaultAmmoPack;
if (b.guns[i].recordedAmmo) b.guns[i].ammo = b.guns[i].recordedAmmo
simulation.updateGunHUD();
break;
}
}
}
}
},
{
name: "rotary cannon",
description: "nail gun has increased muzzle speed,
maximum fire rate, accuracy, and recoil",
isGunTech: true,
maxCount: 1,
count: 0,
frequency: 2,
frequencyDefault: 2,
allowed() {
return tech.haveGunCheck("nail gun") && !tech.nailInstantFireRate && !tech.isNeedles
},
requires: "nail gun, not pneumatic actuator, needle gun",
effect() {
tech.nailRecoil = true
for (i = 0, len = b.guns.length; i < len; i++) { //find which gun
if (b.guns[i].name === "nail gun") b.guns[i].chooseFireMethod()
}
},
remove() {
if (tech.nailRecoil) {
tech.nailRecoil = false
for (i = 0, len = b.guns.length; i < len; i++) { //find which gun
if (b.guns[i].name === "nail gun") b.guns[i].chooseFireMethod()
}
}
}
},
{
name: "supercritical fission",
description: "nails, needles, and rivets can explode
if they strike mobs near their center",
isGunTech: true,
maxCount: 1,
count: 0,
frequency: 2,
frequencyDefault: 2,
allowed() {
return (tech.isNailShot || tech.isNeedles || tech.isNailBotUpgrade || tech.haveGunCheck("nail gun") || tech.isRivets) && !tech.isIncendiary
},
requires: "nail gun, needles, nails, rivets, not incendiary",
effect() {
tech.isNailCrit = true
},
remove() {
tech.isNailCrit = false
}
},
{
name: "irradiated nails",
link: `irradiated nails`,
description: "nails, needles, and rivets are radioactive
about 90% more damage over 3 seconds",
isGunTech: true,
maxCount: 1,
count: 0,
frequency: 2,
frequencyDefault: 2,
allowed() {
return tech.isMineDrop || tech.isNailBotUpgrade || tech.fragments || tech.nailsDeathMob || (tech.haveGunCheck("mine") && !tech.isLaserMine) || (tech.haveGunCheck("nail gun") && !tech.isShieldPierce) || (tech.haveGunCheck("shotgun") && (tech.isNeedles || tech.isNailShot))
},
requires: "nail gun, nails, rivets, mine, not ceramic needles",
effect() {
tech.isNailRadiation = true;
},
remove() {
tech.isNailRadiation = false;
}
},
{
name: "6s half-life",
link: `6s half-life`,
description: "nails are made of plutonium-238
increase damage by 100% over 6 seconds",
isGunTech: true,
maxCount: 1,
count: 0,
frequency: 2,
frequencyDefault: 2,
allowed() {
return tech.isNailRadiation && !tech.isFastRadiation
},
requires: "nail gun, mine, irradiated nails, not 1s half-life",
effect() {
tech.isSlowRadiation = true;
},
remove() {
tech.isSlowRadiation = false;
}
},
{
name: "1s half-life",
link: `1s half-life`,
description: "nails are made of lithium-8
damage occurs after 1 second",
isGunTech: true,
maxCount: 1,
count: 0,
frequency: 2,
frequencyDefault: 2,
allowed() {
return tech.isNailRadiation && !tech.isSlowRadiation
},
requires: "nail gun, mine, irradiated nails, not 6s half-life",
effect() {
tech.isFastRadiation = true;
},
remove() {
tech.isFastRadiation = false;
}
},
{
name: "spin-statistics",
link: `spin-statistics`,
description: "immune to harm while firing the shotgun
shotgun has 50% fewer shots",
isGunTech: true,
maxCount: 1,
count: 0,
frequency: 2,
frequencyDefault: 2,
allowed() {
return tech.haveGunCheck("shotgun")
},
requires: "shotgun",
effect() {
tech.isShotgunImmune = true;
//cut current ammo by 1/2
for (i = 0, len = b.guns.length; i < len; i++) { //find which gun
if (b.guns[i].name === "shotgun") {
b.guns[i].ammo = Math.ceil(b.guns[i].ammo * 0.5);
b.guns[i].ammoPack = b.guns[i].defaultAmmoPack * 0.5
break;
}
}
simulation.updateGunHUD();
},
remove() {
if (tech.isShotgunImmune) {
tech.isShotgunImmune = false;
for (i = 0, len = b.guns.length; i < len; i++) { //find which gun
if (b.guns[i].name === "shotgun") {
b.guns[i].ammoPack = b.guns[i].defaultAmmoPack;
b.guns[i].ammo = Math.ceil(b.guns[i].ammo * 2);
break;
}
}
simulation.updateGunHUD();
}
}
},
{
name: "Newton's 3rd law",
description: "shotgun recoil is increased
decrease shotgun delay after firing by 66%",
isGunTech: true,
maxCount: 1,
count: 0,
frequency: 2,
frequencyDefault: 2,
allowed() {
return tech.haveGunCheck("shotgun") && !tech.isShotgunReversed
},
requires: "shotgun, not Noether violation",
effect() {
tech.isShotgunRecoil = true;
},
remove() {
tech.isShotgunRecoil = false;
}
},
{
name: "Noether violation",
link: `Noether violation`,
description: "increase shotgun damage 60%
its recoil is increased and reversed",
isGunTech: true,
maxCount: 1,
count: 0,
frequency: 2,
frequencyDefault: 2,
allowed() {
return (tech.haveGunCheck("shotgun")) && !tech.isShotgunRecoil
},
requires: "shotgun, not Newton's 3rd law",
effect() {
tech.isShotgunReversed = true;
},
remove() {
tech.isShotgunReversed = false;
}
},
{
name: "nail-shot",
link: `nail-shot`,
description: "shotgun fires 17 nails",
isGunTech: true,
maxCount: 1,
count: 0,
frequency: 2,
frequencyDefault: 2,
allowed() {
return tech.haveGunCheck("shotgun") && !tech.isIncendiary && !tech.isRivets && !tech.isIceShot && !tech.isFoamShot && !tech.isSporeWorm && !tech.isNeedles
},
requires: "shotgun, not incendiary, rivets, foam-shot, worm-shot, ice-shot, needles",
effect() {
tech.isNailShot = true;
},
remove() {
tech.isNailShot = false;
}
},
{
name: "foam-shot",
link: `foam-shot`,
description: "shotgun sprays 15 sticky foam bubbles",
isGunTech: true,
maxCount: 1,
count: 0,
frequency: 2,
frequencyDefault: 2,
allowed() {
return tech.haveGunCheck("shotgun") && !tech.isNailShot && !tech.isIncendiary && !tech.isRivets && !tech.isIceShot && !tech.isSporeWorm && !tech.isNeedles
},
requires: "shotgun, not incendiary, nail-shot, rivet, worm-shot, ice-shot, needle",
effect() {
tech.isFoamShot = true;
},
remove() {
tech.isFoamShot = false;
}
},
{
name: "ice-shot",
link: `ice-shot`,
description: "shotgun grows 15 freezing ice IX crystals",
isGunTech: true,
maxCount: 1,
count: 0,
frequency: 2,
frequencyDefault: 2,
allowed() {
return tech.haveGunCheck("shotgun") && !tech.isNailShot && !tech.isIncendiary && !tech.isRivets && !tech.isFoamShot && !tech.isSporeWorm && !tech.isNeedles
},
requires: "shotgun, not incendiary, nail-shot, rivet, foam-shot, worm-shot",
effect() {
tech.isIceShot = true;
},
remove() {
tech.isIceShot = false;
}
},
{
name: "incendiary ammunition",
description: "shotgun, rivets, super balls, and drones
are loaded with explosives",
isGunTech: true,
maxCount: 1,
count: 0,
frequency: 1,
frequencyDefault: 1,
allowed() {
return (tech.haveGunCheck("shotgun") && !tech.isNailShot && !tech.isIceShot && !tech.isRivets && !tech.isFoamShot && !tech.isSporeWorm && !tech.isNeedles) || (tech.haveGunCheck("super balls") && !tech.isFoamBall) || (tech.isRivets && !tech.isNailCrit) || (m.fieldUpgrades[m.fieldMode].name === "molecular assembler" && !(tech.isDroneTeleport || tech.isDroneRadioactive || tech.isSporeField || tech.isMissileField || tech.isIceField)) || (tech.haveGunCheck("drones") && !tech.isForeverDrones && !tech.isDroneRadioactive && !tech.isDroneTeleport)
},
requires: "shotgun, super balls, rivets, drones, not irradiated drones, burst drones, polyurethane",
effect() {
tech.isIncendiary = true
},
remove() {
tech.isIncendiary = false;
}
},
{
name: "autocannon",
description: "fire +1 extra super ball
balls are quickly released in same direction",
isGunTech: true,
maxCount: 1,
count: 0,
frequency: 2,
frequencyDefault: 2,
allowed() {
return tech.haveGunCheck("super balls") && !tech.oneSuperBall
},
requires: "super balls, but not the tech super ball",
effect() {
tech.superBallDelay = true
for (i = 0, len = b.guns.length; i < len; i++) { //find which gun
if (b.guns[i].name === "super balls") b.guns[i].chooseFireMethod()
}
},
remove() {
if (tech.superBallDelay) {
tech.superBallDelay = false;
for (i = 0, len = b.guns.length; i < len; i++) { //find which gun
if (b.guns[i].name === "super balls") b.guns[i].chooseFireMethod()
}
}
}
},
{
name: "super duper",
description: `randomly fire +0, +1, or +2 extra super balls`,
isGunTech: true,
maxCount: 9,
count: 0,
frequency: 2,
frequencyDefault: 2,
allowed() {
return tech.haveGunCheck("super balls") && !tech.oneSuperBall
},
requires: "super balls, not super ball",
effect() {
tech.extraSuperBalls += 3
},
remove() {
tech.extraSuperBalls = 0;
}
},
{
name: "super ball",
description: "fire just 1 large super ball
that stuns mobs for 3 second",
isGunTech: true,
maxCount: 1,
count: 0,
frequency: 2,
frequencyDefault: 2,
allowed() {
return tech.haveGunCheck("super balls") && !tech.extraSuperBalls && !tech.superBallDelay
},
requires: "super balls, not super duper or supertemporal",
effect() {
tech.oneSuperBall = true;
for (i = 0, len = b.guns.length; i < len; i++) { //find which gun
if (b.guns[i].name === "super balls") b.guns[i].chooseFireMethod()
}
},
remove() {
if (tech.oneSuperBall) {
tech.oneSuperBall = false;
for (i = 0, len = b.guns.length; i < len; i++) { //find which gun
if (b.guns[i].name === "super balls") b.guns[i].chooseFireMethod()
}
}
}
},
{
name: "polyurethane foam",
description: "super balls colliding with mobs catalyzes
a reaction that yields foam bubbles",
isGunTech: true,
maxCount: 1,
count: 0,
frequency: 2,
frequencyDefault: 2,
allowed() {
return tech.haveGunCheck("super balls")
},
requires: "super balls",
effect() {
tech.isFoamBall = true;
},
remove() {
tech.isFoamBall = false;
}
},
//
{
name: "phase velocity",
description: "matter wave propagates faster through solids
increase matter wave damage by 20%",
// description: "matter wave propagates faster through solids
up by 3000% in the map and 760% in blocks",
isGunTech: true,
maxCount: 1,
count: 0,
frequency: 2,
frequencyDefault: 2,
allowed() {
return tech.haveGunCheck("matter wave") && !tech.isLongitudinal
},
requires: "matter wave, not phonon",
effect() {
tech.isPhaseVelocity = true;
},
remove() {
tech.isPhaseVelocity = false;
}
},
{
name: "bound state",
description: "wave packets reflect backwards 2 times
range is reduced by 25%",
isGunTech: true,
maxCount: 9,
count: 0,
frequency: 2,
frequencyDefault: 2,
allowed() {
return tech.haveGunCheck("matter wave")
},
requires: "matter wave",
effect() {
tech.waveReflections += 2
},
remove() {
tech.waveReflections = 1
}
},
{
name: "amplitude",
description: "wave packet amplitude is 33% higher
wave damage is increased by 37%",
isGunTech: true,
maxCount: 3,
count: 0,
frequency: 2,
frequencyDefault: 2,
allowed() {
return tech.haveGunCheck("matter wave")
},
requires: "matter wave",
effect() {
tech.waveFrequency *= 0.66
tech.wavePacketDamage *= 1.37
},
remove() {
tech.waveFrequency = 0.2
tech.wavePacketDamage = 1
}
},
{
name: "propagation",
description: "wave packet propagation speed is 25% slower
wave damage is increased by 37%",
isGunTech: true,
maxCount: 9,
count: 0,
frequency: 2,
frequencyDefault: 2,
allowed() {
return tech.haveGunCheck("matter wave")
},
requires: "matter wave",
effect() {
tech.waveBeamSpeed *= 0.8;
tech.waveBeamDamage += 1.5 * 0.37 //this sets base matter wave damage
},
remove() {
tech.waveBeamSpeed = 12;
tech.waveBeamDamage = 1.5 //this sets base matter wave damage
}
},
{
name: "phonon", //longitudinal //gravitational wave?
description: "matter wave emits low frequency, high damage
expanding arcs that propagate through solids",
isGunTech: true,
maxCount: 1,
count: 0,
frequency: 2,
frequencyDefault: 2,
allowed() {
return tech.haveGunCheck("matter wave") && !tech.isPhaseVelocity && !tech.isBulletTeleport
},
requires: "matter wave, not phase velocity, uncertainty principle",
effect() {
tech.isLongitudinal = true;
for (i = 0, len = b.guns.length; i < len; i++) { //find which gun
if (b.guns[i].name === "matter wave") {
b.guns[i].chooseFireMethod()
b.guns[i].ammoPack = b.guns[i].defaultAmmoPack / 9
b.guns[i].ammo = Math.ceil(b.guns[i].ammo / 9);
simulation.updateGunHUD();
break
}
}
},
remove() {
if (tech.isLongitudinal) {
for (i = 0, len = b.guns.length; i < len; i++) { //find which gun
if (b.guns[i].name === "matter wave") {
tech.isLongitudinal = false;
b.guns[i].chooseFireMethod()
b.guns[i].ammoPack = b.guns[i].defaultAmmoPack
b.guns[i].ammo = Math.ceil(b.guns[i].ammo * 9);
simulation.updateGunHUD();
break
}
}
}
tech.isLongitudinal = false;
}
},
{
name: "isotropic radiator",
description: "matter wave expands in all directions
range reduced 40% and damage increased 50%",
isGunTech: true,
maxCount: 1,
count: 0,
frequency: 2,
frequencyDefault: 2,
allowed() {
return tech.isLongitudinal
},
requires: "matter wave, phonon",
effect() {
tech.is360Longitudinal = true;
for (i = 0, len = b.guns.length; i < len; i++) { //find which gun
if (b.guns[i].name === "matter wave") {
b.guns[i].chooseFireMethod()
break
}
}
},
remove() {
tech.is360Longitudinal = false;
for (i = 0, len = b.guns.length; i < len; i++) { //find which gun
if (b.guns[i].name === "matter wave") {
b.guns[i].chooseFireMethod()
break
}
}
}
},
{
name: "cruise missile",
description: "missiles travel 50% slower,
but have a 100% larger explosive payload",
isGunTech: true,
maxCount: 1,
count: 0,
frequency: 2,
frequencyDefault: 2,
allowed() {
return (tech.haveGunCheck("missiles") && tech.missileFireCD === 45) || tech.isMissileField || tech.missileBotCount
},
requires: "missiles",
effect() {
tech.isMissileBig = true
},
remove() {
tech.isMissileBig = false
}
},
{
name: "ICBM",
description: "cruise missiles travel 66% slower,
but have a 100% larger explosive payload",
isGunTech: true,
maxCount: 1,
count: 0,
frequency: 2,
frequencyDefault: 2,
allowed() {
return tech.haveGunCheck("missiles") && tech.isMissileBig //&& !tech.isSmartRadius && !tech.isImmuneExplosion
},
requires: "missiles, cruse missile",
effect() {
tech.isMissileBiggest = true
},
remove() {
tech.isMissileBiggest = false
}
},
{
name: "launch system",
description: `reduce missile launch cooldown 500%
gain 20% more missile ammo per ${powerUps.orb.ammo(1)}`,
isGunTech: true,
maxCount: 1,
count: 0,
frequency: 2,
frequencyDefault: 2,
allowed() {
return tech.haveGunCheck("missiles") && !tech.isMissileBig
},
requires: "missiles",
ammoBonus: 1.2,
effect() {
tech.missileFireCD = 10
for (i = 0, len = b.guns.length; i < len; i++) { //find which gun
if (b.guns[i].name === "missiles") {
b.guns[i].ammoPack *= this.ammoBonus;
b.guns[i].ammo = Math.ceil(b.guns[i].ammo * this.ammoBonus);
simulation.updateGunHUD();
break
}
}
},
remove() {
if (tech.missileFireCD !== 45) {
tech.missileFireCD = 45;
for (i = 0, len = b.guns.length; i < len; i++) { //find which gun
if (b.guns[i].name === "missiles") {
b.guns[i].ammoPack = 5;
b.guns[i].ammo = Math.ceil(b.guns[i].ammo / this.ammoBonus);
simulation.updateGunHUD();
break
}
}
}
}
},
{
name: "missile-bot",
link: `missile-bot`,
description: "gain a bot that fires missiles at mobs
remove your missile gun",
isGunTech: true,
isRemoveGun: true,
maxCount: 1,
count: 0,
frequency: 2,
frequencyDefault: 2,
isBot: true,
isBotTech: true,
allowed() {
return tech.haveGunCheck("missiles", false) && tech.missileFireCD === 45
},
requires: "missiles",
effect() {
tech.missileBotCount++;
b.missileBot();
if (tech.haveGunCheck("missiles", false)) b.removeGun("missiles") //remove your last gun
},
remove() {
if (this.count) {
tech.missileBotCount = 0;
b.clearPermanentBots();
b.respawnBots();
if (!tech.haveGunCheck("missiles", false)) b.giveGuns("missiles")
}
}
},
{
name: "iridium-192",
description: "explosions release gamma radiation
100% more damage, but over 4 seconds",
isGunTech: true,
maxCount: 1,
count: 0,
frequency: 2,
frequencyDefault: 2,
allowed() {
return tech.explosiveRadius === 1 && !tech.isSmallExplosion && !tech.isBlockExplode && !tech.fragments && (tech.haveGunCheck("missiles") || tech.missileBotCount || tech.isIncendiary || (tech.haveGunCheck("grenades") && !tech.isNeutronBomb) || tech.isPulseLaser || tech.isMissileField || tech.isBoomBotUpgrade || tech.isTokamak)
},
requires: "an explosive damage source, not ammonium nitrate, nitroglycerin, chain reaction, fragmentation",
effect: () => {
tech.isExplodeRadio = true; //iridium-192
},
remove() {
tech.isExplodeRadio = false;
}
},
{
name: "fragmentation",
description: "some detonations and collisions eject nails
blocks, grenades, missiles, rivets, harpoon",
isGunTech: true,
maxCount: 9,
count: 0,
frequency: 1,
frequencyDefault: 1,
allowed() {
return !tech.isExplodeRadio && (tech.haveGunCheck("harpoon") || (tech.haveGunCheck("grenades") && !tech.isNeutronBomb) || tech.haveGunCheck("missiles") || tech.missileBotCount || tech.isRivets || tech.blockDamage > 0.075)
},
requires: "grenades, missiles, rivets, harpoon, or mass driver, not iridium-192",
effect() {
tech.fragments++
},
remove() {
tech.fragments = 0
}
},
{
name: "ammonium nitrate",
description: "increase explosive damage by 24%
increase explosive radius by 24%",
isGunTech: true,
maxCount: 9,
count: 0,
frequency: 2,
frequencyDefault: 2,
allowed() {
return !tech.isExplodeRadio && tech.hasExplosiveDamageCheck()
},
requires: "an explosive damage source, not iridium-192",
effect: () => {
tech.explosiveRadius += 0.24;
},
remove() {
tech.explosiveRadius = 1;
}
},
{
name: "nitroglycerin",
description: "increase explosive damage by 66%
decrease explosive radius by 33%",
isGunTech: true,
maxCount: 1,
count: 0,
frequency: 2,
frequencyDefault: 2,
allowed() {
return !tech.isExplodeRadio && tech.hasExplosiveDamageCheck() && !tech.isExplosionHarm
},
requires: "an explosive damage source, not iridium-192, acetone peroxide",
effect: () => {
tech.isSmallExplosion = true;
},
remove() {
tech.isSmallExplosion = false;
}
},
{
name: "acetone peroxide",
description: "increase explosive radius by 70%, but
you take 50% more harm from explosions",
isGunTech: true,
maxCount: 1,
count: 0,
frequency: 2,
frequencyDefault: 2,
isBadRandomOption: true,
allowed() {
return tech.hasExplosiveDamageCheck() && !tech.isSmallExplosion
},
requires: "an explosive damage source, not nitroglycerin",
effect: () => {
tech.isExplosionHarm = true;
},
remove() {
tech.isExplosionHarm = false;
}
},
{
name: "shock wave",
description: "mines and explosions stun for 1-2 seconds
decrease explosive damage by 30%",
isGunTech: true,
maxCount: 1,
count: 0,
frequency: 1,
frequencyDefault: 1,
allowed() {
return tech.haveGunCheck("mine") || (!tech.isExplodeRadio && tech.hasExplosiveDamageCheck())
},
requires: "an explosive damage source, not iridium-192",
effect() {
tech.isExplosionStun = true;
},
remove() {
tech.isExplosionStun = false;
}
},
// {
// name: "blast mines",
// link: `blast mines`,
// description: "when a mine activates
it stuns nearby mobs for 2-4 seconds",
// isGunTech: true,
// maxCount: 1,
// count: 0,
// frequency: 2,
// frequencyDefault: 2,
// allowed() {
// return tech.haveGunCheck("mine")
// },
// requires: "mines",
// effect() {
// tech.isMineStun = true;
// },
// remove() {
// tech.isMineStun = false;
// }
// },
{
name: "controlled explosion",
description: `use ${powerUps.orb.research(4)} to dynamically reduce all
explosions until they do no harm`,
isGunTech: true,
maxCount: 1,
count: 0,
frequency: 2,
frequencyDefault: 2,
allowed() {
return !tech.isImmuneExplosion && (build.isExperimentSelection || powerUps.research.count > 3) && (tech.haveGunCheck("missiles") || tech.isMissileField || tech.missileBotCount > 0 || tech.isIncendiary || tech.isPulseLaser || tech.isTokamak || (tech.haveGunCheck("grenades") && !tech.isNeutronBomb))
},
requires: "an explosive damage source, not electric reactive armor",
effect: () => {
tech.isSmartRadius = true;
for (let i = 0; i < 4; i++) {
if (powerUps.research.count > 0) powerUps.research.changeRerolls(-1)
}
},
remove() {
tech.isSmartRadius = false;
if (this.count > 0) powerUps.research.changeRerolls(4)
}
},
{
name: "electric armor",
// description: "explosions do no harm
while your energy is above 98%",
description: "explosion harm is reduced by 99%, but
they drain 15 energy and have more force",
isGunTech: true,
maxCount: 1,
count: 0,
frequency: 2,
frequencyDefault: 2,
allowed() {
return !tech.isSmartRadius && !tech.isExplodeRadio && tech.hasExplosiveDamageCheck() && !tech.isEnergyHealth
},
requires: "an explosive damage source, not iridium-192, mass-energy",
effect: () => {
tech.isImmuneExplosion = true;
},
remove() {
tech.isImmuneExplosion = false;
}
},
{
name: "MIRV",
description: "fire +1 missile and grenade per shot
decrease explosion damage and radius 12%",
isGunTech: true,
maxCount: 9,
count: 0,
frequency: 2,
frequencyDefault: 2,
allowed() {
return tech.haveGunCheck("missiles") || tech.missileBotCount || tech.haveGunCheck("grenades")
},
requires: "missiles, grenades",
effect() {
tech.missileCount++;
},
remove() {
tech.missileCount = 1;
}
},
{
name: "rocket-propelled grenade",
description: "grenades rapidly accelerate forward
map collisions trigger an explosion",
isGunTech: true,
maxCount: 1,
count: 0,
frequency: 2,
frequencyDefault: 2,
allowed() {
return tech.haveGunCheck("grenades")
},
requires: "grenades",
effect() {
tech.isRPG = true;
b.setGrenadeMode()
},
remove() {
tech.isRPG = false;
b.setGrenadeMode()
}
},
{
name: "vacuum bomb",
description: "grenades fire slower, explode bigger
and, suck everything towards them",
isGunTech: true,
maxCount: 1,
count: 0,
frequency: 2,
frequencyDefault: 2,
allowed() {
return tech.haveGunCheck("grenades") && !tech.isNeutronBomb && !tech.isBlockExplode
},
requires: "grenades, not neutron bomb, chain reaction",
effect() {
tech.isVacuumBomb = true;
b.setGrenadeMode()
},
remove() {
tech.isVacuumBomb = false;
b.setGrenadeMode()
}
},
{
name: "chain reaction",
description: "increase grenade radius and damage 33%
blocks caught in explosions also explode",
isGunTech: true,
maxCount: 1,
count: 0,
frequency: 2,
frequencyDefault: 2,
allowed() {
return tech.haveGunCheck("grenades") && !tech.isExplodeRadio && !tech.isNeutronBomb && !tech.isVacuumBomb
},
requires: "grenades, not iridium-192, neutron bomb, vacuum bomb",
effect() {
tech.isBlockExplode = true; //chain reaction
},
remove() {
tech.isBlockExplode = false;
}
},
{
name: "flame test",
description: "when grenades detonate they release
a colorful cluster of small explosions",
isGunTech: true,
maxCount: 1,
count: 0,
frequency: 2,
frequencyDefault: 2,
allowed() {
return tech.haveGunCheck("grenades") && !tech.isNeutronBomb && !tech.isCircleExplode && !tech.isPetalsExplode
},
requires: "grenades, not neutron bomb, pyrotechnics, fireworks",
effect() {
tech.isClusterExplode = true;
},
remove() {
tech.isClusterExplode = false;
}
},
{
name: "pyrotechnics",
description: "when grenades detonate they release
a colorful circle of explosions",
isGunTech: true,
maxCount: 1,
count: 0,
frequency: 2,
frequencyDefault: 2,
allowed() {
return tech.haveGunCheck("grenades") && !tech.isNeutronBomb && !tech.isClusterExplode && !tech.isPetalsExplode
},
requires: "grenades, not neutron bomb, flame test, fireworks",
effect() {
tech.isCircleExplode = true;
},
remove() {
tech.isCircleExplode = false;
}
},
{
name: "fireworks",
description: "when grenades detonate they release
colorful rings petals of explosions",
isGunTech: true,
maxCount: 1,
count: 0,
frequency: 2,
frequencyDefault: 2,
allowed() {
return tech.haveGunCheck("grenades") && !tech.isNeutronBomb && !tech.isClusterExplode && !tech.isCircleExplode
},
requires: "grenades, not neutron bomb, pyrotechnics, flame test",
effect() {
tech.isPetalsExplode = true;
},
remove() {
tech.isPetalsExplode = false;
}
},
{
name: "neutron bomb",
description: "grenades are irradiated with Cf-252
does damage, harm, and drains energy",
isGunTech: true,
maxCount: 1,
count: 0,
frequency: 2,
frequencyDefault: 2,
allowed() {
return tech.haveGunCheck("grenades") && !tech.fragments && !tech.isVacuumBomb && !tech.isExplodeRadio && !tech.isBlockExplode && !tech.isClusterExplode && !tech.isPetalsExplode && !tech.isCircleExplode
},
requires: "grenades, not fragmentation, vacuum bomb, iridium-192, pyrotechnics, fireworks, flame test",
effect() {
tech.isNeutronBomb = true;
b.setGrenadeMode()
},
remove() {
tech.isNeutronBomb = false;
b.setGrenadeMode()
}
},
{
name: "vacuum permittivity",
description: "increase radioactive range by 20%
objects in range of the bomb are slowed",
isGunTech: true,
maxCount: 1,
count: 0,
frequency: 2,
frequencyDefault: 2,
allowed() {
return tech.isNeutronBomb
},
requires: "grenades, neutron bomb",
effect() {
tech.isNeutronSlow = true
},
remove() {
tech.isNeutronSlow = false
}
},
{
name: "radioactive contamination",
description: "after a mob or shield dies,
leftover radiation spreads to a nearby mob",
isGunTech: true,
maxCount: 1,
count: 0,
frequency: 2,
frequencyDefault: 2,
allowed() {
return tech.isNailRadiation || tech.isWormholeDamage || tech.isNeutronBomb || tech.isExplodeRadio || tech.isBlockRadiation
},
requires: "radiation damage source",
effect() {
tech.isRadioactive = true
},
remove() {
tech.isRadioactive = false
}
},
{
name: "nuclear transmutation",
description: "radiation does 47% more damage and harm
nail, drone, neutron bomb, iridium, string, deflect",
isGunTech: true,
maxCount: 9,
count: 0,
frequency: 2,
frequencyDefault: 2,
allowed() {
return tech.isNailRadiation || tech.isWormholeDamage || tech.isNeutronBomb || tech.isExplodeRadio || tech.isBlockRadiation || tech.isDroneRadioactive
},
requires: "radiation damage source",
effect() {
tech.radioactiveDamage += 1.47
},
remove() {
tech.radioactiveDamage = 1
}
},
{
name: "water shielding",
link: `water shielding`,
description: "radioactive effects on you are reduced by 75%
neutron bomb, drones, explosions, slime",
isGunTech: true,
maxCount: 1,
count: 0,
frequency: 2,
frequencyDefault: 2,
allowed() {
return tech.isNeutronBomb || tech.isDroneRadioactive || tech.isExplodeRadio
},
requires: "neutron bomb or irradiated drones or iridium-192",
effect() {
tech.isRadioactiveResistance = true
},
remove() {
tech.isRadioactiveResistance = false
}
},
{
name: "booby trap",
description: "60% chance to drop a mine from power ups
+46% JUNK to the potential tech pool",
isGunTech: true,
maxCount: 1,
count: 0,
frequency: 2,
frequencyDefault: 2,
allowed() {
return tech.haveGunCheck("mine")
},
requires: "mines",
effect() {
tech.isMineDrop = true;
if (tech.isMineDrop) b.mine(m.pos, { x: 0, y: 0 }, 0)
this.refundAmount += tech.addJunkTechToPool(0.46)
},
refundAmount: 0,
remove() {
tech.isMineDrop = false;
if (this.count > 0 && this.refundAmount > 0) {
tech.removeJunkTechFromPool(this.refundAmount)
this.refundAmount = 0
}
}
},
{
name: "laser-mines",
link: `laser-mines`,
description: "mines laid while you are crouched
use energy to emit 3 unaimed lasers",
isGunTech: true,
maxCount: 1,
count: 0,
frequency: 2,
frequencyDefault: 2,
allowed() {
return tech.haveGunCheck("mine")
},
requires: "mines",
effect() {
tech.isLaserMine = true;
},
remove() {
tech.isLaserMine = false;
}
},
{
name: "sentry",
description: "instead of detonating, mines target mobs
with a stream of nails for about 17 seconds",
isGunTech: true,
maxCount: 1,
count: 0,
frequency: 2,
frequencyDefault: 2,
allowed() {
return tech.haveGunCheck("mine")
},
requires: "mines",
effect() {
tech.isMineSentry = true;
},
remove() {
tech.isMineSentry = false;
}
},
{
name: "mycelial fragmentation",
link: `mycelial fragmentation`,
description: "sporangium release 6 more spores
during their growth phase",
isGunTech: true,
maxCount: 1,
count: 0,
frequency: 2,
frequencyDefault: 2,
allowed() {
return tech.haveGunCheck("spores")
},
requires: "spores",
effect() {
tech.isSporeGrowth = true
},
remove() {
tech.isSporeGrowth = false
}
},
{
name: "tinsellated flagella",
link: `tinsellated flagella`,
description: "sporangium release 2 more spores
spores accelerate 50% faster",
isGunTech: true,
maxCount: 1,
count: 0,
frequency: 2,
frequencyDefault: 2,
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.5 second",
//
spores do 1/3 damage
isGunTech: true,
maxCount: 1,
count: 0,
frequency: 2,
frequencyDefault: 2,
allowed() {
return tech.haveGunCheck("spores") || tech.sporesOnDeath > 0 || tech.isSporeField || tech.isSporeWorm
},
requires: "spores",
effect() {
tech.isSporeFreeze = true
},
remove() {
tech.isSporeFreeze = false
}
},
{
name: "diplochory",
description: "spores use you for dispersal
until they locate a viable host",
isGunTech: true,
maxCount: 1,
count: 0,
frequency: 2,
frequencyDefault: 2,
allowed() {
return tech.haveGunCheck("spores") || tech.sporesOnDeath > 0 || tech.isSporeField || tech.isSporeWorm
},
requires: "spores",
effect() {
tech.isSporeFollow = true
},
remove() {
tech.isSporeFollow = false
}
},
{
name: "mutualism",
description: "increase spore damage by 150%
they borrow 0.5 health until they die",
isGunTech: true,
maxCount: 1,
count: 0,
frequency: 2,
frequencyDefault: 2,
allowed() {
return (tech.haveGunCheck("spores") || tech.sporesOnDeath > 0 || tech.isSporeField) && !tech.isEnergyHealth || tech.isSporeWorm
},
requires: "spores, not mass-energy",
effect() {
tech.isMutualism = true
},
remove() {
tech.isMutualism = false
}
},
// {
// name: "worm-shot",
// link: `worm-shot`,
// description: "shotgun hatches 3-4 mob seeking worms
worms benefit from spore technology", //
worms seek out nearby mobs
// isGunTech: true,
// maxCount: 1,
// count: 0,
// frequency: 2,
// frequencyDefault: 2,
// allowed() {
// return tech.haveGunCheck("shotgun") && !tech.isNailShot && !tech.isIncendiary && !tech.isRivets && !tech.isIceShot && !tech.isFoamShot && !tech.isNeedles
// },
// requires: "shotgun, not incendiary, nail-shot, rivets, foam-shot, ice-shot, needles",
// effect() {
// tech.isWormShot = true;
// },
// remove() {
// tech.isWormShot = false;
// }
// },
{
name: "nematodes",
description: "shotgun and sporangium hatch worms
spore tech applies to worms",
isGunTech: true,
maxCount: 1,
count: 0,
frequency: 3,
frequencyDefault: 3,
allowed() {
return tech.haveGunCheck("spores") || tech.sporesOnDeath > 0 || tech.isSporeField || (tech.haveGunCheck("shotgun") && !tech.isIncendiary && !tech.isRivets && !tech.isIceShot && !tech.isFoamShot && !tech.isNeedles && !tech.isNailShot)
},
requires: "spores",
effect() {
tech.isSporeWorm = true
},
remove() {
tech.isSporeWorm = false
}
},
{
name: "annelids",
description: "increase worm size and damage
by an average of 37%",
isGunTech: true,
maxCount: 3,
count: 0,
frequency: 3,
frequencyDefault: 3,
allowed() {
return tech.isSporeWorm
},
requires: "spores, shotgun, worms",
effect() {
tech.wormSize++
},
remove() {
tech.wormSize = 0
}
},
{
name: "path integration",
description: "drones, spores, and worms
travel with you through levels",
isGunTech: true,
maxCount: 1,
count: 0,
frequency: 2,
frequencyDefault: 2,
allowed() {
return (tech.isSporeFollow && (tech.haveGunCheck("spores") || (tech.haveGunCheck("shotgun") && tech.isSporeWorm))) || tech.haveGunCheck("drones") || (m.fieldUpgrades[m.fieldMode].name === "molecular assembler" && !(tech.isMissileField || tech.isIceField))
},
requires: "spores, worms, diplochory, drones",
effect() {
tech.isDronesTravel = true
},
remove() {
tech.isDronesTravel = false
}
},
{
name: "anti-shear topology",
link: `anti-shear topology`,
description: "some projectiles last 30% longer
drone, spore, worm, missile, foam, wave, neutron, ice",
isGunTech: true,
maxCount: 3,
count: 0,
frequency: 2,
frequencyDefault: 2,
allowed() {
return m.fieldUpgrades[m.fieldMode].name === "molecular assembler" || tech.haveGunCheck("spores") || tech.haveGunCheck("drones") || tech.haveGunCheck("missiles") || tech.haveGunCheck("foam") || tech.haveGunCheck("matter wave") || tech.isNeutronBomb || tech.isIceField || tech.isIceShot || tech.relayIce || tech.isNeedleIce || tech.blockingIce > 1 || tech.isSporeWorm || tech.isFoamBotUpgrade || tech.isFoamBall
},
requires: "drones, spores, missiles, foam, matter wave, neutron bomb, ice IX",
effect() {
tech.isBulletsLastLonger += 0.3
},
remove() {
tech.isBulletsLastLonger = 1;
}
},
{
name: "reduced tolerances",
link: `reduced tolerances`,
description: `increase drones per ${powerUps.orb.ammo()} or energy by 66%
reduce average drone durability by 40%`,
isGunTech: true,
maxCount: 3,
count: 0,
frequency: 2,
frequencyDefault: 2,
allowed() {
return !tech.isDroneRadioactive && (tech.haveGunCheck("drones") || (m.fieldUpgrades[m.fieldMode].name === "molecular assembler" && !(tech.isSporeField || tech.isMissileField || tech.isIceField)))
},
requires: "drones, not irradiated drones",
effect() {
tech.droneCycleReduction = Math.pow(0.6, 1 + this.count)
tech.droneEnergyReduction = Math.pow(0.333, 1 + this.count)
for (i = 0, len = b.guns.length; i < len; i++) { //find which gun
if (b.guns[i].name === "drones") {
const scale = Math.pow(3, this.count + 1)
b.guns[i].ammoPack = b.guns[i].defaultAmmoPack * scale
}
}
},
remove() {
tech.droneCycleReduction = 1
tech.droneEnergyReduction = 1
for (i = 0, len = b.guns.length; i < len; i++) { //find which gun
if (b.guns[i].name === "drones") b.guns[i].ammoPack = b.guns[i].defaultAmmoPack
}
}
},
{
name: "delivery drone",
description: "if a drone picks up a power up,
it becomes larger, faster, and more durable",
isGunTech: true,
maxCount: 1,
count: 0,
frequency: 2,
frequencyDefault: 2,
allowed() {
return !tech.isExtraMaxEnergy && (tech.haveGunCheck("drones") || tech.isForeverDrones || (m.fieldUpgrades[m.fieldMode].name === "molecular assembler" && !(tech.isSporeField || tech.isMissileField || tech.isIceField)))
},
requires: "drones, not weak interaction",
effect() {
tech.isDroneGrab = true
},
remove() {
tech.isDroneGrab = false
}
},
{
name: "drone repair",
link: `drone repair`,
description: "after a drone expires it redeploys
for a 20% chance to use 1 drone ammo",
// description: "broken drones repair if the drone gun is active
repairing has a 25% chance to use 1 drone",
isGunTech: true,
maxCount: 1,
count: 0,
frequency: 2,
frequencyDefault: 2,
allowed() {
return tech.haveGunCheck("drones")
},
requires: "drones",
effect() {
tech.isDroneRespawn = true
},
remove() {
tech.isDroneRespawn = false
}
},
{
name: "brushless motor",
description: "drones rapidly rush towards their target
increase drone collision damage by 33%",
isGunTech: true,
maxCount: 1,
count: 0,
frequency: 3,
frequencyDefault: 3,
allowed() {
return (tech.haveGunCheck("drones") || tech.isForeverDrones || (m.fieldUpgrades[m.fieldMode].name === "molecular assembler" && !(tech.isSporeField || tech.isMissileField || tech.isIceField))) && !tech.isDroneRadioactive && !tech.isIncendiary
},
requires: "drones, molecular assembler, not irradiated drones, incendiary",
effect() {
tech.isDroneTeleport = true
},
remove() {
tech.isDroneTeleport = false
}
},
{
name: "axial flux motor",
description: "drones can rush 66% more often
increase drone collision damage by 44%",
isGunTech: true,
maxCount: 1,
count: 0,
frequency: 2,
frequencyDefault: 2,
allowed() {
return tech.isDroneTeleport
},
requires: "drones, brushless motor",
effect() {
tech.isDroneFastLook = true
},
remove() {
tech.isDroneFastLook = false
}
},
{
name: "irradiated drones",
link: `irradiated drones`,
description: `the space around drones is irradiated
reduce drones per ${powerUps.orb.ammo()} or energy 75%`,
isGunTech: true,
maxCount: 1,
count: 0,
frequency: 2,
frequencyDefault: 2,
allowed() {
return tech.droneCycleReduction === 1 && !tech.isIncendiary && !tech.isDroneTeleport && (tech.haveGunCheck("drones") || tech.isForeverDrones || (m.fieldUpgrades[m.fieldMode].name === "molecular assembler" && !(tech.isSporeField || tech.isMissileField || tech.isIceField)))
},
requires: "drones, not reduced tolerances, incendiary, torque bursts",
effect() {
tech.isDroneRadioactive = true
for (i = 0, len = b.guns.length; i < len; i++) { //find which gun
if (b.guns[i].name === "drones") {
b.guns[i].ammoPack = b.guns[i].defaultAmmoPack * 0.25
b.guns[i].ammo = Math.ceil(b.guns[i].ammo * 0.25)
simulation.makeGunHUD();
}
}
},
remove() {
if (tech.isDroneRadioactive) {
tech.isDroneRadioactive = false
for (i = 0, len = b.guns.length; i < len; i++) { //find which gun
if (b.guns[i].name === "drones") {
b.guns[i].ammoPack = b.guns[i].defaultAmmoPack
b.guns[i].ammo = b.guns[i].ammo * 4
simulation.makeGunHUD();
}
}
}
}
},
{
name: "beta radiation", //"control rod ejection",
description: "reduce the average drone lifetime by 50%
increase radiation damage by 100%",
isGunTech: true,
maxCount: 1,
count: 0,
frequency: 2,
frequencyDefault: 2,
allowed() {
return tech.isDroneRadioactive
},
requires: "drones, irradiated drones",
effect() {
tech.droneRadioDamage = 2
},
remove() {
tech.droneRadioDamage = 1
}
},
{
name: "orthocyclic winding",
link: `orthocyclic winding`,
description: "drones accelerate 66% faster
increase radiation damage by 33%",
isGunTech: true,
maxCount: 1,
count: 0,
frequency: 2,
frequencyDefault: 2,
allowed() {
return tech.isDroneRadioactive
},
requires: "drones, irradiated drones",
effect() {
tech.isFastDrones = true
},
remove() {
tech.isFastDrones = false
}
},
{
name: "fault tolerance",
description: "spawn 6 drones that last forever
remove your drone gun",
isGunTech: true,
isRemoveGun: true,
maxCount: 1,
count: 0,
frequency: 1,
frequencyDefault: 1,
allowed() {
return tech.haveGunCheck("drones", false) && !tech.isDroneRespawn && tech.isBulletsLastLonger === 1 && !tech.isDronesTravel
},
requires: "drones, not drone repair, anti-shear topology, autonomous navigation",
effect() {
const num = 6
tech.isForeverDrones += num
if (tech.haveGunCheck("drones", false)) b.removeGun("drones")
//spawn drones
if (tech.isDroneRadioactive) {
for (let i = 0; i < num * 0.25; i++) {
b.droneRadioactive({ x: m.pos.x + 30 * (Math.random() - 0.5), y: m.pos.y + 30 * (Math.random() - 0.5) }, 5)
bullet[bullet.length - 1].endCycle = Infinity
}
} else {
for (let i = 0; i < num; i++) {
b.drone({ x: m.pos.x + 30 * (Math.random() - 0.5), y: m.pos.y + 30 * (Math.random() - 0.5) }, 5)
bullet[bullet.length - 1].endCycle = Infinity
}
}
},
remove() {
tech.isForeverDrones = 0
if (this.count && !tech.haveGunCheck("drones", false)) b.giveGuns("drones")
}
},
{
name: "surfactant",
description: "trade your foam gun for 2 foam-bots
and upgrade all bots to foam",
isGunTech: true,
isRemoveGun: true,
maxCount: 1,
count: 0,
frequency: 1,
frequencyDefault: 1,
isBot: true,
isBotTech: true,
isNonRefundable: true,
requires: "at least 2 guns, foam gun, NOT EXPERIMENT MODE, bot upgrades, fractionation, pressure vessel",
allowed() {
return b.inventory.length > 1 && tech.haveGunCheck("foam", false) && !b.hasBotUpgrade() && !tech.isAmmoFoamSize && !tech.isFoamPressure
},
effect() {
tech.giveTech("foam-bot upgrade")
for (let i = 0; i < 2; i++) {
b.foamBot()
tech.foamBotCount++;
}
simulation.makeTextLog(`tech.isFoamBotUpgrade = true`)
if (tech.haveGunCheck("foam", false)) b.removeGun("foam")
},
remove() {
// if (this.count) {
// b.clearPermanentBots();
// b.respawnBots();
// if (!tech.haveGunCheck("foam")) b.giveGuns("foam")
// }
}
},
{
name: "electrostatic induction",
description: "foam bubbles are electrically charged
causing attraction to nearby mobs",
isGunTech: true,
maxCount: 1,
count: 0,
frequency: 2,
frequencyDefault: 2,
allowed() {
return !tech.isBulletTeleport && (tech.haveGunCheck("foam") || tech.isFoamBotUpgrade || tech.isFoamShot || tech.isFoamBall)
},
requires: "foam, not uncertainty",
effect() {
tech.isFoamAttract = true
},
remove() {
tech.isFoamAttract = false
}
},
{
name: "uncertainty principle",
description: "foam and wave particle positions are random
increase their damage by 47%",
isGunTech: true,
maxCount: 1,
count: 0,
frequency: 2,
frequencyDefault: 2,
allowed() {
return (!tech.isFoamAttract && (tech.haveGunCheck("foam") || tech.isFoamBotUpgrade || tech.isFoamShot || tech.isFoamBall)) || (tech.haveGunCheck("matter wave") && !tech.isLongitudinal)
},
requires: "foam, matter wave, not electrostatic induction, not phonon",
effect() {
tech.isBulletTeleport = true
},
remove() {
tech.isBulletTeleport = false;
}
},
{
name: "necrophage",
description: "if foam or worms kill their target
grow 3 copies",
isGunTech: true,
maxCount: 1,
count: 0,
frequency: 2,
frequencyDefault: 2,
allowed() {
return tech.haveGunCheck("foam") || tech.isFoamBall || tech.isFoamBotUpgrade || tech.isFoamShot || tech.isSporeWorm
},
requires: "foam, worms",
effect() {
tech.isSpawnBulletsOnDeath = true
},
remove() {
tech.isSpawnBulletsOnDeath = false;
}
},
{
name: "aerogel",
description: "foam bubbles float and dissipate 50% faster
increase foam damage per second by 150%",
isGunTech: true,
maxCount: 1,
count: 0,
frequency: 2,
frequencyDefault: 2,
allowed() {
return tech.haveGunCheck("foam") || tech.isFoamBotUpgrade || tech.isFoamShot || tech.isFoamBall
},
requires: "foam",
effect() {
tech.isFastFoam = true
tech.foamGravity = -0.0003
},
remove() {
tech.isFastFoam = false;
tech.foamGravity = 0.00008
}
},
{
name: "surface tension",
description: "foam bubbles have improved adhesion which
does 41% more damage",
isGunTech: true,
maxCount: 9,
count: 0,
frequency: 2,
frequencyDefault: 2,
allowed() {
return tech.haveGunCheck("foam") || tech.isFoamBotUpgrade || tech.isFoamShot || tech.isFoamBall
},
requires: "foam",
effect() {
tech.foamDamage += 0.011 * 0.41
},
remove() {
tech.foamDamage = 0.011;
}
},
{
name: "foam fractionation",
description: "foam gun bubbles are 100% larger
when you have below 300 ammo",
isGunTech: true,
maxCount: 1,
count: 0,
frequency: 2,
frequencyDefault: 2,
allowed() {
return tech.haveGunCheck("foam")
},
requires: "foam",
effect() {
tech.isAmmoFoamSize = true
},
remove() {
tech.isAmmoFoamSize = false;
}
},
{
name: "ideal gas law",
description: `flush away all of your current foam ammo
gain 1200% more foam ammo from ${powerUps.orb.ammo(1)}`,
isGunTech: true,
maxCount: 1,
count: 0,
frequency: 2,
frequencyDefault: 2,
allowed() {
return tech.haveGunCheck("foam") && !tech.isEnergyNoAmmo
},
requires: "foam, not exciton",
ammoLost: 0,
effect() {
b.guns[8].ammoPack = b.guns[8].ammoPack * 12;
this.ammoLost = b.guns[8].ammo
b.guns[8].ammo = 0
simulation.updateGunHUD()
},
remove() {
b.guns[8].ammoPack = 24
if (this.count) {
b.guns[8].ammo += this.ammoLost
simulation.updateGunHUD()
}
}
},
{
name: "pressure vessel",
description: "the foam gun builds up pressure as you fire
that discharges foam after firing",
isGunTech: true,
maxCount: 1,
count: 0,
frequency: 2,
frequencyDefault: 2,
allowed() {
return tech.haveGunCheck("foam")
},
requires: "foam",
effect() {
tech.isFoamPressure = true;
for (i = 0, len = b.guns.length; i < len; i++) { //find which gun
if (b.guns[i].name === "foam") {
b.guns[i].chooseFireMethod()
break
}
}
},
remove() {
tech.isFoamPressure = false;
for (i = 0, len = b.guns.length; i < len; i++) { //find which gun
if (b.guns[i].name === "foam") {
b.guns[i].chooseFireMethod()
break
}
}
}
},
{
name: "capacitor bank",
// description: "charge effects build up almost instantly
throwing blocks, foam, railgun, pulse, tokamak",
descriptionFunction() { return `charge effects build up almost instantly
throwing, ${tech.haveGunCheck("foam", false) ? "foam" : "foam"}, ${tech.isPlasmaBall ? "plasma ball" : "plasma ball"}, ${tech.isRailGun ? "railgun" : "railgun"}, ${tech.isPulseLaser ? "pulse" : "pulse"}, ${tech.isTokamak ? "tokamak" : "tokamak"}` },
isGunTech: true,
maxCount: 1,
count: 0,
frequency: 2,
frequencyDefault: 2,
allowed() {
return tech.blockDamage > 0.075 || tech.isRailGun || (tech.haveGunCheck("foam") && tech.isFoamPressure) || tech.isTokamak || tech.isPulseLaser || tech.isPlasmaBall
},
requires: "throwing blocks, railgun, foam, pulse, tokamak, plasma ball",
effect() {
tech.isCapacitor = true;
},
remove() {
tech.isCapacitor = false;
}
},
{
name: "railgun",
description: `harpoons are 50% denser, but don't retract
gain 800% more harpoon ammo per ${powerUps.orb.ammo(1)}`,
isGunTech: true,
maxCount: 1,
count: 0,
frequency: 2,
frequencyDefault: 2,
allowed() {
return tech.haveGunCheck("harpoon") && !tech.isFilament && !tech.isHarpoonPowerUp && !tech.isGrapple
},
requires: "harpoon, not UHMWPE, induction furnace, grappling hook",
ammoBonus: 8,
effect() {
tech.isRailGun = true;
for (i = 0, len = b.guns.length; i < len; i++) { //find which gun
if (b.guns[i].name === "harpoon") {
b.guns[i].chooseFireMethod()
b.guns[i].ammoPack = this.ammoBonus;
b.guns[i].ammo = b.guns[i].ammo * this.ammoBonus;
simulation.updateGunHUD();
break
}
}
},
remove() {
if (tech.isRailGun) {
tech.isRailGun = false;
for (i = 0, len = b.guns.length; i < len; i++) { //find which gun
if (b.guns[i].name === "harpoon") {
b.guns[i].chooseFireMethod()
b.guns[i].ammoPack = 0.6;
b.guns[i].ammo = Math.ceil(b.guns[i].ammo / this.ammoBonus);
simulation.updateGunHUD();
break
}
}
}
}
},
// {
// name: "dielectric polarization",
// description: "firing the railgun damages nearby mobs",
// isGunTech: true,
// maxCount: 1,
// count: 0,
// frequency: 2,
// frequencyDefault: 2,
// allowed() {
// return tech.haveGunCheck("railgun")
// },
// requires: "railgun",
// effect() {
// tech.isRailAreaDamage = true;
// },
// remove() {
// tech.isRailAreaDamage = false;
// }
// },
// {
// name: "aerodynamic heating",
// description: "railgun rod damage nearby mobs",
// isGunTech: true,
// maxCount: 1,
// count: 0,
// frequency: 2,
// frequencyDefault: 2,
// allowed() {
// return tech.haveGunCheck("railgun")
// },
// requires: "railgun",
// effect() {
// tech.isRodAreaDamage = true;
// },
// remove() {
// tech.isRodAreaDamage = false;
// }
// },
{
name: "grappling hook",
description: `harpoons attach to the map and pull you
your rope extends while holding fire`,
isGunTech: true,
maxCount: 1,
count: 0,
frequency: 2,
frequencyDefault: 2,
allowed() {
return tech.haveGunCheck("harpoon") && !tech.isFilament && !tech.isHarpoonPowerUp && !tech.isRailGun && !tech.isFireMoveLock
},
requires: "harpoon, not railgun, filament, toggling harpoon, Higgs mechanism",
effect() {
tech.isGrapple = true;
for (i = 0, len = b.guns.length; i < len; i++) { //find which gun
if (b.guns[i].name === "harpoon") b.guns[i].chooseFireMethod()
}
},
remove() {
if (tech.isGrapple) {
tech.isGrapple = false;
for (i = 0, len = b.guns.length; i < len; i++) { //find which gun
if (b.guns[i].name === "harpoon") b.guns[i].chooseFireMethod()
}
}
}
},
{
name: "bulk modulus",
description: `become immune to harm while grappling
drains energy and prevents regen`,
isGunTech: true,
maxCount: 1,
count: 0,
frequency: 2,
frequencyDefault: 2,
allowed() {
return tech.isGrapple && !tech.isRailEnergyGain
},
requires: "grappling hook, not alternator",
effect() {
tech.isImmuneGrapple = true;
},
remove() {
tech.isImmuneGrapple = false
}
},
{
name: "alternator",
description: "harpoon, railgun, and grappling hook
drain 60% less energy",
isGunTech: true,
maxCount: 1,
count: 0,
frequency: 2,
frequencyDefault: 2,
allowed() {
return tech.haveGunCheck("harpoon") && !tech.isImmuneGrapple
},
requires: "harpoon, not bulk modulus",
effect() {
tech.isRailEnergyGain = true;
},
remove() {
tech.isRailEnergyGain = false;
}
},
{
name: "ceramics",
description: `needles and harpoons pierce shields
directly damaging shielded mobs`,
isGunTech: true,
maxCount: 1,
count: 0,
frequency: 2,
frequencyDefault: 2,
allowed() {
return (!tech.isLargeHarpoon && tech.haveGunCheck("harpoon")) || tech.isNeedles
},
requires: "needle gun, harpoon, not Bessemer process",
effect() {
tech.isShieldPierce = true
},
remove() {
tech.isShieldPierce = false
}
},
{
name: "Bessemer process",
descriptionFunction() {
return `increase harpoon size and damage
by 1/10 √ harpoon ammo (${(10 * Math.sqrt(b.guns[9].ammo)).toFixed(0)}%)`
},
// description: "increase the size of your harpoon
by 10% of √ of harpoon ammo",
isGunTech: true,
maxCount: 1,
count: 0,
frequency: 2,
frequencyDefault: 2,
allowed() {
return tech.haveGunCheck("harpoon") && !tech.isShieldPierce
},
requires: "harpoon, not ceramics",
effect() {
tech.isLargeHarpoon = true;
},
remove() {
tech.isLargeHarpoon = false;
}
},
{
name: "smelting",
// description: `forge 3 ammo into a new harpoon
fire +1 harpoon with each shot`,
descriptionFunction() { return `forge ${(tech.isRailGun ?5:3)*(2+this.count)} ammo into a new harpoon
fire +1 harpoon with each shot` },
// description: `spend ${powerUps.orb.ammo(2)}to upgrade the harpoon
fire +1 harpoon with each shot`,
// descriptionFunction() { return `forge ${tech.isRailGun? 10: 2} ammo into a new harpoon
fire +1 harpoon with each shot` },
isGunTech: true,
maxCount: 9,
count: 0,
frequency: 2,
frequencyDefault: 2,
allowed() {
return tech.haveGunCheck("harpoon") && b.returnGunAmmo('harpoon') >= (tech.isRailGun ? 5 : 3) * (1 + this.count)
},
requires: "harpoon",
effect() {
for (i = 0, len = b.guns.length; i < len; i++) { //find which gun
if (b.guns[i].name === "harpoon") {
b.guns[i].ammo -= (tech.isRailGun ? 5 : 3) * (1 + this.count)
// console.log(3 + this.count * 3)
if (b.guns[i].ammo < 0) b.guns[i].ammo = 0
simulation.updateGunHUD();
tech.extraHarpoons++;
break
}
}
// this.description = `forge ${3+(this.count+1)*3} ammo into a new harpoon
fire +1 harpoon with each shot`
},
remove() {
if (tech.extraHarpoons) {
// this.description = `forge ${2} ammo into a new harpoon
fire +1 harpoon with each shot`
for (i = 0, len = b.guns.length; i < len; i++) { //find which gun
if (b.guns[i].name === "harpoon") {
b.guns[i].ammo += (tech.isRailGun ? 5 : 3)
simulation.updateGunHUD();
break
}
}
}
tech.extraHarpoons = 0;
}
},
{
name: "UHMWPE",
descriptionFunction() {
return `increase the length of your harpoon's rope
by 1/100 of harpoon ammo (${(b.guns[9].ammo).toFixed(0)}%)`
},
// description: "increase the length of your harpoon's rope
by 1% per harpoon ammo",
isGunTech: true,
maxCount: 1,
count: 0,
frequency: 2,
frequencyDefault: 2,
allowed() {
return tech.haveGunCheck("harpoon") && !tech.isRailGun && !tech.isGrapple
},
requires: "harpoon, not grappling hook, railgun",
effect() {
tech.isFilament = true;
},
remove() {
tech.isFilament = false;
}
},
{
name: "induction furnace",
description: "increase the damage of your next harpoon
by 600% after using it to collect a power up",
isGunTech: true,
maxCount: 1,
count: 0,
frequency: 2,
frequencyDefault: 2,
allowed() {
return tech.haveGunCheck("harpoon") && !tech.isRailGun && !tech.isGrapple
},
requires: "harpoon, not grappling hook, railgun",
effect() {
tech.isHarpoonPowerUp = true
},
remove() {
tech.isHarpoonPowerUp = false
tech.harpoonDensity = 0.004
}
},
{
name: "optical amplifier",
description: "gain 3 random laser guntech
laser only turns off if you have no energy",
isGunTech: true,
maxCount: 1,
count: 0,
frequency: 2,
frequencyDefault: 2,
isNonRefundable: true,
allowed() {
return tech.haveGunCheck("laser") && !tech.isPulseLaser
},
requires: "laser gun, not pulse",
effect() {
let techGiven = 0
for (let j = 0; j < 3; j++) {
const names = ["laser diode", "free-electron laser", "relativistic momentum", "specular reflection", "diffraction grating", "diffuse beam", "output coupler", "slow light"]
//convert names into indexes
const options = []
for (let i = 0; i < names.length; i++) {
for (let k = 0; k < tech.tech.length; k++) {
if (tech.tech[k].name === names[i]) {
options.push(k)
break
}
}
}
//remove options that don't meet requirements
for (let i = options.length - 1; i > -1; i--) {
const index = options[i]
if (!(tech.tech[index].count < tech.tech[index].maxCount) || !tech.tech[index].allowed()) {
options.splice(i, 1);
}
}
//pick one option
if (options.length) {
const index = options[Math.floor(Math.random() * options.length)]
simulation.makeTextLog(`tech.giveTech("${tech.tech[index].name}") //optical amplifier`);
tech.giveTech(index)
techGiven++
}
}
if (techGiven > 0) {
tech.isStuckOn = true
} else {
simulation.makeTextLog(`0 tech found //optical amplifier`);
const loop = () => {
if (!simulation.paused && m.alive) {
for (let i = 0; i < tech.tech.length; i++) {
if (tech.tech[i].name === this.name) powerUps.ejectTech(i)
}
return
}
requestAnimationFrame(loop);
}
requestAnimationFrame(loop);
}
},
remove() {
tech.isStuckOn = false
}
},
{
name: "laser diode",
description: "all lasers drain 30% less energy
affects laser-gun, laser-bot, laser-mines, pulse",
isGunTech: true,
maxCount: 1,
count: 0,
frequency: 2,
frequencyDefault: 2,
allowed() {
return (tech.haveGunCheck("laser") || tech.isLaserBotUpgrade || tech.isLaserMine) && tech.laserDamage === 0.16
},
requires: "laser, not free-electron",
effect() {
tech.isLaserDiode = 0.70; //100%-37%
tech.laserColor = "rgb(0, 11, 255)"
tech.laserColorAlpha = "rgba(0, 11, 255,0.5)"
},
remove() {
tech.isLaserDiode = 1;
tech.laserColor = "#f02"
tech.laserColorAlpha = "rgba(255, 0, 0, 0.5)"
}
},
{
name: "free-electron laser",
description: "increase all laser damage by 200%
increase all laser energy drain by 250%",
isGunTech: true,
maxCount: 1,
count: 0,
frequency: 2,
frequencyDefault: 2,
allowed() {
return (tech.haveGunCheck("laser") || tech.isLaserMine || tech.isLaserBotUpgrade) && !tech.isPulseLaser && tech.isLaserDiode === 1
},
requires: "laser, not pulse, diodes",
effect() {
tech.laserFieldDrain = 0.0063 //base is 0.002
tech.laserDamage = 0.48; //base is 0.16
tech.laserColor = "#83f"
tech.laserColorAlpha = "rgba(136, 51, 255,0.5)"
},
remove() {
tech.laserFieldDrain = 0.0018;
tech.laserDamage = 0.18; //used in check on pulse and diode: tech.laserDamage === 0.18
tech.laserColor = "#f00"
tech.laserColorAlpha = "rgba(255, 0, 0, 0.5)"
}
},
{
name: "relativistic momentum",
description: "all lasers push mobs and blocks away
affects laser-gun, laser-bot, and laser-mines",
isGunTech: true,
maxCount: 1,
count: 0,
frequency: 2,
frequencyDefault: 2,
allowed() {
return (tech.haveGunCheck("laser") && !tech.isPulseLaser) || tech.isLaserBotUpgrade
},
requires: "laser, not pulse",
effect() {
tech.isLaserPush = true;
},
remove() {
tech.isLaserPush = false;
}
},
{
name: "specular reflection",
description: "+2 reflection for all lasers
affects laser-gun, laser-bot, and laser-mines",
isGunTech: true,
maxCount: 3,
count: 0,
frequency: 2,
frequencyDefault: 2,
allowed() {
return (tech.haveGunCheck("laser") || tech.isLaserMine || tech.isLaserBotUpgrade) && !tech.isWideLaser && !tech.isPulseLaser && !tech.historyLaser
},
requires: "laser, not diffuse beam, pulse, or slow light",
effect() {
tech.laserReflections += 2;
},
remove() {
tech.laserReflections = 2;
}
},
{
name: "diffraction grating",
description: `laser gains a diverging beam`,
isGunTech: true,
maxCount: 9,
count: 0,
frequency: 2,
frequencyDefault: 2,
allowed() {
return tech.haveGunCheck("laser") && !tech.isWideLaser && !tech.isPulseAim && !tech.historyLaser
},
requires: "laser gun, not neocognitron, diffuse beam, or slow light",
effect() {
tech.beamSplitter++
for (i = 0, len = b.guns.length; i < len; i++) { //find which gun
if (b.guns[i].name === "laser") b.guns[i].chooseFireMethod()
}
},
remove() {
if (tech.beamSplitter !== 0) {
tech.beamSplitter = 0
for (i = 0, len = b.guns.length; i < len; i++) { //find which gun
if (b.guns[i].name === "laser") b.guns[i].chooseFireMethod()
}
}
}
},
{
name: "diffuse beam",
link: `diffuse beam`,
description: "laser beam is wider and doesn't reflect
increase full beam damage by 220%",
isGunTech: true,
maxCount: 1,
count: 0,
frequency: 2,
frequencyDefault: 2,
allowed() {
return tech.haveGunCheck("laser") && tech.laserReflections < 3 && !tech.beamSplitter && !tech.isPulseLaser && !tech.historyLaser
},
requires: "laser gun, not specular reflection, diffraction grating, slow light, pulse",
effect() {
if (tech.wideLaser === 0) tech.wideLaser = 3
tech.isWideLaser = true;
for (i = 0, len = b.guns.length; i < len; i++) { //find which gun
if (b.guns[i].name === "laser") b.guns[i].chooseFireMethod()
}
},
remove() {
if (tech.isWideLaser) {
// tech.wideLaser = 0
tech.isWideLaser = false;
for (i = 0, len = b.guns.length; i < len; i++) { //find which gun
if (b.guns[i].name === "laser") b.guns[i].chooseFireMethod()
}
}
}
},
{
name: "output coupler",
description: "widen diffuse laser beam by 30%
increase full beam damage by 30%",
isGunTech: true,
maxCount: 9,
count: 0,
frequency: 2,
frequencyDefault: 2,
allowed() {
return tech.isWideLaser
},
requires: "laser gun, diffuse beam",
effect() {
tech.wideLaser += 2
for (i = 0, len = b.guns.length; i < len; i++) { //find which gun
if (b.guns[i].name === "laser") b.guns[i].chooseFireMethod()
}
},
remove() {
if (tech.isWideLaser) {
tech.wideLaser = 3
} else {
tech.wideLaser = 0
}
for (i = 0, len = b.guns.length; i < len; i++) { //find which gun
if (b.guns[i].name === "laser") b.guns[i].chooseFireMethod()
}
}
},
{
name: "slow light",
description: "laser beam is spread into your recent past
increase total beam damage by 300%",
isGunTech: true,
maxCount: 9,
count: 0,
frequency: 1,
frequencyDefault: 1,
allowed() {
return tech.haveGunCheck("laser") && tech.laserReflections < 3 && !tech.beamSplitter && !tech.isWideLaser
},
requires: "laser gun, not specular reflection, diffraction grating, diffuse beam",
effect() {
// this.description = `add 5 more laser beams into into your past`
tech.historyLaser++
for (i = 0, len = b.guns.length; i < len; i++) { //find which gun
if (b.guns[i].name === "laser") b.guns[i].chooseFireMethod()
}
},
remove() {
// this.description = "laser beam is spread into your recent past
increase total beam damage by 300%"
if (tech.historyLaser) {
tech.historyLaser = 0
for (i = 0, len = b.guns.length; i < len; i++) { //find which gun
if (b.guns[i].name === "laser") b.guns[i].chooseFireMethod()
}
}
}
},
{
name: "pulse",
description: "charge your energy and release it as a
laser pulse that initiates an explosion cluster",
isGunTech: true,
maxCount: 1,
count: 0,
frequency: 2,
frequencyDefault: 2,
allowed() {
return tech.haveGunCheck("laser") && tech.laserReflections < 3 && !tech.isWideLaser && tech.laserDamage === 0.16 && !tech.isStuckOn
},
requires: "laser gun, not specular reflection, diffuse, free-electron laser, optical amplifier",
effect() {
tech.isPulseLaser = true;
for (i = 0, len = b.guns.length; i < len; i++) { //find which gun
if (b.guns[i].name === "laser") b.guns[i].chooseFireMethod()
}
},
remove() {
if (tech.isPulseLaser) {
tech.isPulseLaser = false;
for (i = 0, len = b.guns.length; i < len; i++) { //find which gun
if (b.guns[i].name === "laser") b.guns[i].chooseFireMethod()
}
}
}
},
{
name: "neocognitron",
description: "pulse automatically aims at a nearby mob",
isGunTech: true,
maxCount: 1,
count: 0,
frequency: 2,
frequencyDefault: 2,
allowed() {
return tech.isPulseLaser && !tech.beamSplitter
},
requires: "laser gun, pulse, not diffraction grating",
effect() {
tech.isPulseAim = true;
},
remove() {
tech.isPulseAim = false;
}
},
//**************************************************
//************************************************** field
//************************************************** tech
//**************************************************
{
name: "zero point energy",
description: `use ${powerUps.orb.research(2)}to increase your max energy by 100`,
// description: "use 2 research to
increase your maximum energy by 74",
isFieldTech: true,
maxCount: 1,
count: 0,
frequency: 3,
frequencyDefault: 3,
allowed() {
return (m.fieldUpgrades[m.fieldMode].name === "standing wave" || m.fieldUpgrades[m.fieldMode].name === "pilot wave" || m.fieldUpgrades[m.fieldMode].name === "time dilation") && (build.isExperimentSelection || powerUps.research.count > 1)
},
requires: "standing wave, pilot wave, time dilation",
effect() {
tech.harmonicEnergy = 1
m.setMaxEnergy()
for (let i = 0; i < 2; i++) {
if (powerUps.research.count > 0) powerUps.research.changeRerolls(-1)
}
},
remove() {
tech.harmonicEnergy = 0;
m.setMaxEnergy()
if (this.count > 0) powerUps.research.changeRerolls(2)
}
},
{
name: "spherical harmonics",
description: "standing wave deflects 40% more efficiently
no longer deactivates with mob shields", //standing wave oscillates in a 3rd dimension
isFieldTech: true,
maxCount: 9,
count: 0,
frequency: 3,
frequencyDefault: 3,
allowed() {
return m.fieldUpgrades[m.fieldMode].name === "standing wave"
},
requires: "standing wave",
effect() {
tech.harmonics++
m.fieldShieldingScale = (tech.isStandingWaveExpand ? 0.9 : 1.3) * Math.pow(0.6, (tech.harmonics - 2))
m.harmonicShield = m.harmonicAtomic
},
remove() {
tech.harmonics = 2
m.fieldShieldingScale = (tech.isStandingWaveExpand ? 0.9 : 1.3) * Math.pow(0.6, (tech.harmonics - 2))
m.harmonicShield = m.harmonic3Phase
}
},
{
name: "expansion",
description: "standing wave deflects 40% more efficiently
using standing wave field expands its radius",
// description: "use energy to expand standing wave
the field slowly contracts when not used",
isFieldTech: true,
maxCount: 1,
count: 0,
frequency: 2,
frequencyDefault: 2,
allowed() {
return m.fieldUpgrades[m.fieldMode].name === "standing wave" && (tech.blockDmg || tech.blockingIce)
},
requires: "standing wave, bremsstrahlung, triple point",
effect() {
tech.isStandingWaveExpand = true
m.fieldShieldingScale = (tech.isStandingWaveExpand ? 0.9 : 1.3) * Math.pow(0.6, (tech.harmonics - 2))
},
remove() {
tech.isStandingWaveExpand = false
m.fieldShieldingScale = (tech.isStandingWaveExpand ? 0.9 : 1.3) * Math.pow(0.6, (tech.harmonics - 2))
m.harmonicRadius = 1
}
},
{
name: "bremsstrahlung",
description: "deflecting and thrown blocks
do braking damage to mobs",
isFieldTech: true,
maxCount: 9,
count: 0,
frequency: 2,
frequencyDefault: 2,
allowed() {
return m.fieldUpgrades[m.fieldMode].name === "standing wave" || m.fieldUpgrades[m.fieldMode].name === "perfect diamagnetism" || m.fieldUpgrades[m.fieldMode].name === "pilot wave"
},
requires: "standing wave, perfect diamagnetism",
effect() {
tech.blockDmg += 3 //if you change this value also update the for loop in the electricity graphics in m.pushMass
},
remove() {
tech.blockDmg = 0;
}
},
{
name: "cherenkov radiation", //deflecting and blocks
description: "bremsstrahlung's effects are radioactive
increase damage 300% over 6 seconds",
isFieldTech: true,
maxCount: 1,
count: 0,
frequency: 2,
frequencyDefault: 2,
allowed() {
return (m.fieldUpgrades[m.fieldMode].name === "standing wave" || m.fieldUpgrades[m.fieldMode].name === "perfect diamagnetism" || m.fieldUpgrades[m.fieldMode].name === "pilot wave") && tech.blockDmg
},
requires: "bremsstrahlung",
effect() {
tech.isBlockRadiation = true
},
remove() {
tech.isBlockRadiation = false;
}
},
{
name: "triple point",
description: "the pressure from deflecting is used
to condense ice IX crystals",
isFieldTech: true,
maxCount: 9,
count: 0,
frequency: 2,
frequencyDefault: 2,
allowed() {
return m.fieldUpgrades[m.fieldMode].name === "standing wave" || m.fieldUpgrades[m.fieldMode].name === "perfect diamagnetism"
},
requires: "standing wave, perfect diamagnetism",
effect() {
tech.blockingIce++
},
remove() {
tech.blockingIce = 0;
}
},
{
name: "flux pinning",
description: "deflecting mobs with your field
stuns them for 4 seconds",
isFieldTech: true,
maxCount: 9,
count: 0,
frequency: 2,
frequencyDefault: 2,
allowed() {
return m.fieldUpgrades[m.fieldMode].name === "perfect diamagnetism" || m.fieldUpgrades[m.fieldMode].name === "standing wave" || m.fieldUpgrades[m.fieldMode].name === "molecular assembler"
},
requires: "a field that can block",
effect() {
tech.isStunField += 240;
},
remove() {
tech.isStunField = 0;
}
},
{
name: "eddy current brake",
description: "perfect diamagnetism slows nearby mobs
effect radius scales with stored energy",
isFieldTech: true,
maxCount: 1,
count: 0,
frequency: 2,
frequencyDefault: 2,
allowed() {
return m.fieldUpgrades[m.fieldMode].name === "perfect diamagnetism"
},
requires: "perfect diamagnetism",
effect() {
tech.isPerfectBrake = true;
},
remove() {
tech.isPerfectBrake = false;
}
},
{
name: "Meissner effect",
description: "increase perfect diamagnetism field
radius by 55% and circular arc by 22°",
isFieldTech: true,
maxCount: 1,
count: 0,
frequency: 2,
frequencyDefault: 2,
allowed() {
return m.fieldUpgrades[m.fieldMode].name === "perfect diamagnetism"
},
requires: "perfect diamagnetism",
effect() {
tech.isBigField = true;
},
remove() {
tech.isBigField = false;
}
},
{
name: "tessellation",
description: `use ${powerUps.orb.research(2)}to reduce harm by 50%`,
// description: "use 4 research
reduce harm by 50%",
isFieldTech: true,
maxCount: 1,
count: 0,
frequency: 3,
frequencyDefault: 3,
allowed() {
return (m.fieldUpgrades[m.fieldMode].name === "pilot wave" || m.fieldUpgrades[m.fieldMode].name === "perfect diamagnetism" || m.fieldUpgrades[m.fieldMode].name === "negative mass") && (build.isExperimentSelection || powerUps.research.count > 3) && !tech.isEnergyHealth
},
requires: "perfect diamagnetism, negative mass, pilot wave, not mass energy",
effect() {
tech.isFieldHarmReduction = true
for (let i = 0; i < 2; i++) {
if (powerUps.research.count > 0) powerUps.research.changeRerolls(-1)
}
},
remove() {
tech.isFieldHarmReduction = false
if (this.count > 0) powerUps.research.changeRerolls(2)
}
},
{
name: "radiative equilibrium",
description: "for 10 seconds after receiving harm
increase damage by 200%",
isFieldTech: true,
maxCount: 1,
count: 0,
frequency: 1,
frequencyDefault: 1,
allowed() {
return m.fieldUpgrades[m.fieldMode].name === "pilot wave" || m.fieldUpgrades[m.fieldMode].name === "negative mass"
},
requires: "negative mass, pilot wave",
effect() {
tech.isHarmDamage = true;
},
remove() {
tech.isHarmDamage = false;
}
},
{
name: "neutronium",
description: `reduce harm by 90% when your field is active
move and jump 25% slower`,
isFieldTech: true,
maxCount: 1,
count: 0,
frequency: 2,
frequencyDefault: 2,
allowed() {
return m.fieldUpgrades[m.fieldMode].name === "negative mass" && !tech.isEnergyHealth
},
requires: "negative mass, not mass-energy",
effect() {
tech.isNeutronium = true
tech.baseFx *= 0.75
tech.baseJumpForce *= 0.75
m.setMovement()
},
//also removed in m.setHoldDefaults() if player switches into a bad field
remove() {
tech.isNeutronium = false
if (!tech.isFreeWormHole) {
tech.baseFx = 0.08
tech.baseJumpForce = 10.5
m.setMovement()
}
}
},
{
name: "annihilation",
description: "touching normal mobs annihilates them
but drains 33% of your maximum energy",
isFieldTech: true,
maxCount: 1,
count: 0,
frequency: 2,
frequencyDefault: 2,
allowed() {
return m.fieldUpgrades[m.fieldMode].name === "negative mass"
},
requires: "negative mass",
effect() {
tech.isAnnihilation = true
},
remove() {
tech.isAnnihilation = false;
}
},
{
name: "inertial mass",
description: "negative mass is larger and faster
blocks also move horizontally with the field",
isFieldTech: true,
maxCount: 1,
count: 0,
frequency: 2,
frequencyDefault: 2,
allowed() {
return m.fieldUpgrades[m.fieldMode].name === "negative mass"
},
requires: "negative mass",
effect() {
tech.isFlyFaster = true
},
remove() {
tech.isFlyFaster = false;
}
},
// {
// name: "Bose Einstein condensate",
// description: "use energy to freeze mobs in your field
pilot wave, negative mass, time dilation",
// isFieldTech: true,
// maxCount: 1,
// count: 0,
// frequency: 2,
// frequencyDefault: 2,
// allowed() {
// return m.fieldUpgrades[m.fieldMode].name === "pilot wave" || m.fieldUpgrades[m.fieldMode].name === "negative mass" || (m.fieldUpgrades[m.fieldMode].name === "time dilation" && !tech.isRewindField)
// },
// requires: "pilot wave, negative mass, time dilation, not retrocausality",
// effect() {
// tech.isFreezeMobs = true
// },
// remove() {
// tech.isFreezeMobs = false
// }
// },
{
name: "pair production",
description: "picking up a power up gives you 200 energy",
isFieldTech: true,
maxCount: 1,
count: 0,
frequency: 2,
frequencyDefault: 2,
allowed() {
return m.fieldUpgrades[m.fieldMode].name === "molecular assembler" || m.fieldUpgrades[m.fieldMode].name === "standing wave" || m.fieldUpgrades[m.fieldMode].name === "pilot wave"
},
requires: "molecular assembler, pilot wave, standing wave",
effect: () => {
tech.isMassEnergy = true // used in m.grabPowerUp
m.energy += 2
},
remove() {
tech.isMassEnergy = false;
}
},
{
name: "bot manufacturing",
description: `use ${powerUps.orb.research(2)} to build
3 random bots`,
isFieldTech: true,
maxCount: 1,
count: 0,
frequency: 1,
frequencyDefault: 1,
isBotTech: true,
isNonRefundable: true,
// isExperimentHide: true,
allowed() {
return powerUps.research.count > 1 && m.fieldUpgrades[m.fieldMode].name === "molecular assembler"
},
requires: "NOT EXPERIMENT MODE, molecular assembler",
effect: () => {
for (let i = 0; i < 2; i++) {
if (powerUps.research.count > 0) powerUps.research.changeRerolls(-1)
}
m.energy = 0.01;
b.randomBot()
b.randomBot()
b.randomBot()
},
remove() {}
},
{
name: "bot prototypes",
description: `use ${powerUps.orb.research(3)}to build
2 random bots and upgrade all bots to that type`,
isFieldTech: true,
maxCount: 1,
count: 0,
frequency: 1,
frequencyDefault: 1,
isBotTech: true,
isNonRefundable: true,
// isExperimentHide: true,
allowed() {
return powerUps.research.count > 2 && m.fieldUpgrades[m.fieldMode].name === "molecular assembler"
},
requires: "NOT EXPERIMENT MODE, molecular assembler",
effect: () => {
for (let i = 0; i < 3; i++) {
if (powerUps.research.count > 0) powerUps.research.changeRerolls(-1)
}
//fill array of available bots
const notUpgradedBots = []
const num = 2
notUpgradedBots.push(() => {
tech.giveTech("nail-bot upgrade")
for (let i = 0; i < num; i++) {
b.nailBot()
tech.nailBotCount++;
}
simulation.makeTextLog(`tech.isNailBotUpgrade = true`)
})
notUpgradedBots.push(() => {
tech.giveTech("foam-bot upgrade")
for (let i = 0; i < num; i++) {
b.foamBot()
tech.foamBotCount++;
}
simulation.makeTextLog(`tech.isFoamBotUpgrade = true`)
})
notUpgradedBots.push(() => {
tech.giveTech("boom-bot upgrade")
for (let i = 0; i < num; i++) {
b.boomBot()
tech.boomBotCount++;
}
simulation.makeTextLog(`tech.isBoomBotUpgrade = true`)
})
notUpgradedBots.push(() => {
tech.giveTech("laser-bot upgrade")
for (let i = 0; i < num; i++) {
b.laserBot()
tech.laserBotCount++;
}
simulation.makeTextLog(`tech.isLaserBotUpgrade = true`)
})
notUpgradedBots.push(() => {
tech.giveTech("orbital-bot upgrade")
for (let i = 0; i < num; i++) {
b.orbitBot()
tech.orbitBotCount++;
}
simulation.makeTextLog(`tech.isOrbitalBotUpgrade = true`)
})
for (let i = 0; i < 2; i++) { //double chance for dynamo-bot, since it's very good for assembler
notUpgradedBots.push(() => {
tech.giveTech("dynamo-bot upgrade")
for (let i = 0; i < num; i++) {
b.dynamoBot()
tech.dynamoBotCount++;
}
simulation.makeTextLog(`tech.isDynamoBotUpgrade = true`)
})
}
notUpgradedBots[Math.floor(Math.random() * notUpgradedBots.length)]() //choose random function from the array and run it
},
remove() {}
},
{
name: "mycelium manufacturing",
link: `mycelium manufacturing`,
description: `use ${powerUps.orb.research(1)}to repurpose molecular assembler
excess energy used to grow spores`,
// description: "use 3 research to repurpose assembler
excess energy used to grow spores",
isFieldTech: true,
maxCount: 1,
count: 0,
frequency: 3,
frequencyDefault: 3,
allowed() {
return (build.isExperimentSelection || powerUps.research.count > 0) && m.fieldUpgrades[m.fieldMode].name === "molecular assembler" && !(tech.isMissileField || tech.isIceField || tech.isFastDrones || tech.isDroneGrab || tech.isDroneRadioactive || tech.isDroneTeleport)
},
requires: "molecular assembler, no other manufacturing, no drone tech",
effect() {
if (!build.isExperimentSelection) {
for (let i = 0; i < 1; i++) {
if (powerUps.research.count > 0) powerUps.research.changeRerolls(-1)
}
}
tech.isSporeField = true;
},
remove() {
tech.isSporeField = false;
if (this.count > 0) powerUps.research.changeRerolls(1)
}
},
{
name: "missile manufacturing",
link: `missile manufacturing`,
description: `use ${powerUps.orb.research(1)}to repurpose molecular assembler
excess energy used to construct missiles`,
// description: "use 3 research to repurpose assembler
excess energy used to construct missiles",
isFieldTech: true,
maxCount: 1,
count: 0,
frequency: 3,
frequencyDefault: 3,
allowed() {
return (build.isExperimentSelection || powerUps.research.count > 0) && m.maxEnergy > 0.5 && m.fieldUpgrades[m.fieldMode].name === "molecular assembler" && !(tech.isSporeField || tech.isIceField || tech.isFastDrones || tech.isDroneGrab || tech.isDroneRadioactive || tech.isDroneTeleport || tech.isDronesTravel)
},
requires: "molecular assembler, no other manufacturing, no drone tech",
effect() {
if (!build.isExperimentSelection) {
for (let i = 0; i < 1; i++) {
if (powerUps.research.count > 0) powerUps.research.changeRerolls(-1)
}
}
tech.isMissileField = true;
},
remove() {
tech.isMissileField = false;
if (this.count > 0) powerUps.research.changeRerolls(1)
}
},
{
name: "ice IX manufacturing",
link: `ice IX manufacturing`,
description: `use ${powerUps.orb.research(1)}to repurpose molecular assembler
excess energy used to condense ice IX`,
// description: "use 3 research to repurpose assembler
excess energy used to condense ice IX",
isFieldTech: true,
maxCount: 1,
count: 0,
frequency: 3,
frequencyDefault: 3,
allowed() {
return (build.isExperimentSelection || powerUps.research.count > 0) && m.fieldUpgrades[m.fieldMode].name === "molecular assembler" && !(tech.isSporeField || tech.isMissileField || tech.isFastDrones || tech.isDroneGrab || tech.isDroneRadioactive || tech.isDroneTeleport || tech.isDronesTravel)
},
requires: "molecular assembler, no other manufacturing, no drone tech",
effect() {
if (!build.isExperimentSelection) {
for (let i = 0; i < 1; i++) {
if (powerUps.research.count > 0) powerUps.research.changeRerolls(-1)
}
}
tech.isIceField = true;
},
remove() {
tech.isIceField = false;
if (this.count > 0) powerUps.research.changeRerolls(1)
}
},
// {
// name: "scrap-bot manufacturing",
// link: `manufacturing`,
// description: `use ${powerUps.orb.research(1)}to repurpose molecular assembler
excess energy used to condense scrap bot `,
// isFieldTech: true,
// maxCount: 1,
// count: 0,
// frequency: 3,
// frequencyDefault: 3,
// allowed() {
// return (build.isExperimentSelection || powerUps.research.count > 0) && m.fieldUpgrades[m.fieldMode].name === "molecular assembler" && !(tech.isIceField || tech.isSporeField || tech.isMissileField || tech.isFastDrones || tech.isDroneGrab || tech.isDroneRadioactive || tech.isDroneTeleport || tech.isDronesTravel)
// },
// requires: "molecular assembler, no other manufacturing, no drone tech",
// effect() {
// if (!build.isExperimentSelection) {
// for (let i = 0; i < 1; i++) {
// if (powerUps.research.count > 0) powerUps.research.changeRerolls(-1)
// }
// }
// tech.isBotField = true;
// },
// remove() {
// tech.isBotField = false;
// if (this.count > 0) powerUps.research.changeRerolls(1)
// }
// },
// {
// name: "thermal reservoir",
// description: "increase your plasma damage by 100%
plasma temporarily lowers health not energy",
// isFieldTech: true,
// maxCount: 1,
// count: 0,
// frequency: 2,
// allowed() {
// return m.fieldUpgrades[m.fieldMode].name === "plasma torch" && !tech.isEnergyHealth
// },
// requires: "plasma torch, not mass-energy equivalence",
// effect() {
// tech.isPlasmaRange += 0.27;
// },
// remove() {
// tech.isPlasmaRange = 1;
// }
// },
{
name: "degenerate matter",
description: "reduce harm by 75% while your field is active",
isFieldTech: true,
maxCount: 1,
count: 0,
frequency: 2,
frequencyDefault: 2,
allowed() {
return (m.fieldUpgrades[m.fieldMode].name === "plasma torch" || m.fieldUpgrades[m.fieldMode].name === "perfect diamagnetism" || m.fieldUpgrades[m.fieldMode].name === "pilot wave") && !tech.isEnergyHealth
},
requires: "plasma torch, perfect diamagnetism, pilot wave, not mass-energy",
effect() {
tech.isHarmReduce = true
},
remove() {
tech.isHarmReduce = false;
}
},
{
name: "tokamak",
description: "throwing a block converts it into energy
and a pulsed fusion explosion",
isFieldTech: true,
maxCount: 1,
count: 0,
frequency: 2,
frequencyDefault: 2,
allowed() {
return m.fieldUpgrades[m.fieldMode].name === "plasma torch" || m.fieldUpgrades[m.fieldMode].name === "molecular assembler"
},
requires: "plasma torch or molecular assembler",
effect() {
tech.isTokamak = true;
},
remove() {
tech.isTokamak = false;
}
},
{
name: "plasma-bot",
link: `plasma-bot`,
description: "remove your field to build a bot
that uses energy to emit plasma",
isFieldTech: true,
maxCount: 1,
count: 0,
frequency: 3,
frequencyDefault: 3,
isBot: true,
isBotTech: true,
allowed() {
return m.fieldUpgrades[m.fieldMode].name === "plasma torch" && (build.isExperimentSelection || powerUps.research.count > 0) && !tech.isPlasmaBall && !tech.isExtruder
},
requires: "plasma torch, not extruder, plasma ball",
effect() {
tech.plasmaBotCount++;
b.plasmaBot();
if (build.isExperimentSelection) {
document.getElementById("field-" + m.fieldMode).classList.remove("build-field-selected");
document.getElementById("field-0").classList.add("build-field-selected");
}
m.setField("field emitter")
},
remove() {
if (this.count > 0) {
tech.plasmaBotCount = 0;
b.clearPermanentBots();
b.respawnBots();
if (m.fieldMode === 0) {
m.setField("plasma torch")
if (build.isExperimentSelection) {
document.getElementById("field-0").classList.remove("build-field-selected");
document.getElementById("field-" + m.fieldMode).classList.add("build-field-selected");
}
}
}
}
},
{
name: "plasma jet",
link: `plasma jet`,
description: `use ${powerUps.orb.research(2)} to increase plasma torch range 50%`,
// description: "use 1 research to
increase plasma torch's range by 50%",
isFieldTech: true,
maxCount: 1,
count: 0,
frequency: 2,
frequencyDefault: 2,
allowed() {
return (tech.plasmaBotCount || m.fieldUpgrades[m.fieldMode].name === "plasma torch") && (build.isExperimentSelection || powerUps.research.count > 1) && !tech.isPlasmaBall
},
requires: "plasma torch, not plasma ball",
effect() {
tech.isPlasmaRange += 0.5;
for (let i = 0; i < 2; i++) {
if (powerUps.research.count > 0) powerUps.research.changeRerolls(-1)
}
},
remove() {
tech.isPlasmaRange = 1;
if (this.count > 0) powerUps.research.changeRerolls(this.count * 2)
}
},
{
name: "extruder",
description: "extrude a thin hot wire of plasma
increases damage and energy drain",
isFieldTech: true,
maxCount: 1,
count: 0,
frequency: 2,
frequencyDefault: 2,
allowed() {
return m.fieldUpgrades[m.fieldMode].name === "plasma torch" && !tech.isPlasmaBall
},
requires: "plasma torch, not plasma ball",
effect() {
tech.isExtruder = true;
m.fieldUpgrades[m.fieldMode].set()
},
remove() {
tech.isExtruder = false;
if (this.count && m.fieldUpgrades[m.fieldMode].name === "plasma torch") m.fieldUpgrades[m.fieldMode].set()
}
},
{
name: "refractory metal",
description: "extrude metals at a higher temperature
increases effective radius and damage",
isFieldTech: true,
maxCount: 9,
count: 0,
frequency: 2,
frequencyDefault: 2,
allowed() {
return tech.isExtruder
},
requires: "extruder",
effect() {
tech.extruderRange += 55
},
remove() {
tech.extruderRange = 15
}
},
{
name: "plasma ball",
description: "grow an expanding ball of plasma
increases damage and energy drain",
isFieldTech: true,
maxCount: 1,
count: 0,
frequency: 2,
frequencyDefault: 2,
allowed() {
return m.fieldUpgrades[m.fieldMode].name === "plasma torch" && !tech.isExtruder && tech.isPlasmaRange === 1
},
requires: "plasma torch, not extruder, plasma jet",
effect() {
tech.isPlasmaBall = true;
m.fieldUpgrades[m.fieldMode].set()
},
remove() {
tech.isPlasmaBall = false;
if (this.count && m.fieldUpgrades[m.fieldMode].name === "plasma torch") m.fieldUpgrades[m.fieldMode].set()
}
},
{
name: "corona discharge",
description: "increase the range and frequency
of plasma ball's electric arc ",
isFieldTech: true,
maxCount: 9,
count: 0,
frequency: 2,
frequencyDefault: 2,
allowed() {
return m.fieldUpgrades[m.fieldMode].name === "plasma torch" && tech.isPlasmaBall
},
requires: "plasma ball",
effect() {
tech.plasmaDischarge += 0.03
},
remove() {
tech.plasmaDischarge = 0.01 //default chance per cycle of a discharge
}
},
{
name: "retrocausality",
description: "time dilation uses energy to rewind your
health, velocity, and position up to 10 s",
isFieldTech: true,
maxCount: 1,
count: 0,
frequency: 2,
frequencyDefault: 2,
allowed() {
return m.fieldUpgrades[m.fieldMode].name === "time dilation" && !m.isShipMode && !tech.isRewindAvoidDeath && !tech.isEnergyHealth && !tech.isTimeSkip
},
requires: "time dilation, not CPT symmetry, mass-energy",
effect() {
tech.isRewindField = true;
m.fieldUpgrades[m.fieldMode].set()
m.wakeCheck();
},
remove() {
tech.isRewindField = false;
if (this.count) m.fieldUpgrades[m.fieldMode].set()
}
},
{
name: "Lorentz transformation",
description: `use ${powerUps.orb.research(3)}to increase your time rate
move, jump, and shoot 50% faster`,
isFieldTech: true,
maxCount: 1,
count: 0,
frequency: 3,
frequencyDefault: 3,
allowed() {
return (m.fieldUpgrades[m.fieldMode].name === "time dilation") && (build.isExperimentSelection || powerUps.research.count > 2)
},
requires: "time dilation",
effect() {
tech.isFastTime = true
m.setMovement();
b.setFireCD();
for (let i = 0; i < 3; i++) {
if (powerUps.research.count > 0) powerUps.research.changeRerolls(-1)
}
},
remove() {
tech.isFastTime = false
m.setMovement();
b.setFireCD();
if (this.count > 0) powerUps.research.changeRerolls(3)
}
},
{
name: "time crystals",
description: "quadruple your base energy regeneration",
isFieldTech: true,
maxCount: 1,
count: 0,
frequency: 2,
frequencyDefault: 2,
allowed() {
return !tech.isGroundState && (m.fieldUpgrades[m.fieldMode].name === "time dilation" || m.fieldUpgrades[m.fieldMode].name === "pilot wave")
},
requires: "time dilation or pilot wave, not ground state",
effect: () => {
m.fieldRegen = 0.004
tech.isTimeCrystals = true
},
remove() {
m.fieldRegen = 0.001
tech.isTimeCrystals = false
}
},
{
name: "no-cloning theorem",
description: `45% chance to duplicate spawned power ups
after a mob dies, lose 2% duplication chance`,
isFieldTech: true,
maxCount: 1,
count: 0,
frequency: 2,
frequencyDefault: 2,
allowed() {
return (m.fieldUpgrades[m.fieldMode].name === "time dilation" || m.fieldUpgrades[m.fieldMode].name === "metamaterial cloaking")
},
requires: "cloaking, time dilation",
effect() {
tech.cloakDuplication = 0.45
powerUps.setDupChance(); //needed after adjusting duplication chance
if (!build.isExperimentSelection && !simulation.isTextLogOpen) simulation.circleFlare(0.4);
},
remove() {
tech.cloakDuplication = 0
powerUps.setDupChance(); //needed after adjusting duplication chance
}
},
{
name: "symbiosis",
description: "after a mob dies, lose 0.45 max health
bosses spawn 1 extra tech after they die",
isFieldTech: true,
maxCount: 1,
count: 0,
frequency: 2,
frequencyDefault: 2,
allowed() {
return m.fieldUpgrades[m.fieldMode].name === "metamaterial cloaking" || m.fieldUpgrades[m.fieldMode].name === "time dilation"
},
requires: "cloaking or time dilation",
effect() {
tech.isAddRemoveMaxHealth = true
},
remove() {
tech.isAddRemoveMaxHealth = false
}
},
// {
// name: "symbiosis",
// description: "after a mob dies, lose 0.5 max health
after picking up tech gain 10 max health",
// isFieldTech: true,
// maxCount: 1,
// count: 0,
// frequency: 2,
// frequencyDefault: 2,
// allowed() {
// return (m.fieldUpgrades[m.fieldMode].name === "time dilation" || m.fieldUpgrades[m.fieldMode].name === "metamaterial cloaking")
// },
// requires: "metamaterial cloaking",
// effect() {
// tech.isAddRemoveMaxHealth = true
// tech.extraMaxHealth += 0.1 //increase max health
// m.setMaxHealth();
// },
// remove() {
// tech.isAddRemoveMaxHealth = false
// }
// },
// {
// name: "symbiosis",
// description: "if a mob dies, lose 1% max health
at the end of each level spawn 2 tech",
// isFieldTech: true,
// maxCount: 1,
// count: 0,
// frequency: 2,
// frequencyDefault: 2,
// allowed() {
// return (m.fieldUpgrades[m.fieldMode].name === "time dilation" || m.fieldUpgrades[m.fieldMode].name === "metamaterial cloaking") && !tech.removeMaxHealthOnKill
// },
// requires: "metamaterial cloaking, not -symbiosis-",
// effect() {
// tech.removeMaxHealthOnKill = 0.01
// tech.isSpawnExitTech = true
// // for (let i = 0; i < 2; i++) powerUps.spawn(player.position.x + 90 * (Math.random() - 0.5), player.position.y + 90 * (Math.random() - 0.5), "tech", false); //start
// for (let i = 0; i < 2; i++) powerUps.spawn(level.exit.x + 10 * (Math.random() - 0.5), level.exit.y - 100 + 10 * (Math.random() - 0.5), "tech", false) //exit
// },
// remove() {
// tech.removeMaxHealthOnKill = 0
// tech.isSpawnExitTech = false
// }
// },
{
name: "boson composite",
link: `boson composite`,
description: "intangible to blocks and mobs while cloaked
passing through shields drains your energy",
isFieldTech: true,
maxCount: 1,
count: 0,
frequency: 2,
frequencyDefault: 2,
allowed() {
return m.fieldUpgrades[m.fieldMode].name === "metamaterial cloaking"
},
requires: "metamaterial cloaking",
effect() {
tech.isIntangible = true;
},
remove() {
if (tech.isIntangible) {
tech.isIntangible = false;
player.collisionFilter.mask = cat.body | cat.map | cat.mob | cat.mobBullet | cat.mobShield //normal collisions
}
}
},
{
name: "dazzler",
description: "decloaking stuns nearby mobs
and drains 10 energy",
isFieldTech: true,
maxCount: 1,
count: 0,
frequency: 2,
frequencyDefault: 2,
allowed() {
return m.fieldUpgrades[m.fieldMode].name === "metamaterial cloaking"
},
requires: "metamaterial cloaking",
effect() {
tech.isCloakStun = true;
},
remove() {
tech.isCloakStun = false;
}
},
{
name: "ambush",
description: "metamaterial cloaking field damage effect
is increased from 333% to 666%",
isFieldTech: true,
maxCount: 1,
count: 0,
frequency: 2,
frequencyDefault: 2,
allowed() {
return m.fieldUpgrades[m.fieldMode].name === "metamaterial cloaking"
},
requires: "metamaterial cloaking",
effect() {
tech.sneakAttackDmg = 7.66
},
remove() {
tech.sneakAttackDmg = 4.33
}
},
{
name: "dynamical systems",
description: `use ${powerUps.orb.research(2)}to increase your damage by 35%`,
// description: "use 1 research
increase your damage by 35%",
isFieldTech: true,
maxCount: 1,
count: 0,
frequency: 3,
frequencyDefault: 3,
allowed() {
return (m.fieldUpgrades[m.fieldMode].name === "plasma torch" || m.fieldUpgrades[m.fieldMode].name === "metamaterial cloaking" || m.fieldUpgrades[m.fieldMode].name === "pilot wave") && (build.isExperimentSelection || powerUps.research.count > 1)
},
requires: "cloaking, pilot wave, or plasma torch",
effect() {
tech.isCloakingDamage = true
for (let i = 0; i < 2; i++) {
if (powerUps.research.count > 0) powerUps.research.changeRerolls(-1)
}
},
remove() {
tech.isCloakingDamage = false
if (this.count > 0) powerUps.research.changeRerolls(2)
}
},
{
name: "discrete optimization",
description: "increase damage by 35%
35% increased delay after firing",
isFieldTech: true,
maxCount: 1,
count: 0,
frequency: 2,
frequencyDefault: 2,
allowed() {
return m.fieldUpgrades[m.fieldMode].name === "plasma torch" || m.fieldUpgrades[m.fieldMode].name === "metamaterial cloaking" || m.fieldUpgrades[m.fieldMode].name === "pilot wave" || m.fieldUpgrades[m.fieldMode].name === "molecular assembler"
},
requires: "cloaking, molecular assembler, plasma torch, pilot wave",
effect() {
tech.aimDamage = 1.35
b.setFireCD();
},
remove() {
tech.aimDamage = 1
b.setFireCD();
}
},
// {
// name: "potential well",
// description: "the force that pilot wave generates
to trap blocks is greatly increased",
// isFieldTech: true,
// maxCount: 1,
// count: 0,
// frequency: 2,
// frequencyDefault: 2,
// allowed() {
// return m.fieldUpgrades[m.fieldMode].name === "pilot wave"
// },
// requires: "pilot wave",
// effect() {
// tech.pilotForce = 0.0006
// },
// remove() {
// tech.pilotForce = 0.00002
// }
// },
{
name: "WIMPs",
description: `at the end of each level spawn ${powerUps.orb.research(5)}
and a harmful particle that slowly chases you`,
isFieldTech: true,
maxCount: 9,
count: 0,
frequency: 1,
frequencyDefault: 1,
allowed() {
return m.fieldUpgrades[m.fieldMode].name === "wormhole" || m.fieldUpgrades[m.fieldMode].name === "pilot wave"
},
requires: "wormhole or pilot wave",
effect: () => {
tech.wimpCount++
spawn.WIMP()
for (let j = 0, len = 5; j < len; j++) powerUps.spawn(level.exit.x + 100 * (Math.random() - 0.5), level.exit.y - 100 + 100 * (Math.random() - 0.5), "research", false)
},
remove() {
tech.wimpCount = 0
}
},
{
name: "virtual particles",
description: `use ${powerUps.orb.research(4)}to exploit your field for a
12% chance to duplicate spawned power ups`,
isFieldTech: true,
maxCount: 1,
count: 0,
frequency: 3,
frequencyDefault: 3,
allowed() {
return (m.fieldUpgrades[m.fieldMode].name === "negative mass" || m.fieldUpgrades[m.fieldMode].name === "time dilation" || m.fieldUpgrades[m.fieldMode].name === "wormhole") && (build.isExperimentSelection || powerUps.research.count > 3)
},
requires: "wormhole, time dilation, negative mass",
effect() {
tech.fieldDuplicate = 0.12
powerUps.setDupChance(); //needed after adjusting duplication chance
if (!build.isExperimentSelection && !simulation.isTextLogOpen) simulation.circleFlare(0.13);
for (let i = 0; i < 4; i++) {
if (powerUps.research.count > 0) powerUps.research.changeRerolls(-1)
}
},
remove() {
tech.fieldDuplicate = 0
powerUps.setDupChance(); //needed after adjusting duplication chance
if (this.count > 0) powerUps.research.changeRerolls(4)
}
},
{
name: "Penrose process",
description: "after a block falls into a wormhole
you gain 53 energy",
isFieldTech: true,
maxCount: 1,
count: 0,
frequency: 2,
frequencyDefault: 2,
allowed() {
return m.fieldUpgrades[m.fieldMode].name === "wormhole"
},
requires: "wormhole",
effect() {
tech.isWormholeEnergy = true
},
remove() {
tech.isWormholeEnergy = false
}
},
{
name: "transdimensional worms",
link: `transdimensional worms`,
description: "when blocks fall into a wormhole
higher dimension worms are summoned",
isFieldTech: true,
maxCount: 1,
count: 0,
frequency: 2,
frequencyDefault: 2,
allowed() {
return m.fieldUpgrades[m.fieldMode].name === "wormhole"
},
requires: "wormhole",
effect() {
tech.isWormholeWorms = true
},
remove() {
tech.isWormholeWorms = false
}
},
{
name: "geodesics",
description: `your projectiles can traverse wormholes
spawn 2 guns and ${powerUps.orb.ammo(2)}`,
isFieldTech: true,
maxCount: 1,
count: 0,
frequency: 2,
frequencyDefault: 2,
allowed() {
return m.fieldUpgrades[m.fieldMode].name === "wormhole"
},
requires: "wormhole",
effect() {
tech.isWormBullets = true
for (let i = 0; i < 2; i++) {
powerUps.spawn(m.pos.x, m.pos.y, "gun");
powerUps.spawn(m.pos.x, m.pos.y, "ammo");
}
},
remove() {
if (tech.isWormBullets) {
for (let i = 0; i < 2; i++) {
if (b.inventory.length) b.removeGun(b.guns[b.inventory[b.inventory.length - 1]].name) //remove your last gun
}
tech.isWormBullets = false;
}
}
},
{
name: "cosmic string",
description: "stun and do radioactive damage to mobs
if you tunnel through them with a wormhole",
isFieldTech: true,
maxCount: 1,
count: 0,
frequency: 2,
frequencyDefault: 2,
allowed() {
return m.fieldUpgrades[m.fieldMode].name === "wormhole"
},
requires: "wormhole",
effect() {
tech.isWormholeDamage = true
},
remove() {
tech.isWormholeDamage = false
}
},
{
name: "invariant",
description: "use energy to pause time
while placing your wormhole",
isFieldTech: true,
maxCount: 1,
count: 0,
frequency: 2,
frequencyDefault: 2,
allowed() {
return m.fieldUpgrades[m.fieldMode].name === "wormhole" && !tech.isNoDraftPause
},
requires: "wormhole, not eternalism",
effect() {
tech.isWormHolePause = true
},
remove() {
tech.isWormHolePause = false
}
},
{
name: "charmed baryons",
description: `wormholes require zero energy
move and jump 33% slower`,
isFieldTech: true,
maxCount: 1,
count: 0,
frequency: 2,
frequencyDefault: 2,
allowed() {
return m.fieldUpgrades[m.fieldMode].name === "wormhole" && !tech.isWormholeMapIgnore
},
requires: "wormhole, not affine connection",
effect() {
tech.isFreeWormHole = true
tech.baseFx *= 0.66
tech.baseJumpForce *= 0.66
m.setMovement()
},
//also removed in m.setHoldDefaults() if player switches into a bad field
remove() {
tech.isFreeWormHole = false
if (!tech.isNeutronium) {
tech.baseFx = 0.08
tech.baseJumpForce = 10.5
m.setMovement()
}
}
},
{
name: "affine connection",
description: "wormholes can tunnel through the map
at 200% increased energy cost",
isFieldTech: true,
maxCount: 1,
count: 0,
frequency: 2,
frequencyDefault: 2,
allowed() {
return m.fieldUpgrades[m.fieldMode].name === "wormhole" && !tech.isFreeWormHole
},
requires: "wormhole, not charmed baryons",
effect() {
tech.isWormholeMapIgnore = true
},
remove() {
tech.isWormholeMapIgnore = false
}
},
//**************************************************
//************************************************** experimental
//************************************************** modes
//**************************************************
{
name: "-ship-",
description: "experiment: fly around with no legs
aim with the keyboard",
maxCount: 1,
count: 0,
frequency: 0,
isNonRefundable: true,
isBadRandomOption: true,
isExperimentalMode: true,
allowed() {
return build.isExperimentSelection && !m.isShipMode && m.fieldUpgrades[m.fieldMode].name !== "negative mass"
},
requires: "",
effect() {
m.shipMode()
},
remove() {}
},
{
name: "-quantum leap-",
description: "experiment: every 20 seconds
become an alternate version of yourself",
maxCount: 1,
count: 0,
frequency: 0,
isBadRandomOption: true,
isExperimentalMode: true,
allowed() {
return build.isExperimentSelection
},
requires: "",
interval: undefined,
effect() {
this.interval = setInterval(() => {
if (!build.isExperimentSelection) {
m.switchWorlds()
simulation.trails()
}
}, 20000); //every 20 seconds
},
remove() {
if (this.count > 0) clearTimeout(this.interval);
}
},
{
name: "-shields-",
description: "experiment: every 5 seconds
all mobs gain a shield",
maxCount: 1,
count: 0,
frequency: 0,
isBadRandomOption: true,
isExperimentalMode: true,
allowed() {
return build.isExperimentSelection
},
requires: "",
effect() {
this.interval = setInterval(() => {
if (!build.isExperimentSelection) {
for (let i = 0; i < mob.length; i++) {
if (!mob[i].isShielded && !mob[i].shield && mob[i].isDropPowerUp) spawn.shield(mob[i], mob[i].position.x, mob[i].position.y, 1, true);
}
}
}, 5000); //every 5 seconds
},
interval: undefined,
remove() {
if (this.count > 0) clearTimeout(this.interval);
}
},
{
name: "-Fourier analysis-",
description: "experiment: your aiming is random",
maxCount: 1,
count: 0,
frequency: 0,
isBadRandomOption: true,
isExperimentalMode: true,
allowed() {
return build.isExperimentSelection && !m.isShipMode
},
requires: "not ship",
effect() {
m.look = () => {
m.angle = 2 * Math.sin(m.cycle * 0.0133) + Math.sin(m.cycle * 0.013) + 0.5 * Math.sin(m.cycle * 0.031) + 0.33 * Math.sin(m.cycle * 0.03)
const scale = 0.8;
m.transSmoothX = canvas.width2 - m.pos.x - (simulation.mouse.x - canvas.width2) * scale;
m.transSmoothY = canvas.height2 - m.pos.y - (simulation.mouse.y - canvas.height2) * scale;
m.transX += (m.transSmoothX - m.transX) * 0.07;
m.transY += (m.transSmoothY - m.transY) * 0.07;
}
},
remove() {
if (this.count > 0) m.look = m.lookDefault()
}
},
{
name: "-panopticon-",
description: "experiment: mobs can always see you",
maxCount: 1,
count: 0,
frequency: 0,
isBadRandomOption: true,
isExperimentalMode: true,
allowed() {
return build.isExperimentSelection
},
requires: "",
effect() {
this.interval = setInterval(() => {
if (!build.isExperimentSelection) {
for (let i = 0; i < mob.length; i++) {
if (!mob[i].shield && mob[i].isDropPowerUp) {
mob[i].locatePlayer()
mob[i].seePlayer.yes = true;
}
}
}
}, 1000); //every 1 seconds
},
interval: undefined,
remove() {
if (this.count > 0) clearTimeout(this.interval);
}
},
{
name: "-decomposers-",
description: "experiment: after they die
mobs leave behind spawns",
maxCount: 1,
count: 0,
frequency: 0,
isBadRandomOption: true,
isExperimentalMode: true,
allowed() {
return build.isExperimentSelection
},
requires: "",
effect() {
tech.deathSpawns = 0.2
},
remove() {
tech.deathSpawns = 0
}
},
{
name: "-WIMP-",
description: "experiment: harmful particles slowly chase you",
maxCount: 1,
count: 0,
frequency: 0,
isBadRandomOption: true,
isExperimentalMode: true,
allowed() {
return build.isExperimentSelection
},
requires: "",
effect() {
tech.wimpExperiment = 5
},
remove() {
tech.wimpExperiment = 0
}
},
{
name: "-symbiosis-",
description: "experiment: if you kill a mob
lose 0.2 max health",
maxCount: 1,
count: 0,
frequency: 0,
isBadRandomOption: true,
isExperimentalMode: true,
allowed() {
return build.isExperimentSelection
},
requires: "",
effect() {
tech.removeMaxHealthOnKill = 0.002
},
remove() {
tech.removeMaxHealthOnKill = 0
}
},
{
name: "-parthenocarpy-",
description: "experiment: spawn about 50% more mobs",
maxCount: 1,
count: 1,
frequency: 0,
isBadRandomOption: true,
isExperimentalMode: true,
allowed() {
return build.isExperimentSelection
},
requires: "",
effect() {
tech.isMoreMobs = true
},
remove() {
tech.isMoreMobs = false
}
},
//**************************************************
//************************************************** JUNK
//************************************************** tech
//**************************************************
// {
// name: "junk",
// description: "",
// maxCount: 9,
// count: 0,
// frequency: 0,
// isNonRefundable: true,
// isJunk: true,
// allowed() {
// return true
// },
// requires: "",
// effect() {
// },
// remove() {}
// },
{
name: "return",
description: "return to the introduction level
reduce combat difficulty by 2 levels",
maxCount: 1,
count: 0,
frequency: 0,
isJunk: true,
isNonRefundable: true,
allowed() {
return true
},
requires: "",
effect() {
// level.levelsCleared = 0 //increases chance of power ups spawns, so shouldn't reset
level.difficultyDecrease(simulation.difficultyMode * 2)
level.onLevel = 0
simulation.clearNow = true //end current level
},
remove() {}
},
{
name: "panpsychism",
description: "awaken all blocks
blocks have a chance to spawn power ups",
maxCount: 1,
count: 0,
frequency: 0,
isJunk: true,
isNonRefundable: true,
allowed() {
return true
},
requires: "",
effect() {
setInterval(() => {
for (let i = body.length - 1; i > -1; i--) {
if (!body[i].isNotHoldable) {
Matter.Composite.remove(engine.world, body[i]);
spawn.blockMob(body[i].position.x, body[i].position.y, body[i], 0);
if (!body[i].isAboutToBeRemoved) mob[mob.length - 1].isDropPowerUp = true
body.splice(i, 1);
}
}
}, 6000);
},
remove() {}
},
{
name: "meteor shower",
description: "take a shower, but meteors instead of water",
maxCount: 1,
count: 0,
frequency: 0,
isJunk: true,
isNonRefundable: true,
allowed() {
return true
},
requires: "",
effect() {
setInterval(() => {
fireBlock = function(xPos, yPos) {
const index = body.length
spawn.bodyRect(xPos, yPos, 20 + 50 * Math.random(), 20 + 50 * Math.random());
const bodyBullet = body[index]
Matter.Body.setVelocity(bodyBullet, { x: 5 * (Math.random() - 0.5), y: 10 * (Math.random() - 0.5) });
bodyBullet.isAboutToBeRemoved = true
bodyBullet.collisionFilter.category = cat.body;
bodyBullet.collisionFilter.mask = cat.player | cat.map | cat.body | cat.bullet | cat.mob | cat.mobBullet
bodyBullet.classType = "body";
Composite.add(engine.world, bodyBullet); //add to world
setTimeout(() => { //remove block
for (let i = 0; i < body.length; i++) {
if (body[i] === bodyBullet) {
Matter.Composite.remove(engine.world, body[i]);
body.splice(i, 1);
}
}
}, 4000 + Math.floor(9000 * Math.random()));
}
fireBlock(player.position.x + 600 * (Math.random() - 0.5), player.position.y - 500 - 500 * Math.random());
// for (let i = 0, len = Math.random(); i < len; i++) {
// }
}, 1000);
},
remove() {}
},
{
name: "closed timelike curve",
description: "spawn 5 field power ups, but every 12 seconds
teleport a second into your future or past",
maxCount: 1,
count: 0,
frequency: 0,
isJunk: true,
isNonRefundable: true,
allowed() {
return true
},
requires: "",
effect() {
for (let i = 0; i < 5; i++) powerUps.spawn(m.pos.x + 10 * Math.random(), m.pos.y + 10 * Math.random(), "field");
function loop() {
if (!simulation.paused && m.alive) {
if (!(simulation.cycle % 720)) {
requestAnimationFrame(() => {
if ((simulation.cycle % 1440) > 720) { //kinda alternate between each option
m.rewind(60)
m.energy += 0.4 //to make up for lost energy
} else {
simulation.timePlayerSkip(60)
}
}); //wrapping in animation frame prevents errors, probably
}
}
requestAnimationFrame(loop);
}
requestAnimationFrame(loop);
},
remove() {}
},
// {
// name: "translate",
// description: "translate n-gon into a random language",
// maxCount: 1,
// count: 0,
// frequency: 0,
// isJunk: true,
// isNonRefundable: true,
// allowed() {
// return true
// },
// requires: "",
// effect() {
// // generate a container
// const gtElem = document.createElement('div')
// gtElem.id = "gtElem"
// gtElem.style.visibility = 'hidden' // make it invisible
// document.body.append(gtElem)
// // generate a script to run after creation
// function initGT() {
// // create a new translate element
// new google.translate.TranslateElement({ pageLanguage: 'en', layout: google.translate.TranslateElement.InlineLayout.HORIZONTAL }, 'gtElem')
// // ok now since it's loaded perform a funny hack to make it work
// const langSelect = document.getElementsByClassName("goog-te-combo")[0]
// // select a random language. It takes a second for all langauges to load, so wait a second.
// setTimeout(() => {
// langSelect.selectedIndex = Math.round(langSelect.options.length * Math.random())
// // simulate a click
// langSelect.dispatchEvent(new Event('change'))
// // now make it go away
// const bar = document.getElementById(':1.container')
// bar.style.display = 'none'
// bar.style.visibility = 'hidden'
// }, 1000)
// }
// // add the google translate script
// const translateScript = document.createElement('script')
// translateScript.src = '//translate.google.com/translate_a/element.js?cb=initGT'
// document.body.append(translateScript)
// },
// remove() {}
// },
{
name: "discount",
description: "get 3 random JUNK tech for the price of 1!",
maxCount: 1,
count: 0,
frequency: 0,
isJunk: true,
isNonRefundable: true,
allowed() {
return true
},
requires: "",
effect() {
tech.giveRandomJUNK()
tech.giveRandomJUNK()
tech.giveRandomJUNK()
},
remove() {}
},
// {
// name: "hi",
// description: `spawn to seed 616 `,
// maxCount: 1,
// count: 0,
// frequency: 0,
// isNonRefundable: true,
// isJunk: true,
// allowed() {
// return true
// },
// requires: "",
// effect() {
// document.getElementById("seed").placeholder = Math.initialSeed = String(616)
// Math.seed = Math.abs(Math.hash(Math.initialSeed)) //update randomizer seed in case the player changed it
// },
// remove() {}
// },
{
name: "Higgs phase transition",
description: "instantly spawn 5 tech, but add a chance to
remove everything with a 5 minute half-life",
maxCount: 1,
count: 0,
frequency: 0,
frequencyDefault: 0,
isJunk: true,
isNonRefundable: true,
allowed() {
return true
},
requires: "",
effect() {
powerUps.spawn(m.pos.x, m.pos.y, "tech");
powerUps.spawn(m.pos.x + 30, m.pos.y, "tech");
powerUps.spawn(m.pos.x + 60, m.pos.y, "tech");
powerUps.spawn(m.pos.x, m.pos.y - 30, "tech");
powerUps.spawn(m.pos.x + 30, m.pos.y - 60, "tech");
function loop() {
// (1-X)^cycles = chance to be removed //Math.random() < 0.000019 10 min
if (!simulation.paused && m.alive) {
if (Math.random() < 0.000038) {
// m.death();
simulation.clearMap();
simulation.draw.setPaths();
return
}
}
requestAnimationFrame(loop);
}
requestAnimationFrame(loop);
},
remove() {}
},
{
name: "harvest",
description: "convert all the mobs on this level into ammo",
maxCount: 1,
count: 0,
frequency: 0,
frequencyDefault: 0,
isJunk: true,
isNonRefundable: true,
allowed() {
return true
},
requires: "",
effect() {
for (let i = 0, len = mob.length; i < len; i++) {
if (mob[i].isDropPowerUp) {
powerUps.directSpawn(mob[i].position.x, mob[i].position.y, "ammo");
mob[i].death();
}
}
// for (let i = powerUp.length - 1; i > -1; i--) {
// if (powerUp[i].name !== "ammo") {
// Matter.Composite.remove(engine.world, powerUp[i]);
// powerUp.splice(i, 1);
// }
// }
},
remove() {}
},
{
name: " ",
description: "",
maxCount: 1,
count: 0,
frequency: 0,
frequencyDefault: 0,
isJunk: true,
allowed() {
return true
},
requires: "",
effect() {
},
remove() {
}
},
{
name: "brainstorm",
description: "the tech choice menu randomizes
every 0.5 seconds for 10 seconds",
maxCount: 1,
count: 0,
frequency: 0,
frequencyDefault: 0,
isJunk: true,
allowed() {
return true
},
requires: "",
effect() {
tech.isBrainstorm = true
tech.isBrainstormActive = false
tech.brainStormDelay = 30
},
remove() {
tech.isBrainstorm = false
tech.isBrainstormActive = false
}
},
{
name: "catabolysis",
description: `set your maximum health to 1
double your current ammo 10 times`,
maxCount: 1,
count: 0,
frequency: 0,
isNonRefundable: true,
isJunk: true,
allowed() { return !tech.isFallingDamage && !tech.isOverHeal && !tech.isEnergyHealth },
requires: "not quenching, tungsten carbide, mass-energy",
effect() {
m.baseHealth = 0.01
m.setMaxHealth();
for (let i = 0; i < b.guns.length; i++) b.guns[i].ammo = b.guns[i].ammo * Math.pow(2, 10)
simulation.updateGunHUD();
},
remove() {}
},
{
name: "palantír",
description: `see far away lands`,
maxCount: 1,
count: 0,
frequency: 0,
// isNonRefundable: true,
isJunk: true,
allowed() { return true },
requires: "",
effect() {
m.look = () => {
//always on mouse look
m.angle = Math.atan2(
simulation.mouseInGame.y - m.pos.y,
simulation.mouseInGame.x - m.pos.x
);
//smoothed mouse look translations
const scale = 2;
m.transSmoothX = canvas.width2 - m.pos.x - (simulation.mouse.x - canvas.width2) * scale;
m.transSmoothY = canvas.height2 - m.pos.y - (simulation.mouse.y - canvas.height2) * scale;
m.transX += (m.transSmoothX - m.transX) * m.lookSmoothing;
m.transY += (m.transSmoothY - m.transY) * m.lookSmoothing;
}
},
remove() {
if (this.count) m.look = m.lookDefault
}
},
{
name: "motion sickness",
description: `disable camera smoothing`,
maxCount: 1,
count: 0,
frequency: 0,
// isNonRefundable: true,
isJunk: true,
allowed() { return true },
requires: "",
effect() {
m.look = () => {
//always on mouse look
m.angle = Math.atan2(
simulation.mouseInGame.y - m.pos.y,
simulation.mouseInGame.x - m.pos.x
);
//smoothed mouse look translations
const scale = 1.2;
m.transSmoothX = canvas.width2 - m.pos.x - (simulation.mouse.x - canvas.width2) * scale;
m.transSmoothY = canvas.height2 - m.pos.y - (simulation.mouse.y - canvas.height2) * scale;
m.transX = canvas.width2 - m.pos.x - (simulation.mouse.x - canvas.width2) * scale;
m.transY = canvas.height2 - m.pos.y - (simulation.mouse.y - canvas.height2) * scale;
// m.transX += (m.transSmoothX - m.transX) * m.lookSmoothing;
// m.transY += (m.transSmoothY - m.transY) * m.lookSmoothing;
}
},
remove() {
if (this.count) m.look = m.lookDefault
}
},
{
name: "facsimile",
description: `inserts a copy of your current level into the level list`,
maxCount: 1,
count: 0,
frequency: 0,
isNonRefundable: true,
isJunk: true,
allowed() { return true },
requires: "",
effect() {
level.levels.splice(level.onLevel, 0, level.levels[level.onLevel]);
},
remove() {}
},
{
name: "negative friction",
description: "when you touch walls you speed up instead of slowing down. It's kinda fun.",
maxCount: 1,
count: 0,
frequency: 0,
isJunk: true,
allowed() { return true },
requires: "",
effect() {
player.friction = -0.4
},
remove() {
if (this.count) player.friction = 0.002
}
},
{
name: "bounce",
description: "you bounce off things. It's annoying, but not that bad.",
maxCount: 1,
count: 0,
frequency: 0,
isJunk: true,
allowed() { return true },
requires: "",
effect() {
player.restitution = 0.9
},
remove() {
if (this.count) player.restitution = 0
}
},
{
name: "mouth",
description: "mobs have a non functional mouth",
maxCount: 1,
count: 0,
frequency: 0,
isJunk: true,
allowed() { return true },
requires: "",
effect() {
mobs.draw = () => {
ctx.lineWidth = 2;
let i = mob.length;
while (i--) {
ctx.beginPath();
const vertices = mob[i].vertices;
ctx.moveTo(vertices[0].x, vertices[0].y);
for (let j = 1, len = vertices.length; j < len; ++j) ctx.lineTo(vertices[j].x, vertices[j].y);
ctx.quadraticCurveTo(mob[i].position.x, mob[i].position.y, vertices[0].x, vertices[0].y);
ctx.fillStyle = mob[i].fill;
ctx.strokeStyle = mob[i].stroke;
ctx.fill();
ctx.stroke();
}
}
},
remove() {
mobs.draw = () => {
ctx.lineWidth = 2;
let i = mob.length;
while (i--) {
ctx.beginPath();
const vertices = mob[i].vertices;
ctx.moveTo(vertices[0].x, vertices[0].y);
for (let j = 1, len = vertices.length; j < len; ++j) ctx.lineTo(vertices[j].x, vertices[j].y);
ctx.lineTo(vertices[0].x, vertices[0].y);
ctx.fillStyle = mob[i].fill;
ctx.strokeStyle = mob[i].stroke;
ctx.fill();
ctx.stroke();
}
}
}
},
{
name: "all-stars",
description: "make all mobs look like stars",
maxCount: 1,
count: 0,
frequency: 0,
isJunk: true,
allowed() { return true },
requires: "",
effect() {
mobs.draw = () => {
ctx.lineWidth = 2;
let i = mob.length;
while (i--) {
ctx.beginPath();
const vertices = mob[i].vertices;
ctx.moveTo(vertices[0].x, vertices[0].y);
for (let j = 1, len = vertices.length; j < len; ++j) ctx.quadraticCurveTo(mob[i].position.x, mob[i].position.y, vertices[j].x, vertices[j].y);
ctx.quadraticCurveTo(mob[i].position.x, mob[i].position.y, vertices[0].x, vertices[0].y);
ctx.fillStyle = mob[i].fill;
ctx.strokeStyle = mob[i].stroke;
ctx.fill();
ctx.stroke();
}
}
},
remove() {
mobs.draw = () => {
ctx.lineWidth = 2;
let i = mob.length;
while (i--) {
ctx.beginPath();
const vertices = mob[i].vertices;
ctx.moveTo(vertices[0].x, vertices[0].y);
for (let j = 1, len = vertices.length; j < len; ++j) ctx.lineTo(vertices[j].x, vertices[j].y);
ctx.lineTo(vertices[0].x, vertices[0].y);
ctx.fillStyle = mob[i].fill;
ctx.strokeStyle = mob[i].stroke;
ctx.fill();
ctx.stroke();
}
}
}
},
// draw() {
// ctx.lineWidth = 2;
// let i = mob.length;
// while (i--) {
// ctx.beginPath();
// const vertices = mob[i].vertices;
// ctx.moveTo(vertices[0].x, vertices[0].y);
// for (let j = 1, len = vertices.length; j < len; ++j) ctx.lineTo(vertices[j].x, vertices[j].y);
// ctx.lineTo(vertices[0].x, vertices[0].y);
// ctx.fillStyle = mob[i].fill;
// ctx.strokeStyle = mob[i].stroke;
// ctx.fill();
// ctx.stroke();
// }
// },
{
name: "true colors",
description: `set all power ups to their real world colors`,
maxCount: 1,
count: 0,
frequency: 0,
isJunk: true,
allowed() { return true },
requires: "",
effect() {
// const colors = shuffle(["#f7b", "#0eb", "#467", "#0cf", "hsl(246,100%,77%)", "#26a"])
const colors = shuffle([powerUps.research.color, powerUps.heal.color, powerUps.ammo.color, powerUps.ammo.color, powerUps.field.color, powerUps.gun.color])
powerUps.research.color = colors[0]
powerUps.heal.color = colors[1]
powerUps.ammo.color = colors[2]
powerUps.field.color = colors[3]
powerUps.tech.color = colors[4]
powerUps.gun.color = colors[5]
for (let i = 0; i < powerUp.length; i++) {
switch (powerUp[i].name) {
case "research":
powerUp[i].color = colors[0]
break;
case "heal":
powerUp[i].color = colors[1]
break;
case "ammo":
powerUp[i].color = colors[2]
break;
case "field":
powerUp[i].color = colors[3]
break;
case "tech":
powerUp[i].color = colors[4]
break;
case "gun":
powerUp[i].color = colors[5]
break;
}
}
},
remove() {
const colors = ["#f7b", "#0eb", "#467", "#0cf", "hsl(246,100%,77%)", "#26a"] //no shuffle
powerUps.research.color = colors[0]
powerUps.heal.color = colors[1]
powerUps.ammo.color = colors[2]
powerUps.field.color = colors[3]
powerUps.tech.color = colors[4]
powerUps.gun.color = colors[5]
for (let i = 0; i < powerUp.length; i++) {
switch (powerUp[i].name) {
case "research":
powerUp[i].color = colors[0]
break;
case "heal":
powerUp[i].color = colors[1]
break;
case "ammo":
powerUp[i].color = colors[2]
break;
case "field":
powerUp[i].color = colors[3]
break;
case "tech":
powerUp[i].color = colors[4]
break;
case "gun":
powerUp[i].color = colors[5]
break;
}
}
}
},
{
name: "emergency broadcasting",
description: "emit 2 sine waveforms at 853 Hz and 960 Hz
lower your volume",
maxCount: 1,
count: 0,
frequency: 0,
isJunk: true,
isNonRefundable: true,
allowed() { return true },
requires: "",
effect: () => {
//setup audio context
function tone(frequency) {
const audioCtx = new(window.AudioContext || window.webkitAudioContext)();
const oscillator1 = audioCtx.createOscillator();
const gainNode1 = audioCtx.createGain();
gainNode1.gain.value = 0.5; //controls volume
oscillator1.connect(gainNode1);
gainNode1.connect(audioCtx.destination);
oscillator1.type = "sine"; // 'sine' 'square', 'sawtooth', 'triangle' and 'custom'
oscillator1.frequency.value = frequency; // value in hertz
oscillator1.start();
return audioCtx
}
// let sound = tone(1050)
function EBS() {
const audioCtx = new(window.AudioContext || window.webkitAudioContext)();
const oscillator1 = audioCtx.createOscillator();
const gainNode1 = audioCtx.createGain();
gainNode1.gain.value = 0.3; //controls volume
oscillator1.connect(gainNode1);
gainNode1.connect(audioCtx.destination);
oscillator1.type = "sine"; // 'sine' 'square', 'sawtooth', 'triangle' and 'custom'
oscillator1.frequency.value = 850; // value in hertz
oscillator1.start();
const oscillator2 = audioCtx.createOscillator();
const gainNode2 = audioCtx.createGain();
gainNode2.gain.value = 0.3; //controls volume
oscillator2.connect(gainNode2);
gainNode2.connect(audioCtx.destination);
oscillator2.type = "sine"; // 'sine' 'square', 'sawtooth', 'triangle' and 'custom'
oscillator2.frequency.value = 957; // value in hertz
oscillator2.start();
return audioCtx
}
let sound = EBS()
delay = 1000
setTimeout(() => {
sound.suspend()
powerUps.spawn(m.pos.x + 160 * (Math.random() - 0.5), m.pos.y + 160 * (Math.random() - 0.5), "heal");
setTimeout(() => {
sound.resume()
setTimeout(() => {
sound.suspend()
powerUps.spawn(m.pos.x + 160 * (Math.random() - 0.5), m.pos.y + 160 * (Math.random() - 0.5), "heal");
setTimeout(() => {
sound.resume()
setTimeout(() => {
sound.suspend()
powerUps.spawn(m.pos.x + 160 * (Math.random() - 0.5), m.pos.y + 160 * (Math.random() - 0.5), "heal");
setTimeout(() => {
sound.resume()
setTimeout(() => {
sound.suspend()
powerUps.spawn(m.pos.x + 160 * (Math.random() - 0.5), m.pos.y + 160 * (Math.random() - 0.5), "heal");
setTimeout(() => {
sound.resume()
setTimeout(() => {
sound.suspend()
powerUps.spawn(m.pos.x + 160 * (Math.random() - 0.5), m.pos.y + 160 * (Math.random() - 0.5), "heal");
setTimeout(() => {
sound.resume()
setTimeout(() => {
sound.suspend()
sound.close()
powerUps.spawn(m.pos.x + 160 * (Math.random() - 0.5), m.pos.y + 160 * (Math.random() - 0.5), "heal");
}, delay);
}, delay);
}, delay);
}, delay);
}, delay);
}, delay);
}, delay);
}, delay);
}, delay);
}, delay);
}, delay);
},
remove() {}
},
{
name: "automatic",
description: "you can't fire when moving
always fire when at rest",
maxCount: 1,
count: 0,
frequency: 0,
isJunk: true,
allowed() {
return !tech.isFireMoveLock
},
requires: "not Higgs mechanism",
effect: () => {
tech.isAlwaysFire = true;
b.setFireMethod();
},
remove() {
if (tech.isAlwaysFire) {
tech.isAlwaysFire = false
b.setFireMethod();
}
}
},
{
name: "hidden variable",
description: `spawn ${powerUps.orb.heal(20)}
but hide your health bar`,
maxCount: 1,
count: 0,
frequency: 0,
isNonRefundable: true,
isJunk: true,
allowed() {
return !tech.isEnergyHealth
},
requires: "not mass-energy",
effect() {
document.getElementById("health").style.display = "none"
document.getElementById("health-bg").style.display = "none"
for (let i = 0; i < 20; i++) powerUps.spawn(m.pos.x + 160 * (Math.random() - 0.5), m.pos.y + 160 * (Math.random() - 0.5), "heal");
},
remove() {}
},
{
name: "not a bug",
description: "initiate a totally safe game crash for 10 seconds",
maxCount: 1,
count: 0,
frequency: 0,
isNonRefundable: true,
isJunk: true,
allowed() { return true },
requires: "",
effect() {
const savedfunction = simulation.drawCircle
simulation.drawCircle = () => {
const a = mob[Infinity].position //crashed the game in a visually interesting way, because of the ctx.translate command is never reverted in the main game loop
}
setTimeout(() => {
simulation.drawCircle = savedfunction
canvas.width = canvas.width //clears the canvas // works on chrome at least
powerUps.spawn(m.pos.x, m.pos.y, "tech");
}, 10000);
// for (;;) {} //freezes the tab
},
remove() {}
},
{
name: "posture",
description: "stand a bit taller",
maxCount: 1,
count: 0,
frequency: 0,
isJunk: true,
allowed() {
return !m.isShipMode
},
requires: "",
effect() {
m.yOffWhen.stand = 70
},
remove() {
m.yOffWhen.stand = 49
}
},
{
name: "rhythm",
description: "you oscillate up and down",
maxCount: 1,
count: 0,
frequency: 0,
isJunk: true,
isNonRefundable: true,
allowed() {
return !m.isShipMode
},
requires: "",
effect() {
setInterval(() => {
m.yOffWhen.stand = 53 + 28 * Math.sin(simulation.cycle * 0.2)
if (m.onGround && !m.crouch) m.yOffGoal = m.yOffWhen.stand
}, 100);
},
remove() {}
},
{
name: "spinor",
description: "the direction you aim is determined by your position",
maxCount: 1,
count: 0,
frequency: 0,
isNonRefundable: true,
isJunk: true,
allowed() {
return !m.isShipMode
},
requires: "",
effect() {
m.look = function() {
//always on mouse look
m.angle = (((m.pos.x + m.pos.y) / 100 + Math.PI) % Math.PI * 2) - Math.PI
//smoothed mouse look translations
const scale = 0.8;
m.transSmoothX = canvas.width2 - m.pos.x - (simulation.mouse.x - canvas.width2) * scale;
m.transSmoothY = canvas.height2 - m.pos.y - (simulation.mouse.y - canvas.height2) * scale;
m.transX += (m.transSmoothX - m.transX) * 0.07;
m.transY += (m.transSmoothY - m.transY) * 0.07;
}
},
remove() {
if (this.count) m.look = m.lookDefault
}
},
{
name: "decomposers",
description: "after they die mobs leave behind spawns",
maxCount: 1,
count: 0,
frequency: 0,
isNonRefundable: true,
isJunk: true,
allowed() {
return tech.deathSpawns === 0
},
requires: "",
effect() {
tech.deathSpawns = 0.2
},
remove() {
tech.deathSpawns = 0
}
},
{
name: "panopticon",
description: "mobs can always see you",
maxCount: 1,
count: 0,
frequency: 0,
isNonRefundable: true,
isJunk: true,
allowed() { return true },
requires: "",
effect() {
setInterval(() => {
for (let i = 0; i < mob.length; i++) {
if (!mob[i].shield && mob[i].isDropPowerUp) {
mob[i].locatePlayer()
mob[i].seePlayer.yes = true;
}
}
}, 1000); //every 1 seconds
},
remove() {}
},
// {
// name: "inverted mouse",
// description: "your mouse is scrambled
it's fine, just rotate it 90 degrees",
// maxCount: 1,
// count: 0,
// frequency: 0,
// isExperimentHide: true,
// isNonRefundable: true,
// isJunk: true,
// allowed() {
// return !m.isShipMode
// },
// requires: "not ship",
// effect() {
// document.body.addEventListener("mousemove", (e) => {
// const ratio = window.innerWidth / window.innerHeight
// simulation.mouse.x = e.clientY * ratio
// simulation.mouse.y = e.clientX / ratio;
// });
// },
// remove() {
// // m.look = m.lookDefault
// }
// },
{
name: "Fourier analysis",
description: "your aiming is now controlled by this equation:
2sin(0.0133t) + sin(0.013t) + 0.5sin(0.031t)+ 0.33sin(0.03t)",
maxCount: 1,
count: 0,
frequency: 0,
isJunk: true,
allowed() {
return !m.isShipMode
},
requires: "not ship",
effect() {
m.look = () => {
m.angle = 2 * Math.sin(m.cycle * 0.0133) + Math.sin(m.cycle * 0.013) + 0.5 * Math.sin(m.cycle * 0.031) + 0.33 * Math.sin(m.cycle * 0.03)
const scale = 0.8;
simulation.mouse.y
m.transSmoothX = canvas.width2 - m.pos.x - (simulation.mouse.x - canvas.width2) * scale;
m.transSmoothY = canvas.height2 - m.pos.y - (simulation.mouse.y - canvas.height2) * scale;
m.transX += (m.transSmoothX - m.transX) * 0.07;
m.transY += (m.transSmoothY - m.transY) * 0.07;
}
},
remove() {
if (this.count) m.look = m.lookDefault
}
},
{
name: "disintegrated armament",
description: "spawn a gun
remove your active gun",
maxCount: 1,
count: 0,
frequency: 0,
isNonRefundable: true,
isJunk: true,
allowed() {
return b.inventory.length > 0
},
requires: "at least 1 gun",
effect() {
if (b.inventory.length > 0) b.removeGun(b.guns[b.activeGun].name)
simulation.makeGunHUD()
powerUps.spawn(m.pos.x + 60 * (Math.random() - 0.5), m.pos.y + 60 * (Math.random() - 0.5), "gun");
},
remove() {}
},
{
name: "probability",
description: "increase the frequency
of one random tech by 100",
maxCount: 1,
count: 0,
frequency: 0,
isNonRefundable: true,
isJunk: true,
allowed() { return true },
requires: "",
effect() {
let options = []; //find what tech I could get
for (let i = 0, len = tech.tech.length; i < len; i++) {
if (
tech.tech[i].count < tech.tech[i].maxCount &&
tech.tech[i].allowed() &&
!tech.tech[i].isJunk &&
!tech.tech.isLore
) {
options.push(i);
}
}
if (options.length) {
const index = options[Math.floor(Math.random() * options.length)]
tech.tech[index].frequency = 100
}
},
remove() {}
},
{
name: "encryption",
description: "secure tech information",
maxCount: 1,
count: 0,
frequency: 0,
isNonRefundable: true,
isJunk: true,
allowed() { return true },
requires: "",
effect() {
String.prototype.shuffle = function() {
var a = this.split(""),
n = a.length;
for (var i = n - 1; i > 0; i--) {
var j = Math.floor(Math.random() * (i + 1));
var tmp = a[i];
a[i] = a[j];
a[j] = tmp;
}
return a.join("");
}
for (let i = 0, len = tech.tech.length; i < len; i++) tech.tech[i].name = tech.tech[i].name.shuffle()
},
remove() {}
},
{
name: "transparency",
description: "become invisible to yourself
mobs can still see you",
maxCount: 1,
count: 0,
frequency: 0,
isNonRefundable: true,
isJunk: true,
allowed() { return true },
requires: "",
effect() {
m.draw = () => {}
},
remove() {}
},
{
name: "quantum leap",
description: "become an alternate version of yourself
every 20 seconds",
maxCount: 1,
count: 0,
frequency: 0,
isNonRefundable: true,
isJunk: true,
allowed() { return true },
requires: "",
effect() {
setInterval(() => {
m.switchWorlds()
simulation.trails()
}, 20000); //every 30 seconds
},
remove() {}
},
{
name: "score",
description: "Add a score to n-gon!",
maxCount: 1,
count: 0,
frequency: 0,
isNonRefundable: true,
isJunk: true,
allowed() { return true },
requires: "",
effect() {
setInterval(() => {
let score = Math.ceil(1000 * Math.random() * Math.random() * Math.random() * Math.random() * Math.random())
simulation.makeTextLog(`simulation.score = ${score.toFixed(0)}`);
}, 10000); //every 10 seconds
},
remove() {}
},
{
name: "pop-ups",
description: "sign up to learn endless easy ways to win n-gon
that Landgreen doesn't want you to know!!!1!!",
maxCount: 1,
count: 0,
frequency: 0,
isNonRefundable: true,
isJunk: true,
allowed() { return true },
requires: "",
effect() {
setInterval(() => {
alert(`The best combo is ${tech.tech[Math.floor(Math.random() * tech.tech.length)].name} with ${tech.tech[Math.floor(Math.random() * tech.tech.length)].name}!`);
}, 30000); //every 30 seconds
},
remove() {}
},
{
name: "music",
description: "add music to n-gon",
maxCount: 1,
count: 0,
frequency: 0,
isNonRefundable: true,
isJunk: true,
allowed() { return true },
requires: "",
effect() {
window.open('https://www.youtube.com/watch?v=lEbHeSdmS-k&list=PL9Z5wjoBiPKEDhwCW2RN-VZoCpmhIojdn', '_blank')
},
remove() {}
},
{
name: "performance",
description: "display performance stats to n-gon",
maxCount: 1,
count: 0,
frequency: 0,
isNonRefundable: true,
isJunk: true,
allowed() { return true },
requires: "",
effect() {
(function() {
var script = document.createElement('script');
script.onload = function() {
var stats = new Stats();
document.body.appendChild(stats.dom);
requestAnimationFrame(function loop() {
stats.update();
requestAnimationFrame(loop)
});
};
script.src = 'https://unpkg.com/stats.js@0.17.0/build/stats.min.js';
document.head.appendChild(script);
})()
//move health to the right
document.getElementById("health").style.left = "86px"
document.getElementById("health-bg").style.left = "86px"
},
remove() {}
},
{
name: "repartitioning",
description: "set the frequency of finding normal tech to 0
spawn 5 tech",
maxCount: 1,
count: 0,
frequency: 0,
isNonRefundable: true,
isJunk: true,
allowed() { return true },
requires: "",
effect() {
for (let i = 0, len = tech.tech.length; i < len; i++) {
if (tech.tech[i].isJunk) {
tech.tech[i].frequency = 2
} else {
tech.tech[i].frequency = 0
}
}
for (let i = 0; i < 5; i++) powerUps.spawn(m.pos.x, m.pos.y, "tech");
},
remove() {}
},
{
name: "defragment",
description: "set the frequency of finding JUNK tech to zero",
maxCount: 1,
count: 0,
frequency: 0,
isNonRefundable: true,
isJunk: true,
allowed() { return true },
requires: "",
effect() {
for (let i = tech.tech.length - 1; i > 0; i--) {
if (tech.tech[i].isJunk) tech.tech[i].frequency = 0
}
},
remove() {}
},
{
name: "ship",
description: "fly around with no legs
reduce combat difficulty by 1 level",
maxCount: 1,
count: 0,
frequency: 0,
isNonRefundable: true,
isJunk: true,
allowed() {
return !m.isShipMode && m.fieldUpgrades[m.fieldMode].name !== "negative mass"
},
requires: "",
effect() {
m.shipMode()
level.difficultyDecrease(simulation.difficultyMode)
},
remove() {}
},
// {
// name: "lubrication",
// description: "reduce block density and friction for this level",
// maxCount: 9,
// count: 0,
// frequency: 0,
// isNonRefundable: true,
// isExperimentHide: true,
// isJunk: true,
// allowed() {
// return true
// },
// requires: "",
// effect() {
// for (let i = 0; i < body.length; i++) {
// Matter.Body.setDensity(body[i], 0.0001) // 0.001 is normal
// body[i].friction = 0.01
// }
// },
// remove() {}
// },
{
name: "pitch",
description: "oscillate the pitch of your world",
maxCount: 1,
count: 0,
frequency: 0,
isNonRefundable: true,
isJunk: true,
allowed() { return true },
requires: "",
effect() {
setInterval(() => { if (!simulation.paused) ctx.rotate(0.001 * Math.sin(simulation.cycle * 0.01)) }, 16);
},
remove() {}
},
{
name: "umbra",
description: "produce a blue glow around everything
and probably some simulation lag",
maxCount: 1,
count: 0,
frequency: 0,
isNonRefundable: true,
isJunk: true,
allowed() { return true },
requires: "",
effect() {
ctx.shadowColor = '#06f';
ctx.shadowBlur = 25;
},
remove() {}
},
{
name: "lighter",
description: `ctx.globalCompositeOperation = "lighter"`,
maxCount: 1,
count: 0,
frequency: 0,
isNonRefundable: true,
isJunk: true,
allowed() {
return m.fieldUpgrades[m.fieldMode].name !== "negative mass"
},
requires: "",
effect() {
ctx.globalCompositeOperation = "lighter";
},
remove() {}
},
{
name: "rewind",
description: "every 10 seconds rewind 2 seconds",
maxCount: 9,
count: 0,
frequency: 0,
isNonRefundable: true,
isJunk: true,
allowed() { return true },
requires: "",
effect() {
setInterval(() => {
m.rewind(120)
m.energy += 0.4
}, 10000);
// for (let i = 0; i < 24; i++) {
// setTimeout(() => { m.rewind(120) }, i * 5000);
// }
},
remove() {}
},
{
name: "undo",
description: "every 4 seconds rewind 1/2 a second",
maxCount: 9,
count: 0,
frequency: 0,
isNonRefundable: true,
isJunk: true,
allowed() { return true },
requires: "",
effect() {
setInterval(() => {
m.rewind(30)
m.energy += 0.2
}, 4000);
},
remove() {}
},
{
name: "energy to mass conversion",
description: "convert your energy into blocks",
maxCount: 9,
count: 0,
frequency: 0,
isNonRefundable: true,
isJunk: true,
allowed() { return true },
requires: "",
effect() {
for (let i = 0, len = 40; i < len; i++) {
setTimeout(() => {
m.energy -= 1 / len
const index = body.length
where = Vector.add(m.pos, { x: 400 * (Math.random() - 0.5), y: 400 * (Math.random() - 0.5) })
spawn.bodyRect(where.x, where.y, Math.floor(15 + 100 * Math.random()), Math.floor(15 + 100 * Math.random()));
body[index].collisionFilter.category = cat.body;
body[index].collisionFilter.mask = cat.player | cat.map | cat.body | cat.bullet | cat.mob | cat.mobBullet
body[index].classType = "body";
Composite.add(engine.world, body[index]); //add to world
}, i * 100);
}
},
remove() {}
},
{
name: "level.nextLevel()",
description: "advance to the next level",
maxCount: 9,
count: 0,
frequency: 0,
isNonRefundable: true,
isJunk: true,
allowed() { return true },
requires: "",
effect() {
level.nextLevel();
},
remove() {}
},
{
name: "expert system",
description: "spawn a tech power up
+64% JUNK to the potential tech pool",
maxCount: 9,
count: 0,
frequency: 0,
isNonRefundable: true,
isJunk: true,
allowed() { return true },
requires: "",
effect() {
powerUps.spawn(m.pos.x, m.pos.y, "tech");
tech.addJunkTechToPool(0.64)
},
remove() {}
},
{
name: "energy investment",
description: "every 10 seconds drain your energy
return it doubled 5 seconds later",
maxCount: 9,
count: 0,
frequency: 0,
isNonRefundable: true,
isJunk: true,
allowed() { return true },
requires: "",
effect() {
setInterval(() => {
if (!simulation.paused) {
const energy = m.energy
m.energy = 0
setTimeout(() => { //return energy
m.energy += 2 * energy
}, 5000);
}
}, 10000);
},
remove() {}
},
{
name: "missile launching system",
description: "fire missiles for the next 120 seconds",
maxCount: 9,
count: 0,
frequency: 0,
isNonRefundable: true,
isJunk: true,
allowed() { return true },
requires: "",
effect() {
for (let i = 0; i < 120; 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 a grenade every 2 seconds",
maxCount: 9,
count: 0,
frequency: 0,
isNonRefundable: true,
isJunk: true,
allowed() { return true },
requires: "",
effect() {
setInterval(() => {
if (!simulation.paused && document.visibilityState !== "hidden") {
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
});
}
}, 2000);
},
remove() {}
},
// {
// name: "inverted input",
// description: "left input becomes right and up input becomes down",
// maxCount: 9,
// count: 0,
// frequency: 0,
// isNonRefundable: true,
// isExperimentHide: true,
// isJunk: true,
// allowed() {
// return true
// },
// requires: "",
// effect() {
// const left = input.key.left
// input.key.left = input.key.right
// input.key.right = left
// const up = input.key.up
// input.key.up = input.key.down
// input.key.down = up
// },
// remove() {}
// },
{
name: "Sleipnir",
description: "grow more legs",
maxCount: 1,
count: 0,
frequency: 0,
isNonRefundable: true,
isJunk: true,
allowed() {
return !m.isShipMode
},
requires: "",
effect() {
m.draw = function() {
ctx.fillStyle = m.fillColor;
m.walk_cycle += m.flipLegs * m.Vx;
//draw body
ctx.save();
ctx.globalAlpha = (m.immuneCycle < m.cycle) ? 1 : 0.5
ctx.translate(m.pos.x, m.pos.y);
for (let i = 0; i < 16; i++) {
m.calcLeg(Math.PI * i / 8, -3 * i / 16)
m.drawLeg("#444")
}
ctx.rotate(m.angle);
ctx.beginPath();
ctx.arc(0, 0, 30, 0, 2 * Math.PI);
ctx.fillStyle = this.bodyGradient
ctx.fill();
ctx.arc(15, 0, 4, 0, 2 * Math.PI);
ctx.strokeStyle = "#333";
ctx.lineWidth = 2;
ctx.stroke();
// ctx.beginPath();
// ctx.arc(15, 0, 3, 0, 2 * Math.PI);
// ctx.fillStyle = '#0cf';
// ctx.fill()
ctx.restore();
m.yOff = m.yOff * 0.85 + m.yOffGoal * 0.15; //smoothly move leg height towards height goal
}
},
remove() {}
},
{
name: "diegesis",
description: "indicate gun fire delay
through a rotation of your head",
maxCount: 1,
count: 0,
frequency: 0,
isNonRefundable: true,
isJunk: true,
allowed() {
return !m.isShipMode
},
requires: "",
effect() {
m.draw = function() {
ctx.fillStyle = m.fillColor;
m.walk_cycle += m.flipLegs * m.Vx;
ctx.save();
ctx.globalAlpha = (m.immuneCycle < m.cycle) ? 1 : 0.5
ctx.translate(m.pos.x, m.pos.y);
m.calcLeg(Math.PI, -3);
m.drawLeg("#4a4a4a");
m.calcLeg(0, 0);
m.drawLeg("#333");
ctx.rotate(m.angle - (m.fireCDcycle != Infinity ? m.flipLegs * 0.25 * Math.pow(Math.max(m.fireCDcycle - m.cycle, 0), 0.5) : 0));
ctx.beginPath();
ctx.arc(0, 0, 30, 0, 2 * Math.PI);
ctx.fillStyle = this.bodyGradient
ctx.fill();
ctx.arc(15, 0, 4, 0, 2 * Math.PI);
ctx.strokeStyle = "#333";
ctx.lineWidth = 2;
ctx.stroke();
ctx.restore();
m.yOff = m.yOff * 0.85 + m.yOffGoal * 0.15; //smoothly move leg height towards height goal
}
},
remove() {}
},
{
name: "🐱",
description: "🐈",
maxCount: 1,
count: 0,
frequency: 0,
isNonRefundable: 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");
if (!(m.angle > -Math.PI / 2 && m.angle < Math.PI / 2)) {
ctx.scale(1, -1);
ctx.rotate(Math.PI);
}
ctx.beginPath();
ctx.moveTo(-30, 0);
ctx.bezierCurveTo(-65, -75,
-5, 150 + (5 * Math.sin(simulation.cycle / 10)),
-70 + (10 * Math.sin(simulation.cycle / 10)), 0 + (10 * Math.sin(simulation.cycle / 10)));
ctx.strokeStyle = "#333";
ctx.lineWidth = 4;
ctx.stroke();
if (!(m.angle > -Math.PI / 2 && m.angle < Math.PI / 2)) {
ctx.scale(1, -1);
ctx.rotate(0 - Math.PI);
}
m.calcLeg(0, 0);
m.drawLeg("#333");
ctx.rotate(m.angle);
if (!(m.angle > -Math.PI / 2 && m.angle < Math.PI / 2)) ctx.scale(1, -1);
ctx.beginPath();
ctx.moveTo(5, -30);
ctx.lineTo(20, -40);
ctx.lineTo(20, -20);
ctx.lineWidth = 2;
ctx.fillStyle = "#f3f";
ctx.fill();
ctx.stroke();
ctx.beginPath();
ctx.arc(0, 0, 30, 0, 2 * Math.PI);
ctx.fillStyle = this.bodyGradient
ctx.fill();
ctx.stroke();
ctx.moveTo(19, 0);
ctx.arc(15, 0, 4, Math.PI, 2 * Math.PI);
ctx.stroke();
ctx.beginPath();
ctx.arc(24.3, 6, 5, Math.PI * 2, Math.PI);
ctx.stroke();
ctx.beginPath();
ctx.moveTo(30, 6);
ctx.lineTo(32, 0);
ctx.lineTo(26, 0);
ctx.lineTo(30, 6);
ctx.fillStyle = "#f3f";
ctx.fill();
ctx.stroke();
ctx.restore();
m.yOff = m.yOff * 0.85 + m.yOffGoal * 0.15; //smoothly move leg height towards height goal
}
},
remove() {}
},
{
name: "pareidolia",
description: "don't",
maxCount: 1,
count: 0,
frequency: 0,
isNonRefundable: true,
isJunk: true,
allowed() {
return !m.isShipMode
},
requires: "",
effect() {
m.draw = function() {
ctx.fillStyle = m.fillColor;
m.walk_cycle += m.flipLegs * m.Vx;
ctx.save();
ctx.globalAlpha = (m.immuneCycle < m.cycle) ? 1 : 0.7
ctx.translate(m.pos.x, m.pos.y);
m.calcLeg(Math.PI, -3);
m.drawLeg("#4a4a4a");
m.calcLeg(0, 0);
m.drawLeg("#333");
ctx.rotate(m.angle);
ctx.beginPath();
ctx.arc(0, 0, 30, 0, 2 * Math.PI);
ctx.fillStyle = this.bodyGradient
ctx.fill();
ctx.strokeStyle = "#333";
ctx.lineWidth = 2;
if (!(m.angle > -Math.PI / 2 && m.angle < Math.PI / 2)) ctx.scale(1, -1); //here is the flip
ctx.stroke();
ctx.beginPath();
ctx.arc(2, -6, 7, 0, 2 * Math.PI);
ctx.stroke();
ctx.beginPath();
ctx.arc(25, -6, 7, 0.25 * Math.PI, 1.6 * Math.PI);
ctx.stroke();
ctx.beginPath();
ctx.arc(2, -10, 9, 1.25 * Math.PI, 1.75 * Math.PI);
ctx.stroke();
ctx.beginPath();
ctx.arc(25, -10, 9, 1.25 * Math.PI, 1.4 * Math.PI);
ctx.stroke();
ctx.beginPath();
ctx.arc(18, 13, 10, 0, 2 * Math.PI);
ctx.fillStyle = this.bodyGradient;
ctx.fill();
ctx.stroke();
ctx.beginPath();
ctx.arc(18, 13, 6, 0, 2 * Math.PI);
ctx.fillStyle = "#555";
ctx.fill();
ctx.stroke();
ctx.beginPath();
ctx.arc(3, -6, 3, 0, 2 * Math.PI);
ctx.fill();
ctx.stroke();
ctx.beginPath();
ctx.arc(26, -6, 3, 0, 2 * Math.PI);
ctx.fill();
ctx.stroke();
ctx.restore();
m.yOff = m.yOff * 0.85 + m.yOffGoal * 0.15;
}
},
remove() {}
},
{
name: "prism",
description: "you cycle through different colors",
maxCount: 1,
count: 0,
frequency: 0,
isNonRefundable: true,
isJunk: true,
allowed() { return true },
requires: "",
effect() {
m.color = {
hue: 0,
sat: 100,
light: 50
}
setInterval(function() {
m.color.hue++
m.setFillColors()
}, 10);
},
remove() {}
},
{
name: "assimilation",
description: "all your bots are converted to the same random model",
maxCount: 1,
count: 0,
frequency: 0,
isBotTech: true,
isNonRefundable: true,
isJunk: true,
allowed() {
return b.totalBots() > 2
},
requires: "at least 3 bots",
effect() {
const total = b.totalBots();
tech.dynamoBotCount = 0;
tech.nailBotCount = 0;
tech.laserBotCount = 0;
tech.orbitBotCount = 0;
tech.foamBotCount = 0;
tech.boomBotCount = 0;
tech.plasmaBotCount = 0;
tech.missileBotCount = 0;
for (let i = 0; i < bullet.length; i++) {
if (bullet[i].botType) bullet[i].endCycle = 0
}
const bots = [
() => {
b.nailBot();
tech.nailBotCount++;
},
() => {
b.foamBot();
tech.foamBotCount++;
},
() => {
b.boomBot();
tech.boomBotCount++;
},
() => {
b.laserBot();
tech.laserBotCount++;
},
() => {
b.orbitBot();
tech.orbitBotCount++
},
() => {
b.dynamoBot();
tech.dynamoBotCount++
}
]
const index = Math.floor(Math.random() * bots.length)
for (let i = 0; i < total; i++) bots[index]()
},
remove() {}
},
{
name: "growth hacking",
description: "increase combat difficulty by 1 level",
maxCount: 1,
count: 0,
frequency: 0,
isNonRefundable: true,
isJunk: true,
allowed() { return true },
requires: "",
effect() {
level.difficultyIncrease(simulation.difficultyMode)
},
remove() {}
},
{
name: "stun",
description: "stun all mobs for up to 8 seconds",
maxCount: 9,
count: 0,
frequency: 0,
isNonRefundable: true,
isJunk: true,
allowed() { return true },
requires: "",
effect() {
for (let i = 0; i < mob.length; i++) mobs.statusStun(mob[i], 480)
},
remove() {}
},
{
name: "re-arm",
description: "remove all your guns,
and spawn new ones",
maxCount: 9,
count: 0,
frequency: 0,
isNonRefundable: true,
isJunk: true,
allowed() {
return b.inventory.length > 0
},
requires: "at least 1 gun",
effect() {
for (let i = 0; i < b.inventory.length; i++) powerUps.spawn(m.pos.x + 60 * (Math.random() - 0.5), m.pos.y + 60 * (Math.random() - 0.5), "gun");
//removes guns and ammo
b.inventory = [];
b.activeGun = null;
b.inventoryGun = 0;
for (let i = 0, len = b.guns.length; i < len; ++i) {
b.guns[i].have = false;
if (b.guns[i].ammo !== Infinity) b.guns[i].ammo = 0;
}
simulation.makeGunHUD(); //update gun HUD
},
remove() {}
},
{
name: "re-research",
description: `eject all your ${powerUps.orb.research(1)}`,
maxCount: 9,
count: 0,
frequency: 0,
isNonRefundable: true,
isJunk: true,
allowed() {
return powerUps.research.count > 3
},
requires: "at least 4 research",
effect() {
const dist = 10 * powerUps.research.count + 100
for (let i = 0; i < powerUps.research.count; i++) {
powerUps.directSpawn(m.pos.x + dist * (Math.random() - 0.5), m.pos.y + dist * (Math.random() - 0.5), "research");
}
powerUps.research.count = 0
},
remove() {}
},
{
name: "black hole",
description: `use your energy and ${powerUps.orb.research(4)} to spawn
inside the event horizon of a huge black hole`,
maxCount: 1,
count: 0,
frequency: 0,
isNonRefundable: true,
isJunk: true,
allowed() {
return powerUps.research.count > 3
},
requires: "at least 4 research",
effect() {
m.energy = 0
spawn.suckerBoss(m.pos.x, m.pos.y - 700)
powerUps.research.changeRerolls(-4)
simulation.makeTextLog(`m.research --
${powerUps.research.count}`)
},
remove() {}
},
{
name: "black hole cluster",
description: `spawn 30 nearby black holes`,
maxCount: 1,
count: 0,
frequency: 0,
isNonRefundable: true,
isJunk: true,
allowed() { return true },
requires: "",
effect() {
const unit = { x: 1, y: 0 }
for (let i = 0; i < 30; i++) {
const where = Vector.add(m.pos, Vector.mult(Vector.rotate(unit, Math.random() * 2 * Math.PI), 2000 + 1200 * Math.random()))
spawn.sucker(where.x, where.y, 140)
const who = mob[mob.length - 1]
who.locatePlayer()
// who.damageReduction = 0.2
}
},
remove() {}
},
// {
// name: "JUNKie", //just crashes the game
// description: "all junk",
// maxCount: 1,
// count: 0,
// frequency: 1,
// frequencyDefault: 1,
// isNonRefundable: true,
// isJunk: true,
// allowed() { return true },
// requires: "",
// effect() {
// for (let i = 0, len = tech.tech.length; i < len; i++) {
// if (tech.tech[i].isJunk && tech.tech[i].count < tech.tech[i].maxCount) tech.tech[i].effect()
// }
// },
// remove() {
// tech.tooManyTechChoices = 0
// }
// },
{
name: "path integral",
link: `path integral`,
// description: "your next 3 tech choices
present almost every possible option",
description: "your next tech choice
presents almost every possible option",
maxCount: 1,
count: 0,
frequency: 1,
frequencyDefault: 1,
isNonRefundable: true,
isJunk: true,
allowed() { return true },
requires: "",
effect() {
tech.tooManyTechChoices = 1
for (let i = 0; i < this.bonusResearch; i++) powerUps.spawn(m.pos.x + 40 * (Math.random() - 0.5), m.pos.y + 40 * (Math.random() - 0.5), "research", false);
},
remove() {
tech.tooManyTechChoices = 0
}
},
{
name: "rule 30",
maxCount: 1,
count: 0,
frequency: 0,
isJunk: true,
allowed() {
return true
},
requires: "",
effect() {},
remove() {},
state: [
[false, false, false, false, false, false, false, false, false, false, false, false, false, false, true, false, false, false, false, false, false, false, false, false, false, false, false, false, false]
],
rule(state, a, b, c) {
//30
if (state[a] && state[b] && state[c]) return false; // TTT => F
if (state[a] && state[b] && !state[c]) return false; // TTF => F
if (state[a] && !state[b] && state[c]) return false; //TFT => F
if (state[a] && !state[b] && !state[c]) return true; //TFF => T
if (!state[a] && state[b] && state[c]) return true; //FTT => T
if (!state[a] && state[b] && !state[c]) return true; //FTF => T
if (!state[a] && !state[b] && state[c]) return true; //FFT => T
if (!state[a] && !state[b] && !state[c]) return false; //FFF => F
},
id: 0,
descriptionFunction() {
if (this.id === 0 && Math.random() < 0.5) {
// for (let i = 0; i < 29; i++) this.state[0][i] = Math.random() < 0.5 //randomize seed
this.name = "rule 90"
this.link = `${this.name}`
// console.log(this.name)
this.state[0] = [false, false, false, false, false, false, false, false, false, false, false, false, false, false, true, true, false, false, false, false, false, false, false, false, false, false, false, false, false]
this.rule = function(state, a, b, c) {
if (state[a] && state[b] && state[c]) return false; // TTT => F
if (state[a] && state[b] && !state[c]) return true; // TTF => T
if (state[a] && !state[b] && state[c]) return false; //TFT => F
if (state[a] && !state[b] && !state[c]) return true; //TFF => T
if (!state[a] && state[b] && state[c]) return true; //FTT => T
if (!state[a] && state[b] && !state[c]) return false; //FTF => F
if (!state[a] && !state[b] && state[c]) return true; //FFT => T
if (!state[a] && !state[b] && !state[c]) return false; //FFF => F
}
}
const loop = () => {
if ((simulation.paused || simulation.isChoosing) && m.alive && !build.isExperimentSelection) { //&& (!simulation.isChoosing || this.count === 0)
let b = []; //produce next row
b.push(this.rule(this.state[this.state.length - 1], this.state[this.state.length - 1].length - 1, 0, 1)); //left edge wrap around
for (let i = 1; i < this.state[this.state.length - 1].length - 1; i++) { //apply rule to the rest of the array
b.push(this.rule(this.state[this.state.length - 1], i - 1, i, i + 1));
}
b.push(this.rule(this.state[this.state.length - 1], this.state[this.state.length - 1].length - 2, this.state[this.state.length - 1].length - 1, 0)); //right edge wrap around
this.state.push(b)
if (document.getElementById(`cellular-rule-id${this.id}`)) document.getElementById(`cellular-rule-id${this.id}`).innerHTML = this.outputText() //convert to squares and send HTML
if (this.count && this.state.length < 120 && !(this.state.length % 10)) powerUps.spawn(m.pos.x - 50 + 100 * (Math.random() - 0.5), m.pos.y + 100 * (Math.random() - 0.5), "research");
setTimeout(() => { loop() }, 400);
}
}
setTimeout(() => { loop() }, 400);
this.id++
return `${this.outputText()}`
},
outputText() {
let text = ""
for (let j = 0; j < this.state.length; j++) {
text += ""
for (let i = 0; i < this.state[j].length; i++) {
if (this.state[j][i]) {
text += "⬛" //"█" //"■"
} else {
text += "⬜" //" " //"□"
}
}
text += "
"
}
return text
},
},
{
name: "cosmogonic myth",
description: `open a portal to a primordial version of reality
in 5 minutes close the portal, spawn 1 of each power up`,
maxCount: 1,
count: 0,
frequency: 0,
isNonRefundable: true,
isJunk: true,
allowed() { return true },
requires: "",
effect() {
const urls = ["https://scratch.mit.edu/projects/14005697/fullscreen/", "https://scratch.mit.edu/projects/22573757/fullscreen/", "https://scratch.mit.edu/projects/41429974/fullscreen/", "https://scratch.mit.edu/projects/43690666/fullscreen/", "https://codepen.io/lilgreenland/full/ozXNWZ", "https://codepen.io/lilgreenland/full/wzARJY", "classic/7-1-2017/", "classic/4-15-2018/", "classic/7-11-2019/", "classic/9-8-2019/", "classic/7-15-2020/", "classic/6-1-2021/"]
const choose = urls[Math.floor(Math.random() * urls.length)]
console.log(`opening new tab" ${choose}`)
let tab = window.open(choose, "_blank");
setTimeout(() => {
tab.close();
powerUps.spawn(m.pos.x, m.pos.y, "gun");
setTimeout(() => { powerUps.spawn(m.pos.x, m.pos.y - 50, "ammo") }, 250);
setTimeout(() => { powerUps.spawn(m.pos.x + 50, m.pos.y, "field"); }, 500);
setTimeout(() => { powerUps.spawn(m.pos.x + 50, m.pos.y - 50, "heal"); }, 750);
setTimeout(() => { powerUps.spawn(m.pos.x - 50, m.pos.y, "tech"); }, 1000);
setTimeout(() => { powerUps.spawn(m.pos.x - 50, m.pos.y - 50, "research"); }, 1250);
}, 1000 * 5 * 60);
},
remove() {}
},
{
name: "planetesimals",
description: `play planetesimals (an asteroids-like game)
clear levels in planetesimals to spawn tech
if you die in planetesimals you die in n-gon`,
maxCount: 1,
count: 0,
frequency: 0,
isNonRefundable: true,
isJunk: true,
allowed() { return true },
requires: "",
effect() {
window.open('../../planetesimals/index.html', '_blank')
// powerUps.spawn(m.pos.x, m.pos.y, "tech");
// for communicating to other tabs, like planetesimals
// Connection to a broadcast channel
const bc = new BroadcastChannel('planetesimals');
bc.activated = false
bc.onmessage = function(ev) {
if (ev.data === 'tech') powerUps.directSpawn(m.pos.x, m.pos.y, "tech");
if (ev.data === 'death') {
m.death()
bc.close(); //end session
}
if (ev.data === 'ready' && !bc.activated) {
bc.activated = true //prevents n-gon from activating multiple copies of planetesimals
bc.postMessage("activate");
}
}
},
remove() {}
},
{
name: "tinker",
description: "permanently unlock JUNK tech in experiment mode
this effect is stored for future visits",
maxCount: 1,
count: 0,
frequency: 0,
frequencyDefault: 0,
isJunk: true,
isNonRefundable: true,
allowed() {
return !localSettings.isJunkExperiment
},
requires: "",
effect() {
localSettings.isJunkExperiment = true
if (localSettings.isAllowed) localStorage.setItem("localSettings", JSON.stringify(localSettings)); //update local storage
},
remove() {}
},
{
name: "NFT",
descriptionFunction() { return `buy your current game seed: ${Math.initialSeed}
no one is allowed to use your seeds
if they use them they are gonna get in trouble
your seeds: ${localSettings.personalSeeds.join(", ")}` },
maxCount: 1,
count: 0,
frequency: 0,
isJunk: true,
isNonRefundable: true,
allowed() {
return true
},
requires: "",
effect() {
localSettings.personalSeeds.push(Math.initialSeed)
if (localSettings.isAllowed) localStorage.setItem("localSettings", JSON.stringify(localSettings)); //update local storage
},
remove() {}
},
// {
// name: "rule 90",
// maxCount: 1,
// count: 0,
// frequency: 0,
// isJunk: true,
// allowed() {
// return true
// },
// requires: "",
// effect() {},
// remove() {},
// state: [
// [false, false, false, false, false, false, false, false, false, false, false, false, false, false, true, true, false, false, false, false, false, false, false, false, false, false, false, false, false]
// ],
// rule(state, a, b, c) {
// if (state[a] && state[b] && state[c]) return false; // TTT => F
// if (state[a] && state[b] && !state[c]) return true; // TTF => T
// if (state[a] && !state[b] && state[c]) return false; //TFT => F
// if (state[a] && !state[b] && !state[c]) return true; //TFF => T
// if (!state[a] && state[b] && state[c]) return true; //FTT => T
// if (!state[a] && state[b] && !state[c]) return false; //FTF => F
// if (!state[a] && !state[b] && state[c]) return true; //FFT => T
// if (!state[a] && !state[b] && !state[c]) return false; //FFF => F
// },
// id: 0,
// descriptionFunction() {
// const loop = () => {
// if ((simulation.paused || simulation.isChoosing) && m.alive && !build.isExperimentSelection) { //&& (!simulation.isChoosing || this.count === 0)
// let b = []; //produce next row
// b.push(this.rule(this.state[this.state.length - 1], this.state[this.state.length - 1].length - 1, 0, 1)); //left edge wrap around
// for (let i = 1; i < this.state[this.state.length - 1].length - 1; i++) { //apply rule to the rest of the array
// b.push(this.rule(this.state[this.state.length - 1], i - 1, i, i + 1));
// }
// b.push(this.rule(this.state[this.state.length - 1], this.state[this.state.length - 1].length - 2, this.state[this.state.length - 1].length - 1, 0)); //right edge wrap around
// this.state.push(b)
// if (document.getElementById(`cellular-rule-id${this.id}`)) document.getElementById(`cellular-rule-id${this.id}`).innerHTML = this.outputText() //convert to squares and send HTML
// if (this.count && this.state.length < 120 && !(this.state.length % 10)) powerUps.spawn(m.pos.x - 50 + 100 * (Math.random() - 0.5), m.pos.y + 100 * (Math.random() - 0.5), "research");
// setTimeout(() => { loop() }, 400);
// }
// }
// setTimeout(() => { loop() }, 400);
// // if (this.id === 0) {
// // for (let i = 0; i < 29; i++) this.state[0][i] = Math.random() < 0.5 //randomize seed
// // }
// this.id++
// return `${this.outputText()}`
// },
// outputText() {
// let text = ""
// for (let j = 0; j < this.state.length; j++) {
// text += ""
// for (let i = 0; i < this.state[j].length; i++) {
// if (this.state[j][i]) {
// text += "⬛" //"█" //"■"
// } else {
// text += "⬜" //" " //"□"
// }
// }
// text += "
"
// }
// return text
// },
// },
//**************************************************
//************************************************** undefined / lore
//************************************************** tech
//**************************************************
{
name: `undefined`,
// description: `${lore.techCount+1}/${lore.techGoal}
add copies of this to the potential tech pool`,
description: `this`,
maxCount: 1,
count: 0,
frequency: 3,
frequencyDefault: 3,
isLore: true,
// isNonRefundable: true,
isExperimentHide: true,
allowed() { return true },
requires: "",
effect() {
setTimeout(() => { //a short delay, I can't remember why
lore.techCount++
if (lore.techCount === lore.techGoal) {
// tech.removeLoreTechFromPool();
this.frequency = 0;
this.description = `null is open at level.final()`
} else {
this.frequency += lore.techGoal * 2
// for (let i = 0; i < tech.tech.length; i++) { //set name for all unchosen copies of this tech
// if (tech.tech[i].isLore && tech.tech[i].count === 0) tech.tech[i].description = `${lore.techCount+1}/${lore.techGoal}
add copies of this to the potential tech pool`
// }
// for (let i = 0, len = 10; i < len; i++) tech.addLoreTechToPool()
this.description = `uncaught error:
${Math.max(0, lore.techGoal - lore.techCount)} more required for access to null`
}
}, 1);
},
remove() {
lore.techCount = 0;
this.maxCount = lore.techGoal;
this.description = `this`
}
}
],
// addLoreTechToPool() { //adds lore tech to tech pool
// if (!simulation.isCheating) {
// tech.tech.push({
// name: `undefined`,
// description: `${lore.techCount+1}/${lore.techGoal}
add copies of this to the potential tech pool`,
// maxCount: 1,
// count: 0,
// frequency: 2,
// isLore: true,
// isNonRefundable: true,
// isExperimentHide: true,
// allowed() {
// return true
// },
// requires: "",
// effect() {
// setTimeout(() => { //a short delay, I can't remember why
// lore.techCount++
// if (lore.techCount > lore.techGoal - 1) {
// // tech.removeLoreTechFromPool();
// for (let i = tech.tech.length - 1; i > 0; i--) {
// if (tech.tech[i].isLore && tech.tech[i].count === 0) tech.tech.splice(i, 1)
// }
// } else {
// for (let i = 0; i < tech.tech.length; i++) { //set name for all unchosen copies of this tech
// if (tech.tech[i].isLore && tech.tech[i].count === 0) tech.tech[i].description = `${lore.techCount+1}/${lore.techGoal}
add copies of this to the potential tech pool`
// }
// for (let i = 0, len = 10; i < len; i++) tech.addLoreTechToPool()
// }
// }, 1);
// },
// remove() {}
// })
// }
// },
// junk: [
// ],
//variables use for gun tech upgrades
fireRate: null,
bulletSize: null,
energySiphon: null,
healthDrain: null,
crouchAmmoCount: 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,
isBlockRadiation: null,
isPiezo: null,
isFastDrones: null,
isFastSpores: null,
oneSuperBall: null,
laserReflections: null,
laserDamage: null,
laserFieldDrain: null,
isAmmoFromHealth: null,
mobSpawnWithHealth: null,
isEnergyRecovery: null,
isHealthRecovery: null,
isEnergyLoss: null,
isDeathAvoid: null,
isDeathAvoidedThisLevel: null,
isSporeField: null,
isMissileField: null,
isIceField: null,
isPlasmaRange: null,
isFreezeMobs: null,
isIceCrystals: null,
blockDamage: null,
isBlockStun: null,
isStunField: null,
isHarmDamage: null,
energyRegen: null,
isVacuumBomb: null,
renormalization: null,
fragments: null,
isEnergyDamage: null,
botSpawner: null,
isBotSpawnerReset: null,
isSporeFollow: null,
isNailRadiation: null,
isEnergyHealth: null,
isExplosionStun: null,
restDamage: null,
isRPG: null,
missileCount: null,
isDeterminism: null,
isSuperDeterminism: null,
isHarmReduce: null,
nailsDeathMob: null,
isSlowFPS: null,
isNeutronStun: null,
isAnsatz: null,
isDamageFromBulletCount: null,
isLaserDiode: null,
isNailShot: null,
slowFire: null,
fastTime: null,
squirrelJump: null,
isFastRadiation: null,
isExtraMaxEnergy: null,
isAmmoForGun: null,
isRapidPulse: null,
isPulseAim: null,
isSporeFreeze: null,
isShotgunRecoil: null,
isHealLowHealth: null,
isAoESlow: null,
isHarmArmor: null,
isTurret: null,
isRerollDamage: null,
isHarmFreeze: null,
isBotArmor: null,
isRerollHaste: null,
researchHaste: null,
isMineDrop: null,
isRerollBots: null,
isNailBotUpgrade: null,
isFoamBotUpgrade: null,
isLaserBotUpgrade: null,
isBoomBotUpgrade: null,
isOrbitBotUpgrade: null,
isDroneGrab: null,
isOneGun: null,
isDamageForGuns: null,
isGunCycle: null,
isFastFoam: null,
isSporeGrowth: null,
isStimulatedEmission: null,
nailGun: null,
nailInstantFireRate: null,
isCapacitor: null,
isEnergyNoAmmo: null,
// isFreezeHarmImmune: null,
isSmallExplosion: null,
isExplosionHarm: null,
extraMaxHealth: null,
// bonusHealth: null,
isIntangible: null,
isCloakStun: null,
bonusEnergy: null,
healGiveMaxEnergy: null,
healMaxEnergyBonus: 0, //not null
aimDamage: null,
isNoFireDefense: null,
isNoFireDamage: null,
duplicateChance: null,
beamSplitter: null,
iceEnergy: null,
isPerfectBrake: null,
explosiveRadius: null,
isWormholeEnergy: null,
isWormholeDamage: null,
isNailCrit: null,
isFlechetteExplode: null,
isWormholeWorms: null,
isWormBullets: null,
isWideLaser: null,
wideLaser: null,
isPulseLaser: null,
isRadioactive: null,
radioactiveDamage: null,
isRailEnergyGain: null,
isMineSentry: null,
isIncendiary: null,
overfillDrain: null,
isNeutronSlow: null,
// isRailAreaDamage: null,
historyLaser: null,
isSpeedHarm: null,
isSpeedDamage: null,
isTimeSkip: null,
isCancelDuplication: null,
cancelCount: null,
isCancelRerolls: null,
isCancelTech: null,
isBotDamage: null,
isBanish: null,
isMaxEnergyTech: null,
isLowEnergyDamage: null,
isRewindBot: null,
isRewindGrenade: null,
isExtruder: null,
isEndLevelPowerUp: null,
isMissileBig: null,
isMissileBiggest: null,
isLaserMine: null,
isAmmoFoamSize: null,
isIceIX: null,
isDupDamage: null,
isFireRateForGuns: null,
cyclicImmunity: null,
isTechDamage: null,
isRestHarm: null,
isFireMoveLock: null,
isRivets: null,
isNeedles: null,
isExplodeRadio: null,
isPauseSwitchField: null,
isPauseEjectTech: null,
isShieldPierce: null,
isDuplicateBoss: null,
is111Duplicate: null,
isDynamoBotUpgrade: null,
isBlockPowerUps: null,
isDamageAfterKillNoRegen: null,
isHarmReduceNoKill: null,
isSwitchReality: null,
isResearchReality: null,
isAnthropicDamage: null,
isFlipFlop: null,
isFlipFlopHarm: null,
isFlipFlopOn: null,
// isFlipFlopLevelReset: null,
isFlipFlopDamage: null,
isFlipFlopEnergy: null,
isRelay: null,
relayIce: null,
isMetaAnalysis: null,
isFoamAttract: null,
droneCycleReduction: null,
droneEnergyReduction: null,
isNoHeals: null,
isAlwaysFire: null,
isDroneRespawn: null,
deathSpawns: null,
isMobBlockFling: null,
blockingIce: null,
isPhaseVelocity: null,
waveBeamSpeed: null,
wavePacketAmplitude: null,
isCollisionRealitySwitch: null,
iceIXOnDeath: null,
wimpCount: null,
isAddBlockMass: null,
isMACHO: null,
isHarmMACHO: null,
isSneakAttack: null,
isFallingDamage: null,
harmonics: null,
isStandingWaveExpand: null,
isTokamak: null,
superBallDelay: null,
isBlockExplode: null,
isOverHeal: null,
isDroneRadioactive: null,
droneRadioDamage: null,
isDroneTeleport: null,
isDroneFastLook: null,
isBulletTeleport: null,
isResearchBoss: null,
isJunkResearch: null,
junkResearchNumber: null,
laserColor: null,
laserColorAlpha: null,
isLongitudinal: null,
is360Longitudinal: null,
isShotgunReversed: null,
fieldDuplicate: null,
isCloakingDamage: null,
harmonicEnergy: null,
isFieldHarmReduction: null,
isFastTime: null,
isAnthropicTech: null,
isSporeWorm: null,
isFoamShot: null,
isIceShot: null,
isBlockRestitution: null,
isZeno: null,
isFieldFree: null,
isExtraGunField: null,
isBigField: null,
isSmartRadius: null,
isFilament: null,
isLargeHarpoon: null,
extraHarpoons: null,
ammoCap: null,
isHarpoonPowerUp: null,
harpoonDensity: null,
isAddRemoveMaxHealth: null,
removeMaxHealthOnKill: null,
isSpawnExitTech: null,
cloakDuplication: null,
extruderRange: null,
isForeverDrones: null,
isMoreMobs: null,
nailRecoil: null,
baseJumpForce: null,
baseFx: null,
isNeutronium: null,
isFreeWormHole: null,
isRewindField: null,
isCrouchRegen: null,
OccamDamage: null,
isAxion: null,
isWormholeMapIgnore: null,
isLessDamageReduction: null,
// bulletSize: null,
needleTunnel: null,
isBrainstorm: null,
isBrainstormActive: null,
brainStormDelay: null,
wormSize: null,
extraSuperBalls: null,
isTimeCrystals: null,
isGroundState: null,
isRailGun: null,
isGrapple: null,
isImmuneGrapple: null,
isDronesTravel: null,
isTechDebt: null,
isPlasmaBall: null,
plasmaDischarge: null,
isFlipFlopHealth: null,
isRelayEnergy: null,
coyoteTime: null,
missileFireCD: null,
isBotField: null,
isFoamBall: null,
isNoDraftPause: null,
isFoamPressure: null,
foamDamage: null,
isClusterExplode: null,
isCircleExplode: null,
isPetalsExplode: null,
isDeathSkipTime: null
}