const tech = { totalCount: null, removeCount: 0, setupAllTech() { for (let i = 0, len = tech.tech.length; i < len; i++) { tech.tech[i].isLost = false tech.tech[i].isBanished = false tech.tech[i].remove(); tech.tech[i].count = 0 if (tech.tech[i].isJunk) { tech.tech[i].frequency = 0 } else if (tech.tech[i].frequencyDefault) { tech.tech[i].frequency = tech.tech[i].frequencyDefault } else { tech.tech[i].frequency = 2 } if (tech.tech[i].name === "heals" || tech.tech[i].name === "ammo" || tech.tech[i].name === "research") tech.tech[i].value = tech.tech[i].defaultValue } //remove lore if it's your first time playing since it's confusing //also remove lore if cheating tech.removeCount = 0; tech.pauseEjectTech = 1; //used in paradigm shift lore.techCount = 0; if (simulation.isCheating || localSettings.runCount < 1) { //simulation.isCommunityMaps || for (let i = 0, len = tech.tech.length; i < len; i++) { if (tech.tech[i].isLore) { tech.tech[i].frequency = 0; tech.tech[i].count = 0; } } } tech.damage = 1 tech.junkChance = 0; tech.extraMaxHealth = 0; tech.totalCount = 0; simulation.updateTechHUD(); simulation.updateGunHUD(); }, removeTech(index = 'random') { if (index === 'random') { const have = [] //find which tech you have for (let i = 0; i < tech.tech.length; i++) { if (tech.tech[i].count > 0 && !tech.tech[i].isInstant) have.push(i) } if (have.length) { index = have[Math.floor(Math.random() * have.length)] } else { return 0 //if none found don't remove any tech } } else if (isNaN(index)) { //find index by name let found = false; for (let i = 0; i < tech.tech.length; i++) { if (index === tech.tech[i].name) { index = i; found = true; break; } } if (!found) return 0 //if name not found don't remove any tech } if (tech.tech[index].count === 0) return 0 const totalRemoved = tech.tech[index].count simulation.makeTextLog(`tech.removeTech("${tech.tech[index].name}")`, 360) tech.tech[index].remove(); tech.removeCount += totalRemoved tech.tech[index].count = 0; tech.totalCount -= totalRemoved simulation.updateTechHUD(); tech.tech[index].isLost = true simulation.updateTechHUD(); return totalRemoved //return the total number of tech removed }, junkChance: 0, addJunkTechToPool(percent) { //percent is number between 0-1 simulation.makeTextLog(`+${(100 * percent).toFixed(0)}% JUNKtech chance`) tech.junkChance += (1 - tech.junkChance) * percent return percent //make an array for possible junk tech to add // let options = []; // for (let i = 0; i < tech.tech.length; i++) { // if (tech.tech[i].count < tech.tech[i].maxCount && tech.tech[i].isJunk) options.push(i); // } // if (options.length) { // let countNonJunk = 0 // count total non junk tech // 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) countNonJunk += tech.tech[i].frequency // } // const num = Math.ceil(percent * countNonJunk) //scale number added // for (let i = 0; i < num; i++) tech.tech[options[Math.floor(Math.random() * options.length)]].frequency++ //add random array options to tech pool // simulation.makeTextLog(`tech.tech.push(${num.toFixed(0)} JUNK)`) // return num // } else { // return 0 // } }, removeJunkTechFromPool(percent) { // for (let j = 0; j < num; j++) { // for (let i = 0; i < tech.tech.length; i++) { // if (tech.tech[i].isJunk && tech.tech[i].frequency > 0 && tech.tech[i].count < tech.tech[i].maxCount) { // tech.tech[i].frequency-- // break // } // } // } if (percent > 0) { tech.junkChance = (tech.junkChance - percent) / (1 - percent) if (tech.junkChance < 0.001 || tech.junkChance === undefined) tech.junkChance = 0 } }, giveTech(index = 'random') { if (index === 'random') { let options = []; for (let i = 0; i < tech.tech.length; i++) { if (tech.tech[i].count < tech.tech[i].maxCount && tech.tech[i].allowed() && !tech.tech[i].isJunk && !tech.tech[i].isLore && !tech.tech[i].isBadRandomOption) options.push(i); } // give a random tech from the tech I don't have if (options.length > 0) { let newTech = options[Math.floor(Math.random() * options.length)] simulation.makeTextLog(`tech.giveTech("${tech.tech[newTech].name}") //random tech`); tech.giveTech(newTech) } } else { if (isNaN(index)) { //find index by name let found = false; for (let i = 0; i < tech.tech.length; i++) { if (index === tech.tech[i].name) { index = i; found = true; break; } } if (!found) return //if name not found don't give any tech } if (tech.isMetaAnalysis && tech.tech[index].isJunk) { simulation.makeTextLog(`//tech: meta-analysis replaced junk tech with random tech`); tech.giveTech('random') for (let i = 0; i < 2; i++) powerUps.spawn(m.pos.x + 40 * Math.random(), m.pos.y + 40 * Math.random(), "research"); return } if (tech.tech[index].isLost) tech.tech[index].isLost = false; //give specific tech if (tech.isBanish && tech.tech[index].isBanished) tech.tech[index].isBanished = false //stops the bug where you can't gets stacks of tech you take with decoherence, I think tech.tech[index].effect(); //give specific tech tech.tech[index].count++ tech.totalCount++ //used in power up randomization requestAnimationFrame(() => { //move new tech to the top of the tech list if (index > 0 && !build.isExperimentSelection) { const [item] = tech.tech.splice(index, 1); // Remove the element from the array tech.tech.unshift(item); // Add the element to the front of the array } simulation.updateTechHUD(); }) } }, setCheating() { if (!simulation.isCheating) { simulation.isCheating = true; level.levelAnnounce(); lore.techCount = 0; for (let i = 0, len = tech.tech.length; i < len; i++) { if (tech.tech[i].isLore) { tech.tech[i].frequency = 0; tech.tech[i].count = 0; } } console.log('cheating') sound.tone(250) sound.tone(300) sound.tone(375) } }, haveGunCheck(name, needActive = true) { // if ( // !build.isExperimentSelection && // b.inventory.length > 2 && // name !== b.guns[b.activeGun].name && // Math.random() > 2 - b.inventory.length * 0.5 // ) { // return false // } // for (i = 0, len = b.inventory.length; i < len; i++) { // if (b.guns[b.inventory[i]].name === name) return true // } // return false if (build.isExperimentSelection || !needActive) { for (i = 0, len = b.inventory.length; i < len; i++) { if (b.guns[b.inventory[i]].name === name) return true } return false } else { //must be holding gun, this is the standard while playing return b.inventory.length > 0 && b.guns[b.activeGun].name === name } }, hasExplosiveDamageCheck() { return tech.haveGunCheck("missiles") || (m.fieldMode === 4 && simulation.molecularMode === 1) || tech.missileBotCount > 0 || tech.isBoomBotUpgrade || tech.isIncendiary || tech.isPulseLaser || tech.isTokamak || (tech.haveGunCheck("grenades") && !tech.isNeutronBomb) }, damage: 1, //used for tech changes to player damage that don't have complex conditions damageFromTech() { let dmg = tech.damage //m.fieldDamage if (tech.isImmunityDamage && m.immuneCycle > m.cycle) dmg *= 4 if (tech.isPowerUpDamage) dmg *= 1 + 0.05 * powerUp.length if (tech.isDamageCooldown) dmg *= m.lastKillCycle + tech.isDamageCooldownTime > m.cycle ? 0.5 : 4 if (tech.isDamageAfterKillNoRegen && m.lastKillCycle + 300 > m.cycle) dmg *= 2 if (tech.isDivisor && b.activeGun !== undefined && b.activeGun !== null && b.guns[b.activeGun].ammo % 3 === 0) dmg *= 1.8 if (tech.isNoGroundDamage) dmg *= m.onGround ? 0.85 : 2 if (tech.isDilate) dmg *= 1.9 + 1.1 * Math.sin(m.cycle * 0.01) if (tech.isGunChoice && tech.buffedGun === b.inventoryGun) dmg *= 1 + 0.3 * b.inventory.length if (powerUps.boost.endCycle > m.cycle) dmg *= 1 + powerUps.boost.damage if (m.coupling && (m.fieldMode === 0 || m.fieldMode === 5)) dmg *= 1 + 0.015 * m.coupling if (m.isSneakAttack && m.sneakAttackCycle + Math.min(100, 0.66 * (m.cycle - m.enterCloakCycle)) > m.cycle) dmg *= 4.5 * (1 + 0.033 * m.coupling) if (tech.deathSkipTime) dmg *= 1 + 0.6 * tech.deathSkipTime if (tech.isTechDebt) dmg *= tech.totalCount > 20 ? Math.pow(0.85, tech.totalCount - 20) : 4 - 0.15 * tech.totalCount if (tech.isAnthropicDamage && tech.isDeathAvoidedThisLevel) dmg *= 2.71828 if (tech.isDupDamage) dmg *= 1 + Math.min(1, tech.duplicationChance()) if (tech.isDamageForGuns) dmg *= 1 + 0.22 * Math.max(0, b.inventory.length - 1) if (tech.isOneGun && b.inventory.length < 2) dmg *= 1.3 if (tech.isAcidDmg && m.health > 1) dmg *= 1.35; if (tech.isRerollDamage) dmg *= 1 + Math.max(0, 0.05 * powerUps.research.count) if (tech.isBotDamage) dmg *= 1 + 0.05 * b.totalBots() if (tech.restDamage > 1 && player.speed < 1) dmg *= tech.restDamage if (tech.isLowEnergyDamage) dmg *= 1 + Math.max(0, 1 - m.energy) if (tech.energyDamage) dmg *= 1 + m.energy * 0.23 * tech.energyDamage; if (tech.isDamageFromBulletCount) dmg *= 1 + bullet.length * 0.01 if (tech.isNoFireDamage && m.cycle > m.fireCDcycle + 120) dmg *= 2 if (tech.isSpeedDamage) dmg *= 1 + Math.min(1, (tech.speedAdded + player.speed) * 0.0193) if (tech.isAxion && tech.isHarmMACHO) dmg *= (tech.isMoveMACHO ? 3 : 2) if (tech.isHarmDamage && m.lastHarmCycle + 480 > m.cycle) dmg *= 3; if (tech.lastHitDamage && m.lastHit) dmg *= 1 + tech.lastHitDamage * m.lastHit if (tech.isLowHealthDmg) dmg *= 1 + 0.7 * Math.max(0, 1 - (tech.isEnergyHealth ? m.energy : m.health)) if (tech.isJunkDNA) dmg *= 1 + 2 * tech.junkChance return dmg }, duplicationChance() { return Math.min(1, Math.max(0, (tech.isPowerUpsVanish ? 0.13 : 0) + (tech.isStimulatedEmission ? 0.2 : 0) + tech.duplication + tech.duplicateChance + 0.05 * tech.isExtraGunField + m.duplicateChance + tech.fieldDuplicate + 0.08 * tech.isDuplicateMobs + 0.03 * tech.isMassProduction + 0.04 * tech.isHealAttract + tech.cloakDuplication + (tech.isAnthropicTech && tech.isDeathAvoidedThisLevel ? 0.6 : 0) + 0.06 * tech.isDupEnergy)) }, setTechFrequency(name, frequency) { for (let i = 0, len = tech.tech.length; i < len; i++) { if (tech.tech[i].name === name) tech.tech[i].frequency = frequency } }, setBotTechFrequency(f = 0) { for (let i = 0, len = tech.tech.length; i < len; i++) { if (tech.tech[i].isBotTech) { switch (tech.tech[i].name) { case "dynamo-bot": tech.tech[i].frequency = f break; case "orbital-bot": tech.tech[i].frequency = f break; case "laser-bot": tech.tech[i].frequency = f break; case "boom-bot": tech.tech[i].frequency = f break; case "foam-bot": tech.tech[i].frequency = f break; case "nail-bot": tech.tech[i].frequency = f break; } } } }, tech: [{ name: "tungsten carbide", description: "+400 maximum health
lose health after hard landings", maxCount: 1, count: 0, frequency: 1, frequencyDefault: 1, isSkin: true, allowed() { return !m.isAltSkin }, requires: "not skin", effect() { tech.isFallingDamage = true; m.setMaxHealth(); m.addHealth(3 / simulation.healScale) m.skin.tungsten() }, remove() { tech.isFallingDamage = false; m.setMaxHealth(); if (this.count) m.resetSkin(); } }, { name: "nitinol", description: "1.3x movement and jumping
0.17 seconds of coyote time", maxCount: 1, count: 0, frequency: 1, frequencyDefault: 1, isSkin: true, allowed() { return !m.isAltSkin }, requires: "not skinned", effect() { m.skin.mech(); m.setMovement() }, remove() { if (this.count) m.resetSkin(); } }, { name: "Higgs mechanism", description: "4x fire rate
while firing your position is fixed", maxCount: 1, count: 0, frequency: 1, frequencyDefault: 1, isSkin: true, allowed() { return !m.isAltSkin && !m.isShipMode && !tech.isAlwaysFire }, requires: "not skinned, ship mode, automatic", effect() { tech.isFireMoveLock = true; b.setFireCD(); b.setFireMethod(); m.skin.strokeGap(); }, remove() { tech.isFireMoveLock = false if (tech.isFireMoveLock) { b.setFireCD(); b.setFireMethod(); if (this.count) m.resetSkin(); } } }, { name: "Hilbert space", description: "4x damage
after a collision enter an alternate reality", maxCount: 1, count: 0, frequency: 1, frequencyDefault: 1, isAltRealityTech: true, isSkin: true, allowed() { return !m.isAltSkin && !tech.isResearchReality && !tech.isSwitchReality }, requires: "not skinned, Ψ(t) collapse, many-worlds", damage: 4, effect() { m.skin.anodize(); tech.damage *= this.damage tech.isCollisionRealitySwitch = true; }, remove() { tech.isCollisionRealitySwitch = false; if (this.count && m.alive) { tech.damage /= this.damage m.resetSkin(); } } }, { name: "aperture", description: "every 4 seconds your damage cycles
between 0.8x and 3x damage", maxCount: 1, count: 0, frequency: 1, frequencyDefault: 1, isSkin: true, allowed() { return !m.isAltSkin }, requires: "not skinned", effect() { tech.isDilate = true m.skin.dilate() }, remove() { tech.isDilate = false if (this.count) m.resetSkin(); } }, { name: "diaphragm", description: "every 4 seconds your damage taken cycles
between 0.9x and 0.2x damage taken", maxCount: 1, count: 0, frequency: 2, frequencyDefault: 2, isSkin: true, allowed() { return tech.isDilate }, requires: "aperture", effect() { tech.isDiaphragm = true m.resetSkin(); m.skin.dilate2() }, remove() { tech.isDiaphragm = false if (this.count) m.resetSkin(); } }, { name: "mass-energy equivalence", description: `energy protects you instead of health`, maxCount: 1, count: 0, frequency: 1, frequencyDefault: 1, isSkin: true, allowed() { return !m.isAltSkin && !tech.isPiezo && !tech.isRewindAvoidDeath && !tech.isAnnihilation //&& !tech.isAmmoFromHealth && !tech.isRewindGun }, requires: "not piezoelectricity, CPT, annihilation", effect() { m.health = 0 document.getElementById("health").style.display = "none" document.getElementById("health-bg").style.display = "none" document.getElementById("dmg").style.backgroundColor = "#0cf"; tech.isEnergyHealth = true; simulation.mobDmgColor = "rgba(0, 255, 255,0.6)" //"#0cf" m.displayHealth(); m.lastCalculatedDefense = 0 //this triggers a redraw of the defense bar m.skin.energy(); }, remove() { if (this.count > 0) { tech.isEnergyHealth = false; document.getElementById("health").style.display = "inline" document.getElementById("health-bg").style.display = "inline" document.getElementById("dmg").style.backgroundColor = "#f67"; m.health = Math.max(Math.min(m.maxHealth, m.energy), 0.1); simulation.mobDmgColor = "rgba(255,0,0,0.7)" m.displayHealth(); m.lastCalculatedDefense = 0 //this triggers a redraw of the defense bar m.resetSkin(); } tech.isEnergyHealth = false; } }, { name: "1st ionization energy", link: `1st ionization energy`, descriptionFunction() { return `convert current and future
into

give +${14 * tech.largerHeals * (tech.isHalfHeals ? 0.5 : 1)} maximum energy` }, maxCount: 1, count: 0, frequency: 5, frequencyDefault: 5, allowed() { return tech.isEnergyHealth && !tech.isOverHeal }, requires: "mass-energy equivalence, not quenching", effect() { powerUps.healGiveMaxEnergy = true; //tech.healMaxEnergyBonus given from heal power up powerUps.heal.color = "#ff0" //"#0ae" for (let i = 0; i < powerUp.length; i++) { //find active heal power ups and adjust color live if (powerUp[i].name === "heal") powerUp[i].color = powerUps.heal.color } }, remove() { powerUps.healGiveMaxEnergy = false; // tech.healMaxEnergyBonus = 0 powerUps.heal.color = "#0eb" for (let i = 0; i < powerUp.length; i++) { //find active heal power ups and adjust color live if (powerUp[i].name === "heal") powerUp[i].color = powerUps.heal.color } } }, { name: "depolarization", descriptionFunction() { return `4x damage, but if a mob dies
0.5x damage for ${(tech.isDamageCooldownTime / 60).toFixed(1)} seconds instead` }, maxCount: 1, count: 0, frequency: 1, frequencyDefault: 1, isSkin: true, allowed() { return !m.isAltSkin }, requires: "not skinned", effect() { m.skin.polar(); tech.isDamageCooldown = true; }, remove() { tech.isDamageCooldown = false; if (this.count) m.resetSkin(); } }, { name: "repolarization", descriptionFunction() { return `the damage from depolarization
resets 1.25 seconds sooner after a mob dies` }, maxCount: 3, count: 0, frequency: 4, frequencyDefault: 4, allowed() { return tech.isDamageCooldown }, requires: "depolarization", effect() { tech.isDamageCooldownTime -= 75 }, remove() { tech.isDamageCooldownTime = 240 } }, { name: "CPT symmetry", descriptionFunction() { return `after losing health, if you have above ${(85 * Math.min(100, m.maxEnergy)).toFixed(0)} energy
rewind time for 20 energy per second` }, maxCount: 1, count: 0, frequency: 1, frequencyDefault: 1, isSkin: true, allowed() { return !m.isAltSkin && m.fieldUpgrades[m.fieldMode].name !== "standing wave" && !tech.isRewindField && !tech.isEnergyHealth }, requires: "not skinned, standing wave, max energy reduction, retrocausality, mass-energy", effect() { tech.isRewindAvoidDeath = true; m.skin.CPT() }, remove() { tech.isRewindAvoidDeath = false; if (this.count) m.resetSkin(); } }, { name: "causality bots", link: `causality bots`, description: "when you rewind build scrap bots
that protect you for about 9 seconds", maxCount: 3, count: 0, frequency: 2, frequencyDefault: 2, isBotTech: true, allowed() { return tech.isRewindAvoidDeath || tech.isRewindField }, requires: "CPT, retrocausality", effect() { tech.isRewindBot++; }, remove() { tech.isRewindBot = 0; } }, { name: "causality bombs", link: `causality bombs`, description: "when you rewind drop several grenades", //
become invulnerable until they explode maxCount: 1, count: 0, frequency: 2, frequencyDefault: 2, allowed() { return tech.isRewindAvoidDeath || tech.isRewindField }, requires: "CPT, retrocausality", effect() { tech.isRewindGrenade = true; }, remove() { tech.isRewindGrenade = false; } }, { name: "ternary", //"divisor", descriptionFunction() { return `1.8x damage while your current gun
has ammo divisible by 3` }, maxCount: 1, count: 0, frequency: 1, frequencyDefault: 1, allowed: () => true, requires: "", // divisible: 3, // + Math.floor(6 * Math.random()), effect() { tech.isDivisor = true; }, remove() { tech.isDivisor = false; } }, { name: "integrated armament", link: `integrated armament`, description: `1.3x damage, but new guns replace
your current gun and convert guntech
`, maxCount: 1, count: 0, frequency: 1, frequencyDefault: 1, allowed() { return b.inventory.length === 1 }, requires: "only 1 gun", effect() { tech.isOneGun = true; }, remove() { tech.isOneGun = false; } }, { name: "ordnance", description: "spawn a gun, gain 2x guntech frequency
+6% JUNKtech chance", maxCount: 1, count: 0, frequency: 1, frequencyDefault: 1, isInstant: true, isBadRandomOption: true, allowed: () => true, requires: "", effect() { powerUps.spawn(m.pos.x, m.pos.y, "gun"); for (let i = 0, len = tech.tech.length; i < len; i++) { if (tech.tech[i].isGunTech) tech.tech[i].frequency *= 2 } this.refundAmount += tech.addJunkTechToPool(0.06) }, refundAmount: 0, remove() { if (this.count > 0 && this.refundAmount > 0) { tech.removeJunkTechFromPool(this.refundAmount) this.refundAmount = 0 } } }, { name: "arsenal", descriptionFunction() { return `1.25x damage per unequipped gun
(${(1 + 0.25 * Math.max(0, b.inventory.length - 1)).toFixed(2)}x)` }, maxCount: 1, count: 0, frequency: 1, frequencyDefault: 1, allowed: () => b.inventory.length > 1, requires: "at least 2 guns", effect() { tech.isDamageForGuns = true; }, remove() { tech.isDamageForGuns = false; } }, { name: "active cooling", descriptionFunction() { return `1.25x fire rate per unequipped gun
(${(1 / Math.pow(0.8, Math.max(0, b.inventory.length - 1))).toFixed(2)}x)` }, //
but not including your equipped gun` }, maxCount: 1, count: 0, frequency: 1, frequencyDefault: 1, allowed: () => b.inventory.length > 1, requires: "at least 2 guns", effect() { tech.isFireRateForGuns = true; b.setFireCD(); }, remove() { tech.isFireRateForGuns = false; b.setFireCD(); } }, { name: "pigeonhole principle", descriptionFunction() { let info = "" if (this.count > 0 && Number.isInteger(tech.buffedGun) && b.inventory.length) { let gun = b.guns[b.inventory[tech.buffedGun]].name info = `
this level: ${(1.3 * Math.max(0, b.inventory.length)).toFixed(2)}x damage for ${gun}` } return `a new gun is chosen to be improved each level
1.3x damage per gun for the chosen gun${info}` }, maxCount: 1, count: 0, frequency: 1, frequencyDefault: 1, allowed() { return b.inventory.length > 1 }, requires: "at least 2 guns", effect() { tech.isGunChoice = true //switches gun on new level //generalist uses the same chosen gun so they match }, remove() { tech.isGunChoice = false; } }, { name: "generalist", description: "spawn 7 guns, but you can't switch guns
your equipped gun cycles after each level", maxCount: 1, count: 0, frequency: 1, frequencyDefault: 1, isInstant: true, isBadRandomOption: true, allowed() { return b.inventory.length < b.guns.length - 5 && b.inventory.length > 1 }, requires: "at least 2 guns, at least 5 unclaimed guns", effect() { tech.isGunCycle = true; for (let i = 0; i < 7; i++) powerUps.spawn(m.pos.x + 10 * Math.random(), m.pos.y + 10 * Math.random(), "gun"); }, remove() { tech.isGunCycle = false; // only set to false if you don't have this tech } }, { name: "ad hoc", descriptionFunction() { return `spawn a ${powerUps.orb.heal()}, ${powerUps.orb.research(1)}, ${powerUps.orb.ammo(1)}, field, gun, or tech
for each of your guns` }, maxCount: 1, //random power up count: 0, frequency: 1, frequencyDefault: 1, isInstant: true, allowed() { return b.inventory.length > 1 }, requires: "at least 2 guns", effect() { for (let i = 0; i < b.inventory.length; i++) { if (Math.random() < 1 / 6) { powerUps.spawn(m.pos.x + 10 * Math.random(), m.pos.y + 10 * Math.random(), "gun"); } else if (Math.random() < 1 / 5) { powerUps.spawn(m.pos.x + 10 * Math.random(), m.pos.y + 10 * Math.random(), "tech"); } else if (Math.random() < 1 / 4) { powerUps.spawn(m.pos.x + 10 * Math.random(), m.pos.y + 10 * Math.random(), "field"); } else if (Math.random() < 1 / 3) { powerUps.spawn(m.pos.x + 10 * Math.random(), m.pos.y + 10 * Math.random(), "heal"); } else if (Math.random() < 1 / 2) { powerUps.spawn(m.pos.x + 10 * Math.random(), m.pos.y + 10 * Math.random(), "ammo"); } else { powerUps.spawn(m.pos.x + 10 * Math.random(), m.pos.y + 10 * Math.random(), "research"); } } }, remove() { } }, { name: "applied science", description: `get a random guntech
for each of your guns`, //spawn ${powerUps.orb.research(1)} and maxCount: 1, count: 0, isInstant: true, frequency: 2, frequencyDefault: 2, allowed() { return b.inventory.length > 1 }, requires: "at least 2 guns", effect() { for (let i = b.inventory.length - 1; i > -1; i--) { //backwards because some tech can remove or add guns const gunTechPool = [] //find gun tech for this gun for (let j = 0, len = tech.tech.length; j < len; j++) { // console.log(j, tech.tech[j].isGunTech, tech.tech[j].allowed(), !tech.tech[j].isJunk, !tech.tech[j].isBadRandomOption, tech.tech[j].count < tech.tech[j].maxCount) const originalActiveGunIndex = b.activeGun //set current gun to active so allowed works b.activeGun = b.inventory[i] //to make the .allowed work for guns that aren't active if (tech.tech[j].isGunTech && tech.tech[j].allowed() && !tech.tech[j].isJunk && !tech.tech[j].isBadRandomOption && tech.tech[j].count < tech.tech[j].maxCount) { const regex = tech.tech[j].requires.search(b.guns[b.inventory[i]].name) //get string index of gun name const not = tech.tech[j].requires.search(' not ') //get string index of ' not ' if (regex !== -1 && (not === -1 || not > regex)) gunTechPool.push(j) //look for the gun name in the requirements, but the gun name needs to show up before the word ' not ' } b.activeGun = originalActiveGunIndex if (!b.guns[b.activeGun].have) { if (b.inventory.length === 0) { b.activeGun = null } else { b.activeGun = b.inventory[0] } b.inventoryGun = 0; } } if (gunTechPool.length) { const index = Math.floor(Math.random() * gunTechPool.length) // console.log(gunTechPool, index, gunTechPool[index], tech.tech[gunTechPool[index]].name) simulation.makeTextLog(`tech.giveTech("${tech.tech[gunTechPool[index]].name}")`, 360) // tech.tech[gunTechPool[index]].isInstant = true //makes it not remove properly under paradigm shift tech.giveTech(gunTechPool[index]) // choose from the gun pool // console.log(gunTechPool, index, gunTechPool[index], tech.tech[gunTechPool[index]].name) // tech.tech[gunTechPool[index]].isFromAppliedScience = true //makes it not remove properly under paradigm shift } } simulation.boldActiveGunHUD(); }, remove() { } }, { name: "supply chain", descriptionFunction() { return `spawn a gun
spawn ${powerUps.orb.ammo(1)} equal to all your active gun's ammo` }, maxCount: 9, count: 0, isInstant: true, frequency: 1, frequencyDefault: 1, allowed() { return true }, requires: "", effect() { let ammoCount = 0 //count ammo if (b.activeGun && b.activeGun !== undefined && b.guns[b.activeGun].have && b.guns[b.activeGun].ammo !== Infinity) { ammoCount += b.guns[b.activeGun].ammo / b.guns[b.activeGun].ammoPack } powerUps.spawnDelay("ammo", Math.ceil(ammoCount)) powerUps.spawn(m.pos.x, m.pos.y, "gun"); }, remove() { } }, { name: "marginal utility", descriptionFunction() { if (this.count === 0) this.gun = Math.floor(Math.random() * (b.guns.length - 1)) //don't pick laser return `2x ammo per ${powerUps.orb.ammo(1)} for ${b.guns[this.gun].name}` }, maxCount: 9, count: 0, frequency: 1, frequencyDefault: 1, allowed() { return true }, requires: "", gun: undefined, effect() { if (this.gun === undefined) this.gun = Math.floor(Math.random() * (b.guns.length - 1)) //don't pick laser simulation.makeTextLog(`${b.guns[this.gun].ammoPack} → ${2 * b.guns[this.gun].ammoPack} average ammo per ${powerUps.orb.ammo(1)} for ${b.guns[this.gun].name}`) b.guns[this.gun].ammoPack *= 2 // simulation.makeTextLog(`${(tech.interestRate * 100).toFixed(0)}% interest on health = ${h > 20 ? h + powerUps.orb.heal(1) : powerUps.orb.heal(h)}`) // for (let i = 0; i < 4; i++) powerUps.spawn(m.pos.x + 10 * Math.random(), m.pos.y + 10 * Math.random(), "ammo"); }, remove() { if (this.count) { b.guns[this.gun].ammoPack /= 2 } } }, { name: "Pareto efficiency", descriptionFunction() { // return `for each gun randomly
gain 5x or 0.2x ammo per ${powerUps.orb.ammo(1)}` // return `randomly adjust your guns by
5x or 0.2x ammo per ${powerUps.orb.ammo(1)}` return `for each of your guns
randomly get 5x or 0.2x ammo per ${powerUps.orb.ammo(1)}` }, maxCount: 1, count: 0, frequency: 1, frequencyDefault: 1, isBadRandomOption: true, allowed() { return b.inventory.length > 2 }, requires: "at least 3 guns", effect() { let options = [] for (let i = 0; i < b.inventory.length; i++) options.push(b.inventory[i]) options = shuffle(options) for (let i = 0; i < options.length; i++) { const index = options[i] const scale = (i < options.length / 2) ? 4 : 0.25 simulation.makeTextLog(`${(b.guns[index].ammoPack).toFixed(1)} ${(b.guns[index].ammoPack * scale).toFixed(1)} average ammo per ${powerUps.orb.ammo(1)} for ${b.guns[index].name}`, Infinity) b.guns[index].ammoPack *= scale } // let options = [] // for (let i = 0; i < b.guns.length - 1; i++) options.push(i) // options = shuffle(options) // for (let i = 0; i < options.length; i++) { // const index = options[i] // const scale = (i < options.length / 2) ? 4 : 0.25 // simulation.makeTextLog(`${(b.guns[index].ammoPack).toFixed(1)} ${(b.guns[index].ammoPack * scale).toFixed(1)} average ammo per ${powerUps.orb.ammo(1)} for ${b.guns[index].name}`, Infinity) // b.guns[index].ammoPack *= scale // } }, remove() { } }, { name: "logistics", description: `2x ammo per ${powerUps.orb.ammo()}, but
ammo is only added to your current gun`, maxCount: 1, count: 0, frequency: 1, frequencyDefault: 1, allowed() { return !tech.isEnergyNoAmmo }, requires: "not non-renewables", effect() { tech.isAmmoForGun = true; }, remove() { tech.isAmmoForGun = false; } }, { name: "cache", link: `cache`, description: `15x ammo per ${powerUps.orb.ammo()}, but
you can't store any more ammo than that`, maxCount: 1, count: 0, frequency: 1, frequencyDefault: 1, allowed() { return !tech.isEnergyNoAmmo }, requires: "not non-renewables", effect() { tech.ammoCap = 15; powerUps.ammo.effect() }, remove() { tech.ammoCap = 0; } }, { name: "catabolism", descriptionFunction() { return `if you fire while out of ammo
spawn ${powerUps.orb.ammo(4)} and ${tech.isEnergyHealth ? "–4 maximum energy" : "–2 maximum health"}` }, maxCount: 1, count: 0, frequency: 1, frequencyDefault: 1, allowed() { return !tech.isEnergyNoAmmo }, requires: "not non-renewables", effect() { tech.isAmmoFromHealth = true; }, remove() { tech.isAmmoFromHealth = false; } }, { name: "non-renewables", description: `2x damage
${powerUps.orb.ammo()} can't spawn`, maxCount: 1, count: 0, frequency: 1, frequencyDefault: 1, allowed() { return !tech.isAmmoFromHealth && !tech.isBoostReplaceAmmo }, requires: "not catabolism, quasiparticles", damage: 2, effect() { tech.damage *= this.damage tech.isEnergyNoAmmo = true; }, remove() { if (this.count && m.alive) tech.damage /= this.damage tech.isEnergyNoAmmo = false; } }, { name: "desublimated ammunition", description: `if crouching
alternating shots use no ammo`, maxCount: 1, count: 0, frequency: 1, frequencyDefault: 1, allowed: () => true, requires: "", effect() { tech.crouchAmmoCount = true }, remove() { tech.crouchAmmoCount = false; } }, { name: "gun turret", description: "if crouching
0.3x damage taken", maxCount: 1, count: 0, frequency: 1, frequencyDefault: 1, allowed() { return true }, requires: "", effect() { tech.isTurret = true }, remove() { tech.isTurret = false; } }, { name: "dead reckoning", description: `if your speed is 0
1.5x damage`, maxCount: 9, count: 0, frequency: 1, frequencyDefault: 1, allowed() { return true }, requires: "", effect() { tech.restDamage += 0.5 }, remove() { tech.restDamage = 1; } }, { name: "Newtons 1st law", descriptionFunction() { return `damage taken is proportional to your speed
up to 0.2x damage taken at 55 speed (${(1 - Math.min((tech.speedAdded + player.speed) * 0.0193, 0.8)).toFixed(2)}x)` }, description: "", maxCount: 1, count: 0, frequency: 1, frequencyDefault: 1, allowed() { return true }, requires: "", effect() { tech.isSpeedHarm = true //max at speed = 40 }, remove() { tech.isSpeedHarm = false } }, { name: "Newtons 2nd law", descriptionFunction() { return `damage is proportional to your speed
up to 2x damage at 55 speed (${(1 + Math.min(1, ((tech.speedAdded + player.speed) * 0.0193))).toFixed(2)}x)` }, maxCount: 1, count: 0, frequency: 1, frequencyDefault: 1, allowed() { return true }, requires: "", effect() { tech.isSpeedDamage = true //max at speed = 40 }, remove() { tech.isSpeedDamage = false } }, { name: "modified Newtonian dynamics", descriptionFunction() { return `your speed counts as +20 higher
(for Newton's 1st and 2nd laws)` }, maxCount: 1, count: 0, frequency: 2, frequencyDefault: 2, allowed() { return tech.isSpeedDamage || tech.isSpeedHarm }, requires: "Newtons 1st or 2nd law", effect() { tech.speedAdded = 20 }, remove() { tech.speedAdded = 0 } }, { name: "kinetic bombardment", description: "far away mobs take more damage
up to 1.3x damage at 3000 displacement", maxCount: 1, count: 0, frequency: 1, frequencyDefault: 1, allowed() { return true }, requires: "", effect() { tech.isFarAwayDmg = true; //used in mob.damage() }, remove() { tech.isFarAwayDmg = false; } }, { name: "microstates", link: `microstates`, descriptionFunction() { return `use ${powerUps.orb.research(3)}
1.01x damage per bullet or bot` }, maxCount: 1, count: 0, frequency: 1, frequencyDefault: 1, allowed() { return powerUps.research.count > 2 || build.isExperimentSelection }, requires: "", effect() { tech.isDamageFromBulletCount = true for (let i = 0; i < 3; i++) { if (powerUps.research.count > 0) powerUps.research.changeRerolls(-1) } }, remove() { tech.isDamageFromBulletCount = false if (this.count > 0) { powerUps.research.changeRerolls(3) } } }, { name: "regression", description: "bullet collisions increase vulnerability to
damage by 1.05x for mobs (+1.025x for bosses)", maxCount: 1, count: 0, frequency: 1, frequencyDefault: 1, allowed() { return true }, requires: "", effect() { tech.isLessDamageReduction = true }, remove() { tech.isLessDamageReduction = false } }, { name: "simulated annealing", description: "1.2x damage
0.8x fire rate", maxCount: 1, count: 0, frequency: 1, frequencyDefault: 1, allowed() { return true }, damage: 1.2, effect() { tech.damage *= this.damage tech.slowFire = 1.25 b.setFireCD(); }, remove() { if (this.count && m.alive) tech.damage /= this.damage tech.slowFire = 1; b.setFireCD(); } }, { name: "heuristics", description: "1.3x fire rate", maxCount: 9, count: 0, frequency: 1, frequencyDefault: 1, allowed() { return true }, requires: "", effect() { tech.fireRate *= 0.77 b.setFireCD(); }, remove() { tech.fireRate = 1; b.setFireCD(); } }, { name: "mechatronics", descriptionFunction() { let damageTotal = 1 for (let i = 0; i < this.damageSoFar.length; i++) damageTotal *= this.damageSoFar[i] let currentDamage = "" if (this.count) currentDamage = `
(${(damageTotal).toFixed(2)}x)` return `randomly gain between 1x and 1.3x damage` + currentDamage }, maxCount: 9, count: 0, frequency: 1, frequencyDefault: 1, allowed() { return true }, requires: "", damage: 1.1, damageSoFar: [], //tracks the random damage upgrades so it can be removed and in descriptionFunction effect() { const damage = (Math.floor((Math.random() * 0.3 + 1) * 100)) / 100 tech.damage *= damage this.damageSoFar.push(damage) }, remove() { if (this.count && m.alive) for (let i = 0; i < this.damageSoFar.length; i++) tech.damage /= this.damageSoFar[i] this.damageSoFar.length = 0 } }, { name: "dynamical systems", description: `use ${powerUps.orb.research(2)}
1.3x damage`, // isFieldTech: true, maxCount: 1, count: 0, frequency: 1, frequencyDefault: 1, allowed() { return powerUps.research.count > 1 || build.isExperimentSelection }, requires: "", // allowed() { // return (m.fieldMode === 5 || m.fieldMode === 7 || m.fieldMode === 8) && (build.isExperimentSelection || powerUps.research.count > 1) // }, // requires: "cloaking, pilot wave, or plasma torch", damage: 1.3, effect() { tech.damage *= this.damage 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 && m.alive) { tech.damage /= this.damage powerUps.research.changeRerolls(2) } } }, { name: "anti-shear topology", link: `anti-shear topology`, description: "your bullets last 1.3x longer", //
drone spore worm flea missile foam wave neutron ice", maxCount: 3, count: 0, frequency: 1, frequencyDefault: 1, allowed: () => true, requires: "", effect() { tech.bulletsLastLonger += 0.3 }, remove() { tech.bulletsLastLonger = 1; } }, { name: "fracture analysis", description: "if a mob is stunned it takes
5x damage from bullet impacts", maxCount: 1, count: 0, frequency: 2, frequencyDefault: 2, allowed() { return tech.isStunField || tech.oneSuperBall || tech.isCloakStun || tech.isOrbitBotUpgrade || tech.isStun }, requires: "a stun effect", effect() { tech.isCrit = true; }, remove() { tech.isCrit = false; } }, { name: "shear stress", description: "after mobs die
they fire a nail at nearby mobs", maxCount: 9, count: 0, frequency: 1, frequencyDefault: 1, allowed() { return !tech.sporesOnDeath && !tech.isExplodeMob && !tech.botSpawner && !tech.isMobBlockFling && !tech.iceIXOnDeath }, requires: "no other mob death tech", effect() { tech.nailsDeathMob++ }, remove() { tech.nailsDeathMob = 0; } }, { name: "thermal runaway", description: "after mobs die they explode", maxCount: 1, count: 0, frequency: 1, frequencyDefault: 1, allowed() { return !tech.sporesOnDeath && !tech.nailsDeathMob && !tech.botSpawner && !tech.isMobBlockFling && !tech.iceIXOnDeath }, requires: "no other mob death tech", effect() { tech.isExplodeMob = true; }, remove() { tech.isExplodeMob = false; } }, { name: "zoospore vector", link: `zoospore vector`, descriptionFunction() { return `after mobs die there is a 10% chance
they grow ${b.guns[6].nameString('s')}` }, // description: "after mobs die
they have a +10% chance to grow spores", maxCount: 9, count: 0, frequency: 1, frequencyDefault: 1, allowed() { return !tech.nailsDeathMob && !tech.isExplodeMob && !tech.botSpawner && !tech.isMobBlockFling && !tech.iceIXOnDeath }, requires: "no other mob death tech", effect() { tech.sporesOnDeath += 0.1; // if (tech.isSporeWorm) { // for (let i = 0; i < 4; i++) b.worm(m.pos) // } else { // for (let i = 0; i < 8; i++) b.spore(m.pos) // } }, remove() { tech.sporesOnDeath = 0; } }, { name: "propagator", description: "after mobs die advance time 0.5 seconds
1.6x damage", maxCount: 3, count: 0, frequency: 1, frequencyDefault: 1, allowed: () => true, requires: "", effect() { tech.deathSkipTime++ }, remove() { tech.deathSkipTime = 0 } }, { name: "collider", descriptionFunction() { return `after mobs die existing power ups
collide to form new power ups` // return `after mobs die there is a +33% chance to convert
${powerUps.orb.heal()}, ${powerUps.orb.ammo()}, ${powerUps.orb.research(1)}, tech, field, gun into other types` }, maxCount: 1, count: 0, frequency: 1, frequencyDefault: 1, allowed: () => true, requires: "", effect() { tech.collidePowerUps = true }, remove() { tech.collidePowerUps = false } }, { name: "bubble fusion", descriptionFunction() { return `after destroying a mob's shield
spawn 1-2 ${powerUps.orb.heal()}, ${powerUps.orb.ammo()}, or ${powerUps.orb.research(1)} (once per mob)` }, maxCount: 1, count: 0, frequency: 1, frequencyDefault: 1, isHealTech: true, allowed() { return true }, requires: "", effect() { tech.isShieldAmmo = true; }, remove() { tech.isShieldAmmo = false; } }, { name: "enthalpy", descriptionFunction() { return `after mobs die
they have an 8% chance to spawn ${powerUps.orb.heal(1)}` }, maxCount: 9, count: 0, frequency: 1, frequencyDefault: 1, isHealTech: true, allowed() { return true }, requires: "", effect() { tech.healSpawn += 0.08; }, remove() { tech.healSpawn = 0; } }, { name: "cascading failure", description: "3x damage to mobs below 25% durability", maxCount: 1, count: 0, frequency: 3, frequencyDefault: 3, allowed() { return tech.mobSpawnWithHealth > 0 }, requires: "reaction inhibitor", effect() { tech.isMobLowHealth = true }, remove() { tech.isMobLowHealth = false } }, { name: "reaction inhibitor", description: "mobs spawn with 0.88x initial durability", maxCount: 3, count: 0, frequency: 1, frequencyDefault: 1, allowed() { return !tech.isMobFullHealthCloak }, requires: "not topological defect", effect() { tech.mobSpawnWithHealth++ mobs.setMobSpawnHealth() for (let i = 0; i < mob.length; i++) { if (mob.health > mobs.mobSpawnWithHealth) mob.health = mobs.mobSpawnWithHealth } }, remove() { tech.mobSpawnWithHealth = 0 mobs.setMobSpawnHealth() } }, { name: "scrap bots", link: `scrap bots`, description: "after mobs die you have a 33% chance
to build scrap bots that operate for 15 seconds", maxCount: 3, count: 0, frequency: 1, frequencyDefault: 1, isBotTech: true, allowed() { return !tech.sporesOnDeath && !tech.nailsDeathMob && !tech.isExplodeMob && !tech.isMobBlockFling && !tech.iceIXOnDeath }, requires: "no other mob death tech", effect() { tech.botSpawner += 0.33; }, remove() { tech.botSpawner = 0; } }, { name: "scrap refit", link: `scrap refit`, description: "after mobs die
reset scrap bots to 15 seconds of operation", maxCount: 1, count: 0, frequency: 3, frequencyDefault: 3, isBotTech: true, allowed() { return tech.botSpawner }, requires: "scrap bots", effect() { tech.isBotSpawnerReset = true; }, remove() { tech.isBotSpawnerReset = false; } }, { name: "nail-bot", link: `nail-bot`, description: "a bot fires nails at mobs in line of sight", maxCount: 9, count: 0, frequency: 1, frequencyDefault: 1, isBot: true, isBotTech: true, allowed() { return true }, requires: "", effect() { tech.nailBotCount++; b.nailBot(); }, remove() { if (this.count) { tech.nailBotCount -= this.count; b.clearPermanentBots(); b.respawnBots(); } } }, { name: "nail-bot upgrade", link: `nail-bot upgrade`, description: "convert your bots to nail-bots
5x fire rate and 1.4x nail velocity", maxCount: 1, count: 0, frequency: 3, frequencyDefault: 3, isBotTech: true, allowed() { return tech.nailBotCount > 1 && !b.hasBotUpgrade() }, requires: "2 or more nail bots and no other bot upgrade", effect() { tech.isNailBotUpgrade = true b.convertBotsTo("nail-bot") for (let i = 0; i < bullet.length; i++) { if (bullet[i].botType === 'nail') bullet[i].isUpgraded = true } tech.setBotTechFrequency() tech.setTechFrequency("nail-bot", 5) }, remove() { if (this.count) { for (let i = 0; i < bullet.length; i++) { if (bullet[i].botType === 'nail') bullet[i].isUpgraded = false } tech.setBotTechFrequency(1) } tech.isNailBotUpgrade = false } }, { name: "foam-bot", link: `foam-bot`, description: "a bot sprays sticky foam at nearby mobs", maxCount: 9, count: 0, frequency: 1, frequencyDefault: 1, isBot: true, isBotTech: true, allowed() { return true }, requires: "", effect() { tech.foamBotCount++; b.foamBot(); }, remove() { if (this.count) { tech.foamBotCount -= this.count; b.clearPermanentBots(); b.respawnBots(); } } }, { name: "foam-bot upgrade", link: `foam-bot upgrade`, description: "convert your bots to foam-bots
3x foam size and fire rate", maxCount: 1, count: 0, frequency: 3, frequencyDefault: 3, isBotTech: true, allowed() { return tech.foamBotCount > 1 && !b.hasBotUpgrade() }, requires: "2 or more foam bots and no other bot upgrade", effect() { tech.isFoamBotUpgrade = true b.convertBotsTo("foam-bot") for (let i = 0; i < bullet.length; i++) { if (bullet[i].botType === 'foam') bullet[i].isUpgraded = true } tech.setBotTechFrequency() tech.setTechFrequency("foam-bot", 5) }, remove() { if (this.count) { for (let i = 0; i < bullet.length; i++) { if (bullet[i].botType === 'foam') bullet[i].isUpgraded = false } tech.setBotTechFrequency(1) } tech.isFoamBotUpgrade = false } }, { name: "sound-bot", link: `sound-bot`, description: "a bot emits expanding arcs
aimed towards nearby mobs", maxCount: 9, count: 0, frequency: 1, frequencyDefault: 1, isBot: true, isBotTech: true, allowed() { return true }, requires: "", effect() { tech.soundBotCount++; b.soundBot(); }, remove() { if (this.count) { tech.soundBotCount -= this.count; b.clearPermanentBots(); b.respawnBots(); } } }, { name: "sound-bot upgrade", link: `sound-bot upgrade`, description: "convert your bots to sound-bots
2.5x wave fire rate and 2.5x damage", maxCount: 1, count: 0, frequency: 3, frequencyDefault: 3, isBotTech: true, allowed() { return tech.soundBotCount > 1 && !b.hasBotUpgrade() }, requires: "2 or more sound bots and no other bot upgrade", effect() { tech.isSoundBotUpgrade = true b.convertBotsTo("sound-bot") for (let i = 0; i < bullet.length; i++) { if (bullet[i].botType === 'sound') bullet[i].isUpgraded = true } tech.setBotTechFrequency() tech.setTechFrequency("sound-bot", 5) }, remove() { if (this.count) { for (let i = 0; i < bullet.length; i++) { if (bullet[i].botType === 'sound') bullet[i].isUpgraded = false } tech.setBotTechFrequency(1) } tech.isSoundBotUpgrade = false } }, { name: "boom-bot", link: `boom-bot`, description: "a bot defends the space around you
ignites an explosion after hitting a mob", maxCount: 9, count: 0, frequency: 1, frequencyDefault: 1, isBot: true, isBotTech: true, allowed() { return true }, requires: "", effect() { tech.boomBotCount++; b.boomBot(); }, remove() { if (this.count) { tech.boomBotCount -= this.count; b.clearPermanentBots(); b.respawnBots(); } } }, { name: "boom-bot upgrade", link: `boom-bot upgrade`, description: "convert your bots to boom-bots
4x explosion damage and size", maxCount: 1, count: 0, frequency: 3, frequencyDefault: 3, isBotTech: true, allowed() { return tech.boomBotCount > 1 && !b.hasBotUpgrade() }, requires: "2 or more boom bots and no other bot upgrade", effect() { tech.isBoomBotUpgrade = true b.convertBotsTo("boom-bot") for (let i = 0; i < bullet.length; i++) { if (bullet[i].botType === 'boom') bullet[i].isUpgraded = true } tech.setBotTechFrequency() tech.setTechFrequency("boom-bot", 5) }, remove() { if (this.count) { for (let i = 0; i < bullet.length; i++) { if (bullet[i].botType === 'boom') bullet[i].isUpgraded = false } tech.setBotTechFrequency(1) } tech.isBoomBotUpgrade = false } }, { name: "laser-bot", link: `laser-bot`, description: "a bot uses energy to emit a laser beam
that targets nearby mobs", maxCount: 9, count: 0, frequency: 1, frequencyDefault: 1, isBot: true, isBotTech: true, allowed() { return m.maxEnergy > 0.5 }, requires: "maximum energy above 50", effect() { tech.laserBotCount++; b.laserBot(); }, remove() { if (this.count) { tech.laserBotCount -= this.count; b.clearPermanentBots(); b.respawnBots(); } } }, { name: "laser-bot upgrade", link: `laser-bot upgrade`, description: "convert your bots to laser-bots
2.00x damage, efficiency, and range", maxCount: 1, count: 0, frequency: 3, frequencyDefault: 3, isBotTech: true, allowed() { return tech.laserBotCount > 1 && !b.hasBotUpgrade() }, requires: "2 or more laser bots and no other bot upgrade", effect() { tech.isLaserBotUpgrade = true b.convertBotsTo("laser-bot") for (let i = 0; i < bullet.length; i++) { if (bullet[i].botType === 'laser') bullet[i].isUpgraded = true } tech.setBotTechFrequency() tech.setTechFrequency("laser-bot", 5) }, remove() { if (this.count) { for (let i = 0; i < bullet.length; i++) { if (bullet[i].botType === 'laser') bullet[i].isUpgraded = false } tech.setBotTechFrequency(1) } tech.isLaserBotUpgrade = false } }, { name: "orbital-bot", link: `orbital-bot`, description: "a bot is locked in orbit around you
stuns and damages mobs on contact", maxCount: 9, count: 0, frequency: 1, frequencyDefault: 1, isBot: true, isBotTech: true, allowed() { return true }, requires: "", effect() { b.orbitBot(); tech.orbitBotCount++; }, remove() { if (this.count) { tech.orbitBotCount -= this.count; b.clearPermanentBots(); b.respawnBots(); } } }, { name: "orbital-bot upgrade", link: `orbital-bot upgrade`, description: "convert your bots to orbital-bots
4x orbital damage and 1.5x radius", maxCount: 1, count: 0, frequency: 3, frequencyDefault: 3, isBotTech: true, allowed() { return tech.orbitBotCount > 1 && !b.hasBotUpgrade() }, requires: "2 or more orbital bots and no other bot upgrade", effect() { tech.isOrbitBotUpgrade = true b.convertBotsTo("orbital-bot") const range = 190 + 120 * tech.isOrbitBotUpgrade for (let i = 0; i < bullet.length; i++) { if (bullet[i].botType === 'orbit') { bullet[i].isUpgraded = true bullet[i].range = range bullet[i].orbitalSpeed = Math.sqrt(0.25 / range) } } tech.setBotTechFrequency() tech.setTechFrequency("orbital-bot", 5) }, remove() { if (this.count) { const range = 190 + 100 * tech.isOrbitBotUpgrade for (let i = 0; i < bullet.length; i++) { if (bullet[i].botType === 'orbit') { bullet[i].range = range bullet[i].orbitalSpeed = Math.sqrt(0.25 / range) } } tech.setBotTechFrequency(1) } tech.isOrbitBotUpgrade = false } }, { name: "dynamo-bot", link: `dynamo-bot`, description: "a bot damages mobs while it traces your path
+8 energy per second when nearby", maxCount: 9, count: 0, frequency: 1, frequencyDefault: 1, isBot: true, isBotTech: true, allowed() { return true }, requires: "", effect() { tech.dynamoBotCount++; b.dynamoBot(); }, remove() { if (this.count) { tech.dynamoBotCount -= this.count; b.clearPermanentBots(); b.respawnBots(); } } }, { name: "dynamo-bot upgrade", link: `dynamo-bot upgrade`, description: "convert your bots to dynamo-bots
+24 energy per second when nearby", maxCount: 1, count: 0, frequency: 3, frequencyDefault: 3, isBotTech: true, allowed() { return tech.dynamoBotCount > 1 && !b.hasBotUpgrade() }, requires: "2 or more dynamo bots and no other bot upgrade", effect() { tech.isDynamoBotUpgrade = true b.convertBotsTo("dynamo-bot") for (let i = 0; i < bullet.length; i++) { if (bullet[i].botType === 'dynamo') bullet[i].isUpgraded = true } tech.setBotTechFrequency() tech.setTechFrequency("dynamo-bot", 5) }, remove() { if (this.count) { for (let i = 0; i < bullet.length; i++) { if (bullet[i].botType === 'dynamo') bullet[i].isUpgraded = false } tech.setBotTechFrequency(1) } tech.isDynamoBotUpgrade = false } }, { name: "perimeter defense", description: "for each permanent bot
0.95x damage taken", maxCount: 1, count: 0, frequency: 2, frequencyDefault: 2, isBotTech: true, allowed() { return b.totalBots() > 1 }, requires: "at least 2 bots", effect() { tech.isBotArmor = true }, remove() { tech.isBotArmor = false } }, { name: "network effect", description: "for each permanent bot
1.05x damage", maxCount: 1, count: 0, frequency: 2, frequencyDefault: 2, isBotTech: true, allowed() { return b.totalBots() > 1 }, requires: "at least 2 bots", effect() { tech.isBotDamage = true }, remove() { tech.isBotDamage = false } }, { name: "bot fabrication", link: `bot fabrication`, descriptionFunction() { return `after you collect ${powerUps.orb.research(2 + Math.floor(0.1666 * b.totalBots()))}use them to build a
random bot (+1 cost every 5 bots)` }, // description: `if you collect ${powerUps.orb.research(2)}use them to build a
random bot (+1 cost every 5 bots)`, maxCount: 1, count: 0, frequency: 2, frequencyDefault: 2, isBotTech: true, allowed() { return powerUps.research.count > 1 || build.isExperimentSelection }, requires: "at least 2 research", effect() { tech.isRerollBots = true; powerUps.research.changeRerolls(0) simulation.makeTextLog(`m.research = 0`) }, remove() { tech.isRerollBots = false; // this.description = `if you collect ${powerUps.orb.research(2 + Math.floor(0.2 * b.totalBots()))}use them to build a
random bot (+1 cost every 5 bots)` } }, { name: "open-source", description: `tech, fields, and guns have +1 bot choice
3x bottech frequency`, maxCount: 1, count: 0, frequency: 1, frequencyDefault: 1, isBotTech: true, allowed() { return b.totalBots() > 1 && !tech.isDeterminism }, requires: "at least 2 bots", effect() { tech.isExtraBotOption = true for (let i = 0, len = tech.tech.length; i < len; i++) { if (tech.tech[i].isBotTech) tech.tech[i].frequency *= 3 } }, remove() { if (this.count > 0) { for (let i = 0, len = tech.tech.length; i < len; i++) { if (tech.tech[i].isBotTech) tech.tech[i].frequency = Math.ceil(tech.tech[i].frequency / 3) } } tech.isExtraBotOption = false } }, { name: "ersatz bots", link: `ersatz bots`, description: "double your current permanent bots
remove all guns in your inventory", maxCount: 1, count: 0, frequency: 2, frequencyDefault: 2, isBotTech: true, isInstant: true, isBadRandomOption: true, numberOfGunsLost: 0, allowed() { return b.totalBots() > 3 && !build.isExperimentSelection }, requires: "NOT EXPERIMENT MODE, at least 4 bots", effect() { this.numberOfGunsLost = b.inventory.length b.inventory = []; //removes guns and ammo for (let i = 0, len = b.guns.length; i < len; ++i) { b.guns[i].count = 0; b.guns[i].have = false; if (b.guns[i].ammo != Infinity) b.guns[i].ammo = 0; } tech.buffedGun = 0 b.activeGun = null; b.inventoryGun = 0; simulation.drawCursor = simulation.drawCursorBasic //set cross hairs simulation.makeGunHUD(); //double bots for (let i = 0; i < tech.nailBotCount; i++) b.nailBot(); tech.nailBotCount *= 2 for (let i = 0; i < tech.laserBotCount; i++) b.laserBot(); tech.laserBotCount *= 2 for (let i = 0; i < tech.foamBotCount; i++) b.foamBot(); tech.foamBotCount *= 2 for (let i = 0; i < tech.boomBotCount; i++) b.boomBot(); tech.boomBotCount *= 2 for (let i = 0; i < tech.orbitBotCount; i++) b.orbitBot(); tech.orbitBotCount *= 2 for (let i = 0; i < tech.dynamoBotCount; i++) b.dynamoBot(); tech.dynamoBotCount *= 2 for (let i = 0; i < tech.soundBotCount; i++) b.soundBot(); tech.soundBotCount *= 2 for (let i = 0; i < tech.plasmaBotCount; i++) b.plasmaBot(); tech.plasmaBotCount *= 2 for (let i = 0; i < tech.missileBotCount; i++) b.missileBot(); tech.missileBotCount *= 2 }, remove() { // if (this.count) { // //return guns // for (let i = 0; i < this.numberOfGunsLost; i++) powerUps.spawn(m.pos.x + 60 * (Math.random() - 0.5), m.pos.y + 60 * (Math.random() - 0.5), "gun"); // this.numberOfGunsLost = 0; // //half all current bots // tech.nailBotCount = Math.round(tech.nailBotCount / 2) // tech.laserBotCount = Math.round(tech.laserBotCount / 2) // tech.foamBotCount = Math.round(tech.foamBotCount / 2) // tech.soundBotCount = Math.round(tech.soundBotCount / 2) // tech.boomBotCount = Math.round(tech.boomBotCount / 2) // tech.orbitBotCount = Math.round(tech.orbitBotCount / 2) // tech.dynamoBotCount = Math.round(tech.dynamoBotCount / 2) // tech.plasmaBotCount = Math.round(tech.plasmaBotCount / 2) // tech.missileBotCount = Math.round(tech.missileBotCount / 2) // b.clearPermanentBots(); // b.respawnBots(); // } } }, { name: "robotics", description: `spawn 2 random bots`, maxCount: 1, count: 0, frequency: 1, frequencyDefault: 1, isBotTech: true, isInstant: true, allowed() { return b.totalBots() > 2 }, requires: "at least 3 bots", effect() { for (let i = 0; i < 2; i++) b.randomBot() }, remove() { } }, { name: "bot manufacturing", description: `use ${powerUps.orb.research(2)} to build
3 random bots`, maxCount: 1, count: 0, frequency: 1, frequencyDefault: 1, isBotTech: true, isInstant: true, allowed() { return b.totalBots() > 3 && powerUps.research.count > 1 }, requires: "at least 4 bots", 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 a random type`, maxCount: 1, count: 0, frequency: 1, frequencyDefault: 1, isBotTech: true, isInstant: true, allowed() { return b.totalBots() > 5 && powerUps.research.count > 2 }, requires: "at least 6 bots", effect() { requestAnimationFrame(() => { 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("sound-bot upgrade") for (let i = 0; i < num; i++) { b.soundBot() tech.soundBotCount++; } simulation.makeTextLog(`tech.isSoundBotUpgrade = 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`) }) 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: "decorrelation", description: "if your gun and field are unused for 2 seconds
0.3x damage taken", maxCount: 1, count: 0, frequency: 1, frequencyDefault: 1, allowed() { return !tech.isRewindField }, requires: "not retrocausality", effect() { tech.isNoFireDefense = true }, remove() { tech.isNoFireDefense = false } }, { name: "anticorrelation", description: "if your gun and field are unused for 2 seconds
2x damage", maxCount: 1, count: 0, frequency: 1, frequencyDefault: 1, allowed() { return !tech.isRewindField }, requires: "not retrocausality", effect() { tech.isNoFireDamage = true }, remove() { tech.isNoFireDamage = false } }, { name: "mass driver", description: "4x block collision damage", maxCount: 1, count: 0, frequency: 1, frequencyDefault: 1, allowed() { return m.fieldMode !== 9 && !tech.isTokamak && !tech.isReel }, requires: "not wormhole, reel, tokamak", effect() { tech.blockDamage = 0.3 }, remove() { tech.blockDamage = 0.075 } }, { name: "inflation", link: `inflation`, description: "if holding a block 0.1x damage taken
after throwing a block it expands 3x", maxCount: 1, count: 0, frequency: 3, frequencyDefault: 3, allowed() { return (tech.blockDamage > 0.075 || tech.isPrinter) && m.fieldMode !== 8 && m.fieldMode !== 9 && !tech.isTokamak }, requires: "mass driver, not pilot wave, tokamak, wormhole", effect() { tech.isAddBlockMass = true }, remove() { tech.isAddBlockMass = false } }, { name: "restitution", description: "2.5x block collision damage
after throwing a block it becomes very bouncy", maxCount: 1, count: 0, frequency: 3, frequencyDefault: 3, allowed() { return (tech.blockDamage > 0.075 || tech.isPrinter) && m.fieldUpgrades[m.fieldMode].name !== "pilot wave" && m.fieldUpgrades[m.fieldMode].name !== "wormhole" && !tech.isTokamak }, requires: "mass driver, not pilot wave, tokamak, wormhole", effect() { tech.isBlockRestitution = true }, remove() { tech.isBlockRestitution = false } }, { name: "flywheel", description: "2.5x block collision damage
after a mob dies its block is flung at mobs", maxCount: 1, count: 0, frequency: 3, frequencyDefault: 3, allowed() { return (tech.blockDamage > 0.075 || tech.isPrinter) && !tech.nailsDeathMob && !tech.sporesOnDeath && !tech.isExplodeMob && !tech.botSpawner && !tech.iceIXOnDeath }, requires: "mass driver, no other mob death tech", effect() { tech.isMobBlockFling = true }, remove() { tech.isMobBlockFling = false } }, { name: "buckling", descriptionFunction() { return `if a block kills a mob there's a 50% chance
to spawn either ${powerUps.orb.coupling(1)}, ${powerUps.orb.boost(1)}, ${powerUps.orb.heal()}, ${powerUps.orb.ammo()}, or ${powerUps.orb.research(1)}` }, maxCount: 1, count: 0, frequency: 3, frequencyDefault: 3, allowed() { return (tech.blockDamage > 0.075 || tech.isPrinter) && m.fieldUpgrades[m.fieldMode].name !== "pilot wave" && !tech.isTokamak }, requires: "mass driver, not pilot wave, tokamak", effect() { tech.isBlockPowerUps = true }, remove() { tech.isBlockPowerUps = false } }, { name: "first derivative", descriptionFunction() { return `while your first gun is equipped
0.85x damage taken per gun (${(0.85 ** b.inventory.length).toFixed(2)}x)` }, maxCount: 1, count: 0, frequency: 1, frequencyDefault: 1, allowed() { return true }, requires: "", effect() { tech.isFirstDer = true }, remove() { tech.isFirstDer = false; } }, { name: "MACHO", description: "a massive compact halo object follows you
0.4x damage taken inside the MACHO", maxCount: 1, count: 0, frequency: 1, frequencyDefault: 1, allowed() { return true }, requires: "", effect() { tech.isMACHO = true; //this harm reduction comes from the particle toggling tech.isHarmMACHO spawn.MACHO() }, remove() { tech.isMACHO = false; tech.isHarmMACHO = false; for (let i = 0, len = mob.length; i < len; i++) { if (mob[i].isMACHO) mob[i].alive = false; } } }, { name: "entropic gravity", description: "crouching pulls the MACHO towards you
1.5x for all MACHO effects", maxCount: 1, count: 0, frequency: 2, frequencyDefault: 2, allowed() { return tech.isMACHO }, requires: "MACHO", effect() { tech.isMoveMACHO = true }, remove() { tech.isMoveMACHO = false } }, { name: "axion", description: "while inside the MACHO
2x damage", maxCount: 1, count: 0, frequency: 2, frequencyDefault: 2, allowed() { return tech.isMACHO }, requires: "MACHO", effect() { tech.isAxion = true }, remove() { tech.isAxion = false } }, { name: "dark star", description: `mobs inside the MACHO are damaged
1.3x MACHO radius`, maxCount: 1, count: 0, frequency: 2, frequencyDefault: 2, allowed() { return tech.isMACHO }, requires: "MACHO", effect() { tech.isDarkStar = true }, remove() { tech.isDarkStar = false } }, { name: "ablative drones", description: "after losing health there is a chance
to rebuild your broken parts as drones", maxCount: 1, count: 0, frequency: 1, frequencyDefault: 1, allowed: () => true, requires: "", effect() { tech.isDroneOnDamage = true; // for (let i = 0; i < 4; i++) b.drone() }, remove() { tech.isDroneOnDamage = false; } }, { name: "non-Newtonian armor", link: `non-Newtonian armor`, description: "after mob collisions
0.3x damage taken for 10 seconds", maxCount: 1, count: 0, frequency: 1, frequencyDefault: 1, allowed() { return true }, requires: "", effect() { tech.isHarmArmor = true; }, remove() { tech.isHarmArmor = false; } }, { name: "tessellation", description: `use ${powerUps.orb.research(2)}
0.6x damage taken`, maxCount: 1, count: 0, frequency: 1, frequencyDefault: 1, allowed() { return powerUps.research.count > 1 || build.isExperimentSelection }, requires: "", 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: "Pauli exclusion", description: `for 6 seconds after mob collisions
become invulnerable and inhibit energy regen`, maxCount: 9, count: 0, frequency: 1, frequencyDefault: 1, allowed() { return true }, requires: "", effect() { m.collisionImmuneCycles += 360; if (m.immuneCycle < m.cycle + m.collisionImmuneCycles) m.immuneCycle = m.cycle + m.collisionImmuneCycles; //player is immune to damage }, remove() { m.collisionImmuneCycles = 30; } }, { name: "spin-statistics theorem", description: `for 1.9 seconds out of every 7 seconds
become invulnerable and inhibit energy regen`, maxCount: 3, count: 0, frequency: 1, frequencyDefault: 1, allowed() { return true //m.collisionImmuneCycles > 30 }, requires: "", effect() { tech.cyclicImmunity += 114; }, remove() { tech.cyclicImmunity = 0; } }, { name: "fermion", description: `for 6 seconds after mobs die
become invulnerable and inhibit energy regen`, maxCount: 1, count: 0, frequency: 1, frequencyDefault: 1, allowed() { return true }, requires: "", effect() { tech.isMobDeathImmunity = true; }, remove() { tech.isMobDeathImmunity = false; } }, { name: "abelian group", description: `4x damage while invulnerable`, maxCount: 1, count: 0, frequency: 2, frequencyDefault: 2, allowed() { return tech.isMobDeathImmunity || tech.cyclicImmunity || m.collisionImmuneCycles > 30 }, requires: "invincibility tech", effect() { tech.isImmunityDamage = true; }, remove() { tech.isImmunityDamage = false; } }, { name: "refrigerant", descriptionFunction() { return `after losing at least 5 ${tech.isEnergyHealth ? "energy" : "health"}
freeze all mobs for 7 seconds` }, maxCount: 1, count: 0, frequency: 1, frequencyDefault: 1, allowed() { return true }, requires: "", effect() { tech.isHarmFreeze = true; }, remove() { tech.isHarmFreeze = false; } }, { name: "piezoelectricity", description: "if you collide with a mob
generate +2048 energy", maxCount: 1, count: 0, frequency: 1, frequencyDefault: 1, allowed() { return !tech.isEnergyHealth }, requires: "not mass-energy", effect() { tech.isPiezo = true; // if (simulation.isTextLogOpen) m.energy += 20.48; }, remove() { tech.isPiezo = false; } }, { name: "ground state", description: "+300 maximum energy
0.66x passive energy generation", maxCount: 1, count: 0, frequency: 1, frequencyDefault: 1, allowed() { return !tech.isTimeCrystals }, requires: "not time crystals", effect() { tech.isGroundState = true m.setFieldRegen() m.setMaxEnergy() }, remove() { tech.isGroundState = false m.setFieldRegen() m.setMaxEnergy() } }, { name: "heat engine", description: `1.4x damage
–50 maximum energy`, maxCount: 1, count: 0, frequency: 1, frequencyDefault: 1, allowed: () => true, requires: "not CPT", damage: 1.4, effect() { tech.damage *= this.damage tech.isMaxEnergyTech = true; m.setMaxEnergy() }, remove() { if (this.count && m.alive) tech.damage /= this.damage tech.isMaxEnergyTech = false; m.setMaxEnergy() } }, { name: "exothermic process", description: "1.6x damage
after mobs die –5 energy", maxCount: 1, count: 0, frequency: 1, frequencyDefault: 1, allowed() { return true }, requires: "", damage: 1.6, effect() { tech.damage *= this.damage tech.isEnergyLoss = true; }, remove() { if (this.count && m.alive) tech.damage /= this.damage tech.isEnergyLoss = false; } }, { name: "Gibbs free energy", descriptionFunction() { return `use ${powerUps.orb.research(2)}
1.01x damage per energy below 100 (${(1 + Math.max(0, 1 - m.energy)).toFixed(2)}x)` }, maxCount: 1, count: 0, frequency: 1, frequencyDefault: 1, allowed() { return powerUps.research.count > 1 || build.isExperimentSelection }, requires: "", effect() { tech.isLowEnergyDamage = true; for (let i = 0; i < 2; i++) { if (powerUps.research.count > 0) powerUps.research.changeRerolls(-1) } }, remove() { tech.isLowEnergyDamage = false; if (this.count > 0) { powerUps.research.changeRerolls(2) } } }, { name: "overcharge", description: "+88 maximum energy
+5% JUNKtech chance", maxCount: 9, count: 0, frequency: 1, frequencyDefault: 1, allowed() { return true }, requires: "", effect() { tech.bonusEnergy += 0.88 m.setMaxEnergy() this.refundAmount += tech.addJunkTechToPool(0.05) }, refundAmount: 0, remove() { tech.bonusEnergy = 0; m.setMaxEnergy() if (this.count > 0 && this.refundAmount > 0) { tech.removeJunkTechFromPool(this.refundAmount) this.refundAmount = 0 } } }, { name: "Maxwells demon", description: "energy above max decays 30x slower
+5% JUNKtech chance", maxCount: 1, count: 0, frequency: 2, frequencyDefault: 2, allowed() { return m.energy > m.maxEnergy || build.isExperimentSelection }, requires: "energy above your max", effect() { tech.overfillDrain = 0.99 //70% = 1-(1-0.75)/(1-0.15) //92% = 1-(1-0.75)/(1-0.87) this.refundAmount += tech.addJunkTechToPool(0.05) }, refundAmount: 0, remove() { tech.overfillDrain = 0.7 if (this.count > 0 && this.refundAmount > 0) { tech.removeJunkTechFromPool(this.refundAmount) this.refundAmount = 0 } } }, { name: "inductive charging", description: "if crouched 7x passive energy generation
otherwise 0x passive energy generation", maxCount: 1, count: 0, frequency: 1, frequencyDefault: 1, allowed() { return !tech.isDamageAfterKillNoRegen }, requires: "not parasitism", effect() { tech.isCrouchRegen = true; //only used to check for requirements m.regenEnergy = function () { if (m.immuneCycle < m.cycle && m.crouch && m.fieldCDcycle < m.cycle) m.energy += 7 * m.fieldRegen; if (m.energy < 0) m.energy = 0 } }, remove() { tech.isCrouchRegen = false; m.regenEnergy = m.regenEnergyDefault } }, { name: "energy conservation", description: "1.04x of damage done recovered as energy", maxCount: 9, count: 0, frequency: 1, frequencyDefault: 1, allowed() { return true }, requires: "", effect() { tech.energySiphon += 0.04; }, remove() { tech.energySiphon = 0; } }, { name: "parasitism", description: "if a mob has died in the last 5 seconds
2x damage, no passive energy generation", maxCount: 1, count: 0, frequency: 1, frequencyDefault: 1, allowed() { return !tech.isCrouchRegen }, requires: "not inductive charging", effect() { tech.isDamageAfterKillNoRegen = true; m.regenEnergy = function () { if (m.immuneCycle < m.cycle && (m.lastKillCycle + 300 < m.cycle) && m.fieldCDcycle < m.cycle) m.energy += m.fieldRegen; if (m.energy < 0) m.energy = 0 } }, remove() { if (this.count) m.regenEnergy = m.regenEnergyDefault tech.isDamageAfterKillNoRegen = false; } }, { name: "waste heat recovery", description: "if a mob has died in the last 5 seconds
generate 0.05x max energy per second", maxCount: 1, count: 0, frequency: 1, frequencyDefault: 1, allowed() { return true }, requires: "", effect() { tech.isEnergyRecovery = true; }, remove() { tech.isEnergyRecovery = false; } }, { name: "recycling", descriptionFunction() { return `if a mob has died in the last 5 seconds
recover 0.005x max ${tech.isEnergyHealth ? "energy" : "health"} per second` }, description: "", maxCount: 1, count: 0, frequency: 1, frequencyDefault: 1, isHealTech: true, allowed() { return true }, requires: "", effect() { tech.isHealthRecovery = true; }, remove() { tech.isHealthRecovery = false; } }, { name: "torpor", description: "if a mob has not died in the last 5 seconds
0.3x damage taken", maxCount: 1, count: 0, frequency: 1, frequencyDefault: 1, allowed() { return true }, requires: "", effect() { tech.isHarmReduceNoKill = true; }, remove() { tech.isHarmReduceNoKill = false; } }, { name: "homeostasis", descriptionFunction() { return `0.8x damage taken per health below 100
(${(1 - Math.max(0, 1 - m.health) * 0.8).toFixed(2)}x)` }, maxCount: 1, count: 0, frequency: 1, frequencyDefault: 1, allowed() { return m.health < 0.6 || build.isExperimentSelection }, requires: "health below 60", effect() { tech.isLowHealthDefense = true; }, remove() { tech.isLowHealthDefense = false; } }, { name: "negative feedback", descriptionFunction() { return `1.007x damage per ${tech.isEnergyHealth ? "energy" : "health"} below 100
(${(1 + 0.7 * Math.max(0, 1 - (tech.isEnergyHealth ? m.energy : m.health))).toFixed(2)}x)` }, 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: "Zenos paradox", descriptionFunction() { return `0.15x damage taken
–5% of current ${tech.isEnergyHealth ? "energy" : "health"} every 5 seconds` }, maxCount: 1, count: 0, frequency: 1, frequencyDefault: 1, allowed() { return true }, requires: "", effect() { tech.isZeno = true; }, remove() { tech.isZeno = false; } }, { name: "antiscience", descriptionFunction() { return `–10 ${tech.isEnergyHealth ? "energy" : "health"} after picking up a tech
1.7x damage` }, maxCount: 1, count: 0, frequency: 1, frequencyDefault: 1, allowed() { return true }, requires: "", damage: 1.7, effect() { tech.damage *= this.damage tech.isTechDamage = true; }, remove() { if (this.count && m.alive) tech.damage /= this.damage tech.isTechDamage = false; } }, { name: "ergodicity", descriptionFunction() { return `0.50x healing from ${powerUps.orb.heal()}
1.7x damage` }, maxCount: 1, count: 0, frequency: 1, frequencyDefault: 1, allowed() { return true }, requires: "", damage: 1.7, effect() { tech.damage *= this.damage tech.isHalfHeals = true; for (let i = 0; i < powerUp.length; i++) { if (powerUp[i].name === "heal") { const scale = Math.sqrt(0.5) powerUp[i].size *= scale Matter.Body.scale(powerUp[i], scale, scale); //grow } } }, remove() { if (this.count && m.alive) { tech.damage /= this.damage for (let i = 0; i < powerUp.length; i++) { if (powerUp[i].name === "heal") { const scale = 1 / Math.sqrt(0.5) powerUp[i].size *= scale Matter.Body.scale(powerUp[i], scale, scale); //grow } } } tech.isHalfHeals = false; } }, { name: "fluoroantimonic acid", description: "if your health is above 100
1.35x damage", 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: "induction brake", descriptionFunction() { return `after using ${powerUps.orb.heal()} slow nearby mobs for 17 seconds` }, maxCount: 1, count: 0, frequency: 1, frequencyDefault: 1, allowed() { return !tech.isPerfectBrake }, requires: "not eddy current brake", effect() { tech.isHealBrake = true; }, remove() { tech.isHealBrake = false; } }, { name: "adiabatic healing", descriptionFunction() { return `2x healing from ${powerUps.orb.heal()}
+4% JUNKtech chance` }, maxCount: 3, count: 0, frequency: 1, frequencyDefault: 1, isHealTech: true, allowed() { return (m.health / m.maxHealth) < 0.7 || build.isExperimentSelection }, requires: "under 70% health", effect() { tech.largerHeals++; for (let i = 0; i < powerUp.length; i++) { if (powerUp[i].name === "heal") { const oldSize = powerUp[i].size powerUp[i].size = powerUps.heal.size() //update current heals const scale = powerUp[i].size / oldSize Matter.Body.scale(powerUp[i], scale, scale); //grow } } this.refundAmount += tech.addJunkTechToPool(0.04) }, refundAmount: 0, remove() { tech.largerHeals = 1; for (let i = 0; i < powerUp.length; i++) { if (powerUp[i].name === "heal") { const oldSize = powerUp[i].size powerUp[i].size = powerUps.heal.size() //update current heals const scale = powerUp[i].size / oldSize Matter.Body.scale(powerUp[i], scale, scale); //grow } } if (this.count > 0 && this.refundAmount > 0) { tech.removeJunkTechFromPool(this.refundAmount) this.refundAmount = 0 } } }, { name: "quenching", descriptionFunction() { return `${powerUps.orb.heal()} overhealing results in 2x health loss
and 2x max health increase` }, maxCount: 1, count: 0, frequency: 1, frequencyDefault: 1, isHealTech: true, allowed() { return !tech.isEnergyHealth }, requires: "not mass-energy", effect() { tech.isOverHeal = true; }, remove() { tech.isOverHeal = false; } }, { name: "accretion", descriptionFunction() { return `${powerUps.orb.heal(1)} follow you, even between levels
+4% chance to duplicate spawned power ups` }, maxCount: 1, count: 0, frequency: 1, frequencyDefault: 1, isHealTech: true, allowed() { return m.fieldMode !== 9 }, requires: "not wormhole", effect() { tech.isHealAttract = true powerUps.setPowerUpMode(); if (!build.isExperimentSelection && !simulation.isTextLogOpen) simulation.circleFlare(0.04); }, remove() { tech.isHealAttract = false powerUps.setPowerUpMode(); }, }, { name: "accretion disk", descriptionFunction() { return `1.05x damage (${(1 + 0.05 * powerUp.length).toFixed(2)}x)
for each power up that exists on this level` }, maxCount: 1, count: 0, frequency: 3, frequencyDefault: 3, isHealTech: true, allowed() { return tech.isHealAttract }, requires: "accretion", effect() { tech.isPowerUpDamage = true }, remove() { tech.isPowerUpDamage = false }, }, { name: "maintenance", descriptionFunction() { return `2x healtech frequency
spawn ${powerUps.orb.heal(13)}` }, maxCount: 1, count: 0, frequency: 1, frequencyDefault: 1, isInstant: true, isBadRandomOption: true, allowed() { return true }, requires: "", 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: "self-assembly", descriptionFunction() { return `at the start of each level
for every 20 missing ${tech.isEnergyHealth ? "energy" : "health"} spawn ${powerUps.orb.heal()}` }, maxCount: 1, count: 0, frequency: 1, frequencyDefault: 1, isHealTech: true, allowed() { return true }, requires: "", effect() { tech.isHealLowHealth = true; }, remove() { tech.isHealLowHealth = false; } }, { name: "interest", descriptionFunction() { return `at the start of each level spawn
${(100 * this.rate).toFixed(0)}% of your ${powerUps.orb.research(1)}, ${powerUps.orb.ammo(1)}, ${powerUps.orb.coupling(1)}, and ${powerUps.orb.heal(1)} (rounded up)` }, maxCount: 9, count: 0, frequency: 1, frequencyDefault: 1, allowed() { return true }, requires: "", rate: 0.06, effect() { tech.interestRate += this.rate; }, remove() { tech.interestRate = 0; } }, { name: "anthropic principle", // nameInfo: "", // addNameInfo() { // setTimeout(function () { // powerUps.research.changeRerolls(0) // }, 1000); // }, descriptionFunction() { return `once per level, instead of dying
use ${powerUps.orb.research(1)} and spawn ${powerUps.orb.heal(16)}` }, 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
+60% 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.setPowerUpMode(); //needed after adjusting duplication chance }, remove() { tech.isAnthropicTech = false powerUps.setPowerUpMode(); //needed after adjusting duplication chance } }, { name: "strong anthropic principle", description: "after anthropic principle prevents your death
2.71828x damage 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: "0.7x damage taken
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: "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 spawn a tech power up
and enter an alternate reality`, maxCount: 1, count: 0, frequency: 1, frequencyDefault: 1, isAltRealityTech: true, allowed() { return !tech.isResearchReality && !tech.isCollisionRealitySwitch }, requires: "not Ψ(t) collapse, Hilbert space", effect() { tech.isSwitchReality = true; }, remove() { tech.isSwitchReality = false; } }, { name: "Ψ(t) collapse", link: `Ψ(t) collapse`, description: `after a boss dies spawn ${powerUps.orb.research(5)}
after you research enter an alternate reality`, maxCount: 1, count: 0, frequency: 1, frequencyDefault: 1, isAltRealityTech: true, allowed() { return !tech.isSwitchReality && !tech.isCollisionRealitySwitch && !tech.isJunkResearch }, requires: "not many-worlds, Hilbert space, pseudoscience", bonusResearch: 21, 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: `after a boss dies spawn ${powerUps.orb.research(2)}
tech options you don't choose won't reoccur`, maxCount: 1, count: 0, frequency: 1, frequencyDefault: 1, allowed() { return !tech.isSuperDeterminism }, requires: "not superdeterminism", bonusResearch: 7, effect() { tech.isBanish = true }, 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 } } tech.isBanish = false } }, { name: "peer review", description: `after you research gain 1.05x damage
and +1% JUNKtech chance`, maxCount: 1, count: 0, frequency: 1, frequencyDefault: 1, allowed() { return (powerUps.research.count > 1 || build.isExperimentSelection) && !tech.isSuperDeterminism }, requires: "at least 2 research, not superdeterminism", effect() { tech.isResearchDamage = true; }, remove() { tech.isResearchDamage = false; } }, { name: "pseudoscience", description: "research 2 times for free, but
add 1% JUNKtech chance each time", maxCount: 1, count: 0, frequency: 1, frequencyDefault: 1, allowed() { return !tech.isResearchReality && !tech.isSuperDeterminism }, requires: "not Ψ(t) collapse, superdeterminism", effect() { tech.isJunkResearch = true; }, remove() { tech.isJunkResearch = false; } }, { name: "renormalization", description: `+5% JUNKtech chance
47% chance to spawn ${powerUps.orb.research(1)} after consuming ${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; this.refundAmount += tech.addJunkTechToPool(0.05) }, refundAmount: 0, remove() { tech.renormalization = false; if (this.count > 0 && this.refundAmount > 0) { tech.removeJunkTechFromPool(this.refundAmount) this.refundAmount = 0 } } }, { name: "perturbation theory", description: `if you have no ${powerUps.orb.research(1)} in your inventory
2x fire rate`, maxCount: 1, count: 0, frequency: 1, frequencyDefault: 1, allowed() { return powerUps.research.count === 0 }, requires: "no research", effect() { tech.isRerollHaste = true; powerUps.research.changeRerolls(0) b.setFireCD(); }, remove() { tech.isRerollHaste = false; tech.researchHaste = 1; b.setFireCD(); } }, { name: "Bayesian statistics", descriptionFunction() { return `1.05x damage per ${powerUps.orb.research(1)} in your inventory
(${(1 + Math.max(0, 0.05 * powerUps.research.count)).toFixed(2)}x)` }, maxCount: 1, count: 0, frequency: 2, frequencyDefault: 2, allowed() { return powerUps.research.count > 1 || build.isExperimentSelection }, requires: "at least 2 research", effect() { tech.isRerollDamage = true; }, remove() { tech.isRerollDamage = false; } }, { name: "ansatz", description: `after choosing a field, tech, or gun
if you have no ${powerUps.orb.research(1)} in your inventory spawn ${powerUps.orb.research(3)}`, maxCount: 1, count: 0, frequency: 1, frequencyDefault: 1, allowed() { return powerUps.research.count < 1 && !tech.isSuperDeterminism && !tech.isRerollHaste }, requires: "no research, not superdeterminism, Ψ(t) collapse, perturbation theory", effect() { tech.isAnsatz = true; }, remove() { tech.isAnsatz = false; } }, { name: "unified field theory", description: `when paused you can click to switch fields
2x fieldtech frequency`, 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 *= 2 } }, 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 /= 2 } } } }, { name: "eternalism", description: "1.25x damage
time can't be paused (time can be dilated)", maxCount: 1, count: 0, frequency: 1, frequencyDefault: 1, allowed() { return !tech.isPauseSwitchField && !tech.isPauseEjectTech && !tech.isWormHolePause }, requires: "not unified field theory, paradigm shift, invariant", damage: 1.25, effect() { tech.damage *= this.damage tech.isNoDraftPause = true }, remove() { if (this.count && m.alive) tech.damage /= this.damage tech.isNoDraftPause = false } }, { name: "brainstorming", description: "tech choices randomize
every 1.5 seconds for 10 seconds", maxCount: 1, count: 0, frequency: 1, frequencyDefault: 1, allowed() { return !tech.isSuperDeterminism }, requires: "not superdeterminism", effect() { tech.isBrainstorm = true tech.isBrainstormActive = false tech.brainStormDelay = 2000 - simulation.difficultyMode * 100 }, remove() { tech.isBrainstorm = false tech.isBrainstormActive = false } }, { name: "cross-disciplinary", description: "tech have an extra field or gun choice
+5% chance to duplicate spawned power ups", maxCount: 1, count: 0, frequency: 1, frequencyDefault: 1, allowed() { return !tech.isDeterminism }, requires: "not determinism", effect() { tech.isExtraGunField = true; powerUps.setPowerUpMode(); //needed after adjusting duplication chance if (!build.isExperimentSelection && !simulation.isTextLogOpen) simulation.circleFlare(0.05); }, remove() { tech.isExtraGunField = false; if (this.count) powerUps.setPowerUpMode(); //needed after adjusting duplication chance } }, { name: "emergence", description: "tech, fields, and guns have +1 choice
1.1x damage", maxCount: 9, count: 0, frequency: 1, frequencyDefault: 1, allowed() { return !tech.isDeterminism }, requires: "not determinism", damage: 1.1, effect() { tech.extraChoices += 1; tech.damage *= this.damage }, refundAmount: 0, remove() { tech.extraChoices = 0; if (this.count && m.alive) tech.damage /= this.damage } }, { name: "path integral", link: `path integral`, description: "your next tech choice has all possible options
+5% JUNKtech chance", maxCount: 1, count: 0, frequency: 1, frequencyDefault: 1, isInstant: true, // isJunk: true, allowed() { return !tech.isDeterminism && !tech.isBrainstorm }, requires: "not determinism, brainstorm", 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); this.refundAmount += tech.addJunkTechToPool(0.05) }, refundAmount: 0, remove() { tech.tooManyTechChoices = 0 if (this.count > 0 && this.refundAmount > 0) { tech.removeJunkTechFromPool(this.refundAmount) this.refundAmount = 0 } } }, { name: "determinism", description: "spawn 5 tech
only 1 choice for tech, fields, and guns", maxCount: 1, count: 0, frequency: 1, frequencyDefault: 1, isBadRandomOption: true, isInstant: true, allowed() { return !tech.extraChoices && !tech.isExtraGunField && !tech.isExtraBotOption }, requires: "not emergence, cross-disciplinary, integrated circuit", 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; } }, { name: "superdeterminism", description: `spawn 5 tech
you can't cancel and ${powerUps.orb.research(1)} no longer spawn`, maxCount: 1, count: 0, frequency: 3, frequencyDefault: 3, isBadRandomOption: true, isInstant: true, allowed() { return tech.isDeterminism && !tech.isAnsatz && !tech.isJunkResearch && !tech.isBrainstorm }, requires: "determinism, not ansatz, pseudoscience, brainstorming", 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; } }, { name: "technical debt", descriptionFunction() { return `4x damage but lose 0.15x damage
for each tech you have learned (${(tech.totalCount > 20 ? (Math.pow(0.85, tech.totalCount - 20)) : (4 - 0.15 * tech.totalCount)).toFixed(2)}x)` }, maxCount: 1, count: 0, frequency: 1, frequencyDefault: 1, isBadRandomOption: true, allowed() { return true }, requires: "", effect() { tech.isTechDebt = true; }, remove() { tech.isTechDebt = false; } }, { name: "meta-analysis", description: `if you choose a JUNKtech you instead get a
random normal tech and spawn ${powerUps.orb.research(2)}`, maxCount: 1, count: 0, frequency: 1, frequencyDefault: 1, allowed() { return tech.junkChance > 0 }, requires: "some JUNK tech", effect() { tech.isMetaAnalysis = true }, remove() { tech.isMetaAnalysis = false } }, { name: "dark patterns", description: "1.3x damage
+17% JUNKtech chance", maxCount: 9, count: 0, frequency: 1, frequencyDefault: 1, allowed() { return true }, requires: "", damage: 1.3, effect() { tech.damage *= this.damage this.refundAmount += tech.addJunkTechToPool(0.17) }, refundAmount: 0, remove() { if (this.count && m.alive) { tech.damage /= this.damage if (this.refundAmount > 0) tech.removeJunkTechFromPool(this.refundAmount) } } }, { name: "junk DNA", descriptionFunction() { return `increase damage by twice the
JUNKtech chance (${(1 + 2 * tech.junkChance).toFixed(2)}x)` }, maxCount: 1, count: 0, frequency: 1, frequencyDefault: 1, allowed() { return tech.junkChance > 0 }, requires: "JUNK in tech pool", effect() { tech.isJunkDNA = true }, remove() { tech.isJunkDNA = false } }, { name: "exciton", descriptionFunction() { return `after mobs die they have a 14% chance to
spawn ${powerUps.orb.boost(1)} that give ${(1 + powerUps.boost.damage).toFixed(2)}x damage for ${(powerUps.boost.duration / 60).toFixed(0)} seconds
` }, maxCount: 1, count: 0, frequency: 1, frequencyDefault: 1, allowed: () => true, requires: "", effect() { tech.isBoostPowerUps = true }, remove() { tech.isBoostPowerUps = false } }, { name: "band gap", descriptionFunction() { return `${powerUps.orb.boost(1)} give 1.77x damage
but their duration is reduced by 1 second` }, maxCount: 9, count: 1, frequency: 2, frequencyDefault: 2, allowed() { return tech.isBoostPowerUps || tech.isBoostReplaceAmmo }, requires: "exciton, quasiparticles", effect() { powerUps.boost.duration -= 60 powerUps.boost.damage += 0.77 }, remove() { powerUps.boost.duration = 600 powerUps.boost.damage = 1.25 } }, { name: "mass production", descriptionFunction() { return `tech have extra choices to spawn ${powerUps.orb.ammo(1)},  ${powerUps.orb.heal(1)},  or  ${powerUps.orb.research(1)}
+3% chance to duplicate spawned power ups` }, maxCount: 1, count: 0, frequency: 1, frequencyDefault: 1, allowed() { return true }, requires: "", effect() { tech.isMassProduction = true powerUps.setPowerUpMode(); //needed after adjusting duplication chance if (!build.isExperimentSelection && !simulation.isTextLogOpen) simulation.circleFlare(0.03); }, remove() { tech.isMassProduction = false if (this.count) powerUps.setPowerUpMode(); //needed after adjusting duplication chance } }, { name: "research", descriptionFunction() { return `spawn ${this.value > 36 ? this.value + powerUps.orb.research(1) : powerUps.orb.research(this.value)}
next time increase amount spawned by +5${powerUps.orb.research(1)}` }, maxCount: 9, count: 0, frequency: 1, frequencyDefault: 1, isInstant: true, isMassProduction: true, allowed() { return true }, requires: "", value: 8, defaultValue: 8, effect() { powerUps.spawnDelay("research", this.value); this.value += 5 }, remove() { } }, { name: "ammo", descriptionFunction() { return `spawn ${this.value > 33 ? this.value + powerUps.orb.ammo(1) : powerUps.orb.ammo(this.value)}
next time increase amount spawned by +7${powerUps.orb.ammo(1)}` }, maxCount: 9, count: 0, frequency: 1, frequencyDefault: 1, isInstant: true, isMassProduction: true, allowed() { return true }, requires: "", value: 10, defaultValue: 10, effect() { powerUps.spawnDelay("ammo", this.value); this.value += 7 }, remove() { } }, { name: "heals", descriptionFunction() { return `spawn ${this.value > 30 ? this.value + powerUps.orb.heal(1) : powerUps.orb.heal(this.value)}
next time increase amount spawned by +7${powerUps.orb.heal(1)}` }, maxCount: 9, count: 0, frequency: 1, frequencyDefault: 1, isInstant: true, isMassProduction: true, allowed() { return true }, requires: "", value: 10, defaultValue: 10, effect() { powerUps.spawnDelay("heal", this.value); this.value += 7 }, remove() { } }, { name: "field coupling", descriptionFunction() { return `spawn ${powerUps.orb.coupling(10)}
${m.couplingDescription(1)} per ${powerUps.orb.coupling(1)}` }, maxCount: 9, count: 0, frequency: 1, frequencyDefault: 1, isInstant: true, allowed() { return true }, requires: "", effect() { powerUps.spawnDelay("coupling", 10) }, remove() { // if (this.count) m.couplingChange(-this.count * 10) } }, { name: "quintessence", descriptionFunction() { if (this.count) { converted = this.researchUsed * this.couplingToResearch let orbText if (converted > 15) { orbText = `${converted} ${powerUps.orb.coupling()}` } else { orbText = powerUps.orb.coupling(converted) } return `convert ${this.researchUsed} ${powerUps.orb.research(1)} into ${orbText}
${m.couplingDescription(1)} per ${powerUps.orb.coupling(1)}` } else { let converted = powerUps.research.count * this.couplingToResearch let orbText if (converted > 15) { orbText = `${converted} ${powerUps.orb.coupling()}` } else { orbText = powerUps.orb.coupling(converted) } return `convert ${powerUps.research.count} ${powerUps.orb.research(1)} into ${orbText}
${m.couplingDescription(1)} per ${powerUps.orb.coupling(1)}` } }, maxCount: 1, count: 0, frequency: 1, frequencyDefault: 1, isInstant: true, allowed() { return powerUps.research.count > 3 }, requires: "", researchUsed: 0, couplingToResearch: 3, effect() { // let count = 0 // while (powerUps.research.count > 0 && powerUps.research.count !== Infinity) { // powerUps.research.changeRerolls(-1) // count += 2.5 // this.researchUsed++ // } // powerUps.spawnDelay("coupling", Math.floor(count)) let cycle = () => { if (powerUps.research.count > 0 && powerUps.research.count !== Infinity) { if (m.alive) requestAnimationFrame(cycle); if (!simulation.paused && !simulation.isChoosing) { //&& !(simulation.cycle % 2) powerUps.research.changeRerolls(-1) this.researchUsed++ powerUps.spawnDelay("coupling", this.couplingToResearch) } } // else exit delay loop } requestAnimationFrame(cycle); }, remove() { if (this.count) { m.couplingChange(-this.researchUsed * this.couplingToResearch) powerUps.research.changeRerolls(this.researchUsed) this.researchUsed = 0 } } }, { name: "virtual particles", descriptionFunction() { return `17% chance after mobs die to spawn ${powerUps.orb.coupling(1)}
${m.couplingDescription(1)} per ${powerUps.orb.coupling(1)}` // return `17% chance after mobs die to spawn ${powerUps.orb.coupling(1)} that each give +0.1 coupling` //
${m.couplingDescription(1)} ${m.fieldMode === 0 ? "" : "per coupling"} }, maxCount: 1, count: 0, frequency: 1, frequencyDefault: 1, allowed: () => true, requires: "", effect() { tech.isCouplingPowerUps = true //about 20-30 mobs per level so at 16% and 0.1 coupling that's about 25 * 0.16 * 0.1 = 0.4 coupling per level with out duplication }, remove() { tech.isCouplingPowerUps = false } }, { name: "fine-structure constant", descriptionFunction() { // return `spawn ${this.value} ${powerUps.orb.coupling(1)} that each give +0.1 coupling
-0.5 coupling after mob collisions`//
${m.couplingDescription(1)} ${m.fieldMode === 0 ? "" : "per coupling"} return `after a boss dies spawn ${powerUps.orb.coupling(9)}
lose ${powerUps.orb.coupling(4)} after mob collisions`//
${m.couplingDescription(1)} per ${powerUps.orb.coupling(1)} }, maxCount: 1, count: 0, frequency: 1, frequencyDefault: 1, // isInstant: true, allowed: () => true, requires: "", value: 60, effect() { tech.isCouplingNoHit = true // powerUps.spawnDelay("coupling", this.value) }, remove() { // if (this.count) m.couplingChange(-this.value) tech.isCouplingNoHit = false } }, { name: "residual dipolar coupling", descriptionFunction() { // return `clicking cancel for a field, tech, or gun
spawns ${powerUps.orb.coupling(5)}that each give +0.1 coupling`//
${m.couplingDescription(1)} ${m.fieldMode === 0 ? "" : "per coupling"} return `clicking cancel spawns ${powerUps.orb.coupling(8)}
${m.couplingDescription(1)} per ${powerUps.orb.coupling(1)}` }, maxCount: 1, count: 0, frequency: 1, frequencyDefault: 1, allowed() { return !tech.isSuperDeterminism }, requires: "not superdeterminism", effect() { tech.isCancelCouple = true }, remove() { tech.isCancelCouple = false } }, { name: "commodities exchange", descriptionFunction() { return `clicking cancel for a field, tech, or gun
spawns 8-12 ${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: "options exchange", link: `options exchange`, // description: `once per level clicking cancel will randomize
with 2x choices for fields, tech, or guns
`, description: `clicking cancel for fields, tech, or guns
will randomize with 3x choices, once a level`, maxCount: 1, count: 0, frequency: 1, frequencyDefault: 1, allowed() { return !tech.isSuperDeterminism //&& (tech.isCancelRerolls || tech.isCancelDuplication || tech.isCancelCouple) }, requires: "not superdeterminism", //residual dipolar coupling, commodities exchange, futures exchange, effect() { tech.isCancelTech = true tech.cancelTechCount = 0 }, remove() { tech.isCancelTech = false tech.cancelTechCount = 0 } }, { name: "futures exchange", description: "clicking cancel for a field, tech, or gun
gives +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.duplication to balance powerUps.setPowerUpMode(); //needed after adjusting duplication chance }, remove() { tech.isCancelDuplication = false if (this.count) powerUps.setPowerUpMode(); //needed after adjusting duplication chance } }, { name: "replication", description: "+10% chance to duplicate spawned power ups
+22% JUNKtech chance", maxCount: 9, count: 0, frequency: 1, frequencyDefault: 1, allowed() { return tech.duplicationChance() < 1. }, requires: "below 100% duplication chance", effect() { tech.duplicateChance += 0.1 powerUps.setPowerUpMode(); //needed after adjusting duplication chance if (!build.isExperimentSelection && !simulation.isTextLogOpen) simulation.circleFlare(0.1); this.refundAmount += tech.addJunkTechToPool(0.22) }, refundAmount: 0, remove() { tech.duplicateChance = 0 powerUps.setPowerUpMode(); //needed after adjusting duplication chance if (this.count > 0 && this.refundAmount > 0) { tech.removeJunkTechFromPool(this.refundAmount) this.refundAmount = 0 } } }, { name: "metastability", description: "+13% chance to duplicate spawned power ups
duplicates explode with a 4 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.setPowerUpMode(); //needed after adjusting duplication chance if (!build.isExperimentSelection && !simulation.isTextLogOpen) simulation.circleFlare(0.11); }, remove() { tech.isPowerUpsVanish = false if (this.count) powerUps.setPowerUpMode(); //needed after adjusting duplication chance } } }, { name: "correlated damage", descriptionFunction() { return `duplication increases damage
(${(1 + Math.min(1, tech.duplicationChance())).toFixed(2)}x)` }, maxCount: 1, count: 0, frequency: 1, frequencyDefault: 1, allowed() { return tech.duplicationChance() > 0.03 }, requires: "duplication chance > 3%", effect() { tech.isDupDamage = true; }, remove() { tech.isDupDamage = false; } }, { name: "parthenogenesis", description: "+8% chance to duplicate spawned power ups
duplication also duplicates mobs", maxCount: 1, count: 0, frequency: 1, frequencyDefault: 1, allowed() { return tech.duplicationChance() > 0// && !tech.isResearchBoss }, requires: "some duplication chance", effect() { tech.isDuplicateMobs = true; powerUps.setPowerUpMode(); //needed after adjusting duplication chance if (!build.isExperimentSelection && !simulation.isTextLogOpen) simulation.circleFlare(0.08); }, remove() { tech.isDuplicateMobs = false; if (this.count) powerUps.setPowerUpMode(); //needed after adjusting duplication chance } }, { name: "stimulated emission", description: "+20% chance to duplicate spawned power ups,
collisions eject a random tech", maxCount: 1, count: 0, frequency: 1, frequencyDefault: 1, allowed() { return tech.duplicationChance() < 1 }, requires: "below 1% duplication chance", effect() { tech.isStimulatedEmission = true powerUps.setPowerUpMode(); //needed after adjusting duplication chance if (!build.isExperimentSelection && !simulation.isTextLogOpen) simulation.circleFlare(0.20); }, remove() { tech.isStimulatedEmission = false if (this.count) powerUps.setPowerUpMode(); //needed after adjusting duplication chance } }, { name: "strange attractor", descriptionFunction() { return `1.1x damage
removing this increases duplication by +11%` }, maxCount: 1, count: 0, frequency: 1, frequencyDefault: 1, isBadRandomOption: true, allowed() { return true }, requires: "", damage: 1.1, effect() { tech.damage *= this.damage }, isRemoveBenefit: true, remove() { if (this.count > 0 && m.alive) { tech.duplication += 0.11 powerUps.setPowerUpMode(); //needed after adjusting duplication chance if (!build.isExperimentSelection && !simulation.isTextLogOpen) simulation.circleFlare(0.11); tech.damage /= this.damage this.frequency = 0 } } }, { name: "strange loop", description: `1.1x damage
removing this gives a random removetech`, maxCount: 1, count: 0, frequency: 1, frequencyDefault: 1, isBadRandomOption: true, allowed() { return true }, requires: "", damage: 1.1, effect() { tech.damage *= this.damage }, isRemoveBenefit: true, remove() { if (this.count > 0 && m.alive) { tech.damage /= this.damage this.frequency = 0 requestAnimationFrame(() => { const options = [] for (let i = 0, len = tech.tech.length; i < len; i++) if (tech.tech[i].isRemoveBenefit && tech.tech[i].count === 0) options.push(i) const index = options[Math.floor(Math.random() * options.length)] tech.giveTech(tech.tech[index].name) }); } } }, { name: "null hypothesis", description: `1.1x damage
removing this spawns ${powerUps.orb.research(15)}`, maxCount: 1, count: 0, frequency: 1, frequencyDefault: 1, isBadRandomOption: true, allowed() { return true }, requires: "", damage: 1.1, effect() { tech.damage *= this.damage }, isRemoveBenefit: true, remove() { if (this.count > 0 && m.alive) { tech.damage /= this.damage requestAnimationFrame(() => { powerUps.spawnDelay("research", 15) }); this.frequency = 0 } } }, { name: "martingale", descriptionFunction() { return `${(1 + this.damage).toFixed(1)}x damage. removing this has a 50%
chance return with 2x its damage (${(1 + this.damage).toFixed(1)}x→${(1 + 2 * this.damage).toFixed(1)}x)
` }, maxCount: 1, count: 0, frequency: 1, frequencyDefault: 1, isBadRandomOption: true, allowed() { return true }, requires: "", damage: 0.1, effect() { tech.damage *= 1 + this.damage }, isRemoveBenefit: true, remove() { if (this.count > 0 && m.alive) { tech.damage /= 1 + this.damage if (Math.random() < 0.5) { this.damage *= 2 requestAnimationFrame(() => { tech.giveTech("martingale") }); } this.frequency = 0 } } }, { name: "externality", descriptionFunction() { return `1.1x damage
removing this spawns ${this.ammo} ${powerUps.orb.ammo(1)}` }, maxCount: 1, count: 0, frequency: 1, frequencyDefault: 1, isBadRandomOption: true, allowed() { return true }, requires: "", damage: 1.1, ammo: 50, effect() { tech.damage *= this.damage }, isRemoveBenefit: true, remove() { if (this.count > 0 && m.alive) { tech.damage /= this.damage this.frequency = 0 requestAnimationFrame(() => { powerUps.spawnDelay("ammo", this.ammo) }); } } }, { name: "deprecated", scale: 0.07, descriptionFunction() { return `after removing this gain
${1 + this.scale}x damage per removed tech(${(1 + this.scale * ((this.frequency === 0 ? 0 : 1) + tech.removeCount)).toFixed(2)}x)` }, maxCount: 1, count: 0, frequency: 1, frequencyDefault: 1, isBadRandomOption: true, allowed() { return true }, requires: "", damage: 1.1, effect() { }, isRemoveBenefit: true, remove() { if (this.count > 0 && m.alive) { tech.damage *= 1 + this.scale * (1 + tech.removeCount) this.frequency = 0 } } }, { name: "paradigm shift", descriptionFunction() { return `when paused clicking a tech ejects it
–${tech.pauseEjectTech.toFixed(1)} ${tech.isEnergyHealth ? "energy" : "health"} cost (1.3x cost each use)` }, 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: "Born rule", description: "eject all your tech", maxCount: 1, count: 0, frequency: 1, frequencyDefault: 1, isInstant: true, isBadRandomOption: true, allowed() { return (tech.totalCount > 6) }, requires: "more than 6 tech", effect() { // //remove active bullets //to get rid of bots // for (let i = 0; i < bullet.length; ++i) Matter.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].isInstant) count += tech.tech[i].count // } // if (tech.isDeterminism) count -= 4 //remove the bonus tech // if (tech.isSuperDeterminism) count -= 4 //remove the bonus tech // const removeCount = tech.removeCount // tech.setupAllTech(); // remove all tech // tech.removeCount = removeCount // 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() let count = 0 //count tech for (let i = 0, len = tech.tech.length; i < len; i++) { // spawn new tech power ups if (!tech.tech[i].isInstant && tech.tech[i].count) { count += tech.tech[i].count tech.removeTech(i) // powerUps.ejectTech(index) } } powerUps.spawnDelay("tech", count); // 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 }, remove() { } }, { name: "Occams razor", descriptionFunction() { return `randomly remove half your tech
for each removed ${(1 + this.damagePerRemoved).toFixed(2)}x damage (~${((this.count === 0) ? 1 + this.damagePerRemoved * 0.5 * tech.totalCount : this.damage).toFixed(2)}x)` }, maxCount: 1, count: 0, frequency: 1, frequencyDefault: 1, isInstant: true, isBadRandomOption: true, allowed() { return (tech.totalCount > 6) }, requires: "more than 6 tech", damagePerRemoved: 0.5, damage: null, 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].isInstant) pool.push(i) } pool = shuffle(pool); //shuffles order of maps let removeCount = 0 for (let i = 0, len = pool.length * 0.5; i < len; i++) removeCount += tech.removeTech(pool[i]) this.damage = this.damagePerRemoved * removeCount tech.damage *= (1 + this.damage) }, remove() { if (this.count && m.alive) tech.damage /= (1 + this.damage) } }, { name: "exchange symmetry", description: "remove 1 random tech
spawn 2 new guns", maxCount: 1, count: 0, frequency: 1, frequencyDefault: 1, isInstant: true, isBadRandomOption: true, allowed() { return (tech.totalCount > 3) && !tech.isSuperDeterminism }, requires: "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].isInstant) have.push(i) } const choose = have[Math.floor(Math.random() * have.length)] 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"); tech.removeTech(choose) }, remove() { } }, { name: "Monte Carlo method", description: "remove 1 random tech
spawn 2 tech", maxCount: 1, count: 0, frequency: 1, frequencyDefault: 1, isInstant: true, isBadRandomOption: true, allowed() { return (tech.totalCount > 3) && tech.duplicationChance() > 0 && !tech.isSuperDeterminism }, requires: "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() { } }, //************************************************** //************************************************** gun //************************************************** tech //************************************************** { name: "needle ice", description: `after 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
1.2x needle damage`, 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: "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 || tech.isHookDefense }, requires: "needle gun, harpoon, not Bessemer process", effect() { tech.isShieldPierce = true }, remove() { tech.isShieldPierce = 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.isRicochet) || (tech.haveGunCheck("shotgun") && !tech.isNailShot && !tech.isFoamShot && !tech.isSporeWorm && !tech.isSporeFlea)) && !tech.isRivets && !tech.isIncendiary && !tech.isIceCrystals && !tech.isIceShot }, requires: "nail gun, shotgun, not ice crystal, rivets, rotary cannon, pneumatic, incendiary, nail-shot, 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 / this.ammoScale); b.guns[i].ammoPack = b.guns[i].defaultAmmoPack / this.ammoScale; b.guns[i].chooseFireMethod() simulation.updateGunHUD(); break } } }, ammoScale: 3, 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 * this.ammoScale); b.guns[i].ammoPack = b.guns[i].ammo * this.ammoScale; simulation.updateGunHUD(); break } } } } }, { name: "stress concentration", description: "mobs below 50% durability die after you shoot
them near their center with needles or rivets", isGunTech: true, maxCount: 1, count: 0, frequency: 2, frequencyDefault: 2, allowed() { return (tech.isNeedles || tech.isRivets) && !tech.isNailCrit && !tech.isIncendiary }, requires: "needles, rivets, not incendiary, supercritical fission", effect() { tech.isCritKill = true }, remove() { tech.isCritKill = false } }, { 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.isRicochet) || (tech.haveGunCheck("shotgun") && !tech.isNailShot && !tech.isFoamShot && !tech.isSporeWorm && !tech.isSporeFlea)) && !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: "pneumatic actuator", description: "nail gun takes no time to ramp up
to its fastest fire rate", 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: "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; b.guns[0].ammoPack = Infinity b.guns[0].recordedAmmo = b.guns[i].ammo b.guns[0].ammo = Infinity simulation.updateGunHUD(); }, remove() { if (tech.isIceCrystals) { tech.isIceCrystals = false; b.guns[0].ammoPack = b.guns[0].defaultAmmoPack; if (b.guns[0].recordedAmmo) b.guns[0].ammo = b.guns[0].recordedAmmo simulation.updateGunHUD(); if (this.count) requestAnimationFrame(() => { simulation.updateGunHUD(); }); } tech.isIceCrystals = false; if (b.guns[0].ammo === Infinity) b.guns[0].ammo = 0 } }, { 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: "gauge", description: `rivets, needles, super balls, and nails
have 1.3x mass and physical damage`, isGunTech: true, maxCount: 9, count: 0, frequency: 2, frequencyDefault: 2, allowed() { return tech.hookNails + tech.isMineDrop + tech.isNailBotUpgrade + tech.fragments + tech.nailsDeathMob + (tech.haveGunCheck("super balls") + (tech.haveGunCheck("mine") && !tech.isFoamMine) + (tech.haveGunCheck("nail gun")) + tech.isNeedles + tech.isNailShot + tech.isRivets) * 2 > 1 }, requires: "nails, nail gun, rivets, shotgun, super balls, mine", effect() { tech.bulletSize = 1 + 0.25 * Math.pow(this.count + 1, 0.5) }, remove() { tech.bulletSize = 1; } }, { name: "supercritical fission", description: "if nails, needles, or rivets strike mobs
near their center they can explode", 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.isMineDrop || tech.haveGunCheck("mine")) && !(tech.isFoamMine || tech.isSuperMine))) && !tech.isIncendiary && !tech.isCritKill }, requires: "nail gun, mine, needles, nails, rivets, not incendiary, stress concentration", effect() { tech.isNailCrit = true }, remove() { tech.isNailCrit = false } }, { name: "irradiated nails", link: `irradiated nails`, description: "nails, needles, and rivets are radioactive
1.9x radioactive damage over 3 seconds", isGunTech: true, maxCount: 1, count: 0, frequency: 2, frequencyDefault: 2, allowed() { return tech.isNailBotUpgrade || tech.hookNails || tech.fragments || tech.nailsDeathMob || ((tech.isMineDrop || tech.haveGunCheck("mine")) && !(tech.isFoamMine || tech.isSuperMine)) || (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, needles, rivets are made of plutonium-238
radioactive damage lasts +3 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, needles, rivets are made of lithium-8
4x radioactive damage for 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: `after firing the shotgun you are invulnerable
shotgun has 0.7x bullets per ${powerUps.orb.ammo(1)}`, 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.7); b.guns[i].ammoPack *= 0.7 break; } } simulation.updateGunHUD(); }, remove() { tech.isShotgunImmune = false; if (this.count > 0) { for (i = 0, len = b.guns.length; i < len; i++) { //find which gun if (b.guns[i].name === "shotgun") { b.guns[i].ammoPack /= 0.7 b.guns[i].ammo = Math.ceil(b.guns[i].ammo / 0.7); simulation.updateGunHUD(); break; } } } } }, { name: "Newtons 3rd law", description: "1.7x shotgun fire rate and recoil", 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: "1.5x shotgun damage
shotgun recoil is reversed", isGunTech: true, maxCount: 1, count: 0, frequency: 1, frequencyDefault: 1, allowed() { return (tech.haveGunCheck("shotgun")) && !tech.isShotgunRecoil }, requires: "shotgun, not Newtons 3rd law", effect() { tech.isShotgunReversed = true; }, remove() { tech.isShotgunReversed = false; } }, { name: "repeater", description: "shotgun immediately fires again for no ammo
reduced 0.5x shotgun fire rate", isGunTech: true, maxCount: 9, count: 0, frequency: 2, frequencyDefault: 2, allowed() { return (tech.haveGunCheck("shotgun")) }, requires: "shotgun, not Newtons 3rd law", effect() { tech.shotgunExtraShots++; }, remove() { tech.shotgunExtraShots = 0 } }, { name: "nail-shot", link: `nail-shot`, description: "shotgun drives a long clip of 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.isSporeFlea && !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 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.isSporeFlea && !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 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.isSporeFlea && !tech.isNeedles }, requires: "shotgun, not incendiary, nail-shot, rivet, foam-shot, worm-shot", effect() { tech.isIceShot = true; }, remove() { tech.isIceShot = false; } }, { name: "freezer burn", description: "mobs frozen while below 33% durability die", isGunTech: true, maxCount: 1, count: 0, frequency: 2, frequencyDefault: 2, allowed() { return tech.isIceCrystals || tech.isSporeFreeze || (m.fieldMode === 4 && simulation.molecularMode === 2) || tech.isIceShot || tech.isNeedleIce || (m.coupling && (m.fieldMode === 3 || m.fieldMode === 0)) }, requires: "a freeze effect", effect() { tech.isIceKill = true }, remove() { tech.isIceKill = false } }, { name: "flash freeze", description: "mobs frozen while above 66% durability
have their durability reduced to 66%", isGunTech: true, maxCount: 1, count: 0, frequency: 2, frequencyDefault: 2, allowed() { return tech.isIceCrystals || tech.isSporeFreeze || (m.fieldMode === 4 && simulation.molecularMode === 2) || tech.isIceShot || tech.isNeedleIce || (m.coupling && (m.fieldMode === 2 || m.fieldMode === 0)) }, requires: "a freeze effect", effect() { tech.isIceMaxHealthLoss = true }, remove() { tech.isIceMaxHealthLoss = false } }, { name: "crystallizer", description: "after frozen mobs die they
shatter into ice IX crystals", isGunTech: true, maxCount: 9, count: 0, frequency: 2, frequencyDefault: 2, allowed() { return (tech.isIceCrystals || tech.isSporeFreeze || (m.fieldMode === 4 && simulation.molecularMode === 2) || tech.isIceShot || tech.isNeedleIce || (m.coupling && (m.fieldMode === 3 || m.fieldMode === 0))) && !tech.sporesOnDeath && !tech.isExplodeMob && !tech.botSpawner && !tech.isMobBlockFling && !tech.nailsDeathMob }, requires: "a localized freeze effect, no other mob death tech", effect() { tech.iceIXOnDeath++ }, remove() { tech.iceIXOnDeath = 0 } }, { name: "thermoelectric effect", description: "after killing mobs with ice IX
+100 energy", isGunTech: true, maxCount: 9, count: 0, frequency: 2, frequencyDefault: 2, allowed() { return (m.fieldMode === 4 && simulation.molecularMode === 2) || tech.isNeedleIce || (m.coupling && (m.fieldMode === 3 || m.fieldMode === 0)) || tech.iceIXOnDeath || tech.isIceShot }, requires: "ice IX", effect() { tech.iceEnergy++ }, remove() { tech.iceEnergy = 0; } }, { name: "superfluidity", description: "freeze effects are applied
to a small area around the target", isGunTech: true, maxCount: 1, count: 0, frequency: 2, frequencyDefault: 2, allowed() { return tech.isIceCrystals || tech.isSporeFreeze || (m.fieldMode === 4 && simulation.molecularMode === 2) || tech.isNeedleIce || (m.coupling && (m.fieldMode === 3 || m.fieldMode === 0)) || tech.iceIXOnDeath || tech.isIceShot }, requires: "a localized freeze effect", effect() { tech.isAoESlow = true }, remove() { tech.isAoESlow = false } }, { name: "triple point", descriptionFunction() { return `+5 second freeze duration` }, isGunTech: true, maxCount: 3, count: 0, frequency: 1, frequencyDefault: 1, // allowed() { // return (m.fieldMode === 2 && m.coupling > 0) || (tech.haveGunCheck("shotgun") && tech.isIceShot) // }, // requires: "perfect diamagnetism", allowed() { return (tech.isIceCrystals || tech.isSporeFreeze || (m.fieldMode === 4 && simulation.molecularMode === 2) || tech.isIceShot || tech.isNeedleIce || (m.coupling && (m.fieldMode === 3 || m.fieldMode === 0))) }, requires: "a localized freeze effect", effect() { tech.iceIXFreezeTime += 5 * 60 // powerUps.spawnDelay("coupling", 10) }, remove() { tech.iceIXFreezeTime = 150 // if (this.count) m.couplingChange(-10 * this.count) } }, { 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.isSporeFlea && !tech.isNeedles) || ((tech.haveGunCheck("super balls") || tech.isSuperMine) && !tech.isSuperBounce && !tech.isFoamBall && !tech.isSuperHarm) || (tech.isRivets && !tech.isNailCrit) || (m.fieldMode === 4 && simulation.molecularMode === 3) || (tech.haveGunCheck("drones") && !tech.isForeverDrones && !tech.isDroneRadioactive && !tech.isDroneTeleport) }, requires: "shotgun, super balls, rivets, drones, not irradiated drones, burst drones, polyurethane, Zectron", effect() { tech.isIncendiary = true }, remove() { tech.isIncendiary = false; } }, { name: "rebound", description: `after they collide with a mob, super balls
gain speed, duration, and 1.3x damage`, isGunTech: true, maxCount: 1, count: 0, frequency: 2, frequencyDefault: 2, allowed() { return (tech.haveGunCheck("super balls") || tech.isSuperMine) && !tech.isIncendiary && !tech.isFoamBall }, requires: "super balls, not incendiary", effect() { tech.isSuperBounce = true }, remove() { tech.isSuperBounce = false } }, { name: "Zectron", description: `2x super ball damage, but
after colliding with super balls -4 energy`, isGunTech: true, maxCount: 1, count: 0, frequency: 2, frequencyDefault: 2, allowed() { return (tech.haveGunCheck("super balls") || tech.isSuperMine) && !tech.isIncendiary && !tech.isBulletTeleport }, requires: "super balls not incendiary ammunition, uncertainty principle", effect() { tech.isSuperHarm = true }, remove() { tech.isSuperHarm = false } }, { name: "polyurethane foam", description: "super balls and harpoons 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") || tech.isSuperMine) && !tech.isSuperBounce) || (tech.haveGunCheck("harpoon") && !tech.fragments) || tech.isHookDefense }, requires: "super balls, harpoon, not fragmentation", effect() { tech.isFoamBall = true; }, remove() { tech.isFoamBall = false; } }, { name: "autocannon", description: "fire +2 super balls in a line
1.4x super ball velocity and gravity", 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, +2, or +3 extra super balls`, isGunTech: true, maxCount: 9, count: 0, frequency: 2, frequencyDefault: 2, allowed() { return (tech.haveGunCheck("super balls") || tech.isSuperMine) && !tech.oneSuperBall }, requires: "super balls, not super ball", effect() { tech.extraSuperBalls += 4 }, remove() { tech.extraSuperBalls = 0; } }, { name: "super ball", description: "fire just 1 large super ball
that stuns mobs for 2 second", isGunTech: true, maxCount: 1, count: 0, frequency: 2, frequencyDefault: 2, allowed() { return (tech.haveGunCheck("super balls") || tech.isSuperMine) && !tech.extraSuperBalls && !tech.superBallDelay }, requires: "super balls, not super duper or autocannon", 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: "phase velocity", description: "wave particles propagate faster as solids
1.4x wave damage", isGunTech: true, maxCount: 1, count: 0, frequency: 2, frequencyDefault: 2, allowed() { return tech.haveGunCheck("wave") && !tech.isLongitudinal }, requires: "wave, not phonon", effect() { tech.isPhaseVelocity = true; }, remove() { tech.isPhaseVelocity = false; } }, { name: "amplitude", description: "1.4x wave damage
1.4x wave bullet amplitude", isGunTech: true, maxCount: 3, count: 0, frequency: 2, frequencyDefault: 2, allowed() { return tech.haveGunCheck("wave") || tech.isSoundBotUpgrade }, requires: "wave", effect() { tech.waveFrequency *= 0.66 tech.wavePacketDamage *= 1.4 }, remove() { tech.waveFrequency = 0.2 //adjust this to make the waves much larger tech.wavePacketDamage = 1 } }, { name: "propagation", description: `0.75x wave propagation speed
1.4x wave damage`, isGunTech: true, maxCount: 9, count: 0, frequency: 2, frequencyDefault: 2, allowed() { return tech.haveGunCheck("wave") || tech.isSoundBotUpgrade }, requires: "wave", effect() { tech.waveBeamSpeed *= 0.75; tech.waveBeamDamage += 0.3 * 0.4 //this sets base wave damage }, remove() { tech.waveBeamSpeed = 11; tech.waveBeamDamage = 0.3 //this sets base wave damage } }, { name: "bound state", description: "wave packets reflect backwards 2 times
0.66x wave range", isGunTech: true, maxCount: 9, count: 0, frequency: 2, frequencyDefault: 2, allowed() { return tech.haveGunCheck("wave") && !tech.isLongitudinal }, requires: "wave, not phonon", effect() { tech.waveReflections += 2 }, remove() { tech.waveReflections = 1 } }, { name: "frequency", description: `wave has unlimited ammo
0.75x wave damage`, isGunTech: true, maxCount: 1, count: 0, frequency: 1, frequencyDefault: 1, allowed: () => tech.haveGunCheck("wave"), requires: "wave", effect() { tech.isInfiniteWaveAmmo = true b.guns[3].savedAmmo = b.guns[3].ammo b.guns[3].ammo = Infinity simulation.updateGunHUD(); }, remove() { tech.isInfiniteWaveAmmo = false if (this.count > 0 && b.guns[3].savedAmmo !== undefined) { b.guns[3].ammo = b.guns[3].savedAmmo simulation.updateGunHUD(); requestAnimationFrame(() => { simulation.updateGunHUD(); }); } else if (b.guns[3].ammo === Infinity) { b.guns[3].ammo = 0 } } }, { name: "phonon", description: "waves are low frequency, high damage
expanding arcs that propagate through solids", isGunTech: true, maxCount: 1, count: 0, frequency: 3, frequencyDefault: 3, allowed() { return tech.haveGunCheck("wave") && !tech.isPhaseVelocity && tech.waveReflections === 1 }, requires: "wave, not phase velocity, bound state", ammoScale: 6, effect() { tech.isLongitudinal = true; b.guns[3].chooseFireMethod() b.guns[3].ammoPack /= this.ammoScale if (tech.isInfiniteWaveAmmo) { b.guns[3].savedAmmo = Math.ceil(b.guns[3].savedAmmo / this.ammoScale); //used with low frequency } else { b.guns[3].ammo = Math.ceil(b.guns[3].ammo / this.ammoScale); } simulation.updateGunHUD(); }, remove() { tech.isLongitudinal = false; if (this.count > 0) { b.guns[3].chooseFireMethod() b.guns[3].ammoPack *= this.ammoScale if (tech.isInfiniteWaveAmmo) { b.guns[3].savedAmmo = Math.ceil(b.guns[3].savedAmmo * this.ammoScale); //used with low frequency } else { b.guns[3].ammo = Math.ceil(b.guns[3].ammo * this.ammoScale); } simulation.updateGunHUD(); } } }, { name: "isotropic", description: "waves expand in all directions
0.6x range and 1.5x damage", isGunTech: true, maxCount: 1, count: 0, frequency: 3, frequencyDefault: 3, allowed() { return tech.isLongitudinal && tech.haveGunCheck("wave") && !tech.isBulletTeleport }, requires: "wave, phonon, not uncertainty principle", effect() { tech.is360Longitudinal = true; b.guns[3].chooseFireMethod() }, remove() { tech.is360Longitudinal = false; b.guns[3].chooseFireMethod() } }, { name: "mechanical resonance", description: "after a block gets vibrated by a phonon
there is a chance it's flung at nearby mobs", isGunTech: true, maxCount: 1, count: 0, frequency: 2, frequencyDefault: 2, allowed() { return (tech.isLongitudinal && tech.haveGunCheck("wave")) || tech.isSoundBotUpgrade }, requires: "wave, phonon", effect() { tech.isPhononBlock = true }, remove() { tech.isPhononBlock = false } }, { name: "sympathetic resonance", description: "after a mob gets vibrated by a phonon
a new resonance wave expands", isGunTech: true, maxCount: 1, count: 0, frequency: 2, frequencyDefault: 2, allowed() { return (tech.isLongitudinal && tech.haveGunCheck("wave")) || tech.isSoundBotUpgrade }, requires: "wave, phonon", effect() { tech.isPhononWave = true }, remove() { tech.isPhononWave = false } }, { name: "missile-bot", link: `missile-bot`, description: `use ${powerUps.orb.research(1)}to trade your missile gun
for a bot that fires missiles`, // isGunTech: true, isRemoveGun: true, maxCount: 1, count: 0, frequency: 1, frequencyDefault: 1, isBot: true, isBotTech: true, isInstant: true, allowed() { return tech.haveGunCheck("missiles", false) && tech.missileFireCD === 45 && (build.isExperimentSelection || powerUps.research.count > 0) }, requires: "missiles, not launch system", effect() { tech.missileBotCount++; b.missileBot(); if (tech.haveGunCheck("missiles", false)) b.removeGun("missiles") //remove your last gun for (let i = 0; i < 1; i++) { if (powerUps.research.count > 0) powerUps.research.changeRerolls(-1) } }, remove() { // if (this.count) { // tech.missileBotCount = 0; // b.clearPermanentBots(); // b.respawnBots(); // if (!tech.haveGunCheck("missiles", false)) b.giveGuns("missiles") // powerUps.research.changeRerolls(1) // } } }, { name: "cruise missile", description: "2x missile explosive damage, radius
0.5x missile speed", isGunTech: true, maxCount: 1, count: 0, frequency: 2, frequencyDefault: 2, allowed() { return (tech.haveGunCheck("missiles") && tech.missileFireCD === 45) || (m.fieldMode === 4 && simulation.molecularMode === 1) || tech.missileBotCount }, requires: "missiles, not launch system", effect() { tech.isMissileBig = true }, remove() { tech.isMissileBig = false } }, { name: "ICBM", description: "1.75x missile explosive damage, radius
0.5x missile speed", isGunTech: true, maxCount: 1, count: 0, frequency: 2, frequencyDefault: 2, allowed() { return (tech.haveGunCheck("missiles") || (m.fieldMode === 4 && simulation.molecularMode === 1)) && tech.isMissileBig }, requires: "missiles, cruise missile", effect() { tech.isMissileBiggest = true }, remove() { tech.isMissileBiggest = false } }, { name: "launch system", description: `5x missile gun fire rate
1.2x 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, not cruise missile", 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() { tech.missileFireCD = 45; if (this.count > 0) { 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 } } } } }, { name: "iridium-192", description: "explosions release gamma radiation
2x explosion damage over 4 seconds", isGunTech: true, maxCount: 1, count: 0, frequency: 1, frequencyDefault: 1, allowed() { return !tech.isImmuneExplosion && tech.explosiveRadius === 1 && !tech.isSmallExplosion && !tech.isBlockExplode && !tech.fragments && (tech.haveGunCheck("missiles") || tech.missileBotCount || tech.isIncendiary || (tech.haveGunCheck("grenades") && !tech.isNeutronBomb) || tech.isPulseLaser || (m.fieldMode === 4 && simulation.molecularMode === 1) || tech.isBoomBotUpgrade || tech.isTokamak) }, requires: "an explosive damage source, not ammonium nitrate, nitroglycerin, chain reaction, fragmentation, electric armor", 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.isFoamBall) || (tech.haveGunCheck("grenades") && !tech.isNeutronBomb) || tech.haveGunCheck("missiles") || (m.fieldMode === 4 && simulation.molecularMode === 1) || tech.missileBotCount || tech.isRivets || tech.blockDamage > 0.075 || tech.isHookDefense) }, requires: "grenades, missiles, rivets, harpoon, or mass driver, not iridium-192, not polyurethane foam", effect() { tech.fragments++ }, remove() { tech.fragments = 0 } }, { name: "ammonium nitrate", description: "1.25x explosive damage, radius", 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.25; }, remove() { tech.explosiveRadius = 1; } }, { name: "nitroglycerin", description: "1.66x explosive damage
0.66x smaller explosive radius", 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: "1.7x explosive radius
1.4x explosive damage taken", 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 sporangium stun for 3-5 seconds
explosions stun for 0.5 seconds", isGunTech: true, maxCount: 1, count: 0, frequency: 1, frequencyDefault: 1, allowed() { return tech.haveGunCheck("spores") || tech.haveGunCheck("mine") || (!tech.isExplodeRadio && tech.hasExplosiveDamageCheck()) }, requires: "mine, spores, an explosive damage source, not iridium-192", effect() { tech.isStun = true; }, remove() { tech.isStun = false; } }, { name: "shaped charge", description: `use ${powerUps.orb.research(2)} to dynamically reduce
all explosions to prevent health loss`, isGunTech: true, maxCount: 1, count: 0, frequency: 2, frequencyDefault: 2, allowed() { return !tech.isImmuneExplosion && (build.isExperimentSelection || powerUps.research.count > 1) && (tech.haveGunCheck("missiles") || (m.fieldMode === 4 && simulation.molecularMode === 1) || tech.missileBotCount > 0 || tech.isIncendiary || tech.isPulseLaser || tech.isTokamak || (tech.haveGunCheck("grenades") && !tech.isNeutronBomb)) }, requires: "an explosive damage source, not rocket propelled grenade", effect() { tech.isSmartRadius = true; for (let i = 0; i < 2; i++) { if (powerUps.research.count > 0) powerUps.research.changeRerolls(-1) } }, remove() { tech.isSmartRadius = false; if (this.count > 0) powerUps.research.changeRerolls(3) } }, { name: "MIRV", description: "fire +1 missile or grenade per shot
0.88x explosion damage and radius", 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 explode on map collisions
explosions drain energy, not health", isGunTech: true, maxCount: 1, count: 0, frequency: 2, frequencyDefault: 2, allowed() { return tech.haveGunCheck("grenades") && !tech.isVacuumBomb && !tech.isSmartRadius }, requires: "grenades, not vacuum bomb, shaped charges", effect() { tech.isImmuneExplosion = true; tech.isRPG = true; b.setGrenadeMode() }, remove() { tech.isImmuneExplosion = false; 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 && !tech.isRPG }, requires: "grenades, not neutron bomb, chain reaction, RPG", effect() { tech.isVacuumBomb = true; b.setGrenadeMode() }, remove() { tech.isVacuumBomb = false; b.setGrenadeMode() } }, { name: "chain reaction", description: "1.33x grenade radius and damage
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: "after 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: "after 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: "after grenades detonate they release
colorful 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 radioactive damage over time", 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, chain reaction", effect() { tech.isNeutronBomb = true; b.setGrenadeMode() }, remove() { tech.isNeutronBomb = false; b.setGrenadeMode() } }, { name: "vacuum permittivity", description: "1.2x radioactive range
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: "1.5x radiation damage
nail, drone, neutron bomb, iridium, cosmic 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.5 }, remove() { tech.radioactiveDamage = 1 } }, { name: "water shielding", link: `water shielding`, description: "reduce radioactive effects on you by 0.8x
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, irradiated drones, iridium-192", effect() { tech.isRadioactiveResistance = true }, remove() { tech.isRadioactiveResistance = false } }, { name: "ricochet", description: "after nails hit a mob they rebound towards
a new mob with 2.8x damage per bounce", isGunTech: true, maxCount: 1, count: 0, frequency: 2, frequencyDefault: 2, allowed() { // return (tech.haveGunCheck("nail gun") && !tech.isRivets && !tech.isNeedles) || (tech.haveGunCheck("mines")) return tech.isMineDrop || tech.isNailBotUpgrade || tech.hookNails || tech.fragments || tech.nailsDeathMob || (tech.haveGunCheck("mine") && !(tech.isLaserMine || tech.isFoamMine || tech.isSuperMine)) || (tech.haveGunCheck("nail gun") && !tech.isRivets && !tech.isNeedles) || (tech.haveGunCheck("shotgun") && (tech.isNeedles || tech.isNailShot) && !tech.isRivets && !tech.isNeedles) }, // requires: "nail gun, not rotary cannon, rivets, or needles", effect() { tech.isRicochet = true }, remove() { tech.isRicochet = false } }, { name: "booby trap", description: "50% chance to drop a mine from power ups
+30% JUNKtech chance", 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.30) }, refundAmount: 0, remove() { tech.isMineDrop = false; if (this.count > 0 && this.refundAmount > 0) { tech.removeJunkTechFromPool(this.refundAmount) this.refundAmount = 0 } } }, { name: "elephants toothpaste", description: "instead of nails mines catalyze a reaction
that yields foam bubbles", isGunTech: true, maxCount: 1, count: 0, frequency: 2, frequencyDefault: 2, allowed() { return tech.haveGunCheck("mine") && !tech.isSuperMine && !tech.isRicochet && !tech.isNailRadiation && !tech.isNailCrit }, requires: "mines, not blast ball, ricochet, irradiated nails, supercritical fission", effect() { tech.isFoamMine = true; }, remove() { tech.isFoamMine = false; } }, { name: "blast ball", descriptionFunction() { return `instead of nails mines fire bouncy balls` }, isGunTech: true, maxCount: 1, count: 0, frequency: 2, frequencyDefault: 2, allowed() { return tech.haveGunCheck("mine") && !tech.isFoamMine && !tech.isRicochet && !tech.isNailRadiation && !tech.isNailCrit }, requires: "mines, not elephants toothpaste, ricochet, irradiated nails, supercritical fission", effect() { tech.isSuperMine = true; }, remove() { tech.isSuperMine = false; } }, { 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", descriptionFunction() { return `mines fire one ${b.guns[10].nameString()} at a time
mines fire 1.5x more ${b.guns[10].nameString('s')}` }, isGunTech: true, maxCount: 1, count: 0, frequency: 2, frequencyDefault: 2, allowed() { return tech.haveGunCheck("mine") }, requires: "mines, not elephants toothpaste", effect() { tech.isMineSentry = true; }, remove() { tech.isMineSentry = false; } }, { name: "extended magazine", descriptionFunction() { return `sentry mines fire 1.5x more ${b.guns[10].nameString('s')}` }, isGunTech: true, maxCount: 9, count: 0, frequency: 2, frequencyDefault: 2, allowed() { return tech.haveGunCheck("mine") && tech.isMineSentry }, requires: "mines, sentry", effect() { tech.sentryAmmo += 17; }, remove() { tech.sentryAmmo = 33; } }, { name: "mycelial fragmentation", link: `mycelial fragmentation`, description: "during their growth phase
1.7x sporangium discharge", 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: "cordyceps", // descriptionFunction() { // return `mobs infected by ${b.guns[6].nameString('s')} have a 5% chance
to resurrect and attack other mobs` // }, description: "sporangium infect mobs they attach to
infected mobs resurrect and attack other mobs", isGunTech: true, maxCount: 1, count: 0, frequency: 2, frequencyDefault: 2, allowed() { return tech.haveGunCheck("spores") }, requires: "spores", effect() { tech.isZombieMobs = true }, remove() { tech.isZombieMobs = false } }, { name: "colony", description: "1.6x sporangium discharge
33% chance to discharge something different", link: `colony`, isGunTech: true, maxCount: 1, count: 0, frequency: 2, frequencyDefault: 2, allowed() { return tech.haveGunCheck("spores") }, requires: "spores", effect() { tech.isSporeColony = true }, remove() { tech.isSporeColony = false } }, { name: "cryodesiccation", descriptionFunction() { return `1.25x sporangium discharge
${b.guns[6].nameString('s')} freeze mobs for 1.5 second` }, // description: "+25% sporangium discharge
spores freeze mobs for 1.5 second", isGunTech: true, maxCount: 1, count: 0, frequency: 2, frequencyDefault: 2, allowed() { return tech.haveGunCheck("spores") || tech.sporesOnDeath > 0 || (m.fieldMode === 4 && simulation.molecularMode === 0) || tech.isSporeWorm || tech.isSporeFlea }, requires: "spores", effect() { tech.isSporeFreeze = true }, remove() { tech.isSporeFreeze = false } }, { name: "flagella", descriptionFunction() { return `2x ${b.guns[6].nameString()} acceleration
if they can't find a target ${b.guns[6].nameString('s')} follow you` }, isGunTech: true, maxCount: 1, count: 0, frequency: 2, frequencyDefault: 2, allowed() { return tech.haveGunCheck("spores") || tech.sporesOnDeath > 0 || (m.fieldMode === 4 && simulation.molecularMode === 0) || tech.isSporeWorm || tech.isSporeFlea }, requires: "spores", effect() { tech.isSporeFollow = true }, remove() { tech.isSporeFollow = false } }, { name: "mutualism", descriptionFunction() { return `3x ${b.guns[6].nameString()} damage
${b.guns[6].nameString('s')} borrow 1 health until they die` }, isGunTech: true, maxCount: 1, count: 0, frequency: 2, frequencyDefault: 2, allowed() { return (tech.haveGunCheck("spores") || tech.sporesOnDeath > 0 || (m.fieldMode === 4 && simulation.molecularMode === 0)) || tech.isSporeWorm || tech.isSporeFlea }, requires: "spores", effect() { tech.isMutualism = true }, remove() { tech.isMutualism = false } }, { name: "necrophage", description: "if foam, fleas, or worms kill their target
they 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 || tech.isSporeFlea || tech.isFoamMine }, requires: "foam, spores, worms, fleas", effect() { tech.isSpawnBulletsOnDeath = true }, remove() { tech.isSpawnBulletsOnDeath = false; } }, { name: "siphonaptera", description: "spores metamorphose into fleas
shotgun fires fleas", isGunTech: true, maxCount: 1, count: 0, frequency: 3, frequencyDefault: 3, allowed() { return (tech.haveGunCheck("spores") || tech.sporesOnDeath > 0 || (m.fieldMode === 4 && simulation.molecularMode === 0) || (tech.haveGunCheck("shotgun") && !tech.isIncendiary && !tech.isRivets && !tech.isIceShot && !tech.isFoamShot && !tech.isNeedles && !tech.isNailShot)) && !tech.isSporeWorm }, requires: "spores, not worms", effect() { tech.isSporeFlea = true }, remove() { tech.isSporeFlea = false } }, { name: "nematodes", description: "spores metamorphose into worms
shotgun fires worms", isGunTech: true, maxCount: 1, count: 0, frequency: 3, frequencyDefault: 3, allowed() { return (tech.haveGunCheck("spores") || tech.sporesOnDeath > 0 || (m.fieldMode === 4 && simulation.molecularMode === 0) || (tech.haveGunCheck("shotgun") && !tech.isIncendiary && !tech.isRivets && !tech.isIceShot && !tech.isFoamShot && !tech.isNeedles && !tech.isNailShot)) && !tech.isSporeFlea }, requires: "spores, not fleas", effect() { tech.isSporeWorm = true }, remove() { tech.isSporeWorm = false } }, { name: "K-selection", description: "1.37x worm and flea damage", isGunTech: true, maxCount: 3, count: 0, frequency: 2, frequencyDefault: 2, allowed() { return tech.isSporeWorm || tech.isSporeFlea }, requires: "spores, shotgun, worms, fleas", effect() { tech.wormSize++ }, remove() { tech.wormSize = 0 } }, { name: "path integration", descriptionFunction() { return `drones and ${b.guns[6].nameString("s")}
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.fieldMode === 4 && (simulation.molecularMode === 0 || simulation.molecularMode === 3)) }, requires: "spores, worms, flagella, drones", effect() { tech.isDronesTravel = true }, remove() { tech.isDronesTravel = false } }, { name: "fault tolerance", description: `use ${powerUps.orb.research(2)}to trade your drone gun
for 5 drones that last forever`, // isGunTech: true, isRemoveGun: true, maxCount: 1, count: 0, frequency: 1, frequencyDefault: 1, isInstant: true, allowed() { return tech.haveGunCheck("drones", false) && !tech.isDroneRespawn && tech.bulletsLastLonger === 1 && !tech.isDronesTravel && (build.isExperimentSelection || powerUps.research.count > 1) }, requires: "drones, not drone repair, anti-shear topology, autonomous navigation", effect() { const num = 5 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 } } for (let i = 0; i < 2; i++) { if (powerUps.research.count > 0) powerUps.research.changeRerolls(-1) } }, remove() { tech.isForeverDrones = 0 // if (this.count && !tech.haveGunCheck("drones", false)) b.giveGuns("drones") // if (this.count > 0) powerUps.research.changeRerolls(2) } }, { name: "reduced tolerances", link: `reduced tolerances`, description: `2x drones per ${powerUps.orb.ammo()} and energy
0.6x drone duration`, isGunTech: true, maxCount: 1, count: 0, frequency: 2, frequencyDefault: 2, allowed() { return !tech.isDroneRadioactive && (tech.haveGunCheck("drones") || (m.fieldMode === 4 && simulation.molecularMode === 3)) }, requires: "drones, not irradiated drones", effect() { tech.droneCycleReduction = 0.6 tech.droneEnergyReduction = 0.3 for (i = 0, len = b.guns.length; i < len; i++) { //find which gun if (b.guns[i].name === "drones") b.guns[i].ammoPack *= 2 } }, remove() { tech.droneCycleReduction = 1 tech.droneEnergyReduction = 1 if (this.count > 0) { for (i = 0, len = b.guns.length; i < len; i++) { //find which gun if (b.guns[i].name === "drones") b.guns[i].ammoPack /= 2 } } } }, { 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.haveGunCheck("drones") || tech.isForeverDrones || (m.fieldMode === 4 && simulation.molecularMode === 3) }, requires: "drones", effect() { tech.isDroneGrab = true }, remove() { tech.isDroneGrab = false } }, { name: "von Neumann probe", //"drone repair", description: "after a drone expires it will use -4 energy
and a nearby block to replicate itself", // 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") || (m.fieldMode === 4 && simulation.molecularMode === 3) }, requires: "drones", effect() { tech.isDroneRespawn = true }, remove() { tech.isDroneRespawn = false } }, { name: "brushless motor", description: "drones rapidly rush towards their target
1.33x drone collision damage", isGunTech: true, maxCount: 1, count: 0, frequency: 3, frequencyDefault: 3, allowed() { return (tech.haveGunCheck("drones") || tech.isForeverDrones || (m.fieldMode === 4 && simulation.molecularMode === 3)) && !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: "1.66x drones rush frequency
1.44x drone collision damage", 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
0.25x drones per ${powerUps.orb.ammo()} and energy`, 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.fieldMode === 4 && simulation.molecularMode === 3)) }, 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 *= 0.25 b.guns[i].ammo = Math.ceil(b.guns[i].ammo * 0.25) simulation.makeGunHUD(); } } }, remove() { tech.isDroneRadioactive = false if (this.count > 0) { for (i = 0, len = b.guns.length; i < len; i++) { //find which gun if (b.guns[i].name === "drones") { b.guns[i].ammoPack /= 0.25 b.guns[i].ammo = b.guns[i].ammo * 4 simulation.makeGunHUD(); } } } } }, { name: "beta radiation", //"control rod ejection", description: "0.5x drone duration
2x drone radiation damage", 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: "1.66x drone acceleration
1.33x radiation damage", isGunTech: true, maxCount: 1, count: 0, frequency: 2, frequencyDefault: 2, allowed() { return tech.isDroneRadioactive }, requires: "drones, irradiated drones", effect() { tech.isFastDrones = true }, remove() { tech.isFastDrones = false } }, { name: "electrostatic induction", description: "foam bubbles are electrically charged
causing attraction to nearby mobs", isGunTech: true, maxCount: 1, count: 0, frequency: 1, frequencyDefault: 1, allowed() { return tech.haveGunCheck("foam") || tech.isFoamBotUpgrade || tech.isFoamShot || tech.isFoamBall || tech.isFoamMine }, requires: "foam", effect() { tech.isFoamAttract = true }, remove() { tech.isFoamAttract = false } }, { name: "uncertainty principle", description: "foam, wave, and super ball positions are erratic
1.5x foam, wave, and super ball damage", isGunTech: true, maxCount: 1, count: 0, frequency: 1, frequencyDefault: 1, allowed() { return (tech.haveGunCheck("foam") || tech.isFoamBotUpgrade || tech.isFoamShot || tech.isFoamBall || tech.isFoamMine) || (tech.haveGunCheck("wave") && !tech.is360Longitudinal) || (tech.haveGunCheck("super balls") && !tech.isSuperHarm) || tech.isSoundBotUpgrade }, requires: "foam, wave, super balls, not isotropic, Zectron", effect() { tech.isBulletTeleport = true }, remove() { tech.isBulletTeleport = false; } }, { name: "surfactant", description: `use ${powerUps.orb.research(2)}to trade your foam gun
for 2 foam-bots and foam-bot upgrade`, // isGunTech: true, isRemoveGun: true, maxCount: 1, count: 0, frequency: 1, frequencyDefault: 1, isBot: true, isBotTech: true, isInstant: true, requires: "foam gun, not bot upgrades, fractionation, pressure vessel", allowed() { return tech.haveGunCheck("foam", false) && !b.hasBotUpgrade() && !tech.isAmmoFoamSize && !tech.isFoamPressure && (build.isExperimentSelection || powerUps.research.count > 1) }, effect() { requestAnimationFrame(() => { 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") for (let i = 0; i < 2; i++) { if (powerUps.research.count > 0) powerUps.research.changeRerolls(-1) } }, remove() { // if (this.count) { // b.clearPermanentBots(); // b.respawnBots(); // if (!tech.haveGunCheck("foam")) b.giveGuns("foam") // } // if (this.count > 0) powerUps.research.changeRerolls(2) } }, { name: "aerogel", description: "foam bubbles float with 0.5x foam duration
2.8x foam damage", isGunTech: true, maxCount: 1, count: 0, frequency: 2, frequencyDefault: 2, allowed() { return tech.haveGunCheck("foam") || tech.isFoamBotUpgrade || tech.isFoamShot || tech.isFoamBall || tech.isFoamMine }, requires: "foam", effect() { tech.isFastFoam = true tech.foamGravity = -0.0003 }, remove() { tech.isFastFoam = false; tech.foamGravity = 0.00008 } }, { name: "surface tension", description: "1.4x foam damage", isGunTech: true, maxCount: 9, count: 0, frequency: 2, frequencyDefault: 2, allowed() { return tech.haveGunCheck("foam") || tech.isFoamBotUpgrade || tech.isFoamShot || tech.isFoamBall || tech.isFoamMine }, requires: "foam", effect() { tech.foamDamage += 0.01 * 0.4 }, remove() { tech.foamDamage = 0.01; } }, { name: "cavitation", description: "25% chance to discharge a huge foam bubble
2x foam gun recoil", isGunTech: true, maxCount: 1, count: 0, frequency: 2, frequencyDefault: 2, allowed() { return tech.haveGunCheck("foam") || tech.isFoamBotUpgrade || tech.isFoamShot || tech.isFoamBall || tech.isFoamMine }, requires: "foam", effect() { tech.isFoamCavitation = true; b.guns[8].knockBack = 0.001 }, remove() { tech.isFoamCavitation = false; b.guns[8].knockBack = 0.0005 } }, { name: "foam fractionation", description: "if you have below 300 ammo
2x foam gun bubble size", 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: `6x foam ammo per ${powerUps.orb.ammo(1)}`, //remove all current foam ammo
isGunTech: true, maxCount: 1, count: 0, frequency: 2, frequencyDefault: 2, allowed() { return tech.haveGunCheck("foam") && !tech.isEnergyNoAmmo }, requires: "foam, not non-renewables", // ammoLost: 0, effect() { b.guns[8].ammoPack *= 6; // this.ammoLost = b.guns[8].ammo // b.guns[8].ammo = 0 simulation.updateGunHUD() }, remove() { if (this.count) { b.guns[8].ammoPack /= 8 // b.guns[8].ammo += this.ammoLost simulation.updateGunHUD() } } }, { name: "pressure vessel", description: "build up charge while firing foam gun
after firing discharge foam bubbles", isGunTech: true, maxCount: 1, count: 0, frequency: 2, frequencyDefault: 2, allowed() { return tech.haveGunCheck("foam") }, requires: "foam", effect() { tech.isFoamPressure = true; b.guns[8].chooseFireMethod() }, remove() { tech.isFoamPressure = false; b.guns[8].chooseFireMethod() } }, { name: "capacitor bank", // description: "charge effects build up almost instantly
throwing blocks, foam, railgun, pulse, tokamak", descriptionFunction() { return `charge effects build up almost instantly
blocks, ${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: "mass driver, railgun, foam, pressure vessel, pulse, tokamak, plasma ball", effect() { tech.isCapacitor = true; }, remove() { tech.isCapacitor = false; } }, { name: "Bitter electromagnet", descriptionFunction() { return `0.66x railgun charge rate
2x harpoon density and damage` }, isGunTech: true, maxCount: 3, count: 0, frequency: 2, frequencyDefault: 2, allowed() { return tech.haveGunCheck("harpoon") && tech.isRailGun }, requires: "harpoon, railgun", effect() { tech.railChargeRate *= 1.06 tech.harpoonDensity += 0.007 }, remove() { tech.railChargeRate = 0.97; tech.harpoonDensity = tech.isRailGun ? 0.007 : 0.004 } }, { name: "railgun", description: `hold fire to charge harpoon and release to launch
harpoons can't retract`, // description: `+900% harpoon ammo, but it can't retract
+50% harpoon density and damage`, isGunTech: true, maxCount: 1, count: 0, frequency: 2, frequencyDefault: 2, allowed() { return tech.haveGunCheck("harpoon") && !tech.isFilament && !tech.isHarpoonPowerUp && !tech.isBoostReplaceAmmo }, requires: "harpoon, not UHMWPE, induction furnace, quasiparticles", ammoBonus: 9, effect() { tech.isRailGun = true; tech.harpoonDensity = tech.isRailGun ? 0.007 : 0.004 b.guns[9].chooseFireMethod() b.guns[9].ammoPack *= 3; b.guns[9].ammo = b.guns[9].ammo * 6; simulation.updateGunHUD(); }, remove() { tech.isRailGun = false; if (this.count > 0) { tech.harpoonDensity = tech.isRailGun ? 0.007 : 0.004 b.guns[9].chooseFireMethod() b.guns[9].ammoPack /= 3; b.guns[9].ammo = Math.ceil(b.guns[9].ammo / 6); simulation.updateGunHUD(); } } }, { name: "alternator", description: "0.05x harpoon energy cost", isGunTech: true, maxCount: 1, count: 0, frequency: 2, frequencyDefault: 2, allowed() { return tech.haveGunCheck("harpoon") }, requires: "harpoon", effect() { tech.isRailEnergy = true; }, remove() { tech.isRailEnergy = false; } }, { name: "autonomous defense", description: "if you collide with a mob
fire harpoons at nearby mobs", isGunTech: true, maxCount: 1, count: 0, frequency: 2, frequencyDefault: 2, allowed() { return tech.haveGunCheck("harpoon") }, requires: "harpoon", effect() { tech.isHarpoonDefense = true }, remove() { tech.isHarpoonDefense = false } }, { name: "Bessemer process", descriptionFunction() { return `${(1 + 0.1 * Math.sqrt(b.guns[9].ammo)).toFixed(2)}x harpoon size and damage
(effect scales by 1/10 √ 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", descriptionFunction() { return `forge ${this.removeAmmo()} ammo into a new harpoon
fire +1 harpoon with each shot` }, isGunTech: true, maxCount: 9, count: 0, frequency: 2, frequencyDefault: 2, ammoRemoved: 0, removeAmmo() { return (tech.isRailGun ? 5 : 1) * (2 + 2 * this.count) }, allowed() { return tech.haveGunCheck("harpoon") && b.guns[9].ammo >= this.removeAmmo() }, requires: "harpoon", effect() { for (i = 0, len = b.guns.length; i < len; i++) { //find which gun if (b.guns[i].name === "harpoon") { const removeAmmo = this.removeAmmo() this.ammoRemoved += removeAmmo b.guns[i].ammo -= removeAmmo if (b.guns[i].ammo < 0) b.guns[i].ammo = 0 simulation.updateGunHUD(); tech.extraHarpoons++; break } } }, remove() { if (tech.extraHarpoons) { for (i = 0, len = b.guns.length; i < len; i++) { //find which gun if (b.guns[i].name === "harpoon") { b.guns[i].ammo += this.ammoRemoved simulation.updateGunHUD(); break } } } this.ammoRemoved = 0 tech.extraHarpoons = 0; } }, { name: "UHMWPE", descriptionFunction() { return `${(1 + b.guns[9].ammo * 0.0125).toFixed(2)}x harpoon rope length
(effect scales by 1/80 of harpoon ammo)` }, isGunTech: true, maxCount: 1, count: 0, frequency: 2, frequencyDefault: 2, allowed() { return tech.haveGunCheck("harpoon") && !tech.isRailGun }, requires: "harpoon, not railgun", effect() { tech.isFilament = true; }, remove() { tech.isFilament = false; } }, { name: "induction furnace", description: "after using harpoon/grapple to collect power ups
1.8x harpoon or grapple damage for 8 seconds", isGunTech: true, maxCount: 1, count: 0, frequency: 2, frequencyDefault: 2, allowed() { return ((tech.haveGunCheck("harpoon") && !tech.isRailGun) || m.fieldMode === 10) && !tech.isHarpoonFullHealth }, requires: "harpoon, grappling hook, not railgun, brittle", effect() { tech.isHarpoonPowerUp = true }, remove() { tech.isHarpoonPowerUp = false tech.harpoonPowerUpCycle = 0 } }, { name: "brittle", description: "2.2x harpoon/grapple damage
to mobs at maximum durability", isGunTech: true, maxCount: 1, count: 0, frequency: 2, frequencyDefault: 2, allowed() { return (tech.haveGunCheck("harpoon") || m.fieldMode === 10) && !tech.isHarpoonPowerUp }, requires: "harpoon, grappling hook, not induction furnace", effect() { tech.isHarpoonFullHealth = true }, remove() { tech.isHarpoonFullHealth = false } }, { name: "quasiparticles", descriptionFunction() { return `convert current and future ${powerUps.orb.ammo(1)} into ${powerUps.orb.boost(1)}
that give ${(1 + powerUps.boost.damage).toFixed(2)}x damage for ${(powerUps.boost.duration / 60).toFixed(0)} seconds` }, isGunTech: true, maxCount: 1, count: 0, frequency: 1, frequencyDefault: 1, allowed() { return ((tech.haveGunCheck("wave") && tech.isInfiniteWaveAmmo) || tech.haveGunCheck("laser") || (tech.haveGunCheck("harpoon") && !tech.isRailGun)) && !tech.isEnergyNoAmmo }, requires: "harpoon, laser, wave, frequency, not railgun, non-renewables", effect() { tech.isBoostReplaceAmmo = true for (let i = powerUp.length - 1; i > -1; i--) { if (powerUp[i].name === "ammo") { powerUps.spawn(powerUp[i].position.x + 50 * (Math.random() - 0.5), powerUp[i].position.y + 50 * (Math.random() - 0.5), "boost"); Matter.Composite.remove(engine.world, powerUp[i]); powerUp.splice(i, 1); } } }, remove() { tech.isBoostReplaceAmmo = false } }, { 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, isInstant: true, allowed() { return tech.haveGunCheck("laser") && !tech.isPulseLaser }, requires: "laser gun, not pulse", effect() { requestAnimationFrame(() => { let techGiven = 0 for (let j = 0; j < 3; j++) { const names = ["quasiparticles", "lens", "compound lens", "arc length", "infrared diode", "free-electron laser", "dye laser", "relativistic momentum", "specular reflection", "diffraction grating", "diffuse beam", "output coupler", "slow light", "laser-bot", "laser-bot upgrade"] //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`, 360); tech.giveTech(index) techGiven++ } } if (techGiven > 0) { tech.isStuckOn = true } else { //eject if none found 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: "relativistic momentum", description: "lasers push mobs and blocks", isGunTech: true, maxCount: 1, count: 0, frequency: 2, frequencyDefault: 2, allowed() { return (tech.haveGunCheck("laser") && !tech.isPulseLaser) || tech.isLaserBotUpgrade || tech.isLaserField }, requires: "laser, not pulse", effect() { tech.isLaserPush = true; }, remove() { tech.isLaserPush = false; } }, { name: "iridescence", description: "if laser beams hit mobs near their center
2x laser damage", isGunTech: true, maxCount: 9, count: 0, frequency: 2, frequencyDefault: 2, allowed() { return (tech.haveGunCheck("laser") && !tech.isPulseLaser) || tech.isLaserBotUpgrade || tech.isLaserMine }, requires: "laser, not pulse", effect() { tech.laserCrit += 1; }, remove() { tech.laserCrit = 0; } }, { name: "lens", description: "2.5x laser gun damage if it passes
through a revolving 90° arc circular lens", //π / 2 isGunTech: true, maxCount: 1, count: 0, frequency: 2, frequencyDefault: 2, allowed() { return tech.haveGunCheck("laser") }, requires: "laser", effect() { tech.isLaserLens = true b.guns[11].chooseFireMethod() // if (this.count > 0) b.guns[11].lensDamageOn += 20 * Math.PI / 180 // b.guns[11].arcRange = 0.78 }, remove() { tech.isLaserLens = false b.guns[11].chooseFireMethod() // b.guns[11].lensDamageOn = 2.5 // 100% + 150% // b.guns[11].arcRange = 0 } }, { name: "compound lens", description: "1.4x laser lens damage
+25° lens arc", isGunTech: true, maxCount: 9, count: 0, frequency: 2, frequencyDefault: 2, allowed() { return tech.haveGunCheck("laser") && tech.isLaserLens }, requires: "lens", effect() { b.guns[11].arcRange += 25 * Math.PI / 180 / 2 b.guns[11].lensDamageOn += 0.4 }, remove() { b.guns[11].arcRange = 90 * Math.PI / 180 / 2 //0.78 divded by 2 because of how it's drawn b.guns[11].lensDamageOn = 2.5 } }, { name: "specular reflection", description: "+2 laser beam reflections", isGunTech: true, maxCount: 3, count: 0, frequency: 1, frequencyDefault: 1, allowed() { return (tech.haveGunCheck("laser") || tech.isLaserMine || tech.isLaserBotUpgrade || tech.isLaserField) && !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: `+1 diverging laser gun beam`, isGunTech: true, maxCount: 9, count: 0, frequency: 1, frequencyDefault: 1, allowed() { return tech.haveGunCheck("laser") && !tech.isWideLaser && !tech.historyLaser }, requires: "laser gun, diffuse beam, or slow light", effect() { tech.beamSplitter++ b.guns[11].chooseFireMethod() }, remove() { if (tech.beamSplitter !== 0) { tech.beamSplitter = 0 b.guns[11].chooseFireMethod() } } }, { name: "diffuse beam", link: `diffuse beam`, description: "laser gun beam is wider and doesn't reflect
3.2x laser damage", 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; b.guns[11].chooseFireMethod() }, remove() { if (tech.isWideLaser) { // tech.wideLaser = 0 tech.isWideLaser = false; b.guns[11].chooseFireMethod() } } }, { name: "output coupler", description: "1.3x laser gun beam width
1.3x laser damage", isGunTech: true, maxCount: 9, count: 0, frequency: 2, frequencyDefault: 2, allowed() { return tech.haveGunCheck("laser") && tech.isWideLaser }, requires: "laser gun, diffuse beam", effect() { tech.wideLaser += 2 b.guns[11].chooseFireMethod() }, remove() { if (tech.isWideLaser) { tech.wideLaser = 3 } else { tech.wideLaser = 0 } b.guns[11].chooseFireMethod() } }, { name: "slow light", description: "laser gun beam is spread into your recent past
4x full beam damage", 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++ b.guns[11].chooseFireMethod() }, remove() { if (tech.historyLaser) { tech.historyLaser = 0 b.guns[11].chooseFireMethod() } } }, { name: "infrared diode", description: "0.4x laser energy cost
infrared light is outside visual perception", isGunTech: true, maxCount: 1, count: 0, frequency: 1, frequencyDefault: 1, allowed() { return (tech.haveGunCheck("laser") || tech.isLaserBotUpgrade || tech.isLaserMine || tech.isLaserField) && !tech.isPulseLaser && tech.laserDrain === 0.003 }, requires: "laser, not free-electron, pulse", effect() { tech.laserDrain *= 0.4; //100%-50% tech.laserColor = "transparent" //"rgb(255,0,20,0.02)" // tech.laserColorAlpha = "rgba(255,0,20,0.05)" }, remove() { tech.laserDrain = 0.003; tech.laserColor = "#f02" tech.laserColorAlpha = "rgba(255, 0, 0, 0.5)" } }, { name: "dye laser", description: "0.75x laser energy cost
1.25x laser damage", isGunTech: true, maxCount: 1, count: 0, frequency: 1, frequencyDefault: 1, allowed() { return (tech.haveGunCheck("laser") || tech.isLaserMine || tech.isLaserBotUpgrade || tech.isLaserField) && !tech.isPulseLaser && tech.laserDrain === 0.003 }, requires: "laser, not pulse, infrared diode", effect() { tech.laserDrain *= 0.75 tech.laserDamage *= 1.25 tech.laserColor = "rgb(0, 11, 255)" tech.laserColorAlpha = "rgba(0, 11, 255,0.5)" }, remove() { tech.laserDrain = 0.003; 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: "free-electron laser", description: "3x laser energy cost
3x laser damage", isGunTech: true, maxCount: 1, count: 0, frequency: 1, frequencyDefault: 1, allowed() { return (tech.haveGunCheck("laser") || tech.isLaserMine || tech.isLaserBotUpgrade || tech.isLaserField) && !tech.isPulseLaser && tech.laserDrain === 0.003 }, requires: "laser, not pulse, infrared diode", effect() { tech.laserDrain *= 3 tech.laserDamage *= 3 tech.laserColor = "#83f" tech.laserColorAlpha = "rgba(136, 51, 255,0.5)" }, remove() { tech.laserDrain = 0.003; 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: "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.laserDrain === 0.003 && !tech.isStuckOn }, requires: "laser gun, not specular reflection, diffuse, free-electron laser, optical amplifier", effect() { tech.isPulseLaser = true; b.guns[11].chooseFireMethod() }, remove() { if (tech.isPulseLaser) { tech.isPulseLaser = false; b.guns[11].chooseFireMethod() } } }, //************************************************** //************************************************** field //************************************************** tech //************************************************** { name: "spherical harmonics", description: "1.5x standing wave deflection energy efficiency
shield deflection radius holds it's max range", //standing wave oscillates in a 3rd dimension
isFieldTech: true, maxCount: 9, count: 0, frequency: 2, frequencyDefault: 2, allowed() { return m.fieldMode === 1 && !tech.isLaserField }, requires: "standing wave, not surface plasmons", effect() { tech.harmonics++ m.fieldShieldingScale = 1.6 * Math.pow(0.5, (tech.harmonics - 2)) m.harmonicShield = m.harmonicAtomic }, remove() { tech.harmonics = 2 m.fieldShieldingScale = 1.6 * Math.pow(0.5, (tech.harmonics - 2)) m.harmonicShield = m.harmonic3Phase } }, { name: "surface plasmons", description: "if deflecting drains all your energy
emit laser beams that scale with max energy", isFieldTech: true, maxCount: 1, count: 0, frequency: 2, frequencyDefault: 2, allowed() { return m.fieldMode === 1 && tech.harmonics === 2 }, requires: "standing wave", effect() { tech.isLaserField = true }, remove() { tech.isLaserField = false } }, { name: "zero point energy", description: `use ${powerUps.orb.research(2)}
+166 maximum energy`, isFieldTech: true, maxCount: 1, count: 0, frequency: 3, frequencyDefault: 3, allowed() { return (m.fieldMode === 1 || m.fieldMode === 8 || m.fieldMode === 6) && (build.isExperimentSelection || powerUps.research.count > 1) }, requires: "standing wave, pilot wave, time dilation", effect() { tech.harmonicEnergy = 1.66 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: "expansion", description: "using standing wave field expands its radius
+77 maximum energy", isFieldTech: true, maxCount: 1, count: 0, frequency: 2, frequencyDefault: 2, allowed() { return m.fieldMode === 1 }, requires: "standing wave", effect() { tech.isStandingWaveExpand = true m.setMaxEnergy() // m.fieldShieldingScale = (tech.isStandingWaveExpand ? 0.9 : 1.6) * Math.pow(0.6, (tech.harmonics - 2)) }, remove() { tech.isStandingWaveExpand = false m.setMaxEnergy() // m.fieldShieldingScale = (tech.isStandingWaveExpand ? 0.9 : 1.6) * Math.pow(0.6, (tech.harmonics - 2)) m.harmonicRadius = 1 } }, { name: "electronegativity", descriptionFunction() { return `1.0023x damage per energy
(${(1 + 0.23 * m.maxEnergy).toFixed(2)}x damage at max energy)` }, // description: "+1% damage per 8 stored energy", isFieldTech: true, maxCount: 9, count: 0, frequency: 2, frequencyDefault: 2, allowed() { return m.fieldMode === 1 || m.fieldMode === 9 || m.fieldMode === 8 }, requires: "standing wave, wormhole, pilot wave", effect() { tech.energyDamage++ }, remove() { tech.energyDamage = 0; } }, { 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.fieldMode === 1 || m.fieldMode === 2 || m.fieldMode === 8 }, requires: "standing wave, perfect diamagnetism, pilot wave", effect() { tech.blockDmg += 5 //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
3.5x damage over 3 seconds", isFieldTech: true, maxCount: 1, count: 0, frequency: 2, frequencyDefault: 2, allowed() { return (m.fieldMode === 1 || m.fieldMode === 2 || m.fieldMode === 8) && tech.blockDmg }, requires: "bremsstrahlung", effect() { tech.isBlockRadiation = true }, remove() { tech.isBlockRadiation = false; } }, { name: "flux pinning", description: "after deflecting a mob
it is stunned for up to 4 seconds", isFieldTech: true, maxCount: 9, count: 0, frequency: 2, frequencyDefault: 2, allowed() { return m.fieldMode === 2 || m.fieldMode === 1 || m.fieldMode === 4 }, 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.fieldMode === 2 && !tech.isHealBrake }, requires: "perfect diamagnetism, not induction brake", effect() { tech.isPerfectBrake = true; }, remove() { tech.isPerfectBrake = false; } }, { name: "Meissner effect", description: "1.55x perfect diamagnetism radius
+22° perfect diamagnetism circular arc", isFieldTech: true, maxCount: 1, count: 0, frequency: 2, frequencyDefault: 2, allowed() { return m.fieldMode === 2 }, requires: "perfect diamagnetism", effect() { tech.isBigField = true; }, remove() { tech.isBigField = false; } }, { name: "radiative equilibrium", descriptionFunction() { return `after losing ${tech.isEnergyHealth ? "energy" : "health"}
3x damage for 8 seconds` }, isFieldTech: true, maxCount: 1, count: 0, frequency: 2, frequencyDefault: 2, allowed() { return m.fieldMode === 8 || m.fieldMode === 3 }, requires: "negative mass, pilot wave", effect() { tech.isHarmDamage = true; }, remove() { tech.isHarmDamage = false; } }, { name: "dynamic equilibrium", descriptionFunction() { return `increase damage by your last ${tech.isEnergyHealth ? "energy" : "health"} loss
(${(1 + tech.lastHitDamage * m.lastHit).toFixed(2)}x damage)` }, isFieldTech: true, maxCount: 9, count: 0, frequency: 2, frequencyDefault: 2, allowed() { return m.fieldMode === 8 || m.fieldMode === 3 }, requires: "negative mass, pilot wave", effect() { tech.lastHitDamage += 5; }, remove() { tech.lastHitDamage = 0; } }, { name: "neutronium", description: `0.8x move and jump, but
if your field is active 0.05x damage taken`, isFieldTech: true, maxCount: 1, count: 0, frequency: 2, frequencyDefault: 2, allowed() { return m.fieldMode === 3 }, requires: "negative mass", effect() { tech.isNeutronium = true tech.baseFx *= 0.8 tech.baseJumpForce *= 0.8 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: "aerostat", description: `2x damage while off the ground
0.85x damage while on the ground`, isFieldTech: true, maxCount: 1, count: 0, frequency: 2, frequencyDefault: 2, allowed() { return m.fieldMode === 3 || m.fieldMode === 10 }, requires: "negative mass, grappling hook", effect() { tech.isNoGroundDamage = true }, remove() { tech.isNoGroundDamage = false } }, { name: "annihilation", description: "after colliding with non-boss mobs
they are annihilated and –10 energy", isFieldTech: true, maxCount: 1, count: 0, frequency: 2, frequencyDefault: 2, allowed() { return m.fieldMode === 3 && !tech.isEnergyHealth }, requires: "negative mass, not mass-energy", 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.fieldMode === 3 }, 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.fieldMode === 8 || m.fieldMode === 3 || (m.fieldMode === 6 && !tech.isRewindField) // }, // requires: "pilot wave, negative mass, time dilation, not retrocausality", // effect() { // tech.isFreezeMobs = true // }, // remove() { // tech.isFreezeMobs = false // } // }, // { // name: "mycelium manufacturing", // link: `mycelium manufacturing`, // // description: `use ${powerUps.orb.research(1)}to repurpose molecular assembler
excess energy used to grow spores`, // descriptionFunction() { return `use ${powerUps.orb.research(1)}to repurpose molecular assembler
excess energy used to grow ${b.guns[6].nameString('s')}` }, // isFieldTech: true, // maxCount: 1, // count: 0, // frequency: 3, // frequencyDefault: 3, // allowed() { // return (build.isExperimentSelection || powerUps.research.count > 0) && m.fieldMode === 4 && !(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.fieldMode === 4 && !(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.fieldMode === 4 && !(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: "additive manufacturing", description: "hold crouch and use your field to print a block
with 1.8x density, damage, and launch speed", isFieldTech: true, maxCount: 1, count: 0, frequency: 2, frequencyDefault: 2, allowed() { return (m.fieldMode === 4 || m.fieldMode === 8) && !tech.isTokamak }, requires: "molecular assembler, pilot wave, not tokamak", effect() { tech.isPrinter = true; }, remove() { if (this.count > 0) m.holdingTarget = null; tech.isPrinter = false; } }, { name: "working mass", // description: "molecular assembler prints one block
to jump off while midair", descriptionFunction() { const fieldName = m.fieldMode === 8 ? "pilot wave" : "molecular assembler" return `${fieldName} prints a block
to jump off while midair a second time` }, isFieldTech: true, maxCount: 1, count: 0, frequency: 2, frequencyDefault: 2, allowed() { return (m.fieldMode === 4 || m.fieldMode === 8) }, requires: "molecular assembler, pilot wave", effect() { tech.isBlockJump = true }, remove() { tech.isBlockJump = false } }, { name: "pair production", description: "after picking up a power up
+200 energy", isFieldTech: true, maxCount: 1, count: 0, frequency: 2, frequencyDefault: 2, allowed() { return m.fieldMode === 4 || m.fieldMode === 1 || m.fieldMode === 8 }, requires: "molecular assembler, pilot wave, standing wave", effect() { tech.isMassEnergy = true // used in m.grabPowerUp m.energy += 2 }, remove() { tech.isMassEnergy = false; } }, { name: "electric generator", description: "after deflecting mobs
molecular assembler generates +50 energy", isFieldTech: true, maxCount: 9, count: 0, frequency: 2, frequencyDefault: 2, allowed() { return m.fieldMode === 4 }, requires: "molecular assembler", effect() { tech.deflectEnergy += 0.5; }, remove() { tech.deflectEnergy = 0; } }, { name: "combinatorial optimization", description: "1.35x damage
0.7x fire rate", isFieldTech: true, maxCount: 1, count: 0, frequency: 2, frequencyDefault: 2, allowed() { return m.fieldMode === 6 || m.fieldMode === 7 || m.fieldMode === 8 }, requires: "time dilation, cloaking, pilot wave", damage: 1.35, effect() { tech.damage *= this.damage tech.aimDamage = 1.42 b.setFireCD(); }, remove() { if (this.count && m.alive) tech.damage /= this.damage tech.aimDamage = 1 b.setFireCD(); } }, { 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.fieldMode === 5 || m.fieldMode === 4 || m.fieldMode === 10) && !tech.isPrinter && !tech.isReel && !tech.hookNails }, requires: "plasma torch, molecular assembler, grappling hook, not printer, reel, swarf", effect() { tech.isTokamak = true; }, remove() { tech.isTokamak = false; } }, { name: "degenerate matter", description: "if your field is active
0.1x damage taken", isFieldTech: true, maxCount: 1, count: 0, frequency: 2, frequencyDefault: 2, allowed() { return (m.fieldMode === 10 || m.fieldMode === 5 || m.fieldMode === 8) //1 standing wave 2 perfect diamagnetism 3 negative mass 4 molecular assembler 5 plasma torch 6 time dilation 7 metamaterial cloaking 8 pilot wave 9 wormhole 10 grappling hook }, requires: "plasma torch, grappling hook, pilot wave", effect() { tech.isHarmReduce = true }, remove() { tech.isHarmReduce = false; } }, { name: "plasma-bot", link: `plasma-bot`, description: `use ${powerUps.orb.research(2)}to trade your field
for a bot that uses energy to emit plasma`, // isFieldTech: true, isInstant: true, maxCount: 1, count: 0, frequency: 3, frequencyDefault: 3, isBot: true, isBotTech: true, allowed() { return m.fieldMode === 5 && !tech.isPlasmaBall && !tech.isExtruder && (build.isExperimentSelection || powerUps.research.count > 1) }, 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") for (let i = 0; i < 2; i++) { if (powerUps.research.count > 0) powerUps.research.changeRerolls(-1) } }, 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"); } } powerUps.research.changeRerolls(2) } } }, { name: "plasma jet", link: `plasma jet`, description: `use ${powerUps.orb.research(2)}
1.5x plasma torch range`, isFieldTech: true, maxCount: 1, count: 0, frequency: 2, frequencyDefault: 2, allowed() { return (tech.plasmaBotCount || m.fieldMode === 5) && (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 cost", isFieldTech: true, maxCount: 1, count: 0, frequency: 2, frequencyDefault: 2, allowed() { return m.fieldMode === 5 && !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.fieldMode === 5) 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 m.fieldMode === 5 && 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 cost", isFieldTech: true, maxCount: 1, count: 0, frequency: 2, frequencyDefault: 2, allowed() { return m.fieldMode === 5 && !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.fieldMode === 5) 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.fieldMode === 5 && 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 seconds", isFieldTech: true, maxCount: 1, count: 0, frequency: 1, frequencyDefault: 1, allowed() { return m.fieldMode === 6 && !m.isShipMode && !tech.isRewindAvoidDeath && !tech.isTimeSkip }, requires: "time dilation, not CPT symmetry", effect() { tech.isRewindField = true; m.fieldUpgrades[6].set() m.wakeCheck(); }, remove() { tech.isRewindField = false; if (this.count) m.fieldUpgrades[6].set() } }, { name: "frame-dragging", //"non-inertial frame", description: "when not moving time dilation stops time
0.6x damage taken", isFieldTech: true, maxCount: 1, count: 0, frequency: 1, frequencyDefault: 1, allowed() { return m.fieldMode === 6 }, requires: "time dilation", effect() { tech.isTimeStop = true; m.fieldHarmReduction = 0.66; //33% reduction }, remove() { tech.isTimeStop = false; if (m.fieldMode === 6) m.fieldHarmReduction = 1; } }, { name: "Lorentz transformation", description: `use ${powerUps.orb.research(3)}
1.5x movement, jumping, and fire rate`, isFieldTech: true, maxCount: 1, count: 0, frequency: 3, frequencyDefault: 3, allowed() { return (m.fieldMode === 6 || m.fieldMode === 8) && (build.isExperimentSelection || powerUps.research.count > 2) }, requires: "time dilation or pilot wave", 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", descriptionFunction() { return `2.5x passive energy generation
(+${(150 * m.fieldRegen * 60).toFixed(1)} energy per second)` }, isFieldTech: true, maxCount: 1, count: 0, frequency: 2, frequencyDefault: 2, allowed() { return !tech.isGroundState && (m.fieldMode === 6 || m.fieldMode === 8) }, requires: "time dilation or pilot wave, not ground state", effect() { tech.isTimeCrystals = true m.setFieldRegen() this.descriptionFunction = function () { return `2.5x passive energy generation
(+${(60 * m.fieldRegen * 60).toFixed(1)} energy per second)` } }, remove() { tech.isTimeCrystals = false m.setFieldRegen() this.descriptionFunction = function () { return `2.5x passive energy generation
(+${(150 * m.fieldRegen * 60).toFixed(1)} energy per second)` } } }, { name: "no-cloning theorem", description: `+40% chance to duplicate spawned power ups
after a mob dies –1% duplication`, isFieldTech: true, maxCount: 1, count: 0, frequency: 2, frequencyDefault: 2, allowed() { return (m.fieldMode === 6 || m.fieldMode === 7) }, requires: "cloaking, time dilation", effect() { tech.cloakDuplication = 0.4 powerUps.setPowerUpMode(); //needed after adjusting duplication chance if (!build.isExperimentSelection && !simulation.isTextLogOpen) simulation.circleFlare(0.4); }, remove() { tech.cloakDuplication = 0 if (this.count) powerUps.setPowerUpMode(); //needed after adjusting duplication chance } }, { name: "metamaterial absorber", //quantum eraser descriptionFunction() { return `for each mob left alive after you exit a level
there is a 30% chance to spawn a random power up` }, isFieldTech: true, maxCount: 1, count: 0, frequency: 2, frequencyDefault: 2, allowed() { return (m.fieldMode === 7) && !tech.cloakDuplication }, requires: "cloaking", effect() { tech.isQuantumEraser = true }, remove() { tech.isQuantumEraser = false } }, { name: "symbiosis", descriptionFunction() { return `after a boss dies spawn ${powerUps.orb.research(4)}${powerUps.orb.heal(3)} and a tech
after a mob dies –0.25 maximum ${tech.isEnergyHealth ? "energy" : "health"}` }, isFieldTech: true, maxCount: 1, count: 0, frequency: 2, frequencyDefault: 2, allowed() { return m.fieldMode === 7 //|| m.fieldMode === 6 }, requires: "cloaking", effect() { tech.isAddRemoveMaxHealth = true }, remove() { tech.isAddRemoveMaxHealth = false } }, { name: "boson composite", link: `boson composite`, description: "while cloaked you are intangible
to blocks and mobs, but mobs drain energy", isFieldTech: true, maxCount: 1, count: 0, frequency: 2, frequencyDefault: 2, allowed() { return m.fieldMode === 7 }, 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: "patch", link: `patch`, description: "after cloaking recover 0.75x
of your last health lost", isFieldTech: true, maxCount: 1, count: 0, frequency: 2, frequencyDefault: 2, allowed() { return m.fieldMode === 7 && !tech.isEnergyHealth }, requires: "metamaterial cloaking, not mass-energy", effect() { tech.isCloakHealLastHit = true; }, remove() { tech.isCloakHealLastHit = false; } }, { name: "dazzler", link: `dazzler`, description: "after decloaking
stun nearby mobs for 2 second", isFieldTech: true, maxCount: 1, count: 0, frequency: 2, frequencyDefault: 2, allowed() { return m.fieldMode === 7 }, requires: "metamaterial cloaking", effect() { tech.isCloakStun = true; }, remove() { tech.isCloakStun = false; } }, { name: "topological defect", description: "2.1x damage
to mobs at maximum durability", isFieldTech: true, maxCount: 1, count: 0, frequency: 1, frequencyDefault: 1, allowed() { return (m.fieldMode === 8 || m.fieldMode === 7) && tech.mobSpawnWithHealth === 0 }, requires: "cloaking, pilot wave, not reaction inhibitor", effect() { tech.isMobFullHealthCloak = true }, remove() { tech.isMobFullHealthCloak = false } }, { name: "WIMPs", description: `at the end of each level spawn ${powerUps.orb.research(4)}
and a dangerous particle that slowly chases you`, isFieldTech: true, maxCount: 9, count: 0, frequency: 2, frequencyDefault: 2, allowed() { return m.fieldMode === 9 || m.fieldMode === 8 || m.fieldMode === 6 }, requires: "wormhole, pilot wave, time dilation", effect() { tech.wimpCount++ spawn.WIMP() for (let j = 0, len = 4; 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: "vacuum fluctuation", description: `use ${powerUps.orb.research(3)}
+11% chance to duplicate spawned power ups`, isFieldTech: true, maxCount: 1, count: 0, frequency: 3, frequencyDefault: 3, allowed() { return (m.fieldMode === 8 || m.fieldMode === 6 || m.fieldMode === 9) && (build.isExperimentSelection || powerUps.research.count > 2) }, requires: "wormhole, time dilation, negative mass, pilot wave", effect() { tech.fieldDuplicate = 0.11 powerUps.setPowerUpMode(); //needed after adjusting duplication chance if (!build.isExperimentSelection && !simulation.isTextLogOpen) simulation.circleFlare(0.11); for (let i = 0; i < 3; i++) { if (powerUps.research.count > 0) powerUps.research.changeRerolls(-1) } }, remove() { tech.fieldDuplicate = 0 if (this.count) powerUps.setPowerUpMode(); //needed after adjusting duplication chance if (this.count > 0) powerUps.research.changeRerolls(3) } }, { name: "transdimensional worms", link: `transdimensional worms`, description: "after a block falls into a wormhole
spawn a worm", isFieldTech: true, maxCount: 1, count: 0, frequency: 2, frequencyDefault: 2, allowed() { return m.fieldMode === 9 }, requires: "wormhole", effect() { tech.isWormholeWorms = true }, remove() { tech.isWormholeWorms = false } }, { name: "anyon", descriptionFunction() { return `2x energy after duplicating a power up
+6% chance to duplicate spawned power ups` }, isFieldTech: true, maxCount: 1, count: 0, frequency: 1, frequencyDefault: 1, allowed() { return m.fieldMode === 9 || m.fieldMode === 1 }, requires: "wormhole, standing wave", effect() { tech.isDupEnergy = true; powerUps.setPowerUpMode(); //needed after adjusting duplication chance if (!build.isExperimentSelection && !simulation.isTextLogOpen) simulation.circleFlare(0.06); }, remove() { tech.isDupEnergy = false; if (this.count) powerUps.setPowerUpMode(); //needed after adjusting duplication chance } } }, { name: "geodesics", description: `your bullets can traverse wormholes
1.5x damage`, isFieldTech: true, maxCount: 1, count: 0, frequency: 2, frequencyDefault: 2, allowed() { return m.fieldMode === 9 }, requires: "wormhole", damage: 1.5, effect() { tech.damage *= this.damage tech.isWormHoleBullets = true // for (let i = 0; i < 2; i++) powerUps.spawn(m.pos.x + 200 * (Math.random() - 0.5), m.pos.y + 200 * (Math.random() - 0.5), "gun"); // for (let i = 0; i < 4; i++) powerUps.spawn(m.pos.x + 200 * (Math.random() - 0.5), m.pos.y + 200 * (Math.random() - 0.5), "ammo"); }, remove() { // if (tech.isWormHoleBullets) { // 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 // } // } if (this.count && m.alive) tech.damage /= this.damage tech.isWormHoleBullets = false; } }, { name: "cosmic string", description: "after tunneling through mobs with a wormhole
stun them and do radioactive damage", isFieldTech: true, maxCount: 1, count: 0, frequency: 2, frequencyDefault: 2, allowed() { return m.fieldMode === 9 }, requires: "wormhole", effect() { tech.isWormholeDamage = true }, remove() { tech.isWormholeDamage = false } }, { name: "invariant", description: "while placing your wormhole
use energy to pause time", isFieldTech: true, maxCount: 1, count: 0, frequency: 2, frequencyDefault: 2, allowed() { return m.fieldMode === 9 && !tech.isNoDraftPause }, requires: "wormhole, not eternalism", effect() { tech.isWormHolePause = true }, remove() { if (tech.isWormHolePause && m.isBodiesAsleep) m.wakeCheck(); tech.isWormHolePause = false } }, { name: "charmed baryons", description: `0.66x movement and jumping
wormholes cost zero energy`, isFieldTech: true, maxCount: 1, count: 0, frequency: 2, frequencyDefault: 2, allowed() { return m.fieldMode === 9 && !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 anything
for 2x energy cost", isFieldTech: true, maxCount: 1, count: 0, frequency: 2, frequencyDefault: 2, allowed() { return m.fieldMode === 9 && !tech.isFreeWormHole }, requires: "wormhole, not charmed baryons", effect() { tech.isWormholeMapIgnore = true }, remove() { tech.isWormholeMapIgnore = false } }, { name: "CIWS", description: "grappling hook uses 10 energy
to fire harpoons at nearby mobs", isFieldTech: true, maxCount: 1, count: 0, frequency: 2, frequencyDefault: 2, allowed() { return m.fieldMode === 10 }, requires: "grappling hook", effect() { tech.isHookDefense = true }, remove() { tech.isHookDefense = false } }, { name: "swarf", // description: "after grappling hook impacts solid objects generate an explosion and become briefly invulnerable", description: "after grappling hook impacts something
eject nails splinters towards nearby mobs", isFieldTech: true, maxCount: 3, count: 0, frequency: 2, frequencyDefault: 2, allowed() { return m.fieldMode === 10 && !tech.isReel && !tech.isTokamak }, requires: "grappling hook, not reel, tokamak", effect() { tech.hookNails += 4 }, remove() { tech.hookNails = 0 } }, { name: "reel", description: "5x block collision damage
up to +100 energy after reeling in blocks", isFieldTech: true, maxCount: 1, count: 0, frequency: 1, frequencyDefault: 1, allowed() { return m.fieldMode === 10 && !tech.isTokamak && tech.blockDamage === 0.075 && !tech.hookNails }, requires: "grappling hook, not mass driver, swarf, tokamak", effect() { tech.blockDamage = 0.375 tech.isReel = true }, remove() { tech.blockDamage = 0.075 tech.isReel = false } }, // { // name: "CIWS", // description: "if you collide with a mob
fire harpoons at nearby mobs", // isFieldTech: true, // maxCount: 1, // count: 0, // frequency: 2, // frequencyDefault: 2, // allowed() { // return m.fieldMode === 10 && !tech.isHookDefense // }, // requires: "grappling hook, not automatic offense", // effect() { // tech.isHookDefense = true // }, // remove() { // tech.isHookDefense = false // } // }, // { // name: "wire", // description: "", // isFieldTech: true, // maxCount: 1, // count: 0, // frequency: 2, // frequencyDefault: 2, // allowed() { // return m.fieldMode === 10 // }, // requires: "grappling hook", // effect() { // tech.isHookWire = true // }, // remove() { // tech.isHookWire = false // } // }, //************************************************** //************************************************** experimental //************************************************** modes //************************************************** // { // name: "-ship-", // description: "experiment: fly around with no legs
aim with the keyboard", // maxCount: 1, // count: 0, // frequency: 0, // isInstant: 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 // } // }, //************************************************** //************************************************** JUNK //************************************************** tech //************************************************** // { // name: "junk", // description: "", // maxCount: 9, // count: 0, // frequency: 0, // isInstant: true, // isJunk: true, // allowed() { // return true // }, // requires: "", // effect() { // }, // remove() {} // }, { name: "swap meet", description: "normal tech become JUNK
and JUNK become normal tech", maxCount: 1, count: 0, frequency: 0, isJunk: true, isInstant: true, allowed() { return true }, requires: "", effect() { for (let i = 0, len = tech.tech.length; i < len; i++) { tech.tech[i].isJunk = !tech.tech[i].isJunk if (tech.tech[i].isJunk) { } else { } if (tech.tech[i].frequency > 0) { tech.tech[i].frequency = 0 } else { tech.tech[i].frequency = 2 } } }, remove() { } }, // { // name: "pocket dimension", // description: "rotate tech descriptions into a higher spacial dimension", // maxCount: 1, // count: 0, // frequency: 0, // isJunk: true, // isInstant: true, // allowed() { // return true // }, // requires: "", // effect() { // document.getElementById("choose-grid").classList.add("flipX"); // }, // remove() {} // }, { name: "random", link: `random`, delay: 333, descriptionFunction() { const delay = 333 const loop = () => { if ((simulation.isChoosing) && m.alive && !build.isExperimentSelection) { const dmg = Math.floor(27 * Math.random()) * 0.01 this.text = `+${(1 + dmg).toFixed(2).padStart(2, '0')}x damage` this.damage = 1 + dmg if (document.getElementById(`damage-JUNK-id${this.id}`)) document.getElementById(`damage-JUNK-id${this.id}`).innerHTML = this.text setTimeout(() => { loop() }, delay); } } setTimeout(() => { loop() }, delay); this.id++ return `${this.text}` }, maxCount: 3, count: 0, frequency: 1, isJunk: true, allowed() { return !build.isExperimentSelection }, requires: "NOT EXPERIMENT MODE", damage: 0, effect() { tech.damage *= this.damage }, remove() { if (this.count && m.alive) tech.damage /= this.damage } }, { name: "boost", maxCount: 1, count: 0, frequency: 0, isJunk: true, isInstant: true, allowed() { return !build.isExperimentSelection }, requires: "NOT EXPERIMENT MODE", effect() { powerUps.spawnDelay("boost", this.spawnCount) }, remove() { }, id: 0, text: "", delay: 100, spawnCount: 0, descriptionFunction() { let count = 9999 * Math.random() const loop = () => { if ((simulation.isChoosing) && m.alive && !build.isExperimentSelection) { //&& (!simulation.isChoosing || this.count === 0) //simulation.paused || count += 4.5 const waves = 2 * Math.sin(count * 0.0133) + Math.sin(count * 0.013) + 0.5 * Math.sin(count * 0.031) + 0.33 * Math.sin(count * 0.03) this.spawnCount = Math.floor(100 * Math.abs(waves)) this.text = `spawn ${this.spawnCount.toLocaleString(undefined, { minimumIntegerDigits: 3 })} ${powerUps.orb.boost(1)}
that give ${(1 + powerUps.boost.damage).toFixed(2)}x damage for ${(powerUps.boost.duration / 60).toFixed(0)} seconds` if (document.getElementById(`boost-JUNK-id${this.id}`)) document.getElementById(`boost-JUNK-id${this.id}`).innerHTML = this.text setTimeout(() => { loop() }, this.delay); } } setTimeout(() => { loop() }, this.delay); this.id++ return `${this.text}` }, }, { name: "placebo", description: "7.77x damage", maxCount: 1, count: 0, frequency: 0, isJunk: true, allowed: () => true, requires: "", effect() { if (Math.random() < 0.07) tech.damage *= 7.77 }, remove() { } }, { name: "universal healthcare", description: "make your damage negative", maxCount: 1, count: 0, frequency: 0, isJunk: true, allowed: () => true, requires: "", effect() { tech.damage *= -1 }, remove() { } }, { name: "planned obsolescence", description: "build 100 scrap bots
bots might last for 30 seconds", maxCount: 1, count: 0, frequency: 0, isJunk: true, allowed: () => true, requires: "", effect() { for (let i = 0; i < 100; i++) { b.randomBot(m.pos, false) bullet[bullet.length - 1].endCycle = simulation.cycle + 800 + 1000 * Math.random() //15 seconds } }, remove() { } }, // { // name: "synchrotron", // descriptionFunction() { // return `power ups change into a different flavor after a boss dies` // }, // maxCount: 3, // count: 0, // frequency: 1, // frequencyDefault: 1, // allowed: () => true, // requires: "", // effect() { // }, // remove() { // } // }, { name: "return", description: "return to the start of the game", maxCount: 1, count: 0, frequency: 0, isJunk: true, isInstant: true, allowed: () => true, requires: "", effect() { 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, isInstant: true, allowed: () => 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, isInstant: true, allowed: () => 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 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: "reinforcement learning", description: "10x current tech frequency", maxCount: 1, count: 0, frequency: 1, isJunk: true, 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() { if (this.count) { 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: "startle response", description: `if a threat is nearby, activate a ${powerUps.orb.boost(1)}
and lock your mouse until you press escape`, maxCount: 1, count: 0, frequency: 0, isJunk: true, isInstant: true, allowed: () => true, requires: "", effect() { // tech.damage *= 1.33 setInterval(() => { if (powerUps.boost.endCycle < m.cycle && !simulation.paused && m.alive) { for (let i = 0; i < mob.length; i++) { if (mob[i].distanceToPlayer2() < 400000) { //650 canvas.requestPointerLock(); powerUps.boost.effect(); break } } } }, 2000); }, 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, isInstant: true, allowed: () => 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, // isInstant: 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 JUNKtech for the price of 1!", maxCount: 1, count: 0, frequency: 0, isJunk: true, isInstant: true, allowed: () => true, requires: "", effect() { for (let i = 0; i < 3; i++) { const list = [] for (let i = 0; i < tech.tech.length; i++) { if (tech.tech[i].isJunk) list.push(tech.tech[i].name) } let name = list[Math.floor(Math.random() * list.length)] simulation.makeTextLog(`tech.giveTech("${name}")`); tech.giveTech(name) } }, remove() { } }, // { // name: "hi", // description: `spawn to seed 616 `, // maxCount: 1, // count: 0, // frequency: 0, // isInstant: 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, isInstant: true, allowed: () => 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, isInstant: true, allowed: () => 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: "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: () => true, requires: "", effect() { tech.isBrainstorm = true tech.isBrainstormActive = false tech.brainStormDelay = 500 //show each option for 0.5 seconds }, 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, isInstant: 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, // isInstant: 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, // isInstant: 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, isInstant: true, isJunk: true, allowed() { return true }, requires: "", effect() { const index = Math.min(level.levels.length - 1, level.onLevel) level.levels.splice(index, 0, level.levels[index]); }, 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 = mobs.drawDefault } }, { 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 = mobs.drawDefault } }, // 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, isInstant: 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, isInstant: 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", descriptionFunction() { return `spawn ${powerUps.orb.heal(20)}
but hide your health bar` }, maxCount: 1, count: 0, frequency: 0, isInstant: true, isJunk: true, allowed() { return true }, requires: "", effect() { document.getElementById("health").style.display = "none" document.getElementById("health-bg").style.display = "none" document.getElementById("defense-bar").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, isInstant: 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: "what the block?", description: "throwing a block throws you instead", maxCount: 1, count: 0, frequency: 0, isInstant: true, isJunk: true, allowed() { return m.fieldMode !== 8 && m.fieldMode !== 9 && !tech.isTokamak }, requires: "not pilot wave, tokamak, wormhole", effect() { }, remove() { m.throwBlock = m.throwBlockDefault } }, { name: "spinor", description: "the direction you aim is determined by your position", maxCount: 1, count: 0, frequency: 0, isInstant: 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: "p-zombie", description: "set your health to 1
all mobs, not bosses, die and resurrect as zombies", maxCount: 1, count: 0, frequency: 0, isInstant: true, isJunk: true, allowed() { return true }, requires: "", effect() { m.health = 0.01 //set health to 1 m.displayHealth(); for (let i = mob.length - 1; i > -1; i--) { //replace mobs with zombies if (mob[i].isDropPowerUp && !mob[i].isBoss && mob[i].alive) { mob[i].isSoonZombie = true mob[i].death() } } }, remove() { } }, { name: "decomposers", description: "after they die mobs leave behind spawns", maxCount: 1, count: 0, frequency: 0, isInstant: 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, isInstant: 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, // isInstant: 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, isInstant: 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: "100x frequency
of one random tech", maxCount: 1, count: 0, frequency: 0, isInstant: 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, isInstant: 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: "quantum leap", description: "become an alternate version of yourself
every 20 seconds", maxCount: 1, count: 0, frequency: 0, isInstant: 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, isInstant: 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, isInstant: 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, isInstant: 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, isInstant: 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" document.getElementById("defense-bar").style.left = "86px" document.getElementById("damage-bar").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, isInstant: 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 JUNKtech to zero", maxCount: 1, count: 0, frequency: 0, isInstant: true, isJunk: true, allowed() { return true }, requires: "", effect() { tech.junkChance = 0; }, remove() { } }, // { // name: "lubrication", // description: "reduce block density and friction for this level", // maxCount: 9, // count: 0, // frequency: 0, // isInstant: 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, isInstant: 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: "flatland", // description: "map blocks line of sight", // maxCount: 1, // count: 0, // frequency: 0, // isInstant: true, // isJunk: true, // allowed() { return true }, // requires: "", // effect() { // simulation.draw.lineOfSightPrecalculation() //required precalculation for line of sight // simulation.draw.drawMapPath = simulation.draw.drawMapSight // simulation.ephemera.push({ // name: "LoS", count: 0, do() { // const pos = m.pos // const radius = 3000 // if (!simulation.isTimeSkipping) { // const vertices = simulation.sight.circleLoS(pos, radius); // if (vertices.length) { // ctx.beginPath(); // ctx.moveTo(vertices[0].x, vertices[0].y); // for (var i = 1; i < vertices.length; i++) { // var currentDistance = Math.sqrt((vertices[i - 1].x - pos.x) ** 2 + (vertices[i - 1].y - pos.y) ** 2); // var newDistance = Math.sqrt((vertices[i].x - pos.x) ** 2 + (vertices[i].y - pos.y) ** 2); // if (Math.abs(currentDistance - radius) < 1 && Math.abs(newDistance - radius) < 1) { // const currentAngle = Math.atan2(vertices[i - 1].y - pos.y, vertices[i - 1].x - pos.x); // const newAngle = Math.atan2(vertices[i].y - pos.y, vertices[i].x - pos.x); // ctx.arc(pos.x, pos.y, radius, currentAngle, newAngle); // } else { // ctx.lineTo(vertices[i].x, vertices[i].y) // } // } // newDistance = Math.sqrt((vertices[0].x - pos.x) ** 2 + (vertices[0].y - pos.y) ** 2); // currentDistance = Math.sqrt((vertices[vertices.length - 1].x - pos.x) ** 2 + (vertices[vertices.length - 1].y - pos.y) ** 2); // if (Math.abs(currentDistance - radius) < 1 && Math.abs(newDistance - radius) < 1) { // const currentAngle = Math.atan2(vertices[vertices.length - 1].y - pos.y, vertices[vertices.length - 1].x - pos.x); // const newAngle = Math.atan2(vertices[0].y - pos.y, vertices[0].x - pos.x); // ctx.arc(pos.x, pos.y, radius, currentAngle, newAngle); // } else { // ctx.lineTo(vertices[0].x, vertices[0].y) // } // //stroke the map, so it looks different form the line of sight // ctx.strokeStyle = "#234"; // ctx.lineWidth = 9; // ctx.stroke(simulation.draw.mapPath); //this has a pretty large impact on performance, maybe 5% worse performance // ctx.globalCompositeOperation = "destination-in"; // ctx.fillStyle = "#000"; // ctx.fill(); // ctx.globalCompositeOperation = "source-over"; // // also see the map // // ctx.fill(simulation.draw.mapPath); // // ctx.fillStyle = "#000"; // ctx.clip(); // } // } // }, // }) // }, // remove() { } // }, { name: "umbra", description: "produce a blue glow around everything
and probably some simulation lag", maxCount: 1, count: 0, frequency: 0, isInstant: 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, isInstant: true, isJunk: true, allowed() { return true }, requires: "", effect() { ctx.globalCompositeOperation = "lighter"; }, remove() { } }, { name: "rewind", description: "every 10 seconds rewind 2 seconds", maxCount: 9, count: 0, frequency: 0, isInstant: 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, isInstant: 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, isInstant: true, isJunk: true, allowed() { return true }, requires: "", effect() { for (let i = 0, len = 40; i < len; i++) { setTimeout(() => { m.energy -= 1 / len 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())); }, i * 100); } }, remove() { } }, { name: "level.nextLevel()", description: "advance to the next level", maxCount: 9, count: 0, frequency: 0, isInstant: true, isJunk: true, allowed() { return true }, requires: "", effect() { level.nextLevel(); }, remove() { } }, { name: "reincarnation", description: "kill all mobs and spawn new ones
(also spawn a few extra mobs for fun)", maxCount: 3, count: 0, frequency: 0, isInstant: true, isJunk: true, allowed() { return true }, requires: "", effect() { spawn.setSpawnList(); spawn.setSpawnList(); for (let i = 0, len = mob.length; i < len; i++) { if (mob[i].alive && !mob[i].shield && !mob[i].isBadTarget) { const pick = spawn.pickList[Math.floor(Math.random() * spawn.pickList.length)]; spawn[pick](mob[i].position.x, mob[i].position.y); if (Math.random() < 0.5) spawn[pick](mob[i].position.x, mob[i].position.y); mob[i].death(); } } }, remove() { } }, { name: "expert system", description: "spawn a tech power up
+64% JUNKtech chance", maxCount: 9, count: 0, frequency: 0, isInstant: 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, isInstant: 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, isInstant: 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, isInstant: 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: "wall jump", description: "no knees or toes are drawn on the player
you can wall climb though", maxCount: 1, count: 0, frequency: 0, isJunk: true, isInstant: true, allowed() { return !m.isShipMode }, requires: "", effect() { m.skin.stubs() jumpSensor.vertices[0].x += -22 jumpSensor.vertices[3].x += -22 jumpSensor.vertices[1].x += 22 jumpSensor.vertices[2].x += 22 }, remove() { } }, { name: "Sleipnir", description: "grow more legs", maxCount: 1, count: 0, frequency: 0, isJunk: true, allowed() { return !m.isShipMode }, requires: "", effect() { m.skin.Sleipnir() }, remove() { if (this.count) m.resetSkin(); } }, { name: "diegesis", description: "indicate fire cooldown
through a rotation of your head", maxCount: 1, count: 0, frequency: 0, isJunk: true, allowed() { return !m.isShipMode }, requires: "", effect() { m.skin.diegesis() }, remove() { if (this.count) m.resetSkin(); } }, { name: "🐱", description: "🐈", maxCount: 1, count: 0, frequency: 0, isJunk: true, allowed() { return !m.isShipMode }, requires: "", effect() { m.skin.cat(); }, remove() { if (this.count) m.resetSkin(); } }, { name: "n-gone", description: "become invisible to yourself
mobs can still see you", maxCount: 1, count: 0, frequency: 0, isJunk: true, allowed() { return true }, requires: "", effect() { m.draw = () => { } }, remove() { if (this.count) m.resetSkin(); } }, { name: "pareidolia", description: "don't", maxCount: 1, count: 0, frequency: 0, isInstant: true, isJunk: true, allowed() { return !m.isShipMode }, requires: "", effect() { m.skin.pareidolia() }, remove() { if (this.count) m.resetSkin(); } }, { 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, isInstant: 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: "prism", description: "you cycle through different colors", maxCount: 1, count: 0, frequency: 0, isInstant: 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: "microtransactions", // description: `when you choose a tech you can
use ${powerUps.orb.research(1)} to buy a free in game skin`, // maxCount: 1, // count: 0, // frequency: 0, // isJunk: true, // allowed() { // return true // }, // requires: "", // effect() { // tech.isMicroTransactions = true // }, // remove() { // tech.isMicroTransactions = false // } // }, { name: "ship", description: "fly around with no legs", maxCount: 1, count: 0, frequency: 0, isInstant: true, isJunk: true, allowed() { return !m.isShipMode && !m.isAltSkin && m.fieldUpgrades[m.fieldMode].name !== "negative mass" }, requires: "", effect() { m.isAltSkin = true m.shipMode() //unlock relativistic rotation for (let i = 0; i < tech.tech.length; i++) { if (tech.tech[i].name === "relativistic rotation") tech.tech[i].frequency = 10 } }, remove() { } }, { name: "circular symmetry", description: "turning the ship rotates the universe instead
2x damage", maxCount: 1, count: 0, frequency: 0, isInstant: true, isJunk: true, allowed() { return m.isShipMode }, requires: "", effect() { tech.damage *= 3 m.look = () => { // const scale = 0; 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; ctx.restore(); ctx.save(); ctx.translate(canvas.width2, canvas.height2); //center ctx.rotate(-m.angle) ctx.translate(-canvas.width2, -canvas.height2); //center } }, remove() { } }, { name: "assimilation", description: "all your bots are converted to the same random model", maxCount: 1, count: 0, frequency: 0, isBotTech: true, isInstant: 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.soundBotCount = 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.soundBot(); tech.soundBotCount++; }, () => { 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: "stun", description: "stun all mobs for up to 8 seconds", maxCount: 9, count: 0, frequency: 0, isInstant: true, isJunk: true, allowed() { return true }, requires: "", effect() { for (let i = 0; i < mob.length; i++) mobs.statusStun(mob[i], 480) }, remove() { } }, { name: "translucent", description: "spawn 3 gun power ups
your bullets and bots are transparent", maxCount: 1, count: 0, frequency: 0, isInstant: true, isJunk: true, allowed() { return true }, requires: "", effect() { for (let i = 0; i < 3; 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 b.bulletDraw = () => { }; //make bullets invisible }, remove() { } }, { name: "difficulty", description: "spawn a power up that lets you
adjust the simulation difficulty parameters", maxCount: 1, count: 0, frequency: 0, isInstant: true, isJunk: true, allowed() { return (level.levelsCleared < 5) }, requires: "before level 5", effect() { powerUps.spawn(m.pos.x, m.pos.y, "difficulty"); }, remove() { } }, { name: "re-research", description: `eject all your ${powerUps.orb.research(1)}`, maxCount: 9, count: 0, frequency: 0, isInstant: true, isJunk: true, allowed() { return powerUps.research.count > 3 }, requires: "at least 4 research", effect() { powerUps.spawnDelay("research", powerUps.research.count); 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, isInstant: 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: "apomixis", description: `spawn 11 bosses`, maxCount: 1, count: 0, frequency: 2, frequencyDefault: 2, isInstant: true, isJunk: true, allowed() { return tech.duplicationChance() > 0.99 }, requires: "duplication chance above 99%", effect() { const range = 1300 for (let i = 0, len = 9; i < len; i++) { const angle = 2 * Math.PI * i / len spawn.randomLevelBoss(m.pos.x + range * Math.cos(angle), m.pos.y + range * Math.sin(angle), spawn.nonCollideBossList); } spawn.historyBoss(0, 0) spawn.pulsarBoss(level.exit.x, level.exit.y, 70, true) spawn.blockBoss(level.enter.x, level.enter.y) }, remove() { } }, { name: "mobs!", descriptionFunction() { if (this.mobType === "") this.mobType = spawn.fullPickList[Math.floor(Math.random() * spawn.fullPickList.length)] return `spawn 20 ${this.mobType} mobs` }, maxCount: 1, count: 0, frequency: 0, isInstant: true, isJunk: true, allowed() { return true }, requires: "", mobType: "", effect() { if (this.mobType === "") this.mobType = spawn.fullPickList[Math.floor(Math.random() * spawn.fullPickList.length)] for (let i = 0; i < 20; i++) { spawn[this.mobType](m.pos.x, m.pos.y - 700) } simulation.makeTextLog(`spawn.${this.mobType}(x,y)`) }, remove() { } }, { name: "black hole cluster", description: `spawn 30 nearby black holes`, maxCount: 1, count: 0, frequency: 0, isInstant: 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: "rule 30", maxCount: 1, count: 0, frequency: 0, isJunk: true, allowed() { return !build.isExperimentSelection }, requires: "NOT EXPERIMENT MODE", effect() { }, remove() { }, state: [ [false, false, false, Math.random() > 0.8, false, false, false, Math.random() > 0.8, false, false, false, false, false, false, false, false, false, true, false, false, false, Math.random() > 0.8, false, false, false, Math.random() > 0.8, false, false, false, false, Math.random() > 0.8, false, Math.random() > 0.8, false, false, false, Math.random() > 0.8, 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, researchSpawned: 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.researchSpawned < 12 && !(this.state.length % 10)) { this.researchSpawned++ powerUps.spawn(m.pos.x - 50 + 100 * (Math.random() - 0.5), m.pos.y + 100 * (Math.random() - 0.5), "research"); } setTimeout(() => { loop() }, 300 + 5 * this.state.length); } } setTimeout(() => { loop() }, 300); this.id++ return `${this.outputText()}` }, outputText() { let text = "
"
            for (let j = 0; j < this.state.length; j++) {
                // text += "

" text += "

" for (let i = 0; i < this.state[j].length; i++) { if (this.state[j][i]) { text += "■" //"☻" //"⬛" //"█" //"■" } else { text += " " //"□" //"☺" //"⬜" //"    " //"□" } } text += "

" } text += "
" return text }, }, { name: "rule 90", maxCount: 1, count: 0, frequency: 0, isJunk: true, allowed() { return !build.isExperimentSelection }, requires: "NOT EXPERIMENT MODE", effect() { }, remove() { }, state: [ [false, false, false, Math.random() > 0.8, false, false, false, Math.random() > 0.8, false, false, false, false, false, false, false, false, false, true, true, false, false, false, Math.random() > 0.8, false, false, false, Math.random() > 0.8, false, false, false, false, Math.random() > 0.8, false, Math.random() > 0.8, false, false, false, Math.random() > 0.8, false, false, false, false, false, false, false, false] ], rule(state, a, b, c) { //90 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: 90, researchSpawned: 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.researchSpawned < 12 && !(this.state.length % 10)) { this.researchSpawned++ powerUps.spawn(m.pos.x - 50 + 100 * (Math.random() - 0.5), m.pos.y + 100 * (Math.random() - 0.5), "research"); } setTimeout(() => { loop() }, 300 + 5 * this.state.length); } } setTimeout(() => { loop() }, 300); this.id++ return `${this.outputText()}` }, outputText() { let text = "
"
            for (let j = 0; j < this.state.length; j++) {
                // text += "

" text += "

" for (let i = 0; i < this.state[j].length; i++) { if (this.state[j][i]) { text += "■" //"☻" //"⬛" //"█" //"■" } else { text += " " //"□" //"☺" //"⬜" //"    " //"□" } } 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, isInstant: 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: "beforeunload", description: "75% of the time when you attempt to exit n-gon
you are prompted to cancel or continue.
Each time you cancel gain 1.25x damage.", maxCount: 1, count: 0, frequency: 1, isJunk: true, allowed() { return tech.totalCount > 9 }, requires: "at least 10 tech", effect() { tech.isExitPrompt = true addEventListener('beforeunload', beforeUnloadEventListener); }, remove() { tech.isExitPrompt = false if (this.count > 0) removeEventListener('beforeunload', beforeUnloadEventListener); } }, { 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, isInstant: 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 JUNKtech in experiment mode
this effect is stored for future visits", maxCount: 1, count: 0, frequency: 0, frequencyDefault: 0, isJunk: true, isInstant: 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, isInstant: true, allowed: () => 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: `this
 `, maxCount: 1, count: 0, frequency: 2, frequencyDefault: 2, isLore: true, // isExperimentHide: true, allowed() { return !build.isExperimentSelection }, requires: "NOT EXPERIMENT MODE", effect() { if (localSettings.loreCount > lore.conversation.length - 1) { //reward for people done with lore chapters (or on the final chapter) for (let i = mob.length - 1; i > -1; i--) { //replace mobs with starters if (!mob[i].isBoss && mob[i].isDropPowerUp && mob[i].alive) { spawn.starter(mob[i].position.x, mob[i].position.y) mob[i].leaveBody = false mob[i].isDropPowerUp = false mob[i].death() //spawn a random power up // if (Math.random() < 1 / 5) { // powerUps.spawn(mob[i].position.x, mob[i].position.y, "research") // } else if (Math.random() < 1 / 4) { powerUps.spawn(mob[i].position.x, mob[i].position.y, "ammo") } else if (Math.random() < 1 / 3) { powerUps.spawn(mob[i].position.x, mob[i].position.y, "heal") } else if (Math.random() < 1 / 2) { powerUps.spawn(mob[i].position.x, mob[i].position.y, "boost") } else { powerUps.spawn(mob[i].position.x, mob[i].position.y, "coupling") } } } } 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, // isInstant: 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, healSpawn: null, crouchAmmoCount: null, bulletsLastLonger: null, isImmortal: null, sporesOnDeath: null, isImmuneExplosion: null, isExplodeMob: null, isDroneOnDamage: null, isAcidDmg: null, isAnnihilation: null, largerHeals: null, isCrit: null, isLowHealthDmg: null, isLowHealthDefense: null, isLowHealthFireRate: null, isFarAwayDmg: null, isFirstDer: null, isMassEnergy: null, extraChoices: null, laserBotCount: null, dynamoBotCount: null, nailBotCount: null, foamBotCount: null, soundBotCount: null, boomBotCount: null, plasmaBotCount: null, missileBotCount: null, orbitBotCount: null, blockDmg: null, isBlockRadiation: null, isPiezo: null, isFastDrones: null, oneSuperBall: null, laserReflections: null, laserDamage: null, isAmmoFromHealth: null, mobSpawnWithHealth: null, isEnergyRecovery: null, isHealthRecovery: null, isEnergyLoss: null, isDeathAvoid: null, isDeathAvoidedThisLevel: null, isPlasmaRange: null, isFreezeMobs: null, isIceCrystals: null, blockDamage: null, isBlockStun: null, isStunField: null, isHarmDamage: null, isVacuumBomb: null, renormalization: null, fragments: null, energyDamage: null, botSpawner: null, isBotSpawnerReset: null, isSporeFollow: null, isNailRadiation: null, isEnergyHealth: null, isStun: null, restDamage: null, isRPG: null, missileCount: null, isDeterminism: null, isSuperDeterminism: null, isHarmReduce: null, nailsDeathMob: null, isSlowFPS: null, isNeutronStun: null, isAnsatz: null, isDamageFromBulletCount: null, laserDrain: null, isNailShot: null, slowFire: null, fastTime: null, isFastRadiation: null, isAmmoForGun: null, isRapidPulse: 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, isSoundBotUpgrade: 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, isWormHoleBullets: null, isWideLaser: null, wideLaser: null, isPulseLaser: null, isRadioactive: null, radioactiveDamage: null, isRailEnergy: null, isMineSentry: null, isIncendiary: null, overfillDrain: null, isNeutronSlow: null, // isRailAreaDamage: null, historyLaser: null, isSpeedHarm: null, isSpeedDamage: null, speedAdded: null, isTimeSkip: null, isCancelDuplication: null, duplication: null, isCancelRerolls: null, isCancelTech: null, cancelTechCount: null, isBotDamage: null, isBanish: null, isMaxEnergyTech: null, isLowEnergyDamage: null, isRewindBot: null, isRewindGrenade: null, isExtruder: null, isEndLevelPowerUp: null, isMissileBig: null, isMissileBiggest: null, isLaserMine: null, isFoamMine: null, isAmmoFoamSize: null, isIceIX: null, isDupDamage: null, isDupEnergy: null, isFireRateForGuns: null, cyclicImmunity: null, isTechDamage: null, isRestHarm: null, isFireMoveLock: null, isRivets: null, isNeedles: null, isExplodeRadio: null, isPauseSwitchField: null, isPauseEjectTech: null, pauseEjectTech: null, isShieldPierce: null, isDuplicateMobs: null, isDynamoBotUpgrade: null, isBlockPowerUps: null, isDamageAfterKillNoRegen: null, isHarmReduceNoKill: null, isSwitchReality: null, isResearchReality: null, isAnthropicDamage: null, isMetaAnalysis: null, isFoamAttract: null, droneCycleReduction: null, droneEnergyReduction: null, isHalfHeals: 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, isMoveMACHO: null, isSneakAttack: null, isFallingDamage: null, harmonics: null, isStandingWaveExpand: null, isTokamak: null, deflectEnergy: null, superBallDelay: null, isBlockExplode: null, isOverHeal: null, isDroneRadioactive: null, droneRadioDamage: null, isDroneTeleport: null, isDroneFastLook: null, isBulletTeleport: null, isResearchBoss: null, isJunkResearch: 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, isSporeFlea: 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, cloakDuplication: null, extruderRange: null, isForeverDrones: null, nailRecoil: null, baseJumpForce: null, baseFx: null, isNeutronium: null, isFreeWormHole: null, isRewindField: null, isCrouchRegen: null, isAxion: null, isDarkStar: null, isWormholeMapIgnore: null, isLessDamageReduction: null, needleTunnel: null, isBrainstorm: null, isBrainstormActive: null, brainStormDelay: null, wormSize: null, extraSuperBalls: null, isTimeCrystals: null, isGroundState: null, isRailGun: null, isDronesTravel: null, isTechDebt: null, isPlasmaBall: null, plasmaDischarge: null, missileFireCD: null, isBotField: null, isFoamBall: null, isNoDraftPause: null, isFoamPressure: null, foamDamage: null, isClusterExplode: null, isCircleExplode: null, isPetalsExplode: null, deathSkipTime: null, isIceMaxHealthLoss: null, isIceKill: null, isCritKill: null, isQuantumEraser: null, isPhononBlock: null, isPhononWave: null, isLaserLens: null, laserCrit: null, isSporeColony: null, isExtraBotOption: null, isLastHitDamage: null, isCloakHealLastHit: null, isRicochet: null, isCancelCouple: null, isCouplingPowerUps: null, isBoostPowerUps: null, isBoostReplaceAmmo: null, isInfiniteWaveAmmo: null, isJunkDNA: null, buffedGun: 0, isGunChoice: null, railChargeRate: null, isSuperHarm: null, isZombieMobs: null, isSuperMine: null, sentryAmmo: null, collidePowerUps: null, isDilate: null, isDiaphragm: null, isNoGroundDamage: null, isSuperBounce: null, isDivisor: null, isFoamCavitation: null, isHealAttract: null, isLaserField: null, isHealBrake: null, isMassProduction: null, isPrinter: null, isHookDefense: null, hookNails: null, isHarpoonDefense: null, isReel: null, harpoonPowerUpCycle: null, isHarpoonFullHealth: null, isMobFullHealthCloak: null, isMobLowHealth: null, isDamageCooldown: null, isDamageCooldownTime: null, isPowerUpDamage: null, isExitPrompt: null, isResearchDamage: null, interestRate: null, isImmunityDamage: null, isMobDeathImmunity: null, isBlockJump: null, }