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 ********************************************************