diff --git a/js/index.js b/js/index.js
index b5d8f87..82bb7e3 100644
--- a/js/index.js
+++ b/js/index.js
@@ -474,13 +474,14 @@ const build = {
// ${b.activeGun === null || b.activeGun === undefined ? "undefined" : b.guns[b.activeGun].name} (${b.activeGun === null || b.activeGun === undefined ? "0" : b.guns[b.activeGun].ammo})
+ //
+ //
+ //
+
let text = `
PAUSED
press ${input.key.pause} to resume
-
-
-
diff --git a/js/level.js b/js/level.js
index 3824917..37457e1 100644
--- a/js/level.js
+++ b/js/level.js
@@ -8,8 +8,8 @@ const level = {
defaultZoom: 1400,
onLevel: -1,
levelsCleared: 0,
- //see level.populateLevels: (initial, ... , reservoir or factory, reactor, ... , subway, final) added later
- playableLevels: ["labs", "rooftops", "skyscrapers", "warehouse", "highrise", "office", "aerie", "satellite", "sewers", "testChamber", "pavilion", "lock", "towers", "flocculation", "testChamber2"],
+ //see level.populateLevels: (initial, ... , (reservoir, factory, or gravityInterferometer), reactor, ... , subway, final) added later
+ playableLevels: ["labs", "rooftops", "skyscrapers", "warehouse", "highrise", "office", "aerie", "satellite", "sewers", "testChamber", "pavilion", "lock", "towers", "flocculation", "gravityObservatory"],
communityLevels: ["gauntlet", "stronghold", "basement", "crossfire", "vats", "run", "ngon", "house", "perplex", "coliseum", "tunnel", "islands", "temple", "dripp", "biohazard", "stereoMadness", "yingYang", "staircase", "fortress", "commandeer", "clock", "buttonbutton", "downpour", "superNgonBros", "underpass", "cantilever", "tlinat", "ruins", "ace", "crimsonTowers", "LaunchSite", "shipwreck", "unchartedCave", "dojo", "arena", "soft", "flappyGon", "rings", "trial"],
trainingLevels: ["walk", "crouch", "jump", "hold", "throw", "throwAt", "deflect", "heal", "fire", "nailGun", "shotGun", "superBall", "matterWave", "missile", "stack", "mine", "grenades", "harpoon"],
levels: [],
@@ -30,14 +30,14 @@ const level = {
// tech.tech[297].frequency = 100
// tech.addJunkTechToPool(0.5)
// m.couplingChange(10)
- // m.setField("time dilation") //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("standing wave") //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
// m.energy = 0
// simulation.molecularMode = 2
// m.damage(0.1);
- // b.giveGuns("super balls") //0 nail gun 1 shotgun 2 super balls 3 wave 4 missiles 5 grenades 6 spores 7 drones 8 foam 9 harpoon 10 mine 11 laser
+ // b.giveGuns("nail gun") //0 nail gun 1 shotgun 2 super balls 3 wave 4 missiles 5 grenades 6 spores 7 drones 8 foam 9 harpoon 10 mine 11 laser
// b.giveGuns("spores") //0 nail gun 1 shotgun 2 super balls 3 wave 4 missiles 5 grenades 6 spores 7 drones 8 foam 9 harpoon 10 mine 11 laser
// b.giveGuns("laser") //0 nail gun 1 shotgun 2 super balls 3 wave 4 missiles 5 grenades 6 spores 7 drones 8 foam 9 harpoon 10 mine 11 laser
// tech.laserColor = "#fff"
@@ -45,9 +45,9 @@ const level = {
// b.guns[8].ammo = 100000000
// requestAnimationFrame(() => { tech.giveTech("stimulated emission") });
- // tech.giveTech("mass-energy equivalence")
+ // tech.giveTech("dark matter")
// tech.addJunkTechToPool(0.5)
- // for (let i = 0; i < 1; ++i) tech.giveTech("the upside down")
+ // for (let i = 0; i < 1; ++i) tech.giveTech("entropic gravity")
// for (let i = 0; i < 1; ++i) tech.giveTech("nitinol")
// m.skin.egg();
@@ -59,13 +59,13 @@ const level = {
// for (let i = 0; i < 4; i++) powerUps.directSpawn(450, -50, "tech");
// 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.testChamber2();
+ // level.gravityInterferometer();
level[simulation.isTraining ? "walk" : "initial"]() //normal starting level **************************************************
- // for (let i = 0; i < 1; ++i) spawn.revolutionBoss(1900, -500)
- // for (let i = 0; i < 1; i++) spawn.starter(1900, -500, 20)
+ // for (let i = 0; i < 1; ++i) spawn.snakeBoss(1900, -500)
+ // for (let i = 0; i < 1; i++) spawn.mantisBoss(1900, -500)
// for (let i = 0; i < 1; ++i) powerUps.directSpawn(m.pos.x + 50 * Math.random(), m.pos.y + 50 * Math.random(), "entanglement");
// for (let i = 0; i < 2; ++i) powerUps.directSpawn(m.pos.x + 450, m.pos.y + 50 * Math.random(), "boost");
@@ -137,7 +137,7 @@ const level = {
}
level.newLevelOrPhase()
if (simulation.isTraining) {
- simulation.difficultyMode = 2
+ simulation.difficultyMode = 1
} else {
simulation.inGameConsole(`
level.onLevel
= "
${level.levels[level.onLevel]}"`);
document.title = "n-gon: " + level.levelAnnounce();
@@ -256,13 +256,19 @@ const level = {
customTopLayer() { },
updateDifficulty() {
simulation.difficulty = level.levelsCleared * simulation.difficultyMode
- if (simulation.isTraining) simulation.difficulty = 1
- const scale = simulation.difficultyMode > 3 ? 2 : 1
- m.dmgScale = Math.pow(0.85, level.levelsCleared * scale)
- simulation.dmgScale = Math.max(0.1, 0.25 * level.levelsCleared * scale) //damage done by mobs scales with total levels //a bigger number means the player takes more damage
- if (simulation.difficultyMode > 5) {
- m.dmgScale *= 0.5
- simulation.dmgScale *= 2
+ if (simulation.isTraining) {
+ simulation.difficulty = 1
+ simulation.difficultyMode = 1
+ m.dmgScale = 1
+ simulation.dmgScale = 1//Math.max(0.1, 0.25 * level.levelsCleared * scale) //damage done by mobs scales with total levels //a bigger number means the player takes more damage
+ } else {
+ const scale = simulation.difficultyMode > 3 ? 2 : 1
+ m.dmgScale = Math.pow(0.85, level.levelsCleared * scale)
+ simulation.dmgScale = Math.max(0.1, 0.25 * level.levelsCleared * scale) //damage done by mobs scales with total levels //a bigger number means the player takes more damage
+ if (simulation.difficultyMode > 5) {
+ m.dmgScale *= 0.5
+ simulation.dmgScale *= 2
+ }
}
simulation.healScale = 1 / (1 + simulation.difficulty * 0.043) //a higher denominator makes for lower heals // m.health += heal * simulation.healScale;
if (simulation.difficultyMode === 1) {
@@ -772,7 +778,8 @@ const level = {
}
level.levels = shuffle(level.levels); //shuffles order of maps with seeded random
level.levels.length = 9 //remove any extra levels past 9
- level.levels.splice(Math.floor(Math.seededRandom(level.levels.length * 0.6, level.levels.length)), 0, Math.random() < 0.5 ? "factory" : "reservoir"); //add level to the back half of the randomized levels list
+ pick = ["gravityInterferometer", "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, "reactor"); //add level to the back half of the randomized levels list
if (!build.isExperimentSelection || (build.hasExperimentalMode && !simulation.isCheating)) { //experimental mode is endless, unless you only have an experiment Tech
level.levels.unshift("initial"); //add level to the start of the randomized levels list
@@ -1548,7 +1555,7 @@ const level = {
},
}
},
- button(x, y, width = 126, isSpawnBase = true, isInvertedVertical = false) {
+ button(x, y, width = 126, isSpawnBase = true, isInvertedVertical = false, color = "hsl(0, 100%, 70%)") {
if (isSpawnBase) {
if (isInvertedVertical) {
spawn.mapVertex(x + 65, y - 3, "100 -10 -100 -10 -70 10 70 10");
@@ -1594,18 +1601,19 @@ const level = {
this.isUp = false;
}
},
- query() {
+ queryRemove() {
if (Matter.Query.region(body, this).length === 0 && Matter.Query.region([player], this).length === 0) {
this.isUp = true;
} else {
if (this.isUp === true) {
const list = Matter.Query.region(body, this) //are any blocks colliding with this
if (list.length > 0) {
- if (list[0].bounds.max.x - list[0].bounds.min.x < 150 && list[0].bounds.max.y - list[0].bounds.min.y < 150) { //not too big of a block
- Matter.Body.setPosition(list[0], { //teleport block to the center of the button
- x: this.min.x + width / 2,
- y: list[0].position.y
- })
+ Matter.Composite.remove(engine.world, list[0]);
+ for (let i = 0; i < body.length; i++) {
+ if (body[i] === list[0]) {
+ body.splice(i, 1);
+ break
+ }
}
Matter.Body.setVelocity(list[0], { x: 0, y: 0 });
}
@@ -1613,8 +1621,15 @@ const level = {
this.isUp = false;
}
},
+ queryPlayer() {
+ if (Matter.Query.region([player], this).length === 0) {
+ this.isUp = true;
+ } else {
+ this.isUp = false;
+ }
+ },
draw() {
- ctx.fillStyle = "hsl(0, 100%, 70%)"
+ ctx.fillStyle = color
if (this.isUp) {
ctx.fillRect(this.min.x, this.min.y, this.width, 20)
} else {
@@ -1654,18 +1669,20 @@ const level = {
this.isUp = false;
}
},
- query() {
+ queryRemove() {
if (Matter.Query.region(body, this).length === 0 && Matter.Query.region([player], this).length === 0) {
this.isUp = true;
} else {
if (this.isUp === true) {
const list = Matter.Query.region(body, this) //are any blocks colliding with this
if (list.length > 0) {
- if (list[0].bounds.max.x - list[0].bounds.min.x < 150 && list[0].bounds.max.y - list[0].bounds.min.y < 150) { //not too big of a block
- Matter.Body.setPosition(list[0], { //teleport block to the center of the button
- x: this.min.x + width / 2,
- y: list[0].position.y
- })
+ //delete triggering block
+ Matter.Composite.remove(engine.world, list[0]);
+ for (let i = 0; i < body.length; i++) {
+ if (body[i] === list[0]) {
+ body.splice(i, 1);
+ break
+ }
}
Matter.Body.setVelocity(list[0], { x: 0, y: 0 });
}
@@ -1673,8 +1690,15 @@ const level = {
this.isUp = false;
}
},
+ queryPlayer() {
+ if (Matter.Query.region([player], this).length === 0) {
+ this.isUp = true;
+ } else {
+ this.isUp = false;
+ }
+ },
draw() {
- ctx.fillStyle = "hsl(0, 100%, 70%)"
+ ctx.fillStyle = color
if (this.isUp) {
ctx.fillRect(this.min.x, this.min.y - 10, this.width, 20)
} else {
@@ -3376,7 +3400,7 @@ const level = {
}
//remove any mob that is too far from player
for (let i = 0; i < mob.length; ++i) {
- if (Vector.magnitudeSquared(Vector.sub(player.position, mob[i].position)) > 4000000) { //remove any mob farther then 2000 pixels from player
+ if (Vector.magnitudeSquared(Vector.sub(player.position, mob[i].position)) > 4000000 && !mob[i].isDarkMatter) { //remove any mob farther then 2000 pixels from player
mob[i].removeConsBB()
mob[i].removeCons()
mob[i].leaveBody = false
@@ -7025,7 +7049,8 @@ const level = {
}
},
- testChamber2() {
+ gravityInterferometer() {
+ level.isVerticalFLipLevel = true
mobs.maxMobBody = 20 //normally 40, but set to 10 to avoid too much clutter
simulation.fallHeight = 4000
level.announceMobTypes()
@@ -7133,13 +7158,13 @@ const level = {
spawn.mapRect(-4000, 2000, 8000, 3000); //floor
}
let buildNormalMap = function () {
- buttons.push(level.button(-1895, -1600))
+ buttons.push(level.button(-1895, -1600, 126, true, false, "hsl(330, 100%, 50%)"))
buttons[buttons.length - 1].isUp = false
spawn.mapRect(-1675, -2025, 50, 250);
simulation.ephemera.push({
name: "buttons up",
- count: flipAnimationCycles,
+ count: flipAnimationCycles + 30,
do() {
this.count--
if (this.count < 0) {
@@ -7161,6 +7186,10 @@ const level = {
balance.push(level.rotor(-750, 1700, 400, 25, 0.01, Math.PI / 2, 0.5))
balance.push(level.rotor(-275, 1650, 550, 32, 0.01, 0, 0.5))
+ lasers.push(level.laser({ x: -1625, y: -850 }, { x: 1980, y: -850 })) ////x, y, width, height, damage = 0.002)
+ spawn.mapRect(1980, -862, 25, 25); //laser entrance
+ balance.push(level.rotor(1000, -910, 550, 32, 0.01, 0, 0.5))
+
//left side
//level entrance
spawn.mapRect(level.enter.x, level.enter.y + 20, 100, 20);
@@ -7214,13 +7243,13 @@ const level = {
}
let buildVerticalFLippedMap = function () { // flip Y with this -> spawn.mapRect(x, -y - h, w, h);
- buttons.push(level.button(-1895, 1600, 126, true, true))
+ buttons.push(level.button(-1895, 1600, 126, true, true, "hsl(330, 100%, 50%)"))
buttons[buttons.length - 1].isUp = false
spawn.mapRect(-1675, 2025 - 250, 50, 250);
simulation.ephemera.push({
name: "buttons up",
- count: flipAnimationCycles,
+ count: flipAnimationCycles + 30,
do() {
this.count--
if (this.count < 0) {
@@ -7242,6 +7271,11 @@ const level = {
balance.push(level.rotor(-750, -1700 - 25, 400, 25, 0.01, Math.PI / 2, 0.5))
balance.push(level.rotor(-250, -1650 - 32, 500, 32, 0.01, 0, 0.5))
+ lasers.push(level.laser({ x: -1625, y: 850 }, { x: 1980, y: 850 })) ////x, y, width, height, damage = 0.002)
+ spawn.mapRect(1980, 862 - 25, 25, 25); //laser entrance
+ balance.push(level.rotor(1000, 910 - 32, 550, 32, 0.01, 0, 0.5))
+
+
//left side
//level entrance
spawn.mapRect(level.enter.x, level.enter.y - 20 - 20, 100, 20);
@@ -7329,8 +7363,24 @@ const level = {
m.history[i].angle *= -1
m.history[i].velocity.y *= -1
}
- //stun to wipe history of all mobs, so they don't get confused about player position vertical swap
- for (let i = 0; i < mob.length; i++) mobs.statusStun(mob[i], 1)
+ for (let i = 0; i < mob.length; i++) {
+ //stun to wipe history of all mobs, so they don't get confused about player position vertical swap
+ mobs.statusStun(mob[i], 1)
+ //edge cases
+ if (mob[i].history) {
+ for (let j = 0; j < mob[i].history.length; j++) mob[i].history[j].y *= -1
+ }
+ if (mob[i].laserArray) {
+ for (let j = 0; j < mob[i].laserArray.length; j++) {
+ mob[i].laserArray[j].a.y *= -1
+ mob[i].laserArray[j].b.y *= -1
+ }
+ }
+ if (mob[i].springTarget2) {
+ mob[i].springTarget.y *= -1
+ mob[i].springTarget2.y *= -1
+ }
+ }
}
buildMapOutline()
buildNormalMap()
@@ -7339,7 +7389,8 @@ const level = {
for (let i = 0; i < buttons.length; i++) {
buttons[i].draw()
if (buttons[i].isUp && !isFlipping) {
- buttons[i].query();
+ // buttons[i].query();
+ buttons[i].queryPlayer();
if (!buttons[i].isUp) {
isFlipping = true
if (isFlipped) {
@@ -7444,7 +7495,7 @@ const level = {
}
}
ctx.fill();
- ctx.fillStyle = `rgba(255,255,255,${0 + 0.3 * Math.random()})` //balances center dot
+ ctx.fillStyle = `rgba(255,255,255,${0 + 0.3 * Math.random()})`
if (isFlipped) {
ctx.fillRect(-2025, 2025 - 450, 400, 450);
//shadows
@@ -7518,6 +7569,351 @@ const level = {
spawn.randomLevelBoss(-875, -200);
powerUps.addResearchToLevel() //needs to run after mobs are spawned
},
+ gravityObservatory() {
+ mobs.maxMobBody = 25 //normally 40, but set lower to avoid too much clutter
+ level.isVerticalFLipLevel = true
+ simulation.fallHeight = 4000
+ level.announceMobTypes()
+ level.setPosToSpawn(-2375, 950);
+ level.exit.x = 3750
+ level.exit.y = 165
+ level.defaultZoom = 2600
+ simulation.zoomTransition(level.defaultZoom)
+ document.body.style.backgroundColor = "#c3d6e1";
+ color.map = "#444"
+
+ let buttons = []
+ let isFlipped = false;
+ let isFlipping = false;
+ const flipAnimationCycles = 60
+
+ let buildMapOutline = function () {
+ //boxes center on zero,zero with deep walls to hide background
+ spawn.mapRect(4000, -2000, 2000, 4000); //right map wall
+ spawn.mapRect(-6000, -2000, 2000, 4000); //left map wall
+ spawn.mapRect(-6000, -4000, 12000, 3000); //map ceiling
+ spawn.mapRect(-6000, 1000, 12000, 3000); //floor
+ }
+ let buildNormalMap = function () {
+ buttons.push(level.button(-3350, 985, 126, true, false, "hsl(330, 100%, 50%)"))
+ buttons.push(level.button(-3350, -985, 126, true, true, "hsl(330, 100%, 50%)"))
+ buttons.push(level.button(150, 985, 126, true, false, "hsl(330, 100%, 50%)"))
+ buttons.push(level.button(150, -985, 126, true, true, "hsl(330, 100%, 50%)"))
+ buttons.push(level.button(3725, 985, 126, true, false, "hsl(330, 100%, 50%)"))
+ buttons.push(level.button(3725, -985, 126, true, true, "hsl(330, 100%, 50%)"))
+ for (let i = 0; i < buttons.length; i++) buttons[i].isUp = false
+ simulation.ephemera.push({
+ name: "buttons up",
+ count: flipAnimationCycles,
+ do() {
+ this.count--
+ if (this.count < 0) {
+ for (let i = 0; i < buttons.length; i++) buttons[i].isUp = true
+ simulation.removeEphemera(this.name);
+ isFlipping = false
+ }
+ },
+ })
+ //far left zone
+ spawn.mapRect(-2575, 987, 375, 100);
+ spawn.mapRect(-2575, -600, 600, 1325);
+ spawn.mapRect(-2200, 650, 225, 475);
+ spawn.mapRect(-2575, 700, 35, 125);
+ spawn.mapRect(-3500, -1050, 425, 63);
+ spawn.mapRect(-3500, 987, 425, 50);
+ spawn.mapVertex(-2275, -1000, "-400 0 -300 150 300 150 400 0");
+
+ spawn.mapVertex(-3287, 0, "-213 -500 0 -550 213 -500 213 500 0 550 -213 500");
+ spawn.mapVertex(-3750, -100, "-100 -200 -50 -250 50 -250 100 -200 100 200 50 250 -50 250 -100 200");
+ spawn.mapVertex(-2825, 0, "-100 -400 -50 -450 50 -450 100 -400 100 400 50 450 -50 450 -100 400");
+
+ //dense center left zone
+ spawn.mapVertex(-1150, -750, "400 -75 425 0 400 75 -400 75 -425 0 -400 -75");
+ spawn.mapVertex(-550, -450, "400 -75 425 0 400 75 -400 75 -425 0 -400 -75");
+
+ spawn.mapVertex(-1685, 153, "-150 -500 0 -550 150 -500 150 750 -150 450");
+ spawn.mapVertex(-1106, 707, "500 -150 550 0 500 150 -500 150 -800 -150");
+ spawn.mapRect(-1645, 470, 200, 200);
+ Matter.Body.setAngle(map[map.length - 1], Math.PI / 4)
+ spawn.mapRect(-2085, 910, 200, 200);
+ Matter.Body.setAngle(map[map.length - 1], Math.PI / 4)
+
+ //open center right area with both bosses
+ // spawn.mapRect(0, -450, 425, 1100);
+ spawn.mapVertex(213, 0, "-213 -700 0 -650 213 -600 213 700 0 650 -213 600");
+ spawn.mapRect(0, -1050, 425, 63);
+ spawn.mapRect(0, 987, 425, 50);
+ spawn.mapVertex(1700, -1000, "-600 0 -400 400 400 400 600 0");
+ spawn.mapVertex(2800, -1000, "-500 0 -400 150 400 150 500 0");
+ spawn.mapVertex(1700, 700, "-400 -100 -450 0 -400 100 400 100 450 0 400 -100");
+ spawn.mapVertex(2800, 375, "-400 -100 -450 0 -400 100 400 100 450 0 400 -100");
+
+ //far right exit structure
+ spawn.mapRect(3575, -1050, 425, 63);
+ spawn.mapRect(3575, 987, 425, 50);
+ spawn.mapVertex(3840, 450, "-250 -300 250 -300 250 300 -250 100");
+ spawn.mapVertex(3840, -450, "-250 300 250 300 250 -300 -250 -100");
+ spawn.mapRect(3750, 185, 100, 25);
+ }
+ let buildVerticalFLippedMap = function () { // flip Y with this -> spawn.mapRect(x, -y - h, w, h);
+ buttons.push(level.button(-3350, 985, 126, true, false, "hsl(330, 100%, 50%)"))
+ buttons.push(level.button(-3350, -985, 126, true, true, "hsl(330, 100%, 50%)"))
+ buttons.push(level.button(150, 985, 126, true, false, "hsl(330, 100%, 50%)"))
+ buttons.push(level.button(150, -985, 126, true, true, "hsl(330, 100%, 50%)"))
+ buttons.push(level.button(3725, 985, 126, true, false, "hsl(330, 100%, 50%)"))
+ buttons.push(level.button(3725, -985, 126, true, true, "hsl(330, 100%, 50%)"))
+ for (let i = 0; i < buttons.length; i++) buttons[i].isUp = false
+
+ simulation.ephemera.push({
+ name: "buttons up",
+ count: flipAnimationCycles,
+ do() {
+ this.count--
+ if (this.count < 0) {
+ for (let i = 0; i < buttons.length; i++) buttons[i].isUp = true
+ simulation.removeEphemera(this.name);
+ isFlipping = false
+ }
+ },
+ })
+
+ //far left zone
+ spawn.mapRect(-2575, -1087, 375, 100);
+ spawn.mapRect(-2575, 600 - 1325, 600, 1325);
+ spawn.mapRect(-2200, -650 - 475, 225, 475);
+ spawn.mapRect(-2575, -700 - 125, 35, 125);
+ spawn.mapRect(-3500, 1050 - 63, 425, 63);
+ spawn.mapRect(-3500, -987 - 50, 425, 50);
+ spawn.mapVertex(-2275, 1000, "-300 0 -400 150 400 150 300 0");
+
+ spawn.mapVertex(-3287, 0, "-213 -500 0 -550 213 -500 213 500 0 550 -213 500");
+ spawn.mapVertex(-3750, 100, "-100 -200 -50 -250 50 -250 100 -200 100 200 50 250 -50 250 -100 200");
+ spawn.mapVertex(-2825, 0, "-100 -400 -50 -450 50 -450 100 -400 100 400 50 450 -50 450 -100 400");
+
+ //dense center left zone
+ spawn.mapVertex(-1150, 750, "400 -75 425 0 400 75 -400 75 -425 0 -400 -75");
+ spawn.mapVertex(-550, 450, "400 -75 425 0 400 75 -400 75 -425 0 -400 -75");
+
+ spawn.mapVertex(-1685, -153, "-150 500 0 550 150 500 150 -750 -150 -450");
+ spawn.mapVertex(-1106, -707, "500 150 550 0 500 -150 -500 -150 -800 150");
+ spawn.mapRect(-1645, -470 - 200, 200, 200);
+ Matter.Body.setAngle(map[map.length - 1], Math.PI / 4)
+ spawn.mapRect(-2085, -910 - 200, 200, 200);
+ Matter.Body.setAngle(map[map.length - 1], Math.PI / 4)
+
+ //open center right area with both bosses
+ spawn.mapVertex(213, 0, "-213 -600 0 -650 213 -700 213 600 0 650 -213 700");
+ spawn.mapRect(0, 1050 - 63, 425, 63);
+ spawn.mapRect(0, -987 - 50, 425, 50);
+ spawn.mapVertex(1700, 1000, "-400 0 -600 400 600 400 400 0");
+ spawn.mapVertex(2800, 1000, "-400 0 -500 150 500 150 400 0");
+ spawn.mapVertex(1700, -700, "-400 -100 -450 0 -400 100 400 100 450 0 400 -100");
+ spawn.mapVertex(2800, -375, "-400 -100 -450 0 -400 100 400 100 450 0 400 -100");
+ //far right building like exit structure
+ spawn.mapRect(3575, 1050 - 63, 425, 63);
+ spawn.mapRect(3575, -987 - 50, 425, 50);
+ spawn.mapVertex(3840, 450, "-250 -300 250 -300 250 300 -250 100");
+ spawn.mapVertex(3840, -450, "-250 300 250 300 250 -300 -250 -100");
+ spawn.mapRect(3750, -210, 100, 25);
+ }
+ let flipAndRemove = function () {
+ simulation.translatePlayerAndCamera({ x: player.position.x, y: -player.position.y })
+ level.enter.y = -level.enter.y
+ level.exit.y = -level.exit.y
+ for (let i = body.length - 1; i > -1; i--) {
+ if (body[i].isRotor) body.splice(i, 1);
+ }
+
+ function removeAll(array) {
+ for (let i = 0; i < array.length; ++i) Matter.Composite.remove(engine.world, array[i]);
+ }
+ removeAll(map);
+ map = [];
+ removeAll(buttons);
+ buttons = []
+
+ function invertVertical(array) {
+ for (let i = 0; i < array.length; ++i) {
+ Matter.Body.setPosition(array[i], { x: array[i].position.x, y: -array[i].position.y })
+ }
+ }
+ invertVertical(body);
+ invertVertical(powerUp);
+ invertVertical(bullet);
+ invertVertical(mob);
+ //fields
+ if (m.fieldMode === 9 && m.hole.isOn) {
+ m.hole.pos1.y *= -1
+ m.hole.pos2.y *= -1
+ } else if (m.fieldMode === 2) {
+ m.fieldPosition.y *= -1
+ m.fieldAngle *= -1
+ }
+ //history
+ for (let i = 0; i < m.history.length; i++) {
+ m.history[i].position.y *= -1
+ m.history[i].angle *= -1
+ m.history[i].velocity.y *= -1
+ }
+ for (let i = 0; i < mob.length; i++) {
+ //stun to wipe history of all mobs, so they don't get confused about player position vertical swap
+ mobs.statusStun(mob[i], 1)
+ //edge cases
+ if (mob[i].history) {
+ for (let j = 0; j < mob[i].history.length; j++) mob[i].history[j].y *= -1
+ }
+ if (mob[i].laserArray) {
+ for (let j = 0; j < mob[i].laserArray.length; j++) {
+ mob[i].laserArray[j].a.y *= -1
+ mob[i].laserArray[j].b.y *= -1
+ }
+ }
+ if (mob[i].springTarget2) {
+ mob[i].springTarget.y *= -1
+ mob[i].springTarget2.y *= -1
+ }
+ }
+ }
+ buildMapOutline()
+ buildNormalMap()
+ level.custom = () => {
+ //stuff floats near buttons
+ if ((player.position.x > -3505 && player.position.x < -3075) ||
+ (player.position.x > 0 && player.position.x < 425) ||
+ (player.position.x > 3575)) {
+ if (player.position.y > 0) {
+ player.force.y -= 0.8 * simulation.g * player.mass
+ }
+ }
+ for (let i = 0; i < body.length; i++) {
+ if ((body[i].position.x > -3505 && body[i].position.x < -3075) ||
+ (body[i].position.x > 0 && body[i].position.x < 425) ||
+ (body[i].position.x > 3575)
+ ) {
+ if (body[i].position.y > 0) {
+ body[i].force.y -= 1.04 * simulation.g * body[i].mass
+ } else {
+ body[i].force.y += 1.04 * simulation.g * body[i].mass
+ }
+ }
+ }
+ for (let i = 0; i < powerUp.length; i++) {
+ if ((powerUp[i].position.x > -3505 && powerUp[i].position.x < -3075) ||
+ (powerUp[i].position.x > 0 && powerUp[i].position.x < 425) ||
+ (powerUp[i].position.x > 3575)
+ ) {
+ if (powerUp[i].position.y > 0) {
+ powerUp[i].force.y -= 1.04 * simulation.g * powerUp[i].mass
+ } else {
+ powerUp[i].force.y += 1.04 * simulation.g * powerUp[i].mass
+ }
+ }
+ }
+ for (let i = 0; i < buttons.length; i++) {
+ buttons[i].draw()
+ if (buttons[i].isUp && !isFlipping) {
+ // buttons[i].query();
+ buttons[i].queryPlayer();
+ if (!buttons[i].isUp) {
+ isFlipping = true
+ if (isFlipped) {
+ const normalMap = function () {
+ isFlipped = false
+ flipAndRemove()
+ buildMapOutline()
+ buildNormalMap(); //rewrite flipped version of map
+ simulation.draw.setPaths() //update map graphics
+ level.addToWorld()
+ }
+ simulation.unFlipCameraVertical(flipAnimationCycles, normalMap)
+ } else {
+ const flipMap = function () {
+ isFlipped = true
+ flipAndRemove()
+ buildMapOutline()
+ buildVerticalFLippedMap(); //rewrite flipped version of map
+ simulation.draw.setPaths() //update map graphics
+ level.addToWorld()
+ }
+ simulation.flipCameraVertical(flipAnimationCycles, flipMap)
+ }
+ break
+ }
+ }
+ }
+ ctx.fillStyle = "#d4f4f4"
+ ctx.fillRect(3575, -300, 475, 575);
+ if (isFlipped) {
+ //draw flipped entrance
+ ctx.beginPath();
+ ctx.moveTo(level.enter.x, level.enter.y - 30);
+ ctx.lineTo(level.enter.x, level.enter.y + 80);
+ ctx.bezierCurveTo(level.enter.x, level.enter.y + 170, level.enter.x + 100, level.enter.y + 170, level.enter.x + 100, level.enter.y + 80);
+ ctx.lineTo(level.enter.x + 100, level.enter.y - 30);
+ ctx.lineTo(level.enter.x, level.enter.y - 30);
+ ctx.fillStyle = "#ccc";
+ ctx.fill();
+ //draw flipped exit
+ ctx.fillStyle = "#d4f4f4"
+ // ctx.fillRect(-2000, 1325, 375, 350)
+ ctx.beginPath();
+ ctx.moveTo(level.exit.x, level.exit.y - 30);
+ ctx.lineTo(level.exit.x, level.exit.y + 80);
+ ctx.bezierCurveTo(level.exit.x, level.exit.y + 170, level.exit.x + 100, level.exit.y + 170, level.exit.x + 100, level.exit.y + 80);
+ ctx.lineTo(level.exit.x + 100, level.exit.y - 30);
+ ctx.lineTo(level.exit.x, level.exit.y - 30);
+ ctx.fillStyle = "#0ff";
+ ctx.fill();
+ } else {
+ level.exit.drawAndCheck();
+ level.enter.draw();
+ }
+ };
+ level.customTopLayer = () => {
+ ctx.fillStyle = `rgba(255,255,255,${0 + 0.3 * Math.random()})`
+ ctx.fillRect(-3500, -1075, 425, 2100);
+ ctx.fillRect(0, -1075, 425, 2100);
+ ctx.fillRect(3575, -1075, 425, 2100);
+ ctx.fillStyle = "rgba(0,0,0,0.08)"
+ ctx.fillRect(-2575, -1025, 600, 2050);
+ ctx.fillRect(1300, -1050, 800, 2100);
+ ctx.fillRect(2400, -1050, 800, 2100);
+ };
+
+ // spawn.bodyRect(1900, 1875, 100, 125, 0.5);
+ spawn.bodyRect(-2569, 825, 25, 165);
+
+ spawn.randomMob(-3750, -400, 0.2);
+ spawn.randomMob(-3425, -600, 0.2);
+ spawn.randomMob(-3225, -625, 0.3);
+ spawn.randomMob(-2850, -500, 0.3);
+ spawn.randomMob(-2450, -675, 0.3);
+ spawn.randomMob(-2150, -650, 0.4);
+ spawn.randomMob(-1650, -500, 0.4);
+ spawn.randomMob(-1325, 275, 0.4);
+ spawn.randomMob(-825, 425, 0.5);
+ spawn.randomMob(-400, -575, 0.5);
+ spawn.randomMob(-1275, -900, 0.5);
+ spawn.randomMob(-675, -775, 0.6);
+ spawn.randomMob(150, -725, 0.6);
+ spawn.randomMob(475, 925, 0.7);
+ spawn.randomMob(1500, 550, 0.7);
+ spawn.randomMob(1850, 500, 0.7);
+ spawn.randomMob(2025, 925, 0.8);
+ spawn.randomMob(1575, 875, 0.8);
+ spawn.randomMob(2650, 650, 0.9);
+ spawn.randomMob(3100, 700, 0.9);
+ spawn.randomMob(3050, 100, 1);
+ spawn.randomMob(2350, 100, 1);
+ spawn.randomMob(3400, 875, 1);
+ spawn.randomMob(3375, -725, 1);
+ spawn.randomMob(3925, 100, 1);
+
+ powerUps.spawnStartingPowerUps(-825, -600);
+ spawn.randomLevelBoss(1550, 200);
+ spawn.secondaryBossChance(2675, -125)
+ powerUps.addResearchToLevel() //needs to run after mobs are spawned
+ },
lock() {
level.announceMobTypes()
level.setPosToSpawn(0, -65); //lower start
@@ -18624,7 +19020,7 @@ const level = {
const door3 = level.door(20238, -781.4, 88, 452, 412)
const hazard2 = level.hazard(2550, -150, 10, 0.4) //y=-1485
- simulation.enableConstructMode()
+ // simulation.enableConstructMode()
level.setPosToSpawn(0, -50); //normal spawn
level.exit.x = 15316;
level.exit.y = -30;
@@ -21089,7 +21485,7 @@ const level = {
ctx.fillRect(1675, -2325, 250, 75);
ctx.fillRect(2700, -2525, 25, 150);
};
- simulation.enableConstructMode()
+ // simulation.enableConstructMode()
level.setPosToSpawn(0, -50); //normal spawn
level.exit.x = 23885;
level.exit.y = 800;
@@ -21221,7 +21617,7 @@ const level = {
const slime2 = level.hazard(2400, -2100, 200, 2100);
const slime3 = level.hazard(2600, -2100, 3600, 200);
const slime4 = level.hazard(6400, -2100, 3600, 200);
- simulation.enableConstructMode()
+ // simulation.enableConstructMode()
level.setPosToSpawn(0, -50); //normal spawn
level.exit.x = 13130.3;
let rainCount = 1
@@ -24060,7 +24456,7 @@ const level = {
}
}
};
- simulation.enableConstructMode() //landgreen if you see this can you remove im probably gonna forget
+ // simulation.enableConstructMode() //landgreen if you see this can you remove im probably gonna forget
for (let i = 0; i < spawn.bossTypeSpawnOrder.length * Math.random(); i++) {
spawn.bossTypeSpawnOrder.splice(i * Math.floor(Math.random() * spawn.bossTypeSpawnOrder.length), 1, "restoreBoss") //meh good enough
}
@@ -24212,7 +24608,7 @@ const level = {
}
Composite.add(engine.world, me.constraint);
}
- simulation.enableConstructMode()
+ // simulation.enableConstructMode()
let firstMobsSpawned = 1
let secondMobsSpawned = 0
let thirdMobsSpawned = 0
@@ -27153,7 +27549,7 @@ const level = {
const door3 = level.door(20238, -781.4, 88, 452, 412)
//y=-1485
- simulation.enableConstructMode()
+ // simulation.enableConstructMode()
level.setPosToSpawn(0, -50); //normal spawn
level.exit.x = 15316;
level.exit.y = -84;
@@ -36285,10 +36681,9 @@ const level = {
}
for (let i = 0; i < 2; i++) {
spawn.spinner(1300 + i, -3000 - 200 * i, 25 + 5 * i)
- Matter.Body.setVelocity(mob[mob.length - 1], {
- x: 0,
- y: 62
- });
+ const who = mob[mob.length - 1]
+ Matter.Body.setVelocity(who, { x: 0, y: 62 });
+ who.isDropPowerUp = false
}
spawn.mapRect(-2750, -2800, 2600, 4600); //left wall
@@ -36373,6 +36768,7 @@ const level = {
}
for (let i = 0; i < 3; i++) {
spawn.hopper(1300 + i, -3000 - 2000 * i, 25 + 5 * i)
+ mob[mob.length - 1].isDropPowerUp = false
// Matter.Body.setVelocity(mob[mob.length - 1], { x: 0, y: 0 });
}
spawn.mapRect(-2750, -2800, 2600, 4600); //left wall
@@ -36455,6 +36851,7 @@ const level = {
}
for (let i = 0; i < 6; i++) {
spawn.spawner(i * 230, -800)
+ mob[mob.length - 1].isDropPowerUp = false
// Matter.Body.setVelocity(mob[mob.length - 1], { x: 0, y: 0 });
}
spawn.mapVertex(510, -430, "725 0 725 80 -650 80 -650 -80 650 -80"); //upper room with mobs
@@ -36538,6 +36935,7 @@ const level = {
}
for (let i = 0; i < 6; i++) {
spawn.springer(i * 200, -800)
+ mob[mob.length - 1].isDropPowerUp = false
// Matter.Body.setVelocity(mob[mob.length - 1], { x: 0, y: 0 });
}
spawn.springer(1825, -330, 20);
@@ -36627,6 +37025,7 @@ const level = {
}
for (let i = 0; i < 10; i++) {
spawn.springer(2100 + i * 100, -250)
+ mob[mob.length - 1].isDropPowerUp = false
// Matter.Body.setVelocity(mob[mob.length - 1], { x: 0, y: 0 });
}
diff --git a/js/mob.js b/js/mob.js
index 2826db4..35eb6bd 100644
--- a/js/mob.js
+++ b/js/mob.js
@@ -658,29 +658,6 @@ const mobs = {
this.cons.length = 100 + 1.5 * this.radius;
this.cons2.length = 100 + 1.5 * this.radius;
}
-
-
-
- // if (!(simulation.cycle % (this.seePlayerFreq * 2))) {
- // const unit = Vector.normalise(Vector.sub(this.seePlayer.position, this.position))
- // const goal = Vector.add(this.position, Vector.mult(unit, stepRange))
- // this.springTarget.x = goal.x;
- // this.springTarget.y = goal.y;
- // // this.springTarget.x = this.seePlayer.position.x;
- // // this.springTarget.y = this.seePlayer.position.y;
- // this.cons.length = -200;
- // this.cons2.length = 100 + 1.5 * this.radius;
- // } else if (!(simulation.cycle % this.seePlayerFreq)) {
- // const unit = Vector.normalise(Vector.sub(this.seePlayer.position, this.position))
- // const goal = Vector.add(this.position, Vector.mult(unit, stepRange))
- // this.springTarget2.x = goal.x;
- // this.springTarget2.y = goal.y;
- // // this.springTarget2.x = this.seePlayer.position.x;
- // // this.springTarget2.y = this.seePlayer.position.y;
- // this.cons.length = 100 + 1.5 * this.radius;
- // this.cons2.length = -200;
- // }
-
}
}
}
diff --git a/js/player.js b/js/player.js
index 6398a3e..861e610 100644
--- a/js/player.js
+++ b/js/player.js
@@ -691,8 +691,8 @@ const m = {
return
}
m.lastHarmCycle = m.cycle
- if (tech.isDroneOnDamage && bullet.length < 150) { //chance to build a drone on damage from tech
- const len = Math.min((dmg - 0.06 * Math.random()) * 80, 60) / tech.droneEnergyReduction * (tech.isEnergyHealth ? 0.5 : 1)
+ if (tech.isDroneOnDamage && bullet.length < 180) { //chance to build a drone on damage from tech
+ const len = Math.min((dmg - 0.045 * Math.random()) * 95, 65) / tech.droneEnergyReduction * (tech.isEnergyHealth ? 0.5 : 1)
for (let i = 0; i < len; i++) {
if (Math.random() < 0.5) b.drone({
x: m.pos.x + 30 * Math.cos(m.angle) + 100 * (Math.random() - 0.5),
@@ -3074,6 +3074,51 @@ const m = {
}
},
minEnergyToDeflect: 0.05,
+ bulletsToBlocks(who) {
+ if (who.isMobBullet && !who.isInvulnerable && who.mass < 10 && body.length < mobs.maxMobBody) {
+ // spawn block
+ body[body.length] = Matter.Bodies.polygon(who.position.x, who.position.y, who.vertices.length, who.radius, {
+ friction: 0.05,
+ frictionAir: 0.001,
+ collisionFilter: {
+ category: cat.bullet,
+ mask: cat.map | cat.body | cat.bullet | cat.mob | cat.mobBullet | cat.mobShield
+ },
+ classType: "body",
+ isPrinted: true,
+ radius: 10, //used to grow and warp the shape of the block
+ density: 0.002, //double density for 2x damage
+ });
+ const block = body[body.length - 1]
+ Composite.add(engine.world, block); //add to world
+ //reverse velocity and make sure it's above 40
+ const unit = Vector.mult(Vector.normalise(who.velocity), -Math.max(40, who.speed))
+ Matter.Body.setVelocity(block, unit);
+
+ simulation.ephemera.push({
+ name: "remove block",
+ count: 120, //cycles before it self removes
+ do() {
+ this.count--
+ if (this.count < 0) {
+ simulation.removeEphemera(this.name)
+ Matter.Composite.remove(engine.world, block);
+ //find block
+ for (let i = 0; i < body.length; i++) {
+ if (body[i] === block) {
+ body.splice(i, 1);
+ break
+ }
+ }
+
+ }
+ },
+ })
+ //remove mob bullet
+ Matter.Composite.remove(engine.world, who); //remove from physics early to avoid collisions with block
+ who.alive = false
+ }
+ },
pushMass(who, fieldBlockCost = (0.025 + Math.sqrt(who.mass) * Vector.magnitude(Vector.sub(who.velocity, player.velocity)) * 0.002) * m.fieldShieldingScale) {
if (m.energy > m.minEnergyToDeflect) { //shield needs at least some of the cost to block
if (who.isShielded) fieldBlockCost *= 2; //shielded mobs take more energy to block
@@ -3108,6 +3153,7 @@ const m = {
}
}
}
+ m.bulletsToBlocks(who)
const unit = Vector.normalise(Vector.sub(player.position, who.position))
if (tech.blockDmg) {
Matter.Body.setVelocity(who, { x: 0.5 * who.velocity.x, y: 0.5 * who.velocity.y });
@@ -3282,7 +3328,8 @@ const m = {
case 6: //time dilation
return `
+${(1 + 0.05 * couple).toFixed(2)}x longer
stopped time` //
movement,
jumping, and
case 7: //cloaking
- return `
${(1 + 3.3 * couple).toFixed(3)}x ambush
damage`
+ // return `
${(1 + 3.3 * couple).toFixed(3)}x ambush
damage`
+ return `
${(1 + 0.05 * couple).toFixed(3)}x ambush
damage`
case 8: //pilot wave
return `
${(1 + 0.05 * couple).toFixed(2)}x block collision
damage`
case 9: //wormhole
@@ -3577,6 +3624,7 @@ const m = {
ctx.stroke();
}
}
+ m.bulletsToBlocks(mob[i])
if (tech.isStunField) mobs.statusStun(mob[i], tech.isStunField)
//mob knock backs
const massRoot = Math.sqrt(Math.max(1, mob[i].mass));
@@ -4809,7 +4857,7 @@ const m = {
}
this.drawRegenEnergyCloaking()
if (m.isSneakAttack && m.sneakAttackCycle + Math.min(100, 0.66 * (m.cycle - m.enterCloakCycle)) > m.cycle) { //show sneak attack status
- m.fieldDamage = 4.5 * (1 + 0.033 * m.coupling)
+ m.fieldDamage = 4.5 * (1 + 0.05 * m.coupling)
const timeLeft = (m.sneakAttackCycle + Math.min(100, 0.66 * (m.cycle - m.enterCloakCycle)) - m.cycle) * 0.5
ctx.beginPath();
ctx.arc(m.pos.x, m.pos.y, 32, 0, 2 * Math.PI);
diff --git a/js/powerup.js b/js/powerup.js
index 677f82a..ff002e8 100644
--- a/js/powerup.js
+++ b/js/powerup.js
@@ -351,9 +351,10 @@ const powerUps = {
name: "instructions",
color: "rgba(100,125,140,0.35)",
size() {
- return 150
+ return 130
},
effect() {
+ Matter.Body.setVelocity(player, { x: 0, y: 0 });//power up is so big it launches the player, this stops that
requestAnimationFrame(() => { //add a background behind the power up menu
ctx.fillStyle = `rgba(150,150,150,0.9)`;
ctx.fillRect(0, 0, canvas.width, canvas.height);
@@ -372,7 +373,7 @@ const powerUps = {
document.getElementById("choose-grid").classList.remove('choose-grid');
document.getElementById("choose-grid").style.gridTemplateColumns = "800px"//adjust this to increase the width of the whole menu, but mostly the center column
let lore = localSettings.loreCount > 0 ? "lore.unlockTesting() //press T to enter testing" : ""
- let text = `
//console commands
+ let text = ` //console commands
powerUps.instructions.effect() //reproduce this message
tech.giveTech("name") //replace "name" with tech name
m.setField("name") //standing wave perfect diamagnetism negative mass molecular assembler plasma torch time dilation metamaterial cloaking pilot wave wormhole grappling hook
@@ -382,17 +383,13 @@ const powerUps = {
m.energy = 0 //set energy
m.health = 1 //set health
m.maxHealth = 1 //set max health
- m.energy = 1 //set energy
m.maxEnergy = 1 //set max energy
simulation.enableConstructMode() //press T to build with mouse
${lore}
-
- //tech gun field heal ammo research coupling boost instructions entanglement
- powerUps.spawn(m.pos.x, m.pos.y, "name")
+ powerUps.spawn(m.pos.x, m.pos.y, "name") //tech gun field heal ammo research coupling boost instructions entanglement
Matter.Body.setPosition(player, simulation.mouseInGame);
spawn.bodyRect(simulation.mouseInGame.x, simulation.mouseInGame.y, 50, 50)
spawn.randomLevelBoss(simulation.mouseInGame.x, simulation.mouseInGame.y)
-
chrome firefox
Win/Linux: Ctrl + Shift + J Ctrl + Shift + J
Mac: Cmd + Option + J Cmd + Shift + J
diff --git a/js/simulation.js b/js/simulation.js
index fd68623..2a38814 100644
--- a/js/simulation.js
+++ b/js/simulation.js
@@ -935,6 +935,7 @@ const simulation = {
m.onGround = false
m.lastOnGroundCycle = 0
m.health = 0;
+ level.isNoHeal = false
m.addHealth(0.25)
m.drop();
m.holdingTarget = null
@@ -1185,6 +1186,7 @@ const simulation = {
},
clearNow: false,
clearMap() {
+ level.isVerticalFLipLevel = false
level.isProcedural = false;
level.fallMode = "";
simulation.unFlipCameraVertical()
@@ -1957,6 +1959,7 @@ const simulation = {
},
enableConstructMode() {
level.isProcedural = false //this is set to be true in levels like labs that need x+ and y+ in front of positions
+ level.isVerticalFLipLevel = false
simulation.isConstructionMode = true;
simulation.isHorizontalFlipped = false;
simulation.isAutoZoom = false;
@@ -1983,8 +1986,17 @@ const simulation = {
simulation.outputMapString(`${Math.floor(simulation.constructMouseDownPosition.x)}, ${Math.floor(simulation.constructMouseDownPosition.y)}`);
} else if (simulation.mouseInGame.x > simulation.constructMouseDownPosition.x && simulation.mouseInGame.y > simulation.constructMouseDownPosition.y) { //make sure that the width and height are positive
if (e.button === 0) { //add map
+ // if (level.isProcedural) {
+ // simulation.outputMapString(`spawn.mapRect(x+${x}, ${y}, ${dx}, ${dy});\n`);
+ // } else {
+ // simulation.outputMapString(`spawn.mapRect(${x}, ${y}, ${dx}, ${dy});\n`);
+ // }
if (level.isProcedural) {
simulation.outputMapString(`spawn.mapRect(x+${x}, ${y}, ${dx}, ${dy});\n`);
+ } else if (level.isVerticalFLipLevel) {
+ console.log('hi')
+ simulation.outputMapString(`spawn.mapRect(${x}, ${y}, ${dx}, ${dy});\n`);
+ simulation.outputMapString(`//spawn.mapRect(${x}, ${-y - dy}, ${dx}, ${dy});\n`);
} else {
simulation.outputMapString(`spawn.mapRect(${x}, ${y}, ${dx}, ${dy});\n`);
}
diff --git a/js/spawn.js b/js/spawn.js
index 96b35c9..02ae7db 100644
--- a/js/spawn.js
+++ b/js/spawn.js
@@ -2559,10 +2559,7 @@ const spawn = {
const springStiffness = 0.00014;
const springDampening = 0.0005;
- me.springTarget = {
- x: me.position.x,
- y: me.position.y
- };
+ me.springTarget = { x: me.position.x, y: me.position.y };
const len = cons.length;
cons[len] = Constraint.create({
pointA: me.springTarget,
@@ -4322,7 +4319,7 @@ const spawn = {
me.accelMag = 0.0002 * simulation.accelScale;
spawn.shield(me, x, y);
- me.lasers = [] //keeps track of static laser beams
+ me.laserArray = [] //keeps track of static laser beams
me.laserLimit = simulation.difficultyMode < 3 ? 1 : 2
me.fireDelay = Math.max(75, 140 - simulation.difficulty * 0.5)
me.cycle = 0
@@ -4354,14 +4351,14 @@ const spawn = {
best2.y = save1Y
}
- this.lasers.push({ a: { x: best1.x, y: best1.y }, b: { x: best2.x, y: best2.y }, fade: 0 })
+ this.laserArray.push({ a: { x: best1.x, y: best1.y }, b: { x: best2.x, y: best2.y }, fade: 0 })
//friction to animate the mob dropping something
Matter.Body.setVelocity(this, Vector.mult(this.velocity, 0.05));
Matter.Body.setAngularVelocity(this, this.angularVelocity * 0.05)
// simulation.drawList.push({ x: best1.x, y: best1.y, radius: 10, color: "rgba(255,0,100,0.3)", time: simulation.drawTime * 2 });
// simulation.drawList.push({ x: best2.x, y: best2.y, radius: 10, color: "rgba(255,0,100,0.3)", time: simulation.drawTime * 2 });
- if (this.lasers.length > this.laserLimit) this.lasers.shift() //cap total lasers
+ if (this.laserArray.length > this.laserLimit) this.laserArray.shift() //cap total laserArray
if (!this.seePlayer.recall && (Vector.magnitude(Vector.sub(this.position, this.driftGoal)) < 200 || 0.3 > Math.random())) {
//used in drift when can't find player
const radius = Math.random() * 1000;
@@ -4371,9 +4368,9 @@ const spawn = {
}
}
me.fireLaser = function () {
- for (let i = 0; i < this.lasers.length; i++) { //fire all lasers in the array
- let best = vertexCollision(this.lasers[i].a, this.lasers[i].b, m.isCloak ? [body] : [body, [playerBody, playerHead]]); //not checking map to fix not hitting player bug, this might make some lasers look strange when the map changes
- if (this.lasers[i].fade > 0.99) {
+ for (let i = 0; i < this.laserArray.length; i++) { //fire all lasers in the array
+ let best = vertexCollision(this.laserArray[i].a, this.laserArray[i].b, m.isCloak ? [body] : [body, [playerBody, playerHead]]); //not checking map to fix not hitting player bug, this might make some lasers look strange when the map changes
+ if (this.laserArray[i].fade > 0.99) {
if (best.who && (best.who === playerBody || best.who === playerHead) && m.immuneCycle < m.cycle) { // hitting player
m.immuneCycle = m.cycle + m.collisionImmuneCycles; //player is immune to damage after getting hit
const dmg = 0.03 * simulation.dmgScale;
@@ -4385,7 +4382,7 @@ const spawn = {
color: "rgba(255,0,100,0.5)",
time: 20
});
- this.lasers.splice(i, 1) //remove this laser node
+ this.laserArray.splice(i, 1) //remove this laser node
if (this.distanceToPlayer < 1000) { //mob jumps away from player
const forceMag = 0.03 * this.mass;
const angle = Math.atan2(this.seePlayer.position.y - this.position.y, this.seePlayer.position.x - this.position.x);
@@ -4395,7 +4392,7 @@ const spawn = {
} else if (best.who && best.who.classType === "body") { //hitting block
ctx.beginPath();
ctx.moveTo(best.x, best.y);
- ctx.lineTo(this.lasers[i].a.x, this.lasers[i].a.y);
+ ctx.lineTo(this.laserArray[i].a.x, this.laserArray[i].a.y);
ctx.strokeStyle = `rgb(255,0,100)`;
ctx.lineWidth = 2;
ctx.setLineDash([50 + 120 * Math.random(), 50 * Math.random()]);
@@ -4403,8 +4400,8 @@ const spawn = {
ctx.setLineDash([]);
} else { //hitting nothing
ctx.beginPath();
- ctx.moveTo(this.lasers[i].b.x, this.lasers[i].b.y);
- ctx.lineTo(this.lasers[i].a.x, this.lasers[i].a.y);
+ ctx.moveTo(this.laserArray[i].b.x, this.laserArray[i].b.y);
+ ctx.lineTo(this.laserArray[i].a.x, this.laserArray[i].a.y);
ctx.strokeStyle = `rgb(255,0,100)`;
ctx.lineWidth = 2;
ctx.setLineDash([50 + 120 * Math.random(), 50 * Math.random()]);
@@ -4412,12 +4409,12 @@ const spawn = {
ctx.setLineDash([]);
}
} else {//fade in warning
- this.lasers[i].fade += 0.01
+ this.laserArray[i].fade += 0.01
ctx.beginPath();
- ctx.moveTo(this.lasers[i].a.x, this.lasers[i].a.y);
- ctx.lineTo(this.lasers[i].b.x, this.lasers[i].b.y);
- ctx.lineWidth = 2 + 40 - 40 * this.lasers[i].fade;
- ctx.strokeStyle = `rgba(255,0,100,${0.02 + 0.1 * this.lasers[i].fade})`;
+ ctx.moveTo(this.laserArray[i].a.x, this.laserArray[i].a.y);
+ ctx.lineTo(this.laserArray[i].b.x, this.laserArray[i].b.y);
+ ctx.lineWidth = 2 + 40 - 40 * this.laserArray[i].fade;
+ ctx.strokeStyle = `rgba(255,0,100,${0.02 + 0.1 * this.laserArray[i].fade})`;
ctx.stroke();
}
}
@@ -4486,7 +4483,7 @@ const spawn = {
this.laserDelay = 130
}
};
- me.lasers = [] //keeps track of static laser beams
+ me.laserArray = [] //keeps track of static laser beams
me.laserLimit = 2 + (simulation.difficultyMode > 2) + (simulation.difficultyMode > 4)
me.fireDelay = Math.max(75, 140 - simulation.difficulty * 0.5)
me.cycle = 0
@@ -4517,7 +4514,7 @@ const spawn = {
best2.x = save1X
best2.y = save1Y
}
- this.lasers.push({ a: { x: best1.x, y: best1.y }, b: { x: best2.x, y: best2.y }, fade: 0 })
+ this.laserArray.push({ a: { x: best1.x, y: best1.y }, b: { x: best2.x, y: best2.y }, fade: 0 })
}
// add(m.pos, m.angle)
add(m.pos, this.angle + Math.PI / 4 + Math.PI / 2)
@@ -4534,9 +4531,9 @@ const spawn = {
}
}
me.fireLaser = function () {
- for (let i = 0; i < this.lasers.length; i++) { //fire all lasers in the array
- let best = vertexCollision(this.lasers[i].a, this.lasers[i].b, m.isCloak ? [body] : [body, [playerBody, playerHead]]); //not checking map to fix not hitting player bug, this might make some lasers look strange when the map changes
- if (this.lasers[i].fade > 0.99) {
+ for (let i = 0; i < this.laserArray.length; i++) { //fire all laserArray in the array
+ let best = vertexCollision(this.laserArray[i].a, this.laserArray[i].b, m.isCloak ? [body] : [body, [playerBody, playerHead]]); //not checking map to fix not hitting player bug, this might make some lasers look strange when the map changes
+ if (this.laserArray[i].fade > 0.99) {
if (best.who && (best.who === playerBody || best.who === playerHead) && m.immuneCycle < m.cycle) { // hitting player
m.immuneCycle = m.cycle + m.collisionImmuneCycles; //player is immune to damage after getting hit
const dmg = 0.03 * simulation.dmgScale;
@@ -4548,7 +4545,7 @@ const spawn = {
color: "rgba(255,0,100,0.5)",
time: 20
});
- this.lasers.splice(i, 1) //remove this laser node
+ this.laserArray.splice(i, 1) //remove this laser node
if (this.distanceToPlayer < 1000) { //mob jumps away from player
const forceMag = 0.03 * this.mass;
const angle = Math.atan2(this.seePlayer.position.y - this.position.y, this.seePlayer.position.x - this.position.x);
@@ -4558,7 +4555,7 @@ const spawn = {
} else if (best.who && best.who.classType === "body") { //hitting block
ctx.beginPath();
ctx.moveTo(best.x, best.y);
- ctx.lineTo(this.lasers[i].a.x, this.lasers[i].a.y);
+ ctx.lineTo(this.laserArray[i].a.x, this.laserArray[i].a.y);
ctx.strokeStyle = `rgb(255,0,100)`;
ctx.lineWidth = 2;
ctx.setLineDash([50 + 120 * Math.random(), 50 * Math.random()]);
@@ -4566,8 +4563,8 @@ const spawn = {
ctx.setLineDash([]);
} else { //hitting nothing
ctx.beginPath();
- ctx.moveTo(this.lasers[i].b.x, this.lasers[i].b.y);
- ctx.lineTo(this.lasers[i].a.x, this.lasers[i].a.y);
+ ctx.moveTo(this.laserArray[i].b.x, this.laserArray[i].b.y);
+ ctx.lineTo(this.laserArray[i].a.x, this.laserArray[i].a.y);
ctx.strokeStyle = `rgb(255,0,100)`;
ctx.lineWidth = 2;
ctx.setLineDash([50 + 120 * Math.random(), 50 * Math.random()]);
@@ -4575,16 +4572,16 @@ const spawn = {
ctx.setLineDash([]);
}
} else {//fade in warning
- this.lasers[i].fade += 0.007
+ this.laserArray[i].fade += 0.007
ctx.beginPath();
- ctx.moveTo(this.lasers[i].a.x, this.lasers[i].a.y);
- ctx.lineTo(this.lasers[i].b.x, this.lasers[i].b.y);
- ctx.lineWidth = 2 + 40 - 40 * this.lasers[i].fade;
- ctx.strokeStyle = `rgba(255,0,100,${0.02 + 0.1 * this.lasers[i].fade})`;
+ ctx.moveTo(this.laserArray[i].a.x, this.laserArray[i].a.y);
+ ctx.lineTo(this.laserArray[i].b.x, this.laserArray[i].b.y);
+ ctx.lineWidth = 2 + 40 - 40 * this.laserArray[i].fade;
+ ctx.strokeStyle = `rgba(255,0,100,${0.02 + 0.1 * this.laserArray[i].fade})`;
ctx.stroke();
- if (this.lasers[i].fade > 0.99) {
- this.lasers[i].fade = 1;
- if (this.lasers.length > this.laserLimit) this.lasers.shift() //cap total lasers
+ if (this.laserArray[i].fade > 0.99) {
+ this.laserArray[i].fade = 1;
+ if (this.laserArray.length > this.laserLimit) this.laserArray.shift() //cap total lasers
break
}
}
@@ -4744,9 +4741,9 @@ const spawn = {
Matter.Body.setAngularVelocity(this, 0)
}
ctx.beginPath();
- this.lasers(this.vertices[0], this.angle + Math.PI / 3);
- this.lasers(this.vertices[1], this.angle + Math.PI);
- this.lasers(this.vertices[2], this.angle - Math.PI / 3);
+ this.laserArray(this.vertices[0], this.angle + Math.PI / 3);
+ this.laserArray(this.vertices[1], this.angle + Math.PI);
+ this.laserArray(this.vertices[2], this.angle - Math.PI / 3);
ctx.strokeStyle = "#50f";
ctx.lineWidth = 1.5;
ctx.setLineDash([70 + 300 * Math.random(), 55 * Math.random()]);
@@ -4757,7 +4754,7 @@ const spawn = {
ctx.stroke(); // Draw it
}
};
- me.lasers = function (where, angle) {
+ me.laserArray = function (where, angle) {
const seeRange = 7000;
best = {
x: null,
diff --git a/js/tech.js b/js/tech.js
index 66ad7b7..35f22a9 100644
--- a/js/tech.js
+++ b/js/tech.js
@@ -2634,25 +2634,6 @@ const tech = {
tech.isDarkStar = false
}
},
- {
- name: "ablative drones",
- descriptionFunction() {
- return `after losing ${tech.isEnergyHealth ? "energy" : "health"} there is a chance
to rebuild your broken parts as drones`
- },
- maxCount: 1,
- count: 0,
- frequency: 1,
- frequencyDefault: 1,
- allowed: () => true,
- requires: "",
- effect() {
- tech.isDroneOnDamage = true;
- // for (let i = 0; i < 4; i++) b.drone()
- },
- remove() {
- tech.isDroneOnDamage = false;
- }
- },
{
name: "non-Newtonian armor",
link: `non-Newtonian armor`,
@@ -4574,7 +4555,7 @@ const tech = {
name: "deprecated",
scale: 0.08,
descriptionFunction() {
- return `after removing this gain
${1 + this.scale}x damage per removed ${powerUps.orb.tech()}(${(1 + this.scale * ((this.frequency === 0 ? 0 : 1) + tech.removeCount)).toFixed(2)}x)`
+ return `after removing this gain ${1 + this.scale}x damage
per ${powerUps.orb.tech()} removed this game(${(1 + this.scale * ((this.frequency === 0 ? 0 : 1) + tech.removeCount)).toFixed(2)}x)`
},
maxCount: 1,
count: 0,
@@ -6607,7 +6588,7 @@ const tech = {
allowed() {
return tech.haveGunCheck("drones", false) && !tech.isDroneRespawn && tech.bulletsLastLonger === 1 && !tech.isDronesTravel && (build.isExperimentSelection || powerUps.research.count > 1)
},
- requires: "drones, not drone repair, anti-shear topology, autonomous navigation",
+ requires: "drones, not drone repair, anti-shear topology, autonomous navigation, ",
effect() {
const num = 5
tech.isForeverDrones += num
@@ -6640,6 +6621,27 @@ const tech = {
// if (this.count > 0) powerUps.research.changeRerolls(2)
}
},
+ {
+ name: "ablative drones",
+ descriptionFunction() {
+ return `after losing ${tech.isEnergyHealth ? "energy" : "health"} there is a chance
to rebuild your broken parts as drones`
+ },
+ isGunTech: true,
+ maxCount: 1,
+ count: 0,
+ frequency: 1,
+ frequencyDefault: 1,
+ allowed() {
+ return (tech.haveGunCheck("drones") && !tech.isForeverDrones) || (m.fieldMode === 4 && simulation.molecularMode === 3)
+ },
+ requires: "drones, not fault tolerance",
+ effect() {
+ tech.isDroneOnDamage = true;
+ },
+ remove() {
+ tech.isDroneOnDamage = false;
+ }
+ },
{
name: "reduced tolerances",
link: `reduced tolerances`,
diff --git a/todo.txt b/todo.txt
index 447102b..14c1545 100644
--- a/todo.txt
+++ b/todo.txt
@@ -1,32 +1,27 @@
******************************************************** NEXT PATCH **************************************************
-new level testChamber2
- New camera flip effect
- new laser level element now has collisions with blocks
- elevators are less deadly to mobs at low speeds
+new level: gravityObservatory
-difficulty level progression reworked
-no constraints on final boss
-new constraint - healing disabled
+level: testChamber2 renamed gravityInterferometer
+ it has been added to the levels that are extra hard and only show up late game
+ it also got a bit hard with the addition of 1 more laser
-quenching 0.3->0.4x overheal converted to max health
-tungsten carbide 400->500 extra max health
-paradigm shift's health loss is no longer reduced by damage taken reduction
-coherence no longer remembers tech that is set to zero frequency, like removed tech
+deflected mob bullets are converted into small blocks
+ablative drones is now a gun tech for drones
+ it makes ~33% more drones
+1.033->1.05x sneak attack damage per coupling for cloaking field
-JUNK tech: pet the bot - lets you pet your bots
-JUNK tech: the upside down - flip everything
-
-bug
- prevented possible duplicate choices with coherence tech
- fixed issues with showing and hiding health bars on that constraint
- fixed crash from autonomous defense
- mass-energy mode wasn't getting any benefit from damage taken reduction
- it now gets square root of damage taken reduction
+bug fixes
+ extended vertical flip to edge cases:
+ trail left by snakeBoss
+ laser array from boss and mobs
+ springer,spiderBoss,mantisBoss constraints
+ subway: dark matter no longer removed if it gets too far from the player
+ training: fixed potential lock out from running out of ammo
+ training: fixed accidental difficulty increase
******************************************************** 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?
@@ -55,29 +50,40 @@ player can become crouched while not touching the ground if they exit the ground
*********************************************************** TODO *****************************************************
+considering removing generative AI images from n-gon
+ pros: (of removing images)
+ the novelty of the images has worn off
+ cleaner UI without them
+ reduce the total size of n-gon by about 1/3
+ reduce the complexity of the code
+ avoid ire of people that hate generative AI
+ cons: (of removing images)
+ some people might like them
+
+make a level selector power up
+ it shows up when you enter testing mode on the initial level
+ check box for community maps?
+
+deflecting with field converts mob bullets to blocks that despawn after a few seconds
+ default for all fields that can deflect
+
+add more tips:
+ download latest version of n-gon
+ https://codeload.github.com/landgreen/n-gon/zip/refs/heads/master
+
+
new level based laser element
!!update new version into other levels
level technique: pairs of touch activated elevators jump on one to get high enough to jump on the next one
-flip player upside down
- fieldTech: negative mass?
- JUNK tech?
- final boss effect
- maybe just flip player camera and mouse
- level effect flip map vertically also
- new subway station
- new full level
- needs a device that will flip gravity
- update to old level so it is more surreal
- levels with few effects, just blocks and map
- levels with a roof or infinite fall
- towers, lab
+new vertical flip level
+ long horizontal
+ several buttons
+ shorten flip time?
-
-
constraints should show future constraints in pause menu
pre calculate all constraints for up to 13 levels?
loop constraints after that
@@ -1343,6 +1349,8 @@ possible names for tech
cork - used as a heat shield for rockets
P = NP - something with speeding up calculation times
transistivity - something where a>b and b>c -> a>c
+ lenticular - looks different from different angles (lasers?)
+ De Sitter space - simple model of universe related to general relativity (mass-energy?)
******************************************************* DESIGN ******************************************************