mantisBoss flashes for a second before it drops invulnerability
removed parasitism - it's too similar to invulnerability tech
invariant no longer drains energy while wormhole time is paused
  added 1 research cost
added secret combo to change molecular assembler mode

bug fixes
  issue with constraint: "mob death heals mobs"
    mob health was becoming NaN, this was infecting other values like player energy
  entering a seed in settings wasn't giving the same results as a randomly generated seeds
    also removed some random code that was using seeded shuffle, but didn't need to
      converted it to non seeded random shuffle with .sort(() => Math.random() - 0.5);
This commit is contained in:
landgreen
2025-01-18 17:00:10 -08:00
parent 1040d1ff7e
commit c9a5ab91b8
11 changed files with 2347 additions and 2363 deletions

View File

@@ -3160,7 +3160,7 @@ const b = {
if (
Vector.magnitudeSquared(Vector.sub(this.position, powerUp[i].position)) < 20000 &&
!(
(m.health > 0.93 * m.maxHealth && !tech.isDroneGrab && powerUp[i].name === "heal") ||
(m.health > 0.94 * m.maxHealth && !tech.isOverHeal && !tech.isDroneGrab && powerUp[i].name === "heal") ||
(tech.isSuperDeterminism && powerUp[i].name === "field") ||
((tech.isEnergyNoAmmo || b.inventory.length === 0) && powerUp[i].name === "ammo")
)
@@ -3192,7 +3192,7 @@ const b = {
let closeDist = Infinity;
for (let i = 0, len = powerUp.length; i < len; ++i) {
if (!(
(m.health > 0.93 * m.maxHealth && !tech.isDroneGrab && powerUp[i].name === "heal") ||
(m.health > 0.94 * m.maxHealth && !tech.isOverHeal && !tech.isDroneGrab && powerUp[i].name === "heal") ||
(tech.isSuperDeterminism && powerUp[i].name === "field") ||
((tech.isEnergyNoAmmo || b.inventory.length === 0) && powerUp[i].name === "ammo")
)) {

View File

@@ -28,11 +28,11 @@ Math.seededRandom = function (min = 0, max = 1) { // in order to work 'Math.seed
// console.log(Math.seed)
function shuffle(array) {
function seededShuffle(array) {
var currentIndex = array.length,
temporaryValue,
randomIndex;
// While there remain elements to shuffle...
// While there remain elements
while (0 !== currentIndex) {
// Pick a remaining element...
// randomIndex = Math.floor(Math.random() * currentIndex);
@@ -542,7 +542,7 @@ ${simulation.difficultyMode > 4 ? `<details id="constraints-details" style="padd
<details id = "console-log-details" style="padding: 0 8px;">
<summary>console log</summary>
<div class="pause-details">
<div class="pause-grid-module" style="background-color: rgba(255,255,255,0.3);font-size: 0.8em;">${document.getElementById("text-log").innerHTML}</div>
<div class="pause-grid-module" style=" background-color: #e2e9ec;font-size: 0.8em;">${document.getElementById("text-log").innerHTML}</div>
</div>
</details>
</div>`
@@ -1205,6 +1205,7 @@ const input = {
left: false,
right: false,
isPauseKeyReady: true,
// lastDown: null,
key: {
fire: "KeyF",
field: "Space",
@@ -1379,6 +1380,7 @@ window.addEventListener("keyup", function (event) {
});
window.addEventListener("keydown", function (event) {
// input.lastDown = event.code
// console.log(event.code)
switch (event.code) {
case input.key.right:

View File

@@ -22,7 +22,7 @@ const level = {
// simulation.isHorizontalFlipped = true
// spawn.setSpawnList(); //picks a couple mobs types for a themed random mob spawns
// spawn.setSpawnList(); //picks a couple mobs types for a themed random mob spawns
// level.levelsCleared = 10
// level.levelsCleared = 9
// level.updateDifficulty()
// tech.giveTech("performance")
// m.maxHealth = m.health = 100000000
@@ -33,7 +33,7 @@ const level = {
// tech.tech[297].frequency = 100
// tech.addJunkTechToPool(0.5)
// m.couplingChange(10)
// m.setField("negative mass") //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
// m.setField("wormhole") //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
// m.energy = 0
// powerUps.research.count = 3
// tech.isHookWire = true
@@ -58,7 +58,7 @@ const level = {
// requestAnimationFrame(() => { for (let i = 0; i < 1; i++) tech.giveTech("interest") });
// for (let i = 0; i < 1; i++) tech.giveTech("interest")
// m.lastKillCycle = m.cycle
// for (let i = 0; i < 7; i++) powerUps.directSpawn(450, -50, "tech");
// for (let i = 0; i < 7; i++) powerUps.directSpawn(450, -50, "field");
// for (let i = 0; i < 7; i++) powerUps.directSpawn(m.pos.x + 200, m.pos.y - 250, "research", false);
// spawn.bodyRect(575, -700, 150, 150); //block mob line of site on testing
// level.testing();
@@ -810,14 +810,14 @@ const level = {
if (document.getElementById("seed").value) { //check for player entered seed in settings
Math.initialSeed = String(document.getElementById("seed").value)
Math.seed = Math.abs(Math.hash(Math.initialSeed)) //update randomizer seed in case the player changed it
}
Math.seed = Math.abs(Math.hash(Math.initialSeed)) //update randomizer seed
if (simulation.isTraining) {
simulation.isHorizontalFlipped = false
level.levels = level.trainingLevels.slice(0) //copy array, not by just by assignment
if (simulation.isCommunityMaps) level.trainingLevels.push("diamagnetism")
} else { //add remove and shuffle levels for the normal game (not training levels)
} else {
level.levels = level.playableLevels.slice(0) //copy array, not by just by assignment
if (simulation.isCommunityMaps) {
level.levels = level.levels.concat(level.communityLevels)
@@ -825,7 +825,7 @@ const level = {
} else {
simulation.isHorizontalFlipped = (Math.seededRandom() < 0.5) ? true : false //if true, some maps are flipped horizontally
}
level.levels = shuffle(level.levels); //shuffles order of maps with seeded random
level.levels = seededShuffle(level.levels); //shuffles order of maps with seeded random
level.levels.length = 9 //remove any extra levels past 9
pick = ["interferometer", "factory", "reservoir"]
level.levels.splice(Math.floor(Math.seededRandom(level.levels.length * 0.6, level.levels.length)), 0, pick[Math.floor(Math.random() * pick.length)]); //add level to the back half of the randomized levels list
@@ -3533,7 +3533,7 @@ const level = {
const stationList = [] //use to randomize station order
for (let i = 1, totalNumberOfStations = 10; i < totalNumberOfStations; ++i) stationList.push(i) //!!!! update station number when you add a new station
shuffle(stationList);
stationList.sort(() => Math.random() - 0.5);
stationList.splice(0, 3); //remove some stations to keep it to 4 stations
stationList.unshift(0) //add index zero to the front of the array
@@ -6690,14 +6690,14 @@ const level = {
//3x2: 4 short rooms (3000x1500), 1 double tall room (3000x3000)
//rooms
let rooms = ["exit", "loot", "enter", "empty"]
rooms = shuffle(rooms); //shuffles array order
rooms.sort(() => Math.random() - 0.5);
//look... you and I both know there is a better way to do this, but it works so I'm gonna focus on other things
while ( //makes sure that the exit and entrance aren't both on the same floor
(rooms[0] === "enter" && rooms[2] === "exit") ||
(rooms[2] === "enter" && rooms[0] === "exit") ||
(rooms[1] === "enter" && rooms[3] === "exit") ||
(rooms[3] === "enter" && rooms[1] === "exit")
) rooms = shuffle(rooms); //shuffles array order
) rooms.sort(() => Math.random() - 0.5);
for (let i = 0; i < rooms.length; i++) {
if (rooms[i] === "enter") rooms[i] = enter
if (rooms[i] === "exit") rooms[i] = exit
@@ -6764,7 +6764,7 @@ const level = {
rooms[3]()
},
]
columns = shuffle(columns) //********************************* RUN THIS LINE IN THE FINAL VERSION ***************************************
columns.sort(() => Math.random() - 0.5);
for (let i = 0; i < 3; i++) {
if (i === 0) {
isDoorLeft = false
@@ -7054,7 +7054,7 @@ const level = {
};
powerUps.spawnStartingPowerUps(1875, -3075);
const powerUpPos = shuffle([{ //no debris on this level but 2 random spawn instead
const powerUpPos = [{ //no debris on this level but 2 random spawn instead
x: -150,
y: -1775
}, {
@@ -7066,7 +7066,8 @@ const level = {
}, {
x: 1325,
y: -150
}]);
}];
powerUpPos.sort(() => Math.random() - 0.5);
powerUps.chooseRandomPowerUp(powerUpPos[0].x, powerUpPos[0].y);
powerUps.chooseRandomPowerUp(powerUpPos[1].x, powerUpPos[1].y);
//outer wall
@@ -8858,7 +8859,8 @@ const level = {
// spawn.bodyRect(312, -100, 25, 100);
spawn.bodyRect(1450, -300, 150, 50);
const xPos = shuffle([600, 1250, 2000]);
const xPos = [600, 1250, 2000];
xPos.sort(() => Math.random() - 0.5);
spawn.mapRect(xPos[0], -200, 300, 100);
spawn.mapRect(xPos[1], -250, 300, 300);
spawn.mapRect(xPos[2], -150, 300, 200);

View File

@@ -1069,7 +1069,11 @@ const mobs = {
if (tech.isFarAwayDmg) dmg *= 1 + Math.sqrt(Math.max(500, Math.min(3000, this.distanceToPlayer())) - 500) * 0.0067 //up to 33% dmg at max range of 3000
dmg *= this.damageReduction
//energy and heal drain should be calculated after damage boosts
if (tech.energySiphon && dmg !== Infinity && this.isDropPowerUp && m.immuneCycle < m.cycle) m.energy += Math.min(this.health, dmg) * tech.energySiphon * level.isReducedRegen
if (tech.energySiphon && this.isDropPowerUp && m.immuneCycle < m.cycle) {
//dmg !== Infinity &&
const regen = Math.min(this.health, dmg) * tech.energySiphon * level.isReducedRegen
if (!isNaN(regen) && regen !== Infinity) m.energy += regen
}
dmg /= Math.sqrt(this.mass)
}
@@ -1129,7 +1133,7 @@ const mobs = {
for (let i = 0; i < mob.length; i++) {
if (Vector.magnitudeSquared(Vector.sub(this.position, mob[i].position)) < 500000 && mob[i].alive) { //700
if (mob[i].health < 1) {
mob[i].health += 0.33 + this.isBoss
mob[i].health += 0.33
if (mob[i].health > 1) mob[i].health = 1
simulation.drawList.push({
x: mob[i].position.x,

File diff suppressed because it is too large Load Diff

View File

@@ -1653,6 +1653,7 @@ const powerUps = {
if (b.inventory.length === 0) {
powerUps.spawn(x, y, "gun", false); //first gun
} else if (tech.totalCount === 0) { //first tech
powerUps.spawn(x - 22, y - 50, "ammo", false); //some ammo
powerUps.spawn(x, y, "tech", false);
} else if (b.inventory.length === 1) { //second gun or extra ammo
if (Math.random() < 0.4) {

View File

@@ -987,7 +987,7 @@ const simulation = {
} else {
Composite.add(engine.world, [player])
}
shuffle(level.constraint)
seededShuffle(level.constraint)
level.populateLevels()
input.endKeySensing();
simulation.ephemera = []

View File

@@ -745,7 +745,7 @@ const spawn = {
// exit() { },
// },
]
shuffle(me.mode); //THIS SHOULD NOT BE COMMENTED OUT!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
me.mode.sort(() => Math.random() - 0.5);
me.do = function () {
this.fill = `hsl(${360 * Math.sin(this.cycle * 0.011)},${80 + 20 * Math.sin(this.cycle * 0.004)}%,${60 + 20 * Math.sin(this.cycle * 0.009)}%)`
if (this.health < 1) {
@@ -1771,6 +1771,7 @@ const spawn = {
powerUps.spawn(me.position.x, me.position.y, "heal");
powerUps.spawn(me.position.x, me.position.y, "ammo");
powerUps.spawn(me.position.x, me.position.y, "ammo");
powerUps.spawn(me.position.x, me.position.y, "ammo");
} else if (!m.isCloak) {
me.foundPlayer();
}
@@ -2683,22 +2684,24 @@ const spawn = {
this.seePlayerByHistory()
this.invulnerabilityCountDown--
if (this.isInvulnerable) {
ctx.beginPath();
let vertices = this.vertices;
ctx.moveTo(vertices[0].x, vertices[0].y);
for (let j = 1; j < vertices.length; j++) ctx.lineTo(vertices[j].x, vertices[j].y);
ctx.lineTo(vertices[0].x, vertices[0].y);
for (let i = 0; i < this.babyList.length; i++) {
if (this.babyList[i].alive) {
let vertices = this.babyList[i].vertices;
ctx.moveTo(vertices[0].x, vertices[0].y);
for (let j = 1; j < vertices.length; j++) ctx.lineTo(vertices[j].x, vertices[j].y);
ctx.lineTo(vertices[0].x, vertices[0].y);
if (this.invulnerabilityCountDown > 90 || this.invulnerabilityCountDown % 20 > 10) {
ctx.beginPath();
let vertices = this.vertices;
ctx.moveTo(vertices[0].x, vertices[0].y);
for (let j = 1; j < vertices.length; j++) ctx.lineTo(vertices[j].x, vertices[j].y);
ctx.lineTo(vertices[0].x, vertices[0].y);
for (let i = 0; i < this.babyList.length; i++) {
if (this.babyList[i].alive) {
let vertices = this.babyList[i].vertices;
ctx.moveTo(vertices[0].x, vertices[0].y);
for (let j = 1; j < vertices.length; j++) ctx.lineTo(vertices[j].x, vertices[j].y);
ctx.lineTo(vertices[0].x, vertices[0].y);
}
}
ctx.lineWidth = 3 + 0.2 * Math.min(this.invulnerabilityCountDown, 90) + 5 * Math.random()
ctx.strokeStyle = `rgba(255,255,255,${0.4 + 0.4 * Math.random()})`;
ctx.stroke();
}
ctx.lineWidth = 13 + 5 * Math.random();
ctx.strokeStyle = `rgba(255,255,255,${0.5 + 0.2 * Math.random()})`;
ctx.stroke();
if (this.invulnerabilityCountDown < 0) {
this.invulnerabilityCountDown = 110
this.isInvulnerable = false
@@ -2711,7 +2714,7 @@ const spawn = {
}
}
} else if (this.invulnerabilityCountDown < 0) {
this.invulnerabilityCountDown = 120 + 9 * simulation.difficulty
this.invulnerabilityCountDown = 240 + 5 * simulation.difficulty
this.isInvulnerable = true
if (this.damageReduction) this.startingDamageReduction = this.damageReduction
this.damageReduction = 0

View File

@@ -271,7 +271,6 @@ const tech = {
if (tech.isImmunityDamage && m.immuneCycle > m.cycle) dmg *= 3
if (tech.isPowerUpDamage) dmg *= 1 + 0.07 * powerUp.length
if (tech.isDamageCooldown) dmg *= m.lastKillCycle + tech.isDamageCooldownTime > m.cycle ? 0.4 : 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.9
if (tech.offGroundDamage && !m.onGround) dmg *= tech.offGroundDamage
if (tech.isDilate) dmg *= 1.9 + 1.1 * Math.sin(m.cycle * 0.01)
@@ -1018,7 +1017,7 @@ const tech = {
{
name: "Pareto efficiency",
descriptionFunction() {
return `all you ${powerUps.orb.gun()} randomly get<br><strong>5x</strong> or <strong>0.2x</strong> <strong class='color-ammo'>ammo</strong> per ${powerUps.orb.ammo(1)}`
return `all your ${powerUps.orb.gun()} randomly get<br><strong>5x</strong> or <strong>0.2x</strong> <strong class='color-ammo'>ammo</strong> per ${powerUps.orb.ammo(1)}`
},
maxCount: 1,
count: 0,
@@ -1033,7 +1032,7 @@ const tech = {
let options = []
for (let i = 0; i < b.inventory.length; i++) options.push(b.inventory[i])
options = shuffle(options)
options.sort(() => Math.random() - 0.5);
for (let i = 0; i < options.length; i++) {
const index = options[i]
const scale = (i < options.length / 2) ? 4 : 0.25
@@ -2758,7 +2757,7 @@ const tech = {
},
{
name: "Pauli exclusion",
description: `for <strong>7</strong> seconds after mob <strong>collisions</strong><br>become <strong class="color-invulnerable">invulnerable</strong> and <em style="opacity: 0.3;">inhibit <strong class='color-f'>energy</strong> regen</em>`,
description: `for <strong>7</strong> seconds after mob <strong>collisions</strong><br>gain <strong class="color-invulnerable">invulnerbility</strong> and <em style="opacity: 0.3;">blocked <strong class='color-f'>energy</strong> regen</em>`,
maxCount: 9,
count: 0,
frequency: 1,
@@ -2777,7 +2776,7 @@ const tech = {
},
{
name: "spin-statistics theorem",
description: `for <strong>1.9</strong> seconds out of every <strong>7</strong> seconds<br>become <strong class="color-invulnerable">invulnerable</strong> and <em style="opacity: 0.3;">inhibit <strong class='color-f'>energy</strong> regen</em>`,
description: `for <strong>1.9</strong> seconds out of every <strong>7</strong> seconds<br>gain <strong class="color-invulnerable">invulnerbility</strong> and <em style="opacity: 0.3;">blocked <strong class='color-f'>energy</strong> regen</em>`,
maxCount: 3,
count: 0,
frequency: 1,
@@ -2795,7 +2794,7 @@ const tech = {
},
{
name: "fermion",
description: `for <strong>5</strong> seconds after mobs <strong>die</strong><br>become <strong class="color-invulnerable">invulnerable</strong> and <em style="opacity: 0.3;">inhibit <strong class='color-f'>energy</strong> regen</em>`,
description: `if a mob has <strong>died</strong> in the last <strong>5</strong> seconds<br>gain <strong class="color-invulnerable">invulnerbility</strong> and <em style="opacity: 0.3;">blocked <strong class='color-f'>energy</strong> regen</em>`,
maxCount: 1,
count: 0,
frequency: 1,
@@ -3009,9 +3008,9 @@ const tech = {
frequency: 1,
frequencyDefault: 1,
allowed() {
return !tech.isDamageAfterKillNoRegen
return true
},
requires: "not parasitism",
requires: "",
effect() {
tech.isCrouchRegen = true; //only used to check for requirements
m.regenEnergy = function () {
@@ -3042,29 +3041,6 @@ const tech = {
tech.energySiphon = 0;
}
},
{
name: "parasitism",
description: "if a mob has <strong>died</strong> in the last <strong>5</strong> seconds<br><strong>2x</strong> <strong class='color-d'>damage</strong>, no passive <strong class='color-f'>energy</strong> 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 * level.isReducedRegen;
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 <strong>died</strong> in the last <strong>5</strong> seconds<br>generate <strong>0.05x</strong> maximum <strong class='color-f'>energy</strong> every second",
@@ -4769,7 +4745,7 @@ const tech = {
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
pool.sort(() => Math.random() - 0.5);
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
@@ -8965,22 +8941,31 @@ const tech = {
},
{
name: "invariant",
description: "while placing your <strong class='color-worm'>wormhole</strong><br>use <strong class='color-f'>energy</strong> to <strong>pause</strong> time",
cost: 1,
descriptionFunction() {
return `use ${powerUps.orb.research(this.cost)}<br><strong>pause</strong> time while placing your <strong class='color-worm'>wormhole</strong>`
},
isFieldTech: true,
maxCount: 1,
count: 0,
frequency: 2,
frequencyDefault: 2,
allowed() {
return m.fieldMode === 9 && !tech.isNoDraftPause
return m.fieldMode === 9 && !tech.isNoDraftPause && (build.isExperimentSelection || powerUps.research.count > this.cost - 1)
},
requires: "wormhole, not eternalism",
effect() {
tech.isWormHolePause = true
for (let i = 0; i < this.cost; i++) {
if (powerUps.research.count > 0) powerUps.research.changeRerolls(-1)
}
},
remove() {
if (tech.isWormHolePause && m.isTimeDilated) m.wakeCheck();
tech.isWormHolePause = false
if (this.count) {
powerUps.research.changeRerolls(this.cost)
}
}
},
{
@@ -9031,7 +9016,7 @@ const tech = {
allowed() {
return m.fieldMode === 9 && !tech.isFreeWormHole
},
requires: "wormhole, not charmed baryons",
requires: "wormhole, not holographic principle",
effect() {
tech.isWormholeMapIgnore = true
},
@@ -10044,8 +10029,8 @@ const tech = {
},
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])
const colors = [powerUps.research.color, powerUps.heal.color, powerUps.ammo.color, powerUps.ammo.color, powerUps.field.color, powerUps.gun.color]
colors.sort(() => Math.random() - 0.5);
powerUps.research.color = colors[0]
powerUps.heal.color = colors[1]
powerUps.ammo.color = colors[2]
@@ -10075,37 +10060,7 @@ const tech = {
}
}
},
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;
// }
// }
}
remove() { }
},
{
name: "emergency broadcasting",
@@ -10519,7 +10474,7 @@ const tech = {
},
requires: "",
effect() {
String.prototype.shuffle = function () {
String.prototype.shuf = function () {
var a = this.split(""),
n = a.length;
@@ -10532,7 +10487,7 @@ const tech = {
return a.join("");
}
for (let i = 0, len = tech.tech.length; i < len; i++) tech.tech[i].name = tech.tech[i].name.shuffle()
for (let i = 0, len = tech.tech.length; i < len; i++) tech.tech[i].name = tech.tech[i].name.shuf()
},
remove() { }
},
@@ -12190,7 +12145,6 @@ const tech = {
isDuplicateMobs: null,
isDynamoBotUpgrade: null,
isBlockPowerUps: null,
isDamageAfterKillNoRegen: null,
isHarmReduceNoKill: null,
isSwitchReality: null,
isResearchReality: null,