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