let body = []; //non static bodies let map = []; //all static bodies let cons = []; //all constraints between a point and a body let consBB = []; //all constraints between two bodies let composite = [] //rotors and other map elements that don't fit const level = { defaultZoom: 1400, onLevel: -1, levelsCleared: 0, // playableLevels: ["pavilion", "pavilion", "pavilion", "pavilion", "pavilion", "pavilion", "pavilion", "pavilion", "pavilion", "pavilion", "pavilion"], //see level.populateLevels: (intro, ... , reservoir or factory, reactor, ... , gauntlet, final) added later playableLevels: ["labs", "rooftops", "skyscrapers", "warehouse", "highrise", "office", "aerie", "satellite", "sewers", "testChamber", "pavilion", "lock"], 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", "dojo", "tlinat", "ruins", "ace", "crimsonTowers"], trainingLevels: ["walk", "crouch", "jump", "hold", "throw", "throwAt", "deflect", "heal", "fire", "nailGun", "shotGun", "superBall", "matterWave", "missile", "stack", "mine", "grenades", "harpoon", "diamagnetism"], levels: [], start() { if (level.levelsCleared === 0) { //this code only runs on the first level // simulation.enableConstructMode() //tech.giveTech('motion sickness') //used to build maps in testing mode // simulation.isHorizontalFlipped = true // tech.giveTech("performance") // level.difficultyIncrease(3 * 4) //30 is near max on hard //60 is near max on why // spawn.setSpawnList(); // spawn.setSpawnList(); // m.maxHealth = m.health = 100 // tech.isRerollDamage = true // powerUps.research.changeRerolls(99999) // m.immuneCycle = Infinity //you can't take damage // tech.tech[297].frequency = 100 // m.couplingChange(10) // m.setField("molecular assembler") //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 // 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("drones") //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 // requestAnimationFrame(() => { tech.giveTech("MACHO") }); // for (let i = 0; i < 1; ++i) tech.giveTech("additive manufacturing") // for (let i = 0; i < 1; ++i) tech.giveTech("dark star") // for (let i = 0; i < 1; ++i) tech.giveTech("foam-bot") // for (let i = 0; i < 1; ++i) tech.giveTech("nail-bot") // for (let i = 0; i < 1; ++i) tech.giveTech("sound-bot upgrade") // for (let i = 0; i < 1; ++i) tech.giveTech("nail-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("uncertainty principle") // for (let i = 0; i < 1; ++i) tech.giveTech("mechanical resonance") // for (let i = 0; i < 3; i++) powerUps.directSpawn(450, -50, "tech"); // for (let i = 0; i < 10; i++) powerUps.directSpawn(1750, -500, "research"); // for (let i = 0; i < 10; i++) powerUps.directSpawn(1750, -500, "coupling"); // level.testing(); // for (let i = 0; i < 10; ++i) spawn.sniper(1900, -500) // for (let i = 0; i < 1; ++i) spawn.slasher2(1900, -500) // for (let i = 0; i < 1; ++i) spawn.shooterBoss(1900, -2500) // spawn.suckerBoss(1900, -500, 25) // spawn.slasher2(2000, -1150) // spawn.zombie(-3000, -500 + 300 * Math.random(), 30, 5, "white") // zombie(x, y, radius, sides, color) // for (let i = 0; i < 20; ++i) spawn.starter(1000 + 1000 * Math.random(), -500 + 300 * Math.random()) // tech.addJunkTechToPool(2) // tech.tech[322].frequency = 100 // spawn.tetherBoss(1900, -500, { x: 1900, y: -500 }) // for (let i = 0; i < 40; ++i) tech.giveTech() level[simulation.isTraining ? "walk" : "intro"]() //normal starting level ************************************************** // simulation.isAutoZoom = false; //look in close // simulation.zoomScale *= 0.5; // simulation.setZoom(); // for (let i = 0; i < 2; ++i) powerUps.directSpawn(m.pos.x + 50 * Math.random(), m.pos.y + 50 * Math.random(), "tech"); // for (let i = 0; i < 2; ++i) powerUps.directSpawn(m.pos.x + 50 * Math.random(), m.pos.y + 50 * Math.random(), "boost"); // for (let i = 0; i < 2; ++i) powerUps.directSpawn(m.pos.x + 50 * Math.random(), m.pos.y + 50 * Math.random(), "heal"); // for (let i = 0; i < 2; i++) powerUps.spawn(player.position.x + Math.random() * 50, player.position.y - Math.random() * 50, "field", false); //lore testing // for (let i = 0; i < 5; i++) tech.giveTech("undefined") // lore.techCount = 2 // simulation.isCheating = false //true; // level.levelsCleared = 10 // localSettings.loreCount = 5 //this sets what conversation is heard // if (localSettings.isAllowed) localStorage.setItem("localSettings", JSON.stringify(localSettings)); //update local storage // level.onLevel = -1 //this sets level.levels[level.onLevel] = undefined which is required to run the conversation // level.null() // localSettings.isHuman = true // tech.isNoDraftPause = false //disable pause // mobs.mobDeaths = 200 //to prevent pacifist mode // for (let i = 0; i < 13; i++) level.nextLevel(); //jump to final boss // lore.unlockTesting(); // tech.giveTech("tinker"); //show junk tech in experiment mode // m.storeTech() // powerUps.spawn(m.pos.x, m.pos.y, "entanglement", false); } else { spawn.setSpawnList(); //picks a couple mobs types for a themed random mob spawns // spawn.pickList = ["focuser", "focuser"] level[level.levels[level.onLevel]](); //picks the current map from the the levels array if (!simulation.isCheating && !build.isExperimentRun && !simulation.isTraining) { localSettings.runCount += level.levelsCleared //track the number of total runs locally localSettings.levelsClearedLastGame = level.levelsCleared if (localSettings.isAllowed) localStorage.setItem("localSettings", JSON.stringify(localSettings)); //update local storage } } if (!simulation.isTraining) level.levelAnnounce(); simulation.noCameraScroll(); simulation.setZoom(); level.addToWorld(); //add bodies to game engine simulation.draw.setPaths(); b.respawnBots(); m.resetHistory(); if (tech.isForeverDrones) { if (tech.isDroneRadioactive) { for (let i = 0; i < tech.isForeverDrones * 0.25; i++) { b.droneRadioactive({ x: m.pos.x + 30 * (Math.random() - 0.5), y: m.pos.y + 30 * (Math.random() - 0.5) }, 5) bullet[bullet.length - 1].endCycle = Infinity } } else { for (let i = 0; i < tech.isForeverDrones; i++) { b.drone({ x: m.pos.x + 30 * (Math.random() - 0.5), y: m.pos.y + 30 * (Math.random() - 0.5) }, 5) bullet[bullet.length - 1].endCycle = Infinity } } } if (tech.isMACHO) spawn.MACHO() for (let i = 0; i < tech.wimpCount; i++) { spawn.WIMP() mob[mob.length - 1].isDecoupling = true //so you can find it to remove for (let j = 0, len = 4; j < len; j++) powerUps.spawn(level.exit.x + 100 * (Math.random() - 0.5), level.exit.y - 100 + 100 * (Math.random() - 0.5), "research", false) } // if (tech.isFlipFlopLevelReset && !tech.isFlipFlopOn) { if ((tech.isRelay || tech.isFlipFlop) && !tech.isFlipFlopOn) { tech.isFlipFlopOn = true if (tech.isFlipFlopHealth) m.setMaxHealth() if (tech.isRelayEnergy) m.setMaxEnergy() m.eyeFillColor = m.fieldMeterColor simulation.makeTextLog(`tech.isFlipFlopOn = true`); } // if (m.plasmaBall) m.plasmaBall.reset() if (m.plasmaBall) m.plasmaBall.fire() if (localSettings.entanglement && localSettings.entanglement.levelName === level.levels[level.onLevel]) { const flip = localSettings.entanglement.isHorizontalFlipped === simulation.isHorizontalFlipped ? 1 : -1 powerUps.directSpawn(flip * localSettings.entanglement.position.x, localSettings.entanglement.position.y, "entanglement", false); } level.newLevelOrPhase() }, newLevelOrPhase() { //runs on each new level but also on final boss phases //used for generalist and pigeonhole principle tech.buffedGun++ if (tech.buffedGun > b.inventory.length - 1) tech.buffedGun = 0; if (tech.isGunCycle && b.activeGun !== null && b.inventory.length) { b.inventoryGun = tech.buffedGun; simulation.switchGun(); } if (tech.isGunChoice && Number.isInteger(tech.buffedGun) && b.inventory.length) { var gun = b.guns[b.inventory[tech.buffedGun]].name simulation.makeTextLog(`pigeonhole principle: +${(31 * Math.max(0, b.inventory.length)).toFixed(0)}% damage for ${gun}`, 600); } if (tech.isSwitchReality && level.levelsCleared !== 0) { simulation.makeTextLog(`simulation.amplitude = ${Math.random()}`); m.switchWorlds() simulation.trails() powerUps.spawn(player.position.x + Math.random() * 50, player.position.y - Math.random() * 50, "tech", false); } if (tech.isHealLowHealth) { if (tech.isEnergyHealth) { var len = 4 * (1 - m.energy / m.maxEnergy) //as a percent } else { var len = 4 * (1 - m.health / m.maxHealth) //as a percent } for (let i = 0; i < len; i++) powerUps.spawn(player.position.x + 90 * (Math.random() - 0.5), player.position.y + 90 * (Math.random() - 0.5), "heal", false); } }, trainingText(say) { simulation.lastLogTime = 0; //clear previous messages simulation.isTextLogOpen = true simulation.makeTextLog(`supervised.learning(${(Date.now() / 1000).toFixed(0)} s):
${say}
`, Infinity) simulation.isTextLogOpen = false // lore.trainer.text("Wow. Just a platform.") }, trainingBackgroundColor: "#e1e1e1", custom() { }, customTopLayer() { }, setDifficulty() { simulation.difficulty = 0 m.dmgScale = 1; //damage done by player decreases each level simulation.accelScale = 1 //mob acceleration increases each level simulation.CDScale = 1 //mob CD time decreases each level simulation.dmgScale = Math.max(0.1, 0.29 * simulation.difficulty) //damage done by mobs scales with total levels simulation.healScale = 1 / (1 + simulation.difficulty * 0.045) //a higher denominator makes for lower heals // m.health += heal * simulation.healScale; }, difficultyIncrease(num = 1) { for (let i = 0; i < num; i++) { simulation.difficulty++ m.dmgScale *= 0.905; //damage done by player decreases each level if (simulation.accelScale < 6) simulation.accelScale *= 1.024 //mob acceleration increases each level if (simulation.CDScale > 0.15) simulation.CDScale *= 0.964 //mob CD time decreases each level } simulation.dmgScale = Math.max(0.1, 0.285 * simulation.difficulty) //damage done by mobs scales with total levels simulation.healScale = 1 / (1 + simulation.difficulty * 0.045) //a higher denominator makes for lower heals // m.health += heal * simulation.healScale; // console.log(`CD = ${simulation.CDScale}`) }, difficultyDecrease(num = 1) { //used in easy mode for simulation.reset() for (let i = 0; i < num; i++) { simulation.difficulty-- m.dmgScale /= 0.905; //damage done by player decreases each level if (simulation.accelScale > 1) simulation.accelScale /= 1.024 //mob acceleration increases each level if (simulation.CDScale < 1) simulation.CDScale /= 0.964 //mob CD time decreases each level } if (simulation.difficulty < 1) simulation.difficulty = 0; simulation.dmgScale = Math.max(0.1, 0.285 * simulation.difficulty) //damage done by mobs scales with total levels simulation.healScale = 1 / (1 + simulation.difficulty * 0.045) }, difficultyText() { if (simulation.difficultyMode === 1) { return "easy" } else if (simulation.difficultyMode === 2) { return "normal" } else if (simulation.difficultyMode === 4) { return "hard" } else if (simulation.difficultyMode === 6) { return "why" } }, levelAnnounce() { const difficulty = simulation.isCheating ? "testing" : level.difficultyText() if (level.levelsCleared === 0) { document.title = "n-gon: (" + difficulty + ")"; } else { document.title = `n-gon: ${level.levelsCleared} ${level.levels[level.onLevel]} (${difficulty})` simulation.makeTextLog(`level.onLevel = "${level.levels[level.onLevel]}"`); } // simulation.makeTextLog(` // input.key.up = ["${input.key.up}", "ArrowUp"] //
input.key.left = ["${input.key.left}", "ArrowLeft"] //
input.key.down = ["${input.key.down}", "ArrowDown"] //
input.key.right = ["${input.key.right}", "ArrowRight"] //
//
m.fieldMode = "${m.fieldUpgrades[m.fieldMode].name}" //
input.key.field = ["${input.key.field}", "right mouse"] //
m.field.description = "${m.fieldUpgrades[m.fieldMode].description}" // `, 1200); }, disableExit: false, nextLevel() { if (!level.disableExit) { level.levelsCleared++; level.onLevel++; //cycles map to next level if (simulation.isTraining) { if (level.onLevel > level.levels.length - 1) { level.disableExit = true document.getElementById("health").style.display = "none" document.getElementById("health-bg").style.display = "none" document.getElementById("defense-bar").style.display = "none" document.getElementById("damage-bar").style.display = "none" document.getElementById("text-log").style.display = "none" document.getElementById("fade-out").style.opacity = 1; //slowly fades out setTimeout(function () { simulation.paused = true; level.disableExit = false; engine.world.bodies.forEach((body) => { Matter.Composite.remove(engine.world, body) }) Engine.clear(engine); simulation.splashReturn(); }, 6000); return } else { level.setDifficulty() } } else { if (level.onLevel > level.levels.length - 1) level.onLevel = 0; level.difficultyIncrease(simulation.difficultyMode) } //reset lost tech display for (let i = 0; i < tech.tech.length; i++) { if (tech.tech[i].isLost) tech.tech[i].isLost = false; } tech.isDeathAvoidedThisLevel = false; simulation.updateTechHUD(); simulation.clearNow = true; //triggers in simulation.clearMap to remove all physics bodies and setup for new map } }, populateLevels() { //run a second time if URL is loaded if (document.getElementById("banned").value) { //remove levels from ban list in settings const banList = document.getElementById("banned").value.replace(/,/g, ' ').replace(/\s\s+/g, ' ').replace(/[^\w\s]/g, '') //replace commas with spaces, replace double spaces with single, remove strange symbols const remove = banList.split(" "); // console.log('remove these', remove) // console.log('community levels before', level.communityLevels) for (let i = 0; i < remove.length; i++) { const index = level.communityLevels.indexOf(remove[i]) if (index !== -1) { level.communityLevels.splice(index, 1); // console.log('removed level:', remove[i]) requestAnimationFrame(() => { simulation.makeTextLog(`banned level: ${remove[i]}`); }); } } // console.log('community levels after', level.communityLevels) // console.log('Landgreen levels before', level.playableLevels) for (let i = 0; i < remove.length; i++) { if (level.playableLevels.length + level.communityLevels.length * simulation.isCommunityMaps < 10) break //can't remove too many levels const index = level.playableLevels.indexOf(remove[i]) if (index !== -1) { level.playableLevels.splice(index, 1); // console.log('removed level:', remove[i]) requestAnimationFrame(() => { simulation.makeTextLog(`banned level: ${remove[i]}`); }); } } // console.log('Landgreen levels after', level.playableLevels) } if (document.getElementById("seed").value) { //check for player entered seed in settings Math.initialSeed = String(document.getElementById("seed").value) Math.seed = Math.abs(Math.hash(Math.initialSeed)) //update randomizer seed in case the player changed it } if (simulation.isTraining) { simulation.isHorizontalFlipped = false level.levels = level.trainingLevels.slice(0) //copy array, not by just by assignment } else { //add remove and shuffle levels for the normal game (not training levels) level.levels = level.playableLevels.slice(0) //copy array, not by just by assignment if (simulation.isCommunityMaps) { level.levels = level.levels.concat(level.communityLevels) simulation.isHorizontalFlipped = false; } else { simulation.isHorizontalFlipped = (Math.seededRandom() < 0.5) ? true : false //if true, some maps are flipped horizontally } 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 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("intro"); //add level to the start of the randomized levels list level.levels.push("subway"); //add level to the end of the randomized levels list level.levels.push("final"); //add level to the end of the randomized levels list } } //set seeded random lists of mobs and bosses spawn.mobTypeSpawnOrder = [] for (let i = 0; i < level.levels.length; i++) spawn.mobTypeSpawnOrder.push(spawn.fullPickList[Math.floor(Math.seededRandom(0, spawn.fullPickList.length))]) spawn.bossTypeSpawnOrder = [] for (let i = 0; i < level.levels.length * 2; i++) spawn.bossTypeSpawnOrder.push(spawn.randomBossList[Math.floor(Math.seededRandom(0, spawn.randomBossList.length))]) }, flipHorizontal() { const flipX = (who) => { for (let i = 0, len = who.length; i < len; i++) { Matter.Body.setPosition(who[i], { x: -who[i].position.x, y: who[i].position.y }) } } flipX(map) flipX(body) flipX(mob) flipX(powerUp) for (let i = 0, len = cons.length; i < len; i++) { cons[i].pointA.x *= -1 cons[i].pointB.x *= -1 } for (let i = 0, len = consBB.length; i < len; i++) { consBB[i].pointA.x *= -1 consBB[i].pointB.x *= -1 } level.exit.x = -level.exit.x - 100 //minus the 100 because of the width of the graphic }, exitCount: 0, // playerExitCheck() { // if ( // player.position.x > level.exit.x && // player.position.x < level.exit.x + 100 && // player.position.y > level.exit.y - 150 && // player.position.y < level.exit.y - 40 && // player.velocity.y < 0.1 // ) { // level.exitCount++ // if (level.exitCount > 120) { // level.exitCount = 0 // level.nextLevel() // } // } // }, setPosToSpawn(xPos, yPos) { m.spawnPos.x = m.pos.x = xPos; m.spawnPos.y = m.pos.y = yPos; level.enter.x = m.spawnPos.x - 50; level.enter.y = m.spawnPos.y + 20; m.transX = m.transSmoothX = canvas.width2 - m.pos.x; m.transY = m.transSmoothY = canvas.height2 - m.pos.y; m.Vx = m.spawnVel.x; m.Vy = m.spawnVel.y; player.force.x = 0; player.force.y = 0; Matter.Body.setPosition(player, m.spawnPos); Matter.Body.setVelocity(player, m.spawnVel); //makes perfect diamagnetism tech: Lenz's law show up in the right spot at the start of a level m.fieldPosition = { x: m.pos.x, y: m.pos.y } m.fieldAngle = m.angle }, enter: { x: 0, y: 0, draw() { 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(); } }, exit: { x: 0, y: 0, drawAndCheck() { if ( //check player.position.x > level.exit.x && player.position.x < level.exit.x + 100 && player.position.y > level.exit.y - 150 && player.position.y < level.exit.y - 0 && player.velocity.y < 0.15 ) { // level.exitCount += input.down ? 8 : 2 level.exitCount += 2 } else if (level.exitCount > 0) { level.exitCount -= 2 } 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(); if (level.exitCount > 0) { //stroke outline of door from 2 sides, grows with count ctx.beginPath(); ctx.moveTo(level.exit.x, level.exit.y + 40); ctx.lineTo(level.exit.x, level.exit.y - 80); ctx.bezierCurveTo(level.exit.x, level.exit.y - 148, level.exit.x + 50, level.exit.y - 148, level.exit.x + 50, level.exit.y - 148); ctx.moveTo(level.exit.x + 100, level.exit.y + 40); ctx.lineTo(level.exit.x + 100, level.exit.y - 80); ctx.bezierCurveTo(level.exit.x + 100, level.exit.y - 148, level.exit.x + 50, level.exit.y - 148, level.exit.x + 50, level.exit.y - 148); ctx.setLineDash([200, 200]); ctx.lineDashOffset = Math.max(-15, 185 - 2.1 * level.exitCount) ctx.strokeStyle = "#444" ctx.lineWidth = 2 ctx.stroke(); ctx.setLineDash([0, 0]); if (level.exitCount > 100) { level.exitCount = 0 level.nextLevel() } } }, // draw() { // 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(); // } }, addToWorld() { //needs to be run to put bodies into the world for (let i = 0; i < map.length; i++) { map[i].collisionFilter.category = cat.map; map[i].collisionFilter.mask = cat.player | cat.map | cat.body | cat.bullet | cat.powerUp | cat.mob | cat.mobBullet; Matter.Body.setStatic(map[i], true); //make static Composite.add(engine.world, map[i]); //add to world } }, spinner(x, y, width, height, density = 0.001, angle = 0, frictionAir = 0.001, angularVelocity = 0) { x += width / 2 y += height / 2 const who = body[body.length] = Bodies.rectangle(x, y, width, height, { collisionFilter: { category: cat.body, mask: cat.player | cat.body | cat.bullet | cat.powerUp | cat.mob | cat.mobBullet }, isNotHoldable: true, frictionAir: frictionAir, friction: 1, frictionStatic: 1, restitution: 0, }); Matter.Body.setAngle(who, angle) Matter.Body.setAngularVelocity(who, angularVelocity); Matter.Body.setDensity(who, density) Composite.add(engine.world, who); //add to world who.classType = "body" const constraint = Constraint.create({ //fix rotor in place, but allow rotation pointA: { x: who.position.x, y: who.position.y }, bodyB: who, stiffness: 1, damping: 1 }); Composite.add(engine.world, constraint); return constraint }, rotor(x, y, width, height, density = 0.001, angle = 0, frictionAir = 0.001, angularVelocity = 0, rotationForce = 0.0005) { x += width / 2 y += height / 2 const who = body[body.length] = Bodies.rectangle(x, y, width, height, { collisionFilter: { category: cat.body, mask: cat.player | cat.body | cat.bullet | cat.powerUp | cat.mob | cat.mobBullet }, isNotHoldable: true, frictionAir: frictionAir, friction: 1, frictionStatic: 1, restitution: 0, rotationForce: rotationForce }); Matter.Body.setAngle(who, angle) Matter.Body.setAngularVelocity(who, angularVelocity); Matter.Body.setDensity(who, density) const constraint = Constraint.create({ //fix rotor in place, but allow rotation pointA: { x: who.position.x, y: who.position.y }, bodyB: who, stiffness: 1, damping: 1 }); Composite.add(engine.world, constraint); who.center = { x: who.position.x, y: who.position.y } who.rotate = function () { if (!m.isBodiesAsleep) { Matter.Body.applyForce(this, { x: this.position.x + 100, y: this.position.y + 100 }, { x: this.rotationForce * this.mass, y: 0 }) } else { Matter.Body.setAngularVelocity(this, 0); } } // if (rotate) { // rotor.rotate = function() { // if (!m.isBodiesAsleep) { // Matter.Body.applyForce(rotor, { // x: rotor.position.x + 100, // y: rotor.position.y + 100 // }, { // x: rotate * rotor.mass, // y: 0 // }) // } else { // Matter.Body.setAngularVelocity(rotor, 0); // } // } // } Composite.add(engine.world, who); //add to world who.classType = "body" return who }, boost(x, y, height = 1000) { //height is how high the player will be flung above y who = map[map.length] = Matter.Bodies.fromVertices(x + 50, y + 35, Vertices.fromPath("120 40 -120 40 -50 -40 50 -40"), { collisionFilter: { 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 }, boostBounds: { min: { x: x, y: y - 20 }, max: { x: x + 100, y: y } }, yVelocity: -1.21 * Math.sqrt(Math.abs(height)), query() { // check for collisions query = (who) => { if (Matter.Query.region(who, this.boostBounds).length > 0) { list = Matter.Query.region(who, this.boostBounds) Matter.Body.setVelocity(list[0], { x: list[0].velocity.x + (Math.random() - 0.5) * 2.5, //add a bit of horizontal drift to reduce endless bounces y: this.yVelocity //give a upwards velocity }); } } query(body) query(mob) query(bullet) query(powerUp) //player collision if (Matter.Query.region([player], this.boostBounds).length > 0 && !input.down) { m.buttonCD_jump = 0; // reset short jump counter to prevent short jumps on boosts m.hardLandCD = 0 // disable hard landing if (player.velocity.y > 26) { Matter.Body.setVelocity(player, { x: player.velocity.x, y: -15 //gentle bounce if coming down super fast }); } else { Matter.Body.setVelocity(player, { x: player.velocity.x + (Math.random() - 0.5) * 2.5, y: this.yVelocity //give a upwards velocity that will put the player that the height desired }); } } //draw ctx.fillStyle = "rgba(200,0,255,0.15)"; ctx.fillRect(this.boostBounds.min.x, this.boostBounds.min.y - 10, 100, 30); ctx.fillStyle = "rgba(200,0,255,0.05)"; ctx.fillRect(this.boostBounds.min.x, this.boostBounds.min.y - 50, 100, 70); // ctx.fillStyle = "rgba(200,0,255,0.02)"; // ctx.fillRect(x, y - 120, 100, 120); }, }); return who }, elevator(x, y, width, height, maxHeight, force = 0.003, friction = { up: 0.01, down: 0.2 }, isAtTop = false) { x += width / 2 y += height / 2 maxHeight += height / 2 const yTravel = maxHeight - y force += simulation.g const who = body[body.length] = Bodies.rectangle(x, isAtTop ? maxHeight : y, width, height, { collisionFilter: { category: cat.body, //cat.map, mask: cat.map | cat.player | cat.body | cat.bullet | cat.powerUp | cat.mob | cat.mobBullet }, inertia: Infinity, //prevents rotation isNotHoldable: true, friction: 1, frictionStatic: 1, restitution: 0, frictionAir: 0.001, holdX: x, move() { if (!m.isBodiesAsleep) { if (this.isUp) { //moving up still with high air friction this.force.y -= force * this.mass //hard force propels up, even with high friction if (this.position.y < maxHeight) { //switch to down mode this.isUp = false this.frictionAir = friction.down //adds a hard jerk at the top of vertical motion because it's fun Matter.Body.setPosition(this, { x: this.holdX, y: maxHeight }); Matter.Body.setVelocity(this, { x: 0, y: 0 }); } } else if (this.position.y + 10 * this.velocity.y > y) { //free falling down, with only air friction Matter.Body.setVelocity(this, { //slow down early to avoid a jerky stop that can pass through blocks x: 0, y: this.velocity.y * 0.7 }); if (this.position.y + this.velocity.y > y) { //switch to up mode this.isUp = true this.frictionAir = friction.up } } Matter.Body.setVelocity(this, { x: 0, y: this.velocity.y }); } //edge limits if (this.position.y < maxHeight) { Matter.Body.setPosition(this, { x: this.holdX, y: maxHeight }); } else if (this.position.y > y) { Matter.Body.setPosition(this, { x: this.holdX, y: y }); } // hold horizontal position Matter.Body.setPosition(this, { x: this.holdX, y: this.position.y }); }, off() { Matter.Body.setPosition(this, { x: this.holdX, y: this.position.y }); Matter.Body.setVelocity(this, { x: 0, y: this.velocity.y }); }, constraint: this.null, addConstraint() { this.constraint = Constraint.create({ pointA: { x: this.position.x, y: this.position.y }, bodyB: this, stiffness: 0.01, damping: 0.3 }); Composite.add(engine.world, this.constraint); }, removeConstraint() { Composite.remove(engine.world, this.constraint, true) }, drawTrack() { ctx.fillStyle = "#ccc" ctx.fillRect(this.holdX, y, 5, yTravel) } }); Matter.Body.setDensity(who, 0.01) //10x density for added stability Composite.add(engine.world, who); //add to world who.classType = "body" return who }, spring(x, y, v = "-100 0 100 0 70 40 0 50 -70 40", force = 0.01, distance = 300, angle = 0) { const who = body[body.length] = Matter.Bodies.fromVertices(x, y, Vertices.fromPath(v), { collisionFilter: { 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 }, inertia: Infinity, //prevents rotation isNotHoldable: true, friction: 1, frictionStatic: 1, restitution: 0, frictionAir: 1, density: 0.1, isReady: true, isResetting: false, query() { if (this.isReady) { if (Matter.Query.collides(this, [player]).length) { this.isReady = false this.constraint.stiffness = 0 this.constraint.damping = 0 //0.3 this.frictionAir = 0 Matter.Body.setVelocity(this, { x: 0, y: 0 }); //show graphically being ready? } } else { if (this.isResetting) { this.constraint.stiffness += 0.0005 if (this.constraint.stiffness > 0.1) { this.isResetting = false this.isReady = true } } else { if (Vector.magnitudeSquared(Vector.sub(this.position, { x: x, y: y })) < distance * distance) { this.force.y -= force * this.mass } else { this.constraint.damping = 1 this.frictionAir = 1 this.isResetting = true Matter.Body.setVelocity(this, { x: 0, y: 0 }); } } } } }); who.constraint = Constraint.create({ pointA: { x: who.position.x, y: who.position.y }, bodyB: who, stiffness: 1, damping: 1 }); Composite.add(engine.world, who.constraint); return who }, // rotor(x, y, rotate = 0, radius = 800, width = 40, density = 0.0005) { // const rotor1 = Matter.Bodies.rectangle(x, y, width, radius, { // density: density, // isNotHoldable: true, // isNonStick: true, // collisionFilter: { // category: cat.map, // mask: cat.player | cat.body | cat.bullet | cat.powerUp | cat.mob | cat.mobBullet // }, // }); // const rotor2 = Matter.Bodies.rectangle(x, y, width, radius, { // angle: Math.PI / 2, // density: density, // isNotHoldable: true, // isNonStick: true, // collisionFilter: { // category: cat.map, // mask: cat.player | cat.body | cat.bullet | cat.powerUp | cat.mob | cat.mobBullet // }, // }); // rotor = Body.create({ //combine rotor1 and rotor2 // parts: [rotor1, rotor2], // restitution: 0, // collisionFilter: { // category: cat.map, // mask: cat.player | cat.body | cat.bullet | cat.powerUp | cat.mob | cat.mobBullet // }, // }); // Matter.Body.setPosition(rotor, { // x: x, // y: y // }); // Composite.add(engine.world, [rotor]); // body[body.length] = rotor1 // body[body.length] = rotor2 // // setTimeout(function() { // // rotor.collisionFilter.category = cat.body; // // rotor.collisionFilter.mask = cat.body | cat.player | cat.bullet | cat.mob | cat.mobBullet //| cat.map // // }, 1000); // const constraint = Constraint.create({ //fix rotor in place, but allow rotation // pointA: { // x: x, // y: y // }, // bodyB: rotor // }); // Composite.add(engine.world, constraint); // if (rotate) { // rotor.rotate = function() { // if (!m.isBodiesAsleep) { // Matter.Body.applyForce(rotor, { // x: rotor.position.x + 100, // y: rotor.position.y + 100 // }, { // x: rotate * rotor.mass, // y: 0 // }) // } else { // Matter.Body.setAngularVelocity(rotor, 0); // } // } // } // composite[composite.length] = rotor // return rotor // }, toggle(x, y, isOn = false, isLockOn = false) { spawn.mapVertex(x + 65, y + 2, "70 10 -70 10 -40 -10 40 -10"); //toggle platform map[map.length - 1].restitution = 0; map[map.length - 1].friction = 1; // map[map.length - 1].frictionStatic = 1; const width = 120 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.collisionFilter.category = cat.body flip.collisionFilter.mask = cat.player | cat.body flip.isNotHoldable = true flip.restitution = 0 Matter.Body.setDensity(flip, 0.003) if (isOn) { Matter.Body.setAngle(flip, (0.25 - 0.5) * Math.PI) } else { Matter.Body.setAngle(flip, (-0.25 - 0.5) * Math.PI) } cons[cons.length] = Constraint.create({ pointA: { x: x + 65, y: y - 5 }, bodyB: flip, stiffness: 1, length: 0 }); Composite.add(engine.world, [cons[cons.length - 1]]); Composite.add(engine.world, flip); //add to world flip.classType = "body" return { flip: flip, isOn: isOn, query() { const limit = { right: (-0.25 - 0.5) * Math.PI, left: (0.25 - 0.5) * Math.PI } if (flip.angle < limit.right) { Matter.Body.setAngle(flip, limit.right) Matter.Body.setAngularVelocity(flip, 0); if (!isLockOn) this.isOn = false } else if (flip.angle > limit.left) { Matter.Body.setAngle(flip, limit.left) Matter.Body.setAngularVelocity(flip, 0); this.isOn = true } if (this.isOn) { ctx.beginPath(); ctx.moveTo(flip.vertices[0].x, flip.vertices[0].y); for (let j = 1; j < flip.vertices.length; j++) { ctx.lineTo(flip.vertices[j].x, flip.vertices[j].y); } ctx.lineTo(flip.vertices[0].x, flip.vertices[0].y); ctx.fillStyle = "#3df" ctx.fill(); ctx.lineWidth = 1; ctx.strokeStyle = color.blockS; ctx.stroke(); } }, } }, 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 // }); return { isUp: false, min: { x: x + 2, y: y - 11 }, max: { x: x + width, y: y - 10 }, width: width, height: 20, 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; } }, 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) { ctx.fillRect(this.min.x, this.min.y - 10, this.width, 20) } else { ctx.fillRect(this.min.x, this.min.y - 3, this.width, 25) } } } }, vanish(x, y, width, height, isVertical = false, hide = { x: 0, y: 150 }) { x = x + width / 2 y = y + height / 2 const vertices = [{ x: x, y: y, index: 0, isInternal: false }, { x: x + width, y: y, index: 1, isInternal: false }, { x: x + width, y: y + height, index: 4, isInternal: false }, { x: x, y: y + height, index: 3, isInternal: false }] const block = body[body.length] = Bodies.fromVertices(x, y, vertices, { // const block = body[body.length] = Bodies.rectangle(x, y, width, height, { collisionFilter: { category: cat.map, mask: cat.player | cat.body | cat.bullet | cat.powerUp | cat.mob | cat.mobBullet }, inertia: Infinity, //prevents rotation isNotHoldable: true, isNonStick: true, //this keep sporangium from sticking isTouched: false, fadeTime: 10 + Math.ceil(0.25 * width), fadeCount: null, isThere: true, returnTime: 120, returnCount: 0, shrinkVertices(size) { if (isVertical) { return [{ x: x, y: y * size, index: 0, isInternal: false }, { x: x + width, y: y * size, index: 1, isInternal: false }, { x: x + width, y: (y + height) * size, index: 4, isInternal: false }, { x: x, y: (y + height) * size, index: 3, isInternal: false }] } else { return [{ x: x * size, y: y, index: 0, isInternal: false }, { x: (x + width) * size, y: y, index: 1, isInternal: false }, { x: (x + width) * size, y: y + height, index: 4, isInternal: false }, { x: x * size, y: y + height, index: 3, isInternal: false }] } }, query() { if (this.isThere) { if (this.isTouched) { if (!m.isBodiesAsleep) { this.fadeCount-- Matter.Body.setVertices(this, this.shrinkVertices(Math.max(this.fadeCount / this.fadeTime, 0.03))) } if (this.fadeCount < 1) { Matter.Body.setPosition(this, hide) this.isThere = false this.isTouched = false this.collisionFilter.mask = 0 //cat.player | cat.body | cat.bullet | cat.powerUp | cat.mob | cat.mobBullet this.returnCount = this.returnTime Matter.Body.setVertices(this, this.shrinkVertices(1)) Matter.Body.setVertices(this, vertices) } } else if (Matter.Query.collides(this, [player]).length) { // || (Matter.Query.collides(this, body).length)) { this.isTouched = true this.fadeCount = this.fadeTime; } } else { if (!m.isBodiesAsleep) { this.returnCount-- if (this.returnCount < 1) { Matter.Body.setPosition(this, { x: x, y: y }) if (Matter.Query.collides(this, [player]).length) { //|| (Matter.Query.collides(this, body).length)) { Matter.Body.setPosition(this, hide) this.returnCount = 15 } else { this.isThere = true this.collisionFilter.mask = cat.player | cat.mob | cat.body | cat.bullet | cat.powerUp | cat.mobBullet this.fadeCount = this.fadeTime //delete any overlapping blocks const blocks = Matter.Query.collides(this, body) for (let i = 0; i < blocks.length; i++) { if (blocks[i].bodyB !== this && blocks[i].bodyB !== m.holdingTarget) { //dont' delete yourself <----- bug here maybe... Matter.Composite.remove(engine.world, blocks[i].bodyB); blocks[i].bodyB.isRemoveMeNow = true for (let i = 1; i < body.length; i++) { //find which index in body array it is and remove from array if (body[i].isRemoveMeNow) { body.splice(i, 1); break } } } } //delete any overlapping mobs // const mobsHits = Matter.Query.collides(this, mob) // for (let i = 0; i < mobsHits.length; i++) { // if (mobsHits[i].bodyB !== this && mobsHits[i].bodyB !== m.holdingTarget) { //dont' delete yourself <----- bug here maybe... // Matter.Composite.remove(engine.world, mobsHits[i].bodyB); // mobsHits[i].bodyB.isRemoveMeNow = true // for (let i = 1; i < mob.length; i++) { //find which index in body array it is and remove from array // if (mob[i].isRemoveMeNow) { // mob.splice(i, 1); // break // } // } // } // } } } } } 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.fillStyle = "#586370" ctx.fill(); // const color = 220 * (1 - this.fadeCount / this.fadeTime) // ctx.fillStyle = `rgb(${color},220, 200)` // ctx.fillStyle = `rgba(0,220,200,${this.fadeCount/this.fadeTime+0.05})` // ctx.strokeStyle = `#bff` // ctx.stroke(); }, }); Matter.Body.setStatic(block, true); //make static Composite.add(engine.world, block); //add to world // who.classType = "body" if (simulation.isHorizontalFlipped) x *= -1 return block }, door(x, y, width, height, distance, speed = 1) { x = x + width / 2 y = y + height / 2 const doorBlock = body[body.length] = Bodies.rectangle(x, y, width, height, { collisionFilter: { 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 }, inertia: Infinity, //prevents rotation isNotHoldable: true, friction: 1, frictionStatic: 1, restitution: 0, isClosing: false, openClose() { 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) } } } 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) } } } }, 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(doorBlock, true); //make static Composite.add(engine.world, doorBlock); //add to world 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() simulation.draw.lineOfSightPrecalculation() //required precalculation for line of sight } } } } 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() simulation.draw.lineOfSightPrecalculation() //required precalculation for line of sight } } } } }, 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 const mapWidth = 200 const unitA = Matter.Vector.rotate({ x: 1, y: 0 }, angleA) const unitB = Matter.Vector.rotate({ x: 1, y: 0 }, angleB) draw = function () { ctx.beginPath(); //portal let 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.fillStyle = this.color ctx.fill(); } query = function (isRemoveBlocks = false) { if (Matter.Query.collides(this, [player]).length === 0) { //not touching player if (player.isInPortal === this) player.isInPortal = null } else if (player.isInPortal !== this) { //touching player if (m.buttonCD_jump === m.cycle) player.force.y = 0 // undo a jump right before entering the portal m.buttonCD_jump = 0 //disable short jumps when letting go of jump key player.isInPortal = this.portalPair //teleport if (this.portalPair.angle % (Math.PI / 2)) { //if left, right up or down if (m.immuneCycle < m.cycle + m.collisionImmuneCycles) m.immuneCycle = m.cycle + m.collisionImmuneCycles; //player is immune to damage for 30 cycles Matter.Body.setPosition(player, this.portalPair.portal.position); } else { //if at some odd angle if (m.immuneCycle < m.cycle + m.collisionImmuneCycles) m.immuneCycle = m.cycle + m.collisionImmuneCycles; //player is immune to damage for 30 cycles Matter.Body.setPosition(player, this.portalPair.position); } //rotate velocity let mag if (this.portalPair.angle !== 0 && this.portalPair.angle !== Math.PI) { //portal that fires the player up mag = Math.max(10, Math.min(50, player.velocity.y * 0.8)) + 11 } else { mag = Math.max(6, Math.min(50, Vector.magnitude(player.velocity))) } let v = Vector.mult(this.portalPair.unit, mag) Matter.Body.setVelocity(player, v); // move bots to player for (let i = 0; i < bullet.length; i++) { if (bullet[i].botType) { // Matter.Body.setPosition(bullet[i], this.portalPair.portal.position); Matter.Body.setPosition(bullet[i], Vector.add(this.portalPair.portal.position, { x: 250 * (Math.random() - 0.5), y: 250 * (Math.random() - 0.5) })); Matter.Body.setVelocity(bullet[i], { x: 0, y: 0 }); } } if (tech.isHealAttract) { //send heals to next portal for (let i = 0; i < powerUp.length; i++) { if (powerUp[i].name === "heal" && Vector.magnitudeSquared(Vector.sub(powerUp[i].position, m.pos)) < 1000000) { Matter.Body.setPosition(powerUp[i], Vector.add(this.portalPair.portal.position, { x: 500 * (Math.random() - 0.5), y: 500 * (Math.random() - 0.5) })); } } } } // if (body.length) { for (let i = 0, len = body.length; i < len; i++) { if (body[i] !== m.holdingTarget) { // body[i].bounds.max.x - body[i].bounds.min.x < 100 && body[i].bounds.max.y - body[i].bounds.min.y < 100 if (Matter.Query.collides(this, [body[i]]).length === 0) { if (body[i].isInPortal === this) body[i].isInPortal = null } else if (body[i].isInPortal !== this) { //touching this portal, but for the first time if (isRemoveBlocks) { Matter.Composite.remove(engine.world, body[i]); body.splice(i, 1); break } body[i].isInPortal = this.portalPair //teleport if (this.portalPair.angle % (Math.PI / 2)) { //if left, right up or down Matter.Body.setPosition(body[i], this.portalPair.portal.position); } else { //if at some odd angle Matter.Body.setPosition(body[i], this.portalPair.position); } //rotate velocity let mag if (this.portalPair.angle !== 0 && this.portalPair.angle !== Math.PI) { //portal that fires the player up mag = Math.max(10, Math.min(50, body[i].velocity.y * 0.8)) + 11 } else { mag = Math.max(6, Math.min(50, Vector.magnitude(body[i].velocity))) } let v = Vector.mult(this.portalPair.unit, mag) Matter.Body.setVelocity(body[i], v); } } } // } //remove block if touching // if (body.length) { // touching = Matter.Query.collides(this, body) // for (let i = 0; i < touching.length; i++) { // 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; // } // } // } // } // } // if (touching.length !== 0 && touching[0].bodyB !== m.holdingTarget) { // if (body.length) { // for (let i = 0; i < body.length; i++) { // if (body[i] === touching[0].bodyB) { // body.splice(i, 1); // break; // } // } // } // Matter.Composite.remove(engine.world, touching[0].bodyB); // } } const portalA = composite[composite.length] = Bodies.rectangle(centerA.x, centerA.y, width, height, { isSensor: true, angle: angleA, color: "hsla(197, 100%, 50%,0.7)", draw: draw, }); const portalB = composite[composite.length] = Bodies.rectangle(centerB.x, centerB.y, width, height, { isSensor: true, angle: angleB, color: "hsla(29, 100%, 50%, 0.7)", draw: draw }); const mapA = composite[composite.length] = Bodies.rectangle(centerA.x - 0.5 * unitA.x * mapWidth, centerA.y - 0.5 * unitA.y * mapWidth, mapWidth, height + 10, { collisionFilter: { category: cat.map, mask: cat.bullet | cat.powerUp | cat.mob | cat.mobBullet //cat.player | cat.map | cat.body | cat.bullet | cat.powerUp | cat.mob | cat.mobBullet }, unit: unitA, angle: angleA, color: color.map, draw: draw, query: query, lastPortalCycle: 0 }); Matter.Body.setStatic(mapA, true); //make static Composite.add(engine.world, mapA); //add to world const mapB = composite[composite.length] = Bodies.rectangle(centerB.x - 0.5 * unitB.x * mapWidth, centerB.y - 0.5 * unitB.y * mapWidth, mapWidth, height + 10, { collisionFilter: { category: cat.map, mask: cat.bullet | cat.powerUp | cat.mob | cat.mobBullet //cat.player | cat.map | cat.body | cat.bullet | cat.powerUp | cat.mob | cat.mobBullet }, unit: unitB, angle: angleB, color: color.map, draw: draw, query: query, lastPortalCycle: 0, }); Matter.Body.setStatic(mapB, true); //make static Composite.add(engine.world, mapB); //add to world mapA.portal = portalA mapB.portal = portalB mapA.portalPair = mapB mapB.portalPair = mapA return [portalA, portalB, mapA, mapB] }, drip(x, yMin, yMax, period = 100, color = "hsla(160, 100%, 35%, 0.5)") { return { x: x, y: yMin, period: period, dropCycle: 0, speed: 0, draw() { if (!m.isBodiesAsleep) { if (this.dropCycle < simulation.cycle) { //reset this.dropCycle = simulation.cycle + this.period + Math.floor(40 * Math.random()) this.y = yMin this.speed = 1 } else { //fall this.speed += 0.35 //acceleration from gravity this.y += this.speed } } if (this.y < yMax) { //draw ctx.fillStyle = color //"hsla(160, 100%, 35%,0.75)" ctx.beginPath(); ctx.arc(this.x, this.y, 8, 0, 2 * Math.PI); ctx.fill(); } } } }, isHazardRise: false, hazard(x, y, width, height, damage = 0.002) { return { min: { x: x, y: y }, max: { x: x + width, y: y + height }, width: width, height: height, maxHeight: height, isOn: true, opticalQuery() { if (this.isOn) { //draw ctx.fillStyle = `hsla(0, 100%, 50%,${0.6 + 0.4 * Math.random()})` ctx.fillRect(this.min.x, this.min.y, this.width, this.height) //collision with player if (this.height > 0 && Matter.Query.region([player], this).length && !(m.isCloak)) { if (m.immuneCycle < m.cycle) { m.immuneCycle = m.cycle + m.collisionImmuneCycles; m.damage(damage) simulation.drawList.push({ //add dmg to draw queue x: player.position.x, y: player.position.y, radius: damage * 1500, color: simulation.mobDmgColor, time: 20 }); } } } }, query() { if (this.isOn) { ctx.fillStyle = "hsla(160, 100%, 35%,0.75)" const offset = 5 * Math.sin(simulation.cycle * 0.015) ctx.fillRect(this.min.x, this.min.y + offset, this.width, this.height - offset) if (this.height > 0 && Matter.Query.region([player], this).length) { if (m.immuneCycle < m.cycle) { const DRAIN = 0.004 * (tech.isRadioactiveResistance ? 0.25 : 1) if (m.energy > DRAIN) { m.energy -= DRAIN if (tech.isEnergyHealth && m.energy < 0) m.death() } else { m.damage(damage * (tech.isRadioactiveResistance ? 0.25 : 1)) } } //float if (player.velocity.y > 5) player.force.y -= 0.95 * player.mass * simulation.g const slowY = (player.velocity.y > 0) ? Math.max(0.8, 1 - 0.002 * player.velocity.y * player.velocity.y) : Math.max(0.98, 1 - 0.001 * Math.abs(player.velocity.y)) //down : up Matter.Body.setVelocity(player, { x: Math.max(0.95, 1 - 0.036 * Math.abs(player.velocity.x)) * player.velocity.x, y: slowY * player.velocity.y }); //undo 1/2 of gravity player.force.y -= 0.5 * player.mass * simulation.g; } //float power ups powerUpCollide = Matter.Query.region(powerUp, this) for (let i = 0, len = powerUpCollide.length; i < len; i++) { const diameter = 2 * powerUpCollide[i].size const buoyancy = 1 - 0.2 * Math.max(0, Math.min(diameter, this.min.y - powerUpCollide[i].position.y + powerUpCollide[i].size)) / diameter powerUpCollide[i].force.y -= buoyancy * 1.24 * powerUpCollide[i].mass * simulation.g; Matter.Body.setVelocity(powerUpCollide[i], { x: powerUpCollide[i].velocity.x, y: 0.97 * powerUpCollide[i].velocity.y }); } } }, // draw() { // if (this.isOn) { // ctx.fillStyle = color // ctx.fillRect(this.min.x, this.min.y, this.width, this.height) // } // }, levelRise(growRate = 1) { if (this.height < this.maxHeight && !m.isBodiesAsleep) { this.height += growRate this.min.y -= growRate this.max.y = this.min.y + this.height } }, levelFall(fallRate = 1) { if (this.height > 0 && !m.isBodiesAsleep) { this.height -= fallRate this.min.y += fallRate this.max.y = this.min.y + this.height } }, level(isFill, growSpeed = 1) { if (!m.isBodiesAsleep) { if (isFill) { if (this.height < this.maxHeight) { this.height += growSpeed this.min.y -= growSpeed this.max.y = this.min.y + this.height } } else if (this.height > 0) { this.height -= growSpeed this.min.y += growSpeed this.max.y = this.min.y + this.height } } } } }, mover(x, y, width, height, VxGoal = -6, force = VxGoal > 0 ? 0.0005 : -0.0005) { //VxGoal below 3 don't move well, maybe try adjusting the force x = x + width / 2 y = y + height / 2 const rect = 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 //cat.player | cat.map | cat.body | cat.bullet | cat.powerUp | cat.mob | cat.mobBullet }, inertia: Infinity, //prevents rotation isNotHoldable: true, friction: 0, frictionStatic: 0, restitution: 0, isClosing: false, isMover: true, VxGoal: VxGoal, force: force, push() { if (!m.isBodiesAsleep) { const touchingPlayer = Matter.Query.collides(this, [jumpSensor]) if (touchingPlayer.length) { m.moverX = this.VxGoal if ((this.VxGoal > 0 && player.velocity.x < this.VxGoal) || (this.VxGoal < 0 && player.velocity.x > this.VxGoal)) { player.force.x += this.force * player.mass } m.Vx = player.velocity.x - this.VxGoal } let pushBlock = (who) => { if (!who.isMover) { if ((this.VxGoal > 0 && who.velocity.x < this.VxGoal) || (this.VxGoal < 0 && who.velocity.x > this.VxGoal)) { who.force.x += this.force * who.mass } const stoppingFriction = 0.5 Matter.Body.setVelocity(who, { x: this.VxGoal * (1 - stoppingFriction) + who.velocity.x * stoppingFriction, y: who.velocity.y }); Matter.Body.setAngularVelocity(who, who.angularVelocity * 0.9) } } const blocks = Matter.Query.collides(this, body) for (let i = 0; i < blocks.length; i++) { pushBlock(blocks[i].bodyA) pushBlock(blocks[i].bodyB) } const mobTargets = Matter.Query.collides(this, mob) for (let i = 0; i < mobTargets.length; i++) { pushBlock(mobTargets[i].bodyA) pushBlock(mobTargets[i].bodyB) } let pushPowerUp = (who) => { if (!who.isMover) { if ((this.VxGoal > 0 && who.velocity.x < this.VxGoal) || (this.VxGoal < 0 && who.velocity.x > this.VxGoal)) { who.force.x += 2 * this.force * who.mass } const stoppingFriction = 0.5 Matter.Body.setVelocity(who, { x: this.VxGoal * (1 - stoppingFriction) + who.velocity.x * stoppingFriction, y: who.velocity.y }); } } const powers = Matter.Query.collides(this, powerUp) for (let i = 0; i < powers.length; i++) { pushPowerUp(powers[i].bodyA) pushPowerUp(powers[i].bodyB) } } }, draw() { ctx.beginPath(); const v = this.vertices; ctx.moveTo(v[0].x + 2, v[0].y); // for (let i = 1; i < v.length; ++i) ctx.lineTo(v[i].x, v[i].y); ctx.lineTo(v[1].x - 2, v[1].y); ctx.strokeStyle = "#000" ctx.lineWidth = 4; ctx.setLineDash([40, 40]); ctx.lineDashOffset = (-simulation.cycle * this.VxGoal) % 80; ctx.stroke(); ctx.setLineDash([0, 0]); } }); Matter.Body.setStatic(rect, true); //make static return rect }, transport(x, y, width, height, VxGoal = -6, force = VxGoal > 0 ? 0.0005 : -0.0005) { //horizontal moving platform //VxGoal below 3 don't move well, maybe try adjusting the force x = x + width / 2 y = y + height / 2 const rect = body[body.length] = Bodies.rectangle(x, y, width, height, { collisionFilter: { 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 }, inertia: Infinity, //prevents rotation isNotHoldable: true, friction: 0, frictionStatic: 0, restitution: 0, isClosing: false, isMover: true, VxGoal: VxGoal, force: force, move() { if (!m.isBodiesAsleep) { Matter.Body.setPosition(this, { x: this.position.x + this.VxGoal, y: this.position.y }); //horizontal movement const touchingPlayer = Matter.Query.collides(this, [jumpSensor]) if (touchingPlayer.length) { m.moverX = this.VxGoal if ((this.VxGoal > 0 && player.velocity.x < this.VxGoal) || (this.VxGoal < 0 && player.velocity.x > this.VxGoal)) { player.force.x += this.force * player.mass } m.Vx = player.velocity.x - this.VxGoal } let pushBlock = (who) => { if (!who.isMover) { if ((this.VxGoal > 0 && who.velocity.x < this.VxGoal) || (this.VxGoal < 0 && who.velocity.x > this.VxGoal)) { who.force.x += this.force * who.mass } const stoppingFriction = 0.5 Matter.Body.setVelocity(who, { x: this.VxGoal * (1 - stoppingFriction) + who.velocity.x * stoppingFriction, y: who.velocity.y }); Matter.Body.setAngularVelocity(who, who.angularVelocity * 0.8) } } const blocks = Matter.Query.collides(this, body) for (let i = 0; i < blocks.length; i++) { pushBlock(blocks[i].bodyA) pushBlock(blocks[i].bodyB) } } }, draw() { 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.fillStyle = "#586370" ctx.fill(); }, changeDirection(isRight) { if (isRight) { this.VxGoal = Math.abs(this.VxGoal) this.force = Math.abs(this.force) if (Matter.Query.collides(this, [jumpSensor]).length) player.force.x += this.trainKickPlayer * this.force * player.mass } else { this.VxGoal = -Math.abs(this.VxGoal) this.force = -Math.abs(this.force) if (Matter.Query.collides(this, [jumpSensor]).length) player.force.x += this.trainKickPlayer * this.force * player.mass } }, trainSpeed: Math.abs(VxGoal), trainKickPlayer: 12 * Math.abs(force), isSensing: false, stops: { left: x, right: x + 1000 }, //this should probably be reset in the level code for the actual train stops trainStop() { if (this.isMoving) { this.move(); //oscillate back and forth if (this.position.x < this.stops.left) {//stop this.VxGoal = this.trainSpeed this.force = 0.0005 this.isMoving = false this.isSensing = false if (Matter.Query.collides(this, [jumpSensor]).length) player.force.x += this.trainKickPlayer * player.mass * (this.VxGoal > 0 ? 1 : -1)//give player a kick so they don't fall off } else if (this.position.x > this.stops.right) {//stop this.VxGoal = -this.trainSpeed this.force = -0.0005 this.isMoving = false this.isSensing = false if (Matter.Query.collides(this, [jumpSensor]).length) player.force.x += this.trainKickPlayer * player.mass * (this.VxGoal > 0 ? 1 : -1)//give player a kick so they don't fall off } } else if (this.isSensing) { if (Matter.Query.collides(this, [jumpSensor]).length) { this.isMoving = true this.move(); //needs to move out of the stop range // if (Matter.Query.collides(this, [jumpSensor]).length) player.force.x += trainKickPlayer * player.mass * (this.VxGoal > 0 ? 1 : -1)//give player a kick so they don't fall off if (Matter.Query.collides(this, [jumpSensor]).length) { Matter.Body.setVelocity(player, { x: this.VxGoal, y: player.velocity.y }); } } else if (this.position.x > this.stops.right && player.position.x < this.stops.left + 500) {//head to other stop if the player is far away this.changeDirection(false) //go left this.isMoving = true this.move(); //needs to move out of the stop range } else if (this.position.x < this.stops.left && player.position.x > this.stops.right - 500) {//head to other stop if the player is far away this.changeDirection(true) //go right this.isMoving = true this.move(); //needs to move out of the stop range } } else if (!Matter.Query.collides(this, [jumpSensor]).length) {//wait until player is off the train to start sensing this.isSensing = true } }, }); Matter.Body.setStatic(rect, true); //make static Composite.add(engine.world, rect); //add to world rect.classType = "body" return rect }, chain(x, y, angle = 0, isAttached = true, len = 15, radius = 20, stiffness = 1, damping = 1) { const gap = 2 * radius const unit = { x: Math.cos(angle), y: Math.sin(angle) } for (let i = 0; i < len; i++) { body[body.length] = Bodies.polygon(x + gap * unit.x * i, y + gap * unit.y * i, 12, radius, { inertia: Infinity, isNotHoldable: true }); const who = body[body.length - 1] who.collisionFilter.category = cat.body; who.collisionFilter.mask = cat.player | cat.map | cat.body | cat.bullet | cat.mob | cat.mobBullet Composite.add(engine.world, who); //add to world who.classType = "body" } for (let i = 1; i < len; i++) { //attach blocks to each other consBB[consBB.length] = Constraint.create({ bodyA: body[body.length - i], bodyB: body[body.length - i - 1], stiffness: stiffness, damping: damping }); Composite.add(engine.world, consBB[consBB.length - 1]); } cons[cons.length] = Constraint.create({ //pin first block to a point in space pointA: { x: x, y: y }, bodyB: body[body.length - len], stiffness: 1, damping: damping }); Composite.add(engine.world, cons[cons.length - 1]); if (isAttached) { cons[cons.length] = Constraint.create({ //pin last block to a point in space pointA: { x: x + gap * unit.x * (len - 1), y: y + gap * unit.y * (len - 1) }, bodyB: body[body.length - 1], stiffness: 1, damping: damping }); Composite.add(engine.world, cons[cons.length - 1]); } }, //****************************************************************************************************************** //****************************************************************************************************************** //****************************************************************************************************************** //****************************************************************************************************************** template() { simulation.enableConstructMode() level.setPosToSpawn(0, -50); //normal spawn level.exit.x = 1500; level.exit.y = -1875; spawn.mapRect(level.enter.x, level.enter.y + 20, 100, 20); //bump for level entrance spawn.mapRect(level.exit.x, level.exit.y + 20, 100, 20); //bump for level exit level.defaultZoom = 1800 simulation.zoomTransition(level.defaultZoom) document.body.style.backgroundColor = "#d8dadf"; // color.map = "#444" //custom map color level.custom = () => { level.exit.drawAndCheck(); level.enter.draw(); }; level.customTopLayer = () => { }; spawn.mapRect(-100, 0, 1000, 100); // powerUps.spawnStartingPowerUps(1475, -1175); // spawn.debris(750, -2200, 3700, 16); //16 debris per level // spawn.bodyRect(1540, -1110, 300, 25, 0.9); // spawn.randomSmallMob(1300, -70); // spawn.randomMob(2650, -975, 0.8); // spawn.randomGroup(1700, -900, 0.4); // if (simulation.difficulty > 1) spawn.randomLevelBoss(2200, -1300); // spawn.secondaryBossChance(100, -1500) powerUps.addResearchToLevel() //needs to run after mobs are spawned }, testing() { simulation.enableConstructMode() //tech.giveTech('motion sickness') //used to build maps in testing mode document.body.style.backgroundColor = "#fff"; // color.map = "#444" //custom map color // level.difficultyIncrease(14); //hard mode level 7 level.defaultZoom = 1500 simulation.zoomTransition(level.defaultZoom) const mover = level.mover(2800, -300, 1000, 25); //x,y,width.height,VxGoal,force const train = level.transport(2900, -500, 500, 25, 8); //x,y,width.height,VxGoal,force spawn.bodyRect(1900, -550, 50, 50); const button = level.button(2535, -200) // spawn.bodyRect(250, -450, 50, 50); //block on button level.custom = () => { //oscillate back and forth if (train.position.x < 2000) { train.changeDirection(true) //go right } else if (train.position.x > 4000) { train.changeDirection(false) //go left } if (!button.isUp) train.move(); mover.push(); ctx.fillStyle = "#d4d4d4" ctx.fillRect(2500, -475, 200, 300) ctx.fillStyle = "#ddd" ctx.fillRect(-150, -1000, 6875, 1000); ctx.fillStyle = "rgba(0,255,255,0.1)"; ctx.fillRect(6400, -550, 300, 350); level.exit.drawAndCheck(); level.enter.draw(); }; level.customTopLayer = () => { train.draw() mover.draw(); button.query(); button.draw(); ctx.fillStyle = "rgba(0,0,0,0.1)" ctx.fillRect(-150, -650, 900, 250) }; level.setPosToSpawn(0, -450); //normal spawn spawn.mapRect(level.enter.x, level.enter.y + 20, 100, 20); level.exit.x = 6500; level.exit.y = -230; spawn.mapRect(-950, 0, 8200, 800); //ground spawn.mapRect(-950, -1200, 800, 1400); //left wall spawn.mapRect(-950, -1800, 8200, 800); //roof spawn.mapRect(-250, -400, 1000, 600); // shelf spawn.mapRect(-250, -1200, 1000, 550); // shelf roof // for (let i = 0; i < 10; ++i) powerUps.spawn(550, -800, "ammo", false); function blockDoor(x, y, blockSize = 58) { spawn.mapRect(x, y - 290, 40, 60); // door lip spawn.mapRect(x, y, 40, 50); // door lip for (let i = 0; i < 4; ++i) spawn.bodyRect(x + 5, y - 260 + i * blockSize, 30, blockSize); } spawn.mapRect(2500, -1200, 200, 750); //right wall spawn.mapRect(2500, -200, 200, 300); //right wall spawn.mapRect(4500, -1200, 200, 650); //right wall blockDoor(4585, -310) spawn.mapRect(4500, -300, 200, 400); //right wall spawn.mapRect(6400, -1200, 400, 750); //right wall spawn.mapRect(6400, -200, 400, 300); //right wall spawn.mapRect(6700, -1800, 800, 2600); //right wall spawn.mapRect(level.exit.x, level.exit.y + 20, 100, 100); //exit bump //place to hide spawn.mapRect(4650, -300, 1150, 50); spawn.mapRect(5750, -300, 50, 200); spawn.mapRect(5575, -100, 50, 125); spawn.mapRect(5300, -275, 50, 175); spawn.mapRect(5050, -100, 50, 150); spawn.mapRect(4850, -275, 50, 175); spawn.mapRect(-950, -3250, 850, 1750); //roof spawn.mapRect(-175, -2975, 300, 1425); spawn.mapRect(75, -2650, 325, 1150); spawn.mapRect(375, -2225, 250, 650); spawn.mapRect(4075, -2125, 700, 800); spawn.mapRect(4450, -2950, 675, 1550); spawn.mapRect(4875, -3625, 725, 2225); spawn.mapRect(5525, -4350, 1725, 2925); spawn.mapRect(7200, -5125, 300, 3900); //??? // level.difficultyIncrease(3 * 4) //30 is near max on hard //60 is near max on why // m.addHealth(Infinity) // spawn.starter(1900, -500, 200) //big boy // spawn.starter(1900, -500, 100) //big boy // for (let i = 0; i < 10; ++i) spawn.launcher(1900, -500) // spawn.suckerBoss(1900, -500) // spawn.launcherBoss(3200, -500) // spawn.laserTargetingBoss(1700, -500) // spawn.powerUpBoss(1900, -500) // spawn.powerUpBossBaby(3200, -500) // spawn.dragonFlyBoss(1700, -500) // spawn.streamBoss(3200, -500) // spawn.pulsarBoss(1700, -500) // spawn.spawnerBossCulture(3200, -500) // spawn.grenadierBoss(1700, -500) // spawn.growBossCulture(3200, -500) // spawn.blinkBoss(1700, -500) // spawn.snakeSpitBoss(3200, -500) // spawn.laserBombingBoss(1700, -500) // spawn.launcherBoss(3200, -500) // spawn.blockBoss(1700, -500) // spawn.blinkBoss(3200, -500) // spawn.spiderBoss(1700, -500) // spawn.tetherBoss(1700, -500) //go to actual level? // spawn.revolutionBoss(1900, -500) // spawn.bomberBoss(1400, -500) // spawn.cellBossCulture(1600, -500) // spawn.shieldingBoss(1700, -500) // for (let i = 0; i < 10; ++i) spawn.bodyRect(1600 + 5, -500, 30, 40); // for (let i = 0; i < 4; i++) spawn.starter(1900, -500) // spawn.pulsar(1900, -500) // spawn.shield(mob[mob.length - 1], 1900, -500, 1); // mob[mob.length - 1].isShielded = true // spawn.nodeGroup(1200, 0, "grenadier") // spawn.blinkBoss(1200, -500) // spawn.suckerBoss(2900, -500) // spawn.randomMob(1600, -500) }, null() { level.levels.pop(); //remove lore level from rotation // level.onLevel-- // console.log(level.onLevel, level.levels) //start a conversation based on the number of conversations seen if (localSettings.loreCount > lore.conversation.length - 1) localSettings.loreCount = lore.conversation.length - 1; //repeat final conversation if lore count is too high if (!simulation.isCheating && localSettings.loreCount < lore.conversation.length) { tech.isNoDraftPause = true //disable pause lore.testSpeechAPI() //see if speech is working lore.chapter = localSettings.loreCount //set the chapter to listen to to be the lore level (you can't use the lore level because it changes during conversations) lore.sentence = 0 //what part of the conversation to start on lore.conversation[lore.chapter][lore.sentence]() localSettings.loreCount++ //hear the next conversation next time you win if (localSettings.isAllowed) localStorage.setItem("localSettings", JSON.stringify(localSettings)); //update local storage } // const hazardSlime = level.hazard(-1800, 150, 3600, 650, 0.004, "hsla(160, 100%, 35%,0.75)") level.isHazardRise = false //this is set to true to make the slime rise up const hazardSlime = level.hazard(-1800, -800, 3600, 1600, 0.004) hazardSlime.height -= 950 hazardSlime.min.y += 950 hazardSlime.max.y = hazardSlime.min.y + hazardSlime.height const circle = { x: 0, y: -500, radius: 50 } level.custom = () => { //draw wide line ctx.beginPath(); ctx.moveTo(circle.x, -800) ctx.lineTo(circle.x, circle.y) ctx.lineWidth = 40; ctx.strokeStyle = lore.talkingColor //"#d5dddd" //"#bcc"; ctx.globalAlpha = 0.03; ctx.stroke(); ctx.globalAlpha = 1; //support pillar ctx.fillStyle = "rgba(0,0,0,0.2)"; ctx.fillRect(-25, 0, 50, 1000); //draw circles ctx.beginPath(); ctx.arc(circle.x, circle.y, circle.radius, 0, 2 * Math.PI); ctx.fillStyle = "#bcc" ctx.fill(); ctx.lineWidth = 2; ctx.strokeStyle = "#abb"; ctx.stroke(); ctx.beginPath(); ctx.arc(circle.x, circle.y, circle.radius / 8, 0, 2 * Math.PI); ctx.fillStyle = lore.talkingColor //"#dff" ctx.fill(); // level.enter.draw(); }; let sway = { x: 0, y: 0 } let phase = -Math.PI / 2 level.customTopLayer = () => { ctx.fillStyle = "rgba(0,0,0,0.1)"; ctx.fillRect(-1950, -950, 3900, 1900); //draw center circle lines ctx.beginPath(); const step = Math.PI / 20 const horizontalStep = 85 if (simulation.isCheating) phase += 0.3 * Math.random() * Math.random() //(m.pos.x - circle.x) * 0.0005 //0.05 * Math.sin(simulation.cycle * 0.030) // const sway = 5 * Math.cos(simulation.cycle * 0.007) sway.x = sway.x * 0.995 + 0.005 * (m.pos.x - circle.x) * 0.05 //+ 0.04 * Math.cos(simulation.cycle * 0.01) sway.y = 2.5 * Math.sin(simulation.cycle * 0.015) for (let i = -19.5; i < 20; i++) { const where = { x: circle.x + circle.radius * Math.cos(i * step + phase), y: circle.y + circle.radius * Math.sin(i * step + phase) } ctx.moveTo(where.x, where.y); ctx.bezierCurveTo(sway.x * Math.abs(i) + where.x, where.y + 25 * Math.abs(i) + 60 + sway.y * Math.sqrt(Math.abs(i)), sway.x * Math.abs(i) + where.x + horizontalStep * i, where.y + 25 * Math.abs(i) + 60 + sway.y * Math.sqrt(Math.abs(i)), horizontalStep * i, -800); } ctx.lineWidth = 0.5; ctx.strokeStyle = "#899"; ctx.stroke(); hazardSlime.query(); if (level.isHazardRise) hazardSlime.level(true) //draw wires // ctx.beginPath(); // ctx.moveTo(-500, -800); // ctx.quadraticCurveTo(-800, -100, -1800, -375); // ctx.moveTo(-600, -800); // ctx.quadraticCurveTo(-800, -200, -1800, -325); // ctx.lineWidth = 1; // ctx.strokeStyle = "#9aa"; // ctx.stroke(); }; level.setPosToSpawn(0, -50); //normal spawn spawn.mapRect(level.enter.x, level.enter.y + 25, 100, 10); level.exit.x = 0; level.exit.y = 40000; level.defaultZoom = 1000 simulation.zoomTransition(level.defaultZoom) // document.body.style.backgroundColor = "#aaa"; document.body.style.backgroundColor = "#ddd"; color.map = "#586363" //808f8f" spawn.mapRect(-3000, 800, 5000, 1200); //bottom spawn.mapRect(-2000, -2000, 5000, 1200); //ceiling spawn.mapRect(-3000, -2000, 1200, 3400); //left spawn.mapRect(1800, -1400, 1200, 3400); //right spawn.mapRect(-500, 0, 1000, 50); //center platform spawn.mapRect(-500, -25, 25, 50); //edge shelf spawn.mapRect(475, -25, 25, 50); //edge shelf }, intro() { // console.log(level.levelsCleared) if (level.levelsCleared === 0) { //if this is the 1st level of the game //wait to spawn power ups until unpaused //power ups don't spawn in experiment mode, so they don't get removed at the start of experiment mode const goal = simulation.cycle + 10 function cycle() { if (simulation.cycle > goal) { if (localSettings.loreCount === 6) { powerUps.spawn(2095 + 15 * (Math.random() - 0.5), -2170, "field", false); } else { powerUps.spawnStartingPowerUps(2095 + 15 * (Math.random() - 0.5), -2070 - 125); } if (simulation.difficultyMode < 5) { powerUps.spawn(2095 + 15 * (Math.random() - 0.5), -2070 - 25, "heal", false); powerUps.spawn(2095 + 15 * (Math.random() - 0.5), -2070 - 75, "heal", false); powerUps.spawn(2095 + 15 * (Math.random() - 0.5), -2070, "research", false); //not on why difficulty } } else { requestAnimationFrame(cycle); } } requestAnimationFrame(cycle); if (localSettings.levelsClearedLastGame < 3) { if (!simulation.isCheating && !m.isShipMode && !build.isExperimentRun) { spawn.wireFoot(); spawn.wireFootLeft(); spawn.wireKnee(); spawn.wireKneeLeft(); spawn.wireHead(); // for (let i = 0; i < 3; i++) powerUps.spawn(2095, -1220 - 50 * i, "tech", false); //unavailable tech spawns // spawn.mapRect(2000, -1025, 200, 25); } } else if (!build.isExperimentRun) { simulation.trails() //bonus power ups for clearing runs in the last game if (!simulation.isCheating && localSettings.levelsClearedLastGame > 1) { for (let i = 0; i < localSettings.levelsClearedLastGame / 3; i++) powerUps.spawn(2095 + 2 * Math.random(), -1270 - 50 * i, "tech", false); //spawn a tech for levels cleared in last game simulation.makeTextLog(`for (let i = 0; i < localSettings.levelsClearedLastGame / 3; i++)`); simulation.makeTextLog(`{ powerUps.spawn(m.pos.x, m.pos.y, "tech") //simulation superposition}`); localSettings.levelsClearedLastGame = 0 //after getting bonus power ups reset run history if (localSettings.isAllowed) localStorage.setItem("localSettings", JSON.stringify(localSettings)); //update local storage } } spawn.mapRect(2025, 0, 150, 50); //lid to floor hole } else { for (let i = 0; i < 60; i++) { setTimeout(() => { if (level.levels[level.onLevel] === "intro") spawn.sneaker(2100, -1500 - 50 * i); }, 2000 + 500 * i); } } const wires = new Path2D() //pre-draw the complex lighting path to save processing wires.moveTo(-150, -275) wires.lineTo(80, -275) wires.lineTo(80, -1000) wires.moveTo(-150, -265) wires.lineTo(90, -265) wires.lineTo(90, -1000) wires.moveTo(-150, -255) wires.lineTo(100, -255) wires.lineTo(100, -1000) wires.moveTo(-150, -245) wires.lineTo(1145, -245) wires.lineTo(1145, 0) wires.moveTo(-150, -235) wires.lineTo(1135, -235) wires.lineTo(1135, 0) wires.moveTo(-150, -225) wires.lineTo(1125, -225) wires.lineTo(1125, 0) wires.moveTo(-150, -215) wires.lineTo(460, -215) wires.lineTo(460, 0) wires.moveTo(-150, -205) wires.lineTo(450, -205) wires.lineTo(450, 0) wires.moveTo(-150, -195) wires.lineTo(440, -195) wires.lineTo(440, 0) wires.moveTo(1155, 0) wires.lineTo(1155, -450) wires.lineTo(1000, -450) wires.lineTo(1000, -1000) wires.moveTo(1165, 0) wires.lineTo(1165, -460) wires.lineTo(1010, -460) wires.lineTo(1010, -1000) wires.moveTo(1175, 0) wires.lineTo(1175, -470) wires.lineTo(1020, -470) wires.lineTo(1020, -1000) wires.moveTo(1185, 0) wires.lineTo(1185, -480) wires.lineTo(1030, -480) wires.lineTo(1030, -1000) wires.moveTo(1195, 0) wires.lineTo(1195, -490) wires.lineTo(1040, -490) wires.lineTo(1040, -1000) wires.moveTo(1625, -1000) wires.lineTo(1625, 0) wires.moveTo(1635, -1000) wires.lineTo(1635, 0) wires.moveTo(1645, -1000) wires.lineTo(1645, 0) wires.moveTo(1655, -1000) wires.lineTo(1655, 0) wires.moveTo(1665, -1000) wires.lineTo(1665, 0) wires.moveTo(1675, -465) wires.lineTo(2325, -465) wires.lineTo(2325, 0) wires.moveTo(1675, -455) wires.lineTo(2315, -455) wires.lineTo(2315, 0) wires.moveTo(1675, -445) wires.lineTo(2305, -445) wires.lineTo(2305, 0) wires.moveTo(1675, -435) wires.lineTo(2295, -435) wires.lineTo(2295, 0) wires.moveTo(2335, 0) wires.lineTo(2335, -710) wires.lineTo(2600, -710) wires.moveTo(2345, 0) wires.lineTo(2345, -700) wires.lineTo(2600, -700) wires.moveTo(2355, 0) wires.lineTo(2355, -690) wires.lineTo(2600, -690) level.custom = () => { //push around power ups stuck in the tube wall if (!(simulation.cycle % 30)) { for (let i = 0, len = powerUp.length; i < len; i++) { if (powerUp[i].position.y < -1000) powerUp[i].force.x += 0.01 * (Math.random() - 0.5) * powerUp[i].mass } } //draw binary number const binary = (localSettings.runCount >>> 0).toString(2) const height = 20 const width = 8 const yOff = -40 //-580 let xOff = -130 //2622 ctx.strokeStyle = "#bff" ctx.lineWidth = 1.5; ctx.beginPath() for (let i = 0; i < binary.length; i++) { if (binary[i] === "0") { ctx.moveTo(xOff, yOff) ctx.lineTo(xOff, yOff + height) ctx.lineTo(xOff + width, yOff + height) ctx.lineTo(xOff + width, yOff) ctx.lineTo(xOff, yOff) xOff += 10 + width } else { ctx.moveTo(xOff, yOff) ctx.lineTo(xOff, yOff + height) xOff += 10 } } ctx.stroke(); ctx.beginPath() ctx.strokeStyle = "#ccc" ctx.lineWidth = 5; ctx.stroke(wires); //squares that look like they keep the wires in place ctx.beginPath() ctx.rect(1600, -500, 90, 100) ctx.rect(-55, -285, 12, 100) ctx.rect(1100, -497, 8, 54) ctx.rect(2285, -200, 80, 10) ctx.rect(1110, -70, 100, 10) ctx.fillStyle = "#ccc" ctx.fill() //power up dispenser // ctx.beginPath() // for (let i = 2; i < 10; i++) { // ctx.moveTo(2000, -100 * i) // ctx.lineTo(2080, -100 * i) // } // ctx.strokeStyle = "#ddd" // ctx.lineWidth = 5; // ctx.stroke(); // ctx.beginPath() // for (let i = 2; i < 10; i++) { // ctx.arc(2040, -100 * i, 30, 0, 2 * Math.PI); // ctx.moveTo(2040, -100 * i) // } // ctx.fillStyle = "rgba(0,0,0,0.3)" // ctx.fill() // ctx.fillStyle = "rgba(240,255,255,0.5)" // ctx.fillRect(2000, -1000, 80, 700) //exit room ctx.fillStyle = "#f2f2f2" ctx.fillRect(2600, -600, 400, 300) // level.enter.draw(); level.exit.drawAndCheck(); }; level.customTopLayer = () => { //exit room glow ctx.fillStyle = "rgba(0,255,255,0.05)" ctx.fillRect(2600, -600, 400, 300) //draw shade for ceiling tech ctx.fillStyle = "rgba(68, 68, 68,0.95)" ctx.fillRect(2030, -2800, 150, 1800); ctx.fillStyle = "rgba(68, 68, 68,0.95)" ctx.fillRect(2030, 0, 150, 1800); }; level.setPosToSpawn(460, -100); //normal spawn // level.enter.x = -1000000; //hide enter graphic for first level by moving to the far left level.exit.x = 2800; level.exit.y = -335; spawn.mapRect(level.exit.x, level.exit.y + 25, 100, 100); //exit bump simulation.zoomScale = 1000 //1400 is normal level.defaultZoom = 1600 simulation.zoomTransition(level.defaultZoom, 1) document.body.style.backgroundColor = "#e1e1e1"; spawn.mapRect(-2750, -2800, 2600, 4600); //left wall spawn.mapRect(3000, -2800, 2600, 4600); //right wall // spawn.mapRect(-250, 0, 3600, 1800); //ground spawn.mapRect(-250, 0, 2300, 1800); //split roof spawn.mapRect(2150, 0, 1200, 1800); //split roof spawn.mapRect(2025, -3, 25, 15); //lip on power up chamber spawn.mapRect(2150, -3, 25, 15); //lip on power up chamber // spawn.mapRect(-250, -2800, 3600, 1800); //roof spawn.mapRect(-250, -2800, 2300, 1800); //split roof map[map.length - 1].friction = 0 map[map.length - 1].frictionStatic = 0 spawn.mapRect(2150, -2800, 1200, 1800); //split roof map[map.length - 1].friction = 0 map[map.length - 1].frictionStatic = 0 spawn.mapRect(2025, -1010, 25, 13); //lip on power up chamber spawn.mapRect(2150, -1010, 25, 13); //lip on power up chamber spawn.mapRect(2600, -300, 500, 500); //exit shelf spawn.mapRect(2600, -1200, 500, 600); //exit roof spawn.mapRect(-95, -1100, 80, 110); //wire source spawn.mapRect(410, -10, 90, 20); //small platform for player spawn.bodyRect(2425, -120, 70, 50); spawn.bodyRect(2400, -100, 100, 60); spawn.bodyRect(2500, -150, 100, 150); //exit step }, final() { // color.map = "rgba(0,0,0,0.8)" const slime = level.hazard(simulation.isHorizontalFlipped ? 150 - 860 : -150, -360, 880, 259) //x, y, width, height, damage = 0.002) { slime.height -= slime.maxHeight - 150 //start slime at zero slime.min.y += slime.maxHeight slime.max.y = slime.min.y + slime.height level.custom = () => { level.exit.drawAndCheck(); level.enter.draw(); }; level.customTopLayer = () => { slime.query(); slime.levelRise(0.1) ctx.fillStyle = "rgba(0,255,255,0.1)" ctx.fillRect(5385, -550, 300, 250) }; level.setPosToSpawn(0, -250); //normal spawn spawn.mapRect(level.enter.x, level.enter.y + 20, 100, 20); spawn.mapRect(5500, -330 + 20, 100, 20); //spawn this because the real exit is in the wrong spot level.exit.x = 0; level.exit.y = -8000; level.defaultZoom = 2500 simulation.zoomTransition(level.defaultZoom) document.body.style.backgroundColor = "#ddd"; for (let i = 0; i < 16; i++) powerUps.spawn(4600 + 40 * i, -30, "ammo"); spawn.mapRect(-1950, 0, 8200, 1800); //ground spawn.mapRect(-1950, -1500, 1800, 1900); //left wall spawn.mapRect(-1950, -3300, 8200, 1800); //roof spawn.mapRect(-250, -200, 1000, 300); // shelf spawn.mapRect(-250, -1700, 1000, 1250); // shelf roof spawn.mapRect(705, -210, 25, 50); spawn.mapRect(725, -220, 25, 50); spawn.bodyRect(750, -125, 125, 125); spawn.bodyRect(875, -50, 50, 50); spawn.mapRect(5400, -1700, 400, 1150); //right wall spawn.mapRect(5400, -300, 400, 400); //right wall spawn.mapRect(5700, -3300, 1800, 5100); //right wall spawn.mapRect(level.exit.x, level.exit.y + 20, 100, 100); //exit bump spawn.mapRect(5403, -650, 400, 450); //blocking exit if (mobs.mobDeaths < level.levelsCleared && !simulation.isCheating) { //pacifist run for (let i = 0; i < 250; i++) spawn.starter(1000 + 4000 * Math.random(), -1500 * Math.random()) } else { spawn.finalBoss(3000, -750) } if (simulation.isHorizontalFlipped) { //flip the map horizontally level.flipHorizontal(); //only flips map,body,mob,powerUp,cons,consBB, exit level.setPosToSpawn(0, -250); level.custom = () => { level.exit.drawAndCheck(); level.enter.draw(); }; level.customTopLayer = () => { slime.query(); slime.levelRise(0.1) ctx.fillStyle = "rgba(0,255,255,0.1)" ctx.fillRect(-5385 - 300, -550, 300, 250) }; } if (mobs.mobDeaths < level.levelsCleared && localSettings.loreCount > 5 && !simulation.isCheating) { //open door for pacifist run on final lore chapter if (simulation.isHorizontalFlipped) { level.exit.x = -5500 - 100; } else { level.exit.x = 5500; } level.exit.y = -330; Matter.Composite.remove(engine.world, map[map.length - 1]); map.splice(map.length - 1, 1); simulation.draw.setPaths(); //redraw map draw path level.levels.push("null") } }, gauntlet() { level.custom = () => { level.exit.drawAndCheck(); level.enter.draw(); }; level.customTopLayer = () => { ctx.fillStyle = "rgba(0,255,255,0.1)" ctx.fillRect(6400, -550, 300, 350) ctx.fillStyle = "rgba(0,0,0,0.1)" ctx.fillRect(-175, -975, 900, 575) }; level.setPosToSpawn(0, -475); //normal spawn spawn.mapRect(level.enter.x, level.enter.y + 20, 100, 20); level.exit.x = 6500; level.exit.y = -230; level.defaultZoom = 1500 simulation.zoomTransition(level.defaultZoom) document.body.style.backgroundColor = "#ddd"; // spawn.mapRect(-300, -1050, 300, 200); // Matter.Body.setAngle(map[map.length - 1], -Math.PI / 4) spawn.mapRect(-950, 0, 8200, 800); //ground spawn.mapRect(-950, -1200, 800, 1400); //left wall spawn.mapRect(-950, -1800, 8200, 800); //roof spawn.mapRect(175, -700, 575, 950); spawn.mapRect(-250, -425, 600, 650); spawn.mapRect(-250, -1200, 1000, 250); // shelf roof powerUps.spawnStartingPowerUps(600, -800); spawn.blockDoor(710, -710); spawn.mapRect(2500, -1200, 200, 750); //right wall spawn.blockDoor(2585, -210) spawn.mapRect(2500, -200, 200, 300); //right wall spawn.mapRect(4500, -1200, 200, 750); //right wall spawn.blockDoor(4585, -210) spawn.mapRect(4500, -200, 200, 300); //right wall spawn.mapRect(6400, -1200, 400, 750); //right wall spawn.mapRect(6400, -200, 400, 300); //right wall spawn.mapRect(6700, -1800, 800, 2600); //right wall spawn.mapRect(level.exit.x, level.exit.y + 20, 100, 100); //exit bump if (mobs.mobDeaths < level.levelsCleared && !simulation.isCheating) { //pacifist run // spawn.setSpawnList(); spawn.pickList.splice(0, 1); spawn.pickList.push('starter'); spawn.pickList.splice(0, 1); spawn.pickList.push('starter'); spawn.starter(1500, -200, 150 + Math.random() * 30); spawn.nodeGroup(3500, -200, 'starter'); spawn.lineGroup(5000, -200, 'starter'); for (let i = 0; i < 3; ++i) { if (simulation.difficulty * Math.random() > 15 * i) spawn.nodeGroup(2000 + 500 * (Math.random() - 0.5), -800 + 200 * (Math.random() - 0.5), 'starter'); if (simulation.difficulty * Math.random() > 10 * i) spawn.lineGroup(3500 + 500 * (Math.random() - 0.5), -800 + 200 * (Math.random() - 0.5), 'starter'); if (simulation.difficulty * Math.random() > 7 * i) spawn.nodeGroup(5000 + 500 * (Math.random() - 0.5), -800 + 200 * (Math.random() - 0.5), 'starter'); } } else { spawn[spawn.pickList[0]](1500, -200, 150 + Math.random() * 30); spawn.nodeGroup(3500, -200, spawn.allowedGroupList[Math.floor(Math.random() * spawn.allowedGroupList.length)]); spawn.lineGroup(5000, -200, spawn.allowedGroupList[Math.floor(Math.random() * spawn.allowedGroupList.length)]); for (let i = 0; i < 3; ++i) { if (simulation.difficulty * Math.random() > 15 * i) spawn.randomGroup(2000 + 500 * (Math.random() - 0.5), -800 + 200 * (Math.random() - 0.5), Infinity); if (simulation.difficulty * Math.random() > 10 * i) spawn.randomGroup(3500 + 500 * (Math.random() - 0.5), -800 + 200 * (Math.random() - 0.5), Infinity); if (simulation.difficulty * Math.random() > 7 * i) spawn.randomGroup(5000 + 500 * (Math.random() - 0.5), -800 + 200 * (Math.random() - 0.5), Infinity); } } if (simulation.difficulty > 1) { spawn.randomLevelBoss(5750, -600); spawn.secondaryBossChance(4125, -350) } powerUps.addResearchToLevel() //needs to run after mobs are spawned if (simulation.isHorizontalFlipped) { //flip the map horizontally level.flipHorizontal(); //only flips map,body,mob,powerUp,cons,consBB, exit level.setPosToSpawn(0, -475); level.custom = () => { level.exit.drawAndCheck(); level.enter.draw(); }; level.customTopLayer = () => { ctx.fillStyle = "rgba(0,255,255,0.1)" ctx.fillRect(-6400 - 300, -550, 300, 350) ctx.fillStyle = "rgba(0,0,0,0.1)" ctx.fillRect(175 - 900, -975, 900, 575) }; } }, subway() { // simulation.enableConstructMode() //tech.giveTech('motion sickness') //used to build maps in testing mode // level.difficultyIncrease(10 * 4); // m.maxHealth = m.health = 100 // color.map = "#333" //custom map color document.body.style.backgroundColor = "#e3e3e3"//"#e3e3e3"//color.map//"#333"//"#000" level.defaultZoom = 1400 simulation.zoomTransition(level.defaultZoom) 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 = () => { } let stationCustomTopLayer = () => { } const train = [] train.push(level.transport(1475, -200, 500, 25, 30)) train[train.length - 1].isMoving = false train[train.length - 1].stops = { left: 1725, right: 7225 } train.push(level.transport(-1475 - 500, -200, 500, 25, -30)) train[train.length - 1].isMoving = false train[train.length - 1].stops = { left: -7225, right: -1725 } 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(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 function removeAll(array) { for (let i = 0; i < array.length; ++i) Matter.Composite.remove(engine.world, array[i]); } removeAll(map); map = []; //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 Matter.Composite.remove(engine.world, powerUp[i]); powerUp.splice(i--, 1) } } //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 mob[i].removeConsBB() mob[i].removeCons() mob[i].leaveBody = false mob[i].alive = false Matter.Composite.remove(engine.world, mob[i]); mob.splice(i--, 1) } } } 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) { if (!isExitOpen && gatesOpenRight < stationNumber) level.newLevelOrPhase() //run some new level tech effects gatesOpenRight = stationNumber } else if (stationNumber < 0) { if (!isExitOpen && gatesOpenLeft > stationNumber) level.newLevelOrPhase() //run some new level tech effects 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 () => { //empty starting station if (isExitOpen) { 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 spawn.mapRect(x + -1500, -210, 500, 350); //station floor left spawn.mapRect(x + 1000, -210, 500, 350); //station floor right spawn.mapRect(x + 900, -1250, 125, 1250); spawn.mapRect(x - 1025, -1550, 125, 1625); spawn.mapRect(x - 50, -1900, 100, 1500); spawn.mapRect(x + -975, -1250, 200, 25); spawn.mapRect(x + -950, -625, 150, 25); spawn.mapRect(x - 925, -400, 250, 175); spawn.mapRect(x - 725, -900, 225, 300); spawn.mapRect(x + 325, -225, 325, 75); spawn.mapRect(x + 400, -950, 275, 25); spawn.mapRect(x + 775, -575, 200, 25); spawn.mapRect(x + 0, -1225, 125, 25); spawn.mapRect(x + 0, -575, 225, 175); spawn.mapRect(x - 925, -75, 875, 150); spawn.mapRect(x + 475, -1400, 75, 1250); if (!isExitOpen) { if (Math.random() < 0.5) { spawn.randomMob(x + -850, -450, 0); spawn.randomMob(x + -850, -125, 0); spawn.randomMob(x + -725, -100, 0); spawn.randomMob(x + 0, -100, 0); spawn.randomMob(x + 800, -50, 0); spawn.randomMob(x + 50, -275, 0); spawn.randomMob(x + -300, -425, 0); spawn.randomMob(x + -750, -475, 0); spawn.randomMob(x + -850, -775, 0); spawn.randomMob(x + -650, -1000, 0); spawn.randomMob(x + -150, -1325, 0); spawn.randomMob(x + -825, -1350, 0); spawn.randomMob(x + -375, -150, 0); } else { spawn.randomMob(x + 350, -350, 0); spawn.randomMob(x + 175, -700, 0); spawn.randomMob(x + 350, -1175, 0); spawn.randomMob(x + 200, -1600, 0); spawn.randomMob(x + 500, -1675, 0); spawn.randomMob(x + 425, -50, 0); spawn.randomMob(x + 725, -75, 0); spawn.randomMob(x + 650, -700, 0); spawn.randomMob(x + 775, -1150, 0); spawn.randomMob(x + 500, -1675, 0); spawn.randomMob(x + -150, -175, 0); spawn.randomMob(x + -800, -150, 0); } } const boost1 = level.boost(x - 1185, -225, 1400) const boost2 = level.boost(x + 1100, -225, 1100) const hazard1 = level.hazard(x - 900, -1225, 1800, 1225) let isSlimeRiseUp = false const drip = [] drip.push(level.drip(x - 900 + 1800 * Math.random(), -1900, 0, 100)) // drip(x, yMin, yMax, period = 100, color = "hsla(160, 100%, 35%, 0.5)") { drip.push(level.drip(x - 900 + 1800 * Math.random(), -1900, 0, 150)) drip.push(level.drip(x - 900 + 1800 * Math.random(), -1900, 0, 70)) // drip.push(level.drip(x - 900 + 1800 * Math.random(), -1900, 0, 210)) // drip.push(level.drip(x - 900 + 1800 * Math.random(), -1900, 0, 67)) stationCustom = () => { for (let i = 0; i < drip.length; i++) drip[i].draw() // drip1.draw(); // drip2.draw(); // drip3.draw(); } stationCustomTopLayer = () => { checkGate(gate, gateButton) hazard1.query(); hazard1.level(isSlimeRiseUp, 1.5) if (!(hazard1.height < hazard1.maxHeight)) { isSlimeRiseUp = false } else if (!(hazard1.height > 0)) { isSlimeRiseUp = true } boost1.query(); boost2.query(); } }, () => { //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 = 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"); spawn.mapRect(x + -1500, -210, 1425, 350); //station floor left spawn.mapRect(x + 75, -210, 1425, 350); //station floor right spawn.mapRect(x + 75, -950, 50, 450); 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 - 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 + 625, -1700, 750, 500); if (!isExitOpen) { spawn.randomMob(x + -750, -1925, 0); spawn.randomMob(x + -425, -2300, 0); spawn.randomMob(x + -350, -2200, 0); spawn.randomMob(x + -275, -2175, 0); spawn.randomMob(x + -375, -2175, 0); spawn.randomMob(x + 1075, -1850, 0); spawn.randomMob(x + 925, -1775, 0); spawn.randomMob(x + 1150, -1800, 0); spawn.randomMob(x + 1400, -2150, 0); spawn.randomMob(x + 925, -850, 0); spawn.randomMob(x + 800, -800, 0); spawn.randomMob(x + 875, -825, 0); spawn.randomMob(x + 1050, -900, 0); spawn.randomMob(x + 19050, -2925, 0); spawn.randomMob(x + 17150, -3150, 0); spawn.randomMob(x + 17700, -3300, 0); } const portal1 = level.portal({ x: x + 0, y: -200 }, -Math.PI / 2, { //up x: x + 200, y: -900 }, -Math.PI / 2) //up const portal2 = level.portal({ x: x + 1275, y: -800 }, Math.PI, { //right x: x + -1275, y: -1875 }, 2 * Math.PI) //right stationCustom = () => { portal1[2].query(true) portal1[3].query(true) portal2[2].query() portal2[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(); } }, () => { //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 = 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 spawn.mapRect(x + -1625, -3950, 325, 2250); //roof wall spawn.mapRect(x + -1050, -575, 1000, 75); spawn.mapRect(x + 175, -575, 975, 75); spawn.mapRect(x + -1050, -825, 150, 275); spawn.mapRect(x + -900, -1200, 2275, 75); spawn.mapRect(x + 125, -1425, 1250, 300); spawn.mapRect(x + -925, -1775, 2100, 75); spawn.mapRect(x + -100, -2050, 950, 350); spawn.mapRect(x + -925, -2100, 100, 400); spawn.mapRect(x + -700, -2375, 1225, 75); spawn.mapRect(x + 650, -2375, 575, 75); spawn.mapRect(x + -25, -2750, 350, 269); spawn.mapRect(x + -950, -3125, 975, 75); spawn.mapRect(x + 325, -3025, 900, 75); spawn.bodyRect(x + -125, -1325, 225, 125, 0.3); spawn.bodyRect(x + -225, -2100, 300, 50, 0.3); spawn.bodyRect(x + -225, -2575, 100, 200, 0.3); spawn.bodyRect(x + 850, -2575, 150, 200, 0.3); spawn.bodyRect(x + 850, -1875, 75, 100, 0.3); spawn.bodyRect(x + 500, -725, 175, 150, 0.3); spawn.bodyRect(x + -925, -2250, 100, 150, 0.3); spawn.bodyRect(x + -1050, -950, 150, 125, 0.3); const mobPlacement = [ () => { //1st floor spawn.randomMob(x + -775, -725, 0); spawn.randomMob(x + -575, -700, 0); spawn.randomMob(x + -275, -700, 0); spawn.randomMob(x + -125, -650, 0); spawn.randomMob(x + 250, -675, 0); spawn.randomMob(x + 425, -650, 0); spawn.randomMob(x + 775, -650, 0); spawn.randomMob(x + 1050, -675, 0); spawn.randomMob(x + 675, -950, 0); spawn.randomMob(x + -625, -900, 0); spawn.randomMob(x + -750, -1400, 0); spawn.randomMob(x + -500, -2025, 0); spawn.randomMob(x + -125, -3225, 0); }, () => { //2nd floor spawn.randomMob(x + -950, -925, 0); spawn.randomMob(x + -775, -1325, 0); spawn.randomMob(x + -450, -1500, 0); spawn.randomMob(x + -325, -1250, 0); spawn.randomMob(x + 0, -1500, 0); spawn.randomMob(x + 375, -1525, 0); spawn.randomMob(x + 750, -1550, 0); spawn.randomMob(x + 1175, -1550, 0); spawn.randomMob(x + -875, -1350, 0); spawn.randomMob(x + -875, -2375, 0); spawn.randomMob(x + 175, -2850, 0); spawn.randomMob(x + 750, -2475, 0); }, () => {//3rd floor spawn.randomMob(x + 1075, -2000, 0); spawn.randomMob(x + 725, -2125, 0); spawn.randomMob(x + 350, -2125, 0); spawn.randomMob(x + -325, -2000, 0); spawn.randomMob(x + -675, -1875, 0); spawn.randomMob(x + -725, -2200, 0); spawn.randomMob(x + -675, -2575, 0); spawn.randomMob(x + -425, -2675, 0); spawn.randomMob(x + -50, -2875, 0); spawn.randomMob(x + 425, -2725, 0); spawn.randomMob(x + 1150, -2550, 0); spawn.randomMob(x + 1150, -2175, 0); spawn.randomMob(x + 1000, -1900, 0); spawn.randomMob(x + 500, -2550, 0); spawn.randomMob(x + 125, -2900, 0); }, () => {//all floors spawn.randomMob(x + 1000, -850, 0); spawn.randomMob(x + 300, -850, 0); spawn.randomMob(x + -450, -825, 0); spawn.randomMob(x + -1025, -1125, 0); spawn.randomMob(x + -750, -1375, 0); spawn.randomMob(x + -225, -1375, 0); spawn.randomMob(x + 625, -1525, 0); spawn.randomMob(x + 1025, -1925, 0); spawn.randomMob(x + -425, -2100, 0); spawn.randomMob(x + -400, -2650, 0); spawn.randomMob(x + 150, -3000, 0); spawn.randomMob(x + 675, -3200, 0); spawn.randomMob(x + -550, -3300, 0); }, ] if (!isExitOpen) mobPlacement[Math.floor(Math.random() * mobPlacement.length)]()//different random mob placements, with mobs clustered to surprise player stationCustom = () => { } 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) { 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 spawn.mapRect(x + -3175, -3200, 6175, 225);//roof if (Math.random() < 0.3) spawn.mapRect(x + -1350, -550, 150, 50); //wall ledge if (Math.random() < 0.3) spawn.mapRect(x + 1175, -550, 200, 50); //wall ledge spawn.mapVertex(x + 600, -900, "490 0 350 80 -350 80 -490 0 -350 -80 350 -80"); //hexagon but wide spawn.mapVertex(x - 600, -750, "490 0 350 80 -350 80 -490 0 -350 -80 350 -80"); //hexagon but wide spawn.mapVertex(x - 100, -1850, "-100 -300 0 -350 100 -300 100 300 0 350 -100 300"); //hexagon but tall spawn.mapVertex(x + -600, -2000, "-150 -450 150 -450 150 450 0 525 -150 450"); //hexagon but big and tall and flat spawn.mapVertex(x + 350, -1575, "-150 0 150 0 150 450 0 525 -150 450"); //hexagon but tall and flat top spawn.mapVertex(x + 850, -1575, "-150 0 150 0 150 450 0 525 -150 450"); //hexagon but tall and flat top spawn.mapVertex(x + -2050, -2350, "490 0 350 80 -350 80 -490 0 -350 -80 350 -80"); //left top corner hexagon but wide spawn.mapVertex(x + 1700, -2450, "-100 -300 0 -350 100 -300 100 300 0 350 -100 300"); //hexagon but tall const mobPlacement = [ () => { //rightish spawn.randomMob(x + 2250, -2375, 0); spawn.randomMob(x + 1950, -2825, 0); spawn.randomMob(x + 1275, -2775, 0); spawn.randomMob(x + 1450, -2200, 0); spawn.randomMob(x + 825, -1950, 0); spawn.randomMob(x + 400, -1875, 0); spawn.randomMob(x + -75, -2275, 0); spawn.randomMob(x + -650, -2550, 0); spawn.randomMob(x + 1500, -2075, 0); spawn.randomMob(x + 2125, -2650, 0); spawn.randomMob(x + 2075, -2250, 0); spawn.randomMob(x + 1000, -2850, 0); spawn.randomMob(x + 750, -950, 0); spawn.randomMob(x + -750, -1125, 0); spawn.randomMob(x + -1575, -2050, 0); }, () => { //leftish spawn.randomMob(x + -2225, -2125, 0); spawn.randomMob(x + -2675, -2125, 0); spawn.randomMob(x + -2600, -2600, 0); spawn.randomMob(x + -2100, -2725, 0); spawn.randomMob(x + -1425, -2600, 0); spawn.randomMob(x + -1375, -2050, 0); spawn.randomMob(x + -575, -2575, 0); spawn.randomMob(x + -125, -2300, 0); spawn.randomMob(x + 350, -1925, 0); spawn.randomMob(x + -350, -1050, 0); spawn.randomMob(x + -1000, -1000, 0); spawn.randomMob(x + -700, -1300, 0); spawn.randomMob(x + 350, -1150, 0); spawn.randomMob(x + -575, -2525, 0); spawn.randomMob(x + -1075, -2525, 0); }, () => {//centerish spawn.randomMob(x + 25, -2275, 0); spawn.randomMob(x + 300, -1975, 0); spawn.randomMob(x + 700, -1950, 0); spawn.randomMob(x + 325, -1200, 0); spawn.randomMob(x + -225, -950, 0); spawn.randomMob(x + -925, -975, 0); spawn.randomMob(x + -675, -2575, 0); spawn.randomMob(x + -1425, -2175, 0); spawn.randomMob(x + 1575, -2075, 0); spawn.randomMob(x + 2300, -2075, 0); spawn.randomMob(x + 425, -1925, 0); spawn.randomMob(x + 125, -2175, 0); spawn.randomMob(x + -325, -2150, 0); spawn.randomMob(x + -350, -950, 0); spawn.randomMob(x + 600, -325, 0); spawn.randomMob(x + -600, -375, 0); }, ] 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 = 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 spawn.mapRect(x + -1100, -925, 100, 425); spawn.mapRect(x + -1100, -575, 375, 75); spawn.mapRect(x + -925, -1300, 375, 125); spawn.mapRect(x + -300, -1300, 620, 125); spawn.mapRect(x + 450, -1400, 500, 225); spawn.mapRect(x + 900, -550, 500, 50); spawn.mapRect(x + 950, -925, 400, 270); spawn.mapRect(x + 1250, -1250, 150, 75); spawn.mapRect(x + -225, -525, 800, 210); spawn.mapRect(x + -100, -1600, 300, 193); spawn.mapRect(x + 925, -1250, 75, 75); 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 (!isExitOpen) { spawn.randomMob(x + -750, -1425, 0); spawn.randomMob(x + -1050, -1100, 0); spawn.randomMob(x + -825, -750, 0); spawn.randomMob(x + -500, -400, 0); spawn.randomMob(x + 450, -650, 0); spawn.randomMob(x + 0, -725, 0); spawn.randomMob(x + 300, -1350, 0); spawn.randomMob(x + 550, -1500, 0); spawn.randomMob(x + 725, -1650, 0); spawn.randomMob(x + 900, -1550, 0); spawn.randomMob(x + 1100, -1300, 0); spawn.randomMob(x + -1050, -1050, 0); spawn.randomMob(x + -925, -350, 0); spawn.randomMob(x + 75, -1750, 0); spawn.randomMob(x + 1000, -375, 0); } 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); ctx.fillRect(x + 950, -675, 400, 125); } }, ] // 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 spawn.mapRect(x + -1500 - 6400 + 200, -2000, 6400, 1500); //tunnel roof // add debris so you can see how fast the train moves const debrisCount = 4 const size = 18 + Math.random() * 25; const wide = 6400 if (isInProgress) { //adds new map elements to the level while the level is already running for (let i = 0; i < map.length; ++i) { map[i].collisionFilter.category = cat.map; map[i].collisionFilter.mask = cat.player | cat.map | cat.body | cat.bullet | cat.powerUp | cat.mob | cat.mobBullet; Matter.Body.setStatic(map[i], true); //make static Composite.add(engine.world, map[i]); //add to world } simulation.draw.setPaths() simulation.draw.lineOfSightPrecalculation() //required precalculation for line of sight //shift trains left/right, as you move left or right a train will jump over and become the train needed at the next station let repositionTrain let playerOnTrain if (Math.abs(train[0].position.x - m.pos.x) > Math.abs(train[1].position.x - m.pos.x)) { //figure out which train the player is farthest from and move it repositionTrain = train[0] playerOnTrain = train[1] } else { repositionTrain = train[1] playerOnTrain = train[0] } repositionTrain.isMoving = false if (repositionTrain.position.x > playerOnTrain.position.x) { //decide if the train is moving left or right Matter.Body.setPosition(repositionTrain, { x: -1725 + x, y: repositionTrain.position.y }); repositionTrain.changeDirection(false) //go left repositionTrain.stops = { right: -1725 + x, left: -7225 + x } for (let i = 0; i < debrisCount; ++i) spawn.bodyRect(x + -1500 - 6400 + 200 + Math.random() * wide, -35, size * (0.6 + Math.random()), size * (0.6 + Math.random()), 1); } else { Matter.Body.setPosition(repositionTrain, { x: 1725 + x, y: repositionTrain.position.y }); repositionTrain.changeDirection(true) //go right repositionTrain.stops = { left: 1725 + x, right: 7225 + x } for (let i = 0; i < debrisCount; ++i) spawn.bodyRect(x + 1500 - 200 + Math.random() * wide, -35, size * (0.6 + Math.random()), size * (0.6 + Math.random()), 1); } } else { for (let i = 0; i < debrisCount; ++i) spawn.bodyRect(x + -1500 - 6400 + 200 + Math.random() * wide, -35, size * (0.6 + Math.random()), size * (0.6 + Math.random()), 1); for (let i = 0; i < debrisCount; ++i) spawn.bodyRect(x + 1500 - 200 + Math.random() * wide, -35, size * (0.6 + Math.random()), size * (0.6 + Math.random()), 1); } } infrastructure(0, false) //if this is run before the level starts, it needs a false level.custom = () => { for (let i = 0; i < train.length; i++) train[i].trainStop() ctx.fillStyle = "rgba(0,0,0,0.1)"//"#ddd" ctx.fillRect(m.pos.x - 4000, m.pos.y - 4000, 8000, 8000) level.exit.drawAndCheck(); // level.enter.draw(); //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() }; level.customTopLayer = () => { for (let i = 0; i < train.length; i++) train[i].draw() stationCustomTopLayer() }; level.isProcedural = true //only used in generating text for the level builder simulation.draw.lineOfSightPrecalculation() //required precalculation for line of sight simulation.draw.drawMapPath = simulation.draw.drawMapSight }, reservoir() { level.exit.x = 1700; level.exit.y = -4510; spawn.mapRect(level.exit.x, level.exit.y + 25, 100, 25); level.setPosToSpawn(-500, 850); //normal spawn spawn.mapRect(level.enter.x, level.enter.y + 20, 100, 20); level.defaultZoom = 2300 simulation.zoomTransition(level.defaultZoom) document.body.style.backgroundColor = "#d8dadf"; color.map = "#3d4240" powerUps.spawnStartingPowerUps(-575, -2925) //walls spawn.mapRect(-3500, -5000, 1500, 6500); spawn.mapRect(2000, -5000, 1500, 6500); spawn.mapRect(-2500, 1100, 5000, 400); //slime floor spawn.mapRect(-3500, -5475, 7000, 600); //top spawn.mapRect(-1925, -4900, 175, 375); //pipe spawn.mapRect(-1950, -4550, 225, 25); //pipe //top floor exit spawn.mapRect(1475, -4900, 50, 250); spawn.mapRect(1400, -4475, 650, 50); // ground spawn.mapVertex(-687, 1060, "700 0 -700 0 -450 -300 450 -300"); //left base spawn.mapVertex(863, 1060, "700 0 -700 0 -450 -300 450 -300"); //right base //entrance spawn.mapRect(-730, 525, 475, 50); spawn.mapRect(-730, 550, 50, 150); spawn.mapRect(-305, 550, 50, 500); spawn.bodyRect(-717, 700, 25, 100); //door spawn.bodyRect(-717, 800, 25, 100); //door //1st floor //left spawn.mapVertex(-1125 + 437, -50, "490 0 350 80 -350 80 -490 0 -350 -80 350 -80"); spawn.mapRect(-1225, -100, 1070, 100); if (Math.random() < 0.33) { spawn.mapVertex(-687, -1000, "-100 -300 0 -350 100 -300 100 300 0 350 -100 300"); } else if (Math.random() < 0.5) { spawn.mapVertex(-687, -1000, "-150 -450 0 -525 150 -450 150 450 0 525 -150 450"); } else { spawn.mapVertex(-687, -700, "-150 0 150 0 150 450 0 525 -150 450"); } //right spawn.mapVertex(425 + 437, -50, "490 0 350 80 -350 80 -490 0 -350 -80 350 -80"); spawn.mapRect(325, -100, 1070, 100); spawn.mapRect(175, 675, 425, 25); spawn.mapRect(1125, 225, 425, 25); spawn.mapRect(650, 450, 425, 25); if (Math.random() < 0.33) { spawn.mapVertex(855, -1000, "-100 -300 0 -350 100 -300 100 300 0 350 -100 300"); } else if (Math.random() < 0.5) { spawn.mapVertex(855, -1000, "-150 -450 0 -525 150 -450 150 450 0 525 -150 450"); } else { spawn.mapVertex(855, -700, "-150 0 150 0 150 450 0 525 -150 450"); } //2nd floor spawn.mapVertex(-687, -1936, "-625 50 0 100 625 50 625 -50 -625 -50"); spawn.mapVertex(855, -1936, "-625 50 0 100 625 50 625 -50 -625 -50"); //2nd floor right building spawn.mapRect(550, -3050, 600, 75); spawn.bodyRect(-125, -2025, 475, 25); spawn.mapRect(-925, -2350, 675, 50); spawn.mapRect(-825, -2825, 425, 50); spawn.mapRect(-450, -3125, 50, 350); spawn.mapRect(-750, -3150, 350, 50); spawn.mapRect(-650, -3400, 250, 300); spawn.mapRect(-650, -3675, 200, 50); spawn.bodyRect(-375, -2150, 100, 150, 0.2); //2nd floor left pillar spawn.mapRect(-1400, -2625, 325, 25); spawn.mapRect(-1450, -3225, 425, 25); spawn.mapRect(-1512.5, -3825, 550, 25); spawn.randomMob(1000, -275, 0.2); spawn.randomMob(950, -1725, 0.1); spawn.randomMob(-725, -1775, 0.1); spawn.randomMob(-200, -2075, 0); spawn.randomMob(-550, -3500, -0.2); spawn.randomMob(375, -2125, 0); spawn.randomMob(-700, -2450, -0.1); spawn.randomMob(-1175, -2775, -0.1); spawn.randomMob(1025, -3200, -0.2); spawn.randomMob(-525, -3750, -0.2); spawn.randomMob(1350, -2075, -0.3); spawn.randomMob(1775, 1000, -0.4); spawn.randomSmallMob(-575, -2925); spawn.randomGroup(-400, -4400, 0); if (simulation.difficulty > 1) { spawn.randomLevelBoss(825, -3500); spawn.secondaryBossChance(75, -1350) } powerUps.addResearchToLevel() //needs to run after mobs are spawned const slime = level.hazard(-2000, -5000, 4000, 6060); // hazard(x, y, width, height, damage = 0.003) slime.height -= slime.maxHeight - 60 //start slime at zero slime.min.y += slime.maxHeight slime.max.y = slime.min.y + slime.height const elevator1 = level.elevator(-1625, -90, 310, 800, -2000, 0.0025, { up: 0.1, down: 0.2 }) //x, y, width, height, maxHeight, force = 0.003, friction = { up: 0.01, down: 0.2 }) { const elevator2 = level.elevator(1175, -3050, 200, 250, -4475, 0.0025, { up: 0.12, down: 0.2 }) //x, y, width, height, maxHeight, force = 0.003, friction = { up: 0.01, down: 0.2 }) { let waterFallWidth = 0 let waterFallX = 0 let waterFallSmoothX = 0 let isWaterfallFilling = false const riseRate = 0.30 + Math.min(1, simulation.difficulty * 0.005) const spinnerArray = [] if (simulation.isHorizontalFlipped) { //flip the map horizontally spawn.mapVertex(584, -2500, "0 0 300 0 150 600 0 600"); spawn.mapVertex(1116, -2500, "0 0 300 0 300 600 150 600"); spawn.bodyRect(-200, -125, 625, 25); level.flipHorizontal(); //only flips map,body,mob,powerUp,cons,consBB, exit elevator1.holdX = -elevator1.holdX // flip the elevator horizontally elevator2.holdX = -elevator2.holdX // flip the elevator horizontally spinnerArray.push(level.spinner(-110, -3325, 45, 600, 0.003, 0, 0, 0.01)) // spinner(x, y, width, height, density = 0.001, angle = 0, frictionAir = 0.001, angularVelocity = 0) { const boost1 = level.boost(-900, -2000, 790) level.setPosToSpawn(500, 850); //normal spawn level.custom = () => { ctx.fillStyle = "#c0c3c9" ///!!!!!!!!!! for flipped x: newX = -oldX - width ctx.fillRect(1468, -1975, 2, 1915) //elevator track ctx.fillRect(-1274, -4460, 2, 1425) //elevator track ctx.fillRect(1225, -3825, 25, 1850); //small pillar background ctx.fillStyle = "#d0d4d6" ctx.fillRect(275, -1925, 825, 2925) //large pillar background ctx.fillRect(-1275, -1925, 825, 2925) //large pillar background ctx.fillStyle = "#cff" //exit ctx.fillRect(-2000, -4900, 525, 425) level.exit.drawAndCheck(); level.enter.draw(); }; level.customTopLayer = () => { boost1.query(); elevator1.move(); elevator2.move(); ctx.fillStyle = "#233" ctx.beginPath(); //central dot on spinners ctx.arc(spinnerArray[0].pointA.x, spinnerArray[0].pointA.y, 9, 0, 2 * Math.PI); for (let i = 0, len = spinnerArray.length; i < len; i++) { ctx.moveTo(spinnerArray[i].pointA.x, spinnerArray[i].pointA.y) ctx.arc(spinnerArray[i].pointA.x, spinnerArray[i].pointA.y, 9, 0, 2 * Math.PI); } ctx.fill(); //shadow ctx.fillStyle = "rgba(0,10,30,0.1)" ctx.fillRect(-1150, -3000, 600, 1025); ctx.fillRect(450, -3100, 300, 275); ctx.fillRect(450, -3625, 200, 225); ctx.fillRect(400, -2775, 425, 450); ctx.fillRect(250, -2300, 675, 300); slime.query(); if (isWaterfallFilling) { if (slime.height < 5500) { //draw slime fill ctx.fillStyle = `hsla(160, 100%, 43%,${0.3 + 0.07 * Math.random()})` ctx.fillRect(waterFallX, -5050, waterFallWidth, 6175 - slime.height) if (!m.isBodiesAsleep) { waterFallWidth = 0.98 * waterFallWidth + 4.7 * Math.random() waterFallSmoothX = 0.98 * waterFallSmoothX + 3.5 * Math.random() waterFallX = 1857 - waterFallSmoothX ctx.fillRect(waterFallX + waterFallWidth * Math.random(), -5050, 4, 6175 - slime.height) //push player down if they go under waterfall if (player.position.x > waterFallX && player.position.x < waterFallX + waterFallWidth && player.position.y < slime.height) { Matter.Body.setVelocity(player, { x: player.velocity.x, y: player.velocity.y + 2 }); } } slime.levelRise(riseRate) } } else if (Vector.magnitudeSquared(Vector.sub(player.position, level.enter)) > 100000) { isWaterfallFilling = true } }; } else { //not flipped spawn.mapVertex(1116, -2500, "0 0 300 0 150 600 0 600"); spawn.mapVertex(584, -2500, "0 0 300 0 300 600 150 600"); if (Math.random() < 0.1) { spinnerArray.push(level.spinner(65, -300, 40, 450, 0.003, Math.PI / 2)) } else if (Math.random() < 0.25) { spinnerArray.push(level.spinner(65, -500, 40, 500, 0.003, 0, 0, -0.015)) // spinner(x, y, width, height, density = 0.001, angle = 0, frictionAir = 0.001, angularVelocity = 0) { const r = 250 const hexagon = `${r} 0 ${r * Math.cos(5.236)} ${r * Math.sin(5.236)} ${r * Math.cos(4.189)} ${r * Math.sin(4.189)} ${-r} 0 ${r * Math.cos(2.0944)} ${r * Math.sin(2.0944)} ${r * Math.cos(1.0472)} ${r * Math.sin(1.0472)} ` Matter.Body.setVertices(spinnerArray[spinnerArray.length - 1].bodyB, Vertices.fromPath(hexagon)) } else { const W = 410; const H = 30; spawn.bodyRect(-120, -75, W, H, 1, spawn.propsIsNotHoldable) let b = body[body.length - 1]; cons[cons.length] = Constraint.create({ pointA: { x: b.position.x - (W / 2) + 50 - 211, y: b.position.y - 1825 }, bodyB: b, pointB: { x: -(W / 2) + 50, y: 0 }, damping: 0.01, stiffness: 0.002, length: 1800 }); cons[cons.length] = Constraint.create({ pointA: { x: b.position.x + (W / 2) - 50 + 211, y: b.position.y - 1825 }, bodyB: b, pointB: { x: (W / 2) - 50, y: 0 }, damping: 0.01, stiffness: 0.002, length: 1800 }); Composite.add(engine.world, [cons[cons.length - 1], cons[cons.length - 2]]) } spinnerArray.push(level.spinner(50, -3325, 45, 600, 0.003, 0, 0, 0.01)) // spinner(x, y, width, height, density = 0.001, angle = 0, frictionAir = 0.001, angularVelocity = 0) { if (Math.random() < 0.5) { const r = 200 const hexagon = `${r} 0 ${r * Math.cos(5.236)} ${r * Math.sin(5.236)} ${r * Math.cos(4.189)} ${r * Math.sin(4.189)} ${-r} 0 ${r * Math.cos(2.0944)} ${r * Math.sin(2.0944)} ${r * Math.cos(1.0472)} ${r * Math.sin(1.0472)} ` Matter.Body.setVertices(spinnerArray[spinnerArray.length - 1].bodyB, Vertices.fromPath(hexagon)) } const boost1 = level.boost(800, -2000, 790) level.custom = () => { ctx.fillStyle = "#c0c3c9" ctx.fillRect(-1470, -1975, 2, 1915) //elevator track ctx.fillRect(1276, -4460, 2, 1425) //elevator track ctx.fillRect(-1250, -3825, 25, 1850); //small pillar background ctx.fillStyle = "#d0d4d6" ctx.fillRect(-1100, -1925, 825, 2925) //large pillar background ctx.fillRect(450, -1925, 825, 2925) //large pillar background ctx.fillStyle = "#cff" //exit ctx.fillRect(1475, -4900, 525, 425) level.exit.drawAndCheck(); level.enter.draw(); }; level.customTopLayer = () => { boost1.query(); elevator1.move(); elevator2.move(); ctx.fillStyle = "#233" ctx.beginPath(); //central dot on spinners ctx.arc(spinnerArray[0].pointA.x, spinnerArray[0].pointA.y, 9, 0, 2 * Math.PI); for (let i = 0, len = spinnerArray.length; i < len; i++) { ctx.moveTo(spinnerArray[i].pointA.x, spinnerArray[i].pointA.y) ctx.arc(spinnerArray[i].pointA.x, spinnerArray[i].pointA.y, 9, 0, 2 * Math.PI); } ctx.fill(); //shadow ctx.fillStyle = "rgba(0,10,30,0.1)" ctx.fillRect(550, -3000, 600, 1025); ctx.fillRect(-750, -3100, 300, 275); ctx.fillRect(-650, -3625, 200, 225); ctx.fillRect(-825, -2775, 425, 450); ctx.fillRect(-925, -2300, 675, 300); slime.query(); if (isWaterfallFilling) { if (slime.height < 5500) { //draw slime fill ctx.fillStyle = `hsla(160, 100%, 43%,${0.3 + 0.07 * Math.random()})` ctx.fillRect(waterFallX, -5050, waterFallWidth, 6175 - slime.height) if (!m.isBodiesAsleep) { waterFallWidth = 0.98 * waterFallWidth + 4.7 * Math.random() waterFallSmoothX = 0.98 * waterFallSmoothX + 3.5 * Math.random() waterFallX = waterFallSmoothX - 1985 ctx.fillRect(waterFallX + waterFallWidth * Math.random(), -5050, 4, 6175 - slime.height) //push player down if they go under waterfall if (player.position.x > waterFallX && player.position.x < waterFallX + waterFallWidth && player.position.y < slime.height) { Matter.Body.setVelocity(player, { x: player.velocity.x, y: player.velocity.y + 2 }); } } slime.levelRise(riseRate) } } else if (Vector.magnitudeSquared(Vector.sub(player.position, level.enter)) > 100000) { isWaterfallFilling = true } }; } }, reactor() { level.exit.x = 3500; level.exit.y = -42; spawn.mapRect(level.exit.x, level.exit.y + 25, 100, 25); level.defaultZoom = 2000 simulation.zoomTransition(level.defaultZoom) document.body.style.backgroundColor = "#c3d6df" //"#d8dadf"; color.map = "#303639"; // powerUps.spawnStartingPowerUps(1475, -1175); // spawn.debris(750, -2200, 3700, 16); //16 debris per level spawn.bodyRect(250, -70, 100, 70, 1); spawn.mapRect(-425, 0, 4500, 2100); spawn.mapRect(-475, -2825, 4500, 1025); // spawn.mapRect(1200, -1300, 600, 800); const a = 400 //side length const c = 100 //corner offset spawn.mapVertex(1487, -900, `${-a} ${-a + c} ${-a + c} ${-a} ${a - c} ${-a} ${a} ${-a + c} ${a} ${a - c} ${a - c} ${a} ${-a + c} ${a} ${-a} ${a - c}`); //square with edges cut off //entrance spawn.mapRect(-2025, -2825, 1250, 4925); spawn.mapRect(-900, -2825, 1125, 1725); spawn.mapRect(-900, -750, 1125, 2850); spawn.mapRect(-325, -1250, 550, 300); //exit spawn.mapRect(3800, -2825, 1225, 4925); spawn.mapRect(2750, -2150, 1325, 1775); spawn.mapRect(2750, -475, 550, 300); spawn.mapRect(2750, -7, 1050, 150); //exit room floor const doorIn = level.door(-313, -950, 525, 200, 190, 2) //x, y, width, height, distance, speed = 1 const doorOut = level.door(2762, -175, 525, 200, 190, 2) //x, y, width, height, distance, speed = 1 doorIn.collisionFilter.category = cat.map; doorOut.collisionFilter.category = cat.map; // to prevent boson composite from letting the player skip the level // doorOut.isClosing = true let isDoorsLocked = false let isFightOver = false let isSpawnedBoss = false if (simulation.isHorizontalFlipped) { //flip the map horizontally level.flipHorizontal(); //only flips map,body,mob,powerUp,cons,consBB, exit level.setPosToSpawn(550, -800); //normal spawn spawn.mapRect(level.enter.x, level.enter.y + 20, 100, 20); const button = level.button(-1500, 0) button.isUp = true level.custom = () => { if (isDoorsLocked) { if (player.position.x > 300) { //if player gets trapped inside starting room open up again isDoorsLocked = false doorIn.isClosing = false } } doorIn.openClose(); doorOut.openClose(); ctx.fillStyle = "#d5ebef" ctx.fillRect(-3800, -375, 1050, 375) level.enter.draw(); level.exit.drawAndCheck(); button.draw(); if (button.isUp) { button.query(); } else if (!isSpawnedBoss) { if (player.position.x < 0) { if (!doorOut.isClosed() || !doorIn.isClosed()) { doorIn.isClosing = true doorOut.isClosing = true //block caught in a door if (Matter.Query.collides(doorOut, body).length > 1 || Matter.Query.collides(doorIn, body).length > 1) { button.isUp = true doorIn.isClosing = false doorOut.isClosing = false } } else { isSpawnedBoss = true isDoorsLocked = true for (let i = 0; i < 9; ++i) powerUps.spawn(-1800 + 550 * Math.random(), -1700, "ammo") for (let i = 0; i < 3; ++i) powerUps.spawn(-1800 + 550 * Math.random(), -1700, "heal"); const scale = Math.pow(simulation.difficulty, 0.7) //hard around 30, why around 54 if (mobs.mobDeaths < level.levelsCleared && !simulation.isCheating) { for (let i = 0; i < 250; i++) spawn.starter(-2700 + 2400 * Math.random(), -1300 - 500 * Math.random()) } else { if (Math.random() < 0.07 && simulation.difficulty > 35) { for (let i = 0, len = scale * 0.25 / 6; i < len; ++i) spawn.timeBoss(-1327 - 200 * i, -1525, 60, false); //spawn 1-2 at difficulty 15 for (let i = 0, len = scale * 0.1 / 6; i < len; ++i) spawn.bounceBoss(-1327 - 200 * i, -1525, 80, false); for (let i = 0, len = scale * 0.15 / 6; i < len; ++i) spawn.sprayBoss(-1327 - 200 * i, -1525, 30, false) for (let i = 0, len = scale * 0.26 / 6; i < len; ++i) spawn.mineBoss(-1327 - 200 * i, -1525, 50, false); } else { if (Math.random() < 0.25) { for (let i = 0, len = scale * 0.25; i < len; ++i) spawn.timeBoss(-1327 - 200 * i, -1525, 80, false); //spawn 1-2 at difficulty 15 } else if (Math.random() < 0.33) { for (let i = 0, len = scale * 0.1; i < len; ++i) spawn.bounceBoss(-1327 - 200 * i, -1525, 80, false); //spawn 1-2 at difficulty 15 } else if (Math.random() < 0.5) { for (let i = 0, len = scale * 0.15; i < len; ++i) spawn.sprayBoss(-1327 - 200 * i, -1525, 30, false) //spawn 2-3 at difficulty 15 } else { for (let i = 0, len = scale * 0.26; i < len; ++i) spawn.mineBoss(-1327 - 200 * i, -1525, 50, false); //spawn 3-4 at difficulty 15 } } } // spawn.secondaryBossChance(-2300, -800) } } else { doorIn.isClosing = false } } else if (!isFightOver && !(simulation.cycle % 180)) { let isFoundBoss = false for (let i = 0; i < mob.length; i++) { if (mob[i].isReactorBoss) { isFoundBoss = true break } } if (!isFoundBoss) { isFightOver = true doorIn.isClosing = false doorOut.isClosing = false // powerUps.spawnBossPowerUp(-3600, -100) powerUps.spawn(-3650, -50, "tech") powerUps.spawn(-3650, -150, "tech") powerUps.spawn(-3650, -300, "tech") // if (player.position.x < 2760 && player.position.x > 210) {} } } }; level.customTopLayer = () => { doorIn.draw(); doorOut.draw(); ctx.fillStyle = "rgba(0,0,0,0.1)" ctx.fillRect(-225, -1100, 1000, 350); }; } else { level.setPosToSpawn(-550, -800); //normal spawn spawn.mapRect(level.enter.x, level.enter.y + 20, 100, 20); const button = level.button(1400, 0) button.isUp = true level.custom = () => { if (isDoorsLocked) { if (player.position.x < -300) { //if player gets trapped inside starting room open up again isDoorsLocked = false doorIn.isClosing = false } } doorIn.openClose(); doorOut.openClose(); ctx.fillStyle = "#d5ebef" ctx.fillRect(2750, -375, 1050, 375) level.enter.draw(); level.exit.drawAndCheck(); button.draw(); if (button.isUp) { button.query(); } else if (!isSpawnedBoss) { if (player.position.x > 0) { if (!doorOut.isClosed() || !doorIn.isClosed()) { doorIn.isClosing = true doorOut.isClosing = true //block caught in a door if (Matter.Query.collides(doorOut, body).length > 1 || Matter.Query.collides(doorIn, body).length > 1) { button.isUp = true doorIn.isClosing = false doorOut.isClosing = false } } else { isSpawnedBoss = true isDoorsLocked = true for (let i = 0; i < 9; ++i) powerUps.spawn(1200 + 550 * Math.random(), -1700, "ammo") for (let i = 0; i < 3; ++i) powerUps.spawn(1200 + 550 * Math.random(), -1700, "heal"); const scale = Math.pow(simulation.difficulty, 0.7) //hard around 30, why around 54 if (mobs.mobDeaths < level.levelsCleared && !simulation.isCheating) { for (let i = 0; i < 250; i++) spawn.starter(300 + 2400 * Math.random(), -1300 - 500 * Math.random()) } else { if (Math.random() < 0.07 && simulation.difficulty > 35) { for (let i = 0, len = scale * 0.25 / 6; i < len; ++i) spawn.timeBoss(1487 + 200 * i, -1525, 60, false); //spawn 1-2 at difficulty 15 for (let i = 0, len = scale * 0.1 / 6; i < len; ++i) spawn.bounceBoss(1487 + 200 * i, -1525, 80, false); for (let i = 0, len = scale * 0.15 / 6; i < len; ++i) spawn.sprayBoss(1487 + 200 * i, -1525, 30, false) for (let i = 0, len = scale * 0.26 / 6; i < len; ++i) spawn.mineBoss(1487 + 200 * i, -1525, 50, false); } else { if (Math.random() < 0.25) { for (let i = 0, len = scale * 0.25; i < len; ++i) spawn.timeBoss(1487 + 200 * i, -1525, 80, false); //spawn 1-2 at difficulty 15 } else if (Math.random() < 0.33) { for (let i = 0, len = scale * 0.1; i < len; ++i) spawn.bounceBoss(1487 + 200 * i, -1525, 80, false); //spawn 1-2 at difficulty 15 } else if (Math.random() < 0.5) { for (let i = 0, len = scale * 0.15; i < len; ++i) spawn.sprayBoss(1487 + 200 * i, -1525, 30, false) //spawn 2-3 at difficulty 15 } else { for (let i = 0, len = scale * 0.26; i < len; ++i) spawn.mineBoss(1487 + 200 * i, -1525, 50, false); //spawn 3-4 at difficulty 15 } } } // spawn.secondaryBossChance(2200, -800) } } else { doorIn.isClosing = false } } else if (!isFightOver && !(simulation.cycle % 180)) { let isFoundBoss = false for (let i = 0; i < mob.length; i++) { if (mob[i].isBoss) { isFoundBoss = true break } } if (!isFoundBoss) { isFightOver = true doorIn.isClosing = false doorOut.isClosing = false // powerUps.spawnBossPowerUp(3600, -100) powerUps.spawn(3650, -50, "tech") powerUps.spawn(3650, -150, "tech") powerUps.spawn(3650, -300, "tech") // if (player.position.x < 2760 && player.position.x > 210) {} } } }; level.customTopLayer = () => { doorIn.draw(); doorOut.draw(); ctx.fillStyle = "rgba(0,0,0,0.1)" ctx.fillRect(-775, -1100, 1000, 350); }; } // if (simulation.difficulty > 1) spawn.randomLevelBoss(2200, -1300); powerUps.addResearchToLevel() //needs to run after mobs are spawned }, factory() { // simulation.enableConstructMode() //remove this!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! // level.difficultyIncrease(10 * 4) //30 is near max on hard //60 is near max on why level.setPosToSpawn(2235, -1375); //normal spawn spawn.mapRect(level.enter.x, level.enter.y + 20, 100, 20); //bump for level entrance level.exit.x = 7875; level.exit.y = -2480; spawn.mapRect(level.exit.x, level.exit.y + 20, 100, 20); //bump for level exit level.defaultZoom = 1500 simulation.zoomTransition(level.defaultZoom) document.body.style.backgroundColor = "#d0d2d4s"; // color.map = "#262a2f" let isPowerLeft = true const movers = [] //left side movers.push(level.mover(125, -140, 925, 35, -5)) movers.push(level.mover(1100, -437, 1100, 35, -5)) movers.push(level.mover(2000, -600, 850, 35, -5)) //right side const moveSpeedStopGo = 8 movers.push(level.mover(2700, -200, 3600, 35, 0)) movers.push(level.mover(7175, -215, 2275, 50, 3)) movers.push(level.mover(6475, -215, 275, 100, -3)) movers.push(level.mover(6725, -500, 500, 375, 3)) movers.push(level.mover(7675, -725, 500, 410, 0)) movers.push(level.mover(6775, -1075, 375, 50, 0)) movers.push(level.mover(5525, -1075, 450, 50, 0)) movers.push(level.mover(6775, -2100, 375, 50, 0)) movers.push(level.mover(5450, -1900, 525, 50, 0)) function setMoverDirection(VxGoal) { for (let i = 7; i < movers.length; i++) movers[i].VxGoal = VxGoal } setMoverDirection(0) const buttonRight = level.button(7735, -1825) buttonRight.isUp = true const buttonLeft = level.button(5275, -1900) const lasers = [] const laserX = 3390 //3882 - 1130 / 2 const laserGap = 1295 //1130 lasers.push(level.hazard(laserX, -500, 6, 300, 0.4)) lasers.push(level.hazard(laserX + laserGap, -500, 6, 300, 0.4)) lasers.push(level.hazard(laserX + laserGap * 2, -500, 6, 300, 0.4)) for (let i = 0; i < lasers.length; i++) { lasers[i].isOn = false; spawn.mapRect(lasers[i].min.x - 55, -550, 110, 50); spawn.mapRect(lasers[i].min.x - 10, -500, 25, 20); } const button1 = level.button(2235, -200) button1.isUp = true let bonusAmmoCount = 0 level.custom = () => { if (isPowerLeft) { if (!(simulation.cycle % 90)) spawn.bodyRect(2730, -1600, 50, 50); } else { // for (let i = 0; i < trains.length; i++) { // //oscillate back and forth // if (trains[i].position.x < 5275) { // trains[i].changeDirection(true) //go right // } else if (trains[i].position.x > 7875) { // trains[i].changeDirection(false) //go left // } // trains[i].move(); // } const rate = 160 //multiples of 32! if ((simulation.cycle % rate) === 80) { for (let i = 0; i < lasers.length; i++) lasers[i].isOn = false; movers[3].VxGoal = moveSpeedStopGo; movers[3].force = 0.0005 movers[2].VxGoal = moveSpeedStopGo; movers[2].force = 0.0005 } else if ((simulation.cycle % rate) === 0) { movers[3].VxGoal = 0; movers[3].force = 0 movers[2].VxGoal = 0; movers[2].force = 0 spawn.bodyRect(2730, -1600, 50, 50); if ((simulation.cycle % (rate * 3)) === 0) { if (bonusAmmoCount < 3 && Math.random() < 0.5) { //some extra ammo because of all the extra mobs that don't drop ammo bonusAmmoCount++ powerUps.spawn(2760, -1550, Math.random() < 0.5 ? "heal" : "ammo", false); } for (let i = 0; i < lasers.length; i++) lasers[i].isOn = true; const block2Mob = (laserIndex) => { //convert block into mob const laserHit = Matter.Query.ray(body, lasers[laserIndex].min, lasers[laserIndex].max) //check for collisions with 3rd laser if (laserHit.length) { for (let i = 0; i < body.length; i++) { if (laserHit[0].body.id === body[i].id) { //need to find the block id so it can be removed const list = ["flutter", "flutter", "flutter", "hopper", "slasher", "slasher", "slasher", "stabber", "springer", "striker", "sneaker", "launcher", "launcherOne", "exploder", "sucker", "spinner", "grower", "beamer", "spawner", "ghoster"] const pick = list[Math.floor(Math.random() * list.length)] spawn[pick](lasers[laserIndex].max.x, lasers[laserIndex].max.y - 20); const who = mob[mob.length - 1] Matter.Body.setVelocity(who, { x: (8 + 5 * Math.random()), y: -(14 + 10 * Math.random()) }); who.locatePlayer() who.leaveBody = false; who.isDropPowerUp = false //remove block Matter.Composite.remove(engine.world, body[i]); body.splice(i, 1); break } } } } if (mob.length < 100 && !m.isBodiesAsleep) { block2Mob(0) block2Mob(1) block2Mob(2) } } } } if (buttonLeft.isUp) { buttonLeft.query(); if (!buttonLeft.isUp) { setMoverDirection(7) buttonRight.isUp = true //flip the other button up } } else if (buttonRight.isUp) { buttonRight.query(); if (!buttonRight.isUp) { setMoverDirection(-7) //check for blocks and remove them const list = Matter.Query.region(body, buttonLeft) //are any blocks colliding with this buttonLeft.isUp = true //flip the other button up if (list.length > 0) { list[0].isRemoveMeNow = true for (let i = 1; i < body.length; i++) { //find which index in body array it is and remove from array if (body[i].isRemoveMeNow) { Matter.Composite.remove(engine.world, list[0]); body.splice(i, 1); break } } } } } if (button1.isUp) { button1.query(); if (!button1.isUp) { isPowerLeft = false for (let i = 0; i < 3; i++) { movers[i].VxGoal = 0; movers[i].force = movers[i].VxGoal > 0 ? 0.0005 : -0.0005 } powerUps.spawnStartingPowerUps(2760, -1550); spawn.randomMob(2700, -350, 0.2); spawn.randomMob(6975, -650, 0.2); spawn.randomMob(6550, -325, 0.3); spawn.randomMob(7350, -350, 0.3); spawn.randomMob(7925, -975, 0.5); spawn.randomMob(7950, -1725, 0.5); spawn.randomMob(7000, -1375, 0.3); spawn.randomMob(5700, -1350, 0.5); spawn.randomMob(5250, -1575, 0.5); spawn.randomMob(6325, -75, 0.3); spawn.randomMob(7900, -1925, 0.1); spawn.randomMob(5300, -1975, 0.3); spawn.randomMob(7875, -1900, 0.3); spawn.randomMob(5325, -1975, 0.4); spawn.randomGroup(3900, -725, 0.4); if (simulation.difficulty > 1) spawn.randomLevelBoss(6501, -1771); spawn.secondaryBossChance(6063, -661) powerUps.addResearchToLevel() //needs to run after mobs are spawned } } buttonRight.draw(); buttonLeft.draw(); button1.draw(); for (let i = 0; i < movers.length; i++) movers[i].push(); level.exit.drawAndCheck(); level.enter.draw(); ctx.fillStyle = "rgba(0,0,0,0.1)" ctx.fillRect(6937, -2075, 50, 1775); //6937, -1050, 50, 675); ctx.fillStyle = "rgba(0,255,255,0.15)" // ctx.fillStyle = "#f2f2f2" ctx.fillRect(7675, -2875, 500, 425); //exit room }; level.customTopLayer = () => { if (isPowerLeft) { ctx.fillStyle = "rgba(0,0,0,0.2)" ctx.fillRect(2400, -1650, 7050, 2750) //right side ctx.fillRect(4950, -3075, 3225, 1425); ctx.beginPath() ctx.moveTo(2407, -576); ctx.lineTo(2000, -573) ctx.lineTo(1950, -439) ctx.lineTo(1100, -432) ctx.lineTo(1020, -143) ctx.lineTo(125, -137) ctx.lineTo(-109, 300) ctx.lineTo(-125, 1089) ctx.lineTo(2372, 1081) ctx.lineTo(2452, 65) ctx.fill(); } else { // for (let i = 0; i < trains.length; i++) trains[i].draw() ctx.beginPath() ctx.moveTo(2526, -589); ctx.lineTo(2531, -597) ctx.lineTo(2506, -594) ctx.lineTo(2850, -600) ctx.lineTo(2890, -193) ctx.lineTo(6300, -200) ctx.lineTo(6618, 857) ctx.lineTo(6622, 1100) ctx.lineTo(2521, 1100) ctx.fillStyle = "rgba(0,0,0,0.2)" ctx.fill(); ctx.fillRect(-100, -1650, 2625, 2750) //left side for (let i = 0; i < lasers.length; i++) lasers[i].opticalQuery() } ctx.fillStyle = "rgba(0,0,0,0.07)" ctx.fillRect(7675, -2200, 1775, 2025); ctx.fillRect(4950, -2075, 500, 1000); ctx.fillRect(2050, -1650, 350, 325) //entrance room for (let i = 0; i < movers.length; i++) movers[i].draw(); }; spawn.mapRect(-1550, -3050, 1450, 4150); //left wall spawn.mapRect(-1550, -3050, 6525, 1400); //ceiling spawn.mapRect(-1550, -3050, 6525, 1400); spawn.mapRect(3000, -1700, 1975, 675); //ceiling center spawn.mapRect(3800, -4000, 5650, 950); spawn.mapRect(3800, -4000, 1175, 2975); spawn.mapRect(8175, -4000, 1275, 3685); //right wall spawn.mapRect(8175, -200, 1275, 1300); //right wall spawn.mapRect(75, 0, 6275, 1100); //ground spawn.mapRect(6475, -200, 2750, 1300); spawn.mapRect(4975, -1087, 550, 62); spawn.mapRect(4975, -1100, 500, 75); spawn.mapRect(7875, -1100, 175, 25); //right 3 hop stairs spawn.mapRect(8075, -1450, 200, 25); spawn.mapRect(7675, -1825, 375, 25); spawn.mapRect(7675, -1800, 250, 725); spawn.mapRect(5125, -1275, 200, 25); //left 3 hop stairs spawn.mapRect(4900, -1575, 175, 25); spawn.mapRect(5125, -1900, 325, 25); spawn.mapRect(5225, -1875, 225, 625); spawn.mapRect(4950, -3075, 500, 1000); //exit spawn.mapRect(7675, -2450, 525, 250); spawn.mapRect(7675, -3050, 550, 175); spawn.mapRect(7675, -2925, 50, 175); spawn.mapRect(1925, -1325, 550, 50); //entrance spawn.mapRect(2050, -1675, 50, 175); //entrance spawn.mapRect(1700, -200, 750, 275); //button shelf if (Math.random() < 0.5) { //left side spawn.mapRect(625, -1100, 425, 300); spawn.mapRect(1375, -1100, 425, 300); spawn.mapRect(1750, -835, 100, 35); spawn.mapRect(-200, -525, 150, 35); } else { spawn.mapRect(800, -1125, 925, 400); spawn.mapRect(75, -775, 400, 50); spawn.mapRect(1700, -760, 75, 35); spawn.mapRect(-200, -425, 150, 35); } spawn.mapRect(2400, -600, 125, 675); spawn.mapRect(2400, -1750, 125, 1050); spawn.mapRect(2700, -1700, 125, 85); spawn.randomMob(350, -325, 0.5); spawn.randomMob(875, -375, 0.5); spawn.randomMob(1250, -575, 0.5); spawn.randomMob(1550, -600, 0.5); spawn.randomSmallMob(1250, -175); spawn.randomSmallMob(1500, -229); spawn.randomSmallMob(1850, -300); powerUps.spawn(5200, -1300, "ammo"); }, labs() { level.isProcedural = true //used in generating text for the level builder level.defaultZoom = 1700 simulation.zoomTransition(level.defaultZoom) document.body.style.backgroundColor = "#d9d9de" //"#d3d3db" //"#dcdcdf"; let isDoorLeft, isDoorRight, x, y doCustom = [] doCustomTopLayer = [] offset = { x: 0, y: 0 } const mobSpawnChance = 0 // Math.random() < chance + 0.07 * simulation.difficulty enterOptions = [ (x = offset.x, y = offset.y) => { //lasers level.setPosToSpawn(x + 1750, y - 800); spawn.mapRect(level.enter.x, level.enter.y + 20, 100, 20); spawn.mapRect(x + 1450, y - 1350, 50, 450); //entrance left wall spawn.bodyRect(x + 1460, y - 900, 30, 150); //entrance door spawn.mapRect(x + 1600, y - 350, 500, 100); //toggle shelf const toggle = level.toggle(x + 1650, y - 350, true) //(x,y,isOn,isLockOn = true/false) let hazard1 if (Math.random() > 0.5) { spawn.mapRect(x + 550, y - 750, 1500, 50); //entrance shelf hazard1 = level.hazard(x + 850, y - 920, 600, 10, 0.4) //laser spawn.mapRect(x + 860, y - 925, 10, 20); //laser nose spawn.mapRect(x + 660, y - 975, 200, 120); //laser body } else { spawn.mapRect(x + 1350, y - 750, 700, 50); //entrance shelf hazard1 = level.hazard(x + 1040, y - 660, 1000, 10, 0.4) //laser spawn.mapRect(x + 1050, y - 665, 10, 20); //laser nose spawn.mapRect(x + 650, y - 705, 400, 100); //laser body } const hazard2 = level.hazard(x, y - 330, 450, 10, 0.4) //laser spawn.mapRect(x + 440, y - 335, 10, 20); //laser nose spawn.mapRect(x + 450, y - 375, 400, 100); //laser body //exit hazards const Xoffset = Math.floor(400 * Math.random()) const hazard3 = level.hazard(x + Xoffset, y - 1300, 10, 1300, 0.4) //laser spawn.mapRect(x + Xoffset - 5, y - 1310, 20, 20); //laser nose const Xoffset2 = 1650 + Math.floor(300 * Math.random()) const hazard4 = level.hazard(x + Xoffset2, y - 240, 10, 250, 0.4) //laser spawn.mapRect(x + Xoffset2 - 5, y - 250, 20, 20); //laser nose spawn.randomMob(x + 150, y + -1100, mobSpawnChance); spawn.randomMob(x + 175, y + -775, mobSpawnChance); spawn.randomMob(x + 150, y + -350, mobSpawnChance); spawn.randomMob(x + 150, y + -75, mobSpawnChance); spawn.randomMob(x + 650, y + -125, mobSpawnChance); spawn.randomMob(x + 1200, y + -75, mobSpawnChance); // let isSpawnedMobs = false doCustomTopLayer.push( () => { toggle.query(); hazard1.isOn = toggle.isOn hazard2.isOn = toggle.isOn hazard3.isOn = toggle.isOn hazard4.isOn = toggle.isOn if ((simulation.cycle % 120) > 60) { hazard1.opticalQuery(); hazard2.opticalQuery(); } else { hazard3.opticalQuery(); hazard4.opticalQuery(); } // if (!isSpawnedMobs && !toggle.isOn) { // isSpawnedMobs = true // spawn.randomMob(x + 150, y + -1100, mobSpawnChance); // spawn.randomMob(x + 175, y + -775, mobSpawnChance); // spawn.randomMob(x + 150, y + -350, mobSpawnChance); // spawn.randomMob(x + 150, y + -75, mobSpawnChance); // spawn.randomMob(x + 650, y + -125, mobSpawnChance); // spawn.randomMob(x + 1200, y + -75, mobSpawnChance); // } } ) }, ] exitOptions = [ (x = offset.x, y = offset.y) => { level.exit.x = x + 1725; level.exit.y = y - 980; spawn.mapRect(level.exit.x, level.exit.y + 20, 100, 20); spawn.mapRect(x + 1500, y - 950, 500, 25); //exit platform spawn.mapRect(x + 1550, y - 1300, 25, 175); //exit side wall spawn.mapVertex(x + 1300, y - 125, "-400 0 -250 -400 250 -400 400 0"); spawn.bodyRect(x + 1075, y - 475, 125, 125, 0.25); spawn.bodyRect(x + 500, y - 100, 125, 100, 0.25); spawn.bodyRect(x + 200, y - 150, 100, 150, 0.25); spawn.bodyRect(x + 1075, y - 1075, 100, 125, 0.25); const density = 0.0015 //+ (simulation.difficultyMode < 5 ? 0.0035 : 0) const angle = Math.PI / 2 const variance = 0 //Math.PI const frictionAir = 0.03 const angularVelocity = 0 //0.01 const spinVariance = 0 //0.02 balance1 = level.spinner(x + 200, y - 500, 30, 400, density, angle + variance * (Math.random() - 0.5), frictionAir, angularVelocity + spinVariance * (Math.random() - 0.5)) // spinner(x, y, width, height, density = 0.001, angle=0,frictionAir=0.001,angularVelocity=0) { balance2 = level.spinner(x + 200, y - 950, 30, 400, density, angle + variance * (Math.random() - 0.5), frictionAir, angularVelocity + spinVariance * (Math.random() - 0.5)) balance3 = level.spinner(x + 650, y - 750, 30, 400, density, angle + variance * (Math.random() - 0.5), frictionAir, angularVelocity + spinVariance * (Math.random() - 0.5)) // balance4 = level.spinner(x + 750, y - 1050, 25, 350, density, angle + variance * (Math.random() - 0.5), frictionAir, angularVelocity + spinVariance * (Math.random() - 0.5)) balance4 = level.spinner(x + 1250, y - 1000, 30, 400, density, angle + variance * (Math.random() - 0.5), frictionAir, angularVelocity + spinVariance * (Math.random() - 0.5)) let isInRoom = false doCustom.push( () => { if (!isInRoom && m.pos.x > x - 100 && m.pos.x < x + 2700 && m.pos.y > y - 1300 && m.pos.y < y) { //check if player is in this room and run code once isInRoom = true spawn.randomMob(x + 1175, y - 725, mobSpawnChance); spawn.randomMob(x + 1450, y - 725, mobSpawnChance); spawn.randomMob(x + 425, y - 100, mobSpawnChance); spawn.randomMob(x + 1700, y - 300, mobSpawnChance); spawn.randomMob(x + 1300, y - 375, mobSpawnChance); } ctx.fillStyle = "#d4f4f4" ctx.fillRect(x + 1550, y - 1300, 450, 350) } ) doCustomTopLayer.push( () => { ctx.fillStyle = "#233" ctx.beginPath(); ctx.arc(balance1.pointA.x, balance1.pointA.y, 9, 0, 2 * Math.PI); ctx.moveTo(balance2.pointA.x, balance2.pointA.y) ctx.arc(balance2.pointA.x, balance2.pointA.y, 9, 0, 2 * Math.PI); ctx.moveTo(balance3.pointA.x, balance3.pointA.y) ctx.arc(balance3.pointA.x, balance3.pointA.y, 9, 0, 2 * Math.PI); ctx.moveTo(balance4.pointA.x, balance4.pointA.y) ctx.arc(balance4.pointA.x, balance4.pointA.y, 9, 0, 2 * Math.PI); ctx.fill(); } ) }, (x = offset.x, y = offset.y) => { level.exit.x = x + 1750; level.exit.y = y - 980; spawn.mapRect(level.exit.x, level.exit.y + 20, 100, 20); spawn.mapRect(x + 1550, y - 950, 500, 25); //exit platform spawn.mapRect(x + 1600, y - 1300, 25, 175); //exit side wall spawn.bodyRect(x + 1275, y - 475, 125, 125, 0.25); spawn.bodyRect(x + 500, y - 100, 125, 100, 0.25); spawn.bodyRect(x + 800, y - 150, 100, 150, 0.25); spawn.bodyRect(x + 875, y + -50, 50, 50); spawn.bodyRect(x + 1025, y + -50, 50, 50); if (Math.random() > 0.5) { const density = 0.0012 //+ (simulation.difficultyMode < 5 ? 0.003 : 0) const angle = Math.PI / 2 const variance = 0.2 //Math.PI const frictionAir = 0.015 const height = 35 balance1 = level.spinner(x + 1300, y - 425, height, 410, density, angle + variance * (Math.random() - 0.5), frictionAir) // spinner(x, y, width, height, density = 0.001, angle=0,frictionAir=0.001,angularVelocity=0) { balance3 = level.spinner(x + 750, y - 650, height, 410, density, angle + variance * (Math.random() - 0.5), frictionAir) balance2 = level.spinner(x + 300, y - 425, height, 410, density, angle + variance * (Math.random() - 0.5), frictionAir) balance4 = level.spinner(x + 1250, y - 950, 50, 550, density, angle, 0.1) const rotatingBlock = body[body.length - 1] doCustom.push( () => { if (!isInRoom && m.pos.x > x - 100 && m.pos.x < x + 2700 && m.pos.y > y - 1300 && m.pos.y < y) { //check if player is in this room and run code once isInRoom = true spawn.randomMob(x + 1175, y - 725, mobSpawnChance); spawn.randomMob(x + 1450, y - 725, mobSpawnChance); spawn.randomMob(x + 425, y - 100, mobSpawnChance); spawn.randomMob(x + 1200, y - 125, mobSpawnChance); spawn.randomMob(x + 1300, y - 375, mobSpawnChance); } ctx.fillStyle = "#d4f4f4" ctx.fillRect(x + 1600, y - 1300, 400, 350) rotatingBlock.torque += rotatingBlock.inertia * 0.000005 } ) } else { const density = 0.001 //+ (simulation.difficultyMode < 5 ? 0.003 : 0) const angle = Math.PI / 2 const variance = Math.PI const frictionAir = 0.015 const width = 200 const height = 200 const spinVariance = 0.05 balance1 = level.spinner(x + 175, y - 300, height, width, density, angle + variance * (Math.random() - 0.5), frictionAir, spinVariance * (Math.random() - 0.5)) // spinner(x, y, width, height, density = 0.001, angle=0,frictionAir=0.001,angularVelocity=0) { balance2 = level.spinner(x + 500, y - 525, height, width, density, angle + variance * (Math.random() - 0.5), frictionAir, spinVariance * (Math.random() - 0.5)) balance3 = level.spinner(x + 850, y - 700, height, width, density, angle + variance * (Math.random() - 0.5), frictionAir, spinVariance * (Math.random() - 0.5)) balance4 = level.spinner(x + 1250, y - 850, height, width, density, angle + variance * (Math.random() - 0.5), frictionAir, spinVariance * (Math.random() - 0.5)) doCustom.push( () => { if (!isInRoom && m.pos.x > x - 100 && m.pos.x < x + 2700 && m.pos.y > y - 1300 && m.pos.y < y) { //check if player is in this room and run code once isInRoom = true spawn.randomMob(x + 1175, y - 725, mobSpawnChance); spawn.randomMob(x + 1450, y - 725, mobSpawnChance); spawn.randomMob(x + 425, y - 100, mobSpawnChance); spawn.randomMob(x + 1200, y - 125, mobSpawnChance); spawn.randomMob(x + 1300, y - 375, mobSpawnChance); } ctx.fillStyle = "#d4f4f4" ctx.fillRect(x + 1600, y - 1300, 400, 350) } ) } let isInRoom = false doCustomTopLayer.push( () => { ctx.fillStyle = "#233" ctx.beginPath(); ctx.arc(balance1.pointA.x, balance1.pointA.y, 9, 0, 2 * Math.PI); ctx.moveTo(balance2.pointA.x, balance2.pointA.y) ctx.arc(balance2.pointA.x, balance2.pointA.y, 9, 0, 2 * Math.PI); ctx.moveTo(balance3.pointA.x, balance3.pointA.y) ctx.arc(balance3.pointA.x, balance3.pointA.y, 9, 0, 2 * Math.PI); ctx.moveTo(balance4.pointA.x, balance4.pointA.y) ctx.arc(balance4.pointA.x, balance4.pointA.y, 9, 0, 2 * Math.PI); ctx.fill(); } ) } ] emptyOptions = [ //nothing good here except the starting power up, and duplicated bosses (x = offset.x, y = offset.y) => { //pulse if (!isDoorLeft && isDoorRight) { //flipped, entering from the right powerUps.spawnStartingPowerUps(x + 2000 - 1650, y + -400); spawn.mapRect(x + 2000 - 1575 - 25, y + -625, 25, 375); //wall on top of wall spawn.mapRect(x + 2000 - 1575 - 25, y + -1325, 25, 525); //wall on top of wall spawn.mapRect(x + 2000 - 1525 - 250, y + -350, 250, 450); //wall spawn.mapRect(x + 2000 - 245 - 300, y + -200, 300, 100); //gun spawn.mapRect(x + 2000 - 530 - 25, y + -190, 25, 80); //gun nose const button = level.button(x + 2000 - 290 - 140, y - 200) button.isReadyToFire = true doCustom.push( () => { ctx.fillStyle = "rgba(0,0,0,0.05)"; //"rgba(0,0,0,0.1)"; ctx.fillRect(x + 2000 - 255 - 280, y + -100, 280, 100); button.query(); button.draw(); if (!button.isReadyToFire && button.isUp) { button.isReadyToFire = true } else if (button.isReadyToFire && !button.isUp) { button.isReadyToFire = false b.pulse(90, Math.PI, { x: x + 2000 - 560, y: y - 150 }) } } ) spawn.randomMob(x + 2000 - 1600, y + -425, mobSpawnChance); spawn.randomMob(x + 2000 - 1725, y + -1250, mobSpawnChance); spawn.randomMob(x + 2000 - 1250, y + -1200, mobSpawnChance); spawn.randomMob(x + 2000 - 300, y + -1200, mobSpawnChance); spawn.randomMob(x + 2000 - 800, y + -125, mobSpawnChance); let pick = spawn.pickList[Math.floor(Math.random() * spawn.pickList.length)]; spawn[pick](x + 2000 - 1275, y + -150, 90 + Math.random() * 40); //one extra large mob } else { powerUps.spawnStartingPowerUps(x + 1650, y + -400); spawn.mapRect(x + 1575, y + -625, 25, 375); //wall on top of wall spawn.mapRect(x + 1575, y + -1325, 25, 525); //wall on top of wall spawn.mapRect(x + 1525, y + -350, 250, 450); //wall spawn.mapRect(x + 245, y + -200, 300, 100); //gun spawn.mapRect(x + 530, y + -190, 25, 80); //gun nose const button = level.button(x + 290, y - 200) button.isReadyToFire = true doCustom.push( () => { ctx.fillStyle = "rgba(0,0,0,0.05)"; //"rgba(0,0,0,0.1)"; ctx.fillRect(x + 255, y + -100, 280, 100); button.query(); button.draw(); if (!button.isReadyToFire && button.isUp) { button.isReadyToFire = true } else if (button.isReadyToFire && !button.isUp) { button.isReadyToFire = false b.pulse(90, 0, { x: x + 560, y: y - 150 }) } } ) spawn.randomMob(x + 1600, y + -425, mobSpawnChance); spawn.randomMob(x + 1725, y + -1250, mobSpawnChance); spawn.randomMob(x + 1250, y + -1200, mobSpawnChance); spawn.randomMob(x + 300, y + -1200, mobSpawnChance); spawn.randomMob(x + 800, y + -125, mobSpawnChance); let pick = spawn.pickList[Math.floor(Math.random() * spawn.pickList.length)]; spawn[pick](x + 1275, y + -150, 90 + Math.random() * 40); //one extra large mob } }, (x = offset.x, y = offset.y) => { //spawn block and fire it if (!isDoorLeft && isDoorRight) { powerUps.spawnStartingPowerUps(x + 1650, y + -400); spawn.mapRect(x + 2000 - 1575 - 25, y + -625, 25, 375); //wall on top of wall spawn.mapRect(x + 2000 - 1575 - 25, y + -1325, 25, 525); //wall on top of wall spawn.mapRect(x + 2000 - 1525 - 250, y + -350, 250, 450); //wall spawn.mapRect(x + 2000 - 245 - 300, y + -200, 300, 100); //gun spawn.mapRect(x + 2000 - 530 - 25, y + -190, 25, 80); const button = level.button(x + 2000 - 290 - 140, y - 200) button.isReadyToFire = true doCustom.push( () => { ctx.fillStyle = "rgba(0,0,0,0.05)"; //"rgba(0,0,0,0.1)"; ctx.fillRect(x + 2000 - 255 - 280, y + -100, 280, 100); button.query(); button.draw(); if (!button.isReadyToFire && button.isUp) { button.isReadyToFire = true } else if (button.isReadyToFire && !button.isUp) { button.isReadyToFire = false fireBlock = function (xPos, yPos) { const index = body.length spawn.bodyRect(xPos, yPos, 35 + 50 * Math.random(), 35 + 50 * Math.random()); const bodyBullet = body[body.length - 1] Matter.Body.setVelocity(body[index], { x: -120, y: -5 }); body[index].isAboutToBeRemoved = true; setTimeout(() => { //remove block for (let i = 0; i < body.length; i++) { if (body[i] === bodyBullet) { Matter.Composite.remove(engine.world, body[i]); body.splice(i, 1); } } }, 1000); } fireBlock(x + 2000 - 90 - 560 + 30 * Math.random(), y - 140); fireBlock(x + 2000 - 90 - 560 + 30 * Math.random(), y - 160); fireBlock(x + 2000 - 90 - 560 + 30 * Math.random(), y - 180); fireBlock(x + 2000 - 90 - 560 + 30 * Math.random(), y - 200); fireBlock(x + 2000 - 90 - 560 + 30 * Math.random(), y - 220); fireBlock(x + 2000 - 90 - 560 + 30 * Math.random(), y - 240); } } ) spawn.randomMob(x + 2000 - 1600, y + -425, mobSpawnChance); spawn.randomMob(x + 2000 - 1725, y + -1250, mobSpawnChance); spawn.randomMob(x + 2000 - 1250, y + -1200, mobSpawnChance); spawn.randomMob(x + 2000 - 300, y + -1200, mobSpawnChance); spawn.randomMob(x + 2000 - 800, y + -125, mobSpawnChance); let pick = spawn.pickList[Math.floor(Math.random() * spawn.pickList.length)]; spawn[pick](x + 2000 - 1275, y + -150, 90 + Math.random() * 40); //one extra large mob } else { powerUps.spawnStartingPowerUps(x + 1650, y + -400); spawn.mapRect(x + 1575, y + -625, 25, 375); //wall on top of wall spawn.mapRect(x + 1575, y + -1325, 25, 525); //wall on top of wall spawn.mapRect(x + 1525, y + -350, 250, 450); //wall spawn.mapRect(x + 245, y + -200, 300, 100); //gun spawn.mapRect(x + 530, y + -190, 25, 80); const button = level.button(x + 290, y - 200) button.isReadyToFire = true doCustom.push( () => { ctx.fillStyle = "rgba(0,0,0,0.05)"; //"rgba(0,0,0,0.1)"; ctx.fillRect(x + 255, y + -100, 280, 100); button.query(); button.draw(); if (!button.isReadyToFire && button.isUp) { button.isReadyToFire = true } else if (button.isReadyToFire && !button.isUp) { button.isReadyToFire = false fireBlock = function (xPos, yPos) { const index = body.length spawn.bodyRect(xPos, yPos, 35 + 50 * Math.random(), 35 + 50 * Math.random()); const bodyBullet = body[body.length - 1] Matter.Body.setVelocity(body[index], { x: 120, y: -5 }); body[index].isAboutToBeRemoved = true; setTimeout(() => { //remove block for (let i = 0; i < body.length; i++) { if (body[i] === bodyBullet) { Matter.Composite.remove(engine.world, body[i]); body.splice(i, 1); } } }, 1000); } fireBlock(x + 560 + 30 * Math.random(), y - 140); fireBlock(x + 560 + 30 * Math.random(), y - 160); fireBlock(x + 560 + 30 * Math.random(), y - 180); fireBlock(x + 560 + 30 * Math.random(), y - 200); fireBlock(x + 560 + 30 * Math.random(), y - 220); fireBlock(x + 560 + 30 * Math.random(), y - 240); } } ) spawn.randomMob(x + 1600, y + -425, mobSpawnChance); spawn.randomMob(x + 1725, y + -1250, mobSpawnChance); spawn.randomMob(x + 1250, y + -1200, mobSpawnChance); spawn.randomMob(x + 300, y + -1200, mobSpawnChance); spawn.randomMob(x + 800, y + -125, mobSpawnChance); let pick = spawn.pickList[Math.floor(Math.random() * spawn.pickList.length)]; spawn[pick](x + 1275, y + -150, 90 + Math.random() * 40); //one extra large mob } }, (x = offset.x, y = offset.y) => { //fire an "ammo clip" of blocks if (!isDoorLeft && isDoorRight) { //flipped, entering from the right powerUps.spawnStartingPowerUps(x + 2000 - 1650, y + -400); spawn.mapRect(x + 2000 - 1575 - 25, y + -625, 25, 375); //wall on top of wall spawn.mapRect(x + 2000 - 1575 - 25, y + -1325, 25, 525); //wall on top of wall spawn.mapRect(x + 2000 - 1525 - 250, y + -350, 250, 450); //wall spawn.mapRect(x + 2000 - 175 - 370, y + -200, 370, 100); //gun spawn.mapRect(x + 2000 - 530 - 25, y + -190, 25, 80); spawn.mapRect(x + 2000 - 545 - 10, y + -770, 10, 325); //block loader for gun //walls spawn.mapRect(x + 2000 - 620 - 10, y + -770, 10, 325); //walls spawn.mapRect(x + 2000 + 50 - 150, y + -425, 150, 50); spawn.mapRect(x + 2000 - 175 - 370, y + -650, 370, 50); spawn.mapRect(x + 2000 - 540 - 95, y + -460, 95, 15); //bottom that opens and closes const bulletDoor = map[map.length - 1] //keep track of this body so it can be make non-collide later for (let i = 0; i < 6; i++) spawn.bodyRect(x + 2000 - 60 - 555 + Math.floor(Math.random() * 10), y + -520 - 50 * i, 50, 50); //bullets for gun spawn.bodyRect(x + 2000 - 250 - 40, y + -700, 40, 50); //extra bullets spawn.bodyRect(x + 2000 - 350 - 30, y + -700, 30, 35); spawn.bodyRect(x + 2000 - 425 - 40, y + -700, 40, 70); const button = level.button(x + 2000 - 280 - 140, y - 200) //trigger for gun button.isReadyToFire = true doCustom.push( () => { ctx.fillStyle = "rgba(0,0,0,0.05)"; //"rgba(0,0,0,0.1)"; ctx.fillRect(x + 2000 - 200 - 325, y + -625, 325, 650); button.query(); button.draw(); if (!button.isReadyToFire && button.isUp) { button.isReadyToFire = true bulletDoor.collisionFilter.mask = cat.player | cat.map | cat.body | cat.bullet | cat.mob | cat.mobBullet } else if (button.isReadyToFire && !button.isUp) { button.isReadyToFire = false bulletDoor.collisionFilter.mask = 0 //cat.player | cat.map | cat.body | cat.bullet | cat.mob | cat.mobBullet } else if (!button.isUp) { const bounds = { min: { x: x + 2000 - 580, y: y - 125 }, max: { x: x + 2000 - 530, y: y - 110 } } const list = Matter.Query.region(body, bounds) for (let i = 0, len = list.length; i < len; i++) { Matter.Body.setVelocity(list[i], { x: -120, y: -5 }); } if (Matter.Query.region([player], bounds).length) { Matter.Body.setVelocity(player, { x: -100, y: -5 }); } ctx.fillStyle = `rgba(255,0,255,${0.2 + 0.7 * Math.random()})` ctx.fillRect(bounds.min.x, y - 185, 38, 70); } } ) spawn.randomMob(x + 2000 - 1600, y + -425, mobSpawnChance); spawn.randomMob(x + 2000 - 1725, y + -1250, mobSpawnChance); spawn.randomMob(x + 2000 - 1250, y + -1200, mobSpawnChance); spawn.randomMob(x + 2000 - 300, y + -1200, mobSpawnChance); spawn.randomMob(x + 2000 - 800, y + -125, mobSpawnChance); let pick = spawn.pickList[Math.floor(Math.random() * spawn.pickList.length)]; spawn[pick](x + 2000 - 1275, y + -150, 90 + Math.random() * 40); //one extra large mob } else { powerUps.spawnStartingPowerUps(x + 1650, y + -400); spawn.mapRect(x + 1575, y + -625, 25, 375); //wall on top of wall spawn.mapRect(x + 1575, y + -1325, 25, 525); //wall on top of wall spawn.mapRect(x + 1525, y + -350, 250, 450); //wall spawn.mapRect(x + 175, y + -200, 370, 100); //gun spawn.mapRect(x + 530, y + -190, 25, 80); spawn.mapRect(x + 545, y + -770, 10, 325); //block loader for gun //walls spawn.mapRect(x + 620, y + -770, 10, 325); //walls spawn.mapRect(x - 50, y + -425, 150, 50); spawn.mapRect(x + 175, y + -650, 370, 50); spawn.mapRect(x + 540, y + -460, 95, 15); //bottom that opens and closes const bulletDoor = map[map.length - 1] //keep track of this body so it can be make non-collide later for (let i = 0; i < 6; i++) spawn.bodyRect(x + 555 + Math.floor(Math.random() * 10), y + -520 - 50 * i, 50, 50); //bullets for gun spawn.bodyRect(x + 250, y + -700, 40, 50); //extra bullets spawn.bodyRect(x + 350, y + -700, 30, 35); spawn.bodyRect(x + 425, y + -700, 40, 70); const button = level.button(x + 280, y - 200) //trigger for gun button.isReadyToFire = true doCustom.push( () => { ctx.fillStyle = "rgba(0,0,0,0.05)"; //"rgba(0,0,0,0.1)"; ctx.fillRect(x + 200, y + -625, 325, 650); button.query(); button.draw(); if (!button.isReadyToFire && button.isUp) { button.isReadyToFire = true bulletDoor.collisionFilter.mask = cat.player | cat.map | cat.body | cat.bullet | cat.mob | cat.mobBullet } else if (button.isReadyToFire && !button.isUp) { button.isReadyToFire = false bulletDoor.collisionFilter.mask = 0 //cat.player | cat.map | cat.body | cat.bullet | cat.mob | cat.mobBullet } else if (!button.isUp) { const bounds = { min: { x: x + 530, y: y - 125 }, max: { x: x + 580, y: y - 110 } } const list = Matter.Query.region(body, bounds) for (let i = 0, len = list.length; i < len; i++) { Matter.Body.setVelocity(list[i], { x: 120, y: -5 }); } if (Matter.Query.region([player], bounds).length) { Matter.Body.setVelocity(player, { x: 100, y: -5 }); } ctx.fillStyle = `rgba(255,0,255,${0.2 + 0.7 * Math.random()})` ctx.fillRect(bounds.min.x, y - 185, 38, 70); } } ) spawn.randomMob(x + 1600, y + -425, mobSpawnChance); spawn.randomMob(x + 1725, y + -1250, mobSpawnChance); spawn.randomMob(x + 1250, y + -1200, mobSpawnChance); spawn.randomMob(x + 300, y + -1200, mobSpawnChance); spawn.randomMob(x + 800, y + -125, mobSpawnChance); let pick = spawn.pickList[Math.floor(Math.random() * spawn.pickList.length)]; spawn[pick](x + 1275, y + -150, 90 + Math.random() * 40); //one extra large mob } } ] lootOptions = [ //has some power up reward //field, ammo, research, gun (x = offset.x, y = offset.y) => { spawn.mapRect(x + 1925, y + -325, 125, 150); //4 wall ledges spawn.mapRect(x + 1925, y + -865, 125, 150); //4 wall ledges spawn.mapRect(x + -50, y + -325, 125, 150); //4 wall ledges spawn.mapRect(x + -50, y + -865, 125, 150); //4 wall ledges spawn.mapRect(x + 1700, y + -500, 200, 25); spawn.mapRect(x + 75, y + -500, 200, 25); let chamberY = -650 if (Math.random() > 0.5) { //upper chamber chamberY = -650 - 640 spawn.mapRect(x + 550, y + -10 - 640, 900, 25); //raised floor spawn.mapRect(x + 450, y + -20 - 640, 1100, 25); spawn.mapRect(x + 450, y + -675 - 640, 1100, 25); //chamber ceiling powerUps.directSpawn(x + 998, y - 333 - 640, "tech", false); spawn.mapVertex(x + 1000, y + -0, "575 0 -575 0 -450 -100 450 -100"); //base } else { //lower chamber spawn.mapRect(x + 400, y + -10, 1200, 50); //raised floor spawn.mapRect(x + 450, y + -20, 1100, 50); spawn.mapRect(x + 450, y + -675, 1100, 25); //chamber ceiling spawn.mapRect(x + 550, y + -685, 900, 25); powerUps.directSpawn(x + 998, y - 333, "tech", false); } const powerUp1 = powerUp[powerUp.length - 1] powerUp1.holdPosition = { x: powerUp1.position.x, y: powerUp1.position.y } let isSpawnedMobs = false doCustom.push( () => { ctx.fillStyle = "#e4e4e9" //"rgba(255,255,255,1)"; ctx.fillRect(x + 450, y + chamberY, 1100, 650); //chamber background // if (!isInRoom && m.pos.x > x - 100 && m.pos.x < x + 2000 && m.pos.y > y - 1300 && m.pos.y < y) { //is player inside this room? // isInRoom = true // } else if (powerUp1.velocity.y !== 0) { //don't run this code if power up is gone //hack: powerUp1.velocity.y !== 0 seems to only be true if the power up up doesn't exist and is no longer being affected by gravity ctx.strokeStyle = "#f0f" ctx.lineWidth = 2; if (Vector.magnitudeSquared(Vector.sub(m.pos, powerUp1.position)) < 90000) { //zone radius is 300 //damage player and drain energy if (m.immuneCycle < m.cycle) { m.damage(0.01); if (m.energy > 0.1) m.energy -= 0.02 } //draw electricity going towards player const unit = Vector.normalise(Vector.sub(m.pos, powerUp1.position)) let xElec = powerUp1.position.x + 40 * unit.x; let yElec = powerUp1.position.y + 40 * unit.y; ctx.beginPath(); ctx.moveTo(xElec, yElec); const step = 40 for (let i = 0; i < 6; i++) { xElec += step * (unit.x + 1.5 * (Math.random() - 0.5)) yElec += step * (unit.y + 1.5 * (Math.random() - 0.5)) ctx.lineTo(xElec, yElec); } } else { //draw electricity going in random directions const angle = Math.random() * 2 * Math.PI const Dx = Math.cos(angle); const Dy = Math.sin(angle); let xElec = powerUp1.position.x + 40 * Dx; let yElec = powerUp1.position.y + 40 * Dy; ctx.beginPath(); ctx.moveTo(xElec, yElec); const step = 40 for (let i = 0; i < 6; i++) { xElec += step * (Dx + 1.5 * (Math.random() - 0.5)) yElec += step * (Dy + 1.5 * (Math.random() - 0.5)) ctx.lineTo(xElec, yElec); } } ctx.lineWidth = 2 * Math.random(); ctx.stroke(); //draw electricity ctx.beginPath(); //outline damage zone ctx.arc(powerUp1.position.x, powerUp1.position.y, 300, 0, 2 * Math.PI); ctx.stroke(); //float power up in the air Matter.Body.setPosition(powerUp1, { x: powerUp1.holdPosition.x + 4 * Math.random(), //1300 -2 y: powerUp1.holdPosition.y + 4 * Math.random() //335 -2 }); Matter.Body.setVelocity(powerUp1, { x: 0, y: 0 }); } else if (!isSpawnedMobs) { isSpawnedMobs = true if (chamberY === -650) { //lower chamber spawn.randomMob(x + 250, y + -650, mobSpawnChance); spawn.randomMob(x + 1825, y + -600, mobSpawnChance); spawn.randomGroup(x + 275, y + -1050, mobSpawnChance); spawn.randomGroup(x + 675, y + -975, mobSpawnChance); spawn.randomGroup(x + 1225, y + -975, Infinity); } else { //upper chamber spawn.randomMob(x + 250, y + -650, mobSpawnChance); spawn.randomMob(x + 1800, y + -625, mobSpawnChance); spawn.randomGroup(x + 300, y + -300, mobSpawnChance); spawn.randomGroup(x + 650, y + -275, mobSpawnChance); spawn.randomGroup(x + 1125, y + -300, Infinity); } } } ) } ] upDownOptions = [ //extra tall vertical section 3000x3000 //this is where the level boss is (x = offset.x, y = offset.y) => { //mover const button = level.button(x + 935, y + 0) button.isUp = true doCustomTopLayer.push( () => { button.draw(); if (button.isUp) { button.query(); if (!button.isUp) { const mapStartingLength = map.length //track this so you know how many you added when running addMapToLevelInProgress addMapToLevelInProgress = (who) => { //adds new map elements to the level while the level is already running //don't forget to run simulation.draw.setPaths() after you all the the elements so they show up visually who.collisionFilter.category = cat.map; who.collisionFilter.mask = cat.player | cat.map | cat.body | cat.bullet | cat.powerUp | cat.mob | cat.mobBullet; Matter.Body.setStatic(who, true); //make static Composite.add(engine.world, who); //add to world } //map elements go here //box around portals spawn.mapRect(x + -50, y + -2700, 150, 110); spawn.mapRect(x + -50, y + -2440, 150, 25); spawn.mapRect(x + 1900, y + -2715, 150, 550); spawn.mapRect(x + 1900, y + -2015, 150, 50); spawn.mapRect(x + 1900, y + -1115, 150, 150); spawn.mapRect(x + 1900, y + -815, 150, 50); spawn.mapRect(x + -50, y + -340, 150, 50); // spawn.mapRect(x + -50, y + -640, 150, 150); spawn.mapRect(x + 1975, y - 1015, 50, 225); spawn.mapRect(x + 1975, y - 2190, 50, 200); spawn.mapRect(x + -25, y - 2615, 50, 200); spawn.mapRect(x + -25, y - 515, 75, 200); //ledge to get to upper left door // spawn.mapRect(x + -50, y - 1400, 100, 25); spawn.mapRect(x + -25, y - 1075, 250, 25); spawn.mapRect(x + -50, y - 1075, 150, 590); const rampSpeed = 8 //+ Math.floor(4 * Math.random()) const mover4 = level.mover(x, y + -2425, 1000, 50, rampSpeed) const mover3 = level.mover(x + 1000, y + -2000, 1000, 50, rampSpeed) const mover2 = level.mover(x + 1000, y + -800, 1000, 50, -rampSpeed) const mover1 = level.mover(x, y + -325, 1000, 50, -rampSpeed) const portal1 = level.portal({ x: x + 125, y: y - 415 }, 2 * Math.PI, { //right x: x + 125, y: y - 2515 }, 2 * Math.PI) //right const portal2 = level.portal({ x: x + 1875, y: y - 890 }, Math.PI, { //left x: x + 1875, y: y - 2090 }, Math.PI) //left doCustom.push(() => { portal1[2].query() portal1[3].query() portal2[2].query() portal2[3].query() mover1.push(); mover2.push(); mover3.push(); mover4.push(); }) doCustomTopLayer.push(() => { portal1[0].draw(); portal1[1].draw(); portal1[2].draw(); portal1[3].draw(); portal2[0].draw(); portal2[1].draw(); portal2[2].draw(); portal2[3].draw(); mover1.draw(); mover2.draw(); mover3.draw(); mover4.draw(); }) for (let i = 0, numberOfMapElementsAdded = map.length - mapStartingLength; i < numberOfMapElementsAdded; i++) addMapToLevelInProgress(map[map.length - 1 - i]) simulation.draw.setPaths() //update map graphics //blocks that ride the movers and portals spawn.bodyRect(x + 175, y + -2525, 50, 75); spawn.bodyRect(x + 300, y + -2525, 50, 50); spawn.bodyRect(x + 500, y + -2525, 80, 75); //mobs go here spawn.randomMob(x + 175, y + -125, 0); spawn.randomMob(x + 1775, y + -125, 0); // spawn.randomMob(x + 1750, y + -525, 0); spawn.randomMob(x + 225, y + -1000, 0); spawn.randomMob(x + 1675, y + -1075, 0); // spawn.randomMob(x + 1575, y + -2450, 0); spawn.randomMob(x + 425, y + -1850, 0); spawn.randomMob(x + 1425, y + -1200, 0); spawn.randomMob(x + 350, y + -1000, 0); spawn.randomLevelBoss(x + 475, y + -1475); spawn.secondaryBossChance(x + 1425, y + -1425); } } } ) }, (x = offset.x, y = offset.y) => { //hopBoss2 const button = level.button(x + 935, y + 0) button.isUp = true // spawn.mapVertex(x + 5, y + -1318, "0 0 0 -250 125 -250"); //left ledges // spawn.mapVertex(x + 1995, y + -1318, "0 0 0 -250 -125 -250"); // right ledges doCustomTopLayer.push( () => { button.draw(); if (button.isUp) { button.query(); if (!button.isUp) { const mapStartingLength = map.length //track this so you know how many you added when running addMapToLevelInProgress addMapToLevelInProgress = (who) => { //adds new map elements to the level while the level is already running //don't forget to run simulation.draw.setPaths() after you all the the elements so they show up visually who.collisionFilter.category = cat.map; who.collisionFilter.mask = cat.player | cat.map | cat.body | cat.bullet | cat.powerUp | cat.mob | cat.mobBullet; Matter.Body.setStatic(who, true); //make static Composite.add(engine.world, who); //add to world } //map elements go here spawn.mapRect(x + 150, y + -1400, 750, 50); spawn.mapRect(x + 1100, y + -1400, 750, 50); spawn.mapRect(x + 1825, y + -1050, 200, 50); spawn.mapRect(x + -25, y + -1050, 200, 50); spawn.mapRect(x + 1825, y + -325, 200, 50); spawn.mapRect(x + -25, y + -325, 200, 50); spawn.mapRect(x + 275, y + -700, 525, 50); spawn.mapRect(x + 1200, y + -700, 525, 50); spawn.mapRect(x + -25, y + -1400, 125, 1125); //side walls spawn.mapRect(x + 1900, y + -1400, 150, 1125); spawn.mapRect(x + 1900, y + -2700, 125, 1000); spawn.mapRect(x + -50, y + -2725, 150, 1025); spawn.mapRect(x + -25, y + -1750, 450, 50); spawn.mapRect(x + 1575, y + -1750, 450, 50); spawn.mapRect(x + 525, y + -1750, 950, 50); for (let i = 0, numberOfMapElementsAdded = map.length - mapStartingLength; i < numberOfMapElementsAdded; i++) addMapToLevelInProgress(map[map.length - 1 - i]) simulation.draw.setPaths() //update map graphics //mobs go here powerUps.directSpawn(x + 50, y - 1525, "ammo"); powerUps.directSpawn(x + 1950, y - 1525, "ammo"); powerUps.directSpawn(x + 1900, y - 1525, "ammo"); spawn.hopMomBoss(x + 800, y + -2200) for (let i = 0; i < 6; ++i) spawn.hopBullet(x + 150 + 750 * Math.random(), y + -1600) for (let i = 0; i < 6; ++i) spawn.hopBullet(x + 1100 + 750 * Math.random(), y + -1600) spawn.hopper(x + 1550, y + -775); spawn.hopper(x + 500, y + -775); spawn.hopper(x + 1400, y + -775); spawn.hopper(x + 550, y + -775); spawn.hopper(x + 525, y + -1475); spawn.hopper(x + 1550, y + -1500); } } } ) }, (x = offset.x, y = offset.y) => { // const toggle = level.toggle(x + 950, y + 0, false, true) // toggle(x, y, isOn = false, isLockOn = false) { // toggle.isAddedElements = false const button = level.button(x + 935, y + 0) button.isUp = true spawn.mapVertex(x + 5, y + -1318, "0 0 0 -250 125 -250"); //left ledges spawn.mapVertex(x + 1995, y + -1318, "0 0 0 -250 -125 -250"); // right ledges doCustomTopLayer.push( () => { button.draw(); if (button.isUp) { button.query(); if (!button.isUp) { // toggle.isAddedElements = true //only do this once addMapToLevelInProgress = (who) => { //adds new map elements to the level while the level is already running //don't forget to run simulation.draw.setPaths() after you all the the elements so they show up visually who.collisionFilter.category = cat.map; who.collisionFilter.mask = cat.player | cat.map | cat.body | cat.bullet | cat.powerUp | cat.mob | cat.mobBullet; Matter.Body.setStatic(who, true); //make static Composite.add(engine.world, who); //add to world } let r = 150 let hexagon = `${r} 0 ${r * Math.cos(5.236)} ${r * Math.sin(5.236)} ${r * Math.cos(4.189)} ${r * Math.sin(4.189)} ${-r} 0 ${r * Math.cos(2.0944)} ${r * Math.sin(2.0944)} ${r * Math.cos(1.0472)} ${r * Math.sin(1.0472)} ` //450 horizontal spread // -130-130-130 = 390 vertical if (Math.random() < 0.5) { spawn.mapVertex(x + 775, y + -260, hexagon); spawn.mapVertex(x + 1225, y + -260, hexagon); spawn.mapVertex(x + 550, y + -650, hexagon); spawn.mapVertex(x + 1000, y + -650, hexagon); spawn.mapVertex(x + 1450, y + -650, hexagon); spawn.mapVertex(x + 325, y + -1040, hexagon); spawn.mapVertex(x + 775, y + -1040, hexagon); spawn.mapVertex(x + 1225, y + -1040, hexagon); spawn.mapVertex(x + 1675, y + -1040, hexagon); spawn.mapVertex(x + 550, y + -1430, hexagon); spawn.mapVertex(x + 1000, y + -1430, hexagon); spawn.mapVertex(x + 1450, y + -1430, hexagon); const numberOfMapElementsAdded = 12 for (let i = 0; i < numberOfMapElementsAdded; i++) addMapToLevelInProgress(map[map.length - 1 - i]) spawn.randomMob(x + 225, y + -1775, mobSpawnChance); spawn.randomMob(x + 700, y + -1750, mobSpawnChance); spawn.randomMob(x + 1175, y + -1725, mobSpawnChance); spawn.randomMob(x + 1700, y + -1700, mobSpawnChance); spawn.randomMob(x + 1750, y + -250, mobSpawnChance); spawn.randomMob(x + 125, y + -250, mobSpawnChance); } else { spawn.mapVertex(x + 775, y + -260, hexagon); spawn.mapVertex(x + 1225, y + -260, hexagon); spawn.mapVertex(x + 550, y + -650, hexagon); spawn.mapVertex(x + 1000, y + -650, hexagon); spawn.mapVertex(x + 1450, y + -650, hexagon); spawn.mapVertex(x + 775, y + -1040, hexagon); spawn.mapVertex(x + 1225, y + -1040, hexagon); spawn.mapVertex(x + 550, y + -1430, hexagon); spawn.mapVertex(x + 1000, y + -1430, hexagon); spawn.mapVertex(x + 1450, y + -1430, hexagon); spawn.mapVertex(x + 775, y + -1820, hexagon); spawn.mapVertex(x + 1225, y + -1820, hexagon); const numberOfMapElementsAdded = 12 for (let i = 0; i < numberOfMapElementsAdded; i++) addMapToLevelInProgress(map[map.length - 1 - i]) spawn.randomMob(x + 225, y + -1025, mobSpawnChance); spawn.randomMob(x + 250, y + -1025, mobSpawnChance); spawn.randomMob(x + 200, y + -675, mobSpawnChance); spawn.randomMob(x + 225, y + -200, mobSpawnChance); spawn.randomMob(x + 1750, y + -1075, mobSpawnChance); spawn.randomMob(x + 1700, y + -650, mobSpawnChance); spawn.randomMob(x + 1725, y + -650, mobSpawnChance); spawn.randomMob(x + 1675, y + -175, mobSpawnChance); } simulation.draw.setPaths() //update map graphics spawn.randomGroup(x + 300, y + -2200); spawn.randomGroup(x + 1625, y + -2200); spawn.randomLevelBoss(x + 700, y + -2300); spawn.secondaryBossChance(x + 1250, y + -2300) } } // toggle.query(); // if (toggle.isOn && !toggle.isAddedElements) { //this code runs once after the toggle is triggered // } } ) }, (x = offset.x, y = offset.y) => { // const toggle = level.toggle(x + 950, y + 0, false, true) // toggle(x, y, isOn = false, isLockOn = false) { // toggle.isAddedElements = false const button = level.button(x + 935, y + 0) button.isUp = true //left ledges spawn.mapVertex(x + 5, y + -1868, "0 0 0 -250 125 -250"); spawn.mapVertex(x + 5, y + -1318, "0 0 0 -250 125 -250"); //door spawn.mapVertex(x + 5, y + -768, "0 0 0 -250 125 -250"); // right ledges spawn.mapVertex(x + 2000, y + -1868, "0 0 0 -250 -125 -250"); spawn.mapVertex(x + 2000, y + -1318, "0 0 0 -250 -125 -250"); //door spawn.mapVertex(x + 2000, y + -768, "0 0 0 -250 -125 -250"); doCustomTopLayer.push( () => { button.draw(); if (button.isUp) { button.query(); if (!button.isUp) { addMapToLevelInProgress = (who) => { //adds new map elements to the level while the level is already running //don't forget to run simulation.draw.setPaths() after you all the the elements so they show up visually who.collisionFilter.category = cat.map; who.collisionFilter.mask = cat.player | cat.map | cat.body | cat.bullet | cat.powerUp | cat.mob | cat.mobBullet; Matter.Body.setStatic(who, true); //make static Composite.add(engine.world, who); //add to world } //right side hexagons let r = 300 let hexagon = `${r} 0 ${r * Math.cos(5.236)} ${r * Math.sin(5.236)} ${r * Math.cos(4.189)} ${r * Math.sin(4.189)} ${-r} 0 ${r * Math.cos(2.0944)} ${r * Math.sin(2.0944)} ${r * Math.cos(1.0472)} ${r * Math.sin(1.0472)} ` spawn.mapVertex(x + 1640, y + -365, hexagon); // r = 275 // let hexagonHalf = `${r} 0 ${r*Math.cos(5.236)} ${r*Math.sin(5.236)} ${r*Math.cos(4.189)} ${r*Math.sin(4.189)} ${-r} 0 ` // spawn.mapVertex(x + 2300, y + -75, hexagonHalf); r = 150 const hexagon150 = `${r} 0 ${r * Math.cos(5.236)} ${r * Math.sin(5.236)} ${r * Math.cos(4.189)} ${r * Math.sin(4.189)} ${-r} 0 ${r * Math.cos(2.0944)} ${r * Math.sin(2.0944)} ${r * Math.cos(1.0472)} ${r * Math.sin(1.0472)} ` // spawn.mapVertex(x + 1750, y + -550, hexagon150); spawn.mapVertex(x + 1750, y + -1100, hexagon150); spawn.mapVertex(x + 1750, y + -1650, hexagon150); spawn.mapVertex(x + 1750, y + -2200, hexagon150); //left side r = 350 let hexagonHalf = `${r} 0 ${r * Math.cos(5.236)} ${r * Math.sin(5.236)} ${r * Math.cos(4.189)} ${r * Math.sin(4.189)} ${-r} 0 ` spawn.mapVertex(x + 425, y + -90, hexagonHalf); spawn.mapVertex(x + 850, y + -500, hexagon150); spawn.mapVertex(x + 550, y + -850, hexagon150); spawn.mapVertex(x + 250, y + -1200, hexagon150); spawn.mapVertex(x + 250, y + -1700, hexagon150); spawn.mapVertex(x + 725, y + -1950, hexagon150); spawn.mapVertex(x + 1200, y + -2200, hexagon150); const numberOfMapElementsAdded = 11 for (let i = 0; i < numberOfMapElementsAdded; i++) addMapToLevelInProgress(map[map.length - 1 - i]) spawn.randomMob(x + 1050, y + -1500, mobSpawnChance); spawn.randomMob(x + 1075, y + -1500, mobSpawnChance); spawn.randomMob(x + 325, y + -550, mobSpawnChance); spawn.randomMob(x + 800, y + -925, mobSpawnChance); spawn.randomMob(x + 1400, y + -1250, mobSpawnChance); spawn.randomMob(x + 1325, y + -1725, mobSpawnChance); spawn.randomMob(x + 1350, y + -1725, mobSpawnChance); spawn.randomMob(x + 575, y + -1375, mobSpawnChance); spawn.randomMob(x + 225, y + -2275, mobSpawnChance); spawn.randomMob(x + 875, y + -2450, mobSpawnChance); spawn.randomMob(x + 1550, y + -2525, mobSpawnChance); spawn.randomMob(x + 1525, y + -2525, mobSpawnChance); spawn.randomLevelBoss(x + 1075, y + -1500); spawn.secondaryBossChance(x + 1200, y + -1000) simulation.draw.setPaths() //update map graphics } } } ) }, // (x = offset.x, y = offset.y) => { // const elevator1 = level.elevator(x + 1100, y - 200, 250, 30, -2100, 0.0015) // x, y, width, height, maxHeight, force = 0.003, friction = { up: 0.01, down: 0.2 }, isTeleport = false) { // // const elevator1 = level.elevator(x + 175, y - 200, 250, 30, -1400, 0.001) // // const elevator2 = level.elevator(x + 2175, y - 200, 250, 30, -1400, 0.001) // spawn.mapRect(-200, -1400, 350, 50); //up left door ledge // spawn.mapRect(2450, -1400, 350, 50); //up right door ledge // spawn.mapRect(225, -450, 350, 350); //left crawl zone // // spawn.mapRect(725, -175, 275, 75); // spawn.mapRect(725, -225, 350, 100); // spawn.mapRect(275, -750, 200, 200); // spawn.mapRect(1375, -700, 500, 750); //right side big elevator wall // spawn.mapRect(2375, -325, 350, 50); // spawn.mapRect(1800, -500, 250, 50); // //up high elevator // spawn.mapRect(1375, -2100, 500, 175); // spawn.mapRect(600, -2100, 475, 175); // if (simulation.difficulty > 3) spawn.randomLevelBoss(x + 1250, y - 1400); // doCustomTopLayer.push( // () => { // elevator1.move() // } // ) // } ] //pick which type of room spawns enter = enterOptions[Math.floor(Math.random() * enterOptions.length)]; exit = exitOptions[Math.floor(Math.random() * exitOptions.length)]; empty = emptyOptions[Math.floor(Math.random() * emptyOptions.length)]; loot = lootOptions[Math.floor(Math.random() * lootOptions.length)]; upDown = upDownOptions[Math.floor(Math.random() * upDownOptions.length)]; // upDown = upDownOptions[0] //controls what level spawns for map designing building //********************************* DO !NOT! RUN THIS LINE IN THE FINAL VERSION *************************************** //3x2: 4 short rooms (3000x1500), 1 double tall room (3000x3000) //rooms let rooms = ["exit", "loot", "enter", "empty"] rooms = shuffle(rooms); //shuffles array order //look... you and I both know there is a better way to do this, but it works so I'm gonna focus on other things while ( //makes sure that the exit and entrance aren't both on the same floor (rooms[0] === "enter" && rooms[2] === "exit") || (rooms[2] === "enter" && rooms[0] === "exit") || (rooms[1] === "enter" && rooms[3] === "exit") || (rooms[3] === "enter" && rooms[1] === "exit") ) rooms = shuffle(rooms); //shuffles array order for (let i = 0; i < rooms.length; i++) { if (rooms[i] === "enter") rooms[i] = enter if (rooms[i] === "exit") rooms[i] = exit if (rooms[i] === "empty") rooms[i] = empty if (rooms[i] === "loot") rooms[i] = loot } // rooms = [enter, exit, loot, empty, ] //controls what level spawns for map designing building //********************************* DO !NOT! RUN THIS LINE IN THE FINAL VERSION *************************************** outline = (isLower = true) => { spawn.mapRect(offset.x - 100, offset.y - 1400, 2100, 100); //ceiling if (isLower) spawn.mapRect(offset.x - 100, offset.y, 2200, 100); //only draw floor if on the lower level if (!isDoorLeft) spawn.mapRect(offset.x - 100, offset.y - 1400, 100, 1500); //left wall if (isDoorRight) { //if door only add wall on right side spawn.mapRect(offset.x + 2000, offset.y - 1400, 100, 1225); //right wall spawn.mapRect(offset.x + 2000, offset.y - 10, 100, 20); //right doorstep const doorWidth = 15 + Math.floor(100 * Math.random() * Math.random()) spawn.bodyRect(offset.x + 2050 - doorWidth / 2, offset.y - 175, doorWidth, 165); //block door } else { spawn.mapRect(offset.x + 2000, offset.y - 1400, 100, 1500); //right wall } } outlineUpDown = () => { spawn.mapRect(offset.x - 100, offset.y + 0, 2100, 100); //floor spawn.mapRect(offset.x - 100, offset.y - 2800, 2100, 100); //ceiling if (!isDoorLeft) spawn.mapRect(offset.x - 100, offset.y - 2800, 100, 2900); //left wall if (isDoorRight) { //if door only add wall on right side //upper door spawn.mapRect(offset.x + 2000, offset.y - 2800, 100, 1225); //right wall spawn.mapRect(offset.x + 2000, offset.y - 1410, 100, 20); //right doorstep const doorWidth = 15 + Math.floor(100 * Math.random() * Math.random()) spawn.bodyRect(offset.x + 2050 - doorWidth / 2, offset.y - 1575, doorWidth, 165); //block door //lower door spawn.mapRect(offset.x + 2000, offset.y - 1400, 100, 1225); //right wall spawn.mapRect(offset.x + 2000, offset.y - 10, 100, 20); //right doorstep const doorWidth2 = 15 + Math.floor(100 * Math.random() * Math.random()) spawn.bodyRect(offset.x + 2050 - doorWidth2 / 2, offset.y - 175, doorWidth2, 165); //block door } else { spawn.mapRect(offset.x + 2000, offset.y - 2800, 100, 2900); //right wall } } let columns = [ () => { offset.y = 0 outlineUpDown() upDown() }, () => { offset.y = 0 outline() rooms[0]() offset.y = -1400 outline(false) rooms[1]() }, () => { offset.y = 0 outline() rooms[2]() offset.y = -1400 outline(false) rooms[3]() }, ] columns = shuffle(columns) //********************************* RUN THIS LINE IN THE FINAL VERSION *************************************** for (let i = 0; i < 3; i++) { if (i === 0) { isDoorLeft = false isDoorRight = true } else if (i === 1) { isDoorLeft = true isDoorRight = true } else { isDoorLeft = true isDoorRight = false } offset.x = i * 2100 columns[i]() } level.custom = () => { for (let i = 0, len = doCustom.length; i < len; i++) doCustom[i]() //runs all the active code from each room level.exit.drawAndCheck(); level.enter.draw(); }; level.customTopLayer = () => { for (let i = 0, len = doCustomTopLayer.length; i < len; i++) doCustomTopLayer[i]() //runs all the active code from each room }; powerUps.addResearchToLevel() //needs to run after mobs are spawned }, pavilion() { const vanish = [] level.exit.x = -850; level.exit.y = -1485; spawn.mapRect(level.exit.x, level.exit.y + 25, 100, 25); level.setPosToSpawn(-900, 225); //normal spawn spawn.mapRect(level.enter.x, level.enter.y + 20, 100, 20); level.defaultZoom = 1500 simulation.zoomTransition(level.defaultZoom) document.body.style.backgroundColor = "#dcdcde"; spawn.debris(-150, -775, 1425, 3); //16 debris per level spawn.debris(1525, -25, 950, 3); //16 debris per level spawn.debris(-650, -2100, 575, 2); //16 debris per level //bottom floor //entrance spawn.mapRect(-200, -750, 1500, 100); spawn.mapRect(-575, 0, 2150, 500); // spawn.mapRect(-1275, 275, 875, 225); spawn.mapRect(-1275, 275, 3975, 225); spawn.mapRect(-1050, 0, 325, 50); spawn.mapRect(-775, 0, 50, 140); vanish.push(level.vanish(-725, 13, 150, 25)) spawn.mapRect(-200, -750, 100, 600); // spawn.mapRect(1200, -750, 100, 600); vanish.push(level.vanish(-350, -225, 150, 225)) vanish.push(level.vanish(-350, -450, 150, 223)) spawn.mapRect(2475, -1800, 250, 2300); spawn.mapRect(1200, -750, 100, 450); spawn.mapRect(1200, -375, 250, 75); powerUps.spawnStartingPowerUps(550, -100); spawn.mapRect(125, -12, 850, 50); spawn.mapRect(175, -25, 750, 50); spawn.bodyRect(1350, -175, 150, 175, 0.5); spawn.bodyRect(1350, -600, 125, 225, 0.2); //middle floor spawn.bodyRect(215, -1175, 100, 100, 0.3); spawn.mapRect(-1300, -1800, 250, 2300); // spawn.mapRect(-1300, -2075, 250, 2575); if (Math.random() < 0.5) { spawn.mapRect(500, -1350, 525, 425); spawn.mapRect(25, -1050, 300, 198); } else { spawn.mapRect(500, -1350, 525, 497); spawn.mapRect(25, -1050, 300, 150); } if (Math.random() < 0.5) { vanish.push(level.vanish(400, -1600, 175, 25)) vanish.push(level.vanish(950, -1600, 175, 25)) } else { vanish.push(level.vanish(550, -1575, 50, 225)) vanish.push(level.vanish(925, -1575, 50, 225)) } // vanish.push(level.vanish(575, -1575, 375, 225)) spawn.bodyRect(225, -850, 50, 100, 0.4); spawn.mapRect(600, -1800, 325, 225); spawn.mapRect(1900, -1500, 325, 25); spawn.bodyRect(1050, -1825, 250, 20, 0.2); if (Math.random() < 0.5) { vanish.push(level.vanish(1400, -1000, 200, 25)) vanish.push(level.vanish(1625, -1250, 200, 25)) } else { vanish.push(level.vanish(1400, -1075, 175, 175)) vanish.push(level.vanish(1575, -1250, 175, 175)) } vanish.push(level.vanish(1125, -1800, 625, 25)) // vanish.push(level.vanish(1500, -1800, 225, 25)) vanish.push(level.vanish(-50, -1800, 450, 25)) //exit spawn.mapRect(-1050, -1450, 700, 25); spawn.mapRect(-1050, -1800, 525, 25); spawn.mapRect(-550, -1800, 25, 200); spawn.randomMob(-1175, -1975, -0.4); spawn.randomMob(275, -1500, -0.3); spawn.randomMob(700, -1875, -0.2); spawn.randomMob(2000, -800, -0.2); spawn.randomMob(2600, -1850, 0); spawn.randomMob(1425, -525, 0.1); spawn.randomMob(2025, -1600, 0.3); spawn.randomMob(1625, -1875, 0.3); spawn.randomMob(-150, -1975, 0.4); spawn.randomSmallMob(900, -825); spawn.randomSmallMob(1050, -50); if (simulation.difficulty > 1) { spawn.randomGroup(750, -2150, -0.8) spawn.randomLevelBoss(2050, -2025) spawn.secondaryBossChance(100, -1500) } powerUps.addResearchToLevel() //needs to run after mobs are spawned if (simulation.isHorizontalFlipped) { //flip the map horizontally level.flipHorizontal(); //only flips map,body,mob,powerUp,cons,consBB, exit level.setPosToSpawn(900, 225); //normal spawn level.custom = () => { ctx.fillStyle = "#d0d3d9" ctx.fillRect(-2500, -1800, 3575, 2100); ctx.fillStyle = "#c0c3c9" ctx.fillRect(-2075, -1475, 25, 1800); ctx.fillStyle = "#cff" //exit ctx.fillRect(550, -1800, 525, 350) level.exit.drawAndCheck(); level.enter.draw(); }; level.customTopLayer = () => { //shadow ctx.fillStyle = "rgba(0,10,30,0.1)" ctx.fillRect(-1450, -300, 150, 325); ctx.fillRect(-1300, -650, 1500, 650) ctx.fillRect(725, 50, 325, 225) ctx.fillRect(-325, -950, 300, 225) ctx.fillRect(-1025, -1000, 525, 275); ctx.fillRect(-925, -1600, 325, 275); for (let i = 0, len = vanish.length; i < len; i++) vanish[i].query() }; } else { level.custom = () => { ctx.fillStyle = "#d0d3d9" ctx.fillRect(-1075, -1800, 3575, 2100); ctx.fillStyle = "#c0c3c9" ctx.fillRect(2050, -1475, 25, 1800); ctx.fillStyle = "#cff" //exit ctx.fillRect(-1050, -1800, 525, 350) level.exit.drawAndCheck(); level.enter.draw(); }; level.customTopLayer = () => { //shadow ctx.fillStyle = "rgba(0,10,30,0.1)" ctx.fillRect(1300, -300, 150, 325); ctx.fillRect(-200, -675, 1500, 700) ctx.fillRect(500, -950, 525, 225); ctx.fillRect(600, -1600, 325, 275); ctx.fillRect(-1050, 50, 325, 225) ctx.fillRect(25, -950, 300, 225) for (let i = 0, len = vanish.length; i < len; i++) vanish[i].query() }; } }, testChamber() { level.setPosToSpawn(0, -50); //lower start level.exit.y = level.enter.y - 550; spawn.mapRect(level.enter.x, level.enter.y + 20, 100, 20); level.exit.x = level.enter.x; spawn.mapRect(level.exit.x, level.exit.y + 20, 100, 20); level.defaultZoom = 2200 simulation.zoomTransition(level.defaultZoom) document.body.style.backgroundColor = "#d0d5d5"; color.map = "#444" spawn.mapRect(0, -1955, 175, 30); const removeIndex1 = map.length - 1 //so much work to catch blocks caught at the bottom of the vertical portals spawn.mapRect(1225, -1955, 175, 30); const removeIndex2 = map.length - 1 //so much work to catch blocks caught at the bottom of the vertical portals let portal, portal2, portal3 const hazard = level.hazard((simulation.isHorizontalFlipped ? -350 - 700 : 350), -2025, 700, 10, 0.4) //laser spawn.mapRect(340, -2032.5, 20, 25); //laser nose const hazard2 = level.hazard((simulation.isHorizontalFlipped ? -1775 - 150 : 1775), -2550, 150, 10, 0.4) //laser spawn.mapRect(1920, -2557.5, 20, 25); //laser nose const button = level.button(2100, -2600) const buttonDoor = level.button(600, -550) const door = level.door(312, -750, 25, 190, 185) level.custom = () => { if (!(m.cycle % 60)) { //so much work to catch blocks caught at the bottom of the vertical portals let touching = Matter.Query.collides(map[removeIndex1], body) if (touching.length) { Matter.Composite.remove(engine.world, touching[0].bodyB); for (let i = 0, len = body.length; i < len; i++) { if (body[i].id === touching[0].bodyB.id) { body.splice(i, 1); break } } } touching = Matter.Query.collides(map[removeIndex2], body) if (touching.length) { Matter.Composite.remove(engine.world, touching[0].bodyB); for (let i = 0, len = body.length; i < len; i++) { if (body[i].id === touching[0].bodyB.id) { body.splice(i, 1); break } } } } buttonDoor.query(); buttonDoor.draw(); if (buttonDoor.isUp) { door.isClosing = true } else { door.isClosing = false } door.openClose(); portal[2].query() portal[3].query() portal2[2].query() portal2[3].query() portal3[2].query() portal3[3].query() if (button.isUp) { hazard.isOn = false; hazard2.isOn = false; } else { hazard.isOn = true; hazard2.isOn = true; } button.query(); button.draw(); ctx.fillStyle = "#d4f4f4" ctx.fillRect(-300, -1000, 650, 500) level.exit.drawAndCheck(); level.enter.draw(); }; level.customTopLayer = () => { door.draw(); hazard.opticalQuery(); hazard2.opticalQuery(); portal[0].draw(); portal[1].draw(); portal[2].draw(); portal[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(); }; powerUps.spawnStartingPowerUps(1875, -3075); const powerUpPos = shuffle([{ //no debris on this level but 2 random spawn instead x: -150, y: -1775 }, { x: 2400, y: -2650 }, { x: -175, y: -1375 }, { x: 1325, y: -150 }]); powerUps.chooseRandomPowerUp(powerUpPos[0].x, powerUpPos[0].y); powerUps.chooseRandomPowerUp(powerUpPos[1].x, powerUpPos[1].y); //outer wall spawn.mapRect(2500, -3700, 1200, 3800); //right map wall spawn.mapRect(-1400, -3800, 1100, 3900); //left map wall spawn.mapRect(-1400, -4800, 5100, 1200); //map ceiling spawn.mapRect(-1400, 0, 5100, 1200); //floor //lower entrance /exit spawn.mapRect(300, -375, 50, 225); spawn.bodyRect(312, -150, 25, 140); spawn.mapRect(300, -10, 50, 50); spawn.mapVertex(1555, 0, "625 0 75 0 200 -100 500 -100"); //entrance ramp //upper entrance / exit spawn.mapRect(-400, -1050, 750, 50); spawn.mapRect(300, -1050, 50, 300); // spawn.bodyRect(312, -750, 25, 190); spawn.mapRect(300, -560, 50, 50); spawn.bodyRect(750, -725, 125, 125); spawn.mapRect(1150, -1050, 250, 575); spawn.mapRect(1725, -550, 50, 200); //walls around portal 3 spawn.mapRect(1925, -550, 500, 200); spawn.mapRect(1750, -390, 200, 40); spawn.mapRect(-400, -550, 1800, 200); spawn.mapRect(-200, -1700, 150, 25); //platform above exit room spawn.mapRect(-200, -1325, 350, 25); //portal 3 angled spawn.mapRect(2425, -450, 100, 100); //portal 1 bottom spawn.mapRect(2290, -12, 375, 100); spawn.mapRect(2350, -24, 375, 100); spawn.mapRect(2410, -36, 375, 100); //portal 1 top spawn.mapRect(2290, -3012, 375, 50); spawn.mapRect(2350, -3024, 375, 50); spawn.mapRect(2410, -3036, 375, 50); spawn.mapRect(1400, -3000, 1300, 50); //floor spawn.mapRect(1750, -3050, 250, 75); spawn.mapRect(1400, -3625, 50, 200); spawn.mapRect(350, -3625, 50, 225); spawn.mapRect(350, -3260, 50, 60); spawn.mapRect(200, -3250, 1240, 50); spawn.mapRect(1400, -3260, 50, 310); spawn.bodyRect(1412, -3425, 25, 165); spawn.mapRect(-150, -2925, 150, 25); //portal 2 spawn.mapRect(-300, -2600, 300, 675); //left platform spawn.mapRect(1400, -2600, 375, 675); //right platform spawn.mapRect(1925, -2600, 775, 675); //far right platform spawn.bodyRect(2130, -2660, 50, 50); //button's block spawn.mapRect(150, -2100, 200, 175); spawn.mapRect(1050, -2100, 200, 175); //mobs spawn.randomMob(1075, -3500, -0.3); spawn.randomMob(2175, -700, -0.2); spawn.randomMob(-75, -850, -0.1); spawn.randomMob(550, -3400, 0); spawn.randomMob(0, -1175, 0.5); spawn.randomMob(-75, -1150, 0.5); spawn.randomMob(1075, -625, 0.5); spawn.randomMob(800, -3400, -0.3); spawn.randomMob(1225, -3375, -0.2); spawn.randomMob(1200, -1125, -0.1); spawn.randomMob(2050, -950, 0.5); if (simulation.difficulty > 40) { spawn.randomMob(2300, -2775, -0.5); spawn.randomMob(600, -925, -0.5); spawn.randomMob(1550, -2750, -0.5); spawn.randomMob(1350, -1150, -0.5); spawn.randomMob(-75, -1475, 0); spawn.randomGroup(600, -2600, 0); } if (simulation.difficulty > 1) { if (Math.random() < 0.5) { spawn.randomLevelBoss(700, -1550); } else { spawn.randomLevelBoss(675, -2775); //["shooterBoss", "launcherBoss", "laserTargetingBoss", "streamBoss", "shieldingBoss", "pulsarBoss", "grenadierBoss"] } } powerUps.addResearchToLevel() //needs to run after mobs are spawned spawn.secondaryBossChance(1925, -1250) if (simulation.isHorizontalFlipped) { //flip the map horizontally level.flipHorizontal(); //only flips map,body,mob,powerUp,cons,consBB, exit // level.setPosToSpawn(0, -50); //-x // no need since 0 button.min.x = -button.min.x - 126 // flip the button horizontally button.max.x = -button.max.x + 126 // flip the button horizontally buttonDoor.min.x = -buttonDoor.min.x - 126 // flip the button horizontally buttonDoor.max.x = -buttonDoor.max.x + 126 // flip the button horizontally //this makes the hazard draw, but not collide for reasons I don't understand //so don't use it, instead just call the hazard differently based on this flip flag // hazard.min.x = -hazard.min.x - hazard.width //-x-width // hazard.max.x = -hazard.max.x - hazard.width //-x-width // hazard2.min.x = -hazard2.min.x - hazard2.width //-x-width // hazard2.max.x = -hazard2.max.x - hazard2.width //-x-width portal = level.portal({ x: -2475, y: -140 }, 2 * Math.PI, { //right x: -2475, y: -3140 }, 2 * Math.PI) //right portal2 = level.portal({ x: -75, y: -2150 }, -Math.PI / 2, { //up x: -1325, y: -2150 }, -Math.PI / 2) //up portal3 = level.portal({ x: -1850, y: -585 }, -Math.PI / 2, { //up x: -2425, y: -600 }, -1 * Math.PI / 3) //up left // level.custom = () => { }; // level.customTopLayer = () => {}; } else { portal = level.portal({ x: 2475, y: -140 }, Math.PI, { //left x: 2475, y: -3140 }, Math.PI) //left portal2 = level.portal({ x: 75, y: -2150 }, -Math.PI / 2, { //up x: 1325, y: -2150 }, -Math.PI / 2) //up portal3 = level.portal({ x: 1850, y: -585 }, -Math.PI / 2, { //up x: 2425, y: -600 }, -2 * Math.PI / 3) //up left } }, lock() { level.setPosToSpawn(0, -65); //lower start spawn.mapRect(level.enter.x, level.enter.y + 20, 100, 20); level.exit.y = 2010; level.exit.x = 2625; spawn.mapRect(level.exit.x, level.exit.y + 20, 100, 20); level.defaultZoom = 2200 simulation.zoomTransition(level.defaultZoom) document.body.style.backgroundColor = "hsl(138, 5%, 82%)"; color.map = "#444" powerUps.spawnStartingPowerUps(1768, 870); //on left side const portal = level.portal({ x: 1070, y: -1485 }, -0.9, { x: 475, y: 50 }, -Math.PI / 2) const doorCenterRight = level.door(2787, 775, 25, 225, 195, 5) //x, y, width, height, distance, speed = 1 const doorCenterLeft = level.door(2537, 775, 25, 225, 195, 5) const doorButtonRight = level.door(4462, 1010, 25, 225, 195, 5) const doorLeft = level.door(2538, 1825, 25, 225, 195, 5) const buttonLeft = level.button(4565, 1235) const buttonRight = level.button(4142, -355) // spawn.mapRect(4000, -350, 700, 125); //button platform spawn.mapRect(4000, -350, 600, 75); buttonLeft.isUp = true buttonRight.isUp = true const hazardSlimeLeft = level.hazard(900, -300, 1638, 2450) //hazard(x, y, width, height, damage = 0.002) { const hazardSlimeRight = level.hazard(2812, -300, 1650, 2450) //hazard(x, y, width, height, damage = 0.002) { //set slime to empty // hazardSlimeLeft.height -= hazardSlimeLeft.maxHeight //start slime at zero // hazardSlimeLeft.min.y += hazardSlimeLeft.maxHeight // hazardSlimeLeft.max.y = hazardSlimeLeft.min.y + hazardSlimeLeft.height // hazardSlimeRight.height -= hazardSlimeRight.maxHeight //start slime at zero // hazardSlimeRight.min.y += hazardSlimeRight.maxHeight // hazardSlimeRight.max.y = hazardSlimeRight.min.y + hazardSlimeRight.height const balance = [] level.custom = () => { ctx.fillStyle = "hsl(175, 35%, 76%)" //exit ctx.fillRect(2537, 1775, 275, 275) level.exit.drawAndCheck(); level.enter.draw(); doorButtonRight.isClosing = hazardSlimeRight.min.y < 1275 doorCenterRight.isClosing = hazardSlimeRight.min.y < 1000 doorCenterLeft.isClosing = hazardSlimeLeft.min.y < 1000 doorLeft.isClosing = hazardSlimeLeft.min.y < 2050 doorButtonRight.openClose(); doorCenterRight.openClose(); doorCenterLeft.openClose(); doorLeft.openClose(); if (buttonRight.isUp) { buttonRight.query(); if (!buttonRight.isUp) spawnRightMobs() } if (buttonLeft.isUp) { buttonLeft.query(); if (!buttonLeft.isUp) spawnLeftMobs() } buttonRight.draw(); buttonLeft.draw(); if (hazardSlimeLeft.min.y < 2050) { const drainRate = Math.min(Math.max(0.25, 4 - hazardSlimeLeft.min.y / 500), 4) hazardSlimeLeft.level(buttonLeft.isUp, drainRate) } if (hazardSlimeRight.min.y < 2050) { const drainRate = Math.min(Math.max(0.25, 4 - hazardSlimeRight.min.y / 500), 4) hazardSlimeRight.level(buttonRight.isUp, drainRate) } portal[2].query() portal[3].query() }; level.customTopLayer = () => { doorButtonRight.draw(); doorCenterRight.draw(); doorCenterLeft.draw(); doorLeft.draw(); hazardSlimeLeft.query(); hazardSlimeRight.query(); portal[0].draw(); portal[1].draw(); portal[2].draw(); portal[3].draw(); ctx.fillStyle = color.map //below portal ctx.fillRect(375, 150, 200, 2525); ctx.fillStyle = "rgba(0,0,0,0.1)" //shadows ctx.fillRect(-250, -1550, 1250, 1575); ctx.fillRect(2537, -350, 275, 2425); ctx.fillStyle = "rgba(0,0,0,0.05)" //exit ctx.fillRect(-175, -300, 375, 300) ctx.fillRect(4460, 950, 350, 325); ctx.fillStyle = "#233" //balances center dot ctx.beginPath(); for (let i = 0; i < balance.length; i++) { ctx.arc(balance[i].center.x, balance[i].center.y, 9, 0, 2 * Math.PI); } ctx.fill(); // for (let i = 0, len = vanish.length; i < len; i++) vanish[i].query() }; //entrance and outer walls spawn.mapRect(-1400, 0, 1800, 2675); spawn.mapRect(-1400, -1025, 1225, 1500); spawn.mapRect(-325, -15, 525, 225); spawn.mapRect(150, -350, 50, 175); spawn.mapRect(-1400, -3525, 1600, 3225); spawn.mapRect(550, 0, 450, 2675); spawn.mapRect(550, -1550, 450, 125); spawn.mapRect(150, -1550, 250, 125); spawn.mapRect(750, -1425, 1100, 175); spawn.mapRect(750, -1400, 250, 825); spawn.mapRect(750, -350, 250, 575); spawn.mapRect(625, 2100, 4300, 575); //floor spawn.mapRect(-1400, -4425, 7250, 1000); //ceiling // const vanish = [] // vanish.push(level.vanish(400, -1512, 150, 50)) // vanish.push(level.vanish(825, -625, 100, 325)) //left button room (on the far right in the spawn.mapRect(4450, -3525, 1400, 4500); spawn.mapRect(4450, 1235, 1400, 1440); spawn.mapRect(4775, 750, 1075, 825); spawn.mapRect(4450, 950, 50, 75); //other ideas for left and right alternate setups //just a floor covered with boosts //something focused on funnel shapes //several rooms with tunnels connecting them //spinners //right side if (Math.random() < 1) { spawn.mapVertex(3350, 350, "-100 0 100 0 100 700 0 750 -100 700"); balance.push(level.rotor(3463, 150, 300, 25, 0.001, 0)) //balance(x, y, width, height, density = 0.001, angle = 0, frictionAir = 0.001, angularVelocity = 0, rotationForce = 0.0005) { balance.push(level.rotor(3463, 500, 300, 25, 0.001, 0)) spawn.mapVertex(3875, 350, "-100 0 100 0 100 700 0 750 -100 700"); spawn.mapVertex(2900, 1743, "-100 0 70 0 100 30 100 1000 -100 1000"); spawn.mapVertex(3025, 1811, "-150 0 120 0 150 30 150 600 -150 600"); spawn.mapVertex(3200, 2079, "-150 0 120 0 150 30 150 600 -150 600"); spawn.mapVertex(4425, 1743, "-150 30 -120 0 150 0 150 1000 -150 1000"); spawn.mapVertex(4250, 1811, "-150 30 -120 0 150 0 150 600 -150 600"); spawn.mapVertex(4075, 2079, "-150 30 -120 0 150 0 150 600 -150 600"); //escape ledge when drowning spawn.mapRect(2750, 525, 100, 25); spawn.mapRect(2750, 125, 100, 25); spawn.mapRect(4425, 800, 75, 25); spawn.mapRect(4425, 325, 75, 25); // spawn.mapRect(4425, -100, 75, 25); // spawn.mapRect(4425, -550, 75, 25); // spawn.mapRect(4425, -1000, 75, 25); // if (Math.random() < 0.5) { // spawn.mapRect(2775, 525, 100, 25); // spawn.mapRect(3200, 75, 125, 25); // } else { // spawn.mapRect(4400, 800, 100, 25); // spawn.mapRect(3925, 400, 100, 25); // } } //left side if (Math.random() < 1) { // spawn.mapVertex(2325, 1325, "-150 0 150 0 150 150 0 225 -150 150"); spawn.mapVertex(1285, 1275, "-150 0 150 0 150 150 0 225 -150 150"); spawn.mapVertex(1033, 1750, "0 200 200 200 300 50 300 -50 200 -200 0 -200"); spawn.mapVertex(1575, 1750, "0 200 -200 200 -300 50 -300 -50 -200 -200 0 -200 100 -50 100 50"); //larger "0 400 -250 400 -400 100 -400 -100 -250 -400 0 -400" spawn.mapVertex(1287, 2185, "-100 30 -80 0 80 0 100 30 100 300 -100 300"); spawn.mapVertex(2050, 2050, "-150 30 -120 0 120 0 150 30 150 300 -150 300"); // spawn.mapRect(1700, 1550, 275, 25); // spawn.mapRect(2175, 1275, 325, 25); spawn.mapRect(1600, 950, 375, 25); spawn.mapRect(1025, -50, 50, 25); spawn.mapRect(1025, 275, 175, 25); spawn.mapRect(1025, 600, 325, 25); spawn.mapRect(2450, -50, 50, 25); spawn.mapRect(2325, 275, 175, 25); spawn.mapRect(2175, 600, 325, 25); // spawn.mapVertex(3400, 1250, "-100 -300 0 -350 100 -300 100 300 0 350 -100 300"); } //left button room in center divider spawn.mapRect(2525, -350, 300, 1100); spawn.mapRect(2525, 975, 300, 800); spawn.mapRect(2775, 650, 50, 125); spawn.mapRect(2525, 650, 50, 125); //exit room spawn.mapRect(2475, 2040, 350, 200); spawn.mapRect(2800, 1750, 25, 325); spawn.mapRect(2525, 1750, 50, 75); //safety edge blocks //maybe remove? // spawn.mapRect(2525, -375, 25, 50); // spawn.mapRect(2800, -375, 25, 50); // spawn.mapRect(1825, -1450, 25, 50); // spawn.mapRect(4000, -375, 25, 50); //blocks spawn.bodyRect(150, -175, 50, 165, 0.2); //block at entrance spawn.bodyRect(1275, 825, 100, 100, 0.2); spawn.bodyRect(2600, -425, 150, 50, 0.2); spawn.bodyRect(3900, -150, 50, 100, 0.2); spawn.bodyRect(3350, 1950, 75, 100, 0.2); spawn.bodyRect(3850, 1975, 75, 75, 0.2); spawn.bodyRect(1600, 1950, 75, 100, 0.2); spawn.bodyRect(725, -1650, 150, 100, 0.2); spawn.bodyRect(800, -1700, 75, 50, 0.2); const spawnRightMobs = () => { spawn.randomMob(4200, 100, 1); spawn.randomMob(4200, 600, 1); spawn.randomMob(2975, 625, 0.5); spawn.randomMob(3050, 100, 0.5); spawn.randomMob(3400, -100, 0.4); spawn.randomMob(3825, -100, 0.4); spawn.randomMob(3625, 1950, 0.4); spawn.randomMob(3275, 1650, 0.4); spawn.randomMob(3075, 1375, 0.3); spawn.randomMob(4000, 1650, 0.1); spawn.randomMob(4100, 1425, 0); spawn.randomGroup(3025, 325, 1); if (simulation.difficulty > 1) spawn.secondaryBossChance(3520, 1169) } const spawnLeftMobs = () => { spawn.randomMob(2375, 1900, 1); spawn.randomMob(1825, 1325, 0.5); spawn.randomMob(2250, 1050, 0.5); spawn.randomMob(1675, 825, 0.4); spawn.randomMob(1250, 575, 0.4); spawn.randomMob(2400, 575, 0.4); spawn.randomMob(1250, 1575, 0.3); spawn.randomMob(1075, -100, 0.3); spawn.randomMob(2450, -100, 0.2); spawn.randomGroup(1350, -775, 1); if (simulation.difficulty > 1) spawn.randomLevelBoss(1491, 495); } spawn.randomMob(2650, -750, 0.4); spawn.randomMob(300, -1725, 0.4); spawn.randomMob(750, -1775, 0.4); spawn.randomMob(550, -2225, 0.4); spawn.randomMob(2700, -475, 0.4); spawn.randomMob(2375, -200, 0.2); spawn.randomMob(3350, -225, 0.3); powerUps.addResearchToLevel() //needs to run after mobs are spawned }, sewers() { const button1 = level.button(6600, 2675) // const hazard = level.hazard(4550, 2750, 4550, 150) const hazard = level.hazard(simulation.isHorizontalFlipped ? -4550 - 4550 : 4550, 2750, 4550, 150) let balance1, balance2, balance3, balance4, rotor const drip1 = level.drip(6100, 1900, 2900, 100) // drip(x, yMin, yMax, period = 100, color = "hsla(160, 100%, 35%, 0.5)") { const drip2 = level.drip(7300, 1900, 2900, 150) const drip3 = level.drip(8750, 1900, 2900, 70) level.custom = () => { drip1.draw(); drip2.draw(); drip3.draw(); button1.query(); button1.draw(); ctx.fillStyle = "hsl(175, 15%, 76%)" ctx.fillRect(9100, 2200, 800, 400) ctx.fillStyle = "rgba(0,0,0,0.03)" //shadows ctx.fillRect(6250, 2025, 700, 650) ctx.fillRect(8000, 2025, 600, 575) level.exit.drawAndCheck(); level.enter.draw(); }; level.customTopLayer = () => { rotor.rotate(); ctx.fillStyle = "#233" ctx.beginPath(); ctx.arc(balance1.center.x, balance1.center.y, 9, 0, 2 * Math.PI); ctx.moveTo(balance2.center.x, balance2.center.y) ctx.arc(balance2.center.x, balance2.center.y, 9, 0, 2 * Math.PI); ctx.moveTo(balance3.center.x, balance3.center.y) ctx.arc(balance3.center.x, balance3.center.y, 9, 0, 2 * Math.PI); ctx.moveTo(balance4.center.x, balance4.center.y) ctx.arc(balance4.center.x, balance4.center.y, 9, 0, 2 * Math.PI); ctx.moveTo(balance5.center.x, balance5.center.y) ctx.arc(balance5.center.x, balance5.center.y, 9, 0, 2 * Math.PI); ctx.moveTo(rotor.center.x, rotor.center.y) ctx.arc(rotor.center.x, rotor.center.y, 9, 0, 2 * Math.PI); ctx.fill(); hazard.query(); hazard.level(button1.isUp) }; level.setPosToSpawn(0, -50); //normal spawn spawn.mapRect(level.enter.x, level.enter.y + 20, 100, 20); level.exit.x = 9700; level.exit.y = 2560; level.defaultZoom = 1800 simulation.zoomTransition(level.defaultZoom) document.body.style.backgroundColor = "hsl(138, 3%, 74%)"; color.map = "#3d4240" powerUps.spawnStartingPowerUps(3475, 1775); spawn.debris(4575, 2550, 1600, 9); //16 debris per level spawn.debris(7000, 2550, 2000, 7); //16 debris per level spawn.mapRect(-500, -600, 200, 800); //left entrance wall spawn.mapRect(-400, -600, 3550, 200); //ceiling spawn.mapRect(-400, 0, 3000, 200); //floor // spawn.mapRect(300, -500, 50, 400); //right entrance wall // spawn.bodyRect(312, -100, 25, 100); spawn.bodyRect(1450, -300, 150, 50); const xPos = shuffle([600, 1250, 2000]); spawn.mapRect(xPos[0], -200, 300, 100); spawn.mapRect(xPos[1], -250, 300, 300); spawn.mapRect(xPos[2], -150, 300, 200); spawn.bodyRect(3100, 410, 75, 100); spawn.bodyRect(2450, -25, 250, 25); spawn.mapRect(3050, -600, 200, 800); //right down tube wall spawn.mapRect(3100, 0, 1200, 200); //tube right exit ceiling spawn.mapRect(4200, 0, 200, 1900); spawn.mapVertex(3500, 1000, "-500 -500 -400 -600 400 -600 500 -500 500 500 400 600 -400 600 -500 500"); spawn.mapVertex(3600, 1940, "-400 -40 -350 -90 350 -90 400 -40 400 40 350 90 -350 90 -400 40"); spawn.mapRect(3925, 2288, 310, 50); spawn.mapRect(3980, 2276, 200, 50); spawn.mapRect(2625, 2288, 650, 50); spawn.mapRect(2700, 2276, 500, 50); spawn.mapRect(2400, 0, 200, 1925); //left down tube wall spawn.mapRect(600, 2300, 3750, 200); spawn.bodyRect(3800, 275, 125, 125); spawn.mapRect(4200, 1700, 5000, 200); spawn.mapRect(4150, 2300, 200, 400); spawn.mapRect(600, 1700, 2000, 200); //bottom left room ceiling spawn.mapRect(500, 1700, 200, 800); //left wall spawn.mapRect(675, 1875, 325, 150, 0.5); spawn.mapRect(4450, 2900, 4900, 200); //boss room floor spawn.mapRect(4150, 2600, 400, 500); spawn.mapRect(6250, 2675, 700, 325); spawn.mapRect(8000, 2600, 600, 400); spawn.bodyRect(5875, 2725, 200, 200); spawn.bodyRect(6800, 2490, 50, 50); spawn.bodyRect(6800, 2540, 50, 50); spawn.bodyRect(6800, 2590, 50, 50); spawn.bodyRect(8225, 2225, 100, 100); spawn.mapRect(6250, 1875, 700, 150); spawn.mapRect(8000, 1875, 600, 150); spawn.mapRect(9100, 1700, 900, 500); //exit spawn.mapRect(9100, 2600, 900, 500); spawn.mapRect(9900, 1700, 200, 1400); //back wall // spawn.mapRect(9300, 2150, 50, 250); spawn.mapRect(9300, 2590, 650, 25); spawn.mapRect(9700, 2580, 100, 50); spawn.randomGroup(1300, 2100, 0.1); spawn.randomMob(8300, 2100, 0.1); spawn.randomSmallMob(2575, -75, 0.1); //entrance spawn.randomMob(8125, 2450, 0.1); spawn.randomSmallMob(3200, 250, 0.1); spawn.randomMob(2425, 2150, 0.1); spawn.randomSmallMob(3500, 250, 0.2); spawn.randomMob(3800, 2175, 0.2); spawn.randomSmallMob(2500, -275, 0.2); //entrance spawn.randomMob(4450, 2500, 0.2); spawn.randomMob(6350, 2525, 0.2); spawn.randomGroup(9200, 2400, 0.3); spawn.randomSmallMob(1900, -250, 0.3); //entrance spawn.randomMob(1500, 2100, 0.4); spawn.randomSmallMob(1700, -150, 0.4); //entrance spawn.randomMob(8800, 2725, 0.5); spawn.randomMob(7300, 2200, 0.5); spawn.randomMob(2075, 2025, 0.5); spawn.randomMob(3475, 2175, 0.5); spawn.randomMob(8900, 2825, 0.5); spawn.randomMob(9600, 2425, 0.9); spawn.randomMob(3600, 1725, 0.9); spawn.randomMob(4100, 1225, 0.9); spawn.randomMob(2825, 400, 0.9); if (simulation.difficulty > 1) spawn.randomLevelBoss(6000, 2300, ["dragonFlyBoss", "beetleBoss", "spiderBoss", "launcherBoss", "laserTargetingBoss", "blinkBoss", "streamBoss", "historyBoss", "orbitalBoss", "grenadierBoss", "blockBoss", "revolutionBoss", "slashBoss"]); powerUps.addResearchToLevel() //needs to run after mobs are spawned spawn.secondaryBossChance(7725, 2275) if (simulation.isHorizontalFlipped) { //flip the map horizontally level.flipHorizontal(); //only flips map,body,mob,powerUp,cons,consBB, exit // rotor(x, y, width, height, density = 0.001, angle = 0, frictionAir = 0.001, angularVelocity = 0, rotationForce = 0.0005) { // rotor = level.rotor(-5100, 2475, 0.001) //rotates other direction because flipped rotor = level.rotor(-5600, 2390, 850, 50, 0.001, 0, 0.01, 0, 0.001) //balance(x, y, width, height, density = 0.001, angle = 0, frictionAir = 0.001, angularVelocity = 0, rotationForce = 0.0005) { balance1 = level.rotor(-300 - 25, -395, 25, 390, 0.001) //entrance balance2 = level.rotor(-2605 - 390, 500, 390, 25, 0.001) //falling balance3 = level.rotor(-2608 - 584, 1900, 584, 25, 0.001) //falling balance4 = level.rotor(-9300 - 25, 2205, 25, 380, 0.001) //exit balance5 = level.rotor(-2605 - 390, 1100, 390, 25, 0.001) //falling // boost1.boostBounds.min.x = -boost1.boostBounds.min.x - 100 // boost1.boostBounds.max.x = -boost1.boostBounds.max.x + 100 // level.setPosToSpawn(300, -700); //-x // no need since 0 button1.min.x = -button1.min.x - 126 // flip the button horizontally button1.max.x = -button1.max.x + 126 // flip the button horizontally drip1.x *= -1 drip2.x *= -1 drip3.x *= -1 level.custom = () => { drip1.draw(); drip2.draw(); drip3.draw(); button1.query(); button1.draw(); rotor.rotate(); ctx.fillStyle = "hsl(175, 15%, 76%)" ctx.fillRect(-9900, 2200, 800, 400) ctx.fillStyle = "rgba(0,0,0,0.03)" //shadows ctx.fillRect(-6950, 2025, 700, 650) ctx.fillRect(-8600, 2025, 600, 575) level.exit.drawAndCheck(); level.enter.draw(); }; // level.customTopLayer = () => {}; } else { // rotor = level.rotor(5100, 2475, -0.001) rotor = level.rotor(4700, 2390, 850, 50, 0.001, 0, 0.01, 0, -0.001) //balance(x, y, width, height, density = 0.001, angle = 0, frictionAir = 0.001, angularVelocity = 0, rotationForce = 0.0005) { balance1 = level.rotor(300, -395, 25, 390, 0.001) //entrance balance2 = level.rotor(2605, 500, 390, 25, 0.001) //falling balance3 = level.rotor(2608, 1900, 584, 25, 0.001) //falling balance4 = level.rotor(9300, 2205, 25, 380, 0.001) //exit balance5 = level.rotor(2605, 1100, 390, 25, 0.001) //falling } }, satellite() { const boost1 = level.boost(5825, 235, 1400) const elevator = level.elevator(4210, -1265, 380, 50, -3450) //, 0.003, { up: 0.01, down: 0.2 } level.custom = () => { boost1.query(); ctx.fillStyle = "#d4f4f4" ctx.fillRect(-250, -750, 420, 450) ctx.fillStyle = "#d0d4d6" ctx.fillRect(-300, -1900, 500, 1100) ctx.fillRect(900, -2450, 450, 2050) ctx.fillRect(2000, -2800, 450, 2500) ctx.fillRect(3125, -3100, 450, 3300) level.exit.drawAndCheck(); level.enter.draw(); }; level.customTopLayer = () => { ctx.fillStyle = "rgba(0,20,40,0.25)" ctx.fillRect(-250, -400, 1800, 775) ctx.fillRect(1800, -275, 850, 775) ctx.fillRect(5200, 125, 450, 200) ctx.fillStyle = "rgba(0,20,40,0.1)" ctx.fillRect(4000, -1200, 1050, 1500) ctx.fillRect(4100, -3450, 600, 2250) elevator.move() }; level.setPosToSpawn(-100, 210); //normal spawn spawn.mapRect(-150, 240, 100, 30); level.exit.x = -100; level.exit.y = -425; spawn.mapRect(level.exit.x, level.exit.y + 15, 100, 50); //exit bump level.defaultZoom = 1700 // 4500 // 1400 simulation.zoomTransition(level.defaultZoom) powerUps.spawnStartingPowerUps(4900, -500); //1 per level spawn.debris(1000, 20, 1800, 3); //16 debris per level //but less here because a few mobs die from laser spawn.debris(4830, -1330, 850, 3); //16 debris per level spawn.debris(3035, -3900, 1500, 3); //16 debris per level document.body.style.backgroundColor = "#dbdcde"; //spawn start building spawn.mapRect(-350, -800, 100, 1100); // spawn.mapRect(-300, -10, 500, 50); spawn.mapRect(150, -510, 50, 365); spawn.bodyRect(170, -140, 20, 163, 1, spawn.propsFriction); //door to starting room spawn.mapVertex(175, 200, "625 0 300 0 425 -300 500 -300"); //entrance ramp // spawn.mapRect(-300, 0, 1000, 300); //ground spawn.mapRect(-350, 250, 6350, 300); //deeper ground spawn.bodyRect(2100, 50, 80, 80); spawn.bodyRect(2000, 50, 60, 60); // spawn.bodyRect(1650, 50, 300, 200); // spawn.mapRect(1800, Math.floor(Math.random() * 200), 850, 300); //stops above body from moving to right spawn.mapVertex(2225, 250, "575 0 -575 0 -450 -100 450 -100"); //base //exit building // spawn.mapRect(-100, -410, 100, 30); spawn.mapRect(-350, -850, 550, 100); spawn.mapRect(150, -800, 50, 110); spawn.bodyRect(170, -690, 14, 180, 1, spawn.propsFriction); //door to exit room spawn.mapRect(-300, -400, 500, 150); //far left starting ceiling //tall platform above exit spawn.mapRect(-500, -1900, 400, 50); //super high shade spawn.mapRect(0, -1900, 400, 50); //super high shade spawn.mapRect(-150, -1350, 200, 25); //super high shade spawn.bodyRect(140, -2100, 150, 200); //shield from laser //tall platform spawn.mapVertex(1125, -450, "325 0 250 80 -250 80 -325 0 -250 -80 250 -80"); //base spawn.mapRect(150, -500, 1410, 100); //far left starting ceiling spawn.mapRect(625, -2450, 1000, 50); //super high shade spawn.bodyRect(1300, -3600, 150, 150); //shield from laser //tall platform spawn.mapVertex(2225, -250, "325 0 250 80 -250 80 -325 0 -250 -80 250 -80"); //base spawn.mapRect(1725, -2800, 1000, 50); //super high shade spawn.mapRect(1790, -300, 870, 100); //far left starting ceiling spawn.bodyRect(2400, -2950, 150, 150); //shield from laser //tall platform spawn.mapVertex(3350, 175, "425 0 -425 0 -275 -300 275 -300"); //base spawn.bodyRect(3350, -150, 200, 120); spawn.mapRect(2850, -3150, 1000, 50); //super high shade spawn.bodyRect(3675, -3470, 525, 20); //plank spawn.bodyRect(3600, -3450, 200, 300); //plank support block //far right structure spawn.mapRect(5200, -725, 100, 870); spawn.mapRect(5300, -1075, 350, 1220); //structure bellow tall stairs spawn.mapRect(3900, -300, 450, 50); spawn.mapRect(4675, -375, 450, 50); // spawn.mapRect(4000, -1300, 1050, 100); spawn.mapRect(4000, -1300, 200, 100); spawn.mapRect(4600, -1300, 450, 100); //steep stairs spawn.mapRect(4100, -2250, 100, 650); spawn.mapRect(4100, -3450, 100, 850); //left top shelf spawn.mapRect(4600, -3450, 100, 1850); spawn.randomSmallMob(4400, -3500); spawn.randomSmallMob(4800, -800); spawn.randomMob(800, -2600); spawn.randomMob(700, -600, 0.3); spawn.randomMob(3100, -3600, 0.3); spawn.randomMob(3300, -1000, 0.3); spawn.randomMob(4200, -250, 0.3); spawn.randomMob(4900, -1500, 0.3); spawn.randomMob(3800, 175, 0.4); spawn.randomMob(5750, 125, 0.4); spawn.randomMob(5900, -1500, 0.4); spawn.randomMob(4700, -800, 0.4); spawn.randomMob(1400, 200, 0.3); spawn.randomMob(2850, 175, 0.4); spawn.randomMob(2000, -2800, 0.4); spawn.randomMob(2400, -400, 0.4); spawn.randomMob(4475, -3550, 0.3); spawn.randomGroup(5000, -2150, 1); spawn.randomGroup(3700, -4100, 0.3); spawn.randomGroup(2700, -1600, 0.1); spawn.randomGroup(1600, -100, 0); spawn.randomGroup(5000, -3900, -0.3); if (simulation.difficulty > 1) { if (Math.random() < 0.25) { spawn.randomLevelBoss(2800, -1400); } else if (Math.random() < 0.25) { spawn.laserBoss(2900 + 300 * Math.random(), -2950 + 150 * Math.random()); } else if (Math.random() < 0.33) { spawn.laserBoss(1800 + 250 * Math.random(), -2600 + 150 * Math.random()); } else if (Math.random() < 0.5) { spawn.laserBoss(3500 + 250 * Math.random(), -2600 + 1000 * Math.random()); } else { spawn.laserBoss(600 + 200 * Math.random(), -2150 + 250 * Math.random()); } } powerUps.addResearchToLevel() //needs to run after mobs are spawned spawn.secondaryBossChance(3950, -850) if (simulation.isHorizontalFlipped) { //flip the map horizontally level.flipHorizontal(); //only flips map,body,mob,powerUp,cons,consBB, exit boost1.boostBounds.min.x = -boost1.boostBounds.min.x - 100 boost1.boostBounds.max.x = -boost1.boostBounds.max.x + 100 level.setPosToSpawn(100, 210); //-x elevator.holdX = -elevator.holdX // flip the elevator horizontally level.custom = () => { boost1.query(); ctx.fillStyle = "#d4f4f4" ctx.fillRect(250 - 420, -750, 420, 450) ctx.fillStyle = "#d0d4d6" ctx.fillRect(300 - 500, -1900, 500, 1100) ctx.fillRect(-900 - 450, -2450, 450, 2050) ctx.fillRect(-2000 - 450, -2800, 450, 2500) ctx.fillRect(-3125 - 450, -3100, 450, 3300) level.exit.drawAndCheck(); level.enter.draw(); }; level.customTopLayer = () => { elevator.move() ctx.fillStyle = "rgba(0,20,40,0.25)" ctx.fillRect(250 - 1800, -400, 1800, 775) ctx.fillRect(-1800 - 850, -275, 850, 775) ctx.fillRect(-5200 - 450, 125, 450, 200) ctx.fillStyle = "rgba(0,20,40,0.1)" ctx.fillRect(-4000 - 1050, -1200, 1050, 1500) ctx.fillRect(-4100 - 600, -3450, 600, 2250) }; } }, rooftops() { const elevator = level.elevator(1450, -990, 235, 45, -2000) const boost1 = level.boost(4950, 0, 1100) level.custom = () => { boost1.query(); elevator.move(); elevator.drawTrack(); ctx.fillStyle = "#d4f4f4" if (isBackwards) { ctx.fillRect(-650, -2300, 440, 300) } else { ctx.fillRect(3460, -700, 1090, 800) } level.exit.drawAndCheck(); level.enter.draw(); }; level.customTopLayer = () => { ctx.fillStyle = "rgba(0,0,0,0.1)" ctx.fillRect(710, -2225, 580, 225) ctx.fillRect(3510, -1550, 330, 300) ctx.fillRect(1735, -900, 1515, 1900) ctx.fillRect(1735, -1550, 1405, 550) ctx.fillRect(1860, -1950, 630, 350) ctx.fillRect(-700, -1950, 2100, 2950) ctx.fillRect(3400, 100, 2150, 900) ctx.fillRect(4550, -725, 900, 725) ctx.fillRect(3460, -1250, 1080, 550) if (isBackwards) { ctx.fillRect(3460, -700, 1090, 800) } else { ctx.fillRect(-650, -2300, 440, 300) } }; level.defaultZoom = 1700 simulation.zoomTransition(level.defaultZoom) document.body.style.backgroundColor = "#dcdcde"; let isBackwards = false if (Math.random() < 0.75) { //normal direction start in top left level.setPosToSpawn(-450, -2060); level.exit.x = 3600; level.exit.y = -300; spawn.mapRect(3600, -285, 100, 50); //ground bump wall //mobs that spawn in exit room spawn.bodyRect(4850, -750, 300, 25, 0.6); // spawn.randomSmallMob(4100, -100); spawn.randomSmallMob(4600, -100); spawn.randomMob(3765, -450, 0.3); } else { isBackwards = true //reverse direction, start in bottom right level.setPosToSpawn(3650, -325); level.exit.x = -550; level.exit.y = -2030; spawn.mapRect(-550, -2015, 100, 50); //ground bump wall } spawn.mapRect(level.enter.x, level.enter.y + 20, 100, 20); spawn.debris(1650, -1800, 3800, 16); //16 debris per level powerUps.spawnStartingPowerUps(2450, -1675); //spawn.mapRect(-700, 0, 6250, 100); //ground spawn.mapRect(3400, 0, 2150, 100); //ground spawn.mapRect(-700, -2000, 2125, 50); //Top left ledge spawn.bodyRect(1300, -2125, 50, 125, 0.8); spawn.bodyRect(1307, -2225, 50, 100, 0.8); spawn.mapRect(-700, -2350, 50, 400); //far left starting left wall spawn.mapRect(-700, -2010, 500, 50); //far left starting ground spawn.mapRect(-700, -2350, 500, 50); //far left starting ceiling spawn.mapRect(-250, -2350, 50, 200); //far left starting right part of wall spawn.bodyRect(-240, -2150, 30, 36); //door to starting room spawn.bodyRect(-240, -2115, 30, 36); //door to starting room spawn.bodyRect(-240, -2080, 30, 35); //door to starting room spawn.bodyRect(-240, -2045, 30, 35); //door to starting room spawn.mapRect(1850, -2000, 650, 50); spawn.bodyRect(200, -2150, 80, 220, 0.8); spawn.mapRect(700, -2275, 600, 50); spawn.mapRect(1000, -1350, 410, 50); spawn.bodyRect(1050, -2350, 30, 30, 0.8); // spawn.bodyRect(1625, -1100, 100, 75); // spawn.bodyRect(1350, -1025, 400, 25); // ground plank spawn.mapRect(-725, -1000, 2150, 100); //lower left ledge spawn.bodyRect(350, -1100, 200, 100, 0.8); spawn.bodyRect(370, -1200, 100, 100, 0.8); spawn.bodyRect(360, -1300, 100, 100, 0.8); spawn.bodyRect(950, -1050, 300, 50, 0.8); spawn.bodyRect(-575, -1150, 125, 150, 0.8); spawn.mapRect(1710, -1000, 1565, 100); //middle ledge spawn.mapRect(3400, -1000, 75, 25); spawn.bodyRect(2600, -1950, 100, 250, 0.8); spawn.bodyRect(2700, -1125, 125, 125, 0.8); spawn.bodyRect(2710, -1250, 125, 125, 0.8); spawn.bodyRect(2705, -1350, 75, 100, 0.8); spawn.mapRect(3500, -1600, 350, 50); spawn.mapRect(1725, -1600, 1435, 50); spawn.bodyRect(3100, -1015, 375, 15); spawn.bodyRect(3500, -850, 75, 125, 0.8); spawn.mapRect(3450, -1000, 50, 580); //left building wall spawn.bodyRect(3460, -420, 30, 144); spawn.mapRect(5450, -775, 100, 875); //right building wall spawn.bodyRect(3925, -1400, 100, 150, 0.8); spawn.mapRect(3450, -1250, 1090, 50); // spawn.mapRect(3450, -1225, 50, 75); spawn.mapRect(4500, -1250, 50, 415); spawn.mapRect(3450, -725, 1500, 50); spawn.mapRect(5100, -725, 400, 50); spawn.mapRect(4500, -735, 50, 635); spawn.bodyRect(4500, -100, 50, 100); spawn.mapRect(4500, -885, 100, 50); spawn.spawnStairs(3800, 0, 3, 150, 206); //stairs top exit spawn.mapRect(3400, -275, 450, 275); //exit platform spawn.randomSmallMob(2200, -1775); spawn.randomSmallMob(4000, -825); spawn.randomSmallMob(-350, -3400); spawn.randomMob(4250, -1350, 0.8); spawn.randomMob(2550, -1350, 0.8); spawn.randomMob(1875, -1075, 0.3); spawn.randomMob(1120, -1200, 0.3); spawn.randomMob(3000, -1150, 0.2); spawn.randomMob(3200, -1150, 0.3); spawn.randomMob(3300, -1750, 0.3); spawn.randomMob(3650, -1350, 0.3); spawn.randomMob(3600, -1800, 0.1); spawn.randomMob(5200, -100, 0.3); spawn.randomMob(5275, -900, 0.2); spawn.randomMob(0, -1075, 0.3); spawn.randomGroup(600, -1575, 0); spawn.randomGroup(2225, -1325, 0.4); spawn.randomGroup(4900, -1200, 0); if (simulation.difficulty > 1) spawn.randomLevelBoss(3200, -1900); powerUps.addResearchToLevel() //needs to run after mobs are spawned spawn.secondaryBossChance(2175, -2425) if (simulation.isHorizontalFlipped) { //flip the map horizontally level.flipHorizontal(); //only flips map,body,mob,powerUp,cons,consBB, exit boost1.boostBounds.min.x = -boost1.boostBounds.min.x - 100 boost1.boostBounds.max.x = -boost1.boostBounds.max.x + 100 elevator.holdX = -elevator.holdX // flip the elevator horizontally if (isBackwards) { level.setPosToSpawn(-3650, -325); //-x } else { level.setPosToSpawn(450, -2060); //-x } level.custom = () => { boost1.query(); elevator.move(); elevator.drawTrack(); ctx.fillStyle = "#d4f4f4" if (isBackwards) { ctx.fillRect(650 - 440, -2300, 440, 300) } else { ctx.fillRect(-3460 - 1090, -700, 1090, 800) } level.exit.drawAndCheck(); level.enter.draw(); }; level.customTopLayer = () => { ctx.fillStyle = "rgba(0,0,0,0.1)" ctx.fillRect(-710 - 580, -2225, 580, 225) ctx.fillRect(-3510 - 330, -1550, 330, 300) ctx.fillRect(-1735 - 1515, -900, 1515, 1900) ctx.fillRect(-1735 - 1405, -1550, 1405, 550) ctx.fillRect(-1860 - 630, -1950, 630, 350) ctx.fillRect(700 - 2100, -1950, 2100, 2950) ctx.fillRect(-3400 - 2150, 100, 2150, 900) ctx.fillRect(-4550 - 900, -725, 900, 725) ctx.fillRect(-3460 - 1080, -1250, 1080, 550) if (isBackwards) { ctx.fillRect(-3460 - 1090, -700, 1090, 800) } else { ctx.fillRect(650 - 440, -2300, 440, 300) } }; } }, aerie() { const boost1 = level.boost(-425, 100, 1400) const boost2 = level.boost(5350, 275, 2850); level.custom = () => { boost1.query(); boost2.query(); if (backwards) { ctx.fillStyle = "#d4f4f4" ctx.fillRect(-275, -1275, 425, 300) } else { ctx.fillStyle = "#d4f4f4" ctx.fillRect(3750, -3650, 550, 400) } ctx.fillStyle = "#c7c7ca" ctx.fillRect(4200, -2200, 100, 2600) // ctx.fillStyle = "#c7c7ca" ctx.fillRect(-100, -1000, 1450, 1400) level.exit.drawAndCheck(); level.enter.draw(); }; level.customTopLayer = () => { if (backwards) { ctx.fillStyle = "rgba(0,0,0,0.1)" ctx.fillRect(3750, -3650, 550, 400) } else { ctx.fillStyle = "rgba(0,0,0,0.1)" ctx.fillRect(-275, -1275, 425, 300) } ctx.fillStyle = "rgba(0,0,0,0.1)" ctx.fillRect(3700, -3150, 1100, 950) ctx.fillRect(2000, -1110, 450, 1550) ctx.fillStyle = "rgba(0,0,0,0.04)" ctx.beginPath() ctx.moveTo(-100, -900) ctx.lineTo(300, -900) ctx.lineTo(150, 100) ctx.lineTo(-100, 100) ctx.moveTo(600, -900) ctx.lineTo(1350, -900) ctx.lineTo(1350, 100) ctx.lineTo(750, 100) ctx.fill() }; // simulation.difficulty = 4; //for testing to simulate possible mobs spawns level.defaultZoom = 2100 simulation.zoomTransition(level.defaultZoom) const backwards = (Math.random() < 0.25 && simulation.difficulty > 8) ? true : false; if (backwards) { level.setPosToSpawn(4000, -3300); //normal spawn level.exit.x = -100; level.exit.y = -1025; } else { level.setPosToSpawn(-50, -1050); //normal spawn level.exit.x = 3950; level.exit.y = -3275; } spawn.mapRect(level.enter.x, level.enter.y + 20, 100, 20); spawn.mapRect(level.exit.x, level.exit.y + 15, 100, 20); powerUps.spawnStartingPowerUps(1075, -550); document.body.style.backgroundColor = "#dcdcde"; // starting room spawn.mapRect(-300, -1000, 600, 100); spawn.mapRect(-300, -1300, 450, 50); spawn.mapRect(-300, -1300, 50, 350); if (!backwards) spawn.bodyRect(100, -1250, 200, 240); //remove on backwards //left building spawn.mapRect(-100, -975, 100, 975); spawn.mapRect(-500, 100, 1950, 400); spawn.mapRect(600, -1000, 750, 100); spawn.mapRect(900, -500, 550, 100); spawn.mapRect(1250, -975, 100, 375); spawn.bodyRect(1250, -600, 100, 100, 0.7); spawn.mapRect(1250, -450, 100, 450); spawn.bodyRect(1250, -1225, 100, 200, 0.7); //remove on backwards spawn.bodyRect(1200, -1025, 350, 35); //remove on backwards //middle super tower if (backwards) { spawn.bodyRect(2000, -800, 700, 35); } else { spawn.bodyRect(1750, -800, 700, 35); } spawn.mapVertex(2225, -2100, "0 0 450 0 300 -2500 150 -2500") spawn.mapRect(2000, -700, 450, 300); spawn.bodyRect(2360, -450, 100, 300, 0.6); spawn.mapRect(2000, -75, 450, 275); spawn.bodyRect(2450, 150, 150, 150, 0.4); spawn.mapRect(1550, 300, 4600, 200); //ground // spawn.mapRect(6050, -700, 450, 1200); spawn.mapRect(6050, -1060, 450, 1560); spawn.mapVertex(6275, -2100, "0 0 450 0 300 -2500 150 -2500") //right tall tower spawn.mapRect(3700, -3200, 100, 800); spawn.mapRect(4700, -2910, 100, 510); spawn.mapRect(3700, -2600, 300, 50); spawn.mapRect(4100, -2900, 900, 50); spawn.mapRect(3450, -2300, 750, 100); spawn.mapRect(4300, -2300, 750, 100); spawn.mapRect(4150, -1600, 200, 25); spawn.mapRect(4150, -700, 200, 25); //exit room on top of tower spawn.mapRect(3700, -3700, 600, 50); spawn.mapRect(3700, -3700, 50, 500); spawn.mapRect(4250, -3700, 50, 300); spawn.mapRect(3700, -3250, 1100, 100); spawn.randomGroup(350, -500, 1) spawn.randomSmallMob(-225, 25); spawn.randomSmallMob(2100, -900); spawn.randomSmallMob(4000, -250); spawn.randomSmallMob(4450, -3000); spawn.randomSmallMob(5600, 100); spawn.randomMob(4275, -2600, 0.8); spawn.randomMob(1050, -700, 0.8) spawn.randomMob(6050, -850, 0.7); spawn.randomMob(2150, -300, 0.6) spawn.randomMob(3900, -2700, 0.8); spawn.randomMob(3600, -500, 0.8); spawn.randomMob(3400, -200, 0.8); // spawn.randomMob(1650, -1300, 0.7) spawn.randomMob(425, 0, 0.7); spawn.randomMob(4100, -50, 0.7); spawn.randomMob(4100, -50, 0.5); spawn.randomMob(1700, -50, 0.3) spawn.randomMob(2350, -900, 0.3) spawn.randomMob(4700, -150, 0.2); spawn.randomGroup(4000, -350, 0.6); spawn.randomGroup(2750, -550, 0.1); spawn.randomMob(2175, -925, 0.5); spawn.randomMob(2750, 100, 0.5); spawn.randomMob(4250, -1725, 0.5); spawn.randomMob(3575, -2425, 0.5); spawn.randomMob(3975, -3900, 0.5); spawn.randomMob(1725, 125, 0.5); if (simulation.difficulty > 1) { if (Math.random() < 0.5) { spawn.randomLevelBoss(4250, -250); spawn.debris(-250, 50, 1650, 2); //16 debris per level spawn.debris(2475, 0, 750, 2); //16 debris per level spawn.debris(3450, 0, 2000, 16); //16 debris per level spawn.debris(3500, -2350, 1500, 2); //16 debris per level } else { powerUps.chooseRandomPowerUp(4000, 200); powerUps.chooseRandomPowerUp(4000, 200); //floor below right tall tower spawn.bodyRect(3000, 50, 150, 250, 0.9); spawn.bodyRect(4500, -500, 300, 250, 0.7); spawn.bodyRect(3500, -100, 100, 150, 0.7); spawn.bodyRect(4200, -500, 110, 30, 0.7); spawn.bodyRect(3800, -500, 150, 130, 0.7); spawn.bodyRect(4000, 50, 200, 150, 0.9); spawn.bodyRect(4500, 50, 300, 200, 0.9); spawn.bodyRect(4200, -350, 200, 50, 0.9); spawn.bodyRect(4700, -350, 50, 200, 0.9); spawn.bodyRect(4900, -100, 300, 300, 0.7); spawn.suckerBoss(4500, -400); } } powerUps.addResearchToLevel() //needs to run after mobs are spawned spawn.secondaryBossChance(5350, -325) if (simulation.isHorizontalFlipped) { //flip the map horizontally level.flipHorizontal(); //only flips map,body,mob,powerUp,cons,consBB, exit boost1.boostBounds.min.x = -boost1.boostBounds.min.x - 100 boost1.boostBounds.max.x = -boost1.boostBounds.max.x + 100 boost2.boostBounds.min.x = -boost2.boostBounds.min.x - 100 boost2.boostBounds.max.x = -boost2.boostBounds.max.x + 100 if (backwards) { level.setPosToSpawn(-4000, -3300); //-x } else { level.setPosToSpawn(50, -1050); //-x } level.custom = () => { boost1.query(); boost2.query(); if (backwards) { ctx.fillStyle = "#d4f4f4" ctx.fillRect(275 - 425, -1275, 425, 300) } else { ctx.fillStyle = "#d4f4f4" ctx.fillRect(-3750 - 550, -3650, 550, 400) } ctx.fillStyle = "#c7c7ca" ctx.fillRect(-4200 - 100, -2200, 100, 2600) // ctx.fillStyle = "#c7c7ca" ctx.fillRect(100 - 1450, -1000, 1450, 1400) level.exit.drawAndCheck(); level.enter.draw(); }; level.customTopLayer = () => { if (backwards) { ctx.fillStyle = "rgba(0,0,0,0.1)" ctx.fillRect(-3750 - 550, -3650, 550, 400) } else { ctx.fillStyle = "rgba(0,0,0,0.1)" ctx.fillRect(275 - 425, -1275, 425, 300) } ctx.fillStyle = "rgba(0,0,0,0.1)" ctx.fillRect(-3700 - 1100, -3150, 1100, 950) ctx.fillRect(-2000 - 450, -1110, 450, 1550) ctx.fillStyle = "rgba(0,0,0,0.04)" ctx.beginPath() ctx.moveTo(100, -900) ctx.lineTo(-300, -900) ctx.lineTo(-150, 100) ctx.lineTo(100, 100) ctx.moveTo(-600, -900) ctx.lineTo(-1350, -900) ctx.lineTo(-1350, 100) ctx.lineTo(-750, 100) ctx.fill() }; } }, skyscrapers() { const boost1 = level.boost(475, 0, 1300) const boost2 = level.boost(4450, 0, 1300); level.custom = () => { boost1.query(); boost2.query(); ctx.fillStyle = "#d4f4f4" ctx.fillRect(1350, -2100, 400, 250) ctx.fillStyle = "#d4d4d7" ctx.fillRect(3350, -1300, 50, 1325) ctx.fillRect(1300, -1800, 750, 1800) level.exit.drawAndCheck(); level.enter.draw(); }; level.customTopLayer = () => { ctx.fillStyle = "rgba(0,0,0,0.1)" ctx.fillRect(2500, -1100, 450, 250) ctx.fillRect(2400, -550, 600, 150) ctx.fillRect(2550, -1650, 250, 200) ctx.fillStyle = "rgba(0,0,0,0.2)" ctx.fillRect(700, -110, 400, 110) ctx.fillRect(3800, -110, 400, 110) ctx.fillStyle = "rgba(0,0,0,0.15)" ctx.fillRect(-250, -300, 450, 300) }; level.setPosToSpawn(-50, -60); //normal spawn spawn.mapRect(level.enter.x, level.enter.y + 20, 100, 20); level.exit.x = 1500; level.exit.y = -1875; level.defaultZoom = 2000 simulation.zoomTransition(level.defaultZoom) powerUps.spawnStartingPowerUps(1475, -1175); spawn.debris(750, -2200, 3700, 16); //16 debris per level document.body.style.backgroundColor = "#dcdcde"; spawn.mapRect(-300, 0, 5100, 300); //***********ground spawn.mapRect(-300, -350, 50, 400); //far left starting left wall spawn.mapRect(-300, -10, 500, 50); //far left starting ground spawn.mapRect(-300, -350, 500, 50); //far left starting ceiling spawn.mapRect(150, -350, 50, 200); //far left starting right part of wall spawn.bodyRect(170, -130, 14, 140, 1, spawn.propsFriction); //door to starting room spawn.mapRect(700, -1100, 400, 990); //far left building spawn.mapRect(1600, -400, 1500, 500); //long center building spawn.mapRect(1345, -1100, 250, 25); //left platform spawn.mapRect(1755, -1100, 250, 25); //right platform spawn.mapRect(1300, -1850, 800, 50); //left higher platform spawn.mapRect(1300, -2150, 50, 350); //left higher platform left edge wall spawn.mapRect(1300, -2150, 450, 50); //left higher platform roof spawn.mapRect(1500, -1860, 100, 50); //ground bump wall spawn.mapRect(2400, -850, 600, 300); //center floating large square //spawn.bodyRect(2500, -1100, 25, 250); //wall before chasers spawn.mapRect(2500, -1450, 450, 350); //higher center floating large square spawn.mapRect(2500, -1675, 50, 300); //left wall on higher center floating large square spawn.mapRect(2500, -1700, 300, 50); //roof on higher center floating large square spawn.mapRect(3275, -750, 200, 25); //ledge by far right building spawn.mapRect(3275, -1300, 200, 25); //higher ledge by far right building spawn.mapRect(3800, -1100, 400, 990); //far right building spawn.bodyRect(3200, -1375, 300, 25, 0.9); spawn.bodyRect(1825, -1875, 400, 25, 0.9); // spawn.bodyRect(1800, -575, 250, 150, 0.8); spawn.bodyRect(1800, -600, 110, 150, 0.8); spawn.bodyRect(2557, -450, 35, 55, 0.7); spawn.bodyRect(2957, -450, 30, 15, 0.7); spawn.bodyRect(2900, -450, 60, 45, 0.7); spawn.bodyRect(915, -1200, 60, 100, 0.95); spawn.bodyRect(925, -1300, 50, 100, 0.95); if (Math.random() < 0.9) { spawn.bodyRect(2300, -1720, 400, 20); spawn.bodyRect(2590, -1780, 80, 80); } spawn.bodyRect(2925, -1100, 25, 250, 0.8); spawn.bodyRect(3325, -1550, 50, 200, 0.3); if (Math.random() < 0.8) { spawn.bodyRect(1400, -75, 200, 75); //block to get up ledge from ground spawn.bodyRect(1525, -125, 50, 50); //block to get up ledge from ground } spawn.bodyRect(1025, -1110, 400, 25, 0.9); //block on far left building spawn.bodyRect(1425, -1110, 115, 25, 0.9); //block on far left building spawn.bodyRect(1540, -1110, 300, 25, 0.9); //block on far left building spawn.randomMob(-100, -1300, 0.5); spawn.randomSmallMob(1850, -600); spawn.randomSmallMob(3200, -100); spawn.randomSmallMob(4450, -100); spawn.randomSmallMob(2700, -475); spawn.randomMob(2650, -975, 0.8); spawn.randomMob(2650, -1550, 0.8); spawn.randomMob(4150, -200, 0.15); spawn.randomMob(1700, -1300, 0.2); spawn.randomMob(1850, -1950, 0.25); spawn.randomMob(2610, -1880, 0.25); spawn.randomMob(3350, -950, 0.25); spawn.randomMob(1690, -2250, 0.25); spawn.randomMob(2200, -600, 0.2); spawn.randomMob(850, -1300, 0.25); spawn.randomMob(-100, -1700, -0.2); spawn.randomGroup(3700, -1500, 0.4); spawn.randomGroup(1700, -900, 0.4); if (simulation.difficulty > 1) spawn.randomLevelBoss(2800 + 200 * Math.random(), -2200 + 200 * Math.random()); powerUps.addResearchToLevel() //needs to run after mobs are spawned spawn.secondaryBossChance(4000, -1825) if (simulation.isHorizontalFlipped) { //flip the map horizontally level.flipHorizontal(); //only flips map,body,mob,powerUp,cons,consBB, exit boost1.boostBounds.min.x = -boost1.boostBounds.min.x - 100 boost1.boostBounds.max.x = -boost1.boostBounds.max.x + 100 boost2.boostBounds.min.x = -boost2.boostBounds.min.x - 100 boost2.boostBounds.max.x = -boost2.boostBounds.max.x + 100 level.setPosToSpawn(50, -60); //-x level.custom = () => { boost1.query(); boost2.query(); ctx.fillStyle = "#d4f4f4" ctx.fillRect(-1350 - 400, -2100, 400, 250) ctx.fillStyle = "#d4d4d7" ctx.fillRect(-3350 - 50, -1300, 50, 1325) ctx.fillRect(-1300 - 750, -1800, 750, 1800) level.exit.drawAndCheck(); level.enter.draw(); }; level.customTopLayer = () => { ctx.fillStyle = "rgba(0,0,0,0.1)" ctx.fillRect(-2500 - 450, -1100, 450, 250) ctx.fillRect(-2400 - 600, -550, 600, 150) ctx.fillRect(-2550 - 250, -1650, 250, 200) ctx.fillStyle = "rgba(0,0,0,0.2)" ctx.fillRect(-700 - 400, -110, 400, 110) ctx.fillRect(-3800 - 400, -110, 400, 110) ctx.fillStyle = "rgba(0,0,0,0.15)" ctx.fillRect(250 - 450, -300, 450, 300) }; } }, highrise() { const elevator1 = level.elevator(-790, -190, 180, 25, -1150, 0.0025, { up: 0.01, down: 0.2 }, true) //x, y, width, height, maxHeight, force = 0.003, friction = { up: 0.01, down: 0.2 }) { elevator1.addConstraint(); // const button1 = level.button(-500, -200) const toggle1 = level.toggle(-300, -200) //(x,y,isOn,isLockOn = true/false) const elevator2 = level.elevator(-3630, -970, 180, 25, -1740, 0.004) //x, y, width, height, maxHeight, force = 0.003, friction = { up: 0.01, down: 0.2 }) { elevator2.addConstraint(); // const button2 = level.button(-3100, -1330) const toggle2 = level.toggle(-3100, -1330) //(x,y,isOn, isLockOn = true/false) level.custom = () => { // ctx.fillStyle = "#d0d0d2" // ctx.fillRect(-2475, -2450, 25, 750) // ctx.fillRect(-2975, -2750, 25, 600) // ctx.fillRect(-3375, -2875, 25, 725) ctx.fillStyle = "#cff" //exit ctx.fillRect(-4425, -3050, 425, 275) level.exit.drawAndCheck(); level.enter.draw(); }; level.customTopLayer = () => { // button1.draw(); toggle1.query(); if (!toggle1.isOn) { if (elevator1.isOn) { elevator1.isOn = false elevator1.frictionAir = 0.2 elevator1.addConstraint(); } } else if (!elevator1.isOn) { elevator1.isOn = true elevator1.isUp = false elevator1.removeConstraint(); elevator1.frictionAir = 0.2 //elevator.isUp ? 0.01 : 0.2 } if (elevator1.isOn) { elevator1.move(); ctx.fillStyle = "#444" } else { ctx.fillStyle = "#aaa" } ctx.fillRect(-700, -1140, 1, 975) toggle2.query(); // button2.draw(); if (!toggle2.isOn) { if (elevator2.isOn) { elevator2.isOn = false elevator2.frictionAir = 0.2 elevator2.addConstraint(); } } else if (!elevator2.isOn) { elevator2.isOn = true elevator2.isUp = false elevator2.removeConstraint(); elevator2.frictionAir = 0.2 //elevator.isUp ? 0.01 : 0.2 } if (elevator2.isOn) { elevator2.move(); ctx.fillStyle = "#444" } else { ctx.fillStyle = "#aaa" } ctx.fillRect(-3540, -1720, 1, 770) ctx.fillStyle = "rgba(64,64,64,0.97)" //hidden section ctx.fillRect(-4450, -750, 800, 200) ctx.fillStyle = "rgba(0,0,0,0.12)" ctx.fillRect(-2500, -1975, 150, 300); ctx.fillRect(-1830, -1150, 2030, 1150) ctx.fillRect(-3410, -2150, 495, 1550) ctx.fillRect(-2585, -1675, 420, 1125) ctx.fillRect(-1650, -1575, 750, 450) }; level.setPosToSpawn(-300, -700); //normal spawn spawn.mapRect(level.enter.x, level.enter.y + 20, 100, 20); level.exit.x = -4275; level.exit.y = -2805; level.defaultZoom = 1500 simulation.zoomTransition(level.defaultZoom) powerUps.spawnStartingPowerUps(-2550, -700); document.body.style.backgroundColor = "#dcdcde" //"#fafcff"; spawn.debris(-2325, -1825, 2400); //16 debris per level spawn.debris(-2625, -600, 600, 5); //16 debris per level spawn.debris(-2000, -60, 1200, 5); //16 debris per level //3 platforms that lead to exit // spawn.mapRect(-3440, -2875, 155, 25); // spawn.mapRect(-3025, -2775, 125, 25); // spawn.mapRect(-2525, -2475, 125, 25); // spawn.bodyRect(-2600, -2500, 225, 20, 0.7); // spawn.bodyRect(-3350, -2900, 25, 25, 0.5); // spawn.bodyRect(-3400, -2950, 50, 75, 0.5); powerUps.spawn(-4300, -700, "heal"); powerUps.spawn(-4200, -700, "ammo"); powerUps.spawn(-4000, -700, "ammo"); spawn.mapRect(-4450, -1000, 100, 500); spawn.bodyRect(-3300, -750, 150, 150); //building 1 spawn.bodyRect(-1000, -675, 25, 25); spawn.mapRect(-2225, 0, 2475, 150); spawn.mapRect(175, -1000, 75, 1100); spawn.mapRect(-600, -1075, 50, 475); spawn.mapRect(-600, -650, 625, 50); spawn.mapRect(-1300, -650, 500, 50); spawn.bodyRect(-75, -300, 50, 50); spawn.mapRect(-600, -200, 500, 250); //ledge for boarding elevator spawn.bodyRect(-500, -300, 100, 100, 0.6); //a nice block near the elevator spawn.bodyRect(-425, -1375, 400, 225); spawn.mapRect(-925, -1575, 50, 475); spawn.bodyRect(-1475, -1275, 250, 125); // spawn.mapRect(-1650, -1575, 600, 50); // spawn.mapRect(-1875, -1575, 850, 50); spawn.mapRect(-1675, -1575, 650, 50); spawn.mapRect(-600, -1150, 850, 175); spawn.mapRect(-1850, -1150, 1050, 175); spawn.bodyRect(-1907, -1600, 550, 25); if (simulation.difficulty < 4) { spawn.bodyRect(-1600, -125, 125, 125); spawn.bodyRect(-1560, -200, 75, 75); } else { spawn.bodyRect(-1200, -125, 125, 125); spawn.bodyRect(-1160, -200, 75, 75); } //building 2 spawn.mapRect(-4450, -600, 2300, 750); spawn.mapRect(-2225, -450, 175, 550); // spawn.mapRect(-2600, -975, 450, 50); spawn.mapRect(-3425, -1325, 525, 75); spawn.mapRect(-3425, -2200, 525, 50); spawn.mapRect(-2600, -1700, 450, 50); // spawn.mapRect(-2600, -2450, 450, 50); spawn.bodyRect(-2275, -2700, 50, 60); // spawn.bodyRect(-2560, -1925, 250, 225); // spawn.mapRect(-2525, -2025, 125, 25); // spawn.mapRect(-2525, -1900, 125, 225); // spawn.mapRect(-2600, -1975, 250, 25); spawn.mapRect(-2515, -2000, 180, 50); spawn.bodyRect(-3410, -1425, 50, 50); spawn.bodyRect(-3390, -1525, 40, 60); // spawn.bodyRect(-3245, -1425, 100, 100); //building 3 spawn.mapRect(-4450, -1750, 800, 1050); // spawn.mapRect(-3850, -2000, 125, 400); spawn.mapRect(-4000, -2390, 200, 800); // spawn.mapRect(-4450, -2650, 475, 1000); spawn.mapRect(-4450, -2775, 475, 1125); spawn.bodyRect(-3715, -2050, 50, 50); // spawn.bodyRect(-3570, -1800, 50, 50); spawn.bodyRect(-2970, -2250, 50, 50); spawn.bodyRect(-3080, -2250, 40, 40); spawn.bodyRect(-3420, -650, 50, 50); //exit spawn.mapRect(-4450, -3075, 25, 300); spawn.mapRect(-4450, -3075, 450, 25); spawn.mapRect(-4025, -3075, 25, 100); spawn.mapRect(-4275, -2785, 100, 25); spawn.bodyRect(-3900, -2400, 50, 50); //mobs spawn.randomMob(-2500, -2700, 1); spawn.randomMob(-3200, -750, 1); spawn.randomMob(-1875, -775, 0.2); spawn.randomMob(-950, -1675, 0.2); spawn.randomMob(-1525, -1750, 0.2); spawn.randomMob(-1375, -1400, 0.2); spawn.randomMob(-1625, -1275, 0.2); spawn.randomMob(-1900, -1250, 0.2); spawn.randomMob(-2250, -1850, 0.2); spawn.randomMob(-2475, -2200, 0.2); spawn.randomMob(-3000, -1475, 0.2); spawn.randomMob(-3850, -2500, 0.2); spawn.randomMob(-3650, -2125, 0.2); spawn.randomMob(-4010, -3200, 0.2); spawn.randomMob(-3500, -1825, 0.2); spawn.randomMob(-975, -100, 0); spawn.randomMob(-1050, -725, 0.2); spawn.randomMob(-1525, -100, 0); spawn.randomMob(-525, -1700, -0.1); spawn.randomMob(-125, -1500, -0.1); spawn.randomMob(-325, -1900, -0.1); spawn.randomMob(-550, -100, -0.1); spawn.randomGroup(-3250, -2700, 0.2); spawn.randomGroup(-2450, -1100, 0); if (simulation.difficulty > 1) spawn.randomLevelBoss(-2400, -2650); powerUps.addResearchToLevel() //needs to run after mobs are spawned spawn.secondaryBossChance(-1825, -1975) if (simulation.isHorizontalFlipped) { //flip the map horizontally level.flipHorizontal(); //only flips map,body,mob,powerUp,cons,consBB, exit // boost1.boostBounds.min.x = -boost1.boostBounds.min.x - 100 // boost1.boostBounds.max.x = -boost1.boostBounds.max.x + 100 level.setPosToSpawn(300, -700); //-x elevator1.holdX = -elevator1.holdX // flip the elevator horizontally elevator1.removeConstraint(); elevator1.addConstraint(); elevator2.holdX = -elevator2.holdX // flip the elevator horizontally elevator2.removeConstraint(); elevator2.addConstraint(); level.custom = () => { ctx.fillStyle = "#cff" //exit ctx.fillRect(4425 - 425, -3050, 425, 275) level.exit.drawAndCheck(); level.enter.draw(); }; level.customTopLayer = () => { toggle1.query(); if (!toggle1.isOn) { if (elevator1.isOn) { elevator1.isOn = false elevator1.frictionAir = 0.2 elevator1.addConstraint(); } } else if (!elevator1.isOn) { elevator1.isOn = true elevator1.isUp = false elevator1.removeConstraint(); elevator1.frictionAir = 0.2 //elevator.isUp ? 0.01 : 0.2 } if (elevator1.isOn) { elevator1.move(); ctx.fillStyle = "#444" ctx.fillRect(700 - 1, -1140, 1, 975) } else { ctx.fillStyle = "#aaa" ctx.fillRect(700 - 1, -1140, 1, 975) } toggle2.query(); if (!toggle2.isOn) { if (elevator2.isOn) { elevator2.isOn = false elevator2.frictionAir = 0.2 elevator2.addConstraint(); } } else if (!elevator2.isOn) { elevator2.isOn = true elevator2.isUp = false elevator2.removeConstraint(); elevator2.frictionAir = 0.2 //elevator.isUp ? 0.01 : 0.2 } if (elevator2.isOn) { elevator2.move(); ctx.fillStyle = "#444" ctx.fillRect(3540 - 1, -1720, 1, 740) } else { ctx.fillStyle = "#aaa" ctx.fillRect(3540 - 1, -1720, 1, 740) } ctx.fillStyle = "rgba(64,64,64,0.97)" //hidden section ctx.fillRect(4450 - 800, -750, 800, 200) ctx.fillStyle = "rgba(0,0,0,0.12)" ctx.fillRect(2500 - 150, -1975, 150, 300); ctx.fillRect(1830 - 2030, -1150, 2030, 1150) ctx.fillRect(3410 - 495, -2150, 495, 1550) ctx.fillRect(2585 - 420, -1675, 420, 1125) ctx.fillRect(1650 - 750, -1575, 750, 450) }; } }, warehouse() { level.custom = () => { ctx.fillStyle = "#444" //light fixtures ctx.fillRect(-920, -505, 40, 10) ctx.fillRect(-920, 95, 40, 10) ctx.fillRect(180, 95, 40, 10) ctx.fillRect(-20, 695, 40, 10) ctx.fillRect(-2320, 945, 40, 10) ctx.fillStyle = "#cff" //exit ctx.fillRect(300, -250, 350, 250) level.exit.drawAndCheck(); level.enter.draw(); }; const lightingPath = new Path2D() //pre-draw the complex lighting path to save processing lightingPath.moveTo(-1800, -500) lightingPath.lineTo(-910, -500) //3rd floor light lightingPath.lineTo(-1300, 0) lightingPath.lineTo(-500, 0) lightingPath.lineTo(-890, -500) lightingPath.lineTo(-175, -500) lightingPath.lineTo(-175, -250) lightingPath.lineTo(175, -250) lightingPath.lineTo(175, 0) lightingPath.lineTo(-910, 100) //2nd floor light left lightingPath.lineTo(-1300, 600) lightingPath.lineTo(-500, 600) lightingPath.lineTo(-890, 100) lightingPath.lineTo(190, 100) //2nd floor light right lightingPath.lineTo(-200, 600) lightingPath.lineTo(600, 600) lightingPath.lineTo(210, 100) lightingPath.lineTo(1100, 100) lightingPath.lineTo(1100, 1400) lightingPath.lineTo(600, 1400) //1st floor light right lightingPath.lineTo(10, 700) lightingPath.lineTo(-10, 700) lightingPath.lineTo(-600, 1400) lightingPath.lineTo(-1950, 1400) //1st floor light left lightingPath.lineTo(-2290, 950) lightingPath.lineTo(-2310, 950) lightingPath.lineTo(-2650, 1400) lightingPath.lineTo(-3025, 1400) lightingPath.lineTo(-3025, 150) lightingPath.lineTo(-2590, 150) lightingPath.lineTo(-2600, -150) lightingPath.lineTo(-1800, -150) lightingPath.lineTo(-1800, -500) //top left end/start of path level.customTopLayer = () => { ctx.fillStyle = "rgba(0,0,0,0.15)"; //shadows and lights ctx.fill(lightingPath); }; level.setPosToSpawn(25, -55); //normal spawn level.exit.x = 425; level.exit.y = -30; level.defaultZoom = 1300 simulation.zoomTransition(level.defaultZoom) spawn.debris(-2250, 1330, 3000, 6); //16 debris per level spawn.debris(-3000, -800, 3280, 6); //16 debris per level spawn.debris(-1400, 410, 2300, 5); //16 debris per level powerUps.spawnStartingPowerUps(25, 500); document.body.style.backgroundColor = "#dcdcde" //"#f2f5f3"; spawn.mapRect(-1500, 0, 2750, 100); spawn.mapRect(175, -270, 125, 300); spawn.mapRect(-1900, -600, 1775, 100); spawn.mapRect(-1900, -550, 100, 1250); //house spawn.mapRect(-225, -550, 100, 400); spawn.mapRect(-225, -10, 400, 50); spawn.mapRect(-25, -20, 100, 50); //exit house spawn.mapRect(300, -10, 350, 50); spawn.mapRect(-150, -350, 800, 100); spawn.mapRect(600, -275, 50, 75); spawn.mapRect(425, -20, 100, 25); // spawn.mapRect(-1900, 600, 2700, 100); spawn.mapRect(1100, 0, 150, 1500); spawn.mapRect(-3150, 1400, 4400, 100); spawn.mapRect(-2375, 875, 1775, 75); spawn.mapRect(-1450, 865, 75, 435); spawn.mapRect(-1450, 662, 75, 100); spawn.bodyRect(-1418, 773, 11, 102, 1, spawn.propsFriction); //blocking path spawn.mapRect(-3150, 50, 125, 1450); spawn.mapRect(-2350, 600, 3150, 100); spawn.mapRect(-2125, 400, 250, 275); // spawn.mapRect(-1950, -400, 100, 25); spawn.mapRect(-3150, 50, 775, 100); spawn.mapRect(-2600, -250, 775, 100); let isElevators = false let elevator1, elevator2, elevator3 if (Math.random() < 0.5) { isElevators = true elevator1 = level.elevator(-1780, 500, 260, 40, 7, 0.0003) // x, y, width, height, maxHeight, force = 0.003, friction = { up: 0.01, down: 0.2 }) { elevator2 = level.elevator(820, 1300, 260, 40, 607, 0.0003) elevator3 = level.elevator(-2850, 1250, 160, 40, 600, 0.007) if (simulation.isHorizontalFlipped) { spawn.mapVertex(-2900, 225, "0 0 0 -500 -500 -500") } else { spawn.mapVertex(-2900, 225, "0 0 0 -500 500 -500") } spawn.mapRect(-3050, 1175, 175, 300); spawn.bodyRect(-2375, 1300, 100, 100); spawn.bodyRect(-2325, 1250, 50, 50); spawn.bodyRect(-2275, 1350, 125, 50); level.custom = () => { elevator1.move(); elevator1.drawTrack(); elevator2.move(); elevator2.drawTrack(); elevator3.move(); elevator3.drawTrack(); ctx.fillStyle = "#444" //light fixtures ctx.fillRect(-920, -505, 40, 10) ctx.fillRect(-920, 95, 40, 10) ctx.fillRect(180, 95, 40, 10) ctx.fillRect(-20, 695, 40, 10) ctx.fillRect(-2320, 945, 40, 10) ctx.fillStyle = "#cff" //exit ctx.fillRect(300, -250, 350, 250) level.exit.drawAndCheck(); level.enter.draw(); }; } else { spawn.mapRect(-2950, 1250, 175, 250); spawn.mapRect(-3050, 1100, 150, 400); spawn.bodyRect(-1450, -125, 125, 125, 1, spawn.propsSlide); //weight spawn.bodyRect(-1800, 0, 300, 100, 1, spawn.propsHoist); //hoist cons[cons.length] = Constraint.create({ pointA: { x: -1650, y: -500 }, bodyB: body[body.length - 1], stiffness: 0.0001815, length: 1 }); Composite.add(engine.world, cons[cons.length - 1]); spawn.bodyRect(600, 525, 125, 125, 1, spawn.propsSlide); //weight spawn.bodyRect(800, 600, 300, 100, 1, spawn.propsHoist); //hoist cons[cons.length] = Constraint.create({ pointA: { x: 950, y: 100 }, bodyB: body[body.length - 1], stiffness: 0.0001815, length: 1 }); Composite.add(engine.world, cons[cons.length - 1]); spawn.bodyRect(-2700, 1150, 100, 160, 1, spawn.propsSlide); //weight spawn.bodyRect(-2550, 1200, 150, 150, 1, spawn.propsSlide); //weight spawn.bodyRect(-2763, 1300, 350, 100, 1, spawn.propsHoist); //hoist cons[cons.length] = Constraint.create({ pointA: { x: -2575, y: 150 }, bodyB: body[body.length - 1], stiffness: 0.0004, length: 566 }); Composite.add(engine.world, cons[cons.length - 1]); } //blocks spawn.bodyRect(-212, -150, 30, 35, 1); spawn.bodyRect(-212, -115, 30, 35, 1); spawn.bodyRect(-212, -80, 30, 35, 1); spawn.bodyRect(-212, -45, 30, 35, 1); spawn.bodyRect(-750, 400, 150, 150, 0.5); spawn.bodyRect(-400, 1175, 100, 250, 1); //block to get to top path on bottom level spawn.bodyRect(-2525, -50, 145, 100, 0.5); spawn.bodyRect(-2325, -300, 150, 100, 0.5); spawn.bodyRect(-1275, -750, 200, 150, 0.5); //roof block spawn.bodyRect(-525, -700, 125, 100, 0.5); //roof block //mobs spawn.randomSmallMob(-1125, 550); spawn.randomSmallMob(-2950, -50); spawn.randomMob(-2025, 175, 0.3); spawn.randomMob(-2325, 450, 0.3); spawn.randomMob(-2925, 675, 0.2); spawn.randomMob(-2700, 300, 0.1); spawn.randomMob(-2500, 300, 0.1); spawn.randomMob(-2075, -425, 0.1); spawn.randomMob(-1550, -725, 0.1); spawn.randomMob(375, 1100, 0); spawn.randomMob(-1575, 1100, 0); spawn.randomSmallMob(825, 300); spawn.randomMob(-800, -1750, 0); spawn.randomMob(400, -750, -0.1); spawn.randomMob(650, 1300, -0.1); spawn.randomMob(-2450, 1050, -0.1); spawn.randomMob(500, 400, -0.1); spawn.randomMob(-75, -1700, -0.1); spawn.randomMob(900, -800, -0.2); spawn.randomGroup(-75, 1050, -0.1); spawn.randomGroup(-900, 1000, 0.2); spawn.randomGroup(-1300, -1100, -0.3); spawn.randomSmallMob(-2325, 800); spawn.randomSmallMob(-900, 825); if (simulation.difficulty > 1) { if (Math.random() < 0.80) { spawn.randomLevelBoss(-800, -1300) } else { spawn.dragonFlyBoss(-1000 + Math.random() * 2500, -1300); //boss snake with head } } powerUps.addResearchToLevel() //needs to run after mobs are spawned spawn.secondaryBossChance(300, -800) if (simulation.isHorizontalFlipped) { //flip the map horizontally level.flipHorizontal(); //only flips map,body,mob,powerUp,cons,consBB, exit // boost1.boostBounds.min.x = -boost1.boostBounds.min.x - 100 // boost1.boostBounds.max.x = -boost1.boostBounds.max.x + 100 level.setPosToSpawn(-25, -55); //-x if (isElevators) { elevator1.holdX = -elevator1.holdX // flip the elevator horizontally elevator2.holdX = -elevator2.holdX // flip the elevator horizontally elevator3.holdX = -elevator3.holdX // flip the elevator horizontally level.custom = () => { elevator1.move(); elevator1.drawTrack(); elevator2.move(); elevator2.drawTrack(); elevator3.move(); elevator3.drawTrack(); ctx.fillStyle = "#444" //light fixtures ctx.fillRect(920 - 40, -505, 40, 10) ctx.fillRect(920 - 40, 95, 40, 10) ctx.fillRect(-180 - 40, 95, 40, 10) ctx.fillRect(20 - 40, 695, 40, 10) ctx.fillRect(2320 - 40, 945, 40, 10) ctx.fillStyle = "#cff" //exit ctx.fillRect(-300 - 350, -250, 350, 250) level.exit.drawAndCheck(); level.enter.draw(); }; } else { level.custom = () => { ctx.fillStyle = "#444" //light fixtures ctx.fillRect(920 - 40, -505, 40, 10) ctx.fillRect(920 - 40, 95, 40, 10) ctx.fillRect(-180 - 40, 95, 40, 10) ctx.fillRect(20 - 40, 695, 40, 10) ctx.fillRect(2320 - 40, 945, 40, 10) ctx.fillStyle = "#cff" //exit ctx.fillRect(-300 - 350, -250, 350, 250) level.exit.drawAndCheck(); level.enter.draw(); }; } level.customTopLayer = () => { ctx.fillStyle = "rgba(0,0,0,0.15)"; //shadows and lights ctx.beginPath() ctx.moveTo(1800, -500) ctx.lineTo(910, -500) //3rd floor light ctx.lineTo(1300, 0) ctx.lineTo(500, 0) ctx.lineTo(890, -500) ctx.lineTo(175, -500) ctx.lineTo(175, -250) ctx.lineTo(-175, -250) ctx.lineTo(-175, 0) ctx.lineTo(910, 100) //2nd floor light left ctx.lineTo(1300, 600) ctx.lineTo(500, 600) ctx.lineTo(890, 100) ctx.lineTo(-190, 100) //2nd floor light right ctx.lineTo(200, 600) ctx.lineTo(-600, 600) ctx.lineTo(-210, 100) ctx.lineTo(-1100, 100) ctx.lineTo(-1100, 1400) ctx.lineTo(-600, 1400) //1st floor light right ctx.lineTo(-10, 700) ctx.lineTo(10, 700) ctx.lineTo(600, 1400) ctx.lineTo(1950, 1400) //1st floor light left ctx.lineTo(2290, 950) ctx.lineTo(2310, 950) ctx.lineTo(2650, 1400) ctx.lineTo(3025, 1400) ctx.lineTo(3025, 150) ctx.lineTo(2590, 150) ctx.lineTo(2600, -150) ctx.lineTo(1800, -150) ctx.lineTo(1800, -500) //top left end/start of path ctx.fill() }; } }, office() { let button, door let isReverse = false if (Math.random() < 0.75) { //normal direction start in top left button = level.button(525, 0) door = level.door(1362, -400, 25, 400, 355, 1.5) //door(x, y, width, height, distance, speed = 1) { level.setPosToSpawn(1375, -1550); //normal spawn level.exit.x = 3088; level.exit.y = -630; } else { //reverse direction, start in bottom right isReverse = true button = level.button(3800, 0) door = level.door(3012, -400, 25, 400, 355, 1.5) level.setPosToSpawn(3137, -650); //normal spawn level.exit.x = 1375; level.exit.y = -1530; } level.custom = () => { button.query(); button.draw(); if (button.isUp) { door.isClosing = true } else { door.isClosing = false } door.openClose(); ctx.fillStyle = "#ccc" ctx.fillRect(2495, -500, 10, 525) ctx.fillStyle = "#dff" if (isReverse) { ctx.fillRect(725, -1950, 825, 450) } else { ctx.fillRect(3050, -950, 625, 500) } level.exit.drawAndCheck(); level.enter.draw(); }; level.customTopLayer = () => { ctx.fillStyle = "rgba(0,0,0,0.1)" ctx.fillRect(3650, -1300, 1300, 1300) ctx.fillRect(3000, -1000, 650, 1000) ctx.fillRect(750, -1950, 800, 450) ctx.fillRect(750, -1450, 650, 1450) ctx.fillRect(-550, -1700, 1300, 1700) // ctx.fillRect(0, 0, 0, 0) door.draw(); }; level.defaultZoom = 1400 simulation.zoomTransition(level.defaultZoom) spawn.mapRect(level.exit.x, level.exit.y + 20, 100, 50); //ground bump wall spawn.mapRect(level.enter.x, level.enter.y + 20, 100, 20); document.body.style.backgroundColor = "#e0e5e0"; spawn.debris(-300, -200, 1000, 6); //ground debris //16 debris per level spawn.debris(3500, -200, 800, 5); //ground debris //16 debris per level spawn.debris(-300, -650, 1200, 5); //1st floor debris //16 debris per level powerUps.spawnStartingPowerUps(-525, -700); spawn.mapRect(-600, 0, 2000, 325); //ground spawn.mapRect(1400, 25, 1600, 300); //ground spawn.mapRect(3000, 0, 2000, 325); //ground spawn.mapRect(-600, -1700, 50, 2000 - 100); //left wall spawn.bodyRect(-295, -1540, 40, 40); //center block under wall spawn.bodyRect(-298, -1580, 40, 40); //center block under wall spawn.bodyRect(1500, -1540, 30, 30); //left of entrance spawn.mapRect(1550, -2000, 50, 550); //right wall // spawn.mapRect(1350, -2000 + 505, 50, 1295); spawn.mapRect(1350, -1500, 50, 1125); //right wall spawn.mapRect(-600, -2000 + 250, 2000 - 700, 50); //roof left spawn.mapRect(-600 + 1300, -2000, 50, 300); //right roof wall spawn.mapRect(-600 + 1300, -2000, 900, 50); //center wall map[map.length] = Bodies.polygon(725, -1700, 0, 15); //circle above door spawn.bodyRect(720, -1675, 15, 170, 1, spawn.propsDoor); // door body[body.length - 1].isNotHoldable = true; //makes door swing consBB[consBB.length] = Constraint.create({ bodyA: body[body.length - 1], pointA: { x: 0, y: -90 }, bodyB: map[map.length - 1], stiffness: 1 }); Composite.add(engine.world, consBB[consBB.length - 1]); spawn.mapRect(-600 + 300, -2000 * 0.75, 1900, 50); //3rd floor spawn.mapRect(-600 + 2000 * 0.7, -2000 * 0.74, 50, 375); //center wall spawn.bodyRect(-600 + 2000 * 0.7, -2000 * 0.5 - 106, 50, 106); //center block under wall spawn.mapRect(-600, -1000, 1100, 50); //2nd floor spawn.mapRect(600, -1000, 500, 50); //2nd floor spawn.spawnStairs(-600, -1000, 4, 250, 350); //stairs 2nd spawn.mapRect(375, -600, 350, 150); //center table spawn.mapRect(-300, -2000 * 0.25, 1690, 50); //1st floor spawn.spawnStairs(-610 + 2000 - 50, -500, 4, 250, 350, true); //stairs spawn.spawnStairs(-600, 0, 4, 250, 350); //stairs ground spawn.bodyRect(700, -200, 100, 100); //center block under wall spawn.bodyRect(700, -300, 100, 100); //center block under wall spawn.bodyRect(700, -400, 100, 100); //center block under wall spawn.mapRect(1390, 13, 30, 20); //step left spawn.mapRect(2980, 13, 30, 20); //step right spawn.bodyRect(4250, -700, 50, 100); spawn.mapRect(3000, -1000, 50, 625); //left wall spawn.mapRect(3000 + 2000 - 50, -1300, 50, 1100); //right wall spawn.mapRect(4150, -600, 350, 150); //table spawn.mapRect(3650, -1300, 50, 700); //exit wall spawn.mapRect(3650, -1300, 1350, 50); //exit wall spawn.bodyRect(3665, -600, 20, 100); //door spawn.mapRect(3025, -600, 250, 125); spawn.mapRect(3175, -550, 175, 75); // spawn.mapVertex(3160, -525, "625 0 300 0 300 -140 500 -140"); //entrance/exit ramp spawn.mapRect(3000, -2000 * 0.5, 700, 50); //exit roof spawn.mapRect(3010, -2000 * 0.25, 1690, 50); //1st floor spawn.spawnStairs(3000 + 2000 - 50, 0, 4, 250, 350, true); //stairs ground spawn.randomSmallMob(4575, -560, 1); spawn.randomSmallMob(1315, -880, 1); spawn.randomSmallMob(800, -600); spawn.randomMob(4100, -225, 0.8); spawn.randomMob(-250, -700, 0.8); spawn.randomMob(4500, -225, 0.15); spawn.randomMob(3250, -225, 0.15); spawn.randomMob(-100, -225, 0.1); spawn.randomMob(1150, -225, 0.15); spawn.randomMob(2000, -225, 0.15); spawn.randomMob(450, -225, 0.15); spawn.randomMob(100, -1200, 1); spawn.randomMob(950, -1150, -0.1); spawn.randomGroup(1800, -800, -0.2); spawn.randomGroup(4150, -1000, 0.6); if (simulation.difficulty > 1) { if (Math.random() < 0.5) { spawn.tetherBoss(2850, -80, { x: 2500, y: -500 }) } else { spawn.randomLevelBoss(2200, -450) } } powerUps.addResearchToLevel() //needs to run after mobs are spawned spawn.secondaryBossChance(1875, -675) if (simulation.isHorizontalFlipped) { //flip the map horizontally level.flipHorizontal(); //only flips map,body,mob,powerUp,cons,consBB, exit level.setPosToSpawn(50, -60); if (!isReverse) { //normal direction start in top left level.setPosToSpawn(-1375, -1550); //normal spawn //-x } else { //reverse direction, start in bottom right level.setPosToSpawn(-3137, -650); //normal spawn } button.min.x = -button.min.x - 126 // flip the button horizontally button.max.x = -button.max.x + 126 // flip the button horizontally level.custom = () => { button.query(); button.draw(); if (button.isUp) { door.isClosing = true } else { door.isClosing = false } door.openClose(); ctx.fillStyle = "#ccc" ctx.fillRect(-2495 - 10, -500, 10, 525) ctx.fillStyle = "#dff" if (isReverse) { ctx.fillRect(-725 - 825, -1950, 825, 450) } else { ctx.fillRect(-3050 - 625, -950, 625, 500) } level.exit.drawAndCheck(); level.enter.draw(); }; level.customTopLayer = () => { ctx.fillStyle = "rgba(0,0,0,0.1)" ctx.fillRect(-3650 - 1300, -1300, 1300, 1300) ctx.fillRect(-3000 - 650, -1000, 650, 1000) ctx.fillRect(-750 - 800, -1950, 800, 450) ctx.fillRect(-750 - 650, -1450, 650, 1450) ctx.fillRect(550 - 1300, -1700, 1300, 1700) // ctx.fillRect(0, 0, 0, 0) door.draw(); }; } }, stronghold() { // player made level by Francois 👑 from discord simulation.makeTextLog(`stronghold by Francois`); const boost1 = level.boost(1470, -250, 1080) const boost2 = level.boost(-370, 0, 800) const boost3 = level.boost(4865, 0, 1800) level.custom = () => { boost1.query(); boost2.query(); boost3.query(); ctx.fillStyle = "#edf9f9"; ctx.fillRect(-500, -1220, 550, -480); ctx.fillStyle = "rgba(0,0,0,0.1)"; ctx.fillRect(0, -700, 1050, 700); ctx.fillRect(-550, -1170, 550, 1170); ctx.fillRect(1150, -1700, 250, 1700); ctx.fillRect(1100, -1700, 50, 450); ctx.fillRect(1050, -1200, 100, 1200); ctx.fillRect(1400, -250, 200, -1500); ctx.fillRect(1600, -550, 600, -1150); ctx.fillRect(2530, -550, 430, -1450); ctx.fillRect(3270, -1700, 80, 600); ctx.fillRect(3350, -1350, 700, 230); ctx.fillRect(4050, -1700, 600, 1290); ctx.fillRect(3650, -110, 1000, 170); ctx.fillRect(4865, -55, 100, 55); level.exit.drawAndCheck(); level.enter.draw(); }; level.customTopLayer = () => { }; level.setPosToSpawn(1900, -40); //normal spawn level.exit.x = -350; level.exit.y = -1250; level.defaultZoom = 1400 simulation.zoomTransition(level.defaultZoom) spawn.mapRect(level.exit.x, level.exit.y + 25, 100, 20); //exit bump spawn.debris(3800, -1480, 300, 12); spawn.debris(3600, -1130, 200, 2); document.body.style.backgroundColor = "#dbdcde"; // simulation.draw.mapFill = "#444" // simulation.draw.bodyFill = "rgba(140,140,140,0.85)" // simulation.draw.bodyStroke = "#222" // __________________________________________________________________________________________________ // Spawn Box spawn.mapRect(1600, -500, 50, 500); //Left Wall spawn.mapRect(1600, -550, 1500, 50); //Roof spawn.mapRect(2300, -500, 50, 300); //Right Wall spawn.mapRect(-550, 0, 4300, 200); //ground spawn.mapRect(3700, 55, 1300, 145); //2nd ground spawn.mapRect(5000, 0, 50, 200); //Last small part of the ground spawn.mapRect(3100, -1070, 50, 570); // vertical 2nd roof spawn.mapRect(3100, -1120, 950, 50); // Horizontal 2nd Roof spawn.mapRect(4050, -1750, 600, 50); // Roof after lift spawn.mapRect(4600, -1700, 50, 100); // Petit retour de toit, après ascenseur //Spawn "Upstairs" spawn.mapRect(3650, -160, 400, 50); //Thin Walk spawn.mapRect(4050, -410, 600, 300); //Large staircase block spawn.mapRect(4600, -1120, 50, 710); //Left Wall Wall upstairs spawn.mapRect(4550, -1170, 100, 50); //Bloque ascenseur spawn.mapVertex(3700, 35, "0 0 450 0 300 -60 150 -60"); //first slope spawn.mapVertex(4850, 35, "0 0 370 0 370 -65 150 -65"); //second slope spawn.bodyRect(3950, -280, 170, 120); //Bloc Marche Pour Monter À Ascenseur // spawn.bodyRect(-2700, 1150, 100, 160, 1, spawn.propsSlide); //weight // spawn.bodyRect(-2550, 1150, 200, 100, 1, spawn.propsSlide); //weight spawn.bodyRect(4050, -500, 275, 100, 1, spawn.propsSlide); //weight spawn.bodyRect(4235, -500, 275, 100, 1, spawn.propsSlide); //weight // spawn.bodyRect(-2775, 1300, 400, 100, 1, spawn.propsHoist); //hoist spawn.bodyRect(4025, -450, 550, 100, 1, spawn.propsHoist); //hoist cons[cons.length] = Constraint.create({ pointA: { x: 4325, y: -1700, }, bodyB: body[body.length - 1], stiffness: 0.0002, //1217, length: 200 }); Composite.add(engine.world, cons[cons.length - 1]); spawn.bodyRect(2799, -870, 310, 290); //Gros bloc angle toit spawn.mapRect(4000, -1750, 50, 400); //Right Wall Cuve spawn.mapRect(3400, -1400, 600, 50); // Bottom Cuve spawn.mapRect(3350, -1750, 50, 400); // Left Wall Cuve spawn.bodyRect(3400, -1470, 110, 70); //Moyen bloc dans la cuve spawn.mapRect(3270, -1750, 80, 50); // Rebord gauche cuve spawn.mapRect(2530, -2000, 430, 50); //First Plateforme spawn.mapRect(1600, -1750, 600, 50); // Middle plateforme spawn.mapRect(1100, -1750, 300, 50); //Derniere plateforme // Toit petite boite en [ spawn.bodyRect(1830, -1980, 190, 230); // Fat bloc plateforme middle spawn.bodyRect(1380, -1770, 250, 20) // Pont last plateforme spawn.mapRect(1000, -1250, 400, 50); //Sol de la petite boite en [ spawn.mapRect(1100, -1550, 50, 190); //Mur gauche petite boite en [ spawn.bodyRect(1100, -1380, 48, 109); //Bloc-porte petite boite en [ spawn.mapRect(-100, -750, 1100, 50); //Sol last salle spawn.mapRect(1000, -1200, 50, 500) // Mur droit last salle spawn.mapRect(50, -1550, 1050, 50); // Toit last salle spawn.bodyRect(1, -900, 48, 150); //Bloc porte last salle spawn.mapRect(0, -1170, 50, 270); //Mur gauche en bas last salle spawn.bodyRect(920, -900, 120, 120); //Gros bloc last salle spawn.mapRect(0, -1700, 50, 320); // Mur droit salle exit / Mur gauche last salle spawn.mapRect(-550, -1220, 600, 50); // Sol exit room spawn.mapRect(-500, -1750, 550, 50); // Toit exit room spawn.mapRect(-550, -1750, 50, 530); // Mur gauche exit room spawn.bodyRect(-503, -1250, 30, 30); // Petit bloc exit room spawn.mapRect(500, -700, 100, 590); //Bloc noir un dessous last salle spawn.mapRect(1350, -250, 250, 250); //Black Block left from the spawn map[map.length] = Bodies.polygon(2325, -205, 0, 15); //circle above door spawn.bodyRect(2325, -180, 15, 170, 1, spawn.propsDoor); // door body[body.length - 1].isNotHoldable = true; //makes door swing consBB[consBB.length] = Constraint.create({ bodyA: body[body.length - 1], pointA: { x: 0, y: -90 }, bodyB: map[map.length - 1], stiffness: 1 }); Composite.add(engine.world, consBB[consBB.length - 1]); spawn.bodyRect(650, 50, 70, 50); spawn.bodyRect(300, 0, 100, 60); spawn.bodyRect(400, 0, 100, 150); spawn.bodyRect(2545, -50, 70, 50); spawn.bodyRect(2550, 0, 100, 30); spawn.randomSmallMob(200, -1300, 0.5); spawn.randomSmallMob(300, -1300, 0.9); spawn.randomSmallMob(470, -650, 1); spawn.randomSmallMob(1000, -400, 1); spawn.randomSmallMob(2550, -560, 1); spawn.randomSmallMob(3350, -900, 1); spawn.randomSmallMob(3600, -1210, 1); spawn.randomSmallMob(700, -1950, 0.2); spawn.randomSmallMob(5050, -550); spawn.randomMob(-250, -250, 0.8); spawn.randomMob(-300, -600, 0.6); spawn.randomMob(350, -900, 0.5); spawn.randomMob(770, -950, 0.8) spawn.randomMob(900, -160, 1); spawn.randomMob(2360, -820, 0.8); spawn.randomMob(2700, -2020, 0.8); spawn.randomMob(3050, -1650, 0.8); spawn.randomMob(3350, -600, 0.8); spawn.randomMob(4400, -50, 1); spawn.randomGroup(1500, -1900, 0.5); spawn.randomGroup(2350, -850, 1); spawn.randomGroup(100, -450, 0.9); if (simulation.difficulty > 1) spawn.randomLevelBoss(1850, -1400); spawn.secondaryBossChance(1850, -1400) powerUps.addResearchToLevel() //needs to run after mobs are spawned }, basement() { // player made level by Francois 👑 from discord simulation.makeTextLog(`basement by Francois`); let button, door, buttonDoor, buttonPlateformEnd, doorPlateform let isLevelReversed = Math.random(); if (isLevelReversed < 0.7) { isLevelReversed = false; } else { isLevelReversed = true; } const elevator = level.elevator(4545, -220, 110, 30, -3000) const hazard = level.hazard(1675, -1050, 800, 150); const portal = level.portal({ x: -620, y: -257 }, Math.PI / 2, { //down x: 500, y: 2025 }, -Math.PI / 2) //up spawn.mapRect(350, 2025, 300, 300); //Bloc portail n°2 if (isLevelReversed === false) { /// Normal Spawn button = level.button(2700, -1150); level.setPosToSpawn(2600, -2050); //normal spawn level.exit.x = level.enter.x + 4510; level.exit.y = level.enter.y + 600; spawn.mapRect(level.exit.x, level.exit.y + 20, 100, 20); spawn.mapRect(level.enter.x, level.enter.y + 20, 100, 20); } else { /// Reversed spawn button = level.button(1450, -1150); buttonPlateformEnd = level.button(3530, -1150); buttonDoor = level.button(8033, -3625); door = level.door(7700, -3905, 25, 184, 184); doorPlateform = level.door(3200, -1225, 299, 80, 525); level.setPosToSpawn(7110, -1450); //normal spawn level.exit.x = level.enter.x - 4510; level.exit.y = level.enter.y - 600; spawn.mapRect(level.exit.x, level.exit.y + 20, 100, 20); spawn.mapRect(level.enter.x, level.enter.y + 20, 100, 20); spawn.mapRect(7675, -3935, 75, 25); spawn.mapRect(7675, -3715, 75, 25); spawn.bodyRect(8075, -3675, 50, 25); } const boost1 = level.boost(8290, -2100, 1800) level.custom = () => { boost1.query(); level.exit.drawAndCheck(); portal[2].query() portal[3].query() button.query(); button.draw(); if (isLevelReversed === true) { ///Reversed spawn buttonDoor.draw(); buttonDoor.query(); buttonPlateformEnd.draw(); buttonPlateformEnd.query(); // hazard.query(); //bug reported from discord? if (buttonDoor.isUp) { door.isClosing = false } else { door.isClosing = true } door.openClose(); if (buttonPlateformEnd.isUp) { doorPlateform.isClosing = true; } else { doorPlateform.isClosing = false; } door.openClose(); doorPlateform.openClose(); } hazard.level(button.isUp) level.enter.draw(); elevator.move(); elevator.drawTrack(); }; level.customTopLayer = () => { ctx.fillStyle = "rgba(61,62,62,0.95)"; ctx.fillRect(-750, -900, 750, 450); if (isLevelReversed === true) { door.draw(); doorPlateform.draw(); } portal[0].draw(); portal[1].draw(); portal[2].draw(); portal[3].draw(); hazard.query(); }; level.defaultZoom = 1300 simulation.zoomTransition(level.defaultZoom) document.body.style.backgroundColor = "#c7c7c7"; // GROUND // spawn.mapRect(-400, -2000, 400, 1430); //Gros left wall spawn.mapRect(3700, -3000, 700, 2650); //Gros right wall //Puit spawn.mapRect(-400, -2000, 3700, 250); //Ground spawn.mapRect(2475, -1150, 1225, 250); spawn.mapRect(500, -1150, 1175, 250); //Ground level 3 spawn.mapRect(350, -180, 4600, 1255); // Last ground spawn.mapRect(-400, -458, 750, 3337); //mur left sous-sol spawn.mapRect(-2850, -3375, 5300, 1375); spawn.mapRect(-2850, -4200, 8000, 825); spawn.mapRect(3700, -3375, 550, 375); spawn.mapRect(-2850, -5200, 10200, 1000); spawn.mapRect(5600, -1250, 3550, 2000); spawn.mapRect(9150, -5200, 1725, 5800); // SPAWN BOX // spawn.mapRect(2300, -3375, 950, 1000); spawn.mapRect(3550, -3375, 150, 1625); spawn.mapVertex(2020, -791, " 250 250 -860 250 -2200 0 250 0"); //map vertex en haut spawn.mapVertex(690, -295, "1700 0 -200 0 -200 -284 500 -284"); //map vertex en bas spawn.mapRect(2950, -900, 750, 250); //Extension ground apres map vertex if (isLevelReversed === false) { spawn.mapRect(3250, -1800, 50, 150); //Petit picot en haut, à gauche spawn.mapRect(3400, -1800, 50, 150); //Petit picot en haut, à droite spawn.mapRect(3150, -1300, 50, 200) //Petit picot en bas, à gauche spawn.mapRect(3500, -1300, 50, 200) //Petit picot en bas, à droite spawn.mapRect(3050, -3375, 500, 1260); spawn.mapRect(3400, -2265, 150, 515); //Mur fond tunnel spawn.bodyRect(3625, -1225, 75, 75); //Pitit bloc à droite en bas spawn } else { spawn.mapRect(3050, -3375, 500, 1000); spawn.mapRect(3400, -2400, 150, 650); //Mur fond tunnel spawn.bodyRect(3425, -1515, 75, 75); //Petit en bas spawn spawn.mapRect(3200, -1275, 300, 175); } // TRAMPOLING // if (isLevelReversed === false) { /// Normal spawn spawn.bodyRect(0, -1000, 500, 120, 1, spawn.propsHoist); //hoist cons[cons.length] = Constraint.create({ pointA: { x: 250, y: -1750, }, bodyB: body[body.length - 1], stiffness: 0.00014, length: 120 }); Composite.add(engine.world, cons[cons.length - 1]); spawn.bodyRect(0, -1250, 240, 190) //Fat cube ascenseur } else { /// Reversed spawn spawn.bodyRect(0, -650, 225, 175); spawn.mapRect(425, -950, 175, 50); spawn.mapRect(-25, -1150, 100, 50); } // PUIT // spawn.mapVertex(4200, -1810, "0 0 450 0 600 -2500 0 -2500") spawn.mapVertex(5000, -1809, "0 0 450 0 450 -2500 -150 -2500") spawn.mapRect(4800, -3000, 800, 5875); //big right Puit // BOSS AREA // spawn.mapRect(4800, -3150, 50, 200); //Premiere barriere spawn.mapRect(5100, -3530, 50, 380); //2nd barriere spawn.mapRect(5100, -3200, 150, 50); //Marche en dessous mapVertex 1 spawn.mapVertex(5450, -3650, "220 0 200 30 -200 30 -220 0 -200 -30 200 -30"); spawn.mapVertex(6225, -3350, "275 0 250 50 -250 50 -275 0 -250 -50 250 -50"); spawn.mapRect(5600, -3000, 1600, 725); //ground Boss Area //Ouverture right boss area spawn.mapRect(7300, -3325, 50, 50); //petite marche pour accéder à l'ouverture spawn.mapRect(7350, -4075, 850, 50); //Bouche spawn.mapRect(7400, -4050, 800, 50); //Bouche spawn.mapRect(7450, -4025, 750, 50); //Bouche spawn.mapRect(7500, -4000, 700, 50); //Bouche spawn.mapRect(7550, -3975, 650, 50); //Bouche spawn.mapRect(7350, -3600, 850, 50); //Bouche spawn.mapRect(7400, -3625, 800, 50); //Bouche spawn.mapRect(7450, -3650, 575, 50); //Bouche spawn.mapRect(7500, -3675, 525, 50); //Bouche spawn.mapRect(7550, -3700, 475, 50); //Bouche //Murs spawn.mapRect(7350, -5200, 1800, 1125); spawn.mapRect(8475, -4075, 675, 2825); spawn.mapRect(7300, -2100, 1175, 850); spawn.mapRect(7350, -3550, 850, 1275); //Escaliers spawn.mapRect(6600, -2100, 200, 75); //escaliers spawn.mapRect(6750, -2100, 750, 250); //escaliers spawn.mapRect(6950, -1850, 550, 200); //escaliers spawn.mapRect(6750, -1400, 750, 150); //escaliers spawn.mapRect(6550, -1625, 250, 375); //escaliers spawn.mapRect(6350, -1800, 250, 550); //escaliers spawn.mapRect(5600, -2275, 800, 1025); //escaliers // BLOCS if (isLevelReversed === false) { /// Normal spawn spawn.bodyRect(1350, -1175, 225, 25); spawn.bodyRect(1450, -1200, 25, 25); } else { /// Reversed spawn spawn.bodyRect(700, -1175, 225, 25); spawn.bodyRect(800, -1200, 25, 25); } spawn.bodyRect(1100, -1375, 225, 225); spawn.bodyRect(1775, -925, 75, 25); spawn.bodyRect(2225, -950, 75, 50); spawn.bodyRect(2000, -1000, 50, 100); spawn.bodyRect(3100, -1175, 50, 25); spawn.bodyRect(2200, -375, 50, 50); spawn.bodyRect(2200, -425, 50, 50); spawn.bodyRect(2200, -475, 50, 50); spawn.bodyRect(2200, -525, 50, 50); spawn.bodyRect(1050, -400, 50, 25); spawn.mapRect(2200, -650, 50, 125); spawn.mapRect(2200, -325, 50, 150); spawn.mapRect(2875, -225, 250, 50); spawn.mapRect(2050, -1225, 75, 100); //Plateforme over acid // MOBS if (isLevelReversed === false) { ///Normal spawn if (simulation.difficulty > 1) { if (Math.random() < 0.2) { spawn.tetherBoss(7000, -3300, { x: 7300, y: -3300 }) // tether ball } else { spawn.randomLevelBoss(6100, -3600, ["shooterBoss", "launcherBoss", "laserTargetingBoss", "spiderBoss", "laserBoss", "pulsarBoss"]); } } } else { /// Reversed spawn if (simulation.difficulty > 1) { if (Math.random() < 0.2) { spawn.tetherBoss(2300, -1300, { x: 2300, y: -1750 }) // tether ball } else { spawn.randomLevelBoss(2300, -1400, ["shooterBoss", "launcherBoss", "laserTargetingBoss", "spiderBoss", "laserBoss", "dragonFlyBoss", "pulsarBoss"]); } } } spawn.randomSmallMob(100, -1000, 1); spawn.randomSmallMob(1340, -675, 1); spawn.randomSmallMob(7000, -3750, 1); spawn.randomSmallMob(6050, -3200, 1); spawn.randomMob(1970 + 10 * Math.random(), -1150 + 20 * Math.random(), 1); spawn.randomMob(3500, -525, 0.8); spawn.randomMob(6700, -3700, 0.8); spawn.randomMob(2600, -1300, 0.7); spawn.randomMob(600, -1250, 0.7); spawn.randomMob(2450, -250, 0.6); spawn.randomMob(6200, -3200, 0.6); spawn.randomMob(900, -700, 0.5); spawn.randomMob(1960, -400, 0.5); spawn.randomMob(5430, -3520, 0.5); spawn.randomMob(400, -700, 0.5); spawn.randomMob(6500, -4000, 0.4); spawn.randomMob(3333, -400, 0.4); spawn.randomMob(3050, -1220, 0.4); spawn.randomMob(800, 1200, 0.3); spawn.randomMob(7200, -4000, 0.3); spawn.randomMob(250, -1550, 0.3); spawn.randomGroup(900, -1450, 0.3); spawn.randomGroup(2980, -400, 0.3); spawn.randomGroup(5750, -3860, 0.4); spawn.randomGroup(1130, 1300, 0.1); powerUps.addResearchToLevel() //needs to run after mobs are spawned powerUps.spawn(1900, -940, "heal"); powerUps.spawn(3000, -230, "heal"); powerUps.spawn(5450, -3675, "ammo"); // SECRET BOSS AREA // //hidden house spawn.mapRect(-850, -2000, 600, 1150); //Toit hidden house spawn.mapRect(-2850, -2000, 2150, 4880); //Mur gauche hidden house spawn.mapRect(-850, -458, 500, 3340); //Bloc sol hidden house // spawn.mapRect(-400, 2025, 3450, 850); //Sol secret boss area spawn.mapRect(625, 1300, 225, 50); //Plateforme horizontale n°1 spawn.mapRect(850, 1775, 470, 50); //Plateforme horizontale n°2 spawn.mapRect(1000, 1625, 100, 150); //Plateforme vertiale n°1 spawn.mapRect(1400, 1275, 100, 100); //Plateforme carrée spawn.mapRect(1700, 1675, 75, 450); //Plateforme verticale n°2 spawn.mapRect(2100, 1375, 450, 50); //Plateforme accroche boss spawn.mapRect(2900, 900, 175, 325); //Débord de toit droite haut spawn.mapRect(2900, 1675, 150, 350); //Muret en bas à droite spawn.mapRect(2900, 1225, 75, 100); //Picot haut entrée salle trésor spawn.mapRect(2900, 1575, 75, 100); //Picot bas entrée salle trésor spawn.mapRect(2800, 1575, 100, 25); //Plongeoir sortie salle trésor spawn.mapRect(3050, 1675, 400, 1200); //Sol sallle trésor spawn.mapRect(3075, 1075, 375, 150); //Plafond salle trésor spawn.mapRect(3300, 1075, 1500, 1800); //Mur droite salle trésor // tether ball spawn.tetherBoss(2330, 1850, { x: 2330, y: 1425 }) spawn.secondaryBossChance(2330, 1850) powerUps.chooseRandomPowerUp(3100, 1630); }, // detours() { //by Francois from discord // simulation.makeTextLog(`detours by Francois`); // level.setPosToSpawn(0, 0); //lower start // level.exit.y = 150; // spawn.mapRect(level.enter.x, 45, 100, 20); // level.exit.x = 10625; // spawn.mapRect(level.exit.x, level.exit.y + 20, 100, 20); // level.defaultZoom = 1400; // simulation.zoomTransition(level.defaultZoom) // document.body.style.backgroundColor = "#d5d5d5"; // const BGColor = "rgba(0,0,0,0.1)"; // // level.fill.push({ // // x: -150, // // y: -250, // // width: 625, // // height: 325, // // color: BGColor // // }); // // level.fill.push({ // // x: 475, // // y: -520, // // width: 5375, // // height: 875, // // color: BGColor // // }); // // level.fill.push({ // // x: 5850, // // y: -1275, // // width: 2800, // // height: 2475, // // color: BGColor // // }); // // level.fill.push({ // // x: 8650, // // y: -500, // // width: 1600, // // height: 750, // // color: BGColor // // }); // // level.fill.push({ // // x: 10250, // // y: -700, // // width: 900, // // height: 950, // // color: BGColor // // }); // const balance = level.spinner(5500, -412.5, 25, 660) //entrance // const rotor = level.rotor(7000, 580, -0.001); // const doorSortieSalle = level.door(8590, -520, 20, 800, 750) // // let buttonSortieSalle // // let portalEnBas // let portalEnHaut // // let door3isClosing = false; // function drawOnTheMapMapRect(x, y, dx, dy) { // spawn.mapRect(x, y, dx, dy); // len = map.length - 1 // map[len].collisionFilter.category = cat.map; // map[len].collisionFilter.mask = cat.player | cat.map | cat.body | cat.bullet | cat.powerUp | cat.mob | cat.mobBullet; // Matter.Body.setStatic(map[len], true); //make static // Composite.add(engine.world, map[len]); //add to world // simulation.draw.setPaths() //update map graphics // } // function drawOnTheMapBodyRect(x, y, dx, dy) { // spawn.bodyRect(x, y, dx, dy); // len = body.length - 1 // body[len].collisionFilter.category = cat.body; // body[len].collisionFilter.mask = cat.player | cat.map | cat.body | cat.bullet | cat.mob | cat.mobBullet // Composite.add(engine.world, body[len]); //add to world // body[len].classType = "body" // } // function spawnCouloirEnHaut() { // // level.fill.push({ // // x: 2575, // // y: -1150, // // width: 2550, // // height: 630, // // color: BGColor // // }); // // level.fill.push({ // // x: 1900, // // y: -2300, // // width: 1650, // // height: 1150, // // color: BGColor // // }); // // level.fill.push({ // // x: 3550, // // y: -1625, // // width: 1650, // // height: 475, // // color: BGColor // // }); // // level.fill.push({ // // x: 1800, // // y: -1120, // // width: 775, // // height: 600, // // color: BGColor // // }); // drawOnTheMapMapRect(3800, -270, 75, 75); // drawOnTheMapMapRect(3900, -895, 500, 75); // drawOnTheMapMapRect(3900, -1195, 75, 375); // drawOnTheMapMapRect(3525, -1195, 450, 75); // drawOnTheMapMapRect(3525, -1995, 50, 1575); // drawOnTheMapMapRect(3325, -1995, 50, 1575); // drawOnTheMapMapRect(3525, -1670, 1675, 75); // drawOnTheMapMapRect(5100, -1670, 100, 1250); // drawOnTheMapMapRect(1800, -1195, 1575, 75); // drawOnTheMapMapRect(1800, -1520, 375, 400); // drawOnTheMapMapRect(1800, -2370, 100, 1250); // drawOnTheMapMapRect(2375, -1845, 375, 250); // drawOnTheMapMapRect(2700, -1745, 650, 75); // drawOnTheMapMapRect(1800, -2370, 1775, 100); // drawOnTheMapMapRect(3525, -2370, 50, 775); // drawOnTheMapMapRect(4650, -1220, 550, 75); // drawOnTheMapBodyRect(3225, -1845, 100, 100); // drawOnTheMapBodyRect(3575, 1255, 125, 25); // drawOnTheMapBodyRect(2450, 2255, 25, 25); // drawOnTheMapBodyRect(3975, -945, 175, 50); // drawOnTheMapBodyRect(4825, -1295, 50, 75); // drawOnTheMapBodyRect(4850, -720, 250, 200); // drawOnTheMapBodyRect(4050, -970, 25, 25); // drawOnTheMapBodyRect(3075, -1245, 50, 50); // portalEnHaut = level.portal({ // x: 3650, // y: -1470 // }, Math.PI / 2, { // x: 3250, // y: -1473 // }, Math.PI / 2) // spawn.randomSmallMob(2500, -2070 + Math.random(), 1); // spawn.randomSmallMob(5000, -1370, 1); // spawn.randomMob(5000, -645, 0.9); // spawn.randomMob(4050, -970, 0.9); // spawn.randomSmallMob(2800, -1620, 0.7); // spawn.randomMob(2400, -1370, 0.5); // spawn.randomMob(3725, -1320, 0.3); // spawn.randomGroup(2115, -2020, 0.1) // powerUps.spawn(5000, -1275, "heal"); // levelCustom2(); // } // ////////////////////////////////////////// // level.custom = () => { // level.exit.drawAndCheck(); // rotor.rotate(); // // rotor2.rotate() // level.enter.draw(); // }; // level.customTopLayer = () => { // doorSortieSalle.draw(); // ctx.fillStyle = "#233" // ctx.beginPath(); // ctx.arc(balance.pointA.x, balance.pointA.y, 9, 0, 2 * Math.PI); // ctx.fill(); // }; // //////////////////////////////////////// // function levelCustom2() { // level.custom = () => { // portalEnHaut[2].query(); // portalEnHaut[3].query(); // rotor.rotate(); // doorSortieSalle.openClose(); // level.exit.drawAndCheck(); // level.enter.draw(); // }; // // ////////////////////////////////////// // level.customTopLayer = () => { // doorSortieSalle.draw(); // portalEnHaut[0].draw(); // portalEnHaut[1].draw(); // portalEnHaut[2].draw(); // portalEnHaut[3].draw(); // ctx.fillStyle = "#233" // ctx.beginPath(); // ctx.arc(balance.pointA.x, balance.pointA.y, 9, 0, 2 * Math.PI); // ctx.fill(); // }; // } // //spawn box // spawn.mapRect(-200, -295, 75, 425); // spawn.mapRect(-200, 55, 700, 75); // spawn.mapRect(-200, -295, 700, 75); // spawn.bodyRect(470, -220, 25, 275); //porte spawn box // //couloir // spawn.mapRect(450, -520, 50, 300); //muret gauche haut // spawn.mapRect(450, 55, 50, 300); //muret gauche bas // spawn.mapRect(1700, -520, 50, 325); //muret 2 haut // spawn.mapRect(1700, 55, 50, 300); //muret 2 bas // spawn.mapRect(4375, 55, 50, 300); // spawn.mapRect(4575, 55, 50, 300); // spawn.bodyRect(4625, 155, 75, 100); // spawn.bodyRect(4725, 230, 50, 25); // if (Math.random() > 0.5) { // powerUps.chooseRandomPowerUp(4500, 200); // } else { // powerUps.chooseRandomPowerUp(8350, -630); // } // //blocs // spawn.bodyRect(7475, 1055, 50, 75); // spawn.bodyRect(7775, 1105, 25, 25); // spawn.bodyRect(6925, 1105, 125, 25); // spawn.bodyRect(6375, 380, 50, 50); // spawn.bodyRect(6425, -220, 125, 150); // spawn.bodyRect(6475, -245, 125, 25); // spawn.bodyRect(7675, -245, 100, 50); // spawn.bodyRect(7075, -520, 50, 100); // spawn.bodyRect(8400, -595, 100, 75); // spawn.bodyRect(1700, 5, 50, 50); // spawn.bodyRect(1700, -45, 50, 50); // spawn.bodyRect(1700, -95, 50, 50); // spawn.bodyRect(1700, -145, 50, 50); // spawn.bodyRect(1700, -195, 50, 50); // spawn.mapRect(450, -520, 1600, 100); //plafond 1 // spawn.mapRect(450, 255, 1600, 100); //sol 1 // spawn.mapRect(2250, -45, 1450, 75); //entresol // spawn.mapRect(3900, -520, 2000, 100); //plafond 2 // spawn.mapRect(3900, 255, 2000, 100); //sol 2 // //grande salle // spawn.bodyRect(5900, 830, 325, 300); //bloc en bas à gauche // spawn.mapRect(5775, -1295, 2900, 100); // spawn.mapRect(5775, 1130, 2900, 100); //plancher + sol grande salle // spawn.mapRect(5925, -70, 650, 50); //plateforme middle entrée // spawn.mapRect(7575, -520, 1100, 100); //sol salle en haut à droite // spawn.mapRect(6800, -420, 450, 50); //petite plateforme transition vers salle en haut // spawn.mapRect(7750, -1295, 75, 575); //mur gauche salle en haut à droite // spawn.mapRect(6100, 430, 375, 50); //plateforme en bas, gauche rotor // spawn.mapRect(7450, -195, 1225, 75); //longue plateforme // //murs grande salle // spawn.mapRect(5775, -1295, 125, 875); // spawn.mapRect(5775, 255, 125, 975); // spawn.mapRect(8550, -1295, 125, 875); // spawn.mapRect(8550, 180, 125, 1050); // //couloir 2 // spawn.mapRect(8875, -520, 1425, 325); // spawn.mapRect(8550, -520, 1750, 100); // spawn.mapRect(8550, 180, 2625, 100); // spawn.mapRect(10175, -745, 125, 325); // spawn.mapRect(10175, -745, 1000, 125); // spawn.mapRect(11050, -745, 125, 1025); // spawn.mapRect(8875, 80, 1425, 200); // //MOBS // spawn.randomSmallMob(900, -70, 1); // spawn.randomMob(4300, 95, 1); // spawn.randomSmallMob(6250, 630, 1); // spawn.randomMob(6255, -835, 0.9); // spawn.randomMob(8200, -900, 0.7); // spawn.randomMob(5700, -270, 0.7); // spawn.randomMob(8275, -320, 0.7); // spawn.randomMob(2700, -270, 0.7); // spawn.randomMob(7575, 950, 0.5); // spawn.randomMob(7000, -695, 0.4); // spawn.randomMob(1850, -345, 0.3); // spawn.randomMob(3600, -270, 0.3); // spawn.randomMob(1500, -270, 0.2); // spawn.randomMob(1250, 55, 0.2); // spawn.randomMob(8800, -45, 0.2); // spawn.randomGroup(8025, -845, 0.2); // if (simulation.difficulty > 2) { // // if (Math.random() < 0.2) { // // // tether ball // // spawn.tetherBoss(8000, 630, { x: 8550, y: 680 }) // // let me = mob[mob.length - 1]; // // me.onDeath = function() { //please don't edit the onDeath function this causes serious bugs // // this.removeCons(); //remove constraint // // spawnCouloirEnHaut() // // doorSortieSalle.isClosing = false; // // }; // // if (simulation.difficulty > 4) spawn.nodeGroup(8000, 630, "spawns", 8, 20, 105); // // } else { // spawn.randomLevelBoss(8000, 630, ["shooterBoss", "launcherBoss", "laserTargetingBoss", "spiderBoss", "laserBoss", "bomberBoss", "orbitalBoss", "pulsarBoss"]); // spawn.secondaryBossChance(8000, 630) // //find level boss index // let me // for (let i = 0, len = mob.length; i < len; i++) { // if (mob[i].isBoss) me = mob[i] // } // if (me) { // me.onDeath = function() { //please don't edit the onDeath function this causes serious bugs // spawnCouloirEnHaut() // doorSortieSalle.isClosing = false; // }; // } else { // spawnCouloirEnHaut() // doorSortieSalle.isClosing = false; // } // // } // } else { // spawn.randomLevelBoss(8000, 630, ["shooterBoss"]); // let me // for (let i = 0, len = mob.length; i < len; i++) { // if (mob[i].isBoss) me = mob[i] // } // if (me) { // me.onDeath = function() { //please don't edit the onDeath function this causes serious bugs // spawnCouloirEnHaut() // doorSortieSalle.isClosing = false; // }; // } else { // spawnCouloirEnHaut() // doorSortieSalle.isClosing = false; // } // } // }, house() { //by Francois from discord simulation.makeTextLog(`house by Francois`); const rotor = level.rotor(4251, -325, 120, 20, 200, 0, 0.01, 0, -0.0001); const hazard = level.hazard(4350, -1000, 300, 110); const doorBedroom = level.door(1152, -1150, 25, 250, 250); const doorGrenier = level.door(1152, -1625, 25, 150, 160); const buttonBedroom = level.button(1250, -850); const voletLucarne1 = level.door(1401, -2150, 20, 26, 28); const voletLucarne2 = level.door(1401, -2125, 20, 26, 53); const voletLucarne3 = level.door(1401, -2100, 20, 26, 78); const voletLucarne4 = level.door(1401, -2075, 20, 26, 103); const voletLucarne5 = level.door(1401, -2050, 20, 26, 128); const voletLucarne6 = level.door(1401, -2025, 20, 26, 153); let hasAlreadyBeenActivated = false; let grd level.setPosToSpawn(0, -50); //normal spawn spawn.mapRect(level.enter.x, level.enter.y + 20, 100, 20); level.exit.x = 3100; level.exit.y = -2480; spawn.mapRect(level.exit.x, level.exit.y + 20, 100, 20); level.defaultZoom = 1800 simulation.zoomTransition(level.defaultZoom) document.body.style.backgroundColor = "rgb(170 170 170)" level.custom = () => { ctx.fillStyle = "rgb(221, 221, 221)"; ctx.fillRect(1175, -1425, 4000, 1200); ctx.fillStyle = "rgb(170 170 170)"; ctx.fillRect(1650, -1300, 175, 150); ctx.fillStyle = "rgb(77, 76, 76)"; ctx.fillRect(624, -1150, 28, 1075); ctx.fillStyle = "#ababab"; ctx.fillRect(3420, -380, 285, 40); ctx.fillStyle = "#474747"; ctx.fillRect(3555, -367.5, 15, 15); ctx.fillRect(3418, -344, 288, 8); ctx.fillRect(3555, -327.5, 15, 15); ctx.fillRect(3418, -304, 288, 8); ctx.fillRect(3555, -285, 15, 15); ctx.fillStyle = "#ababab"; ctx.fillRect(3420, -340, 285, 40); ctx.fillRect(3420, -300, 285, 45); ctx.fillStyle = "rgba(141, 141, 141,1)"; ctx.fillRect(3800, -1275, 250, 425); ctx.fillStyle = "#000"; ctx.fillRect(3800, -1275, 250, 3); ctx.fillRect(4048, -1275, 3, 425); ctx.fillRect(3800, -1275, 3, 425); ctx.fillRect(3830, -1050, 35, 10); ctx.fillStyle = "rgba(225, 242, 245,0.6)"; ctx.fillRect(4050, -1425, 1125, 600); ctx.fillStyle = "#444"; ctx.fillRect(1736, -1300, 3, 150); ctx.fillRect(1650, -1224, 175, 3); ctx.fillStyle = "#5806ac"; ctx.fillRect(3375, -625, 375, 175); ctx.fillStyle = "rgba(166, 166, 166,0.8)"; ctx.fillRect(4050, -1425, 1, 600); ctx.fillRect(4090, -1425, 1, 600); ctx.fillRect(4130, -1425, 1, 600); ctx.fillRect(4170, -1425, 1, 600); ctx.fillRect(4210, -1425, 1, 600); ctx.fillRect(4250, -1425, 1, 600); ctx.fillRect(4290, -1425, 1, 600); ctx.fillRect(4330, -1425, 1, 600); ctx.fillRect(4370, -1425, 1, 600); ctx.fillRect(4410, -1425, 1, 600); ctx.fillRect(4450, -1425, 1, 600); ctx.fillRect(4490, -1425, 1, 600); ctx.fillRect(4530, -1425, 1, 600); ctx.fillRect(4570, -1425, 1, 600); ctx.fillRect(4610, -1425, 1, 600); ctx.fillRect(4650, -1425, 1, 600); ctx.fillRect(4690, -1425, 1, 600); ctx.fillRect(4730, -1425, 1, 600); ctx.fillRect(4770, -1425, 1, 600); ctx.fillRect(4810, -1425, 1, 600); ctx.fillRect(4850, -1425, 1, 600); ctx.fillRect(4890, -1425, 1, 600); ctx.fillRect(4930, -1425, 1, 600); ctx.fillRect(4970, -1425, 1, 600); ctx.fillRect(5010, -1425, 1, 600); ctx.fillRect(5050, -1425, 1, 600); ctx.fillRect(5090, -1425, 1, 600); ctx.fillRect(5130, -1425, 1, 600); ctx.fillRect(4050, -1425, 1125, 2); ctx.fillRect(4050, -1385, 1125, 2); ctx.fillRect(4050, -1345, 1125, 2); ctx.fillRect(4050, -1305, 1125, 2); ctx.fillRect(4050, -1265, 1125, 2); ctx.fillRect(4050, -1225, 1125, 2); ctx.fillRect(4050, -1185, 1125, 2); ctx.fillRect(4050, -1145, 1125, 2); ctx.fillRect(4050, -1105, 1125, 2); ctx.fillRect(4050, -1065, 1125, 2); ctx.fillRect(4050, -1025, 1125, 2); ctx.fillRect(4050, -985, 1125, 2); ctx.fillRect(4050, -945, 1125, 2); ctx.fillRect(4050, -905, 1125, 2); ctx.fillRect(4050, -865, 1125, 2); buttonBedroom.query(); buttonBedroom.draw(); if (buttonBedroom.isUp) { if (hasAlreadyBeenActivated == false) { doorBedroom.isClosing = true; doorGrenier.isClosing = true; voletLucarne1.isClosing = true; voletLucarne2.isClosing = true; voletLucarne3.isClosing = true; voletLucarne4.isClosing = true; voletLucarne5.isClosing = true; voletLucarne6.isClosing = true; } } else { doorBedroom.isClosing = false; doorGrenier.isClosing = false; voletLucarne1.isClosing = false; voletLucarne2.isClosing = false; voletLucarne3.isClosing = false; voletLucarne4.isClosing = false; voletLucarne5.isClosing = false; voletLucarne6.isClosing = false; if (hasAlreadyBeenActivated == false) { hasAlreadyBeenActivated = true; } } doorBedroom.openClose(); doorGrenier.openClose(); voletLucarne1.openClose(); voletLucarne2.openClose(); voletLucarne3.openClose(); voletLucarne4.openClose(); voletLucarne5.openClose(); voletLucarne6.openClose(); rotor.rotate(); /// grd = ctx.createRadialGradient(512.5, -1025, 5, 512.5, -1025, 100); grd.addColorStop(0, "rgb(255, 199, 43)"); grd.addColorStop(1, "rgb(170 170 170)"); ctx.fillStyle = grd; ctx.fillRect(450, -1025, 125, 100); /// grd = ctx.createRadialGradient(762.5, -1025, 5, 762.5, -1025, 100); grd.addColorStop(0, "rgb(255, 199, 43, 1)"); grd.addColorStop(1, "rgb(170 170 170)"); ctx.fillStyle = grd; ctx.fillRect(700, -1025, 125, 100); /// ctx.lineWidth = 7; ctx.strokeStyle = "#444444" ctx.strokeRect(1650, -1300, 175, 150); chair.force.y += chair.mass * simulation.g; chair2.force.y += chair2.mass * simulation.g; person.force.y += person.mass * simulation.g; level.exit.drawAndCheck(); level.enter.draw(); }; level.customTopLayer = () => { ctx.fillStyle = "rgba(64,64,64,0.97)"; ctx.fillRect(2800, -400, 275, 175); hazard.query(); doorBedroom.draw(); doorGrenier.draw(); voletLucarne1.draw(); voletLucarne2.draw(); voletLucarne3.draw(); voletLucarne4.draw(); voletLucarne5.draw(); voletLucarne6.draw(); }; //chairs const part1 = Matter.Bodies.rectangle(4525, -255, 25, 200, { density: 0.0005, isNotHoldable: true, }); const part2 = Matter.Bodies.rectangle(4562, -235, 100, 25, { density: 0.0005, isNotHoldable: true, }); const part3 = Matter.Bodies.rectangle(4600, -202, 25, 91.5, { density: 0.0005, isNotHoldable: true, }); const part4 = Matter.Bodies.rectangle(5100, -255, 25, 200, { density: 0.0005, isNotHoldable: true, }); const part5 = Matter.Bodies.rectangle(5063, -235, 100, 25, { density: 0.0005, isNotHoldable: true, }); const part6 = Matter.Bodies.rectangle(5025, -202, 25, 91.5, { density: 0.0005, isNotHoldable: true, }); chair = Body.create({ parts: [part1, part2, part3], }); chair2 = Body.create({ parts: [part4, part5, part6], }); Composite.add(engine.world, [chair]); Composite.add(engine.world, [chair2]); composite[composite.length] = chair; composite[composite.length] = chair2; body[body.length] = part1; body[body.length] = part2; body[body.length] = part3; body[body.length] = part4; body[body.length] = part5; body[body.length] = part6; setTimeout(function () { chair.collisionFilter.category = cat.body; chair.collisionFilter.mask = cat.body | cat.player | cat.bullet | cat.mob | cat.mobBullet | cat.map }, 1000); setTimeout(function () { chair2.collisionFilter.category = cat.body; chair2.collisionFilter.mask = cat.body | cat.player | cat.bullet | cat.mob | cat.mobBullet | cat.map }, 1000); var head = Matter.Bodies.rectangle(300, -200 - 60, 34, 40, { isNotHoldable: true, }); var chest = Matter.Bodies.rectangle(300, -200, 55, 80, { isNotHoldable: true, }); var rightUpperArm = Matter.Bodies.rectangle(300 + 39, -200 - 15, 20, 40, { isNotHoldable: true, }); var rightLowerArm = Matter.Bodies.rectangle(300 + 39, -200 + 25, 20, 60, { isNotHoldable: true, }); var leftUpperArm = Matter.Bodies.rectangle(300 - 39, -200 - 15, 20, 40, { isNotHoldable: true, }); var leftLowerArm = Matter.Bodies.rectangle(300 - 39, -200 + 25, 20, 60, { isNotHoldable: true, }); var leftUpperLeg = Matter.Bodies.rectangle(300 - 20, -200 + 57, 20, 40, { isNotHoldable: true, }); var leftLowerLeg = Matter.Bodies.rectangle(300 - 20, -200 + 97, 20, 60, { isNotHoldable: true, }); var rightUpperLeg = Matter.Bodies.rectangle(300 + 20, -200 + 57, 20, 40, { isNotHoldable: true, }); var rightLowerLeg = Matter.Bodies.rectangle(300 + 20, -200 + 97, 20, 60, { isNotHoldable: true, }); //man var person = Body.create({ parts: [chest, head, leftLowerArm, leftUpperArm, rightLowerArm, rightUpperArm, leftLowerLeg, rightLowerLeg, leftUpperLeg, rightUpperLeg ], }); Composite.add(engine.world, [person]); composite[composite.length] = person body[body.length] = chest body[body.length] = head body[body.length] = part3 body[body.length] = leftLowerLeg body[body.length] = leftUpperLeg body[body.length] = leftUpperArm body[body.length] = leftLowerArm body[body.length] = rightLowerLeg body[body.length] = rightUpperLeg body[body.length] = rightLowerArm body[body.length] = rightUpperArm setTimeout(function () { person.collisionFilter.category = cat.body; person.collisionFilter.mask = cat.body | cat.player | cat.bullet | cat.mob | cat.mobBullet | cat.map }, 1000); //rez de chaussée spawn.mapRect(-200, 0, 5400, 100); //ground spawn.mapRect(1150, -255, 4050, 355); //additionnal ground spawn.mapRect(800, -255, 400, 90); //1st step spawn.mapRect(650, -170, 550, 90); //2nd step spawn.mapRect(500, -85, 700, 90); //3rd step spawn.mapRect(1150, -850, 50, 175); //porte entrée spawn.bodyRect(1162.5, -675, 25, 420) //porte entrée spawn.mapRect(1150, -850, 1500, 50); //plafond 1 spawn.mapRect(3025, -850, 2175, 50); //plafond 2 spawn.mapRect(5150, -850, 50, 650); //mur cuisine //lave-vaisselle spawn.mapRect(4225, -400, 25, 150); spawn.mapRect(4225, -400, 175, 25); spawn.mapRect(4375, -400, 25, 150); spawn.bodyRect(4350, -350, 20, 40); spawn.bodyRect(4325, -325, 20, 20); spawn.bodyRect(4325, -275, 20, 20); //escalier spawn.mapRect(3025, -850, 50, 225); spawn.mapRect(2925, -775, 150, 150); spawn.mapRect(2800, -700, 275, 75); spawn.mapRect(2575, -400, 175, 175); spawn.mapRect(2475, -325, 175, 100); spawn.mapRect(2675, -475, 400, 100); spawn.mapRect(2675, -475, 150, 250); //cuisine spawn.mapRect(4025, -850, 50, 175); //porte cuisine spawn.mapRect(4025, -375, 50, 125); //porte cuisine map[map.length] = Bodies.polygon(4050, -675, 0, 15); //circle above door spawn.bodyRect(4040, -650, 20, 260, 1, spawn.propsDoor); // door body[body.length - 1].isNotHoldable = true; //makes door swing consBB[consBB.length] = Constraint.create({ bodyA: body[body.length - 1], pointA: { x: 0, y: -130 }, bodyB: map[map.length - 1], stiffness: 1 }); Composite.add(engine.world, consBB[consBB.length - 1]); //table + chaises spawn.mapRect(4025, -850, 50, 175); spawn.mapRect(4650, -375, 325, 25); spawn.mapRect(4700, -350, 25, 100); spawn.mapRect(4900, -350, 25, 100); spawn.bodyRect(4875, -400, 75, 25); spawn.bodyRect(4700, -400, 75, 25); //murs télé spawn.mapRect(3400, -400, 20, 150); spawn.mapRect(3705, -400, 20, 150); spawn.mapRect(3400, -400, 325, 20); //socle écran spawn.mapRect(3500, -415, 125, 17); spawn.mapRect(3550, -450, 25, 50); // ??? spawn.bodyRect(3075, -375, 125, 125); spawn.bodyRect(3075, -400, 50, 25); spawn.bodyRect(3725, -325, 100, 75); spawn.bodyRect(3375, -275, 25, 25); // premier étage spawn.mapRect(1150, -1450, 4050, 50); spawn.mapRect(5150, -1450, 50, 650); spawn.mapRect(1150, -1450, 50, 300); spawn.mapRect(1150, -900, 50, 100); spawn.mapVertex(1066, -730, "-200 60 0 -60 100 -60 100 60") //chambre spawn.mapRect(2350, -1450, 50, 175); //porte chambre //lit spawn.mapRect(1475, -1025, 25, 225); //pied de lit 1 spawn.mapRect(1850, -925, 25, 125); //pied de lit 2 spawn.mapRect(1475, -925, 400, 50); //sommier spawn.bodyRect(1500, -950, 375, 25); //matelat spawn.bodyRect(1500, -1000, 75, 50); //oreiller //table spawn.bodyRect(1950, -1000, 30, 150); //pied table spawn.bodyRect(2250, -1000, 30, 150); //pied table spawn.bodyRect(1920, -1025, 390, 25); //table //salle de bain spawn.mapRect(4025, -1450, 50, 175); //porte salle de bain map[map.length] = Bodies.polygon(5050, -925, 0, 35.4); spawn.mapRect(5015, -960, 125, 40); spawn.mapRect(5050, -925, 90, 35.4); spawn.mapVertex(5086.5, -875, "100 60 -30 60 20 0 100 0") spawn.mapRect(5125, -1070, 15, 120) spawn.bodyRect(5016, -965, 108, 15) //baignoire spawn.mapVertex(4316, -965, "30 100 0 100 -80 -50 30 -50") //bord 1 spawn.mapVertex(4675, -961.5, "30 100 0 100 0 -50 80 -50") //bord 2 spawn.mapVertex(4400, -860, "0 -20 -20 20 20 20 0 -20") //pied 1 spawn.mapVertex(4600, -860, "0 -20 -20 20 20 20 0 -20") //pied 2 spawn.mapRect(4325, -900, 350, 25); //fond baignoire spawn.mapRect(4300, -1175, 25, 175); spawn.mapRect(4300, -1175, 125, 25); spawn.mapRect(4400, -1175, 25, 50); //pied pommeau de douche spawn.mapVertex(4412.5, -1105, "-20 -20 -30 40 30 40 20 -20") //pommeau de douche //grenier spawn.mapRect(1150, -1475, 50, 50); spawn.mapRect(1150, -1800, 50, 175); spawn.mapRect(5150, -1800, 50, 400); //murs spawn.mapVertex(1300, -1900, "-150 200 -200 200 50 0 100 0"); spawn.mapVertex(1800, -2300, "-150 200 -200 200 175 -100 225 -100"); spawn.mapRect(1390, -2180, 250, 30); //lucarne spawn.mapVertex(5050, -1900, "150 200 200 200 -50 0 -100 0"); spawn.mapVertex(4550, -2300, "150 200 200 200 -175 -100 -225 -100"); spawn.mapRect(4710, -2175, 250, 25); //lucarne 2 spawn.mapRect(5150, -1450, 200, 50); //obstacles spawn.mapRect(3775, -1800, 99, 50); spawn.mapRect(2425, -2150, 50, 425); spawn.mapRect(2150, -1775, 325, 50); spawn.mapRect(3825, -2150, 50, 750); spawn.mapRect(3826, -2150, 149, 50); spawn.mapRect(4125, -2150, 149, 50); spawn.mapRect(4225, -2150, 50, 450); spawn.mapRect(4225, -1750, 250, 50); level.chain(2495, -2130, 0, true, 10); spawn.bodyRect(2950, -375, 120, 120) //bloc hidden zone spawn.bodyRect(2350, -1850, 75, 75); spawn.bodyRect(4275, -1900, 75, 100); spawn.bodyRect(4825, -1650, 325, 200); spawn.bodyRect(5025, -1725, 25, 25); spawn.bodyRect(4900, -1700, 200, 75); spawn.mapVertex(2950, -2096, "-75 -50 75 -50 75 0 0 100 -75 0") /*cheminée + roof*/ spawn.mapRect(1963, -2450, 2425, 35); spawn.mapRect(2925, -2900, 125, 480); spawn.mapRect(2900, -2900, 175, 75); spawn.mapRect(2900, -2975, 25, 100); spawn.mapRect(3050, -2975, 25, 100); spawn.mapRect(2875, -3000, 225, 25); // lampadaire + jump spawn.mapRect(1000, -1450, 200, 25); spawn.mapRect(500, -1150, 275, 25); spawn.mapRect(750, -1150, 25, 75); spawn.mapRect(500, -1150, 25, 75); spawn.mapRect(450, -1075, 125, 50); spawn.mapRect(700, -1075, 125, 50); spawn.mapRect(2985, -4600, 0.1, 1700) //bodyRects ~= debris spawn.bodyRect(1740, -475, 80, 220) spawn.bodyRect(1840, -290, 38, 23) spawn.bodyRect(1200 + 1475 * Math.random(), -350, 15 + 110 * Math.random(), 15 + 110 * Math.random()); spawn.bodyRect(1200 + 1475 * Math.random(), -350, 15 + 110 * Math.random(), 15 + 110 * Math.random()); spawn.bodyRect(3070 + 600 * Math.random(), -1100, 20 + 50 * Math.random(), 150 + 100 * Math.random()) spawn.bodyRect(3050 + 1000 * Math.random(), -920, 30 + 100 * Math.random(), 15 + 65 * Math.random()); spawn.bodyRect(1600 + 250 * Math.random(), -1540, 80, 220) //boss room spawn.debris(3070, -900, 1000, 3); //16 debris per level spawn.debris(1200, -350, 1475, 4); //16 debris per level spawn.debris(1250, -1550, 3565, 9); //16 debris per level powerUps.chooseRandomPowerUp(2860, -270); // Mobs spawn.randomSmallMob(1385, -600, 1); spawn.randomSmallMob(5000, -680, 1); spawn.randomSmallMob(4750, -925, 1); spawn.randomSmallMob(2300, -1830, 1); spawn.randomMob(3170, -720, 0.8); spawn.randomMob(3700, -975, 0.8); spawn.randomMob(2625, -1150, 0.7); spawn.randomMob(4175, -750, 0.7); spawn.randomMob(2100, -370, 0.7); spawn.randomMob(2000, -1230, 0.7); spawn.randomMob(4175, -1075, 0.6); spawn.randomMob(3965, -1650, 0.6) spawn.randomMob(4650, -1750, 0.6); spawn.randomMob(830, -1170, 0.5); spawn.randomGroup(3730, -1100, 0.5); spawn.randomMob(2650, -2250, 0.3); spawn.randomMob(1615, -2270, 0.3); spawn.randomMob(1380, -1280, 0.25); spawn.randomMob(2280, -650, 0.2); spawn.randomGroup(2450, -2650, 0.2); spawn.randomMob(3800, -580, 0.2); spawn.randomMob(4630, -425, 0.1); spawn.randomGroup(630, -1300, -0.1); spawn.randomGroup(3450, -2880, -0.2) if (simulation.difficulty > 3) { spawn.secondaryBossChance(3380, -1775) if (Math.random() < 0.16) { spawn.tetherBoss(3380, -1775, { x: 3775, y: -1775 }) } else { spawn.randomLevelBoss(3100, -1850, ["shooterBoss", "spiderBoss", "launcherBoss", "laserTargetingBoss", "dragonFlyBoss", "laserBoss"]); } } }, perplex() { //by Oranger from discord simulation.makeTextLog(`perplex by Oranger`); document.body.style.backgroundColor = "#dcdcde"; level.setPosToSpawn(-600, 400); spawn.mapRect(level.enter.x, level.enter.y + 20, 100, 20); level.exit.x = 550; level.exit.y = -2730; spawn.mapRect(level.exit.x, level.exit.y + 20, 100, 20); const portal = level.portal({ //main portals x: -1000, y: 50 }, -Math.PI / 2, { //up x: 1000, y: 50 }, -Math.PI / 2) //up const portal2 = level.portal({ //portals in upper right corner x: 1400, y: -2200 }, -Math.PI / 2, { //up x: 1700, y: -1700 }, -Math.PI / 2) //up // rotor(x, y, width, height, density = 0.001, angle = 0, frictionAir = 0.001, angularVelocity = 0, rotationForce = 0.0005) { const rotor = level.rotor(-600, -1950, 800, 50, 0.001, 0, 0.01, 0, -0.001) level.custom = () => { portal[2].query(true) portal[3].query(true) portal2[2].query(true) portal2[3].query(true) rotor.rotate(); ctx.fillStyle = "#d4f4f4"; ctx.fillRect(375, -3000, 450, 300); level.exit.drawAndCheck(); level.enter.draw(); }; level.customTopLayer = () => { portal[0].draw(); portal[1].draw(); portal[2].draw(); portal[3].draw(); portal2[0].draw(); portal2[1].draw(); portal2[2].draw(); portal2[3].draw(); ctx.fillStyle = "rgba(0,0,0,0.03)"; ctx.fillRect(-875, -250, 1500, 700); ctx.fillRect(-925, -505, 930, 255); ctx.fillStyle = "rgba(0,0,0,0.1)"; ctx.fillRect(725, -1400, 200, 200); ctx.fillRect(925, -2150, 150, 2175); ctx.fillRect(925, -3400, 150, 850); ctx.fillStyle = "rgba(0,0,0,0.03)"; ctx.fillRect(1800, -2600, 400, 400); ctx.fillRect(2200, -2600, 400, 1250); }; level.defaultZoom = 1700 // 4500 // 1400 simulation.zoomTransition(level.defaultZoom) //section 1: before portals spawn.mapRect(-925, 450, 1850, 250); //1-1 base spawn.mapRect(-925, -300, 55, 755); //1 left wall spawn.mapRect(-875, 50, 1100, 50); //1-1 ceiling spawn.mapRect(620, -300, 305, 755); //1-1 and 1-2 right wall spawn.bodyRect(200, 350, 230, 100); spawn.bodyRect(300, 250, 150, 100); spawn.mapRect(-875, -300, 580, 50); //1-2 ceiling on left spawn.mapRect(0, -300, 625, 50); //1-2 ceiling on right spawn.mapRect(0, -650, 150, 350); //1-3 right wall spawn.mapRect(-925, -650, 975, 150); //1-3 ceiling spawn.mapRect(-1280, 100, 205, 150); //1-4 floor spawn.mapRect(-1280, 245, 360, 455); //bottom left corner spawn.mapRect(-1600, -200, 200, 50); //1-4 platform 1 //section 2: lower central room (gone through main portals 1 time) spawn.mapRect(920, 245, 160, 455); //below right portal spawn.mapRect(1075, -300, 500, 1000); //2-1 right floor spawn.bodyRect(100, -1000, 50, 350); spawn.bodyRect(100, -1015, 250, 15); spawn.mapRect(-925, -1600, 100, 1000); //2-2 left wall spawn.mapRect(725, -2150, 200, 750); //2-2 right wall spawn.mapRect(725, -1200, 200, 200); //2-2 right wall 2 spawn.mapRect(300, -1000, 625, 50); //2 central ledge //shute spawn.mapRect(1075, -2005, 550, 1055); //shute right wall spawn.mapRect(875, -1000, 50, 300); //shute left 1 spawn.mapRect(860, -1030, 50, 300); //shute left 2 spawn.mapRect(850, -1100, 50, 300); //shute left 3 spawn.mapRect(830, -980, 50, 50); //shute left 4 spawn.mapRect(1075, -1000, 50, 300); //shute right 1 spawn.mapRect(1090, -1030, 50, 300); //shute right 2 spawn.mapRect(1100, -1100, 50, 300); //shute right 3 spawn.mapRect(1120, -980, 50, 50); //shute right 4 spawn.mapRect(1850, -650, 400, 50); //drop from 4-1 //section 3: upper left room and upper central room (gone through main portals 2 times) //3-2 is just the upper part of 2-2 spawn.mapRect(-1775, -1000, 700, 300); //3-1 floor spawn.mapRect(-1900, -2300, 175, 1600); //3-1 left wall spawn.mapRect(-1375, -1300, 300, 50); //3-1 platform 1 spawn.mapRect(-1600, -1650, 300, 50); //3-1 platform 2 spawn.mapRect(-1775, -2300, 700, 300); //3-1 ceiling spawn.mapRect(-830, -1600, 300, 50); //3-2 left ledge spawn.mapRect(250, -2150, 675, 50); //3-2 right ledge spawn.mapRect(-925, -2300, 100, 300); //3-2 left wall spawn.mapRect(-600, -2700, 1525, 150); //3-2 ceiling spawn.mapRect(1075, -2150, 250, 150); //next to upper portal // level.fill.push({ // x: -1730, // y: -2300, // width: 870, // height: 1600, // color: "rgba(0,0,0,0.03)" // }); //section 4: upper right portals spawn.mapRect(1475, -2700, 150, 700); //4-1 left wall spawn.mapRect(1775, -1650, 250, 150); //4-1 floor-ish spawn.mapRect(1575, -1505, 450, 555); //below upper right portal spawn.mapRect(1800, -2250, 400, 50); //4-1 platform 2 spawn.bodyRect(2200, -2250, 15, 300); spawn.mapRect(2200, -1950, 400, 50); //4-1 platform 1 //spawn.bodyRect(2575, -2600, 25, 650); spawn.mapRect(2600, -1650, 400, 50); //4-1 platform 0 spawn.mapRect(2200, -1350, 400, 50); //4-1 platform -1 spawn.bodyRect(2200, -1900, 15, 550); spawn.bodyRect(2585, -1650, 15, 300); spawn.mapRect(1800, -4200, 800, 1600); //4-2 right wall spawn.mapRect(800, -4200, 1800, -500); //4-2 ceiling spawn.mapRect(1075, -3400, 225, 850); //upper shute right wall spawn.mapRect(800, -3400, 125, 850); //upper shute left wall //section 5: after portals (gone through main portals 3 times) spawn.mapRect(-700, -2700, 100, 450); //5-1 right wall spawn.mapRect(-1450, -2700, 900, 50); //5-1 ceiling spawn.mapRect(-925, -2300, 325, 50); //5-1 right floor spawn.mapRect(-1900, -3000, 450, 50); //stair cover spawn.bodyRect(-1150, -2950, 150, 250); //5-2 block //top left corner stuff spawn.mapRect(-1900, -2450, 250, 450); // //exit room spawn.mapRect(350, -3000, 50, 100); //exit room left wall spawn.mapRect(350, -3000, 450, -1700); //exit room ceiling spawn.bodyRect(350, -2900, 50, 50.5); //door spawn.bodyRect(350, -2850, 50, 50.5); //door spawn.bodyRect(350, -2800, 50, 50.5); //door spawn.bodyRect(350, -2750, 50, 50.5); //door spawn.debris(-400, 450, 400, 5); //16 debris per level spawn.debris(-1650, -2300, 250, 4); //16 debris per level spawn.debris(-750, -650, 750, 3); //16 debris per level //mobs spawn.randomMob(-650, -100, 0.7); //1-2 left spawn.randomMob(100, -150, 0.3); //1-2 right spawn.randomMob(-100, -400, 0); //1-3 right //spawn.randomMob(-1500, -300, 0.3); //1-4 platform spawn.randomMob(1450, -450, 0); //2-1 right spawn.randomMob(1700, -800, 1); //2-1 off the edge. chance is 1 because some enemies just fall spawn.randomGroup(-550, -900, -0.3); //2-2 spawn.randomMob(-1550, -1800, 0.7); //3-1 upper platform //spawn.randomMob(-1225, -1400, 0.3); //3-1 lower platform spawn.randomMob(450, -2350, 0.3); //3-2 right ledge //spawn.randomMob(1150, -2250, 0); //3-2 far right spawn.randomGroup(2400, -2300, -0.3); //4-1 floating spawn.randomMob(2400, -1450, 0); //4-1 platform -1 spawn.randomMob(2800, -1800, 0.5); //4-1 platform 0 spawn.randomMob(-1700, -3200, 0.7); //5-2 left platform spawn.randomMob(-550, -2800, 0.3); //5-2 middle if (simulation.difficulty > 3) { if (Math.random() < 0.5) { spawn.randomLevelBoss(450, -1350, ["shooterBoss", "launcherBoss", "laserTargetingBoss", "streamBoss", "shieldingBoss", "pulsarBoss", "laserBoss"]); } else { spawn.randomLevelBoss(-300, -3200, ["shooterBoss", "launcherBoss", "laserTargetingBoss", "streamBoss", "shieldingBoss", "pulsarBoss", "laserBoss"]); } } powerUps.addResearchToLevel() //needs to run after mobs are spawned spawn.secondaryBossChance(7725, 2275) }, coliseum() { simulation.makeTextLog(`coliseum by iNoobBoi`); level.custom = () => { level.exit.drawAndCheck(); level.enter.draw(); }; level.customTopLayer = () => { }; level.defaultZoom = 1800 simulation.zoomTransition(level.defaultZoom) document.body.style.backgroundColor = "#dcdcde"; //Level level.setPosToSpawn(200, 50); spawn.mapRect(level.enter.x, level.enter.y + 20, 100, 20); level.exit.x = 8950; level.exit.y = 170; spawn.mapRect(level.exit.x, level.exit.y + 20, 100, 20); //Map spawn.mapRect(-100, -400, 100, 600); spawn.mapRect(-100, 100, 700, 100); spawn.mapRect(500, 100, 100, 1700); spawn.mapRect(500, 1700, 4000, 100); spawn.mapRect(4100, 600, 400, 100); spawn.mapRect(4400, 600, 100, 1600); spawn.mapRect(4400, 2100, 4300, 100); spawn.mapRect(8600, 200, 100, 2000); spawn.mapRect(8600, 200, 700, 100); spawn.mapRect(9200, -300, 100, 600); spawn.mapRect(8600, -300, 700, 100); spawn.mapRect(8600, -700, 100, 500); spawn.mapRect(4400, -700, 4300, 100); spawn.mapRect(4400, -700, 100, 900); spawn.mapRect(-100, -400, 4600, 100); //Platforms spawn.mapRect(1100, 400, 300, 100); spawn.mapRect(500, 500, 300, 100); spawn.mapRect(1050, 800, 300, 100); spawn.mapRect(1770, 1050, 300, 100); spawn.mapRect(1800, 500, 300, 100); spawn.mapRect(2550, 900, 300, 100); spawn.mapRect(2800, 1400, 300, 100); spawn.mapRect(1250, 1350, 300, 100); spawn.mapRect(4750, 850, 300, 100); spawn.mapRect(3200, 1050, 300, 100); spawn.mapRect(4700, 100, 300, 100); spawn.mapRect(5350, 0, 300, 100); spawn.mapRect(3800, 900, 300, 100); spawn.mapRect(5100, 500, 300, 100); spawn.mapRect(5900, -300, 300, 100); spawn.mapRect(6500, -700, 300, 1300); spawn.mapRect(7900, 0, 300, 100); spawn.mapRect(8050, 800, 300, 100); spawn.mapRect(7800, 1900, 300, 100); spawn.mapRect(8300, 450, 300, 100); spawn.mapRect(8400, 1200, 300, 100); spawn.mapRect(7570, 1100, 300, 100); spawn.mapRect(6700, 1850, 300, 100); spawn.mapRect(8000, 1500, 300, 100); spawn.mapRect(7120, -100, 300, 100); spawn.mapRect(7000, 1500, 300, 100); spawn.mapRect(6500, 1000, 300, 1200); spawn.mapRect(5800, 1100, 300, 100); spawn.mapRect(5900, 1700, 300, 100); spawn.mapRect(5300, 1400, 300, 100); spawn.mapRect(5200, 1100, 300, 100); spawn.mapRect(6700, 1100, 300, 100); spawn.mapRect(4800, 1650, 300, 100); //Room 1 Spawning spawn.randomMob(1000, 700, 0.7); spawn.randomGroup(1100, 700, 0.5); spawn.randomMob(1900, 400, 0.7); spawn.randomGroup(2000, 400, 0.4); spawn.randomGroup(1800, 1100, 0.4); spawn.randomGroup(2700, 700, 0.5); spawn.randomMob(2900, 1200, 0.7); spawn.randomSmallMob(3200, 300, 0.9); spawn.randomSmallMob(3700, 800, 0.9); spawn.randomMob(1100, 700, 0.6); spawn.randomGroup(1200, 700, 0.5); spawn.randomMob(2000, 400, 0.8); spawn.randomGroup(2100, 400, 0.5); spawn.randomGroup(1900, 1100, 0.5); spawn.randomGroup(2800, 700, 0.5); spawn.randomMob(3000, 1200, 0.7); spawn.randomSmallMob(3200, 300, 0.9); spawn.randomSmallMob(3700, 800, 0.9); spawn.randomMob(800, 1500, 0.9); spawn.randomMob(1500, 1500, 0.7); spawn.randomMob(2200, 1500, 0.6); spawn.randomMob(2500, 1500, 0.7); spawn.randomMob(2800, 1500, 0.7); spawn.randomMob(3300, 1500, 0.6); //Room 2 Spawning spawn.randomGroup(4700, 2000, 0.9); spawn.randomMob(5000, 2000, 0.5); spawn.randomSmallMob(5700, 1500, 0.9); spawn.randomMob(8500, 2000, 0.6); spawn.randomGroup(8000, 1300, 0.9); spawn.randomMob(8300, -300, 0.4); spawn.randomSmallMob(7600, -200, 0.9); spawn.randomMob(5200, -300, 0.5); spawn.randomSmallMob(4700, -200, 0.5); spawn.randomGroup(4700, 2000, 0.8); spawn.randomMob(5000, 2000, 0.5); spawn.randomSmallMob(5700, 1500, 0.9); spawn.randomGroup(8500, 2000, 0.3); spawn.randomSmallMob(8000, 1300, 0.4); spawn.randomMob(8300, -300, 0.3); spawn.randomGroup(7600, -200, 0.5); spawn.randomMob(5200, -300, 0.3); spawn.randomGroup(4700, -200, 0.4); spawn.randomGroup(8650, -200, 0.9); //end guards spawn.randomMob(8650, -200, 0.9); //end guards //Boss Spawning if (simulation.difficulty > 3) { spawn.randomLevelBoss(6000, 700, ["pulsarBoss", "laserTargetingBoss", "powerUpBoss", "bomberBoss", "historyBoss", "orbitalBoss"]); // if (simulation.difficulty > 10) spawn.shieldingBoss(7200, 500); // if (simulation.difficulty > 20) spawn.randomLevelBoss(2000, 300, ["historyBoss", "shooterBoss"]); } //Blocks spawn.bodyRect(550, -300, 50, 400); //spawn door spawn.bodyRect(4400, 200, 100, 400); //boss door spawn.bodyRect(6600, 600, 50, 400); //boss 2 door spawn.debris(400, 800, 400, 2); spawn.debris(3800, 1600, 1200, 6); spawn.debris(7500, 2000, 800, 4); spawn.debris(5500, 2000, 800, 4); //Powerups powerUps.spawnStartingPowerUps(1250, 1500); // powerUps.spawnStartingPowerUps(1500, 1500); powerUps.spawn(8650, -200, "ammo"); // powerUps.spawn(8650, -200, "ammo"); // powerUps.spawn(8650, -200, "ammo"); // powerUps.spawn(8650, -200, "ammo"); powerUps.spawn(200, 50, "heal"); // powerUps.spawn(200, 50, "ammo"); // powerUps.spawn(200, 50, "ammo"); // powerUps.spawn(200, 50, "ammo"); powerUps.addResearchToLevel() //needs to run after mobs are spawned spawn.secondaryBossChance(6600, 600) }, crossfire() { simulation.makeTextLog(`crossfire by iNoobBoi`); //*1.5 //Level Setup const slimePitOne = level.hazard(0, 850, 3800, 120); const slimePitTwo = level.hazard(4600, 430, 2000, 120); const slimePitThree = level.hazard(6500, 200, 1000, 170); level.custom = () => { level.exit.drawAndCheck(); level.enter.draw(); }; level.customTopLayer = () => { slimePitOne.query(); slimePitTwo.query(); slimePitThree.query(); }; level.setPosToSpawn(-500, 550); //normal spawn spawn.mapRect(level.enter.x, level.enter.y + 20, 100, 20); level.exit.x = 10300; level.exit.y = -830; spawn.mapRect(level.exit.x, level.exit.y + 20, 100, 20); level.defaultZoom = 3000 simulation.zoomTransition(level.defaultZoom) document.body.style.backgroundColor = "#dcdcde"; //Map Elements spawn.mapRect(-800, -600, 800, 200); spawn.mapRect(-200, -600, 200, 800); spawn.mapRect(-800, -600, 200, 800); spawn.mapRect(-1000, 0, 1000, 200); spawn.mapRect(-1000, 0, 200, 800); spawn.mapRect(-1000, 600, 1400, 200); spawn.mapRect(0, 600, 200, 400); spawn.mapRect(0, 950, 4000, 100); spawn.mapRect(800, 800, 600, 200); spawn.mapRect(1700, 700, 500, 300); spawn.mapRect(2500, 600, 400, 400); spawn.mapRect(3200, 600, 1200, 200); spawn.mapRect(3800, 600, 200, 800); // spawn.mapRect(3800, 1200, 800, 200); spawn.mapRect(4400, 400, 300, 1000); spawn.mapRect(4400, 500, 2000, 100); spawn.mapRect(6500, 300, 1000, 100); spawn.mapRect(5000, 200, 700, 400); spawn.mapRect(6000, 0, 650, 600); spawn.mapRect(6900, -300, 700, 100); spawn.mapRect(7400, -600, 200, 1100); spawn.mapRect(7400, 300, 2600, 200); spawn.mapRect(9800, -800, 200, 1300); spawn.mapRect(9800, -800, 1000, 200); spawn.mapRect(10600, -1400, 200, 800); spawn.mapRect(9800, -1400, 200, 400); spawn.mapRect(7400, -1400, 3400, 200); spawn.mapRect(7400, -1600, 200, 800); spawn.mapRect(5400, -1600, 2200, 200); spawn.mapRect(6000, -1600, 200, 800); spawn.mapRect(5400, -1600, 200, 800); spawn.mapRect(4800, -1000, 1400, 200); spawn.mapRect(4800, -1000, 200, 600); spawn.mapRect(3800, -600, 1200, 200); spawn.mapRect(3200, -800, 800, 200); spawn.mapRect(3200, -800, 200, 800); spawn.mapRect(3800, -800, 200, 800); spawn.mapRect(-200, -200, 4200, 200); //Boss Room Platforms spawn.mapRect(7700, 100, 300, 40); spawn.mapRect(8600, 0, 300, 40); spawn.mapRect(9200, 100, 300, 40); spawn.mapRect(9400, -200, 300, 40); spawn.mapRect(8000, -200, 300, 40); spawn.mapRect(8500, -400, 300, 40); spawn.mapRect(9000, -600, 300, 40); spawn.mapRect(9400, -800, 300, 40); spawn.mapRect(8600, -1000, 300, 40); spawn.mapRect(7900, -800, 300, 40); //Mob Spawning spawn.randomMob(200, 400, 0.7); // spawn.randomMob(1200, 400, 0.7); spawn.randomMob(2000, 400, 0.7); // spawn.randomMob(3000, 400, 0.7); spawn.randomMob(5000, 0, 0.7); spawn.randomMob(5600, 0, 0.7); spawn.randomMob(6200, -200, 0.7); // spawn.randomMob(6600, -200, 0.7); spawn.randomMob(7200, -800, 0.7); spawn.randomSmallMob(800, 400, 0.9); spawn.randomSmallMob(1800, 400, 0.9); // spawn.randomSmallMob(2600, 400, 0.9); spawn.randomSmallMob(5200, 0, 0.9); // spawn.randomSmallMob(5400, 0, 0.9); spawn.randomSmallMob(6400, -200, 0.9); spawn.randomGroup(3800, 400, 0.5); spawn.randomGroup(4200, 400, 0.5); // spawn.randomGroup(4400, 200, 0.5); spawn.randomGroup(7000, -800, 0.5); // spawn.randomGroup(7700, 300, 0.5); spawn.randomGroup(9800, 300, 0.5); // spawn.randomGroup(7700, -1100, 0.5); spawn.randomGroup(9800, -1100, 0.5); if (simulation.difficulty > 3) spawn.randomLevelBoss(8600, -600, ["powerUpBoss", "bomberBoss", "dragonFlyBoss", "spiderBoss", "historyBoss"]) spawn.secondaryBossChance(7900, -400) //Boss Spawning if (simulation.difficulty > 10) { spawn.pulsarBoss(3600, -400); powerUps.chooseRandomPowerUp(4006, 400); powerUps.chooseRandomPowerUp(4407, 400); powerUps.spawnStartingPowerUps(4400, 400); if (simulation.difficulty > 30) { powerUps.chooseRandomPowerUp(4002, 400); powerUps.chooseRandomPowerUp(4004, 400); spawn.pulsarBoss(4200, 1000); if (simulation.difficulty > 60) { powerUps.chooseRandomPowerUp(4409, 400); spawn.pulsarBoss(5800, -1200); if (simulation.difficulty > 80) { spawn.pulsarBoss(-400, -200); if (simulation.difficulty > 100) { spawn.pulsarBoss(3600, -400); if (simulation.difficulty > 120) { spawn.pulsarBoss(-400, -200); } } } } } } //Powerup Spawning powerUps.spawnStartingPowerUps(4000, 400); powerUps.addResearchToLevel(); //needs to run after mobs are spawned //Block Spawning // spawn.bodyRect(-100, 200, 100, 400); //spawn door spawn.bodyRect(7450, -800, 25, 200); //boss room door spawn.bodyRect(9850, -1000, 25, 200); //end door spawn.mapRect(-200, 350, 200, 450); // spawn.mapRect(3875, -75, 50, 575); spawn.mapRect(3800, -75, 200, 525); spawn.mapRect(3875, 590, 50, 150); spawn.mapRect(3875, 350, 50, 140); const debrisCount = 3 spawn.debris(1050, 700, 400, debrisCount); spawn.debris(1900, 600, 400, debrisCount); spawn.debris(2700, 500, 400, debrisCount); // spawn.debris(3500, 450, 400, debrisCount); spawn.debris(4150, 500, 400, debrisCount); spawn.debris(5300, 0, 400, debrisCount); spawn.debris(6300, -100, 400, debrisCount); spawn.debris(7200, -500, 400, debrisCount); spawn.debris(8000, -600, 400, debrisCount); spawn.debris(8700, -700, 400, debrisCount); spawn.debris(9300, -900, 400, debrisCount); }, vats() { // Made by Dablux#6610 on Discord simulation.makeTextLog(`vats by Dablux`); simulation.zoomScale = 1500; level.setPosToSpawn(4400, -1060) spawn.mapRect(level.enter.x, level.enter.y + 30, 100, 20) level.exit.x = 3900; level.exit.y = 1060; spawn.mapRect(level.exit.x, level.exit.y + 30, 100, 20) document.body.style.backgroundColor = "#dcdcde"; var nextBlockSpawn = simulation.cycle + Math.floor(Math.random() * 60 + 30) const door = level.door(475, 900, 50, 200, 201) const exitDoor = level.door(3375, 900, 50, 200, 201) const deliveryButton = level.button(3500, -410) const buttonGreen = level.button(-1600, 1090) const buttonYellow = level.button(-1600, -1160) const buttonRed = level.button(5874, -2410) let g = false; let y = false; let r = false; const deliverySlime = level.hazard(3700, -940, 100, 480) const deliverySlime2 = level.hazard(3700, -461, 100, 1141) const slimePit = level.hazard(700, 1200, 2500, 1300, 0.004) const topSlime = level.hazard(800, -460, 2900, 90, 0.004) // const rotor = level.rotor(0, -725, 0.001) const rotor = level.rotor(-400, -725, 800, 50, 0.001, 0, 0.01, 0, 0.001) const portal = level.portal({ x: -135, y: 800 }, Math.PI / 2, { x: 570, y: -395 }, -Math.PI / 2) const portal2 = level.portal({ x: -1800, y: 1900 }, Math.PI, { x: 200, y: 1105 }, -Math.PI / 2) const drip1 = level.drip(1875, -660, -400, 70) const drip2 = level.drip(3525, -940, -400, 150) const drip3 = level.drip(1975, 100, 1200, 100) door.isClosing = true; exitDoor.isClosing = true; // UPPER AREA // spawn.mapRect(4500, -2400, 1700, 2050) spawn.mapRect(3800, -1000, 700, 650) spawn.mapRect(4000, -1310, 50, 60) spawn.mapRect(4450, -1310, 50, 60) spawn.mapRect(4000, -1320, 500, 20) level.chain(4025, -1225, 0.5 * Math.PI, false, 5, 25) spawn.mapRect(3650, -460, 50, 90) spawn.mapRect(3525, -1000, 325, 20) spawn.mapRect(3650, -1000, 50, 440) spawn.mapRect(3300, -1000, 50, 450) spawn.mapRect(3325, -725, 150, 25) spawn.mapRect(3500, -980, 175, 35) spawn.mapRect(3325, -980, 50, 35) spawn.mapRect(-1800, -1250, 50, 120) spawn.mapRect(6150, -2500, 50, 120) spawn.bodyRect(3350, -1000, 175, 20, 1, spawn.propsIsNotHoldable) // Cover Matter.Body.setMass(body[body.length - 1], 0.7) // Make cover easier to remove spawn.mapRect(750, -475, 50, 75); for (let i = 1; i < 5; i++) { spawn.mapRect(800 + (i * 100) + (500 * (i - 1)), -460 + (i * -120) + (20 * (i - 1)), 500, 20) } // ARENA // spawn.mapRect(400, -400, 2950, 500) spawn.mapRect(-1800, -1150, 1800, 1950) spawn.mapRect(-1800, 1100, 780, 1800) spawn.mapRect(-300, 1100, 1000, 1800) //spawn.mapRect(-1800, -1450, 100, 2000) spawn.blockDoor(-1800, 1070) level.chain(-1000, 1120, 0, true, 18, 20) spawn.mapRect(700, 2500, 2500, 900) spawn.mapRect(400, 100, 200, 599) spawn.mapRect(400, 650, 75, 250) spawn.mapRect(525, 650, 75, 250) spawn.mapRect(3300, 650, 75, 250) spawn.mapRect(3425, 650, 75, 250) spawn.mapRect(3200, 1100, 1800, 2200) spawn.mapRect(3300, -400, 200, 1099) // STOP CHANGING THIS ONE!!!! spawn.mapRect(3450, -400, 250, 1100) spawn.mapRect(3650, 680, 200, 20) spawn.mapRect(3800, -400, 1400, 1100) spawn.mapRect(4100, 700, 100, 300) spawn.mapRect(4900, -400, 1300, 2500) spawn.bodyRect(4100, 1000, 100, 100) spawn.bodyRect(-2100, 2050, 290, 30) //Portal platform let b = body[body.length - 1]; b.isNotHoldable = true cons[cons.length] = Constraint.create({ pointA: { x: -1820, y: 2065 }, bodyB: b, pointB: { x: -135, y: 0 }, stiffness: 1, length: 1 }); cons[cons.length] = Constraint.create({ pointA: { x: -1800, y: 1400 }, bodyB: b, pointB: { x: 135, y: 0 }, stiffness: 0.005, length: 700 }); Composite.add(engine.world, [cons[cons.length - 2], cons[cons.length - 1]]); spawn.bodyRect(5225, -2525, 300, 75); spawn.bodyRect(4700, -2525, 100, 75, 0.5); spawn.bodyRect(4900, -2600, 50, 50, 0.4); spawn.bodyRect(5050, -2475, 500, 100, 0.4); spawn.bodyRect(2950, -950, 175, 75, 0.5); spawn.bodyRect(3050, -1000, 75, 50, 0.3); spawn.bodyRect(2300, -850, 75, 50, 0.7); spawn.bodyRect(2150, -575, 100, 175, 0.6); spawn.bodyRect(2500, -550, 400, 150, 0.2); spawn.bodyRect(1525, -500, 225, 100, 0.2); spawn.bodyRect(1625, -575, 100, 75); spawn.bodyRect(1000, -475, 100, 100, 0.8); spawn.bodyRect(1225, -450, 125, 50, 0.9); spawn.bodyRect(525, -500, 175, 125, 0.75); spawn.bodyRect(575, -600, 100, 75, 0.5); spawn.bodyRect(-925, -1225, 275, 75, 0.4); spawn.bodyRect(-1125, -1300, 200, 150, 0.7); spawn.bodyRect(-475, -1250, 200, 100, 0.8); spawn.bodyRect(-425, -1300, 100, 50, 0.75); spawn.bodyRect(-1225, -1200, 100, 25, 0.45); spawn.bodyRect(-1025, -1350, 75, 50, 0.5); spawn.bodyRect(-450, 1025, 75, 50, 0.5); spawn.bodyRect(-775, 1050, 50, 50, 0.6); spawn.bodyRect(-650, 975, 75, 75, 0.2); spawn.bodyRect(-475, 1025, 100, 50, 0.7); spawn.bodyRect(-450, 1025, 75, 50, 0.6); spawn.bodyRect(-800, 1050, 100, 50, 0.5); spawn.bodyRect(-600, 950, 75, 75, 0.3); spawn.bodyRect(-500, 1000, 75, 25, 0.2); spawn.bodyRect(-900, 1025, 150, 50); spawn.bodyRect(-1350, 1000, 100, 100, 0.4); spawn.bodyRect(-1225, 1075, 100, 25); spawn.debris(900, -1000, 2000, 16); // MOBS // spawn.randomSmallMob(2900, -1000) spawn.randomSmallMob(1750, -700) spawn.randomMob(4250, -1400) spawn.randomMob(4800, -2400, 0.3) spawn.randomMob(1000, 600, 0.3) spawn.randomMob(1650, 950, 0.2) spawn.randomMob(1300, -1250, 0) spawn.randomMob(-600, -1250, 0.1) spawn.randomMob(1000, -600, 0.4) spawn.randomMob(1800, -700, 0.4) spawn.randomMob(2200, 950, 0.2) spawn.randomMob(-1900, 1400, 0.3) spawn.randomMob(-750, -1000, 0.3) spawn.randomMob(3250, 1000, 0.1) spawn.randomMob(2000, -2800, 0.4) spawn.randomMob(2200, -500, 0) spawn.randomMob(1800, -450, 0.3) spawn.randomGroup(2300, -450, 1) spawn.randomGroup(3000, -450, 0.3) spawn.randomGroup(6000, -2700, 0) spawn.randomGroup(-1200, -1300, -0.3) powerUps.addResearchToLevel() if (simulation.difficulty > 3) { spawn.randomLevelBoss(1900, 400, ["shieldingBoss", "shooterBoss", "launcherBoss", "streamBoss"]) } else { exitDoor.isClosing = false; } spawn.secondaryBossChance(800, -800) powerUps.spawn(4450, 1050, "heal"); if (Math.random() > (0.2 + (simulation.difficulty / 60))) { powerUps.spawn(4500, 1050, "ammo"); powerUps.spawn(4550, 1050, "ammo"); } else { powerUps.spawn(4500, 1050, "tech"); spawn.randomMob(4550, 1050, Infinity); } powerUps.spawnStartingPowerUps(3750, -940) const W = 500; const H = 20; for (let i = 1; i < 5; i++) { spawn.bodyRect(700 + (i * 100) + (W * (i - 1)), 1110, W, H, 1, spawn.propsIsNotHoldable) let b = body[body.length - 1]; cons[cons.length] = Constraint.create({ pointA: { x: b.position.x - (W / 2) + 50, y: b.position.y - 1025 }, bodyB: b, pointB: { x: -(W / 2) + 50, y: 0 }, stiffness: 0.002, length: 1000 }); cons[cons.length] = Constraint.create({ pointA: { x: b.position.x + (W / 2) - 50, y: b.position.y - 1025 }, bodyB: b, pointB: { x: (W / 2) - 50, y: 0 }, stiffness: 0.002, length: 1000 }); Composite.add(engine.world, [cons[cons.length - 1], cons[cons.length - 2]]) } const boost1 = level.boost(4400, -1385, 1200) level.custom = () => { boost1.query(); buttonGreen.query() buttonYellow.query() buttonRed.query() if (!buttonGreen.isUp) { if (!g) { Matter.Composite.remove(engine.world, cons[1]) cons.splice(1, 2) } g = true; } if (!buttonYellow.isUp) { y = true; } if (!buttonRed.isUp) { r = true; } if (g && y && r) { door.isClosing = false; } else { door.isClosing = true; } door.openClose() exitDoor.openClose() if (m.pos.y > 1600 && 700 < m.pos.x && m.pos.x < 3200) { // Saving player from slime pit Matter.Body.setVelocity(player, { x: 0, y: 0 }); Matter.Body.setPosition(player, { x: 200, y: 1000 }); // move bots for (let i = 0; i < bullet.length; i++) { if (bullet[i].botType) { Matter.Body.setPosition(bullet[i], Vector.add(player.position, { x: 250 * (Math.random() - 0.5), y: 250 * (Math.random() - 0.5) })); Matter.Body.setVelocity(bullet[i], { x: 0, y: 0 }); } } m.damage(0.1 * simulation.difficultyMode) m.energy -= 0.1 * simulation.difficultyMode } if (simulation.cycle >= nextBlockSpawn && body.length < 100) { var len = body.length; body[len] = Matter.Bodies.polygon(Math.floor(Math.random() * 1700) + 1050, 100, Math.floor(Math.random() * 11) + 10, Math.floor(Math.random() * 20) + 15) body[len].collisionFilter.category = cat.body; body[len].collisionFilter.mask = cat.player | cat.map | cat.body | cat.bullet | cat.mob | cat.mobBullet; Composite.add(engine.world, body[len]) nextBlockSpawn = simulation.cycle + Math.floor(Math.random() * 60 + 30) } if (exitDoor.isClosing) { exitDoor.isClosing = false; for (i = 0; i < mob.length; i++) { if (mob[i].isBoss && 525 < mob[i].position.x < 3200 && -2500 < mob[i].position.y < 100) { exitDoor.isClosing = true; } } } for (let i = 0, len = body.length; i < len; i++) { if (body[i].position.x > 700 && body[i].position.x < 3200 && body[i].position.y > 1200 && !body[i].isNotHoldable) { Matter.Body.scale(body[i], 0.99, 0.99); if (body[i].velocity.y > 3) body[i].force.y -= 0.96 * body[i].mass * simulation.g const slowY = (body[i].velocity.y > 0) ? Math.max(0.3, 1 - 0.0015 * body[i].velocity.y * body[i].velocity.y) : Math.max(0.98, 1 - 0.001 * Math.abs(body[i].velocity.y)) //down : up Matter.Body.setVelocity(body[i], { x: Math.max(0.6, 1 - 0.07 * Math.abs(body[i].velocity.x)) * body[i].velocity.x, y: slowY * body[i].velocity.y }); if (body[i].mass < 0.05) { Matter.Composite.remove(engine.world, body[i]) body.splice(i, 1) break } } } for (let i = 0, len = mob.length; i < len; ++i) { if (mob[i].position.x > 700 && mob[i].position.x < 3200 && mob[i].alive && !mob[i].isShielded && mob[i].position.y > 1200) { mobs.statusDoT(mob[i], 0.005, 30) } } ctx.beginPath() ctx.fillStyle = "#666"; ctx.arc(buttonGreen.min.x - 50, buttonGreen.min.y - 70, 20, 0, 2 * Math.PI) ctx.fillRect(buttonGreen.min.x - 55, buttonGreen.max.y + 25, 10, -95) ctx.fill() ctx.beginPath() ctx.arc(buttonYellow.min.x - 50, buttonYellow.min.y - 70, 20, 0, 2 * Math.PI) ctx.fillRect(buttonYellow.min.x - 55, buttonYellow.max.y + 25, 10, -95) ctx.fill() ctx.beginPath() ctx.arc(buttonRed.min.x - 50, buttonRed.min.y - 70, 20, 0, 2 * Math.PI) ctx.fillRect(buttonRed.min.x - 55, buttonRed.max.y + 25, 10, -95) ctx.fill() ctx.beginPath() ctx.arc(buttonGreen.min.x - 50, buttonGreen.min.y - 70, 10, 0, 2 * Math.PI) ctx.fillStyle = (g ? `rgba(0, 255, 0, 0.9)` : `rgba(255, 0, 0, 0.9)`); ctx.fill() ctx.beginPath() ctx.arc(buttonYellow.min.x - 50, buttonYellow.min.y - 70, 10, 0, 2 * Math.PI) ctx.fillStyle = (y ? `rgba(0, 255, 0, 0.9)` : `rgba(255, 0, 0, 0.9)`); ctx.fill() ctx.beginPath() ctx.arc(buttonRed.min.x - 50, buttonRed.min.y - 70, 10, 0, 2 * Math.PI) ctx.fillStyle = (r ? `rgba(0, 255, 0, 0.9)` : `rgba(255, 0, 0, 0.9)`); ctx.fill() slimePit.query(); ctx.shadowColor = 'hsla(160, 100%, 50%, 1)' ctx.shadowBlur = 100; // slimePit.draw() ctx.shadowBlur = 0; ctx.shadowColor = 'rgba(0, 0, 0, 0)' deliveryButton.query() portal[2].query() //portal[3].query() portal2[2].query() //portal2[3].query() deliverySlime.level(deliveryButton.isUp) topSlime.level(!r) rotor.rotate() ctx.fillStyle = "#d4f4f4" ctx.fillRect(3500, 675, 600, 450) level.enter.draw() level.exit.drawAndCheck() } level.customTopLayer = () => { topSlime.query(); deliverySlime.query() deliverySlime2.query() drip1.draw() drip2.draw() drip3.draw() ctx.fillStyle = `rgba(68, 68, 68, ${Math.max(0.3, Math.min((4200 - m.pos.x) / 100, 0.99))})` ctx.fillRect(4100, 650, 850, 500) ctx.fillStyle = "rgba(0,20,40,0.1)" ctx.fillRect(4025, -1300, 475, 300) ctx.fillRect(3325, -1000, 375, 600) ctx.fillRect(425, 100, 3050, 2400) ctx.fillRect(-1775, 800, 1750, 2100) ctx.fillStyle = "rgba(0,20,40,0.2)" ctx.fillRect(2725, -860, 450, 460) ctx.fillRect(2125, -760, 450, 360) ctx.fillRect(1525, -660, 450, 260) ctx.fillRect(925, -560, 450, 160) ctx.fillRect(3700, -980, 100, 1200) ctx.fillStyle = `#444`; ctx.fillRect(465, 690, 70, 209) ctx.fillRect(3365, 690, 70, 209) ctx.beginPath() ctx.arc(500, 870, 20, 0, 2 * Math.PI) ctx.arc(500, 820, 20, 0, 2 * Math.PI) ctx.arc(500, 770, 20, 0, 2 * Math.PI) ctx.fillStyle = "rgba(0, 0, 0, 0.3"; ctx.fill() ctx.beginPath() ctx.arc(500, 870, 10, 0, 2 * Math.PI) ctx.fillStyle = (g ? `rgba(0, 255, 0, 0.9)` : `rgba(255, 0, 0, 0.9)`); ctx.fill() ctx.beginPath() ctx.arc(500, 820, 10, 0, 2 * Math.PI) ctx.fillStyle = (y ? `rgba(0, 255, 0, 0.9)` : `rgba(255, 0, 0, 0.9)`); ctx.fill() ctx.beginPath() ctx.arc(500, 770, 10, 0, 2 * Math.PI) ctx.fillStyle = (r ? `rgba(0, 255, 0, 0.9)` : `rgba(255, 0, 0, 0.9)`); ctx.fill() deliveryButton.draw() // deliverySlime.draw() // deliverySlime2.draw() // topSlime.draw() buttonGreen.draw() buttonYellow.draw() buttonRed.draw() portal[0].draw() portal[2].draw() portal2[0].draw() portal2[2].draw() } }, ngon() { //make by Oranger simulation.makeTextLog(`"ngon" by Oranger`); document.body.style.backgroundColor = "#dcdcde"; let needGravity = []; let s = { //mech statue x: -200, y: -2350, angle: 0, scale: 15, h: { //hip x: 12, y: 24 }, k: { //knee x: -30.96, //-17.38 y: 58.34, //70.49 //x2: -33.96, //x - 3 //y2: 58.34 //same as y }, f: { //foot x: 0, y: 91 //112 }, fillColor: "#ccc", //white fillColorDark: "#bbb", //25% from white lineColor: "#999", //#333 lineColorLight: "#aaa" //#4a4a4a } const boost1 = level.boost(2550, 1500, 1700) const boost2 = level.boost(-3400, -2050, 2100) level.custom = () => { boost1.query(); boost2.query(); level.exit.drawAndCheck(); level.enter.draw(); for (let i = 0; i < needGravity.length; i++) { needGravity[i].force.y += needGravity[i].mass * simulation.g; } ctx.fillStyle = "#444" //light fixtures ctx.fillRect(2350, 995, 40, 10) //ctx.fillRect(2280, -6005, 40, 10) //statue ctx.save(); ctx.translate(s.x, s.y); //statueLeg is at the bottom, below the enemies but above the NGON function statueLeg(-3, s.lineColorLight); statueLeg(0, s.lineColor); //head ctx.rotate(s.angle); ctx.beginPath(); ctx.arc(0, 0, 30 * s.scale, 0, 2 * Math.PI); let grd = ctx.createLinearGradient(-30 * s.scale, 0, 30 * s.scale, 0); grd.addColorStop(0, s.fillColorDark); grd.addColorStop(1, s.fillColor); ctx.fillStyle = grd; ctx.fill(); ctx.arc(15 * s.scale, 0, 4 * s.scale, 0, 2 * Math.PI); ctx.strokeStyle = s.lineColor; ctx.lineWidth = 2 * s.scale; ctx.stroke(); ctx.restore(); }; level.customTopLayer = () => { //boost chute for lack of a better name ctx.fillStyle = "rgba(60,60,60,0.9)"; ctx.fillRect(-3451, -4000, 202, 1500); ctx.fillRect(2499, -170, 202, 1170); ctx.fillStyle = "rgba(0,0,0,0.2)"; ctx.beginPath(); //basement ctx.moveTo(2360, 1000); ctx.lineTo(2120, 900); ctx.lineTo(1500, 900); ctx.lineTo(1500, 1500); ctx.lineTo(3000, 1500); ctx.lineTo(3000, 1000); ctx.lineTo(2380, 1000); ctx.lineTo(2870, 1500); ctx.lineTo(1870, 1500); ctx.lineTo(2360, 1000); ctx.fill(); // ctx.beginPath(); //exit // ctx.moveTo(1600, -6000); // ctx.lineTo(1600, -5000); // ctx.lineTo(3000, -5000); // ctx.lineTo(3000, -6000); // ctx.lineTo(2310, -6000); // ctx.lineTo(2600, -5000); // ctx.lineTo(2000, -5000); // ctx.lineTo(2290, -6000); // ctx.lineTo(1600, -6000); // ctx.fill(); ctx.fillStyle = "rgba(0,0,0,0.3)"; ctx.fillRect(1600, -1000, 1400, 830); ctx.fillRect(1600, -170, 520, 170); ctx.fillRect(-1300, -200, 2200, 200); //statue base ctx.fillRect(-800, -400, 1200, 200); ctx.fillRect(-500, -700, 600, 300); //ctx.fillRect(-4000, -6000, 2000, 1000); //left side ctx.fillRect(-4000, -2500, 2000, 2500); }; level.setPosToSpawn(1810, 1450); spawn.mapRect(level.enter.x, level.enter.y + 20, 100, 20); level.exit.x = 2700; level.exit.y = -4030; spawn.mapRect(level.exit.x, level.exit.y + 20, 100, 20); level.defaultZoom = 3500 simulation.zoomTransition(level.defaultZoom) // powerUps.spawnStartingPowerUps(1475, -1175); spawn.debris(2750, 1500, 200, 4); //16 debris per level spawn.debris(1770, -350, 120, 4); //16 debris per level spawn.debris(-3200, 0, 1000, 6); //16 debris per level //boundaries spawn.mapRect(-4100, 1500, 7200, 100); //base floor spawn.mapRect(3000, -4000, 100, 5600); //right barrier spawn.mapRect(-4100, -4000, 100, 5600); //left barrier //spawn.mapRect(1600, -10000, 1500, 4000); //upper right wall //spawn.mapRect(-4100, -10000, 2100, 4000); //upper left wall spawn.mapRect(1600, -4000, 1500, 3000); //right wall spawn.mapRect(-4100, 0, 5600, 1550); //floor //starting room spawn.mapRect(1500, 0, 700, 900); spawn.mapRect(2120, -170, 380, 1170); spawn.mapRect(2700, -170, 400, 1170); //spawn.mapVertex(2296, 400, "0 0 0 1200 300 1200 400 0"); //spawn.mapVertex(2904, 400, "0 0 0 1200 -300 1200 -400 0"); //left area spawn.mapRect(-3500, -300, 300, 400); //floor 1 spawn.mapRect(-3900, -600, 300, 100); spawn.mapRect(-3500, -900, 300, 100); spawn.mapRect(-3100, -1150, 1000, 150); //floor 2 spawn.mapRect(-2200, -2600, 200, 1600); spawn.mapRect(-2700, -1450, 300, 100); spawn.mapRect(-3100, -1750, 300, 100); spawn.mapRect(-3500, -2050, 300, 100); spawn.mapRect(-4100, -4000, 650, 1500); //floor 3 spawn.mapRect(-3250, -4000, 1250, 1500); //statue base spawn.mapRect(-700, -900, 1000, 200); //top //left spawn.mapRect(-700, -900, 200, 500); spawn.mapRect(-1000, -600, 500, 200); spawn.mapRect(-1000, -600, 200, 400); spawn.mapRect(-1300, -300, 500, 100); //right spawn.mapRect(100, -900, 200, 500); spawn.mapRect(100, -600, 500, 200); spawn.mapRect(400, -600, 200, 400); spawn.mapRect(400, -300, 500, 100); hangingNGON(-1900, -4000, 1, 1000, 1, false, { density: 0.001, //default density is 0.001 friction: 0.0001, frictionAir: 0.001, frictionStatic: 0, restitution: 0, isNotHoldable: true }); hangingNGON(1900, -4600, 0.2, 300, 0.0005, false, { density: 0.00005, //default density is 0.001 friction: 0.0001, frictionAir: 0.003, frictionStatic: 0, restitution: 1, isNotHoldable: true }); // // Never gonna give you up // spawn.bodyRect(-8000, -10100, 15, 100); // // Never gonna let you down // spawn.bodyRect(-7915, -10100, 15, 100); // // Never gonna run around and desert you // body[body.length] = Bodies.polygon(-7950, -10025, 0, 25, { //circle // friction: 0.05, // frictionAir: 0.001 // }); // // Never gonna make you cry // spawn.bodyRect(6985, -10100, 15, 100); // // Never gonna say goodbye // spawn.bodyRect(6900, -10100, 15, 100); // // Never gonna tell a lie and hurt you // body[body.length] = Bodies.polygon(6950, -10025, 0, 25, { //circle // friction: 0.05, // frictionAir: 0.001 // }); //pile of blocks spawn.bodyRect(1920, -400, 200, 400) spawn.bodyRect(1720, -250, 200, 250) spawn.bodyRect(1770, -300, 150, 50) spawn.bodyRect(2120, -280, 100, 100) spawn.bodyRect(1990, -500, 100, 100) //doors under statue spawn.bodyRect(850, -50, 50, 50) spawn.bodyRect(850, -100, 50, 50) spawn.bodyRect(850, -150, 50, 50) spawn.bodyRect(850, -200, 50, 50) spawn.bodyRect(-1300, -50, 50, 50) spawn.bodyRect(-1300, -100, 50, 50) spawn.bodyRect(-1300, -150, 50, 50) spawn.bodyRect(-1300, -200, 50, 50) // on the statue base spawn.randomMob(700 + Math.random() * 100, -500 + Math.random() * 100, 1); spawn.randomMob(400 + Math.random() * 100, -800 + Math.random() * 100, 0.4); spawn.randomMob(100 + Math.random() * 100, -1100 + Math.random() * 100, -0.2); spawn.randomGroup(-200, -1400, -0.4); spawn.randomMob(-600 + Math.random() * 100, -1100 + Math.random() * 100, -0.2); spawn.randomMob(-900 + Math.random() * 100, -800 + Math.random() * 100, 0.4); spawn.randomMob(-1200 + Math.random() * 100, -500 + Math.random() * 100, 1); //in the statue base spawn.randomSmallMob(400 + Math.random() * 300, -150 + Math.random() * 100, 0.2); spawn.randomSmallMob(-1100 + Math.random() * 300, -150 + Math.random() * 100, 0.2); //bottom left spawn.randomMob(-2600 + Math.random() * 300, -700 + Math.random() * 300, 0.6); spawn.randomSmallMob(-3000 + Math.random() * 300, -400 + Math.random() * 300, 0.2); spawn.randomSmallMob(-3000 + Math.random() * 300, -400 + Math.random() * 300, 0); spawn.randomMob(-3900 + Math.random() * 100, -200 + Math.random() * 100, 0.6); spawn.randomMob(-3400 + Math.random() * 100, -400, 0.4); spawn.randomSmallMob(-3800 + Math.random() * 100, -700, -0.4); spawn.randomMob(-3400 + Math.random() * 100, -1000, 0.6); spawn.randomMob(-3000 + Math.random() * 100, -1850, 0); spawn.randomGroup(-2700, -2000, 0.4); //top left spawn.randomSmallMob(-3800, -5800, -0.2); spawn.randomSmallMob(-2400, -5200, 0.2); //top right spawn.randomGroup(2000, -5700, 0.6); powerUps.addResearchToLevel() //needs to run after mobs are spawned let bosses = ["shooterBoss", "launcherBoss", "laserTargetingBoss", "streamBoss", "pulsarBoss", "spawnerBossCulture", "laserBoss", "growBossCulture"]; let abc = Math.random(); if (simulation.difficulty > 3) { if (abc < 0.6) { spawn.randomLevelBoss(-1500 + Math.random() * 250, -1100 + Math.random() * 200, bosses); } else if (abc < 0.85) { spawn.laserBoss(-350 + Math.random() * 300, -600 + Math.random() * 200); } else { spawn.randomLevelBoss(850 + Math.random() * 250, -1100 + Math.random() * 200, bosses); } } spawn.secondaryBossChance(850 + Math.random() * 250, -1100 + Math.random() * 200) //draw leg for statue function statueLeg(shift, color) { ctx.save(); ctx.translate(shift, shift); //front leg let stroke = color; ctx.beginPath(); ctx.moveTo((s.h.x + shift) * s.scale, (s.h.y + shift) * s.scale); ctx.lineTo((s.k.x + 2 * shift) * s.scale, (s.k.y + shift) * s.scale); ctx.lineTo((s.f.x + shift) * s.scale, (s.f.y + shift) * s.scale); ctx.strokeStyle = stroke; ctx.lineWidth = 7 * s.scale; ctx.stroke(); //toe lines ctx.beginPath(); ctx.moveTo((s.f.x + shift) * s.scale, (s.f.y + shift) * s.scale); ctx.lineTo((s.f.x - 15 + shift) * s.scale, (s.f.y + 5 + shift) * s.scale); ctx.moveTo((s.f.x + shift) * s.scale, (s.f.y + shift) * s.scale); ctx.lineTo((s.f.x + 15 + shift) * s.scale, (s.f.y + 5 + shift) * s.scale); ctx.lineWidth = 4 * s.scale; ctx.stroke(); //hip joint ctx.beginPath(); ctx.arc((s.h.x + shift) * s.scale, (s.h.y + shift) * s.scale, 11 * s.scale, 0, 2 * Math.PI); //knee joint ctx.moveTo((s.k.x + 7 + 2 * shift) * s.scale, (s.k.y + shift) * s.scale); ctx.arc((s.k.x + 2 * shift) * s.scale, (s.k.y + shift) * s.scale, 7 * s.scale, 0, 2 * Math.PI); //foot joint ctx.moveTo((s.f.x + 6 + shift) * s.scale, (s.f.y + shift) * s.scale); ctx.arc((s.f.x + shift) * s.scale, (s.f.y + shift) * s.scale, 6 * s.scale, 0, 2 * Math.PI); ctx.fillStyle = s.fillColor; ctx.fill(); ctx.lineWidth = 2 * s.scale; ctx.stroke(); ctx.restore(); } // | | | | | // n - g o n //when s = 1 (scale), it's 3408 long and 800 tall (height of g) function hangingNGON(x, y, s, height, stiffness, pin, properties) { //makes a compound part of 3 bodyVertex parts function compound3Parts(x1, y1, v1, x2, y2, v2, x3, y3, v3, properties) { const part1 = Matter.Bodies.fromVertices(x1, y1, Vertices.fromPath(v1), properties); const part2 = Matter.Bodies.fromVertices(x2, y2, Vertices.fromPath(v2), properties); const part3 = Matter.Bodies.fromVertices(x3, y3, Vertices.fromPath(v3), properties); const compoundParts = Body.create({ parts: [part1, part2, part3], }); Composite.add(engine.world, [compoundParts]); needGravity[needGravity.length] = compoundParts; composite[composite.length] = compoundParts; body[body.length] = part1; body[body.length] = part2; body[body.length] = part3; setTimeout(function () { compoundParts.collisionFilter.category = cat.body; compoundParts.collisionFilter.mask = cat.body | cat.player | cat.bullet | cat.mob | cat.mobBullet | cat.map }, 1000); } //for attaching the block to a point function addConstraint(x, y, px, py, stiff, body, pin = false) { if (pin) { map[map.length] = Bodies.polygon(x, y, 0, 15); //circle above } cons[cons.length] = Constraint.create({ pointA: { x: x, y: y }, bodyB: body, pointB: { x: px, y: py }, stiffness: stiff }); Composite.add(engine.world, cons[cons.length - 1]); } //I SINCERELY APOLOGIZE FOR THE ILLEGIBLE BLOCKS OF STRING CONCATENATION //s is scale //n compound3Parts( x + 100 * s, y + 310 * s, ("0 0 " + 200 * s + " 0 " + 200 * s + " " + 620 * s + " 0 " + 620 * s), x + 300 * s, y + 160 * s, (200 * s + " " + 40 * s + " " + 400 * s + " " + 40 * s + " " + 400 * s + " " + 280 * s + " " + 200 * s + " " + 280 * s), x + 499 * s, y + 333.3 * s, (400 * s + " " + 40 * s + " " + 540 * s + " " + 40 * s + " " + 600 * s + " " + 100 * s + " " + 600 * s + " " + 620 * s + " " + 400 * s + " " + 620 * s + " " + 400 * s + " " + 280 * s), properties ); addConstraint(x + 300 * s, y - height, 0, -10 * s, stiffness, composite[composite.length - 1], pin); //- spawn.bodyRect(x + 800 * s, y + 250 * s, 200 * s, 100 * s, 1, properties); body[body.length - 1].frictionAir = 0.05 //friction to make jump easier addConstraint(x + 900 * s, y - height, 0, -30 * s, stiffness, body[body.length - 1], pin); //g compound3Parts( x + 1400 * s, y + 300 * s, ("0 0 " + 250 * s + " 0 " + 425 * s + " " + 175 * s + " " + 425 * s + " " + 450 * s + " " + 275 * s + " " + 600 * s + " 0 " + 600 * s + " " + -175 * s + " " + 425 * s + " " + -175 * s + " " + 175 * s), x + 1636 * s, y + 633 * s, (425 * s + " " + 450 * s + " " + 425 * s + " " + 750 * s + " " + 375 * s + " " + 800 * s + " " + 275 * s + " " + 675 * s + " " + 275 * s + " " + 600 * s), x + 1398 * s, y + 737 * s, (375 * s + " " + 800 * s + " " + -75 * s + " " + 800 * s + " " + -75 * s + " " + 675 * s + " " + 275 * s + " " + 675 * s), properties ); addConstraint(x + 1500 * s, y - height, 0, -15 * s, stiffness, composite[composite.length - 1], pin); //o spawn.bodyVertex( x + 2300 * s, y + 300 * s, ("0 0 " + 250 * s + " 0 " + 425 * s + " " + 175 * s + " " + 425 * s + " " + 425 * s + " " + 250 * s + " " + 600 * s + " 0 " + 600 * s + " " + -175 * s + " " + 425 * s + " " + -175 * s + " " + 175 * s), properties ); addConstraint(x + 2300 * s, y - height, 0, -10 * s, stiffness, body[body.length - 1], pin); //n compound3Parts( x + 2900 * s, y + 310 * s, ("0 0 " + 200 * s + " 0 " + 200 * s + " " + 620 * s + " 0 " + 620 * s), x + 3100 * s, y + 160 * s, (200 * s + " " + 40 * s + " " + 400 * s + " " + 40 * s + " " + 400 * s + " " + 280 * s + " " + 200 * s + " " + 280 * s), x + 3300 * s, y + 333.3 * s, (400 * s + " " + 40 * s + " " + 540 * s + " " + 40 * s + " " + 600 * s + " " + 100 * s + " " + 600 * s + " " + 620 * s + " " + 400 * s + " " + 620 * s + " " + 400 * s + " " + 280 * s), properties ); addConstraint(x + 3100 * s, y - height, 0, -10 * s, stiffness, composite[composite.length - 1], pin); } }, tunnel() { // by Scarlettt simulation.makeTextLog(`tunnel by Scarlettt`); level.custom = () => { level.exit.drawAndCheck(); //enter 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 = "#013"; ctx.fill(); //exit 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 = "#9ff"; ctx.fill(); // hiding rooms in path to second floor ctx.fillStyle = "#322"; ctx.fillRect(3750, -1650, 3500, 350); // prevent the user from getting into the secreter room without defeating all mobs if (m.pos.x > 1500 && m.pos.x < 2500 && m.pos.y > -4000 && m.pos.y < -3500 && mob.reduce((a, i) => { return a || ((Math.sqrt((i.position.x - 3600) * (i.position.x - 3600) + (i.position.y + 3600) * (i.position.y + 3600)) < 20000) && i.isDropPowerUp); }, false) && !emergencyActivated) { Matter.Body.setPosition(player, { x: 2800, y: m.pos.y }); } button.query(); isButtonTapped = isButtonTapped || !button.isUp; hazard.level(!isButtonTapped); if (Matter.Query.region([player], hazard).length) m.energy -= 0.001; buttonSec.query(); buttonSec.draw(); if (!buttonSec.isUp && !hasSecretButton) { for (var i = 0; i < 5; i++) { powerUps.spawn(3614, -3700, "ammo"); } hasSecretButton = true; } buttonThird.query(); buttonThird.draw(); if (!buttonThird.isUp && !hasSecretButton2) { for (var i = 0; i < 1; i++) powerUps.spawn(1614, -3700, "research"); hasSecretButton2 = true; } if (!buttonSec.isUp) { secretAnimTrans += 2; secretAnimTime = 1; secretAnimTrans = Math.max(0, Math.min(secretAnimTrans, 60)); } else { secretAnimTrans--; if (secretAnimTime) secretAnimTrans += 3; secretAnimTrans = Math.min(60, Math.max(secretAnimTrans, 0)); } if (secretAnimTime > 0) { secretAnimTime++; if (secretAnimTime > 150) secretAnimTime = 0; } if (emergencyActivated || !buttonThird.isUp) { secretAnimTrans2 += 2; secretAnimTime2 = 1; secretAnimTrans2 = Math.max(0, Math.min(secretAnimTrans2, 60)); } else { secretAnimTrans2--; if (secretAnimTime2) secretAnimTrans2 += 3; secretAnimTrans2 = Math.min(60, Math.max(secretAnimTrans2, 0)); } if (secretAnimTime2 > 0) { secretAnimTime2++; if (secretAnimTime2 > 150) secretAnimTime2 = 0; } ctx.beginPath(); ctx.arc(m.pos.x, m.pos.y, 200, 0, 2 * Math.PI); ctx.fillStyle = "#ff25"; ctx.fill(); ctx.beginPath(); ctx.arc(m.pos.x, m.pos.y, 400, 0, 2 * Math.PI); ctx.fillStyle = "#ff22"; ctx.fill(); ctx.beginPath(); ctx.arc(m.pos.x, m.pos.y, 700, 0, 2 * Math.PI); ctx.fillStyle = "#ff21"; ctx.fill(); elevator.move(); elevator.drawTrack(); }; level.customTopLayer = () => { hazard.query(); secretHazard.level(emergencyActivated); secretHazard.query(); button.draw(); // Fire damage let isInRange = flames.reduce((a, i) => a || Math.sqrt((m.pos.x - i[0]) * (m.pos.x - i[0]) + (m.pos.y + 90 - i[1]) * (m.pos.y + 90 - i[1])) < 50, false); if (isInRange) { fireDmgLevel++; fireDmgLevel = Math.min(fireDmgLevel, 100); } else { fireDmgLevel--; fireDmgLevel = Math.max(fireDmgLevel, -8); } if (fireDmgLevel > -8) { ctx.fillStyle = "#fa0b"; ctx.fillRect(m.pos.x - 50, m.pos.y - 100, Math.min(fireDmgLevel * 12.5 + 100, 100), 15); } if (fireDmgLevel > 0) { ctx.fillStyle = "#f00c"; ctx.fillRect(m.pos.x - 50, m.pos.y - 100, fireDmgLevel, 15); m.damage(0.001 * (1.5 * isInRange + 1)); drawFlame(m.pos.x, m.pos.y + 90, "#d40", Math.PI / 2 + 1); drawFlame(m.pos.x, m.pos.y + 90, "#d40", Math.PI / 2 + 1); drawFlame(m.pos.x, m.pos.y + 90, "#d40", Math.PI / 2 + 1); drawFlame(m.pos.x, m.pos.y + 90, "#d40", Math.PI / 2 - 1); drawFlame(m.pos.x, m.pos.y + 90, "#d40", Math.PI / 2 - 1); drawFlame(m.pos.x, m.pos.y + 90, "#d40", Math.PI / 2 - 1); drawFlame(m.pos.x, m.pos.y + 90, "#f70", Math.PI / 2); drawFlame(m.pos.x, m.pos.y + 90, "#f70", Math.PI / 2); drawFlame(m.pos.x, m.pos.y + 90, "#f70", Math.PI / 2); } for (let j = 0; j < 5; j++) { drawFlame(1130 + j * 10, -1700) for (let i = 0; i < 7; i++) drawFlame(2550 + i * 200, -2800); for (let i = 0; i < 10; i++) drawFlame(2800 + i * 500, -1650); for (let i = 0; i < 9; i++) drawFlame(1595 + i * 95, -3860); drawFlame(4850, -1300); drawFlame(6350, -1300); } ctx.fillStyle = "#541"; for (let i = 0; i < 9; i++) { ctx.fillRect(1592 + i * 95, -3860, 6, 70); } if (m.pos.x > 1500 && m.pos.x < 3750 && m.pos.y > -5000 && m.pos.y < -3300) { secretRoomTrans -= 5; secretRoomTrans = Math.max(secretRoomTrans, 85); } else { secretRoomTrans += 5; secretRoomTrans = Math.min(secretRoomTrans, 250); } let hasMob = mob.reduce((a, i) => { return a || ((Math.sqrt((i.position.x - 3600) * (i.position.x - 3600) + (i.position.y + 3600) * (i.position.y + 3600)) < 20000) && i.isDropPowerUp); }, false) && !emergencyActivated; door.isClosing = hasMob; door.openClose(); ctx.fillStyle = "#444444" + secretRoomTrans.toString(16); ctx.fillRect(1480, -5000, 2270, 1710); if (hasMob) { ctx.fillStyle = "#444"; ctx.fillRect(1480, -5000, 1070, 1710); } if (secretAnimTrans > 0) { drawProject([3614, -3530], [2900, -3900], [3400, -3600], secretAnimTrans, 60); if (secretAnimTrans >= 42) { ctx.font = "27px monospace"; ctx.textAlign = "start" ctx.fillStyle = "#00ffff" + Math.floor((secretAnimTrans - 40) * 12.75).toString(16); ctx.fillText("Waste Discharge Interruption:", 2910, -3870); ctx.fillText("Owner 'Scarlet' not found", 2910, -3830); ctx.fillText("Detected user: 'm'", 2910, -3790); ctx.fillStyle = (hasMob ? "#ff6644" : "#ffff44") + Math.floor((secretAnimTrans - 40) * 12.75).toString(16); ctx.fillText(hasMob ? "AREA HAS MOBS." : "Area clear.", 2910, -3710); ctx.fillText(hasMob ? "'openDoor' failed." : "'openDoor' complete.", 2910, -3670); ctx.strokeStyle = "#00ff00" + Math.floor((secretAnimTrans - 40) * 6).toString(16); ctx.beginPath(); ctx.arc(3300, -3730, 60, 0, 2 * Math.PI); ctx.stroke(); ctx.arc(3330, -3730, 8, 0, 2 * Math.PI); ctx.lineWidth = 4; ctx.stroke(); ctx.textAlign = "center"; ctx.fillStyle = "#00ffff" + Math.floor((secretAnimTrans - 40) * 12.75).toString(16); ctx.font = "30px monospace"; ctx.fillText("n-gon inc", 3300, -3630); ctx.font = "25px Arial"; } } if (secretAnimTrans2 > 0) { drawProject([1614, -3530], [2050, -3900], [1550, -3600], secretAnimTrans2, 60); if (secretAnimTrans2 >= 42) { ctx.font = "27px monospace"; ctx.textAlign = "start"; ctx.fillStyle = "#00ffff" + Math.floor((secretAnimTrans2 - 40) * 12.75).toString(16); ctx.fillText("SECURITY BREACH DETECTED", 1560, -3870); ctx.fillText("Entity name: m", 1560, -3830); ctx.fillStyle = (tech.totalCount < 25 ? (tech.totalCount < 10 ? "#ffff44" : "#22ff22") : "#ff6644") + Math.floor((secretAnimTrans2 - 40) * 12.75).toString(16); ctx.fillText("Threat level: " + (tech.totalCount < 25 ? (tech.totalCount < 10 ? "Low" : "Medium") : "HIGH"), 1560, -3790); if (tech.totalCount >= 15) ctx.fillText("PROCEDURE ACTIVATED", 1560, -3750); ctx.strokeStyle = "#00ff00" + Math.floor((secretAnimTrans2 - 40) * 6).toString(16); ctx.beginPath(); ctx.arc(1950, -3730, 60, 0, 2 * Math.PI); ctx.stroke(); ctx.arc(1980, -3730, 8, 0, 2 * Math.PI); ctx.lineWidth = 4; ctx.stroke(); ctx.textAlign = "center"; ctx.fillStyle = "#00ffff" + Math.floor((secretAnimTrans2 - 40) * 12.75).toString(16); ctx.font = "30px monospace"; ctx.fillText("n-gon inc", 1950, -3630); ctx.font = "25px Arial"; if (secretAnimTrans2 >= 60) { if (!emergencyActivated && tech.totalCount >= 10) { for (let i = 0; i < 5; i++) { spawn.exploder(1614, -3900); if (tech.totalCount >= 25) spawn.randomMob(1614, -3900, Infinity); } emergencyActivated = true; } } } } }; level.setPosToSpawn(0, -50); //normal spawn level.exit.x = 8500; level.exit.y = 680; level.defaultZoom = 1800 simulation.zoomTransition(level.defaultZoom) document.body.style.backgroundColor = "#123"; // powerUps.spawnStartingPowerUps(1475, -1175); // spawn.debris(750, -2200, 3700, 16); //16 debris per level // spawn blocks spawn.mapRect(-100, 0, 1050, 100); spawn.mapRect(900, -300, 50, 300); spawn.mapRect(700, -300, 50, 200); // first room spawn.mapRect(-100, -350, 850, 50); spawn.mapRect(900, -350, 850, 50); spawn.mapRect(-100, -1550, 50, 1200); spawn.mapRect(1700, -1550, 50, 1200); spawn.mapRect(-100, -1550, 850, 50); spawn.mapRect(900, -1550, 850, 50); spawn.bodyRect(700, -400, 50, 50); spawn.bodyRect(900, -400, 50, 50); spawn.mapRect(500, -650, 650, 25); spawn.mapRect(200, -1000, 200, 25); spawn.mapRect(1250, -1000, 200, 25); spawn.mapRect(600, -1300, 450, 25); spawn.mapRect(700, -1650, 50, 100); spawn.mapRect(900, -1650, 50, 100); // pathway to second room spawn.mapRect(950, -1650, 3050, 50); spawn.mapRect(1100, -1700, 100, 50); // second room spawn.mapRect(0, -5000, 1500, 3000); spawn.mapRect(1500, -2050, 300, 50); spawn.mapRect(2000, -3100, 300, 1100); spawn.mapRect(1500, -5000, 2250, 1000); spawn.mapRect(1500, -3500, 1050, 225); spawn.mapRect(4000, -5000, 500, 3000); spawn.mapRect(3748, -5000, 252, 1550); spawn.mapRect(1700, -2400, 300, 50); spawn.mapRect(1500, -2750, 300, 50); spawn.mapRect(2300, -3000, 1700, 50); spawn.mapRect(2300, -2800, 1700, 800); spawn.mapRect(2450, -3300, 1300, 100); // secret room in second room spawn.mapRect(2700, -3500, 1050, 50); spawn.mapRect(2549, -5000, 1201, 1000); const buttonSec = level.button(3550, -3500); const buttonThird = level.button(1550, -3500); let hasSecretButton = false, hasSecretButton2 = false, secretAnimTrans = 0, secretAnimTime = 0, secretAnimTrans2 = 0, secretAnimTime2 = 0; let emergencyActivated = false; const door = level.door(2450, -4000, 100, 500, 490); const secretHazard = level.hazard(1500, -4000, 1000, 510, 0.01); // hazards const button = level.button(3800, -3000); const hazard = level.hazard(2300, -3090, 1700, 110, 0.005); let isButtonTapped = false; // if (b.inventory.length < 5) powerUps.spawn(3800, -3200, "gun"); powerUps.spawn(3900, -3100, "heal", true, null, 30); powerUps.spawn(3900, -3100, "heal", true, null, 30); // path to the third room spawn.mapRect(2000, -1850, 50, 200); spawn.mapRect(2200, -2000, 50, 200); spawn.mapRect(2400, -1850, 50, 200); spawn.mapRect(4200, -1650, 1300, 50); spawn.mapRect(5700, -1650, 1300, 50); spawn.mapRect(7200, -1650, 750, 50); spawn.mapRect(3700, -1600, 50, 350); spawn.mapRect(7250, -1600, 50, 350); spawn.mapRect(3750, -1300, 3500, 50); spawn.mapRect(4500, -2150, 3550, 50) // third room spawn.mapRect(7900, -1600, 50, 1000); spawn.mapRect(8050, -3000, 50, 2400); spawn.mapRect(7000, -600, 950, 50); spawn.mapRect(8050, -600, 950, 50); spawn.mapRect(7000, -600, 50, 1000); spawn.mapRect(8950, -600, 50, 1000); spawn.mapRect(7000, 400, 950, 50); spawn.mapRect(8050, 400, 950, 50); spawn.mapRect(7900, 400, 50, 300); spawn.mapRect(7900, 700, 1000, 50); const elevator = level.elevator(7962.5, 500, 75, 50, -1800) // fire damage const flames = []; flames.push([1150, -1700], [1150, -1770]); for (let i = 0; i < 10; i++) flames.push([2800 + i * 500, -1650], [2800 + i * 500, -1720]); flames.push([4850, -1300], [6350, -1300], [4850, -1370], [6350, -1370]); let fireDmgLevel = -8; let secretRoomTrans = 250; // mobs let mobList1 = [ [500, -750], [1150, -750], [825, -1100], [300, -1100], [1350, -1100] ]; while (mobList1.length > 5 - Math.sqrt(simulation.difficulty * 2.5) && mobList1.length) { let rand = Math.floor(Math.random() * mobList1.length); spawn[["hopper", "sneaker", "striker"][Math.floor(Math.random() * 3)]](mobList1[rand][0], mobList1[rand][1], 60 + Math.random() * 10); mobList1.splice(rand, 1); } let hasLaser = spawn.pickList.includes("laser"); if (hasLaser) spawn.pickList.splice(spawn.pickList.indexOf("laser"), 1); let mobList2 = [ [50, -1400], [1600, -450], [50, -450], [1600, -1400] ]; for (let i = 0; i < 10; i++) mobList2.push([2800 + i * 500, -1800]); while (mobList2.length && mob.length < -1 + 16 * Math.log10(simulation.difficulty + 1)) { let rand = Math.floor(Math.random() * mobList2.length); spawn.randomMob(...mobList2[rand]); mobList2.splice(rand, 1); } let groupList = ["spawn.randomGroup(8250, 575);", `spawn.randomGroup(3200, -3700); if (simulation.difficulty > 15) spawn.randomGroup(3500, -3700, 0.3);`, "spawn.randomGroup(7800, -1800, 0.5);" ]; while (groupList.length > 0) { let ind = Math.floor(Math.random() * groupList.length); Function(groupList[ind])(); groupList.splice(ind, 1); } if (hasLaser) spawn.pickList.push("laser"); spawn.shieldingBoss(3900, -3200, 70); let randomBoss = Math.floor(Math.random() * 2); if (simulation.difficulty > 5) spawn[["shooterBoss", "launcherBoss"][randomBoss]](7500, -150, 100, false); else spawn[["shooter", "launcher"][randomBoss]](7500, -150, 150); spawn[["shooter", "launcher"][randomBoss]](8500, -150, 150); // canvas stuff function drawFlame(x, y, color = "#f81", angle = Math.PI / 2) { ctx.beginPath(); ctx.moveTo(x, y); ctx.strokeStyle = color; ctx.lineWidth = 3; for (let i = 0; i < 3; i++) { let randAng = (Math.random() - 0.5) * 2 + angle; randLen = 30 + Math.random() * 10; x = x + Math.cos(randAng) * randLen; y = y - Math.sin(randAng) * randLen; ctx.lineTo(x, y); } ctx.stroke(); } function drawProject(startPos, endPos1, endPos2, tValue, tValueM) { ctx.strokeStyle = "#003"; ctx.fillStyle = "#0055aa" + ('0' + (tValue * 60 / tValueM).toString(16)).slice(-2); let inter = (tValueM - tValue) / tValueM; let endpos1i = endPos1.map((i, j) => (startPos[j] - i) * inter), endpos2i = endPos2.map((i, j) => (startPos[j] - i) * inter); ctx.beginPath(); ctx.moveTo(endPos1[0] + endpos1i[0], endPos1[1] + endpos1i[1]); ctx.lineTo(...startPos); ctx.lineTo(endPos2[0] + endpos2i[0], endPos1[1] + endpos1i[1]); ctx.fill(); ctx.stroke(); ctx.beginPath(); ctx.moveTo(endPos1[0] + endpos1i[0], endPos1[1] + endpos1i[1]); ctx.lineTo(...startPos); ctx.lineTo(endPos1[0] + endpos1i[0], endPos2[1] + endpos2i[1]); ctx.fill(); ctx.stroke(); ctx.beginPath(); ctx.moveTo(endPos1[0] + endpos1i[0], endPos2[1] + endpos2i[1]); ctx.lineTo(...startPos); ctx.lineTo(endPos2[0] + endpos2i[0], endPos2[1] + endpos2i[1]); ctx.fill(); ctx.stroke(); ctx.beginPath(); ctx.moveTo(endPos2[0] + endpos2i[0], endPos2[1] + endpos2i[1]); ctx.lineTo(...startPos); ctx.lineTo(endPos2[0] + endpos2i[0], endPos1[1] + endpos1i[1]); ctx.fill(); ctx.stroke(); if (tValue >= tValueM * 2 / 3) { ctx.fillStyle = "#0055aa" + ('0' + Math.floor((tValue - tValueM * 2 / 3) * 6.25 * 60 / tValueM).toString(16)).slice(-2); ctx.strokeStyle = "#000033" + ('0' + Math.floor((tValue - tValueM * 2 / 3) * 12.75 * 60 / tValueM).toString(16)).slice(-2); ctx.fillRect(endPos1[0], endPos1[1], endPos2[0] - endPos1[0], endPos2[1] - endPos1[1]); ctx.shadowColor = "#00aaaa" + ('0' + Math.floor((tValue - tValueM * 2 / 3) * 12.75 * 60 / tValueM).toString(16)).slice(-2); ctx.shadowBlur = 10; ctx.strokeRect(endPos1[0], endPos1[1], endPos2[0] - endPos1[0], endPos2[1] - endPos1[1]); ctx.shadowBlur = 0; ctx.shadowColor = "#0000"; } } }, run() { simulation.makeTextLog(`run by iNoobBoi`); addPartToMap = (len) => { //adds new map elements to the level while the level is already running //don't forget to run simulation.draw.setPaths() after you all the the elements so they show up visually map[len].collisionFilter.category = cat.map; map[len].collisionFilter.mask = cat.player | cat.map | cat.body | cat.bullet | cat.powerUp | cat.mob | cat.mobBullet; Matter.Body.setStatic(map[len], true); //make static Composite.add(engine.world, map[len]); } anotherBoss = (x, y) => { if (tech.isDuplicateMobs && Math.random() < tech.duplicationChance()) { tech.isScaleMobsWithDuplication = true spawn.randomLevelBoss(x, y, ["historyBoss"]); tech.isScaleMobsWithDuplication = false } else if (tech.isResearchBoss) { if (powerUps.research.count > 2) { powerUps.research.changeRerolls(-3) simulation.makeTextLog(`m.research -= 3
${powerUps.research.count}`) } else { tech.addJunkTechToPool(0.49) } spawn.randomLevelBoss(x, y, ["historyBoss"]); } } const climbPad = level.boost(8200, -200, 500); var climbTime = false; var climbGroup = 0; var initialSpawn = false; var endTime = false; let runMobList = [ "hopper", "slasher", "striker", "stabber", "springer", "pulsar", "sneaker", "spinner", "grower", "focuser", "spawner", ]; let removeList = []; level.custom = () => { level.exit.drawAndCheck(); level.enter.draw(); climbPad.query(); if (m.pos.x > 8000 && climbTime === false) { spawn.mapRect(7800, -900, 200, 900); addPartToMap(map.length - 1); simulation.draw.setPaths(); simulation.makeTextLog(`UNKNOWN: "Well done. Now climb."`, 600); simulation.makeTextLog(`UNKNOWN: "I left a gift at the top."`, 600); climbTime = true; } //toggles on a mapRect when player passes a certain area if (m.pos.x > 9000 && endTime === false) { simulation.makeTextLog("UNKNOWN: \"Good luck. I hope you get out of here.\"", 600); endTime = true; } for (i in mob) { mob[i].damageReduction = 0; Matter.Body.setVelocity(mob[i], { x: mob[i].velocity.x * 0.97, y: mob[i].velocity.y * 0.97 }); } //makes everything slow and immune }; level.customTopLayer = () => { ctx.fillStyle = "#888"; if (climbGroup === 0) { //toggle on fillRect: 1 ctx.fillRect(8000, -900, 300, 100); ctx.fillRect(8500, -1800, 300, 100); ctx.fillRect(8300, -2700, 300, 100); ctx.fillRect(8000, -3600, 300, 100); ctx.fillRect(8200, -4500, 300, 100); } else if (climbGroup === 1) { //toggle on fillRect: 2 ctx.fillRect(8300, -1200, 300, 100); ctx.fillRect(8500, -2100, 300, 100); ctx.fillRect(8100, -3000, 300, 100); ctx.fillRect(8000, -3900, 300, 100); ctx.fillRect(8200, -4800, 300, 100); } else if (climbGroup === 2) { //toggle on fillRect: 0 ctx.fillRect(8500, -600, 300, 100); ctx.fillRect(8100, -1500, 300, 100); ctx.fillRect(8000, -2400, 300, 100); ctx.fillRect(8500, -3300, 300, 100); ctx.fillRect(8500, -4200, 300, 100); } if ((simulation.cycle % 120) === 0) { for (var i = 0; i < map.length; i++) { if (map[i].isRemove) { Matter.Composite.remove(engine.world, map[i]); map.splice(i, 1); } } if (climbGroup === 0) { //toggle on platforms: 0 spawn.mapRect(8000, -900, 300, 100); addPartToMap(map.length - 1); map[map.length - 1].isRemove = true; spawn.mapRect(8500, -1800, 300, 100); addPartToMap(map.length - 1); map[map.length - 1].isRemove = true; spawn.mapRect(8300, -2700, 300, 100); addPartToMap(map.length - 1); map[map.length - 1].isRemove = true; spawn.mapRect(8000, -3600, 300, 100); addPartToMap(map.length - 1); map[map.length - 1].isRemove = true; spawn.mapRect(8200, -4500, 300, 100); addPartToMap(map.length - 1); map[map.length - 1].isRemove = true; climbGroup = 1; } else if (climbGroup === 1) { //toggle on platforms: 1 spawn.mapRect(8300, -1200, 300, 100); addPartToMap(map.length - 1); map[map.length - 1].isRemove = true; spawn.mapRect(8500, -2100, 300, 100); addPartToMap(map.length - 1); map[map.length - 1].isRemove = true; spawn.mapRect(8100, -3000, 300, 100); addPartToMap(map.length - 1); map[map.length - 1].isRemove = true; spawn.mapRect(8000, -3900, 300, 100); addPartToMap(map.length - 1); map[map.length - 1].isRemove = true; spawn.mapRect(8200, -4800, 300, 100); addPartToMap(map.length - 1); map[map.length - 1].isRemove = true; climbGroup = 2; } else if (climbGroup === 2) { //toggle on platforms: 2 spawn.mapRect(8500, -600, 300, 100); addPartToMap(map.length - 1); map[map.length - 1].isRemove = true; spawn.mapRect(8100, -1500, 300, 100); addPartToMap(map.length - 1); map[map.length - 1].isRemove = true; spawn.mapRect(8000, -2400, 300, 100); addPartToMap(map.length - 1); map[map.length - 1].isRemove = true; spawn.mapRect(8500, -3300, 300, 100); addPartToMap(map.length - 1); map[map.length - 1].isRemove = true; spawn.mapRect(8500, -4200, 300, 100); addPartToMap(map.length - 1); map[map.length - 1].isRemove = true; climbGroup = 0; } simulation.draw.setPaths(); //update map graphics } //every 120 cycles, first deletes previous group, then cycles through one of 3 toggle groups }; if (!initialSpawn) { level.defaultZoom = 1300 //was 800 I changed this simulation.zoomTransition(level.defaultZoom) document.body.style.backgroundColor = "#dcdcde"; //Level level.setPosToSpawn(-100, -1450); spawn.mapRect(level.enter.x, level.enter.y + 20, 100, 20); level.exit.x = 9300; level.exit.y = -5130; spawn.mapRect(level.exit.x, level.exit.y + 20, 100, 20); //Map spawn.mapRect(-1400, -2200, 1700, 200); spawn.mapRect(100, -2200, 200, 1000); spawn.mapRect(-600, -1400, 8600, 200); spawn.mapRect(-1400, -2200, 200, 1000); spawn.mapRect(-2800, -1400, 1600, 200); spawn.mapRect(-2800, -1400, 200, 1400); spawn.mapRect(-2800, -200, 11800, 200); spawn.mapRect(-900, -600, 600, 600); spawn.mapRect(200, -800, 500, 100); spawn.mapRect(1300, -1400, 200, 900); spawn.mapRect(1300, -600, 500, 100); spawn.mapRect(2300, -800, 200, 200); spawn.mapRect(2900, -400, 100, 400); spawn.mapRect(3200, -600, 100, 600); spawn.mapRect(3500, -800, 100, 800); spawn.mapRect(4400, -900, 500, 100); spawn.mapRect(4400, -600, 500, 100); spawn.mapRect(4800, -900, 100, 400); spawn.mapRect(5300, -550, 600, 550); spawn.mapRect(5600, -900, 300, 800); spawn.mapRect(6300, -300, 1100, 300); spawn.mapRect(6600, -400, 500, 200); spawn.mapRect(6600, -800, 500, 100); spawn.mapRect(7000, -1400, 100, 700); spawn.mapRect(7800, -5900, 200, 5100); spawn.mapRect(7800, -5900, 1900, 200); spawn.mapRect(9500, -5900, 200, 1000); spawn.mapRect(8800, -5100, 900, 200); spawn.mapRect(8800, -5100, 200, 5100); //Text spawn.mapRect(400, -1600, 100, 10); spawn.mapRect(400, -1600, 10, 100); spawn.mapRect(490, -1600, 10, 40); spawn.mapRect(400, -1570, 100, 10); spawn.mapRect(400, -1540, 100, 10); spawn.mapRect(490, -1540, 10, 40); spawn.mapRect(600, -1600, 10, 100); spawn.mapRect(600, -1510, 100, 10); spawn.mapRect(690, -1600, 10, 100); spawn.mapRect(800, -1600, 100, 10); spawn.mapRect(800, -1600, 10, 100); spawn.mapRect(890, -1600, 10, 100); spawn.mapRect(0, 0, 1, 1); //dont ask why i have these spawn.mapRect(1, 0, 1, 1); //dont ask why i have these spawn.mapRect(0, 1, 1, 1); //dont ask why i have these spawn.mapRect(1, 1, 1, 1); //dont ask why i have these spawn.mapRect(-1, 0, 1, 1); //dont ask why i have these spawn.mapRect(0, -1, 1, 1); //dont ask why i have these spawn.mapRect(-1, -1, 1, 1); //dont ask why i have these spawn.mapRect(1, -1, 1, 1); //dont ask why i have these spawn.mapRect(-1, 1, 1, 1); //dont ask why i have these //Mob Spawning setTimeout(() => { simulation.makeTextLog("UNKNOWN: \"You cannot kill them.\"", 600); }, 2000); setTimeout(() => { simulation.makeTextLog("UNKNOWN: \"But I have slowed them down for you.\"", 600); }, 6000); spawn[runMobList[Math.floor(Math.random() * runMobList.length)]](200, -400); spawn[runMobList[Math.floor(Math.random() * runMobList.length)]](1800, -1000); spawn[runMobList[Math.floor(Math.random() * runMobList.length)]](3200, -1000); spawn[runMobList[Math.floor(Math.random() * runMobList.length)]](6200, -400); if (simulation.difficulty > 10) { spawn[runMobList[Math.floor(Math.random() * runMobList.length)]](1000, -400); spawn[runMobList[Math.floor(Math.random() * runMobList.length)]](2400, -400); spawn[runMobList[Math.floor(Math.random() * runMobList.length)]](4000, -400); spawn[runMobList[Math.floor(Math.random() * runMobList.length)]](6600, -1000); setTimeout(() => { simulation.makeTextLog("UNKNOWN: \"Run.\"", 600); }, 10000); } //some of the mobs if (simulation.difficulty > 20) { spawn[runMobList[Math.floor(Math.random() * runMobList.length)]](1000, -1000); spawn[runMobList[Math.floor(Math.random() * runMobList.length)]](3100, -300); spawn[runMobList[Math.floor(Math.random() * runMobList.length)]](4200, -1000); spawn[runMobList[Math.floor(Math.random() * runMobList.length)]](7400, -800); setTimeout(() => { simulation.makeTextLog("UNKNOWN: \"RUN!\"", 600); }, 11000); } //most of the mobs if (simulation.difficulty > 30) { spawn[runMobList[Math.floor(Math.random() * runMobList.length)]](200, -1000); spawn[runMobList[Math.floor(Math.random() * runMobList.length)]](3400, -300); spawn[runMobList[Math.floor(Math.random() * runMobList.length)]](5200, -800); spawn[runMobList[Math.floor(Math.random() * runMobList.length)]](7500, -300); setTimeout(() => { simulation.makeTextLog("UNKNOWN: \"GET OUT OF HERE.\"", 600); }, 12000); } //all the mobs //Boss Spawning if (simulation.difficulty > 5) { spawn.randomLevelBoss(-2200, -700, ["powerUpBossBaby", "blockBoss", "revolutionBoss"]); setTimeout(() => { simulation.makeTextLog("UNKNOWN: \"They are coming for you.\"", 600); }, 14000); } anotherBoss(-1800, -700); //custom second boss spawn //Blocks spawn.bodyRect(1300, -500, 200, 100); spawn.bodyRect(1400, -500, 200, 100); spawn.bodyRect(1500, -500, 200, 100); spawn.bodyRect(5700, -1200, 100, 100); spawn.bodyRect(5700, -1100, 100, 100); spawn.bodyRect(5700, -1000, 100, 100); spawn.bodyRect(6800, -700, 100, 100); spawn.bodyRect(6800, -600, 100, 100); spawn.bodyRect(6800, -500, 100, 100); spawn.debris(4400, -300, 500, 16); spawn.debris(3300, -600, 200, 6); spawn.debris(3000, -500, 20, 6); spawn.debris(2300, -300, 200, 6); spawn.debris(200, -300, 500, 16); //Powerups if (simulation.difficulty > 10) { powerUps.spawn(1600, -700, "tech"); } powerUps.spawnRandomPowerUp(1700, -700); // if (simulation.difficulty > 20) { // powerUps.spawn(4600, -700, "tech"); // } powerUps.spawnRandomPowerUp(4700, -700); // if (simulation.difficulty > 30) { // powerUps.spawn(6800, -1000, "tech"); // } powerUps.spawnRandomPowerUp(6900, -1000); powerUps.spawn(9200, -5400, "tech"); if (simulation.difficulty > 10) { powerUps.spawn(9200, -5500, "tech"); } // if (simulation.difficulty > 20) { // powerUps.spawn(9200, -5600, "tech"); // } // if (simulation.difficulty > 30) { // powerUps.spawn(9200, -5700, "tech"); // } powerUps.addResearchToLevel() //needs to run after mobs are spawned initialSpawn == true; } }, islands() { simulation.makeTextLog(`islands by Richard0820`); const boost1 = level.boost(58500, -18264, 1300); let portal2, portal3; // const removeIndex1 = map.length - 1; const drip1 = level.drip(59300, -18975, -18250, 100); // drip(x, yMin, yMax, period = 100, color = "hsla(160, 100%, 35%, 0.5)") { const drip2 = level.drip(60000, -18953, -18250, 150); const drip3 = level.drip(60905, -18652, -18250, 70); const slimePit1 = level.hazard(58850, -18300, 2275, 100, 0.01); //hazard(x, y, width, height, damage = 0.003) spawn.mapRect(58850, -18300, 2275, 100); const slimePit2 = level.hazard(74400, -18075, 350, 100, 0.01); let isSpawnedBoss = false; level.custom = () => { level.exit.drawAndCheck(); boost1.query(); level.enter.draw(); drip1.draw(); drip2.draw(); drip3.draw(); // portal[2].query(); // portal[3].query(); // portal[0].draw(); // portal[1].draw(); // portal[2].draw(); // portal[3].draw(); portal2[2].query(); portal2[3].query(); portal2[0].draw(); portal2[1].draw(); portal2[2].draw(); portal2[3].draw(); portal3[2].query(); portal3[3].query(); portal3[0].draw(); portal3[1].draw(); portal3[2].draw(); portal3[3].draw(); }; level.customTopLayer = () => { slimePit1.query(); slimePit2.query(); ctx.fillStyle = `rgba(68, 68, 68, ${Math.max(0.3, Math.min((-17650 - m.pos.y) / 100, 0.99))})`; ctx.fillRect(58390, -17655, 1490, 740); }; document.body.style.backgroundColor = "hsl(138, 3%, 74%)"; level.setPosToSpawn(57680, -18330); level.exit.x = 76343; level.exit.y = -18020; spawn.mapRect(level.enter.x, level.enter.y + 20, 100, 30); spawn.mapRect(level.exit.x, level.exit.y + 20, 100, 30); level.defaultZoom = 2000; simulation.zoomTransition(level.defaultZoom); // spawn.setSpawnList = [ // "hopper", // "slasher", // "striker", // "stabber", // "springer", // "pulsar", // "sneaker", // "spinner", // "grower", // "focuser", // "spawner", // ]; spawn.mapRect(57800, -18550, 50, 100); spawn.mapRect(57500, -18550, 50, 275); spawn.mapRect(66900, -18675, 300, 200); spawn.mapRect(66925, -19050, 125, 225); spawn.mapRect(67825, -16975, 125, 100); spawn.mapRect(74900, -18075, 225, 100); spawn.mapRect(73925, -18225, 150, 275); spawn.mapRect(76200, -18325, 50, 125); spawn.mapRect(76525, -18325, 75, 400); spawn.mapRect(61325, -18350, 50, 25); spawn.mapRect(61450, -18425, 50, 25); spawn.mapRect(61475, -18450, 25, 25); spawn.mapRect(58725, -18350, 125, 50); spawn.mapRect(58675, -18275, 50, 75); spawn.mapRect(58600, -18275, 75, 75); spawn.mapRect(58675, -18325, 50, 50); spawn.mapRect(58250, -16925, 1825, 1050); spawn.mapRect(57500, -18200, 4475, 550); spawn.mapRect(61500, -18475, 475, 275); spawn.mapRect(62175, -18575, 325, 400); spawn.mapRect(62900, -18850, 525, 375); spawn.mapRect(63900, -18925, 450, 400); spawn.mapRect(64725, -19000, 625, 500); spawn.mapRect(65825, -19050, 675, 400); spawn.mapRect(66800, -18950, 400, 400); spawn.mapRect(68775, -18850, 525, 400); spawn.mapRect(67375, -16900, 1800, 1450); spawn.mapRect(67375, -17475, 325, 575); spawn.mapRect(68900, -17500, 250, 500); spawn.mapRect(69425, -17050, 500, 475); spawn.mapRect(70400, -17150, 425, 175); spawn.mapRect(71175, -17325, 450, 325); spawn.mapRect(72000, -17425, 325, 300); spawn.mapRect(72725, -17450, 350, 275); spawn.mapRect(70050, -18800, 550, 350); spawn.mapRect(67750, -19400, 375, 1200); spawn.mapRect(67750, -18200, 1425, 700); spawn.mapRect(66800, -18550, 575, 1650); spawn.mapRect(66800, -16900, 575, 1450); spawn.mapRect(67350, -18175, 250, 750); spawn.mapRect(71050, -18450, 725, 275); spawn.mapRect(72100, -18150, 475, 200); spawn.mapRect(73325, -17975, 3275, 475); spawn.mapRect(73175, -17775, 150, 300); spawn.mapRect(72975, -17675, 225, 250); spawn.mapRect(76200, -18325, 400, 75); spawn.mapRect(76525, -18250, 75, 275); spawn.mapRect(76200, -18250, 50, 50); spawn.mapRect(57500, -17675, 900, 1800); spawn.mapRect(59875, -17675, 1975, 1800); spawn.mapRect(57550, -18275, 225, 75); spawn.mapRect(61375, -18375, 50, 50); spawn.mapRect(61100, -18350, 75, 50); spawn.mapRect(61175, -18325, 50, 25); spawn.mapRect(61850, -16525, 250, 175); spawn.mapRect(57500, -18500, 50, 325); spawn.mapRect(57500, -18550, 350, 50); spawn.mapRect(57800, -18500, 50, 50); spawn.mapRect(61275, -18325, 375, 125); spawn.mapRect(61425, -18400, 200, 75); spawn.mapRect(62125, -18575, 125, 75); spawn.mapRect(62250, -18200, 175, 125); spawn.mapRect(62850, -18725, 100, 75); spawn.mapRect(63075, -18550, 225, 225); spawn.mapRect(62800, -18275, 75, 75); spawn.mapRect(62500, -18475, 75, 50); spawn.mapRect(63825, -18900, 150, 50); spawn.mapRect(63950, -18575, 150, 125); spawn.mapRect(64200, -18550, 100, 250); spawn.mapRect(64925, -18525, 200, 275); spawn.mapRect(64625, -18425, 75, 125); spawn.mapRect(65225, -18675, 150, 175); spawn.mapRect(65350, -18950, 75, 100); spawn.mapRect(65950, -18575, 75, 150); spawn.mapRect(66000, -18725, 225, 175); spawn.mapRect(66275, -18675, 75, 125); spawn.mapRect(66275, -18550, 75, 75); spawn.mapRect(66150, -18550, 100, 50); spawn.mapRect(66225, -18875, 25, 150); spawn.mapRect(66200, -18750, 75, 25); spawn.mapRect(66925, -19100, 125, 150); spawn.mapRect(66000, -19100, 75, 50); spawn.mapRect(65000, -19075, 100, 75); spawn.mapRect(66750, -18625, 100, 100); spawn.mapRect(68050, -18500, 350, 350); spawn.mapRect(68125, -18975, 150, 475); spawn.mapRect(69850, -18675, 150, 200); spawn.mapRect(70000, -18625, 150, 50); spawn.mapRect(68850, -18575, 325, 225); spawn.mapRect(69100, -18400, 75, 100); spawn.mapRect(70150, -18525, 125, 200); spawn.mapRect(70425, -18525, 125, 200); spawn.mapRect(70250, -18350, 175, 225); spawn.mapRect(70325, -18475, 50, 150); spawn.mapRect(70275, -18450, 150, 150); spawn.mapRect(71175, -18250, 525, 250); spawn.mapRect(71050, -18200, 150, 375); spawn.mapRect(70925, -18300, 200, 250); spawn.mapRect(71425, -18525, 175, 150); spawn.mapRect(70225, -18950, 275, 250); spawn.mapRect(70475, -17050, 225, 175); spawn.mapRect(70625, -17250, 100, 150); spawn.mapRect(71300, -17150, 200, 350); spawn.mapRect(71100, -17250, 125, 100); spawn.mapRect(71550, -17400, 150, 150); spawn.mapRect(67675, -17150, 225, 300); spawn.mapRect(68225, -17000, 100, 125); spawn.mapRect(67900, -16975, 375, 100); spawn.mapRect(68275, -16950, 150, 50); spawn.bodyRect(76200, -18200, 50, 200); spawn.mapRect(76200, -18000, 50, 25); spawn.bodyRect(57800, -18450, 50, 175); spawn.mapRect(68725, -17600, 300, 250); spawn.mapRect(68625, -17550, 175, 100); spawn.mapRect(68850, -17400, 150, 125); spawn.mapRect(69325, -16900, 200, 225); spawn.mapRect(69575, -16625, 175, 275); spawn.mapRect(69850, -16875, 250, 200); spawn.mapRect(69875, -16650, 150, 300); spawn.mapRect(69825, -16800, 375, 325); spawn.mapRect(69650, -16775, 325, 475); spawn.mapRect(71975, -17325, 100, 125); spawn.mapRect(72075, -17200, 150, 150); spawn.mapRect(72275, -17350, 150, 150); spawn.mapRect(72325, -17275, 150, 225); spawn.mapRect(72225, -18050, 200, 225); spawn.mapRect(71925, -18150, 250, 175); spawn.mapRect(72075, -18275, 125, 175); spawn.mapRect(72500, -18025, 125, 175); spawn.mapRect(72400, -17975, 150, 175); spawn.mapRect(73925, -18225, 350, 275); spawn.mapRect(74750, -18125, 275, 175); spawn.mapRect(74250, -18100, 150, 75); spawn.mapRect(74275, -18050, 200, 75); spawn.mapRect(73750, -18100, 275, 125); spawn.mapRect(73075, -17475, 3525, 300); spawn.mapRect(73275, -17600, 3325, 225); spawn.mapRect(57775, -18250, 150, 50); spawn.mapRect(57775, -18275, 75, 25); spawn.mapRect(57925, -18225, 50, 25); spawn.debris(68300, -17000, 3700, 16); spawn.mapRect(62000, -16525, 100, 200); spawn.mapRect(59125, -19125, 325, 200); spawn.mapRect(59925, -19175, 350, 225); spawn.mapRect(60800, -18850, 275, 200); spawn.mapRect(75025, -18075, 200, 100); spawn.mapRect(75225, -18025, 100, 50); spawn.bodyRect(74300, -18150, 50, 25); spawn.bodyRect(73850, -18150, 75, 75); spawn.bodyRect(74700, -18000, 75, 50); spawn.bodyRect(74250, -18325, 25, 25); spawn.bodyRect(74275, -18325, 25, 25); spawn.bodyRect(74275, -18325, 25, 25); spawn.bodyRect(74300, -18325, 100, 25); // portal = level.portal( // { // x: 58625, // y: -16925, // }, // 1.5 * Math.PI, // { // //right // x: 58625, // y: -17650, // }, // 2.5 * Math.PI // ); //right portal2 = level.portal({ x: 61920, y: -16525, }, 1.5 * Math.PI, { //right x: 58400, y: -17325, }, 2 * Math.PI ); portal3 = level.portal({ x: 59865, y: -17300, }, 3 * Math.PI, { //right x: 60820, y: -31130, }, 2.5 * Math.PI ); spawn.mapRect(60275, -32250, 975, 400); spawn.mapRect(60375, -31925, 275, 225); spawn.mapRect(61025, -31950, 175, 300); spawn.mapRect(60825, -31725, 100, 350); spawn.mapRect(60675, -31875, 200, 225); spawn.mapRect(60225, -31950, 100, 725); spawn.mapRect(60250, -31525, 250, 375); spawn.mapRect(60675, -31475, 425, 350); spawn.mapRect(60625, -32500, 225, 300); spawn.mapRect(61025, -32325, 125, 175); spawn.mapRect(60375, -32325, 175, 150); spawn.mapRect(60250, -19075, 100, 100); spawn.randomMob(59850, -18825, Infinity); spawn.randomMob(62325, -18800, Infinity); spawn.randomMob(61725, -18800, Infinity); spawn.randomMob(63050, -19025, Infinity); spawn.randomMob(64100, -19200, Infinity); spawn.randomMob(64225, -19100, Infinity); spawn.randomMob(64875, -19300, Infinity); spawn.randomMob(65125, -19325, Infinity); spawn.randomMob(65850, -19275, Infinity); spawn.randomMob(66200, -19300, Infinity); spawn.randomMob(65975, -19425, Infinity); spawn.randomMob(67925, -19600, Infinity); spawn.randomMob(66975, -19275, Infinity); spawn.randomMob(67550, -18750, Infinity); spawn.randomMob(69625, -17275, Infinity); spawn.randomMob(70550, -17350, Infinity); spawn.randomMob(71375, -17475, Infinity); spawn.randomMob(72200, -17600, Infinity); spawn.randomMob(73000, -18025, Infinity); spawn.randomMob(73850, -18350, Infinity); spawn.randomMob(75725, -18300, Infinity); spawn.randomMob(75875, -18275, Infinity); spawn.randomMob(75700, -18200, Infinity); spawn.randomMob(75550, -18275, Infinity); spawn.randomMob(75825, -18150, Infinity); spawn.randomMob(75575, -18150, Infinity); spawn.randomGroup(75575, -18150, 0); level.chain(67250, -19325, 0, true, 14, 20); spawn.mapRect(58725, -18300, 125, 100); spawn.mapRect(61100, -18300, 175, 100); spawn.mapRect(67175, -19375, 100, 100); spawn.mapRect(59125, -19125, 325, 200); spawn.mapRect(59925, -19175, 350, 225); spawn.mapRect(60800, -18850, 275, 200); spawn.mapRect(60850, -18725, 50, 200); spawn.mapRect(60950, -18675, 50, 200); spawn.mapRect(59975, -19025, 50, 250); spawn.mapRect(60125, -19025, 50, 400); spawn.mapRect(60075, -19025, 50, 450); spawn.mapRect(59425, -19075, 100, 100); spawn.mapRect(59175, -19000, 100, 225); spawn.mapRect(59325, -19000, 75, 450); spawn.mapRect(59050, -19000, 100, 100); spawn.mapRect(61050, -18775, 100, 75); spawn.mapRect(60725, -18850, 125, 125); spawn.bodyRect(61850, -16525, 250, 175); if (simulation.difficulty > 1) { spawn.randomGroup(75575, -18150, 0); spawn.randomLevelBoss(68450, -17300); } if (!isSpawnedBoss) { isSpawnedBoss = true; if (Math.random() < 0.33) { for ( let i = 0, len = Math.min(simulation.difficulty / 20, 6); i < len; ++i ) spawn.bounceBoss(59025, -17325, 50, false); } else if (Math.random() < 0.5) { for ( let i = 0, len = Math.min(simulation.difficulty / 9, 8); i < len; ++i ) spawn.sprayBoss(59025, -17325, 50, false); } else { for ( let i = 0, len = Math.min(simulation.difficulty / 6, 10); i < len; ++i ) spawn.mineBoss(59025, -17325, 50, false); } powerUps.spawn(59352, -17115, "tech"); // for (let i = 0, len = 3 + simulation.difficulty / 20; i < len; ++i) spawn.mantisBoss(1487 + 300 * i, -1525, 35, false) } simulation.fallHeight = -15000; powerUps.addResearchToLevel(); powerUps.spawn(3000, -230, "heal"); // level.difficultyIncrease(60) }, temple() { simulation.makeTextLog(`temple by Scar1337`); const V = Vector; const Equation = (function () { function Equation(a, b, c) { this.a = a; this.b = b; this.c = c; } Equation.prototype.getXfromY = function (y) { return (-this.b * y - this.c) / this.a; } Equation.prototype.getYfromX = function (x) { return (-this.a * x - this.c) / this.b; } Equation.fromPoints = function (v1, v2) { if (v1.x === v2.x) return new Equation(1, 0, -v1.x); if (v1.y === v2.y) return new Equation(0, 1, -v1.y); const d = (v2.y - v1.y) / (v2.x - v1.x); return new Equation(-d, 1, d * v1.x - v1.y); }; return Equation; })(); const Rect = (function () { function Rect(x, y, w, h) { this.pos = { x, y }; this.width = w; this.height = h; } Rect.prototype.has = function ({ x, y }) { return x >= this.pos.x && x <= this.pos.x + this.width && y >= this.pos.y && y <= this.pos.y + this.height; } Rect.prototype.hasLine = function (eq) { const leftInter = eq.getYfromX(this.pos.x); const rightInter = eq.getYfromX(this.pos.x + this.width); const topInter = eq.getXfromY(this.pos.y); return (leftInter >= this.pos.y && leftInter <= this.pos.y + this.height) || (rightInter >= this.pos.y && rightInter <= this.pos.y + this.height) || (topInter >= this.pos.x && topInter <= this.pos.x + this.width); } Rect.prototype.addToMap = function () { spawn.mapRect(this.pos.x, this.pos.y, this.width, this.height); } Object.defineProperty(Rect.prototype, "midPos", { get() { return V.add(this.pos, { x: this.width / 2, y: this.height / 2 }); } }); Rect.fromBounds = function (min, max) { return new Rect(min.x, min.y, max.x - min.x, max.y - min.y); } Rect.prototype.isCollidingWith = function (other) { const tc = { p1: [this.pos.x, this.pos.y], p2: [this.pos.x + this.width, this.pos.y + this.height] }; const oc = { p1: [other.pos.x, other.pos.y], p2: [other.pos.x + other.width, other.pos.y + other.height] }; // If one rectangle is on left side of other if (tc.p1[0] >= oc.p2[0] || oc.p1[0] >= tc.p2[0]) return false; // If one rectangle is above other if (tc.p1[1] >= oc.p2[1] || oc.p1[1] >= tc.p2[1]) return false; return true; } return Rect; })(); function isInBound(bound) { return bound.has(player.bounds.min) || bound.has(player.bounds.max); } function addWIMP(x, y) { spawn.WIMP(x, y); const me = mob[mob.length - 1]; me.isWIMP = true; } function relocateWIMPs(x, y) { for (const i of mob) { if (i.isWIMP) { setPos(i, { x: x + 300 * (Math.random() - 0.5), y: y + 300 * (Math.random() - 0.5) }); } } } function secondRoomSuckerBoss(x, y, isDark = false, radius = 25) { mobs.spawn(x, y, 12, radius, isDark ? "#000" : "#fff"); let me = mob[mob.length - 1]; me.isBoss = true; me.isDark = isDark; me.stroke = "transparent"; me.eventHorizon = 500; // How family friendly content much do I have to reduce this me.seeAtDistance2 = 5e6; // Basically just see at all times, in the context it's given me.accelMag = 0.00003 * simulation.accelScale; me.collisionFilter.mask = cat.player | cat.bullet; me.memory = 1600; me.randomPRNGMult = Math.random() * 500; me.attackCycle = 0; me.lastAttackCycle = 0; Matter.Body.setDensity(me, 0.012); // extra dense, normal is 0.001 // makes effective life much larger me.onDeath = function () { // applying forces to player doesn't seem to work inside this method, not sure why powerUps.spawn(this.position.x + 20, this.position.y, "ammo"); if (Math.random() > 0.5) powerUps.spawn(this.position.x, this.position.y, "ammo"); if (Math.random() > 0.3) powerUps.spawn(this.position.x, this.position.y, "heal", true, null, 30 * (simulation.healScale ** 0.25) * Math.sqrt(tech.largerHeals) * Math.sqrt(0.1 + Math.random() * 0.5)); }; me.damageReduction = 0.25 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1); me.do = function () { // keep it slow, to stop issues from explosion knock backs if (this.speed > 1) { Matter.Body.setVelocity(this, { x: this.velocity.x * 0.95, y: this.velocity.y * 0.95 }); } if (!(simulation.cycle % this.seePlayerFreq)) { if (this.distanceToPlayer2() < this.seeAtDistance2) { // ignore cloak for black holes this.locatePlayer(); if (!this.seePlayer.yes) this.seePlayer.yes = true; } else if (this.seePlayer.recall) { this.lostPlayer(); } } this.checkStatus(); if (this.seePlayer.recall) { // accelerate towards the player const forceMag = this.accelMag * this.mass; const dx = this.seePlayer.position.x - this.position.x const dy = this.seePlayer.position.y - this.position.y const mag = Math.sqrt(dx * dx + dy * dy) this.force.x += forceMag * dx / mag; this.force.y += forceMag * dy / mag; // eventHorizon waves in and out const eventHorizon = this.eventHorizon * (1 + 0.2 * Math.sin(simulation.cycle * 0.008)); // draw darkness ctx.fillStyle = this.isDark ? "rgba(0,20,40,0.6)" : "rgba(225,215,255,0.6)"; DrawTools.arc(this.position.x, this.position.y, eventHorizon * 0.2, 0, 2 * Math.PI); ctx.fillStyle = this.isDark ? "rgba(0,20,40,0.4)" : "rgba(225,215,255,0.4)"; DrawTools.arc(this.position.x, this.position.y, eventHorizon * 0.4, 0, 2 * Math.PI); ctx.fillStyle = this.isDark ? "rgba(0,20,40,0.3)" : "rgba(225,215,255,0.3)"; DrawTools.arc(this.position.x, this.position.y, eventHorizon * 0.6, 0, 2 * Math.PI); ctx.fillStyle = this.isDark ? "rgba(0,20,40,0.2)" : "rgba(225,215,255,0.2)"; DrawTools.arc(this.position.x, this.position.y, eventHorizon * 0.8, 0, 2 * Math.PI); ctx.fillStyle = this.isDark ? "rgba(0,0,0,0.05)" : "rgba(255,255,255,0.05)"; DrawTools.arc(this.position.x, this.position.y, eventHorizon, 0, 2 * Math.PI); // when player is inside event horizon if (distance(this.position, player.position) < eventHorizon) { if (this.isDark) { // Standard black hole stuff if (m.immuneCycle < m.cycle) { if (m.energy > 0) m.energy -= 0.003; if (m.energy < 0.1) m.damage(0.00015 * simulation.dmgScale); } const angle = Math.atan2(player.position.y - this.position.y, player.position.x - this.position.x); player.force.x -= 0.0005 * Math.cos(angle) * player.mass * (m.onGround ? 1.7 : 1); player.force.y -= 0.0005 * Math.sin(angle) * player.mass; // draw line to player ctx.lineWidth = Math.min(60, this.radius * 2); ctx.strokeStyle = "rgba(0,0,0,0.5)"; DrawTools.line([this.position, m.pos]); ctx.fillStyle = "rgba(0,0,0,0.3)"; DrawTools.arc(m.pos.x, m.pos.y, 40, 0, 2 * Math.PI); } else { // Lightning attacks this.attackCycle++; if (this.attackCycle >= 30) { this.attackCycle = 0; this.lastAttackCycle = simulation.cycle; Matter.Body.setVelocity(player, V.add(player.velocity, { x: 0, y: -10 })); if (m.immuneCycle < m.cycle) { if (m.energy > 0) m.energy -= 0.03; m.damage(0.005 * simulation.dmgScale); } } DrawTools.lightning(this.position, m.pos, this.lastAttackCycle, this.randomPRNGMult); ctx.fillStyle = `rgba(255,240,127,${0.12 * Math.max(15 - simulation.cycle + this.lastAttackCycle, 0)})`; DrawTools.arc(m.pos.x, m.pos.y, 40, 0, 2 * Math.PI); } } } } }; function secondRoomPlacerBoss(x, y, isDark = false, size = 70) { mobs.spawn(x, y, isDark ? 3 : 4, size, isDark ? "#0008" : "#fff8"); let me = mob[mob.length - 1]; me.isBoss = true; me.isDark = isDark; me.stroke = isDark ? "#000" : "#fff"; me.seeAtDistance2 = 5e6; // Basically just see at all times, in the context it's given me.accelMag = 0.0001 * simulation.accelScale; me.collisionFilter.mask = cat.player | cat.bullet; me.memory = 1600; me.randomPRNGMult = Math.random() * 500; me.attackCycle = 0; me.maxAttackCycle = isDark ? 90 : 240; Matter.Body.setDensity(me, 0.006); // extra dense, normal is 0.001 // makes effective life much larger me.onDeath = function () { powerUps.spawn(this.position.x + 20, this.position.y, "ammo"); if (Math.random() > 0.5) powerUps.spawn(this.position.x, this.position.y, "ammo"); if (Math.random() > 0.3) powerUps.spawn(this.position.x, this.position.y, "heal", true, null, 30 * (simulation.healScale ** 0.25) * Math.sqrt(tech.largerHeals) * Math.sqrt(0.1 + Math.random() * 0.5)); }; me.damageReduction = 0.25 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1); me.do = function () { // keep it slow, to stop issues from explosion knock backs if (this.speed > 2) { Matter.Body.setVelocity(this, { x: this.velocity.x * 0.95, y: this.velocity.y * 0.95 }); } if (!(simulation.cycle % this.seePlayerFreq)) { if (this.distanceToPlayer2() < this.seeAtDistance2) { // ignore cloak this.locatePlayer(); if (!this.seePlayer.yes) this.seePlayer.yes = true; } else if (this.seePlayer.recall) { this.lostPlayer(); } } this.checkStatus(); if (this.seePlayer.recall) { // accelerate towards the player const forceMag = this.accelMag * this.mass; const dx = this.seePlayer.position.x - this.position.x const dy = this.seePlayer.position.y - this.position.y const mag = Math.sqrt(dx * dx + dy * dy) this.force.x += forceMag * dx / mag; this.force.y += forceMag * dy / mag; this.attackCycle++; if (this.attackCycle > this.maxAttackCycle) { this.attackCycle = 0; secondRoomObstacle(this.position.x, this.position.y, this.isDark, size); } } } }; function secondRoomObstacle(x, y, isDark = false, size = 70) { mobs.spawn(x, y, isDark ? 3 : 4, size, isDark ? "#0004" : "#fff4"); let me = mob[mob.length - 1]; me.stroke = isDark ? "#000b" : "#fffb"; me.collisionFilter.mask = isDark ? cat.player | cat.bullet : 0; me.isDropPowerUp = false; me.showHealthBar = false; me.leaveBody = false; me.timeLeft = 1200; me.isObstacle = true; me.damageReduction = isDark ? 0.5 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1) : 0; if (!isDark) { me.isBadTarget = true; me.attackCycle = 0; me.maxAttackCycle = 10; me.inertia = Infinity; } me.do = isDark ? function () { Matter.Body.setVelocity(this, { x: this.velocity.x * 0.95, y: this.velocity.y * 0.95 }); } : function () { Matter.Body.setVelocity(this, { x: this.velocity.x * 0.95, y: this.velocity.y * 0.95 }); if (Rect.fromBounds(this.bounds.min, this.bounds.max).isCollidingWith(Rect.fromBounds(player.bounds.min, player.bounds.max))) { this.attackCycle++; this.attackCycle = Math.min(this.attackCycle, 10); } else { this.attackCycle--; this.attackCycle = Math.max(this.attackCycle, 0); } if (this.attackCycle > 0) { ctx.beginPath(); const vertices = this.vertices; ctx.moveTo(vertices[0].x, vertices[0].y); for (let j = 1, len = vertices.length; j < len; ++j) ctx.lineTo(vertices[j].x, vertices[j].y); ctx.lineTo(vertices[0].x, vertices[0].y); if (this.attackCycle >= 10) { ctx.shadowBlur = 10; ctx.shadowColor = "rgb(255, 210, 64)"; } ctx.fillStyle = `rgba(255, 210, 64, ${this.attackCycle / 15})`; ctx.fill(); ctx.shadowBlur = 0; if (this.attackCycle >= 10) { DrawTools.lightning(this.position, m.pos, simulation.cycle); m.damage(0.003 * simulation.dmgScale); } } this.timeLimit(); } } function mobGrenade(...args) { spawn.grenade(...args); const pulseRadius = args[3] || Math.min(550, 250 + simulation.difficulty * 3) let me = mob[mob.length - 1]; me.fill = "#ace"; me.damageReduction = 0; me.onDeath = function () { //damage player if in range if (distance(player.position, this.position) < pulseRadius && m.immuneCycle < m.cycle) { m.immuneCycle = m.cycle + m.collisionImmuneCycles; //player is immune to damage m.damage(0.02 * simulation.dmgScale); } simulation.drawList.push({ //add dmg to draw queue x: this.position.x, y: this.position.y, radius: pulseRadius, color: "rgba(170,204,238,0.3)", time: simulation.drawTime }); }; me.do = function () { this.timeLimit(); ctx.beginPath(); //draw explosion outline ctx.arc(this.position.x, this.position.y, pulseRadius * (1.01 - this.timeLeft / this.lifeSpan), 0, 2 * Math.PI); //* this.fireCycle / this.fireDelay ctx.fillStyle = "rgba(170,204,238,0.1)"; ctx.fill(); }; } // Todo: nerf ThirdRoomBoss a bit? function thirdRoomBoss(x, y) { mobs.spawn(x, y, 6, 60, "#000"); let me = mob[mob.length - 1]; // Fix in place me.constraint = Constraint.create({ pointA: { x: me.position.x, y: me.position.y }, bodyB: me, stiffness: 1, damping: 1 }); Composite.add(engine.world, me.constraint); me.isBoss = true; me.stroke = "transparent"; me.eventHorizon = 900; me.collisionFilter.mask = cat.player | cat.bullet | cat.body; me.memory = Infinity; me.attackCycle = 0; me.lastAttackCycle = 0; me.spawnCycle = 0; Matter.Body.setDensity(me, 0.08); //extra dense //normal is 0.001 //makes effective life much larger me.onDeath = function () { for (let j = 0; j < 8; j++) { //in case some mobs leave things after they die for (let i = 0, len = mob.length; i < len; ++i) { if (mob[i] !== this) { if (mob[i].isInvulnerable) { //disable invulnerability mob[i].isInvulnerable = false mob[i].damageReduction = 1 } mob[i].damage(Infinity, true); } } } // You earned it: One more tech powerUps.spawn(this.position.x, this.position.y, "tech"); powerUps.spawnBossPowerUp(this.position.x, this.position.y); templePlayer.room3ToEndAnim = 1; }; me.nextHealthThreshold = 0.75; me.trapCycle = 0; me.onDamage = function () { if (this.health < this.nextHealthThreshold) { this.health = this.nextHealthThreshold - 0.01 this.nextHealthThreshold = Math.floor(this.health * 4) / 4 //0.75,0.5,0.25 this.trapCycle = 1; this.isInvulnerable = true; this.damageReduction = 0; } }; me.damageReduction = 0.25 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1); me.rings = [{ colour: "#65f", radius: 300, id: 0 }, { colour: "#0f0", radius: 400, id: 1 }, { colour: "#f00", radius: 500, id: 2 }]; me.ring = function () { if (this.isInvulnerable) return; ctx.lineWidth = 10; for (const ring of this.rings) { const radius = ring.radius * (1 + 0.3 * Math.sin(simulation.cycle / 60 * (ring.id + 2))); if (Math.abs(distance(player.position, this.position) - radius) < 60 && m.immuneCycle < simulation.cycle) { m.damage(0.4 / radius); } ctx.strokeStyle = ring.colour; DrawTools.arcOut(this.position.x, this.position.y, radius, 0, Math.PI * 2); } } me.horizon = function () { if (this.isInvulnerable) return this.fill = "#f00"; // eventHorizon waves in and out const eventHorizon = this.eventHorizon * (1 + 0.2 * Math.sin(simulation.cycle * 0.008)); const charge = this.attackCycle / 90; this.fill = `rgb(${charge * 255},${charge * 255},${charge * 255})`; // draw darkness ctx.fillStyle = `rgba(${charge * 225},${20 + charge * 195},${40 + charge * 215},0.6)`; DrawTools.arc(this.position.x, this.position.y, eventHorizon * 0.2, 0, 2 * Math.PI); ctx.fillStyle = `rgba(${charge * 225},${20 + charge * 195},${40 + charge * 215},0.4)`; DrawTools.arc(this.position.x, this.position.y, eventHorizon * 0.4, 0, 2 * Math.PI); ctx.fillStyle = `rgba(${charge * 225},${20 + charge * 195},${40 + charge * 215},0.3)`; DrawTools.arc(this.position.x, this.position.y, eventHorizon * 0.6, 0, 2 * Math.PI); ctx.fillStyle = `rgba(${charge * 225},${20 + charge * 195},${40 + charge * 215},0.2)`; DrawTools.arc(this.position.x, this.position.y, eventHorizon * 0.8, 0, 2 * Math.PI); ctx.fillStyle = `rgba(${charge * 255},${charge * 255},${charge * 255},0.05)`; DrawTools.arc(this.position.x, this.position.y, eventHorizon, 0, 2 * Math.PI); // when player is inside event horizon if (V.magnitude(V.sub(this.position, player.position)) < eventHorizon) { // Standard black hole stuff if (m.immuneCycle < m.cycle) { if (m.energy > 0) m.energy -= 0.004; if (m.energy < 0.1) m.damage(0.0002 * simulation.dmgScale); } const angle = Math.atan2(player.position.y - this.position.y, player.position.x - this.position.x); player.force.x -= 0.001 * Math.cos(angle) * player.mass * (m.onGround ? 1.7 : 1); player.force.y -= 0.001 * Math.sin(angle) * player.mass; // draw line to player ctx.lineWidth = Math.min(60, this.radius * 2); ctx.strokeStyle = "rgba(0,0,0,0.5)"; DrawTools.line([this.position, m.pos]); ctx.fillStyle = "rgba(0,0,0,0.3)"; DrawTools.arc(m.pos.x, m.pos.y, 40, 0, 2 * Math.PI); // Lightning attacks this.attackCycle++; if (this.attackCycle >= 90) { this.attackCycle = 0; this.lastAttackCycle = simulation.cycle; Matter.Body.setVelocity(player, V.add(player.velocity, { x: 0, y: -20 })); if (m.immuneCycle < m.cycle) { m.damage(0.012 * simulation.dmgScale); } } const lightningCycle = simulation.cycle * 2 / 3 + this.lastAttackCycle / 3; DrawTools.lightning(this.position, m.pos, lightningCycle, 1, 12); DrawTools.lightning(this.position, m.pos, lightningCycle, 2, 12); ctx.fillStyle = `rgba(255,240,127,${0.12 * Math.max(15 - simulation.cycle + this.lastAttackCycle, 0)})`; DrawTools.arc(m.pos.x, m.pos.y, 40, 0, 2 * Math.PI); } } me.periodicSpawns = function () { if (this.isInvulnerable) return; this.spawnCycle++; // Spawn annoying purple thing(s) that chases the player if (!(this.spawnCycle % 180)) { spawn.seeker(this.position.x, this.position.y, 15 * (0.7 + 0.5 * Math.random()), 7); spawn.seeker(this.position.x, this.position.y, 4 * (0.7 + 0.5 * Math.random()), 7); spawn.seeker(this.position.x, this.position.y, 4 * (0.7 + 0.5 * Math.random()), 7); } // Spawn the annoying pink (now blue) exploder that doesn't chase the player if (!(this.spawnCycle % 300)) { for (let i = 0; i < 3; i++) { mobGrenade(1100 + 700 * i, -13030, undefined, Math.min(700, 300 + simulation.difficulty * 4), 10); setVel(mob[mob.length - 1], { x: 0, y: -10 }); mobGrenade(1100 + 700 * i, -14370, undefined, Math.min(700, 300 + simulation.difficulty * 4), 10); setVel(mob[mob.length - 1], { x: 0, y: 10 }); } } // Spawn a bunch of mobs if (simulation.difficulty > 10 && !(this.spawnCycle % 600)) { // This is just ripped off of spawn.nodeGroup because I don't want the shield const nodes = 3; const radius = Math.ceil(Math.random() * 10) + 18; const sideLength = Math.ceil(Math.random() * 100) + 70; const stiffness = Math.random() * 0.03 + 0.005; spawn.allowShields = false; //don't want shields on individual group mobs const angle = 2 * Math.PI / nodes for (let i = 0; i < nodes; ++i) { spawn.focuser(x + sideLength * Math.sin(i * angle), y + sideLength * Math.cos(i * angle), radius); } spawn.constrainAllMobCombos(nodes, stiffness); spawn.allowShields = true; } } me.invulnerableTrap = function () { if (this.trapCycle < 1) return; this.trapCycle++; // 24 is just an arbitrarily large number const spawnCycles = Math.min(24, Math.max(6, 4 + Math.floor(simulation.difficulty / 3))); // I have no idea how to balance at all, please help me const spawnDelay = Math.floor(5 + 10 / (1 + Math.sqrt(simulation.difficulty) / 5)); if (this.trapCycle >= 90) { const cycle = this.trapCycle - 90; if (!(cycle % spawnDelay)) { const radius = Math.min(500, 200 + simulation.difficulty * 3); mobGrenade(600, -13050, 30, radius); Matter.Body.setVelocity(mob[mob.length - 1], { x: 35, y: 0 }); mobGrenade(3000, -13050, 30, radius); Matter.Body.setVelocity(mob[mob.length - 1], { x: -35, y: 0 }); mobGrenade(600, -14350, 30, radius); Matter.Body.setVelocity(mob[mob.length - 1], { x: 35, y: 0 }); mobGrenade(3000, -14350, 30, radius); Matter.Body.setVelocity(mob[mob.length - 1], { x: -35, y: 0 }); if (Math.floor(cycle / spawnDelay) >= spawnCycles - 1) { this.trapCycle = 0; this.isInvulnerable = false; this.damageReduction = 0.25 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1); } } } ctx.font = "100px Arial"; ctx.fillStyle = "#f00"; ctx.shadowBlur = 10; ctx.shadowColor = "#f00"; ctx.textAlign = "center"; ctx.textBaseLine = "middle"; ctx.fillText("!", 900, -13050); ctx.fillText("!", 900, -14350); ctx.fillText("!", 1800, -13050); ctx.fillText("!", 1800, -14350); ctx.fillText("!", 2700, -13050); ctx.fillText("!", 2700, -14350); ctx.shadowBlur = 0; } me.do = function () { this.checkStatus(); this.horizon(); this.ring(); this.periodicSpawns(); this.invulnerableTrap(); } }; let oldNextLevel = level.nextLevel; const oldFallHeight = simulation.fallHeight; level.nextLevel = () => { color.map = "#444"; m.death = m.oldDeath; canvas.style.filter = ""; level.nextLevel = oldNextLevel; simulation.fallHeight = oldFallHeight; oldNextLevel(); } let bounds = []; let mobPositionsQueue = Array.from(Array(10), () => []); m.oldDeath = m.death; m.death = function () { if (!tech.isImmortal) { requestAnimationFrame(() => color.map = "#444"); m.death = m.oldDeath; canvas.style.filter = ""; level.nextLevel = oldNextLevel; simulation.fallHeight = oldFallHeight; m.oldDeath(); } else { m.switchWorlds(); Matter.Body.setPosition(player, { x: level.enter.x + 50, y: level.enter.y - 20 }); makeLore("How did you not die?"); setTimeout(() => makeLore("I'll let you off this one time."), 2000); tech.isImmortal = false; } } let name = "⥟ᘊ5⪊Ыᳪៗⱕ␥ዘᑧ⍗"; addPartToMap = (len = map.length - 1) => { map[len].collisionFilter.category = cat.map; map[len].collisionFilter.mask = cat.player | cat.map | cat.body | cat.bullet | cat.powerUp | cat.mob | cat.mobBullet; Matter.Body.setStatic(map[len], true); // make static Composite.add(engine.world, map[len]); } level.setPosToSpawn(50, -50); // normal spawn // Make the level exit really far away so WIMP powerups don't show up at all level.exit.x = 1e6; level.exit.y = -1e6; Promise.resolve().then(() => { // Clear all WIMPS and their research for (let i = 0; i < mob.length; i++) { while (mob[i] && !mob[i].isMACHO) { mob[i].replace(i); } } for (let i = 0; i < powerUp.length; i++) { while (powerUp[i] && powerUp[i].name === "research") { Matter.Composite.remove(engine.world, powerUp[i]); powerUp.splice(i, 1); } } level.exit.x = 1500; level.exit.y = -30; }); spawn.mapRect(level.enter.x, level.enter.y + 20, 100, 20); spawn.mapRect(1500, -10, 100, 20); level.defaultZoom = 1800 simulation.setZoom(1200); document.body.style.backgroundColor = "#daa69f"; color.map = "#600"; function box(x, y, w, h, s) { spawn.mapRect(x, y, w, s); spawn.mapRect(x, y, s, h); spawn.mapRect(x + w - s, y, s, h); spawn.mapRect(x, y + h - s, w, s); } function diamond(x, y, s) { spawn.mapVertex(x, y, `0 -${s} -${s} 0 0 ${s} ${s} 0`); } // Fake level bounds.push(new Rect(-200, -500, 2000, 600)); box(-200, -500, 2000, 600, 100); // Actual level, Room 1 const firstRoomBounds = new Rect(-200, -4000, 5000, 2100); bounds.push(firstRoomBounds); box(-200, -4000, 5000, 2100, 100); spawn.mapRect(-200, -2500, 1300, 100); spawn.mapRect(3500, -2500, 1300, 100); spawn.mapRect(-200, -4000, 1000, 1600); spawn.mapRect(3800, -4000, 1000, 1600); // Enter and Exit platforms spawn.mapRect(0, -2010, 100, 20); spawn.mapRect(4500, -2010, 100, 20); // Altar of Room 1 spawn.mapRect(2100, -2200, 100, 300); spawn.mapRect(2400, -2200, 100, 300); spawn.mapRect(2070, -2200, 460, 20); spawn.debris(1700, -2100, 300, 10); spawn.debris(2500, -2100, 300, 10); // Actual level, Room 2 const secondRoomBounds = new Rect(-1500, -10500, 3000, 3600); bounds.push(secondRoomBounds); box(-1500, -10500, 3000, 3600, 100); spawn.mapRect(-2000, -8500, 1600, 1600); spawn.mapRect(400, -8500, 1600, 1600); // Enter and Exit platforms spawn.mapRect(-50, -7010, 100, 20); spawn.mapRect(-50, -10010, 100, 20); // Hazard traversing spawn.mapRect(-300, -7320, 800, 20); spawn.mapRect(175, -7600, 325, 20); spawn.mapRect(200, -7775, 300, 20); spawn.mapRect(-500, -7600, 325, 20); spawn.mapRect(-500, -7775, 300, 20); spawn.mapRect(-500, -7950, 800, 20); spawn.mapRect(-300, -8100, 800, 20); spawn.mapRect(-500, -8250, 800, 20); for (let i = 0; i < 2; i++) spawn.mapRect(-250, -8400 + 150 * i, 500, 60); const room2SlimePit = level.hazard(-400, -8410, 800, 1090); room2SlimePit.logic = function () { if (this.height > 0 && Matter.Query.region([player], this).length) { if (m.immuneCycle < m.cycle) { // Trolled const hasCPT = tech.isRewindAvoidDeath; tech.isRewindAvoidDeath = false; const DRAIN = 0.002 * (tech.isRadioactiveResistance ? 0.25 : 1) + 0.001; if (m.energy > DRAIN && !tech.isEnergyHealth) { m.energy -= DRAIN; } m.damage(0.00015 * (tech.isRadioactiveResistance ? 0.25 : 1)); if (tech.isEnergyHealth) { const previousEnergy = m.energy; m.regenEnergy(); const energyRegenerated = m.energy - previousEnergy; if (energyRegenerated > 0) { m.energy = previousEnergy; m.damage(energyRegenerated); } } tech.isRewindAvoidDeath = hasCPT; } player.force.y -= 0.3 * player.mass * simulation.g; setVel(player, Vector.sub(player.velocity, { x: 0, y: player.velocity.y * 0.02 })); } // Float power ups powerUpCollide = Matter.Query.region(powerUp, this) for (let i = 0, len = powerUpCollide.length; i < len; i++) { const diameter = 2 * powerUpCollide[i].size const buoyancy = 1 - 0.2 * Math.max(0, Math.min(diameter, this.min.y - powerUpCollide[i].position.y + powerUpCollide[i].size)) / diameter powerUpCollide[i].force.y -= buoyancy * 1.14 * powerUpCollide[i].mass * simulation.g; setVel(powerUpCollide[i], { x: powerUpCollide[i].velocity.x, y: 0.96 * powerUpCollide[i].velocity.y }); } } room2SlimePit.draw = function () { if (this.isOn) { ctx.fillStyle = "hsla(160, 100%, 35%, 0.75)"; ctx.fillRect(this.min.x, this.min.y, this.width, this.height); } } // Room 2 spawning bounds const secondRoomSpawnBounds = new Rect(-1500, -10500, 3000, 2000); spawn.mapRect(-700, -8700, 150, 20); spawn.mapRect(550, -8700, 150, 20); spawn.mapRect(-400, -8900, 800, 20); diamond(-600, -9800, 30); diamond(0, -9800, 30); diamond(600, -9800, 30); spawn.mapRect(-1000, -10000, 2000, 30); // Actual level, Room 3 (Final Boss?) const thirdRoomBounds = new Rect(-200, -14500, 4000, 1600); bounds.push(thirdRoomBounds); box(-200, -14500, 4000, 1600, 100); spawn.mapRect(-200, -14500, 800, 1100); spawn.mapRect(3000, -14500, 800, 1100); // Enter and Exit platforms spawn.mapRect(0, -13110, 100, 20); spawn.mapRect(-200, -13100, 800, 200); spawn.mapRect(3500, -13110, 100, 20); spawn.mapRect(3000, -13100, 800, 200); for (let i = 0; i < 4; i++) spawn.bodyRect(570, -13400 + i * 75, 30, 75); for (let i = 0; i < 3; i++) { diamond(1100 + 700 * i, -13000, 30, 30); diamond(1100 + 700 * i, -14400, 30, 30); } const Objects = { altar: { get isHeal() { return simulation.cycle % 600 >= 300; }, pos: { x: 2300, y: -2200 }, get isActive() { const roughPlayerCentre = V.add(m.pos, { x: 0, y: 40 }); return distance(roughPlayerCentre, this.pos) < 240 && (Math.abs(angle(roughPlayerCentre, this.pos) - Math.PI / 2) < 1); }, logic() { if (!this.isActive) return; if (this.isHeal) { m.energy += 0.005; } else { m.energy = Math.max(m.energy - 0.006, 0); if (m.energy <= 0.01 && m.immuneCycle < m.cycle) m.damage(0.002); } }, drawTop() { if (!isInBound(firstRoomBounds)) return; const colour = this.isHeal ? m.fieldMeterColor : "#f00"; DrawTools.flame([2300, -2200, 26, 4, colour], 7); ctx.fillStyle = colour; ctx.fillRect(2200, -2200, 200, 200); }, drawBottom() { ctx.fillStyle = this.isHeal ? "#fff5" : "#0005"; for (const radius of [230, 180, 130, 80, 30]) { DrawTools.arc(2300, -2200, radius, 0, Math.PI, true); } } }, room2Initiator: { pos: { x: 0, y: -9050 }, get distance() { return distance(player.position, this.pos); }, range: 120, rings: [{ colour: [102, 85, 255], radius: 200 }, { colour: [0, 255, 0], radius: 300 }, { colour: [255, 0, 0], radius: 400 }], get ringNumber() { return this.rings.length; }, get cap() { return (this.ringNumber + 1) * 90 + 240; }, get capped() { return templePlayer.room2.spawnInitiatorCycles > this.cap; }, logic() { if (this.distance < this.range) { templePlayer.room2.spawnInitiatorCycles++; } }, draw() { Promise.resolve().then(() => { const cycle = templePlayer.room2.spawnInitiatorCycles; if (!this.capped && this.distance < 400) { ctx.fillStyle = `rgba(0, 0, 0, ${Math.min(1, (400 - this.distance) / (400 - this.range)) * 0.9})`; ctx.fillRect(0, 0, canvas.width, canvas.height); } ctx.save(); simulation.camera(); if (this.distance < this.range && !this.capped) { DrawTools.lightning(V.sub(this.pos, { x: 300, y: 300 }), V.add(this.pos, { x: 300, y: 300 }), simulation.cycle - 5); DrawTools.lightning(V.add(this.pos, { x: -300, y: 300 }), V.add(this.pos, { x: 300, y: -300 }), simulation.cycle - 5); } if (!this.capped && cycle >= this.cap - 200) { const multCoeff = (cycle - this.cap + 200) * 0.4 ctx.translate((Math.random() - 0.5) * multCoeff, (Math.random() - 0.5) * multCoeff); } ctx.shadowBlur = 20; ctx.lineWidth = 12; ctx.strokeStyle = (templePlayer.room2.cycles % 60 < 30) ? "#fff" : "#000"; ctx.shadowColor = (templePlayer.room2.cycles % 60 < 30) ? "#fff" : "#000"; DrawTools.arcOut(this.pos.x, this.pos.y, 100, 0, Math.PI * 2); if (templePlayer.room2.cycles <= 100) { for (let i = 0; i < this.ringNumber; i++) { if (cycle < i * 90 + 90) break; const ring = this.rings[i]; ctx.shadowColor = `rgb(${ring.colour.join(",")})`; const opacity = this.capped ? 1 - 0.01 * templePlayer.room2.cycles : (cycle / 180 - i / 2 - 0.5); ctx.strokeStyle = `rgba(${ring.colour.join(",")}, ${Math.min(opacity, 1)})`; const radius = (this.capped ? 1 + 0.07 * templePlayer.room2.cycles : Math.sin(Math.min(cycle - i * 90 - 90, 45) / 90 * Math.PI)) * ring.radius; DrawTools.arcOut(this.pos.x, this.pos.y, radius, 0, Math.PI * 2); } } ctx.restore(); }); } }, room2Lightning: { one: [{ x: -1400, y: -10400 }, { x: 1400, y: -8500 }], two: [{ x: -1400, y: -8500 }, { x: 1400, y: -10400 }], get isHeal() { return simulation.cycle % 360 < 180; }, get oneEq() { return Equation.fromPoints(this.one[0], this.one[1]); }, get twoEq() { return Equation.fromPoints(this.two[0], this.two[1]); }, logic() { if (!isInBound(secondRoomSpawnBounds) || !templePlayer.room2.cycles) return; const playerbounds = Rect.fromBounds(player.bounds.min, player.bounds.max); if (playerbounds.hasLine(this.oneEq) || playerbounds.hasLine(this.twoEq)) { if (this.isHeal) { m.energy += 0.003; } else if (m.immuneCycle < m.cycle) { m.energy -= 0.003; } } }, draw() { if (!isInBound(secondRoomBounds) || !templePlayer.room2.cycles) return; const colour = this.isHeal ? undefined : [0, 0, 0]; DrawTools.lightning(...this.one, Math.floor(simulation.cycle / 15) * 15, 1, 9, colour); DrawTools.lightning(...this.two, Math.floor(simulation.cycle / 15) * 15, 2, 9, colour); } }, room2GeneratedPath: { rects: (function () { const rects = []; for (let i = 0; i < 4; i++) { rects.push(new Rect(-1405 + (i & 1) * 200, -9700 + i * 300, 205, 30)); rects.push(new Rect(1200 - (i & 1) * 200, -9700 + i * 300, 205, 30)); } return rects; })(), logic() { if (templePlayer.room2.readyPathCycle && simulation.cycle - templePlayer.room2.readyPathCycle === 180) { for (const r of this.rects) { r.addToMap(); addPartToMap(); simulation.draw.setPaths(); } } }, draw() { if (templePlayer.room2.readyPathCycle && simulation.cycle - templePlayer.room2.readyPathCycle < 180) { ctx.fillStyle = "#fe79"; for (const r of this.rects) { ctx.fillRect(r.pos.x, r.pos.y, r.width, r.height); } } else if (simulation.cycle - templePlayer.room2.readyPathCycle < 195) { for (const r of this.rects) { DrawTools.lightning(Objects.room2Initiator.pos, r.midPos, templePlayer.room2.readyPathCycle + 180); } } } }, room3Rotors: { rotor1: (function () { const rotor = level.spinner(900, -13700, 200, 30); rotor.rotate = function () { Matter.Body.setAngularVelocity(this.bodyB, (this.bodyB.angularVelocity + 0.01) * 0.9) } return rotor; })(), rotor2: (function () { const rotor = level.spinner(2700, -13700, 200, 30); rotor.rotate = function () { Matter.Body.setAngularVelocity(this.bodyB, (this.bodyB.angularVelocity - 0.01) * 0.9) } return rotor; })(), logic() { this.rotor1.rotate(); this.rotor2.rotate(); } }, room3SlimePits: { pit1: level.hazard(-100, -13400, 0, 0, 0.004), pit2: level.hazard(3700, -13400, 0, 0, 0.004), logic() { if (templePlayer.room2ToRoom3Anim >= 1320 && templePlayer.room2ToRoom3Anim <= 1570) { this.pit1.height = this.pit2.height = 300; this.pit1.max.y = this.pit2.max.y = -13100; this.pit1.width = this.pit2.width = templePlayer.room2ToRoom3Anim * 2 - 2640; this.pit1.max.x = this.pit1.min.x + this.pit1.width; this.pit2.min.x = this.pit2.max.x - this.pit2.width; } if (templePlayer.room3ToEndAnim) { this.pit1.height = this.pit1.width = 0; this.pit2.height = this.pit2.width = 0; } }, draw() { this.pit1.query(); this.pit2.query(); } } }; let templePlayer = { room1: { cycles: 300 }, room2: { spawnInitiatorCycles: 0, cycles: 0, readyPathCycle: 0 }, stage: 1, startAnim: 0, room1ToRoom2Anim: 0, room2ToRoom3Anim: 0, room3ToEndAnim: 0, initialTrapY: 0, clearedCycle: 0, drawExit: true }; const RoomTransitionHandler = { room0() { if (templePlayer.startAnim <= 0) return; templePlayer.startAnim++; if (templePlayer.startAnim == 120) { makeLore("Not so fast."); } if (templePlayer.startAnim < 360) { trapPlayer(1000, templePlayer.initialTrapY); } else { level.exit.x = 4500; level.exit.y = -2030; relocateTo(50, -2050); simulation.fallHeight = -1000; simulation.setZoom(1800); templePlayer.startAnim = -1; templePlayer.drawExit = false; } }, room1() { if (templePlayer.room1ToRoom2Anim <= 0) return; if (templePlayer.room1ToRoom2Anim === 1) { level.exit.x = -50; level.exit.y = -10030; makeLore("Pathetic."); } if (templePlayer.room1ToRoom2Anim === 121) { makeLore("You will never succeed."); } if (templePlayer.room1ToRoom2Anim >= 360 && templePlayer.room1ToRoom2Anim <= 720) { const factor = 200 - 200 * Math.cos((templePlayer.room1ToRoom2Anim / 120 - 3) * Math.PI); ctx.translate(factor, factor); Promise.resolve().then(() => { ctx.save(); ctx.globalCompositeOperation = "color-burn"; ctx.fillStyle = DrawTools.randomColours; DrawTools.updateRandomColours(5); ctx.fillRect(0, 0, canvas.width, canvas.height); ctx.restore(); }); } if (templePlayer.room1ToRoom2Anim === 720) { makeLore("You are trying too hard."); relocateTo(0, -7050); simulation.fallHeight = -6000; templePlayer.stage = 2; } if (templePlayer.room1ToRoom2Anim === 960) { makeLore("I have mastered the understandings of the universe."); } if (templePlayer.room1ToRoom2Anim === 1200) { // Congrats, you discovered the actual words by looking at the source code. Are you happy now? const x = ( ["a speck of dust", "an insignificant hindrance", "a tiny obstacle"] )[Math.floor(Math.random() * 3)].split(""); for (let i = 0; i < x.length / 1.6; i++) { const randomIndex = Math.floor(Math.random() * x.length); if (x[randomIndex] !== " ") { x[randomIndex] = String.fromCharCode(Math.floor(Math.random() * 50) + 192); } }; makeLore(`You are no more than ${x.join("")} to me.`); relocateWIMPs(0, -10030); } templePlayer.room1ToRoom2Anim++; }, room2() { if (templePlayer.room2ToRoom3Anim <= 0) return; if (templePlayer.room2ToRoom3Anim === 1) { level.exit.x = 3500; level.exit.y = -13130; makeLore("Do not try me."); } if (templePlayer.room2ToRoom3Anim === 180) { makeLore("I have absolute power over you."); canvas.style.filter = "hue-rotate(90deg)"; } if (templePlayer.room2ToRoom3Anim === 360) { makeLore("You will not succeed..."); canvas.style.filter = "invert(0.2)"; } if (templePlayer.room2ToRoom3Anim === 420) { makeLore("
...
"); canvas.style.filter = "invert(0.4)"; } if (templePlayer.room2ToRoom3Anim > 480 && templePlayer.room2ToRoom3Anim <= 660) { canvas.style.filter = `sepia(${(templePlayer.room2ToRoom3Anim - 480) / 180}) invert(${0.5 + (templePlayer.room2ToRoom3Anim - 480) / 180})`; } if (templePlayer.room2ToRoom3Anim === 780) { makeLore("Do not interfere with me."); templePlayer.stage = 3; relocateTo(50, -13150); simulation.fallHeight = -10000; simulation.zoomTransition(1800); templePlayer.startAnim = -1; // Might be a bit harsh to the player if the WIMPs are involved in the third level for (let i = 0; i < mob.length; i++) { while (mob[i] && !mob[i].isWIMP) { mob[i].replace(i); } } } if (templePlayer.room2ToRoom3Anim > 780 && templePlayer.room2ToRoom3Anim <= 960) { canvas.style.filter = `sepia(${(960 - templePlayer.room2ToRoom3Anim) / 180}) invert(${(960 - templePlayer.room2ToRoom3Anim) / 180})`; } templePlayer.room2ToRoom3Anim++; }, room3() { if (templePlayer.room3ToEndAnim <= 0) return; if (templePlayer.room3ToEndAnim === 1) { makeLore("No."); } if (templePlayer.room3ToEndAnim === 120) { makeLore("This cannot be."); } if (templePlayer.room3ToEndAnim === 240) { makeLore("Has my power failed me?"); } if (templePlayer.room3ToEndAnim === 360) { makeLore("Was it worth it, destroying this place?"); } if (templePlayer.room3ToEndAnim === 600) { makeLore("No one is greater than me."); } const text = "noone-"; for (let i = 0; i < 12; i++) { if (templePlayer.room3ToEndAnim === 720 + i * 20) { name = name.slice(0, -1); simulation.makeTextLog(`${name}:   ${text[i % 6]}`); canvas.style.filter = `brightness(${1 - i / 22})`; } } if (templePlayer.room3ToEndAnim === 1060) { templePlayer.drawExit = true; for (let i = 0; i < 5 * tech.wimpCount; i++) { powerUps.spawn(level.exit.x + 100 * (Math.random() - 0.5), level.exit.y - 100 + 100 * (Math.random() - 0.5), "research", false); } canvas.style.filter = ""; } templePlayer.room3ToEndAnim++; }, end() { if (!templePlayer.clearedCycle) return; Promise.resolve().then(() => { ctx.save(); ctx.setTransform(1, 0, 0, 1, 0, 0); ctx.fillStyle = `rgba(0, 0, 0, ${(simulation.cycle - templePlayer.clearedCycle) / 300})`; ctx.fillRect(0, 0, canvas.width, canvas.height); ctx.restore(); }); if (simulation.cycle - templePlayer.clearedCycle > 420) level.nextLevel(); } }; const LogicHandler = { bounds() { let isInBounds = false; for (const b of bounds) { if (isInBound(b)) { isInBounds = true; break; } } if (!isInBounds) { m.damage(0.1 * simulation.difficultyMode); trapPlayer(level.enter.x, level.enter.y); simulation.makeTextLog("" + name + ":   You thought I could let you get away with that?"); } }, room0() { // Block the player from entering the first seemingly innocuous exit if ((m.pos.x > 1000) && templePlayer.startAnim === 0) { spawn.mapRect(1200, -500, 100, 600); templePlayer.initialTrapY = Math.min(player.position.y, -75); trapPlayer(1000, templePlayer.initialTrapY); addPartToMap(); simulation.draw.setPaths(); templePlayer.startAnim = 1; } }, room1() { WaveHandler.room1(); Objects.altar.logic(); }, room2() { room2SlimePit.logic(); Objects.room2Initiator.logic(); Objects.room2Lightning.logic(); Objects.room2GeneratedPath.logic(); WaveHandler.room2(); }, room3() { Objects.room3Rotors.logic(); Objects.room3SlimePits.logic(); WaveHandler.room3(); }, exit() { if (!templePlayer.drawExit) return; if (player.position.x > level.exit.x && player.position.x < level.exit.x + 100 && player.position.y > level.exit.y - 150 && player.position.y < level.exit.y - 40 && player.velocity.y < 0.1 && level.exitCount + (input.down ? 8 : 2) > 100) { if (templePlayer.stage === 1) { templePlayer.drawExit = false; level.exitCount = 0; templePlayer.room1ToRoom2Anim = 1; } else if (templePlayer.stage === 2) { templePlayer.drawExit = false; templePlayer.room2ToRoom3Anim = 1; level.exitCount = 0; } else { level.exitCount = 99 - (input.down ? 8 : 2); if (!templePlayer.clearedCycle) templePlayer.clearedCycle = simulation.cycle; } } } }; const DrawHandler = { // Bottom layer base() { // Draw base red background ctx.save(); ctx.setTransform(1, 0, 0, 1, 0, 0); ctx.fillStyle = color.map; ctx.fillRect(0, 0, canvas.width, canvas.height); ctx.restore(); // Draw the normal bg on the bounds ctx.fillStyle = "#eab6af"; for (const b of bounds) { if (isInBound(b)) ctx.fillRect(b.pos.x + 2, b.pos.y + 2, b.width - 4, b.height - 4); } }, 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 = "#fca"; ctx.fill(); }, room1() { if (!isInBound(firstRoomBounds)) return; // Draw Cross ctx.fillStyle = "#fed"; ctx.fillRect(2200, -3300, 200, 800); ctx.fillRect(2000, -3100, 600, 200); // Final boss-like spawn fire thing. Was it necessary? No! const spawnFlameAngle = Math.min(Math.min(templePlayer.room1.cycles, 2520) % 600, 120) * Math.PI / 30 + Math.PI / 2; DrawTools.flame([2300, -3000, 26, 4, "#f60", spawnFlameAngle], 7); Objects.altar.drawBottom(); }, room2() { if (!isInBound(secondRoomBounds)) return; if (templePlayer.room2.cycles) { ctx.fillStyle = "#0006"; ctx.fillRect(secondRoomBounds.pos.x + 2, secondRoomBounds.pos.y + 2, secondRoomBounds.width - 4, secondRoomBounds.height - 4); } room2SlimePit.draw(); }, room3() { if (!isInBound(thirdRoomBounds)) return; ctx.fillStyle = "#0006"; ctx.fillRect(thirdRoomBounds.pos.x + 2, thirdRoomBounds.pos.y + 2, thirdRoomBounds.width - 4, thirdRoomBounds.height - 4); Objects.room3SlimePits.draw(); }, // Top layer mobTrails() { if (simulation.cycle % 4 === 0) { let newMobPositions = []; for (const i of mob) { if (!(i.isMACHO || i.isWIMP || i.isObstacle)) newMobPositions.push({ x: i.position.x, y: i.position.y }); } mobPositionsQueue.shift(); mobPositionsQueue.push(newMobPositions); } // Draw "holy" trails for mobs for no particular reason at all ctx.strokeStyle = "#bae"; ctx.lineWidth = 3; for (let i = 0; i < 10; i++) { const p = mobPositionsQueue[i]; for (const m of p) { DrawTools.holy(m.x, m.y, i / 2 + 10); } } ctx.shadowBlur = 0; }, waveTimer() { const roomConditions = [ isInBound(firstRoomBounds) && templePlayer.room1.cycles < 2400, isInBound(secondRoomBounds) && templePlayer.room2.cycles > 0 && templePlayer.room2.cycles < 2160, isInBound(thirdRoomBounds) && templePlayer.room2ToRoom3Anim < 1320 ]; Promise.resolve(roomConditions).then(roomConditions => { // First Room if (roomConditions[0]) { ctx.save(); ctx.setTransform(1, 0, 0, 1, 0, 0); ctx.fillStyle = "#0004"; ctx.fillRect(canvas.width2 - 288, 50, 576, 20); ctx.fillStyle = "#0cf"; ctx.fillRect(canvas.width2 - 288, 50, 0.96 * (600 - templePlayer.room1.cycles % 600), 20); ctx.restore(); } // Second Room if (roomConditions[1]) { ctx.save(); ctx.setTransform(1, 0, 0, 1, 0, 0); ctx.fillStyle = "#0004"; ctx.fillRect(canvas.width2 - 288, 50, 576, 20); ctx.fillStyle = (Math.ceil(templePlayer.room2.cycles / 720) & 1) ? "#000" : "#e1d7ff"; ctx.fillRect(canvas.width2 - 288, 50, 0.8 * (720 - templePlayer.room2.cycles % 720), 20); ctx.restore(); } // Third Room if (roomConditions[2]) { ctx.save(); ctx.setTransform(1, 0, 0, 1, 0, 0); ctx.fillStyle = "#0004"; ctx.fillRect(canvas.width2 - 288, 50, 576, 20); ctx.fillStyle = "#000"; ctx.fillRect(canvas.width2 - 288, 50, 1.6 * (1320 - templePlayer.room2ToRoom3Anim), 20); ctx.restore(); } }); }, room2Top() { if (!isInBound(secondRoomBounds)) return; Objects.room2Lightning.draw(); Objects.room2GeneratedPath.draw(); Objects.room2Initiator.draw(); } }; const WaveHandler = { room1() { if (!isInBound(firstRoomBounds)) return; if (templePlayer.room1.cycles === 0) powerUps.spawnStartingPowerUps(0, -2050); templePlayer.room1.cycles++; if (templePlayer.room1.cycles === 2400) { spawn.secondaryBossChance(2300, -2800); powerUps.addResearchToLevel(); } if (templePlayer.room1.cycles % 600 === 0 && templePlayer.room1.cycles <= 2400) { const spawnAmt = Math.min(3 + Math.pow(simulation.difficulty / 1.7, 0.6), 10) + Math.floor(templePlayer.room1.cycles / 720); for (let i = 0; i < spawnAmt; i++) { if (Math.random() < 0.5 + 0.07 * simulation.difficulty) { spawn.randomMob(800 + Math.random() * 3e3, -2400 - Math.random() * 600, Infinity); } } spawn.randomMob(800 + Math.random() * 3e3, -2400 - Math.random() * 600, Infinity); } if (templePlayer.room1.cycles === 2520) { templePlayer.drawExit = true; } }, room2() { if (!isInBound(secondRoomBounds)) return; if (templePlayer.room2.spawnInitiatorCycles > Objects.room2Initiator.cap) { const randomSecondRoomBoss = [secondRoomSuckerBoss, secondRoomPlacerBoss]; if (templePlayer.room2.cycles % 720 === 0 && templePlayer.room2.cycles <= 2160) { const isOdd = Math.floor(templePlayer.room2.cycles / 720) & 1; randomSecondRoomBoss[Math.floor(randomSecondRoomBoss.length * Math.random())](-600, -9800, isOdd); randomSecondRoomBoss[Math.floor(randomSecondRoomBoss.length * Math.random())](600, -9800, isOdd); randomSecondRoomBoss[Math.floor(randomSecondRoomBoss.length * Math.random())](0, -9800, !isOdd); } templePlayer.room2.cycles++; if (templePlayer.room2.cycles === 2400) { templePlayer.drawExit = true; templePlayer.room2.readyPathCycle = simulation.cycle; } } }, room3() { if (templePlayer.room2ToRoom3Anim === 1320) { thirdRoomBoss(1800, -13700); for (let i = 0; i < 3; i++) { powerUps.spawn(m.spawnPos.x, m.spawnPos.y, "heal"); } } } }; const DrawTools = { get randomColours() { return `rgb(${this._randomColours.join(",")})` }, _randomColours: [Math.random() * 255, Math.random() * 255, Math.random() * 255], updateRandomColours(x = 0.8) { for (let i = 0; i < this._randomColours.length; i++) { this._randomColours[i] = Math.max(Math.min(this._randomColours[i] + (this.randFact() * x * 2) - x, 255), 0); } }, randFact() { return Math.random() * 0.8 + Math.sin(Date.now() / 300) * 0.2; }, line(vecs) { ctx.beginPath(); ctx.moveTo(vecs[0].x, vecs[0].y); for (const v of vecs.slice(1)) ctx.lineTo(v.x, v.y); ctx.stroke(); }, arc(...x) { ctx.beginPath(); ctx.arc(...x); ctx.fill(); }, arcOut(...x) { ctx.beginPath(); ctx.arc(...x); ctx.stroke(); }, flame(props, repeat) { for (let i = 0; i < repeat; i++) this.singleFlame(...props); }, singleFlame(x, y, size = 10, repeat = 3, color = "#f00", angle = Math.PI / 2) { ctx.strokeStyle = color; ctx.lineWidth = 3; const path = [{ x, y }]; for (let i = 0; i < repeat; i++) { const randAng = (Math.random() - 0.5) * 2 + angle; const randLen = 2 * size + Math.random() * size; x += Math.cos(randAng) * randLen; y -= Math.sin(randAng) * randLen; path.push({ x, y }) } DrawTools.line(path); }, lightning(from, to, cycle, randomPRNGMult = 1, width = 8, color = [255, 240, 127]) { const diff = simulation.cycle - cycle; if (diff >= 15) return; ctx.strokeStyle = `rgba(${color.join(",")},${(1 - diff / 15) * 255})`; ctx.lineWidth = width * (1 - diff / 15); ctx.shadowColor = `rgb(${color.join(",")})`; ctx.shadowBlur = 20; const path = [{ x: from.x, y: from.y }]; let vector = { x: from.x, y: from.y }; let distanceLeft = V.magnitude(V.sub(from, to)); const d = distanceLeft > 800 ? distanceLeft / 40 : 20; const normalised = V.normalise(V.sub(to, from)); while (1) { const randOffset = rotateVector({ y: RNG(Math.floor(cycle * randomPRNGMult + distanceLeft)) * 2 * d - d, x: 0 }, normalised); const randLen = RNG(Math.floor(cycle * (randomPRNGMult + 1) + distanceLeft)) * d + d; distanceLeft -= randLen; if (distanceLeft <= 0) { path.push({ x: to.x, y: to.y }); break; } vector = V.add(vector, V.mult(normalised, randLen)); path.push({ x: vector.x + randOffset.x, y: vector.y + randOffset.y }); } DrawTools.line(path); ctx.shadowBlur = 0; }, holy(x, y, size = 12) { this.line([{ x, y: y - size }, { x: x - size, y }, { x, y: y + size }, { x: x + size, y }, { x, y: y - size } ]); } }; function RNG(x) { x += Math.seed; let start = Math.pow(x % 97, 4.3) * 232344573; const a = 15485863; const b = 521791; start = (start * a) % b; for (let i = 0; i < (x * x) % 90 + 90; i++) { start = (start * a) % b; } return start / b; } function rotateVector(v, ang) { const c = typeof ang === "number" ? { x: Math.cos(ang), y: Math.sin(ang) } : V.normalise(ang); return { x: v.x * c.x - v.y * c.y, y: v.y * c.x + v.x * c.y }; } function trapPlayer(x, y) { setPosAndFreeze(player, { x, y }); const bLen = bullet.length; for (let i = 0; i < bLen; i++) { if (bullet[i].botType) setPosAndFreeze(bullet[i], V.add(player.position, { x: 100 * (RNG(i) - 0.5), y: 100 * (RNG(i + bLen) - 0.5) })); } } function relocateTo(x, y) { level.setPosToSpawn(x, y); trapPlayer(x, y); for (let i = 0; i < mob.length; i++) { if (mob[i].isMACHO) { setPos(mob[i], { x, y }); break; } } m.resetHistory(); } const distance = (a, b) => V.magnitude(V.sub(a, b)); const angle = (a, b) => Math.atan2(b.y - a.y, a.x - b.x); const setPos = (a, b) => Matter.Body.setPosition(a, b); const setVel = (a, b) => Matter.Body.setVelocity(a, b); const freeze = a => setVel(a, { x: 0, y: 0 }); const setPosAndFreeze = (a, b) => { setPos(a, b); freeze(a); }; const makeLore = (x, t) => simulation.makeTextLog(`

${name}:

 

${x}

`, t); level.custom = () => { // All the logic gets handled here. How nice! for (const i in LogicHandler) { LogicHandler[i](); } // Animations and lore for things that seem like exits for (const i in RoomTransitionHandler) { RoomTransitionHandler[i](); } // Bottom layer graphics DrawHandler.base(); DrawHandler.room1(); DrawHandler.room2(); DrawHandler.room3(); DrawHandler.entrance(); if (templePlayer.drawExit) level.exit.drawAndCheck(); }; level.customTopLayer = () => { // Top layer graphics DrawHandler.mobTrails(); Objects.altar.drawTop(); DrawHandler.waveTimer(); DrawHandler.room2Top(); }; }, dripp() { simulation.makeTextLog(`dripp by M. B.`); const door = level.door(780, -350, 15, 400, 265); const buttonDoor = level.button(420, -10); const boost = level.boost(130, -445); const hazard = level.hazard(690, -1050, 10, 700, 0.4) const hazard2 = level.hazard(2470, -1515, 162, 14, 0.4) const hazard3 = level.hazard(740, -1050, 10, 700, 0.4) const hazard4 = level.hazard(3400, -380, 350, 6, 0.2) const hazard5 = level.hazard(3425, -1420, 400, 8, 0.2) const slimePit = level.hazard(2250, -100, 2700, 200, 0.004) const door2 = level.door(3131, -898, 40, 520, 522) const buttonDoor2 = level.button(2495, -270) const toggle = level.toggle(1463, -708, true) const elevator = level.elevator(4310, -150, 200, 50, -1443, 0.0025, { up: 0.1, down: 0.2 }) const portal = level.portal({ //main portals x: 2117, y: -1560 }, -2 * Math.PI, { //up x: -80, y: -475 }, -Math.PI / 100) //up const drip1 = level.drip(4100 + 1000 * Math.random(), -1900, 50, 100) // drip(x, yMin, yMax, period = 100, color = "hsla(160, 100%, 35%, 0.5)") { const drip2 = level.drip(4100 + 1000 * Math.random(), -1900, 50, 207) // drip(x, yMin, yMax, period = 100, color = "hsla(160, 100%, 35%, 0.5)") { const drip3 = level.drip(4100 + 1000 * Math.random(), -1900, 50, 133) // drip(x, yMin, yMax, period = 100, color = "hsla(160, 100%, 35%, 0.5)") { const drip4 = level.drip(4100 + 1000 * Math.random(), -1900, 50, 157) // drip(x, yMin, yMax, period = 100, color = "hsla(160, 100%, 35%, 0.5)") { level.custom = () => { level.exit.drawAndCheck(); drip1.draw() drip2.draw() drip3.draw() drip4.draw() buttonDoor.query(); buttonDoor.draw(); if (buttonDoor.isUp) { door.isClosing = true } else { door.isClosing = false } door.openClose(); buttonDoor2.query(); buttonDoor2.draw(); if (buttonDoor2.isUp) { door2.isClosing = true } else { door2.isClosing = false } door2.openClose(); // shadow/shades builds ctx.fillStyle = "rgba(0, 0, 0, 0.05)" ctx.fillRect(3169, -900, 891, 580) ctx.fillRect(417, -1057, 380, 730) ctx.fillRect(930, -515, 207, 520) ctx.fillRect(930, -1280, 207, 760) ctx.fillRect(1220, -1280, 54, 800) ctx.fillRect(1221, -1394, 451, 1398) ctx.fillRect(1924, -800, 219, 674) ctx.fillRect(2264, -1488, 214, 1550) ctx.fillRect(2631, -1488, 201, 1550) ctx.fillRect(2889, -930, 237, 1090) ctx.fillRect(3124, -311, 957, 360) ctx.fillRect(1919, -1480, 220, 700) // ctx.fillRect(1768, -1200, 71, 500) level.enter.draw(); elevator.move(); toggle.query(); }; level.customTopLayer = () => { boost.query(); hazard.opticalQuery(); hazard2.opticalQuery(); hazard3.opticalQuery(); hazard4.opticalQuery(); hazard5.opticalQuery(); slimePit.query(); // slimePit.draw(); hazard.isOn = toggle.isOn hazard3.isOn = toggle.isOn portal[0].draw(); portal[1].draw(); portal[2].draw(); portal[2].query() portal[3].query() }; level.setPosToSpawn(0, -50); //normal spawn level.exit.x = 1400; level.exit.y = -1500; spawn.mapRect(level.enter.x, level.enter.y + 20, 100, 20); level.defaultZoom = 1800 simulation.zoomTransition(level.defaultZoom) document.body.style.backgroundColor = "#d8dadf"; //builds spawn.mapRect(-100, 0, 1485, 100); spawn.mapRect(-279, -750, 200, 850); spawn.mapRect(1781, -125, 375, 75); spawn.mapRect(1670, -100, 590, 200); spawn.mapRect(2261, 50, 3100, 50); spawn.mapRect(2420, -260, 300, 50); spawn.bodyRect(235, -240, 50, 50, 1, spawn.propSLide) spawn.mapRect(410, -1100, 400, 50); spawn.mapRect(1220, -1470, 420, 80) spawn.mapRect(927, -1325, 220, 50); spawn.mapRect(4950, -200, 425, 375); spawn.bodyRect(5200, -300, 100, 100); //random builds spawn.mapRect(2150, 50, 225, 50); //large border walls spawn.mapRect(-300, -2375, 6075, 475); spawn.mapRect(-951, -2374, 675, 2476); spawn.mapRect(-950, 100, 6950, 500); spawn.mapRect(5300, -2374, 700, 2700); // create enemies spawn.randomMob(3000, -300, 0.5); spawn.randomMob(1900, -1000, 0.5); spawn.randomMob(2960, -800, 0.6) spawn.randomMob(3500, -1700, 0.4) spawn.randomMob(800, -1700, 0.6) spawn.randomMob(100, -1150, 0.6) spawn.randomMob(1095, -700, 0.6) //powerUps powerUps.spawn(590, -200, "ammo") powerUps.spawn(600, -200, "heal") // powerUps.spawn(590, -200, "gun") powerUps.spawnStartingPowerUps(590, -200); // more builds spawn.blockDoor(1230, -1490) // spawn.blockDoor(728, -1130); spawn.mapRect(-100, -380, 900, 50); spawn.mapRect(-279, -1400, 200, 650); spawn.mapRect(-279, -1900, 200, 650); // spawn.mapRect(-100, -1900, 2300, 75); // spawn.mapRect(2200, -1900, 1025, 75); // spawn.mapRect(2700, -1900, 2000, 75); spawn.mapRect(2270, -1530, 199, 50); spawn.mapRect(2633, -1530, 199, 50) // spawn.mapRect(4570, -1825, 125, 1925); spawn.mapRect(3106, -400, 300, 50) spawn.mapRect(3750, -400, 330, 50) spawn.mapRect(3130, -1030, 930, 130); spawn.mapRect(4015, -900, 46, 275); spawn.blockDoor(4016, -400) spawn.mapRect(3168, -1440, 290, 50); spawn.mapRect(3771, -1440, 294, 50); spawn.mapRect(3106, -355, 974, 42); spawn.mapRect(3228, -1395, 834, 380); spawn.mapRect(3129, -1350, 100, 325); spawn.mapRect(3129, -1400, 175, 100); spawn.mapRect(3129, -1437, 125, 75); spawn.mapRect(1382, 0, 295, 100); spawn.mapRect(1600, -50, 560, 85); spawn.mapRect(2264, -945, 220, 50); spawn.mapRect(1925, -800, 220, 50); spawn.mapRect(1390, -700, 260, 50); spawn.mapRect(927, -520, 220, 50); spawn.mapRect(2894, -948, 300, 50) spawn.mapRect(1230, -1825, 440, 81); spawn.mapRect(1616, -1750, 54, 360); spawn.mapRect(3128, -1440, 50, 50); spawn.mapRect(1705, -120, 125, 75); spawn.mapRect(1550, -25, 150, 50); spawn.mapRect(1628, -75, 100, 50); spawn.mapRect(1729, -130, 650, 75); //ground for blue portal spawn.mapRect(1917, -1484, 300, 50); spawn.mapRect(1917, -1950, 200, 325); spawn.mapRect(1917, -1825, 50, 375); //split spawn.mapRect(1221, -1420, 57, 465); spawn.mapRect(1221, -634, 57, 450); spawn.bodyRect(1227, -105, 42, 189, 1, spawn.propSlide) // spawn.mapRect(1770, -1900, 70, 750); spawn.mapRect(1770, -780, 70, 400) spawn.bodyRect(1783, -289, 38, 250, 1, spawn.propSlide) if (simulation.difficulty > 1) spawn.randomLevelBoss(4800, -750); spawn.secondaryBossChance(4700, -1500) powerUps.addResearchToLevel() //needs to run after mobs are spawned }, biohazard() { // MAP BY INOOBBOI AND THESHWARMA simulation.makeTextLog(`biohazard by INOOBBOI and THESHWARMA`); // set here for the cutscene later level.setPosToSpawn(-2800, -150) // set up cutscenes simulation.cutscene = (locations, speed, stay, xPos = m.pos.x, yPos = m.pos.y) => { // locations: an array of location vectors, reversed for the pop ahead locations.reverse() // speed: the speed of the cutscene transition (0 to 1) // stay: how much to stay in the destination (ticks) // xPos & yPos: the initial location, also used as the current location // start by disabling the default camera draw. Don't worry, it's backed up const camera = simulation.camera // create a new camera function simulation.camera = () => { ctx.save() ctx.translate(canvas.width2, canvas.height2) //center ctx.scale(simulation.zoom, simulation.zoom) const xScaled = canvas.width2 - xPos const yScaled = canvas.height2 - yPos ctx.translate(-canvas.width2 + xScaled, -canvas.height2 + yScaled) //translate } // and set a restoring function const restore = () => (simulation.camera = camera) // then choose the next destination. There should be always at least one destination, // if there isn't there's no point checking, the game should and will crash let dest = locations.pop() // animate the camera const lerp = (first, second, percent) => first * (1 - percent) + second * percent const speedDelta = speed / 5 // wait timer let wait = 0 // polls the animation, should be called every tick const poll = () => { // update position xPos = lerp(xPos, dest.x, speedDelta) yPos = lerp(yPos, dest.y, speedDelta) // if position is close enough, wait and go to the next position const TOO_CLOSE = 100 if (Math.abs(dest.x - xPos) < TOO_CLOSE && Math.abs(dest.y - yPos) < TOO_CLOSE) { // wait for a bit if (++wait > stay) { // if there is another target, reset the wait timer and go there // otherwise end the cutscene wait = 0 if (!(dest = locations.pop())) { // no more locations! End restore() return true } } } // early return if the player skips by fielding if (input.field) { restore() return true } return false } return poll } const boost1 = level.boost(-1400, -100, 900) const boost2 = level.boost(500, -900, 2500) const boost3 = level.boost(4200, -100, 900) const boost4 = level.boost(2200, -900, 2500) const toggle = level.toggle(1340, -600, false, true) let bossInit = false const cutscenePoll = simulation.cutscene([{ x: 230, y: -2700 }, { x: 3500, y: -1400 }, { x: 1450, y: -1150 }, m.pos], 0.1, 10) let hasEnded = false // ** PROPS ** // create some drips const rndInRange = (min, max) => Math.random() * (max - min) + min const amount = Math.round(5 + 20 * Math.random()) const drips = [] for (let i = 0; i < amount; i++) { const locX = rndInRange(-2000, 4800) drips.push(level.drip(locX, -3100, 1500, 200 + Math.random() * 500)) } // a barrel of radioactive waste, which can drop ammo and heals const barrelMob = (x, y, dirVector) => { const MAX_WIDTH = 150 const personalWidth = MAX_WIDTH / 2 mobs.spawn(x, y, 4, personalWidth, 'rgb(232, 191, 40)') const me = mob[mob.length - 1] // steal some vertices const betterVertices = Matter.Bodies.rectangle(x, y, personalWidth, personalWidth * 1.7).vertices me.vertices = betterVertices me.collisionFilter.mask = cat.player | cat.map | cat.body | cat.mob | cat.bullet me.g = simulation.g me.leaveBody = me.isDropPowerUp = false me.do = function () { this.gravity() // apply shock damage when touching the map, if it's fast if (this.speed > 5) { const collision = Matter.Query.collides(this, map) if (collision.length > 0) { // on collision reduce health this.health = this.health - this.speed / 250 // die when it's too low, doesn't register for some reason } } // becomes more radioactive as it gets damaged! this.fill = `rgb(${232 * this.health}, 191, 40)` } me.onDeath = function () { const END = Math.floor(input.down ? 10 : 7) const totalBullets = 10 const angleStep = (input.down ? 0.4 : 1.3) / totalBullets let dir = m.angle - (angleStep * totalBullets) / 2 for (let i = 0; i < totalBullets; i++) { //5 -> 7 dir += angleStep const me = bullet.length bullet[me] = Bodies.rectangle( this.position.x + 50 * Math.cos(this.angle), this.position.y + 50 * Math.sin(this.angle), 17, 4, b.fireAttributes(dir) ) const end = END + Math.random() * 4 bullet[me].endCycle = 2 * end + simulation.cycle const speed = (25 * end) / END const dirOff = dir + (Math.random() - 0.5) * 3 Matter.Body.setVelocity(bullet[me], { x: speed * Math.cos(dirOff), y: speed * Math.sin(dirOff) }) bullet[me].onEnd = function () { b.explosion( this.position, 150 + (Math.random() - 0.5) * 40 ) //makes bullet do explosive damage at end } bullet[me].beforeDmg = function () { this.endCycle = 0 //bullet ends cycle after hitting a mob and triggers explosion } bullet[me].do = function () { } Composite.add(engine.world, bullet[me]) //add bullet to world } // barrels drop a ton of ammo and some heals, scales up with difficulty because I have mercy const amount = ~~(5 * Math.random() * simulation.difficulty / 10) for (let i = 0; i < amount; i++) { powerUps.spawn(this.position.x, this.position.y, 'ammo', true) if (Math.random() > 0.7) { powerUps.spawn(this.position.x, this.position.y, 'heal', true) } } } Matter.Body.rotate(me, Math.random() * Math.PI) Matter.Body.setVelocity(me, dirVector) } // creates a platform with shadow const platformShadow = (x, y, width, height, shadowList) => { // faster than making manual shadows... Why not just calculate them semi-realsitically? // the shadows are calculated on the object creation, so if you add map blocks it won't update. // shadowList is an array of shadows that'll be rendered. When the platform shadow is ready, // it is added to the list. // some helper functions first const perpCollision = point => { // takes a point, and finds a collision with the map downwards // the end of the ray, 3000 units down const lowerPoint = Vector.add(point, { x: 0, y: 3000 }) // the destination point. If a collision was not found, then it defaults to some // arbiterary point 3000 units down. let dest = lowerPoint for (const mapBody of map) { const check = simulation.checkLineIntersection(point, lowerPoint, mapBody.vertices[0], mapBody.vertices[1]) // a collision was found if (check.onLine1 && check.onLine2) { dest = { x: check.x, y: check.y } break } } return dest } const boundsToRectangle = (firstBound, secondBound) => { // takes two bounds and returns an (x, y, width, height) rectangle. The first one // must be the top left, and the second one must be the bottom right // sub to find the width and height const width = Math.abs(firstBound.x - secondBound.x) const height = Math.abs(firstBound.y - secondBound.y) // compile to an object return { x: firstBound.x, y: firstBound.y, width, height } } // create the actual platform spawn.mapRect(x, y, width, height) const me = map[map.length - 1] // the bottom vertices are the third and fourth ones const first = me.vertices[3] const second = me.vertices[2] // cast shadows to find the last shadow location. // iterate over all map objects, and check for collisions between a perpendicular ray // cast from the vertex down to the map object's top panel // const firstDown = perpCollision(first) // not needed in a rectangle settings const secondDown = perpCollision(second) // possible TODO: make it multirect for efficiency // create a single rectangle and return shadowList.push(boundsToRectangle(first, secondDown)) } // cages with mobs, One of them holds the boss pre mutation const cage = (x, y, maxChainLength, drawList, mobType = null, isTheBoss = false) => { // the drawList is an array that the drawing function is added to // if the cage containing the boss it has a 50% chance to just not spawn. Spices things a bit if (!isTheBoss && Math.random() > 0.5) { return } if (!mobType) { // if mob type is null, then it picks a random mob mobType = spawn.fullPickList[~~(Math.random() * spawn.fullPickList.length)] } // create the chain length, must take into account the radius of the mob. // therefore, it'll create a pseudo mob of that type, take it radius and instantly kill it const chainLength = maxChainLength / 5 + maxChainLength * Math.random() // spawn and insantly kill a mob of the same type to get the radius. // this is used to prevent spawning the mob too short, it's a horrible // solution but it works spawn[mobType](0, 0) mob[mob.length - 1].leaveBody = mob[mob.length - 1].isDropPowerUp = false const radius = mob[mob.length - 1].radius mob[mob.length - 1].alive = false // spawn the mob. Disable shields first spawn.allowShields = false spawn[mobType](x, y + chainLength + radius * 2) const trappedMob = mob[mob.length - 1] // destroy its mind so it won't attack trappedMob.do = () => { } // spawn the cage mobs.spawn(x, y + chainLength + radius * 2, 4, trappedMob.radius + 50, 'rgba(150, 255, 150, 0.3)') const cage = mob[mob.length - 1] cage.g = simulation.g cage.do = function () { this.gravity() } // label it cage.label = 'Cage' // a special orb when hit let damageTick = 0 cage.onDamage = (dmg) => { // add some damage ticks, if the trapped mob is still alive. // activating the boss by this method is almost impossible, since you need 10x damage if (trappedMob.alive) damageTick += ~~(isTheBoss ? 5 * dmg : 50 * dmg) } // collision filter trappedMob.collisionFilter.mask = cage.collisionFilter.mask = cat.player | cat.map | cat.bullet // constrain together spawn.constrain2AdjacentMobs(2, 0.05, false) // move them to be together trappedMob.position = Vector.clone(cage.position) // make sure you clone... Otherwise........ // invincibility, make homing bullets not hit these, remove health bar trappedMob.health = cage.health = Infinity trappedMob.isBadTarget = cage.isBadTarget = true trappedMob.showHealthBar = cage.showHealthBar = false trappedMob.leaveBody = trappedMob.isDropPowerUp = cage.leaveBody = trappedMob.isDropPowerUp = false // cross all edges of the cage with the rope, and see where it collides. Attach the rope there const verts = cage.vertices // the crossing location, doesn't stay null let cross = null for (let i = 0; i < verts.length; i++) { // iterate over all vertices to form lines const v1 = verts[i] const v2 = verts[(i + 1) % verts.length] const result = simulation.checkLineIntersection(cage.position, { x, y }, v1, v2) if (result.onLine1 && result.onLine2) { // both lines cross! cross = result break } } if (!cross) { // for some odd reason, sometimes it never finds a collision. I have no idea why // just default to the center then console.error("Couldn't find a cross... Origin: ", { x, y }, " center: ", cage.position, ' vertices: ', cage.vertices) cross = cage.position } // create the rope const rope = Constraint.create({ pointA: { x, y }, // offset the point be in the attachment point pointB: Vector.sub(cross, cage.position), bodyB: cage, // the longer the rope, the looser it is stiffness: Math.max(0.0005 - chainLength / 10000000, 0.00000001), length: chainLength }) Matter.Composite.add(engine.world, rope) // create and return a function for drawing the rope const draw = () => { // draw a little recantagle at the base ctx.fillStyle = color.map ctx.fillRect(x - 20, y - 5, 40, 25) // if the cage was destroyed... Do nothing beyond if (!cage.alive) { return } // draw the rope ctx.beginPath() ctx.moveTo(x, y) // line to the crossing point // ctx.lineTo(cons[i].bodyB.position.x, cons[i].bodyB.position.y); ctx.lineTo(cage.position.x + rope.pointB.x, cage.position.y + rope.pointB.y); ctx.lineWidth = 7 ctx.strokeStyle = 'rgba(0, 0, 0, 0.5)' ctx.stroke() // now draw a mystic hit orb if touched if (damageTick) damageTick-- // reduce the ticks ctx.beginPath() ctx.arc(cage.position.x, cage.position.y, cage.radius + 30, 0, Math.PI * 2) ctx.lineWidth = 10 ctx.fillStyle = `rgba(255, 0, 0, ${Math.min(1, damageTick / 2000)})` ctx.strokeStyle = `rgba(255, 100, 0, ${Math.min(1, damageTick / 1000)})` ctx.setLineDash([125 * Math.random(), 125 * Math.random()]) ctx.stroke() ctx.setLineDash([]) ctx.fill() // if it's the boss, draw sucking arcs if (isTheBoss && bossInit) { for (const entity of mob) { // suck the health of all mobs // I hate string manipulation in control flow but heh if (entity.alive) { ctx.beginPath() ctx.moveTo(entity.position.x, entity.position.y) ctx.lineTo(trappedMob.position.x, trappedMob.position.y) ctx.lineWidth = 10 ctx.strokeStyle = 'rgba(38, 0, 255, 0.67)' ctx.stroke() // damage the mob entity.damage(1) // damage itself bonus cage.damage(1) } } cage.damage(5) } // ok if it's too much, explode if (damageTick > 2000) { b.explosion(cage.position, cage.radius * 10) // die a silent death trappedMob.alive = cage.alive = false damageTick = 0 if (isTheBoss) { // become the real boss geneticBoss(trappedMob.position.x, trappedMob.position.y) } } } // restore the shields spawn.allowShields = true // add the drawing function drawList.push(draw) } // platform shadows const shadows = [] // cages const cages = [] level.custom = () => { level.exit.drawAndCheck() //draws the exit level.enter.draw() //draws the entrance player.force.y -= player.mass * simulation.g * 0.25 //this gets rid of some gravity on player // if the cutscene is yet to end, continue polling if (!hasEnded) { hasEnded = cutscenePoll() } for (const drip of drips) drip.draw() // throw some barrels after the boss spawns if (Math.random() > 0.999 && bossInit && !hasEnded) { const spawnLocs = [-1415, -30, 1345, 2815, 4285] // const randVec = Vector.mult({ x: Math.cos(randAngle), y: Math.sin(randAngle) }, Math.random() * 15) barrelMob(spawnLocs[~~(spawnLocs.length * Math.random())], -4200, { x: 0, y: 0 }) } // platform shadow ctx.beginPath() for (const shadow of shadows) { ctx.rect(shadow.x, shadow.y, shadow.width, shadow.height) } ctx.fillStyle = 'rgba(0,10,30,0.1)' ctx.fill() // player pressed lever if (toggle.isOn && !bossInit) { bossInit = true } // draw the cage } //for dynamic stuff that updates while playing that is one Z layer below the player level.customTopLayer = () => { boost1.query() boost2.query() boost3.query() boost4.query() toggle.query() // shadow holes ctx.fillStyle = 'rgba(68, 68, 68,0.95)' ctx.fillRect(-1450 - 10, -4350, 150 + 20, 1250) ctx.fillRect(-50 - 10, -4350, 150 + 20, 1250) ctx.fillRect(1325 - 10, -4350, 150 + 20, 1250) ctx.fillRect(2800 - 10, -4350, 150 + 20, 1250) ctx.fillRect(4275 - 10, -4350, 150 + 20, 1250) for (const drawCage of cages) { drawCage() } } //for dynamic stuff that updates while playing that is one Z layer above the player const anotherBoss = (x, y) => { if (tech.isDuplicateMobs && Math.random() < tech.duplicationChance()) { tech.isScaleMobsWithDuplication = true spawn.historyBoss(x, y) tech.isScaleMobsWithDuplication = false } else if (tech.isResearchBoss) { if (powerUps.research.count > 2) { powerUps.research.changeRerolls(-3) simulation.makeTextLog( `m.research -= 3
${powerUps.research.count}` ) } else { tech.addJunkTechToPool(0.49) } spawn.historyBoss(x, y) } } //GENETICBOSS function drawEnergyBar(mob) { if (mob.seePlayer.recall && mob.energy > 0) { const h = mob.radius * 0.3 const w = mob.radius * 2 const x = mob.position.x - w / 2 const y = mob.position.y - w * 0.9 ctx.fillStyle = 'rgba(100, 100, 100, 0.3)' ctx.fillRect(x, y, w, h) ctx.fillStyle = '#0cf' ctx.fillRect(x, y, w * mob.energy, h) } } function generateGenome() { // creates a random genome and returns it const genome = { density: Math.random() * 0.001, size: 15 + Math.random() * 15, speed: Math.random() * 0.1, // color and vertex properties are "trash" genes as they don't really contribute to the orb color: [Math.random() * 255, Math.random() * 255, Math.random() * 255, 50 + Math.random() * 205], vertexCount: Math.floor(Math.random() * 5) + 3, // TODO fix possible concaving vertexOffset: null // placeholder } // initialized here as it depends on vertexCount. I could use `new function()` but nah. genome.vertexOffset = Array(genome.vertexCount) .fill() .map(() => ({ x: Math.random() - 0.5, y: Math.random() - 0.5 })) return genome } function mutateGenome(genome) { // takes an existing genome and applies tiny changes const randomInRange = (min, max) => Math.random() * (max - min) + min const tinyChange = x => randomInRange(-x, x) const vertexMutator = x => ({ x: x.x + tinyChange(0.5), y: x.y + tinyChange(0.5) }) // mutates a genome and returns the mutated version. const newGenome = { density: genome.density + tinyChange(0.0005), size: genome.size + tinyChange(5), speed: genome.speed + tinyChange(0.05), color: genome.color.map(x => (x + tinyChange(10)) % 255), // wrap around vertexCount: Math.max(genome.vertexCount + Math.round(tinyChange(1)), 3), vertexOffset: genome.vertexOffset.map(vertexMutator) } if (genome.vertexOffset.length < newGenome.vertexCount) { const vo = newGenome.vertexOffset vo.push(vertexMutator(vo[~~(vo.length * Math.random())])) } else if (genome.vertexOffset.length > newGenome.vertexCount) { newGenome.vertexOffset.pop() } return newGenome } function calculateGenomeCost(genome) { // calculates the cost of a genome and returns it. The cost is used to // determine how "costly" the genome is to make, and after the orb's life ends it // is used together with the orb success score to determine the fittest orb. const score = (1 / (genome.density * genome.size * genome.speed)) * 0.000001 return score } // distance functions const dist2 = (a, b) => (a.x - b.x) ** 2 + (a.y - b.y) ** 2 const dist = (a, b) => Math.sqrt(dist2(a, b)) // ** MAP SPECIFIC MOBS ** function energyTransferBall(origin, target, boss, charge) { // transports energy to the boss // when the boss is hit by it, how much of the energy stored the boss actually recives const ENERGY_TRANSFER_RATE = 80 /*%*/ // add 1 to the active ball list boss.activeBalls++ const color = `rgba(${150 + 105 * charge}, 81, 50, 0.6)` mobs.spawn(origin.x, origin.y, 12, 20 + 20 * charge, color) const me = mob[mob.length - 1] me.end = function () { simulation.drawList.push({ // some nice graphics x: this.position.x, y: this.position.y, radius: this.radius, color: '#f3571d', time: ~~(Math.random() * 20 + 10) }) // on death spawn and explode a bomb if (Math.random() > 0.95) { spawn.bomb(this.position.x, this.position.y, this.radius, this.vertices.length) mob[mob.length - 1].death() } // remove 1 from the active ball list boss.activeBalls-- this.death() } me.collisionFilter.mask = cat.player | cat.map // me.onHit = this.end me.life = 0 me.isDropPowerUp = false me.leaveBody = false me.do = function () { // die on collision with the map if (Matter.Query.collides(this, map).length > 0) { this.end() } // die if too much time passes. Stronger bullets explode earlier if (++this.life > 200 - charge * 100) { this.end() } // if the orb collides with the boss, die but give energy to the boss if (Matter.Query.collides(this, [boss]).length > 0) { boss.energy = Math.min(charge * (ENERGY_TRANSFER_RATE / 100) + boss.energy, 1) // also make the boss fire once regardless of energy boss.spawnOrbs() this.end() } const movement = Vector.normalise(Vector.sub(target, origin)) Matter.Body.setVelocity(this, { x: this.velocity.x + movement.x, y: this.velocity.y + movement.y }) // nice graphics simulation.drawList.push({ x: this.position.x, y: this.position.y, radius: this.radius, color: '#e81e1e', time: 3 }) simulation.drawList.push({ x: this.position.x, y: this.position.y, radius: this.radius, color: '#e87f1e', time: 6 }) simulation.drawList.push({ x: this.position.x, y: this.position.y, radius: this.radius, color: '#e8e41e', time: 9 }) } me.onDamage = me.end } function energyBeacon(x, y, parentBoss) { // an unmoving beacon that charges the genetic boss with energy either stolen // from the player or generated. That energy is used to create stronger mobs. mobs.spawn(x, y, 3, 50, '') // default color, changed an instant later const me = mob[mob.length - 1] me.laserRange = 500 me.leaveBody = false me.isDropPowerUp = false // custom variables // max energy is 1 me.energy = 0 me.seed = simulation.cycle // seed to make sure this mob is unique render wise me.chargeTicks = 0 // used to time charging the boss me.bossPos = null // the position that the mob remembers when charging me.density = me.density * 2 Matter.Body.setDensity(me, 0.0022 * 3 + 0.0002 * Math.sqrt(simulation.difficulty)) //extra dense me.do = function () { // if the boss is dead, die if (!parentBoss.alive) { this.death() } // slowly rotate Matter.Body.setAngularVelocity(this, 0.01) this.fill = `rgba(${this.energy * 255}, 29, 136, 0.80)` this.seePlayerCheck() // steal energy from player // this.harmZone() // regular harmZone // custom effects on top of that if (this.distanceToPlayer() < this.laserRange) { if (m.immuneCycle < m.cycle) { // suck extra energy from the player if it's in range if (m.energy > 0.1 && this.energy < 1 - 0.012) { m.energy -= 0.012 this.energy += 0.012 } // special "sucking" graphics ctx.beginPath() ctx.moveTo(this.position.x, this.position.y) ctx.lineTo(m.pos.x, m.pos.y) ctx.lineWidth = 3 + Math.abs(Math.sin((simulation.cycle + this.seed) / 100)) * 2 ctx.strokeStyle = `rgb(${( Math.abs(Math.sin((simulation.cycle + this.seed + 100) / 100)) * 255 ).toFixed(3)}, 204, 255)` ctx.setLineDash([125 * Math.random(), 125 * Math.random()]) ctx.stroke() ctx.setLineDash([]) } } // if the mob's energy is at least 50% full, try to send that energy to the boss. // don't send that energy yet if more than 5 other transfer balls are active if (this.energy > 0.5 && parentBoss.energy < 1 && parentBoss.activeBalls <= 5 && this.chargeTicks === 0) { const seesBoss = Matter.Query.ray(map, this.position, parentBoss.position).length === 0 if (seesBoss) { this.chargeTicks = 100 this.bossPos = Vector.clone(parentBoss.position) } } if (this.chargeTicks > 0) { if (--this.chargeTicks === 0) { // spawn the orb const location = Vector.add( Vector.mult(Vector.normalise(Vector.sub(this.bossPos, this.position)), this.radius * 3), this.position ) energyTransferBall(location, this.bossPos, parentBoss, this.energy) this.energy = 0 } // create a beam and aim it at the bossPos ctx.beginPath() ctx.moveTo(this.position.x, this.position.y) ctx.lineTo(this.bossPos.x, this.bossPos.y) ctx.lineWidth = 10 + Math.abs(Math.sin((simulation.cycle + this.seed) / 100)) * 5 ctx.strokeStyle = `rgb(${( Math.abs(Math.sin((simulation.cycle + this.seed + 100) / 100)) * 255 ).toFixed(3)}, 204, 255)` ctx.setLineDash([125 * Math.random(), 125 * Math.random()]) ctx.stroke() ctx.setLineDash([]) } // generate (0.15 * difficulty / 4)% energy per tick if (this.energy < 1) this.energy += 0.0015 * (simulation.difficulty / 4) // draw energy bar drawEnergyBar(this) } me.onDeath = function () { // remove itself from the list const beacons = parentBoss.energyBeacons beacons.splice(beacons.indexOf(this), 1) // explode with the strength of its energy! this.alive = false // to prevent retriggering infinitly b.explosion(this.position, this.energy * this.radius * 15) // when it dies, it reduces some of the boss' energy parentBoss.energy -= 0.025 // and stuns it mobs.statusStun(parentBoss, 70 + ~~(100 / simulation.difficulty)) } } function geneticSeeker(x, y, genome, parentBoss) { // special bullets that get score based on their performance. mobs.spawn(x, y, genome.vertexCount, genome.size, '#' + genome.color.map(it => (~~it).toString(16)).join('')) const me = mob[mob.length - 1] // apply genome Matter.Body.setDensity(me, genome.density) me.accelMag = genome.speed // apply vertex offset for (let i = 0; i < me.vertices.length; i++) { const vertex = me.vertices[i] const offset = genome.vertexOffset[i] if (!offset) console.log(genome, me) vertex.x += offset.x vertex.y += offset.y } me.stroke = 'transparent' Matter.Body.setDensity(me, 0.00001) //normal is 0.001 // increased if the orb done things that are deemed successful me.score = 30 me.timeLeft = 9001 / 9 me.accelMag = 0.00017 * simulation.accelScale //* (0.8 + 0.4 * Math.random()) me.frictionAir = 0.01 me.restitution = 0.5 me.leaveBody = false me.isDropPowerUp = false me.isBadTarget = true me.isMobBullet = true me.showHealthBar = false me.collisionFilter.category = cat.mobBullet me.collisionFilter.mask = cat.player | cat.map | cat.body | cat.bullet me.do = function () { this.alwaysSeePlayer() this.attraction() this.timeLimit() if (Matter.Query.collides(this, map).length > 0) { // colliding with the map gives a score reduction, 0.5 per tick this.score -= 0.5 } // default score is slowly reduced every tick to give mobs that reached the player faster a benefit this.score -= 0.05 if (this.score < 0) { this.alive = false // no point continuing if this orb is that bad! Silent death } // give a bonus if some projectile is nearby or the mouse position is close (like laser pointing) // if a mob survives this for long, then it gets a score benefit. const bulletCloseToOrb = bullet.some(it => dist2(this.position, it.position) < 10000 /* 100 ^ 2 */) // player shoots and aims close const mouseCloseToOrb = dist2(this.position, simulation.mouseInGame) < 10000 && input.fire if (bulletCloseToOrb || mouseCloseToOrb) { this.score += 1 } // die if too far from the boss... It would be incredibly difficult to dodge otherwise if (dist2(this.position, parentBoss.position) > 2000 * 2000) { this.alive = false } // DEBUG score printer // ctx.font = '48px sans-serif' // ctx.fillStyle = 'rgba(252, 0, 143, 1)' // ctx.fillText(~~this.score, this.position.x - this.radius, this.position.y - this.radius) } me.onHit = function () { // hitting the player gives a 50 points score bonus this.score += 50 this.score += this.mass * 2 // bigger mass = bigger damage, add that too // a marker for later this.hitPlayer = true this.explode(this.mass) } me.onDeath = function () { if (!this.hitPlayer) { // if it didn't hit the player, give it a score based on its distance this.score += 10000 / this.distanceToPlayer() } // 3% chance to drop ammo if (Math.random() > 0.97) { powerUps.spawn(this.position.x, this.position.y, 'ammo', true) } parentBoss.deadOrbs.push({ genome: genome, score: this.score }) } } function geneticBoss(x, y, radius = 130, spawnBossPowerUp = true) { // a modified orb shooting boss that evolves its orbs. // the way this boss works is different from the regular orb shooting boss, // because the orbs have evolving properties via a "machine learning" scoring algorithm. const MAX_BEACONS = Math.round(3 + Math.random() * simulation.difficulty / 3) mobs.spawn(x, y, 8, radius, 'rgb(83, 32, 58)') let me = mob[mob.length - 1] me.isBoss = true me.accelMag = 0.0001 * simulation.accelScale me.fireFreq = Math.floor((330 * simulation.CDScale) / simulation.difficulty) me.frictionStatic = 0 me.friction = 0 me.frictionAir = 0.02 me.memory = (420 / 69) * 42 // 🧌 me.repulsionRange = 1000000 me.energyBeacons = [] me.activeBalls = 0 // starts by random, or by the stored genomes if they exist const init = () => ({ genome: generateGenome(), score: 0 }) me.fittestOrbs = (localStorage && localStorage.genome) ? JSON.parse(localStorage.genome) : [init(), init(), init()] // best genomes so far. Size of three // when an orb died it's moved here. When a new spawn cycle starts, their scores get calculated // and they get put in the fittest orbs array, if they are better than the old ones. me.deadOrbs = [] me.energy = 1 // this boss has no orbitals, because it's not meant to ever attack on its own me.damageReduction = 0.25 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1) // has a shield and sustains that shield spawn.shield(me, x, y, Infinity) me.fireFreq = 30 me.ionizeFreq = 20 me.ionized = [] me.laserRange = radius * 4 Matter.Body.setDensity(me, 0.0022 * 4 + 0.0002 * Math.sqrt(simulation.difficulty)) //extra dense //normal is 0.001 //makes effective life much larger me.onDeath = function () { if (spawnBossPowerUp) { powerUps.spawnBossPowerUp(this.position.x, this.position.y) const amount = ~~(5 * Math.random() * simulation.difficulty / 10) * 2 for (let i = 0; i < amount; i++) { powerUps.spawn(this.position.x, this.position.y, 'ammo', true) if (Math.random() > 0.7) { powerUps.spawn(this.position.x, this.position.y, 'heal', true) } } } // keep the best genome and use it next fight... if (localStorage) { localStorage.setItem("genome", JSON.stringify(this.fittestOrbs)) } // stop spawning barrels bossInit = false } me.onDamage = function () { } me.spawnBeacon = function () { // the vertex to spawn the beacon from const vert = this.vertices[~~(Math.random() * this.vertices.length)] // the position should be a little to the side to prevent crashing into the boss // TODO check for collisions with the wall const spawnPos = Vector.add(vert, Vector.mult(Vector.normalise(Vector.sub(this.position, vert)), -60)) // some velocity const velocity = Vector.mult(Vector.normalise(Vector.sub(this.position, vert)), -5) energyBeacon(spawnPos.x, spawnPos.y, this) // spawn the beacon, a bit ahead const beacon = mob[mob.length - 1] this.energyBeacons.push(beacon) Matter.Body.setVelocity(beacon, { x: this.velocity.x + velocity.x, y: this.velocity.y + velocity.y }) } me.spawnOrbs = function () { Matter.Body.setAngularVelocity(this, 0.11) // sort the vertices by the distance to the player const sorted = [...this.vertices].sort(dist2) // spawn the bullets based on how close they are to the player. // the way it works is it picks the fittest three orbs and clones them. // but start by taking old mobs and checking if they are better than the new ones let next while ((next = this.deadOrbs.pop())) { // material costs are calculated as a contra to the score const cost = calculateGenomeCost(next.genome) * 500 // normalize via multiplication const totalScore = next.score - cost // try to insert itself into the best orbs, if it can for (let i = 0; i < this.fittestOrbs.length; i++) { const fitEntry = this.fittestOrbs[i] if (fitEntry.score < totalScore) { this.fittestOrbs[i] = next break } } } // finally sort them using their score this.fittestOrbs.sort((a, b) => a.score - b.score) // only take the genome, the score doesn't matter here const bestOrbs = this.fittestOrbs.map(it => it.genome) for (let vertex of sorted) { // pick a random fit orb and try to spawn it. If the cost is too high, it'll attempt // to generate a new random orb instead. If that orb is too expensive too, just ignore this vertex. // the evolution part comes here, as the genome is mutated first. let randGenome = mutateGenome(bestOrbs[~~(Math.random() * bestOrbs.length)]) const cost = calculateGenomeCost(randGenome) * 2 if (this.energy - cost < 0) { // okay this orb is too expensive for the boss to spawn, // make a new orb from scratch randGenome = generateGenome() const secondCost = calculateGenomeCost(randGenome) if (this.energy - secondCost < 0) { // that was too expensive too, heh continue } } else { // alright the boss can afford that this.energy -= Math.abs(cost) // TODO: Fix this, why the heck can it even be negative?? } geneticSeeker(vertex.x, vertex.y, randGenome, this) // give the bullet a rotational velocity as if they were attached to a vertex const velocity = Vector.mult( Vector.perp(Vector.normalise(Vector.sub(this.position, vertex))), -10 ) Matter.Body.setVelocity(mob[mob.length - 1], { x: this.velocity.x + velocity.x, y: this.velocity.y + velocity.y }) } } me.do = function () { this.seePlayerCheck() this.checkStatus() this.attraction() this.repulsion() // draw laser arcs if it sees the player this.harmZone() // const regularChance = Math.random() > 0.99 const biggerChance = Math.random() > 0.95 && this.energy > 0.25 // start by making sure there is always at least one beacon if (this.energyBeacons.length === 0) { this.spawnBeacon() } // then, spawn some energy beacons if there are less than the maximum. // small chance if there's no energy, bigger chance if there is at least 10% (which is drained) if ((this.energyBeacons.length < MAX_BEACONS && biggerChance) || regularChance) { if (biggerChance) { // if the spawn was a selection of bigger chance, reduce 10% energy this.energy -= 0.10 } this.spawnBeacon() } // then, spawn genetic seekers if (this.seePlayer.recall && !(simulation.cycle % this.fireFreq)) { // fire a bullet from each vertex if there's enough energy if (this.energy > 0.15) { this.spawnOrbs() } } if (this.energy > 1) { // clean excess energy this.energy -= 0.003 } else { // or slowly generate energy this.energy += 0.001 } // the boss will ionize every bullet in its radius, but that will cause its energy to deplete if (!(simulation.cycle % this.ionizeFreq)) { for (let i = 0; i < bullet.length; i++) { const it = bullet[i] // if it's not a bot and it's close if (!it.botType && dist(this.position, it.position) < this.laserRange) { // add it to the ionized list this.ionized.push({ target: it, ticks: 0 }) } } } for (let i = 0; i < this.ionized.length; i++) { const entry = this.ionized[i] // skip if there's not enough energy if (this.energy <= 0) break // terminate if it's no longer in the radius if (dist(this.position, entry.target.position) > this.laserRange) { this.ionized.splice(i, 1) continue } // terminate after some ticks if (++entry.ticks === 10) { entry.target.endCycle = 0 // draw nice popping graphics simulation.drawList.push({ x: entry.target.position.x, y: entry.target.position.y, radius: 5, color: '#f24', time: ~~(Math.random() * 20 + 10) }) // and remove this.ionized.splice(i, 1) continue } // draw line ctx.beginPath() ctx.moveTo(this.position.x, this.position.y) ctx.lineTo(entry.target.position.x, entry.target.position.y) ctx.lineWidth = 7 ctx.strokeStyle = `rgb(${60 - entry.ticks * 2}, 50, 50)` ctx.stroke() // reduce energy, as it's hard to ionize this.energy -= entry.target.mass / 25 } // if it has energy, shield itself and drain energy if (!this.isShielded && this.energy > 0.5) { spawn.shield(this, this.position.x, this.position.y, Infinity) this.energy -= 0.25 } drawEnergyBar(this) // change fill color this.fill = `rgb(${((Math.sin(simulation.cycle / 100) + 1) / 2) * 100}, 32, 58)` } // start by spawning several beacons to gain initial energy const amount = Math.ceil(2 + Math.random() * simulation.difficulty / 5) for (let i = 0; i < amount; i++) me.spawnBeacon() } // LEVEL SETUP spawn.mapRect(level.enter.x, level.enter.y + 20, 100, 20) //don't change this level.exit.x = 5700 //you exit at x level.exit.y = -130 //you exit at y spawn.mapRect(5800, -110, -100, 10) level.defaultZoom = 2000 //how far out you want the image to be zoomed at (lower = zoom in, higher = zoom out) simulation.zoomTransition(level.defaultZoom) //makes the level transition to have the zoom at the start of a level document.body.style.backgroundColor = 'hsl(138, 3%, 74%)' //sets background color //LEVEL STRUCTURE spawn.mapRect(-3100, -100, 9200, 100) spawn.mapRect(-3100, -600, 100, 500) spawn.mapRect(-3100, -600, 1100, 100) spawn.mapRect(-2100, -3100, 100, 2700) spawn.mapRect(4800, -3100, 100, 2600) spawn.mapRect(4800, -600, 1300, 100) spawn.mapRect(6000, -600, 100, 600) spawn.mapRect(400, -200, 2000, 100) spawn.mapRect(600, -300, 1600, 100) spawn.mapRect(800, -400, 1200, 100) spawn.mapRect(1000, -500, 800, 100) spawn.mapRect(1200, -600, 400, 100) // roof spawn.mapRect(-2100, -4350, 650, 1250) spawn.mapRect(-1300, -4350, 1250, 1250) spawn.mapRect(100, -4350, 1225, 1250) spawn.mapRect(1475, -4350, 1325, 1250) spawn.mapRect(2950, -4350, 1325, 1250) spawn.mapRect(4425, -4350, 475, 1250) // arc // spawn.mapVertex(1400, -892, '700, -800, 700, -900, 1000, -1000, 1800, -1000, 2100, -900, 2100, -800') //PLATFORMS platformShadow(-1200, -500, 300, 100, shadows) platformShadow(-400, -700, 300, 100, shadows) platformShadow(400, -900, 300, 100, shadows) platformShadow(-2000, -800, 300, 100, shadows) platformShadow(-1000, -1000, 300, 100, shadows) platformShadow(-400, -1300, 300, 100, shadows) platformShadow(-1600, -1300, 300, 100, shadows) platformShadow(-1300, -1600, 300, 100, shadows) platformShadow(-2000, -1700, 300, 100, shadows) platformShadow(-700, -1800, 300, 100, shadows) platformShadow(-1500, -2100, 300, 100, shadows) platformShadow(-600, -2200, 300, 100, shadows) platformShadow(-2000, -2500, 300, 100, shadows) platformShadow(-1100, -2400, 300, 100, shadows) platformShadow(-500, -2700, 300, 100, shadows) platformShadow(100, -2400, 300, 100, shadows) platformShadow(700, -2700, 300, 100, shadows) platformShadow(3700, -500, 300, 100, shadows) platformShadow(2900, -700, 300, 100, shadows) platformShadow(2100, -900, 300, 100, shadows) platformShadow(4500, -800, 300, 100, shadows) platformShadow(3500, -1000, 300, 100, shadows) platformShadow(4100, -1300, 300, 100, shadows) platformShadow(2900, -1300, 300, 100, shadows) platformShadow(3800, -1600, 300, 100, shadows) platformShadow(4500, -1700, 300, 100, shadows) platformShadow(3200, -1800, 300, 100, shadows) platformShadow(4000, -2100, 300, 100, shadows) platformShadow(3100, -2200, 300, 100, shadows) platformShadow(4500, -2500, 300, 100, shadows) platformShadow(3600, -2400, 300, 100, shadows) platformShadow(3000, -2700, 300, 100, shadows) platformShadow(2400, -2400, 300, 100, shadows) platformShadow(1800, -2700, 300, 100, shadows) // cages cage(-1492, -1200, 100, cages) cage(-875, -2300, 300, cages) cage(-1600, -3100, 1000, cages) cage(225, -2300, 1000, cages) cage(-750, -3100, 700, cages) cage(-625, -1700, 1200, cages) cage(2200, -3100, 500, cages) cage(3275, -1700, 500, cages) cage(3650, -900, 300, cages) cage(2500, -2300, 300, cages) cage(3625, -2300, 300, cages) cage(3875, -1500, 300, cages) cage(4025, -3100, 300, cages) // boss cage platformShadow(1275, -2150, 250, 100, shadows) cage(1400, -2050, 500, cages, 'starter', true) map[map.length] = Bodies.trapezoid(1400, -2193, 250, 100, 0.5) //DEBRIS //idk just put the debris wherever you want spawn.debris(-550, -225, 100) spawn.debris(-1150, -1725, 75) spawn.debris(-275, -1400, 50) spawn.debris(2850, -2075, 150) spawn.debris(4250, -2250, 150) //BOSS // geneticBoss(1400, -3800) anotherBoss(0, 0) //will only spawn historyBoss if there is an additional boss }, stereoMadness() { simulation.makeTextLog(`stereoMadness by Richard0820`); let totalCoin = 0; const hunter = function (x, y, radius = 30) { //doesn't stop chasing until past 105000 mobs.spawn(x, y, 6, radius, "black"); let me = mob[mob.length - 1]; me.stroke = "transparent"; me.collisionFilter.mask = cat.player | cat.bullet; me.accelMag = 0.0006 * Math.min(simulation.difficulty + 1, 4); me.showHealthBar = false; me.isUnblockable = true; me.isShielded = true; me.memory = Infinity; me.seeAtDistance2 = Infinity; Matter.Body.setDensity(me, 1) simulation.makeTextLog(`Ω: Intruder Detected`); me.boost = 10; me.do = function () { if (me.boost == 1 && m.fieldMode == 3 || m.fieldMode == 9 && me.boost == 1) { me.accelMag *= 1.5; me.boost--; } this.attraction(); this.checkStatus(); this.repelBullets(); this.locatePlayer(); this.alwaysSeePlayer() if (player.position.x > 105000) { this.death() } }; me.onHit = function () { for (let i = 0; i < 10; i++) { spawn.spawns(this.position.x + Math.random() * 1000 - Math.random() * 1000, this.position.y - Math.random() * 1000) } } } const coin = function (x, y, radius = 50) { mobs.spawn(x, y, 40, radius, "yellow"); let me = mob[mob.length - 1]; me.stroke = "#00000055" me.isShielded = true; me.leaveBody = false; me.isBadTarget = true; me.isUnblockable = true; me.isDropPowerUp = false; me.showHealthBar = false; me.collisionFilter.category = 0; Matter.Body.setDensity(me, 0.0045); me.onDeath = function () { totalCoin++; }; me.damageReduction = 0.35 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1) me.do = function () { ctx.save() ctx.translate(this.position.x, this.position.y) ctx.rotate(Math.PI / 2 + 0.5) ctx.strokeStyle = "#000000"; ctx.beginPath() ctx.arc(0, 0, 30, -1, Math.PI, false) ctx.moveTo(20, -5) ctx.arc(0, 0, 20, -1, Math.PI, false) ctx.lineWidth = 5; ctx.stroke() ctx.restore() if (!simulation.isTimeSkipping) { const sine = Math.sin(simulation.cycle * 0.015) this.radius = 50 * (1 + 0.1 * sine) const sub = Vector.sub(player.position, this.position) const mag = Vector.magnitude(sub) const force = Vector.mult(Vector.normalise(sub), 0.000000003) if (mag < this.radius) { //heal player when inside radius if (m.health < 0.7) { m.damage(-0.001); } else if (m.health == 0.7 || m.health > 0.7) { this.death() } ctx.strokeStyle = "#00FFDD55"; ctx.beginPath(); ctx.arc(m.pos.x, m.pos.y, 34, 0, 2 * Math.PI); ctx.lineWidth = 6; ctx.stroke(); } ctx.beginPath(); ctx.arc(this.position.x, this.position.y, this.radius + 15, 0, 2 * Math.PI); ctx.strokeStyle = "#000" ctx.lineWidth = 1; ctx.stroke(); }; } } const spike = function (x, y, angle = Math.PI * 0.5, radius = 50) { mobs.spawn(x, y, 3, radius, "#454545"); let me = mob[mob.length - 1]; me.stroke = "transparent"; me.isDropPowerUp = false; me.showHealthBar = false; Matter.Body.setDensity(me, 50) me.collisionFilter.mask = cat.player | cat.mob | cat.bullet; me.constraint = Constraint.create({ pointA: { x: me.position.x, y: me.position.y }, bodyB: me, stiffness: 0, damping: 0 }); me.do = function () { if (this.health < 1) { this.health += 0.001; //regen simulation.drawList.push({ x: this.position.x, y: this.position.y, radius: this.radius / 1.5, color: `rgba(0, 255, 20, ${Math.random() * 0.1})`, time: simulation.drawTime }); } this.checkStatus(); Matter.Body.setAngle(me, angle); }; me.onHit = function () { m.damage(0.01) //extra damage me.collisionFilter.mask = 0; setTimeout(() => { me.collisionFilter.mask = cat.player | cat.mob | cat.bullet; }, 1000); } me.onDeath = function () { tech.addJunkTechToPool(0.1) } Composite.add(engine.world, me.constraint); } function drawStar(cx, cy, spikes, outerRadius, innerRadius) { outerRadius *= (1 + 0.1 * Math.sin(simulation.cycle * 0.15)); innerRadius *= (1 + 0.1 * Math.sin(simulation.cycle * 0.15)); var rot = Math.PI / 2 * 3; var xs = cx; var y = cy; var step = Math.PI / spikes; ctx.strokeSyle = "#000"; ctx.beginPath(); ctx.moveTo(cx, cy - outerRadius) for (i = 0; i < spikes; i++) { xs = cx + Math.cos(rot) * outerRadius; y = cy + Math.sin(rot) * outerRadius; ctx.lineTo(xs, y) rot += step xs = cx + Math.cos(rot) * innerRadius; y = cy + Math.sin(rot) * innerRadius; ctx.lineTo(xs, y) rot += step } ctx.lineTo(cx, cy - outerRadius) ctx.closePath(); ctx.lineWidth = 5; ctx.strokeStyle = 'red'; ctx.stroke(); ctx.fillStyle = 'darkred'; ctx.fill(); } const slimePit1 = level.hazard(7475, -75, 475, 100, 0.01) const slimePit2 = level.hazard(11275, -75, 1450, 100, 0.01) const slimePit3 = level.hazard(13400, -150, 3500, 200, 0.1) const slimePit4 = level.hazard(20275, -400, 3475, 450, 0.01) const slimePit5 = level.hazard(25300, -800, 20000, 650, 0.003) const slimePit6 = level.hazard(47725, -425, 2500, 475, 0.01) const slimePit7 = level.hazard(50975, -575, 4050, 650, 0.01) const slimePit8 = level.hazard(54950, -950, 6650, 1050, 0.01) const slimePit9 = level.hazard(63550, -75, 2150, 100, 0.01) const slimePit10 = level.hazard(70875, -75, 1200, 100, 0.01) const slimePit11 = level.hazard(72075, -50, 900, 75, 1) const slimePit12 = level.hazard(75900, -75, 2575, 100, 0.01) const slimePit13 = level.hazard(78475, -35, 2300, 70, 0.01) const slimePit14 = level.hazard(82875, -75, 5100, 100, 0.1) const drip1 = level.drip(32474, -2165, -800, 100); const drip2 = level.drip(37750, -2165, -800, 100); const drip3 = level.drip(43525, -2165, -800, 100); const drip4 = level.drip(20475, -830, -375, 100); const drip5 = level.drip(49960, -2315, -423, 200) let textlogOne = 0; let textlogTwo = 0; let barThere = true; let bar = document.createElement("div"); bar.style.cssText = `position: absolute; top: 80px; background-color: black; width: 80vw; z-index: 1; border-radius: 10px; height: 20px; left: 5em; right: 5em;`; bar.innerHTML += `
` document.body.appendChild(bar); let innerBar = document.getElementById("innerBar"); level.custom = () => { level.exit.drawAndCheck(); if (barThere == true) { innerBar.style.width = "calc(" + `${Math.max(0, Math.min(player.position.x / 1310, 80))}` + "vw - 10px)"; innerBar.style.backgroundColor = m.eyeFillColor; } if (m.pos.x > 25360 && textlogOne == 0) { simulation.makeTextLog(`
A stong force pushes you forward...
`) textlogOne++; } if (m.pos.x < -3000) { Matter.Body.setVelocity(player, { x: 99, y: 19 }); if (textlogTwo == 0) simulation.makeTextLog(`
A strong force pushes you away...
`); textlogTwo++; } if (m.pos.y > 1055) { Matter.Body.setPosition(player, { x: 0, y: -150 }); simulation.makeTextLog(`
There is nowhere to run...
`); m.damage(0.1 * simulation.difficultyMode); } if (m.alive == false && barThere == true) { document.body.removeChild(bar); barThere = false; } ctx.beginPath() ctx.lineWidth = 5; ctx.strokeStyle = "#000000"; ctx.moveTo(40, -1000) ctx.arc(0, -1000, 40, 0, 2 * Math.PI) ctx.stroke() ctx.fillStyle = "#FF00FF55" ctx.fillRect(89750, -1300, 800, 200) ctx.fillRect(89750, -200, 800, 200) ctx.fillRect(92050, -200, 800, 200) ctx.fillRect(92050, -1675, 800, 575) ctx.fillRect(93950, -350, 200, 350); ctx.fillRect(95100, -1350, 150, 375); ctx.fillRect(100900, -1325, 1175, 250); ctx.fillRect(100900, -225, 1200, 250); ctx.fillRect(98725, -1325, 450, 150); ctx.fillRect(98725, -125, 450, 150); ctx.beginPath() //lines! ctx.lineWidth = 10; ctx.strokeStyle = "#000000"; ctx.moveTo(7462.5, -250) ctx.lineTo(7462.5, -170) ctx.moveTo(7710, -330) ctx.lineTo(7710, -250) ctx.moveTo(7960, -420) ctx.lineTo(7960, -320) ctx.moveTo(13725, -250) ctx.lineTo(13725, -180) ctx.moveTo(14025, -350) ctx.lineTo(14025, -280) ctx.moveTo(14325, -450) ctx.lineTo(14325, -380) ctx.moveTo(14625, -550) ctx.lineTo(14625, -480) ctx.moveTo(14925, -650) ctx.lineTo(14925, -580) ctx.moveTo(15225, -750) ctx.lineTo(15225, -680) ctx.moveTo(15525, -850) ctx.lineTo(15525, -780) ctx.moveTo(15825, -950) ctx.lineTo(15825, -880) ctx.moveTo(16125, -1050) ctx.lineTo(16125, -980) ctx.moveTo(16425, -1150) ctx.lineTo(16425, -1080) ctx.moveTo(22600, -650) ctx.lineTo(22600, -580) ctx.moveTo(22800, -750) ctx.lineTo(22800, -680) ctx.moveTo(23000, -850) ctx.lineTo(23000, -780) ctx.moveTo(23200, -950) ctx.lineTo(23200, -880) ctx.moveTo(23400, -1050) ctx.lineTo(23400, -980) ctx.moveTo(23600, -1150) ctx.lineTo(23600, -1080) ctx.moveTo(29550, -1625) ctx.lineTo(29550, -1425) ctx.moveTo(32275, -2125) ctx.lineTo(32275, -1925) ctx.moveTo(33775, -2125) ctx.lineTo(33775, -1925) ctx.moveTo(32275, -350) ctx.lineTo(32275, -550) ctx.moveTo(33775, -350) ctx.lineTo(33775, -550) ctx.moveTo(35475, -650) ctx.lineTo(35475, -450) ctx.moveTo(37650, -2000) ctx.lineTo(37650, -1800) ctx.moveTo(39675, -400) ctx.lineTo(39675, -600) ctx.moveTo(40375, -500) ctx.lineTo(40375, -700) ctx.moveTo(41075, -600) ctx.lineTo(41075, -800) ctx.moveTo(43625, -1925) ctx.lineTo(43625, -1725) ctx.moveTo(50975, -1125) ctx.lineTo(50975, -925) ctx.moveTo(51387.5, -1325) ctx.lineTo(51387.5, -1125) ctx.moveTo(51787.5, -1525) ctx.lineTo(51787.5, -1325) ctx.moveTo(52187.5, -1725) ctx.lineTo(52187.5, -1525) ctx.moveTo(52587.5, -1725) ctx.lineTo(52587.5, -1925) ctx.moveTo(52987.5, -2125) ctx.lineTo(52987.5, -1925) ctx.moveTo(53387.5, -2325) ctx.lineTo(53387.5, -2125) ctx.moveTo(53787.5, -2525) ctx.lineTo(53787.5, -2325) ctx.moveTo(54187.5, -2725) ctx.lineTo(54187.5, -2525) ctx.moveTo(54587.5, -2925) ctx.lineTo(54587.5, -2725) ctx.moveTo(54987.5, -3125) ctx.lineTo(54987.5, -2925) ctx.moveTo(57500, -1925) ctx.lineTo(57650, -1925) ctx.moveTo(57520, -1845) ctx.lineTo(57650, -1845) ctx.moveTo(57500, -1925) ctx.lineTo(57895 + 300, -1925) ctx.moveTo(57520, -1845) ctx.lineTo(57895 + 300, -1845) ctx.moveTo(58525, -1725) ctx.lineTo(58525 + 125, -1725) ctx.moveTo(58525, -1625) ctx.lineTo(58525 + 125, -1625) ctx.moveTo(59000, -1725) ctx.lineTo(59150, -1725) ctx.moveTo(59150, -1625) ctx.lineTo(59000, -1625) ctx.moveTo(70875, -200) ctx.lineTo(70875, -100) ctx.moveTo(63700, -200) ctx.lineTo(63800, -200) ctx.moveTo(64000, -200) ctx.lineTo(64100, -200) ctx.moveTo(64675, -200) ctx.lineTo(64575, -200) ctx.moveTo(64875, -200) ctx.lineTo(64975, -200) ctx.moveTo(65025, -300) ctx.lineTo(64925, -300) ctx.moveTo(65225, -300) ctx.lineTo(65325, -300) ctx.moveTo(71275, -200) ctx.lineTo(71275, -300) ctx.moveTo(71675, -300) ctx.lineTo(71675, -400) ctx.moveTo(72075, -400) ctx.lineTo(72075, -500) ctx.moveTo(72425, -325) ctx.lineTo(72425, -425) ctx.moveTo(72675, -200) ctx.lineTo(72675, -300) ctx.moveTo(72925, -75) ctx.lineTo(72925, -175) ctx.moveTo(75225, -100) ctx.lineTo(75225, -200) ctx.moveTo(76600, -125) ctx.lineTo(76600, -225) ctx.moveTo(76900, -200) ctx.lineTo(76900, -300) ctx.moveTo(77175, -275) ctx.lineTo(77175, -375) ctx.moveTo(77475, -350) ctx.lineTo(77475, -450) ctx.moveTo(85575, -275) ctx.lineTo(85575, -375) ctx.moveTo(86000, -275) ctx.lineTo(86000, -375) ctx.moveTo(86275, -275) ctx.lineTo(86275, -375) ctx.moveTo(86950, -425) ctx.lineTo(86950, -525) ctx.moveTo(89700, -175) ctx.lineTo(89700, -75) ctx.moveTo(89700, -1125) ctx.lineTo(89700, -1225) ctx.moveTo(90600, -1225) ctx.lineTo(90600, -1125) ctx.moveTo(90600, -100) ctx.lineTo(90600, -175) ctx.moveTo(92000, -100) ctx.lineTo(92000, -175) ctx.moveTo(92900, -100) ctx.lineTo(92900, -175) ctx.moveTo(92900, -1225) ctx.lineTo(92900, -1125) ctx.moveTo(94500, -1475) ctx.lineTo(94500, -1575) ctx.moveTo(94700, -1475) ctx.lineTo(94700, -1575) ctx.moveTo(94900, -1475) ctx.lineTo(94900, -1575) ctx.moveTo(96125, -1500) ctx.lineTo(96125, -1575) ctx.moveTo(96550, -1575) ctx.lineTo(96550, -1500) ctx.moveTo(97000, -1475) ctx.lineTo(97000, -1575) ctx.stroke() ctx.beginPath() ctx.strokeStyle = "#FFFFFF"; ctx.fillStyle = document.body.style.backgroundColor; let cycles = Math.sin(simulation.cycle * 0.15) ctx.moveTo(7482.5, -270) ctx.arc(7462.5, -270, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI); ctx.moveTo(7730, -350) ctx.arc(7710, -350, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI); ctx.moveTo(7980, -420) ctx.arc(7960, -420, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI) ctx.moveTo(13745, -180) ctx.arc(13725, -180, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI) ctx.moveTo(14045, -280) ctx.arc(14025, -280, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI) ctx.moveTo(14345, -380) ctx.arc(14325, -380, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI) ctx.moveTo(14645, -480) ctx.arc(14625, -480, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI) ctx.moveTo(14945, -580) ctx.arc(14925, -580, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI) ctx.moveTo(15245, -680) ctx.arc(15225, -680, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI) ctx.moveTo(15545, -780) ctx.arc(15525, -780, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI) ctx.moveTo(15845, -880) ctx.arc(15825, -880, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI) ctx.moveTo(16145, -980) ctx.arc(16125, -980, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI) ctx.moveTo(16445, -1080) ctx.arc(16425, -1080, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI) ctx.moveTo(22620, -580); ctx.arc(22600, -580, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI) ctx.moveTo(22820, -680); ctx.arc(22800, -680, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI) ctx.moveTo(23020, -780); ctx.arc(23000, -780, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI) ctx.moveTo(23220, -880); ctx.arc(23200, -880, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI) ctx.moveTo(23420, -980); ctx.arc(23400, -980, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI) ctx.moveTo(23620, -1080); ctx.arc(23600, -1080, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI) ctx.moveTo(29570, -1425) ctx.arc(29550, -1425, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI) ctx.moveTo(32295, -1925) ctx.arc(32275, -1925, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI) ctx.moveTo(33795, -1925) ctx.arc(33775, -1925, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI) ctx.moveTo(32295, -550) ctx.arc(32275, -550, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI) ctx.moveTo(33795, -550) ctx.arc(33775, -550, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI) ctx.moveTo(35495, -650) ctx.arc(35475, -650, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI) ctx.moveTo(37670, -1800) ctx.arc(37650, -1800, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI) ctx.moveTo(39695, -600) ctx.arc(39675, -600, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI) ctx.moveTo(40395, -700) ctx.arc(40375, -700, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI) ctx.moveTo(41095, -800) ctx.arc(41075, -800, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI) ctx.moveTo(43645, -1725) ctx.arc(43625, -1725, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI) ctx.moveTo(50995, -1125) ctx.arc(50975, -1125, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI) ctx.moveTo(51407.5, -1325) ctx.arc(51387.5, -1325, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI) ctx.moveTo(51807.5, -1525) ctx.arc(51787.5, -1525, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI) ctx.moveTo(52207.5, -1725) ctx.arc(52187.5, -1725, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI) ctx.moveTo(52607.5, -1925) ctx.arc(52587.5, -1925, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI) ctx.moveTo(53007.5, -2125) ctx.arc(52987.5, -2125, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI) ctx.moveTo(53407.5, -2325) ctx.arc(53387.5, -2325, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI) ctx.moveTo(53807.5, -2525) ctx.arc(53787.5, -2525, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI) ctx.moveTo(54207.5, -2725) ctx.arc(54187.5, -2725, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI) ctx.moveTo(54607.5, -2925) ctx.arc(54587.5, -2925, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI) ctx.moveTo(55007.5, -3125) ctx.arc(54987.5, -3125, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI) ctx.moveTo(57520, -1925) ctx.arc(57500, -1925, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI) ctx.moveTo(57520, -1845) ctx.arc(57500, -1845, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI) ctx.moveTo(58525, -1725) ctx.arc(58505, -1725, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI) ctx.moveTo(57895 + 300, -1925) ctx.arc(57875 + 300, -1925, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI) ctx.moveTo(57895 + 300, -1845) ctx.arc(57875 + 300, -1845, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI) ctx.moveTo(58525, -1625) ctx.arc(58505, -1625, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI) ctx.moveTo(58690 + 375 + 125, -1625) ctx.arc(58670 + 375 + 125, -1625, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI) ctx.moveTo(59190, -1725) ctx.arc(59170, -1725, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI) ctx.moveTo(70895, -200) ctx.arc(70875, -200, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI) ctx.moveTo(63720, -200) ctx.arc(63700, -200, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI) ctx.moveTo(64120, -200) ctx.arc(64100, -200, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI) ctx.moveTo(64595, -200) ctx.arc(64575, -200, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI) ctx.moveTo(64995, -200) ctx.arc(64975, -200, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI) ctx.moveTo(64945, -300) ctx.arc(64925, -300, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI) ctx.moveTo(65345, -300) ctx.arc(65325, -300, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI) ctx.moveTo(71295, -300) ctx.arc(71275, -300, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI) ctx.moveTo(71695, -400) ctx.arc(71675, -400, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI) ctx.moveTo(72095, -500) ctx.arc(72075, -500, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI) ctx.moveTo(72445, -425) ctx.arc(72425, -425, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI) ctx.moveTo(72695, -300) ctx.arc(72675, -300, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI) ctx.moveTo(72945, -175) ctx.arc(72925, -175, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI) ctx.moveTo(75245, -200) ctx.arc(75225, -200, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI) ctx.moveTo(76620, -125) ctx.arc(76600, -125, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI) ctx.moveTo(76920, -200) ctx.arc(76900, -200, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI) ctx.moveTo(77195, -275) ctx.arc(77175, -275, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI) ctx.moveTo(77495, -350) ctx.arc(77475, -350, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI) ctx.moveTo(85595, -375) ctx.arc(85575, -375, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI) ctx.moveTo(86020, -375) ctx.arc(86000, -375, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI) ctx.moveTo(86295, -375) ctx.arc(86275, -375, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI) ctx.moveTo(86970, -525) ctx.arc(86950, -525, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI) ctx.moveTo(89720, -175) ctx.arc(89700, -175, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI) ctx.moveTo(89720, -1125) ctx.arc(89700, -1125, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI) ctx.moveTo(90620, -1125) ctx.arc(90600, -1125, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI) ctx.moveTo(90620, -175) ctx.arc(90600, -175, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI) ctx.moveTo(92020, -175) ctx.arc(92000, -175, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI) ctx.moveTo(92920, -175) ctx.arc(92900, -175, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI) ctx.moveTo(92920, -1125) ctx.arc(92900, -1125, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI) ctx.moveTo(94520, -1575) ctx.arc(94500, -1575, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI) ctx.moveTo(94720, -1575) ctx.arc(94700, -1575, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI) ctx.moveTo(94920, -1575) ctx.arc(94900, -1575, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI) ctx.moveTo(96145, -1575) ctx.arc(96125, -1575, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI) ctx.moveTo(96570, -1500) ctx.arc(96550, -1500, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI) ctx.moveTo(97020, -1575) ctx.arc(97000, -1575, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI) ctx.fill() ctx.stroke() slimePit1.query() slimePit2.query() slimePit3.query() slimePit4.query() slimePit5.query() slimePit5.query() slimePit6.query() slimePit7.query() slimePit8.query() slimePit9.query() slimePit10.query() slimePit11.query() slimePit12.query() slimePit13.query() slimePit14.query() drip1.draw() drip2.draw() drip3.draw() drip4.draw() drip5.draw() ctx.fillStyle = "rgba(0,255,255,0.9)" ctx.fillRect(25325, -1375, 75, 400) ctx.fillRect(46425, -1350, 100, 500) ctx.fillRect(87925, -725, 75, 450) /* if (player.position.x < 46470) { document.body.style.backgroundColor = "#DD00DD"; } */ if (player.position.x > 25360 && player.position.x < 46470) { Matter.Body.setVelocity(player, { x: player.velocity.x, //+ 0.2, y: player.velocity.y, }); if (input.up) { Matter.Body.setVelocity(player, { x: player.velocity.x, y: player.velocity.y, //- 1, }); } document.body.style.backgroundColor = "#fb3310" } else if (player.position.x > 46470 && player.position.x < 61675) { document.body.style.backgroundColor = "#7704FF" } else if (player.position.x > 9700 && player.position.x < 46470) { document.body.style.backgroundColor = "#7704FF" } else if (player.position.x > 61675 && player.position.x < 87950) { document.body.style.backgroundColor = "#DD1111" } else if (player.position.x > 87950) { document.body.style.backgroundColor = "#7704FF" } if (m.pos.y > -200 && 20350 < m.pos.x && m.pos.x < 23635) { Matter.Body.setVelocity(player, { x: 0, y: 0 }); Matter.Body.setPosition(player, { x: 20250, y: -1000 }); // move bots for (let i = 0; i < bullet.length; i++) { if (bullet[i].botType) { Matter.Body.setPosition(bullet[i], Vector.add(player.position, { x: 250 * (Math.random() - 0.5), y: 250 * (Math.random() - 0.5) })); Matter.Body.setVelocity(bullet[i], { x: 0, y: 0 }); } } m.damage(0.1 * simulation.difficultyMode) m.energy -= 0.1 * simulation.difficultyMode } if (m.pos.y > -150 && m.pos.x > 47770 && m.pos.x < 50130) { Matter.Body.setVelocity(player, { x: 0, y: 0 }); Matter.Body.setPosition(player, { x: 47640, y: -900 }); // move bots for (let i = 0; i < bullet.length; i++) { if (bullet[i].botType) { Matter.Body.setPosition(bullet[i], Vector.add(player.position, { x: 250 * (Math.random() - 0.5), y: 250 * (Math.random() - 0.5) })); Matter.Body.setVelocity(bullet[i], { x: 0, y: 0 }); } } m.damage(0.1 * simulation.difficultyMode) m.energy -= 0.1 * simulation.difficultyMode } if (m.pos.y > -150 && 50975 < m.pos.x && m.pos.x < 54925) { Matter.Body.setVelocity(player, { x: 0, y: 0 }); Matter.Body.setPosition(player, { x: 50965, y: -1100 }); // move bots for (let i = 0; i < bullet.length; i++) { if (bullet[i].botType) { Matter.Body.setPosition(bullet[i], Vector.add(player.position, { x: 250 * (Math.random() - 0.5), y: 250 * (Math.random() - 0.5) })); Matter.Body.setVelocity(bullet[i], { x: 0, y: 0 }); } } m.damage(0.1 * simulation.difficultyMode) m.energy -= 0.1 * simulation.difficultyMode } if (m.pos.y > -150 && 55025 < m.pos.x && m.pos.x < 57675) { Matter.Body.setVelocity(player, { x: 0, y: 0 }); Matter.Body.setPosition(player, { x: 55148, y: -3072 }); // move bots for (let i = 0; i < bullet.length; i++) { if (bullet[i].botType) { Matter.Body.setPosition(bullet[i], Vector.add(player.position, { x: 250 * (Math.random() - 0.5), y: 250 * (Math.random() - 0.5) })); Matter.Body.setVelocity(bullet[i], { x: 0, y: 0 }); } } m.damage(0.1 * simulation.difficultyMode) m.energy -= 0.1 * simulation.difficultyMode } if (m.pos.y > -150 && 57875 < m.pos.x && m.pos.x < 58700) { Matter.Body.setVelocity(player, { x: 0, y: 0 }); Matter.Body.setPosition(player, { x: 57800, y: -2200 }); // move bots for (let i = 0; i < bullet.length; i++) { if (bullet[i].botType) { Matter.Body.setPosition(bullet[i], Vector.add(player.position, { x: 250 * (Math.random() - 0.5), y: 250 * (Math.random() - 0.5) })); Matter.Body.setVelocity(bullet[i], { x: 0, y: 0 }); } } m.damage(0.1 * simulation.difficultyMode) m.energy -= 0.1 * simulation.difficultyMode } if (m.pos.y > -150 && 58875 < m.pos.x && m.pos.x < 61650) { Matter.Body.setVelocity(player, { x: 0, y: 0 }); Matter.Body.setPosition(player, { x: 58850, y: -2025 }); // move bots for (let i = 0; i < bullet.length; i++) { if (bullet[i].botType) { Matter.Body.setPosition(bullet[i], Vector.add(player.position, { x: 250 * (Math.random() - 0.5), y: 250 * (Math.random() - 0.5) })); Matter.Body.setVelocity(bullet[i], { x: 0, y: 0 }); } } m.damage(0.1 * simulation.difficultyMode) m.energy -= 0.1 * simulation.difficultyMode } if (m.pos.y > -1677 && 104650 < m.pos.x && m.pos.x < 105000 && barThere == true) { Matter.Body.setVelocity(player, { x: 0, y: 0 }); Matter.Body.setPosition(player, { x: 132577, y: -300 }); // move bots for (let i = 0; i < bullet.length; i++) { if (bullet[i].botType) { Matter.Body.setPosition(bullet[i], Vector.add(player.position, { x: 250 * (Math.random() - 0.5), y: 250 * (Math.random() - 0.5) })); Matter.Body.setVelocity(bullet[i], { x: 0, y: 0 }); } } document.body.style.transitionDuration = "0ms"; document.body.style.backgroundColor = "#696969"; simulation.makeTextLog(`
You have earned: ` + Math.min(3, totalCoin) + ` tech
`) if (barThere == true) { //only runs once document.body.removeChild(bar); for (let i = 0, len = Math.min(3, totalCoin); i < len; i++) powerUps.directSpawn(player.position.x, player.position.y, "tech"); barThere = false; } } ctx.fillStyle = "#FFFFFF53" ctx.fillRect(57645, -2055, 385, 85) ctx.fillRect(58645, -1880, 385, 85) //chains ctx.strokeStyle = "#FF0000" ctx.strokeRect(66975, -725, 25, 50) ctx.strokeRect(67050, -725, 25, 50) ctx.strokeRect(66975 + 1150, -725, 25, 50) ctx.strokeRect(67050 + 1250, -725, 25, 50) ctx.strokeRect(69162, -725, 25, 50) ctx.strokeRect(69862, -725, 25, 50) ctx.strokeRect(74412.5, -412.5, 25, 50) ctx.strokeRect(74612.5, -412.5, 25, 50) ctx.strokeRect(77962.5, -900, 25, 50) ctx.strokeRect(78212.5, -775, 25, 50) ctx.strokeRect(78462.5, -650, 25, 50) ctx.strokeRect(81587.5, -725, 25, 50) ctx.strokeRect(81687.5, -725, 25, 50) ctx.strokeRect(81787.5, -725, 25, 50) ctx.strokeRect(83037.5, -215, 25, 50) ctx.strokeRect(83362.5, -215, 25, 50) ctx.strokeRect(83687.5, -215, 25, 50) ctx.strokeRect(84187.5, -315, 25, 50) ctx.strokeStyle = "#FF000088" ctx.strokeRect(66975, -850, 25, 50) ctx.strokeRect(67050, -850, 25, 50) ctx.strokeRect(66975 + 1150, -850, 25, 50) ctx.strokeRect(67050 + 1250, -850, 25, 50) ctx.strokeRect(69162, -850, 25, 50) ctx.strokeRect(69862, -850, 25, 50) ctx.strokeRect(74412.5, -525, 25, 50) ctx.strokeRect(74612.5, -525, 25, 50) ctx.strokeRect(77962.5, -985, 25, 50) ctx.strokeRect(78212.5, -860, 25, 50) ctx.strokeRect(78462.5, -735, 25, 50) ctx.strokeRect(81587.5, -850, 25, 50) ctx.strokeRect(81687.5, -850, 25, 50) ctx.strokeRect(81787.5, -850, 25, 50) ctx.strokeRect(83037.5, -315, 25, 50) ctx.strokeRect(83362.5, -315, 25, 50) ctx.strokeRect(83687.5, -315, 25, 50) ctx.strokeRect(84187.5, -415, 25, 50) ctx.strokeStyle = "#FF000044" ctx.strokeRect(66975, -975, 25, 50) ctx.strokeRect(67050, -975, 25, 50) ctx.strokeRect(66975 + 1150, -975, 25, 50) ctx.strokeRect(67050 + 1250, -975, 25, 50) ctx.strokeRect(69162, -975, 25, 50) ctx.strokeRect(69862, -975, 25, 50) ctx.strokeRect(74412.5, -633, 25, 50) ctx.strokeRect(74612.5, -633, 25, 50) ctx.strokeRect(77962.5, -1075, 25, 50) ctx.strokeRect(78212.5, -950, 25, 50) ctx.strokeRect(78462.5, -825, 25, 50) ctx.strokeRect(81587.5, -975, 25, 50) ctx.strokeRect(81687.5, -975, 25, 50) ctx.strokeRect(81787.5, -975, 25, 50) ctx.strokeRect(83037.5, -415, 25, 50) ctx.strokeRect(83362.5, -415, 25, 50) ctx.strokeRect(83687.5, -415, 25, 50) ctx.strokeRect(84187.5, -515, 25, 50) ctx.strokeStyle = "#FF000011" ctx.strokeRect(66975, -1100, 25, 50) ctx.strokeRect(67050, -1100, 25, 50) ctx.strokeRect(66975 + 1150, -1100, 25, 50) ctx.strokeRect(67050 + 1250, -1100, 25, 50) ctx.strokeRect(69162, -1100, 25, 50) ctx.strokeRect(69862, -1100, 25, 50) ctx.strokeRect(74412.5, -741, 25, 50) ctx.strokeRect(74612.5, -741, 25, 50) ctx.strokeRect(77962.5, -1165, 25, 50) ctx.strokeRect(78212.5, -1040, 25, 50) ctx.strokeRect(78462.5, -915, 25, 50) ctx.strokeRect(81587.5, -1100, 25, 50) ctx.strokeRect(81687.5, -1100, 25, 50) ctx.strokeRect(81787.5, -1100, 25, 50) ctx.strokeRect(83037.5, -515, 25, 50) ctx.strokeRect(83362.5, -515, 25, 50) ctx.strokeRect(83687.5, -515, 25, 50) ctx.strokeRect(84187.5, -615, 25, 50) ctx.stroke() ctx.beginPath() ctx.strokeStyle = "#FF0000" ctx.moveTo(66987.5, -680) ctx.lineTo(66987.5, -625) ctx.moveTo(67062.5, -680) ctx.lineTo(67062.5, -625) ctx.moveTo(66987.5 + 1150, -680) ctx.lineTo(66987.5 + 1150, -625) ctx.moveTo(67062.5 + 1250, -680) ctx.lineTo(67062.5 + 1250, -625) ctx.moveTo(69175, -680) ctx.lineTo(69175, -625) ctx.moveTo(69875, -680) ctx.lineTo(69875, -625) ctx.moveTo(74425, -290) ctx.lineTo(74425, -370) ctx.moveTo(74625, -290) ctx.lineTo(74625, -370) ctx.moveTo(77975, -790) ctx.lineTo(77975, -855) ctx.moveTo(78225, -665) ctx.lineTo(78225, -730) ctx.moveTo(78475, -540) ctx.lineTo(78475, -605) ctx.moveTo(81600, -680) ctx.lineTo(81600, -625) ctx.moveTo(81700, -680) ctx.lineTo(81700, -625) ctx.moveTo(81800, -680) ctx.lineTo(81800, -625) ctx.moveTo(83050, -100) ctx.lineTo(83050, -170) ctx.moveTo(83375, -100) ctx.lineTo(83375, -170) ctx.moveTo(83700, -100) ctx.lineTo(83700, -170) ctx.moveTo(84200, -200) ctx.lineTo(84200, -270) ctx.stroke() ctx.strokeStyle = "#FF000099" ctx.moveTo(66987.5, -810) ctx.lineTo(66987.5, -715) ctx.moveTo(67062.5, -810) ctx.lineTo(67062.5, -715) ctx.moveTo(66987.5 + 1150, -810) ctx.lineTo(66987.5 + 1150, -715) ctx.moveTo(67062.5 + 1250, -810) ctx.lineTo(67062.5 + 1250, -715) ctx.moveTo(69175, -810) ctx.lineTo(69175, -715) ctx.moveTo(69875, -810) ctx.lineTo(69875, -715) ctx.moveTo(74425, -405) ctx.lineTo(74425, -480) ctx.moveTo(74625, -405) ctx.lineTo(74625, -480) ctx.moveTo(77975, -890) ctx.lineTo(77975, -940) ctx.moveTo(78225, -765) ctx.lineTo(78225, -815) ctx.moveTo(78475, -640) ctx.lineTo(78475, -690) ctx.moveTo(81600, -810) ctx.lineTo(81600, -715) ctx.moveTo(81700, -810) ctx.lineTo(81700, -715) ctx.moveTo(81800, -810) ctx.lineTo(81800, -715) ctx.moveTo(83050, -210) ctx.lineTo(83050, -270) ctx.moveTo(83375, -210) ctx.lineTo(83375, -270) ctx.moveTo(83700, -210) ctx.lineTo(83700, -270) ctx.moveTo(84200, -310) ctx.lineTo(84200, -370) ctx.stroke() ctx.strokeStyle = "#FF000044" ctx.moveTo(66987.5, -930) ctx.lineTo(66987.5, -845) ctx.moveTo(67062.5, -930) ctx.lineTo(67062.5, -845) ctx.moveTo(66987.5 + 1150, -930) ctx.lineTo(66987.5 + 1150, -845) ctx.moveTo(67062.5 + 1250, -930) ctx.lineTo(67062.5 + 1250, -845) ctx.moveTo(69175, -930) ctx.lineTo(69175, -845) ctx.moveTo(69875, -930) ctx.lineTo(69875, -845) ctx.moveTo(74425, -515) ctx.lineTo(74425, -590) ctx.moveTo(74625, -515) ctx.lineTo(74625, -590) ctx.moveTo(77975, -975) ctx.lineTo(77975, -1040) ctx.moveTo(78225, -850) ctx.lineTo(78225, -915) ctx.moveTo(78475, -725) ctx.lineTo(78475, -790) ctx.moveTo(81600, -930) ctx.lineTo(81600, -845) ctx.moveTo(81700, -930) ctx.lineTo(81700, -845) ctx.moveTo(81800, -930) ctx.lineTo(81800, -845) ctx.moveTo(83050, -305) ctx.lineTo(83050, -370) ctx.moveTo(83375, -305) ctx.lineTo(83375, -370) ctx.moveTo(83700, -305) ctx.lineTo(83700, -370) ctx.moveTo(84200, -405) ctx.lineTo(84200, -470) ctx.stroke() ctx.strokeStyle = "#FF000022" ctx.moveTo(66987.5, -1060) ctx.lineTo(66987.5, -965) ctx.moveTo(67062.5, -1060) ctx.lineTo(67062.5, -965) ctx.moveTo(66987.5 + 1150, -1060) ctx.lineTo(66987.5 + 1150, -965) ctx.moveTo(67062.5 + 1250, -1060) ctx.lineTo(67062.5 + 1250, -965) ctx.moveTo(69175, -1060) ctx.lineTo(69175, -965) ctx.moveTo(69875, -1060) ctx.lineTo(69875, -965) ctx.moveTo(74425, -620) ctx.lineTo(74425, -712.5) ctx.moveTo(74625, -620) ctx.lineTo(74625, -712.5) ctx.moveTo(77975, -1075) ctx.lineTo(77975, -1120) ctx.moveTo(78225, -950) ctx.lineTo(78225, -995) ctx.moveTo(78475, -825) ctx.lineTo(78475, -870) ctx.moveTo(81600, -1060) ctx.lineTo(81600, -965) ctx.moveTo(81700, -1060) ctx.lineTo(81700, -965) ctx.moveTo(81800, -1060) ctx.lineTo(81800, -965) ctx.moveTo(83050, -405) ctx.lineTo(83050, -470) ctx.moveTo(83375, -405) ctx.lineTo(83375, -470) ctx.moveTo(83700, -405) ctx.lineTo(83700, -470) ctx.moveTo(84200, -505) ctx.lineTo(84200, -570) ctx.stroke() let star = 95525; for (let i = 0; i < 3; i++) { drawStar(star, -1540, 5, 40, 15); star += 200; } drawStar(97375, -1540, 5, 25, 10) drawStar(97675, -1540, 5, 25, 10) drawStar(97975, -1540, 5, 25, 10) drawStar(98275, -1540, 5, 25, 10) drawStar(98575, -1540, 5, 25, 10) }; var gradient = ctx.createLinearGradient(0, 0, 175, 0); gradient.addColorStop(0, "#7704FF00"); gradient.addColorStop(1, "#00FFFF"); level.customTopLayer = () => { if (player.position.x > 25360 && player.position.x < 46470 && player.position.y > -2348 || player.position.x > 87995 && player.position.x < 103950 && player.position.y > -1350) { player.force.x += 0.0045 m.airControl = () => { if (input.up) { player.force.y -= 0.02 } } m.draw = () => { ctx.save(); ctx.globalAlpha = (m.immuneCycle < m.cycle) ? 1 : 0.5 ctx.translate(player.position.x, player.position.y); ctx.rotate(player.angle); if (input.up) { //forward thrust drawing ctx.rotate(`${Math.max(-0.5, Math.min(m.angle, 0.5))}`) } else { ctx.rotate(`${Math.max(0.5, Math.min(m.angle, -0.5))}`) } ctx.beginPath(); ctx.arc(0, 0, 30, 0, 2 * Math.PI); ctx.fillStyle = m.bodyGradient ctx.fill(); ctx.arc(15, 0, 4, 0, 2 * Math.PI); ctx.strokeStyle = "#333"; ctx.lineWidth = 2; ctx.stroke(); ctx.beginPath() ctx.moveTo(30, 0) ctx.lineTo(60, 10) ctx.lineTo(60, 30) ctx.lineTo(20, 40) ctx.lineTo(0, 40) ctx.lineTo(-50, 60) ctx.lineTo(-50, 0) ctx.lineTo(-40, -40) ctx.lineTo(-40, -40) ctx.lineTo(-30, 10) ctx.lineTo(30, 10) ctx.lineTo(30, 0) ctx.fill(); ctx.moveTo(-50, 30) ctx.lineTo(-60, 30) ctx.lineTo(-60, 0) ctx.lineTo(-50, 0) ctx.fill() ctx.stroke() ctx.restore(); } } else { m.resetSkin() m.airControl = () => { if (input.up && m.buttonCD_jump + 20 < m.cycle && m.yOffWhen.stand > 23 && m.lastOnGroundCycle + 5 > m.cycle) m.jump() if (m.buttonCD_jump + 60 > m.cycle && !(input.up) && m.Vy < 0) { Matter.Body.setVelocity(player, { x: player.velocity.x, y: player.velocity.y * 0.94 }); } if (input.left) { if (player.velocity.x > -m.airSpeedLimit / player.mass / player.mass) player.force.x -= m.FxAir; // move player left / a } else if (input.right) { if (player.velocity.x < m.airSpeedLimit / player.mass / player.mass) player.force.x += m.FxAir; //move player right / d } } } ctx.fillStyle = `rgba(68, 68, 68, ${Math.max(0.1, Math.min((-1400 - m.pos.y) / -100, 0.99))})`; ctx.fillRect(91900, -1675, 12050, 375) ctx.save() ctx.translate(104700, -1675); ctx.fillStyle = gradient; ctx.fillRect(0, 0, 175, 1675) ctx.restore() }; level.setPosToSpawn(0, -150); //spawn level.defaultZoom = 1800 //default I think simulation.zoomTransition(level.defaultZoom) document.body.style.backgroundColor = "#0150FF"; document.body.style.transitionDuration = "1500ms"; //smoother transitions, so that people don't complain level.exit.x = 133150; level.exit.y = -260; spawn.mapRect(level.exit.x, level.exit.y + 25, 100, 20); //ground? I forgot spawn.mapRect(-5000, 0, 110000, 1000); //*mountains to prevent player from running away* spawn.mapVertex(-5775, -1330, "0 0 1000 7000 -1000 7000"); spawn.mapVertex(-7000, -330, "0 0 1000 4000 -1000 4000"); spawn.mapVertex(-10000, -330, "0 0 1000 4000 -1000 4000"); spawn.mapVertex(-9000, -330, "0 0 1000 4000 -1000 4000"); spawn.mapVertex(-7500, -330, "0 0 1000 4000 -1000 4000"); spawn.mapRect(-10000, 0, 3500, 1000); //spawn.mapRect(-10000, -10000, 1000, 11000); //hunter(-600, -150) //stage one --> 0% //spikes spike(3000, -30); spike(5000, -30); spike(4925, 0); spike(7275, -30); spike(7375, -30); spike(9806, -30); spike(9900, -30); spike(12000, -130); spike(13050, -255); spike(17300, -1105); spike(17400, -1105); spike(17200, -1105); spike(17500, -1105); spike(18200, -1105); spike(18100, -1105); spike(18300, -1105); spike(18400, -1105); spike(19875, -1108); spike(19975, -930); spike(21000, -930); spike(21725, -780); spike(23600, -1230); spawn.mapVertex(62000, -300, "0 1000 1000 1000 0 0"); //upside down spikes let u = Math.PI * 1.5; spike(18825, -1170, u); spike(18925, -1170, u); spike(19025, -1170, u); spike(19125, -1170, u); spike(19225, -1170, u); spike(19325, -1170, u); spike(49962.5, -2370, u); spike(50062.5, -2370, u); spike(50162.5, -2370, u); spike(50262.5, -2370, u); spike(50362.5, -2370, u); //bottom of the flying part spike(32375, -280); spike(32475, -280); spike(32575, -280); spike(32675, -280); spike(32775, -280); spike(32875, -280); spike(32975, -280); spike(33075, -280); spike(33175, -280); spike(33275, -280); spike(33375, -280); spike(33475, -280); spike(33575, -280); spike(33675, -280); spike(35575, -280); spike(35675, -280); spike(35775, -280); spike(35875, -280); spike(39775, -280); spike(39875, -280); spike(39975, -280); spike(40075, -280); spike(40175, -280); spike(40275, -280); spike(40475, -280); spike(40575, -280); spike(40675, -280); spike(40775, -280); spike(40875, -280); spike(40975, -280); //top of the flying part spike(32375, -2193, u); spike(32475, -2193, u); spike(32575, -2193, u); spike(32675, -2193, u); spike(32775, -2193, u); spike(32875, -2193, u); spike(32975, -2193, u); spike(33075, -2193, u); spike(33175, -2193, u); spike(33275, -2193, u); spike(33375, -2193, u); spike(33475, -2193, u); spike(33575, -2193, u); spike(33675, -2193, u); spike(37750, -2193, u); spike(37850, -2193, u); spike(37950, -2193, u); spike(38050, -2193, u); spike(43025, -2193, u); spike(43125, -2193, u); spike(43225, -2193, u); spike(43325, -2193, u); spike(43425, -2193, u); spike(43525, -2193, u); spike(43725, -2193, u); spike(43825, -2193, u); spike(43925, -2193, u); spike(44025, -2193, u); spike(44125, -2193, u); spike(44225, -2193, u); spike(44325, -2193, u); spike(44425, -2193, u); spike(44525, -2193, u); spike(44625, -2193, u); spike(44725, -2193, u); spike(44825, -2193, u); //about 55% of the map spike(63375, -30); spike(63475, -30); spike(65775, -30); spike(65875, -30); spike(66975, -30); spike(67075, -30); spike(66975, -500, u); spike(67075, -500, u); spike(68125, -30); spike(68225, -30); spike(68325, -30); spike(68125, -500, u); spike(68225, -500, u); spike(68325, -500, u); spike(69175, -500, u); spike(69175, -30); spike(69875, -500, u); spike(69875, -30); spike(70675, -30); spike(70775, -30); spike(73725, -30); spike(73825, -30); spike(74425, -195, u); spike(74525, -195, u); spike(74625, -195, u); spike(75125, -30); spawn.mapVertex(78725, -466, "0 50 100 50 50 0"); //ocd still triggers from -467 spike(79300, -180); spike(80800, -30); spike(80900, -30); spike(81600, -30); spike(81700, -30); spike(81800, -30); spike(81600, -500, u); spike(81700, -500, u); spike(81800, -500, u); spike(93425, -1430); spike(93525, -1430); spike(85800, -305); spike(86475, -305); spike(87150, -455); spike(94025, -1570, u); spike(94125, -1570, u); spike(94500, -1430); spike(94700, -1430); spike(94900, -1430); spike(94600, -1400); spike(94800, -1400); spike(94212.5, -1675, u); spike(94287.5, -1675, u); //even more 90% spike(95525, -1400) spike(95525, -1675, u) spike(95625, -1400) spike(95625, -1675, u) spike(95725, -1400) spike(95725, -1675, u) spike(95825, -1400) spike(95825, -1675, u) spike(95925, -1400) spike(95925, -1675, u) spike(96025, -1400) spike(96225, -1400) spike(96650, -1675, u) spike(96900, -1400) spike(97150, -1675, u) spike(98900, -1400) spike(96975, -155) spike(97075, -155) spike(97175, -155) spike(97275, -155) spike(97375, -155) spike(97475, -155) spike(96975, -1170, u) spike(97075, -1170, u) spike(97175, -1170, u) spike(97275, -1170, u) spike(97375, -1170, u) spike(97475, -1170, u) spike(98700, -1070, u) spike(98800, -1070, u) spike(98900, -1070, u) spike(99000, -1070, u) spike(99100, -1070, u) spike(99200, -1070, u) spike(98700, -230) spike(98800, -230) spike(98900, -230) spike(99000, -230) spike(99100, -230) spike(99200, -230) spike(98975, -1400) spike(99375, -1675, u) spike(99300, -1675, u) spike(99575, -1675, u) spike(100000, -1400) //actual stuff spawn.mapRect(7425, -175, 75, 175); spawn.mapRect(7675, -250, 75, 250); spawn.mapRect(7925, -325, 75, 325); spawn.mapRect(10625, -100, 725, 100); spawn.mapRect(11625, -100, 725, 100); spawn.mapRect(12650, -225, 800, 225); spawn.mapRect(13675, -300, 100, 50); spawn.mapRect(13975, -400, 100, 50); spawn.mapRect(14275, -500, 100, 50); spawn.mapRect(14575, -600, 100, 50); spawn.mapRect(14875, -700, 100, 50); spawn.mapRect(15175, -800, 100, 50); spawn.mapRect(15475, -900, 100, 50); spawn.mapRect(15775, -1000, 100, 50); spawn.mapRect(16075, -1100, 100, 50); spawn.mapRect(16375, -1200, 100, 50); spawn.mapRect(16625, -1075, 350, 100); spawn.mapRect(16800, -1075, 1825, 1125); spawn.mapRect(17250, -1225, 200, 50); spawn.mapRect(18150, -1225, 200, 50); spawn.mapRect(18550, -975, 1050, 1025); spawn.mapRect(19525, -1075, 400, 1125); spawn.mapRect(18775, -1275, 600, 75); spawn.mapRect(19825, -900, 500, 950); spawn.mapRect(20150, -900, 375, 125); spawn.mapRect(20750, -900, 300, 50); spawn.mapRect(21225, -750, 550, 50); spawn.mapRect(21950, -625, 450, 50); spawn.mapRect(22550, -700, 100, 50); spawn.mapRect(22750, -800, 100, 50); spawn.mapRect(22950, -900, 100, 50); spawn.mapRect(23150, -1000, 100, 50); spawn.mapRect(23350, -1100, 100, 50); spawn.mapRect(23550, -1200, 100, 50); spawn.mapRect(23525, -975, 400, 100); spawn.mapRect(23650, -975, 1825, 1025); spawn.mapRect(23750, -2500, 625, 1125); spawn.mapRect(24000, -2500, 1200, 1300); spawn.mapRect(24900, -2500, 575, 1125); spawn.mapRect(25425, -2500, 20000, 275); spawn.mapRect(25425, -250, 20000, 300); spawn.mapRect(29450, -2300, 200, 675); spawn.mapRect(32225, -2225, 100, 100); spawn.mapRect(33725, -2225, 100, 100); spawn.mapRect(32225, -350, 100, 100); spawn.mapRect(33725, -350, 100, 100); spawn.mapRect(37600, -2225, 100, 225); spawn.mapRect(35425, -475, 100, 225); spawn.mapRect(39625, -400, 100, 150); spawn.mapRect(40325, -500, 100, 250); spawn.mapRect(41025, -600, 100, 350); spawn.mapRect(43575, -2225, 100, 300); spawn.mapRect(44875, -2500, 1675, 1225); spawn.mapRect(44875, -950, 1675, 1000); spawn.mapRect(46425, -825, 1400, 875); spawn.mapRect(48075, -1100, 175, 1150); spawn.mapRect(48575, -1300, 175, 1375); spawn.mapRect(49075, -1500, 175, 1550); spawn.mapRect(49575, -1700, 175, 975); spawn.mapRect(49575, -500, 175, 550); spawn.mapRect(50075, -1900, 175, 700); spawn.mapRect(50075, -1000, 1000, 1000); spawn.mapRect(49912.5, -2525, 500, 125); spawn.mapRect(51300, -1200, 175, 1225); spawn.mapRect(51700, -1400, 175, 1425); spawn.mapRect(52100, -1600, 175, 1625); spawn.mapRect(52500, -1800, 175, 1825); spawn.mapRect(52900, -2000, 175, 2025); spawn.mapRect(53300, -2200, 175, 2225); spawn.mapRect(53700, -2400, 175, 2425); spawn.mapRect(54100, -2600, 175, 2625); spawn.mapRect(54500, -2800, 175, 2825); spawn.mapRect(54900, -3000, 175, 3025); spawn.mapRect(55050, -3000, 175, 75); spawn.mapRect(55475, -2875, 250, 75); spawn.mapRect(55900, -2625, 250, 75); spawn.mapRect(56500, -2400, 375, 75); spawn.mapRect(57200, -2200, 250, 75); spawn.mapRect(57650, -2050, 375, 75); spawn.mapRect(57650, -1970, 375, 1975); spawn.mapRect(58650, -1875, 375, 75); spawn.mapRect(58650, -1795, 375, 1975); spawn.mapRect(59525, -1750, 175, 75); spawn.mapRect(60125, -1600, 175, 75); spawn.mapRect(60750, -1425, 175, 75); spawn.mapRect(61250, -1225, 175, 75); spawn.mapRect(61550, -1025, 225, 1125); spawn.mapRect(63525, -100, 100, 100); spawn.mapRect(63800, -225, 200, 50); spawn.mapRect(64175, -100, 100, 100); spawn.mapRect(64275, -100, 100, 100); spawn.mapRect(64375, -100, 100, 100); spawn.mapRect(64675, -225, 200, 50); spawn.mapRect(65025, -325, 200, 50); spawn.mapRect(65425, -100, 300, 100); spawn.mapRect(66925, -650, 200, 120); spawn.mapRect(68075, -650, 300, 120); spawn.mapRect(69125, -650, 100, 120); spawn.mapRect(69825, -650, 100, 120); spawn.mapRect(70825, -100, 100, 100); spawn.mapRect(71225, -200, 100, 200); spawn.mapRect(71625, -300, 100, 300); spawn.mapRect(72025, -400, 100, 400); spawn.mapRect(72125, -400, 100, 50); spawn.mapRect(72375, -325, 100, 50); spawn.mapRect(72625, -200, 100, 50); spawn.mapRect(72875, -75, 100, 50); spawn.mapRect(74375, -300, 300, 75); spawn.mapRect(75175, -100, 100, 100); spawn.mapRect(75900, -150, 400, 50); spawn.mapRect(76550, -250, 100, 50); spawn.mapRect(76850, -325, 100, 50); spawn.mapRect(77125, -400, 100, 50); spawn.mapRect(77425, -475, 300, 50); spawn.mapRect(77925, -400, 100, 100); spawn.mapRect(78175, -275, 100, 100); spawn.mapRect(78425, -150, 100, 100); spawn.mapRect(78675, -50, 100, 100); spawn.mapRect(77925, -800, 100, 100); spawn.mapRect(78175, -675, 100, 100); spawn.mapRect(78425, -550, 100, 100); spawn.mapRect(78675, -450, 100, 100); spawn.mapRect(78450, -100, 50, 125); spawn.mapRect(79025, -150, 100, 50); spawn.mapRect(79250, -150, 100, 50); spawn.mapRect(79475, -150, 100, 50); spawn.mapRect(79800, -225, 300, 50); spawn.mapRect(80250, -150, 100, 50); spawn.mapRect(80450, -100, 300, 50); spawn.mapRect(81550, -650, 300, 120); spawn.mapRect(82800, -100, 100, 100); spawn.mapRect(82900, -100, 200, 50); spawn.mapRect(83325, -100, 100, 50); spawn.mapRect(83650, -100, 200, 50); spawn.mapRect(83850, -100, 100, 100); spawn.mapRect(83950, -200, 100, 200); spawn.mapRect(84050, -200, 200, 50); spawn.mapRect(84500, -350, 100, 50); spawn.mapRect(84725, -250, 100, 50); spawn.mapRect(84950, -150, 300, 50); spawn.mapRect(85525, -275, 100, 50); spawn.mapRect(85750, -275, 100, 50); spawn.mapRect(85950, -275, 375, 50); spawn.mapRect(86425, -275, 100, 50); spawn.mapRect(86625, -275, 100, 50); spawn.mapRect(86900, -425, 300, 50); spawn.mapRect(87375, -275, 300, 50); spawn.mapRect(87900, -300, 125, 300); spawn.mapRect(87900, -1850, 125, 1150); spawn.mapRect(87900, -1850, 17000, 175); spawn.mapRect(104875, -1850, 125, 2850); //Last part spawn.mapRect(87900, -1850, 4000, 550); spawn.mapRect(89650, -100, 100, 100); spawn.mapRect(89750, -200, 100, 100); spawn.mapRect(89850, -300, 600, 100); spawn.mapRect(90450, -200, 100, 100); spawn.mapRect(90550, -100, 100, 100); spawn.mapRect(89650, -1300, 100, 100); spawn.mapRect(89750, -1200, 100, 100); spawn.mapRect(89850, -1100, 600, 100); spawn.mapRect(90450, -1200, 100, 100); spawn.mapRect(90550, -1300, 100, 100); spawn.mapRect(91950, -100, 100, 100); spawn.mapRect(92050, -200, 100, 100); spawn.mapRect(92150, -300, 600, 100); spawn.mapRect(92750, -200, 100, 100); spawn.mapRect(92850, -100, 100, 100); spawn.mapRect(92050, -1200, 100, 100); spawn.mapRect(92150, -1100, 600, 100); spawn.mapRect(92750, -1200, 100, 100); spawn.mapRect(92850, -1300, 100, 100); spawn.mapRect(92950, -1400, 11000, 100); spawn.mapRect(93975, -1700, 200, 100); spawn.mapRect(96075, -1500, 100, 100); spawn.mapRect(96500, -1675, 100, 100); spawn.mapRect(96950, -1490, 1900, 100); spawn.mapRect(97200, -1685, 1650, 100); spawn.mapRect(93900, -300, 100, 300); spawn.mapRect(93900, -400, 300, 100); spawn.mapRect(94100, -300, 100, 300); spawn.mapRect(95025, -1300, 100, 300); spawn.mapRect(95025, -1000, 300, 100); spawn.mapRect(95225, -1300, 100, 300); spawn.mapRect(96925, -1300, 600, 100); spawn.mapRect(96925, -125, 600, 125); spawn.mapRect(98650, -1300, 100, 200); spawn.mapRect(98650, -1200, 600, 100); spawn.mapRect(99150, -1300, 100, 200); spawn.mapRect(98650, -200, 100, 200); spawn.mapRect(99150, -200, 100, 200); spawn.mapRect(98650, -200, 600, 100); spawn.mapRect(100825, -1300, 100, 300); spawn.mapRect(100825, -1100, 1325, 100); spawn.mapRect(102050, -1300, 100, 300); spawn.mapRect(100825, -300, 100, 300); spawn.mapRect(100825, -300, 1350, 100); spawn.mapRect(102075, -300, 100, 300); spawn.mapRect(99425, -1675, 100, 125); spawn.mapRect(100050, -1525, 100, 125); spawn.mapRect(132025, -225, 2325, 525); spawn.mapRect(132025, -1450, 500, 1750); spawn.mapRect(133875, -1475, 475, 1775); spawn.mapRect(132025, -1925, 2325, 475); // simulation.enableConstructMode() //also remove when done coin(50165.9, -1090) coin(78725.4, -600) coin(103830.0, -1473) hunter(0, -1000) }, yingYang() { simulation.makeTextLog(`yingYang by Richard0820`); let destroyed = false; const lock = level.door(425, -1400, 50, 300, 300); const core = function (x, y, radius = 100 + Math.ceil(Math.random() * 25)) { radius = 9 + radius / 8; //extra small mobs.spawn(x, y, 6, radius, "transparent"); let me = mob[mob.length - 1]; me.constraint = Constraint.create({ pointA: { x: me.position.x, y: me.position.y }, bodyB: me, stiffness: 1, damping: 1 }); Composite.add(engine.world, me.constraint); me.stroke = "transparent"; //used for drawSneaker me.eventHorizon = radius * 40; me.seeAtDistance2 = (me.eventHorizon + 400) * (me.eventHorizon + 400); //vision limit is event horizon me.accelMag = 0.00012 * simulation.accelScale; me.frictionAir = 0.025; me.collisionFilter.mask = cat.player | cat.bullet //| cat.body me.showHealthBar = false; me.memory = Infinity; me.isBoss = true; Matter.Body.setDensity(me, 1); //extra dense //normal is 0.001 //makes effective life much larger me.onDeath = function () { destroyed = true; powerUps.spawnBossPowerUp(this.position.x, this.position.y); } me.do = function () { if (this.health < 1) { this.health += 0.001; //regen simulation.drawList.push({ x: this.position.x, y: this.position.y, radius: this.radius / 1.5, color: `rgba(0, 255, 20, ${Math.random() * 0.1})`, time: simulation.drawTime }); } this.curl() this.repelBullets() this.seePlayerCheckByDistance() this.checkStatus(); const eventHorizon = this.eventHorizon * (0.93 + 0.17 * Math.sin(simulation.cycle * 0.011)) //draw darkness ctx.beginPath(); ctx.arc(this.position.x, this.position.y, eventHorizon * 0.25, 0, 2 * Math.PI); ctx.fillStyle = "rgba(250,250,250,0.9)"; ctx.fill(); ctx.beginPath(); ctx.arc(this.position.x, this.position.y, eventHorizon * 0.55, 0, 2 * Math.PI); ctx.fillStyle = "rgba(250,250,250,0.5)"; ctx.fill(); ctx.beginPath(); ctx.arc(this.position.x, this.position.y, eventHorizon, 0, 2 * Math.PI); ctx.fillStyle = "rgba(250,250,250,0.1)"; ctx.fill(); //when player is inside event horizon if (Vector.magnitude(Vector.sub(this.position, player.position)) < eventHorizon) { if (m.immuneCycle < m.cycle) { if (m.energy > 0) m.energy -= 0.005 if (m.energy < 0.1) m.damage(0.0001 * simulation.dmgScale); } const angle = Math.atan2(player.position.y - this.position.y, player.position.x - this.position.x); player.force.x += 0.00125 * player.mass * Math.cos(angle) * (m.onGround ? 1.8 : 1); player.force.y += 0.0001 * player.mass * Math.sin(angle); //draw line to player ctx.beginPath(); ctx.moveTo(this.position.x, this.position.y); ctx.lineTo(m.pos.x, m.pos.y); ctx.lineWidth = Math.min(60, this.radius * 2); ctx.strokeStyle = "rgba(250,250,250,0.5)"; ctx.stroke(); ctx.beginPath(); ctx.arc(m.pos.x, m.pos.y, 40, 0, 2 * Math.PI); ctx.fillStyle = "rgba(250,250,250,0.3)"; ctx.fill(); } } } const sniper = function (x, y, radius = 35 + Math.ceil(Math.random() * 30)) { //same, just white so that we can seen mobs.spawn(x, y, 3, radius, "transparent"); //"rgb(25,0,50)") let me = mob[mob.length - 1]; me.vertices = Matter.Vertices.rotate(me.vertices, Math.PI, me.position); //make the pointy side of triangle the front me.isVerticesChange = true // Matter.Body.rotate(me, Math.PI) me.stroke = "transparent"; //used for drawSneaker me.alpha = 1; //used in drawSneaker me.showHealthBar = false; me.frictionStatic = 0; me.friction = 0; me.canTouchPlayer = false; //used in drawSneaker me.isBadTarget = true; me.collisionFilter.mask = cat.map | cat.body | cat.bullet | cat.mob //can't touch player me.memory = 30 //140; me.fireFreq = 0.005 + Math.random() * 0.002 + 0.0005 * simulation.difficulty; //larger = fire more often me.noseLength = 0; me.fireAngle = 0; me.accelMag = 0.0005 * simulation.accelScale; me.frictionAir = 0.05; me.torque = 0.0001 * me.inertia; me.fireDir = { x: 0, y: 0 }; me.onDeath = function () { //helps collisions functions work better after vertex have been changed // this.vertices = Matter.Vertices.hull(Matter.Vertices.clockwiseSort(this.vertices)) } // spawn.shield(me, x, y); me.do = function () { // this.seePlayerByLookingAt(); this.seePlayerCheck(); this.checkStatus(); const setNoseShape = () => { const mag = this.radius + this.radius * this.noseLength; this.vertices[1].x = this.position.x + Math.cos(this.angle) * mag; this.vertices[1].y = this.position.y + Math.sin(this.angle) * mag; }; //throw a mob/bullet at player if (this.seePlayer.recall) { //set direction to turn to fire if (!(simulation.cycle % this.seePlayerFreq)) { this.fireDir = Vector.normalise(Vector.sub(this.seePlayer.position, this.position)); // this.fireDir.y -= Math.abs(this.seePlayer.position.x - this.position.x) / 1600; //gives the bullet an arc } //rotate towards fireAngle const angle = this.angle + Math.PI / 2; // c = Math.cos(angle) * this.fireDir.x + Math.sin(angle) * this.fireDir.y; //rotate towards fireAngle const dot = Vector.dot({ x: Math.cos(angle), y: Math.sin(angle) }, this.fireDir) const threshold = 0.03; if (dot > threshold) { this.torque += 0.000004 * this.inertia; } else if (dot < -threshold) { this.torque -= 0.000004 * this.inertia; } else if (this.noseLength > 1.5 && dot > -0.2 && dot < 0.2) { //fire spawn.sniperBullet(this.vertices[1].x, this.vertices[1].y, 7 + Math.ceil(this.radius / 15), 5); const v = 10 + 8 * simulation.accelScale; Matter.Body.setVelocity(mob[mob.length - 1], { x: this.velocity.x + this.fireDir.x * v + Math.random(), y: this.velocity.y + this.fireDir.y * v + Math.random() }); this.noseLength = 0; // recoil this.force.x -= 0.005 * this.fireDir.x * this.mass; this.force.y -= 0.005 * this.fireDir.y * this.mass; } if (this.noseLength < 1.5) this.noseLength += this.fireFreq; setNoseShape(); } else if (this.noseLength > 0.1) { this.noseLength -= this.fireFreq / 2; setNoseShape(); } // else if (this.noseLength < -0.1) { // this.noseLength += this.fireFreq / 4; // setNoseShape(); // } if (this.seePlayer.recall) { if (this.alpha < 1) this.alpha += 0.01; } else { if (this.alpha > 0) this.alpha -= 0.03; } //draw if (this.alpha > 0) { if (this.alpha > 0.95) { this.healthBar(); if (!this.canTouchPlayer) { this.canTouchPlayer = true; this.isBadTarget = false; this.collisionFilter.mask = cat.player | cat.map | cat.body | cat.bullet | cat.mob; //can touch player } } //draw body ctx.beginPath(); const vertices = this.vertices; ctx.moveTo(vertices[0].x, vertices[0].y); for (let j = 1, len = vertices.length; j < len; ++j) { ctx.lineTo(vertices[j].x, vertices[j].y); } ctx.lineTo(vertices[0].x, vertices[0].y); ctx.fillStyle = `rgba(250,250,250,${this.alpha * this.alpha})`; ctx.fill(); } else if (this.canTouchPlayer) { this.canTouchPlayer = false; this.isBadTarget = true this.collisionFilter.mask = cat.map | cat.body | cat.bullet | cat.mob //can't touch player } }; } const portal = level.portal({ x: 650, y: -1000 }, Math.PI * 1.5, { x: 525, y: 2625 }, -Math.PI) document.body.style.transition = '0ms' document.body.style.backgroundColor = "#061026" //"#061026"; var yy = new Image(); yy.src = 'https://raw.githubusercontent.com/Whyisthisnotavalable/image-yy/main/Hotpot6.png'; color.map = "#FFFFFF11"; color.bullet = "#FFFFFF"; level.custom = () => { level.enter.draw(); level.exit.drawAndCheck(); ctx.drawImage(yy, 0 - 500, 0 - 500, 1000, 1000) portal[0].draw(); portal[1].draw(); portal[2].query(); portal[3].query(); if (destroyed == false) { lock.isClosing = true; } else { lock.isClosing = false; } lock.openClose(); }; level.customTopLayer = () => { lock.draw() /* ctx.beginPath() ctx.strokeStyle = "transparent"; ctx.fillStyle = "#FFFFFF22" ctx.arc(m.pos.x, m.pos.y, 500, 0, Math.PI * 2) ctx.fill() ctx.fillStyle = "#FFFFFF55" ctx.arc(m.pos.x, m.pos.y, 1000, 0, Math.PI * 2) ctx.fill(); ctx.stroke(); */ ctx.beginPath(); ctx.moveTo(m.pos.x, m.pos.y) const arc = Math.PI / 4 ctx.arc(m.pos.x, m.pos.y, 100, m.angle + arc, m.angle - arc) ctx.arc(m.pos.x, m.pos.y, 4000, m.angle - arc, m.angle + arc) ctx.fillStyle = "rgba(255,255,255,0.7)"; ctx.globalCompositeOperation = "destination-in"; ctx.fill(); ctx.globalCompositeOperation = "source-over"; ctx.clip(); }; level.setPosToSpawn(0, -50); level.exit.x = -275; level.exit.y = 2900; level.defaultZoom = 1800 simulation.zoomTransition(level.defaultZoom) //map spawn.mapRect(-125, -325, 225, 25); spawn.mapRect(300, -150, 25, 325); spawn.mapRect(-325, -125, 25, 300); spawn.mapRect(-150, 300, 275, 25); spawn.mapRect(125, -300, 25, 25); spawn.mapRect(175, -275, 25, 25); spawn.mapRect(225, -250, 25, 25); spawn.mapRect(250, -200, 25, 25); spawn.mapRect(-175, -300, 25, 25); spawn.mapRect(-225, -275, 25, 25); spawn.mapRect(-300, -200, 25, 25); spawn.mapRect(150, 275, 25, 25); spawn.mapRect(200, 225, 25, 25); spawn.mapRect(250, 200, 25, 25); spawn.mapRect(250, -225, 25, 25); spawn.mapRect(275, -175, 25, 25); spawn.mapRect(200, -275, 25, 25); spawn.mapRect(150, -300, 25, 25); spawn.mapRect(100, -325, 25, 25); spawn.mapRect(-150, -300, 25, 25); spawn.mapRect(-200, -300, 25, 25); spawn.mapRect(-250, -250, 25, 25); spawn.mapRect(-275, -225, 25, 25); spawn.mapRect(-300, -175, 25, 50); spawn.mapRect(275, 175, 25, 25); spawn.mapRect(250, 200, 25, 25); spawn.mapRect(225, 225, 25, 25); spawn.mapRect(175, 250, 25, 25); spawn.mapRect(125, 300, 25, 25); spawn.mapRect(-300, 325, 200, 150); spawn.mapRect(-400, 425, 225, 150); spawn.mapRect(-4450, 2900, 1550, 150); spawn.mapRect(-4500, 2525, 150, 525); spawn.mapRect(-4800, 2150, 150, 400); spawn.mapRect(-4400, 2025, 650, 150); spawn.mapRect(-2425, 50, 2125, 150); spawn.mapRect(-2425, 50, 150, 1300); spawn.mapRect(-4600, 1175, 2325, 175); spawn.mapRect(-5075, 1650, 450, 150); spawn.mapRect(-4650, 1225, 75, 125); spawn.mapRect(-4700, 1275, 75, 75); spawn.mapRect(-425, 2925, 425, 125); spawn.mapRect(-450, 2375, 450, 100); spawn.mapRect(-3050, 550, 150, 450); spawn.mapRect(-2925, 825, 100, 175); spawn.mapRect(-2650, 375, 275, 125); spawn.mapRect(-75, 2950, 300, 100); spawn.mapRect(-625, -500, 125, 575); spawn.mapRect(-1050, -325, 275, 100); spawn.mapRect(-1075, -775, 100, 550); spawn.mapRect(-1075, -775, 300, 100); spawn.mapRect(-525, -1100, 1025, 625); spawn.mapRect(450, -1000, 450, 1500); spawn.mapRect(-300, 500, 1200, 75); spawn.mapRect(-200, 425, 725, 100); spawn.mapRect(525, 2450, 275, 600); spawn.mapRect(-25, 2375, 825, 125); spawn.mapRect(400, -1500, 500, 100); spawn.mapRect(800, -1500, 100, 525); spawn.mapRect(-400, 500, 1250, 1900); spawn.mapRect(-300, 2910, 150, 25); spawn.mapRect(-625, -1000, 125, 175); spawn.spawnStairs(-400, 3000, 25, 2500, 2500, 250); spawn.spawnStairs(500, 3000, 5, 250, 250, 250); spawn.debris(-1550, -250, 100); spawn.debris(-1100, 850, 100); spawn.debris(-3700, 1025, 100); spawn.debris(-3525, 2725, 100); spawn.debris(-4750, 2050, 100); spawn.debris(-4000, 1900, 100); spawn.debris(225, -1225, 100); //mobs spawn.sneaker(-1350, 1350); spawn.sneaker(-2275, 2275); sniper(-3050 + Math.floor(Math.random() * 100) - Math.floor(Math.random() * 100), 1475 + Math.floor(Math.random() * 100) - Math.floor(Math.random() * 100)); sniper(-2925 + Math.floor(Math.random() * 100) - Math.floor(Math.random() * 100), 1775 + Math.floor(Math.random() * 100) - Math.floor(Math.random() * 100)); sniper(-3075 + Math.floor(Math.random() * 100) - Math.floor(Math.random() * 100), 1600 + Math.floor(Math.random() * 100) - Math.floor(Math.random() * 100)); sniper(-3100 + Math.floor(Math.random() * 100) - Math.floor(Math.random() * 100), 1975 + Math.floor(Math.random() * 100) - Math.floor(Math.random() * 100)); sniper(-3075 + Math.floor(Math.random() * 100) - Math.floor(Math.random() * 100), 1750 + Math.floor(Math.random() * 100) - Math.floor(Math.random() * 100)); sniper(-3350, 425); sniper(-3550, 600); sniper(-3325, 775); sniper(-5525, 1975); sniper(-50, -1300); for (let i = 0; i < 10 + simulation.difficulty; i++) { spawn.ghoster(0 + Math.floor(Math.random() * 5000) - Math.floor(Math.random() * 5000), 0 + Math.floor(Math.random() * 5000) - Math.floor(Math.random() * 5000)) } core(-2000, -1000); powerUps.spawnStartingPowerUps(0, 0) powerUps.addResearchToLevel() }, staircase() { simulation.makeTextLog(`staircase by ryanbear`); level.custom = () => { level.exit.drawAndCheck(); level.enter.draw(); }; level.customTopLayer = () => { aaa.query(); bbb.query(); ccc.query(); ddd.move(); eee.query(); fff.query(); ggg.query(); hhh.query(); iii.query(); jjj.query(); kk.query(); lll.query(); mmm.query(); nnn.query(); ooo.query(); ppp.query(); }; level.setPosToSpawn(0, -50); //normal spawn level.exit.x = 7300; level.exit.y = -5154; // spawn.mapRect(level.enter.x, level.enter.y + 20, 100, 20); level.defaultZoom = 1800 simulation.zoomTransition(level.defaultZoom) document.body.style.backgroundColor = "#d8dadf"; // powerUps.spawnStartingPowerUps(1475, -1175); // spawn.debris(750, -2200, 3700, 16); //16 debris per level spawn.mapRect(-100, 0, 2100, 100); spawn.mapRect(1984, 17, 100, 500); spawn.mapRect(2013, 522, 1618, 100); spawn.bodyRect(2090, 328, 100, 100); spawn.mapRect(3619, 14, 100, 500) var aaa = level.hazard(1999, 10, 1618, 500); var bbb = level.vanish(2320, -345, 234, 20); var ccc = level.vanish(2862, -324, 234, 20); var eee = level.vanish(3002, -1100, 234, 20); var ddd = level.elevator(3399, -420, 200, 200, -950, 0.003, { up: 0.1, down: 0.2 }) //x, y, width, height, maxHeight, force = 0.003, friction = { up: 0.01, down: 0.2 }) { var fff = level.vanish(3359, -1300, 234, 20); var ggg = level.boost(3020, -1600, 700); var hhh = level.vanish(2700, -1940, 1147, 20); var iii = level.boost(5038, -2000, 700); var jjj = level.vanish(5092, -3498, 100, 100); var kk = level.boost(5092, -3772, 700); var lll = level.boost(5372, -2824, 700); var mmm = level.vanish(5112, -3000, 100, 100); var nnn = level.vanish(5367, -3000, 100, 100); var ooo = level.boost(4810, -3161, 700); var ppp = level.vanish(5383, -3485, 100, 100); spawn.mapRect(5377, -4198, 1000, 100); spawn.mapRect(6390, -4359, 200, 200); spawn.mapRect(6605, -4563, 200, 200); spawn.mapRect(6809, -4758, 200, 200); spawn.mapRect(7014, -4962, 200, 200); spawn.mapRect(7212, -5158, 200, 200); spawn.mapRect(4156, -1898, 1000, 100); // spawn.bodyRect(1540, -1110, 300, 25, 0.9); // spawn.randomSmallMob(1300, -70); spawn.randomMob(590, -315); spawn.randomMob(1343, -757); spawn.randomMob(4037, -926); spawn.randomMob(3621, -2376); spawn.randomMob(5026, -2441); spawn.randomMob(4253, -2863); spawn.randomMob(4355, -2430); spawn.randomMob(5316, -3265); spawn.randomMob(5885, -4427); spawn.randomMob(6666, -4979); spawn.laserBoss(6128, -4905); // spawn.randomGroup(1700, -900, 0.4); // if (simulation.difficulty > 1) spawn.randomLevelBoss(2200, -1300); powerUps.addResearchToLevel() //needs to run after mobs are spawned }, fortress() { simulation.makeTextLog(`fortress by Desboot`); const boost1 = level.boost(3600, -250, 1000) const boost2 = level.boost(60, -604, 1000) const boost3 = level.boost(2160, -1260, 1000) powerUps.spawnStartingPowerUps(1033.3, -121.4) level.custom = () => { boost1.query(); boost2.query(); boost3.query(); level.exit.drawAndCheck(); level.enter.draw(); }; level.setPosToSpawn(0, -50); //normal spawn level.exit.x = 3586; //3586.5, -1464.0 level.exit.y = -1494; spawn.mapRect(level.enter.x, level.enter.y + 20, 100, 20); level.defaultZoom = 1800 simulation.zoomTransition(level.defaultZoom) document.body.style.backgroundColor = "#d8dadf"; level.customTopLayer = () => { ctx.fillStyle = "rgba(0,0,0,0.3)" ctx.fillRect(-272, -580, 1700, 600) ctx.fillRect(1427.5, -487, 1280, 600) ctx.fillRect(2707.3, -580, 1200, 600) ctx.fillStyle = "rgba(0,0,0,0.2)" ctx.fillRect(2752, -1744, 1075, 1164) ctx.fillRect(937, -1590, 339, 550) ctx.fillRect(1158, -1040, 118, 550) ctx.fillRect(3049, -1063, 339, 500) ctx.fillRect(1439, -1281, 297, 800) ctx.fillRect(2130, -1182, 167, 800) ctx.fillRect(1892, -2073, 238, 1593) ctx.fillRect(2297, -2073, 238, 1593) ctx.fillStyle = "rgba(0,0,0,0.15)" ctx.fillRect(483, -1277, 350, 700) ctx.fillRect(833, -1000, 325, 450) ctx.fillStyle = "rgba(64,64,64,0.97)" //hidden section ctx.fillRect(2800, -1712, 730, 300) }; spawn.debris(2700, -120, 180, 3); spawn.debris(1350, -100, 280, 3); spawn.debris(2300, -700, 380, 5); spawn.debris(976, -775, 38, 5); spawn.debris(840, -1424, 3080, 5); spawn.debris(2300, -700, 3080, 5); spawn.mapRect(-272, 0, 4198, 123); spawn.mapRect(-272, -581, 132, 581); spawn.mapRect(-272, -581, 572, 326); spawn.mapRect(1462, -229, 92, 229); spawn.mapRect(1462, -229, 352, 57); spawn.mapRect(2872, -220, 1056, 330); spawn.mapRect(170, -260, 484, 80); spawn.mapRect(476, -581, 1162, 75); spawn.mapRect(951, -519, 1760, 132); spawn.mapRect(1747, -492, 506, 66); spawn.mapRect(2462, -581, 1074, 75); spawn.mapRect(1136, -616, 510, 100); spawn.mapRect(3815.6, -1461, 114, 1300); //far right wall spawn.mapRect(480, -1456, 106, 651); //far left wall spawn.mapRect(1426, -906, 106, 400); spawn.mapRect(480, -1302, 374, 57); spawn.mapRect(788, -1302, 75, 308); spawn.mapRect(788, -1055, 370, 62); spawn.mapRect(3049, -1170, 471, 106); spawn.mapRect(3348, -1170, 188, 663); spawn.mapRect(2751, -1461, 1088, 53); //roof under the exit spawn.mapRect(2751, -1743, 92, 915); //wall on left or far right side spawn.mapRect(937, -1667, 339, 84); //upper left platform spawn.mapRect(1135, -3239, 119, 1450); spawn.mapRect(1440, -1346, 295, 66); //center left platform spawn.mapRect(2090, -1240, 242, 57); //center righ platform spawn.mapRect(1892, -2214, 88, 220); //vertical part of left L spawn.mapRect(1892, -2073, 238, 84); //flat part of left L spawn.mapRect(2447, -2214, 88, 220); //vertical part of right L spawn.mapRect(2297, -2073, 238, 84); //flat part of right L spawn.mapRect(2751, -1743, 1078, 57); //exit roof //3587.2, -1470.0 spawn.mapRect(3584, -1470, 103, 57); //wall below door3689 spawn.mapRect(3428, -1735, 103, 173); //wall covering secret spawn.mapRect(-11000, -1000, 100, 10); //SAL spawn.mapRect(-11000, -1000, 10, 100); //SAL spawn.mapRect(-10900, -900, 10, 100); //SAL spawn.mapRect(-11000, -900, 100, 10); //SAL spawn.mapRect(-11000, -800, 100, 10); //SAL spawn.mapRect(-10800, -1000, 10, 200); //SAL spawn.mapRect(-10700, -1000, 10, 200); //SAL spawn.mapRect(-10800, -1000, 100, 10); //SAL spawn.mapRect(-10800, -900, 100, 10); //SAL spawn.mapRect(-10600, -1000, 10, 200); //SAL spawn.mapRect(-10600, -800, 100, 10); //SAL spawn.mapRect(-11000, -91000, 100, 10); //SAL spawn.mapRect(-11000, -91000, 10, 100); //SAL spawn.mapRect(-10900, -90900, 10, 100); //SAL spawn.mapRect(-11000, -90900, 100, 10); //SAL spawn.mapRect(-11000, -90800, 100, 10); //SAL spawn.mapRect(-10800, -91000, 10, 200); //SAL spawn.mapRect(-10700, -91000, 10, 200); //SAL spawn.mapRect(-10800, -91000, 100, 10); //SAL spawn.mapRect(-10800, -90900, 100, 10); //SAL spawn.mapRect(-10600, -91000, 10, 200); //SAL spawn.mapRect(-10600, -90800, 100, 10); //SAL //mobs spawn.randomMob(3104.9, -1284.9, 0.2); spawn.randomMob(1784.7, -95.9, 0.2); spawn.randomMob(3474.2, -406.7, 0.1); spawn.randomMob(1603.2, -1493.5, 0.4); spawn.randomMob(772.4, -1505.2, 0.2); spawn.randomMob(824.6, -781.3, 0.2); spawn.randomMob(818.8, -1468.9, 0.2); spawn.randomMob(-124.7, -853, 0.2); spawn.randomMob(3011.1, -1978.0, -0.2); spawn.randomMob(2428.0, -236.8, 0.1); spawn.randomSmallMob(694.3, -385.3); spawn.randomSmallMob(1142.0, -808.4); spawn.randomSmallMob(791.5, -803.7); spawn.randomSmallMob(3175.8, -830.8); spawn.randomSmallMob(1558.5, -1940.8); spawn.randomSmallMob(2700, -475); spawn.randomSmallMob(2700, -475); spawn.pulsar(1762.9, -2768.3) spawn.pulsar(3821.5, -2373.9) let randomBoss = Math.floor(Math.random() * 5); spawn[["laserBoss", "blinkBoss", "shooterBoss", "launcherBoss", "pulsarBoss", "beetleBoss", "bladeBoss", "revolutionBoss", "dragonFlyBoss", "spiderBoss"][randomBoss]](2058.5, -711.4, 100, false); //spawn powerups // powerUps.spawn(3167.6, -1300, "tech") powerUps.spawn(3125.8, -1543.4, "tech") powerUps.spawn(3125.8, -1543.4, "heal") powerUps.spawn(3125.8, -1543.4, "ammo") powerUps.spawn(3125.8, -1543.4, "ammo") powerUps.spawn(3137.6, -1300, "ammo") powerUps.spawn(1605.2, -1436.9, "heal") powerUps.spawn(2912.9, -1940.9, "ammo") powerUps.spawn(3167.6, -1300, "heal") powerUps.spawn(1, 1, "ammo") powerUps.addResearchToLevel() //needs to run after mobs are spawned }, commandeer() { simulation.makeTextLog(`commandeer by Desboot`); let waterFallWidth = 400 let waterFallX = 15900 let waterFallSmoothX = 0 const elevator = level.elevator(-80.4, -931.6, 180, 50, -1550) 15900 && player.position.x < 16300 && player.position.y > -960.2 //const slime = level.hazard(15900, -960, 400, 6000); const slime2 = level.hazard(15147.2, -1782.4, 2000, 822); const boost1 = level.boost(5950, -20, 700) const boost2 = level.boost(21088, -1672, 700) const boost3 = level.boost(19390, -31, 1700) const boost4 = level.boost(19390, -31, 1700) const boost5 = level.boost(17274, -1242, 1000) const portal = level.portal({ x: 443, y: -1636 }, Math.PI, { x: 21391.9, y: -1806.3 }, -Math.PI) const portal2 = level.portal({ x: 16838.3, y: -626.7 }, Math.PI, { x: 16882.8, y: -2566.5 }, -Math.PI) const buttonDoor = level.button(21889, -10) const door = level.door(19119, -2133, 110, 510, 480) const buttonDoor2 = level.button(18711, -2210) const door2 = level.door(17041, -412, 110, 510, 480) const buttonDoor3 = level.button(20456.6, -1636.2) const door3 = level.door(20238, -781.4, 88, 452, 412) const hazard2 = level.hazard(2550, -150, 10, 0.4) //y=-1485 simulation.enableConstructMode() level.setPosToSpawn(0, -50); //normal spawn level.exit.x = 15316; level.exit.y = -30; spawn.mapRect(level.enter.x, level.enter.y + 20, 100, 20); //bump for level entrance spawn.mapRect(level.exit.x, level.exit.y + 20, 100, 20); //bump for level exit level.defaultZoom = 1800 simulation.zoomTransition(level.defaultZoom) document.body.style.backgroundColor = "#001738"; color.map = "#444" //custom map color level.custom = () => { //spawn.mapRect(22330, -2688.75, 400, 800); //spawn.mapRect(22330, -1793.5, 400, 800);//-46.25*2=-92.5 //spawn.mapRect(22330, -804.25, 400, 800);//-46.25*3 ctx.fillStyle = "rgba(250,250,250,0.8)"//lights ctx.beginPath() ctx.moveTo(1124, -628) ctx.lineTo(496, 0) ctx.lineTo(1852, 0) ctx.lineTo(1224, -628) ctx.fill() ctx.beginPath() ctx.moveTo(906, -1365) ctx.lineTo(206, -690) ctx.lineTo(1706, -690) ctx.lineTo(1006, -1365) ctx.fill() ctx.beginPath() ctx.moveTo(3330, -1905)//-700 ctx.lineTo(2815.6, -1405.8) ctx.lineTo(2815.6, -1230) ctx.lineTo(4022.9, -1283.9) ctx.lineTo(4023.5, -1405.8) ctx.lineTo(3430, -1905) ctx.fill() ctx.fillStyle = "rgba(63,247,251,0.8)" ctx.fillRect(22330, -2713.75, 550, 700)//15845.0, -1262.2 ctx.fillRect(22330, -1743.5, 550, 700) ctx.fillRect(22330, -754.25, 550, 700) ctx.fillRect(6237, -1830.7, 550, 700) ctx.fillRect(6237, -840.4, 550, 700) ctx.fillStyle = "rgba(200,200,200,0.8)" ctx.fillRect(-192, -1973, 6484, 2071) ctx.fillStyle = "rgba(240,240,240,0.8)" ctx.fillRect(15109.5, -2867.5, 7284, 2971) ctx.fillStyle = "rgba(35,35,35,0.8)" ctx.fillRect(15145.9, -960, 200, 25) ctx.fillStyle = "rgba(255,255,255,0.9)" buttonDoor.query(); buttonDoor.draw(); buttonDoor2.query(); buttonDoor2.draw(); buttonDoor3.query(); buttonDoor3.draw(); //slime.query(); slime2.query(); if (buttonDoor.isUp) { door.isClosing = true } else { door.isClosing = false } if (buttonDoor2.isUp) { door2.isClosing = true } else { door2.isClosing = false } if (buttonDoor3.isUp) { door3.isClosing = true } else { door3.isClosing = false } door.openClose(); door2.openClose(); door3.openClose(); portal[2].query() portal[3].query() portal2[2].query() portal2[3].query() boost1.query(); boost2.query(); boost3.query(); boost4.query(); boost5.query(); level.exit.drawAndCheck(); level.enter.draw(); ctx.fillStyle = "rgba(0,0,0,0.2)"//shadows ctx.fillRect(2773, -682, 469, 500) ctx.fillRect(3947, -851, 469, 700) ctx.fillRect(4818, -1006, 400, 400) ctx.fillRect(5313, -1309, 1000, 700) ctx.fillRect(16705, -2831, 40, 700) ctx.fillRect(16140, -2812, 40, 400) ctx.fillRect(15559, -2855, 40, 800) ctx.fillRect(16530, -2855, 30, 200) ctx.beginPath() ctx.moveTo(18254.7, -2194.1) ctx.lineTo(18554.6, -1952.7) ctx.lineTo(18554.6, -1992.7) ctx.lineTo(18294.7, -2194.1) ctx.fill() ctx.beginPath() ctx.moveTo(18154.7, -1004.1) ctx.lineTo(18554.6, -762.7) ctx.lineTo(18554.6, -802.7) ctx.lineTo(18214.7, -1004.1) ctx.fill() ctx.beginPath() ctx.moveTo(17585.2, -1123.8) ctx.lineTo(17151.2, -781.7) ctx.lineTo(17151.2, -741.7) ctx.lineTo(17625.2, -1123.8) ctx.fill() ctx.fillRect(20540, -1103, 610, 300) ctx.fillRect(20820, -243, 410, 300) ctx.fillRect(5772, -609, 469, 700) ctx.fillRect(5772, -609, 469, 700) ctx.fillStyle = "rgba(48,184,140,255)" ctx.fillRect(waterFallX, -960, waterFallWidth, 6000) ctx.fillStyle = `hsla(160, 100%, 43%,${0.3 + 0.07 * Math.random()})` ctx.fillRect(waterFallX + waterFallWidth * Math.random(), -900 - Math.random() * 400, Math.random() * 5 + 8, 6000) ctx.fillRect(waterFallX + waterFallWidth * Math.random(), -900 - Math.random() * 400, Math.random() * 5 + 5, 6000) waterFallWidth = 0.995 * waterFallWidth + 4 * Math.random()//4.7 waterFalSmoothlX = 0.96 * waterFallSmoothX + 20 * Math.random()//3.5 waterFallX = waterFallSmoothX + 15900 ctx.fillStyle = "rgba(0,0,0,0.4)"//wires ctx.fillRect(20990, -2672, 20, 112) ctx.fillRect(21090, -2506, 72, 20) ctx.fillRect(21090, -1970, 72, 20) ctx.fillRect(16901.8, -2497.7, 25, 100) ctx.fillRect(16901.8, -2397.7, 50, 25) ctx.fillRect(16951.8, -2397.7, 25, 1640) ctx.fillRect(16901.8, -782.7, 50, 25) ctx.fillRect(16901.8, -757.7, 25, 100) ctx.fillRect(20900, -2666, 500, 9) ctx.fillRect(20900, -2651, 1315, 9) ctx.fillRect(20900, -2636, 1300, 9) ctx.fillRect(20900, -2621, 245, 9) ctx.fillRect(20900, -2606, 230, 9) ctx.fillRect(20900, -2591, 215, 9) ctx.fillRect(20900, -2576, 200, 9) ctx.fillRect(21145, -2621, 9, 700) ctx.fillRect(21130, -2606, 9, 1000) ctx.fillRect(21115, -2591, 9, 1000) ctx.fillRect(21100, -2576, 9, 850) ctx.fillRect(21400, -3066, 9, 409) ctx.fillRect(20900, -1726, 209, 9) ctx.fillRect(21145, -1921, 270, 9) ctx.fillRect(21415, -1921, 9, 50) ctx.fillRect(22200, -2636, 9, 1300) ctx.fillRect(22215, -2651, 9, 300) ctx.fillRect(22200, -1336, 300, 9) ctx.fillRect(22215, -2351, 300, 9) //943.9, -1698.0 ctx.fillRect(916.5, -1725, 80, 80)//+55 // 55/2=27.5 ctx.fillRect(1204, -1706, 25, 40)//179 ctx.fillRect(1354, -1706, 25, 40) ctx.fillRect(1504, -1885, 25, 40) ctx.fillRect(3504, -1885, 25, 40) ctx.fillRect(5504, -1885, 25, 40) ctx.fillRect(1019, -1718, 9, 20) ctx.fillRect(1019, -1674, 9, 20) ctx.fillRect(996, -1718, 23, 9) ctx.fillRect(996, -1663, 23, 9) ctx.fillRect(1019, -1698, 425, 9) ctx.fillRect(1444, -1868, 9, 179) ctx.fillRect(1444, -1877, 4700, 9) ctx.fillRect(1019, -1683, 440, 9) ctx.fillRect(1459, -1853, 9, 179) ctx.fillRect(1459, -1862, 4670, 9) ctx.fillRect(6144, -1877, 9, 100) ctx.fillRect(6144, -1777, 100, 9) ctx.fillRect(6129, -1862, 9, 1100) ctx.fillRect(6129, -762, 150, 9) }; level.customTopLayer = () => { door.draw(); door2.draw(); door3.draw(); portal[0].draw(); portal[1].draw(); portal[2].draw(); portal[3].draw(); portal2[0].draw(); portal2[1].draw(); portal2[2].draw(); portal2[3].draw(); elevator.move() // if (player.position.x > 15900 && player.position.x < 16300 && player.position.y > -1360.2) { // Matter.Body.setVelocity(player, { // x: player.velocity.x, // y: player.velocity.y + 10 // }); // }else{ // if (Math.abs(player.velocity.x) > 0.5){ // if (m.onGround){ // Matter.Body.setVelocity(player, { // x: player.velocity.x + (0.07 * (Math.abs(player.velocity.x) / player.velocity.x)), // y: player.velocity.y - 0.2 // }); // }else{ // Matter.Body.setVelocity(player, { // x: player.velocity.x, // y: player.velocity.y - 0.2 // }); // } // }else{ // Matter.Body.setVelocity(player, { // x: player.velocity.x, // y: player.velocity.y - 0.2 // }); // } // } if (player.position.x > 15900 && player.position.x < 16300 && player.position.y > -1360.2) { Matter.Body.setVelocity(player, { x: player.velocity.x, y: player.velocity.y + 2 }); } else { if (Math.abs(player.velocity.x) > 0.5) { if (m.onGround) { Matter.Body.setVelocity(player, { x: player.velocity.x + (0.07 * (Math.abs(player.velocity.x) / player.velocity.x)), y: player.velocity.y - 0.2 }); } else { Matter.Body.setVelocity(player, { x: player.velocity.x, y: player.velocity.y - 0.2 }); } } else { Matter.Body.setVelocity(player, { x: player.velocity.x, y: player.velocity.y - 0.2 }); } } hazard2.opticalQuery(); }; //1273.2, -1404.7 spawn.mapRect(1124, -653, 100, 25); spawn.mapRect(906, -1390, 100, 25); spawn.mapRect(3330, -1930, 100, 25); //first ship base spawn.mapRect(-300, 0, 6684, 100);//lower floor spawn.mapRect(-300, -2071, 154, 2071);//left right wall spawn.mapRect(2511, -300, 1309, 308);//left big block spawn.mapRect(3820, -184, 1309, 184);//right big block spawn.mapRect(-300, -739, 2549, 100);//upper right floor spawn.mapRect(2056, -1309, 2764, 169);//upper center floor spawn.mapRect(2056, -1309, 193, 650);//upper left floor wall spawn.mapRect(4636, -1309, 193, 793);//upper right floor wall spawn.mapRect(4821, -654, 955, 138);//upper right floor spawn.mapRect(6237, -2071, 147, 2071);//far right wall spawn.mapRect(-300, -2071, 6684, 154);//roof //first ship details spawn.mapRect(245, -360, 70, 400);//start room wall spawn.mapRect(500, -1929, 154, 462); spawn.mapRect(185, -1517, 469, 77); spawn.mapRect(2773, -682, 469, 77);//walls in 1st room spawn.mapRect(3743, -566, 77, 469); spawn.mapRect(3947, -851, 469, 77); spawn.mapRect(5313, -1309, 1000, 70);//walls in second area spawn.mapRect(4818, -1006, 400, 70); spawn.mapRect(4768, -1626, 800, 70); spawn.mapRect(4760, -1626, 70, 400); spawn.mapRect(645.1, -1480.8, 700, 100);//room for shielding boss spawn.mapVertex(515, -1447, "0 0 0 100 -400 0"); spawn.mapRect(1245.1, -1980.8, 100, 500); spawn.mapRect(2346.9, -1658.8, 469, 77); spawn.mapRect(4023.6, -1723.7, 469, 77); //engines //y -2972 -> 0 spawn.mapRect(6237, -1880.7, 400, 800); spawn.mapRect(6237, -890.4, 400, 800); //first ship blocks/debris spawn.debris(3267.6, -797.1, 700, 5); //16 debris per level spawn.debris(1626.0, -372.5, 1700, 8); //16 debris per level spawn.debris(1880.1, -1508.9, 3700, 16); //16 debris per level spawn.debris(5335.3, -1431.6, 3700, 16); //16 debris per level spawn.debris(1563.8, -1087.9, 700, 5); //16 debris per level spawn.bodyRect(1540, -1110, 218, 125, 0.9); //first ship mobs spawn.randomSmallMob(893.5, -120.8); // spawn.randomMob(2903.9, -754.5, 0.4); // spawn.randomMob(5577.0, -217.0, 0.2); // spawn.randomMob(765.8, -1029.7, 0.5); // spawn.randomMob(2680.1, -1779.2, 0.6); // spawn.randomMob(20079.4, -2219.7, 0.4); // spawn.randomMob(3924.9, -1504.1, 0.5); // spawn.randomMob(21284.2, -983.1, 0.3); // spawn.randomMob(20381.0, -254.2, 0.5); // spawn.randomMob(18375.6, -1574.4, 0.6); // spawn.randomMob(19448.2, -1323.3, 0.3); // spawn.randomMob(18397.7, -711.2, 0.3); // spawn.randomMob(15547.2, -2249.6, 0.5); // spawn.randomSmallMob(16114.6, -2524.2); // spawn.randomSmallMob(15378.9, -2549.6); // spawn.randomSmallMob(3266.4, -1578.4); // spawn.randomSmallMob(4386.2, -439.6); // spawn.randomSmallMob(5667.0, -847.8); // spawn.randomSmallMob(3158.5, -1581.8); // spawn.randomSmallMob(3866.7, -1483.2); // spawn.randomSmallMob(4652.3, -1729.4); // spawn.randomSmallMob(1068.7, -106.1); // spawn.randomSmallMob(3382.5, -1590.6);//3545.0, -413.0 // spawn.randomSmallMob(5099.7, -1204.2); // spawn.randomSmallMob(1456.4, -1014.8); // spawn.randomSmallMob(20432.4, -1374.3); // spawn.randomSmallMob(20381.0, -254.2); // spawn.randomSmallMob(3505.1, -1531.1); // spawn.randomSmallMob(20648.1, -136.8); // spawn.randomSmallMob(17502.8, -1520.6); // spawn.randomSmallMob(17438.7, -876.7); spawn.randomMob(18375.6, -1574.4, 0.2); spawn.randomSmallMob(15378.9, -2549.6); spawn.randomSmallMob(5820.2, -1545.2); spawn.randomMob(765.8, -1029.7, 0.2); spawn.randomMob(21284.2, -983.1, 0.3); spawn.randomSmallMob(3382.5, -1590.6); spawn.randomSmallMob(3545.0, -413.0); spawn.randomMob(20381.0, -254.2, 0.6); spawn.randomSmallMob(20432.4, -1374.3); spawn.randomSmallMob(5667.0, -847.8); spawn.randomMob(2903.9, -754.5, 0.2); spawn.randomSmallMob(3266.4, -1578.4); spawn.randomSmallMob(20648.1, -136.8); spawn.randomSmallMob(16114.6, -2524.2); spawn.randomSmallMob(20381.0, -254.2); spawn.randomMob(5577.0, -217.0, 0.3); spawn.randomSmallMob(1456.4, -1014.8); spawn.randomSmallMob(1068.7, -106.1); spawn.randomSmallMob(5099.7, -1204.2); spawn.randomSmallMob(17502.8, -1520.6); spawn.randomMob(15547.2, -2249.6, 0.2); spawn.randomMob(19448.2, -1323.3, 0.7); spawn.randomSmallMob(3158.5, -1581.8); spawn.randomSmallMob(17438.7, -876.7); spawn.randomMob(20079.4, -2219.7, 0.2); spawn.randomMob(2680.1, -1779.2, 0.6); spawn.randomMob(3924.9, -1504.1, 0.3); spawn.randomSmallMob(4652.3, -1729.4); spawn.randomMob(18397.7, -711.2, 0.3); spawn.randomSmallMob(4386.2, -439.6); spawn.randomSmallMob(3505.1, -1531.1); spawn.randomSmallMob(3866.7, -1483.2); //second ship mobs spawn.debris(17732.3, -550.0, 700, 5); //16 debris per level spawn.debris(17827.2, -2357.1, 700, 5); //16 debris per level spawn.debris(16108.6, -2621.1, 700, 5); //16 debris per level spawn.debris(20823.6, -1332.1, 1300, 5); //16 debris per level spawn.debris(21095.5, -423.4, 700, 5); //16 debris per level spawn.debris(20534.5, -1282.1, 700, 5); //16 debris per level spawn.randomSmallMob(1300, -70); spawn.shieldingBoss(943.9, -1698.0) //second ship base spawn.mapRect(15000, 0, 515, 185);//lower floor 1 spawn.mapRect(17015, 0, 5500, 185);//lower floor 2 spawn.mapRect(15000, -2972, 185, 2972);//left wall spawn.mapRect(15000, -2972, 7515, 185);//roof spawn.mapRect(22330, -2972, 185, 2972);//right wall spawn.mapRect(17002, -2972, 169, 2564);//left middle wall spawn.mapRect(19089, -2972, 169, 855);//right middle wall upper spawn.mapRect(19089, -1625, 169, 1800);//right middle wall lower spawn.mapRect(20760, -2972, 169, 1350);//medium wall left of portal spawn.mapRect(19720, -1625, 1725, 162);//right room upper floor spawn.mapRect(21440, -2325, 169, 863);//medium wall right of portal spawn.mapRect(19720, -855, 2725, 162);//right room lower floor //engines //y -2972 -> 0 spawn.mapRect(22330, -2763.75, 400, 800); spawn.mapRect(22330, -1793.5, 400, 800); spawn.mapRect(22330, -804.25, 400, 800); //second ship details spawn.mapRect(19904, -1465, 85, 362);//upper L spawn.mapRect(19542, -1191, 412, 88);//lower L spawn.mapRect(18546, -2199, 600, 82);//2nd room enternce wall spawn.mapRect(18546, -2499, 82, 2300); spawn.mapRect(18108, -326, 500, 82);//walls/floors in middle room spawn.mapRect(17750, -682, 300, 82); spawn.mapRect(17156, -468, 500, 60); spawn.mapRect(18022, -1082, 600, 82); spawn.mapRect(17151, -1196, 500, 82); spawn.mapRect(17453, -2060, 500, 82); spawn.mapRect(18197, -2269, 400, 82); spawn.mapRect(18108, -326, 500, 82); spawn.mapRect(20542, -1191, 612, 88); spawn.mapRect(20238, -1191, 88, 412); spawn.mapRect(21520, -1468, 88, 412); spawn.mapRect(20238, -330.2, 88, 412); spawn.mapRect(20819, -328.3, 412, 88); spawn.mapRect(21532, -708, 88, 412); spawn.mapRect(15483.8, 12.5, 388, 30);//broken floor spawn.mapRect(15487.6, 76.6, 488, 24); spawn.mapRect(15506.5, 134.2, 288, 45); spawn.mapVertex(16758.6, 135.3, "400 -30 -350 -40 -400 30 400 30"); spawn.mapVertex(16758.6, 55.3, "423 -30 -408 -20 -400 20 400 20"); //tank spawn.mapRect(15310, -960, 600, 135); spawn.mapRect(16290, -960, 800, 135); //in tank spawn.mapRect(16524.8, -2726.8, 40, 400); spawn.mapRect(16524.8, -2130.9, 400, 40); spawn.mapRect(16010.2, -2412.2, 300, 40); spawn.mapRect(15379.2, -2055.1, 400, 40); spawn.mapVertex(17626.3, -3035, "-245 0 -220 -110 -173 -173 -110 -220 0 -250 110 -220 173 -173 220 -110 245 0"); spawn.mapRect(17226.3, -3035, 400, 40); spawn.mapVertex(17626.3, 225, "-245 0 -220 110 -173 173 -110 220 0 250 110 220 173 173 220 110 245 0"); spawn.mapRect(17226.3, 225, 400, 40); spawn.mapVertex(19626.3, -3035, "-245 0 -220 -110 -173 -173 -110 -220 0 -250 110 -220 173 -173 220 -110 245 0"); spawn.mapRect(19226.3, -3035, 400, 40); spawn.mapVertex(19626.3, 225, "-245 0 -220 110 -173 173 -110 220 0 250 110 220 173 173 220 110 245 0"); spawn.mapRect(19226.3, 225, 400, 40); spawn.mapVertex(21626.3, -3035, "-245 0 -220 -110 -173 -173 -110 -220 0 -250 110 -220 173 -173 220 -110 245 0"); spawn.mapRect(21226.3, -3035, 400, 40); spawn.mapVertex(21626.3, 225, "-245 0 -220 110 -173 173 -110 220 0 250 110 220 173 173 220 110 245 0"); spawn.mapRect(21226.3, 225, 400, 40); //add fuel tanks in the last room spawn.mapRect(21531.9, -707.8, 488, 8); //22185.5, -114.8 spawn.mapVertex(22207.8, -103, "325 -200 100 -200 325 -300"); spawn.mapRect(22056.6, -70, 225, 212); spawn.mapVertex(20723.1, -1734, "325 -200 100 -200 325 -300"); spawn.mapRect(20571.9, -1701.0, 225, 212); spawn.mapVertex(22207.8, -103, "325 -200 100 -200 325 -300"); spawn.mapRect(22056.6, -70, 225, 212); //spawn.mapVertex(x,y, "coordinates") //the parts in quotes is "x y x y x y x y x y" x and y need to be the coordinates of points that define the shape in a concave clockwise direction //second ship blocks/debris spawn.bodyRect(21525, -113, 50, 50, 9);//first button block spawn.bodyRect(18993, -2283, 50, 50, 9);//second button block spawn.bodyRect(20303, -1736, 50, 50, 9);//third button block // spawn.randomLevelBoss(17902, -1689, ["blinkBoss", "shooterBoss", "launcherBoss", "pulsarBoss", "blockBoss", "bladeBoss", "revolutionBoss", "spawnerBossCulture", "spiderBoss", "sneakBoss", "snakeSpitBoss"]) spawn.randomLevelBoss(17902, -1689, ["launcherBoss", "laserTargetingBoss", "blinkBoss", "streamBoss", "historyBoss", "grenadierBoss", "blockBoss", "revolutionBoss", "slashBoss"]); // powerUps.spawnStartingPowerUps(1475, -1175); // spawn.debris(750, -2200, 3700, 16); //16 debris per level // spawn.bodyRect(1540, -1110, 300, 25, 0.9); // spawn.randomSmallMob(1300, -70); // spawn.randomMob(2650, -975, 0.8); // spawn.randomGroup(1700, -900, 0.4); // if (simulation.difficulty > 1) spawn.randomLevelBoss(2200, -1300); // spawn.secondaryBossChance(100, -1500) powerUps.addResearchToLevel() //needs to run after mobs are spawned }, clock() { simulation.makeTextLog(`clock by Cornbread 2100`); function drawBackgroundGear(x, y, r1, r2, rot, color, speed, numTeeth = 5, toothWidth = 75, linew = 2) { var vertices = getGearVertices(x, y, r1, r2, numTeeth, simulation.cycle * speed + rot, toothWidth / 100); // draw gear ctx.beginPath(); ctx.moveTo(vertices[0].x, vertices[0].y); for (var i = 1; i < vertices.length; i++) { ctx.lineTo(vertices[i].x, vertices[i].y); } ctx.lineTo(vertices[0].x, vertices[0].y); ctx.lineWidth = 2; ctx.fillStyle = color; ctx.fill(); ctx.strokeStyle = "#3a3f20"; ctx.lineWidth = linew; ctx.stroke(); } function drawFallingBackgroundGear(x, y, r1, r2, rot, color, speed, fallSpeed, startCycle, numTeeth = 5, linew = 2) { rot *= speed; numTeeth *= 2; const gearInc = (2 * Math.PI) / numTeeth; ctx.beginPath() for (var i = 0; i <= numTeeth; i++) { var gear_r = r2; if (i % 2 == 1) gear_r = r1; ctx.arc(x, y + (simulation.cycle - startCycle) * fallSpeed, gear_r, (i * gearInc) + rot, ((i + 1) * gearInc) + rot); } ctx.fillStyle = color; ctx.fill(); ctx.strokeStyle = "#3a3f20"; ctx.lineWidth = linew; ctx.stroke(); } function getGearVertices(x, y, r1, r2, numTeeth, rot = 0, teethWidth = 0) { if (teethWidth == 0) { teethWidth = (2 * Math.PI) / (2 * numTeeth); } const gearInc = (2 * Math.PI) / numTeeth; var vertices = []; for (var i = 0; i < numTeeth; i++) { //inner vertices of gear teeth var distance = i * gearInc + rot; var vX = Math.sin(distance + teethWidth / 2) * r1; var vY = Math.cos(distance + teethWidth / 2) * r1; var point1 = { x: vX, y: vY, point: 1 }; vX = Math.sin(distance - teethWidth / 2) * r1; vY = Math.cos(distance - teethWidth / 2) * r1; var point4 = { x: vX, y: vY, point: 4 }; vX = Math.sin(distance) * r1; vY = Math.cos(distance) * r1; if (vX == 0) { vX = 0.0001 } var slope = vY / vX; var angle = Math.atan2(vY, vX); //outer vertices of gear teeth var point2 = { x: point1.x, y: point1.y, point: 2 }; point2.x += Math.cos(angle) * (r2 - r1); point2.y += Math.sin(angle) * (r2 - r1); var point3 = { x: point4.x, y: point4.y, point: 3 }; point3.x += Math.cos(angle) * (r2 - r1); point3.y += Math.sin(angle) * (r2 - r1); vertices.push(point4); vertices.push(point3); vertices.push(point2); vertices.push(point1); } for (var i = 0; i < vertices.length; i++) { vertices[i].x += x; vertices[i].y += y; } return vertices; } function getGearTeethVertices(x, y, r1, r2, numTeeth, toothIndex, teethWidth = 0) { if (teethWidth == 0) { teethWidth = (2 * Math.PI) / (2 * numTeeth); } const gearInc = (2 * Math.PI) / numTeeth; var vertices = []; for (var i = 0; i < numTeeth; i++) { //inner vertices of gear teeth var distance = i * gearInc; var vX = Math.sin(distance + teethWidth / 2) * r1; var vY = Math.cos(distance + teethWidth / 2) * r1; var point1 = { x: vX, y: vY, point: 1 }; vX = Math.sin(distance - teethWidth / 2) * r1; vY = Math.cos(distance - teethWidth / 2) * r1; var point4 = { x: vX, y: vY, point: 4 }; vX = Math.sin(distance) * r1; vY = Math.cos(distance) * r1; if (vX == 0) { vX = 0.0001 } var slope = vY / vX; var angle = Math.atan2(vY, vX); //outer vertices of gear teeth var point2 = { x: point1.x, y: point1.y, point: 2 }; point2.x += Math.cos(angle) * (r2 - r1); point2.y += Math.sin(angle) * (r2 - r1); var point3 = { x: point4.x, y: point4.y, point: 3 }; point3.x += Math.cos(angle) * (r2 - r1); point3.y += Math.sin(angle) * (r2 - r1); if (i == toothIndex) { vertices.push(point4); vertices.push(point3); vertices.push(point2); vertices.push(point1); } } for (var i = 0; i < vertices.length; i++) { vertices[i].x += x; vertices[i].y += y; } return vertices; } function mapGear(x, y, r1, r2, rot, speed, numTeeth = 5, toothWidth = 50, additionalCircleRadius = 10) { const part1 = body[body.length] = Bodies.polygon(x, y, 0, r1 + additionalCircleRadius, { collisionFilter: { category: cat.body, mask: cat.player | cat.body | cat.bullet | cat.powerUp | cat.mob | cat.mobBullet }, isNotHoldable: true, frictionAir: 0, friction: 1, frictionStatic: 1, restitution: 0 }); var parts = [part1]; for (var i = 0; i < numTeeth; i++) { var toothVertices = getGearTeethVertices(0, 0, r2 - r1, toothWidth + r2 - r1, numTeeth, i, 70); // for some reason the teeth are sideways var center = { // the center of the inner line of the gear x: toothVertices[3].x - toothVertices[0].x, y: toothVertices[3].y - toothVertices[0].y }; distanceToCenter = Math.sqrt((center.x ** 2) + (center.y ** 2)); var radiusScale = (r1 + ((r2 - r1) / 2)) / distanceToCenter; gearToothSlope = center.y / center.x; var newPart = body[body.length] = Bodies.fromVertices(x + center.x * radiusScale, y + center.y * radiusScale, toothVertices, { collisionFilter: { category: cat.body, mask: cat.player | cat.body | cat.bullet | cat.powerUp | cat.mob | cat.mobBullet }, isNotHoldable: true, frictionAir: 0.01, friction: 1, frictionStatic: 1, restitution: 0 }); parts.push(newPart); } const who = Body.create({ parts: parts }); Composite.add(engine.world, who); composite[composite.length] = who; who.collisionFilter.category = cat.body; who.collisionFilter.mask = cat.body | cat.player | cat.bullet | cat.mob | cat.mobBullet | cat.map const constraint = Constraint.create({ pointA: { x: x, y: y }, bodyB: who, stiffness: 1, damping: 1 }); Matter.Body.setDensity(who, 0.0001) Composite.add(engine.world, constraint); Matter.Body.setAngle(who, 0) Matter.Body.setAngularVelocity(who, 0); who.center = { x: x, y: y } who.rotate = function () { var rotation = simulation.cycle * speed + rot; Matter.Body.setAngle(who, rotation); } who.gearSettings = { x: x, y: y, r1: r1, r2: r2, rot: rot, speed: speed, numTeeth: numTeeth, toothWidth: toothWidth } return who; } function clockHand(x, y, width, height, speed = 15 * Math.PI / 180, angle = 0, density = 0.001) { var who1 = body[body.length] = Bodies.rectangle(x, y + height / 2, width, height, { collisionFilter: { category: cat.body, mask: cat.player | cat.body | cat.bullet | cat.powerUp | cat.mob | cat.mobBullet }, isNotHoldable: true, friction: 1, frictionStatic: 1, restitution: 0 }); const who = Body.create({ parts: [who1], handRotation: 0 }); Composite.add(engine.world, who); composite[composite.length] = who; who.collisionFilter.category = cat.body; who.collisionFilter.mask = cat.body | cat.player | cat.bullet | cat.mob | cat.mobBullet | cat.map who.position.y = y; const constraint = Constraint.create({ pointA: { x: who.position.x, y: who.position.y }, bodyB: who, stiffness: 1, damping: 1 }); Matter.Body.setDensity(who, density) Composite.add(engine.world, constraint); who.center = { x: who.position.x, y: who.position.y } who.rotate = function () { if (simulation.cycle % 60 == 0) { who.handRotation += speed; if (Math.abs(who.handRotation % (Math.PI * 2) - Math.PI) < 0.2) { // spawn random mob at exit door const pick = spawn.fullPickList[Math.floor(Math.random() * spawn.fullPickList.length)]; spawn[pick](300, 600); } if (Matter.Query.collides(player, [this]).length != 0) { var playerAngle = Math.atan((m.pos.y - y) / (m.pos.x - x)); if (m.pos.x - x < 0) playerAngle += Math.PI; const playerDistance = Math.sqrt((m.pos.x - x) ** 2 + (m.pos.y - y) ** 2); Matter.Body.setPosition(player, { x: x + Math.cos(playerAngle + speed) * playerDistance, y: y + Math.sin(playerAngle + speed) * playerDistance }) } } Matter.Body.setAngle(who, who.handRotation + angle); } return who } function pendulum(x, y, width, height, swingTime = 50, swingDistanceMultiplier = 0.5, bobSides = 0, bobRadius = 200, density = 100, angle = 0, frictionAir = 0, angularVelocity = 0) { const who1 = body[body.length] = Bodies.rectangle(x, y + height / 2, width, height, { collisionFilter: { category: cat.body, mask: cat.player | cat.body | cat.bullet | cat.powerUp | cat.mob | cat.mobBullet }, isNotHoldable: true, frictionAir: frictionAir, friction: 1, frictionStatic: 1, restitution: 0 }); const who2 = body[body.length] = Bodies.polygon(x, y + height, bobSides, bobRadius, { collisionFilter: { category: cat.body, mask: cat.player | cat.body | cat.bullet | cat.powerUp | cat.mob | cat.mobBullet }, isNotHoldable: true, frictionAir: 0.01, friction: 1, frictionStatic: 1, restitution: 0 }); const who = Body.create({ parts: [who1, who2], }); Composite.add(engine.world, who); composite[composite.length] = who; who.collisionFilter.category = cat.body; who.collisionFilter.mask = cat.body | cat.player | cat.bullet | cat.mob | cat.mobBullet | cat.map who.position.y = y; const constraint = Constraint.create({ pointA: { x: x, y: y }, bodyB: who, stiffness: 1, damping: 1 }); Matter.Body.setDensity(who, density) Composite.add(engine.world, constraint); Matter.Body.setAngle(who, angle) Matter.Body.setAngularVelocity(who, angularVelocity); who.center = { x: x, y: y + height / 2 } who.rotate = function () { var rotation = Math.sin(simulation.cycle / swingTime) * swingDistanceMultiplier; if (Matter.Query.collides(player, [this]).length != 0) { var playerAngle = Math.atan((player.position.y - y) / (player.position.x - x)) + rotation - Math.sin((simulation.cycle - 1) / swingTime) * swingDistanceMultiplier; if (player.position.x - x < 0) playerAngle += Math.PI; const playerDistance = Math.sqrt((player.position.x - x) ** 2 + (player.position.y - y) ** 2); Matter.Body.setPosition(player, { x: x + Math.cos(playerAngle) * playerDistance, y: y + Math.sin(playerAngle) * playerDistance }) } Matter.Body.setAngle(who, rotation); } return who; } function gearMob(x, y, leaveBody = true, autoFindPlayer = false, radius = Math.floor(25 + 40 * Math.random()), teethRadius = 0) { if (teethRadius == 0) { teethRadius = radius + 15 + Math.floor(Math.random() * 20); } mobs.spawn(x, y, 0, teethRadius, "transparent"); let me = mob[mob.length - 1]; me.stroke = "transparent"; me.delay = 100 + 40 * simulation.CDScale; me.accelMag = Math.PI / 10000; me.memory = 120; me.seeAtDistance2 = 2000000; // 140 Matter.Body.setDensity(me, 0.001); me.leaveBody = leaveBody; const numTeeth = Math.round(5 + Math.random() * 3); me.gearRotation = 0; me.gearSpeed = Math.round(-0.1 + Math.random() * 0.2); me.gearAccelerating = true; me.do = function () { if (autoFindPlayer) { me.locatePlayer(); } this.seePlayerByLookingAt(); this.checkStatus(); this.attraction(); if (me.gearAccelerating && (Math.random() > 0.99 || me.gearSpeed >= 0.1)) { me.gearAccelerating = false; } else if (!me.gearAccelerating && (Math.random() > 0.99 || me.gearSpeed <= -0.1)) { me.gearAccelerating = true; } if (me.gearAccelerating) { me.gearSpeed += 0.001; } else { me.gearSpeed -= 0.001; } me.gearRotation += me.gearSpeed; var newVertices = getGearVertices(me.position.x, me.position.y, radius, teethRadius, numTeeth, me.gearRotation); // draw body ctx.beginPath(); ctx.moveTo(newVertices[0].x, newVertices[0].y); for (let i = 1; i < newVertices.length; i++) { ctx.lineTo(newVertices[i].x, newVertices[i].y); } ctx.lineTo(newVertices[0].x, newVertices[0].y); ctx.fillStyle = "#7b3f00"; ctx.fill(); ctx.strokeStyle = "#000"; ctx.lineWidth = 2; ctx.stroke(); } } function customDoor(x, y, width, height) { x = x + width / 2 y = y + height / 2 const doorBlock = body[body.length] = Bodies.rectangle(x, y, width, height, { collisionFilter: { 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 }, inertia: Infinity, //prevents rotation isNotHoldable: true, friction: 1, frictionStatic: 1, restitution: 0, isClosing: false, setPos(x, y) { if (y - this.position.y <= 0 || ( // only move down if clear of stuff Matter.Query.ray([player], Matter.Vector.create(this.position.x - width / 2, this.position.y + height / 2), Matter.Vector.create(this.position.x + width / 2, this.position.y + height / 2), 5).length === 0 && Matter.Query.ray(body, Matter.Vector.create(this.position.x - width / 2, this.position.y + height / 2), Matter.Vector.create(this.position.x + width / 2, this.position.y + height / 2), 5).length <= 1 && Matter.Query.ray(mob, Matter.Vector.create(this.position.x - width / 2, this.position.y + height / 2), Matter.Vector.create(this.position.x + width / 2, this.position.y + height / 2), 5).length === 0) ) { const position = { x: x, y: y } Matter.Body.setPosition(this, position); } }, draw() { ctx.fillStyle = "#555" 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(doorBlock, true); //make static Composite.add(engine.world, doorBlock); //add to world doorBlock.classType = "body" return doorBlock } function horizontalDoor(x, y, width, height, distance, speed = 1) { x = x + width / 2 y = y + height / 2 const doorBlock = body[body.length] = Bodies.rectangle(x, y, width, height, { collisionFilter: { 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 }, inertia: Infinity, //prevents rotation isNotHoldable: true, friction: 1, frictionStatic: 1, restitution: 0, isClosing: false, openClose() { if (!m.isBodiesAsleep) { if (this.isClosing) { if (this.position.x > x) { //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 - speed, y: this.position.y } Matter.Body.setPosition(this, position) } } } else { if (this.position.x < x + distance) { //try to open const position = { x: this.position.x + speed, y: this.position.y } Matter.Body.setPosition(this, position) } } } }, isClosed() { return this.position.x < x + 1 }, draw() { ctx.fillStyle = "#555" 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(doorBlock, true); //make static Composite.add(engine.world, doorBlock); //add to world doorBlock.classType = "body" return doorBlock } function drawBelt(circle1, circle2) { // circle 1 const distance = Math.sqrt((circle2.x - circle1.x) ** 2 + (circle2.y - circle1.y) ** 2); const distanceToIntersection = (-circle1.radius * distance) / (-circle1.radius + circle2.radius); const slopeAngle = Math.atan((circle2.y - circle1.y) / (circle2.x - circle1.x)); const angleToTangent = Math.acos(-circle1.radius / distanceToIntersection); const tangentIntersection = { x: Math.cos(slopeAngle) * distanceToIntersection + circle1.x, y: Math.sin(slopeAngle) * distanceToIntersection + circle1.y } const tangentPoint = { x: Math.cos(angleToTangent + slopeAngle) * -circle1.radius + circle1.x, y: Math.sin(angleToTangent + slopeAngle) * -circle1.radius + circle1.y } const invertedTangentPoint = { x: Math.cos(-angleToTangent + slopeAngle) * -circle1.radius + circle1.x, y: Math.sin(-angleToTangent + slopeAngle) * -circle1.radius + circle1.y } // circle 2 const tangentPoint2 = { x: Math.cos(angleToTangent + slopeAngle) * -circle2.radius + circle2.x, y: Math.sin(angleToTangent + slopeAngle) * -circle2.radius + circle2.y } const invertedTangentPoint2 = { x: Math.cos(-angleToTangent + slopeAngle) * -circle2.radius + circle2.x, y: Math.sin(-angleToTangent + slopeAngle) * -circle2.radius + circle2.y } // draw ctx.beginPath(); ctx.moveTo(tangentPoint.x, tangentPoint.y); ctx.lineTo(tangentPoint2.x, tangentPoint2.y); const newAngle = Math.atan((tangentPoint2.y - circle2.y) / (tangentPoint2.x - circle2.x)); const newAngle2 = Math.atan((invertedTangentPoint2.y - circle2.y) / (invertedTangentPoint2.x - circle2.x)); ctx.arc(circle2.x, circle2.y, circle2.radius, newAngle, newAngle2 + Math.PI); ctx.lineTo(invertedTangentPoint.x, invertedTangentPoint.y); const newAngle3 = Math.atan((invertedTangentPoint.y - circle1.y) / (invertedTangentPoint.x - circle1.x)); const newAngle4 = Math.atan((tangentPoint.y - circle1.y) / (tangentPoint.x - circle1.x)); ctx.arc(circle1.x, circle1.y, circle1.radius, newAngle3 + Math.PI, newAngle4); ctx.strokeStyle = '#000'; ctx.lineWidth = 5; ctx.stroke(); } function drawDiagonalBelt(circle1, circle2) { // circle 1 const distance = Math.sqrt((circle2.x - circle1.x) ** 2 + (circle2.y - circle1.y) ** 2); const distanceToIntersection = (circle1.radius * distance) / (circle1.radius + circle2.radius); const slopeAngle = Math.atan((circle2.y - circle1.y) / (circle2.x - circle1.x)); const angleToTangent = Math.acos(circle1.radius / distanceToIntersection); const tangentIntersection = { x: Math.cos(slopeAngle) * distanceToIntersection + circle1.x, y: Math.sin(slopeAngle) * distanceToIntersection + circle1.y } const tangentPoint = { x: Math.cos(angleToTangent + slopeAngle) * circle1.radius + circle1.x, y: Math.sin(angleToTangent + slopeAngle) * circle1.radius + circle1.y } const invertedTangentPoint = { x: Math.cos(-angleToTangent + slopeAngle) * circle1.radius + circle1.x, y: Math.sin(-angleToTangent + slopeAngle) * circle1.radius + circle1.y } // circle 2 const tangentPoint2 = { x: Math.cos(angleToTangent + slopeAngle) * -circle2.radius + circle2.x, y: Math.sin(angleToTangent + slopeAngle) * -circle2.radius + circle2.y } const invertedTangentPoint2 = { x: Math.cos(-angleToTangent + slopeAngle) * -circle2.radius + circle2.x, y: Math.sin(-angleToTangent + slopeAngle) * -circle2.radius + circle2.y } // draw ctx.beginPath(); ctx.moveTo(tangentPoint.x, tangentPoint.y); ctx.lineTo(tangentPoint2.x, tangentPoint2.y); const newAngle = Math.atan((tangentPoint2.y - circle2.y) / (tangentPoint2.x - circle2.x)); const newAngle2 = Math.atan((invertedTangentPoint2.y - circle2.y) / (invertedTangentPoint2.x - circle2.x)); ctx.arc(circle2.x, circle2.y, circle2.radius, newAngle, newAngle2 + Math.PI); ctx.lineTo(invertedTangentPoint.x, invertedTangentPoint.y); const newAngle3 = Math.atan((invertedTangentPoint.y - circle1.y) / (invertedTangentPoint.x - circle1.x)); const newAngle4 = Math.atan((tangentPoint.y - circle1.y) / (tangentPoint.x - circle1.x)); ctx.arc(circle1.x, circle1.y, circle1.radius, newAngle3, newAngle4 + Math.PI, true); ctx.strokeStyle = '#000'; ctx.lineWidth = 5; ctx.stroke(); } function getIntersection(v1, v1End, domain) { const intersections = getIntersections(v1, v1End, domain); var best = { x: v1End.x, y: v1End.y, dist: Math.sqrt((v1End.x - v1.x) ** 2 + (v1End.y - v1.y) ** 2) } for (const intersection of intersections) { const dist = Math.sqrt((intersection.x - v1.x) ** 2 + (intersection.y - v1.y) ** 2); if (dist < best.dist) { best = { x: intersection.x, y: intersection.y, dist: dist }; } } return best; } function getIntersections(v1, v1End, domain) { const intersections = []; for (const obj of domain) { for (var i = 0; i < obj.vertices.length - 1; i++) { results = simulation.checkLineIntersection(v1, v1End, obj.vertices[i], obj.vertices[i + 1]); if (results.onLine1 && results.onLine2) intersections.push({ x: results.x, y: results.y }); } results = simulation.checkLineIntersection(v1, v1End, obj.vertices[obj.vertices.length - 1], obj.vertices[0]); if (results.onLine1 && results.onLine2) intersections.push({ x: results.x, y: results.y }); } return intersections; } function circleLoS(pos, radius, domain) { function allCircleLineCollisions(c, radius, domain) { var lines = []; for (const obj of domain) { //const obj = domain[0] for (var i = 0; i < obj.vertices.length - 1; i++) { lines.push(circleLineCollisions(obj.vertices[i], obj.vertices[i + 1], c, radius)); } lines.push(circleLineCollisions(obj.vertices[obj.vertices.length - 1], obj.vertices[0], c, radius)); } const collisionLines = []; for (const line of lines) { if (line.length == 2) { const distance1 = Math.sqrt((line[0].x - c.x) ** 2 + (line[0].y - c.y) ** 2) const angle1 = Math.atan2(line[0].y - c.y, line[0].x - c.x); const queryPoint1 = { x: Math.cos(angle1) * (distance1 - 1) + c.x, y: Math.sin(angle1) * (distance1 - 1) + c.y } const distance2 = Math.sqrt((line[1].x - c.x) ** 2 + (line[1].y - c.y) ** 2) const angle2 = Math.atan2(line[1].y - c.y, line[1].x - c.x); const queryPoint2 = { x: Math.cos(angle2) * (distance2 - 1) + c.x, y: Math.sin(angle2) * (distance2 - 1) + c.y } collisionLines.push(line) } } return collisionLines; } function circleLineCollisions(a, b, c, radius) { // calculate distances const angleOffset = Math.atan2(b.y - a.y, b.x - a.x); const sideB = Math.sqrt((a.x - c.x) ** 2 + (a.y - c.y) ** 2); const sideC = Math.sqrt((b.x - a.x) ** 2 + (b.y - a.y) ** 2); const sideA = Math.sqrt((c.x - b.x) ** 2 + (c.y - b.y) ** 2); // calculate the closest point on line AB to point C const angleA = Math.acos((sideB ** 2 + sideC ** 2 - sideA ** 2) / (2 * sideB * sideC)) * (a.x - c.x) / -Math.abs(a.x - c.x) const sideAD = Math.cos(angleA) * sideB; const d = { // closest point x: Math.cos(angleOffset) * sideAD + a.x, y: Math.sin(angleOffset) * sideAD + a.y } const distance = Math.sqrt((d.x - c.x) ** 2 + (d.y - c.y) ** 2); if (distance == radius) { // tangent return [d]; } else if (distance < radius) { // secant const angleOffset = Math.atan2(d.y - c.y, d.x - c.x); const innerAngle = Math.acos(distance / radius); const intersection1 = { x: Math.cos(angleOffset + innerAngle) * radius + c.x, y: Math.sin(angleOffset + innerAngle) * radius + c.y } const intersection2 = { x: Math.cos(angleOffset - innerAngle) * radius + c.x, y: Math.sin(angleOffset - innerAngle) * radius + c.y } const distance1 = { a: Math.sqrt((intersection1.x - a.x) ** 2 + (intersection1.y - a.y) ** 2), b: Math.sqrt((intersection1.x - b.x) ** 2 + (intersection1.y - b.y) ** 2) } const distance2 = { a: Math.sqrt((intersection2.x - a.x) ** 2 + (intersection2.y - a.y) ** 2), b: Math.sqrt((intersection2.x - b.x) ** 2 + (intersection2.y - b.y) ** 2) } const result = []; if (Math.abs(sideC - (distance1.a + distance1.b)) < 0.01) { result.push(intersection1); } else { if (distance1.a < distance1.b) { if (sideB <= radius) result.push(a); } else { if (sideA <= radius) result.push(b) } } if (Math.abs(sideC - (distance2.a + distance2.b)) < 0.01) { result.push(intersection2); } else { if (distance2.a <= distance2.b) { if (sideB <= radius) result.push(a); } else { if (sideA <= radius) result.push(b) } } return result; } else { // no intersection return []; } } var vertices = []; for (const obj of losDomain) { for (var i = 0; i < obj.vertices.length; i++) { const vertex = obj.vertices[i]; const angleToVertex = Math.atan2(vertex.y - pos.y, vertex.x - pos.x); const queryPoint = { x: Math.cos(angleToVertex + Math.PI) + vertex.x, y: Math.sin(angleToVertex + Math.PI) + vertex.y } if (Matter.Query.ray(domain, pos, queryPoint).length == 0) { var distance = Math.sqrt((vertex.x - pos.x) ** 2 + (vertex.y - pos.y) ** 2); var endPoint = { x: vertex.x, y: vertex.y } if (distance > radius) { const angle = Math.atan2(vertex.y - pos.y, vertex.x - pos.x); endPoint = { x: Math.cos(angle) * radius + pos.x, y: Math.sin(angle) * radius + pos.y } distance = radius } var best = getIntersection(pos, endPoint, domain); if (best.dist >= distance) { best = { x: endPoint.x, y: endPoint.y, dist: distance } } vertices.push(best) var angle = Math.atan2(vertex.y - pos.y, vertex.x - pos.x); endPoint = { x: Math.cos(angle + 0.001) * radius + pos.x, y: Math.sin(angle + 0.001) * radius + pos.y } best = getIntersection(pos, endPoint, domain); if (best.dist >= radius) { best = { x: endPoint.x, y: endPoint.y, dist: radius } } vertices.push(best) angle = Math.atan2(vertex.y - pos.y, vertex.x - pos.x); endPoint = { x: Math.cos(angle - 0.001) * radius + pos.x, y: Math.sin(angle - 0.001) * radius + pos.y } best = getIntersection(pos, endPoint, domain); if (best.dist >= radius) { best = { x: endPoint.x, y: endPoint.y, dist: radius } } vertices.push(best) } } } const outerCollisions = allCircleLineCollisions(pos, radius, domain); const circleCollisions = []; for (const line of outerCollisions) { for (const vertex of line) { const distance = Math.sqrt((vertex.x - pos.x) ** 2 + (vertex.y - pos.y) ** 2) const angle = Math.atan2(vertex.y - pos.y, vertex.x - pos.x); const queryPoint = { x: Math.cos(angle) * (distance - 1) + pos.x, y: Math.sin(angle) * (distance - 1) + pos.y } if (Math.abs(distance - radius) < 1 && Matter.Query.ray(domain, pos, queryPoint).length == 0) circleCollisions.push(vertex) } } for (var i = 0; i < circleCollisions.length; i++) { const vertex = circleCollisions[i]; var nextIndex = i + 1; if (nextIndex == circleCollisions.length) nextIndex = 0; const nextVertex = circleCollisions[nextIndex]; const angle1 = Math.atan2(vertex.y - pos.y, vertex.x - pos.x); const angle2 = Math.atan2(nextVertex.y - pos.y, nextVertex.x - pos.x); var newAngle; if (Math.abs(angle1) > Math.PI / 2 && Math.abs(angle2) > Math.PI / 2 && angle1 / Math.abs(angle1) != angle2 / Math.abs(angle2)) { // if the arc between the to points crosses over the left side (+/- pi radians) const newAngle1 = (Math.PI - Math.abs(angle1)) * (angle1 / Math.abs(angle1)); const newAngle2 = (Math.PI - Math.abs(angle2)) * (angle2 / Math.abs(angle2)); newAngle = (newAngle1 + newAngle2) / 2; var multiplier; if (newAngle == 0) { multiplier = 1; } else { multiplier = newAngle / Math.abs(newAngle); } newAngle = Math.PI * multiplier - newAngle * multiplier; test = true; } else { newAngle = (angle1 + angle2) / 2; } // shoot ray between them var endPoint = { x: Math.cos(newAngle) * radius + pos.x, y: Math.sin(newAngle) * radius + pos.y } var best = getIntersection(pos, endPoint, domain); vertices.push(vertex); if (best.dist <= radius) vertices.push({ x: best.x, y: best.y }) } vertices.sort((a, b) => Math.atan2(a.y - pos.y, a.x - pos.x) - Math.atan2(b.y - pos.y, b.x - pos.x)); return vertices; } function compareArrays(array1, array2) { for (var i = 0; i < array1.length; i++) { if (array1[i] != array2[i]) return false; } return true; } function generateIntersectMap() { // include intersections in map elements to avoid LoS issues with overlapping const intersectMap = []; for (var i = 0; i < map.length; i++) { const obj = map[i]; const newVertices = []; const restOfMap = [...map].slice(0, i).concat([...map].slice(i + 1)) for (var j = 0; j < obj.vertices.length - 1; j++) { var intersections = getIntersections(obj.vertices[j], obj.vertices[j + 1], restOfMap); newVertices.push(obj.vertices[j]); for (const vertex of intersections) { newVertices.push({ x: vertex.x, y: vertex.y }); } } intersections = getIntersections(obj.vertices[obj.vertices.length - 1], obj.vertices[0], restOfMap); newVertices.push(obj.vertices[obj.vertices.length - 1]); for (const vertex of intersections) { newVertices.push({ x: vertex.x, y: vertex.y }); } intersectMap.push({ vertices: newVertices }); } return intersectMap; } function addPartToMap(len) { // from "run" map map[len].collisionFilter.category = cat.map; map[len].collisionFilter.mask = cat.player | cat.map | cat.body | cat.bullet | cat.powerUp | cat.mob | cat.mobBullet; Matter.Body.setStatic(map[len], true); Composite.add(engine.world, map[len]); } level.setPosToSpawn(-500, -50); spawn.mapRect(level.enter.x, level.enter.y + 20, 100, 20); level.exit.x = 250; level.exit.y = 720; spawn.mapRect(level.exit.x, level.exit.y + 20, 100, 20); level.defaultZoom = 1800; simulation.zoomTransition(level.defaultZoom); document.body.style.backgroundColor = "#d8dadf"; spawn.mapRect(-925, 0, 2650, 100); spawn.mapRect(-925, -1700, 325, 1800); spawn.mapRect(-650, -325, 325, 50); spawn.mapRect(-650, -1400, 325, 50); spawn.mapRect(-1700, -1700, 1100, 200); spawn.mapRect(-1700, -4600, 300, 3100); spawn.mapRect(-1700, -4600, 1250, 200); spawn.mapRect(200, -4600, 2750, 200); spawn.mapRect(-400, -4225, 200, 50); spawn.mapRect(2800, -4600, 150, 1400); spawn.mapRect(1350, -325, 100, 50); spawn.mapRect(1400, -1500, 325, 1600); spawn.mapRect(1400, -1500, 1550, 50); spawn.mapRect(1400, -1900, 900, 50); spawn.mapRect(1400, -2900, 100, 1050); spawn.mapRect(-600, -2900, 3550, 100); spawn.mapRect(2850, -2900, 100, 700); spawn.mapRect(2850, -2200, 100, 350); map[map.length - 1].fallsOff2 = true; // this piece will fall off in the middle of cutscene spawn.mapRect(2300, -1900, 500, 50); map[map.length - 1].fallsOff = true; // this piece wall fall off at the start of cutscene spawn.mapRect(2800, -1900, 200, 50); spawn.mapRect(2900, -1900, 50, 450); powerUps.directSpawn(2700, -1675, "tech"); spawn.mapRect(2800, -3300, 825, 100); spawn.mapRect(3525, -3300, 100, 3000); spawn.mapRect(3400, -2850, 225, 50); spawn.mapRect(2875, -2525, 175, 25); spawn.mapRect(3325, -2675, 150, 25); spawn.mapRect(3400, -2850, 75, 200); spawn.mapRect(3150, -2225, 100, 25); spawn.mapRect(-2300, 750, 5450, 100); pendulum1 = pendulum(400, -2500, 75, 1700, 50, 0.3, 0, 300); const gear1 = mapGear(-1200, -2000, 100, 200, 0, -0.05, 5, 75); const gear2 = mapGear(-700, -2500, 150, 270, -0.5, 0.05, 5, 50); const gear3 = mapGear(-3500, -1000, 1100, 1500, -0.5, 0.005, 10, 150, 40); const piston1 = customDoor(1650, -1850, 100, 350); // x, y, width, height, distance, speed = 1 const piston2 = customDoor(1950, -1850, 100, 350); const piston3 = horizontalDoor(-2000, -4200, 300, 100, 300, 20); const piston4 = horizontalDoor(-2000, -3800, 300, 100, 300, 20); const piston5 = horizontalDoor(-2000, -3400, 300, 100, 300, 20); const piston6 = horizontalDoor(-2000, -3000, 300, 100, 300, 20); const piston7 = horizontalDoor(-2000, -2600, 300, 100, 300, 20); const hand1 = clockHand(400, -3700, 75, 600); const elevator1 = level.elevator(3200, 0, 150, 50, -1750, 0.0025, { up: 0.05, down: 0.2 }); spawn.debris(-300, 0, 1300, 6); spawn.debris(0, -2900, 2500, 8); spawn.randomSmallMob(-500, -500, 1); spawn.randomMob(190, -1300, 1); spawn.randomMob(200, -320, 0.3); spawn.randomMob(1000, -1100, 1); spawn.randomMob(-160, -2050, 1); spawn.randomMob(-1100, -2900, 0.5); spawn.randomLevelBoss(1900, -3800, spawn.randomBossList.splice(0, spawn.randomBossList.indexOf("shieldingBoss"), 1).concat(spawn.randomBossList.splice(spawn.randomBossList.indexOf("shieldingBoss") + 1))); // shieldingBoss lags out the lighting system for some reason spawn.randomMob(2500, -3500, 0.3); spawn.randomMob(1300, -4100, 0.5); spawn.randomMob(3400, -2450, 1); spawn.randomMob(2850, -2050, 0.4); spawn.randomGroup(-150, -2400, 0.5); spawn.randomMob(-1250, -5150, 1); spawn.randomMob(-2900, -4000, 0.4); spawn.randomMob(-1350, -950, 1); spawn.randomMob(2700, -850, 0.4); spawn.randomMob(2500, -50, 0.4); powerUps.addResearchToLevel() // needs to run after mobs are spawned var dealtPiston1Damage = false; var dealtPiston2Damage = false; var dealtPiston1MobDamage = false; var dealtPiston2MobDamage = false; var lastPistonDirection = false; var pistonsLocked = false; var finishedGearFight = false; var roofReadyToFall = false; var roofFallCycle = 0; var drawGear = false; var gearCycle = simulation.cycle; var gearPositions = []; var pistonUnlockCycle = 0; for (var i = 0; i < 15; i++) { gearPositions.push({ x: 2400 + Math.random() * 200, y: -3300 - Math.random() * 3000 }); } var gearSizes = []; for (var i = 0; i < 15; i++) { const r1 = 30 + Math.random() * 50; gearSizes.push({ r1: r1, r2: r1 + 15 + Math.random() * 30 }) } 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, piston1, player, circleHead]); var oldMap = [...map]; var oldMob = [...mob]; var spawnGearMobCycle = 0; var gearsSpawned = 0; var lastSmallGearRot = 0; var smallGearRot = 0; var smallGearPosRot = 0; var bigGearRot = 0; var finalGearRot; var lastFinalGearRot; var startCycle = simulation.cycle; // used to offset simulation.cycle to avoid the swing starting halfway through at the start of the level and messing up syncronization 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, piston1, player, circleHead]); } oldMap = [...map]; oldMob = [...mob]; ctx.fillStyle = "#b0b0b2"; ctx.fillRect(-600, -1700, 2000, 1700); ctx.fillRect(1350, -1851, 1550, 350); ctx.fillRect(-1400, -2950, 4250, 1450); ctx.fillRect(-1400, -4400, 4350, 1500); ctx.fillRect(-450, -4600, 650, 250); ctx.fillRect(2750, -3200, 200, 1300); ctx.fillRect(2750, -3200, 200, 1300); ctx.fillStyle = "#000"; ctx.fillRect(350, -2800, 100, 25); // 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, 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++) { var currentDistance = Math.sqrt((vertices[i - 1].x - lightPos.x) ** 2 + (vertices[i - 1].y - lightPos.y) ** 2); var newDistance = Math.sqrt((vertices[i].x - lightPos.x) ** 2 + (vertices[i].y - lightPos.y) ** 2); if (Math.abs(currentDistance - lightRadius) < 1 && Math.abs(newDistance - lightRadius) < 1) { const currentAngle = Math.atan2(vertices[i - 1].y - lightPos.y, vertices[i - 1].x - lightPos.x); const newAngle = Math.atan2(vertices[i].y - lightPos.y, vertices[i].x - lightPos.x); ctx.arc(lightPos.x, lightPos.y, lightRadius, currentAngle, newAngle); } else { ctx.lineTo(vertices[i].x, vertices[i].y) } } newDistance = Math.sqrt((vertices[0].x - lightPos.x) ** 2 + (vertices[0].y - lightPos.y) ** 2); currentDistance = Math.sqrt((vertices[vertices.length - 1].x - lightPos.x) ** 2 + (vertices[vertices.length - 1].y - lightPos.y) ** 2); if (Math.abs(currentDistance - lightRadius) < 1 && Math.abs(newDistance - lightRadius) < 1) { const currentAngle = Math.atan2(vertices[vertices.length - 1].y - lightPos.y, vertices[vertices.length - 1].x - lightPos.x); const newAngle = Math.atan2(vertices[0].y - lightPos.y, vertices[0].x - lightPos.x); ctx.arc(lightPos.x, lightPos.y, lightRadius, currentAngle, newAngle); } else { ctx.lineTo(vertices[0].x, vertices[0].y) } ctx.fillStyle = "rgba(216, 218, 223, 0.5)"; ctx.fill(); } ctx.beginPath(); ctx.moveTo(425, -2775); ctx.arc(400, -2775, 25, 0, Math.PI); ctx.fillStyle = "#c6aa12"; ctx.fill(); ctx.strokeStyle = "#000000"; ctx.lineWidth = 1; ctx.stroke(); pendulum1.rotate(); gear1.rotate(); gear2.rotate(); gear3.rotate(); hand1.rotate(); drawBackgroundGear(-1200, -2300, 75, 150, 0.3, "#ccc", -0.05); drawBackgroundGear(-1010, -2380, 30, 100, -0.1, "#ccc", 0.05); // pendulum gears if (!m.isBodiesAsleep) smallGearPosRot += Math.sin((simulation.cycle - startCycle) / 50) * 0.3 - Math.sin((simulation.cycle - startCycle - 1) / 50) * 0.3; if (smallGearPosRot > 0.1) smallGearPosRot = 0.1; if (smallGearPosRot < -0.1) smallGearPosRot = -0.1; var circ = 2 * Math.PI * 150; var arcLength = ((smallGearPosRot - Math.sin((simulation.cycle - startCycle) / 50) * 0.2) / (Math.PI * 2)) * circ; lastSmallGearRot = smallGearRot; smallGearRot = arcLength / (2 * Math.PI * 50) * Math.PI * -2 + 0.6; if (Math.abs(smallGearPosRot) == 0.1) { bigGearRot += Math.abs((smallGearRot - lastSmallGearRot) * (50 / 75)); } drawBackgroundGear(740, -2625, 270, 330, bigGearRot, "#d2d3d4", 0, 15, 20); // the big one in the background drawBackgroundGear(400, -2500, 100, 150, Math.sin((simulation.cycle - startCycle) / 50) * -0.3, "#ccc", 0, 8, 20); // attached to pendulum drawBackgroundGear(400 + Math.cos(smallGearPosRot) * 200, -2500 + Math.sin(smallGearPosRot) * 200, 50, 75, smallGearRot, "#ccc", 0, 7, 20); ctx.beginPath(); ctx.arc(400 + Math.cos(smallGearPosRot) * 200, -2500 + Math.sin(smallGearPosRot) * 200, 10, 0, 2 * Math.PI); ctx.strokeStyle = "#444"; ctx.lineWidth = 10; ctx.stroke(); ctx.beginPath(); ctx.arc(400, -2500, 200, -0.1, 0.1); ctx.strokeStyle = "#444"; ctx.lineWidth = 10; ctx.stroke(); drawBackgroundGear(740, -2625, 75, 110, bigGearRot, "#ccc", 0, 8, 20); ctx.beginPath(); ctx.arc(740, -2625, 40, 0, 2 * Math.PI); ctx.fillStyle = "#bbb"; ctx.fill(); ctx.strokeStyle = "#000"; ctx.lineWidth = 2; ctx.stroke(); drawBackgroundGear(740, -2375, 75, 110, bigGearRot * -1, "#ccc", 0, 8, 20); ctx.beginPath(); ctx.arc(740, -2375, 40, 0, 2 * Math.PI); ctx.fillStyle = "#bbb"; ctx.fill(); ctx.strokeStyle = "#000"; ctx.lineWidth = 2; ctx.stroke(); drawDiagonalBelt({ x: 740, y: -2625, radius: 40 }, { x: 740, y: -2375, radius: 40 }) if (finalGearRot != null) lastFinalGearRot = finalGearRot; finalGearRot = Math.round((-bigGearRot * 294.72 / 25) * 100) / 100 + Math.PI / 2; drawBackgroundGear(1080, -2650, 10, 20, finalGearRot, "#ccc", 0, 5, 50); ctx.beginPath(); ctx.arc(1080, -2650, 10, 0, 2 * Math.PI); ctx.fillStyle = "#bbb"; ctx.fill(); ctx.strokeStyle = "#000"; ctx.lineWidth = 2; ctx.stroke(); drawBackgroundGear(1650, -2550, 300, 360, finalGearRot, "#ccc", 0, 6, 50); ctx.beginPath(); ctx.arc(1650, -2550, 100, 0, 2 * Math.PI); ctx.fillStyle = "#bbb"; ctx.fill(); ctx.strokeStyle = "#000"; ctx.lineWidth = 2; ctx.stroke(); drawBelt({ x: 1080, y: -2650, radius: 10 }, { x: 1650, y: -2550, radius: 100 }); ctx.beginPath(); ctx.arc(Math.cos(-finalGearRot) * 294 + 1650, Math.sin(-finalGearRot) * 294 - 2550, 25, 0, 2 * Math.PI); ctx.fillStyle = "#000"; ctx.fill(); drawBackgroundGear(2300, -2550, 300, 360, -finalGearRot + 0.5, "#ccc", 0, 6, 50); ctx.beginPath(); ctx.arc(Math.cos(finalGearRot) * 294 + 2300, Math.sin(finalGearRot) * 294 - 2550, 25, 0, 2 * Math.PI); ctx.fillStyle = "#000"; ctx.fill(); ctx.beginPath(); ctx.arc(1630, -2215, 15, 0, 2 * Math.PI); ctx.fillStyle = "#000"; ctx.fill(); ctx.beginPath(); ctx.arc(1670, -2215, 15, 0, 2 * Math.PI); ctx.fillStyle = "#000"; ctx.fill(); ctx.beginPath(); ctx.arc(1940, -2250, 15, 0, 2 * Math.PI); ctx.fillStyle = "#000"; ctx.fill(); ctx.beginPath(); ctx.arc(2300, -2215, 15, 0, 2 * Math.PI); ctx.fillStyle = "#000"; ctx.fill(); if (!finishedGearFight && !pistonsLocked && m.pos.x > 2100 && m.pos.x < 2900 && m.pos.y > -1850 && m.pos.y < -1500) { pistonsLocked = true; roofFallCycle = simulation.cycle + 250; roofReadyToFall = true; } if (roofReadyToFall && simulation.cycle >= roofFallCycle) { // section of roof is deleted for (var i = 0; i < map.length; i++) { if (map[i].fallsOff) { Matter.Composite.remove(engine.world, map[i]); map.splice(i, 1); } } // replace it with a block spawn.bodyRect(2310, -1900, 480, 50); roofReadyToFall = false; drawGear = true; gearCycle = simulation.cycle + 100; } //draw some background gears falling when roof falls if (drawGear && simulation.cycle >= gearCycle) { for (var i = 0; i < 15; i++) { drawFallingBackgroundGear(gearPositions[i].x, gearPositions[i].y, gearSizes[i].r1, gearSizes[i].r2, simulation.cycle, "#ccc", 0.1, 25, gearCycle); } if (spawnGearMobCycle == 0) { spawnGearMobCycle = simulation.cycle + 100; } } if (spawnGearMobCycle > 0 && simulation.cycle >= spawnGearMobCycle) { if (gearsSpawned < 40) { gearMob(1600 + Math.random() * 1000, -2300 - Math.random() * 300, false, true); gearsSpawned++; spawnGearMobCycle = simulation.cycle + 25 - (simulation.difficulty - simulation.difficultyMode) / 2; } else if (pistonUnlockCycle == 0) { pistonUnlockCycle = simulation.cycle + 50; } } if (!finishedGearFight && pistonUnlockCycle > 0 && simulation.cycle > pistonUnlockCycle) { pistonsLocked = false; finishedGearFight = true; for (var i = 0; i < map.length; i++) { if (map[i].fallsOff2) { Matter.Composite.remove(engine.world, map[i]); map.splice(i, 1); } } spawn.bodyRect(2850, -2180, 100, 280); Matter.Body.setAngularVelocity(body[body.length - 1], 0.025); } if (Math.sin((simulation.cycle + 15) / 25) < 0 && !lastPistonDirection) { // 15 cycles early to line up better with pendulum piston3.isClosing = true; piston4.isClosing = false; piston5.isClosing = true; piston6.isClosing = false; piston7.isClosing = true; } else if (Math.sin((simulation.cycle + 15) / 25) > 0 && lastPistonDirection) { piston3.isClosing = false; piston4.isClosing = true; piston5.isClosing = false; piston6.isClosing = true; piston7.isClosing = false; } if (Math.sin(-finalGearRot) - Math.sin(-lastFinalGearRot) < -0.01) { dealtPiston1Damage = false; dealtPiston1MobDamage = false; } if (Math.sin(-finalGearRot) - Math.sin(-lastFinalGearRot) > 0.01) { dealtPiston2Damage = false; dealtPiston2MobDamage = false; } piston3.openClose(); piston4.openClose(); piston5.openClose(); piston6.openClose(); piston7.openClose(); if (!pistonsLocked) { piston1.isLocked = false; } if (!pistonsLocked || ((Math.sin(-finalGearRot) - Math.sin(-lastFinalGearRot) != 0 || piston1.position.y < -1850) && !piston1.isLocked)) { piston1.setPos(1650, -1850 + Math.sin(-finalGearRot) * 175); } else { piston1.isLocked = true; } piston2.setPos(1950, -1850 + Math.sin(finalGearRot) * 175); ctx.beginPath(); ctx.moveTo(Math.cos(-finalGearRot) * 294 + 1650, Math.sin(-finalGearRot) * 294 - 2550); ctx.lineTo(1650, -2230); ctx.lineTo(piston1.position.x, piston1.position.y - 175) ctx.strokeStyle = "#777"; ctx.lineWidth = 10; ctx.stroke(); var circle1; var circle2; if (Math.cos(finalGearRot) * 294 > 0) { circle1 = { x: Math.cos(finalGearRot) * 294 + 2300, y: Math.sin(finalGearRot) * 294 - 2550, radius: -25 } circle2 = { x: 2300, y: -2215, radius: -15 } } else { circle1 = { x: Math.cos(finalGearRot) * 294 + 2300, y: Math.sin(finalGearRot) * 294 - 2550, radius: 25 } circle2 = { x: 2300, y: -2215, radius: 15 } } // same method used in drawBelt() var distance = Math.sqrt((circle2.x - circle1.x) ** 2 + (circle2.y - circle1.y) ** 2); var distanceToIntersection = (-circle1.radius * distance) / (-circle1.radius + circle2.radius); var slopeAngle = Math.atan((circle2.y - circle1.y) / (circle2.x - circle1.x)); var angleToTangent = Math.acos(-circle1.radius / distanceToIntersection); const tangentPoint = { x: Math.cos(angleToTangent + slopeAngle) * -circle2.radius + circle2.x, y: Math.sin(angleToTangent + slopeAngle) * -circle2.radius + circle2.y } // same method used in drawDiagonalBelt() const circle3 = { x: 1940, y: -2250, radius: 15 } distance = Math.sqrt((circle2.x - circle3.x) ** 2 + (circle2.y - circle3.y) ** 2); distanceToIntersection = (circle3.radius * distance) / (circle3.radius + circle2.radius); slopeAngle = Math.atan((circle2.y - circle3.y) / (circle2.x - circle3.x)); angleToTangent = Math.acos(circle3.radius / distanceToIntersection); const tangentPoint2 = { x: Math.cos(angleToTangent + slopeAngle) * circle3.radius + circle3.x, y: Math.sin(angleToTangent + slopeAngle) * circle3.radius + circle3.y } const invertedTangentPoint2 = { x: Math.cos(-angleToTangent + slopeAngle) * circle3.radius + circle3.x, y: Math.sin(-angleToTangent + slopeAngle) * circle3.radius + circle3.y } const tangentPoint3 = { x: Math.cos(angleToTangent + slopeAngle) * -circle2.radius + circle2.x, y: Math.sin(angleToTangent + slopeAngle) * -circle2.radius + circle2.y } const invertedTangentPoint3 = { x: Math.cos(-angleToTangent + slopeAngle) * -circle2.radius + circle2.x, y: Math.sin(-angleToTangent + slopeAngle) * -circle2.radius + circle2.y } distance = Math.sqrt((piston2.position.y - 175 - circle3.y) ** 2 + (piston2.position.x - 50 - circle3.x) ** 2); slopeAngle = Math.atan((piston2.position.y - 175 - circle3.y) / (piston2.position.x - 50 - circle3.x)); angleToTangent = Math.acos(circle3.radius / distance); const tangentPoint4 = { x: Math.cos(angleToTangent) * distance + circle3.x, y: Math.sin(angleToTangent) * distance + circle3.y } // draw ctx.beginPath(); ctx.moveTo(circle1.x, circle1.y); ctx.lineTo(tangentPoint.x, tangentPoint.y); const newAngle = Math.atan((tangentPoint.y - circle2.y) / (tangentPoint.x - circle2.x)); const newAngle2 = Math.atan((tangentPoint3.y - circle2.y) / (tangentPoint3.x - circle2.x)); ctx.arc(circle2.x, circle2.y, Math.abs(circle2.radius), newAngle, -newAngle2); ctx.lineTo(invertedTangentPoint2.x, invertedTangentPoint2.y); const newAngle3 = Math.atan((invertedTangentPoint2.y - circle3.y) / (invertedTangentPoint2.x - circle3.x)); ctx.arc(circle3.x, circle3.y, circle3.radius, newAngle3, Math.PI / 2 + angleToTangent, true); ctx.lineTo(tangentPoint4.x, tangentPoint4.y); ctx.strokeStyle = '#777'; ctx.lineWidth = 10; ctx.stroke(); lastPistonDirection = Math.sin((simulation.cycle + 15) / 25) < 0; if (Matter.Query.ray([player], Matter.Vector.create(piston1.position.x - 50, piston1.position.y + 175), Matter.Vector.create(piston1.position.x + 50, piston1.position.y + 175), 5).length > 0 && !dealtPiston1Damage && Math.sin(-finalGearRot) - Math.sin(-lastFinalGearRot) > 0.01) { m.damage(0.1); dealtPiston1Damage = true; } var piston1MobCollisions = Matter.Query.ray(mob, Matter.Vector.create(piston1.position.x - 50, piston1.position.y + 175), Matter.Vector.create(piston1.position.x + 50, piston1.position.y + 175), 5); if (piston1MobCollisions.length > 0 && !dealtPiston1MobDamage && Math.sin(-finalGearRot) - Math.sin(-lastFinalGearRot) > 0.01) { for (var mobHit of piston1MobCollisions) { mobHit.body.damage(1); } dealtPiston1MobDamage = true; } if (Matter.Query.ray([player], Matter.Vector.create(piston2.position.x - 50, piston2.position.y + 175), Matter.Vector.create(piston2.position.x + 50, piston2.position.y + 175), 5).length > 0 && !dealtPiston2Damage && Math.sin(-finalGearRot) - Math.sin(-lastFinalGearRot) < -0.01) { m.damage(0.1); dealtPiston2Damage = true; } var piston2MobCollisions = Matter.Query.ray(mob, Matter.Vector.create(piston2.position.x - 50, piston2.position.y + 175), Matter.Vector.create(piston2.position.x + 50, piston2.position.y + 175), 5); if (piston2MobCollisions.length > 0 && !dealtPiston2MobDamage && Math.sin(-finalGearRot) - Math.sin(-lastFinalGearRot) > 0.01) { for (var mobHit of piston2MobCollisions) { mobHit.body.damage(1); } dealtPiston2MobDamage = true; } // clock ctx.beginPath(); ctx.arc(400, -3700, 600, 0, 2 * Math.PI); ctx.fillStyle = "#e9e9e9"; ctx.fill(); ctx.strokeStyle = "#3a3f20"; ctx.lineWidth = 2; ctx.stroke(); ctx.lineCap = "butt"; ctx.beginPath(); ctx.moveTo(350, -4275); ctx.lineTo(390, -4150); ctx.strokeStyle = "#000"; ctx.lineWidth = 20; ctx.stroke(); ctx.beginPath(); ctx.moveTo(390, -4275); ctx.lineTo(350, -4150); ctx.stroke(); ctx.beginPath(); ctx.moveTo(420, -4275); ctx.lineTo(420, -4150); ctx.stroke(); ctx.beginPath(); ctx.moveTo(450, -4275); ctx.lineTo(450, -4150); ctx.stroke(); var numberOffset = { x: 400 + Math.cos(-Math.PI / 3) * 510, y: -3700 + Math.sin(-Math.PI / 3) * 510 } ctx.beginPath(); ctx.moveTo(numberOffset.x, numberOffset.y - 62); ctx.lineTo(numberOffset.x, numberOffset.y + 63); ctx.stroke(); var numberOffset = { x: 400 + Math.cos(-Math.PI / 6) * 510, y: -3700 + Math.sin(-Math.PI / 6) * 510 } ctx.beginPath(); ctx.moveTo(numberOffset.x - 20, numberOffset.y - 62); ctx.lineTo(numberOffset.x - 20, numberOffset.y + 63); ctx.stroke(); ctx.beginPath(); ctx.moveTo(numberOffset.x + 20, numberOffset.y - 62); ctx.lineTo(numberOffset.x + 20, numberOffset.y + 63); ctx.stroke(); ctx.beginPath(); ctx.moveTo(870, -3762); ctx.lineTo(870, -3637); ctx.stroke(); ctx.beginPath(); ctx.moveTo(910, -3762); ctx.lineTo(910, -3637); ctx.stroke(); ctx.beginPath(); ctx.moveTo(950, -3762); ctx.lineTo(950, -3637); ctx.stroke(); var numberOffset = { x: 400 + Math.cos(Math.PI / 6) * 535, y: -3700 + Math.sin(Math.PI / 6) * 535 } ctx.beginPath(); ctx.moveTo(numberOffset.x - 50, numberOffset.y - 62); ctx.lineTo(numberOffset.x - 50, numberOffset.y + 63); ctx.stroke(); ctx.beginPath(); ctx.moveTo(numberOffset.x - 20, numberOffset.y - 62); ctx.lineTo(numberOffset.x, numberOffset.y + 53); ctx.lineTo(numberOffset.x + 20, numberOffset.y - 62); ctx.stroke(); var numberOffset = { x: 400 + Math.cos(Math.PI / 3) * 515, y: -3700 + Math.sin(Math.PI / 3) * 515 } ctx.beginPath(); ctx.moveTo(numberOffset.x - 20, numberOffset.y - 62); ctx.lineTo(numberOffset.x, numberOffset.y + 53); ctx.lineTo(numberOffset.x + 20, numberOffset.y - 62); ctx.stroke(); var numberOffset = { x: 400 + Math.cos(Math.PI / 2) * 515, y: -3700 + Math.sin(Math.PI / 2) * 515 } ctx.beginPath(); ctx.moveTo(numberOffset.x - 35, numberOffset.y - 62); ctx.lineTo(numberOffset.x - 15, numberOffset.y + 53); ctx.lineTo(numberOffset.x + 5, numberOffset.y - 62); ctx.stroke(); ctx.beginPath(); ctx.moveTo(numberOffset.x + 35, numberOffset.y - 62); ctx.lineTo(numberOffset.x + 35, numberOffset.y + 63); ctx.stroke(); var numberOffset = { x: 400 + Math.cos(Math.PI * 2 / 3) * 500, y: -3700 + Math.sin(Math.PI * 2 / 3) * 500 } ctx.beginPath(); ctx.moveTo(numberOffset.x - 65, numberOffset.y - 62); ctx.lineTo(numberOffset.x - 45, numberOffset.y + 53); ctx.lineTo(numberOffset.x - 25, numberOffset.y - 62); ctx.stroke(); ctx.beginPath(); ctx.moveTo(numberOffset.x + 5, numberOffset.y - 62); ctx.lineTo(numberOffset.x + 5, numberOffset.y + 63); ctx.stroke(); ctx.beginPath(); ctx.moveTo(numberOffset.x + 35, numberOffset.y - 62); ctx.lineTo(numberOffset.x + 35, numberOffset.y + 63); ctx.stroke(); var numberOffset = { x: 400 + Math.cos(Math.PI * 5 / 6) * 500, y: -3700 + Math.sin(Math.PI * 5 / 6) * 500 } ctx.beginPath(); ctx.moveTo(numberOffset.x - 65, numberOffset.y - 62); ctx.lineTo(numberOffset.x - 45, numberOffset.y + 53); ctx.lineTo(numberOffset.x - 25, numberOffset.y - 62); ctx.stroke(); ctx.beginPath(); ctx.moveTo(numberOffset.x + 5, numberOffset.y - 62); ctx.lineTo(numberOffset.x + 5, numberOffset.y + 63); ctx.stroke(); ctx.beginPath(); ctx.moveTo(numberOffset.x + 35, numberOffset.y - 62); ctx.lineTo(numberOffset.x + 35, numberOffset.y + 63); ctx.stroke(); ctx.beginPath(); ctx.moveTo(numberOffset.x + 65, numberOffset.y - 62); ctx.lineTo(numberOffset.x + 65, numberOffset.y + 63); ctx.stroke(); var numberOffset = { x: 400 + Math.cos(Math.PI) * 500, y: -3700 + Math.sin(Math.PI) * 500 } ctx.beginPath(); ctx.moveTo(numberOffset.x - 5, numberOffset.y - 62); ctx.lineTo(numberOffset.x + 35, numberOffset.y + 63); ctx.stroke(); ctx.beginPath(); ctx.moveTo(numberOffset.x - 5, numberOffset.y + 63); ctx.lineTo(numberOffset.x + 35, numberOffset.y - 62); ctx.stroke(); ctx.beginPath(); ctx.moveTo(numberOffset.x - 35, numberOffset.y - 62); ctx.lineTo(numberOffset.x - 35, numberOffset.y + 63); ctx.stroke(); var numberOffset = { x: 400 + Math.cos(-Math.PI * 5 / 6) * 500, y: -3700 + Math.sin(-Math.PI * 5 / 6) * 500 } ctx.beginPath(); ctx.moveTo(numberOffset.x - 25, numberOffset.y - 62); ctx.lineTo(numberOffset.x + 25, numberOffset.y + 63); ctx.stroke(); ctx.beginPath(); ctx.moveTo(numberOffset.x - 25, numberOffset.y + 63); ctx.lineTo(numberOffset.x + 25, numberOffset.y - 62); ctx.stroke(); var numberOffset = { x: 400 + Math.cos(-Math.PI * 2 / 3) * 500, y: -3700 + Math.sin(-Math.PI * 2 / 3) * 500 } ctx.beginPath(); ctx.moveTo(numberOffset.x - 10, numberOffset.y - 62); ctx.lineTo(numberOffset.x + 40, numberOffset.y + 63); ctx.stroke(); ctx.beginPath(); ctx.moveTo(numberOffset.x - 10, numberOffset.y + 63); ctx.lineTo(numberOffset.x + 40, numberOffset.y - 62); ctx.stroke(); ctx.beginPath(); ctx.moveTo(numberOffset.x - 40, numberOffset.y + 63); ctx.lineTo(numberOffset.x - 40, numberOffset.y - 62); ctx.stroke(); ctx.lineCap = "round"; level.exit.drawAndCheck(); level.enter.draw(); } var lastBlock = Math.sin(simulation.cycle / 50) < 0; level.customTopLayer = () => { elevator1.move(); ctx.beginPath(); ctx.moveTo(pendulum1.parts[2].vertices[0].x, pendulum1.parts[2].vertices[0].y); for (var i = 0; i < pendulum1.parts[2].vertices.length; i++) { ctx.lineTo(pendulum1.parts[2].vertices[i].x, pendulum1.parts[2].vertices[i].y); } ctx.lineTo(pendulum1.parts[2].vertices[0].x, pendulum1.parts[2].vertices[0].y); ctx.fillStyle = "#999"; ctx.fill(); ctx.lineWidth = 2 ctx.strokeStyle = color.blockS; ctx.stroke(); ctx.beginPath(); ctx.moveTo(gear3.parts[1].vertices[0].x, gear3.parts[1].vertices[0].y); for (var i = 0; i < gear3.parts[1].vertices.length; i++) { ctx.lineTo(gear3.parts[1].vertices[i].x, gear3.parts[1].vertices[i].y); } ctx.lineTo(gear3.parts[1].vertices[0].x, gear3.parts[1].vertices[0].y); ctx.fillStyle = "#999"; ctx.fill(); ctx.lineWidth = 2 ctx.strokeStyle = color.blockS; ctx.stroke(); ctx.fillStyle = "#444"; ctx.fillRect(3275, -1750, 1, 1750); ctx.fillStyle = "#888"; if (Math.sin(simulation.cycle / 50) < 0 && !lastBlock) { // remove old elements for (var i = 0; i < map.length; i++) { if (map[i].isRemove) { Matter.Composite.remove(engine.world, map[i]); map.splice(i, 1); } } // add new element spawn.mapRect(-200, -600, 275, 50); addPartToMap(map.length - 1); map[map.length - 1].isRemove = true; } else if (Math.sin(simulation.cycle / 50) * 0.3 >= 0 && lastBlock) { for (var i = 0; i < map.length; i++) { if (map[i].isRemove) { Matter.Composite.remove(engine.world, map[i]); map.splice(i, 1); } } spawn.mapRect(825, -600, 275, 50); addPartToMap(map.length - 1); map[map.length - 1].isRemove = true; } simulation.draw.setPaths(); lastBlock = Math.sin(simulation.cycle / 50) * 0.3 < 0; } }, buttonbutton() { simulation.makeTextLog(`buttonbutton by ||Destabilized E||`); const mover = level.mover(1425, -1949, 600, 25); //x,y,width.height,VxGoal,force let portal portal = level.portal({ x: -146, y: 131 }, 2 * Math.PI, { x: 1805, y: -2295 }, 90) const button = level.button(-456, -1320) spawn.bodyRect(-400, -1475, 75, 75); const button2 = level.button(1781, -61) spawn.bodyRect(1781, (-61) - 100, 75, 75); const boost1 = level.boost(1366, -1942, 1300) button.isUp = true button2.isUp = true const train = level.transport(-250, 1151, 400, 50, 8 + simulation.difficultyMode) level.custom = () => { if (train.position.x < -244) { train.changeDirection(true) //go right } else if (train.position.x > 1700) { train.changeDirection(false) //go left } if (button.isUp && button2.isUp) train.move(); mover.push(); ctx.fillStyle = "rgba(0,255,255,0.1)"; ctx.fillRect(6400, -550, 300, 350); level.exit.drawAndCheck(); level.enter.draw(); }; level.customTopLayer = () => { button.query(); button.draw(); button2.query(); button2.draw(); boost1.query(); train.draw() portal[2].query() portal[3].query() portal[0].draw(); portal[1].draw(); mover.draw(); ctx.fillStyle = "rgba(0,0,0,0.1)" ctx.fillRect(-150, -650, 900, 250) }; level.setPosToSpawn(0, -450); //normal spawn spawn.mapRect(level.enter.x, level.enter.y + 20, 100, 20); level.exit.x = -525; level.exit.y = 1128; level.defaultZoom = 1500 simulation.zoomTransition(level.defaultZoom) document.body.style.backgroundColor = "#ddd"; spawn.mapRect(-725, -1325, 575, 1900); spawn.mapRect(1425, -1925, 600, 1550); spawn.mapRect(1450, -50, 500, 425); spawn.mapRect(1950, 75, 325, 300); spawn.mapRect(2275, 200, 200, 175); spawn.mapRect(-150, -400, 900, 250); spawn.mapRect(-150, 300, 900, 275); spawn.mapRect(1700, 900, 450, 275); spawn.mapRect(1800, 1600, 450, 250); spawn.mapRect(1675, 1675, 275, 175); spawn.mapRect(1575, 1675, 275, 175); spawn.mapRect(-550, 1150, 150, 100); spawn.bodyRect(-1475, -225, 50, 50); spawn.bodyRect(2450, 1525, 925, 850); spawn.mapRect(2275, 1400, 300, 150); spawn.mapRect(2125, 1025, 125, 150); spawn.mapRect(2250, 1175, 175, 75); spawn.mapRect(2150, 1175, 175, 75); spawn.mapRect(1725, 1150, 475, 100); spawn.mapRect(2225, 675, 650, 50); spawn.bodyRect(2400, 500, 150, 175); spawn.nodeGroup(326, 85, "grenadier", 6) spawn.mapRect(-225, -1325, 625, 225); spawn.randomMob(151, -1500) spawn.randomMob(-88, -1829) spawn.randomMob(2339, 896) spawn.randomMob(1907, 1381) spawn.randomMob(2398, 1301) spawn.randomMob(1839, 811) spawn.randomMob(2282, 1103) spawn.randomMob(8, 124) spawn.randomMob(629, 111) spawn.randomMob(43, 831) spawn.randomMob(168, 1002) spawn.randomMob(2956, 1006) spawn.randomMob(2713, 535) spawn.randomMob(2396, 117) spawn.randomMob(1498, -121) spawn.nodeGroup(2030, -16, "grower", 6) spawn.randomLevelBoss(1840, 675) }, movers() { simulation.makeTextLog(`movers by ryanbear`); level.custom = () => { level.exit.drawAndCheck(); level.enter.draw(); for (var i = 0; i < trains.length; i++) { //oscillate back and forth if (trains[i].position.x < 5075) { trains[i].changeDirection(true) //go right } else if (trains[i].position.x > 7875) { trains[i].changeDirection(false) //go left } trains[i].draw(); trains[i].move(); } for (var j = 0; j < zzz.length; j++) { zzz[j][0].query(); } mvr.push(); v3.query(); ctx.fillStyle = "rgba(68,68,68,1)"; ctx.fillRect(1725, -2400, 1000, 150); ctx.fillRect(2175, -2775, 250, 450); ctx.fillRect(2200, -2825, 225, 200); ctx.fillRect(2075, -2575, 150, 200); ctx.fillRect(2075, -2700, 150, 150); ctx.fillRect(1875, -2525, 300, 125); ctx.fillRect(1975, -2575, 150, 75); ctx.fillRect(1800, -2475, 175, 100); ctx.fillRect(2150, -2725, 350, 375); ctx.fillRect(2475, -2575, 175, 200); ctx.fillRect(2675, -2550, 25, 175); ctx.fillRect(2625, -2550, 75, 200); ctx.fillRect(2025, -2600, 200, 175); ctx.fillRect(2025, -2675, 225, 225); ctx.fillRect(2125, -2800, 250, 375); ctx.fillRect(2400, -2625, 175, 175); ctx.fillRect(2450, -2700, 100, 225); ctx.fillRect(1950, -2600, 150, 200); ctx.fillRect(1675, -2325, 250, 75); ctx.fillRect(2700, -2525, 25, 150); }; simulation.enableConstructMode() level.setPosToSpawn(0, -50); //normal spawn level.exit.x = 23885; level.exit.y = 800; spawn.mapRect(-98, -8, 1000, 20); //bump for level entrance spawn.mapRect(972, -287, 200, 20); //x, y, width, height, maxHeight, force = 0.003, friction = { up: 0.01, down: 0.2 }) { level.defaultZoom = 1800 simulation.zoomTransition(level.defaultZoom) var trains = []; var zzz = []; spawn.mapRect(9850, 475, 200, 75); for (var i = 0; i < 6; i++) { trains.push(level.transport(6275, -2100 + 525 * i, 600, 50, (2 * i % 2 - 1) * 4 * Math.min(simulation.difficulty / 2, 2) * (1 + Math.random()))) zzz.push([level.boost(6275, -2100 + 525 * i, 100), 6275]); } document.body.style.backgroundColor = "#d8dadf"; const portal1 = level.portal({ x: 3984, y: 1293 }, -2 * Math.PI, { //right x: 23863, y: 82 }, 2 * Math.PI) //right spawn.mapRect(1825, -2250, 3300, 300); spawn.mapRect(3250, -2875, 150, 625); spawn.mapRect(3250, -2875, 425, 125); spawn.mapRect(3425, -2725, 150, 300); spawn.mapRect(3400, -2750, 175, 350); spawn.mapRect(3575, -2625, 150, 375); spawn.mapRect(3175, -2750, 75, 300); spawn.mapRect(3100, -2750, 275, 300); spawn.mapRect(3675, -2875, 75, 125); spawn.mapRect(3675, -2625, 75, 400); spawn.mapRect(3350, -2425, 100, 175); spawn.mapRect(8350, 825, 1825, 250); spawn.mapRect(3950, 800, 800, 375); var hzd = level.hazard(3750, -2625, 1375, 375); // spawn.mapRect(3750, -2625, 1375, 375); var v1 = level.vanish(3975, -2600, 225, 25); var v2 = level.vanish(4275, -2975, 225, 25); var mvr = level.mover(2585, 1928, 2375, 100); //spawn.mapRect(4925, 1725, 300, 25); var v3 = level.vanish(4925, 1725, 100, 25); for (var i = 0; i < 16; i++) { if (i < 10) { level.boost(1600 + 62 * i, -2307 - 62 * i, 100); } else { level.boost(1600 + 62 * i, -2307 - 62 * 20 + 62 * i, 100); } } for (var i = -1; i < 10; i++) { level.boost(3847 - 62 * i, 879 + 62 * i, 100); } spawn.mapRect(3050, 1500, 1600, 200); spawn.mapRect(1850, -1950, 3275, 1275); spawn.mapRect(1850, -675, 3275, 1300); spawn.mapRect(2700, -2525, 25, 175); spawn.mapRect(3825, 925, 125, 575); spawn.mapRect(3600, 1100, 350, 400); spawn.mapRect(3375, 1350, 275, 150); spawn.mapRect(3550, 1300, 100, 50); spawn.mapRect(3800, 1000, 100, 150); spawn.mapRect(3725, 1075, 150, 125); spawn.mapRect(3725, 1025, 150, 125); spawn.mapRect(3550, 1225, 150, 125); spawn.mapRect(3500, 1275, 175, 125); // color.map = "#444" //custom map color bosses = ["laserBoss", "blinkBoss", "shooterBoss", "launcherBoss", "pulsarBoss", "beetleBoss", "bladeBoss", "revolutionBoss", "dragonFlyBoss", "spiderBoss"]; let randomBoss = Math.floor(Math.random() * bosses.length); spawn[bosses[randomBoss]](2240, -2499, 100, false); var btn = level.button(9889, 747); btn.isUp = true; spawn.randomMob(475, -725, 0.7); spawn.randomMob(825, -1825, 0.7); spawn.randomMob(3275, -3475, 0.7); spawn.randomMob(8550, 350, 0.7); spawn.randomMob(9350, -175, 0.7); spawn.randomMob(1575, 225, 0.7); spawn.randomMob(22825, 250, 0.7); spawn.mapRect(-100, 0, 1000, 100); var ddd = level.elevator(1326, -447, 200, 200, -2131, 0.003, { up: 0.1, down: 0.2 }); /// transport(x, y, width, height, VxGoal = -6, force = VxGoal > 0 ? 0.0005 : -0.0005) { spawn.mapRect(9500, 750, 675, 75); spawn.mapRect(22350, 825, 3000, 150); powerUps.spawn(4246, 1335, "tech") powerUps.spawn(4246.8, 1335, "heal") powerUps.spawn(4246.8, 1335.4, "ammo") spawn.bodyRect(9200, 725, 50, 25); spawn.mapRect(12200, 675, 125, 50); spawn.mapRect(12925, 675, 100, 100); spawn.mapRect(13675, 650, 150, 150); spawn.mapRect(14200, 750, 25, 25); spawn.mapRect(14200, 675, 25, 75); spawn.mapRect(14550, 675, 125, 50); spawn.mapRect(15850, 675, 125, 100); spawn.mapRect(17175, 600, 25, 200); spawn.mapRect(17725, 700, 175, 50); spawn.mapRect(18775, 675, 175, 75); spawn.bodyRect(8975, 700, 25, 25); spawn.bodyRect(8850, 575, 50, 50); spawn.bodyRect(9050, 650, 50, 50); spawn.bodyRect(8625, 575, 100, 75); spawn.bodyRect(8475, 675, 75, 25); var train1 = level.transport(10250 - 700, 775, Math.max(1200 / simulation.difficulty, 200), 1350, 8); level.customTopLayer = () => { ddd.move(); hzd.query(); v1.query(); v2.query(); btn.draw(); portal1[2].query(); portal1[2].draw(); portal1[3].query(); portal1[3].draw(); btn.query(); if (!btn.isUp) { spawn.mapRect(4050, 1175, 600, 325); } if (!btn.isUp && train1.position.x < 23785) { train1.draw(); train1.move(); } // if (trains[i].position.x < 5075) { // trains[i].changeDirection(true) //go right }; // powerUps.spawnStartingPowerUps(1475, -1175); // spawn.debris(750, -2200, 3700, 16); //16 debris per level // spawn.bodyRect(1540, -1110, 300, 25, 0.9); // spawn.randomSmallMob(1300, -70); // spawn.randomMob(2650, -975, 0.8); // spawn.randomGroup(1700, -900, 0.4); // if (simulation.difficulty > 1) spawn.randomLevelBoss(2200, -1300); // spawn.secondaryBossChance(100, -1500) powerUps.addResearchToLevel() //needs to run after mobs are spawned }, downpour() { simulation.makeTextLog(`Downpour by DesBoot`); let mobsspawned = 0 const laser = level.hazard(7492, -2612, 10, 500, 0.3) //laserintro //5381, -3268, 10, 0.4 spawn.mapRect(340, -2032.5, 20, 25); //laser nose //laserintro const laserbutton = level.button(5485, -2510) const doorbutton = level.button(7618, -3204) const doortoggle = level.toggle(5088.4, 1226.7) const door = level.door(6500, -1200, 100, 350, 100) const bunkerdoor = level.door(10700, -2500, 100, 500, 200) const boost1 = level.boost(7300, 1209, 2200) const boost2 = level.boost(6232.6, -832.8, 1400) const portal = level.portal({ x: 4886.4, y: 1050.7 }, 2 * Math.PI, { x: 7686, y: -2121 }, 2 * Math.PI) //let portal const slime = level.hazard(-1800, 10, 4200, 400); 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() level.setPosToSpawn(0, -50); //normal spawn level.exit.x = 13130.3; let rainCount = 1 level.exit.y = -370; spawn.mapRect(level.enter.x, level.enter.y + 20, 100, 20); //bump for level entrance spawn.mapRect(level.exit.x, level.exit.y + 20, 100, 20); //bump for level exit level.defaultZoom = 1800 let stopcycle = 0 let flashcycle = Math.round(Math.random() * 25 + 260) simulation.zoomTransition(level.defaultZoom) document.body.style.backgroundColor = "#2e416e";//d8dadf // color.map = "#444" //custom map color //simulation.makeTextLog(stopcycle) level.custom = () => { do { ctx.beginPath() ctx.fillStyle = "rgba(30,150,117,255)" ctx.rect(Math.random() * 4500 - 2000, -5000, Math.random() * 3 + 2.5, 5000) ctx.rect(Math.random() * 4500 - 2000, -5000, Math.random() * 3 + 2.5, 5000) ctx.rect(Math.random() * 4500 - 2000, -5000, Math.random() * 3 + 2.5, 5000) ctx.rect(Math.random() * 2000 + 2500, -5000, Math.random() * 3 + 2.5, 3000) ctx.rect(Math.random() * 2000 + 2500, -5000, Math.random() * 3 + 2.5, 3000) ctx.rect(Math.random() * 1300 + 4500, -5000, Math.random() * 3 + 2.5, 2500) ctx.rect(Math.random() * 1300 + 7500, -5000, Math.random() * 3 + 2.5, 1800) ctx.rect(Math.random() * 1800 + 5700, -5000, Math.random() * 3 + 2.5, 3000) ctx.rect(Math.random() * 1800 + 5700, -5000, Math.random() * 3 + 2.5, 3000) ctx.rect(Math.random() * 1800 + 8400, -5000, Math.random() * 3 + 2.5, 3000) ctx.rect(Math.random() * 1800 + 8400, -5000, Math.random() * 3 + 2.5, 3000) ctx.rect(Math.random() * 4500 - 2000, -5000, Math.random() * 3 + 2.5, 5000) ctx.rect(Math.random() * 1800 + 10200, -5000, Math.random() * 3 + 2.5, 3000) ctx.rect(Math.random() * 1800 + 10200, -5000, Math.random() * 3 + 2.5, 3000) ctx.rect(Math.random() * 1800 + 12000, -5000, Math.random() * 3 + 2.5, 3000) ctx.rect(Math.random() * 1800 + 12000, -5000, Math.random() * 3 + 2.5, 3000) ctx.fillStyle = "rgba(30,150,117,255)" ctx.fill() // } // if (rainCount > 12) { // rainCount = 1 // simulation.makeTextLog(rainCount) // } else { // rainCount = rainCount + 1 // simulation.makeTextLog(rainCount) // } } while (Math.random() < 0.8); //simulation.makeTextLog(stopcycle) //simulation.makeTextLog(m.cycle) // ctx.fillStyle = "rgba(228,255,0,0.8)" // //simulation.makeTextLog(stopcycle) // ctx.fillRect(50.4, -1210.0, 100, 100) // stopcycle = m.cycle + Math.random * 600; //stopcycle = m.cycles + Math.random * 600 if (stopcycle > 300) { stopcycle = 0 flashcycle = Math.round(Math.random() * 25 + 260) document.body.style.backgroundColor = "#2e416e"; } else { if (stopcycle > flashcycle) { document.body.style.backgroundColor = "#7391ff"; for (let i = 0; i < mob.length; i++) mobs.statusStun(mob[i], Math.random() * 20 + 30) } stopcycle = stopcycle + 1 } ctx.fillStyle = "#d4f4f4" ctx.fillRect(12984, -704, 420, 450) ctx.fillStyle = "rgba(0,0,0,0.5)" ctx.fillRect(4703, -2362, 100, 100) ctx.fillRect(5053, -2362, 100, 100) ctx.fillRect(5403, -2362, 100, 100) ctx.fillRect(4703, -2062, 100, 100) ctx.fillRect(5053, -2062, 100, 100) ctx.fillRect(5403, -2062, 100, 100) ctx.fillRect(4523, -2512, 1150, 800) ctx.fillRect(4735, -1233, 100, 500)//tree ctx.beginPath() ctx.moveTo(4487, -1195)//slope of -1/3 ctx.lineTo(4736, -792) ctx.lineTo(4736, -852) ctx.lineTo(4527, -1195) ctx.moveTo(5087, -1195)//slope of -1/3 ctx.lineTo(4836, -792) ctx.lineTo(4836, -852) ctx.lineTo(5047, -1195) ctx.fill() ctx.moveTo(5252.4, -2483.5) ctx.lineTo(5141.2, -2507.8) ctx.lineTo(5209.2, -2625.2) ctx.lineTo(5290.2, -2626.6) ctx.lineTo(5361.2, -2697.9) ctx.lineTo(5410.6, -2717.0) ctx.lineTo(5680.2, -2648.7) ctx.lineTo(5687.7, -2471.5) ctx.fill() //building 2 spawn.mapRect(8473, -2513, 50, 50); ctx.fillRect(8673, -2137, 50, 175) ctx.fillRect(7630, -2540, 100, 100) ctx.fillRect(7930, -2540, 100, 100) ctx.fillRect(8230, -2540, 100, 100) ctx.fillRect(8530, -2765, 100, 100) ctx.fillRect(7630, -2990, 100, 100) ctx.fillRect(7930, -2990, 100, 100) ctx.fillRect(8230, -2990, 100, 100) ctx.beginPath() ctx.moveTo(7475, -3213) ctx.lineTo(8100, -3213) ctx.lineTo(8191.2, -3334.7) ctx.lineTo(8318.0, -3388.3) ctx.lineTo(8348.5, -3496.9) ctx.lineTo(8480.0, -3512.6) ctx.lineTo(8670, -3482) ctx.lineTo(8725, -3213) ctx.lineTo(8725, -1463) ctx.lineTo(7475, -1463) ctx.fill() //stairs spawn.mapRect(7523, -2313, 800, 75); ctx.fillRect(8523, -2563, 50, 50) ctx.fillRect(8473, -2613, 50, 50) ctx.fillRect(8423, -2663, 50, 50) ctx.fillRect(8373, -2713, 50, 50) ctx.fillRect(8323, -2763, 50, 50) ctx.fillRect(8323, -2813, 50, 50) ctx.fillRect(8373, -2863, 50, 50) ctx.fillRect(8423, -2913, 50, 50) ctx.fillRect(8473, -2963, 50, 50) ctx.fillRect(8523, -3013, 50, 50)//make block ctx.fillRect(8473, -3063, 50, 50)//make block ctx.fillRect(8423, -3113, 50, 50)//make block ctx.fillRect(8373, -3163, 50, 50) ctx.fillRect(8323, -3213, 50, 50) //caves ctx.fillStyle = "rgba(30,150,117,255)"//fake slime //87,189,146,255 ctx.fillRect(6100, -1900, 100, 1050) ctx.fillRect(6400, -1900, 100, 1050) ctx.fillRect(2600, -850, 4700, 200) ctx.fillRect(7200, -650, 100, 1900) ctx.fillRect(2399, -1, 200, 400) //bunker ctx.fillStyle = "rgba(0,0,0,0.5)" ctx.beginPath() ctx.moveTo(10800, -2400)//slope of -1/3 ctx.lineTo(10800, -340) ctx.lineTo(12980, -340) ctx.lineTo(12980, -700) ctx.lineTo(13465, -700) ctx.lineTo(13541, -1737) ctx.lineTo(11864.6, -1967.0) ctx.lineTo(11003, -2400) ctx.fill() ctx.fillRect(6100, -2000, 400, 50) // -2000 -> 2500 // Math.random() * 5000 -2500 ctx.fillStyle = "rgba(0,0,0,0.6)" ctx.beginPath() ctx.moveTo(6100, -1700) ctx.lineTo(5799.5, -800) ctx.lineTo(2600, -800) ctx.lineTo(2600, -1700) ctx.lineTo(5799.5, -1700) ctx.moveTo(6500, -1200) ctx.lineTo(7600, -1200) ctx.lineTo(8000, 1400) ctx.lineTo(4600, 1500) ctx.lineTo(4500.5, 0) ctx.lineTo(6500, -200) ctx.lineTo(6500, -1200) ctx.fill() portal[2].query() portal[3].query() if (laserbutton.isUp) { laser.isOn = true; } else { laser.isOn = false; } ctx.fillStyle = "rgba(0,0,0,0.6)" ctx.fillRect(2113, -791, 500, 75) ctx.fillRect(1766, -1091, 250, 310) ctx.fillRect(4473, -2912, 50, 1000) ctx.fillRect(5673, -2712, 50, 800) ctx.fillStyle = "rgba(0,0,0,0.2)" ctx.fillRect(4523, -2512, 350, 75) ctx.fillRect(5273, -2212, 400, 75) level.exit.drawAndCheck(); slime.query(); slime2.query(); slime3.query(); slime4.query(); // spawn.mapRect(4873, -2512, 800, 75); // spawn.mapRect(4473, -2212, 800, 75); //setTimeout(function(){/*YourCode*/},1000); //water falling/flowing effect ctx.fillStyle = `hsla(160, 100%, 26%,${0.5 + 0.07 * Math.random()})`//lower river ctx.fillRect(-1800 + Math.random() * 100, 10 + 400 * Math.random(), 3900, 5) ctx.fillRect(-1800, 10 + 400 * Math.random(), 4400, 5) ctx.fillRect(2400 + 200 * Math.random(), Math.random() * - 100 - 2000, 5, 2000)//first waterfall ctx.fillRect(6100 + 100 * Math.random(), Math.random() * - 100 - 1900, 5, 1050)//twin waterfalls ctx.fillRect(6400 + 100 * Math.random(), Math.random() * - 100 - 1900, 5, 1050) ctx.fillRect(7200 + 100 * Math.random(), -800 - 50 * Math.random(), 5, 2032) level.enter.draw(); laserbutton.query(); laserbutton.draw(); doortoggle.query(); if (!doortoggle.isOn) { door.isClosing = true bunkerdoor.isClosing = true } else { door.isClosing = false bunkerdoor.isClosing = false if (mobsspawned == 0) { spawn.randomSmallMob(6128.0, 822.6); spawn.randomSmallMob(6854.8, 560.2); spawn.randomSmallMob(8320.7, -3402.4); spawn.randomMob(6629.0, 711.3, 0.8); spawn.randomMob(8199.2, -2545.5, 0.8); spawn.randomMob(8067.7, -2957.2, 0.8); spawn.randomMob(5149.6, -1444.1, 0.8); mobsspawned = 1 } } door.openClose(); bunkerdoor.openClose(); }; level.customTopLayer = () => { door.draw(); bunkerdoor.draw(); laser.opticalQuery(); if (player.position.y > -70 && player.position.x < 2785) { if (m.onGround) { Matter.Body.setVelocity(player, { x: player.velocity.x - (2 + m.pos.y / 150), y: player.velocity.y }); } else { Matter.Body.setVelocity(player, { x: player.velocity.x - (1 + m.pos.y / 150), y: player.velocity.y }); } } if (player.position.x > 2400 && player.position.x < 2600) { Matter.Body.setVelocity(player, { x: player.velocity.x, y: player.velocity.y + 4 }); } boost1.query(); boost2.query(); if (player.position.x > 2600 && player.position.x < 4500 && player.position.y < -1900 && player.position.y > -2121.3) { Matter.Body.setVelocity(player, { x: player.velocity.x - 2, y: player.velocity.y }); } if (player.position.x > 4500 && player.position.x < 6000 && player.position.y < -1900 && player.position.y > -2121.3) { if (input.left) { Matter.Body.setVelocity(player, { x: player.velocity.x + 0.1, y: player.velocity.y }); } else { Matter.Body.setVelocity(player, { x: player.velocity.x + 0.5, y: player.velocity.y }); } } if (player.position.x > 6500 && player.position.x < 7500 && player.position.y < -1900 && player.position.y > -2121.3) { Matter.Body.setVelocity(player, { x: player.velocity.x - 1, y: player.velocity.y }); } if (player.position.x > 7500 && player.position.x < 10000 && player.position.y < -1900 && player.position.y > -2121.3) { Matter.Body.setVelocity(player, { x: player.velocity.x - 1, y: player.velocity.y }); } if (player.position.x > 2600 && player.position.x < 6100 && player.position.y < -650 && player.position.y > -920) { if (input.right) { Matter.Body.setVelocity(player, { x: player.velocity.x - 0.2, y: player.velocity.y }); } else { Matter.Body.setVelocity(player, { x: player.velocity.x - 0.4, y: player.velocity.y }); } } if (player.position.x > 6500 && player.position.x < 7300 && player.position.y < -650 && player.position.y > -920 && m.onGround) { if (input.left) { Matter.Body.setVelocity(player, { x: player.velocity.x + 0.2, y: player.velocity.y }); } else { Matter.Body.setVelocity(player, { x: player.velocity.x + 0.4, y: player.velocity.y }); } } if (player.position.x > 7200 && player.position.x < 7350 && player.position.y > -950 && player.position.y < 1250) { Matter.Body.setVelocity(player, { x: player.velocity.x, y: player.velocity.y + 0.8 }); } if (player.position.x > 6100 && player.position.x < 6200 && player.position.y < -800 && player.position.y > -2000) { Matter.Body.setVelocity(player, { x: player.velocity.x, y: player.velocity.y + 0.3 }); } if (player.position.x > 6400 && player.position.x < 6500 && player.position.y < -800 && player.position.y > -2000) { Matter.Body.setVelocity(player, { x: player.velocity.x, y: player.velocity.y + 0.3 }); } // ctx.fillRect(7200, -650, 100, 1900) portal[0].draw(); portal[1].draw(); portal[2].draw(); portal[3].draw(); }; spawn.mapRect(-100, 0, 1000, 100); spawn.mapRect(-1800, 400, 4400, 1300); spawn.mapRect(-1800, 0, 100, 400); spawn.mapRect(2600, -2000, 3500, 300); spawn.mapRect(2600, -2000, 500, 800); spawn.mapRect(2955, -1779, 800, 300); spawn.mapRect(2600, -800, 2300, 2500); spawn.mapRect(-460, 100, 1570, 400); spawn.mapVertex(965, 67, "0 -100 220 0 0 0"); spawn.mapVertex(-185, 67, "0 -100 -420 0 0 0"); spawn.mapVertex(1210, 365, "0 -400 300 0 0 0"); spawn.mapRect(217.5, -358.5, 50, 360); spawn.mapRect(-83, -358.5, 300, 50); //blocks in river/waterfall spawn.mapRect(1275, 0, 450, 75); spawn.mapRect(2027, -388, 600, 75); spawn.mapRect(1666, -791, 450, 75); spawn.mapRect(1666, -1091, 450, 75); //buildings spawn.mapRect(4873, -2512, 800, 75); spawn.mapRect(4473, -2212, 800, 75); spawn.mapRect(4473, -2912, 50, 800); spawn.mapRect(5673, -2712, 50, 575); spawn.mapRect(6671.5, -2401.4, 500, 50); spawn.mapRect(6105.1, -2354.1, 400, 50); spawn.mapRect(4473, -2952, 8, 75);//1,3,2 spawn.mapRect(4493, -3032, 15, 150); spawn.mapRect(4513, -2982, 7, 75); spawn.mapRect(5673, -2742, 12, 50); spawn.mapRect(5703, -2772, 8, 100); //building 2 // ctx.fillRect(8323, -2363, 50, 50) spawn.mapRect(7473, -3412, 50, 800); spawn.mapRect(7473, -2312, 50, 500); spawn.mapRect(8673, -3212, 50, 1075); spawn.mapRect(7523, -2313, 800, 75); spawn.mapRect(7523, -2763, 800, 75); spawn.mapRect(7523, -3213, 800, 75); spawn.mapRect(8725, -2340, 400, 50); spawn.mapRect(8925, -2640, 200, 50); spawn.mapRect(8725, -2940, 200, 50); //stairs spawn.mapRect(8323, -2363, 50, 50); spawn.mapRect(8373, -2413, 50, 50); spawn.mapRect(8423, -2463, 50, 50); spawn.mapRect(8473, -2513, 250, 50); //stairs 2 spawn.mapRect(8523, -3013, 50, 50)//make block spawn.mapRect(8473, -3063, 50, 50)//make block spawn.mapRect(8423, -3113, 50, 50)//make block //trees in tunnel spawn.mapRect(4485, -1243, 600, 50) spawn.mapRect(3967, -1056, 400, 50) spawn.mapRect(5453, -1150, 50, 300) spawn.mapRect(5453, -1700, 50, 300) //tunnels and boss spawn.mapRect(6500, -2000, 3100, 800); spawn.mapRect(7500, -2000, 3300, 3700); spawn.mapRect(4900, -800, 2300, 1000); spawn.mapRect(4354, 1230, 4000, 470); spawn.mapRect(5388, 863, 100, 500); spawn.mapRect(5388, 63, 100, 500); spawn.mapRect(5834, 549, 500, 80); spawn.mapRect(6756, 897, 400, 80); //extra boss spawn.mapRect(9196, -11492, 500, 100); spawn.mapRect(9196, -11492, 500, 100); //bunker spawn.mapRect(11500, -2000, 1900, 500); spawn.mapRect(10800, -900, 800, 2600); spawn.mapRect(11600, -340, 1800, 2600); spawn.mapRect(13400, -2000, 1800, 3600); spawn.mapRect(10800, -2500, 200, 100); spawn.mapVertex(11400, -2235, "0 10 900 510 800 510 750 510 0 110"); spawn.mapVertex(10100, -2000, "0 0 0 -250 400 0"); spawn.mapRect(12945.0, -741.9, 600, 50); spawn.mapRect(12945.0, -741.9, 50, 250); //stairs spawn.mapRect(11600, -850, 50, 550); spawn.mapRect(11650, -800, 50, 500); spawn.mapRect(11700, -750, 50, 450); spawn.mapRect(11750, -700, 50, 400); spawn.mapRect(11800, -650, 50, 350); spawn.mapRect(11850, -600, 50, 300); spawn.mapRect(11900, -550, 50, 250); spawn.mapRect(11950, -500, 50, 200); spawn.mapRect(12000, -450, 50, 150); spawn.mapRect(12050, -400, 50, 100); spawn.mapRect(12100, -350, 50, 50); //mobs //spawn.tetherBoss(6480, 992, { x: 6480, y: 210 }) if (Math.random() < 0.5) { spawn.tetherBoss(6480, 992, { x: 6480, y: 210 }) } else { spawn.randomLevelBoss(5977, 992) } //mobs for waterfall and first cavern //spawn.randomSmallMob(1999.2, -487.4); spawn.randomMob(1999.2, -487.4, 0.8); //spawn.randomSmallMob(2080.0, -1206.4); spawn.randomMob(2080.0, -1206.4, 0.8); spawn.randomSmallMob(3287.5, -1021.1); //spawn.randomSmallMob(3992.2, -1223.9); spawn.randomSmallMob(5018.1, -1483.5); spawn.randomGroup(6776.2, -3054.5, 0.4); spawn.randomGroup(4217.4, -1403.6, 0.4); //surface area mobs spawn.randomSmallMob(5089.0, -2284.1); spawn.randomSmallMob(6988.3, -2580.2); spawn.randomSmallMob(7975.0, -2920.3); spawn.randomMob(5132.0, -2646.2, 0.8); spawn.randomMob(6365.2, -2459.2, 0.8); spawn.randomMob(8129.0, -2406.7, 0.8); spawn.randomMob(8129.0, -2406.7, 0.8); spawn.randomGroup(2225.3, -1543.2, 0.4); spawn.debris(4426.9, -1433.8, 700, 1); //16 debris per level spawn.debris(4651.2, -2597.3, 700, 1); //16 debris per level spawn.debris(9920.9, -2378.3, 700, 2); //16 debris per level spawn.debris(8298.5, -2883.8, 700, 1); //16 debris per level spawn.debris(6779.2, -2662.9, 700, 1); //16 debris per level spawn.debris(6371.5, 442.3, 700, 2); //16 debris per level spawn.debris(1873.5, -1297.5, 700, 1); //16 debris per level spawn.bodyRect(6457.9, -2541.5, 300, 25, 0.9); //spawn.bodyRect(5685, -2140, 25, 140, 0.9); spawn.bodyRect(4473, -2110, 50, 110, 0.9); //spawn.bodyRect(5292.1, -2617.2, 50, 50, 0.9); spawn.bodyRect(6370.1, -2408.4, 50, 50, 0.9); //spawn.bodyRect(5467, -1400, 25, 250, 0.9); spawn.bodyRect(4509.0, -1425.7, 30 + 45 * Math.random(), 30 + 45 * Math.random(), 0.9); //spawn.bodyRect(8082.9, -2488.1, 30 + 45 * Math.random(), 30 + 45 * Math.random(), 0.9); spawn.bodyRect(7859.6, -2883.6, 30 + 45 * Math.random(), 30 + 45 * Math.random(), 0.9); //spawn.bodyRect(5609.5, 948.5, 30 + 45 * Math.random(), 30 + 45 * Math.random(), 0.9); spawn.bodyRect(5803.7, 1125.5, 30 + 45 * Math.random(), 30 + 45 * Math.random(), 0.9); //spawn.bodyRect(5492.1, 1061.7, 90, 169, 0.9); spawn.bodyRect(5582.1, 1061.7, 110, 70, 0.9); //spawn.bodyRect(5582.1, 961.7, 50, 30, 0.9); // spawn.randomSmallMob(1300, -70); // spawn.randomSmallMob(1300, -70); // spawn.randomSmallMob(1300, -70); // spawn.randomSmallMob(1300, -70); // powerUps.spawnStartingPowerUps(1475, -1175); // spawn.debris(750, -2200, 3700, 16); //16 debris per level // spawn.bodyRect(1540, -1110, 300, 25, 0.9); // spawn.randomSmallMob(1300, -70); // spawn.randomMob(2650, -975, 0.8); // spawn.randomGroup(1700, -900, 0.4); // if (simulation.difficulty > 1) spawn.randomLevelBoss(2200, -1300); // spawn.secondaryBossChance(100, -1500) powerUps.addResearchToLevel() //needs to run after mobs are spawned }, dungeon() { let destroyed = false; const door = level.door(2650, -825, 50, 250, 250, 10); const elevator = level.elevator(-11050, -650, 450, 75, -2975, 0.003, { up: 0.1, down: 0.1 }) const slimePit = level.hazard(-4775, -350, 1975, 175); const boost = level.boost(137.5, -600, 75, 25); let base = Matter.Bodies.rectangle(-4375, -1000, 100, 100, { density: 0.05, isNotHoldable: true, restitution: 1.05, isStatic: false }, true, [true], 0); let left = Matter.Bodies.rectangle(-4375 + 50, -1000 - 50, 50, 50, {//not actually left/right density: 0.05, isNotHoldable: true, isStatic: false }); let right = Matter.Bodies.rectangle(-4375 - 50, -1000 - 50, 50, 50, { density: 0.05, isNotHoldable: true, isStatic: false }); let left2 = Matter.Bodies.rectangle(-4375 - 50, -1000 + 50, 50, 50, { density: 0.05, isNotHoldable: true, isStatic: false }); let right2 = Matter.Bodies.rectangle(-4375 + 50, -1000 + 50, 50, 50, { density: 0.05, isNotHoldable: true, isStatic: false }); dong = Matter.Body.create({ parts: [base, left, right, left2, right2] }); body[body.length] = base; body[body.length] = left; body[body.length] = right; body[body.length] = left2; body[body.length] = right2; Matter.Composite.add(engine.world, dong) Matter.Composite.add(engine.world, Constraint.create({ pointA: { x: -3825, y: -975 }, bodyB: dong, stiffness: 0.2, damping: 0.1 })); composite[composite.length] = dong; setTimeout(function () { dong.collisionFilter.category = cat.body; dong.collisionFilter.mask = cat.body | cat.player | cat.bullet | cat.mobBullet | cat.mob //| cat.map }, 1000); level.custom = () => { ctx.save() ctx.beginPath() ctx.fillStyle = "#80808077"; ctx.strokeStyle = "#80808022"; ctx.fillRect(225, -1025, 2400, 450); ctx.fillRect(-2950, -1025, 3100, 450); ctx.fillRect(-7050, -1025, 2400, 450); ctx.fillRect(-10575, -3975, 4525, 1025); ctx.fillRect(-4650, -1700, 1700, 1100); ctx.fillRect(-11150, -3575, 575, 3050); ctx.fillRect(-11900, -1000, 750, 475); ctx.fill() ctx.stroke() ctx.restore() ctx.save() ctx.beginPath() ctx.fillStyle = "#d8dadf"; ctx.strokeStyle = "#d8dadf"; ctx.moveTo(-2950, -600); ctx.lineTo(-3730, -1725); ctx.lineTo(-3730, -600); ctx.moveTo(-4650, -600); ctx.lineTo(-3925, -1725); ctx.lineTo(-3925, -575); ctx.moveTo(-10575, -3425); //NE section ctx.lineTo(-10100, -2975); ctx.lineTo(-10575, -2975); // ctx.moveTo(-7625, -3800); // ctx.lineTo(-6750, -2975); // ctx.lineTo(-7625, -2975); ctx.moveTo(-7975, -2975); ctx.lineTo(-7625, -3800); ctx.lineTo(-7350, -2950); ctx.moveTo(-6750, -2975); ctx.lineTo(-7075, -3800); ctx.lineTo(-7350, -2950); // ctx.moveTo(-7975, -2975); // ctx.lineTo(-7075, -3800); // ctx.lineTo(-7075, -2975); ctx.moveTo(-11900, -950); ctx.lineTo(-11900, -550); ctx.lineTo(-11500, -550); ctx.fillRect(-3925, -1675, 200, 1075); ctx.fillRect(-7625, -3800, 550, 875); ctx.clearRect(-10600, -4000, 525, 475); ctx.clearRect(-10100, -4000, 500, 300); ctx.clearRect(-9625, -4000, 500, 175); ctx.fillRect(-11125, -3600, 550, 50); ctx.fillRect(-10600, -3400, 50, 425); ctx.fillRect(-11925, -925, 45, 375); ctx.fillRect(-3950, -1675, 75, 1100); ctx.fillRect(-3925, -625, 950, 50); ctx.fillRect(-4650, -600, 1700, 375); ctx.fillRect(-14550, -2400, 2650, 2050); //ctx.clearRect(-11050, -3000, 475, 50); ctx.moveTo(-11150, -3575); ctx.lineTo(-10575, -2150); ctx.lineTo(-10575, -3575); ctx.stroke() ctx.fill() ctx.restore() boost.query() slimePit.query() if (Matter.Query.collides(dong, [player]).length > 0 && !(m.isCloak && tech.isIntangible) && m.immuneCycle < m.cycle) { m.immuneCycle = m.cycle + m.collisionImmuneCycles; //player is immune to damage for 30 cycles const dmg = 0.05 * Math.min(simulation.dmgScale, simulation.difficulty); m.damage(dmg); simulation.drawList.push({ //add dmg to draw queue x: dong.position.x, y: dong.position.y, radius: Math.sqrt(dmg) * 200, color: simulation.mobDmgColor, time: simulation.drawTime }); } for (let i = 0; i < mob.length; i++) { if (Matter.Query.collides(dong, [mob[i]]).length > 0) { const dmg = 1; mob[i].damage(dmg, true); simulation.drawList.push({ //add dmg to draw queue x: dong.position.x, y: dong.position.y, radius: Math.sqrt(dmg) * 50, color: simulation.mobDmgColor, time: simulation.drawTime }); break } } level.exit.drawAndCheck(); ctx.beginPath() ctx.fillStyle = '#68686822'; ctx.fillRect(-25, -2175, 100, 200); ctx.fill() ctx.setLineDash([125 * Math.random(), 125 * Math.random()]); ctx.moveTo(-3825, -975) ctx.lineTo(dong.position.x, dong.position.y) ctx.stroke(); ctx.setLineDash([]); simulation.drawList.push({ //add dmg to draw queue x: dong.position.x, y: dong.position.y, radius: 10, color: color.block, time: 20 }); ctx.beginPath() ctx.fillStyle = `rgba(68,68,68, ${3 * Math.sin(simulation.cycle * 0.015)})` ctx.fillRect(-3000, -2175, 175, 25); ctx.fillRect(-2850, -2300, 25, 150); ctx.fillRect(-3000, -2300, 175, 25); ctx.fillRect(-3000, -2425, 25, 150); ctx.fillRect(-3000, -2425, 175, 25); ctx.fill() ctx.fillStyle = `rgba(68,68,68, ${5 * Math.sin(simulation.cycle * 0.015)})` ctx.fillRect(-2725, -2425, 25, 275); ctx.fillRect(-2725, -2425, 175, 25); ctx.fillRect(-2575, -2425, 25, 275); ctx.fillRect(-2725, -2300, 175, 25); ctx.fill() ctx.fillStyle = `rgba(68,68,68, ${7 * Math.sin(simulation.cycle * 0.015)})` ctx.fillRect(-2450, -2425, 25, 275); ctx.fillRect(-2450, -2175, 175, 25); ctx.fill() ctx.stroke(); ctx.fillStyle = `#00FFFF22`; ctx.fillRect(-7650, -2975 - 100, 600, 2375 + 100) ctx.fill() ctx.fillStyle = `#00FFFF66` ctx.fillRect(-7650 + Math.floor(Math.random() * 600), -2975 - 100, 5, 2375 + 100) ctx.fillRect(-7650 + Math.floor(Math.random() * 600), -2975 - 100, 5, 2375 + 100) ctx.fillStyle = `rgba(68, 68, 68)` ctx.fillRect(-7675, -3075, 50, 125); ctx.fillRect(-7075, -3075, 50, 125); ctx.fillRect(-7725, -3025, 75, 75); ctx.fillRect(-7050, -3025, 75, 75); ctx.fill() for (let i = 0, len = body.length; i < len; ++i) { //push blocks away vertically if (body[i].position.x > -7625 && body[i].position.x < -7075 && body[i].position.y > -2975 - 100 && body[i].position.y < -625) { body[i].force.y -= simulation.g * body[i].mass + 0.012; } } for (let i = 0, len = bullet.length; i < len; ++i) { //push bullets away vertically if (bullet[i].position.x > -7625 && bullet[i].position.x < -7075 && bullet[i].position.y > -2975 - 100 && bullet[i].position.y < -625) { bullet[i].force.y -= simulation.g * bullet[i].mass; } } for (let i = 0, len = powerUp.length; i < len; ++i) { //push powerups away vertically if (powerUp[i].position.x > -7625 && powerUp[i].position.x < -7075 && powerUp[i].position.y > -2975 - 100 && powerUp[i].position.y < -625) { powerUp[i].force.y -= simulation.g * powerUp[i].mass + 0.12; } } for (let i = 0, len = mob.length; i < len; ++i) { //push mobs away vertically if (mob[i].position.x > -7625 && mob[i].position.x < -7075 && mob[i].position.y > -2975 - 100 && mob[i].position.y < -625) { mob[i].force.y -= simulation.g * mob[i].mass + 0.0012; } } if (m.pos.x > -7625 && m.pos.x < -7075 && m.pos.y > -2975 - 100 && m.pos.y < -625) { player.force.y -= m.mass * simulation.g + (input.down ? 0 : 0.012 * 2); } elevator.move() }; level.setPosToSpawn(30, -2000); //normal spawn level.exit.x = 2775; level.exit.y = -650; level.defaultZoom = 1800 simulation.zoomTransition(level.defaultZoom + 800) document.body.style.backgroundColor = "#d8dadf"; spawn.mapRect(-225, -1950, 350, 75); spawn.mapRect(225, -1950, 50, 75); spawn.mapRect(-250, -2025, 50, 150); spawn.mapRect(250, -2025, 50, 150); spawn.mapRect(-250, -2250, 50, 125); spawn.mapRect(-225, -2325, 500, 100); spawn.mapRect(250, -2250, 50, 125); spawn.mapRect(-100, -2400, 250, 100); spawn.mapRect(-25, -2475, 100, 100); spawn.mapRect(125, -2350, 50, 50); spawn.mapRect(-125, -2350, 50, 50); spawn.mapRect(-50, -2425, 50, 50); spawn.mapRect(50, -2425, 50, 50); spawn.mapRect(-250, -2350, 50, 50); spawn.mapRect(250, -2350, 50, 50); spawn.mapRect(-75, -1975, 200, 50); spawn.mapRect(-50, -2000, 150, 50); spawn.mapRect(100, -1950, 50, 75); spawn.mapRect(-75, -2250, 200, 50); spawn.mapRect(-50, -2225, 150, 50); spawn.mapRect(-2950, -1900, 3100, 900); spawn.mapRect(225, -1900, 2875, 900); spawn.mapRect(-2950, -600, 6050, 450); spawn.mapRect(-3050, -500, 200, 350); spawn.mapRect(-3150, -400, 200, 250); spawn.mapRect(-3250, -300, 200, 150); spawn.mapRect(2950, -1050, 150, 500); spawn.mapRect(-4675, -1900, 1825, 200); spawn.mapRect(-5325, -1900, 675, 900); spawn.mapRect(-5325, -250, 2100, 100); spawn.mapRect(-5325, -600, 675, 450); // - spawn.mapRect(-4700, -500, 150, 350); spawn.mapRect(-4650, -400, 200, 250); spawn.mapRect(-4550, -300, 200, 150); spawn.mapRect(-3875, -1025, 100, 100); spawn.mapRect(-3800, -1050, 50, 50); spawn.mapRect(-3900, -1050, 50, 50); spawn.mapRect(-3800, -950, 50, 50); spawn.mapRect(-3900, -950, 50, 50); spawn.mapRect(-6925, -1175, 1700, 175); spawn.mapRect(-6925, -600, 1725, 175); spawn.mapRect(-7700, -600, 800, 425);// - spawn.mapRect(-7800, -2950, 175, 2775); spawn.mapRect(-7075, -2950, 175, 1950); spawn.mapRect(-9150, -2975, 1525, 175); spawn.mapRect(-7075, -2975, 1150, 175); spawn.mapRect(-6100, -3900, 175, 1100); spawn.mapRect(-9150, -3975, 3225, 175); spawn.mapRect(-9175, -3850, 75, 75); spawn.mapRect(-9625, -3825, 500, 150); spawn.mapRect(-9650, -3725, 75, 75); spawn.mapRect(-10100, -3700, 500, 150); spawn.mapRect(-10100, -2975, 975, 175); spawn.mapRect(-10125, -3600, 75, 75); spawn.mapRect(-10575, -3575, 500, 150); spawn.mapRect(-10575, -2975, 500, 175); spawn.mapRect(-11325, -2975, 250, 175); spawn.mapRect(-11325, -3575, 175, 775); // spawn.mapRect(-11325, -3575, 800, 150); spawn.mapRect(-11225, -2975, 150, 2000); spawn.mapRect(-10575, -2975, 150, 2500); spawn.mapRect(-11650, -550, 1225, 150); spawn.mapRect(-11650, -1100, 575, 150); spawn.mapRect(-14675, -2525, 2925, 150); spawn.mapRect(-11900, -2525, 150, 1575); spawn.mapRect(-11850, -1100, 250, 150); spawn.mapRect(-11875, -550, 275, 150); spawn.mapRect(-11900, -550, 150, 350); spawn.mapRect(-14675, -2525, 150, 2300); spawn.mapRect(-14675, -375, 2925, 175); spawn.mapRect(2725, -625, 250, 50); spawn.mapRect(2625, -1025, 100, 225); spawn.mapRect(2700, -1025, 300, 125); spawn.mapRect(2625, -612.5, 125, 50); spawn.mapRect(-3950, -1725, 250, 50); spawn.mapRect(-7650, -3825, 600, 50); spawn.mapRect(-13900, -2400, 200, 50); spawn.mapVertex(-11957, -430, '-175 175 0 175 0 0'); spawn.mapVertex(-14470, -430, '175 175 0 175 0 0'); spawn.mapVertex(-11957, -2319, '-175 -175 0 -175 0 0'); spawn.mapVertex(-14470, -2319, '0 0 0 -175 175 -175'); //spawn.mapRect(-13900, -2150, 1375, 125); const sword = function () { //the ultimate blade of las destruction mobs.spawn(player.position.x, player.position.y, 5, 30, "transparent"); let me = mob[mob.length - 1]; Matter.Body.setDensity(me, 1); me.vertices = Matter.Vertices.rotate(me.vertices, Math.PI, me.position); me.collisionFilter.category = cat.bullet; me.collisionFilter.mask = cat.mob | cat.mobBullet; me.isDropPowerUp = false; me.isShielded = true; me.showHealthBar = false; me.isUnblockable = true; me.leaveBody = false; me.isBadTarget = true; me.stroke = "transparent"; me.isSword = true; let index = 0; let radius = 50; me.do = function () { this.health = Infinity;//just in case for (let i = 0; i < mob.length; i++) { if (Matter.Query.collides(this, [mob[i]]).length > 0 && !mob[i].isSword) { const dmg = 0.25;//do not nerf mob[i].damage(dmg, true); simulation.drawList.push({ //add dmg to draw queue x: mob[i].position.x, y: mob[i].position.y, radius: Math.sqrt(dmg) * 50, color: simulation.mobDmgColor, time: simulation.drawTime }); break } } Matter.Body.setPosition(this, { x: player.position.x + Math.cos(m.angle) * 100, y: player.position.y - (input.down ? 0 : 30) + Math.sin(m.angle) * 100 }) Matter.Body.setAngle(this, m.angle + Math.PI * 2); const setNoseShape = () => { const mag = radius + radius * 10; this.vertices[2].x = this.position.x + Math.cos(this.angle) * mag; this.vertices[2].y = this.position.y + Math.sin(this.angle) * mag; this.vertices[4].x = this.position.x + Math.cos(this.angle) * mag; this.vertices[4].y = this.position.y + Math.sin(this.angle) * mag; this.vertices[0].x = this.position.x + Math.cos(this.angle) * mag; this.vertices[0].y = this.position.y + Math.sin(this.angle) * mag; }; const spike = Vector.mult(Vector.normalise(Vector.sub(this.vertices[2], this.position)), radius * 100) const spike2 = Vector.mult(Vector.normalise(Vector.sub(this.vertices[4], this.position)), radius * 500) const spike3 = Vector.mult(Vector.normalise(Vector.sub(this.vertices[0], this.position)), radius * 500) this.vertices[2].x = this.position.x + spike.x / 100 this.vertices[2].y = this.position.y + spike.y / 100 this.vertices[4].x = this.position.x + spike2.x / 75 this.vertices[4].y = this.position.y + spike2.y / 75 this.vertices[0].x = this.position.x + spike3.x / 75 this.vertices[0].y = this.position.y + spike3.y / 75 if (index == 0) { setNoseShape(); index++; } ctx.save() ctx.beginPath(); const vertices = this.vertices; ctx.lineWidth = 100; ctx.moveTo(vertices[0].x, vertices[0].y); for (let j = 1, len = vertices.length; j < len; ++j) ctx.lineTo(vertices[j].x, vertices[j].y); ctx.lineTo(vertices[0].x, vertices[0].y); const gradient = ctx.createRadialGradient(this.position.x, this.position.y, 15, this.position.x, this.position.y, Math.abs(275 * Math.sin(simulation.cycle / 50)) + 15); // Add three color stops gradient.addColorStop(0, m.eyeFillColor); gradient.addColorStop(0.9, "white"); gradient.addColorStop(1, "darkgray"); ctx.fillStyle = gradient; ctx.strokeStyle = "transparent"; ctx.shadowBlur = 10; ctx.shadowColor = m.eyeFillColor; ctx.fill(); ctx.stroke(); ctx.restore() const Dx = Math.cos(m.angle); const Dy = Math.sin(m.angle); let xElec = this.position.x + 10 * Dx; let yElec = this.position.y + 10 * Dy; ctx.beginPath(); ctx.moveTo(xElec, yElec); const step = 40 for (let i = 0; i < 6; i++) { xElec += step * (Dx + 1.5 * (Math.random() - 0.5)) yElec += step * (Dy + 1.5 * (Math.random() - 0.5)) ctx.lineTo(xElec, yElec); } ctx.strokeStyle = m.eyeFillColor; ctx.lineWidth = 1.5; ctx.setLineDash([70 + 300 * Math.random(), 55 * Math.random()]); ctx.stroke(); // Draw it ctx.setLineDash([]); if (this.alive && m.energy > 0) { const gradient = ctx.createRadialGradient(this.position.x, this.position.y, 0, this.position.x, this.position.y, Math.abs(20 * Math.cos(simulation.cycle / 50))); // Add three color stops gradient.addColorStop(0, m.eyeFillColor); gradient.addColorStop(0.9, "white"); gradient.addColorStop(1, "gray"); ctx.save() ctx.beginPath() ctx.moveTo(this.position.x, this.position.y) ctx.arc(this.position.x, this.position.y, 20, 0, 2 * Math.PI) ctx.fillStyle = gradient; ctx.strokeStyle = "transparent"; ctx.shadowBlur = 10; ctx.shadowColor = m.eyeFillColor; ctx.fill() ctx.stroke() ctx.restore() m.energy -= 0.002; ctx.save() ctx.translate(this.vertices[2].x, this.vertices[2].y) ctx.rotate(m.angle + Math.PI / 2) ctx.beginPath() ctx.font = "16px Arial"; ctx.fillStyle = "black"; ctx.strokeStyle = "black"; // ctx.fillText("Θ", 0,0 - 110) // ctx.fillText("ά", 0,15 - 110) // ctx.fillText("ν", 0,30 - 110) // ctx.fillText("α", 0,45 - 110) // ctx.fillText("τ", 0,60 - 110) // ctx.fillText("ο", 0,75 - 110) // ctx.fillText("ς", 0,90 - 110) ctx.fillText("Ω", 0, 55) ctx.fill() ctx.stroke() ctx.restore() simulation.drawList.push({ x: this.position.x + Math.floor(Math.random() * 300 - Math.random() * 300), y: this.position.y + Math.floor(Math.random() * 300 - Math.random() * 300), radius: 2, color: m.eyeFillColor, time: simulation.drawTime }); } else { this.death() powerUps.activated = false } } } //setTimeout(function() {sword();}, 100); const wire = function () { const breakingPoint = -1600; const spawnx = -13800 + Math.floor(Math.random() * 100 - Math.random() * 100); mobs.spawn(spawnx, -2375, 0, 2, "transparent"); let me = mob[mob.length - 1]; let boss = mob[0]; me.collisionFilter.category = cat.body; me.collisionFilter.mask = cat.map; me.g = 0.0003; //required for gravity me.restitution = 0; me.stroke = "transparent" me.freeOfWires = false; me.frictionAir = 0.01; me.isDropPowerUp = false; me.showHealthBar = false; me.isBadTarget = true; me.isUnblockable = true; const wireX = spawnx; const wireY = -2375; //const randomw = Math.floor(Math.random() * 100 - Math.random() * 100); const width = Math.abs(10 + Math.floor(Math.random() * 10 - Math.random() * 10)); const randomx = Math.floor(30 * Math.random() - 30 * Math.random()); const randomy = Math.floor(10 * Math.random() - 10 * Math.random()) me.do = function () { if (this.freeOfWires) { this.gravity(); } else { if (boss.position.y > breakingPoint) { this.freeOfWires = true; this.force.y -= -0.0006; this.force.x += Math.random() * boss.velocity.x / 10000; this.fill = "#111"; } //move mob to mob Matter.Body.setPosition(this, { x: boss.position.x + randomx, y: boss.position.y + randomy }) } //draw wire ctx.beginPath(); ctx.moveTo(wireX, wireY); ctx.quadraticCurveTo(wireX, -100, this.position.x, this.position.y); ctx.lineWidth = width; ctx.lineCap = "butt"; ctx.strokeStyle = "#111"; ctx.stroke(); ctx.lineCap = "round"; }; } const ball = function (x, y, radius = 11 * tech.bulletSize, sides = 70) {//superball //also, why is it called superballs? mobs.spawn(x, y, sides, radius, "rgba(0,0,0)"); let me = mob[mob.length - 1]; me.stroke = "transparent"; me.onHit = function () { simulation.drawList.push({ //add dmg to draw queue x: this.position.x, y: this.position.y, radius: 20, color: simulation.mobDmgColor, time: simulation.drawTime }); }; Matter.Body.setDensity(me, 0.00001); //normal is 0.001 me.timeLeft = 500; me.friction = 0; me.restitution = 1; me.leaveBody = false; me.isDropPowerUp = false; //me.inertia = Infinity; me.isBadTarget = true; me.isMobBullet = true; me.showHealthBar = false; me.collisionFilter.category = cat.mobBullet; me.collisionFilter.mask = cat.bullet | cat.player | cat.map | cat.body; let index = 0; me.do = function () { this.timeLimit(); this.alwaysSeePlayer() this.force.y += this.mass * 0.0012; } } const normalBullet = function (x, y, radius = 9, sides = 3) { //bullets mobs.spawn(x, y, sides, radius, "rgba(0,0,0)"); let me = mob[mob.length - 1]; me.stroke = "transparent"; me.vertices = Matter.Vertices.rotate(me.vertices, Math.PI, me.position); me.onHit = function () { simulation.drawList.push({ //add dmg to draw queue x: this.position.x, y: this.position.y, radius: 20, color: simulation.mobDmgColor, time: simulation.drawTime }); }; Matter.Body.setDensity(me, 0.00004); //normal is 0.001 me.timeLeft = 220; me.frictionAir = -0.01; me.restitution = -1; me.leaveBody = false; me.isDropPowerUp = false; //me.inertia = Infinity; me.isBadTarget = true; me.isMobBullet = true; me.showHealthBar = false; me.collisionFilter.category = cat.mobBullet; me.collisionFilter.mask = null; let index = 0; me.do = function () { this.timeLimit(); this.alwaysSeePlayer() const setNoseShape = () => { const mag = this.radius + this.radius * 10; this.vertices[1].x = this.position.x + Math.cos(this.angle) * mag; this.vertices[1].y = this.position.y + Math.sin(this.angle) * mag; const angle = Math.atan2(player.position.y - this.position.y, player.position.x - this.position.x); Matter.Body.setAngle(this, angle); }; const spike = Vector.mult(Vector.normalise(Vector.sub(this.vertices[1], this.position)), radius * 1000) this.vertices[1].x = this.position.x + spike.x / 100 this.vertices[1].y = this.position.y + spike.y / 100 if (index == 0) { setNoseShape(); index++; } if (Matter.Query.collides(this, map).length > 0 || Matter.Query.collides(this, body).length > 0) { const slow = 0.69 //im sorry it looks cool though Matter.Body.setVelocity(this, { x: this.velocity.x * slow, y: this.velocity.y * slow }); simulation.drawList.push({ //add dmg to draw queue x: this.position.x, y: this.position.y, radius: 10, color: '#000000', time: simulation.drawTime }); if (this.velocity.x == 0 && this.velocity.y == 0) { this.death(); } this.frictionAir += 0.0001; Matter.Body.setAngularVelocity(this, 0) } if (Matter.Query.collides(this, [player]).length > 0 && !(m.isCloak && tech.isIntangible) && m.immuneCycle < m.cycle) { m.immuneCycle = m.cycle + m.collisionImmuneCycles; //player is immune to damage for 30 cycles // I wasnt gonna add this but since ya'll would have killed me if I didn't I added this const dmg = 0.013 * simulation.dmgScale; m.damage(dmg); simulation.drawList.push({ //add dmg to draw queue x: this.position.x, y: this.position.y, radius: Math.sqrt(dmg) * 200, color: '#000000', time: simulation.drawTime }); } }; } const foamBullet = function (x, y, radius = 9, sides = 70) { //bullets mobs.spawn(x, y, sides, radius, "rgb(0,0,0)"); let me = mob[mob.length - 1]; me.stroke = "transparent"; Matter.Body.setDensity(me, 0.00005); //normal is 0.001 me.timeLeft = 120; // me.g = 0.0005; //required if using this.gravity me.accelMag = 0.00006; me.isVerticesChange = true me.delay = 360 * simulation.CDScale; me.spikeVertex = 0; me.spikeLength = 0; me.isSpikeGrowing = false; me.spikeGrowth = 0; me.isSpikeReset = false; me.frictionAir = 0; me.restitution = 0; me.leaveBody = false; me.isDropPowerUp = false; me.isBadTarget = true; me.isMobBullet = true; me.showHealthBar = false; me.isUnblockable = true; me.collisionFilter.category = cat.mobBullet; me.collisionFilter.mask = cat.body //| cat.bullet;// | cat.player; me.do = function () { if (this.distanceToPlayer2() < 40000) { this.force = Vector.mult(Vector.normalise(Vector.sub(player.position, this.position)), this.mass * 0.004) const slow = 0.99999999999999999; Matter.Body.setVelocity(this, { x: this.velocity.x * slow, y: this.velocity.y * slow }); } // this.gravity(); this.timeLimit(); // for (let i = 0, len = this.vertices.length; i < len; i++) { // const dist = Vector.sub(this.seePlayer.position, this.vertices[i]); // const distMag = Vector.magnitude(dist); // const spike = Vector.mult(Vector.normalise(Vector.sub(this.vertices[i], this.position)), radius * distMag) // this.vertices[i].x = this.position.x + spike.x / 100 // this.vertices[i].y = this.position.y + spike.y / 100 // } if (this.radius < 50) { const scale = 1.05; Matter.Body.scale(this, scale, scale); this.radius *= scale; } if (Matter.Query.collides(this, map).length > 0 || Matter.Query.collides(this, body).length > 0 && this.speed < 10) { const slow = 0.97 Matter.Body.setVelocity(this, { x: this.velocity.x * slow, y: this.velocity.y * slow }); const SCALE = 0.9 Matter.Body.scale(this, SCALE, SCALE); this.radius *= SCALE; if (this.radius < 1) { this.death() } } else { this.attach(); } }; me.attach = function () { if (Matter.Query.collides(this, [player]).length > 0) { Matter.Body.setPosition(this, player.position) if (player.speed > 2.5) Matter.Body.setVelocity(player, Vector.mult(player.velocity, 0.94)) Matter.Body.setAngularVelocity(player, player.angularVelocity * 0.9); m.damage(0.00003); //balanced? not sure } } }; const orbital = function (who, radius, phase, speed, radius2) {//basically orbitBot mobs.spawn(who.position.x, who.position.y, 8, 12, "rgba(0,0,0, 1)"); let me = mob[mob.length - 1]; me.stroke = "transparent"; Matter.Body.setDensity(me, 0.01); //normal is 0.001 me.leaveBody = false; me.isDropPowerUp = false; me.isBadTarget = true; me.showHealthBar = false; me.isOrbital = true; me.isShielded = true me.collisionFilter.category = cat.mobBullet; me.collisionFilter.mask = cat.bullet; //cat.player | cat.map | cat.body me.do = function () { //if host is gone if (!who || !who.alive) { this.death(); return } //set orbit const time = simulation.cycle * speed + phase const orbit = { x: Math.cos(time), y: Math.sin(time) } Matter.Body.setPosition(this, Vector.add(Vector.add(who.position, who.velocity), Vector.mult(orbit, radius + radius2))) //damage player if (Matter.Query.collides(this, [player]).length > 0 && !(m.isCloak && tech.isIntangible) && m.immuneCycle < m.cycle) { m.immuneCycle = m.cycle + m.collisionImmuneCycles; //player is immune to damage for 30 cycles const dmg = 0.013 * simulation.dmgScale m.damage(dmg); simulation.drawList.push({ //add dmg to draw queue x: this.position.x, y: this.position.y, radius: Math.sqrt(dmg) * 200, color: simulation.mobDmgColor, time: simulation.drawTime }); //this.death(); } }; } const sniper = function (x, y, radius = 30) { mobs.spawn(x, y, 8, radius, '#00000000'); let me = mob[mob.length - 1]; me.accelMag = 0.0003 me.stroke = 'transparent'; //me.isBoss = true; me.searchTarget = map[Math.floor(Math.random() * (map.length - 1))].position; me.frictionStatic = 0; me.friction = 0; me.seeAtDistance2 = 20000000 //14000 vision range me.vertices = Matter.Vertices.rotate(me.vertices, Math.PI, me.position); Matter.Body.rotate(me, Math.random() * Math.PI * 2); me.showHealthBar = false Matter.Body.setDensity(me, 0.01) me.fireDir = { x: 0, y: 0 } me.seePlayerFreq = 0 me.repulsionRange = 400000 + radius * radius; me.collisionFilter.mask = cat.bullet | cat.player | cat.body | cat.map | cat.mob | cat.mobBullet me.do = function () { this.seePlayerCheck(); this.attraction(); this.repulsion(); this.search() if (this.seePlayer.recall) { const h = this.radius * 0.3; const w = this.radius * 2; const x = this.position.x - w / 2; const y = this.position.y - w * 0.7; ctx.fillStyle = "rgba(100, 100, 100, 0.3)"; ctx.fillRect(x, y, w, h); ctx.fillStyle = "rgba(0,255,255,0.7)"; ctx.fillRect(x, y, w * this.health, h); } if (this.health < 1) { this.health += 0.0005; //regen } ctx.save() ctx.translate(this.position.x, this.position.y) ctx.rotate(Math.atan2(this.seePlayer.position.y - this.position.y, this.seePlayer.position.x - this.position.x)) if (this.seePlayer.recall > 0 && this.distanceToPlayer2() > this.repulsionRange) { var grd2 = ctx.createLinearGradient(0, 0, -150, 0); // grd2.addColorStop(0, 'rgba(255, 255, 155, 0.8)'); // grd2.addColorStop(1, 'rgba(255, 200, 0, 0.1)'); grd2.addColorStop(0, 'rgba(150, 200, 255, 0.7)'); grd2.addColorStop(1, 'rgba(150, 200, 255, 0)'); ctx.fillStyle = grd2; ctx.beginPath(); ctx.moveTo(-18, -25); //10 * (Math.random() - 0.5), 10 * (Math.random() - 0.5) ctx.lineTo(-18, 25); ctx.lineTo(-50 - 100 * Math.random(), 0); ctx.fill(); } else if (this.distanceToPlayer2() < this.repulsionRange) { var grd2 = ctx.createLinearGradient(0, 0, 80, 0); grd2.addColorStop(0, 'rgba(150, 200, 255, 0.7)'); grd2.addColorStop(1, 'rgba(150, 200, 255, 0)'); ctx.fillStyle = grd2; ctx.beginPath(); ctx.moveTo(20, -16); //10 * (Math.random() - 0.5), 10 * (Math.random() - 0.5) ctx.lineTo(20, 16); ctx.lineTo(35 + 43 * Math.random(), 0); ctx.fill(); } ctx.restore() ctx.save() ctx.translate(this.position.x, this.position.y) ctx.rotate(Math.atan2(this.seePlayer.position.y - this.position.y, this.seePlayer.position.x - this.position.x)) ctx.beginPath(); ctx.arc(0, 0, 30, 0, 2 * Math.PI); ctx.fillStyle = m.bodyGradient ctx.fill(); ctx.arc(15, 0, 4, 0, 2 * Math.PI); ctx.strokeStyle = "#333"; ctx.lineWidth = 2; ctx.stroke(); ctx.beginPath(); //eye ctx.arc(15, 0, 3.5, 0, 2 * Math.PI); ctx.fillStyle = `rgba(255, 0, 0, ${this.health * this.health})`; ctx.fill() ctx.restore() //set direction to turn to fire if (this.seePlayer.recall && !(simulation.cycle % 30)) { this.seePlayer.recall -= 10; this.fireDir = Vector.normalise(Vector.sub(this.seePlayer.position, this.position)); spawn.sniperBullet(this.position.x, this.position.y, 7 + Math.ceil(this.radius / 15), 5); const v = 10 + 8 * simulation.accelScale; Matter.Body.setVelocity(mob[mob.length - 1], { x: this.velocity.x + this.fireDir.x * v, y: this.velocity.y + this.fireDir.y * v }); } }; } const laserEM = function (x, y, radius = 30) { mobs.spawn(x, y, 8, radius, '#00000000'); let me = mob[mob.length - 1]; me.accelMag = 0.0003 me.stroke = 'transparent'; //me.isBoss = true; me.frictionStatic = 0; me.friction = 0; me.seeAtDistance2 = 20000000 //14000 vision range me.vertices = Matter.Vertices.rotate(me.vertices, Math.PI, me.position); Matter.Body.rotate(me, Math.random() * Math.PI * 2); me.showHealthBar = false Matter.Body.setDensity(me, 0.01) me.seePlayerFreq = 0 me.searchTarget = map[Math.floor(Math.random() * (map.length - 1))].position; me.swordDamage = 0.025 * simulation.dmgScale me.collisionFilter.mask = cat.bullet | cat.player | cat.body | cat.map | cat.mob | cat.mobBullet me.repulsionRange = 50000; me.do = function () { this.repulsion(); this.search() if (this.seePlayer.recall) { const h = this.radius * 0.3; const w = this.radius * 2; const x = this.position.x - w / 2; const y = this.position.y - w * 0.7; ctx.fillStyle = "rgba(100, 100, 100, 0.3)"; ctx.fillRect(x, y, w, h); ctx.fillStyle = "rgba(0,255,255,0.7)"; ctx.fillRect(x, y, w * this.health, h); } if (this.health < 1) { this.health += 0.0005; //regen } if (this.seePlayer.recall) { this.laserSword(this.position, Math.atan2(this.seePlayer.position.y - this.position.y, this.seePlayer.position.x - this.position.x), 500 * Math.random()); } ctx.save() ctx.translate(this.position.x, this.position.y) ctx.rotate(Math.atan2(this.seePlayer.position.y - this.position.y, this.seePlayer.position.x - this.position.x)) if (this.seePlayer.recall > 0 && this.distanceToPlayer2() > this.repulsionRange) { var grd2 = ctx.createLinearGradient(0, 0, -150, 0); // grd2.addColorStop(0, 'rgba(255, 255, 155, 0.8)'); // grd2.addColorStop(1, 'rgba(255, 200, 0, 0.1)'); grd2.addColorStop(0, 'rgba(150, 200, 255, 0.7)'); grd2.addColorStop(1, 'rgba(150, 200, 255, 0)'); ctx.fillStyle = grd2; ctx.beginPath(); ctx.moveTo(-18, -25); //10 * (Math.random() - 0.5), 10 * (Math.random() - 0.5) ctx.lineTo(-18, 25); ctx.lineTo(-50 - 100 * Math.random(), 0); ctx.fill(); } else if (this.distanceToPlayer2() < this.repulsionRange) { var grd2 = ctx.createLinearGradient(0, 0, 80, 0); grd2.addColorStop(0, 'rgba(150, 200, 255, 0.7)'); grd2.addColorStop(1, 'rgba(150, 200, 255, 0)'); ctx.fillStyle = grd2; ctx.beginPath(); ctx.moveTo(20, -16); //10 * (Math.random() - 0.5), 10 * (Math.random() - 0.5) ctx.lineTo(20, 16); ctx.lineTo(35 + 43 * Math.random(), 0); ctx.fill(); } ctx.restore() ctx.save() ctx.translate(this.position.x, this.position.y) ctx.rotate(Math.atan2(this.seePlayer.position.y - this.position.y, this.seePlayer.position.x - this.position.x)) ctx.beginPath(); ctx.arc(0, 0, 30, 0, 2 * Math.PI); ctx.fillStyle = m.bodyGradient ctx.fill(); ctx.arc(15, 0, 4, 0, 2 * Math.PI); ctx.strokeStyle = "#333"; ctx.lineWidth = 2; ctx.stroke(); ctx.beginPath(); //eye ctx.arc(15, 0, 3.5, 0, 2 * Math.PI); ctx.fillStyle = `rgba(255, 0, 0, ${this.health * this.health})`; ctx.fill() ctx.restore() this.seePlayerCheck(); this.attraction(); } me.laserSword = function (where, angle, length) { const sub = Vector.sub(this.seePlayer.position, this.position) const unit = Vector.normalise(sub) const path = [{ x: this.position.x + 20 * Math.cos(this.angle), y: this.position.y + 20 * Math.sin(this.angle) }, { x: this.position.x + (120 + 400) * Math.sqrt(Math.random()) * Math.cos(this.angle), y: this.position.y + (120 + 400) * Math.sqrt(Math.random()) * Math.sin(this.angle) } ]; this.seePlayer.recall -= 3; const vertexCollision = function (v1, v1End, domain) { for (let i = 0; i < domain.length; ++i) { let vertices = domain[i].vertices; const len = vertices.length - 1; for (let j = 0; j < len; j++) { results = simulation.checkLineIntersection(v1, v1End, vertices[j], vertices[j + 1]); if (results.onLine1 && results.onLine2) { const dx = v1.x - results.x; const dy = v1.y - results.y; const dist2 = dx * dx + dy * dy; if (dist2 < best.dist2 && (!domain[i].mob || domain[i].alive)) best = { x: results.x, y: results.y, dist2: dist2, who: domain[i], v1: vertices[j], v2: vertices[j + 1] }; } } results = simulation.checkLineIntersection(v1, v1End, vertices[0], vertices[len]); if (results.onLine1 && results.onLine2) { const dx = v1.x - results.x; const dy = v1.y - results.y; const dist2 = dx * dx + dy * dy; if (dist2 < best.dist2) best = { x: results.x, y: results.y, dist2: dist2, who: domain[i], v1: vertices[0], v2: vertices[len] }; } } }; best = { x: null, y: null, dist2: Infinity, who: null, v1: null, v2: null }; const look = { x: where.x + length * Math.cos(angle), y: where.y + length * Math.sin(angle) }; // vertexCollision(where, look, body); // vertexCollision(where, look, mob); vertexCollision(where, look, map); if (!m.isCloak) vertexCollision(where, look, [player]); if (best.who && (best.who === player) && m.immuneCycle < m.cycle) { m.immuneCycle = m.cycle + m.collisionImmuneCycles; //player is immune to damage for an extra second m.damage(this.swordDamage); simulation.drawList.push({ //add dmg to draw queue x: best.x, y: best.y, radius: this.swordDamage * 1500, color: "rgba(80,0,255,0.5)", time: 20 }); } if (best.dist2 === Infinity) best = look; ctx.beginPath(); //draw beam ctx.moveTo(where.x, where.y); ctx.lineTo(best.x, best.y); ctx.strokeStyle = "#50f"; ctx.lineWidth = 1.5; ctx.setLineDash([70 + 300 * Math.random(), 55 * Math.random()]); ctx.stroke(); // Draw it ctx.setLineDash([]); ctx.lineWidth = 20; ctx.strokeStyle = "rgba(80,0,255,0.07)"; ctx.stroke(); // Draw it const Dx = Math.cos(Math.atan2(this.seePlayer.position.y - this.position.y, this.seePlayer.position.x - this.position.x)); const Dy = Math.sin(Math.atan2(this.seePlayer.position.y - this.position.y, this.seePlayer.position.x - this.position.x)); let xElec = this.position.x + 40 * Dx; let yElec = this.position.y + 40 * Dy; ctx.beginPath(); ctx.moveTo(xElec, yElec); const step = 40 for (let i = 0; i < 6; i++) { xElec += step * (Dx + 1.5 * (Math.random() - 0.5)) yElec += step * (Dy + 1.5 * (Math.random() - 0.5)) ctx.lineTo(xElec, yElec); } ctx.strokeStyle = "#50f"; ctx.lineWidth = 1.5; ctx.setLineDash([70 + 300 * Math.random(), 55 * Math.random()]); ctx.stroke(); // Draw it ctx.setLineDash([]); } }; if (powerUps.pass == undefined) { let pass = { pass: true, activated: false }; Object.assign(powerUps, pass) } const loadOut = { loadOut: { name: "loadOut", color: "#000000", //"hsl(248,100%,65%)", size() { return 40 }, effect() { if (m.alive) { // tech.damage *= 2; let text = ""; if (!tech.isSuperDeterminism) { text += `
${tech.isCancelTech ? "?" : "✕"}
`; }; text += `

Blessing Of Sal

`; text += `
  Speed Boost
Increase speed by 5%
`; text += `
  Defense Boost
Reduce damage by 5%
`; text += `
  Damage Boost
Increase damage by 10%
`; if (powerUps.pass == true) { text += `
  Blade of Sal
Press Shift to summon the Mythical Las Slayer
Drains Energy
`; } document.getElementById("choose-grid").innerHTML = text; powerUps.showDraft();//no known bugs ig idk, im keep this as it is } }, choose(index) { if (index == 1) { tech.squirrelFx += 0.25; tech.squirrelJump += 0.1; m.setMovement(); powerUps.endDraft("buff"); } else if (index == 2) { simulation.dmgScale *= 0.95; powerUps.endDraft("buff"); } else if (index == 3) { m.dmgScale *= 1.1; powerUps.endDraft("buff"); } else if (index == 4) { //sword! powerUps.pass = false; addEventListener("keydown", function (event) { if (event.key == "Shift" && powerUps.activated == false) { sword() powerUps.activated = true; } else if (event.key == "Shift" && powerUps.activated == true) { for (let i = 0; i < mob.length; i++) { if (mob[i].isSword) { mob[i].death() } powerUps.activated = false; } } }) powerUps.endDraft("buff"); } } } } Object.assign(powerUps, loadOut) const restoreBoss = function (x, y, radius = 30) { mobs.spawn(x, y, 8, radius, 'transparent'); let me = mob[mob.length - 1]; me.stroke = 'transparent'; let aim = '#FFFFFF'; me.accelMag = 0.0006 me.isBoss = true; me.restoreBoss = true; me.frictionStatic = 0; me.friction = 0; me.seeAtDistance2 = 20000000 //14000 vision range me.vertices = Matter.Vertices.rotate(me.vertices, Math.PI, me.position); //make the pointy side of triangle the front Matter.Body.rotate(me, Math.random() * Math.PI * 2); me.showHealthBar = false Matter.Body.setDensity(me, m.maxHealth / (simulation.difficulty < 5 ? 0.5 : simulation.difficulty / simulation.difficultyMode)) me.seePlayerFreq = 0 me.swordDamage = 0.025 * simulation.dmgScale me.collisionFilter.mask = cat.bullet | cat.player | cat.body | cat.map | cat.mob | cat.mobBullet me.repulsionRange = 500000; me.isDropPowerUp = false; //Matter.Body.setVelocity(me, { x: 10 * (Math.random() - 0.5), y: 10 * (Math.random() - 0.5) }); let index = 0; me.energy = 1; me.maxEnergy = 1; me.immuneCycle = 0; me.cycle = 0; me.onDeath = function () { powerUps.spawn(this.position.x, this.position.y, "loadOut"); } for (let i = 0; i < b.totalBots(); i++) { //normal orbitals look too boring, so... orbital(me, 190 + 130 * tech.isOrbitBotUpgrade, (index / b.totalBots()) * 2 * Math.PI, 0.05, Math.floor(Math.sin(simulation.cycle / 10) * 100)); //who, radius, phase, speed index++; } me.do = function () { this.cycle++; if (this.seePlayer.recall) { //fields if (m.fieldMode == 0 && this.distanceToPlayer2() < 200000) { if (Vector.magnitude(Vector.sub(m.pos, this.position)) - this.radius < m.fieldRange && Matter.Query.ray(map, m.pos, this.position).length === 0) { this.pushM(); } this.drawField(); // this.repel(); } if (m.fieldMode == 2) { if (this.distanceToPlayer2() < 200000) { if (Vector.magnitude(Vector.sub(m.pos, this.position)) - this.radius < m.fieldRange && Matter.Query.ray(map, m.pos, this.position).length === 0) { this.pushM(); } this.drawField() } if (tech.isPerfectBrake) { //cap player and bullet speed around restoreBoss //mobs basically can't hit you when you have this, so to make it fair... const wave = Math.cos(m.cycle * 0.022); const range = 200 + 140 * wave + 150 * m.energy const distance = Vector.magnitude(Vector.sub(this.position, m.pos)) const cap = this.immuneCycle > this.cycle ? 8 : 4 if (distance < range) { if (player.speed > cap && Vector.dot(player.velocity, Vector.sub(this.position, m.pos)) > 0) { // if velocity is directed towards player Matter.Body.setVelocity(player, Vector.mult(Vector.normalise(player.velocity), cap)); //set velocity to cap, but keep the direction } } for (let i = 0; i < bullet.length; i++) { const distance2 = Vector.magnitude(Vector.sub(this.position, bullet[i].position)) if (distance2 < range) { if (bullet[i].speed > cap && Vector.dot(bullet[i].velocity, Vector.sub(this.position, bullet[i].position)) > 0) { // if velocity is directed towards player Matter.Body.setVelocity(bullet[i], Vector.mult(Vector.normalise(bullet[i].velocity), cap)); //set velocity to cap, but keep the direction } } } ctx.beginPath(); ctx.arc(this.position.x, this.position.y, range, 0, 2 * Math.PI); ctx.fillStyle = "rgba(0,0,0,0.08)"; ctx.fill(); } } if (m.fieldMode == 5 && this.distanceToPlayer2() < 200000) { this.laserSword(this.position, Math.atan2(this.seePlayer.position.y - this.position.y, this.seePlayer.position.x - this.position.x), 500 * Math.random()); } if (m.fieldMode == 6) { this.timeAttack(); } if (m.fieldMode == 9) { if (this.distanceToPlayer2() < 300000) { this.teleportAway() //blink but reversed } } } if (m.immuneCycle > m.cycle) { me.damageReduction = 0; } else { me.damageReduction = 1; } this.repulsion(); this.seePlayerCheck(); this.attraction(); this.lostPlayer(); if (this.seePlayer.recall) { const h = this.radius * 0.3; const w = this.radius * 2; const x = this.position.x - w / 2; const y = this.position.y - w * 0.7; ctx.fillStyle = "rgba(10, 10, 10, 0.3)"; ctx.fillRect(x, y, w, h); ctx.fillStyle = "rgba(0,0,0,0.7)"; ctx.fillRect(x, y, w * this.energy, h); } if (this.health < 1) { this.health += 0.0001; //regen } else if (this.health < 1) { this.health += 0.00005; //reduced regen } if (this.energy < 0) {//energy thingy this.energy = 0; } else if (this.energy > this.maxEnergy) { this.energy = this.maxEnergy; } else if (this.energy < this.maxEnergy) { this.energy += 0.001; } ctx.save() ctx.translate(this.position.x, this.position.y) ctx.rotate(Math.atan2(this.seePlayer.position.y - this.position.y, this.seePlayer.position.x - this.position.x)) if (this.seePlayer.recall > 0 && this.distanceToPlayer2() > this.repulsionRange) { ctx.globalAlpha = (this.immuneCycle < this.cycle) ? 1 : 0.5 var grd2 = ctx.createLinearGradient(0, 0, -150, 0); // grd2.addColorStop(0, 'rgba(255, 255, 155, 0.8)'); // grd2.addColorStop(1, 'rgba(255, 200, 0, 0.1)'); grd2.addColorStop(0, 'rgba(150, 200, 255, 0.7)'); grd2.addColorStop(1, 'rgba(150, 200, 255, 0)'); ctx.fillStyle = grd2; ctx.beginPath(); ctx.moveTo(-18, -25); //10 * (Math.random() - 0.5), 10 * (Math.random() - 0.5) ctx.lineTo(-18, 25); ctx.lineTo(-50 - 100 * Math.random(), 0); ctx.fill(); } else if (this.distanceToPlayer2() < this.repulsionRange) { ctx.globalAlpha = (this.immuneCycle < this.cycle) ? 1 : 0.5 var grd2 = ctx.createLinearGradient(0, 0, 80, 0); grd2.addColorStop(0, 'rgba(150, 200, 255, 0.7)'); grd2.addColorStop(1, 'rgba(150, 200, 255, 0)'); ctx.fillStyle = grd2; ctx.beginPath(); ctx.moveTo(20, -16); //10 * (Math.random() - 0.5), 10 * (Math.random() - 0.5) ctx.lineTo(20, 16); ctx.lineTo(35 + 43 * Math.random(), 0); ctx.fill(); } ctx.restore() ctx.save() ctx.globalAlpha = (this.immuneCycle < this.cycle) ? 1 : 0.5 ctx.translate(this.position.x, this.position.y) ctx.rotate(Math.atan2(this.seePlayer.position.y - this.position.y, this.seePlayer.position.x - this.position.x)) if (this.health > 0.5) { ctx.beginPath(); ctx.arc(0, 0, 30, 0, 2 * Math.PI); ctx.fillStyle = m.bodyGradient ctx.fill(); ctx.arc(15, 0, 4, 0, 2 * Math.PI); ctx.strokeStyle = "#333"; ctx.lineWidth = 2; ctx.stroke(); ctx.beginPath(); //eye ctx.arc(15, 0, 3.5, 0, 2 * Math.PI); ctx.fillStyle = `rgba(255, 0, 0, ${this.health * this.health})`; ctx.fill() ctx.restore() } else { ctx.beginPath(); ctx.arc(0, 0, 30, 0, 2 * Math.PI); ctx.fillStyle = m.bodyGradient ctx.fill(); ctx.strokeStyle = "#333"; ctx.lineWidth = 2; if (!(Math.atan2(this.seePlayer.position.y - this.position.y, this.seePlayer.position.x - this.position.x) > -Math.PI / 2 && Math.atan2(this.seePlayer.position.y - this.position.y, this.seePlayer.position.x - this.position.x) < Math.PI / 2)) ctx.scale(1, -1); //here is the flip ctx.stroke(); ctx.beginPath(); ctx.arc(2, -6, 7, 0, 2 * Math.PI); ctx.stroke(); ctx.beginPath(); ctx.arc(25, -6, 7, 0.25 * Math.PI, 1.6 * Math.PI); ctx.stroke(); ctx.beginPath(); ctx.arc(2, -10, 9, 1.25 * Math.PI, 1.75 * Math.PI); ctx.stroke(); ctx.beginPath(); ctx.arc(25, -10, 9, 1.25 * Math.PI, 1.4 * Math.PI); ctx.stroke(); ctx.beginPath(); ctx.arc(18, 13, 10, 0, 2 * Math.PI); ctx.fillStyle = m.bodyGradient; ctx.fill(); ctx.stroke(); ctx.beginPath(); ctx.arc(18, 13, 6, 0, 2 * Math.PI); ctx.fillStyle = "#555"; ctx.fill(); ctx.stroke(); ctx.beginPath(); ctx.arc(3, -6, 3, 0, 2 * Math.PI); ctx.fill(); ctx.stroke(); ctx.beginPath(); ctx.arc(26, -6, 3, 0, 2 * Math.PI); ctx.fill(); ctx.stroke(); ctx.restore(); } if (m.fieldMode == 1) { //render over I think if (this.energy > 0.1) { this.harmonic3Phase(); } } if (m.fieldMode == 3) { const angle = Math.atan2(this.seePlayer.position.y - this.position.y, this.seePlayer.position.x - this.position.x); me.damageReduction = 0.5; me.accelMag = 0.0012; if (!(simulation.cycle % Math.floor(100 + 90 * Math.random() * simulation.CDScale))) { this.diveAttack() } ctx.beginPath(); ctx.arc(this.position.x, this.position.y, 1000, 0, 2 * Math.PI); ctx.fillStyle = "#f5f5ff"; ctx.strokeStyle = "#f5f5ff55"; ctx.setLineDash([125 * Math.random(), 125 * Math.random()]); ctx.globalCompositeOperation = "difference"; ctx.fill(); ctx.globalCompositeOperation = "source-over"; ctx.stroke() ctx.setLineDash([]); } else { me.accelMag = 0.0006; } if (this.immuneCycle > this.cycle) { this.damageReduction = 0; } else { if (m.fieldMode == 3) { this.damageReduction = 0.5; } else { this.damageReduction = 1; } } if (this.seePlayer.recall) { //fields this.gun() } } me.laserSword = function (where, angle, length) { const sub = Vector.sub(this.seePlayer.position, this.position) const unit = Vector.normalise(sub) const path = [{ x: this.position.x + 20 * Math.cos(this.angle), y: this.position.y + 20 * Math.sin(this.angle) }, { x: this.position.x + (120 + 400) * Math.sqrt(Math.random()) * Math.cos(this.angle), y: this.position.y + (120 + 400) * Math.sqrt(Math.random()) * Math.sin(this.angle) } ]; this.seePlayer.recall -= 3; const vertexCollision = function (v1, v1End, domain) { for (let i = 0; i < domain.length; ++i) { let vertices = domain[i].vertices; const len = vertices.length - 1; for (let j = 0; j < len; j++) { results = simulation.checkLineIntersection(v1, v1End, vertices[j], vertices[j + 1]); if (results.onLine1 && results.onLine2) { const dx = v1.x - results.x; const dy = v1.y - results.y; const dist2 = dx * dx + dy * dy; if (dist2 < best.dist2 && (!domain[i].mob || domain[i].alive)) best = { x: results.x, y: results.y, dist2: dist2, who: domain[i], v1: vertices[j], v2: vertices[j + 1] }; } } results = simulation.checkLineIntersection(v1, v1End, vertices[0], vertices[len]); if (results.onLine1 && results.onLine2) { const dx = v1.x - results.x; const dy = v1.y - results.y; const dist2 = dx * dx + dy * dy; if (dist2 < best.dist2) best = { x: results.x, y: results.y, dist2: dist2, who: domain[i], v1: vertices[0], v2: vertices[len] }; } } }; best = { x: null, y: null, dist2: Infinity, who: null, v1: null, v2: null }; const look = { x: where.x + length * Math.cos(angle), y: where.y + length * Math.sin(angle) }; // vertexCollision(where, look, body); // vertexCollision(where, look, mob); vertexCollision(where, look, map); if (!m.isCloak) vertexCollision(where, look, [player]); if (best.who && (best.who === player) && m.immuneCycle < m.cycle) { m.immuneCycle = m.cycle + m.collisionImmuneCycles; //player is immune to damage for an extra second m.damage(this.swordDamage); simulation.drawList.push({ //add dmg to draw queue x: best.x, y: best.y, radius: this.swordDamage * 1500, color: "rgba(0,0,0,0.5)", time: 20 }); } if (best.dist2 === Infinity) best = look; ctx.beginPath(); //draw beam ctx.moveTo(where.x, where.y); ctx.lineTo(best.x, best.y); ctx.strokeStyle = "#000"; ctx.lineWidth = 1.5; ctx.setLineDash([70 + 300 * Math.random(), 55 * Math.random()]); ctx.stroke(); // Draw it ctx.setLineDash([]); ctx.lineWidth = 20; ctx.strokeStyle = "rgba(0,0,0,0.07)"; ctx.stroke(); // Draw it const Dx = Math.cos(Math.atan2(this.seePlayer.position.y - this.position.y, this.seePlayer.position.x - this.position.x)); const Dy = Math.sin(Math.atan2(this.seePlayer.position.y - this.position.y, this.seePlayer.position.x - this.position.x)); let xElec = this.position.x + 40 * Dx; let yElec = this.position.y + 40 * Dy; ctx.beginPath(); ctx.moveTo(xElec, yElec); const step = 40 for (let i = 0; i < 6; i++) { xElec += step * (Dx + 1.5 * (Math.random() - 0.5)) yElec += step * (Dy + 1.5 * (Math.random() - 0.5)) ctx.lineTo(xElec, yElec); } ctx.strokeStyle = "#000"; ctx.lineWidth = 1.5; ctx.setLineDash([70 + 300 * Math.random(), 55 * Math.random()]); ctx.stroke(); // Draw it ctx.setLineDash([]); } me.drawField = function () { if (m.fieldMode != 2) { const angle = Math.atan2(this.seePlayer.position.y - this.position.y, this.seePlayer.position.x - this.position.x) const range = m.fieldRange; ctx.beginPath(); ctx.arc(this.position.x, this.position.y, range, angle - Math.PI * m.fieldArc, angle + Math.PI * m.fieldArc, false); ctx.lineWidth = 2; ctx.stroke(); let eye = 13; if (m.fieldMode == 2) { eye = 30 } let aMag = 0.75 * Math.PI * m.fieldArc let a = angle + aMag let cp1x = this.position.x + 0.6 * range * Math.cos(a) let cp1y = this.position.y + 0.6 * range * Math.sin(a) ctx.quadraticCurveTo(cp1x, cp1y, this.position.x + eye * Math.cos(angle), this.position.y + eye * Math.sin(angle)) a = angle - aMag cp1x = this.position.x + 0.6 * range * Math.cos(a) cp1y = this.position.y + 0.6 * range * Math.sin(a) ctx.quadraticCurveTo(cp1x, cp1y, this.position.x + 1 * range * Math.cos(angle - Math.PI * m.fieldArc), this.position.y + 1 * range * Math.sin(angle - Math.PI * m.fieldArc)) ctx.fill(); // ctx.lineTo(this.position.x + eye * Math.cos(angle), this.position.y + eye * Math.sin(angle)); //draw random lines in field for cool effect let offAngle = angle + 1.7 * Math.PI * m.fieldArc * (Math.random() - 0.5); ctx.beginPath(); eye = 15; ctx.moveTo(this.position.x + eye * Math.cos(angle), this.position.y + eye * Math.sin(angle)); ctx.lineTo(this.position.x + range * Math.cos(offAngle), this.position.y + range * Math.sin(offAngle)); ctx.strokeStyle = "rgba(0,0,0,0.6)"; ctx.lineWidth = 1; ctx.stroke(); } else { ctx.beginPath(); const wave = Math.cos(m.cycle * 0.022); const angle = Math.atan2(this.seePlayer.position.y - this.position.y, this.seePlayer.position.x - this.position.x) ctx.arc(this.position.x, this.position.y, m.fieldRange, angle - Math.PI * m.fieldArc, angle + Math.PI * m.fieldArc, false); ctx.lineWidth = 2.5 - 1.5 * wave; ctx.stroke(); const curve = 0.57 + 0.04 * wave const aMag = (1 - curve * 1.2) * Math.PI * m.fieldArc let a = angle + aMag let cp1x = this.position.x + curve * m.fieldRange * Math.cos(a) let cp1y = this.position.y + curve * m.fieldRange * Math.sin(a) ctx.quadraticCurveTo(cp1x, cp1y, this.position.x + 30 * Math.cos(angle), this.position.y + 30 * Math.sin(angle)) a = angle - aMag cp1x = this.position.x + curve * m.fieldRange * Math.cos(a) cp1y = this.position.y + curve * m.fieldRange * Math.sin(a) ctx.quadraticCurveTo(cp1x, cp1y, this.position.x + 1 * m.fieldRange * Math.cos(angle - Math.PI * m.fieldArc), this.position.y + 1 * m.fieldRange * Math.sin(angle - Math.PI * m.fieldArc)) ctx.fill(); } } me.pushM = function () { const unit = Vector.normalise(Vector.sub(this.position, player.position)) if (tech.blockDmg) { Matter.Body.setVelocity(player, { x: 0.5 * player.velocity.x, y: 0.5 * player.velocity.y }); //draw electricity const step = 40 ctx.beginPath(); for (let i = 0, len = 0.8 * tech.blockDmg; i < len; i++) { let x = this.position.x - 20 * unit.x; let y = this.position.y - 20 * unit.y; ctx.moveTo(x, y); for (let i = 0; i < 8; i++) { x += step * (-unit.x + 1.5 * (Math.random() - 0.5)) y += step * (-unit.y + 1.5 * (Math.random() - 0.5)) ctx.lineTo(x, y); } } if (m.immuneCycle < m.cycle) { m.immuneCycle = m.cycle + m.collisionImmuneCycles m.damage(0.025 * simulation.dmgScale) } ctx.lineWidth = 3; ctx.strokeStyle = "#000"; ctx.stroke(); } const massRoot = Math.sqrt(Math.min(12, Math.max(0.15, player.mass))); // masses above 12 can start to overcome the push back //idk Matter.Body.setVelocity(player, { x: this.velocity.x - (15 * unit.x) / massRoot, y: this.velocity.y - (15 * unit.y) / massRoot }); } me.diveAttack = function () { const forceMag = this.accelMag * this.mass; const angle = Math.atan2(this.seePlayer.position.y - this.position.y, this.seePlayer.position.x - this.position.x); this.force.x += 150 * forceMag * Math.cos(angle); this.force.y += 150 * forceMag * Math.sin(angle); ctx.beginPath() ctx.moveTo(this.position.x + Math.sin(angle), this.position.y + Math.cos(angle)) ctx.lineTo(this.seePlayer.position.x, this.seePlayer.position.y) aim = '#000000'; ctx.stroke() } me.teleportAway = function () {//hehe ctx.beginPath(); ctx.moveTo(this.position.x, this.position.y); if (this.seePlayer.recall && !(simulation.cycle % 17)) { const dist = Vector.sub(this.position, this.seePlayer.position); const distMag = Vector.magnitude(dist); const unitVector = Vector.normalise(dist); const rando = (Math.random() - 0.5) * 50; if (distMag < 20000) { Matter.Body.translate(this, Vector.mult(unitVector, distMag + rando)); } else { Matter.Body.translate(this, Vector.mult(unitVector, 20000 + rando)); } } ctx.lineTo(this.position.x, this.position.y); ctx.lineWidth = radius * 2; ctx.strokeStyle = "rgba(0,0,0,0.08)"; ctx.stroke(); if (!this.seePlayer.yes) { ctx.beginPath(); ctx.moveTo(this.position.x, this.position.y); if (this.seePlayer.recall && !(simulation.cycle % 17)) { const dist = Vector.sub(this.seePlayer.position, this.position); const distMag = Vector.magnitude(dist); const unitVector = Vector.normalise(dist); const rando = (Math.random() - 0.5) * 50; if (distMag < 200000) { Matter.Body.translate(this, Vector.mult(unitVector, distMag + rando)); } else { Matter.Body.translate(this, Vector.mult(unitVector, 200000 + rando)); } } ctx.lineTo(this.position.x, this.position.y); ctx.lineWidth = radius * 2; ctx.strokeStyle = "rgba(0,0,0,0.08)"; ctx.stroke(); } } me.timeAttack = function () { if (!simulation.isTimeSkipping || input.field) { requestAnimationFrame(() => { simulation.timePlayerSkip(5) m.walk_cycle += m.flipLegs * m.Vx * 5 }); //just doing what lilgreenland did } } me.harmonic3Phase = function () { //normal standard 3 different 2-d circles if (tech.harmonics === 2) { const fieldRange1 = (0.75 + 0.3 * Math.cos(m.cycle / 23)) * m.fieldRange * m.harmonicRadius const fieldRange2 = (0.68 + 0.37 * Math.cos(m.cycle / 37)) * m.fieldRange * m.harmonicRadius const fieldRange3 = (0.7 + 0.35 * Math.cos(m.cycle / 47)) * m.fieldRange * m.harmonicRadius const netfieldRange = Math.max(fieldRange1, fieldRange2, fieldRange3) ctx.fillStyle = "rgba(0,0,0," + Math.min(0.6, (0.04 + m.energy * (0.1 + 0.11 * Math.random()))) + ")"; ctx.beginPath(); ctx.arc(this.position.x, this.position.y, fieldRange1, 0, 2 * Math.PI); ctx.fill(); ctx.beginPath(); ctx.arc(this.position.x, this.position.y, fieldRange2, 0, 2 * Math.PI); ctx.fill(); ctx.beginPath(); ctx.arc(this.position.x, this.position.y, fieldRange3, 0, 2 * Math.PI); ctx.fill(); //360 block if (Vector.magnitude(Vector.sub(player.position, this.position)) - this.radius < netfieldRange) { me.pushM(); } for (let i = 0; i < bullet.length; i++) { if (Vector.magnitude(Vector.sub(bullet[i].position, this.position)) - this.radius < netfieldRange) { const dx = bullet[i].position.x - this.position.x; const dy = bullet[i].position.y - this.position.y; const dist = Math.sqrt(dx * dx + dy * dy); if (dist < m.fieldRange) { const angle = Math.atan2(dy, dx); const mag = (1500 * bullet[i].mass * simulation.g) / dist; bullet[i].force.x += mag * Math.cos(angle); bullet[i].force.y += mag * Math.sin(angle); } this.energy -= 0.0012; } } } else { const rotation = simulation.cycle * 0.0031 const phase = simulation.cycle * 0.023 const radius = m.fieldRange * m.harmonicRadius ctx.lineWidth = 1; ctx.strokeStyle = "rgba(0,0,0,0.5)" ctx.fillStyle = `rgba(0,0,0,${Math.min(0.6, m.energy * (0.11 + 0.1 * Math.random()) * (3 / tech.harmonics))})`; // ctx.fillStyle = "rgba(0,0,0," + Math.min(0.7, m.energy * (0.22 - 0.01 * tech.harmonics) * (0.5 + 0.5 * Math.random())) + ")"; for (let i = 0; i < tech.harmonics; i++) { ctx.beginPath(); ctx.ellipse(this.position.x, this.position.y, radius * Math.abs(Math.sin(phase + i / tech.harmonics * Math.PI)), radius, rotation + i / tech.harmonics * Math.PI, 0, 2 * Math.PI); ctx.fill(); ctx.stroke(); } //360 block if (Vector.magnitude(Vector.sub(player.position, this.position)) - this.radius < radius) { me.pushM(); } for (let i = 0; i < bullet.length; i++) { if (Vector.magnitude(Vector.sub(bullet[i].position, this.position)) - this.radius < radius) { const dx = bullet[i].position.x - this.position.x; const dy = bullet[i].position.y - this.position.y; const dist = Math.sqrt(dx * dx + dy * dy); if (dist < m.fieldRange) { const angle = Math.atan2(dy, dx); const mag = (1500 * bullet[i].mass * simulation.g) / dist; bullet[i].force.x += mag * Math.cos(angle); bullet[i].force.y += mag * Math.sin(angle); } this.energy -= 0.0012; } } } } me.railGun = function () { const angle = Math.atan2(this.seePlayer.position.y - this.position.y, this.seePlayer.position.x - this.position.x); const X = this.position.x const Y = this.position.y const unitVector = { x: Math.cos(angle), y: Math.sin(angle) } const unitVectorPerp = Vector.perp(unitVector) function magField(mag, arc) { ctx.moveTo(X, Y); ctx.bezierCurveTo( X + unitVector.x * mag, Y + unitVector.y * mag, X + unitVector.x * mag + unitVectorPerp.x * arc, Y + unitVector.y * mag + unitVectorPerp.y * arc, X + unitVectorPerp.x * arc, Y + unitVectorPerp.y * arc) ctx.bezierCurveTo( X - unitVector.x * mag + unitVectorPerp.x * arc, Y - unitVector.y * mag + unitVectorPerp.y * arc, X - unitVector.x * mag, Y - unitVector.y * mag, X, Y) } ctx.fillStyle = `rgba(50,0,100,0.05)`; const magSize = 8 * c * tech.railChargeRate ** 3 const arcSize = 6 * c * tech.railChargeRate ** 3 for (let i = 3; i < 7; i++) { const MAG = magSize * i * i * (0.93 + 0.07 * Math.random()) const ARC = arcSize * i * i * (0.93 + 0.07 * Math.random()) ctx.beginPath(); magField(MAG, ARC) magField(MAG, -ARC) ctx.fill(); } } me.waves = []; me.doLongitudinal = function () { if (!m.isBodiesAsleep) { ctx.strokeStyle = "rgba(0,0,0,0.6)" //"000"; ctx.lineWidth = 2 * tech.wavePacketDamage ctx.beginPath(); // const end = 1100 * tech.bulletsLastLonger / Math.sqrt(tech.waveReflections * 0.5) //should equal about 1767 const end = 1100 * tech.bulletsLastLonger * Math.pow(0.93, tech.waveReflections) //should equal about 1767 const damage = 0.0005 * simulation.dmgScale//normal damage for m basically shreds m, so had to nerf this for (let i = this.waves.length - 1; i > -1; i--) { const v1 = Vector.add(this.waves[i].position, Vector.mult(this.waves[i].unit1, this.waves[i].radius)) const v2 = Vector.add(this.waves[i].position, Vector.mult(this.waves[i].unit2, this.waves[i].radius)) //draw wave ctx.moveTo(v1.x, v1.y) ctx.arc(this.waves[i].position.x, this.waves[i].position.y, this.waves[i].radius, this.waves[i].angle, this.waves[i].angle + this.waves[i].arc); //using small angle linear approximation of circle arc, this will not work if the arc gets large // https://stackoverflow.com/questions/13652518/efficiently-find-points-inside-a-circle-sector let hits = Matter.Query.ray([player], v1, v2, 50) //Matter.Query.ray(bodies, startPoint, endPoint, [rayWidth]) for (let j = 0, len = Math.min(30, hits.length); j < len; j++) { player.force.x += 0.01 * (Math.random() - 0.5) * player.mass player.force.y += (0.01 * (Math.random() - 0.5) - simulation.g * 0.25) * player.mass //remove force of gravity Matter.Body.setVelocity(player, { //friction x: player.velocity.x * 0.95, y: player.velocity.y * 0.95 }); m.damage(damage) } hits = Matter.Query.ray(body, v1, v2, 50) for (let j = 0, len = Math.min(30, hits.length); j < len; j++) { const who = hits[j].body //make them shake around who.force.x += 0.01 * (Math.random() - 0.5) * who.mass who.force.y += (0.01 * (Math.random() - 0.5) - simulation.g * 0.25) * who.mass //remove force of gravity let vertices = who.vertices; const vibe = 25 ctx.moveTo(vertices[0].x + vibe * (Math.random() - 0.5), vertices[0].y + vibe * (Math.random() - 0.5)); for (let j = 1; j < vertices.length; j++) { ctx.lineTo(vertices[j].x + vibe * (Math.random() - 0.5), vertices[j].y + vibe * (Math.random() - 0.5)); } ctx.lineTo(vertices[0].x + vibe * (Math.random() - 0.5), vertices[0].y + vibe * (Math.random() - 0.5)); if (tech.isPhononBlock && !who.isNotHoldable && who.speed < 5 && who.angularSpeed < 0.1) { if (Math.random() < 0.5) b.targetedBlock(who, 50 - Math.min(25, who.mass * 3)) // targetedBlock(who, speed = 50 - Math.min(20, who.mass * 2), range = 1600) { // Matter.Body.setAngularVelocity(who, (0.25 + 0.12 * Math.random()) * (Math.random() < 0.5 ? -1 : 1)); who.torque += who.inertia * 0.001 * (Math.random() - 0.5) } } // ctx.stroke(); //draw vibes this.waves[i].radius += tech.waveBeamSpeed * 1.8 * this.waves[i].expanding //expand / move if (this.waves[i].radius > end - 30) { this.waves[i].expanding = -1 if (this.waves[i].reflection < 1) this.waves.splice(i, 1) //end } else if (this.waves[i].radius < 25) { this.waves[i].expanding = 1 if (this.waves[i].reflection < 1) this.waves.splice(i, 1) //end } } ctx.stroke(); } } me.lasers = function (where, angle) { const vertexCollision = function (v1, v1End, domain) { for (let i = 0; i < domain.length; ++i) { let vertices = domain[i].vertices; const len = vertices.length - 1; for (let j = 0; j < len; j++) { results = simulation.checkLineIntersection(v1, v1End, vertices[j], vertices[j + 1]); if (results.onLine1 && results.onLine2) { const dx = v1.x - results.x; const dy = v1.y - results.y; const dist2 = dx * dx + dy * dy; if (dist2 < best.dist2 && (!domain[i].mob || domain[i].alive)) best = { x: results.x, y: results.y, dist2: dist2, who: domain[i], v1: vertices[j], v2: vertices[j + 1] }; } } results = simulation.checkLineIntersection(v1, v1End, vertices[0], vertices[len]); if (results.onLine1 && results.onLine2) { const dx = v1.x - results.x; const dy = v1.y - results.y; const dist2 = dx * dx + dy * dy; if (dist2 < best.dist2) best = { x: results.x, y: results.y, dist2: dist2, who: domain[i], v1: vertices[0], v2: vertices[len] }; } } }; const seeRange = 7000; best = { x: null, y: null, dist2: Infinity, who: null, v1: null, v2: null }; const look = { x: where.x + seeRange * Math.cos(angle), y: where.y + seeRange * Math.sin(angle) }; // vertexCollision(where, look, mob); vertexCollision(where, look, map); vertexCollision(where, look, body); if (!m.isCloak) vertexCollision(where, look, [player]); if (best.who && (best.who === player) && m.immuneCycle < m.cycle) { const dmg = 0.0011 * simulation.dmgScale; m.damage(dmg); simulation.drawList.push({ //add dmg to draw queue x: best.x, y: best.y, radius: dmg * 1500, color: "rgba(80,0,255,0.5)", time: 20 }); } //draw beam if (best.dist2 === Infinity) best = look; ctx.moveTo(where.x, where.y); ctx.lineTo(best.x, best.y); ctx.lineWidth = 10; ctx.stroke(); } me.pulse = function (charge, angle, where = this.position) { let best; angle = Math.atan2(this.seePlayer.position.y - this.position.y, this.seePlayer.position.x - this.position.x) let explosionRadius = 5.5 * charge let range = 5000 const path = [{ x: where.x + 20 * Math.cos(angle), y: where.y + 20 * Math.sin(angle) }, { x: where.x + range * Math.cos(angle), y: where.y + range * Math.sin(angle) } ]; const vertexCollision = function (v1, v1End, domain) { for (let i = 0; i < domain.length; ++i) { let vertices = domain[i].vertices; const len = vertices.length - 1; for (let j = 0; j < len; j++) { results = simulation.checkLineIntersection(v1, v1End, vertices[j], vertices[j + 1]); if (results.onLine1 && results.onLine2) { const dx = v1.x - results.x; const dy = v1.y - results.y; const dist2 = dx * dx + dy * dy; if (dist2 < best.dist2 && (!domain[i].mob || domain[i].alive)) best = { x: results.x, y: results.y, dist2: dist2, who: domain[i], v1: vertices[j], v2: vertices[j + 1] }; } } results = simulation.checkLineIntersection(v1, v1End, vertices[0], vertices[len]); if (results.onLine1 && results.onLine2) { const dx = v1.x - results.x; const dy = v1.y - results.y; const dist2 = dx * dx + dy * dy; if (dist2 < best.dist2) best = { x: results.x, y: results.y, dist2: dist2, who: domain[i], v1: vertices[0], v2: vertices[len] }; } } }; //check for collisions best = { x: null, y: null, dist2: Infinity, who: null, v1: null, v2: null }; if (!best.who) { vertexCollision(path[0], path[1], body); vertexCollision(path[0], path[1], [player]); vertexCollision(path[0], path[1], map); if (best.dist2 != Infinity) { //if hitting something path[path.length - 1] = { x: best.x, y: best.y }; } } if (best.who) { b.explosion(path[1], explosionRadius, "rgba(0,0,0,0)") const off = explosionRadius * 1.2 b.explosion({ x: path[1].x + off * (Math.random() - 0.5), y: path[1].y + off * (Math.random() - 0.5) }, explosionRadius, "rgba(0,0,0,0.7)") b.explosion({ x: path[1].x + off * (Math.random() - 0.5), y: path[1].y + off * (Math.random() - 0.5) }, explosionRadius, "rgba(0,0,0,0.7)") } //draw laser beam ctx.beginPath(); ctx.moveTo(path[0].x, path[0].y); ctx.lineTo(path[1].x, path[1].y); if (charge > 50) { ctx.strokeStyle = "rgba(0,0,0,0.10)" ctx.lineWidth = 70 ctx.stroke(); } ctx.strokeStyle = "rgba(0,0,0,0.25)" ctx.lineWidth = 20 ctx.stroke(); ctx.strokeStyle = "#f00"; ctx.lineWidth = 4 ctx.stroke(); //draw little dots along the laser path const sub = Vector.sub(path[1], path[0]) const mag = Vector.magnitude(sub) for (let i = 0, len = Math.floor(mag * 0.0005 * charge); i < len; i++) { const dist = Math.random() simulation.drawList.push({ x: path[0].x + sub.x * dist + 10 * (Math.random() - 0.5), y: path[0].y + sub.y * dist + 10 * (Math.random() - 0.5), radius: 1.5 + 5 * Math.random(), color: "rgba(0,0,0,0.5)", time: Math.floor(9 + 25 * Math.random() * Math.random()) }); } } let c = 0 me.gun = function () { if (b.activeGun == 0) {// nailgun if (this.seePlayer.recall && !(simulation.cycle % 20)) { this.fireDir = Vector.normalise(Vector.sub(this.seePlayer.position, this.position)); normalBullet(this.position.x, this.position.y); const v = 10 + 8 * simulation.accelScale; Matter.Body.setVelocity(mob[mob.length - 1], { x: this.velocity.x + this.fireDir.x * v, y: this.velocity.y + this.fireDir.y * v }); } } if (b.activeGun == 1) {// shotgun if (this.seePlayer.recall && !(simulation.cycle % 90)) { const side = 22 for (let i = 0; i < 12; i++) { const angle = Math.atan2(this.seePlayer.position.y - this.position.y, this.seePlayer.position.x - this.position.x); const dir = angle + (Math.random() - 0.5) * 1 const SPEED = 52 + Math.random() * 8 normalBullet(this.position.x + 35 * Math.cos(angle) + 15 * (Math.random() - 0.5), this.position.y + 35 * Math.sin(angle) + 15 * (Math.random() - 0.5)) Matter.Body.setVelocity(mob[mob.length - 1], { x: SPEED * Math.cos(dir), y: SPEED * Math.sin(dir) }); } } } else if (b.activeGun == 2) { // super balls if (this.seePlayer.recall && !(simulation.cycle % 20)) { const num = 3; const SPREAD = 0.13; const angle = Math.atan2(this.seePlayer.position.y - this.position.y, this.seePlayer.position.x - this.position.x); let dir = angle - SPREAD * (num - 1) / 2; const SPEED = 33 for (let i = 0; i < num; i++) { ball(this.position.x + 30 * Math.cos(angle), this.position.y + 30 * Math.sin(angle)) Matter.Body.setVelocity(mob[mob.length - 1], { x: SPEED * Math.cos(dir), y: SPEED * Math.sin(dir) }); dir += SPREAD } } } else if (b.activeGun == 3) { // wave this.doLongitudinal() const halfArc = 0.275 const anglex = Math.atan2(this.seePlayer.position.y - this.position.y, this.seePlayer.position.x - this.position.x); const angle = anglex + 0.3 * (Math.random() - 0.5) this.waves.push({ position: { x: this.position.x + 25 * Math.cos(anglex), y: this.position.y + 25 * Math.sin(anglex), }, angle: angle - halfArc, //used in drawing ctx.arc unit1: { x: Math.cos(angle - halfArc), y: Math.sin(angle - halfArc) }, //used for collision unit2: { x: Math.cos(angle + halfArc), y: Math.sin(angle + halfArc) }, //used for collision arc: halfArc * 2, radius: 25, reflection: 0, expanding: 1, resonanceCount: 0 }) } else if (b.activeGun == 4) { // missiles } else if (b.activeGun == 5) { // grenades } else if (b.activeGun == 6) { // spores if (this.seePlayer.recall && !(simulation.cycle % 30)) { this.fireDir = Vector.normalise(Vector.sub(this.seePlayer.position, this.position)); me.drop(this.position.x, this.position.y) const v = 10 + 8 * simulation.accelScale + (Math.random() * 20 - Math.random() * 20); Matter.Body.setVelocity(mob[mob.length - 1], { x: this.velocity.x + this.fireDir.x * v, y: this.velocity.y + this.fireDir.y * v }); } } else if (b.activeGun == 7) { // drones } else if (b.activeGun == 8) { // foam if (this.seePlayer.recall && !(simulation.cycle % 1)) { this.fireDir = Vector.normalise(Vector.sub(this.seePlayer.position, this.position)); foamBullet(this.position.x, this.position.y, 7 + Math.ceil(this.radius / 15), 69); const v = 10 + 8 * simulation.accelScale + (Math.random() * 20 - Math.random() * 20); Matter.Body.setVelocity(mob[mob.length - 1], { x: this.velocity.x + this.fireDir.x * v, y: this.velocity.y + this.fireDir.y * v }); } } else if (b.activeGun == 9) { // harpoon - railgun if (c > 1) { this.fireDir = Vector.normalise(Vector.sub(this.seePlayer.position, this.position)); this.railBullet(this.position.x, this.position.y); const v = 10 + 80 * simulation.accelScale; Matter.Body.setVelocity(mob[mob.length - 1], { x: this.velocity.x + this.fireDir.x * v, y: this.velocity.y + this.fireDir.y * v }); c = 0; } else { c += 0.02; this.railGun(); } } else if (b.activeGun == 10) { // laserMines if (this.seePlayer.recall && !(simulation.cycle % 100)) { this.fireDir = Vector.normalise(Vector.sub(this.seePlayer.position, this.position)); me.laserMine(this.position.x, this.position.y) const v = 10 + 8 * simulation.accelScale + (Math.random() * 20 - Math.random() * 20); Matter.Body.setVelocity(mob[mob.length - 1], { x: this.velocity.x + this.fireDir.x * v, y: this.velocity.y + this.fireDir.y * v }); } } else if (b.activeGun == 11) { // laser - pulse //this.lasers(this.position, Math.atan2(this.seePlayer.position.y - this.position.y, this.seePlayer.position.x - this.position.x)) //if (this.seePlayer.recall && !(simulation.cycle % 20)) { if (c > 1) { this.pulse(c * 100) c = 0; } else { if (this.energy < 1 || this.energy > 0.5) { c += 0.01; ctx.beginPath(); const mag = Math.sqrt(c) ctx.arc(this.position.x, this.position.y, c * 30, 0, 2 * Math.PI) ctx.fillStyle = '#000000' ctx.strokeStyle = 'transparent' ctx.fill(); ctx.stroke(); this.energy -= 0.01; ctx.strokeStyle = "#000000"; ctx.lineWidth = 1.5 // ctx.globalAlpha = 1; } else { c = 0; this.energy += 0.1 } } //} } } me.railBullet = function (x, y) { mobs.spawn(x, y, 5, 20, "black"); let xy = mob[mob.length - 1]; xy.stroke = "black"; xy.vertices = Matter.Vertices.rotate(xy.vertices, Math.PI, xy.position); xy.onHit = function () { simulation.drawList.push({ //add dmg to draw queue x: this.position.x, y: this.position.y, radius: 20, color: simulation.mobDmgColor, time: simulation.drawTime }); }; Matter.Body.setDensity(xy, 0.00004); //normal is 0.001 xy.timeLeft = 220; xy.frictionAir = -0.01; xy.restitution = -1; xy.leaveBody = false; xy.isDropPowerUp = false; //xy.inertia = Infinity; xy.isBadTarget = true; xy.isMobBullet = true; xy.showHealthBar = false; xy.collisionFilter.category = cat.mobBullet; xy.collisionFilter.mask = cat.player; let index = 0; xy.do = function () { this.timeLimit(); this.alwaysSeePlayer() const setNoseShape = () => { const mag = this.radius + this.radius * 10; const angle = Math.atan2(me.seePlayer.position.y - this.position.y, me.seePlayer.position.x - this.position.x); this.vertices[2].x = this.position.x + Math.cos(this.angle) * mag; this.vertices[2].y = this.position.y + Math.sin(this.angle) * mag; this.vertices[4].x = this.position.x + Math.cos(this.angle) * mag; this.vertices[4].y = this.position.y + Math.sin(this.angle) * mag; this.vertices[0].x = this.position.x + Math.cos(this.angle) * mag; this.vertices[0].y = this.position.y + Math.sin(this.angle) * mag; Matter.Body.setAngle(this, angle); }; const spike = Vector.mult(Vector.normalise(Vector.sub(this.vertices[2], this.position)), radius * 1000) const spike2 = Vector.mult(Vector.normalise(Vector.sub(this.vertices[4], this.position)), radius * 1000) const spike3 = Vector.mult(Vector.normalise(Vector.sub(this.vertices[0], this.position)), radius * 1000) this.vertices[2].x = this.position.x + spike.x / 100 this.vertices[2].y = this.position.y + spike.y / 100 this.vertices[4].x = this.position.x + spike2.x / 75 this.vertices[4].y = this.position.y + spike2.y / 75 this.vertices[0].x = this.position.x + spike3.x / 75 this.vertices[0].y = this.position.y + spike3.y / 75 if (index == 0) { setNoseShape(); index++; } if (Matter.Query.collides(this, map).length > 0 || Matter.Query.collides(this, body).length > 0) { const slow = 0.69 //im sorry it looks cool though Matter.Body.setVelocity(this, { x: this.velocity.x * slow, y: this.velocity.y * slow }); // simulation.drawList.push({ //add dmg to draw queue // x: this.position.x, // y: this.position.y, // radius: 10, // color: '#000000', // time: simulation.drawTime // }); if (this.velocity.x == 0 && this.velocity.y == 0) { this.death(); } this.frictionAir += 0.0001; Matter.Body.setAngularVelocity(this, 0) } if (Matter.Query.collides(this, [player]).length > 0 && !(m.isCloak && tech.isIntangible) && m.immuneCycle < m.cycle) { m.immuneCycle = m.cycle + m.collisionImmuneCycles; //player is immune to damage for 30 cycles // I wasnt gonna add this but since ya'll would have killed me if I didn't I added this const dmg = 0.013 * simulation.dmgScale; m.damage(dmg); // simulation.drawList.push({ //add dmg to draw queue // x: this.position.x, // y: this.position.y, // radius: Math.sqrt(dmg) * 200, // color: '#000000', // time: simulation.drawTime // }); } }; } me.laserMine = function (x, y) { mobs.spawn(x, y, 3, 20, "#000000"); let xx = mob[mob.length - 1]; xx.stroke = "#00000000"; Matter.Body.setDensity(xx, 0.000005) //one tap xx.isUnstable = true; xx.timeLeft = 40 + Math.floor(180 * Math.random()) xx.leaveBody = false; xx.isDropPowerUp = false; xx.collisionFilter.mask = cat.bullet | cat.player xx.showHealthBar = false; //xx.vertices = Matter.Vertices.rotate(xx.vertices, Math.PI, xx.position); me.onHit = function () { this.death(); }; xx.do = function () { this.timeLimit(); Matter.Body.setAngularVelocity(this, 0.01) ctx.beginPath(); ctx.lineWidth = 1; ctx.strokeStyle = "#00000000" for (let i = 0; i < this.vertices.length; i++) { const where = this.vertices[i] const endPoint = Vector.add(where, Vector.mult(Vector.normalise(Vector.sub(where, this.position)), 2500)) me.lasers(this.vertices[0], this.angle + Math.PI / 3); me.lasers(this.vertices[1], this.angle + Math.PI); me.lasers(this.vertices[2], this.angle - Math.PI / 3); } ctx.strokeStyle = randomColor({ hue: "#FF00FF" }); ctx.stroke(); ctx.save() ctx.beginPath(); ctx.moveTo(this.vertices[0].x, this.vertices[0].y); ctx.lineTo(this.vertices[1].x, this.vertices[1].y); ctx.lineTo(this.vertices[2].x, this.vertices[2].y); ctx.fillStyle = "#000000"; ctx.strokeStyle = "transparent"; ctx.fill(); ctx.closePath(); ctx.stroke(); ctx.restore() } } me.seeker = function (x, y) { mobs.spawn(x, y, sides = 5, radius = 5, "rgb(0,0,0)"); let yy = mob[mob.length - 1]; yy.stroke = "transparent"; yy.onHit = function () { this.explode(this.mass * 20); }; Matter.Body.setDensity(yy, 0.000015); //normal is 0.001 yy.timeLeft = 420 //* (0.8 + 0.4 * Math.random()); yy.accelMag = 0.00017 * simulation.accelScale; //* (0.8 + 0.4 * Math.random()) yy.frictionAir = 0.01 //* (0.8 + 0.4 * Math.random()); yy.restitution = 0.5; yy.leaveBody = false; yy.isDropPowerUp = false; yy.isBadTarget = true; yy.isMobBullet = true; yy.showHealthBar = false; yy.collisionFilter.category = cat.mobBullet; yy.collisionFilter.mask = cat.player | cat.map | cat.body | cat.bullet; let index = 0; yy.do = function () { this.alwaysSeePlayer() this.timeLimit(); this.attraction(); }; } me.drop = function (x, y) { mobs.spawn(x, y, sides = 90, radius = 30, "rgb(0,255,100,0.7)"); let yyy = mob[mob.length - 1]; yyy.stroke = "transparent"; yyy.onDeath = function () { for (let i = 0, len = 5; i < len; i++) { me.seeker(this.position.x, this.position.y) Matter.Body.setVelocity(mob[mob.length - 1], { x: Math.random() * 30 - Math.random() * 30, y: Math.random() * 30 - Math.random() * 30 }); } }; Matter.Body.setDensity(yyy, 0.000015); //normal is 0.001 yyy.timeLeft = 60 //* (0.8 + 0.4 * Math.random()); yyy.frictionAir = 0.01 //* (0.8 + 0.4 * Math.random()); yyy.restitution = 0.5; yyy.leaveBody = false; yyy.isDropPowerUp = false; yyy.isBadTarget = true; yyy.isMobBullet = true; yyy.showHealthBar = false; yyy.collisionFilter.category = cat.mobBullet; yyy.collisionFilter.mask = null; yyy.maxRadius = 30; let index = 0; yyy.do = function () { if (Matter.Query.collides(this, [player]).length > 0 && !(m.isCloak && tech.isIntangible) && m.immuneCycle < m.cycle) { Matter.Body.setPosition(this, player.position) if (player.speed > 2.5) Matter.Body.setVelocity(player, Vector.mult(player.velocity, 0.94)) } this.alwaysSeePlayer() this.timeLimit(); ctx.save() ctx.beginPath(); ctx.moveTo(this.position.x, this.position.y) ctx.fillStyle = "black"; ctx.arc(this.position.x, this.position.y, this.maxRadius, 0, 2 * Math.PI) ctx.stroke() ctx.fill() ctx.restore() if (this.maxRadius > 0) { this.maxRadius -= 0.5; } }; } }; restoreBoss(-13350, -1800); laserEM(-6500 + Math.floor(Math.random() * 200) - Math.floor(Math.random() * 200), -3400 + Math.floor(Math.random() * 200) - Math.floor(Math.random() * 200)); sniper(-9275 + Math.floor(Math.random() * 200) - Math.floor(Math.random() * 200), -3325 + Math.floor(Math.random() * 200) - Math.floor(Math.random() * 200)); laserEM(-5750 + Math.floor(Math.random() * 200) - Math.floor(Math.random() * 200), -850 + Math.floor(Math.random() * 200) - Math.floor(Math.random() * 200)); sniper(-3600 + Math.floor(Math.random() * 200) - Math.floor(Math.random() * 200), -1325 + Math.floor(Math.random() * 200) - Math.floor(Math.random() * 200)); laserEM(1425 + Math.floor(Math.random() * 200) - Math.floor(Math.random() * 200), -800 + Math.floor(Math.random() * 200) - Math.floor(Math.random() * 200)); //restoreBoss(-350, -3225); wire(); wire(); wire(); wire(); wire(); color.map = '#00000000'; level.customTopLayer = () => { if (dong.position.x > -3825) { dong.force.y -= dong.mass * simulation.g; } else { dong.force.y += dong.mass * simulation.g; } Matter.Body.setAngularVelocity(dong, -0.5) if (destroyed == false) { door.isClosing = false; } else { door.isClosing = true; } door.openClose(); door.draw() for (let i = 0, len = map.length; i < len; i++) { //so boss bar renders over the map // ctx.beginPath() // //ctx.setLineDash([125 * Math.random(), 125 * Math.random()]); // ctx.fillRect(map[i].vertices[0].x , map[i].vertices[0].y, Math.abs(map[i].vertices[0].x - map[i].vertices[1].x), Math.abs(map[i].vertices[1].y - map[i].vertices[2].y)) // //ctx.strokeRect(map[i].vertices[0].x , map[i].vertices[0].y, Math.abs(map[i].vertices[0].x - map[i].vertices[1].x), Math.abs(map[i].vertices[1].y - map[i].vertices[2].y)) // ctx.fillStyle = "rgba(68,68,68)" // //ctx.strokeStyle = "transparent" // ctx.stroke() // ctx.fill() ctx.beginPath(); ctx.moveTo(map[i].vertices[0].x, map[i].vertices[0].y); for (let j = 0, length = map[i].vertices.length; j < length; j++) { ctx.lineTo(map[i].vertices[j].x, map[i].vertices[j].y); } ctx.lineTo(map[i].vertices[0].x, map[i].vertices[0].y); ctx.fillStyle = "rgba(68,68,68)"; ctx.strokeStyle = "transparent"; ctx.fill(); ctx.stroke(); // ctx.setLineDash([]); } for (let i = 0, len = mob.length; i < len; i++) { if (mob[i].restoreBoss) { ctx.save(); ctx.setTransform(1, 0, 0.5, 1, 0, 0); //slanted ctx.fillStyle = "rgba(100, 100, 100, 0.3)"; ctx.fillRect(canvas.width2 / 2, canvas.height2 / 10, canvas.width2, 30); ctx.fillStyle = "rgba(0,0,0,0.7)"; ctx.fillRect(canvas.width2 / 2, canvas.height2 / 10, canvas.width2 * mob[i].health, 30); ctx.restore(); } } }; 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 } const obj = { restoreBoss }; Object.assign(spawn, obj); //ez }, superNgonBros() { simulation.makeTextLog(`Super N-gon Bros by DesBoot`); let bowserKilled = 0 let flagY = -750 let flagReached = 0 const elevator1 = level.elevator(3975, -11650, 450, 50, -13100, 0.003) const elevator2 = level.elevator(5575, -11650, 450, 50, -13100, 0.003) let firstElevatorY = -11650 const portal = level.portal({ x: 3990, y: 100 }, 1.5 * Math.PI, { x: 100, y: -13500 }, -1.5 * Math.PI) const portal2 = level.portal({ x: 7135, y: -12270 }, -1 * Math.PI, { x: 12325, y: -2000 }, -1.5 * Math.PI) const bowser = function (x, y, radius = 150) { //define the mob the same as spawn mob code mobs.spawn(x, y, 5, radius, "rgb(0,200,180)"); let me = mob[mob.length - 1]; me.accelMag = 0.05; me.g = 0.002; //required if using this.gravity me.frictionAir = 0.01; me.friction = 1 me.frictionStatic = 1 me.restitution = 0; me.delay = 80 * simulation.CDScale; me.randomHopFrequency = 200 + Math.floor(Math.random() * 150); me.randomHopCD = simulation.cycle + me.randomHopFrequency; Matter.Body.rotate(me, Math.random() * Math.PI); spawn.shield(me, x, y); me.do = function () { //spawn.grenade(me.position.x, me.position.y); // //const v = 5 * simulation.accelScale; // Matter.Body.setVelocity(mob[mob.length - 1], { // x: this.velocity.x + this.fireDir.x * v + Math.random(), // y: this.velocity.y + this.fireDir.y * v + Math.random() // }); this.gravity(); this.seePlayerCheck(); this.checkStatus(); if (this.seePlayer.recall) { if (this.cd < simulation.cycle && (Matter.Query.collides(this, map).length || Matter.Query.collides(this, body).length)) { this.cd = simulation.cycle + this.delay; const forceMag = (this.accelMag + this.accelMag * Math.random()) * this.mass; const angle = Math.atan2(this.seePlayer.position.y - this.position.y, this.seePlayer.position.x - this.position.x); this.force.x += forceMag * Math.cos(angle) * 0.5; this.force.y += (forceMag * Math.sin(angle) - (Math.random() * 0.07 + 0.1) * this.mass) * 0.7; //antigravity if (Math.random() < 0.5) { spawn.grenade(me.position.x, me.position.y - 250 * Math.random(), 500); Matter.Body.setVelocity(mob[mob.length - 1], { x: -5, y: 0 }); } else { spawn.bullet(this.position.x, this.position.y, 25); Matter.Body.setVelocity(mob[mob.length - 1], { x: -25, y: -25 }); } } } else { //randomly hob if not aware of player if (this.randomHopCD < simulation.cycle && (Matter.Query.collides(this, map).length || Matter.Query.collides(this, body).length)) { this.randomHopCD = simulation.cycle + this.randomHopFrequency; //slowly change randomHopFrequency after each hop this.randomHopFrequency = Math.max(100, this.randomHopFrequency + (0.5 - Math.random()) * 200); const forceMag = (this.accelMag + this.accelMag * Math.random()) * this.mass * (0.1 + Math.random() * 0.3); const angle = -Math.PI / 2 + (Math.random() - 0.5) * Math.PI; this.force.x += forceMag * Math.cos(angle); this.force.y += forceMag * Math.sin(angle) - 0.07 * this.mass; //antigravity spawn.grenade(me.position.x, me.position.y - 250 * Math.random(), 500); Matter.Body.setVelocity(mob[mob.length - 1], { x: -5, y: 0 }); } } }; me.onDeath = function () { powerUps.spawnBossPowerUp(this.position.x, this.position.y) bowserKilled = 1 } } bowser(20500, -400) //call the mob //fire(15300, -200) const brick = function (x, y, angle = Math.PI * 0.5, radius = 53) {//credit to Richard0820 for the code mobs.spawn(x, y, 4, radius, "#ab6101"); let me = mob[mob.length - 1]; me.stroke = "transparent"; me.isDropPowerUp = false; me.showHealthBar = false; Matter.Body.setDensity(me, 999999) me.collisionFilter.mask = cat.player | cat.mob | cat.bullet; me.constraint = Constraint.create({ pointA: { x: me.position.x, y: me.position.y }, bodyB: me, stiffness: 0, damping: 0 }); me.do = function () { this.isStunned = true; if (this.health < 1) { this.health += 0.001; //regen } this.checkStatus(); Matter.Body.setAngle(me, angle); if ((player.velocity.y < 0 && player.position.y > me.position.y || player.velocity.y > 30 && player.position.y < me.position.y) && Math.abs(player.position.x - me.position.x) < 50 && Math.abs(player.position.y - me.position.y) < 150) { me.death() } }; me.onHit = function () { if (player.velocity.y < 0 && player.position.y > me.position.y || player.velocity.y > 30 && player.position.y < me.position.y) { me.death() } } me.onDeath = function () { if (Math.random() < 0.1) { spawn.randomSmallMob(me.position.x, me.position.y - 75); simulation.makeTextLog('mob') } else { if (Math.random() < 0.07) { powerUps.spawn(me.position.x, me.position.y + (75 * (player.velocity.y / Math.abs(player.velocity.y))), "tech", true); simulation.makeTextLog('tech') } else { if (Math.random() < 0.4) { powerUps.spawn(me.position.x, me.position.y + (75 * (player.velocity.y / Math.abs(player.velocity.y))), "heal", true); simulation.makeTextLog('heal') } else { //if (Math.random() < 0.8){ powerUps.spawn(me.position.x, me.position.y + (75 * (player.velocity.y / Math.abs(player.velocity.y))), "ammo", true); simulation.makeTextLog('ammo') //} } } } } Composite.add(engine.world, me.constraint); } simulation.enableConstructMode() let firstMobsSpawned = 1 let secondMobsSpawned = 0 let thirdMobsSpawned = 0 let fourthMobsSpawned = 0 let firstMobsReached = 0 let secondMobsReached = 0 let thirdMobsReached = 0 let fourthMobsReached = 0 let finalRoomReached = 0 let undergroundMobsSpawned = 0 let undergroundMobsReached = 0 level.setPosToSpawn(0, -50); //normal spawn level.exit.x = 22100; level.exit.y = -40; spawn.mapRect(level.enter.x, level.enter.y + 20, 100, 20); //bump for level entrance spawn.mapRect(level.exit.x, level.exit.y + 20, 100, 20); //bump for level exit level.defaultZoom = 1800 simulation.zoomTransition(level.defaultZoom) document.body.style.backgroundColor = "#a2a5ff"; // color.map = "#444" //custom map color level.custom = () => { if (player.position.x > 14950 && flagReached == 0) { flagReached = 1 } if (flagReached == 1 && flagY < -150) { flagY += 5 } ctx.fillStyle = "rgba(64,64,64,0.97)" ctx.fillRect(4200, -13100, 2, 1450) ctx.fillRect(5800, -13100, 2, 1450) if (firstElevatorY < -12099) { firstElevatorY = -11650 } else { firstElevatorY -= 5 } //simulation.makeTextLog(firstElevatorY) elevator1.move(); elevator2.move(); if (player.position.x > 0 && player.position.y < -9000 && player.position.y > -10000) { //m.death() m.damage(0.05 * simulation.difficultyMode) Matter.Body.setPosition(player, { x: 275, y: -12175 }); } portal[2].query() portal[3].query() portal2[2].query() portal2[3].query() //simulation.makeTextLog(firstBlockBroken) level.exit.drawAndCheck(); if (player.position.x > 4100 && secondMobsReached == 0) { secondMobsSpawned = 1 } if (player.position.x > 7000 && thirdMobsReached == 0) { thirdMobsSpawned = 1 } if (player.position.x > 14300 && fourthMobsReached == 0) { fourthMobsSpawned = 1 } if (player.position.y < -11000 && undergroundMobsReached == 0) { undergroundMobsSpawned = 1 }//player.position.x > 14300 && if (m.onGround) { if (Math.abs(player.velocity.x) > 0.3) { Matter.Body.setVelocity(player, { x: player.velocity.x + (0.1 * (Math.abs(player.velocity.x) / player.velocity.x)), y: player.velocity.y + 0.2 }); } else { Matter.Body.setVelocity(player, { x: player.velocity.x, y: player.velocity.y + 0.2 }); } } else { if (input.down) { Matter.Body.setVelocity(player, { x: player.velocity.x, y: player.velocity.y + 2 }); } else { Matter.Body.setVelocity(player, { x: player.velocity.x, y: player.velocity.y + 0.2 }); } } level.enter.draw(); if (finalRoomReached == 0 && player.position.x > 21150) { finalRoomReached = 1 simulation.makeTextLog('Thank you M, but our techs are in another castle!') } //mobs if (firstMobsSpawned == 1 && firstMobsReached == 0) { spawn.randomSmallMob(1260, -75); spawn.randomMob(2100, -75, 0.4); spawn.randomSmallMob(2400, -75); spawn.randomSmallMob(2500, -75); spawn.randomMob(2900, -75, 0.2); spawn.randomMob(3400, -75, 0.4); spawn.randomMob(3400, -75, 0.4); firstMobsReached = 1 } if (secondMobsSpawned == 1 && secondMobsReached == 0) { spawn.randomSmallMob(4400, -75); spawn.randomSmallMob(5500, -75); spawn.randomSmallMob(5835.6, -402.4); spawn.randomSmallMob(5835.6, -402.4); spawn.randomSmallMob(6543.2, -730.0); spawn.randomMob(6795.4, -162.4, 0.1); spawn.randomMob(6795.4, -162.4, 0.1); secondMobsReached = 1 } if (thirdMobsSpawned == 1 && thirdMobsReached == 0) { spawn.randomMob(8465.6, -469.9, 0.1); spawn.randomMob(9839.6, -444.5, 0.4); spawn.randomSmallMob(11033.2, -155.3); spawn.randomMob(12161.3, -85.1, 0.3); spawn.randomMob(12161.3, -85.1, 0.3); spawn.randomMob(13399.8, -93.4, 0.4); thirdMobsReached = 1 } if (fourthMobsSpawned == 1 && fourthMobsReached == 0) { spawn.randomSmallMob(16500, -400); spawn.randomSmallMob(19278.9, -211.1); spawn.randomMob(18839.0, -463.2, 0.3); spawn.randomMob(18036.9, -205.9, 0.3); spawn.randomMob(16950.4, -365.2, 0.4); spawn.randomMob(16355.6, -390.8, 0.1); fourthMobsReached = 1 } if (undergroundMobsSpawned == 1 && undergroundMobsReached == 0) { spawn.randomSmallMob(1140.0, -12228.0); spawn.randomSmallMob(2429.9, -12371.2); spawn.randomSmallMob(4899.4, -12139.6); spawn.randomMob(18839.0, -463.2, 0.3); spawn.randomMob(2844.5, -12281.0, 0.2); spawn.randomMob(4967.5, -12550.8, 0.4); spawn.randomMob(6696.9, -12437.9, 0.1); undergroundMobsReached = 1 } portal[0].draw(); portal[1].draw(); portal[2].draw(); portal[3].draw(); portal2[0].draw(); portal2[1].draw(); portal2[2].draw(); portal2[3].draw(); }; level.customTopLayer = () => { //spawn.mapRect(886, firstElevatorY + 10000, 75, 5); ctx.fillStyle = "rgba(64,64,64,0.97)" ctx.fillRect(3928, -300, 120, 500) //6940, -12360, 200, 5 ctx.fillRect(6940, -12350, 170, 120) ctx.fillRect(7090, -12380, 120, 200) ctx.fillRect(14980, -750, 10, 750)//flagpole ctx.beginPath() ctx.moveTo(14980, flagY) ctx.lineTo(14905, flagY) ctx.lineTo(14980, flagY + 75) ctx.fill() }; brick(923.5, -262); spawn.mapRect(886, -304, 75, 5); spawn.mapRect(886, -229, 75, 5); spawn.mapRect(883, -304, 5, 80); spawn.mapRect(958, -304, 5, 80); brick(1250.5, -262); spawn.mapRect(1138, -304, 375, 5); spawn.mapRect(1138, -229, 375, 5); brick(1400.5, -262); brick(1325.5, -562); spawn.mapRect(1288, -604, 75, 5); spawn.mapRect(1288, -529, 75, 5); spawn.mapRect(1285, -604, 5, 80); spawn.mapRect(1360, -604, 5, 80); brick(5787.5, -262); spawn.mapRect(5675, -304, 225, 5); spawn.mapRect(5675, -229, 225, 5); brick(6987.5, -562); spawn.mapRect(6725, -604, 300, 5); spawn.mapRect(6725, -529, 300, 5); spawn.mapRect(7025, -604, 5, 80); brick(7887.5, -262);//4 separated blocks in the middle spawn.mapRect(7850, -304, 75, 5); spawn.mapRect(7850, -225, 75, 5); spawn.mapRect(7850, -304, 5, 80); spawn.mapRect(7925, -304, 5, 84); brick(8112.5, -262); spawn.mapRect(8075, -304, 75, 5); spawn.mapRect(8075, -225, 75, 5); spawn.mapRect(8075, -304, 5, 80); spawn.mapRect(8150, -304, 5, 84); brick(8337.5, -262); spawn.mapRect(8300, -304, 75, 5); spawn.mapRect(8300, -225, 75, 5); spawn.mapRect(8300, -304, 5, 80); spawn.mapRect(8375, -304, 5, 84); brick(8112.5, -562); spawn.mapRect(8075, -604, 75, 5); spawn.mapRect(8075, -525, 75, 5); spawn.mapRect(8075, -604, 5, 80); spawn.mapRect(8150, -604, 5, 84); brick(9612.5, -562); spawn.mapRect(9500, -604, 300, 5); spawn.mapRect(9500, -525, 300, 5); spawn.mapRect(9647.5, -600, 5, 75); brick(9687.5, -562); brick(12887.5, -262); spawn.mapRect(12700, -304, 300, 5); spawn.mapRect(12700, -225, 300, 5); brick(5212.5, -12337.5); spawn.mapRect(4725, -12377, 525, 5); spawn.mapRect(4725, -12303, 525, 5); spawn.mapRect(5250, -12377, 5, 79); spawn.mapRect(-100, 0, 4033, 2000); spawn.mapRect(4043, 0, 882, 2000); spawn.mapRect(3909.5, 203.6, 150, 2000); spawn.mapRect(1138, -300, 75, 75); spawn.mapRect(1288, -300, 75, 75); spawn.mapRect(1438, -300, 75, 75); spawn.mapRect(1738, -150, 150, 75);//pipe 1 spawn.mapRect(1753, -150, 120, 150); spawn.mapRect(2488, -225, 150, 75);//pipe 2 spawn.mapRect(2503, -225, 120, 225); spawn.mapRect(3088, -300, 150, 75);//pipe 3 spawn.mapRect(3103, -300, 120, 300); spawn.mapRect(3913, -300, 20, 75);//pipe 4 spawn.mapRect(3928, -300, 5, 300); spawn.mapRect(4043, -300, 20, 75); spawn.mapRect(4043, -300, 5, 300); spawn.mapRect(5225, 0, 1125, 2000); spawn.mapRect(6575, 0, 4900, 2000); spawn.mapRect(5675, -300, 75, 75); spawn.mapRect(5825, -300, 75, 75); spawn.mapRect(5900, -600, 600, 75); spawn.mapRect(6725, -600, 225, 75); spawn.mapRect(6950, -300, 75, 75); spawn.mapRect(7400, -300, 150, 75); spawn.mapRect(8750, -300, 75, 75); spawn.mapRect(8975, -600, 225, 75);//raised platform spawn.mapRect(9575, -300, 150, 75); spawn.mapRect(9500, -600, 75, 75);//upper block with double bricks spawn.mapRect(9725, -600, 75, 75); spawn.mapRect(9950, -75, 300, 75);//staircase spawn.mapRect(10025, -150, 225, 75); spawn.mapRect(10100, -225, 150, 75); spawn.mapRect(10175, -300, 75, 75); spawn.mapRect(10475, -75, 300, 75); spawn.mapRect(10475, -150, 225, 75); spawn.mapRect(10475, -225, 150, 75); spawn.mapRect(10475, -300, 75, 75); spawn.mapRect(11100, -75, 375, 75);//staircase 2 spawn.mapRect(11175, -150, 300, 75); spawn.mapRect(11250, -225, 225, 75); spawn.mapRect(11325, -300, 150, 75); spawn.mapRect(11725, -75, 300, 75); spawn.mapRect(11725, -150, 225, 75); spawn.mapRect(11725, -225, 150, 75); spawn.mapRect(11725, -300, 75, 75); spawn.mapRect(11725, 0, 5975, 2000);//platform after the staircase spawn.mapRect(12325, -150, 150, 75);//exit pipe spawn.mapRect(12340, -150, 120, 300); spawn.mapRect(12700, -300, 150, 75); spawn.mapRect(12925, -300, 75, 75); spawn.mapRect(13525, -150, 150, 72);//final pipe spawn.mapRect(13540, -150, 120, 150); spawn.mapRect(13675, -75, 675, 75);//final staircase spawn.mapRect(13750, -150, 600, 75); spawn.mapRect(13825, -225, 525, 75); spawn.mapRect(13900, -300, 450, 75); spawn.mapRect(13975, -375, 375, 75); spawn.mapRect(14050, -450, 300, 75); spawn.mapRect(14125, -525, 225, 75); spawn.mapRect(14200, -600, 150, 75); //flag spawn.mapRect(14950, -75, 75, 75); spawn.mapRect(1750, -4600, 500, 25);//loss spawn.mapRect(2000, -4850, 25, 500); spawn.mapRect(1800, -4800, 25, 150); spawn.mapRect(2075, -4800, 25, 150); spawn.mapRect(2150, -4775, 25, 125); spawn.mapRect(1875, -4550, 25, 150); spawn.mapRect(1800, -4550, 25, 150); spawn.mapRect(2075, -4550, 25, 150); spawn.mapRect(2123, -4430, 100, 25); // spawn.mapRect(-250, -600, 500, 25); // spawn.mapRect(-250, -600, 500, 25); //underground area spawn.mapRect(0, -12000, 2025, 2000); spawn.mapRect(2325, -12225, 150, 2000); spawn.mapRect(2775, -12000, 900, 2000); spawn.mapRect(3525, -12300, 150, 300); spawn.mapRect(3450, -12225, 75, 300); spawn.mapRect(3375, -12150, 75, 300); spawn.mapRect(3300, -12075, 75, 300); spawn.mapRect(4725, -12375, 450, 75); spawn.mapRect(4725, -12000, 600, 2000); spawn.mapRect(-100, -13500, 100, 3500); spawn.mapRect(-100, -13500, 100, 3500); spawn.mapRect(6375, -12225, 1650, 2000); spawn.mapRect(7225, -13225, 2850, 3000); spawn.mapRect(-100, -13700, 100, 200);//roof spawn.mapRect(-100, -13700, 3775, 100); spawn.mapRect(6450, -13225, 1000, 100); spawn.mapRect(7090, -13225, 120, 885);//pipe //spawn.mapRect(6940, -12360, 200, 120); spawn.mapRect(6940, -12350, 200, 5); spawn.mapRect(6950, -12240, 140, 5); spawn.mapRect(6940, -12365, 75, 15); spawn.mapRect(6940, -12235, 75, 15); //castle spawn.mapRect(17700, 0, 4975, 2000); spawn.mapRect(18600, -225, 375, 2225); spawn.mapRect(19500, -225, 450, 2225); spawn.mapRect(19500, -825, 450, 225); spawn.mapRect(15924, -1575, 6751, 750); spawn.mapRect(19950, -225, 975, 75); spawn.mapRect(20925, -300, 225, 300); spawn.mapRect(21000, -825, 150, 300); spawn.mapRect(15924, -225, 1776, 2225); spawn.mapRect(17175, -825, 525, 225); spawn.mapRect(22600, -825, 75, 825); // powerUps.spawnStartingPowerUps(1475, -1175); // spawn.debris(750, -2200, 3700, 16); //16 debris per level // spawn.bodyRect(1540, -1110, 300, 25, 0.9); // spawn.randomSmallMob(1300, -70); // spawn.randomMob(2650, -975, 0.8); // spawn.randomGroup(1700, -900, 0.4); // if (simulation.difficulty > 1) spawn.randomLevelBoss(2200, -1300); // spawn.secondaryBossChance(100, -1500) powerUps.addResearchToLevel() //needs to run after mobs are spawned }, underpass() { simulation.makeTextLog(`underpass by Richard0820`); let key = false; const door = level.door(2650, -825, 50, 250, 250, 10); const elevator = level.elevator(-11050, -650, 450, 75, -2975, 0.003, { up: 0.1, down: 0.1 }) const slimePit = level.hazard(-4775, -350, 1975, 175); const boost = level.boost(137.5, -600, 75, 25); let base = Matter.Bodies.rectangle(-4375, -1000, 100, 100, { density: 0.05, isNotHoldable: true, restitution: 1.05, isStatic: false }, true, [true], 0); let left = Matter.Bodies.rectangle(-4375 + 50, -1000 - 50, 50, 50, {//not actually left/right density: 0.05, isNotHoldable: true, isStatic: false }); let right = Matter.Bodies.rectangle(-4375 - 50, -1000 - 50, 50, 50, { density: 0.05, isNotHoldable: true, isStatic: false }); let left2 = Matter.Bodies.rectangle(-4375 - 50, -1000 + 50, 50, 50, { density: 0.05, isNotHoldable: true, isStatic: false }); let right2 = Matter.Bodies.rectangle(-4375 + 50, -1000 + 50, 50, 50, { density: 0.05, isNotHoldable: true, isStatic: false }); dong = Matter.Body.create({ parts: [base, left, right, left2, right2] }); body[body.length] = base; body[body.length] = left; body[body.length] = right; body[body.length] = left2; body[body.length] = right2; Matter.Composite.add(engine.world, dong) Matter.Composite.add(engine.world, Constraint.create({ pointA: { x: -3825, y: -975 }, bodyB: dong, stiffness: 0.2, damping: 0.1 })); composite[composite.length] = dong; setTimeout(function () { dong.collisionFilter.category = cat.body; dong.collisionFilter.mask = cat.body | cat.player | cat.bullet | cat.mobBullet | cat.mob //| cat.map }, 1000); level.custom = () => { ctx.save() ctx.beginPath() ctx.fillStyle = "#80808077"; ctx.strokeStyle = "#80808022"; ctx.fillRect(225, -1025, 2400, 450); ctx.fillRect(-2950, -1025, 3100, 450); ctx.fillRect(-7050, -1025, 2400, 450); ctx.fillRect(-10575, -3975, 4525, 1025); ctx.fillRect(-4650, -1700, 1700, 1100); ctx.fillRect(-11150, -3575, 575, 3050); ctx.fillRect(-11900, -1000, 750, 475); ctx.fill() ctx.stroke() ctx.restore() ctx.save() ctx.beginPath() ctx.fillStyle = "#d8dadf"; ctx.strokeStyle = "#d8dadf"; ctx.moveTo(-2950, -600); ctx.lineTo(-3730, -1725); ctx.lineTo(-3730, -600); ctx.moveTo(-4650, -600); ctx.lineTo(-3925, -1725); ctx.lineTo(-3925, -575); ctx.moveTo(-10575, -3425); //NE section ctx.lineTo(-10100, -2975); ctx.lineTo(-10575, -2975); // ctx.moveTo(-7625, -3800); // ctx.lineTo(-6750, -2975); // ctx.lineTo(-7625, -2975); ctx.moveTo(-7975, -2975); ctx.lineTo(-7625, -3800); ctx.lineTo(-7350, -2950); ctx.moveTo(-6750, -2975); ctx.lineTo(-7075, -3800); ctx.lineTo(-7350, -2950); // ctx.moveTo(-7975, -2975); // ctx.lineTo(-7075, -3800); // ctx.lineTo(-7075, -2975); ctx.moveTo(-11900, -950); ctx.lineTo(-11900, -550); ctx.lineTo(-11500, -550); ctx.fillRect(-3925, -1675, 200, 1075); ctx.fillRect(-7625, -3800, 550, 875); ctx.clearRect(-10600, -4000, 525, 475); ctx.clearRect(-10100, -4000, 500, 300); ctx.clearRect(-9625, -4000, 500, 175); ctx.fillRect(-11125, -3600, 550, 50); ctx.fillRect(-10600, -3400, 50, 425); ctx.fillRect(-11925, -925, 45, 375); ctx.fillRect(-3950, -1675, 75, 1100); ctx.fillRect(-3925, -625, 950, 50); ctx.fillRect(-4650, -600, 1700, 375); ctx.fillRect(-14550, -2400, 2650, 2050); //ctx.clearRect(-11050, -3000, 475, 50); ctx.moveTo(-11150, -3575); ctx.lineTo(-10575, -2150); ctx.lineTo(-10575, -3575); ctx.stroke() ctx.fill() ctx.restore() boost.query() slimePit.query() if (Matter.Query.collides(dong, [player]).length > 0 && !(m.isCloak && tech.isIntangible) && m.immuneCycle < m.cycle) { m.immuneCycle = m.cycle + m.collisionImmuneCycles; //player is immune to damage for 30 cycles const dmg = 0.05 * Math.min(simulation.dmgScale, simulation.difficulty); m.damage(dmg); simulation.drawList.push({ //add dmg to draw queue x: dong.position.x, y: dong.position.y, radius: Math.sqrt(dmg) * 200, color: simulation.mobDmgColor, time: simulation.drawTime }); } for (let i = 0; i < mob.length; i++) { if (Matter.Query.collides(dong, [mob[i]]).length > 0) { const dmg = 1; mob[i].damage(dmg, true); simulation.drawList.push({ //add dmg to draw queue x: dong.position.x, y: dong.position.y, radius: Math.sqrt(dmg) * 50, color: simulation.mobDmgColor, time: simulation.drawTime }); break } } level.exit.drawAndCheck(); ctx.beginPath() ctx.fillStyle = '#68686822'; ctx.fillRect(-25, -2175, 100, 200); ctx.fill() ctx.setLineDash([125 * Math.random(), 125 * Math.random()]); ctx.moveTo(-3825, -975) ctx.lineTo(dong.position.x, dong.position.y) ctx.stroke(); ctx.setLineDash([]); simulation.drawList.push({ //add dmg to draw queue x: dong.position.x, y: dong.position.y, radius: 10, color: color.block, time: 20 }); ctx.beginPath() ctx.fillStyle = `rgba(68,68,68, ${3 * Math.sin(simulation.cycle * 0.015)})` ctx.fillRect(-3000, -2175, 175, 25); ctx.fillRect(-2850, -2300, 25, 150); ctx.fillRect(-3000, -2300, 175, 25); ctx.fillRect(-3000, -2425, 25, 150); ctx.fillRect(-3000, -2425, 175, 25); ctx.fill() ctx.fillStyle = `rgba(68,68,68, ${5 * Math.sin(simulation.cycle * 0.015)})` ctx.fillRect(-2725, -2425, 25, 275); ctx.fillRect(-2725, -2425, 175, 25); ctx.fillRect(-2575, -2425, 25, 275); ctx.fillRect(-2725, -2300, 175, 25); ctx.fill() ctx.fillStyle = `rgba(68,68,68, ${7 * Math.sin(simulation.cycle * 0.015)})` ctx.fillRect(-2450, -2425, 25, 275); ctx.fillRect(-2450, -2175, 175, 25); ctx.fill() ctx.stroke(); ctx.fillStyle = `#00FFFF22`; ctx.fillRect(-7650, -2975 - 100, 600, 2375 + 100) ctx.fill() ctx.fillStyle = `#00FFFF66` ctx.fillRect(-7650 + Math.floor(Math.random() * 600), -2975 - 100, 5, 2375 + 100) ctx.fillRect(-7650 + Math.floor(Math.random() * 600), -2975 - 100, 5, 2375 + 100) ctx.fillStyle = `rgba(68, 68, 68)` ctx.fillRect(-7675, -3075, 50, 125); ctx.fillRect(-7075, -3075, 50, 125); ctx.fillRect(-7725, -3025, 75, 75); ctx.fillRect(-7050, -3025, 75, 75); ctx.fill() for (let i = 0, len = body.length; i < len; ++i) { //push blocks away vertically if (body[i].position.x > -7625 && body[i].position.x < -7075 && body[i].position.y > -2975 - 100 && body[i].position.y < -625) { body[i].force.y -= simulation.g * body[i].mass + 0.012; } } for (let i = 0, len = bullet.length; i < len; ++i) { //push bullets away vertically if (bullet[i].position.x > -7625 && bullet[i].position.x < -7075 && bullet[i].position.y > -2975 - 100 && bullet[i].position.y < -625) { bullet[i].force.y -= simulation.g * bullet[i].mass; } } for (let i = 0, len = powerUp.length; i < len; ++i) { //push powerups away vertically if (powerUp[i].position.x > -7625 && powerUp[i].position.x < -7075 && powerUp[i].position.y > -2975 - 100 && powerUp[i].position.y < -625) { powerUp[i].force.y -= simulation.g * powerUp[i].mass + 0.12; } } for (let i = 0, len = mob.length; i < len; ++i) { //push mobs away vertically if (mob[i].position.x > -7625 && mob[i].position.x < -7075 && mob[i].position.y > -2975 - 100 && mob[i].position.y < -625) { mob[i].force.y -= simulation.g * mob[i].mass + 0.0012; } } if (m.pos.x > -7625 && m.pos.x < -7075 && m.pos.y > -2975 - 100 && m.pos.y < -625) { player.force.y -= m.mass * simulation.g + (input.down ? 0 : 0.012 * 2); } ctx.save() ctx.beginPath(); ctx.fillStyle = `rgba(0,250,250, 0.05)`; ctx.fillRect(2625, -1000, 350, 450); ctx.fill() ctx.restore() elevator.move() if (mob.length <= 5) { key = true; } if (key) { level.exit.x = 2775; level.exit.y = -650; } else { ctx.save() ctx.beginPath(); ctx.moveTo(2775, -650 + 30); ctx.lineTo(2775, -650 - 80); ctx.bezierCurveTo(2775, -650 - 170, 2775 + 100, -650 - 170, 2775 + 100, -650 - 80); ctx.lineTo(2775 + 100, -650 + 30); ctx.lineTo(2775, -650 + 30); ctx.fillStyle = "#00ffff"; ctx.fill(); ctx.restore() const omega = new Image(); //the seal hehe let width = Math.random() * 40; //Math.max(Math.sin(m.cycle * 0.05), Math.cos(m.cycle * 0.05)) * let length = Math.random() * 40; //Math.max(Math.sin(m.cycle * 0.05), Math.cos(m.cycle * 0.05)) * omega.src = "https://raw.githubusercontent.com/Whyisthisnotavalable/image-yy/main/MOSHED-2023-4-20-12-55-58-removebg-preview.png"; //"https://raw.githubusercontent.com/Whyisthisnotavalable/image-yy/main/omegafr.png"; ctx.drawImage(omega, 2775 + 50 - width, -650 - 50 - length, width * 2, length * 2) ctx.save() ctx.setLineDash([70 + 300 * Math.random(), 55 * Math.random()]); ctx.beginPath() // ctx.font = "80px monospace"; // ctx.fillText("Ω", 2775 + 50, -650 - 50); ctx.strokeStyle = "gray"; ctx.arc(2775 + 50, -650 - 50, 100 * Math.abs(Math.sin(m.cycle * 0.07)) + 10, 0 + Math.sin(m.cycle * 0.1 * Math.PI), 0 + Math.cos(m.cycle * 0.1 + Math.PI)) ctx.shadowBlur = 10; ctx.shadowColor = "black"; ctx.stroke() //ctx.fill() ctx.restore() ctx.save() ctx.setLineDash([70 + 300 * Math.random(), 55 * Math.random()]); ctx.beginPath() // ctx.font = "80px monospace"; // ctx.fillText("Ω", 2775 + 50, -650 - 50); ctx.strokeStyle = "gray"; ctx.arc(2775 + 50, -650 - 50, 90 * Math.abs(Math.sin(m.cycle * 0.08)) + 10, 0 + Math.cos(m.cycle * 0.1 * Math.PI), 0 + Math.sin(m.cycle * 0.1 + Math.PI)) ctx.shadowBlur = 10; ctx.shadowColor = "black"; ctx.stroke() //ctx.fill() ctx.restore() ctx.save() ctx.setLineDash([70 + 300 * Math.random(), 55 * Math.random()]); ctx.beginPath() // ctx.font = "80px monospace"; // ctx.fillText("Ω", 2775 + 50, -650 - 50); ctx.strokeStyle = "gray"; ctx.arc(2775 + 50, -650 - 50, 80 * Math.abs(Math.sin(m.cycle * 0.09)) + 10, 0 + Math.tan(m.cycle * 0.1 * Math.PI), 0 + Math.tan(m.cycle * 0.1 + Math.PI)) ctx.shadowBlur = 10; ctx.shadowColor = "black"; ctx.stroke() //ctx.fill() ctx.setLineDash([]); ctx.restore() } }; level.setPosToSpawn(30, -2000); //normal spawn level.exit.x = 1375; level.exit.y = -1500; level.defaultZoom = 1800 simulation.zoomTransition(level.defaultZoom + 800) document.body.style.backgroundColor = "#d8dadf"; spawn.mapRect(-225, -1950, 350, 75); spawn.mapRect(225, -1950, 50, 75); spawn.mapRect(-250, -2025, 50, 150); spawn.mapRect(250, -2025, 50, 150); spawn.mapRect(-250, -2250, 50, 125); spawn.mapRect(-225, -2325, 500, 100); spawn.mapRect(250, -2250, 50, 125); spawn.mapRect(-100, -2400, 250, 100); spawn.mapRect(-25, -2475, 100, 100); spawn.mapRect(125, -2350, 50, 50); spawn.mapRect(-125, -2350, 50, 50); spawn.mapRect(-50, -2425, 50, 50); spawn.mapRect(50, -2425, 50, 50); spawn.mapRect(-250, -2350, 50, 50); spawn.mapRect(250, -2350, 50, 50); spawn.mapRect(-75, -1975, 200, 50); spawn.mapRect(-50, -2000, 150, 50); spawn.mapRect(100, -1950, 50, 75); spawn.mapRect(-75, -2250, 200, 50); spawn.mapRect(-50, -2225, 150, 50); spawn.mapRect(-2950, -1900, 3100, 900); spawn.mapRect(225, -1900, 2875, 900); spawn.mapRect(-2950, -600, 6050, 450); spawn.mapRect(-3050, -500, 200, 350); spawn.mapRect(-3150, -400, 200, 250); spawn.mapRect(-3250, -300, 200, 150); spawn.mapRect(2950, -1050, 150, 500); spawn.mapRect(-4675, -1900, 1825, 200); spawn.mapRect(-5325, -1900, 675, 900); spawn.mapRect(-5325, -250, 2100, 100); spawn.mapRect(-5325, -600, 675, 450); // - spawn.mapRect(-4700, -500, 150, 350); spawn.mapRect(-4650, -400, 200, 250); spawn.mapRect(-4550, -300, 200, 150); spawn.mapRect(-3875, -1025, 100, 100); spawn.mapRect(-3800, -1050, 50, 50); spawn.mapRect(-3900, -1050, 50, 50); spawn.mapRect(-3800, -950, 50, 50); spawn.mapRect(-3900, -950, 50, 50); spawn.mapRect(-6925, -1175, 1700, 175); spawn.mapRect(-6925, -600, 1725, 175); spawn.mapRect(-7700, -600, 800, 425);// - spawn.mapRect(-7800, -2950, 175, 2775); spawn.mapRect(-7075, -2950, 175, 1950); spawn.mapRect(-9150, -2975, 1525, 175); spawn.mapRect(-7075, -2975, 1150, 175); spawn.mapRect(-6100, -3900, 175, 1100); spawn.mapRect(-9150, -3975, 3225, 175); spawn.mapRect(-9175, -3850, 75, 75); spawn.mapRect(-9625, -3825, 500, 150); spawn.mapRect(-9650, -3725, 75, 75); spawn.mapRect(-10100, -3700, 500, 150); spawn.mapRect(-10100, -2975, 975, 175); spawn.mapRect(-10125, -3600, 75, 75); spawn.mapRect(-10575, -3575, 500, 150); spawn.mapRect(-10575, -2975, 500, 175); spawn.mapRect(-11325, -2975, 250, 175); spawn.mapRect(-11325, -3575, 175, 775); // spawn.mapRect(-11325, -3575, 800, 150); spawn.mapRect(-11225, -2975, 150, 2000); spawn.mapRect(-10575, -2975, 150, 2500); spawn.mapRect(-11650, -550, 1225, 150); spawn.mapRect(-11650, -1100, 575, 150); spawn.mapRect(-14675, -2525, 2925, 150); spawn.mapRect(-11900, -2525, 150, 1575); spawn.mapRect(-11850, -1100, 250, 150); spawn.mapRect(-11875, -550, 275, 150); spawn.mapRect(-11900, -550, 150, 350); spawn.mapRect(-14675, -2525, 150, 2300); spawn.mapRect(-14675, -375, 2925, 175); spawn.mapRect(2725, -625, 250, 50); spawn.mapRect(2625, -1025, 100, 225); spawn.mapRect(2700, -1025, 300, 125); spawn.mapRect(2625, -612.5, 125, 50); spawn.mapRect(-3950, -1725, 250, 50); spawn.mapRect(-7650, -3825, 600, 50); spawn.mapRect(-13900, -2400, 200, 50); spawn.mapVertex(-11957, -430, '-175 175 0 175 0 0'); spawn.mapVertex(-14470, -430, '175 175 0 175 0 0'); spawn.mapVertex(-11957, -2319, '-175 -175 0 -175 0 0'); spawn.mapVertex(-14470, -2319, '0 0 0 -175 175 -175'); //spawn.mapRect(-13900, -2150, 1375, 125); // const sword = function() { //the ultimate blade of las destruction // NO SWORD NO DROPS >:( // mobs.spawn(player.position.x, player.position.y, 5, 30, "transparent"); // let me = mob[mob.length - 1]; // Matter.Body.setDensity(me, 1); // me.vertices = Matter.Vertices.rotate(me.vertices, Math.PI, me.position); // me.collisionFilter.category = cat.bullet; // me.collisionFilter.mask = cat.mob | cat.mobBullet; // me.isDropPowerUp = false; // me.isShielded = true; // me.showHealthBar = false; // me.isUnblockable = true; // me.leaveBody = false; // me.isBadTarget = true; // me.stroke = "transparent"; // me.isSword = true; // let index = 0; // let radius = 50; // // const len = cons.length; // // cons[len] = Constraint.create({ // // pointA: player.position, // // bodyB: me, // // stiffness: 0.14, // // damping: 0.5 // // }); // // setTimeout(function(){ // // Composite.add(engine.world, cons[cons.length - 1]); // // }, 1) // // cons[len].length = 100 + 1.5 * radius; // me.do = function() { // // this.health = Infinity;//just in case // // for(let i = 0; i < mob.length; i++) { // // if(Matter.Query.collides(this, [mob[i]]).length > 0 && !mob[i].isSword) { // // const dmg = 0.25;//do not nerf // // mob[i].damage(dmg, true); // // simulation.drawList.push({ //add dmg to draw queue // // x: mob[i].position.x, // // y: mob[i].position.y, // // radius: Math.sqrt(dmg) * 50, // // color: simulation.mobDmgColor, // // time: simulation.drawTime // // }); // // break // // } // // } // Matter.Body.setPosition(this, { // x: player.position.x + Math.cos(m.angle) * 100, // y: player.position.y - (input.down ? 0 : 30) + Math.sin(m.angle) * 100 // }) // // this.force.x = Math.cos(m.angle) * 350; // // this.force.y = Math.sin(m.angle) * 350; // Matter.Body.setAngle(this, m.angle + Math.PI * 2); // const setNoseShape = () => { // const mag = radius + radius * 10; // this.vertices[2].x = this.position.x + Math.cos(this.angle) * mag; // this.vertices[2].y = this.position.y + Math.sin(this.angle) * mag; // this.vertices[4].x = this.position.x + Math.cos(this.angle) * mag; // this.vertices[4].y = this.position.y + Math.sin(this.angle) * mag; // this.vertices[0].x = this.position.x + Math.cos(this.angle) * mag; // this.vertices[0].y = this.position.y + Math.sin(this.angle) * mag; // }; // const spike = Vector.mult(Vector.normalise(Vector.sub(this.vertices[2], this.position)), radius * 100) // const spike2 = Vector.mult(Vector.normalise(Vector.sub(this.vertices[4], this.position)), radius * 500) // const spike3 = Vector.mult(Vector.normalise(Vector.sub(this.vertices[0], this.position)), radius * 500) // this.vertices[2].x = this.position.x + spike.x / 100 // this.vertices[2].y = this.position.y + spike.y / 100 // this.vertices[4].x = this.position.x + spike2.x / 75 // this.vertices[4].y = this.position.y + spike2.y / 75 // this.vertices[0].x = this.position.x + spike3.x / 75 // this.vertices[0].y = this.position.y + spike3.y / 75 // if(index == 0) { // setNoseShape(); // index++; // } // ctx.save() // ctx.beginPath(); // const vertices = this.vertices; // ctx.lineWidth = 100; // ctx.moveTo(vertices[0].x, vertices[0].y); // for (let j = 1, len = vertices.length; j < len; ++j) ctx.lineTo(vertices[j].x, vertices[j].y); // ctx.lineTo(vertices[0].x, vertices[0].y); // const gradient = ctx.createRadialGradient(this.position.x, this.position.y, 15, this.position.x, this.position.y, Math.abs(275 * Math.sin(simulation.cycle / 50)) + 15); // // Add three color stops // gradient.addColorStop(0, m.eyeFillColor); // gradient.addColorStop(0.9, "white"); // gradient.addColorStop(1, "darkgray"); // ctx.fillStyle = gradient; // ctx.strokeStyle = "transparent"; // ctx.shadowBlur = 10; // ctx.shadowColor = m.eyeFillColor; // ctx.fill(); // ctx.stroke(); // ctx.restore() // const Dx = Math.cos(m.angle); // const Dy = Math.sin(m.angle); // let xElec = this.position.x + 10 * Dx; // let yElec = this.position.y + 10 * Dy; // ctx.beginPath(); // ctx.moveTo(xElec, yElec); // const step = 40 // for (let i = 0; i < 6; i++) { // xElec += step * (Dx + 1.5 * (Math.random() - 0.5)) // yElec += step * (Dy + 1.5 * (Math.random() - 0.5)) // ctx.lineTo(xElec, yElec); // } // ctx.strokeStyle = m.eyeFillColor; // ctx.lineWidth = 1.5; // ctx.setLineDash([70 + 300 * Math.random(), 55 * Math.random()]); // ctx.stroke(); // Draw it // ctx.setLineDash([]); // if(this.alive && m.energy > 0) { // const gradient = ctx.createRadialGradient(this.position.x, this.position.y, 0, this.position.x, this.position.y, Math.abs(20 * Math.cos(simulation.cycle / 50))); // // Add three color stops // gradient.addColorStop(0, m.eyeFillColor); // gradient.addColorStop(0.9, "white"); // gradient.addColorStop(1, "gray"); // ctx.save() // ctx.beginPath() // ctx.moveTo(this.position.x, this.position.y) // ctx.arc(this.position.x, this.position.y, 20, 0, 2 * Math.PI) // ctx.fillStyle = gradient; // ctx.strokeStyle = "transparent"; // ctx.shadowBlur = 10; // ctx.shadowColor = m.eyeFillColor; // ctx.fill() // ctx.stroke() // ctx.restore() // m.energy -= 0.002; // ctx.save() // ctx.translate(this.vertices[2].x, this.vertices[2].y) // ctx.rotate(m.angle + Math.PI / 2) // ctx.beginPath() // ctx.font = "16px Arial"; // ctx.fillStyle = "black"; // ctx.strokeStyle = "black"; // // ctx.fillText("Θ", 0,0 - 110) // // ctx.fillText("ά", 0,15 - 110) // // ctx.fillText("ν", 0,30 - 110) // // ctx.fillText("α", 0,45 - 110) // // ctx.fillText("τ", 0,60 - 110) // // ctx.fillText("ο", 0,75 - 110) // // ctx.fillText("ς", 0,90 - 110) // ctx.fillText("Ω", 0,55) // ctx.fill() // ctx.stroke() // ctx.restore() // simulation.drawList.push({ // x: this.position.x + Math.floor(Math.random() * 300 - Math.random() * 300), // y: this.position.y + Math.floor(Math.random() * 300 - Math.random() * 300), // radius: 2, // color: m.eyeFillColor, // time: simulation.drawTime // }); // } else { // this.death() // powerUps.activated = false // } // // me.onDeath = function() { // // this.removeCons(); // // }; // } // } //setTimeout(function() {sword();}, 100); const wire = function () { const breakingPoint = -1600; const spawnx = -13800 + Math.floor(Math.random() * 100 - Math.random() * 100); mobs.spawn(spawnx, -2375, 0, 2, "transparent"); let me = mob[mob.length - 1]; let boss = mob[0]; me.collisionFilter.category = cat.body; me.collisionFilter.mask = cat.map; me.g = 0.0003; //required for gravity me.restitution = 0; me.stroke = "transparent" me.freeOfWires = false; me.frictionAir = 0.01; me.isDropPowerUp = false; me.showHealthBar = false; me.isBadTarget = true; me.isUnblockable = true; const wireX = spawnx; const wireY = -2375; //const randomw = Math.floor(Math.random() * 100 - Math.random() * 100); const width = Math.abs(10 + Math.floor(Math.random() * 10 - Math.random() * 10)); const randomx = Math.floor(30 * Math.random() - 30 * Math.random()); const randomy = Math.floor(10 * Math.random() - 10 * Math.random()) me.do = function () { if (this.freeOfWires) { this.gravity(); } else { if (boss.position.y > breakingPoint) { this.freeOfWires = true; this.force.y -= -0.0006; this.force.x += Math.random() * boss.velocity.x / 10000; this.fill = "#111"; } //move mob to mob Matter.Body.setPosition(this, { x: boss.position.x + randomx, y: boss.position.y + randomy }) } //draw wire ctx.beginPath(); ctx.moveTo(wireX, wireY); ctx.quadraticCurveTo(wireX, -100, this.position.x, this.position.y); ctx.lineWidth = width; ctx.lineCap = "butt"; ctx.strokeStyle = "#111"; ctx.stroke(); ctx.lineCap = "round"; }; } const ball = function (x, y, radius = 11 * tech.bulletSize, sides = 70) {//superball //also, why is it called superballs? mobs.spawn(x, y, sides, radius, "rgba(0,0,0)"); let me = mob[mob.length - 1]; me.stroke = "transparent"; me.onHit = function () { simulation.drawList.push({ //add dmg to draw queue x: this.position.x, y: this.position.y, radius: 20, color: simulation.mobDmgColor, time: simulation.drawTime }); }; Matter.Body.setDensity(me, 0.00001); //normal is 0.001 me.timeLeft = 500; me.friction = 0; me.restitution = 1; me.leaveBody = false; me.isDropPowerUp = false; //me.inertia = Infinity; me.isBadTarget = true; me.isMobBullet = true; me.showHealthBar = false; me.collisionFilter.category = cat.mobBullet; me.collisionFilter.mask = cat.bullet | cat.player | cat.map | cat.body; let index = 0; me.do = function () { this.timeLimit(); this.alwaysSeePlayer() this.force.y += this.mass * 0.0012; } } const normalBullet = function (x, y, radius = 9, sides = 3) { //bullets mobs.spawn(x, y, sides, radius, "rgba(0,0,0)"); let me = mob[mob.length - 1]; me.stroke = "transparent"; me.vertices = Matter.Vertices.rotate(me.vertices, Math.PI, me.position); me.onHit = function () { simulation.drawList.push({ //add dmg to draw queue x: this.position.x, y: this.position.y, radius: 20, color: simulation.mobDmgColor, time: simulation.drawTime }); }; Matter.Body.setDensity(me, 0.00004); //normal is 0.001 me.timeLeft = 220; me.frictionAir = -0.01; me.restitution = -1; me.leaveBody = false; me.isDropPowerUp = false; //me.inertia = Infinity; me.isBadTarget = true; me.isMobBullet = true; me.showHealthBar = false; me.collisionFilter.category = cat.mobBullet; me.collisionFilter.mask = null; me.boss = null; for (let i = 0, len = mob.length; i < len; i++) { if (mob[i].restoreBoss) { me.boss = mob[i]; } } let index = 0; me.do = function () { this.timeLimit(); this.alwaysSeePlayer() const setNoseShape = () => { const mag = this.radius + this.radius * 10; this.vertices[1].x = this.position.x + Math.cos(this.angle) * mag; this.vertices[1].y = this.position.y + Math.sin(this.angle) * mag; const angle = Math.atan2(this.position.y - me.boss.position.y, this.position.x - me.boss.position.x); Matter.Body.setAngle(this, angle); }; const spike = Vector.mult(Vector.normalise(Vector.sub(this.vertices[1], this.position)), radius * 1000) this.vertices[1].x = this.position.x + spike.x / 100 this.vertices[1].y = this.position.y + spike.y / 100 if (index == 0) { setNoseShape(); index++; } if (Matter.Query.collides(this, map).length > 0 || Matter.Query.collides(this, body).length > 0) { const slow = 0.69 //im sorry it looks cool though Matter.Body.setVelocity(this, { x: this.velocity.x * slow, y: this.velocity.y * slow }); simulation.drawList.push({ //add dmg to draw queue x: this.position.x, y: this.position.y, radius: 10, color: '#000000', time: simulation.drawTime }); if (this.velocity.x == 0 && this.velocity.y == 0) { this.death(); } this.frictionAir += 0.0001; Matter.Body.setAngularVelocity(this, 0) } if (Matter.Query.collides(this, [player]).length > 0 && !(m.isCloak && tech.isIntangible) && m.immuneCycle < m.cycle) { m.immuneCycle = m.cycle + m.collisionImmuneCycles; //player is immune to damage for 30 cycles // I wasnt gonna add this but since ya'll would have killed me if I didn't I added this const dmg = 0.013 * simulation.dmgScale; m.damage(dmg); simulation.drawList.push({ //add dmg to draw queue x: this.position.x, y: this.position.y, radius: Math.sqrt(dmg) * 200, color: '#000000', time: simulation.drawTime }); } }; } const foamBullet = function (x, y, radius = 9, sides = 70) { //bullets mobs.spawn(x, y, sides, radius, "rgb(0,0,0)"); let me = mob[mob.length - 1]; me.stroke = "transparent"; Matter.Body.setDensity(me, 0.00005); //normal is 0.001 me.timeLeft = 120; // me.g = 0.0005; //required if using this.gravity me.accelMag = 0.00006; me.isVerticesChange = true me.delay = 360 * simulation.CDScale; me.spikeVertex = 0; me.spikeLength = 0; me.isSpikeGrowing = false; me.spikeGrowth = 0; me.isSpikeReset = false; me.frictionAir = 0; me.restitution = 0; me.leaveBody = false; me.isDropPowerUp = false; me.isBadTarget = true; me.isMobBullet = true; me.showHealthBar = false; me.isUnblockable = true; me.collisionFilter.category = cat.mobBullet; me.collisionFilter.mask = cat.body //| cat.bullet;// | cat.player; me.do = function () { if (this.distanceToPlayer2() < 40000) { this.force = Vector.mult(Vector.normalise(Vector.sub(player.position, this.position)), this.mass * 0.004) const slow = 0.99999999999999999; Matter.Body.setVelocity(this, { x: this.velocity.x * slow, y: this.velocity.y * slow }); } // this.gravity(); this.timeLimit(); // for (let i = 0, len = this.vertices.length; i < len; i++) { // const dist = Vector.sub(this.seePlayer.position, this.vertices[i]); // const distMag = Vector.magnitude(dist); // const spike = Vector.mult(Vector.normalise(Vector.sub(this.vertices[i], this.position)), radius * distMag) // this.vertices[i].x = this.position.x + spike.x / 100 // this.vertices[i].y = this.position.y + spike.y / 100 // } if (this.radius < 50) { const scale = 1.05; Matter.Body.scale(this, scale, scale); this.radius *= scale; } if (Matter.Query.collides(this, map).length > 0 || Matter.Query.collides(this, body).length > 0 && this.speed < 10) { const slow = 0.97 Matter.Body.setVelocity(this, { x: this.velocity.x * slow, y: this.velocity.y * slow }); const SCALE = 0.9 Matter.Body.scale(this, SCALE, SCALE); this.radius *= SCALE; if (this.radius < 1) { this.death() } } else { this.attach(); } }; me.attach = function () { if (Matter.Query.collides(this, [player]).length > 0) { Matter.Body.setPosition(this, player.position) if (player.speed > 2.5) Matter.Body.setVelocity(player, Vector.mult(player.velocity, 0.94)) Matter.Body.setAngularVelocity(player, player.angularVelocity * 0.9); m.damage(0.00003); //balanced? not sure } } }; const orbital = function (who, radius, phase, speed, radius2) {//basically orbitBot mobs.spawn(who.position.x, who.position.y, 8, 12, "rgba(0,0,0, 1)"); let me = mob[mob.length - 1]; me.stroke = "transparent"; Matter.Body.setDensity(me, 0.01); //normal is 0.001 me.leaveBody = false; me.isDropPowerUp = false; me.isBadTarget = true; me.showHealthBar = false; me.isOrbital = true; me.isShielded = true me.collisionFilter.category = cat.mobBullet; me.collisionFilter.mask = cat.bullet; //cat.player | cat.map | cat.body me.do = function () { //if host is gone if (!who || !who.alive) { this.death(); return } //set orbit const time = simulation.cycle * speed + phase const orbit = { x: Math.cos(time), y: Math.sin(time) } Matter.Body.setPosition(this, Vector.add(Vector.add(who.position, who.velocity), Vector.mult(orbit, radius + radius2))) //damage player if (Matter.Query.collides(this, [player]).length > 0 && !(m.isCloak && tech.isIntangible) && m.immuneCycle < m.cycle) { m.immuneCycle = m.cycle + m.collisionImmuneCycles; //player is immune to damage for 30 cycles const dmg = 0.013 * simulation.dmgScale m.damage(dmg); simulation.drawList.push({ //add dmg to draw queue x: this.position.x, y: this.position.y, radius: Math.sqrt(dmg) * 200, color: simulation.mobDmgColor, time: simulation.drawTime }); //this.death(); } }; } const missile = function (where, speed = 0.007, size = 1) { mobs.spawn(where.x, where.y, 3, 20, '#000000'); let me = mob[mob.length - 1]; me.accelMag = speed; Matter.Body.setDensity(me, 0.0000000000001); me.stroke = 'transparent'; me.showHealthBar = false; me.collisionFilter.category = cat.mobBullet; me.collisionFilter.mask = cat.bullet | cat.player | cat.map | cat.body; me.leaveBody = false; me.isDropPowerUp = false; me.onHit = function () { b.explosion(this.position, (tech.isMissileBig ? 230 : 180) + 60 * Math.random()) this.death() } me.do = function () { this.seePlayerByHistory(); this.alwaysSeePlayer(); this.attraction(); const angle = Math.atan2(this.seePlayer.position.y - this.position.y, this.seePlayer.position.x - this.position.x); Matter.Body.setAngle(this, angle); // ctx.beginPath() // ctx.strokeStyle = "rgba(255,155,0,0.5)"; // ctx.lineWidth = 15; // ctx.arc(this.vertices[0].x, this.vertices[0].x, 15, 0, 2 * Math.PI); // ctx.stroke() simulation.drawList.push({ //add dmg to draw queue x: this.position.x, y: this.position.y, radius: 10, color: `rgba(255,155,0,${Math.random()})`, time: simulation.drawTime }); } } const railBullet = function (x, y) { mobs.spawn(x, y, 5, 20, "black"); let me = mob[mob.length - 1]; me.stroke = "black"; me.vertices = Matter.Vertices.rotate(me.vertices, Math.PI, me.position); me.onHit = function () { simulation.drawList.push({ //add dmg to draw queue x: this.position.x, y: this.position.y, radius: 20, color: simulation.mobDmgColor, time: simulation.drawTime }); }; Matter.Body.setDensity(me, 0.00004); //normal is 0.001 me.timeLeft = 220; me.frictionAir = -0.01; me.restitution = -1; me.leaveBody = false; me.isDropPowerUp = false; //me.inertia = Infinity; me.isBadTarget = true; me.isMobBullet = true; me.showHealthBar = false; me.collisionFilter.category = cat.mobBullet; me.collisionFilter.mask = cat.player; const radius = 30; let index = 0; me.do = function () { this.timeLimit(); this.alwaysSeePlayer() const setNoseShape = () => { const mag = me.radius + me.radius * 10; const angle = Math.atan2(me.seePlayer.position.y - this.position.y, me.seePlayer.position.x - this.position.x); me.vertices[2].x = this.position.x + Math.cos(this.angle) * mag; me.vertices[2].y = this.position.y + Math.sin(this.angle) * mag; me.vertices[4].x = this.position.x + Math.cos(this.angle) * mag; me.vertices[4].y = this.position.y + Math.sin(this.angle) * mag; me.vertices[0].x = this.position.x + Math.cos(this.angle) * mag; me.vertices[0].y = this.position.y + Math.sin(this.angle) * mag; Matter.Body.setAngle(this, angle); }; const spike = Vector.mult(Vector.normalise(Vector.sub(this.vertices[2], me.position)), radius * 1000) const spike2 = Vector.mult(Vector.normalise(Vector.sub(this.vertices[4], me.position)), radius * 1000) const spike3 = Vector.mult(Vector.normalise(Vector.sub(this.vertices[0], me.position)), radius * 1000) me.vertices[2].x = this.position.x + spike.x / 100 me.vertices[2].y = this.position.y + spike.y / 100 me.vertices[4].x = this.position.x + spike2.x / 75 me.vertices[4].y = this.position.y + spike2.y / 75 me.vertices[0].x = this.position.x + spike3.x / 75 me.vertices[0].y = this.position.y + spike3.y / 75 if (index == 0) { setNoseShape(); index++; } if (Matter.Query.collides(this, map).length > 0 || Matter.Query.collides(this, body).length > 0) { const slow = 0.69 //im sorry it looks cool though Matter.Body.setVelocity(this, { x: this.velocity.x * slow, y: this.velocity.y * slow }); // simulation.drawList.push({ //add dmg to draw queue // x: this.position.x, // y: this.position.y, // radius: 10, // color: '#000000', // time: simulation.drawTime // }); if (this.velocity.x == 0 && this.velocity.y == 0) { this.death(); } this.frictionAir += 0.0001; Matter.Body.setAngularVelocity(this, 0) } if (Matter.Query.collides(this, [player]).length > 0 && !(m.isCloak && tech.isIntangible) && m.immuneCycle < m.cycle) { m.immuneCycle = m.cycle + m.collisionImmuneCycles; //player is immune to damage for 30 cycles // I wasnt gonna add this but since ya'll would have killed me if I didn't I added this const dmg = 0.013 * simulation.dmgScale; m.damage(dmg); // simulation.drawList.push({ //add dmg to draw queue // x: this.position.x, // y: this.position.y, // radius: Math.sqrt(dmg) * 200, // color: '#000000', // time: simulation.drawTime // }); } }; } const grenade = function (x, y, lifeSpan = 90 + Math.ceil(60 / simulation.accelScale), pulseRadius = Math.min(550, 250 + simulation.difficulty * 3), size = 3) { mobs.spawn(x, y, 4, size, "rgb(0,0,0)"); //rgb(215,80,190) let me = mob[mob.length - 1]; me.stroke = "transparent"; me.onHit = function () { this.explode(this.mass); }; Matter.Body.setDensity(me, 0.00004); //normal is 0.001 me.lifeSpan = lifeSpan; me.timeLeft = me.lifeSpan; // me.g = 0.0002; //required if using this.gravity me.frictionAir = 0; me.restitution = 0.8; me.leaveBody = false; me.isDropPowerUp = false; me.isBadTarget = true; me.isMobBullet = true; me.onDeath = function () { //damage player if in range if (Vector.magnitude(Vector.sub(player.position, this.position)) < pulseRadius && m.immuneCycle < m.cycle) { m.immuneCycle = m.cycle + m.collisionImmuneCycles; //player is immune to damage m.damage(0.015 * simulation.dmgScale); } simulation.drawList.push({ //add dmg to draw queue x: this.position.x, y: this.position.y, radius: pulseRadius, color: "rgba(0,0,0,0.3)", time: simulation.drawTime }); }; me.showHealthBar = false; me.collisionFilter.category = cat.mobBullet; me.collisionFilter.mask = cat.map | cat.body | cat.player // me.collisionFilter.mask = 0 me.do = function () { this.timeLimit(); ctx.save() ctx.beginPath(); //draw explosion outline ctx.arc(this.position.x, this.position.y, pulseRadius * (1.01 - this.timeLeft / this.lifeSpan), 0, 2 * Math.PI); //* this.fireCycle / this.fireDelay ctx.fillStyle = "rgba(0,0,0,0.05)"; ctx.fill(); ctx.restore() }; } const sniper = function (x, y, radius = 30) { mobs.spawn(x, y, 8, radius, '#00000000'); let me = mob[mob.length - 1]; me.accelMag = 0.0003 me.stroke = 'transparent'; //me.isBoss = true; me.searchTarget = map[Math.floor(Math.random() * (map.length - 1))].position; me.frictionStatic = 0; me.friction = 0; me.seeAtDistance2 = 20000000 //14000 vision range me.vertices = Matter.Vertices.rotate(me.vertices, Math.PI, me.position); Matter.Body.rotate(me, Math.random() * Math.PI * 2); me.showHealthBar = false Matter.Body.setDensity(me, 0.01) me.fireDir = { x: 0, y: 0 } me.seePlayerFreq = 0 me.repulsionRange = 400000 + radius * radius; me.collisionFilter.mask = cat.bullet | cat.player | cat.body | cat.map | cat.mob | cat.mobBullet me.do = function () { this.seePlayerCheck(); this.attraction(); this.repulsion(); this.search() if (this.seePlayer.recall) { const h = this.radius * 0.3; const w = this.radius * 2; const x = this.position.x - w / 2; const y = this.position.y - w * 0.7; ctx.fillStyle = "rgba(100, 100, 100, 0.3)"; ctx.fillRect(x, y, w, h); ctx.fillStyle = "rgba(0,255,255,0.7)"; ctx.fillRect(x, y, w * this.health, h); } if (this.health < 1) { this.health += 0.0005; //regen } ctx.save() ctx.translate(this.position.x, this.position.y) ctx.rotate(Math.atan2(this.seePlayer.position.y - this.position.y, this.seePlayer.position.x - this.position.x)) if (this.seePlayer.recall > 0 && this.distanceToPlayer2() > this.repulsionRange) { var grd2 = ctx.createLinearGradient(0, 0, -150, 0); // grd2.addColorStop(0, 'rgba(255, 255, 155, 0.8)'); // grd2.addColorStop(1, 'rgba(255, 200, 0, 0.1)'); grd2.addColorStop(0, 'rgba(150, 200, 255, 0.7)'); grd2.addColorStop(1, 'rgba(150, 200, 255, 0)'); ctx.fillStyle = grd2; ctx.beginPath(); ctx.moveTo(-18, -25); //10 * (Math.random() - 0.5), 10 * (Math.random() - 0.5) ctx.lineTo(-18, 25); ctx.lineTo(-50 - 100 * Math.random(), 0); ctx.fill(); } else if (this.distanceToPlayer2() < this.repulsionRange) { var grd2 = ctx.createLinearGradient(0, 0, 80, 0); grd2.addColorStop(0, 'rgba(150, 200, 255, 0.7)'); grd2.addColorStop(1, 'rgba(150, 200, 255, 0)'); ctx.fillStyle = grd2; ctx.beginPath(); ctx.moveTo(20, -16); //10 * (Math.random() - 0.5), 10 * (Math.random() - 0.5) ctx.lineTo(20, 16); ctx.lineTo(35 + 43 * Math.random(), 0); ctx.fill(); } ctx.restore() ctx.save() ctx.translate(this.position.x, this.position.y) ctx.rotate(Math.atan2(this.seePlayer.position.y - this.position.y, this.seePlayer.position.x - this.position.x)) ctx.beginPath(); ctx.arc(0, 0, 30, 0, 2 * Math.PI); ctx.fillStyle = m.bodyGradient ctx.fill(); ctx.arc(15, 0, 4, 0, 2 * Math.PI); ctx.strokeStyle = "#333"; ctx.lineWidth = 2; ctx.stroke(); ctx.beginPath(); //eye ctx.arc(15, 0, 3.5, 0, 2 * Math.PI); ctx.fillStyle = `rgba(255, 0, 0, ${this.health * this.health})`; ctx.fill() ctx.restore() //set direction to turn to fire if (this.seePlayer.recall && !(simulation.cycle % 30)) { this.seePlayer.recall -= 10; this.fireDir = Vector.normalise(Vector.sub(this.seePlayer.position, this.position)); spawn.sniperBullet(this.position.x, this.position.y, 7 + Math.ceil(this.radius / 15), 5); const v = 10 + 8 * simulation.accelScale; Matter.Body.setVelocity(mob[mob.length - 1], { x: this.velocity.x + this.fireDir.x * v, y: this.velocity.y + this.fireDir.y * v }); } }; } const laserEM = function (x, y, radius = 30) { mobs.spawn(x, y, 8, radius, '#00000000'); let me = mob[mob.length - 1]; me.accelMag = 0.0003 me.stroke = 'transparent'; //me.isBoss = true; me.frictionStatic = 0; me.friction = 0; me.seeAtDistance2 = 20000000 //14000 vision range me.vertices = Matter.Vertices.rotate(me.vertices, Math.PI, me.position); Matter.Body.rotate(me, Math.random() * Math.PI * 2); me.showHealthBar = false Matter.Body.setDensity(me, 0.01) me.seePlayerFreq = 0 me.searchTarget = map[Math.floor(Math.random() * (map.length - 1))].position; me.swordDamage = 0.025 * simulation.dmgScale me.collisionFilter.mask = cat.bullet | cat.player | cat.body | cat.map | cat.mob | cat.mobBullet me.repulsionRange = 50000; me.do = function () { this.repulsion(); this.search() if (this.seePlayer.recall) { const h = this.radius * 0.3; const w = this.radius * 2; const x = this.position.x - w / 2; const y = this.position.y - w * 0.7; ctx.fillStyle = "rgba(100, 100, 100, 0.3)"; ctx.fillRect(x, y, w, h); ctx.fillStyle = "rgba(0,255,255,0.7)"; ctx.fillRect(x, y, w * this.health, h); } if (this.health < 1) { this.health += 0.0005; //regen } if (this.seePlayer.recall) { this.laserSword(this.position, Math.atan2(this.seePlayer.position.y - this.position.y, this.seePlayer.position.x - this.position.x), 500 * Math.random()); } ctx.save() ctx.translate(this.position.x, this.position.y) ctx.rotate(Math.atan2(this.seePlayer.position.y - this.position.y, this.seePlayer.position.x - this.position.x)) if (this.seePlayer.recall > 0 && this.distanceToPlayer2() > this.repulsionRange) { var grd2 = ctx.createLinearGradient(0, 0, -150, 0); // grd2.addColorStop(0, 'rgba(255, 255, 155, 0.8)'); // grd2.addColorStop(1, 'rgba(255, 200, 0, 0.1)'); grd2.addColorStop(0, 'rgba(150, 200, 255, 0.7)'); grd2.addColorStop(1, 'rgba(150, 200, 255, 0)'); ctx.fillStyle = grd2; ctx.beginPath(); ctx.moveTo(-18, -25); //10 * (Math.random() - 0.5), 10 * (Math.random() - 0.5) ctx.lineTo(-18, 25); ctx.lineTo(-50 - 100 * Math.random(), 0); ctx.fill(); } else if (this.distanceToPlayer2() < this.repulsionRange) { var grd2 = ctx.createLinearGradient(0, 0, 80, 0); grd2.addColorStop(0, 'rgba(150, 200, 255, 0.7)'); grd2.addColorStop(1, 'rgba(150, 200, 255, 0)'); ctx.fillStyle = grd2; ctx.beginPath(); ctx.moveTo(20, -16); //10 * (Math.random() - 0.5), 10 * (Math.random() - 0.5) ctx.lineTo(20, 16); ctx.lineTo(35 + 43 * Math.random(), 0); ctx.fill(); } ctx.restore() ctx.save() ctx.translate(this.position.x, this.position.y) ctx.rotate(Math.atan2(this.seePlayer.position.y - this.position.y, this.seePlayer.position.x - this.position.x)) ctx.beginPath(); ctx.arc(0, 0, 30, 0, 2 * Math.PI); ctx.fillStyle = m.bodyGradient ctx.fill(); ctx.arc(15, 0, 4, 0, 2 * Math.PI); ctx.strokeStyle = "#333"; ctx.lineWidth = 2; ctx.stroke(); ctx.beginPath(); //eye ctx.arc(15, 0, 3.5, 0, 2 * Math.PI); ctx.fillStyle = `rgba(255, 0, 0, ${this.health * this.health})`; ctx.fill() ctx.restore() this.seePlayerCheck(); this.attraction(); } me.laserSword = function (where, angle, length) { const sub = Vector.sub(this.seePlayer.position, this.position) const unit = Vector.normalise(sub) const path = [{ x: this.position.x + 20 * Math.cos(this.angle), y: this.position.y + 20 * Math.sin(this.angle) }, { x: this.position.x + (120 + 400) * Math.sqrt(Math.random()) * Math.cos(this.angle), y: this.position.y + (120 + 400) * Math.sqrt(Math.random()) * Math.sin(this.angle) } ]; this.seePlayer.recall -= 3; const vertexCollision = function (v1, v1End, domain) { for (let i = 0; i < domain.length; ++i) { let vertices = domain[i].vertices; const len = vertices.length - 1; for (let j = 0; j < len; j++) { results = simulation.checkLineIntersection(v1, v1End, vertices[j], vertices[j + 1]); if (results.onLine1 && results.onLine2) { const dx = v1.x - results.x; const dy = v1.y - results.y; const dist2 = dx * dx + dy * dy; if (dist2 < best.dist2 && (!domain[i].mob || domain[i].alive)) best = { x: results.x, y: results.y, dist2: dist2, who: domain[i], v1: vertices[j], v2: vertices[j + 1] }; } } results = simulation.checkLineIntersection(v1, v1End, vertices[0], vertices[len]); if (results.onLine1 && results.onLine2) { const dx = v1.x - results.x; const dy = v1.y - results.y; const dist2 = dx * dx + dy * dy; if (dist2 < best.dist2) best = { x: results.x, y: results.y, dist2: dist2, who: domain[i], v1: vertices[0], v2: vertices[len] }; } } }; best = { x: null, y: null, dist2: Infinity, who: null, v1: null, v2: null }; const look = { x: where.x + length * Math.cos(angle), y: where.y + length * Math.sin(angle) }; // vertexCollision(where, look, body); // vertexCollision(where, look, mob); vertexCollision(where, look, map); if (!m.isCloak) vertexCollision(where, look, [player]); if (best.who && (best.who === player) && m.immuneCycle < m.cycle) { m.immuneCycle = m.cycle + m.collisionImmuneCycles; //player is immune to damage for an extra second m.damage(this.swordDamage); simulation.drawList.push({ //add dmg to draw queue x: best.x, y: best.y, radius: this.swordDamage * 1500, color: "rgba(80,0,255,0.5)", time: 20 }); } if (best.dist2 === Infinity) best = look; ctx.beginPath(); //draw beam ctx.moveTo(where.x, where.y); ctx.lineTo(best.x, best.y); ctx.strokeStyle = "#50f"; ctx.lineWidth = 1.5; ctx.setLineDash([70 + 300 * Math.random(), 55 * Math.random()]); ctx.stroke(); // Draw it ctx.setLineDash([]); ctx.lineWidth = 20; ctx.strokeStyle = "rgba(80,0,255,0.07)"; ctx.stroke(); // Draw it const Dx = Math.cos(Math.atan2(this.seePlayer.position.y - this.position.y, this.seePlayer.position.x - this.position.x)); const Dy = Math.sin(Math.atan2(this.seePlayer.position.y - this.position.y, this.seePlayer.position.x - this.position.x)); let xElec = this.position.x + 40 * Dx; let yElec = this.position.y + 40 * Dy; ctx.beginPath(); ctx.moveTo(xElec, yElec); const step = 40 for (let i = 0; i < 6; i++) { xElec += step * (Dx + 1.5 * (Math.random() - 0.5)) yElec += step * (Dy + 1.5 * (Math.random() - 0.5)) ctx.lineTo(xElec, yElec); } ctx.strokeStyle = "#50f"; ctx.lineWidth = 1.5; ctx.setLineDash([70 + 300 * Math.random(), 55 * Math.random()]); ctx.stroke(); // Draw it ctx.setLineDash([]); } }; // if(powerUps.pass == undefined) { // let pass = {pass:true, activated:false}; // Object.assign(powerUps, pass) // } // const loadOut = { // loadOut: { // name: "loadOut", // color: "#000000", //"hsl(248,100%,65%)", // size() { return 40 }, // effect() { // if(m.alive) { // // tech.damage *= 2; // let text = ""; // if (!tech.isSuperDeterminism) { text += `
${tech.isCancelTech ? "?":"✕"}
`; }; // text += `

Blessing Of Sal

`; // text += `
  Speed Boost
Increase speed by 5%
`; // text += `
  Defense Boost
Reduce damage by 5%
`; // text += `
  Damage Boost
Increase damage by 10%
`; // if(powerUps.pass == true) { // text += `
  Blade of Sal
Press Shift to summon the Mythical Las Slayer
Drains Energy
`; // } // document.getElementById("choose-grid").innerHTML = text; // powerUps.showDraft();//no known bugs ig idk, im keep this as it is // } // }, // choose(index) { // if(index == 1) { // tech.squirrelFx += 0.25; // tech.squirrelJump += 0.1; // m.setMovement(); // powerUps.endDraft("buff"); // } else if(index == 2) { // simulation.dmgScale *= 0.95; // powerUps.endDraft("buff"); // } else if(index == 3) { // m.dmgScale *= 1.1; // powerUps.endDraft("buff"); // } else if(index == 4) { //sword! // powerUps.pass = false; // addEventListener("keydown", function(event) { // if(event.key == "Shift" && powerUps.activated == false) { // sword() // powerUps.activated = true; // } else if(event.key == "Shift" && powerUps.activated == true) { // for(let i = 0; i < mob.length; i++) { // if(mob[i].isSword) { // mob[i].death() // } // powerUps.activated = false; // } // } // }) // powerUps.endDraft("buff"); // } // } // } // } // Object.assign(powerUps, loadOut) const restoreBoss = function (x, y, radius = 30) { //ATTENTION LANDGREEN: RESTOREBOSS WILL NOT DROP ANY TECH, NOR WILL THERE BE ANY IN THE MAP. DO NOT ADD ANY TECH TO MY MAP mobs.spawn(x, y, 8, radius, 'transparent'); let me = mob[mob.length - 1]; me.stroke = 'transparent'; let aim = '#FFFFFF'; me.accelMag = 0.0006 me.isBoss = true; me.restoreBoss = true; me.frictionStatic = 0; me.friction = 0; me.seeAtDistance2 = 20000000 //14000 vision range me.vertices = Matter.Vertices.rotate(me.vertices, Math.PI, me.position); //make the pointy side of triangle the front Matter.Body.rotate(me, Math.random() * Math.PI * 2); me.showHealthBar = false Matter.Body.setDensity(me, m.maxHealth / (simulation.difficulty < 5 ? 0.5 : simulation.difficulty / simulation.difficultyMode)) me.seePlayerFreq = 0 me.swordDamage = 0.025 * simulation.dmgScale me.collisionFilter.mask = cat.bullet | cat.player | cat.body | cat.map | cat.mob | cat.mobBullet me.repulsionRange = 500000; me.isDropPowerUp = false; //Matter.Body.setVelocity(me, { x: 10 * (Math.random() - 0.5), y: 10 * (Math.random() - 0.5) }); let index = 0; me.curlRange = 10000; //xd me.fieldPhase = 0; me.energy = 1; me.maxEnergy = 1; me.immuneCycle = 0; me.cycle = 0; // me.onDeath = function() { // powerUps.spawn(this.position.x, this.position.y, "loadOut"); // } for (let i = 0; i < b.totalBots(); i++) { //normal orbitals look too boring, so... orbital(me, 190 + 130 * tech.isOrbitBotUpgrade, (index / b.totalBots()) * 2 * Math.PI, 0.05, Math.floor(Math.sin(simulation.cycle / 10) * 100)); //who, radius, phase, speed index++; } me.do = function () { if (this.position.x > -11500 && this.position.x < 10510) {// doesn't get one tapped by the elevator me.collisionFilter.mask = cat.bullet | cat.player | cat.map | cat.mob | cat.mobBullet; } else { me.collisionFilter.mask = cat.bullet | cat.player | cat.body | cat.map | cat.mob | cat.mobBullet; } this.cycle++; if ((Matter.Query.ray(map, player.position, this.position).length === 0) == false) { this.seePlayer.recall = null; } if (this.seePlayer.recall) { //fields if (m.fieldMode == 0 && this.distanceToPlayer2() < 200000) { if (Vector.magnitude(Vector.sub(m.pos, this.position)) - this.radius < m.fieldRange) { this.pushM(); } this.drawField(); // this.repel(); } if (m.fieldMode == 2) { if (this.distanceToPlayer2() < 200000) { if (Vector.magnitude(Vector.sub(m.pos, this.position)) - this.radius < m.fieldRange) { this.pushM(); } this.drawField() } if (tech.isPerfectBrake) { //cap player and bullet speed around restoreBoss //mobs basically can't hit you when you have this, so to make it fair... const wave = Math.cos(m.cycle * 0.022); const range = 200 + 140 * wave + 150 * m.energy const distance = Vector.magnitude(Vector.sub(this.position, m.pos)) const cap = this.immuneCycle > this.cycle ? 8 : 4 if (distance < range) { if (player.speed > cap && Vector.dot(player.velocity, Vector.sub(this.position, m.pos)) > 0) { // if velocity is directed towards player Matter.Body.setVelocity(player, Vector.mult(Vector.normalise(player.velocity), cap)); //set velocity to cap, but keep the direction } } for (let i = 0; i < bullet.length; i++) { const distance2 = Vector.magnitude(Vector.sub(this.position, bullet[i].position)) if (distance2 < range) { if (bullet[i].speed > cap && Vector.dot(bullet[i].velocity, Vector.sub(this.position, bullet[i].position)) > 0) { // if velocity is directed towards player Matter.Body.setVelocity(bullet[i], Vector.mult(Vector.normalise(bullet[i].velocity), cap)); //set velocity to cap, but keep the direction } } } ctx.beginPath(); ctx.arc(this.position.x, this.position.y, range, 0, 2 * Math.PI); ctx.fillStyle = "rgba(0,0,0,0.08)"; ctx.fill(); } } if (m.fieldMode == 5 && this.distanceToPlayer2() < 200000) { this.laserSword(this.position, Math.atan2(this.seePlayer.position.y - this.position.y, this.seePlayer.position.x - this.position.x), 500 * Math.random()); } if (m.fieldMode == 9) { if (this.distanceToPlayer2() < 300000) { this.teleportAway() //blink but reversed } } } if (m.immuneCycle > m.cycle) { me.damageReduction = 0; } else { me.damageReduction = 1; } this.repulsion(); this.seePlayerCheck(); this.attraction(); this.lostPlayer(); if (this.speed > 10) { // speed cap Matter.Body.setVelocity(this, { x: this.velocity.x * 0.99, y: this.velocity.y * 0.99 }); } if (this.seePlayer.recall) { const angle = Math.atan2(this.position.y - player.position.y, this.position.x - player.position.x); let positionR = { x: 500 * Math.cos(angle) + mob[0].position.x, y: 500 * Math.sin(angle) + mob[0].position.y }; let isWall = (Matter.Query.ray(map, mob[0].position, positionR).length === 0); let isBlock = (Matter.Query.ray(body, mob[0].position, positionR).length === 0); if (isWall == false || isBlock == false) { this.force.x -= Math.cos(angle) * 10; this.force.y -= Math.sin(angle) * 10; } } if (this.seePlayer.recall) { const angle = Math.atan2(this.position.y - player.position.y, this.position.x - player.position.x) - Math.PI * 1.5; let positionR = { x: 5000 * Math.cos(angle) + mob[0].position.x, y: 5000 * Math.sin(angle) + mob[0].position.y }; let isBullet = (Matter.Query.ray(bullet, mob[0].position, positionR).length === 0); if (isBullet == false) { this.force.x -= Math.cos(angle) * 30; this.force.y -= Math.sin(angle) * 30; } } if (this.seePlayer.recall) { const angle = Math.atan2(this.position.y - player.position.y, this.position.x - player.position.x) - Math.PI * 2.5; let positionR = { x: 5000 * Math.cos(angle) + mob[0].position.x, y: 5000 * Math.sin(angle) + mob[0].position.y }; let isBullet = (Matter.Query.ray(bullet, mob[0].position, positionR).length === 0); if (isBullet == false) { this.force.x -= Math.cos(angle) * 30; this.force.y -= Math.sin(angle) * 30; } } if (this.seePlayer.recall) { const h = this.radius * 0.3; const w = this.radius * 2; const x = this.position.x - w / 2; const y = this.position.y - w * 0.7; ctx.fillStyle = "rgba(10, 10, 10, 0.3)"; ctx.fillRect(x, y, w, h); ctx.fillStyle = "rgba(0,0,0,0.7)"; ctx.fillRect(x, y, w * this.energy, h); } if (this.health < 1) { this.health += 0.0001; //regen } else if (this.health < 1) { this.health += 0.00005; //reduced regen } if (this.energy < 0) {//energy thingy this.energy = 0; } else if (this.energy > this.maxEnergy) { this.energy = this.maxEnergy; } else if (this.energy < this.maxEnergy) { this.energy += 0.001; } ctx.save() ctx.translate(this.position.x, this.position.y) ctx.rotate(Math.atan2(this.seePlayer.position.y - this.position.y, this.seePlayer.position.x - this.position.x)) if (this.seePlayer.recall > 0 && this.distanceToPlayer2() > this.repulsionRange) { ctx.globalAlpha = (this.immuneCycle < this.cycle) ? 1 : 0.5 var grd2 = ctx.createLinearGradient(0, 0, -150, 0); // grd2.addColorStop(0, 'rgba(255, 255, 155, 0.8)'); // grd2.addColorStop(1, 'rgba(255, 200, 0, 0.1)'); grd2.addColorStop(0, 'rgba(150, 200, 255, 0.7)'); grd2.addColorStop(1, 'rgba(150, 200, 255, 0)'); ctx.fillStyle = grd2; ctx.beginPath(); ctx.moveTo(-18, -25); //10 * (Math.random() - 0.5), 10 * (Math.random() - 0.5) ctx.lineTo(-18, 25); ctx.lineTo(-50 - 100 * Math.random(), 0); ctx.fill(); } else if (this.distanceToPlayer2() < this.repulsionRange) { ctx.globalAlpha = (this.immuneCycle < this.cycle) ? 1 : 0.5 var grd2 = ctx.createLinearGradient(0, 0, 80, 0); grd2.addColorStop(0, 'rgba(150, 200, 255, 0.7)'); grd2.addColorStop(1, 'rgba(150, 200, 255, 0)'); ctx.fillStyle = grd2; ctx.beginPath(); ctx.moveTo(20, -16); //10 * (Math.random() - 0.5), 10 * (Math.random() - 0.5) ctx.lineTo(20, 16); ctx.lineTo(35 + 43 * Math.random(), 0); ctx.fill(); } ctx.restore() ctx.save() ctx.globalAlpha = (this.immuneCycle < this.cycle) ? 1 : 0.5 ctx.translate(this.position.x, this.position.y) ctx.rotate(Math.atan2(this.seePlayer.position.y - this.position.y, this.seePlayer.position.x - this.position.x)) if (this.health > 0.5) { ctx.beginPath(); ctx.arc(0, 0, 30, 0, 2 * Math.PI); ctx.fillStyle = m.bodyGradient ctx.fill(); ctx.arc(15, 0, 4, 0, 2 * Math.PI); ctx.strokeStyle = "#333"; ctx.lineWidth = 2; ctx.stroke(); ctx.beginPath(); //eye ctx.arc(15, 0, 3.5, 0, 2 * Math.PI); ctx.fillStyle = `rgba(255, 0, 0, ${this.health * this.health})`; ctx.fill() ctx.restore() } else { ctx.beginPath(); ctx.arc(0, 0, 30, 0, 2 * Math.PI); ctx.fillStyle = m.bodyGradient ctx.fill(); ctx.strokeStyle = "#333"; ctx.lineWidth = 2; if (!(Math.atan2(this.seePlayer.position.y - this.position.y, this.seePlayer.position.x - this.position.x) > -Math.PI / 2 && Math.atan2(this.seePlayer.position.y - this.position.y, this.seePlayer.position.x - this.position.x) < Math.PI / 2)) ctx.scale(1, -1); //here is the flip ctx.stroke(); ctx.beginPath(); ctx.arc(2, -6, 7, 0, 2 * Math.PI); ctx.stroke(); ctx.beginPath(); ctx.arc(25, -6, 7, 0.25 * Math.PI, 1.6 * Math.PI); ctx.stroke(); ctx.beginPath(); ctx.arc(2, -10, 9, 1.25 * Math.PI, 1.75 * Math.PI); ctx.stroke(); ctx.beginPath(); ctx.arc(25, -10, 9, 1.25 * Math.PI, 1.4 * Math.PI); ctx.stroke(); ctx.beginPath(); ctx.arc(18, 13, 10, 0, 2 * Math.PI); ctx.fillStyle = m.bodyGradient; ctx.fill(); ctx.stroke(); ctx.beginPath(); ctx.arc(18, 13, 6, 0, 2 * Math.PI); ctx.fillStyle = "#555"; ctx.fill(); ctx.stroke(); ctx.beginPath(); ctx.arc(3, -6, 3, 0, 2 * Math.PI); ctx.fill(); ctx.stroke(); ctx.beginPath(); ctx.arc(26, -6, 3, 0, 2 * Math.PI); ctx.fill(); ctx.stroke(); ctx.restore(); } if (m.fieldMode == 1) { //render over I think if (this.energy > 0.1) { this.harmonic3Phase(); } } if (m.fieldMode == 7) { this.locatePlayer(); } if (m.fieldMode == 8) { me.pilotWave() } if (m.fieldMode == 3) { const angle = Math.atan2(this.seePlayer.position.y - this.position.y, this.seePlayer.position.x - this.position.x); me.damageReduction = 0.5; me.accelMag = 0.0012; if (!(simulation.cycle % Math.floor(100 + 90 * Math.random() * simulation.CDScale))) { this.diveAttack() } ctx.beginPath(); ctx.arc(this.position.x, this.position.y, 1000, 0, 2 * Math.PI); ctx.fillStyle = "#f5f5ff"; ctx.strokeStyle = "#f5f5ff55"; ctx.setLineDash([125 * Math.random(), 125 * Math.random()]); ctx.globalCompositeOperation = "difference"; ctx.fill(); ctx.globalCompositeOperation = "source-over"; ctx.stroke() ctx.setLineDash([]); } else { me.accelMag = 0.0006; } if (this.immuneCycle > this.cycle) { this.damageReduction = 0; } else { if (m.fieldMode == 3) { this.damageReduction = 0.5; } else { this.damageReduction = 1; } } if (m.fieldMode == 6) { this.timeAttack(); ctx.globalCompositeOperation = "saturation" ctx.fillStyle = "#ccc"; ctx.fillRect(-50000, -50000, 100000, 100000) ctx.globalCompositeOperation = "source-over" // stop time // m.isBodiesAsleep = true; // function sleep(who) { // for (let i = 0, len = who.length; i < len; ++i) { // if (!who[i].isSleeping) { // who[i].storeVelocity = who[i].velocity // who[i].storeAngularVelocity = who[i].angularVelocity // } // Matter.Sleeping.set(who[i], true) // } // } // sleep(mob); // sleep(body); // sleep(bullet); // sleep([player]); // simulation.cycle--; } if (this.seePlayer.recall) { //fields this.gun() } } me.laserSword = function (where, angle, length) { const sub = Vector.sub(this.seePlayer.position, this.position) const unit = Vector.normalise(sub) const path = [{ x: this.position.x + 20 * Math.cos(this.angle), y: this.position.y + 20 * Math.sin(this.angle) }, { x: this.position.x + (120 + 400) * Math.sqrt(Math.random()) * Math.cos(this.angle), y: this.position.y + (120 + 400) * Math.sqrt(Math.random()) * Math.sin(this.angle) } ]; const vertexCollision = function (v1, v1End, domain) { for (let i = 0; i < domain.length; ++i) { let vertices = domain[i].vertices; const len = vertices.length - 1; for (let j = 0; j < len; j++) { results = simulation.checkLineIntersection(v1, v1End, vertices[j], vertices[j + 1]); if (results.onLine1 && results.onLine2) { const dx = v1.x - results.x; const dy = v1.y - results.y; const dist2 = dx * dx + dy * dy; if (dist2 < best.dist2 && (!domain[i].mob || domain[i].alive)) best = { x: results.x, y: results.y, dist2: dist2, who: domain[i], v1: vertices[j], v2: vertices[j + 1] }; } } results = simulation.checkLineIntersection(v1, v1End, vertices[0], vertices[len]); if (results.onLine1 && results.onLine2) { const dx = v1.x - results.x; const dy = v1.y - results.y; const dist2 = dx * dx + dy * dy; if (dist2 < best.dist2) best = { x: results.x, y: results.y, dist2: dist2, who: domain[i], v1: vertices[0], v2: vertices[len] }; } } }; best = { x: null, y: null, dist2: Infinity, who: null, v1: null, v2: null }; const look = { x: where.x + length * Math.cos(angle), y: where.y + length * Math.sin(angle) }; // vertexCollision(where, look, body); // vertexCollision(where, look, mob); vertexCollision(where, look, map); if (!m.isCloak) vertexCollision(where, look, [player]); if (best.who && (best.who === player) && m.immuneCycle < m.cycle) { m.immuneCycle = m.cycle + m.collisionImmuneCycles; //player is immune to damage for an extra second m.damage(this.swordDamage); simulation.drawList.push({ //add dmg to draw queue x: best.x, y: best.y, radius: this.swordDamage * 1500, color: "rgba(0,0,0,0.5)", time: 20 }); } if (best.dist2 === Infinity) best = look; ctx.beginPath(); //draw beam ctx.moveTo(where.x, where.y); ctx.lineTo(best.x, best.y); ctx.strokeStyle = "#000"; ctx.lineWidth = 1.5; ctx.setLineDash([70 + 300 * Math.random(), 55 * Math.random()]); ctx.stroke(); // Draw it ctx.setLineDash([]); ctx.lineWidth = 20; ctx.strokeStyle = "rgba(0,0,0,0.07)"; ctx.stroke(); // Draw it const Dx = Math.cos(Math.atan2(this.seePlayer.position.y - this.position.y, this.seePlayer.position.x - this.position.x)); const Dy = Math.sin(Math.atan2(this.seePlayer.position.y - this.position.y, this.seePlayer.position.x - this.position.x)); let xElec = this.position.x + 40 * Dx; let yElec = this.position.y + 40 * Dy; ctx.beginPath(); ctx.moveTo(xElec, yElec); const step = 40 for (let i = 0; i < 6; i++) { xElec += step * (Dx + 1.5 * (Math.random() - 0.5)) yElec += step * (Dy + 1.5 * (Math.random() - 0.5)) ctx.lineTo(xElec, yElec); } ctx.strokeStyle = "#000"; ctx.lineWidth = 1.5; ctx.setLineDash([70 + 300 * Math.random(), 55 * Math.random()]); ctx.stroke(); // Draw it ctx.setLineDash([]); } me.drawField = function () { if (m.fieldMode != 2) { const angle = Math.atan2(this.seePlayer.position.y - this.position.y, this.seePlayer.position.x - this.position.x) const range = m.fieldRange; ctx.save() ctx.beginPath(); ctx.fillStyle = "rgba(0,0,0," + Math.min(0.6, (0.04 + m.energy * (0.1 + 0.11 * Math.random()))) + ")"; ctx.arc(this.position.x, this.position.y, range, angle - Math.PI * m.fieldArc, angle + Math.PI * m.fieldArc, false); ctx.lineWidth = 2; ctx.stroke(); let eye = 13; if (m.fieldMode == 2) { eye = 30 } let aMag = 0.75 * Math.PI * m.fieldArc let a = angle + aMag let cp1x = this.position.x + 0.6 * range * Math.cos(a) let cp1y = this.position.y + 0.6 * range * Math.sin(a) ctx.quadraticCurveTo(cp1x, cp1y, this.position.x + eye * Math.cos(angle), this.position.y + eye * Math.sin(angle)) a = angle - aMag cp1x = this.position.x + 0.6 * range * Math.cos(a) cp1y = this.position.y + 0.6 * range * Math.sin(a) ctx.quadraticCurveTo(cp1x, cp1y, this.position.x + 1 * range * Math.cos(angle - Math.PI * m.fieldArc), this.position.y + 1 * range * Math.sin(angle - Math.PI * m.fieldArc)) ctx.fill(); // ctx.lineTo(this.position.x + eye * Math.cos(angle), this.position.y + eye * Math.sin(angle)); //draw random lines in field for cool effect let offAngle = angle + 1.7 * Math.PI * m.fieldArc * (Math.random() - 0.5); ctx.beginPath(); eye = 15; ctx.moveTo(this.position.x + eye * Math.cos(angle), this.position.y + eye * Math.sin(angle)); ctx.lineTo(this.position.x + range * Math.cos(offAngle), this.position.y + range * Math.sin(offAngle)); ctx.strokeStyle = "rgba(0,0,0,0.6)"; ctx.lineWidth = 1; ctx.stroke(); ctx.restore() } else { ctx.save() ctx.beginPath(); const wave = Math.cos(m.cycle * 0.022); const angle = Math.atan2(this.seePlayer.position.y - this.position.y, this.seePlayer.position.x - this.position.x) ctx.arc(this.position.x, this.position.y, m.fieldRange, angle - Math.PI * m.fieldArc, angle + Math.PI * m.fieldArc, false); ctx.lineWidth = 2.5 - 1.5 * wave; ctx.stroke(); const curve = 0.57 + 0.04 * wave const aMag = (1 - curve * 1.2) * Math.PI * m.fieldArc let a = angle + aMag let cp1x = this.position.x + curve * m.fieldRange * Math.cos(a) let cp1y = this.position.y + curve * m.fieldRange * Math.sin(a) ctx.quadraticCurveTo(cp1x, cp1y, this.position.x + 30 * Math.cos(angle), this.position.y + 30 * Math.sin(angle)) a = angle - aMag cp1x = this.position.x + curve * m.fieldRange * Math.cos(a) cp1y = this.position.y + curve * m.fieldRange * Math.sin(a) ctx.quadraticCurveTo(cp1x, cp1y, this.position.x + 1 * m.fieldRange * Math.cos(angle - Math.PI * m.fieldArc), this.position.y + 1 * m.fieldRange * Math.sin(angle - Math.PI * m.fieldArc)) ctx.fill(); ctx.restore() } } me.pushM = function () { const unit = Vector.normalise(Vector.sub(this.position, player.position)) if (tech.blockDmg) { Matter.Body.setVelocity(player, { x: 0.5 * player.velocity.x, y: 0.5 * player.velocity.y }); //draw electricity const step = 40 ctx.beginPath(); for (let i = 0, len = 0.8 * tech.blockDmg; i < len; i++) { let x = this.position.x - 20 * unit.x; let y = this.position.y - 20 * unit.y; ctx.moveTo(x, y); for (let i = 0; i < 8; i++) { x += step * (-unit.x + 1.5 * (Math.random() - 0.5)) y += step * (-unit.y + 1.5 * (Math.random() - 0.5)) ctx.lineTo(x, y); } } if (m.immuneCycle < m.cycle) { m.immuneCycle = m.cycle + m.collisionImmuneCycles m.damage(0.025 * simulation.dmgScale) } ctx.lineWidth = 3; ctx.strokeStyle = "#000"; ctx.stroke(); } const massRoot = Math.sqrt(Math.min(12, Math.max(0.15, player.mass))); // masses above 12 can start to overcome the push back //idk Matter.Body.setVelocity(player, { x: this.velocity.x - (15 * unit.x) / massRoot, y: this.velocity.y - (15 * unit.y) / massRoot }); } me.diveAttack = function () { const forceMag = this.accelMag * this.mass; const angle = Math.atan2(this.seePlayer.position.y - this.position.y, this.seePlayer.position.x - this.position.x); this.force.x += 150 * forceMag * Math.cos(angle); this.force.y += 150 * forceMag * Math.sin(angle); ctx.beginPath() ctx.moveTo(this.position.x + Math.sin(angle), this.position.y + Math.cos(angle)) ctx.lineTo(this.seePlayer.position.x, this.seePlayer.position.y) aim = '#000000'; ctx.stroke() } me.phase = 2 * Math.PI * Math.random(); me.index2 = 0; me.pilotWave = function () { const rotate = this.cycle * 0.008; this.fieldPhase += 0.002; const off1 = 1 + 0.01 * Math.sin(this.fieldPhase); const off2 = 1 - 0.01 * Math.cos(this.fieldPhase); ctx.save() ctx.beginPath(); ctx.ellipse(player.position.x, player.position.y, 1.2 * 200 * off1, 1.2 * 150 * off2, rotate, 0, 2 * Math.PI); ctx.globalCompositeOperation = "exclusion"; //"exclusion" "difference"; ctx.fillStyle = "#ffffff"; //"#eef"; ctx.fill(); ctx.globalCompositeOperation = "source-over"; ctx.beginPath(); ctx.ellipse(player.position.x, player.position.y, 1.2 * 200 * off1, 1.2 * 150 * off2, rotate, 0, 2 * Math.PI * m.energy / m.maxEnergy); ctx.strokeStyle = "#000000"; ctx.lineWidth = 4; ctx.stroke(); ctx.restore() const range = this.curlRange / 15, mag = -50; const angle = Math.atan2(this.seePlayer.position.y - this.position.y, this.seePlayer.position.x - this.position.x); const applyCurl = function (center, array, isAntiGravity = true) { for (let i = 0; i < array.length; ++i) { if (!array[i].isNotHoldable) { const sub = Vector.sub(center, array[i].position) const radius2 = Vector.magnitudeSquared(sub); //if too close, like center mob or shield, don't curl // if too far don't curl if (radius2 < range * range && radius2 > 10000) { const curlVector = Vector.mult(Vector.perp(Vector.normalise(sub)), mag) //apply curl force if (array[i].isMobBullet) { Matter.Body.setVelocity(array[i], { x: array[i].velocity.x * 0.97 + curlVector.x * 0.06 - (Math.cos(angle) * 5), y: array[i].velocity.y * 0.97 + curlVector.y * 0.06 - (Math.sin(angle) * 5) }) } else { Matter.Body.setVelocity(array[i], { x: array[i].velocity.x * 0.94 + curlVector.x * 0.06 - (Math.cos(angle) * 5), y: array[i].velocity.y * 0.94 + curlVector.y * 0.06 - (Math.sin(angle) * 5) }) } if (isAntiGravity) array[i].force.y -= 0.8 * simulation.g * array[i].mass } } } } applyCurl(this.position, [player]); } me.teleportAway = function () {//hehe ctx.beginPath(); ctx.moveTo(this.position.x, this.position.y); if (this.seePlayer.recall && !(simulation.cycle % 17)) { const dist = Vector.sub(this.position, this.seePlayer.position); const distMag = Vector.magnitude(dist); const unitVector = Vector.normalise(dist); const rando = (Math.random() - 0.5) * 50; if (distMag < 20000) { Matter.Body.translate(this, Vector.mult(unitVector, distMag + rando)); } else { Matter.Body.translate(this, Vector.mult(unitVector, 20000 + rando)); } } ctx.lineTo(this.position.x, this.position.y); ctx.lineWidth = radius * 2; ctx.strokeStyle = "rgba(0,0,0,0.08)"; ctx.stroke(); if (!this.seePlayer.yes) { ctx.beginPath(); ctx.moveTo(this.position.x, this.position.y); if (this.seePlayer.recall && !(simulation.cycle % 17)) { const dist = Vector.sub(this.seePlayer.position, this.position); const distMag = Vector.magnitude(dist); const unitVector = Vector.normalise(dist); const rando = (Math.random() - 0.5) * 50; if (distMag < 200000) { Matter.Body.translate(this, Vector.mult(unitVector, distMag + rando)); } else { Matter.Body.translate(this, Vector.mult(unitVector, 200000 + rando)); } } ctx.lineTo(this.position.x, this.position.y); ctx.lineWidth = radius * 2; ctx.strokeStyle = "rgba(0,0,0,0.08)"; ctx.stroke(); } } me.timeAttack = function () { if (this.seePlayer.recall && !(simulation.cycle % 30) || this.distanceToPlayer2() < 300) { requestAnimationFrame(() => { simulation.timePlayerSkip(45) simulation.loop(); //ending with a wipe and normal loop fixes some very minor graphical issues where things are draw in the wrong locations }); //wrapping in animation frame prevents errors, probably } } me.harmonic3Phase = function () { //normal standard 3 different 2-d circles if (tech.harmonics === 2) { const fieldRange1 = (0.75 + 0.3 * Math.cos(m.cycle / 23)) * m.fieldRange * m.harmonicRadius const fieldRange2 = (0.68 + 0.37 * Math.cos(m.cycle / 37)) * m.fieldRange * m.harmonicRadius const fieldRange3 = (0.7 + 0.35 * Math.cos(m.cycle / 47)) * m.fieldRange * m.harmonicRadius const netfieldRange = Math.max(fieldRange1, fieldRange2, fieldRange3) ctx.fillStyle = "rgba(0,0,0," + Math.min(0.6, (0.04 + m.energy * (0.1 + 0.11 * Math.random()))) + ")"; ctx.beginPath(); ctx.arc(this.position.x, this.position.y, fieldRange1, 0, 2 * Math.PI); ctx.fill(); ctx.beginPath(); ctx.arc(this.position.x, this.position.y, fieldRange2, 0, 2 * Math.PI); ctx.fill(); ctx.beginPath(); ctx.arc(this.position.x, this.position.y, fieldRange3, 0, 2 * Math.PI); ctx.fill(); //360 block if (Vector.magnitude(Vector.sub(player.position, this.position)) - this.radius < netfieldRange) { me.pushM(); } for (let i = 0; i < bullet.length; i++) { if (Vector.magnitude(Vector.sub(bullet[i].position, this.position)) - this.radius < netfieldRange) { const dx = bullet[i].position.x - this.position.x; const dy = bullet[i].position.y - this.position.y; const dist = Math.sqrt(dx * dx + dy * dy); if (dist < m.fieldRange) { const angle = Math.atan2(dy, dx); const mag = (1500 * bullet[i].mass * simulation.g) / dist; bullet[i].force.x += mag * Math.cos(angle); bullet[i].force.y += mag * Math.sin(angle); } this.energy -= 0.0012; } } } else { const rotation = simulation.cycle * 0.0031 const phase = simulation.cycle * 0.023 const radius = m.fieldRange * m.harmonicRadius ctx.lineWidth = 1; ctx.strokeStyle = "rgba(0,0,0,0.5)" ctx.fillStyle = `rgba(0,0,0,${Math.min(0.6, m.energy * (0.11 + 0.1 * Math.random()) * (3 / tech.harmonics))})`; // ctx.fillStyle = "rgba(0,0,0," + Math.min(0.7, m.energy * (0.22 - 0.01 * tech.harmonics) * (0.5 + 0.5 * Math.random())) + ")"; for (let i = 0; i < tech.harmonics; i++) { ctx.beginPath(); ctx.ellipse(this.position.x, this.position.y, radius * Math.abs(Math.sin(phase + i / tech.harmonics * Math.PI)), radius, rotation + i / tech.harmonics * Math.PI, 0, 2 * Math.PI); ctx.fill(); ctx.stroke(); } //360 block if (Vector.magnitude(Vector.sub(player.position, this.position)) - this.radius < radius) { me.pushM(); } for (let i = 0; i < bullet.length; i++) { if (Vector.magnitude(Vector.sub(bullet[i].position, this.position)) - this.radius < radius) { const dx = bullet[i].position.x - this.position.x; const dy = bullet[i].position.y - this.position.y; const dist = Math.sqrt(dx * dx + dy * dy); if (dist < m.fieldRange) { const angle = Math.atan2(dy, dx); const mag = (1500 * bullet[i].mass * simulation.g) / dist; bullet[i].force.x += mag * Math.cos(angle); bullet[i].force.y += mag * Math.sin(angle); } this.energy -= 0.0012; } } } } me.railGun = function () { const angle = Math.atan2(this.seePlayer.position.y - this.position.y, this.seePlayer.position.x - this.position.x); const X = this.position.x const Y = this.position.y const unitVector = { x: Math.cos(angle), y: Math.sin(angle) } const unitVectorPerp = Vector.perp(unitVector) function magField(mag, arc) { ctx.moveTo(X, Y); ctx.bezierCurveTo( X + unitVector.x * mag, Y + unitVector.y * mag, X + unitVector.x * mag + unitVectorPerp.x * arc, Y + unitVector.y * mag + unitVectorPerp.y * arc, X + unitVectorPerp.x * arc, Y + unitVectorPerp.y * arc) ctx.bezierCurveTo( X - unitVector.x * mag + unitVectorPerp.x * arc, Y - unitVector.y * mag + unitVectorPerp.y * arc, X - unitVector.x * mag, Y - unitVector.y * mag, X, Y) } ctx.fillStyle = `rgba(50,20,100,0.05)`; const magSize = 8 * c * tech.railChargeRate ** 3 const arcSize = 6 * c * tech.railChargeRate ** 3 for (let i = 3; i < 7; i++) { const MAG = magSize * i * i * (0.93 + 0.07 * Math.random()) const ARC = arcSize * i * i * (0.93 + 0.07 * Math.random()) ctx.beginPath(); magField(MAG, ARC) magField(MAG, -ARC) ctx.fill(); } } me.waves = []; me.doLongitudinal = function () { if (!m.isBodiesAsleep) { ctx.strokeStyle = "rgba(0,0,0,0.6)" //"000"; ctx.lineWidth = 2 * tech.wavePacketDamage ctx.beginPath(); // const end = 1100 * tech.bulletsLastLonger / Math.sqrt(tech.waveReflections * 0.5) //should equal about 1767 const end = 1100 * tech.bulletsLastLonger * Math.pow(0.93, tech.waveReflections) //should equal about 1767 const damage = 0.0005 * simulation.dmgScale//normal damage for m basically shreds m, so had to nerf this for (let i = this.waves.length - 1; i > -1; i--) { const v1 = Vector.add(this.waves[i].position, Vector.mult(this.waves[i].unit1, this.waves[i].radius)) const v2 = Vector.add(this.waves[i].position, Vector.mult(this.waves[i].unit2, this.waves[i].radius)) //draw wave ctx.moveTo(v1.x, v1.y) ctx.arc(this.waves[i].position.x, this.waves[i].position.y, this.waves[i].radius, this.waves[i].angle, this.waves[i].angle + this.waves[i].arc); //using small angle linear approximation of circle arc, this will not work if the arc gets large // https://stackoverflow.com/questions/13652518/efficiently-find-points-inside-a-circle-sector let hits = Matter.Query.ray([player], v1, v2, 50) //Matter.Query.ray(bodies, startPoint, endPoint, [rayWidth]) for (let j = 0, len = Math.min(30, hits.length); j < len; j++) { player.force.x += 0.01 * (Math.random() - 0.5) * player.mass player.force.y += (0.01 * (Math.random() - 0.5) - simulation.g * 0.25) * player.mass //remove force of gravity Matter.Body.setVelocity(player, { //friction x: player.velocity.x * 0.95, y: player.velocity.y * 0.95 }); m.damage(damage) } hits = Matter.Query.ray(body, v1, v2, 50) for (let j = 0, len = Math.min(30, hits.length); j < len; j++) { const who = hits[j].body //make them shake around who.force.x += 0.01 * (Math.random() - 0.5) * who.mass who.force.y += (0.01 * (Math.random() - 0.5) - simulation.g * 0.25) * who.mass //remove force of gravity let vertices = who.vertices; const vibe = 25 ctx.moveTo(vertices[0].x + vibe * (Math.random() - 0.5), vertices[0].y + vibe * (Math.random() - 0.5)); for (let j = 1; j < vertices.length; j++) { ctx.lineTo(vertices[j].x + vibe * (Math.random() - 0.5), vertices[j].y + vibe * (Math.random() - 0.5)); } ctx.lineTo(vertices[0].x + vibe * (Math.random() - 0.5), vertices[0].y + vibe * (Math.random() - 0.5)); if (tech.isPhononBlock && !who.isNotHoldable && who.speed < 5 && who.angularSpeed < 0.1) { if (Math.random() < 0.5) b.targetedBlock(who, 50 - Math.min(25, who.mass * 3)) // targetedBlock(who, speed = 50 - Math.min(20, who.mass * 2), range = 1600) { // Matter.Body.setAngularVelocity(who, (0.25 + 0.12 * Math.random()) * (Math.random() < 0.5 ? -1 : 1)); who.torque += who.inertia * 0.001 * (Math.random() - 0.5) } } // ctx.stroke(); //draw vibes this.waves[i].radius += tech.waveBeamSpeed * 1.8 * this.waves[i].expanding //expand / move if (this.waves[i].radius > end - 30) { this.waves[i].expanding = -1 if (this.waves[i].reflection < 1) this.waves.splice(i, 1) //end } else if (this.waves[i].radius < 25) { this.waves[i].expanding = 1 if (this.waves[i].reflection < 1) this.waves.splice(i, 1) //end } } ctx.stroke(); } } me.lasers = function (where, angle) { const vertexCollision = function (v1, v1End, domain) { for (let i = 0; i < domain.length; ++i) { let vertices = domain[i].vertices; const len = vertices.length - 1; for (let j = 0; j < len; j++) { results = simulation.checkLineIntersection(v1, v1End, vertices[j], vertices[j + 1]); if (results.onLine1 && results.onLine2) { const dx = v1.x - results.x; const dy = v1.y - results.y; const dist2 = dx * dx + dy * dy; if (dist2 < best.dist2 && (!domain[i].mob || domain[i].alive)) best = { x: results.x, y: results.y, dist2: dist2, who: domain[i], v1: vertices[j], v2: vertices[j + 1] }; } } results = simulation.checkLineIntersection(v1, v1End, vertices[0], vertices[len]); if (results.onLine1 && results.onLine2) { const dx = v1.x - results.x; const dy = v1.y - results.y; const dist2 = dx * dx + dy * dy; if (dist2 < best.dist2) best = { x: results.x, y: results.y, dist2: dist2, who: domain[i], v1: vertices[0], v2: vertices[len] }; } } }; const seeRange = 7000; best = { x: null, y: null, dist2: Infinity, who: null, v1: null, v2: null }; const look = { x: where.x + seeRange * Math.cos(angle), y: where.y + seeRange * Math.sin(angle) }; // vertexCollision(where, look, mob); vertexCollision(where, look, map); vertexCollision(where, look, body); if (!m.isCloak) vertexCollision(where, look, [player]); if (best.who && (best.who === player) && m.immuneCycle < m.cycle) { const dmg = 0.0011 * simulation.dmgScale; m.damage(dmg); simulation.drawList.push({ //add dmg to draw queue x: best.x, y: best.y, radius: dmg * 1500, color: "rgba(80,0,255,0.5)", time: 20 }); } //draw beam if (best.dist2 === Infinity) best = look; ctx.moveTo(where.x, where.y); ctx.lineTo(best.x, best.y); ctx.lineWidth = 10; ctx.stroke(); } me.pulse = function (charge, angle, where = this.position) { let best; angle = Math.atan2(this.seePlayer.position.y - this.position.y, this.seePlayer.position.x - this.position.x) let explosionRadius = 5.5 * charge let range = 5000 const path = [{ x: where.x + 20 * Math.cos(angle), y: where.y + 20 * Math.sin(angle) }, { x: where.x + range * Math.cos(angle), y: where.y + range * Math.sin(angle) } ]; const vertexCollision = function (v1, v1End, domain) { for (let i = 0; i < domain.length; ++i) { let vertices = domain[i].vertices; const len = vertices.length - 1; for (let j = 0; j < len; j++) { results = simulation.checkLineIntersection(v1, v1End, vertices[j], vertices[j + 1]); if (results.onLine1 && results.onLine2) { const dx = v1.x - results.x; const dy = v1.y - results.y; const dist2 = dx * dx + dy * dy; if (dist2 < best.dist2 && (!domain[i].mob || domain[i].alive)) best = { x: results.x, y: results.y, dist2: dist2, who: domain[i], v1: vertices[j], v2: vertices[j + 1] }; } } results = simulation.checkLineIntersection(v1, v1End, vertices[0], vertices[len]); if (results.onLine1 && results.onLine2) { const dx = v1.x - results.x; const dy = v1.y - results.y; const dist2 = dx * dx + dy * dy; if (dist2 < best.dist2) best = { x: results.x, y: results.y, dist2: dist2, who: domain[i], v1: vertices[0], v2: vertices[len] }; } } }; //check for collisions best = { x: null, y: null, dist2: Infinity, who: null, v1: null, v2: null }; if (!best.who) { vertexCollision(path[0], path[1], body); vertexCollision(path[0], path[1], [player]); vertexCollision(path[0], path[1], map); if (best.dist2 != Infinity) { //if hitting something path[path.length - 1] = { x: best.x, y: best.y }; } } if (best.who) { b.explosion(path[1], explosionRadius, "rgba(0,0,0,0)") const off = explosionRadius * 1.2 b.explosion({ x: path[1].x + off * (Math.random() - 0.5), y: path[1].y + off * (Math.random() - 0.5) }, explosionRadius, "rgba(0,0,0,0.7)") b.explosion({ x: path[1].x + off * (Math.random() - 0.5), y: path[1].y + off * (Math.random() - 0.5) }, explosionRadius, "rgba(0,0,0,0.7)") } //draw laser beam ctx.beginPath(); ctx.moveTo(path[0].x, path[0].y); ctx.lineTo(path[1].x, path[1].y); if (charge > 50) { ctx.strokeStyle = "rgba(0,0,0,0.10)" ctx.lineWidth = 70 ctx.stroke(); } ctx.strokeStyle = "rgba(0,0,0,0.25)" ctx.lineWidth = 20 ctx.stroke(); ctx.strokeStyle = "#f00"; ctx.lineWidth = 4 ctx.stroke(); //draw little dots along the laser path const sub = Vector.sub(path[1], path[0]) const mag = Vector.magnitude(sub) for (let i = 0, len = Math.floor(mag * 0.0005 * charge); i < len; i++) { const dist = Math.random() simulation.drawList.push({ x: path[0].x + sub.x * dist + 10 * (Math.random() - 0.5), y: path[0].y + sub.y * dist + 10 * (Math.random() - 0.5), radius: 1.5 + 5 * Math.random(), color: "rgba(0,0,0,0.5)", time: Math.floor(9 + 25 * Math.random() * Math.random()) }); } } let c = 0 me.gun = function () { if (b.activeGun == 0) {// nailgun if (this.seePlayer.recall && !(simulation.cycle % 20)) { this.fireDir = Vector.normalise(Vector.sub(this.seePlayer.position, this.position)); const dist = Vector.magnitudeSquared(Vector.sub(this.position, player.position)); const unit = Vector.normalise(Vector.sub(Vector.add(player.position, Vector.mult(player.velocity, Math.sqrt(dist) / 60)), this.position)) normalBullet(this.position.x, this.position.y); const v = 10 + 8 * simulation.accelScale; Matter.Body.setVelocity(mob[mob.length - 1], Vector.mult(unit, 0.009 * this.mass)) // Matter.Body.setVelocity(mob[mob.length - 1], { // x: this.velocity.x + this.fireDir.x * v, // y: this.velocity.y + this.fireDir.y * v // }); } } if (b.activeGun == 1) {// shotgun if (this.seePlayer.recall && !(simulation.cycle % 90)) { const side = 22 for (let i = 0; i < 12; i++) { const angle = Math.atan2(this.seePlayer.position.y - this.position.y, this.seePlayer.position.x - this.position.x); const dir = angle + (Math.random() - 0.5) * 1 const SPEED = 52 + Math.random() * 8 normalBullet(this.position.x + 35 * Math.cos(angle) + 15 * (Math.random() - 0.5), this.position.y + 35 * Math.sin(angle) + 15 * (Math.random() - 0.5)) Matter.Body.setVelocity(mob[mob.length - 1], { x: SPEED * Math.cos(dir), y: SPEED * Math.sin(dir) }); } } } else if (b.activeGun == 2) { // super balls if (this.seePlayer.recall && !(simulation.cycle % 20)) { const num = 3; const SPREAD = 0.13; const angle = Math.atan2(this.seePlayer.position.y - this.position.y, this.seePlayer.position.x - this.position.x); let dir = angle - SPREAD * (num - 1) / 2; const SPEED = 33 for (let i = 0; i < num; i++) { ball(this.position.x + 30 * Math.cos(angle), this.position.y + 30 * Math.sin(angle)) Matter.Body.setVelocity(mob[mob.length - 1], { x: SPEED * Math.cos(dir), y: SPEED * Math.sin(dir) }); dir += SPREAD } } } else if (b.activeGun == 3) { // wave this.doLongitudinal() const halfArc = 0.275 const anglex = Math.atan2(this.seePlayer.position.y - this.position.y, this.seePlayer.position.x - this.position.x); const angle = anglex + 0.3 * (Math.random() - 0.5) this.waves.push({ position: { x: this.position.x + 25 * Math.cos(anglex), y: this.position.y + 25 * Math.sin(anglex), }, angle: angle - halfArc, //used in drawing ctx.arc unit1: { x: Math.cos(angle - halfArc), y: Math.sin(angle - halfArc) }, //used for collision unit2: { x: Math.cos(angle + halfArc), y: Math.sin(angle + halfArc) }, //used for collision arc: halfArc * 2, radius: 25, reflection: 0, expanding: 1, resonanceCount: 0 }) } else if (b.activeGun == 4) { // missiles if (this.seePlayer.recall && !(simulation.cycle % 30)) { this.fireDir = Vector.normalise(Vector.sub(this.seePlayer.position, this.position)); missile(this.position); const v = 10 + 8 * simulation.accelScale + (Math.random() * 20 - Math.random() * 20); Matter.Body.setVelocity(mob[mob.length - 1], { x: this.velocity.x + this.fireDir.x * v, y: this.velocity.y + this.fireDir.y * v }); } } else if (b.activeGun == 5) { // grenades if (this.seePlayer.recall && !(simulation.cycle % 30)) { this.fireDir = Vector.normalise(Vector.sub(this.seePlayer.position, this.position)); grenade(this.position.x, this.position.y) const v = 10 + 8 * simulation.accelScale + (Math.random() * 20 - Math.random() * 20); Matter.Body.setVelocity(mob[mob.length - 1], { x: this.velocity.x + this.fireDir.x * v, y: this.velocity.y + this.fireDir.y * v }); } } else if (b.activeGun == 6) { // spores if (this.seePlayer.recall && !(simulation.cycle % 30)) { this.fireDir = Vector.normalise(Vector.sub(this.seePlayer.position, this.position)); me.drop(this.position.x, this.position.y) const v = 10 + 8 * simulation.accelScale + (Math.random() * 20 - Math.random() * 20); Matter.Body.setVelocity(mob[mob.length - 1], { x: this.velocity.x + this.fireDir.x * v, y: this.velocity.y + this.fireDir.y * v }); } } else if (b.activeGun == 7) { // drones ctx.save() ctx.lineWidth = "8"; ctx.strokeStyle = "rgba(100, 0, 150, 0.1)"; ctx.beginPath(); for (let i = 0, len = bullet.length; i < len; ++i) { const dx = bullet[i].position.x - this.position.x; const dy = bullet[i].position.y - this.position.y; const dist = Math.sqrt(dx * dx + dy * dy); if (dist < 500) { ctx.moveTo(this.position.x + dist, this.position.y); ctx.arc(this.position.x, this.position.y, dist, 0, 2 * Math.PI) //ctx.lineTo(bullet[i].position.x, bullet[i].position.y); const angle = Math.atan2(dy, dx); const mag = (1500 * bullet[i].mass * simulation.g) / (dist * 0.05); bullet[i].force.x += mag * Math.cos(angle); bullet[i].force.y += mag * Math.sin(angle); } } ctx.stroke(); ctx.restore() } else if (b.activeGun == 8) { // foam if (this.seePlayer.recall && !(simulation.cycle % 1)) { this.fireDir = Vector.normalise(Vector.sub(this.seePlayer.position, this.position)); foamBullet(this.position.x, this.position.y, 7 + Math.ceil(this.radius / 15), 69); const v = 10 + 8 * simulation.accelScale + (Math.random() * 20 - Math.random() * 20); Matter.Body.setVelocity(mob[mob.length - 1], { x: this.velocity.x + this.fireDir.x * v, y: this.velocity.y + this.fireDir.y * v }); } } else if (b.activeGun == 9) { // harpoon - railgun if (c > 1) { this.fireDir = Vector.normalise(Vector.sub(this.seePlayer.position, this.position)); railBullet(this.position.x, this.position.y); const v = 10 + 80 * simulation.accelScale; Matter.Body.setVelocity(mob[mob.length - 1], { x: this.velocity.x + this.fireDir.x * v, y: this.velocity.y + this.fireDir.y * v }); c = 0; } else { c += 0.02; this.railGun(); } } else if (b.activeGun == 10) { // laserMines if (this.seePlayer.recall && !(simulation.cycle % 100)) { this.fireDir = Vector.normalise(Vector.sub(this.seePlayer.position, this.position)); me.laserMine(this.position.x, this.position.y) const v = 10 + 8 * simulation.accelScale + (Math.random() * 20 - Math.random() * 20); Matter.Body.setVelocity(mob[mob.length - 1], { x: this.velocity.x + this.fireDir.x * v, y: this.velocity.y + this.fireDir.y * v }); } } else if (b.activeGun == 11) { // laser - pulse //this.lasers(this.position, Math.atan2(this.seePlayer.position.y - this.position.y, this.seePlayer.position.x - this.position.x)) //if (this.seePlayer.recall && !(simulation.cycle % 20)) { if (c > 1) { this.pulse(c * 100) c = 0; } else { if (this.energy < 1 || this.energy > 0.5) { c += 0.01; ctx.beginPath(); const mag = Math.sqrt(c) ctx.arc(this.position.x, this.position.y, c * 30, 0, 2 * Math.PI) ctx.fillStyle = '#000000' ctx.strokeStyle = 'transparent' ctx.fill(); ctx.stroke(); this.energy -= 0.01; ctx.strokeStyle = "#000000"; ctx.lineWidth = 1.5 // ctx.globalAlpha = 1; } else { c = 0; this.energy += 0.1 } } //} } } me.laserMine = function (x, y) { mobs.spawn(x, y, 3, 20, "#000000"); let xx = mob[mob.length - 1]; xx.stroke = "#00000000"; Matter.Body.setDensity(xx, 0.000005) //one tap xx.isUnstable = true; xx.timeLeft = 40 + Math.floor(180 * Math.random()) xx.leaveBody = false; xx.isDropPowerUp = false; xx.collisionFilter.mask = cat.bullet | cat.player | cat.map xx.showHealthBar = false; //xx.vertices = Matter.Vertices.rotate(xx.vertices, Math.PI, xx.position); me.onHit = function () { this.death(); }; xx.do = function () { this.timeLimit(); Matter.Body.setAngularVelocity(this, 0.01) ctx.beginPath(); ctx.lineWidth = 1; ctx.strokeStyle = "#00000000" for (let i = 0; i < this.vertices.length; i++) { const where = this.vertices[i] const endPoint = Vector.add(where, Vector.mult(Vector.normalise(Vector.sub(where, this.position)), 2500)) me.lasers(this.vertices[0], this.angle + Math.PI / 3); me.lasers(this.vertices[1], this.angle + Math.PI); me.lasers(this.vertices[2], this.angle - Math.PI / 3); } ctx.strokeStyle = "black"; ctx.stroke(); ctx.save() ctx.beginPath(); ctx.moveTo(this.vertices[0].x, this.vertices[0].y); ctx.lineTo(this.vertices[1].x, this.vertices[1].y); ctx.lineTo(this.vertices[2].x, this.vertices[2].y); ctx.fillStyle = "#000000"; ctx.strokeStyle = "transparent"; ctx.fill(); ctx.closePath(); ctx.stroke(); ctx.restore() } } me.seeker = function (x, y) { mobs.spawn(x, y, sides = 5, radius = 5, "rgb(0,0,0)"); let yy = mob[mob.length - 1]; yy.stroke = "transparent"; yy.onHit = function () { this.explode(this.mass * 20); }; Matter.Body.setDensity(yy, 0.000015); //normal is 0.001 yy.timeLeft = 420 //* (0.8 + 0.4 * Math.random()); yy.accelMag = 0.00017 * simulation.accelScale; //* (0.8 + 0.4 * Math.random()) yy.frictionAir = 0.01 //* (0.8 + 0.4 * Math.random()); yy.restitution = 0.5; yy.leaveBody = false; yy.isDropPowerUp = false; yy.isBadTarget = true; yy.isMobBullet = true; yy.showHealthBar = false; yy.collisionFilter.category = cat.mobBullet; yy.collisionFilter.mask = cat.player | cat.map | cat.body | cat.bullet; let index = 0; yy.do = function () { this.alwaysSeePlayer() this.timeLimit(); this.attraction(); }; } me.drop = function (x, y) { mobs.spawn(x, y, sides = 90, radius = 30, "rgb(0,255,100,0.7)"); let yyy = mob[mob.length - 1]; yyy.stroke = "transparent"; yyy.onDeath = function () { for (let i = 0, len = 5; i < len; i++) { me.seeker(this.position.x, this.position.y) Matter.Body.setVelocity(mob[mob.length - 1], { x: Math.random() * 30 - Math.random() * 30, y: Math.random() * 30 - Math.random() * 30 }); } }; Matter.Body.setDensity(yyy, 0.000015); //normal is 0.001 yyy.timeLeft = 60 //* (0.8 + 0.4 * Math.random()); yyy.frictionAir = 0.01 //* (0.8 + 0.4 * Math.random()); yyy.restitution = 0.5; yyy.leaveBody = false; yyy.isDropPowerUp = false; yyy.isBadTarget = true; yyy.isMobBullet = true; yyy.showHealthBar = false; yyy.collisionFilter.category = cat.mobBullet; yyy.collisionFilter.mask = null; yyy.maxRadius = 30; let index = 0; yyy.do = function () { if (Matter.Query.collides(this, [player]).length > 0 && !(m.isCloak && tech.isIntangible) && m.immuneCycle < m.cycle) { Matter.Body.setPosition(this, player.position) if (player.speed > 2.5) Matter.Body.setVelocity(player, Vector.mult(player.velocity, 0.94)) } if (Matter.Query.collides(this, map).length > 0) { Matter.Body.setVelocity(this, Vector.mult(this.velocity, 0.1)) } this.alwaysSeePlayer() this.timeLimit(); ctx.save() ctx.beginPath(); ctx.moveTo(this.position.x, this.position.y) ctx.fillStyle = "black"; ctx.arc(this.position.x, this.position.y, this.maxRadius, 0, 2 * Math.PI) ctx.stroke() ctx.fill() ctx.restore() if (this.maxRadius > 0) { this.maxRadius -= 0.5; } }; } }; restoreBoss(-13350, -1800); laserEM(-6500 + Math.floor(Math.random() * 200) - Math.floor(Math.random() * 200), -3400 + Math.floor(Math.random() * 200) - Math.floor(Math.random() * 200)); sniper(-9275 + Math.floor(Math.random() * 200) - Math.floor(Math.random() * 200), -3325 + Math.floor(Math.random() * 200) - Math.floor(Math.random() * 200)); laserEM(-5750 + Math.floor(Math.random() * 200) - Math.floor(Math.random() * 200), -850 + Math.floor(Math.random() * 200) - Math.floor(Math.random() * 200)); sniper(-3600 + Math.floor(Math.random() * 200) - Math.floor(Math.random() * 200), -1325 + Math.floor(Math.random() * 200) - Math.floor(Math.random() * 200)); laserEM(1425 + Math.floor(Math.random() * 200) - Math.floor(Math.random() * 200), -800 + Math.floor(Math.random() * 200) - Math.floor(Math.random() * 200)); //restoreBoss(-350, -3225); wire(); wire(); wire(); wire(); wire(); color.map = '#00000000'; level.customTopLayer = () => { if (dong.position.x > -3825) { dong.force.y -= dong.mass * simulation.g; } else { dong.force.y += dong.mass * simulation.g; } Matter.Body.setAngularVelocity(dong, -0.5) if (key == true) { door.isClosing = false; } else { door.isClosing = true; } door.openClose(); door.draw() for (let i = 0, len = map.length; i < len; i++) { //so boss bar renders over the map ctx.beginPath(); ctx.moveTo(map[i].vertices[0].x, map[i].vertices[0].y); for (let j = 0, length = map[i].vertices.length; j < length; j++) { ctx.lineTo(map[i].vertices[j].x, map[i].vertices[j].y); } ctx.lineTo(map[i].vertices[0].x, map[i].vertices[0].y); ctx.fillStyle = "rgba(68,68,68)"; ctx.strokeStyle = "transparent"; ctx.fill(); ctx.stroke(); // ctx.setLineDash([]); } for (let i = 0, len = mob.length; i < len; i++) { if (mob[i].restoreBoss) { ctx.save(); ctx.setTransform(1, 0, 0.5, 1, 0, 0); //slanted ctx.fillStyle = "rgba(100, 100, 100, 0.3)"; ctx.fillRect(canvas.width2 / 2, canvas.height2 / 10, canvas.width2, 30); ctx.fillStyle = "rgba(0,0,0,0.7)"; ctx.fillRect(canvas.width2 / 2, canvas.height2 / 10, canvas.width2 * mob[i].health, 30); ctx.restore(); } } }; const obj = { restoreBoss }; Object.assign(spawn, obj); //for next map, gonna be a rpg-like thingy I think }, map() { const elevator = level.elevator(-80.4, -931.6, 180, 50, -1550) 15900 && player.position.x < 16300 && player.position.y > -960.2 const slime = level.hazard(15900, -960, 400, 6000); const slime2 = level.hazard(15147.2, -1782.4, 2000, 822); const boost1 = level.boost(5950, -20, 700) const boost2 = level.boost(21088, -1672, 700) const boost3 = level.boost(19390, -31, 1700) const boost4 = level.boost(19390, -31, 1700) const boost5 = level.boost(17274, -1242, 1000) const portal = level.portal({ x: 443, y: -1636 }, Math.PI, { x: 21391.9, y: -1806.3 }, -Math.PI) const portal2 = level.portal({ x: 16838.3, y: -626.7 }, Math.PI, { x: 16882.8, y: -2566.5 }, -Math.PI) const buttonDoor = level.button(21889, -10) const door = level.door(19119, -2133, 110, 510, 480) const buttonDoor2 = level.button(18711, -2210) const door2 = level.door(17041, -412, 110, 510, 480) const buttonDoor3 = level.button(20456.6, -1636.2) const door3 = level.door(20238, -781.4, 88, 452, 412) //y=-1485 simulation.enableConstructMode() level.setPosToSpawn(0, -50); //normal spawn level.exit.x = 15316; level.exit.y = -84; spawn.mapRect(level.enter.x, level.enter.y + 20, 100, 20); //bump for level entrance spawn.mapRect(level.exit.x, level.exit.y + 20, 100, 20); //bump for level exit level.defaultZoom = 1800 simulation.zoomTransition(level.defaultZoom) document.body.style.backgroundColor = "#001738"; color.map = "#444" //custom map color level.custom = () => { //spawn.mapRect(22330, -2688.75, 400, 800); //spawn.mapRect(22330, -1793.5, 400, 800);//-46.25*2=-92.5 //spawn.mapRect(22330, -804.25, 400, 800);//-46.25*3 ctx.fillStyle = "rgba(63,247,251,0.8)" ctx.fillRect(22330, -2713.75, 550, 700) //15845.0, -1262.2 ctx.fillRect(22330, -1743.5, 550, 700) ctx.fillRect(22330, -754.25, 550, 700) ctx.fillRect(15845.0, -1262.2, 550, 300) ctx.fillStyle = "rgba(235,235,235,0.9)" ctx.fillRect(-192, -1973, 6484, 2071) ctx.fillRect(15109.5, -2867.5, 7284, 2971) ctx.fillStyle = "rgba(35,35,35,0.8)" ctx.fillRect(15145.9, -960, 200, 25) ctx.fillStyle = "rgba(255,255,255,0.9)" ctx.fillRect(-677.3, -610.9, 15, 15) ctx.fillRect(-910.4, 458.3, 15, 15) ctx.fillRect(-1029.0, 713.7, 15, 15) ctx.fillRect(42.6, 1332.2, 15, 15) ctx.fillRect(277.3, 751.8, 15, 15) ctx.fillRect(797.1, 553.2, 15, 15) ctx.fillRect(-1458.9, 340.9, 15, 15) ctx.fillRect(-1780.0, -54.6, 15, 15) ctx.fillRect(-1260.3, -686.4, 15, 15) ctx.fillRect(-2064.3, -394.6, 15, 15) ctx.fillRect(-1815.7, 1156.2, 15, 15) ctx.fillRect(-1998.1, 1118.4, 15, 15) buttonDoor.query(); buttonDoor.draw(); buttonDoor2.query(); buttonDoor2.draw(); buttonDoor3.query(); buttonDoor3.draw(); slime.query(); slime2.query(); ctx.fillStyle = `hsla(160, 100%, 43%,${0.3 + 0.07 * Math.random()})` ctx.fillRect(15900 + 400 * Math.random(), -1360, 2, 6000) if (buttonDoor.isUp) { door.isClosing = true } else { door.isClosing = false } if (buttonDoor2.isUp) { door2.isClosing = true } else { door2.isClosing = false } if (buttonDoor3.isUp) { door3.isClosing = true } else { door3.isClosing = false } door.openClose(); door2.openClose(); door3.openClose(); portal[2].query() portal[3].query() portal2[2].query() portal2[3].query() boost1.query(); boost2.query(); boost3.query(); boost4.query(); boost5.query(); level.exit.drawAndCheck(); level.enter.draw(); }; level.customTopLayer = () => { door.draw(); door2.draw(); door3.draw(); portal[0].draw(); portal[1].draw(); portal[2].draw(); portal[3].draw(); portal2[0].draw(); portal2[1].draw(); portal2[2].draw(); portal2[3].draw(); elevator.move() if (player.position.x > 15900 && player.position.x < 16300 && player.position.y > -1360.2) { Matter.Body.setVelocity(player, { x: player.velocity.x, y: player.velocity.y + 10 }); } else { Matter.Body.setVelocity(player, { x: player.velocity.x, y: player.velocity.y - 0.2 }); } }; //1273.2, -1404.7 //first ship base spawn.mapRect(-300, 0, 6684, 100); //lower floor spawn.mapRect(-300, -2071, 154, 2071); //far right wall spawn.mapRect(2511, -300, 1309, 308); //left big block spawn.mapRect(3820, -184, 1309, 184); //right big block spawn.mapRect(-300, -739, 2549, 100); //upper right floor spawn.mapRect(2056, -1309, 2764, 169); //upper center floor spawn.mapRect(2056, -1309, 193, 650); //upper left floor wall spawn.mapRect(4636, -1309, 193, 793); //upper right floor wall spawn.mapRect(4821, -654, 955, 138); //upper right floor spawn.mapRect(6237, -2071, 147, 2071); //far right wall spawn.mapRect(-300, -2071, 6684, 154); //roof //first ship details spawn.mapRect(245, -360, 70, 400); //start room wall spawn.mapRect(500, -1929, 154, 462); spawn.mapRect(185, -1517, 469, 77); spawn.mapRect(2773, -682, 469, 77); //walls in 1st room spawn.mapRect(3743, -566, 77, 469); spawn.mapRect(3947, -851, 469, 77); spawn.mapRect(5313, -1309, 1000, 70); //walls in second area spawn.mapRect(4818, -1006, 400, 70); spawn.mapRect(4768, -1626, 800, 70); spawn.mapRect(4760, -1626, 70, 400); //first ship blocks/debris spawn.debris(3267.6, -797.1, 700, 5); //16 debris per level spawn.debris(1626.0, -372.5, 1700, 8); //16 debris per level spawn.debris(1880.1, -1508.9, 3700, 16); //16 debris per level spawn.debris(5335.3, -1431.6, 3700, 16); //16 debris per level spawn.debris(1563.8, -1087.9, 700, 5); //16 debris per level spawn.bodyRect(1540, -1110, 218, 125, 0.9); //first ship mobs spawn.randomMob(2903.9, -754.5, 0.7); spawn.randomMob(5577.0, -217.0, 0.6); spawn.randomMob(765.8, -1029.7, 0.5); spawn.randomMob(20079.4, -2219.7, 0.6); spawn.randomMob(20079.4, -2219.7, 0.7); spawn.randomMob(20890.9, -1306.0, 0.5); spawn.randomMob(21284.2, -983.1, 0.5); spawn.randomMob(20381.0, -254.2, 0.7); spawn.randomMob(21027.8, -473.8, 0.6); spawn.randomMob(19448.2, -1323.3, 0.6); spawn.randomMob(18397.7, -711.2, 0.6); spawn.randomMob(15547.2, -2249.6, 0.6); spawn.randomSmallMob(16114.6, -2524.2); spawn.randomSmallMob(15378.9, -2549.6); spawn.randomSmallMob(893.5, -120.8); spawn.randomSmallMob(3521.8, -419.6); spawn.randomSmallMob(4386.2, -439.6); spawn.randomSmallMob(5667.0, -847.8); spawn.randomSmallMob(3158.5, -1581.8); spawn.randomSmallMob(3866.7, -1483.2); spawn.randomSmallMob(4652.3, -1729.4); spawn.randomSmallMob(1068.7, -106.1); spawn.randomSmallMob(3545.0, -413.0); spawn.randomSmallMob(4231.7, -446.3); spawn.randomSmallMob(1456.4, -1014.8); spawn.randomSmallMob(20432.4, -1374.3); spawn.randomSmallMob(20381.0, -254.2); spawn.randomSmallMob(20353.4, -1845.8); spawn.randomSmallMob(20353.4, -1845.8); spawn.randomSmallMob(20648.1, -136.8); spawn.randomSmallMob(20024.4, -2213.1); spawn.randomSmallMob(17438.7, -876.7); //second ship mobs spawn.debris(17732.3, -550.0, 700, 5); //16 debris per level spawn.debris(18006.4, -2181.3, 700, 5); //16 debris per level spawn.debris(16108.6, -2621.1, 700, 5); //16 debris per level spawn.debris(20823.6, -1332.1, 1300, 5); //16 debris per level spawn.debris(21095.5, -423.4, 700, 5); //16 debris per level spawn.randomSmallMob(1300, -70); // const index = mob.length spawn.shieldingBoss(769.8, -1119.0) // console.log(mob[index].onDeath) // requestAnimationFrame(() => mob[index].onDeath = function() {}); // console.log(mob[index].onDeath) //second ship base spawn.mapRect(15000, 0, 515, 185); //lower floor 1 spawn.mapRect(17015, 0, 5500, 185); //lower floor 2 spawn.mapRect(15000, -2972, 185, 2972); //left wall spawn.mapRect(15000, -2972, 7515, 185); //roof spawn.mapRect(22330, -2972, 185, 2972); //right wall spawn.mapRect(17002, -2972, 169, 2564); //left middle wall spawn.mapRect(19089, -2972, 169, 855); //right middle wall upper spawn.mapRect(19089, -1625, 169, 1800); //right middle wall lower spawn.mapRect(20760, -2972, 169, 1350); //medium wall left of portal spawn.mapRect(19720, -1625, 1725, 162); //right room upper floor spawn.mapRect(21440, -2325, 169, 863); //medium wall right of portal spawn.mapRect(19720, -855, 2725, 162); //right room lower floor //engines //y -2972 -> 0 spawn.mapRect(22330, -2763.75, 400, 800); spawn.mapRect(22330, -1793.5, 400, 800); spawn.mapRect(22330, -804.25, 400, 800); //second ship details spawn.mapRect(19904, -1465, 85, 362); //upper L spawn.mapRect(19542, -1191, 412, 88); //lower L spawn.mapRect(18546, -2199, 600, 82); //2nd room enternce wall spawn.mapRect(18546, -2499, 82, 2300); spawn.mapRect(18108, -326, 500, 82); //walls/floors in middle room spawn.mapRect(17750, -682, 300, 82); spawn.mapRect(17156, -468, 500, 60); spawn.mapRect(18022, -1082, 600, 82); spawn.mapRect(17151, -1196, 500, 82); spawn.mapRect(17453, -2060, 500, 82); spawn.mapRect(18197, -2269, 400, 82); spawn.mapRect(18108, -326, 500, 82); spawn.mapRect(20542, -1191, 612, 88); spawn.mapRect(20238, -1191, 88, 412); spawn.mapRect(21520, -1468, 88, 412); spawn.mapRect(20238, -330.2, 88, 412); spawn.mapRect(20819, -328.3, 412, 88); spawn.mapRect(21532, -708, 88, 412); spawn.mapRect(15483.8, 12.5, 388, 30); //broken floor spawn.mapRect(15487.6, 76.6, 488, 24); spawn.mapRect(15506.5, 134.2, 288, 45); spawn.mapVertex(16758.6, 135.3, "400 -30 -350 -40 -400 30 400 30"); spawn.mapVertex(16758.6, 55.3, "423 -30 -408 -20 -400 20 400 20"); //tank spawn.mapRect(15310, -960, 600, 135); spawn.mapRect(16290, -960, 800, 135); //in tank spawn.mapRect(16524.8, -2726.8, 40, 400); spawn.mapRect(16524.8, -2130.9, 400, 40); spawn.mapRect(16010.2, -2412.2, 300, 40); spawn.mapRect(15379.2, -2055.1, 400, 40); //add fuel tanks in the last room spawn.mapRect(21531.9, -707.8, 488, 8); //22185.5, -114.8 spawn.mapVertex(22207.8, -103, "325 -200 100 -200 325 -300"); spawn.mapRect(22056.6, -70, 225, 212); spawn.mapVertex(20723.1, -1734, "325 -200 100 -200 325 -300"); spawn.mapRect(20571.9, -1701.0, 225, 212); spawn.mapVertex(22207.8, -103, "325 -200 100 -200 325 -300"); spawn.mapRect(22056.6, -70, 225, 212); //spawn.mapVertex(x,y, "coordinates") //the parts in quotes is "x y x y x y x y x y" x and y need to be the coordinates of points that define the shape in a concave clockwise direction //second ship blocks/debris spawn.bodyRect(21525, -113, 50, 50, 9); //first button block spawn.bodyRect(18993, -2283, 50, 50, 9); //second button block spawn.bodyRect(20303, -1736, 50, 50, 9); //third button block let randomBoss = Math.floor(Math.random() * 5); //change the bosses spawn[["blinkBoss", "shooterBoss", "launcherBoss", "pulsarBoss", "beetleBoss", "bladeBoss", "revolutionBoss", "dragonFlyBoss", "spiderBoss"][randomBoss]](17902, -1689, 100, false); // powerUps.spawnStartingPowerUps(1475, -1175); // spawn.debris(750, -2200, 3700, 16); //16 debris per level // spawn.bodyRect(1540, -1110, 300, 25, 0.9); // spawn.randomSmallMob(1300, -70); // spawn.randomMob(2650, -975, 0.8); // spawn.randomGroup(1700, -900, 0.4); // if (simulation.difficulty > 1) spawn.randomLevelBoss(2200, -1300); // spawn.secondaryBossChance(100, -1500) powerUps.addResearchToLevel() //needs to run after mobs are spawned }, cantilever() { // made by Eclipse#7932 on discord, (TheSpudguy)(@PurpleSunsetGames on github) // simulation.enableConstructMode(); simulation.makeTextLog(`underpass by Eclipse#7932`); level.setPosToSpawn(0, -50); //normal spawn level.exit.x = 5500; level.exit.y = 950; spawn.mapRect(level.enter.x, level.enter.y + 20, 100, 20); //bump for level entrance spawn.mapRect(level.exit.x, level.exit.y + 20, 100, 20); //bump for level exit spawn.mapRect(level.exit.x - 50, level.exit.y + 30, 200, 100); // exit platform spawn.mapRect(level.exit.x - 50, level.exit.y - 300, 200, 100); // exit platform roof const endElevator = level.elevator(level.exit.x - 150, level.exit.y - 300, 100, 425, level.exit.y - 300); // end access door spawn.randomMob(-200, 350, Infinity); // random mob at the beginning spawn.mapRect(-100, 0, 600, 100); // main platform at start spawn.bodyRect(0, 300, 50, 50); // little squares at start (one of these should be taken while crossing the cantilever to complete the level more easily) spawn.bodyRect(100, 200, 50, 50); spawn.bodyRect(50, 250, 50, 50); spawn.mapRect(450, -20, 50, 20); // main platform ledge spawn.mapRect(-200, 500, 2200, 100); // lower platform spawn.mapRect(1850, 380, 100, 50); // cantilever block spawn.bodyRect(80, -1300, 70, 1300, 1, { friction: .03, frictionAir: .001 }); // cantilever spawn.mapRect(3400, 500, 300, 100); // lever platform spawn.mapRect(3650, 500, 100, 800); // pit spawn.mapRect(3650, 1300, 2600, 100); spawn.mapRect(6150, 600, 100, 800); spawn.mapRect(5650, 600, 100, 650); spawn.randomMob(4700, 550, Infinity); spawn.randomMob(4700, 450, Infinity); spawn.randomMob(4600, 550, Infinity); const toggle = level.toggle(3500, 500, false); // first lever const button = level.button(5900, 1300); const slidingWall = level.elevator(3750, -1200, 100, 1800, -1200); // first sliding wall level.defaultZoom = 1500; simulation.zoomTransition(level.defaultZoom); document.body.style.backgroundColor = "#d8badf"; // color.map = "#444" //custom map color level.custom = () => { level.exit.drawAndCheck(); level.enter.draw(); }; level.customTopLayer = () => { toggle.query(); button.query(); button.draw(); if (toggle.isOn) { slidingWall.force.y -= 400; } if (!button.isUp) { endElevator.force.y -= 100; } slidingWall.move(); endElevator.move() }; powerUps.addResearchToLevel(); //needs to run after mobs are spawned }, dojo() { // By simulation.makeTextLog(`underpass by weird_pusheen`); const vanishes = []; const smoofes = []; const leftRotor = level.rotor(-550, 900, 950, 25); leftRotor.frictionAir = 0.01; var leftSchwoof = level.boost(-20, -60, -2000); var rightSchwoof = level.button(2550, -50); var rightSchwoofState = false; var rightSchwoofLive = true; spawn.mapRect(2513, -39, 200, 100); var pathPoints = [ [0, 0], // Index 0 is owned by M and is set to M's position during play // this means that occasionally the boss will bonk M on the way to somewhere else, which gives it a chance to hurt M and gives the player a chance to hurt it [250, -750], /* Left bases */ [250, -2500], [350, -1500], // Left doorway [1150, -1500], // Home base [1150, -2750], // Upper base [1950, -1500], // Right doorway [2050, -750], /* Right bases */ [2050, -2500], [-150, -250], // Left porthole ]; function isntIn(point, array) { for (var x = 0; x < array.length; x++) { if (point[0] == array[x][0] && point[1] == array[x][1]) { return false; } } return true; } function isObstructed(v1, v2) { var ret = Matter.Query.ray(map, { x: v1[0], y: v1[1], }, { x: v2[0], y: v2[1] }).length != 0; return ret; // Kinda-ish stolen from mob.js } function pythag(p1, p2) { var dx = p1[0] - p2[0]; var dy = p1[1] - p2[1]; return Math.sqrt(dx * dx + dy * dy); } var path = undefined; // This is a stupid way to go about pathfinding code. I might even clean it up! function pathFind(goalPoint, startPoint, curPath = []) { var myPoint = startPoint; if (curPath.length) { myPoint = curPath[curPath.length - 1]; } if (path && (curPath.length >= path.length)) { // If we've already found a shorter or equal path, no reason to continue and waste CPU time return; // Minimizes for HOP COUNT, not PATH LENGTH - path length was buggy } if (!isObstructed(myPoint, goalPoint)) { // If the line to the goal point ain't blocked by a map object, we've arrived! path = [...curPath]; path.push(goalPoint); return; } pathPoints.forEach(testPoint => { if (isntIn(testPoint, curPath)) { // If it's reusing points, there's clearly something wrong if (!isObstructed(myPoint, testPoint)) { // If the line to the test point ain't blocked by a map object var thing = [...curPath]; thing.push(testPoint); pathFind(goalPoint, startPoint, thing); // Branch to a valid test point } } }); } level.setPosToSpawn(1200, 500); level.exit.x = 51500; level.exit.y = -1875; spawn.mapRect(level.enter.x, level.enter.y + 20, 100, 20); level.defaultZoom = 1500; simulation.zoomTransition(level.defaultZoom) document.body.style.backgroundColor = "#d8dadf"; spawn.mapRect(-500, 0, 3300, 300); // Floor spawn.mapRect(-100, -3000, 2500, 100); // Ceiling spawn.mapRect(-200, -3000, 100, 2600); // Left wall spawn.mapRect(2400, -3000, 100, 3000); // Right wall spawn.mapRect(500, -1000, 100, 500); /* obstruction blocks */ smoofes.push(map[map.length - 1]); spawn.mapRect(500, -2500, 100, 500); smoofes.push(map[map.length - 1]); spawn.mapRect(1700, -1000, 100, 500); smoofes.push(map[map.length - 1]); spawn.mapRect(1700, -2500, 100, 500); smoofes.push(map[map.length - 1]); spawn.mapRect(-1000, 550, 200, 50); // Left chonky stepppp low spawn.mapRect(-800, 300, 200, 50); // Left chonky stepppp high spawn.mapVertex(-1000, 1200, "0 0 100 0 700 500 700 700 0 700"); // Left chonky spawn.mapRect(3100, 550, 200, 50); // Right chonky stepppp low spawn.mapRect(2900, 300, 200, 50); // Right chonky stepppp high spawn.mapVertex(3300, 1200, "0 0 -100 0 -700 500 -700 700 0 700"); // Right chonky const leftElevator = level.elevator(-1400 - 300, 1450, 300, 100, 500); const rightElevator = level.elevator(-1400 + 5100, 1450, 300, 100, 500); spawn.mapRect(-150, -1700, 200, 50); spawn.mapRect(400, -2050, 200, 50); spawn.mapRect(1600, -1000, 200, 50); spawn.randomMob(1200, 700); spawn.randomMob(600, 1000); spawn.randomMob(1800, 1000); spawn.randomMob(3200, 400); spawn.randomMob(3000, 200); spawn.randomMob(-900, 400); spawn.randomMob(-700, 200); spawn.randomMob(1200, 1000); for (var i = 0; i < 4; i++) { spawn.randomSmallMob(Math.random() * 600 - 600, Math.random() * 3000 - 400); } spawn.grenadier(-300, -1000); spawn.grenadier(2600, -1000); spawn.mapRect(-1400, 1450, 5100, 100); // The True Floor const slime = level.hazard(-1250, 1400, 4800, 50); slime.maxHeight = 600; simulation.draw.body = function () { ctx.beginPath(); for (let i = 0, len = body.length; i < len; ++i) { if (!body[i].hidden) { let vertices = body[i].vertices; ctx.moveTo(vertices[0].x, vertices[0].y); for (let j = 1; j < vertices.length; j++) { ctx.lineTo(vertices[j].x, vertices[j].y); } ctx.lineTo(vertices[0].x, vertices[0].y); } } ctx.lineWidth = 2; ctx.fillStyle = color.block; ctx.fill(); ctx.strokeStyle = color.blockS; ctx.stroke(); } // Override the old draw code to allow intelligent hiding of blocks - preferably this becomes official code because it's just a single added if statement and makes a lot of things cleaner and more intelligent const vanish = function (x, y, width, height) { // normal vanishes don't work well on my map for some reason, so I rewrote x += width / 2; y += height / 2; const getVertices = function (bX, bY, bW, bH) { return [{ x: bX, y: bY, index: 0, isInternal: false }, { x: bX + bW, y: bY, index: 1, isInternal: false }, { x: bX + bW, y: bY + bH, index: 4, isInternal: false }, { x: bX, y: bY + bH, index: 3, isInternal: false }] }; const cMask = cat.player | cat.body | cat.bullet | cat.powerUp | cat.mob | cat.mobBullet; const vertices = getVertices(x, y, width, height); const block = body[body.length] = Bodies.fromVertices(x, y, vertices, { collisionFilter: { category: cat.map, mask: cMask }, isNoSetCollision: true, inertia: Infinity, //prevents rotation isNotHoldable: true, isNonStick: true, //this keep sporangium from sticking isTouched: false, cWidth: width, hiddenCycle: 0, isStatic: true, query() { if (this.cWidth <= 0) { if (this.cWidth > -100) { this.cWidth = -100; Matter.Body.setVertices(this, vertices); } this.isTouched = false; this.collisionFilter.mask = undefined; this.hidden = true; this.hiddenCycle++; if (this.hiddenCycle > 100) { if (Matter.Query.collides(this, [player]).length) { this.hiddenCycle = 50; } else { this.hiddenCycle = 0; this.cWidth = width; this.collisionFilter.mask = cMask; this.hidden = false; } } } else if (this.isTouched) { Matter.Body.setVertices(this, getVertices(x, y, this.cWidth, height * (this.cWidth / width))); this.cWidth -= 3; } else if (Matter.Query.collides(this, [player]).length) { // Elseif short circuit avoids expensive collision detection this.isTouched = true; } } }); return block; }; vanishes.push(vanish(800, 800, 800, 50)); vanishes.push(vanish(400, 1100, 400, 50)); vanishes.push(vanish(1600, 1100, 400, 50)); spawn.bodyRect(1700, 812, 300, 25, 1, { collisionFilter: { category: cat.body, mask: cat.player | cat.body | cat.bullet | cat.powerUp | cat.mob | cat.mobBullet | cat.map }, isNoSetCollision: true, isNotHoldable: true, isNonStick: true, //this keep sporangium from sticking restitution: 1, friction: 0, frictionAir: 0, frictionStatic: 0, query() { Matter.Body.setAngularVelocity(this, 0); Matter.Body.applyForce(this, this.position, { x: 0, y: -(this.position.y - 812) * 0.002 }); } }); const zigzag = body[body.length - 1]; Matter.Body.applyForce(zigzag, zigzag.position, { x: 0.1, y: 0 }); var buttonWasDown = false; level.customTopLayer = () => { } level.custom = () => { rightSchwoof.isUp = false; level.exit.drawAndCheck(); leftSchwoof.query(); level.enter.draw(); pathPoints[0][0] = m.pos.x; pathPoints[0][1] = m.pos.y; leftElevator.move(); rightElevator.move(); slime.query(); zigzag.query(); slime.levelRise(0.2); for (var i = 0; i < vanishes.length; i++) { vanishes[i].query(); } if (!rightSchwoofState) { var math = m.pos.y < leftRotor.position.y; Matter.Body.setAngularVelocity(leftRotor, (math ? 1 : -1) * Math.PI / 45); } if (rightSchwoofLive) { rightSchwoof.query(); rightSchwoof.draw(); if (rightSchwoofState) { ctx.fillStyle = "lightgreen"; } else { ctx.fillStyle = "red"; } ctx.beginPath(); ctx.arc(2615, -220, 40, 0, Math.PI * 2); ctx.fill(); } if (rightSchwoof.isUp) { buttonWasDown = true; } else if (buttonWasDown) { buttonWasDown = false; rightSchwoofState = !rightSchwoofState; } if (Matter.Query.collides(player, smoofes).length) { Matter.Body.applyForce(player, player.position, { x: 0, y: -0.015 }); } }; mobs.spawn(500, -500, 10, 100, "yellow"); /* TacticalBoss Modes: Spawn: Pathfinds to a point above M and starts dropping mobs. Learns which mobs to drop to cause the most damage, of course. Occasionally strikes at M. Hide: Pathfinds to the point furthest from M Strike: Pathfind really, really fast to M Recharge: Stop moving for a bit to "recharge" (this is so the player has a chance to hit it) It must always Hide or Recharge after Spawning or Striking. Which one it does is based on some factor I'll figure out. Pathfinding is a hypersimplified algorithm with hard-coded "points" that it can travel between. M is one of these. */ var boss = mob[mob.length - 1]; boss.isBoss = true; boss.damageReduction = 0.2 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1) boss.onDeath = function () { powerUps.spawnBossPowerUp(this.position.x, this.position.y); level.exit.x = 2560; level.exit.y = -90; rightSchwoofLive = false; }; var spawnables = {}; ["hopper", "stabber", "springer", "striker", "sneaker", "grower"].forEach((m) => { /* Used to be spawn.fullPickList, but some of those mobs don't do collision-only damage and would thus never be properly selected for */ if (spawn[m]) { spawnables[m] = { fun: spawn[m], name: m, weight: 1 } } }); boss.stabCycle = 0; boss.spawnCycle = 0; function spawny() { var totalWeight = 0; Object.keys(spawnables).forEach(key => { totalWeight += spawnables[key].weight; }); var cursorWeight = 0; var choice = Math.random(); var mC = undefined; Object.values(spawnables).forEach((thing) => { var lower = cursorWeight / totalWeight; cursorWeight += thing.weight; var upper = cursorWeight / totalWeight; if ((choice > lower && choice <= upper) || !mC) { mC = thing; } }); mC.fun(boss.position.x, boss.position.y); var sp = mob[mob.length - 1]; sp.typeName = mC.name; sp.onHit = () => { spawnables[sp.typeName].weight += 1; }; var oldFun = sp.onDeath; sp.onDeath = () => { /* Mobs that die are worth less */ oldFun.call(sp); spawnables[sp.typeName].weight -= 0.3; /* But not too much less */ }; } boss.spawnDelay = 40; boss.mode = "hide"; boss.modeSwitch = -1; // Randomize mode immediately boss.damageReduction = 0.1; var oldOnHit = boss.onHit; boss.onHit = () => { boss.modeSwitch = -1; // After striking the player, always switch modes oldOnHit.call(boss); }; boss.do = () => { path = undefined; var pfGoal = [0, 0]; boss.modeSwitch--; if (boss.modeSwitch < 0) { if (!boss.isShielded) { spawn.shield(boss, boss.position.x, boss.position.y, 0.75); // Every time the mode switches, have a 75% chance to gain a new shield } if (boss.mode == "hide" || boss.mode == "recharge") { if (Math.random() > 0.5) { boss.mode = "spawn"; } else { boss.mode = "strike"; } boss.modeSwitch = 600; } else { if (boss.mode == "strike") { boss.mode = "hide"; // Always hides after striking } else { if (Math.random() > 0.5) { boss.mode = "hide"; } else { boss.mode = "recharge"; // same when it goes into recharge mode spawn.shield(boss, boss.position.x, boss.position.y, 1); } } boss.modeSwitch = 200; } } if (boss.mode == "hide") { /* Find the furthest point from M and get to it */ var longest = 0; pathPoints.forEach(item => { if (item[0] == 1150) { return; } var iL = pythag(item, [m.pos.x, m.pos.y]); if (iL > longest) { longest = iL; pfGoal = item; } }); } else if (boss.mode == "strike") { pfGoal = pathPoints[0]; // Target M } else if (boss.mode == "spawn") { pfGoal = pathPoints[4]; // Go to Home Base to spawn } if (boss.mode != "recharge") { if (m.pos.x > 2350 || m.pos.x < -150 || m.pos.y > 50) { boss.mode = "hide"; } pathFind(pfGoal, [boss.position.x, boss.position.y]); if (!path) { return; // If it couldn't pathfind, just drift } var goalX = path[0][0]; var goalY = path[0][1]; var dX = goalX - boss.position.x; var dY = goalY - boss.position.y; var hyp = Math.sqrt(dX * dX + dY * dY); Matter.Body.applyForce(boss, { x: goalX, y: goalY }, { x: dX / hyp * 0.04 * (boss.mode == "strike" ? 2 : 1), y: dY / hyp * 0.04 * (boss.mode == "strike" ? 2 : 1)// - 0.005 }); } if (boss.mode == "spawn") { boss.stabCycle++; if (boss.stabCycle > 25) { if (Math.abs(dX) < 200 && dY > 0) { Matter.Body.applyForce(boss, { x: player.position.x, y: player.position.y }, { x: 0, y: 5 }); } boss.stabCycle = 0; } boss.spawnCycle++; if (boss.spawnCycle > boss.spawnDelay) { spawny(); boss.spawnDelay += 4; boss.spawnCycle = 0; } } }; boss.showHealthBar = true; powerUps.addResearchToLevel() //needs to run after mobs are spawned }, tlinat() { // _Destined_ formerly Richard0820#2652 simulation.makeTextLog(`tlinat by Richard0820`); simulation.fallHeight = 1 / 0, level.setPosToSpawn(0, -1e3), level.exit.x = 5100, level.exit.y = 3770, spawn.mapRect(level.enter.x, level.enter.y + 20, 100, 20), spawn.mapRect(level.exit.x, level.exit.y + 20, 100, 20), level.defaultZoom = 3000, simulation.zoomTransition(level.defaultZoom), document.body.style.backgroundColor = "#d8dadf"; let e = 0, t = 0; const boidsFlocking = function (mob, otherMobs) { const cohesionFactor = 0.01; const separationFactor = 0.0001; const alignmentFactor = 0.04; let averagePosition = { x: 0, y: 0 }; let averageVelocity = { x: 0, y: 0 }; let nearbyMobsCount = 0; for (const otherMob of otherMobs) { if (otherMob !== mob) { const distanceSquared = Vector.magnitudeSquared(Vector.sub(mob.position, otherMob.position)); const boidRangeSquared = 300 * 300; // Adjust boid range as needed if (distanceSquared < boidRangeSquared) { averagePosition = Vector.add(averagePosition, otherMob.position); averageVelocity = Vector.add(averageVelocity, otherMob.velocity); nearbyMobsCount++; } } } if (nearbyMobsCount > 0) { averagePosition = Vector.div(averagePosition, nearbyMobsCount); averageVelocity = Vector.div(averageVelocity, nearbyMobsCount); const cohesionForce = Vector.mult(Vector.sub(averagePosition, mob.position), cohesionFactor); mob.force = Vector.add(mob.force, cohesionForce); const separationForce = Vector.mult(Vector.sub(mob.position, averagePosition), separationFactor); mob.force = Vector.add(mob.force, separationForce); const alignmentForce = Vector.mult(Vector.sub(averageVelocity, mob.velocity), alignmentFactor); mob.force = Vector.add(mob.force, alignmentForce); } }; function ghoster(x, y, radius = 50 + Math.ceil(Math.random() * 90)) { mobs.spawn(x, y, 7, radius, "transparent"); let me = mob[mob.length - 1]; me.seeAtDistance2 = 300000; me.accelMag = 0.00004 + 0.00015 * simulation.accelScale; if (map.length) me.searchTarget = map[Math.floor(Math.random() * (map.length - 1))].position; //required for search // Matter.Body.setDensity(me, 0.0015); //normal is 0.001 me.damageReduction = 0.5 me.stroke = "transparent"; //used for drawGhost me.alpha = 1; //used in drawGhost me.isNotCloaked = false; //used in drawGhost me.isBadTarget = true; // me.leaveBody = false; me.collisionFilter.mask = cat.bullet //| cat.body me.showHealthBar = false; me.memory = 600; me.do = function () { boidsFlocking(me, mob);//Stack, increase power. if (this.speed > 7) { Matter.Body.setVelocity(this, { x: this.velocity.x * 0.8, y: this.velocity.y * 0.8 }); } this.seePlayerCheckByDistance(); this.checkStatus(); this.attraction(); this.search(); //draw if (this.distanceToPlayer2() < this.seeAtDistance2) { if (this.alpha < 1) this.alpha += 0.011 * simulation.CDScale; //near player go solid } else { if (this.alpha > 0) this.alpha -= 0.05; ///away from player, hide } if (this.alpha > 0) { if (this.alpha > 0.7 && this.seePlayer.recall) { this.healthBar(); if (!this.isNotCloaked) { this.isNotCloaked = true; this.isBadTarget = false; this.collisionFilter.mask = cat.player | cat.bullet } } //draw body ctx.beginPath(); const vertices = this.vertices; ctx.moveTo(vertices[0].x, vertices[0].y); for (let j = 1, len = vertices.length; j < len; ++j) { ctx.lineTo(vertices[j].x, vertices[j].y); } ctx.lineTo(vertices[0].x, vertices[0].y); // ctx.lineWidth = 1; ctx.fillStyle = `rgba(255,255,255,${this.alpha * this.alpha})`; ctx.fill(); } else if (this.isNotCloaked) { this.isNotCloaked = false; this.isBadTarget = true; this.collisionFilter.mask = cat.bullet; //can't touch player or walls } }; } function o(e, t, o) { const l = { J: [" #### ", " # ", " # ", " # ", " # # ", " # # ", " ## "], I: [" # ", " # ", " # ", " # ", " # ", " # ", " # "], N: [" # # ", " ## # ", " ## # ", " # ## ", " # ## ", " # # ", " # # "], " ": [" ", " ", " ", " ", " ", " ", " "], O: [" ## ", " # # ", " # # ", " # # ", " # # ", " # # ", " ## "], U: [" # # ", " # # ", " # # ", " # # ", " # # ", " # # ", " ### "], R: [" #### ", " # #", " #### ", " # # ", " # # ", " # #", " # #"], D: [" ### ", " # ## ", " # # ", " # # ", " # # ", " # ## ", " ### "], S: [" #### ", " # ", " # ", " ### ", " ## ", " # ", " ##### "], C: [" ##### ", " # ", " # ", " # ", " # ", " # ", " ##### "], V: [" # # ", " # # ", " # # ", " # # ", " # # ", " # # ", " # "], E: [" ##### ", " # ", " # ", " ##### ", " # ", " # ", " ##### "], ".": [" ", " ", " ", " ", " ", " ## ", " ## "], "/": [" #", " # ", " # ", " # ", " # ", " # ", "# "], G: [" ###### ", " # ", " # ", " # ### ", " # # ", " # # ", " ###### "], Q: [" ###### ", " # # ", " # # ", " # # ", " # # # ", " # # ", " #### # ", " # "], 8: [" ##### ", " # # ", " # # ", " ##### ", " # # ", " # # ", " ##### "], g: [" ##### ", " # # ", " # # ", " ##### ", " # ", " # ", " ###### "], Y: [" # # ", " # # ", " # # ", " # ", " # ", " # ", " # "], 4: [" # ", " # # ", " # # ", " # # ", " ###### ", " # ", " # "], W: [" # # ", " # # ", " # # ", " # # # ", " # # # # ", " ## ## ", " # # "], e: [" ###### ", " # # ", " # # ", " ####### ", " # ", " # ", " ###### "], c: [" ###### ", "# ", "# ", "# ", "# ", "# ", " ###### ", " "], m: [" # ", " ### ### ", " # # # ", " # # # ", " # # # ", " # # # ", " # # # "] }, a = (e, t) => { ctx.fillStyle = "black", ctx.fillRect(e, t, 50, 50) }, n = (e, t, o) => { const n = l[e]; if (n) for (let e = 0; e < n.length; e++) { const l = n[e]; for (let n = 0; n < l.length; n++) { if ("#" === l[n]) { a(t + 20 * n, o + 20 * e) } } } }; for (let l = 0; l < o.length; l++) { n(o[l], e + 250 * l - Math.abs(1.5 * e), t) } } simulation.makeTextLog(`
Look up
Walk right to tp to maze
Exit is at the bottom left`), Matter.Body.scale(player.parts[3], 2, 2), level.custom = () => { if (level.exit.drawAndCheck(), level.enter.draw(), player.position.y > 1e5 && Matter.Body.setPosition(player, { x: 5100, y: -5925 }), player.position.x > 2500 && 0 == e) { Matter.Body.setPosition(player, { x: 5100, y: -5925 }), e++; for (let e = 0; e < map.length; e++) Math.random() < .75 && ghoster(map[e].position.x, map[e].position.y); simulation.makeTextLog("Watch out for ghosters
Peace ✌️") } player.position.x > level.exit.x && player.position.x < level.exit.x + 100 && player.position.y > level.exit.y - 150 && player.position.y < level.exit.y - 0 && player.velocity.y < .15 && 0 == t && (t++, Matter.Body.scale(player.parts[3], .5, .5)) }, level.customTopLayer = () => { player.position.x > -1200 && player.position.x < 4500 && (o(2e3, -3e3, "JOIN OUR DISCORD SERVER"), o(1500, -2700, "DISCORD.GG/Q8gY4WeUcm")) }, spawn.mapRect(-1e3, -950, 5950, 100), spawn.mapRect(-1325, -3450, 100, 2575), spawn.mapRect(-1325, -950, 350, 100), spawn.mapRect(4850, -3400, 100, 2550), spawn.mapRect(-1325, -3450, 6275, 100), function (e, t, o, l, a) { const n = o / a, s = l / a, i = e - o / 2, p = t - l / 2, r = []; for (let e = 0; e < a; e++) { r[e] = []; for (let t = 0; t < a; t++) r[e][t] = 1 } const c = []; (function e(t, o) { r[t][o] = 0; const l = [{ dx: 0, dy: -1 }, { dx: 1, dy: 0 }, { dx: 0, dy: 1 }, { dx: -1, dy: 0 }]; l.sort((() => Math.random() - .5)); for (const n of l) { const l = t + 2 * n.dx, s = o + 2 * n.dy; l >= 0 && l < a && s >= 0 && s < a && 1 === r[l][s] && (r[t + n.dx][o + n.dy] = 0, r[l][s] = 0, c.push({ x: t + n.dx, y: o + n.dy }), e(l, s)) } })(0, 0), r[a - 1][a - 1] = 1; for (let e = -1; e < a + 1; e++) { let t = -1, o = -1; for (let l = -1; l < a + 1; l++) if (e >= 0 && e < a && l >= 0 && l < a && 1 === r[e][l]) - 1 === t && (t = l), o = l; else if (-1 !== t) { const l = i + e * n, a = p + t * s, r = n, c = (o - t + 1) * s; c !== s && spawn.mapRect(l, a, r, c), t = -1, o = -1 } } for (let e = -1; e < a + 1; e++) { let t = -1, o = -1; for (let l = -1; l < a + 1; l++) if (l >= 0 && l < a && e >= 0 && e < a && 1 === r[l][e]) - 1 === t && (t = l), o = l; else if (-1 !== t) { const l = i + t * n, a = p + e * s, r = (o - t + 1) * n, c = s; r !== n && spawn.mapRect(l, a, r, c), t = -1, o = -1 } } spawn.mapRect(i - n, p - s, n * a, s), spawn.mapRect(i - n, p - s, n, s * a), spawn.mapRect(i + (a - 1) * n, p - s, n, s * (a + 1)), spawn.mapRect(i - n, p + (a - 1) * s, n * (a + 1), s) }(1e4, -1e3, 1e4, 1e4, 50); }, ruins() { // by SiddhUPe // simulation.enableConstructMode() simulation.makeTextLog(`ruins by SiddhUPe`); level.setPosToSpawn(0, -50); //normal spawn level.exit.x = 19531; level.exit.y = 882 + 70; spawn.mapRect(level.enter.x, level.enter.y + 20, 100, 20); //bump for level entrance spawn.mapRect(level.exit.x, level.exit.y + 20, 100, 20); //bump for level exit level.defaultZoom = 1800 simulation.zoomTransition(level.defaultZoom) document.body.style.backgroundColor = "#d8dadf"; // color.map = "#444" //custom map color level.customTopLayer = () => { }; spawn.mapRect(875, 0, 1000, 100); spawn.mapRect(1825, -400, 50, 225); spawn.mapRect(1825, -400, 675, 50); spawn.mapRect(1825, 0, 675, 100); spawn.mapRect(2500, -575, 50, 225); spawn.mapRect(2500, -575, 850, 50); spawn.mapRect(2500, -100, 875, 175); spawn.mapRect(2500, 75, 875, 25); spawn.mapRect(3350, -575, 25, 50); spawn.mapRect(2450, -50, 75, 75); spawn.mapRect(2425, -25, 25, 25); spawn.mapRect(2475, -75, 25, 25); spawn.mapRect(3375, -575, 75, 325); spawn.mapRect(3375, -100, 75, 300); spawn.mapRect(3450, -50, 25, 250); spawn.mapRect(3475, -25, 25, 225); spawn.mapRect(3500, 0, 25, 200); spawn.mapRect(3525, 25, 25, 175); spawn.mapRect(3550, 75, 25, 125); spawn.mapRect(3550, 50, 25, 150); spawn.mapRect(3575, 75, 25, 125); spawn.mapRect(3600, 100, 25, 100); spawn.mapRect(3625, 150, 25, 50); spawn.mapRect(2875, -1375, 350, 75); spawn.mapRect(3150, -1375, 75, 350); spawn.mapRect(3100, -1300, 50, 50); spawn.mapRect(3075, -1300, 25, 25); spawn.mapRect(3125, -1250, 25, 25); spawn.mapRect(2825, -1375, 50, 125); spawn.mapRect(3100, -1025, 125, 50); spawn.mapRect(2800, -1350, 25, 75); spawn.mapRect(3125, -975, 75, 25); spawn.mapRect(3225, -1350, 25, 300); spawn.mapRect(2875, -1400, 275, 25); spawn.mapRect(2900, -1425, 225, 25); spawn.mapRect(3250, -1325, 25, 250); spawn.mapRect(2875, -1300, 25, 25); spawn.mapRect(3125, -1050, 50, 25); spawn.mapRect(5325, 800, 50, 225); spawn.mapRect(5325, 975, 300, 50); spawn.mapRect(5375, 925, 50, 50); spawn.mapRect(5375, 900, 25, 25); spawn.mapRect(5425, 950, 25, 25); spawn.mapRect(5325, 775, 100, 25); spawn.mapRect(5625, 925, 25, 100); spawn.mapRect(5350, 800, 50, 25); spawn.mapRect(5600, 950, 25, 50); spawn.mapRect(5300, 800, 25, 175); spawn.mapRect(5400, 1025, 225, 25); spawn.mapRect(5450, 1050, 125, 25); spawn.mapRect(5275, 850, 25, 100); spawn.mapRect(5350, 750, 50, 25); spawn.mapRect(5650, 950, 25, 50); spawn.mapRect(16775, -975, 275, 50); spawn.mapRect(17000, -975, 50, 200); spawn.mapRect(16775, -975, 25, 100); spawn.mapRect(17000, -775, 50, 50); spawn.mapRect(16975, -725, 75, 25); spawn.mapRect(16950, -925, 50, 50); spawn.mapRect(16925, -925, 75, 25); spawn.mapRect(17000, -925, 25, 75); spawn.mapRect(16975, -925, 25, 50); spawn.mapRect(16975, -925, 25, 75); spawn.mapRect(16800, -1000, 200, 25); spawn.mapRect(16850, -1025, 100, 25); spawn.mapRect(17050, -925, 25, 200); spawn.mapRect(17075, -925, 25, 150); spawn.mapRect(16775, -925, 50, 25); spawn.mapRect(17000, -750, 25, 25); spawn.mapRect(16975, -750, 25, 50); spawn.mapRect(16950, -725, 75, 25); spawn.mapRect(9475, -1150, 50, 200); spawn.mapRect(9475, -1150, 25, 25); spawn.mapRect(9475, -1150, 300, 50); spawn.mapRect(9725, -1150, 25, 25); spawn.mapRect(9725, -1150, 50, 200); spawn.mapRect(9500, -975, 25, 25); spawn.mapRect(9500, -975, 75, 25); spawn.mapRect(9700, -975, 25, 25); spawn.mapRect(9675, -975, 75, 25); spawn.mapRect(9525, -1175, 200, 25); spawn.mapRect(9550, -1200, 150, 100); spawn.mapRect(9450, -1125, 50, 150); spawn.mapRect(9750, -1125, 50, 150); spawn.mapRect(9525, -1100, 50, 50); spawn.mapRect(9675, -1100, 50, 50); spawn.mapRect(9575, -1100, 25, 25); spawn.mapRect(9650, -1100, 25, 25); spawn.mapRect(9500, -1050, 50, 25); spawn.mapRect(9700, -1100, 25, 75); spawn.mapRect(11925, -1175, 75, 275); spawn.mapRect(11925, -1175, 475, 75); spawn.mapRect(12325, -1175, 75, 275); spawn.mapRect(11925, -925, 175, 25); spawn.mapRect(12225, -925, 175, 25); spawn.mapRect(11950, -925, 125, 50); spawn.mapRect(12275, -925, 100, 50); spawn.mapRect(11925, -1200, 475, 25); spawn.mapRect(11975, -1225, 375, 25); spawn.mapRect(12000, -1225, 50, 25); spawn.mapRect(12000, -1275, 325, 75); spawn.mapRect(11900, -1175, 50, 250); spawn.mapRect(12375, -1175, 50, 250); spawn.mapRect(11900, -1150, 50, 150); spawn.mapRect(11875, -1150, 50, 200); spawn.mapRect(12375, -1150, 75, 200); spawn.mapRect(11975, -1100, 50, 25); spawn.mapRect(12300, -1100, 75, 25); spawn.mapRect(12300, -950, 25, 50); spawn.mapRect(12000, -950, 25, 75); spawn.mapRect(3625, 125, 25, 50); spawn.mapRect(3650, 150, 25, 50); spawn.mapRect(3675, 175, 25, 25); spawn.mapRect(3450, -75, 25, 50); spawn.mapRect(3475, -50, 25, 125); spawn.mapRect(3500, -25, 25, 125); spawn.mapRect(3500, 0, 50, 125); spawn.mapRect(3550, 25, 25, 125); spawn.mapRect(3575, 50, 25, 125); spawn.mapRect(3600, 75, 25, 75); spawn.mapRect(3600, 100, 50, 75); spawn.mapRect(3650, 125, 25, 75); spawn.mapRect(3675, 150, 25, 50); spawn.mapRect(3675, 150, 75, 450); spawn.mapRect(3675, 525, 700, 75); spawn.mapRect(4300, 150, 75, 450); mover = level.mover(3375, -100, 75, 100); pool = level.hazard(3750, 200, 550, 325); spawn.mapRect(-150, -225, 75, 325); spawn.mapRect(-150, -325, 425, 100); spawn.mapRect(-100, -400, 300, 75); spawn.mapRect(-25, -475, 150, 75); spawn.mapRect(200, -350, 25, 50); spawn.mapRect(-50, -425, 25, 100); spawn.mapRect(-125, -350, 25, 100); spawn.mapRect(100, -425, 50, 125); spawn.mapRect(1875, -450, 625, 50); spawn.mapRect(1950, -500, 550, 50); spawn.mapRect(2025, -525, 475, 25); spawn.mapRect(2025, -550, 525, 25); spawn.mapRect(2125, -575, 400, 25); spawn.mapRect(2125, -600, 1325, 50); spawn.mapRect(2475, -550, 950, 200); spawn.mapRect(1825, -350, 100, 175); spawn.mapRect(3350, -375, 25, 125); spawn.mapRect(1850, -425, 50, 25); spawn.mapRect(1925, -475, 125, 25); spawn.mapRect(2000, -525, 125, 25); spawn.mapRect(2100, -575, 200, 25); spawn.mapRect(2400, -650, 725, 75); spawn.mapRect(2500, -675, 475, 75); spawn.mapRect(2625, -725, 225, 100); spawn.mapRect(2675, -750, 125, 25); spawn.mapRect(2600, -700, 25, 50); spawn.mapRect(2850, -700, 25, 75); spawn.mapRect(3075, -625, 75, 50); spawn.mapRect(2375, -625, 50, 75); spawn.mapRect(1900, -350, 100, 75); spawn.mapRect(1925, -300, 50, 50); spawn.mapRect(1975, -350, 75, 50); spawn.mapRect(3325, -350, 50, 25); spawn.mapRect(150, -25, 1425, 50); spawn.mapRect(175, 75, 1200, 50); spawn.mapRect(400, -25, 575, 25); spawn.mapRect(425, -50, 750, 75); spawn.mapRect(1250, -50, 125, 100); spawn.mapRect(1175, -50, 100, 25); spawn.mapRect(725, 100, 500, 50); spawn.mapRect(625, -75, 300, 50); spawn.mapRect(250, -25, 225, 25); spawn.mapRect(1400, 75, 225, 50); spawn.mapRect(950, -75, 200, 25); spawn.mapRect(1200, -75, 125, 25); spawn.mapRect(425, -50, 100, 25); spawn.mapRect(450, -75, 100, 50); spawn.mapRect(250, -50, 125, 50); spawn.mapRect(250, 125, 125, 25); spawn.mapRect(475, 100, 100, 50); spawn.mapRect(650, 125, 25, 25); spawn.mapRect(675, 100, 75, 50); spawn.mapRect(825, 125, 200, 50); spawn.mapRect(-75, 75, 325, 50); spawn.mapRect(0, 100, 175, 50); spawn.mapRect(775, -100, 275, 50); spawn.mapRect(2475, 0, 925, 125); spawn.mapRect(2500, 50, 875, 100); spawn.mapRect(2550, 100, 775, 75); spawn.mapRect(2625, 150, 600, 50); spawn.mapRect(3225, 125, 275, 75); spawn.mapRect(1750, -225, 150, 50); spawn.mapRect(1800, -275, 50, 75); spawn.mapRect(1775, -250, 75, 75); spawn.mapRect(3200, -625, 250, 50); spawn.mapRect(3275, -650, 75, 25); spawn.mapRect(3175, -625, 25, 25); spawn.mapRect(3250, -700, 100, 100); spawn.mapRect(3200, -650, 75, 50); spawn.mapRect(3225, -675, 75, 100); spawn.mapRect(3325, -675, 50, 100); spawn.mapRect(3375, -650, 25, 75); spawn.mapRect(1575, -25, 100, 25); spawn.mapRect(1450, 100, 125, 50); spawn.mapRect(250, -300, 50, 50); spawn.mapRect(275, -275, 50, 25); spawn.mapRect(200, -275, 125, 50); spawn.mapRect(3725, 200, 50, 375); spawn.mapRect(3750, 275, 50, 300); spawn.mapRect(3800, 350, 25, 200); spawn.mapRect(3825, 425, 25, 150); spawn.mapRect(3850, 500, 25, 75); spawn.mapRect(4275, 250, 50, 325); spawn.mapRect(4250, 300, 50, 300); spawn.mapRect(4225, 375, 75, 200); spawn.mapRect(4200, 450, 75, 150); spawn.mapRect(4175, 500, 75, 75); spawn.mapRect(3950, 500, 150, 50); spawn.mapRect(4000, 500, 50, 25); spawn.mapRect(3875, 500, 425, 25); spawn.mapRect(3625, 200, 50, 75); spawn.mapRect(3575, 200, 50, 25); spawn.mapRect(3675, 275, 25, 25); spawn.mapRect(3650, 275, 25, 25); spawn.mapRect(3600, 200, 25, 50); spawn.mapRect(2600, 175, 25, 25); spawn.mapRect(2700, 175, 425, 100); spawn.mapRect(2650, 200, 75, 50); spawn.mapRect(3100, 200, 75, 50); spawn.mapRect(2675, 250, 25, 25); spawn.mapRect(2625, 200, 100, 25); spawn.mapRect(3150, 200, 75, 25); spawn.mapRect(3175, 225, 25, 25); spawn.mapRect(3775, 250, 25, 50); spawn.mapRect(3800, 275, 25, 125); spawn.mapRect(3800, 325, 50, 225); spawn.mapRect(3875, 400, 25, 200); spawn.mapRect(3825, 425, 50, 125); spawn.mapRect(3850, 375, 25, 100); spawn.mapRect(3900, 450, 25, 75); spawn.mapRect(3925, 475, 25, 50); spawn.mapRect(3450, -600, 25, 325); spawn.mapRect(3475, -575, 25, 275); spawn.mapRect(3500, -525, 25, 200); spawn.mapRect(3525, -500, 25, 150); spawn.mapRect(3550, -475, 25, 100); spawn.mapRect(2725, 250, 350, 50); spawn.mapRect(2750, 275, 300, 50); spawn.mapRect(3150, 250, 25, 25); spawn.mapRect(4325, 150, 975, 75); spawn.mapRect(4375, 225, 900, 25); spawn.mapRect(4375, 175, 875, 100); spawn.mapRect(4375, 225, 850, 75); spawn.mapRect(4375, 225, 825, 100); spawn.mapRect(4375, 275, 800, 75); spawn.mapRect(4350, 325, 800, 50); spawn.mapRect(4375, 350, 750, 50); spawn.mapRect(4375, 350, 725, 75); spawn.mapRect(4350, 375, 725, 75); spawn.mapRect(4350, 400, 700, 75); spawn.mapRect(4350, 425, 675, 75); spawn.mapRect(4350, 475, 650, 50); spawn.mapRect(4375, 500, 600, 50); spawn.mapRect(4375, 500, 575, 75); spawn.mapRect(4375, 550, 550, 50); spawn.mapRect(4425, 125, 775, 75); spawn.mapRect(5300, 175, 75, 50); spawn.mapRect(4475, 100, 150, 25); spawn.mapRect(4825, 125, 300, 25); spawn.mapRect(4800, 100, 250, 25); spawn.mapRect(5100, 100, 50, 25); spawn.mapRect(4650, 100, 75, 25); spawn.mapRect(5475, 225, 125, 300); spawn.mapRect(5450, 275, 25, 125); spawn.mapRect(5450, 450, 25, 75); spawn.mapRect(5425, 325, 25, 75); spawn.mapRect(5425, 475, 25, 50); spawn.mapRect(5575, 250, 50, 150); spawn.mapRect(5575, 450, 50, 50); spawn.mapRect(5475, 525, 125, 25); spawn.mapRect(5500, 550, 75, 25); spawn.mapRect(5525, 575, 25, 25); spawn.mapRect(3675, 575, 1050, 50); spawn.mapRect(4175, 600, 250, 50); spawn.mapRect(3850, 625, 100, 25); spawn.mapRect(3700, 625, 75, 25); spawn.mapRect(4050, 625, 50, 25); spawn.mapRect(4500, 625, 225, 25); spawn.mapRect(5725, 150, 75, 225); spawn.mapRect(5700, 175, 25, 150); spawn.mapRect(5775, 250, 50, 100); spawn.mapRect(5950, 325, 75, 75); spawn.mapRect(5925, 375, 25, 25); spawn.mapRect(6000, 350, 50, 50); spawn.mapRect(6125, 425, 1050, 75); spawn.mapRect(6425, 0, 750, 75); spawn.mapRect(6400, 25, 50, 50); spawn.mapRect(6500, -25, 675, 75); spawn.mapRect(6550, -25, 275, 25); spawn.mapRect(6475, -25, 125, 75); spawn.mapRect(6450, -25, 100, 100); spawn.mapRect(6475, -75, 700, 75); spawn.mapRect(6500, -75, 75, 25); spawn.mapRect(6500, -125, 675, 125); spawn.mapRect(6525, -150, 650, 125); spawn.mapRect(6550, -175, 625, 50); spawn.mapRect(6900, -200, 275, 75); spawn.mapRect(6925, -250, 250, 175); spawn.mapRect(6950, -275, 225, 75); spawn.mapRect(6975, -300, 200, 50); spawn.mapRect(7025, -325, 125, 125); spawn.mapRect(6400, 50, 75, 175); spawn.mapRect(6450, 50, 100, 100); spawn.mapRect(6475, 150, 25, 25); spawn.mapRect(6550, 75, 25, 25); spawn.mapRect(6375, 75, 25, 150); spawn.mapRect(6350, 100, 25, 75); spawn.mapRect(6650, 50, 225, 50); spawn.mapRect(6975, 75, 75, 25); spawn.mapRect(6625, -175, 225, 25); spawn.mapRect(6675, -200, 275, 25); spawn.mapRect(6750, -225, 250, 25); spawn.mapRect(6200, 475, 575, 50); spawn.mapRect(6925, 500, 125, 25); spawn.mapRect(6325, 400, 475, 25); spawn.mapRect(6950, 400, 200, 25); spawn.mapRect(7100, 75, 75, 100); spawn.mapRect(7075, 75, 25, 25); spawn.mapRect(7175, -300, 1650, 350); spawn.mapRect(7325, -700, 100, 450); spawn.mapRect(7600, -700, 100, 450); spawn.mapRect(7900, -700, 100, 450); spawn.mapRect(8200, -700, 100, 425); spawn.mapRect(8500, -700, 100, 425); spawn.mapRect(7275, -825, 1375, 125); spawn.mapRect(7400, -700, 50, 25); spawn.mapRect(7575, -700, 50, 25); spawn.mapRect(7875, -700, 150, 25); spawn.mapRect(8175, -700, 150, 25); spawn.mapRect(8475, -700, 150, 25); spawn.mapRect(7300, -325, 150, 75); spawn.mapRect(7575, -325, 150, 75); spawn.mapRect(7875, -325, 150, 75); spawn.mapRect(8175, -325, 150, 75); spawn.mapRect(8475, -325, 150, 75); spawn.mapRect(7700, -700, 25, 25); spawn.mapRect(7300, -700, 75, 25); spawn.mapRect(7150, 50, 75, 50); spawn.mapRect(7175, 100, 25, 25); spawn.mapRect(7225, 50, 25, 25); spawn.mapRect(7300, -850, 1325, 75); spawn.mapRect(7325, -875, 1275, 50); spawn.mapRect(7375, -900, 1200, 25); spawn.mapRect(7350, -900, 25, 25); spawn.mapRect(7375, -900, 200, 25); spawn.mapRect(7375, -925, 1175, 25); spawn.mapRect(7400, -950, 1125, 25); spawn.mapRect(7425, -975, 1075, 25); spawn.mapRect(7450, -1000, 1025, 25); spawn.mapRect(7675, -1050, 525, 50); spawn.mapRect(7700, -1050, 100, 25); spawn.mapRect(7700, -1075, 450, 25); spawn.mapRect(7725, -1100, 400, 25); spawn.mapRect(7775, -1125, 300, 25); spawn.mapRect(7650, -1025, 75, 25); spawn.mapRect(8200, -1025, 25, 25); spawn.mapRect(8825, -275, 25, 300); spawn.mapRect(8825, -225, 50, 125); spawn.mapRect(8850, -50, 25, 75); spawn.mapRect(7150, 425, 100, 425); spawn.mapRect(7150, 775, 1600, 100); spawn.mapRect(8750, 400, 75, 475); spawn.mapRect(8825, 400, 25, 475); spawn.mapRect(7225, 450, 50, 325); spawn.mapRect(7250, 500, 50, 275); spawn.mapRect(7300, 550, 25, 225); spawn.mapRect(7325, 600, 25, 175); spawn.mapRect(7350, 650, 25, 125); spawn.mapRect(7375, 675, 25, 100); spawn.mapRect(7400, 700, 25, 75); spawn.mapRect(7425, 725, 25, 50); spawn.mapRect(7450, 750, 25, 25); spawn.mapRect(8725, 425, 50, 375); spawn.mapRect(8700, 450, 75, 375); spawn.mapRect(8675, 475, 100, 375); spawn.mapRect(8650, 500, 125, 375); spawn.mapRect(8600, 525, 100, 350); spawn.mapRect(8575, 550, 100, 275); spawn.mapRect(8550, 575, 150, 250); spawn.mapRect(8525, 625, 100, 225); spawn.mapRect(8500, 675, 100, 125); spawn.mapRect(8625, 825, 25, 25); spawn.mapRect(8475, 700, 75, 75); spawn.mapRect(8450, 725, 50, 50); spawn.mapRect(8425, 750, 100, 25); wastepool = level.hazard(7250, 575, 1450, 200); spawn.mapRect(7375, 0, 250, 75); spawn.mapRect(7700, 0, 725, 75); spawn.mapRect(8575, 50, 150, 25); spawn.mapRect(7750, 50, 475, 50); spawn.mapRect(7425, 50, 175, 50); spawn.mapRect(8600, 50, 50, 50); spawn.mapRect(7200, 850, 875, 50); spawn.mapRect(8225, 850, 125, 50); spawn.mapRect(8475, 850, 275, 50); spawn.mapRect(7300, 875, 375, 50); spawn.mapRect(7925, 875, 100, 50); spawn.mapRect(8525, 875, 125, 50); spawn.mapRect(8250, 875, 75, 50); spawn.mapRect(7800, 900, 50, 25); spawn.mapRect(8125, 875, 50, 50); spawn.mapRect(8075, 875, 50, 50); spawn.mapRect(7125, 500, 25, 325); spawn.mapRect(7100, 475, 25, 300); spawn.mapRect(7075, 600, 50, 125); spawn.mapRect(7075, 500, 50, 25); spawn.mapRect(8850, 425, 75, 450); spawn.mapRect(8925, 475, 75, 400); spawn.mapRect(9000, 550, 75, 325); spawn.mapRect(9075, 650, 75, 25); spawn.mapRect(9075, 625, 75, 250); spawn.mapRect(9150, 675, 75, 200); spawn.mapRect(9225, 750, 75, 125); spawn.mapRect(8925, 450, 25, 25); spawn.mapRect(9000, 500, 25, 75); spawn.mapRect(9000, 525, 25, 100); spawn.mapRect(9000, 525, 50, 100); spawn.mapRect(9050, 575, 50, 100); spawn.mapRect(9075, 600, 50, 75); spawn.mapRect(9150, 650, 25, 100); spawn.mapRect(9225, 725, 50, 100); spawn.mapRect(9225, 700, 25, 100); spawn.mapRect(9300, 800, 1375, 75); spawn.mapRect(9300, 775, 25, 50); spawn.mapRect(9425, 775, 200, 25); spawn.mapRect(9500, 875, 200, 25); spawn.mapRect(9725, 825, 200, 75); spawn.mapRect(10000, 850, 125, 50); spawn.mapRect(10225, 850, 400, 50); spawn.mapRect(9775, 775, 125, 25); spawn.mapRect(10100, 775, 75, 75); spawn.mapRect(10275, 750, 225, 75); spawn.mapRect(9975, 775, 25, 50); spawn.mapRect(10000, 775, 25, 50); spawn.mapRect(10025, 775, 25, 75); spawn.mapRect(10675, 775, 50, 100); spawn.mapRect(10725, 725, 50, 150); spawn.mapRect(10775, 650, 50, 225); spawn.mapRect(10825, 575, 50, 300); spawn.mapRect(10875, 500, 50, 375); spawn.mapRect(10925, 425, 275, 450); spawn.mapRect(11200, 500, 50, 375); spawn.mapRect(11250, 575, 50, 300); spawn.mapRect(11300, 650, 50, 225); spawn.mapRect(11350, 725, 50, 150); spawn.mapRect(11400, 775, 50, 100); spawn.mapRect(10700, 750, 75, 75); spawn.mapRect(10775, 700, 25, 25); spawn.mapRect(10750, 700, 75, 75); spawn.mapRect(10800, 625, 125, 75); spawn.mapRect(10850, 550, 125, 50); spawn.mapRect(10900, 475, 150, 75); spawn.mapRect(11125, 475, 100, 50); spawn.mapRect(11200, 550, 75, 50); spawn.mapRect(11275, 625, 50, 75); spawn.mapRect(11325, 700, 50, 75); spawn.mapRect(11375, 750, 50, 75); spawn.mapRect(11550, 225, 1525, 75); spawn.mapRect(11450, 825, 1625, 50); spawn.mapRect(11450, 800, 1625, 75); spawn.mapRect(11525, -350, 50, 650); spawn.mapRect(11850, -350, 50, 650); spawn.mapRect(12225, -350, 50, 650); spawn.mapRect(12600, -350, 50, 650); spawn.mapRect(13000, -350, 75, 650); spawn.mapRect(11525, -200, 1525, 50); spawn.mapRect(11525, 50, 1550, 50); spawn.mapRect(11525, -400, 1550, 50); spawn.mapRect(11575, -425, 1450, 50); spawn.mapRect(11625, -450, 1325, 75); spawn.mapRect(11700, -475, 1175, 75); spawn.mapRect(11725, -500, 1125, 75); spawn.mapRect(11825, -400, 100, 75); spawn.mapRect(11825, 200, 100, 75); spawn.mapRect(12200, -375, 100, 50); spawn.mapRect(12200, 200, 100, 75); spawn.mapRect(12575, 200, 100, 75); spawn.mapRect(12575, -375, 100, 50); spawn.mapRect(11500, 825, 50, 25); spawn.mapRect(11550, 775, 175, 25); spawn.mapRect(11525, 875, 250, 25); spawn.mapRect(11875, 750, 225, 50); spawn.mapRect(11950, 850, 375, 50); spawn.mapRect(12500, 775, 250, 75); spawn.mapRect(12750, 850, 175, 50); // books spawn.bodyRect(11575, -300, 25, 100); spawn.bodyRect(11600, -300, 25, 100); spawn.bodyRect(11625, -300, 25, 100); spawn.bodyRect(11650, -300, 25, 100); spawn.bodyRect(11675, -300, 25, 100); spawn.bodyRect(11700, -300, 25, 100); spawn.bodyRect(11725, -300, 25, 100); spawn.bodyRect(11750, -300, 25, 100); spawn.bodyRect(11775, -300, 25, 100); spawn.bodyRect(11800, -300, 25, 100); spawn.bodyRect(11825, -300, 25, 100); spawn.bodyRect(11900, -50, 25, 100); spawn.bodyRect(11925, -50, 25, 100); spawn.bodyRect(11950, -50, 25, 100); spawn.bodyRect(11975, -50, 50, 100); spawn.bodyRect(12025, -50, 50, 100); spawn.bodyRect(12075, -50, 25, 100); spawn.bodyRect(12100, -50, 50, 100); spawn.bodyRect(12150, -50, 25, 100); spawn.bodyRect(12175, -50, 25, 100); spawn.bodyRect(12200, -50, 25, 100); spawn.bodyRect(11900, -300, 25, 100); spawn.bodyRect(11925, -300, 25, 100); spawn.bodyRect(11950, -225, 75, 25); spawn.bodyRect(12650, -50, 25, 100); spawn.bodyRect(12675, -50, 25, 100); spawn.bodyRect(12725, -50, 25, 100); spawn.bodyRect(12750, -50, 50, 100); spawn.bodyRect(12650, -275, 25, 75); spawn.bodyRect(12675, -275, 25, 75); spawn.bodyRect(12700, -275, 50, 75); spawn.bodyRect(12750, -275, 25, 75); spawn.bodyRect(12775, -275, 25, 75); spawn.bodyRect(12800, -275, 25, 75); spawn.bodyRect(12825, -275, 25, 75); spawn.bodyRect(12850, -275, 50, 75); spawn.bodyRect(12900, -275, 50, 75); spawn.bodyRect(12950, -275, 50, 75); spawn.mapRect(12200, 775, 175, 50); spawn.mapRect(11550, 250, 1500, 75); spawn.mapRect(11575, 275, 1450, 75); spawn.mapRect(11600, 325, 1400, 50); spawn.mapRect(11625, 350, 1350, 50); spawn.mapRect(11725, 375, 1150, 50); spawn.mapRect(11900, 400, 800, 50); spawn.mapRect(12100, 425, 425, 50); spawn.mapRect(12125, 475, 375, 25); spawn.mapRect(12200, 475, 225, 50); spawn.mapRect(11475, -400, 50, 700); spawn.mapRect(11450, -375, 50, 625); spawn.mapRect(11425, -350, 100, 600); spawn.mapRect(11400, -300, 75, 525); spawn.mapRect(11375, -250, 100, 400); spawn.mapRect(11350, -150, 50, 200); spawn.mapRect(13075, 825, 25, 50); spawn.mapRect(13100, 850, 25, 25); spawn.mapRect(13200, 700, 100, 225); spawn.mapRect(13300, 775, 25, 100); spawn.mapRect(13325, 825, 25, 50); spawn.mapRect(13175, 775, 25, 125); spawn.mapRect(13225, 675, 50, 25); spawn.mapRect(13225, 925, 50, 25); spawn.mapRect(9250, 75, 1400, 150); spawn.mapRect(9250, -225, 150, 300); spawn.mapRect(9250, -275, 575, 50); spawn.mapRect(9675, -225, 150, 300); spawn.mapRect(9325, -325, 400, 50); spawn.mapRect(9400, -350, 250, 25); spawn.mapRect(9475, -375, 125, 25); spawn.mapRect(9825, -225, 150, 300); spawn.mapRect(10225, -225, 150, 300); spawn.mapRect(9825, -275, 550, 50); spawn.mapRect(9900, -325, 375, 50); spawn.mapRect(9950, -350, 275, 25); spawn.mapRect(10000, -375, 175, 25); spawn.mapRect(10350, -275, 50, 375); spawn.mapRect(10400, -250, 25, 325); spawn.mapRect(10425, -225, 25, 375); spawn.mapRect(10450, -200, 25, 325); spawn.mapRect(10475, -175, 25, 350); spawn.mapRect(10500, -150, 25, 300); spawn.mapRect(10525, -125, 25, 300); spawn.mapRect(10550, -100, 25, 225); spawn.mapRect(10575, -75, 25, 200); spawn.mapRect(10600, -50, 25, 150); spawn.mapRect(10625, -25, 25, 175); spawn.mapRect(9225, -225, 25, 450); spawn.mapRect(9200, -175, 25, 400); spawn.mapRect(9175, -150, 50, 375); spawn.mapRect(9150, -125, 50, 350); spawn.mapRect(9400, -175, 25, 275); spawn.mapRect(9425, -125, 25, 200); spawn.mapRect(9650, -175, 25, 250); spawn.mapRect(9625, -125, 25, 275); spawn.mapRect(9975, -175, 25, 300); spawn.mapRect(10000, -125, 25, 250); spawn.mapRect(10200, -175, 25, 300); spawn.mapRect(10175, -125, 25, 225); spawn.mapRect(9325, 225, 225, 25); spawn.mapRect(9675, 225, 250, 50); spawn.mapRect(10075, 225, 200, 25); spawn.mapRect(10400, 200, 175, 50); spawn.mapRect(13425, 675, 1425, 100); spawn.mapRect(13450, 725, 375, 75); spawn.mapRect(13850, 775, 225, 50); spawn.mapRect(14150, 750, 300, 50); spawn.mapRect(14575, 750, 200, 75); spawn.mapRect(13550, 800, 150, 25); spawn.mapRect(14250, 800, 225, 25); spawn.mapRect(13425, 275, 1425, 100); spawn.mapRect(13475, 250, 1325, 75); spawn.mapRect(13550, 225, 1125, 75); spawn.mapRect(13600, 200, 1025, 25); spawn.mapRect(13650, 150, 925, 50); spawn.mapRect(13725, 100, 775, 100); spawn.mapRect(13825, 50, 525, 100); spawn.mapRect(13900, 0, 350, 75); spawn.mapRect(13975, -25, 175, 75); spawn.mapRect(13875, 25, 50, 50); spawn.mapRect(13800, 75, 75, 50); spawn.mapRect(13700, 125, 75, 50); spawn.mapRect(13625, 200, 50, 25); spawn.mapRect(13650, 175, 25, 25); spawn.mapRect(13625, 175, 125, 75); spawn.mapRect(14350, 75, 25, 50); spawn.mapRect(14250, 0, 25, 75); spawn.mapRect(14275, 50, 25, 50); spawn.mapRect(14275, 25, 25, 75); spawn.mapRect(14500, 125, 25, 75); spawn.mapRect(14575, 175, 25, 75); spawn.mapRect(13475, 650, 400, 50); spawn.mapRect(13975, 675, 75, 25); spawn.mapRect(14000, 650, 50, 50); spawn.mapRect(14150, 625, 675, 100); spawn.mapRect(14325, 625, 100, 25); spawn.mapRect(14300, 600, 325, 25); spawn.mapRect(13525, 325, 375, 100); spawn.mapRect(13975, 375, 400, 25); spawn.mapRect(14500, 325, 100, 75); spawn.mapRect(14850, 675, 50, 200); spawn.mapRect(14875, 700, 50, 175); spawn.mapRect(14925, 725, 50, 150); spawn.mapRect(14975, 750, 50, 125); spawn.mapRect(15025, 775, 50, 100); spawn.mapRect(15075, 750, 1150, 100); spawn.mapRect(15100, 825, 225, 50); spawn.mapRect(15500, 850, 225, 25); spawn.mapRect(15925, 775, 275, 100); spawn.mapRect(15775, 825, 50, 50); spawn.mapRect(15225, 250, 1050, 125); spawn.mapRect(15250, 200, 1000, 50); spawn.mapRect(15275, 175, 950, 50); spawn.mapRect(15300, 150, 900, 50); spawn.mapRect(15325, 125, 850, 25); spawn.mapRect(15350, 100, 800, 25); spawn.mapRect(15375, 75, 750, 25); spawn.mapRect(15400, 50, 700, 100); spawn.mapRect(15425, 25, 650, 75); spawn.mapRect(15450, 0, 600, 50); spawn.mapRect(15475, -25, 550, 75); spawn.mapRect(15500, -50, 500, 75); spawn.mapRect(15525, -75, 450, 75); spawn.mapRect(15550, -100, 400, 75); spawn.mapRect(15575, -125, 350, 75); spawn.mapRect(15600, -150, 300, 50); spawn.mapRect(15625, -175, 250, 50); spawn.mapRect(15650, -200, 200, 25); spawn.mapRect(15675, -225, 150, 75); spawn.mapRect(15700, -250, 100, 75); spawn.mapRect(16275, 250, 25, 125); spawn.mapRect(16250, 225, 25, 25); spawn.mapRect(15200, 250, 25, 125); spawn.mapRect(15225, 225, 25, 25); spawn.mapRect(15275, 350, 175, 50); spawn.mapRect(15550, 350, 425, 75); spawn.mapRect(16100, 375, 175, 25); spawn.mapRect(14700, -375, 50, 325); spawn.mapRect(14700, -425, 375, 50); spawn.mapRect(14750, -375, 125, 100); spawn.mapRect(14750, -275, 75, 75); spawn.mapRect(14850, -375, 100, 50); spawn.mapRect(14825, -275, 25, 25); spawn.mapRect(14950, -375, 25, 25); spawn.mapRect(14875, -325, 25, 25); spawn.mapRect(14725, -200, 50, 25); spawn.mapRect(14700, -75, 100, 25); spawn.mapRect(15050, -425, 25, 100); spawn.mapRect(14725, -450, 325, 25); spawn.mapRect(14775, -475, 225, 25); spawn.mapRect(14825, -500, 125, 25); spawn.mapRect(14675, -350, 25, 100); spawn.mapRect(14675, -175, 25, 75); spawn.mapRect(14850, 325, 25, 50); spawn.mapRect(5700, -725, 375, 50); spawn.mapRect(6025, -725, 50, 325); spawn.mapRect(5775, -675, 250, 25); spawn.mapRect(6000, -675, 25, 225); spawn.mapRect(5950, -650, 50, 75); spawn.mapRect(5900, -650, 75, 25); spawn.mapRect(6000, -575, 25, 25); spawn.mapRect(6050, -700, 50, 275); spawn.mapRect(5925, -625, 75, 25); spawn.mapRect(5775, -750, 100, 25); spawn.mapRect(5950, -750, 100, 25); spawn.mapRect(5675, -725, 25, 150); spawn.mapRect(5975, -400, 100, 25); spawn.mapRect(5650, -700, 25, 75); spawn.mapRect(5700, -675, 25, 50); spawn.mapRect(5700, -600, 25, 25); spawn.mapRect(15275, 750, 225, 25); spawn.mapRect(15225, 725, 250, 25); spawn.mapRect(15675, 725, 275, 100); spawn.mapRect(16075, 725, 125, 50); spawn.pulsar(5775.349354333542, -594.9058498351887) spawn.pulsar(5852.915433009502, -545.5679375496002) spawn.pulsar(5921.99534574469, -480.69487503053097) spawn.mapRect(3725, -975, 1525, 100); spawn.mapRect(3750, -650, 300, 75); spawn.mapRect(4300, -650, 300, 75); spawn.mapRect(4950, -650, 300, 75); spawn.mapRect(5250, -975, 75, 400); spawn.mapRect(3725, -975, 75, 400); spawn.mapRect(4325, -600, 250, 50); spawn.mapRect(4350, -550, 200, 25); spawn.mapRect(3800, -575, 225, 25); spawn.mapRect(3825, -550, 175, 25); spawn.mapRect(4975, -600, 275, 50); spawn.mapRect(5025, -550, 175, 25); spawn.mapRect(3800, -1025, 1450, 50); spawn.mapRect(3875, -1075, 1275, 50); spawn.mapRect(3975, -1125, 1000, 50); spawn.mapRect(3950, -1100, 50, 25); spawn.mapRect(3850, -1050, 150, 25); spawn.mapRect(3775, -1000, 200, 25); spawn.mapRect(5225, -975, 75, 25); spawn.mapRect(4950, -1100, 75, 125); spawn.mapRect(5100, -1050, 75, 75); spawn.mapRect(5225, -1000, 50, 100); spawn.mapRect(4350, -675, 150, 75); spawn.mapRect(4525, -650, 50, 25); spawn.mapRect(4550, -675, 50, 75); spawn.mapRect(3825, -650, 50, 25); spawn.mapRect(3825, -675, 150, 50); spawn.mapRect(4025, -675, 25, 100); spawn.mapRect(4950, -675, 75, 50); spawn.mapRect(5075, -675, 75, 75); spawn.mapRect(5200, -675, 75, 50); spawn.pulsar(4068.196906578167, -653.550201356403) spawn.pulsar(4147.672553167598, -651.0093457935446) spawn.pulsar(4228.863663369247, -653.4768859607283) spawn.pulsar(4619.092688319791, -657.3942377732394) spawn.pulsar(4724.821759138369, -653.4213864043036) spawn.pulsar(4873.583205330713, -657.4103118310311) spawn.pulsar(3871.920598597728, -804.0595760512573) spawn.pulsar(4053.019377134256, -778.0061810623848) spawn.pulsar(4211.732836201937, -780.4633597161928) spawn.pulsar(4380.7768131190005, -776.3400515412312) spawn.pulsar(4533.031170401828, -791.1397513503708) spawn.pulsar(4663.577749297493, -789.0488615794887) spawn.pulsar(4965.48351658387, -809.0025104385204) spawn.pulsar(5122.782442346123, -810.2526936643312) spawn.mapRect(3700, -875, 25, 250); spawn.mapRect(5325, -900, 25, 250); spawn.mapRect(5325, -850, 50, 150); spawn.mapRect(5375, -825, 25, 75); spawn.pulsar(14786.058375868968, -140.5759223979466) spawn.pulsar(14862.320083571307, -177.02507110101413) spawn.pulsar(14888.982047411475, -216.4856450590454) spawn.pulsar(14950.503812885598, -280.9333882582806) spawn.pulsar(15003.202057456116, -316.6767970823471) spawn.spinner(759.4093972764956, -356.0541595435453) spawn.spinner(1467.1412487475097, -617.4326431210314) spawn.mapRect(11850, 775, 50, 50); spawn.mapRect(12075, 775, 50, 50); spawn.mapRect(16225, 750, 75, 325); spawn.mapRect(16300, 775, 50, 325); spawn.mapRect(16350, 800, 50, 275); spawn.mapRect(16375, 825, 50, 200); spawn.mapRect(16450, 875, 25, 150); spawn.mapRect(16450, 875, 25, 225); spawn.mapRect(16400, 875, 50, 150); spawn.mapRect(16225, 1025, 250, 75); spawn.mapRect(16475, 925, 25, 175); spawn.mapRect(16500, 975, 25, 125); spawn.mapRect(16525, 1025, 25, 50); spawn.mapRect(16425, 1075, 150, 25); spawn.mapRect(16225, 1100, 1175, 75); spawn.mapRect(17200, 1050, 25, 50); spawn.mapRect(17225, 950, 25, 200); spawn.mapRect(17250, 800, 25, 300); spawn.mapRect(17275, 725, 25, 400); spawn.mapRect(17300, 750, 75, 400); spawn.mapRect(17300, 725, 100, 450); spawn.mapRect(16300, 250, 1075, 125); spawn.mapRect(16450, -75, 100, 400); spawn.mapRect(17100, -75, 100, 400); spawn.mapRect(16425, 200, 150, 50); spawn.mapRect(17075, 200, 150, 50); spawn.mapRect(16425, -75, 150, 25); spawn.mapRect(17075, -75, 150, 50); spawn.mapRect(16425, -50, 150, 50); spawn.mapRect(16575, -75, 525, 50); spawn.mapRect(17075, -50, 150, 50); spawn.mapRect(16550, -100, 550, 25); spawn.mapRect(16575, -125, 500, 75); spawn.mapRect(16600, -150, 450, 75); spawn.mapRect(16625, -175, 400, 75); spawn.mapRect(16675, -200, 275, 50); spawn.mapRect(16750, -225, 125, 100); spawn.mapRect(19700, 675, 50, 325); spawn.mapRect(19725, 700, 50, 250); spawn.mapRect(19750, 750, 25, 175); spawn.mapRect(16775, -25, 100, 275); spawn.mapRect(16750, -25, 150, 25); spawn.mapRect(16750, 225, 150, 50); spawn.pulsar(3037.797768861211, -1242.9871362505041) spawn.pulsar(3070.307596879197, -1219.5627538123044) spawn.pulsar(3111.2633762820287, -1107.7297980154415) spawn.pulsar(5417.516810698634, 842.824851834252) spawn.pulsar(5484.672534515589, 883.9519420960905) spawn.pulsar(5588.5457723826075, 907.389646857348) spawn.pulsar(16845.139047921595, -885.6942536135854) spawn.pulsar(16892.187197333486, -849.5235136465661) spawn.pulsar(16912.323783455256, -764.5275187038021) powerUps.spawn(2571.591711269197, -145.6717604789277, 'heal') powerUps.spawn(4415.693974666946, -15.077304620299628, 'heal') powerUps.spawn(7505.795753124317, -360.0330849392626, 'heal') powerUps.spawn(7809.5145838152075, -388.5517653996709, 'heal') powerUps.spawn(8049.726318297545, 534.4543327703304, 'heal') powerUps.spawn(8514.444440033667, 551.0268033205841, 'heal') powerUps.spawn(8927.146055851512, 407.25359241772685, 'heal') powerUps.spawn(9730.170170158563, 463.5594890235955, 'ammo') powerUps.spawn(9998.34942087522, 434.9511651200589, 'ammo') powerUps.spawn(10119.083720019844, 437.4195779326937, 'ammo') powerUps.spawn(10346.197135080345, 423.1868836972815, 'ammo') powerUps.spawn(1853.3194789931017, -36.87254038474242, 'ammo') powerUps.spawn(4491.396397908616, 40.2862012621236, 'ammo') powerUps.spawn(4954.207518897743, 50.27790416201856, 'ammo') spawn.mapRect(9125, -50, 75, 275); spawn.mapRect(9100, 0, 50, 225); spawn.mapRect(9075, 75, 75, 150); spawn.mapRect(9050, 150, 125, 50); spawn.mapRect(9050, 200, 225, 25); mover1 = level.mover(4000, -1125, 975, 25); mover2 = level.mover(15675, 725, 275, 25); spawn.mapRect(15025, -375, 25, 25); spawn.mapRect(12200, -150, 100, 25); spawn.mapRect(11825, -150, 100, 25); spawn.mapRect(11825, 75, 100, 50); spawn.mapRect(12200, 75, 100, 50); spawn.mapRect(12575, 100, 75, 25); spawn.mapRect(12625, 50, 50, 75); spawn.mapRect(12600, -175, 75, 50); spawn.mapRect(12575, -175, 75, 50); spawn.mapRect(14125, 650, 75, 25); spawn.mapRect(13875, 375, 50, 25); spawn.mapRect(13300, -525, 325, 50); spawn.mapRect(13575, -525, 50, 250); spawn.mapRect(13550, -300, 75, 25); spawn.mapRect(13300, -525, 25, 75); spawn.mapRect(13525, -475, 50, 50); spawn.mapRect(13500, -475, 100, 25); spawn.mapRect(13550, -475, 25, 100); spawn.mapRect(13325, -550, 275, 25); spawn.mapRect(13350, -575, 200, 25); spawn.mapRect(13625, -500, 25, 175); spawn.mapRect(13650, -450, 25, 75); spawn.mapRect(13500, 375, 75, 25); spawn.mapRect(15550, -950, 50, 225); spawn.mapRect(15575, -750, 75, 25); spawn.mapRect(15550, -950, 375, 50); spawn.mapRect(15925, -950, 50, 225); spawn.mapRect(15875, -750, 100, 25); spawn.mapRect(15575, -1000, 375, 75); spawn.mapRect(15625, -1050, 250, 100); spawn.mapRect(15600, -1025, 75, 50); spawn.mapRect(15875, -1000, 25, 25); spawn.mapRect(15825, -1025, 75, 75); spawn.mapRect(15700, -1100, 125, 50); spawn.mapRect(15650, -1075, 75, 50); spawn.mapRect(15800, -1075, 50, 75); spawn.mapRect(15575, -725, 50, 25); spawn.mapRect(15900, -725, 50, 25); spawn.mapRect(15525, -925, 25, 175); spawn.mapRect(15950, -925, 50, 200); spawn.mapRect(15500, -875, 25, 75); spawn.mapRect(16000, -875, 25, 75); spawn.mapRect(15600, -900, 50, 75); spawn.mapRect(15650, -900, 25, 25); spawn.mapRect(15600, -825, 25, 25); spawn.mapRect(15875, -900, 50, 50); spawn.mapRect(15850, -925, 75, 50); spawn.mapRect(15925, -875, 25, 25); spawn.mapRect(15925, -850, 25, 25); spawn.mapRect(15900, -875, 50, 50); bigpool = level.hazard(9075, 575, 1950, 250); spawn.mapRect(16000, -775, 25, 50); spawn.mapRect(16000, -800, 25, 25); spawn.mapRect(15500, -825, 25, 75); spawn.mapRect(15475, -850, 75, 75); spawn.mapRect(16000, -850, 50, 100); spawn.mapRect(10775, -450, 50, 200); spawn.mapRect(10775, -275, 100, 25); spawn.mapRect(11100, -450, 50, 200); spawn.mapRect(11050, -275, 75, 25); spawn.mapRect(10775, -450, 375, 25); spawn.mapRect(10825, -475, 275, 25); spawn.mapRect(10875, -500, 150, 25); spawn.mapRect(10900, -525, 100, 25); spawn.mapRect(10750, -425, 25, 150); spawn.mapRect(11150, -400, 25, 125); spawn.mapRect(10725, -400, 50, 100); spawn.mapRect(11150, -375, 50, 75); spawn.mapRect(10800, -250, 50, 25); spawn.mapRect(10825, -425, 50, 50); spawn.mapRect(10875, -425, 25, 25); spawn.mapRect(10825, -375, 25, 25); spawn.mapRect(11050, -425, 50, 50); spawn.mapRect(11025, -425, 25, 25); spawn.mapRect(11075, -375, 25, 25); spawn.mapRect(950, -1075, 50, 200); spawn.mapRect(950, -1125, 300, 50); spawn.mapRect(1000, -1075, 50, 50); spawn.mapRect(1050, -1075, 25, 25); spawn.mapRect(975, -1025, 50, 25); spawn.mapRect(975, -1150, 250, 25); spawn.mapRect(1000, -1175, 200, 25); spawn.mapRect(900, -1075, 50, 175); spawn.mapRect(875, -1050, 25, 125); spawn.mapRect(950, -875, 125, 25); spawn.mapRect(1250, -1125, 25, 125); spawn.mapRect(975, -850, 75, 25); spawn.mapRect(1250, -1100, 50, 75); spawn.mapRect(925, -900, 50, 25); spawn.mapRect(1050, -1200, 100, 25); spawn.mapRect(1225, -1000, 50, 25); spawn.mapRect(16375, 350, 900, 50); spawn.mapRect(16400, 375, 850, 50); spawn.mapRect(16425, 400, 800, 50); spawn.mapRect(16475, 425, 675, 50); spawn.mapRect(16625, 475, 375, 25); spawn.mapRect(16650, 500, 325, 25); spawn.mapRect(16675, 500, 275, 50); spawn.mapRect(17400, 775, 25, 325); spawn.mapRect(17425, 825, 25, 225); spawn.mapRect(16200, 900, 25, 225); spawn.mapRect(16175, 925, 25, 125); spawn.mapRect(16150, 975, 25, 25); spawn.mapRect(16400, 1150, 850, 50); spawn.mapRect(16475, 1175, 650, 50); spawn.mapRect(16575, 1225, 450, 25); spawn.sneaker(7895.471733263175, 257.75477496554186) spawn.sneaker(8109.4934675858085, 349.44686618726473) spawn.sneaker(7525.886813944122, 391.9511482895349) spawn.sneaker(8076.43795816953, 441.14947363958373) spawn.pulsar(1064.583377612331, -976.2077284446908) spawn.pulsar(1158.3436115513837, -1054.4975368803214) spawn.pulsar(10966.055009228428, -373.8481911663377) spawn.pulsar(10913.989668763379, -261.59108542627166) spawn.pulsar(13454.158594286884, -402.8270664336466) spawn.pulsar(13360.079608974078, -246.97797933698774) spawn.pulsar(13497.913481830354, -251.68317759640576) spawn.pulsar(15687.09056963911, -850.8426925141155) spawn.pulsar(15829.058084589731, -785.4134546702737) spawn.pulsar(15674.313958480483, -685.0594164868394) spawn.pulsar(15819.881465281747, -686.4370174238113) spawn.sneakBoss(18189.441342796745, 537.6633241821036) thirdpool = level.hazard(16425, 925, 925, 200); spawn.mapRect(17675, -525, 75, 725); spawn.mapRect(17625, -475, 75, 650); spawn.mapRect(17575, -425, 75, 575); spawn.mapRect(17700, -525, 1125, 75); spawn.mapRect(17675, 175, 1125, 75); spawn.mapRect(18775, -525, 75, 775); spawn.mapRect(18825, -475, 75, 675); spawn.mapRect(18900, -450, 50, 625); spawn.mapRect(18950, -400, 50, 500); spawn.mapRect(17750, -575, 1000, 50); spawn.mapRect(17775, -625, 950, 50); spawn.mapRect(17800, -675, 900, 75); spawn.mapRect(17825, -725, 850, 125); spawn.mapRect(17850, -750, 800, 25); spawn.mapRect(17750, 125, 50, 50); spawn.mapRect(17750, 100, 25, 25); spawn.mapRect(17800, 150, 25, 25); spawn.mapRect(17750, -450, 75, 75); spawn.mapRect(17750, -400, 25, 50); spawn.mapRect(17800, -450, 50, 25); spawn.mapRect(18750, -450, 25, 25); spawn.mapRect(18725, -450, 50, 50); spawn.mapRect(18700, -475, 25, 25); spawn.mapRect(18725, -450, 25, 25); spawn.mapRect(18700, -450, 75, 25); spawn.mapRect(18750, -425, 25, 50); spawn.mapRect(18725, 125, 75, 75); spawn.mapRect(18700, 150, 50, 50); spawn.mapRect(18750, 100, 75, 50); spawn.mapRect(17850, 150, 850, 50); spawn.mapRect(17825, 150, 25, 50); spawn.mapRect(17550, -350, 25, 450); spawn.mapRect(19000, -325, 25, 400); spawn.mapRect(18000, -775, 475, 25); spawn.mapRect(18025, -800, 425, 75); spawn.mapRect(18050, -825, 375, 75); spawn.mapRect(18075, -850, 325, 50); spawn.mapRect(18100, -875, 275, 100); spawn.mapRect(18125, -900, 225, 75); spawn.mapRect(18150, -925, 175, 75); spawn.mapRect(17275, 750, 1775, 125); spawn.mapRect(17475, 725, 450, 50); spawn.mapRect(18200, 725, 200, 50); spawn.mapRect(18650, 725, 225, 75); spawn.shieldingBoss(18253.51035871325, -131.1707821125636) // spawn.blockBoss(12604.846253470663, 607.6074958800299) spawn.mapRect(17725, 250, 1025, 25); spawn.mapRect(17775, 275, 925, 25); spawn.mapRect(17800, 300, 875, 25); spawn.mapRect(17850, 325, 775, 25); spawn.mapRect(17375, 275, 25, 75); spawn.mapRect(19050, 750, 25, 275); spawn.mapRect(19075, 775, 25, 250); spawn.mapRect(19100, 800, 25, 225); spawn.mapRect(19125, 850, 25, 175); spawn.mapRect(19150, 875, 25, 150); spawn.mapRect(19175, 925, 25, 100); spawn.mapRect(19200, 950, 25, 75); spawn.mapRect(19000, 850, 100, 175); spawn.mapRect(19050, 975, 650, 50); spawn.mapRect(19425, 650, 275, 50); spawn.mapRect(19675, 650, 50, 375); spawn.mapRect(19050, 1025, 625, 25); spawn.mapRect(19075, 1050, 575, 25); spawn.mapRect(19250, 1100, 200, 25); spawn.mapRect(19175, 1075, 375, 25); spawn.mapRect(19450, 625, 225, 25); spawn.mapRect(19500, 600, 150, 50); spawn.mapRect(19625, 700, 50, 50); spawn.mapRect(19600, 700, 25, 25); spawn.mapRect(19650, 750, 25, 25); spawn.mapRect(19400, 650, 25, 100); spawn.mapRect(19375, 675, 25, 50); spawn.mapRect(17600, 875, 250, 25); spawn.mapRect(18100, 850, 375, 50); spawn.mapRect(18650, 875, 325, 25); pooldunker = level.mover(7175, 425, 50, 25); level.custom = () => { level.exit.drawAndCheck(); pooldunker.VxGoal = 90; pooldunker.push(); mover.VxGoal = 45; mover.push(); level.enter.draw(); pool.query(); wastepool.query(); thirdpool.query(); mover1.VxGoal = 12; mover1.push(); mover2.VxGoal = 24; mover2.push(); bigpool.query(); for (i = 0; i < mob.length; i++) { if (mob[i].isBoss == false) { mob[i].damageReduction = 0.13 } } }; spawn.mapRect(-100, 0, 1000, 100); powerUps.addResearchToLevel() //needs to run after mobs are spawned }, ace() { //join us at discord.gg/Q8gY4WeUcm simulation.makeTextLog(`ace by Richard0820`); let isDestroyed = false; const ace = { spawnOrbitals(who, radius, chance = Math.min(0.25 + simulation.difficulty * 0.005)) { if (Math.random() < chance) { // simulation.difficulty = 50 const len = Math.floor(Math.min(15, 3 + Math.sqrt(simulation.difficulty))) // simulation.difficulty = 40 on hard mode level 10 const speed = (0.003 + 0.004 * Math.random() + 0.002 * Math.sqrt(simulation.difficulty)) * ((Math.random() < 0.5) ? 1 : -1) const offSet = 6.28 * Math.random() for (let i = 0; i < len; i++) ace.orbital(who, radius, i / len * 2 * Math.PI + offSet, speed) } }, orbital(who, radius, phase, speed) { // for (let i = 0, len = 7; i < len; i++) spawn.orbital(me, radius + 250, 2 * Math.PI / len * i) mobs.spawn(who.position.x, who.position.y, 8, 12, "rgb(0,0,0)"); let me = mob[mob.length - 1]; me.stroke = "transparent"; Matter.Body.setDensity(me, 0.01); //normal is 0.001 me.leaveBody = false; me.isDropPowerUp = false; me.isBadTarget = true; me.isUnstable = true; //dies when blocked me.showHealthBar = false; me.isOrbital = true; // me.isShielded = true me.collisionFilter.category = cat.mobBullet; me.collisionFilter.mask = cat.bullet; //cat.player | cat.map | cat.body me.do = function () { //if host is gone if (!who || !who.alive) { this.death(); return } //set orbit const time = simulation.cycle * speed + phase const orbit = { x: Math.cos(time), y: Math.sin(time) } Matter.Body.setPosition(this, Vector.add(Vector.add(who.position, who.velocity), Vector.mult(orbit, radius))) //damage player if (Matter.Query.collides(this, [player]).length > 0 && !(m.isCloak && tech.isIntangible) && m.immuneCycle < m.cycle) { m.immuneCycle = m.cycle + m.collisionImmuneCycles; //player is immune to damage for 30 cycles const dmg = 0.03 * simulation.dmgScale m.damage(dmg); simulation.drawList.push({ //add dmg to draw queue x: this.position.x, y: this.position.y, radius: Math.sqrt(dmg) * 200, color: simulation.mobDmgColor, time: simulation.drawTime }); this.death(); } }; }, shield(target, x, y, chance = Math.min(0.02 + simulation.difficulty * 0.005, 0.2) + tech.duplicationChance(), isExtraShield = false) { if (this.allowShields && Math.random() < chance) { mobs.spawn(x, y, 9, target.radius + 30, "rgba(255,255,255,0.9)"); let me = mob[mob.length - 1]; me.stroke = "rgb(0,0,0)"; Matter.Body.setDensity(me, 0.00001) //very low density to not mess with the original mob's motion me.shield = true; me.damageReduction = 0.05 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1) me.isUnblockable = true me.isExtraShield = isExtraShield //this prevents spamming with tech.isShieldAmmo me.collisionFilter.category = cat.mobShield me.collisionFilter.mask = cat.bullet; consBB[consBB.length] = Constraint.create({ bodyA: me, bodyB: target, //attach shield to target stiffness: 0.4, damping: 0.1 }); Composite.add(engine.world, consBB[consBB.length - 1]); me.onDamage = function () { //make sure the mob that owns the shield can tell when damage is done this.alertNearByMobs(); this.fill = `rgba(255,255,255,${0.3 + 0.6 * this.health})` }; me.leaveBody = false; me.isDropPowerUp = false; me.showHealthBar = false; me.shieldTargetID = target.id target.isShielded = true; target.shieldID = me.id me.onDeath = function () { //clear isShielded status from target for (let i = 0, len = mob.length; i < len; i++) { if (mob[i].id === this.shieldTargetID) mob[i].isShielded = false; } }; me.do = function () { this.checkStatus(); }; mob.unshift(me); //move shield to the front of the array, so that mob is behind shield graphically //swap order of shield and mob, so that mob is behind shield graphically // mob[mob.length - 1] = mob[mob.length - 2]; // mob[mob.length - 2] = me; } }, groupShield(targets, x, y, radius, stiffness = 0.4) { const nodes = targets.length mobs.spawn(x, y, 9, radius, "rgba(255,255,255,0.9)"); let me = mob[mob.length - 1]; me.stroke = "rgb(0,0,0)"; Matter.Body.setDensity(me, 0.00001) //very low density to not mess with the original mob's motion me.frictionAir = 0; me.shield = true; me.damageReduction = 0.075 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1) me.collisionFilter.category = cat.mobShield me.collisionFilter.mask = cat.bullet; for (let i = 0; i < nodes; ++i) { mob[mob.length - i - 2].isShielded = true; //constrain to all mob nodes in group consBB[consBB.length] = Constraint.create({ bodyA: me, bodyB: mob[mob.length - i - 2], stiffness: stiffness, damping: 0.1 }); Composite.add(engine.world, consBB[consBB.length - 1]); } me.onDamage = function () { this.alertNearByMobs(); //makes sure the mob that owns the shield can tell when damage is done this.fill = `rgba(255,255,255,${0.3 + 0.6 * this.health})` }; me.onDeath = function () { //clear isShielded status from target for (let j = 0; j < targets.length; j++) { for (let i = 0, len = mob.length; i < len; i++) { if (mob[i].id === targets[j]) mob[i].isShielded = false; } } }; me.leaveBody = false; me.isDropPowerUp = false; me.showHealthBar = false; mob[mob.length - 1] = mob[mob.length - 1 - nodes]; mob[mob.length - 1 - nodes] = me; me.do = function () { this.checkStatus(); }; }, slasher2(x, y, radius = 33 + Math.ceil(Math.random() * 30)) { mobs.spawn(x, y, 6, radius, "rgb(0,0,0)"); let me = mob[mob.length - 1]; Matter.Body.rotate(me, 2 * Math.PI * Math.random()); me.accelMag = 0.0009 * simulation.accelScale; me.torqueMagnitude = 0.000012 * me.inertia //* (Math.random() > 0.5 ? -1 : 1); me.frictionStatic = 0; me.friction = 0; me.frictionAir = 0.035; me.delay = 140 * simulation.CDScale; me.cd = 0; me.swordRadius = 0; me.swordVertex = 1 me.swordRadiusMax = 275 + 3.5 * simulation.difficulty; me.swordRadiusGrowRate = me.swordRadiusMax * (0.011 + 0.0002 * simulation.difficulty) me.isSlashing = false; me.swordDamage = 0.03 * simulation.dmgScale me.laserAngle = 3 * Math.PI / 5 const seeDistance2 = 200000 ace.shield(me, x, y); me.onDamage = function () { }; me.do = function () { this.checkStatus(); this.seePlayerByHistory(15); this.attraction(); this.sword() //does various things depending on what stage of the sword swing }; me.swordWaiting = function () { if ( this.seePlayer.recall && this.cd < simulation.cycle && this.distanceToPlayer2() < seeDistance2 && Matter.Query.ray(map, this.position, this.playerPosRandomY()).length === 0 && Matter.Query.ray(body, this.position, this.playerPosRandomY()).length === 0 ) { this.laserAngle = -Math.PI / 6 this.sword = this.swordGrow this.accelMag = 0 } } me.sword = me.swordWaiting //base function that changes during different aspects of the sword swing me.swordGrow = function () { this.laserSword(this.vertices[0], this.angle + this.laserAngle); this.laserSword(this.vertices[1], this.angle + this.laserAngle + (Math.PI / 3)); this.laserSword(this.vertices[2], this.angle + this.laserAngle + (Math.PI * 2 / 3)); this.laserSword(this.vertices[3], this.angle + this.laserAngle + Math.PI); this.laserSword(this.vertices[4], this.angle + this.laserAngle + (Math.PI * 4 / 3)); this.laserSword(this.vertices[5], this.angle + this.laserAngle + (Math.PI * 5 / 3)); this.swordRadius += this.swordRadiusGrowRate if (this.swordRadius > this.swordRadiusMax || this.isStunned) { this.sword = this.swordSlash this.spinCount = 0 } } me.swordSlash = function () { this.laserSword(this.vertices[0], this.angle + this.laserAngle); this.laserSword(this.vertices[1], this.angle + this.laserAngle + (Math.PI / 3)); this.laserSword(this.vertices[2], this.angle + this.laserAngle + (Math.PI * 2 / 3)); this.laserSword(this.vertices[3], this.angle + this.laserAngle + Math.PI); this.laserSword(this.vertices[4], this.angle + this.laserAngle + (Math.PI * 4 / 3)); this.laserSword(this.vertices[5], this.angle + this.laserAngle + (Math.PI * 5 / 3)); this.torque += this.torqueMagnitude; this.spinCount++ if (this.spinCount > 100 || this.isStunned) { this.sword = this.swordWaiting this.swordRadius = 0 this.accelMag = 0.001 * simulation.accelScale; this.cd = simulation.cycle + this.delay; } } me.laserSword = function (where, angle) { const vertexCollision = function (v1, v1End, domain) { for (let i = 0; i < domain.length; ++i) { let v = domain[i].vertices; const len = v.length - 1; for (let j = 0; j < len; j++) { results = simulation.checkLineIntersection(v1, v1End, v[j], v[j + 1]); if (results.onLine1 && results.onLine2) { const dx = v1.x - results.x; const dy = v1.y - results.y; const dist2 = dx * dx + dy * dy; if (dist2 < best.dist2 && (!domain[i].mob || domain[i].alive)) best = { x: results.x, y: results.y, dist2: dist2, who: domain[i], v1: v[j], v2: v[j + 1] }; } } results = simulation.checkLineIntersection(v1, v1End, v[0], v[len]); if (results.onLine1 && results.onLine2) { const dx = v1.x - results.x; const dy = v1.y - results.y; const dist2 = dx * dx + dy * dy; if (dist2 < best.dist2) best = { x: results.x, y: results.y, dist2: dist2, who: domain[i], v1: v[0], v2: v[len] }; } } }; best = { x: null, y: null, dist2: Infinity, who: null, v1: null, v2: null }; const look = { x: where.x + this.swordRadius * Math.cos(angle), y: where.y + this.swordRadius * Math.sin(angle) }; vertexCollision(where, look, body); // vertexCollision(where, look, mob); vertexCollision(where, look, map); if (!m.isCloak) vertexCollision(where, look, [playerBody, playerHead]); if (best.who && (best.who === playerBody || best.who === playerHead) && m.immuneCycle < m.cycle) { m.immuneCycle = m.cycle + m.collisionImmuneCycles + 60; //player is immune to damage for an extra second m.damage(this.swordDamage); simulation.drawList.push({ //add dmg to draw queue x: best.x, y: best.y, radius: this.swordDamage * 1500, color: "rgba(80,0,255,0.5)", time: 20 }); } if (best.dist2 === Infinity) best = look; ctx.beginPath(); //draw beam ctx.moveTo(where.x, where.y); ctx.lineTo(best.x, best.y); ctx.strokeStyle = "rgba(0,0,0,0.1)"; // 0 path ctx.lineWidth = 15; ctx.stroke(); ctx.strokeStyle = "rgba(0,0,0,0.5)"; // 0 path ctx.lineWidth = 4; ctx.setLineDash([70 + 300 * Math.random(), 55 * Math.random()]); ctx.stroke(); // Draw it ctx.setLineDash([]); } }, slasher3(x, y, radius = 33 + Math.ceil(Math.random() * 30)) { const sides = 6 mobs.spawn(x, y, sides, radius, "rgb(0,0,0)"); let me = mob[mob.length - 1]; Matter.Body.rotate(me, 2 * Math.PI * Math.random()); me.accelMag = 0.0005 * simulation.accelScale; me.frictionStatic = 0; me.friction = 0; me.frictionAir = 0.02; me.delay = 150 * simulation.CDScale; me.cd = 0; me.cycle = 0; me.swordVertex = 1 me.swordRadiusInitial = radius / 2; me.swordRadius = me.swordRadiusInitial; me.swordRadiusMax = 750 + 6 * simulation.difficulty; me.swordRadiusGrowRateInitial = 1.08 me.swordRadiusGrowRate = me.swordRadiusGrowRateInitial//me.swordRadiusMax * (0.009 + 0.0002 * simulation.difficulty) me.isSlashing = false; me.swordDamage = 0.04 * simulation.dmgScale me.laserAngle = 3 * Math.PI / 5 const seeDistance2 = me.swordRadiusMax * me.swordRadiusMax ace.shield(me, x, y); me.onDamage = function () { }; me.do = function () { this.checkStatus(); this.seePlayerByHistory(15); this.sword() //does various things depending on what stage of the sword swing }; me.swordWaiting = function () { this.attraction(); if ( this.seePlayer.recall && this.cd < simulation.cycle && this.distanceToPlayer2() < seeDistance2 && Matter.Query.ray(map, this.position, this.playerPosRandomY()).length === 0 && Matter.Query.ray(body, this.position, this.playerPosRandomY()).length === 0 ) { //find vertex closest to the player let dist = Infinity for (let i = 0, len = this.vertices.length; i < len; i++) { const D = Vector.magnitudeSquared(Vector.sub({ x: this.vertices[i].x, y: this.vertices[i].y }, m.pos)) if (D < dist) { dist = D this.swordVertex = i } } this.laserAngle = this.swordVertex / sides * 2 * Math.PI + Math.PI / sides this.sword = this.swordGrow this.cycle = 0 this.swordRadius = this.swordRadiusInitial //slow velocity but don't stop Matter.Body.setVelocity(this, Vector.mult(this.velocity, 0.5)) //set angular velocity to 50% // Matter.Body.setAngularVelocity(this, this.angularVelocity * 0.5) //gently rotate towards the player with a torque, use cross product to decided clockwise or counterclockwise const laserStartVector = Vector.sub(this.position, this.vertices[this.swordVertex]) const playerVector = Vector.sub(this.position, m.pos) const cross = Matter.Vector.cross(laserStartVector, playerVector) this.torque = 0.00002 * this.inertia * (cross > 0 ? 1 : -1) } } me.sword = me.swordWaiting //base function that changes during different aspects of the sword swing me.swordGrow = function () { const angle = this.angle + this.laserAngle; const end = { x: this.vertices[this.swordVertex].x + this.swordRadiusMax * Math.cos(angle), y: this.vertices[this.swordVertex].y + this.swordRadiusMax * Math.sin(angle) }; const dx = end.x - this.vertices[this.swordVertex + 1 > (sides - 1) ? 0 : this.swordVertex + 1].x; const dy = end.y - this.vertices[this.swordVertex + 1 > (sides - 1) ? 0 : this.swordVertex + 1].y; const angle1 = Math.atan2(dy, dx) * (180 / Math.PI); const dx1 = end.x - this.vertices[this.swordVertex - 1 < 0 ? (sides - 1) : this.swordVertex - 1].x; const dy1 = end.y - this.vertices[this.swordVertex - 1 < 0 ? (sides - 1) : this.swordVertex - 1].y; const angle2 = Math.atan2(dy1, dx1) * (180 / Math.PI); this.laserSpear(this.vertices[this.swordVertex], this.angle + this.laserAngle); this.laserSpear(this.vertices[this.swordVertex + 1 > (sides - 1) ? 0 : this.swordVertex + 1], angle1 * (Math.PI / 180)) this.laserSpear(this.vertices[this.swordVertex - 1 < 0 ? (sides - 1) : this.swordVertex - 1], angle2 * (Math.PI / 180)) Matter.Body.setVelocity(this, Vector.mult(this.velocity, 0.9)) // this.swordRadius += this.swordRadiusGrowRate this.cycle++ // this.swordRadius = this.swordRadiusMax * Math.sin(this.cycle * 0.03) this.swordRadius *= this.swordRadiusGrowRate if (this.swordRadius > this.swordRadiusMax) this.swordRadiusGrowRate = 1 / this.swordRadiusGrowRateInitial // if (this.swordRadius > this.swordRadiusMax) this.swordRadiusGrowRate = -Math.abs(this.swordRadiusGrowRate) if (this.swordRadius < this.swordRadiusInitial || this.isStunned) { // this.swordRadiusGrowRate = Math.abs(this.swordRadiusGrowRate) this.swordRadiusGrowRate = this.swordRadiusGrowRateInitial this.sword = this.swordWaiting this.swordRadius = 0 this.cd = simulation.cycle + this.delay; } } me.laserSpear = function (where, angle) { const vertexCollision = function (v1, v1End, domain) { for (let i = 0; i < domain.length; ++i) { let v = domain[i].vertices; const len = v.length - 1; for (let j = 0; j < len; j++) { results = simulation.checkLineIntersection(v1, v1End, v[j], v[j + 1]); if (results.onLine1 && results.onLine2) { const dx = v1.x - results.x; const dy = v1.y - results.y; const dist2 = dx * dx + dy * dy; if (dist2 < best.dist2 && (!domain[i].mob || domain[i].alive)) best = { x: results.x, y: results.y, dist2: dist2, who: domain[i], v1: v[j], v2: v[j + 1] }; } } results = simulation.checkLineIntersection(v1, v1End, v[0], v[len]); if (results.onLine1 && results.onLine2) { const dx = v1.x - results.x; const dy = v1.y - results.y; const dist2 = dx * dx + dy * dy; if (dist2 < best.dist2) best = { x: results.x, y: results.y, dist2: dist2, who: domain[i], v1: v[0], v2: v[len] }; } } }; best = { x: null, y: null, dist2: Infinity, who: null, v1: null, v2: null }; const look = { x: where.x + this.swordRadius * Math.cos(angle), y: where.y + this.swordRadius * Math.sin(angle) }; vertexCollision(where, look, body); // vertexCollision(where, look, mob); vertexCollision(where, look, map); if (!m.isCloak) vertexCollision(where, look, [playerBody, playerHead]); if (best.who && (best.who === playerBody || best.who === playerHead)) { this.swordRadiusGrowRate = 1 / this.swordRadiusGrowRateInitial //!!!! this retracts the sword if it hits the player if (m.immuneCycle < m.cycle) { m.immuneCycle = m.cycle + m.collisionImmuneCycles + 60; //player is immune to damage for an extra second m.damage(this.swordDamage); simulation.drawList.push({ //add dmg to draw queue x: best.x, y: best.y, radius: this.swordDamage * 1500, color: "rgba(80,0,255,0.5)", time: 20 }); } } if (best.dist2 === Infinity) best = look; ctx.beginPath(); //draw beam ctx.moveTo(where.x, where.y); ctx.lineTo(best.x, best.y); ctx.strokeStyle = "rgba(0,0,0,0.1)"; // 0 path ctx.lineWidth = 15; ctx.stroke(); ctx.strokeStyle = "rgba(0,0,0,0.5)"; // 0 path ctx.lineWidth = 4; ctx.setLineDash([70 + 300 * Math.random(), 55 * Math.random()]); ctx.stroke(); // Draw it ctx.setLineDash([]); } }, stabber(x, y, radius = 25 + Math.ceil(Math.random() * 12), spikeMax = 7) { if (radius > 80) radius = 65; mobs.spawn(x, y, 6, radius, "rgb(0,0,0)"); //can't have sides above 6 or collision events don't work (probably because of a convex problem) let me = mob[mob.length - 1]; me.isVerticesChange = true me.accelMag = 0.0006 * simulation.accelScale; // me.g = 0.0002; //required if using this.gravity me.isInvulnerable = false me.delay = 360 * simulation.CDScale; me.spikeVertex = 0; me.spikeLength = 0; me.isSpikeGrowing = false; me.spikeGrowth = 0; me.isSpikeReset = true; me.collisionFilter.mask = cat.map | cat.body | cat.bullet | cat.player //can't touch other mobs Matter.Body.rotate(me, Math.PI * 0.1); ace.shield(me, x, y); // me.onDamage = function () {}; // me.onHit = function() { //run this function on hitting player // }; me.onDeath = function () { if (this.spikeLength > 4) { this.spikeLength = 4 const spike = Vector.mult(Vector.normalise(Vector.sub(this.vertices[this.spikeVertex], this.position)), this.radius * this.spikeLength) this.vertices[this.spikeVertex].x = this.position.x + spike.x this.vertices[this.spikeVertex].y = this.position.y + spike.y // this.vertices = Matter.Vertices.hull(Matter.Vertices.clockwiseSort(this.vertices)) } }; me.do = function () { this.seePlayerByLookingAt(); this.checkStatus(); this.attraction(); if (this.isSpikeReset) { if (this.seePlayer.recall) { const dist = Vector.sub(this.seePlayer.position, this.position); const distMag = Vector.magnitude(dist); if (distMag < radius * spikeMax) { //find nearest vertex let nearestDistance = Infinity for (let i = 0, len = this.vertices.length; i < len; i++) { //find distance to player for each vertex const dist = Vector.sub(this.seePlayer.position, this.vertices[i]); const distMag = Vector.magnitude(dist); //save the closest distance if (distMag < nearestDistance) { this.spikeVertex = i nearestDistance = distMag } } this.spikeLength = 1 this.isSpikeGrowing = true; this.isSpikeReset = false; Matter.Body.setAngularVelocity(this, 0) } me.isInvulnerable = false } } else { if (this.isSpikeGrowing) { this.spikeLength += Math.pow(this.spikeGrowth += 0.02, 8) // if (this.spikeLength < 2) { // this.spikeLength += 0.035 // } else { // this.spikeLength += 1 // } if (this.spikeLength > spikeMax) { this.isSpikeGrowing = false; this.spikeGrowth = 0 } } else { Matter.Body.setAngularVelocity(this, this.angularVelocity * 0.8) //reduce rotation this.spikeLength -= 0.3 if (this.spikeLength < 1) { this.spikeLength = 1 this.isSpikeReset = true this.radius = radius } } const spike = Vector.mult(Vector.normalise(Vector.sub(this.vertices[this.spikeVertex], this.position)), radius * this.spikeLength) this.vertices[this.spikeVertex].x = this.position.x + spike.x this.vertices[this.spikeVertex].y = this.position.y + spike.y me.isInvulnerable = true // this.radius = radius * this.spikeLength; } if (this.isInvulnerable) { ctx.beginPath(); let vertices = this.vertices; ctx.moveTo(vertices[0].x, vertices[0].y); for (let j = 1; j < vertices.length; j++) ctx.lineTo(vertices[j].x, vertices[j].y); ctx.lineTo(vertices[0].x, vertices[0].y); ctx.lineWidth = 13 + 5 * Math.random(); ctx.strokeStyle = `rgba(255,255,255,${0.5 + 0.2 * Math.random()})`; ctx.stroke(); me.damageReduction = 0; } else { me.damageReduction = 1; } }; }, slash(x, y, radius = 80) { let targets = [] const sides = 6; mobs.spawn(x, y, 6, radius, "#000000"); let me = mob[mob.length - 1]; Matter.Body.rotate(me, 2 * Math.PI * Math.random()); targets.push(me.id) //add to shield protection const nodeBalance = Math.random() const nodes2 = Math.min(15, Math.floor(2 + 4 * nodeBalance + 0.75 * Math.sqrt(simulation.difficulty))) me.isBoss = true; me.isSlashBoss = true; me.showHealthBar = false; me.damageReduction = 0.1 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1) me.startingDamageReduction = me.damageReduction me.isInvulnerable = false me.frictionAir = 0.02 me.seeAtDistance2 = 1000000; me.accelMag = 0.0004 + 0.00015 * simulation.accelScale; Matter.Body.setDensity(me, 0.0005); //normal is 0.001 me.collisionFilter.mask = cat.bullet | cat.player | cat.body | cat.map me.memory = Infinity; me.seePlayerFreq = 20 me.lockedOn = null; me.laserRange = 500; me.torqueMagnitude = 0.00024 * me.inertia * (Math.random() > 0.5 ? -1 : 1); me.delay = 70 + 70 * simulation.CDScale; me.cd = 0; me.swordRadius = 0; me.swordVertex = 1 me.swordRadiusMax = 1100 + 20 * simulation.difficulty; me.swordRadiusGrowRate = me.swordRadiusMax * (0.005 + 0.0003 * simulation.difficulty) me.isSlashing = false; me.swordDamage = 0.07 * simulation.dmgScale me.laserAngle = 3 * Math.PI / 5 me.eventHorizon = 550; const seeDistance2 = 200000 ace.shield(me, x, y); const rangeInnerVsOuter = Math.random() let speed = (0.006 + 0.001 * Math.sqrt(simulation.difficulty)) * ((Math.random() < 0.5) ? 1 : -1) let range = radius + 350 + 200 * rangeInnerVsOuter + nodes2 * 7 for (let i = 0; i < nodes2; i++) ace.orbital(me, range, i / nodes2 * 2 * Math.PI, speed) const orbitalIndexes = [] //find indexes for all the current nodes2 for (let i = 0; i < nodes2; i++) orbitalIndexes.push(mob.length - 1 - i) // add orbitals for each orbital range = Math.max(60, 100 + 100 * Math.random() - nodes2 * 3 - rangeInnerVsOuter * 80) speed = speed * (1.25 + 2 * Math.random()) const subNodes = Math.max(2, Math.floor(6 - 5 * nodeBalance + 0.5 * Math.sqrt(simulation.difficulty))) for (let j = 0; j < nodes2; j++) { for (let i = 0, len = subNodes; i < len; i++) ace.orbital(mob[orbitalIndexes[j]], range, i / len * 2 * Math.PI, speed) } for (let i = 0, len = 3 + 0.5 * Math.sqrt(simulation.difficulty); i < len; i++) ace.spawnOrbitals(me, radius + 40 + 10 * i, 1); const springStiffness = 0.00014; const springDampening = 0.0005; me.springTarget = { x: me.position.x, y: me.position.y }; const len = cons.length; cons[len] = Constraint.create({ pointA: me.springTarget, bodyB: me, stiffness: springStiffness, damping: springDampening }); Composite.add(engine.world, cons[cons.length - 1]); cons[len].length = 100 + 1.5 * radius; me.cons = cons[len]; me.springTarget2 = { x: me.position.x, y: me.position.y }; const len2 = cons.length; cons[len2] = Constraint.create({ pointA: me.springTarget2, bodyB: me, stiffness: springStiffness, damping: springDampening, length: 0 }); Composite.add(engine.world, cons[cons.length - 1]); cons[len2].length = 100 + 1.5 * radius; me.cons2 = cons[len2]; me.onDamage = function () { }; me.onDeath = function () { isDestroyed = true; this.removeCons(); powerUps.spawnBossPowerUp(this.position.x, this.position.y); }; me.do = function () { for (let i = 0; i < this.vertices.length; i++) { this.harmField(this.vertices[i].x, this.vertices[i].y); } this.seePlayerByHistory(40); this.springAttack(); this.checkStatus(); this.sword() //does various things depending on what stage of the sword swing const eventHorizon = this.eventHorizon * (1 + 0.2 * Math.sin(simulation.cycle * 0.008)) me.laserRange = eventHorizon; }; me.swordWaiting = function () { if ( this.seePlayer.recall && this.cd < simulation.cycle && this.distanceToPlayer2() < seeDistance2 && !m.isCloak && Matter.Query.ray(map, this.position, this.playerPosRandomY()).length === 0 && Matter.Query.ray(body, this.position, this.playerPosRandomY()).length === 0 ) { //find vertex farthest away from player let dist = 0 for (let i = 0, len = this.vertices.length; i < len; i++) { const D = Vector.magnitudeSquared(Vector.sub({ x: this.vertices[i].x, y: this.vertices[i].y }, m.pos)) if (D > dist) { dist = D this.swordVertex = i } } this.laserAngle = this.swordVertex / 6 * 2 * Math.PI + 0.6283 this.sword = this.swordGrow Matter.Body.setAngularVelocity(this, 0) this.accelMag = 0.0004 + 0.00015 * simulation.accelScale; this.damageReduction = 0 this.isInvulnerable = true this.frictionAir = 1 } } me.sword = me.swordWaiting //base function that changes during different aspects of the sword swing me.swordGrow = function () { const angle = this.angle + this.laserAngle; const end = { x: this.vertices[this.swordVertex].x + this.swordRadiusMax * Math.cos(angle), y: this.vertices[this.swordVertex].y + this.swordRadiusMax * Math.sin(angle) }; const dx = end.x - this.vertices[this.swordVertex + 1 > (sides - 1) ? 0 : this.swordVertex + 1].x; const dy = end.y - this.vertices[this.swordVertex + 1 > (sides - 1) ? 0 : this.swordVertex + 1].y; const angle1 = Math.atan2(dy, dx) * (180 / Math.PI); const dx1 = end.x - this.vertices[this.swordVertex - 1 < 0 ? (sides - 1) : this.swordVertex - 1].x; const dy1 = end.y - this.vertices[this.swordVertex - 1 < 0 ? (sides - 1) : this.swordVertex - 1].y; const angle2 = Math.atan2(dy1, dx1) * (180 / Math.PI); this.laserSword(this.vertices[this.swordVertex], this.angle + this.laserAngle); this.laserSword(this.vertices[this.swordVertex + 1 > (sides - 1) ? 0 : this.swordVertex + 1], angle1 * (Math.PI / 180)) this.laserSword(this.vertices[this.swordVertex - 1 < 0 ? (sides - 1) : this.swordVertex - 1], angle2 * (Math.PI / 180)) this.swordRadius += this.swordRadiusGrowRate if (this.swordRadius > this.swordRadiusMax) { this.sword = this.swordSlash this.spinCount = 0 } ctx.beginPath(); let vertices = this.vertices; ctx.moveTo(vertices[0].x, vertices[0].y); for (let j = 1; j < vertices.length; j++) ctx.lineTo(vertices[j].x, vertices[j].y); ctx.lineTo(vertices[0].x, vertices[0].y); ctx.lineWidth = 13 + 5 * Math.random(); ctx.strokeStyle = `rgba(255,255,255,${0.5 + 0.2 * Math.random()})`; ctx.stroke(); } me.swordSlash = function () { const angle = this.angle + this.laserAngle; const end = { x: this.vertices[this.swordVertex].x + this.swordRadiusMax * Math.cos(angle), y: this.vertices[this.swordVertex].y + this.swordRadiusMax * Math.sin(angle) }; const dx = end.x - this.vertices[this.swordVertex + 1 > (sides - 1) ? 0 : this.swordVertex + 1].x; const dy = end.y - this.vertices[this.swordVertex + 1 > (sides - 1) ? 0 : this.swordVertex + 1].y; const angle1 = Math.atan2(dy, dx) * (180 / Math.PI); const dx1 = end.x - this.vertices[this.swordVertex - 1 < 0 ? (sides - 1) : this.swordVertex - 1].x; const dy1 = end.y - this.vertices[this.swordVertex - 1 < 0 ? (sides - 1) : this.swordVertex - 1].y; const angle2 = Math.atan2(dy1, dx1) * (180 / Math.PI); this.laserSword(this.vertices[this.swordVertex], this.angle + this.laserAngle); this.laserSword(this.vertices[this.swordVertex + 1 > (sides - 1) ? 0 : this.swordVertex + 1], angle1 * (Math.PI / 180)) this.laserSword(this.vertices[this.swordVertex - 1 < 0 ? (sides - 1) : this.swordVertex - 1], angle2 * (Math.PI / 180)) this.torque += this.torqueMagnitude; this.spinCount++ if (this.spinCount > 80) { this.sword = this.swordWaiting this.swordRadius = 0 this.accelMag = 0.0004 + 0.00015 * simulation.accelScale; this.cd = simulation.cycle + this.delay; this.damageReduction = this.startingDamageReduction this.isInvulnerable = false this.frictionAir = 0.01 } ctx.beginPath(); let vertices = this.vertices; ctx.moveTo(vertices[0].x, vertices[0].y); for (let j = 1; j < vertices.length; j++) ctx.lineTo(vertices[j].x, vertices[j].y); ctx.lineTo(vertices[0].x, vertices[0].y); ctx.lineWidth = 13 + 5 * Math.random(); ctx.strokeStyle = `rgba(255,255,255,${0.5 + 0.2 * Math.random()})`; ctx.stroke(); } me.laserSword = function (where, angle) { const vertexCollision = function (v1, v1End, domain) { for (let i = 0; i < domain.length; ++i) { let v = domain[i].vertices; const len = v.length - 1; for (let j = 0; j < len; j++) { results = simulation.checkLineIntersection(v1, v1End, v[j], v[j + 1]); if (results.onLine1 && results.onLine2) { const dx = v1.x - results.x; const dy = v1.y - results.y; const dist2 = dx * dx + dy * dy; if (dist2 < best.dist2 && (!domain[i].mob || domain[i].alive)) best = { x: results.x, y: results.y, dist2: dist2, who: domain[i], v1: v[j], v2: v[j + 1] }; } } results = simulation.checkLineIntersection(v1, v1End, v[0], v[len]); if (results.onLine1 && results.onLine2) { const dx = v1.x - results.x; const dy = v1.y - results.y; const dist2 = dx * dx + dy * dy; if (dist2 < best.dist2) best = { x: results.x, y: results.y, dist2: dist2, who: domain[i], v1: v[0], v2: v[len] }; } } }; best = { x: null, y: null, dist2: Infinity, who: null, v1: null, v2: null }; const look = { x: where.x + this.swordRadius * Math.cos(angle), y: where.y + this.swordRadius * Math.sin(angle) }; vertexCollision(where, look, body); // vertexCollision(where, look, mob); vertexCollision(where, look, map); if (!m.isCloak) vertexCollision(where, look, [playerBody, playerHead]); if (best.who && (best.who === playerBody || best.who === playerHead) && m.immuneCycle < m.cycle) { m.immuneCycle = m.cycle + m.collisionImmuneCycles + 60; //player is immune to damage for an extra second m.damage(this.swordDamage); simulation.drawList.push({ //add dmg to draw queue x: best.x, y: best.y, radius: this.swordDamage * 1500, color: "rgba(0,0,0,0.5)", time: 20 }); } if (best.dist2 === Infinity) best = look; ctx.beginPath(); //draw beam ctx.moveTo(where.x, where.y); ctx.lineTo(best.x, best.y); ctx.strokeStyle = "rgba(0,0,0,0.1)"; // Black path ctx.lineWidth = 25; ctx.stroke(); ctx.strokeStyle = "rgba(0,0,0,0.5)"; // Black path ctx.lineWidth = 5; ctx.setLineDash([70 + 300 * Math.random(), 55 * Math.random()]); ctx.stroke(); // Draw it ctx.setLineDash([]); } me.harmField = function (x, y) { ctx.setLineDash([125 * Math.random(), 125 * Math.random()]); // ctx.lineDashOffset = 6*(simulation.cycle % 215); if (this.distanceToPlayer3(x, y) < this.laserRange) { if (m.immuneCycle < m.cycle) { m.damage(0.0003 * simulation.dmgScale); if (m.energy > 0.1) m.energy -= 0.003 } ctx.beginPath(); ctx.moveTo(x, y); ctx.lineTo(m.pos.x, m.pos.y); ctx.lineTo(m.pos.x + (Math.random() - 0.5) * 3000, m.pos.y + (Math.random() - 0.5) * 3000); ctx.lineWidth = 2; ctx.strokeStyle = "rgb(0,0,0)"; ctx.stroke(); ctx.beginPath(); ctx.arc(m.pos.x, m.pos.y, 40, 0, 2 * Math.PI); ctx.fillStyle = "rgba(0,0,0,0.15)"; ctx.fill(); } ctx.beginPath(); ctx.arc(x, y, this.laserRange * 0.9, 0, 2 * Math.PI); ctx.strokeStyle = "rgba(0,0,0,0.5)"; ctx.lineWidth = 1; ctx.stroke(); ctx.setLineDash([]); ctx.fillStyle = "rgba(0,0,0,0.03)"; ctx.fill(); } me.distanceToPlayer3 = function (x, y) { const dx = x - player.position.x; const dy = y - player.position.y; return Math.sqrt(dx * dx + dy * dy); } radius = 22 // radius of each node mob const sideLength = 100 // distance between each node mob const nodes = 6 const angle = 2 * Math.PI / nodes spawn.allowShields = false; //don't want shields on individual mobs for (let i = 0; i < nodes; ++i) { ace.stabber(x + sideLength * Math.sin(i * angle), y + sideLength * Math.cos(i * angle), radius, 12); Matter.Body.setDensity(mob[mob.length - 1], 0.003); //extra dense //normal is 0.001 //makes effective life much larger mob[mob.length - 1].damageReduction = 0.12 mob[mob.length - 1].showHealthBar = false; mob[mob.length - 1].isBoss = true; targets.push(mob[mob.length - 1].id) //track who is in the node boss, for shields } const attachmentStiffness = 0.02 spawn.constrain2AdjacentMobs(nodes, attachmentStiffness, true); //loop mobs together for (let i = 0; i < nodes; ++i) { //attach to center mob consBB[consBB.length] = Constraint.create({ bodyA: me, bodyB: mob[mob.length - i - 1], stiffness: attachmentStiffness, damping: 0.03 }); Composite.add(engine.world, consBB[consBB.length - 1]); } //spawn shield around all nodes ace.groupShield(targets, x, y, sideLength + 1 * radius + nodes * 5 - 25); spawn.allowShields = true; }, } level.setPosToSpawn(-625, -100); //normal spawn level.exit.x = -23650; level.exit.y = 11100; simulation.fallHeight = 20000; const door = level.door(350, -200, 25, 225, 225, 10) const door2 = level.door(6325, -200, 25, 225, 225, 10); // spawn.mapRect(level.enter.x, level.enter.y + 20, 100, 20); //bump for level entrance spawn.mapRect(level.exit.x, level.exit.y + 20, 100, 20); //bump for level exit level.defaultZoom = 1800 simulation.zoomTransition(level.defaultZoom) document.body.style.backgroundColor = "#d8dadf"; ace.stabber(425, -100); ace.stabber(725, -100); ace.stabber(1000, -100); ace.stabber(1300, -100); ace.stabber(1550, -100); ace.stabber(1850, -100); ace.stabber(2125, -100); ace.stabber(2400, -100); ace.stabber(2675, -100); ace.stabber(2975, -100); ace.stabber(3225, -100); ace.stabber(3525, -100); ace.stabber(3800, -100); ace.stabber(4100, -100); ace.stabber(4375, -100); ace.stabber(4650, -100); ace.stabber(4925, -100); ace.stabber(5200, -100); ace.stabber(5500, -100); ace.stabber(5775, -100); spawn.mapRect(-200, -450, 2825, 75); spawn.mapRect(-200, 0, 2825, 75); spawn.mapRect(-300, -400, 150, 50); spawn.mapRect(-575, -375, 325, 50); spawn.mapRect(-1175, 0, 700, 75); spawn.mapRect(-1100, 50, 675, 75); spawn.mapRect(-1100, -50, 225, 75); spawn.mapRect(-1025, -75, 200, 50); spawn.mapRect(-875, -50, 150, 75); spawn.mapRect(-700, -350, 325, 50); spawn.mapRect(-950, -100, 150, 50); spawn.mapRect(-675, -50, 125, 25); spawn.mapRect(-575, -150, 25, 125); spawn.mapRect(-650, -50, 25, 75); spawn.mapRect(-600, -50, 25, 75); spawn.mapRect(-800, -325, 250, 50); spawn.mapRect(-1450, 50, 500, 75); spawn.mapRect(-1550, 100, 475, 100); spawn.mapRect(-1650, 175, 525, 375); spawn.mapRect(-1700, 275, 200, 175); spawn.mapRect(-1550, 525, 475, 100); spawn.mapRect(-1475, 600, 4125, 100); spawn.mapRect(-50, 50, 75, 75); level.chain(-450, 75, 0, false, 13) level.chain(7475, 600, -0.5498531827, false, 200) spawn.mapRect(325, -425, 75, 275); spawn.mapRect(-850, -300, 100, 50); spawn.mapRect(-900, -125, 75, 50); spawn.mapRect(-875, -275, 50, 50); spawn.mapRect(425, -400, 125, 100); spawn.mapRect(600, -400, 125, 100); spawn.mapRect(775, -400, 125, 100); spawn.mapRect(950, -400, 125, 100); spawn.mapRect(375, -350, 2250, 25); spawn.mapRect(1125, -400, 125, 100); spawn.mapRect(1300, -400, 125, 100); spawn.mapRect(1475, -400, 125, 100); spawn.mapRect(1650, -400, 125, 100); spawn.mapRect(1825, -400, 125, 100); spawn.mapRect(2000, -400, 125, 100); spawn.mapRect(2175, -400, 125, 100); spawn.mapRect(2350, -400, 125, 100); spawn.mapRect(2525, -400, 125, 100); spawn.mapRect(-1350, 650, 4000, 150); spawn.mapRect(-1400, 675, 125, 75); spawn.mapRect(-1325, 25, 200, 50); spawn.mapRect(2600, 600, 4350, 200); spawn.mapRect(2550, 0, 4400, 75); spawn.mapRect(6875, 0, 875, 75); spawn.mapRect(2500, -450, 5300, 50); spawn.mapRect(7575, -425, 575, 475); spawn.mapRect(6825, 600, 650, 125); spawn.mapRect(6875, 675, 475, 100); spawn.mapRect(7050, -525, 1175, 175); spawn.mapRect(7650, -25, 575, 150); spawn.mapRect(6075, -500, 1125, 75); spawn.mapRect(2550, -350, 3450, 25); spawn.mapRect(2700, -400, 125, 100); spawn.mapRect(2875, -400, 125, 100); spawn.mapRect(3050, -400, 125, 100); spawn.mapRect(3225, -400, 125, 100); spawn.mapRect(3400, -400, 125, 100); spawn.mapRect(3575, -400, 125, 100); spawn.mapRect(3750, -400, 125, 100); spawn.mapRect(3925, -400, 125, 100); spawn.mapRect(4100, -400, 125, 100); spawn.mapRect(4275, -400, 125, 100); spawn.mapRect(4450, -400, 125, 100); spawn.mapRect(4625, -400, 125, 100); spawn.mapRect(4800, -400, 125, 100); spawn.mapRect(4975, -400, 125, 100); spawn.mapRect(5150, -400, 125, 100); spawn.mapRect(5325, -400, 125, 100); spawn.mapRect(5500, -400, 125, 100); spawn.mapRect(5675, -400, 125, 100); spawn.mapRect(5850, -400, 125, 100); spawn.mapRect(6000, -400, 125, 100); spawn.mapRect(6100, -425, 125, 100); spawn.mapRect(6150, -450, 200, 75); spawn.mapRect(2575, -400, 3575, 25); spawn.mapRect(6300, -425, 75, 250); spawn.mapRect(-200, -175, 50, 50); spawn.bodyRect(-950, 475, 150, 125); spawn.bodyRect(-650, 475, 150, 125); spawn.bodyRect(-1000, 350, 550, 125); spawn.bodyRect(-650, 150, 225, 200); spawn.bodyRect(-1050, 150, 400, 200); spawn.bodyRect(-1125, 225, 25, 275); spawn.bodyRect(-1100, 350, 125, 200); spawn.bodyRect(-800, 475, 150, 125); spawn.bodyRect(-500, 475, 125, 125); spawn.mapRect(-3325, -50, 600, 75); spawn.mapRect(-3250, 0, 450, 75); spawn.mapRect(-2950, -100, 350, 75); spawn.mapRect(-2975, -150, 325, 75); spawn.mapRect(-3150, -250, 125, 25); spawn.mapRect(-3050, -225, 100, 25); spawn.mapRect(-3000, -200, 125, 25); spawn.mapRect(-3425, -75, 325, 50); spawn.mapRect(-3250, -225, 125, 25); spawn.mapRect(-3325, -200, 100, 25); spawn.mapRect(-3100, -300, 25, 50); spawn.mapRect(-3725, -325, 1300, 25); spawn.mapRect(-2925, -175, 125, 25); spawn.mapRect(-3375, -175, 100, 25); spawn.mapRect(-3550, -150, 250, 75); spawn.mapRect(-3725, -150, 250, 50); spawn.mapRect(-3725, -200, 125, 75); spawn.mapRect(-3625, -175, 50, 25); spawn.mapRect(-3750, -125, 50, 25); spawn.mapRect(-3750, -50, 125, 50); spawn.mapRect(-3650, -125, 75, 100); spawn.mapRect(-2750, 0, 100, 75); spawn.mapRect(-2675, 25, 175, 25); spawn.mapRect(-2850, 0, 150, 50); spawn.mapRect(-3150, 50, 25, 75); spawn.mapRect(-2900, 50, 25, 75); spawn.mapRect(-3300, 100, 575, 25); spawn.mapRect(-24300, 11125, 51525, 6250); spawn.mapVertex(7900, -675, "0 0 500 -200 500 300 -450 300") spawn.mapRect(-24300, 9575, 475, 1750); spawn.mapRect(26800, 9575, 425, 1750); spawn.hopper(-3100, -150); spawn.mapRect(11625, 8375, 11200, 225); spawn.mapRect(22600, 8375, 225, 3225); spawn.mapRect(11625, 8375, 225, 1800); spawn.mapRect(12425, 9825, 225, 875); spawn.mapRect(13150, 10725, 225, 575); spawn.mapRect(14125, 10450, 5025, 200); spawn.mapRect(21775, 10625, 1050, 225); spawn.mapRect(20325, 10925, 1300, 200); spawn.mapRect(20825, 10250, 750, 225); spawn.mapRect(19500, 10000, 1000, 225); ace.slasher2(-22725, 10325); ace.slasher3(-23425, 10250); ace.slasher2(-23350, 10700); ace.slasher3(-21725, 11075); ace.slasher2(-21525, 10025); ace.slasher3(-20950, 9750); ace.slasher2(-19975, 9700); ace.slasher3(-18850, 9650); ace.slasher2(-18675, 9700); ace.slasher3(-18250, 9125); ace.slasher2(-17775, 8925); ace.slasher3(-16975, 8875); ace.slasher2(-16475, 9125); ace.slasher3(-16125, 9275); ace.slasher2(-15650, 9225); ace.slasher3(-15200, 9175); ace.slasher2(-16800, 9325); ace.slasher3(-17450, 9525); ace.slasher2(-18375, 9625); ace.slasher3(-19650, 9375); ace.slasher2(-20600, 9225); ace.slasher3(-21625, 9400); ace.slasher2(-22450, 9775); ace.slasher3(-22900, 10000); ace.slasher2(-23275, 9300); ace.slasher3(-23125, 9150); ace.slasher2(2800, 9350); ace.slasher3(4925, 9825); ace.slasher2(3725, 10525); ace.slasher3(1850, 10450); ace.slash(16850, 10075); spawn.mapVertex(-14325, 11000, "0 0 4000 -2000 10000 -3000 16000 -2000 20000 0"); let q = Matter.Bodies.rectangle(7525 + (1100 / 2), 10675 + (150 / 2), 1100, 150, { density: 0.05, isNotHoldable: false, restitution: 1.05, isStatic: false }, true, [true], 0); let qq = Matter.Bodies.rectangle(7375 + (150 / 2), 10550 + (200 / 2), 150, 200, { density: 0.05, isNotHoldable: true, restitution: 1.05, isStatic: false }, true, [true], 0); let qqq = Matter.Bodies.rectangle(7450 + (1250 / 2), 10500 + (100 / 2), 1250, 100, { density: 0.05, isNotHoldable: true, restitution: 1.05, isStatic: false }, true, [true], 0); let qqqq = Matter.Bodies.rectangle(8625 + (150 / 2), 10550 + (200 / 2), 150, 200, { density: 0.05, isNotHoldable: true, restitution: 1.05, isStatic: false }, true, [true], 0); let qqqqq = Matter.Bodies.rectangle(7600 + (100 / 2), 10350 + (200 / 2), 100, 200, { density: 0.05, isNotHoldable: true, restitution: 1.05, isStatic: false }, true, [true], 0); let qqqqqq = Matter.Bodies.rectangle(8475 + (100 / 2), 10350 + (200 / 2), 100, 200, { density: 0.05, isNotHoldable: true, restitution: 1.05, isStatic: false }, true, [true], 0); let qqqqqqq = Matter.Bodies.rectangle(7650 + (725 / 2), 10325 + (75 / 2), 725, 75, { density: 0.05, isNotHoldable: true, restitution: 1.05, isStatic: false }, true, [true], 0); let qqqqqqqq = Matter.Bodies.rectangle(8000 + 100, 10200 + (150 / 2), 200, 150, { density: 0.05, isNotHoldable: true, restitution: 1.05, isStatic: false }, true, [true], 0); let qqqqqqqqq = Matter.Bodies.rectangle(6975 + (1125 / 2), 10250 + 25, 1125, 50, { density: 0.05, isNotHoldable: true, restitution: 1.05, isStatic: false }, true, [true], 0); let qqqqqqqqqq = Matter.Bodies.rectangle(7600 + 50, 10575 + (125 / 2), 100, 125, { density: 0.05, isNotHoldable: true, restitution: 1.05, isStatic: false }, true, [true], 0); let qqqqqqqqqqq = Matter.Bodies.rectangle(8475 + 50, 10575 + (125 / 2), 100, 125, { density: 0.05, isNotHoldable: true, restitution: 1.05, isStatic: false }, true, [true], 0); let qqqqqqqqqqqq = Matter.Bodies.rectangle(8025 + 50, 10575 + (125 / 2), 100, 125, { density: 0.05, isNotHoldable: true, restitution: 1.05, isStatic: false }, true, [true], 0); wasd = Matter.Body.create({ parts: [q, qq, qqq, qqqq, qqqqq, qqqqqq, qqqqqqq, qqqqqqqq, qqqqqqqqq, qqqqqqqqqq, qqqqqqqqqqq, qqqqqqqqqqqq] }); body[body.length] = q; body[body.length] = qq; body[body.length] = qqq; body[body.length] = qqqq; body[body.length] = qqqqq; body[body.length] = qqqqqq; body[body.length] = qqqqqqq; body[body.length] = qqqqqqqq; body[body.length] = qqqqqqqqq; body[body.length] = qqqqqqqqqq; body[body.length] = qqqqqqqqqqq; body[body.length] = qqqqqqqqqqqq; // body[body.length] = wasd; Matter.Composite.add(engine.world, wasd) composite[composite.length] = wasd; // wasd.friction -= 0.5 setTimeout(function () { wasd.collisionFilter.category = cat.map; wasd.collisionFilter.mask = cat.body | cat.player | cat.bullet | cat.mobBullet | cat.mob | cat.map }, 100); let Vx = 0; var gradient = ctx.createLinearGradient(0, 0, 10975 / 2, 0); gradient.addColorStop(0, "#00000000"); gradient.addColorStop(1, "#686868"); level.custom = () => { wasd.force.y += simulation.g * wasd.mass; if (Matter.Query.collides(wasd, [player]).length > 0 && !(m.isCloak && tech.isIntangible) && input.down && isDestroyed) { wasd.force.x += Math.cos(m.angle) * 75; Matter.Body.setVelocity(player, wasd.velocity) m.Vx = player.velocity.x - wasd.velocity.x; } for (let i = 0; i < mob.length; i++) { if (Matter.Query.collides(wasd, [mob[i]]).length > 0 && !mob[i].isBoss && isDestroyed) { const dmg = 1; mob[i].damage(dmg, true); simulation.drawList.push({ //add dmg to draw queue x: mob[i].position.x, y: mob[i].position.y, radius: Math.sqrt(dmg) * 50, color: simulation.mobDmgColor, time: simulation.drawTime }); break } } Vx = wasd.velocity.x / 5; level.exit.drawAndCheck(); drawSeats(475, -50, 5600, 125, 20, "darkgray"); door.openClose() door2.openClose() if (player.position.y < 25) { door.isClosing = false; door2.isClosing = false; } else { door.isClosing = true; door2.isClosing = true; } ctx.fillStyle = "red"; ctx.fillRect(-825, -75, 50, 50); b.pulse(30, 0, { x: -2500, y: (25 + (25 / 2)) }); ctx.save() ctx.translate(11750, 8475) ctx.fillStyle = gradient; ctx.fillRect(0, 0, 10975, 2800); ctx.restore() drawHead(7400, 0, Math.PI * 0.1); drawHead(7460, 0, Math.PI * 0.5); drawHead(7520, 0, Math.PI * 0.3); drawHead(22400, 11125, Math.PI * 0.3); drawHead(21925, 10625, Math.PI * 0.5); drawHead(21175, 10250, Math.PI * 0.1); drawHead(22525, 10625, Math.PI * 0.7); drawHead(22525, 11125, Math.PI * 0.9); drawHead(22225, 11125, Math.PI * 1.5); }; level.customTopLayer = () => { drawSeats(500, -50, 5600, 125); ctx.strokeStyle = 'red'; ctx.lineWidth = 20; ctx.beginPath(); ctx.setLineDash([40, 40]); ctx.lineDashOffset = (-simulation.cycle * Vx) % 80; ctx.moveTo(q.vertices[0].x, q.vertices[0].y); for (let i = 1; i < q.vertices.length; i++) { ctx.lineTo(q.vertices[i].x, q.vertices[i].y); } ctx.lineTo(q.vertices[0].x, q.vertices[0].y); ctx.closePath(); ctx.stroke(); ctx.setLineDash([0, 0]); }; function drawSeats(x, y, w, h, num = 20, c = "gray") { const seatWidth = w / num; const seatHeight = h / num; for (let i = 0; i < num; i++) { const seatX = x + i * seatWidth; const seatY = y; // Draw the seat parts ctx.fillStyle = c; ctx.fillRect(seatX - 100, seatY, 125, 25); ctx.fillRect(seatX, seatY - 125, 25, 150); ctx.fillRect(seatX - 75, seatY, 25, 75); ctx.fillRect(seatX - 25, seatY, 25, 75); } } function drawHead(x, y, angle) { ctx.save(); ctx.translate(x, y - 30); ctx.rotate(angle); ctx.beginPath(); ctx.arc(0, 0, 30, 0, 2 * Math.PI); ctx.fillStyle = m.bodyGradient ctx.fill(); ctx.arc(15, 0, 4, 0, 2 * Math.PI); ctx.strokeStyle = "#333"; ctx.lineWidth = 2; ctx.stroke(); ctx.restore(); } for (let i = 0, len = mob.length; i < len; i++) { if (mob[i].isSlashBoss) { simulation.ephemera.push({ name: "bossBar", do() { if (level.levels[level.onLevel] == "ace" && !isDestroyed) { ctx.save(); ctx.setTransform(1, 0, -0.5, 1, 0, 0); //slanted ctx.fillStyle = "rgba(100, 100, 100, 0.3)"; ctx.fillRect(canvas.width2 / 2, canvas.height2 / 10, canvas.width2, 30); ctx.fillStyle = "rgba(0,0,0,0.7)"; ctx.fillRect(canvas.width2 / 2, canvas.height2 / 10, canvas.width2 * mob[i].health, 30); ctx.restore(); } }, }) } } }, crimsonTowers() { simulation.makeTextLog(`crimsonTowers by Richard0820. Thank you desboot for the video: Source`) const ace = { spawnOrbitals(who, radius, chance = Math.min(0.25 + simulation.difficulty * 0.005)) { if (Math.random() < chance) { // simulation.difficulty = 50 const len = Math.floor(Math.min(15, 3 + Math.sqrt(simulation.difficulty))) // simulation.difficulty = 40 on hard mode level 10 const speed = (0.003 + 0.004 * Math.random() + 0.002 * Math.sqrt(simulation.difficulty)) * ((Math.random() < 0.5) ? 1 : -1) const offSet = 6.28 * Math.random() for (let i = 0; i < len; i++) ace.orbital(who, radius, i / len * 2 * Math.PI + offSet, speed) } }, orbital(who, radius, phase, speed) { // for (let i = 0, len = 7; i < len; i++) spawn.orbital(me, radius + 250, 2 * Math.PI / len * i) mobs.spawn(who.position.x, who.position.y, 8, 12, "rgb(0,0,0)"); let me = mob[mob.length - 1]; me.stroke = "transparent"; Matter.Body.setDensity(me, 0.01); //normal is 0.001 me.leaveBody = false; me.isDropPowerUp = false; me.isBadTarget = true; me.isUnstable = true; //dies when blocked me.showHealthBar = false; me.isOrbital = true; // me.isShielded = true me.collisionFilter.category = cat.mobBullet; me.collisionFilter.mask = cat.bullet; //cat.player | cat.map | cat.body me.do = function () { //if host is gone if (!who || !who.alive) { this.death(); return } //set orbit const time = simulation.cycle * speed + phase const orbit = { x: Math.cos(time), y: Math.sin(time) } Matter.Body.setPosition(this, Vector.add(Vector.add(who.position, who.velocity), Vector.mult(orbit, radius))) //damage player if (Matter.Query.collides(this, [player]).length > 0 && !(m.isCloak && tech.isIntangible) && m.immuneCycle < m.cycle) { m.immuneCycle = m.cycle + m.collisionImmuneCycles; //player is immune to damage for 30 cycles const dmg = 0.03 * simulation.dmgScale m.damage(dmg); simulation.drawList.push({ //add dmg to draw queue x: this.position.x, y: this.position.y, radius: Math.sqrt(dmg) * 200, color: simulation.mobDmgColor, time: simulation.drawTime }); this.death(); } }; }, shield(target, x, y, chance = Math.min(0.02 + simulation.difficulty * 0.005, 0.2) + tech.duplicationChance(), isExtraShield = false) { if (this.allowShields && Math.random() < chance) { mobs.spawn(x, y, 9, target.radius + 30, "rgba(255,255,255,0.9)"); let me = mob[mob.length - 1]; me.stroke = "rgb(0,0,0)"; Matter.Body.setDensity(me, 0.00001) //very low density to not mess with the original mob's motion me.shield = true; me.damageReduction = 0.05 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1) me.isUnblockable = true me.isExtraShield = isExtraShield //this prevents spamming with tech.isShieldAmmo me.collisionFilter.category = cat.mobShield me.collisionFilter.mask = cat.bullet; consBB[consBB.length] = Constraint.create({ bodyA: me, bodyB: target, //attach shield to target stiffness: 0.4, damping: 0.1 }); Composite.add(engine.world, consBB[consBB.length - 1]); me.onDamage = function () { //make sure the mob that owns the shield can tell when damage is done this.alertNearByMobs(); this.fill = `rgba(255,255,255,${0.3 + 0.6 * this.health})` }; me.leaveBody = false; me.isDropPowerUp = false; me.showHealthBar = false; me.shieldTargetID = target.id target.isShielded = true; target.shieldID = me.id me.onDeath = function () { //clear isShielded status from target for (let i = 0, len = mob.length; i < len; i++) { if (mob[i].id === this.shieldTargetID) mob[i].isShielded = false; } }; me.do = function () { this.checkStatus(); }; mob.unshift(me); //move shield to the front of the array, so that mob is behind shield graphically //swap order of shield and mob, so that mob is behind shield graphically // mob[mob.length - 1] = mob[mob.length - 2]; // mob[mob.length - 2] = me; } }, groupShield(targets, x, y, radius, stiffness = 0.4) { const nodes = targets.length mobs.spawn(x, y, 9, radius, "rgba(255,255,255,0.9)"); let me = mob[mob.length - 1]; me.stroke = "rgb(0,0,0)"; Matter.Body.setDensity(me, 0.00001) //very low density to not mess with the original mob's motion me.frictionAir = 0; me.shield = true; me.damageReduction = 0.075 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1) me.collisionFilter.category = cat.mobShield me.collisionFilter.mask = cat.bullet; for (let i = 0; i < nodes; ++i) { mob[mob.length - i - 2].isShielded = true; //constrain to all mob nodes in group consBB[consBB.length] = Constraint.create({ bodyA: me, bodyB: mob[mob.length - i - 2], stiffness: stiffness, damping: 0.1 }); Composite.add(engine.world, consBB[consBB.length - 1]); } me.onDamage = function () { this.alertNearByMobs(); //makes sure the mob that owns the shield can tell when damage is done this.fill = `rgba(255,255,255,${0.3 + 0.6 * this.health})` }; me.onDeath = function () { //clear isShielded status from target for (let j = 0; j < targets.length; j++) { for (let i = 0, len = mob.length; i < len; i++) { if (mob[i].id === targets[j]) mob[i].isShielded = false; } } }; me.leaveBody = false; me.isDropPowerUp = false; me.showHealthBar = false; mob[mob.length - 1] = mob[mob.length - 1 - nodes]; mob[mob.length - 1 - nodes] = me; me.do = function () { this.checkStatus(); }; }, slasher2(x, y, radius = 33 + Math.ceil(Math.random() * 30)) { mobs.spawn(x, y, 6, radius, "rgb(0,0,0)"); let me = mob[mob.length - 1]; Matter.Body.rotate(me, 2 * Math.PI * Math.random()); me.accelMag = 0.0009 * simulation.accelScale; me.torqueMagnitude = 0.000012 * me.inertia //* (Math.random() > 0.5 ? -1 : 1); me.frictionStatic = 0; me.friction = 0; me.frictionAir = 0.035; me.delay = 140 * simulation.CDScale; me.cd = 0; me.swordRadius = 0; me.swordVertex = 1 me.swordRadiusMax = 275 + 3.5 * simulation.difficulty; me.swordRadiusGrowRate = me.swordRadiusMax * (0.011 + 0.0002 * simulation.difficulty) me.isSlashing = false; me.swordDamage = 0.03 * simulation.dmgScale me.laserAngle = 3 * Math.PI / 5 const seeDistance2 = 200000 ace.shield(me, x, y); me.onDamage = function () { }; me.do = function () { this.checkStatus(); this.seePlayerByHistory(15); this.attraction(); this.sword() //does various things depending on what stage of the sword swing }; me.swordWaiting = function () { if ( this.seePlayer.recall && this.cd < simulation.cycle && this.distanceToPlayer2() < seeDistance2 && Matter.Query.ray(map, this.position, this.playerPosRandomY()).length === 0 && Matter.Query.ray(body, this.position, this.playerPosRandomY()).length === 0 ) { this.laserAngle = -Math.PI / 6 this.sword = this.swordGrow this.accelMag = 0 } } me.sword = me.swordWaiting //base function that changes during different aspects of the sword swing me.swordGrow = function () { this.laserSword(this.vertices[0], this.angle + this.laserAngle); this.laserSword(this.vertices[1], this.angle + this.laserAngle + (Math.PI / 3)); this.laserSword(this.vertices[2], this.angle + this.laserAngle + (Math.PI * 2 / 3)); this.laserSword(this.vertices[3], this.angle + this.laserAngle + Math.PI); this.laserSword(this.vertices[4], this.angle + this.laserAngle + (Math.PI * 4 / 3)); this.laserSword(this.vertices[5], this.angle + this.laserAngle + (Math.PI * 5 / 3)); this.swordRadius += this.swordRadiusGrowRate if (this.swordRadius > this.swordRadiusMax || this.isStunned) { this.sword = this.swordSlash this.spinCount = 0 } } me.swordSlash = function () { this.laserSword(this.vertices[0], this.angle + this.laserAngle); this.laserSword(this.vertices[1], this.angle + this.laserAngle + (Math.PI / 3)); this.laserSword(this.vertices[2], this.angle + this.laserAngle + (Math.PI * 2 / 3)); this.laserSword(this.vertices[3], this.angle + this.laserAngle + Math.PI); this.laserSword(this.vertices[4], this.angle + this.laserAngle + (Math.PI * 4 / 3)); this.laserSword(this.vertices[5], this.angle + this.laserAngle + (Math.PI * 5 / 3)); this.torque += this.torqueMagnitude; this.spinCount++ if (this.spinCount > 100 || this.isStunned) { this.sword = this.swordWaiting this.swordRadius = 0 this.accelMag = 0.001 * simulation.accelScale; this.cd = simulation.cycle + this.delay; } } me.laserSword = function (where, angle) { const vertexCollision = function (v1, v1End, domain) { for (let i = 0; i < domain.length; ++i) { let v = domain[i].vertices; const len = v.length - 1; for (let j = 0; j < len; j++) { results = simulation.checkLineIntersection(v1, v1End, v[j], v[j + 1]); if (results.onLine1 && results.onLine2) { const dx = v1.x - results.x; const dy = v1.y - results.y; const dist2 = dx * dx + dy * dy; if (dist2 < best.dist2 && (!domain[i].mob || domain[i].alive)) best = { x: results.x, y: results.y, dist2: dist2, who: domain[i], v1: v[j], v2: v[j + 1] }; } } results = simulation.checkLineIntersection(v1, v1End, v[0], v[len]); if (results.onLine1 && results.onLine2) { const dx = v1.x - results.x; const dy = v1.y - results.y; const dist2 = dx * dx + dy * dy; if (dist2 < best.dist2) best = { x: results.x, y: results.y, dist2: dist2, who: domain[i], v1: v[0], v2: v[len] }; } } }; best = { x: null, y: null, dist2: Infinity, who: null, v1: null, v2: null }; const look = { x: where.x + this.swordRadius * Math.cos(angle), y: where.y + this.swordRadius * Math.sin(angle) }; vertexCollision(where, look, body); // vertexCollision(where, look, mob); vertexCollision(where, look, map); if (!m.isCloak) vertexCollision(where, look, [playerBody, playerHead]); if (best.who && (best.who === playerBody || best.who === playerHead) && m.immuneCycle < m.cycle) { m.immuneCycle = m.cycle + m.collisionImmuneCycles + 60; //player is immune to damage for an extra second m.damage(this.swordDamage); simulation.drawList.push({ //add dmg to draw queue x: best.x, y: best.y, radius: this.swordDamage * 1500, color: "rgba(80,0,255,0.5)", time: 20 }); } if (best.dist2 === Infinity) best = look; ctx.beginPath(); //draw beam ctx.moveTo(where.x, where.y); ctx.lineTo(best.x, best.y); ctx.strokeStyle = "rgba(0,0,0,0.1)"; // 0 path ctx.lineWidth = 15; ctx.stroke(); ctx.strokeStyle = "rgba(0,0,0,0.5)"; // 0 path ctx.lineWidth = 4; ctx.setLineDash([70 + 300 * Math.random(), 55 * Math.random()]); ctx.stroke(); // Draw it ctx.setLineDash([]); } }, slasher3(x, y, radius = 33 + Math.ceil(Math.random() * 30)) { const sides = 6 mobs.spawn(x, y, sides, radius, "rgb(0,0,0)"); let me = mob[mob.length - 1]; Matter.Body.rotate(me, 2 * Math.PI * Math.random()); me.accelMag = 0.0005 * simulation.accelScale; me.frictionStatic = 0; me.friction = 0; me.frictionAir = 0.02; me.delay = 150 * simulation.CDScale; me.cd = 0; me.cycle = 0; me.swordVertex = 1 me.swordRadiusInitial = radius / 2; me.swordRadius = me.swordRadiusInitial; me.swordRadiusMax = 750 + 6 * simulation.difficulty; me.swordRadiusGrowRateInitial = 1.08 me.swordRadiusGrowRate = me.swordRadiusGrowRateInitial//me.swordRadiusMax * (0.009 + 0.0002 * simulation.difficulty) me.isSlashing = false; me.swordDamage = 0.04 * simulation.dmgScale me.laserAngle = 3 * Math.PI / 5 const seeDistance2 = me.swordRadiusMax * me.swordRadiusMax ace.shield(me, x, y); me.onDamage = function () { }; me.do = function () { this.checkStatus(); this.seePlayerByHistory(15); this.sword() //does various things depending on what stage of the sword swing }; me.swordWaiting = function () { this.attraction(); if ( this.seePlayer.recall && this.cd < simulation.cycle && this.distanceToPlayer2() < seeDistance2 && Matter.Query.ray(map, this.position, this.playerPosRandomY()).length === 0 && Matter.Query.ray(body, this.position, this.playerPosRandomY()).length === 0 ) { //find vertex closest to the player let dist = Infinity for (let i = 0, len = this.vertices.length; i < len; i++) { const D = Vector.magnitudeSquared(Vector.sub({ x: this.vertices[i].x, y: this.vertices[i].y }, m.pos)) if (D < dist) { dist = D this.swordVertex = i } } this.laserAngle = this.swordVertex / sides * 2 * Math.PI + Math.PI / sides this.sword = this.swordGrow this.cycle = 0 this.swordRadius = this.swordRadiusInitial //slow velocity but don't stop Matter.Body.setVelocity(this, Vector.mult(this.velocity, 0.5)) //set angular velocity to 50% // Matter.Body.setAngularVelocity(this, this.angularVelocity * 0.5) //gently rotate towards the player with a torque, use cross product to decided clockwise or counterclockwise const laserStartVector = Vector.sub(this.position, this.vertices[this.swordVertex]) const playerVector = Vector.sub(this.position, m.pos) const cross = Matter.Vector.cross(laserStartVector, playerVector) this.torque = 0.00002 * this.inertia * (cross > 0 ? 1 : -1) } } me.sword = me.swordWaiting //base function that changes during different aspects of the sword swing me.swordGrow = function () { const angle = this.angle + this.laserAngle; const end = { x: this.vertices[this.swordVertex].x + this.swordRadiusMax * Math.cos(angle), y: this.vertices[this.swordVertex].y + this.swordRadiusMax * Math.sin(angle) }; const dx = end.x - this.vertices[this.swordVertex + 1 > (sides - 1) ? 0 : this.swordVertex + 1].x; const dy = end.y - this.vertices[this.swordVertex + 1 > (sides - 1) ? 0 : this.swordVertex + 1].y; const angle1 = Math.atan2(dy, dx) * (180 / Math.PI); const dx1 = end.x - this.vertices[this.swordVertex - 1 < 0 ? (sides - 1) : this.swordVertex - 1].x; const dy1 = end.y - this.vertices[this.swordVertex - 1 < 0 ? (sides - 1) : this.swordVertex - 1].y; const angle2 = Math.atan2(dy1, dx1) * (180 / Math.PI); this.laserSpear(this.vertices[this.swordVertex], this.angle + this.laserAngle); this.laserSpear(this.vertices[this.swordVertex + 1 > (sides - 1) ? 0 : this.swordVertex + 1], angle1 * (Math.PI / 180)) this.laserSpear(this.vertices[this.swordVertex - 1 < 0 ? (sides - 1) : this.swordVertex - 1], angle2 * (Math.PI / 180)) Matter.Body.setVelocity(this, Vector.mult(this.velocity, 0.9)) // this.swordRadius += this.swordRadiusGrowRate this.cycle++ // this.swordRadius = this.swordRadiusMax * Math.sin(this.cycle * 0.03) this.swordRadius *= this.swordRadiusGrowRate if (this.swordRadius > this.swordRadiusMax) this.swordRadiusGrowRate = 1 / this.swordRadiusGrowRateInitial // if (this.swordRadius > this.swordRadiusMax) this.swordRadiusGrowRate = -Math.abs(this.swordRadiusGrowRate) if (this.swordRadius < this.swordRadiusInitial || this.isStunned) { // this.swordRadiusGrowRate = Math.abs(this.swordRadiusGrowRate) this.swordRadiusGrowRate = this.swordRadiusGrowRateInitial this.sword = this.swordWaiting this.swordRadius = 0 this.cd = simulation.cycle + this.delay; } } me.laserSpear = function (where, angle) { const vertexCollision = function (v1, v1End, domain) { for (let i = 0; i < domain.length; ++i) { let v = domain[i].vertices; const len = v.length - 1; for (let j = 0; j < len; j++) { results = simulation.checkLineIntersection(v1, v1End, v[j], v[j + 1]); if (results.onLine1 && results.onLine2) { const dx = v1.x - results.x; const dy = v1.y - results.y; const dist2 = dx * dx + dy * dy; if (dist2 < best.dist2 && (!domain[i].mob || domain[i].alive)) best = { x: results.x, y: results.y, dist2: dist2, who: domain[i], v1: v[j], v2: v[j + 1] }; } } results = simulation.checkLineIntersection(v1, v1End, v[0], v[len]); if (results.onLine1 && results.onLine2) { const dx = v1.x - results.x; const dy = v1.y - results.y; const dist2 = dx * dx + dy * dy; if (dist2 < best.dist2) best = { x: results.x, y: results.y, dist2: dist2, who: domain[i], v1: v[0], v2: v[len] }; } } }; best = { x: null, y: null, dist2: Infinity, who: null, v1: null, v2: null }; const look = { x: where.x + this.swordRadius * Math.cos(angle), y: where.y + this.swordRadius * Math.sin(angle) }; vertexCollision(where, look, body); // vertexCollision(where, look, mob); vertexCollision(where, look, map); if (!m.isCloak) vertexCollision(where, look, [playerBody, playerHead]); if (best.who && (best.who === playerBody || best.who === playerHead)) { this.swordRadiusGrowRate = 1 / this.swordRadiusGrowRateInitial //!!!! this retracts the sword if it hits the player if (m.immuneCycle < m.cycle) { m.immuneCycle = m.cycle + m.collisionImmuneCycles + 60; //player is immune to damage for an extra second m.damage(this.swordDamage); simulation.drawList.push({ //add dmg to draw queue x: best.x, y: best.y, radius: this.swordDamage * 1500, color: "rgba(80,0,255,0.5)", time: 20 }); } } if (best.dist2 === Infinity) best = look; ctx.beginPath(); //draw beam ctx.moveTo(where.x, where.y); ctx.lineTo(best.x, best.y); ctx.strokeStyle = "rgba(0,0,0,0.1)"; // 0 path ctx.lineWidth = 15; ctx.stroke(); ctx.strokeStyle = "rgba(0,0,0,0.5)"; // 0 path ctx.lineWidth = 4; ctx.setLineDash([70 + 300 * Math.random(), 55 * Math.random()]); ctx.stroke(); // Draw it ctx.setLineDash([]); } }, stabber(x, y, radius = 25 + Math.ceil(Math.random() * 12), spikeMax = 7) { if (radius > 80) radius = 65; mobs.spawn(x, y, 6, radius, "rgb(0,0,0)"); //can't have sides above 6 or collision events don't work (probably because of a convex problem) let me = mob[mob.length - 1]; me.isVerticesChange = true me.accelMag = 0.0006 * simulation.accelScale; // me.g = 0.0002; //required if using this.gravity me.isInvulnerable = false me.delay = 360 * simulation.CDScale; me.spikeVertex = 0; me.spikeLength = 0; me.isSpikeGrowing = false; me.spikeGrowth = 0; me.isSpikeReset = true; me.collisionFilter.mask = cat.map | cat.body | cat.bullet | cat.player //can't touch other mobs Matter.Body.rotate(me, Math.PI * 0.1); ace.shield(me, x, y); // me.onDamage = function () {}; // me.onHit = function() { //run this function on hitting player // }; me.onDeath = function () { if (this.spikeLength > 4) { this.spikeLength = 4 const spike = Vector.mult(Vector.normalise(Vector.sub(this.vertices[this.spikeVertex], this.position)), this.radius * this.spikeLength) this.vertices[this.spikeVertex].x = this.position.x + spike.x this.vertices[this.spikeVertex].y = this.position.y + spike.y // this.vertices = Matter.Vertices.hull(Matter.Vertices.clockwiseSort(this.vertices)) } }; me.do = function () { this.seePlayerByLookingAt(); this.checkStatus(); this.attraction(); if (this.isSpikeReset) { if (this.seePlayer.recall) { const dist = Vector.sub(this.seePlayer.position, this.position); const distMag = Vector.magnitude(dist); if (distMag < radius * spikeMax) { //find nearest vertex let nearestDistance = Infinity for (let i = 0, len = this.vertices.length; i < len; i++) { //find distance to player for each vertex const dist = Vector.sub(this.seePlayer.position, this.vertices[i]); const distMag = Vector.magnitude(dist); //save the closest distance if (distMag < nearestDistance) { this.spikeVertex = i nearestDistance = distMag } } this.spikeLength = 1 this.isSpikeGrowing = true; this.isSpikeReset = false; Matter.Body.setAngularVelocity(this, 0) } me.isInvulnerable = false } } else { if (this.isSpikeGrowing) { this.spikeLength += Math.pow(this.spikeGrowth += 0.02, 8) // if (this.spikeLength < 2) { // this.spikeLength += 0.035 // } else { // this.spikeLength += 1 // } if (this.spikeLength > spikeMax) { this.isSpikeGrowing = false; this.spikeGrowth = 0 } } else { Matter.Body.setAngularVelocity(this, this.angularVelocity * 0.8) //reduce rotation this.spikeLength -= 0.3 if (this.spikeLength < 1) { this.spikeLength = 1 this.isSpikeReset = true this.radius = radius } } const spike = Vector.mult(Vector.normalise(Vector.sub(this.vertices[this.spikeVertex], this.position)), radius * this.spikeLength) this.vertices[this.spikeVertex].x = this.position.x + spike.x this.vertices[this.spikeVertex].y = this.position.y + spike.y me.isInvulnerable = true // this.radius = radius * this.spikeLength; } if (this.isInvulnerable) { ctx.beginPath(); let vertices = this.vertices; ctx.moveTo(vertices[0].x, vertices[0].y); for (let j = 1; j < vertices.length; j++) ctx.lineTo(vertices[j].x, vertices[j].y); ctx.lineTo(vertices[0].x, vertices[0].y); ctx.lineWidth = 13 + 5 * Math.random(); ctx.strokeStyle = `rgba(255,255,255,${0.5 + 0.2 * Math.random()})`; ctx.stroke(); me.damageReduction = 0; } else { me.damageReduction = 1; } }; }, slash(x, y, radius = 80) { let targets = [] const sides = 6; mobs.spawn(x, y, 6, radius, "#000000"); let me = mob[mob.length - 1]; Matter.Body.rotate(me, 2 * Math.PI * Math.random()); targets.push(me.id) //add to shield protection const nodeBalance = Math.random() const nodes2 = Math.min(15, Math.floor(2 + 4 * nodeBalance + 0.75 * Math.sqrt(simulation.difficulty))) me.isBoss = true; me.isSlashBoss = true; me.showHealthBar = false; me.damageReduction = 0.1 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1) me.startingDamageReduction = me.damageReduction me.isInvulnerable = false me.frictionAir = 0.02 me.seeAtDistance2 = 1000000; me.accelMag = 0.0004 + 0.00015 * simulation.accelScale; Matter.Body.setDensity(me, 0.0005); //normal is 0.001 me.collisionFilter.mask = cat.bullet | cat.player | cat.body | cat.map me.memory = Infinity; me.seePlayerFreq = 20 me.lockedOn = null; me.laserRange = 500; me.torqueMagnitude = 0.00024 * me.inertia * (Math.random() > 0.5 ? -1 : 1); me.delay = 70 + 70 * simulation.CDScale; me.cd = 0; me.swordRadius = 0; me.swordVertex = 1 me.swordRadiusMax = 1100 + 20 * simulation.difficulty; me.swordRadiusGrowRate = me.swordRadiusMax * (0.005 + 0.0003 * simulation.difficulty) me.isSlashing = false; me.swordDamage = 0.07 * simulation.dmgScale me.laserAngle = 3 * Math.PI / 5 me.eventHorizon = 550; const seeDistance2 = 200000 ace.shield(me, x, y); const rangeInnerVsOuter = Math.random() let speed = (0.006 + 0.001 * Math.sqrt(simulation.difficulty)) * ((Math.random() < 0.5) ? 1 : -1) let range = radius + 350 + 200 * rangeInnerVsOuter + nodes2 * 7 for (let i = 0; i < nodes2; i++) ace.orbital(me, range, i / nodes2 * 2 * Math.PI, speed) const orbitalIndexes = [] //find indexes for all the current nodes2 for (let i = 0; i < nodes2; i++) orbitalIndexes.push(mob.length - 1 - i) // add orbitals for each orbital range = Math.max(60, 100 + 100 * Math.random() - nodes2 * 3 - rangeInnerVsOuter * 80) speed = speed * (1.25 + 2 * Math.random()) const subNodes = Math.max(2, Math.floor(6 - 5 * nodeBalance + 0.5 * Math.sqrt(simulation.difficulty))) for (let j = 0; j < nodes2; j++) { for (let i = 0, len = subNodes; i < len; i++) ace.orbital(mob[orbitalIndexes[j]], range, i / len * 2 * Math.PI, speed) } for (let i = 0, len = 3 + 0.5 * Math.sqrt(simulation.difficulty); i < len; i++) ace.spawnOrbitals(me, radius + 40 + 10 * i, 1); const springStiffness = 0.00014; const springDampening = 0.0005; me.springTarget = { x: me.position.x, y: me.position.y }; const len = cons.length; cons[len] = Constraint.create({ pointA: me.springTarget, bodyB: me, stiffness: springStiffness, damping: springDampening }); Composite.add(engine.world, cons[cons.length - 1]); cons[len].length = 100 + 1.5 * radius; me.cons = cons[len]; me.springTarget2 = { x: me.position.x, y: me.position.y }; const len2 = cons.length; cons[len2] = Constraint.create({ pointA: me.springTarget2, bodyB: me, stiffness: springStiffness, damping: springDampening, length: 0 }); Composite.add(engine.world, cons[cons.length - 1]); cons[len2].length = 100 + 1.5 * radius; me.cons2 = cons[len2]; me.onDamage = function () { }; me.onDeath = function () { isDestroyed = true; this.removeCons(); powerUps.spawnBossPowerUp(this.position.x, this.position.y); }; me.do = function () { for (let i = 0; i < this.vertices.length; i++) { this.harmField(this.vertices[i].x, this.vertices[i].y); } this.seePlayerByHistory(40); this.springAttack(); this.checkStatus(); this.sword() //does various things depending on what stage of the sword swing const eventHorizon = this.eventHorizon * (1 + 0.2 * Math.sin(simulation.cycle * 0.008)) me.laserRange = eventHorizon; }; me.swordWaiting = function () { if ( this.seePlayer.recall && this.cd < simulation.cycle && this.distanceToPlayer2() < seeDistance2 && !m.isCloak && Matter.Query.ray(map, this.position, this.playerPosRandomY()).length === 0 && Matter.Query.ray(body, this.position, this.playerPosRandomY()).length === 0 ) { //find vertex farthest away from player let dist = 0 for (let i = 0, len = this.vertices.length; i < len; i++) { const D = Vector.magnitudeSquared(Vector.sub({ x: this.vertices[i].x, y: this.vertices[i].y }, m.pos)) if (D > dist) { dist = D this.swordVertex = i } } this.laserAngle = this.swordVertex / 6 * 2 * Math.PI + 0.6283 this.sword = this.swordGrow Matter.Body.setAngularVelocity(this, 0) this.accelMag = 0.0004 + 0.00015 * simulation.accelScale; this.damageReduction = 0 this.isInvulnerable = true this.frictionAir = 1 } } me.sword = me.swordWaiting //base function that changes during different aspects of the sword swing me.swordGrow = function () { const angle = this.angle + this.laserAngle; const end = { x: this.vertices[this.swordVertex].x + this.swordRadiusMax * Math.cos(angle), y: this.vertices[this.swordVertex].y + this.swordRadiusMax * Math.sin(angle) }; const dx = end.x - this.vertices[this.swordVertex + 1 > (sides - 1) ? 0 : this.swordVertex + 1].x; const dy = end.y - this.vertices[this.swordVertex + 1 > (sides - 1) ? 0 : this.swordVertex + 1].y; const angle1 = Math.atan2(dy, dx) * (180 / Math.PI); const dx1 = end.x - this.vertices[this.swordVertex - 1 < 0 ? (sides - 1) : this.swordVertex - 1].x; const dy1 = end.y - this.vertices[this.swordVertex - 1 < 0 ? (sides - 1) : this.swordVertex - 1].y; const angle2 = Math.atan2(dy1, dx1) * (180 / Math.PI); this.laserSword(this.vertices[this.swordVertex], this.angle + this.laserAngle); this.laserSword(this.vertices[this.swordVertex + 1 > (sides - 1) ? 0 : this.swordVertex + 1], angle1 * (Math.PI / 180)) this.laserSword(this.vertices[this.swordVertex - 1 < 0 ? (sides - 1) : this.swordVertex - 1], angle2 * (Math.PI / 180)) this.swordRadius += this.swordRadiusGrowRate if (this.swordRadius > this.swordRadiusMax) { this.sword = this.swordSlash this.spinCount = 0 } ctx.beginPath(); let vertices = this.vertices; ctx.moveTo(vertices[0].x, vertices[0].y); for (let j = 1; j < vertices.length; j++) ctx.lineTo(vertices[j].x, vertices[j].y); ctx.lineTo(vertices[0].x, vertices[0].y); ctx.lineWidth = 13 + 5 * Math.random(); ctx.strokeStyle = `rgba(255,255,255,${0.5 + 0.2 * Math.random()})`; ctx.stroke(); } me.swordSlash = function () { const angle = this.angle + this.laserAngle; const end = { x: this.vertices[this.swordVertex].x + this.swordRadiusMax * Math.cos(angle), y: this.vertices[this.swordVertex].y + this.swordRadiusMax * Math.sin(angle) }; const dx = end.x - this.vertices[this.swordVertex + 1 > (sides - 1) ? 0 : this.swordVertex + 1].x; const dy = end.y - this.vertices[this.swordVertex + 1 > (sides - 1) ? 0 : this.swordVertex + 1].y; const angle1 = Math.atan2(dy, dx) * (180 / Math.PI); const dx1 = end.x - this.vertices[this.swordVertex - 1 < 0 ? (sides - 1) : this.swordVertex - 1].x; const dy1 = end.y - this.vertices[this.swordVertex - 1 < 0 ? (sides - 1) : this.swordVertex - 1].y; const angle2 = Math.atan2(dy1, dx1) * (180 / Math.PI); this.laserSword(this.vertices[this.swordVertex], this.angle + this.laserAngle); this.laserSword(this.vertices[this.swordVertex + 1 > (sides - 1) ? 0 : this.swordVertex + 1], angle1 * (Math.PI / 180)) this.laserSword(this.vertices[this.swordVertex - 1 < 0 ? (sides - 1) : this.swordVertex - 1], angle2 * (Math.PI / 180)) this.torque += this.torqueMagnitude; this.spinCount++ if (this.spinCount > 80) { this.sword = this.swordWaiting this.swordRadius = 0 this.accelMag = 0.0004 + 0.00015 * simulation.accelScale; this.cd = simulation.cycle + this.delay; this.damageReduction = this.startingDamageReduction this.isInvulnerable = false this.frictionAir = 0.01 } ctx.beginPath(); let vertices = this.vertices; ctx.moveTo(vertices[0].x, vertices[0].y); for (let j = 1; j < vertices.length; j++) ctx.lineTo(vertices[j].x, vertices[j].y); ctx.lineTo(vertices[0].x, vertices[0].y); ctx.lineWidth = 13 + 5 * Math.random(); ctx.strokeStyle = `rgba(255,255,255,${0.5 + 0.2 * Math.random()})`; ctx.stroke(); } me.laserSword = function (where, angle) { const vertexCollision = function (v1, v1End, domain) { for (let i = 0; i < domain.length; ++i) { let v = domain[i].vertices; const len = v.length - 1; for (let j = 0; j < len; j++) { results = simulation.checkLineIntersection(v1, v1End, v[j], v[j + 1]); if (results.onLine1 && results.onLine2) { const dx = v1.x - results.x; const dy = v1.y - results.y; const dist2 = dx * dx + dy * dy; if (dist2 < best.dist2 && (!domain[i].mob || domain[i].alive)) best = { x: results.x, y: results.y, dist2: dist2, who: domain[i], v1: v[j], v2: v[j + 1] }; } } results = simulation.checkLineIntersection(v1, v1End, v[0], v[len]); if (results.onLine1 && results.onLine2) { const dx = v1.x - results.x; const dy = v1.y - results.y; const dist2 = dx * dx + dy * dy; if (dist2 < best.dist2) best = { x: results.x, y: results.y, dist2: dist2, who: domain[i], v1: v[0], v2: v[len] }; } } }; best = { x: null, y: null, dist2: Infinity, who: null, v1: null, v2: null }; const look = { x: where.x + this.swordRadius * Math.cos(angle), y: where.y + this.swordRadius * Math.sin(angle) }; vertexCollision(where, look, body); // vertexCollision(where, look, mob); vertexCollision(where, look, map); if (!m.isCloak) vertexCollision(where, look, [playerBody, playerHead]); if (best.who && (best.who === playerBody || best.who === playerHead) && m.immuneCycle < m.cycle) { m.immuneCycle = m.cycle + m.collisionImmuneCycles + 60; //player is immune to damage for an extra second m.damage(this.swordDamage); simulation.drawList.push({ //add dmg to draw queue x: best.x, y: best.y, radius: this.swordDamage * 1500, color: "rgba(0,0,0,0.5)", time: 20 }); } if (best.dist2 === Infinity) best = look; ctx.beginPath(); //draw beam ctx.moveTo(where.x, where.y); ctx.lineTo(best.x, best.y); ctx.strokeStyle = "rgba(0,0,0,0.1)"; // Black path ctx.lineWidth = 25; ctx.stroke(); ctx.strokeStyle = "rgba(0,0,0,0.5)"; // Black path ctx.lineWidth = 5; ctx.setLineDash([70 + 300 * Math.random(), 55 * Math.random()]); ctx.stroke(); // Draw it ctx.setLineDash([]); } me.harmField = function (x, y) { ctx.setLineDash([125 * Math.random(), 125 * Math.random()]); // ctx.lineDashOffset = 6*(simulation.cycle % 215); if (this.distanceToPlayer3(x, y) < this.laserRange) { if (m.immuneCycle < m.cycle) { m.damage(0.0003 * simulation.dmgScale); if (m.energy > 0.1) m.energy -= 0.003 } ctx.beginPath(); ctx.moveTo(x, y); ctx.lineTo(m.pos.x, m.pos.y); ctx.lineTo(m.pos.x + (Math.random() - 0.5) * 3000, m.pos.y + (Math.random() - 0.5) * 3000); ctx.lineWidth = 2; ctx.strokeStyle = "rgb(0,0,0)"; ctx.stroke(); ctx.beginPath(); ctx.arc(m.pos.x, m.pos.y, 40, 0, 2 * Math.PI); ctx.fillStyle = "rgba(0,0,0,0.15)"; ctx.fill(); } ctx.beginPath(); ctx.arc(x, y, this.laserRange * 0.9, 0, 2 * Math.PI); ctx.strokeStyle = "rgba(0,0,0,0.5)"; ctx.lineWidth = 1; ctx.stroke(); ctx.setLineDash([]); ctx.fillStyle = "rgba(0,0,0,0.03)"; ctx.fill(); } me.distanceToPlayer3 = function (x, y) { const dx = x - player.position.x; const dy = y - player.position.y; return Math.sqrt(dx * dx + dy * dy); } radius = 22 // radius of each node mob const sideLength = 100 // distance between each node mob const nodes = 6 const angle = 2 * Math.PI / nodes spawn.allowShields = false; //don't want shields on individual mobs for (let i = 0; i < nodes; ++i) { ace.stabber(x + sideLength * Math.sin(i * angle), y + sideLength * Math.cos(i * angle), radius, 12); Matter.Body.setDensity(mob[mob.length - 1], 0.003); //extra dense //normal is 0.001 //makes effective life much larger mob[mob.length - 1].damageReduction = 0.12 mob[mob.length - 1].showHealthBar = false; mob[mob.length - 1].isBoss = true; targets.push(mob[mob.length - 1].id) //track who is in the node boss, for shields } const attachmentStiffness = 0.02 spawn.constrain2AdjacentMobs(nodes, attachmentStiffness, true); //loop mobs together for (let i = 0; i < nodes; ++i) { //attach to center mob consBB[consBB.length] = Constraint.create({ bodyA: me, bodyB: mob[mob.length - i - 1], stiffness: attachmentStiffness, damping: 0.03 }); Composite.add(engine.world, consBB[consBB.length - 1]); } //spawn shield around all nodes ace.groupShield(targets, x, y, sideLength + 1 * radius + nodes * 5 - 25); spawn.allowShields = true; }, } level.setPosToSpawn(0, -50); color.map = "crimson"; spawn.mapRect(level.enter.x, level.enter.y + 20, 100, 20); spawn.mapRect(0, 0, 1, 1); level.defaultZoom = 1800; simulation.zoomTransition(level.defaultZoom); document.body.style.backgroundColor = "#d8dadf"; const isSus = Math.random() < 0.001; //A very lucky person gets rickrolled const mediaSource = isSus ? "https://ia801509.us.archive.org/10/items/Rick_Astley_Never_Gonna_Give_You_Up/Rick_Astley_Never_Gonna_Give_You_Up.ogv" : "https://cdn.glitch.me/b559a783-c0cb-4369-92e3-0c0a5556ba01/n-gon%20evangelion%20-%20Made%20with%20Clipchamp%20(8).mp4?v=1692134040246" let videoContainer; let video = document.createElement("video"); video.src = mediaSource; video.autoPlay = true; video.loop = true; video.muted = true; videoContainer = { video: video, ready: true, }; video.play(); const boost1 = level.boost(8835, -3675, 7500); const boost2 = level.boost(-8935, -3675, 7500); ace.slash(0, -15000 + 1800); function Raindrop(minX, minY, maxX, maxY) { this.x = minX + Math.random() * (maxX - minX); this.y = minY + Math.random() * (maxY - minY); this.speed = Math.random() * 5 + 25; this.length = Math.random() * 20 + 30; } function forceField(x, y, width, height) { return { min: { x: x, y: y }, max: { x: x + width, y: y + height }, width: width, height: height, maxHeight: height, raindrops: [], drawRaindrop(drop) { if (Math.sqrt(Math.pow(player.position.x - drop.x, 2) + Math.pow(player.position.y - drop.y, 2)) + Math.PI < 5000) { ctx.beginPath(); ctx.moveTo(drop.x, drop.y); ctx.lineTo(drop.x, drop.y + drop.length); ctx.strokeStyle = '#00FFFF'; ctx.lineWidth = 10; ctx.lineCap = 'butt'; ctx.stroke(); } }, updateRaindrop(drop) { drop.y += drop.speed; if ((Matter.Query.ray(map, { x: drop.x, y: drop.y }, { x: drop.x, y: drop.y - drop.length }).length === 0) == false) { simulation.drawList.push({ x: drop.x, y: drop.y - drop.length, radius: 10, color: "rgb(0,100,250,0.3)", time: 8 }); do { drop.y = this.min.y + this.height * Math.random(); drop.x = this.min.x + this.width * Math.random(); } while (drop.x > this.min.x && drop.x < this.max.x && drop.y > this.min.y && drop.y < this.max.y) } }, isOn: true, query() { if (this.isOn) { ctx.fillStyle = `rgba(200, 20, 10, 0.55)` ctx.fillRect(this.min.x, this.min.y, this.width, this.height) if (this.height > 0 && Matter.Query.region([player], this).length) { player.force.y -= 0.015; m.energy = m.maxEnergy; } // if(this.raindrops.length < 300) { // too many (like 900) can cause a little bit of lag minus 5 ~ 10 fps, but it really just depends on your computer // this.raindrops.push(new Raindrop()); // } // for (let i = 0; i < this.raindrops.length; i++) { // const drop = this.raindrops[i]; // this.drawRaindrop(drop); // this.updateRaindrop(drop); // } } }, } } const forceField1 = forceField(-750, -30000, 1500, 20000); level.custom = () => { if (player.position.y < -20000) { level.nextLevel(); } forceField1.query(); boost1.query(); boost2.query(); level.exit.drawAndCheck(); level.enter.draw(); ctx.beginPath(); ctx.strokeStyle = "rgba(220, 20, 10, 0.55)"; ctx.lineWidth = 1500; ctx.lineJoin = "miter" ctx.miterLimit = 100; ctx.moveTo(map[272].vertices[0].x, map[272].vertices[0].y); for (let i = 0; i < map[272].vertices.length; i++) { ctx.lineTo(map[272].vertices[i].x, map[272].vertices[i].y); } ctx.closePath(); ctx.stroke(); }; let checkVid = () => { if (simulation.paused && !videoContainer.paused) { videoContainer.paused = true; video.pause(); } else if (!simulation.paused && videoContainer.paused) { videoContainer.paused = false; video.play(); } requestAnimationFrame(checkVid); } checkVid(); simulation.ephemera.push({ name: "vid", do() { if (level.levels[level.onLevel] !== "crimsonTowers") simulation.removeEphemera(this.name); if (mediaSource && !isSus) { ctx.drawImage(videoContainer.video, -1600, -15000, 3200, 1800); } else if (mediaSource) { ctx.drawImage(videoContainer.video, -1920 / 2, -15000, 1920, 1080); } } }); level.customTopLayer = () => { ctx.fillStyle = "rgba(220, 20, 10, 0.1)"; ctx.fillRect(-6725, -3500, 475, 2925); ctx.fillRect(-8725, -3700, 450, 2925); ctx.fillRect(-4725, -3300, 450, 2925); ctx.fillRect(-2725, -3100, 450, 2925); ctx.fillRect(-725, -2900, 450, 2925); ctx.fillRect(275, -2900, 450, 2925); ctx.fillRect(2275, -3100, 450, 2925); ctx.fillRect(4275, -3300, 450, 2925); ctx.fillRect(6275, -3500, 450, 2925); ctx.fillRect(8275, -3700, 450, 2925); }; spawn.mapRect(-10000, 0, 20000, 2000); spawn.mapRect(-9050, -3650, 350, 50); spawn.mapRect(8700, -3650, 350, 50); spawn.mapRect(-275, -2825, 550, 50); spawn.mapRect(-225, -500, 450, 50); spawn.mapRect(-250, -1575, 500, 50); function spawnTower(index, y = 0) { const x = index - 1325; spawn.mapRect(x + 1025, y + -950, 125, 750); spawn.mapRect(x + 1125, y + -225, 50, 50); spawn.mapRect(x + 1500, y + -950, 125, 750); spawn.mapRect(x + 1475, y + -225, 50, 50); spawn.mapRect(x + 1600, y + -225, 50, 50); spawn.mapRect(x + 1000, y + -225, 50, 50); spawn.mapRect(x + 1475, y + -475, 50, 50); spawn.mapRect(x + 1125, y + -750, 50, 50); spawn.mapRect(x + 1050, y + -2025, 100, 1125); spawn.mapRect(x + 1500, y + -2025, 100, 1125); spawn.mapRect(x + 1475, y + -1050, 50, 50); spawn.mapRect(x + 1125, y + -1325, 50, 50); spawn.mapRect(x + 1475, y + -1550, 50, 50); spawn.mapRect(x + 1125, y + -1875, 50, 50); spawn.mapRect(x + 1075, y + -2900, 75, 925); spawn.mapRect(x + 1500, y + -2900, 75, 925); spawn.mapRect(x + 1475, y + -2150, 50, 50); spawn.mapRect(x + 1125, y + -2475, 50, 50); spawn.mapRect(x + 1475, y + -2800, 50, 50); spawn.mapRect(x + 1000, y + -975, 50, 50); spawn.mapRect(x + 1025, y + -2050, 50, 50); spawn.mapRect(x + 1050, y + -2925, 50, 50); spawn.mapRect(x + 1550, y + -2925, 50, 50); spawn.mapRect(x + 1600, y + -975, 50, 50); spawn.mapRect(x + 1575, y + -2050, 50, 50); for (let i = 0; i < 5; i++) { if (Math.random() > 0.5) { ace.slasher2(index, y - (i * 500) - 500) } else { ace.slasher3(index, y - (i * 500) - 500) }; } } // ace.slash(0, -5000); function spawnChain(x, y, x1, y1, length = 39) { const angle = Math.atan2(y1 - y, x1 - x); chain(x, y, angle, true, length); } function chain(x, y, angle = 0, isAttached = true, len = 15, radius = 20, stiffness = 1, damping = 1) { const gap = 2 * radius const unit = { x: Math.cos(angle), y: Math.sin(angle) } for (let i = 0; i < len; i++) { bullet[bullet.length] = Bodies.polygon(x + gap * unit.x * i, y + gap * unit.y * i, 12, radius, { inertia: Infinity, isNotHoldable: true }); const who = bullet[bullet.length - 1]; who.do = () => { }; who.collisionFilter.category = cat.body; who.collisionFilter.mask = cat.player | cat.bullet | cat.body | cat.bullet | cat.bullet | cat.bulletBullet Composite.add(engine.world, who); //add to world who.classType = "bullet" } for (let i = 1; i < len; i++) { consBB[consBB.length] = Constraint.create({ bodyA: bullet[bullet.length - i], bodyB: bullet[bullet.length - i - 1], stiffness: stiffness, damping: damping }); Composite.add(engine.world, consBB[consBB.length - 1]); } cons[cons.length] = Constraint.create({ pointA: { x: x, y: y }, bodyB: bullet[bullet.length - len], stiffness: 1, damping: damping }); Composite.add(engine.world, cons[cons.length - 1]); if (isAttached) { cons[cons.length] = Constraint.create({ pointA: { x: x + gap * unit.x * (len - 1), y: y + gap * unit.y * (len - 1) }, bodyB: bullet[bullet.length - 1], stiffness: 1, damping: damping }); Composite.add(engine.world, cons[cons.length - 1]); } } spawnChain(-2250, -3100, -750, -2900); spawnChain(-4250, -3300, -2750, -3100); spawnChain(-6250, -3500, -4750, -3300); spawnChain(-8250, -3700, -6750, -3500); spawnChain(750, -2900, 2250, -3100); spawnChain(2750, -3100, 4250, -3300); spawnChain(4750, -3300, 6250, -3500); spawnChain(6750, -3500, 8250, -3700); // spawnChain(-3000, -30000, -9500, -20400, 291); // spawnChain(3000, -30000, 9500, -20400, 291); spawnTower(500); spawnTower(2500, -200); spawn.mapRect(2000, -200, 7000, 300); spawnTower(4500, -400); spawn.mapRect(4000, -400, 5000, 300); spawnTower(6500, -600); spawn.mapRect(6000, -600, 5000, 300); spawnTower(8500, -800); spawn.mapRect(8000, -800, 3000, 300); spawnTower(-500); spawnTower(-2500, -200); spawn.mapRect(-10000, -200, 8000, 300); spawnTower(-4500, -400); spawn.mapRect(-10000, -400, 6000, 300); spawnTower(-6500, -600); spawn.mapRect(-10000, -600, 4000, 300); spawnTower(-8500, -800); spawn.mapRect(-10000, -800, 2000, 300); spawn.mapVertex(10000, -9450, "-1000 0 1000 0 1000 -10000 500 -20000 -500 -20000 -1000 -10000"); spawn.mapVertex(-10000, -9450, "-1000 0 1000 0 1000 -10000 500 -20000 -500 -20000 -1000 -10000"); spawn.mapRect(-11000, -675, 2000, 2675); spawn.mapRect(9000, -675, 2000, 2675); spawn.mapVertex(0, -30000, "0 0 3000 -10000 6000 0 3000 10000"); spawn.mapRect(-8750, -10000, 8000, 100); spawn.mapRect(750, -10000, 8000, 100); spawn.mapVertex(0, -10020, "-1000 0 -5000 300 5000 300 1000 0"); spawn.mapRect(-800, -10250, 100, 350); spawn.mapRect(700, -10250, 100, 350); const a = 200; const maxTheta = 10 * Math.PI; const spiralX = (theta) => a * theta * Math.cos(theta); const spiralY = (theta) => a * theta * Math.sin(theta); for (let i = 1; i <= maxTheta; i += 0.2) { const x = spiralX(i); const y = spiralY(i) + (-15000 + 1800 / 2); spawn.mapRect(x, y, 100, 100); } level.exit.y = map[272].position.y; level.exit.x = map[272].position.x; }, // ******************************************************************************************************** // ******************************************************************************************************** // ***************************************** training levels ********************************************** // ******************************************************************************************************** // ******************************************************************************************************** walk() { //learn to walk if (localSettings.isHideHUD) localSettings.isHideHUD = false m.addHealth(Infinity) document.getElementById("health").style.display = "none" //hide your health bar document.getElementById("health-bg").style.display = "none" document.getElementById("defense-bar").style.display = "none" document.getElementById("damage-bar").style.display = "none" level.setPosToSpawn(60, -50); //normal spawn spawn.mapRect(10, -10, 100, 20); //small platform for player level.exit.x = 1775; level.exit.y = -35; spawn.mapRect(level.exit.x, level.exit.y + 25, 100, 100); //exit bump simulation.zoomScale = 1400 //1400 is normal level.defaultZoom = 1400 simulation.zoomTransition(level.defaultZoom, 1) document.body.style.backgroundColor = level.trainingBackgroundColor simulation.lastLogTime = 0; //clear previous messages let instruction = 0 level.trainingText(`move with ${input.key.left.replace('Key', '').replace('Digit', '')} and ${input.key.right.replace('Key', '').replace('Digit', '')}`) level.custom = () => { if (instruction === 0 && input.right) { instruction++ level.trainingText(`move with ${input.key.left.replace('Key', '').replace('Digit', '')} and ${input.key.right.replace('Key', '').replace('Digit', '')}
exit through the blue door`) } //exit room ctx.fillStyle = "#f2f2f2" ctx.fillRect(1600, -400, 400, 400) level.enter.draw(); level.exit.drawAndCheck(); }; level.customTopLayer = () => { //exit room glow ctx.fillStyle = "rgba(0,255,255,0.05)" ctx.fillRect(1600, -400, 400, 400) }; spawn.mapRect(-2750, -2800, 2600, 4600); //left wall spawn.mapRect(2000, -2800, 2600, 4600); //right wall spawn.mapRect(-250, 0, 3500, 1800); //floor spawn.mapRect(1575, 0, 500, 100); spawn.mapRect(-250, -2800, 3500, 2200); //roof spawn.mapRect(700, -8, 50, 25); spawn.mapRect(725, -16, 75, 25); spawn.mapRect(1375, -16, 50, 50); spawn.mapRect(1400, -8, 50, 25); spawn.mapRect(750, -24, 650, 100); spawn.mapRect(1600, -1200, 500, 850); //exit roof spawn.mapRect(1600, -400, 50, 225); //exit room left upper wall }, crouch() { //learn to crouch if (localSettings.isTrainingNotAttempted) { //after making it to the second training level localSettings.isTrainingNotAttempted = false // this makes the training button less obvious at the start screen if (localSettings.isAllowed) localStorage.setItem("localSettings", JSON.stringify(localSettings)); //update local storage } m.addHealth(Infinity) level.setPosToSpawn(75, -100); //normal spawn spawn.mapRect(25, -60, 100, 20); //small platform for player spawn.mapRect(0, -50, 150, 25); //stairs spawn.mapRect(-25, -40, 200, 25); spawn.mapRect(-50, -30, 250, 25); spawn.mapRect(-75, -20, 300, 25); spawn.mapRect(-100, -10, 350, 25); spawn.mapRect(-150, -50, 175, 75); level.exit.x = 1775; level.exit.y = -35; spawn.mapRect(level.exit.x, level.exit.y + 25, 100, 100); //exit bump simulation.zoomScale = 1400 //1400 is normal level.defaultZoom = 1400 simulation.zoomTransition(level.defaultZoom, 1) document.body.style.backgroundColor = level.trainingBackgroundColor let instruction = 0 level.trainingText(`press ${input.key.down.replace('Key', '').replace('Digit', '')} to crouch`) level.custom = () => { if (instruction === 0 && input.down) { instruction++ level.trainingText(`press ${input.key.down.replace('Key', '').replace('Digit', '')} to crouch`) } //exit room ctx.fillStyle = "#f2f2f2" ctx.fillRect(1625, -350, 375, 350) level.enter.draw(); level.exit.drawAndCheck(); }; level.customTopLayer = () => { //exit room glow ctx.fillStyle = "rgba(0,255,255,0.05)" ctx.fillRect(1625, -350, 375, 350) //dark ctx.fillStyle = "rgba(0,0,0,0.2)" ctx.fillRect(500, -100, 1125, 175); }; // spawn.mapRect(1025, -675, 300, 623); //crouch wall // spawn.mapRect(625, -650, 1025, 550); spawn.mapRect(500, -650, 1125, 550); spawn.mapRect(-200, -650, 875, 300); spawn.mapRect(-2750, -2800, 2600, 4600); //left wall spawn.mapRect(2000, -2800, 2600, 4600); //right wall spawn.mapRect(-250, 50, 3500, 1750); //floor spawn.mapRect(-200, 0, 950, 100); spawn.mapRect(1575, 0, 500, 100); spawn.mapRect(-250, -2800, 3500, 2200); //roof spawn.mapRect(725, 12, 50, 25); spawn.mapRect(725, 25, 75, 25); spawn.mapRect(750, 38, 75, 25); spawn.mapRect(1525, 25, 75, 50); spawn.mapRect(1500, 38, 50, 25); spawn.mapRect(1550, 12, 50, 25); spawn.mapRect(1600, -1200, 500, 850); //exit roof }, jump() { //learn to jump m.addHealth(Infinity) level.setPosToSpawn(60, -50); //normal spawn spawn.mapRect(10, -10, 100, 20); //small platform for player level.exit.x = 1775; level.exit.y = -35; spawn.mapRect(level.exit.x, level.exit.y + 25, 100, 100); //exit bump simulation.zoomScale = 1400 //1400 is normal level.defaultZoom = 1400 simulation.zoomTransition(level.defaultZoom, 1) document.body.style.backgroundColor = level.trainingBackgroundColor let instruction = 0 level.trainingText(`hold down ${input.key.up.replace('Key', '').replace('Digit', '')} longer to jump higher`) level.custom = () => { if (instruction === 0 && m.pos.x > 300) { instruction++ level.trainingText(`hold down ${input.key.up.replace('Key', '').replace('Digit', '')} longer to jump higher`) } m.health = 1 //can't die //exit room ctx.fillStyle = "#f2f2f2" ctx.fillRect(1600, -400, 400, 400) level.enter.draw(); level.exit.drawAndCheck(); }; level.customTopLayer = () => { //dark ctx.fillStyle = "rgba(0,0,0,0.2)" ctx.fillRect(1000, 0, 450, 1800) //exit room glow ctx.fillStyle = "rgba(0,255,255,0.05)" ctx.fillRect(1600, -400, 400, 400) }; spawn.mapRect(-2750, -2800, 2600, 4600); //left wall spawn.mapRect(2000, -2800, 2600, 4600); //right wall spawn.mapRect(275, -350, 200, 375); spawn.mapRect(-250, 0, 1250, 1800); //floor spawn.mapRect(1450, 0, 1075, 1800); //floor spawn.mapRect(-250, -2800, 1250, 2200); //roof spawn.mapRect(1450, -2800, 1075, 2200); //roof spawn.mapVertex(375, 0, "150 0 -150 0 -100 -50 100 -50"); //base spawn.mapRect(1600, -1200, 500, 850); //exit roof spawn.mapRect(1600, -400, 50, 225); //exit room left upper wall //roof steps spawn.mapRect(1000, -650, 25, 25); spawn.mapRect(1000, -675, 50, 25); spawn.mapRect(1000, -700, 75, 25); spawn.mapRect(1000, -725, 100, 25); spawn.mapRect(1425, -650, 25, 25); spawn.mapRect(1400, -675, 50, 25); spawn.mapRect(1375, -700, 75, 25); spawn.mapRect(1350, -725, 100, 25); spawn.mapRect(1325, -750, 150, 25); spawn.mapRect(1300, -775, 150, 25); spawn.mapRect(1000, -750, 125, 25); spawn.mapRect(1275, -2800, 200, 2025); spawn.mapRect(975, -2800, 200, 2025); spawn.mapRect(1000, -775, 150, 25); }, hold() { //put block on button to open door m.addHealth(Infinity) level.setPosToSpawn(60, -50); //normal spawn spawn.mapRect(10, -10, 100, 20); //small platform for player level.exit.x = 1775; level.exit.y = -35; spawn.mapRect(level.exit.x, level.exit.y + 25, 100, 100); //exit bump simulation.zoomScale = 1400 //1400 is normal level.defaultZoom = 1400 simulation.zoomTransition(level.defaultZoom, 1) document.body.style.backgroundColor = level.trainingBackgroundColor spawn.bodyRect(1025, -75, 50, 50); //block to go on button const buttonDoor = level.button(500, 0) const door = level.door(1612.5, -175, 25, 190, 185, 3) let instruction = 0 level.trainingText(`activate your field with ${input.key.field.replace('Key', '').replace('Digit', '')} or right mouse`) level.custom = () => { if (instruction === 0 && input.field) { instruction++ level.trainingText(`activate your field with ${input.key.field.replace('Key', '').replace('Digit', '')} or right mouse
release your field on a block to pick it up`) } else if (instruction === 1 && m.isHolding) { instruction++ level.trainingText(`activate your field with ${input.key.field.replace('Key', '').replace('Digit', '')} or right mouse
release your field on a block to pick it up

drop the block on the red button to open the door`) } else if (instruction === 2 && !buttonDoor.isUp && Vector.magnitudeSquared(Vector.sub(body[0].position, buttonDoor.min)) < 10000) { instruction++ level.trainingText(`activate your field with ${input.key.field.replace('Key', '').replace('Digit', '')} or right mouse
release your field on a block to pick it up
drop the block on the red button to open the door
`) } //exit room ctx.fillStyle = "#f2f2f2" ctx.fillRect(1600, -400, 400, 400) level.enter.draw(); level.exit.drawAndCheck(); }; level.customTopLayer = () => { buttonDoor.query(); buttonDoor.draw(); if (buttonDoor.isUp) { door.isClosing = true } else { door.isClosing = false } door.openClose(); door.draw(); //exit room glow ctx.fillStyle = "rgba(0,255,255,0.05)" ctx.fillRect(1600, -400, 400, 400) }; spawn.mapRect(-2750, -2800, 2600, 4600); //left wall spawn.mapRect(2000, -2800, 2600, 4600); //right wall spawn.mapRect(-250, 50, 3500, 1750); //floor spawn.mapRect(-200, 0, 950, 100); spawn.mapRect(1575, 0, 500, 100); spawn.mapRect(-250, -2800, 3500, 2200); //roof spawn.mapRect(725, 12, 50, 25); spawn.mapRect(725, 25, 75, 25); spawn.mapRect(750, 38, 75, 25); spawn.mapRect(1525, 25, 75, 50); spawn.mapRect(1500, 38, 50, 25); spawn.mapRect(1550, 12, 50, 25); spawn.mapRect(1600, -1200, 500, 850); //exit roof spawn.mapRect(1600, -400, 50, 225); //exit room left upper wall }, throw() { //throw a block on button to open door m.addHealth(Infinity) level.setPosToSpawn(60, -50); //normal spawn spawn.mapRect(10, -10, 100, 20); //small platform for player level.exit.x = 1775; level.exit.y = -35; spawn.mapRect(level.exit.x, level.exit.y + 25, 100, 100); //exit bump simulation.zoomScale = 1400 //1400 is normal level.defaultZoom = 1400 simulation.zoomTransition(level.defaultZoom, 1) document.body.style.backgroundColor = level.trainingBackgroundColor spawn.bodyRect(1025, -75, 50, 50); //block to go on button const buttonDoor = level.button(1635, -400) const door = level.door(1612.5, -175, 25, 190, 185, 3) // activate your field with ${input.key.field.replace('Key', '').replace('Digit', '')} or right mouse let instruction = 0 level.trainingText(`pick up the block with your field`) level.custom = () => { if (instruction === 0 && m.isHolding) { instruction++ level.trainingText(`pick up the block with your field
hold your field down to charge up then release to throw a block`) } else if (instruction === 1 && m.throwCharge > 2) { instruction++ level.trainingText(`pick up the block with your field
hold your field down to charge up then release to throw a block

throw the block onto the button`) // the block at the button } else if (instruction === 2 && !buttonDoor.isUp && Vector.magnitudeSquared(Vector.sub(body[0].position, buttonDoor.min)) < 10000) { instruction++ level.trainingText(`pick up the block with your field
hold your field down to charge up then release to throw a block
throw the block onto the button
`) } //exit room ctx.fillStyle = "#f2f2f2" ctx.fillRect(1600, -400, 400, 400) level.enter.draw(); level.exit.drawAndCheck(); }; level.customTopLayer = () => { buttonDoor.query(); buttonDoor.draw(); if (buttonDoor.isUp) { door.isClosing = true } else { door.isClosing = false } door.openClose(); door.draw(); //exit room glow ctx.fillStyle = "rgba(0,255,255,0.05)" ctx.fillRect(1600, -400, 400, 400) }; spawn.mapRect(-2750, -2800, 2600, 4600); //left wall spawn.mapRect(2000, -2800, 2600, 4600); //right wall spawn.mapRect(-250, 50, 3500, 1750); //floor spawn.mapRect(-200, 0, 950, 100); spawn.mapRect(1575, 0, 500, 100); spawn.mapRect(-250, -2800, 3500, 2200); //roof spawn.mapRect(725, 12, 50, 25); spawn.mapRect(725, 25, 75, 25); spawn.mapRect(750, 38, 75, 25); spawn.mapRect(1525, 25, 75, 50); spawn.mapRect(1500, 38, 50, 25); spawn.mapRect(1550, 12, 50, 25); // spawn.mapRect(1600, -1200, 500, 850); //exit roof spawn.mapRect(1790, -600, 250, 225); //button left wall spawn.mapRect(1625, -400, 400, 50); spawn.mapRect(1600, -400, 50, 225); //exit room left upper wall }, throwAt() { //throw a block at mob to open door m.addHealth(Infinity) level.setPosToSpawn(60, -50); //normal spawn spawn.mapRect(10, -10, 100, 20); //small platform for player level.exit.x = 1775; level.exit.y = -35; spawn.mapRect(level.exit.x, level.exit.y + 25, 100, 100); //exit bump simulation.zoomScale = 1400 //1400 is normal level.defaultZoom = 1400 simulation.zoomTransition(level.defaultZoom, 1) document.body.style.backgroundColor = level.trainingBackgroundColor const door = level.door(1612.5, -175, 25, 190, 185, 3) let instruction = 0 level.trainingText(`throw the block at the mobs to open the door`) level.custom = () => { if (instruction === 0 && !mob.length) { instruction++ level.trainingText(`throw the block at the mobs to open the door`) } //exit room ctx.fillStyle = "#f2f2f2" ctx.fillRect(1600, -400, 400, 400) level.enter.draw(); level.exit.drawAndCheck(); }; level.customTopLayer = () => { if (mob.length > 0) { door.isClosing = true } else { door.isClosing = false } door.openClose(); door.draw(); //exit room glow ctx.fillStyle = "rgba(0,255,255,0.05)" ctx.fillRect(1600, -400, 400, 400) }; spawn.mapRect(-2750, -2800, 2600, 4600); //left wall spawn.mapRect(2000, -2800, 2600, 4600); //right wall spawn.mapRect(-250, 50, 3500, 1750); //floor spawn.mapRect(-200, 0, 950, 100); spawn.mapRect(1575, 0, 500, 100); spawn.mapRect(-250, -2800, 3500, 2200); //roof spawn.mapRect(725, 12, 50, 25); spawn.mapRect(725, 25, 75, 25); spawn.mapRect(750, 38, 75, 25); spawn.mapRect(1525, 25, 75, 50); spawn.mapRect(1500, 38, 50, 25); spawn.mapRect(1550, 12, 50, 25); // spawn.mapRect(1600, -1200, 500, 850); //exit roof // spawn.mapRect(1790, -600, 250, 225); //button left wall // spawn.mapRect(1625, -400, 400, 50); spawn.mapRect(1600, -400, 50, 225); //exit room left upper wall spawn.mapRect(1600, -600, 425, 250); spawn.bodyRect(1025, -75, 50, 50); //block to go on button spawn.starter(425, -350, 35) spawn.starter(800, -350, 44) }, fire() { //throw a block at mob to open door level.setPosToSpawn(60, -50); //normal spawn spawn.mapRect(10, -10, 100, 20); //small platform for player level.exit.x = 1775; level.exit.y = 15; spawn.mapRect(level.exit.x, level.exit.y + 25, 100, 100); //exit bump simulation.zoomScale = 1400 //1400 is normal level.defaultZoom = 1400 simulation.zoomTransition(level.defaultZoom, 1) document.body.style.backgroundColor = level.trainingBackgroundColor const door = level.door(1612.5, -125, 25, 190, 185, 3) const buttonDoor = level.button(400, 0) let instruction = 0 level.trainingText(`use your field to pick up the gun power up`) level.custom = () => { if (instruction === 0 && simulation.isChoosing) { instruction++ level.trainingText(`use your field to pick up the gun power up
choose a gun`) } else if (instruction === 1 && !simulation.isChoosing) { instruction++ level.trainingText(`use your field to pick up the gun power up
choose a gun

use the left mouse button to shoot the mobs`) } else if (instruction === 2 && mob.length === 0) { instruction++ level.trainingText(`use your field to pick up the gun power up
choose a gun
use the left mouse button to shoot the mobs

drop a block on the red button to open the door`) } else if (instruction === 3 && !door.isClosing) { instruction++ level.trainingText(`use your field to pick up the gun power up
choose a gun
use the left mouse button to shoot the mobs
put a block on the red button to open the door
`) } if (!powerUp.length) { //spawn ammo if you run out if (b.inventory.length && b.guns[b.activeGun].ammo === 0) powerUps.directSpawn(1300, -2000, "ammo", false); //spawn a gun power up if don't have one or a gun if (!b.inventory.length && !simulation.isChoosing) powerUps.directSpawn(1300, -2000, "gun", false); } //exit room ctx.fillStyle = "#f2f2f2" ctx.fillRect(1600, -350, 400, 400) level.enter.draw(); level.exit.drawAndCheck(); }; level.customTopLayer = () => { buttonDoor.query(); buttonDoor.draw(); if (buttonDoor.isUp) { door.isClosing = true } else { door.isClosing = false } door.openClose(); door.draw(); //exit room glow ctx.fillStyle = "rgba(0,255,255,0.05)" ctx.fillRect(1600, -350, 400, 400) //ammo tunnel shadow ctx.fillStyle = "rgba(0,0,0,0.4)" ctx.fillRect(1250, -2800, 100, 2200) }; spawn.mapRect(-2750, -2800, 2600, 4600); //left wall spawn.mapRect(2000, -2800, 2600, 4600); //right wall spawn.mapRect(-250, 50, 3500, 1750); //floor spawn.mapRect(-200, 0, 950, 100); spawn.mapRect(-150, -2800, 1400, 2200); //roof with tunnel for ammo spawn.mapRect(1350, -2800, 675, 2200); //ceiling steps spawn.mapRect(725, -588, 50, 25); spawn.mapRect(725, -600, 75, 25); spawn.mapRect(750, -612, 75, 25); spawn.mapRect(-275, -650, 1025, 87); spawn.mapRect(725, 12, 50, 25); spawn.mapRect(725, 25, 75, 25); spawn.mapRect(750, 38, 75, 25); spawn.mapRect(1600, -600, 425, 300); spawn.mapRect(1600, -400, 50, 275); powerUps.directSpawn(1300, -1500, "gun", false); spawn.starter(900, -300, 35) spawn.starter(1400, -400, 44) }, deflect() { //learn to jump m.addHealth(Infinity) level.setPosToSpawn(60, -50); //normal spawn spawn.mapRect(10, -10, 100, 20); //small platform for player level.exit.x = 1775; level.exit.y = -35; spawn.mapRect(level.exit.x, level.exit.y + 25, 100, 100); //exit bump simulation.zoomScale = 1400 //1400 is normal level.defaultZoom = 1400 simulation.zoomTransition(level.defaultZoom, 1) document.body.style.backgroundColor = level.trainingBackgroundColor let instruction = 0 // activate your field with ${input.key.field.replace('Key', '').replace('Digit', '')} or right mouse level.trainingText(`use your field to deflect the mobs`) level.custom = () => { if (instruction === 0 && m.pos.x > 1350) { instruction++ level.trainingText(`use your field to deflect the mobs`) } //teleport to start if hit if (m.immuneCycle > m.cycle) { m.energy = m.maxEnergy Matter.Body.setPosition(player, { x: 60, y: -50 }) } //spawn bullets if (!(simulation.cycle % 5)) { spawn.sniperBullet(660 + 580 * Math.random(), -2000, 10, 4); const who = mob[mob.length - 1] Matter.Body.setVelocity(who, { x: 0, y: 8 }); who.timeLeft = 300 } //exit room ctx.fillStyle = "#f2f2f2" ctx.fillRect(1600, -400, 400, 400) level.enter.draw(); level.exit.drawAndCheck(); }; level.customTopLayer = () => { //dark ctx.fillStyle = "rgba(0,0,0,0.05)" //exit room glow ctx.fillStyle = "rgba(0,255,255,0.05)" ctx.fillRect(1600, -400, 400, 400) //center falling bullets ctx.fillStyle = "rgba(255,0,255,0.013)" //pink? ctx.fillRect(650, -2800, 600, 2800) }; spawn.mapRect(-2750, -2800, 2600, 4600); //left wall spawn.mapRect(2000, -2800, 2600, 4600); //right wall spawn.mapRect(-250, 0, 3000, 1800); //floor spawn.mapRect(-250, -2800, 900, 2200); //roof spawn.mapRect(1250, -2800, 1275, 2200); //roof spawn.mapVertex(950, 0, "400 0 -400 0 -300 -50 300 -50"); //base spawn.mapRect(1600, -1200, 500, 850); //exit roof spawn.mapRect(1600, -400, 50, 225); //exit room left upper wall //spawn bullets on load to avoid rush for (let i = 0; i < 32; i++) { spawn.sniperBullet(660 + 580 * Math.random(), -2000 + 40 * i, 10, 4); const who = mob[mob.length - 1] Matter.Body.setVelocity(who, { x: 0, y: 8 }); who.timeLeft = 300 } }, heal() { //learn to heal m.addHealth(Infinity) m.health = 0; m.addHealth(0.25) document.getElementById("health").style.display = "inline" //show your health bar document.getElementById("health-bg").style.display = "inline" if (!localSettings.isHideHUD) { document.getElementById("defense-bar").style.display = "inline" document.getElementById("damage-bar").style.display = "inline" } level.setPosToSpawn(60, -50); //normal spawn spawn.mapRect(10, -10, 100, 20); //small platform for player level.exit.x = 1775; level.exit.y = -35; spawn.mapRect(level.exit.x, level.exit.y + 25, 100, 100); //exit bump simulation.zoomScale = 1400 //1400 is normal level.defaultZoom = 1400 simulation.zoomTransition(level.defaultZoom, 1) document.body.style.backgroundColor = level.trainingBackgroundColor let instruction = 0 level.trainingText(`your health is displayed in the top left corner
use your field to pick up
until your health is full`) level.custom = () => { if (instruction === 0 && m.health === 1) { instruction++ level.trainingText(`use your field to pick up
until your health is full
`) } //exit room ctx.fillStyle = "#f2f2f2" ctx.fillRect(1600, -400, 400, 400) level.enter.draw(); level.exit.drawAndCheck(); }; level.customTopLayer = () => { if (m.health !== 1) { door.isClosing = true } else { door.isClosing = false } door.openClose(); door.draw(); //exit room glow ctx.fillStyle = "rgba(0,255,255,0.05)" ctx.fillRect(1600, -400, 400, 400) }; spawn.mapRect(-2750, -2800, 2600, 4600); //left wall spawn.mapRect(2000, -2800, 2600, 4600); //right wall spawn.mapRect(-250, 0, 3500, 1800); //floor spawn.mapRect(1575, 0, 500, 100); spawn.mapRect(-250, -2800, 3500, 2200); //roof spawn.mapRect(700, -8, 50, 25); spawn.mapRect(725, -16, 75, 25); spawn.mapRect(1375, -16, 50, 50); spawn.mapRect(1400, -8, 50, 25); spawn.mapRect(750, -24, 650, 100); powerUps.directSpawn(875, -40, "heal", false, null, 15); powerUps.directSpawn(1075, -50, "heal", false, null, 25); powerUps.directSpawn(1275, -65, "heal", false, null, 35); const door = level.door(1612.5, -175, 25, 190, 185, 3) spawn.mapRect(1600, -1200, 500, 850); //exit roof spawn.mapRect(1600, -400, 50, 225); //exit room left upper wall }, nailGun() { level.difficultyIncrease(1) //difficulty on training mode resets to zero with each new level level.setPosToSpawn(60, -50); //normal spawn spawn.mapRect(10, -10, 100, 20); //small platform for player level.exit.x = 1775; level.exit.y = -35; spawn.mapRect(level.exit.x, level.exit.y + 25, 100, 100); //exit bump simulation.zoomScale = 1400 //1400 is normal level.defaultZoom = 1400 simulation.zoomTransition(level.defaultZoom, 1) document.body.style.backgroundColor = level.trainingBackgroundColor b.removeAllGuns(); b.giveGuns("nail gun") b.guns[b.activeGun].ammo = 0 simulation.updateGunHUD(); const door = level.door(1612.5, -175, 25, 190, 185, 3) let instruction = 0 level.trainingText(`use your field to pick up
for your nail gun`) level.custom = () => { if (instruction === 0 && b.inventory.length && b.guns[b.activeGun].ammo > 0) { instruction++ level.trainingText(`use your field to pick up
for your nail gun

use the left mouse button to shoot the mobs`) } else if (instruction === 1 && mob.length === 0) { instruction++ level.trainingText(`use your field to pick up
for your nail gun
use the left mouse button to shoot the mobs
`) } //spawn ammo if you run out let isAmmo = false for (let i = 0; i < powerUp.length; i++) { if (powerUp[i].name === 'ammo') isAmmo = true } if (!isAmmo && b.inventory.length && b.guns[b.activeGun].ammo === 0) { powerUps.directSpawn(1300, -2000, "ammo", false); powerUps.directSpawn(1301, -2200, "ammo", false); } //exit room ctx.fillStyle = "#f2f2f2" ctx.fillRect(1600, -400, 400, 400) level.enter.draw(); level.exit.drawAndCheck(); }; level.customTopLayer = () => { if (mob.length > 0) { door.isClosing = true } else { door.isClosing = false } door.openClose(); door.draw(); //exit room glow ctx.fillStyle = "rgba(0,255,255,0.05)" ctx.fillRect(1600, -400, 400, 400) //ammo tunnel shadow ctx.fillStyle = "rgba(0,0,0,0.4)" ctx.fillRect(1250, -2800, 100, 2200) }; if (m.health < 1) { powerUps.directSpawn(1298, -3500, "heal", false, 23); powerUps.directSpawn(1305, -3000, "heal", false, 35); } 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 }); } spawn.mapRect(-2750, -2800, 2600, 4600); //left wall spawn.mapRect(2000, -2800, 2600, 4600); //right wall spawn.mapRect(-250, 50, 3500, 1750); //floor spawn.mapRect(-200, 0, 950, 100); spawn.mapRect(1575, 0, 500, 100); spawn.mapRect(-150, -2800, 1400, 2200); //roof with tunnel for ammo spawn.mapRect(1350, -2800, 675, 2200); spawn.mapRect(725, 12, 50, 25); spawn.mapRect(725, 25, 75, 25); spawn.mapRect(750, 38, 75, 25); spawn.mapRect(1525, 25, 75, 50); spawn.mapRect(1500, 38, 50, 25); spawn.mapRect(1550, 12, 50, 25); // spawn.mapRect(1600, -1200, 500, 850); //exit roof // spawn.mapRect(1790, -600, 250, 225); //button left wall // spawn.mapRect(1625, -400, 400, 50); spawn.mapRect(1600, -400, 50, 225); //exit room left upper wall spawn.mapRect(1600, -600, 425, 250); }, shotGun() { level.difficultyIncrease(1) //difficulty on training mode resets to zero with each new level level.setPosToSpawn(60, -50); //normal spawn spawn.mapRect(10, -10, 100, 20); //small platform for player level.exit.x = 1775; level.exit.y = -35; spawn.mapRect(level.exit.x, level.exit.y + 25, 100, 100); //exit bump simulation.zoomScale = 1400 //1400 is normal level.defaultZoom = 1400 simulation.zoomTransition(level.defaultZoom, 1) document.body.style.backgroundColor = level.trainingBackgroundColor b.removeAllGuns(); b.giveGuns("shotgun") // b.guns[b.activeGun].ammo = 0 // simulation.updateGunHUD(); const door = level.door(1612.5, -175, 25, 190, 185, 3) let instruction = 0 level.trainingText(`use your shotgun to clear the room of mobs`) level.custom = () => { if (instruction === 0 && mob.length === 0) { instruction++ level.trainingText(`use your shotgun to clear the room of mobs`) } //spawn ammo if you run out let isAmmo = false for (let i = 0; i < powerUp.length; i++) { if (powerUp[i].name === 'ammo') isAmmo = true } if (!isAmmo && b.inventory.length && b.guns[b.activeGun].ammo === 0) { powerUps.directSpawn(1300, -2000, "ammo", false); powerUps.directSpawn(1301, -2200, "ammo", false); } //exit room ctx.fillStyle = "#f2f2f2" ctx.fillRect(1600, -400, 400, 400) level.enter.draw(); level.exit.drawAndCheck(); }; level.customTopLayer = () => { if (mob.length > 0) { door.isClosing = true } else { door.isClosing = false } door.openClose(); door.draw(); //exit room glow ctx.fillStyle = "rgba(0,255,255,0.05)" ctx.fillRect(1600, -400, 400, 400) //ammo tunnel shadow ctx.fillStyle = "rgba(0,0,0,0.4)" ctx.fillRect(1250, -2800, 100, 2200) }; if (m.health < 1) { powerUps.directSpawn(1298, -3500, "heal", false, 23); powerUps.directSpawn(1305, -3000, "heal", false, 35); } for (let i = 0; i < 3; i++) { spawn.hopper(1300 + i, -3000 - 2000 * i, 25 + 5 * i) // Matter.Body.setVelocity(mob[mob.length - 1], { x: 0, y: 0 }); } spawn.mapRect(-2750, -2800, 2600, 4600); //left wall spawn.mapRect(2000, -2800, 2600, 4600); //right wall spawn.mapRect(-250, 50, 3500, 1750); //floor spawn.mapRect(-200, 0, 950, 100); spawn.mapRect(1575, 0, 500, 100); spawn.mapRect(-150, -2800, 1400, 2200); //roof with tunnel for ammo spawn.mapRect(1350, -2800, 675, 2200); spawn.mapRect(725, 12, 50, 25); spawn.mapRect(725, 25, 75, 25); spawn.mapRect(750, 38, 75, 25); spawn.mapRect(1525, 25, 75, 50); spawn.mapRect(1500, 38, 50, 25); spawn.mapRect(1550, 12, 50, 25); spawn.mapRect(1600, -400, 50, 225); //exit room left upper wall spawn.mapRect(1600, -600, 425, 250); }, superBall() { level.difficultyIncrease(1) //difficulty on training mode resets to zero with each new level level.setPosToSpawn(60, -50); //normal spawn spawn.mapRect(10, -10, 100, 20); //small platform for player level.exit.x = 1775; level.exit.y = -35; spawn.mapRect(level.exit.x, level.exit.y + 25, 100, 100); //exit bump simulation.zoomScale = 1400 //1400 is normal level.defaultZoom = 1400 simulation.zoomTransition(level.defaultZoom, 1) document.body.style.backgroundColor = level.trainingBackgroundColor b.removeAllGuns(); b.giveGuns("super balls") // b.guns[b.activeGun].ammo = 0 // simulation.updateGunHUD(); const door = level.door(1612.5, -175, 25, 190, 185, 3) let instruction = 0 level.trainingText(`use super balls to clear the room of mobs`) level.custom = () => { if (instruction === 0 && mob.length === 0) { instruction++ level.trainingText(`use super balls to clear the room of mobs`) } //spawn ammo if you run out let isAmmo = false for (let i = 0; i < powerUp.length; i++) { if (powerUp[i].name === 'ammo') isAmmo = true } if (!isAmmo && b.inventory.length && b.guns[b.activeGun].ammo === 0) { powerUps.directSpawn(1300, -2000, "ammo", false); powerUps.directSpawn(1301, -2200, "ammo", false); } //exit room ctx.fillStyle = "#f2f2f2" ctx.fillRect(1600, -400, 400, 400) level.enter.draw(); level.exit.drawAndCheck(); }; level.customTopLayer = () => { if (mob.length > 0) { door.isClosing = true } else { door.isClosing = false } door.openClose(); door.draw(); //exit room glow ctx.fillStyle = "rgba(0,255,255,0.05)" ctx.fillRect(1600, -400, 400, 400) //ammo tunnel shadow ctx.fillStyle = "rgba(0,0,0,0.2)" // ctx.fillRect(1225, -2800, 125, 2450) ctx.fillRect(-150, -2800, 1500, 2450); }; if (m.health < 1) { powerUps.directSpawn(1298, -3500, "heal", false, 23); powerUps.directSpawn(1305, -3000, "heal", false, 35); } for (let i = 0; i < 6; i++) { spawn.spawner(i * 230, -800) // 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 spawn.mapRect(-225, -2800, 1450, 2000); spawn.mapRect(1350, -2800, 675, 2450); spawn.mapRect(-2750, -2800, 2600, 4600); //left wall spawn.mapRect(2000, -2800, 2600, 4600); //right wall spawn.mapRect(-250, 50, 3500, 1750); //floor spawn.mapRect(-200, 0, 950, 100); spawn.mapRect(1575, 0, 500, 100); spawn.mapRect(725, 12, 50, 25); spawn.mapRect(725, 25, 75, 25); spawn.mapRect(750, 38, 75, 25); spawn.mapRect(1525, 25, 75, 50); spawn.mapRect(1500, 38, 50, 25); spawn.mapRect(1550, 12, 50, 25); spawn.mapRect(1600, -400, 50, 225); //exit room left upper wall }, matterWave() { //fire wave through the map to kill mosb level.difficultyIncrease(1) //difficulty on training mode resets to zero with each new level level.setPosToSpawn(60, -50); //normal spawn spawn.mapRect(10, -10, 100, 20); //small platform for player level.exit.x = 1775; level.exit.y = -35; spawn.mapRect(level.exit.x, level.exit.y + 25, 100, 100); //exit bump simulation.zoomScale = 1400 //1400 is normal level.defaultZoom = 1400 simulation.zoomTransition(level.defaultZoom, 1) document.body.style.backgroundColor = level.trainingBackgroundColor b.removeAllGuns(); b.giveGuns("wave") // b.guns[b.activeGun].ammo = 0 // simulation.updateGunHUD(); const door = level.door(1612.5, -175, 25, 190, 185, 3) let instruction = 0 level.trainingText(`use wave to clear the room of mobs`) level.custom = () => { if (instruction === 0 && mob.length === 0) { instruction++ level.trainingText(`use wave to clear the room of mobs`) } //spawn ammo if you run out let isAmmo = false for (let i = 0; i < powerUp.length; i++) { if (powerUp[i].name === 'ammo') isAmmo = true } if (!isAmmo && b.inventory.length && b.guns[b.activeGun].ammo === 0) { powerUps.directSpawn(1300, -2000, "ammo", false); powerUps.directSpawn(1301, -2200, "ammo", false); } //exit room ctx.fillStyle = "#f2f2f2" ctx.fillRect(1600, -400, 400, 400) level.enter.draw(); level.exit.drawAndCheck(); }; level.customTopLayer = () => { if (mob.length > 0) { door.isClosing = true } else { door.isClosing = false } door.openClose(); door.draw(); //exit room glow ctx.fillStyle = "rgba(0,255,255,0.05)" ctx.fillRect(1600, -400, 400, 400) //ammo tunnel shadow ctx.fillStyle = "rgba(0,0,0,0.2)" // ctx.fillRect(1225, -2800, 125, 2450) ctx.fillRect(-150, -2800, 1500, 2450); }; if (m.health < 1) { powerUps.directSpawn(1298, -3500, "heal", false, 23); powerUps.directSpawn(1305, -3000, "heal", false, 35); } for (let i = 0; i < 6; i++) { spawn.springer(i * 200, -800) // Matter.Body.setVelocity(mob[mob.length - 1], { x: 0, y: 0 }); } spawn.springer(1825, -330, 20); spawn.mapRect(1175, -850, 50, 500); //upper room with mobs spawn.mapRect(-225, -400, 1450, 50); spawn.mapRect(-225, -2800, 1450, 2000); spawn.mapRect(1350, -2800, 675, 2450); spawn.mapRect(-2750, -2800, 2600, 4600); //left wall spawn.mapRect(2000, -2800, 2600, 4600); //right wall spawn.mapRect(-250, 50, 3500, 1750); //floor spawn.mapRect(-200, 0, 950, 100); spawn.mapRect(1575, 0, 500, 100); spawn.mapRect(725, 12, 50, 25); spawn.mapRect(725, 25, 75, 25); spawn.mapRect(750, 38, 75, 25); spawn.mapRect(1525, 25, 75, 50); spawn.mapRect(1500, 38, 50, 25); spawn.mapRect(1550, 12, 50, 25); spawn.mapRect(1600, -400, 50, 225); //exit room left upper wall }, missile() { //fire a missile to kill mobs and trigger button level.difficultyIncrease(1) //difficulty on training mode resets to zero with each new level level.setPosToSpawn(60, -50); //normal spawn spawn.mapRect(10, -10, 100, 20); //small platform for player level.exit.x = 1775; level.exit.y = -35; spawn.mapRect(level.exit.x, level.exit.y + 25, 100, 30); //exit bump simulation.zoomScale = 1400 //1400 is normal level.defaultZoom = 1400 simulation.zoomTransition(level.defaultZoom, 1) document.body.style.backgroundColor = level.trainingBackgroundColor b.removeAllGuns(); b.giveGuns("missiles") // b.guns[b.activeGun].ammo = 0 // simulation.updateGunHUD(); const buttonDoor = level.button(2500, 50) const door = level.door(1612.5, -175, 25, 190, 185, 3) let instruction = 0 level.trainingText(`use missiles to drop a block on the button`) level.custom = () => { if (instruction === 0 && mob.length === 0) { instruction++ level.trainingText(`use missiles to drop a block on the button`) } //spawn ammo if you run out let isAmmo = false for (let i = 0; i < powerUp.length; i++) { if (powerUp[i].name === 'ammo') isAmmo = true } if (!isAmmo && b.inventory.length && b.guns[b.activeGun].ammo === 0) { powerUps.directSpawn(1300, -2000, "ammo", false); powerUps.directSpawn(1301, -2200, "ammo", false); } //exit room ctx.fillStyle = "#f2f2f2" ctx.fillRect(1600, -400, 400, 400) level.enter.draw(); level.exit.drawAndCheck(); }; level.customTopLayer = () => { buttonDoor.query(); buttonDoor.draw(); if (buttonDoor.isUp) { door.isClosing = true } else { door.isClosing = false } door.openClose(); door.draw(); //exit room glow ctx.fillStyle = "rgba(0,255,255,0.05)" ctx.fillRect(1600, -400, 400, 400) //tunnel shadow ctx.fillStyle = "rgba(0,0,0,0.4)" ctx.fillRect(1250, -2800, 100, 2200) ctx.fillRect(1550, 25, 475, 25); }; if (m.health < 1) { powerUps.directSpawn(1298, -3500, "heal", false, 23); powerUps.directSpawn(1305, -3000, "heal", false, 35); } for (let i = 0; i < 10; i++) { spawn.springer(2100 + i * 100, -250) // Matter.Body.setVelocity(mob[mob.length - 1], { x: 0, y: 0 }); } spawn.mapRect(-2750, -2800, 2600, 4600); //left wall // spawn.mapRect(2000, -2800, 2600, 4600); //right wall spawn.mapRect(3050, -2800, 1550, 4600); spawn.mapRect(-250, 50, 3500, 1750); //floor spawn.mapRect(-200, 0, 950, 100); spawn.mapRect(-150, -2800, 1400, 2200); //roof with tunnel for ammo spawn.mapRect(1350, -2800, 675, 2200); spawn.mapRect(725, 12, 50, 25); spawn.mapRect(725, 25, 75, 25); spawn.mapRect(750, 38, 75, 25); // spawn.mapRect(1350, 0, 675, 30); spawn.mapRect(1550, 0, 475, 35); spawn.mapRect(1600, -400, 50, 225); //exit room left upper wall spawn.mapRect(1600, -600, 425, 250); spawn.mapRect(1975, -600, 50, 625); spawn.mapRect(2025, -2800, 1075, 2450); }, stack() { //stack blocks to get to exit level.difficultyIncrease(1) //difficulty on training mode resets to zero with each new level level.setPosToSpawn(60, -50); //normal spawn spawn.mapRect(10, -10, 100, 20); //small platform for player level.exit.x = 1775; level.exit.y = -685; spawn.mapRect(level.exit.x, level.exit.y + 25, 100, 100); //exit bump simulation.zoomScale = 1400 //1400 is normal level.defaultZoom = 1400 simulation.zoomTransition(level.defaultZoom, 1) document.body.style.backgroundColor = level.trainingBackgroundColor b.removeAllGuns(); let instruction = 0 level.trainingText(`use your field to stack the blocks`) level.custom = () => { if (instruction === 0 && m.pos.x > 1635) { instruction++ level.trainingText(`use your field to stack the blocks`) } //exit room ctx.fillStyle = "#f2f2f2" ctx.fillRect(1600, -1050, 400, 400) level.enter.draw(); level.exit.drawAndCheck(); }; level.customTopLayer = () => { //exit room glow ctx.fillStyle = "rgba(0,255,255,0.05)" ctx.fillRect(1600, -1050, 400, 400) //ammo tunnel shadow ctx.fillStyle = "rgba(0,0,0,0.4)" ctx.fillRect(250, -2800, 200, 1800) }; if (m.health < 1) { powerUps.directSpawn(298, -3500, "heal", false, 23); powerUps.directSpawn(305, -3000, "heal", false, 35); } for (let i = 0; i < 15; i++) { spawn.bodyRect(280, -2000 - 500 * i, 30 + 80 * Math.random(), 30 + 80 * Math.random()); } spawn.mapRect(-2750, -2800, 2600, 4600); //left wall spawn.mapRect(2000, -2800, 2600, 4600); //right wall spawn.mapRect(-250, 0, 3500, 1800); //floor spawn.mapRect(1600, -650, 450, 775); spawn.mapRect(-150, -2800, 400, 1800); //roof with tunnel for ammo spawn.mapRect(450, -2800, 1675, 1800); spawn.mapVertex(1300, 0, "400 0 -500 0 -300 -125 400 -125"); //base }, mine() { //kill mobs and tack their bodies level.difficultyIncrease(1) //difficulty on training mode resets to zero with each new level level.setPosToSpawn(300, -50); //normal spawn spawn.mapRect(250, -10, 100, 20); //small platform for player level.exit.x = 1775; level.exit.y = -685; spawn.mapRect(level.exit.x, level.exit.y + 25, 100, 100); //exit bump simulation.zoomScale = 1400 //1400 is normal level.defaultZoom = 1400 simulation.zoomTransition(level.defaultZoom, 1) document.body.style.backgroundColor = level.trainingBackgroundColor b.removeAllGuns(); b.giveGuns("mine") let instruction = 0 level.trainingText(`press the red button to spawn a mob`) const button = level.button(-100, -200) button.isUp = true spawn.mapRect(-150, -200, 240, 425); level.custom = () => { if (instruction === 0 && !button.isUp) { instruction++ level.trainingText(`press the red button to spawn a mob
turn the mobs into blocks`) } else if (instruction === 1 && body.length > 2) { instruction++ level.trainingText(`press the red button to spawn a mob
turn the mobs into blocks

use your field to stack the blocks`) } else if (instruction === 2 && m.pos.x > 1635) { instruction++ level.trainingText(`press the red button to spawn a mob
turn the mobs into blocks
use your field to stack the blocks
`) } //spawn ammo if you run out let isAmmo = false for (let i = 0; i < powerUp.length; i++) { if (powerUp[i].name === 'ammo') isAmmo = true } if (!isAmmo && b.inventory.length && b.guns[b.activeGun].ammo === 0) { powerUps.directSpawn(1300, -2000, "ammo", false); powerUps.directSpawn(1301, -2200, "ammo", false); } //exit room ctx.fillStyle = "#f2f2f2" ctx.fillRect(1600, -1050, 400, 400) level.enter.draw(); level.exit.drawAndCheck(); }; level.customTopLayer = () => { button.query(); button.draw(); if (!button.isUp) { if (button.isReady) { button.isReady = false spawn.exploder(335, -1700) Matter.Body.setVelocity(mob[mob.length - 1], { x: 0, y: 20 }); ctx.fillStyle = "rgba(255,0,0,0.9)" ctx.fillRect(550, -2800, 200, 1800) } } else { button.isReady = true } //exit room glow ctx.fillStyle = "rgba(0,255,255,0.05)" ctx.fillRect(1600, -1050, 400, 400) //ammo tunnel shadow ctx.fillStyle = "rgba(0,0,0,0.4)" ctx.fillRect(550, -2800, 200, 1800) }; if (m.health < 1) { powerUps.directSpawn(298, -3500, "heal", false, 23); powerUps.directSpawn(305, -3000, "heal", false, 35); } spawn.mapRect(-2750, -2800, 2600, 4600); //left wall spawn.mapRect(2000, -2800, 2600, 4600); //right wall spawn.mapRect(-250, 0, 3500, 1800); //floor spawn.mapRect(1600, -650, 450, 775); spawn.mapRect(-150, -2800, 700, 1800); //roof with tunnel for ammo spawn.mapRect(750, -2800, 1675, 1800); spawn.mapVertex(1300, 0, "400 0 -600 0 -300 -125 400 -125"); //base }, grenades() { //jump at the top of the elevator's path to go extra high level.difficultyIncrease(1) //difficulty on training mode resets to zero with each new level level.setPosToSpawn(0, -50); //normal spawn spawn.mapRect(-50, -10, 100, 20); //small platform for player level.exit.x = 1900; level.exit.y = -2835; spawn.mapRect(level.exit.x, level.exit.y + 25, 100, 100); //exit bump simulation.zoomScale = 1400 //1400 is normal level.defaultZoom = 1400 simulation.zoomTransition(level.defaultZoom, 1) document.body.style.backgroundColor = level.trainingBackgroundColor b.removeAllGuns(); b.giveGuns("grenades") const elevator1 = level.elevator(550, -100, 180, 25, -840, 0.003, { up: 0.05, down: 0.2 }) // x, y, width, height, maxHeight, force = 0.003, friction = { up: 0.01, down: 0.2 }) { elevator1.addConstraint(); const toggle1 = level.toggle(275, 0) //(x,y,isOn,isLockOn = true/false) const elevator2 = level.elevator(1400, -950, 180, 25, -2400, 0.0025) // x, y, width, height, maxHeight, force = 0.003, friction = { up: 0.01, down: 0.2 }) { elevator2.addConstraint(); const button2 = level.button(1000, -850) let instruction = 0 level.trainingText(`flip the switch to turn on the elevator`) level.custom = () => { if (instruction === 0 && elevator1.isOn) { instruction++ level.trainingText(`flip the switch to turn on the elevator
put a block on the button to active the elevator`) } else if (instruction === 1 && elevator2.isOn) { instruction++ level.trainingText(`flip the switch to turn on the elevator
put a block on the button to active the elevator

hold jump before the elevator's apex to reach the exit`) } else if (instruction === 2 && m.pos.x > 1635) { instruction++ level.trainingText(`flip the switch to turn on the elevator
put a block on the button to active the elevator
hold jump before the elevator's apex to reach the exit
`) } //exit room ctx.fillStyle = "#f2f2f2" ctx.fillRect(1725, -3100, 375, 300); level.enter.draw(); level.exit.drawAndCheck(); }; level.customTopLayer = () => { toggle1.query(); if (!toggle1.isOn) { if (elevator1.isOn) { elevator1.isOn = false elevator1.frictionAir = 0.2 elevator1.addConstraint(); } } else if (!elevator1.isOn) { elevator1.isOn = true elevator1.isUp = false elevator1.removeConstraint(); elevator1.frictionAir = 0.2 //elevator.isUp ? 0.01 : 0.2 } if (elevator1.isOn) { elevator1.move(); ctx.fillStyle = "#444" } else { ctx.fillStyle = "#aaa" } ctx.fillRect(640, -825, 1, 745) button2.query(); button2.draw(); if (button2.isUp) { if (elevator2.isOn) { elevator2.isOn = false elevator2.frictionAir = 0.2 elevator2.addConstraint(); } } else if (!elevator2.isOn) { elevator2.isOn = true elevator2.isUp = false elevator2.removeConstraint(); elevator2.frictionAir = 0.2 //elevator.isUp ? 0.01 : 0.2 } if (elevator2.isOn) { elevator2.move(); ctx.fillStyle = "#444" } else { ctx.fillStyle = "#aaa" } ctx.fillRect(1490, -2300, 1, 1375) //exit room glow ctx.fillStyle = "rgba(0,255,255,0.05)" ctx.fillRect(1725, -3100, 375, 300); //shadows ctx.fillStyle = "rgba(0,0,0,0.05)" ctx.fillRect(-150, -250, 300, 250); let grd = ctx.createLinearGradient(0, -150, 0, -2300); grd.addColorStop(0, "rgba(0,0,0,0.35)"); grd.addColorStop(1, "rgba(0,0,0,0)"); ctx.fillStyle = grd //"rgba(0,0,100,0.01)" ctx.fillRect(-200, -2300, 1825, 2300); }; if (m.health < 1) { powerUps.directSpawn(298, -3500, "heal", false, 23); powerUps.directSpawn(305, -3000, "heal", false, 35); } spawn.mapRect(-2750, -4800, 2600, 6600); //left wall spawn.mapRect(1600, -2800, 3000, 4600); //right wall spawn.mapRect(-150, -4800, 300, 4550); spawn.mapRect(2125, -4775, 2475, 2050); spawn.mapRect(-250, 0, 3500, 1800); //floor spawn.mapRect(750, -850, 950, 950); spawn.mapRect(125, -275, 25, 100); spawn.mapRect(2100, -3150, 50, 350); spawn.mapRect(1725, -3150, 50, 175); spawn.mapRect(1725, -3150, 425, 50); spawn.nodeGroup(1200, -1500, "grenadier", 7); }, harpoon() { //jump at the top of the elevator's path to go extra high level.difficultyIncrease(1) //difficulty on training mode resets to zero with each new level level.setPosToSpawn(0, -50); //normal spawn spawn.mapRect(-50, -10, 100, 20); //small platform for player level.exit.x = 1900; level.exit.y = -2835; spawn.mapRect(level.exit.x, level.exit.y + 25, 100, 100); //exit bump simulation.zoomScale = 1400 //1400 is normal level.defaultZoom = 1400 simulation.zoomTransition(level.defaultZoom, 1) document.body.style.backgroundColor = level.trainingBackgroundColor b.removeAllGuns(); b.giveGuns("harpoon") let instruction = 0 level.trainingText(`climb up to the exit`) level.custom = () => { if (instruction === 0 && m.pos.x > 1635) { instruction++ level.trainingText(`climb up to the exit`) } //exit room ctx.fillStyle = "#f2f2f2" ctx.fillRect(1725, -3100, 375, 300); level.enter.draw(); level.exit.drawAndCheck(); }; level.customTopLayer = () => { //exit room glow ctx.fillStyle = "rgba(0,255,255,0.05)" ctx.fillRect(1725, -3100, 375, 300); //shadows ctx.fillStyle = "rgba(0,90,100,0.05)" ctx.fillRect(-150, -250, 300, 250); let grd = ctx.createLinearGradient(0, -150, 0, -2300); grd.addColorStop(0, "rgba(0,90,100,0.35)"); grd.addColorStop(1, "rgba(0,90,100,0)"); ctx.fillStyle = grd //"rgba(0,0,100,0.01)" ctx.fillRect(-200, -2300, 1825, 2300); vanish1.query(); vanish2.query(); vanish3.query(); vanish4.query(); vanish5.query(); vanish6.query(); vanish7.query(); vanish8.query(); vanish9.query(); vanish10.query(); vanish11.query(); vanish12.query(); }; const vanish1 = level.vanish(175, -325, 175, 25); //x, y, width, height, hide = { x: 0, y: 100 } //hide should just be somewhere behind the map so the player can't see it const vanish2 = level.vanish(525, -625, 175, 25); const vanish3 = level.vanish(1125, -1125, 175, 25); const vanish4 = level.vanish(1500, -1450, 100, 25); const vanish5 = level.vanish(1125, -1675, 175, 25); const vanish6 = level.vanish(750, -1950, 175, 25); const vanish7 = level.vanish(550, -1950, 175, 25); const vanish8 = level.vanish(350, -1950, 175, 25); const vanish9 = level.vanish(150, -1950, 175, 25); const vanish10 = level.vanish(325, -2300, 200, 25); const vanish11 = level.vanish(725, -2550, 100, 25); const vanish12 = level.vanish(1125, -2700, 150, 25); if (m.health < 1) { powerUps.directSpawn(298, -3500, "heal", false, 23); powerUps.directSpawn(305, -3000, "heal", false, 35); } spawn.mapRect(-2750, -4800, 2600, 6600); //left wall spawn.mapRect(1600, -2800, 3000, 4600); //right wall spawn.mapRect(-150, -4800, 300, 4550); spawn.mapRect(2125, -4775, 2475, 2050); spawn.mapRect(-250, 0, 3500, 1800); //floor spawn.mapRect(750, -850, 950, 950); spawn.mapRect(125, -275, 25, 100); spawn.mapRect(2100, -3150, 50, 350); spawn.mapRect(1725, -3150, 50, 175); spawn.mapRect(1725, -3150, 425, 50); spawn.grower(250, -375); spawn.grower(1000, -900) spawn.grower(1475, -925); spawn.grower(275, -2000); spawn.grower(650, -2000); spawn.grower(1475, -975); spawn.grower(1575, -1525); spawn.grower(1700, -2850); }, diamagnetism() { if (localSettings.isHideHUD) localSettings.isHideHUD = false m.addHealth(Infinity) document.getElementById("health").style.display = "none" //hide your health bar document.getElementById("health-bg").style.display = "none" document.getElementById("defense-bar").style.display = "none" document.getElementById("damage-bar").style.display = "none" const futureGuns = ["harpoon", "shotgun", "nail gun", "super balls", "wave", "foam", "laser"]; const futureGun = Math.floor(Math.random() * futureGuns.length) b.giveGuns(futureGuns[futureGun], Infinity) m.setField(2) m.fieldRegen = 0; level.trainingText(`diamagnetism by Richard0820
Don't get hit.
Find the portal to the exit.`) const dodge = []; const button = level.button(350 - 63, -300) const door = level.door(750, -275, 50, 125, 125) const door2 = level.door(750, -525, 50, 125, 125); const forceOne = forceField(4425, -3925, 525, 3975); const forceTwo = forceField(1550, -9950, 275, 3300); const forceThree = forceField(4200, -8725, 750, 4450); const respawnX = []; respawnX.push(setRespawn(-50, -625, 825, 375)); respawnX.push(setRespawn(3225, -3675, 1200, 1000)); respawnX.push(setRespawn(3575, -5675, 625, 800)); respawnX.push(setRespawn(775, -4250, 400, 375)); respawnX.push(setRespawn(2825, -2975, 250, 300)); respawnX.push(setRespawn(3675, -1125, 325, 250)); let respawnPoints = { x: 125, y: -9575, } door2.isClosing = true; button.isUp = true level.setPosToSpawn(125, -9575); //normal spawn level.exit.x = -1825; level.exit.y = 50; spawn.mapRect(level.enter.x, level.enter.y + 20, 100, 20); //bump for level entrance spawn.mapRect(level.exit.x, level.exit.y + 20, 100, 20); //bump for level exit level.defaultZoom = 1800 simulation.zoomTransition(level.defaultZoom) document.body.style.backgroundColor = "#d8dadf"; alternate(-50, -9050, 425, 100); const image = new Image() image.src = "https://raw.githubusercontent.com/Whyisthisnotavalable/image-yy/main/Hotpot-removed.png"; level.chain(-675, 400, -0.4366271598, true, 20) level.chain(-1600, 125, 0.5144513131, true, 19) const portal = portall({ x: 3825, y: -1000, }, 3 * Math.PI, { x: 550, y: -100, }, 3 * Math.PI ); level.custom = () => { portal[2].query(); portal[3].query(); portal[0].draw(); portal[1].draw(); portal[2].draw(); portal[3].draw(); forceOne.query() forceTwo.query() forceThree.query() if (input.field && player.position.x < 775 && player.position.x > -50) { if (m.energy > 0.02) { m.energy -= 0.01 } else { input.field = false } } level.exit.drawAndCheck(); level.enter.draw(); button.query() button.draw() if (!button.isUp) { door.isClosing = true; door2.isClosing = false; } else if (button.isUp) { door.isClosing = false; door2.isClosing = true; } door.draw() door.openClose() door2.draw() door2.openClose() for (let i = 0; i < dodge.length; i++) dodge[i].query(); for (let i = 0; i < respawnX.length; i++) respawnX[i].query(); ctx.fillStyle = "gray"; ctx.fillRect(1175, -6650, 2400, 2375); ctx.drawImage(image, 1175 + 1200 - 250, -6650 + (2375 / 2) - 250, 500, 500) if (m.immuneCycle > m.cycle) { m.energy = m.maxEnergy Matter.Body.setPosition(player, { x: respawnPoints.x, y: respawnPoints.y }) } }; level.customTopLayer = () => { }; spawn.mapRect(-100, 0, 5100, 100); spawn.mapRect(-100, -10000, 5100, 100); spawn.mapRect(4900, -10000, 100, 10100); spawn.mapRect(-100, -10000, 100, 9800); spawn.mapRect(-100, -9525, 450, 100); spawn.mapRect(725, -300, 100, 400); spawn.mapRect(725, -10000, 100, 9500); spawn.mapRect(-100, -300, 925, 100); spawn.mapRect(800, -675, 3675, 100); spawn.mapRect(4375, -1425, 100, 850); spawn.mapRect(1350, -1425, 3125, 100); spawn.mapRect(1350, -1425, 100, 600); spawn.mapRect(1350, -925, 2700, 100); spawn.mapRect(1575, -1175, 2475, 100); spawn.mapRect(3950, -1175, 100, 350); spawn.mapRect(4375, -2725, 100, 1400); spawn.mapRect(775, -2725, 2325, 100); spawn.mapRect(3200, -2725, 1275, 100); spawn.mapRect(3200, -3975, 100, 1350); spawn.mapRect(4375, -3950, 100, 1125); spawn.mapRect(4375, -3975, 625, 100); spawn.mapRect(3200, -4325, 100, 450); spawn.mapRect(3200, -4325, 1600, 100); spawn.mapRect(4450, -2725, 50, 25); spawn.mapRect(1125, -3025, 2175, 100); spawn.mapRect(725, -3925, 2175, 100); spawn.mapRect(3525, -6700, 100, 2475); spawn.mapRect(4150, -6700, 100, 2475); spawn.mapRect(1125, -6700, 2500, 105); spawn.mapRect(1125, -6700, 100, 2625); spawn.mapRect(1500, -8775, 100, 2175); spawn.mapRect(4150, -8775, 100, 1900); spawn.mapRect(1775, -8775, 2475, 100); spawn.mapRect(4225, -6700, 50, 25); spawn.mapRect(4150, -8775, 850, 100); spawn.mapRect(3600, -2825, 125, 125); spawn.mapRect(3275, -3050, 125, 125); spawn.mapRect(3600, -3275, 125, 125); spawn.mapRect(3300, -3525, 125, 125); spawn.mapRect(3575, -3725, 900, 125); spawn.mapRect(4075, -3775, 75, 75); spawn.mapRect(4225, -3875, 75, 175); spawn.mapRect(3600, -6625, 100, 100); spawn.mapRect(4075, -6475, 100, 100); spawn.mapRect(3600, -6300, 100, 100); spawn.mapRect(4075, -6175, 100, 100); spawn.mapRect(3600, -6000, 100, 100); spawn.mapRect(4075, -5875, 100, 100); spawn.mapRect(3600, -5700, 100, 100); spawn.mapRect(4075, -5550, 100, 100); spawn.mapRect(3600, -5400, 100, 1125); spawn.mapRect(3675, -5300, 100, 1025); spawn.mapRect(3750, -5225, 100, 950); spawn.mapRect(3825, -5150, 100, 875); spawn.mapRect(3900, -5075, 100, 800); spawn.mapRect(3975, -5000, 100, 725); spawn.mapRect(4050, -4925, 125, 650); spawn.mapRect(4150, -6925, 75, 125); spawn.mapRect(1775, -8775, 100, 1900); spawn.mapRect(1775, -9950, 100, 975); spawn.mapRect(1500, -9950, 100, 975); spawn.mapRect(1275, -8775, 325, 100); spawn.mapRect(1200, -7775, 25, 1175); spawn.mapRect(1250, -7950, 25, 1350); spawn.mapRect(1300, -8175, 25, 1575); spawn.mapRect(1350, -8500, 25, 1900); spawn.mapRect(1400, -8625, 25, 2025); spawn.mapRect(1450, -8700, 25, 2100); spawn.mapRect(1150, -7625, 25, 1025); spawn.mapRect(1125, -4325, 2175, 100); spawn.mapRect(4250, -925, 150, 100); spawn.mapRect(575, -225, 175, 50); spawn.mapRect(575, -50, 175, 75); spawn.mapRect(-25, 50, 125, 100); spawn.mapRect(75, 75, 50, 50); spawn.sniper(3600, -7300); spawn.sniper(3325, -7475); spawn.sniper(2825, -7500); spawn.sniper(2250, -7450); spawn.sniper(4125, -5150); spawn.sniper(4100, -5675); spawn.sniper(4100, -5950); spawn.sniper(4125, -6325); spawn.sniper(3875, -6975); spawn.stabber(4075, -4075); spawn.stabber(3775, -3950); spawn.stabber(3500, -3850); spawn.stabber(4000, -3500); spawn.stabber(3850, -3125); spawn.stabber(3450, -3125); spawn.stabber(4225, -2900); spawn.hopper(4125, -250); spawn.hopper(3525, -250); spawn.hopper(2925, -325); spawn.hopper(2175, -150); spawn.hopper(1175, -400); spawn.mantisBoss(3425, -9350); spawn.pulsarBoss(1725, -6050, 1); spawn.pulsarBoss(1800, -4850, 1); spawn.pulsarBoss(3000, -4825, 1); spawn.pulsarBoss(2975, -6175, 1); spawn.spinner(2025, -4050); spawn.spinner(2125, -2825); spawn.pulsar(2450, -3775); spawn.pulsar(2200, -3750); spawn.pulsar(1900, -3775); spawn.pulsar(1600, -3725); spawn.pulsar(1300, -3750); spawn.pulsar(925, -3725); spawn.focuser(3925, -2375); spawn.focuser(1150, -2450); spawn.focuser(2450, -1675); spawn.mapVertex(-850, 500, "0 0 500 0 250 500"); spawn.mapVertex(-1775, 250, "0 0 500 0 250 500"); spawn.bodyRect(25, -375, 50, 50); function alternate(x, y, width, height, spacingX = 25, spacingY = 1500, number = 6) { for (let i = 0; i < number; i++) { if (i % 2 === 0) { dodge.push(back(x, y + i * (height + spacingY), width, height, level.enter.x, level.enter.y)) } else { dodge.push(back(x + width - spacingX, y + i * (height + spacingY), width, height, level.enter.x, level.enter.y)) } } } function back(x, y, width, height, x1, y1) { return { move: { x: x1, y: y1 }, min: { x: x, y: y }, max: { x: x + width, y: y + height }, width: width, height: height, maxHeight: height, isOn: true, query() { if (this.isOn) { ctx.lineWidth = 5; ctx.strokeStyle = `hsla(0, 100%, 50%,${0.6 + 0.4 * Math.random()})` ctx.strokeRect(this.min.x, this.min.y, this.width, this.height) if (this.height > 0 && Matter.Query.region([player], this).length) { Matter.Body.setVelocity(player, { x: 0, y: 0 }) Matter.Body.setPosition(player, { x: this.move.x, y: this.move.y }) m.energy = m.maxEnergy; } } }, } } function forceField(x, y, width, height) { return { min: { x: x, y: y }, max: { x: x + width, y: y + height }, width: width, height: height, maxHeight: height, isOn: true, query() { if (this.isOn) { ctx.fillStyle = `rgba(0, 250, 250, 0.55)` ctx.fillRect(this.min.x, this.min.y, this.width, this.height) if (this.height > 0 && Matter.Query.region([player], this).length && input.field) { player.force.y -= 0.015; m.energy = m.maxEnergy; } ctx.fillStyle = `rgba(0, 250, 250)` ctx.fillRect(this.min.x + this.width * Math.random(), this.min.y, 5, this.height) } }, } } function setRespawn(x, y, width, height) { return { min: { x: x, y: y }, max: { x: x + width, y: y + height }, width: width, height: height, maxHeight: height, isOn: true, query() { if (this.isOn) { ctx.fillStyle = `rgba(0, 250, 0, 0.11)` ctx.fillRect(this.min.x, this.min.y, this.width, this.height) if (this.height > 0 && Matter.Query.region([player], this).length) { m.energy = m.maxEnergy; respawnPoints.x = this.min.x + (this.width / 2); respawnPoints.y = this.min.y + (this.height / 2); } } }, } } function portall(centerA, angleA, centerB, angleB) { const width = 50 const height = 150 const mapWidth = 200 const unitA = Matter.Vector.rotate({ x: 1, y: 0 }, angleA) const unitB = Matter.Vector.rotate({ x: 1, y: 0 }, angleB) draw = function () { ctx.beginPath(); //portal let 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.fillStyle = this.color ctx.fill(); } query = function (isRemoveBlocks = false) { if (Matter.Query.collides(this, [player]).length === 0) { //not touching player if (player.isInPortal === this) player.isInPortal = null } else if (player.isInPortal !== this) { //touching player if (m.buttonCD_jump === m.cycle) player.force.y = 0 // undo a jump right before entering the portal m.buttonCD_jump = 0 //disable short jumps when letting go of jump key player.isInPortal = this.portalPair if (this.portalPair.angle % (Math.PI / 2)) { //if left, right up or down // if (m.immuneCycle < m.cycle + m.collisionImmuneCycles) m.immuneCycle = m.cycle + m.collisionImmuneCycles; //player is immune to damage for 30 cycles Matter.Body.setPosition(player, this.portalPair.portal.position); } else { // if (m.immuneCycle < m.cycle + m.collisionImmuneCycles) m.immuneCycle = m.cycle + m.collisionImmuneCycles; //player is immune to damage for 30 cycles Matter.Body.setPosition(player, this.portalPair.position); } let mag if (this.portalPair.angle !== 0 && this.portalPair.angle !== Math.PI) { //portal that fires the player up mag = Math.max(10, Math.min(50, player.velocity.y * 0.8)) + 11 } else { mag = Math.max(6, Math.min(50, Vector.magnitude(player.velocity))) } let v = Vector.mult(this.portalPair.unit, mag) Matter.Body.setVelocity(player, v); // move bots to player for (let i = 0; i < bullet.length; i++) { if (bullet[i].botType) { // Matter.Body.setPosition(bullet[i], this.portalPair.portal.position); Matter.Body.setPosition(bullet[i], Vector.add(this.portalPair.portal.position, { x: 250 * (Math.random() - 0.5), y: 250 * (Math.random() - 0.5) })); Matter.Body.setVelocity(bullet[i], { x: 0, y: 0 }); } } if (tech.isHealAttract) { //send heals to next portal for (let i = 0; i < powerUp.length; i++) { if (powerUp[i].name === "heal" && Vector.magnitudeSquared(Vector.sub(powerUp[i].position, m.pos)) < 1000000) { Matter.Body.setPosition(powerUp[i], Vector.add(this.portalPair.portal.position, { x: 500 * (Math.random() - 0.5), y: 500 * (Math.random() - 0.5) })); } } } } // if (body.length) { for (let i = 0, len = body.length; i < len; i++) { if (body[i] !== m.holdingTarget) { // body[i].bounds.max.x - body[i].bounds.min.x < 100 && body[i].bounds.max.y - body[i].bounds.min.y < 100 if (Matter.Query.collides(this, [body[i]]).length === 0) { if (body[i].isInPortal === this) body[i].isInPortal = null } else if (body[i].isInPortal !== this) { //touching this portal, but for the first time if (isRemoveBlocks) { Matter.Composite.remove(engine.world, body[i]); body.splice(i, 1); break } body[i].isInPortal = this.portalPair //teleport if (this.portalPair.angle % (Math.PI / 2)) { //if left, right up or down Matter.Body.setPosition(body[i], this.portalPair.portal.position); } else { //if at some odd angle Matter.Body.setPosition(body[i], this.portalPair.position); } //rotate velocity let mag if (this.portalPair.angle !== 0 && this.portalPair.angle !== Math.PI) { //portal that fires the player up mag = Math.max(10, Math.min(50, body[i].velocity.y * 0.8)) + 11 } else { mag = Math.max(6, Math.min(50, Vector.magnitude(body[i].velocity))) } let v = Vector.mult(this.portalPair.unit, mag) Matter.Body.setVelocity(body[i], v); } } } // } //remove block if touching // if (body.length) { // touching = Matter.Query.collides(this, body) // for (let i = 0; i < touching.length; i++) { // 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; // } // } // } // } // } // if (touching.length !== 0 && touching[0].bodyB !== m.holdingTarget) { // if (body.length) { // for (let i = 0; i < body.length; i++) { // if (body[i] === touching[0].bodyB) { // body.splice(i, 1); // break; // } // } // } // Matter.Composite.remove(engine.world, touching[0].bodyB); // } } const portalA = composite[composite.length] = Bodies.rectangle(centerA.x, centerA.y, width, height, { isSensor: true, angle: angleA, color: "hsla(197, 100%, 50%,0.7)", draw: draw, }); const portalB = composite[composite.length] = Bodies.rectangle(centerB.x, centerB.y, width, height, { isSensor: true, angle: angleB, color: "hsla(29, 100%, 50%, 0.7)", draw: draw }); const mapA = composite[composite.length] = Bodies.rectangle(centerA.x - 0.5 * unitA.x * mapWidth, centerA.y - 0.5 * unitA.y * mapWidth, mapWidth, height + 10, { collisionFilter: { category: cat.map, mask: cat.bullet | cat.powerUp | cat.mob | cat.mobBullet //cat.player | cat.map | cat.body | cat.bullet | cat.powerUp | cat.mob | cat.mobBullet }, unit: unitA, angle: angleA, color: color.map, draw: draw, query: query, lastPortalCycle: 0 }); Matter.Body.setStatic(mapA, true); //make static Composite.add(engine.world, mapA); //add to world const mapB = composite[composite.length] = Bodies.rectangle(centerB.x - 0.5 * unitB.x * mapWidth, centerB.y - 0.5 * unitB.y * mapWidth, mapWidth, height + 10, { collisionFilter: { category: cat.map, mask: cat.bullet | cat.powerUp | cat.mob | cat.mobBullet //cat.player | cat.map | cat.body | cat.bullet | cat.powerUp | cat.mob | cat.mobBullet }, unit: unitB, angle: angleB, color: color.map, draw: draw, query: query, lastPortalCycle: 0, }); Matter.Body.setStatic(mapB, true); //make static Composite.add(engine.world, mapB); //add to world mapA.portal = portalA mapB.portal = portalB mapA.portalPair = mapB mapB.portalPair = mapA return [portalA, portalB, mapA, mapB] } }, trainingTemplate() { //learn to crouch m.addHealth(Infinity) document.getElementById("health").style.display = "none" //hide your health bar document.getElementById("health-bg").style.display = "none" document.getElementById("defense-bar").style.display = "none" document.getElementById("damage-bar").style.display = "none" level.setPosToSpawn(60, -50); //normal spawn spawn.mapRect(10, -10, 100, 20); //small platform for player level.exit.x = 1775; level.exit.y = -35; spawn.mapRect(level.exit.x, level.exit.y + 25, 100, 100); //exit bump simulation.zoomScale = 1400 //1400 is normal level.defaultZoom = 1400 simulation.zoomTransition(level.defaultZoom, 1) document.body.style.backgroundColor = level.trainingBackgroundColor let instruction = 0 level.trainingText(`press ${input.key.down.replace('Key', '').replace('Digit', '')} to crouch`) level.custom = () => { if (instruction === 0 && input.down) { instruction++ level.trainingText(`press ${input.key.down.replace('Key', '').replace('Digit', '')} to crouch`) } //exit room ctx.fillStyle = "#f2f2f2" ctx.fillRect(1600, -400, 400, 400) level.enter.draw(); level.exit.drawAndCheck(); }; level.customTopLayer = () => { //exit room glow ctx.fillStyle = "rgba(0,255,255,0.05)" ctx.fillRect(1600, -400, 400, 400) }; spawn.mapRect(-2750, -2800, 2600, 4600); //left wall spawn.mapRect(2000, -2800, 2600, 4600); //right wall spawn.mapRect(-250, 50, 3500, 1750); //floor spawn.mapRect(-200, 0, 950, 100); spawn.mapRect(1575, 0, 500, 100); spawn.mapRect(-250, -2800, 3500, 2200); //roof spawn.mapRect(725, 12, 50, 25); spawn.mapRect(725, 25, 75, 25); spawn.mapRect(750, 38, 75, 25); spawn.mapRect(1525, 25, 75, 50); spawn.mapRect(1500, 38, 50, 25); spawn.mapRect(1550, 12, 50, 25); spawn.mapRect(1600, -1200, 500, 850); //exit roof spawn.mapRect(1600, -400, 50, 225); //exit room left upper wall }, };