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 = { fallMode: "", defaultZoom: 1400, onLevel: -1, levelsCleared: 0, isFlipped: false, uniqueLevels: ["initial", "reservoir", "factory", "interferometer", "reactor", "subway", "final"], //see level.populateLevels: (initial, ... , (reservoir, factory, or interferometer), reactor, ... , subway, final) added later playableLevels: ["labs", "rooftops", "skyscrapers", "warehouse", "highrise", "office", "aerie", "satellite", "sewers", "testChamber", "pavilion", "lock", "towers", "flocculation", "gravitron", "substructure", "corridor"], communityLevels: ["gauntlet", "stronghold", "basement", "crossfire", "vats", "run", "ngon", "house", "perplex", "coliseum", "tunnel", "islands", "temple", "dripp", "biohazard", "stereoMadness", "yingYang", "staircase", "fortress", "commandeer", "clock", "buttonbutton", "downpour", "superNgonBros", "underpass", "cantilever", "tlinat", "ruins", "ace", "crimsonTowers", "LaunchSite", "shipwreck", "unchartedCave", "dojo", "arena", "soft", "flappyGon", "rings", "trial"], trainingLevels: ["walk", "crouch", "jump", "hold", "throw", "throwAt", "deflect", "heal", "fire", "nailGun", "shotGun", "superBall", "matterWave", "missile", "stack", "mine", "grenades", "harpoon"], levels: [], 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.difficultyMode = 1 // simulation.isHorizontalFlipped = true // spawn.setSpawnList(); //picks a couple mobs types for a themed random mob spawns // spawn.setSpawnList(); //picks a couple mobs types for a themed random mob spawns // level.levelsCleared = 10 // level.updateDifficulty() // tech.giveTech("performance") // m.maxHealth = m.health = 100000000 // m.maxEnergy = m.energy = 10000000 // tech.isRerollDamage = true // powerUps.research.changeRerolls(99999) // m.immuneCycle = Infinity //you can't take damage // tech.tech[297].frequency = 100 // tech.addJunkTechToPool(0.5) // m.couplingChange(10) // m.setField("negative mass") //1 standing wave 2 perfect diamagnetism 3 negative mass 4 molecular assembler 5 plasma torch 6 time dilation 7 metamaterial cloaking 8 pilot wave 9 wormhole 10 grappling hook // m.energy = 0 // powerUps.research.count = 3 // tech.isHookWire = true // m.energy = 0 // simulation.molecularMode = 2 // m.damage(0.1); // b.giveGuns("nail gun") //0 nail gun 1 shotgun 2 super balls 3 wave 4 missiles 5 grenades 6 spores 7 drones 8 foam 9 harpoon 10 mine 11 laser // b.giveGuns("harpoon") //0 nail gun 1 shotgun 2 super balls 3 wave 4 missiles 5 grenades 6 spores 7 drones 8 foam 9 harpoon 10 mine 11 laser // b.giveGuns("laser") //0 nail gun 1 shotgun 2 super balls 3 wave 4 missiles 5 grenades 6 spores 7 drones 8 foam 9 harpoon 10 mine 11 laser // tech.laserColor = "#fff" // tech.laserColorAlpha = "rgba(255, 255, 255, 0.5)" // b.guns[8].ammo = 100000000 // requestAnimationFrame(() => { tech.giveTech("non-renewables") }); // tech.giveTech("dark matter") // tech.addJunkTechToPool(0.5) // for (let i = 0; i < 1; ++i) tech.giveTech("demineralization") // for (let i = 0; i < 1; ++i) tech.giveTech("remineralization") // m.skin.egg(); // for (let i = 0; i < 1; ++i) tech.giveTech("many-worlds") // requestAnimationFrame(() => { for (let i = 0; i < 1; i++) tech.giveTech("quasiparticles") }); // requestAnimationFrame(() => { for (let i = 0; i < 1; i++) tech.giveTech("interest") }); // for (let i = 0; i < 1; i++) tech.giveTech("interest") // m.lastKillCycle = m.cycle // for (let i = 0; i < 7; i++) powerUps.directSpawn(450, -50, "tech"); // for (let i = 0; i < 7; i++) powerUps.directSpawn(m.pos.x + 200, m.pos.y - 250, "research", false); // spawn.bodyRect(575, -700, 150, 150); //block mob line of site on testing // level.testing(); level[simulation.isTraining ? "walk" : "initial"]() //normal starting level ************************************************** // for (let i = 0; i < 10; ++i) spawn.starter(1900, -500) // for (let i = 0; i < 1; i++) spawn.mantisBoss(1900, -500) // for (let i = 0; i < 1; ++i) powerUps.directSpawn(m.pos.x + 50 * Math.random(), m.pos.y + 50 * Math.random(), "entanglement"); // for (let i = 0; i < 2; ++i) powerUps.directSpawn(m.pos.x + 450, m.pos.y + 50 * Math.random(), "gun"); // for (let i = 0; i < 100; ++i) powerUps.directSpawn(m.pos.x + 50 * Math.random(), m.pos.y + 50 * Math.random(), "ammo"); // 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 // localSettings.isTrainingNotAttempted = true // simulation.isCheating = false //true; // for (let i = 0; i < 5; i++) tech.giveTech("undefined") // lore.techCount = 1 // level.levelsCleared = 10 // localSettings.loreCount = 1 //this sets what conversation is heard // localSettings.levelsClearedLastGame = 10 // 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); // for (let i = 0; i < 4; i++) localSettings.difficultyCompleted[i] = true // localStorage.setItem("localSettings", JSON.stringify(localSettings)); //update local storage } 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 } } setupCanvas() simulation.setupCamera(player.position); 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.isDarkMatter) spawn.darkMatter() 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 (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() if (simulation.isTraining) { simulation.difficultyMode = 1 } else { simulation.inGameConsole(`level.onLevel = "${level.levels[level.onLevel]}"`); document.title = "n-gon: " + level.levelAnnounce(); } level.setConstraints() }, newLevelOrPhase() { //runs on each new level but also on final boss phases //used for generalist and pigeonhole principle tech.cancelTechCount = 0 tech.tokamakHealCount = 0 tech.buffedGun++ if (tech.buffedGun > b.inventory.length - 1) tech.buffedGun = 0; if ((tech.isGunCycle || tech.isGunChoice) && (b.activeGun !== null && b.activeGun !== undefined) && 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.inGameConsole(`pigeonhole principle: ${(1 + 0.4 * Math.max(0, b.inventory.length)).toFixed(2)}x damage for ${gun}`, 600); // } if (tech.isSwitchReality && level.levelsCleared !== 0) { simulation.inGameConsole(`simulation.amplitude = ${Math.random()}`); m.switchWorlds("many-worlds") simulation.trails() powerUps.spawn(player.position.x + 50, player.position.y - Math.random() * 50, "tech", false); } if (tech.isHealLowHealth) { const len = tech.isEnergyHealth ? 5 * Math.max(0, m.maxEnergy - m.energy) : 5 * Math.max(0, m.maxHealth - m.health) 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); } if (tech.interestRate > 0) { // const rate = ((level[level.levels[level.onLevel]].name === "final" || level[level.levels[level.onLevel]].name === "subway") ? 1 / 3 : 1) * tech.interestRate //this effect triggers extra times on these final levels let rate = tech.interestRate if (level.onLevel < level.levels.length - 1) {//make sure it's not on the lore level which has an undefined name const levelName = level.levels[level.onLevel] if (levelName === "final" || levelName === "subway") rate *= 1 / 3 } let ammoSum = 0 for (let i = 0; i < b.inventory.length; i++) { if (b.guns[b.inventory[i]].ammo !== Infinity) ammoSum += b.guns[b.inventory[i]].ammo / b.guns[b.inventory[i]].ammoPack } if (ammoSum > 0 && b.inventory.length > 0) { const amount = Math.ceil(rate * ammoSum / b.inventory.length) powerUps.spawnDelay("ammo", amount, 4); simulation.inGameConsole(`${(rate * 100).toFixed(0)}% interest on ammo = ${amount > 20 ? amount + powerUps.orb.ammo(1) : powerUps.orb.ammo(amount)}`) } // if (b.activeGun !== null && b.activeGun !== undefined && b.guns[b.activeGun].ammo !== Infinity) { // const ammoPerOrb = b.guns[b.activeGun].ammoPack // const a = Math.ceil(rate * b.guns[b.activeGun].ammo / ammoPerOrb) // powerUps.spawnDelay("ammo", a, 4); // simulation.inGameConsole(`${(rate * 100).toFixed(0)}% interest on ammo = ${a > 20 ? a + powerUps.orb.ammo(1) : powerUps.orb.ammo(a)}`) // } if (powerUps.research.count > 0) { const r = Math.ceil(rate * powerUps.research.count) simulation.inGameConsole(`${(rate * 100).toFixed(0)}% interest on research = ${r > 20 ? r + powerUps.orb.research(1) : powerUps.orb.research(r)}`) powerUps.spawnDelay("research", r, 4); } if (m.coupling > 0) { const c = Math.ceil(rate * m.coupling) powerUps.spawnDelay("coupling", c, 4); simulation.inGameConsole(`${(rate * 100).toFixed(0)}% interest on coupling = ${c > 20 ? c + powerUps.orb.coupling(1) : powerUps.orb.coupling(c)}`) } const healPerOrb = (powerUps.heal.size() / 40 / (simulation.healScale ** 0.25)) ** 2 const h = Math.ceil(rate * m.health / healPerOrb) powerUps.spawnDelay("heal", h, 4); simulation.inGameConsole(`${(rate * 100).toFixed(0)}% interest on health = ${h > 20 ? h + powerUps.orb.heal(1) : powerUps.orb.heal(h)}`) // trying to spawn smaller heals // const healPerOrb = (powerUps.heal.size() / 40 / (simulation.healScale ** 0.25)) ** 2 // console.log(healPerOrb) // let h = tech.interestRate * m.health / healPerOrb // console.log(tech.interestRate, m.health, healPerOrb, h) // const overHeal = h - Math.floor(h) // powerUps.spawn(m.pos.x, m.pos.y, "heal", true, null, Math.max(0.25, overHeal) * 40 * (simulation.healScale ** 0.25)) // if (h > healPerOrb) powerUps.spawnDelay("heal", h); // simulation.inGameConsole(`${(Math.ceil(tech.interestRate * 100)).toFixed(0)}% interest on health = ${h > 20 ? h + powerUps.orb.heal(1) : powerUps.orb.heal(h)}`) } if (tech.isEjectOld) { let index = null //find oldest tech that you have for (let i = 0; i < tech.tech.length; i++) { if (tech.tech[i].count > 0 && !tech.tech[i].isInstant) { index = i } } if (index) { //eject it const effect = Math.pow(1.1, tech.tech[index].count) simulation.inGameConsole(`${(effect).toFixed(2)}x damage //from obsolescence`, 360) tech.damage *= effect powerUps.ejectTech(index) } } }, trainingText(say) { simulation.lastLogTime = 0; //clear previous messages simulation.isTextLogOpen = true simulation.inGameConsole(`supervised.learning(${(Date.now() / 1000).toFixed(0)} s):
${say}
`, Infinity) simulation.isTextLogOpen = false }, trainingBackgroundColor: "#e1e1e1", custom() { }, customTopLayer() { }, updateDifficulty() { simulation.difficulty = level.levelsCleared * simulation.difficultyMode if (simulation.isTraining) { simulation.difficulty = 1 simulation.difficultyMode = 1 m.dmgScale = 1 simulation.dmgScale = 1//Math.max(0.1, 0.25 * level.levelsCleared * scale) //damage done by mobs scales with total levels //a bigger number means the player takes more damage } else { const scale = simulation.difficultyMode > 3 ? 2 : 1 m.dmgScale = Math.pow(0.85, level.levelsCleared * scale) simulation.dmgScale = Math.max(0.1, 0.25 * level.levelsCleared * scale) //damage done by mobs scales with total levels //a bigger number means the player takes more damage if (simulation.difficultyMode > 5) { m.dmgScale *= 0.5 simulation.dmgScale *= 2 } } simulation.healScale = 1 / (1 + simulation.difficulty * 0.043) //a higher denominator makes for lower heals // m.health += heal * simulation.healScale; if (simulation.difficultyMode === 1) { simulation.accelScale = 1.1 simulation.CDScale = 0.9 } else { simulation.accelScale = Math.min(6, Math.pow(1.024, simulation.difficulty)) simulation.CDScale = Math.max(0.15, Math.pow(0.964, simulation.difficulty)) } }, constraintIndex: 0, constraintPopUp() { //pause if (!simulation.paused) { simulation.paused = true; simulation.isChoosing = true; //stops p from un pausing on key down document.body.style.cursor = "auto"; document.getElementById("choose-grid").style.pointerEvents = "auto"; document.getElementById("choose-grid").style.transitionDuration = "0s"; } //build level info document.getElementById("choose-grid").classList.add('choose-grid-no-images') document.getElementById("choose-grid").classList.remove('choose-grid') document.getElementById("choose-grid").style.gridTemplateColumns = "auto"//"450px" let text = `
${level.constraintDescription1}
` if (level.constraintDescription2) text += `
${level.constraintDescription2}
` text += `
continue
` document.getElementById("choose-grid").innerHTML = text //show level info document.getElementById("choose-grid").style.opacity = "1" document.getElementById("choose-grid").style.transitionDuration = "0.25s"; //how long is the fade in on document.getElementById("choose-grid").style.visibility = "visible" document.getElementById("choose-unPause").addEventListener("click", () => { level.unPause() document.body.style.cursor = "none"; //reset hide image style if (localSettings.isHideImages) { document.getElementById("choose-grid").classList.add('choose-grid-no-images'); document.getElementById("choose-grid").classList.remove('choose-grid'); } else { document.getElementById("choose-grid").classList.add('choose-grid'); document.getElementById("choose-grid").classList.remove('choose-grid-no-images'); } }); requestAnimationFrame(() => { ctx.fillStyle = `rgba(150,150,150,0.9)`; //`rgba(221,221,221,0.6)`; ctx.fillRect(0, 0, canvas.width, canvas.height); }); }, setConstraints() { //populate array with possible constraints and reset constraints level.constraintDescription1 = level.constraintDescription2 = "" const possible = [] for (let i = 0; i < level.constraint.length; i++) { level.constraint[i].remove() possible.push(i) } if (level.levels[level.onLevel] !== "final" && level.levels[level.onLevel] !== "null" && level.levels[level.onLevel] !== "initial" && !simulation.isTraining && m.alive && level.levelsCleared) { if (simulation.difficultyMode > 4 && possible.length) { //choose a random constraint from possible array and remove it from that array // level.constraintIndex = 0 //REMOVE THIS FROM LIVE GAME level.constraint[level.constraintIndex].effect() possible.splice(level.constraintIndex, 1) //generate text to describe the active constraints for the pause menu level.constraintDescription1 = level.constraint[level.constraintIndex].description level.constraintIndex++ if (level.constraintIndex > level.constraint.length - 1) level.constraintIndex = 0 if (simulation.difficultyMode > 6 && possible.length) { level.constraint[level.constraintIndex].effect() possible.splice(level.constraintIndex, 1) level.constraintDescription2 += level.constraint[level.constraintIndex].description level.constraintIndex++ if (level.constraintIndex > level.constraint.length - 1) level.constraintIndex = 0 } document.getElementById("right-HUD-constraint").style.display = "block"; // level.constraintPopUp() //animate making constraint HUD bigger then smaller if (!localSettings.isHideHUD) { requestAnimationFrame(() => { //grow and get bright document.getElementById("right-HUD-constraint").style.opacity = 1 document.getElementById("right-HUD-constraint").style.fontSize = "23px" document.getElementById("right-HUD-constraint").style.top = simulation.difficultyMode > 6 ? "6px" : "9px" setTimeout(() => { if (m.alive) { //fade to background document.getElementById("right-HUD-constraint").style.opacity = 0.35 document.getElementById("right-HUD-constraint").style.fontSize = "20px" document.getElementById("right-HUD-constraint").style.top = "12px" } }, 5000); }); } } else { document.getElementById("right-HUD-constraint").style.display = "none"; } } else { document.getElementById("right-HUD-constraint").style.display = "none"; } //update HUD with constraints let text = `${level.constraintDescription1}` if (level.constraintDescription1) simulation.inGameConsole(`level.constraint.description: "${level.constraintDescription1}"`) if (simulation.difficultyMode > 6 && level.constraintDescription2) { text += `
${level.constraintDescription2}` if (level.constraintDescription2) simulation.inGameConsole(`level.constraint.description: "${level.constraintDescription2}"`) } document.getElementById("right-HUD-constraint").innerHTML = text if (level.constraintDescription1) { if (level.constraintDescription2) { document.getElementById("right-HUD").style.top = "80px"; } else { document.getElementById("right-HUD").style.top = "57px"; //make room for tech list in "right-HUD" } } else { document.getElementById("right-HUD").style.top = "15px"; } }, constraintDescription1: "", //used in pause menu and console constraintDescription2: "", constraint: [ { description: "0.5x healing", effect() { level.isLowHeal = true }, remove() { level.isLowHeal = false } }, { description: "no pause while choosing", effect() { level.isNoPause = true }, remove() { level.isNoPause = false } }, { description: "no health bars", effect() { mobs.healthBar = () => { } level.isHideHealth = true document.getElementById("health").style.display = "none" document.getElementById("health-bg").style.display = "none" }, remove() { mobs.healthBar = mobs.defaultHealthBar level.isHideHealth = false if (tech.isEnergyHealth) { document.getElementById("health").style.display = "none" document.getElementById("health-bg").style.display = "none" } else if (!level.isHideHealth) { document.getElementById("health").style.display = "inline" document.getElementById("health-bg").style.display = "inline" } } }, { description: "0.5x energy regen", effect() { level.isReducedRegen = 0.5 }, remove() { level.isReducedRegen = 1 } }, { description: "0.5x max health", effect() { level.isReducedHealth = true m.setMaxHealth() }, remove() { if (level.isReducedHealth) { level.isReducedHealth = false m.setMaxHealth() m.addHealth(level.reducedHealthLost); level.reducedHealthLost = 0 } else { level.isReducedHealth = false } } }, { description: "after 40 seconds spawn WIMPs", effect() { simulation.ephemera.push({ name: "WIMPS", time: 0, levelName: level.levels[level.onLevel], do() { this.time++ if (level.levels[level.onLevel] === this.levelName) { if (this.time > 2400 && !(this.time % 420)) spawn.WIMP(level.enter.x, level.enter.y) } else { simulation.removeEphemera(this.name); } }, }) }, remove() { } }, { description: "0.3x damage after using power ups", effect() { level.isNoDamage = true level.noDamageCycle = 0 }, remove() { level.isNoDamage = false level.noDamageCycle = 0 } }, { description: "mobs heal after you take damage", effect() { level.isMobHealPlayerDamage = true }, remove() { level.isMobHealPlayerDamage = false } }, { description: "mob death heals nearby mobs", effect() { level.isMobDeathHeal = true }, remove() { level.isMobDeathHeal = false } }, // { // description: "full damage taken after boss dies", // // description: "after boss dies damage taken = 1", // effect() { // level.noDefenseSetting = 1 //defense goes to zero once equal to 2 // }, // remove() { // level.noDefenseSetting = 0 // } // }, { description: "4x shielded mobs", effect() { level.isMobShields = true }, remove() { level.isMobShields = false } }, { description: "40% JUNK chance", effect() { level.junkAdded = 0.4 }, remove() { level.junkAdded = 0 } }, { description: "-1 choice", effect() { level.fewerChoices = true }, remove() { level.fewerChoices = false } }, { description: "power ups in stasis", effect() { level.isNextLevelPowerUps = true //remove all current power ups for (let i = powerUp.length - 1; i > -1; i--) { powerUps.powerUpStorage.push({ name: powerUp[i].name, size: powerUp[i].size }) Matter.Composite.remove(engine.world, powerUp[i]); powerUp.splice(i, 1) } }, remove() { level.isNextLevelPowerUps = false if (powerUps.powerUpStorage.length) { const delay = 5 let i = 0 let cycle = () => { if (powerUps.powerUpStorage.length && m.alive && powerUp.length < 300) { requestAnimationFrame(cycle); if (!simulation.paused && !simulation.isChoosing) { if (!(simulation.cycle % delay)) { const where = { x: m.pos.x + 70 * (Math.random() - 0.5), y: m.pos.y + 70 * (Math.random() - 0.5) } powerUps.directSpawn(where.x, where.y, powerUps.powerUpStorage[i].name, true, powerUps.powerUpStorage[i].size); powerUps.powerUpStorage.splice(i, 1); } } } else { powerUps.powerUpStorage = [] } } requestAnimationFrame(cycle); } } }, { description: "33% of mobs respawn", effect() { level.isMobRespawn = true }, remove() { level.isMobRespawn = false } }, { description: "0 duplication", effect() { level.isNoDuplicate = true }, remove() { level.isNoDuplicate = false } }, { description: "2x ammo cost", effect() { level.is2xAmmo = true }, remove() { level.is2xAmmo = false } }, { description: "0.5x max energy", effect() { level.isReducedEnergy = true m.setMaxEnergy() }, remove() { if (level.isReducedEnergy) { level.isReducedEnergy = false m.setMaxEnergy() } else { level.isReducedEnergy = false } } }, { description: "slow bots", effect() { level.isSlowBots = true b.clearPermanentBots(); b.respawnBots(); }, remove() { if (level.isSlowBots) { level.isSlowBots = false b.clearPermanentBots(); b.respawnBots(); } else { level.isSlowBots = false } } }, ], isMobShields: false, junkAdded: 0, isNextLevelPowerUps: false, isMobRespawn: false, fewerChoices: false, isNoDuplicate: false, is2xAmmo: false, isReducedEnergy: false, isSlowBots: false, // noDefenseSetting: 0, isMobDeathHeal: false, isMobHealPlayerDamage: false, isNoDamage: false, noDamageCycle: 0, reducedHealthLost: 0, isReducedHealth: false, isReducedRegen: 1, isHideHealth: false, isNoPause: false, isLowHeal: false, levelAnnounce() { const cheating = simulation.isCheating ? "(testing)" : "" if (level.levelsCleared === 0) { return `initial ${cheating}`; } else { return `${level.levelsCleared} ${level.levels[level.onLevel]} ${cheating}` } }, announceMobTypes() { simulation.inGameConsole(`spawn.${spawn.pickList[0]}(x,y)`) simulation.inGameConsole(`spawn.${spawn.pickList[1]}(x,y)`) }, disableExit: false, nextLevel() { if (!level.disableExit) { level.levelsCleared++; level.onLevel++; //cycles map to next level level.updateDifficulty() if (simulation.isTraining) { if (level.onLevel > level.levels.length - 1) { //if all training levels are completed 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 { if (level.onLevel > level.levels.length - 1) level.onLevel = 0; } //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 //pop up new level info screen for a few seconds //|| level.levels[level.onLevel] === "subway" if (!localSettings.isHideHUD && m.alive && (level.levels[level.onLevel] === "final" || level.levels[level.onLevel] === "reactor")) { // if (!localSettings.isHideHUD && m.alive) { //pause if (!simulation.paused) { simulation.paused = true; simulation.isChoosing = true; //stops p from un pausing on key down } //build level info document.getElementById("choose-grid").style.gridTemplateColumns = "250px" //onclick="level.unPause()" // if (level.levels[level.onLevel] === "final") { //|| level.levels[level.onLevel] === "reactor" let text = `
` for (let i = 0; i < level.levels.length; i++) { if (i < level.levelsCleared) { text += `
${level.levels[i]}
` } else if (i === level.levelsCleared) { // text += `
${level.levels[i]}
` text += `
${level.levels[i]}
` } else { text += `
${level.levels[i]}
` //blurry text // ???????? text // text += `
` // for (let j = 0; j < level.levels[i].length; j++) text += `?` // text += `
` } } text += `
` document.getElementById("choose-grid").innerHTML = text //show level info document.getElementById("choose-grid").style.opacity = "1" document.getElementById("choose-grid").style.transitionDuration = "0.25s"; //how long is the fade in on document.getElementById("choose-grid").style.visibility = "visible" // } simulation.draw.cons(); simulation.draw.body(); level.customTopLayer(); let count = countMax = simulation.testing ? 0 : 240 let newLevelDraw = () => { count-- if (count > 0) { requestAnimationFrame(newLevelDraw); } else { //unpause // document.body.style.cursor = "none"; if (m.immuneCycle < m.cycle + 15) m.immuneCycle = m.cycle + 30; //player is immune to damage for 30 cycles if (simulation.paused) requestAnimationFrame(cycle); if (m.alive) simulation.paused = false; simulation.isChoosing = false; //stops p from un pausing on key down build.unPauseGrid() document.getElementById("choose-grid").style.opacity = "0" setTimeout(() => { document.getElementById("choose-grid").style.visibility = "hidden" }, 1000); } //draw simulation.wipe(); m.look(); simulation.camera(); const scale = 15 ctx.setLineDash([scale * (countMax - count), scale * count]); simulation.draw.wireFrame(); ctx.setLineDash([]); ctx.restore(); simulation.drawCursor(); } requestAnimationFrame(newLevelDraw); } } }, unPause() { if (m.immuneCycle < m.cycle + 15) m.immuneCycle = m.cycle + 30; //player is immune to damage for 30 cycles if (simulation.paused) requestAnimationFrame(cycle); if (m.alive) simulation.paused = false; simulation.isChoosing = false; //stops p from un pausing on key down build.unPauseGrid() document.getElementById("choose-grid").style.opacity = "0" document.getElementById("choose-grid").style.visibility = "hidden" // setTimeout(() => { // }, 1000); }, 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.inGameConsole(`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.inGameConsole(`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 if (simulation.isCommunityMaps) level.trainingLevels.push("diamagnetism") } 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 pick = ["interferometer", "factory", "reservoir"] level.levels.splice(Math.floor(Math.seededRandom(level.levels.length * 0.6, level.levels.length)), 0, pick[Math.floor(Math.random() * pick.length)]); //add level to the back half of the randomized levels list level.levels.splice(Math.floor(Math.seededRandom(level.levels.length * 0.6, level.levels.length)), 0, "reactor"); //add level to the back half of the randomized levels list if (!build.isExperimentSelection || (build.hasExperimentalMode && !simulation.isCheating)) { //experimental mode is endless, unless you only have an experiment Tech level.levels.unshift("initial"); //add level to the start of the randomized levels list 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 }, flipVertical() { const flipY = (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 - player.position.y }) } } flipY(map) flipY(body) flipY(mob) flipY(powerUp) Matter.Body.setPosition(player, { x: player.position.x, y: -2 * player.position.y }) // 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, 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 += 3 } else if (level.exitCount > 0) { level.exitCount -= 3 } 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 //prompt an option to do the training levels or continue to the normal game if (!simulation.isChoosing && m.alive && !simulation.isTraining && !simulation.isCheating && b.inventory.length === 0 && level.levelsCleared === 0 && localSettings.isTrainingNotAttempted) { //pause if (!simulation.paused) { simulation.paused = true; simulation.isChoosing = true; //stops p from un pausing on key down document.body.style.cursor = "auto"; document.getElementById("choose-grid").style.pointerEvents = "auto"; document.getElementById("choose-grid").style.transitionDuration = "0s"; } //build level info document.getElementById("choose-grid").classList.add('choose-grid-no-images'); document.getElementById("choose-grid").classList.remove('choose-grid'); document.getElementById("choose-grid").style.gridTemplateColumns = "350px" let text = `

training

Begin the guided tutorial that shows you how to use your ${powerUps.orb.field()} and ${powerUps.orb.gun()}.

play

Begin the standard game where you progress through 13 random levels and beat the final boss.
` document.getElementById("choose-grid").innerHTML = text //show level info document.getElementById("choose-grid").style.opacity = "1" document.getElementById("choose-grid").style.transitionDuration = "0.25s"; //how long is the fade in on document.getElementById("choose-grid").style.visibility = "visible" document.getElementById("choose-training").addEventListener("click", () => { level.unPause() document.body.style.cursor = "none"; simulation.isTraining = true level.levelsCleared--; level.onLevel-- simulation.isHorizontalFlipped = false level.levels = level.trainingLevels.slice(0) //copy array, not by just by assignment level.nextLevel() //reset hide image style if (localSettings.isHideImages) { document.getElementById("choose-grid").classList.add('choose-grid-no-images'); document.getElementById("choose-grid").classList.remove('choose-grid'); } else { document.getElementById("choose-grid").classList.add('choose-grid'); document.getElementById("choose-grid").classList.remove('choose-grid-no-images'); } }); document.getElementById("choose-unPause").addEventListener("click", () => { level.unPause() document.body.style.cursor = "none"; level.nextLevel() //reset hide image style if (localSettings.isHideImages) { document.getElementById("choose-grid").classList.add('choose-grid-no-images'); document.getElementById("choose-grid").classList.remove('choose-grid'); } else { document.getElementById("choose-grid").classList.add('choose-grid'); document.getElementById("choose-grid").classList.remove('choose-grid-no-images'); } }); requestAnimationFrame(() => { ctx.fillStyle = `rgba(150,150,150,0.9)`; //`rgba(221,221,221,0.6)`; ctx.fillRect(0, 0, canvas.width, canvas.height); }); } else { //advance to next level 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, { isRotor: true, 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.isTimeDilated) { 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.isTimeDilated) { // 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, speed = 1000, angle = Math.PI / 2) { //height is how high the player will be flung above y if (angle !== Math.PI / 2) { //angle !== 3 * Math.PI / 2 angle *= -1 who = map[map.length] = Matter.Bodies.fromVertices(x + 50, y + 35, Vertices.fromPath("80 40 -80 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 }, query() { // check for collisions const rayVector = Vector.add(this.position, Vector.rotate({ x: 100, y: 0 }, angle)) query = (who) => { const list = Matter.Query.ray(who, this.position, rayVector, 100) if (list.length > 0) { Matter.Body.setVelocity(list[0].bodyA, Vector.rotate({ x: 1.21 * Math.sqrt(Math.abs(speed)), y: 0 }, angle)); } } query(body) query(mob) query(bullet) query(powerUp) //player collision const list = Matter.Query.ray([player], this.position, rayVector, 100) if (list.length > 0) { Matter.Body.setVelocity(player, Vector.rotate({ x: 1.21 * Math.sqrt(Math.abs(speed)), y: 0 }, angle)); m.buttonCD_jump = 0; // reset short jump counter to prevent short jumps on boosts m.hardLandCD = 0 // disable hard landing } //draw const v1 = this.vertices[0] const v2 = this.vertices[1] let unit = Vector.rotate({ x: 60, y: 0 }, angle) let v3 = Vector.add(v2, unit) let v4 = Vector.add(v1, unit) ctx.beginPath(); ctx.moveTo(v1.x, v1.y) ctx.lineTo(v2.x, v2.y) ctx.lineTo(v3.x, v3.y) ctx.lineTo(v4.x, v4.y) ctx.fillStyle = "rgba(200,0,255,0.05)"; ctx.fill() unit = Vector.rotate({ x: 20, y: 0 }, angle) v3 = Vector.add(v2, unit) v4 = Vector.add(v1, unit) ctx.beginPath(); ctx.moveTo(v1.x, v1.y) ctx.lineTo(v2.x, v2.y) ctx.lineTo(v3.x, v3.y) ctx.lineTo(v4.x, v4.y) ctx.fillStyle = "rgba(200,0,255,0.15)"; ctx.fill() }, }); Matter.Body.rotate(who, angle + Math.PI / 2); return who } else { 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 } }, 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: -1.21 * Math.sqrt(Math.abs(speed)) //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: -1.21 * Math.sqrt(Math.abs(speed)) //give an 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); }, }); 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.player | cat.body | cat.bullet | cat.mob | cat.mobBullet //| cat.powerUp }, inertia: Infinity, //prevents rotation isNotHoldable: true, friction: 1, frictionStatic: 1, restitution: 0, frictionAir: 0.001, holdX: x, move() { if (!m.isTimeDilated) { 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 }); }, moveOnTouch() { if (!m.isTimeDilated) { 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 //slow down early to avoid a jerky stop that can pass through blocks Matter.Body.setVelocity(this, { x: 0, y: this.velocity.y * 0.7 }); //switch to up mode // if (this.position.y + this.velocity.y > y) { // this.isUp = true // this.frictionAir = friction.up // } } Matter.Body.setVelocity(this, { x: 0, y: this.velocity.y }); } //draw line to show how far to will extend ctx.beginPath(); ctx.moveTo(x, y + height / 2); ctx.lineTo(x, maxHeight - height / 2); ctx.strokeStyle = `rgba(0,0,0,0.2)` ctx.lineWidth = "2" ctx.stroke(); //draw body ctx.beginPath(); ctx.moveTo(this.vertices[0].x, this.vertices[0].y); for (let j = 1; j < this.vertices.length; j++) { ctx.lineTo(this.vertices[j].x, this.vertices[j].y); } ctx.lineTo(this.vertices[0].x, this.vertices[0].y); ctx.lineWidth = "2" ctx.strokeStyle = `#333` ctx.fillStyle = `rgba(200,200,200,1)` //edge limits if (this.position.y < maxHeight) { Matter.Body.setPosition(this, { x: this.holdX, y: maxHeight }); } else if (this.position.y > y) { ctx.fillStyle = `rgba(255,255,255,${0.5 + 0.15 * Math.random()})` Matter.Body.setPosition(this, { x: this.holdX, y: y }); //undoing force of gravity this.force.y -= this.mass * simulation.g; if (Matter.Query.collides(this, [player]).length) { this.isUp = true this.frictionAir = friction.up } } ctx.fill(); ctx.stroke(); // 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.isTimeDilated) { // 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, isInvertedVertical = false, color = "hsl(0, 100%, 70%)") { if (isSpawnBase) { if (isInvertedVertical) { spawn.mapVertex(x + 65, y - 3, "100 -10 -100 -10 -70 10 70 10"); } else { 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 // }); if (isInvertedVertical) { return { isUp: false, min: { x: x + 2, y: y - 1 }, max: { x: x + width, y: y }, 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; } }, queryRemove() { if (Matter.Query.region(body, this).length === 0 && Matter.Query.region([player], this).length === 0) { this.isUp = true; } else { if (this.isUp === true) { const list = Matter.Query.region(body, this) //are any blocks colliding with this if (list.length > 0) { Matter.Composite.remove(engine.world, list[0]); for (let i = 0; i < body.length; i++) { if (body[i] === list[0]) { body.splice(i, 1); break } } Matter.Body.setVelocity(list[0], { x: 0, y: 0 }); } } this.isUp = false; } }, queryPlayer() { if (Matter.Query.region([player], this).length === 0) { this.isUp = true; } else { this.isUp = false; } }, draw() { ctx.fillStyle = color if (this.isUp) { ctx.fillRect(this.min.x, this.min.y, this.width, 20) } else { ctx.fillRect(this.min.x, this.min.y - 12, this.width, 25) } } } } else { 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; } }, queryRemove() { if (Matter.Query.region(body, this).length === 0 && Matter.Query.region([player], this).length === 0) { this.isUp = true; } else { if (this.isUp === true) { const list = Matter.Query.region(body, this) //are any blocks colliding with this if (list.length > 0) { //delete triggering block Matter.Composite.remove(engine.world, list[0]); for (let i = 0; i < body.length; i++) { if (body[i] === list[0]) { body.splice(i, 1); break } } Matter.Body.setVelocity(list[0], { x: 0, y: 0 }); } } this.isUp = false; } }, queryPlayer() { if (Matter.Query.region([player], this).length === 0) { this.isUp = true; } else { this.isUp = false; } }, draw() { ctx.fillStyle = color 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: 400 }) { 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.isTimeDilated) { 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.isTimeDilated) { 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.isTimeDilated) { 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.isTimeDilated) { 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); simulation.translatePlayerAndCamera(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); simulation.translatePlayerAndCamera(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], Vector.sub(bullet[i].position, change)); // // 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) })); } } } } 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 up mag = Math.max(10, Math.min(50, body[i].velocity.y * 0.8)) + 11 let v = Vector.mult(this.portalPair.unit, mag) //rotate the velocity vector of blocks fired directly up to keep them from getting stuck endlessly in vertical portals Matter.Body.setVelocity(body[i], Vector.rotate(v, 0.5 * (Math.random() - 0.5))); } 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.isTimeDilated) { 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(); } } } }, laser(p1, p2, damage = 0.14, color = "#f00") { return { isOn: true, position: p1, look: p2, color: color, query() { if (!m.isTimeDilated) { let best = { x: null, y: null, dist2: Infinity, who: null, v1: null, v2: null } best = vertexCollision(this.position, this.look, m.isCloak ? [map, body] : [map, body, [playerBody, playerHead]]); // hitting player if ((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 const dmg = damage * simulation.dmgScale; m.damage(dmg); simulation.drawList.push({ //add dmg to draw queue x: best.x, y: best.y, radius: dmg * 1500, color: "rgba(255,0,0,0.5)", time: 20 }); } //draw if (best.dist2 === Infinity) best = this.look; ctx.beginPath(); ctx.moveTo(this.position.x, this.position.y); ctx.lineTo(best.x, best.y); ctx.strokeStyle = this.color; ctx.lineWidth = 5; ctx.setLineDash([50 + 200 * Math.random(), 50 * Math.random()]); ctx.stroke(); ctx.setLineDash([]); } }, countDown: 0, countTotal: 480, countDelay: 440, motionQuery() { if (!m.isTimeDilated) { let best = { x: null, y: null, dist2: Infinity, who: null, v1: null, v2: null } best = vertexCollision(this.position, this.look, m.isCloak ? [map, body] : [map, body, [playerBody, playerHead]]); if (this.countDown === 0) { if ((best.who === playerBody || best.who === playerHead)) this.countDown = this.countTotal // hitting player ctx.strokeStyle = `rgba(255,255,255,0.4)`; ctx.lineWidth = 8 + 3 * Math.sin(simulation.cycle * 0.3); } else if (this.countDown > this.countDelay) { ctx.strokeStyle = `rgba(255,255,255,0.8)`; ctx.lineWidth = 11; this.countDown-- } else { this.countDown-- if ((best.who === playerBody || best.who === playerHead) && m.immuneCycle < m.cycle) { // hitting player m.immuneCycle = m.cycle + m.collisionImmuneCycles + 60; //player is immune to damage for an extra second const dmg = damage * simulation.dmgScale; m.damage(dmg); simulation.drawList.push({ //add dmg to draw queue x: best.x, y: best.y, radius: dmg * 1500, color: "rgba(255,0,0,0.5)", time: 20 }); } ctx.strokeStyle = this.color; ctx.lineWidth = 5; ctx.setLineDash([50 + 200 * Math.random(), 50 * Math.random()]); } //draw if (best.dist2 === Infinity) best = this.look; ctx.beginPath(); ctx.moveTo(this.position.x, this.position.y); ctx.lineTo(best.x, best.y); ctx.stroke(); ctx.setLineDash([]); } }, } }, fizzler(p1, p2) { return { isOn: true, position: p1, look: p2, color: color, query() { if (!m.isTimeDilated) { // let best = { x: null, y: null, dist2: Infinity, who: null, v1: null, v2: null } // best = vertexCollision(this.position, this.look, [body]); const hits = Matter.Query.ray(body, this.position, this.look, 25) for (let i = hits.length - 1; i > -1; i--) { // console.log(what) const what = hits[i].bodyA simulation.drawList.push({ x: what.position.x, y: what.position.y, radius: 11, color: "rgba(0,160,255,0.7)", time: 10 }); if (what === m.holdingTarget) m.drop() for (let i = 0; i < body.length; i++) { if (body[i] === what) { body.splice(i, 1); break } } Matter.Composite.remove(engine.world, what); } //draw ctx.beginPath(); ctx.moveTo(this.position.x, this.position.y); ctx.lineTo(this.look.x, this.look.y); // ctx.strokeStyle = "rgba(50,200,255,0.3)"; // ctx.lineWidth = 2 + 5 * Math.random() // ctx.stroke(); ctx.strokeStyle = "rgba(50,160,255,0.17)"; ctx.lineWidth = 35 + 25 * Math.random() * Math.random(); ctx.stroke(); //draw random dots in the path if (Math.random() < 0.05) { const r = Math.random(); const where = { x: this.position.x + r * (this.look.x - this.position.x) + 60 * (Math.random() - 0.5), y: this.position.y + r * (this.look.y - this.position.y) + 60 * (Math.random() - 0.5) }; simulation.drawList.push({ x: where.x, y: where.y, radius: 6, color: "rgba(0,160,255,0.7)", time: 5 }); } } }, } }, 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.2 : 1) if (m.energy > DRAIN) { m.energy -= DRAIN if (tech.isEnergyHealth && m.energy < 0) m.death() } else { m.damage(damage * (tech.isRadioactiveResistance ? 0.2 : 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.isTimeDilated) { this.height += growRate this.min.y -= growRate this.max.y = this.min.y + this.height } }, levelFall(fallRate = 1) { if (this.height > 0 && !m.isTimeDilated) { this.height -= fallRate this.min.y += fallRate this.max.y = this.min.y + this.height } }, level(isFill, growSpeed = 1) { if (!m.isTimeDilated) { 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.isTimeDilated) { 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]); }, drawFast() { 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([60, 60]); ctx.lineDashOffset = (-simulation.cycle * this.VxGoal) % 120; 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.isTimeDilated) { 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() { // level.announceMobTypes() 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.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); //??? // 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) { 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 }, initialPowerUps() { //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 + 20 * (Math.random() - 0.5), -2170, "field", false); } else { powerUps.spawnStartingPowerUps(2095 + 20 * (Math.random() - 0.5), -2200); } if (simulation.difficultyMode === 1) { powerUps.spawn(2095 + 20 * (Math.random() - 0.5), -2600, "ammo", false); powerUps.spawn(2095 + 20 * (Math.random() - 0.5), -2550, "ammo", false); powerUps.spawn(2095 + 20 * (Math.random() - 0.5), -2400, "heal", false); powerUps.spawn(2095 + 20 * (Math.random() - 0.5), -2350, "heal", false); powerUps.spawn(2095 + 20 * (Math.random() - 0.5), -2350, "heal", false); powerUps.spawn(2095 + 20 * (Math.random() - 0.5), -2100, "research", false); powerUps.spawn(2095 + 20 * (Math.random() - 0.5), -2060, "research", false); powerUps.spawn(2095 + 20 * (Math.random() - 0.5), -2120, "research", false); powerUps.spawn(2095 + 20 * (Math.random() - 0.5), -2075, "research", false); } else if (simulation.difficultyMode > 4) { } else { powerUps.spawn(2095 + 20 * (Math.random() - 0.5), -2300, "heal", false); powerUps.spawn(2095 + 20 * (Math.random() - 0.5), -2100, "heal", false); powerUps.spawn(2095 + 20 * (Math.random() - 0.5), -2060, "research", false); } //spin the power ups to prevent them from stacking awkwardly for (let i = 0; i < powerUp.length; i++) { Matter.Body.setAngularVelocity(powerUp[i], 5 * (Math.random() - 0.5)) } } else { requestAnimationFrame(cycle); } } requestAnimationFrame(cycle); }, initial() { if (level.levelsCleared === 0) { //if this is the 1st level of the game if (simulation.difficultyMode > 2) spawn.setSpawnList() // don't begin with starter mobs level.initialPowerUps() if (level.levelsCleared === 0) powerUps.directSpawn(-60, -950, "difficulty", false); if (!simulation.isCheating && !m.isShipMode && !build.isExperimentRun) { spawn.wireFoot(); spawn.wireFootLeft(); spawn.wireKnee(); spawn.wireKneeLeft(); spawn.wireHead(); } else { simulation.isCheating = true; } if (localSettings.levelsClearedLastGame < 3) { } else if (!build.isExperimentRun) { simulation.trails(70) //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.inGameConsole(`for (let i = 0; i < localSettings.levelsClearedLastGame / 3; i++)`); simulation.inGameConsole(`{ 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] === "initial") 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) let isSpawnedWarp = false level.custom = () => { if (!isSpawnedWarp && simulation.testing) { isSpawnedWarp = true powerUps.directSpawn(m.pos.x, -900, "warp") // powerUps.directSpawn(2100, -1200, "warp") } //working on a message using text // ctx.font = "50px Arial"; // ctx.fillStyle = "rgba(0,0,0,0.3)" // for (let i = 0; i < 5; i++) { // const wiggle = 2 // ctx.fillText("move", 500 + wiggle * Math.random(), -500 + wiggle * Math.random()); // ctx.fillText("move", 500, -400); // } //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].name === "instructions") { if (simulation.isCheating) { Matter.Composite.remove(engine.world, powerUp[i]); powerUp.splice(i, 1); break } } else 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() //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.fillRect(2030, 0, 150, 1800); ctx.fillStyle = "rgba(68, 68, 68,0.98)" // ctx.fillRect(-2750, -300, 2600, 125); ctx.fillRect(-2925, -2800, 2775, 2650); }; 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(-2750, -2800, 2600, 2515); spawn.mapRect(-3275, -185, 3125, 1985); requestAnimationFrame(() => { powerUps.directSpawn(-2315, -3050, "instructions", false); }); spawn.mapRect(-3275, -2800, 400, 3250); spawn.mapRect(-2775, -575, 50, 25); spawn.mapRect(-2775, -950, 50, 25); spawn.mapRect(-2775, -1325, 50, 25); spawn.mapRect(-2775, -1700, 50, 25); spawn.mapRect(-2775, -2075, 50, 25); spawn.mapRect(-2775, -2450, 50, 25); spawn.mapRect(3000, -2800, 2600, 4600); //right wall // spawn.mapRect(-250, 0, 3600, 1800); //ground spawn.mapRect(-250, 0, 2300, 1800); //ground // Matter.Body.setVelocity(map[map.length - 1], { x: 10, y: -10 }); spawn.mapRect(2150, 0, 1200, 1800); //ground 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, 130); //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"); if (simulation.difficultyMode > 5) for (let i = 0; i < 8; i++) powerUps.spawn(4600 + 40 * i, -30, "ammo"); //extra ammo on why difficulty 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.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 // 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 = 10; 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 = []; removeAll(composite); composite = [] //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 && !mob[i].isDarkMatter) { //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) { simulation.inGameConsole(`station gate opened`, 360); 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.inGameConsole(`level exit opened`, 360); isExitOpen = true; } } } // gateButton.draw(); if (gateButton.isUp) { //aura around button ctx.beginPath(); ctx.ellipse(gateButton.min.x + gateButton.width * 0.5, gateButton.min.y + 6, 0.75 * gateButton.width, 0.5 * gateButton.width, 0, Math.PI, 0); //ellipse(x, y, radiusX, radiusY, rotation, startAngle, endAngle, counterclockwise) ctx.fillStyle = `hsla(345, 100%, 80%,${0.1 + 0.4 * Math.random()})` ctx.fill(); ctx.fillStyle = "hsl(345, 100%, 75%)" ctx.fillRect(gateButton.min.x, gateButton.min.y - 10, gateButton.width, 25) ctx.strokeStyle = "#000"//"rgba(255,255,255,0.2)" ctx.lineWidth = 2 ctx.strokeRect(gateButton.min.x, gateButton.min.y - 10, gateButton.width, 25) } else { ctx.fillStyle = "hsl(345, 100%, 75%)" ctx.fillRect(gateButton.min.x, gateButton.min.y, gateButton.width, 10) ctx.strokeStyle = "#000"//"rgba(255,255,255,0.2)" ctx.lineWidth = 2 ctx.strokeRect(gateButton.min.x, gateButton.min.y, gateButton.width, 10) } } } const stations = [ //update totalNumberOfStations as you add more stations () => { //empty starting station if (isExitOpen) { level.exit.x = x - 50; level.exit.y = -260; if (simulation.difficultyMode < 7) powerUps.spawn(level.exit.x, level.exit.y - 100, "tech"); } 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, 1000); //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, -700, 450, 600); //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: -2080 },] 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 + 278, y: -1814 }, { x: x + 778, y: -1814 }, { x: x + 2025, y: -1995 }, { x: x - 2025, y: -2425 }, { 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); } }, () => { //angled jumps const buttonsCoords = [{ x: x + 50, y: -1395 }, { x: x - 625, y: -2945 }, { x: x + 900, y: -2945 }] const buttonsCoordsIndex = Math.floor(Math.random() * buttonsCoords.length) //pick a random element from the array spawn.mapRect(x + -1500, -210, 3000, 400);//station floor boosts = [] boosts.push(level.boost(x - 311, -218, 1200, 1.85)) spawn.mapRect(x + -225, -525, 675, 375); spawn.mapRect(x + -1350, -1175, 400, 675); spawn.mapRect(x + -225, -2125, 675, 400); // spawn.mapRect(x + -225, -1325, 675, 550); spawn.mapRect(x + -225, -1400, 675, 650); boosts.push(level.boost(x - 1335, -1200, 1800, 1)) boosts.push(level.boost(x + 1272, -1300, 1550, 2.75)) //far right //high up walls boosts.push(level.boost(x + 1455, -2048, 1450, 2.5)) spawn.mapRect(x + 1500, -3825, 325, 1900); boosts.push(level.boost(x - 1555, -2048, 1450, 0.64)) // spawn.mapRect(x + -1625, -3975, 3450, 325); spawn.mapRect(x + -1825, -4000, 325, 2150); spawn.mapRect(x + -1825, -4070, 3650, 375);//roof spawn.randomMob(x + 100, -2125, 0); boosts.push(level.boost(x + 75, -2175, 2800)) spawn.mapRect(x + -100, -3900, 400, 400); Matter.Body.setAngle(map[map.length - 1], map[map.length - 1].angle - Math.PI / 4); spawn.mapRect(x + 225, -2950, 1100, 150); spawn.mapRect(x + -1325, -2950, 1325, 150); 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) { spawn.randomMob(x + 350, -600, 0); spawn.randomMob(x + -25, -600, 0); spawn.randomMob(x + 600, -300, 0); spawn.randomMob(x + 1050, -300, 0); spawn.randomMob(x + 350, -1525, 0); spawn.randomMob(x + -75, -1525, 0); spawn.randomMob(x + -1075, -1275, 0); spawn.randomMob(x + -1350, -2050, 0); spawn.randomMob(x + -50, -2250, 0); spawn.randomMob(x + -200, -3050, 0); spawn.randomMob(x + -925, -3150, 0); spawn.randomMob(x + 450, -3125, 0); spawn.randomMob(x + 1075, -3025, 0); spawn.randomMob(x + 750, -3125, 0); spawn.randomMob(x + -725, -3125, 0); } stationCustom = () => { for (let i = 0; i < boosts.length; i++) { boosts[i].query() } } stationCustomTopLayer = () => { checkGate(gate, gateButton) ctx.fillStyle = "rgba(0,0,0,0.08)" ctx.fillRect(x - 225, -775, 675, 275); ctx.fillRect(x - 225, -1750, 675, 375); } }, () => { //people movers simulation.removeEphemera("zoom")//stop previous zooms simulation.zoomTransition(2000) const buttonsCoords = [{ x: x - 65, y: -2045 }] //only one button location? const buttonsCoordsIndex = Math.floor(Math.random() * buttonsCoords.length) //pick a random element from the array const moverDirection = stationNumber > 0 ? 1 : -1 console.log(stationNumber) 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 } } //floor 0 spawn.mapRect(x + -1500, -210, 3000, 400);//station floor const movers = [] movers.push(level.mover(x + -1200, -220, 900, 50, 3 * moverDirection)) movers.push(level.mover(x + 300, -220, 900, 50, 3 * moverDirection)) spawn.mapRect(x + -4700, -7000, 700, 5200);//Left wall spawn.mapRect(x + 4000, -7000, 500, 5200);//Right wall const portals = [] portals.push(level.portal({ x: x - 315, y: -310 }, Math.PI, { x: x - 3985, y: -2110 }, 0)) spawn.mapRect(x - 1375, -1100, 2750, 300); spawn.mapRect(x + -300, -525, 600, 550); //floor 1 fast with jump in middle movers.push(level.mover(x - 4000, -2025, 2700, 50, 30 * moverDirection)) movers.push(level.mover(x + 1300, -2025, 2700, 50, 30 * moverDirection)) portals.push(level.portal({ x: x + 3985, y: -2110 }, Math.PI, { x: x - 3985, y: -3410 }, 0)) spawn.mapRect(x + -500, -2050, 1000, 150); spawn.mapRect(x + -4200, -2300, 1225, 125); spawn.mapRect(x + 2675, -2350, 1625, 150); //up mode triggered by player contact const elevator0 = level.elevator(x - 1300, -1175, 175, 50, -1600, 0.011, { up: 0.01, down: 0.7 }) const elevator1 = level.elevator(x + 1125, -1175, 175, 50, -1600, 0.011, { up: 0.01, down: 0.7 }) //floor 2 slow with some things to jump on and mobs portals.push(level.portal({ x: x + 3985, y: -3410 }, Math.PI, { x: x - 3985, y: -5110 }, 0)) movers.push(level.mover(x - 4000, -3325, 8000, 50, 7 * moverDirection)) if (Math.random() < 0.5) { spawn.mapRect(x + 1125, -3625, 325, 200); spawn.mapRect(x - 1350, -3600, 375, 175); spawn.mapRect(x + 325, -3825, 325, 100); spawn.mapRect(x - 675, -3800, 450, 75); spawn.mapRect(x - 1775, -3900, 175, 400); spawn.mapRect(x - 2100, -4275, 325, 775); spawn.mapRect(x + 2625, -3700, 450, 125); spawn.mapRect(x - 3350, -3335, 175, 50); spawn.mapRect(x - 200, -3335, 500, 50); spawn.mapRect(x + 3200, -3335, 325, 50); } else { spawn.mapRect(x + -325, -3550, 425, 125); spawn.mapRect(x + -1100, -3750, 425, 75); spawn.mapRect(x + -2175, -3500, 200, 200); spawn.mapRect(x + 675, -3700, 175, 75); spawn.mapRect(x + 2375, -3425, 275, 125); spawn.mapRect(x + 1750, -3650, 275, 75); spawn.mapRect(x + 1125, -3850, 175, 550); spawn.mapRect(x + -3300, -4175, 675, 550); } spawn.mapRect(x + 3550, -3625, 550, 100); spawn.mapRect(x + -4100, -3650, 325, 100); if (!isExitOpen) { spawn.randomMob(x + 3900, -3725, 0); spawn.randomMob(x + 3675, -3700, 0); spawn.randomMob(x + 2075, -3400, 0); spawn.randomMob(x + 2500, -3500, 0); spawn.randomMob(x + 1975, -3700, 0); spawn.randomMob(x + 1250, -3900, 0); spawn.randomMob(x + 800, -3750, 0); spawn.randomMob(x + 2700, -4700, 0); spawn.randomMob(x + -75, -3650, 0); spawn.randomMob(x + 575, -3500, 0); spawn.randomMob(x + -850, -3900, 0); spawn.randomMob(x + -2725, -4350, 0); spawn.randomMob(x + -2975, -4300, 0); spawn.randomMob(x + -3950, -3675, 0); spawn.randomMob(x + -2950, -3450, 0); spawn.randomMob(x + -2075, -3575, 0); spawn.randomMob(x + -1650, -3450, 0); spawn.randomMob(x + -2825, -4400, 0); spawn.randomMob(x + -900, -4475, 0); spawn.randomMob(x + -75, -3575, 0); spawn.randomMob(x + 3900, -3775, 0); spawn.randomMob(x + 2825, -3375, 0); spawn.randomMob(x + 2075, -3425, 0); spawn.randomMob(x + 1525, -3425, 0); spawn.randomMob(x + 350, -3500, 0); spawn.randomMob(x + -1675, -3650, 0); spawn.randomMob(x + -3025, -3450, 0); spawn.randomMob(x + -3850, -3750, 0); } //floor 3 fast with bumps spawn.mapRect(x + -4250, -7000, 8475, 325);//roof portals.push(level.portal({ x: x + 3985, y: -5110 }, Math.PI, { x: x + 320, y: -310 }, 0)) movers.push(level.mover(x - 4000, -5025, 8000, 50, 50 * moverDirection)) if (Math.random() < 0.5) { spawn.mapVertex(x - 2100, -5050, "-150 0 150 0 5 -150 -5 -150") spawn.mapVertex(x - 0, -5100, "-500 0 500 0 25 -300 -25 -300") spawn.mapVertex(x + 2100, -5050, "-300 0 300 0 100 -100 -100 -100") } else { spawn.mapVertex(x - 2100, -5050, "-100 0 100 0 25 -100 -25 -100") spawn.mapVertex(x - 0, -5050, "-400 0 400 0 100 -100 -100 -100") spawn.mapVertex(x + 2100, -5050, "-400 0 400 0 100 -100 -100 -100") } spawn.mapRect(x + 2000, -6700, 200, 1250); spawn.mapRect(x + -100, -6700, 200, 1075); spawn.mapRect(x + -2125, -6700, 50, 925); // spawn.mapRect(x + -4150, -5325, 975, 125); //portal over hang // spawn.mapRect(x + 3325, -5300, 850, 100);//portal over hang stationCustom = () => { for (let i = 0; i < movers.length; i++) movers[i].push(); for (let i = 0; i < portals.length; i++) { portals[i][2].query() portals[i][3].query() } } stationCustomTopLayer = () => { for (let i = 0; i < portals.length; i++) { portals[i][0].draw() portals[i][1].draw() portals[i][2].draw() portals[i][3].draw() } elevator0.moveOnTouch() elevator1.moveOnTouch() //custom draw so you can see the mover tracks on subway map with the Line of sight graphics ctx.strokeStyle = "#000" ctx.lineWidth = 4; ctx.setLineDash([40, 40]); for (let i = 0; i < movers.length; i++) { ctx.beginPath(); ctx.moveTo(movers[i].vertices[0].x + 2, movers[i].vertices[0].y - 3); ctx.lineTo(movers[i].vertices[1].x - 2, movers[i].vertices[1].y - 3); ctx.lineDashOffset = (-simulation.cycle * movers[i].VxGoal) % 80; ctx.stroke(); } ctx.setLineDash([0, 0]); checkGate(gate, gateButton) } }, ] //update totalNumberOfStations to a higher number when adding new maps simulation.zoomTransition(level.defaultZoom) // stations[9]() //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.announceMobTypes() 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.isTimeDilated) { 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.isTimeDilated) { 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 < 12; ++i) powerUps.spawn(-1800 + 550 * Math.random(), -1800, "ammo") for (let i = 0; i < 5; ++i) powerUps.spawn(-1800 + 550 * Math.random(), -1700, "heal"); for (let i = 0; i < 1; ++i) powerUps.spawn(-1800 + 550 * Math.random(), -1750, "research"); const scale = Math.pow(simulation.difficulty, 0.7) 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.22 / 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.13 / 6; i < len; ++i) spawn.sprayBoss(-1327 - 200 * i, -1525, 30, false) for (let i = 0, len = scale * 0.25 / 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.22; 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.13; 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.25; i < len; ++i) spawn.mineBoss(-1327 - 200 * i, -1525, 50, false); //spawn 3-4 at difficulty 15 } } } } } 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") if (simulation.difficultyMode < 7) powerUps.spawn(-3650, -300, "tech") } } }; 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"); if (simulation.difficultyMode > 5) for (let i = 0; i < 8; i++) powerUps.spawn(1200 + 550 * Math.random(), -1700, "ammo"); //extra ammo on why difficulty 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 }, towers() { // simulation.isHorizontalFlipped = true level.announceMobTypes() const isFlippedHorizontal = (simulation.isHorizontalFlipped && Math.random() < 0.33) ? true : false if (isFlippedHorizontal) { level.setPosToSpawn(9150 + 50, -2230 - 25); level.exit.x = 400 - 50; level.exit.y = -50 + 25; leftRoomColor = "#cff" rightRoomColor = "rgba(0,0,0,0.13)" } else { level.setPosToSpawn(400, -50); level.exit.x = 9150; level.exit.y = -2230; } spawn.mapRect(level.enter.x, level.enter.y + 20, 100, 20); //bump for level entrance level.fallMode = "position"; //must set level.fallModeBounds in this mode to prevent player getting stuck left or right level.fallModeBounds = { left: level.enter.x, right: level.exit.x } //used with level.fallMode = "position"; if (isFlippedHorizontal) level.fallModeBounds = { left: level.exit.x, right: level.enter.x } //used with level.fallMode = "position"; simulation.fallHeight = 5000 //level.enter.y - 4000 spawn.mapRect(level.exit.x, level.exit.y + 20, 100, 20); //bump for level exit level.defaultZoom = 2300 simulation.zoomTransition(level.defaultZoom) document.body.style.backgroundColor = "#cdd9df"; powerUps.spawnStartingPowerUps(6300, 1025) const boost1 = level.boost(7560, 1480, 1700, 1.75) const boost2 = level.boost(7098, 0, 1250, Math.PI / 3) //x,y,push,angle radians const boost3 = level.boost(9700, -730, 1050, 1.95) const boost4 = level.boost(4300, -720, 1500, 1.25) const boost5 = level.boost(3000, -1215, 3000, 1.25) const boost6 = level.boost(8251, -619, 1200, 2.7) const boost7 = level.boost(7750, -1540, 1050, 1.2) // const boost6 = level.boost(8235, -619, 3500, 2.9) const train1 = level.transport(3650, 100, 415, 500, 8); //x,y,width.height,VxGoal,force const train2 = level.transport(1250, 100, 415, 500, -8); //x,y,width.height,VxGoal,force const train3 = level.transport(4050, 100, 415, 500, 8); //x,y,width.height,VxGoal,force let portal1, portal2 portal1 = level.portal({ x: 3675, y: -2225 + 1025 }, -Math.PI / 2, { //up x: 3675, y: -375 }, Math.PI / 2) //down portal2 = level.portal({ x: 6300, y: -1225 }, -Math.PI / 2, { //up x: 6300, y: -375 }, Math.PI / 2) //down level.custom = () => { boost1.query(); boost2.query(); boost3.query(); boost4.query(); boost5.query(); boost6.query(); boost7.query(); //trains oscillate back and forth and act like they are bouncing off each other if (train1.position.x < 2850) { train1.changeDirection(true) //go right } else if (train1.position.x > 3850) { train1.changeDirection(false) //go left } if (train2.position.x < 1450) { train2.changeDirection(true) //go right } else if (train2.position.x > 2450) { train2.changeDirection(false) //go left } if (train3.position.x < 4250) { train3.changeDirection(true) //go right } else if (train3.position.x > 5250) { train3.changeDirection(false) //go left } train1.move(); train2.move(); train3.move(); ctx.fillStyle = "rgba(0,0,0,0.25)" ctx.fillRect(1250, 121, 4200, 6) ctx.fillStyle = "rgba(50,70,100,0.04)" ctx.fillRect(2500, -10000, 1800, 30000); ctx.fillRect(8300, -10000, 1800, 30000); ctx.fillRect(-500, -10000, 1800, 30000); ctx.fillRect(5400, -10000, 1800, 30000); portal1[2].query() portal1[3].query() portal2[2].query() portal2[3].query() ctx.fillStyle = "#cff" if (isFlippedHorizontal) { ctx.fillRect(150, -300, 525, 325); //entrance typically } else { ctx.fillRect(8925, -2575, 525, 400) //exit typically } level.exit.drawAndCheck(); level.enter.draw(); }; level.customTopLayer = () => { ctx.fillStyle = "rgba(0,0,0,0.13)" ctx.fillRect(8300, -1950, 1550, 1275); ctx.fillRect(5400, 875, 1800, 650); ctx.fillRect(2950, -2200, 875, 1050); ctx.fillRect(5900, -1025, 800, 450); if (isFlippedHorizontal) { ctx.fillRect(8925, -2575, 575, 400) //exit typically } else { ctx.fillRect(150, -300, 525, 325); //entrance typically } ctx.fillStyle = "rgba(0,0,0,0.5)" ctx.fillRect(7175, -1515, 125, 180); portal1[0].draw(); portal1[1].draw(); portal1[2].draw(); portal1[3].draw(); portal2[0].draw(); portal2[1].draw(); portal2[2].draw(); portal2[3].draw(); }; // four large rounded squares let a = 900 //side length let c = 100 //corner offset // spawn.mapVertex(3400, -1300, `${-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 // spawn.mapVertex(9200, -1300, `${-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 // spawn.mapVertex(6300, 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 spawn.mapVertex(400, 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 //lower 1st zone entrance /exit spawn.mapRect(100, -350, 575, 75); spawn.mapRect(100, -300, 75, 375); spawn.mapRect(600, -325, 75, 175); spawn.mapRect(600, -10, 75, 50); //2nd zone upper hollow square spawn.mapVertex(5650 - 2900, 900 - 2200, `${-a} ${-a + c} ${-a + c} ${-a} ${-400} ${-a} ${-400} ${a} ${-a + c} ${a} ${-a} ${a - c}`); //1/2 square with edges cut off spawn.mapVertex(6950 - 2900, 900 - 2200, `${400} ${-a} ${a - c} ${-a} ${a} ${-a + c} ${a} ${a - c} ${a - c} ${a} ${400} ${a}`); //1/2 square with edges cut off // spawn.mapRect(5600 - 2900, 1400 - 2200, 1350, 400); spawn.mapRect(2950, -1175, 650, 775); spawn.mapRect(3750, -1175, 100, 775); spawn.mapRect(3575, -1025, 200, 475); //4th zone far right hollow square near exit spawn.mapVertex(9200, -2050, `${-a} ${-a + c} ${-a + c} ${-a} ${a - c} ${-a} ${a} ${-a + c} ${a} ${-600} ${-a} ${-600}`); //square with edges cut off --- hollow top spawn.mapVertex(9200, -550, `${-a} ${600} ${a} ${600} ${a} ${a - c} ${a - c} ${a} ${-a + c} ${a} ${-a} ${a - c}`); //square with edges cut off --- hollow bottom spawn.mapRect(9800, -2100, 300, 1600); //hollow left wall spawn.mapVertex(8175, -1425, "-1400 -90 350 -90 400 -40 400 40 350 90 -1400 90"); spawn.mapVertex(6856, -1425, "300 -90 -350 -90 -400 -40 -400 40 -350 90 300 90"); //exit housing spawn.mapRect(8925, -2575, 575, 75); if (isFlippedHorizontal) { spawn.mapRect(8925, -2550, 75, 400); spawn.mapRect(9425, -2550, 75, 125); spawn.mapRect(9425, -2215, 75, 50); spawn.bodyRect(9425, -2425, 75, 210); } else { spawn.mapRect(9425, -2550, 75, 400); spawn.mapRect(8925, -2550, 75, 125); spawn.mapRect(8925, -2215, 75, 50); } //lower 3rd zone spawn.mapVertex(6300, 450, `${-a} ${-a + c} ${-a + c} ${-a} ${a - c} ${-a} ${a} ${-a + c} ${a} ${0} ${-a} ${0}`); //square with edges cut off --- hollow top spawn.mapVertex(6300, 1200, "-400 -40 -350 -90 350 -90 400 -40 400 40 350 90 -350 90 -400 40"); spawn.mapVertex(6450, 1650, `${-a} ${600} ${a + 700} ${600} ${a + 700} ${a - c} ${a - c + 700} ${a} ${-a + c} ${a} ${-a} ${a - c}`); //square with edges cut off --- hollow bottom //upper 3rd zone a = 400 //side length c = 50 //corner offset // spawn.mapVertex(6300, -800, `${-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 spawn.mapVertex(6300, -1100, `${-a} ${-a + c} ${-a + c} ${-a} ${a - c} ${-a} ${a} ${-a + c} ${a} ${-200} ${-a} ${-200}`); //square with edges cut off spawn.mapVertex(6300, -500, `${-a} ${200} ${a} ${200} ${a} ${a - c} ${a - c} ${a} ${-a + c} ${a} ${-a} ${a - c}`); //square with edges cut off spawn.mapVertex(5800, -1425, "-300 -40 -250 -90 250 -90 300 -40 300 40 250 90 -250 90 -300 40"); spawn.mapVertex(5485, -1850, "-400 -40 -350 -90 350 -90 400 -40 400 40 350 90 -350 90 -400 40"); spawn.mapVertex(7115, -1850, "-400 -40 -350 -90 350 -90 400 -40 400 40 350 90 -350 90 -400 40"); //long spawn.mapVertex(6300, -2175, "-300 -40 -250 -90 250 -90 300 -40 300 40 250 90 -250 90 -300 40"); //highest spawn.mapVertex(4450, -1850, "-200 -40 -150 -90 150 -90 200 -40 200 40 150 90 -150 90 -200 40"); // spawn.mapVertex(5300, -300, "-300 -60 -270 -90 270 -90 300 -60 300 60 270 90 -270 90 -300 60"); spawn.mapVertex(5300, -300, "-300 -40 -250 -90 250 -90 300 -40 300 40 250 90 -250 90 -300 40"); spawn.mapVertex(4500, -590, "-300 -90 250 -90 300 -40 300 40 250 90 -300 90"); // spawn.mapVertex(4600, -590, "-500 -90 170 -90 200 -60 200 60 170 90 -500 90"); //no debris on this level, so spawn some heals and ammo powerUps.chooseRandomPowerUp(6275, 1425); powerUps.chooseRandomPowerUp(6300, -650); powerUps.chooseRandomPowerUp(9550, -750); //random blocks spawn.bodyRect(7725, -2200, 150, 250, 0.2); spawn.bodyRect(4625, -825, 75, 125, 0.2); spawn.bodyRect(3250, -1200, 25, 25, 0.2); spawn.bodyRect(3375, -1275, 25, 75, 0.2); spawn.bodyRect(3450, -1200, 50, 25, 0.2); spawn.bodyRect(2825, -2225, 25, 25, 0.2); spawn.bodyRect(4075, -2225, 50, 25, 0.2); spawn.bodyRect(8850, -800, 75, 100, 0.2); spawn.bodyRect(6900, -100, 75, 100, 0.2); spawn.bodyRect(8975, -1575, 50, 50, 0.2); spawn.bodyRect(5725, -1700, 125, 175, 0.2); spawn.bodyRect(6850, -1725, 150, 200, 0.2); spawn.bodyRect(500, -400, 100, 50, 0.3); spawn.bodyRect(6025, 1050, 100, 50, 0.2); spawn.bodyRect(6000, -800, 75, 200, 0.2); spawn.bodyRect(6775, -75, 125, 75, 0.5); spawn.bodyRect(7200, 1300, 50, 200, 0.5); //mobs spawn.randomMob(5700, -75, 0); spawn.randomMob(6200, -100, 0); spawn.randomMob(6900, -100, 0.1); spawn.randomMob(5550, -500, 0.1); spawn.randomMob(4675, -850, 0.1); spawn.randomMob(4450, -2050, 0.1); spawn.randomMob(4050, -2325, 0.1); spawn.randomMob(3350, -1325, 0.2); spawn.randomMob(5300, -2050, 0.2); spawn.randomMob(5675, -2050, 0.2); spawn.randomMob(5850, -1625, 0.3); spawn.randomMob(6775, -1600, 0.3); spawn.randomMob(7700, -1625, 0.4); spawn.randomMob(7850, -2000, 0.4); spawn.randomMob(7225, -2000, 0.4); spawn.randomMob(6350, -2400, 0.5); spawn.randomMob(8850, -1650, 0.5); spawn.randomMob(9500, -1300, 0.5); spawn.randomMob(9250, -900, 0.5); spawn.randomMob(8600, -875, 0.6); spawn.randomMob(5575, 1350, 0.6); spawn.randomMob(6075, 1025, 0.6); spawn.randomMob(6300, 1025, 0.7); spawn.randomMob(6525, 1425, 0.8); spawn.randomMob(7125, 1450, 0.9); // spawn.randomMob(8600, -2325, 0.7); // spawn.randomMob(8650, -2825, 0.8); // spawn.randomMob(9225, -2850, 0.9); // spawn.randomMob(8525, -2375, 0.9); spawn.randomGroup(4925, -2850, 1); if (simulation.difficulty > 1) { spawn.randomLevelBoss(7275, -2475); spawn.secondaryBossChance(8400, -1025) } powerUps.addResearchToLevel() //needs to run after mobs are spawned }, factory() { level.announceMobTypes() // simulation.enableConstructMode() //remove this!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! 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 = "#d0d2d4"; // 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.isTimeDilated) { block2Mob(0) block2Mob(1) block2Mob(2) } } } } if (buttonLeft.isUp) { buttonLeft.query(); if (!buttonLeft.isUp) { setMoverDirection(7) buttonRight.isUp = true //flip the other button up //remove any blocks on top of right button const badBlocks = Matter.Query.region(body, buttonRight) //figure out block's index for (let j = 0; j < badBlocks.length; j++) { let index = null for (let i = 0; i < body.length; i++) { if (badBlocks[j] === body[i]) index = i } //remove block // console.log(index, j) if (index) { Matter.Composite.remove(engine.world, badBlocks[j]); body.splice(index, 1); } } } } 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) { //opens up secondary zone 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.announceMobTypes() 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 hazard1 = level.laser({ x: x + 870, y: y - 915 }, { x: x + 1450, y: y - 915 }) 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 hazard1 = level.laser({ x: x + 1060, y: y - 655 }, { x: x + 2000, y: y - 655 }) 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 const hazard2 = level.laser({ x: x + 5, y: y - 325 }, { x: x + 455, y: y - 325 }) spawn.mapRect(x + 440, y - 335, 10, 20); //laser nose spawn.mapRect(x + 450, y - 375, 400, 100); //laser body const Xoffset = Math.floor(400 * Math.random()) //level.hazard(x + Xoffset, y - 1300, 10, 1300, 0.4) //laser const hazard3 = level.laser({ x: x + Xoffset + 5, y: y - 1290 }, { x: x + Xoffset + 5, y: y }) 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 hazard4 = level.laser({ x: x + Xoffset2 + 5, y: y - 230 }, { x: x + Xoffset2 + 5, y: y - 230 + 250 }) 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(); if (toggle.isOn) { if ((simulation.cycle % 120) > 60) { hazard1.query(); hazard2.query(); } else { hazard3.query(); hazard4.query() } } } ) }, ] 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 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 + 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 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 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.spawn(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.spawn(x + 998, y - 333, "tech", false); } const powerUp1 = powerUp[powerUp.length - 1] if (powerUp1) 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 && 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 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) { if (m.energy < 0.02) { //push out // const force = Vector.mult(Vector.normalise(Vector.sub(player.position, powerUp1.position)), 0.02 * player.mass) // player.force.x += force.x // player.force.y += force.y player.force.x += (player.position.x > powerUp1.position.x) ? 0.02 * player.mass : - 0.02 * player.mass } else { m.energy -= 0.01 //friction Matter.Body.setVelocity(player, { x: player.velocity.x * 0.45, y: player.velocity.y * 0.98 }); } } //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.spawn(x + 50, y - 1525, "ammo"); powerUps.spawn(x + 1950, y - 1525, "ammo"); powerUps.spawn(x + 1900, y - 1525, "ammo"); spawn.hopMotherBoss(x + 800, y + -2200) for (let i = 0; i < 4; ++i) spawn.hopBullet(x + 150 + 750 * Math.random(), y + -1600) for (let i = 0; i < 4; ++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 + 500, y + -2200); spawn.hopper(x + 1100, y + -2200); spawn.hopMother(x + 1400, y + -775); spawn.hopMother(x + 550, y + -775); spawn.hopMother(x + 525, y + -1475); spawn.hopMother(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() { level.announceMobTypes() level.fallMode = "start"; 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 powerUps.chooseRandomPowerUp(2075, -1525); powerUps.chooseRandomPowerUp(2550, -1825); powerUps.chooseRandomPowerUp(1975, 250); //bottom floor //entrance spawn.mapRect(-200, -750, 1500, 100); // spawn.mapRect(-575, 0, 2150, 500); spawn.mapRect(-575, 0, 2150, 165); const mover = level.mover(-525, 270, 2050, 75, 15 * (simulation.isHorizontalFlipped ? -1 : 1)) spawn.bodyRect(-1050, -75, 75, 75); spawn.bodyRect(-573, 170, 30, 105); // spawn.mapRect(-1275, 275, 875, 225); spawn.mapRect(-1300, 275, 4025, 3300); // 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); vanish.push(level.vanish(-525, -150, 425, 150)) vanish.push(level.vanish(-475, -300, 275, 150)) vanish.push(level.vanish(-425, -450, 225, 150)) vanish.push(level.vanish(-375, -600, 175, 150)) vanish.push(level.vanish(-325, -750, 125, 150)) 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); spawn.bodyRect(1575, 50, 50, 225); vanish.push(level.vanish(1900, -25, 325, 25)) vanish.push(level.vanish(1925, -375, 275, 25)) vanish.push(level.vanish(1950, -725, 225, 25)) vanish.push(level.vanish(1950, -1075, 225, 25)) spawn.mapRect(1950, -1500, 225, 25); vanish.push(level.vanish(1350, -1075, 225, 25)) vanish.push(level.vanish(1637, -1300, 225, 25)) //middle floor spawn.bodyRect(215, -1175, 100, 100, 0.3); spawn.mapRect(-1300, -1800, 250, 2300); 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); } spawn.bodyRect(225, -850, 50, 100, 0.4); // spawn.mapRect(600, -1800, 325, 225); spawn.mapRect(650, -1800, 225, 225); vanish.push(level.vanish(600, -1575, 100, 225)) vanish.push(level.vanish(825, -1575, 100, 225)) spawn.bodyRect(1050, -1825, 250, 20, 0.2); vanish.push(level.vanish(1125, -1800, 625, 25)) vanish.push(level.vanish(-50, -1800, 450, 25)) //exit spawn.mapRect(-575, -1800, 50, 200); spawn.mapRect(-1050, -1800, 525, 75); spawn.mapRect(-1050, -1450, 700, 75); 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(); mover.push(); }; level.customTopLayer = () => { mover.draw(); //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(-875, -1600, 225, 275); ctx.fillStyle = "rgba(68,68,68,0.93)" ctx.fillRect(-1575, 150, 2150, 150); 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(); mover.push(); }; level.customTopLayer = () => { mover.draw(); //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(650, -1600, 225, 275); ctx.fillRect(-1050, 50, 325, 225) ctx.fillRect(25, -950, 300, 225) ctx.fillStyle = "rgba(68,68,68,0.93)" ctx.fillRect(-575, 150, 2150, 150); for (let i = 0, len = vanish.length; i < len; i++) vanish[i].query() }; } }, testChamber() { level.announceMobTypes() 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 // const hazard2 = level.hazard((simulation.isHorizontalFlipped ? -1775 - 150 : 1775), -2550, 150, 10, 0.4) //laser // const hazard = level.laser({ x: (simulation.isHorizontalFlipped ? -350 - 700 : 350), y: -2025 }, { x: 700 + (simulation.isHorizontalFlipped ? -350 - 700 : 350), y: -2025 }) ////x, y, width, height, damage = 0.002) // const hazard2 = level.laser({ x: 145 + (simulation.isHorizontalFlipped ? -1775 - 150 : 1775), y: -2545 }, { x: (simulation.isHorizontalFlipped ? -1775 - 150 : 1775), y: -2545 }) ////x, y, width, height, damage = 0.002) let hazard, hazard2 if (simulation.isHorizontalFlipped) { hazard = level.laser({ x: -360, y: -2020 }, { x: -1050, y: -2020 }) ////x, y, width, height, damage = 0.002) hazard2 = level.laser({ x: - 1920, y: -2545 }, { x: -1775, y: -2545 }) ////x, y, width, height, damage = 0.002) } else { hazard = level.laser({ x: 360, y: -2020 }, { x: 700 + 360, y: -2020 }) ////x, y, width, height, damage = 0.002) hazard2 = level.laser({ x: 145 + 1775, y: -2545 }, { x: 1775, y: -2545 }) ////x, y, width, height, damage = 0.002) } spawn.mapRect(340, -2032.5, 20, 25); //laser nose 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() button.query(); button.draw(); ctx.fillStyle = "#d4f4f4" ctx.fillRect(-300, -1000, 650, 500) level.exit.drawAndCheck(); level.enter.draw(); }; level.customTopLayer = () => { door.draw(); if (!button.isUp) { hazard.query(); hazard2.query(); } 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 } }, interferometer() { level.isVerticalFLipLevel = true mobs.maxMobBody = 20 //normally 40, but set to 10 to avoid too much clutter simulation.fallHeight = 4000 level.announceMobTypes() level.setPosToSpawn(-1825, 1950); //lower start level.exit.x = -1875 level.exit.y = 1355 level.defaultZoom = 2300 simulation.zoomTransition(level.defaultZoom) document.body.style.backgroundColor = "#d0d5d5"; color.map = "#444" powerUps.chooseRandomPowerUp(-1550, 300); powerUps.chooseRandomPowerUp(200, 50); powerUps.chooseRandomPowerUp(-975, -1475); powerUps.chooseRandomPowerUp(2150, -750); powerUps.chooseRandomPowerUp(1850, 1925); let buttons = [] let lasers = [] let balance = [] level.isFlipped = false; let isFlipping = false; let isSpawned = false const flipAnimationCycles = 120 let elevator = body[body.length] = Bodies.rectangle(800, 500, 300, 50, { collisionFilter: { category: cat.body, //cat.map, mask: cat.player | cat.body | cat.bullet | cat.mob | cat.mobBullet //| cat.powerUp }, density: 0.1, inertia: Infinity, //prevents rotation isNotHoldable: true, friction: 1, frictionStatic: 1, restitution: 0, frictionAir: 1, classType: "body", holdX: 1762, maxHeight: -1580, minHeight: 90, verticalForce: 0.02, isUp: false, drag: 0.01, move() { this.force.y -= this.mass * simulation.g; //undo gravity if (!m.isTimeDilated) { if (level.isFlipped) { ctx.fillStyle = "#ccc" ctx.fillRect(this.holdX, -this.maxHeight, 5, this.maxHeight - this.minHeight) //draw path if (elevator.isUp) { elevator.force.y += elevator.verticalForce * elevator.mass if (elevator.position.y > -elevator.maxHeight) { elevator.isUp = false Matter.Body.setPosition(elevator, { x: elevator.holdX, y: -elevator.maxHeight }); Matter.Body.setVelocity(elevator, { x: 0, y: 0 }); } } else { elevator.force.y -= (elevator.verticalForce) * elevator.mass if (elevator.position.y < -elevator.minHeight) { elevator.isUp = true Matter.Body.setPosition(elevator, { x: elevator.holdX, y: -elevator.minHeight }); Matter.Body.setVelocity(elevator, { x: 0, y: 0 }); } } //vertical position limits if (this.position.y > -elevator.maxHeight) { Matter.Body.setPosition(elevator, { x: elevator.holdX, y: -elevator.maxHeight }); } else if (this.position.y < -elevator.minHeight) { Matter.Body.setPosition(elevator, { x: elevator.holdX, y: -elevator.minHeight }); } } else { ctx.fillStyle = "#ccc" ctx.fillRect(this.holdX, this.maxHeight, 5, this.minHeight - this.maxHeight) //draw path if (elevator.isUp) { elevator.force.y -= elevator.verticalForce * elevator.mass if (elevator.position.y < elevator.maxHeight) { elevator.isUp = false Matter.Body.setPosition(elevator, { x: elevator.holdX, y: elevator.maxHeight }); Matter.Body.setVelocity(elevator, { x: 0, y: 0 }); } } else { elevator.force.y += (elevator.verticalForce) * elevator.mass if (elevator.position.y > elevator.minHeight) { elevator.isUp = true Matter.Body.setPosition(elevator, { x: elevator.holdX, y: elevator.minHeight }); Matter.Body.setVelocity(elevator, { x: 0, y: 0 }); } } //vertical position limits if (this.position.y < elevator.maxHeight) { Matter.Body.setPosition(elevator, { x: elevator.holdX, y: elevator.maxHeight }); } else if (this.position.y > elevator.minHeight) { Matter.Body.setPosition(elevator, { x: elevator.holdX, y: elevator.minHeight }); } } } Matter.Body.setVelocity(elevator, { x: 0, y: elevator.velocity.y * this.drag }); //zero horizontal velocity and drag Matter.Body.setPosition(elevator, { x: elevator.holdX, y: elevator.position.y }); //hold horizontal position }, }); Composite.add(engine.world, elevator); //add to world let buildMapOutline = function () { //boxes center on zero,zero with deep walls to hide background spawn.mapRect(2225, -2000, 1775, 4000); //right map wall spawn.mapRect(-4000, -2000, 2000, 4000); //left map wall spawn.mapRect(-4000, -5000, 8000, 3000); //map ceiling spawn.mapRect(-4000, 2000, 8000, 3000); //floor } let buildNormalMap = function () { buttons.push(level.button(-1895, -1600, 126, true, false, "hsl(330, 100%, 50%)")) buttons[buttons.length - 1].isUp = false spawn.mapRect(-1675, -2025, 50, 250); simulation.ephemera.push({ name: "buttons up", count: flipAnimationCycles + 30, do() { this.count-- if (this.count < 0) { for (let i = 0; i < buttons.length; i++) buttons[i].isUp = true simulation.removeEphemera(this.name); isFlipping = false } }, }) lasers.push(level.laser({ x: -1100, y: 1990 }, { x: -1100, y: -2000 })) ////x, y, width, height, damage = 0.002) spawn.mapRect(-1112, 1990, 25, 25); //laser entrance lasers.push(level.laser({ x: -600, y: 1990 }, { x: -600, y: -2000 })) ////x, y, width, height, damage = 0.002) spawn.mapRect(-612, 1990, 25, 25); //laser entrance lasers.push(level.laser({ x: -100, y: 1990 }, { x: -100, y: -2000 })) ////x, y, width, height, damage = 0.002) spawn.mapRect(-112, 1990, 25, 25); //laser entrance balance.push(level.rotor(-1250, 1755, 400, 25, 0.01, 0, 0.5)) //balance(x, y, width, height, density = 0.001, angle = 0, frictionAir = 0.001, angularVelocity = 0, rotationForce = 0.0005) { balance.push(level.rotor(-750, 1755, 400, 25, 0.01, Math.PI / 2, 0.5)) balance.push(level.rotor(-275, 1675, 550, 32, 0.01, 0, 0.5)) lasers.push(level.laser({ x: 1610, y: -850 }, { x: -1625, y: -850 })) ////x, y, width, height, damage = 0.002) // spawn.mapRect(1980, -862, 25, 25); //laser entrance // balance.push(level.rotor(1000, -910, 550, 32, 0.01, 0, 0.5)) //left side //level entrance spawn.mapRect(level.enter.x, level.enter.y + 20, 100, 20); spawn.mapRect(level.exit.x, level.exit.y - 40, 100, 20); spawn.mapRect(-2025, 1650, 400, 50); spawn.mapRect(-2100, -1600, 475, 2925); spawn.mapRect(-1675, 1500, 50, 325); // spawn.mapRect(-1625, 365, 500, 950); // spawn.mapRect(-1075, 390, 450, 900); // spawn.mapRect(-575, 415, 500, 850); spawn.mapVertex(-1375, 835, "-250 -475 0 -500 250 -475 250 475 -250 475"); spawn.mapVertex(-850, 840, "-225 -475 0 -500 225 -475 225 475 -225 475"); spawn.mapVertex(-350, 840, "-225 -475 0 -500 225 -475 225 475 -225 475"); //lower right side //far right wall ledges spawn.mapRect(1925, -1700, 400, 200); spawn.mapRect(1925, -1200, 400, 200); spawn.mapRect(1925, -700, 400, 200); spawn.mapRect(1925, -200, 400, 200); spawn.mapRect(1925, 300, 400, 200); spawn.mapRect(1925, 800, 400, 200); spawn.mapRect(1925, 1300, 400, 200); spawn.mapRect(1250, 1650, 500, 25); spawn.mapRect(1300, 1125, 400, 25); spawn.mapRect(1350, 600, 300, 25); spawn.mapRect(1400, 75, 200, 25); spawn.mapRect(650, 1287, 475, 50); spawn.mapRect(650, 800, 500, 125); spawn.mapRect(650, 150, 500, 225); spawn.mapRect(350, 1300, 800, 375); spawn.mapRect(350, 950, 150, 400); spawn.mapRect(-25, 950, 525, 100); spawn.mapRect(-75, 525, 575, 200); spawn.mapRect(-75, 75, 575, 200); spawn.mapRect(475, 1987, 550, 50); //ceiling zone spawn.mapRect(1200, -1600, 400, 25); spawn.mapRect(-75, -1725, 1075, 25); spawn.mapRect(-575, -1625, 450, 25); spawn.mapRect(-1075, -1850, 450, 25); spawn.mapRect(-1075, -1425, 450, 25); spawn.mapRect(-1675, -1588, 550, 25); } let buildVerticalFLippedMap = function () { // flip Y with this -> spawn.mapRect(x, -y - h, w, h); buttons.push(level.button(-1895, 1600, 126, true, true, "hsl(330, 100%, 50%)")) buttons[buttons.length - 1].isUp = false spawn.mapRect(-1675, 2025 - 250, 50, 250); simulation.ephemera.push({ name: "buttons up", count: flipAnimationCycles + 30, do() { this.count-- if (this.count < 0) { for (let i = 0; i < buttons.length; i++) buttons[i].isUp = true simulation.removeEphemera(this.name); isFlipping = false } }, }) lasers.push(level.laser({ x: -1100, y: -1990 }, { x: -1100, y: 2000 })) ////x, y, width, height, damage = 0.002) spawn.mapRect(-1112, -1990 - 25, 25, 25); //laser entrance lasers.push(level.laser({ x: -600, y: -1990 }, { x: -600, y: 2000 })) ////x, y, width, height, damage = 0.002) spawn.mapRect(-612, -1990 - 25, 25, 25); //laser entrance lasers.push(level.laser({ x: -100, y: -1990 }, { x: -100, y: 2000 })) ////x, y, width, height, damage = 0.002) spawn.mapRect(-112, -1990 - 25, 25, 25); //laser entrance balance.push(level.rotor(-1250, -1755 - 25, 400, 25, 0.01, 0, 0.5)) //balance(x, y, width, height, density = 0.001, angle = 0, frictionAir = 0.001, angularVelocity = 0, rotationForce = 0.0005) { balance.push(level.rotor(-750, -1755 - 25, 400, 25, 0.01, Math.PI / 2, 0.5)) balance.push(level.rotor(-250, -1675 - 32, 500, 32, 0.01, 0, 0.5)) lasers.push(level.laser({ x: 1610, y: 850 }, { x: -1625, y: 850 })) ////x, y, width, height, damage = 0.002) // spawn.mapRect(1980, 862 - 25, 25, 25); //laser entrance // balance.push(level.rotor(1000, 910 - 32, 550, 32, 0.01, 0, 0.5)) //left side //level entrance spawn.mapRect(level.enter.x, level.enter.y - 20 - 20, 100, 20); spawn.mapRect(level.exit.x, level.exit.y + 40 - 20, 100, 20); spawn.mapRect(-2025, -1650 - 50, 400, 50); spawn.mapRect(-2100, +1600 - 2925, 475, 2925); spawn.mapRect(-1675, -1500 - 325, 50, 325); spawn.mapVertex(-1375, -835, "-250 -475 250 -475 250 475 0 500 -250 475"); spawn.mapVertex(-850, -835, "-225 -475 225 -475 225 475 0 500 -225 475"); spawn.mapVertex(-350, -835, "-225 -475 225 -475 225 475 0 500 -225 475"); //far right wall ledges spawn.mapRect(1925, 1700 - 200, 400, 200); spawn.mapRect(1925, 1200 - 200, 400, 200); spawn.mapRect(1925, 700 - 200, 400, 200); spawn.mapRect(1925, 200 - 200, 400, 200); spawn.mapRect(1925, -300 - 200, 400, 200); spawn.mapRect(1925, -800 - 200, 400, 200); spawn.mapRect(1925, -1300 - 200, 400, 200); spawn.mapRect(1250, -1650 - 25, 500, 25); spawn.mapRect(1300, -1125 - 25, 400, 25); spawn.mapRect(1350, -600 - 25, 300, 25); spawn.mapRect(1400, -75 - 25, 200, 25); spawn.mapRect(650, -1287 - 50, 475, 50); spawn.mapRect(650, -800 - 125, 500, 125); spawn.mapRect(650, -150 - 225, 500, 225); spawn.mapRect(350, -1300 - 375, 800, 375); spawn.mapRect(350, -950 - 400, 150, 400); spawn.mapRect(-25, -950 - 100, 525, 100); spawn.mapRect(-75, -525 - 200, 575, 200); spawn.mapRect(-75, -75 - 200, 575, 200); spawn.mapRect(475, -1987 - 50, 550, 50); //ceiling zone spawn.mapRect(1200, 1575, 400, 25); spawn.mapRect(-75, 1700, 1075, 25); spawn.mapRect(-575, 1625 - 25, 450, 25); spawn.mapRect(-1075, 1850 - 25, 450, 25); spawn.mapRect(-1075, 1425 - 25, 450, 25); spawn.mapRect(-1675, 1588 - 25, 550, 25); } let flipAndRemove = function () { simulation.translatePlayerAndCamera({ x: player.position.x, y: -player.position.y }) level.enter.y = -level.enter.y level.exit.y = -level.exit.y for (let i = body.length - 1; i > -1; i--) { if (body[i].isRotor) body.splice(i, 1); } function removeAll(array) { for (let i = 0; i < array.length; ++i) Matter.Composite.remove(engine.world, array[i]); } removeAll(map); map = []; removeAll(balance); balance = [] removeAll(buttons); buttons = [] lasers = [] function invertVertical(array) { for (let i = 0; i < array.length; ++i) { Matter.Body.setPosition(array[i], { x: array[i].position.x, y: -array[i].position.y }) } } invertVertical(body); invertVertical(powerUp); invertVertical(bullet); invertVertical(mob); //fields if (m.fieldMode === 9 && m.hole.isOn) { m.hole.pos1.y *= -1 m.hole.pos2.y *= -1 } else if (m.fieldMode === 2) { m.fieldPosition.y *= -1 m.fieldAngle *= -1 } //history for (let i = 0; i < m.history.length; i++) { m.history[i].position.y *= -1 m.history[i].angle *= -1 m.history[i].velocity.y *= -1 } for (let i = 0; i < mob.length; i++) { //stun to wipe history of all mobs, so they don't get confused about player position vertical swap mobs.statusStun(mob[i], 1) //edge cases if (mob[i].history) { for (let j = 0; j < mob[i].history.length; j++) mob[i].history[j].y *= -1 } if (mob[i].laserArray) { for (let j = 0; j < mob[i].laserArray.length; j++) { mob[i].laserArray[j].a.y *= -1 mob[i].laserArray[j].b.y *= -1 } } if (mob[i].springTarget2) { mob[i].springTarget.y *= -1 mob[i].springTarget2.y *= -1 } } } buildMapOutline() buildNormalMap() level.custom = () => { elevator.move() // console.log(elevator) lasers[lasers.length - 1].look.y = elevator.position.y lasers[lasers.length - 1].position.y = elevator.position.y for (let i = 0; i < buttons.length; i++) { buttons[i].draw() if (buttons[i].isUp && !isFlipping) { // buttons[i].query(); buttons[i].queryPlayer(); if (!buttons[i].isUp) { isFlipping = true if (level.isFlipped) { const normalMap = function () { level.isFlipped = false flipAndRemove() buildMapOutline() buildNormalMap(); //rewrite flipped version of map simulation.draw.setPaths() //update map graphics level.addToWorld() } simulation.unFlipCameraVertical(flipAnimationCycles, normalMap) } else { const flipMap = function () { level.isFlipped = true flipAndRemove() buildMapOutline() buildVerticalFLippedMap(); //rewrite flipped version of map simulation.draw.setPaths() //update map graphics level.addToWorld() if (!isSpawned) { isSpawned = true //spawn second wave of flipped mobs only once spawn.randomMob(-1500, -1425, 0); spawn.randomMob(-950, -1425, 0); spawn.randomMob(-800, -1475, 0); spawn.randomMob(-425, -1425, 0); spawn.randomMob(850, -1750, 0.1); spawn.randomMob(325, -850, 0.1); spawn.randomMob(400, -400, 0.2); spawn.randomMob(825, -475, 0.2); spawn.randomMob(875, -1050, 0.3); spawn.randomMob(1425, 1425, 0.4); spawn.randomMob(675, 1450, 0.5); spawn.randomMob(225, 1475, 0.6); spawn.randomMob(-275, 1425, 1); spawn.randomMob(-800, 1375, 1); spawn.secondaryBossChance(700, 1100) } } simulation.flipCameraVertical(flipAnimationCycles, flipMap) } break } } } if (level.isFlipped) { //background structure ctx.fillStyle = "#c3c7c7" ctx.fillRect(1487, -75 - 1925, 25, 1925); ctx.fillRect(1925, -2050, 300, 4100); //exit room ctx.fillStyle = "#d4f4f4" ctx.fillRect(-2000, -1325 - 350, 375, 350) level.exit.drawAndCheck(); //draw flipped entrance // ctx.translate(0, -3940) 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(); // ctx.translate(0, 3940) } else { //background structure ctx.fillStyle = "#c5c9c9" ctx.fillRect(1487, 75, 25, 1925); ctx.fillRect(1925, -2050, 300, 4100); //draw flipped exit ctx.fillStyle = "#d4f4f4" ctx.fillRect(-2000, 1325, 375, 350) ctx.beginPath(); ctx.moveTo(level.exit.x, level.exit.y - 30); ctx.lineTo(level.exit.x, level.exit.y + 80); ctx.bezierCurveTo(level.exit.x, level.exit.y + 170, level.exit.x + 100, level.exit.y + 170, level.exit.x + 100, level.exit.y + 80); ctx.lineTo(level.exit.x + 100, level.exit.y - 30); ctx.lineTo(level.exit.x, level.exit.y - 30); ctx.fillStyle = "#0ff"; ctx.fill(); level.enter.draw(); } }; level.customTopLayer = () => { for (let i = 0; i < lasers.length; i++) lasers[i].query() ctx.fillStyle = "#233" //balances center dot ctx.beginPath(); for (let i = 0; i < balance.length; i++) { ctx.moveTo(balance[i].center.x, balance[i].center.y) ctx.arc(balance[i].center.x, balance[i].center.y, 9, 0, 2 * Math.PI); //rotor spins and stops at vertical and horizontal angles if ((simulation.cycle % 140) < 15) { balance[i].torque = 0.0002 * balance[i].inertia } else if (Math.floor(10 * (balance[i].angle % (Math.PI / 2))) === 0) { Matter.Body.setAngularVelocity(balance[i], balance[i].angularVelocity * 0.1) } } ctx.fill(); ctx.fillStyle = `rgba(255,255,255,${0 + 0.3 * Math.random()})` if (level.isFlipped) { ctx.fillRect(-2025, 2025 - 450, 400, 450); //shadows ctx.fillStyle = "rgba(0,0,0,0.08)" ctx.fillRect(-2025, -2075, 900, 775); ctx.fillRect(-1075, -2025, 450, 725); ctx.fillRect(-575, -2025, 450, 725); ctx.fillRect(-25, -250 - 725, 525, 725); ctx.fillRect(650, -350 - 975, 475, 975); ctx.fillRect(375, -1650 - 400, 750, 400); //ceiling ctx.fillStyle = "rgba(0,0,0,0.04)" ctx.fillRect(1225, 2025 - 450, 350, 450); ctx.fillRect(-50, 1700, 1025, 325); ctx.fillRect(-550, 2025 - 425, 400, 425); ctx.fillRect(-1050, 2025 - 625, 400, 625); ctx.fillRect(-1625, 2025 - 450, 475, 450); } else { ctx.fillRect(-2025, -2025, 400, 450); //shadows ctx.fillStyle = "rgba(0,0,0,0.08)" ctx.fillRect(-2025, 1300, 900, 775); ctx.fillRect(-1075, 1300, 450, 725); ctx.fillRect(-575, 1300, 450, 725); ctx.fillRect(-25, 250, 525, 725); ctx.fillRect(650, 350, 475, 975); ctx.fillRect(375, 1650, 750, 400); //ceiling ctx.fillStyle = "rgba(0,0,0,0.04)" ctx.fillRect(1225, -2025, 350, 450); ctx.fillRect(-50, -2025, 1025, 325); ctx.fillRect(-550, -2025, 400, 425); ctx.fillRect(-1050, -2025, 400, 625); ctx.fillRect(-1625, -2025, 475, 450); } }; spawn.bodyRect(1325, -1775, 175, 175); spawn.bodyRect(-375, -1725, 100, 75, 0.5); spawn.bodyRect(-900, -1625, 125, 200, 0.5); spawn.bodyRect(875, -25, 200, 175); spawn.bodyRect(-1662, 1325, 25, 175); spawn.bodyRect(-1662, 1825, 25, 175); // spawn.bodyRect(-1662, -1825, 25, 225); spawn.bodyRect(1900, 1875, 100, 125, 0.5); spawn.bodyRect(400, 1925, 225, 50, 0.1); spawn.bodyRect(950, 750, 75, 50, 0.1); spawn.bodyRect(200, -25, 150, 100, 0.1); spawn.bodyRect(300, 900, 75, 50, 0.1); spawn.bodyRect(1475, 1025, 100, 100, 0.1); spawn.bodyRect(250, 450, 75, 75, 0.1); spawn.bodyRect(775, 75, 75, 75, 0.1); spawn.bodyRect(1200, 1900, 125, 100, 0.1); spawn.randomMob(125, -1900, 0); spawn.randomMob(-375, -1875, 0); spawn.randomMob(-1350, -1750, 0); spawn.randomMob(-875, -1575, 0); spawn.randomMob(500, -1875, 0); spawn.randomMob(350, 825, 0); spawn.randomMob(375, 400, 0); spawn.randomMob(1500, -25, 0.1); spawn.randomMob(650, -1950, 0.2); spawn.randomMob(775, 700, 0.2); spawn.randomMob(275, -50, 0.3); spawn.randomMob(75, -1750, 0.3); spawn.randomMob(1750, -1425, 0.4); spawn.randomMob(950, 50, 0.4); spawn.randomMob(-1375, 175, 0.4); spawn.randomMob(-350, 175, 0.5); spawn.randomMob(725, 1175, 0.5); spawn.randomMob(-850, -1950, 0.6); spawn.randomMob(-1400, -1725, 0.7); spawn.randomMob(1400, -1700, 0.7); spawn.randomMob(-800, 200, 0.7); spawn.randomMob(1475, 1550, 0.8); spawn.randomMob(1475, 500, 0.8); powerUps.spawnStartingPowerUps(-875, -1925); spawn.randomLevelBoss(-875, -200); powerUps.addResearchToLevel() //needs to run after mobs are spawned }, gravitron() { mobs.maxMobBody = 25 //normally 40, but set lower to avoid too much clutter level.isVerticalFLipLevel = true simulation.fallHeight = 4000 level.announceMobTypes() level.setPosToSpawn(-2375, 950); level.exit.x = 3750 level.exit.y = 165 level.defaultZoom = 2600 simulation.zoomTransition(level.defaultZoom) document.body.style.backgroundColor = "#c3d6e1"; color.map = "#444" powerUps.chooseRandomPowerUp(-1825, 975); powerUps.chooseRandomPowerUp(-3975, 975); powerUps.chooseRandomPowerUp(3900, 925); let buttons = [] level.isFlipped = false; let isFlipping = false; const flipAnimationCycles = 60 let buildMapOutline = function () { //boxes center on zero,zero with deep walls to hide background spawn.mapRect(4000, -2000, 2000, 4000); //right map wall spawn.mapRect(-6000, -2000, 2000, 4000); //left map wall spawn.mapRect(-6000, -4000, 12000, 3000); //map ceiling spawn.mapRect(-6000, 1000, 12000, 3000); //floor } let buildNormalMap = function () { buttons.push(level.button(-3350, 985, 126, true, false, "hsl(330, 100%, 50%)")) buttons.push(level.button(-3350, -985, 126, true, true, "hsl(330, 100%, 50%)")) buttons.push(level.button(150, 985, 126, true, false, "hsl(330, 100%, 50%)")) buttons.push(level.button(150, -985, 126, true, true, "hsl(330, 100%, 50%)")) buttons.push(level.button(3725, 985, 126, true, false, "hsl(330, 100%, 50%)")) buttons.push(level.button(3725, -985, 126, true, true, "hsl(330, 100%, 50%)")) for (let i = 0; i < buttons.length; i++) buttons[i].isUp = false simulation.ephemera.push({ name: "buttons up", count: flipAnimationCycles, do() { this.count-- if (this.count < 0) { for (let i = 0; i < buttons.length; i++) buttons[i].isUp = true simulation.removeEphemera(this.name); isFlipping = false } }, }) //far left zone spawn.mapRect(-2575, 987, 375, 100); spawn.mapRect(-2575, -600, 600, 1325); spawn.mapRect(-2200, 650, 225, 475); spawn.mapRect(-2575, 700, 35, 125); spawn.mapRect(-3500, -1050, 425, 63); spawn.mapRect(-3500, 987, 425, 50); spawn.mapVertex(-2275, -1000, "-400 0 -300 150 300 150 400 0"); spawn.mapVertex(-3287, 0, "-213 -500 0 -550 213 -500 213 500 0 550 -213 500"); spawn.mapVertex(-3750, -100, "-100 -200 -50 -250 50 -250 100 -200 100 200 50 250 -50 250 -100 200"); spawn.mapVertex(-2825, 0, "-100 -400 -50 -450 50 -450 100 -400 100 400 50 450 -50 450 -100 400"); //dense center left zone spawn.mapVertex(-1150, -750, "400 -75 425 0 400 75 -400 75 -425 0 -400 -75"); spawn.mapVertex(-550, -450, "400 -75 425 0 400 75 -400 75 -425 0 -400 -75"); spawn.mapVertex(-1685, 153, "-150 -500 0 -550 150 -500 150 750 -150 450"); spawn.mapVertex(-1106, 707, "500 -150 550 0 500 150 -500 150 -800 -150"); spawn.mapRect(-1645, 470, 200, 200); Matter.Body.setAngle(map[map.length - 1], Math.PI / 4) spawn.mapRect(-2085, 910, 200, 200); Matter.Body.setAngle(map[map.length - 1], Math.PI / 4) //open center right area with both bosses // spawn.mapRect(0, -450, 425, 1100); spawn.mapVertex(213, 0, "-213 -700 0 -650 213 -600 213 700 0 650 -213 600"); spawn.mapRect(0, -1050, 425, 63); spawn.mapRect(0, 987, 425, 50); spawn.mapVertex(1700, -1000, "-600 0 -400 400 400 400 600 0"); spawn.mapVertex(2800, -1000, "-500 0 -400 150 400 150 500 0"); spawn.mapVertex(1700, 700, "-400 -100 -450 0 -400 100 400 100 450 0 400 -100"); spawn.mapVertex(2800, 375, "-400 -100 -450 0 -400 100 400 100 450 0 400 -100"); //far right exit structure spawn.mapRect(3575, -1050, 425, 63); spawn.mapRect(3575, 987, 425, 50); spawn.mapVertex(3840, 450, "-250 -300 250 -300 250 300 -250 100"); spawn.mapVertex(3840, -450, "-250 300 250 300 250 -300 -250 -100"); spawn.mapRect(3750, 185, 100, 25); } let buildVerticalFLippedMap = function () { // flip Y with this -> spawn.mapRect(x, -y - h, w, h); buttons.push(level.button(-3350, 985, 126, true, false, "hsl(330, 100%, 50%)")) buttons.push(level.button(-3350, -985, 126, true, true, "hsl(330, 100%, 50%)")) buttons.push(level.button(150, 985, 126, true, false, "hsl(330, 100%, 50%)")) buttons.push(level.button(150, -985, 126, true, true, "hsl(330, 100%, 50%)")) buttons.push(level.button(3725, 985, 126, true, false, "hsl(330, 100%, 50%)")) buttons.push(level.button(3725, -985, 126, true, true, "hsl(330, 100%, 50%)")) for (let i = 0; i < buttons.length; i++) buttons[i].isUp = false simulation.ephemera.push({ name: "buttons up", count: flipAnimationCycles, do() { this.count-- if (this.count < 0) { for (let i = 0; i < buttons.length; i++) buttons[i].isUp = true simulation.removeEphemera(this.name); isFlipping = false } }, }) //far left zone spawn.mapRect(-2575, -1087, 375, 100); spawn.mapRect(-2575, 600 - 1325, 600, 1325); spawn.mapRect(-2200, -650 - 475, 225, 475); spawn.mapRect(-2575, -700 - 125, 35, 125); spawn.mapRect(-3500, 1050 - 63, 425, 63); spawn.mapRect(-3500, -987 - 50, 425, 50); spawn.mapVertex(-2275, 1000, "-300 0 -400 150 400 150 300 0"); spawn.mapVertex(-3287, 0, "-213 -500 0 -550 213 -500 213 500 0 550 -213 500"); spawn.mapVertex(-3750, 100, "-100 -200 -50 -250 50 -250 100 -200 100 200 50 250 -50 250 -100 200"); spawn.mapVertex(-2825, 0, "-100 -400 -50 -450 50 -450 100 -400 100 400 50 450 -50 450 -100 400"); //dense center left zone spawn.mapVertex(-1150, 750, "400 -75 425 0 400 75 -400 75 -425 0 -400 -75"); spawn.mapVertex(-550, 450, "400 -75 425 0 400 75 -400 75 -425 0 -400 -75"); spawn.mapVertex(-1685, -153, "-150 500 0 550 150 500 150 -750 -150 -450"); spawn.mapVertex(-1106, -707, "500 150 550 0 500 -150 -500 -150 -800 150"); spawn.mapRect(-1645, -470 - 200, 200, 200); Matter.Body.setAngle(map[map.length - 1], Math.PI / 4) spawn.mapRect(-2085, -910 - 200, 200, 200); Matter.Body.setAngle(map[map.length - 1], Math.PI / 4) //open center right area with both bosses spawn.mapVertex(213, 0, "-213 -600 0 -650 213 -700 213 600 0 650 -213 700"); spawn.mapRect(0, 1050 - 63, 425, 63); spawn.mapRect(0, -987 - 50, 425, 50); spawn.mapVertex(1700, 1000, "-400 0 -600 400 600 400 400 0"); spawn.mapVertex(2800, 1000, "-400 0 -500 150 500 150 400 0"); spawn.mapVertex(1700, -700, "-400 -100 -450 0 -400 100 400 100 450 0 400 -100"); spawn.mapVertex(2800, -375, "-400 -100 -450 0 -400 100 400 100 450 0 400 -100"); //far right building like exit structure spawn.mapRect(3575, 1050 - 63, 425, 63); spawn.mapRect(3575, -987 - 50, 425, 50); spawn.mapVertex(3840, 450, "-250 -300 250 -300 250 300 -250 100"); spawn.mapVertex(3840, -450, "-250 300 250 300 250 -300 -250 -100"); spawn.mapRect(3750, -210, 100, 25); } let flipAndRemove = function () { level.enter.y = -level.enter.y level.exit.y = -level.exit.y for (let i = body.length - 1; i > -1; i--) { if (body[i].isRotor) body.splice(i, 1); } function removeAll(array) { for (let i = 0; i < array.length; ++i) Matter.Composite.remove(engine.world, array[i]); } removeAll(map); map = []; removeAll(buttons); buttons = [] simulation.translatePlayerAndCamera({ x: player.position.x, y: -player.position.y }, false) function invertVertical(array) { for (let i = 0; i < array.length; ++i) { Matter.Body.setPosition(array[i], { x: array[i].position.x, y: -array[i].position.y }) } } invertVertical(body); invertVertical(powerUp); invertVertical(bullet); invertVertical(mob); //fields if (m.fieldMode === 9 && m.hole.isOn) { m.hole.pos1.y *= -1 m.hole.pos2.y *= -1 } else if (m.fieldMode === 2) { m.fieldPosition.y *= -1 m.fieldAngle *= -1 } //history for (let i = 0; i < m.history.length; i++) { m.history[i].position.y *= -1 m.history[i].angle *= -1 m.history[i].velocity.y *= -1 } for (let i = 0; i < mob.length; i++) { //stun to wipe history of all mobs, so they don't get confused about player position vertical swap mobs.statusStun(mob[i], 1) //edge cases if (mob[i].history) { for (let j = 0; j < mob[i].history.length; j++) mob[i].history[j].y *= -1 } if (mob[i].laserArray) { for (let j = 0; j < mob[i].laserArray.length; j++) { mob[i].laserArray[j].a.y *= -1 mob[i].laserArray[j].b.y *= -1 } } if (mob[i].springTarget2) { mob[i].springTarget.y *= -1 mob[i].springTarget2.y *= -1 } } } buildMapOutline() buildNormalMap() level.custom = () => { //stuff floats near buttons // if ((player.position.x > -3505 && player.position.x < -3075) || // (player.position.x > 0 && player.position.x < 425) || // (player.position.x > 3575)) { // if (player.position.y > 0) { // player.force.y -= 0.8 * simulation.g * player.mass // } // } for (let i = 0; i < body.length; i++) { if ((body[i].position.x > -3505 && body[i].position.x < -3075) || (body[i].position.x > 0 && body[i].position.x < 425) || (body[i].position.x > 3575) ) { if (body[i].position.y > 0) { body[i].force.y -= 1.04 * simulation.g * body[i].mass } else { body[i].force.y += 1.04 * simulation.g * body[i].mass } } } for (let i = 0; i < powerUp.length; i++) { if ((powerUp[i].position.x > -3505 && powerUp[i].position.x < -3075) || (powerUp[i].position.x > 0 && powerUp[i].position.x < 425) || (powerUp[i].position.x > 3575) ) { if (powerUp[i].position.y > 0) { powerUp[i].force.y -= 1.04 * simulation.g * powerUp[i].mass } else { powerUp[i].force.y += 1.04 * simulation.g * powerUp[i].mass } } } for (let i = 0; i < buttons.length; i++) { buttons[i].draw() if (buttons[i].isUp && !isFlipping) { // buttons[i].query(); buttons[i].queryPlayer(); if (!buttons[i].isUp) { isFlipping = true if (level.isFlipped) { const normalMap = function () { level.isFlipped = false flipAndRemove() buildMapOutline() buildNormalMap(); //rewrite flipped version of map simulation.draw.setPaths() //update map graphics level.addToWorld() } simulation.unFlipCameraVertical(flipAnimationCycles, normalMap) } else { const flipMap = function () { level.isFlipped = true flipAndRemove() buildMapOutline() buildVerticalFLippedMap(); //rewrite flipped version of map simulation.draw.setPaths() //update map graphics level.addToWorld() } simulation.flipCameraVertical(flipAnimationCycles, flipMap) } break } } } ctx.fillStyle = "#d4f4f4" ctx.fillRect(3575, -300, 475, 575); if (level.isFlipped) { //draw flipped entrance ctx.beginPath(); ctx.moveTo(level.enter.x, level.enter.y - 30); ctx.lineTo(level.enter.x, level.enter.y + 80); ctx.bezierCurveTo(level.enter.x, level.enter.y + 170, level.enter.x + 100, level.enter.y + 170, level.enter.x + 100, level.enter.y + 80); ctx.lineTo(level.enter.x + 100, level.enter.y - 30); ctx.lineTo(level.enter.x, level.enter.y - 30); ctx.fillStyle = "#ccc"; ctx.fill(); //draw flipped exit ctx.fillStyle = "#d4f4f4" // ctx.fillRect(-2000, 1325, 375, 350) ctx.beginPath(); ctx.moveTo(level.exit.x, level.exit.y - 30); ctx.lineTo(level.exit.x, level.exit.y + 80); ctx.bezierCurveTo(level.exit.x, level.exit.y + 170, level.exit.x + 100, level.exit.y + 170, level.exit.x + 100, level.exit.y + 80); ctx.lineTo(level.exit.x + 100, level.exit.y - 30); ctx.lineTo(level.exit.x, level.exit.y - 30); ctx.fillStyle = "#0ff"; ctx.fill(); } else { level.exit.drawAndCheck(); level.enter.draw(); } }; level.customTopLayer = () => { ctx.fillStyle = `rgba(255,255,255,${0 + 0.3 * Math.random()})` ctx.fillRect(-3500, -1075, 425, 2100); ctx.fillRect(0, -1075, 425, 2100); ctx.fillRect(3575, -1075, 425, 2100); ctx.fillStyle = "rgba(0,0,0,0.08)" ctx.fillRect(-2575, -1025, 600, 2050); ctx.fillRect(1300, -1050, 800, 2100); ctx.fillRect(2400, -1050, 800, 2100); }; // spawn.bodyRect(1900, 1875, 100, 125, 0.5); spawn.bodyRect(-2569, 825, 25, 165); spawn.randomMob(-2275, -675, 0); spawn.randomMob(-1200, 475, 0); spawn.randomMob(525, 875, 0.1); spawn.randomMob(1975, 900, 0.2); spawn.randomMob(2800, 875, 0.2); spawn.randomMob(-3275, -600, 0.3); spawn.randomMob(-1250, -900, 0.3); spawn.randomMob(-475, -600, 0.3); spawn.randomMob(-1750, 850, 0.4); spawn.randomMob(1700, 525, 0.4); spawn.randomMob(2925, 175, 0.5); spawn.randomMob(-2300, -825, 0.5); spawn.randomMob(-1625, -450, 0.6); spawn.randomMob(-225, 900, 0.6); spawn.randomMob(275, -775, 0.7); spawn.randomMob(2800, 875, 0.8); spawn.randomMob(3825, -750, 0.9); spawn.randomMob(2825, 150, 1); spawn.randomMob(-1900, 875, 1); powerUps.spawnStartingPowerUps(-825, -600); spawn.randomLevelBoss(1550, 200); spawn.secondaryBossChance(2675, -125) powerUps.addResearchToLevel() //needs to run after mobs are spawned }, substructure() { // simulation.fallHeight = 4000 level.announceMobTypes() level.setPosToSpawn(-3800, -750); level.exit.x = 3750 level.exit.y = -625 level.defaultZoom = 2000 simulation.zoomTransition(level.defaultZoom) document.body.style.backgroundColor = "#d0d5d5"; color.map = "#444" const boost1 = level.boost(-2225, 1000, 1750) const boost2 = level.boost(3400, 1000, 1750) const lasers = [] const center = { x: 2800, y: 200 } map[map.length] = Matter.Bodies.polygon(center.x, center.y, 20, 100) //center circle with lasers lasers.push(level.laser({ x: center.x, y: center.y }, { x: center.x, y: center.y })) //oscillating laser lasers[lasers.length - 1].oscillate = function () { const angle = -0.45 + Math.PI / 2 - 1.47 * Math.sin(0.02 * simulation.cycle) //oscillate around circle this.position = { x: center.x + 102 * Math.cos(angle), y: center.y + 102 * Math.sin(angle) } this.look = { x: center.x + 2000 * Math.cos(angle), y: center.y + 2000 * Math.sin(angle) } } lasers.push(level.laser({ x: center.x, y: center.y }, { x: center.x, y: center.y })) //oscillating laser lasers[lasers.length - 1].oscillate = function () { const angle = -0.45 + -Math.PI / 2 + 1.47 * Math.sin(0.02 * simulation.cycle) //oscillate around circle this.position = { x: center.x + 102 * Math.cos(angle), y: center.y + 102 * Math.sin(angle) } this.look = { x: center.x + 2000 * Math.cos(angle), y: center.y + 2000 * Math.sin(angle) } } lasers.push(level.laser({ x: -1500, y: -963 }, { x: -1500, y: 0 })) //oscillating laser lasers[lasers.length - 1].oscillate = function () { // if (this.countDown === 0) {} const angle = Math.PI / 2 + 0.6 * Math.sin(simulation.cycle * 0.02) //oscillate around down this.look = { x: this.position.x + 600 * Math.cos(angle), y: this.position.y + 600 * Math.sin(angle) } } lasers.push(level.laser({ x: 600, y: 580 }, { x: 600, y: 1000 })) //scrolling laser lasers[lasers.length - 1].oscillate = function () { this.position.x = 600 + 200 * Math.sin(simulation.cycle * 0.03) this.look.x = 600 + 400 * Math.sin(simulation.cycle * 0.03) } lasers.push(level.laser({ x: -115, y: -853 }, { x: 600, y: -50 })) if (Math.random() < 0.33) { lasers[lasers.length - 1].oscillate = function () { this.look.x = 300 + Math.abs(600 * Math.sin(simulation.cycle * 0.017)) } } else if (Math.random() < 0.5) { lasers[lasers.length - 1].oscillate = function () { this.look.x = 600 + 300 * Math.sin(simulation.cycle * 0.017) } } else { lasers[lasers.length - 1].oscillate = function () { this.look.x = 300 + (4 * simulation.cycle % 600) } } lasers.push(level.laser({ x: 2375, y: -876 }, { x: 2375, y: -300 })) //exit top lasers[lasers.length - 1].oscillate = function () { const angle = 1.4 + 1.15 * Math.sin(simulation.cycle * 0.021) //oscillate around down this.look = { x: this.position.x + 2000 * Math.cos(angle), y: this.position.y + 2000 * Math.sin(angle) } } lasers.push(level.laser({ x: 2375, y: -876 }, { x: 2375, y: -876 })) //exit top lasers[lasers.length - 1].oscillate = function () { const angle = 1.4 + 1.15 * Math.cos(simulation.cycle * 0.021) //oscillate around down this.look = { x: this.position.x + 2000 * Math.cos(angle), y: this.position.y + 2000 * Math.sin(angle) } } lasers.push(level.laser({ x: -3565, y: -915 }, { x: -3565, y: -710 })) //entrance door lasers.push(level.laser({ x: 3535, y: -915 }, { x: 3535, y: -575 })) //exit door if (Math.random() < 0.33) { lasers.push(level.laser({ x: -400, y: -713 }, { x: -400, y: -295 })) //pillar top } else if (Math.random() < 0.5) { lasers.push(level.laser({ x: -400, y: -250 }, { x: -400, y: 200 })) //pillar mid } else { lasers.push(level.laser({ x: -400, y: 250 }, { x: -400, y: 750 })) //pillar low } level.custom = () => { boost1.query(); boost2.query(); ctx.fillStyle = "#cacfcf" ctx.fillRect(2787, -425, 25, 650); ctx.fillRect(-600, -1050, 400, 1800); level.exit.drawAndCheck(); level.enter.draw(); }; level.customTopLayer = () => { ctx.fillStyle = "rgba(0,255,255,0.1)" //"#d4f4f4" //exit ctx.fillRect(3535, -1050, 500, 475); //shadows ctx.fillStyle = "rgba(0,20,60,0.09)" ctx.fillRect(-4025, -1050, 1750, 2275); ctx.fillRect(-2025, -1050, 1050, 2175); ctx.fillRect(200, 0, 800, 975); ctx.fillRect(1400, -150, 650, 1175); ctx.fillRect(2200, -425, 1175, 1475); //rotate angle of lasers for (let i = 0; i < 7; i++) lasers[i].oscillate() for (let i = 0; i < lasers.length; i++) { lasers[i].motionQuery() } }; //boxes center on zero,zero with deep walls to hide background spawn.mapRect(4000, -2000, 2000, 4000); //right map wall spawn.mapRect(-6000, -2000, 2000, 4000); //left map wall spawn.mapRect(-6000, -4000, 12000, 3000); //map ceiling spawn.mapRect(-6000, 1000, 12000, 3000); //floor //entrance spawn.mapRect(-4000, -710, 450, 1800); spawn.mapVertex(-3565, -1013, "-140 0 -8 150 8 150 140 0"); //entrance door spawn.mapVertex(-3975, -975, "0 0 100 0 0 100"); //triangle at corner spawn.mapVertex(-2900, 268, "-650 0 0 -40 650 0 650 2000 -650 2000 "); //angled floating structure spawn.mapVertex(-2900, -990, "-600 0 0 40 600 0"); //wide ceiling triangle //pillar 1 spawn.mapVertex(-1500, -350, "-550 0 0 -40 550 0 550 350 0 390 -550 350 "); spawn.mapVertex(-1500, 535, "-550 0 0 -40 550 0 550 500 0 540 -550 500 "); spawn.mapVertex(-1500, -990, "-600 0 0 40 600 0"); spawn.mapVertex(-1500, 990, "-550 0 0 -40 550 0 550 20 -550 20"); //pillar 2 spawn.mapVertex(-400, -875, "225 0 -225 0 -350 -300 350 -300"); spawn.mapRect(-600, 200, 400, 50); spawn.mapRect(-600, -300, 400, 50); spawn.mapVertex(-400, 900, "350 0 -350 0 -225 -300 225 -300"); //pillar 3 spawn.mapVertex(600, 1000, "575 0 -575 0 -450 -100 450 -100"); spawn.mapVertex(600, 500, "325 0 250 80 -250 80 -325 0 -250 -80 250 -80"); spawn.mapRect(175, 450, 850, 100); spawn.mapVertex(600, 0, "425 -20 425 20 390 50 -390 50 -425 20 -425 -20 -390 -50 390 -50"); //far right building spawn.mapRect(1600, 990, 450, 100); spawn.mapRect(2200, 990, 1175, 100); spawn.mapVertex(1500, 1015, "200 0 -200 0 -100 -100 100 -100"); spawn.mapVertex(1500, 200, "-100 100 0 0 100 0 100 1000 -100 1000"); //left wall spawn.mapRect(1550, -300, 500, 200); spawn.mapVertex(2303.5, -350, "-100 100 0 0 100 0 100 500 -100 500"); //left wall spawn.mapRect(2350, -600, 1025, 200); spawn.mapVertex(2375, -975, "-140 0 -8 150 8 150 140 0"); //laser mount //exit spawn.mapRect(3525, -600, 550, 1675); spawn.mapRect(3750, -610, 100, 50); spawn.mapVertex(3535, -1013, "-140 0 -8 150 8 150 140 0"); //entrance door spawn.mapVertex(3975, -990, "0 0 100 0 100 100"); //triangle at corner spawn.randomMob(-1150, 900, 0.1); spawn.randomMob(675, 750, 0.1); spawn.randomMob(3100, 875, 0.2); spawn.randomMob(2975, -775, 0.2); spawn.randomMob(1675, -550, 0.3); spawn.randomMob(700, -300, 0.3); spawn.randomMob(-325, -425, 0.4); spawn.randomMob(-1375, -675, 0.4); spawn.randomMob(-1425, 100, 0.5); spawn.randomMob(-500, 75, 0.6); spawn.randomMob(625, 250, 0.7); spawn.randomMob(2125, 375, 0.7); spawn.randomMob(-500, 600, 0.8); spawn.randomMob(-1950, 875, 0.8); spawn.randomMob(800, -400, 0.9); spawn.randomMob(1675, -600, 0.9); spawn.randomMob(2825, 75, 0.9); spawn.randomLevelBoss(2400, 600); spawn.secondaryBossChance(800, -300) powerUps.chooseRandomPowerUp(600, 375); powerUps.chooseRandomPowerUp(600, 925); powerUps.spawnStartingPowerUps(1750, -325); powerUps.addResearchToLevel() //needs to run after mobs are spawned powerUps.chooseRandomPowerUp(2825, 175); powerUps.chooseRandomPowerUp(2475, -650); powerUps.chooseRandomPowerUp(2100, 925); powerUps.chooseRandomPowerUp(625, -100); }, corridor() { // simulation.fallHeight = 4000 level.announceMobTypes() level.defaultZoom = 2400 simulation.zoomTransition(level.defaultZoom) document.body.style.backgroundColor = "#d0d5d5"; color.map = "#444" powerUps.chooseRandomPowerUp(5925, -2125); powerUps.chooseRandomPowerUp(75, -4225); powerUps.chooseRandomPowerUp(2950, -1450); // level.isHorizontalFlipped = true if (level.isHorizontalFlipped) { level.setPosToSpawn(14075, -625); level.exit.x = -350 level.exit.y = 505 spawn.bodyRect(13525, -675, 50, 100); var color1 = "rgba(0,20,60,0.09)" var color2 = "rgba(0,255,255,0.1)" } else { level.setPosToSpawn(-350, 475); level.exit.x = 14025 level.exit.y = -600 spawn.bodyRect(-225, 475, 50, 50); var color1 = "rgba(0,255,255,0.1)" var color2 = "rgba(0,20,60,0.09)" } spawn.mapRect(14015, -585, 120, 75); //exit/entrance platform const buttonLeft = level.button(-4100, 991) const buttonRight = level.button(4050, 991) buttonLeft.isUp = true // const buttonCamera = level.button(940, -1545) // buttonRight.isUp = false const boosts = [] boosts.push(level.boost(-3650, 990, 2700, 1.45)) boosts.push(level.boost(3325, 990, 1600, 1.4)) boosts.push(level.boost(7960, -1110, 1650, 2.3)) boosts.push(level.boost(13345, -460, 2070, 2.35)) const fizzlers = [] fizzlers.push(level.fizzler({ x: -135, y: 265 }, { x: -135, y: 535 })) fizzlers.push(level.fizzler({ x: -3850, y: 650 }, { x: -3850, y: 1025 })) fizzlers.push(level.fizzler({ x: 3875, y: 675 }, { x: 3875, y: 1025 })) fizzlers.push(level.fizzler({ x: 13425, y: -1275 }, { x: 13425, y: -550 })) const movers = [] const baseMoverSpeed = 15 movers.push(level.mover(-3550, 995, 6875, 150, -baseMoverSpeed)) movers.push(level.mover(225, -1190, 2450, 50, -baseMoverSpeed)) movers.push(level.mover(-3000, -1190, 3000, 50, baseMoverSpeed)) movers.push(level.mover(4000, -2025, 2000, 150, -23)) movers.push(level.mover(8000, -1125, 2000, 150, -23)) movers.push(level.mover(3775, -425, 1650, 150, 20)) movers.push(level.mover(5425, -425, 1925, 150, 40)) movers.push(level.mover(7350, -425, 6000, 150, 60)) function setMoverDirection(index, VxGoal, force) { movers[index].VxGoal = VxGoal movers[index].force = force } level.custom = () => { // buttonCamera.query() // if (!buttonCamera.isUp) { // simulation.setCameraPosition(100, -1000, 0.29) // //block spawner // spawn.mapRect(0, -2375, 200, 100); // if (!(simulation.cycle % 10) && !m.isTimeDilated && body.length < 200) { // const where = { x: 112, y: -3800 } // // simulation.drawList.push({ x: where.x + 100 * (Math.random() - 0.5), y: where.y + 100 * (Math.random() - 0.5), radius: 11, color: "rgba(0,160,255,0.5)", time: 10 }); // let makeBlock = function (where, size) { // const sides = Math.floor(4 + 6 * Math.random() * Math.random()) // body[body.length] = Matter.Bodies.polygon(where.x, where.y, sides, size, { // friction: 0.05, // frictionAir: 0.001, // collisionFilter: { // category: cat.body, // mask: cat.player | cat.map | cat.body | cat.bullet | cat.mob | cat.mobBullet // }, // classType: "body", // density: 0.001, // }); // const who = body[body.length - 1] // Composite.add(engine.world, who); //add to world // } // makeBlock({ x: where.x, y: where.y }, Math.floor(20 + 35 * Math.random() * Math.random())) // } // } ctx.fillStyle = "#c8cccc"//background color is "#d0d5d5" ctx.fillRect(-2150, 675, 500, 400); ctx.fillRect(-1050, 675, 500, 400); ctx.fillRect(750, 675, 500, 400); ctx.fillRect(1850, 675, 500, 400); ctx.fillRect(-2250, -2425, 700, 1325); ctx.fillRect(-1150, -2400, 700, 1300); ctx.fillRect(650, -2375, 700, 1325); ctx.fillRect(1750, -2375, 700, 1350); ctx.fillRect(8000, -2425, 2000, 2225); ctx.fillRect(4000, -2375, 2000, 2100); ctx.fillRect(11125, -2425, 1000, 2150) level.exit.drawAndCheck(); level.enter.draw(); if (buttonRight.isUp) { buttonRight.query(); if (!buttonRight.isUp) { requestAnimationFrame(() => buttonLeft.isUp = true); setMoverDirection(0, -baseMoverSpeed, -0.0005) const list = Matter.Query.region(body, buttonLeft) //are any blocks colliding with this if (list.length > 0) Matter.Body.setVelocity(list[0], { x: baseMoverSpeed, y: -20 }); } } else if (buttonLeft.isUp) { buttonLeft.query(); if (!buttonLeft.isUp) { requestAnimationFrame(() => buttonRight.isUp = true); setMoverDirection(0, 20, 0.0005) const list = Matter.Query.region(body, buttonRight) //are any blocks colliding with this if (list.length > 0) Matter.Body.setVelocity(list[0], { x: -15, y: -20 }); } } buttonRight.draw(); buttonLeft.draw(); for (let i = 0; i < movers.length; i++) movers[i].push(); for (let i = 0; i < boosts.length; i++) boosts[i].query(); }; level.customTopLayer = () => { for (let i = 0; i < fizzlers.length; i++) fizzlers[i].query(); ctx.fillStyle = color1 //exit ctx.fillRect(13400, -1325, 1000, 825); //shadows ctx.fillStyle = color2 ctx.fillRect(-500, 225, 494, 350); ctx.fillStyle = "rgba(0,5,10,0.06)" ctx.beginPath(); ctx.moveTo(0, -1180) ctx.lineTo(225, -1180) ctx.lineTo(3220, 669) ctx.lineTo(3180, 1010) ctx.lineTo(-2960, 1010) ctx.lineTo(-2995, 674) ctx.fill() ctx.beginPath(); //right button room ctx.beginPath(); ctx.moveTo(3780, 720) ctx.lineTo(4325, 720) ctx.lineTo(4325, 1010) ctx.lineTo(3810, 1010) ctx.fill() //left button room ctx.beginPath(); ctx.moveTo(-3755, 675) ctx.lineTo(-3785, 1010) ctx.lineTo(-4250, 1010) ctx.lineTo(-4250, 675) ctx.fill() ctx.fillStyle = "rgba(68, 68, 68,0.9)" ctx.fillRect(-50, -4300, 325, 1950); for (let i = 0; i < movers.length; i++) movers[i].draw(); }; spawn.mapRect(-6000, 1000, 12000, 3000); //floor spawn.mapRect(-6000, -4300, 6020, 1950); spawn.mapRect(205, -4300, 15120, 1950); spawn.mapVertex(-250, 602.5, "-200 0 235 0 400 50 400 150 -200 150"); spawn.mapVertex(-3675, -2275, "0 0 500 0 0 500"); spawn.mapVertex(13275, -2275, "0 0 -500 0 0 500"); spawn.mapRect(-525, -1175, 525, 1450); spawn.mapRect(225, -1175, 3000, 1850); spawn.mapRect(-3000, -1175, 2525, 1850); spawn.mapRect(-4350, -2500, 600, 3175); spawn.mapRect(-6000, -2350, 1775, 3350); spawn.mapVertex(-1900, 675, "-350 0 -250 100 250 100 350 0"); spawn.mapVertex(-800, 675, "-350 0 -250 100 250 100 350 0"); spawn.mapVertex(1000, 675, "-350 0 -250 100 250 100 350 0"); spawn.mapVertex(2100, 675, "-350 0 -250 100 250 100 350 0"); spawn.mapVertex(-1900, -1450, "-400 -40 -350 -90 350 -90 400 -40 400 40 350 90 -350 90 -400 40"); spawn.mapVertex(-800, -1450, "-400 -40 -350 -90 350 -90 400 -40 400 40 350 90 -350 90 -400 40"); spawn.mapVertex(1000, -1450, "-400 -40 -350 -90 350 -90 400 -40 400 40 350 90 -350 90 -400 40"); spawn.mapVertex(2100, -1450, "-400 -40 -350 -90 350 -90 400 -40 400 40 350 90 -350 90 -400 40"); spawn.mapVertex(-1900, -2350, "-450 0 -350 100 350 100 450 0"); spawn.mapVertex(-800, -2350, "-450 0 -350 100 350 100 450 0"); spawn.mapVertex(1000, -2350, "-450 0 -350 100 350 100 450 0"); spawn.mapVertex(2100, -2350, "-450 0 -350 100 350 100 450 0"); spawn.mapRect(-1500, 840, 300, 20); spawn.mapRect(1400, 840, 300, 20); //ramp to catch blocks spawn.mapVertex(3001, -1260, "0 0 400 -200 550 -200 550 75 0 75"); spawn.mapVertex(4100, -1100, "-625 0 -600 -60 600 -60 625 0 600 60 -600 60"); spawn.mapVertex(5550, -750, "-625 0 -600 -60 600 -60 625 0 600 60 -600 60"); spawn.mapVertex(11625, -900, "-525 0 -500 -50 500 -50 525 0 500 50 -500 50"); //base for mover spawn.mapVertex(5000, -1935, "-1050 0 -1000 -90 1000 -90 1050 0 1000 90 -1000 90"); spawn.mapVertex(9000, -1035, "-1050 0 -1000 -90 1000 -90 1050 0 1000 90 -1000 90"); spawn.mapVertex(5000, -2370, "-1200 0 -1000 100 1000 100 1200 0"); spawn.mapVertex(9000, -2310, "-1200 0 -1000 100 1000 100 1200 0"); spawn.mapVertex(11625, -2310, "-600 0 -500 100 500 100 600 0"); spawn.mapRect(3775, -400, 675, 1125); spawn.mapRect(4300, -400, 11025, 4400); //exit spawn.mapRect(13400, -575, 1925, 300); spawn.mapRect(14275, -2375, 1050, 2050); spawn.mapRect(13400, -2375, 900, 1125); //blocks on movers spawn.bodyRect(-200, 950, 50, 50); spawn.bodyRect(-1100, 925, 65, 75); spawn.bodyRect(-2275, 975, 70, 25); spawn.bodyRect(-3325, 925, 75, 75); spawn.bodyRect(-2950, -1225, 90, 25); spawn.bodyRect(-1425, -1275, 45, 75); spawn.bodyRect(600, -1275, 70, 75); spawn.bodyRect(1900, -1225, 90, 50); spawn.bodyRect(4250, -2100, 115, 50); spawn.bodyRect(2175, 900, 100, 65); spawn.bodyRect(4075, -450, 75, 20); spawn.bodyRect(8350, -1175, 90, 50); spawn.bodyRect(6525, -525, 70, 100); spawn.bodyRect(12025, -475, 130, 50); spawn.bodyRect(625, 950, 55, 45); spawn.bodyRect(6250, -450, 55, 25); spawn.bodyRect(3950, -475, 46, 53); //other blocks spawn.bodyRect(3525, -1300, 100, 125, 0.6); spawn.bodyRect(11550, -1150, 100, 200, 0.4); spawn.randomMob(-1775, -1650, 0); spawn.randomMob(950, -1775, 0); spawn.randomMob(1550, 775, 0); spawn.randomMob(4500, -1250, 0); spawn.randomMob(11400, -1300, 0); spawn.randomMob(-800, -1675, 0); spawn.randomMob(-1325, 775, 0.1); spawn.randomMob(2050, -1625, 0.1); spawn.randomMob(3100, -1475, 0.2); spawn.randomMob(5400, -900, 0.2); spawn.randomMob(11950, -1025, 0.3); spawn.randomMob(-925, -1700, 0.3); spawn.randomMob(2025, -1725, 0.4); spawn.randomMob(1575, 775, 0.4); spawn.randomMob(-1350, 775, 0.6); spawn.randomMob(11925, -1275, 0.6); spawn.randomMob(4325, -1425, 0.6); spawn.randomMob(5425, -950, 0.6); spawn.randomMob(3575, 375, 0.6); spawn.randomGroup(5300, -1400, 1.3); spawn.randomLevelBoss(2025, -1825, ["pulsarBoss", "shieldingBoss", "laserLayerBoss", "shooterBoss"]); spawn.secondaryBossChance(-1900, -1800, ["historyBoss", "spawnerBossCulture", "blockBoss"]); powerUps.spawnStartingPowerUps(11750, -1000); powerUps.addResearchToLevel() //needs to run after mobs are spawned }, lock() { level.announceMobTypes() 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() { level.announceMobTypes() 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 } }, flocculation() { level.announceMobTypes() const button0 = level.button(1125, 795) const button1 = level.button(6538, 2670) const button2 = level.button(1225, -100) button0.isUp = true button1.isUp = true button2.isUp = true // const hazard = level.hazard(4550, 2750, 4550, 150) const hazard = level.hazard(simulation.isHorizontalFlipped ? -7200 : 675, 50, 7500, 3000) //1869 // hazard.min.y = 3000 //REMOVE THIS IN LIVE VERSION!!!!! set slime to lowest level let balance1, balance2, balance3, rotor1, rotor2 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) //up mode triggered by player contact const elevator0 = level.elevator(700, 1865, 200, 490, 1400, 0.011, { up: 0.01, down: 0.7 }) const elevator1 = level.elevator(3995, 2335, 210, 150, 1700, 0.011, { up: 0.01, down: 0.7 }) level.custom = () => { drip1.draw(); drip2.draw(); drip3.draw(); if (button0.isUp) { button0.query(); if (!button0.isUp) { //summon second set of mobs //1 boss, 1-2 groups, 11 mobs (all on lower ground level, where the slime is leaving) spawn.randomMob(918, 2695, 0); spawn.randomMob(1818, 2719, 0.1); spawn.randomMob(2530, 2460, 0.1); spawn.randomMob(3109, 2665, 0.2); spawn.randomMob(3909, 2191, 0.2); spawn.randomMob(4705, 2711, 0.3); spawn.randomMob(5800, 2796, 0.3); spawn.randomMob(7287, 2757, 0.4); spawn.randomMob(5759, 2691, 0.4); spawn.randomMob(5675, 2225, 0.5); spawn.randomMob(7450, 2775, 0.5); spawn.randomGroup(6600, 2400, 0.1); if (simulation.difficulty > 1) spawn.randomLevelBoss(6076, 2341); } } button0.draw(); if (button1.isUp) button1.query(); button1.draw(); if (button2.isUp) button2.query(); button2.draw(); ctx.fillStyle = "hsl(175, 15%, 76%)" ctx.fillRect(7625, 2625, 400, 300) ctx.fillStyle = "rgba(0,0,0,0.03)" //shadows ctx.fillRect(6250, 1875, 700, 875) ctx.fillRect(900, 1200, 600, 1725) //900, 1350, 600, 1600); ctx.fillRect(3000, 1200, 1000, 1750); ctx.fillRect(2200, 625, 400, 2050); level.exit.drawAndCheck(); level.enter.draw(); }; level.customTopLayer = () => { elevator0.moveOnTouch() elevator1.moveOnTouch() rotor1.rotate(); rotor2.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(balance5.center.x, balance5.center.y) ctx.arc(balance5.center.x, balance5.center.y, 9, 0, 2 * Math.PI); ctx.moveTo(rotor1.center.x, rotor1.center.y) ctx.arc(rotor1.center.x, rotor1.center.y, 9, 0, 2 * Math.PI); ctx.moveTo(rotor2.center.x, rotor2.center.y) ctx.arc(rotor2.center.x, rotor2.center.y, 9, 0, 2 * Math.PI); ctx.fill(); hazard.query(); const drainRate = Math.max(1, 4 - hazard.min.y / 800) hazard.level( (button2.isUp || hazard.height < 1150) && (button0.isUp || hazard.height < 350) && button1.isUp , drainRate) //true = hold, false = lower exitDoor.isClosing = hazard.min.y < 2900 exitDoor.openClose(); exitDoor.draw(); }; level.setPosToSpawn(0, -50); //normal spawn spawn.mapRect(level.enter.x, level.enter.y + 20, 100, 20); level.exit.x = 7800; level.exit.y = 2865; const exitDoor = level.door(7637, 2680, 25, 225, 195, 5) 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, 6); //16 debris per level spawn.debris(750, 2550, 2250, 6); //16 debris per level spawn.mapRect(-500, -600, 200, 800); //left entrance wall spawn.mapRect(-400, -600, 3550, 200); //ceiling spawn.mapRect(-400, 0, 1400, 600); spawn.mapRect(575, 475, 250, 250); Matter.Body.setAngle(map[map.length - 1], map[map.length - 1].angle - Math.PI / 4); spawn.mapRect(4075, 75, 250, 250); Matter.Body.setAngle(map[map.length - 1], map[map.length - 1].angle - Math.PI / 4); spawn.mapRect(4259, 1559, 282.5, 282.5); Matter.Body.setAngle(map[map.length - 1], map[map.length - 1].angle - Math.PI / 4); spawn.mapRect(3140, -600, 200, 800); //right down tube wall spawn.mapRect(3150, 0, 1200, 200); //tube right exit ceiling spawn.mapVertex(2400, 500, "-200 -100 -100 -200 100 -200 200 -100 200 200 -200 200"); spawn.mapVertex(2400, 1200, "-200 -200 200 -200 200 100 100 200 -100 200 -200 100"); spawn.mapVertex(1200, 2150, "-300 -300 300 -300 300 200 200 300 -200 300 -300 200"); spawn.mapVertex(1200, 1100, "-300 -200 -200 -300 200 -300 300 -200 300 300 -300 300"); spawn.mapVertex(3500, 950, "-500 -450 -400 -550 400 -550 500 -450 500 450 400 550 -400 550 -500 450"); spawn.mapVertex(3500, 1990, "-300 -40 -230 -110 230 -110 300 -40 300 40 230 110 -230 110 -300 40"); // spawn.mapVertex(2400, 1940, "-200 -40 -150 -90 150 -90 200 -40 200 40 150 90 -150 90 -200 40"); spawn.mapRect(2200, 1850, 400, 200); spawn.bodyRect(3825, 2240, 150, 75, 0.5); spawn.mapVertex(3500, 2452, "-500 -135 500 -135 500 35 400 135 -400 135 -500 35"); spawn.mapVertex(1200, 2850, "-500 -100 -550 -50 500 -100 550 -50 550 300 -550 300"); // spawn.mapVertex(1200, 2875, "-400 0 -300 -100 300 -100 400 0"); spawn.mapVertex(1317, 275, "-500 0 -300 -200 300 -200 550 50 550 500 -500 500"); spawn.mapVertex(1300, -357, "-300 0 -400 -100 400 -100 300 0"); spawn.bodyRect(1550, -308, 50, 208, 0.5); spawn.bodyRect(2000, 965, 525, 25, 0.5); spawn.mapVertex(2400, 2850, "-350 -50 -300 -100 300 -100 350 -50 350 300 -350 300"); spawn.mapVertex(6600, 1925, "-350 0 -450 -100 450 -100 350 0"); spawn.mapVertex(6600, 2875, "-400 -50 -350 -100 350 -100 400 -50 400 300 -400 300"); spawn.bodyRect(2375, 300, 100, 100, 0.6); spawn.bodyRect(1025, 1775, 100, 75, 0.6); spawn.bodyRect(1250, 1825, 50, 25, 0.6); spawn.bodyRect(3700, 275, 125, 125, 0.6); spawn.bodyRect(5875, 2725, 200, 200, 0.6); spawn.bodyRect(6900, 2590, 50, 50, 0.6); spawn.mapRect(4200, 2325, 250, 625); spawn.mapRect(-500, 50, 1200, 3050); spawn.mapRect(-500, 2900, 9600, 775); spawn.mapRect(4400, 0, 4700, 1900); spawn.mapRect(4200, 0, 200, 1700); // spawn.mapRect(6250, 2675, 700, 325); // spawn.mapRect(6250, 1875, 700, 150); //exit room spawn.mapRect(8000, 1775, 1100, 1375); spawn.mapRect(7625, 1825, 450, 825); spawn.mapRect(7625, 2625, 50, 75); spawn.mapRect(7625, 2890, 400, 25); spawn.mapRect(7800, 2880, 100, 25); spawn.randomMob(2450, 250, 0); spawn.randomMob(3250, 325, 0); spawn.randomMob(3625, 350, 0.1); spawn.randomMob(1750, -25, 0.1); spawn.randomMob(1300, 1750, 0.2); spawn.randomMob(2350, 1725, 0.2); spawn.randomMob(3350, 1775, 0.2); spawn.randomMob(1025, 750, 0.3); spawn.randomMob(2400, 1775, 0.3); spawn.randomMob(1250, 1725, 0.3); spawn.randomMob(775, 1775, 0.4); powerUps.addResearchToLevel() //needs to run after mobs are spawned spawn.secondaryBossChance(1822, 1336) if (simulation.isHorizontalFlipped) { //flip the map horizontally level.flipHorizontal(); //only flips map,body,mob,powerUp,cons,consBB, exit rotor1 = 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) { rotor2 = level.rotor(-2175, 1900, 650, 50, 0.001, 0, 0.01, 0, 0.0007) balance1 = level.rotor(-800 - 25, -395, 25, 390, 0.001) //entrance balance2 = level.rotor(-2605 - 390, 500, 390, 25, 0.001) //falling balance3 = level.rotor(-2608 - 400, 1950, 400, 25, 0.001) //falling balance5 = level.rotor(-2605 - 390, 1020, 390, 25, 0.001) //falling button1.min.x = -button1.min.x - 126 button1.max.x = -button1.max.x + 126 button0.min.x = -button0.min.x - 126 button0.max.x = -button0.max.x + 126 button2.min.x = -button2.min.x - 126 button2.max.x = -button2.max.x + 126 // flip the button horizontally drip1.x *= -1 drip2.x *= -1 drip3.x *= -1 elevator0.holdX *= -1 elevator1.holdX *= -1 // console.log(hazard) hazard.min.x -= 840 hazard.max.x -= 840 level.custom = () => { drip1.draw(); drip2.draw(); drip3.draw(); if (button0.isUp) { button0.query(); if (!button0.isUp) { //summon second set of mobs //1 boss, 1-2 groups, 11 mobs (all on lower ground level, where the slime is leaving) spawn.randomMob(-7475, 2800, 0); spawn.randomMob(-6475, 2500, 0.1); spawn.randomMob(-4575, 2775, 0.2); spawn.randomMob(-7575, 2850, 0.2); spawn.randomMob(-6425, 2575, 0.2); spawn.randomMob(-5750, 2775, 0.3); spawn.randomMob(-4675, 2800, 0.3); spawn.randomMob(-3425, 2800, 0.4); spawn.randomMob(-2475, 2475, 0.4); spawn.randomMob(-3350, 2250, 0.5); spawn.randomMob(-1275, 2725, 0.5); spawn.randomGroup(-6225, 2400, 0.1); if (simulation.difficulty > 1) spawn.randomLevelBoss(-6250, 2350); } } button0.draw(); if (button1.isUp) button1.query(); button1.draw(); if (button2.isUp) button2.query(); button2.draw(); rotor1.rotate(); rotor2.rotate(); ctx.fillStyle = "hsl(175, 15%, 76%)" ctx.fillRect(-8025, 2625, 400, 300) ctx.fillStyle = "rgba(0,0,0,0.03)" //shadows ctx.fillRect(-6950, 1875, 700, 875) ctx.fillRect(-4000, 1400, 1000, 1550); ctx.fillRect(-2600, 675, 400, 2025); ctx.fillRect(-1500, 1375, 600, 1500); level.exit.drawAndCheck(); level.enter.draw(); }; // level.customTopLayer = () => {}; } else { rotor1 = 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) { rotor2 = level.rotor(1525, 1900, 650, 50, 0.001, 0, 0.01, 0, -0.0007) balance1 = level.rotor(800, -395, 25, 390, 0.001) //entrance balance2 = level.rotor(2605, 500, 390, 25, 0.001) //falling balance3 = level.rotor(2608, 1950, 400, 25, 0.001) //falling balance5 = level.rotor(2605, 1020, 390, 25, 0.001) //falling } }, satellite() { level.announceMobTypes() level.fallMode = "start"; 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, 6); //16 debris per level // 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); //steps up and down spawn.mapVertex(4525, 250, "-650 0 -625 -20 625 -20 650 0"); spawn.mapVertex(4525, 237, "-550 0 -525 -20 525 -20 550 0"); // spawn.mapVertex(4525, 225, "-400 0 -375 -20 375 -20 400 0"); 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() { level.announceMobTypes() // level.fallPosition = { x: 5000, y:-4000} 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"; // level.fallMode = "start"; 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) } }; } level.fallMode = "position"; //must set level.fallModeBounds in this mode to prevent player getting stuck left or right if (level.enter.x > level.exit.x) { level.fallModeBounds = { left: level.exit.x, right: level.enter.x } //used with level.fallMode = "position"; } else { level.fallModeBounds = { left: level.enter.x, right: level.exit.x } //used with level.fallMode = "position"; } }, aerie() { level.announceMobTypes() level.fallMode = "start"; 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() }; 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() { level.announceMobTypes() level.fallMode = "start"; 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() { level.announceMobTypes() level.fallMode = "start"; 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.announceMobTypes() level.fallMode = "start"; 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() { level.announceMobTypes() 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.inGameConsole(`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.inGameConsole(`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.inGameConsole(`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.inGameConsole(`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.inGameConsole(`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.inGameConsole(`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.inGameConsole(`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.inGameConsole(`vats by Dablux`); level.defaultZoom = 1500 simulation.zoomTransition(level.defaultZoom) 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.inGameConsole(`"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.inGameConsole(`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, 30); powerUps.spawn(3900, -3100, "heal", true, 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.inGameConsole(`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()) { 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.inGameConsole(`UNKNOWN: "Well done. Now climb."`, 600); simulation.inGameConsole(`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.inGameConsole("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.inGameConsole("UNKNOWN: \"You cannot kill them.\"", 600); }, 2000); setTimeout(() => { simulation.inGameConsole("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.inGameConsole("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.inGameConsole("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.inGameConsole("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.inGameConsole("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.inGameConsole(`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"); }, temple() { simulation.inGameConsole(`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 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, 30 * (simulation.healScale ** 0.25) * Math.sqrt(tech.largerHeals) * Math.sqrt(0.1 + Math.random() * 0.5)); }; me.damageReduction = 0.25 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, 30 * (simulation.healScale ** 0.25) * Math.sqrt(tech.largerHeals) * Math.sqrt(0.1 + Math.random() * 0.5)); }; me.damageReduction = 0.25 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 : 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 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 } } } 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++) { if (mob[i] && !mob[i].isDarkMatter) { mob[i].isWIMP = true; } } relocateWIMPs(0, -10030); 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); simulation.zoomTransition(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.2 : 1) + 0.001; if (m.energy > DRAIN && !tech.isEnergyHealth) { m.energy -= DRAIN; } m.damage(0.00015 * (tech.isRadioactiveResistance ? 0.2 : 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 * 90 + 180; }, 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 - 180) { const multCoeff = (cycle - this.cap + 180) * 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) 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); 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, 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 == 60) { makeLore("Not so fast."); } if (templePlayer.startAnim < 180) { trapPlayer(1000, templePlayer.initialTrapY); } else { level.exit.x = 4500; level.exit.y = -2030; relocateWIMPs(level.exit.x, level.exit.y); relocateTo(50, -2050); simulation.fallHeight = -1000; level.defaultZoom = 1800 simulation.zoomTransition(level.defaultZoom) templePlayer.startAnim = -1; templePlayer.drawExit = false; } }, room1() { const frame = templePlayer.room1ToRoom2Anim; if (frame <= 0) return; if (frame === 1) { level.exit.x = -50; level.exit.y = -10030; makeLore("Pathetic."); } if (frame >= 1 && frame <= 360) { const factor = 100 - 100 * Math.cos((frame / 90) * Math.PI); ctx.translate(factor, factor); Promise.resolve().then(() => { ctx.save(); ctx.globalCompositeOperation = "color-burn"; ctx.fillStyle = DrawTools.randomColours((frame) * (360 - frame) / 32400); DrawTools.updateRandomColours(5); ctx.fillRect(0, 0, canvas.width, canvas.height); ctx.restore(); }); } if (frame === 180) { makeLore("You are trying too hard."); relocateTo(0, -7050); simulation.fallHeight = -6000; templePlayer.stage = 2; relocateWIMPs(level.exit.x, level.exit.y - 3000); } 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 >= 1 && templePlayer.room2ToRoom3Anim <= 180) { canvas.style.filter = `sepia(${templePlayer.room2ToRoom3Anim / 180}) invert(${templePlayer.room2ToRoom3Anim / 180})`; } if (templePlayer.room2ToRoom3Anim === 180) { 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); } } 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 = ""; } if (templePlayer.room2ToRoom3Anim > 180 && templePlayer.room2ToRoom3Anim <= 360) { canvas.style.filter = `sepia(${(360 - templePlayer.room2ToRoom3Anim) / 180}) invert(${(360 - templePlayer.room2ToRoom3Anim) / 180})`; } templePlayer.room2ToRoom3Anim++; }, room3() { if (templePlayer.room3ToEndAnim <= 0) return; if (templePlayer.room3ToEndAnim === 1) { const x = "Nooooooooooo".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(x.join("")); } 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) / 30})`; ctx.fillRect(0, 0, canvas.width, canvas.height); ctx.restore(); }); if (simulation.cycle - templePlayer.clearedCycle > 30) 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.inGameConsole("" + 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.isDarkMatter || 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 < 540 ]; 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 * (540 - 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 === 540) { thirdRoomBoss(1800, -13700); for (let i = 0; i < 3; i++) { powerUps.spawn(m.spawnPos.x, m.spawnPos.y, "heal"); } } } }; const DrawTools = { randomColours(alpha = 1) { return `rgba(${this._randomColours.join(",")},${alpha})` }, _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].isDarkMatter) { 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.inGameConsole(`

${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.inGameConsole(`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.inGameConsole(`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 = cage.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()) { 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 // 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.inGameConsole(`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.inGameConsole(`Ω: 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 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); } 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.inGameConsole(`
A stong force pushes you forward...
`) textlogOne++; } if (m.pos.x < -3000) { Matter.Body.setVelocity(player, { x: 99, y: 19 }); if (textlogTwo == 0) simulation.inGameConsole(`
A strong force pushes you away...
`); textlogTwo++; } if (m.pos.y > 1055) { Matter.Body.setPosition(player, { x: 0, y: -150 }); simulation.inGameConsole(`
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.inGameConsole(`
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.inGameConsole(`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.inGameConsole(`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.inGameConsole(`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.inGameConsole(`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.inGameConsole(`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.isTimeDilated) { 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(1250, -1900, 1050, 50); spawn.mapRect(1250, -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 }); const lightButton = level.button(1400, -1900); lightButton.isUp = true; var lightOn = false; simulation.ephemera.push({ name: "lightWire", do() { if (level.levels[level.onLevel] == "clock") { // light wire ctx.beginPath(); ctx.moveTo(1460, -1887); ctx.lineTo(1300, -1887); ctx.lineTo(1300, -2860); ctx.lineTo(400, -2860); ctx.lineTo(400, -2800); ctx.lineWidth = 6; ctx.strokeStyle = lightOn ? "#ffd700" : "000"; ctx.stroke(); } else { simulation.removeEphemera(this.name); } }, }) 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.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, 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 = () => { if (lightOn) { Matter.Body.setPosition(circleHead, m.pos) if (!(compareArrays(oldMap, map) && compareArrays(oldMob, mob))) losDomain = generateIntersectMap().concat(mob.filter((obj) => { return obj.isNotCloaked == null && (obj.isBoss || obj.label != 'Circle Body') }), [pendulum1, gear1, gear2, player, circleHead]); 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 if (lightOn) { var lightPos = { x: 400, y: -2775 }; var lightRadius = 2950; const vertices = circleLoS(lightPos, lightRadius, map.concat(mob.filter((obj) => { return obj.isNotCloaked == null && (obj.isBoss || obj.label != 'Circle Body') }), [pendulum1, gear1, gear2, player, circleHead])); if (vertices.length > 0 && vertices[0].x) { 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 = lightOn ? "#ffe245" : "transparent"; 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.isTimeDilated) 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 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 } 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 = () => { if (!lightOn) lightButton.query(); if (!lightButton.isUp) lightOn = true; lightButton.draw(); 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.inGameConsole(`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.inGameConsole(`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.inGameConsole(`Downpour by DesBoot`); /* NEW CHANGES: Added lights in the buildings activate when lever is flicked Changed lightning: now has a chance to strike twice in a row Small map changes Slight rework of the start Added sounds: thunder buzz from lights */ //BUILD EVERYTHING const laser = level.hazard(7492, -2612, 10, 500, 0.3) //laserintro 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 mutetoggle = level.toggle(100, 0) 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) 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); const slime5 = level.hazard(-2000, 10, 200, 3000); const drip1 = level.drip(1750, -700, 0, 70) const oldOnLevel = level.levelsCleared; let whereToDrip = Math.random() * 2; const mainDropRange = (min, max) => Math.random() * (max - min) + min const amount = Math.round(15 + 20 * Math.random()) const drips = [] for (let i = 0; i < amount; i++) { if (whereToDrip < 1.25) { const locX = mainDropRange(3800, 6000)//2200, 2300 drips.push(level.drip(locX, -1700, -800, 200 + Math.random() * 500)) } else { const locX = mainDropRange(4900, 7100)//2200, 2300 drips.push(level.drip(locX, 200, 1200, 200 + Math.random() * 500)) } whereToDrip = Math.random() * 2; } //ADD MORE // simulation.enableConstructMode() //LEVEL SETUP AND VARIABLES level.setPosToSpawn(0, -50); //normal spawn level.exit.x = 13130.3; level.exit.y = -370; level.defaultZoom = 1800 let rainCount = 1 let hasDoubleFlashed = false; let lightningTime = 0; let canBePushed = false; let rainXtemp1 = 0; let rainXtemp2 = 0; let stopcycle = 0 let flashcycle = Math.round(Math.random() * 25 + 260) let mobsspawned = 0 let distanceToLight1 = 0; let distanceToLight2 = 0; let customExitTimer = 0; 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 simulation.zoomTransition(level.defaultZoom) document.body.style.backgroundColor = "#2e416e";//d8dadf // color.map = "#444" //custom map color //SOUNDS let thunder1 = new Audio('https://github.com/des-boot/n-gon-downpour-sound-effects/blob/main/thunder1.mp3?raw=true'); let chemicalLove = new Audio('https://github.com/des-boot/n-gon-downpour-sound-effects/blob/main/Chemical%20Love%20thunder.mp3?raw=true'); chemicalLove.play(); let thunder2 = new Audio('https://github.com/des-boot/n-gon-downpour-sound-effects/blob/main/thunder2.mp3?raw=true'); let thunder3 = new Audio('https://github.com/des-boot/n-gon-downpour-sound-effects/blob/main/thunder3.wav?raw=true'); let thunder4 = new Audio('https://github.com/des-boot/n-gon-downpour-sound-effects/blob/main/thunder4.wav?raw=true'); let ambiance1 = new Audio('https://github.com/des-boot/n-gon-downpour-sound-effects/blob/main/buzz%20(1).wav?raw=true'); let rain1 = new Audio('https://github.com/des-boot/n-gon-downpour-sound-effects/blob/main/light-rain-109591.mp3?raw=true'); let rain3 = new Audio('https://github.com/des-boot/n-gon-downpour-sound-effects/blob/main/8mb.video-ggm-Jd62jXAH.m4a?raw=true'); rain1.volume = 0.125; rain3.volume = 0.125; thunder1.volume = 0.25; thunder2.volume = 0.25; thunder3.volume = 0.25; thunder4.volume = 0.25; //simulation.inGameConsole(stopcycle) level.custom = () => { for (const drip of drips) drip.draw() drip1.draw(); // drip1.x = Math.random() * 500 + 1630 // if (false) { // rain1.pause(); // rain2.pause(); // rain3.pause(); // thunder1.pause(); // thunder2.pause(); // thunder3.pause(); // thunder4.pause(); // ambiance1.pause(); // } 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) // do { if (simulation.paused) { rain1.pause(); // rain2.pause(); rain3.pause(); thunder1.pause(); thunder2.pause(); thunder3.pause(); thunder4.pause(); } else { // if (!mutetoggle.isOn) rain3.play(); if (player.position.x > 3100 && player.position.y > -1700) { rain1.pause(); // rain2.pause(); rain3.volume = 0.025; } else { if (player.position.x > 2600 && player.position.x < 3200) { rain1.volume = (3200 - player.position.x) / 4000 // rain2.volume = (3100 - player.position.x) / 2000 rain3.volume = (3200 - player.position.x) / 4000 } if (player.position.y > -2000 && player.position.y < -1700) { // rain1.volume = -1 * (1700 + player.position.y) / 3000 rain1.volume = (-3 * player.position.y) / 68000 - 0.05 // rain2.volume = (3100 - player.position.x) / 2000 rain3.volume = -1 * (1700 + player.position.y) / 3000 } // if (!mutetoggle.isOn) rain1.play(); // rain2.play(); } } distanceToLight1 = Math.sqrt((player.position.x - 6300) * (player.position.x - 6300) + (player.position.y - 212) * (player.position.y - 212)) distanceToLight2 = Math.sqrt((player.position.x - 4877) * (player.position.x - 4877) + (player.position.y + 1690) * (player.position.y + 1690)) if (doortoggle.isOn) { if (simulation.paused) { //is it paused ambiance1.pause(); } else { if (distanceToLight1 < 2000 || distanceToLight2 < 2000) { // is M close enough // if (!mutetoggle.isOn) ambiance1.play(); if (distanceToLight2 < distanceToLight1) { // check for distance and set volume ambiance1.volume = (1 - ((distanceToLight2) / 2000)) } else { ambiance1.volume = (1 - ((distanceToLight1) / 2000)) } } else { ambiance1.pause(); } } } if (mutetoggle.isOn) { // simulation.inGameConsole(isMuted) muteAll(); } else { // simulation.inGameConsole(isMuted) rain1.muted = false ambiance1.muted = false rain3.muted = false thunder1.muted = false thunder2.muted = false thunder3.muted = false thunder4.muted = false } //mute volumes // rain1.volume = rain1.volume * ismuted; // // simulation.inGameConsole(ismuted) // ambiance1.volume = ambiance1.volume * ismuted; // rain3.volume = rain3.volume * ismuted; // thunder1.volume = thunder1.volume * ismuted; // thunder2.volume = thunder2.volume * ismuted; // thunder3.volume = thunder3.volume * ismuted; // thunder4.volume = thunder4.volume * ismuted; do { ctx.fillStyle = "rgba(242, 255, 0, 0.3})" ctx.fillStyle = `rgba(242,255,0,${(Math.round(Math.random + 0.3)) / 3})` ctx.fillStyle = "rgba(242,255,0,0.3)" if (doortoggle.isOn) { ctx.beginPath() ctx.moveTo(6325, 212) ctx.lineTo(6325 - 75, 212) ctx.lineTo((6325 - 75) - 338, 212 + 338) ctx.lineTo(6325 + 10, 212 + 338) ctx.lineTo(6325 + 29.97, 212 + 1018) ctx.lineTo(6325 + 597.4443, 212 + 1018) //at an angle to the right platform ctx.lineTo((6325 + 75) + 375, 212 + 763) ctx.lineTo((6325 + 75) + 375, 212 + 688) ctx.lineTo((6325 + 75) + 688, 212 + 688) ctx.lineTo((6325 + 75) + 100, 212 + 100) ctx.lineTo(6325 + 75, 212) ctx.fill() //4875, -1688 ctx.beginPath() ctx.moveTo(4875, -1688)//middle ctx.lineTo(4875 - 75, -1688)//right side ctx.lineTo((4875 - 75) - 638, -1688 + 638)//middle of left platform ctx.lineTo((4875 - 75) - 638 + 150, -1688 + 638) ctx.lineTo((4875 - 75) - 315, -1688 + 448) ctx.lineTo(4875 + 75 + 135, -1688 + 445)//right side of right platofrm ctx.lineTo(4875 + 75 + 135 + 445, -1688 + 445 + 445) ctx.lineTo(5460, -880) ctx.lineTo(4875 + 75 + 538, -1688 + 538) ctx.lineTo(4875 + 75, -1688)//left side ctx.fill() } //rain // if (!mutetoggle.isOn) { ctx.beginPath() ctx.fillStyle = "rgba(30,150,117,255)" ctx.rect(Math.random() * 4500 - 2000, -5000, 3 + 2.5, 5030) ctx.rect(Math.random() * 4500 - 2000, -5000, 3 + 2.5, 5030) ctx.rect(Math.random() * 4500 - 2000, -5000, 3 + 2.5, 5030) ctx.rect(Math.random() * 2000 + 2500, -5000, 3 + 2.5, 3000) ctx.rect(Math.random() * 2000 + 2500, -5000, 3 + 2.5, 3000) ctx.rect(Math.random() * 1300 + 4500, -5000, 3 + 2.5, 2500) ctx.rect(Math.random() * 1300 + 7500, -5000, 3 + 2.5, 1800) ctx.rect(Math.random() * 1800 + 5700, -5000, 3 + 2.5, 3000) ctx.rect(Math.random() * 1800 + 5700, -5000, 3 + 2.5, 3000) ctx.rect(Math.random() * 1800 + 8400, -5000, 3 + 2.5, 3000) ctx.rect(Math.random() * 1800 + 8400, -5000, 3 + 2.5, 3000) ctx.rect(Math.random() * 4500 - 2000, -5000, 3 + 2.5, 5030) ctx.fillStyle = "rgba(30,150,117,255)" ctx.fill() //rain on shed rainXtemp1 = Math.random() * 900 + 11100 rainXtemp2 = Math.random() * 900 + 10200 if (rainXtemp2 < 10800) { ctx.rect(rainXtemp2, -5000, Math.random() * 3 + 2.5, 3000) } else { ctx.rect(rainXtemp2, -5000, Math.random() * 3 + 2.5, 2600) } ctx.rect(rainXtemp1, -5000, Math.random() * 3 + 2.5, 5000 + 0.5468 * rainXtemp1 - 8507) // ctx.rect(Math.random() * 900 + 10200, -5000, Math.random() * 3 + 2.5, 3000) // ctx.rect(Math.random() * 900 + 11100, -5000, Math.random() * 3 + 2.5, 5000 + 0.5468 * this.x - 8507) 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.rect(Math.random() * 1500 - 3500, -5000, Math.random() * 3 + 2.5, 10030) ctx.fillStyle = "rgba(30,150,117,255)" ctx.fill() // } } while (Math.random() < 0.5); //this is really important, keep it //simulation.inGameConsole(stopcycle) //simulation.inGameConsole(m.cycle) // ctx.fillStyle = "rgba(228,255,0,0.8)" // //simulation.inGameConsole(stopcycle) // ctx.fillRect(50.4, -1210.0, 100, 100) // stopcycle = m.cycle + Math.random * 600; //stopcycle = m.cycles + Math.random * 600 //LIGHTNING //flash cycle gets set to a random number 260-295 //stop cycle increases until it is bigger than flash cycle //lightning effect starts //stop cycle continues increasing until it reaches 300 //repeat // simulation.inGameConsole(stopcycle) if (stopcycle > 300) { //reset if (Math.random() > 0.8 && hasDoubleFlashed == false) { flashcycle = Math.round(Math.random() * 10 + 275) stopcycle = flashcycle - 20 hasDoubleFlashed = true } else { flashcycle = Math.round(Math.random() * 25 + 260) stopcycle = Math.random() * -100 hasDoubleFlashed = false } document.body.style.backgroundColor = "#2e416e"; playRandomThunder() } else { if (stopcycle > flashcycle) { document.body.style.backgroundColor = "#7391ff"; for (let i = 0; i < mob.length; i++) mobs.statusStun(mob[i], 300 - flashcycle)//Math.random() * 20 + 20 lightningTime = flashcycle - 300 } stopcycle = stopcycle + 1 } //mute button ctx.textAlign = "start" ctx.fillStyle = "#00ffff"; // ctx.fillText("Waste Discharge Interruption:", 2910, -3870); // ctx.fillText("Owner 'Scarlet' not found", 2910, -3830); // ctx.fillText("Detected user: 'm'", 2910, -3790); ctx.font = "27px monospace"; ctx.fillText("Audio:", 150, -270); ctx.font = "54px monospace"; ctx.textAlign = "right"; ctx.fillText(mutetoggle.isOn ? "Muted" : "Unmuted", 250, -210); //mute symbol if (mutetoggle.isOn) { ctx.strokeStyle = "#ff0400"; } else { ctx.strokeStyle = "#00ff00"; } // ctx.beginPath(); // ctx.rect(80 + 50, -187.5, 20, 25) // ctx.stroke(); ctx.lineWidth = 10; ctx.beginPath(); ctx.moveTo(130, -167.5) //top left ctx.lineTo(130 + 15 * 2, -167.5) //top mid ctx.lineTo(130 + 30 * 2, -167.5 - 10 * 2) //top right ctx.lineTo(130 + 30 * 2, -167.5 + 35 * 2) //bottom right ctx.lineTo(130 + 15 * 2, -167.5 + 22 * 2) //bottom mid ctx.lineTo(130, -167.5 + 22 * 2) // bottom left ctx.lineTo(130, -167.5) //top left ctx.stroke(); // canvas.width += 5 // ctx.strokeSyle() if (mutetoggle.isOn) { ctx.lineWidth = 9; ctx.moveTo(230, -185) ctx.lineTo(140, -95) ctx.stroke(); ctx.lineWidth = 5; ctx.stroke(); ctx.beginPath(); ctx.arc(170, -145, 45, 1.75 * Math.PI, 0.25 * Math.PI); ctx.stroke(); ctx.beginPath(); ctx.arc(170, -145, 60, 1.75 * Math.PI, 0.25 * Math.PI); ctx.stroke(); } else { ctx.stroke(); ctx.beginPath(); ctx.arc(170, -145, 45, 1.75 * Math.PI, 0.25 * Math.PI); ctx.stroke(); ctx.beginPath(); ctx.arc(170, -145, 60, 1.75 * Math.PI, 0.25 * Math.PI); ctx.stroke(); } // ctx.arc(130, -175, 8, 0, 2 * Math.PI); ctx.lineWidth = 4; ctx.stroke(); ctx.textAlign = "center"; ctx.fillStyle = "#00ffff"; // if (isMuted = 0) { // ctx.fillText("Muted", 360, -230); // } else { // ctx.fillText("Unmuted", 360, -190); // } // ctx.strokeStyle = "#00ff00"; // 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"; // ctx.font = "30px monospace"; // ctx.fillText("n-gon inc", 3300, -3630); ctx.font = "25px Arial"; ctx.fillStyle = "#d4f4f4" ctx.fillRect(12984, -704, 420, 450) //windows //scrapped, but i might work on this again // if (stopcycle > flashcycle) { // ctx.fillStyle = "rgba(255, 255, 255, 1)" // 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) // } else { 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.fillStyle = "rgba(0,0,0,0.5)" 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 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() //rocks in river ctx.fillStyle = "rgba(50,50,50,0.6)" ctx.beginPath() ctx.moveTo(-2050, 0) ctx.lineTo(1725, 0) ctx.lineTo(1980, 88) ctx.lineTo(2118, 257) ctx.lineTo(2167, 491) ctx.lineTo(-1800, 3000) ctx.lineTo(-2050, 3000) // 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(2013, -791, 600, 75) ctx.fillRect(1766, -1091, 250, 310) ctx.beginPath() ctx.moveTo(1816, -781) ctx.lineTo(1816, 32) ctx.lineTo(1966, 84) ctx.lineTo(1966, -781) ctx.fill() // ctx.fillRect(1816, -781, 150, 2000) 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) // if (level.levelsCleared > oldOnLevel) { // simulation.inGameConsole("muted bc next level"); // console.log("muted bc next level"); // rain1.muted = true // ambiance1.muted = true // rain3.muted = true // thunder1.muted = true // thunder2.muted = true // thunder3.muted = true // thunder4.muted = true // } 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 - 0 && player.velocity.y < 0.15) { // level.exitCount += input.down ? 8 : 2 customExitTimer += 3 } else if (customExitTimer > 0) { customExitTimer -= 3 } // simulation.inGameConsole(customExitTimer); if (customExitTimer > 80) { // simulation.inGameConsole("muted bc next level"); // console.log("muted bc next level"); muteAll(); } level.exit.drawAndCheck(); addEventListener("keydown", function (event) { if (event.key == "u") { muteAll(); } }) // if (simulation.testing) { // if (key.toLowerCase = "o") { // rain1.muted = true // ambiance1.muted = true // rain3.muted = true // thunder1.muted = true // thunder2.muted = true // thunder3.muted = true // thunder4.muted = true // } // } slime.query(); slime2.query(); slime3.query(); slime4.query(); slime5.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(-2000 + 200 * Math.random(), Math.random() * 100, 5, 2000)//far left waterfall ctx.fillRect(7200 + 100 * Math.random(), -800 - 50 * Math.random(), 5, 2032) level.enter.draw(); laserbutton.query(); laserbutton.draw(); doortoggle.query(); mutetoggle.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(); //lights in basement // if (doortoggle.isOn) { // ctx.beginPath() // ctx.moveTo(6325, 212) // ctx.lineTo(6325 - 75, 212) // ctx.lineTo((6325 - 75) - 338, 212 + 338) // ctx.lineTo(6325 + 10, 212 + 338) // ctx.lineTo(6325 + 29.97, 212 + 1018) // ctx.lineTo(6325 + 597.4443, 212 + 1018) //at an angle to the right platform // ctx.lineTo((6325 + 75) + 375, 212 + 763) // ctx.lineTo((6325 + 75) + 375, 212 + 688) // ctx.lineTo((6325 + 75) + 688, 212 + 688) // ctx.lineTo((6325 + 75) + 100, 212 + 100) // ctx.lineTo(6325 + 75, 212) // ctx.fillStyle = `rgba(242, 255, 0, 0.3})` // ctx.fill() // } spawn.mapRect(6250, 200, 150, 12); laser.opticalQuery(); if (checkForPush(m.pos.x, m.pos.y)) { Matter.Body.setVelocity(player, { x: player.velocity.x + checkForWaterXSpeed(m.pos.x, m.pos.y), y: player.velocity.y + checkForWaterYSpeed(m.pos.x, m.pos.y) }); } //push stuff for (let i = 0, len = body.length; i < len; ++i) { //push blocks away if (checkForPush(body[i].position.x, body[i].position.y)) { if (checkForWaterYSpeed(body[i].position.x, body[i].position.y) == 0) { body[i].force.x += checkForWaterXSpeed(body[i].position.x, body[i].position.y) / 300; body[i].force.y += checkForWaterYSpeed(body[i].position.x, body[i].position.y) / 1000 - 0.001; } else { body[i].force.x += checkForWaterXSpeed(body[i].position.x, body[i].position.y) / 300; body[i].force.y += checkForWaterYSpeed(body[i].position.x, body[i].position.y) / 1000; } } for (let i = 0, len = powerUp.length; i < len; ++i) { //push blocks away if (checkForPush(powerUp[i].position.x, powerUp[i].position.y - 50)) { powerUp[i].force.x += checkForWaterXSpeed(powerUp[i].position.x, powerUp[i].position.y) / 800; powerUp[i].force.y += checkForWaterYSpeed(powerUp[i].position.x, powerUp[i].position.y) / 800; powerUp[i].position.x -= 0.1; } } } // for (let i = 0, len = powerUp.length; i < len; ++i) { //push blocks away // if (checkForPush(powerUp[i].position.x, powerUp[i].position.y)) { // powerUp[i].force.x += checkForWaterXSpeed(powerUp[i].position.x, powerUp[i].position.y) / 1000; // powerUp[i].force.y += checkForWaterYSpeed(powerUp[i].position.x, powerUp[i].position.y) / 1000; // } // } for (let i = 0, len = mob.length; i < len; ++i) { //push blocks away if (checkForPush(mob[i].position.x, mob[i].position.y)) { mob[i].force.x += checkForWaterXSpeed(mob[i].position.x, mob[i].position.y) / 2000; mob[i].force.y += checkForWaterYSpeed(mob[i].position.x, mob[i].position.y) / 2000; } } for (let i = 0, len = bullet.length; i < len; ++i) { //push blocks away if (checkForPush(bullet[i].position.x, bullet[i].position.y)) { if (b.activeGun == 0 || b.activeGun == 1 || b.activeGun == 2 || b.activeGun == 4 || b.activeGun == 7) { bullet[i].velocity.x += checkForWaterXSpeed(bullet[i].position.x, bullet[i].position.y) * 100; bullet[i].velocity.y += checkForWaterYSpeed(bullet[i].position.x, bullet[i].position.y) * 100; } } } // 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 // 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 // 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; // } // } boost1.query(); boost2.query(); // ctx.fillRect(7200, -650, 100, 1900) portal[0].draw(); portal[1].draw(); portal[2].draw(); portal[3].draw(); }; //little block to stop push spawn.mapRect(50, -10, 250, 20) spawn.mapRect(4800, -1700, 150, 12); 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(257.5, -358.5, 50, 360); spawn.mapRect(-83, -358.5, 350, 50); //blocks in river/waterfall spawn.mapRect(1275, 0, 450, 75); spawn.mapRect(2027, -388, 600, 75); spawn.mapRect(1726, -791, 330, 19); spawn.mapRect(1696, -772, 390, 19); spawn.mapRect(1666, -753, 450, 19); spawn.mapRect(1636, -734, 510, 19); 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 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); //light spawn.mapRect(6250, 200, 150, 12); //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, -2233, "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.15) { 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, 1, 1); //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); //button block spawn.bodyRect(4900 + Math.random() * 400, -2600, 70, 70, 1); // 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 function muteAll() { rain1.muted = true ambiance1.muted = true rain3.muted = true thunder1.muted = true thunder2.muted = true thunder3.muted = true thunder4.muted = true } function checkForWaterXSpeed(objectX, objectY) { let waterXForce = 0; if (objectY > -70 && objectX < 2785) { waterXForce = -1 * (2 + objectY / 150) } if (objectX > 2600 && objectX < 4500 && objectY < -1900 && objectY > -2121.3) { waterXForce = -2 } if (objectX > 4500 && objectX < 6000 && objectY < -1900 && objectY > -2121.3) { waterXForce = 0.4 } if (objectX > 6500 && objectX < 10000 && objectY < -1900 && objectY > -2121.3) { waterXForce = -1 } if (objectX > 2600 && objectX < 6100 && objectY < -650 && objectY > -920) { waterXForce = -0.4 } if (objectX > 6500 && objectX < 7300 && objectY < -650 && objectY > -920 && m.onGround) { waterXForce = 0.2 } return waterXForce; } function checkForWaterYSpeed(objectX, objectY) { let waterYForce = 0; if (objectX > 2400 && objectX < 2600) { waterYForce = 4 } if (player.position.x > 7200 && player.position.x < 7350 && player.position.y > -950 && player.position.y < 1250) { waterYForce = 0.8 } if (player.position.x > 6100 && player.position.x < 6200 && player.position.y < -800 && player.position.y > -2000) { waterYForce = 0.3 } if (player.position.x > 6400 && player.position.x < 6500 && player.position.y < -800 && player.position.y > -2000) { waterYForce = 0.3 } return waterYForce; } function checkForPush(objectX, objectY) { return (objectY > -70 && objectX < 2785 || objectX > 2400 && objectX < 2600 || objectX > 2600 && objectX < 6000 && objectY < -1900 && objectY > -2121.3 || objectX > 6500 && objectX < 10000 && objectY < -1900 && objectY > -2121.3 || objectX > 2600 && objectX < 6100 && objectY < -650 && objectY > -920 || objectX > 6500 && objectX < 7300 && objectY < -650 && objectY > -920 || objectX > 7200 && objectX < 7350 && objectY > -950 && objectY < 1250 || objectX > 6100 && objectX < 6200 && objectY < -800 && objectY > -2000 || objectX > 6400 && objectX < 6500 && objectY < -800 && objectY > -2000); } function playRandomThunder() { let tempRandom = Math.floor(4 * Math.random()) switch (tempRandom) { case 1: // simulation.inGameConsole(`thunder1`) thunder1.play(); break; case 2: // simulation.inGameConsole(`thunder2`) thunder2.play(); break; case 3: // simulation.inGameConsole(`thunder3`) thunder3.play(); break; case 4: // simulation.inGameConsole(`thunder4`) thunder4.play(); break; default: // simulation.inGameConsole(`thunder5`) thunder4.play(); break; } } }, 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 && tech.cancelTechCount === 0) ? "?" : "✕"}
`; }; 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) { m.squirrelFx += 0.25; m.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.isTimeDilated) { 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.inGameConsole(`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.inGameConsole('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.inGameConsole('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.inGameConsole('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.inGameConsole('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.inGameConsole(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.inGameConsole(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.inGameConsole('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.inGameConsole(`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); 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([]); } }; 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.isTimeDilated = 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.isTimeDilated) { 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.inGameConsole(`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 }, tlinat() { // _Destined_ formerly Richard0820#2652 simulation.inGameConsole(`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.inGameConsole(`
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.inGameConsole("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.inGameConsole(`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() { simulation.inGameConsole(`ace by Richard0820`); let isDestroyed = false; const ace = { spawnOrbitals(who, radius, chance = Math.min(0.25 + simulation.difficulty * 0.005)) { if (Math.random() < chance) { const len = Math.floor(Math.min(15, 3 + Math.sqrt(simulation.difficulty))) 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()) { 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 me.isUnblockable = true 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; if (target.shieldCount > 0) { target.shieldCount++ } else { target.shieldCount = 1 } me.shieldCount = target.shieldCount //used with "bubble fusion" 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 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 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.inGameConsole(`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) { const len = Math.floor(Math.min(15, 3 + Math.sqrt(simulation.difficulty))) 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()) { 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 me.isUnblockable = true 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; if (target.shieldCount > 0) { target.shieldCount++ } else { target.shieldCount = 1 } me.shieldCount = target.shieldCount //used with "bubble fusion" 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 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 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; }, LaunchSite() { simulation.inGameConsole(`Launch Site by Des Boot`); simulation.inGameConsole(`The rain stopped...`); level.setPosToSpawn(0, -50); //normal spawn const elevatortoggle = level.toggle(13650, 3000) let newMobsSpawned = false; let leverTimer = 0; level.exit.x = 11900; level.exit.y = -800; const boost1 = level.boost(12050, 200, 1000) const boost2 = level.boost(13575, 780, 700) 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"; const mover = level.mover(5700, 2680, 3800, 25, 10); //x,y,width.height,VxGoal,force //spawning debris spawn.debris(2700, 2200, 3000, 10); //16 debris per level spawn.debris(10055, 483, 1300, 10); //16 debris per level spawn.debris(10500, -240, 1000, 10); //16 debris per level spawn.debris(9500, 1773.3, 3700, 10); //16 debris per level // color.map = "#444" //custom map color powerUps.spawn(5400, 850, "heal") powerUps.spawn(5400, 850, "heal") powerUps.spawn(8650, 2000, "ammo") powerUps.spawn(8650, 2000, "ammo") powerUps.spawn(8650, 2000, "ammo") powerUps.spawn(8650, 2000, "ammo") level.custom = () => { ctx.fillStyle = `rgba(68, 68, 68)` ctx.fillRect(11450, 700, 50, 125); ctx.fillRect(11425, 750, 75, 75); if (newMobsSpawned) { // ctx.fillStyle = `#00FFFF22`; ctx.fillStyle = `rgba(0, 255, 255, ${leverTimer / 5})` ctx.fillRect(11500, 700, 450, 2375 + 100) ctx.fill() ctx.fillStyle = `#00f2ff` ctx.fillRect(11500 + Math.floor(Math.random() * 450), 700, 5, 2375 + 100) ctx.fillRect(11500 + Math.floor(Math.random() * 450), 700, 5, 2375 + 100) ctx.fill() if (m.pos.x > 11500 && m.pos.x < 11950 && m.pos.y > 800 && m.pos.y < 2700) { player.force.y -= m.mass * simulation.g + (input.down ? 0 : 0.012 * 4); } } //light rain ctx.beginPath() ctx.fillStyle = "rgba(30,150,117,255)" ctx.rect(Math.random() * 1600 - 1500, -5000, Math.random() * 3 + 1.5, 5000) ctx.rect(Math.random() * 2400 - 1500, -5000, Math.random() * 3 + 1.5, 5000) ctx.rect(Math.random() * 3200 - 1500, -5000, Math.random() * 3 + 1.5, 5000) ctx.rect(Math.random() * 3000 - 4500, -5000, Math.random() * 3 + 1.5, 8000) ctx.rect(Math.random() * 3000 - 4500, -5000, Math.random() * 3 + 1.5, 8000) ctx.rect(Math.random() * 3000 - 4500, -5000, Math.random() * 3 + 1.5, 8000) ctx.fill(); mover.push(); boost1.query(); boost2.query(); elevatortoggle.query(); if (elevatortoggle.isOn) { if (newMobsSpawned == false) { leverTimer += 0.0125; //last room mobs spawn.randomSmallMob(11723.3, -127.5); spawn.randomSmallMob(10525.2, 727.5); spawn.randomSmallMob(11290.5, 556.0); spawn.randomMob(11182.6, 1788.0, 0.8); spawn.randomGroup(10606.7, -248, 0.4); spawn.randomGroup(11066.3, 560.4, 0.4); newMobsSpawned = true; } }; if (leverTimer > 0 && leverTimer < 1) { leverTimer += 0.0125 } ctx.fillStyle = "rgba(0,0,0,0.3)" ctx.beginPath() ctx.moveTo(5150, 1050) ctx.lineTo(5580, 1480) ctx.lineTo(5818, 1203) ctx.lineTo(5873.4, 1012.4) ctx.fill() ctx.beginPath() ctx.moveTo(5150, 1050) ctx.lineTo(5580, 1480) ctx.lineTo(5818, 1203) ctx.lineTo(5873.4, 1012.4) ctx.fill() ctx.beginPath() ctx.moveTo(5700, 2300) ctx.lineTo(6100, 2700) ctx.lineTo(9600, 2700) ctx.lineTo(10000, 2300) ctx.fill() ctx.fillRect(4400, 2050, 550, 800) ctx.fillStyle = "rgba(0,0,0,0.2)" ctx.fillRect(4730, 1347, 253, 700) ctx.fillRect(4950, 2047, 33, 700) ctx.fillRect(11500, 100, 550, 150) ctx.moveTo(11800, -700) ctx.lineTo(12050, -700) ctx.lineTo(12050, -280) ctx.lineTo(11950, -280) ctx.fill() //ladder ctx.fillRect(12065, -770, 20, 870) ctx.fillRect(12115, -770, 20, 870) ctx.fillRect(12055, -770, 90, 15) ctx.fillRect(12055, -710, 90, 15) ctx.fillRect(12055, -660, 90, 15) ctx.fillRect(12055, -600, 90, 15) ctx.fillRect(12055, -540, 90, 15) ctx.fillRect(12055, -480, 90, 15) ctx.fillRect(12055, -420, 90, 15) ctx.fillRect(12055, -360, 90, 15) ctx.fillRect(12055, -300, 90, 15) ctx.fillRect(12055, -240, 90, 15) ctx.fillRect(12055, -180, 90, 15) ctx.fillRect(12055, -120, 90, 15) ctx.fillRect(12055, -60, 90, 15) ctx.fillRect(12055, 0, 90, 15) ctx.fillRect(12055, 60, 90, 15) //post rocket shadows ctx.fillStyle = "rgba(0,0,0,0.3)" ctx.fillRect(10000, 510, 50, 700) //general background shadows ctx.fillStyle = "rgba(0,0,0,0.1)" ctx.fillRect(9500, -800, 5000, 3100) ctx.fillRect(10000, 2300, 5000, 700) ctx.moveTo(10000, 2300) ctx.lineTo(10000, 2700) ctx.lineTo(9600, 2700) ctx.fill() ctx.moveTo(2500, 500) ctx.lineTo(3660, 1660) ctx.lineTo(4291.1, 2978.1) ctx.lineTo(6100, 2700) ctx.lineTo(5700, 2300) ctx.lineTo(5884.8, 380.1) ctx.fill() //boss room shadow ctx.fillRect(11950, 2000, 5000, 1100) ctx.fillRect(12150, -25, 3000, 250) ctx.fillRect(12000, 225, 3000, 750) ctx.fillRect(11700, -1200, 600, 400) level.exit.drawAndCheck(); level.enter.draw(); } level.customTopLayer = () => { if (12150 < m.pos.x && m.pos.y < 1000) { ctx.fillStyle = `rgba(68, 68, 68, ${Math.max(0.3, Math.min((12350 - m.pos.x) / 100, 0.99))})` } else { ctx.fillStyle = `rgba(68, 68, 68, 68)` } ctx.fillRect(12150, -30, 1600, 1000) mover.draw(); //vanishing platform vanisher.query(); //rocket ctx.fillStyle = "rgba(202,88,0,1)" ctx.fillRect(6800, -1000, 600, 2800) ctx.beginPath() ctx.moveTo(7400, -998)//base right ctx.lineTo(7350, -1400)//1/3rd up ctx.lineTo(7250, -1700)//2/3rd up ctx.lineTo(7100, -1900)//peak ctx.lineTo(6950, -1700)//2/3rd down ctx.lineTo(6850, -1400)//1/3rd down ctx.lineTo(6800, -998)//baseleft ctx.fill() ctx.fillStyle = "rgba(255,255,255,1)" ctx.fillRect(6900, 0, 400, 2000) ctx.fillRect(6800, 1700, 600, 300) ctx.fillRect(6450, -400, 250, 2100) ctx.fillRect(7500, -400, 250, 2100) //right engine ctx.beginPath() ctx.moveTo(6450, -398)//base right ctx.lineTo(6575, -900)//peak ctx.lineTo(6700, -398)//baseleft ctx.fill() //right engine ctx.beginPath() ctx.moveTo(7500, -398)//base right ctx.lineTo(7625, -900)//peak ctx.lineTo(7750, -398)//baseleft ctx.fill() //right wing ctx.beginPath() ctx.moveTo(6910, 450) ctx.lineTo(6910, 1710) ctx.lineTo(6250, 1700) ctx.lineTo(6250, 1600) ctx.lineTo(6780, 1030) ctx.fill() //left wing ctx.beginPath() ctx.moveTo((7100 - 6910) + 7100, 450) ctx.lineTo((7100 - 6910) + 7100, 1710) ctx.lineTo((7100 - 6250) + 7100, 1700) ctx.lineTo((7100 - 6250) + 7100, 1600) ctx.lineTo((7100 - 6780) + 7100, 1030) ctx.fill() //main rocket tip ctx.beginPath() ctx.moveTo(7300, 2)//base right ctx.lineTo(7250, -300)//1/3rd up ctx.lineTo(7175, -500)//2/3rd up ctx.lineTo(7100, -600)//peak ctx.lineTo(7025, -500)//2/3rd down ctx.lineTo(6950, -300)//1/3rd down ctx.lineTo(6900, 2)//baseleft ctx.fill() //right wing outline ctx.fillStyle = "rgba(0,0,0,1)" ctx.beginPath() ctx.moveTo((7100 - 6250) + 7100, 1700) ctx.lineTo((7100 - 6250) + 7100, 1600) ctx.lineTo((7100 - 6780) + 7100, 1030) ctx.lineTo((7100 - 6800) + 7100, 918) ctx.lineTo((7100 - 6800) + 7100, 1030) ctx.lineTo((7100 - 6290) + 7100, 1600) ctx.fill() //left wing outline ctx.beginPath() ctx.moveTo(6250, 1700) ctx.lineTo(6250, 1600) ctx.lineTo(6780, 1030) ctx.lineTo(6800, 918) ctx.lineTo(6800, 1030) ctx.lineTo(6290, 1600) ctx.fill() ctx.fillRect(6800, 2000, 600, 50) ctx.fillRect(6850, 2050, 500, 150) //light if (elevatortoggle.isOn) { ctx.fillStyle = `rgba(242, 255, 0, ${3 * Math.sqrt(leverTimer) / 10})` ctx.beginPath() ctx.moveTo(13070, 2012) ctx.lineTo(12930, 2012) ctx.lineTo(12230, 2700) ctx.lineTo(13770, 2700) ctx.fill() } //shadows ctx.fillStyle = "rgba(0,0,0,0.3)" ctx.beginPath() ctx.moveTo(1800, -400) ctx.lineTo(2500, 300) ctx.lineTo(2854, 21) ctx.lineTo(1974.5, -453.5) ctx.fill() ctx.beginPath() ctx.moveTo(1800, 0) ctx.lineTo(3460, 1660) ctx.lineTo(1522.9, 2431.8) ctx.lineTo(983.5, 887.3) ctx.fill() }; //GEOMETRY //vanishing platform var vanisher = level.vanish(10250, 201, 500, 20); //blocks spawn.bodyRect(3700, 1210, 200, 300, 0.9); spawn.bodyRect(3900, 1210, 220, 150, 0.9); //main ground spawn.mapRect(-1500, 0, 3300, 3700); spawn.mapRect(-1500, 2700, 15000, 700); spawn.mapRect(9500, -1500, 2000, 800); spawn.mapRect(9500, -1500, 500, 2300); spawn.mapRect(2500, 0, 3200, 500); spawn.mapRect(5700, 0, 500, 2300); spawn.mapRect(1800, 1100, 800, 2600); spawn.mapRect(2600, 1660, 1800, 2040); spawn.mapRect(1800, -495, 200, 100); spawn.mapVertex(2400, -230, "0 10 900 510 800 510 750 510 0 110"); //stairs spawn.mapRect(2600, 1150, 50, 550); spawn.mapRect(2650, 1200, 50, 500); spawn.mapRect(2700, 1250, 50, 450); spawn.mapRect(2750, 1300, 50, 400); spawn.mapRect(2800, 1350, 50, 350); spawn.mapRect(2850, 1400, 50, 300); spawn.mapRect(2900, 1450, 50, 250); spawn.mapRect(2950, 1500, 50, 200); spawn.mapRect(3000, 1550, 50, 150); spawn.mapRect(3050, 1600, 50, 100); spawn.mapRect(12100, 1650, 50, 50); //platforms in first room spawn.mapRect(4435, 1300, 550, 50); spawn.mapRect(4685, 1300, 50, 550); spawn.mapRect(4400, 2000, 550, 50); spawn.mapRect(5150, 1000, 550, 50); spawn.mapVertex(5500, 1550, "0 0 -500 600 100 0 -400 600"); //second room spawn.mapRect(8000, 0, 1500, 2300); spawn.mapRect(6200, 2200, 1800, 100); //stairs after rocket spawn.mapRect(10300, 2650, 1000, 50); spawn.mapRect(10350, 2600, 950, 50); spawn.mapRect(10400, 2550, 900, 50); spawn.mapRect(10450, 2500, 850, 50); spawn.mapRect(10500, 2450, 800, 50); spawn.mapRect(10550, 2400, 750, 50); spawn.mapRect(10600, 2350, 700, 50); spawn.mapRect(10650, 2300, 650, 50); spawn.mapRect(10700, 2250, 600, 50); spawn.mapRect(10750, 2200, 550, 50); spawn.mapRect(10800, 2150, 500, 50); spawn.mapRect(10850, 2100, 450, 50); spawn.mapRect(10900, 2050, 400, 50); spawn.mapRect(10950, 2000, 550, 700); //light spawn.mapRect(12925, 2000, 150, 12); //roof over stairs spawn.mapRect(9500, 1200, 1000, 500); spawn.mapRect(9500, 2200, 500, 100); spawn.mapRect(9500, 800, 2000, 400); spawn.mapRect(11950, 800, 2000, 1200); spawn.mapRect(13900, 800, 2000, 2600); spawn.mapRect(11950, 2000, 150, 400); //last room spawn.mapRect(10500, 200, 3000, 80); spawn.mapRect(11950, 200, 100, 1000); spawn.mapRect(10000, 435, 50, 75); spawn.mapRect(11000, -50, 60, 250); spawn.mapRect(11500, -1500, 60, 1100); spawn.mapRect(11500, 50, 500, 50); spawn.mapRect(12000, 260, 155, 1000); //exit room spawn.mapRect(11500, -1500, 300, 800); spawn.mapRect(11950, -280, 100, 380); spawn.mapRect(12150, -1500, 100, 1600); spawn.mapRect(11800, -770, 250, 70); spawn.mapRect(11800, -1500, 350, 400); spawn.mapRect(12150, -1500, 3000, 1500); spawn.mapRect(13700, 0, 2000, 800); spawn.mapRect(13200, 550, 50, 300); //MOBS //mobs in first room spawn.randomSmallMob(4700, 2500); spawn.randomMob(4100, 1000, 0.8); spawn.randomGroup(5000, 700, 0.4); //second room mobs spawn.randomSmallMob(9800, 2000); spawn.randomMob(10910.6, 1559.8, 0.8); spawn.randomMob(12441.5, 2340.6, 0.8); spawn.randomMob(11182.6, 1788.0, 0.8); spawn.randomMob(13381.7, 2437.2, 0.8); spawn.randomGroup(10472.4, 2079.0, 0.4); //boss room spawn.randomLevelBoss(12786, 2461, ["launcherBoss", "laserTargetingBoss", "blinkBoss", "streamBoss", "historyBoss", "grenadierBoss", "blockBoss", "revolutionBoss", "slashBoss"]); spawn.mapRect(13500, 3000, 500, 400); spawn.mapRect(13500, 2700, 325, 60); //extra boss spawn.randomLevelBoss(12808.8, 527.0, ["blinkBoss"]); // 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 }, shipwreck() { simulation.inGameConsole(`shipwreck by 3xionDev`); 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 = "#05001C"; // color.map = "#444" //custom map color spawn.mapRect(-325, 0, 650, 300); spawn.mapRect(-275, -675, 50, 700); spawn.mapRect(-325, -1025, 650, 300); spawn.mapRect(-275, -750, 50, 75); spawn.mapRect(300, 50, 675, 200); spawn.mapRect(925, 50, 450, 150); spawn.mapRect(275, -750, 50, 275); spawn.mapRect(275, -325, 50, 325); spawn.bodyRect(150, -175, 100, 100); spawn.bodyRect(-200, -450, 100, 400); spawn.bodyRect(-150, -650, 25, 50); spawn.bodyRect(-200, -700, 25, 25); spawn.bodyRect(175, -250, 75, 100); spawn.mapRect(875, -50, 100, 100); spawn.mapRect(350, -175, 25, 25); spawn.mapRect(325, -175, 175, 225); spawn.bodyRect(375, -225, 50, 50); spawn.bodyRect(700, -450, 125, 125); spawn.mapRect(1375, 25, 400, 100); spawn.mapRect(1675, -175, 100, 200); spawn.mapRect(1775, -175, 225, 100); spawn.mapRect(1575, -75, 100, 100); spawn.mapRect(1775, -75, 175, 100); spawn.mapRect(1300, 125, 250, 25); spawn.mapRect(850, 200, 225, 25); spawn.mapRect(1100, 200, 50, 100); spawn.mapRect(1250, 175, 50, 125); spawn.mapRect(1950, -150, 275, 50); spawn.mapRect(2125, -150, 25, 25); spawn.mapRect(2125, -175, 1025, 100); spawn.mapRect(2625, -350, 25, 25); spawn.mapRect(2650, -325, 25, 150); spawn.mapRect(2625, -400, 50, 250); spawn.bodyRect(1900, -200, 325, 25); spawn.bodyRect(2525, -275, 100, 100); spawn.bodyRect(2575, -325, 50, 50); spawn.mapRect(425, -75, 150, 125); spawn.bodyRect(350, -250, 75, 75); spawn.bodyRect(1250, -200, 25, 25); spawn.bodyRect(1150, -350, 100, 175); spawn.bodyRect(1375, -375, 25, 25); spawn.bodyRect(1375, -400, 50, 100); spawn.mapRect(2650, -400, 500, 50); spawn.mapRect(3100, -175, 300, 50); spawn.bodyRect(2700, -250, 75, 25); spawn.bodyRect(2725, -275, 25, 25); spawn.bodyRect(2750, -250, 25, 25); spawn.bodyRect(2825, -275, 75, 100); spawn.randomMob(3650, -250, 0); spawn.bodyRect(2925, -225, 50, 50); spawn.mapRect(325, -950, 25, 25); spawn.mapRect(325, -975, 650, 200); spawn.mapRect(975, -900, 200, 100); spawn.mapRect(1175, -850, 100, 100); spawn.mapRect(1275, -800, 75, 125); spawn.mapRect(1350, -725, 25, 125); spawn.mapRect(1225, -800, 25, 25); spawn.mapRect(950, -925, 100, 25); spawn.mapRect(1150, -875, 100, 25); spawn.mapRect(1250, -825, 75, 50); spawn.mapRect(1125, -825, 75, 50); spawn.mapRect(1250, -775, 50, 50); spawn.mapRect(1400, -225, 100, 200); spawn.mapRect(1550, -150, 125, 75); spawn.mapRect(1500, -100, 75, 75); spawn.mapRect(1175, -225, 225, 100); spawn.mapRect(1475, -200, 100, 100); spawn.mapRect(1300, -175, 125, 100); spawn.mapRect(1250, -150, 50, 200); spawn.mapRect(1600, -275, 50, 150); spawn.mapRect(1300, -275, 75, 75); spawn.mapRect(1475, -375, 50, 225); spawn.mapRect(1575, -225, 25, 100); spawn.mapRect(1500, -325, 50, 125); spawn.bodyRect(1400, -350, 25, 50); spawn.bodyRect(1650, -425, 50, 50); spawn.bodyRect(1750, -475, 25, 125); spawn.bodyRect(1725, -575, 150, 50); spawn.bodyRect(1625, -425, 25, 50); spawn.bodyRect(1175, -425, 75, 75); spawn.mapRect(325, -625, 175, 50); spawn.mapRect(425, -800, 25, 175); spawn.mapRect(375, -800, 25, 200); spawn.mapRect(500, -800, 25, 225); spawn.mapRect(475, -800, 50, 225); spawn.mapRect(675, -875, 50, 575); spawn.mapRect(675, -150, 25, 175); spawn.mapRect(700, -150, 25, 75); spawn.mapRect(675, -125, 25, 200); spawn.bodyRect(675, -300, 50, 150); spawn.mapRect(2125, -1025, 1025, 100); spawn.mapRect(3050, -975, 325, 50); spawn.mapRect(3300, -925, 75, 600); spawn.bodyRect(3300, -325, 75, 125); spawn.bodyRect(3325, -325, 25, 25); spawn.mapRect(3300, -325, 75, 25); spawn.mapRect(3325, -175, 1100, 25); spawn.mapRect(3325, -950, 1100, 25); spawn.mapRect(3350, -725, 225, 25); spawn.mapRect(3500, -925, 75, 200); spawn.mapRect(3350, -850, 175, 25); spawn.bodyRect(4075, -625, 125, 125); spawn.bodyRect(3850, -825, 75, 50); spawn.bodyRect(4050, -800, 25, 50); spawn.bodyRect(4150, -825, 75, 100); spawn.bodyRect(3900, -800, 50, 75); spawn.bodyRect(3575, -375, 100, 75); spawn.bodyRect(3800, -675, 75, 100); spawn.bodyRect(3950, -875, 250, 150); spawn.bodyRect(3975, -700, 50, 100); spawn.bodyRect(4150, -775, 200, 125); spawn.bodyRect(3825, -700, 50, 125); spawn.bodyRect(3575, -550, 125, 50); spawn.bodyRect(3750, -550, 25, 25); spawn.bodyRect(3600, -625, 75, 50); spawn.bodyRect(3550, -500, 75, 50); spawn.bodyRect(4200, -675, 75, 75); spawn.bodyRect(4400, -600, 50, 125); spawn.mapRect(4375, -175, 350, 25); spawn.mapRect(4475, -200, 475, 50); spawn.mapRect(4450, -925, 25, 25); spawn.mapRect(4475, -950, 475, 50); spawn.mapRect(4350, -950, 225, 25); spawn.mapRect(4450, -925, 100, 750); spawn.mapRect(4650, -900, 825, 700); spawn.mapRect(5250, -825, 475, 550); spawn.mapRect(5550, -725, 700, 350); spawn.mapRect(6100, -625, 550, 150); spawn.mapRect(6600, -575, 225, 50); spawn.mapRect(1325, -875, 50, 200); spawn.mapRect(1275, -825, 50, 25); spawn.mapRect(1275, -875, 25, 50); spawn.mapRect(1225, -900, 75, 25); spawn.mapRect(1325, -900, 50, 75); spawn.mapRect(1075, -925, 200, 75); spawn.mapRect(1275, -975, 75, 150); spawn.mapRect(1300, -800, 100, 150); spawn.mapRect(1375, -725, 50, 150); spawn.mapRect(-325, -1525, 650, 300); spawn.mapRect(150, -1275, 50, 375); spawn.mapRect(-100, -1350, 50, 450); spawn.mapRect(-325, -2600, 650, 300); spawn.mapRect(-275, -2400, 25, 50); spawn.mapRect(-275, -2325, 50, 825); spawn.mapRect(300, -1475, 675, 200); spawn.bodyRect(375, -1250, 75, 75); spawn.bodyRect(800, -1275, 25, 300); spawn.mapRect(1950, -1000, 175, 100); spawn.mapRect(1850, -950, 125, 125); spawn.mapRect(1825, -875, 75, 125); spawn.mapRect(1825, -800, 25, 125); spawn.mapRect(1800, -750, 25, 150); spawn.mapRect(1775, -625, 50, 150); spawn.mapRect(2000, -900, 25, 225); spawn.mapRect(2075, -925, 50, 400); spawn.mapRect(1000, -825, 25, 300); spawn.mapRect(1050, -900, 50, 25); spawn.mapRect(1050, -925, 50, 25); spawn.mapRect(2475, -100, 50, 350); spawn.mapRect(2650, -100, 25, 725); spawn.mapRect(2350, -950, 50, 350); spawn.mapRect(775, -825, 25, 375); spawn.mapRect(3750, -950, 25, 175); spawn.mapRect(3625, -925, 25, 275); spawn.mapRect(4225, -925, 50, 200); spawn.mapRect(950, -1425, 200, 100); spawn.mapRect(1150, -1400, 150, 75); spawn.mapRect(1300, -1350, 25, 100); spawn.mapRect(1275, -1350, 25, 50); spawn.bodyRect(1300, -1250, 25, 275); spawn.bodyRect(2600, -1575, 375, 550); spawn.bodyRect(2625, -1300, 75, 150); spawn.bodyRect(2700, -1475, 100, 275); spawn.bodyRect(2525, -1200, 75, 150); spawn.mapRect(1675, -1400, 200, 75); spawn.mapRect(1825, -1425, 225, 100); spawn.mapRect(1650, -1350, 75, 100); spawn.mapRect(1700, -1275, 25, 125); spawn.bodyRect(1225, -1425, 550, 25); spawn.bodyRect(1300, -1650, 100, 150); spawn.bodyRect(1600, -1675, 100, 200); spawn.bodyRect(1575, -1525, 25, 25); spawn.bodyRect(1450, -1575, 25, 125); spawn.bodyRect(1500, -1650, 75, 50); spawn.mapRect(2325, -1225, 50, 200); spawn.mapRect(2375, -1300, 100, 275); spawn.mapRect(2225, -1125, 125, 100); spawn.mapRect(2300, -1150, 50, 50); spawn.bodyRect(2250, -850, 75, 100); spawn.mapRect(150, -2550, 800, 200); spawn.mapRect(875, -2500, 275, 100); spawn.mapRect(325, -2400, 75, 375); spawn.mapRect(325, -1800, 75, 350); spawn.bodyRect(325, -2025, 75, 225); spawn.mapRect(-150, -2375, 25, 375); spawn.mapRect(25, -2400, 50, 500); spawn.mapRect(-100, -2375, 25, 225); spawn.mapRect(200, -2350, 50, 250); spawn.bodyRect(250, -1875, 25, 75); spawn.bodyRect(-50, -2050, 50, 50); spawn.mapRect(1050, -1350, 50, 150); spawn.mapRect(575, -1325, 25, 100); spawn.mapRect(400, -1300, 25, 75); spawn.mapRect(525, -1300, 50, 125); spawn.mapRect(575, -2400, 75, 275); spawn.mapRect(650, -2325, 25, 325); spawn.mapRect(625, -2150, 50, 75); spawn.mapRect(625, -2375, 50, 100); spawn.mapRect(600, -2125, 25, 25); spawn.mapRect(650, -2075, 25, 150); spawn.mapRect(675, -2375, 50, 200); spawn.mapRect(650, -2200, 50, 75); spawn.mapRect(625, -2100, 50, 75); spawn.mapRect(1100, -2475, 950, 50); spawn.mapRect(1325, -1825, 450, 25); spawn.mapRect(1475, -1850, 150, 50); spawn.mapRect(1725, -2425, 50, 600); spawn.mapRect(1325, -2450, 50, 450); spawn.mapRect(1475, -2425, 25, 150); spawn.mapRect(1675, -2425, 25, 600); spawn.bodyRect(1450, -2175, 50, 75); spawn.bodyRect(1650, -2200, 50, 50); spawn.mapRect(950, -1550, 75, 125); spawn.mapRect(900, -1500, 50, 50); spawn.mapRect(2000, -2475, 125, 50); spawn.mapRect(2100, -2475, 1050, 100); spawn.mapRect(3050, -2425, 300, 50); spawn.mapRect(3225, -2400, 1350, 25); spawn.mapRect(4475, -2400, 475, 50); spawn.mapRect(4900, -2375, 1125, 50); spawn.mapRect(3950, -1350, 2075, 50); spawn.mapRect(4075, -1325, 75, 400); spawn.mapRect(4775, -1325, 75, 425); spawn.mapRect(6000, -2350, 1075, 1025); spawn.mapRect(6675, -2250, 950, 825); spawn.mapRect(7375, -2050, 700, 425); spawn.mapRect(7850, -1900, 425, 125); spawn.mapRect(8200, -1850, 275, 25); spawn.mapRect(5000, -2350, 75, 400); spawn.mapRect(5200, -2350, 25, 600); spawn.mapRect(5600, -2325, 25, 475); spawn.mapRect(5750, -2350, 50, 300); spawn.mapRect(5800, -2325, 25, 400); spawn.mapRect(5775, -2075, 25, 50); spawn.bodyRect(5325, -2250, 75, 125); spawn.bodyRect(5925, -1800, 75, 125); spawn.bodyRect(5475, -1800, 75, 225); spawn.bodyRect(5350, -2050, 175, 100); spawn.bodyRect(5475, -2125, 75, 125); spawn.bodyRect(5750, -1750, 100, 100); spawn.bodyRect(5900, -1950, 175, 150); spawn.bodyRect(4600, -1950, 150, 275); spawn.bodyRect(4875, -1875, 150, 100); spawn.mapRect(5675, -1600, 350, 50); spawn.mapRect(4325, -1300, 25, 200); spawn.mapRect(3975, -2375, 75, 350); spawn.mapRect(4250, -2375, 25, 550); spawn.mapRect(2875, -2400, 75, 400); spawn.mapRect(3050, -2425, 25, 700); spawn.mapRect(2450, -2425, 75, 550); spawn.mapRect(3375, -2375, 25, 525); spawn.mapRect(3325, -1125, 75, 225); spawn.mapRect(3125, -1200, 25, 200); spawn.mapRect(2975, -1225, 75, 225); spawn.mapRect(1875, -2425, 50, 550); spawn.mapRect(1900, -1925, 475, 50); spawn.mapRect(2300, -2400, 75, 475); spawn.bodyRect(2025, -2325, 50, 50); spawn.bodyRect(2150, -2300, 100, 100); spawn.bodyRect(2025, -2325, 25, 100); spawn.bodyRect(2125, -2275, 75, 75); spawn.bodyRect(2250, -2250, 25, 50); spawn.bodyRect(2000, -2325, 75, 100); spawn.bodyRect(2150, -2300, 75, 100); spawn.bodyRect(1975, -2300, 75, 75); spawn.bodyRect(2150, -2300, 75, 75); spawn.bodyRect(2025, -2350, 50, 125); spawn.bodyRect(2250, -2325, 50, 75); spawn.randomMob(2625, -750, 0); spawn.randomMob(3200, -725, 0); spawn.randomMob(2900, -575, 0); spawn.randomMob(700, -1100, 0); spawn.randomMob(3275, -1575, 0); spawn.randomMob(3950, -1500, 0); spawn.randomMob(3725, -1300, 0); spawn.randomMob(3625, -1700, 0); spawn.randomMob(2250, -1675, 0); spawn.randomMob(550, -1875, 0); spawn.randomMob(1600, -700, 0); spawn.randomMob(1050, -400, 0); spawn.randomSmallMob(1085, -1591); spawn.randomSmallMob(1516, -532); spawn.randomGroup(1551, -466, 0.4); if (simulation.difficulty > 1) spawn.randomLevelBoss(3928, -655); spawn.secondaryBossChance(4088, -1744) level.custom = () => { level.exit.drawAndCheck(); level.enter.draw(); }; level.customTopLayer = () => { }; }, unchartedCave() { simulation.inGameConsole(`unchartedCave by 3xionDev`); level.setPosToSpawn(0, -50); //normal spawn level.exit.x = 20985; level.exit.y = 2816; 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 = 1900 simulation.zoomTransition(level.defaultZoom) document.body.style.backgroundColor = "#979797"; spawn.randomMob(1000, -975, 0); spawn.randomMob(2550, -575, 0); spawn.randomMob(2050, -325, 0); spawn.randomMob(2475, -1350, 0); spawn.randomMob(2800, -1100, 0); spawn.randomMob(3400, -1250, 0); spawn.randomMob(3375, -425, 0); spawn.randomMob(4575, -775, 0); spawn.randomMob(5750, -750, 0); spawn.randomMob(5475, -325, 0); spawn.randomMob(7625, -250, 0); spawn.randomMob(9675, 525, 0); spawn.randomMob(12150, 1200, 0); spawn.randomMob(12825, 1275, 0); spawn.randomMob(13175, 1725, 0); spawn.randomMob(13725, 1375, 0); spawn.randomMob(15550, 2375, 0); spawn.randomMob(17625, 3550, 0); spawn.randomMob(18850, 3500, 0); spawn.mapRect(-150, 0, 300, 75); spawn.mapRect(-375, 25, 2550, 75); spawn.mapRect(-400, -950, 75, 1025); spawn.mapRect(-400, 75, 50, 25); spawn.mapRect(-400, -1750, 100, 800); spawn.mapRect(-350, -1050, 50, 1125); spawn.mapRect(-400, -1950, 100, 275); spawn.mapRect(1875, 25, 2075, 75); spawn.mapRect(-400, 50, 4350, 1175); spawn.mapRect(-400, 650, 4350, 1175); spawn.mapRect(2975, 25, 3575, 1800); spawn.mapRect(-375, -1925, 1725, 800); spawn.mapRect(-400, -2275, 5625, 725); spawn.mapRect(1075, -1725, 500, 425); spawn.mapRect(1275, -1325, 200, 125); spawn.mapRect(1525, -1325, 25, 75); spawn.mapRect(1400, -1425, 125, 200); spawn.mapRect(1400, -1725, 325, 275); spawn.mapRect(1675, -1600, 125, 100); spawn.mapRect(-350, -1200, 250, 250); spawn.mapRect(-175, -1225, 250, 175); spawn.mapRect(-50, -1150, 275, 50); spawn.bodyRect(-275, -475, 75, 175); spawn.bodyRect(325, -750, 50, 50); spawn.mapRect(2075, -1575, 75, 725); spawn.mapRect(1900, -1600, 50, 325); spawn.mapRect(2600, -1675, 25, 850); spawn.mapRect(2125, -1625, 125, 425); spawn.mapRect(2125, -1200, 50, 75); spawn.mapRect(2075, -875, 25, 150); spawn.mapRect(2025, -1625, 50, 250); spawn.mapRect(4700, -2275, 2175, 1275); spawn.mapRect(3975, -1725, 1125, 400); spawn.mapRect(4300, -1400, 650, 200); spawn.mapRect(4600, -1225, 175, 125); spawn.mapRect(3925, -1600, 175, 150); spawn.mapRect(3950, -1450, 50, 75); spawn.mapRect(4225, -1350, 150, 100); spawn.mapRect(3875, -1600, 175, 175); spawn.mapRect(4450, -1275, 200, 125); spawn.mapRect(4850, -1125, 50, 550); spawn.mapRect(5225, -1175, 25, 400); spawn.mapRect(3250, -1725, 25, 1200); spawn.mapRect(3600, -1750, 75, 550); spawn.mapRect(2850, -1700, 325, 325); spawn.mapRect(2900, -1525, 175, 750); spawn.mapRect(2975, -900, 75, 450); spawn.mapRect(550, -175, 150, 500); spawn.mapRect(625, -300, 125, 450); spawn.mapRect(725, -175, 50, 475); spawn.mapRect(675, -400, 25, 200); spawn.mapRect(450, -25, 150, 150); spawn.mapRect(275, -1200, 75, 625); spawn.mapRect(300, -1300, 150, 600); spawn.mapRect(425, -1250, 25, 800); spawn.mapRect(425, -1225, 125, 425); spawn.mapRect(525, -1175, 100, 250); spawn.mapRect(175, -1200, 175, 375); spawn.mapRect(1600, -375, 50, 650); spawn.mapRect(1650, -525, 75, 725); spawn.mapRect(1450, -225, 150, 475); spawn.mapRect(1400, -125, 100, 275); spawn.mapRect(1500, -325, 125, 150); spawn.mapRect(1750, -225, 100, 350); spawn.mapRect(1700, -300, 75, 275); spawn.mapRect(1850, -150, 25, 325); spawn.mapRect(1750, -50, 75, 225); spawn.mapRect(1600, -50, 200, 250); spawn.mapRect(1700, -600, 25, 125); spawn.bodyRect(1275, -250, 50, 125); spawn.mapRect(6450, 50, 675, 2000); spawn.mapRect(6925, 175, 675, 300); spawn.mapRect(7450, 300, 675, 775); spawn.mapRect(6925, 325, 1275, 1850); spawn.mapRect(7025, 125, 375, 100); spawn.mapRect(7500, 275, 425, 100); spawn.mapRect(6700, -2275, 1975, 1575); spawn.mapRect(6475, -1175, 400, 300); spawn.mapRect(6550, -925, 250, 150); spawn.mapRect(6600, -825, 175, 75); spawn.mapRect(6300, -1050, 325, 100); spawn.mapRect(8050, 400, 450, 1675); spawn.mapRect(8275, 475, 800, 1550); spawn.mapRect(8600, 575, 925, 1475); spawn.mapRect(8400, 425, 300, 100); spawn.mapRect(8975, 550, 300, 100); spawn.mapRect(6900, -775, 2375, 200); spawn.mapRect(7350, -725, 2125, 275); spawn.mapRect(7725, -550, 1800, 225); spawn.mapRect(8300, -400, 1750, 125); spawn.mapRect(8725, -350, 1350, 225); spawn.mapRect(8525, -375, 400, 150); spawn.mapRect(8650, -250, 150, 50); spawn.mapRect(8200, -350, 175, 50); spawn.mapRect(7600, -475, 275, 100); spawn.mapRect(7475, -500, 175, 75); spawn.mapRect(7200, -600, 300, 75); spawn.mapRect(7075, -600, 150, 25); spawn.mapRect(7125, -575, 100, 25); spawn.mapRect(7275, -525, 100, 25); spawn.mapRect(7500, 225, 275, 125); spawn.mapRect(5800, -1175, 675, 100); spawn.mapRect(9400, 625, 500, 1725); spawn.mapRect(9750, 775, 575, 1500); spawn.mapRect(10050, 1075, 675, 1300); spawn.mapRect(9725, -250, 975, 350); spawn.mapRect(10275, -100, 1000, 625); spawn.mapRect(10125, 25, 375, 350); spawn.mapRect(9900, 25, 375, 225); spawn.mapRect(10075, 250, 200, 50); spawn.mapRect(10275, 425, 75, 75); spawn.mapRect(10150, 325, 200, 75); spawn.mapRect(10200, 375, 125, 100); spawn.mapRect(9600, -175, 225, 200); spawn.mapRect(9650, -25, 125, 100); spawn.mapRect(9450, -200, 225, 175); spawn.mapRect(10000, 200, 50, 25); spawn.mapRect(9800, 50, 200, 125); spawn.mapRect(9875, 725, 150, 75); spawn.mapRect(10300, 900, 100, 325); spawn.mapRect(10375, 950, 75, 225); spawn.mapRect(10425, 1000, 75, 175); spawn.mapRect(9100, -150, 375, 75); spawn.mapRect(3525, 0, 1850, 100); spawn.mapRect(3650, -25, 1475, 175); spawn.mapRect(4000, -100, 950, 225); spawn.mapRect(4450, -175, 300, 175); spawn.mapRect(1825, -25, 775, 100); spawn.mapRect(4150, -1375, 175, 75); spawn.mapRect(3800, -1600, 125, 100); spawn.mapRect(2700, 0, 175, 100); spawn.mapRect(2750, -50, 75, 150); spawn.mapRect(2775, -25, 75, 75); spawn.bodyRect(3775, -575, 50, 75); spawn.mapRect(8050, -2300, 4650, 1750); spawn.mapRect(9300, -975, 2450, 575); spawn.mapRect(9925, -775, 2350, 700); spawn.mapRect(10675, -600, 2025, 1175); spawn.mapRect(10300, 825, 50, 125); spawn.mapRect(10000, 750, 100, 25); spawn.mapRect(9875, 700, 75, 75); spawn.mapRect(9850, 675, 75, 50); spawn.mapRect(8975, 525, 225, 50); spawn.mapRect(8625, 450, 225, 50); spawn.mapRect(8925, -150, 175, 50); spawn.mapRect(-500, 1775, 10800, 1800); spawn.mapRect(10075, 2200, 625, 1850); spawn.mapRect(10525, 475, 250, 75); spawn.mapRect(10450, 1025, 25, 25); spawn.mapRect(10450, 1050, 150, 25); spawn.mapRect(10675, 1125, 175, 1700); spawn.mapRect(10725, 1225, 400, 725); spawn.mapRect(10975, 1425, 300, 1300); spawn.mapRect(11225, 1550, 700, 1275); spawn.mapRect(11725, 1625, 1150, 1150); spawn.mapRect(11050, 1275, 125, 350); spawn.mapRect(11150, 1350, 75, 175); spawn.mapRect(10825, 1200, 125, 75); spawn.mapRect(10325, 1750, 1275, 1150); spawn.mapRect(11250, 1500, 325, 100); spawn.mapRect(11225, 1475, 200, 75); spawn.mapRect(11525, 1525, 350, 75); spawn.mapRect(11875, 1575, 425, 100); spawn.mapRect(12200, 1625, 25, 25); spawn.mapRect(12200, 1600, 250, 75); spawn.randomMob(11250, 675, 0); spawn.bodyRect(10900, 575, 50, 625); spawn.mapRect(11050, 575, 1725, 200); spawn.mapRect(11275, 750, 1650, 125); spawn.mapRect(11575, 875, 1525, 150); spawn.mapRect(11450, 850, 250, 125); spawn.mapRect(11325, 850, 225, 50); spawn.mapRect(11400, 875, 100, 75); spawn.mapRect(11175, 750, 200, 75); spawn.mapRect(11125, 775, 100, 25); spawn.mapRect(11250, 825, 50, 25); spawn.mapRect(11675, 1025, 400, 50); spawn.mapRect(12175, 1025, 50, 150); spawn.mapRect(11625, 1000, 75, 50); spawn.mapRect(12200, 1175, 25, 25); spawn.mapRect(12400, 950, 100, 275); spawn.mapRect(12450, 1200, 50, 100); spawn.mapRect(12425, 1225, 50, 125); spawn.mapRect(12425, 1325, 25, 75); spawn.mapRect(12325, 1025, 25, 100); spawn.mapRect(12625, 1000, 625, 150); spawn.mapRect(12775, 1150, 600, 50); spawn.bodyRect(12075, 1325, 100, 50); spawn.mapRect(12825, 2000, 800, 975); spawn.mapRect(13475, 1625, 150, 475); spawn.mapRect(12850, 1750, 50, 275); spawn.mapRect(12875, 1850, 75, 225); spawn.mapRect(12950, 1925, 75, 125); spawn.mapRect(13450, 1700, 50, 350); spawn.mapRect(13400, 1750, 75, 350); spawn.mapRect(13325, 1875, 100, 200); spawn.mapRect(13350, 1800, 75, 100); spawn.mapRect(13300, 1950, 25, 75); spawn.mapRect(12925, 1900, 75, 25); spawn.mapRect(12900, 1800, 25, 75); spawn.mapRect(13000, 1975, 150, 25); spawn.mapRect(12475, 100, 1150, 925); spawn.mapRect(13200, 100, 925, 1100); spawn.mapRect(13525, 1150, 400, 75); spawn.mapRect(13875, 1200, 175, 125); spawn.mapRect(13975, 1300, 75, 225); spawn.mapRect(14025, 1425, 25, 225); spawn.mapRect(13600, 1700, 100, 1250); spawn.mapRect(13675, 1775, 150, 1200); spawn.mapRect(13775, 1825, 75, 1150); spawn.mapRect(13825, 1900, 50, 1075); spawn.mapRect(13850, 1950, 75, 1025); spawn.mapRect(13875, 2025, 75, 950); spawn.mapRect(13925, 2100, 50, 1025); spawn.mapRect(13900, 2125, 100, 1000); spawn.mapRect(13975, 2225, 75, 900); spawn.mapRect(14000, 2375, 100, 750); spawn.mapRect(14050, 1200, 75, 525); spawn.mapRect(14075, 1625, 100, 150); spawn.mapRect(14175, 1750, 50, 175); spawn.mapRect(14150, 1750, 50, 75); spawn.mapRect(14225, 1875, 50, 150); spawn.mapRect(14250, 2000, 50, 150); spawn.mapRect(14100, 300, 300, 1450); spawn.mapRect(14225, 1675, 150, 250); spawn.mapRect(14300, 1700, 100, 675); spawn.mapRect(14275, 1925, 50, 175); spawn.mapRect(14025, 2300, 50, 200); spawn.mapRect(14075, 2600, 275, 700); spawn.mapRect(14050, 2425, 75, 475); spawn.mapRect(14100, 2525, 50, 150); spawn.mapRect(14150, 2575, 50, 100); spawn.mapRect(14325, 2650, 800, 1300); spawn.mapRect(15000, 2600, 1075, 1275); spawn.mapRect(15100, 2550, 1200, 1400); spawn.mapRect(14375, 1075, 1400, 1175); spawn.mapRect(14375, 2225, 200, 50); spawn.mapRect(14375, 2275, 75, 75); spawn.mapRect(14425, 2275, 75, 25); spawn.mapRect(14775, 2200, 50, 125); spawn.mapRect(14725, 2250, 25, 125); spawn.bodyRect(14300, 2375, 50, 225); spawn.mapRect(15075, 2600, 50, 25); spawn.mapRect(15075, 2575, 50, 50); spawn.mapRect(14950, 2625, 50, 25); spawn.mapRect(15525, 2525, 300, 50); spawn.mapRect(15550, 2500, 175, 100); spawn.mapRect(14975, 2250, 200, 25); spawn.mapRect(14400, 1000, 1900, 1250); spawn.mapRect(15925, 2250, 275, 25); spawn.mapRect(15975, 2275, 150, 25); spawn.mapRect(16075, 2300, 25, 75); spawn.mapRect(16250, 2575, 275, 1275); spawn.mapRect(16400, 2600, 250, 1300); spawn.mapRect(16525, 2675, 300, 1150); spawn.mapRect(16650, 2725, 300, 1175); spawn.mapRect(16200, 1050, 600, 1225); spawn.mapRect(16525, 1025, 500, 1300); spawn.mapRect(16725, 1150, 525, 1225); spawn.mapRect(16900, 1425, 550, 1000); spawn.mapRect(16375, 2250, 250, 50); spawn.mapRect(16675, 2325, 75, 25); spawn.mapRect(16850, 2400, 25, 25); spawn.mapRect(16850, 2375, 100, 50); spawn.mapRect(16800, 2375, 50, 25); spawn.mapRect(16575, 2650, 150, 75); spawn.mapRect(16625, 2625, 50, 50); spawn.mapRect(16800, 2700, 50, 50); spawn.mapRect(16950, 2900, 450, 1175); spawn.mapRect(16900, 2825, 100, 275); spawn.mapRect(16900, 2775, 75, 150); spawn.mapRect(16950, 2850, 100, 125); spawn.mapRect(17600, 3000, 350, 25); spawn.mapRect(17650, 3025, 200, 50); spawn.mapRect(17750, 3050, 100, 1200); spawn.mapRect(17850, 3025, 25, 425); spawn.mapRect(18200, 3100, 450, 25); spawn.mapRect(18250, 3125, 150, 100); spawn.mapRect(18400, 3125, 200, 50); spawn.mapRect(18400, 3150, 75, 900); spawn.mapRect(18400, 3950, 75, 375); spawn.mapRect(17750, 4000, 100, 325); spawn.mapRect(18200, 3075, 525, 50); spawn.mapRect(18450, 3175, 50, 275); spawn.mapRect(18575, 3125, 50, 50); spawn.mapRect(18600, 3100, 50, 50); spawn.mapRect(18325, 3200, 100, 175); spawn.mapRect(18350, 3350, 75, 100); spawn.mapRect(18375, 3450, 25, 75); spawn.mapRect(18500, 3175, 25, 150); spawn.mapRect(17725, 3075, 50, 175); spawn.mapRect(17700, 3075, 25, 50); spawn.mapRect(18550, 3150, 50, 1275); spawn.mapRect(18975, 2975, 525, 25); spawn.mapRect(19025, 2975, 400, 75); spawn.mapRect(19150, 3025, 150, 1400); spawn.mapRect(19250, 3025, 100, 325); spawn.mapRect(19100, 3050, 75, 225); spawn.mapRect(19125, 3275, 50, 75); spawn.mapRect(19275, 3325, 50, 200); spawn.mapRect(19700, 3000, 525, 50); spawn.mapRect(19750, 3050, 175, 1275); spawn.mapRect(19925, 3025, 100, 225); spawn.mapRect(19900, 3200, 75, 250); spawn.mapRect(20025, 3050, 50, 100); spawn.mapRect(20000, 3150, 50, 75); spawn.mapRect(19900, 3450, 50, 125); spawn.mapRect(19725, 3050, 75, 150); spawn.mapRect(19650, 3025, 25, 25); spawn.mapRect(19650, 3000, 625, 25); spawn.mapRect(17375, 1450, 3525, 250); spawn.mapRect(17400, 1675, 100, 400); spawn.mapRect(17475, 1675, 75, 150); spawn.mapRect(17525, 1650, 125, 75); spawn.mapRect(17475, 1775, 50, 175); spawn.mapRect(17425, 2025, 50, 200); spawn.mapRect(17825, 1650, 375, 75); spawn.mapRect(17975, 1700, 100, 125); spawn.mapRect(18050, 1800, 50, 150); spawn.mapRect(18025, 1675, 100, 150); spawn.mapRect(18075, 1900, 25, 175); spawn.mapRect(20550, 2825, 825, 1525); spawn.mapRect(20825, 1450, 550, 250); spawn.mapRect(21350, 1450, 300, 2700); spawn.mapRect(21100, 1650, 275, 425); spawn.mapRect(21225, 2025, 175, 275); spawn.mapRect(21175, 2025, 75, 150); spawn.mapRect(21300, 2275, 75, 150); spawn.mapRect(21000, 1675, 100, 300); spawn.mapRect(21050, 1950, 50, 100); spawn.mapRect(20700, 1700, 200, 50); spawn.mapRect(20750, 1725, 50, 225); spawn.mapRect(20800, 1725, 50, 175); spawn.mapRect(20750, 1925, 25, 150); spawn.mapRect(20975, 1675, 50, 225); spawn.mapRect(21275, 2300, 50, 50); spawn.mapRect(20550, 1700, 150, 50); spawn.mapRect(20550, 1725, 50, 825); spawn.mapRect(20575, 1725, 50, 300); spawn.mapRect(20600, 1725, 50, 75); spawn.mapRect(20600, 1875, 25, 350); spawn.mapRect(20675, 1750, 25, 400); spawn.bodyRect(20550, 2550, 50, 275); spawn.mapRect(20500, 2850, 75, 1500); spawn.mapRect(20475, 2900, 50, 1425); spawn.bodyRect(21200, 2550, 50, 75); spawn.bodyRect(20775, 2225, 75, 75); spawn.mapRect(19525, 1700, 550, 25); spawn.mapRect(19650, 1725, 100, 275); spawn.mapRect(19725, 1700, 50, 525); spawn.mapRect(19750, 1725, 250, 150); spawn.mapRect(19775, 1850, 100, 225); spawn.mapRect(19850, 1875, 100, 75); spawn.mapRect(19600, 1675, 125, 175); spawn.mapRect(18425, 1700, 1050, 50); spawn.mapRect(18475, 1750, 800, 25); spawn.randomMob(12000, 2850, 0); spawn.mapRect(10550, 2725, 5275, 1775); spawn.mapRect(15700, 3850, 5950, 1050); spawn.mapRect(8225, 2975, 4050, 1500); spawn.mapRect(12100, -2300, 9350, 2925); spawn.mapRect(14175, 350, 8725, 1075); spawn.mapRect(20825, -2325, 3700, 2650); spawn.mapRect(21450, 50, 4450, 5925); spawn.mapRect(16325, 1125, 7175, 400); spawn.mapRect(-875, -3775, 27375, 1625); spawn.mapRect(-3250, -4000, 2925, 8075); spawn.mapRect(8125, 4475, 18250, 7300); spawn.mapRect(7125, 3975, 8775, 2800); spawn.mapRect(-4300, -4475, 2300, 11350); spawn.randomGroup(5835, -532, 0.4); if (simulation.difficulty > 1) spawn.randomLevelBoss(18823, 2191); spawn.secondaryBossChance(20217, 1913) level.custom = () => { level.exit.drawAndCheck(); level.enter.draw(); }; level.customTopLayer = () => { }; }, dojo() { // By weird_pusheen simulation.inGameConsole(`dojo by werid_pusheen, fixed by Cornbread 2100`) 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)); for (const vanishBlock of vanishes) Composite.add(engine.world, vanishBlock); 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 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); //this is the line that is bugging <----- }; 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 }, arena() { simulation.inGameConsole(`arena by Whyisthisnotavalable`) let genisis, genisisJumpSensor, genisisBody, genisisHead, genisisHeadSensor, genisisBodySensor; let control = { left: false, right: false, up: false, down: false }; const g = { spawn() { //load genisis in matter.js physic engine // let vector = Vertices.fromPath("0 40 50 40 50 115 0 115 30 130 20 130"); //genisis as a series of vertices let vertices = Vertices.fromPath("0,40, 50,40, 50,115, 30,130, 20,130, 0,115, 0,40"); //genisis as a series of vertices genisisBody = Bodies.fromVertices(0, 0, vertices); genisisJumpSensor = Bodies.rectangle(0, 46, 36, 6, { //this sensor check if the genisis is on the ground to enable jumping sleepThreshold: 99999999999, isSensor: true }); vertices = Vertices.fromPath("16 -82 2 -66 2 -37 43 -37 43 -66 30 -82"); genisisHead = Bodies.fromVertices(0, -55, vertices); //this part of the genisis lowers on crouch genisisHeadSensor = Bodies.rectangle(0, -57, 48, 45, { //senses if the genisis's head is empty and can return after crouching sleepThreshold: 99999999999, isSensor: true }); genisisBodySensor = Bodies.rectangle(0, 0, 70, 45, { sleepThreshold: 99999999999, isSensor: true }); genisis = Body.create({ //combine genisisJumpSensor and genisisBody parts: [genisisBody, genisisHead, genisisJumpSensor, genisisHeadSensor, genisisBodySensor], inertia: Infinity, //prevents genisis rotation friction: 0.002, frictionAir: 0.001, //frictionStatic: 0.5, restitution: 0, sleepThreshold: Infinity, collisionFilter: { group: 0, category: cat.mob, mask: cat.body | cat.map | cat.mob | cat.mobBullet | cat.mobShield | cat.player | cat.bullet }, // death() { // g.death(); // } }); Matter.Body.setMass(genisis, g.mass); Composite.add(engine.world, [genisis]); }, health: 1000, maxHealth: 1000, //set in simulation.reset() cycle: 600, //starts at 600 cycles instead of 0 to prevent bugs with g.history lastKillCycle: 0, lastHarmCycle: 0, width: 50, radius: 30, eyeFillColor: null, fillColor: null, //set by setFillColors fillColorDark: null, //set by setFillColors bodyGradient: null, //set by setFillColors color: { hue: 0, sat: 0, light: 50, }, setFillColors() { g.fillColor = `hsl(${g.color.hue},${g.color.sat}%,${g.color.light}%)` g.fillColorDark = `hsl(${g.color.hue},${g.color.sat}%,${g.color.light - 25}%)` let grd = ctx.createLinearGradient(-30, 0, 30, 0); grd.addColorStop(0, g.fillColorDark); grd.addColorStop(1, g.fillColor); g.bodyGradient = grd }, setFillColorsAlpha(alpha = 0.5) { g.fillColor = `hsla(${g.color.hue},${g.color.sat}%,${g.color.light}%,${alpha})` g.fillColorDark = `hsla(${g.color.hue},${g.color.sat}%,${g.color.light - 25}%,${alpha})` let grd = ctx.createLinearGradient(-30, 0, 30, 0); grd.addColorStop(0, g.fillColorDark); grd.addColorStop(1, g.fillColor); g.bodyGradient = grd }, height: 42, yOffWhen: { crouch: 22, stand: 49, jump: 70 }, defaultMass: 5, mass: 5, FxNotHolding: 0.015, Fx: 0.016, //run Force on ground // jumpForce: 0.64, setMovement() { // g.Fx = 0.08 / mass * tech.squirrelFx // g.FxAir = 0.4 / mass / mass g.Fx = tech.baseFx * g.fieldFx * tech.squirrelFx * (tech.isFastTime ? 1.5 : 1) / genisis.mass //base genisis mass is 5 g.jumpForce = tech.baseJumpForce * g.fieldJump * tech.squirrelJump * (tech.isFastTime ? 1.13 : 1) / genisis.mass / genisis.mass //base genisis mass is 5 }, FxAir: 0.032, // 0.4/5/5 run Force in Air yOff: 70, yOffGoal: 70, onGround: false, //checks if on ground or in air lastOnGroundCycle: 0, //use to calculate coyote time standingOn: undefined, numTouching: 0, crouch: false, // isHeadClear: true, spawnPos: { x: 0, y: 0 }, spawnVel: { x: 0, y: 0 }, pos: { x: 0, y: 0 }, yPosDifference: 24.2859, //genisis.position.y - g.pos.y //24.285923217549026 // yPosDifferenceCrouched: -2.7140767824453604, Sy: 0, //adds a smoothing effect to vertical only Vx: 0, Vy: 0, friction: { ground: 0.01, air: 0.0025 }, airSpeedLimit: 125, // 125/mass/mass = 5 angle: 0, walk_cycle: 0, stepSize: 0, flipLegs: -1, hip: { x: 12, y: 24 }, knee: { x: 0, y: 0, x2: 0, y2: 0 }, foot: { x: 0, y: 0 }, legLength1: 55, legLength2: 45, transX: 0, transY: 0, history: new Array(600), //[], //tracks the last second of genisis position rewindCount: 0, //used with CPT resetHistory() { const set = { position: { x: genisis.position.x, y: genisis.position.y, }, velocity: { x: genisis.velocity.x, y: genisis.velocity.y }, yOff: g.yOff, angle: g.angle, health: g.health, energy: g.energy, activeGun: b.activeGun } for (let i = 0; i < 600; i++) { //reset history g.history[i] = set } }, move() { g.pos.x = genisis.position.x; g.pos.y = genisisBody.position.y - g.yOff; g.Vx = genisis.velocity.x; g.Vy = genisis.velocity.y; //tracks the last 10s of genisis information g.history.splice(g.cycle % 600, 1, { position: { x: genisis.position.x, y: genisis.position.y, }, velocity: { x: genisis.velocity.x, y: genisis.velocity.y }, yOff: g.yOff, angle: g.angle, health: g.health, energy: g.energy, activeGun: b.activeGun }); // const back = 59 // 59 looks at 1 second ago //29 looks at 1/2 a second ago // historyIndex = (g.cycle - back) % 600 }, targetX: 0, targetY: 0, transSmoothX: 0, transSmoothY: 0, lastGroundedPositionY: 0, // mouseZoom: 0, lookSmoothing: 0.07, //1 is instant jerky, 0.001 is slow smooth zoom, 0.07 is standard look() { }, //set to lookDefault() lookDefault() { g.angle = Math.atan2( g.targetY - g.pos.y, g.targetX - g.pos.x ); // //smoothed mouse look translations const scale = 0.8; g.transSmoothX = canvas.width2 - g.pos.x - (simulation.mouse.x - canvas.width2) * scale; g.transSmoothY = canvas.height2 - g.pos.y - (simulation.mouse.y - canvas.height2) * scale; g.transX += (g.transSmoothX - g.transX) * g.lookSmoothing; g.transY += (g.transSmoothY - g.transY) * g.lookSmoothing; }, doCrouch() { if (!g.crouch) { g.crouch = true; g.yOffGoal = g.yOffWhen.crouch; if ((genisisHead.position.y - genisis.position.y) < 0) { Matter.Body.setPosition(genisisHead, { x: genisis.position.x, y: genisis.position.y + 9.1740767 }) } } }, undoCrouch() { if (g.crouch) { g.crouch = false; g.yOffGoal = g.yOffWhen.stand; if ((genisisHead.position.y - genisis.position.y) > 0) { Matter.Body.setPosition(genisisHead, { x: genisis.position.x, y: genisis.position.y - 30.28592321 }) } } }, hardLandCD: 0, checkHeadClear() { if (Matter.Query.collides(headSensor, map).length > 0) { return false } else { return true } }, buttonCD_jump: 0, //cool down for genisis buttons jump() { // if (!g.onGround) g.lastOnGroundCycle = 0 //g.cycle - tech.coyoteTime g.buttonCD_jump = g.cycle + 20; //can't jump again until 20 cycles pass //apply a fraction of the jump force to the body the genisis is jumping off of Matter.Body.applyForce(g.standingOn, g.pos, { x: 0, y: g.jumpForce * 0.12 * Math.min(g.standingOn.mass, 5) }); genisis.force.y = -g.jumpForce; //genisis jump force Matter.Body.setVelocity(genisis, { //zero genisis y-velocity for consistent jumps x: genisis.velocity.x, y: Math.max(-10, Math.min(g.standingOn.velocity.y, 10)) //cap velocity contribution from blocks you are standing on to 10 in the vertical }); }, groundControl() { //check for crouch or jump if (g.crouch) { if (!(control.down) && g.checkHeadClear() && g.hardLandCD < g.cycle) g.undoCrouch(); } else if (control.down || g.hardLandCD > g.cycle) { g.doCrouch(); //on ground && not crouched and pressing s or down } else if (control.up && g.buttonCD_jump + 20 < g.cycle && g.yOffWhen.stand > 23) { g.jump() } if (control.left) { if (genisis.velocity.x > -2) { genisis.force.x -= g.Fx * 1.5 } else { genisis.force.x -= g.Fx } // } } else if (control.right) { if (genisis.velocity.x < 2) { genisis.force.x += g.Fx * 1.5 } else { genisis.force.x += g.Fx } } else { const stoppingFriction = 0.92; //come to a stop if no move key is pressed Matter.Body.setVelocity(genisis, { x: genisis.velocity.x * stoppingFriction, y: genisis.velocity.y * stoppingFriction }); } //come to a stop if fast if (genisis.speed > 4) { const stoppingFriction = (g.crouch) ? 0.65 : 0.89; // this controls speed when crouched Matter.Body.setVelocity(genisis, { x: genisis.velocity.x * stoppingFriction, y: genisis.velocity.y * stoppingFriction }); } }, airControl() { //check for coyote time jump // if (control.up && g.buttonCD_jump + 20 + tech.coyoteTime < g.cycle && g.yOffWhen.stand > 23 && g.lastOnGroundCycle + tech.coyoteTime > g.cycle) g.jump() if (control.up && g.buttonCD_jump + 20 < g.cycle && g.yOffWhen.stand > 23 && g.lastOnGroundCycle + 5 > g.cycle) g.jump() //check for short jumps //moving up //recently pressed jump //but not pressing jump key now if (g.buttonCD_jump + 60 > g.cycle && !(control.up) && g.Vy < 0) { Matter.Body.setVelocity(genisis, { //reduce genisis y-velocity every cycle x: genisis.velocity.x, y: genisis.velocity.y * 0.94 }); } if (control.left) { if (genisis.velocity.x > -g.airSpeedLimit / genisis.mass / genisis.mass) genisis.force.x -= g.FxAir; // move genisis left / a } else if (control.right) { if (genisis.velocity.x < g.airSpeedLimit / genisis.mass / genisis.mass) genisis.force.x += g.FxAir; //move genisis right / d } }, alive: true, dmgScale: 1, //scales all damage, but not raw .dmg //set in levels.setDifficulty defaultFPSCycle: 0, //tracks when to return to normal fps immuneCycle: 0, //used in engine collisionImmuneCycles: 30, buttonCD: 0, //cool down for genisis buttons drawLeg(stroke) { // if (simulation.mouseInGame.x > g.pos.x) { if (g.angle > -Math.PI / 2 && g.angle < Math.PI / 2) { g.flipLegs = 1; } else { g.flipLegs = -1; } ctx.save(); ctx.scale(g.flipLegs, 1); //leg lines ctx.beginPath(); ctx.moveTo(g.hip.x, g.hip.y); ctx.lineTo(g.knee.x, g.knee.y); ctx.lineTo(g.foot.x, g.foot.y); ctx.strokeStyle = stroke; ctx.lineWidth = 7; ctx.stroke(); //toe lines ctx.beginPath(); ctx.moveTo(g.foot.x, g.foot.y); ctx.lineTo(g.foot.x - 15, g.foot.y + 5); ctx.moveTo(g.foot.x, g.foot.y); ctx.lineTo(g.foot.x + 15, g.foot.y + 5); ctx.lineWidth = 4; ctx.stroke(); //hip joint ctx.beginPath(); ctx.arc(g.hip.x, g.hip.y, 11, 0, 2 * Math.PI); //knee joint ctx.moveTo(g.knee.x + 7, g.knee.y); ctx.arc(g.knee.x, g.knee.y, 7, 0, 2 * Math.PI); //foot joint ctx.moveTo(g.foot.x + 6, g.foot.y); ctx.arc(g.foot.x, g.foot.y, 6, 0, 2 * Math.PI); ctx.fillStyle = g.fillColor; ctx.fill(); ctx.lineWidth = 2; ctx.stroke(); ctx.restore(); }, calcLeg(cycle_offset, offset) { g.hip.x = 12 + offset; g.hip.y = 24 + offset; //stepSize goes to zero if Vx is zero or not on ground (make m transition cleaner) g.stepSize = 0.8 * g.stepSize + 0.2 * (7 * Math.sqrt(Math.min(9, Math.abs(g.Vx))) * g.onGround); //changes to stepsize are smoothed by adding only a percent of the new value each cycle const stepAngle = 0.034 * g.walk_cycle + cycle_offset; g.foot.x = 2.2 * g.stepSize * Math.cos(stepAngle) + offset; g.foot.y = offset + 1.2 * g.stepSize * Math.sin(stepAngle) + g.yOff + g.height; const Ymax = g.yOff + g.height; if (g.foot.y > Ymax) g.foot.y = Ymax; //calculate knee position as intersection of circle from hip and foot const d = Math.sqrt((g.hip.x - g.foot.x) * (g.hip.x - g.foot.x) + (g.hip.y - g.foot.y) * (g.hip.y - g.foot.y)); const l = (g.legLength1 * g.legLength1 - g.legLength2 * g.legLength2 + d * d) / (2 * d); const h = Math.sqrt(g.legLength1 * g.legLength1 - l * l); g.knee.x = (l / d) * (g.foot.x - g.hip.x) - (h / d) * (g.foot.y - g.hip.y) + g.hip.x + offset; g.knee.y = (l / d) * (g.foot.y - g.hip.y) + (h / d) * (g.foot.x - g.hip.x) + g.hip.y; }, draw() { }, drawDefault() { ctx.fillStyle = g.fillColor; g.walk_cycle += g.flipLegs * g.Vx; ctx.save(); ctx.globalAlpha = (g.immuneCycle < g.cycle) ? 1 : 0.5 //|| (g.cycle % 40 > 20) ctx.translate(g.pos.x, g.pos.y); g.calcLeg(Math.PI, -3); g.drawLeg("#FFFFFF"); g.calcLeg(0, 0); g.drawLeg("#FFFFFF"); ctx.rotate(g.angle); ctx.beginPath(); ctx.arc(0, 0, 30, 0, 2 * Math.PI); ctx.fillStyle = g.bodyGradient ctx.fill(); ctx.arc(15, 0, 4, 0, 2 * Math.PI); ctx.strokeStyle = "#FFFFFF"; ctx.lineWidth = 2; ctx.stroke(); ctx.restore(); g.yOff = g.yOff * 0.85 + g.yOffGoal * 0.15; //smoothly move leg height towards height goal }, drawDamage() { ctx.fillStyle = "red"; g.walk_cycle += g.flipLegs * g.Vx; ctx.save(); ctx.globalAlpha = 0.7; ctx.translate(g.pos.x, g.pos.y); g.calcLeg(Math.PI, -3); g.drawLeg("#FF0000"); g.calcLeg(0, 0); g.drawLeg("#FF0000"); ctx.rotate(g.angle); ctx.beginPath(); ctx.arc(0, 0, 30, 0, 2 * Math.PI); ctx.fillStyle = g.bodyGradient ctx.fill(); ctx.arc(15, 0, 4, 0, 2 * Math.PI); ctx.strokeStyle = "#FF0000"; ctx.lineWidth = 2; ctx.stroke(); ctx.restore(); g.yOff = g.yOff * 0.85 + g.yOffGoal * 0.15; //smoothly move leg height towards height goal }, damage(dmg) { g.health -= dmg; }, rebirth() { g.health = g.maxHealth; genisis.collisionFilter.mask = cat.body | cat.map | cat.mob | cat.mobBullet | cat.mobShield | cat.player | cat.bullet }, lastHealth: 1000, drawHealth() { let interpolated = this.lastHealth + (g.health - this.lastHealth) * 0.1; ctx.save(); ctx.setTransform(1, 0, 0.2, 1, 0, 0); //slanted ctx.fillStyle = "rgba(250, 100, 100, 0.3)"; ctx.fillRect(canvas.width2 / 2, canvas.height2 / 10, canvas.width2, 30); const grad = ctx.createLinearGradient(0, 0, canvas.width2, 0); grad.addColorStop(0, "lightblue"); grad.addColorStop(1, "crimson"); ctx.fillStyle = grad; ctx.fillRect(canvas.width2 / 2, canvas.height2 / 10, canvas.width2 * interpolated / 1000, 30); ctx.restore(); this.lastHealth = interpolated; }, genisisOnGroundCheck(event) { //runs on collisions events function enter() { g.numTouching++; if (!g.onGround) { g.onGround = true; if (g.crouch) { if (g.checkHeadClear()) { g.undoCrouch(); } else { g.yOffGoal = g.yOffWhen.crouch; } } else { //sets a hard land where genisis stays in a crouch for a bit and can't jump //crouch is forced in groundControl below const momentum = genisis.velocity.y * genisis.mass //genisis mass is 5 so this triggers at 26 down velocity, unless the genisis is holding something if (momentum > 130) { g.doCrouch(); g.yOff = g.yOffWhen.jump; g.hardLandCD = g.cycle + Math.min(momentum / 6.5 - 6, 40) //falling damage if (tech.isFallingDamage && g.immuneCycle < g.cycle && momentum > 150) { g.damage(Math.min(Math.sqrt(momentum - 133) * 0.01, 0.25)); if (g.immuneCycle < g.cycle + g.collisionImmuneCycles) g.immuneCycle = g.cycle + g.collisionImmuneCycles; //genisis is immune to damage for 30 cycles } } else { g.yOffGoal = g.yOffWhen.stand; } } } } const pairs = event.pairs; for (let i = 0, j = pairs.length; i != j; ++i) { let pair = pairs[i]; if (pair.bodyA === genisisJumpSensor) { g.standingOn = pair.bodyB; //keeping track to correctly provide recoil on jump if (g.standingOn.alive !== true) enter(); } else if (pair.bodyB === genisisJumpSensor) { g.standingOn = pair.bodyA; //keeping track to correctly provide recoil on jump if (g.standingOn.alive !== true) enter(); } } g.numTouching = 0; }, genisisOffGroundCheck(event) { //runs on collisions events const pairs = event.pairs; for (let i = 0, j = pairs.length; i != j; ++i) { if (pairs[i].bodyA === genisisJumpSensor || pairs[i].bodyB === genisisJumpSensor) { if (g.onGround && g.numTouching === 0) { g.onGround = false; g.lastOnGroundCycle = g.cycle; g.hardLandCD = 0 // disable hard landing if (g.checkHeadClear()) { if (g.crouch) { g.undoCrouch(); } g.yOffGoal = g.yOffWhen.jump; } } } } } }; function GenisisCollisionChecks(event) { const pairs = event.pairs; for (let i = 0, j = pairs.length; i != j; i++) { for (let k = 0; k < bullet.length; k++) { if (pairs[i].bodyA === bullet[k]) { collideBullet(pairs[i].bodyB); break; } else if (pairs[i].bodyB === bullet[k]) { collideBullet(pairs[i].bodyA); break; } function collideBullet(obj) { if ( g.immuneCycle < g.cycle && (obj === genisisBody || obj === genisisHead) ) { let dmg = Math.sqrt(Math.abs(0.000025 * Math.sqrt((bullet[k].mass + Math.sqrt(Vector.magnitude(bullet[k].velocity)) * 0.0000125)))); g.damage(dmg); return; } } } for (let k = 0; k < mob.length; k++) { if (mob[k].alive) { if (pairs[i].bodyA === mob[k]) { collideMob(pairs[i].bodyB); break; } else if (pairs[i].bodyB === mob[k]) { collideMob(pairs[i].bodyA); break; } function collideMob(obj) { //genisis + mob collision if ( g.immuneCycle < g.cycle && (obj === genisisBody || obj === genisisHead) && !mob[k].isSlowed && !mob[k].isStunned ) { let dmg = Math.max(0.025 * Math.sqrt(mob[k].mass), 0.05); // if (g.isCloak) dmg *= 0.5 if (tech.isRewindAvoidDeath && g.energy > 0.85 * Math.min(1, g.maxEnergy) && dmg > 0.01) { //CPT reversal runs in g.damage, but it stops the rest of the collision code here too g.damage(dmg); return } g.damage(dmg); if (tech.isCollisionRealitySwitch && g.alive) { g.switchWorlds() simulation.trails() simulation.inGameConsole(`simulation.amplitude = ${Math.random()}`); } if (tech.isPiezo) g.energy += 20.48; if (tech.isCouplingNoHit && g.coupling > 0) { g.couplingChange(-3) const unit = Vector.rotate({ x: 1, y: 0 }, 6.28 * Math.random()) let where = Vector.add(g.pos, Vector.mult(unit, 17)) simulation.drawList.push({ //add dmg to draw queue x: where.x, y: where.y, radius: 22, color: 'rgba(0, 171, 238, 0.33)', time: 8 }); where = Vector.add(g.pos, Vector.mult(unit, 60)) simulation.drawList.push({ //add dmg to draw queue x: where.x, y: where.y, radius: 18, color: 'rgba(0, 171, 238, 0.5)', time: 16 }); where = Vector.add(g.pos, Vector.mult(unit, 100)) simulation.drawList.push({ //add dmg to draw queue x: where.x, y: where.y, radius: 14, color: 'rgba(0, 171, 238, 0.6)', time: 24 }); where = Vector.add(g.pos, Vector.mult(unit, 135)) simulation.drawList.push({ //add dmg to draw queue x: where.x, y: where.y, radius: 10, color: 'rgba(0, 171, 238, 0.7)', time: 32 }); // simulation.drawList.push({ //add dmg to draw queue // x: g.pos.x, // y: g.pos.y, // radius: 150, // color: 'rgba(0, 171, 238, 0.33)', // time: 6 // }); // simulation.drawList.push({ //add dmg to draw queue // x: g.pos.x, // y: g.pos.y, // radius: 75, // color: 'rgba(0, 171, 238, 0.5)', // time: 16 // }); // simulation.drawList.push({ //add dmg to draw queue // x: g.pos.x, // y: g.pos.y, // radius: 25, // color: 'rgba(0, 171, 238, 0.75)', // time: 25 // }); } if (mob[k].onHit) mob[k].onHit(); if (g.immuneCycle < g.cycle + g.collisionImmuneCycles) g.immuneCycle = g.cycle + g.collisionImmuneCycles; //genisis is immune to damage for 30 cycles //extra kick between genisis and mob //this section would be better with forces but they don't work... let angle = Math.atan2(genisis.position.y - mob[k].position.y, genisis.position.x - mob[k].position.x); Matter.Body.setVelocity(genisis, { x: genisis.velocity.x + 8 * Math.cos(angle), y: genisis.velocity.y + 8 * Math.sin(angle) }); Matter.Body.setVelocity(mob[k], { x: mob[k].velocity.x - 8 * Math.cos(angle), y: mob[k].velocity.y - 8 * Math.sin(angle) }); if (tech.isAnnihilation && !mob[k].shield && !mob[k].isShielded && !mob[k].isBoss && mob[k].isDropPowerUp && g.energy > 0.08 && mob[k].damageReduction > 0) { g.energy -= 0.08 //* Math.max(g.maxEnergy, g.energy) //0.33 * g.energy if (g.immuneCycle === g.cycle + g.collisionImmuneCycles) g.immuneCycle = 0; //genisis doesn't go immune to collision damage mob[k].death(); simulation.drawList.push({ //add dmg to draw queue x: pairs[i].activeContacts[0].vertex.x, y: pairs[i].activeContacts[0].vertex.y, radius: Math.sqrt(dmg) * 500, color: "rgba(255,0,255,0.2)", time: simulation.drawTime }); } else { simulation.drawList.push({ //add dmg to draw queue x: pairs[i].activeContacts[0].vertex.x, y: pairs[i].activeContacts[0].vertex.y, radius: Math.sqrt(dmg) * 200, color: simulation.mobDmgColor, time: simulation.drawTime }); } // return; // } } } } } } } g.spawn(); Matter.Body.setPosition(genisis, { x: 7875, y: -2530 }) let isUsingSwordMod = false; for (let i = 0; i < tech.tech.length; i++) { if (tech.tech[i].name === 'size-weight illusion') { //to detect if the player is using the sword mod so that it won't mess up their mod. The sword mod adds this tech so if it is detected then the sword won't be removed from the gun array //Landgreen, don't add a tech with the same name isUsingSwordMod = true; } } if (!isUsingSwordMod) { const e = { name: "sword", descriptionFunction() { return `swing a sword that lifesteals health
drains health instead of ammunition
doesn't use ammo` }, ammo: Infinity, ammoPack: Infinity, defaultAmmoPack: Infinity, have: false, cycle: 0, sword: undefined, swordArray: [], bladeSegments: undefined, bladeTrails: [], angle: 0, constraint: undefined, charge: 0, angle2: 0, fire() { }, do() { if (this.sword && this.cycle < 1) { this.angle2 = Math.atan2(this.sword.position.y - m.pos.y, this.sword.position.x - m.pos.x); } if (this.sword) { this.cycle++; } this.normalFire(); this.renderDefault(); this.collision(); }, normalFire() { if (this.constraint) { this.constraint.pointA = player.position; } if (tech.isStabSword && !m.crouch && this.cycle > 0 && this.stabStatus) { if (this.sword) { this.stabStatus = false; if (tech.isEnergyHealth) { m.energy = 0.01; m.immuneCycle = m.cycle + 30; } this.cycle = 0; Matter.Body.setAngularVelocity(this.sword, 0); Composite.remove(engine.world, this.sword); this.sword.parts.forEach(part => { Composite.remove(engine.world, part); const index = bullet.indexOf(part); if (index !== -1) { bullet.splice(index, 1); } }); this.sword = undefined; if (this.constraint) { Composite.remove(engine.world, this.constraint); this.constraint = undefined; } this.bladeTrails = []; m.fireCDcycle = 0; } } if (input.fire && (tech.isEnergyHealth ? m.energy >= 0.11 : m.health >= 0.11)) { if (tech.isEnergyHealth) { m.energy -= 0.004; } else { m.health -= 0.001; m.displayHealth(); } } if (input.fire && (tech.isEnergyHealth ? m.energy >= 0.11 : m.health >= 0.11)) { if (!this.sword && b.guns[b.activeGun].name === 'sword') { ({ sword: this.sword, bladeSegments: this.bladeSegments } = this.createAndSwingSword()); this.angle = m.angle; } } if (this.sword && !input.fire) { this.cycle = 0; Matter.Body.setAngularVelocity(this.sword, 0); player.force.x *= 0.01; player.force.y *= 0.01; Composite.remove(engine.world, this.sword); this.sword.parts.forEach(part => { Composite.remove(engine.world, part); const index = bullet.indexOf(part); if (index !== -1) { bullet.splice(index, 1); } }); this.sword = undefined; if (this.constraint) { Composite.remove(engine.world, this.constraint); this.constraint = undefined; } this.bladeTrails = []; m.fireCDcycle = m.cycle + 10; } else { if (this.sword && (tech.isEnergyHealth ? m.energy >= 0.11 : m.health >= 0.11)) { let handle; for (let i = 0; i < bullet.length; i++) { if (bullet[i].customName == "handle") { handle = bullet[i]; } } if (tech.infinityEdge) { const newSize = Math.sqrt(0.5 * m.health) + 1; Matter.Body.scale(this.sword, newSize * (1 / (this.sword.scale == undefined ? 1 : this.sword.scale)), newSize * (1 / (this.sword.scale == undefined ? 1 : this.sword.scale)), handle.position); this.sword.scale = newSize; } if (!(this.angle > -Math.PI / 2 && this.angle < Math.PI / 2)) { Matter.Body.setAngularVelocity(this.sword, -Math.PI * 0.1); } else { Matter.Body.setAngularVelocity(this.sword, Math.PI * 0.1); } if (tech.sizeIllusion) { player.force.x += Math.cos(m.angle) * player.mass / 500; player.force.y += Math.sin(m.angle) * player.mass / 500; } if (!this.constraint && (m.angle > -Math.PI / 2 && m.angle < Math.PI / 2)) { this.constraint = Constraint.create({ pointA: player.position, bodyB: this.sword, pointB: { x: -9, y: ((handle.position.y - this.sword.position.y)) }, stiffness: (tech.infinityEdge ? 0.05 : 0.1), damping: 0.001815, length: 0, }); Composite.add(engine.world, this.constraint); } else if (!this.constraint) { this.constraint = Constraint.create({ pointA: player.position, bodyB: this.sword, pointB: { x: 9, y: ((handle.position.y - this.sword.position.y)) }, stiffness: (tech.infinityEdge ? 0.05 : 0.1), damping: 0.001815, length: 0, }); Composite.add(engine.world, this.constraint); } } else if (this.sword) { if (tech.isEnergyHealth) { m.energy = 0.01; m.immuneCycle = m.cycle + 30; } this.cycle = 0; Matter.Body.setAngularVelocity(this.sword, 0); player.force.x *= 0.01; player.force.y *= 0.01; Composite.remove(engine.world, this.sword); this.sword.parts.forEach(part => { Composite.remove(engine.world, part); const index = bullet.indexOf(part); if (index !== -1) { bullet.splice(index, 1); } }); this.sword = undefined; if (this.constraint) { Composite.remove(engine.world, this.constraint); this.constraint = undefined; } this.bladeTrails = []; m.fireCDcycle = 0; } } }, createAndSwingSword(x = player.position.x, y = player.position.y, angle = m.angle) { const handleWidth = 20; const handleHeight = 150; const handle = Bodies.rectangle(x, y, handleWidth, handleHeight, spawn.propsIsNotHoldable); bullet[bullet.length] = handle; handle.customName = "handle"; bullet[bullet.length - 1].do = () => { }; const pommelWidth = 30; const pommelHeight = 40; const pommelVertices = [ { x: x, y: y + handleHeight / 2 + pommelHeight / 2 }, { x: x + pommelWidth / 2, y: y + handleHeight / 2 }, { x: x, y: y + handleHeight / 2 - pommelHeight / 2 }, { x: x - pommelWidth / 2, y: y + handleHeight / 2 }, ]; const pommel = Bodies.fromVertices(x, y + handleHeight / 2, pommelVertices, spawn.propsIsNotHoldable); bullet[bullet.length] = pommel; bullet[bullet.length - 1].do = () => { }; if (tech.soundSword) { bullet[bullet.length - 1].draw = () => { }; } // Blade setup const bladeWidth = 100 * (tech.soundSword ? 3 : 1); const bladeHeight = 20 * (tech.soundSword ? 3 : 1); const numBlades = 15; const extensionFactor = 5; const bladeSegments = []; if ((angle > -Math.PI / 2 && angle < Math.PI / 2)) { for (let i = 0; i < numBlades; i++) { const extensionFactorFraction = (i / (numBlades - 1)) * extensionFactor; const bladeX = x + i * (bladeWidth / 20); const bladeY = y - handleHeight / 2 - i * (bladeHeight / 4.5) * extensionFactor; const vertices = [ { x: bladeX, y: bladeY - bladeHeight / 2 }, { x: bladeX + bladeWidth / 2, y: bladeY + bladeHeight / 2 }, { x: bladeX - bladeWidth / 2, y: bladeY + bladeHeight / 2 }, { x: bladeX, y: bladeY - bladeHeight / 2 + 10 }, ]; const blade = Bodies.fromVertices(bladeX, bladeY, vertices, spawn.propsIsNotHoldable); bullet[bullet.length] = blade; bullet[bullet.length - 1].do = () => { }; if (tech.soundSword) { bullet[bullet.length - 1].draw = () => { }; } Matter.Body.rotate(blade, -Math.sin(i * (Math.PI / 270) * 15)); bladeSegments.push(blade); } } else { for (let i = 0; i < numBlades; i++) { const extensionFactorFraction = (i / (numBlades - 1)) * extensionFactor; const mirroredBladeX = x - i * (bladeWidth / 20); const mirroredBladeY = y - handleHeight / 2 - i * (bladeHeight / 4.5) * extensionFactor; const mirroredVertices = [ { x: mirroredBladeX, y: mirroredBladeY - bladeHeight / 2 }, { x: mirroredBladeX + bladeWidth / 2, y: mirroredBladeY + bladeHeight / 2 }, { x: mirroredBladeX - bladeWidth / 2, y: mirroredBladeY + bladeHeight / 2 }, { x: mirroredBladeX, y: mirroredBladeY - bladeHeight / 2 + 10 }, ]; const mirroredBlade = Bodies.fromVertices(mirroredBladeX, mirroredBladeY, mirroredVertices, spawn.propsIsNotHoldable); bullet[bullet.length] = mirroredBlade; bullet[bullet.length - 1].do = () => { }; if (tech.soundSword) { bullet[bullet.length - 1].draw = () => { }; } Matter.Body.rotate(mirroredBlade, Math.sin(i * (Math.PI / 270) * 15)); bladeSegments.push(mirroredBlade); } } bladeSegments.push(pommel); const sword = Body.create({ parts: [handle, ...bladeSegments], }); Composite.add(engine.world, sword); Matter.Body.setPosition(sword, { x, y }); sword.collisionFilter.category = cat.bullet; sword.collisionFilter.mask = cat.mobBullet | cat.powerup | cat.mob; Body.scale(sword, -1, 1, { x, y }); return { sword, bladeSegments }; }, renderDefault() { if (this.sword) { for (let i = 0; i < this.bladeSegments.length; i++) { const blade = this.bladeSegments[i]; const trail = this.bladeTrails[i] || []; const vertices = blade.vertices.map(vertex => ({ x: vertex.x, y: vertex.y })); trail.push(vertices); if (trail.length > 10) { trail.shift(); } this.bladeTrails[i] = trail; } for (let i = 0; i < this.bladeTrails.length; i++) { const trail = this.bladeTrails[i]; const alphaStep = 1 / trail.length; let alpha = 0; for (let j = 0; j < trail.length; j++) { const vertices = trail[j]; ctx.beginPath(); ctx.moveTo(vertices[0].x, vertices[0].y); for (let k = 1; k < vertices.length; k++) { ctx.lineTo(vertices[k].x, vertices[k].y); }; alpha += alphaStep; ctx.closePath(); if (tech.isEnergyHealth) { const eyeColor = m.fieldMeterColor; const r = eyeColor[1]; const g = eyeColor[2]; const b = eyeColor[3]; const color = `#${r}${r}${g}${g}${b}${b}${Math.round(alpha * 255).toString(16).padStart(2, '0')}`; ctx.fillStyle = color; } else { ctx.fillStyle = `rgba(220, 20, 60, ${alpha})`; } ctx.fill(); } } for (let i = 0; i < this.bladeSegments.length; i++) { ctx.beginPath(); ctx.lineJoin = "miter"; ctx.miterLimit = 100; ctx.strokeStyle = tech.isEnergyHealth ? m.fieldMeterColor : tech.isAmmoSword ? "#c0c0c0" : "crimson"; ctx.lineWidth = 5; ctx.fillStyle = "black"; ctx.moveTo(this.bladeSegments[i].vertices[0].x, this.bladeSegments[i].vertices[0].y); for (let j = 0; j < this.bladeSegments[i].vertices.length; j++) { ctx.lineTo(this.bladeSegments[i].vertices[j].x, this.bladeSegments[i].vertices[j].y) }; ctx.closePath(); ctx.stroke(); ctx.fill(); ctx.lineJoin = "round"; ctx.miterLimit = 10; } } }, collision() { if (this.sword) { for (let i = 0; i < mob.length; i++) { if (Matter.Query.collides(this.sword, [mob[i]]).length > 0) { const dmg = m.dmgScale * Math.sqrt(this.sword.speed) * (tech.sizeIllusion ? 1.1 : 1) * (tech.isStabSword ? 1.5 : 1) * (tech.infinityEdge ? 1.1 : 1); if (!tech.soundSword) { if (m.health < m.maxHealth) { if (tech.isEnergyHealth) { m.energy += 0.04; } else { m.health += 0.01 * (dmg - mob[i].health); m.displayHealth(); } } else { if (tech.isEnergyHealth) { m.energy += 0.04; } else { m.health = m.maxHealth; m.displayHealth(); } } } mob[i].damage(dmg, true); simulation.drawList.push({ x: mob[i].position.x, y: mob[i].position.y, radius: Math.abs(Math.log(dmg * this.sword.speed) * 40 * mob[i].damageReduction + 3), color: (tech.soundSword ? "rgba(0, 0, 0, 0.3)" : simulation.mobDmgColor), time: simulation.drawTime }); break } } if (Matter.Query.collides(this.sword, [genisis]).length > 0) { m.damage(-0.0142) //balanced! } } } }; b.guns.push(e); const gunArray = b.guns.filter( (obj, index, self) => index === self.findIndex((item) => item.name === obj.name) ); b.guns = gunArray; } else { simulation.inGameConsole(`Thank you for using my sword mod
I'll save you the trouble of killing genisis
g.damage(Infinity)
`); g.damage(Infinity); } simulation.inGameConsole(`arena by Richard0820`); let index = 0; let index2 = 0; let { sword: sword, bladeSegments: bladeSegments } = createSword(); const door = level.door(-950, -3000, 400, 4000, 2000, 10); const door2 = level.door(550, -3000, 400, 4000, 2000, 10); level.setPosToSpawn(-7900, -2550); //normal spawn level.exit.x = 7875; level.exit.y = -2530; 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 = 2000 simulation.zoomTransition(level.defaultZoom) document.body.style.backgroundColor = "#987654"; color.map = "#765432" //custom map color color.block = "#876543"; door.isClosing = true; door2.isClosing = true; for (let i = 0; i < 5; i++) { powerUps.spawn(-6075, -2000, "heal"); } let bladeTrails = []; let isOwned = false; class Particle { constructor() { this.x = player.position.x + Math.random() * 10000 - 5000; this.y = player.position.y + Math.random() * 10000 - 5000; this.vx = 0; this.vy = 0; this.accelX = 0; this.accelY = 0; this.life = 2000; this.alpha = 1; this.size = 8; } update() { this.vx += this.accelX; this.vy += this.accelY; this.x += this.vx; this.y += this.vy; if (this.x < player.position.x - 5000 || this.x > player.position.x + 5000 || this.y < player.position.y - 5000 || this.y > player.position.y + 5000) { this.reset(); } } reset() { this.x = player.position.x + Math.random() * 10000 - 5000; this.y = player.position.y + Math.random() * 10000 - 5000; this.vx = 0; this.vy = 0; this.life = Math.random() * 1000 + 1000; this.maxLife = this.life; } draw() { ctx.beginPath(); ctx.fillStyle = `rgba(255, 255, 255, ${this.alpha})`; ctx.arc(this.x, this.y, this.size, 0, Math.PI * 2); ctx.fill(); ctx.closePath(); } isAlive() { return this.life >= 0; } } class ParticleSystem { constructor() { this.particles = []; this.updateHandler = undefined; } addParticle(particle) { this.particles.push(particle); } update(deltaTime = 0) { this.particles.forEach(particle => { particle.update(deltaTime); this.updateHandler && this.updateHandler(particle); }); } onUpdate(fn) { this.updateHandler = fn; } } let system = new ParticleSystem(); for (let i = 0; i < 200; i++) { let particle = new Particle(); system.addParticle(particle); } system.onUpdate((particle) => { if (!particle.isAlive()) { particle.reset(); } particle.life -= 10; particle.accelX = (Math.random() - 0.5) * 0.02; particle.accelY = (Math.random() - 0.5) * 0.02; if (particle.life >= particle.maxLife / 2) { particle.alpha = 1 - (particle.life / particle.maxLife); } else { particle.alpha = particle.life / particle.maxLife; } particle.update(); }); function update() { system.update(); } function draw() { system.particles.forEach(particle => particle.draw()); } for (let i = 0; i < system.particles.length; i++) { system.particles[i].life = 0; } level.custom = () => { update(); draw(); for (let i = 0, len = b.guns.length; i < len; i++) { if (b.guns[i].name === "sword" && b.guns[i].have) { isOwned = true; } } Matter.Body.setPosition(sword, { x: -3950, y: -275 - (Math.sin(simulation.cycle / 100) * 50) }); Matter.Body.setAngularVelocity(sword, 0); door.openClose(); door2.openClose(); if (Matter.Collision.collides(sword, player) && index <= 0 || isOwned) { bladeTrails = []; bladeSegments = []; Composite.remove(engine.world, sword); sword.parts.forEach(part => { Composite.remove(engine.world, part); const index = bullet.indexOf(part); if (index !== -1) { bullet.splice(index, 1); } }); b.giveGuns("sword"); door.isClosing = false; door2.isClosing = false; index++; } const oldLevel = level.onLevel; level.exit.drawAndCheck(); level.enter.draw(); if (tech.isEnergyHealth) { ctx.beginPath(); const gradient = ctx.createRadialGradient(-3950, 0, 5, -3950, 0, 350 + Math.sin(simulation.cycle * 0.15) * 0.1); gradient.addColorStop(0, m.fieldMeterColor); gradient.addColorStop(0.9, "transparent"); // gradient.addColorStop(1, "darkgray"); ctx.fillStyle = gradient; ctx.strokeStyle = "transparent"; ctx.fillRect(-4000, -350, 100, 350); ctx.fill(); ctx.stroke(); } else { ctx.beginPath(); const gradient = ctx.createLinearGradient(-3500, 0, -3500, -350 + Math.sin(simulation.cycle * 0.15) * 0.1); gradient.addColorStop(0, "crimson"); gradient.addColorStop(0.9, "transparent"); // gradient.addColorStop(1, "darkgray"); ctx.fillStyle = gradient; ctx.strokeStyle = "transparent"; ctx.fillRect(-4000, -350, 100, 350); ctx.fill(); ctx.stroke(); } if (player.position.x > -4000 && player.position.x < -3900 && player.position.y > -350 && player.position.y < 0) { player.force.y -= 0.03; } // 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 - 0 && player.velocity.y < .15 && index2 == 0 && !isUsingSwordMod) { if (level.onLevel !== oldLevel && simulation.clearNow && !isUsingSwordMod) { b.removeGun("sword"); //completely removing the stuff (if you leave properly through the door) for (let i = 0, len = b.guns.length; i < len; i++) { if (b.guns[i].name === "sword") { b.guns.splice(i, 1); break; } } index2++; setTimeout(() => { simulation.inGameConsole(`If you want to keep this sword, visit https://github.com/Whyisthisnotavalable/n-scythe. The sword is there.`) }, 1000) } for (let i = 0; i < bladeSegments.length; i++) { const blade = bladeSegments[i]; const trail = bladeTrails[i] || []; const vertices = blade.vertices.map(vertex => ({ x: vertex.x, y: vertex.y })); trail.push(vertices); if (trail.length > 10) { trail.shift(); } bladeTrails[i] = trail; } for (let i = 0; i < bladeTrails.length; i++) { const trail = bladeTrails[i]; const alphaStep = 1 / trail.length; let alpha = 0; for (let j = 0; j < trail.length; j++) { const vertices = trail[j]; ctx.beginPath(); ctx.moveTo(vertices[0].x, vertices[0].y); for (let k = 1; k < vertices.length; k++) { ctx.lineTo(vertices[k].x, vertices[k].y); }; alpha += alphaStep; ctx.closePath(); if (tech.isEnergyHealth) { const eyeColor = m.fieldMeterColor; const r = eyeColor[1]; const g = eyeColor[2]; const b = eyeColor[3]; const color = `#${r}${r}${g}${g}${b}${b}${Math.round(alpha * 255).toString(16).padStart(2, '0')}`; ctx.fillStyle = color; } else { ctx.fillStyle = `rgba(220, 20, 60, ${alpha})`; } ctx.fill(); } } for (let i = 0; i < bladeSegments.length; i++) { ctx.beginPath(); ctx.lineJoin = "miter"; ctx.miterLimit = 100; ctx.strokeStyle = tech.isEnergyHealth ? m.fieldMeterColor : tech.isAmmoSword ? "#c0c0c0" : "crimson"; ctx.lineWidth = 5; ctx.fillStyle = "black"; ctx.moveTo(bladeSegments[i].vertices[0].x, bladeSegments[i].vertices[0].y); for (let j = 0; j < bladeSegments[i].vertices.length; j++) { ctx.lineTo(bladeSegments[i].vertices[j].x, bladeSegments[i].vertices[j].y) }; ctx.closePath(); ctx.stroke(); ctx.fill(); ctx.lineJoin = "round"; ctx.miterLimit = 10; } if (bladeSegments.length) { ctx.beginPath(); ctx.lineJoin = "miter"; ctx.miterLimit = 100; ctx.strokeStyle = tech.isEnergyHealth ? m.fieldMeterColor : tech.isAmmoSword ? "#c0c0c0" : "crimson"; ctx.lineWidth = 5; ctx.fillStyle = "black"; ctx.moveTo(sword.parts[1].vertices[0].x, sword.parts[1].vertices[0].y); for (let j = 0; j < sword.parts[1].vertices.length; j++) { ctx.lineTo(sword.parts[1].vertices[j].x, sword.parts[1].vertices[j].y) }; ctx.closePath(); ctx.fill(); ctx.lineJoin = "round"; ctx.miterLimit = 10; } }; level.customTopLayer = () => { }; simulation.ephemera.push({ name: "genesis", death: false, pwuspawn: 0, do() { if (this.death === true) { b.explosion(g.pos, 200 * Math.random(), "#000000") setTimeout(() => { if (this.pwuspawn === 0) { powerUps.spawnBossPowerUp(g.pos.x, g.pos.y) this.pwuspawn++; } simulation.removeEphemera(this.name); simulation.removeEphemera("genisisScythe"); }, 1000); } if (g.health >= 0) { if (g.health < g.maxHealth) { g.health++; } const dist = Matter.Vector.magnitudeSquared(Matter.Vector.sub(genisis.position, player.position)); const time = Math.sqrt(dist) / 60; g.alive = true; g.targetX = m.pos.x + player.velocity.x * time; g.targetY = m.pos.y + player.velocity.y * time; } else { this.death = true; g.alive = false; } if (g.alive) { g.cycle++; g.move() g.lookDefault(); g.drawDefault(); g.drawHealth(); genisis.force.y += g.mass * simulation.g g.setFillColors(); control.right = g.angle > -Math.PI * 2 / 5 && g.angle < Math.PI * 2 / 5; control.left = g.angle > Math.PI * 3 / 5 || g.angle < -Math.PI * 3 / 5; control.down = g.angle > Math.PI * 3 / 10 && g.angle < Math.PI * 7 / 10; if (Matter.Query.collides(genisis, body).length || Matter.Query.collides(genisisHead, map).length || Matter.Query.collides(genisisBodySensor, map).length && !control.down) { if (g.buttonCD_jump < g.cycle) { g.jump(); } } control.up = g.angle > -Math.PI * 6 / 10 && g.angle < -Math.PI * 4 / 10; if (g.onGround) { g.groundControl() } else { g.airControl() } if (g.pos.y > simulation.fallHeight) { Matter.Body.setPosition(genisis, { x: level.exit.x, y: level.exit.y }) } } else { genisis.collisionFilter.mask = cat.map | cat.body; Matter.Body.setPosition(genisis, { x: 0, y: 0 }) } if (simulation.testing) { ctx.beginPath(); let bodyDraw = genisisJumpSensor.vertices; ctx.moveTo(bodyDraw[0].x, bodyDraw[0].y); for (let j = 1; j < bodyDraw.length; ++j) { ctx.lineTo(bodyDraw[j].x, bodyDraw[j].y); } ctx.lineTo(bodyDraw[0].x, bodyDraw[0].y); ctx.fillStyle = "rgba(255, 0, 0, 0.5)"; ctx.fill(); ctx.beginPath(); bodyDraw = genisisBody.vertices; ctx.moveTo(bodyDraw[0].x, bodyDraw[0].y); for (let j = 1; j < bodyDraw.length; ++j) { ctx.lineTo(bodyDraw[j].x, bodyDraw[j].y); } ctx.lineTo(bodyDraw[0].x, bodyDraw[0].y); ctx.fillStyle = "rgba(0, 255, 255, 0.25)"; ctx.fill(); ctx.beginPath(); bodyDraw = genisisHead.vertices; ctx.moveTo(bodyDraw[0].x, bodyDraw[0].y); for (let j = 1; j < bodyDraw.length; ++j) { ctx.lineTo(bodyDraw[j].x, bodyDraw[j].y); } ctx.lineTo(bodyDraw[0].x, bodyDraw[0].y); ctx.fillStyle = "rgba(255, 255, 0, 0.4)"; ctx.fill(); ctx.beginPath(); bodyDraw = genisisHeadSensor.vertices; ctx.moveTo(bodyDraw[0].x, bodyDraw[0].y); for (let j = 1; j < bodyDraw.length; ++j) { ctx.lineTo(bodyDraw[j].x, bodyDraw[j].y); } ctx.lineTo(bodyDraw[0].x, bodyDraw[0].y); ctx.fillStyle = "rgba(0, 0, 255, 0.25)"; ctx.fill(); ctx.beginPath(); bodyDraw = genisisBodySensor.vertices; ctx.moveTo(bodyDraw[0].x, bodyDraw[0].y); for (let j = 1; j < bodyDraw.length; ++j) { ctx.lineTo(bodyDraw[j].x, bodyDraw[j].y); } ctx.lineTo(bodyDraw[0].x, bodyDraw[0].y); ctx.fillStyle = "rgba(255, 0, 255, 0.25)"; ctx.fill(); } Events.on(engine, "collisionStart", function (event) { g.genisisOnGroundCheck(event); GenisisCollisionChecks(event); }); Events.on(engine, "collisionActive", function (event) { g.genisisOnGroundCheck(event); }); Events.on(engine, "collisionEnd", function (event) { g.genisisOffGroundCheck(event); }); } }) let evo = { isLongBlade: true, isScytheRange: true, scytheRange: 3, isScytheRad: false, scytheRad: 0, isDoubleScythe: false, isPhaseScythe: false, isMeleeScythe: false, isStunScythe: false, }; simulation.ephemera.push({ name: "genisisScythe", cycle: 0, scythe: undefined, bladeSegments: undefined, bladeTrails: [], angle: 0, constraint: undefined, fireCD: 0, do() { if (isOwned) { if (g.health < 500 && g.health > 200) { evo = { isLongBlade: true, isScytheRange: true, scytheRange: 3, isScytheRad: false, scytheRad: 1, isDoubleScythe: true, isPhaseScythe: false, isMeleeScythe: false, isStunScythe: false, }; } else if (g.health < 200 && g.health > 50) { evo = { isLongBlade: true, isScytheRange: true, scytheRange: 3, isScytheRad: true, scytheRad: 1, isDoubleScythe: true, isPhaseScythe: true, isMeleeScythe: true, isStunScythe: true, }; } else if (g.health < 50) { evo = { isLongBlade: true, isScytheRange: true, scytheRange: 9, isScytheRad: true, scytheRad: 6, isDoubleScythe: true, isPhaseScythe: true, isMeleeScythe: true, isStunScythe: true, }; } if (g.cycle > this.fireCD && !this.scythe) { this.fireCD = g.cycle + 30; if (!this.scythe) { ({ scythe: this.scythe, bladeSegments: this.bladeSegments } = this.createAndSwingScythe()); this.angle = g.angle; } } if (this.scythe && g.cycle > this.cycle + 30 || !g.alive && this.scythe) { Composite.remove(engine.world, this.scythe); this.scythe.parts.forEach(part => { Composite.remove(engine.world, part); const index = bullet.indexOf(part); if (index !== -1) { bullet.splice(index, 1); } }); this.scythe = undefined; this.bladeTrails = []; } else { if (this.scythe && !evo.isMeleeScythe) { if (!(this.angle > -Math.PI / 2 && this.angle < Math.PI / 2)) { Matter.Body.setAngularVelocity(this.scythe, -Math.PI * 0.15 - (evo.scytheRad ? evo.scytheRad * 0.1 : 0)); } else { Matter.Body.setAngularVelocity(this.scythe, Math.PI * 0.15 + (evo.scytheRad ? evo.scytheRad * 0.1 : 0)); } Matter.Body.setVelocity(this.scythe, { x: Math.cos(this.angle) * 30, y: Math.sin(this.angle) * 30 }); } else if (this.scythe && evo.isMeleeScythe) { if (!(this.angle > -Math.PI / 2 && this.angle < Math.PI / 2)) { Matter.Body.setAngularVelocity(this.scythe, -Math.PI * 0.1 + (evo.isStunScythe ? 0.1 : 0)); } else { Matter.Body.setAngularVelocity(this.scythe, Math.PI * 0.1 - (evo.isStunScythe ? 0.1 : 0)); } Matter.Body.setPosition(this.scythe, genisis.position); } } if (this.scythe) { for (let i = 0; i < this.bladeSegments.length; i++) { const blade = this.bladeSegments[i]; const trail = this.bladeTrails[i] || []; const vertices = blade.vertices.map(vertex => ({ x: vertex.x, y: vertex.y })); trail.push(vertices); if (trail.length > 10) { trail.shift(); } this.bladeTrails[i] = trail; } for (let i = 0; i < this.bladeTrails.length; i++) { const trail = this.bladeTrails[i]; const alphaStep = 1 / trail.length; let alpha = 0; for (let j = 0; j < trail.length; j++) { const vertices = trail[j]; ctx.beginPath(); ctx.moveTo(vertices[0].x, vertices[0].y); for (let k = 1; k < vertices.length; k++) { ctx.lineTo(vertices[k].x, vertices[k].y); }; alpha += alphaStep; ctx.closePath(); ctx.fillStyle = `rgba(100, 20, 255, ${alpha})`; ctx.fill(); } } for (let i = 0; i < this.bladeSegments.length; i++) { ctx.beginPath(); ctx.lineJoin = "miter"; ctx.miterLimit = 100; ctx.strokeStyle = `rgb(100, 20, 255)`; ctx.lineWidth = 5; ctx.fillStyle = "black"; ctx.moveTo(this.bladeSegments[i].vertices[0].x, this.bladeSegments[i].vertices[0].y); for (let j = 0; j < this.bladeSegments[i].vertices.length; j++) { ctx.lineTo(this.bladeSegments[i].vertices[j].x, this.bladeSegments[i].vertices[j].y) }; ctx.closePath(); ctx.stroke(); ctx.fill(); ctx.lineJoin = "round"; ctx.miterLimit = 10; } } if (this.scythe) { for (let i = 0; i < mob.length; i++) { if (Matter.Query.collides(this.scythe, [mob[i]]).length > 0) { const dmg = m.dmgScale * 0.12 * 2.73 * (evo.isLongBlade ? 1.3 : 1) * (evo.scytheRange ? evo.scytheRange * 1.15 : 1) * (evo.isDoubleScythe ? 0.9 : 1) * (evo.scytheRad ? evo.scytheRad * 1.5 : 1); mob[i].damage(dmg, true); simulation.drawList.push({ x: mob[i].position.x, y: mob[i].position.y, radius: Math.sqrt(dmg) * 50, color: simulation.mobDmgColor, time: simulation.drawTime }); if (!evo.isMeleeScythe) { const angle = Math.atan2(mob[i].position.y - this.scythe.position.y, mob[i].position.x - this.scythe.position.x); this.scythe.force.x += Math.cos(angle) * 2; this.scythe.force.y += Math.sin(angle) * 2; } if (evo.isStunScythe) { mobs.statusStun(mob[i], 90); } break } } if (Matter.Query.collides(this.scythe, [player]).length > 0 && m.immuneCycle < m.cycle) { const dmg = 0.02 * (evo.isLongBlade ? 1.3 : 1) * (evo.scytheRange ? evo.scytheRange * 1.15 : 1) * (evo.isDoubleScythe ? 0.9 : 1) * (evo.scytheRad ? evo.scytheRad * 1.5 : 1); // actual scythe scallings one tap the player so this is nerfed for genisis m.damage(dmg); m.immuneCycle = m.cycle + 10; simulation.drawList.push({ x: player.position.x, y: player.position.y, radius: Math.sqrt(dmg) * 50, color: simulation.mobDmgColor, time: simulation.drawTime }); if (!evo.isMeleeScythe) { const angle = Math.atan2(player.position.y - this.scythe.position.y, player.position.x - this.scythe.position.x); this.scythe.force.x += Math.cos(angle) * 2; this.scythe.force.y += Math.sin(angle) * 2; } } } } }, createAndSwingScythe(x = genisis.position.x, y = genisis.position.y, angle = g.angle) { this.cycle = g.cycle + 60 + (evo.scytheRange * 6); const handleWidth = 20; const handleHeight = 200 + (evo.isLongBlade ? 30 : 0) + (evo.isMeleeScythe ? 200 : 0); const handle = Bodies.rectangle(x, y, handleWidth, handleHeight, spawn.propsIsNotHoldable); bullet[bullet.length] = handle; bullet[bullet.length - 1].do = () => { }; const bladeWidth = 100; const bladeHeight = 20; const numBlades = 10 + (evo.isLongBlade ? 1 : 0) + (evo.isMeleeScythe ? 3 : 0); const extensionFactor = 5.5; const bladeSegments = []; if (!evo.isDoubleScythe) { for (let i = 0; i < numBlades; i++) { const extensionFactorFraction = (i / (numBlades - 1)) * extensionFactor; const bladeX = x - handleWidth / 2 + i * (bladeWidth / 2) - extensionFactorFraction * (bladeWidth / 2); const bladeY = y + handleHeight / 2 - i * (bladeHeight / (3 ** i)); const vertices = [ { x: bladeX, y: bladeY - bladeHeight / 2 }, { x: bladeX + bladeWidth / 2, y: bladeY + bladeHeight / 2 }, { x: bladeX - bladeWidth / 2, y: bladeY + bladeHeight / 2 }, { x: bladeX, y: bladeY - bladeHeight / 2 + 10 }, ]; const blade = Bodies.fromVertices(bladeX, bladeY, vertices, spawn.propsIsNotHoldable); bullet[bullet.length] = blade; bullet[bullet.length - 1].do = () => { }; Matter.Body.rotate(blade, -Math.sin(i * (Math.PI / 180) * 5)); bladeSegments.push(blade); } } else { for (let i = 0; i < numBlades; i++) { const extensionFactorFraction = (i / (numBlades - 1)) * extensionFactor; const bladeX = x - handleWidth / 2 + i * (bladeWidth / 2) - extensionFactorFraction * (bladeWidth / 2); const bladeY = y + handleHeight / 2 - i * (bladeHeight / (3 ** i)); const vertices = [ { x: bladeX, y: bladeY - bladeHeight / 2 }, { x: bladeX + bladeWidth / 2, y: bladeY + bladeHeight / 2 }, { x: bladeX - bladeWidth / 2, y: bladeY + bladeHeight / 2 }, { x: bladeX, y: bladeY - bladeHeight / 2 + 10 }, ]; const blade = Bodies.fromVertices(bladeX, bladeY, vertices, spawn.propsIsNotHoldable); bullet[bullet.length] = blade; bullet[bullet.length - 1].do = () => { }; Matter.Body.rotate(blade, -Math.sin(i * (Math.PI / 180) * 5)); bladeSegments.push(blade); } for (let i = 0; i < numBlades; i++) { const extensionFactorFraction = (i / (numBlades - 1)) * extensionFactor; const bladeX = x + handleWidth / 2 - i * (bladeWidth / 2) + extensionFactorFraction * (bladeWidth / 2); const bladeY = y - handleHeight / 2 - i * (bladeHeight / (3 ** i)); const vertices = [ { x: bladeX, y: bladeY - bladeHeight / 2 }, { x: bladeX + bladeWidth / 2, y: bladeY + bladeHeight / 2 }, { x: bladeX - bladeWidth / 2, y: bladeY + bladeHeight / 2 }, { x: bladeX, y: bladeY - bladeHeight / 2 + 10 }, ]; const blade = Bodies.fromVertices(bladeX, bladeY, vertices, spawn.propsIsNotHoldable); bullet[bullet.length] = blade; bullet[bullet.length - 1].do = () => { }; Matter.Body.rotate(blade, -Math.sin(i * (Math.PI / 180) * 5) + Math.PI); bladeSegments.push(blade); } } const scythe = Body.create({ parts: [handle, ...bladeSegments], }); Composite.add(engine.world, scythe); Matter.Body.setPosition(scythe, { x, y }); scythe.collisionFilter.category = cat.body; scythe.collisionFilter.mask = cat.mobBullet | cat.player; if (!evo.isMeleeScythe) { setTimeout(() => { scythe.collisionFilter.mask = cat.mobBullet | cat.mob | cat.player; }, 1000) } if ((angle > -Math.PI / 2 && angle < Math.PI / 2)) { Body.scale(scythe, -1, 1, { x, y }); } scythe.frictionAir -= 0.01; return { scythe, bladeSegments }; }, }) spawn.mapRect(-10000, 0, 20000, 2000); spawn.mapRect(-10000, -10000, 2000, 10000); spawn.mapRect(8000, -10000, 2000, 10000); spawn.mapRect(-10000, -10000, 20000, 2000); spawn.spawnStairs(8000, 0, 15, 2500, 2500, true); spawn.spawnStairs(-8000, 0, 15, 2500, 2500, false); spawn.mapRect(-4000, -10, 100, 20); spawn.mapRect(4000, -10, 100, 20); spawn.mapRect(-1000, -10000, 2000, 8000); // spawn.mapRect(-500, -10000, 1000, 9700); function createSword(x = 0, y = 0) { //sword asthetic const handleWidth = 20; const handleHeight = 150; const handle = Bodies.rectangle(x, y, handleWidth, handleHeight, spawn.propsIsNotHoldable); bullet[bullet.length] = handle; handle.customName = "handle"; bullet[bullet.length - 1].do = () => { }; const pommelWidth = 30; const pommelHeight = 40; const pommelVertices = [ { x: x, y: y + handleHeight / 2 + pommelHeight / 2 }, { x: x + pommelWidth / 2, y: y + handleHeight / 2 }, { x: x, y: y + handleHeight / 2 - pommelHeight / 2 }, { x: x - pommelWidth / 2, y: y + handleHeight / 2 }, ]; const pommel = Bodies.fromVertices(x, y + handleHeight / 2, pommelVertices, spawn.propsIsNotHoldable); bullet[bullet.length] = pommel; bullet[bullet.length - 1].do = () => { }; if (tech.soundSword) { bullet[bullet.length - 1].draw = () => { }; } // Blade setup const bladeWidth = 100 * (tech.soundSword ? 3 : 1); const bladeHeight = 20 * (tech.soundSword ? 3 : 1); const numBlades = 15; const extensionFactor = 5; const bladeSegments = []; for (let i = 0; i < numBlades; i++) { const extensionFactorFraction = (i / (numBlades - 1)) * extensionFactor; const mirroredBladeX = x - i * (bladeWidth / 20); const mirroredBladeY = y - handleHeight / 2 - i * (bladeHeight / 4.5) * extensionFactor; const mirroredVertices = [ { x: mirroredBladeX, y: mirroredBladeY - bladeHeight / 2 }, { x: mirroredBladeX + bladeWidth / 2, y: mirroredBladeY + bladeHeight / 2 }, { x: mirroredBladeX - bladeWidth / 2, y: mirroredBladeY + bladeHeight / 2 }, { x: mirroredBladeX, y: mirroredBladeY - bladeHeight / 2 + 10 }, ]; const mirroredBlade = Bodies.fromVertices(mirroredBladeX, mirroredBladeY, mirroredVertices, spawn.propsIsNotHoldable); bullet[bullet.length] = mirroredBlade; bullet[bullet.length - 1].do = () => { }; if (tech.soundSword) { bullet[bullet.length - 1].draw = () => { }; } Matter.Body.rotate(mirroredBlade, Math.sin(i * (Math.PI / 270) * 15)); bladeSegments.push(mirroredBlade); } bladeSegments.push(pommel); const sword = Body.create({ parts: [handle, ...bladeSegments], }); Composite.add(engine.world, sword); Matter.Body.setPosition(sword, { x, y }); sword.collisionFilter.category = cat.bullet; sword.collisionFilter.mask = cat.mobBullet | cat.powerup | cat.mob; Body.scale(sword, -1, 1, { x, y }); Body.rotate(sword, Math.PI + Math.PI / 15) return { sword, bladeSegments }; } }, soft() { simulation.inGameConsole(``); simulation.inGameConsole(`soft by Richard0820`); simulation.inGameConsole("The lasers deal less damage the higher level you are") const portals = []; portals.push(level.portal({ x: -1525, y: -250 }, Math.PI / 2, { x: 1100, y: -1025 }, Math.PI / 2)) const soft = { createCloth(x, y, radius, width, height, attachToPlayer = false, stayStill = false, options, touchPlayer = true, constrictionStrength = 0.001) { const bodies = []; const constraints = []; const otherCons = []; const bodyWidth = radius; const bodyHeight = radius; const numRows = Math.ceil(height / bodyHeight); const numCols = Math.ceil(width / bodyWidth); for (let i = 0; i < numRows; i++) { for (let j = 0; j < numCols; j++) { const posX = x + j * bodyWidth + bodyWidth / 2; const posY = y + i * bodyHeight + bodyHeight / 2; const rect = Matter.Bodies.circle(posX, posY, (bodyWidth + bodyHeight) / 4, options); rect.collisionFilter.category = cat.body; rect.collisionFilter.mask = (touchPlayer ? cat.player | cat.body | cat.bullet | cat.mob | cat.mobBullet : cat.body | cat.bullet | cat.mob | cat.mobBullet); rect.classType = "body"; Composite.add(engine.world, rect); bodies.push(rect); } } for (let i = 0; i < numRows; i++) { for (let j = 0; j < numCols; j++) { const bodyIndexA = i * numCols + j; if (j < numCols - 1) { const bodyIndexB = i * numCols + (j + 1); const constraint = Constraint.create({ bodyA: bodies[bodyIndexA], bodyB: bodies[bodyIndexB], stiffness: 0.06, damping: 0.001 }); Composite.add(engine.world, constraint); constraints.push(constraint); } if (i < numRows - 1) { const bodyIndexB = (i + 1) * numCols + j; const constraint = Constraint.create({ bodyA: bodies[bodyIndexA], bodyB: bodies[bodyIndexB], stiffness: 0.06, damping: 0.001 }); Composite.add(engine.world, constraint); constraints.push(constraint); } } } for (let i = 0; i < numRows - 1; i++) { for (let j = 0; j < numCols - 1; j++) { const bodyA = bodies[i * numCols + j]; const bodyB = bodies[(i + 1) * numCols + j + 1]; const constraint = Constraint.create({ bodyA: bodyA, bodyB: bodyB, stiffness: 0.02 }); constraints.push(constraint); } } for (let i = 0; i < numRows - 1; i++) { for (let j = 1; j < numCols; j++) { const bodyA = bodies[i * numCols + j]; const bodyB = bodies[(i + 1) * numCols + j - 1]; const constraint = Constraint.create({ bodyA: bodyA, bodyB: bodyB, stiffness: 0.02 }); constraints.push(constraint); } } if (stayStill) { for (let i = 0; i < bodies.length; i++) { const by = bodies[i]; const spawnX = by.position.x + bodyWidth / 2; const spawnY = by.position.y + bodyHeight / 2; const isLastColumn = (i + 1) % numCols === 0; const isFirstColumn = i % numCols === 0; const stiffness = constrictionStrength * (isLastColumn || isFirstColumn ? 100 : 1); // Apply extra stiffness to first and last columns const cost = Constraint.create({ bodyA: by, pointB: { x: spawnX, y: spawnY }, stiffness: stiffness, length: 0 }); Composite.add(engine.world, cost); otherCons.push(cost); } } if (attachToPlayer) { for (let i = 0; i < bodies.length; i++) { const cost = Constraint.create({ bodyA: bodies[i], pointB: player.position, stiffness: 0.0005, length: 0 }); Composite.add(engine.world, cost); } } return { bodies, constraints, otherCons }; }, clothOptions: { frictionAir: 0.005, }, isOuterBoundary(body, bodies) { //unused const neighbors = [ { x: body.position.x + 1, y: body.position.y }, { x: body.position.x - 1, y: body.position.y }, { x: body.position.x, y: body.position.y + 1 }, { x: body.position.x, y: body.position.y - 1 } ]; for (let i = 0; i < neighbors.length; i++) { const neighbor = neighbors[i]; const isNeighbor = bodies.some(b => b.position.x === neighbor.x && b.position.y === neighbor.y); if (!isNeighbor) { return true; } } return false; }, draw(cloth) { ctx.beginPath(); ctx.lineWidth = 2; ctx.strokeStyle = "rgba(0,0,0,0.3)"; ctx.fillStyle = "black"; for (let i = 0, len = cloth.constraints.length; i < len; ++i) { const constraint = cloth.constraints[i]; ctx.moveTo(constraint.bodyA.position.x, constraint.bodyA.position.y); ctx.lineTo(constraint.bodyB.position.x, constraint.bodyB.position.y); } ctx.closePath(); ctx.fill(); ctx.stroke(); }, addGravity(bodies, magnitude) { for (var i = 0; i < bodies.length; i++) { bodies[i].force.y += bodies[i].mass * magnitude; } }, gravity(cloth) { this.addGravity(cloth.bodies, simulation.g); }, breaker(cloth, percentage = 0.5) { const totalConstraints = cloth.constraints.length; const constraintsToRemove = Math.ceil(totalConstraints * percentage); for (let i = 0; i < constraintsToRemove; i++) { const randomIndex = Math.floor(Math.random() * cloth.constraints.length); let removedConstraint = cloth.constraints.splice(randomIndex, 1)[0]; Composite.remove(engine.world, removedConstraint); } }, destroyer(cloth, percentage = 0.99999) { const otherCons = cloth.otherCons.length; const otherCons2Remove = Math.ceil(otherCons * percentage); for (let i = 0; i < otherCons2Remove; i++) { const randomIndex = Math.floor(Math.random() * cloth.otherCons.length); let removedConstraint = cloth.otherCons.splice(randomIndex, 1)[0]; Composite.remove(engine.world, removedConstraint); } }, annihilate(cloth) { const totalBodies = cloth.bodies.length; for (let i = 0; i < totalBodies; i++) { const removeBody = cloth.bodies[i]; Composite.remove(engine.world, removeBody); } cloth.bodies.length = 0; // Clear the bodies array after removal } } const clothArray = []; clothArray.push(soft.createCloth(-100, 0, 50, 1000, 300, false, true, soft.clothOptions, true)) clothArray.push(soft.createCloth(-2000, 2375, 50, 1525, 200, false, true, soft.clothOptions, true)) clothArray.push(soft.createCloth(-3950, 125, 50, 1800, 125, false, true, soft.clothOptions, true)) const annoyingStuff = { lasers(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, [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 const dmg = 0.5 / 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); }, laserBoss(x, y, radius = 30) { mobs.spawn(x, y, 6, radius, "#f00"); let me = mob[mob.length - 1]; setTimeout(() => { //fix mob in place, but allow rotation 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); }, 2000); //add in a delay in case the level gets flipped left right me.count = 0; me.frictionAir = 0.03; // me.torque -= me.inertia * 0.002 spawn.spawnOrbitals(me, radius + 50 + 200 * Math.random()) Matter.Body.setDensity(me, 0.03); //extra dense //normal is 0.001 //makes effective life much larger me.damageReduction = 0.25 me.isBoss = true; // spawn.shield(me, x, y, 1); //not working, not sure why me.onDeath = function () { powerUps.spawnBossPowerUp(this.position.x, this.position.y) }; me.rotateVelocity = -Math.min(0.0045, 0.0015 * simulation.accelScale * simulation.accelScale) * (level.levelsCleared > 8 ? 1 : -1) me.do = function () { this.fill = '#' + Math.random().toString(16).substr(-6); //flash colors this.checkStatus(); if (!this.isStunned) { //check if slowed let slowed = false for (let i = 0; i < this.status.length; i++) { if (this.status[i].type === "slow") { slowed = true break } } if (!slowed) { this.count++ Matter.Body.setAngle(this, this.count * this.rotateVelocity) Matter.Body.setAngularVelocity(this, 0) } ctx.beginPath(); for (let i = 0; i < this.vertices.length; i++) { if (Math.sin((2 * Math.PI * simulation.cycle) / (50 + i)) > 0) { this.lasers(this.vertices[i], Math.atan2(this.vertices[i].y - this.position.y, this.vertices[i].x - this.position.x)); } } 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 } }; 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, [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 const dmg = 0.5 / 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); } } } level.setPosToSpawn(-350, 0); level.exit.x = 1075; level.exit.y = 20; 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 = "#aaFFFF55"; spawn.mapRect(900, 50, 425, 250); // spawn.mapRect(900, -1050, 0.1, 1350); spawn.mapRect(-475, 2375, 1800, 250); spawn.mapRect(-4400, 2375, 2475, 250); spawn.mapRect(-4400, -450, 250, 3075); spawn.mapRect(-4400, -450, 2225, 250); spawn.mapRect(-2425, -1300, 250, 1100); spawn.mapRect(-2425, -1300, 3825, 250); spawn.mapRect(1325, -1300, 250, 3925); spawn.mapRect(-875, -1300, 250, 1375); spawn.mapRect(-725, 50, 675, 250); spawn.mapRect(-875, 175, 175, 125); for (let i = 0; i < 6; i++) { spawn.mapRect(-4175, 2000 - i * 375, 50, 125); } spawn.mapRect(-3925, 162.5, 50, 125); spawn.mapRect(-2175, 162.5, 50, 125); spawn.mapRect(300, 2025, 250, 600); spawn.mapRect(-2150, 175, 50, 25); spawn.mapRect(-2150, 250, 50, 25); spawn.mapRect(-900, 175, 50, 25); spawn.mapRect(-900, 250, 50, 25); spawn.mapRect(-1600, 175, 50, 25); spawn.mapRect(-1500, 175, 50, 25); spawn.mapRect(-1600, 250, 50, 25); spawn.mapRect(-1500, 250, 50, 25); spawn.mapRect(-1925, 175, 50, 25); spawn.mapRect(-1925, 250, 50, 25); spawn.mapRect(-1200, 175, 50, 25); spawn.mapRect(-1200, 250, 50, 25); spawn.bodyRect(-2125, 200, 1250, 50); spawn.debris(425, 200, 50); spawn.debris(-650, 2100, 50); spawn.debris(-3000, 1925, 50); spawn.debris(-3825, 1550, 50); spawn.debris(-2475, -50, 50); const bouncyBody = body[body.length - 1]; bouncyBody.restitution = 0.9; spawn.mapVertex(-2175 + 1300 / 2, -1050 + 1225 / 2, "0 -400 -100 -300 -100 0 100 0 100 -300"); spawn.mapVertex(-4150 + 1975 / 2, -200 + 2575 / 2, "0 -800 -200 -600 -200 0 0 200 200 0 200 -600 0 200"); const mapWithVertex = map[map.length - 1]; let index1 = 0; level.custom = () => { level.exit.drawAndCheck(); level.enter.draw(); if (player.position.x > 425 && index1 === 0) { soft.breaker(clothArray[0], 0.7); soft.destroyer(clothArray[0]); index1++; } if (player.position.y > 1300 && index1 === 1) { setTimeout(() => { soft.breaker(clothArray[0], 1); soft.annihilate(clothArray[0]); clothArray.splice(0, 1); }, 1000); //prevents bugs simulation.inGameConsole("Couldn't be so simple, could it?", 2000 * Math.random()); index1++; } }; level.customTopLayer = () => { for (let i = 0; i < portals.length; i++) { portals[i][2].query(); portals[i][3].query(); portals[i][0].draw(); portals[i][1].draw(); portals[i][2].draw(); portals[i][3].draw(); } ctx.beginPath(); if (Math.sin((2 * Math.PI * simulation.cycle) / (50)) > 0) { annoyingStuff.lasers(mapWithVertex.vertices[0], Math.atan2(mapWithVertex.vertices[0].y - mapWithVertex.position.y, mapWithVertex.vertices[0].x - mapWithVertex.position.x)); annoyingStuff.lasers(mapWithVertex.vertices[3], Math.atan2(mapWithVertex.vertices[3].y - mapWithVertex.position.y, mapWithVertex.vertices[3].x - mapWithVertex.position.x)); } if (Math.sin((2 * Math.PI * simulation.cycle) / (51)) > 0) { annoyingStuff.lasers(mapWithVertex.vertices[1], Math.atan2(mapWithVertex.vertices[1].y - mapWithVertex.position.y, mapWithVertex.vertices[1].x - mapWithVertex.position.x)); annoyingStuff.lasers(mapWithVertex.vertices[4], Math.atan2(mapWithVertex.vertices[4].y - mapWithVertex.position.y, mapWithVertex.vertices[4].x - mapWithVertex.position.x)); } if (Math.sin((2 * Math.PI * simulation.cycle) / (52)) > 0) { annoyingStuff.lasers(mapWithVertex.vertices[2], Math.atan2(mapWithVertex.vertices[2].y - mapWithVertex.position.y, mapWithVertex.vertices[2].x - mapWithVertex.position.x)); annoyingStuff.lasers(mapWithVertex.vertices[5], Math.atan2(mapWithVertex.vertices[5].y - mapWithVertex.position.y, mapWithVertex.vertices[5].x - mapWithVertex.position.x)); } 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 for (let i = 0; i < clothArray.length; i++) { soft.draw(clothArray[i]); soft.gravity(clothArray[i]); } ctx.beginPath(); ctx.fillStyle = "rgba(69, 69, 69, 0.1)"; ctx.rect(-475, 175, 425, 2300); ctx.rect(900, 175, 425, 2300); ctx.rect(-875, 175, 400, 10000); ctx.rect(-4200, -250, 2025, 2775); ctx.fill(); ctx.beginPath(); ctx.fillStyle = (m.pos.x < -725 && m.pos.y < 175) ? `rgba(68, 68, 68, ${Math.max(0.3, Math.min((-775 - m.pos.x) / 100, 0.99))})` : color.map; ctx.rect(-875, 50, 175, 150); ctx.fill(); }; annoyingStuff.laserBoss(-1525, 1025); spawn.pulsar(-1525, -850); spawn.pulsar(1125, 1600); spawn.pulsar(-250, 1600); spawn.pulsar(-1450, 1600); spawn.pulsar(-2950, 1750); spawn.pulsar(-3375, 1750); spawn.pulsar(-3825, 1300); spawn.pulsar(-3825, 850); spawn.pulsar(-3450, 50); spawn.pulsar(-2925, 50); spawn.pulsar(-1900, -400); spawn.pulsar(-1200, -400); powerUps.addResearchToLevel() powerUps.directSpawn(-775, 125, "tech"); if (!level.isNextLevelPowerUps && powerUp[powerUp.length - 1]) powerUp[powerUp.length - 1].collisionFilter.mask = cat.map | cat.body | cat.powerUp spawn.bodyRect(-875, 75, 25, 100); let hardBody = body[body.length - 1]; hardBody.collisionFilter.mask = cat.player | cat.map | cat.body | cat.bullet | cat.mob | cat.mobBullet | cat.powerUp }, flappyGon() { //community map by digin level.announceMobTypes(); simulation.inGameConsole(`flappy n-gon by Digin`); setTimeout(() => { simulation.inGameConsole("gravity is a choice"); }, 1000); setTimeout(() => { simulation.inGameConsole("everyone will fly"); }, 2000); setTimeout(() => { simulation.inGameConsole("jump from the post and find out"); }, 3000); level.setPosToSpawn(0, -50); //normal spawn level.exit.x = 8600; level.exit.y = -1100; level.defaultZoom = 1800; simulation.zoomTransition(level.defaultZoom); document.body.style.backgroundColor = "#55FF55"; var slimey = level.hazard(-200, -10, 9000, 10); // allow "flight" const old_playerOffGroundCheck = playerOffGroundCheck; playerOffGroundCheck = (event) => { old_playerOffGroundCheck(event); if (player.position.y < -300) { m.onGround = true; } }; const oldNextLevel = level.nextLevel; level.nextLevel = () => { // clear the flappy effects, because apparently there's no established api for this playerOffGroundCheck = old_playerOffGroundCheck; level.nextLevel = oldNextLevel; oldNextLevel(); }; spawn.mapRect(level.enter.x, level.enter.y + 20, 100, 20); // standard bumps spawn.mapRect(level.exit.x, level.exit.y + 20, 100, 20); spawn.mapRect(level.exit.x - 100, level.exit.y + 40, 200, 100); // room basis spawn.mapRect(-200, 0, 9000, 100); spawn.mapRect(-200, -1500, 9000, 100); spawn.mapRect(-200, -1500, 100, 1500); spawn.mapRect(8700, -1500, 100, 1500); // somewhat randomized flappy pylons const pylon = 1500; // height of the entire pylon assembly for (var i = 0; i < 10; i++) { var xbasis = 700 + i * 750; var window = 300 + (10 - i) * 50; var toph = pylon - window - 400 + (Math.random() - 0.5) * 400 - i * 50; if (i == 0) { // on the first one, the lower pile will always have a height of 300 toph = pylon - window - 300; } spawn.mapRect(xbasis, -1500, 100, toph); spawn.mapRect(xbasis, toph + window - pylon, 100, pylon - toph - window); if (i < 9) { spawn.randomMob(xbasis + 300, Math.random() * -1400); } else { spawn.randomLevelBoss(xbasis + 300, Math.random() * -1400); } if (i == 5) { spawn.secondaryBossChance(xbasis + 300, Math.random() * -1400); } } level.custom = () => { level.exit.drawAndCheck(); player.onGround = true; level.enter.draw(); }; const slimeRise = 0.15; level.customTopLayer = () => { slimey.height += slimeRise; slimey.min.y -= slimeRise; slimey.query(); }; powerUps.addResearchToLevel(); }, rings() { level.announceMobTypes(); simulation.inGameConsole(`rings by ThatLittleFrog`); setTimeout(() => { simulation.inGameConsole("go up"); }, 2000); level.setPosToSpawn(0, -2000); // spawn high up so you can go to the bottom of the lowest ring without tripping the too-low reset level.exit.x = 0; level.exit.y = -6400; 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"; function mkrect(x, y, w, h) { let who = body[body.length] = Bodies.rectangle(x, y, w, h, { collisionFilter: { category: cat.map, mask: cat.body | cat.player | cat.bullet | cat.powerUp | cat.mob | cat.mobBullet }, inertia: Infinity, //prevents rotation isNotHoldable: true, friction: 1, frictionStatic: 1, restitution: 0, frictionAir: 1, isStatic: true }); Composite.add(engine.world, who); who.classType = "body"; return body[body.length - 1]; } function makeRing(x, y, linegth, thicc = 200) { // I don't feel like doing trigonometry, so linegth is slightly different from the radius var _shape = [undefined, undefined, undefined, undefined]; _shape[0] = mkrect(x - linegth / 2 - thicc, y - linegth / 2 - thicc, linegth, thicc); _shape[1] = mkrect(x - linegth / 2 - thicc, y - linegth / 2, thicc, linegth); _shape[2] = mkrect(x - linegth / 2, y + linegth / 2 - thicc, linegth, thicc); _shape[3] = mkrect(x + linegth / 2 - thicc, y - linegth / 2 - thicc, thicc, linegth - thicc * 2); let ret = { shape: _shape, x: x, y: y, r: 0, rot(ang) { this.r = ang; let offs = 0; for (let shape of this.shape) { offs += Math.PI / 2; Matter.Body.setAngle(shape, ang); if (shape == this.shape[3]) { Matter.Body.setPosition(shape, { x: this.x + Math.cos(ang + offs) * (linegth / 2 - thicc / 2) - Math.cos(ang + offs + Math.PI / 2) * thicc, y: this.y + Math.sin(ang + offs) * (linegth / 2 - thicc / 2) - Math.sin(ang + offs + Math.PI / 2) * thicc }); } else { Matter.Body.setPosition(shape, { x: this.x + Math.cos(ang + offs) * (linegth / 2 - thicc / 2), y: this.y + Math.sin(ang + offs) * (linegth / 2 - thicc / 2) }); } } }, rotBy(ang) { this.rot(this.r + ang); } }; ret.rot(0); return ret; } var inner = makeRing(level.enter.x, level.enter.y, 1000); var mid = makeRing(level.enter.x, level.enter.y, 2500); var mid2 = makeRing(level.enter.x, level.enter.y, 4000); var out = makeRing(level.enter.x, level.enter.y, 6000); spawn.randomMob(level.enter.x + 250, level.enter.y); spawn.randomMob(level.enter.x + 1250, level.enter.y); spawn.randomMob(level.enter.x - 1250, level.enter.y); spawn.randomMob(level.enter.x, level.enter.y + 1250); spawn.randomMob(level.enter.x, level.enter.y - 1250); spawn.randomMob(level.enter.x + 1250, level.enter.y + 500); spawn.randomMob(level.enter.x - 1250, level.enter.y + 500); spawn.randomMob(level.enter.x + 500, level.enter.y + 1250); spawn.randomMob(level.enter.x + 500, level.enter.y - 1250); spawn.randomMob(level.enter.x + 2750, level.enter.y); spawn.randomMob(level.enter.x - 2750, level.enter.y); spawn.randomMob(level.enter.x, level.enter.y + 2750); spawn.randomMob(level.enter.x, level.enter.y - 2750); spawn.randomMob(level.enter.x + 2750, level.enter.y + 500); spawn.randomMob(level.enter.x - 2750, level.enter.y + 500); spawn.randomMob(level.enter.x + 500, level.enter.y + 2750); spawn.randomMob(level.enter.x + 500, level.enter.y - 2750); spawn.randomLevelBoss(level.enter.x, level.enter.y - 4250); spawn.secondaryBossChance(level.enter.x, level.enter.y + 4250); level.custom = () => { level.exit.drawAndCheck(); level.enter.draw(); }; level.customTopLayer = () => { inner.rotBy(0.01); mid.rotBy(-0.005); mid2.rotBy(0.003); out.rotBy(-0.002); }; powerUps.addResearchToLevel(); }, trial() { // trial, collab between Cirryn and Tarantula Hawk simulation.inGameConsole(`trial by Cirryn and Tarantula Hawk`); level.setPosToSpawn(0, -50); level.exit.x = 4150; 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 = "#d8dadf"; const button = level.button(2000, 0); const door = level.door(3930, -300, 40, 300, 300, 10); door.isClosing = false; var didTrialBegin = false; const customMob = { assassin(x, y) { // modified slasher mobs.spawn(x, y, 3, 30, "black"); let me = mob[mob.length - 1]; Matter.Body.rotate(me, 2 * Math.PI * Math.random()); me.accelMag = 0.0008 * simulation.accelScale; me.torqueMagnitude = 0.00002 * me.inertia * (Math.random() > 0.5 ? -1 : 1); me.frictionStatic = 0; me.frictionAir = 0.08; me.delay = 120 * simulation.CDScale; me.cd = 0; spawn.shield(me, x, y); me.damageReduction = 0; const start = window.performance.now(); // they only last ~ten seconds const lifespan = 15000 + 700 * (Math.random() - 0.5); me.onDamage = function () { Matter.Body.setAngularVelocity(me, me.angularVelocity + 1); }; me.do = function () { this.checkStatus(); this.alwaysSeePlayer(); this.attraction(); this.health = 1 - (window.performance.now() - start) / lifespan; if (this.health < 0) { this.death(); } Matter.Body.setAngularVelocity(me, me.angularVelocity + 0.05); }; }, mercenary(x, y) { // fast boi mobs.spawn(x, y, 3, 60, "white"); let me = mob[mob.length - 1]; Matter.Body.rotate(me, 2 * Math.PI * Math.random()); me.accelMag = 0.001 * simulation.accelScale; me.torqueMagnitude = 0.00001 * me.inertia * (Math.random() > 0.5 ? -1 : 1); me.frictionStatic = 0; me.frictionAir = 0.03; me.delay = 120 * simulation.CDScale; me.cd = 0; spawn.shield(me, x, y); me.damageReduction = 0; const start = window.performance.now(); // they only last ~ten seconds const lifespan = 25000 + 700 * (Math.random() - 0.5); me.onDamage = function () { Matter.Body.setAngularVelocity(me, me.angularVelocity + 1); }; me.do = function () { this.checkStatus(); this.attraction(); this.health = 1 - (window.performance.now() - start) / lifespan; if (this.health < 0) { this.death(); } this.alwaysSeePlayer(); }; } // eventually maybe add more custom mob types }; function randomWave(count, source) { // generates a wave list from a source // checks in spawn first, then customMob, for the sources var ret = []; for (var i = 0; i < count; i++) { var pick = source[Math.floor(Math.random() * source.length)]; if (spawn[pick]) { ret.push(spawn[pick]); } else if (customMob[pick]) { ret.push(customMob[pick]); } } return ret; } function wave(mobs) { // takes a list of functions that accept x,y coordinates to spawn a mob and spawns them in the ceiling for (var i = 0; i < mobs.length; i++) { var x = 1000 + 2400 * i / mobs.length + 200 * (Math.random() - 0.5); var y = -950 - 100 * Math.random(); mobs[i](x, y); } const ammoCount = Math.random() * (10 - simulation.difficulty / 4); for (var i = 0; i < ammoCount; i++) { powerUps.spawn(3300, -1000, "ammo"); } } level.custom = () => { door.openClose(); level.exit.drawAndCheck(); level.enter.draw(); }; level.customTopLayer = () => { button.query(); button.draw(); door.draw(); if (!button.isUp && !didTrialBegin) { didTrialBegin = true; simulation.inGameConsole('The Trial has begun.'); setTimeout(() => { simulation.inGameConsole('first wave (domitable)'); wave(randomWave(2 + simulation.difficulty * 0.1, spawn.fullPickList)); }, 3000); setTimeout(() => { simulation.inGameConsole('second wave (domitable)'); wave(randomWave(2 + simulation.difficulty * 0.1, spawn.fullPickList)); }, 13000); setTimeout(() => { simulation.inGameConsole('third wave (indomitable)'); wave(randomWave(4, ["assassin"])); }, 23000); setTimeout(() => { simulation.inGameConsole('fourth wave (domitable)'); wave(randomWave(4 + simulation.difficulty / 2, spawn.fullPickList)); }, 39000); setTimeout(() => { simulation.inGameConsole('fifth wave (domitable)'); wave(randomWave(4 + simulation.difficulty / 2, spawn.fullPickList)); }, 49000); setTimeout(() => { simulation.inGameConsole('sixth wave (indomitable)'); wave(randomWave(7, ["mercenary"])); }, 59000); setTimeout(() => { simulation.inGameConsole('seventh wave (boss)'); spawn.randomLevelBoss(700, -1000); var mainBoss = mob[mob.length - 1]; mainBoss.oldOnDeath = mainBoss.onDeath; mainBoss.onDeath = () => { door.isClosing = false; powerUps.spawn(4150, -30, "tech"); powerUps.spawn(4150, -30, "tech"); mainBoss.oldOnDeath(); } spawn.secondaryBossChance(3500, -1000); }, 86000); door.isClosing = true; } }; spawn.mapRect(-100, 0, 10000, 10000); // the left half of the room spawn.mapRect(-10000, -300, 9900, 10000); spawn.mapRect(-100, -300, 400, 100); spawn.mapRect(200, -800, 100, 500); spawn.mapRect(200, -800, 500, 100); spawn.mapRect(600, -1000, 100, 200); spawn.mapRect(600, -1100, 3000, 100); // the ceiling spawn.mapRect(3500, -1000, 100, 200); // the right half of the room spawn.mapRect(3500, -800, 500, 100); spawn.mapRect(3900, -800, 100, 500); spawn.mapRect(3900, -300, 400, 100); spawn.mapRect(4300, -300, 10000, 10000); for (var i = 0; i < 4; i++) { // "door" at the entrance spawn.bodyRect(200, -200 + i * 50, 20, 50); } for (var i = 0; i < 5; i++) { // some random rubble in the first half of the room spawn.bodyRect(400 + Math.random() * 1000, -200, 40 + Math.random() * 40, 40 + Math.random() * 40); } powerUps.addResearchToLevel(); //needs to run after mobs are spawneds }, // ******************************************************************************************************** // ******************************************************************************************************** // ***************************************** 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(); //check if blocks are in the exit zone and destroy them for (let i = 0; i < body.length; i++) { if (body[i].position.x > 1675) { Matter.Composite.remove(engine.world, body[i]); body.splice(i, 1); break } } //if no blocks left make a new one if (body.length < 2) { //< 2 because the door is a body spawn.bodyRect(1025, -550, 50, 50); } }; 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) //check if blocks are in the exit zone and destroy them for (let i = 0; i < body.length; i++) { if (body[i].position.x > 1675 && body[i].position.y > -350) { Matter.Composite.remove(engine.world, body[i]); body.splice(i, 1); break } } //if no blocks left make a new one if (body.length < 2) { //< 2 because the door is a body spawn.bodyRect(1025, -550, 50, 50); } }; 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) //check if blocks are in the exit zone and destroy them for (let i = 0; i < body.length; i++) { if (body[i].position.x > 1675 && body[i].position.y > -350) { Matter.Composite.remove(engine.world, body[i]); body.splice(i, 1); break } } //if no blocks left make a new one if (body.length < 2) { //< 2 because the door is a body spawn.bodyRect(1025, -550, 50, 50); } }; 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 ${powerUps.orb.gun()}`) level.custom = () => { if (instruction === 0 && simulation.isChoosing) { instruction++ level.trainingText(`use your field to pick up ${powerUps.orb.gun()}
choose a gun`) } else if (instruction === 1 && !simulation.isChoosing) { instruction++ level.trainingText(`use your field to pick up ${powerUps.orb.gun()}
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 ${powerUps.orb.gun()}
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 ${powerUps.orb.gun()}
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, 15); powerUps.directSpawn(1075, -50, "heal", false, 25); powerUps.directSpawn(1275, -65, "heal", false, 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.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.resetAllGuns(); 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) const who = mob[mob.length - 1] Matter.Body.setVelocity(who, { x: 0, y: 62 }); who.isDropPowerUp = false } spawn.mapRect(-2750, -2800, 2600, 4600); //left wall 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.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.resetAllGuns(); 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) mob[mob.length - 1].isDropPowerUp = false // Matter.Body.setVelocity(mob[mob.length - 1], { x: 0, y: 0 }); } spawn.mapRect(-2750, -2800, 2600, 4600); //left wall 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.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.resetAllGuns(); 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) mob[mob.length - 1].isDropPowerUp = false // Matter.Body.setVelocity(mob[mob.length - 1], { x: 0, y: 0 }); } spawn.mapVertex(510, -430, "725 0 725 80 -650 80 -650 -80 650 -80"); //upper room with mobs 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.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.resetAllGuns(); 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) mob[mob.length - 1].isDropPowerUp = false // 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.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.resetAllGuns(); 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) mob[mob.length - 1].isDropPowerUp = false // Matter.Body.setVelocity(mob[mob.length - 1], { x: 0, y: 0 }); } spawn.mapRect(-2750, -2800, 2600, 4600); //left wall // 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.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.resetAllGuns(); 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.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.resetAllGuns(); 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.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.resetAllGuns(); 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.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.resetAllGuns(); 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 }, };