diff --git a/index.html b/index.html index 92d77d5..cbdbe76 100644 --- a/index.html +++ b/index.html @@ -81,16 +81,16 @@
-
+
- + - custom + experiment
diff --git a/js/index.js b/js/index.js index 99e461f..01cfcaf 100644 --- a/js/index.js +++ b/js/index.js @@ -38,7 +38,7 @@ if (screen.height < 800) { //********************************************************************** -// check for URL parameters to load a custom game +// check for URL parameters to load an experimental game //********************************************************************** //example https://landgreen.github.io/sidescroller/index.html? @@ -61,8 +61,8 @@ function getUrlVars() { window.addEventListener('load', (event) => { const set = getUrlVars() if (Object.keys(set).length !== 0) { - openCustomBuildMenu(); - //add custom selections based on url + openExperimentMenu(); + //add experimental selections based on url for (const property in set) { set[property] = set[property].replace(/%20/g, " ") set[property] = set[property].replace(/%CE%A8/g, "Ψ") @@ -100,7 +100,7 @@ window.addEventListener('load', (event) => { } if (property === "difficulty") { simulation.difficultyMode = Number(set[property]) - document.getElementById("difficulty-select-custom").value = Number(set[property]) + document.getElementById("difficulty-select-experiment").value = Number(set[property]) } if (property === "level") { document.getElementById("starting-level").value = Number(set[property]) @@ -146,7 +146,7 @@ window.onresize = () => { }; //********************************************************************** -// custom build grid display and pause +// experimental build grid display and pause //********************************************************************** const build = { onLoadPowerUps() { @@ -253,7 +253,7 @@ const build = { document.getElementById("pause-grid-right").style.display = "none" window.scrollTo(0, 0); }, - isCustomSelection: true, + isExperimentSelection: true, choosePowerUp(who, index, type, isAllowed = false) { if (type === "gun") { let isDeselect = false @@ -318,14 +318,14 @@ const build = { techID.innerHTML = `
  ${tech.tech[i].name} ${isCount}
${tech.tech[i].description}
` } - if (techID.classList.contains("build-grid-disabled")) { - techID.classList.remove("build-grid-disabled"); + if (techID.classList.contains("experiment-grid-disabled")) { + techID.classList.remove("experiment-grid-disabled"); techID.setAttribute("onClick", `javascript: build.choosePowerUp(this,${i},'tech')`); } } else { techID.innerHTML = `
${tech.tech[i].name}
requires: ${tech.tech[i].requires}` - if (!techID.classList.contains("build-grid-disabled")) { - techID.classList.add("build-grid-disabled"); + if (!techID.classList.contains("experiment-grid-disabled")) { + techID.classList.add("experiment-grid-disabled"); techID.onclick = null } if (tech.tech[i].count > 0) tech.removeTech(i) @@ -337,7 +337,7 @@ const build = { populateGrid() { let text = `
- + start @@ -357,7 +357,7 @@ const build = {
starting level:
- @@ -370,34 +370,34 @@ const build = {
` for (let i = 0, len = mech.fieldUpgrades.length; i < len; i++) { - text += `
  ${mech.fieldUpgrades[i].name}
${mech.fieldUpgrades[i].description}
` + text += `
  ${mech.fieldUpgrades[i].name}
${mech.fieldUpgrades[i].description}
` } for (let i = 0, len = b.guns.length; i < len; i++) { - text += `
  ${b.guns[i].name}
${b.guns[i].description}
` + text += `
  ${b.guns[i].name}
${b.guns[i].description}
` } for (let i = 0, len = tech.tech.length; i < len; i++) { if (!tech.tech[i].isCustomHide) { if (!tech.tech[i].allowed()) { // || tech.tech[i].name === "+1 cardinality") { //|| tech.tech[i].name === "leveraged investment" - text += `
${tech.tech[i].name}
requires: ${tech.tech[i].requires}
` + text += `
${tech.tech[i].name}
requires: ${tech.tech[i].requires}
` // } else if (tech.tech[i].count > 1) { - // text += `
  ${tech.tech[i].name} (${tech.tech[i].count}x)
${tech.tech[i].description}
` + // text += `
  ${tech.tech[i].name} (${tech.tech[i].count}x)
${tech.tech[i].description}
` } else { - text += `
  ${tech.tech[i].name}
${tech.tech[i].description}
` + text += `
  ${tech.tech[i].name}
${tech.tech[i].description}
` } } } - document.getElementById("build-grid").innerHTML = text - document.getElementById("difficulty-select-custom").value = document.getElementById("difficulty-select").value - document.getElementById("difficulty-select-custom").addEventListener("input", () => { - simulation.difficultyMode = Number(document.getElementById("difficulty-select-custom").value) - localSettings.difficultyMode = Number(document.getElementById("difficulty-select-custom").value) - document.getElementById("difficulty-select").value = document.getElementById("difficulty-select-custom").value + document.getElementById("experiment-grid").innerHTML = text + document.getElementById("difficulty-select-experiment").value = document.getElementById("difficulty-select").value + document.getElementById("difficulty-select-experiment").addEventListener("input", () => { + simulation.difficultyMode = Number(document.getElementById("difficulty-select-experiment").value) + localSettings.difficultyMode = Number(document.getElementById("difficulty-select-experiment").value) + document.getElementById("difficulty-select").value = document.getElementById("difficulty-select-experiment").value localStorage.setItem("localSettings", JSON.stringify(localSettings)); //update local storage }); }, reset() { - build.isCustomSelection = true; + build.isExperimentSelection = true; mech.setField(0) b.inventory = []; //removes guns and ammo @@ -412,7 +412,7 @@ const build = { tech.setupAllTech(); build.populateGrid(); document.getElementById("field-0").classList.add("build-field-selected"); - document.getElementById("build-grid").style.display = "grid" + document.getElementById("experiment-grid").style.display = "grid" }, shareURL(isCustom = false) { let url = "https://landgreen.github.io/sidescroller/index.html?" @@ -445,8 +445,8 @@ const build = { console.log(url) simulation.copyToClipBoard(url) }, - startBuildRun() { - build.isCustomSelection = false; + startExperiment() { + build.isExperimentSelection = false; spawn.setSpawnList(); //gives random mobs, not starter mobs spawn.setSpawnList(); if (b.inventory.length > 0) { @@ -476,27 +476,27 @@ const build = { tech.removeLoreTechFromPool(); document.body.style.cursor = "none"; document.body.style.overflow = "hidden" - document.getElementById("build-grid").style.display = "none" + document.getElementById("experiment-grid").style.display = "none" simulation.paused = false; requestAnimationFrame(cycle); } } -function openCustomBuildMenu() { - document.getElementById("build-button").style.display = "none"; - const el = document.getElementById("build-grid") +function openExperimentMenu() { + document.getElementById("experiment-button").style.display = "none"; + const el = document.getElementById("experiment-grid") el.style.display = "grid" document.body.style.overflowY = "scroll"; document.body.style.overflowX = "hidden"; document.getElementById("info").style.display = 'none' simulation.startGame(true); //starts game, but pauses it - build.isCustomSelection = true; + build.isExperimentSelection = true; simulation.paused = true; build.reset(); } -//record settings so they can be reproduced in the custom menu -document.getElementById("build-button").addEventListener("click", () => { //setup build run +//record settings so they can be reproduced in the experimental menu +document.getElementById("experiment-button").addEventListener("click", () => { //setup build run let field = 0; let inventory = []; let techList = []; @@ -507,7 +507,7 @@ document.getElementById("build-button").addEventListener("click", () => { //setu techList.push(tech.tech[i].count) } } - openCustomBuildMenu(); + openExperimentMenu(); }); // ************************************************************************************************ @@ -952,6 +952,10 @@ if (localSettings) { input.setDefault() } + if (localSettings.loreCount === undefined) { + localSettings.loreCount = 0 + localStorage.setItem("localSettings", JSON.stringify(localSettings)); //update local storage + } simulation.isCommunityMaps = localSettings.isCommunityMaps document.getElementById("community-maps").checked = localSettings.isCommunityMaps @@ -981,7 +985,7 @@ if (localSettings) { document.getElementById("fps-select").value = localSettings.fpsCapDefault } document.getElementById("control-testing").style.visibility = (localSettings.loreCount === 0) ? "hidden" : "visible" -document.getElementById("build-button").style.visibility = (localSettings.loreCount === 0) ? "hidden" : "visible" +// document.getElementById("experiment-button").style.visibility = (localSettings.loreCount === 0) ? "hidden" : "visible" input.controlTextUpdate() @@ -1005,7 +1009,7 @@ document.getElementById("community-maps").addEventListener("input", () => { localStorage.setItem("localSettings", JSON.stringify(localSettings)); //update local storage }); -// difficulty-select-custom event listener is set in build.makeGrid +// difficulty-select-experiment event listener is set in build.makeGrid document.getElementById("difficulty-select").addEventListener("input", () => { simulation.difficultyMode = Number(document.getElementById("difficulty-select").value) localSettings.difficultyMode = simulation.difficultyMode diff --git a/js/level.js b/js/level.js index f2963bb..a3f20fd 100644 --- a/js/level.js +++ b/js/level.js @@ -53,9 +53,9 @@ const level = { // for (let i = 0; i < 150; i++) tech.addLoreTechToPool(); // tech.giveTech("undefined") // lore.techCount = 1 - // localSettings.loreCount = 0; + // localSettings.loreCount = 2; // simulation.isCheating = true; - // localSettings.loreCount = 0; + // localSettings.loreCount = undefined; // localStorage.setItem("localSettings", JSON.stringify(localSettings)); //update local storage // level.null() } else { @@ -113,7 +113,7 @@ const level = { null() { level.levels.pop(); //remove lore level from rotation //start a conversation based on the number of conversations seen - if (!simulation.isCheating) lore.conversation[localSettings.loreCount % lore.conversation.length]() + if (!simulation.isCheating && localSettings.loreCount < lore.conversation.length) lore.conversation[localSettings.loreCount]() const hazardSlime = level.hazard(-1800, 150, 3600, 650, 0.01, "hsla(160, 100%, 35%,0.75)") const circle = { @@ -225,12 +225,8 @@ const level = { // spawn.mapRect(450, -820, 50, 25); //edge shelf ceiling // spawn.bodyRect(1540, -1110, 300, 25, 0.9); - // spawn.mapRect(-50, -500, 100, 100); //center square // setTimeout(() => { simulation.makeTextLog(`test`) }, 3000); - - - }, testing() { const button = level.button(200, -700) @@ -295,8 +291,8 @@ const level = { spawn.mapRect(level.exit.x, level.exit.y + 20, 100, 100); //exit bump // spawn.boost(1500, 0, 900); - spawn.starter(1900, -500, 200) //big boy - // spawn.exploder(2900, -500) + // spawn.starter(1900, -500, 200) //big boy + spawn.sneaker(2900, -500) // spawn.launcherBoss(1200, -500) // spawn.laserTargetingBoss(1600, -400) // spawn.striker(1600, -500) diff --git a/js/lore.js b/js/lore.js index c534cac..ea4a360 100644 --- a/js/lore.js +++ b/js/lore.js @@ -35,7 +35,7 @@ const lore = { localSettings.loreCount = 1 localStorage.setItem("localSettings", JSON.stringify(localSettings)); //update local storage document.getElementById("control-testing").style.visibility = (localSettings.loreCount === 0) ? "hidden" : "visible" - document.getElementById("build-button").style.visibility = (localSettings.loreCount === 0) ? "hidden" : "visible" + document.getElementById("experiment-button").style.visibility = (localSettings.loreCount === 0) ? "hidden" : "visible" } let delay = 6000 setTimeout(() => { lore.miriam.text("I've never seen it generate this level before.", true) }, delay); diff --git a/js/simulation.js b/js/simulation.js index 19b1eed..7147dba 100644 --- a/js/simulation.js +++ b/js/simulation.js @@ -325,7 +325,7 @@ const simulation = { // SVGleftMouse: ' ', // SVGrightMouse: ' ', makeTextLog(text, time = 120) { - if (simulation.isTextLogOpen && !build.isCustomSelection) { + if (simulation.isTextLogOpen && !build.isExperimentSelection) { if (simulation.lastLogTime > mech.cycle) { //if there is an older message document.getElementById("text-log").innerHTML = document.getElementById("text-log").innerHTML + '
' + text; simulation.lastLogTime = mech.cycle + time; @@ -363,6 +363,24 @@ const simulation = { simulation.updateGunHUD(); simulation.boldActiveGunHUD(); // mech.drop(); + if (true && powerUps.research.count > 0) { + powerUps.research.changeRerolls(-1) + const energy = mech.energy + mech.setField((mech.fieldMode === mech.fieldUpgrades.length - 1) ? 1 : mech.fieldMode + 1) //cycle to next field + mech.energy = energy //field swap sets energy to max, this undoes that + + //update text to show next field + for (let i = tech.tech.length - 1; i > 0; i--) { + if (tech.tech[i].name === "unified field theory") { + const index = (mech.fieldMode === mech.fieldUpgrades.length - 1) ? 1 : mech.fieldMode + 1 + tech.tech[i].description = `after switching guns
use a research to cycle your field +
(next field: ${mech.fieldUpgrades[index].name})` + break + } + } + + + } }, zoom: null, zoomScale: 1000, @@ -467,8 +485,8 @@ const simulation = { }; document.getElementById("choose-grid").style.display = "none" document.getElementById("info").style.display = "inline"; - document.getElementById("build-button").style.display = "inline" - document.getElementById("build-grid").style.display = "none" + document.getElementById("experiment-button").style.display = "inline" + document.getElementById("experiment-grid").style.display = "none" document.getElementById("pause-grid-left").style.display = "none" document.getElementById("pause-grid-right").style.display = "none" document.getElementById("splash").style.display = "inline"; @@ -479,15 +497,15 @@ const simulation = { fpsInterval: 0, //set in startGame then: null, startGame(isBuildRun = false) { - if (!isBuildRun) { //if a build run logic flow returns to "build-button").addEventListener + if (!isBuildRun) { //if a build run logic flow returns to "experiment-button").addEventListener document.body.style.cursor = "none"; document.body.style.overflow = "hidden" } simulation.onTitlePage = false; document.getElementById("choose-grid").style.display = "none" - document.getElementById("build-grid").style.display = "none" + document.getElementById("experiment-grid").style.display = "none" document.getElementById("info").style.display = "none"; - document.getElementById("build-button").style.display = "none"; + document.getElementById("experiment-button").style.display = "none"; document.getElementById("splash").onclick = null; //removes the onclick effect so the function only runs once document.getElementById("splash").style.display = "none"; //hides the element that spawned the function document.getElementById("dmg").style.display = "inline"; @@ -510,6 +528,7 @@ const simulation = { b.removeAllGuns(); simulation.isNoPowerUps = false; tech.setupAllTech(); //sets tech to default values + tech.cancelCount = 0; for (i = 0, len = b.guns.length; i < len; i++) { //find which gun if (b.guns[i].name === "laser") b.guns[i].chooseFireMethod() if (b.guns[i].name === "nail gun") b.guns[i].chooseFireMethod() @@ -553,7 +572,7 @@ const simulation = { simulation.CDScale = 1; simulation.difficulty = 0; simulation.difficultyMode = Number(document.getElementById("difficulty-select").value) - build.isCustomSelection = false; + build.isExperimentSelection = false; simulation.clearNow = true; document.getElementById("text-log").style.opacity = 0; diff --git a/js/spawn.js b/js/spawn.js index 5971afe..1889605 100644 --- a/js/spawn.js +++ b/js/spawn.js @@ -1653,11 +1653,13 @@ const spawn = { } }; }, - sneaker(x, y, radius = 15 + Math.ceil(Math.random() * 25)) { + sneaker(x, y, radius = 15 + Math.ceil(Math.random() * 20)) { let me; mobs.spawn(x, y, 5, radius, "transparent"); me = mob[mob.length - 1]; - me.accelMag = 0.0007 * simulation.accelScale; + Matter.Body.setDensity(me, 0.002); //extra dense //normal is 0.001 //makes effective life much larger + me.accelMag = 0.001 * simulation.accelScale; + me.frictionAir = 0.01; me.g = 0.0002; //required if using 'gravity' me.stroke = "transparent"; //used for drawSneaker me.alpha = 1; //used in drawSneaker @@ -1680,7 +1682,7 @@ const spawn = { } } if (this.alpha > 0) { - if (this.alpha > 0.95) { + if (this.alpha > 0.7) { this.healthBar(); if (!this.canTouchPlayer) { this.canTouchPlayer = true; diff --git a/js/tech.js b/js/tech.js index d7c292a..ab51965 100644 --- a/js/tech.js +++ b/js/tech.js @@ -77,7 +77,7 @@ const tech = { }, haveGunCheck(name) { if ( - !build.isCustomSelection && + !build.isExperimentSelection && b.inventory.length > 2 && name !== b.guns[b.activeGun].name && Math.random() > 2 / (b.inventory.length + tech.isGunCycle * 3) //lower chance of tech specific to a gun if you have lots of guns @@ -928,7 +928,7 @@ const tech = { maxCount: 1, count: 0, allowed() { - return powerUps.research.count > 5 || build.isCustomSelection + return powerUps.research.count > 5 || build.isExperimentSelection }, requires: "at least 6 research", effect() { @@ -1489,7 +1489,7 @@ const tech = { maxCount: 1, count: 0, allowed() { - return mech.health < 0.5 || build.isCustomSelection + return mech.health < 0.5 || build.isExperimentSelection }, requires: "health below 60", effect() { @@ -1622,7 +1622,7 @@ const tech = { maxCount: 3, count: 0, allowed() { - return (mech.health < 0.7 || build.isCustomSelection) && !tech.isEnergyHealth + return (mech.health < 0.7 || build.isExperimentSelection) && !tech.isEnergyHealth }, requires: "not mass-energy equivalence", effect() { @@ -1660,7 +1660,7 @@ const tech = { maxCount: 1, count: 0, allowed() { - return powerUps.research.count > 0 || build.isCustomSelection + return powerUps.research.count > 0 || build.isExperimentSelection }, requires: "at least 1 research", effect() { @@ -1680,7 +1680,7 @@ const tech = { maxCount: 1, count: 0, allowed() { - return powerUps.research.count > 1 || build.isCustomSelection + return powerUps.research.count > 1 || build.isExperimentSelection }, requires: "at least 2 research", effect() { @@ -1985,13 +1985,43 @@ const tech = { tech.manyWorlds = false; } }, + { + name: "unified field theory", + description: "after switching guns
use a research to cycle your field", + maxCount: 1, + count: 0, + allowed() { + return (powerUps.research.count > 1 && b.inventory.length > 1) || build.isExperimentSelection + }, + requires: "at least 2 guns, and 2 research", + effect() { + tech.isGunSwitchField = true; + for (let i = tech.tech.length - 1; i > 0; i--) { + if (tech.tech[i].name === "unified field theory") { + const index = (mech.fieldMode === mech.fieldUpgrades.length - 1) ? 1 : mech.fieldMode + 1 + tech.tech[i].description = `after switching guns
use a research to cycle your field +
(next field: ${mech.fieldUpgrades[index].name})` + break + } + } + }, + remove() { + tech.isGunSwitchField = false; + for (let i = tech.tech.length - 1; i > 0; i--) { + if (tech.tech[i].name === "unified field theory") { + tech.tech[i].description = "after switching guns
use a research to cycle your field" + break + } + } + } + }, { name: "renormalization", description: "using a research for any purpose
has a 37% chance to spawn a research", maxCount: 1, count: 0, allowed() { - return (powerUps.research.count > 1 || build.isCustomSelection) && !tech.isSuperDeterminism && !tech.isRerollHaste + return (powerUps.research.count > 1 || build.isExperimentSelection) && !tech.isSuperDeterminism && !tech.isRerollHaste }, requires: "not superdeterminism or Ψ(t) collapse
at least 2 research", effect() { @@ -2007,7 +2037,7 @@ const tech = { maxCount: 1, count: 0, allowed() { - return (powerUps.research.count > 2 || build.isCustomSelection) && !tech.isDeterminism + return (powerUps.research.count > 2 || build.isExperimentSelection) && !tech.isDeterminism }, requires: "not determinism, at least 3 research", effect() { @@ -2027,7 +2057,7 @@ const tech = { maxCount: 1, count: 0, allowed() { - return powerUps.research.count > 4 || build.isCustomSelection + return powerUps.research.count > 4 || build.isExperimentSelection }, requires: "at least 5 research", effect() { @@ -2095,7 +2125,7 @@ const tech = { maxCount: 1, count: 0, allowed() { - return (tech.totalBots() > 5 || mech.fieldUpgrades[mech.fieldMode].name === "nano-scale manufacturing" || mech.fieldUpgrades[mech.fieldMode].name === "plasma torch" || mech.fieldUpgrades[mech.fieldMode].name === "pilot wave") && !tech.isEnergyHealth && !tech.isRewindAvoidDeath //build.isCustomSelection || + return (tech.totalBots() > 5 || mech.fieldUpgrades[mech.fieldMode].name === "nano-scale manufacturing" || mech.fieldUpgrades[mech.fieldMode].name === "plasma torch" || mech.fieldUpgrades[mech.fieldMode].name === "pilot wave") && !tech.isEnergyHealth && !tech.isRewindAvoidDeath //build.isExperimentSelection || }, requires: "bots > 5, plasma torch, nano-scale, pilot wave, not mass-energy equivalence, CPT", effect() { @@ -4212,5 +4242,6 @@ const tech = { isFireMoveLock: null, isRivets: null, isNeedles: null, - isExplodeRadio: null + isExplodeRadio: null, + isGunSwitchField: null } \ No newline at end of file diff --git a/style.css b/style.css index 694f705..074760d 100644 --- a/style.css +++ b/style.css @@ -103,7 +103,7 @@ summary { background-color: #e8e8ee; } -#build-button { +#experiment-button { position: absolute; bottom: 3px; right: 3px; @@ -217,7 +217,7 @@ summary { font-size: 0.65em; } -#build-grid { +#experiment-grid { /* align-content: space-between; */ padding: 1px; /* padding: 16px; */ @@ -238,7 +238,7 @@ summary { font-size: 1.3em; } -.build-grid-module { +.experiment-grid-module { margin: -1px; padding: 10px; line-height: 170%; @@ -256,7 +256,7 @@ summary { font-weight: 600; } -.build-grid-module:hover { +.experiment-grid-module:hover { background-color: #efeff5; } @@ -284,14 +284,14 @@ summary { background-color: hsl(253, 100%, 81%); } -.build-grid-disabled { +.experiment-grid-disabled { /* opacity: 0.5; */ background-color: var(--build-bg-color); color: rgba(0, 0, 0, 0.2); /* transition: background-color 1s, color 1s; */ } -.build-grid-disabled:hover { +.experiment-grid-disabled:hover { /* background-color: #fff; */ background-color: var(--build-bg-color); } @@ -461,7 +461,7 @@ summary { color: hsl(218, 100%, 58%); } -/* colors for pause, selection and custom */ +/* colors for pause, selection and experiment */ /* #text-log { z-index: 2; @@ -665,7 +665,7 @@ summary { } .lore { - animation: bgColor 10s linear infinite; + animation: bgColor 6s linear infinite; } @keyframes bgColor { diff --git a/todo.txt b/todo.txt index 96ac4e1..2fc07c5 100644 --- a/todo.txt +++ b/todo.txt @@ -1,15 +1,14 @@ ******************************************************** NEXT PATCH ******************************************************** -lore: chapter 1 and 2 are now somewhere in the game - lore is the same for all difficulty levels +duplication bug fix -testing mode and custom are now locked by default until you reach chapter 1 - or just hack the game to skip the lore and enable testing and custom - localSettings.loreCount = Infinity; - localStorage.setItem("localSettings", JSON.stringify(localSettings)); +renaming custom mode -> experimental mode + experimental mode is available again even without completing lore -bug fix: performance greatly improved on drawing multiple duplicated power ups +mob: sneaker is a bit faster and stronger + (the invisible one that attacks from stealth) +tech: unified field theory - switching guns uses a reroll to cycle your field ******************************************************** BUGS ******************************************************** @@ -31,8 +30,6 @@ bug fix: performance greatly improved on drawing multiple duplicated power ups ******************************************************** TODO ******************************************************** -chapter 3: why is the bot attacking things? - mechanic: use gun swap as an active ability this effect is spammable, so it needs a cost or a cooldown tech: @@ -42,6 +39,13 @@ mechanic: use gun swap as an active ability rewind, still uses energy cycle to next field, uses a reroll +chapter 3: why is the bot attacking things? +voice singing with pitch? + +bot: ice blast, long CD AOE freeze + +RPG default or tech: grenades detonate on your cursor / where your cursor was when they were fired + tech: double your rerolls set your duplication chance to zero requires 5 rerolls and 20% duplication chance @@ -210,12 +214,6 @@ tech- foam is attracted to mobs name - static cling could also do bremsstrahlung radiation like damage on attachment -field - one block orbits you, it can protect you a bit and do collision damage - use field to fire and press field again to pull it back - tech- more blocks - tech- attach a permanent neutron bomb to the block - lowers energy regen, but it can damage mobs - repeat map in vertical and horizontal space or at least vertical space camera looks strange when you teleport player with a high velocity