diff --git a/img/perimeter defense.webp b/img/perimeter defense.webp
index a1b4c26..071ecc5 100644
Binary files a/img/perimeter defense.webp and b/img/perimeter defense.webp differ
diff --git a/js/bullet.js b/js/bullet.js
index 0f32794..cdf687d 100644
--- a/js/bullet.js
+++ b/js/bullet.js
@@ -5500,21 +5500,10 @@ const b = {
const push = 0.4
const reflectivity = 1 - 1 / (tech.laserReflections * 3)
let damage = m.dmgScale * this.laserDamage * tech.laserDamage
- let best = {
- x: 1,
- y: 1,
- dist2: Infinity,
- who: null,
- v1: 1,
- v2: 1
- };
- const path = [{
- x: this.vertices[0].x,
- y: this.vertices[0].y
- }, {
- x: this.lockedOn.position.x,
- y: this.lockedOn.position.y
- }];
+ //make the laser wiggle as it aims at the target
+ let best = { x: 1, y: 1, dist2: Infinity, who: null, v1: 1, v2: 1 };
+ const perp2 = Vector.mult(Vector.rotate({ x: 1, y: 0 }, m.angle + Math.PI / 2), 0.6 * this.lockedOn.radius * Math.sin(simulation.cycle / this.lookFrequency))
+ const path = [{ x: this.vertices[0].x, y: this.vertices[0].y }, Vector.add(this.lockedOn.position, perp2)];
const vertexCollision = function (v1, v1End, domain) {
for (let i = 0; i < domain.length; ++i) {
diff --git a/js/index.js b/js/index.js
index 24aad57..f8467de 100644
--- a/js/index.js
+++ b/js/index.js
@@ -786,7 +786,7 @@ ${simulation.isCheating ? "
lore disabled" : ""}
-
+
diff --git a/js/level.js b/js/level.js
index 4d7bd77..e08f8ae 100644
--- a/js/level.js
+++ b/js/level.js
@@ -34,13 +34,13 @@ const level = {
// b.giveGuns("nail gun") //0 nail gun 1 shotgun 2 super balls 3 wave 4 missiles 5 grenades 6 spores 7 drones 8 foam 9 harpoon 10 mine 11 laser
// b.giveGuns("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("bremsstrahlung")
+ // tech.giveTech("iridescence")
// tech.giveTech("cherenkov radiation")
// tech.giveTech("irradiated nails")
// for (let i = 0; i < 6; ++i) tech.giveTech("Lorentz transformation")
// for (let i = 0; i < 1; ++i) tech.giveTech("rivet gun")
- // requestAnimationFrame(() => { for (let i = 0; i < 1; i++) tech.giveTech("foam-bot") });
- // for (let i = 0; i < 1; i++) tech.giveTech("foam-bot upgrade")
+ // requestAnimationFrame(() => { for (let i = 0; i < 30; i++) tech.giveTech("laser-bot") });
+ // for (let i = 0; i < 1; i++) tech.giveTech("laser-bot upgrade")
// for (let i = 0; i < 1; ++i) tech.giveTech("ternary")
// for (let i = 0; i < 3; ++i) tech.giveTech("mechatronics")
// for (let i = 0; i < 3; i++) powerUps.directSpawn(450, -50, "tech");
@@ -485,7 +485,6 @@ const level = {
category: cat.body,
mask: cat.player | cat.body | cat.bullet | cat.powerUp | cat.mob | cat.mobBullet
},
- isNoSetCollision: true,
isNotHoldable: true,
frictionAir: frictionAir,
friction: 1,
@@ -518,7 +517,6 @@ const level = {
category: cat.body,
mask: cat.player | cat.body | cat.bullet | cat.powerUp | cat.mob | cat.mobBullet
},
- isNoSetCollision: true,
isNotHoldable: true,
frictionAir: frictionAir,
friction: 1,
@@ -652,7 +650,6 @@ const level = {
category: cat.body, //cat.map,
mask: cat.map | cat.player | cat.body | cat.bullet | cat.powerUp | cat.mob | cat.mobBullet
},
- isNoSetCollision: true,
inertia: Infinity, //prevents rotation
isNotHoldable: true,
friction: 1,
@@ -821,7 +818,6 @@ const level = {
// category: cat.map,
// mask: cat.player | cat.body | cat.bullet | cat.powerUp | cat.mob | cat.mobBullet
// },
- // isNoSetCollision: true,
// });
// const rotor2 = Matter.Bodies.rectangle(x, y, width, radius, {
// angle: Math.PI / 2,
@@ -832,7 +828,6 @@ const level = {
// category: cat.map,
// mask: cat.player | cat.body | cat.bullet | cat.powerUp | cat.mob | cat.mobBullet
// },
- // isNoSetCollision: true,
// });
// rotor = Body.create({ //combine rotor1 and rotor2
// parts: [rotor1, rotor2],
@@ -841,7 +836,6 @@ const level = {
// category: cat.map,
// mask: cat.player | cat.body | cat.bullet | cat.powerUp | cat.mob | cat.mobBullet
// },
- // isNoSetCollision: true,
// });
// Matter.Body.setPosition(rotor, {
// x: x,
@@ -892,7 +886,6 @@ const level = {
const height = 15
body[body.length] = Bodies.rectangle(x + width / 2, y + height / 2, width, height, { friction: 0.05, frictionAir: 0.01 });
let flip = body[body.length - 1];
- flip.isNoSetCollision = true //prevents collision from being rewritten in level.addToWorld
flip.collisionFilter.category = cat.body
flip.collisionFilter.mask = cat.player | cat.body
flip.isNotHoldable = true
@@ -948,12 +941,13 @@ const level = {
},
}
},
- button(x, y, width = 126) {
- spawn.mapVertex(x + 65, y + 2, "100 10 -100 10 -70 -10 70 -10");
- map[map.length - 1].restitution = 0;
- map[map.length - 1].friction = 1;
- map[map.length - 1].frictionStatic = 1;
-
+ button(x, y, width = 126, isSpawnBase = true) {
+ if (isSpawnBase) {
+ spawn.mapVertex(x + 65, y + 2, "100 10 -100 10 -70 -10 70 -10");
+ map[map.length - 1].restitution = 0;
+ map[map.length - 1].friction = 1;
+ map[map.length - 1].frictionStatic = 1;
+ }
// const buttonSensor = Bodies.rectangle(x + 35, y - 1, 70, 20, {
// isSensor: true
// });
@@ -992,6 +986,25 @@ const level = {
this.isUp = false;
}
},
+ query() {
+ if (Matter.Query.region(body, this).length === 0 && Matter.Query.region([player], this).length === 0) {
+ this.isUp = true;
+ } else {
+ if (this.isUp === true) {
+ const list = Matter.Query.region(body, this) //are any blocks colliding with this
+ if (list.length > 0) {
+ if (list[0].bounds.max.x - list[0].bounds.min.x < 150 && list[0].bounds.max.y - list[0].bounds.min.y < 150) { //not too big of a block
+ Matter.Body.setPosition(list[0], { //teleport block to the center of the button
+ x: this.min.x + width / 2,
+ y: list[0].position.y
+ })
+ }
+ Matter.Body.setVelocity(list[0], { x: 0, y: 0 });
+ }
+ }
+ this.isUp = false;
+ }
+ },
draw() {
ctx.fillStyle = "hsl(0, 100%, 70%)"
if (this.isUp) {
@@ -1035,7 +1048,6 @@ const level = {
category: cat.map,
mask: cat.player | cat.body | cat.bullet | cat.powerUp | cat.mob | cat.mobBullet
},
- isNoSetCollision: true,
inertia: Infinity, //prevents rotation
isNotHoldable: true,
isNonStick: true, //this keep sporangium from sticking
@@ -1187,7 +1199,6 @@ const level = {
category: cat.body,//cat.map,
mask: cat.player | cat.body | cat.bullet | cat.powerUp | cat.mob | cat.mobBullet //cat.player | cat.map | cat.body | cat.bullet | cat.powerUp | cat.mob | cat.mobBullet
},
- isNoSetCollision: true,
inertia: Infinity, //prevents rotation
isNotHoldable: true,
friction: 1,
@@ -1210,25 +1221,6 @@ const level = {
Matter.Body.setPosition(this, position)
}
}
- // else {
- // const blocks = Matter.Query.collides(this, body)
- // console.log(blocks.length)
- // for (let i = 0; i < blocks.length; i++) {
- // // console.log(blocks[i])
- // if (blocks[i].bodyA) {
- // if (touching[i].bodyB !== m.holdingTarget) {
- // for (let j = 0, len = body.length; j < len; j++) {
- // if (body[j] === touching[i].bodyB) {
- // body.splice(j, 1);
- // len--
- // Matter.Composite.remove(engine.world, touching[i].bodyB);
- // break;
- // }
- // }
- // }
- // }
- // }
- // }
} else {
if (this.position.y > y - distance) { //try to open
const position = {
@@ -1248,9 +1240,7 @@ const level = {
ctx.beginPath();
const v = this.vertices;
ctx.moveTo(v[0].x, v[0].y);
- for (let i = 1; i < v.length; ++i) {
- ctx.lineTo(v[i].x, v[i].y);
- }
+ for (let i = 1; i < v.length; ++i) ctx.lineTo(v[i].x, v[i].y);
ctx.lineTo(v[0].x, v[0].y);
ctx.fill();
}
@@ -1260,6 +1250,69 @@ const level = {
doorBlock.classType = "body"
return doorBlock
},
+ doorMap(x, y, width, height, distance, speed = 20, addToWorld = true) { //for doors that use line of sight
+ x = x + width / 2
+ y = y + height / 2
+ const door = map[map.length] = Bodies.rectangle(x, y, width, height, {
+ collisionFilter: {
+ category: cat.map,
+ mask: cat.player | cat.body | cat.bullet | cat.powerUp | cat.mob | cat.mobBullet,
+ // mask: cat.player | cat.body | cat.bullet | cat.powerUp | cat.mob | cat.mobBullet //cat.player | cat.map | cat.body | cat.bullet | cat.powerUp | cat.mob | cat.mobBullet
+ },
+ inertia: Infinity, //prevents rotation
+ isNotHoldable: true,
+ friction: 1,
+ frictionStatic: 1,
+ restitution: 0,
+ isClosing: false,
+ openClose(isSetPaths = false) {
+ if (!m.isBodiesAsleep) {
+ if (this.isClosing) {
+ if (this.position.y < y) { //try to close
+ if ( //if clear of stuff
+ Matter.Query.collides(this, [player]).length === 0 &&
+ Matter.Query.collides(this, body).length < 2 &&
+ Matter.Query.collides(this, mob).length === 0
+ ) {
+ const position = {
+ x: this.position.x,
+ y: this.position.y + speed
+ }
+ Matter.Body.setPosition(this, position)
+ if (isSetPaths) simulation.draw.setPaths()
+ }
+ }
+ } else {
+ if (this.position.y > y - distance) { //try to open
+ const position = {
+ x: this.position.x,
+ y: this.position.y - speed
+ }
+ Matter.Body.setPosition(this, position)
+ if (isSetPaths) simulation.draw.setPaths()
+ }
+ }
+ }
+ },
+ isClosed() {
+ return this.position.y > y - 1
+ },
+ draw() {
+ ctx.fillStyle = "#666"
+ ctx.beginPath();
+ const v = this.vertices;
+ ctx.moveTo(v[0].x, v[0].y);
+ for (let i = 1; i < v.length; ++i) ctx.lineTo(v[i].x, v[i].y);
+ ctx.lineTo(v[0].x, v[0].y);
+ ctx.fill();
+ }
+ });
+
+ Matter.Body.setStatic(door, true); //make static
+ if (addToWorld) Composite.add(engine.world, door); //add to world
+ door.classType = "map"
+ return door
+ },
portal(centerA, angleA, centerB, angleB) {
const width = 50
const height = 150
@@ -1572,7 +1625,6 @@ const level = {
category: cat.map,
mask: cat.player | cat.body | cat.bullet | cat.powerUp | cat.mob | cat.mobBullet //cat.player | cat.map | cat.body | cat.bullet | cat.powerUp | cat.mob | cat.mobBullet
},
- isNoSetCollision: true,
inertia: Infinity, //prevents rotation
isNotHoldable: true,
friction: 0,
@@ -1655,7 +1707,6 @@ const level = {
category: cat.body,
mask: cat.player | cat.body | cat.bullet | cat.powerUp | cat.mob | cat.mobBullet //cat.player | cat.map | cat.body | cat.bullet | cat.powerUp | cat.mob | cat.mobBullet
},
- isNoSetCollision: true,
inertia: Infinity, //prevents rotation
isNotHoldable: true,
friction: 0,
@@ -2534,8 +2585,6 @@ const level = {
// level.difficultyIncrease(10 * 4);
// m.maxHealth = m.health = 100
-
- const mobSpawnCap = mobs.mobDeaths + 100
level.isProcedural = true //used in generating text for the level builder
simulation.draw.drawMapPath = simulation.draw.drawMapSight
@@ -2543,13 +2592,12 @@ const level = {
document.body.style.backgroundColor = "#e3e3e3"//"#e3e3e3"//color.map//"#333"//"#000"
level.defaultZoom = 1400
simulation.zoomTransition(level.defaultZoom)
- level.setPosToSpawn(1400 * (Math.random() < 0.5 ? 1 : -1), -250); //normal spawn
+ level.setPosToSpawn(450 * (Math.random() < 0.5 ? 1 : -1), -300); //normal spawn
// spawn.mapRect(level.enter.x, level.enter.y + 20, 100, 20); //entrance bump disabled for performance
level.exit.x = 0;
level.exit.y = -9000;
// spawn.mapRect(level.exit.x, level.exit.y + 20, 100, 100); //exit bump disabled for performance
-
const stationWidth = 9000
let stationNumber = 0;
let stationCustom = () => { }
@@ -2562,43 +2610,23 @@ const level = {
train[train.length - 1].isMoving = false
train[train.length - 1].stops = { left: -7225, right: -1725 }
- const stationList = [] //organize the possible stations into a random order, with one station removed
- for (let i = 0, totalNumberOfStations = 5; i < totalNumberOfStations; ++i) stationList.push(i) //!!!! update station number when you add a new station
+ const stationList = [] //use to randomize station order
+ for (let i = 1, totalNumberOfStations = 8; i < totalNumberOfStations; ++i) stationList.push(i) //!!!! update station number when you add a new station
shuffle(stationList);
- stationList.splice(Math.floor(Math.random() * stationList.length), 1); //remove one random element from array
+ stationList.splice(0, 3); //remove some stations to keep it to 4 stations
+ stationList.unshift(0) //add index zero to the front of the array
+
let isExitOpen = false
+ let gatesOpenRight = -1
+ let gatesOpenLeft = -1
const infrastructure = (x, isInProgress = true) => {
if (isInProgress) {
spawn.setSpawnList(); //picks a couple mobs types for a themed random mob spawns
-
- const mobsLeft = Math.floor(mobSpawnCap - mobs.mobDeaths - 50 / simulation.difficultyMode)
- if (mobsLeft > 0) {
- simulation.makeTextLog(`${mobsLeft} mobs left before exit opens`);
- } else {
- isExitOpen = true
- simulation.makeTextLog(`exit opened`);
- }
-
function removeAll(array) {
for (let i = 0; i < array.length; ++i) Matter.Composite.remove(engine.world, array[i]);
}
removeAll(map);
map = [];
- // removeAll(body); //can't remove bodies because it removes the train
- // body = [];
- // removeAll(mob);
- // mob = [];
- // removeAll(bullet);
- // bullet = [];
- // removeAll(composite);
- // composite = [];
- // removeAll(cons); // don't allow constraints that don't come from a mob (like elevators, rotors?)
- // cons = [];
- // removeAll(consBB); // don't allow constraints that don't come from a mob (like elevators, rotors?)
- // consBB = [];
- // removeAll(powerUp);
- // powerUp = [];
-
//remove any powerUp that is too far from player
for (let i = 0; i < powerUp.length; ++i) {
if (Vector.magnitudeSquared(Vector.sub(player.position, powerUp[i].position)) > 9000000) { //remove any powerUp farther then 3000 pixels from player
@@ -2617,15 +2645,293 @@ const level = {
mob.splice(i--, 1)
}
}
- } else {
- for (let i = 0; i < 8; ++i) powerUps.chooseRandomPowerUp(100 * (Math.random() - 0.5), -200 - 100 * Math.random())//only spawn heal or ammo once at the first station
+ }
+ const checkGate = (gate, gateButton) => {
+ if (gate) { //check status of buttons and gates
+ gate.isClosing = gateButton.isUp
+ gate.openClose(true);
+ if (gateButton.isUp) {
+ gateButton.query();
+ if (!gateButton.isUp) {
+ if (stationNumber > 0) {
+ gatesOpenRight = stationNumber
+ } else if (stationNumber < 0) {
+ gatesOpenLeft = stationNumber
+ } else { //starting station both doors open
+ gatesOpenLeft = stationNumber
+ gatesOpenRight = stationNumber
+ }
+ if (Math.abs(stationNumber) > 0 && ((Math.abs(stationNumber) + 1) % stationList.length) === 0) {
+ simulation.makeTextLog(`exit opened`);
+ isExitOpen = true;
+ }
+ }
+ }
+ gateButton.draw();
+ }
}
const stations = [ //update totalNumberOfStations as you add more stations
- () => { //slime
+ () => { //empty starting station
if (isExitOpen) {
- level.exit.x = x - 665;
- level.exit.y = -920;
+ level.exit.x = x - 50;
+ level.exit.y = -260;
+ } else {
+ var gateButton = level.button(x - 62, -237, 125, false) //x, y, width = 126, isSpawnBase = true
+ gateButton.isUp = true
+ if (stationNumber === 0 && gatesOpenRight === -1 && gatesOpenLeft === -1) {
+ var gateR = level.doorMap(x + 1375, -525, 50, 375, 300, 20, false) //x, y, width, height, distance, speed = 20
+ var gateL = level.doorMap(x - 1375, -525, 50, 375, 300, 20, false) //x, y, width, height, distance, speed = 20
+ for (let i = 0; i < 10; ++i) powerUps.chooseRandomPowerUp(x + 800 * (Math.random() - 0.5), -300 - 100 * Math.random())//only spawn heal or ammo once at the first station
+ }
}
+
+ spawn.mapRect(x + -1400, -750, 3375, 100); //roof
+ spawn.mapRect(x + -1500, -210, 3000, 400);//station floor
+ // spawn.mapRect(x + -550, -220, 1125, 100); //floor
+ // spawn.mapRect(x + -475, -230, 975, 150);//floor
+ spawn.mapVertex(x + 0, -200, "400 0 -400 0 -300 -80 300 -80"); //hexagon but wide
+
+ // spawn.mapRect(x + -1350, -550, 50, 150);
+ // spawn.mapRect(x + 1300, -550, 50, 150);
+ stationCustom = () => { };
+ stationCustomTopLayer = () => {
+ checkGate(gateR, gateButton)
+ checkGate(gateL, gateButton)
+ };
+ },
+ () => { //portal maze
+ const buttonsCoords = [{ x: x + 50, y: -1595 }, { x: x + 637, y: -2195 }, { x: x - 1487, y: -2145 }]
+ const buttonsCoordsIndex = Math.floor(Math.random() * buttonsCoords.length) //pick a random element from the array
+ if (isExitOpen) {
+ level.exit.x = buttonsCoords[buttonsCoordsIndex].x;
+ level.exit.y = buttonsCoords[buttonsCoordsIndex].y - 25;
+ } else {
+ var gateButton = level.button(buttonsCoords[buttonsCoordsIndex].x, buttonsCoords[buttonsCoordsIndex].y, 126, false) //x, y, width = 126, isSpawnBase = true
+ gateButton.isUp = true
+ if (stationNumber > gatesOpenRight) {
+ var gate = level.doorMap(x + 1375, -525, 50, 375, 300, 20, false) //x, y, width, height, distance, speed = 20
+ } else if (stationNumber < gatesOpenLeft) {
+ var gate = level.doorMap(x - 1375, -525, 50, 375, 300, 20, false) //x, y, width, height, distance, speed = 20
+ }
+ }
+
+ spawn.mapRect(x + -1500, -210, 3000, 400);//station floor
+ spawn.mapRect(x + -1775, -1600, 3400, 1100); //center pillar
+ spawn.mapRect(x + -4100, -3325, 8000, 700); //roof
+ spawn.mapRect(x + -4100, -3325, 325, 1500);
+ spawn.mapRect(x + 3500, -3325, 400, 1500);
+ spawn.mapRect(x + -225, -575, 450, 425); //lower portal blocks
+
+ //upper parts
+ spawn.mapRect(x + -1425, -2400, 1900, 50);
+ spawn.mapRect(x + -775, -2750, 575, 1045);
+ spawn.mapRect(x + 475, -1900, 450, 375);
+ spawn.mapRect(x + 2225, -2300, 125, 350);
+ spawn.mapRect(x + 2550, -2350, 700, 50);
+ spawn.mapRect(x + 1375, -2850, 125, 650);
+ spawn.mapRect(x + 600, -2200, 200, 195);
+ spawn.mapRect(x + -3500, -2275, 825, 75);
+ spawn.mapRect(x + -1550, -2150, 250, 250);
+ spawn.mapRect(x + -2575, -2450, 275, 345);
+
+ if (!isExitOpen) {
+ if (Math.random() < 0.5) {
+ spawn.randomMob(x + 2850, -2425, 0);
+ spawn.randomMob(x + 2275, -2425, 0);
+ spawn.randomMob(x + 2000, -2150, 0);
+ spawn.randomMob(x + 1650, -2150, 0);
+ spawn.randomMob(x + 1000, -2475, 0);
+ spawn.randomMob(x + 725, -2450, 0);
+ spawn.randomMob(x + 525, -2175, 0);
+ spawn.randomMob(x + 200, -1950, 0);
+ spawn.randomMob(x + -25, -1825, 0);
+ spawn.randomMob(x + -975, -2000, 0);
+ spawn.randomMob(x + -1500, -2225, 0);
+ spawn.randomMob(x + 1850, -2125, 0);
+ spawn.randomMob(x + 225, -1975, 0);
+ spawn.randomMob(x + 25, -1950, 0);
+ spawn.randomMob(x + 25, -1950, 0);
+ } else {
+ spawn.randomMob(x + 250, -1850, 0);
+ spawn.randomMob(x + 225, -1950, 0);
+ spawn.randomMob(x + 125, -2000, 0);
+ spawn.randomMob(x + 0, -1800, 0);
+ spawn.randomMob(x + -1725, -2300, 0);
+ spawn.randomMob(x + -2025, -2175, 0);
+ spawn.randomMob(x + -2050, -2250, 0);
+ spawn.randomMob(x + -2000, -2350, 0);
+ spawn.randomMob(x + -2950, -2400, 0);
+ spawn.randomMob(x + -2775, -2400, 0);
+ spawn.randomMob(x + -2425, -2550, 0);
+ spawn.randomMob(x + 1950, -2225, 0);
+ spawn.randomMob(x + -2700, -2100, 0);
+ spawn.randomMob(x + -1925, -2175, 0);
+ spawn.randomMob(x + -825, -2050, 0);
+ }
+ }
+
+ const portal1 = level.portal({ x: x - 250, y: -310 }, Math.PI,
+ { x: x + -3750, y: -2100 }, 0)
+ const portal2 = level.portal({ x: x + 250, y: -310 }, 0,
+ { x: x + 3475, y: -2100 }, Math.PI)
+ const portal3 = level.portal({ x: x - 800, y: -2500 }, Math.PI,
+ { x: x - 175, y: -2500 }, 0)
+ const portal4 = level.portal({ x: x + 1275, y: -1700 }, Math.PI,
+ { x: x - 1275, y: -1700 }, 0)
+ stationCustom = () => {
+ portal1[2].query()
+ portal1[3].query()
+ portal2[2].query()
+ portal2[3].query()
+ portal3[2].query()
+ portal3[3].query()
+ portal4[2].query()
+ portal4[3].query()
+ }
+ stationCustomTopLayer = () => {
+ checkGate(gate, gateButton)
+ portal1[0].draw();
+ portal1[1].draw();
+ portal1[2].draw();
+ portal1[3].draw();
+ portal2[0].draw();
+ portal2[1].draw();
+ portal2[2].draw();
+ portal2[3].draw();
+ portal3[0].draw();
+ portal3[1].draw();
+ portal3[2].draw();
+ portal3[3].draw();
+ portal4[0].draw();
+ portal4[1].draw();
+ portal4[2].draw();
+ portal4[3].draw();
+ }
+ },
+ () => { //opening and closing doors
+ const buttonsCoords = [{ x: x - 800, y: -2245 }, { x: x + 250, y: -870 }, { x: x + 1075, y: -1720 }, { x: x - 1600, y: -1995 }]
+ const buttonsCoordsIndex = Math.floor(Math.random() * buttonsCoords.length) //pick a random element from the array
+ if (isExitOpen) {
+ level.exit.x = buttonsCoords[buttonsCoordsIndex].x;
+ level.exit.y = buttonsCoords[buttonsCoordsIndex].y - 25;
+ } else {
+ var gateButton = level.button(buttonsCoords[buttonsCoordsIndex].x, buttonsCoords[buttonsCoordsIndex].y, 126, false) //x, y, width = 126, isSpawnBase = true
+ gateButton.isUp = true
+ if (stationNumber > gatesOpenRight) {
+ var gate = level.doorMap(x + 1375, -525, 50, 375, 300, 20, false) //x, y, width, height, distance, speed = 20
+ } else if (stationNumber < gatesOpenLeft) {
+ var gate = level.doorMap(x - 1375, -525, 50, 375, 300, 20, false) //x, y, width, height, distance, speed = 20
+ }
+ }
+
+ if (!isExitOpen) {
+ if (Math.random() < 0.5) {
+ spawn.randomMob(x + 1125, -650, 0);
+ spawn.randomMob(x + 150, -950, 0);
+ spawn.randomMob(x + 100, -975, 0);
+ spawn.randomMob(x + 75, -975, 0);
+ spawn.randomMob(x + 275, -1225, 0);
+ spawn.randomMob(x + 825, -975, 0);
+ spawn.randomMob(x + -50, -1625, 0);
+ spawn.randomMob(x + -950, -1550, 0);
+ spawn.randomMob(x + -975, -1550, 0);
+ spawn.randomMob(x + -900, -2500, 0);
+ spawn.randomMob(x + -975, -2550, 0);
+ spawn.randomMob(x + 675, -1950, 0);
+ spawn.randomMob(x + 675, -2550, 0);
+ spawn.randomMob(x + 1225, -1825, 0);
+ spawn.randomMob(x + -750, -2450, 0);
+ spawn.randomMob(x + -700, -825, 0);
+ } else {
+ spawn.randomMob(x + -675, -675, 0);
+ spawn.randomMob(x + -575, -925, 0);
+ spawn.randomMob(x + -425, -1100, 0);
+ spawn.randomMob(x + -225, -1225, 0);
+ spawn.randomMob(x + -650, -1250, 0);
+ spawn.randomMob(x + -675, -775, 0);
+ spawn.randomMob(x + 75, -1000, 0);
+ spawn.randomMob(x + -1100, -1575, 0);
+ spawn.randomMob(x + -1250, -1850, 0);
+ spawn.randomMob(x + -1625, -2100, 0);
+ spawn.randomMob(x + -700, -2500, 0);
+ spawn.randomMob(x + -375, -2550, 0);
+ spawn.randomMob(x + 250, -2025, 0);
+ spawn.randomMob(x + 675, -2175, 0);
+ spawn.randomMob(x + -1000, -2000, 0);
+ spawn.randomMob(x + -1550, -2325, 0);
+ spawn.randomMob(x + -1725, -2425, 0);
+ }
+ }
+
+ spawn.mapRect(x + -1500, -210, 3000, 400);//station floor
+ spawn.mapRect(x + -2550, -3200, 425, 1375);//roof left wall
+ spawn.mapRect(x + 2125, -3175, 450, 1375);//roof right wall
+ spawn.mapRect(x + -2550, -3200, 5125, 225);//roof
+
+ spawn.mapRect(x + -1325, -550, 1375, 50);//first floor roof/ground
+ spawn.mapRect(x + 775, -550, 675, 50);
+ spawn.mapRect(x + -200, -875, 1300, 50); //2nd floor roof/ground
+ spawn.mapRect(x + -125, -1125, 50, 275);
+ spawn.mapRect(x + -125, -1150, 800, 50); //3rd floor roof/ground
+ spawn.mapRect(x + -1450, -1475, 1600, 50);
+ spawn.mapRect(x + -1325, -1725, 800, 50); //4th floor roof/ground
+ spawn.mapRect(x + 50, -1725, 1350, 50);
+ spawn.mapRect(x + -1125, -2250, 700, 50);
+
+ spawn.mapRect(x, -525, 50, 150); //door cap for ground at ground y = -210
+ const door1 = level.doorMap(x + 12, -380, 25, 170, 140, 20, false) //x, y, width, height, distance, speed = 20
+ spawn.mapRect(x - 200, -525 - 340, 50, 150); //door cap for ground at ground y = -210
+ const door2 = level.doorMap(x - 188, -380 - 340, 25, 170, 140, 20, false) //x, y, width, height, distance, speed = 20
+ spawn.mapRect(x + 100, -525 - 940, 50, 150); //door cap for ground at ground y = -210
+ const door3 = level.doorMap(x + 112, -380 - 940, 25, 170, 140, 20, false) //x, y, width, height, distance, speed = 20
+ spawn.mapRect(x + 450, -3050, 50, 775);
+ const door4 = level.doorMap(x + 462, -2300, 25, 575, 520, 30, false) //x, y, width, height, distance, speed = 20
+
+ const portal1 = level.portal({
+ x: x + 2100,
+ y: -2100
+ }, Math.PI, { //right
+ x: x + -1275,
+ y: -650
+ }, 2 * Math.PI) //right
+
+ stationCustom = () => {
+ door1.isClosing = (simulation.cycle % 240) < 120
+ door1.openClose(true);
+ door2.isClosing = (simulation.cycle % 240) > 120
+ door2.openClose(true);
+ door3.isClosing = (simulation.cycle % 240) < 120
+ door3.openClose(true);
+ door4.isClosing = (simulation.cycle % 240) > 120
+ door4.openClose(true);
+ portal1[2].query()
+ portal1[3].query()
+ }
+ stationCustomTopLayer = () => {
+ checkGate(gate, gateButton)
+ portal1[0].draw();
+ portal1[1].draw();
+ portal1[2].draw();
+ portal1[3].draw();
+ }
+ },
+ () => { //slime
+ const buttonsCoords = [{ x: x - 675, y: -895 }, { x: x - 750, y: -70 }, { x: x + 75, y: -570 },]
+ const buttonsCoordsIndex = Math.floor(Math.random() * buttonsCoords.length) //pick a random element from the array
+ if (isExitOpen) {
+ level.exit.x = buttonsCoords[buttonsCoordsIndex].x;
+ level.exit.y = buttonsCoords[buttonsCoordsIndex].y - 25;
+ } else {
+ var gateButton = level.button(buttonsCoords[buttonsCoordsIndex].x, buttonsCoords[buttonsCoordsIndex].y, 126, false) //x, y, width = 126, isSpawnBase = true
+ gateButton.isUp = true
+ if (stationNumber > gatesOpenRight) {
+ var gate = level.doorMap(x + 1375, -525, 50, 375, 300, 20, false) //x, y, width, height, distance, speed = 20
+ } else if (stationNumber < gatesOpenLeft) {
+ var gate = level.doorMap(x - 1375, -525, 50, 375, 300, 20, false) //x, y, width, height, distance, speed = 20
+ }
+ }
+
+
spawn.mapRect(x + -1575, -2000, 3025, 100); //roof
// spawn.mapRect(x + -1575, -2200, 3025, 300); //roof
// spawn.mapRect(x + -1500, -210, 3000, 400);//station floor
@@ -2646,7 +2952,7 @@ const level = {
spawn.mapRect(x - 925, -75, 875, 150);
spawn.mapRect(x + 475, -1400, 75, 1250);
- if (mobs.mobDeaths < mobSpawnCap) {
+ if (!isExitOpen) {
if (Math.random() < 0.5) {
spawn.randomMob(x + -850, -450, 0);
spawn.randomMob(x + -850, -125, 0);
@@ -2694,6 +3000,8 @@ const level = {
// drip3.draw();
}
stationCustomTopLayer = () => {
+ checkGate(gate, gateButton)
+
hazard1.query();
hazard1.level(isSlimeRiseUp, 1.5)
if (!(hazard1.height < hazard1.maxHeight)) {
@@ -2705,11 +3013,24 @@ const level = {
boost2.query();
}
},
- () => { //portals
+ () => { //portal fling
+ const buttonsCoords = [{ x: x + 775, y: -1695 }, { x: x - 775, y: -800 }, { x: x - 375, y: -2083 },]
+ const buttonsCoordsIndex = Math.floor(Math.random() * buttonsCoords.length) //pick a random element from the array
if (isExitOpen) {
- level.exit.x = x + 950;
- level.exit.y = -1725;
+ level.exit.x = buttonsCoords[buttonsCoordsIndex].x;
+ level.exit.y = buttonsCoords[buttonsCoordsIndex].y - 25;
+ } else {
+ var gateButton = level.button(buttonsCoords[buttonsCoordsIndex].x, buttonsCoords[buttonsCoordsIndex].y, 126, false) //x, y, width = 126, isSpawnBase = true
+ gateButton.isUp = true
+ if (stationNumber > gatesOpenRight) {
+ var gate = level.doorMap(x + 1375, -525, 50, 375, 300, 20, false) //x, y, width, height, distance, speed = 20
+ } else if (stationNumber < gatesOpenLeft) {
+ var gate = level.doorMap(x - 1375, -525, 50, 375, 300, 20, false) //x, y, width, height, distance, speed = 20
+ }
}
+ spawn.mapRect(x + -1600, -3450, 300, 1475); //roof
+ spawn.mapRect(x + -1600, -3450, 3225, 100);
+ spawn.mapRect(x + 1300, -3450, 325, 1525);
spawn.mapVertex(x + 400, -180, "-300 0 -300 -100 300 -100 400 0");
spawn.mapVertex(x - 400, -180, "300 0 300 -100 -300 -100 -400 0");
@@ -2719,12 +3040,11 @@ const level = {
spawn.mapRect(x + 125, -700, 1225, 200);
spawn.mapRect(x + -1325, -1775, 775, 175);
spawn.mapVertex(x + 445, -800, "-200 0 -200 -300 100 -300 185 0");
- spawn.mapVertex(x - 350, -1850, "-185 0 -100 -400 400 -400 400 0");
+ spawn.mapVertex(x - 310, -1880, "-185 0 -100 -400 400 -400 400 0");
spawn.mapVertex(x + -675, -725, "325 0 250 80 -250 80 -325 0 -250 -80 250 -80");
- spawn.mapRect(x + 650, -1700, 725, 550);
- spawn.mapRect(x + 1500, -2375, 275, 450);
+ spawn.mapRect(x + 625, -1700, 750, 500);
- if (mobs.mobDeaths < mobSpawnCap) {
+ if (!isExitOpen) {
spawn.randomMob(x + -750, -1925, 0);
spawn.randomMob(x + -425, -2300, 0);
spawn.randomMob(x + -350, -2200, 0);
@@ -2745,10 +3065,10 @@ const level = {
const portal1 = level.portal({
x: x + 0,
y: -200
- }, -Math.PI / 2, { //right
+ }, -Math.PI / 2, { //up
x: x + 200,
y: -900
- }, -Math.PI / 2) //right
+ }, -Math.PI / 2) //up
const portal2 = level.portal({
x: x + 1275,
y: -800
@@ -2764,6 +3084,7 @@ const level = {
portal2[3].query()
}
stationCustomTopLayer = () => {
+ checkGate(gate, gateButton)
portal1[0].draw();
portal1[1].draw();
portal1[2].draw();
@@ -2775,10 +3096,21 @@ const level = {
}
},
() => { //tower levels and squares
+ const buttonsCoords = [{ x: x - 300, y: -3120 }, { x: x + 600, y: -3020 }, { x: x - 575, y: -1770 }, { x: x - 450, y: -2370 }]
+ const buttonsCoordsIndex = Math.floor(Math.random() * buttonsCoords.length) //pick a random element from the array
if (isExitOpen) {
- level.exit.x = x - 450;
- level.exit.y = -3150;
+ level.exit.x = buttonsCoords[buttonsCoordsIndex].x;
+ level.exit.y = buttonsCoords[buttonsCoordsIndex].y - 25;
+ } else {
+ var gateButton = level.button(buttonsCoords[buttonsCoordsIndex].x, buttonsCoords[buttonsCoordsIndex].y, 126, false) //x, y, width = 126, isSpawnBase = true
+ gateButton.isUp = true
+ if (stationNumber > gatesOpenRight) {
+ var gate = level.doorMap(x + 1375, -525, 50, 375, 300, 20, false) //x, y, width, height, distance, speed = 20
+ } else if (stationNumber < gatesOpenLeft) {
+ var gate = level.doorMap(x - 1375, -525, 50, 375, 300, 20, false) //x, y, width, height, distance, speed = 20
+ }
}
+
spawn.mapRect(x + -1500, -210, 3000, 400);//station floor
spawn.mapRect(x + -1625, -3950, 3225, 350);//roof
spawn.mapRect(x + 1300, -3850, 300, 2150); //roof wall
@@ -2869,20 +3201,28 @@ const level = {
},
]
- if (mobs.mobDeaths < mobSpawnCap) mobPlacement[Math.floor(Math.random() * mobPlacement.length)]()//different random mob placements, with mobs clustered to surprise player
+ if (!isExitOpen) mobPlacement[Math.floor(Math.random() * mobPlacement.length)]()//different random mob placements, with mobs clustered to surprise player
stationCustom = () => { }
- stationCustomTopLayer = () => { }
+ stationCustomTopLayer = () => {
+ checkGate(gate, gateButton)
+ }
},
() => { //jump pads and 6 sided platforms
+ const buttonsCoords = [{ x: x + 275, y: -1817 }, { x: x + 2025, y: -1995 }, { x: x - 2025, y: -2420 }, { x: x - 2100, y: -1995 }]
+ const buttonsCoordsIndex = Math.floor(Math.random() * buttonsCoords.length) //pick a random element from the array
if (isExitOpen) {
- if (Math.random() < 0.5) {
- level.exit.x = x - 2075;
- level.exit.y = -2450;
- } else {
- level.exit.x = x + 2250;
- level.exit.y = -2020;
+ level.exit.x = buttonsCoords[buttonsCoordsIndex].x;
+ level.exit.y = buttonsCoords[buttonsCoordsIndex].y - 25;
+ } else {
+ var gateButton = level.button(buttonsCoords[buttonsCoordsIndex].x, buttonsCoords[buttonsCoordsIndex].y, 126, false) //x, y, width = 126, isSpawnBase = true
+ gateButton.isUp = true
+ if (stationNumber > gatesOpenRight) {
+ var gate = level.doorMap(x + 1375, -525, 50, 375, 300, 20, false) //x, y, width, height, distance, speed = 20
+ } else if (stationNumber < gatesOpenLeft) {
+ var gate = level.doorMap(x - 1375, -525, 50, 375, 300, 20, false) //x, y, width, height, distance, speed = 20
}
}
+
spawn.mapRect(x + -1500, -210, 3000, 400);//station floor
spawn.mapRect(x + -3200, -3200, 300, 1400); //roof left wall
spawn.mapRect(x + 2600, -3200, 300, 1400);//roof right wall
@@ -2952,21 +3292,32 @@ const level = {
spawn.randomMob(x + -600, -375, 0);
},
]
- if (mobs.mobDeaths < mobSpawnCap) mobPlacement[Math.floor(Math.random() * mobPlacement.length)]()//different random mob placements, with mobs clustered to surprise player
+ if (!isExitOpen) mobPlacement[Math.floor(Math.random() * mobPlacement.length)]()//different random mob placements, with mobs clustered to surprise player
const boost1 = level.boost(x - 50, -225, 790)
const boost2 = level.boost(x + 550, -985, 900)
const boost3 = level.boost(x + -850, -835, 1900)
stationCustom = () => { }
stationCustomTopLayer = () => {
+ checkGate(gate, gateButton)
boost1.query();
boost2.query();
boost3.query();
}
},
() => { //crouch tunnels
+ const buttonsCoords = [{ x: x + 625, y: -1395 }, { x: x - 15, y: -1595 }, { x: x - 800, y: -1295 }]
+ const buttonsCoordsIndex = Math.floor(Math.random() * buttonsCoords.length) //pick a random element from the array
if (isExitOpen) {
- level.exit.x = x + 0;
- level.exit.y = -1620;
+ level.exit.x = buttonsCoords[buttonsCoordsIndex].x;
+ level.exit.y = buttonsCoords[buttonsCoordsIndex].y - 25;
+ } else {
+ var gateButton = level.button(buttonsCoords[buttonsCoordsIndex].x, buttonsCoords[buttonsCoordsIndex].y, 126, false) //x, y, width = 126, isSpawnBase = true
+ gateButton.isUp = true
+ if (stationNumber > gatesOpenRight) {
+ var gate = level.doorMap(x + 1375, -525, 50, 375, 300, 20, false) //x, y, width, height, distance, speed = 20
+ } else if (stationNumber < gatesOpenLeft) {
+ var gate = level.doorMap(x - 1375, -525, 50, 375, 300, 20, false) //x, y, width, height, distance, speed = 20
+ }
}
spawn.mapRect(x + -1500, -210, 3000, 400);//station floor
spawn.mapRect(x + -1575, -2200, 3025, 300); //roof
@@ -2981,13 +3332,12 @@ const level = {
spawn.mapRect(x + -225, -525, 800, 210);
spawn.mapRect(x + -100, -1600, 300, 193);
spawn.mapRect(x + 925, -1250, 75, 75);
- spawn.bodyRect(x + 625, -1550, 150, 150, 0.3);
spawn.bodyRect(x + 200, -1475, 75, 175, 0.3);
spawn.bodyRect(x + -25, -625, 225, 100, 0.3);
spawn.bodyRect(x + -1000, -750, 125, 175, 0.3);
spawn.bodyRect(x + -625, -1450, 75, 150, 0.3);
spawn.bodyRect(x + -650, -300, 300, 75, 0.3);
- if (mobs.mobDeaths < mobSpawnCap) {
+ if (!isExitOpen) {
spawn.randomMob(x + -750, -1425, 0);
spawn.randomMob(x + -1050, -1100, 0);
spawn.randomMob(x + -825, -750, 0);
@@ -3006,6 +3356,7 @@ const level = {
}
stationCustom = () => { }
stationCustomTopLayer = () => {
+ checkGate(gate, gateButton)
ctx.fillStyle = "rgba(0,0,0,0.08)"
ctx.fillRect(x + -225, -325, 800, 125);
ctx.fillRect(x + -100, -1425, 300, 125);
@@ -3013,8 +3364,8 @@ const level = {
}
},
]
- // stations[0]() //for testing a specific station
- stations[stationList[Math.abs((stationNumber + Math.floor(stationList.length / 2)) % stationList.length)]]() //*************** run this one when uploading
+ // stations[1]() //for testing a specific station
+ stations[stationList[Math.abs(stationNumber % stationList.length)]]() //*************** run this one when uploading
//add in standard station map infrastructure
spawn.mapRect(x + -8000, 0, 16000, 800);//tunnel floor
spawn.mapRect(x + 1500 - 200, -2000, 6400, 1500); //tunnel roof
@@ -3073,9 +3424,11 @@ const level = {
//track what station the player is in
if (m.pos.x > 0.55 * stationWidth + stationNumber * stationWidth) {
stationNumber++
+ // if ((stationNumber % stationList.length) == 0) stationNumber++ //skip the stationNumber that is the modulus of the length of the stationList
infrastructure(stationNumber * stationWidth)
} else if (m.pos.x < -0.55 * stationWidth + stationNumber * stationWidth) {
stationNumber--
+ // if ((stationNumber % stationList.length) == 0) stationNumber--//skip the stationNumber that is the modulus of the length of the stationList
infrastructure(stationNumber * stationWidth)
}
stationCustom()
@@ -17029,7 +17382,6 @@ const level = {
category: cat.body,
mask: cat.player | cat.body | cat.bullet | cat.powerUp | cat.mob | cat.mobBullet
},
- isNoSetCollision: true,
isNotHoldable: true,
frictionAir: 0,
friction: 1,
@@ -17058,7 +17410,6 @@ const level = {
category: cat.body,
mask: cat.player | cat.body | cat.bullet | cat.powerUp | cat.mob | cat.mobBullet
},
- isNoSetCollision: true,
isNotHoldable: true,
frictionAir: 0.01,
friction: 1,
@@ -17120,7 +17471,6 @@ const level = {
category: cat.body,
mask: cat.player | cat.body | cat.bullet | cat.powerUp | cat.mob | cat.mobBullet
},
- isNoSetCollision: true,
isNotHoldable: true,
friction: 1,
frictionStatic: 1,
@@ -17183,7 +17533,6 @@ const level = {
category: cat.body,
mask: cat.player | cat.body | cat.bullet | cat.powerUp | cat.mob | cat.mobBullet
},
- isNoSetCollision: true,
isNotHoldable: true,
frictionAir: frictionAir,
friction: 1,
@@ -17196,7 +17545,6 @@ const level = {
category: cat.body,
mask: cat.player | cat.body | cat.bullet | cat.powerUp | cat.mob | cat.mobBullet
},
- isNoSetCollision: true,
isNotHoldable: true,
frictionAir: 0.01,
friction: 1,
@@ -17321,7 +17669,6 @@ const level = {
category: cat.map,
mask: cat.player | cat.body | cat.bullet | cat.powerUp | cat.mob | cat.mobBullet //cat.player | cat.map | cat.body | cat.bullet | cat.powerUp | cat.mob | cat.mobBullet
},
- isNoSetCollision: true,
inertia: Infinity, //prevents rotation
isNotHoldable: true,
friction: 1,
@@ -17367,7 +17714,6 @@ const level = {
category: cat.map,
mask: cat.player | cat.body | cat.bullet | cat.powerUp | cat.mob | cat.mobBullet //cat.player | cat.map | cat.body | cat.bullet | cat.powerUp | cat.mob | cat.mobBullet
},
- isNoSetCollision: true,
inertia: Infinity, //prevents rotation
isNotHoldable: true,
friction: 1,
@@ -17943,7 +18289,7 @@ const level = {
}
var circleHead = Matter.Bodies.polygon(m.pos.x, m.pos.y, 0, 31);
- var losDomain = generateIntersectMap().concat(mob.filter((obj) => { return obj.isNotCloaked == null && (obj.isBoss || obj.label != 'Circle Body') }), [pendulum1, gear1, gear2, player, circleHead]);
+ var losDomain = generateIntersectMap().concat(mob.filter((obj) => { return obj.isNotCloaked == null && (obj.isBoss || obj.label != 'Circle Body') }), [pendulum1, gear1, gear2, piston1, player, circleHead]);
var oldMap = [...map];
var oldMob = [...mob];
var spawnGearMobCycle = 0;
@@ -17959,7 +18305,7 @@ const level = {
level.custom = () => {
Matter.Body.setPosition(circleHead, m.pos)
if (!(compareArrays(oldMap, map) && compareArrays(oldMob, mob))) {
- losDomain = generateIntersectMap().concat(mob.filter((obj) => { return obj.isNotCloaked == null && (obj.isBoss || obj.label != 'Circle Body') }), [pendulum1, gear1, gear2, player, circleHead]);
+ losDomain = generateIntersectMap().concat(mob.filter((obj) => { return obj.isNotCloaked == null && (obj.isBoss || obj.label != 'Circle Body') }), [pendulum1, gear1, gear2, piston1, player, circleHead]);
}
oldMap = [...map];
oldMob = [...mob];
@@ -17976,8 +18322,7 @@ const level = {
// light
var lightPos = { x: 400, y: -2775 };
var lightRadius = 2950;
- const vertices = circleLoS(lightPos, lightRadius, map.concat(mob.filter((obj) => { return obj.isNotCloaked == null && (obj.isBoss || obj.label != 'Circle Body') }), [pendulum1, gear1, gear2, player, circleHead]));
- if (vertices.length > 0 && vertices[0].x) {
+ const vertices = circleLoS(lightPos, lightRadius, map.concat(mob.filter((obj) => { return obj.isNotCloaked == null && (obj.isBoss || obj.label != 'Circle Body') }), [pendulum1, gear1, gear2, piston1, player, circleHead])); if (vertices.length > 0 && vertices[0].x) {
ctx.beginPath();
ctx.moveTo(vertices[0].x, vertices[0].y);
for (var i = 1; i < vertices.length; i++) {
diff --git a/js/tech.js b/js/tech.js
index b45c964..141d7e4 100644
--- a/js/tech.js
+++ b/js/tech.js
@@ -6103,7 +6103,7 @@ const tech = {
},
{
name: "siphonaptera",
- description: "sporangium and shotgun hatch fleas",
+ description: "spores metamorphose into fleas",
isGunTech: true,
maxCount: 1,
count: 0,
@@ -6123,7 +6123,7 @@ const tech = {
},
{
name: "nematodes",
- description: "shotgun and sporangium hatch worms",
+ description: "spores metamorphose into worms",
isGunTech: true,
maxCount: 1,
count: 0,
diff --git a/todo.txt b/todo.txt
index f6d76c9..0c693f0 100644
--- a/todo.txt
+++ b/todo.txt
@@ -1,14 +1,26 @@
******************************************************** NEXT PATCH **************************************************
-new community level cantilever
+subway
+ start in an empty station
+ station exits are blocked by a gate that opens when you press a button
+ level exit spawns after you clear 4 stations
+ added 2 new stations (7 possible stations)
-extended tech sorting to experiment
+laser-bots lasers wiggle as it aims at the target
+ this is a nerf to iridescence and it looks cool
-acetone peroxide does 40->33% more explosion harm to player
-fleas can do up to 30% more damage before they despawn
+bug fixes
*********************************************************** TODO *****************************************************
+only calculate setPaths on subway level?
+ does this mess with flatland, no
+
+improve flatland performance?
+
+get rid of the word permanent in bot tech
+ ersatz bots, perimeter defense, network effect
+
maybe reduce the fps on the line of sight graphics to make it look more like a sensor?
make a bot the follows the player the pov for line of sight levels, not the player
also need to make the vision a slice of a circle, not a full circle
@@ -1228,6 +1240,7 @@ possible names for tech
this is violated by expansion of the universe
https://en.wikipedia.org/wiki/Cosmic_censorship_hypothesis - black holes can't leak
Alcubierre warp drive (FTL with negative mass)
+ Spherules - A spherule is a small sphere or spherical body. It can also refer to a thick-walled spherical structure that contains endospores and occurs in the parasitic form of fungi
******************************************************** CARS IMAGES ********************************************************