diff --git a/img/aerogel.webp b/img/aerogel.webp
index a3b9e83..597078b 100644
Binary files a/img/aerogel.webp and b/img/aerogel.webp differ
diff --git a/img/cavitation.webp b/img/cavitation.webp
index d02ca81..332ec5a 100644
Binary files a/img/cavitation.webp and b/img/cavitation.webp differ
diff --git a/img/electrostatic induction.webp b/img/electrostatic induction.webp
index 8d1b018..3cc3bf3 100644
Binary files a/img/electrostatic induction.webp and b/img/electrostatic induction.webp differ
diff --git a/img/foam fractionation.webp b/img/foam fractionation.webp
index 46b24ab..811a613 100644
Binary files a/img/foam fractionation.webp and b/img/foam fractionation.webp differ
diff --git a/img/foam-bot upgrade.webp b/img/foam-bot upgrade.webp
index 5b729cb..f64ec65 100644
Binary files a/img/foam-bot upgrade.webp and b/img/foam-bot upgrade.webp differ
diff --git a/img/foam-bot.webp b/img/foam-bot.webp
index b6a49b7..7c9fe50 100644
Binary files a/img/foam-bot.webp and b/img/foam-bot.webp differ
diff --git a/img/foam-shot.webp b/img/foam-shot.webp
index f7ecda9..73a53a7 100644
Binary files a/img/foam-shot.webp and b/img/foam-shot.webp differ
diff --git a/img/ideal gas law.webp b/img/ideal gas law.webp
index e00129a..c2097ef 100644
Binary files a/img/ideal gas law.webp and b/img/ideal gas law.webp differ
diff --git a/img/polyurethane foam.webp b/img/polyurethane foam.webp
index a875deb..d574562 100644
Binary files a/img/polyurethane foam.webp and b/img/polyurethane foam.webp differ
diff --git a/img/pressure vessel.webp b/img/pressure vessel.webp
index 0feb70d..a4e27de 100644
Binary files a/img/pressure vessel.webp and b/img/pressure vessel.webp differ
diff --git a/img/surface tension.webp b/img/surface tension.webp
index d736c0b..469fe59 100644
Binary files a/img/surface tension.webp and b/img/surface tension.webp differ
diff --git a/img/surfactant.webp b/img/surfactant.webp
index 9ded9c8..291fd6c 100644
Binary files a/img/surfactant.webp and b/img/surfactant.webp differ
diff --git a/img/uncertainty principle.webp b/img/uncertainty principle.webp
index da73750..892fcde 100644
Binary files a/img/uncertainty principle.webp and b/img/uncertainty principle.webp differ
diff --git a/js/bullet.js b/js/bullet.js
index b2c991f..752a589 100644
--- a/js/bullet.js
+++ b/js/bullet.js
@@ -6751,7 +6751,7 @@ const b = {
},
{
name: "wave", //3
- description: "emit a wave packet of oscillating particles
that propagates through solids",
+ description: "emit wave packets that propagate through solids
waves slow mobs", // of oscillating particles
ammo: 0,
ammoPack: 115,
defaultAmmoPack: 115,
diff --git a/js/level.js b/js/level.js
index 91e6f03..fafe17a 100644
--- a/js/level.js
+++ b/js/level.js
@@ -34,17 +34,17 @@ const level = {
// b.giveGuns("harpoon") //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("shotgun") //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.guns[3].ammo = 100000000
- // tech.giveTech("Maxwells demon")
+ // tech.giveTech("propagator")
// tech.giveTech("missile-bot")
// for (let i = 0; i < 6; ++i) tech.giveTech("replication")
// for (let i = 0; i < 1; ++i) tech.giveTech("waste heat recovery")
// requestAnimationFrame(() => { for (let i = 0; i < 1; i++) tech.giveTech("foam-bot") });
// for (let i = 0; i < 1; i++) tech.giveTech("foam-bot upgrade")
- // for (let i = 0; i < 1; ++i) tech.giveTech("Occams razor")
+ for (let i = 0; i < 1; ++i) tech.giveTech("flatland")
// for (let i = 0; i < 3; i++) powerUps.directSpawn(450, -50, "tech");
// for (let i = 0; i < 10; i++) powerUps.directSpawn(1750, -500, "research");
// for (let i = 0; i < 10; i++) powerUps.directSpawn(1750, -500, "coupling");
- // level.factory();
+ // level.testing();
// spawn.nodeGroup(3200, -300, "sniper")
// spawn.nodeGroup(2200, -300, "sniper")
// spawn.nodeGroup(2200, -300, "sniper")
@@ -198,7 +198,7 @@ const level = {
difficultyIncrease(num = 1) {
for (let i = 0; i < num; i++) {
simulation.difficulty++
- m.dmgScale *= 0.9; //damage done by player decreases each level
+ m.dmgScale *= 0.91; //damage done by player decreases each level
if (simulation.accelScale < 6) simulation.accelScale *= 1.024 //mob acceleration increases each level
if (simulation.CDScale > 0.15) simulation.CDScale *= 0.964 //mob CD time decreases each level
}
@@ -209,7 +209,7 @@ const level = {
difficultyDecrease(num = 1) { //used in easy mode for simulation.reset()
for (let i = 0; i < num; i++) {
simulation.difficulty--
- m.dmgScale /= 0.9; //damage done by player decreases each level
+ m.dmgScale /= 0.91; //damage done by player decreases each level
if (simulation.accelScale > 1) simulation.accelScale /= 1.024 //mob acceleration increases each level
if (simulation.CDScale < 1) simulation.CDScale /= 0.964 //mob CD time decreases each level
}
@@ -1774,6 +1774,1621 @@ const level = {
//******************************************************************************************************************
//******************************************************************************************************************
//******************************************************************************************************************
+ template() {
+ simulation.enableConstructMode()
+ level.setPosToSpawn(0, -50); //normal spawn
+ level.exit.x = 1500;
+ level.exit.y = -1875;
+ spawn.mapRect(level.enter.x, level.enter.y + 20, 100, 20); //bump for level entrance
+ spawn.mapRect(level.exit.x, level.exit.y + 20, 100, 20); //bump for level exit
+ level.defaultZoom = 1800
+ simulation.zoomTransition(level.defaultZoom)
+ document.body.style.backgroundColor = "#d8dadf";
+ // color.map = "#444" //custom map color
+
+ level.custom = () => {
+ level.exit.drawAndCheck();
+
+ level.enter.draw();
+ };
+ level.customTopLayer = () => { };
+
+ spawn.mapRect(-100, 0, 1000, 100);
+ // powerUps.spawnStartingPowerUps(1475, -1175);
+ // spawn.debris(750, -2200, 3700, 16); //16 debris per level
+ // spawn.bodyRect(1540, -1110, 300, 25, 0.9);
+ // spawn.randomSmallMob(1300, -70);
+ // spawn.randomMob(2650, -975, 0.8);
+ // spawn.randomGroup(1700, -900, 0.4);
+ // if (simulation.difficulty > 1) spawn.randomLevelBoss(2200, -1300);
+ // spawn.secondaryBossChance(100, -1500)
+ powerUps.addResearchToLevel() //needs to run after mobs are spawned
+ },
+ testing() {
+ simulation.enableConstructMode() //tech.giveTech('motion sickness') //used to build maps in testing mode
+
+ //clipping everything except LoS
+ //stroke around map
+ // simulation.ephemera.push({
+ // name: "LoS", count: 0, do() {
+ // const pos = m.pos
+ // const radius = 5000
+ // if (!simulation.isTimeSkipping) {
+ // const vertices = simulation.sight.circleLoS(pos, radius);
+ // if (vertices.length) {
+ // ctx.beginPath();
+ // ctx.moveTo(vertices[0].x, vertices[0].y);
+ // for (var i = 1; i < vertices.length; i++) {
+ // var currentDistance = Math.sqrt((vertices[i - 1].x - pos.x) ** 2 + (vertices[i - 1].y - pos.y) ** 2);
+ // var newDistance = Math.sqrt((vertices[i].x - pos.x) ** 2 + (vertices[i].y - pos.y) ** 2);
+ // if (Math.abs(currentDistance - radius) < 1 && Math.abs(newDistance - radius) < 1) {
+ // const currentAngle = Math.atan2(vertices[i - 1].y - pos.y, vertices[i - 1].x - pos.x);
+ // const newAngle = Math.atan2(vertices[i].y - pos.y, vertices[i].x - pos.x);
+ // ctx.arc(pos.x, pos.y, radius, currentAngle, newAngle);
+ // } else {
+ // ctx.lineTo(vertices[i].x, vertices[i].y)
+ // }
+ // }
+ // newDistance = Math.sqrt((vertices[0].x - pos.x) ** 2 + (vertices[0].y - pos.y) ** 2);
+ // currentDistance = Math.sqrt((vertices[vertices.length - 1].x - pos.x) ** 2 + (vertices[vertices.length - 1].y - pos.y) ** 2);
+ // if (Math.abs(currentDistance - radius) < 1 && Math.abs(newDistance - radius) < 1) {
+ // const currentAngle = Math.atan2(vertices[vertices.length - 1].y - pos.y, vertices[vertices.length - 1].x - pos.x);
+ // const newAngle = Math.atan2(vertices[0].y - pos.y, vertices[0].x - pos.x);
+ // ctx.arc(pos.x, pos.y, radius, currentAngle, newAngle);
+ // } else {
+ // ctx.lineTo(vertices[0].x, vertices[0].y)
+ // }
+ // ctx.strokeStyle = "#000";
+ // ctx.lineWidth = 20;
+ // ctx.stroke(simulation.draw.mapPath);
+ // ctx.globalCompositeOperation = "destination-in";
+ // ctx.fillStyle = "#000";
+ // ctx.fill();
+ // ctx.globalCompositeOperation = "source-over";
+ // ctx.clip();
+ // }
+ // }
+ // },
+ // })
+
+ //clipping everything except LoS
+ //redrawing map, so it's still visible
+ // simulation.ephemera.push({
+ // name: "LoS", count: 0, do() {
+ // const pos = m.pos
+ // const radius = 3000
+ // if (!simulation.isTimeSkipping) {
+ // const vertices = simulation.sight.circleLoS(pos, radius);
+ // if (vertices.length) {
+ // ctx.beginPath();
+ // ctx.moveTo(vertices[0].x, vertices[0].y);
+ // for (var i = 1; i < vertices.length; i++) {
+ // var currentDistance = Math.sqrt((vertices[i - 1].x - pos.x) ** 2 + (vertices[i - 1].y - pos.y) ** 2);
+ // var newDistance = Math.sqrt((vertices[i].x - pos.x) ** 2 + (vertices[i].y - pos.y) ** 2);
+ // if (Math.abs(currentDistance - radius) < 1 && Math.abs(newDistance - radius) < 1) {
+ // const currentAngle = Math.atan2(vertices[i - 1].y - pos.y, vertices[i - 1].x - pos.x);
+ // const newAngle = Math.atan2(vertices[i].y - pos.y, vertices[i].x - pos.x);
+ // ctx.arc(pos.x, pos.y, radius, currentAngle, newAngle);
+ // } else {
+ // ctx.lineTo(vertices[i].x, vertices[i].y)
+ // }
+ // }
+ // newDistance = Math.sqrt((vertices[0].x - pos.x) ** 2 + (vertices[0].y - pos.y) ** 2);
+ // currentDistance = Math.sqrt((vertices[vertices.length - 1].x - pos.x) ** 2 + (vertices[vertices.length - 1].y - pos.y) ** 2);
+ // if (Math.abs(currentDistance - radius) < 1 && Math.abs(newDistance - radius) < 1) {
+ // const currentAngle = Math.atan2(vertices[vertices.length - 1].y - pos.y, vertices[vertices.length - 1].x - pos.x);
+ // const newAngle = Math.atan2(vertices[0].y - pos.y, vertices[0].x - pos.x);
+ // ctx.arc(pos.x, pos.y, radius, currentAngle, newAngle);
+ // } else {
+ // ctx.lineTo(vertices[0].x, vertices[0].y)
+ // }
+
+ // ctx.strokeStyle = "#234";
+ // ctx.lineWidth = 9;
+ // ctx.stroke(simulation.draw.mapPath);
+
+ // ctx.globalCompositeOperation = "destination-in";
+ // ctx.fillStyle = "#000";
+ // ctx.fill();
+ // ctx.globalCompositeOperation = "source-over";
+
+ // // ctx.fill(simulation.draw.mapPath);
+ // // ctx.fillStyle = "#000";
+
+ // ctx.clip();
+ // }
+ // }
+ // },
+ // })
+
+ document.body.style.backgroundColor = "#fff";
+ // color.map = "#444" //custom map color
+ // level.difficultyIncrease(14); //hard mode level 7
+ level.defaultZoom = 1500
+ simulation.zoomTransition(level.defaultZoom)
+
+ const mover = level.mover(2800, -300, 1000, 25); //x,y,width.height,VxGoal,force
+
+ const train = level.transport(2900, -500, 500, 25, 8); //x,y,width.height,VxGoal,force
+ spawn.bodyRect(1900, -550, 50, 50);
+ const button = level.button(2535, -200)
+ // spawn.bodyRect(250, -450, 50, 50); //block on button
+
+ level.custom = () => {
+
+ //oscillate back and forth
+ if (train.position.x < 2000) {
+ train.changeDirection(true) //go right
+ } else if (train.position.x > 4000) {
+ train.changeDirection(false) //go left
+ }
+ if (!button.isUp) train.move();
+
+ mover.push();
+ ctx.fillStyle = "#d4d4d4"
+ ctx.fillRect(2500, -475, 200, 300)
+
+ ctx.fillStyle = "#ddd"
+ ctx.fillRect(-150, -1000, 6875, 1000);
+ ctx.fillStyle = "rgba(0,255,255,0.1)";
+ ctx.fillRect(6400, -550, 300, 350);
+ level.exit.drawAndCheck();
+ level.enter.draw();
+ };
+ level.customTopLayer = () => {
+ train.draw()
+ mover.draw();
+ button.query();
+ button.draw();
+ ctx.fillStyle = "rgba(0,0,0,0.1)"
+ ctx.fillRect(-150, -650, 900, 250)
+ };
+ level.setPosToSpawn(0, -450); //normal spawn
+ spawn.mapRect(level.enter.x, level.enter.y + 20, 100, 20);
+ level.exit.x = 6500;
+ level.exit.y = -230;
+
+ spawn.mapRect(-950, 0, 8200, 800); //ground
+ spawn.mapRect(-950, -1200, 800, 1400); //left wall
+ spawn.mapRect(-950, -1800, 8200, 800); //roof
+ spawn.mapRect(-250, -400, 1000, 600); // shelf
+ spawn.mapRect(-250, -1200, 1000, 550); // shelf roof
+ // for (let i = 0; i < 10; ++i) powerUps.spawn(550, -800, "ammo", false);
+
+ function blockDoor(x, y, blockSize = 58) {
+ spawn.mapRect(x, y - 290, 40, 60); // door lip
+ spawn.mapRect(x, y, 40, 50); // door lip
+ for (let i = 0; i < 4; ++i) spawn.bodyRect(x + 5, y - 260 + i * blockSize, 30, blockSize);
+ }
+
+ spawn.mapRect(2500, -1200, 200, 750); //right wall
+ spawn.mapRect(2500, -200, 200, 300); //right wall
+ spawn.mapRect(4500, -1200, 200, 650); //right wall
+ blockDoor(4585, -310)
+ spawn.mapRect(4500, -300, 200, 400); //right wall
+ spawn.mapRect(6400, -1200, 400, 750); //right wall
+ spawn.mapRect(6400, -200, 400, 300); //right wall
+ spawn.mapRect(6700, -1800, 800, 2600); //right wall
+ spawn.mapRect(level.exit.x, level.exit.y + 20, 100, 100); //exit bump
+ //place to hide
+ spawn.mapRect(4650, -300, 1150, 50);
+ spawn.mapRect(5750, -300, 50, 200);
+ spawn.mapRect(5575, -100, 50, 125);
+ spawn.mapRect(5300, -275, 50, 175);
+ spawn.mapRect(5050, -100, 50, 150);
+ spawn.mapRect(4850, -275, 50, 175);
+ spawn.mapRect(-950, -3250, 850, 1750);
+ //roof
+ spawn.mapRect(-175, -2975, 300, 1425);
+ spawn.mapRect(75, -2650, 325, 1150);
+ spawn.mapRect(375, -2225, 250, 650);
+ spawn.mapRect(4075, -2125, 700, 800);
+ spawn.mapRect(4450, -2950, 675, 1550);
+ spawn.mapRect(4875, -3625, 725, 2225);
+ spawn.mapRect(5525, -4350, 1725, 2925);
+ spawn.mapRect(7200, -5125, 300, 3900);
+
+
+ //???
+ // level.difficultyIncrease(30) //30 is near max on hard //60 is near max on why
+ // m.addHealth(Infinity)
+
+ // spawn.starter(1900, -500, 200) //big boy
+ // for (let i = 0; i < 10; ++i) spawn.launcher(1900, -500)
+ // spawn.suckerBoss(1900, -500)
+ // spawn.launcherBoss(3200, -500)
+ // spawn.laserTargetingBoss(1700, -500)
+ // spawn.powerUpBoss(1900, -500)
+ // spawn.powerUpBossBaby(3200, -500)
+ // spawn.dragonFlyBoss(1700, -500)
+ // spawn.streamBoss(3200, -500)
+ // spawn.pulsarBoss(1700, -500)
+ // spawn.spawnerBossCulture(3200, -500)
+ // spawn.grenadierBoss(1700, -500)
+ // spawn.growBossCulture(3200, -500)
+ // spawn.blinkBoss(1700, -500)
+ // spawn.snakeSpitBoss(3200, -500)
+ // spawn.laserBombingBoss(1700, -500)
+ // spawn.launcherBoss(3200, -500)
+ // spawn.blockBoss(1700, -500)
+ // spawn.blinkBoss(3200, -500)
+ // spawn.spiderBoss(1700, -500)
+ // spawn.tetherBoss(1700, -500) //go to actual level?
+ // spawn.revolutionBoss(1900, -500)
+ // spawn.bomberBoss(1400, -500)
+ // spawn.cellBossCulture(1600, -500)
+ // spawn.shieldingBoss(1700, -500)
+
+ // for (let i = 0; i < 10; ++i) spawn.bodyRect(1600 + 5, -500, 30, 40);
+ // for (let i = 0; i < 4; i++) spawn.starter(1900, -500)
+ // spawn.pulsar(1900, -500)
+ // spawn.shield(mob[mob.length - 1], 1900, -500, 1);
+ // mob[mob.length - 1].isShielded = true
+ // spawn.nodeGroup(1200, 0, "grenadier")
+ // spawn.blinkBoss(1200, -500)
+ // spawn.suckerBoss(2900, -500)
+ // spawn.randomMob(1600, -500)
+ },
+ null() {
+ level.levels.pop(); //remove lore level from rotation
+ // level.onLevel--
+ // console.log(level.onLevel, level.levels)
+ //start a conversation based on the number of conversations seen
+ if (localSettings.loreCount > lore.conversation.length - 1) localSettings.loreCount = lore.conversation.length - 1; //repeat final conversation if lore count is too high
+ if (!simulation.isCheating && localSettings.loreCount < lore.conversation.length) {
+ tech.isNoDraftPause = true //disable pause
+ lore.testSpeechAPI() //see if speech is working
+ lore.chapter = localSettings.loreCount //set the chapter to listen to to be the lore level (you can't use the lore level because it changes during conversations)
+ lore.sentence = 0 //what part of the conversation to start on
+ lore.conversation[lore.chapter][lore.sentence]()
+ localSettings.loreCount++ //hear the next conversation next time you win
+ if (localSettings.isAllowed) localStorage.setItem("localSettings", JSON.stringify(localSettings)); //update local storage
+ }
+ // const hazardSlime = level.hazard(-1800, 150, 3600, 650, 0.004, "hsla(160, 100%, 35%,0.75)")
+ level.isHazardRise = false //this is set to true to make the slime rise up
+ const hazardSlime = level.hazard(-1800, -800, 3600, 1600, 0.004)
+ hazardSlime.height -= 950
+ hazardSlime.min.y += 950
+ hazardSlime.max.y = hazardSlime.min.y + hazardSlime.height
+ const circle = {
+ x: 0,
+ y: -500,
+ radius: 50
+ }
+ level.custom = () => {
+ //draw wide line
+ ctx.beginPath();
+ ctx.moveTo(circle.x, -800)
+ ctx.lineTo(circle.x, circle.y)
+ ctx.lineWidth = 40;
+ ctx.strokeStyle = lore.talkingColor //"#d5dddd" //"#bcc";
+ ctx.globalAlpha = 0.03;
+ ctx.stroke();
+ ctx.globalAlpha = 1;
+ //support pillar
+ ctx.fillStyle = "rgba(0,0,0,0.2)";
+ ctx.fillRect(-25, 0, 50, 1000);
+
+ //draw circles
+ ctx.beginPath();
+ ctx.arc(circle.x, circle.y, circle.radius, 0, 2 * Math.PI);
+ ctx.fillStyle = "#bcc"
+ ctx.fill();
+ ctx.lineWidth = 2;
+ ctx.strokeStyle = "#abb";
+ ctx.stroke();
+
+ ctx.beginPath();
+ ctx.arc(circle.x, circle.y, circle.radius / 8, 0, 2 * Math.PI);
+ ctx.fillStyle = lore.talkingColor //"#dff"
+ ctx.fill();
+
+ // level.enter.draw();
+ };
+ let sway = {
+ x: 0,
+ y: 0
+ }
+ let phase = -Math.PI / 2
+ level.customTopLayer = () => {
+ ctx.fillStyle = "rgba(0,0,0,0.1)";
+ ctx.fillRect(-1950, -950, 3900, 1900);
+ //draw center circle lines
+ ctx.beginPath();
+ const step = Math.PI / 20
+ const horizontalStep = 85
+ if (simulation.isCheating) phase += 0.3 * Math.random() * Math.random() //(m.pos.x - circle.x) * 0.0005 //0.05 * Math.sin(simulation.cycle * 0.030)
+ // const sway = 5 * Math.cos(simulation.cycle * 0.007)
+ sway.x = sway.x * 0.995 + 0.005 * (m.pos.x - circle.x) * 0.05 //+ 0.04 * Math.cos(simulation.cycle * 0.01)
+ sway.y = 2.5 * Math.sin(simulation.cycle * 0.015)
+ for (let i = -19.5; i < 20; i++) {
+ const where = {
+ x: circle.x + circle.radius * Math.cos(i * step + phase),
+ y: circle.y + circle.radius * Math.sin(i * step + phase)
+ }
+ ctx.moveTo(where.x, where.y);
+ ctx.bezierCurveTo(sway.x * Math.abs(i) + where.x, where.y + 25 * Math.abs(i) + 60 + sway.y * Math.sqrt(Math.abs(i)),
+ sway.x * Math.abs(i) + where.x + horizontalStep * i, where.y + 25 * Math.abs(i) + 60 + sway.y * Math.sqrt(Math.abs(i)),
+ horizontalStep * i, -800);
+ }
+ ctx.lineWidth = 0.5;
+ ctx.strokeStyle = "#899";
+ ctx.stroke();
+ hazardSlime.query();
+ if (level.isHazardRise) hazardSlime.level(true)
+ //draw wires
+ // ctx.beginPath();
+ // ctx.moveTo(-500, -800);
+ // ctx.quadraticCurveTo(-800, -100, -1800, -375);
+ // ctx.moveTo(-600, -800);
+ // ctx.quadraticCurveTo(-800, -200, -1800, -325);
+ // ctx.lineWidth = 1;
+ // ctx.strokeStyle = "#9aa";
+ // ctx.stroke();
+ };
+ level.setPosToSpawn(0, -50); //normal spawn
+ spawn.mapRect(level.enter.x, level.enter.y + 25, 100, 10);
+ level.exit.x = 0;
+ level.exit.y = 40000;
+ level.defaultZoom = 1000
+ simulation.zoomTransition(level.defaultZoom)
+ // document.body.style.backgroundColor = "#aaa";
+ document.body.style.backgroundColor = "#ddd";
+ color.map = "#586363" //808f8f"
+
+ spawn.mapRect(-3000, 800, 5000, 1200); //bottom
+ spawn.mapRect(-2000, -2000, 5000, 1200); //ceiling
+ spawn.mapRect(-3000, -2000, 1200, 3400); //left
+ spawn.mapRect(1800, -1400, 1200, 3400); //right
+
+ spawn.mapRect(-500, 0, 1000, 50); //center platform
+ spawn.mapRect(-500, -25, 25, 50); //edge shelf
+ spawn.mapRect(475, -25, 25, 50); //edge shelf
+ },
+ intro() {
+ // console.log(level.levelsCleared)
+ if (level.levelsCleared === 0) { //if this is the 1st level of the game
+ //wait to spawn power ups until unpaused
+ //power ups don't spawn in experiment mode, so they don't get removed at the start of experiment mode
+ const goal = simulation.cycle + 10
+
+ function cycle() {
+ if (simulation.cycle > goal) {
+ if (localSettings.loreCount === 6) {
+ powerUps.spawn(2095 + 15 * (Math.random() - 0.5), -2170, "field", false);
+ } else {
+ powerUps.spawnStartingPowerUps(2095 + 15 * (Math.random() - 0.5), -2070 - 125);
+ }
+ if (simulation.difficultyMode < 5) {
+ powerUps.spawn(2095 + 15 * (Math.random() - 0.5), -2070 - 25, "heal", false);
+ powerUps.spawn(2095 + 15 * (Math.random() - 0.5), -2070 - 75, "heal", false);
+ powerUps.spawn(2095 + 15 * (Math.random() - 0.5), -2070, "research", false); //not on why difficulty
+ }
+ } else {
+ requestAnimationFrame(cycle);
+ }
+ }
+ requestAnimationFrame(cycle);
+
+ if (localSettings.levelsClearedLastGame < 3) {
+ if (!simulation.isCheating && !m.isShipMode && !build.isExperimentRun) {
+ spawn.wireFoot();
+ spawn.wireFootLeft();
+ spawn.wireKnee();
+ spawn.wireKneeLeft();
+ spawn.wireHead();
+ // for (let i = 0; i < 3; i++) powerUps.spawn(2095, -1220 - 50 * i, "tech", false); //unavailable tech spawns
+ // spawn.mapRect(2000, -1025, 200, 25);
+ }
+ } else if (!build.isExperimentRun) {
+ simulation.trails()
+ //bonus power ups for clearing runs in the last game
+ if (!simulation.isCheating && localSettings.levelsClearedLastGame > 1) {
+ for (let i = 0; i < localSettings.levelsClearedLastGame / 3; i++) powerUps.spawn(2095 + 2 * Math.random(), -1270 - 50 * i, "tech", false); //spawn a tech for levels cleared in last game
+ simulation.makeTextLog(`for (let i = 0; i < localSettings.levelsClearedLastGame / 3; i++)`);
+ simulation.makeTextLog(`{ powerUps.spawn(m.pos.x, m.pos.y, "tech") //simulation superposition}`);
+ localSettings.levelsClearedLastGame = 0 //after getting bonus power ups reset run history
+ if (localSettings.isAllowed) localStorage.setItem("localSettings", JSON.stringify(localSettings)); //update local storage
+ }
+ }
+ spawn.mapRect(2025, 0, 150, 50); //lid to floor hole
+ } else {
+ for (let i = 0; i < 60; i++) {
+ setTimeout(() => {
+ if (level.levels[level.onLevel] === "intro") spawn.sneaker(2100, -1500 - 50 * i);
+ }, 2000 + 500 * i);
+ }
+ }
+ const wires = new Path2D() //pre-draw the complex lighting path to save processing
+ wires.moveTo(-150, -275)
+ wires.lineTo(80, -275)
+ wires.lineTo(80, -1000)
+ wires.moveTo(-150, -265)
+ wires.lineTo(90, -265)
+ wires.lineTo(90, -1000)
+ wires.moveTo(-150, -255)
+ wires.lineTo(100, -255)
+ wires.lineTo(100, -1000)
+ wires.moveTo(-150, -245)
+ wires.lineTo(1145, -245)
+ wires.lineTo(1145, 0)
+ wires.moveTo(-150, -235)
+ wires.lineTo(1135, -235)
+ wires.lineTo(1135, 0)
+ wires.moveTo(-150, -225)
+ wires.lineTo(1125, -225)
+ wires.lineTo(1125, 0)
+ wires.moveTo(-150, -215)
+ wires.lineTo(460, -215)
+ wires.lineTo(460, 0)
+ wires.moveTo(-150, -205)
+ wires.lineTo(450, -205)
+ wires.lineTo(450, 0)
+ wires.moveTo(-150, -195)
+ wires.lineTo(440, -195)
+ wires.lineTo(440, 0)
+
+ wires.moveTo(1155, 0)
+ wires.lineTo(1155, -450)
+ wires.lineTo(1000, -450)
+ wires.lineTo(1000, -1000)
+ wires.moveTo(1165, 0)
+ wires.lineTo(1165, -460)
+ wires.lineTo(1010, -460)
+ wires.lineTo(1010, -1000)
+ wires.moveTo(1175, 0)
+ wires.lineTo(1175, -470)
+ wires.lineTo(1020, -470)
+ wires.lineTo(1020, -1000)
+ wires.moveTo(1185, 0)
+ wires.lineTo(1185, -480)
+ wires.lineTo(1030, -480)
+ wires.lineTo(1030, -1000)
+ wires.moveTo(1195, 0)
+ wires.lineTo(1195, -490)
+ wires.lineTo(1040, -490)
+ wires.lineTo(1040, -1000)
+
+ wires.moveTo(1625, -1000)
+ wires.lineTo(1625, 0)
+ wires.moveTo(1635, -1000)
+ wires.lineTo(1635, 0)
+ wires.moveTo(1645, -1000)
+ wires.lineTo(1645, 0)
+ wires.moveTo(1655, -1000)
+ wires.lineTo(1655, 0)
+ wires.moveTo(1665, -1000)
+ wires.lineTo(1665, 0)
+
+ wires.moveTo(1675, -465)
+ wires.lineTo(2325, -465)
+ wires.lineTo(2325, 0)
+ wires.moveTo(1675, -455)
+ wires.lineTo(2315, -455)
+ wires.lineTo(2315, 0)
+ wires.moveTo(1675, -445)
+ wires.lineTo(2305, -445)
+ wires.lineTo(2305, 0)
+ wires.moveTo(1675, -435)
+ wires.lineTo(2295, -435)
+ wires.lineTo(2295, 0)
+
+ wires.moveTo(2335, 0)
+ wires.lineTo(2335, -710)
+ wires.lineTo(2600, -710)
+ wires.moveTo(2345, 0)
+ wires.lineTo(2345, -700)
+ wires.lineTo(2600, -700)
+ wires.moveTo(2355, 0)
+ wires.lineTo(2355, -690)
+ wires.lineTo(2600, -690)
+
+ level.custom = () => {
+ //push around power ups stuck in the tube wall
+ if (!(simulation.cycle % 30)) {
+ for (let i = 0, len = powerUp.length; i < len; i++) {
+ if (powerUp[i].position.y < -1000) powerUp[i].force.x += 0.01 * (Math.random() - 0.5) * powerUp[i].mass
+ }
+ }
+ //draw binary number
+ const binary = (localSettings.runCount >>> 0).toString(2)
+ const height = 20
+ const width = 8
+ const yOff = -40 //-580
+ let xOff = -130 //2622
+ ctx.strokeStyle = "#bff"
+ ctx.lineWidth = 1.5;
+ ctx.beginPath()
+ for (let i = 0; i < binary.length; i++) {
+ if (binary[i] === "0") {
+ ctx.moveTo(xOff, yOff)
+ ctx.lineTo(xOff, yOff + height)
+ ctx.lineTo(xOff + width, yOff + height)
+ ctx.lineTo(xOff + width, yOff)
+ ctx.lineTo(xOff, yOff)
+ xOff += 10 + width
+ } else {
+ ctx.moveTo(xOff, yOff)
+ ctx.lineTo(xOff, yOff + height)
+ xOff += 10
+ }
+ }
+ ctx.stroke();
+
+ ctx.beginPath()
+ ctx.strokeStyle = "#ccc"
+ ctx.lineWidth = 5;
+ ctx.stroke(wires);
+
+ //squares that look like they keep the wires in place
+ ctx.beginPath()
+ ctx.rect(1600, -500, 90, 100)
+ ctx.rect(-55, -285, 12, 100)
+ ctx.rect(1100, -497, 8, 54)
+ ctx.rect(2285, -200, 80, 10)
+ ctx.rect(1110, -70, 100, 10)
+ ctx.fillStyle = "#ccc"
+ ctx.fill()
+
+ //power up dispenser
+ // ctx.beginPath()
+ // for (let i = 2; i < 10; i++) {
+ // ctx.moveTo(2000, -100 * i)
+ // ctx.lineTo(2080, -100 * i)
+ // }
+ // ctx.strokeStyle = "#ddd"
+ // ctx.lineWidth = 5;
+ // ctx.stroke();
+
+ // ctx.beginPath()
+ // for (let i = 2; i < 10; i++) {
+ // ctx.arc(2040, -100 * i, 30, 0, 2 * Math.PI);
+ // ctx.moveTo(2040, -100 * i)
+ // }
+ // ctx.fillStyle = "rgba(0,0,0,0.3)"
+ // ctx.fill()
+
+ // ctx.fillStyle = "rgba(240,255,255,0.5)"
+ // ctx.fillRect(2000, -1000, 80, 700)
+
+ //exit room
+ ctx.fillStyle = "#f2f2f2"
+ ctx.fillRect(2600, -600, 400, 300)
+
+ // level.enter.draw();
+ level.exit.drawAndCheck();
+ };
+
+ level.customTopLayer = () => {
+ //exit room glow
+ ctx.fillStyle = "rgba(0,255,255,0.05)"
+ ctx.fillRect(2600, -600, 400, 300)
+ //draw shade for ceiling tech
+ ctx.fillStyle = "rgba(68, 68, 68,0.95)"
+ ctx.fillRect(2030, -2800, 150, 1800);
+ ctx.fillStyle = "rgba(68, 68, 68,0.95)"
+ ctx.fillRect(2030, 0, 150, 1800);
+ };
+
+
+
+ level.setPosToSpawn(460, -100); //normal spawn
+ // level.enter.x = -1000000; //hide enter graphic for first level by moving to the far left
+ level.exit.x = 2800;
+ level.exit.y = -335;
+ spawn.mapRect(level.exit.x, level.exit.y + 25, 100, 100); //exit bump
+ simulation.zoomScale = 1000 //1400 is normal
+ level.defaultZoom = 1600
+ simulation.zoomTransition(level.defaultZoom, 1)
+ document.body.style.backgroundColor = "#e1e1e1";
+
+ spawn.mapRect(-2750, -2800, 2600, 4600); //left wall
+ spawn.mapRect(3000, -2800, 2600, 4600); //right wall
+
+ // spawn.mapRect(-250, 0, 3600, 1800); //ground
+ spawn.mapRect(-250, 0, 2300, 1800); //split roof
+ spawn.mapRect(2150, 0, 1200, 1800); //split roof
+ spawn.mapRect(2025, -3, 25, 15); //lip on power up chamber
+ spawn.mapRect(2150, -3, 25, 15); //lip on power up chamber
+
+ // spawn.mapRect(-250, -2800, 3600, 1800); //roof
+ spawn.mapRect(-250, -2800, 2300, 1800); //split roof
+ map[map.length - 1].friction = 0
+ map[map.length - 1].frictionStatic = 0
+ spawn.mapRect(2150, -2800, 1200, 1800); //split roof
+ map[map.length - 1].friction = 0
+ map[map.length - 1].frictionStatic = 0
+ spawn.mapRect(2025, -1010, 25, 13); //lip on power up chamber
+ spawn.mapRect(2150, -1010, 25, 13); //lip on power up chamber
+
+ spawn.mapRect(2600, -300, 500, 500); //exit shelf
+ spawn.mapRect(2600, -1200, 500, 600); //exit roof
+ spawn.mapRect(-95, -1100, 80, 110); //wire source
+ spawn.mapRect(410, -10, 90, 20); //small platform for player
+
+ spawn.bodyRect(2425, -120, 70, 50);
+ spawn.bodyRect(2400, -100, 100, 60);
+ spawn.bodyRect(2500, -150, 100, 150); //exit step
+ },
+ final() {
+ // color.map = "rgba(0,0,0,0.8)"
+ const slime = level.hazard(simulation.isHorizontalFlipped ? 150 - 860 : -150, -360, 880, 259) //x, y, width, height, damage = 0.002) {
+ slime.height -= slime.maxHeight - 150 //start slime at zero
+ slime.min.y += slime.maxHeight
+ slime.max.y = slime.min.y + slime.height
+ level.custom = () => {
+ level.exit.drawAndCheck();
+ level.enter.draw();
+ };
+ level.customTopLayer = () => {
+ slime.query();
+ slime.levelRise(0.1)
+
+ ctx.fillStyle = "rgba(0,255,255,0.1)"
+ ctx.fillRect(5385, -550, 300, 250)
+ };
+
+ level.setPosToSpawn(0, -250); //normal spawn
+ spawn.mapRect(level.enter.x, level.enter.y + 20, 100, 20);
+ spawn.mapRect(5500, -330 + 20, 100, 20); //spawn this because the real exit is in the wrong spot
+ level.exit.x = 0;
+ level.exit.y = -8000;
+
+ level.defaultZoom = 2500
+ simulation.zoomTransition(level.defaultZoom)
+ document.body.style.backgroundColor = "#ddd";
+
+ for (let i = 0; i < 16; i++) powerUps.spawn(4600 + 40 * i, -30, "ammo");
+
+ spawn.mapRect(-1950, 0, 8200, 1800); //ground
+ spawn.mapRect(-1950, -1500, 1800, 1900); //left wall
+ spawn.mapRect(-1950, -3300, 8200, 1800); //roof
+ spawn.mapRect(-250, -200, 1000, 300); // shelf
+ spawn.mapRect(-250, -1700, 1000, 1250); // shelf roof
+ spawn.mapRect(705, -210, 25, 50);
+ spawn.mapRect(725, -220, 25, 50);
+ spawn.bodyRect(750, -125, 125, 125);
+ spawn.bodyRect(875, -50, 50, 50);
+
+ spawn.mapRect(5400, -1700, 400, 1150); //right wall
+ spawn.mapRect(5400, -300, 400, 400); //right wall
+ spawn.mapRect(5700, -3300, 1800, 5100); //right wall
+ spawn.mapRect(level.exit.x, level.exit.y + 20, 100, 100); //exit bump
+ spawn.mapRect(5403, -650, 400, 450); //blocking exit
+ if (mobs.mobDeaths < level.levelsCleared && !simulation.isCheating) { //pacifist run
+ for (let i = 0; i < 250; i++) spawn.starter(1000 + 4000 * Math.random(), -1500 * Math.random())
+ } else {
+ spawn.finalBoss(3000, -750)
+ }
+
+ if (simulation.isHorizontalFlipped) { //flip the map horizontally
+ level.flipHorizontal(); //only flips map,body,mob,powerUp,cons,consBB, exit
+
+ level.setPosToSpawn(0, -250);
+ level.custom = () => {
+ level.exit.drawAndCheck();
+
+ level.enter.draw();
+ };
+ level.customTopLayer = () => {
+ slime.query();
+ slime.levelRise(0.1)
+ ctx.fillStyle = "rgba(0,255,255,0.1)"
+ ctx.fillRect(-5385 - 300, -550, 300, 250)
+ };
+ }
+ if (mobs.mobDeaths < level.levelsCleared && localSettings.loreCount > 5 && !simulation.isCheating) {
+ //open door for pacifist run on final lore chapter
+ if (simulation.isHorizontalFlipped) {
+ level.exit.x = -5500 - 100;
+ } else {
+ level.exit.x = 5500;
+ }
+ level.exit.y = -330;
+ Matter.Composite.remove(engine.world, map[map.length - 1]);
+ map.splice(map.length - 1, 1);
+ simulation.draw.setPaths(); //redraw map draw path
+ level.levels.push("null")
+ }
+ },
+ gauntlet() {
+ level.custom = () => {
+ level.exit.drawAndCheck();
+
+ level.enter.draw();
+ };
+ level.customTopLayer = () => {
+ ctx.fillStyle = "rgba(0,255,255,0.1)"
+ ctx.fillRect(6400, -550, 300, 350)
+ ctx.fillStyle = "rgba(0,0,0,0.1)"
+ ctx.fillRect(-175, -975, 900, 575)
+ };
+ level.setPosToSpawn(0, -475); //normal spawn
+ spawn.mapRect(level.enter.x, level.enter.y + 20, 100, 20);
+ level.exit.x = 6500;
+ level.exit.y = -230;
+ level.defaultZoom = 1500
+ simulation.zoomTransition(level.defaultZoom)
+ document.body.style.backgroundColor = "#ddd";
+
+ // spawn.mapRect(-300, -1050, 300, 200);
+ // Matter.Body.setAngle(map[map.length - 1], -Math.PI / 4)
+
+
+ spawn.mapRect(-950, 0, 8200, 800); //ground
+ spawn.mapRect(-950, -1200, 800, 1400); //left wall
+ spawn.mapRect(-950, -1800, 8200, 800); //roof
+ spawn.mapRect(175, -700, 575, 950);
+ spawn.mapRect(-250, -425, 600, 650);
+ spawn.mapRect(-250, -1200, 1000, 250); // shelf roof
+ powerUps.spawnStartingPowerUps(600, -800);
+ spawn.blockDoor(710, -710);
+ spawn.mapRect(2500, -1200, 200, 750); //right wall
+ spawn.blockDoor(2585, -210)
+ spawn.mapRect(2500, -200, 200, 300); //right wall
+
+ spawn.mapRect(4500, -1200, 200, 750); //right wall
+ spawn.blockDoor(4585, -210)
+ spawn.mapRect(4500, -200, 200, 300); //right wall
+
+ spawn.mapRect(6400, -1200, 400, 750); //right wall
+ spawn.mapRect(6400, -200, 400, 300); //right wall
+ spawn.mapRect(6700, -1800, 800, 2600); //right wall
+ spawn.mapRect(level.exit.x, level.exit.y + 20, 100, 100); //exit bump
+
+
+ if (mobs.mobDeaths < level.levelsCleared && !simulation.isCheating) { //pacifist run
+ // spawn.setSpawnList();
+ spawn.pickList.splice(0, 1);
+ spawn.pickList.push('starter');
+ spawn.pickList.splice(0, 1);
+ spawn.pickList.push('starter');
+ spawn.starter(1500, -200, 150 + Math.random() * 30);
+ spawn.nodeGroup(3500, -200, 'starter');
+ spawn.lineGroup(5000, -200, 'starter');
+ for (let i = 0; i < 3; ++i) {
+ if (simulation.difficulty * Math.random() > 15 * i) spawn.nodeGroup(2000 + 500 * (Math.random() - 0.5), -800 + 200 * (Math.random() - 0.5), 'starter');
+ if (simulation.difficulty * Math.random() > 10 * i) spawn.lineGroup(3500 + 500 * (Math.random() - 0.5), -800 + 200 * (Math.random() - 0.5), 'starter');
+ if (simulation.difficulty * Math.random() > 7 * i) spawn.nodeGroup(5000 + 500 * (Math.random() - 0.5), -800 + 200 * (Math.random() - 0.5), 'starter');
+ }
+ } else {
+ spawn[spawn.pickList[0]](1500, -200, 150 + Math.random() * 30);
+ spawn.nodeGroup(3500, -200, spawn.allowedGroupList[Math.floor(Math.random() * spawn.allowedGroupList.length)]);
+ spawn.lineGroup(5000, -200, spawn.allowedGroupList[Math.floor(Math.random() * spawn.allowedGroupList.length)]);
+ for (let i = 0; i < 3; ++i) {
+ if (simulation.difficulty * Math.random() > 15 * i) spawn.randomGroup(2000 + 500 * (Math.random() - 0.5), -800 + 200 * (Math.random() - 0.5), Infinity);
+ if (simulation.difficulty * Math.random() > 10 * i) spawn.randomGroup(3500 + 500 * (Math.random() - 0.5), -800 + 200 * (Math.random() - 0.5), Infinity);
+ if (simulation.difficulty * Math.random() > 7 * i) spawn.randomGroup(5000 + 500 * (Math.random() - 0.5), -800 + 200 * (Math.random() - 0.5), Infinity);
+ }
+ }
+
+ powerUps.addResearchToLevel() //needs to run after mobs are spawned
+ spawn.secondaryBossChance(4125, -350)
+
+ if (simulation.isHorizontalFlipped) { //flip the map horizontally
+ level.flipHorizontal(); //only flips map,body,mob,powerUp,cons,consBB, exit
+ level.setPosToSpawn(0, -475);
+ level.custom = () => {
+ level.exit.drawAndCheck();
+
+ level.enter.draw();
+ };
+ level.customTopLayer = () => {
+ ctx.fillStyle = "rgba(0,255,255,0.1)"
+ ctx.fillRect(-6400 - 300, -550, 300, 350)
+ ctx.fillStyle = "rgba(0,0,0,0.1)"
+ ctx.fillRect(175 - 900, -975, 900, 575)
+ };
+ }
+ },
+ reservoir() {
+ level.exit.x = 1700;
+ level.exit.y = -4510;
+ spawn.mapRect(level.exit.x, level.exit.y + 25, 100, 25);
+ level.setPosToSpawn(-500, 850); //normal spawn
+ spawn.mapRect(level.enter.x, level.enter.y + 20, 100, 20);
+ level.defaultZoom = 2300
+ simulation.zoomTransition(level.defaultZoom)
+ document.body.style.backgroundColor = "#d8dadf";
+ color.map = "#3d4240"
+ powerUps.spawnStartingPowerUps(-575, -2925)
+ //walls
+ spawn.mapRect(-3500, -5000, 1500, 6500);
+ spawn.mapRect(2000, -5000, 1500, 6500);
+ spawn.mapRect(-2500, 1100, 5000, 400); //slime floor
+ spawn.mapRect(-3500, -5475, 7000, 600); //top
+ spawn.mapRect(-1925, -4900, 175, 375); //pipe
+ spawn.mapRect(-1950, -4550, 225, 25); //pipe
+ //top floor exit
+ spawn.mapRect(1475, -4900, 50, 250);
+ spawn.mapRect(1400, -4475, 650, 50);
+ // ground
+ spawn.mapVertex(-687, 1060, "700 0 -700 0 -450 -300 450 -300"); //left base
+ spawn.mapVertex(863, 1060, "700 0 -700 0 -450 -300 450 -300"); //right base
+ //entrance
+ spawn.mapRect(-730, 525, 475, 50);
+ spawn.mapRect(-730, 550, 50, 150);
+ spawn.mapRect(-305, 550, 50, 500);
+ spawn.bodyRect(-717, 700, 25, 100); //door
+ spawn.bodyRect(-717, 800, 25, 100); //door
+ //1st floor //left
+ spawn.mapVertex(-1125 + 437, -50, "490 0 350 80 -350 80 -490 0 -350 -80 350 -80");
+ spawn.mapRect(-1225, -100, 1070, 100);
+ if (Math.random() < 0.33) {
+ spawn.mapVertex(-687, -1000, "-100 -300 0 -350 100 -300 100 300 0 350 -100 300");
+ } else if (Math.random() < 0.5) {
+ spawn.mapVertex(-687, -1000, "-150 -450 0 -525 150 -450 150 450 0 525 -150 450");
+ } else {
+ spawn.mapVertex(-687, -700, "-150 0 150 0 150 450 0 525 -150 450");
+ }
+ //right
+ spawn.mapVertex(425 + 437, -50, "490 0 350 80 -350 80 -490 0 -350 -80 350 -80");
+ spawn.mapRect(325, -100, 1070, 100);
+ spawn.mapRect(175, 675, 425, 25);
+ spawn.mapRect(1125, 225, 425, 25);
+ spawn.mapRect(650, 450, 425, 25);
+ if (Math.random() < 0.33) {
+ spawn.mapVertex(855, -1000, "-100 -300 0 -350 100 -300 100 300 0 350 -100 300");
+ } else if (Math.random() < 0.5) {
+ spawn.mapVertex(855, -1000, "-150 -450 0 -525 150 -450 150 450 0 525 -150 450");
+ } else {
+ spawn.mapVertex(855, -700, "-150 0 150 0 150 450 0 525 -150 450");
+ }
+ //2nd floor
+ spawn.mapVertex(-687, -1936, "-625 50 0 100 625 50 625 -50 -625 -50");
+ spawn.mapVertex(855, -1936, "-625 50 0 100 625 50 625 -50 -625 -50");
+ //2nd floor right building
+ spawn.mapRect(550, -3050, 600, 75);
+ spawn.bodyRect(-125, -2025, 475, 25);
+ spawn.mapRect(-925, -2350, 675, 50);
+ spawn.mapRect(-825, -2825, 425, 50);
+ spawn.mapRect(-450, -3125, 50, 350);
+ spawn.mapRect(-750, -3150, 350, 50);
+ spawn.mapRect(-650, -3400, 250, 300);
+ spawn.mapRect(-650, -3675, 200, 50);
+ spawn.bodyRect(-375, -2150, 100, 150, 0.2);
+ //2nd floor left pillar
+ spawn.mapRect(-1400, -2625, 325, 25);
+ spawn.mapRect(-1450, -3225, 425, 25);
+ spawn.mapRect(-1512.5, -3825, 550, 25);
+
+ spawn.randomMob(1000, -275, 0.2);
+ spawn.randomMob(950, -1725, 0.1);
+ spawn.randomMob(-725, -1775, 0.1);
+ spawn.randomMob(-200, -2075, 0);
+ spawn.randomMob(-550, -3500, -0.2);
+ spawn.randomMob(375, -2125, 0);
+ spawn.randomMob(-700, -2450, -0.1);
+ spawn.randomMob(-1175, -2775, -0.1);
+ spawn.randomMob(1025, -3200, -0.2);
+ spawn.randomMob(-525, -3750, -0.2);
+ spawn.randomMob(1350, -2075, -0.3);
+ spawn.randomMob(1775, 1000, -0.4);
+ spawn.randomSmallMob(-575, -2925);
+ spawn.randomGroup(-400, -4400, 0);
+ if (simulation.difficulty > 1) {
+ spawn.randomLevelBoss(825, -3500);
+ spawn.secondaryBossChance(75, -1350)
+ }
+ powerUps.addResearchToLevel() //needs to run after mobs are spawned
+ const slime = level.hazard(-2000, -5000, 4000, 6060); // hazard(x, y, width, height, damage = 0.003)
+ slime.height -= slime.maxHeight - 60 //start slime at zero
+ slime.min.y += slime.maxHeight
+ slime.max.y = slime.min.y + slime.height
+ const elevator1 = level.elevator(-1625, -90, 310, 800, -2000, 0.0025, { up: 0.1, down: 0.2 }) //x, y, width, height, maxHeight, force = 0.003, friction = { up: 0.01, down: 0.2 }) {
+ const elevator2 = level.elevator(1175, -3050, 200, 250, -4475, 0.0025, { up: 0.12, down: 0.2 }) //x, y, width, height, maxHeight, force = 0.003, friction = { up: 0.01, down: 0.2 }) {
+ let waterFallWidth = 0
+ let waterFallX = 0
+ let waterFallSmoothX = 0
+ let isWaterfallFilling = false
+ const riseRate = 0.30 + Math.min(1, simulation.difficulty * 0.005)
+ const spinnerArray = []
+ if (simulation.isHorizontalFlipped) { //flip the map horizontally
+ spawn.mapVertex(584, -2500, "0 0 300 0 150 600 0 600");
+ spawn.mapVertex(1116, -2500, "0 0 300 0 300 600 150 600");
+ spawn.bodyRect(-200, -125, 625, 25);
+ level.flipHorizontal(); //only flips map,body,mob,powerUp,cons,consBB, exit
+ elevator1.holdX = -elevator1.holdX // flip the elevator horizontally
+ elevator2.holdX = -elevator2.holdX // flip the elevator horizontally
+ spinnerArray.push(level.spinner(-110, -3325, 45, 600, 0.003, 0, 0, 0.01)) // spinner(x, y, width, height, density = 0.001, angle = 0, frictionAir = 0.001, angularVelocity = 0) {
+ const boost1 = level.boost(-900, -2000, 790)
+ level.setPosToSpawn(500, 850); //normal spawn
+ level.custom = () => {
+ ctx.fillStyle = "#c0c3c9" ///!!!!!!!!!! for flipped x: newX = -oldX - width
+ ctx.fillRect(1468, -1975, 2, 1915) //elevator track
+ ctx.fillRect(-1274, -4460, 2, 1425) //elevator track
+ ctx.fillRect(1225, -3825, 25, 1850); //small pillar background
+ ctx.fillStyle = "#d0d4d6"
+ ctx.fillRect(275, -1925, 825, 2925) //large pillar background
+ ctx.fillRect(-1275, -1925, 825, 2925) //large pillar background
+ ctx.fillStyle = "#cff" //exit
+ ctx.fillRect(-2000, -4900, 525, 425)
+ level.exit.drawAndCheck();
+ level.enter.draw();
+ };
+ level.customTopLayer = () => {
+ boost1.query();
+ elevator1.move();
+ elevator2.move();
+ ctx.fillStyle = "#233"
+ ctx.beginPath(); //central dot on spinners
+ ctx.arc(spinnerArray[0].pointA.x, spinnerArray[0].pointA.y, 9, 0, 2 * Math.PI);
+ for (let i = 0, len = spinnerArray.length; i < len; i++) {
+ ctx.moveTo(spinnerArray[i].pointA.x, spinnerArray[i].pointA.y)
+ ctx.arc(spinnerArray[i].pointA.x, spinnerArray[i].pointA.y, 9, 0, 2 * Math.PI);
+ }
+ ctx.fill();
+ //shadow
+ ctx.fillStyle = "rgba(0,10,30,0.1)"
+ ctx.fillRect(-1150, -3000, 600, 1025);
+ ctx.fillRect(450, -3100, 300, 275);
+ ctx.fillRect(450, -3625, 200, 225);
+ ctx.fillRect(400, -2775, 425, 450);
+ ctx.fillRect(250, -2300, 675, 300);
+ slime.query();
+ if (isWaterfallFilling) {
+ if (slime.height < 5500) {
+ //draw slime fill
+ ctx.fillStyle = `hsla(160, 100%, 43%,${0.3 + 0.07 * Math.random()})`
+ ctx.fillRect(waterFallX, -5050, waterFallWidth, 6175 - slime.height)
+ if (!m.isBodiesAsleep) {
+ waterFallWidth = 0.98 * waterFallWidth + 4.7 * Math.random()
+ waterFallSmoothX = 0.98 * waterFallSmoothX + 3.5 * Math.random()
+ waterFallX = 1857 - waterFallSmoothX
+ ctx.fillRect(waterFallX + waterFallWidth * Math.random(), -5050, 4, 6175 - slime.height)
+ //push player down if they go under waterfall
+ if (player.position.x > waterFallX && player.position.x < waterFallX + waterFallWidth && player.position.y < slime.height) {
+ Matter.Body.setVelocity(player, {
+ x: player.velocity.x,
+ y: player.velocity.y + 2
+ });
+ }
+ }
+ slime.levelRise(riseRate)
+ }
+ } else if (Vector.magnitudeSquared(Vector.sub(player.position, level.enter)) > 100000) {
+ isWaterfallFilling = true
+ }
+ };
+ } else { //not flipped
+ spawn.mapVertex(1116, -2500, "0 0 300 0 150 600 0 600");
+ spawn.mapVertex(584, -2500, "0 0 300 0 300 600 150 600");
+ if (Math.random() < 0.1) {
+ spinnerArray.push(level.spinner(65, -300, 40, 450, 0.003, Math.PI / 2))
+ } else if (Math.random() < 0.25) {
+ spinnerArray.push(level.spinner(65, -500, 40, 500, 0.003, 0, 0, -0.015)) // spinner(x, y, width, height, density = 0.001, angle = 0, frictionAir = 0.001, angularVelocity = 0) {
+ const r = 250
+ const hexagon = `${r} 0 ${r * Math.cos(5.236)} ${r * Math.sin(5.236)} ${r * Math.cos(4.189)} ${r * Math.sin(4.189)} ${-r} 0 ${r * Math.cos(2.0944)} ${r * Math.sin(2.0944)} ${r * Math.cos(1.0472)} ${r * Math.sin(1.0472)} `
+ Matter.Body.setVertices(spinnerArray[spinnerArray.length - 1].bodyB, Vertices.fromPath(hexagon))
+ } else {
+ const W = 410;
+ const H = 30;
+ spawn.bodyRect(-120, -75, W, H, 1, spawn.propsIsNotHoldable)
+ let b = body[body.length - 1];
+ cons[cons.length] = Constraint.create({
+ pointA: {
+ x: b.position.x - (W / 2) + 50 - 211,
+ y: b.position.y - 1825
+ },
+ bodyB: b,
+ pointB: {
+ x: -(W / 2) + 50,
+ y: 0
+ },
+ damping: 0.01,
+ stiffness: 0.002,
+ length: 1800
+ });
+ cons[cons.length] = Constraint.create({
+ pointA: {
+ x: b.position.x + (W / 2) - 50 + 211,
+ y: b.position.y - 1825
+ },
+ bodyB: b,
+ pointB: {
+ x: (W / 2) - 50,
+ y: 0
+ },
+ damping: 0.01,
+ stiffness: 0.002,
+ length: 1800
+ });
+ Composite.add(engine.world, [cons[cons.length - 1], cons[cons.length - 2]])
+ }
+
+ spinnerArray.push(level.spinner(50, -3325, 45, 600, 0.003, 0, 0, 0.01)) // spinner(x, y, width, height, density = 0.001, angle = 0, frictionAir = 0.001, angularVelocity = 0) {
+ if (Math.random() < 0.5) {
+ const r = 200
+ const hexagon = `${r} 0 ${r * Math.cos(5.236)} ${r * Math.sin(5.236)} ${r * Math.cos(4.189)} ${r * Math.sin(4.189)} ${-r} 0 ${r * Math.cos(2.0944)} ${r * Math.sin(2.0944)} ${r * Math.cos(1.0472)} ${r * Math.sin(1.0472)} `
+ Matter.Body.setVertices(spinnerArray[spinnerArray.length - 1].bodyB, Vertices.fromPath(hexagon))
+ }
+
+ const boost1 = level.boost(800, -2000, 790)
+
+ level.custom = () => {
+ ctx.fillStyle = "#c0c3c9"
+ ctx.fillRect(-1470, -1975, 2, 1915) //elevator track
+ ctx.fillRect(1276, -4460, 2, 1425) //elevator track
+ ctx.fillRect(-1250, -3825, 25, 1850); //small pillar background
+ ctx.fillStyle = "#d0d4d6"
+ ctx.fillRect(-1100, -1925, 825, 2925) //large pillar background
+ ctx.fillRect(450, -1925, 825, 2925) //large pillar background
+ ctx.fillStyle = "#cff" //exit
+ ctx.fillRect(1475, -4900, 525, 425)
+ level.exit.drawAndCheck();
+
+ level.enter.draw();
+ };
+
+ level.customTopLayer = () => {
+ boost1.query();
+ elevator1.move();
+ elevator2.move();
+
+ ctx.fillStyle = "#233"
+ ctx.beginPath(); //central dot on spinners
+ ctx.arc(spinnerArray[0].pointA.x, spinnerArray[0].pointA.y, 9, 0, 2 * Math.PI);
+ for (let i = 0, len = spinnerArray.length; i < len; i++) {
+ ctx.moveTo(spinnerArray[i].pointA.x, spinnerArray[i].pointA.y)
+ ctx.arc(spinnerArray[i].pointA.x, spinnerArray[i].pointA.y, 9, 0, 2 * Math.PI);
+ }
+ ctx.fill();
+ //shadow
+ ctx.fillStyle = "rgba(0,10,30,0.1)"
+ ctx.fillRect(550, -3000, 600, 1025);
+ ctx.fillRect(-750, -3100, 300, 275);
+ ctx.fillRect(-650, -3625, 200, 225);
+ ctx.fillRect(-825, -2775, 425, 450);
+ ctx.fillRect(-925, -2300, 675, 300);
+
+ slime.query();
+ if (isWaterfallFilling) {
+ if (slime.height < 5500) {
+ //draw slime fill
+ ctx.fillStyle = `hsla(160, 100%, 43%,${0.3 + 0.07 * Math.random()})`
+ ctx.fillRect(waterFallX, -5050, waterFallWidth, 6175 - slime.height)
+ if (!m.isBodiesAsleep) {
+ waterFallWidth = 0.98 * waterFallWidth + 4.7 * Math.random()
+ waterFallSmoothX = 0.98 * waterFallSmoothX + 3.5 * Math.random()
+ waterFallX = waterFallSmoothX - 1985
+ ctx.fillRect(waterFallX + waterFallWidth * Math.random(), -5050, 4, 6175 - slime.height)
+ //push player down if they go under waterfall
+ if (player.position.x > waterFallX && player.position.x < waterFallX + waterFallWidth && player.position.y < slime.height) {
+ Matter.Body.setVelocity(player, {
+ x: player.velocity.x,
+ y: player.velocity.y + 2
+ });
+ }
+ }
+ slime.levelRise(riseRate)
+ }
+ } else if (Vector.magnitudeSquared(Vector.sub(player.position, level.enter)) > 100000) {
+ isWaterfallFilling = true
+ }
+ };
+ }
+ },
+ reactor() {
+ level.exit.x = 3500;
+ level.exit.y = -42;
+ spawn.mapRect(level.exit.x, level.exit.y + 25, 100, 25);
+ level.defaultZoom = 2000
+ simulation.zoomTransition(level.defaultZoom)
+ document.body.style.backgroundColor = "#c3d6df" //"#d8dadf";
+ color.map = "#303639";
+ // powerUps.spawnStartingPowerUps(1475, -1175);
+ // spawn.debris(750, -2200, 3700, 16); //16 debris per level
+
+ spawn.bodyRect(250, -70, 100, 70, 1);
+ spawn.mapRect(-425, 0, 4500, 2100);
+ spawn.mapRect(-475, -2825, 4500, 1025);
+ // spawn.mapRect(1200, -1300, 600, 800);
+ const a = 400 //side length
+ const c = 100 //corner offset
+ spawn.mapVertex(1487, -900, `${-a} ${-a + c} ${-a + c} ${-a} ${a - c} ${-a} ${a} ${-a + c} ${a} ${a - c} ${a - c} ${a} ${-a + c} ${a} ${-a} ${a - c}`); //square with edges cut off
+ //entrance
+ spawn.mapRect(-2025, -2825, 1250, 4925);
+ spawn.mapRect(-900, -2825, 1125, 1725);
+ spawn.mapRect(-900, -750, 1125, 2850);
+ spawn.mapRect(-325, -1250, 550, 300);
+ //exit
+ spawn.mapRect(3800, -2825, 1225, 4925);
+ spawn.mapRect(2750, -2150, 1325, 1775);
+ spawn.mapRect(2750, -475, 550, 300);
+ spawn.mapRect(2750, -7, 1050, 150); //exit room floor
+
+ const doorIn = level.door(-313, -950, 525, 200, 190, 2) //x, y, width, height, distance, speed = 1
+ const doorOut = level.door(2762, -175, 525, 200, 190, 2) //x, y, width, height, distance, speed = 1
+ doorIn.collisionFilter.category = cat.map;
+ doorOut.collisionFilter.category = cat.map; // to prevent boson composite from letting the player skip the level
+ // doorOut.isClosing = true
+ let isDoorsLocked = false
+ let isFightOver = false
+ let isSpawnedBoss = false
+
+ if (simulation.isHorizontalFlipped) { //flip the map horizontally
+ level.flipHorizontal(); //only flips map,body,mob,powerUp,cons,consBB, exit
+ level.setPosToSpawn(550, -800); //normal spawn
+ spawn.mapRect(level.enter.x, level.enter.y + 20, 100, 20);
+
+ const button = level.button(-1500, 0)
+ button.isUp = true
+
+ level.custom = () => {
+ if (isDoorsLocked) {
+ if (player.position.x > 300) { //if player gets trapped inside starting room open up again
+ isDoorsLocked = false
+ doorIn.isClosing = false
+ }
+ }
+ doorIn.openClose();
+ doorOut.openClose();
+ ctx.fillStyle = "#d5ebef"
+ ctx.fillRect(-3800, -375, 1050, 375)
+ level.enter.draw();
+ level.exit.drawAndCheck();
+ button.draw();
+ if (button.isUp) {
+ button.query();
+ } else if (!isSpawnedBoss) {
+ if (player.position.x < 0) {
+ if (!doorOut.isClosed() || !doorIn.isClosed()) {
+ doorIn.isClosing = true
+ doorOut.isClosing = true
+ //block caught in a door
+ if (Matter.Query.collides(doorOut, body).length > 1 || Matter.Query.collides(doorIn, body).length > 1) {
+ button.isUp = true
+ doorIn.isClosing = false
+ doorOut.isClosing = false
+ }
+ } else {
+ isSpawnedBoss = true
+ isDoorsLocked = true
+ for (let i = 0; i < 9; ++i) powerUps.spawn(-1800 + 550 * Math.random(), -1700, "ammo")
+ for (let i = 0; i < 3; ++i) powerUps.spawn(-1800 + 550 * Math.random(), -1700, "heal");
+ const scale = Math.pow(simulation.difficulty, 0.7) //hard around 30, why around 54
+ if (mobs.mobDeaths < level.levelsCleared && !simulation.isCheating) {
+ for (let i = 0; i < 250; i++) spawn.starter(-2700 + 2400 * Math.random(), -1300 - 500 * Math.random())
+ } else {
+ if (Math.random() < 0.07 && simulation.difficulty > 35) {
+ for (let i = 0, len = scale * 0.25 / 6; i < len; ++i) spawn.timeBoss(-1327 - 200 * i, -1525, 60, false); //spawn 1-2 at difficulty 15
+ for (let i = 0, len = scale * 0.1 / 6; i < len; ++i) spawn.bounceBoss(-1327 - 200 * i, -1525, 80, false);
+ for (let i = 0, len = scale * 0.15 / 6; i < len; ++i) spawn.sprayBoss(-1327 - 200 * i, -1525, 30, false)
+ for (let i = 0, len = scale * 0.26 / 6; i < len; ++i) spawn.mineBoss(-1327 - 200 * i, -1525, 50, false);
+ } else {
+ if (Math.random() < 0.25) {
+ for (let i = 0, len = scale * 0.25; i < len; ++i) spawn.timeBoss(-1327 - 200 * i, -1525, 80, false); //spawn 1-2 at difficulty 15
+ } else if (Math.random() < 0.33) {
+ for (let i = 0, len = scale * 0.1; i < len; ++i) spawn.bounceBoss(-1327 - 200 * i, -1525, 80, false); //spawn 1-2 at difficulty 15
+ } else if (Math.random() < 0.5) {
+ for (let i = 0, len = scale * 0.15; i < len; ++i) spawn.sprayBoss(-1327 - 200 * i, -1525, 30, false) //spawn 2-3 at difficulty 15
+ } else {
+ for (let i = 0, len = scale * 0.26; i < len; ++i) spawn.mineBoss(-1327 - 200 * i, -1525, 50, false); //spawn 3-4 at difficulty 15
+ }
+ }
+ }
+ spawn.secondaryBossChance(-2300, -800)
+ }
+ } else {
+ doorIn.isClosing = false
+ }
+ } else if (!isFightOver && !(simulation.cycle % 180)) {
+ let isFoundBoss = false
+ for (let i = 0; i < mob.length; i++) {
+ if (mob[i].isReactorBoss) {
+ isFoundBoss = true
+ break
+ }
+ }
+ if (!isFoundBoss) {
+ isFightOver = true
+ doorIn.isClosing = false
+ doorOut.isClosing = false
+ powerUps.spawnBossPowerUp(-3600, -100)
+ powerUps.spawn(-3650, -200, "tech")
+ // if (player.position.x < 2760 && player.position.x > 210) {}
+ }
+ }
+ };
+
+ level.customTopLayer = () => {
+ doorIn.draw();
+ doorOut.draw();
+ ctx.fillStyle = "rgba(0,0,0,0.1)"
+ ctx.fillRect(-225, -1100, 1000, 350);
+ };
+ } else {
+ level.setPosToSpawn(-550, -800); //normal spawn
+ spawn.mapRect(level.enter.x, level.enter.y + 20, 100, 20);
+
+ const button = level.button(1400, 0)
+ button.isUp = true
+
+ level.custom = () => {
+ if (isDoorsLocked) {
+ if (player.position.x < -300) { //if player gets trapped inside starting room open up again
+ isDoorsLocked = false
+ doorIn.isClosing = false
+ }
+ }
+ doorIn.openClose();
+ doorOut.openClose();
+ ctx.fillStyle = "#d5ebef"
+ ctx.fillRect(2750, -375, 1050, 375)
+ level.enter.draw();
+ level.exit.drawAndCheck();
+ button.draw();
+ if (button.isUp) {
+ button.query();
+ } else if (!isSpawnedBoss) {
+ if (player.position.x > 0) {
+ if (!doorOut.isClosed() || !doorIn.isClosed()) {
+ doorIn.isClosing = true
+ doorOut.isClosing = true
+ //block caught in a door
+ if (Matter.Query.collides(doorOut, body).length > 1 || Matter.Query.collides(doorIn, body).length > 1) {
+ button.isUp = true
+ doorIn.isClosing = false
+ doorOut.isClosing = false
+ }
+ } else {
+ isSpawnedBoss = true
+ isDoorsLocked = true
+ for (let i = 0; i < 9; ++i) powerUps.spawn(1200 + 550 * Math.random(), -1700, "ammo")
+ for (let i = 0; i < 3; ++i) powerUps.spawn(1200 + 550 * Math.random(), -1700, "heal");
+ const scale = Math.pow(simulation.difficulty, 0.7) //hard around 30, why around 54
+ if (mobs.mobDeaths < level.levelsCleared && !simulation.isCheating) {
+ for (let i = 0; i < 250; i++) spawn.starter(300 + 2400 * Math.random(), -1300 - 500 * Math.random())
+ } else {
+ if (Math.random() < 0.07 && simulation.difficulty > 35) {
+ for (let i = 0, len = scale * 0.25 / 6; i < len; ++i) spawn.timeBoss(1487 + 200 * i, -1525, 60, false); //spawn 1-2 at difficulty 15
+ for (let i = 0, len = scale * 0.1 / 6; i < len; ++i) spawn.bounceBoss(1487 + 200 * i, -1525, 80, false);
+ for (let i = 0, len = scale * 0.15 / 6; i < len; ++i) spawn.sprayBoss(1487 + 200 * i, -1525, 30, false)
+ for (let i = 0, len = scale * 0.26 / 6; i < len; ++i) spawn.mineBoss(1487 + 200 * i, -1525, 50, false);
+ } else {
+ if (Math.random() < 0.25) {
+ for (let i = 0, len = scale * 0.25; i < len; ++i) spawn.timeBoss(1487 + 200 * i, -1525, 80, false); //spawn 1-2 at difficulty 15
+ } else if (Math.random() < 0.33) {
+ for (let i = 0, len = scale * 0.1; i < len; ++i) spawn.bounceBoss(1487 + 200 * i, -1525, 80, false); //spawn 1-2 at difficulty 15
+ } else if (Math.random() < 0.5) {
+ for (let i = 0, len = scale * 0.15; i < len; ++i) spawn.sprayBoss(1487 + 200 * i, -1525, 30, false) //spawn 2-3 at difficulty 15
+ } else {
+ for (let i = 0, len = scale * 0.26; i < len; ++i) spawn.mineBoss(1487 + 200 * i, -1525, 50, false); //spawn 3-4 at difficulty 15
+ }
+ }
+ }
+ spawn.secondaryBossChance(2200, -800)
+ }
+ } else {
+ doorIn.isClosing = false
+ }
+ } else if (!isFightOver && !(simulation.cycle % 180)) {
+ let isFoundBoss = false
+ for (let i = 0; i < mob.length; i++) {
+ if (mob[i].isBoss) {
+ isFoundBoss = true
+ break
+ }
+ }
+ if (!isFoundBoss) {
+ isFightOver = true
+ doorIn.isClosing = false
+ doorOut.isClosing = false
+ powerUps.spawnBossPowerUp(3600, -100)
+ powerUps.spawn(3650, -200, "tech")
+ // if (player.position.x < 2760 && player.position.x > 210) {}
+ }
+ }
+ };
+
+ level.customTopLayer = () => {
+ doorIn.draw();
+ doorOut.draw();
+ ctx.fillStyle = "rgba(0,0,0,0.1)"
+ ctx.fillRect(-775, -1100, 1000, 350);
+ };
+ }
+
+ // if (simulation.difficulty > 1) spawn.randomLevelBoss(2200, -1300);
+ powerUps.addResearchToLevel() //needs to run after mobs are spawned
+ },
+ factory() {
+ // simulation.enableConstructMode() //remove this!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+ // level.difficultyIncrease(10 * 4) //30 is near max on hard //60 is near max on why
+
+ level.setPosToSpawn(2235, -1375); //normal spawn
+ spawn.mapRect(level.enter.x, level.enter.y + 20, 100, 20); //bump for level entrance
+ level.exit.x = 7875;
+ level.exit.y = -2480;
+
+ spawn.mapRect(level.exit.x, level.exit.y + 20, 100, 20); //bump for level exit
+ level.defaultZoom = 1500
+ simulation.zoomTransition(level.defaultZoom)
+ document.body.style.backgroundColor = "#d0d2d4s";
+ // color.map = "#262a2f"
+
+ let isPowerLeft = true
+
+ const movers = []
+ //left side
+ movers.push(level.mover(125, -140, 925, 35, -5))
+ movers.push(level.mover(1100, -437, 1100, 35, -5))
+ movers.push(level.mover(2000, -600, 850, 35, -5))
+ //right side
+ const moveSpeedStopGo = 8
+ movers.push(level.mover(2700, -200, 3600, 35, 0))
+ movers.push(level.mover(7175, -215, 2275, 50, 3))
+ movers.push(level.mover(6475, -215, 275, 100, -3))
+ movers.push(level.mover(6725, -500, 500, 375, 3))
+
+ movers.push(level.mover(7675, -725, 500, 410, 0))
+ movers.push(level.mover(6775, -1075, 375, 50, 0))
+ movers.push(level.mover(5525, -1075, 450, 50, 0))
+ movers.push(level.mover(6775, -2100, 375, 50, 0))
+ movers.push(level.mover(5450, -1900, 525, 50, 0))
+
+ function setMoverDirection(VxGoal) {
+ for (let i = 7; i < movers.length; i++) {
+ movers[i].VxGoal = VxGoal
+ }
+ }
+ setMoverDirection(0)
+
+ const buttonRight = level.button(7735, -1825)
+ buttonRight.isUp = true
+ const buttonLeft = level.button(5275, -1900)
+
+ const lasers = []
+ const laserX = 3390 //3882 - 1130 / 2
+ const laserGap = 1295 //1130
+ lasers.push(level.hazard(laserX, -500, 6, 300, 0.4))
+ lasers.push(level.hazard(laserX + laserGap, -500, 6, 300, 0.4))
+ lasers.push(level.hazard(laserX + laserGap * 2, -500, 6, 300, 0.4))
+ for (let i = 0; i < lasers.length; i++) {
+ lasers[i].isOn = false;
+ spawn.mapRect(lasers[i].min.x - 55, -550, 110, 50);
+ spawn.mapRect(lasers[i].min.x - 10, -500, 25, 20);
+ }
+ const button1 = level.button(2235, -200)
+ button1.isUp = true
+ let bonusAmmoCount = 0
+ spawnBlock = (x, y) => {
+ const index = body.length
+ spawn.bodyRect(x, y, 50, 50); // spawn.bodyRect(x, y, 40 + Math.floor(30 * Math.random()), 40 + Math.floor(30 * Math.random()));
+ body[index].collisionFilter.category = cat.body;
+ body[index].collisionFilter.mask = cat.player | cat.map | cat.body | cat.bullet | cat.mob | cat.mobBullet
+ body[index].classType = "body";
+ Composite.add(engine.world, body[index]); //add to world
+ }
+
+ level.custom = () => {
+ if (isPowerLeft) {
+ if (!(simulation.cycle % 90)) spawnBlock(2730, -1600);
+ } else {
+ // for (let i = 0; i < trains.length; i++) {
+ // //oscillate back and forth
+ // if (trains[i].position.x < 5275) {
+ // trains[i].changeDirection(true) //go right
+ // } else if (trains[i].position.x > 7875) {
+ // trains[i].changeDirection(false) //go left
+ // }
+ // trains[i].move();
+ // }
+
+ const rate = 160 //multiples of 32!
+ if ((simulation.cycle % rate) === 80) {
+ for (let i = 0; i < lasers.length; i++) lasers[i].isOn = false;
+ movers[3].VxGoal = moveSpeedStopGo;
+ movers[3].force = 0.0005
+ movers[2].VxGoal = moveSpeedStopGo;
+ movers[2].force = 0.0005
+ } else if ((simulation.cycle % rate) === 0) {
+ movers[3].VxGoal = 0;
+ movers[3].force = 0
+ movers[2].VxGoal = 0;
+ movers[2].force = 0
+ spawnBlock(2730, -1600); //3315, -1600);
+ if ((simulation.cycle % (rate * 3)) === 0) {
+ if (bonusAmmoCount < 3 && Math.random() < 0.5) { //some extra ammo because of all the extra mobs that don't drop ammo
+ bonusAmmoCount++
+ powerUps.spawn(2760, -1550, Math.random() < 0.5 ? "heal" : "ammo", false);
+ }
+
+ for (let i = 0; i < lasers.length; i++) lasers[i].isOn = true;
+ const block2Mob = (laserIndex) => { //convert block into mob
+ const laserHit = Matter.Query.ray(body, lasers[laserIndex].min, lasers[laserIndex].max) //check for collisions with 3rd laser
+ if (laserHit.length) {
+ for (let i = 0; i < body.length; i++) {
+ if (laserHit[0].body.id === body[i].id) { //need to find the block id so it can be removed
+ const list = ["flutter", "flutter", "flutter", "hopper", "slasher", "slasher", "slasher", "stabber", "springer", "striker", "sneaker", "launcher", "launcherOne", "exploder", "sucker", "spinner", "grower", "beamer", "spawner", "ghoster"]
+ const pick = list[Math.floor(Math.random() * list.length)]
+ spawn[pick](lasers[laserIndex].max.x, lasers[laserIndex].max.y - 20);
+ const who = mob[mob.length - 1]
+ Matter.Body.setVelocity(who, { x: (8 + 5 * Math.random()), y: -(14 + 10 * Math.random()) });
+ who.locatePlayer()
+ who.leaveBody = false;
+ who.isDropPowerUp = false
+ //remove block
+ Matter.Composite.remove(engine.world, body[i]);
+ body.splice(i, 1);
+ break
+ }
+ }
+ }
+ }
+ if (mob.length < 100 && !m.isBodiesAsleep) {
+ block2Mob(0)
+ block2Mob(1)
+ block2Mob(2)
+ }
+ }
+ }
+ }
+ if (buttonLeft.isUp) {
+ buttonLeft.query();
+ if (!buttonLeft.isUp) {
+ setMoverDirection(7)
+ buttonRight.isUp = true //flip the other button up
+ }
+ } else if (buttonRight.isUp) {
+ buttonRight.query();
+ if (!buttonRight.isUp) {
+ setMoverDirection(-7)
+ //check for blocks and remove them
+ const list = Matter.Query.region(body, buttonLeft) //are any blocks colliding with this
+ buttonLeft.isUp = true //flip the other button up
+ if (list.length > 0) {
+ list[0].isRemoveMeNow = true
+ for (let i = 1; i < body.length; i++) { //find which index in body array it is and remove from array
+ if (body[i].isRemoveMeNow) {
+ Matter.Composite.remove(engine.world, list[0]);
+ body.splice(i, 1);
+ break
+ }
+ }
+ }
+ }
+ }
+
+ if (button1.isUp) {
+ button1.query();
+ if (!button1.isUp) {
+ isPowerLeft = false
+ for (let i = 0; i < 3; i++) {
+ movers[i].VxGoal = 0;
+ movers[i].force = movers[i].VxGoal > 0 ? 0.0005 : -0.0005
+ }
+ powerUps.spawnStartingPowerUps(2760, -1550);
+ spawn.randomMob(2700, -350, 0.2);
+ spawn.randomMob(6975, -650, 0.2);
+ spawn.randomMob(6550, -325, 0.3);
+ spawn.randomMob(7350, -350, 0.3);
+ spawn.randomMob(7925, -975, 0.5);
+ spawn.randomMob(7950, -1725, 0.5);
+ spawn.randomMob(7000, -1375, 0.3);
+ spawn.randomMob(5700, -1350, 0.5);
+ spawn.randomMob(5250, -1575, 0.5);
+ spawn.randomMob(6325, -75, 0.3);
+ spawn.randomMob(7900, -1925, 0.1);
+ spawn.randomMob(5300, -1975, 0.3);
+ spawn.randomMob(7875, -1900, 0.3);
+ spawn.randomMob(5325, -1975, 0.4);
+
+ spawn.randomGroup(3900, -725, 0.4);
+ if (simulation.difficulty > 1) spawn.randomLevelBoss(6501, -1771);
+ spawn.secondaryBossChance(6063, -661)
+ powerUps.addResearchToLevel() //needs to run after mobs are spawned
+ }
+ }
+ buttonRight.draw();
+ buttonLeft.draw();
+ button1.draw();
+ for (let i = 0; i < movers.length; i++) movers[i].push();
+ level.exit.drawAndCheck();
+ level.enter.draw();
+ ctx.fillStyle = "rgba(0,0,0,0.1)"
+ ctx.fillRect(6937, -2075, 50, 1775); //6937, -1050, 50, 675);
+ ctx.fillStyle = "rgba(0,255,255,0.15)" // ctx.fillStyle = "#f2f2f2"
+ ctx.fillRect(7675, -2875, 500, 425); //exit room
+ };
+ level.customTopLayer = () => {
+ if (isPowerLeft) {
+ ctx.fillStyle = "rgba(0,0,0,0.2)"
+ ctx.fillRect(2400, -1650, 7050, 2750) //right side
+ ctx.fillRect(4950, -3075, 3225, 1425);
+ ctx.beginPath()
+ ctx.moveTo(2407, -576);
+ ctx.lineTo(2000, -573)
+ ctx.lineTo(1950, -439)
+ ctx.lineTo(1100, -432)
+ ctx.lineTo(1020, -143)
+ ctx.lineTo(125, -137)
+ ctx.lineTo(-109, 300)
+ ctx.lineTo(-125, 1089)
+ ctx.lineTo(2372, 1081)
+ ctx.lineTo(2452, 65)
+ ctx.fill();
+ } else {
+ // for (let i = 0; i < trains.length; i++) trains[i].draw()
+ ctx.beginPath()
+ ctx.moveTo(2526, -589);
+ ctx.lineTo(2531, -597)
+ ctx.lineTo(2506, -594)
+ ctx.lineTo(2850, -600)
+ ctx.lineTo(2890, -193)
+ ctx.lineTo(6300, -200)
+ ctx.lineTo(6618, 857)
+ ctx.lineTo(6622, 1100)
+ ctx.lineTo(2521, 1100)
+ ctx.fillStyle = "rgba(0,0,0,0.2)"
+ ctx.fill();
+ ctx.fillRect(-100, -1650, 2625, 2750) //left side
+ for (let i = 0; i < lasers.length; i++) lasers[i].opticalQuery()
+ }
+ ctx.fillStyle = "rgba(0,0,0,0.07)"
+ ctx.fillRect(7675, -2200, 1775, 2025);
+ ctx.fillRect(4950, -2075, 500, 1000);
+ ctx.fillRect(2050, -1650, 350, 325) //entrance room
+ for (let i = 0; i < movers.length; i++) movers[i].draw();
+ };
+ spawn.mapRect(-1550, -3050, 1450, 4150); //left wall
+ spawn.mapRect(-1550, -3050, 6525, 1400); //ceiling
+ spawn.mapRect(-1550, -3050, 6525, 1400);
+ spawn.mapRect(3000, -1700, 1975, 675); //ceiling center
+
+ spawn.mapRect(3800, -4000, 5650, 950);
+ spawn.mapRect(3800, -4000, 1175, 2975);
+ spawn.mapRect(8175, -4000, 1275, 3685); //right wall
+ spawn.mapRect(8175, -200, 1275, 1300); //right wall
+
+ spawn.mapRect(75, 0, 6275, 1100); //ground
+ spawn.mapRect(6475, -200, 2750, 1300);
+ spawn.mapRect(4975, -1087, 550, 62);
+ spawn.mapRect(4975, -1100, 500, 75);
+
+ spawn.mapRect(7875, -1100, 175, 25); //right 3 hop stairs
+ spawn.mapRect(8075, -1450, 200, 25);
+ spawn.mapRect(7675, -1825, 375, 25);
+ spawn.mapRect(7675, -1800, 250, 725);
+
+ spawn.mapRect(5125, -1275, 200, 25); //left 3 hop stairs
+ spawn.mapRect(4900, -1575, 175, 25);
+ spawn.mapRect(5125, -1900, 325, 25);
+ spawn.mapRect(5225, -1875, 225, 625);
+ spawn.mapRect(4950, -3075, 500, 1000);
+
+ //exit
+ spawn.mapRect(7675, -2450, 525, 250);
+ spawn.mapRect(7675, -3050, 550, 175);
+ spawn.mapRect(7675, -2925, 50, 175);
+
+ spawn.mapRect(1925, -1325, 550, 50); //entrance
+ spawn.mapRect(2050, -1675, 50, 175); //entrance
+ spawn.mapRect(1700, -200, 750, 275); //button shelf
+ if (Math.random() < 0.5) { //left side
+ spawn.mapRect(625, -1100, 425, 300);
+ spawn.mapRect(1375, -1100, 425, 300);
+ spawn.mapRect(1750, -835, 100, 35);
+ spawn.mapRect(-200, -525, 150, 35);
+ } else {
+ spawn.mapRect(800, -1125, 925, 400);
+ spawn.mapRect(75, -775, 400, 50);
+ spawn.mapRect(1700, -760, 75, 35);
+ spawn.mapRect(-200, -425, 150, 35);
+ }
+ spawn.mapRect(2400, -600, 125, 675);
+ spawn.mapRect(2400, -1750, 125, 1050);
+ spawn.mapRect(2700, -1700, 125, 85);
+
+ spawn.randomMob(350, -325, 0.5);
+ spawn.randomMob(875, -375, 0.5);
+ spawn.randomMob(1250, -575, 0.5);
+ spawn.randomMob(1550, -600, 0.5);
+ spawn.randomSmallMob(1250, -175);
+ spawn.randomSmallMob(1500, -229);
+ spawn.randomSmallMob(1850, -300);
+ powerUps.spawn(5200, -1300, "ammo");
+ },
labs() {
level.isProcedural = true //used in generating text it the level builder
level.defaultZoom = 1700
@@ -2083,10 +3698,7 @@ const level = {
const index = body.length
spawn.bodyRect(xPos, yPos, 35 + 50 * Math.random(), 35 + 50 * Math.random());
const bodyBullet = body[body.length - 1]
- Matter.Body.setVelocity(body[index], {
- x: -120,
- y: -5
- });
+ Matter.Body.setVelocity(body[index], { x: -120, y: -5 });
body[index].collisionFilter.category = cat.body;
body[index].collisionFilter.mask = cat.player | cat.map | cat.body | cat.bullet | cat.mob | cat.mobBullet
body[index].classType = "body";
@@ -2899,1846 +4511,6 @@ const level = {
};
powerUps.addResearchToLevel() //needs to run after mobs are spawned
},
- null() {
- level.levels.pop(); //remove lore level from rotation
- // level.onLevel--
- // console.log(level.onLevel, level.levels)
- //start a conversation based on the number of conversations seen
- if (localSettings.loreCount > lore.conversation.length - 1) localSettings.loreCount = lore.conversation.length - 1; //repeat final conversation if lore count is too high
- if (!simulation.isCheating && localSettings.loreCount < lore.conversation.length) {
- tech.isNoDraftPause = true //disable pause
- lore.testSpeechAPI() //see if speech is working
- lore.chapter = localSettings.loreCount //set the chapter to listen to to be the lore level (you can't use the lore level because it changes during conversations)
- lore.sentence = 0 //what part of the conversation to start on
- lore.conversation[lore.chapter][lore.sentence]()
- localSettings.loreCount++ //hear the next conversation next time you win
- if (localSettings.isAllowed) localStorage.setItem("localSettings", JSON.stringify(localSettings)); //update local storage
- }
- // const hazardSlime = level.hazard(-1800, 150, 3600, 650, 0.004, "hsla(160, 100%, 35%,0.75)")
- level.isHazardRise = false //this is set to true to make the slime rise up
- const hazardSlime = level.hazard(-1800, -800, 3600, 1600, 0.004)
- hazardSlime.height -= 950
- hazardSlime.min.y += 950
- hazardSlime.max.y = hazardSlime.min.y + hazardSlime.height
- const circle = {
- x: 0,
- y: -500,
- radius: 50
- }
- level.custom = () => {
- //draw wide line
- ctx.beginPath();
- ctx.moveTo(circle.x, -800)
- ctx.lineTo(circle.x, circle.y)
- ctx.lineWidth = 40;
- ctx.strokeStyle = lore.talkingColor //"#d5dddd" //"#bcc";
- ctx.globalAlpha = 0.03;
- ctx.stroke();
- ctx.globalAlpha = 1;
- //support pillar
- ctx.fillStyle = "rgba(0,0,0,0.2)";
- ctx.fillRect(-25, 0, 50, 1000);
-
- //draw circles
- ctx.beginPath();
- ctx.arc(circle.x, circle.y, circle.radius, 0, 2 * Math.PI);
- ctx.fillStyle = "#bcc"
- ctx.fill();
- ctx.lineWidth = 2;
- ctx.strokeStyle = "#abb";
- ctx.stroke();
-
- ctx.beginPath();
- ctx.arc(circle.x, circle.y, circle.radius / 8, 0, 2 * Math.PI);
- ctx.fillStyle = lore.talkingColor //"#dff"
- ctx.fill();
-
- // level.enter.draw();
- };
- let sway = {
- x: 0,
- y: 0
- }
- let phase = -Math.PI / 2
- level.customTopLayer = () => {
- ctx.fillStyle = "rgba(0,0,0,0.1)";
- ctx.fillRect(-1950, -950, 3900, 1900);
- //draw center circle lines
- ctx.beginPath();
- const step = Math.PI / 20
- const horizontalStep = 85
- if (simulation.isCheating) phase += 0.3 * Math.random() * Math.random() //(m.pos.x - circle.x) * 0.0005 //0.05 * Math.sin(simulation.cycle * 0.030)
- // const sway = 5 * Math.cos(simulation.cycle * 0.007)
- sway.x = sway.x * 0.995 + 0.005 * (m.pos.x - circle.x) * 0.05 //+ 0.04 * Math.cos(simulation.cycle * 0.01)
- sway.y = 2.5 * Math.sin(simulation.cycle * 0.015)
- for (let i = -19.5; i < 20; i++) {
- const where = {
- x: circle.x + circle.radius * Math.cos(i * step + phase),
- y: circle.y + circle.radius * Math.sin(i * step + phase)
- }
- ctx.moveTo(where.x, where.y);
- ctx.bezierCurveTo(sway.x * Math.abs(i) + where.x, where.y + 25 * Math.abs(i) + 60 + sway.y * Math.sqrt(Math.abs(i)),
- sway.x * Math.abs(i) + where.x + horizontalStep * i, where.y + 25 * Math.abs(i) + 60 + sway.y * Math.sqrt(Math.abs(i)),
- horizontalStep * i, -800);
- }
- ctx.lineWidth = 0.5;
- ctx.strokeStyle = "#899";
- ctx.stroke();
- hazardSlime.query();
- if (level.isHazardRise) hazardSlime.level(true)
- //draw wires
- // ctx.beginPath();
- // ctx.moveTo(-500, -800);
- // ctx.quadraticCurveTo(-800, -100, -1800, -375);
- // ctx.moveTo(-600, -800);
- // ctx.quadraticCurveTo(-800, -200, -1800, -325);
- // ctx.lineWidth = 1;
- // ctx.strokeStyle = "#9aa";
- // ctx.stroke();
- };
- level.setPosToSpawn(0, -50); //normal spawn
- spawn.mapRect(level.enter.x, level.enter.y + 25, 100, 10);
- level.exit.x = 0;
- level.exit.y = 40000;
- level.defaultZoom = 1000
- simulation.zoomTransition(level.defaultZoom)
- // document.body.style.backgroundColor = "#aaa";
- document.body.style.backgroundColor = "#ddd";
- color.map = "#586363" //808f8f"
-
- spawn.mapRect(-3000, 800, 5000, 1200); //bottom
- spawn.mapRect(-2000, -2000, 5000, 1200); //ceiling
- spawn.mapRect(-3000, -2000, 1200, 3400); //left
- spawn.mapRect(1800, -1400, 1200, 3400); //right
-
- spawn.mapRect(-500, 0, 1000, 50); //center platform
- spawn.mapRect(-500, -25, 25, 50); //edge shelf
- spawn.mapRect(475, -25, 25, 50); //edge shelf
- },
- testing() {
-
- const mover = level.mover(2800, -300, 1000, 25); //x,y,width.height,VxGoal,force
-
- const train = level.transport(2900, -500, 500, 25, 8); //x,y,width.height,VxGoal,force
- spawn.bodyRect(1900, -550, 50, 50);
- const button = level.button(2535, -200)
- // spawn.bodyRect(250, -450, 50, 50); //block on button
-
- level.custom = () => {
-
- //oscillate back and forth
- if (train.position.x < 2000) {
- train.changeDirection(true) //go right
- } else if (train.position.x > 4000) {
- train.changeDirection(false) //go left
- }
- if (!button.isUp) train.move();
-
- mover.push();
- ctx.fillStyle = "#d4d4d4"
- ctx.fillRect(2500, -475, 200, 300)
-
- ctx.fillStyle = "rgba(0,255,255,0.1)";
- ctx.fillRect(6400, -550, 300, 350);
- level.exit.drawAndCheck();
- level.enter.draw();
- };
- level.customTopLayer = () => {
- train.draw()
- mover.draw();
- // hazard.opticalQuery();
- button.query();
- button.draw();
- ctx.fillStyle = "rgba(0,0,0,0.1)"
- ctx.fillRect(-150, -650, 900, 250)
- };
- level.setPosToSpawn(0, -450); //normal spawn
- spawn.mapRect(level.enter.x, level.enter.y + 20, 100, 20);
- level.exit.x = 6500;
- level.exit.y = -230;
-
- // level.difficultyIncrease(14); //hard mode level 7
- level.defaultZoom = 1500
- simulation.zoomTransition(level.defaultZoom)
- document.body.style.backgroundColor = "#ddd";
- spawn.mapRect(-950, 0, 8200, 800); //ground
- spawn.mapRect(-950, -1200, 800, 1400); //left wall
- spawn.mapRect(-950, -1800, 8200, 800); //roof
- spawn.mapRect(-250, -400, 1000, 600); // shelf
- spawn.mapRect(-250, -1200, 1000, 550); // shelf roof
- // for (let i = 0; i < 10; ++i) powerUps.spawn(550, -800, "ammo", false);
-
- function blockDoor(x, y, blockSize = 58) {
- spawn.mapRect(x, y - 290, 40, 60); // door lip
- spawn.mapRect(x, y, 40, 50); // door lip
- for (let i = 0; i < 4; ++i) spawn.bodyRect(x + 5, y - 260 + i * blockSize, 30, blockSize);
- }
-
- spawn.mapRect(2500, -1200, 200, 750); //right wall
- spawn.mapRect(2500, -200, 200, 300); //right wall
- spawn.mapRect(4500, -1200, 200, 650); //right wall
- blockDoor(4585, -310)
- spawn.mapRect(4500, -300, 200, 400); //right wall
- spawn.mapRect(6400, -1200, 400, 750); //right wall
- spawn.mapRect(6400, -200, 400, 300); //right wall
- spawn.mapRect(6700, -1800, 800, 2600); //right wall
- spawn.mapRect(level.exit.x, level.exit.y + 20, 100, 100); //exit bump
- //place to hide
- spawn.mapRect(4650, -300, 1150, 50);
- spawn.mapRect(5750, -300, 50, 200);
- spawn.mapRect(5575, -100, 50, 125);
- spawn.mapRect(5300, -275, 50, 175);
- spawn.mapRect(5050, -100, 50, 150);
- spawn.mapRect(4850, -275, 50, 175);
- spawn.mapRect(-950, -3250, 850, 1750);
- //roof
- spawn.mapRect(-175, -2975, 300, 1425);
- spawn.mapRect(75, -2650, 325, 1150);
- spawn.mapRect(375, -2225, 250, 650);
- spawn.mapRect(4075, -2125, 700, 800);
- spawn.mapRect(4450, -2950, 675, 1550);
- spawn.mapRect(4875, -3625, 725, 2225);
- spawn.mapRect(5525, -4350, 1725, 2925);
- spawn.mapRect(7200, -5125, 300, 3900);
-
-
- //???
- // level.difficultyIncrease(30) //30 is near max on hard //60 is near max on why
- // m.addHealth(Infinity)
-
- // spawn.starter(1900, -500, 200) //big boy
- // for (let i = 0; i < 10; ++i) spawn.launcher(1900, -500)
- // spawn.suckerBoss(1900, -500)
- // spawn.launcherBoss(3200, -500)
- // spawn.laserTargetingBoss(1700, -500)
- // spawn.powerUpBoss(1900, -500)
- // spawn.powerUpBossBaby(3200, -500)
- // spawn.dragonFlyBoss(1700, -500)
- // spawn.streamBoss(3200, -500)
- // spawn.pulsarBoss(1700, -500)
- // spawn.spawnerBossCulture(3200, -500)
- // spawn.grenadierBoss(1700, -500)
- // spawn.growBossCulture(3200, -500)
- // spawn.blinkBoss(1700, -500)
- // spawn.snakeSpitBoss(3200, -500)
- // spawn.laserBombingBoss(1700, -500)
- // spawn.launcherBoss(3200, -500)
- // spawn.blockBoss(1700, -500)
- // spawn.blinkBoss(3200, -500)
- // spawn.spiderBoss(1700, -500)
- // spawn.tetherBoss(1700, -500) //go to actual level?
- // spawn.revolutionBoss(1900, -500)
- // spawn.bomberBoss(1400, -500)
- // spawn.cellBossCulture(1600, -500)
- // spawn.shieldingBoss(1700, -500)
-
- // for (let i = 0; i < 10; ++i) spawn.bodyRect(1600 + 5, -500, 30, 40);
- // for (let i = 0; i < 4; i++) spawn.starter(1900, -500)
- // spawn.pulsar(1900, -500)
- // spawn.shield(mob[mob.length - 1], 1900, -500, 1);
- // mob[mob.length - 1].isShielded = true
- // spawn.nodeGroup(1200, 0, "grenadier")
- // spawn.blinkBoss(1200, -500)
- // spawn.suckerBoss(2900, -500)
- // spawn.randomMob(1600, -500)
- },
- reactor() {
- level.exit.x = 3500;
- level.exit.y = -42;
- spawn.mapRect(level.exit.x, level.exit.y + 25, 100, 25);
- level.defaultZoom = 2000
- simulation.zoomTransition(level.defaultZoom)
- document.body.style.backgroundColor = "#c3d6df" //"#d8dadf";
- color.map = "#303639";
- // powerUps.spawnStartingPowerUps(1475, -1175);
- // spawn.debris(750, -2200, 3700, 16); //16 debris per level
-
- spawn.bodyRect(250, -70, 100, 70, 1);
- spawn.mapRect(-425, 0, 4500, 2100);
- spawn.mapRect(-475, -2825, 4500, 1025);
- // spawn.mapRect(1200, -1300, 600, 800);
- const a = 400 //side length
- const c = 100 //corner offset
- spawn.mapVertex(1487, -900, `${-a} ${-a + c} ${-a + c} ${-a} ${a - c} ${-a} ${a} ${-a + c} ${a} ${a - c} ${a - c} ${a} ${-a + c} ${a} ${-a} ${a - c}`); //square with edges cut off
- //entrance
- spawn.mapRect(-2025, -2825, 1250, 4925);
- spawn.mapRect(-900, -2825, 1125, 1725);
- spawn.mapRect(-900, -750, 1125, 2850);
- spawn.mapRect(-325, -1250, 550, 300);
- //exit
- spawn.mapRect(3800, -2825, 1225, 4925);
- spawn.mapRect(2750, -2150, 1325, 1775);
- spawn.mapRect(2750, -475, 550, 300);
- spawn.mapRect(2750, -7, 1050, 150); //exit room floor
-
- const doorIn = level.door(-313, -950, 525, 200, 190, 2) //x, y, width, height, distance, speed = 1
- const doorOut = level.door(2762, -175, 525, 200, 190, 2) //x, y, width, height, distance, speed = 1
- doorIn.collisionFilter.category = cat.map;
- doorOut.collisionFilter.category = cat.map; // to prevent boson composite from letting the player skip the level
- // doorOut.isClosing = true
- let isDoorsLocked = false
- let isFightOver = false
- let isSpawnedBoss = false
-
- if (simulation.isHorizontalFlipped) { //flip the map horizontally
- level.flipHorizontal(); //only flips map,body,mob,powerUp,cons,consBB, exit
- level.setPosToSpawn(550, -800); //normal spawn
- spawn.mapRect(level.enter.x, level.enter.y + 20, 100, 20);
-
- const button = level.button(-1500, 0)
- button.isUp = true
-
- level.custom = () => {
- if (isDoorsLocked) {
- if (player.position.x > 300) { //if player gets trapped inside starting room open up again
- isDoorsLocked = false
- doorIn.isClosing = false
- }
- }
- doorIn.openClose();
- doorOut.openClose();
- ctx.fillStyle = "#d5ebef"
- ctx.fillRect(-3800, -375, 1050, 375)
- level.enter.draw();
- level.exit.drawAndCheck();
- button.draw();
- if (button.isUp) {
- button.query();
- } else if (!isSpawnedBoss) {
- if (player.position.x < 0) {
- if (!doorOut.isClosed() || !doorIn.isClosed()) {
- doorIn.isClosing = true
- doorOut.isClosing = true
- //block caught in a door
- if (Matter.Query.collides(doorOut, body).length > 1 || Matter.Query.collides(doorIn, body).length > 1) {
- button.isUp = true
- doorIn.isClosing = false
- doorOut.isClosing = false
- }
- } else {
- isSpawnedBoss = true
- isDoorsLocked = true
- for (let i = 0; i < 9; ++i) powerUps.spawn(-1800 + 550 * Math.random(), -1700, "ammo")
- for (let i = 0; i < 3; ++i) powerUps.spawn(-1800 + 550 * Math.random(), -1700, "heal");
- const scale = Math.pow(simulation.difficulty, 0.7) //hard around 30, why around 54
- if (mobs.mobDeaths < level.levelsCleared && !simulation.isCheating) {
- for (let i = 0; i < 250; i++) spawn.starter(-2700 + 2400 * Math.random(), -1300 - 500 * Math.random())
- } else {
- if (Math.random() < 0.07 && simulation.difficulty > 35) {
- for (let i = 0, len = scale * 0.25 / 6; i < len; ++i) spawn.timeBoss(-1327 - 200 * i, -1525, 60, false); //spawn 1-2 at difficulty 15
- for (let i = 0, len = scale * 0.1 / 6; i < len; ++i) spawn.bounceBoss(-1327 - 200 * i, -1525, 80, false);
- for (let i = 0, len = scale * 0.15 / 6; i < len; ++i) spawn.sprayBoss(-1327 - 200 * i, -1525, 30, false)
- for (let i = 0, len = scale * 0.26 / 6; i < len; ++i) spawn.mineBoss(-1327 - 200 * i, -1525, 50, false);
- } else {
- if (Math.random() < 0.25) {
- for (let i = 0, len = scale * 0.25; i < len; ++i) spawn.timeBoss(-1327 - 200 * i, -1525, 80, false); //spawn 1-2 at difficulty 15
- } else if (Math.random() < 0.33) {
- for (let i = 0, len = scale * 0.1; i < len; ++i) spawn.bounceBoss(-1327 - 200 * i, -1525, 80, false); //spawn 1-2 at difficulty 15
- } else if (Math.random() < 0.5) {
- for (let i = 0, len = scale * 0.15; i < len; ++i) spawn.sprayBoss(-1327 - 200 * i, -1525, 30, false) //spawn 2-3 at difficulty 15
- } else {
- for (let i = 0, len = scale * 0.26; i < len; ++i) spawn.mineBoss(-1327 - 200 * i, -1525, 50, false); //spawn 3-4 at difficulty 15
- }
- }
- }
- spawn.secondaryBossChance(-2300, -800)
- }
- } else {
- doorIn.isClosing = false
- }
- } else if (!isFightOver && !(simulation.cycle % 180)) {
- let isFoundBoss = false
- for (let i = 0; i < mob.length; i++) {
- if (mob[i].isReactorBoss) {
- isFoundBoss = true
- break
- }
- }
- if (!isFoundBoss) {
- isFightOver = true
- doorIn.isClosing = false
- doorOut.isClosing = false
- powerUps.spawnBossPowerUp(-3600, -100)
- powerUps.spawn(-3650, -200, "tech")
- // if (player.position.x < 2760 && player.position.x > 210) {}
- }
- }
- };
-
- level.customTopLayer = () => {
- doorIn.draw();
- doorOut.draw();
- ctx.fillStyle = "rgba(0,0,0,0.1)"
- ctx.fillRect(-225, -1100, 1000, 350);
- };
- } else {
- level.setPosToSpawn(-550, -800); //normal spawn
- spawn.mapRect(level.enter.x, level.enter.y + 20, 100, 20);
-
- const button = level.button(1400, 0)
- button.isUp = true
-
- level.custom = () => {
- if (isDoorsLocked) {
- if (player.position.x < -300) { //if player gets trapped inside starting room open up again
- isDoorsLocked = false
- doorIn.isClosing = false
- }
- }
- doorIn.openClose();
- doorOut.openClose();
- ctx.fillStyle = "#d5ebef"
- ctx.fillRect(2750, -375, 1050, 375)
- level.enter.draw();
- level.exit.drawAndCheck();
- button.draw();
- if (button.isUp) {
- button.query();
- } else if (!isSpawnedBoss) {
- if (player.position.x > 0) {
- if (!doorOut.isClosed() || !doorIn.isClosed()) {
- doorIn.isClosing = true
- doorOut.isClosing = true
- //block caught in a door
- if (Matter.Query.collides(doorOut, body).length > 1 || Matter.Query.collides(doorIn, body).length > 1) {
- button.isUp = true
- doorIn.isClosing = false
- doorOut.isClosing = false
- }
- } else {
- isSpawnedBoss = true
- isDoorsLocked = true
- for (let i = 0; i < 9; ++i) powerUps.spawn(1200 + 550 * Math.random(), -1700, "ammo")
- for (let i = 0; i < 3; ++i) powerUps.spawn(1200 + 550 * Math.random(), -1700, "heal");
- const scale = Math.pow(simulation.difficulty, 0.7) //hard around 30, why around 54
- if (mobs.mobDeaths < level.levelsCleared && !simulation.isCheating) {
- for (let i = 0; i < 250; i++) spawn.starter(300 + 2400 * Math.random(), -1300 - 500 * Math.random())
- } else {
- if (Math.random() < 0.07 && simulation.difficulty > 35) {
- for (let i = 0, len = scale * 0.25 / 6; i < len; ++i) spawn.timeBoss(1487 + 200 * i, -1525, 60, false); //spawn 1-2 at difficulty 15
- for (let i = 0, len = scale * 0.1 / 6; i < len; ++i) spawn.bounceBoss(1487 + 200 * i, -1525, 80, false);
- for (let i = 0, len = scale * 0.15 / 6; i < len; ++i) spawn.sprayBoss(1487 + 200 * i, -1525, 30, false)
- for (let i = 0, len = scale * 0.26 / 6; i < len; ++i) spawn.mineBoss(1487 + 200 * i, -1525, 50, false);
- } else {
- if (Math.random() < 0.25) {
- for (let i = 0, len = scale * 0.25; i < len; ++i) spawn.timeBoss(1487 + 200 * i, -1525, 80, false); //spawn 1-2 at difficulty 15
- } else if (Math.random() < 0.33) {
- for (let i = 0, len = scale * 0.1; i < len; ++i) spawn.bounceBoss(1487 + 200 * i, -1525, 80, false); //spawn 1-2 at difficulty 15
- } else if (Math.random() < 0.5) {
- for (let i = 0, len = scale * 0.15; i < len; ++i) spawn.sprayBoss(1487 + 200 * i, -1525, 30, false) //spawn 2-3 at difficulty 15
- } else {
- for (let i = 0, len = scale * 0.26; i < len; ++i) spawn.mineBoss(1487 + 200 * i, -1525, 50, false); //spawn 3-4 at difficulty 15
- }
- }
- }
- spawn.secondaryBossChance(2200, -800)
- }
- } else {
- doorIn.isClosing = false
- }
- } else if (!isFightOver && !(simulation.cycle % 180)) {
- let isFoundBoss = false
- for (let i = 0; i < mob.length; i++) {
- if (mob[i].isBoss) {
- isFoundBoss = true
- break
- }
- }
- if (!isFoundBoss) {
- isFightOver = true
- doorIn.isClosing = false
- doorOut.isClosing = false
- powerUps.spawnBossPowerUp(3600, -100)
- powerUps.spawn(3650, -200, "tech")
- // if (player.position.x < 2760 && player.position.x > 210) {}
- }
- }
- };
-
- level.customTopLayer = () => {
- doorIn.draw();
- doorOut.draw();
- ctx.fillStyle = "rgba(0,0,0,0.1)"
- ctx.fillRect(-775, -1100, 1000, 350);
- };
- }
-
- // if (simulation.difficulty > 1) spawn.randomLevelBoss(2200, -1300);
- powerUps.addResearchToLevel() //needs to run after mobs are spawned
- },
- template() {
- simulation.enableConstructMode()
- level.setPosToSpawn(0, -50); //normal spawn
- level.exit.x = 1500;
- level.exit.y = -1875;
- spawn.mapRect(level.enter.x, level.enter.y + 20, 100, 20); //bump for level entrance
- spawn.mapRect(level.exit.x, level.exit.y + 20, 100, 20); //bump for level exit
- level.defaultZoom = 1800
- simulation.zoomTransition(level.defaultZoom)
- document.body.style.backgroundColor = "#d8dadf";
- // color.map = "#444" //custom map color
-
- level.custom = () => {
- level.exit.drawAndCheck();
-
- level.enter.draw();
- };
- level.customTopLayer = () => { };
-
- spawn.mapRect(-100, 0, 1000, 100);
- // powerUps.spawnStartingPowerUps(1475, -1175);
- // spawn.debris(750, -2200, 3700, 16); //16 debris per level
- // spawn.bodyRect(1540, -1110, 300, 25, 0.9);
- // spawn.randomSmallMob(1300, -70);
- // spawn.randomMob(2650, -975, 0.8);
- // spawn.randomGroup(1700, -900, 0.4);
- // if (simulation.difficulty > 1) spawn.randomLevelBoss(2200, -1300);
- // spawn.secondaryBossChance(100, -1500)
- powerUps.addResearchToLevel() //needs to run after mobs are spawned
- },
- factory() {
- // simulation.enableConstructMode() //remove this!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
- // level.difficultyIncrease(10 * 4) //30 is near max on hard //60 is near max on why
-
- level.setPosToSpawn(2235, -1375); //normal spawn
- spawn.mapRect(level.enter.x, level.enter.y + 20, 100, 20); //bump for level entrance
- level.exit.x = 7875;
- level.exit.y = -2480;
-
- spawn.mapRect(level.exit.x, level.exit.y + 20, 100, 20); //bump for level exit
- level.defaultZoom = 1500
- simulation.zoomTransition(level.defaultZoom)
- document.body.style.backgroundColor = "#d0d2d4s";
- // color.map = "#262a2f"
-
- let isPowerLeft = true
-
- const movers = []
- //left side
- movers.push(level.mover(125, -140, 925, 35, -5))
- movers.push(level.mover(1100, -437, 1100, 35, -5))
- movers.push(level.mover(2000, -600, 850, 35, -5))
- //right side
- const moveSpeedStopGo = 8
- movers.push(level.mover(2700, -200, 3600, 35, 0))
- movers.push(level.mover(7175, -215, 2275, 50, 3))
- movers.push(level.mover(6475, -215, 275, 100, -3))
- movers.push(level.mover(6725, -500, 500, 375, 3))
-
- movers.push(level.mover(7675, -725, 500, 410, 0))
- movers.push(level.mover(6775, -1075, 375, 50, 0))
- movers.push(level.mover(5525, -1075, 450, 50, 0))
- movers.push(level.mover(6775, -2100, 375, 50, 0))
- movers.push(level.mover(5450, -1900, 525, 50, 0))
-
- function setMoverDirection(VxGoal) {
- for (let i = 7; i < movers.length; i++) {
- movers[i].VxGoal = VxGoal
- }
- }
- setMoverDirection(0)
-
- const buttonRight = level.button(7735, -1825)
- buttonRight.isUp = true
- const buttonLeft = level.button(5275, -1900)
-
- const lasers = []
- const laserX = 3390 //3882 - 1130 / 2
- const laserGap = 1295 //1130
- lasers.push(level.hazard(laserX, -500, 6, 300, 0.4))
- lasers.push(level.hazard(laserX + laserGap, -500, 6, 300, 0.4))
- lasers.push(level.hazard(laserX + laserGap * 2, -500, 6, 300, 0.4))
- for (let i = 0; i < lasers.length; i++) {
- lasers[i].isOn = false;
- spawn.mapRect(lasers[i].min.x - 55, -550, 110, 50);
- spawn.mapRect(lasers[i].min.x - 10, -500, 25, 20);
- }
- const button1 = level.button(2235, -200)
- button1.isUp = true
- let bonusAmmoCount = 0
- spawnBlock = (x, y) => {
- const index = body.length
- spawn.bodyRect(x, y, 50, 50); // spawn.bodyRect(x, y, 40 + Math.floor(30 * Math.random()), 40 + Math.floor(30 * Math.random()));
- body[index].collisionFilter.category = cat.body;
- body[index].collisionFilter.mask = cat.player | cat.map | cat.body | cat.bullet | cat.mob | cat.mobBullet
- body[index].classType = "body";
- Composite.add(engine.world, body[index]); //add to world
- }
-
- level.custom = () => {
- if (isPowerLeft) {
- if (!(simulation.cycle % 90)) spawnBlock(2730, -1600);
- } else {
- // for (let i = 0; i < trains.length; i++) {
- // //oscillate back and forth
- // if (trains[i].position.x < 5275) {
- // trains[i].changeDirection(true) //go right
- // } else if (trains[i].position.x > 7875) {
- // trains[i].changeDirection(false) //go left
- // }
- // trains[i].move();
- // }
-
- const rate = 160 //multiples of 32!
- if ((simulation.cycle % rate) === 80) {
- for (let i = 0; i < lasers.length; i++) lasers[i].isOn = false;
- movers[3].VxGoal = moveSpeedStopGo;
- movers[3].force = 0.0005
- movers[2].VxGoal = moveSpeedStopGo;
- movers[2].force = 0.0005
- } else if ((simulation.cycle % rate) === 0) {
- movers[3].VxGoal = 0;
- movers[3].force = 0
- movers[2].VxGoal = 0;
- movers[2].force = 0
- spawnBlock(2730, -1600); //3315, -1600);
- if ((simulation.cycle % (rate * 3)) === 0) {
- if (bonusAmmoCount < 3 && Math.random() < 0.5) { //some extra ammo because of all the extra mobs that don't drop ammo
- bonusAmmoCount++
- powerUps.spawn(2760, -1550, Math.random() < 0.5 ? "heal" : "ammo", false);
- }
-
- for (let i = 0; i < lasers.length; i++) lasers[i].isOn = true;
- const block2Mob = (laserIndex) => { //convert block into mob
- const laserHit = Matter.Query.ray(body, lasers[laserIndex].min, lasers[laserIndex].max) //check for collisions with 3rd laser
- if (laserHit.length) {
- for (let i = 0; i < body.length; i++) {
- if (laserHit[0].body.id === body[i].id) { //need to find the block id so it can be removed
- const list = ["flutter", "flutter", "flutter", "hopper", "slasher", "slasher", "slasher", "stabber", "springer", "striker", "sneaker", "launcher", "launcherOne", "exploder", "sucker", "spinner", "grower", "beamer", "spawner", "ghoster"]
- const pick = list[Math.floor(Math.random() * list.length)]
- spawn[pick](lasers[laserIndex].max.x, lasers[laserIndex].max.y - 20);
- const who = mob[mob.length - 1]
- Matter.Body.setVelocity(who, { x: (8 + 5 * Math.random()), y: -(14 + 10 * Math.random()) });
- who.locatePlayer()
- who.leaveBody = false;
- who.isDropPowerUp = false
- //remove block
- Matter.Composite.remove(engine.world, body[i]);
- body.splice(i, 1);
- break
- }
- }
- }
- }
- if (mob.length < 100 && !m.isBodiesAsleep) {
- block2Mob(0)
- block2Mob(1)
- block2Mob(2)
- }
- }
- }
- }
- if (buttonRight.isUp) {
- buttonRight.query();
- if (!buttonRight.isUp) {
- buttonLeft.isUp = true //flip the other button up
- setMoverDirection(-7)
- }
- }
- if (buttonLeft.isUp) {
- buttonLeft.query();
- if (!buttonLeft.isUp) {
- buttonRight.isUp = true //flip the other button up
- setMoverDirection(7)
- }
- }
-
- if (button1.isUp) {
- button1.query();
- if (!button1.isUp) {
- isPowerLeft = false
- for (let i = 0; i < 3; i++) {
- movers[i].VxGoal = 0;
- movers[i].force = movers[i].VxGoal > 0 ? 0.0005 : -0.0005
- }
- powerUps.spawnStartingPowerUps(2760, -1550);
- spawn.randomMob(2700, -350, 0.2);
- spawn.randomMob(6975, -650, 0.2);
- spawn.randomMob(6550, -325, 0.3);
- spawn.randomMob(7350, -350, 0.3);
- spawn.randomMob(7925, -975, 0.5);
- spawn.randomMob(7950, -1725, 0.5);
- spawn.randomMob(7000, -1375, 0.3);
- spawn.randomMob(5700, -1350, 0.5);
- spawn.randomMob(5250, -1575, 0.5);
- spawn.randomMob(6325, -75, 0.3);
- spawn.randomMob(7900, -1925, 0.1);
- spawn.randomMob(5300, -1975, 0.3);
- spawn.randomMob(7875, -1900, 0.3);
- spawn.randomMob(5325, -1975, 0.4);
-
- spawn.randomGroup(3900, -725, 0.4);
- if (simulation.difficulty > 1) spawn.randomLevelBoss(6501, -1771);
- spawn.secondaryBossChance(6063, -661)
- powerUps.addResearchToLevel() //needs to run after mobs are spawned
- }
- }
- buttonRight.draw();
- buttonLeft.draw();
- button1.draw();
- for (let i = 0; i < movers.length; i++) movers[i].push();
- level.exit.drawAndCheck();
- level.enter.draw();
- ctx.fillStyle = "rgba(0,0,0,0.1)"
- ctx.fillRect(6937, -2075, 50, 1775); //6937, -1050, 50, 675);
- ctx.fillStyle = "rgba(0,255,255,0.15)" // ctx.fillStyle = "#f2f2f2"
- ctx.fillRect(7675, -2875, 500, 425); //exit room
- };
- level.customTopLayer = () => {
- if (isPowerLeft) {
- ctx.fillStyle = "rgba(0,0,0,0.2)"
- ctx.fillRect(2400, -1650, 7050, 2750) //right side
- ctx.fillRect(4950, -3075, 3225, 1425);
- ctx.beginPath()
- ctx.moveTo(2407, -576);
- ctx.lineTo(2000, -573)
- ctx.lineTo(1950, -439)
- ctx.lineTo(1100, -432)
- ctx.lineTo(1020, -143)
- ctx.lineTo(125, -137)
- ctx.lineTo(-109, 300)
- ctx.lineTo(-125, 1089)
- ctx.lineTo(2372, 1081)
- ctx.lineTo(2452, 65)
- ctx.fill();
- } else {
- // for (let i = 0; i < trains.length; i++) trains[i].draw()
- ctx.beginPath()
- ctx.moveTo(2526, -589);
- ctx.lineTo(2531, -597)
- ctx.lineTo(2506, -594)
- ctx.lineTo(2850, -600)
- ctx.lineTo(2890, -193)
- ctx.lineTo(6300, -200)
- ctx.lineTo(6618, 857)
- ctx.lineTo(6622, 1100)
- ctx.lineTo(2521, 1100)
- ctx.fillStyle = "rgba(0,0,0,0.2)"
- ctx.fill();
- ctx.fillRect(-100, -1650, 2625, 2750) //left side
- for (let i = 0; i < lasers.length; i++) lasers[i].opticalQuery()
- }
- ctx.fillStyle = "rgba(0,0,0,0.07)"
- ctx.fillRect(7675, -2200, 1775, 2025);
- ctx.fillRect(4950, -2075, 500, 1000);
- ctx.fillRect(2050, -1650, 350, 325) //entrance room
- for (let i = 0; i < movers.length; i++) movers[i].draw();
- };
- spawn.mapRect(-1550, -3050, 1450, 4150); //left wall
- spawn.mapRect(-1550, -3050, 6525, 1400); //ceiling
- spawn.mapRect(-1550, -3050, 6525, 1400);
- spawn.mapRect(3000, -1700, 1975, 675); //ceiling center
-
- spawn.mapRect(3800, -4000, 5650, 950);
- spawn.mapRect(3800, -4000, 1175, 2975);
- spawn.mapRect(8175, -4000, 1275, 3685); //right wall
- spawn.mapRect(8175, -200, 1275, 1300); //right wall
-
- spawn.mapRect(75, 0, 6275, 1100); //ground
- spawn.mapRect(6475, -200, 2750, 1300);
- spawn.mapRect(4975, -1087, 550, 62);
- spawn.mapRect(4975, -1100, 500, 75);
-
- spawn.mapRect(7875, -1100, 175, 25); //right 3 hop stairs
- spawn.mapRect(8075, -1450, 200, 25);
- spawn.mapRect(7675, -1825, 375, 25);
- spawn.mapRect(7675, -1800, 250, 725);
-
- spawn.mapRect(5125, -1275, 200, 25); //left 3 hop stairs
- spawn.mapRect(4900, -1575, 175, 25);
- spawn.mapRect(5125, -1900, 325, 25);
- spawn.mapRect(5225, -1875, 225, 625);
- spawn.mapRect(4950, -3075, 500, 1000);
-
- //exit
- spawn.mapRect(7675, -2450, 525, 250);
- spawn.mapRect(7675, -3050, 550, 175);
- spawn.mapRect(7675, -2925, 50, 175);
-
- spawn.mapRect(1925, -1325, 550, 50); //entrance
- spawn.mapRect(2050, -1675, 50, 175); //entrance
- spawn.mapRect(1700, -200, 750, 275); //button shelf
- if (Math.random() < 0.5) { //left side
- spawn.mapRect(625, -1100, 425, 300);
- spawn.mapRect(1375, -1100, 425, 300);
- spawn.mapRect(1750, -835, 100, 35);
- spawn.mapRect(-200, -525, 150, 35);
- } else {
- spawn.mapRect(800, -1125, 925, 400);
- spawn.mapRect(75, -775, 400, 50);
- spawn.mapRect(1700, -760, 75, 35);
- spawn.mapRect(-200, -425, 150, 35);
- }
- spawn.mapRect(2400, -600, 125, 675);
- spawn.mapRect(2400, -1750, 125, 1050);
- spawn.mapRect(2700, -1700, 125, 85);
-
- spawn.randomMob(350, -325, 0.5);
- spawn.randomMob(875, -375, 0.5);
- spawn.randomMob(1250, -575, 0.5);
- spawn.randomMob(1550, -600, 0.5);
- spawn.randomSmallMob(1250, -175);
- spawn.randomSmallMob(1500, -229);
- spawn.randomSmallMob(1850, -300);
- powerUps.spawn(5200, -1300, "ammo");
- },
- final() {
- // color.map = "rgba(0,0,0,0.8)"
- const slime = level.hazard(simulation.isHorizontalFlipped ? 150 - 860 : -150, -360, 880, 259) //x, y, width, height, damage = 0.002) {
- slime.height -= slime.maxHeight - 150 //start slime at zero
- slime.min.y += slime.maxHeight
- slime.max.y = slime.min.y + slime.height
- level.custom = () => {
- level.exit.drawAndCheck();
- level.enter.draw();
- };
- level.customTopLayer = () => {
- slime.query();
- slime.levelRise(0.1)
-
- ctx.fillStyle = "rgba(0,255,255,0.1)"
- ctx.fillRect(5385, -550, 300, 250)
- };
-
- level.setPosToSpawn(0, -250); //normal spawn
- spawn.mapRect(level.enter.x, level.enter.y + 20, 100, 20);
- spawn.mapRect(5500, -330 + 20, 100, 20); //spawn this because the real exit is in the wrong spot
- level.exit.x = 0;
- level.exit.y = -8000;
-
- level.defaultZoom = 2500
- simulation.zoomTransition(level.defaultZoom)
- document.body.style.backgroundColor = "#ddd";
-
- for (let i = 0; i < 16; i++) powerUps.spawn(4600 + 40 * i, -30, "ammo");
-
- spawn.mapRect(-1950, 0, 8200, 1800); //ground
- spawn.mapRect(-1950, -1500, 1800, 1900); //left wall
- spawn.mapRect(-1950, -3300, 8200, 1800); //roof
- spawn.mapRect(-250, -200, 1000, 300); // shelf
- spawn.mapRect(-250, -1700, 1000, 1250); // shelf roof
- spawn.mapRect(705, -210, 25, 50);
- spawn.mapRect(725, -220, 25, 50);
- spawn.bodyRect(750, -125, 125, 125);
- spawn.bodyRect(875, -50, 50, 50);
-
- spawn.mapRect(5400, -1700, 400, 1150); //right wall
- spawn.mapRect(5400, -300, 400, 400); //right wall
- spawn.mapRect(5700, -3300, 1800, 5100); //right wall
- spawn.mapRect(level.exit.x, level.exit.y + 20, 100, 100); //exit bump
- spawn.mapRect(5403, -650, 400, 450); //blocking exit
- if (mobs.mobDeaths < level.levelsCleared && !simulation.isCheating) { //pacifist run
- for (let i = 0; i < 250; i++) spawn.starter(1000 + 4000 * Math.random(), -1500 * Math.random())
- } else {
- spawn.finalBoss(3000, -750)
- }
-
- if (simulation.isHorizontalFlipped) { //flip the map horizontally
- level.flipHorizontal(); //only flips map,body,mob,powerUp,cons,consBB, exit
-
- level.setPosToSpawn(0, -250);
- level.custom = () => {
- level.exit.drawAndCheck();
-
- level.enter.draw();
- };
- level.customTopLayer = () => {
- slime.query();
- slime.levelRise(0.1)
- ctx.fillStyle = "rgba(0,255,255,0.1)"
- ctx.fillRect(-5385 - 300, -550, 300, 250)
- };
- }
- if (mobs.mobDeaths < level.levelsCleared && localSettings.loreCount > 5 && !simulation.isCheating) {
- //open door for pacifist run on final lore chapter
- if (simulation.isHorizontalFlipped) {
- level.exit.x = -5500 - 100;
- } else {
- level.exit.x = 5500;
- }
- level.exit.y = -330;
- Matter.Composite.remove(engine.world, map[map.length - 1]);
- map.splice(map.length - 1, 1);
- simulation.draw.setPaths(); //redraw map draw path
- level.levels.push("null")
- }
- },
- gauntlet() {
- level.custom = () => {
- level.exit.drawAndCheck();
-
- level.enter.draw();
- };
- level.customTopLayer = () => {
- ctx.fillStyle = "rgba(0,255,255,0.1)"
- ctx.fillRect(6400, -550, 300, 350)
- ctx.fillStyle = "rgba(0,0,0,0.1)"
- ctx.fillRect(-175, -975, 900, 575)
- };
- level.setPosToSpawn(0, -475); //normal spawn
- spawn.mapRect(level.enter.x, level.enter.y + 20, 100, 20);
- level.exit.x = 6500;
- level.exit.y = -230;
- level.defaultZoom = 1500
- simulation.zoomTransition(level.defaultZoom)
- document.body.style.backgroundColor = "#ddd";
-
- // spawn.mapRect(-300, -1050, 300, 200);
- // Matter.Body.setAngle(map[map.length - 1], -Math.PI / 4)
-
-
- spawn.mapRect(-950, 0, 8200, 800); //ground
- spawn.mapRect(-950, -1200, 800, 1400); //left wall
- spawn.mapRect(-950, -1800, 8200, 800); //roof
- spawn.mapRect(175, -700, 575, 950);
- spawn.mapRect(-250, -425, 600, 650);
- spawn.mapRect(-250, -1200, 1000, 250); // shelf roof
- powerUps.spawnStartingPowerUps(600, -800);
- spawn.blockDoor(710, -710);
- spawn.mapRect(2500, -1200, 200, 750); //right wall
- spawn.blockDoor(2585, -210)
- spawn.mapRect(2500, -200, 200, 300); //right wall
-
- spawn.mapRect(4500, -1200, 200, 750); //right wall
- spawn.blockDoor(4585, -210)
- spawn.mapRect(4500, -200, 200, 300); //right wall
-
- spawn.mapRect(6400, -1200, 400, 750); //right wall
- spawn.mapRect(6400, -200, 400, 300); //right wall
- spawn.mapRect(6700, -1800, 800, 2600); //right wall
- spawn.mapRect(level.exit.x, level.exit.y + 20, 100, 100); //exit bump
-
-
- if (mobs.mobDeaths < level.levelsCleared && !simulation.isCheating) { //pacifist run
- // spawn.setSpawnList();
- spawn.pickList.splice(0, 1);
- spawn.pickList.push('starter');
- spawn.pickList.splice(0, 1);
- spawn.pickList.push('starter');
- spawn.starter(1500, -200, 150 + Math.random() * 30);
- spawn.nodeGroup(3500, -200, 'starter');
- spawn.lineGroup(5000, -200, 'starter');
- for (let i = 0; i < 3; ++i) {
- if (simulation.difficulty * Math.random() > 15 * i) spawn.nodeGroup(2000 + 500 * (Math.random() - 0.5), -800 + 200 * (Math.random() - 0.5), 'starter');
- if (simulation.difficulty * Math.random() > 10 * i) spawn.lineGroup(3500 + 500 * (Math.random() - 0.5), -800 + 200 * (Math.random() - 0.5), 'starter');
- if (simulation.difficulty * Math.random() > 7 * i) spawn.nodeGroup(5000 + 500 * (Math.random() - 0.5), -800 + 200 * (Math.random() - 0.5), 'starter');
- }
- } else {
- spawn[spawn.pickList[0]](1500, -200, 150 + Math.random() * 30);
- spawn.nodeGroup(3500, -200, spawn.allowedGroupList[Math.floor(Math.random() * spawn.allowedGroupList.length)]);
- spawn.lineGroup(5000, -200, spawn.allowedGroupList[Math.floor(Math.random() * spawn.allowedGroupList.length)]);
- for (let i = 0; i < 3; ++i) {
- if (simulation.difficulty * Math.random() > 15 * i) spawn.randomGroup(2000 + 500 * (Math.random() - 0.5), -800 + 200 * (Math.random() - 0.5), Infinity);
- if (simulation.difficulty * Math.random() > 10 * i) spawn.randomGroup(3500 + 500 * (Math.random() - 0.5), -800 + 200 * (Math.random() - 0.5), Infinity);
- if (simulation.difficulty * Math.random() > 7 * i) spawn.randomGroup(5000 + 500 * (Math.random() - 0.5), -800 + 200 * (Math.random() - 0.5), Infinity);
- }
- }
-
- powerUps.addResearchToLevel() //needs to run after mobs are spawned
- spawn.secondaryBossChance(4125, -350)
-
- if (simulation.isHorizontalFlipped) { //flip the map horizontally
- level.flipHorizontal(); //only flips map,body,mob,powerUp,cons,consBB, exit
- level.setPosToSpawn(0, -475);
- level.custom = () => {
- level.exit.drawAndCheck();
-
- level.enter.draw();
- };
- level.customTopLayer = () => {
- ctx.fillStyle = "rgba(0,255,255,0.1)"
- ctx.fillRect(-6400 - 300, -550, 300, 350)
- ctx.fillStyle = "rgba(0,0,0,0.1)"
- ctx.fillRect(175 - 900, -975, 900, 575)
- };
- }
- },
- map() {
-
- const elevator = level.elevator(-80.4, -931.6, 180, 50, -1550)
- 15900 && player.position.x < 16300 && player.position.y > -960.2
- const slime = level.hazard(15900, -960, 400, 6000);
- const slime2 = level.hazard(15147.2, -1782.4, 2000, 822);
- const boost1 = level.boost(5950, -20, 700)
- const boost2 = level.boost(21088, -1672, 700)
- const boost3 = level.boost(19390, -31, 1700)
- const boost4 = level.boost(19390, -31, 1700)
- const boost5 = level.boost(17274, -1242, 1000)
- const portal = level.portal({ x: 443, y: -1636 }, Math.PI, { x: 21391.9, y: -1806.3 }, -Math.PI)
- const portal2 = level.portal({ x: 16838.3, y: -626.7 }, Math.PI, { x: 16882.8, y: -2566.5 }, -Math.PI)
- const buttonDoor = level.button(21889, -10)
- const door = level.door(19119, -2133, 110, 510, 480)
- const buttonDoor2 = level.button(18711, -2210)
- const door2 = level.door(17041, -412, 110, 510, 480)
- const buttonDoor3 = level.button(20456.6, -1636.2)
- const door3 = level.door(20238, -781.4, 88, 452, 412)
- //y=-1485
-
- simulation.enableConstructMode()
- level.setPosToSpawn(0, -50); //normal spawn
- level.exit.x = 15316;
- level.exit.y = -84;
- spawn.mapRect(level.enter.x, level.enter.y + 20, 100, 20); //bump for level entrance
- spawn.mapRect(level.exit.x, level.exit.y + 20, 100, 20); //bump for level exit
- level.defaultZoom = 1800
- simulation.zoomTransition(level.defaultZoom)
- document.body.style.backgroundColor = "#001738";
- color.map = "#444" //custom map color
-
-
-
- level.custom = () => {
- //spawn.mapRect(22330, -2688.75, 400, 800);
- //spawn.mapRect(22330, -1793.5, 400, 800);//-46.25*2=-92.5
- //spawn.mapRect(22330, -804.25, 400, 800);//-46.25*3
-
- ctx.fillStyle = "rgba(63,247,251,0.8)"
- ctx.fillRect(22330, -2713.75, 550, 700) //15845.0, -1262.2
- ctx.fillRect(22330, -1743.5, 550, 700)
- ctx.fillRect(22330, -754.25, 550, 700)
- ctx.fillRect(15845.0, -1262.2, 550, 300)
- ctx.fillStyle = "rgba(235,235,235,0.9)"
- ctx.fillRect(-192, -1973, 6484, 2071)
- ctx.fillRect(15109.5, -2867.5, 7284, 2971)
- ctx.fillStyle = "rgba(35,35,35,0.8)"
- ctx.fillRect(15145.9, -960, 200, 25)
-
- ctx.fillStyle = "rgba(255,255,255,0.9)"
- ctx.fillRect(-677.3, -610.9, 15, 15)
- ctx.fillRect(-910.4, 458.3, 15, 15)
- ctx.fillRect(-1029.0, 713.7, 15, 15)
- ctx.fillRect(42.6, 1332.2, 15, 15)
- ctx.fillRect(277.3, 751.8, 15, 15)
- ctx.fillRect(797.1, 553.2, 15, 15)
- ctx.fillRect(-1458.9, 340.9, 15, 15)
- ctx.fillRect(-1780.0, -54.6, 15, 15)
- ctx.fillRect(-1260.3, -686.4, 15, 15)
- ctx.fillRect(-2064.3, -394.6, 15, 15)
- ctx.fillRect(-1815.7, 1156.2, 15, 15)
- ctx.fillRect(-1998.1, 1118.4, 15, 15)
-
-
-
- buttonDoor.query();
- buttonDoor.draw();
- buttonDoor2.query();
- buttonDoor2.draw();
- buttonDoor3.query();
- buttonDoor3.draw();
- slime.query();
- slime2.query();
-
- ctx.fillStyle = `hsla(160, 100%, 43%,${0.3 + 0.07 * Math.random()})`
- ctx.fillRect(15900 + 400 * Math.random(), -1360, 2, 6000)
- if (buttonDoor.isUp) {
- door.isClosing = true
- } else {
- door.isClosing = false
- }
- if (buttonDoor2.isUp) {
- door2.isClosing = true
- } else {
- door2.isClosing = false
- }
- if (buttonDoor3.isUp) {
- door3.isClosing = true
- } else {
- door3.isClosing = false
- }
- door.openClose();
- door2.openClose();
- door3.openClose();
- portal[2].query()
- portal[3].query()
- portal2[2].query()
- portal2[3].query()
-
- boost1.query();
- boost2.query();
- boost3.query();
- boost4.query();
- boost5.query();
- level.exit.drawAndCheck();
- level.enter.draw();
- };
-
-
- level.customTopLayer = () => {
- door.draw();
- door2.draw();
- door3.draw();
-
- portal[0].draw();
- portal[1].draw();
- portal[2].draw();
- portal[3].draw();
- portal2[0].draw();
- portal2[1].draw();
- portal2[2].draw();
- portal2[3].draw();
- elevator.move()
-
- if (player.position.x > 15900 && player.position.x < 16300 && player.position.y > -1360.2) {
- Matter.Body.setVelocity(player, {
- x: player.velocity.x,
- y: player.velocity.y + 10
- });
- } else {
-
- Matter.Body.setVelocity(player, {
- x: player.velocity.x,
- y: player.velocity.y - 0.2
- });
-
- }
-
- };
-
-
- //1273.2, -1404.7
-
- //first ship base
- spawn.mapRect(-300, 0, 6684, 100); //lower floor
- spawn.mapRect(-300, -2071, 154, 2071); //far right wall
- spawn.mapRect(2511, -300, 1309, 308); //left big block
- spawn.mapRect(3820, -184, 1309, 184); //right big block
- spawn.mapRect(-300, -739, 2549, 100); //upper right floor
- spawn.mapRect(2056, -1309, 2764, 169); //upper center floor
- spawn.mapRect(2056, -1309, 193, 650); //upper left floor wall
- spawn.mapRect(4636, -1309, 193, 793); //upper right floor wall
- spawn.mapRect(4821, -654, 955, 138); //upper right floor
- spawn.mapRect(6237, -2071, 147, 2071); //far right wall
- spawn.mapRect(-300, -2071, 6684, 154); //roof
-
- //first ship details
- spawn.mapRect(245, -360, 70, 400); //start room wall
- spawn.mapRect(500, -1929, 154, 462);
- spawn.mapRect(185, -1517, 469, 77);
- spawn.mapRect(2773, -682, 469, 77); //walls in 1st room
- spawn.mapRect(3743, -566, 77, 469);
- spawn.mapRect(3947, -851, 469, 77);
- spawn.mapRect(5313, -1309, 1000, 70); //walls in second area
- spawn.mapRect(4818, -1006, 400, 70);
- spawn.mapRect(4768, -1626, 800, 70);
- spawn.mapRect(4760, -1626, 70, 400);
-
-
- //first ship blocks/debris
- spawn.debris(3267.6, -797.1, 700, 5); //16 debris per level
- spawn.debris(1626.0, -372.5, 1700, 8); //16 debris per level
- spawn.debris(1880.1, -1508.9, 3700, 16); //16 debris per level
- spawn.debris(5335.3, -1431.6, 3700, 16); //16 debris per level
- spawn.debris(1563.8, -1087.9, 700, 5); //16 debris per level
- spawn.bodyRect(1540, -1110, 218, 125, 0.9);
-
-
-
- //first ship mobs
- spawn.randomMob(2903.9, -754.5, 0.7);
- spawn.randomMob(5577.0, -217.0, 0.6);
- spawn.randomMob(765.8, -1029.7, 0.5);
- spawn.randomMob(20079.4, -2219.7, 0.6);
- spawn.randomMob(20079.4, -2219.7, 0.7);
- spawn.randomMob(20890.9, -1306.0, 0.5);
- spawn.randomMob(21284.2, -983.1, 0.5);
- spawn.randomMob(20381.0, -254.2, 0.7);
- spawn.randomMob(21027.8, -473.8, 0.6);
- spawn.randomMob(19448.2, -1323.3, 0.6);
- spawn.randomMob(18397.7, -711.2, 0.6);
- spawn.randomMob(15547.2, -2249.6, 0.6);
- spawn.randomSmallMob(16114.6, -2524.2);
- spawn.randomSmallMob(15378.9, -2549.6);
-
- spawn.randomSmallMob(893.5, -120.8);
- spawn.randomSmallMob(3521.8, -419.6);
- spawn.randomSmallMob(4386.2, -439.6);
- spawn.randomSmallMob(5667.0, -847.8);
- spawn.randomSmallMob(3158.5, -1581.8);
- spawn.randomSmallMob(3866.7, -1483.2);
- spawn.randomSmallMob(4652.3, -1729.4);
- spawn.randomSmallMob(1068.7, -106.1);
- spawn.randomSmallMob(3545.0, -413.0);
- spawn.randomSmallMob(4231.7, -446.3);
- spawn.randomSmallMob(1456.4, -1014.8);
- spawn.randomSmallMob(20432.4, -1374.3);
- spawn.randomSmallMob(20381.0, -254.2);
- spawn.randomSmallMob(20353.4, -1845.8);
- spawn.randomSmallMob(20353.4, -1845.8);
- spawn.randomSmallMob(20648.1, -136.8);
- spawn.randomSmallMob(20024.4, -2213.1);
- spawn.randomSmallMob(17438.7, -876.7);
-
-
-
- //second ship mobs
- spawn.debris(17732.3, -550.0, 700, 5); //16 debris per level
- spawn.debris(18006.4, -2181.3, 700, 5); //16 debris per level
- spawn.debris(16108.6, -2621.1, 700, 5); //16 debris per level
- spawn.debris(20823.6, -1332.1, 1300, 5); //16 debris per level
- spawn.debris(21095.5, -423.4, 700, 5); //16 debris per level
-
-
-
-
-
-
-
- spawn.randomSmallMob(1300, -70);
-
-
- // const index = mob.length
- spawn.shieldingBoss(769.8, -1119.0)
- // console.log(mob[index].onDeath)
- // requestAnimationFrame(() => mob[index].onDeath = function() {});
- // console.log(mob[index].onDeath)
-
- //second ship base
- spawn.mapRect(15000, 0, 515, 185); //lower floor 1
- spawn.mapRect(17015, 0, 5500, 185); //lower floor 2
- spawn.mapRect(15000, -2972, 185, 2972); //left wall
- spawn.mapRect(15000, -2972, 7515, 185); //roof
- spawn.mapRect(22330, -2972, 185, 2972); //right wall
- spawn.mapRect(17002, -2972, 169, 2564); //left middle wall
- spawn.mapRect(19089, -2972, 169, 855); //right middle wall upper
- spawn.mapRect(19089, -1625, 169, 1800); //right middle wall lower
- spawn.mapRect(20760, -2972, 169, 1350); //medium wall left of portal
- spawn.mapRect(19720, -1625, 1725, 162); //right room upper floor
- spawn.mapRect(21440, -2325, 169, 863); //medium wall right of portal
- spawn.mapRect(19720, -855, 2725, 162); //right room lower floor
-
- //engines //y -2972 -> 0
- spawn.mapRect(22330, -2763.75, 400, 800);
- spawn.mapRect(22330, -1793.5, 400, 800);
- spawn.mapRect(22330, -804.25, 400, 800);
-
-
-
- //second ship details
- spawn.mapRect(19904, -1465, 85, 362); //upper L
- spawn.mapRect(19542, -1191, 412, 88); //lower L
- spawn.mapRect(18546, -2199, 600, 82); //2nd room enternce wall
- spawn.mapRect(18546, -2499, 82, 2300);
- spawn.mapRect(18108, -326, 500, 82); //walls/floors in middle room
- spawn.mapRect(17750, -682, 300, 82);
- spawn.mapRect(17156, -468, 500, 60);
- spawn.mapRect(18022, -1082, 600, 82);
- spawn.mapRect(17151, -1196, 500, 82);
- spawn.mapRect(17453, -2060, 500, 82);
- spawn.mapRect(18197, -2269, 400, 82);
- spawn.mapRect(18108, -326, 500, 82);
- spawn.mapRect(20542, -1191, 612, 88);
- spawn.mapRect(20238, -1191, 88, 412);
- spawn.mapRect(21520, -1468, 88, 412);
- spawn.mapRect(20238, -330.2, 88, 412);
- spawn.mapRect(20819, -328.3, 412, 88);
- spawn.mapRect(21532, -708, 88, 412);
- spawn.mapRect(15483.8, 12.5, 388, 30); //broken floor
- spawn.mapRect(15487.6, 76.6, 488, 24);
- spawn.mapRect(15506.5, 134.2, 288, 45);
- spawn.mapVertex(16758.6, 135.3, "400 -30 -350 -40 -400 30 400 30");
- spawn.mapVertex(16758.6, 55.3, "423 -30 -408 -20 -400 20 400 20");
- //tank
- spawn.mapRect(15310, -960, 600, 135);
- spawn.mapRect(16290, -960, 800, 135);
- //in tank
- spawn.mapRect(16524.8, -2726.8, 40, 400);
- spawn.mapRect(16524.8, -2130.9, 400, 40);
- spawn.mapRect(16010.2, -2412.2, 300, 40);
- spawn.mapRect(15379.2, -2055.1, 400, 40);
-
-
-
- //add fuel tanks in the last room
-
-
- spawn.mapRect(21531.9, -707.8, 488, 8);
-
- //22185.5, -114.8
- spawn.mapVertex(22207.8, -103, "325 -200 100 -200 325 -300");
- spawn.mapRect(22056.6, -70, 225, 212);
-
- spawn.mapVertex(20723.1, -1734, "325 -200 100 -200 325 -300");
- spawn.mapRect(20571.9, -1701.0, 225, 212);
-
- spawn.mapVertex(22207.8, -103, "325 -200 100 -200 325 -300");
- spawn.mapRect(22056.6, -70, 225, 212);
- //spawn.mapVertex(x,y, "coordinates")
- //the parts in quotes is "x y x y x y x y x y" x and y need to be the coordinates of points that define the shape in a concave clockwise direction
-
- //second ship blocks/debris
- spawn.bodyRect(21525, -113, 50, 50, 9); //first button block
- spawn.bodyRect(18993, -2283, 50, 50, 9); //second button block
- spawn.bodyRect(20303, -1736, 50, 50, 9); //third button block
-
-
-
- let randomBoss = Math.floor(Math.random() * 5); //change the bosses
- spawn[["blinkBoss", "shooterBoss", "launcherBoss", "pulsarBoss", "beetleBoss", "bladeBoss", "revolutionBoss", "dragonFlyBoss", "spiderBoss"][randomBoss]](17902, -1689, 100, false);
-
-
-
- // powerUps.spawnStartingPowerUps(1475, -1175);
- // spawn.debris(750, -2200, 3700, 16); //16 debris per level
- // spawn.bodyRect(1540, -1110, 300, 25, 0.9);
- // spawn.randomSmallMob(1300, -70);
- // spawn.randomMob(2650, -975, 0.8);
- // spawn.randomGroup(1700, -900, 0.4);
- // if (simulation.difficulty > 1) spawn.randomLevelBoss(2200, -1300);
- // spawn.secondaryBossChance(100, -1500)
- powerUps.addResearchToLevel() //needs to run after mobs are spawned
- },
- intro() {
- // console.log(level.levelsCleared)
- if (level.levelsCleared === 0) { //if this is the 1st level of the game
- //wait to spawn power ups until unpaused
- //power ups don't spawn in experiment mode, so they don't get removed at the start of experiment mode
- const goal = simulation.cycle + 10
-
- function cycle() {
- if (simulation.cycle > goal) {
- if (localSettings.loreCount === 6) {
- powerUps.spawn(2095 + 15 * (Math.random() - 0.5), -2170, "field", false);
- } else {
- powerUps.spawnStartingPowerUps(2095 + 15 * (Math.random() - 0.5), -2070 - 125);
- }
- if (simulation.difficultyMode < 5) {
- powerUps.spawn(2095 + 15 * (Math.random() - 0.5), -2070 - 25, "heal", false);
- powerUps.spawn(2095 + 15 * (Math.random() - 0.5), -2070 - 75, "heal", false);
- powerUps.spawn(2095 + 15 * (Math.random() - 0.5), -2070, "research", false); //not on why difficulty
- }
- } else {
- requestAnimationFrame(cycle);
- }
- }
- requestAnimationFrame(cycle);
-
- if (localSettings.levelsClearedLastGame < 3) {
- if (!simulation.isCheating && !m.isShipMode && !build.isExperimentRun) {
- spawn.wireFoot();
- spawn.wireFootLeft();
- spawn.wireKnee();
- spawn.wireKneeLeft();
- spawn.wireHead();
- // for (let i = 0; i < 3; i++) powerUps.spawn(2095, -1220 - 50 * i, "tech", false); //unavailable tech spawns
- // spawn.mapRect(2000, -1025, 200, 25);
- }
- } else if (!build.isExperimentRun) {
- simulation.trails()
- //bonus power ups for clearing runs in the last game
- if (!simulation.isCheating && localSettings.levelsClearedLastGame > 1) {
- for (let i = 0; i < localSettings.levelsClearedLastGame / 3; i++) powerUps.spawn(2095 + 2 * Math.random(), -1270 - 50 * i, "tech", false); //spawn a tech for levels cleared in last game
- simulation.makeTextLog(`for (let i = 0; i < localSettings.levelsClearedLastGame / 3; i++)`);
- simulation.makeTextLog(`{ powerUps.spawn(m.pos.x, m.pos.y, "tech") //simulation superposition}`);
- localSettings.levelsClearedLastGame = 0 //after getting bonus power ups reset run history
- if (localSettings.isAllowed) localStorage.setItem("localSettings", JSON.stringify(localSettings)); //update local storage
- }
- }
- spawn.mapRect(2025, 0, 150, 50); //lid to floor hole
- } else {
- for (let i = 0; i < 60; i++) {
- setTimeout(() => {
- if (level.levels[level.onLevel] === "intro") spawn.sneaker(2100, -1500 - 50 * i);
- }, 2000 + 500 * i);
- }
- }
- const wires = new Path2D() //pre-draw the complex lighting path to save processing
- wires.moveTo(-150, -275)
- wires.lineTo(80, -275)
- wires.lineTo(80, -1000)
- wires.moveTo(-150, -265)
- wires.lineTo(90, -265)
- wires.lineTo(90, -1000)
- wires.moveTo(-150, -255)
- wires.lineTo(100, -255)
- wires.lineTo(100, -1000)
- wires.moveTo(-150, -245)
- wires.lineTo(1145, -245)
- wires.lineTo(1145, 0)
- wires.moveTo(-150, -235)
- wires.lineTo(1135, -235)
- wires.lineTo(1135, 0)
- wires.moveTo(-150, -225)
- wires.lineTo(1125, -225)
- wires.lineTo(1125, 0)
- wires.moveTo(-150, -215)
- wires.lineTo(460, -215)
- wires.lineTo(460, 0)
- wires.moveTo(-150, -205)
- wires.lineTo(450, -205)
- wires.lineTo(450, 0)
- wires.moveTo(-150, -195)
- wires.lineTo(440, -195)
- wires.lineTo(440, 0)
-
- wires.moveTo(1155, 0)
- wires.lineTo(1155, -450)
- wires.lineTo(1000, -450)
- wires.lineTo(1000, -1000)
- wires.moveTo(1165, 0)
- wires.lineTo(1165, -460)
- wires.lineTo(1010, -460)
- wires.lineTo(1010, -1000)
- wires.moveTo(1175, 0)
- wires.lineTo(1175, -470)
- wires.lineTo(1020, -470)
- wires.lineTo(1020, -1000)
- wires.moveTo(1185, 0)
- wires.lineTo(1185, -480)
- wires.lineTo(1030, -480)
- wires.lineTo(1030, -1000)
- wires.moveTo(1195, 0)
- wires.lineTo(1195, -490)
- wires.lineTo(1040, -490)
- wires.lineTo(1040, -1000)
-
- wires.moveTo(1625, -1000)
- wires.lineTo(1625, 0)
- wires.moveTo(1635, -1000)
- wires.lineTo(1635, 0)
- wires.moveTo(1645, -1000)
- wires.lineTo(1645, 0)
- wires.moveTo(1655, -1000)
- wires.lineTo(1655, 0)
- wires.moveTo(1665, -1000)
- wires.lineTo(1665, 0)
-
- wires.moveTo(1675, -465)
- wires.lineTo(2325, -465)
- wires.lineTo(2325, 0)
- wires.moveTo(1675, -455)
- wires.lineTo(2315, -455)
- wires.lineTo(2315, 0)
- wires.moveTo(1675, -445)
- wires.lineTo(2305, -445)
- wires.lineTo(2305, 0)
- wires.moveTo(1675, -435)
- wires.lineTo(2295, -435)
- wires.lineTo(2295, 0)
-
- wires.moveTo(2335, 0)
- wires.lineTo(2335, -710)
- wires.lineTo(2600, -710)
- wires.moveTo(2345, 0)
- wires.lineTo(2345, -700)
- wires.lineTo(2600, -700)
- wires.moveTo(2355, 0)
- wires.lineTo(2355, -690)
- wires.lineTo(2600, -690)
-
- level.custom = () => {
- //push around power ups stuck in the tube wall
- if (!(simulation.cycle % 30)) {
- for (let i = 0, len = powerUp.length; i < len; i++) {
- if (powerUp[i].position.y < -1000) powerUp[i].force.x += 0.01 * (Math.random() - 0.5) * powerUp[i].mass
- }
- }
- //draw binary number
- const binary = (localSettings.runCount >>> 0).toString(2)
- const height = 20
- const width = 8
- const yOff = -40 //-580
- let xOff = -130 //2622
- ctx.strokeStyle = "#bff"
- ctx.lineWidth = 1.5;
- ctx.beginPath()
- for (let i = 0; i < binary.length; i++) {
- if (binary[i] === "0") {
- ctx.moveTo(xOff, yOff)
- ctx.lineTo(xOff, yOff + height)
- ctx.lineTo(xOff + width, yOff + height)
- ctx.lineTo(xOff + width, yOff)
- ctx.lineTo(xOff, yOff)
- xOff += 10 + width
- } else {
- ctx.moveTo(xOff, yOff)
- ctx.lineTo(xOff, yOff + height)
- xOff += 10
- }
- }
- ctx.stroke();
-
- ctx.beginPath()
- ctx.strokeStyle = "#ccc"
- ctx.lineWidth = 5;
- ctx.stroke(wires);
-
- //squares that look like they keep the wires in place
- ctx.beginPath()
- ctx.rect(1600, -500, 90, 100)
- ctx.rect(-55, -285, 12, 100)
- ctx.rect(1100, -497, 8, 54)
- ctx.rect(2285, -200, 80, 10)
- ctx.rect(1110, -70, 100, 10)
- ctx.fillStyle = "#ccc"
- ctx.fill()
-
- //power up dispenser
- // ctx.beginPath()
- // for (let i = 2; i < 10; i++) {
- // ctx.moveTo(2000, -100 * i)
- // ctx.lineTo(2080, -100 * i)
- // }
- // ctx.strokeStyle = "#ddd"
- // ctx.lineWidth = 5;
- // ctx.stroke();
-
- // ctx.beginPath()
- // for (let i = 2; i < 10; i++) {
- // ctx.arc(2040, -100 * i, 30, 0, 2 * Math.PI);
- // ctx.moveTo(2040, -100 * i)
- // }
- // ctx.fillStyle = "rgba(0,0,0,0.3)"
- // ctx.fill()
-
- // ctx.fillStyle = "rgba(240,255,255,0.5)"
- // ctx.fillRect(2000, -1000, 80, 700)
-
- //exit room
- ctx.fillStyle = "#f2f2f2"
- ctx.fillRect(2600, -600, 400, 300)
-
- // level.enter.draw();
- level.exit.drawAndCheck();
- };
-
- level.customTopLayer = () => {
- //exit room glow
- ctx.fillStyle = "rgba(0,255,255,0.05)"
- ctx.fillRect(2600, -600, 400, 300)
- //draw shade for ceiling tech
- ctx.fillStyle = "rgba(68, 68, 68,0.95)"
- ctx.fillRect(2030, -2800, 150, 1800);
- ctx.fillStyle = "rgba(68, 68, 68,0.95)"
- ctx.fillRect(2030, 0, 150, 1800);
- };
-
-
-
- level.setPosToSpawn(460, -100); //normal spawn
- // level.enter.x = -1000000; //hide enter graphic for first level by moving to the far left
- level.exit.x = 2800;
- level.exit.y = -335;
- spawn.mapRect(level.exit.x, level.exit.y + 25, 100, 100); //exit bump
- simulation.zoomScale = 1000 //1400 is normal
- level.defaultZoom = 1600
- simulation.zoomTransition(level.defaultZoom, 1)
- document.body.style.backgroundColor = "#e1e1e1";
-
- spawn.mapRect(-2750, -2800, 2600, 4600); //left wall
- spawn.mapRect(3000, -2800, 2600, 4600); //right wall
-
- // spawn.mapRect(-250, 0, 3600, 1800); //ground
- spawn.mapRect(-250, 0, 2300, 1800); //split roof
- spawn.mapRect(2150, 0, 1200, 1800); //split roof
- spawn.mapRect(2025, -3, 25, 15); //lip on power up chamber
- spawn.mapRect(2150, -3, 25, 15); //lip on power up chamber
-
- // spawn.mapRect(-250, -2800, 3600, 1800); //roof
- spawn.mapRect(-250, -2800, 2300, 1800); //split roof
- map[map.length - 1].friction = 0
- map[map.length - 1].frictionStatic = 0
- spawn.mapRect(2150, -2800, 1200, 1800); //split roof
- map[map.length - 1].friction = 0
- map[map.length - 1].frictionStatic = 0
- spawn.mapRect(2025, -1010, 25, 13); //lip on power up chamber
- spawn.mapRect(2150, -1010, 25, 13); //lip on power up chamber
-
- spawn.mapRect(2600, -300, 500, 500); //exit shelf
- spawn.mapRect(2600, -1200, 500, 600); //exit roof
- spawn.mapRect(-95, -1100, 80, 110); //wire source
- spawn.mapRect(410, -10, 90, 20); //small platform for player
-
- spawn.bodyRect(2425, -120, 70, 50);
- spawn.bodyRect(2400, -100, 100, 60);
- spawn.bodyRect(2500, -150, 100, 150); //exit step
- },
- reservoir() {
- level.exit.x = 1700;
- level.exit.y = -4510;
- spawn.mapRect(level.exit.x, level.exit.y + 25, 100, 25);
- level.setPosToSpawn(-500, 850); //normal spawn
- spawn.mapRect(level.enter.x, level.enter.y + 20, 100, 20);
- level.defaultZoom = 2300
- simulation.zoomTransition(level.defaultZoom)
- document.body.style.backgroundColor = "#d8dadf";
- color.map = "#3d4240"
- powerUps.spawnStartingPowerUps(-575, -2925)
- //walls
- spawn.mapRect(-3500, -5000, 1500, 6500);
- spawn.mapRect(2000, -5000, 1500, 6500);
- spawn.mapRect(-2500, 1100, 5000, 400); //slime floor
- spawn.mapRect(-3500, -5475, 7000, 600); //top
- spawn.mapRect(-1925, -4900, 175, 375); //pipe
- spawn.mapRect(-1950, -4550, 225, 25); //pipe
- //top floor exit
- spawn.mapRect(1475, -4900, 50, 250);
- spawn.mapRect(1400, -4475, 650, 50);
- // ground
- spawn.mapVertex(-687, 1060, "700 0 -700 0 -450 -300 450 -300"); //left base
- spawn.mapVertex(863, 1060, "700 0 -700 0 -450 -300 450 -300"); //right base
- //entrance
- spawn.mapRect(-730, 525, 475, 50);
- spawn.mapRect(-730, 550, 50, 150);
- spawn.mapRect(-305, 550, 50, 500);
- spawn.bodyRect(-717, 700, 25, 100); //door
- spawn.bodyRect(-717, 800, 25, 100); //door
- //1st floor //left
- spawn.mapVertex(-1125 + 437, -50, "490 0 350 80 -350 80 -490 0 -350 -80 350 -80");
- spawn.mapRect(-1225, -100, 1070, 100);
- if (Math.random() < 0.33) {
- spawn.mapVertex(-687, -1000, "-100 -300 0 -350 100 -300 100 300 0 350 -100 300");
- } else if (Math.random() < 0.5) {
- spawn.mapVertex(-687, -1000, "-150 -450 0 -525 150 -450 150 450 0 525 -150 450");
- } else {
- spawn.mapVertex(-687, -700, "-150 0 150 0 150 450 0 525 -150 450");
- }
- //right
- spawn.mapVertex(425 + 437, -50, "490 0 350 80 -350 80 -490 0 -350 -80 350 -80");
- spawn.mapRect(325, -100, 1070, 100);
- spawn.mapRect(175, 675, 425, 25);
- spawn.mapRect(1125, 225, 425, 25);
- spawn.mapRect(650, 450, 425, 25);
- if (Math.random() < 0.33) {
- spawn.mapVertex(855, -1000, "-100 -300 0 -350 100 -300 100 300 0 350 -100 300");
- } else if (Math.random() < 0.5) {
- spawn.mapVertex(855, -1000, "-150 -450 0 -525 150 -450 150 450 0 525 -150 450");
- } else {
- spawn.mapVertex(855, -700, "-150 0 150 0 150 450 0 525 -150 450");
- }
- //2nd floor
- spawn.mapVertex(-687, -1936, "-625 50 0 100 625 50 625 -50 -625 -50");
- spawn.mapVertex(855, -1936, "-625 50 0 100 625 50 625 -50 -625 -50");
- //2nd floor right building
- spawn.mapRect(550, -3050, 600, 75);
- spawn.bodyRect(-125, -2025, 475, 25);
- spawn.mapRect(-925, -2350, 675, 50);
- spawn.mapRect(-825, -2825, 425, 50);
- spawn.mapRect(-450, -3125, 50, 350);
- spawn.mapRect(-750, -3150, 350, 50);
- spawn.mapRect(-650, -3400, 250, 300);
- spawn.mapRect(-650, -3675, 200, 50);
- spawn.bodyRect(-375, -2150, 100, 150, 0.2);
- //2nd floor left pillar
- spawn.mapRect(-1400, -2625, 325, 25);
- spawn.mapRect(-1450, -3225, 425, 25);
- spawn.mapRect(-1512.5, -3825, 550, 25);
-
- spawn.randomMob(1000, -275, 0.2);
- spawn.randomMob(950, -1725, 0.1);
- spawn.randomMob(-725, -1775, 0.1);
- spawn.randomMob(-200, -2075, 0);
- spawn.randomMob(-550, -3500, -0.2);
- spawn.randomMob(375, -2125, 0);
- spawn.randomMob(-700, -2450, -0.1);
- spawn.randomMob(-1175, -2775, -0.1);
- spawn.randomMob(1025, -3200, -0.2);
- spawn.randomMob(-525, -3750, -0.2);
- spawn.randomMob(1350, -2075, -0.3);
- spawn.randomMob(1775, 1000, -0.4);
- spawn.randomSmallMob(-575, -2925);
- spawn.randomGroup(-400, -4400, 0);
- if (simulation.difficulty > 1) {
- spawn.randomLevelBoss(825, -3500);
- spawn.secondaryBossChance(75, -1350)
- }
- powerUps.addResearchToLevel() //needs to run after mobs are spawned
- const slime = level.hazard(-2000, -5000, 4000, 6060); // hazard(x, y, width, height, damage = 0.003)
- slime.height -= slime.maxHeight - 60 //start slime at zero
- slime.min.y += slime.maxHeight
- slime.max.y = slime.min.y + slime.height
- const elevator1 = level.elevator(-1625, -90, 310, 800, -2000, 0.0025, { up: 0.1, down: 0.2 }) //x, y, width, height, maxHeight, force = 0.003, friction = { up: 0.01, down: 0.2 }) {
- const elevator2 = level.elevator(1175, -3050, 200, 250, -4475, 0.0025, { up: 0.12, down: 0.2 }) //x, y, width, height, maxHeight, force = 0.003, friction = { up: 0.01, down: 0.2 }) {
- let waterFallWidth = 0
- let waterFallX = 0
- let waterFallSmoothX = 0
- let isWaterfallFilling = false
- const riseRate = 0.30 + Math.min(1, simulation.difficulty * 0.005)
- const spinnerArray = []
- if (simulation.isHorizontalFlipped) { //flip the map horizontally
- spawn.mapVertex(584, -2500, "0 0 300 0 150 600 0 600");
- spawn.mapVertex(1116, -2500, "0 0 300 0 300 600 150 600");
- spawn.bodyRect(-200, -125, 625, 25);
- level.flipHorizontal(); //only flips map,body,mob,powerUp,cons,consBB, exit
- elevator1.holdX = -elevator1.holdX // flip the elevator horizontally
- elevator2.holdX = -elevator2.holdX // flip the elevator horizontally
- spinnerArray.push(level.spinner(-110, -3325, 45, 600, 0.003, 0, 0, 0.01)) // spinner(x, y, width, height, density = 0.001, angle = 0, frictionAir = 0.001, angularVelocity = 0) {
- const boost1 = level.boost(-900, -2000, 790)
- level.setPosToSpawn(500, 850); //normal spawn
- level.custom = () => {
- ctx.fillStyle = "#c0c3c9" ///!!!!!!!!!! for flipped x: newX = -oldX - width
- ctx.fillRect(1468, -1975, 2, 1915) //elevator track
- ctx.fillRect(-1274, -4460, 2, 1425) //elevator track
- ctx.fillRect(1225, -3825, 25, 1850); //small pillar background
- ctx.fillStyle = "#d0d4d6"
- ctx.fillRect(275, -1925, 825, 2925) //large pillar background
- ctx.fillRect(-1275, -1925, 825, 2925) //large pillar background
- ctx.fillStyle = "#cff" //exit
- ctx.fillRect(-2000, -4900, 525, 425)
- level.exit.drawAndCheck();
- level.enter.draw();
- };
- level.customTopLayer = () => {
- boost1.query();
- elevator1.move();
- elevator2.move();
- ctx.fillStyle = "#233"
- ctx.beginPath(); //central dot on spinners
- ctx.arc(spinnerArray[0].pointA.x, spinnerArray[0].pointA.y, 9, 0, 2 * Math.PI);
- for (let i = 0, len = spinnerArray.length; i < len; i++) {
- ctx.moveTo(spinnerArray[i].pointA.x, spinnerArray[i].pointA.y)
- ctx.arc(spinnerArray[i].pointA.x, spinnerArray[i].pointA.y, 9, 0, 2 * Math.PI);
- }
- ctx.fill();
- //shadow
- ctx.fillStyle = "rgba(0,10,30,0.1)"
- ctx.fillRect(-1150, -3000, 600, 1025);
- ctx.fillRect(450, -3100, 300, 275);
- ctx.fillRect(450, -3625, 200, 225);
- ctx.fillRect(400, -2775, 425, 450);
- ctx.fillRect(250, -2300, 675, 300);
- slime.query();
- if (isWaterfallFilling) {
- if (slime.height < 5500) {
- //draw slime fill
- ctx.fillStyle = `hsla(160, 100%, 43%,${0.3 + 0.07 * Math.random()})`
- ctx.fillRect(waterFallX, -5050, waterFallWidth, 6175 - slime.height)
- if (!m.isBodiesAsleep) {
- waterFallWidth = 0.98 * waterFallWidth + 4.7 * Math.random()
- waterFallSmoothX = 0.98 * waterFallSmoothX + 3.5 * Math.random()
- waterFallX = 1857 - waterFallSmoothX
- ctx.fillRect(waterFallX + waterFallWidth * Math.random(), -5050, 4, 6175 - slime.height)
- //push player down if they go under waterfall
- if (player.position.x > waterFallX && player.position.x < waterFallX + waterFallWidth && player.position.y < slime.height) {
- Matter.Body.setVelocity(player, {
- x: player.velocity.x,
- y: player.velocity.y + 2
- });
- }
- }
- slime.levelRise(riseRate)
- }
- } else if (Vector.magnitudeSquared(Vector.sub(player.position, level.enter)) > 100000) {
- isWaterfallFilling = true
- }
- };
- } else { //not flipped
- spawn.mapVertex(1116, -2500, "0 0 300 0 150 600 0 600");
- spawn.mapVertex(584, -2500, "0 0 300 0 300 600 150 600");
- if (Math.random() < 0.1) {
- spinnerArray.push(level.spinner(65, -300, 40, 450, 0.003, Math.PI / 2))
- } else if (Math.random() < 0.25) {
- spinnerArray.push(level.spinner(65, -500, 40, 500, 0.003, 0, 0, -0.015)) // spinner(x, y, width, height, density = 0.001, angle = 0, frictionAir = 0.001, angularVelocity = 0) {
- const r = 250
- const hexagon = `${r} 0 ${r * Math.cos(5.236)} ${r * Math.sin(5.236)} ${r * Math.cos(4.189)} ${r * Math.sin(4.189)} ${-r} 0 ${r * Math.cos(2.0944)} ${r * Math.sin(2.0944)} ${r * Math.cos(1.0472)} ${r * Math.sin(1.0472)} `
- Matter.Body.setVertices(spinnerArray[spinnerArray.length - 1].bodyB, Vertices.fromPath(hexagon))
- } else {
- const W = 410;
- const H = 30;
- spawn.bodyRect(-120, -75, W, H, 1, spawn.propsIsNotHoldable)
- let b = body[body.length - 1];
- cons[cons.length] = Constraint.create({
- pointA: {
- x: b.position.x - (W / 2) + 50 - 211,
- y: b.position.y - 1825
- },
- bodyB: b,
- pointB: {
- x: -(W / 2) + 50,
- y: 0
- },
- damping: 0.01,
- stiffness: 0.002,
- length: 1800
- });
- cons[cons.length] = Constraint.create({
- pointA: {
- x: b.position.x + (W / 2) - 50 + 211,
- y: b.position.y - 1825
- },
- bodyB: b,
- pointB: {
- x: (W / 2) - 50,
- y: 0
- },
- damping: 0.01,
- stiffness: 0.002,
- length: 1800
- });
- Composite.add(engine.world, [cons[cons.length - 1], cons[cons.length - 2]])
- }
-
- spinnerArray.push(level.spinner(50, -3325, 45, 600, 0.003, 0, 0, 0.01)) // spinner(x, y, width, height, density = 0.001, angle = 0, frictionAir = 0.001, angularVelocity = 0) {
- if (Math.random() < 0.5) {
- const r = 200
- const hexagon = `${r} 0 ${r * Math.cos(5.236)} ${r * Math.sin(5.236)} ${r * Math.cos(4.189)} ${r * Math.sin(4.189)} ${-r} 0 ${r * Math.cos(2.0944)} ${r * Math.sin(2.0944)} ${r * Math.cos(1.0472)} ${r * Math.sin(1.0472)} `
- Matter.Body.setVertices(spinnerArray[spinnerArray.length - 1].bodyB, Vertices.fromPath(hexagon))
- }
-
- const boost1 = level.boost(800, -2000, 790)
-
- level.custom = () => {
- ctx.fillStyle = "#c0c3c9"
- ctx.fillRect(-1470, -1975, 2, 1915) //elevator track
- ctx.fillRect(1276, -4460, 2, 1425) //elevator track
- ctx.fillRect(-1250, -3825, 25, 1850); //small pillar background
- ctx.fillStyle = "#d0d4d6"
- ctx.fillRect(-1100, -1925, 825, 2925) //large pillar background
- ctx.fillRect(450, -1925, 825, 2925) //large pillar background
- ctx.fillStyle = "#cff" //exit
- ctx.fillRect(1475, -4900, 525, 425)
- level.exit.drawAndCheck();
-
- level.enter.draw();
- };
-
- level.customTopLayer = () => {
- boost1.query();
- elevator1.move();
- elevator2.move();
-
- ctx.fillStyle = "#233"
- ctx.beginPath(); //central dot on spinners
- ctx.arc(spinnerArray[0].pointA.x, spinnerArray[0].pointA.y, 9, 0, 2 * Math.PI);
- for (let i = 0, len = spinnerArray.length; i < len; i++) {
- ctx.moveTo(spinnerArray[i].pointA.x, spinnerArray[i].pointA.y)
- ctx.arc(spinnerArray[i].pointA.x, spinnerArray[i].pointA.y, 9, 0, 2 * Math.PI);
- }
- ctx.fill();
- //shadow
- ctx.fillStyle = "rgba(0,10,30,0.1)"
- ctx.fillRect(550, -3000, 600, 1025);
- ctx.fillRect(-750, -3100, 300, 275);
- ctx.fillRect(-650, -3625, 200, 225);
- ctx.fillRect(-825, -2775, 425, 450);
- ctx.fillRect(-925, -2300, 675, 300);
-
- slime.query();
- if (isWaterfallFilling) {
- if (slime.height < 5500) {
- //draw slime fill
- ctx.fillStyle = `hsla(160, 100%, 43%,${0.3 + 0.07 * Math.random()})`
- ctx.fillRect(waterFallX, -5050, waterFallWidth, 6175 - slime.height)
- if (!m.isBodiesAsleep) {
- waterFallWidth = 0.98 * waterFallWidth + 4.7 * Math.random()
- waterFallSmoothX = 0.98 * waterFallSmoothX + 3.5 * Math.random()
- waterFallX = waterFallSmoothX - 1985
- ctx.fillRect(waterFallX + waterFallWidth * Math.random(), -5050, 4, 6175 - slime.height)
- //push player down if they go under waterfall
- if (player.position.x > waterFallX && player.position.x < waterFallX + waterFallWidth && player.position.y < slime.height) {
- Matter.Body.setVelocity(player, {
- x: player.velocity.x,
- y: player.velocity.y + 2
- });
- }
- }
- slime.levelRise(riseRate)
- }
- } else if (Vector.magnitudeSquared(Vector.sub(player.position, level.enter)) > 100000) {
- isWaterfallFilling = true
- }
- };
- }
- },
pavilion() {
const vanish = []
level.exit.x = -850;
@@ -24201,6 +23973,340 @@ const level = {
const obj = { restoreBoss };
Object.assign(spawn, obj); //for next map, gonna be a rpg-like thingy I think
},
+ map() {
+
+ const elevator = level.elevator(-80.4, -931.6, 180, 50, -1550)
+ 15900 && player.position.x < 16300 && player.position.y > -960.2
+ const slime = level.hazard(15900, -960, 400, 6000);
+ const slime2 = level.hazard(15147.2, -1782.4, 2000, 822);
+ const boost1 = level.boost(5950, -20, 700)
+ const boost2 = level.boost(21088, -1672, 700)
+ const boost3 = level.boost(19390, -31, 1700)
+ const boost4 = level.boost(19390, -31, 1700)
+ const boost5 = level.boost(17274, -1242, 1000)
+ const portal = level.portal({ x: 443, y: -1636 }, Math.PI, { x: 21391.9, y: -1806.3 }, -Math.PI)
+ const portal2 = level.portal({ x: 16838.3, y: -626.7 }, Math.PI, { x: 16882.8, y: -2566.5 }, -Math.PI)
+ const buttonDoor = level.button(21889, -10)
+ const door = level.door(19119, -2133, 110, 510, 480)
+ const buttonDoor2 = level.button(18711, -2210)
+ const door2 = level.door(17041, -412, 110, 510, 480)
+ const buttonDoor3 = level.button(20456.6, -1636.2)
+ const door3 = level.door(20238, -781.4, 88, 452, 412)
+ //y=-1485
+
+ simulation.enableConstructMode()
+ level.setPosToSpawn(0, -50); //normal spawn
+ level.exit.x = 15316;
+ level.exit.y = -84;
+ spawn.mapRect(level.enter.x, level.enter.y + 20, 100, 20); //bump for level entrance
+ spawn.mapRect(level.exit.x, level.exit.y + 20, 100, 20); //bump for level exit
+ level.defaultZoom = 1800
+ simulation.zoomTransition(level.defaultZoom)
+ document.body.style.backgroundColor = "#001738";
+ color.map = "#444" //custom map color
+
+
+
+ level.custom = () => {
+ //spawn.mapRect(22330, -2688.75, 400, 800);
+ //spawn.mapRect(22330, -1793.5, 400, 800);//-46.25*2=-92.5
+ //spawn.mapRect(22330, -804.25, 400, 800);//-46.25*3
+
+ ctx.fillStyle = "rgba(63,247,251,0.8)"
+ ctx.fillRect(22330, -2713.75, 550, 700) //15845.0, -1262.2
+ ctx.fillRect(22330, -1743.5, 550, 700)
+ ctx.fillRect(22330, -754.25, 550, 700)
+ ctx.fillRect(15845.0, -1262.2, 550, 300)
+ ctx.fillStyle = "rgba(235,235,235,0.9)"
+ ctx.fillRect(-192, -1973, 6484, 2071)
+ ctx.fillRect(15109.5, -2867.5, 7284, 2971)
+ ctx.fillStyle = "rgba(35,35,35,0.8)"
+ ctx.fillRect(15145.9, -960, 200, 25)
+
+ ctx.fillStyle = "rgba(255,255,255,0.9)"
+ ctx.fillRect(-677.3, -610.9, 15, 15)
+ ctx.fillRect(-910.4, 458.3, 15, 15)
+ ctx.fillRect(-1029.0, 713.7, 15, 15)
+ ctx.fillRect(42.6, 1332.2, 15, 15)
+ ctx.fillRect(277.3, 751.8, 15, 15)
+ ctx.fillRect(797.1, 553.2, 15, 15)
+ ctx.fillRect(-1458.9, 340.9, 15, 15)
+ ctx.fillRect(-1780.0, -54.6, 15, 15)
+ ctx.fillRect(-1260.3, -686.4, 15, 15)
+ ctx.fillRect(-2064.3, -394.6, 15, 15)
+ ctx.fillRect(-1815.7, 1156.2, 15, 15)
+ ctx.fillRect(-1998.1, 1118.4, 15, 15)
+
+
+
+ buttonDoor.query();
+ buttonDoor.draw();
+ buttonDoor2.query();
+ buttonDoor2.draw();
+ buttonDoor3.query();
+ buttonDoor3.draw();
+ slime.query();
+ slime2.query();
+
+ ctx.fillStyle = `hsla(160, 100%, 43%,${0.3 + 0.07 * Math.random()})`
+ ctx.fillRect(15900 + 400 * Math.random(), -1360, 2, 6000)
+ if (buttonDoor.isUp) {
+ door.isClosing = true
+ } else {
+ door.isClosing = false
+ }
+ if (buttonDoor2.isUp) {
+ door2.isClosing = true
+ } else {
+ door2.isClosing = false
+ }
+ if (buttonDoor3.isUp) {
+ door3.isClosing = true
+ } else {
+ door3.isClosing = false
+ }
+ door.openClose();
+ door2.openClose();
+ door3.openClose();
+ portal[2].query()
+ portal[3].query()
+ portal2[2].query()
+ portal2[3].query()
+
+ boost1.query();
+ boost2.query();
+ boost3.query();
+ boost4.query();
+ boost5.query();
+ level.exit.drawAndCheck();
+ level.enter.draw();
+ };
+
+
+ level.customTopLayer = () => {
+ door.draw();
+ door2.draw();
+ door3.draw();
+
+ portal[0].draw();
+ portal[1].draw();
+ portal[2].draw();
+ portal[3].draw();
+ portal2[0].draw();
+ portal2[1].draw();
+ portal2[2].draw();
+ portal2[3].draw();
+ elevator.move()
+
+ if (player.position.x > 15900 && player.position.x < 16300 && player.position.y > -1360.2) {
+ Matter.Body.setVelocity(player, {
+ x: player.velocity.x,
+ y: player.velocity.y + 10
+ });
+ } else {
+
+ Matter.Body.setVelocity(player, {
+ x: player.velocity.x,
+ y: player.velocity.y - 0.2
+ });
+
+ }
+
+ };
+
+
+ //1273.2, -1404.7
+
+ //first ship base
+ spawn.mapRect(-300, 0, 6684, 100); //lower floor
+ spawn.mapRect(-300, -2071, 154, 2071); //far right wall
+ spawn.mapRect(2511, -300, 1309, 308); //left big block
+ spawn.mapRect(3820, -184, 1309, 184); //right big block
+ spawn.mapRect(-300, -739, 2549, 100); //upper right floor
+ spawn.mapRect(2056, -1309, 2764, 169); //upper center floor
+ spawn.mapRect(2056, -1309, 193, 650); //upper left floor wall
+ spawn.mapRect(4636, -1309, 193, 793); //upper right floor wall
+ spawn.mapRect(4821, -654, 955, 138); //upper right floor
+ spawn.mapRect(6237, -2071, 147, 2071); //far right wall
+ spawn.mapRect(-300, -2071, 6684, 154); //roof
+
+ //first ship details
+ spawn.mapRect(245, -360, 70, 400); //start room wall
+ spawn.mapRect(500, -1929, 154, 462);
+ spawn.mapRect(185, -1517, 469, 77);
+ spawn.mapRect(2773, -682, 469, 77); //walls in 1st room
+ spawn.mapRect(3743, -566, 77, 469);
+ spawn.mapRect(3947, -851, 469, 77);
+ spawn.mapRect(5313, -1309, 1000, 70); //walls in second area
+ spawn.mapRect(4818, -1006, 400, 70);
+ spawn.mapRect(4768, -1626, 800, 70);
+ spawn.mapRect(4760, -1626, 70, 400);
+
+
+ //first ship blocks/debris
+ spawn.debris(3267.6, -797.1, 700, 5); //16 debris per level
+ spawn.debris(1626.0, -372.5, 1700, 8); //16 debris per level
+ spawn.debris(1880.1, -1508.9, 3700, 16); //16 debris per level
+ spawn.debris(5335.3, -1431.6, 3700, 16); //16 debris per level
+ spawn.debris(1563.8, -1087.9, 700, 5); //16 debris per level
+ spawn.bodyRect(1540, -1110, 218, 125, 0.9);
+
+
+
+ //first ship mobs
+ spawn.randomMob(2903.9, -754.5, 0.7);
+ spawn.randomMob(5577.0, -217.0, 0.6);
+ spawn.randomMob(765.8, -1029.7, 0.5);
+ spawn.randomMob(20079.4, -2219.7, 0.6);
+ spawn.randomMob(20079.4, -2219.7, 0.7);
+ spawn.randomMob(20890.9, -1306.0, 0.5);
+ spawn.randomMob(21284.2, -983.1, 0.5);
+ spawn.randomMob(20381.0, -254.2, 0.7);
+ spawn.randomMob(21027.8, -473.8, 0.6);
+ spawn.randomMob(19448.2, -1323.3, 0.6);
+ spawn.randomMob(18397.7, -711.2, 0.6);
+ spawn.randomMob(15547.2, -2249.6, 0.6);
+ spawn.randomSmallMob(16114.6, -2524.2);
+ spawn.randomSmallMob(15378.9, -2549.6);
+
+ spawn.randomSmallMob(893.5, -120.8);
+ spawn.randomSmallMob(3521.8, -419.6);
+ spawn.randomSmallMob(4386.2, -439.6);
+ spawn.randomSmallMob(5667.0, -847.8);
+ spawn.randomSmallMob(3158.5, -1581.8);
+ spawn.randomSmallMob(3866.7, -1483.2);
+ spawn.randomSmallMob(4652.3, -1729.4);
+ spawn.randomSmallMob(1068.7, -106.1);
+ spawn.randomSmallMob(3545.0, -413.0);
+ spawn.randomSmallMob(4231.7, -446.3);
+ spawn.randomSmallMob(1456.4, -1014.8);
+ spawn.randomSmallMob(20432.4, -1374.3);
+ spawn.randomSmallMob(20381.0, -254.2);
+ spawn.randomSmallMob(20353.4, -1845.8);
+ spawn.randomSmallMob(20353.4, -1845.8);
+ spawn.randomSmallMob(20648.1, -136.8);
+ spawn.randomSmallMob(20024.4, -2213.1);
+ spawn.randomSmallMob(17438.7, -876.7);
+
+
+
+ //second ship mobs
+ spawn.debris(17732.3, -550.0, 700, 5); //16 debris per level
+ spawn.debris(18006.4, -2181.3, 700, 5); //16 debris per level
+ spawn.debris(16108.6, -2621.1, 700, 5); //16 debris per level
+ spawn.debris(20823.6, -1332.1, 1300, 5); //16 debris per level
+ spawn.debris(21095.5, -423.4, 700, 5); //16 debris per level
+
+
+
+
+
+
+
+ spawn.randomSmallMob(1300, -70);
+
+
+ // const index = mob.length
+ spawn.shieldingBoss(769.8, -1119.0)
+ // console.log(mob[index].onDeath)
+ // requestAnimationFrame(() => mob[index].onDeath = function() {});
+ // console.log(mob[index].onDeath)
+
+ //second ship base
+ spawn.mapRect(15000, 0, 515, 185); //lower floor 1
+ spawn.mapRect(17015, 0, 5500, 185); //lower floor 2
+ spawn.mapRect(15000, -2972, 185, 2972); //left wall
+ spawn.mapRect(15000, -2972, 7515, 185); //roof
+ spawn.mapRect(22330, -2972, 185, 2972); //right wall
+ spawn.mapRect(17002, -2972, 169, 2564); //left middle wall
+ spawn.mapRect(19089, -2972, 169, 855); //right middle wall upper
+ spawn.mapRect(19089, -1625, 169, 1800); //right middle wall lower
+ spawn.mapRect(20760, -2972, 169, 1350); //medium wall left of portal
+ spawn.mapRect(19720, -1625, 1725, 162); //right room upper floor
+ spawn.mapRect(21440, -2325, 169, 863); //medium wall right of portal
+ spawn.mapRect(19720, -855, 2725, 162); //right room lower floor
+
+ //engines //y -2972 -> 0
+ spawn.mapRect(22330, -2763.75, 400, 800);
+ spawn.mapRect(22330, -1793.5, 400, 800);
+ spawn.mapRect(22330, -804.25, 400, 800);
+
+
+
+ //second ship details
+ spawn.mapRect(19904, -1465, 85, 362); //upper L
+ spawn.mapRect(19542, -1191, 412, 88); //lower L
+ spawn.mapRect(18546, -2199, 600, 82); //2nd room enternce wall
+ spawn.mapRect(18546, -2499, 82, 2300);
+ spawn.mapRect(18108, -326, 500, 82); //walls/floors in middle room
+ spawn.mapRect(17750, -682, 300, 82);
+ spawn.mapRect(17156, -468, 500, 60);
+ spawn.mapRect(18022, -1082, 600, 82);
+ spawn.mapRect(17151, -1196, 500, 82);
+ spawn.mapRect(17453, -2060, 500, 82);
+ spawn.mapRect(18197, -2269, 400, 82);
+ spawn.mapRect(18108, -326, 500, 82);
+ spawn.mapRect(20542, -1191, 612, 88);
+ spawn.mapRect(20238, -1191, 88, 412);
+ spawn.mapRect(21520, -1468, 88, 412);
+ spawn.mapRect(20238, -330.2, 88, 412);
+ spawn.mapRect(20819, -328.3, 412, 88);
+ spawn.mapRect(21532, -708, 88, 412);
+ spawn.mapRect(15483.8, 12.5, 388, 30); //broken floor
+ spawn.mapRect(15487.6, 76.6, 488, 24);
+ spawn.mapRect(15506.5, 134.2, 288, 45);
+ spawn.mapVertex(16758.6, 135.3, "400 -30 -350 -40 -400 30 400 30");
+ spawn.mapVertex(16758.6, 55.3, "423 -30 -408 -20 -400 20 400 20");
+ //tank
+ spawn.mapRect(15310, -960, 600, 135);
+ spawn.mapRect(16290, -960, 800, 135);
+ //in tank
+ spawn.mapRect(16524.8, -2726.8, 40, 400);
+ spawn.mapRect(16524.8, -2130.9, 400, 40);
+ spawn.mapRect(16010.2, -2412.2, 300, 40);
+ spawn.mapRect(15379.2, -2055.1, 400, 40);
+
+
+
+ //add fuel tanks in the last room
+
+
+ spawn.mapRect(21531.9, -707.8, 488, 8);
+
+ //22185.5, -114.8
+ spawn.mapVertex(22207.8, -103, "325 -200 100 -200 325 -300");
+ spawn.mapRect(22056.6, -70, 225, 212);
+
+ spawn.mapVertex(20723.1, -1734, "325 -200 100 -200 325 -300");
+ spawn.mapRect(20571.9, -1701.0, 225, 212);
+
+ spawn.mapVertex(22207.8, -103, "325 -200 100 -200 325 -300");
+ spawn.mapRect(22056.6, -70, 225, 212);
+ //spawn.mapVertex(x,y, "coordinates")
+ //the parts in quotes is "x y x y x y x y x y" x and y need to be the coordinates of points that define the shape in a concave clockwise direction
+
+ //second ship blocks/debris
+ spawn.bodyRect(21525, -113, 50, 50, 9); //first button block
+ spawn.bodyRect(18993, -2283, 50, 50, 9); //second button block
+ spawn.bodyRect(20303, -1736, 50, 50, 9); //third button block
+
+
+
+ let randomBoss = Math.floor(Math.random() * 5); //change the bosses
+ spawn[["blinkBoss", "shooterBoss", "launcherBoss", "pulsarBoss", "beetleBoss", "bladeBoss", "revolutionBoss", "dragonFlyBoss", "spiderBoss"][randomBoss]](17902, -1689, 100, false);
+
+
+
+ // powerUps.spawnStartingPowerUps(1475, -1175);
+ // spawn.debris(750, -2200, 3700, 16); //16 debris per level
+ // spawn.bodyRect(1540, -1110, 300, 25, 0.9);
+ // spawn.randomSmallMob(1300, -70);
+ // spawn.randomMob(2650, -975, 0.8);
+ // spawn.randomGroup(1700, -900, 0.4);
+ // if (simulation.difficulty > 1) spawn.randomLevelBoss(2200, -1300);
+ // spawn.secondaryBossChance(100, -1500)
+ powerUps.addResearchToLevel() //needs to run after mobs are spawned
+ },
// ********************************************************************************************************
// ********************************************************************************************************
// ***************************************** training levels **********************************************
diff --git a/js/simulation.js b/js/simulation.js
index 1a47771..0f34c86 100644
--- a/js/simulation.js
+++ b/js/simulation.js
@@ -388,6 +388,213 @@ const simulation = {
}
requestAnimationFrame(loop)
},
+
+ sight: { //credit to Cornbread for adding this algorithm to n-gon
+ intersectMap: [], //this is precalculated in simulation.draw.setPaths() when the map changes
+ getIntersection(v1, v1End, domain) {
+ const intersections = simulation.sight.getIntersections(v1, v1End, domain);
+ var best = { x: v1End.x, y: v1End.y, dist: Math.sqrt((v1End.x - v1.x) ** 2 + (v1End.y - v1.y) ** 2) }
+ for (const intersection of intersections) {
+ const dist = Math.sqrt((intersection.x - v1.x) ** 2 + (intersection.y - v1.y) ** 2);
+ if (dist < best.dist) best = { x: intersection.x, y: intersection.y, dist: dist }
+ }
+ return best;
+ },
+ getIntersections(v1, v1End, domain) {
+ const intersections = [];
+ for (const obj of domain) {
+ for (var i = 0; i < obj.vertices.length - 1; i++) {
+ results = simulation.checkLineIntersection(v1, v1End, obj.vertices[i], obj.vertices[i + 1]);
+ if (results.onLine1 && results.onLine2) intersections.push({ x: results.x, y: results.y });
+ }
+ results = simulation.checkLineIntersection(v1, v1End, obj.vertices[obj.vertices.length - 1], obj.vertices[0]);
+ if (results.onLine1 && results.onLine2) intersections.push({ x: results.x, y: results.y });
+ }
+ return intersections;
+ },
+ circleLoS(pos, radius) {
+ function allCircleLineCollisions(c, radius, domain) {
+ var lines = [];
+ for (const obj of domain) {
+ for (var i = 0; i < obj.vertices.length - 1; i++) lines.push(circleLineCollisions(obj.vertices[i], obj.vertices[i + 1], c, radius));
+ lines.push(circleLineCollisions(obj.vertices[obj.vertices.length - 1], obj.vertices[0], c, radius));
+ }
+ const collisionLines = [];
+ for (const line of lines) {
+ if (line.length == 2) {
+ // const distance1 = Math.sqrt((line[0].x - c.x) ** 2 + (line[0].y - c.y) ** 2)
+ // const angle1 = Math.atan2(line[0].y - c.y, line[0].x - c.x);
+ // const queryPoint1 = {
+ // x: Math.cos(angle1) * (distance1 - 1) + c.x,
+ // y: Math.sin(angle1) * (distance1 - 1) + c.y
+ // }
+ // const distance2 = Math.sqrt((line[1].x - c.x) ** 2 + (line[1].y - c.y) ** 2)
+ // const angle2 = Math.atan2(line[1].y - c.y, line[1].x - c.x);
+ // const queryPoint2 = {
+ // x: Math.cos(angle2) * (distance2 - 1) + c.x,
+ // y: Math.sin(angle2) * (distance2 - 1) + c.y
+ // }
+ collisionLines.push(line)
+ }
+ }
+
+ return collisionLines;
+ }
+
+ function circleLineCollisions(a, b, c, radius) {
+ // calculate distances
+ const angleOffset = Math.atan2(b.y - a.y, b.x - a.x);
+ const sideB = Math.sqrt((a.x - c.x) ** 2 + (a.y - c.y) ** 2);
+ const sideC = Math.sqrt((b.x - a.x) ** 2 + (b.y - a.y) ** 2);
+ const sideA = Math.sqrt((c.x - b.x) ** 2 + (c.y - b.y) ** 2);
+
+ // calculate the closest point on line AB to point C
+ const angleA = Math.acos((sideB ** 2 + sideC ** 2 - sideA ** 2) / (2 * sideB * sideC)) * (a.x - c.x) / -Math.abs(a.x - c.x)
+ const sideAD = Math.cos(angleA) * sideB;
+ const d = { // closest point
+ x: Math.cos(angleOffset) * sideAD + a.x,
+ y: Math.sin(angleOffset) * sideAD + a.y
+ }
+ const distance = Math.sqrt((d.x - c.x) ** 2 + (d.y - c.y) ** 2);
+ if (distance == radius) {
+ // tangent
+ return [d];
+ } else if (distance < radius) {
+ // secant
+ const angleOffset = Math.atan2(d.y - c.y, d.x - c.x);
+ const innerAngle = Math.acos(distance / radius);
+ const intersection1 = {
+ x: Math.cos(angleOffset + innerAngle) * radius + c.x,
+ y: Math.sin(angleOffset + innerAngle) * radius + c.y
+ }
+
+ const intersection2 = {
+ x: Math.cos(angleOffset - innerAngle) * radius + c.x,
+ y: Math.sin(angleOffset - innerAngle) * radius + c.y
+ }
+
+ const distance1 = {
+ a: Math.sqrt((intersection1.x - a.x) ** 2 + (intersection1.y - a.y) ** 2),
+ b: Math.sqrt((intersection1.x - b.x) ** 2 + (intersection1.y - b.y) ** 2)
+ }
+ const distance2 = {
+ a: Math.sqrt((intersection2.x - a.x) ** 2 + (intersection2.y - a.y) ** 2),
+ b: Math.sqrt((intersection2.x - b.x) ** 2 + (intersection2.y - b.y) ** 2)
+ }
+ const result = [];
+ if (Math.abs(sideC - (distance1.a + distance1.b)) < 0.01) {
+ result.push(intersection1);
+ } else {
+ if (distance1.a < distance1.b) {
+ if (sideB <= radius) result.push(a);
+ } else {
+ if (sideA <= radius) result.push(b)
+ }
+ }
+ if (Math.abs(sideC - (distance2.a + distance2.b)) < 0.01) {
+ result.push(intersection2);
+ } else {
+ if (distance2.a <= distance2.b) {
+ if (sideB <= radius) result.push(a);
+ } else {
+ if (sideA <= radius) result.push(b)
+ }
+ }
+
+ return result;
+ } else {
+ // no intersection
+ return [];
+ }
+ }
+
+ var vertices = [];
+ for (const obj of simulation.sight.intersectMap) {
+ for (var i = 0; i < obj.vertices.length; i++) {
+ const vertex = obj.vertices[i];
+ const angleToVertex = Math.atan2(vertex.y - pos.y, vertex.x - pos.x);
+ const distanceToVertex = Math.sqrt((vertex.x - pos.x) ** 2 + (vertex.y - pos.y) ** 2);
+ const queryPoint = { x: Math.cos(angleToVertex) * (distanceToVertex - 1) + pos.x, y: Math.sin(angleToVertex) * (distanceToVertex - 1) + pos.y }
+
+ if (Matter.Query.ray(map, pos, queryPoint).length == 0) {
+ var distance = Math.sqrt((vertex.x - pos.x) ** 2 + (vertex.y - pos.y) ** 2);
+ var endPoint = { x: vertex.x, y: vertex.y }
+
+ if (distance > radius) {
+ const angle = Math.atan2(vertex.y - pos.y, vertex.x - pos.x);
+ endPoint = { x: Math.cos(angle) * radius + pos.x, y: Math.sin(angle) * radius + pos.y }
+ distance = radius
+ }
+
+ var best = simulation.sight.getIntersection(pos, endPoint, map);
+ if (best.dist >= distance) best = { x: endPoint.x, y: endPoint.y, dist: distance }
+ vertices.push(best)
+
+ var angle = Math.atan2(vertex.y - pos.y, vertex.x - pos.x);
+ endPoint = { x: Math.cos(angle + 0.001) * radius + pos.x, y: Math.sin(angle + 0.001) * radius + pos.y }
+ best = simulation.sight.getIntersection(pos, endPoint, map);
+
+ if (best.dist >= radius) best = { x: endPoint.x, y: endPoint.y, dist: radius }
+ vertices.push(best)
+
+ angle = Math.atan2(vertex.y - pos.y, vertex.x - pos.x);
+ endPoint = { x: Math.cos(angle - 0.001) * radius + pos.x, y: Math.sin(angle - 0.001) * radius + pos.y }
+
+ best = simulation.sight.getIntersection(pos, endPoint, map);
+ if (best.dist >= radius) best = { x: endPoint.x, y: endPoint.y, dist: radius }
+ vertices.push(best)
+ }
+ }
+ }
+
+ const outerCollisions = allCircleLineCollisions(pos, radius, map);
+ const circleCollisions = [];
+ for (const line of outerCollisions) {
+ for (const vertex of line) {
+ const distance = Math.sqrt((vertex.x - pos.x) ** 2 + (vertex.y - pos.y) ** 2)
+ const angle = Math.atan2(vertex.y - pos.y, vertex.x - pos.x);
+ const queryPoint = {
+ x: Math.cos(angle) * (distance - 1) + pos.x,
+ y: Math.sin(angle) * (distance - 1) + pos.y
+ }
+ if (Math.abs(distance - radius) < 1 && Matter.Query.ray(map, pos, queryPoint).length == 0) circleCollisions.push(vertex)
+ }
+ }
+ for (var i = 0; i < circleCollisions.length; i++) {
+ const vertex = circleCollisions[i];
+ var nextIndex = i + 1;
+ if (nextIndex == circleCollisions.length) nextIndex = 0;
+ const nextVertex = circleCollisions[nextIndex];
+ const angle1 = Math.atan2(vertex.y - pos.y, vertex.x - pos.x);
+ const angle2 = Math.atan2(nextVertex.y - pos.y, nextVertex.x - pos.x);
+ var newAngle;
+ if (Math.abs(angle1) > Math.PI / 2 && Math.abs(angle2) > Math.PI / 2 && angle1 / Math.abs(angle1) != angle2 / Math.abs(angle2)) {
+ // if the arc between the to points crosses over the left side (+/- pi radians)
+ const newAngle1 = (Math.PI - Math.abs(angle1)) * (angle1 / Math.abs(angle1));
+ const newAngle2 = (Math.PI - Math.abs(angle2)) * (angle2 / Math.abs(angle2));
+ newAngle = (newAngle1 + newAngle2) / 2;
+ var multiplier;
+ if (newAngle == 0) {
+ multiplier = 1;
+ } else {
+ multiplier = newAngle / Math.abs(newAngle);
+ }
+ newAngle = Math.PI * multiplier - newAngle * multiplier;
+ test = true;
+ } else {
+ newAngle = (angle1 + angle2) / 2;
+ }
+
+ // shoot ray between them
+ var endPoint = { x: Math.cos(newAngle) * radius + pos.x, y: Math.sin(newAngle) * radius + pos.y }
+ var best = simulation.sight.getIntersection(pos, endPoint, map);
+ vertices.push(vertex);
+ if (best.dist <= radius) vertices.push({ x: best.x, y: best.y })
+ }
+ vertices.sort((a, b) => Math.atan2(a.y - pos.y, a.x - pos.x) - Math.atan2(b.y - pos.y, b.x - pos.x));
+ return vertices;
+ },
+ },
boldActiveGunHUD() {
if (b.inventory.length > 0) {
for (let i = 0, len = b.inventory.length; i < len; ++i) {
@@ -398,23 +605,9 @@ const simulation = {
}
}
}
-
- // if (b.inventory.length > 0) {
- // for (let i = 0, len = b.inventory.length; i < len; ++i) document.getElementById(b.inventory[i]).style.opacity = "0.3";
- // // document.getElementById(b.activeGun).style.fontSize = "30px";
- // if (document.getElementById(b.activeGun)) document.getElementById(b.activeGun).style.opacity = "1";
- // }
},
updateGunHUD() {
- // for (let i = 0, len = b.inventory.length; i < len; ++i) {
- // if (flashIndex === i) {
- // document.getElementById(b.inventory[i]).innerHTML = b.guns[b.inventory[i]].name + " - " + b.guns[b.inventory[i]].ammo;
- // } else {
- // document.getElementById(b.inventory[i]).innerHTML = b.guns[b.inventory[i]].name + " - " + b.guns[b.inventory[i]].ammo;
- // }
- // }
for (let i = 0, len = b.inventory.length; i < len; ++i) {
- // document.getElementById(b.inventory[i]).innerHTML = b.guns[b.inventory[i]].name + " - " + b.guns[b.inventory[i]].ammo;
document.getElementById(b.inventory[i]).innerHTML = `${b.guns[b.inventory[i]].name} - ${b.guns[b.inventory[i]].ammo}`
}
},
@@ -1395,6 +1588,38 @@ const simulation = {
}
simulation.draw.mapPath.lineTo(vertices[0].x, vertices[0].y);
}
+
+
+
+
+
+ //store data for line of sight precalculation
+ simulation.sight.intersectMap = [];
+ for (var i = 0; i < map.length; i++) {
+ const obj = map[i];
+ const newVertices = [];
+ const restOfMap = [...map].slice(0, i).concat([...map].slice(i + 1))
+ for (var j = 0; j < obj.vertices.length - 1; j++) {
+ var intersections = simulation.sight.getIntersections(obj.vertices[j], obj.vertices[j + 1], restOfMap);
+ newVertices.push(obj.vertices[j]);
+ for (const vertex of intersections) newVertices.push({ x: vertex.x, y: vertex.y });
+ }
+ intersections = simulation.sight.getIntersections(obj.vertices[obj.vertices.length - 1], obj.vertices[0], restOfMap);
+ newVertices.push(obj.vertices[obj.vertices.length - 1]);
+ for (const vertex of intersections) newVertices.push({ x: vertex.x, y: vertex.y });
+ //draw the vertices as black circles for debugging
+ // for (const vertex of newVertices) {
+ // ctx.beginPath();
+ // ctx.moveTo(vertex.x, vertex.y);
+ // ctx.arc(vertex.x, vertex.y, 10, 0, 2 * Math.PI);
+ // ctx.fillStyle = '#000';
+ // ctx.fill()
+ // }
+ simulation.sight.intersectMap.push({ vertices: newVertices });
+ }
+
+
+
},
drawMapPath() {
ctx.fillStyle = color.map;
diff --git a/js/tech.js b/js/tech.js
index b3d4f7b..efa9d9f 100644
--- a/js/tech.js
+++ b/js/tech.js
@@ -523,7 +523,7 @@ const tech = {
{
name: "causality bombs",
link: `causality bombs`,
- description: "when you rewind drop several grenades
become invulnerable until they explode",
+ description: "when you rewind drop several grenades", //
become invulnerable until they explode
maxCount: 1,
count: 0,
frequency: 2,
@@ -7257,9 +7257,9 @@ const tech = {
frequency: 2,
frequencyDefault: 2,
allowed() {
- return (m.fieldMode === 4 && tech.deflectEnergy === 0) || (m.fieldMode === 1 && tech.harmonics === 2)
+ return m.fieldMode === 1 && tech.harmonics === 2
},
- requires: "molecular assembler, standing wave, not electric generator",
+ requires: "standing wave",
effect() {
tech.isLaserField = true
},
@@ -7836,9 +7836,9 @@ const tech = {
frequency: 2,
frequencyDefault: 2,
allowed() {
- return m.fieldMode === 4 && !tech.isLaserField
+ return m.fieldMode === 4
},
- requires: "molecular assembler, not surface plasmon",
+ requires: "molecular assembler",
effect() {
tech.deflectEnergy += 0.5;
},
@@ -8360,7 +8360,7 @@ const tech = {
},
{
name: "vacuum fluctuation",
- description: `use ${powerUps.orb.research(3)}to exploit your field for a
+11% chance to duplicate spawned power ups`,
+ description: `use ${powerUps.orb.research(3)}
+11% chance to duplicate spawned power ups`,
isFieldTech: true,
maxCount: 1,
count: 0,
@@ -10081,6 +10081,66 @@ const tech = {
},
remove() { }
},
+ {
+ name: "flatland",
+ description: "map blocks line of sight",
+ maxCount: 1,
+ count: 0,
+ frequency: 0,
+ isNonRefundable: true,
+ isJunk: true,
+ allowed() { return true },
+ requires: "",
+ effect() {
+ simulation.ephemera.push({
+ name: "LoS", count: 0, do() {
+ const pos = m.pos
+ const radius = 5000
+ if (!simulation.isTimeSkipping) {
+ const vertices = simulation.sight.circleLoS(pos, radius);
+ if (vertices.length) {
+ ctx.beginPath();
+ ctx.moveTo(vertices[0].x, vertices[0].y);
+ for (var i = 1; i < vertices.length; i++) {
+ var currentDistance = Math.sqrt((vertices[i - 1].x - pos.x) ** 2 + (vertices[i - 1].y - pos.y) ** 2);
+ var newDistance = Math.sqrt((vertices[i].x - pos.x) ** 2 + (vertices[i].y - pos.y) ** 2);
+ if (Math.abs(currentDistance - radius) < 1 && Math.abs(newDistance - radius) < 1) {
+ const currentAngle = Math.atan2(vertices[i - 1].y - pos.y, vertices[i - 1].x - pos.x);
+ const newAngle = Math.atan2(vertices[i].y - pos.y, vertices[i].x - pos.x);
+ ctx.arc(pos.x, pos.y, radius, currentAngle, newAngle);
+ } else {
+ ctx.lineTo(vertices[i].x, vertices[i].y)
+ }
+ }
+ newDistance = Math.sqrt((vertices[0].x - pos.x) ** 2 + (vertices[0].y - pos.y) ** 2);
+ currentDistance = Math.sqrt((vertices[vertices.length - 1].x - pos.x) ** 2 + (vertices[vertices.length - 1].y - pos.y) ** 2);
+ if (Math.abs(currentDistance - radius) < 1 && Math.abs(newDistance - radius) < 1) {
+ const currentAngle = Math.atan2(vertices[vertices.length - 1].y - pos.y, vertices[vertices.length - 1].x - pos.x);
+ const newAngle = Math.atan2(vertices[0].y - pos.y, vertices[0].x - pos.x);
+ ctx.arc(pos.x, pos.y, radius, currentAngle, newAngle);
+ } else {
+ ctx.lineTo(vertices[0].x, vertices[0].y)
+ }
+ //stroke the map, so it looks different form the line of sight
+ ctx.strokeStyle = "#234";
+ ctx.lineWidth = 9;
+ ctx.stroke(simulation.draw.mapPath);
+
+ ctx.globalCompositeOperation = "destination-in";
+ ctx.fillStyle = "#000";
+ ctx.fill();
+ ctx.globalCompositeOperation = "source-over";
+ // also see the map
+ // ctx.fill(simulation.draw.mapPath);
+ // ctx.fillStyle = "#000";
+ ctx.clip();
+ }
+ }
+ },
+ })
+ },
+ remove() { }
+ },
{
name: "umbra",
description: "produce a blue glow around everything
and probably some simulation lag",
diff --git a/todo.txt b/todo.txt
index ca75494..fb2c7e5 100644
--- a/todo.txt
+++ b/todo.txt
@@ -1,21 +1,48 @@
******************************************************** NEXT PATCH **************************************************
-factory: rewrote the end
+JUNK tech: flatland - draw line of sight
+ credit to Cornbread for line of sight algorithm
+ a preview of future line of site content
+ try it out in console: tech.giveTech("flatland")
-clock gating was removed because it's annoying
-liquid cooling -> refrigerant - freezes mobs after losing at least 5% health
-mass-energy gets more effect from defense (0.13 -> 0.19)
-ternary 84 -> 77% damage
-dark patterns 15 -> 17% damage and JUNK
-Maxwell's demon 3% -> 1% energy loss above max
-exciton 16 -> 14% chance to drop
-
-10% increase in overall mob health
new images
bug fixes
*********************************************************** TODO *****************************************************
+LoS
+ LoS clipping
+ performance
+ calculate things that don't change when the map ctx is done
+ like the intersections?
+ rewrite code for infinite range
+ how to use this?
+ JUNK tech?
+ custom level?
+ give it a dark back ground for contrast?
+ game setting?
+ boss that you need to avoid, probably requires a custom level
+ boss is a source of light
+ level that is dark, and you can only see LoS
+LoS with limited radius
+ explosion graphic
+ mobs area of effect damage
+ maybe make a shared mob AoE damage function
+ standing wave graphic
+
+Also another thing I made that could fit in-game: https://kgurchiek.github.io/universal-n-gon-loader/
+by default it just plays a random version of n-gon downloaded from past github commits
+maybe the "snapshots" could work like this rather than downloading 8 versions of the game?
+also you can play any version with https://kgurchiek.github.io/universal-n-gon-loader/?commitIndex=NUM
+where setting "NUM" to 0 is the very first commit
+here's the code if you want to check it out: https://github.com/kgurchiek/universal-n-gon-loader/blob/main/script.js
+
+level - funicular
+ The system is characterized by two counterbalanced carriages (also called cars or trains) permanently attached to opposite ends of a haulage cable, which is looped over a pulley at the upper end of the track.[2][3] The result of such a configuration is that the two carriages move synchronously: as one ascends, the other descends at an equal speed.
+
+missile bot and plasma bot don't get converted by bot upgrade tech?
+ is this more confusing because it contradicts text?
+
use ephemera to replace things
JUNK?
request animation stuff