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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 52 KiB

View File

@@ -3160,7 +3160,7 @@ const b = {
if ( if (
Vector.magnitudeSquared(Vector.sub(this.position, powerUp[i].position)) < 20000 && 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.isSuperDeterminism && powerUp[i].name === "field") ||
((tech.isEnergyNoAmmo || b.inventory.length === 0) && powerUp[i].name === "ammo") ((tech.isEnergyNoAmmo || b.inventory.length === 0) && powerUp[i].name === "ammo")
) )
@@ -3192,7 +3192,7 @@ const b = {
let closeDist = Infinity; let closeDist = Infinity;
for (let i = 0, len = powerUp.length; i < len; ++i) { for (let i = 0, len = powerUp.length; i < len; ++i) {
if (!( 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.isSuperDeterminism && powerUp[i].name === "field") ||
((tech.isEnergyNoAmmo || b.inventory.length === 0) && powerUp[i].name === "ammo") ((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) // console.log(Math.seed)
function shuffle(array) { function seededShuffle(array) {
var currentIndex = array.length, var currentIndex = array.length,
temporaryValue, temporaryValue,
randomIndex; randomIndex;
// While there remain elements to shuffle... // While there remain elements
while (0 !== currentIndex) { while (0 !== currentIndex) {
// Pick a remaining element... // Pick a remaining element...
// randomIndex = Math.floor(Math.random() * currentIndex); // 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;"> <details id = "console-log-details" style="padding: 0 8px;">
<summary>console log</summary> <summary>console log</summary>
<div class="pause-details"> <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> </div>
</details> </details>
</div>` </div>`
@@ -1205,6 +1205,7 @@ const input = {
left: false, left: false,
right: false, right: false,
isPauseKeyReady: true, isPauseKeyReady: true,
// lastDown: null,
key: { key: {
fire: "KeyF", fire: "KeyF",
field: "Space", field: "Space",
@@ -1379,6 +1380,7 @@ window.addEventListener("keyup", function (event) {
}); });
window.addEventListener("keydown", function (event) { window.addEventListener("keydown", function (event) {
// input.lastDown = event.code
// console.log(event.code) // console.log(event.code)
switch (event.code) { switch (event.code) {
case input.key.right: case input.key.right:

View File

@@ -22,7 +22,7 @@ const level = {
// simulation.isHorizontalFlipped = true // 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
// 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() // level.updateDifficulty()
// tech.giveTech("performance") // tech.giveTech("performance")
// m.maxHealth = m.health = 100000000 // m.maxHealth = m.health = 100000000
@@ -33,7 +33,7 @@ const level = {
// tech.tech[297].frequency = 100 // tech.tech[297].frequency = 100
// tech.addJunkTechToPool(0.5) // tech.addJunkTechToPool(0.5)
// m.couplingChange(10) // 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 // m.energy = 0
// powerUps.research.count = 3 // powerUps.research.count = 3
// tech.isHookWire = true // tech.isHookWire = true
@@ -58,7 +58,7 @@ const level = {
// requestAnimationFrame(() => { for (let i = 0; i < 1; i++) tech.giveTech("interest") }); // requestAnimationFrame(() => { for (let i = 0; i < 1; i++) tech.giveTech("interest") });
// for (let i = 0; i < 1; i++) tech.giveTech("interest") // for (let i = 0; i < 1; i++) tech.giveTech("interest")
// m.lastKillCycle = m.cycle // 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); // 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 // spawn.bodyRect(575, -700, 150, 150); //block mob line of site on testing
// level.testing(); // level.testing();
@@ -810,14 +810,14 @@ const level = {
if (document.getElementById("seed").value) { //check for player entered seed in settings if (document.getElementById("seed").value) { //check for player entered seed in settings
Math.initialSeed = String(document.getElementById("seed").value) 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) { if (simulation.isTraining) {
simulation.isHorizontalFlipped = false simulation.isHorizontalFlipped = false
level.levels = level.trainingLevels.slice(0) //copy array, not by just by assignment level.levels = level.trainingLevels.slice(0) //copy array, not by just by assignment
if (simulation.isCommunityMaps) level.trainingLevels.push("diamagnetism") 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 level.levels = level.playableLevels.slice(0) //copy array, not by just by assignment
if (simulation.isCommunityMaps) { if (simulation.isCommunityMaps) {
level.levels = level.levels.concat(level.communityLevels) level.levels = level.levels.concat(level.communityLevels)
@@ -825,7 +825,7 @@ const level = {
} else { } else {
simulation.isHorizontalFlipped = (Math.seededRandom() < 0.5) ? true : false //if true, some maps are flipped horizontally 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 level.levels.length = 9 //remove any extra levels past 9
pick = ["interferometer", "factory", "reservoir"] 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 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 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 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.splice(0, 3); //remove some stations to keep it to 4 stations
stationList.unshift(0) //add index zero to the front of the array 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) //3x2: 4 short rooms (3000x1500), 1 double tall room (3000x3000)
//rooms //rooms
let rooms = ["exit", "loot", "enter", "empty"] 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 //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 while ( //makes sure that the exit and entrance aren't both on the same floor
(rooms[0] === "enter" && rooms[2] === "exit") || (rooms[0] === "enter" && rooms[2] === "exit") ||
(rooms[2] === "enter" && rooms[0] === "exit") || (rooms[2] === "enter" && rooms[0] === "exit") ||
(rooms[1] === "enter" && rooms[3] === "exit") || (rooms[1] === "enter" && rooms[3] === "exit") ||
(rooms[3] === "enter" && rooms[1] === "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++) { for (let i = 0; i < rooms.length; i++) {
if (rooms[i] === "enter") rooms[i] = enter if (rooms[i] === "enter") rooms[i] = enter
if (rooms[i] === "exit") rooms[i] = exit if (rooms[i] === "exit") rooms[i] = exit
@@ -6764,7 +6764,7 @@ const level = {
rooms[3]() 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++) { for (let i = 0; i < 3; i++) {
if (i === 0) { if (i === 0) {
isDoorLeft = false isDoorLeft = false
@@ -7054,7 +7054,7 @@ const level = {
}; };
powerUps.spawnStartingPowerUps(1875, -3075); 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, x: -150,
y: -1775 y: -1775
}, { }, {
@@ -7066,7 +7066,8 @@ const level = {
}, { }, {
x: 1325, x: 1325,
y: -150 y: -150
}]); }];
powerUpPos.sort(() => Math.random() - 0.5);
powerUps.chooseRandomPowerUp(powerUpPos[0].x, powerUpPos[0].y); powerUps.chooseRandomPowerUp(powerUpPos[0].x, powerUpPos[0].y);
powerUps.chooseRandomPowerUp(powerUpPos[1].x, powerUpPos[1].y); powerUps.chooseRandomPowerUp(powerUpPos[1].x, powerUpPos[1].y);
//outer wall //outer wall
@@ -8858,7 +8859,8 @@ const level = {
// spawn.bodyRect(312, -100, 25, 100); // spawn.bodyRect(312, -100, 25, 100);
spawn.bodyRect(1450, -300, 150, 50); 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[0], -200, 300, 100);
spawn.mapRect(xPos[1], -250, 300, 300); spawn.mapRect(xPos[1], -250, 300, 300);
spawn.mapRect(xPos[2], -150, 300, 200); 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 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 dmg *= this.damageReduction
//energy and heal drain should be calculated after damage boosts //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) dmg /= Math.sqrt(this.mass)
} }
@@ -1129,7 +1133,7 @@ const mobs = {
for (let i = 0; i < mob.length; i++) { for (let i = 0; i < mob.length; i++) {
if (Vector.magnitudeSquared(Vector.sub(this.position, mob[i].position)) < 500000 && mob[i].alive) { //700 if (Vector.magnitudeSquared(Vector.sub(this.position, mob[i].position)) < 500000 && mob[i].alive) { //700
if (mob[i].health < 1) { 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 if (mob[i].health > 1) mob[i].health = 1
simulation.drawList.push({ simulation.drawList.push({
x: mob[i].position.x, x: mob[i].position.x,

View File

@@ -1226,11 +1226,7 @@ const m = {
m.isAltSkin = true m.isAltSkin = true
m.yOffWhen.stand = 52 m.yOffWhen.stand = 52
m.yOffWhen.jump = 72 m.yOffWhen.jump = 72
m.coyoteCycles = 11 m.squirrelJump = 1.15;
m.hardLandCDScale = 0.5
m.hardLanding = 160
m.squirrelFx = 1.4;
m.squirrelJump = 1.16;
m.setMovement() m.setMovement()
m.draw = function () { m.draw = function () {
@@ -3631,6 +3627,9 @@ const m = {
// if ((m.fieldMode === 0 || m.fieldMode === 9) && !build.isExperimentSelection && !simulation.isTextLogOpen) simulation.circleFlare(0.4); // if ((m.fieldMode === 0 || m.fieldMode === 9) && !build.isExperimentSelection && !simulation.isTextLogOpen) simulation.circleFlare(0.4);
}, },
setField(index) { setField(index) {
// console.log("field mode: ", index)
window.removeEventListener("keydown", m.fieldEvent);
if (isNaN(index)) { //find index by name if (isNaN(index)) { //find index by name
let found = false let found = false
for (let i = 0; i < m.fieldUpgrades.length; i++) { for (let i = 0; i < m.fieldUpgrades.length; i++) {
@@ -3647,8 +3646,11 @@ const m = {
m.setHoldDefaults(); m.setHoldDefaults();
m.fieldUpgrades[index].effect(); m.fieldUpgrades[index].effect();
simulation.inGameConsole(`<div class="circle-grid field"></div> &nbsp; <span class='color-var'>m</span>.setField("<strong class='color-text'>${m.fieldUpgrades[m.fieldMode].name}</strong>")<br>input.key.field<span class='color-symbol'>:</span> ["<span class='color-text'>MouseRight</span>"]`); simulation.inGameConsole(`<div class="circle-grid field"></div> &nbsp; <span class='color-var'>m</span>.setField("<strong class='color-text'>${m.fieldUpgrades[m.fieldMode].name}</strong>")<br>input.key.field<span class='color-symbol'>:</span> ["<span class='color-text'>MouseRight</span>"]`);
if (m.fieldMode === 4) simulation.inGameConsole(`simulation<span class='color-symbol'>.</span>molecularMode <span class='color-symbol'>=</span> ${m.fieldUpgrades[4].modeText()} &nbsp; &nbsp; <em style="float: right;font-family: monospace;font-size: 1rem;color: #055;">↓↘→↓↙←↑↑↓</em>`);
}, },
fieldUpgrades: [{ fieldEvent: null,
fieldUpgrades: [
{
name: "field emitter", name: "field emitter",
imageNumber: Math.floor(Math.random() * 26), //pick one of the 25 field emitter image files at random imageNumber: Math.floor(Math.random() * 26), //pick one of the 25 field emitter image files at random
description: `<em>initial field</em><br>use <strong class='color-f'>energy</strong> to <strong>deflect</strong> mobs and <strong>throw</strong> <strong class='color-block'>blocks</strong> description: `<em>initial field</em><br>use <strong class='color-f'>energy</strong> to <strong>deflect</strong> mobs and <strong>throw</strong> <strong class='color-block'>blocks</strong>
@@ -4200,12 +4202,53 @@ const m = {
}, },
{ {
name: "molecular assembler", name: "molecular assembler",
description: `use <strong class='color-f'>energy</strong> to <strong>deflect</strong> mobs<br>excess <strong class='color-f'>energy</strong> used to <strong class='color-print'>print</strong> ${simulation.molecularMode === 0 ? "<strong class='color-p' style='letter-spacing: 2px;'>spores" : simulation.molecularMode === 1 ? "<strong>missiles" : simulation.molecularMode === 2 ? "<strong class='color-s'>ice IX" : "<strong>drones"}</strong><br><strong>12</strong> <strong class='color-f'>energy</strong> per second`, modeText() {
setDescription() { return `${simulation.molecularMode === 0 ? "<strong class='color-p' style='letter-spacing: 2px;'>spores" : simulation.molecularMode === 1 ? "<strong>missiles" : simulation.molecularMode === 2 ? "<strong class='color-s'>ice IX" : "<strong>drones"}</strong>`
return `use <strong class='color-f'>energy</strong> to <strong>deflect</strong> mobs<br>excess <strong class='color-f'>energy</strong> used to <strong class='color-print'>print</strong> ${simulation.molecularMode === 0 ? "<strong class='color-p' style='letter-spacing: 2px;'>spores" : simulation.molecularMode === 1 ? "<strong>missiles" : simulation.molecularMode === 2 ? "<strong class='color-s'>ice IX" : "<strong>drones"}</strong><br><strong>12</strong> <strong class='color-f'>energy</strong> per second`
}, },
description: `use <strong class='color-f'>energy</strong> to <strong>deflect</strong> mobs<br>excess <strong class='color-f'>energy</strong> used to <strong class='color-print'>print</strong> ${simulation.molecularMode === 0 ? "<strong class='color-p' style='letter-spacing: 2px;'>spores" : simulation.molecularMode === 1 ? "<strong>missiles" : simulation.molecularMode === 2 ? "<strong class='color-s'>ice IX" : "<strong>drones"}</strong><br><strong>12</strong> <strong class='color-f'>energy</strong> per second <em style ="float: right; font-family: monospace;font-size:1rem;color:#fff;">↓↘→↓↙←↑↑↓</em>`,
setDescription() {
return `use <strong class='color-f'>energy</strong> to <strong>deflect</strong> mobs<br>excess <strong class='color-f'>energy</strong> used to <strong class='color-print'>print</strong> ${simulation.molecularMode === 0 ? "<strong class='color-p' style='letter-spacing: 2px;'>spores" : simulation.molecularMode === 1 ? "<strong>missiles" : simulation.molecularMode === 2 ? "<strong class='color-s'>ice IX" : "<strong>drones"}</strong><br><strong>12</strong> <strong class='color-f'>energy</strong> per second <em style ="float: right; font-family: monospace;font-size:1rem;color:#fff;">↓↘→↓↙←↑↑↓</em>`
},
keyLog: [],
effect: () => { effect: () => {
//store event function so it can be found and removed in m.setField()
m.fieldEvent = function (event) {
m.fieldUpgrades[4].keyLog.push(event.code)
// Helper function to compare arrays
function arraysEqual(arr1, arr2) {
if (arr1.length !== arr2.length) return false;
for (let i = 0; i < arr1.length; i++) {
if (arr1[i] !== arr2[i]) return false;
}
return true;
}
const pattern = [input.key.down, input.key.right, input.key.down, input.key.left, input.key.up, input.key.up, input.key.down]
//check if the newest key press is correct
if (event.code !== pattern[m.fieldUpgrades[4].keyLog.length - 1]) {
m.fieldUpgrades[4].keyLog = [] //pattern is wrong, reset log
} else if (arraysEqual(m.fieldUpgrades[4].keyLog, pattern)) { //pattern is complete
//cycle to next molecular mode
m.fieldUpgrades[4].keyLog = []
const energy = m.energy //save current energy
if (simulation.molecularMode < 3) {
simulation.molecularMode++
} else {
simulation.molecularMode = 0
}
// m.setField((m.fieldMode === m.fieldUpgrades.length - 1) ? 1 : m.fieldMode + 1) //cycle to next field, skip field emitter
m.fieldUpgrades[4].description = m.fieldUpgrades[4].setDescription()
m.energy = energy //return to current energy
const name = `${simulation.molecularMode === 0 ? "<em class='color-p' style='letter-spacing: 2px;'>spores" : simulation.molecularMode === 1 ? "<em>missiles" : simulation.molecularMode === 2 ? "<em class='color-s'>ice IX" : "<em>drones"}</em>`
simulation.inGameConsole(`simulation<span class='color-symbol'>.</span>molecularMode <span class='color-symbol'>=</span> ${simulation.molecularMode} // ${name} &nbsp; <em style="float: right;font-family: monospace;font-size: 1rem;color: #055;">↓↘→↓↙←↑↑↓</em>`);
}
// console.log(m.fieldUpgrades[4].keyLog)
}
window.addEventListener("keydown", m.fieldEvent);
m.fieldMeterColor = "#ff0" m.fieldMeterColor = "#ff0"
m.eyeFillColor = m.fieldMeterColor m.eyeFillColor = m.fieldMeterColor
m.hold = function () { m.hold = function () {
@@ -4240,7 +4283,7 @@ const m = {
} }
} else { } else {
const drain = 0.095 + (Math.max(bullet.length, 130) - 130) * 0.01 const drain = 0.095 + (Math.max(bullet.length, 130) - 130) * 0.01
for (let i = 0, len = Math.random() * 20; i < len; i++) { for (let i = 0, len = 5; i < len; i++) {
if (m.energy > 3 * drain) { if (m.energy > 3 * drain) {
m.energy -= drain m.energy -= drain
b.spore(m.pos) b.spore(m.pos)
@@ -5534,9 +5577,9 @@ const m = {
if (input.field) { if (input.field) {
if (tech.isWormHolePause) { if (tech.isWormHolePause) {
const drain = m.fieldRegen + 0.000035 // const drain = m.fieldRegen + 0.000035
if (m.energy > drain) { // if (m.energy > drain) {
m.energy -= drain // m.energy -= drain
if (m.immuneCycle < m.cycle + 1) m.immuneCycle = m.cycle + 1; //player is immune to damage for 1 cycle if (m.immuneCycle < m.cycle + 1) m.immuneCycle = m.cycle + 1; //player is immune to damage for 1 cycle
m.isTimeDilated = true; m.isTimeDilated = true;
@@ -5559,10 +5602,10 @@ const m = {
}); });
player.force.x = 0 player.force.x = 0
player.force.y = 0 player.force.y = 0
} else { // } else {
m.wakeCheck(); // m.wakeCheck();
m.energy = 0; // m.energy = 0;
} // }
} }
m.grabPowerUp(); m.grabPowerUp();
@@ -5619,16 +5662,12 @@ const m = {
m.hole.isReady && mag > 250 && m.energy > this.drain && m.hole.isReady && mag > 250 && m.energy > this.drain &&
(tech.isWormholeMapIgnore || Matter.Query.ray(map, m.pos, justPastMouse).length === 0) && (tech.isWormholeMapIgnore || Matter.Query.ray(map, m.pos, justPastMouse).length === 0) &&
Matter.Query.region(map, { Matter.Query.region(map, {
min: { min: { x: simulation.mouseInGame.x - scale, y: simulation.mouseInGame.y - scale },
x: simulation.mouseInGame.x - scale, max: { x: simulation.mouseInGame.x + scale, y: simulation.mouseInGame.y + scale }
y: simulation.mouseInGame.y - scale
},
max: {
x: simulation.mouseInGame.x + scale,
y: simulation.mouseInGame.y + scale
}
}).length === 0 }).length === 0
) { ) {
//tech.isWormHolePause
// console.log(this.drain)
m.energy -= this.drain m.energy -= this.drain
m.hole.isReady = false; m.hole.isReady = false;
m.fieldRange = 0 m.fieldRange = 0

View File

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

View File

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

View File

@@ -745,7 +745,7 @@ const spawn = {
// exit() { }, // exit() { },
// }, // },
] ]
shuffle(me.mode); //THIS SHOULD NOT BE COMMENTED OUT!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! me.mode.sort(() => Math.random() - 0.5);
me.do = function () { 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)}%)` 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) { 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, "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"); powerUps.spawn(me.position.x, me.position.y, "ammo");
powerUps.spawn(me.position.x, me.position.y, "ammo");
} else if (!m.isCloak) { } else if (!m.isCloak) {
me.foundPlayer(); me.foundPlayer();
} }
@@ -2683,6 +2684,7 @@ const spawn = {
this.seePlayerByHistory() this.seePlayerByHistory()
this.invulnerabilityCountDown-- this.invulnerabilityCountDown--
if (this.isInvulnerable) { if (this.isInvulnerable) {
if (this.invulnerabilityCountDown > 90 || this.invulnerabilityCountDown % 20 > 10) {
ctx.beginPath(); ctx.beginPath();
let vertices = this.vertices; let vertices = this.vertices;
ctx.moveTo(vertices[0].x, vertices[0].y); ctx.moveTo(vertices[0].x, vertices[0].y);
@@ -2696,9 +2698,10 @@ const spawn = {
ctx.lineTo(vertices[0].x, vertices[0].y); ctx.lineTo(vertices[0].x, vertices[0].y);
} }
} }
ctx.lineWidth = 13 + 5 * Math.random(); ctx.lineWidth = 3 + 0.2 * Math.min(this.invulnerabilityCountDown, 90) + 5 * Math.random()
ctx.strokeStyle = `rgba(255,255,255,${0.5 + 0.2 * Math.random()})`; ctx.strokeStyle = `rgba(255,255,255,${0.4 + 0.4 * Math.random()})`;
ctx.stroke(); ctx.stroke();
}
if (this.invulnerabilityCountDown < 0) { if (this.invulnerabilityCountDown < 0) {
this.invulnerabilityCountDown = 110 this.invulnerabilityCountDown = 110
this.isInvulnerable = false this.isInvulnerable = false
@@ -2711,7 +2714,7 @@ const spawn = {
} }
} }
} else if (this.invulnerabilityCountDown < 0) { } else if (this.invulnerabilityCountDown < 0) {
this.invulnerabilityCountDown = 120 + 9 * simulation.difficulty this.invulnerabilityCountDown = 240 + 5 * simulation.difficulty
this.isInvulnerable = true this.isInvulnerable = true
if (this.damageReduction) this.startingDamageReduction = this.damageReduction if (this.damageReduction) this.startingDamageReduction = this.damageReduction
this.damageReduction = 0 this.damageReduction = 0

View File

@@ -271,7 +271,6 @@ const tech = {
if (tech.isImmunityDamage && m.immuneCycle > m.cycle) dmg *= 3 if (tech.isImmunityDamage && m.immuneCycle > m.cycle) dmg *= 3
if (tech.isPowerUpDamage) dmg *= 1 + 0.07 * powerUp.length if (tech.isPowerUpDamage) dmg *= 1 + 0.07 * powerUp.length
if (tech.isDamageCooldown) dmg *= m.lastKillCycle + tech.isDamageCooldownTime > m.cycle ? 0.4 : 4 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.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.offGroundDamage && !m.onGround) dmg *= tech.offGroundDamage
if (tech.isDilate) dmg *= 1.9 + 1.1 * Math.sin(m.cycle * 0.01) if (tech.isDilate) dmg *= 1.9 + 1.1 * Math.sin(m.cycle * 0.01)
@@ -1018,7 +1017,7 @@ const tech = {
{ {
name: "Pareto efficiency", name: "Pareto efficiency",
descriptionFunction() { 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, maxCount: 1,
count: 0, count: 0,
@@ -1033,7 +1032,7 @@ const tech = {
let options = [] let options = []
for (let i = 0; i < b.inventory.length; i++) options.push(b.inventory[i]) 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++) { for (let i = 0; i < options.length; i++) {
const index = options[i] const index = options[i]
const scale = (i < options.length / 2) ? 4 : 0.25 const scale = (i < options.length / 2) ? 4 : 0.25
@@ -2758,7 +2757,7 @@ const tech = {
}, },
{ {
name: "Pauli exclusion", 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, maxCount: 9,
count: 0, count: 0,
frequency: 1, frequency: 1,
@@ -2777,7 +2776,7 @@ const tech = {
}, },
{ {
name: "spin-statistics theorem", 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, maxCount: 3,
count: 0, count: 0,
frequency: 1, frequency: 1,
@@ -2795,7 +2794,7 @@ const tech = {
}, },
{ {
name: "fermion", 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, maxCount: 1,
count: 0, count: 0,
frequency: 1, frequency: 1,
@@ -3009,9 +3008,9 @@ const tech = {
frequency: 1, frequency: 1,
frequencyDefault: 1, frequencyDefault: 1,
allowed() { allowed() {
return !tech.isDamageAfterKillNoRegen return true
}, },
requires: "not parasitism", requires: "",
effect() { effect() {
tech.isCrouchRegen = true; //only used to check for requirements tech.isCrouchRegen = true; //only used to check for requirements
m.regenEnergy = function () { m.regenEnergy = function () {
@@ -3042,29 +3041,6 @@ const tech = {
tech.energySiphon = 0; 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", 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", 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 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) 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 let removeCount = 0
for (let i = 0, len = pool.length * 0.5; i < len; i++) removeCount += tech.removeTech(pool[i]) for (let i = 0, len = pool.length * 0.5; i < len; i++) removeCount += tech.removeTech(pool[i])
this.damage = this.damagePerRemoved * removeCount this.damage = this.damagePerRemoved * removeCount
@@ -8965,22 +8941,31 @@ const tech = {
}, },
{ {
name: "invariant", 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, isFieldTech: true,
maxCount: 1, maxCount: 1,
count: 0, count: 0,
frequency: 2, frequency: 2,
frequencyDefault: 2, frequencyDefault: 2,
allowed() { 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", requires: "wormhole, not eternalism",
effect() { effect() {
tech.isWormHolePause = true tech.isWormHolePause = true
for (let i = 0; i < this.cost; i++) {
if (powerUps.research.count > 0) powerUps.research.changeRerolls(-1)
}
}, },
remove() { remove() {
if (tech.isWormHolePause && m.isTimeDilated) m.wakeCheck(); if (tech.isWormHolePause && m.isTimeDilated) m.wakeCheck();
tech.isWormHolePause = false tech.isWormHolePause = false
if (this.count) {
powerUps.research.changeRerolls(this.cost)
}
} }
}, },
{ {
@@ -9031,7 +9016,7 @@ const tech = {
allowed() { allowed() {
return m.fieldMode === 9 && !tech.isFreeWormHole return m.fieldMode === 9 && !tech.isFreeWormHole
}, },
requires: "wormhole, not charmed baryons", requires: "wormhole, not holographic principle",
effect() { effect() {
tech.isWormholeMapIgnore = true tech.isWormholeMapIgnore = true
}, },
@@ -10044,8 +10029,8 @@ const tech = {
}, },
requires: "", requires: "",
effect() { effect() {
// const colors = shuffle(["#f7b", "#0eb", "#467", "#0cf", "hsl(246,100%,77%)", "#26a"]) const colors = [powerUps.research.color, powerUps.heal.color, powerUps.ammo.color, powerUps.ammo.color, powerUps.field.color, powerUps.gun.color]
const colors = shuffle([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.research.color = colors[0]
powerUps.heal.color = colors[1] powerUps.heal.color = colors[1]
powerUps.ammo.color = colors[2] powerUps.ammo.color = colors[2]
@@ -10075,37 +10060,7 @@ const tech = {
} }
} }
}, },
remove() { 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", name: "emergency broadcasting",
@@ -10519,7 +10474,7 @@ const tech = {
}, },
requires: "", requires: "",
effect() { effect() {
String.prototype.shuffle = function () { String.prototype.shuf = function () {
var a = this.split(""), var a = this.split(""),
n = a.length; n = a.length;
@@ -10532,7 +10487,7 @@ const tech = {
return a.join(""); 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() { } remove() { }
}, },
@@ -12190,7 +12145,6 @@ const tech = {
isDuplicateMobs: null, isDuplicateMobs: null,
isDynamoBotUpgrade: null, isDynamoBotUpgrade: null,
isBlockPowerUps: null, isBlockPowerUps: null,
isDamageAfterKillNoRegen: null,
isHarmReduceNoKill: null, isHarmReduceNoKill: null,
isSwitchReality: null, isSwitchReality: null,
isResearchReality: null, isResearchReality: null,

143
todo.txt
View File

@@ -1,99 +1,78 @@
******************************************************** NEXT PATCH ************************************************** ******************************************************** NEXT PATCH **************************************************
tech: demineralization - after mobs die gain 0.85x damage taken, effect stacks, but fades 10% every second mantisBoss flashes for a second before it drops invulnerability
tech: remineralization - after mobs die gain 1.08x damage, effect stacks, but fades 10% every second removed parasitism - it's too similar to invulnerability tech
tech: equivalence principle - negative mass field doesn't cost energy invariant no longer drains energy while wormhole time is paused
new JUNK tech: aerodynamics added 1 research cost
added secret combo to change molecular assembler mode
interferometer
slower elevator and lasers
wider side ledges
large laser blocking blocks
flocculation
fewer mobs
it's easier to get out of the slime
pavilion
move vanish elements
easier traversal
secret tunnel
removed debris, but added power ups and blocks
corridor
limited to bosses that don't interact with the movers poorly
gravitron, substructure, corridor, interferometer
added more heal and ammo power ups to match other levels
because some newer levels are zoomed out more
laser max range is 3000->5000
nails last 1/3 of a second longer
bosses spawn an extra ammo power up
and 2 extra ammo on the hardest 2 difficulties
slasher mob's laserSwords will now damage a cloaked player
constraint announcement text looks more like computer code style to match game theme
foam recoil is back: 1->0.7x horizontal force and 2->4.3x vertical up force
this makes it less annoying to horizontally and easier to kinda fly/float
negative mass field damage reduction 0.4->0.5x
holographic principle no longer slows player movement
added 2 research cost
fermion gives 6->5 seconds of invulnerability after mobs die
stability 0.2->0.1x damage taken at max health
non-Newtonian armor 0.3->0.4x damage taken after collisions
Zeno's paradox 0.15->0.2x damage taken
annihilation energy cost 10->8 to destroy mobs after collisions
radiative equilibrium damage is 3->4x for 8->4 seconds
aerostat can be taken 1->3 times
dynamic equilibrium damage increased by 6->8x damage per last damage taken
aerostat no longer has 0.9x damage for being on the ground
launch system 1.2->1.3x ammo for missiles
research says that repeatedly entering alternate realities builds up some positive effects
Hilbert space 4x->3x damage
Ψ(t) collapse 6->4 research on boss death
transdimensional worms: 50% chance for a second worm per block in wormhole
wormhole 7->8 energy regen per second
hidden-variable theory 1.15->1.2 damage after choosing a field tech
ghoster mobs are less likely to get knocked far away from the player for long periods of time
bug fixes bug fixes
dynamic equilibrium was set to 100 times higher frequency then normal issue with constraint: "mob death heals mobs"
when constraints hide health bar's it's now hidden in the pause menu mob health was becoming NaN, this was infecting other values like player energy
mobs aiming at cloaked player entering a seed in settings wasn't giving the same results as a randomly generated seeds
snakeBoss more intelligently chases player for a few seconds also removed some random code that was using seeded shuffle, but didn't need to
pulsarBoss aims at player's history 3 seconds in past converted it to non seeded random shuffle with .sort(() => Math.random() - 0.5);
pulsar will not stop firing
but it will still not fire at cloaked player
******************************************************** BUGS ******************************************************** ******************************************************** BUGS ********************************************************
figure out why seeded random isn't making runs the same:
shuffle is being used for a wide variety of things that don't need a seeded random
make two shuffle functions?
check which of these is working
level order
is flipped
constraint order
mob type order
boss order
gun, field, tech options
make field options offered precalculated so it doesn't depend on player choices?
generate all constraints at the start of the game
doesn't seem to be determined by the seed...
display future constraints in pause menu?
player can become crouched while not touching the ground if they exit the ground while crouched player can become crouched while not touching the ground if they exit the ground while crouched
*********************************************************** TODO ***************************************************** *********************************************************** TODO *****************************************************
use ←↑→↓↖↗↘↙ combos to allow fields to have special actions
!!should this be wasd, arrows, or both?
how to limit spam?
on cooldown
timer or once per level
energy cost
research cost
toggle modes
combos are listed in white text in field description, and console message when getting field
shows up on highlight, and mouse over in experiment mode
field ideas:
standing wave
push mobs away are any distance
pull mobs towards you
perfect diamagnetism
negative mass
molecular assembler - done
plasma torch
time dilation
metamaterial cloaking
pilot wave
spawn blocks
wormhole
shoot out all the blocks that were sucked in this level (maybe cap at like 10?, cap with energy spent to fire)
are block sizes stored properly? because they shrink before they get eaten...
store vertices on the body object if one does already exists
after eating store the saved vertices
where to fire blocks from and in what direction?
target mobs?
fire from player (and draw a wormhole looking graphic)
grappling hook
new level idea: escort mission
player has to stay near something that moves slowly through the level
maybe only a zone around the escort is safe
safe from?
lasers
slime
radiation
use line of site vision?
is it going to feel too slow?
where to put in level order?
random option instead of reactor?
normal level?
4th hard level?
already too many hard options
you could put in 2 hard levels in the level list
make the style look like substructure, that level looks great
use the motion sense lasers
tech synergy ideas tech synergy ideas
a tech that spawns mobs that the player can use to trigger mob death tech a tech that spawns mobs that the player can use to trigger mob death tech
wormhole field needs a buff
it's good on wide open maps, but it needs a defensive effect on more closed maps
fix?
increase energy regen?
new tech?
a way to make more blocks
the block can feed into worms/coupling energy
tech: - remove the research costs of all tech tech: - remove the research costs of all tech
there's about 15 tech with a research cost there's about 15 tech with a research cost
!!conformal - similar rules for small and big scales linked to holographic principle !!conformal - similar rules for small and big scales linked to holographic principle