1
0
forked from sent/waves
waves/public/assets/g/ngon/js/level.js
2025-04-09 17:11:14 -05:00

31702 lines
1.4 MiB
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

let body = []; //non static bodies
let map = []; //all static bodies
let cons = []; //all constraints between a point and a body
let consBB = []; //all constraints between two bodies
let composite = [] //rotors and other map elements that don't fit
const level = {
defaultZoom: 1400,
onLevel: -1,
levelsCleared: 0,
// playableLevels: ["pavilion", "pavilion", "pavilion", "pavilion", "pavilion", "pavilion", "pavilion", "pavilion", "pavilion", "pavilion", "pavilion"],
//see level.populateLevels: (intro, ... , reservoir or factory, reactor, ... , gauntlet, final) added later
playableLevels: ["labs", "rooftops", "skyscrapers", "warehouse", "highrise", "office", "aerie", "satellite", "sewers", "testChamber", "pavilion", "lock"],
communityLevels: ["gauntlet", "stronghold", "basement", "crossfire", "vats", "run", "ngon", "house", "perplex", "coliseum", "tunnel", "islands", "temple", "dripp", "biohazard", "stereoMadness", "yingYang", "staircase", "fortress", "commandeer", "clock", "buttonbutton", "downpour", "superNgonBros", "underpass", "cantilever", "dojo", "tlinat", "ruins", "ace", "crimsonTowers"],
trainingLevels: ["walk", "crouch", "jump", "hold", "throw", "throwAt", "deflect", "heal", "fire", "nailGun", "shotGun", "superBall", "matterWave", "missile", "stack", "mine", "grenades", "harpoon", "diamagnetism"],
levels: [],
start() {
if (level.levelsCleared === 0) { //this code only runs on the first level
// simulation.enableConstructMode() //tech.giveTech('motion sickness') //used to build maps in testing mode
// simulation.isHorizontalFlipped = true
// tech.giveTech("performance")
// level.difficultyIncrease(3 * 4) //30 is near max on hard //60 is near max on why
// spawn.setSpawnList();
// spawn.setSpawnList();
// m.maxHealth = m.health = 100
// tech.isRerollDamage = true
// powerUps.research.changeRerolls(99999)
// m.immuneCycle = Infinity //you can't take damage
// tech.tech[297].frequency = 100
// m.couplingChange(10)
// m.setField("molecular assembler") //1 standing wave 2 perfect diamagnetism 3 negative mass 4 molecular assembler 5 plasma torch 6 time dilation 7 metamaterial cloaking 8 pilot wave 9 wormhole
// m.energy = 0
// simulation.molecularMode = 2
// m.damage(0.1);
// b.giveGuns("super balls") //0 nail gun 1 shotgun 2 super balls 3 wave 4 missiles 5 grenades 6 spores 7 drones 8 foam 9 harpoon 10 mine 11 laser
// b.giveGuns("drones") //0 nail gun 1 shotgun 2 super balls 3 wave 4 missiles 5 grenades 6 spores 7 drones 8 foam 9 harpoon 10 mine 11 laser
// b.guns[3].ammo = 100000000
// requestAnimationFrame(() => { tech.giveTech("MACHO") });
// for (let i = 0; i < 1; ++i) tech.giveTech("additive manufacturing")
// for (let i = 0; i < 1; ++i) tech.giveTech("dark star")
// for (let i = 0; i < 1; ++i) tech.giveTech("foam-bot")
// for (let i = 0; i < 1; ++i) tech.giveTech("nail-bot")
// for (let i = 0; i < 1; ++i) tech.giveTech("sound-bot upgrade")
// for (let i = 0; i < 1; ++i) tech.giveTech("nail-bot upgrade")
// requestAnimationFrame(() => { for (let i = 0; i < 30; i++) tech.giveTech("laser-bot") });
// for (let i = 0; i < 1; i++) tech.giveTech("laser-bot upgrade")
// for (let i = 0; i < 1; ++i) tech.giveTech("uncertainty principle")
// for (let i = 0; i < 1; ++i) tech.giveTech("mechanical resonance")
// for (let i = 0; i < 3; i++) powerUps.directSpawn(450, -50, "tech");
// for (let i = 0; i < 10; i++) powerUps.directSpawn(1750, -500, "research");
// for (let i = 0; i < 10; i++) powerUps.directSpawn(1750, -500, "coupling");
// level.testing();
// for (let i = 0; i < 10; ++i) spawn.sniper(1900, -500)
// for (let i = 0; i < 1; ++i) spawn.slasher2(1900, -500)
// for (let i = 0; i < 1; ++i) spawn.shooterBoss(1900, -2500)
// spawn.suckerBoss(1900, -500, 25)
// spawn.slasher2(2000, -1150)
// spawn.zombie(-3000, -500 + 300 * Math.random(), 30, 5, "white") // zombie(x, y, radius, sides, color)
// for (let i = 0; i < 20; ++i) spawn.starter(1000 + 1000 * Math.random(), -500 + 300 * Math.random())
// tech.addJunkTechToPool(2)
// tech.tech[322].frequency = 100
// spawn.tetherBoss(1900, -500, { x: 1900, y: -500 })
// for (let i = 0; i < 40; ++i) tech.giveTech()
level[simulation.isTraining ? "walk" : "intro"]() //normal starting level **************************************************
// simulation.isAutoZoom = false; //look in close
// simulation.zoomScale *= 0.5;
// simulation.setZoom();
// for (let i = 0; i < 2; ++i) powerUps.directSpawn(m.pos.x + 50 * Math.random(), m.pos.y + 50 * Math.random(), "tech");
// for (let i = 0; i < 2; ++i) powerUps.directSpawn(m.pos.x + 50 * Math.random(), m.pos.y + 50 * Math.random(), "boost");
// for (let i = 0; i < 2; ++i) powerUps.directSpawn(m.pos.x + 50 * Math.random(), m.pos.y + 50 * Math.random(), "heal");
// for (let i = 0; i < 2; i++) powerUps.spawn(player.position.x + Math.random() * 50, player.position.y - Math.random() * 50, "field", false);
//lore testing
// for (let i = 0; i < 5; i++) tech.giveTech("undefined")
// lore.techCount = 2
// simulation.isCheating = false //true;
// level.levelsCleared = 10
// localSettings.loreCount = 5 //this sets what conversation is heard
// if (localSettings.isAllowed) localStorage.setItem("localSettings", JSON.stringify(localSettings)); //update local storage
// level.onLevel = -1 //this sets level.levels[level.onLevel] = undefined which is required to run the conversation
// level.null()
// localSettings.isHuman = true
// tech.isNoDraftPause = false //disable pause
// mobs.mobDeaths = 200 //to prevent pacifist mode
// for (let i = 0; i < 13; i++) level.nextLevel(); //jump to final boss
// lore.unlockTesting();
// tech.giveTech("tinker"); //show junk tech in experiment mode
// m.storeTech()
// powerUps.spawn(m.pos.x, m.pos.y, "entanglement", false);
} else {
spawn.setSpawnList(); //picks a couple mobs types for a themed random mob spawns
// spawn.pickList = ["focuser", "focuser"]
level[level.levels[level.onLevel]](); //picks the current map from the the levels array
if (!simulation.isCheating && !build.isExperimentRun && !simulation.isTraining) {
localSettings.runCount += level.levelsCleared //track the number of total runs locally
localSettings.levelsClearedLastGame = level.levelsCleared
if (localSettings.isAllowed) localStorage.setItem("localSettings", JSON.stringify(localSettings)); //update local storage
}
}
if (!simulation.isTraining) level.levelAnnounce();
simulation.noCameraScroll();
simulation.setZoom();
level.addToWorld(); //add bodies to game engine
simulation.draw.setPaths();
b.respawnBots();
m.resetHistory();
if (tech.isForeverDrones) {
if (tech.isDroneRadioactive) {
for (let i = 0; i < tech.isForeverDrones * 0.25; i++) {
b.droneRadioactive({ x: m.pos.x + 30 * (Math.random() - 0.5), y: m.pos.y + 30 * (Math.random() - 0.5) }, 5)
bullet[bullet.length - 1].endCycle = Infinity
}
} else {
for (let i = 0; i < tech.isForeverDrones; i++) {
b.drone({ x: m.pos.x + 30 * (Math.random() - 0.5), y: m.pos.y + 30 * (Math.random() - 0.5) }, 5)
bullet[bullet.length - 1].endCycle = Infinity
}
}
}
if (tech.isMACHO) spawn.MACHO()
for (let i = 0; i < tech.wimpCount; i++) {
spawn.WIMP()
mob[mob.length - 1].isDecoupling = true //so you can find it to remove
for (let j = 0, len = 4; j < len; j++) powerUps.spawn(level.exit.x + 100 * (Math.random() - 0.5), level.exit.y - 100 + 100 * (Math.random() - 0.5), "research", false)
}
// if (tech.isFlipFlopLevelReset && !tech.isFlipFlopOn) {
if ((tech.isRelay || tech.isFlipFlop) && !tech.isFlipFlopOn) {
tech.isFlipFlopOn = true
if (tech.isFlipFlopHealth) m.setMaxHealth()
if (tech.isRelayEnergy) m.setMaxEnergy()
m.eyeFillColor = m.fieldMeterColor
simulation.makeTextLog(`tech.isFlipFlopOn <span class='color-symbol'>=</span> true`);
}
// if (m.plasmaBall) m.plasmaBall.reset()
if (m.plasmaBall) m.plasmaBall.fire()
if (localSettings.entanglement && localSettings.entanglement.levelName === level.levels[level.onLevel]) {
const flip = localSettings.entanglement.isHorizontalFlipped === simulation.isHorizontalFlipped ? 1 : -1
powerUps.directSpawn(flip * localSettings.entanglement.position.x, localSettings.entanglement.position.y, "entanglement", false);
}
level.newLevelOrPhase()
},
newLevelOrPhase() { //runs on each new level but also on final boss phases
//used for generalist and pigeonhole principle
tech.buffedGun++
if (tech.buffedGun > b.inventory.length - 1) tech.buffedGun = 0;
if (tech.isGunCycle && b.activeGun !== null && b.inventory.length) {
b.inventoryGun = tech.buffedGun;
simulation.switchGun();
}
if (tech.isGunChoice && Number.isInteger(tech.buffedGun) && b.inventory.length) {
var gun = b.guns[b.inventory[tech.buffedGun]].name
simulation.makeTextLog(`pigeonhole principle: <strong>+${(31 * Math.max(0, b.inventory.length)).toFixed(0)}%</strong> <strong class='color-d'>damage</strong> for <strong class="highlight">${gun}</strong>`, 600);
}
if (tech.isSwitchReality && level.levelsCleared !== 0) {
simulation.makeTextLog(`simulation.amplitude <span class='color-symbol'>=</span> ${Math.random()}`);
m.switchWorlds()
simulation.trails()
powerUps.spawn(player.position.x + Math.random() * 50, player.position.y - Math.random() * 50, "tech", false);
}
if (tech.isHealLowHealth) {
if (tech.isEnergyHealth) {
var len = 4 * (1 - m.energy / m.maxEnergy) //as a percent
} else {
var len = 4 * (1 - m.health / m.maxHealth) //as a percent
}
for (let i = 0; i < len; i++) powerUps.spawn(player.position.x + 90 * (Math.random() - 0.5), player.position.y + 90 * (Math.random() - 0.5), "heal", false);
}
},
trainingText(say) {
simulation.lastLogTime = 0; //clear previous messages
simulation.isTextLogOpen = true
simulation.makeTextLog(`<span style="font-size: 120%;line-height: 120%;"><span style="color:#51f;">supervised.learning</span>(<span style="color:#777; font-size: 80%;">${(Date.now() / 1000).toFixed(0)} s</span>)<span class='color-symbol'>:</span><br>${say}</span>`, Infinity)
simulation.isTextLogOpen = false
// lore.trainer.text("Wow. Just a platform.")
},
trainingBackgroundColor: "#e1e1e1",
custom() { },
customTopLayer() { },
setDifficulty() {
simulation.difficulty = 0
m.dmgScale = 1; //damage done by player decreases each level
simulation.accelScale = 1 //mob acceleration increases each level
simulation.CDScale = 1 //mob CD time decreases each level
simulation.dmgScale = Math.max(0.1, 0.29 * simulation.difficulty) //damage done by mobs scales with total levels
simulation.healScale = 1 / (1 + simulation.difficulty * 0.045) //a higher denominator makes for lower heals // m.health += heal * simulation.healScale;
},
difficultyIncrease(num = 1) {
for (let i = 0; i < num; i++) {
simulation.difficulty++
m.dmgScale *= 0.905; //damage done by player decreases each level
if (simulation.accelScale < 6) simulation.accelScale *= 1.024 //mob acceleration increases each level
if (simulation.CDScale > 0.15) simulation.CDScale *= 0.964 //mob CD time decreases each level
}
simulation.dmgScale = Math.max(0.1, 0.285 * simulation.difficulty) //damage done by mobs scales with total levels
simulation.healScale = 1 / (1 + simulation.difficulty * 0.045) //a higher denominator makes for lower heals // m.health += heal * simulation.healScale;
// console.log(`CD = ${simulation.CDScale}`)
},
difficultyDecrease(num = 1) { //used in easy mode for simulation.reset()
for (let i = 0; i < num; i++) {
simulation.difficulty--
m.dmgScale /= 0.905; //damage done by player decreases each level
if (simulation.accelScale > 1) simulation.accelScale /= 1.024 //mob acceleration increases each level
if (simulation.CDScale < 1) simulation.CDScale /= 0.964 //mob CD time decreases each level
}
if (simulation.difficulty < 1) simulation.difficulty = 0;
simulation.dmgScale = Math.max(0.1, 0.285 * simulation.difficulty) //damage done by mobs scales with total levels
simulation.healScale = 1 / (1 + simulation.difficulty * 0.045)
},
difficultyText() {
if (simulation.difficultyMode === 1) {
return "easy"
} else if (simulation.difficultyMode === 2) {
return "normal"
} else if (simulation.difficultyMode === 4) {
return "hard"
} else if (simulation.difficultyMode === 6) {
return "why"
}
},
levelAnnounce() {
const difficulty = simulation.isCheating ? "testing" : level.difficultyText()
if (level.levelsCleared === 0) {
document.title = "n-gon: (" + difficulty + ")";
} else {
document.title = `n-gon: ${level.levelsCleared} ${level.levels[level.onLevel]} (${difficulty})`
simulation.makeTextLog(`<span class='color-var'>level</span>.onLevel <span class='color-symbol'>=</span> "<span class='color-text'>${level.levels[level.onLevel]}</span>"`);
}
// simulation.makeTextLog(`
// input.key.up = ["<span class='color-text'>${input.key.up}</span>", "<span class='color-text'>ArrowUp</span>"]
// <br>input.key.left = ["<span class='color-text'>${input.key.left}</span>", "<span class='color-text'>ArrowLeft</span>"]
// <br>input.key.down = ["<span class='color-text'>${input.key.down}</span>", "<span class='color-text'>ArrowDown</span>"]
// <br>input.key.right = ["<span class='color-text'>${input.key.right}</span>", "<span class='color-text'>ArrowRight</span>"]
// <br>
// <br><span class='color-var'>m</span>.fieldMode = "<span class='color-text'>${m.fieldUpgrades[m.fieldMode].name}</span>"
// <br>input.key.field = ["<span class='color-text'>${input.key.field}</span>", "<span class='color-text'>right mouse</span>"]
// <br><span class='color-var'>m</span>.field.description = "<span class='color-text'>${m.fieldUpgrades[m.fieldMode].description}</span>"
// `, 1200);
},
disableExit: false,
nextLevel() {
if (!level.disableExit) {
level.levelsCleared++;
level.onLevel++; //cycles map to next level
if (simulation.isTraining) {
if (level.onLevel > level.levels.length - 1) {
level.disableExit = true
document.getElementById("health").style.display = "none"
document.getElementById("health-bg").style.display = "none"
document.getElementById("defense-bar").style.display = "none"
document.getElementById("damage-bar").style.display = "none"
document.getElementById("text-log").style.display = "none"
document.getElementById("fade-out").style.opacity = 1; //slowly fades out
setTimeout(function () {
simulation.paused = true;
level.disableExit = false;
engine.world.bodies.forEach((body) => {
Matter.Composite.remove(engine.world, body)
})
Engine.clear(engine);
simulation.splashReturn();
}, 6000);
return
} else {
level.setDifficulty()
}
} else {
if (level.onLevel > level.levels.length - 1) level.onLevel = 0;
level.difficultyIncrease(simulation.difficultyMode)
}
//reset lost tech display
for (let i = 0; i < tech.tech.length; i++) {
if (tech.tech[i].isLost) tech.tech[i].isLost = false;
}
tech.isDeathAvoidedThisLevel = false;
simulation.updateTechHUD();
simulation.clearNow = true; //triggers in simulation.clearMap to remove all physics bodies and setup for new map
}
},
populateLevels() { //run a second time if URL is loaded
if (document.getElementById("banned").value) { //remove levels from ban list in settings
const banList = document.getElementById("banned").value.replace(/,/g, ' ').replace(/\s\s+/g, ' ').replace(/[^\w\s]/g, '') //replace commas with spaces, replace double spaces with single, remove strange symbols
const remove = banList.split(" ");
// console.log('remove these', remove)
// console.log('community levels before', level.communityLevels)
for (let i = 0; i < remove.length; i++) {
const index = level.communityLevels.indexOf(remove[i])
if (index !== -1) {
level.communityLevels.splice(index, 1);
// console.log('removed level:', remove[i])
requestAnimationFrame(() => { simulation.makeTextLog(`banned level: <strong style="color: '#f00';">${remove[i]}</strong>`); });
}
}
// console.log('community levels after', level.communityLevels)
// console.log('Landgreen levels before', level.playableLevels)
for (let i = 0; i < remove.length; i++) {
if (level.playableLevels.length + level.communityLevels.length * simulation.isCommunityMaps < 10) break //can't remove too many levels
const index = level.playableLevels.indexOf(remove[i])
if (index !== -1) {
level.playableLevels.splice(index, 1);
// console.log('removed level:', remove[i])
requestAnimationFrame(() => { simulation.makeTextLog(`banned level: <strong style="color: '#f00';">${remove[i]}</strong>`); });
}
}
// console.log('Landgreen levels after', level.playableLevels)
}
if (document.getElementById("seed").value) { //check for player entered seed in settings
Math.initialSeed = String(document.getElementById("seed").value)
Math.seed = Math.abs(Math.hash(Math.initialSeed)) //update randomizer seed in case the player changed it
}
if (simulation.isTraining) {
simulation.isHorizontalFlipped = false
level.levels = level.trainingLevels.slice(0) //copy array, not by just by assignment
} else { //add remove and shuffle levels for the normal game (not training levels)
level.levels = level.playableLevels.slice(0) //copy array, not by just by assignment
if (simulation.isCommunityMaps) {
level.levels = level.levels.concat(level.communityLevels)
simulation.isHorizontalFlipped = false;
} else {
simulation.isHorizontalFlipped = (Math.seededRandom() < 0.5) ? true : false //if true, some maps are flipped horizontally
}
level.levels = shuffle(level.levels); //shuffles order of maps with seeded random
level.levels.length = 9 //remove any extra levels past 9
level.levels.splice(Math.floor(Math.seededRandom(level.levels.length * 0.6, level.levels.length)), 0, Math.random() < 0.5 ? "factory" : "reservoir"); //add level to the back half of the randomized levels list
level.levels.splice(Math.floor(Math.seededRandom(level.levels.length * 0.6, level.levels.length)), 0, "reactor"); //add level to the back half of the randomized levels list
if (!build.isExperimentSelection || (build.hasExperimentalMode && !simulation.isCheating)) { //experimental mode is endless, unless you only have an experiment Tech
level.levels.unshift("intro"); //add level to the start of the randomized levels list
level.levels.push("subway"); //add level to the end of the randomized levels list
level.levels.push("final"); //add level to the end of the randomized levels list
}
}
//set seeded random lists of mobs and bosses
spawn.mobTypeSpawnOrder = []
for (let i = 0; i < level.levels.length; i++) spawn.mobTypeSpawnOrder.push(spawn.fullPickList[Math.floor(Math.seededRandom(0, spawn.fullPickList.length))])
spawn.bossTypeSpawnOrder = []
for (let i = 0; i < level.levels.length * 2; i++) spawn.bossTypeSpawnOrder.push(spawn.randomBossList[Math.floor(Math.seededRandom(0, spawn.randomBossList.length))])
},
flipHorizontal() {
const flipX = (who) => {
for (let i = 0, len = who.length; i < len; i++) {
Matter.Body.setPosition(who[i], {
x: -who[i].position.x,
y: who[i].position.y
})
}
}
flipX(map)
flipX(body)
flipX(mob)
flipX(powerUp)
for (let i = 0, len = cons.length; i < len; i++) {
cons[i].pointA.x *= -1
cons[i].pointB.x *= -1
}
for (let i = 0, len = consBB.length; i < len; i++) {
consBB[i].pointA.x *= -1
consBB[i].pointB.x *= -1
}
level.exit.x = -level.exit.x - 100 //minus the 100 because of the width of the graphic
},
exitCount: 0,
// playerExitCheck() {
// if (
// player.position.x > level.exit.x &&
// player.position.x < level.exit.x + 100 &&
// player.position.y > level.exit.y - 150 &&
// player.position.y < level.exit.y - 40 &&
// player.velocity.y < 0.1
// ) {
// level.exitCount++
// if (level.exitCount > 120) {
// level.exitCount = 0
// level.nextLevel()
// }
// }
// },
setPosToSpawn(xPos, yPos) {
m.spawnPos.x = m.pos.x = xPos;
m.spawnPos.y = m.pos.y = yPos;
level.enter.x = m.spawnPos.x - 50;
level.enter.y = m.spawnPos.y + 20;
m.transX = m.transSmoothX = canvas.width2 - m.pos.x;
m.transY = m.transSmoothY = canvas.height2 - m.pos.y;
m.Vx = m.spawnVel.x;
m.Vy = m.spawnVel.y;
player.force.x = 0;
player.force.y = 0;
Matter.Body.setPosition(player, m.spawnPos);
Matter.Body.setVelocity(player, m.spawnVel);
//makes perfect diamagnetism tech: Lenz's law show up in the right spot at the start of a level
m.fieldPosition = {
x: m.pos.x,
y: m.pos.y
}
m.fieldAngle = m.angle
},
enter: {
x: 0,
y: 0,
draw() {
ctx.beginPath();
ctx.moveTo(level.enter.x, level.enter.y + 30);
ctx.lineTo(level.enter.x, level.enter.y - 80);
ctx.bezierCurveTo(level.enter.x, level.enter.y - 170, level.enter.x + 100, level.enter.y - 170, level.enter.x + 100, level.enter.y - 80);
ctx.lineTo(level.enter.x + 100, level.enter.y + 30);
ctx.lineTo(level.enter.x, level.enter.y + 30);
ctx.fillStyle = "#ccc";
ctx.fill();
}
},
exit: {
x: 0,
y: 0,
drawAndCheck() {
if ( //check
player.position.x > level.exit.x &&
player.position.x < level.exit.x + 100 &&
player.position.y > level.exit.y - 150 &&
player.position.y < level.exit.y - 0 &&
player.velocity.y < 0.15
) {
// level.exitCount += input.down ? 8 : 2
level.exitCount += 2
} else if (level.exitCount > 0) {
level.exitCount -= 2
}
ctx.beginPath();
ctx.moveTo(level.exit.x, level.exit.y + 30);
ctx.lineTo(level.exit.x, level.exit.y - 80);
ctx.bezierCurveTo(level.exit.x, level.exit.y - 170, level.exit.x + 100, level.exit.y - 170, level.exit.x + 100, level.exit.y - 80);
ctx.lineTo(level.exit.x + 100, level.exit.y + 30);
ctx.lineTo(level.exit.x, level.exit.y + 30);
ctx.fillStyle = "#0ff";
ctx.fill();
if (level.exitCount > 0) { //stroke outline of door from 2 sides, grows with count
ctx.beginPath();
ctx.moveTo(level.exit.x, level.exit.y + 40);
ctx.lineTo(level.exit.x, level.exit.y - 80);
ctx.bezierCurveTo(level.exit.x, level.exit.y - 148, level.exit.x + 50, level.exit.y - 148, level.exit.x + 50, level.exit.y - 148);
ctx.moveTo(level.exit.x + 100, level.exit.y + 40);
ctx.lineTo(level.exit.x + 100, level.exit.y - 80);
ctx.bezierCurveTo(level.exit.x + 100, level.exit.y - 148, level.exit.x + 50, level.exit.y - 148, level.exit.x + 50, level.exit.y - 148);
ctx.setLineDash([200, 200]);
ctx.lineDashOffset = Math.max(-15, 185 - 2.1 * level.exitCount)
ctx.strokeStyle = "#444"
ctx.lineWidth = 2
ctx.stroke();
ctx.setLineDash([0, 0]);
if (level.exitCount > 100) {
level.exitCount = 0
level.nextLevel()
}
}
},
// draw() {
// ctx.beginPath();
// ctx.moveTo(level.exit.x, level.exit.y + 30);
// ctx.lineTo(level.exit.x, level.exit.y - 80);
// ctx.bezierCurveTo(level.exit.x, level.exit.y - 170, level.exit.x + 100, level.exit.y - 170, level.exit.x + 100, level.exit.y - 80);
// ctx.lineTo(level.exit.x + 100, level.exit.y + 30);
// ctx.lineTo(level.exit.x, level.exit.y + 30);
// ctx.fillStyle = "#0ff";
// ctx.fill();
// }
},
addToWorld() { //needs to be run to put bodies into the world
for (let i = 0; i < map.length; i++) {
map[i].collisionFilter.category = cat.map;
map[i].collisionFilter.mask = cat.player | cat.map | cat.body | cat.bullet | cat.powerUp | cat.mob | cat.mobBullet;
Matter.Body.setStatic(map[i], true); //make static
Composite.add(engine.world, map[i]); //add to world
}
},
spinner(x, y, width, height, density = 0.001, angle = 0, frictionAir = 0.001, angularVelocity = 0) {
x += width / 2
y += height / 2
const who = body[body.length] = Bodies.rectangle(x, y, width, height, {
collisionFilter: {
category: cat.body,
mask: cat.player | cat.body | cat.bullet | cat.powerUp | cat.mob | cat.mobBullet
},
isNotHoldable: true,
frictionAir: frictionAir,
friction: 1,
frictionStatic: 1,
restitution: 0,
});
Matter.Body.setAngle(who, angle)
Matter.Body.setAngularVelocity(who, angularVelocity);
Matter.Body.setDensity(who, density)
Composite.add(engine.world, who); //add to world
who.classType = "body"
const constraint = Constraint.create({ //fix rotor in place, but allow rotation
pointA: {
x: who.position.x,
y: who.position.y
},
bodyB: who,
stiffness: 1,
damping: 1
});
Composite.add(engine.world, constraint);
return constraint
},
rotor(x, y, width, height, density = 0.001, angle = 0, frictionAir = 0.001, angularVelocity = 0, rotationForce = 0.0005) {
x += width / 2
y += height / 2
const who = body[body.length] = Bodies.rectangle(x, y, width, height, {
collisionFilter: {
category: cat.body,
mask: cat.player | cat.body | cat.bullet | cat.powerUp | cat.mob | cat.mobBullet
},
isNotHoldable: true,
frictionAir: frictionAir,
friction: 1,
frictionStatic: 1,
restitution: 0,
rotationForce: rotationForce
});
Matter.Body.setAngle(who, angle)
Matter.Body.setAngularVelocity(who, angularVelocity);
Matter.Body.setDensity(who, density)
const constraint = Constraint.create({ //fix rotor in place, but allow rotation
pointA: {
x: who.position.x,
y: who.position.y
},
bodyB: who,
stiffness: 1,
damping: 1
});
Composite.add(engine.world, constraint);
who.center = {
x: who.position.x,
y: who.position.y
}
who.rotate = function () {
if (!m.isBodiesAsleep) {
Matter.Body.applyForce(this, {
x: this.position.x + 100,
y: this.position.y + 100
}, {
x: this.rotationForce * this.mass,
y: 0
})
} else {
Matter.Body.setAngularVelocity(this, 0);
}
}
// if (rotate) {
// rotor.rotate = function() {
// if (!m.isBodiesAsleep) {
// Matter.Body.applyForce(rotor, {
// x: rotor.position.x + 100,
// y: rotor.position.y + 100
// }, {
// x: rotate * rotor.mass,
// y: 0
// })
// } else {
// Matter.Body.setAngularVelocity(rotor, 0);
// }
// }
// }
Composite.add(engine.world, who); //add to world
who.classType = "body"
return who
},
boost(x, y, height = 1000) { //height is how high the player will be flung above y
who = map[map.length] = Matter.Bodies.fromVertices(x + 50, y + 35, Vertices.fromPath("120 40 -120 40 -50 -40 50 -40"), {
collisionFilter: {
category: cat.body,
mask: cat.player | cat.body | cat.bullet | cat.powerUp | cat.mob | cat.mobBullet //cat.player | cat.map | cat.body | cat.bullet | cat.powerUp | cat.mob | cat.mobBullet
},
boostBounds: {
min: {
x: x,
y: y - 20
},
max: {
x: x + 100,
y: y
}
},
yVelocity: -1.21 * Math.sqrt(Math.abs(height)),
query() {
// check for collisions
query = (who) => {
if (Matter.Query.region(who, this.boostBounds).length > 0) {
list = Matter.Query.region(who, this.boostBounds)
Matter.Body.setVelocity(list[0], {
x: list[0].velocity.x + (Math.random() - 0.5) * 2.5, //add a bit of horizontal drift to reduce endless bounces
y: this.yVelocity //give a upwards velocity
});
}
}
query(body)
query(mob)
query(bullet)
query(powerUp)
//player collision
if (Matter.Query.region([player], this.boostBounds).length > 0 && !input.down) {
m.buttonCD_jump = 0; // reset short jump counter to prevent short jumps on boosts
m.hardLandCD = 0 // disable hard landing
if (player.velocity.y > 26) {
Matter.Body.setVelocity(player, {
x: player.velocity.x,
y: -15 //gentle bounce if coming down super fast
});
} else {
Matter.Body.setVelocity(player, {
x: player.velocity.x + (Math.random() - 0.5) * 2.5,
y: this.yVelocity //give a upwards velocity that will put the player that the height desired
});
}
}
//draw
ctx.fillStyle = "rgba(200,0,255,0.15)";
ctx.fillRect(this.boostBounds.min.x, this.boostBounds.min.y - 10, 100, 30);
ctx.fillStyle = "rgba(200,0,255,0.05)";
ctx.fillRect(this.boostBounds.min.x, this.boostBounds.min.y - 50, 100, 70);
// ctx.fillStyle = "rgba(200,0,255,0.02)";
// ctx.fillRect(x, y - 120, 100, 120);
},
});
return who
},
elevator(x, y, width, height, maxHeight, force = 0.003, friction = {
up: 0.01,
down: 0.2
}, isAtTop = false) {
x += width / 2
y += height / 2
maxHeight += height / 2
const yTravel = maxHeight - y
force += simulation.g
const who = body[body.length] = Bodies.rectangle(x, isAtTop ? maxHeight : y, width, height, {
collisionFilter: {
category: cat.body, //cat.map,
mask: cat.map | cat.player | cat.body | cat.bullet | cat.powerUp | cat.mob | cat.mobBullet
},
inertia: Infinity, //prevents rotation
isNotHoldable: true,
friction: 1,
frictionStatic: 1,
restitution: 0,
frictionAir: 0.001,
holdX: x,
move() {
if (!m.isBodiesAsleep) {
if (this.isUp) { //moving up still with high air friction
this.force.y -= force * this.mass //hard force propels up, even with high friction
if (this.position.y < maxHeight) { //switch to down mode
this.isUp = false
this.frictionAir = friction.down
//adds a hard jerk at the top of vertical motion because it's fun
Matter.Body.setPosition(this, {
x: this.holdX,
y: maxHeight
});
Matter.Body.setVelocity(this, {
x: 0,
y: 0
});
}
} else if (this.position.y + 10 * this.velocity.y > y) { //free falling down, with only air friction
Matter.Body.setVelocity(this, { //slow down early to avoid a jerky stop that can pass through blocks
x: 0,
y: this.velocity.y * 0.7
});
if (this.position.y + this.velocity.y > y) { //switch to up mode
this.isUp = true
this.frictionAir = friction.up
}
}
Matter.Body.setVelocity(this, { x: 0, y: this.velocity.y });
}
//edge limits
if (this.position.y < maxHeight) {
Matter.Body.setPosition(this, {
x: this.holdX,
y: maxHeight
});
} else if (this.position.y > y) {
Matter.Body.setPosition(this, {
x: this.holdX,
y: y
});
}
// hold horizontal position
Matter.Body.setPosition(this, {
x: this.holdX,
y: this.position.y
});
},
off() {
Matter.Body.setPosition(this, {
x: this.holdX,
y: this.position.y
});
Matter.Body.setVelocity(this, {
x: 0,
y: this.velocity.y
});
},
constraint: this.null,
addConstraint() {
this.constraint = Constraint.create({
pointA: {
x: this.position.x,
y: this.position.y
},
bodyB: this,
stiffness: 0.01,
damping: 0.3
});
Composite.add(engine.world, this.constraint);
},
removeConstraint() {
Composite.remove(engine.world, this.constraint, true)
},
drawTrack() {
ctx.fillStyle = "#ccc"
ctx.fillRect(this.holdX, y, 5, yTravel)
}
});
Matter.Body.setDensity(who, 0.01) //10x density for added stability
Composite.add(engine.world, who); //add to world
who.classType = "body"
return who
},
spring(x, y, v = "-100 0 100 0 70 40 0 50 -70 40", force = 0.01, distance = 300, angle = 0) {
const who = body[body.length] = Matter.Bodies.fromVertices(x, y, Vertices.fromPath(v), {
collisionFilter: {
category: cat.body,
mask: cat.player | cat.body | cat.bullet | cat.powerUp | cat.mob | cat.mobBullet //cat.player | cat.map | cat.body | cat.bullet | cat.powerUp | cat.mob | cat.mobBullet
},
inertia: Infinity, //prevents rotation
isNotHoldable: true,
friction: 1,
frictionStatic: 1,
restitution: 0,
frictionAir: 1,
density: 0.1,
isReady: true,
isResetting: false,
query() {
if (this.isReady) {
if (Matter.Query.collides(this, [player]).length) {
this.isReady = false
this.constraint.stiffness = 0
this.constraint.damping = 0 //0.3
this.frictionAir = 0
Matter.Body.setVelocity(this, {
x: 0,
y: 0
});
//show graphically being ready?
}
} else {
if (this.isResetting) {
this.constraint.stiffness += 0.0005
if (this.constraint.stiffness > 0.1) {
this.isResetting = false
this.isReady = true
}
} else {
if (Vector.magnitudeSquared(Vector.sub(this.position, {
x: x,
y: y
})) < distance * distance) {
this.force.y -= force * this.mass
} else {
this.constraint.damping = 1
this.frictionAir = 1
this.isResetting = true
Matter.Body.setVelocity(this, {
x: 0,
y: 0
});
}
}
}
}
});
who.constraint = Constraint.create({
pointA: {
x: who.position.x,
y: who.position.y
},
bodyB: who,
stiffness: 1,
damping: 1
});
Composite.add(engine.world, who.constraint);
return who
},
// rotor(x, y, rotate = 0, radius = 800, width = 40, density = 0.0005) {
// const rotor1 = Matter.Bodies.rectangle(x, y, width, radius, {
// density: density,
// isNotHoldable: true,
// isNonStick: true,
// collisionFilter: {
// category: cat.map,
// mask: cat.player | cat.body | cat.bullet | cat.powerUp | cat.mob | cat.mobBullet
// },
// });
// const rotor2 = Matter.Bodies.rectangle(x, y, width, radius, {
// angle: Math.PI / 2,
// density: density,
// isNotHoldable: true,
// isNonStick: true,
// collisionFilter: {
// category: cat.map,
// mask: cat.player | cat.body | cat.bullet | cat.powerUp | cat.mob | cat.mobBullet
// },
// });
// rotor = Body.create({ //combine rotor1 and rotor2
// parts: [rotor1, rotor2],
// restitution: 0,
// collisionFilter: {
// category: cat.map,
// mask: cat.player | cat.body | cat.bullet | cat.powerUp | cat.mob | cat.mobBullet
// },
// });
// Matter.Body.setPosition(rotor, {
// x: x,
// y: y
// });
// Composite.add(engine.world, [rotor]);
// body[body.length] = rotor1
// body[body.length] = rotor2
// // setTimeout(function() {
// // rotor.collisionFilter.category = cat.body;
// // rotor.collisionFilter.mask = cat.body | cat.player | cat.bullet | cat.mob | cat.mobBullet //| cat.map
// // }, 1000);
// const constraint = Constraint.create({ //fix rotor in place, but allow rotation
// pointA: {
// x: x,
// y: y
// },
// bodyB: rotor
// });
// Composite.add(engine.world, constraint);
// if (rotate) {
// rotor.rotate = function() {
// if (!m.isBodiesAsleep) {
// Matter.Body.applyForce(rotor, {
// x: rotor.position.x + 100,
// y: rotor.position.y + 100
// }, {
// x: rotate * rotor.mass,
// y: 0
// })
// } else {
// Matter.Body.setAngularVelocity(rotor, 0);
// }
// }
// }
// composite[composite.length] = rotor
// return rotor
// },
toggle(x, y, isOn = false, isLockOn = false) {
spawn.mapVertex(x + 65, y + 2, "70 10 -70 10 -40 -10 40 -10"); //toggle platform
map[map.length - 1].restitution = 0;
map[map.length - 1].friction = 1; //
map[map.length - 1].frictionStatic = 1;
const width = 120
const height = 15
body[body.length] = Bodies.rectangle(x + width / 2, y + height / 2, width, height, { friction: 0.05, frictionAir: 0.01 });
let flip = body[body.length - 1];
flip.collisionFilter.category = cat.body
flip.collisionFilter.mask = cat.player | cat.body
flip.isNotHoldable = true
flip.restitution = 0
Matter.Body.setDensity(flip, 0.003)
if (isOn) {
Matter.Body.setAngle(flip, (0.25 - 0.5) * Math.PI)
} else {
Matter.Body.setAngle(flip, (-0.25 - 0.5) * Math.PI)
}
cons[cons.length] = Constraint.create({
pointA: {
x: x + 65,
y: y - 5
},
bodyB: flip,
stiffness: 1,
length: 0
});
Composite.add(engine.world, [cons[cons.length - 1]]);
Composite.add(engine.world, flip); //add to world
flip.classType = "body"
return {
flip: flip,
isOn: isOn,
query() {
const limit = {
right: (-0.25 - 0.5) * Math.PI,
left: (0.25 - 0.5) * Math.PI
}
if (flip.angle < limit.right) {
Matter.Body.setAngle(flip, limit.right)
Matter.Body.setAngularVelocity(flip, 0);
if (!isLockOn) this.isOn = false
} else if (flip.angle > limit.left) {
Matter.Body.setAngle(flip, limit.left)
Matter.Body.setAngularVelocity(flip, 0);
this.isOn = true
}
if (this.isOn) {
ctx.beginPath();
ctx.moveTo(flip.vertices[0].x, flip.vertices[0].y);
for (let j = 1; j < flip.vertices.length; j++) {
ctx.lineTo(flip.vertices[j].x, flip.vertices[j].y);
}
ctx.lineTo(flip.vertices[0].x, flip.vertices[0].y);
ctx.fillStyle = "#3df"
ctx.fill();
ctx.lineWidth = 1;
ctx.strokeStyle = color.blockS;
ctx.stroke();
}
},
}
},
button(x, y, width = 126, isSpawnBase = true) {
if (isSpawnBase) {
spawn.mapVertex(x + 65, y + 2, "100 10 -100 10 -70 -10 70 -10");
map[map.length - 1].restitution = 0;
map[map.length - 1].friction = 1;
map[map.length - 1].frictionStatic = 1;
}
// const buttonSensor = Bodies.rectangle(x + 35, y - 1, 70, 20, {
// isSensor: true
// });
return {
isUp: false,
min: {
x: x + 2,
y: y - 11
},
max: {
x: x + width,
y: y - 10
},
width: width,
height: 20,
query() {
if (Matter.Query.region(body, this).length === 0 && Matter.Query.region([player], this).length === 0) {
this.isUp = true;
} else {
if (this.isUp === true) {
const list = Matter.Query.region(body, this) //are any blocks colliding with this
if (list.length > 0) {
if (list[0].bounds.max.x - list[0].bounds.min.x < 150 && list[0].bounds.max.y - list[0].bounds.min.y < 150) { //not too big of a block
Matter.Body.setPosition(list[0], { //teleport block to the center of the button
x: this.min.x + width / 2,
y: list[0].position.y
})
}
Matter.Body.setVelocity(list[0], {
x: 0,
y: 0
});
}
}
this.isUp = false;
}
},
query() {
if (Matter.Query.region(body, this).length === 0 && Matter.Query.region([player], this).length === 0) {
this.isUp = true;
} else {
if (this.isUp === true) {
const list = Matter.Query.region(body, this) //are any blocks colliding with this
if (list.length > 0) {
if (list[0].bounds.max.x - list[0].bounds.min.x < 150 && list[0].bounds.max.y - list[0].bounds.min.y < 150) { //not too big of a block
Matter.Body.setPosition(list[0], { //teleport block to the center of the button
x: this.min.x + width / 2,
y: list[0].position.y
})
}
Matter.Body.setVelocity(list[0], { x: 0, y: 0 });
}
}
this.isUp = false;
}
},
draw() {
ctx.fillStyle = "hsl(0, 100%, 70%)"
if (this.isUp) {
ctx.fillRect(this.min.x, this.min.y - 10, this.width, 20)
} else {
ctx.fillRect(this.min.x, this.min.y - 3, this.width, 25)
}
}
}
},
vanish(x, y, width, height, isVertical = false, hide = {
x: 0,
y: 150
}) {
x = x + width / 2
y = y + height / 2
const vertices = [{
x: x,
y: y,
index: 0,
isInternal: false
}, {
x: x + width,
y: y,
index: 1,
isInternal: false
}, {
x: x + width,
y: y + height,
index: 4,
isInternal: false
}, {
x: x,
y: y + height,
index: 3,
isInternal: false
}]
const block = body[body.length] = Bodies.fromVertices(x, y, vertices, {
// const block = body[body.length] = Bodies.rectangle(x, y, width, height, {
collisionFilter: {
category: cat.map,
mask: cat.player | cat.body | cat.bullet | cat.powerUp | cat.mob | cat.mobBullet
},
inertia: Infinity, //prevents rotation
isNotHoldable: true,
isNonStick: true, //this keep sporangium from sticking
isTouched: false,
fadeTime: 10 + Math.ceil(0.25 * width),
fadeCount: null,
isThere: true,
returnTime: 120,
returnCount: 0,
shrinkVertices(size) {
if (isVertical) {
return [{
x: x,
y: y * size,
index: 0,
isInternal: false
}, {
x: x + width,
y: y * size,
index: 1,
isInternal: false
}, {
x: x + width,
y: (y + height) * size,
index: 4,
isInternal: false
}, {
x: x,
y: (y + height) * size,
index: 3,
isInternal: false
}]
} else {
return [{
x: x * size,
y: y,
index: 0,
isInternal: false
}, {
x: (x + width) * size,
y: y,
index: 1,
isInternal: false
}, {
x: (x + width) * size,
y: y + height,
index: 4,
isInternal: false
}, {
x: x * size,
y: y + height,
index: 3,
isInternal: false
}]
}
},
query() {
if (this.isThere) {
if (this.isTouched) {
if (!m.isBodiesAsleep) {
this.fadeCount--
Matter.Body.setVertices(this, this.shrinkVertices(Math.max(this.fadeCount / this.fadeTime, 0.03)))
}
if (this.fadeCount < 1) {
Matter.Body.setPosition(this, hide)
this.isThere = false
this.isTouched = false
this.collisionFilter.mask = 0 //cat.player | cat.body | cat.bullet | cat.powerUp | cat.mob | cat.mobBullet
this.returnCount = this.returnTime
Matter.Body.setVertices(this, this.shrinkVertices(1))
Matter.Body.setVertices(this, vertices)
}
} else if (Matter.Query.collides(this, [player]).length) { // || (Matter.Query.collides(this, body).length)) {
this.isTouched = true
this.fadeCount = this.fadeTime;
}
} else {
if (!m.isBodiesAsleep) {
this.returnCount--
if (this.returnCount < 1) {
Matter.Body.setPosition(this, {
x: x,
y: y
})
if (Matter.Query.collides(this, [player]).length) { //|| (Matter.Query.collides(this, body).length)) {
Matter.Body.setPosition(this, hide)
this.returnCount = 15
} else {
this.isThere = true
this.collisionFilter.mask = cat.player | cat.mob | cat.body | cat.bullet | cat.powerUp | cat.mobBullet
this.fadeCount = this.fadeTime
//delete any overlapping blocks
const blocks = Matter.Query.collides(this, body)
for (let i = 0; i < blocks.length; i++) {
if (blocks[i].bodyB !== this && blocks[i].bodyB !== m.holdingTarget) { //dont' delete yourself <----- bug here maybe...
Matter.Composite.remove(engine.world, blocks[i].bodyB);
blocks[i].bodyB.isRemoveMeNow = true
for (let i = 1; i < body.length; i++) { //find which index in body array it is and remove from array
if (body[i].isRemoveMeNow) {
body.splice(i, 1);
break
}
}
}
}
//delete any overlapping mobs
// const mobsHits = Matter.Query.collides(this, mob)
// for (let i = 0; i < mobsHits.length; i++) {
// if (mobsHits[i].bodyB !== this && mobsHits[i].bodyB !== m.holdingTarget) { //dont' delete yourself <----- bug here maybe...
// Matter.Composite.remove(engine.world, mobsHits[i].bodyB);
// mobsHits[i].bodyB.isRemoveMeNow = true
// for (let i = 1; i < mob.length; i++) { //find which index in body array it is and remove from array
// if (mob[i].isRemoveMeNow) {
// mob.splice(i, 1);
// break
// }
// }
// }
// }
}
}
}
}
ctx.beginPath();
const v = this.vertices;
ctx.moveTo(v[0].x, v[0].y);
for (let i = 1; i < v.length; ++i) ctx.lineTo(v[i].x, v[i].y);
ctx.lineTo(v[0].x, v[0].y);
ctx.fillStyle = "#586370"
ctx.fill();
// const color = 220 * (1 - this.fadeCount / this.fadeTime)
// ctx.fillStyle = `rgb(${color},220, 200)`
// ctx.fillStyle = `rgba(0,220,200,${this.fadeCount/this.fadeTime+0.05})`
// ctx.strokeStyle = `#bff`
// ctx.stroke();
},
});
Matter.Body.setStatic(block, true); //make static
Composite.add(engine.world, block); //add to world
// who.classType = "body"
if (simulation.isHorizontalFlipped) x *= -1
return block
},
door(x, y, width, height, distance, speed = 1) {
x = x + width / 2
y = y + height / 2
const doorBlock = body[body.length] = Bodies.rectangle(x, y, width, height, {
collisionFilter: {
category: cat.body,//cat.map,
mask: cat.player | cat.body | cat.bullet | cat.powerUp | cat.mob | cat.mobBullet //cat.player | cat.map | cat.body | cat.bullet | cat.powerUp | cat.mob | cat.mobBullet
},
inertia: Infinity, //prevents rotation
isNotHoldable: true,
friction: 1,
frictionStatic: 1,
restitution: 0,
isClosing: false,
openClose() {
if (!m.isBodiesAsleep) {
if (this.isClosing) {
if (this.position.y < y) { //try to close
if ( //if clear of stuff
Matter.Query.collides(this, [player]).length === 0 &&
Matter.Query.collides(this, body).length < 2 &&
Matter.Query.collides(this, mob).length === 0
) {
const position = {
x: this.position.x,
y: this.position.y + speed
}
Matter.Body.setPosition(this, position)
}
}
} else {
if (this.position.y > y - distance) { //try to open
const position = {
x: this.position.x,
y: this.position.y - speed
}
Matter.Body.setPosition(this, position)
}
}
}
},
isClosed() {
return this.position.y > y - 1
},
draw() {
ctx.fillStyle = "#666"
ctx.beginPath();
const v = this.vertices;
ctx.moveTo(v[0].x, v[0].y);
for (let i = 1; i < v.length; ++i) ctx.lineTo(v[i].x, v[i].y);
ctx.lineTo(v[0].x, v[0].y);
ctx.fill();
}
});
Matter.Body.setStatic(doorBlock, true); //make static
Composite.add(engine.world, doorBlock); //add to world
doorBlock.classType = "body"
return doorBlock
},
doorMap(x, y, width, height, distance, speed = 20, addToWorld = true) { //for doors that use line of sight
x = x + width / 2
y = y + height / 2
const door = map[map.length] = Bodies.rectangle(x, y, width, height, {
collisionFilter: {
category: cat.map,
mask: cat.player | cat.body | cat.bullet | cat.powerUp | cat.mob | cat.mobBullet,
// mask: cat.player | cat.body | cat.bullet | cat.powerUp | cat.mob | cat.mobBullet //cat.player | cat.map | cat.body | cat.bullet | cat.powerUp | cat.mob | cat.mobBullet
},
inertia: Infinity, //prevents rotation
isNotHoldable: true,
friction: 1,
frictionStatic: 1,
restitution: 0,
isClosing: false,
openClose(isSetPaths = false) {
if (!m.isBodiesAsleep) {
if (this.isClosing) {
if (this.position.y < y) { //try to close
if ( //if clear of stuff
Matter.Query.collides(this, [player]).length === 0 &&
Matter.Query.collides(this, body).length < 2 &&
Matter.Query.collides(this, mob).length === 0
) {
const position = {
x: this.position.x,
y: this.position.y + speed
}
Matter.Body.setPosition(this, position)
if (isSetPaths) {
simulation.draw.setPaths()
simulation.draw.lineOfSightPrecalculation() //required precalculation for line of sight
}
}
}
} else {
if (this.position.y > y - distance) { //try to open
const position = {
x: this.position.x,
y: this.position.y - speed
}
Matter.Body.setPosition(this, position)
if (isSetPaths) {
simulation.draw.setPaths()
simulation.draw.lineOfSightPrecalculation() //required precalculation for line of sight
}
}
}
}
},
isClosed() {
return this.position.y > y - 1
},
draw() {
ctx.fillStyle = "#666"
ctx.beginPath();
const v = this.vertices;
ctx.moveTo(v[0].x, v[0].y);
for (let i = 1; i < v.length; ++i) ctx.lineTo(v[i].x, v[i].y);
ctx.lineTo(v[0].x, v[0].y);
ctx.fill();
}
});
Matter.Body.setStatic(door, true); //make static
if (addToWorld) Composite.add(engine.world, door); //add to world
door.classType = "map"
return door
},
portal(centerA, angleA, centerB, angleB) {
const width = 50
const height = 150
const mapWidth = 200
const unitA = Matter.Vector.rotate({ x: 1, y: 0 }, angleA)
const unitB = Matter.Vector.rotate({ x: 1, y: 0 }, angleB)
draw = function () {
ctx.beginPath(); //portal
let v = this.vertices;
ctx.moveTo(v[0].x, v[0].y);
for (let i = 1; i < v.length; ++i) ctx.lineTo(v[i].x, v[i].y);
ctx.fillStyle = this.color
ctx.fill();
}
query = function (isRemoveBlocks = false) {
if (Matter.Query.collides(this, [player]).length === 0) { //not touching player
if (player.isInPortal === this) player.isInPortal = null
} else if (player.isInPortal !== this) { //touching player
if (m.buttonCD_jump === m.cycle) player.force.y = 0 // undo a jump right before entering the portal
m.buttonCD_jump = 0 //disable short jumps when letting go of jump key
player.isInPortal = this.portalPair
//teleport
if (this.portalPair.angle % (Math.PI / 2)) { //if left, right up or down
if (m.immuneCycle < m.cycle + m.collisionImmuneCycles) m.immuneCycle = m.cycle + m.collisionImmuneCycles; //player is immune to damage for 30 cycles
Matter.Body.setPosition(player, this.portalPair.portal.position);
} else { //if at some odd angle
if (m.immuneCycle < m.cycle + m.collisionImmuneCycles) m.immuneCycle = m.cycle + m.collisionImmuneCycles; //player is immune to damage for 30 cycles
Matter.Body.setPosition(player, this.portalPair.position);
}
//rotate velocity
let mag
if (this.portalPair.angle !== 0 && this.portalPair.angle !== Math.PI) { //portal that fires the player up
mag = Math.max(10, Math.min(50, player.velocity.y * 0.8)) + 11
} else {
mag = Math.max(6, Math.min(50, Vector.magnitude(player.velocity)))
}
let v = Vector.mult(this.portalPair.unit, mag)
Matter.Body.setVelocity(player, v);
// move bots to player
for (let i = 0; i < bullet.length; i++) {
if (bullet[i].botType) {
// Matter.Body.setPosition(bullet[i], this.portalPair.portal.position);
Matter.Body.setPosition(bullet[i], Vector.add(this.portalPair.portal.position, {
x: 250 * (Math.random() - 0.5),
y: 250 * (Math.random() - 0.5)
}));
Matter.Body.setVelocity(bullet[i], { x: 0, y: 0 });
}
}
if (tech.isHealAttract) { //send heals to next portal
for (let i = 0; i < powerUp.length; i++) {
if (powerUp[i].name === "heal" && Vector.magnitudeSquared(Vector.sub(powerUp[i].position, m.pos)) < 1000000) {
Matter.Body.setPosition(powerUp[i], Vector.add(this.portalPair.portal.position, { x: 500 * (Math.random() - 0.5), y: 500 * (Math.random() - 0.5) }));
}
}
}
}
// if (body.length) {
for (let i = 0, len = body.length; i < len; i++) {
if (body[i] !== m.holdingTarget) {
// body[i].bounds.max.x - body[i].bounds.min.x < 100 && body[i].bounds.max.y - body[i].bounds.min.y < 100
if (Matter.Query.collides(this, [body[i]]).length === 0) {
if (body[i].isInPortal === this) body[i].isInPortal = null
} else if (body[i].isInPortal !== this) { //touching this portal, but for the first time
if (isRemoveBlocks) {
Matter.Composite.remove(engine.world, body[i]);
body.splice(i, 1);
break
}
body[i].isInPortal = this.portalPair
//teleport
if (this.portalPair.angle % (Math.PI / 2)) { //if left, right up or down
Matter.Body.setPosition(body[i], this.portalPair.portal.position);
} else { //if at some odd angle
Matter.Body.setPosition(body[i], this.portalPair.position);
}
//rotate velocity
let mag
if (this.portalPair.angle !== 0 && this.portalPair.angle !== Math.PI) { //portal that fires the player up
mag = Math.max(10, Math.min(50, body[i].velocity.y * 0.8)) + 11
} else {
mag = Math.max(6, Math.min(50, Vector.magnitude(body[i].velocity)))
}
let v = Vector.mult(this.portalPair.unit, mag)
Matter.Body.setVelocity(body[i], v);
}
}
}
// }
//remove block if touching
// if (body.length) {
// touching = Matter.Query.collides(this, body)
// for (let i = 0; i < touching.length; i++) {
// if (touching[i].bodyB !== m.holdingTarget) {
// for (let j = 0, len = body.length; j < len; j++) {
// if (body[j] === touching[i].bodyB) {
// body.splice(j, 1);
// len--
// Matter.Composite.remove(engine.world, touching[i].bodyB);
// break;
// }
// }
// }
// }
// }
// if (touching.length !== 0 && touching[0].bodyB !== m.holdingTarget) {
// if (body.length) {
// for (let i = 0; i < body.length; i++) {
// if (body[i] === touching[0].bodyB) {
// body.splice(i, 1);
// break;
// }
// }
// }
// Matter.Composite.remove(engine.world, touching[0].bodyB);
// }
}
const portalA = composite[composite.length] = Bodies.rectangle(centerA.x, centerA.y, width, height, {
isSensor: true,
angle: angleA,
color: "hsla(197, 100%, 50%,0.7)",
draw: draw,
});
const portalB = composite[composite.length] = Bodies.rectangle(centerB.x, centerB.y, width, height, {
isSensor: true,
angle: angleB,
color: "hsla(29, 100%, 50%, 0.7)",
draw: draw
});
const mapA = composite[composite.length] = Bodies.rectangle(centerA.x - 0.5 * unitA.x * mapWidth, centerA.y - 0.5 * unitA.y * mapWidth, mapWidth, height + 10, {
collisionFilter: {
category: cat.map,
mask: cat.bullet | cat.powerUp | cat.mob | cat.mobBullet //cat.player | cat.map | cat.body | cat.bullet | cat.powerUp | cat.mob | cat.mobBullet
},
unit: unitA,
angle: angleA,
color: color.map,
draw: draw,
query: query,
lastPortalCycle: 0
});
Matter.Body.setStatic(mapA, true); //make static
Composite.add(engine.world, mapA); //add to world
const mapB = composite[composite.length] = Bodies.rectangle(centerB.x - 0.5 * unitB.x * mapWidth, centerB.y - 0.5 * unitB.y * mapWidth, mapWidth, height + 10, {
collisionFilter: {
category: cat.map,
mask: cat.bullet | cat.powerUp | cat.mob | cat.mobBullet //cat.player | cat.map | cat.body | cat.bullet | cat.powerUp | cat.mob | cat.mobBullet
},
unit: unitB,
angle: angleB,
color: color.map,
draw: draw,
query: query,
lastPortalCycle: 0,
});
Matter.Body.setStatic(mapB, true); //make static
Composite.add(engine.world, mapB); //add to world
mapA.portal = portalA
mapB.portal = portalB
mapA.portalPair = mapB
mapB.portalPair = mapA
return [portalA, portalB, mapA, mapB]
},
drip(x, yMin, yMax, period = 100, color = "hsla(160, 100%, 35%, 0.5)") {
return {
x: x,
y: yMin,
period: period,
dropCycle: 0,
speed: 0,
draw() {
if (!m.isBodiesAsleep) {
if (this.dropCycle < simulation.cycle) { //reset
this.dropCycle = simulation.cycle + this.period + Math.floor(40 * Math.random())
this.y = yMin
this.speed = 1
} else { //fall
this.speed += 0.35 //acceleration from gravity
this.y += this.speed
}
}
if (this.y < yMax) { //draw
ctx.fillStyle = color //"hsla(160, 100%, 35%,0.75)"
ctx.beginPath();
ctx.arc(this.x, this.y, 8, 0, 2 * Math.PI);
ctx.fill();
}
}
}
},
isHazardRise: false,
hazard(x, y, width, height, damage = 0.002) {
return {
min: { x: x, y: y },
max: { x: x + width, y: y + height },
width: width,
height: height,
maxHeight: height,
isOn: true,
opticalQuery() {
if (this.isOn) {
//draw
ctx.fillStyle = `hsla(0, 100%, 50%,${0.6 + 0.4 * Math.random()})`
ctx.fillRect(this.min.x, this.min.y, this.width, this.height)
//collision with player
if (this.height > 0 && Matter.Query.region([player], this).length && !(m.isCloak)) {
if (m.immuneCycle < m.cycle) {
m.immuneCycle = m.cycle + m.collisionImmuneCycles;
m.damage(damage)
simulation.drawList.push({ //add dmg to draw queue
x: player.position.x,
y: player.position.y,
radius: damage * 1500,
color: simulation.mobDmgColor,
time: 20
});
}
}
}
},
query() {
if (this.isOn) {
ctx.fillStyle = "hsla(160, 100%, 35%,0.75)"
const offset = 5 * Math.sin(simulation.cycle * 0.015)
ctx.fillRect(this.min.x, this.min.y + offset, this.width, this.height - offset)
if (this.height > 0 && Matter.Query.region([player], this).length) {
if (m.immuneCycle < m.cycle) {
const DRAIN = 0.004 * (tech.isRadioactiveResistance ? 0.25 : 1)
if (m.energy > DRAIN) {
m.energy -= DRAIN
if (tech.isEnergyHealth && m.energy < 0) m.death()
} else {
m.damage(damage * (tech.isRadioactiveResistance ? 0.25 : 1))
}
}
//float
if (player.velocity.y > 5) player.force.y -= 0.95 * player.mass * simulation.g
const slowY = (player.velocity.y > 0) ? Math.max(0.8, 1 - 0.002 * player.velocity.y * player.velocity.y) : Math.max(0.98, 1 - 0.001 * Math.abs(player.velocity.y)) //down : up
Matter.Body.setVelocity(player, {
x: Math.max(0.95, 1 - 0.036 * Math.abs(player.velocity.x)) * player.velocity.x,
y: slowY * player.velocity.y
});
//undo 1/2 of gravity
player.force.y -= 0.5 * player.mass * simulation.g;
}
//float power ups
powerUpCollide = Matter.Query.region(powerUp, this)
for (let i = 0, len = powerUpCollide.length; i < len; i++) {
const diameter = 2 * powerUpCollide[i].size
const buoyancy = 1 - 0.2 * Math.max(0, Math.min(diameter, this.min.y - powerUpCollide[i].position.y + powerUpCollide[i].size)) / diameter
powerUpCollide[i].force.y -= buoyancy * 1.24 * powerUpCollide[i].mass * simulation.g;
Matter.Body.setVelocity(powerUpCollide[i], {
x: powerUpCollide[i].velocity.x,
y: 0.97 * powerUpCollide[i].velocity.y
});
}
}
},
// draw() {
// if (this.isOn) {
// ctx.fillStyle = color
// ctx.fillRect(this.min.x, this.min.y, this.width, this.height)
// }
// },
levelRise(growRate = 1) {
if (this.height < this.maxHeight && !m.isBodiesAsleep) {
this.height += growRate
this.min.y -= growRate
this.max.y = this.min.y + this.height
}
},
levelFall(fallRate = 1) {
if (this.height > 0 && !m.isBodiesAsleep) {
this.height -= fallRate
this.min.y += fallRate
this.max.y = this.min.y + this.height
}
},
level(isFill, growSpeed = 1) {
if (!m.isBodiesAsleep) {
if (isFill) {
if (this.height < this.maxHeight) {
this.height += growSpeed
this.min.y -= growSpeed
this.max.y = this.min.y + this.height
}
} else if (this.height > 0) {
this.height -= growSpeed
this.min.y += growSpeed
this.max.y = this.min.y + this.height
}
}
}
}
},
mover(x, y, width, height, VxGoal = -6, force = VxGoal > 0 ? 0.0005 : -0.0005) {
//VxGoal below 3 don't move well, maybe try adjusting the force
x = x + width / 2
y = y + height / 2
const rect = map[map.length] = Bodies.rectangle(x, y, width, height, {
collisionFilter: {
category: cat.map,
mask: cat.player | cat.body | cat.bullet | cat.powerUp | cat.mob | cat.mobBullet //cat.player | cat.map | cat.body | cat.bullet | cat.powerUp | cat.mob | cat.mobBullet
},
inertia: Infinity, //prevents rotation
isNotHoldable: true,
friction: 0,
frictionStatic: 0,
restitution: 0,
isClosing: false,
isMover: true,
VxGoal: VxGoal,
force: force,
push() {
if (!m.isBodiesAsleep) {
const touchingPlayer = Matter.Query.collides(this, [jumpSensor])
if (touchingPlayer.length) {
m.moverX = this.VxGoal
if ((this.VxGoal > 0 && player.velocity.x < this.VxGoal) || (this.VxGoal < 0 && player.velocity.x > this.VxGoal)) {
player.force.x += this.force * player.mass
}
m.Vx = player.velocity.x - this.VxGoal
}
let pushBlock = (who) => {
if (!who.isMover) {
if ((this.VxGoal > 0 && who.velocity.x < this.VxGoal) || (this.VxGoal < 0 && who.velocity.x > this.VxGoal)) {
who.force.x += this.force * who.mass
}
const stoppingFriction = 0.5
Matter.Body.setVelocity(who, { x: this.VxGoal * (1 - stoppingFriction) + who.velocity.x * stoppingFriction, y: who.velocity.y });
Matter.Body.setAngularVelocity(who, who.angularVelocity * 0.9)
}
}
const blocks = Matter.Query.collides(this, body)
for (let i = 0; i < blocks.length; i++) {
pushBlock(blocks[i].bodyA)
pushBlock(blocks[i].bodyB)
}
const mobTargets = Matter.Query.collides(this, mob)
for (let i = 0; i < mobTargets.length; i++) {
pushBlock(mobTargets[i].bodyA)
pushBlock(mobTargets[i].bodyB)
}
let pushPowerUp = (who) => {
if (!who.isMover) {
if ((this.VxGoal > 0 && who.velocity.x < this.VxGoal) || (this.VxGoal < 0 && who.velocity.x > this.VxGoal)) {
who.force.x += 2 * this.force * who.mass
}
const stoppingFriction = 0.5
Matter.Body.setVelocity(who, { x: this.VxGoal * (1 - stoppingFriction) + who.velocity.x * stoppingFriction, y: who.velocity.y });
}
}
const powers = Matter.Query.collides(this, powerUp)
for (let i = 0; i < powers.length; i++) {
pushPowerUp(powers[i].bodyA)
pushPowerUp(powers[i].bodyB)
}
}
},
draw() {
ctx.beginPath();
const v = this.vertices;
ctx.moveTo(v[0].x + 2, v[0].y);
// for (let i = 1; i < v.length; ++i) ctx.lineTo(v[i].x, v[i].y);
ctx.lineTo(v[1].x - 2, v[1].y);
ctx.strokeStyle = "#000"
ctx.lineWidth = 4;
ctx.setLineDash([40, 40]);
ctx.lineDashOffset = (-simulation.cycle * this.VxGoal) % 80;
ctx.stroke();
ctx.setLineDash([0, 0]);
}
});
Matter.Body.setStatic(rect, true); //make static
return rect
},
transport(x, y, width, height, VxGoal = -6, force = VxGoal > 0 ? 0.0005 : -0.0005) {
//horizontal moving platform
//VxGoal below 3 don't move well, maybe try adjusting the force
x = x + width / 2
y = y + height / 2
const rect = body[body.length] = Bodies.rectangle(x, y, width, height, {
collisionFilter: {
category: cat.body,
mask: cat.player | cat.body | cat.bullet | cat.powerUp | cat.mob | cat.mobBullet //cat.player | cat.map | cat.body | cat.bullet | cat.powerUp | cat.mob | cat.mobBullet
},
inertia: Infinity, //prevents rotation
isNotHoldable: true,
friction: 0,
frictionStatic: 0,
restitution: 0,
isClosing: false,
isMover: true,
VxGoal: VxGoal,
force: force,
move() {
if (!m.isBodiesAsleep) {
Matter.Body.setPosition(this, { x: this.position.x + this.VxGoal, y: this.position.y }); //horizontal movement
const touchingPlayer = Matter.Query.collides(this, [jumpSensor])
if (touchingPlayer.length) {
m.moverX = this.VxGoal
if ((this.VxGoal > 0 && player.velocity.x < this.VxGoal) || (this.VxGoal < 0 && player.velocity.x > this.VxGoal)) {
player.force.x += this.force * player.mass
}
m.Vx = player.velocity.x - this.VxGoal
}
let pushBlock = (who) => {
if (!who.isMover) {
if ((this.VxGoal > 0 && who.velocity.x < this.VxGoal) || (this.VxGoal < 0 && who.velocity.x > this.VxGoal)) {
who.force.x += this.force * who.mass
}
const stoppingFriction = 0.5
Matter.Body.setVelocity(who, { x: this.VxGoal * (1 - stoppingFriction) + who.velocity.x * stoppingFriction, y: who.velocity.y });
Matter.Body.setAngularVelocity(who, who.angularVelocity * 0.8)
}
}
const blocks = Matter.Query.collides(this, body)
for (let i = 0; i < blocks.length; i++) {
pushBlock(blocks[i].bodyA)
pushBlock(blocks[i].bodyB)
}
}
},
draw() {
ctx.beginPath();
const v = this.vertices;
ctx.moveTo(v[0].x, v[0].y);
for (let i = 1; i < v.length; ++i) ctx.lineTo(v[i].x, v[i].y);
ctx.lineTo(v[0].x, v[0].y);
ctx.fillStyle = "#586370"
ctx.fill();
},
changeDirection(isRight) {
if (isRight) {
this.VxGoal = Math.abs(this.VxGoal)
this.force = Math.abs(this.force)
if (Matter.Query.collides(this, [jumpSensor]).length) player.force.x += this.trainKickPlayer * this.force * player.mass
} else {
this.VxGoal = -Math.abs(this.VxGoal)
this.force = -Math.abs(this.force)
if (Matter.Query.collides(this, [jumpSensor]).length) player.force.x += this.trainKickPlayer * this.force * player.mass
}
},
trainSpeed: Math.abs(VxGoal),
trainKickPlayer: 12 * Math.abs(force),
isSensing: false,
stops: { left: x, right: x + 1000 }, //this should probably be reset in the level code for the actual train stops
trainStop() {
if (this.isMoving) {
this.move();
//oscillate back and forth
if (this.position.x < this.stops.left) {//stop
this.VxGoal = this.trainSpeed
this.force = 0.0005
this.isMoving = false
this.isSensing = false
if (Matter.Query.collides(this, [jumpSensor]).length) player.force.x += this.trainKickPlayer * player.mass * (this.VxGoal > 0 ? 1 : -1)//give player a kick so they don't fall off
} else if (this.position.x > this.stops.right) {//stop
this.VxGoal = -this.trainSpeed
this.force = -0.0005
this.isMoving = false
this.isSensing = false
if (Matter.Query.collides(this, [jumpSensor]).length) player.force.x += this.trainKickPlayer * player.mass * (this.VxGoal > 0 ? 1 : -1)//give player a kick so they don't fall off
}
} else if (this.isSensing) {
if (Matter.Query.collides(this, [jumpSensor]).length) {
this.isMoving = true
this.move(); //needs to move out of the stop range
// if (Matter.Query.collides(this, [jumpSensor]).length) player.force.x += trainKickPlayer * player.mass * (this.VxGoal > 0 ? 1 : -1)//give player a kick so they don't fall off
if (Matter.Query.collides(this, [jumpSensor]).length) {
Matter.Body.setVelocity(player, { x: this.VxGoal, y: player.velocity.y });
}
} else if (this.position.x > this.stops.right && player.position.x < this.stops.left + 500) {//head to other stop if the player is far away
this.changeDirection(false) //go left
this.isMoving = true
this.move(); //needs to move out of the stop range
} else if (this.position.x < this.stops.left && player.position.x > this.stops.right - 500) {//head to other stop if the player is far away
this.changeDirection(true) //go right
this.isMoving = true
this.move(); //needs to move out of the stop range
}
} else if (!Matter.Query.collides(this, [jumpSensor]).length) {//wait until player is off the train to start sensing
this.isSensing = true
}
},
});
Matter.Body.setStatic(rect, true); //make static
Composite.add(engine.world, rect); //add to world
rect.classType = "body"
return rect
},
chain(x, y, angle = 0, isAttached = true, len = 15, radius = 20, stiffness = 1, damping = 1) {
const gap = 2 * radius
const unit = {
x: Math.cos(angle),
y: Math.sin(angle)
}
for (let i = 0; i < len; i++) {
body[body.length] = Bodies.polygon(x + gap * unit.x * i, y + gap * unit.y * i, 12, radius, {
inertia: Infinity,
isNotHoldable: true
});
const who = body[body.length - 1]
who.collisionFilter.category = cat.body;
who.collisionFilter.mask = cat.player | cat.map | cat.body | cat.bullet | cat.mob | cat.mobBullet
Composite.add(engine.world, who); //add to world
who.classType = "body"
}
for (let i = 1; i < len; i++) { //attach blocks to each other
consBB[consBB.length] = Constraint.create({
bodyA: body[body.length - i],
bodyB: body[body.length - i - 1],
stiffness: stiffness,
damping: damping
});
Composite.add(engine.world, consBB[consBB.length - 1]);
}
cons[cons.length] = Constraint.create({ //pin first block to a point in space
pointA: {
x: x,
y: y
},
bodyB: body[body.length - len],
stiffness: 1,
damping: damping
});
Composite.add(engine.world, cons[cons.length - 1]);
if (isAttached) {
cons[cons.length] = Constraint.create({ //pin last block to a point in space
pointA: {
x: x + gap * unit.x * (len - 1),
y: y + gap * unit.y * (len - 1)
},
bodyB: body[body.length - 1],
stiffness: 1,
damping: damping
});
Composite.add(engine.world, cons[cons.length - 1]);
}
},
//******************************************************************************************************************
//******************************************************************************************************************
//******************************************************************************************************************
//******************************************************************************************************************
template() {
simulation.enableConstructMode()
level.setPosToSpawn(0, -50); //normal spawn
level.exit.x = 1500;
level.exit.y = -1875;
spawn.mapRect(level.enter.x, level.enter.y + 20, 100, 20); //bump for level entrance
spawn.mapRect(level.exit.x, level.exit.y + 20, 100, 20); //bump for level exit
level.defaultZoom = 1800
simulation.zoomTransition(level.defaultZoom)
document.body.style.backgroundColor = "#d8dadf";
// color.map = "#444" //custom map color
level.custom = () => {
level.exit.drawAndCheck();
level.enter.draw();
};
level.customTopLayer = () => { };
spawn.mapRect(-100, 0, 1000, 100);
// powerUps.spawnStartingPowerUps(1475, -1175);
// spawn.debris(750, -2200, 3700, 16); //16 debris per level
// spawn.bodyRect(1540, -1110, 300, 25, 0.9);
// spawn.randomSmallMob(1300, -70);
// spawn.randomMob(2650, -975, 0.8);
// spawn.randomGroup(1700, -900, 0.4);
// if (simulation.difficulty > 1) spawn.randomLevelBoss(2200, -1300);
// spawn.secondaryBossChance(100, -1500)
powerUps.addResearchToLevel() //needs to run after mobs are spawned
},
testing() {
simulation.enableConstructMode() //tech.giveTech('motion sickness') //used to build maps in testing mode
document.body.style.backgroundColor = "#fff";
// color.map = "#444" //custom map color
// level.difficultyIncrease(14); //hard mode level 7
level.defaultZoom = 1500
simulation.zoomTransition(level.defaultZoom)
const mover = level.mover(2800, -300, 1000, 25); //x,y,width.height,VxGoal,force
const train = level.transport(2900, -500, 500, 25, 8); //x,y,width.height,VxGoal,force
spawn.bodyRect(1900, -550, 50, 50);
const button = level.button(2535, -200)
// spawn.bodyRect(250, -450, 50, 50); //block on button
level.custom = () => {
//oscillate back and forth
if (train.position.x < 2000) {
train.changeDirection(true) //go right
} else if (train.position.x > 4000) {
train.changeDirection(false) //go left
}
if (!button.isUp) train.move();
mover.push();
ctx.fillStyle = "#d4d4d4"
ctx.fillRect(2500, -475, 200, 300)
ctx.fillStyle = "#ddd"
ctx.fillRect(-150, -1000, 6875, 1000);
ctx.fillStyle = "rgba(0,255,255,0.1)";
ctx.fillRect(6400, -550, 300, 350);
level.exit.drawAndCheck();
level.enter.draw();
};
level.customTopLayer = () => {
train.draw()
mover.draw();
button.query();
button.draw();
ctx.fillStyle = "rgba(0,0,0,0.1)"
ctx.fillRect(-150, -650, 900, 250)
};
level.setPosToSpawn(0, -450); //normal spawn
spawn.mapRect(level.enter.x, level.enter.y + 20, 100, 20);
level.exit.x = 6500;
level.exit.y = -230;
spawn.mapRect(-950, 0, 8200, 800); //ground
spawn.mapRect(-950, -1200, 800, 1400); //left wall
spawn.mapRect(-950, -1800, 8200, 800); //roof
spawn.mapRect(-250, -400, 1000, 600); // shelf
spawn.mapRect(-250, -1200, 1000, 550); // shelf roof
// for (let i = 0; i < 10; ++i) powerUps.spawn(550, -800, "ammo", false);
function blockDoor(x, y, blockSize = 58) {
spawn.mapRect(x, y - 290, 40, 60); // door lip
spawn.mapRect(x, y, 40, 50); // door lip
for (let i = 0; i < 4; ++i) spawn.bodyRect(x + 5, y - 260 + i * blockSize, 30, blockSize);
}
spawn.mapRect(2500, -1200, 200, 750); //right wall
spawn.mapRect(2500, -200, 200, 300); //right wall
spawn.mapRect(4500, -1200, 200, 650); //right wall
blockDoor(4585, -310)
spawn.mapRect(4500, -300, 200, 400); //right wall
spawn.mapRect(6400, -1200, 400, 750); //right wall
spawn.mapRect(6400, -200, 400, 300); //right wall
spawn.mapRect(6700, -1800, 800, 2600); //right wall
spawn.mapRect(level.exit.x, level.exit.y + 20, 100, 100); //exit bump
//place to hide
spawn.mapRect(4650, -300, 1150, 50);
spawn.mapRect(5750, -300, 50, 200);
spawn.mapRect(5575, -100, 50, 125);
spawn.mapRect(5300, -275, 50, 175);
spawn.mapRect(5050, -100, 50, 150);
spawn.mapRect(4850, -275, 50, 175);
spawn.mapRect(-950, -3250, 850, 1750);
//roof
spawn.mapRect(-175, -2975, 300, 1425);
spawn.mapRect(75, -2650, 325, 1150);
spawn.mapRect(375, -2225, 250, 650);
spawn.mapRect(4075, -2125, 700, 800);
spawn.mapRect(4450, -2950, 675, 1550);
spawn.mapRect(4875, -3625, 725, 2225);
spawn.mapRect(5525, -4350, 1725, 2925);
spawn.mapRect(7200, -5125, 300, 3900);
//???
// level.difficultyIncrease(3 * 4) //30 is near max on hard //60 is near max on why
// m.addHealth(Infinity)
// spawn.starter(1900, -500, 200) //big boy
// spawn.starter(1900, -500, 100) //big boy
// for (let i = 0; i < 10; ++i) spawn.launcher(1900, -500)
// spawn.suckerBoss(1900, -500)
// spawn.launcherBoss(3200, -500)
// spawn.laserTargetingBoss(1700, -500)
// spawn.powerUpBoss(1900, -500)
// spawn.powerUpBossBaby(3200, -500)
// spawn.dragonFlyBoss(1700, -500)
// spawn.streamBoss(3200, -500)
// spawn.pulsarBoss(1700, -500)
// spawn.spawnerBossCulture(3200, -500)
// spawn.grenadierBoss(1700, -500)
// spawn.growBossCulture(3200, -500)
// spawn.blinkBoss(1700, -500)
// spawn.snakeSpitBoss(3200, -500)
// spawn.laserBombingBoss(1700, -500)
// spawn.launcherBoss(3200, -500)
// spawn.blockBoss(1700, -500)
// spawn.blinkBoss(3200, -500)
// spawn.spiderBoss(1700, -500)
// spawn.tetherBoss(1700, -500) //go to actual level?
// spawn.revolutionBoss(1900, -500)
// spawn.bomberBoss(1400, -500)
// spawn.cellBossCulture(1600, -500)
// spawn.shieldingBoss(1700, -500)
// for (let i = 0; i < 10; ++i) spawn.bodyRect(1600 + 5, -500, 30, 40);
// for (let i = 0; i < 4; i++) spawn.starter(1900, -500)
// spawn.pulsar(1900, -500)
// spawn.shield(mob[mob.length - 1], 1900, -500, 1);
// mob[mob.length - 1].isShielded = true
// spawn.nodeGroup(1200, 0, "grenadier")
// spawn.blinkBoss(1200, -500)
// spawn.suckerBoss(2900, -500)
// spawn.randomMob(1600, -500)
},
null() {
level.levels.pop(); //remove lore level from rotation
// level.onLevel--
// console.log(level.onLevel, level.levels)
//start a conversation based on the number of conversations seen
if (localSettings.loreCount > lore.conversation.length - 1) localSettings.loreCount = lore.conversation.length - 1; //repeat final conversation if lore count is too high
if (!simulation.isCheating && localSettings.loreCount < lore.conversation.length) {
tech.isNoDraftPause = true //disable pause
lore.testSpeechAPI() //see if speech is working
lore.chapter = localSettings.loreCount //set the chapter to listen to to be the lore level (you can't use the lore level because it changes during conversations)
lore.sentence = 0 //what part of the conversation to start on
lore.conversation[lore.chapter][lore.sentence]()
localSettings.loreCount++ //hear the next conversation next time you win
if (localSettings.isAllowed) localStorage.setItem("localSettings", JSON.stringify(localSettings)); //update local storage
}
// const hazardSlime = level.hazard(-1800, 150, 3600, 650, 0.004, "hsla(160, 100%, 35%,0.75)")
level.isHazardRise = false //this is set to true to make the slime rise up
const hazardSlime = level.hazard(-1800, -800, 3600, 1600, 0.004)
hazardSlime.height -= 950
hazardSlime.min.y += 950
hazardSlime.max.y = hazardSlime.min.y + hazardSlime.height
const circle = {
x: 0,
y: -500,
radius: 50
}
level.custom = () => {
//draw wide line
ctx.beginPath();
ctx.moveTo(circle.x, -800)
ctx.lineTo(circle.x, circle.y)
ctx.lineWidth = 40;
ctx.strokeStyle = lore.talkingColor //"#d5dddd" //"#bcc";
ctx.globalAlpha = 0.03;
ctx.stroke();
ctx.globalAlpha = 1;
//support pillar
ctx.fillStyle = "rgba(0,0,0,0.2)";
ctx.fillRect(-25, 0, 50, 1000);
//draw circles
ctx.beginPath();
ctx.arc(circle.x, circle.y, circle.radius, 0, 2 * Math.PI);
ctx.fillStyle = "#bcc"
ctx.fill();
ctx.lineWidth = 2;
ctx.strokeStyle = "#abb";
ctx.stroke();
ctx.beginPath();
ctx.arc(circle.x, circle.y, circle.radius / 8, 0, 2 * Math.PI);
ctx.fillStyle = lore.talkingColor //"#dff"
ctx.fill();
// level.enter.draw();
};
let sway = {
x: 0,
y: 0
}
let phase = -Math.PI / 2
level.customTopLayer = () => {
ctx.fillStyle = "rgba(0,0,0,0.1)";
ctx.fillRect(-1950, -950, 3900, 1900);
//draw center circle lines
ctx.beginPath();
const step = Math.PI / 20
const horizontalStep = 85
if (simulation.isCheating) phase += 0.3 * Math.random() * Math.random() //(m.pos.x - circle.x) * 0.0005 //0.05 * Math.sin(simulation.cycle * 0.030)
// const sway = 5 * Math.cos(simulation.cycle * 0.007)
sway.x = sway.x * 0.995 + 0.005 * (m.pos.x - circle.x) * 0.05 //+ 0.04 * Math.cos(simulation.cycle * 0.01)
sway.y = 2.5 * Math.sin(simulation.cycle * 0.015)
for (let i = -19.5; i < 20; i++) {
const where = {
x: circle.x + circle.radius * Math.cos(i * step + phase),
y: circle.y + circle.radius * Math.sin(i * step + phase)
}
ctx.moveTo(where.x, where.y);
ctx.bezierCurveTo(sway.x * Math.abs(i) + where.x, where.y + 25 * Math.abs(i) + 60 + sway.y * Math.sqrt(Math.abs(i)),
sway.x * Math.abs(i) + where.x + horizontalStep * i, where.y + 25 * Math.abs(i) + 60 + sway.y * Math.sqrt(Math.abs(i)),
horizontalStep * i, -800);
}
ctx.lineWidth = 0.5;
ctx.strokeStyle = "#899";
ctx.stroke();
hazardSlime.query();
if (level.isHazardRise) hazardSlime.level(true)
//draw wires
// ctx.beginPath();
// ctx.moveTo(-500, -800);
// ctx.quadraticCurveTo(-800, -100, -1800, -375);
// ctx.moveTo(-600, -800);
// ctx.quadraticCurveTo(-800, -200, -1800, -325);
// ctx.lineWidth = 1;
// ctx.strokeStyle = "#9aa";
// ctx.stroke();
};
level.setPosToSpawn(0, -50); //normal spawn
spawn.mapRect(level.enter.x, level.enter.y + 25, 100, 10);
level.exit.x = 0;
level.exit.y = 40000;
level.defaultZoom = 1000
simulation.zoomTransition(level.defaultZoom)
// document.body.style.backgroundColor = "#aaa";
document.body.style.backgroundColor = "#ddd";
color.map = "#586363" //808f8f"
spawn.mapRect(-3000, 800, 5000, 1200); //bottom
spawn.mapRect(-2000, -2000, 5000, 1200); //ceiling
spawn.mapRect(-3000, -2000, 1200, 3400); //left
spawn.mapRect(1800, -1400, 1200, 3400); //right
spawn.mapRect(-500, 0, 1000, 50); //center platform
spawn.mapRect(-500, -25, 25, 50); //edge shelf
spawn.mapRect(475, -25, 25, 50); //edge shelf
},
intro() {
// console.log(level.levelsCleared)
if (level.levelsCleared === 0) { //if this is the 1st level of the game
//wait to spawn power ups until unpaused
//power ups don't spawn in experiment mode, so they don't get removed at the start of experiment mode
const goal = simulation.cycle + 10
function cycle() {
if (simulation.cycle > goal) {
if (localSettings.loreCount === 6) {
powerUps.spawn(2095 + 15 * (Math.random() - 0.5), -2170, "field", false);
} else {
powerUps.spawnStartingPowerUps(2095 + 15 * (Math.random() - 0.5), -2070 - 125);
}
if (simulation.difficultyMode < 5) {
powerUps.spawn(2095 + 15 * (Math.random() - 0.5), -2070 - 25, "heal", false);
powerUps.spawn(2095 + 15 * (Math.random() - 0.5), -2070 - 75, "heal", false);
powerUps.spawn(2095 + 15 * (Math.random() - 0.5), -2070, "research", false); //not on why difficulty
}
} else {
requestAnimationFrame(cycle);
}
}
requestAnimationFrame(cycle);
if (localSettings.levelsClearedLastGame < 3) {
if (!simulation.isCheating && !m.isShipMode && !build.isExperimentRun) {
spawn.wireFoot();
spawn.wireFootLeft();
spawn.wireKnee();
spawn.wireKneeLeft();
spawn.wireHead();
// for (let i = 0; i < 3; i++) powerUps.spawn(2095, -1220 - 50 * i, "tech", false); //unavailable tech spawns
// spawn.mapRect(2000, -1025, 200, 25);
}
} else if (!build.isExperimentRun) {
simulation.trails()
//bonus power ups for clearing runs in the last game
if (!simulation.isCheating && localSettings.levelsClearedLastGame > 1) {
for (let i = 0; i < localSettings.levelsClearedLastGame / 3; i++) powerUps.spawn(2095 + 2 * Math.random(), -1270 - 50 * i, "tech", false); //spawn a tech for levels cleared in last game
simulation.makeTextLog(`for (let i <span class='color-symbol'>=</span> 0; i <span class='color-symbol'><</span> localSettings.levelsClearedLastGame <span class='color-symbol'>/</span> 3; i<span class='color-symbol'>++</span>)`);
simulation.makeTextLog(`{ powerUps.spawn(m.pos.x, m.pos.y, "tech") <em>//simulation superposition</em>}`);
localSettings.levelsClearedLastGame = 0 //after getting bonus power ups reset run history
if (localSettings.isAllowed) localStorage.setItem("localSettings", JSON.stringify(localSettings)); //update local storage
}
}
spawn.mapRect(2025, 0, 150, 50); //lid to floor hole
} else {
for (let i = 0; i < 60; i++) {
setTimeout(() => {
if (level.levels[level.onLevel] === "intro") spawn.sneaker(2100, -1500 - 50 * i);
}, 2000 + 500 * i);
}
}
const wires = new Path2D() //pre-draw the complex lighting path to save processing
wires.moveTo(-150, -275)
wires.lineTo(80, -275)
wires.lineTo(80, -1000)
wires.moveTo(-150, -265)
wires.lineTo(90, -265)
wires.lineTo(90, -1000)
wires.moveTo(-150, -255)
wires.lineTo(100, -255)
wires.lineTo(100, -1000)
wires.moveTo(-150, -245)
wires.lineTo(1145, -245)
wires.lineTo(1145, 0)
wires.moveTo(-150, -235)
wires.lineTo(1135, -235)
wires.lineTo(1135, 0)
wires.moveTo(-150, -225)
wires.lineTo(1125, -225)
wires.lineTo(1125, 0)
wires.moveTo(-150, -215)
wires.lineTo(460, -215)
wires.lineTo(460, 0)
wires.moveTo(-150, -205)
wires.lineTo(450, -205)
wires.lineTo(450, 0)
wires.moveTo(-150, -195)
wires.lineTo(440, -195)
wires.lineTo(440, 0)
wires.moveTo(1155, 0)
wires.lineTo(1155, -450)
wires.lineTo(1000, -450)
wires.lineTo(1000, -1000)
wires.moveTo(1165, 0)
wires.lineTo(1165, -460)
wires.lineTo(1010, -460)
wires.lineTo(1010, -1000)
wires.moveTo(1175, 0)
wires.lineTo(1175, -470)
wires.lineTo(1020, -470)
wires.lineTo(1020, -1000)
wires.moveTo(1185, 0)
wires.lineTo(1185, -480)
wires.lineTo(1030, -480)
wires.lineTo(1030, -1000)
wires.moveTo(1195, 0)
wires.lineTo(1195, -490)
wires.lineTo(1040, -490)
wires.lineTo(1040, -1000)
wires.moveTo(1625, -1000)
wires.lineTo(1625, 0)
wires.moveTo(1635, -1000)
wires.lineTo(1635, 0)
wires.moveTo(1645, -1000)
wires.lineTo(1645, 0)
wires.moveTo(1655, -1000)
wires.lineTo(1655, 0)
wires.moveTo(1665, -1000)
wires.lineTo(1665, 0)
wires.moveTo(1675, -465)
wires.lineTo(2325, -465)
wires.lineTo(2325, 0)
wires.moveTo(1675, -455)
wires.lineTo(2315, -455)
wires.lineTo(2315, 0)
wires.moveTo(1675, -445)
wires.lineTo(2305, -445)
wires.lineTo(2305, 0)
wires.moveTo(1675, -435)
wires.lineTo(2295, -435)
wires.lineTo(2295, 0)
wires.moveTo(2335, 0)
wires.lineTo(2335, -710)
wires.lineTo(2600, -710)
wires.moveTo(2345, 0)
wires.lineTo(2345, -700)
wires.lineTo(2600, -700)
wires.moveTo(2355, 0)
wires.lineTo(2355, -690)
wires.lineTo(2600, -690)
level.custom = () => {
//push around power ups stuck in the tube wall
if (!(simulation.cycle % 30)) {
for (let i = 0, len = powerUp.length; i < len; i++) {
if (powerUp[i].position.y < -1000) powerUp[i].force.x += 0.01 * (Math.random() - 0.5) * powerUp[i].mass
}
}
//draw binary number
const binary = (localSettings.runCount >>> 0).toString(2)
const height = 20
const width = 8
const yOff = -40 //-580
let xOff = -130 //2622
ctx.strokeStyle = "#bff"
ctx.lineWidth = 1.5;
ctx.beginPath()
for (let i = 0; i < binary.length; i++) {
if (binary[i] === "0") {
ctx.moveTo(xOff, yOff)
ctx.lineTo(xOff, yOff + height)
ctx.lineTo(xOff + width, yOff + height)
ctx.lineTo(xOff + width, yOff)
ctx.lineTo(xOff, yOff)
xOff += 10 + width
} else {
ctx.moveTo(xOff, yOff)
ctx.lineTo(xOff, yOff + height)
xOff += 10
}
}
ctx.stroke();
ctx.beginPath()
ctx.strokeStyle = "#ccc"
ctx.lineWidth = 5;
ctx.stroke(wires);
//squares that look like they keep the wires in place
ctx.beginPath()
ctx.rect(1600, -500, 90, 100)
ctx.rect(-55, -285, 12, 100)
ctx.rect(1100, -497, 8, 54)
ctx.rect(2285, -200, 80, 10)
ctx.rect(1110, -70, 100, 10)
ctx.fillStyle = "#ccc"
ctx.fill()
//power up dispenser
// ctx.beginPath()
// for (let i = 2; i < 10; i++) {
// ctx.moveTo(2000, -100 * i)
// ctx.lineTo(2080, -100 * i)
// }
// ctx.strokeStyle = "#ddd"
// ctx.lineWidth = 5;
// ctx.stroke();
// ctx.beginPath()
// for (let i = 2; i < 10; i++) {
// ctx.arc(2040, -100 * i, 30, 0, 2 * Math.PI);
// ctx.moveTo(2040, -100 * i)
// }
// ctx.fillStyle = "rgba(0,0,0,0.3)"
// ctx.fill()
// ctx.fillStyle = "rgba(240,255,255,0.5)"
// ctx.fillRect(2000, -1000, 80, 700)
//exit room
ctx.fillStyle = "#f2f2f2"
ctx.fillRect(2600, -600, 400, 300)
// level.enter.draw();
level.exit.drawAndCheck();
};
level.customTopLayer = () => {
//exit room glow
ctx.fillStyle = "rgba(0,255,255,0.05)"
ctx.fillRect(2600, -600, 400, 300)
//draw shade for ceiling tech
ctx.fillStyle = "rgba(68, 68, 68,0.95)"
ctx.fillRect(2030, -2800, 150, 1800);
ctx.fillStyle = "rgba(68, 68, 68,0.95)"
ctx.fillRect(2030, 0, 150, 1800);
};
level.setPosToSpawn(460, -100); //normal spawn
// level.enter.x = -1000000; //hide enter graphic for first level by moving to the far left
level.exit.x = 2800;
level.exit.y = -335;
spawn.mapRect(level.exit.x, level.exit.y + 25, 100, 100); //exit bump
simulation.zoomScale = 1000 //1400 is normal
level.defaultZoom = 1600
simulation.zoomTransition(level.defaultZoom, 1)
document.body.style.backgroundColor = "#e1e1e1";
spawn.mapRect(-2750, -2800, 2600, 4600); //left wall
spawn.mapRect(3000, -2800, 2600, 4600); //right wall
// spawn.mapRect(-250, 0, 3600, 1800); //ground
spawn.mapRect(-250, 0, 2300, 1800); //split roof
spawn.mapRect(2150, 0, 1200, 1800); //split roof
spawn.mapRect(2025, -3, 25, 15); //lip on power up chamber
spawn.mapRect(2150, -3, 25, 15); //lip on power up chamber
// spawn.mapRect(-250, -2800, 3600, 1800); //roof
spawn.mapRect(-250, -2800, 2300, 1800); //split roof
map[map.length - 1].friction = 0
map[map.length - 1].frictionStatic = 0
spawn.mapRect(2150, -2800, 1200, 1800); //split roof
map[map.length - 1].friction = 0
map[map.length - 1].frictionStatic = 0
spawn.mapRect(2025, -1010, 25, 13); //lip on power up chamber
spawn.mapRect(2150, -1010, 25, 13); //lip on power up chamber
spawn.mapRect(2600, -300, 500, 500); //exit shelf
spawn.mapRect(2600, -1200, 500, 600); //exit roof
spawn.mapRect(-95, -1100, 80, 110); //wire source
spawn.mapRect(410, -10, 90, 20); //small platform for player
spawn.bodyRect(2425, -120, 70, 50);
spawn.bodyRect(2400, -100, 100, 60);
spawn.bodyRect(2500, -150, 100, 150); //exit step
},
final() {
// color.map = "rgba(0,0,0,0.8)"
const slime = level.hazard(simulation.isHorizontalFlipped ? 150 - 860 : -150, -360, 880, 259) //x, y, width, height, damage = 0.002) {
slime.height -= slime.maxHeight - 150 //start slime at zero
slime.min.y += slime.maxHeight
slime.max.y = slime.min.y + slime.height
level.custom = () => {
level.exit.drawAndCheck();
level.enter.draw();
};
level.customTopLayer = () => {
slime.query();
slime.levelRise(0.1)
ctx.fillStyle = "rgba(0,255,255,0.1)"
ctx.fillRect(5385, -550, 300, 250)
};
level.setPosToSpawn(0, -250); //normal spawn
spawn.mapRect(level.enter.x, level.enter.y + 20, 100, 20);
spawn.mapRect(5500, -330 + 20, 100, 20); //spawn this because the real exit is in the wrong spot
level.exit.x = 0;
level.exit.y = -8000;
level.defaultZoom = 2500
simulation.zoomTransition(level.defaultZoom)
document.body.style.backgroundColor = "#ddd";
for (let i = 0; i < 16; i++) powerUps.spawn(4600 + 40 * i, -30, "ammo");
spawn.mapRect(-1950, 0, 8200, 1800); //ground
spawn.mapRect(-1950, -1500, 1800, 1900); //left wall
spawn.mapRect(-1950, -3300, 8200, 1800); //roof
spawn.mapRect(-250, -200, 1000, 300); // shelf
spawn.mapRect(-250, -1700, 1000, 1250); // shelf roof
spawn.mapRect(705, -210, 25, 50);
spawn.mapRect(725, -220, 25, 50);
spawn.bodyRect(750, -125, 125, 125);
spawn.bodyRect(875, -50, 50, 50);
spawn.mapRect(5400, -1700, 400, 1150); //right wall
spawn.mapRect(5400, -300, 400, 400); //right wall
spawn.mapRect(5700, -3300, 1800, 5100); //right wall
spawn.mapRect(level.exit.x, level.exit.y + 20, 100, 100); //exit bump
spawn.mapRect(5403, -650, 400, 450); //blocking exit
if (mobs.mobDeaths < level.levelsCleared && !simulation.isCheating) { //pacifist run
for (let i = 0; i < 250; i++) spawn.starter(1000 + 4000 * Math.random(), -1500 * Math.random())
} else {
spawn.finalBoss(3000, -750)
}
if (simulation.isHorizontalFlipped) { //flip the map horizontally
level.flipHorizontal(); //only flips map,body,mob,powerUp,cons,consBB, exit
level.setPosToSpawn(0, -250);
level.custom = () => {
level.exit.drawAndCheck();
level.enter.draw();
};
level.customTopLayer = () => {
slime.query();
slime.levelRise(0.1)
ctx.fillStyle = "rgba(0,255,255,0.1)"
ctx.fillRect(-5385 - 300, -550, 300, 250)
};
}
if (mobs.mobDeaths < level.levelsCleared && localSettings.loreCount > 5 && !simulation.isCheating) {
//open door for pacifist run on final lore chapter
if (simulation.isHorizontalFlipped) {
level.exit.x = -5500 - 100;
} else {
level.exit.x = 5500;
}
level.exit.y = -330;
Matter.Composite.remove(engine.world, map[map.length - 1]);
map.splice(map.length - 1, 1);
simulation.draw.setPaths(); //redraw map draw path
level.levels.push("null")
}
},
gauntlet() {
level.custom = () => {
level.exit.drawAndCheck();
level.enter.draw();
};
level.customTopLayer = () => {
ctx.fillStyle = "rgba(0,255,255,0.1)"
ctx.fillRect(6400, -550, 300, 350)
ctx.fillStyle = "rgba(0,0,0,0.1)"
ctx.fillRect(-175, -975, 900, 575)
};
level.setPosToSpawn(0, -475); //normal spawn
spawn.mapRect(level.enter.x, level.enter.y + 20, 100, 20);
level.exit.x = 6500;
level.exit.y = -230;
level.defaultZoom = 1500
simulation.zoomTransition(level.defaultZoom)
document.body.style.backgroundColor = "#ddd";
// spawn.mapRect(-300, -1050, 300, 200);
// Matter.Body.setAngle(map[map.length - 1], -Math.PI / 4)
spawn.mapRect(-950, 0, 8200, 800); //ground
spawn.mapRect(-950, -1200, 800, 1400); //left wall
spawn.mapRect(-950, -1800, 8200, 800); //roof
spawn.mapRect(175, -700, 575, 950);
spawn.mapRect(-250, -425, 600, 650);
spawn.mapRect(-250, -1200, 1000, 250); // shelf roof
powerUps.spawnStartingPowerUps(600, -800);
spawn.blockDoor(710, -710);
spawn.mapRect(2500, -1200, 200, 750); //right wall
spawn.blockDoor(2585, -210)
spawn.mapRect(2500, -200, 200, 300); //right wall
spawn.mapRect(4500, -1200, 200, 750); //right wall
spawn.blockDoor(4585, -210)
spawn.mapRect(4500, -200, 200, 300); //right wall
spawn.mapRect(6400, -1200, 400, 750); //right wall
spawn.mapRect(6400, -200, 400, 300); //right wall
spawn.mapRect(6700, -1800, 800, 2600); //right wall
spawn.mapRect(level.exit.x, level.exit.y + 20, 100, 100); //exit bump
if (mobs.mobDeaths < level.levelsCleared && !simulation.isCheating) { //pacifist run
// spawn.setSpawnList();
spawn.pickList.splice(0, 1);
spawn.pickList.push('starter');
spawn.pickList.splice(0, 1);
spawn.pickList.push('starter');
spawn.starter(1500, -200, 150 + Math.random() * 30);
spawn.nodeGroup(3500, -200, 'starter');
spawn.lineGroup(5000, -200, 'starter');
for (let i = 0; i < 3; ++i) {
if (simulation.difficulty * Math.random() > 15 * i) spawn.nodeGroup(2000 + 500 * (Math.random() - 0.5), -800 + 200 * (Math.random() - 0.5), 'starter');
if (simulation.difficulty * Math.random() > 10 * i) spawn.lineGroup(3500 + 500 * (Math.random() - 0.5), -800 + 200 * (Math.random() - 0.5), 'starter');
if (simulation.difficulty * Math.random() > 7 * i) spawn.nodeGroup(5000 + 500 * (Math.random() - 0.5), -800 + 200 * (Math.random() - 0.5), 'starter');
}
} else {
spawn[spawn.pickList[0]](1500, -200, 150 + Math.random() * 30);
spawn.nodeGroup(3500, -200, spawn.allowedGroupList[Math.floor(Math.random() * spawn.allowedGroupList.length)]);
spawn.lineGroup(5000, -200, spawn.allowedGroupList[Math.floor(Math.random() * spawn.allowedGroupList.length)]);
for (let i = 0; i < 3; ++i) {
if (simulation.difficulty * Math.random() > 15 * i) spawn.randomGroup(2000 + 500 * (Math.random() - 0.5), -800 + 200 * (Math.random() - 0.5), Infinity);
if (simulation.difficulty * Math.random() > 10 * i) spawn.randomGroup(3500 + 500 * (Math.random() - 0.5), -800 + 200 * (Math.random() - 0.5), Infinity);
if (simulation.difficulty * Math.random() > 7 * i) spawn.randomGroup(5000 + 500 * (Math.random() - 0.5), -800 + 200 * (Math.random() - 0.5), Infinity);
}
}
if (simulation.difficulty > 1) {
spawn.randomLevelBoss(5750, -600);
spawn.secondaryBossChance(4125, -350)
}
powerUps.addResearchToLevel() //needs to run after mobs are spawned
if (simulation.isHorizontalFlipped) { //flip the map horizontally
level.flipHorizontal(); //only flips map,body,mob,powerUp,cons,consBB, exit
level.setPosToSpawn(0, -475);
level.custom = () => {
level.exit.drawAndCheck();
level.enter.draw();
};
level.customTopLayer = () => {
ctx.fillStyle = "rgba(0,255,255,0.1)"
ctx.fillRect(-6400 - 300, -550, 300, 350)
ctx.fillStyle = "rgba(0,0,0,0.1)"
ctx.fillRect(175 - 900, -975, 900, 575)
};
}
},
subway() {
// simulation.enableConstructMode() //tech.giveTech('motion sickness') //used to build maps in testing mode
// level.difficultyIncrease(10 * 4);
// m.maxHealth = m.health = 100
// color.map = "#333" //custom map color
document.body.style.backgroundColor = "#e3e3e3"//"#e3e3e3"//color.map//"#333"//"#000"
level.defaultZoom = 1400
simulation.zoomTransition(level.defaultZoom)
level.setPosToSpawn(450 * (Math.random() < 0.5 ? 1 : -1), -300); //normal spawn
// spawn.mapRect(level.enter.x, level.enter.y + 20, 100, 20); //entrance bump disabled for performance
level.exit.x = 0;
level.exit.y = -9000;
// spawn.mapRect(level.exit.x, level.exit.y + 20, 100, 100); //exit bump disabled for performance
const stationWidth = 9000
let stationNumber = 0;
let stationCustom = () => { }
let stationCustomTopLayer = () => { }
const train = []
train.push(level.transport(1475, -200, 500, 25, 30))
train[train.length - 1].isMoving = false
train[train.length - 1].stops = { left: 1725, right: 7225 }
train.push(level.transport(-1475 - 500, -200, 500, 25, -30))
train[train.length - 1].isMoving = false
train[train.length - 1].stops = { left: -7225, right: -1725 }
const stationList = [] //use to randomize station order
for (let i = 1, totalNumberOfStations = 8; i < totalNumberOfStations; ++i) stationList.push(i) //!!!! update station number when you add a new station
shuffle(stationList);
stationList.splice(0, 3); //remove some stations to keep it to 4 stations
stationList.unshift(0) //add index zero to the front of the array
let isExitOpen = false
let gatesOpenRight = -1
let gatesOpenLeft = -1
const infrastructure = (x, isInProgress = true) => {
if (isInProgress) {
spawn.setSpawnList(); //picks a couple mobs types for a themed random mob spawns
function removeAll(array) {
for (let i = 0; i < array.length; ++i) Matter.Composite.remove(engine.world, array[i]);
}
removeAll(map);
map = [];
//remove any powerUp that is too far from player
for (let i = 0; i < powerUp.length; ++i) {
if (Vector.magnitudeSquared(Vector.sub(player.position, powerUp[i].position)) > 9000000) { //remove any powerUp farther then 3000 pixels from player
Matter.Composite.remove(engine.world, powerUp[i]);
powerUp.splice(i--, 1)
}
}
//remove any mob that is too far from player
for (let i = 0; i < mob.length; ++i) {
if (Vector.magnitudeSquared(Vector.sub(player.position, mob[i].position)) > 4000000) { //remove any mob farther then 2000 pixels from player
mob[i].removeConsBB()
mob[i].removeCons()
mob[i].leaveBody = false
mob[i].alive = false
Matter.Composite.remove(engine.world, mob[i]);
mob.splice(i--, 1)
}
}
}
const checkGate = (gate, gateButton) => {
if (gate) { //check status of buttons and gates
gate.isClosing = gateButton.isUp
gate.openClose(true);
if (gateButton.isUp) {
gateButton.query();
if (!gateButton.isUp) {
if (stationNumber > 0) {
if (!isExitOpen && gatesOpenRight < stationNumber) level.newLevelOrPhase() //run some new level tech effects
gatesOpenRight = stationNumber
} else if (stationNumber < 0) {
if (!isExitOpen && gatesOpenLeft > stationNumber) level.newLevelOrPhase() //run some new level tech effects
gatesOpenLeft = stationNumber
} else { //starting station both doors open
gatesOpenLeft = stationNumber
gatesOpenRight = stationNumber
}
if (Math.abs(stationNumber) > 0 && ((Math.abs(stationNumber) + 1) % stationList.length) === 0) {
simulation.makeTextLog(`exit opened`);
isExitOpen = true;
}
}
}
gateButton.draw();
}
}
const stations = [ //update totalNumberOfStations as you add more stations
() => { //empty starting station
if (isExitOpen) {
level.exit.x = x - 50;
level.exit.y = -260;
} else {
var gateButton = level.button(x - 62, -237, 125, false) //x, y, width = 126, isSpawnBase = true
gateButton.isUp = true
if (stationNumber === 0 && gatesOpenRight === -1 && gatesOpenLeft === -1) {
var gateR = level.doorMap(x + 1375, -525, 50, 375, 300, 20, false) //x, y, width, height, distance, speed = 20
var gateL = level.doorMap(x - 1375, -525, 50, 375, 300, 20, false) //x, y, width, height, distance, speed = 20
for (let i = 0; i < 10; ++i) powerUps.chooseRandomPowerUp(x + 800 * (Math.random() - 0.5), -300 - 100 * Math.random())//only spawn heal or ammo once at the first station
}
}
spawn.mapRect(x + -1400, -750, 3375, 100); //roof
spawn.mapRect(x + -1500, -210, 3000, 400);//station floor
// spawn.mapRect(x + -550, -220, 1125, 100); //floor
// spawn.mapRect(x + -475, -230, 975, 150);//floor
spawn.mapVertex(x + 0, -200, "400 0 -400 0 -300 -80 300 -80"); //hexagon but wide
// spawn.mapRect(x + -1350, -550, 50, 150);
// spawn.mapRect(x + 1300, -550, 50, 150);
stationCustom = () => { };
stationCustomTopLayer = () => {
checkGate(gateR, gateButton)
checkGate(gateL, gateButton)
};
},
() => { //portal maze
const buttonsCoords = [{ x: x + 50, y: -1595 }, { x: x + 637, y: -2195 }, { x: x - 1487, y: -2145 }]
const buttonsCoordsIndex = Math.floor(Math.random() * buttonsCoords.length) //pick a random element from the array
if (isExitOpen) {
level.exit.x = buttonsCoords[buttonsCoordsIndex].x;
level.exit.y = buttonsCoords[buttonsCoordsIndex].y - 25;
} else {
var gateButton = level.button(buttonsCoords[buttonsCoordsIndex].x, buttonsCoords[buttonsCoordsIndex].y, 126, false) //x, y, width = 126, isSpawnBase = true
gateButton.isUp = true
if (stationNumber > gatesOpenRight) {
var gate = level.doorMap(x + 1375, -525, 50, 375, 300, 20, false) //x, y, width, height, distance, speed = 20
} else if (stationNumber < gatesOpenLeft) {
var gate = level.doorMap(x - 1375, -525, 50, 375, 300, 20, false) //x, y, width, height, distance, speed = 20
}
}
spawn.mapRect(x + -1500, -210, 3000, 400);//station floor
spawn.mapRect(x + -1775, -1600, 3400, 1100); //center pillar
spawn.mapRect(x + -4100, -3325, 8000, 700); //roof
spawn.mapRect(x + -4100, -3325, 325, 1500);
spawn.mapRect(x + 3500, -3325, 400, 1500);
spawn.mapRect(x + -225, -575, 450, 425); //lower portal blocks
//upper parts
spawn.mapRect(x + -1425, -2400, 1900, 50);
spawn.mapRect(x + -775, -2750, 575, 1045);
spawn.mapRect(x + 475, -1900, 450, 375);
spawn.mapRect(x + 2225, -2300, 125, 350);
spawn.mapRect(x + 2550, -2350, 700, 50);
spawn.mapRect(x + 1375, -2850, 125, 650);
spawn.mapRect(x + 600, -2200, 200, 195);
spawn.mapRect(x + -3500, -2275, 825, 75);
spawn.mapRect(x + -1550, -2150, 250, 250);
spawn.mapRect(x + -2575, -2450, 275, 345);
if (!isExitOpen) {
if (Math.random() < 0.5) {
spawn.randomMob(x + 2850, -2425, 0);
spawn.randomMob(x + 2275, -2425, 0);
spawn.randomMob(x + 2000, -2150, 0);
spawn.randomMob(x + 1650, -2150, 0);
spawn.randomMob(x + 1000, -2475, 0);
spawn.randomMob(x + 725, -2450, 0);
spawn.randomMob(x + 525, -2175, 0);
spawn.randomMob(x + 200, -1950, 0);
spawn.randomMob(x + -25, -1825, 0);
spawn.randomMob(x + -975, -2000, 0);
spawn.randomMob(x + -1500, -2225, 0);
spawn.randomMob(x + 1850, -2125, 0);
spawn.randomMob(x + 225, -1975, 0);
spawn.randomMob(x + 25, -1950, 0);
spawn.randomMob(x + 25, -1950, 0);
} else {
spawn.randomMob(x + 250, -1850, 0);
spawn.randomMob(x + 225, -1950, 0);
spawn.randomMob(x + 125, -2000, 0);
spawn.randomMob(x + 0, -1800, 0);
spawn.randomMob(x + -1725, -2300, 0);
spawn.randomMob(x + -2025, -2175, 0);
spawn.randomMob(x + -2050, -2250, 0);
spawn.randomMob(x + -2000, -2350, 0);
spawn.randomMob(x + -2950, -2400, 0);
spawn.randomMob(x + -2775, -2400, 0);
spawn.randomMob(x + -2425, -2550, 0);
spawn.randomMob(x + 1950, -2225, 0);
spawn.randomMob(x + -2700, -2100, 0);
spawn.randomMob(x + -1925, -2175, 0);
spawn.randomMob(x + -825, -2050, 0);
}
}
const portal1 = level.portal({ x: x - 250, y: -310 }, Math.PI,
{ x: x + -3750, y: -2100 }, 0)
const portal2 = level.portal({ x: x + 250, y: -310 }, 0,
{ x: x + 3475, y: -2100 }, Math.PI)
const portal3 = level.portal({ x: x - 800, y: -2500 }, Math.PI,
{ x: x - 175, y: -2500 }, 0)
const portal4 = level.portal({ x: x + 1275, y: -1700 }, Math.PI,
{ x: x - 1275, y: -1700 }, 0)
stationCustom = () => {
portal1[2].query()
portal1[3].query()
portal2[2].query()
portal2[3].query()
portal3[2].query()
portal3[3].query()
portal4[2].query()
portal4[3].query()
}
stationCustomTopLayer = () => {
checkGate(gate, gateButton)
portal1[0].draw();
portal1[1].draw();
portal1[2].draw();
portal1[3].draw();
portal2[0].draw();
portal2[1].draw();
portal2[2].draw();
portal2[3].draw();
portal3[0].draw();
portal3[1].draw();
portal3[2].draw();
portal3[3].draw();
portal4[0].draw();
portal4[1].draw();
portal4[2].draw();
portal4[3].draw();
}
},
() => { //opening and closing doors
const buttonsCoords = [{ x: x - 800, y: -2245 }, { x: x + 250, y: -870 }, { x: x + 1075, y: -1720 }, { x: x - 1600, y: -1995 }]
const buttonsCoordsIndex = Math.floor(Math.random() * buttonsCoords.length) //pick a random element from the array
if (isExitOpen) {
level.exit.x = buttonsCoords[buttonsCoordsIndex].x;
level.exit.y = buttonsCoords[buttonsCoordsIndex].y - 25;
} else {
var gateButton = level.button(buttonsCoords[buttonsCoordsIndex].x, buttonsCoords[buttonsCoordsIndex].y, 126, false) //x, y, width = 126, isSpawnBase = true
gateButton.isUp = true
if (stationNumber > gatesOpenRight) {
var gate = level.doorMap(x + 1375, -525, 50, 375, 300, 20, false) //x, y, width, height, distance, speed = 20
} else if (stationNumber < gatesOpenLeft) {
var gate = level.doorMap(x - 1375, -525, 50, 375, 300, 20, false) //x, y, width, height, distance, speed = 20
}
}
if (!isExitOpen) {
if (Math.random() < 0.5) {
spawn.randomMob(x + 1125, -650, 0);
spawn.randomMob(x + 150, -950, 0);
spawn.randomMob(x + 100, -975, 0);
spawn.randomMob(x + 75, -975, 0);
spawn.randomMob(x + 275, -1225, 0);
spawn.randomMob(x + 825, -975, 0);
spawn.randomMob(x + -50, -1625, 0);
spawn.randomMob(x + -950, -1550, 0);
spawn.randomMob(x + -975, -1550, 0);
spawn.randomMob(x + -900, -2500, 0);
spawn.randomMob(x + -975, -2550, 0);
spawn.randomMob(x + 675, -1950, 0);
spawn.randomMob(x + 675, -2550, 0);
spawn.randomMob(x + 1225, -1825, 0);
spawn.randomMob(x + -750, -2450, 0);
spawn.randomMob(x + -700, -825, 0);
} else {
spawn.randomMob(x + -675, -675, 0);
spawn.randomMob(x + -575, -925, 0);
spawn.randomMob(x + -425, -1100, 0);
spawn.randomMob(x + -225, -1225, 0);
spawn.randomMob(x + -650, -1250, 0);
spawn.randomMob(x + -675, -775, 0);
spawn.randomMob(x + 75, -1000, 0);
spawn.randomMob(x + -1100, -1575, 0);
spawn.randomMob(x + -1250, -1850, 0);
spawn.randomMob(x + -1625, -2100, 0);
spawn.randomMob(x + -700, -2500, 0);
spawn.randomMob(x + -375, -2550, 0);
spawn.randomMob(x + 250, -2025, 0);
spawn.randomMob(x + 675, -2175, 0);
spawn.randomMob(x + -1000, -2000, 0);
spawn.randomMob(x + -1550, -2325, 0);
spawn.randomMob(x + -1725, -2425, 0);
}
}
spawn.mapRect(x + -1500, -210, 3000, 400);//station floor
spawn.mapRect(x + -2550, -3200, 425, 1375);//roof left wall
spawn.mapRect(x + 2125, -3175, 450, 1375);//roof right wall
spawn.mapRect(x + -2550, -3200, 5125, 225);//roof
spawn.mapRect(x + -1325, -550, 1375, 50);//first floor roof/ground
spawn.mapRect(x + 775, -550, 675, 50);
spawn.mapRect(x + -200, -875, 1300, 50); //2nd floor roof/ground
spawn.mapRect(x + -125, -1125, 50, 275);
spawn.mapRect(x + -125, -1150, 800, 50); //3rd floor roof/ground
spawn.mapRect(x + -1450, -1475, 1600, 50);
spawn.mapRect(x + -1325, -1725, 800, 50); //4th floor roof/ground
spawn.mapRect(x + 50, -1725, 1350, 50);
spawn.mapRect(x + -1125, -2250, 700, 50);
spawn.mapRect(x, -525, 50, 150); //door cap for ground at ground y = -210
const door1 = level.doorMap(x + 12, -380, 25, 170, 140, 20, false) //x, y, width, height, distance, speed = 20
spawn.mapRect(x - 200, -525 - 340, 50, 150); //door cap for ground at ground y = -210
const door2 = level.doorMap(x - 188, -380 - 340, 25, 170, 140, 20, false) //x, y, width, height, distance, speed = 20
spawn.mapRect(x + 100, -525 - 940, 50, 150); //door cap for ground at ground y = -210
const door3 = level.doorMap(x + 112, -380 - 940, 25, 170, 140, 20, false) //x, y, width, height, distance, speed = 20
spawn.mapRect(x + 450, -3050, 50, 775);
const door4 = level.doorMap(x + 462, -2300, 25, 575, 520, 30, false) //x, y, width, height, distance, speed = 20
const portal1 = level.portal({
x: x + 2100,
y: -2100
}, Math.PI, { //right
x: x + -1275,
y: -650
}, 2 * Math.PI) //right
stationCustom = () => {
door1.isClosing = (simulation.cycle % 240) < 120
door1.openClose(true);
door2.isClosing = (simulation.cycle % 240) > 120
door2.openClose(true);
door3.isClosing = (simulation.cycle % 240) < 120
door3.openClose(true);
door4.isClosing = (simulation.cycle % 240) > 120
door4.openClose(true);
portal1[2].query()
portal1[3].query()
}
stationCustomTopLayer = () => {
checkGate(gate, gateButton)
portal1[0].draw();
portal1[1].draw();
portal1[2].draw();
portal1[3].draw();
}
},
() => { //slime
const buttonsCoords = [{ x: x - 675, y: -895 }, { x: x - 750, y: -70 }, { x: x + 75, y: -570 },]
const buttonsCoordsIndex = Math.floor(Math.random() * buttonsCoords.length) //pick a random element from the array
if (isExitOpen) {
level.exit.x = buttonsCoords[buttonsCoordsIndex].x;
level.exit.y = buttonsCoords[buttonsCoordsIndex].y - 25;
} else {
var gateButton = level.button(buttonsCoords[buttonsCoordsIndex].x, buttonsCoords[buttonsCoordsIndex].y, 126, false) //x, y, width = 126, isSpawnBase = true
gateButton.isUp = true
if (stationNumber > gatesOpenRight) {
var gate = level.doorMap(x + 1375, -525, 50, 375, 300, 20, false) //x, y, width, height, distance, speed = 20
} else if (stationNumber < gatesOpenLeft) {
var gate = level.doorMap(x - 1375, -525, 50, 375, 300, 20, false) //x, y, width, height, distance, speed = 20
}
}
spawn.mapRect(x + -1575, -2000, 3025, 100); //roof
// spawn.mapRect(x + -1575, -2200, 3025, 300); //roof
// spawn.mapRect(x + -1500, -210, 3000, 400);//station floor
spawn.mapRect(x + -1500, -210, 500, 350); //station floor left
spawn.mapRect(x + 1000, -210, 500, 350); //station floor right
spawn.mapRect(x + 900, -1250, 125, 1250);
spawn.mapRect(x - 1025, -1550, 125, 1625);
spawn.mapRect(x - 50, -1900, 100, 1500);
spawn.mapRect(x + -975, -1250, 200, 25);
spawn.mapRect(x + -950, -625, 150, 25);
spawn.mapRect(x - 925, -400, 250, 175);
spawn.mapRect(x - 725, -900, 225, 300);
spawn.mapRect(x + 325, -225, 325, 75);
spawn.mapRect(x + 400, -950, 275, 25);
spawn.mapRect(x + 775, -575, 200, 25);
spawn.mapRect(x + 0, -1225, 125, 25);
spawn.mapRect(x + 0, -575, 225, 175);
spawn.mapRect(x - 925, -75, 875, 150);
spawn.mapRect(x + 475, -1400, 75, 1250);
if (!isExitOpen) {
if (Math.random() < 0.5) {
spawn.randomMob(x + -850, -450, 0);
spawn.randomMob(x + -850, -125, 0);
spawn.randomMob(x + -725, -100, 0);
spawn.randomMob(x + 0, -100, 0);
spawn.randomMob(x + 800, -50, 0);
spawn.randomMob(x + 50, -275, 0);
spawn.randomMob(x + -300, -425, 0);
spawn.randomMob(x + -750, -475, 0);
spawn.randomMob(x + -850, -775, 0);
spawn.randomMob(x + -650, -1000, 0);
spawn.randomMob(x + -150, -1325, 0);
spawn.randomMob(x + -825, -1350, 0);
spawn.randomMob(x + -375, -150, 0);
} else {
spawn.randomMob(x + 350, -350, 0);
spawn.randomMob(x + 175, -700, 0);
spawn.randomMob(x + 350, -1175, 0);
spawn.randomMob(x + 200, -1600, 0);
spawn.randomMob(x + 500, -1675, 0);
spawn.randomMob(x + 425, -50, 0);
spawn.randomMob(x + 725, -75, 0);
spawn.randomMob(x + 650, -700, 0);
spawn.randomMob(x + 775, -1150, 0);
spawn.randomMob(x + 500, -1675, 0);
spawn.randomMob(x + -150, -175, 0);
spawn.randomMob(x + -800, -150, 0);
}
}
const boost1 = level.boost(x - 1185, -225, 1400)
const boost2 = level.boost(x + 1100, -225, 1100)
const hazard1 = level.hazard(x - 900, -1225, 1800, 1225)
let isSlimeRiseUp = false
const drip = []
drip.push(level.drip(x - 900 + 1800 * Math.random(), -1900, 0, 100)) // drip(x, yMin, yMax, period = 100, color = "hsla(160, 100%, 35%, 0.5)") {
drip.push(level.drip(x - 900 + 1800 * Math.random(), -1900, 0, 150))
drip.push(level.drip(x - 900 + 1800 * Math.random(), -1900, 0, 70))
// drip.push(level.drip(x - 900 + 1800 * Math.random(), -1900, 0, 210))
// drip.push(level.drip(x - 900 + 1800 * Math.random(), -1900, 0, 67))
stationCustom = () => {
for (let i = 0; i < drip.length; i++) drip[i].draw()
// drip1.draw();
// drip2.draw();
// drip3.draw();
}
stationCustomTopLayer = () => {
checkGate(gate, gateButton)
hazard1.query();
hazard1.level(isSlimeRiseUp, 1.5)
if (!(hazard1.height < hazard1.maxHeight)) {
isSlimeRiseUp = false
} else if (!(hazard1.height > 0)) {
isSlimeRiseUp = true
}
boost1.query();
boost2.query();
}
},
() => { //portal fling
const buttonsCoords = [{ x: x + 775, y: -1695 }, { x: x - 775, y: -800 }, { x: x - 375, y: -2083 },]
const buttonsCoordsIndex = Math.floor(Math.random() * buttonsCoords.length) //pick a random element from the array
if (isExitOpen) {
level.exit.x = buttonsCoords[buttonsCoordsIndex].x;
level.exit.y = buttonsCoords[buttonsCoordsIndex].y - 25;
} else {
var gateButton = level.button(buttonsCoords[buttonsCoordsIndex].x, buttonsCoords[buttonsCoordsIndex].y, 126, false) //x, y, width = 126, isSpawnBase = true
gateButton.isUp = true
if (stationNumber > gatesOpenRight) {
var gate = level.doorMap(x + 1375, -525, 50, 375, 300, 20, false) //x, y, width, height, distance, speed = 20
} else if (stationNumber < gatesOpenLeft) {
var gate = level.doorMap(x - 1375, -525, 50, 375, 300, 20, false) //x, y, width, height, distance, speed = 20
}
}
spawn.mapRect(x + -1600, -3450, 300, 1475); //roof
spawn.mapRect(x + -1600, -3450, 3225, 100);
spawn.mapRect(x + 1300, -3450, 325, 1525);
spawn.mapVertex(x + 400, -180, "-300 0 -300 -100 300 -100 400 0");
spawn.mapVertex(x - 400, -180, "300 0 300 -100 -300 -100 -400 0");
spawn.mapRect(x + -1500, -210, 1425, 350); //station floor left
spawn.mapRect(x + 75, -210, 1425, 350); //station floor right
spawn.mapRect(x + 75, -950, 50, 450);
spawn.mapRect(x + 125, -700, 1225, 200);
spawn.mapRect(x + -1325, -1775, 775, 175);
spawn.mapVertex(x + 445, -800, "-200 0 -200 -300 100 -300 185 0");
spawn.mapVertex(x - 310, -1880, "-185 0 -100 -400 400 -400 400 0");
spawn.mapVertex(x + -675, -725, "325 0 250 80 -250 80 -325 0 -250 -80 250 -80");
spawn.mapRect(x + 625, -1700, 750, 500);
if (!isExitOpen) {
spawn.randomMob(x + -750, -1925, 0);
spawn.randomMob(x + -425, -2300, 0);
spawn.randomMob(x + -350, -2200, 0);
spawn.randomMob(x + -275, -2175, 0);
spawn.randomMob(x + -375, -2175, 0);
spawn.randomMob(x + 1075, -1850, 0);
spawn.randomMob(x + 925, -1775, 0);
spawn.randomMob(x + 1150, -1800, 0);
spawn.randomMob(x + 1400, -2150, 0);
spawn.randomMob(x + 925, -850, 0);
spawn.randomMob(x + 800, -800, 0);
spawn.randomMob(x + 875, -825, 0);
spawn.randomMob(x + 1050, -900, 0);
spawn.randomMob(x + 19050, -2925, 0);
spawn.randomMob(x + 17150, -3150, 0);
spawn.randomMob(x + 17700, -3300, 0);
}
const portal1 = level.portal({
x: x + 0,
y: -200
}, -Math.PI / 2, { //up
x: x + 200,
y: -900
}, -Math.PI / 2) //up
const portal2 = level.portal({
x: x + 1275,
y: -800
}, Math.PI, { //right
x: x + -1275,
y: -1875
}, 2 * Math.PI) //right
stationCustom = () => {
portal1[2].query(true)
portal1[3].query(true)
portal2[2].query()
portal2[3].query()
}
stationCustomTopLayer = () => {
checkGate(gate, gateButton)
portal1[0].draw();
portal1[1].draw();
portal1[2].draw();
portal1[3].draw();
portal2[0].draw();
portal2[1].draw();
portal2[2].draw();
portal2[3].draw();
}
},
() => { //tower levels and squares
const buttonsCoords = [{ x: x - 300, y: -3120 }, { x: x + 600, y: -3020 }, { x: x - 575, y: -1770 }, { x: x - 450, y: -2370 }]
const buttonsCoordsIndex = Math.floor(Math.random() * buttonsCoords.length) //pick a random element from the array
if (isExitOpen) {
level.exit.x = buttonsCoords[buttonsCoordsIndex].x;
level.exit.y = buttonsCoords[buttonsCoordsIndex].y - 25;
} else {
var gateButton = level.button(buttonsCoords[buttonsCoordsIndex].x, buttonsCoords[buttonsCoordsIndex].y, 126, false) //x, y, width = 126, isSpawnBase = true
gateButton.isUp = true
if (stationNumber > gatesOpenRight) {
var gate = level.doorMap(x + 1375, -525, 50, 375, 300, 20, false) //x, y, width, height, distance, speed = 20
} else if (stationNumber < gatesOpenLeft) {
var gate = level.doorMap(x - 1375, -525, 50, 375, 300, 20, false) //x, y, width, height, distance, speed = 20
}
}
spawn.mapRect(x + -1500, -210, 3000, 400);//station floor
spawn.mapRect(x + -1625, -3950, 3225, 350);//roof
spawn.mapRect(x + 1300, -3850, 300, 2150); //roof wall
spawn.mapRect(x + -1625, -3950, 325, 2250); //roof wall
spawn.mapRect(x + -1050, -575, 1000, 75);
spawn.mapRect(x + 175, -575, 975, 75);
spawn.mapRect(x + -1050, -825, 150, 275);
spawn.mapRect(x + -900, -1200, 2275, 75);
spawn.mapRect(x + 125, -1425, 1250, 300);
spawn.mapRect(x + -925, -1775, 2100, 75);
spawn.mapRect(x + -100, -2050, 950, 350);
spawn.mapRect(x + -925, -2100, 100, 400);
spawn.mapRect(x + -700, -2375, 1225, 75);
spawn.mapRect(x + 650, -2375, 575, 75);
spawn.mapRect(x + -25, -2750, 350, 269);
spawn.mapRect(x + -950, -3125, 975, 75);
spawn.mapRect(x + 325, -3025, 900, 75);
spawn.bodyRect(x + -125, -1325, 225, 125, 0.3);
spawn.bodyRect(x + -225, -2100, 300, 50, 0.3);
spawn.bodyRect(x + -225, -2575, 100, 200, 0.3);
spawn.bodyRect(x + 850, -2575, 150, 200, 0.3);
spawn.bodyRect(x + 850, -1875, 75, 100, 0.3);
spawn.bodyRect(x + 500, -725, 175, 150, 0.3);
spawn.bodyRect(x + -925, -2250, 100, 150, 0.3);
spawn.bodyRect(x + -1050, -950, 150, 125, 0.3);
const mobPlacement = [
() => { //1st floor
spawn.randomMob(x + -775, -725, 0);
spawn.randomMob(x + -575, -700, 0);
spawn.randomMob(x + -275, -700, 0);
spawn.randomMob(x + -125, -650, 0);
spawn.randomMob(x + 250, -675, 0);
spawn.randomMob(x + 425, -650, 0);
spawn.randomMob(x + 775, -650, 0);
spawn.randomMob(x + 1050, -675, 0);
spawn.randomMob(x + 675, -950, 0);
spawn.randomMob(x + -625, -900, 0);
spawn.randomMob(x + -750, -1400, 0);
spawn.randomMob(x + -500, -2025, 0);
spawn.randomMob(x + -125, -3225, 0);
},
() => { //2nd floor
spawn.randomMob(x + -950, -925, 0);
spawn.randomMob(x + -775, -1325, 0);
spawn.randomMob(x + -450, -1500, 0);
spawn.randomMob(x + -325, -1250, 0);
spawn.randomMob(x + 0, -1500, 0);
spawn.randomMob(x + 375, -1525, 0);
spawn.randomMob(x + 750, -1550, 0);
spawn.randomMob(x + 1175, -1550, 0);
spawn.randomMob(x + -875, -1350, 0);
spawn.randomMob(x + -875, -2375, 0);
spawn.randomMob(x + 175, -2850, 0);
spawn.randomMob(x + 750, -2475, 0);
},
() => {//3rd floor
spawn.randomMob(x + 1075, -2000, 0);
spawn.randomMob(x + 725, -2125, 0);
spawn.randomMob(x + 350, -2125, 0);
spawn.randomMob(x + -325, -2000, 0);
spawn.randomMob(x + -675, -1875, 0);
spawn.randomMob(x + -725, -2200, 0);
spawn.randomMob(x + -675, -2575, 0);
spawn.randomMob(x + -425, -2675, 0);
spawn.randomMob(x + -50, -2875, 0);
spawn.randomMob(x + 425, -2725, 0);
spawn.randomMob(x + 1150, -2550, 0);
spawn.randomMob(x + 1150, -2175, 0);
spawn.randomMob(x + 1000, -1900, 0);
spawn.randomMob(x + 500, -2550, 0);
spawn.randomMob(x + 125, -2900, 0);
},
() => {//all floors
spawn.randomMob(x + 1000, -850, 0);
spawn.randomMob(x + 300, -850, 0);
spawn.randomMob(x + -450, -825, 0);
spawn.randomMob(x + -1025, -1125, 0);
spawn.randomMob(x + -750, -1375, 0);
spawn.randomMob(x + -225, -1375, 0);
spawn.randomMob(x + 625, -1525, 0);
spawn.randomMob(x + 1025, -1925, 0);
spawn.randomMob(x + -425, -2100, 0);
spawn.randomMob(x + -400, -2650, 0);
spawn.randomMob(x + 150, -3000, 0);
spawn.randomMob(x + 675, -3200, 0);
spawn.randomMob(x + -550, -3300, 0);
},
]
if (!isExitOpen) mobPlacement[Math.floor(Math.random() * mobPlacement.length)]()//different random mob placements, with mobs clustered to surprise player
stationCustom = () => { }
stationCustomTopLayer = () => {
checkGate(gate, gateButton)
}
},
() => { //jump pads and 6 sided platforms
const buttonsCoords = [{ x: x + 275, y: -1817 }, { x: x + 2025, y: -1995 }, { x: x - 2025, y: -2420 }, { x: x - 2100, y: -1995 }]
const buttonsCoordsIndex = Math.floor(Math.random() * buttonsCoords.length) //pick a random element from the array
if (isExitOpen) {
level.exit.x = buttonsCoords[buttonsCoordsIndex].x;
level.exit.y = buttonsCoords[buttonsCoordsIndex].y - 25;
} else {
var gateButton = level.button(buttonsCoords[buttonsCoordsIndex].x, buttonsCoords[buttonsCoordsIndex].y, 126, false) //x, y, width = 126, isSpawnBase = true
gateButton.isUp = true
if (stationNumber > gatesOpenRight) {
var gate = level.doorMap(x + 1375, -525, 50, 375, 300, 20, false) //x, y, width, height, distance, speed = 20
} else if (stationNumber < gatesOpenLeft) {
var gate = level.doorMap(x - 1375, -525, 50, 375, 300, 20, false) //x, y, width, height, distance, speed = 20
}
}
spawn.mapRect(x + -1500, -210, 3000, 400);//station floor
spawn.mapRect(x + -3200, -3200, 300, 1400); //roof left wall
spawn.mapRect(x + 2600, -3200, 300, 1400);//roof right wall
spawn.mapRect(x + -3175, -3200, 6175, 225);//roof
if (Math.random() < 0.3) spawn.mapRect(x + -1350, -550, 150, 50); //wall ledge
if (Math.random() < 0.3) spawn.mapRect(x + 1175, -550, 200, 50); //wall ledge
spawn.mapVertex(x + 600, -900, "490 0 350 80 -350 80 -490 0 -350 -80 350 -80"); //hexagon but wide
spawn.mapVertex(x - 600, -750, "490 0 350 80 -350 80 -490 0 -350 -80 350 -80"); //hexagon but wide
spawn.mapVertex(x - 100, -1850, "-100 -300 0 -350 100 -300 100 300 0 350 -100 300"); //hexagon but tall
spawn.mapVertex(x + -600, -2000, "-150 -450 150 -450 150 450 0 525 -150 450"); //hexagon but big and tall and flat
spawn.mapVertex(x + 350, -1575, "-150 0 150 0 150 450 0 525 -150 450"); //hexagon but tall and flat top
spawn.mapVertex(x + 850, -1575, "-150 0 150 0 150 450 0 525 -150 450"); //hexagon but tall and flat top
spawn.mapVertex(x + -2050, -2350, "490 0 350 80 -350 80 -490 0 -350 -80 350 -80"); //left top corner hexagon but wide
spawn.mapVertex(x + 1700, -2450, "-100 -300 0 -350 100 -300 100 300 0 350 -100 300"); //hexagon but tall
const mobPlacement = [
() => { //rightish
spawn.randomMob(x + 2250, -2375, 0);
spawn.randomMob(x + 1950, -2825, 0);
spawn.randomMob(x + 1275, -2775, 0);
spawn.randomMob(x + 1450, -2200, 0);
spawn.randomMob(x + 825, -1950, 0);
spawn.randomMob(x + 400, -1875, 0);
spawn.randomMob(x + -75, -2275, 0);
spawn.randomMob(x + -650, -2550, 0);
spawn.randomMob(x + 1500, -2075, 0);
spawn.randomMob(x + 2125, -2650, 0);
spawn.randomMob(x + 2075, -2250, 0);
spawn.randomMob(x + 1000, -2850, 0);
spawn.randomMob(x + 750, -950, 0);
spawn.randomMob(x + -750, -1125, 0);
spawn.randomMob(x + -1575, -2050, 0);
},
() => { //leftish
spawn.randomMob(x + -2225, -2125, 0);
spawn.randomMob(x + -2675, -2125, 0);
spawn.randomMob(x + -2600, -2600, 0);
spawn.randomMob(x + -2100, -2725, 0);
spawn.randomMob(x + -1425, -2600, 0);
spawn.randomMob(x + -1375, -2050, 0);
spawn.randomMob(x + -575, -2575, 0);
spawn.randomMob(x + -125, -2300, 0);
spawn.randomMob(x + 350, -1925, 0);
spawn.randomMob(x + -350, -1050, 0);
spawn.randomMob(x + -1000, -1000, 0);
spawn.randomMob(x + -700, -1300, 0);
spawn.randomMob(x + 350, -1150, 0);
spawn.randomMob(x + -575, -2525, 0);
spawn.randomMob(x + -1075, -2525, 0);
},
() => {//centerish
spawn.randomMob(x + 25, -2275, 0);
spawn.randomMob(x + 300, -1975, 0);
spawn.randomMob(x + 700, -1950, 0);
spawn.randomMob(x + 325, -1200, 0);
spawn.randomMob(x + -225, -950, 0);
spawn.randomMob(x + -925, -975, 0);
spawn.randomMob(x + -675, -2575, 0);
spawn.randomMob(x + -1425, -2175, 0);
spawn.randomMob(x + 1575, -2075, 0);
spawn.randomMob(x + 2300, -2075, 0);
spawn.randomMob(x + 425, -1925, 0);
spawn.randomMob(x + 125, -2175, 0);
spawn.randomMob(x + -325, -2150, 0);
spawn.randomMob(x + -350, -950, 0);
spawn.randomMob(x + 600, -325, 0);
spawn.randomMob(x + -600, -375, 0);
},
]
if (!isExitOpen) mobPlacement[Math.floor(Math.random() * mobPlacement.length)]()//different random mob placements, with mobs clustered to surprise player
const boost1 = level.boost(x - 50, -225, 790)
const boost2 = level.boost(x + 550, -985, 900)
const boost3 = level.boost(x + -850, -835, 1900)
stationCustom = () => { }
stationCustomTopLayer = () => {
checkGate(gate, gateButton)
boost1.query();
boost2.query();
boost3.query();
}
},
() => { //crouch tunnels
const buttonsCoords = [{ x: x + 625, y: -1395 }, { x: x - 15, y: -1595 }, { x: x - 800, y: -1295 }]
const buttonsCoordsIndex = Math.floor(Math.random() * buttonsCoords.length) //pick a random element from the array
if (isExitOpen) {
level.exit.x = buttonsCoords[buttonsCoordsIndex].x;
level.exit.y = buttonsCoords[buttonsCoordsIndex].y - 25;
} else {
var gateButton = level.button(buttonsCoords[buttonsCoordsIndex].x, buttonsCoords[buttonsCoordsIndex].y, 126, false) //x, y, width = 126, isSpawnBase = true
gateButton.isUp = true
if (stationNumber > gatesOpenRight) {
var gate = level.doorMap(x + 1375, -525, 50, 375, 300, 20, false) //x, y, width, height, distance, speed = 20
} else if (stationNumber < gatesOpenLeft) {
var gate = level.doorMap(x - 1375, -525, 50, 375, 300, 20, false) //x, y, width, height, distance, speed = 20
}
}
spawn.mapRect(x + -1500, -210, 3000, 400);//station floor
spawn.mapRect(x + -1575, -2200, 3025, 300); //roof
spawn.mapRect(x + -1100, -925, 100, 425);
spawn.mapRect(x + -1100, -575, 375, 75);
spawn.mapRect(x + -925, -1300, 375, 125);
spawn.mapRect(x + -300, -1300, 620, 125);
spawn.mapRect(x + 450, -1400, 500, 225);
spawn.mapRect(x + 900, -550, 500, 50);
spawn.mapRect(x + 950, -925, 400, 270);
spawn.mapRect(x + 1250, -1250, 150, 75);
spawn.mapRect(x + -225, -525, 800, 210);
spawn.mapRect(x + -100, -1600, 300, 193);
spawn.mapRect(x + 925, -1250, 75, 75);
spawn.bodyRect(x + 200, -1475, 75, 175, 0.3);
spawn.bodyRect(x + -25, -625, 225, 100, 0.3);
spawn.bodyRect(x + -1000, -750, 125, 175, 0.3);
spawn.bodyRect(x + -625, -1450, 75, 150, 0.3);
spawn.bodyRect(x + -650, -300, 300, 75, 0.3);
if (!isExitOpen) {
spawn.randomMob(x + -750, -1425, 0);
spawn.randomMob(x + -1050, -1100, 0);
spawn.randomMob(x + -825, -750, 0);
spawn.randomMob(x + -500, -400, 0);
spawn.randomMob(x + 450, -650, 0);
spawn.randomMob(x + 0, -725, 0);
spawn.randomMob(x + 300, -1350, 0);
spawn.randomMob(x + 550, -1500, 0);
spawn.randomMob(x + 725, -1650, 0);
spawn.randomMob(x + 900, -1550, 0);
spawn.randomMob(x + 1100, -1300, 0);
spawn.randomMob(x + -1050, -1050, 0);
spawn.randomMob(x + -925, -350, 0);
spawn.randomMob(x + 75, -1750, 0);
spawn.randomMob(x + 1000, -375, 0);
}
stationCustom = () => { }
stationCustomTopLayer = () => {
checkGate(gate, gateButton)
ctx.fillStyle = "rgba(0,0,0,0.08)"
ctx.fillRect(x + -225, -325, 800, 125);
ctx.fillRect(x + -100, -1425, 300, 125);
ctx.fillRect(x + 950, -675, 400, 125);
}
},
]
// stations[1]() //for testing a specific station
stations[stationList[Math.abs(stationNumber % stationList.length)]]() //*************** run this one when uploading
//add in standard station map infrastructure
spawn.mapRect(x + -8000, 0, 16000, 800);//tunnel floor
spawn.mapRect(x + 1500 - 200, -2000, 6400, 1500); //tunnel roof
spawn.mapRect(x + -1500 - 6400 + 200, -2000, 6400, 1500); //tunnel roof
// add debris so you can see how fast the train moves
const debrisCount = 4
const size = 18 + Math.random() * 25;
const wide = 6400
if (isInProgress) {
//adds new map elements to the level while the level is already running
for (let i = 0; i < map.length; ++i) {
map[i].collisionFilter.category = cat.map;
map[i].collisionFilter.mask = cat.player | cat.map | cat.body | cat.bullet | cat.powerUp | cat.mob | cat.mobBullet;
Matter.Body.setStatic(map[i], true); //make static
Composite.add(engine.world, map[i]); //add to world
}
simulation.draw.setPaths()
simulation.draw.lineOfSightPrecalculation() //required precalculation for line of sight
//shift trains left/right, as you move left or right a train will jump over and become the train needed at the next station
let repositionTrain
let playerOnTrain
if (Math.abs(train[0].position.x - m.pos.x) > Math.abs(train[1].position.x - m.pos.x)) { //figure out which train the player is farthest from and move it
repositionTrain = train[0]
playerOnTrain = train[1]
} else {
repositionTrain = train[1]
playerOnTrain = train[0]
}
repositionTrain.isMoving = false
if (repositionTrain.position.x > playerOnTrain.position.x) { //decide if the train is moving left or right
Matter.Body.setPosition(repositionTrain, { x: -1725 + x, y: repositionTrain.position.y });
repositionTrain.changeDirection(false) //go left
repositionTrain.stops = { right: -1725 + x, left: -7225 + x }
for (let i = 0; i < debrisCount; ++i) spawn.bodyRect(x + -1500 - 6400 + 200 + Math.random() * wide, -35, size * (0.6 + Math.random()), size * (0.6 + Math.random()), 1);
} else {
Matter.Body.setPosition(repositionTrain, { x: 1725 + x, y: repositionTrain.position.y });
repositionTrain.changeDirection(true) //go right
repositionTrain.stops = { left: 1725 + x, right: 7225 + x }
for (let i = 0; i < debrisCount; ++i) spawn.bodyRect(x + 1500 - 200 + Math.random() * wide, -35, size * (0.6 + Math.random()), size * (0.6 + Math.random()), 1);
}
} else {
for (let i = 0; i < debrisCount; ++i) spawn.bodyRect(x + -1500 - 6400 + 200 + Math.random() * wide, -35, size * (0.6 + Math.random()), size * (0.6 + Math.random()), 1);
for (let i = 0; i < debrisCount; ++i) spawn.bodyRect(x + 1500 - 200 + Math.random() * wide, -35, size * (0.6 + Math.random()), size * (0.6 + Math.random()), 1);
}
}
infrastructure(0, false) //if this is run before the level starts, it needs a false
level.custom = () => {
for (let i = 0; i < train.length; i++) train[i].trainStop()
ctx.fillStyle = "rgba(0,0,0,0.1)"//"#ddd"
ctx.fillRect(m.pos.x - 4000, m.pos.y - 4000, 8000, 8000)
level.exit.drawAndCheck();
// level.enter.draw();
//track what station the player is in
if (m.pos.x > 0.55 * stationWidth + stationNumber * stationWidth) {
stationNumber++
// if ((stationNumber % stationList.length) == 0) stationNumber++ //skip the stationNumber that is the modulus of the length of the stationList
infrastructure(stationNumber * stationWidth)
} else if (m.pos.x < -0.55 * stationWidth + stationNumber * stationWidth) {
stationNumber--
// if ((stationNumber % stationList.length) == 0) stationNumber--//skip the stationNumber that is the modulus of the length of the stationList
infrastructure(stationNumber * stationWidth)
}
stationCustom()
};
level.customTopLayer = () => {
for (let i = 0; i < train.length; i++) train[i].draw()
stationCustomTopLayer()
};
level.isProcedural = true //only used in generating text for the level builder
simulation.draw.lineOfSightPrecalculation() //required precalculation for line of sight
simulation.draw.drawMapPath = simulation.draw.drawMapSight
},
reservoir() {
level.exit.x = 1700;
level.exit.y = -4510;
spawn.mapRect(level.exit.x, level.exit.y + 25, 100, 25);
level.setPosToSpawn(-500, 850); //normal spawn
spawn.mapRect(level.enter.x, level.enter.y + 20, 100, 20);
level.defaultZoom = 2300
simulation.zoomTransition(level.defaultZoom)
document.body.style.backgroundColor = "#d8dadf";
color.map = "#3d4240"
powerUps.spawnStartingPowerUps(-575, -2925)
//walls
spawn.mapRect(-3500, -5000, 1500, 6500);
spawn.mapRect(2000, -5000, 1500, 6500);
spawn.mapRect(-2500, 1100, 5000, 400); //slime floor
spawn.mapRect(-3500, -5475, 7000, 600); //top
spawn.mapRect(-1925, -4900, 175, 375); //pipe
spawn.mapRect(-1950, -4550, 225, 25); //pipe
//top floor exit
spawn.mapRect(1475, -4900, 50, 250);
spawn.mapRect(1400, -4475, 650, 50);
// ground
spawn.mapVertex(-687, 1060, "700 0 -700 0 -450 -300 450 -300"); //left base
spawn.mapVertex(863, 1060, "700 0 -700 0 -450 -300 450 -300"); //right base
//entrance
spawn.mapRect(-730, 525, 475, 50);
spawn.mapRect(-730, 550, 50, 150);
spawn.mapRect(-305, 550, 50, 500);
spawn.bodyRect(-717, 700, 25, 100); //door
spawn.bodyRect(-717, 800, 25, 100); //door
//1st floor //left
spawn.mapVertex(-1125 + 437, -50, "490 0 350 80 -350 80 -490 0 -350 -80 350 -80");
spawn.mapRect(-1225, -100, 1070, 100);
if (Math.random() < 0.33) {
spawn.mapVertex(-687, -1000, "-100 -300 0 -350 100 -300 100 300 0 350 -100 300");
} else if (Math.random() < 0.5) {
spawn.mapVertex(-687, -1000, "-150 -450 0 -525 150 -450 150 450 0 525 -150 450");
} else {
spawn.mapVertex(-687, -700, "-150 0 150 0 150 450 0 525 -150 450");
}
//right
spawn.mapVertex(425 + 437, -50, "490 0 350 80 -350 80 -490 0 -350 -80 350 -80");
spawn.mapRect(325, -100, 1070, 100);
spawn.mapRect(175, 675, 425, 25);
spawn.mapRect(1125, 225, 425, 25);
spawn.mapRect(650, 450, 425, 25);
if (Math.random() < 0.33) {
spawn.mapVertex(855, -1000, "-100 -300 0 -350 100 -300 100 300 0 350 -100 300");
} else if (Math.random() < 0.5) {
spawn.mapVertex(855, -1000, "-150 -450 0 -525 150 -450 150 450 0 525 -150 450");
} else {
spawn.mapVertex(855, -700, "-150 0 150 0 150 450 0 525 -150 450");
}
//2nd floor
spawn.mapVertex(-687, -1936, "-625 50 0 100 625 50 625 -50 -625 -50");
spawn.mapVertex(855, -1936, "-625 50 0 100 625 50 625 -50 -625 -50");
//2nd floor right building
spawn.mapRect(550, -3050, 600, 75);
spawn.bodyRect(-125, -2025, 475, 25);
spawn.mapRect(-925, -2350, 675, 50);
spawn.mapRect(-825, -2825, 425, 50);
spawn.mapRect(-450, -3125, 50, 350);
spawn.mapRect(-750, -3150, 350, 50);
spawn.mapRect(-650, -3400, 250, 300);
spawn.mapRect(-650, -3675, 200, 50);
spawn.bodyRect(-375, -2150, 100, 150, 0.2);
//2nd floor left pillar
spawn.mapRect(-1400, -2625, 325, 25);
spawn.mapRect(-1450, -3225, 425, 25);
spawn.mapRect(-1512.5, -3825, 550, 25);
spawn.randomMob(1000, -275, 0.2);
spawn.randomMob(950, -1725, 0.1);
spawn.randomMob(-725, -1775, 0.1);
spawn.randomMob(-200, -2075, 0);
spawn.randomMob(-550, -3500, -0.2);
spawn.randomMob(375, -2125, 0);
spawn.randomMob(-700, -2450, -0.1);
spawn.randomMob(-1175, -2775, -0.1);
spawn.randomMob(1025, -3200, -0.2);
spawn.randomMob(-525, -3750, -0.2);
spawn.randomMob(1350, -2075, -0.3);
spawn.randomMob(1775, 1000, -0.4);
spawn.randomSmallMob(-575, -2925);
spawn.randomGroup(-400, -4400, 0);
if (simulation.difficulty > 1) {
spawn.randomLevelBoss(825, -3500);
spawn.secondaryBossChance(75, -1350)
}
powerUps.addResearchToLevel() //needs to run after mobs are spawned
const slime = level.hazard(-2000, -5000, 4000, 6060); // hazard(x, y, width, height, damage = 0.003)
slime.height -= slime.maxHeight - 60 //start slime at zero
slime.min.y += slime.maxHeight
slime.max.y = slime.min.y + slime.height
const elevator1 = level.elevator(-1625, -90, 310, 800, -2000, 0.0025, { up: 0.1, down: 0.2 }) //x, y, width, height, maxHeight, force = 0.003, friction = { up: 0.01, down: 0.2 }) {
const elevator2 = level.elevator(1175, -3050, 200, 250, -4475, 0.0025, { up: 0.12, down: 0.2 }) //x, y, width, height, maxHeight, force = 0.003, friction = { up: 0.01, down: 0.2 }) {
let waterFallWidth = 0
let waterFallX = 0
let waterFallSmoothX = 0
let isWaterfallFilling = false
const riseRate = 0.30 + Math.min(1, simulation.difficulty * 0.005)
const spinnerArray = []
if (simulation.isHorizontalFlipped) { //flip the map horizontally
spawn.mapVertex(584, -2500, "0 0 300 0 150 600 0 600");
spawn.mapVertex(1116, -2500, "0 0 300 0 300 600 150 600");
spawn.bodyRect(-200, -125, 625, 25);
level.flipHorizontal(); //only flips map,body,mob,powerUp,cons,consBB, exit
elevator1.holdX = -elevator1.holdX // flip the elevator horizontally
elevator2.holdX = -elevator2.holdX // flip the elevator horizontally
spinnerArray.push(level.spinner(-110, -3325, 45, 600, 0.003, 0, 0, 0.01)) // spinner(x, y, width, height, density = 0.001, angle = 0, frictionAir = 0.001, angularVelocity = 0) {
const boost1 = level.boost(-900, -2000, 790)
level.setPosToSpawn(500, 850); //normal spawn
level.custom = () => {
ctx.fillStyle = "#c0c3c9" ///!!!!!!!!!! for flipped x: newX = -oldX - width
ctx.fillRect(1468, -1975, 2, 1915) //elevator track
ctx.fillRect(-1274, -4460, 2, 1425) //elevator track
ctx.fillRect(1225, -3825, 25, 1850); //small pillar background
ctx.fillStyle = "#d0d4d6"
ctx.fillRect(275, -1925, 825, 2925) //large pillar background
ctx.fillRect(-1275, -1925, 825, 2925) //large pillar background
ctx.fillStyle = "#cff" //exit
ctx.fillRect(-2000, -4900, 525, 425)
level.exit.drawAndCheck();
level.enter.draw();
};
level.customTopLayer = () => {
boost1.query();
elevator1.move();
elevator2.move();
ctx.fillStyle = "#233"
ctx.beginPath(); //central dot on spinners
ctx.arc(spinnerArray[0].pointA.x, spinnerArray[0].pointA.y, 9, 0, 2 * Math.PI);
for (let i = 0, len = spinnerArray.length; i < len; i++) {
ctx.moveTo(spinnerArray[i].pointA.x, spinnerArray[i].pointA.y)
ctx.arc(spinnerArray[i].pointA.x, spinnerArray[i].pointA.y, 9, 0, 2 * Math.PI);
}
ctx.fill();
//shadow
ctx.fillStyle = "rgba(0,10,30,0.1)"
ctx.fillRect(-1150, -3000, 600, 1025);
ctx.fillRect(450, -3100, 300, 275);
ctx.fillRect(450, -3625, 200, 225);
ctx.fillRect(400, -2775, 425, 450);
ctx.fillRect(250, -2300, 675, 300);
slime.query();
if (isWaterfallFilling) {
if (slime.height < 5500) {
//draw slime fill
ctx.fillStyle = `hsla(160, 100%, 43%,${0.3 + 0.07 * Math.random()})`
ctx.fillRect(waterFallX, -5050, waterFallWidth, 6175 - slime.height)
if (!m.isBodiesAsleep) {
waterFallWidth = 0.98 * waterFallWidth + 4.7 * Math.random()
waterFallSmoothX = 0.98 * waterFallSmoothX + 3.5 * Math.random()
waterFallX = 1857 - waterFallSmoothX
ctx.fillRect(waterFallX + waterFallWidth * Math.random(), -5050, 4, 6175 - slime.height)
//push player down if they go under waterfall
if (player.position.x > waterFallX && player.position.x < waterFallX + waterFallWidth && player.position.y < slime.height) {
Matter.Body.setVelocity(player, {
x: player.velocity.x,
y: player.velocity.y + 2
});
}
}
slime.levelRise(riseRate)
}
} else if (Vector.magnitudeSquared(Vector.sub(player.position, level.enter)) > 100000) {
isWaterfallFilling = true
}
};
} else { //not flipped
spawn.mapVertex(1116, -2500, "0 0 300 0 150 600 0 600");
spawn.mapVertex(584, -2500, "0 0 300 0 300 600 150 600");
if (Math.random() < 0.1) {
spinnerArray.push(level.spinner(65, -300, 40, 450, 0.003, Math.PI / 2))
} else if (Math.random() < 0.25) {
spinnerArray.push(level.spinner(65, -500, 40, 500, 0.003, 0, 0, -0.015)) // spinner(x, y, width, height, density = 0.001, angle = 0, frictionAir = 0.001, angularVelocity = 0) {
const r = 250
const hexagon = `${r} 0 ${r * Math.cos(5.236)} ${r * Math.sin(5.236)} ${r * Math.cos(4.189)} ${r * Math.sin(4.189)} ${-r} 0 ${r * Math.cos(2.0944)} ${r * Math.sin(2.0944)} ${r * Math.cos(1.0472)} ${r * Math.sin(1.0472)} `
Matter.Body.setVertices(spinnerArray[spinnerArray.length - 1].bodyB, Vertices.fromPath(hexagon))
} else {
const W = 410;
const H = 30;
spawn.bodyRect(-120, -75, W, H, 1, spawn.propsIsNotHoldable)
let b = body[body.length - 1];
cons[cons.length] = Constraint.create({
pointA: {
x: b.position.x - (W / 2) + 50 - 211,
y: b.position.y - 1825
},
bodyB: b,
pointB: {
x: -(W / 2) + 50,
y: 0
},
damping: 0.01,
stiffness: 0.002,
length: 1800
});
cons[cons.length] = Constraint.create({
pointA: {
x: b.position.x + (W / 2) - 50 + 211,
y: b.position.y - 1825
},
bodyB: b,
pointB: {
x: (W / 2) - 50,
y: 0
},
damping: 0.01,
stiffness: 0.002,
length: 1800
});
Composite.add(engine.world, [cons[cons.length - 1], cons[cons.length - 2]])
}
spinnerArray.push(level.spinner(50, -3325, 45, 600, 0.003, 0, 0, 0.01)) // spinner(x, y, width, height, density = 0.001, angle = 0, frictionAir = 0.001, angularVelocity = 0) {
if (Math.random() < 0.5) {
const r = 200
const hexagon = `${r} 0 ${r * Math.cos(5.236)} ${r * Math.sin(5.236)} ${r * Math.cos(4.189)} ${r * Math.sin(4.189)} ${-r} 0 ${r * Math.cos(2.0944)} ${r * Math.sin(2.0944)} ${r * Math.cos(1.0472)} ${r * Math.sin(1.0472)} `
Matter.Body.setVertices(spinnerArray[spinnerArray.length - 1].bodyB, Vertices.fromPath(hexagon))
}
const boost1 = level.boost(800, -2000, 790)
level.custom = () => {
ctx.fillStyle = "#c0c3c9"
ctx.fillRect(-1470, -1975, 2, 1915) //elevator track
ctx.fillRect(1276, -4460, 2, 1425) //elevator track
ctx.fillRect(-1250, -3825, 25, 1850); //small pillar background
ctx.fillStyle = "#d0d4d6"
ctx.fillRect(-1100, -1925, 825, 2925) //large pillar background
ctx.fillRect(450, -1925, 825, 2925) //large pillar background
ctx.fillStyle = "#cff" //exit
ctx.fillRect(1475, -4900, 525, 425)
level.exit.drawAndCheck();
level.enter.draw();
};
level.customTopLayer = () => {
boost1.query();
elevator1.move();
elevator2.move();
ctx.fillStyle = "#233"
ctx.beginPath(); //central dot on spinners
ctx.arc(spinnerArray[0].pointA.x, spinnerArray[0].pointA.y, 9, 0, 2 * Math.PI);
for (let i = 0, len = spinnerArray.length; i < len; i++) {
ctx.moveTo(spinnerArray[i].pointA.x, spinnerArray[i].pointA.y)
ctx.arc(spinnerArray[i].pointA.x, spinnerArray[i].pointA.y, 9, 0, 2 * Math.PI);
}
ctx.fill();
//shadow
ctx.fillStyle = "rgba(0,10,30,0.1)"
ctx.fillRect(550, -3000, 600, 1025);
ctx.fillRect(-750, -3100, 300, 275);
ctx.fillRect(-650, -3625, 200, 225);
ctx.fillRect(-825, -2775, 425, 450);
ctx.fillRect(-925, -2300, 675, 300);
slime.query();
if (isWaterfallFilling) {
if (slime.height < 5500) {
//draw slime fill
ctx.fillStyle = `hsla(160, 100%, 43%,${0.3 + 0.07 * Math.random()})`
ctx.fillRect(waterFallX, -5050, waterFallWidth, 6175 - slime.height)
if (!m.isBodiesAsleep) {
waterFallWidth = 0.98 * waterFallWidth + 4.7 * Math.random()
waterFallSmoothX = 0.98 * waterFallSmoothX + 3.5 * Math.random()
waterFallX = waterFallSmoothX - 1985
ctx.fillRect(waterFallX + waterFallWidth * Math.random(), -5050, 4, 6175 - slime.height)
//push player down if they go under waterfall
if (player.position.x > waterFallX && player.position.x < waterFallX + waterFallWidth && player.position.y < slime.height) {
Matter.Body.setVelocity(player, {
x: player.velocity.x,
y: player.velocity.y + 2
});
}
}
slime.levelRise(riseRate)
}
} else if (Vector.magnitudeSquared(Vector.sub(player.position, level.enter)) > 100000) {
isWaterfallFilling = true
}
};
}
},
reactor() {
level.exit.x = 3500;
level.exit.y = -42;
spawn.mapRect(level.exit.x, level.exit.y + 25, 100, 25);
level.defaultZoom = 2000
simulation.zoomTransition(level.defaultZoom)
document.body.style.backgroundColor = "#c3d6df" //"#d8dadf";
color.map = "#303639";
// powerUps.spawnStartingPowerUps(1475, -1175);
// spawn.debris(750, -2200, 3700, 16); //16 debris per level
spawn.bodyRect(250, -70, 100, 70, 1);
spawn.mapRect(-425, 0, 4500, 2100);
spawn.mapRect(-475, -2825, 4500, 1025);
// spawn.mapRect(1200, -1300, 600, 800);
const a = 400 //side length
const c = 100 //corner offset
spawn.mapVertex(1487, -900, `${-a} ${-a + c} ${-a + c} ${-a} ${a - c} ${-a} ${a} ${-a + c} ${a} ${a - c} ${a - c} ${a} ${-a + c} ${a} ${-a} ${a - c}`); //square with edges cut off
//entrance
spawn.mapRect(-2025, -2825, 1250, 4925);
spawn.mapRect(-900, -2825, 1125, 1725);
spawn.mapRect(-900, -750, 1125, 2850);
spawn.mapRect(-325, -1250, 550, 300);
//exit
spawn.mapRect(3800, -2825, 1225, 4925);
spawn.mapRect(2750, -2150, 1325, 1775);
spawn.mapRect(2750, -475, 550, 300);
spawn.mapRect(2750, -7, 1050, 150); //exit room floor
const doorIn = level.door(-313, -950, 525, 200, 190, 2) //x, y, width, height, distance, speed = 1
const doorOut = level.door(2762, -175, 525, 200, 190, 2) //x, y, width, height, distance, speed = 1
doorIn.collisionFilter.category = cat.map;
doorOut.collisionFilter.category = cat.map; // to prevent boson composite from letting the player skip the level
// doorOut.isClosing = true
let isDoorsLocked = false
let isFightOver = false
let isSpawnedBoss = false
if (simulation.isHorizontalFlipped) { //flip the map horizontally
level.flipHorizontal(); //only flips map,body,mob,powerUp,cons,consBB, exit
level.setPosToSpawn(550, -800); //normal spawn
spawn.mapRect(level.enter.x, level.enter.y + 20, 100, 20);
const button = level.button(-1500, 0)
button.isUp = true
level.custom = () => {
if (isDoorsLocked) {
if (player.position.x > 300) { //if player gets trapped inside starting room open up again
isDoorsLocked = false
doorIn.isClosing = false
}
}
doorIn.openClose();
doorOut.openClose();
ctx.fillStyle = "#d5ebef"
ctx.fillRect(-3800, -375, 1050, 375)
level.enter.draw();
level.exit.drawAndCheck();
button.draw();
if (button.isUp) {
button.query();
} else if (!isSpawnedBoss) {
if (player.position.x < 0) {
if (!doorOut.isClosed() || !doorIn.isClosed()) {
doorIn.isClosing = true
doorOut.isClosing = true
//block caught in a door
if (Matter.Query.collides(doorOut, body).length > 1 || Matter.Query.collides(doorIn, body).length > 1) {
button.isUp = true
doorIn.isClosing = false
doorOut.isClosing = false
}
} else {
isSpawnedBoss = true
isDoorsLocked = true
for (let i = 0; i < 9; ++i) powerUps.spawn(-1800 + 550 * Math.random(), -1700, "ammo")
for (let i = 0; i < 3; ++i) powerUps.spawn(-1800 + 550 * Math.random(), -1700, "heal");
const scale = Math.pow(simulation.difficulty, 0.7) //hard around 30, why around 54
if (mobs.mobDeaths < level.levelsCleared && !simulation.isCheating) {
for (let i = 0; i < 250; i++) spawn.starter(-2700 + 2400 * Math.random(), -1300 - 500 * Math.random())
} else {
if (Math.random() < 0.07 && simulation.difficulty > 35) {
for (let i = 0, len = scale * 0.25 / 6; i < len; ++i) spawn.timeBoss(-1327 - 200 * i, -1525, 60, false); //spawn 1-2 at difficulty 15
for (let i = 0, len = scale * 0.1 / 6; i < len; ++i) spawn.bounceBoss(-1327 - 200 * i, -1525, 80, false);
for (let i = 0, len = scale * 0.15 / 6; i < len; ++i) spawn.sprayBoss(-1327 - 200 * i, -1525, 30, false)
for (let i = 0, len = scale * 0.26 / 6; i < len; ++i) spawn.mineBoss(-1327 - 200 * i, -1525, 50, false);
} else {
if (Math.random() < 0.25) {
for (let i = 0, len = scale * 0.25; i < len; ++i) spawn.timeBoss(-1327 - 200 * i, -1525, 80, false); //spawn 1-2 at difficulty 15
} else if (Math.random() < 0.33) {
for (let i = 0, len = scale * 0.1; i < len; ++i) spawn.bounceBoss(-1327 - 200 * i, -1525, 80, false); //spawn 1-2 at difficulty 15
} else if (Math.random() < 0.5) {
for (let i = 0, len = scale * 0.15; i < len; ++i) spawn.sprayBoss(-1327 - 200 * i, -1525, 30, false) //spawn 2-3 at difficulty 15
} else {
for (let i = 0, len = scale * 0.26; i < len; ++i) spawn.mineBoss(-1327 - 200 * i, -1525, 50, false); //spawn 3-4 at difficulty 15
}
}
}
// spawn.secondaryBossChance(-2300, -800)
}
} else {
doorIn.isClosing = false
}
} else if (!isFightOver && !(simulation.cycle % 180)) {
let isFoundBoss = false
for (let i = 0; i < mob.length; i++) {
if (mob[i].isReactorBoss) {
isFoundBoss = true
break
}
}
if (!isFoundBoss) {
isFightOver = true
doorIn.isClosing = false
doorOut.isClosing = false
// powerUps.spawnBossPowerUp(-3600, -100)
powerUps.spawn(-3650, -50, "tech")
powerUps.spawn(-3650, -150, "tech")
powerUps.spawn(-3650, -300, "tech")
// if (player.position.x < 2760 && player.position.x > 210) {}
}
}
};
level.customTopLayer = () => {
doorIn.draw();
doorOut.draw();
ctx.fillStyle = "rgba(0,0,0,0.1)"
ctx.fillRect(-225, -1100, 1000, 350);
};
} else {
level.setPosToSpawn(-550, -800); //normal spawn
spawn.mapRect(level.enter.x, level.enter.y + 20, 100, 20);
const button = level.button(1400, 0)
button.isUp = true
level.custom = () => {
if (isDoorsLocked) {
if (player.position.x < -300) { //if player gets trapped inside starting room open up again
isDoorsLocked = false
doorIn.isClosing = false
}
}
doorIn.openClose();
doorOut.openClose();
ctx.fillStyle = "#d5ebef"
ctx.fillRect(2750, -375, 1050, 375)
level.enter.draw();
level.exit.drawAndCheck();
button.draw();
if (button.isUp) {
button.query();
} else if (!isSpawnedBoss) {
if (player.position.x > 0) {
if (!doorOut.isClosed() || !doorIn.isClosed()) {
doorIn.isClosing = true
doorOut.isClosing = true
//block caught in a door
if (Matter.Query.collides(doorOut, body).length > 1 || Matter.Query.collides(doorIn, body).length > 1) {
button.isUp = true
doorIn.isClosing = false
doorOut.isClosing = false
}
} else {
isSpawnedBoss = true
isDoorsLocked = true
for (let i = 0; i < 9; ++i) powerUps.spawn(1200 + 550 * Math.random(), -1700, "ammo")
for (let i = 0; i < 3; ++i) powerUps.spawn(1200 + 550 * Math.random(), -1700, "heal");
const scale = Math.pow(simulation.difficulty, 0.7) //hard around 30, why around 54
if (mobs.mobDeaths < level.levelsCleared && !simulation.isCheating) {
for (let i = 0; i < 250; i++) spawn.starter(300 + 2400 * Math.random(), -1300 - 500 * Math.random())
} else {
if (Math.random() < 0.07 && simulation.difficulty > 35) {
for (let i = 0, len = scale * 0.25 / 6; i < len; ++i) spawn.timeBoss(1487 + 200 * i, -1525, 60, false); //spawn 1-2 at difficulty 15
for (let i = 0, len = scale * 0.1 / 6; i < len; ++i) spawn.bounceBoss(1487 + 200 * i, -1525, 80, false);
for (let i = 0, len = scale * 0.15 / 6; i < len; ++i) spawn.sprayBoss(1487 + 200 * i, -1525, 30, false)
for (let i = 0, len = scale * 0.26 / 6; i < len; ++i) spawn.mineBoss(1487 + 200 * i, -1525, 50, false);
} else {
if (Math.random() < 0.25) {
for (let i = 0, len = scale * 0.25; i < len; ++i) spawn.timeBoss(1487 + 200 * i, -1525, 80, false); //spawn 1-2 at difficulty 15
} else if (Math.random() < 0.33) {
for (let i = 0, len = scale * 0.1; i < len; ++i) spawn.bounceBoss(1487 + 200 * i, -1525, 80, false); //spawn 1-2 at difficulty 15
} else if (Math.random() < 0.5) {
for (let i = 0, len = scale * 0.15; i < len; ++i) spawn.sprayBoss(1487 + 200 * i, -1525, 30, false) //spawn 2-3 at difficulty 15
} else {
for (let i = 0, len = scale * 0.26; i < len; ++i) spawn.mineBoss(1487 + 200 * i, -1525, 50, false); //spawn 3-4 at difficulty 15
}
}
}
// spawn.secondaryBossChance(2200, -800)
}
} else {
doorIn.isClosing = false
}
} else if (!isFightOver && !(simulation.cycle % 180)) {
let isFoundBoss = false
for (let i = 0; i < mob.length; i++) {
if (mob[i].isBoss) {
isFoundBoss = true
break
}
}
if (!isFoundBoss) {
isFightOver = true
doorIn.isClosing = false
doorOut.isClosing = false
// powerUps.spawnBossPowerUp(3600, -100)
powerUps.spawn(3650, -50, "tech")
powerUps.spawn(3650, -150, "tech")
powerUps.spawn(3650, -300, "tech")
// if (player.position.x < 2760 && player.position.x > 210) {}
}
}
};
level.customTopLayer = () => {
doorIn.draw();
doorOut.draw();
ctx.fillStyle = "rgba(0,0,0,0.1)"
ctx.fillRect(-775, -1100, 1000, 350);
};
}
// if (simulation.difficulty > 1) spawn.randomLevelBoss(2200, -1300);
powerUps.addResearchToLevel() //needs to run after mobs are spawned
},
factory() {
// simulation.enableConstructMode() //remove this!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
// level.difficultyIncrease(10 * 4) //30 is near max on hard //60 is near max on why
level.setPosToSpawn(2235, -1375); //normal spawn
spawn.mapRect(level.enter.x, level.enter.y + 20, 100, 20); //bump for level entrance
level.exit.x = 7875;
level.exit.y = -2480;
spawn.mapRect(level.exit.x, level.exit.y + 20, 100, 20); //bump for level exit
level.defaultZoom = 1500
simulation.zoomTransition(level.defaultZoom)
document.body.style.backgroundColor = "#d0d2d4s";
// color.map = "#262a2f"
let isPowerLeft = true
const movers = []
//left side
movers.push(level.mover(125, -140, 925, 35, -5))
movers.push(level.mover(1100, -437, 1100, 35, -5))
movers.push(level.mover(2000, -600, 850, 35, -5))
//right side
const moveSpeedStopGo = 8
movers.push(level.mover(2700, -200, 3600, 35, 0))
movers.push(level.mover(7175, -215, 2275, 50, 3))
movers.push(level.mover(6475, -215, 275, 100, -3))
movers.push(level.mover(6725, -500, 500, 375, 3))
movers.push(level.mover(7675, -725, 500, 410, 0))
movers.push(level.mover(6775, -1075, 375, 50, 0))
movers.push(level.mover(5525, -1075, 450, 50, 0))
movers.push(level.mover(6775, -2100, 375, 50, 0))
movers.push(level.mover(5450, -1900, 525, 50, 0))
function setMoverDirection(VxGoal) {
for (let i = 7; i < movers.length; i++) movers[i].VxGoal = VxGoal
}
setMoverDirection(0)
const buttonRight = level.button(7735, -1825)
buttonRight.isUp = true
const buttonLeft = level.button(5275, -1900)
const lasers = []
const laserX = 3390 //3882 - 1130 / 2
const laserGap = 1295 //1130
lasers.push(level.hazard(laserX, -500, 6, 300, 0.4))
lasers.push(level.hazard(laserX + laserGap, -500, 6, 300, 0.4))
lasers.push(level.hazard(laserX + laserGap * 2, -500, 6, 300, 0.4))
for (let i = 0; i < lasers.length; i++) {
lasers[i].isOn = false;
spawn.mapRect(lasers[i].min.x - 55, -550, 110, 50);
spawn.mapRect(lasers[i].min.x - 10, -500, 25, 20);
}
const button1 = level.button(2235, -200)
button1.isUp = true
let bonusAmmoCount = 0
level.custom = () => {
if (isPowerLeft) {
if (!(simulation.cycle % 90)) spawn.bodyRect(2730, -1600, 50, 50);
} else {
// for (let i = 0; i < trains.length; i++) {
// //oscillate back and forth
// if (trains[i].position.x < 5275) {
// trains[i].changeDirection(true) //go right
// } else if (trains[i].position.x > 7875) {
// trains[i].changeDirection(false) //go left
// }
// trains[i].move();
// }
const rate = 160 //multiples of 32!
if ((simulation.cycle % rate) === 80) {
for (let i = 0; i < lasers.length; i++) lasers[i].isOn = false;
movers[3].VxGoal = moveSpeedStopGo;
movers[3].force = 0.0005
movers[2].VxGoal = moveSpeedStopGo;
movers[2].force = 0.0005
} else if ((simulation.cycle % rate) === 0) {
movers[3].VxGoal = 0;
movers[3].force = 0
movers[2].VxGoal = 0;
movers[2].force = 0
spawn.bodyRect(2730, -1600, 50, 50);
if ((simulation.cycle % (rate * 3)) === 0) {
if (bonusAmmoCount < 3 && Math.random() < 0.5) { //some extra ammo because of all the extra mobs that don't drop ammo
bonusAmmoCount++
powerUps.spawn(2760, -1550, Math.random() < 0.5 ? "heal" : "ammo", false);
}
for (let i = 0; i < lasers.length; i++) lasers[i].isOn = true;
const block2Mob = (laserIndex) => { //convert block into mob
const laserHit = Matter.Query.ray(body, lasers[laserIndex].min, lasers[laserIndex].max) //check for collisions with 3rd laser
if (laserHit.length) {
for (let i = 0; i < body.length; i++) {
if (laserHit[0].body.id === body[i].id) { //need to find the block id so it can be removed
const list = ["flutter", "flutter", "flutter", "hopper", "slasher", "slasher", "slasher", "stabber", "springer", "striker", "sneaker", "launcher", "launcherOne", "exploder", "sucker", "spinner", "grower", "beamer", "spawner", "ghoster"]
const pick = list[Math.floor(Math.random() * list.length)]
spawn[pick](lasers[laserIndex].max.x, lasers[laserIndex].max.y - 20);
const who = mob[mob.length - 1]
Matter.Body.setVelocity(who, { x: (8 + 5 * Math.random()), y: -(14 + 10 * Math.random()) });
who.locatePlayer()
who.leaveBody = false;
who.isDropPowerUp = false
//remove block
Matter.Composite.remove(engine.world, body[i]);
body.splice(i, 1);
break
}
}
}
}
if (mob.length < 100 && !m.isBodiesAsleep) {
block2Mob(0)
block2Mob(1)
block2Mob(2)
}
}
}
}
if (buttonLeft.isUp) {
buttonLeft.query();
if (!buttonLeft.isUp) {
setMoverDirection(7)
buttonRight.isUp = true //flip the other button up
}
} else if (buttonRight.isUp) {
buttonRight.query();
if (!buttonRight.isUp) {
setMoverDirection(-7)
//check for blocks and remove them
const list = Matter.Query.region(body, buttonLeft) //are any blocks colliding with this
buttonLeft.isUp = true //flip the other button up
if (list.length > 0) {
list[0].isRemoveMeNow = true
for (let i = 1; i < body.length; i++) { //find which index in body array it is and remove from array
if (body[i].isRemoveMeNow) {
Matter.Composite.remove(engine.world, list[0]);
body.splice(i, 1);
break
}
}
}
}
}
if (button1.isUp) {
button1.query();
if (!button1.isUp) {
isPowerLeft = false
for (let i = 0; i < 3; i++) {
movers[i].VxGoal = 0;
movers[i].force = movers[i].VxGoal > 0 ? 0.0005 : -0.0005
}
powerUps.spawnStartingPowerUps(2760, -1550);
spawn.randomMob(2700, -350, 0.2);
spawn.randomMob(6975, -650, 0.2);
spawn.randomMob(6550, -325, 0.3);
spawn.randomMob(7350, -350, 0.3);
spawn.randomMob(7925, -975, 0.5);
spawn.randomMob(7950, -1725, 0.5);
spawn.randomMob(7000, -1375, 0.3);
spawn.randomMob(5700, -1350, 0.5);
spawn.randomMob(5250, -1575, 0.5);
spawn.randomMob(6325, -75, 0.3);
spawn.randomMob(7900, -1925, 0.1);
spawn.randomMob(5300, -1975, 0.3);
spawn.randomMob(7875, -1900, 0.3);
spawn.randomMob(5325, -1975, 0.4);
spawn.randomGroup(3900, -725, 0.4);
if (simulation.difficulty > 1) spawn.randomLevelBoss(6501, -1771);
spawn.secondaryBossChance(6063, -661)
powerUps.addResearchToLevel() //needs to run after mobs are spawned
}
}
buttonRight.draw();
buttonLeft.draw();
button1.draw();
for (let i = 0; i < movers.length; i++) movers[i].push();
level.exit.drawAndCheck();
level.enter.draw();
ctx.fillStyle = "rgba(0,0,0,0.1)"
ctx.fillRect(6937, -2075, 50, 1775); //6937, -1050, 50, 675);
ctx.fillStyle = "rgba(0,255,255,0.15)" // ctx.fillStyle = "#f2f2f2"
ctx.fillRect(7675, -2875, 500, 425); //exit room
};
level.customTopLayer = () => {
if (isPowerLeft) {
ctx.fillStyle = "rgba(0,0,0,0.2)"
ctx.fillRect(2400, -1650, 7050, 2750) //right side
ctx.fillRect(4950, -3075, 3225, 1425);
ctx.beginPath()
ctx.moveTo(2407, -576);
ctx.lineTo(2000, -573)
ctx.lineTo(1950, -439)
ctx.lineTo(1100, -432)
ctx.lineTo(1020, -143)
ctx.lineTo(125, -137)
ctx.lineTo(-109, 300)
ctx.lineTo(-125, 1089)
ctx.lineTo(2372, 1081)
ctx.lineTo(2452, 65)
ctx.fill();
} else {
// for (let i = 0; i < trains.length; i++) trains[i].draw()
ctx.beginPath()
ctx.moveTo(2526, -589);
ctx.lineTo(2531, -597)
ctx.lineTo(2506, -594)
ctx.lineTo(2850, -600)
ctx.lineTo(2890, -193)
ctx.lineTo(6300, -200)
ctx.lineTo(6618, 857)
ctx.lineTo(6622, 1100)
ctx.lineTo(2521, 1100)
ctx.fillStyle = "rgba(0,0,0,0.2)"
ctx.fill();
ctx.fillRect(-100, -1650, 2625, 2750) //left side
for (let i = 0; i < lasers.length; i++) lasers[i].opticalQuery()
}
ctx.fillStyle = "rgba(0,0,0,0.07)"
ctx.fillRect(7675, -2200, 1775, 2025);
ctx.fillRect(4950, -2075, 500, 1000);
ctx.fillRect(2050, -1650, 350, 325) //entrance room
for (let i = 0; i < movers.length; i++) movers[i].draw();
};
spawn.mapRect(-1550, -3050, 1450, 4150); //left wall
spawn.mapRect(-1550, -3050, 6525, 1400); //ceiling
spawn.mapRect(-1550, -3050, 6525, 1400);
spawn.mapRect(3000, -1700, 1975, 675); //ceiling center
spawn.mapRect(3800, -4000, 5650, 950);
spawn.mapRect(3800, -4000, 1175, 2975);
spawn.mapRect(8175, -4000, 1275, 3685); //right wall
spawn.mapRect(8175, -200, 1275, 1300); //right wall
spawn.mapRect(75, 0, 6275, 1100); //ground
spawn.mapRect(6475, -200, 2750, 1300);
spawn.mapRect(4975, -1087, 550, 62);
spawn.mapRect(4975, -1100, 500, 75);
spawn.mapRect(7875, -1100, 175, 25); //right 3 hop stairs
spawn.mapRect(8075, -1450, 200, 25);
spawn.mapRect(7675, -1825, 375, 25);
spawn.mapRect(7675, -1800, 250, 725);
spawn.mapRect(5125, -1275, 200, 25); //left 3 hop stairs
spawn.mapRect(4900, -1575, 175, 25);
spawn.mapRect(5125, -1900, 325, 25);
spawn.mapRect(5225, -1875, 225, 625);
spawn.mapRect(4950, -3075, 500, 1000);
//exit
spawn.mapRect(7675, -2450, 525, 250);
spawn.mapRect(7675, -3050, 550, 175);
spawn.mapRect(7675, -2925, 50, 175);
spawn.mapRect(1925, -1325, 550, 50); //entrance
spawn.mapRect(2050, -1675, 50, 175); //entrance
spawn.mapRect(1700, -200, 750, 275); //button shelf
if (Math.random() < 0.5) { //left side
spawn.mapRect(625, -1100, 425, 300);
spawn.mapRect(1375, -1100, 425, 300);
spawn.mapRect(1750, -835, 100, 35);
spawn.mapRect(-200, -525, 150, 35);
} else {
spawn.mapRect(800, -1125, 925, 400);
spawn.mapRect(75, -775, 400, 50);
spawn.mapRect(1700, -760, 75, 35);
spawn.mapRect(-200, -425, 150, 35);
}
spawn.mapRect(2400, -600, 125, 675);
spawn.mapRect(2400, -1750, 125, 1050);
spawn.mapRect(2700, -1700, 125, 85);
spawn.randomMob(350, -325, 0.5);
spawn.randomMob(875, -375, 0.5);
spawn.randomMob(1250, -575, 0.5);
spawn.randomMob(1550, -600, 0.5);
spawn.randomSmallMob(1250, -175);
spawn.randomSmallMob(1500, -229);
spawn.randomSmallMob(1850, -300);
powerUps.spawn(5200, -1300, "ammo");
},
labs() {
level.isProcedural = true //used in generating text for the level builder
level.defaultZoom = 1700
simulation.zoomTransition(level.defaultZoom)
document.body.style.backgroundColor = "#d9d9de" //"#d3d3db" //"#dcdcdf";
let isDoorLeft, isDoorRight, x, y
doCustom = []
doCustomTopLayer = []
offset = { x: 0, y: 0 }
const mobSpawnChance = 0 // Math.random() < chance + 0.07 * simulation.difficulty
enterOptions = [
(x = offset.x, y = offset.y) => { //lasers
level.setPosToSpawn(x + 1750, y - 800);
spawn.mapRect(level.enter.x, level.enter.y + 20, 100, 20);
spawn.mapRect(x + 1450, y - 1350, 50, 450); //entrance left wall
spawn.bodyRect(x + 1460, y - 900, 30, 150); //entrance door
spawn.mapRect(x + 1600, y - 350, 500, 100); //toggle shelf
const toggle = level.toggle(x + 1650, y - 350, true) //(x,y,isOn,isLockOn = true/false)
let hazard1
if (Math.random() > 0.5) {
spawn.mapRect(x + 550, y - 750, 1500, 50); //entrance shelf
hazard1 = level.hazard(x + 850, y - 920, 600, 10, 0.4) //laser
spawn.mapRect(x + 860, y - 925, 10, 20); //laser nose
spawn.mapRect(x + 660, y - 975, 200, 120); //laser body
} else {
spawn.mapRect(x + 1350, y - 750, 700, 50); //entrance shelf
hazard1 = level.hazard(x + 1040, y - 660, 1000, 10, 0.4) //laser
spawn.mapRect(x + 1050, y - 665, 10, 20); //laser nose
spawn.mapRect(x + 650, y - 705, 400, 100); //laser body
}
const hazard2 = level.hazard(x, y - 330, 450, 10, 0.4) //laser
spawn.mapRect(x + 440, y - 335, 10, 20); //laser nose
spawn.mapRect(x + 450, y - 375, 400, 100); //laser body
//exit hazards
const Xoffset = Math.floor(400 * Math.random())
const hazard3 = level.hazard(x + Xoffset, y - 1300, 10, 1300, 0.4) //laser
spawn.mapRect(x + Xoffset - 5, y - 1310, 20, 20); //laser nose
const Xoffset2 = 1650 + Math.floor(300 * Math.random())
const hazard4 = level.hazard(x + Xoffset2, y - 240, 10, 250, 0.4) //laser
spawn.mapRect(x + Xoffset2 - 5, y - 250, 20, 20); //laser nose
spawn.randomMob(x + 150, y + -1100, mobSpawnChance);
spawn.randomMob(x + 175, y + -775, mobSpawnChance);
spawn.randomMob(x + 150, y + -350, mobSpawnChance);
spawn.randomMob(x + 150, y + -75, mobSpawnChance);
spawn.randomMob(x + 650, y + -125, mobSpawnChance);
spawn.randomMob(x + 1200, y + -75, mobSpawnChance);
// let isSpawnedMobs = false
doCustomTopLayer.push(
() => {
toggle.query();
hazard1.isOn = toggle.isOn
hazard2.isOn = toggle.isOn
hazard3.isOn = toggle.isOn
hazard4.isOn = toggle.isOn
if ((simulation.cycle % 120) > 60) {
hazard1.opticalQuery();
hazard2.opticalQuery();
} else {
hazard3.opticalQuery();
hazard4.opticalQuery();
}
// if (!isSpawnedMobs && !toggle.isOn) {
// isSpawnedMobs = true
// spawn.randomMob(x + 150, y + -1100, mobSpawnChance);
// spawn.randomMob(x + 175, y + -775, mobSpawnChance);
// spawn.randomMob(x + 150, y + -350, mobSpawnChance);
// spawn.randomMob(x + 150, y + -75, mobSpawnChance);
// spawn.randomMob(x + 650, y + -125, mobSpawnChance);
// spawn.randomMob(x + 1200, y + -75, mobSpawnChance);
// }
}
)
},
]
exitOptions = [
(x = offset.x, y = offset.y) => {
level.exit.x = x + 1725;
level.exit.y = y - 980;
spawn.mapRect(level.exit.x, level.exit.y + 20, 100, 20);
spawn.mapRect(x + 1500, y - 950, 500, 25); //exit platform
spawn.mapRect(x + 1550, y - 1300, 25, 175); //exit side wall
spawn.mapVertex(x + 1300, y - 125, "-400 0 -250 -400 250 -400 400 0");
spawn.bodyRect(x + 1075, y - 475, 125, 125, 0.25);
spawn.bodyRect(x + 500, y - 100, 125, 100, 0.25);
spawn.bodyRect(x + 200, y - 150, 100, 150, 0.25);
spawn.bodyRect(x + 1075, y - 1075, 100, 125, 0.25);
const density = 0.0015 //+ (simulation.difficultyMode < 5 ? 0.0035 : 0)
const angle = Math.PI / 2
const variance = 0 //Math.PI
const frictionAir = 0.03
const angularVelocity = 0 //0.01
const spinVariance = 0 //0.02
balance1 = level.spinner(x + 200, y - 500, 30, 400, density, angle + variance * (Math.random() - 0.5), frictionAir, angularVelocity + spinVariance * (Math.random() - 0.5)) // spinner(x, y, width, height, density = 0.001, angle=0,frictionAir=0.001,angularVelocity=0) {
balance2 = level.spinner(x + 200, y - 950, 30, 400, density, angle + variance * (Math.random() - 0.5), frictionAir, angularVelocity + spinVariance * (Math.random() - 0.5))
balance3 = level.spinner(x + 650, y - 750, 30, 400, density, angle + variance * (Math.random() - 0.5), frictionAir, angularVelocity + spinVariance * (Math.random() - 0.5))
// balance4 = level.spinner(x + 750, y - 1050, 25, 350, density, angle + variance * (Math.random() - 0.5), frictionAir, angularVelocity + spinVariance * (Math.random() - 0.5))
balance4 = level.spinner(x + 1250, y - 1000, 30, 400, density, angle + variance * (Math.random() - 0.5), frictionAir, angularVelocity + spinVariance * (Math.random() - 0.5))
let isInRoom = false
doCustom.push(
() => {
if (!isInRoom && m.pos.x > x - 100 && m.pos.x < x + 2700 && m.pos.y > y - 1300 && m.pos.y < y) { //check if player is in this room and run code once
isInRoom = true
spawn.randomMob(x + 1175, y - 725, mobSpawnChance);
spawn.randomMob(x + 1450, y - 725, mobSpawnChance);
spawn.randomMob(x + 425, y - 100, mobSpawnChance);
spawn.randomMob(x + 1700, y - 300, mobSpawnChance);
spawn.randomMob(x + 1300, y - 375, mobSpawnChance);
}
ctx.fillStyle = "#d4f4f4"
ctx.fillRect(x + 1550, y - 1300, 450, 350)
}
)
doCustomTopLayer.push(
() => {
ctx.fillStyle = "#233"
ctx.beginPath();
ctx.arc(balance1.pointA.x, balance1.pointA.y, 9, 0, 2 * Math.PI);
ctx.moveTo(balance2.pointA.x, balance2.pointA.y)
ctx.arc(balance2.pointA.x, balance2.pointA.y, 9, 0, 2 * Math.PI);
ctx.moveTo(balance3.pointA.x, balance3.pointA.y)
ctx.arc(balance3.pointA.x, balance3.pointA.y, 9, 0, 2 * Math.PI);
ctx.moveTo(balance4.pointA.x, balance4.pointA.y)
ctx.arc(balance4.pointA.x, balance4.pointA.y, 9, 0, 2 * Math.PI);
ctx.fill();
}
)
},
(x = offset.x, y = offset.y) => {
level.exit.x = x + 1750;
level.exit.y = y - 980;
spawn.mapRect(level.exit.x, level.exit.y + 20, 100, 20);
spawn.mapRect(x + 1550, y - 950, 500, 25); //exit platform
spawn.mapRect(x + 1600, y - 1300, 25, 175); //exit side wall
spawn.bodyRect(x + 1275, y - 475, 125, 125, 0.25);
spawn.bodyRect(x + 500, y - 100, 125, 100, 0.25);
spawn.bodyRect(x + 800, y - 150, 100, 150, 0.25);
spawn.bodyRect(x + 875, y + -50, 50, 50);
spawn.bodyRect(x + 1025, y + -50, 50, 50);
if (Math.random() > 0.5) {
const density = 0.0012 //+ (simulation.difficultyMode < 5 ? 0.003 : 0)
const angle = Math.PI / 2
const variance = 0.2 //Math.PI
const frictionAir = 0.015
const height = 35
balance1 = level.spinner(x + 1300, y - 425, height, 410, density, angle + variance * (Math.random() - 0.5), frictionAir) // spinner(x, y, width, height, density = 0.001, angle=0,frictionAir=0.001,angularVelocity=0) {
balance3 = level.spinner(x + 750, y - 650, height, 410, density, angle + variance * (Math.random() - 0.5), frictionAir)
balance2 = level.spinner(x + 300, y - 425, height, 410, density, angle + variance * (Math.random() - 0.5), frictionAir)
balance4 = level.spinner(x + 1250, y - 950, 50, 550, density, angle, 0.1)
const rotatingBlock = body[body.length - 1]
doCustom.push(
() => {
if (!isInRoom && m.pos.x > x - 100 && m.pos.x < x + 2700 && m.pos.y > y - 1300 && m.pos.y < y) { //check if player is in this room and run code once
isInRoom = true
spawn.randomMob(x + 1175, y - 725, mobSpawnChance);
spawn.randomMob(x + 1450, y - 725, mobSpawnChance);
spawn.randomMob(x + 425, y - 100, mobSpawnChance);
spawn.randomMob(x + 1200, y - 125, mobSpawnChance);
spawn.randomMob(x + 1300, y - 375, mobSpawnChance);
}
ctx.fillStyle = "#d4f4f4"
ctx.fillRect(x + 1600, y - 1300, 400, 350)
rotatingBlock.torque += rotatingBlock.inertia * 0.000005
}
)
} else {
const density = 0.001 //+ (simulation.difficultyMode < 5 ? 0.003 : 0)
const angle = Math.PI / 2
const variance = Math.PI
const frictionAir = 0.015
const width = 200
const height = 200
const spinVariance = 0.05
balance1 = level.spinner(x + 175, y - 300, height, width, density, angle + variance * (Math.random() - 0.5), frictionAir, spinVariance * (Math.random() - 0.5)) // spinner(x, y, width, height, density = 0.001, angle=0,frictionAir=0.001,angularVelocity=0) {
balance2 = level.spinner(x + 500, y - 525, height, width, density, angle + variance * (Math.random() - 0.5), frictionAir, spinVariance * (Math.random() - 0.5))
balance3 = level.spinner(x + 850, y - 700, height, width, density, angle + variance * (Math.random() - 0.5), frictionAir, spinVariance * (Math.random() - 0.5))
balance4 = level.spinner(x + 1250, y - 850, height, width, density, angle + variance * (Math.random() - 0.5), frictionAir, spinVariance * (Math.random() - 0.5))
doCustom.push(
() => {
if (!isInRoom && m.pos.x > x - 100 && m.pos.x < x + 2700 && m.pos.y > y - 1300 && m.pos.y < y) { //check if player is in this room and run code once
isInRoom = true
spawn.randomMob(x + 1175, y - 725, mobSpawnChance);
spawn.randomMob(x + 1450, y - 725, mobSpawnChance);
spawn.randomMob(x + 425, y - 100, mobSpawnChance);
spawn.randomMob(x + 1200, y - 125, mobSpawnChance);
spawn.randomMob(x + 1300, y - 375, mobSpawnChance);
}
ctx.fillStyle = "#d4f4f4"
ctx.fillRect(x + 1600, y - 1300, 400, 350)
}
)
}
let isInRoom = false
doCustomTopLayer.push(
() => {
ctx.fillStyle = "#233"
ctx.beginPath();
ctx.arc(balance1.pointA.x, balance1.pointA.y, 9, 0, 2 * Math.PI);
ctx.moveTo(balance2.pointA.x, balance2.pointA.y)
ctx.arc(balance2.pointA.x, balance2.pointA.y, 9, 0, 2 * Math.PI);
ctx.moveTo(balance3.pointA.x, balance3.pointA.y)
ctx.arc(balance3.pointA.x, balance3.pointA.y, 9, 0, 2 * Math.PI);
ctx.moveTo(balance4.pointA.x, balance4.pointA.y)
ctx.arc(balance4.pointA.x, balance4.pointA.y, 9, 0, 2 * Math.PI);
ctx.fill();
}
)
}
]
emptyOptions = [ //nothing good here except the starting power up, and duplicated bosses
(x = offset.x, y = offset.y) => { //pulse
if (!isDoorLeft && isDoorRight) { //flipped, entering from the right
powerUps.spawnStartingPowerUps(x + 2000 - 1650, y + -400);
spawn.mapRect(x + 2000 - 1575 - 25, y + -625, 25, 375); //wall on top of wall
spawn.mapRect(x + 2000 - 1575 - 25, y + -1325, 25, 525); //wall on top of wall
spawn.mapRect(x + 2000 - 1525 - 250, y + -350, 250, 450); //wall
spawn.mapRect(x + 2000 - 245 - 300, y + -200, 300, 100); //gun
spawn.mapRect(x + 2000 - 530 - 25, y + -190, 25, 80); //gun nose
const button = level.button(x + 2000 - 290 - 140, y - 200)
button.isReadyToFire = true
doCustom.push(
() => {
ctx.fillStyle = "rgba(0,0,0,0.05)"; //"rgba(0,0,0,0.1)";
ctx.fillRect(x + 2000 - 255 - 280, y + -100, 280, 100);
button.query();
button.draw();
if (!button.isReadyToFire && button.isUp) {
button.isReadyToFire = true
} else if (button.isReadyToFire && !button.isUp) {
button.isReadyToFire = false
b.pulse(90, Math.PI, {
x: x + 2000 - 560,
y: y - 150
})
}
}
)
spawn.randomMob(x + 2000 - 1600, y + -425, mobSpawnChance);
spawn.randomMob(x + 2000 - 1725, y + -1250, mobSpawnChance);
spawn.randomMob(x + 2000 - 1250, y + -1200, mobSpawnChance);
spawn.randomMob(x + 2000 - 300, y + -1200, mobSpawnChance);
spawn.randomMob(x + 2000 - 800, y + -125, mobSpawnChance);
let pick = spawn.pickList[Math.floor(Math.random() * spawn.pickList.length)];
spawn[pick](x + 2000 - 1275, y + -150, 90 + Math.random() * 40); //one extra large mob
} else {
powerUps.spawnStartingPowerUps(x + 1650, y + -400);
spawn.mapRect(x + 1575, y + -625, 25, 375); //wall on top of wall
spawn.mapRect(x + 1575, y + -1325, 25, 525); //wall on top of wall
spawn.mapRect(x + 1525, y + -350, 250, 450); //wall
spawn.mapRect(x + 245, y + -200, 300, 100); //gun
spawn.mapRect(x + 530, y + -190, 25, 80); //gun nose
const button = level.button(x + 290, y - 200)
button.isReadyToFire = true
doCustom.push(
() => {
ctx.fillStyle = "rgba(0,0,0,0.05)"; //"rgba(0,0,0,0.1)";
ctx.fillRect(x + 255, y + -100, 280, 100);
button.query();
button.draw();
if (!button.isReadyToFire && button.isUp) {
button.isReadyToFire = true
} else if (button.isReadyToFire && !button.isUp) {
button.isReadyToFire = false
b.pulse(90, 0, { x: x + 560, y: y - 150 })
}
}
)
spawn.randomMob(x + 1600, y + -425, mobSpawnChance);
spawn.randomMob(x + 1725, y + -1250, mobSpawnChance);
spawn.randomMob(x + 1250, y + -1200, mobSpawnChance);
spawn.randomMob(x + 300, y + -1200, mobSpawnChance);
spawn.randomMob(x + 800, y + -125, mobSpawnChance);
let pick = spawn.pickList[Math.floor(Math.random() * spawn.pickList.length)];
spawn[pick](x + 1275, y + -150, 90 + Math.random() * 40); //one extra large mob
}
},
(x = offset.x, y = offset.y) => { //spawn block and fire it
if (!isDoorLeft && isDoorRight) {
powerUps.spawnStartingPowerUps(x + 1650, y + -400);
spawn.mapRect(x + 2000 - 1575 - 25, y + -625, 25, 375); //wall on top of wall
spawn.mapRect(x + 2000 - 1575 - 25, y + -1325, 25, 525); //wall on top of wall
spawn.mapRect(x + 2000 - 1525 - 250, y + -350, 250, 450); //wall
spawn.mapRect(x + 2000 - 245 - 300, y + -200, 300, 100); //gun
spawn.mapRect(x + 2000 - 530 - 25, y + -190, 25, 80);
const button = level.button(x + 2000 - 290 - 140, y - 200)
button.isReadyToFire = true
doCustom.push(
() => {
ctx.fillStyle = "rgba(0,0,0,0.05)"; //"rgba(0,0,0,0.1)";
ctx.fillRect(x + 2000 - 255 - 280, y + -100, 280, 100);
button.query();
button.draw();
if (!button.isReadyToFire && button.isUp) {
button.isReadyToFire = true
} else if (button.isReadyToFire && !button.isUp) {
button.isReadyToFire = false
fireBlock = function (xPos, yPos) {
const index = body.length
spawn.bodyRect(xPos, yPos, 35 + 50 * Math.random(), 35 + 50 * Math.random());
const bodyBullet = body[body.length - 1]
Matter.Body.setVelocity(body[index], { x: -120, y: -5 });
body[index].isAboutToBeRemoved = true;
setTimeout(() => { //remove block
for (let i = 0; i < body.length; i++) {
if (body[i] === bodyBullet) {
Matter.Composite.remove(engine.world, body[i]);
body.splice(i, 1);
}
}
}, 1000);
}
fireBlock(x + 2000 - 90 - 560 + 30 * Math.random(), y - 140);
fireBlock(x + 2000 - 90 - 560 + 30 * Math.random(), y - 160);
fireBlock(x + 2000 - 90 - 560 + 30 * Math.random(), y - 180);
fireBlock(x + 2000 - 90 - 560 + 30 * Math.random(), y - 200);
fireBlock(x + 2000 - 90 - 560 + 30 * Math.random(), y - 220);
fireBlock(x + 2000 - 90 - 560 + 30 * Math.random(), y - 240);
}
}
)
spawn.randomMob(x + 2000 - 1600, y + -425, mobSpawnChance);
spawn.randomMob(x + 2000 - 1725, y + -1250, mobSpawnChance);
spawn.randomMob(x + 2000 - 1250, y + -1200, mobSpawnChance);
spawn.randomMob(x + 2000 - 300, y + -1200, mobSpawnChance);
spawn.randomMob(x + 2000 - 800, y + -125, mobSpawnChance);
let pick = spawn.pickList[Math.floor(Math.random() * spawn.pickList.length)];
spawn[pick](x + 2000 - 1275, y + -150, 90 + Math.random() * 40); //one extra large mob
} else {
powerUps.spawnStartingPowerUps(x + 1650, y + -400);
spawn.mapRect(x + 1575, y + -625, 25, 375); //wall on top of wall
spawn.mapRect(x + 1575, y + -1325, 25, 525); //wall on top of wall
spawn.mapRect(x + 1525, y + -350, 250, 450); //wall
spawn.mapRect(x + 245, y + -200, 300, 100); //gun
spawn.mapRect(x + 530, y + -190, 25, 80);
const button = level.button(x + 290, y - 200)
button.isReadyToFire = true
doCustom.push(
() => {
ctx.fillStyle = "rgba(0,0,0,0.05)"; //"rgba(0,0,0,0.1)";
ctx.fillRect(x + 255, y + -100, 280, 100);
button.query();
button.draw();
if (!button.isReadyToFire && button.isUp) {
button.isReadyToFire = true
} else if (button.isReadyToFire && !button.isUp) {
button.isReadyToFire = false
fireBlock = function (xPos, yPos) {
const index = body.length
spawn.bodyRect(xPos, yPos, 35 + 50 * Math.random(), 35 + 50 * Math.random());
const bodyBullet = body[body.length - 1]
Matter.Body.setVelocity(body[index], { x: 120, y: -5 });
body[index].isAboutToBeRemoved = true;
setTimeout(() => { //remove block
for (let i = 0; i < body.length; i++) {
if (body[i] === bodyBullet) {
Matter.Composite.remove(engine.world, body[i]);
body.splice(i, 1);
}
}
}, 1000);
}
fireBlock(x + 560 + 30 * Math.random(), y - 140);
fireBlock(x + 560 + 30 * Math.random(), y - 160);
fireBlock(x + 560 + 30 * Math.random(), y - 180);
fireBlock(x + 560 + 30 * Math.random(), y - 200);
fireBlock(x + 560 + 30 * Math.random(), y - 220);
fireBlock(x + 560 + 30 * Math.random(), y - 240);
}
}
)
spawn.randomMob(x + 1600, y + -425, mobSpawnChance);
spawn.randomMob(x + 1725, y + -1250, mobSpawnChance);
spawn.randomMob(x + 1250, y + -1200, mobSpawnChance);
spawn.randomMob(x + 300, y + -1200, mobSpawnChance);
spawn.randomMob(x + 800, y + -125, mobSpawnChance);
let pick = spawn.pickList[Math.floor(Math.random() * spawn.pickList.length)];
spawn[pick](x + 1275, y + -150, 90 + Math.random() * 40); //one extra large mob
}
},
(x = offset.x, y = offset.y) => { //fire an "ammo clip" of blocks
if (!isDoorLeft && isDoorRight) { //flipped, entering from the right
powerUps.spawnStartingPowerUps(x + 2000 - 1650, y + -400);
spawn.mapRect(x + 2000 - 1575 - 25, y + -625, 25, 375); //wall on top of wall
spawn.mapRect(x + 2000 - 1575 - 25, y + -1325, 25, 525); //wall on top of wall
spawn.mapRect(x + 2000 - 1525 - 250, y + -350, 250, 450); //wall
spawn.mapRect(x + 2000 - 175 - 370, y + -200, 370, 100); //gun
spawn.mapRect(x + 2000 - 530 - 25, y + -190, 25, 80);
spawn.mapRect(x + 2000 - 545 - 10, y + -770, 10, 325); //block loader for gun //walls
spawn.mapRect(x + 2000 - 620 - 10, y + -770, 10, 325); //walls
spawn.mapRect(x + 2000 + 50 - 150, y + -425, 150, 50);
spawn.mapRect(x + 2000 - 175 - 370, y + -650, 370, 50);
spawn.mapRect(x + 2000 - 540 - 95, y + -460, 95, 15); //bottom that opens and closes
const bulletDoor = map[map.length - 1] //keep track of this body so it can be make non-collide later
for (let i = 0; i < 6; i++) spawn.bodyRect(x + 2000 - 60 - 555 + Math.floor(Math.random() * 10), y + -520 - 50 * i, 50, 50); //bullets for gun
spawn.bodyRect(x + 2000 - 250 - 40, y + -700, 40, 50); //extra bullets
spawn.bodyRect(x + 2000 - 350 - 30, y + -700, 30, 35);
spawn.bodyRect(x + 2000 - 425 - 40, y + -700, 40, 70);
const button = level.button(x + 2000 - 280 - 140, y - 200) //trigger for gun
button.isReadyToFire = true
doCustom.push(
() => {
ctx.fillStyle = "rgba(0,0,0,0.05)"; //"rgba(0,0,0,0.1)";
ctx.fillRect(x + 2000 - 200 - 325, y + -625, 325, 650);
button.query();
button.draw();
if (!button.isReadyToFire && button.isUp) {
button.isReadyToFire = true
bulletDoor.collisionFilter.mask = cat.player | cat.map | cat.body | cat.bullet | cat.mob | cat.mobBullet
} else if (button.isReadyToFire && !button.isUp) {
button.isReadyToFire = false
bulletDoor.collisionFilter.mask = 0 //cat.player | cat.map | cat.body | cat.bullet | cat.mob | cat.mobBullet
} else if (!button.isUp) {
const bounds = {
min: {
x: x + 2000 - 580,
y: y - 125
},
max: {
x: x + 2000 - 530,
y: y - 110
}
}
const list = Matter.Query.region(body, bounds)
for (let i = 0, len = list.length; i < len; i++) {
Matter.Body.setVelocity(list[i], {
x: -120,
y: -5
});
}
if (Matter.Query.region([player], bounds).length) {
Matter.Body.setVelocity(player, {
x: -100,
y: -5
});
}
ctx.fillStyle = `rgba(255,0,255,${0.2 + 0.7 * Math.random()})`
ctx.fillRect(bounds.min.x, y - 185, 38, 70);
}
}
)
spawn.randomMob(x + 2000 - 1600, y + -425, mobSpawnChance);
spawn.randomMob(x + 2000 - 1725, y + -1250, mobSpawnChance);
spawn.randomMob(x + 2000 - 1250, y + -1200, mobSpawnChance);
spawn.randomMob(x + 2000 - 300, y + -1200, mobSpawnChance);
spawn.randomMob(x + 2000 - 800, y + -125, mobSpawnChance);
let pick = spawn.pickList[Math.floor(Math.random() * spawn.pickList.length)];
spawn[pick](x + 2000 - 1275, y + -150, 90 + Math.random() * 40); //one extra large mob
} else {
powerUps.spawnStartingPowerUps(x + 1650, y + -400);
spawn.mapRect(x + 1575, y + -625, 25, 375); //wall on top of wall
spawn.mapRect(x + 1575, y + -1325, 25, 525); //wall on top of wall
spawn.mapRect(x + 1525, y + -350, 250, 450); //wall
spawn.mapRect(x + 175, y + -200, 370, 100); //gun
spawn.mapRect(x + 530, y + -190, 25, 80);
spawn.mapRect(x + 545, y + -770, 10, 325); //block loader for gun //walls
spawn.mapRect(x + 620, y + -770, 10, 325); //walls
spawn.mapRect(x - 50, y + -425, 150, 50);
spawn.mapRect(x + 175, y + -650, 370, 50);
spawn.mapRect(x + 540, y + -460, 95, 15); //bottom that opens and closes
const bulletDoor = map[map.length - 1] //keep track of this body so it can be make non-collide later
for (let i = 0; i < 6; i++) spawn.bodyRect(x + 555 + Math.floor(Math.random() * 10), y + -520 - 50 * i, 50, 50); //bullets for gun
spawn.bodyRect(x + 250, y + -700, 40, 50); //extra bullets
spawn.bodyRect(x + 350, y + -700, 30, 35);
spawn.bodyRect(x + 425, y + -700, 40, 70);
const button = level.button(x + 280, y - 200) //trigger for gun
button.isReadyToFire = true
doCustom.push(
() => {
ctx.fillStyle = "rgba(0,0,0,0.05)"; //"rgba(0,0,0,0.1)";
ctx.fillRect(x + 200, y + -625, 325, 650);
button.query();
button.draw();
if (!button.isReadyToFire && button.isUp) {
button.isReadyToFire = true
bulletDoor.collisionFilter.mask = cat.player | cat.map | cat.body | cat.bullet | cat.mob | cat.mobBullet
} else if (button.isReadyToFire && !button.isUp) {
button.isReadyToFire = false
bulletDoor.collisionFilter.mask = 0 //cat.player | cat.map | cat.body | cat.bullet | cat.mob | cat.mobBullet
} else if (!button.isUp) {
const bounds = {
min: {
x: x + 530,
y: y - 125
},
max: {
x: x + 580,
y: y - 110
}
}
const list = Matter.Query.region(body, bounds)
for (let i = 0, len = list.length; i < len; i++) {
Matter.Body.setVelocity(list[i], {
x: 120,
y: -5
});
}
if (Matter.Query.region([player], bounds).length) {
Matter.Body.setVelocity(player, {
x: 100,
y: -5
});
}
ctx.fillStyle = `rgba(255,0,255,${0.2 + 0.7 * Math.random()})`
ctx.fillRect(bounds.min.x, y - 185, 38, 70);
}
}
)
spawn.randomMob(x + 1600, y + -425, mobSpawnChance);
spawn.randomMob(x + 1725, y + -1250, mobSpawnChance);
spawn.randomMob(x + 1250, y + -1200, mobSpawnChance);
spawn.randomMob(x + 300, y + -1200, mobSpawnChance);
spawn.randomMob(x + 800, y + -125, mobSpawnChance);
let pick = spawn.pickList[Math.floor(Math.random() * spawn.pickList.length)];
spawn[pick](x + 1275, y + -150, 90 + Math.random() * 40); //one extra large mob
}
}
]
lootOptions = [ //has some power up reward //field, ammo, research, gun
(x = offset.x, y = offset.y) => {
spawn.mapRect(x + 1925, y + -325, 125, 150); //4 wall ledges
spawn.mapRect(x + 1925, y + -865, 125, 150); //4 wall ledges
spawn.mapRect(x + -50, y + -325, 125, 150); //4 wall ledges
spawn.mapRect(x + -50, y + -865, 125, 150); //4 wall ledges
spawn.mapRect(x + 1700, y + -500, 200, 25);
spawn.mapRect(x + 75, y + -500, 200, 25);
let chamberY = -650
if (Math.random() > 0.5) { //upper chamber
chamberY = -650 - 640
spawn.mapRect(x + 550, y + -10 - 640, 900, 25); //raised floor
spawn.mapRect(x + 450, y + -20 - 640, 1100, 25);
spawn.mapRect(x + 450, y + -675 - 640, 1100, 25); //chamber ceiling
powerUps.directSpawn(x + 998, y - 333 - 640, "tech", false);
spawn.mapVertex(x + 1000, y + -0, "575 0 -575 0 -450 -100 450 -100"); //base
} else { //lower chamber
spawn.mapRect(x + 400, y + -10, 1200, 50); //raised floor
spawn.mapRect(x + 450, y + -20, 1100, 50);
spawn.mapRect(x + 450, y + -675, 1100, 25); //chamber ceiling
spawn.mapRect(x + 550, y + -685, 900, 25);
powerUps.directSpawn(x + 998, y - 333, "tech", false);
}
const powerUp1 = powerUp[powerUp.length - 1]
powerUp1.holdPosition = {
x: powerUp1.position.x,
y: powerUp1.position.y
}
let isSpawnedMobs = false
doCustom.push(
() => {
ctx.fillStyle = "#e4e4e9" //"rgba(255,255,255,1)";
ctx.fillRect(x + 450, y + chamberY, 1100, 650); //chamber background
// if (!isInRoom && m.pos.x > x - 100 && m.pos.x < x + 2000 && m.pos.y > y - 1300 && m.pos.y < y) { //is player inside this room?
// isInRoom = true
// } else
if (powerUp1.velocity.y !== 0) { //don't run this code if power up is gone //hack: powerUp1.velocity.y !== 0 seems to only be true if the power up up doesn't exist and is no longer being affected by gravity
ctx.strokeStyle = "#f0f"
ctx.lineWidth = 2;
if (Vector.magnitudeSquared(Vector.sub(m.pos, powerUp1.position)) < 90000) { //zone radius is 300
//damage player and drain energy
if (m.immuneCycle < m.cycle) {
m.damage(0.01);
if (m.energy > 0.1) m.energy -= 0.02
}
//draw electricity going towards player
const unit = Vector.normalise(Vector.sub(m.pos, powerUp1.position))
let xElec = powerUp1.position.x + 40 * unit.x;
let yElec = powerUp1.position.y + 40 * unit.y;
ctx.beginPath();
ctx.moveTo(xElec, yElec);
const step = 40
for (let i = 0; i < 6; i++) {
xElec += step * (unit.x + 1.5 * (Math.random() - 0.5))
yElec += step * (unit.y + 1.5 * (Math.random() - 0.5))
ctx.lineTo(xElec, yElec);
}
} else {
//draw electricity going in random directions
const angle = Math.random() * 2 * Math.PI
const Dx = Math.cos(angle);
const Dy = Math.sin(angle);
let xElec = powerUp1.position.x + 40 * Dx;
let yElec = powerUp1.position.y + 40 * Dy;
ctx.beginPath();
ctx.moveTo(xElec, yElec);
const step = 40
for (let i = 0; i < 6; i++) {
xElec += step * (Dx + 1.5 * (Math.random() - 0.5))
yElec += step * (Dy + 1.5 * (Math.random() - 0.5))
ctx.lineTo(xElec, yElec);
}
}
ctx.lineWidth = 2 * Math.random();
ctx.stroke(); //draw electricity
ctx.beginPath(); //outline damage zone
ctx.arc(powerUp1.position.x, powerUp1.position.y, 300, 0, 2 * Math.PI);
ctx.stroke();
//float power up in the air
Matter.Body.setPosition(powerUp1, {
x: powerUp1.holdPosition.x + 4 * Math.random(), //1300 -2
y: powerUp1.holdPosition.y + 4 * Math.random() //335 -2
});
Matter.Body.setVelocity(powerUp1, {
x: 0,
y: 0
});
} else if (!isSpawnedMobs) {
isSpawnedMobs = true
if (chamberY === -650) { //lower chamber
spawn.randomMob(x + 250, y + -650, mobSpawnChance);
spawn.randomMob(x + 1825, y + -600, mobSpawnChance);
spawn.randomGroup(x + 275, y + -1050, mobSpawnChance);
spawn.randomGroup(x + 675, y + -975, mobSpawnChance);
spawn.randomGroup(x + 1225, y + -975, Infinity);
} else { //upper chamber
spawn.randomMob(x + 250, y + -650, mobSpawnChance);
spawn.randomMob(x + 1800, y + -625, mobSpawnChance);
spawn.randomGroup(x + 300, y + -300, mobSpawnChance);
spawn.randomGroup(x + 650, y + -275, mobSpawnChance);
spawn.randomGroup(x + 1125, y + -300, Infinity);
}
}
}
)
}
]
upDownOptions = [ //extra tall vertical section 3000x3000 //this is where the level boss is
(x = offset.x, y = offset.y) => { //mover
const button = level.button(x + 935, y + 0)
button.isUp = true
doCustomTopLayer.push(
() => {
button.draw();
if (button.isUp) {
button.query();
if (!button.isUp) {
const mapStartingLength = map.length //track this so you know how many you added when running addMapToLevelInProgress
addMapToLevelInProgress = (who) => { //adds new map elements to the level while the level is already running //don't forget to run simulation.draw.setPaths() after you all the the elements so they show up visually
who.collisionFilter.category = cat.map;
who.collisionFilter.mask = cat.player | cat.map | cat.body | cat.bullet | cat.powerUp | cat.mob | cat.mobBullet;
Matter.Body.setStatic(who, true); //make static
Composite.add(engine.world, who); //add to world
}
//map elements go here
//box around portals
spawn.mapRect(x + -50, y + -2700, 150, 110);
spawn.mapRect(x + -50, y + -2440, 150, 25);
spawn.mapRect(x + 1900, y + -2715, 150, 550);
spawn.mapRect(x + 1900, y + -2015, 150, 50);
spawn.mapRect(x + 1900, y + -1115, 150, 150);
spawn.mapRect(x + 1900, y + -815, 150, 50);
spawn.mapRect(x + -50, y + -340, 150, 50);
// spawn.mapRect(x + -50, y + -640, 150, 150);
spawn.mapRect(x + 1975, y - 1015, 50, 225);
spawn.mapRect(x + 1975, y - 2190, 50, 200);
spawn.mapRect(x + -25, y - 2615, 50, 200);
spawn.mapRect(x + -25, y - 515, 75, 200);
//ledge to get to upper left door
// spawn.mapRect(x + -50, y - 1400, 100, 25);
spawn.mapRect(x + -25, y - 1075, 250, 25);
spawn.mapRect(x + -50, y - 1075, 150, 590);
const rampSpeed = 8 //+ Math.floor(4 * Math.random())
const mover4 = level.mover(x, y + -2425, 1000, 50, rampSpeed)
const mover3 = level.mover(x + 1000, y + -2000, 1000, 50, rampSpeed)
const mover2 = level.mover(x + 1000, y + -800, 1000, 50, -rampSpeed)
const mover1 = level.mover(x, y + -325, 1000, 50, -rampSpeed)
const portal1 = level.portal({
x: x + 125,
y: y - 415
}, 2 * Math.PI, { //right
x: x + 125,
y: y - 2515
}, 2 * Math.PI) //right
const portal2 = level.portal({
x: x + 1875,
y: y - 890
}, Math.PI, { //left
x: x + 1875,
y: y - 2090
}, Math.PI) //left
doCustom.push(() => {
portal1[2].query()
portal1[3].query()
portal2[2].query()
portal2[3].query()
mover1.push();
mover2.push();
mover3.push();
mover4.push();
})
doCustomTopLayer.push(() => {
portal1[0].draw();
portal1[1].draw();
portal1[2].draw();
portal1[3].draw();
portal2[0].draw();
portal2[1].draw();
portal2[2].draw();
portal2[3].draw();
mover1.draw();
mover2.draw();
mover3.draw();
mover4.draw();
})
for (let i = 0, numberOfMapElementsAdded = map.length - mapStartingLength; i < numberOfMapElementsAdded; i++) addMapToLevelInProgress(map[map.length - 1 - i])
simulation.draw.setPaths() //update map graphics
//blocks that ride the movers and portals
spawn.bodyRect(x + 175, y + -2525, 50, 75);
spawn.bodyRect(x + 300, y + -2525, 50, 50);
spawn.bodyRect(x + 500, y + -2525, 80, 75);
//mobs go here
spawn.randomMob(x + 175, y + -125, 0);
spawn.randomMob(x + 1775, y + -125, 0);
// spawn.randomMob(x + 1750, y + -525, 0);
spawn.randomMob(x + 225, y + -1000, 0);
spawn.randomMob(x + 1675, y + -1075, 0);
// spawn.randomMob(x + 1575, y + -2450, 0);
spawn.randomMob(x + 425, y + -1850, 0);
spawn.randomMob(x + 1425, y + -1200, 0);
spawn.randomMob(x + 350, y + -1000, 0);
spawn.randomLevelBoss(x + 475, y + -1475);
spawn.secondaryBossChance(x + 1425, y + -1425);
}
}
}
)
},
(x = offset.x, y = offset.y) => { //hopBoss2
const button = level.button(x + 935, y + 0)
button.isUp = true
// spawn.mapVertex(x + 5, y + -1318, "0 0 0 -250 125 -250"); //left ledges
// spawn.mapVertex(x + 1995, y + -1318, "0 0 0 -250 -125 -250"); // right ledges
doCustomTopLayer.push(
() => {
button.draw();
if (button.isUp) {
button.query();
if (!button.isUp) {
const mapStartingLength = map.length //track this so you know how many you added when running addMapToLevelInProgress
addMapToLevelInProgress = (who) => { //adds new map elements to the level while the level is already running //don't forget to run simulation.draw.setPaths() after you all the the elements so they show up visually
who.collisionFilter.category = cat.map;
who.collisionFilter.mask = cat.player | cat.map | cat.body | cat.bullet | cat.powerUp | cat.mob | cat.mobBullet;
Matter.Body.setStatic(who, true); //make static
Composite.add(engine.world, who); //add to world
}
//map elements go here
spawn.mapRect(x + 150, y + -1400, 750, 50);
spawn.mapRect(x + 1100, y + -1400, 750, 50);
spawn.mapRect(x + 1825, y + -1050, 200, 50);
spawn.mapRect(x + -25, y + -1050, 200, 50);
spawn.mapRect(x + 1825, y + -325, 200, 50);
spawn.mapRect(x + -25, y + -325, 200, 50);
spawn.mapRect(x + 275, y + -700, 525, 50);
spawn.mapRect(x + 1200, y + -700, 525, 50);
spawn.mapRect(x + -25, y + -1400, 125, 1125); //side walls
spawn.mapRect(x + 1900, y + -1400, 150, 1125);
spawn.mapRect(x + 1900, y + -2700, 125, 1000);
spawn.mapRect(x + -50, y + -2725, 150, 1025);
spawn.mapRect(x + -25, y + -1750, 450, 50);
spawn.mapRect(x + 1575, y + -1750, 450, 50);
spawn.mapRect(x + 525, y + -1750, 950, 50);
for (let i = 0, numberOfMapElementsAdded = map.length - mapStartingLength; i < numberOfMapElementsAdded; i++) addMapToLevelInProgress(map[map.length - 1 - i])
simulation.draw.setPaths() //update map graphics
//mobs go here
powerUps.directSpawn(x + 50, y - 1525, "ammo");
powerUps.directSpawn(x + 1950, y - 1525, "ammo");
powerUps.directSpawn(x + 1900, y - 1525, "ammo");
spawn.hopMomBoss(x + 800, y + -2200)
for (let i = 0; i < 6; ++i) spawn.hopBullet(x + 150 + 750 * Math.random(), y + -1600)
for (let i = 0; i < 6; ++i) spawn.hopBullet(x + 1100 + 750 * Math.random(), y + -1600)
spawn.hopper(x + 1550, y + -775);
spawn.hopper(x + 500, y + -775);
spawn.hopper(x + 1400, y + -775);
spawn.hopper(x + 550, y + -775);
spawn.hopper(x + 525, y + -1475);
spawn.hopper(x + 1550, y + -1500);
}
}
}
)
},
(x = offset.x, y = offset.y) => {
// const toggle = level.toggle(x + 950, y + 0, false, true) // toggle(x, y, isOn = false, isLockOn = false) {
// toggle.isAddedElements = false
const button = level.button(x + 935, y + 0)
button.isUp = true
spawn.mapVertex(x + 5, y + -1318, "0 0 0 -250 125 -250"); //left ledges
spawn.mapVertex(x + 1995, y + -1318, "0 0 0 -250 -125 -250"); // right ledges
doCustomTopLayer.push(
() => {
button.draw();
if (button.isUp) {
button.query();
if (!button.isUp) {
// toggle.isAddedElements = true //only do this once
addMapToLevelInProgress = (who) => { //adds new map elements to the level while the level is already running //don't forget to run simulation.draw.setPaths() after you all the the elements so they show up visually
who.collisionFilter.category = cat.map;
who.collisionFilter.mask = cat.player | cat.map | cat.body | cat.bullet | cat.powerUp | cat.mob | cat.mobBullet;
Matter.Body.setStatic(who, true); //make static
Composite.add(engine.world, who); //add to world
}
let r = 150
let hexagon = `${r} 0 ${r * Math.cos(5.236)} ${r * Math.sin(5.236)} ${r * Math.cos(4.189)} ${r * Math.sin(4.189)} ${-r} 0 ${r * Math.cos(2.0944)} ${r * Math.sin(2.0944)} ${r * Math.cos(1.0472)} ${r * Math.sin(1.0472)} `
//450 horizontal spread // -130-130-130 = 390 vertical
if (Math.random() < 0.5) {
spawn.mapVertex(x + 775, y + -260, hexagon);
spawn.mapVertex(x + 1225, y + -260, hexagon);
spawn.mapVertex(x + 550, y + -650, hexagon);
spawn.mapVertex(x + 1000, y + -650, hexagon);
spawn.mapVertex(x + 1450, y + -650, hexagon);
spawn.mapVertex(x + 325, y + -1040, hexagon);
spawn.mapVertex(x + 775, y + -1040, hexagon);
spawn.mapVertex(x + 1225, y + -1040, hexagon);
spawn.mapVertex(x + 1675, y + -1040, hexagon);
spawn.mapVertex(x + 550, y + -1430, hexagon);
spawn.mapVertex(x + 1000, y + -1430, hexagon);
spawn.mapVertex(x + 1450, y + -1430, hexagon);
const numberOfMapElementsAdded = 12
for (let i = 0; i < numberOfMapElementsAdded; i++) addMapToLevelInProgress(map[map.length - 1 - i])
spawn.randomMob(x + 225, y + -1775, mobSpawnChance);
spawn.randomMob(x + 700, y + -1750, mobSpawnChance);
spawn.randomMob(x + 1175, y + -1725, mobSpawnChance);
spawn.randomMob(x + 1700, y + -1700, mobSpawnChance);
spawn.randomMob(x + 1750, y + -250, mobSpawnChance);
spawn.randomMob(x + 125, y + -250, mobSpawnChance);
} else {
spawn.mapVertex(x + 775, y + -260, hexagon);
spawn.mapVertex(x + 1225, y + -260, hexagon);
spawn.mapVertex(x + 550, y + -650, hexagon);
spawn.mapVertex(x + 1000, y + -650, hexagon);
spawn.mapVertex(x + 1450, y + -650, hexagon);
spawn.mapVertex(x + 775, y + -1040, hexagon);
spawn.mapVertex(x + 1225, y + -1040, hexagon);
spawn.mapVertex(x + 550, y + -1430, hexagon);
spawn.mapVertex(x + 1000, y + -1430, hexagon);
spawn.mapVertex(x + 1450, y + -1430, hexagon);
spawn.mapVertex(x + 775, y + -1820, hexagon);
spawn.mapVertex(x + 1225, y + -1820, hexagon);
const numberOfMapElementsAdded = 12
for (let i = 0; i < numberOfMapElementsAdded; i++) addMapToLevelInProgress(map[map.length - 1 - i])
spawn.randomMob(x + 225, y + -1025, mobSpawnChance);
spawn.randomMob(x + 250, y + -1025, mobSpawnChance);
spawn.randomMob(x + 200, y + -675, mobSpawnChance);
spawn.randomMob(x + 225, y + -200, mobSpawnChance);
spawn.randomMob(x + 1750, y + -1075, mobSpawnChance);
spawn.randomMob(x + 1700, y + -650, mobSpawnChance);
spawn.randomMob(x + 1725, y + -650, mobSpawnChance);
spawn.randomMob(x + 1675, y + -175, mobSpawnChance);
}
simulation.draw.setPaths() //update map graphics
spawn.randomGroup(x + 300, y + -2200);
spawn.randomGroup(x + 1625, y + -2200);
spawn.randomLevelBoss(x + 700, y + -2300);
spawn.secondaryBossChance(x + 1250, y + -2300)
}
}
// toggle.query();
// if (toggle.isOn && !toggle.isAddedElements) { //this code runs once after the toggle is triggered
// }
}
)
},
(x = offset.x, y = offset.y) => {
// const toggle = level.toggle(x + 950, y + 0, false, true) // toggle(x, y, isOn = false, isLockOn = false) {
// toggle.isAddedElements = false
const button = level.button(x + 935, y + 0)
button.isUp = true
//left ledges
spawn.mapVertex(x + 5, y + -1868, "0 0 0 -250 125 -250");
spawn.mapVertex(x + 5, y + -1318, "0 0 0 -250 125 -250"); //door
spawn.mapVertex(x + 5, y + -768, "0 0 0 -250 125 -250");
// right ledges
spawn.mapVertex(x + 2000, y + -1868, "0 0 0 -250 -125 -250");
spawn.mapVertex(x + 2000, y + -1318, "0 0 0 -250 -125 -250"); //door
spawn.mapVertex(x + 2000, y + -768, "0 0 0 -250 -125 -250");
doCustomTopLayer.push(
() => {
button.draw();
if (button.isUp) {
button.query();
if (!button.isUp) {
addMapToLevelInProgress = (who) => { //adds new map elements to the level while the level is already running //don't forget to run simulation.draw.setPaths() after you all the the elements so they show up visually
who.collisionFilter.category = cat.map;
who.collisionFilter.mask = cat.player | cat.map | cat.body | cat.bullet | cat.powerUp | cat.mob | cat.mobBullet;
Matter.Body.setStatic(who, true); //make static
Composite.add(engine.world, who); //add to world
}
//right side hexagons
let r = 300
let hexagon = `${r} 0 ${r * Math.cos(5.236)} ${r * Math.sin(5.236)} ${r * Math.cos(4.189)} ${r * Math.sin(4.189)} ${-r} 0 ${r * Math.cos(2.0944)} ${r * Math.sin(2.0944)} ${r * Math.cos(1.0472)} ${r * Math.sin(1.0472)} `
spawn.mapVertex(x + 1640, y + -365, hexagon);
// r = 275
// let hexagonHalf = `${r} 0 ${r*Math.cos(5.236)} ${r*Math.sin(5.236)} ${r*Math.cos(4.189)} ${r*Math.sin(4.189)} ${-r} 0 `
// spawn.mapVertex(x + 2300, y + -75, hexagonHalf);
r = 150
const hexagon150 = `${r} 0 ${r * Math.cos(5.236)} ${r * Math.sin(5.236)} ${r * Math.cos(4.189)} ${r * Math.sin(4.189)} ${-r} 0 ${r * Math.cos(2.0944)} ${r * Math.sin(2.0944)} ${r * Math.cos(1.0472)} ${r * Math.sin(1.0472)} `
// spawn.mapVertex(x + 1750, y + -550, hexagon150);
spawn.mapVertex(x + 1750, y + -1100, hexagon150);
spawn.mapVertex(x + 1750, y + -1650, hexagon150);
spawn.mapVertex(x + 1750, y + -2200, hexagon150);
//left side
r = 350
let hexagonHalf = `${r} 0 ${r * Math.cos(5.236)} ${r * Math.sin(5.236)} ${r * Math.cos(4.189)} ${r * Math.sin(4.189)} ${-r} 0 `
spawn.mapVertex(x + 425, y + -90, hexagonHalf);
spawn.mapVertex(x + 850, y + -500, hexagon150);
spawn.mapVertex(x + 550, y + -850, hexagon150);
spawn.mapVertex(x + 250, y + -1200, hexagon150);
spawn.mapVertex(x + 250, y + -1700, hexagon150);
spawn.mapVertex(x + 725, y + -1950, hexagon150);
spawn.mapVertex(x + 1200, y + -2200, hexagon150);
const numberOfMapElementsAdded = 11
for (let i = 0; i < numberOfMapElementsAdded; i++) addMapToLevelInProgress(map[map.length - 1 - i])
spawn.randomMob(x + 1050, y + -1500, mobSpawnChance);
spawn.randomMob(x + 1075, y + -1500, mobSpawnChance);
spawn.randomMob(x + 325, y + -550, mobSpawnChance);
spawn.randomMob(x + 800, y + -925, mobSpawnChance);
spawn.randomMob(x + 1400, y + -1250, mobSpawnChance);
spawn.randomMob(x + 1325, y + -1725, mobSpawnChance);
spawn.randomMob(x + 1350, y + -1725, mobSpawnChance);
spawn.randomMob(x + 575, y + -1375, mobSpawnChance);
spawn.randomMob(x + 225, y + -2275, mobSpawnChance);
spawn.randomMob(x + 875, y + -2450, mobSpawnChance);
spawn.randomMob(x + 1550, y + -2525, mobSpawnChance);
spawn.randomMob(x + 1525, y + -2525, mobSpawnChance);
spawn.randomLevelBoss(x + 1075, y + -1500);
spawn.secondaryBossChance(x + 1200, y + -1000)
simulation.draw.setPaths() //update map graphics
}
}
}
)
},
// (x = offset.x, y = offset.y) => {
// const elevator1 = level.elevator(x + 1100, y - 200, 250, 30, -2100, 0.0015) // x, y, width, height, maxHeight, force = 0.003, friction = { up: 0.01, down: 0.2 }, isTeleport = false) {
// // const elevator1 = level.elevator(x + 175, y - 200, 250, 30, -1400, 0.001)
// // const elevator2 = level.elevator(x + 2175, y - 200, 250, 30, -1400, 0.001)
// spawn.mapRect(-200, -1400, 350, 50); //up left door ledge
// spawn.mapRect(2450, -1400, 350, 50); //up right door ledge
// spawn.mapRect(225, -450, 350, 350); //left crawl zone
// // spawn.mapRect(725, -175, 275, 75);
// spawn.mapRect(725, -225, 350, 100);
// spawn.mapRect(275, -750, 200, 200);
// spawn.mapRect(1375, -700, 500, 750); //right side big elevator wall
// spawn.mapRect(2375, -325, 350, 50);
// spawn.mapRect(1800, -500, 250, 50);
// //up high elevator
// spawn.mapRect(1375, -2100, 500, 175);
// spawn.mapRect(600, -2100, 475, 175);
// if (simulation.difficulty > 3) spawn.randomLevelBoss(x + 1250, y - 1400);
// doCustomTopLayer.push(
// () => {
// elevator1.move()
// }
// )
// }
]
//pick which type of room spawns
enter = enterOptions[Math.floor(Math.random() * enterOptions.length)];
exit = exitOptions[Math.floor(Math.random() * exitOptions.length)];
empty = emptyOptions[Math.floor(Math.random() * emptyOptions.length)];
loot = lootOptions[Math.floor(Math.random() * lootOptions.length)];
upDown = upDownOptions[Math.floor(Math.random() * upDownOptions.length)];
// upDown = upDownOptions[0] //controls what level spawns for map designing building //********************************* DO !NOT! RUN THIS LINE IN THE FINAL VERSION ***************************************
//3x2: 4 short rooms (3000x1500), 1 double tall room (3000x3000)
//rooms
let rooms = ["exit", "loot", "enter", "empty"]
rooms = shuffle(rooms); //shuffles array order
//look... you and I both know there is a better way to do this, but it works so I'm gonna focus on other things
while ( //makes sure that the exit and entrance aren't both on the same floor
(rooms[0] === "enter" && rooms[2] === "exit") ||
(rooms[2] === "enter" && rooms[0] === "exit") ||
(rooms[1] === "enter" && rooms[3] === "exit") ||
(rooms[3] === "enter" && rooms[1] === "exit")
) rooms = shuffle(rooms); //shuffles array order
for (let i = 0; i < rooms.length; i++) {
if (rooms[i] === "enter") rooms[i] = enter
if (rooms[i] === "exit") rooms[i] = exit
if (rooms[i] === "empty") rooms[i] = empty
if (rooms[i] === "loot") rooms[i] = loot
}
// rooms = [enter, exit, loot, empty, ] //controls what level spawns for map designing building //********************************* DO !NOT! RUN THIS LINE IN THE FINAL VERSION ***************************************
outline = (isLower = true) => {
spawn.mapRect(offset.x - 100, offset.y - 1400, 2100, 100); //ceiling
if (isLower) spawn.mapRect(offset.x - 100, offset.y, 2200, 100); //only draw floor if on the lower level
if (!isDoorLeft) spawn.mapRect(offset.x - 100, offset.y - 1400, 100, 1500); //left wall
if (isDoorRight) { //if door only add wall on right side
spawn.mapRect(offset.x + 2000, offset.y - 1400, 100, 1225); //right wall
spawn.mapRect(offset.x + 2000, offset.y - 10, 100, 20); //right doorstep
const doorWidth = 15 + Math.floor(100 * Math.random() * Math.random())
spawn.bodyRect(offset.x + 2050 - doorWidth / 2, offset.y - 175, doorWidth, 165); //block door
} else {
spawn.mapRect(offset.x + 2000, offset.y - 1400, 100, 1500); //right wall
}
}
outlineUpDown = () => {
spawn.mapRect(offset.x - 100, offset.y + 0, 2100, 100); //floor
spawn.mapRect(offset.x - 100, offset.y - 2800, 2100, 100); //ceiling
if (!isDoorLeft) spawn.mapRect(offset.x - 100, offset.y - 2800, 100, 2900); //left wall
if (isDoorRight) { //if door only add wall on right side
//upper door
spawn.mapRect(offset.x + 2000, offset.y - 2800, 100, 1225); //right wall
spawn.mapRect(offset.x + 2000, offset.y - 1410, 100, 20); //right doorstep
const doorWidth = 15 + Math.floor(100 * Math.random() * Math.random())
spawn.bodyRect(offset.x + 2050 - doorWidth / 2, offset.y - 1575, doorWidth, 165); //block door
//lower door
spawn.mapRect(offset.x + 2000, offset.y - 1400, 100, 1225); //right wall
spawn.mapRect(offset.x + 2000, offset.y - 10, 100, 20); //right doorstep
const doorWidth2 = 15 + Math.floor(100 * Math.random() * Math.random())
spawn.bodyRect(offset.x + 2050 - doorWidth2 / 2, offset.y - 175, doorWidth2, 165); //block door
} else {
spawn.mapRect(offset.x + 2000, offset.y - 2800, 100, 2900); //right wall
}
}
let columns = [
() => {
offset.y = 0
outlineUpDown()
upDown()
},
() => {
offset.y = 0
outline()
rooms[0]()
offset.y = -1400
outline(false)
rooms[1]()
},
() => {
offset.y = 0
outline()
rooms[2]()
offset.y = -1400
outline(false)
rooms[3]()
},
]
columns = shuffle(columns) //********************************* RUN THIS LINE IN THE FINAL VERSION ***************************************
for (let i = 0; i < 3; i++) {
if (i === 0) {
isDoorLeft = false
isDoorRight = true
} else if (i === 1) {
isDoorLeft = true
isDoorRight = true
} else {
isDoorLeft = true
isDoorRight = false
}
offset.x = i * 2100
columns[i]()
}
level.custom = () => {
for (let i = 0, len = doCustom.length; i < len; i++) doCustom[i]() //runs all the active code from each room
level.exit.drawAndCheck();
level.enter.draw();
};
level.customTopLayer = () => {
for (let i = 0, len = doCustomTopLayer.length; i < len; i++) doCustomTopLayer[i]() //runs all the active code from each room
};
powerUps.addResearchToLevel() //needs to run after mobs are spawned
},
pavilion() {
const vanish = []
level.exit.x = -850;
level.exit.y = -1485;
spawn.mapRect(level.exit.x, level.exit.y + 25, 100, 25);
level.setPosToSpawn(-900, 225); //normal spawn
spawn.mapRect(level.enter.x, level.enter.y + 20, 100, 20);
level.defaultZoom = 1500
simulation.zoomTransition(level.defaultZoom)
document.body.style.backgroundColor = "#dcdcde";
spawn.debris(-150, -775, 1425, 3); //16 debris per level
spawn.debris(1525, -25, 950, 3); //16 debris per level
spawn.debris(-650, -2100, 575, 2); //16 debris per level
//bottom floor
//entrance
spawn.mapRect(-200, -750, 1500, 100);
spawn.mapRect(-575, 0, 2150, 500);
// spawn.mapRect(-1275, 275, 875, 225);
spawn.mapRect(-1275, 275, 3975, 225);
spawn.mapRect(-1050, 0, 325, 50);
spawn.mapRect(-775, 0, 50, 140);
vanish.push(level.vanish(-725, 13, 150, 25))
spawn.mapRect(-200, -750, 100, 600);
// spawn.mapRect(1200, -750, 100, 600);
vanish.push(level.vanish(-350, -225, 150, 225))
vanish.push(level.vanish(-350, -450, 150, 223))
spawn.mapRect(2475, -1800, 250, 2300);
spawn.mapRect(1200, -750, 100, 450);
spawn.mapRect(1200, -375, 250, 75);
powerUps.spawnStartingPowerUps(550, -100);
spawn.mapRect(125, -12, 850, 50);
spawn.mapRect(175, -25, 750, 50);
spawn.bodyRect(1350, -175, 150, 175, 0.5);
spawn.bodyRect(1350, -600, 125, 225, 0.2);
//middle floor
spawn.bodyRect(215, -1175, 100, 100, 0.3);
spawn.mapRect(-1300, -1800, 250, 2300);
// spawn.mapRect(-1300, -2075, 250, 2575);
if (Math.random() < 0.5) {
spawn.mapRect(500, -1350, 525, 425);
spawn.mapRect(25, -1050, 300, 198);
} else {
spawn.mapRect(500, -1350, 525, 497);
spawn.mapRect(25, -1050, 300, 150);
}
if (Math.random() < 0.5) {
vanish.push(level.vanish(400, -1600, 175, 25))
vanish.push(level.vanish(950, -1600, 175, 25))
} else {
vanish.push(level.vanish(550, -1575, 50, 225))
vanish.push(level.vanish(925, -1575, 50, 225))
}
// vanish.push(level.vanish(575, -1575, 375, 225))
spawn.bodyRect(225, -850, 50, 100, 0.4);
spawn.mapRect(600, -1800, 325, 225);
spawn.mapRect(1900, -1500, 325, 25);
spawn.bodyRect(1050, -1825, 250, 20, 0.2);
if (Math.random() < 0.5) {
vanish.push(level.vanish(1400, -1000, 200, 25))
vanish.push(level.vanish(1625, -1250, 200, 25))
} else {
vanish.push(level.vanish(1400, -1075, 175, 175))
vanish.push(level.vanish(1575, -1250, 175, 175))
}
vanish.push(level.vanish(1125, -1800, 625, 25))
// vanish.push(level.vanish(1500, -1800, 225, 25))
vanish.push(level.vanish(-50, -1800, 450, 25))
//exit
spawn.mapRect(-1050, -1450, 700, 25);
spawn.mapRect(-1050, -1800, 525, 25);
spawn.mapRect(-550, -1800, 25, 200);
spawn.randomMob(-1175, -1975, -0.4);
spawn.randomMob(275, -1500, -0.3);
spawn.randomMob(700, -1875, -0.2);
spawn.randomMob(2000, -800, -0.2);
spawn.randomMob(2600, -1850, 0);
spawn.randomMob(1425, -525, 0.1);
spawn.randomMob(2025, -1600, 0.3);
spawn.randomMob(1625, -1875, 0.3);
spawn.randomMob(-150, -1975, 0.4);
spawn.randomSmallMob(900, -825);
spawn.randomSmallMob(1050, -50);
if (simulation.difficulty > 1) {
spawn.randomGroup(750, -2150, -0.8)
spawn.randomLevelBoss(2050, -2025)
spawn.secondaryBossChance(100, -1500)
}
powerUps.addResearchToLevel() //needs to run after mobs are spawned
if (simulation.isHorizontalFlipped) { //flip the map horizontally
level.flipHorizontal(); //only flips map,body,mob,powerUp,cons,consBB, exit
level.setPosToSpawn(900, 225); //normal spawn
level.custom = () => {
ctx.fillStyle = "#d0d3d9"
ctx.fillRect(-2500, -1800, 3575, 2100);
ctx.fillStyle = "#c0c3c9"
ctx.fillRect(-2075, -1475, 25, 1800);
ctx.fillStyle = "#cff" //exit
ctx.fillRect(550, -1800, 525, 350)
level.exit.drawAndCheck();
level.enter.draw();
};
level.customTopLayer = () => {
//shadow
ctx.fillStyle = "rgba(0,10,30,0.1)"
ctx.fillRect(-1450, -300, 150, 325);
ctx.fillRect(-1300, -650, 1500, 650)
ctx.fillRect(725, 50, 325, 225)
ctx.fillRect(-325, -950, 300, 225)
ctx.fillRect(-1025, -1000, 525, 275);
ctx.fillRect(-925, -1600, 325, 275);
for (let i = 0, len = vanish.length; i < len; i++) vanish[i].query()
};
} else {
level.custom = () => {
ctx.fillStyle = "#d0d3d9"
ctx.fillRect(-1075, -1800, 3575, 2100);
ctx.fillStyle = "#c0c3c9"
ctx.fillRect(2050, -1475, 25, 1800);
ctx.fillStyle = "#cff" //exit
ctx.fillRect(-1050, -1800, 525, 350)
level.exit.drawAndCheck();
level.enter.draw();
};
level.customTopLayer = () => {
//shadow
ctx.fillStyle = "rgba(0,10,30,0.1)"
ctx.fillRect(1300, -300, 150, 325);
ctx.fillRect(-200, -675, 1500, 700)
ctx.fillRect(500, -950, 525, 225);
ctx.fillRect(600, -1600, 325, 275);
ctx.fillRect(-1050, 50, 325, 225)
ctx.fillRect(25, -950, 300, 225)
for (let i = 0, len = vanish.length; i < len; i++) vanish[i].query()
};
}
},
testChamber() {
level.setPosToSpawn(0, -50); //lower start
level.exit.y = level.enter.y - 550;
spawn.mapRect(level.enter.x, level.enter.y + 20, 100, 20);
level.exit.x = level.enter.x;
spawn.mapRect(level.exit.x, level.exit.y + 20, 100, 20);
level.defaultZoom = 2200
simulation.zoomTransition(level.defaultZoom)
document.body.style.backgroundColor = "#d0d5d5";
color.map = "#444"
spawn.mapRect(0, -1955, 175, 30);
const removeIndex1 = map.length - 1 //so much work to catch blocks caught at the bottom of the vertical portals
spawn.mapRect(1225, -1955, 175, 30);
const removeIndex2 = map.length - 1 //so much work to catch blocks caught at the bottom of the vertical portals
let portal, portal2, portal3
const hazard = level.hazard((simulation.isHorizontalFlipped ? -350 - 700 : 350), -2025, 700, 10, 0.4) //laser
spawn.mapRect(340, -2032.5, 20, 25); //laser nose
const hazard2 = level.hazard((simulation.isHorizontalFlipped ? -1775 - 150 : 1775), -2550, 150, 10, 0.4) //laser
spawn.mapRect(1920, -2557.5, 20, 25); //laser nose
const button = level.button(2100, -2600)
const buttonDoor = level.button(600, -550)
const door = level.door(312, -750, 25, 190, 185)
level.custom = () => {
if (!(m.cycle % 60)) { //so much work to catch blocks caught at the bottom of the vertical portals
let touching = Matter.Query.collides(map[removeIndex1], body)
if (touching.length) {
Matter.Composite.remove(engine.world, touching[0].bodyB);
for (let i = 0, len = body.length; i < len; i++) {
if (body[i].id === touching[0].bodyB.id) {
body.splice(i, 1);
break
}
}
}
touching = Matter.Query.collides(map[removeIndex2], body)
if (touching.length) {
Matter.Composite.remove(engine.world, touching[0].bodyB);
for (let i = 0, len = body.length; i < len; i++) {
if (body[i].id === touching[0].bodyB.id) {
body.splice(i, 1);
break
}
}
}
}
buttonDoor.query();
buttonDoor.draw();
if (buttonDoor.isUp) {
door.isClosing = true
} else {
door.isClosing = false
}
door.openClose();
portal[2].query()
portal[3].query()
portal2[2].query()
portal2[3].query()
portal3[2].query()
portal3[3].query()
if (button.isUp) {
hazard.isOn = false;
hazard2.isOn = false;
} else {
hazard.isOn = true;
hazard2.isOn = true;
}
button.query();
button.draw();
ctx.fillStyle = "#d4f4f4"
ctx.fillRect(-300, -1000, 650, 500)
level.exit.drawAndCheck();
level.enter.draw();
};
level.customTopLayer = () => {
door.draw();
hazard.opticalQuery();
hazard2.opticalQuery();
portal[0].draw();
portal[1].draw();
portal[2].draw();
portal[3].draw();
portal2[0].draw();
portal2[1].draw();
portal2[2].draw();
portal2[3].draw();
portal3[0].draw();
portal3[1].draw();
portal3[2].draw();
portal3[3].draw();
};
powerUps.spawnStartingPowerUps(1875, -3075);
const powerUpPos = shuffle([{ //no debris on this level but 2 random spawn instead
x: -150,
y: -1775
}, {
x: 2400,
y: -2650
}, {
x: -175,
y: -1375
}, {
x: 1325,
y: -150
}]);
powerUps.chooseRandomPowerUp(powerUpPos[0].x, powerUpPos[0].y);
powerUps.chooseRandomPowerUp(powerUpPos[1].x, powerUpPos[1].y);
//outer wall
spawn.mapRect(2500, -3700, 1200, 3800); //right map wall
spawn.mapRect(-1400, -3800, 1100, 3900); //left map wall
spawn.mapRect(-1400, -4800, 5100, 1200); //map ceiling
spawn.mapRect(-1400, 0, 5100, 1200); //floor
//lower entrance /exit
spawn.mapRect(300, -375, 50, 225);
spawn.bodyRect(312, -150, 25, 140);
spawn.mapRect(300, -10, 50, 50);
spawn.mapVertex(1555, 0, "625 0 75 0 200 -100 500 -100"); //entrance ramp
//upper entrance / exit
spawn.mapRect(-400, -1050, 750, 50);
spawn.mapRect(300, -1050, 50, 300);
// spawn.bodyRect(312, -750, 25, 190);
spawn.mapRect(300, -560, 50, 50);
spawn.bodyRect(750, -725, 125, 125);
spawn.mapRect(1150, -1050, 250, 575);
spawn.mapRect(1725, -550, 50, 200); //walls around portal 3
spawn.mapRect(1925, -550, 500, 200);
spawn.mapRect(1750, -390, 200, 40);
spawn.mapRect(-400, -550, 1800, 200);
spawn.mapRect(-200, -1700, 150, 25); //platform above exit room
spawn.mapRect(-200, -1325, 350, 25);
//portal 3 angled
spawn.mapRect(2425, -450, 100, 100);
//portal 1 bottom
spawn.mapRect(2290, -12, 375, 100);
spawn.mapRect(2350, -24, 375, 100);
spawn.mapRect(2410, -36, 375, 100);
//portal 1 top
spawn.mapRect(2290, -3012, 375, 50);
spawn.mapRect(2350, -3024, 375, 50);
spawn.mapRect(2410, -3036, 375, 50);
spawn.mapRect(1400, -3000, 1300, 50); //floor
spawn.mapRect(1750, -3050, 250, 75);
spawn.mapRect(1400, -3625, 50, 200);
spawn.mapRect(350, -3625, 50, 225);
spawn.mapRect(350, -3260, 50, 60);
spawn.mapRect(200, -3250, 1240, 50);
spawn.mapRect(1400, -3260, 50, 310);
spawn.bodyRect(1412, -3425, 25, 165);
spawn.mapRect(-150, -2925, 150, 25);
//portal 2
spawn.mapRect(-300, -2600, 300, 675); //left platform
spawn.mapRect(1400, -2600, 375, 675); //right platform
spawn.mapRect(1925, -2600, 775, 675); //far right platform
spawn.bodyRect(2130, -2660, 50, 50); //button's block
spawn.mapRect(150, -2100, 200, 175);
spawn.mapRect(1050, -2100, 200, 175);
//mobs
spawn.randomMob(1075, -3500, -0.3);
spawn.randomMob(2175, -700, -0.2);
spawn.randomMob(-75, -850, -0.1);
spawn.randomMob(550, -3400, 0);
spawn.randomMob(0, -1175, 0.5);
spawn.randomMob(-75, -1150, 0.5);
spawn.randomMob(1075, -625, 0.5);
spawn.randomMob(800, -3400, -0.3);
spawn.randomMob(1225, -3375, -0.2);
spawn.randomMob(1200, -1125, -0.1);
spawn.randomMob(2050, -950, 0.5);
if (simulation.difficulty > 40) {
spawn.randomMob(2300, -2775, -0.5);
spawn.randomMob(600, -925, -0.5);
spawn.randomMob(1550, -2750, -0.5);
spawn.randomMob(1350, -1150, -0.5);
spawn.randomMob(-75, -1475, 0);
spawn.randomGroup(600, -2600, 0);
}
if (simulation.difficulty > 1) {
if (Math.random() < 0.5) {
spawn.randomLevelBoss(700, -1550);
} else {
spawn.randomLevelBoss(675, -2775); //["shooterBoss", "launcherBoss", "laserTargetingBoss", "streamBoss", "shieldingBoss", "pulsarBoss", "grenadierBoss"]
}
}
powerUps.addResearchToLevel() //needs to run after mobs are spawned
spawn.secondaryBossChance(1925, -1250)
if (simulation.isHorizontalFlipped) { //flip the map horizontally
level.flipHorizontal(); //only flips map,body,mob,powerUp,cons,consBB, exit
// level.setPosToSpawn(0, -50); //-x // no need since 0
button.min.x = -button.min.x - 126 // flip the button horizontally
button.max.x = -button.max.x + 126 // flip the button horizontally
buttonDoor.min.x = -buttonDoor.min.x - 126 // flip the button horizontally
buttonDoor.max.x = -buttonDoor.max.x + 126 // flip the button horizontally
//this makes the hazard draw, but not collide for reasons I don't understand
//so don't use it, instead just call the hazard differently based on this flip flag
// hazard.min.x = -hazard.min.x - hazard.width //-x-width
// hazard.max.x = -hazard.max.x - hazard.width //-x-width
// hazard2.min.x = -hazard2.min.x - hazard2.width //-x-width
// hazard2.max.x = -hazard2.max.x - hazard2.width //-x-width
portal = level.portal({
x: -2475,
y: -140
}, 2 * Math.PI, { //right
x: -2475,
y: -3140
}, 2 * Math.PI) //right
portal2 = level.portal({
x: -75,
y: -2150
}, -Math.PI / 2, { //up
x: -1325,
y: -2150
}, -Math.PI / 2) //up
portal3 = level.portal({
x: -1850,
y: -585
}, -Math.PI / 2, { //up
x: -2425,
y: -600
}, -1 * Math.PI / 3) //up left
// level.custom = () => { };
// level.customTopLayer = () => {};
} else {
portal = level.portal({
x: 2475,
y: -140
}, Math.PI, { //left
x: 2475,
y: -3140
}, Math.PI) //left
portal2 = level.portal({
x: 75,
y: -2150
}, -Math.PI / 2, { //up
x: 1325,
y: -2150
}, -Math.PI / 2) //up
portal3 = level.portal({
x: 1850,
y: -585
}, -Math.PI / 2, { //up
x: 2425,
y: -600
}, -2 * Math.PI / 3) //up left
}
},
lock() {
level.setPosToSpawn(0, -65); //lower start
spawn.mapRect(level.enter.x, level.enter.y + 20, 100, 20);
level.exit.y = 2010;
level.exit.x = 2625;
spawn.mapRect(level.exit.x, level.exit.y + 20, 100, 20);
level.defaultZoom = 2200
simulation.zoomTransition(level.defaultZoom)
document.body.style.backgroundColor = "hsl(138, 5%, 82%)";
color.map = "#444"
powerUps.spawnStartingPowerUps(1768, 870); //on left side
const portal = level.portal({ x: 1070, y: -1485 }, -0.9, { x: 475, y: 50 }, -Math.PI / 2)
const doorCenterRight = level.door(2787, 775, 25, 225, 195, 5) //x, y, width, height, distance, speed = 1
const doorCenterLeft = level.door(2537, 775, 25, 225, 195, 5)
const doorButtonRight = level.door(4462, 1010, 25, 225, 195, 5)
const doorLeft = level.door(2538, 1825, 25, 225, 195, 5)
const buttonLeft = level.button(4565, 1235)
const buttonRight = level.button(4142, -355)
// spawn.mapRect(4000, -350, 700, 125); //button platform
spawn.mapRect(4000, -350, 600, 75);
buttonLeft.isUp = true
buttonRight.isUp = true
const hazardSlimeLeft = level.hazard(900, -300, 1638, 2450) //hazard(x, y, width, height, damage = 0.002) {
const hazardSlimeRight = level.hazard(2812, -300, 1650, 2450) //hazard(x, y, width, height, damage = 0.002) {
//set slime to empty
// hazardSlimeLeft.height -= hazardSlimeLeft.maxHeight //start slime at zero
// hazardSlimeLeft.min.y += hazardSlimeLeft.maxHeight
// hazardSlimeLeft.max.y = hazardSlimeLeft.min.y + hazardSlimeLeft.height
// hazardSlimeRight.height -= hazardSlimeRight.maxHeight //start slime at zero
// hazardSlimeRight.min.y += hazardSlimeRight.maxHeight
// hazardSlimeRight.max.y = hazardSlimeRight.min.y + hazardSlimeRight.height
const balance = []
level.custom = () => {
ctx.fillStyle = "hsl(175, 35%, 76%)" //exit
ctx.fillRect(2537, 1775, 275, 275)
level.exit.drawAndCheck();
level.enter.draw();
doorButtonRight.isClosing = hazardSlimeRight.min.y < 1275
doorCenterRight.isClosing = hazardSlimeRight.min.y < 1000
doorCenterLeft.isClosing = hazardSlimeLeft.min.y < 1000
doorLeft.isClosing = hazardSlimeLeft.min.y < 2050
doorButtonRight.openClose();
doorCenterRight.openClose();
doorCenterLeft.openClose();
doorLeft.openClose();
if (buttonRight.isUp) {
buttonRight.query();
if (!buttonRight.isUp) spawnRightMobs()
}
if (buttonLeft.isUp) {
buttonLeft.query();
if (!buttonLeft.isUp) spawnLeftMobs()
}
buttonRight.draw();
buttonLeft.draw();
if (hazardSlimeLeft.min.y < 2050) {
const drainRate = Math.min(Math.max(0.25, 4 - hazardSlimeLeft.min.y / 500), 4)
hazardSlimeLeft.level(buttonLeft.isUp, drainRate)
}
if (hazardSlimeRight.min.y < 2050) {
const drainRate = Math.min(Math.max(0.25, 4 - hazardSlimeRight.min.y / 500), 4)
hazardSlimeRight.level(buttonRight.isUp, drainRate)
}
portal[2].query()
portal[3].query()
};
level.customTopLayer = () => {
doorButtonRight.draw();
doorCenterRight.draw();
doorCenterLeft.draw();
doorLeft.draw();
hazardSlimeLeft.query();
hazardSlimeRight.query();
portal[0].draw();
portal[1].draw();
portal[2].draw();
portal[3].draw();
ctx.fillStyle = color.map //below portal
ctx.fillRect(375, 150, 200, 2525);
ctx.fillStyle = "rgba(0,0,0,0.1)" //shadows
ctx.fillRect(-250, -1550, 1250, 1575);
ctx.fillRect(2537, -350, 275, 2425);
ctx.fillStyle = "rgba(0,0,0,0.05)" //exit
ctx.fillRect(-175, -300, 375, 300)
ctx.fillRect(4460, 950, 350, 325);
ctx.fillStyle = "#233" //balances center dot
ctx.beginPath();
for (let i = 0; i < balance.length; i++) {
ctx.arc(balance[i].center.x, balance[i].center.y, 9, 0, 2 * Math.PI);
}
ctx.fill();
// for (let i = 0, len = vanish.length; i < len; i++) vanish[i].query()
};
//entrance and outer walls
spawn.mapRect(-1400, 0, 1800, 2675);
spawn.mapRect(-1400, -1025, 1225, 1500);
spawn.mapRect(-325, -15, 525, 225);
spawn.mapRect(150, -350, 50, 175);
spawn.mapRect(-1400, -3525, 1600, 3225);
spawn.mapRect(550, 0, 450, 2675);
spawn.mapRect(550, -1550, 450, 125);
spawn.mapRect(150, -1550, 250, 125);
spawn.mapRect(750, -1425, 1100, 175);
spawn.mapRect(750, -1400, 250, 825);
spawn.mapRect(750, -350, 250, 575);
spawn.mapRect(625, 2100, 4300, 575); //floor
spawn.mapRect(-1400, -4425, 7250, 1000); //ceiling
// const vanish = []
// vanish.push(level.vanish(400, -1512, 150, 50))
// vanish.push(level.vanish(825, -625, 100, 325))
//left button room (on the far right in the
spawn.mapRect(4450, -3525, 1400, 4500);
spawn.mapRect(4450, 1235, 1400, 1440);
spawn.mapRect(4775, 750, 1075, 825);
spawn.mapRect(4450, 950, 50, 75);
//other ideas for left and right alternate setups
//just a floor covered with boosts
//something focused on funnel shapes
//several rooms with tunnels connecting them
//spinners
//right side
if (Math.random() < 1) {
spawn.mapVertex(3350, 350, "-100 0 100 0 100 700 0 750 -100 700");
balance.push(level.rotor(3463, 150, 300, 25, 0.001, 0)) //balance(x, y, width, height, density = 0.001, angle = 0, frictionAir = 0.001, angularVelocity = 0, rotationForce = 0.0005) {
balance.push(level.rotor(3463, 500, 300, 25, 0.001, 0))
spawn.mapVertex(3875, 350, "-100 0 100 0 100 700 0 750 -100 700");
spawn.mapVertex(2900, 1743, "-100 0 70 0 100 30 100 1000 -100 1000");
spawn.mapVertex(3025, 1811, "-150 0 120 0 150 30 150 600 -150 600");
spawn.mapVertex(3200, 2079, "-150 0 120 0 150 30 150 600 -150 600");
spawn.mapVertex(4425, 1743, "-150 30 -120 0 150 0 150 1000 -150 1000");
spawn.mapVertex(4250, 1811, "-150 30 -120 0 150 0 150 600 -150 600");
spawn.mapVertex(4075, 2079, "-150 30 -120 0 150 0 150 600 -150 600");
//escape ledge when drowning
spawn.mapRect(2750, 525, 100, 25);
spawn.mapRect(2750, 125, 100, 25);
spawn.mapRect(4425, 800, 75, 25);
spawn.mapRect(4425, 325, 75, 25);
// spawn.mapRect(4425, -100, 75, 25);
// spawn.mapRect(4425, -550, 75, 25);
// spawn.mapRect(4425, -1000, 75, 25);
// if (Math.random() < 0.5) {
// spawn.mapRect(2775, 525, 100, 25);
// spawn.mapRect(3200, 75, 125, 25);
// } else {
// spawn.mapRect(4400, 800, 100, 25);
// spawn.mapRect(3925, 400, 100, 25);
// }
}
//left side
if (Math.random() < 1) {
// spawn.mapVertex(2325, 1325, "-150 0 150 0 150 150 0 225 -150 150");
spawn.mapVertex(1285, 1275, "-150 0 150 0 150 150 0 225 -150 150");
spawn.mapVertex(1033, 1750, "0 200 200 200 300 50 300 -50 200 -200 0 -200");
spawn.mapVertex(1575, 1750, "0 200 -200 200 -300 50 -300 -50 -200 -200 0 -200 100 -50 100 50"); //larger "0 400 -250 400 -400 100 -400 -100 -250 -400 0 -400"
spawn.mapVertex(1287, 2185, "-100 30 -80 0 80 0 100 30 100 300 -100 300");
spawn.mapVertex(2050, 2050, "-150 30 -120 0 120 0 150 30 150 300 -150 300");
// spawn.mapRect(1700, 1550, 275, 25);
// spawn.mapRect(2175, 1275, 325, 25);
spawn.mapRect(1600, 950, 375, 25);
spawn.mapRect(1025, -50, 50, 25);
spawn.mapRect(1025, 275, 175, 25);
spawn.mapRect(1025, 600, 325, 25);
spawn.mapRect(2450, -50, 50, 25);
spawn.mapRect(2325, 275, 175, 25);
spawn.mapRect(2175, 600, 325, 25);
// spawn.mapVertex(3400, 1250, "-100 -300 0 -350 100 -300 100 300 0 350 -100 300");
}
//left button room in center divider
spawn.mapRect(2525, -350, 300, 1100);
spawn.mapRect(2525, 975, 300, 800);
spawn.mapRect(2775, 650, 50, 125);
spawn.mapRect(2525, 650, 50, 125);
//exit room
spawn.mapRect(2475, 2040, 350, 200);
spawn.mapRect(2800, 1750, 25, 325);
spawn.mapRect(2525, 1750, 50, 75);
//safety edge blocks //maybe remove?
// spawn.mapRect(2525, -375, 25, 50);
// spawn.mapRect(2800, -375, 25, 50);
// spawn.mapRect(1825, -1450, 25, 50);
// spawn.mapRect(4000, -375, 25, 50);
//blocks
spawn.bodyRect(150, -175, 50, 165, 0.2); //block at entrance
spawn.bodyRect(1275, 825, 100, 100, 0.2);
spawn.bodyRect(2600, -425, 150, 50, 0.2);
spawn.bodyRect(3900, -150, 50, 100, 0.2);
spawn.bodyRect(3350, 1950, 75, 100, 0.2);
spawn.bodyRect(3850, 1975, 75, 75, 0.2);
spawn.bodyRect(1600, 1950, 75, 100, 0.2);
spawn.bodyRect(725, -1650, 150, 100, 0.2);
spawn.bodyRect(800, -1700, 75, 50, 0.2);
const spawnRightMobs = () => {
spawn.randomMob(4200, 100, 1);
spawn.randomMob(4200, 600, 1);
spawn.randomMob(2975, 625, 0.5);
spawn.randomMob(3050, 100, 0.5);
spawn.randomMob(3400, -100, 0.4);
spawn.randomMob(3825, -100, 0.4);
spawn.randomMob(3625, 1950, 0.4);
spawn.randomMob(3275, 1650, 0.4);
spawn.randomMob(3075, 1375, 0.3);
spawn.randomMob(4000, 1650, 0.1);
spawn.randomMob(4100, 1425, 0);
spawn.randomGroup(3025, 325, 1);
if (simulation.difficulty > 1) spawn.secondaryBossChance(3520, 1169)
}
const spawnLeftMobs = () => {
spawn.randomMob(2375, 1900, 1);
spawn.randomMob(1825, 1325, 0.5);
spawn.randomMob(2250, 1050, 0.5);
spawn.randomMob(1675, 825, 0.4);
spawn.randomMob(1250, 575, 0.4);
spawn.randomMob(2400, 575, 0.4);
spawn.randomMob(1250, 1575, 0.3);
spawn.randomMob(1075, -100, 0.3);
spawn.randomMob(2450, -100, 0.2);
spawn.randomGroup(1350, -775, 1);
if (simulation.difficulty > 1) spawn.randomLevelBoss(1491, 495);
}
spawn.randomMob(2650, -750, 0.4);
spawn.randomMob(300, -1725, 0.4);
spawn.randomMob(750, -1775, 0.4);
spawn.randomMob(550, -2225, 0.4);
spawn.randomMob(2700, -475, 0.4);
spawn.randomMob(2375, -200, 0.2);
spawn.randomMob(3350, -225, 0.3);
powerUps.addResearchToLevel() //needs to run after mobs are spawned
},
sewers() {
const button1 = level.button(6600, 2675)
// const hazard = level.hazard(4550, 2750, 4550, 150)
const hazard = level.hazard(simulation.isHorizontalFlipped ? -4550 - 4550 : 4550, 2750, 4550, 150)
let balance1, balance2, balance3, balance4, rotor
const drip1 = level.drip(6100, 1900, 2900, 100) // drip(x, yMin, yMax, period = 100, color = "hsla(160, 100%, 35%, 0.5)") {
const drip2 = level.drip(7300, 1900, 2900, 150)
const drip3 = level.drip(8750, 1900, 2900, 70)
level.custom = () => {
drip1.draw();
drip2.draw();
drip3.draw();
button1.query();
button1.draw();
ctx.fillStyle = "hsl(175, 15%, 76%)"
ctx.fillRect(9100, 2200, 800, 400)
ctx.fillStyle = "rgba(0,0,0,0.03)" //shadows
ctx.fillRect(6250, 2025, 700, 650)
ctx.fillRect(8000, 2025, 600, 575)
level.exit.drawAndCheck();
level.enter.draw();
};
level.customTopLayer = () => {
rotor.rotate();
ctx.fillStyle = "#233"
ctx.beginPath();
ctx.arc(balance1.center.x, balance1.center.y, 9, 0, 2 * Math.PI);
ctx.moveTo(balance2.center.x, balance2.center.y)
ctx.arc(balance2.center.x, balance2.center.y, 9, 0, 2 * Math.PI);
ctx.moveTo(balance3.center.x, balance3.center.y)
ctx.arc(balance3.center.x, balance3.center.y, 9, 0, 2 * Math.PI);
ctx.moveTo(balance4.center.x, balance4.center.y)
ctx.arc(balance4.center.x, balance4.center.y, 9, 0, 2 * Math.PI);
ctx.moveTo(balance5.center.x, balance5.center.y)
ctx.arc(balance5.center.x, balance5.center.y, 9, 0, 2 * Math.PI);
ctx.moveTo(rotor.center.x, rotor.center.y)
ctx.arc(rotor.center.x, rotor.center.y, 9, 0, 2 * Math.PI);
ctx.fill();
hazard.query();
hazard.level(button1.isUp)
};
level.setPosToSpawn(0, -50); //normal spawn
spawn.mapRect(level.enter.x, level.enter.y + 20, 100, 20);
level.exit.x = 9700;
level.exit.y = 2560;
level.defaultZoom = 1800
simulation.zoomTransition(level.defaultZoom)
document.body.style.backgroundColor = "hsl(138, 3%, 74%)";
color.map = "#3d4240"
powerUps.spawnStartingPowerUps(3475, 1775);
spawn.debris(4575, 2550, 1600, 9); //16 debris per level
spawn.debris(7000, 2550, 2000, 7); //16 debris per level
spawn.mapRect(-500, -600, 200, 800); //left entrance wall
spawn.mapRect(-400, -600, 3550, 200); //ceiling
spawn.mapRect(-400, 0, 3000, 200); //floor
// spawn.mapRect(300, -500, 50, 400); //right entrance wall
// spawn.bodyRect(312, -100, 25, 100);
spawn.bodyRect(1450, -300, 150, 50);
const xPos = shuffle([600, 1250, 2000]);
spawn.mapRect(xPos[0], -200, 300, 100);
spawn.mapRect(xPos[1], -250, 300, 300);
spawn.mapRect(xPos[2], -150, 300, 200);
spawn.bodyRect(3100, 410, 75, 100);
spawn.bodyRect(2450, -25, 250, 25);
spawn.mapRect(3050, -600, 200, 800); //right down tube wall
spawn.mapRect(3100, 0, 1200, 200); //tube right exit ceiling
spawn.mapRect(4200, 0, 200, 1900);
spawn.mapVertex(3500, 1000, "-500 -500 -400 -600 400 -600 500 -500 500 500 400 600 -400 600 -500 500");
spawn.mapVertex(3600, 1940, "-400 -40 -350 -90 350 -90 400 -40 400 40 350 90 -350 90 -400 40");
spawn.mapRect(3925, 2288, 310, 50);
spawn.mapRect(3980, 2276, 200, 50);
spawn.mapRect(2625, 2288, 650, 50);
spawn.mapRect(2700, 2276, 500, 50);
spawn.mapRect(2400, 0, 200, 1925); //left down tube wall
spawn.mapRect(600, 2300, 3750, 200);
spawn.bodyRect(3800, 275, 125, 125);
spawn.mapRect(4200, 1700, 5000, 200);
spawn.mapRect(4150, 2300, 200, 400);
spawn.mapRect(600, 1700, 2000, 200); //bottom left room ceiling
spawn.mapRect(500, 1700, 200, 800); //left wall
spawn.mapRect(675, 1875, 325, 150, 0.5);
spawn.mapRect(4450, 2900, 4900, 200); //boss room floor
spawn.mapRect(4150, 2600, 400, 500);
spawn.mapRect(6250, 2675, 700, 325);
spawn.mapRect(8000, 2600, 600, 400);
spawn.bodyRect(5875, 2725, 200, 200);
spawn.bodyRect(6800, 2490, 50, 50);
spawn.bodyRect(6800, 2540, 50, 50);
spawn.bodyRect(6800, 2590, 50, 50);
spawn.bodyRect(8225, 2225, 100, 100);
spawn.mapRect(6250, 1875, 700, 150);
spawn.mapRect(8000, 1875, 600, 150);
spawn.mapRect(9100, 1700, 900, 500); //exit
spawn.mapRect(9100, 2600, 900, 500);
spawn.mapRect(9900, 1700, 200, 1400); //back wall
// spawn.mapRect(9300, 2150, 50, 250);
spawn.mapRect(9300, 2590, 650, 25);
spawn.mapRect(9700, 2580, 100, 50);
spawn.randomGroup(1300, 2100, 0.1);
spawn.randomMob(8300, 2100, 0.1);
spawn.randomSmallMob(2575, -75, 0.1); //entrance
spawn.randomMob(8125, 2450, 0.1);
spawn.randomSmallMob(3200, 250, 0.1);
spawn.randomMob(2425, 2150, 0.1);
spawn.randomSmallMob(3500, 250, 0.2);
spawn.randomMob(3800, 2175, 0.2);
spawn.randomSmallMob(2500, -275, 0.2); //entrance
spawn.randomMob(4450, 2500, 0.2);
spawn.randomMob(6350, 2525, 0.2);
spawn.randomGroup(9200, 2400, 0.3);
spawn.randomSmallMob(1900, -250, 0.3); //entrance
spawn.randomMob(1500, 2100, 0.4);
spawn.randomSmallMob(1700, -150, 0.4); //entrance
spawn.randomMob(8800, 2725, 0.5);
spawn.randomMob(7300, 2200, 0.5);
spawn.randomMob(2075, 2025, 0.5);
spawn.randomMob(3475, 2175, 0.5);
spawn.randomMob(8900, 2825, 0.5);
spawn.randomMob(9600, 2425, 0.9);
spawn.randomMob(3600, 1725, 0.9);
spawn.randomMob(4100, 1225, 0.9);
spawn.randomMob(2825, 400, 0.9);
if (simulation.difficulty > 1) spawn.randomLevelBoss(6000, 2300, ["dragonFlyBoss", "beetleBoss", "spiderBoss", "launcherBoss", "laserTargetingBoss", "blinkBoss", "streamBoss", "historyBoss", "orbitalBoss", "grenadierBoss", "blockBoss", "revolutionBoss", "slashBoss"]);
powerUps.addResearchToLevel() //needs to run after mobs are spawned
spawn.secondaryBossChance(7725, 2275)
if (simulation.isHorizontalFlipped) { //flip the map horizontally
level.flipHorizontal(); //only flips map,body,mob,powerUp,cons,consBB, exit
// rotor(x, y, width, height, density = 0.001, angle = 0, frictionAir = 0.001, angularVelocity = 0, rotationForce = 0.0005) {
// rotor = level.rotor(-5100, 2475, 0.001) //rotates other direction because flipped
rotor = level.rotor(-5600, 2390, 850, 50, 0.001, 0, 0.01, 0, 0.001) //balance(x, y, width, height, density = 0.001, angle = 0, frictionAir = 0.001, angularVelocity = 0, rotationForce = 0.0005) {
balance1 = level.rotor(-300 - 25, -395, 25, 390, 0.001) //entrance
balance2 = level.rotor(-2605 - 390, 500, 390, 25, 0.001) //falling
balance3 = level.rotor(-2608 - 584, 1900, 584, 25, 0.001) //falling
balance4 = level.rotor(-9300 - 25, 2205, 25, 380, 0.001) //exit
balance5 = level.rotor(-2605 - 390, 1100, 390, 25, 0.001) //falling
// boost1.boostBounds.min.x = -boost1.boostBounds.min.x - 100
// boost1.boostBounds.max.x = -boost1.boostBounds.max.x + 100
// level.setPosToSpawn(300, -700); //-x // no need since 0
button1.min.x = -button1.min.x - 126 // flip the button horizontally
button1.max.x = -button1.max.x + 126 // flip the button horizontally
drip1.x *= -1
drip2.x *= -1
drip3.x *= -1
level.custom = () => {
drip1.draw();
drip2.draw();
drip3.draw();
button1.query();
button1.draw();
rotor.rotate();
ctx.fillStyle = "hsl(175, 15%, 76%)"
ctx.fillRect(-9900, 2200, 800, 400)
ctx.fillStyle = "rgba(0,0,0,0.03)" //shadows
ctx.fillRect(-6950, 2025, 700, 650)
ctx.fillRect(-8600, 2025, 600, 575)
level.exit.drawAndCheck();
level.enter.draw();
};
// level.customTopLayer = () => {};
} else {
// rotor = level.rotor(5100, 2475, -0.001)
rotor = level.rotor(4700, 2390, 850, 50, 0.001, 0, 0.01, 0, -0.001) //balance(x, y, width, height, density = 0.001, angle = 0, frictionAir = 0.001, angularVelocity = 0, rotationForce = 0.0005) {
balance1 = level.rotor(300, -395, 25, 390, 0.001) //entrance
balance2 = level.rotor(2605, 500, 390, 25, 0.001) //falling
balance3 = level.rotor(2608, 1900, 584, 25, 0.001) //falling
balance4 = level.rotor(9300, 2205, 25, 380, 0.001) //exit
balance5 = level.rotor(2605, 1100, 390, 25, 0.001) //falling
}
},
satellite() {
const boost1 = level.boost(5825, 235, 1400)
const elevator = level.elevator(4210, -1265, 380, 50, -3450) //, 0.003, { up: 0.01, down: 0.2 }
level.custom = () => {
boost1.query();
ctx.fillStyle = "#d4f4f4"
ctx.fillRect(-250, -750, 420, 450)
ctx.fillStyle = "#d0d4d6"
ctx.fillRect(-300, -1900, 500, 1100)
ctx.fillRect(900, -2450, 450, 2050)
ctx.fillRect(2000, -2800, 450, 2500)
ctx.fillRect(3125, -3100, 450, 3300)
level.exit.drawAndCheck();
level.enter.draw();
};
level.customTopLayer = () => {
ctx.fillStyle = "rgba(0,20,40,0.25)"
ctx.fillRect(-250, -400, 1800, 775)
ctx.fillRect(1800, -275, 850, 775)
ctx.fillRect(5200, 125, 450, 200)
ctx.fillStyle = "rgba(0,20,40,0.1)"
ctx.fillRect(4000, -1200, 1050, 1500)
ctx.fillRect(4100, -3450, 600, 2250)
elevator.move()
};
level.setPosToSpawn(-100, 210); //normal spawn
spawn.mapRect(-150, 240, 100, 30);
level.exit.x = -100;
level.exit.y = -425;
spawn.mapRect(level.exit.x, level.exit.y + 15, 100, 50); //exit bump
level.defaultZoom = 1700 // 4500 // 1400
simulation.zoomTransition(level.defaultZoom)
powerUps.spawnStartingPowerUps(4900, -500); //1 per level
spawn.debris(1000, 20, 1800, 3); //16 debris per level //but less here because a few mobs die from laser
spawn.debris(4830, -1330, 850, 3); //16 debris per level
spawn.debris(3035, -3900, 1500, 3); //16 debris per level
document.body.style.backgroundColor = "#dbdcde";
//spawn start building
spawn.mapRect(-350, -800, 100, 1100);
// spawn.mapRect(-300, -10, 500, 50);
spawn.mapRect(150, -510, 50, 365);
spawn.bodyRect(170, -140, 20, 163, 1, spawn.propsFriction); //door to starting room
spawn.mapVertex(175, 200, "625 0 300 0 425 -300 500 -300"); //entrance ramp
// spawn.mapRect(-300, 0, 1000, 300); //ground
spawn.mapRect(-350, 250, 6350, 300); //deeper ground
spawn.bodyRect(2100, 50, 80, 80);
spawn.bodyRect(2000, 50, 60, 60);
// spawn.bodyRect(1650, 50, 300, 200);
// spawn.mapRect(1800, Math.floor(Math.random() * 200), 850, 300); //stops above body from moving to right
spawn.mapVertex(2225, 250, "575 0 -575 0 -450 -100 450 -100"); //base
//exit building
// spawn.mapRect(-100, -410, 100, 30);
spawn.mapRect(-350, -850, 550, 100);
spawn.mapRect(150, -800, 50, 110);
spawn.bodyRect(170, -690, 14, 180, 1, spawn.propsFriction); //door to exit room
spawn.mapRect(-300, -400, 500, 150); //far left starting ceiling
//tall platform above exit
spawn.mapRect(-500, -1900, 400, 50); //super high shade
spawn.mapRect(0, -1900, 400, 50); //super high shade
spawn.mapRect(-150, -1350, 200, 25); //super high shade
spawn.bodyRect(140, -2100, 150, 200); //shield from laser
//tall platform
spawn.mapVertex(1125, -450, "325 0 250 80 -250 80 -325 0 -250 -80 250 -80"); //base
spawn.mapRect(150, -500, 1410, 100); //far left starting ceiling
spawn.mapRect(625, -2450, 1000, 50); //super high shade
spawn.bodyRect(1300, -3600, 150, 150); //shield from laser
//tall platform
spawn.mapVertex(2225, -250, "325 0 250 80 -250 80 -325 0 -250 -80 250 -80"); //base
spawn.mapRect(1725, -2800, 1000, 50); //super high shade
spawn.mapRect(1790, -300, 870, 100); //far left starting ceiling
spawn.bodyRect(2400, -2950, 150, 150); //shield from laser
//tall platform
spawn.mapVertex(3350, 175, "425 0 -425 0 -275 -300 275 -300"); //base
spawn.bodyRect(3350, -150, 200, 120);
spawn.mapRect(2850, -3150, 1000, 50); //super high shade
spawn.bodyRect(3675, -3470, 525, 20); //plank
spawn.bodyRect(3600, -3450, 200, 300); //plank support block
//far right structure
spawn.mapRect(5200, -725, 100, 870);
spawn.mapRect(5300, -1075, 350, 1220);
//structure bellow tall stairs
spawn.mapRect(3900, -300, 450, 50);
spawn.mapRect(4675, -375, 450, 50);
// spawn.mapRect(4000, -1300, 1050, 100);
spawn.mapRect(4000, -1300, 200, 100);
spawn.mapRect(4600, -1300, 450, 100);
//steep stairs
spawn.mapRect(4100, -2250, 100, 650);
spawn.mapRect(4100, -3450, 100, 850); //left top shelf
spawn.mapRect(4600, -3450, 100, 1850);
spawn.randomSmallMob(4400, -3500);
spawn.randomSmallMob(4800, -800);
spawn.randomMob(800, -2600);
spawn.randomMob(700, -600, 0.3);
spawn.randomMob(3100, -3600, 0.3);
spawn.randomMob(3300, -1000, 0.3);
spawn.randomMob(4200, -250, 0.3);
spawn.randomMob(4900, -1500, 0.3);
spawn.randomMob(3800, 175, 0.4);
spawn.randomMob(5750, 125, 0.4);
spawn.randomMob(5900, -1500, 0.4);
spawn.randomMob(4700, -800, 0.4);
spawn.randomMob(1400, 200, 0.3);
spawn.randomMob(2850, 175, 0.4);
spawn.randomMob(2000, -2800, 0.4);
spawn.randomMob(2400, -400, 0.4);
spawn.randomMob(4475, -3550, 0.3);
spawn.randomGroup(5000, -2150, 1);
spawn.randomGroup(3700, -4100, 0.3);
spawn.randomGroup(2700, -1600, 0.1);
spawn.randomGroup(1600, -100, 0);
spawn.randomGroup(5000, -3900, -0.3);
if (simulation.difficulty > 1) {
if (Math.random() < 0.25) {
spawn.randomLevelBoss(2800, -1400);
} else if (Math.random() < 0.25) {
spawn.laserBoss(2900 + 300 * Math.random(), -2950 + 150 * Math.random());
} else if (Math.random() < 0.33) {
spawn.laserBoss(1800 + 250 * Math.random(), -2600 + 150 * Math.random());
} else if (Math.random() < 0.5) {
spawn.laserBoss(3500 + 250 * Math.random(), -2600 + 1000 * Math.random());
} else {
spawn.laserBoss(600 + 200 * Math.random(), -2150 + 250 * Math.random());
}
}
powerUps.addResearchToLevel() //needs to run after mobs are spawned
spawn.secondaryBossChance(3950, -850)
if (simulation.isHorizontalFlipped) { //flip the map horizontally
level.flipHorizontal(); //only flips map,body,mob,powerUp,cons,consBB, exit
boost1.boostBounds.min.x = -boost1.boostBounds.min.x - 100
boost1.boostBounds.max.x = -boost1.boostBounds.max.x + 100
level.setPosToSpawn(100, 210); //-x
elevator.holdX = -elevator.holdX // flip the elevator horizontally
level.custom = () => {
boost1.query();
ctx.fillStyle = "#d4f4f4"
ctx.fillRect(250 - 420, -750, 420, 450)
ctx.fillStyle = "#d0d4d6"
ctx.fillRect(300 - 500, -1900, 500, 1100)
ctx.fillRect(-900 - 450, -2450, 450, 2050)
ctx.fillRect(-2000 - 450, -2800, 450, 2500)
ctx.fillRect(-3125 - 450, -3100, 450, 3300)
level.exit.drawAndCheck();
level.enter.draw();
};
level.customTopLayer = () => {
elevator.move()
ctx.fillStyle = "rgba(0,20,40,0.25)"
ctx.fillRect(250 - 1800, -400, 1800, 775)
ctx.fillRect(-1800 - 850, -275, 850, 775)
ctx.fillRect(-5200 - 450, 125, 450, 200)
ctx.fillStyle = "rgba(0,20,40,0.1)"
ctx.fillRect(-4000 - 1050, -1200, 1050, 1500)
ctx.fillRect(-4100 - 600, -3450, 600, 2250)
};
}
},
rooftops() {
const elevator = level.elevator(1450, -990, 235, 45, -2000)
const boost1 = level.boost(4950, 0, 1100)
level.custom = () => {
boost1.query();
elevator.move();
elevator.drawTrack();
ctx.fillStyle = "#d4f4f4"
if (isBackwards) {
ctx.fillRect(-650, -2300, 440, 300)
} else {
ctx.fillRect(3460, -700, 1090, 800)
}
level.exit.drawAndCheck();
level.enter.draw();
};
level.customTopLayer = () => {
ctx.fillStyle = "rgba(0,0,0,0.1)"
ctx.fillRect(710, -2225, 580, 225)
ctx.fillRect(3510, -1550, 330, 300)
ctx.fillRect(1735, -900, 1515, 1900)
ctx.fillRect(1735, -1550, 1405, 550)
ctx.fillRect(1860, -1950, 630, 350)
ctx.fillRect(-700, -1950, 2100, 2950)
ctx.fillRect(3400, 100, 2150, 900)
ctx.fillRect(4550, -725, 900, 725)
ctx.fillRect(3460, -1250, 1080, 550)
if (isBackwards) {
ctx.fillRect(3460, -700, 1090, 800)
} else {
ctx.fillRect(-650, -2300, 440, 300)
}
};
level.defaultZoom = 1700
simulation.zoomTransition(level.defaultZoom)
document.body.style.backgroundColor = "#dcdcde";
let isBackwards = false
if (Math.random() < 0.75) {
//normal direction start in top left
level.setPosToSpawn(-450, -2060);
level.exit.x = 3600;
level.exit.y = -300;
spawn.mapRect(3600, -285, 100, 50); //ground bump wall
//mobs that spawn in exit room
spawn.bodyRect(4850, -750, 300, 25, 0.6); //
spawn.randomSmallMob(4100, -100);
spawn.randomSmallMob(4600, -100);
spawn.randomMob(3765, -450, 0.3);
} else {
isBackwards = true
//reverse direction, start in bottom right
level.setPosToSpawn(3650, -325);
level.exit.x = -550;
level.exit.y = -2030;
spawn.mapRect(-550, -2015, 100, 50); //ground bump wall
}
spawn.mapRect(level.enter.x, level.enter.y + 20, 100, 20);
spawn.debris(1650, -1800, 3800, 16); //16 debris per level
powerUps.spawnStartingPowerUps(2450, -1675);
//spawn.mapRect(-700, 0, 6250, 100); //ground
spawn.mapRect(3400, 0, 2150, 100); //ground
spawn.mapRect(-700, -2000, 2125, 50); //Top left ledge
spawn.bodyRect(1300, -2125, 50, 125, 0.8);
spawn.bodyRect(1307, -2225, 50, 100, 0.8);
spawn.mapRect(-700, -2350, 50, 400); //far left starting left wall
spawn.mapRect(-700, -2010, 500, 50); //far left starting ground
spawn.mapRect(-700, -2350, 500, 50); //far left starting ceiling
spawn.mapRect(-250, -2350, 50, 200); //far left starting right part of wall
spawn.bodyRect(-240, -2150, 30, 36); //door to starting room
spawn.bodyRect(-240, -2115, 30, 36); //door to starting room
spawn.bodyRect(-240, -2080, 30, 35); //door to starting room
spawn.bodyRect(-240, -2045, 30, 35); //door to starting room
spawn.mapRect(1850, -2000, 650, 50);
spawn.bodyRect(200, -2150, 80, 220, 0.8);
spawn.mapRect(700, -2275, 600, 50);
spawn.mapRect(1000, -1350, 410, 50);
spawn.bodyRect(1050, -2350, 30, 30, 0.8);
// spawn.bodyRect(1625, -1100, 100, 75);
// spawn.bodyRect(1350, -1025, 400, 25); // ground plank
spawn.mapRect(-725, -1000, 2150, 100); //lower left ledge
spawn.bodyRect(350, -1100, 200, 100, 0.8);
spawn.bodyRect(370, -1200, 100, 100, 0.8);
spawn.bodyRect(360, -1300, 100, 100, 0.8);
spawn.bodyRect(950, -1050, 300, 50, 0.8);
spawn.bodyRect(-575, -1150, 125, 150, 0.8);
spawn.mapRect(1710, -1000, 1565, 100); //middle ledge
spawn.mapRect(3400, -1000, 75, 25);
spawn.bodyRect(2600, -1950, 100, 250, 0.8);
spawn.bodyRect(2700, -1125, 125, 125, 0.8);
spawn.bodyRect(2710, -1250, 125, 125, 0.8);
spawn.bodyRect(2705, -1350, 75, 100, 0.8);
spawn.mapRect(3500, -1600, 350, 50);
spawn.mapRect(1725, -1600, 1435, 50);
spawn.bodyRect(3100, -1015, 375, 15);
spawn.bodyRect(3500, -850, 75, 125, 0.8);
spawn.mapRect(3450, -1000, 50, 580); //left building wall
spawn.bodyRect(3460, -420, 30, 144);
spawn.mapRect(5450, -775, 100, 875); //right building wall
spawn.bodyRect(3925, -1400, 100, 150, 0.8);
spawn.mapRect(3450, -1250, 1090, 50);
// spawn.mapRect(3450, -1225, 50, 75);
spawn.mapRect(4500, -1250, 50, 415);
spawn.mapRect(3450, -725, 1500, 50);
spawn.mapRect(5100, -725, 400, 50);
spawn.mapRect(4500, -735, 50, 635);
spawn.bodyRect(4500, -100, 50, 100);
spawn.mapRect(4500, -885, 100, 50);
spawn.spawnStairs(3800, 0, 3, 150, 206); //stairs top exit
spawn.mapRect(3400, -275, 450, 275); //exit platform
spawn.randomSmallMob(2200, -1775);
spawn.randomSmallMob(4000, -825);
spawn.randomSmallMob(-350, -3400);
spawn.randomMob(4250, -1350, 0.8);
spawn.randomMob(2550, -1350, 0.8);
spawn.randomMob(1875, -1075, 0.3);
spawn.randomMob(1120, -1200, 0.3);
spawn.randomMob(3000, -1150, 0.2);
spawn.randomMob(3200, -1150, 0.3);
spawn.randomMob(3300, -1750, 0.3);
spawn.randomMob(3650, -1350, 0.3);
spawn.randomMob(3600, -1800, 0.1);
spawn.randomMob(5200, -100, 0.3);
spawn.randomMob(5275, -900, 0.2);
spawn.randomMob(0, -1075, 0.3);
spawn.randomGroup(600, -1575, 0);
spawn.randomGroup(2225, -1325, 0.4);
spawn.randomGroup(4900, -1200, 0);
if (simulation.difficulty > 1) spawn.randomLevelBoss(3200, -1900);
powerUps.addResearchToLevel() //needs to run after mobs are spawned
spawn.secondaryBossChance(2175, -2425)
if (simulation.isHorizontalFlipped) { //flip the map horizontally
level.flipHorizontal(); //only flips map,body,mob,powerUp,cons,consBB, exit
boost1.boostBounds.min.x = -boost1.boostBounds.min.x - 100
boost1.boostBounds.max.x = -boost1.boostBounds.max.x + 100
elevator.holdX = -elevator.holdX // flip the elevator horizontally
if (isBackwards) {
level.setPosToSpawn(-3650, -325); //-x
} else {
level.setPosToSpawn(450, -2060); //-x
}
level.custom = () => {
boost1.query();
elevator.move();
elevator.drawTrack();
ctx.fillStyle = "#d4f4f4"
if (isBackwards) {
ctx.fillRect(650 - 440, -2300, 440, 300)
} else {
ctx.fillRect(-3460 - 1090, -700, 1090, 800)
}
level.exit.drawAndCheck();
level.enter.draw();
};
level.customTopLayer = () => {
ctx.fillStyle = "rgba(0,0,0,0.1)"
ctx.fillRect(-710 - 580, -2225, 580, 225)
ctx.fillRect(-3510 - 330, -1550, 330, 300)
ctx.fillRect(-1735 - 1515, -900, 1515, 1900)
ctx.fillRect(-1735 - 1405, -1550, 1405, 550)
ctx.fillRect(-1860 - 630, -1950, 630, 350)
ctx.fillRect(700 - 2100, -1950, 2100, 2950)
ctx.fillRect(-3400 - 2150, 100, 2150, 900)
ctx.fillRect(-4550 - 900, -725, 900, 725)
ctx.fillRect(-3460 - 1080, -1250, 1080, 550)
if (isBackwards) {
ctx.fillRect(-3460 - 1090, -700, 1090, 800)
} else {
ctx.fillRect(650 - 440, -2300, 440, 300)
}
};
}
},
aerie() {
const boost1 = level.boost(-425, 100, 1400)
const boost2 = level.boost(5350, 275, 2850);
level.custom = () => {
boost1.query();
boost2.query();
if (backwards) {
ctx.fillStyle = "#d4f4f4"
ctx.fillRect(-275, -1275, 425, 300)
} else {
ctx.fillStyle = "#d4f4f4"
ctx.fillRect(3750, -3650, 550, 400)
}
ctx.fillStyle = "#c7c7ca"
ctx.fillRect(4200, -2200, 100, 2600)
// ctx.fillStyle = "#c7c7ca"
ctx.fillRect(-100, -1000, 1450, 1400)
level.exit.drawAndCheck();
level.enter.draw();
};
level.customTopLayer = () => {
if (backwards) {
ctx.fillStyle = "rgba(0,0,0,0.1)"
ctx.fillRect(3750, -3650, 550, 400)
} else {
ctx.fillStyle = "rgba(0,0,0,0.1)"
ctx.fillRect(-275, -1275, 425, 300)
}
ctx.fillStyle = "rgba(0,0,0,0.1)"
ctx.fillRect(3700, -3150, 1100, 950)
ctx.fillRect(2000, -1110, 450, 1550)
ctx.fillStyle = "rgba(0,0,0,0.04)"
ctx.beginPath()
ctx.moveTo(-100, -900)
ctx.lineTo(300, -900)
ctx.lineTo(150, 100)
ctx.lineTo(-100, 100)
ctx.moveTo(600, -900)
ctx.lineTo(1350, -900)
ctx.lineTo(1350, 100)
ctx.lineTo(750, 100)
ctx.fill()
};
// simulation.difficulty = 4; //for testing to simulate possible mobs spawns
level.defaultZoom = 2100
simulation.zoomTransition(level.defaultZoom)
const backwards = (Math.random() < 0.25 && simulation.difficulty > 8) ? true : false;
if (backwards) {
level.setPosToSpawn(4000, -3300); //normal spawn
level.exit.x = -100;
level.exit.y = -1025;
} else {
level.setPosToSpawn(-50, -1050); //normal spawn
level.exit.x = 3950;
level.exit.y = -3275;
}
spawn.mapRect(level.enter.x, level.enter.y + 20, 100, 20);
spawn.mapRect(level.exit.x, level.exit.y + 15, 100, 20);
powerUps.spawnStartingPowerUps(1075, -550);
document.body.style.backgroundColor = "#dcdcde";
// starting room
spawn.mapRect(-300, -1000, 600, 100);
spawn.mapRect(-300, -1300, 450, 50);
spawn.mapRect(-300, -1300, 50, 350);
if (!backwards) spawn.bodyRect(100, -1250, 200, 240); //remove on backwards
//left building
spawn.mapRect(-100, -975, 100, 975);
spawn.mapRect(-500, 100, 1950, 400);
spawn.mapRect(600, -1000, 750, 100);
spawn.mapRect(900, -500, 550, 100);
spawn.mapRect(1250, -975, 100, 375);
spawn.bodyRect(1250, -600, 100, 100, 0.7);
spawn.mapRect(1250, -450, 100, 450);
spawn.bodyRect(1250, -1225, 100, 200, 0.7); //remove on backwards
spawn.bodyRect(1200, -1025, 350, 35); //remove on backwards
//middle super tower
if (backwards) {
spawn.bodyRect(2000, -800, 700, 35);
} else {
spawn.bodyRect(1750, -800, 700, 35);
}
spawn.mapVertex(2225, -2100, "0 0 450 0 300 -2500 150 -2500")
spawn.mapRect(2000, -700, 450, 300);
spawn.bodyRect(2360, -450, 100, 300, 0.6);
spawn.mapRect(2000, -75, 450, 275);
spawn.bodyRect(2450, 150, 150, 150, 0.4);
spawn.mapRect(1550, 300, 4600, 200); //ground
// spawn.mapRect(6050, -700, 450, 1200);
spawn.mapRect(6050, -1060, 450, 1560);
spawn.mapVertex(6275, -2100, "0 0 450 0 300 -2500 150 -2500")
//right tall tower
spawn.mapRect(3700, -3200, 100, 800);
spawn.mapRect(4700, -2910, 100, 510);
spawn.mapRect(3700, -2600, 300, 50);
spawn.mapRect(4100, -2900, 900, 50);
spawn.mapRect(3450, -2300, 750, 100);
spawn.mapRect(4300, -2300, 750, 100);
spawn.mapRect(4150, -1600, 200, 25);
spawn.mapRect(4150, -700, 200, 25);
//exit room on top of tower
spawn.mapRect(3700, -3700, 600, 50);
spawn.mapRect(3700, -3700, 50, 500);
spawn.mapRect(4250, -3700, 50, 300);
spawn.mapRect(3700, -3250, 1100, 100);
spawn.randomGroup(350, -500, 1)
spawn.randomSmallMob(-225, 25);
spawn.randomSmallMob(2100, -900);
spawn.randomSmallMob(4000, -250);
spawn.randomSmallMob(4450, -3000);
spawn.randomSmallMob(5600, 100);
spawn.randomMob(4275, -2600, 0.8);
spawn.randomMob(1050, -700, 0.8)
spawn.randomMob(6050, -850, 0.7);
spawn.randomMob(2150, -300, 0.6)
spawn.randomMob(3900, -2700, 0.8);
spawn.randomMob(3600, -500, 0.8);
spawn.randomMob(3400, -200, 0.8);
// spawn.randomMob(1650, -1300, 0.7)
spawn.randomMob(425, 0, 0.7);
spawn.randomMob(4100, -50, 0.7);
spawn.randomMob(4100, -50, 0.5);
spawn.randomMob(1700, -50, 0.3)
spawn.randomMob(2350, -900, 0.3)
spawn.randomMob(4700, -150, 0.2);
spawn.randomGroup(4000, -350, 0.6);
spawn.randomGroup(2750, -550, 0.1);
spawn.randomMob(2175, -925, 0.5);
spawn.randomMob(2750, 100, 0.5);
spawn.randomMob(4250, -1725, 0.5);
spawn.randomMob(3575, -2425, 0.5);
spawn.randomMob(3975, -3900, 0.5);
spawn.randomMob(1725, 125, 0.5);
if (simulation.difficulty > 1) {
if (Math.random() < 0.5) {
spawn.randomLevelBoss(4250, -250);
spawn.debris(-250, 50, 1650, 2); //16 debris per level
spawn.debris(2475, 0, 750, 2); //16 debris per level
spawn.debris(3450, 0, 2000, 16); //16 debris per level
spawn.debris(3500, -2350, 1500, 2); //16 debris per level
} else {
powerUps.chooseRandomPowerUp(4000, 200);
powerUps.chooseRandomPowerUp(4000, 200);
//floor below right tall tower
spawn.bodyRect(3000, 50, 150, 250, 0.9);
spawn.bodyRect(4500, -500, 300, 250, 0.7);
spawn.bodyRect(3500, -100, 100, 150, 0.7);
spawn.bodyRect(4200, -500, 110, 30, 0.7);
spawn.bodyRect(3800, -500, 150, 130, 0.7);
spawn.bodyRect(4000, 50, 200, 150, 0.9);
spawn.bodyRect(4500, 50, 300, 200, 0.9);
spawn.bodyRect(4200, -350, 200, 50, 0.9);
spawn.bodyRect(4700, -350, 50, 200, 0.9);
spawn.bodyRect(4900, -100, 300, 300, 0.7);
spawn.suckerBoss(4500, -400);
}
}
powerUps.addResearchToLevel() //needs to run after mobs are spawned
spawn.secondaryBossChance(5350, -325)
if (simulation.isHorizontalFlipped) { //flip the map horizontally
level.flipHorizontal(); //only flips map,body,mob,powerUp,cons,consBB, exit
boost1.boostBounds.min.x = -boost1.boostBounds.min.x - 100
boost1.boostBounds.max.x = -boost1.boostBounds.max.x + 100
boost2.boostBounds.min.x = -boost2.boostBounds.min.x - 100
boost2.boostBounds.max.x = -boost2.boostBounds.max.x + 100
if (backwards) {
level.setPosToSpawn(-4000, -3300); //-x
} else {
level.setPosToSpawn(50, -1050); //-x
}
level.custom = () => {
boost1.query();
boost2.query();
if (backwards) {
ctx.fillStyle = "#d4f4f4"
ctx.fillRect(275 - 425, -1275, 425, 300)
} else {
ctx.fillStyle = "#d4f4f4"
ctx.fillRect(-3750 - 550, -3650, 550, 400)
}
ctx.fillStyle = "#c7c7ca"
ctx.fillRect(-4200 - 100, -2200, 100, 2600)
// ctx.fillStyle = "#c7c7ca"
ctx.fillRect(100 - 1450, -1000, 1450, 1400)
level.exit.drawAndCheck();
level.enter.draw();
};
level.customTopLayer = () => {
if (backwards) {
ctx.fillStyle = "rgba(0,0,0,0.1)"
ctx.fillRect(-3750 - 550, -3650, 550, 400)
} else {
ctx.fillStyle = "rgba(0,0,0,0.1)"
ctx.fillRect(275 - 425, -1275, 425, 300)
}
ctx.fillStyle = "rgba(0,0,0,0.1)"
ctx.fillRect(-3700 - 1100, -3150, 1100, 950)
ctx.fillRect(-2000 - 450, -1110, 450, 1550)
ctx.fillStyle = "rgba(0,0,0,0.04)"
ctx.beginPath()
ctx.moveTo(100, -900)
ctx.lineTo(-300, -900)
ctx.lineTo(-150, 100)
ctx.lineTo(100, 100)
ctx.moveTo(-600, -900)
ctx.lineTo(-1350, -900)
ctx.lineTo(-1350, 100)
ctx.lineTo(-750, 100)
ctx.fill()
};
}
},
skyscrapers() {
const boost1 = level.boost(475, 0, 1300)
const boost2 = level.boost(4450, 0, 1300);
level.custom = () => {
boost1.query();
boost2.query();
ctx.fillStyle = "#d4f4f4"
ctx.fillRect(1350, -2100, 400, 250)
ctx.fillStyle = "#d4d4d7"
ctx.fillRect(3350, -1300, 50, 1325)
ctx.fillRect(1300, -1800, 750, 1800)
level.exit.drawAndCheck();
level.enter.draw();
};
level.customTopLayer = () => {
ctx.fillStyle = "rgba(0,0,0,0.1)"
ctx.fillRect(2500, -1100, 450, 250)
ctx.fillRect(2400, -550, 600, 150)
ctx.fillRect(2550, -1650, 250, 200)
ctx.fillStyle = "rgba(0,0,0,0.2)"
ctx.fillRect(700, -110, 400, 110)
ctx.fillRect(3800, -110, 400, 110)
ctx.fillStyle = "rgba(0,0,0,0.15)"
ctx.fillRect(-250, -300, 450, 300)
};
level.setPosToSpawn(-50, -60); //normal spawn
spawn.mapRect(level.enter.x, level.enter.y + 20, 100, 20);
level.exit.x = 1500;
level.exit.y = -1875;
level.defaultZoom = 2000
simulation.zoomTransition(level.defaultZoom)
powerUps.spawnStartingPowerUps(1475, -1175);
spawn.debris(750, -2200, 3700, 16); //16 debris per level
document.body.style.backgroundColor = "#dcdcde";
spawn.mapRect(-300, 0, 5100, 300); //***********ground
spawn.mapRect(-300, -350, 50, 400); //far left starting left wall
spawn.mapRect(-300, -10, 500, 50); //far left starting ground
spawn.mapRect(-300, -350, 500, 50); //far left starting ceiling
spawn.mapRect(150, -350, 50, 200); //far left starting right part of wall
spawn.bodyRect(170, -130, 14, 140, 1, spawn.propsFriction); //door to starting room
spawn.mapRect(700, -1100, 400, 990); //far left building
spawn.mapRect(1600, -400, 1500, 500); //long center building
spawn.mapRect(1345, -1100, 250, 25); //left platform
spawn.mapRect(1755, -1100, 250, 25); //right platform
spawn.mapRect(1300, -1850, 800, 50); //left higher platform
spawn.mapRect(1300, -2150, 50, 350); //left higher platform left edge wall
spawn.mapRect(1300, -2150, 450, 50); //left higher platform roof
spawn.mapRect(1500, -1860, 100, 50); //ground bump wall
spawn.mapRect(2400, -850, 600, 300); //center floating large square
//spawn.bodyRect(2500, -1100, 25, 250); //wall before chasers
spawn.mapRect(2500, -1450, 450, 350); //higher center floating large square
spawn.mapRect(2500, -1675, 50, 300); //left wall on higher center floating large square
spawn.mapRect(2500, -1700, 300, 50); //roof on higher center floating large square
spawn.mapRect(3275, -750, 200, 25); //ledge by far right building
spawn.mapRect(3275, -1300, 200, 25); //higher ledge by far right building
spawn.mapRect(3800, -1100, 400, 990); //far right building
spawn.bodyRect(3200, -1375, 300, 25, 0.9);
spawn.bodyRect(1825, -1875, 400, 25, 0.9);
// spawn.bodyRect(1800, -575, 250, 150, 0.8);
spawn.bodyRect(1800, -600, 110, 150, 0.8);
spawn.bodyRect(2557, -450, 35, 55, 0.7);
spawn.bodyRect(2957, -450, 30, 15, 0.7);
spawn.bodyRect(2900, -450, 60, 45, 0.7);
spawn.bodyRect(915, -1200, 60, 100, 0.95);
spawn.bodyRect(925, -1300, 50, 100, 0.95);
if (Math.random() < 0.9) {
spawn.bodyRect(2300, -1720, 400, 20);
spawn.bodyRect(2590, -1780, 80, 80);
}
spawn.bodyRect(2925, -1100, 25, 250, 0.8);
spawn.bodyRect(3325, -1550, 50, 200, 0.3);
if (Math.random() < 0.8) {
spawn.bodyRect(1400, -75, 200, 75); //block to get up ledge from ground
spawn.bodyRect(1525, -125, 50, 50); //block to get up ledge from ground
}
spawn.bodyRect(1025, -1110, 400, 25, 0.9); //block on far left building
spawn.bodyRect(1425, -1110, 115, 25, 0.9); //block on far left building
spawn.bodyRect(1540, -1110, 300, 25, 0.9); //block on far left building
spawn.randomMob(-100, -1300, 0.5);
spawn.randomSmallMob(1850, -600);
spawn.randomSmallMob(3200, -100);
spawn.randomSmallMob(4450, -100);
spawn.randomSmallMob(2700, -475);
spawn.randomMob(2650, -975, 0.8);
spawn.randomMob(2650, -1550, 0.8);
spawn.randomMob(4150, -200, 0.15);
spawn.randomMob(1700, -1300, 0.2);
spawn.randomMob(1850, -1950, 0.25);
spawn.randomMob(2610, -1880, 0.25);
spawn.randomMob(3350, -950, 0.25);
spawn.randomMob(1690, -2250, 0.25);
spawn.randomMob(2200, -600, 0.2);
spawn.randomMob(850, -1300, 0.25);
spawn.randomMob(-100, -1700, -0.2);
spawn.randomGroup(3700, -1500, 0.4);
spawn.randomGroup(1700, -900, 0.4);
if (simulation.difficulty > 1) spawn.randomLevelBoss(2800 + 200 * Math.random(), -2200 + 200 * Math.random());
powerUps.addResearchToLevel() //needs to run after mobs are spawned
spawn.secondaryBossChance(4000, -1825)
if (simulation.isHorizontalFlipped) { //flip the map horizontally
level.flipHorizontal(); //only flips map,body,mob,powerUp,cons,consBB, exit
boost1.boostBounds.min.x = -boost1.boostBounds.min.x - 100
boost1.boostBounds.max.x = -boost1.boostBounds.max.x + 100
boost2.boostBounds.min.x = -boost2.boostBounds.min.x - 100
boost2.boostBounds.max.x = -boost2.boostBounds.max.x + 100
level.setPosToSpawn(50, -60); //-x
level.custom = () => {
boost1.query();
boost2.query();
ctx.fillStyle = "#d4f4f4"
ctx.fillRect(-1350 - 400, -2100, 400, 250)
ctx.fillStyle = "#d4d4d7"
ctx.fillRect(-3350 - 50, -1300, 50, 1325)
ctx.fillRect(-1300 - 750, -1800, 750, 1800)
level.exit.drawAndCheck();
level.enter.draw();
};
level.customTopLayer = () => {
ctx.fillStyle = "rgba(0,0,0,0.1)"
ctx.fillRect(-2500 - 450, -1100, 450, 250)
ctx.fillRect(-2400 - 600, -550, 600, 150)
ctx.fillRect(-2550 - 250, -1650, 250, 200)
ctx.fillStyle = "rgba(0,0,0,0.2)"
ctx.fillRect(-700 - 400, -110, 400, 110)
ctx.fillRect(-3800 - 400, -110, 400, 110)
ctx.fillStyle = "rgba(0,0,0,0.15)"
ctx.fillRect(250 - 450, -300, 450, 300)
};
}
},
highrise() {
const elevator1 = level.elevator(-790, -190, 180, 25, -1150, 0.0025, {
up: 0.01,
down: 0.2
}, true) //x, y, width, height, maxHeight, force = 0.003, friction = { up: 0.01, down: 0.2 }) {
elevator1.addConstraint();
// const button1 = level.button(-500, -200)
const toggle1 = level.toggle(-300, -200) //(x,y,isOn,isLockOn = true/false)
const elevator2 = level.elevator(-3630, -970, 180, 25, -1740, 0.004) //x, y, width, height, maxHeight, force = 0.003, friction = { up: 0.01, down: 0.2 }) {
elevator2.addConstraint();
// const button2 = level.button(-3100, -1330)
const toggle2 = level.toggle(-3100, -1330) //(x,y,isOn, isLockOn = true/false)
level.custom = () => {
// ctx.fillStyle = "#d0d0d2"
// ctx.fillRect(-2475, -2450, 25, 750)
// ctx.fillRect(-2975, -2750, 25, 600)
// ctx.fillRect(-3375, -2875, 25, 725)
ctx.fillStyle = "#cff" //exit
ctx.fillRect(-4425, -3050, 425, 275)
level.exit.drawAndCheck();
level.enter.draw();
};
level.customTopLayer = () => {
// button1.draw();
toggle1.query();
if (!toggle1.isOn) {
if (elevator1.isOn) {
elevator1.isOn = false
elevator1.frictionAir = 0.2
elevator1.addConstraint();
}
} else if (!elevator1.isOn) {
elevator1.isOn = true
elevator1.isUp = false
elevator1.removeConstraint();
elevator1.frictionAir = 0.2 //elevator.isUp ? 0.01 : 0.2
}
if (elevator1.isOn) {
elevator1.move();
ctx.fillStyle = "#444"
} else {
ctx.fillStyle = "#aaa"
}
ctx.fillRect(-700, -1140, 1, 975)
toggle2.query();
// button2.draw();
if (!toggle2.isOn) {
if (elevator2.isOn) {
elevator2.isOn = false
elevator2.frictionAir = 0.2
elevator2.addConstraint();
}
} else if (!elevator2.isOn) {
elevator2.isOn = true
elevator2.isUp = false
elevator2.removeConstraint();
elevator2.frictionAir = 0.2 //elevator.isUp ? 0.01 : 0.2
}
if (elevator2.isOn) {
elevator2.move();
ctx.fillStyle = "#444"
} else {
ctx.fillStyle = "#aaa"
}
ctx.fillRect(-3540, -1720, 1, 770)
ctx.fillStyle = "rgba(64,64,64,0.97)" //hidden section
ctx.fillRect(-4450, -750, 800, 200)
ctx.fillStyle = "rgba(0,0,0,0.12)"
ctx.fillRect(-2500, -1975, 150, 300);
ctx.fillRect(-1830, -1150, 2030, 1150)
ctx.fillRect(-3410, -2150, 495, 1550)
ctx.fillRect(-2585, -1675, 420, 1125)
ctx.fillRect(-1650, -1575, 750, 450)
};
level.setPosToSpawn(-300, -700); //normal spawn
spawn.mapRect(level.enter.x, level.enter.y + 20, 100, 20);
level.exit.x = -4275;
level.exit.y = -2805;
level.defaultZoom = 1500
simulation.zoomTransition(level.defaultZoom)
powerUps.spawnStartingPowerUps(-2550, -700);
document.body.style.backgroundColor = "#dcdcde" //"#fafcff";
spawn.debris(-2325, -1825, 2400); //16 debris per level
spawn.debris(-2625, -600, 600, 5); //16 debris per level
spawn.debris(-2000, -60, 1200, 5); //16 debris per level
//3 platforms that lead to exit
// spawn.mapRect(-3440, -2875, 155, 25);
// spawn.mapRect(-3025, -2775, 125, 25);
// spawn.mapRect(-2525, -2475, 125, 25);
// spawn.bodyRect(-2600, -2500, 225, 20, 0.7);
// spawn.bodyRect(-3350, -2900, 25, 25, 0.5);
// spawn.bodyRect(-3400, -2950, 50, 75, 0.5);
powerUps.spawn(-4300, -700, "heal");
powerUps.spawn(-4200, -700, "ammo");
powerUps.spawn(-4000, -700, "ammo");
spawn.mapRect(-4450, -1000, 100, 500);
spawn.bodyRect(-3300, -750, 150, 150);
//building 1
spawn.bodyRect(-1000, -675, 25, 25);
spawn.mapRect(-2225, 0, 2475, 150);
spawn.mapRect(175, -1000, 75, 1100);
spawn.mapRect(-600, -1075, 50, 475);
spawn.mapRect(-600, -650, 625, 50);
spawn.mapRect(-1300, -650, 500, 50);
spawn.bodyRect(-75, -300, 50, 50);
spawn.mapRect(-600, -200, 500, 250); //ledge for boarding elevator
spawn.bodyRect(-500, -300, 100, 100, 0.6); //a nice block near the elevator
spawn.bodyRect(-425, -1375, 400, 225);
spawn.mapRect(-925, -1575, 50, 475);
spawn.bodyRect(-1475, -1275, 250, 125);
// spawn.mapRect(-1650, -1575, 600, 50);
// spawn.mapRect(-1875, -1575, 850, 50);
spawn.mapRect(-1675, -1575, 650, 50);
spawn.mapRect(-600, -1150, 850, 175);
spawn.mapRect(-1850, -1150, 1050, 175);
spawn.bodyRect(-1907, -1600, 550, 25);
if (simulation.difficulty < 4) {
spawn.bodyRect(-1600, -125, 125, 125);
spawn.bodyRect(-1560, -200, 75, 75);
} else {
spawn.bodyRect(-1200, -125, 125, 125);
spawn.bodyRect(-1160, -200, 75, 75);
}
//building 2
spawn.mapRect(-4450, -600, 2300, 750);
spawn.mapRect(-2225, -450, 175, 550);
// spawn.mapRect(-2600, -975, 450, 50);
spawn.mapRect(-3425, -1325, 525, 75);
spawn.mapRect(-3425, -2200, 525, 50);
spawn.mapRect(-2600, -1700, 450, 50);
// spawn.mapRect(-2600, -2450, 450, 50);
spawn.bodyRect(-2275, -2700, 50, 60);
// spawn.bodyRect(-2560, -1925, 250, 225);
// spawn.mapRect(-2525, -2025, 125, 25);
// spawn.mapRect(-2525, -1900, 125, 225);
// spawn.mapRect(-2600, -1975, 250, 25);
spawn.mapRect(-2515, -2000, 180, 50);
spawn.bodyRect(-3410, -1425, 50, 50);
spawn.bodyRect(-3390, -1525, 40, 60);
// spawn.bodyRect(-3245, -1425, 100, 100);
//building 3
spawn.mapRect(-4450, -1750, 800, 1050);
// spawn.mapRect(-3850, -2000, 125, 400);
spawn.mapRect(-4000, -2390, 200, 800);
// spawn.mapRect(-4450, -2650, 475, 1000);
spawn.mapRect(-4450, -2775, 475, 1125);
spawn.bodyRect(-3715, -2050, 50, 50);
// spawn.bodyRect(-3570, -1800, 50, 50);
spawn.bodyRect(-2970, -2250, 50, 50);
spawn.bodyRect(-3080, -2250, 40, 40);
spawn.bodyRect(-3420, -650, 50, 50);
//exit
spawn.mapRect(-4450, -3075, 25, 300);
spawn.mapRect(-4450, -3075, 450, 25);
spawn.mapRect(-4025, -3075, 25, 100);
spawn.mapRect(-4275, -2785, 100, 25);
spawn.bodyRect(-3900, -2400, 50, 50);
//mobs
spawn.randomMob(-2500, -2700, 1);
spawn.randomMob(-3200, -750, 1);
spawn.randomMob(-1875, -775, 0.2);
spawn.randomMob(-950, -1675, 0.2);
spawn.randomMob(-1525, -1750, 0.2);
spawn.randomMob(-1375, -1400, 0.2);
spawn.randomMob(-1625, -1275, 0.2);
spawn.randomMob(-1900, -1250, 0.2);
spawn.randomMob(-2250, -1850, 0.2);
spawn.randomMob(-2475, -2200, 0.2);
spawn.randomMob(-3000, -1475, 0.2);
spawn.randomMob(-3850, -2500, 0.2);
spawn.randomMob(-3650, -2125, 0.2);
spawn.randomMob(-4010, -3200, 0.2);
spawn.randomMob(-3500, -1825, 0.2);
spawn.randomMob(-975, -100, 0);
spawn.randomMob(-1050, -725, 0.2);
spawn.randomMob(-1525, -100, 0);
spawn.randomMob(-525, -1700, -0.1);
spawn.randomMob(-125, -1500, -0.1);
spawn.randomMob(-325, -1900, -0.1);
spawn.randomMob(-550, -100, -0.1);
spawn.randomGroup(-3250, -2700, 0.2);
spawn.randomGroup(-2450, -1100, 0);
if (simulation.difficulty > 1) spawn.randomLevelBoss(-2400, -2650);
powerUps.addResearchToLevel() //needs to run after mobs are spawned
spawn.secondaryBossChance(-1825, -1975)
if (simulation.isHorizontalFlipped) { //flip the map horizontally
level.flipHorizontal(); //only flips map,body,mob,powerUp,cons,consBB, exit
// boost1.boostBounds.min.x = -boost1.boostBounds.min.x - 100
// boost1.boostBounds.max.x = -boost1.boostBounds.max.x + 100
level.setPosToSpawn(300, -700); //-x
elevator1.holdX = -elevator1.holdX // flip the elevator horizontally
elevator1.removeConstraint();
elevator1.addConstraint();
elevator2.holdX = -elevator2.holdX // flip the elevator horizontally
elevator2.removeConstraint();
elevator2.addConstraint();
level.custom = () => {
ctx.fillStyle = "#cff" //exit
ctx.fillRect(4425 - 425, -3050, 425, 275)
level.exit.drawAndCheck();
level.enter.draw();
};
level.customTopLayer = () => {
toggle1.query();
if (!toggle1.isOn) {
if (elevator1.isOn) {
elevator1.isOn = false
elevator1.frictionAir = 0.2
elevator1.addConstraint();
}
} else if (!elevator1.isOn) {
elevator1.isOn = true
elevator1.isUp = false
elevator1.removeConstraint();
elevator1.frictionAir = 0.2 //elevator.isUp ? 0.01 : 0.2
}
if (elevator1.isOn) {
elevator1.move();
ctx.fillStyle = "#444"
ctx.fillRect(700 - 1, -1140, 1, 975)
} else {
ctx.fillStyle = "#aaa"
ctx.fillRect(700 - 1, -1140, 1, 975)
}
toggle2.query();
if (!toggle2.isOn) {
if (elevator2.isOn) {
elevator2.isOn = false
elevator2.frictionAir = 0.2
elevator2.addConstraint();
}
} else if (!elevator2.isOn) {
elevator2.isOn = true
elevator2.isUp = false
elevator2.removeConstraint();
elevator2.frictionAir = 0.2 //elevator.isUp ? 0.01 : 0.2
}
if (elevator2.isOn) {
elevator2.move();
ctx.fillStyle = "#444"
ctx.fillRect(3540 - 1, -1720, 1, 740)
} else {
ctx.fillStyle = "#aaa"
ctx.fillRect(3540 - 1, -1720, 1, 740)
}
ctx.fillStyle = "rgba(64,64,64,0.97)" //hidden section
ctx.fillRect(4450 - 800, -750, 800, 200)
ctx.fillStyle = "rgba(0,0,0,0.12)"
ctx.fillRect(2500 - 150, -1975, 150, 300);
ctx.fillRect(1830 - 2030, -1150, 2030, 1150)
ctx.fillRect(3410 - 495, -2150, 495, 1550)
ctx.fillRect(2585 - 420, -1675, 420, 1125)
ctx.fillRect(1650 - 750, -1575, 750, 450)
};
}
},
warehouse() {
level.custom = () => {
ctx.fillStyle = "#444" //light fixtures
ctx.fillRect(-920, -505, 40, 10)
ctx.fillRect(-920, 95, 40, 10)
ctx.fillRect(180, 95, 40, 10)
ctx.fillRect(-20, 695, 40, 10)
ctx.fillRect(-2320, 945, 40, 10)
ctx.fillStyle = "#cff" //exit
ctx.fillRect(300, -250, 350, 250)
level.exit.drawAndCheck();
level.enter.draw();
};
const lightingPath = new Path2D() //pre-draw the complex lighting path to save processing
lightingPath.moveTo(-1800, -500)
lightingPath.lineTo(-910, -500) //3rd floor light
lightingPath.lineTo(-1300, 0)
lightingPath.lineTo(-500, 0)
lightingPath.lineTo(-890, -500)
lightingPath.lineTo(-175, -500)
lightingPath.lineTo(-175, -250)
lightingPath.lineTo(175, -250)
lightingPath.lineTo(175, 0)
lightingPath.lineTo(-910, 100) //2nd floor light left
lightingPath.lineTo(-1300, 600)
lightingPath.lineTo(-500, 600)
lightingPath.lineTo(-890, 100)
lightingPath.lineTo(190, 100) //2nd floor light right
lightingPath.lineTo(-200, 600)
lightingPath.lineTo(600, 600)
lightingPath.lineTo(210, 100)
lightingPath.lineTo(1100, 100)
lightingPath.lineTo(1100, 1400)
lightingPath.lineTo(600, 1400) //1st floor light right
lightingPath.lineTo(10, 700)
lightingPath.lineTo(-10, 700)
lightingPath.lineTo(-600, 1400)
lightingPath.lineTo(-1950, 1400) //1st floor light left
lightingPath.lineTo(-2290, 950)
lightingPath.lineTo(-2310, 950)
lightingPath.lineTo(-2650, 1400)
lightingPath.lineTo(-3025, 1400)
lightingPath.lineTo(-3025, 150)
lightingPath.lineTo(-2590, 150)
lightingPath.lineTo(-2600, -150)
lightingPath.lineTo(-1800, -150)
lightingPath.lineTo(-1800, -500) //top left end/start of path
level.customTopLayer = () => {
ctx.fillStyle = "rgba(0,0,0,0.15)"; //shadows and lights
ctx.fill(lightingPath);
};
level.setPosToSpawn(25, -55); //normal spawn
level.exit.x = 425;
level.exit.y = -30;
level.defaultZoom = 1300
simulation.zoomTransition(level.defaultZoom)
spawn.debris(-2250, 1330, 3000, 6); //16 debris per level
spawn.debris(-3000, -800, 3280, 6); //16 debris per level
spawn.debris(-1400, 410, 2300, 5); //16 debris per level
powerUps.spawnStartingPowerUps(25, 500);
document.body.style.backgroundColor = "#dcdcde" //"#f2f5f3";
spawn.mapRect(-1500, 0, 2750, 100);
spawn.mapRect(175, -270, 125, 300);
spawn.mapRect(-1900, -600, 1775, 100);
spawn.mapRect(-1900, -550, 100, 1250);
//house
spawn.mapRect(-225, -550, 100, 400);
spawn.mapRect(-225, -10, 400, 50);
spawn.mapRect(-25, -20, 100, 50);
//exit house
spawn.mapRect(300, -10, 350, 50);
spawn.mapRect(-150, -350, 800, 100);
spawn.mapRect(600, -275, 50, 75);
spawn.mapRect(425, -20, 100, 25);
// spawn.mapRect(-1900, 600, 2700, 100);
spawn.mapRect(1100, 0, 150, 1500);
spawn.mapRect(-3150, 1400, 4400, 100);
spawn.mapRect(-2375, 875, 1775, 75);
spawn.mapRect(-1450, 865, 75, 435);
spawn.mapRect(-1450, 662, 75, 100);
spawn.bodyRect(-1418, 773, 11, 102, 1, spawn.propsFriction); //blocking path
spawn.mapRect(-3150, 50, 125, 1450);
spawn.mapRect(-2350, 600, 3150, 100);
spawn.mapRect(-2125, 400, 250, 275);
// spawn.mapRect(-1950, -400, 100, 25);
spawn.mapRect(-3150, 50, 775, 100);
spawn.mapRect(-2600, -250, 775, 100);
let isElevators = false
let elevator1, elevator2, elevator3
if (Math.random() < 0.5) {
isElevators = true
elevator1 = level.elevator(-1780, 500, 260, 40, 7, 0.0003) // x, y, width, height, maxHeight, force = 0.003, friction = { up: 0.01, down: 0.2 }) {
elevator2 = level.elevator(820, 1300, 260, 40, 607, 0.0003)
elevator3 = level.elevator(-2850, 1250, 160, 40, 600, 0.007)
if (simulation.isHorizontalFlipped) {
spawn.mapVertex(-2900, 225, "0 0 0 -500 -500 -500")
} else {
spawn.mapVertex(-2900, 225, "0 0 0 -500 500 -500")
}
spawn.mapRect(-3050, 1175, 175, 300);
spawn.bodyRect(-2375, 1300, 100, 100);
spawn.bodyRect(-2325, 1250, 50, 50);
spawn.bodyRect(-2275, 1350, 125, 50);
level.custom = () => {
elevator1.move();
elevator1.drawTrack();
elevator2.move();
elevator2.drawTrack();
elevator3.move();
elevator3.drawTrack();
ctx.fillStyle = "#444" //light fixtures
ctx.fillRect(-920, -505, 40, 10)
ctx.fillRect(-920, 95, 40, 10)
ctx.fillRect(180, 95, 40, 10)
ctx.fillRect(-20, 695, 40, 10)
ctx.fillRect(-2320, 945, 40, 10)
ctx.fillStyle = "#cff" //exit
ctx.fillRect(300, -250, 350, 250)
level.exit.drawAndCheck();
level.enter.draw();
};
} else {
spawn.mapRect(-2950, 1250, 175, 250);
spawn.mapRect(-3050, 1100, 150, 400);
spawn.bodyRect(-1450, -125, 125, 125, 1, spawn.propsSlide); //weight
spawn.bodyRect(-1800, 0, 300, 100, 1, spawn.propsHoist); //hoist
cons[cons.length] = Constraint.create({
pointA: {
x: -1650,
y: -500
},
bodyB: body[body.length - 1],
stiffness: 0.0001815,
length: 1
});
Composite.add(engine.world, cons[cons.length - 1]);
spawn.bodyRect(600, 525, 125, 125, 1, spawn.propsSlide); //weight
spawn.bodyRect(800, 600, 300, 100, 1, spawn.propsHoist); //hoist
cons[cons.length] = Constraint.create({
pointA: {
x: 950,
y: 100
},
bodyB: body[body.length - 1],
stiffness: 0.0001815,
length: 1
});
Composite.add(engine.world, cons[cons.length - 1]);
spawn.bodyRect(-2700, 1150, 100, 160, 1, spawn.propsSlide); //weight
spawn.bodyRect(-2550, 1200, 150, 150, 1, spawn.propsSlide); //weight
spawn.bodyRect(-2763, 1300, 350, 100, 1, spawn.propsHoist); //hoist
cons[cons.length] = Constraint.create({
pointA: {
x: -2575,
y: 150
},
bodyB: body[body.length - 1],
stiffness: 0.0004,
length: 566
});
Composite.add(engine.world, cons[cons.length - 1]);
}
//blocks
spawn.bodyRect(-212, -150, 30, 35, 1);
spawn.bodyRect(-212, -115, 30, 35, 1);
spawn.bodyRect(-212, -80, 30, 35, 1);
spawn.bodyRect(-212, -45, 30, 35, 1);
spawn.bodyRect(-750, 400, 150, 150, 0.5);
spawn.bodyRect(-400, 1175, 100, 250, 1); //block to get to top path on bottom level
spawn.bodyRect(-2525, -50, 145, 100, 0.5);
spawn.bodyRect(-2325, -300, 150, 100, 0.5);
spawn.bodyRect(-1275, -750, 200, 150, 0.5); //roof block
spawn.bodyRect(-525, -700, 125, 100, 0.5); //roof block
//mobs
spawn.randomSmallMob(-1125, 550);
spawn.randomSmallMob(-2950, -50);
spawn.randomMob(-2025, 175, 0.3);
spawn.randomMob(-2325, 450, 0.3);
spawn.randomMob(-2925, 675, 0.2);
spawn.randomMob(-2700, 300, 0.1);
spawn.randomMob(-2500, 300, 0.1);
spawn.randomMob(-2075, -425, 0.1);
spawn.randomMob(-1550, -725, 0.1);
spawn.randomMob(375, 1100, 0);
spawn.randomMob(-1575, 1100, 0);
spawn.randomSmallMob(825, 300);
spawn.randomMob(-800, -1750, 0);
spawn.randomMob(400, -750, -0.1);
spawn.randomMob(650, 1300, -0.1);
spawn.randomMob(-2450, 1050, -0.1);
spawn.randomMob(500, 400, -0.1);
spawn.randomMob(-75, -1700, -0.1);
spawn.randomMob(900, -800, -0.2);
spawn.randomGroup(-75, 1050, -0.1);
spawn.randomGroup(-900, 1000, 0.2);
spawn.randomGroup(-1300, -1100, -0.3);
spawn.randomSmallMob(-2325, 800);
spawn.randomSmallMob(-900, 825);
if (simulation.difficulty > 1) {
if (Math.random() < 0.80) {
spawn.randomLevelBoss(-800, -1300)
} else {
spawn.dragonFlyBoss(-1000 + Math.random() * 2500, -1300); //boss snake with head
}
}
powerUps.addResearchToLevel() //needs to run after mobs are spawned
spawn.secondaryBossChance(300, -800)
if (simulation.isHorizontalFlipped) { //flip the map horizontally
level.flipHorizontal(); //only flips map,body,mob,powerUp,cons,consBB, exit
// boost1.boostBounds.min.x = -boost1.boostBounds.min.x - 100
// boost1.boostBounds.max.x = -boost1.boostBounds.max.x + 100
level.setPosToSpawn(-25, -55); //-x
if (isElevators) {
elevator1.holdX = -elevator1.holdX // flip the elevator horizontally
elevator2.holdX = -elevator2.holdX // flip the elevator horizontally
elevator3.holdX = -elevator3.holdX // flip the elevator horizontally
level.custom = () => {
elevator1.move();
elevator1.drawTrack();
elevator2.move();
elevator2.drawTrack();
elevator3.move();
elevator3.drawTrack();
ctx.fillStyle = "#444" //light fixtures
ctx.fillRect(920 - 40, -505, 40, 10)
ctx.fillRect(920 - 40, 95, 40, 10)
ctx.fillRect(-180 - 40, 95, 40, 10)
ctx.fillRect(20 - 40, 695, 40, 10)
ctx.fillRect(2320 - 40, 945, 40, 10)
ctx.fillStyle = "#cff" //exit
ctx.fillRect(-300 - 350, -250, 350, 250)
level.exit.drawAndCheck();
level.enter.draw();
};
} else {
level.custom = () => {
ctx.fillStyle = "#444" //light fixtures
ctx.fillRect(920 - 40, -505, 40, 10)
ctx.fillRect(920 - 40, 95, 40, 10)
ctx.fillRect(-180 - 40, 95, 40, 10)
ctx.fillRect(20 - 40, 695, 40, 10)
ctx.fillRect(2320 - 40, 945, 40, 10)
ctx.fillStyle = "#cff" //exit
ctx.fillRect(-300 - 350, -250, 350, 250)
level.exit.drawAndCheck();
level.enter.draw();
};
}
level.customTopLayer = () => {
ctx.fillStyle = "rgba(0,0,0,0.15)"; //shadows and lights
ctx.beginPath()
ctx.moveTo(1800, -500)
ctx.lineTo(910, -500) //3rd floor light
ctx.lineTo(1300, 0)
ctx.lineTo(500, 0)
ctx.lineTo(890, -500)
ctx.lineTo(175, -500)
ctx.lineTo(175, -250)
ctx.lineTo(-175, -250)
ctx.lineTo(-175, 0)
ctx.lineTo(910, 100) //2nd floor light left
ctx.lineTo(1300, 600)
ctx.lineTo(500, 600)
ctx.lineTo(890, 100)
ctx.lineTo(-190, 100) //2nd floor light right
ctx.lineTo(200, 600)
ctx.lineTo(-600, 600)
ctx.lineTo(-210, 100)
ctx.lineTo(-1100, 100)
ctx.lineTo(-1100, 1400)
ctx.lineTo(-600, 1400) //1st floor light right
ctx.lineTo(-10, 700)
ctx.lineTo(10, 700)
ctx.lineTo(600, 1400)
ctx.lineTo(1950, 1400) //1st floor light left
ctx.lineTo(2290, 950)
ctx.lineTo(2310, 950)
ctx.lineTo(2650, 1400)
ctx.lineTo(3025, 1400)
ctx.lineTo(3025, 150)
ctx.lineTo(2590, 150)
ctx.lineTo(2600, -150)
ctx.lineTo(1800, -150)
ctx.lineTo(1800, -500) //top left end/start of path
ctx.fill()
};
}
},
office() {
let button, door
let isReverse = false
if (Math.random() < 0.75) { //normal direction start in top left
button = level.button(525, 0)
door = level.door(1362, -400, 25, 400, 355, 1.5) //door(x, y, width, height, distance, speed = 1) {
level.setPosToSpawn(1375, -1550); //normal spawn
level.exit.x = 3088;
level.exit.y = -630;
} else { //reverse direction, start in bottom right
isReverse = true
button = level.button(3800, 0)
door = level.door(3012, -400, 25, 400, 355, 1.5)
level.setPosToSpawn(3137, -650); //normal spawn
level.exit.x = 1375;
level.exit.y = -1530;
}
level.custom = () => {
button.query();
button.draw();
if (button.isUp) {
door.isClosing = true
} else {
door.isClosing = false
}
door.openClose();
ctx.fillStyle = "#ccc"
ctx.fillRect(2495, -500, 10, 525)
ctx.fillStyle = "#dff"
if (isReverse) {
ctx.fillRect(725, -1950, 825, 450)
} else {
ctx.fillRect(3050, -950, 625, 500)
}
level.exit.drawAndCheck();
level.enter.draw();
};
level.customTopLayer = () => {
ctx.fillStyle = "rgba(0,0,0,0.1)"
ctx.fillRect(3650, -1300, 1300, 1300)
ctx.fillRect(3000, -1000, 650, 1000)
ctx.fillRect(750, -1950, 800, 450)
ctx.fillRect(750, -1450, 650, 1450)
ctx.fillRect(-550, -1700, 1300, 1700)
// ctx.fillRect(0, 0, 0, 0)
door.draw();
};
level.defaultZoom = 1400
simulation.zoomTransition(level.defaultZoom)
spawn.mapRect(level.exit.x, level.exit.y + 20, 100, 50); //ground bump wall
spawn.mapRect(level.enter.x, level.enter.y + 20, 100, 20);
document.body.style.backgroundColor = "#e0e5e0";
spawn.debris(-300, -200, 1000, 6); //ground debris //16 debris per level
spawn.debris(3500, -200, 800, 5); //ground debris //16 debris per level
spawn.debris(-300, -650, 1200, 5); //1st floor debris //16 debris per level
powerUps.spawnStartingPowerUps(-525, -700);
spawn.mapRect(-600, 0, 2000, 325); //ground
spawn.mapRect(1400, 25, 1600, 300); //ground
spawn.mapRect(3000, 0, 2000, 325); //ground
spawn.mapRect(-600, -1700, 50, 2000 - 100); //left wall
spawn.bodyRect(-295, -1540, 40, 40); //center block under wall
spawn.bodyRect(-298, -1580, 40, 40); //center block under wall
spawn.bodyRect(1500, -1540, 30, 30); //left of entrance
spawn.mapRect(1550, -2000, 50, 550); //right wall
// spawn.mapRect(1350, -2000 + 505, 50, 1295);
spawn.mapRect(1350, -1500, 50, 1125); //right wall
spawn.mapRect(-600, -2000 + 250, 2000 - 700, 50); //roof left
spawn.mapRect(-600 + 1300, -2000, 50, 300); //right roof wall
spawn.mapRect(-600 + 1300, -2000, 900, 50); //center wall
map[map.length] = Bodies.polygon(725, -1700, 0, 15); //circle above door
spawn.bodyRect(720, -1675, 15, 170, 1, spawn.propsDoor); // door
body[body.length - 1].isNotHoldable = true;
//makes door swing
consBB[consBB.length] = Constraint.create({
bodyA: body[body.length - 1],
pointA: {
x: 0,
y: -90
},
bodyB: map[map.length - 1],
stiffness: 1
});
Composite.add(engine.world, consBB[consBB.length - 1]);
spawn.mapRect(-600 + 300, -2000 * 0.75, 1900, 50); //3rd floor
spawn.mapRect(-600 + 2000 * 0.7, -2000 * 0.74, 50, 375); //center wall
spawn.bodyRect(-600 + 2000 * 0.7, -2000 * 0.5 - 106, 50, 106); //center block under wall
spawn.mapRect(-600, -1000, 1100, 50); //2nd floor
spawn.mapRect(600, -1000, 500, 50); //2nd floor
spawn.spawnStairs(-600, -1000, 4, 250, 350); //stairs 2nd
spawn.mapRect(375, -600, 350, 150); //center table
spawn.mapRect(-300, -2000 * 0.25, 1690, 50); //1st floor
spawn.spawnStairs(-610 + 2000 - 50, -500, 4, 250, 350, true); //stairs
spawn.spawnStairs(-600, 0, 4, 250, 350); //stairs ground
spawn.bodyRect(700, -200, 100, 100); //center block under wall
spawn.bodyRect(700, -300, 100, 100); //center block under wall
spawn.bodyRect(700, -400, 100, 100); //center block under wall
spawn.mapRect(1390, 13, 30, 20); //step left
spawn.mapRect(2980, 13, 30, 20); //step right
spawn.bodyRect(4250, -700, 50, 100);
spawn.mapRect(3000, -1000, 50, 625); //left wall
spawn.mapRect(3000 + 2000 - 50, -1300, 50, 1100); //right wall
spawn.mapRect(4150, -600, 350, 150); //table
spawn.mapRect(3650, -1300, 50, 700); //exit wall
spawn.mapRect(3650, -1300, 1350, 50); //exit wall
spawn.bodyRect(3665, -600, 20, 100); //door
spawn.mapRect(3025, -600, 250, 125);
spawn.mapRect(3175, -550, 175, 75);
// spawn.mapVertex(3160, -525, "625 0 300 0 300 -140 500 -140"); //entrance/exit ramp
spawn.mapRect(3000, -2000 * 0.5, 700, 50); //exit roof
spawn.mapRect(3010, -2000 * 0.25, 1690, 50); //1st floor
spawn.spawnStairs(3000 + 2000 - 50, 0, 4, 250, 350, true); //stairs ground
spawn.randomSmallMob(4575, -560, 1);
spawn.randomSmallMob(1315, -880, 1);
spawn.randomSmallMob(800, -600);
spawn.randomMob(4100, -225, 0.8);
spawn.randomMob(-250, -700, 0.8);
spawn.randomMob(4500, -225, 0.15);
spawn.randomMob(3250, -225, 0.15);
spawn.randomMob(-100, -225, 0.1);
spawn.randomMob(1150, -225, 0.15);
spawn.randomMob(2000, -225, 0.15);
spawn.randomMob(450, -225, 0.15);
spawn.randomMob(100, -1200, 1);
spawn.randomMob(950, -1150, -0.1);
spawn.randomGroup(1800, -800, -0.2);
spawn.randomGroup(4150, -1000, 0.6);
if (simulation.difficulty > 1) {
if (Math.random() < 0.5) {
spawn.tetherBoss(2850, -80, { x: 2500, y: -500 })
} else {
spawn.randomLevelBoss(2200, -450)
}
}
powerUps.addResearchToLevel() //needs to run after mobs are spawned
spawn.secondaryBossChance(1875, -675)
if (simulation.isHorizontalFlipped) { //flip the map horizontally
level.flipHorizontal(); //only flips map,body,mob,powerUp,cons,consBB, exit
level.setPosToSpawn(50, -60);
if (!isReverse) { //normal direction start in top left
level.setPosToSpawn(-1375, -1550); //normal spawn //-x
} else { //reverse direction, start in bottom right
level.setPosToSpawn(-3137, -650); //normal spawn
}
button.min.x = -button.min.x - 126 // flip the button horizontally
button.max.x = -button.max.x + 126 // flip the button horizontally
level.custom = () => {
button.query();
button.draw();
if (button.isUp) {
door.isClosing = true
} else {
door.isClosing = false
}
door.openClose();
ctx.fillStyle = "#ccc"
ctx.fillRect(-2495 - 10, -500, 10, 525)
ctx.fillStyle = "#dff"
if (isReverse) {
ctx.fillRect(-725 - 825, -1950, 825, 450)
} else {
ctx.fillRect(-3050 - 625, -950, 625, 500)
}
level.exit.drawAndCheck();
level.enter.draw();
};
level.customTopLayer = () => {
ctx.fillStyle = "rgba(0,0,0,0.1)"
ctx.fillRect(-3650 - 1300, -1300, 1300, 1300)
ctx.fillRect(-3000 - 650, -1000, 650, 1000)
ctx.fillRect(-750 - 800, -1950, 800, 450)
ctx.fillRect(-750 - 650, -1450, 650, 1450)
ctx.fillRect(550 - 1300, -1700, 1300, 1700)
// ctx.fillRect(0, 0, 0, 0)
door.draw();
};
}
},
stronghold() { // player made level by Francois 👑 from discord
simulation.makeTextLog(`<strong>stronghold</strong> by <span class='color-var'>Francois</span>`);
const boost1 = level.boost(1470, -250, 1080)
const boost2 = level.boost(-370, 0, 800)
const boost3 = level.boost(4865, 0, 1800)
level.custom = () => {
boost1.query();
boost2.query();
boost3.query();
ctx.fillStyle = "#edf9f9";
ctx.fillRect(-500, -1220, 550, -480);
ctx.fillStyle = "rgba(0,0,0,0.1)";
ctx.fillRect(0, -700, 1050, 700);
ctx.fillRect(-550, -1170, 550, 1170);
ctx.fillRect(1150, -1700, 250, 1700);
ctx.fillRect(1100, -1700, 50, 450);
ctx.fillRect(1050, -1200, 100, 1200);
ctx.fillRect(1400, -250, 200, -1500);
ctx.fillRect(1600, -550, 600, -1150);
ctx.fillRect(2530, -550, 430, -1450);
ctx.fillRect(3270, -1700, 80, 600);
ctx.fillRect(3350, -1350, 700, 230);
ctx.fillRect(4050, -1700, 600, 1290);
ctx.fillRect(3650, -110, 1000, 170);
ctx.fillRect(4865, -55, 100, 55);
level.exit.drawAndCheck();
level.enter.draw();
};
level.customTopLayer = () => {
};
level.setPosToSpawn(1900, -40); //normal spawn
level.exit.x = -350;
level.exit.y = -1250;
level.defaultZoom = 1400
simulation.zoomTransition(level.defaultZoom)
spawn.mapRect(level.exit.x, level.exit.y + 25, 100, 20); //exit bump
spawn.debris(3800, -1480, 300, 12);
spawn.debris(3600, -1130, 200, 2);
document.body.style.backgroundColor = "#dbdcde";
// simulation.draw.mapFill = "#444"
// simulation.draw.bodyFill = "rgba(140,140,140,0.85)"
// simulation.draw.bodyStroke = "#222"
// __________________________________________________________________________________________________
// Spawn Box
spawn.mapRect(1600, -500, 50, 500); //Left Wall
spawn.mapRect(1600, -550, 1500, 50); //Roof
spawn.mapRect(2300, -500, 50, 300); //Right Wall
spawn.mapRect(-550, 0, 4300, 200); //ground
spawn.mapRect(3700, 55, 1300, 145); //2nd ground
spawn.mapRect(5000, 0, 50, 200); //Last small part of the ground
spawn.mapRect(3100, -1070, 50, 570); // vertical 2nd roof
spawn.mapRect(3100, -1120, 950, 50); // Horizontal 2nd Roof
spawn.mapRect(4050, -1750, 600, 50); // Roof after lift
spawn.mapRect(4600, -1700, 50, 100); // Petit retour de toit, après ascenseur
//Spawn "Upstairs"
spawn.mapRect(3650, -160, 400, 50); //Thin Walk
spawn.mapRect(4050, -410, 600, 300); //Large staircase block
spawn.mapRect(4600, -1120, 50, 710); //Left Wall Wall upstairs
spawn.mapRect(4550, -1170, 100, 50); //Bloque ascenseur
spawn.mapVertex(3700, 35, "0 0 450 0 300 -60 150 -60"); //first slope
spawn.mapVertex(4850, 35, "0 0 370 0 370 -65 150 -65"); //second slope
spawn.bodyRect(3950, -280, 170, 120); //Bloc Marche Pour Monter À Ascenseur
// spawn.bodyRect(-2700, 1150, 100, 160, 1, spawn.propsSlide); //weight
// spawn.bodyRect(-2550, 1150, 200, 100, 1, spawn.propsSlide); //weight
spawn.bodyRect(4050, -500, 275, 100, 1, spawn.propsSlide); //weight
spawn.bodyRect(4235, -500, 275, 100, 1, spawn.propsSlide); //weight
// spawn.bodyRect(-2775, 1300, 400, 100, 1, spawn.propsHoist); //hoist
spawn.bodyRect(4025, -450, 550, 100, 1, spawn.propsHoist); //hoist
cons[cons.length] = Constraint.create({
pointA: {
x: 4325,
y: -1700,
},
bodyB: body[body.length - 1],
stiffness: 0.0002, //1217,
length: 200
});
Composite.add(engine.world, cons[cons.length - 1]);
spawn.bodyRect(2799, -870, 310, 290); //Gros bloc angle toit
spawn.mapRect(4000, -1750, 50, 400); //Right Wall Cuve
spawn.mapRect(3400, -1400, 600, 50); // Bottom Cuve
spawn.mapRect(3350, -1750, 50, 400); // Left Wall Cuve
spawn.bodyRect(3400, -1470, 110, 70); //Moyen bloc dans la cuve
spawn.mapRect(3270, -1750, 80, 50); // Rebord gauche cuve
spawn.mapRect(2530, -2000, 430, 50); //First Plateforme
spawn.mapRect(1600, -1750, 600, 50); // Middle plateforme
spawn.mapRect(1100, -1750, 300, 50); //Derniere plateforme // Toit petite boite en [
spawn.bodyRect(1830, -1980, 190, 230); // Fat bloc plateforme middle
spawn.bodyRect(1380, -1770, 250, 20) // Pont last plateforme
spawn.mapRect(1000, -1250, 400, 50); //Sol de la petite boite en [
spawn.mapRect(1100, -1550, 50, 190); //Mur gauche petite boite en [
spawn.bodyRect(1100, -1380, 48, 109); //Bloc-porte petite boite en [
spawn.mapRect(-100, -750, 1100, 50); //Sol last salle
spawn.mapRect(1000, -1200, 50, 500) // Mur droit last salle
spawn.mapRect(50, -1550, 1050, 50); // Toit last salle
spawn.bodyRect(1, -900, 48, 150); //Bloc porte last salle
spawn.mapRect(0, -1170, 50, 270); //Mur gauche en bas last salle
spawn.bodyRect(920, -900, 120, 120); //Gros bloc last salle
spawn.mapRect(0, -1700, 50, 320); // Mur droit salle exit / Mur gauche last salle
spawn.mapRect(-550, -1220, 600, 50); // Sol exit room
spawn.mapRect(-500, -1750, 550, 50); // Toit exit room
spawn.mapRect(-550, -1750, 50, 530); // Mur gauche exit room
spawn.bodyRect(-503, -1250, 30, 30); // Petit bloc exit room
spawn.mapRect(500, -700, 100, 590); //Bloc noir un dessous last salle
spawn.mapRect(1350, -250, 250, 250); //Black Block left from the spawn
map[map.length] = Bodies.polygon(2325, -205, 0, 15); //circle above door
spawn.bodyRect(2325, -180, 15, 170, 1, spawn.propsDoor); // door
body[body.length - 1].isNotHoldable = true;
//makes door swing
consBB[consBB.length] = Constraint.create({
bodyA: body[body.length - 1],
pointA: {
x: 0,
y: -90
},
bodyB: map[map.length - 1],
stiffness: 1
});
Composite.add(engine.world, consBB[consBB.length - 1]);
spawn.bodyRect(650, 50, 70, 50);
spawn.bodyRect(300, 0, 100, 60);
spawn.bodyRect(400, 0, 100, 150);
spawn.bodyRect(2545, -50, 70, 50);
spawn.bodyRect(2550, 0, 100, 30);
spawn.randomSmallMob(200, -1300, 0.5);
spawn.randomSmallMob(300, -1300, 0.9);
spawn.randomSmallMob(470, -650, 1);
spawn.randomSmallMob(1000, -400, 1);
spawn.randomSmallMob(2550, -560, 1);
spawn.randomSmallMob(3350, -900, 1);
spawn.randomSmallMob(3600, -1210, 1);
spawn.randomSmallMob(700, -1950, 0.2);
spawn.randomSmallMob(5050, -550);
spawn.randomMob(-250, -250, 0.8);
spawn.randomMob(-300, -600, 0.6);
spawn.randomMob(350, -900, 0.5);
spawn.randomMob(770, -950, 0.8)
spawn.randomMob(900, -160, 1);
spawn.randomMob(2360, -820, 0.8);
spawn.randomMob(2700, -2020, 0.8);
spawn.randomMob(3050, -1650, 0.8);
spawn.randomMob(3350, -600, 0.8);
spawn.randomMob(4400, -50, 1);
spawn.randomGroup(1500, -1900, 0.5);
spawn.randomGroup(2350, -850, 1);
spawn.randomGroup(100, -450, 0.9);
if (simulation.difficulty > 1) spawn.randomLevelBoss(1850, -1400);
spawn.secondaryBossChance(1850, -1400)
powerUps.addResearchToLevel() //needs to run after mobs are spawned
},
basement() { // player made level by Francois 👑 from discord
simulation.makeTextLog(`<strong>basement</strong> by <span class='color-var'>Francois</span>`);
let button, door, buttonDoor, buttonPlateformEnd, doorPlateform
let isLevelReversed = Math.random();
if (isLevelReversed < 0.7) {
isLevelReversed = false;
} else {
isLevelReversed = true;
}
const elevator = level.elevator(4545, -220, 110, 30, -3000)
const hazard = level.hazard(1675, -1050, 800, 150);
const portal = level.portal({
x: -620,
y: -257
}, Math.PI / 2, { //down
x: 500,
y: 2025
}, -Math.PI / 2) //up
spawn.mapRect(350, 2025, 300, 300); //Bloc portail n°2
if (isLevelReversed === false) { /// Normal Spawn
button = level.button(2700, -1150);
level.setPosToSpawn(2600, -2050); //normal spawn
level.exit.x = level.enter.x + 4510;
level.exit.y = level.enter.y + 600;
spawn.mapRect(level.exit.x, level.exit.y + 20, 100, 20);
spawn.mapRect(level.enter.x, level.enter.y + 20, 100, 20);
} else { /// Reversed spawn
button = level.button(1450, -1150);
buttonPlateformEnd = level.button(3530, -1150);
buttonDoor = level.button(8033, -3625);
door = level.door(7700, -3905, 25, 184, 184);
doorPlateform = level.door(3200, -1225, 299, 80, 525);
level.setPosToSpawn(7110, -1450); //normal spawn
level.exit.x = level.enter.x - 4510;
level.exit.y = level.enter.y - 600;
spawn.mapRect(level.exit.x, level.exit.y + 20, 100, 20);
spawn.mapRect(level.enter.x, level.enter.y + 20, 100, 20);
spawn.mapRect(7675, -3935, 75, 25);
spawn.mapRect(7675, -3715, 75, 25);
spawn.bodyRect(8075, -3675, 50, 25);
}
const boost1 = level.boost(8290, -2100, 1800)
level.custom = () => {
boost1.query();
level.exit.drawAndCheck();
portal[2].query()
portal[3].query()
button.query();
button.draw();
if (isLevelReversed === true) { ///Reversed spawn
buttonDoor.draw();
buttonDoor.query();
buttonPlateformEnd.draw();
buttonPlateformEnd.query();
// hazard.query(); //bug reported from discord?
if (buttonDoor.isUp) {
door.isClosing = false
} else {
door.isClosing = true
}
door.openClose();
if (buttonPlateformEnd.isUp) {
doorPlateform.isClosing = true;
} else {
doorPlateform.isClosing = false;
}
door.openClose();
doorPlateform.openClose();
}
hazard.level(button.isUp)
level.enter.draw();
elevator.move();
elevator.drawTrack();
};
level.customTopLayer = () => {
ctx.fillStyle = "rgba(61,62,62,0.95)";
ctx.fillRect(-750, -900, 750, 450);
if (isLevelReversed === true) {
door.draw();
doorPlateform.draw();
}
portal[0].draw();
portal[1].draw();
portal[2].draw();
portal[3].draw();
hazard.query();
};
level.defaultZoom = 1300
simulation.zoomTransition(level.defaultZoom)
document.body.style.backgroundColor = "#c7c7c7";
// GROUND //
spawn.mapRect(-400, -2000, 400, 1430); //Gros left wall
spawn.mapRect(3700, -3000, 700, 2650); //Gros right wall //Puit
spawn.mapRect(-400, -2000, 3700, 250); //Ground
spawn.mapRect(2475, -1150, 1225, 250);
spawn.mapRect(500, -1150, 1175, 250); //Ground level 3
spawn.mapRect(350, -180, 4600, 1255); // Last ground
spawn.mapRect(-400, -458, 750, 3337); //mur left sous-sol
spawn.mapRect(-2850, -3375, 5300, 1375);
spawn.mapRect(-2850, -4200, 8000, 825);
spawn.mapRect(3700, -3375, 550, 375);
spawn.mapRect(-2850, -5200, 10200, 1000);
spawn.mapRect(5600, -1250, 3550, 2000);
spawn.mapRect(9150, -5200, 1725, 5800);
// SPAWN BOX //
spawn.mapRect(2300, -3375, 950, 1000);
spawn.mapRect(3550, -3375, 150, 1625);
spawn.mapVertex(2020, -791, " 250 250 -860 250 -2200 0 250 0"); //map vertex en haut
spawn.mapVertex(690, -295, "1700 0 -200 0 -200 -284 500 -284"); //map vertex en bas
spawn.mapRect(2950, -900, 750, 250); //Extension ground apres map vertex
if (isLevelReversed === false) {
spawn.mapRect(3250, -1800, 50, 150); //Petit picot en haut, à gauche
spawn.mapRect(3400, -1800, 50, 150); //Petit picot en haut, à droite
spawn.mapRect(3150, -1300, 50, 200) //Petit picot en bas, à gauche
spawn.mapRect(3500, -1300, 50, 200) //Petit picot en bas, à droite
spawn.mapRect(3050, -3375, 500, 1260);
spawn.mapRect(3400, -2265, 150, 515); //Mur fond tunnel
spawn.bodyRect(3625, -1225, 75, 75); //Pitit bloc à droite en bas spawn
} else {
spawn.mapRect(3050, -3375, 500, 1000);
spawn.mapRect(3400, -2400, 150, 650); //Mur fond tunnel
spawn.bodyRect(3425, -1515, 75, 75); //Petit en bas spawn
spawn.mapRect(3200, -1275, 300, 175);
}
// TRAMPOLING //
if (isLevelReversed === false) { /// Normal spawn
spawn.bodyRect(0, -1000, 500, 120, 1, spawn.propsHoist); //hoist
cons[cons.length] = Constraint.create({
pointA: {
x: 250,
y: -1750,
},
bodyB: body[body.length - 1],
stiffness: 0.00014,
length: 120
});
Composite.add(engine.world, cons[cons.length - 1]);
spawn.bodyRect(0, -1250, 240, 190) //Fat cube ascenseur
} else { /// Reversed spawn
spawn.bodyRect(0, -650, 225, 175);
spawn.mapRect(425, -950, 175, 50);
spawn.mapRect(-25, -1150, 100, 50);
}
// PUIT //
spawn.mapVertex(4200, -1810, "0 0 450 0 600 -2500 0 -2500")
spawn.mapVertex(5000, -1809, "0 0 450 0 450 -2500 -150 -2500")
spawn.mapRect(4800, -3000, 800, 5875); //big right Puit
// BOSS AREA //
spawn.mapRect(4800, -3150, 50, 200); //Premiere barriere
spawn.mapRect(5100, -3530, 50, 380); //2nd barriere
spawn.mapRect(5100, -3200, 150, 50); //Marche en dessous mapVertex 1
spawn.mapVertex(5450, -3650, "220 0 200 30 -200 30 -220 0 -200 -30 200 -30");
spawn.mapVertex(6225, -3350, "275 0 250 50 -250 50 -275 0 -250 -50 250 -50");
spawn.mapRect(5600, -3000, 1600, 725); //ground Boss Area
//Ouverture right boss area
spawn.mapRect(7300, -3325, 50, 50); //petite marche pour accéder à l'ouverture
spawn.mapRect(7350, -4075, 850, 50); //Bouche
spawn.mapRect(7400, -4050, 800, 50); //Bouche
spawn.mapRect(7450, -4025, 750, 50); //Bouche
spawn.mapRect(7500, -4000, 700, 50); //Bouche
spawn.mapRect(7550, -3975, 650, 50); //Bouche
spawn.mapRect(7350, -3600, 850, 50); //Bouche
spawn.mapRect(7400, -3625, 800, 50); //Bouche
spawn.mapRect(7450, -3650, 575, 50); //Bouche
spawn.mapRect(7500, -3675, 525, 50); //Bouche
spawn.mapRect(7550, -3700, 475, 50); //Bouche
//Murs
spawn.mapRect(7350, -5200, 1800, 1125);
spawn.mapRect(8475, -4075, 675, 2825);
spawn.mapRect(7300, -2100, 1175, 850);
spawn.mapRect(7350, -3550, 850, 1275);
//Escaliers
spawn.mapRect(6600, -2100, 200, 75); //escaliers
spawn.mapRect(6750, -2100, 750, 250); //escaliers
spawn.mapRect(6950, -1850, 550, 200); //escaliers
spawn.mapRect(6750, -1400, 750, 150); //escaliers
spawn.mapRect(6550, -1625, 250, 375); //escaliers
spawn.mapRect(6350, -1800, 250, 550); //escaliers
spawn.mapRect(5600, -2275, 800, 1025); //escaliers
// BLOCS
if (isLevelReversed === false) { /// Normal spawn
spawn.bodyRect(1350, -1175, 225, 25);
spawn.bodyRect(1450, -1200, 25, 25);
} else { /// Reversed spawn
spawn.bodyRect(700, -1175, 225, 25);
spawn.bodyRect(800, -1200, 25, 25);
}
spawn.bodyRect(1100, -1375, 225, 225);
spawn.bodyRect(1775, -925, 75, 25);
spawn.bodyRect(2225, -950, 75, 50);
spawn.bodyRect(2000, -1000, 50, 100);
spawn.bodyRect(3100, -1175, 50, 25);
spawn.bodyRect(2200, -375, 50, 50);
spawn.bodyRect(2200, -425, 50, 50);
spawn.bodyRect(2200, -475, 50, 50);
spawn.bodyRect(2200, -525, 50, 50);
spawn.bodyRect(1050, -400, 50, 25);
spawn.mapRect(2200, -650, 50, 125);
spawn.mapRect(2200, -325, 50, 150);
spawn.mapRect(2875, -225, 250, 50);
spawn.mapRect(2050, -1225, 75, 100); //Plateforme over acid
// MOBS
if (isLevelReversed === false) { ///Normal spawn
if (simulation.difficulty > 1) {
if (Math.random() < 0.2) {
spawn.tetherBoss(7000, -3300, { x: 7300, y: -3300 }) // tether ball
} else {
spawn.randomLevelBoss(6100, -3600, ["shooterBoss", "launcherBoss", "laserTargetingBoss", "spiderBoss", "laserBoss", "pulsarBoss"]);
}
}
} else { /// Reversed spawn
if (simulation.difficulty > 1) {
if (Math.random() < 0.2) {
spawn.tetherBoss(2300, -1300, { x: 2300, y: -1750 }) // tether ball
} else {
spawn.randomLevelBoss(2300, -1400, ["shooterBoss", "launcherBoss", "laserTargetingBoss", "spiderBoss", "laserBoss", "dragonFlyBoss", "pulsarBoss"]);
}
}
}
spawn.randomSmallMob(100, -1000, 1);
spawn.randomSmallMob(1340, -675, 1);
spawn.randomSmallMob(7000, -3750, 1);
spawn.randomSmallMob(6050, -3200, 1);
spawn.randomMob(1970 + 10 * Math.random(), -1150 + 20 * Math.random(), 1);
spawn.randomMob(3500, -525, 0.8);
spawn.randomMob(6700, -3700, 0.8);
spawn.randomMob(2600, -1300, 0.7);
spawn.randomMob(600, -1250, 0.7);
spawn.randomMob(2450, -250, 0.6);
spawn.randomMob(6200, -3200, 0.6);
spawn.randomMob(900, -700, 0.5);
spawn.randomMob(1960, -400, 0.5);
spawn.randomMob(5430, -3520, 0.5);
spawn.randomMob(400, -700, 0.5);
spawn.randomMob(6500, -4000, 0.4);
spawn.randomMob(3333, -400, 0.4);
spawn.randomMob(3050, -1220, 0.4);
spawn.randomMob(800, 1200, 0.3);
spawn.randomMob(7200, -4000, 0.3);
spawn.randomMob(250, -1550, 0.3);
spawn.randomGroup(900, -1450, 0.3);
spawn.randomGroup(2980, -400, 0.3);
spawn.randomGroup(5750, -3860, 0.4);
spawn.randomGroup(1130, 1300, 0.1);
powerUps.addResearchToLevel() //needs to run after mobs are spawned
powerUps.spawn(1900, -940, "heal");
powerUps.spawn(3000, -230, "heal");
powerUps.spawn(5450, -3675, "ammo");
// SECRET BOSS AREA //
//hidden house
spawn.mapRect(-850, -2000, 600, 1150); //Toit hidden house
spawn.mapRect(-2850, -2000, 2150, 4880); //Mur gauche hidden house
spawn.mapRect(-850, -458, 500, 3340); //Bloc sol hidden house
//
spawn.mapRect(-400, 2025, 3450, 850); //Sol secret boss area
spawn.mapRect(625, 1300, 225, 50); //Plateforme horizontale n°1
spawn.mapRect(850, 1775, 470, 50); //Plateforme horizontale n°2
spawn.mapRect(1000, 1625, 100, 150); //Plateforme vertiale n°1
spawn.mapRect(1400, 1275, 100, 100); //Plateforme carrée
spawn.mapRect(1700, 1675, 75, 450); //Plateforme verticale n°2
spawn.mapRect(2100, 1375, 450, 50); //Plateforme accroche boss
spawn.mapRect(2900, 900, 175, 325); //Débord de toit droite haut
spawn.mapRect(2900, 1675, 150, 350); //Muret en bas à droite
spawn.mapRect(2900, 1225, 75, 100); //Picot haut entrée salle trésor
spawn.mapRect(2900, 1575, 75, 100); //Picot bas entrée salle trésor
spawn.mapRect(2800, 1575, 100, 25); //Plongeoir sortie salle trésor
spawn.mapRect(3050, 1675, 400, 1200); //Sol sallle trésor
spawn.mapRect(3075, 1075, 375, 150); //Plafond salle trésor
spawn.mapRect(3300, 1075, 1500, 1800); //Mur droite salle trésor
// tether ball
spawn.tetherBoss(2330, 1850, { x: 2330, y: 1425 })
spawn.secondaryBossChance(2330, 1850)
powerUps.chooseRandomPowerUp(3100, 1630);
},
// detours() { //by Francois from discord
// simulation.makeTextLog(`<strong>detours</strong> by <span class='color-var'>Francois</span>`);
// level.setPosToSpawn(0, 0); //lower start
// level.exit.y = 150;
// spawn.mapRect(level.enter.x, 45, 100, 20);
// level.exit.x = 10625;
// spawn.mapRect(level.exit.x, level.exit.y + 20, 100, 20);
// level.defaultZoom = 1400;
// simulation.zoomTransition(level.defaultZoom)
// document.body.style.backgroundColor = "#d5d5d5";
// const BGColor = "rgba(0,0,0,0.1)";
// // level.fill.push({
// // x: -150,
// // y: -250,
// // width: 625,
// // height: 325,
// // color: BGColor
// // });
// // level.fill.push({
// // x: 475,
// // y: -520,
// // width: 5375,
// // height: 875,
// // color: BGColor
// // });
// // level.fill.push({
// // x: 5850,
// // y: -1275,
// // width: 2800,
// // height: 2475,
// // color: BGColor
// // });
// // level.fill.push({
// // x: 8650,
// // y: -500,
// // width: 1600,
// // height: 750,
// // color: BGColor
// // });
// // level.fill.push({
// // x: 10250,
// // y: -700,
// // width: 900,
// // height: 950,
// // color: BGColor
// // });
// const balance = level.spinner(5500, -412.5, 25, 660) //entrance
// const rotor = level.rotor(7000, 580, -0.001);
// const doorSortieSalle = level.door(8590, -520, 20, 800, 750)
// // let buttonSortieSalle
// // let portalEnBas
// let portalEnHaut
// // let door3isClosing = false;
// function drawOnTheMapMapRect(x, y, dx, dy) {
// spawn.mapRect(x, y, dx, dy);
// len = map.length - 1
// map[len].collisionFilter.category = cat.map;
// map[len].collisionFilter.mask = cat.player | cat.map | cat.body | cat.bullet | cat.powerUp | cat.mob | cat.mobBullet;
// Matter.Body.setStatic(map[len], true); //make static
// Composite.add(engine.world, map[len]); //add to world
// simulation.draw.setPaths() //update map graphics
// }
// function drawOnTheMapBodyRect(x, y, dx, dy) {
// spawn.bodyRect(x, y, dx, dy);
// len = body.length - 1
// body[len].collisionFilter.category = cat.body;
// body[len].collisionFilter.mask = cat.player | cat.map | cat.body | cat.bullet | cat.mob | cat.mobBullet
// Composite.add(engine.world, body[len]); //add to world
// body[len].classType = "body"
// }
// function spawnCouloirEnHaut() {
// // level.fill.push({
// // x: 2575,
// // y: -1150,
// // width: 2550,
// // height: 630,
// // color: BGColor
// // });
// // level.fill.push({
// // x: 1900,
// // y: -2300,
// // width: 1650,
// // height: 1150,
// // color: BGColor
// // });
// // level.fill.push({
// // x: 3550,
// // y: -1625,
// // width: 1650,
// // height: 475,
// // color: BGColor
// // });
// // level.fill.push({
// // x: 1800,
// // y: -1120,
// // width: 775,
// // height: 600,
// // color: BGColor
// // });
// drawOnTheMapMapRect(3800, -270, 75, 75);
// drawOnTheMapMapRect(3900, -895, 500, 75);
// drawOnTheMapMapRect(3900, -1195, 75, 375);
// drawOnTheMapMapRect(3525, -1195, 450, 75);
// drawOnTheMapMapRect(3525, -1995, 50, 1575);
// drawOnTheMapMapRect(3325, -1995, 50, 1575);
// drawOnTheMapMapRect(3525, -1670, 1675, 75);
// drawOnTheMapMapRect(5100, -1670, 100, 1250);
// drawOnTheMapMapRect(1800, -1195, 1575, 75);
// drawOnTheMapMapRect(1800, -1520, 375, 400);
// drawOnTheMapMapRect(1800, -2370, 100, 1250);
// drawOnTheMapMapRect(2375, -1845, 375, 250);
// drawOnTheMapMapRect(2700, -1745, 650, 75);
// drawOnTheMapMapRect(1800, -2370, 1775, 100);
// drawOnTheMapMapRect(3525, -2370, 50, 775);
// drawOnTheMapMapRect(4650, -1220, 550, 75);
// drawOnTheMapBodyRect(3225, -1845, 100, 100);
// drawOnTheMapBodyRect(3575, 1255, 125, 25);
// drawOnTheMapBodyRect(2450, 2255, 25, 25);
// drawOnTheMapBodyRect(3975, -945, 175, 50);
// drawOnTheMapBodyRect(4825, -1295, 50, 75);
// drawOnTheMapBodyRect(4850, -720, 250, 200);
// drawOnTheMapBodyRect(4050, -970, 25, 25);
// drawOnTheMapBodyRect(3075, -1245, 50, 50);
// portalEnHaut = level.portal({
// x: 3650,
// y: -1470
// }, Math.PI / 2, {
// x: 3250,
// y: -1473
// }, Math.PI / 2)
// spawn.randomSmallMob(2500, -2070 + Math.random(), 1);
// spawn.randomSmallMob(5000, -1370, 1);
// spawn.randomMob(5000, -645, 0.9);
// spawn.randomMob(4050, -970, 0.9);
// spawn.randomSmallMob(2800, -1620, 0.7);
// spawn.randomMob(2400, -1370, 0.5);
// spawn.randomMob(3725, -1320, 0.3);
// spawn.randomGroup(2115, -2020, 0.1)
// powerUps.spawn(5000, -1275, "heal");
// levelCustom2();
// }
// //////////////////////////////////////////
// level.custom = () => {
// level.exit.drawAndCheck();
// rotor.rotate();
// // rotor2.rotate()
// level.enter.draw();
// };
// level.customTopLayer = () => {
// doorSortieSalle.draw();
// ctx.fillStyle = "#233"
// ctx.beginPath();
// ctx.arc(balance.pointA.x, balance.pointA.y, 9, 0, 2 * Math.PI);
// ctx.fill();
// };
// ////////////////////////////////////////
// function levelCustom2() {
// level.custom = () => {
// portalEnHaut[2].query();
// portalEnHaut[3].query();
// rotor.rotate();
// doorSortieSalle.openClose();
// level.exit.drawAndCheck();
// level.enter.draw();
// };
// // //////////////////////////////////////
// level.customTopLayer = () => {
// doorSortieSalle.draw();
// portalEnHaut[0].draw();
// portalEnHaut[1].draw();
// portalEnHaut[2].draw();
// portalEnHaut[3].draw();
// ctx.fillStyle = "#233"
// ctx.beginPath();
// ctx.arc(balance.pointA.x, balance.pointA.y, 9, 0, 2 * Math.PI);
// ctx.fill();
// };
// }
// //spawn box
// spawn.mapRect(-200, -295, 75, 425);
// spawn.mapRect(-200, 55, 700, 75);
// spawn.mapRect(-200, -295, 700, 75);
// spawn.bodyRect(470, -220, 25, 275); //porte spawn box
// //couloir
// spawn.mapRect(450, -520, 50, 300); //muret gauche haut
// spawn.mapRect(450, 55, 50, 300); //muret gauche bas
// spawn.mapRect(1700, -520, 50, 325); //muret 2 haut
// spawn.mapRect(1700, 55, 50, 300); //muret 2 bas
// spawn.mapRect(4375, 55, 50, 300);
// spawn.mapRect(4575, 55, 50, 300);
// spawn.bodyRect(4625, 155, 75, 100);
// spawn.bodyRect(4725, 230, 50, 25);
// if (Math.random() > 0.5) {
// powerUps.chooseRandomPowerUp(4500, 200);
// } else {
// powerUps.chooseRandomPowerUp(8350, -630);
// }
// //blocs
// spawn.bodyRect(7475, 1055, 50, 75);
// spawn.bodyRect(7775, 1105, 25, 25);
// spawn.bodyRect(6925, 1105, 125, 25);
// spawn.bodyRect(6375, 380, 50, 50);
// spawn.bodyRect(6425, -220, 125, 150);
// spawn.bodyRect(6475, -245, 125, 25);
// spawn.bodyRect(7675, -245, 100, 50);
// spawn.bodyRect(7075, -520, 50, 100);
// spawn.bodyRect(8400, -595, 100, 75);
// spawn.bodyRect(1700, 5, 50, 50);
// spawn.bodyRect(1700, -45, 50, 50);
// spawn.bodyRect(1700, -95, 50, 50);
// spawn.bodyRect(1700, -145, 50, 50);
// spawn.bodyRect(1700, -195, 50, 50);
// spawn.mapRect(450, -520, 1600, 100); //plafond 1
// spawn.mapRect(450, 255, 1600, 100); //sol 1
// spawn.mapRect(2250, -45, 1450, 75); //entresol
// spawn.mapRect(3900, -520, 2000, 100); //plafond 2
// spawn.mapRect(3900, 255, 2000, 100); //sol 2
// //grande salle
// spawn.bodyRect(5900, 830, 325, 300); //bloc en bas à gauche
// spawn.mapRect(5775, -1295, 2900, 100);
// spawn.mapRect(5775, 1130, 2900, 100); //plancher + sol grande salle
// spawn.mapRect(5925, -70, 650, 50); //plateforme middle entrée
// spawn.mapRect(7575, -520, 1100, 100); //sol salle en haut à droite
// spawn.mapRect(6800, -420, 450, 50); //petite plateforme transition vers salle en haut
// spawn.mapRect(7750, -1295, 75, 575); //mur gauche salle en haut à droite
// spawn.mapRect(6100, 430, 375, 50); //plateforme en bas, gauche rotor
// spawn.mapRect(7450, -195, 1225, 75); //longue plateforme
// //murs grande salle
// spawn.mapRect(5775, -1295, 125, 875);
// spawn.mapRect(5775, 255, 125, 975);
// spawn.mapRect(8550, -1295, 125, 875);
// spawn.mapRect(8550, 180, 125, 1050);
// //couloir 2
// spawn.mapRect(8875, -520, 1425, 325);
// spawn.mapRect(8550, -520, 1750, 100);
// spawn.mapRect(8550, 180, 2625, 100);
// spawn.mapRect(10175, -745, 125, 325);
// spawn.mapRect(10175, -745, 1000, 125);
// spawn.mapRect(11050, -745, 125, 1025);
// spawn.mapRect(8875, 80, 1425, 200);
// //MOBS
// spawn.randomSmallMob(900, -70, 1);
// spawn.randomMob(4300, 95, 1);
// spawn.randomSmallMob(6250, 630, 1);
// spawn.randomMob(6255, -835, 0.9);
// spawn.randomMob(8200, -900, 0.7);
// spawn.randomMob(5700, -270, 0.7);
// spawn.randomMob(8275, -320, 0.7);
// spawn.randomMob(2700, -270, 0.7);
// spawn.randomMob(7575, 950, 0.5);
// spawn.randomMob(7000, -695, 0.4);
// spawn.randomMob(1850, -345, 0.3);
// spawn.randomMob(3600, -270, 0.3);
// spawn.randomMob(1500, -270, 0.2);
// spawn.randomMob(1250, 55, 0.2);
// spawn.randomMob(8800, -45, 0.2);
// spawn.randomGroup(8025, -845, 0.2);
// if (simulation.difficulty > 2) {
// // if (Math.random() < 0.2) {
// // // tether ball
// // spawn.tetherBoss(8000, 630, { x: 8550, y: 680 })
// // let me = mob[mob.length - 1];
// // me.onDeath = function() { //please don't edit the onDeath function this causes serious bugs
// // this.removeCons(); //remove constraint
// // spawnCouloirEnHaut()
// // doorSortieSalle.isClosing = false;
// // };
// // if (simulation.difficulty > 4) spawn.nodeGroup(8000, 630, "spawns", 8, 20, 105);
// // } else {
// spawn.randomLevelBoss(8000, 630, ["shooterBoss", "launcherBoss", "laserTargetingBoss", "spiderBoss", "laserBoss", "bomberBoss", "orbitalBoss", "pulsarBoss"]);
// spawn.secondaryBossChance(8000, 630)
// //find level boss index
// let me
// for (let i = 0, len = mob.length; i < len; i++) {
// if (mob[i].isBoss) me = mob[i]
// }
// if (me) {
// me.onDeath = function() { //please don't edit the onDeath function this causes serious bugs
// spawnCouloirEnHaut()
// doorSortieSalle.isClosing = false;
// };
// } else {
// spawnCouloirEnHaut()
// doorSortieSalle.isClosing = false;
// }
// // }
// } else {
// spawn.randomLevelBoss(8000, 630, ["shooterBoss"]);
// let me
// for (let i = 0, len = mob.length; i < len; i++) {
// if (mob[i].isBoss) me = mob[i]
// }
// if (me) {
// me.onDeath = function() { //please don't edit the onDeath function this causes serious bugs
// spawnCouloirEnHaut()
// doorSortieSalle.isClosing = false;
// };
// } else {
// spawnCouloirEnHaut()
// doorSortieSalle.isClosing = false;
// }
// }
// },
house() { //by Francois from discord
simulation.makeTextLog(`<strong>house</strong> by <span class='color-var'>Francois</span>`);
const rotor = level.rotor(4251, -325, 120, 20, 200, 0, 0.01, 0, -0.0001);
const hazard = level.hazard(4350, -1000, 300, 110);
const doorBedroom = level.door(1152, -1150, 25, 250, 250);
const doorGrenier = level.door(1152, -1625, 25, 150, 160);
const buttonBedroom = level.button(1250, -850);
const voletLucarne1 = level.door(1401, -2150, 20, 26, 28);
const voletLucarne2 = level.door(1401, -2125, 20, 26, 53);
const voletLucarne3 = level.door(1401, -2100, 20, 26, 78);
const voletLucarne4 = level.door(1401, -2075, 20, 26, 103);
const voletLucarne5 = level.door(1401, -2050, 20, 26, 128);
const voletLucarne6 = level.door(1401, -2025, 20, 26, 153);
let hasAlreadyBeenActivated = false;
let grd
level.setPosToSpawn(0, -50); //normal spawn
spawn.mapRect(level.enter.x, level.enter.y + 20, 100, 20);
level.exit.x = 3100;
level.exit.y = -2480;
spawn.mapRect(level.exit.x, level.exit.y + 20, 100, 20);
level.defaultZoom = 1800
simulation.zoomTransition(level.defaultZoom)
document.body.style.backgroundColor = "rgb(170 170 170)"
level.custom = () => {
ctx.fillStyle = "rgb(221, 221, 221)";
ctx.fillRect(1175, -1425, 4000, 1200);
ctx.fillStyle = "rgb(170 170 170)";
ctx.fillRect(1650, -1300, 175, 150);
ctx.fillStyle = "rgb(77, 76, 76)";
ctx.fillRect(624, -1150, 28, 1075);
ctx.fillStyle = "#ababab";
ctx.fillRect(3420, -380, 285, 40);
ctx.fillStyle = "#474747";
ctx.fillRect(3555, -367.5, 15, 15);
ctx.fillRect(3418, -344, 288, 8);
ctx.fillRect(3555, -327.5, 15, 15);
ctx.fillRect(3418, -304, 288, 8);
ctx.fillRect(3555, -285, 15, 15);
ctx.fillStyle = "#ababab";
ctx.fillRect(3420, -340, 285, 40);
ctx.fillRect(3420, -300, 285, 45);
ctx.fillStyle = "rgba(141, 141, 141,1)";
ctx.fillRect(3800, -1275, 250, 425);
ctx.fillStyle = "#000";
ctx.fillRect(3800, -1275, 250, 3);
ctx.fillRect(4048, -1275, 3, 425);
ctx.fillRect(3800, -1275, 3, 425);
ctx.fillRect(3830, -1050, 35, 10);
ctx.fillStyle = "rgba(225, 242, 245,0.6)";
ctx.fillRect(4050, -1425, 1125, 600);
ctx.fillStyle = "#444";
ctx.fillRect(1736, -1300, 3, 150);
ctx.fillRect(1650, -1224, 175, 3);
ctx.fillStyle = "#5806ac";
ctx.fillRect(3375, -625, 375, 175);
ctx.fillStyle = "rgba(166, 166, 166,0.8)";
ctx.fillRect(4050, -1425, 1, 600);
ctx.fillRect(4090, -1425, 1, 600);
ctx.fillRect(4130, -1425, 1, 600);
ctx.fillRect(4170, -1425, 1, 600);
ctx.fillRect(4210, -1425, 1, 600);
ctx.fillRect(4250, -1425, 1, 600);
ctx.fillRect(4290, -1425, 1, 600);
ctx.fillRect(4330, -1425, 1, 600);
ctx.fillRect(4370, -1425, 1, 600);
ctx.fillRect(4410, -1425, 1, 600);
ctx.fillRect(4450, -1425, 1, 600);
ctx.fillRect(4490, -1425, 1, 600);
ctx.fillRect(4530, -1425, 1, 600);
ctx.fillRect(4570, -1425, 1, 600);
ctx.fillRect(4610, -1425, 1, 600);
ctx.fillRect(4650, -1425, 1, 600);
ctx.fillRect(4690, -1425, 1, 600);
ctx.fillRect(4730, -1425, 1, 600);
ctx.fillRect(4770, -1425, 1, 600);
ctx.fillRect(4810, -1425, 1, 600);
ctx.fillRect(4850, -1425, 1, 600);
ctx.fillRect(4890, -1425, 1, 600);
ctx.fillRect(4930, -1425, 1, 600);
ctx.fillRect(4970, -1425, 1, 600);
ctx.fillRect(5010, -1425, 1, 600);
ctx.fillRect(5050, -1425, 1, 600);
ctx.fillRect(5090, -1425, 1, 600);
ctx.fillRect(5130, -1425, 1, 600);
ctx.fillRect(4050, -1425, 1125, 2);
ctx.fillRect(4050, -1385, 1125, 2);
ctx.fillRect(4050, -1345, 1125, 2);
ctx.fillRect(4050, -1305, 1125, 2);
ctx.fillRect(4050, -1265, 1125, 2);
ctx.fillRect(4050, -1225, 1125, 2);
ctx.fillRect(4050, -1185, 1125, 2);
ctx.fillRect(4050, -1145, 1125, 2);
ctx.fillRect(4050, -1105, 1125, 2);
ctx.fillRect(4050, -1065, 1125, 2);
ctx.fillRect(4050, -1025, 1125, 2);
ctx.fillRect(4050, -985, 1125, 2);
ctx.fillRect(4050, -945, 1125, 2);
ctx.fillRect(4050, -905, 1125, 2);
ctx.fillRect(4050, -865, 1125, 2);
buttonBedroom.query();
buttonBedroom.draw();
if (buttonBedroom.isUp) {
if (hasAlreadyBeenActivated == false) {
doorBedroom.isClosing = true;
doorGrenier.isClosing = true;
voletLucarne1.isClosing = true;
voletLucarne2.isClosing = true;
voletLucarne3.isClosing = true;
voletLucarne4.isClosing = true;
voletLucarne5.isClosing = true;
voletLucarne6.isClosing = true;
}
} else {
doorBedroom.isClosing = false;
doorGrenier.isClosing = false;
voletLucarne1.isClosing = false;
voletLucarne2.isClosing = false;
voletLucarne3.isClosing = false;
voletLucarne4.isClosing = false;
voletLucarne5.isClosing = false;
voletLucarne6.isClosing = false;
if (hasAlreadyBeenActivated == false) {
hasAlreadyBeenActivated = true;
}
}
doorBedroom.openClose();
doorGrenier.openClose();
voletLucarne1.openClose();
voletLucarne2.openClose();
voletLucarne3.openClose();
voletLucarne4.openClose();
voletLucarne5.openClose();
voletLucarne6.openClose();
rotor.rotate();
///
grd = ctx.createRadialGradient(512.5, -1025, 5, 512.5, -1025, 100);
grd.addColorStop(0, "rgb(255, 199, 43)");
grd.addColorStop(1, "rgb(170 170 170)");
ctx.fillStyle = grd;
ctx.fillRect(450, -1025, 125, 100);
///
grd = ctx.createRadialGradient(762.5, -1025, 5, 762.5, -1025, 100);
grd.addColorStop(0, "rgb(255, 199, 43, 1)");
grd.addColorStop(1, "rgb(170 170 170)");
ctx.fillStyle = grd;
ctx.fillRect(700, -1025, 125, 100);
///
ctx.lineWidth = 7;
ctx.strokeStyle = "#444444"
ctx.strokeRect(1650, -1300, 175, 150);
chair.force.y += chair.mass * simulation.g;
chair2.force.y += chair2.mass * simulation.g;
person.force.y += person.mass * simulation.g;
level.exit.drawAndCheck();
level.enter.draw();
};
level.customTopLayer = () => {
ctx.fillStyle = "rgba(64,64,64,0.97)";
ctx.fillRect(2800, -400, 275, 175);
hazard.query();
doorBedroom.draw();
doorGrenier.draw();
voletLucarne1.draw();
voletLucarne2.draw();
voletLucarne3.draw();
voletLucarne4.draw();
voletLucarne5.draw();
voletLucarne6.draw();
};
//chairs
const part1 = Matter.Bodies.rectangle(4525, -255, 25, 200, {
density: 0.0005,
isNotHoldable: true,
});
const part2 = Matter.Bodies.rectangle(4562, -235, 100, 25, {
density: 0.0005,
isNotHoldable: true,
});
const part3 = Matter.Bodies.rectangle(4600, -202, 25, 91.5, {
density: 0.0005,
isNotHoldable: true,
});
const part4 = Matter.Bodies.rectangle(5100, -255, 25, 200, {
density: 0.0005,
isNotHoldable: true,
});
const part5 = Matter.Bodies.rectangle(5063, -235, 100, 25, {
density: 0.0005,
isNotHoldable: true,
});
const part6 = Matter.Bodies.rectangle(5025, -202, 25, 91.5, {
density: 0.0005,
isNotHoldable: true,
});
chair = Body.create({
parts: [part1, part2, part3],
});
chair2 = Body.create({
parts: [part4, part5, part6],
});
Composite.add(engine.world, [chair]);
Composite.add(engine.world, [chair2]);
composite[composite.length] = chair;
composite[composite.length] = chair2;
body[body.length] = part1;
body[body.length] = part2;
body[body.length] = part3;
body[body.length] = part4;
body[body.length] = part5;
body[body.length] = part6;
setTimeout(function () {
chair.collisionFilter.category = cat.body;
chair.collisionFilter.mask = cat.body | cat.player | cat.bullet | cat.mob | cat.mobBullet | cat.map
}, 1000);
setTimeout(function () {
chair2.collisionFilter.category = cat.body;
chair2.collisionFilter.mask = cat.body | cat.player | cat.bullet | cat.mob | cat.mobBullet | cat.map
}, 1000);
var head = Matter.Bodies.rectangle(300, -200 - 60, 34, 40, {
isNotHoldable: true,
});
var chest = Matter.Bodies.rectangle(300, -200, 55, 80, {
isNotHoldable: true,
});
var rightUpperArm = Matter.Bodies.rectangle(300 + 39, -200 - 15, 20, 40, {
isNotHoldable: true,
});
var rightLowerArm = Matter.Bodies.rectangle(300 + 39, -200 + 25, 20, 60, {
isNotHoldable: true,
});
var leftUpperArm = Matter.Bodies.rectangle(300 - 39, -200 - 15, 20, 40, {
isNotHoldable: true,
});
var leftLowerArm = Matter.Bodies.rectangle(300 - 39, -200 + 25, 20, 60, {
isNotHoldable: true,
});
var leftUpperLeg = Matter.Bodies.rectangle(300 - 20, -200 + 57, 20, 40, {
isNotHoldable: true,
});
var leftLowerLeg = Matter.Bodies.rectangle(300 - 20, -200 + 97, 20, 60, {
isNotHoldable: true,
});
var rightUpperLeg = Matter.Bodies.rectangle(300 + 20, -200 + 57, 20, 40, {
isNotHoldable: true,
});
var rightLowerLeg = Matter.Bodies.rectangle(300 + 20, -200 + 97, 20, 60, {
isNotHoldable: true,
});
//man
var person = Body.create({
parts: [chest, head, leftLowerArm, leftUpperArm,
rightLowerArm, rightUpperArm, leftLowerLeg,
rightLowerLeg, leftUpperLeg, rightUpperLeg
],
});
Composite.add(engine.world, [person]);
composite[composite.length] = person
body[body.length] = chest
body[body.length] = head
body[body.length] = part3
body[body.length] = leftLowerLeg
body[body.length] = leftUpperLeg
body[body.length] = leftUpperArm
body[body.length] = leftLowerArm
body[body.length] = rightLowerLeg
body[body.length] = rightUpperLeg
body[body.length] = rightLowerArm
body[body.length] = rightUpperArm
setTimeout(function () {
person.collisionFilter.category = cat.body;
person.collisionFilter.mask = cat.body | cat.player | cat.bullet | cat.mob | cat.mobBullet | cat.map
}, 1000);
//rez de chaussée
spawn.mapRect(-200, 0, 5400, 100); //ground
spawn.mapRect(1150, -255, 4050, 355); //additionnal ground
spawn.mapRect(800, -255, 400, 90); //1st step
spawn.mapRect(650, -170, 550, 90); //2nd step
spawn.mapRect(500, -85, 700, 90); //3rd step
spawn.mapRect(1150, -850, 50, 175); //porte entrée
spawn.bodyRect(1162.5, -675, 25, 420) //porte entrée
spawn.mapRect(1150, -850, 1500, 50); //plafond 1
spawn.mapRect(3025, -850, 2175, 50); //plafond 2
spawn.mapRect(5150, -850, 50, 650); //mur cuisine
//lave-vaisselle
spawn.mapRect(4225, -400, 25, 150);
spawn.mapRect(4225, -400, 175, 25);
spawn.mapRect(4375, -400, 25, 150);
spawn.bodyRect(4350, -350, 20, 40);
spawn.bodyRect(4325, -325, 20, 20);
spawn.bodyRect(4325, -275, 20, 20);
//escalier
spawn.mapRect(3025, -850, 50, 225);
spawn.mapRect(2925, -775, 150, 150);
spawn.mapRect(2800, -700, 275, 75);
spawn.mapRect(2575, -400, 175, 175);
spawn.mapRect(2475, -325, 175, 100);
spawn.mapRect(2675, -475, 400, 100);
spawn.mapRect(2675, -475, 150, 250);
//cuisine
spawn.mapRect(4025, -850, 50, 175); //porte cuisine
spawn.mapRect(4025, -375, 50, 125); //porte cuisine
map[map.length] = Bodies.polygon(4050, -675, 0, 15); //circle above door
spawn.bodyRect(4040, -650, 20, 260, 1, spawn.propsDoor); // door
body[body.length - 1].isNotHoldable = true;
//makes door swing
consBB[consBB.length] = Constraint.create({
bodyA: body[body.length - 1],
pointA: {
x: 0,
y: -130
},
bodyB: map[map.length - 1],
stiffness: 1
});
Composite.add(engine.world, consBB[consBB.length - 1]);
//table + chaises
spawn.mapRect(4025, -850, 50, 175);
spawn.mapRect(4650, -375, 325, 25);
spawn.mapRect(4700, -350, 25, 100);
spawn.mapRect(4900, -350, 25, 100);
spawn.bodyRect(4875, -400, 75, 25);
spawn.bodyRect(4700, -400, 75, 25);
//murs télé
spawn.mapRect(3400, -400, 20, 150);
spawn.mapRect(3705, -400, 20, 150);
spawn.mapRect(3400, -400, 325, 20);
//socle écran
spawn.mapRect(3500, -415, 125, 17);
spawn.mapRect(3550, -450, 25, 50);
// ???
spawn.bodyRect(3075, -375, 125, 125);
spawn.bodyRect(3075, -400, 50, 25);
spawn.bodyRect(3725, -325, 100, 75);
spawn.bodyRect(3375, -275, 25, 25);
// premier étage
spawn.mapRect(1150, -1450, 4050, 50);
spawn.mapRect(5150, -1450, 50, 650);
spawn.mapRect(1150, -1450, 50, 300);
spawn.mapRect(1150, -900, 50, 100);
spawn.mapVertex(1066, -730, "-200 60 0 -60 100 -60 100 60")
//chambre
spawn.mapRect(2350, -1450, 50, 175); //porte chambre
//lit
spawn.mapRect(1475, -1025, 25, 225); //pied de lit 1
spawn.mapRect(1850, -925, 25, 125); //pied de lit 2
spawn.mapRect(1475, -925, 400, 50); //sommier
spawn.bodyRect(1500, -950, 375, 25); //matelat
spawn.bodyRect(1500, -1000, 75, 50); //oreiller
//table
spawn.bodyRect(1950, -1000, 30, 150); //pied table
spawn.bodyRect(2250, -1000, 30, 150); //pied table
spawn.bodyRect(1920, -1025, 390, 25); //table
//salle de bain
spawn.mapRect(4025, -1450, 50, 175); //porte salle de bain
map[map.length] = Bodies.polygon(5050, -925, 0, 35.4);
spawn.mapRect(5015, -960, 125, 40);
spawn.mapRect(5050, -925, 90, 35.4);
spawn.mapVertex(5086.5, -875, "100 60 -30 60 20 0 100 0")
spawn.mapRect(5125, -1070, 15, 120)
spawn.bodyRect(5016, -965, 108, 15)
//baignoire
spawn.mapVertex(4316, -965, "30 100 0 100 -80 -50 30 -50") //bord 1
spawn.mapVertex(4675, -961.5, "30 100 0 100 0 -50 80 -50") //bord 2
spawn.mapVertex(4400, -860, "0 -20 -20 20 20 20 0 -20") //pied 1
spawn.mapVertex(4600, -860, "0 -20 -20 20 20 20 0 -20") //pied 2
spawn.mapRect(4325, -900, 350, 25); //fond baignoire
spawn.mapRect(4300, -1175, 25, 175);
spawn.mapRect(4300, -1175, 125, 25);
spawn.mapRect(4400, -1175, 25, 50); //pied pommeau de douche
spawn.mapVertex(4412.5, -1105, "-20 -20 -30 40 30 40 20 -20") //pommeau de douche
//grenier
spawn.mapRect(1150, -1475, 50, 50);
spawn.mapRect(1150, -1800, 50, 175);
spawn.mapRect(5150, -1800, 50, 400); //murs
spawn.mapVertex(1300, -1900, "-150 200 -200 200 50 0 100 0");
spawn.mapVertex(1800, -2300, "-150 200 -200 200 175 -100 225 -100");
spawn.mapRect(1390, -2180, 250, 30); //lucarne
spawn.mapVertex(5050, -1900, "150 200 200 200 -50 0 -100 0");
spawn.mapVertex(4550, -2300, "150 200 200 200 -175 -100 -225 -100");
spawn.mapRect(4710, -2175, 250, 25); //lucarne 2
spawn.mapRect(5150, -1450, 200, 50);
//obstacles
spawn.mapRect(3775, -1800, 99, 50);
spawn.mapRect(2425, -2150, 50, 425);
spawn.mapRect(2150, -1775, 325, 50);
spawn.mapRect(3825, -2150, 50, 750);
spawn.mapRect(3826, -2150, 149, 50);
spawn.mapRect(4125, -2150, 149, 50);
spawn.mapRect(4225, -2150, 50, 450);
spawn.mapRect(4225, -1750, 250, 50);
level.chain(2495, -2130, 0, true, 10);
spawn.bodyRect(2950, -375, 120, 120) //bloc hidden zone
spawn.bodyRect(2350, -1850, 75, 75);
spawn.bodyRect(4275, -1900, 75, 100);
spawn.bodyRect(4825, -1650, 325, 200);
spawn.bodyRect(5025, -1725, 25, 25);
spawn.bodyRect(4900, -1700, 200, 75);
spawn.mapVertex(2950, -2096, "-75 -50 75 -50 75 0 0 100 -75 0")
/*cheminée + roof*/
spawn.mapRect(1963, -2450, 2425, 35);
spawn.mapRect(2925, -2900, 125, 480);
spawn.mapRect(2900, -2900, 175, 75);
spawn.mapRect(2900, -2975, 25, 100);
spawn.mapRect(3050, -2975, 25, 100);
spawn.mapRect(2875, -3000, 225, 25);
// lampadaire + jump
spawn.mapRect(1000, -1450, 200, 25);
spawn.mapRect(500, -1150, 275, 25);
spawn.mapRect(750, -1150, 25, 75);
spawn.mapRect(500, -1150, 25, 75);
spawn.mapRect(450, -1075, 125, 50);
spawn.mapRect(700, -1075, 125, 50);
spawn.mapRect(2985, -4600, 0.1, 1700)
//bodyRects ~= debris
spawn.bodyRect(1740, -475, 80, 220)
spawn.bodyRect(1840, -290, 38, 23)
spawn.bodyRect(1200 + 1475 * Math.random(), -350, 15 + 110 * Math.random(), 15 + 110 * Math.random());
spawn.bodyRect(1200 + 1475 * Math.random(), -350, 15 + 110 * Math.random(), 15 + 110 * Math.random());
spawn.bodyRect(3070 + 600 * Math.random(), -1100, 20 + 50 * Math.random(), 150 + 100 * Math.random())
spawn.bodyRect(3050 + 1000 * Math.random(), -920, 30 + 100 * Math.random(), 15 + 65 * Math.random());
spawn.bodyRect(1600 + 250 * Math.random(), -1540, 80, 220) //boss room
spawn.debris(3070, -900, 1000, 3); //16 debris per level
spawn.debris(1200, -350, 1475, 4); //16 debris per level
spawn.debris(1250, -1550, 3565, 9); //16 debris per level
powerUps.chooseRandomPowerUp(2860, -270);
// Mobs
spawn.randomSmallMob(1385, -600, 1);
spawn.randomSmallMob(5000, -680, 1);
spawn.randomSmallMob(4750, -925, 1);
spawn.randomSmallMob(2300, -1830, 1);
spawn.randomMob(3170, -720, 0.8);
spawn.randomMob(3700, -975, 0.8);
spawn.randomMob(2625, -1150, 0.7);
spawn.randomMob(4175, -750, 0.7);
spawn.randomMob(2100, -370, 0.7);
spawn.randomMob(2000, -1230, 0.7);
spawn.randomMob(4175, -1075, 0.6);
spawn.randomMob(3965, -1650, 0.6)
spawn.randomMob(4650, -1750, 0.6);
spawn.randomMob(830, -1170, 0.5);
spawn.randomGroup(3730, -1100, 0.5);
spawn.randomMob(2650, -2250, 0.3);
spawn.randomMob(1615, -2270, 0.3);
spawn.randomMob(1380, -1280, 0.25);
spawn.randomMob(2280, -650, 0.2);
spawn.randomGroup(2450, -2650, 0.2);
spawn.randomMob(3800, -580, 0.2);
spawn.randomMob(4630, -425, 0.1);
spawn.randomGroup(630, -1300, -0.1);
spawn.randomGroup(3450, -2880, -0.2)
if (simulation.difficulty > 3) {
spawn.secondaryBossChance(3380, -1775)
if (Math.random() < 0.16) {
spawn.tetherBoss(3380, -1775, { x: 3775, y: -1775 })
} else {
spawn.randomLevelBoss(3100, -1850, ["shooterBoss", "spiderBoss", "launcherBoss", "laserTargetingBoss", "dragonFlyBoss", "laserBoss"]);
}
}
},
perplex() { //by Oranger from discord
simulation.makeTextLog(`<strong>perplex</strong> by <span class='color-var'>Oranger</span>`);
document.body.style.backgroundColor = "#dcdcde";
level.setPosToSpawn(-600, 400);
spawn.mapRect(level.enter.x, level.enter.y + 20, 100, 20);
level.exit.x = 550;
level.exit.y = -2730;
spawn.mapRect(level.exit.x, level.exit.y + 20, 100, 20);
const portal = level.portal({ //main portals
x: -1000,
y: 50
}, -Math.PI / 2, { //up
x: 1000,
y: 50
}, -Math.PI / 2) //up
const portal2 = level.portal({ //portals in upper right corner
x: 1400,
y: -2200
}, -Math.PI / 2, { //up
x: 1700,
y: -1700
}, -Math.PI / 2) //up
// rotor(x, y, width, height, density = 0.001, angle = 0, frictionAir = 0.001, angularVelocity = 0, rotationForce = 0.0005) {
const rotor = level.rotor(-600, -1950, 800, 50, 0.001, 0, 0.01, 0, -0.001)
level.custom = () => {
portal[2].query(true)
portal[3].query(true)
portal2[2].query(true)
portal2[3].query(true)
rotor.rotate();
ctx.fillStyle = "#d4f4f4";
ctx.fillRect(375, -3000, 450, 300);
level.exit.drawAndCheck();
level.enter.draw();
};
level.customTopLayer = () => {
portal[0].draw();
portal[1].draw();
portal[2].draw();
portal[3].draw();
portal2[0].draw();
portal2[1].draw();
portal2[2].draw();
portal2[3].draw();
ctx.fillStyle = "rgba(0,0,0,0.03)";
ctx.fillRect(-875, -250, 1500, 700);
ctx.fillRect(-925, -505, 930, 255);
ctx.fillStyle = "rgba(0,0,0,0.1)";
ctx.fillRect(725, -1400, 200, 200);
ctx.fillRect(925, -2150, 150, 2175);
ctx.fillRect(925, -3400, 150, 850);
ctx.fillStyle = "rgba(0,0,0,0.03)";
ctx.fillRect(1800, -2600, 400, 400);
ctx.fillRect(2200, -2600, 400, 1250);
};
level.defaultZoom = 1700 // 4500 // 1400
simulation.zoomTransition(level.defaultZoom)
//section 1: before portals
spawn.mapRect(-925, 450, 1850, 250); //1-1 base
spawn.mapRect(-925, -300, 55, 755); //1 left wall
spawn.mapRect(-875, 50, 1100, 50); //1-1 ceiling
spawn.mapRect(620, -300, 305, 755); //1-1 and 1-2 right wall
spawn.bodyRect(200, 350, 230, 100);
spawn.bodyRect(300, 250, 150, 100);
spawn.mapRect(-875, -300, 580, 50); //1-2 ceiling on left
spawn.mapRect(0, -300, 625, 50); //1-2 ceiling on right
spawn.mapRect(0, -650, 150, 350); //1-3 right wall
spawn.mapRect(-925, -650, 975, 150); //1-3 ceiling
spawn.mapRect(-1280, 100, 205, 150); //1-4 floor
spawn.mapRect(-1280, 245, 360, 455); //bottom left corner
spawn.mapRect(-1600, -200, 200, 50); //1-4 platform 1
//section 2: lower central room (gone through main portals 1 time)
spawn.mapRect(920, 245, 160, 455); //below right portal
spawn.mapRect(1075, -300, 500, 1000); //2-1 right floor
spawn.bodyRect(100, -1000, 50, 350);
spawn.bodyRect(100, -1015, 250, 15);
spawn.mapRect(-925, -1600, 100, 1000); //2-2 left wall
spawn.mapRect(725, -2150, 200, 750); //2-2 right wall
spawn.mapRect(725, -1200, 200, 200); //2-2 right wall 2
spawn.mapRect(300, -1000, 625, 50); //2 central ledge
//shute
spawn.mapRect(1075, -2005, 550, 1055); //shute right wall
spawn.mapRect(875, -1000, 50, 300); //shute left 1
spawn.mapRect(860, -1030, 50, 300); //shute left 2
spawn.mapRect(850, -1100, 50, 300); //shute left 3
spawn.mapRect(830, -980, 50, 50); //shute left 4
spawn.mapRect(1075, -1000, 50, 300); //shute right 1
spawn.mapRect(1090, -1030, 50, 300); //shute right 2
spawn.mapRect(1100, -1100, 50, 300); //shute right 3
spawn.mapRect(1120, -980, 50, 50); //shute right 4
spawn.mapRect(1850, -650, 400, 50); //drop from 4-1
//section 3: upper left room and upper central room (gone through main portals 2 times)
//3-2 is just the upper part of 2-2
spawn.mapRect(-1775, -1000, 700, 300); //3-1 floor
spawn.mapRect(-1900, -2300, 175, 1600); //3-1 left wall
spawn.mapRect(-1375, -1300, 300, 50); //3-1 platform 1
spawn.mapRect(-1600, -1650, 300, 50); //3-1 platform 2
spawn.mapRect(-1775, -2300, 700, 300); //3-1 ceiling
spawn.mapRect(-830, -1600, 300, 50); //3-2 left ledge
spawn.mapRect(250, -2150, 675, 50); //3-2 right ledge
spawn.mapRect(-925, -2300, 100, 300); //3-2 left wall
spawn.mapRect(-600, -2700, 1525, 150); //3-2 ceiling
spawn.mapRect(1075, -2150, 250, 150); //next to upper portal
// level.fill.push({
// x: -1730,
// y: -2300,
// width: 870,
// height: 1600,
// color: "rgba(0,0,0,0.03)"
// });
//section 4: upper right portals
spawn.mapRect(1475, -2700, 150, 700); //4-1 left wall
spawn.mapRect(1775, -1650, 250, 150); //4-1 floor-ish
spawn.mapRect(1575, -1505, 450, 555); //below upper right portal
spawn.mapRect(1800, -2250, 400, 50); //4-1 platform 2
spawn.bodyRect(2200, -2250, 15, 300);
spawn.mapRect(2200, -1950, 400, 50); //4-1 platform 1
//spawn.bodyRect(2575, -2600, 25, 650);
spawn.mapRect(2600, -1650, 400, 50); //4-1 platform 0
spawn.mapRect(2200, -1350, 400, 50); //4-1 platform -1
spawn.bodyRect(2200, -1900, 15, 550);
spawn.bodyRect(2585, -1650, 15, 300);
spawn.mapRect(1800, -4200, 800, 1600); //4-2 right wall
spawn.mapRect(800, -4200, 1800, -500); //4-2 ceiling
spawn.mapRect(1075, -3400, 225, 850); //upper shute right wall
spawn.mapRect(800, -3400, 125, 850); //upper shute left wall
//section 5: after portals (gone through main portals 3 times)
spawn.mapRect(-700, -2700, 100, 450); //5-1 right wall
spawn.mapRect(-1450, -2700, 900, 50); //5-1 ceiling
spawn.mapRect(-925, -2300, 325, 50); //5-1 right floor
spawn.mapRect(-1900, -3000, 450, 50); //stair cover
spawn.bodyRect(-1150, -2950, 150, 250); //5-2 block
//top left corner stuff
spawn.mapRect(-1900, -2450, 250, 450); //
//exit room
spawn.mapRect(350, -3000, 50, 100); //exit room left wall
spawn.mapRect(350, -3000, 450, -1700); //exit room ceiling
spawn.bodyRect(350, -2900, 50, 50.5); //door
spawn.bodyRect(350, -2850, 50, 50.5); //door
spawn.bodyRect(350, -2800, 50, 50.5); //door
spawn.bodyRect(350, -2750, 50, 50.5); //door
spawn.debris(-400, 450, 400, 5); //16 debris per level
spawn.debris(-1650, -2300, 250, 4); //16 debris per level
spawn.debris(-750, -650, 750, 3); //16 debris per level
//mobs
spawn.randomMob(-650, -100, 0.7); //1-2 left
spawn.randomMob(100, -150, 0.3); //1-2 right
spawn.randomMob(-100, -400, 0); //1-3 right
//spawn.randomMob(-1500, -300, 0.3); //1-4 platform
spawn.randomMob(1450, -450, 0); //2-1 right
spawn.randomMob(1700, -800, 1); //2-1 off the edge. chance is 1 because some enemies just fall
spawn.randomGroup(-550, -900, -0.3); //2-2
spawn.randomMob(-1550, -1800, 0.7); //3-1 upper platform
//spawn.randomMob(-1225, -1400, 0.3); //3-1 lower platform
spawn.randomMob(450, -2350, 0.3); //3-2 right ledge
//spawn.randomMob(1150, -2250, 0); //3-2 far right
spawn.randomGroup(2400, -2300, -0.3); //4-1 floating
spawn.randomMob(2400, -1450, 0); //4-1 platform -1
spawn.randomMob(2800, -1800, 0.5); //4-1 platform 0
spawn.randomMob(-1700, -3200, 0.7); //5-2 left platform
spawn.randomMob(-550, -2800, 0.3); //5-2 middle
if (simulation.difficulty > 3) {
if (Math.random() < 0.5) {
spawn.randomLevelBoss(450, -1350, ["shooterBoss", "launcherBoss", "laserTargetingBoss", "streamBoss", "shieldingBoss", "pulsarBoss", "laserBoss"]);
} else {
spawn.randomLevelBoss(-300, -3200, ["shooterBoss", "launcherBoss", "laserTargetingBoss", "streamBoss", "shieldingBoss", "pulsarBoss", "laserBoss"]);
}
}
powerUps.addResearchToLevel() //needs to run after mobs are spawned
spawn.secondaryBossChance(7725, 2275)
},
coliseum() {
simulation.makeTextLog(`<strong>coliseum</strong> by <span class='color-var'>iNoobBoi</span>`);
level.custom = () => {
level.exit.drawAndCheck();
level.enter.draw();
};
level.customTopLayer = () => { };
level.defaultZoom = 1800
simulation.zoomTransition(level.defaultZoom)
document.body.style.backgroundColor = "#dcdcde";
//Level
level.setPosToSpawn(200, 50);
spawn.mapRect(level.enter.x, level.enter.y + 20, 100, 20);
level.exit.x = 8950;
level.exit.y = 170;
spawn.mapRect(level.exit.x, level.exit.y + 20, 100, 20);
//Map
spawn.mapRect(-100, -400, 100, 600);
spawn.mapRect(-100, 100, 700, 100);
spawn.mapRect(500, 100, 100, 1700);
spawn.mapRect(500, 1700, 4000, 100);
spawn.mapRect(4100, 600, 400, 100);
spawn.mapRect(4400, 600, 100, 1600);
spawn.mapRect(4400, 2100, 4300, 100);
spawn.mapRect(8600, 200, 100, 2000);
spawn.mapRect(8600, 200, 700, 100);
spawn.mapRect(9200, -300, 100, 600);
spawn.mapRect(8600, -300, 700, 100);
spawn.mapRect(8600, -700, 100, 500);
spawn.mapRect(4400, -700, 4300, 100);
spawn.mapRect(4400, -700, 100, 900);
spawn.mapRect(-100, -400, 4600, 100);
//Platforms
spawn.mapRect(1100, 400, 300, 100);
spawn.mapRect(500, 500, 300, 100);
spawn.mapRect(1050, 800, 300, 100);
spawn.mapRect(1770, 1050, 300, 100);
spawn.mapRect(1800, 500, 300, 100);
spawn.mapRect(2550, 900, 300, 100);
spawn.mapRect(2800, 1400, 300, 100);
spawn.mapRect(1250, 1350, 300, 100);
spawn.mapRect(4750, 850, 300, 100);
spawn.mapRect(3200, 1050, 300, 100);
spawn.mapRect(4700, 100, 300, 100);
spawn.mapRect(5350, 0, 300, 100);
spawn.mapRect(3800, 900, 300, 100);
spawn.mapRect(5100, 500, 300, 100);
spawn.mapRect(5900, -300, 300, 100);
spawn.mapRect(6500, -700, 300, 1300);
spawn.mapRect(7900, 0, 300, 100);
spawn.mapRect(8050, 800, 300, 100);
spawn.mapRect(7800, 1900, 300, 100);
spawn.mapRect(8300, 450, 300, 100);
spawn.mapRect(8400, 1200, 300, 100);
spawn.mapRect(7570, 1100, 300, 100);
spawn.mapRect(6700, 1850, 300, 100);
spawn.mapRect(8000, 1500, 300, 100);
spawn.mapRect(7120, -100, 300, 100);
spawn.mapRect(7000, 1500, 300, 100);
spawn.mapRect(6500, 1000, 300, 1200);
spawn.mapRect(5800, 1100, 300, 100);
spawn.mapRect(5900, 1700, 300, 100);
spawn.mapRect(5300, 1400, 300, 100);
spawn.mapRect(5200, 1100, 300, 100);
spawn.mapRect(6700, 1100, 300, 100);
spawn.mapRect(4800, 1650, 300, 100);
//Room 1 Spawning
spawn.randomMob(1000, 700, 0.7);
spawn.randomGroup(1100, 700, 0.5);
spawn.randomMob(1900, 400, 0.7);
spawn.randomGroup(2000, 400, 0.4);
spawn.randomGroup(1800, 1100, 0.4);
spawn.randomGroup(2700, 700, 0.5);
spawn.randomMob(2900, 1200, 0.7);
spawn.randomSmallMob(3200, 300, 0.9);
spawn.randomSmallMob(3700, 800, 0.9);
spawn.randomMob(1100, 700, 0.6);
spawn.randomGroup(1200, 700, 0.5);
spawn.randomMob(2000, 400, 0.8);
spawn.randomGroup(2100, 400, 0.5);
spawn.randomGroup(1900, 1100, 0.5);
spawn.randomGroup(2800, 700, 0.5);
spawn.randomMob(3000, 1200, 0.7);
spawn.randomSmallMob(3200, 300, 0.9);
spawn.randomSmallMob(3700, 800, 0.9);
spawn.randomMob(800, 1500, 0.9);
spawn.randomMob(1500, 1500, 0.7);
spawn.randomMob(2200, 1500, 0.6);
spawn.randomMob(2500, 1500, 0.7);
spawn.randomMob(2800, 1500, 0.7);
spawn.randomMob(3300, 1500, 0.6);
//Room 2 Spawning
spawn.randomGroup(4700, 2000, 0.9);
spawn.randomMob(5000, 2000, 0.5);
spawn.randomSmallMob(5700, 1500, 0.9);
spawn.randomMob(8500, 2000, 0.6);
spawn.randomGroup(8000, 1300, 0.9);
spawn.randomMob(8300, -300, 0.4);
spawn.randomSmallMob(7600, -200, 0.9);
spawn.randomMob(5200, -300, 0.5);
spawn.randomSmallMob(4700, -200, 0.5);
spawn.randomGroup(4700, 2000, 0.8);
spawn.randomMob(5000, 2000, 0.5);
spawn.randomSmallMob(5700, 1500, 0.9);
spawn.randomGroup(8500, 2000, 0.3);
spawn.randomSmallMob(8000, 1300, 0.4);
spawn.randomMob(8300, -300, 0.3);
spawn.randomGroup(7600, -200, 0.5);
spawn.randomMob(5200, -300, 0.3);
spawn.randomGroup(4700, -200, 0.4);
spawn.randomGroup(8650, -200, 0.9); //end guards
spawn.randomMob(8650, -200, 0.9); //end guards
//Boss Spawning
if (simulation.difficulty > 3) {
spawn.randomLevelBoss(6000, 700, ["pulsarBoss", "laserTargetingBoss", "powerUpBoss", "bomberBoss", "historyBoss", "orbitalBoss"]);
// if (simulation.difficulty > 10) spawn.shieldingBoss(7200, 500);
// if (simulation.difficulty > 20) spawn.randomLevelBoss(2000, 300, ["historyBoss", "shooterBoss"]);
}
//Blocks
spawn.bodyRect(550, -300, 50, 400); //spawn door
spawn.bodyRect(4400, 200, 100, 400); //boss door
spawn.bodyRect(6600, 600, 50, 400); //boss 2 door
spawn.debris(400, 800, 400, 2);
spawn.debris(3800, 1600, 1200, 6);
spawn.debris(7500, 2000, 800, 4);
spawn.debris(5500, 2000, 800, 4);
//Powerups
powerUps.spawnStartingPowerUps(1250, 1500);
// powerUps.spawnStartingPowerUps(1500, 1500);
powerUps.spawn(8650, -200, "ammo");
// powerUps.spawn(8650, -200, "ammo");
// powerUps.spawn(8650, -200, "ammo");
// powerUps.spawn(8650, -200, "ammo");
powerUps.spawn(200, 50, "heal");
// powerUps.spawn(200, 50, "ammo");
// powerUps.spawn(200, 50, "ammo");
// powerUps.spawn(200, 50, "ammo");
powerUps.addResearchToLevel() //needs to run after mobs are spawned
spawn.secondaryBossChance(6600, 600)
},
crossfire() {
simulation.makeTextLog(`<strong>crossfire</strong> by <span class='color-var'>iNoobBoi</span>`);
//*1.5
//Level Setup
const slimePitOne = level.hazard(0, 850, 3800, 120);
const slimePitTwo = level.hazard(4600, 430, 2000, 120);
const slimePitThree = level.hazard(6500, 200, 1000, 170);
level.custom = () => {
level.exit.drawAndCheck();
level.enter.draw();
};
level.customTopLayer = () => {
slimePitOne.query();
slimePitTwo.query();
slimePitThree.query();
};
level.setPosToSpawn(-500, 550); //normal spawn
spawn.mapRect(level.enter.x, level.enter.y + 20, 100, 20);
level.exit.x = 10300;
level.exit.y = -830;
spawn.mapRect(level.exit.x, level.exit.y + 20, 100, 20);
level.defaultZoom = 3000
simulation.zoomTransition(level.defaultZoom)
document.body.style.backgroundColor = "#dcdcde";
//Map Elements
spawn.mapRect(-800, -600, 800, 200);
spawn.mapRect(-200, -600, 200, 800);
spawn.mapRect(-800, -600, 200, 800);
spawn.mapRect(-1000, 0, 1000, 200);
spawn.mapRect(-1000, 0, 200, 800);
spawn.mapRect(-1000, 600, 1400, 200);
spawn.mapRect(0, 600, 200, 400);
spawn.mapRect(0, 950, 4000, 100);
spawn.mapRect(800, 800, 600, 200);
spawn.mapRect(1700, 700, 500, 300);
spawn.mapRect(2500, 600, 400, 400);
spawn.mapRect(3200, 600, 1200, 200);
spawn.mapRect(3800, 600, 200, 800); //
spawn.mapRect(3800, 1200, 800, 200);
spawn.mapRect(4400, 400, 300, 1000);
spawn.mapRect(4400, 500, 2000, 100);
spawn.mapRect(6500, 300, 1000, 100);
spawn.mapRect(5000, 200, 700, 400);
spawn.mapRect(6000, 0, 650, 600);
spawn.mapRect(6900, -300, 700, 100);
spawn.mapRect(7400, -600, 200, 1100);
spawn.mapRect(7400, 300, 2600, 200);
spawn.mapRect(9800, -800, 200, 1300);
spawn.mapRect(9800, -800, 1000, 200);
spawn.mapRect(10600, -1400, 200, 800);
spawn.mapRect(9800, -1400, 200, 400);
spawn.mapRect(7400, -1400, 3400, 200);
spawn.mapRect(7400, -1600, 200, 800);
spawn.mapRect(5400, -1600, 2200, 200);
spawn.mapRect(6000, -1600, 200, 800);
spawn.mapRect(5400, -1600, 200, 800);
spawn.mapRect(4800, -1000, 1400, 200);
spawn.mapRect(4800, -1000, 200, 600);
spawn.mapRect(3800, -600, 1200, 200);
spawn.mapRect(3200, -800, 800, 200);
spawn.mapRect(3200, -800, 200, 800);
spawn.mapRect(3800, -800, 200, 800);
spawn.mapRect(-200, -200, 4200, 200);
//Boss Room Platforms
spawn.mapRect(7700, 100, 300, 40);
spawn.mapRect(8600, 0, 300, 40);
spawn.mapRect(9200, 100, 300, 40);
spawn.mapRect(9400, -200, 300, 40);
spawn.mapRect(8000, -200, 300, 40);
spawn.mapRect(8500, -400, 300, 40);
spawn.mapRect(9000, -600, 300, 40);
spawn.mapRect(9400, -800, 300, 40);
spawn.mapRect(8600, -1000, 300, 40);
spawn.mapRect(7900, -800, 300, 40);
//Mob Spawning
spawn.randomMob(200, 400, 0.7);
// spawn.randomMob(1200, 400, 0.7);
spawn.randomMob(2000, 400, 0.7);
// spawn.randomMob(3000, 400, 0.7);
spawn.randomMob(5000, 0, 0.7);
spawn.randomMob(5600, 0, 0.7);
spawn.randomMob(6200, -200, 0.7);
// spawn.randomMob(6600, -200, 0.7);
spawn.randomMob(7200, -800, 0.7);
spawn.randomSmallMob(800, 400, 0.9);
spawn.randomSmallMob(1800, 400, 0.9);
// spawn.randomSmallMob(2600, 400, 0.9);
spawn.randomSmallMob(5200, 0, 0.9);
// spawn.randomSmallMob(5400, 0, 0.9);
spawn.randomSmallMob(6400, -200, 0.9);
spawn.randomGroup(3800, 400, 0.5);
spawn.randomGroup(4200, 400, 0.5);
// spawn.randomGroup(4400, 200, 0.5);
spawn.randomGroup(7000, -800, 0.5);
// spawn.randomGroup(7700, 300, 0.5);
spawn.randomGroup(9800, 300, 0.5);
// spawn.randomGroup(7700, -1100, 0.5);
spawn.randomGroup(9800, -1100, 0.5);
if (simulation.difficulty > 3) spawn.randomLevelBoss(8600, -600, ["powerUpBoss", "bomberBoss", "dragonFlyBoss", "spiderBoss", "historyBoss"])
spawn.secondaryBossChance(7900, -400)
//Boss Spawning
if (simulation.difficulty > 10) {
spawn.pulsarBoss(3600, -400);
powerUps.chooseRandomPowerUp(4006, 400);
powerUps.chooseRandomPowerUp(4407, 400);
powerUps.spawnStartingPowerUps(4400, 400);
if (simulation.difficulty > 30) {
powerUps.chooseRandomPowerUp(4002, 400);
powerUps.chooseRandomPowerUp(4004, 400);
spawn.pulsarBoss(4200, 1000);
if (simulation.difficulty > 60) {
powerUps.chooseRandomPowerUp(4409, 400);
spawn.pulsarBoss(5800, -1200);
if (simulation.difficulty > 80) {
spawn.pulsarBoss(-400, -200);
if (simulation.difficulty > 100) {
spawn.pulsarBoss(3600, -400);
if (simulation.difficulty > 120) {
spawn.pulsarBoss(-400, -200);
}
}
}
}
}
}
//Powerup Spawning
powerUps.spawnStartingPowerUps(4000, 400);
powerUps.addResearchToLevel(); //needs to run after mobs are spawned
//Block Spawning
// spawn.bodyRect(-100, 200, 100, 400); //spawn door
spawn.bodyRect(7450, -800, 25, 200); //boss room door
spawn.bodyRect(9850, -1000, 25, 200); //end door
spawn.mapRect(-200, 350, 200, 450);
// spawn.mapRect(3875, -75, 50, 575);
spawn.mapRect(3800, -75, 200, 525);
spawn.mapRect(3875, 590, 50, 150);
spawn.mapRect(3875, 350, 50, 140);
const debrisCount = 3
spawn.debris(1050, 700, 400, debrisCount);
spawn.debris(1900, 600, 400, debrisCount);
spawn.debris(2700, 500, 400, debrisCount);
// spawn.debris(3500, 450, 400, debrisCount);
spawn.debris(4150, 500, 400, debrisCount);
spawn.debris(5300, 0, 400, debrisCount);
spawn.debris(6300, -100, 400, debrisCount);
spawn.debris(7200, -500, 400, debrisCount);
spawn.debris(8000, -600, 400, debrisCount);
spawn.debris(8700, -700, 400, debrisCount);
spawn.debris(9300, -900, 400, debrisCount);
},
vats() { // Made by Dablux#6610 on Discord
simulation.makeTextLog(`<strong>vats</strong> by <span class='color-var'>Dablux</span>`);
simulation.zoomScale = 1500;
level.setPosToSpawn(4400, -1060)
spawn.mapRect(level.enter.x, level.enter.y + 30, 100, 20)
level.exit.x = 3900;
level.exit.y = 1060;
spawn.mapRect(level.exit.x, level.exit.y + 30, 100, 20)
document.body.style.backgroundColor = "#dcdcde";
var nextBlockSpawn = simulation.cycle + Math.floor(Math.random() * 60 + 30)
const door = level.door(475, 900, 50, 200, 201)
const exitDoor = level.door(3375, 900, 50, 200, 201)
const deliveryButton = level.button(3500, -410)
const buttonGreen = level.button(-1600, 1090)
const buttonYellow = level.button(-1600, -1160)
const buttonRed = level.button(5874, -2410)
let g = false;
let y = false;
let r = false;
const deliverySlime = level.hazard(3700, -940, 100, 480)
const deliverySlime2 = level.hazard(3700, -461, 100, 1141)
const slimePit = level.hazard(700, 1200, 2500, 1300, 0.004)
const topSlime = level.hazard(800, -460, 2900, 90, 0.004)
// const rotor = level.rotor(0, -725, 0.001)
const rotor = level.rotor(-400, -725, 800, 50, 0.001, 0, 0.01, 0, 0.001)
const portal = level.portal({
x: -135,
y: 800
}, Math.PI / 2, {
x: 570,
y: -395
}, -Math.PI / 2)
const portal2 = level.portal({
x: -1800,
y: 1900
}, Math.PI, {
x: 200,
y: 1105
}, -Math.PI / 2)
const drip1 = level.drip(1875, -660, -400, 70)
const drip2 = level.drip(3525, -940, -400, 150)
const drip3 = level.drip(1975, 100, 1200, 100)
door.isClosing = true;
exitDoor.isClosing = true;
// UPPER AREA //
spawn.mapRect(4500, -2400, 1700, 2050)
spawn.mapRect(3800, -1000, 700, 650)
spawn.mapRect(4000, -1310, 50, 60)
spawn.mapRect(4450, -1310, 50, 60)
spawn.mapRect(4000, -1320, 500, 20)
level.chain(4025, -1225, 0.5 * Math.PI, false, 5, 25)
spawn.mapRect(3650, -460, 50, 90)
spawn.mapRect(3525, -1000, 325, 20)
spawn.mapRect(3650, -1000, 50, 440)
spawn.mapRect(3300, -1000, 50, 450)
spawn.mapRect(3325, -725, 150, 25)
spawn.mapRect(3500, -980, 175, 35)
spawn.mapRect(3325, -980, 50, 35)
spawn.mapRect(-1800, -1250, 50, 120)
spawn.mapRect(6150, -2500, 50, 120)
spawn.bodyRect(3350, -1000, 175, 20, 1, spawn.propsIsNotHoldable) // Cover
Matter.Body.setMass(body[body.length - 1], 0.7) // Make cover easier to remove
spawn.mapRect(750, -475, 50, 75);
for (let i = 1; i < 5; i++) {
spawn.mapRect(800 + (i * 100) + (500 * (i - 1)), -460 + (i * -120) + (20 * (i - 1)), 500, 20)
}
// ARENA //
spawn.mapRect(400, -400, 2950, 500)
spawn.mapRect(-1800, -1150, 1800, 1950)
spawn.mapRect(-1800, 1100, 780, 1800)
spawn.mapRect(-300, 1100, 1000, 1800)
//spawn.mapRect(-1800, -1450, 100, 2000)
spawn.blockDoor(-1800, 1070)
level.chain(-1000, 1120, 0, true, 18, 20)
spawn.mapRect(700, 2500, 2500, 900)
spawn.mapRect(400, 100, 200, 599)
spawn.mapRect(400, 650, 75, 250)
spawn.mapRect(525, 650, 75, 250)
spawn.mapRect(3300, 650, 75, 250)
spawn.mapRect(3425, 650, 75, 250)
spawn.mapRect(3200, 1100, 1800, 2200)
spawn.mapRect(3300, -400, 200, 1099) // STOP CHANGING THIS ONE!!!!
spawn.mapRect(3450, -400, 250, 1100)
spawn.mapRect(3650, 680, 200, 20)
spawn.mapRect(3800, -400, 1400, 1100)
spawn.mapRect(4100, 700, 100, 300)
spawn.mapRect(4900, -400, 1300, 2500)
spawn.bodyRect(4100, 1000, 100, 100)
spawn.bodyRect(-2100, 2050, 290, 30) //Portal platform
let b = body[body.length - 1];
b.isNotHoldable = true
cons[cons.length] = Constraint.create({
pointA: {
x: -1820,
y: 2065
},
bodyB: b,
pointB: {
x: -135,
y: 0
},
stiffness: 1,
length: 1
});
cons[cons.length] = Constraint.create({
pointA: {
x: -1800,
y: 1400
},
bodyB: b,
pointB: {
x: 135,
y: 0
},
stiffness: 0.005,
length: 700
});
Composite.add(engine.world, [cons[cons.length - 2], cons[cons.length - 1]]);
spawn.bodyRect(5225, -2525, 300, 75);
spawn.bodyRect(4700, -2525, 100, 75, 0.5);
spawn.bodyRect(4900, -2600, 50, 50, 0.4);
spawn.bodyRect(5050, -2475, 500, 100, 0.4);
spawn.bodyRect(2950, -950, 175, 75, 0.5);
spawn.bodyRect(3050, -1000, 75, 50, 0.3);
spawn.bodyRect(2300, -850, 75, 50, 0.7);
spawn.bodyRect(2150, -575, 100, 175, 0.6);
spawn.bodyRect(2500, -550, 400, 150, 0.2);
spawn.bodyRect(1525, -500, 225, 100, 0.2);
spawn.bodyRect(1625, -575, 100, 75);
spawn.bodyRect(1000, -475, 100, 100, 0.8);
spawn.bodyRect(1225, -450, 125, 50, 0.9);
spawn.bodyRect(525, -500, 175, 125, 0.75);
spawn.bodyRect(575, -600, 100, 75, 0.5);
spawn.bodyRect(-925, -1225, 275, 75, 0.4);
spawn.bodyRect(-1125, -1300, 200, 150, 0.7);
spawn.bodyRect(-475, -1250, 200, 100, 0.8);
spawn.bodyRect(-425, -1300, 100, 50, 0.75);
spawn.bodyRect(-1225, -1200, 100, 25, 0.45);
spawn.bodyRect(-1025, -1350, 75, 50, 0.5);
spawn.bodyRect(-450, 1025, 75, 50, 0.5);
spawn.bodyRect(-775, 1050, 50, 50, 0.6);
spawn.bodyRect(-650, 975, 75, 75, 0.2);
spawn.bodyRect(-475, 1025, 100, 50, 0.7);
spawn.bodyRect(-450, 1025, 75, 50, 0.6);
spawn.bodyRect(-800, 1050, 100, 50, 0.5);
spawn.bodyRect(-600, 950, 75, 75, 0.3);
spawn.bodyRect(-500, 1000, 75, 25, 0.2);
spawn.bodyRect(-900, 1025, 150, 50);
spawn.bodyRect(-1350, 1000, 100, 100, 0.4);
spawn.bodyRect(-1225, 1075, 100, 25);
spawn.debris(900, -1000, 2000, 16);
// MOBS //
spawn.randomSmallMob(2900, -1000)
spawn.randomSmallMob(1750, -700)
spawn.randomMob(4250, -1400)
spawn.randomMob(4800, -2400, 0.3)
spawn.randomMob(1000, 600, 0.3)
spawn.randomMob(1650, 950, 0.2)
spawn.randomMob(1300, -1250, 0)
spawn.randomMob(-600, -1250, 0.1)
spawn.randomMob(1000, -600, 0.4)
spawn.randomMob(1800, -700, 0.4)
spawn.randomMob(2200, 950, 0.2)
spawn.randomMob(-1900, 1400, 0.3)
spawn.randomMob(-750, -1000, 0.3)
spawn.randomMob(3250, 1000, 0.1)
spawn.randomMob(2000, -2800, 0.4)
spawn.randomMob(2200, -500, 0)
spawn.randomMob(1800, -450, 0.3)
spawn.randomGroup(2300, -450, 1)
spawn.randomGroup(3000, -450, 0.3)
spawn.randomGroup(6000, -2700, 0)
spawn.randomGroup(-1200, -1300, -0.3)
powerUps.addResearchToLevel()
if (simulation.difficulty > 3) {
spawn.randomLevelBoss(1900, 400, ["shieldingBoss", "shooterBoss", "launcherBoss", "streamBoss"])
} else {
exitDoor.isClosing = false;
}
spawn.secondaryBossChance(800, -800)
powerUps.spawn(4450, 1050, "heal");
if (Math.random() > (0.2 + (simulation.difficulty / 60))) {
powerUps.spawn(4500, 1050, "ammo");
powerUps.spawn(4550, 1050, "ammo");
} else {
powerUps.spawn(4500, 1050, "tech");
spawn.randomMob(4550, 1050, Infinity);
}
powerUps.spawnStartingPowerUps(3750, -940)
const W = 500;
const H = 20;
for (let i = 1; i < 5; i++) {
spawn.bodyRect(700 + (i * 100) + (W * (i - 1)), 1110, W, H, 1, spawn.propsIsNotHoldable)
let b = body[body.length - 1];
cons[cons.length] = Constraint.create({
pointA: {
x: b.position.x - (W / 2) + 50,
y: b.position.y - 1025
},
bodyB: b,
pointB: {
x: -(W / 2) + 50,
y: 0
},
stiffness: 0.002,
length: 1000
});
cons[cons.length] = Constraint.create({
pointA: {
x: b.position.x + (W / 2) - 50,
y: b.position.y - 1025
},
bodyB: b,
pointB: {
x: (W / 2) - 50,
y: 0
},
stiffness: 0.002,
length: 1000
});
Composite.add(engine.world, [cons[cons.length - 1], cons[cons.length - 2]])
}
const boost1 = level.boost(4400, -1385, 1200)
level.custom = () => {
boost1.query();
buttonGreen.query()
buttonYellow.query()
buttonRed.query()
if (!buttonGreen.isUp) {
if (!g) {
Matter.Composite.remove(engine.world, cons[1])
cons.splice(1, 2)
}
g = true;
}
if (!buttonYellow.isUp) {
y = true;
}
if (!buttonRed.isUp) {
r = true;
}
if (g && y && r) {
door.isClosing = false;
} else {
door.isClosing = true;
}
door.openClose()
exitDoor.openClose()
if (m.pos.y > 1600 && 700 < m.pos.x && m.pos.x < 3200) { // Saving player from slime pit
Matter.Body.setVelocity(player, {
x: 0,
y: 0
});
Matter.Body.setPosition(player, {
x: 200,
y: 1000
});
// move bots
for (let i = 0; i < bullet.length; i++) {
if (bullet[i].botType) {
Matter.Body.setPosition(bullet[i], Vector.add(player.position, {
x: 250 * (Math.random() - 0.5),
y: 250 * (Math.random() - 0.5)
}));
Matter.Body.setVelocity(bullet[i], {
x: 0,
y: 0
});
}
}
m.damage(0.1 * simulation.difficultyMode)
m.energy -= 0.1 * simulation.difficultyMode
}
if (simulation.cycle >= nextBlockSpawn && body.length < 100) {
var len = body.length;
body[len] = Matter.Bodies.polygon(Math.floor(Math.random() * 1700) + 1050, 100, Math.floor(Math.random() * 11) + 10, Math.floor(Math.random() * 20) + 15)
body[len].collisionFilter.category = cat.body;
body[len].collisionFilter.mask = cat.player | cat.map | cat.body | cat.bullet | cat.mob | cat.mobBullet;
Composite.add(engine.world, body[len])
nextBlockSpawn = simulation.cycle + Math.floor(Math.random() * 60 + 30)
}
if (exitDoor.isClosing) {
exitDoor.isClosing = false;
for (i = 0; i < mob.length; i++) {
if (mob[i].isBoss && 525 < mob[i].position.x < 3200 && -2500 < mob[i].position.y < 100) {
exitDoor.isClosing = true;
}
}
}
for (let i = 0, len = body.length; i < len; i++) {
if (body[i].position.x > 700 && body[i].position.x < 3200 && body[i].position.y > 1200 && !body[i].isNotHoldable) {
Matter.Body.scale(body[i], 0.99, 0.99);
if (body[i].velocity.y > 3) body[i].force.y -= 0.96 * body[i].mass * simulation.g
const slowY = (body[i].velocity.y > 0) ? Math.max(0.3, 1 - 0.0015 * body[i].velocity.y * body[i].velocity.y) : Math.max(0.98, 1 - 0.001 * Math.abs(body[i].velocity.y)) //down : up
Matter.Body.setVelocity(body[i], {
x: Math.max(0.6, 1 - 0.07 * Math.abs(body[i].velocity.x)) * body[i].velocity.x,
y: slowY * body[i].velocity.y
});
if (body[i].mass < 0.05) {
Matter.Composite.remove(engine.world, body[i])
body.splice(i, 1)
break
}
}
}
for (let i = 0, len = mob.length; i < len; ++i) {
if (mob[i].position.x > 700 && mob[i].position.x < 3200 && mob[i].alive && !mob[i].isShielded && mob[i].position.y > 1200) {
mobs.statusDoT(mob[i], 0.005, 30)
}
}
ctx.beginPath()
ctx.fillStyle = "#666";
ctx.arc(buttonGreen.min.x - 50, buttonGreen.min.y - 70, 20, 0, 2 * Math.PI)
ctx.fillRect(buttonGreen.min.x - 55, buttonGreen.max.y + 25, 10, -95)
ctx.fill()
ctx.beginPath()
ctx.arc(buttonYellow.min.x - 50, buttonYellow.min.y - 70, 20, 0, 2 * Math.PI)
ctx.fillRect(buttonYellow.min.x - 55, buttonYellow.max.y + 25, 10, -95)
ctx.fill()
ctx.beginPath()
ctx.arc(buttonRed.min.x - 50, buttonRed.min.y - 70, 20, 0, 2 * Math.PI)
ctx.fillRect(buttonRed.min.x - 55, buttonRed.max.y + 25, 10, -95)
ctx.fill()
ctx.beginPath()
ctx.arc(buttonGreen.min.x - 50, buttonGreen.min.y - 70, 10, 0, 2 * Math.PI)
ctx.fillStyle = (g ? `rgba(0, 255, 0, 0.9)` : `rgba(255, 0, 0, 0.9)`);
ctx.fill()
ctx.beginPath()
ctx.arc(buttonYellow.min.x - 50, buttonYellow.min.y - 70, 10, 0, 2 * Math.PI)
ctx.fillStyle = (y ? `rgba(0, 255, 0, 0.9)` : `rgba(255, 0, 0, 0.9)`);
ctx.fill()
ctx.beginPath()
ctx.arc(buttonRed.min.x - 50, buttonRed.min.y - 70, 10, 0, 2 * Math.PI)
ctx.fillStyle = (r ? `rgba(0, 255, 0, 0.9)` : `rgba(255, 0, 0, 0.9)`);
ctx.fill()
slimePit.query();
ctx.shadowColor = 'hsla(160, 100%, 50%, 1)'
ctx.shadowBlur = 100;
// slimePit.draw()
ctx.shadowBlur = 0;
ctx.shadowColor = 'rgba(0, 0, 0, 0)'
deliveryButton.query()
portal[2].query()
//portal[3].query()
portal2[2].query()
//portal2[3].query()
deliverySlime.level(deliveryButton.isUp)
topSlime.level(!r)
rotor.rotate()
ctx.fillStyle = "#d4f4f4"
ctx.fillRect(3500, 675, 600, 450)
level.enter.draw()
level.exit.drawAndCheck()
}
level.customTopLayer = () => {
topSlime.query();
deliverySlime.query()
deliverySlime2.query()
drip1.draw()
drip2.draw()
drip3.draw()
ctx.fillStyle = `rgba(68, 68, 68, ${Math.max(0.3, Math.min((4200 - m.pos.x) / 100, 0.99))})`
ctx.fillRect(4100, 650, 850, 500)
ctx.fillStyle = "rgba(0,20,40,0.1)"
ctx.fillRect(4025, -1300, 475, 300)
ctx.fillRect(3325, -1000, 375, 600)
ctx.fillRect(425, 100, 3050, 2400)
ctx.fillRect(-1775, 800, 1750, 2100)
ctx.fillStyle = "rgba(0,20,40,0.2)"
ctx.fillRect(2725, -860, 450, 460)
ctx.fillRect(2125, -760, 450, 360)
ctx.fillRect(1525, -660, 450, 260)
ctx.fillRect(925, -560, 450, 160)
ctx.fillRect(3700, -980, 100, 1200)
ctx.fillStyle = `#444`;
ctx.fillRect(465, 690, 70, 209)
ctx.fillRect(3365, 690, 70, 209)
ctx.beginPath()
ctx.arc(500, 870, 20, 0, 2 * Math.PI)
ctx.arc(500, 820, 20, 0, 2 * Math.PI)
ctx.arc(500, 770, 20, 0, 2 * Math.PI)
ctx.fillStyle = "rgba(0, 0, 0, 0.3";
ctx.fill()
ctx.beginPath()
ctx.arc(500, 870, 10, 0, 2 * Math.PI)
ctx.fillStyle = (g ? `rgba(0, 255, 0, 0.9)` : `rgba(255, 0, 0, 0.9)`);
ctx.fill()
ctx.beginPath()
ctx.arc(500, 820, 10, 0, 2 * Math.PI)
ctx.fillStyle = (y ? `rgba(0, 255, 0, 0.9)` : `rgba(255, 0, 0, 0.9)`);
ctx.fill()
ctx.beginPath()
ctx.arc(500, 770, 10, 0, 2 * Math.PI)
ctx.fillStyle = (r ? `rgba(0, 255, 0, 0.9)` : `rgba(255, 0, 0, 0.9)`);
ctx.fill()
deliveryButton.draw()
// deliverySlime.draw()
// deliverySlime2.draw()
// topSlime.draw()
buttonGreen.draw()
buttonYellow.draw()
buttonRed.draw()
portal[0].draw()
portal[2].draw()
portal2[0].draw()
portal2[2].draw()
}
},
ngon() { //make by Oranger
simulation.makeTextLog(`<strong>"ngon"</strong> by <span class='color-var'>Oranger</span>`);
document.body.style.backgroundColor = "#dcdcde";
let needGravity = [];
let s = { //mech statue
x: -200,
y: -2350,
angle: 0,
scale: 15,
h: { //hip
x: 12,
y: 24
},
k: { //knee
x: -30.96, //-17.38
y: 58.34, //70.49
//x2: -33.96, //x - 3
//y2: 58.34 //same as y
},
f: { //foot
x: 0,
y: 91 //112
},
fillColor: "#ccc", //white
fillColorDark: "#bbb", //25% from white
lineColor: "#999", //#333
lineColorLight: "#aaa" //#4a4a4a
}
const boost1 = level.boost(2550, 1500, 1700)
const boost2 = level.boost(-3400, -2050, 2100)
level.custom = () => {
boost1.query();
boost2.query();
level.exit.drawAndCheck();
level.enter.draw();
for (let i = 0; i < needGravity.length; i++) {
needGravity[i].force.y += needGravity[i].mass * simulation.g;
}
ctx.fillStyle = "#444" //light fixtures
ctx.fillRect(2350, 995, 40, 10)
//ctx.fillRect(2280, -6005, 40, 10)
//statue
ctx.save();
ctx.translate(s.x, s.y);
//statueLeg is at the bottom, below the enemies but above the NGON function
statueLeg(-3, s.lineColorLight);
statueLeg(0, s.lineColor);
//head
ctx.rotate(s.angle);
ctx.beginPath();
ctx.arc(0, 0, 30 * s.scale, 0, 2 * Math.PI);
let grd = ctx.createLinearGradient(-30 * s.scale, 0, 30 * s.scale, 0);
grd.addColorStop(0, s.fillColorDark);
grd.addColorStop(1, s.fillColor);
ctx.fillStyle = grd;
ctx.fill();
ctx.arc(15 * s.scale, 0, 4 * s.scale, 0, 2 * Math.PI);
ctx.strokeStyle = s.lineColor;
ctx.lineWidth = 2 * s.scale;
ctx.stroke();
ctx.restore();
};
level.customTopLayer = () => {
//boost chute for lack of a better name
ctx.fillStyle = "rgba(60,60,60,0.9)";
ctx.fillRect(-3451, -4000, 202, 1500);
ctx.fillRect(2499, -170, 202, 1170);
ctx.fillStyle = "rgba(0,0,0,0.2)";
ctx.beginPath(); //basement
ctx.moveTo(2360, 1000);
ctx.lineTo(2120, 900);
ctx.lineTo(1500, 900);
ctx.lineTo(1500, 1500);
ctx.lineTo(3000, 1500);
ctx.lineTo(3000, 1000);
ctx.lineTo(2380, 1000);
ctx.lineTo(2870, 1500);
ctx.lineTo(1870, 1500);
ctx.lineTo(2360, 1000);
ctx.fill();
// ctx.beginPath(); //exit
// ctx.moveTo(1600, -6000);
// ctx.lineTo(1600, -5000);
// ctx.lineTo(3000, -5000);
// ctx.lineTo(3000, -6000);
// ctx.lineTo(2310, -6000);
// ctx.lineTo(2600, -5000);
// ctx.lineTo(2000, -5000);
// ctx.lineTo(2290, -6000);
// ctx.lineTo(1600, -6000);
// ctx.fill();
ctx.fillStyle = "rgba(0,0,0,0.3)";
ctx.fillRect(1600, -1000, 1400, 830);
ctx.fillRect(1600, -170, 520, 170);
ctx.fillRect(-1300, -200, 2200, 200); //statue base
ctx.fillRect(-800, -400, 1200, 200);
ctx.fillRect(-500, -700, 600, 300);
//ctx.fillRect(-4000, -6000, 2000, 1000); //left side
ctx.fillRect(-4000, -2500, 2000, 2500);
};
level.setPosToSpawn(1810, 1450);
spawn.mapRect(level.enter.x, level.enter.y + 20, 100, 20);
level.exit.x = 2700;
level.exit.y = -4030;
spawn.mapRect(level.exit.x, level.exit.y + 20, 100, 20);
level.defaultZoom = 3500
simulation.zoomTransition(level.defaultZoom)
// powerUps.spawnStartingPowerUps(1475, -1175);
spawn.debris(2750, 1500, 200, 4); //16 debris per level
spawn.debris(1770, -350, 120, 4); //16 debris per level
spawn.debris(-3200, 0, 1000, 6); //16 debris per level
//boundaries
spawn.mapRect(-4100, 1500, 7200, 100); //base floor
spawn.mapRect(3000, -4000, 100, 5600); //right barrier
spawn.mapRect(-4100, -4000, 100, 5600); //left barrier
//spawn.mapRect(1600, -10000, 1500, 4000); //upper right wall
//spawn.mapRect(-4100, -10000, 2100, 4000); //upper left wall
spawn.mapRect(1600, -4000, 1500, 3000); //right wall
spawn.mapRect(-4100, 0, 5600, 1550); //floor
//starting room
spawn.mapRect(1500, 0, 700, 900);
spawn.mapRect(2120, -170, 380, 1170);
spawn.mapRect(2700, -170, 400, 1170);
//spawn.mapVertex(2296, 400, "0 0 0 1200 300 1200 400 0");
//spawn.mapVertex(2904, 400, "0 0 0 1200 -300 1200 -400 0");
//left area
spawn.mapRect(-3500, -300, 300, 400); //floor 1
spawn.mapRect(-3900, -600, 300, 100);
spawn.mapRect(-3500, -900, 300, 100);
spawn.mapRect(-3100, -1150, 1000, 150); //floor 2
spawn.mapRect(-2200, -2600, 200, 1600);
spawn.mapRect(-2700, -1450, 300, 100);
spawn.mapRect(-3100, -1750, 300, 100);
spawn.mapRect(-3500, -2050, 300, 100);
spawn.mapRect(-4100, -4000, 650, 1500); //floor 3
spawn.mapRect(-3250, -4000, 1250, 1500);
//statue base
spawn.mapRect(-700, -900, 1000, 200); //top
//left
spawn.mapRect(-700, -900, 200, 500);
spawn.mapRect(-1000, -600, 500, 200);
spawn.mapRect(-1000, -600, 200, 400);
spawn.mapRect(-1300, -300, 500, 100);
//right
spawn.mapRect(100, -900, 200, 500);
spawn.mapRect(100, -600, 500, 200);
spawn.mapRect(400, -600, 200, 400);
spawn.mapRect(400, -300, 500, 100);
hangingNGON(-1900, -4000, 1, 1000, 1, false, {
density: 0.001, //default density is 0.001
friction: 0.0001,
frictionAir: 0.001,
frictionStatic: 0,
restitution: 0,
isNotHoldable: true
});
hangingNGON(1900, -4600, 0.2, 300, 0.0005, false, {
density: 0.00005, //default density is 0.001
friction: 0.0001,
frictionAir: 0.003,
frictionStatic: 0,
restitution: 1,
isNotHoldable: true
});
// // Never gonna give you up
// spawn.bodyRect(-8000, -10100, 15, 100);
// // Never gonna let you down
// spawn.bodyRect(-7915, -10100, 15, 100);
// // Never gonna run around and desert you
// body[body.length] = Bodies.polygon(-7950, -10025, 0, 25, { //circle
// friction: 0.05,
// frictionAir: 0.001
// });
// // Never gonna make you cry
// spawn.bodyRect(6985, -10100, 15, 100);
// // Never gonna say goodbye
// spawn.bodyRect(6900, -10100, 15, 100);
// // Never gonna tell a lie and hurt you
// body[body.length] = Bodies.polygon(6950, -10025, 0, 25, { //circle
// friction: 0.05,
// frictionAir: 0.001
// });
//pile of blocks
spawn.bodyRect(1920, -400, 200, 400)
spawn.bodyRect(1720, -250, 200, 250)
spawn.bodyRect(1770, -300, 150, 50)
spawn.bodyRect(2120, -280, 100, 100)
spawn.bodyRect(1990, -500, 100, 100)
//doors under statue
spawn.bodyRect(850, -50, 50, 50)
spawn.bodyRect(850, -100, 50, 50)
spawn.bodyRect(850, -150, 50, 50)
spawn.bodyRect(850, -200, 50, 50)
spawn.bodyRect(-1300, -50, 50, 50)
spawn.bodyRect(-1300, -100, 50, 50)
spawn.bodyRect(-1300, -150, 50, 50)
spawn.bodyRect(-1300, -200, 50, 50)
// on the statue base
spawn.randomMob(700 + Math.random() * 100, -500 + Math.random() * 100, 1);
spawn.randomMob(400 + Math.random() * 100, -800 + Math.random() * 100, 0.4);
spawn.randomMob(100 + Math.random() * 100, -1100 + Math.random() * 100, -0.2);
spawn.randomGroup(-200, -1400, -0.4);
spawn.randomMob(-600 + Math.random() * 100, -1100 + Math.random() * 100, -0.2);
spawn.randomMob(-900 + Math.random() * 100, -800 + Math.random() * 100, 0.4);
spawn.randomMob(-1200 + Math.random() * 100, -500 + Math.random() * 100, 1);
//in the statue base
spawn.randomSmallMob(400 + Math.random() * 300, -150 + Math.random() * 100, 0.2);
spawn.randomSmallMob(-1100 + Math.random() * 300, -150 + Math.random() * 100, 0.2);
//bottom left
spawn.randomMob(-2600 + Math.random() * 300, -700 + Math.random() * 300, 0.6);
spawn.randomSmallMob(-3000 + Math.random() * 300, -400 + Math.random() * 300, 0.2);
spawn.randomSmallMob(-3000 + Math.random() * 300, -400 + Math.random() * 300, 0);
spawn.randomMob(-3900 + Math.random() * 100, -200 + Math.random() * 100, 0.6);
spawn.randomMob(-3400 + Math.random() * 100, -400, 0.4);
spawn.randomSmallMob(-3800 + Math.random() * 100, -700, -0.4);
spawn.randomMob(-3400 + Math.random() * 100, -1000, 0.6);
spawn.randomMob(-3000 + Math.random() * 100, -1850, 0);
spawn.randomGroup(-2700, -2000, 0.4);
//top left
spawn.randomSmallMob(-3800, -5800, -0.2);
spawn.randomSmallMob(-2400, -5200, 0.2);
//top right
spawn.randomGroup(2000, -5700, 0.6);
powerUps.addResearchToLevel() //needs to run after mobs are spawned
let bosses = ["shooterBoss", "launcherBoss", "laserTargetingBoss", "streamBoss", "pulsarBoss", "spawnerBossCulture", "laserBoss", "growBossCulture"];
let abc = Math.random();
if (simulation.difficulty > 3) {
if (abc < 0.6) {
spawn.randomLevelBoss(-1500 + Math.random() * 250, -1100 + Math.random() * 200, bosses);
} else if (abc < 0.85) {
spawn.laserBoss(-350 + Math.random() * 300, -600 + Math.random() * 200);
} else {
spawn.randomLevelBoss(850 + Math.random() * 250, -1100 + Math.random() * 200, bosses);
}
}
spawn.secondaryBossChance(850 + Math.random() * 250, -1100 + Math.random() * 200)
//draw leg for statue
function statueLeg(shift, color) {
ctx.save();
ctx.translate(shift, shift);
//front leg
let stroke = color;
ctx.beginPath();
ctx.moveTo((s.h.x + shift) * s.scale, (s.h.y + shift) * s.scale);
ctx.lineTo((s.k.x + 2 * shift) * s.scale, (s.k.y + shift) * s.scale);
ctx.lineTo((s.f.x + shift) * s.scale, (s.f.y + shift) * s.scale);
ctx.strokeStyle = stroke;
ctx.lineWidth = 7 * s.scale;
ctx.stroke();
//toe lines
ctx.beginPath();
ctx.moveTo((s.f.x + shift) * s.scale, (s.f.y + shift) * s.scale);
ctx.lineTo((s.f.x - 15 + shift) * s.scale, (s.f.y + 5 + shift) * s.scale);
ctx.moveTo((s.f.x + shift) * s.scale, (s.f.y + shift) * s.scale);
ctx.lineTo((s.f.x + 15 + shift) * s.scale, (s.f.y + 5 + shift) * s.scale);
ctx.lineWidth = 4 * s.scale;
ctx.stroke();
//hip joint
ctx.beginPath();
ctx.arc((s.h.x + shift) * s.scale, (s.h.y + shift) * s.scale, 11 * s.scale, 0, 2 * Math.PI);
//knee joint
ctx.moveTo((s.k.x + 7 + 2 * shift) * s.scale, (s.k.y + shift) * s.scale);
ctx.arc((s.k.x + 2 * shift) * s.scale, (s.k.y + shift) * s.scale, 7 * s.scale, 0, 2 * Math.PI);
//foot joint
ctx.moveTo((s.f.x + 6 + shift) * s.scale, (s.f.y + shift) * s.scale);
ctx.arc((s.f.x + shift) * s.scale, (s.f.y + shift) * s.scale, 6 * s.scale, 0, 2 * Math.PI);
ctx.fillStyle = s.fillColor;
ctx.fill();
ctx.lineWidth = 2 * s.scale;
ctx.stroke();
ctx.restore();
}
// | | | | |
// n - g o n
//when s = 1 (scale), it's 3408 long and 800 tall (height of g)
function hangingNGON(x, y, s, height, stiffness, pin, properties) {
//makes a compound part of 3 bodyVertex parts
function compound3Parts(x1, y1, v1, x2, y2, v2, x3, y3, v3, properties) {
const part1 = Matter.Bodies.fromVertices(x1, y1, Vertices.fromPath(v1), properties);
const part2 = Matter.Bodies.fromVertices(x2, y2, Vertices.fromPath(v2), properties);
const part3 = Matter.Bodies.fromVertices(x3, y3, Vertices.fromPath(v3), properties);
const compoundParts = Body.create({
parts: [part1, part2, part3],
});
Composite.add(engine.world, [compoundParts]);
needGravity[needGravity.length] = compoundParts;
composite[composite.length] = compoundParts;
body[body.length] = part1;
body[body.length] = part2;
body[body.length] = part3;
setTimeout(function () {
compoundParts.collisionFilter.category = cat.body;
compoundParts.collisionFilter.mask = cat.body | cat.player | cat.bullet | cat.mob | cat.mobBullet | cat.map
}, 1000);
}
//for attaching the block to a point
function addConstraint(x, y, px, py, stiff, body, pin = false) {
if (pin) {
map[map.length] = Bodies.polygon(x, y, 0, 15); //circle above
}
cons[cons.length] = Constraint.create({
pointA: {
x: x,
y: y
},
bodyB: body,
pointB: {
x: px,
y: py
},
stiffness: stiff
});
Composite.add(engine.world, cons[cons.length - 1]);
}
//I SINCERELY APOLOGIZE FOR THE ILLEGIBLE BLOCKS OF STRING CONCATENATION
//s is scale
//n
compound3Parts(
x + 100 * s,
y + 310 * s,
("0 0 " + 200 * s + " 0 " + 200 * s + " " + 620 * s + " 0 " + 620 * s),
x + 300 * s,
y + 160 * s,
(200 * s + " " + 40 * s + " " + 400 * s + " " + 40 * s + " " + 400 * s + " " + 280 * s + " " + 200 * s + " " + 280 * s),
x + 499 * s,
y + 333.3 * s,
(400 * s + " " + 40 * s + " " + 540 * s + " " + 40 * s + " " + 600 * s + " " + 100 * s + " " + 600 * s + " " + 620 * s + " " + 400 * s + " " + 620 * s + " " + 400 * s + " " + 280 * s),
properties
);
addConstraint(x + 300 * s, y - height, 0, -10 * s, stiffness, composite[composite.length - 1], pin);
//-
spawn.bodyRect(x + 800 * s, y + 250 * s, 200 * s, 100 * s, 1, properties);
body[body.length - 1].frictionAir = 0.05 //friction to make jump easier
addConstraint(x + 900 * s, y - height, 0, -30 * s, stiffness, body[body.length - 1], pin);
//g
compound3Parts(
x + 1400 * s,
y + 300 * s,
("0 0 " + 250 * s + " 0 " + 425 * s + " " + 175 * s + " " + 425 * s + " " + 450 * s + " " + 275 * s + " " + 600 * s + " 0 " + 600 * s + " " + -175 * s + " " + 425 * s + " " + -175 * s + " " + 175 * s),
x + 1636 * s,
y + 633 * s,
(425 * s + " " + 450 * s + " " + 425 * s + " " + 750 * s + " " + 375 * s + " " + 800 * s + " " + 275 * s + " " + 675 * s + " " + 275 * s + " " + 600 * s),
x + 1398 * s,
y + 737 * s,
(375 * s + " " + 800 * s + " " + -75 * s + " " + 800 * s + " " + -75 * s + " " + 675 * s + " " + 275 * s + " " + 675 * s),
properties
);
addConstraint(x + 1500 * s, y - height, 0, -15 * s, stiffness, composite[composite.length - 1], pin);
//o
spawn.bodyVertex(
x + 2300 * s,
y + 300 * s,
("0 0 " + 250 * s + " 0 " + 425 * s + " " + 175 * s + " " + 425 * s + " " + 425 * s + " " + 250 * s + " " + 600 * s + " 0 " + 600 * s + " " + -175 * s + " " + 425 * s + " " + -175 * s + " " + 175 * s),
properties
);
addConstraint(x + 2300 * s, y - height, 0, -10 * s, stiffness, body[body.length - 1], pin);
//n
compound3Parts(
x + 2900 * s,
y + 310 * s,
("0 0 " + 200 * s + " 0 " + 200 * s + " " + 620 * s + " 0 " + 620 * s),
x + 3100 * s,
y + 160 * s,
(200 * s + " " + 40 * s + " " + 400 * s + " " + 40 * s + " " + 400 * s + " " + 280 * s + " " + 200 * s + " " + 280 * s),
x + 3300 * s,
y + 333.3 * s,
(400 * s + " " + 40 * s + " " + 540 * s + " " + 40 * s + " " + 600 * s + " " + 100 * s + " " + 600 * s + " " + 620 * s + " " + 400 * s + " " + 620 * s + " " + 400 * s + " " + 280 * s),
properties
);
addConstraint(x + 3100 * s, y - height, 0, -10 * s, stiffness, composite[composite.length - 1], pin);
}
},
tunnel() { // by Scarlettt
simulation.makeTextLog(`<strong>tunnel</strong> by <span class='color-var'>Scarlettt</span>`);
level.custom = () => {
level.exit.drawAndCheck();
//enter
ctx.beginPath();
ctx.moveTo(level.enter.x, level.enter.y + 30);
ctx.lineTo(level.enter.x, level.enter.y - 80);
ctx.bezierCurveTo(level.enter.x, level.enter.y - 170, level.enter.x + 100, level.enter.y - 170, level.enter.x + 100, level.enter.y - 80);
ctx.lineTo(level.enter.x + 100, level.enter.y + 30);
ctx.lineTo(level.enter.x, level.enter.y + 30);
ctx.fillStyle = "#013";
ctx.fill();
//exit
ctx.beginPath();
ctx.moveTo(level.exit.x, level.exit.y + 30);
ctx.lineTo(level.exit.x, level.exit.y - 80);
ctx.bezierCurveTo(level.exit.x, level.exit.y - 170, level.exit.x + 100, level.exit.y - 170, level.exit.x + 100, level.exit.y - 80);
ctx.lineTo(level.exit.x + 100, level.exit.y + 30);
ctx.lineTo(level.exit.x, level.exit.y + 30);
ctx.fillStyle = "#9ff";
ctx.fill();
// hiding rooms in path to second floor
ctx.fillStyle = "#322";
ctx.fillRect(3750, -1650, 3500, 350);
// prevent the user from getting into the secreter room without defeating all mobs
if (m.pos.x > 1500 && m.pos.x < 2500 && m.pos.y > -4000 && m.pos.y < -3500 && mob.reduce((a, i) => {
return a || ((Math.sqrt((i.position.x - 3600) * (i.position.x - 3600) + (i.position.y + 3600) * (i.position.y + 3600)) < 20000) && i.isDropPowerUp);
}, false) && !emergencyActivated) {
Matter.Body.setPosition(player, {
x: 2800,
y: m.pos.y
});
}
button.query();
isButtonTapped = isButtonTapped || !button.isUp;
hazard.level(!isButtonTapped);
if (Matter.Query.region([player], hazard).length) m.energy -= 0.001;
buttonSec.query();
buttonSec.draw();
if (!buttonSec.isUp && !hasSecretButton) {
for (var i = 0; i < 5; i++) {
powerUps.spawn(3614, -3700, "ammo");
}
hasSecretButton = true;
}
buttonThird.query();
buttonThird.draw();
if (!buttonThird.isUp && !hasSecretButton2) {
for (var i = 0; i < 1; i++) powerUps.spawn(1614, -3700, "research");
hasSecretButton2 = true;
}
if (!buttonSec.isUp) {
secretAnimTrans += 2;
secretAnimTime = 1;
secretAnimTrans = Math.max(0, Math.min(secretAnimTrans, 60));
} else {
secretAnimTrans--;
if (secretAnimTime) secretAnimTrans += 3;
secretAnimTrans = Math.min(60, Math.max(secretAnimTrans, 0));
}
if (secretAnimTime > 0) {
secretAnimTime++;
if (secretAnimTime > 150) secretAnimTime = 0;
}
if (emergencyActivated || !buttonThird.isUp) {
secretAnimTrans2 += 2;
secretAnimTime2 = 1;
secretAnimTrans2 = Math.max(0, Math.min(secretAnimTrans2, 60));
} else {
secretAnimTrans2--;
if (secretAnimTime2) secretAnimTrans2 += 3;
secretAnimTrans2 = Math.min(60, Math.max(secretAnimTrans2, 0));
}
if (secretAnimTime2 > 0) {
secretAnimTime2++;
if (secretAnimTime2 > 150) secretAnimTime2 = 0;
}
ctx.beginPath();
ctx.arc(m.pos.x, m.pos.y, 200, 0, 2 * Math.PI);
ctx.fillStyle = "#ff25";
ctx.fill();
ctx.beginPath();
ctx.arc(m.pos.x, m.pos.y, 400, 0, 2 * Math.PI);
ctx.fillStyle = "#ff22";
ctx.fill();
ctx.beginPath();
ctx.arc(m.pos.x, m.pos.y, 700, 0, 2 * Math.PI);
ctx.fillStyle = "#ff21";
ctx.fill();
elevator.move();
elevator.drawTrack();
};
level.customTopLayer = () => {
hazard.query();
secretHazard.level(emergencyActivated);
secretHazard.query();
button.draw();
// Fire damage
let isInRange = flames.reduce((a, i) => a || Math.sqrt((m.pos.x - i[0]) * (m.pos.x - i[0]) + (m.pos.y + 90 - i[1]) * (m.pos.y + 90 - i[1])) < 50, false);
if (isInRange) {
fireDmgLevel++;
fireDmgLevel = Math.min(fireDmgLevel, 100);
} else {
fireDmgLevel--;
fireDmgLevel = Math.max(fireDmgLevel, -8);
}
if (fireDmgLevel > -8) {
ctx.fillStyle = "#fa0b";
ctx.fillRect(m.pos.x - 50, m.pos.y - 100, Math.min(fireDmgLevel * 12.5 + 100, 100), 15);
}
if (fireDmgLevel > 0) {
ctx.fillStyle = "#f00c";
ctx.fillRect(m.pos.x - 50, m.pos.y - 100, fireDmgLevel, 15);
m.damage(0.001 * (1.5 * isInRange + 1));
drawFlame(m.pos.x, m.pos.y + 90, "#d40", Math.PI / 2 + 1);
drawFlame(m.pos.x, m.pos.y + 90, "#d40", Math.PI / 2 + 1);
drawFlame(m.pos.x, m.pos.y + 90, "#d40", Math.PI / 2 + 1);
drawFlame(m.pos.x, m.pos.y + 90, "#d40", Math.PI / 2 - 1);
drawFlame(m.pos.x, m.pos.y + 90, "#d40", Math.PI / 2 - 1);
drawFlame(m.pos.x, m.pos.y + 90, "#d40", Math.PI / 2 - 1);
drawFlame(m.pos.x, m.pos.y + 90, "#f70", Math.PI / 2);
drawFlame(m.pos.x, m.pos.y + 90, "#f70", Math.PI / 2);
drawFlame(m.pos.x, m.pos.y + 90, "#f70", Math.PI / 2);
}
for (let j = 0; j < 5; j++) {
drawFlame(1130 + j * 10, -1700)
for (let i = 0; i < 7; i++) drawFlame(2550 + i * 200, -2800);
for (let i = 0; i < 10; i++) drawFlame(2800 + i * 500, -1650);
for (let i = 0; i < 9; i++) drawFlame(1595 + i * 95, -3860);
drawFlame(4850, -1300);
drawFlame(6350, -1300);
}
ctx.fillStyle = "#541";
for (let i = 0; i < 9; i++) {
ctx.fillRect(1592 + i * 95, -3860, 6, 70);
}
if (m.pos.x > 1500 && m.pos.x < 3750 && m.pos.y > -5000 && m.pos.y < -3300) {
secretRoomTrans -= 5;
secretRoomTrans = Math.max(secretRoomTrans, 85);
} else {
secretRoomTrans += 5;
secretRoomTrans = Math.min(secretRoomTrans, 250);
}
let hasMob = mob.reduce((a, i) => {
return a || ((Math.sqrt((i.position.x - 3600) * (i.position.x - 3600) + (i.position.y + 3600) * (i.position.y + 3600)) < 20000) && i.isDropPowerUp);
}, false) && !emergencyActivated;
door.isClosing = hasMob;
door.openClose();
ctx.fillStyle = "#444444" + secretRoomTrans.toString(16);
ctx.fillRect(1480, -5000, 2270, 1710);
if (hasMob) {
ctx.fillStyle = "#444";
ctx.fillRect(1480, -5000, 1070, 1710);
}
if (secretAnimTrans > 0) {
drawProject([3614, -3530], [2900, -3900], [3400, -3600], secretAnimTrans, 60);
if (secretAnimTrans >= 42) {
ctx.font = "27px monospace";
ctx.textAlign = "start"
ctx.fillStyle = "#00ffff" + Math.floor((secretAnimTrans - 40) * 12.75).toString(16);
ctx.fillText("Waste Discharge Interruption:", 2910, -3870);
ctx.fillText("Owner 'Scarlet' not found", 2910, -3830);
ctx.fillText("Detected user: 'm'", 2910, -3790);
ctx.fillStyle = (hasMob ? "#ff6644" : "#ffff44") + Math.floor((secretAnimTrans - 40) * 12.75).toString(16);
ctx.fillText(hasMob ? "AREA HAS MOBS." : "Area clear.", 2910, -3710);
ctx.fillText(hasMob ? "'openDoor' failed." : "'openDoor' complete.", 2910, -3670);
ctx.strokeStyle = "#00ff00" + Math.floor((secretAnimTrans - 40) * 6).toString(16);
ctx.beginPath();
ctx.arc(3300, -3730, 60, 0, 2 * Math.PI);
ctx.stroke();
ctx.arc(3330, -3730, 8, 0, 2 * Math.PI);
ctx.lineWidth = 4;
ctx.stroke();
ctx.textAlign = "center";
ctx.fillStyle = "#00ffff" + Math.floor((secretAnimTrans - 40) * 12.75).toString(16);
ctx.font = "30px monospace";
ctx.fillText("n-gon inc", 3300, -3630);
ctx.font = "25px Arial";
}
}
if (secretAnimTrans2 > 0) {
drawProject([1614, -3530], [2050, -3900], [1550, -3600], secretAnimTrans2, 60);
if (secretAnimTrans2 >= 42) {
ctx.font = "27px monospace";
ctx.textAlign = "start";
ctx.fillStyle = "#00ffff" + Math.floor((secretAnimTrans2 - 40) * 12.75).toString(16);
ctx.fillText("SECURITY BREACH DETECTED", 1560, -3870);
ctx.fillText("Entity name: m", 1560, -3830);
ctx.fillStyle = (tech.totalCount < 25 ? (tech.totalCount < 10 ? "#ffff44" : "#22ff22") : "#ff6644") + Math.floor((secretAnimTrans2 - 40) * 12.75).toString(16);
ctx.fillText("Threat level: " + (tech.totalCount < 25 ? (tech.totalCount < 10 ? "Low" : "Medium") : "HIGH"), 1560, -3790);
if (tech.totalCount >= 15) ctx.fillText("PROCEDURE ACTIVATED", 1560, -3750);
ctx.strokeStyle = "#00ff00" + Math.floor((secretAnimTrans2 - 40) * 6).toString(16);
ctx.beginPath();
ctx.arc(1950, -3730, 60, 0, 2 * Math.PI);
ctx.stroke();
ctx.arc(1980, -3730, 8, 0, 2 * Math.PI);
ctx.lineWidth = 4;
ctx.stroke();
ctx.textAlign = "center";
ctx.fillStyle = "#00ffff" + Math.floor((secretAnimTrans2 - 40) * 12.75).toString(16);
ctx.font = "30px monospace";
ctx.fillText("n-gon inc", 1950, -3630);
ctx.font = "25px Arial";
if (secretAnimTrans2 >= 60) {
if (!emergencyActivated && tech.totalCount >= 10) {
for (let i = 0; i < 5; i++) {
spawn.exploder(1614, -3900);
if (tech.totalCount >= 25) spawn.randomMob(1614, -3900, Infinity);
}
emergencyActivated = true;
}
}
}
}
};
level.setPosToSpawn(0, -50); //normal spawn
level.exit.x = 8500;
level.exit.y = 680;
level.defaultZoom = 1800
simulation.zoomTransition(level.defaultZoom)
document.body.style.backgroundColor = "#123";
// powerUps.spawnStartingPowerUps(1475, -1175);
// spawn.debris(750, -2200, 3700, 16); //16 debris per level
// spawn blocks
spawn.mapRect(-100, 0, 1050, 100);
spawn.mapRect(900, -300, 50, 300);
spawn.mapRect(700, -300, 50, 200);
// first room
spawn.mapRect(-100, -350, 850, 50);
spawn.mapRect(900, -350, 850, 50);
spawn.mapRect(-100, -1550, 50, 1200);
spawn.mapRect(1700, -1550, 50, 1200);
spawn.mapRect(-100, -1550, 850, 50);
spawn.mapRect(900, -1550, 850, 50);
spawn.bodyRect(700, -400, 50, 50);
spawn.bodyRect(900, -400, 50, 50);
spawn.mapRect(500, -650, 650, 25);
spawn.mapRect(200, -1000, 200, 25);
spawn.mapRect(1250, -1000, 200, 25);
spawn.mapRect(600, -1300, 450, 25);
spawn.mapRect(700, -1650, 50, 100);
spawn.mapRect(900, -1650, 50, 100);
// pathway to second room
spawn.mapRect(950, -1650, 3050, 50);
spawn.mapRect(1100, -1700, 100, 50);
// second room
spawn.mapRect(0, -5000, 1500, 3000);
spawn.mapRect(1500, -2050, 300, 50);
spawn.mapRect(2000, -3100, 300, 1100);
spawn.mapRect(1500, -5000, 2250, 1000);
spawn.mapRect(1500, -3500, 1050, 225);
spawn.mapRect(4000, -5000, 500, 3000);
spawn.mapRect(3748, -5000, 252, 1550);
spawn.mapRect(1700, -2400, 300, 50);
spawn.mapRect(1500, -2750, 300, 50);
spawn.mapRect(2300, -3000, 1700, 50);
spawn.mapRect(2300, -2800, 1700, 800);
spawn.mapRect(2450, -3300, 1300, 100);
// secret room in second room
spawn.mapRect(2700, -3500, 1050, 50);
spawn.mapRect(2549, -5000, 1201, 1000);
const buttonSec = level.button(3550, -3500);
const buttonThird = level.button(1550, -3500);
let hasSecretButton = false,
hasSecretButton2 = false,
secretAnimTrans = 0,
secretAnimTime = 0,
secretAnimTrans2 = 0,
secretAnimTime2 = 0;
let emergencyActivated = false;
const door = level.door(2450, -4000, 100, 500, 490);
const secretHazard = level.hazard(1500, -4000, 1000, 510, 0.01);
// hazards
const button = level.button(3800, -3000);
const hazard = level.hazard(2300, -3090, 1700, 110, 0.005);
let isButtonTapped = false;
// if (b.inventory.length < 5) powerUps.spawn(3800, -3200, "gun");
powerUps.spawn(3900, -3100, "heal", true, null, 30);
powerUps.spawn(3900, -3100, "heal", true, null, 30);
// path to the third room
spawn.mapRect(2000, -1850, 50, 200);
spawn.mapRect(2200, -2000, 50, 200);
spawn.mapRect(2400, -1850, 50, 200);
spawn.mapRect(4200, -1650, 1300, 50);
spawn.mapRect(5700, -1650, 1300, 50);
spawn.mapRect(7200, -1650, 750, 50);
spawn.mapRect(3700, -1600, 50, 350);
spawn.mapRect(7250, -1600, 50, 350);
spawn.mapRect(3750, -1300, 3500, 50);
spawn.mapRect(4500, -2150, 3550, 50)
// third room
spawn.mapRect(7900, -1600, 50, 1000);
spawn.mapRect(8050, -3000, 50, 2400);
spawn.mapRect(7000, -600, 950, 50);
spawn.mapRect(8050, -600, 950, 50);
spawn.mapRect(7000, -600, 50, 1000);
spawn.mapRect(8950, -600, 50, 1000);
spawn.mapRect(7000, 400, 950, 50);
spawn.mapRect(8050, 400, 950, 50);
spawn.mapRect(7900, 400, 50, 300);
spawn.mapRect(7900, 700, 1000, 50);
const elevator = level.elevator(7962.5, 500, 75, 50, -1800)
// fire damage
const flames = [];
flames.push([1150, -1700], [1150, -1770]);
for (let i = 0; i < 10; i++) flames.push([2800 + i * 500, -1650], [2800 + i * 500, -1720]);
flames.push([4850, -1300], [6350, -1300], [4850, -1370], [6350, -1370]);
let fireDmgLevel = -8;
let secretRoomTrans = 250;
// mobs
let mobList1 = [
[500, -750],
[1150, -750],
[825, -1100],
[300, -1100],
[1350, -1100]
];
while (mobList1.length > 5 - Math.sqrt(simulation.difficulty * 2.5) && mobList1.length) {
let rand = Math.floor(Math.random() * mobList1.length);
spawn[["hopper", "sneaker", "striker"][Math.floor(Math.random() * 3)]](mobList1[rand][0], mobList1[rand][1], 60 + Math.random() * 10);
mobList1.splice(rand, 1);
}
let hasLaser = spawn.pickList.includes("laser");
if (hasLaser) spawn.pickList.splice(spawn.pickList.indexOf("laser"), 1);
let mobList2 = [
[50, -1400],
[1600, -450],
[50, -450],
[1600, -1400]
];
for (let i = 0; i < 10; i++) mobList2.push([2800 + i * 500, -1800]);
while (mobList2.length && mob.length < -1 + 16 * Math.log10(simulation.difficulty + 1)) {
let rand = Math.floor(Math.random() * mobList2.length);
spawn.randomMob(...mobList2[rand]);
mobList2.splice(rand, 1);
}
let groupList = ["spawn.randomGroup(8250, 575);",
`spawn.randomGroup(3200, -3700);
if (simulation.difficulty > 15)
spawn.randomGroup(3500, -3700, 0.3);`,
"spawn.randomGroup(7800, -1800, 0.5);"
];
while (groupList.length > 0) {
let ind = Math.floor(Math.random() * groupList.length);
Function(groupList[ind])();
groupList.splice(ind, 1);
}
if (hasLaser) spawn.pickList.push("laser");
spawn.shieldingBoss(3900, -3200, 70);
let randomBoss = Math.floor(Math.random() * 2);
if (simulation.difficulty > 5) spawn[["shooterBoss", "launcherBoss"][randomBoss]](7500, -150, 100, false);
else spawn[["shooter", "launcher"][randomBoss]](7500, -150, 150);
spawn[["shooter", "launcher"][randomBoss]](8500, -150, 150);
// canvas stuff
function drawFlame(x, y, color = "#f81", angle = Math.PI / 2) {
ctx.beginPath();
ctx.moveTo(x, y);
ctx.strokeStyle = color;
ctx.lineWidth = 3;
for (let i = 0; i < 3; i++) {
let randAng = (Math.random() - 0.5) * 2 + angle;
randLen = 30 + Math.random() * 10;
x = x + Math.cos(randAng) * randLen;
y = y - Math.sin(randAng) * randLen;
ctx.lineTo(x, y);
}
ctx.stroke();
}
function drawProject(startPos, endPos1, endPos2, tValue, tValueM) {
ctx.strokeStyle = "#003";
ctx.fillStyle = "#0055aa" + ('0' + (tValue * 60 / tValueM).toString(16)).slice(-2);
let inter = (tValueM - tValue) / tValueM;
let endpos1i = endPos1.map((i, j) => (startPos[j] - i) * inter),
endpos2i = endPos2.map((i, j) => (startPos[j] - i) * inter);
ctx.beginPath();
ctx.moveTo(endPos1[0] + endpos1i[0], endPos1[1] + endpos1i[1]);
ctx.lineTo(...startPos);
ctx.lineTo(endPos2[0] + endpos2i[0], endPos1[1] + endpos1i[1]);
ctx.fill();
ctx.stroke();
ctx.beginPath();
ctx.moveTo(endPos1[0] + endpos1i[0], endPos1[1] + endpos1i[1]);
ctx.lineTo(...startPos);
ctx.lineTo(endPos1[0] + endpos1i[0], endPos2[1] + endpos2i[1]);
ctx.fill();
ctx.stroke();
ctx.beginPath();
ctx.moveTo(endPos1[0] + endpos1i[0], endPos2[1] + endpos2i[1]);
ctx.lineTo(...startPos);
ctx.lineTo(endPos2[0] + endpos2i[0], endPos2[1] + endpos2i[1]);
ctx.fill();
ctx.stroke();
ctx.beginPath();
ctx.moveTo(endPos2[0] + endpos2i[0], endPos2[1] + endpos2i[1]);
ctx.lineTo(...startPos);
ctx.lineTo(endPos2[0] + endpos2i[0], endPos1[1] + endpos1i[1]);
ctx.fill();
ctx.stroke();
if (tValue >= tValueM * 2 / 3) {
ctx.fillStyle = "#0055aa" + ('0' + Math.floor((tValue - tValueM * 2 / 3) * 6.25 * 60 / tValueM).toString(16)).slice(-2);
ctx.strokeStyle = "#000033" + ('0' + Math.floor((tValue - tValueM * 2 / 3) * 12.75 * 60 / tValueM).toString(16)).slice(-2);
ctx.fillRect(endPos1[0], endPos1[1], endPos2[0] - endPos1[0], endPos2[1] - endPos1[1]);
ctx.shadowColor = "#00aaaa" + ('0' + Math.floor((tValue - tValueM * 2 / 3) * 12.75 * 60 / tValueM).toString(16)).slice(-2);
ctx.shadowBlur = 10;
ctx.strokeRect(endPos1[0], endPos1[1], endPos2[0] - endPos1[0], endPos2[1] - endPos1[1]);
ctx.shadowBlur = 0;
ctx.shadowColor = "#0000";
}
}
},
run() {
simulation.makeTextLog(`<strong>run</strong> by <span class='color-var'>iNoobBoi</span>`);
addPartToMap = (len) => { //adds new map elements to the level while the level is already running //don't forget to run simulation.draw.setPaths() after you all the the elements so they show up visually
map[len].collisionFilter.category = cat.map;
map[len].collisionFilter.mask = cat.player | cat.map | cat.body | cat.bullet | cat.powerUp | cat.mob | cat.mobBullet;
Matter.Body.setStatic(map[len], true); //make static
Composite.add(engine.world, map[len]);
}
anotherBoss = (x, y) => {
if (tech.isDuplicateMobs && Math.random() < tech.duplicationChance()) {
tech.isScaleMobsWithDuplication = true
spawn.randomLevelBoss(x, y, ["historyBoss"]);
tech.isScaleMobsWithDuplication = false
} else if (tech.isResearchBoss) {
if (powerUps.research.count > 2) {
powerUps.research.changeRerolls(-3)
simulation.makeTextLog(`<span class='color-var'>m</span>.<span class='color-r'>research</span> <span class='color-symbol'>-=</span> 3<br>${powerUps.research.count}`)
} else {
tech.addJunkTechToPool(0.49)
}
spawn.randomLevelBoss(x, y, ["historyBoss"]);
}
}
const climbPad = level.boost(8200, -200, 500);
var climbTime = false;
var climbGroup = 0;
var initialSpawn = false;
var endTime = false;
let runMobList = [
"hopper",
"slasher",
"striker",
"stabber",
"springer",
"pulsar",
"sneaker",
"spinner",
"grower",
"focuser",
"spawner",
];
let removeList = [];
level.custom = () => {
level.exit.drawAndCheck();
level.enter.draw();
climbPad.query();
if (m.pos.x > 8000 && climbTime === false) {
spawn.mapRect(7800, -900, 200, 900);
addPartToMap(map.length - 1);
simulation.draw.setPaths();
simulation.makeTextLog(`<strong>UNKNOWN</strong>: "Well done. Now climb."`, 600);
simulation.makeTextLog(`<strong>UNKNOWN</strong>: "I left a gift at the top."`, 600);
climbTime = true;
} //toggles on a mapRect when player passes a certain area
if (m.pos.x > 9000 && endTime === false) {
simulation.makeTextLog("<strong>UNKNOWN</strong>: \"Good luck. I hope you get out of here.\"", 600);
endTime = true;
}
for (i in mob) {
mob[i].damageReduction = 0;
Matter.Body.setVelocity(mob[i], {
x: mob[i].velocity.x * 0.97,
y: mob[i].velocity.y * 0.97
});
} //makes everything slow and immune
};
level.customTopLayer = () => {
ctx.fillStyle = "#888";
if (climbGroup === 0) {
//toggle on fillRect: 1
ctx.fillRect(8000, -900, 300, 100);
ctx.fillRect(8500, -1800, 300, 100);
ctx.fillRect(8300, -2700, 300, 100);
ctx.fillRect(8000, -3600, 300, 100);
ctx.fillRect(8200, -4500, 300, 100);
} else if (climbGroup === 1) {
//toggle on fillRect: 2
ctx.fillRect(8300, -1200, 300, 100);
ctx.fillRect(8500, -2100, 300, 100);
ctx.fillRect(8100, -3000, 300, 100);
ctx.fillRect(8000, -3900, 300, 100);
ctx.fillRect(8200, -4800, 300, 100);
} else if (climbGroup === 2) {
//toggle on fillRect: 0
ctx.fillRect(8500, -600, 300, 100);
ctx.fillRect(8100, -1500, 300, 100);
ctx.fillRect(8000, -2400, 300, 100);
ctx.fillRect(8500, -3300, 300, 100);
ctx.fillRect(8500, -4200, 300, 100);
}
if ((simulation.cycle % 120) === 0) {
for (var i = 0; i < map.length; i++) {
if (map[i].isRemove) {
Matter.Composite.remove(engine.world, map[i]);
map.splice(i, 1);
}
}
if (climbGroup === 0) {
//toggle on platforms: 0
spawn.mapRect(8000, -900, 300, 100);
addPartToMap(map.length - 1);
map[map.length - 1].isRemove = true;
spawn.mapRect(8500, -1800, 300, 100);
addPartToMap(map.length - 1);
map[map.length - 1].isRemove = true;
spawn.mapRect(8300, -2700, 300, 100);
addPartToMap(map.length - 1);
map[map.length - 1].isRemove = true;
spawn.mapRect(8000, -3600, 300, 100);
addPartToMap(map.length - 1);
map[map.length - 1].isRemove = true;
spawn.mapRect(8200, -4500, 300, 100);
addPartToMap(map.length - 1);
map[map.length - 1].isRemove = true;
climbGroup = 1;
} else if (climbGroup === 1) {
//toggle on platforms: 1
spawn.mapRect(8300, -1200, 300, 100);
addPartToMap(map.length - 1);
map[map.length - 1].isRemove = true;
spawn.mapRect(8500, -2100, 300, 100);
addPartToMap(map.length - 1);
map[map.length - 1].isRemove = true;
spawn.mapRect(8100, -3000, 300, 100);
addPartToMap(map.length - 1);
map[map.length - 1].isRemove = true;
spawn.mapRect(8000, -3900, 300, 100);
addPartToMap(map.length - 1);
map[map.length - 1].isRemove = true;
spawn.mapRect(8200, -4800, 300, 100);
addPartToMap(map.length - 1);
map[map.length - 1].isRemove = true;
climbGroup = 2;
} else if (climbGroup === 2) {
//toggle on platforms: 2
spawn.mapRect(8500, -600, 300, 100);
addPartToMap(map.length - 1);
map[map.length - 1].isRemove = true;
spawn.mapRect(8100, -1500, 300, 100);
addPartToMap(map.length - 1);
map[map.length - 1].isRemove = true;
spawn.mapRect(8000, -2400, 300, 100);
addPartToMap(map.length - 1);
map[map.length - 1].isRemove = true;
spawn.mapRect(8500, -3300, 300, 100);
addPartToMap(map.length - 1);
map[map.length - 1].isRemove = true;
spawn.mapRect(8500, -4200, 300, 100);
addPartToMap(map.length - 1);
map[map.length - 1].isRemove = true;
climbGroup = 0;
}
simulation.draw.setPaths(); //update map graphics
} //every 120 cycles, first deletes previous group, then cycles through one of 3 toggle groups
};
if (!initialSpawn) {
level.defaultZoom = 1300 //was 800 I changed this
simulation.zoomTransition(level.defaultZoom)
document.body.style.backgroundColor = "#dcdcde";
//Level
level.setPosToSpawn(-100, -1450);
spawn.mapRect(level.enter.x, level.enter.y + 20, 100, 20);
level.exit.x = 9300;
level.exit.y = -5130;
spawn.mapRect(level.exit.x, level.exit.y + 20, 100, 20);
//Map
spawn.mapRect(-1400, -2200, 1700, 200);
spawn.mapRect(100, -2200, 200, 1000);
spawn.mapRect(-600, -1400, 8600, 200);
spawn.mapRect(-1400, -2200, 200, 1000);
spawn.mapRect(-2800, -1400, 1600, 200);
spawn.mapRect(-2800, -1400, 200, 1400);
spawn.mapRect(-2800, -200, 11800, 200);
spawn.mapRect(-900, -600, 600, 600);
spawn.mapRect(200, -800, 500, 100);
spawn.mapRect(1300, -1400, 200, 900);
spawn.mapRect(1300, -600, 500, 100);
spawn.mapRect(2300, -800, 200, 200);
spawn.mapRect(2900, -400, 100, 400);
spawn.mapRect(3200, -600, 100, 600);
spawn.mapRect(3500, -800, 100, 800);
spawn.mapRect(4400, -900, 500, 100);
spawn.mapRect(4400, -600, 500, 100);
spawn.mapRect(4800, -900, 100, 400);
spawn.mapRect(5300, -550, 600, 550);
spawn.mapRect(5600, -900, 300, 800);
spawn.mapRect(6300, -300, 1100, 300);
spawn.mapRect(6600, -400, 500, 200);
spawn.mapRect(6600, -800, 500, 100);
spawn.mapRect(7000, -1400, 100, 700);
spawn.mapRect(7800, -5900, 200, 5100);
spawn.mapRect(7800, -5900, 1900, 200);
spawn.mapRect(9500, -5900, 200, 1000);
spawn.mapRect(8800, -5100, 900, 200);
spawn.mapRect(8800, -5100, 200, 5100);
//Text
spawn.mapRect(400, -1600, 100, 10);
spawn.mapRect(400, -1600, 10, 100);
spawn.mapRect(490, -1600, 10, 40);
spawn.mapRect(400, -1570, 100, 10);
spawn.mapRect(400, -1540, 100, 10);
spawn.mapRect(490, -1540, 10, 40);
spawn.mapRect(600, -1600, 10, 100);
spawn.mapRect(600, -1510, 100, 10);
spawn.mapRect(690, -1600, 10, 100);
spawn.mapRect(800, -1600, 100, 10);
spawn.mapRect(800, -1600, 10, 100);
spawn.mapRect(890, -1600, 10, 100);
spawn.mapRect(0, 0, 1, 1); //dont ask why i have these
spawn.mapRect(1, 0, 1, 1); //dont ask why i have these
spawn.mapRect(0, 1, 1, 1); //dont ask why i have these
spawn.mapRect(1, 1, 1, 1); //dont ask why i have these
spawn.mapRect(-1, 0, 1, 1); //dont ask why i have these
spawn.mapRect(0, -1, 1, 1); //dont ask why i have these
spawn.mapRect(-1, -1, 1, 1); //dont ask why i have these
spawn.mapRect(1, -1, 1, 1); //dont ask why i have these
spawn.mapRect(-1, 1, 1, 1); //dont ask why i have these
//Mob Spawning
setTimeout(() => {
simulation.makeTextLog("<strong>UNKNOWN</strong>: \"You cannot kill them.\"", 600);
}, 2000);
setTimeout(() => {
simulation.makeTextLog("<strong>UNKNOWN</strong>: \"But I have slowed them down for you.\"", 600);
}, 6000);
spawn[runMobList[Math.floor(Math.random() * runMobList.length)]](200, -400);
spawn[runMobList[Math.floor(Math.random() * runMobList.length)]](1800, -1000);
spawn[runMobList[Math.floor(Math.random() * runMobList.length)]](3200, -1000);
spawn[runMobList[Math.floor(Math.random() * runMobList.length)]](6200, -400);
if (simulation.difficulty > 10) {
spawn[runMobList[Math.floor(Math.random() * runMobList.length)]](1000, -400);
spawn[runMobList[Math.floor(Math.random() * runMobList.length)]](2400, -400);
spawn[runMobList[Math.floor(Math.random() * runMobList.length)]](4000, -400);
spawn[runMobList[Math.floor(Math.random() * runMobList.length)]](6600, -1000);
setTimeout(() => {
simulation.makeTextLog("<strong>UNKNOWN</strong>: \"Run.\"", 600);
}, 10000);
} //some of the mobs
if (simulation.difficulty > 20) {
spawn[runMobList[Math.floor(Math.random() * runMobList.length)]](1000, -1000);
spawn[runMobList[Math.floor(Math.random() * runMobList.length)]](3100, -300);
spawn[runMobList[Math.floor(Math.random() * runMobList.length)]](4200, -1000);
spawn[runMobList[Math.floor(Math.random() * runMobList.length)]](7400, -800);
setTimeout(() => {
simulation.makeTextLog("<strong>UNKNOWN</strong>: \"RUN!\"", 600);
}, 11000);
} //most of the mobs
if (simulation.difficulty > 30) {
spawn[runMobList[Math.floor(Math.random() * runMobList.length)]](200, -1000);
spawn[runMobList[Math.floor(Math.random() * runMobList.length)]](3400, -300);
spawn[runMobList[Math.floor(Math.random() * runMobList.length)]](5200, -800);
spawn[runMobList[Math.floor(Math.random() * runMobList.length)]](7500, -300);
setTimeout(() => {
simulation.makeTextLog("<strong>UNKNOWN</strong>: \"GET OUT OF HERE.\"", 600);
}, 12000);
} //all the mobs
//Boss Spawning
if (simulation.difficulty > 5) {
spawn.randomLevelBoss(-2200, -700, ["powerUpBossBaby", "blockBoss", "revolutionBoss"]);
setTimeout(() => {
simulation.makeTextLog("<strong>UNKNOWN</strong>: \"They are coming for you.\"", 600);
}, 14000);
}
anotherBoss(-1800, -700); //custom second boss spawn
//Blocks
spawn.bodyRect(1300, -500, 200, 100);
spawn.bodyRect(1400, -500, 200, 100);
spawn.bodyRect(1500, -500, 200, 100);
spawn.bodyRect(5700, -1200, 100, 100);
spawn.bodyRect(5700, -1100, 100, 100);
spawn.bodyRect(5700, -1000, 100, 100);
spawn.bodyRect(6800, -700, 100, 100);
spawn.bodyRect(6800, -600, 100, 100);
spawn.bodyRect(6800, -500, 100, 100);
spawn.debris(4400, -300, 500, 16);
spawn.debris(3300, -600, 200, 6);
spawn.debris(3000, -500, 20, 6);
spawn.debris(2300, -300, 200, 6);
spawn.debris(200, -300, 500, 16);
//Powerups
if (simulation.difficulty > 10) {
powerUps.spawn(1600, -700, "tech");
}
powerUps.spawnRandomPowerUp(1700, -700);
// if (simulation.difficulty > 20) {
// powerUps.spawn(4600, -700, "tech");
// }
powerUps.spawnRandomPowerUp(4700, -700);
// if (simulation.difficulty > 30) {
// powerUps.spawn(6800, -1000, "tech");
// }
powerUps.spawnRandomPowerUp(6900, -1000);
powerUps.spawn(9200, -5400, "tech");
if (simulation.difficulty > 10) {
powerUps.spawn(9200, -5500, "tech");
}
// if (simulation.difficulty > 20) {
// powerUps.spawn(9200, -5600, "tech");
// }
// if (simulation.difficulty > 30) {
// powerUps.spawn(9200, -5700, "tech");
// }
powerUps.addResearchToLevel() //needs to run after mobs are spawned
initialSpawn == true;
}
},
islands() {
simulation.makeTextLog(`<strong>islands</strong> by <span class='color-var'>Richard0820</span>`);
const boost1 = level.boost(58500, -18264, 1300);
let portal2, portal3;
// const removeIndex1 = map.length - 1;
const drip1 = level.drip(59300, -18975, -18250, 100); // drip(x, yMin, yMax, period = 100, color = "hsla(160, 100%, 35%, 0.5)") {
const drip2 = level.drip(60000, -18953, -18250, 150);
const drip3 = level.drip(60905, -18652, -18250, 70);
const slimePit1 = level.hazard(58850, -18300, 2275, 100, 0.01); //hazard(x, y, width, height, damage = 0.003) spawn.mapRect(58850, -18300, 2275, 100);
const slimePit2 = level.hazard(74400, -18075, 350, 100, 0.01);
let isSpawnedBoss = false;
level.custom = () => {
level.exit.drawAndCheck();
boost1.query();
level.enter.draw();
drip1.draw();
drip2.draw();
drip3.draw();
// portal[2].query();
// portal[3].query();
// portal[0].draw();
// portal[1].draw();
// portal[2].draw();
// portal[3].draw();
portal2[2].query();
portal2[3].query();
portal2[0].draw();
portal2[1].draw();
portal2[2].draw();
portal2[3].draw();
portal3[2].query();
portal3[3].query();
portal3[0].draw();
portal3[1].draw();
portal3[2].draw();
portal3[3].draw();
};
level.customTopLayer = () => {
slimePit1.query();
slimePit2.query();
ctx.fillStyle = `rgba(68, 68, 68, ${Math.max(0.3, Math.min((-17650 - m.pos.y) / 100, 0.99))})`;
ctx.fillRect(58390, -17655, 1490, 740);
};
document.body.style.backgroundColor = "hsl(138, 3%, 74%)";
level.setPosToSpawn(57680, -18330);
level.exit.x = 76343;
level.exit.y = -18020;
spawn.mapRect(level.enter.x, level.enter.y + 20, 100, 30);
spawn.mapRect(level.exit.x, level.exit.y + 20, 100, 30);
level.defaultZoom = 2000;
simulation.zoomTransition(level.defaultZoom);
// spawn.setSpawnList = [
// "hopper",
// "slasher",
// "striker",
// "stabber",
// "springer",
// "pulsar",
// "sneaker",
// "spinner",
// "grower",
// "focuser",
// "spawner",
// ];
spawn.mapRect(57800, -18550, 50, 100);
spawn.mapRect(57500, -18550, 50, 275);
spawn.mapRect(66900, -18675, 300, 200);
spawn.mapRect(66925, -19050, 125, 225);
spawn.mapRect(67825, -16975, 125, 100);
spawn.mapRect(74900, -18075, 225, 100);
spawn.mapRect(73925, -18225, 150, 275);
spawn.mapRect(76200, -18325, 50, 125);
spawn.mapRect(76525, -18325, 75, 400);
spawn.mapRect(61325, -18350, 50, 25);
spawn.mapRect(61450, -18425, 50, 25);
spawn.mapRect(61475, -18450, 25, 25);
spawn.mapRect(58725, -18350, 125, 50);
spawn.mapRect(58675, -18275, 50, 75);
spawn.mapRect(58600, -18275, 75, 75);
spawn.mapRect(58675, -18325, 50, 50);
spawn.mapRect(58250, -16925, 1825, 1050);
spawn.mapRect(57500, -18200, 4475, 550);
spawn.mapRect(61500, -18475, 475, 275);
spawn.mapRect(62175, -18575, 325, 400);
spawn.mapRect(62900, -18850, 525, 375);
spawn.mapRect(63900, -18925, 450, 400);
spawn.mapRect(64725, -19000, 625, 500);
spawn.mapRect(65825, -19050, 675, 400);
spawn.mapRect(66800, -18950, 400, 400);
spawn.mapRect(68775, -18850, 525, 400);
spawn.mapRect(67375, -16900, 1800, 1450);
spawn.mapRect(67375, -17475, 325, 575);
spawn.mapRect(68900, -17500, 250, 500);
spawn.mapRect(69425, -17050, 500, 475);
spawn.mapRect(70400, -17150, 425, 175);
spawn.mapRect(71175, -17325, 450, 325);
spawn.mapRect(72000, -17425, 325, 300);
spawn.mapRect(72725, -17450, 350, 275);
spawn.mapRect(70050, -18800, 550, 350);
spawn.mapRect(67750, -19400, 375, 1200);
spawn.mapRect(67750, -18200, 1425, 700);
spawn.mapRect(66800, -18550, 575, 1650);
spawn.mapRect(66800, -16900, 575, 1450);
spawn.mapRect(67350, -18175, 250, 750);
spawn.mapRect(71050, -18450, 725, 275);
spawn.mapRect(72100, -18150, 475, 200);
spawn.mapRect(73325, -17975, 3275, 475);
spawn.mapRect(73175, -17775, 150, 300);
spawn.mapRect(72975, -17675, 225, 250);
spawn.mapRect(76200, -18325, 400, 75);
spawn.mapRect(76525, -18250, 75, 275);
spawn.mapRect(76200, -18250, 50, 50);
spawn.mapRect(57500, -17675, 900, 1800);
spawn.mapRect(59875, -17675, 1975, 1800);
spawn.mapRect(57550, -18275, 225, 75);
spawn.mapRect(61375, -18375, 50, 50);
spawn.mapRect(61100, -18350, 75, 50);
spawn.mapRect(61175, -18325, 50, 25);
spawn.mapRect(61850, -16525, 250, 175);
spawn.mapRect(57500, -18500, 50, 325);
spawn.mapRect(57500, -18550, 350, 50);
spawn.mapRect(57800, -18500, 50, 50);
spawn.mapRect(61275, -18325, 375, 125);
spawn.mapRect(61425, -18400, 200, 75);
spawn.mapRect(62125, -18575, 125, 75);
spawn.mapRect(62250, -18200, 175, 125);
spawn.mapRect(62850, -18725, 100, 75);
spawn.mapRect(63075, -18550, 225, 225);
spawn.mapRect(62800, -18275, 75, 75);
spawn.mapRect(62500, -18475, 75, 50);
spawn.mapRect(63825, -18900, 150, 50);
spawn.mapRect(63950, -18575, 150, 125);
spawn.mapRect(64200, -18550, 100, 250);
spawn.mapRect(64925, -18525, 200, 275);
spawn.mapRect(64625, -18425, 75, 125);
spawn.mapRect(65225, -18675, 150, 175);
spawn.mapRect(65350, -18950, 75, 100);
spawn.mapRect(65950, -18575, 75, 150);
spawn.mapRect(66000, -18725, 225, 175);
spawn.mapRect(66275, -18675, 75, 125);
spawn.mapRect(66275, -18550, 75, 75);
spawn.mapRect(66150, -18550, 100, 50);
spawn.mapRect(66225, -18875, 25, 150);
spawn.mapRect(66200, -18750, 75, 25);
spawn.mapRect(66925, -19100, 125, 150);
spawn.mapRect(66000, -19100, 75, 50);
spawn.mapRect(65000, -19075, 100, 75);
spawn.mapRect(66750, -18625, 100, 100);
spawn.mapRect(68050, -18500, 350, 350);
spawn.mapRect(68125, -18975, 150, 475);
spawn.mapRect(69850, -18675, 150, 200);
spawn.mapRect(70000, -18625, 150, 50);
spawn.mapRect(68850, -18575, 325, 225);
spawn.mapRect(69100, -18400, 75, 100);
spawn.mapRect(70150, -18525, 125, 200);
spawn.mapRect(70425, -18525, 125, 200);
spawn.mapRect(70250, -18350, 175, 225);
spawn.mapRect(70325, -18475, 50, 150);
spawn.mapRect(70275, -18450, 150, 150);
spawn.mapRect(71175, -18250, 525, 250);
spawn.mapRect(71050, -18200, 150, 375);
spawn.mapRect(70925, -18300, 200, 250);
spawn.mapRect(71425, -18525, 175, 150);
spawn.mapRect(70225, -18950, 275, 250);
spawn.mapRect(70475, -17050, 225, 175);
spawn.mapRect(70625, -17250, 100, 150);
spawn.mapRect(71300, -17150, 200, 350);
spawn.mapRect(71100, -17250, 125, 100);
spawn.mapRect(71550, -17400, 150, 150);
spawn.mapRect(67675, -17150, 225, 300);
spawn.mapRect(68225, -17000, 100, 125);
spawn.mapRect(67900, -16975, 375, 100);
spawn.mapRect(68275, -16950, 150, 50);
spawn.bodyRect(76200, -18200, 50, 200);
spawn.mapRect(76200, -18000, 50, 25);
spawn.bodyRect(57800, -18450, 50, 175);
spawn.mapRect(68725, -17600, 300, 250);
spawn.mapRect(68625, -17550, 175, 100);
spawn.mapRect(68850, -17400, 150, 125);
spawn.mapRect(69325, -16900, 200, 225);
spawn.mapRect(69575, -16625, 175, 275);
spawn.mapRect(69850, -16875, 250, 200);
spawn.mapRect(69875, -16650, 150, 300);
spawn.mapRect(69825, -16800, 375, 325);
spawn.mapRect(69650, -16775, 325, 475);
spawn.mapRect(71975, -17325, 100, 125);
spawn.mapRect(72075, -17200, 150, 150);
spawn.mapRect(72275, -17350, 150, 150);
spawn.mapRect(72325, -17275, 150, 225);
spawn.mapRect(72225, -18050, 200, 225);
spawn.mapRect(71925, -18150, 250, 175);
spawn.mapRect(72075, -18275, 125, 175);
spawn.mapRect(72500, -18025, 125, 175);
spawn.mapRect(72400, -17975, 150, 175);
spawn.mapRect(73925, -18225, 350, 275);
spawn.mapRect(74750, -18125, 275, 175);
spawn.mapRect(74250, -18100, 150, 75);
spawn.mapRect(74275, -18050, 200, 75);
spawn.mapRect(73750, -18100, 275, 125);
spawn.mapRect(73075, -17475, 3525, 300);
spawn.mapRect(73275, -17600, 3325, 225);
spawn.mapRect(57775, -18250, 150, 50);
spawn.mapRect(57775, -18275, 75, 25);
spawn.mapRect(57925, -18225, 50, 25);
spawn.debris(68300, -17000, 3700, 16);
spawn.mapRect(62000, -16525, 100, 200);
spawn.mapRect(59125, -19125, 325, 200);
spawn.mapRect(59925, -19175, 350, 225);
spawn.mapRect(60800, -18850, 275, 200);
spawn.mapRect(75025, -18075, 200, 100);
spawn.mapRect(75225, -18025, 100, 50);
spawn.bodyRect(74300, -18150, 50, 25);
spawn.bodyRect(73850, -18150, 75, 75);
spawn.bodyRect(74700, -18000, 75, 50);
spawn.bodyRect(74250, -18325, 25, 25);
spawn.bodyRect(74275, -18325, 25, 25);
spawn.bodyRect(74275, -18325, 25, 25);
spawn.bodyRect(74300, -18325, 100, 25);
// portal = level.portal(
// {
// x: 58625,
// y: -16925,
// },
// 1.5 * Math.PI,
// {
// //right
// x: 58625,
// y: -17650,
// },
// 2.5 * Math.PI
// ); //right
portal2 = level.portal({
x: 61920,
y: -16525,
},
1.5 * Math.PI, {
//right
x: 58400,
y: -17325,
},
2 * Math.PI
);
portal3 = level.portal({
x: 59865,
y: -17300,
},
3 * Math.PI, {
//right
x: 60820,
y: -31130,
},
2.5 * Math.PI
);
spawn.mapRect(60275, -32250, 975, 400);
spawn.mapRect(60375, -31925, 275, 225);
spawn.mapRect(61025, -31950, 175, 300);
spawn.mapRect(60825, -31725, 100, 350);
spawn.mapRect(60675, -31875, 200, 225);
spawn.mapRect(60225, -31950, 100, 725);
spawn.mapRect(60250, -31525, 250, 375);
spawn.mapRect(60675, -31475, 425, 350);
spawn.mapRect(60625, -32500, 225, 300);
spawn.mapRect(61025, -32325, 125, 175);
spawn.mapRect(60375, -32325, 175, 150);
spawn.mapRect(60250, -19075, 100, 100);
spawn.randomMob(59850, -18825, Infinity);
spawn.randomMob(62325, -18800, Infinity);
spawn.randomMob(61725, -18800, Infinity);
spawn.randomMob(63050, -19025, Infinity);
spawn.randomMob(64100, -19200, Infinity);
spawn.randomMob(64225, -19100, Infinity);
spawn.randomMob(64875, -19300, Infinity);
spawn.randomMob(65125, -19325, Infinity);
spawn.randomMob(65850, -19275, Infinity);
spawn.randomMob(66200, -19300, Infinity);
spawn.randomMob(65975, -19425, Infinity);
spawn.randomMob(67925, -19600, Infinity);
spawn.randomMob(66975, -19275, Infinity);
spawn.randomMob(67550, -18750, Infinity);
spawn.randomMob(69625, -17275, Infinity);
spawn.randomMob(70550, -17350, Infinity);
spawn.randomMob(71375, -17475, Infinity);
spawn.randomMob(72200, -17600, Infinity);
spawn.randomMob(73000, -18025, Infinity);
spawn.randomMob(73850, -18350, Infinity);
spawn.randomMob(75725, -18300, Infinity);
spawn.randomMob(75875, -18275, Infinity);
spawn.randomMob(75700, -18200, Infinity);
spawn.randomMob(75550, -18275, Infinity);
spawn.randomMob(75825, -18150, Infinity);
spawn.randomMob(75575, -18150, Infinity);
spawn.randomGroup(75575, -18150, 0);
level.chain(67250, -19325, 0, true, 14, 20);
spawn.mapRect(58725, -18300, 125, 100);
spawn.mapRect(61100, -18300, 175, 100);
spawn.mapRect(67175, -19375, 100, 100);
spawn.mapRect(59125, -19125, 325, 200);
spawn.mapRect(59925, -19175, 350, 225);
spawn.mapRect(60800, -18850, 275, 200);
spawn.mapRect(60850, -18725, 50, 200);
spawn.mapRect(60950, -18675, 50, 200);
spawn.mapRect(59975, -19025, 50, 250);
spawn.mapRect(60125, -19025, 50, 400);
spawn.mapRect(60075, -19025, 50, 450);
spawn.mapRect(59425, -19075, 100, 100);
spawn.mapRect(59175, -19000, 100, 225);
spawn.mapRect(59325, -19000, 75, 450);
spawn.mapRect(59050, -19000, 100, 100);
spawn.mapRect(61050, -18775, 100, 75);
spawn.mapRect(60725, -18850, 125, 125);
spawn.bodyRect(61850, -16525, 250, 175);
if (simulation.difficulty > 1) {
spawn.randomGroup(75575, -18150, 0);
spawn.randomLevelBoss(68450, -17300);
}
if (!isSpawnedBoss) {
isSpawnedBoss = true;
if (Math.random() < 0.33) {
for (
let i = 0, len = Math.min(simulation.difficulty / 20, 6); i < len;
++i
)
spawn.bounceBoss(59025, -17325, 50, false);
} else if (Math.random() < 0.5) {
for (
let i = 0, len = Math.min(simulation.difficulty / 9, 8); i < len;
++i
)
spawn.sprayBoss(59025, -17325, 50, false);
} else {
for (
let i = 0, len = Math.min(simulation.difficulty / 6, 10); i < len;
++i
)
spawn.mineBoss(59025, -17325, 50, false);
}
powerUps.spawn(59352, -17115, "tech");
// for (let i = 0, len = 3 + simulation.difficulty / 20; i < len; ++i) spawn.mantisBoss(1487 + 300 * i, -1525, 35, false)
}
simulation.fallHeight = -15000;
powerUps.addResearchToLevel();
powerUps.spawn(3000, -230, "heal");
// level.difficultyIncrease(60)
},
temple() {
simulation.makeTextLog(`<strong>temple</strong> by <span class='color-var'>Scar1337</span>`);
const V = Vector;
const Equation = (function () {
function Equation(a, b, c) {
this.a = a;
this.b = b;
this.c = c;
}
Equation.prototype.getXfromY = function (y) {
return (-this.b * y - this.c) / this.a;
}
Equation.prototype.getYfromX = function (x) {
return (-this.a * x - this.c) / this.b;
}
Equation.fromPoints = function (v1, v2) {
if (v1.x === v2.x) return new Equation(1, 0, -v1.x);
if (v1.y === v2.y) return new Equation(0, 1, -v1.y);
const d = (v2.y - v1.y) / (v2.x - v1.x);
return new Equation(-d, 1, d * v1.x - v1.y);
};
return Equation;
})();
const Rect = (function () {
function Rect(x, y, w, h) {
this.pos = {
x,
y
};
this.width = w;
this.height = h;
}
Rect.prototype.has = function ({
x,
y
}) {
return x >= this.pos.x && x <= this.pos.x + this.width &&
y >= this.pos.y && y <= this.pos.y + this.height;
}
Rect.prototype.hasLine = function (eq) {
const leftInter = eq.getYfromX(this.pos.x);
const rightInter = eq.getYfromX(this.pos.x + this.width);
const topInter = eq.getXfromY(this.pos.y);
return (leftInter >= this.pos.y && leftInter <= this.pos.y + this.height) ||
(rightInter >= this.pos.y && rightInter <= this.pos.y + this.height) ||
(topInter >= this.pos.x && topInter <= this.pos.x + this.width);
}
Rect.prototype.addToMap = function () {
spawn.mapRect(this.pos.x, this.pos.y, this.width, this.height);
}
Object.defineProperty(Rect.prototype, "midPos", {
get() {
return V.add(this.pos, {
x: this.width / 2,
y: this.height / 2
});
}
});
Rect.fromBounds = function (min, max) {
return new Rect(min.x, min.y, max.x - min.x, max.y - min.y);
}
Rect.prototype.isCollidingWith = function (other) {
const tc = {
p1: [this.pos.x, this.pos.y],
p2: [this.pos.x + this.width, this.pos.y + this.height]
};
const oc = {
p1: [other.pos.x, other.pos.y],
p2: [other.pos.x + other.width, other.pos.y + other.height]
};
// If one rectangle is on left side of other
if (tc.p1[0] >= oc.p2[0] || oc.p1[0] >= tc.p2[0])
return false;
// If one rectangle is above other
if (tc.p1[1] >= oc.p2[1] || oc.p1[1] >= tc.p2[1])
return false;
return true;
}
return Rect;
})();
function isInBound(bound) {
return bound.has(player.bounds.min) || bound.has(player.bounds.max);
}
function addWIMP(x, y) {
spawn.WIMP(x, y);
const me = mob[mob.length - 1];
me.isWIMP = true;
}
function relocateWIMPs(x, y) {
for (const i of mob) {
if (i.isWIMP) {
setPos(i, {
x: x + 300 * (Math.random() - 0.5),
y: y + 300 * (Math.random() - 0.5)
});
}
}
}
function secondRoomSuckerBoss(x, y, isDark = false, radius = 25) {
mobs.spawn(x, y, 12, radius, isDark ? "#000" : "#fff");
let me = mob[mob.length - 1];
me.isBoss = true;
me.isDark = isDark;
me.stroke = "transparent";
me.eventHorizon = 500; // How family friendly content much do I have to reduce this
me.seeAtDistance2 = 5e6; // Basically just see at all times, in the context it's given
me.accelMag = 0.00003 * simulation.accelScale;
me.collisionFilter.mask = cat.player | cat.bullet;
me.memory = 1600;
me.randomPRNGMult = Math.random() * 500;
me.attackCycle = 0;
me.lastAttackCycle = 0;
Matter.Body.setDensity(me, 0.012); // extra dense, normal is 0.001 // makes effective life much larger
me.onDeath = function () {
// applying forces to player doesn't seem to work inside this method, not sure why
powerUps.spawn(this.position.x + 20, this.position.y, "ammo");
if (Math.random() > 0.5) powerUps.spawn(this.position.x, this.position.y, "ammo");
if (Math.random() > 0.3) powerUps.spawn(this.position.x, this.position.y, "heal", true, null, 30 * (simulation.healScale ** 0.25) * Math.sqrt(tech.largerHeals) * Math.sqrt(0.1 + Math.random() * 0.5));
};
me.damageReduction = 0.25 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1);
me.do = function () {
// keep it slow, to stop issues from explosion knock backs
if (this.speed > 1) {
Matter.Body.setVelocity(this, {
x: this.velocity.x * 0.95,
y: this.velocity.y * 0.95
});
}
if (!(simulation.cycle % this.seePlayerFreq)) {
if (this.distanceToPlayer2() < this.seeAtDistance2) { // ignore cloak for black holes
this.locatePlayer();
if (!this.seePlayer.yes) this.seePlayer.yes = true;
} else if (this.seePlayer.recall) {
this.lostPlayer();
}
}
this.checkStatus();
if (this.seePlayer.recall) {
// accelerate towards the player
const forceMag = this.accelMag * this.mass;
const dx = this.seePlayer.position.x - this.position.x
const dy = this.seePlayer.position.y - this.position.y
const mag = Math.sqrt(dx * dx + dy * dy)
this.force.x += forceMag * dx / mag;
this.force.y += forceMag * dy / mag;
// eventHorizon waves in and out
const eventHorizon = this.eventHorizon * (1 + 0.2 * Math.sin(simulation.cycle * 0.008));
// draw darkness
ctx.fillStyle = this.isDark ? "rgba(0,20,40,0.6)" : "rgba(225,215,255,0.6)";
DrawTools.arc(this.position.x, this.position.y, eventHorizon * 0.2, 0, 2 * Math.PI);
ctx.fillStyle = this.isDark ? "rgba(0,20,40,0.4)" : "rgba(225,215,255,0.4)";
DrawTools.arc(this.position.x, this.position.y, eventHorizon * 0.4, 0, 2 * Math.PI);
ctx.fillStyle = this.isDark ? "rgba(0,20,40,0.3)" : "rgba(225,215,255,0.3)";
DrawTools.arc(this.position.x, this.position.y, eventHorizon * 0.6, 0, 2 * Math.PI);
ctx.fillStyle = this.isDark ? "rgba(0,20,40,0.2)" : "rgba(225,215,255,0.2)";
DrawTools.arc(this.position.x, this.position.y, eventHorizon * 0.8, 0, 2 * Math.PI);
ctx.fillStyle = this.isDark ? "rgba(0,0,0,0.05)" : "rgba(255,255,255,0.05)";
DrawTools.arc(this.position.x, this.position.y, eventHorizon, 0, 2 * Math.PI);
// when player is inside event horizon
if (distance(this.position, player.position) < eventHorizon) {
if (this.isDark) {
// Standard black hole stuff
if (m.immuneCycle < m.cycle) {
if (m.energy > 0) m.energy -= 0.003;
if (m.energy < 0.1) m.damage(0.00015 * simulation.dmgScale);
}
const angle = Math.atan2(player.position.y - this.position.y, player.position.x - this.position.x);
player.force.x -= 0.0005 * Math.cos(angle) * player.mass * (m.onGround ? 1.7 : 1);
player.force.y -= 0.0005 * Math.sin(angle) * player.mass;
// draw line to player
ctx.lineWidth = Math.min(60, this.radius * 2);
ctx.strokeStyle = "rgba(0,0,0,0.5)";
DrawTools.line([this.position, m.pos]);
ctx.fillStyle = "rgba(0,0,0,0.3)";
DrawTools.arc(m.pos.x, m.pos.y, 40, 0, 2 * Math.PI);
} else {
// Lightning attacks
this.attackCycle++;
if (this.attackCycle >= 30) {
this.attackCycle = 0;
this.lastAttackCycle = simulation.cycle;
Matter.Body.setVelocity(player, V.add(player.velocity, {
x: 0,
y: -10
}));
if (m.immuneCycle < m.cycle) {
if (m.energy > 0) m.energy -= 0.03;
m.damage(0.005 * simulation.dmgScale);
}
}
DrawTools.lightning(this.position, m.pos, this.lastAttackCycle, this.randomPRNGMult);
ctx.fillStyle = `rgba(255,240,127,${0.12 * Math.max(15 - simulation.cycle + this.lastAttackCycle, 0)})`;
DrawTools.arc(m.pos.x, m.pos.y, 40, 0, 2 * Math.PI);
}
}
}
}
};
function secondRoomPlacerBoss(x, y, isDark = false, size = 70) {
mobs.spawn(x, y, isDark ? 3 : 4, size, isDark ? "#0008" : "#fff8");
let me = mob[mob.length - 1];
me.isBoss = true;
me.isDark = isDark;
me.stroke = isDark ? "#000" : "#fff";
me.seeAtDistance2 = 5e6; // Basically just see at all times, in the context it's given
me.accelMag = 0.0001 * simulation.accelScale;
me.collisionFilter.mask = cat.player | cat.bullet;
me.memory = 1600;
me.randomPRNGMult = Math.random() * 500;
me.attackCycle = 0;
me.maxAttackCycle = isDark ? 90 : 240;
Matter.Body.setDensity(me, 0.006); // extra dense, normal is 0.001 // makes effective life much larger
me.onDeath = function () {
powerUps.spawn(this.position.x + 20, this.position.y, "ammo");
if (Math.random() > 0.5) powerUps.spawn(this.position.x, this.position.y, "ammo");
if (Math.random() > 0.3) powerUps.spawn(this.position.x, this.position.y, "heal", true, null, 30 * (simulation.healScale ** 0.25) * Math.sqrt(tech.largerHeals) * Math.sqrt(0.1 + Math.random() * 0.5));
};
me.damageReduction = 0.25 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1);
me.do = function () {
// keep it slow, to stop issues from explosion knock backs
if (this.speed > 2) {
Matter.Body.setVelocity(this, {
x: this.velocity.x * 0.95,
y: this.velocity.y * 0.95
});
}
if (!(simulation.cycle % this.seePlayerFreq)) {
if (this.distanceToPlayer2() < this.seeAtDistance2) { // ignore cloak
this.locatePlayer();
if (!this.seePlayer.yes) this.seePlayer.yes = true;
} else if (this.seePlayer.recall) {
this.lostPlayer();
}
}
this.checkStatus();
if (this.seePlayer.recall) {
// accelerate towards the player
const forceMag = this.accelMag * this.mass;
const dx = this.seePlayer.position.x - this.position.x
const dy = this.seePlayer.position.y - this.position.y
const mag = Math.sqrt(dx * dx + dy * dy)
this.force.x += forceMag * dx / mag;
this.force.y += forceMag * dy / mag;
this.attackCycle++;
if (this.attackCycle > this.maxAttackCycle) {
this.attackCycle = 0;
secondRoomObstacle(this.position.x, this.position.y, this.isDark, size);
}
}
}
};
function secondRoomObstacle(x, y, isDark = false, size = 70) {
mobs.spawn(x, y, isDark ? 3 : 4, size, isDark ? "#0004" : "#fff4");
let me = mob[mob.length - 1];
me.stroke = isDark ? "#000b" : "#fffb";
me.collisionFilter.mask = isDark ? cat.player | cat.bullet : 0;
me.isDropPowerUp = false;
me.showHealthBar = false;
me.leaveBody = false;
me.timeLeft = 1200;
me.isObstacle = true;
me.damageReduction = isDark ? 0.5 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1) : 0;
if (!isDark) {
me.isBadTarget = true;
me.attackCycle = 0;
me.maxAttackCycle = 10;
me.inertia = Infinity;
}
me.do = isDark ? function () {
Matter.Body.setVelocity(this, {
x: this.velocity.x * 0.95,
y: this.velocity.y * 0.95
});
} : function () {
Matter.Body.setVelocity(this, {
x: this.velocity.x * 0.95,
y: this.velocity.y * 0.95
});
if (Rect.fromBounds(this.bounds.min, this.bounds.max).isCollidingWith(Rect.fromBounds(player.bounds.min, player.bounds.max))) {
this.attackCycle++;
this.attackCycle = Math.min(this.attackCycle, 10);
} else {
this.attackCycle--;
this.attackCycle = Math.max(this.attackCycle, 0);
}
if (this.attackCycle > 0) {
ctx.beginPath();
const vertices = this.vertices;
ctx.moveTo(vertices[0].x, vertices[0].y);
for (let j = 1, len = vertices.length; j < len; ++j) ctx.lineTo(vertices[j].x, vertices[j].y);
ctx.lineTo(vertices[0].x, vertices[0].y);
if (this.attackCycle >= 10) {
ctx.shadowBlur = 10;
ctx.shadowColor = "rgb(255, 210, 64)";
}
ctx.fillStyle = `rgba(255, 210, 64, ${this.attackCycle / 15})`;
ctx.fill();
ctx.shadowBlur = 0;
if (this.attackCycle >= 10) {
DrawTools.lightning(this.position, m.pos, simulation.cycle);
m.damage(0.003 * simulation.dmgScale);
}
}
this.timeLimit();
}
}
function mobGrenade(...args) {
spawn.grenade(...args);
const pulseRadius = args[3] || Math.min(550, 250 + simulation.difficulty * 3)
let me = mob[mob.length - 1];
me.fill = "#ace";
me.damageReduction = 0;
me.onDeath = function () {
//damage player if in range
if (distance(player.position, this.position) < pulseRadius && m.immuneCycle < m.cycle) {
m.immuneCycle = m.cycle + m.collisionImmuneCycles; //player is immune to damage
m.damage(0.02 * simulation.dmgScale);
}
simulation.drawList.push({ //add dmg to draw queue
x: this.position.x,
y: this.position.y,
radius: pulseRadius,
color: "rgba(170,204,238,0.3)",
time: simulation.drawTime
});
};
me.do = function () {
this.timeLimit();
ctx.beginPath(); //draw explosion outline
ctx.arc(this.position.x, this.position.y, pulseRadius * (1.01 - this.timeLeft / this.lifeSpan), 0, 2 * Math.PI); //* this.fireCycle / this.fireDelay
ctx.fillStyle = "rgba(170,204,238,0.1)";
ctx.fill();
};
}
// Todo: nerf ThirdRoomBoss a bit?
function thirdRoomBoss(x, y) {
mobs.spawn(x, y, 6, 60, "#000");
let me = mob[mob.length - 1];
// Fix in place
me.constraint = Constraint.create({
pointA: {
x: me.position.x,
y: me.position.y
},
bodyB: me,
stiffness: 1,
damping: 1
});
Composite.add(engine.world, me.constraint);
me.isBoss = true;
me.stroke = "transparent";
me.eventHorizon = 900;
me.collisionFilter.mask = cat.player | cat.bullet | cat.body;
me.memory = Infinity;
me.attackCycle = 0;
me.lastAttackCycle = 0;
me.spawnCycle = 0;
Matter.Body.setDensity(me, 0.08); //extra dense //normal is 0.001 //makes effective life much larger
me.onDeath = function () {
for (let j = 0; j < 8; j++) { //in case some mobs leave things after they die
for (let i = 0, len = mob.length; i < len; ++i) {
if (mob[i] !== this) {
if (mob[i].isInvulnerable) { //disable invulnerability
mob[i].isInvulnerable = false
mob[i].damageReduction = 1
}
mob[i].damage(Infinity, true);
}
}
}
// You earned it: One more tech
powerUps.spawn(this.position.x, this.position.y, "tech");
powerUps.spawnBossPowerUp(this.position.x, this.position.y);
templePlayer.room3ToEndAnim = 1;
};
me.nextHealthThreshold = 0.75;
me.trapCycle = 0;
me.onDamage = function () {
if (this.health < this.nextHealthThreshold) {
this.health = this.nextHealthThreshold - 0.01
this.nextHealthThreshold = Math.floor(this.health * 4) / 4 //0.75,0.5,0.25
this.trapCycle = 1;
this.isInvulnerable = true;
this.damageReduction = 0;
}
};
me.damageReduction = 0.25 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1);
me.rings = [{
colour: "#65f",
radius: 300,
id: 0
}, {
colour: "#0f0",
radius: 400,
id: 1
}, {
colour: "#f00",
radius: 500,
id: 2
}];
me.ring = function () {
if (this.isInvulnerable) return;
ctx.lineWidth = 10;
for (const ring of this.rings) {
const radius = ring.radius * (1 + 0.3 * Math.sin(simulation.cycle / 60 * (ring.id + 2)));
if (Math.abs(distance(player.position, this.position) - radius) < 60 && m.immuneCycle < simulation.cycle) {
m.damage(0.4 / radius);
}
ctx.strokeStyle = ring.colour;
DrawTools.arcOut(this.position.x, this.position.y, radius, 0, Math.PI * 2);
}
}
me.horizon = function () {
if (this.isInvulnerable) return this.fill = "#f00";
// eventHorizon waves in and out
const eventHorizon = this.eventHorizon * (1 + 0.2 * Math.sin(simulation.cycle * 0.008));
const charge = this.attackCycle / 90;
this.fill = `rgb(${charge * 255},${charge * 255},${charge * 255})`;
// draw darkness
ctx.fillStyle = `rgba(${charge * 225},${20 + charge * 195},${40 + charge * 215},0.6)`;
DrawTools.arc(this.position.x, this.position.y, eventHorizon * 0.2, 0, 2 * Math.PI);
ctx.fillStyle = `rgba(${charge * 225},${20 + charge * 195},${40 + charge * 215},0.4)`;
DrawTools.arc(this.position.x, this.position.y, eventHorizon * 0.4, 0, 2 * Math.PI);
ctx.fillStyle = `rgba(${charge * 225},${20 + charge * 195},${40 + charge * 215},0.3)`;
DrawTools.arc(this.position.x, this.position.y, eventHorizon * 0.6, 0, 2 * Math.PI);
ctx.fillStyle = `rgba(${charge * 225},${20 + charge * 195},${40 + charge * 215},0.2)`;
DrawTools.arc(this.position.x, this.position.y, eventHorizon * 0.8, 0, 2 * Math.PI);
ctx.fillStyle = `rgba(${charge * 255},${charge * 255},${charge * 255},0.05)`;
DrawTools.arc(this.position.x, this.position.y, eventHorizon, 0, 2 * Math.PI);
// when player is inside event horizon
if (V.magnitude(V.sub(this.position, player.position)) < eventHorizon) {
// Standard black hole stuff
if (m.immuneCycle < m.cycle) {
if (m.energy > 0) m.energy -= 0.004;
if (m.energy < 0.1) m.damage(0.0002 * simulation.dmgScale);
}
const angle = Math.atan2(player.position.y - this.position.y, player.position.x - this.position.x);
player.force.x -= 0.001 * Math.cos(angle) * player.mass * (m.onGround ? 1.7 : 1);
player.force.y -= 0.001 * Math.sin(angle) * player.mass;
// draw line to player
ctx.lineWidth = Math.min(60, this.radius * 2);
ctx.strokeStyle = "rgba(0,0,0,0.5)";
DrawTools.line([this.position, m.pos]);
ctx.fillStyle = "rgba(0,0,0,0.3)";
DrawTools.arc(m.pos.x, m.pos.y, 40, 0, 2 * Math.PI);
// Lightning attacks
this.attackCycle++;
if (this.attackCycle >= 90) {
this.attackCycle = 0;
this.lastAttackCycle = simulation.cycle;
Matter.Body.setVelocity(player, V.add(player.velocity, {
x: 0,
y: -20
}));
if (m.immuneCycle < m.cycle) {
m.damage(0.012 * simulation.dmgScale);
}
}
const lightningCycle = simulation.cycle * 2 / 3 + this.lastAttackCycle / 3;
DrawTools.lightning(this.position, m.pos, lightningCycle, 1, 12);
DrawTools.lightning(this.position, m.pos, lightningCycle, 2, 12);
ctx.fillStyle = `rgba(255,240,127,${0.12 * Math.max(15 - simulation.cycle + this.lastAttackCycle, 0)})`;
DrawTools.arc(m.pos.x, m.pos.y, 40, 0, 2 * Math.PI);
}
}
me.periodicSpawns = function () {
if (this.isInvulnerable) return;
this.spawnCycle++;
// Spawn annoying purple thing(s) that chases the player
if (!(this.spawnCycle % 180)) {
spawn.seeker(this.position.x, this.position.y, 15 * (0.7 + 0.5 * Math.random()), 7);
spawn.seeker(this.position.x, this.position.y, 4 * (0.7 + 0.5 * Math.random()), 7);
spawn.seeker(this.position.x, this.position.y, 4 * (0.7 + 0.5 * Math.random()), 7);
}
// Spawn the annoying pink (now blue) exploder that doesn't chase the player
if (!(this.spawnCycle % 300)) {
for (let i = 0; i < 3; i++) {
mobGrenade(1100 + 700 * i, -13030, undefined, Math.min(700, 300 + simulation.difficulty * 4), 10);
setVel(mob[mob.length - 1], {
x: 0,
y: -10
});
mobGrenade(1100 + 700 * i, -14370, undefined, Math.min(700, 300 + simulation.difficulty * 4), 10);
setVel(mob[mob.length - 1], {
x: 0,
y: 10
});
}
}
// Spawn a bunch of mobs
if (simulation.difficulty > 10 && !(this.spawnCycle % 600)) {
// This is just ripped off of spawn.nodeGroup because I don't want the shield
const nodes = 3;
const radius = Math.ceil(Math.random() * 10) + 18;
const sideLength = Math.ceil(Math.random() * 100) + 70;
const stiffness = Math.random() * 0.03 + 0.005;
spawn.allowShields = false; //don't want shields on individual group mobs
const angle = 2 * Math.PI / nodes
for (let i = 0; i < nodes; ++i) {
spawn.focuser(x + sideLength * Math.sin(i * angle), y + sideLength * Math.cos(i * angle), radius);
}
spawn.constrainAllMobCombos(nodes, stiffness);
spawn.allowShields = true;
}
}
me.invulnerableTrap = function () {
if (this.trapCycle < 1) return;
this.trapCycle++;
// 24 is just an arbitrarily large number
const spawnCycles = Math.min(24, Math.max(6, 4 + Math.floor(simulation.difficulty / 3)));
// I have no idea how to balance at all, please help me
const spawnDelay = Math.floor(5 + 10 / (1 + Math.sqrt(simulation.difficulty) / 5));
if (this.trapCycle >= 90) {
const cycle = this.trapCycle - 90;
if (!(cycle % spawnDelay)) {
const radius = Math.min(500, 200 + simulation.difficulty * 3);
mobGrenade(600, -13050, 30, radius);
Matter.Body.setVelocity(mob[mob.length - 1], {
x: 35,
y: 0
});
mobGrenade(3000, -13050, 30, radius);
Matter.Body.setVelocity(mob[mob.length - 1], {
x: -35,
y: 0
});
mobGrenade(600, -14350, 30, radius);
Matter.Body.setVelocity(mob[mob.length - 1], {
x: 35,
y: 0
});
mobGrenade(3000, -14350, 30, radius);
Matter.Body.setVelocity(mob[mob.length - 1], {
x: -35,
y: 0
});
if (Math.floor(cycle / spawnDelay) >= spawnCycles - 1) {
this.trapCycle = 0;
this.isInvulnerable = false;
this.damageReduction = 0.25 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1);
}
}
}
ctx.font = "100px Arial";
ctx.fillStyle = "#f00";
ctx.shadowBlur = 10;
ctx.shadowColor = "#f00";
ctx.textAlign = "center";
ctx.textBaseLine = "middle";
ctx.fillText("!", 900, -13050);
ctx.fillText("!", 900, -14350);
ctx.fillText("!", 1800, -13050);
ctx.fillText("!", 1800, -14350);
ctx.fillText("!", 2700, -13050);
ctx.fillText("!", 2700, -14350);
ctx.shadowBlur = 0;
}
me.do = function () {
this.checkStatus();
this.horizon();
this.ring();
this.periodicSpawns();
this.invulnerableTrap();
}
};
let oldNextLevel = level.nextLevel;
const oldFallHeight = simulation.fallHeight;
level.nextLevel = () => {
color.map = "#444";
m.death = m.oldDeath;
canvas.style.filter = "";
level.nextLevel = oldNextLevel;
simulation.fallHeight = oldFallHeight;
oldNextLevel();
}
let bounds = [];
let mobPositionsQueue = Array.from(Array(10), () => []);
m.oldDeath = m.death;
m.death = function () {
if (!tech.isImmortal) {
requestAnimationFrame(() => color.map = "#444");
m.death = m.oldDeath;
canvas.style.filter = "";
level.nextLevel = oldNextLevel;
simulation.fallHeight = oldFallHeight;
m.oldDeath();
} else {
m.switchWorlds();
Matter.Body.setPosition(player, {
x: level.enter.x + 50,
y: level.enter.y - 20
});
makeLore("How did you not die?");
setTimeout(() => makeLore("I'll let you off this one time."), 2000);
tech.isImmortal = false;
}
}
let name = "⥟ᘊ5⪊Ыᳪៗⱕ␥ዘᑧ⍗";
addPartToMap = (len = map.length - 1) => {
map[len].collisionFilter.category = cat.map;
map[len].collisionFilter.mask = cat.player | cat.map | cat.body | cat.bullet | cat.powerUp | cat.mob | cat.mobBullet;
Matter.Body.setStatic(map[len], true); // make static
Composite.add(engine.world, map[len]);
}
level.setPosToSpawn(50, -50); // normal spawn
// Make the level exit really far away so WIMP powerups don't show up at all
level.exit.x = 1e6;
level.exit.y = -1e6;
Promise.resolve().then(() => {
// Clear all WIMPS and their research
for (let i = 0; i < mob.length; i++) {
while (mob[i] && !mob[i].isMACHO) {
mob[i].replace(i);
}
}
for (let i = 0; i < powerUp.length; i++) {
while (powerUp[i] && powerUp[i].name === "research") {
Matter.Composite.remove(engine.world, powerUp[i]);
powerUp.splice(i, 1);
}
}
level.exit.x = 1500;
level.exit.y = -30;
});
spawn.mapRect(level.enter.x, level.enter.y + 20, 100, 20);
spawn.mapRect(1500, -10, 100, 20);
level.defaultZoom = 1800
simulation.setZoom(1200);
document.body.style.backgroundColor = "#daa69f";
color.map = "#600";
function box(x, y, w, h, s) {
spawn.mapRect(x, y, w, s);
spawn.mapRect(x, y, s, h);
spawn.mapRect(x + w - s, y, s, h);
spawn.mapRect(x, y + h - s, w, s);
}
function diamond(x, y, s) {
spawn.mapVertex(x, y, `0 -${s} -${s} 0 0 ${s} ${s} 0`);
}
// Fake level
bounds.push(new Rect(-200, -500, 2000, 600));
box(-200, -500, 2000, 600, 100);
// Actual level, Room 1
const firstRoomBounds = new Rect(-200, -4000, 5000, 2100);
bounds.push(firstRoomBounds);
box(-200, -4000, 5000, 2100, 100);
spawn.mapRect(-200, -2500, 1300, 100);
spawn.mapRect(3500, -2500, 1300, 100);
spawn.mapRect(-200, -4000, 1000, 1600);
spawn.mapRect(3800, -4000, 1000, 1600);
// Enter and Exit platforms
spawn.mapRect(0, -2010, 100, 20);
spawn.mapRect(4500, -2010, 100, 20);
// Altar of Room 1
spawn.mapRect(2100, -2200, 100, 300);
spawn.mapRect(2400, -2200, 100, 300);
spawn.mapRect(2070, -2200, 460, 20);
spawn.debris(1700, -2100, 300, 10);
spawn.debris(2500, -2100, 300, 10);
// Actual level, Room 2
const secondRoomBounds = new Rect(-1500, -10500, 3000, 3600);
bounds.push(secondRoomBounds);
box(-1500, -10500, 3000, 3600, 100);
spawn.mapRect(-2000, -8500, 1600, 1600);
spawn.mapRect(400, -8500, 1600, 1600);
// Enter and Exit platforms
spawn.mapRect(-50, -7010, 100, 20);
spawn.mapRect(-50, -10010, 100, 20);
// Hazard traversing
spawn.mapRect(-300, -7320, 800, 20);
spawn.mapRect(175, -7600, 325, 20);
spawn.mapRect(200, -7775, 300, 20);
spawn.mapRect(-500, -7600, 325, 20);
spawn.mapRect(-500, -7775, 300, 20);
spawn.mapRect(-500, -7950, 800, 20);
spawn.mapRect(-300, -8100, 800, 20);
spawn.mapRect(-500, -8250, 800, 20);
for (let i = 0; i < 2; i++) spawn.mapRect(-250, -8400 + 150 * i, 500, 60);
const room2SlimePit = level.hazard(-400, -8410, 800, 1090);
room2SlimePit.logic = function () {
if (this.height > 0 && Matter.Query.region([player], this).length) {
if (m.immuneCycle < m.cycle) {
// Trolled
const hasCPT = tech.isRewindAvoidDeath;
tech.isRewindAvoidDeath = false;
const DRAIN = 0.002 * (tech.isRadioactiveResistance ? 0.25 : 1) + 0.001;
if (m.energy > DRAIN && !tech.isEnergyHealth) {
m.energy -= DRAIN;
}
m.damage(0.00015 * (tech.isRadioactiveResistance ? 0.25 : 1));
if (tech.isEnergyHealth) {
const previousEnergy = m.energy;
m.regenEnergy();
const energyRegenerated = m.energy - previousEnergy;
if (energyRegenerated > 0) {
m.energy = previousEnergy;
m.damage(energyRegenerated);
}
}
tech.isRewindAvoidDeath = hasCPT;
}
player.force.y -= 0.3 * player.mass * simulation.g;
setVel(player, Vector.sub(player.velocity, {
x: 0,
y: player.velocity.y * 0.02
}));
}
// Float power ups
powerUpCollide = Matter.Query.region(powerUp, this)
for (let i = 0, len = powerUpCollide.length; i < len; i++) {
const diameter = 2 * powerUpCollide[i].size
const buoyancy = 1 - 0.2 * Math.max(0, Math.min(diameter, this.min.y - powerUpCollide[i].position.y + powerUpCollide[i].size)) / diameter
powerUpCollide[i].force.y -= buoyancy * 1.14 * powerUpCollide[i].mass * simulation.g;
setVel(powerUpCollide[i], {
x: powerUpCollide[i].velocity.x,
y: 0.96 * powerUpCollide[i].velocity.y
});
}
}
room2SlimePit.draw = function () {
if (this.isOn) {
ctx.fillStyle = "hsla(160, 100%, 35%, 0.75)";
ctx.fillRect(this.min.x, this.min.y, this.width, this.height);
}
}
// Room 2 spawning bounds
const secondRoomSpawnBounds = new Rect(-1500, -10500, 3000, 2000);
spawn.mapRect(-700, -8700, 150, 20);
spawn.mapRect(550, -8700, 150, 20);
spawn.mapRect(-400, -8900, 800, 20);
diamond(-600, -9800, 30);
diamond(0, -9800, 30);
diamond(600, -9800, 30);
spawn.mapRect(-1000, -10000, 2000, 30);
// Actual level, Room 3 (Final Boss?)
const thirdRoomBounds = new Rect(-200, -14500, 4000, 1600);
bounds.push(thirdRoomBounds);
box(-200, -14500, 4000, 1600, 100);
spawn.mapRect(-200, -14500, 800, 1100);
spawn.mapRect(3000, -14500, 800, 1100);
// Enter and Exit platforms
spawn.mapRect(0, -13110, 100, 20);
spawn.mapRect(-200, -13100, 800, 200);
spawn.mapRect(3500, -13110, 100, 20);
spawn.mapRect(3000, -13100, 800, 200);
for (let i = 0; i < 4; i++) spawn.bodyRect(570, -13400 + i * 75, 30, 75);
for (let i = 0; i < 3; i++) {
diamond(1100 + 700 * i, -13000, 30, 30);
diamond(1100 + 700 * i, -14400, 30, 30);
}
const Objects = {
altar: {
get isHeal() {
return simulation.cycle % 600 >= 300;
},
pos: {
x: 2300,
y: -2200
},
get isActive() {
const roughPlayerCentre = V.add(m.pos, {
x: 0,
y: 40
});
return distance(roughPlayerCentre, this.pos) < 240 &&
(Math.abs(angle(roughPlayerCentre, this.pos) - Math.PI / 2) < 1);
},
logic() {
if (!this.isActive) return;
if (this.isHeal) {
m.energy += 0.005;
} else {
m.energy = Math.max(m.energy - 0.006, 0);
if (m.energy <= 0.01 && m.immuneCycle < m.cycle) m.damage(0.002);
}
},
drawTop() {
if (!isInBound(firstRoomBounds)) return;
const colour = this.isHeal ? m.fieldMeterColor : "#f00";
DrawTools.flame([2300, -2200, 26, 4, colour], 7);
ctx.fillStyle = colour;
ctx.fillRect(2200, -2200, 200, 200);
},
drawBottom() {
ctx.fillStyle = this.isHeal ? "#fff5" : "#0005";
for (const radius of [230, 180, 130, 80, 30]) {
DrawTools.arc(2300, -2200, radius, 0, Math.PI, true);
}
}
},
room2Initiator: {
pos: {
x: 0,
y: -9050
},
get distance() {
return distance(player.position, this.pos);
},
range: 120,
rings: [{
colour: [102, 85, 255],
radius: 200
}, {
colour: [0, 255, 0],
radius: 300
}, {
colour: [255, 0, 0],
radius: 400
}],
get ringNumber() {
return this.rings.length;
},
get cap() {
return (this.ringNumber + 1) * 90 + 240;
},
get capped() {
return templePlayer.room2.spawnInitiatorCycles > this.cap;
},
logic() {
if (this.distance < this.range) {
templePlayer.room2.spawnInitiatorCycles++;
}
},
draw() {
Promise.resolve().then(() => {
const cycle = templePlayer.room2.spawnInitiatorCycles;
if (!this.capped && this.distance < 400) {
ctx.fillStyle = `rgba(0, 0, 0, ${Math.min(1, (400 - this.distance) / (400 - this.range)) * 0.9})`;
ctx.fillRect(0, 0, canvas.width, canvas.height);
}
ctx.save();
simulation.camera();
if (this.distance < this.range && !this.capped) {
DrawTools.lightning(V.sub(this.pos, {
x: 300,
y: 300
}), V.add(this.pos, {
x: 300,
y: 300
}), simulation.cycle - 5);
DrawTools.lightning(V.add(this.pos, {
x: -300,
y: 300
}), V.add(this.pos, {
x: 300,
y: -300
}), simulation.cycle - 5);
}
if (!this.capped && cycle >= this.cap - 200) {
const multCoeff = (cycle - this.cap + 200) * 0.4
ctx.translate((Math.random() - 0.5) * multCoeff, (Math.random() - 0.5) * multCoeff);
}
ctx.shadowBlur = 20;
ctx.lineWidth = 12;
ctx.strokeStyle = (templePlayer.room2.cycles % 60 < 30) ? "#fff" : "#000";
ctx.shadowColor = (templePlayer.room2.cycles % 60 < 30) ? "#fff" : "#000";
DrawTools.arcOut(this.pos.x, this.pos.y, 100, 0, Math.PI * 2);
if (templePlayer.room2.cycles <= 100) {
for (let i = 0; i < this.ringNumber; i++) {
if (cycle < i * 90 + 90) break;
const ring = this.rings[i];
ctx.shadowColor = `rgb(${ring.colour.join(",")})`;
const opacity = this.capped ? 1 - 0.01 * templePlayer.room2.cycles : (cycle / 180 - i / 2 - 0.5);
ctx.strokeStyle = `rgba(${ring.colour.join(",")}, ${Math.min(opacity, 1)})`;
const radius = (this.capped ? 1 + 0.07 * templePlayer.room2.cycles : Math.sin(Math.min(cycle - i * 90 - 90, 45) / 90 * Math.PI)) * ring.radius;
DrawTools.arcOut(this.pos.x, this.pos.y, radius, 0, Math.PI * 2);
}
}
ctx.restore();
});
}
},
room2Lightning: {
one: [{
x: -1400,
y: -10400
}, {
x: 1400,
y: -8500
}],
two: [{
x: -1400,
y: -8500
}, {
x: 1400,
y: -10400
}],
get isHeal() {
return simulation.cycle % 360 < 180;
},
get oneEq() {
return Equation.fromPoints(this.one[0], this.one[1]);
},
get twoEq() {
return Equation.fromPoints(this.two[0], this.two[1]);
},
logic() {
if (!isInBound(secondRoomSpawnBounds) || !templePlayer.room2.cycles) return;
const playerbounds = Rect.fromBounds(player.bounds.min, player.bounds.max);
if (playerbounds.hasLine(this.oneEq) || playerbounds.hasLine(this.twoEq)) {
if (this.isHeal) {
m.energy += 0.003;
} else if (m.immuneCycle < m.cycle) {
m.energy -= 0.003;
}
}
},
draw() {
if (!isInBound(secondRoomBounds) || !templePlayer.room2.cycles) return;
const colour = this.isHeal ? undefined : [0, 0, 0];
DrawTools.lightning(...this.one, Math.floor(simulation.cycle / 15) * 15, 1, 9, colour);
DrawTools.lightning(...this.two, Math.floor(simulation.cycle / 15) * 15, 2, 9, colour);
}
},
room2GeneratedPath: {
rects: (function () {
const rects = [];
for (let i = 0; i < 4; i++) {
rects.push(new Rect(-1405 + (i & 1) * 200, -9700 + i * 300, 205, 30));
rects.push(new Rect(1200 - (i & 1) * 200, -9700 + i * 300, 205, 30));
}
return rects;
})(),
logic() {
if (templePlayer.room2.readyPathCycle && simulation.cycle - templePlayer.room2.readyPathCycle === 180) {
for (const r of this.rects) {
r.addToMap();
addPartToMap();
simulation.draw.setPaths();
}
}
},
draw() {
if (templePlayer.room2.readyPathCycle && simulation.cycle - templePlayer.room2.readyPathCycle < 180) {
ctx.fillStyle = "#fe79";
for (const r of this.rects) {
ctx.fillRect(r.pos.x, r.pos.y, r.width, r.height);
}
} else if (simulation.cycle - templePlayer.room2.readyPathCycle < 195) {
for (const r of this.rects) {
DrawTools.lightning(Objects.room2Initiator.pos, r.midPos, templePlayer.room2.readyPathCycle + 180);
}
}
}
},
room3Rotors: {
rotor1: (function () {
const rotor = level.spinner(900, -13700, 200, 30);
rotor.rotate = function () {
Matter.Body.setAngularVelocity(this.bodyB, (this.bodyB.angularVelocity + 0.01) * 0.9)
}
return rotor;
})(),
rotor2: (function () {
const rotor = level.spinner(2700, -13700, 200, 30);
rotor.rotate = function () {
Matter.Body.setAngularVelocity(this.bodyB, (this.bodyB.angularVelocity - 0.01) * 0.9)
}
return rotor;
})(),
logic() {
this.rotor1.rotate();
this.rotor2.rotate();
}
},
room3SlimePits: {
pit1: level.hazard(-100, -13400, 0, 0, 0.004),
pit2: level.hazard(3700, -13400, 0, 0, 0.004),
logic() {
if (templePlayer.room2ToRoom3Anim >= 1320 && templePlayer.room2ToRoom3Anim <= 1570) {
this.pit1.height = this.pit2.height = 300;
this.pit1.max.y = this.pit2.max.y = -13100;
this.pit1.width = this.pit2.width = templePlayer.room2ToRoom3Anim * 2 - 2640;
this.pit1.max.x = this.pit1.min.x + this.pit1.width;
this.pit2.min.x = this.pit2.max.x - this.pit2.width;
}
if (templePlayer.room3ToEndAnim) {
this.pit1.height = this.pit1.width = 0;
this.pit2.height = this.pit2.width = 0;
}
},
draw() {
this.pit1.query();
this.pit2.query();
}
}
};
let templePlayer = {
room1: {
cycles: 300
},
room2: {
spawnInitiatorCycles: 0,
cycles: 0,
readyPathCycle: 0
},
stage: 1,
startAnim: 0,
room1ToRoom2Anim: 0,
room2ToRoom3Anim: 0,
room3ToEndAnim: 0,
initialTrapY: 0,
clearedCycle: 0,
drawExit: true
};
const RoomTransitionHandler = {
room0() {
if (templePlayer.startAnim <= 0) return;
templePlayer.startAnim++;
if (templePlayer.startAnim == 120) {
makeLore("Not so fast.");
}
if (templePlayer.startAnim < 360) {
trapPlayer(1000, templePlayer.initialTrapY);
} else {
level.exit.x = 4500;
level.exit.y = -2030;
relocateTo(50, -2050);
simulation.fallHeight = -1000;
simulation.setZoom(1800);
templePlayer.startAnim = -1;
templePlayer.drawExit = false;
}
},
room1() {
if (templePlayer.room1ToRoom2Anim <= 0) return;
if (templePlayer.room1ToRoom2Anim === 1) {
level.exit.x = -50;
level.exit.y = -10030;
makeLore("Pathetic.");
}
if (templePlayer.room1ToRoom2Anim === 121) {
makeLore("You will never succeed.");
}
if (templePlayer.room1ToRoom2Anim >= 360 && templePlayer.room1ToRoom2Anim <= 720) {
const factor = 200 - 200 * Math.cos((templePlayer.room1ToRoom2Anim / 120 - 3) * Math.PI);
ctx.translate(factor, factor);
Promise.resolve().then(() => {
ctx.save();
ctx.globalCompositeOperation = "color-burn";
ctx.fillStyle = DrawTools.randomColours;
DrawTools.updateRandomColours(5);
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.restore();
});
}
if (templePlayer.room1ToRoom2Anim === 720) {
makeLore("You are trying too hard.");
relocateTo(0, -7050);
simulation.fallHeight = -6000;
templePlayer.stage = 2;
}
if (templePlayer.room1ToRoom2Anim === 960) {
makeLore("I have mastered the understandings of the universe.");
}
if (templePlayer.room1ToRoom2Anim === 1200) {
// Congrats, you discovered the actual words by looking at the source code. Are you happy now?
const x = (
["a speck of dust", "an insignificant hindrance", "a tiny obstacle"]
)[Math.floor(Math.random() * 3)].split("");
for (let i = 0; i < x.length / 1.6; i++) {
const randomIndex = Math.floor(Math.random() * x.length);
if (x[randomIndex] !== " ") {
x[randomIndex] = String.fromCharCode(Math.floor(Math.random() * 50) + 192);
}
};
makeLore(`You are no more than ${x.join("")} to me.</h3></h2>`);
relocateWIMPs(0, -10030);
}
templePlayer.room1ToRoom2Anim++;
},
room2() {
if (templePlayer.room2ToRoom3Anim <= 0) return;
if (templePlayer.room2ToRoom3Anim === 1) {
level.exit.x = 3500;
level.exit.y = -13130;
makeLore("Do not try me.");
}
if (templePlayer.room2ToRoom3Anim === 180) {
makeLore("I have absolute power over you.");
canvas.style.filter = "hue-rotate(90deg)";
}
if (templePlayer.room2ToRoom3Anim === 360) {
makeLore("You will not succeed...");
canvas.style.filter = "invert(0.2)";
}
if (templePlayer.room2ToRoom3Anim === 420) {
makeLore("<h6 style='display: inline-block'>...</h6>");
canvas.style.filter = "invert(0.4)";
}
if (templePlayer.room2ToRoom3Anim > 480 && templePlayer.room2ToRoom3Anim <= 660) {
canvas.style.filter = `sepia(${(templePlayer.room2ToRoom3Anim - 480) / 180}) invert(${0.5 + (templePlayer.room2ToRoom3Anim - 480) / 180})`;
}
if (templePlayer.room2ToRoom3Anim === 780) {
makeLore("Do not interfere with me.");
templePlayer.stage = 3;
relocateTo(50, -13150);
simulation.fallHeight = -10000;
simulation.zoomTransition(1800);
templePlayer.startAnim = -1;
// Might be a bit harsh to the player if the WIMPs are involved in the third level
for (let i = 0; i < mob.length; i++) {
while (mob[i] && !mob[i].isWIMP) {
mob[i].replace(i);
}
}
}
if (templePlayer.room2ToRoom3Anim > 780 && templePlayer.room2ToRoom3Anim <= 960) {
canvas.style.filter = `sepia(${(960 - templePlayer.room2ToRoom3Anim) / 180}) invert(${(960 - templePlayer.room2ToRoom3Anim) / 180})`;
}
templePlayer.room2ToRoom3Anim++;
},
room3() {
if (templePlayer.room3ToEndAnim <= 0) return;
if (templePlayer.room3ToEndAnim === 1) {
makeLore("No.");
}
if (templePlayer.room3ToEndAnim === 120) {
makeLore("This cannot be.");
}
if (templePlayer.room3ToEndAnim === 240) {
makeLore("Has my power failed me?");
}
if (templePlayer.room3ToEndAnim === 360) {
makeLore("Was it worth it, destroying this place?");
}
if (templePlayer.room3ToEndAnim === 600) {
makeLore("No one is greater than me.");
}
const text = "noone-";
for (let i = 0; i < 12; i++) {
if (templePlayer.room3ToEndAnim === 720 + i * 20) {
name = name.slice(0, -1);
simulation.makeTextLog(`<span style="font-size: 1em"><span style="color: #f00">${name}:</span> &nbsp; ${text[i % 6]}</span>`);
canvas.style.filter = `brightness(${1 - i / 22})`;
}
}
if (templePlayer.room3ToEndAnim === 1060) {
templePlayer.drawExit = true;
for (let i = 0; i < 5 * tech.wimpCount; i++) {
powerUps.spawn(level.exit.x + 100 * (Math.random() - 0.5), level.exit.y - 100 + 100 * (Math.random() - 0.5), "research", false);
}
canvas.style.filter = "";
}
templePlayer.room3ToEndAnim++;
},
end() {
if (!templePlayer.clearedCycle) return;
Promise.resolve().then(() => {
ctx.save();
ctx.setTransform(1, 0, 0, 1, 0, 0);
ctx.fillStyle = `rgba(0, 0, 0, ${(simulation.cycle - templePlayer.clearedCycle) / 300})`;
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.restore();
});
if (simulation.cycle - templePlayer.clearedCycle > 420) level.nextLevel();
}
};
const LogicHandler = {
bounds() {
let isInBounds = false;
for (const b of bounds) {
if (isInBound(b)) {
isInBounds = true;
break;
}
}
if (!isInBounds) {
m.damage(0.1 * simulation.difficultyMode);
trapPlayer(level.enter.x, level.enter.y);
simulation.makeTextLog("<span style='color: #f00'>" + name + "</span>: &nbsp; You thought I could let you get away with that?");
}
},
room0() {
// Block the player from entering the first seemingly innocuous exit
if ((m.pos.x > 1000) && templePlayer.startAnim === 0) {
spawn.mapRect(1200, -500, 100, 600);
templePlayer.initialTrapY = Math.min(player.position.y, -75);
trapPlayer(1000, templePlayer.initialTrapY);
addPartToMap();
simulation.draw.setPaths();
templePlayer.startAnim = 1;
}
},
room1() {
WaveHandler.room1();
Objects.altar.logic();
},
room2() {
room2SlimePit.logic();
Objects.room2Initiator.logic();
Objects.room2Lightning.logic();
Objects.room2GeneratedPath.logic();
WaveHandler.room2();
},
room3() {
Objects.room3Rotors.logic();
Objects.room3SlimePits.logic();
WaveHandler.room3();
},
exit() {
if (!templePlayer.drawExit) return;
if (player.position.x > level.exit.x &&
player.position.x < level.exit.x + 100 &&
player.position.y > level.exit.y - 150 &&
player.position.y < level.exit.y - 40 &&
player.velocity.y < 0.1 &&
level.exitCount + (input.down ? 8 : 2) > 100) {
if (templePlayer.stage === 1) {
templePlayer.drawExit = false;
level.exitCount = 0;
templePlayer.room1ToRoom2Anim = 1;
} else if (templePlayer.stage === 2) {
templePlayer.drawExit = false;
templePlayer.room2ToRoom3Anim = 1;
level.exitCount = 0;
} else {
level.exitCount = 99 - (input.down ? 8 : 2);
if (!templePlayer.clearedCycle) templePlayer.clearedCycle = simulation.cycle;
}
}
}
};
const DrawHandler = {
// Bottom layer
base() {
// Draw base red background
ctx.save();
ctx.setTransform(1, 0, 0, 1, 0, 0);
ctx.fillStyle = color.map;
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.restore();
// Draw the normal bg on the bounds
ctx.fillStyle = "#eab6af";
for (const b of bounds) {
if (isInBound(b)) ctx.fillRect(b.pos.x + 2, b.pos.y + 2, b.width - 4, b.height - 4);
}
},
entrance() {
ctx.beginPath();
ctx.moveTo(level.enter.x, level.enter.y + 30);
ctx.lineTo(level.enter.x, level.enter.y - 80);
ctx.bezierCurveTo(level.enter.x, level.enter.y - 170, level.enter.x + 100, level.enter.y - 170, level.enter.x + 100, level.enter.y - 80);
ctx.lineTo(level.enter.x + 100, level.enter.y + 30);
ctx.lineTo(level.enter.x, level.enter.y + 30);
ctx.fillStyle = "#fca";
ctx.fill();
},
room1() {
if (!isInBound(firstRoomBounds)) return;
// Draw Cross
ctx.fillStyle = "#fed";
ctx.fillRect(2200, -3300, 200, 800);
ctx.fillRect(2000, -3100, 600, 200);
// Final boss-like spawn fire thing. Was it necessary? No!
const spawnFlameAngle = Math.min(Math.min(templePlayer.room1.cycles, 2520) % 600, 120) * Math.PI / 30 + Math.PI / 2;
DrawTools.flame([2300, -3000, 26, 4, "#f60", spawnFlameAngle], 7);
Objects.altar.drawBottom();
},
room2() {
if (!isInBound(secondRoomBounds)) return;
if (templePlayer.room2.cycles) {
ctx.fillStyle = "#0006";
ctx.fillRect(secondRoomBounds.pos.x + 2, secondRoomBounds.pos.y + 2, secondRoomBounds.width - 4, secondRoomBounds.height - 4);
}
room2SlimePit.draw();
},
room3() {
if (!isInBound(thirdRoomBounds)) return;
ctx.fillStyle = "#0006";
ctx.fillRect(thirdRoomBounds.pos.x + 2, thirdRoomBounds.pos.y + 2, thirdRoomBounds.width - 4, thirdRoomBounds.height - 4);
Objects.room3SlimePits.draw();
},
// Top layer
mobTrails() {
if (simulation.cycle % 4 === 0) {
let newMobPositions = [];
for (const i of mob) {
if (!(i.isMACHO || i.isWIMP || i.isObstacle)) newMobPositions.push({
x: i.position.x,
y: i.position.y
});
}
mobPositionsQueue.shift();
mobPositionsQueue.push(newMobPositions);
}
// Draw "holy" trails for mobs for no particular reason at all
ctx.strokeStyle = "#bae";
ctx.lineWidth = 3;
for (let i = 0; i < 10; i++) {
const p = mobPositionsQueue[i];
for (const m of p) {
DrawTools.holy(m.x, m.y, i / 2 + 10);
}
}
ctx.shadowBlur = 0;
},
waveTimer() {
const roomConditions = [
isInBound(firstRoomBounds) && templePlayer.room1.cycles < 2400,
isInBound(secondRoomBounds) && templePlayer.room2.cycles > 0 && templePlayer.room2.cycles < 2160,
isInBound(thirdRoomBounds) && templePlayer.room2ToRoom3Anim < 1320
];
Promise.resolve(roomConditions).then(roomConditions => {
// First Room
if (roomConditions[0]) {
ctx.save();
ctx.setTransform(1, 0, 0, 1, 0, 0);
ctx.fillStyle = "#0004";
ctx.fillRect(canvas.width2 - 288, 50, 576, 20);
ctx.fillStyle = "#0cf";
ctx.fillRect(canvas.width2 - 288, 50, 0.96 * (600 - templePlayer.room1.cycles % 600), 20);
ctx.restore();
}
// Second Room
if (roomConditions[1]) {
ctx.save();
ctx.setTransform(1, 0, 0, 1, 0, 0);
ctx.fillStyle = "#0004";
ctx.fillRect(canvas.width2 - 288, 50, 576, 20);
ctx.fillStyle = (Math.ceil(templePlayer.room2.cycles / 720) & 1) ? "#000" : "#e1d7ff";
ctx.fillRect(canvas.width2 - 288, 50, 0.8 * (720 - templePlayer.room2.cycles % 720), 20);
ctx.restore();
}
// Third Room
if (roomConditions[2]) {
ctx.save();
ctx.setTransform(1, 0, 0, 1, 0, 0);
ctx.fillStyle = "#0004";
ctx.fillRect(canvas.width2 - 288, 50, 576, 20);
ctx.fillStyle = "#000";
ctx.fillRect(canvas.width2 - 288, 50, 1.6 * (1320 - templePlayer.room2ToRoom3Anim), 20);
ctx.restore();
}
});
},
room2Top() {
if (!isInBound(secondRoomBounds)) return;
Objects.room2Lightning.draw();
Objects.room2GeneratedPath.draw();
Objects.room2Initiator.draw();
}
};
const WaveHandler = {
room1() {
if (!isInBound(firstRoomBounds)) return;
if (templePlayer.room1.cycles === 0) powerUps.spawnStartingPowerUps(0, -2050);
templePlayer.room1.cycles++;
if (templePlayer.room1.cycles === 2400) {
spawn.secondaryBossChance(2300, -2800);
powerUps.addResearchToLevel();
}
if (templePlayer.room1.cycles % 600 === 0 && templePlayer.room1.cycles <= 2400) {
const spawnAmt = Math.min(3 + Math.pow(simulation.difficulty / 1.7, 0.6), 10) + Math.floor(templePlayer.room1.cycles / 720);
for (let i = 0; i < spawnAmt; i++) {
if (Math.random() < 0.5 + 0.07 * simulation.difficulty) {
spawn.randomMob(800 + Math.random() * 3e3, -2400 - Math.random() * 600, Infinity);
}
}
spawn.randomMob(800 + Math.random() * 3e3, -2400 - Math.random() * 600, Infinity);
}
if (templePlayer.room1.cycles === 2520) {
templePlayer.drawExit = true;
}
},
room2() {
if (!isInBound(secondRoomBounds)) return;
if (templePlayer.room2.spawnInitiatorCycles > Objects.room2Initiator.cap) {
const randomSecondRoomBoss = [secondRoomSuckerBoss, secondRoomPlacerBoss];
if (templePlayer.room2.cycles % 720 === 0 && templePlayer.room2.cycles <= 2160) {
const isOdd = Math.floor(templePlayer.room2.cycles / 720) & 1;
randomSecondRoomBoss[Math.floor(randomSecondRoomBoss.length * Math.random())](-600, -9800, isOdd);
randomSecondRoomBoss[Math.floor(randomSecondRoomBoss.length * Math.random())](600, -9800, isOdd);
randomSecondRoomBoss[Math.floor(randomSecondRoomBoss.length * Math.random())](0, -9800, !isOdd);
}
templePlayer.room2.cycles++;
if (templePlayer.room2.cycles === 2400) {
templePlayer.drawExit = true;
templePlayer.room2.readyPathCycle = simulation.cycle;
}
}
},
room3() {
if (templePlayer.room2ToRoom3Anim === 1320) {
thirdRoomBoss(1800, -13700);
for (let i = 0; i < 3; i++) {
powerUps.spawn(m.spawnPos.x, m.spawnPos.y, "heal");
}
}
}
};
const DrawTools = {
get randomColours() {
return `rgb(${this._randomColours.join(",")})`
},
_randomColours: [Math.random() * 255, Math.random() * 255, Math.random() * 255],
updateRandomColours(x = 0.8) {
for (let i = 0; i < this._randomColours.length; i++) {
this._randomColours[i] = Math.max(Math.min(this._randomColours[i] + (this.randFact() * x * 2) - x, 255), 0);
}
},
randFact() {
return Math.random() * 0.8 + Math.sin(Date.now() / 300) * 0.2;
},
line(vecs) {
ctx.beginPath();
ctx.moveTo(vecs[0].x, vecs[0].y);
for (const v of vecs.slice(1)) ctx.lineTo(v.x, v.y);
ctx.stroke();
},
arc(...x) {
ctx.beginPath();
ctx.arc(...x);
ctx.fill();
},
arcOut(...x) {
ctx.beginPath();
ctx.arc(...x);
ctx.stroke();
},
flame(props, repeat) {
for (let i = 0; i < repeat; i++) this.singleFlame(...props);
},
singleFlame(x, y, size = 10, repeat = 3, color = "#f00", angle = Math.PI / 2) {
ctx.strokeStyle = color;
ctx.lineWidth = 3;
const path = [{
x,
y
}];
for (let i = 0; i < repeat; i++) {
const randAng = (Math.random() - 0.5) * 2 + angle;
const randLen = 2 * size + Math.random() * size;
x += Math.cos(randAng) * randLen;
y -= Math.sin(randAng) * randLen;
path.push({
x,
y
})
}
DrawTools.line(path);
},
lightning(from, to, cycle, randomPRNGMult = 1, width = 8, color = [255, 240, 127]) {
const diff = simulation.cycle - cycle;
if (diff >= 15) return;
ctx.strokeStyle = `rgba(${color.join(",")},${(1 - diff / 15) * 255})`;
ctx.lineWidth = width * (1 - diff / 15);
ctx.shadowColor = `rgb(${color.join(",")})`;
ctx.shadowBlur = 20;
const path = [{
x: from.x,
y: from.y
}];
let vector = {
x: from.x,
y: from.y
};
let distanceLeft = V.magnitude(V.sub(from, to));
const d = distanceLeft > 800 ? distanceLeft / 40 : 20;
const normalised = V.normalise(V.sub(to, from));
while (1) {
const randOffset = rotateVector({
y: RNG(Math.floor(cycle * randomPRNGMult + distanceLeft)) * 2 * d - d,
x: 0
}, normalised);
const randLen = RNG(Math.floor(cycle * (randomPRNGMult + 1) + distanceLeft)) * d + d;
distanceLeft -= randLen;
if (distanceLeft <= 0) {
path.push({
x: to.x,
y: to.y
});
break;
}
vector = V.add(vector, V.mult(normalised, randLen));
path.push({
x: vector.x + randOffset.x,
y: vector.y + randOffset.y
});
}
DrawTools.line(path);
ctx.shadowBlur = 0;
},
holy(x, y, size = 12) {
this.line([{
x,
y: y - size
}, {
x: x - size,
y
},
{
x,
y: y + size
}, {
x: x + size,
y
}, {
x,
y: y - size
}
]);
}
};
function RNG(x) {
x += Math.seed;
let start = Math.pow(x % 97, 4.3) * 232344573;
const a = 15485863;
const b = 521791;
start = (start * a) % b;
for (let i = 0; i < (x * x) % 90 + 90; i++) {
start = (start * a) % b;
}
return start / b;
}
function rotateVector(v, ang) {
const c = typeof ang === "number" ? {
x: Math.cos(ang),
y: Math.sin(ang)
} : V.normalise(ang);
return {
x: v.x * c.x - v.y * c.y,
y: v.y * c.x + v.x * c.y
};
}
function trapPlayer(x, y) {
setPosAndFreeze(player, {
x,
y
});
const bLen = bullet.length;
for (let i = 0; i < bLen; i++) {
if (bullet[i].botType) setPosAndFreeze(bullet[i], V.add(player.position, {
x: 100 * (RNG(i) - 0.5),
y: 100 * (RNG(i + bLen) - 0.5)
}));
}
}
function relocateTo(x, y) {
level.setPosToSpawn(x, y);
trapPlayer(x, y);
for (let i = 0; i < mob.length; i++) {
if (mob[i].isMACHO) {
setPos(mob[i], {
x,
y
});
break;
}
}
m.resetHistory();
}
const distance = (a, b) => V.magnitude(V.sub(a, b));
const angle = (a, b) => Math.atan2(b.y - a.y, a.x - b.x);
const setPos = (a, b) => Matter.Body.setPosition(a, b);
const setVel = (a, b) => Matter.Body.setVelocity(a, b);
const freeze = a => setVel(a, {
x: 0,
y: 0
});
const setPosAndFreeze = (a, b) => {
setPos(a, b);
freeze(a);
};
const makeLore = (x, t) => simulation.makeTextLog(`<h2 style='color: #f00; display: inline-block'>${name}:</h2> &nbsp; <h3 style='display: inline-block'>${x}</h3>`, t);
level.custom = () => {
// All the logic gets handled here. How nice!
for (const i in LogicHandler) {
LogicHandler[i]();
}
// Animations and lore for things that seem like exits
for (const i in RoomTransitionHandler) {
RoomTransitionHandler[i]();
}
// Bottom layer graphics
DrawHandler.base();
DrawHandler.room1();
DrawHandler.room2();
DrawHandler.room3();
DrawHandler.entrance();
if (templePlayer.drawExit) level.exit.drawAndCheck();
};
level.customTopLayer = () => {
// Top layer graphics
DrawHandler.mobTrails();
Objects.altar.drawTop();
DrawHandler.waveTimer();
DrawHandler.room2Top();
};
},
dripp() {
simulation.makeTextLog(`<strong>dripp</strong> by <span class='color-var'>M. B.</span>`);
const door = level.door(780, -350, 15, 400, 265);
const buttonDoor = level.button(420, -10);
const boost = level.boost(130, -445);
const hazard = level.hazard(690, -1050, 10, 700, 0.4)
const hazard2 = level.hazard(2470, -1515, 162, 14, 0.4)
const hazard3 = level.hazard(740, -1050, 10, 700, 0.4)
const hazard4 = level.hazard(3400, -380, 350, 6, 0.2)
const hazard5 = level.hazard(3425, -1420, 400, 8, 0.2)
const slimePit = level.hazard(2250, -100, 2700, 200, 0.004)
const door2 = level.door(3131, -898, 40, 520, 522)
const buttonDoor2 = level.button(2495, -270)
const toggle = level.toggle(1463, -708, true)
const elevator = level.elevator(4310, -150, 200, 50, -1443, 0.0025, {
up: 0.1,
down: 0.2
})
const portal = level.portal({ //main portals
x: 2117,
y: -1560
}, -2 * Math.PI, { //up
x: -80,
y: -475
}, -Math.PI / 100) //up
const drip1 = level.drip(4100 + 1000 * Math.random(), -1900, 50, 100) // drip(x, yMin, yMax, period = 100, color = "hsla(160, 100%, 35%, 0.5)") {
const drip2 = level.drip(4100 + 1000 * Math.random(), -1900, 50, 207) // drip(x, yMin, yMax, period = 100, color = "hsla(160, 100%, 35%, 0.5)") {
const drip3 = level.drip(4100 + 1000 * Math.random(), -1900, 50, 133) // drip(x, yMin, yMax, period = 100, color = "hsla(160, 100%, 35%, 0.5)") {
const drip4 = level.drip(4100 + 1000 * Math.random(), -1900, 50, 157) // drip(x, yMin, yMax, period = 100, color = "hsla(160, 100%, 35%, 0.5)") {
level.custom = () => {
level.exit.drawAndCheck();
drip1.draw()
drip2.draw()
drip3.draw()
drip4.draw()
buttonDoor.query();
buttonDoor.draw();
if (buttonDoor.isUp) {
door.isClosing = true
} else {
door.isClosing = false
}
door.openClose();
buttonDoor2.query();
buttonDoor2.draw();
if (buttonDoor2.isUp) {
door2.isClosing = true
} else {
door2.isClosing = false
}
door2.openClose();
// shadow/shades builds
ctx.fillStyle = "rgba(0, 0, 0, 0.05)"
ctx.fillRect(3169, -900, 891, 580)
ctx.fillRect(417, -1057, 380, 730)
ctx.fillRect(930, -515, 207, 520)
ctx.fillRect(930, -1280, 207, 760)
ctx.fillRect(1220, -1280, 54, 800)
ctx.fillRect(1221, -1394, 451, 1398)
ctx.fillRect(1924, -800, 219, 674)
ctx.fillRect(2264, -1488, 214, 1550)
ctx.fillRect(2631, -1488, 201, 1550)
ctx.fillRect(2889, -930, 237, 1090)
ctx.fillRect(3124, -311, 957, 360)
ctx.fillRect(1919, -1480, 220, 700)
// ctx.fillRect(1768, -1200, 71, 500)
level.enter.draw();
elevator.move();
toggle.query();
};
level.customTopLayer = () => {
boost.query();
hazard.opticalQuery();
hazard2.opticalQuery();
hazard3.opticalQuery();
hazard4.opticalQuery();
hazard5.opticalQuery();
slimePit.query();
// slimePit.draw();
hazard.isOn = toggle.isOn
hazard3.isOn = toggle.isOn
portal[0].draw();
portal[1].draw();
portal[2].draw();
portal[2].query()
portal[3].query()
};
level.setPosToSpawn(0, -50); //normal spawn
level.exit.x = 1400;
level.exit.y = -1500;
spawn.mapRect(level.enter.x, level.enter.y + 20, 100, 20);
level.defaultZoom = 1800
simulation.zoomTransition(level.defaultZoom)
document.body.style.backgroundColor = "#d8dadf";
//builds
spawn.mapRect(-100, 0, 1485, 100);
spawn.mapRect(-279, -750, 200, 850);
spawn.mapRect(1781, -125, 375, 75);
spawn.mapRect(1670, -100, 590, 200);
spawn.mapRect(2261, 50, 3100, 50);
spawn.mapRect(2420, -260, 300, 50);
spawn.bodyRect(235, -240, 50, 50, 1, spawn.propSLide)
spawn.mapRect(410, -1100, 400, 50);
spawn.mapRect(1220, -1470, 420, 80)
spawn.mapRect(927, -1325, 220, 50);
spawn.mapRect(4950, -200, 425, 375);
spawn.bodyRect(5200, -300, 100, 100);
//random builds
spawn.mapRect(2150, 50, 225, 50);
//large border walls
spawn.mapRect(-300, -2375, 6075, 475);
spawn.mapRect(-951, -2374, 675, 2476);
spawn.mapRect(-950, 100, 6950, 500);
spawn.mapRect(5300, -2374, 700, 2700);
// create enemies
spawn.randomMob(3000, -300, 0.5);
spawn.randomMob(1900, -1000, 0.5);
spawn.randomMob(2960, -800, 0.6)
spawn.randomMob(3500, -1700, 0.4)
spawn.randomMob(800, -1700, 0.6)
spawn.randomMob(100, -1150, 0.6)
spawn.randomMob(1095, -700, 0.6)
//powerUps
powerUps.spawn(590, -200, "ammo")
powerUps.spawn(600, -200, "heal")
// powerUps.spawn(590, -200, "gun")
powerUps.spawnStartingPowerUps(590, -200);
// more builds
spawn.blockDoor(1230, -1490)
// spawn.blockDoor(728, -1130);
spawn.mapRect(-100, -380, 900, 50);
spawn.mapRect(-279, -1400, 200, 650);
spawn.mapRect(-279, -1900, 200, 650);
// spawn.mapRect(-100, -1900, 2300, 75);
// spawn.mapRect(2200, -1900, 1025, 75);
// spawn.mapRect(2700, -1900, 2000, 75);
spawn.mapRect(2270, -1530, 199, 50);
spawn.mapRect(2633, -1530, 199, 50)
// spawn.mapRect(4570, -1825, 125, 1925);
spawn.mapRect(3106, -400, 300, 50)
spawn.mapRect(3750, -400, 330, 50)
spawn.mapRect(3130, -1030, 930, 130);
spawn.mapRect(4015, -900, 46, 275);
spawn.blockDoor(4016, -400)
spawn.mapRect(3168, -1440, 290, 50);
spawn.mapRect(3771, -1440, 294, 50);
spawn.mapRect(3106, -355, 974, 42);
spawn.mapRect(3228, -1395, 834, 380);
spawn.mapRect(3129, -1350, 100, 325);
spawn.mapRect(3129, -1400, 175, 100);
spawn.mapRect(3129, -1437, 125, 75);
spawn.mapRect(1382, 0, 295, 100);
spawn.mapRect(1600, -50, 560, 85);
spawn.mapRect(2264, -945, 220, 50);
spawn.mapRect(1925, -800, 220, 50);
spawn.mapRect(1390, -700, 260, 50);
spawn.mapRect(927, -520, 220, 50);
spawn.mapRect(2894, -948, 300, 50)
spawn.mapRect(1230, -1825, 440, 81);
spawn.mapRect(1616, -1750, 54, 360);
spawn.mapRect(3128, -1440, 50, 50);
spawn.mapRect(1705, -120, 125, 75);
spawn.mapRect(1550, -25, 150, 50);
spawn.mapRect(1628, -75, 100, 50);
spawn.mapRect(1729, -130, 650, 75);
//ground for blue portal
spawn.mapRect(1917, -1484, 300, 50);
spawn.mapRect(1917, -1950, 200, 325);
spawn.mapRect(1917, -1825, 50, 375);
//split
spawn.mapRect(1221, -1420, 57, 465);
spawn.mapRect(1221, -634, 57, 450);
spawn.bodyRect(1227, -105, 42, 189, 1, spawn.propSlide)
// spawn.mapRect(1770, -1900, 70, 750);
spawn.mapRect(1770, -780, 70, 400)
spawn.bodyRect(1783, -289, 38, 250, 1, spawn.propSlide)
if (simulation.difficulty > 1) spawn.randomLevelBoss(4800, -750);
spawn.secondaryBossChance(4700, -1500)
powerUps.addResearchToLevel() //needs to run after mobs are spawned
},
biohazard() {
// MAP BY INOOBBOI AND THESHWARMA
simulation.makeTextLog(`<strong>biohazard</strong> by <span class='color-var'>INOOBBOI</span> and <span class='color-var'>THESHWARMA</span>`);
// set here for the cutscene later
level.setPosToSpawn(-2800, -150)
// set up cutscenes
simulation.cutscene = (locations, speed, stay, xPos = m.pos.x, yPos = m.pos.y) => {
// locations: an array of location vectors, reversed for the pop ahead
locations.reverse()
// speed: the speed of the cutscene transition (0 to 1)
// stay: how much to stay in the destination (ticks)
// xPos & yPos: the initial location, also used as the current location
// start by disabling the default camera draw. Don't worry, it's backed up
const camera = simulation.camera
// create a new camera function
simulation.camera = () => {
ctx.save()
ctx.translate(canvas.width2, canvas.height2) //center
ctx.scale(simulation.zoom, simulation.zoom)
const xScaled = canvas.width2 - xPos
const yScaled = canvas.height2 - yPos
ctx.translate(-canvas.width2 + xScaled, -canvas.height2 + yScaled) //translate
}
// and set a restoring function
const restore = () => (simulation.camera = camera)
// then choose the next destination. There should be always at least one destination,
// if there isn't there's no point checking, the game should and will crash
let dest = locations.pop()
// animate the camera
const lerp = (first, second, percent) => first * (1 - percent) + second * percent
const speedDelta = speed / 5
// wait timer
let wait = 0
// polls the animation, should be called every tick
const poll = () => {
// update position
xPos = lerp(xPos, dest.x, speedDelta)
yPos = lerp(yPos, dest.y, speedDelta)
// if position is close enough, wait and go to the next position
const TOO_CLOSE = 100
if (Math.abs(dest.x - xPos) < TOO_CLOSE && Math.abs(dest.y - yPos) < TOO_CLOSE) {
// wait for a bit
if (++wait > stay) {
// if there is another target, reset the wait timer and go there
// otherwise end the cutscene
wait = 0
if (!(dest = locations.pop())) {
// no more locations! End
restore()
return true
}
}
}
// early return if the player skips by fielding
if (input.field) {
restore()
return true
}
return false
}
return poll
}
const boost1 = level.boost(-1400, -100, 900)
const boost2 = level.boost(500, -900, 2500)
const boost3 = level.boost(4200, -100, 900)
const boost4 = level.boost(2200, -900, 2500)
const toggle = level.toggle(1340, -600, false, true)
let bossInit = false
const cutscenePoll = simulation.cutscene([{
x: 230,
y: -2700
}, {
x: 3500,
y: -1400
}, {
x: 1450,
y: -1150
}, m.pos], 0.1, 10)
let hasEnded = false
// ** PROPS **
// create some drips
const rndInRange = (min, max) => Math.random() * (max - min) + min
const amount = Math.round(5 + 20 * Math.random())
const drips = []
for (let i = 0; i < amount; i++) {
const locX = rndInRange(-2000, 4800)
drips.push(level.drip(locX, -3100, 1500, 200 + Math.random() * 500))
}
// a barrel of radioactive waste, which can drop ammo and heals
const barrelMob = (x, y, dirVector) => {
const MAX_WIDTH = 150
const personalWidth = MAX_WIDTH / 2
mobs.spawn(x, y, 4, personalWidth, 'rgb(232, 191, 40)')
const me = mob[mob.length - 1]
// steal some vertices
const betterVertices = Matter.Bodies.rectangle(x, y, personalWidth, personalWidth * 1.7).vertices
me.vertices = betterVertices
me.collisionFilter.mask = cat.player | cat.map | cat.body | cat.mob | cat.bullet
me.g = simulation.g
me.leaveBody = me.isDropPowerUp = false
me.do = function () {
this.gravity()
// apply shock damage when touching the map, if it's fast
if (this.speed > 5) {
const collision = Matter.Query.collides(this, map)
if (collision.length > 0) {
// on collision reduce health
this.health = this.health - this.speed / 250
// die when it's too low, doesn't register for some reason
}
}
// becomes more radioactive as it gets damaged!
this.fill = `rgb(${232 * this.health}, 191, 40)`
}
me.onDeath = function () {
const END = Math.floor(input.down ? 10 : 7)
const totalBullets = 10
const angleStep = (input.down ? 0.4 : 1.3) / totalBullets
let dir = m.angle - (angleStep * totalBullets) / 2
for (let i = 0; i < totalBullets; i++) {
//5 -> 7
dir += angleStep
const me = bullet.length
bullet[me] = Bodies.rectangle(
this.position.x + 50 * Math.cos(this.angle),
this.position.y + 50 * Math.sin(this.angle),
17,
4,
b.fireAttributes(dir)
)
const end = END + Math.random() * 4
bullet[me].endCycle = 2 * end + simulation.cycle
const speed = (25 * end) / END
const dirOff = dir + (Math.random() - 0.5) * 3
Matter.Body.setVelocity(bullet[me], {
x: speed * Math.cos(dirOff),
y: speed * Math.sin(dirOff)
})
bullet[me].onEnd = function () {
b.explosion(
this.position,
150 + (Math.random() - 0.5) * 40
) //makes bullet do explosive damage at end
}
bullet[me].beforeDmg = function () {
this.endCycle = 0 //bullet ends cycle after hitting a mob and triggers explosion
}
bullet[me].do = function () { }
Composite.add(engine.world, bullet[me]) //add bullet to world
}
// barrels drop a ton of ammo and some heals, scales up with difficulty because I have mercy
const amount = ~~(5 * Math.random() * simulation.difficulty / 10)
for (let i = 0; i < amount; i++) {
powerUps.spawn(this.position.x, this.position.y, 'ammo', true)
if (Math.random() > 0.7) {
powerUps.spawn(this.position.x, this.position.y, 'heal', true)
}
}
}
Matter.Body.rotate(me, Math.random() * Math.PI)
Matter.Body.setVelocity(me, dirVector)
}
// creates a platform with shadow
const platformShadow = (x, y, width, height, shadowList) => {
// faster than making manual shadows... Why not just calculate them semi-realsitically?
// the shadows are calculated on the object creation, so if you add map blocks it won't update.
// shadowList is an array of shadows that'll be rendered. When the platform shadow is ready,
// it is added to the list.
// some helper functions first
const perpCollision = point => {
// takes a point, and finds a collision with the map downwards
// the end of the ray, 3000 units down
const lowerPoint = Vector.add(point, {
x: 0,
y: 3000
})
// the destination point. If a collision was not found, then it defaults to some
// arbiterary point 3000 units down.
let dest = lowerPoint
for (const mapBody of map) {
const check = simulation.checkLineIntersection(point, lowerPoint, mapBody.vertices[0], mapBody.vertices[1])
// a collision was found
if (check.onLine1 && check.onLine2) {
dest = {
x: check.x,
y: check.y
}
break
}
}
return dest
}
const boundsToRectangle = (firstBound, secondBound) => {
// takes two bounds and returns an (x, y, width, height) rectangle. The first one
// must be the top left, and the second one must be the bottom right
// sub to find the width and height
const width = Math.abs(firstBound.x - secondBound.x)
const height = Math.abs(firstBound.y - secondBound.y)
// compile to an object
return {
x: firstBound.x,
y: firstBound.y,
width,
height
}
}
// create the actual platform
spawn.mapRect(x, y, width, height)
const me = map[map.length - 1]
// the bottom vertices are the third and fourth ones
const first = me.vertices[3]
const second = me.vertices[2]
// cast shadows to find the last shadow location.
// iterate over all map objects, and check for collisions between a perpendicular ray
// cast from the vertex down to the map object's top panel
// const firstDown = perpCollision(first) // not needed in a rectangle settings
const secondDown = perpCollision(second)
// possible TODO: make it multirect for efficiency
// create a single rectangle and return
shadowList.push(boundsToRectangle(first, secondDown))
}
// cages with mobs, One of them holds the boss pre mutation
const cage = (x, y, maxChainLength, drawList, mobType = null, isTheBoss = false) => {
// the drawList is an array that the drawing function is added to
// if the cage containing the boss it has a 50% chance to just not spawn. Spices things a bit
if (!isTheBoss && Math.random() > 0.5) {
return
}
if (!mobType) {
// if mob type is null, then it picks a random mob
mobType = spawn.fullPickList[~~(Math.random() * spawn.fullPickList.length)]
}
// create the chain length, must take into account the radius of the mob.
// therefore, it'll create a pseudo mob of that type, take it radius and instantly kill it
const chainLength = maxChainLength / 5 + maxChainLength * Math.random()
// spawn and insantly kill a mob of the same type to get the radius.
// this is used to prevent spawning the mob too short, it's a horrible
// solution but it works
spawn[mobType](0, 0)
mob[mob.length - 1].leaveBody = mob[mob.length - 1].isDropPowerUp = false
const radius = mob[mob.length - 1].radius
mob[mob.length - 1].alive = false
// spawn the mob. Disable shields first
spawn.allowShields = false
spawn[mobType](x, y + chainLength + radius * 2)
const trappedMob = mob[mob.length - 1]
// destroy its mind so it won't attack
trappedMob.do = () => { }
// spawn the cage
mobs.spawn(x, y + chainLength + radius * 2, 4, trappedMob.radius + 50, 'rgba(150, 255, 150, 0.3)')
const cage = mob[mob.length - 1]
cage.g = simulation.g
cage.do = function () {
this.gravity()
}
// label it
cage.label = 'Cage'
// a special orb when hit
let damageTick = 0
cage.onDamage = (dmg) => {
// add some damage ticks, if the trapped mob is still alive.
// activating the boss by this method is almost impossible, since you need 10x damage
if (trappedMob.alive) damageTick += ~~(isTheBoss ? 5 * dmg : 50 * dmg)
}
// collision filter
trappedMob.collisionFilter.mask = cage.collisionFilter.mask = cat.player | cat.map | cat.bullet
// constrain together
spawn.constrain2AdjacentMobs(2, 0.05, false)
// move them to be together
trappedMob.position = Vector.clone(cage.position) // make sure you clone... Otherwise........
// invincibility, make homing bullets not hit these, remove health bar
trappedMob.health = cage.health = Infinity
trappedMob.isBadTarget = cage.isBadTarget = true
trappedMob.showHealthBar = cage.showHealthBar = false
trappedMob.leaveBody = trappedMob.isDropPowerUp = cage.leaveBody = trappedMob.isDropPowerUp = false
// cross all edges of the cage with the rope, and see where it collides. Attach the rope there
const verts = cage.vertices
// the crossing location, doesn't stay null
let cross = null
for (let i = 0; i < verts.length; i++) {
// iterate over all vertices to form lines
const v1 = verts[i]
const v2 = verts[(i + 1) % verts.length]
const result = simulation.checkLineIntersection(cage.position, {
x,
y
}, v1, v2)
if (result.onLine1 && result.onLine2) {
// both lines cross!
cross = result
break
}
}
if (!cross) {
// for some odd reason, sometimes it never finds a collision. I have no idea why
// just default to the center then
console.error("Couldn't find a cross... Origin: ", {
x,
y
}, " center: ", cage.position, ' vertices: ', cage.vertices)
cross = cage.position
}
// create the rope
const rope = Constraint.create({
pointA: {
x,
y
},
// offset the point be in the attachment point
pointB: Vector.sub(cross, cage.position),
bodyB: cage,
// the longer the rope, the looser it is
stiffness: Math.max(0.0005 - chainLength / 10000000, 0.00000001),
length: chainLength
})
Matter.Composite.add(engine.world, rope)
// create and return a function for drawing the rope
const draw = () => {
// draw a little recantagle at the base
ctx.fillStyle = color.map
ctx.fillRect(x - 20, y - 5, 40, 25)
// if the cage was destroyed... Do nothing beyond
if (!cage.alive) {
return
}
// draw the rope
ctx.beginPath()
ctx.moveTo(x, y)
// line to the crossing point
// ctx.lineTo(cons[i].bodyB.position.x, cons[i].bodyB.position.y);
ctx.lineTo(cage.position.x + rope.pointB.x, cage.position.y + rope.pointB.y);
ctx.lineWidth = 7
ctx.strokeStyle = 'rgba(0, 0, 0, 0.5)'
ctx.stroke()
// now draw a mystic hit orb if touched
if (damageTick) damageTick-- // reduce the ticks
ctx.beginPath()
ctx.arc(cage.position.x, cage.position.y, cage.radius + 30, 0, Math.PI * 2)
ctx.lineWidth = 10
ctx.fillStyle = `rgba(255, 0, 0, ${Math.min(1, damageTick / 2000)})`
ctx.strokeStyle = `rgba(255, 100, 0, ${Math.min(1, damageTick / 1000)})`
ctx.setLineDash([125 * Math.random(), 125 * Math.random()])
ctx.stroke()
ctx.setLineDash([])
ctx.fill()
// if it's the boss, draw sucking arcs
if (isTheBoss && bossInit) {
for (const entity of mob) {
// suck the health of all mobs
// I hate string manipulation in control flow but heh
if (entity.alive) {
ctx.beginPath()
ctx.moveTo(entity.position.x, entity.position.y)
ctx.lineTo(trappedMob.position.x, trappedMob.position.y)
ctx.lineWidth = 10
ctx.strokeStyle = 'rgba(38, 0, 255, 0.67)'
ctx.stroke()
// damage the mob
entity.damage(1)
// damage itself bonus
cage.damage(1)
}
}
cage.damage(5)
}
// ok if it's too much, explode
if (damageTick > 2000) {
b.explosion(cage.position, cage.radius * 10)
// die a silent death
trappedMob.alive = cage.alive = false
damageTick = 0
if (isTheBoss) {
// become the real boss
geneticBoss(trappedMob.position.x, trappedMob.position.y)
}
}
}
// restore the shields
spawn.allowShields = true
// add the drawing function
drawList.push(draw)
}
// platform shadows
const shadows = []
// cages
const cages = []
level.custom = () => {
level.exit.drawAndCheck() //draws the exit
level.enter.draw() //draws the entrance
player.force.y -= player.mass * simulation.g * 0.25 //this gets rid of some gravity on player
// if the cutscene is yet to end, continue polling
if (!hasEnded) {
hasEnded = cutscenePoll()
}
for (const drip of drips) drip.draw()
// throw some barrels after the boss spawns
if (Math.random() > 0.999 && bossInit && !hasEnded) {
const spawnLocs = [-1415, -30, 1345, 2815, 4285]
// const randVec = Vector.mult({ x: Math.cos(randAngle), y: Math.sin(randAngle) }, Math.random() * 15)
barrelMob(spawnLocs[~~(spawnLocs.length * Math.random())], -4200, {
x: 0,
y: 0
})
}
// platform shadow
ctx.beginPath()
for (const shadow of shadows) {
ctx.rect(shadow.x, shadow.y, shadow.width, shadow.height)
}
ctx.fillStyle = 'rgba(0,10,30,0.1)'
ctx.fill()
// player pressed lever
if (toggle.isOn && !bossInit) {
bossInit = true
}
// draw the cage
} //for dynamic stuff that updates while playing that is one Z layer below the player
level.customTopLayer = () => {
boost1.query()
boost2.query()
boost3.query()
boost4.query()
toggle.query()
// shadow holes
ctx.fillStyle = 'rgba(68, 68, 68,0.95)'
ctx.fillRect(-1450 - 10, -4350, 150 + 20, 1250)
ctx.fillRect(-50 - 10, -4350, 150 + 20, 1250)
ctx.fillRect(1325 - 10, -4350, 150 + 20, 1250)
ctx.fillRect(2800 - 10, -4350, 150 + 20, 1250)
ctx.fillRect(4275 - 10, -4350, 150 + 20, 1250)
for (const drawCage of cages) {
drawCage()
}
} //for dynamic stuff that updates while playing that is one Z layer above the player
const anotherBoss = (x, y) => {
if (tech.isDuplicateMobs && Math.random() < tech.duplicationChance()) {
tech.isScaleMobsWithDuplication = true
spawn.historyBoss(x, y)
tech.isScaleMobsWithDuplication = false
} else if (tech.isResearchBoss) {
if (powerUps.research.count > 2) {
powerUps.research.changeRerolls(-3)
simulation.makeTextLog(
`<span class='color-var'>m</span>.<span class='color-r'>research</span> <span class='color-symbol'>-=</span> 3<br>${powerUps.research.count}`
)
} else {
tech.addJunkTechToPool(0.49)
}
spawn.historyBoss(x, y)
}
}
//GENETICBOSS
function drawEnergyBar(mob) {
if (mob.seePlayer.recall && mob.energy > 0) {
const h = mob.radius * 0.3
const w = mob.radius * 2
const x = mob.position.x - w / 2
const y = mob.position.y - w * 0.9
ctx.fillStyle = 'rgba(100, 100, 100, 0.3)'
ctx.fillRect(x, y, w, h)
ctx.fillStyle = '#0cf'
ctx.fillRect(x, y, w * mob.energy, h)
}
}
function generateGenome() {
// creates a random genome and returns it
const genome = {
density: Math.random() * 0.001,
size: 15 + Math.random() * 15,
speed: Math.random() * 0.1,
// color and vertex properties are "trash" genes as they don't really contribute to the orb
color: [Math.random() * 255, Math.random() * 255, Math.random() * 255, 50 + Math.random() * 205],
vertexCount: Math.floor(Math.random() * 5) + 3,
// TODO fix possible concaving
vertexOffset: null // placeholder
}
// initialized here as it depends on vertexCount. I could use `new function()` but nah.
genome.vertexOffset = Array(genome.vertexCount)
.fill()
.map(() => ({
x: Math.random() - 0.5,
y: Math.random() - 0.5
}))
return genome
}
function mutateGenome(genome) {
// takes an existing genome and applies tiny changes
const randomInRange = (min, max) => Math.random() * (max - min) + min
const tinyChange = x => randomInRange(-x, x)
const vertexMutator = x => ({
x: x.x + tinyChange(0.5),
y: x.y + tinyChange(0.5)
})
// mutates a genome and returns the mutated version.
const newGenome = {
density: genome.density + tinyChange(0.0005),
size: genome.size + tinyChange(5),
speed: genome.speed + tinyChange(0.05),
color: genome.color.map(x => (x + tinyChange(10)) % 255), // wrap around
vertexCount: Math.max(genome.vertexCount + Math.round(tinyChange(1)), 3),
vertexOffset: genome.vertexOffset.map(vertexMutator)
}
if (genome.vertexOffset.length < newGenome.vertexCount) {
const vo = newGenome.vertexOffset
vo.push(vertexMutator(vo[~~(vo.length * Math.random())]))
} else if (genome.vertexOffset.length > newGenome.vertexCount) {
newGenome.vertexOffset.pop()
}
return newGenome
}
function calculateGenomeCost(genome) {
// calculates the cost of a genome and returns it. The cost is used to
// determine how "costly" the genome is to make, and after the orb's life ends it
// is used together with the orb success score to determine the fittest orb.
const score = (1 / (genome.density * genome.size * genome.speed)) * 0.000001
return score
}
// distance functions
const dist2 = (a, b) => (a.x - b.x) ** 2 + (a.y - b.y) ** 2
const dist = (a, b) => Math.sqrt(dist2(a, b))
// ** MAP SPECIFIC MOBS **
function energyTransferBall(origin, target, boss, charge) {
// transports energy to the boss
// when the boss is hit by it, how much of the energy stored the boss actually recives
const ENERGY_TRANSFER_RATE = 80 /*%*/
// add 1 to the active ball list
boss.activeBalls++
const color = `rgba(${150 + 105 * charge}, 81, 50, 0.6)`
mobs.spawn(origin.x, origin.y, 12, 20 + 20 * charge, color)
const me = mob[mob.length - 1]
me.end = function () {
simulation.drawList.push({
// some nice graphics
x: this.position.x,
y: this.position.y,
radius: this.radius,
color: '#f3571d',
time: ~~(Math.random() * 20 + 10)
})
// on death spawn and explode a bomb
if (Math.random() > 0.95) {
spawn.bomb(this.position.x, this.position.y, this.radius, this.vertices.length)
mob[mob.length - 1].death()
}
// remove 1 from the active ball list
boss.activeBalls--
this.death()
}
me.collisionFilter.mask = cat.player | cat.map
// me.onHit = this.end
me.life = 0
me.isDropPowerUp = false
me.leaveBody = false
me.do = function () {
// die on collision with the map
if (Matter.Query.collides(this, map).length > 0) {
this.end()
}
// die if too much time passes. Stronger bullets explode earlier
if (++this.life > 200 - charge * 100) {
this.end()
}
// if the orb collides with the boss, die but give energy to the boss
if (Matter.Query.collides(this, [boss]).length > 0) {
boss.energy = Math.min(charge * (ENERGY_TRANSFER_RATE / 100) + boss.energy, 1)
// also make the boss fire once regardless of energy
boss.spawnOrbs()
this.end()
}
const movement = Vector.normalise(Vector.sub(target, origin))
Matter.Body.setVelocity(this, {
x: this.velocity.x + movement.x,
y: this.velocity.y + movement.y
})
// nice graphics
simulation.drawList.push({
x: this.position.x,
y: this.position.y,
radius: this.radius,
color: '#e81e1e',
time: 3
})
simulation.drawList.push({
x: this.position.x,
y: this.position.y,
radius: this.radius,
color: '#e87f1e',
time: 6
})
simulation.drawList.push({
x: this.position.x,
y: this.position.y,
radius: this.radius,
color: '#e8e41e',
time: 9
})
}
me.onDamage = me.end
}
function energyBeacon(x, y, parentBoss) {
// an unmoving beacon that charges the genetic boss with energy either stolen
// from the player or generated. That energy is used to create stronger mobs.
mobs.spawn(x, y, 3, 50, '') // default color, changed an instant later
const me = mob[mob.length - 1]
me.laserRange = 500
me.leaveBody = false
me.isDropPowerUp = false
// custom variables
// max energy is 1
me.energy = 0
me.seed = simulation.cycle // seed to make sure this mob is unique render wise
me.chargeTicks = 0 // used to time charging the boss
me.bossPos = null // the position that the mob remembers when charging
me.density = me.density * 2
Matter.Body.setDensity(me, 0.0022 * 3 + 0.0002 * Math.sqrt(simulation.difficulty)) //extra dense
me.do = function () {
// if the boss is dead, die
if (!parentBoss.alive) {
this.death()
}
// slowly rotate
Matter.Body.setAngularVelocity(this, 0.01)
this.fill = `rgba(${this.energy * 255}, 29, 136, 0.80)`
this.seePlayerCheck()
// steal energy from player
// this.harmZone() // regular harmZone
// custom effects on top of that
if (this.distanceToPlayer() < this.laserRange) {
if (m.immuneCycle < m.cycle) {
// suck extra energy from the player if it's in range
if (m.energy > 0.1 && this.energy < 1 - 0.012) {
m.energy -= 0.012
this.energy += 0.012
}
// special "sucking" graphics
ctx.beginPath()
ctx.moveTo(this.position.x, this.position.y)
ctx.lineTo(m.pos.x, m.pos.y)
ctx.lineWidth = 3 + Math.abs(Math.sin((simulation.cycle + this.seed) / 100)) * 2
ctx.strokeStyle = `rgb(${(
Math.abs(Math.sin((simulation.cycle + this.seed + 100) / 100)) * 255
).toFixed(3)}, 204, 255)`
ctx.setLineDash([125 * Math.random(), 125 * Math.random()])
ctx.stroke()
ctx.setLineDash([])
}
}
// if the mob's energy is at least 50% full, try to send that energy to the boss.
// don't send that energy yet if more than 5 other transfer balls are active
if (this.energy > 0.5 && parentBoss.energy < 1 && parentBoss.activeBalls <= 5 && this.chargeTicks === 0) {
const seesBoss = Matter.Query.ray(map, this.position, parentBoss.position).length === 0
if (seesBoss) {
this.chargeTicks = 100
this.bossPos = Vector.clone(parentBoss.position)
}
}
if (this.chargeTicks > 0) {
if (--this.chargeTicks === 0) {
// spawn the orb
const location = Vector.add(
Vector.mult(Vector.normalise(Vector.sub(this.bossPos, this.position)), this.radius * 3),
this.position
)
energyTransferBall(location, this.bossPos, parentBoss, this.energy)
this.energy = 0
}
// create a beam and aim it at the bossPos
ctx.beginPath()
ctx.moveTo(this.position.x, this.position.y)
ctx.lineTo(this.bossPos.x, this.bossPos.y)
ctx.lineWidth = 10 + Math.abs(Math.sin((simulation.cycle + this.seed) / 100)) * 5
ctx.strokeStyle = `rgb(${(
Math.abs(Math.sin((simulation.cycle + this.seed + 100) / 100)) * 255
).toFixed(3)}, 204, 255)`
ctx.setLineDash([125 * Math.random(), 125 * Math.random()])
ctx.stroke()
ctx.setLineDash([])
}
// generate (0.15 * difficulty / 4)% energy per tick
if (this.energy < 1) this.energy += 0.0015 * (simulation.difficulty / 4)
// draw energy bar
drawEnergyBar(this)
}
me.onDeath = function () {
// remove itself from the list
const beacons = parentBoss.energyBeacons
beacons.splice(beacons.indexOf(this), 1)
// explode with the strength of its energy!
this.alive = false // to prevent retriggering infinitly
b.explosion(this.position, this.energy * this.radius * 15)
// when it dies, it reduces some of the boss' energy
parentBoss.energy -= 0.025
// and stuns it
mobs.statusStun(parentBoss, 70 + ~~(100 / simulation.difficulty))
}
}
function geneticSeeker(x, y, genome, parentBoss) {
// special bullets that get score based on their performance.
mobs.spawn(x, y, genome.vertexCount, genome.size, '#' + genome.color.map(it => (~~it).toString(16)).join(''))
const me = mob[mob.length - 1]
// apply genome
Matter.Body.setDensity(me, genome.density)
me.accelMag = genome.speed
// apply vertex offset
for (let i = 0; i < me.vertices.length; i++) {
const vertex = me.vertices[i]
const offset = genome.vertexOffset[i]
if (!offset) console.log(genome, me)
vertex.x += offset.x
vertex.y += offset.y
}
me.stroke = 'transparent'
Matter.Body.setDensity(me, 0.00001) //normal is 0.001
// increased if the orb done things that are deemed successful
me.score = 30
me.timeLeft = 9001 / 9
me.accelMag = 0.00017 * simulation.accelScale //* (0.8 + 0.4 * Math.random())
me.frictionAir = 0.01
me.restitution = 0.5
me.leaveBody = false
me.isDropPowerUp = false
me.isBadTarget = true
me.isMobBullet = true
me.showHealthBar = false
me.collisionFilter.category = cat.mobBullet
me.collisionFilter.mask = cat.player | cat.map | cat.body | cat.bullet
me.do = function () {
this.alwaysSeePlayer()
this.attraction()
this.timeLimit()
if (Matter.Query.collides(this, map).length > 0) {
// colliding with the map gives a score reduction, 0.5 per tick
this.score -= 0.5
}
// default score is slowly reduced every tick to give mobs that reached the player faster a benefit
this.score -= 0.05
if (this.score < 0) {
this.alive = false // no point continuing if this orb is that bad! Silent death
}
// give a bonus if some projectile is nearby or the mouse position is close (like laser pointing)
// if a mob survives this for long, then it gets a score benefit.
const bulletCloseToOrb = bullet.some(it => dist2(this.position, it.position) < 10000 /* 100 ^ 2 */)
// player shoots and aims close
const mouseCloseToOrb = dist2(this.position, simulation.mouseInGame) < 10000 && input.fire
if (bulletCloseToOrb || mouseCloseToOrb) {
this.score += 1
}
// die if too far from the boss... It would be incredibly difficult to dodge otherwise
if (dist2(this.position, parentBoss.position) > 2000 * 2000) {
this.alive = false
}
// DEBUG score printer
// ctx.font = '48px sans-serif'
// ctx.fillStyle = 'rgba(252, 0, 143, 1)'
// ctx.fillText(~~this.score, this.position.x - this.radius, this.position.y - this.radius)
}
me.onHit = function () {
// hitting the player gives a 50 points score bonus
this.score += 50
this.score += this.mass * 2 // bigger mass = bigger damage, add that too
// a marker for later
this.hitPlayer = true
this.explode(this.mass)
}
me.onDeath = function () {
if (!this.hitPlayer) {
// if it didn't hit the player, give it a score based on its distance
this.score += 10000 / this.distanceToPlayer()
}
// 3% chance to drop ammo
if (Math.random() > 0.97) {
powerUps.spawn(this.position.x, this.position.y, 'ammo', true)
}
parentBoss.deadOrbs.push({
genome: genome,
score: this.score
})
}
}
function geneticBoss(x, y, radius = 130, spawnBossPowerUp = true) {
// a modified orb shooting boss that evolves its orbs.
// the way this boss works is different from the regular orb shooting boss,
// because the orbs have evolving properties via a "machine learning" scoring algorithm.
const MAX_BEACONS = Math.round(3 + Math.random() * simulation.difficulty / 3)
mobs.spawn(x, y, 8, radius, 'rgb(83, 32, 58)')
let me = mob[mob.length - 1]
me.isBoss = true
me.accelMag = 0.0001 * simulation.accelScale
me.fireFreq = Math.floor((330 * simulation.CDScale) / simulation.difficulty)
me.frictionStatic = 0
me.friction = 0
me.frictionAir = 0.02
me.memory = (420 / 69) * 42 // 🧌
me.repulsionRange = 1000000
me.energyBeacons = []
me.activeBalls = 0
// starts by random, or by the stored genomes if they exist
const init = () => ({
genome: generateGenome(),
score: 0
})
me.fittestOrbs = (localStorage && localStorage.genome) ? JSON.parse(localStorage.genome) : [init(), init(), init()] // best genomes so far. Size of three
// when an orb died it's moved here. When a new spawn cycle starts, their scores get calculated
// and they get put in the fittest orbs array, if they are better than the old ones.
me.deadOrbs = []
me.energy = 1
// this boss has no orbitals, because it's not meant to ever attack on its own
me.damageReduction = 0.25 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1)
// has a shield and sustains that shield
spawn.shield(me, x, y, Infinity)
me.fireFreq = 30
me.ionizeFreq = 20
me.ionized = []
me.laserRange = radius * 4
Matter.Body.setDensity(me, 0.0022 * 4 + 0.0002 * Math.sqrt(simulation.difficulty)) //extra dense //normal is 0.001 //makes effective life much larger
me.onDeath = function () {
if (spawnBossPowerUp) {
powerUps.spawnBossPowerUp(this.position.x, this.position.y)
const amount = ~~(5 * Math.random() * simulation.difficulty / 10) * 2
for (let i = 0; i < amount; i++) {
powerUps.spawn(this.position.x, this.position.y, 'ammo', true)
if (Math.random() > 0.7) {
powerUps.spawn(this.position.x, this.position.y, 'heal', true)
}
}
}
// keep the best genome and use it next fight...
if (localStorage) {
localStorage.setItem("genome", JSON.stringify(this.fittestOrbs))
}
// stop spawning barrels
bossInit = false
}
me.onDamage = function () { }
me.spawnBeacon = function () {
// the vertex to spawn the beacon from
const vert = this.vertices[~~(Math.random() * this.vertices.length)]
// the position should be a little to the side to prevent crashing into the boss
// TODO check for collisions with the wall
const spawnPos = Vector.add(vert, Vector.mult(Vector.normalise(Vector.sub(this.position, vert)), -60))
// some velocity
const velocity = Vector.mult(Vector.normalise(Vector.sub(this.position, vert)), -5)
energyBeacon(spawnPos.x, spawnPos.y, this) // spawn the beacon, a bit ahead
const beacon = mob[mob.length - 1]
this.energyBeacons.push(beacon)
Matter.Body.setVelocity(beacon, {
x: this.velocity.x + velocity.x,
y: this.velocity.y + velocity.y
})
}
me.spawnOrbs = function () {
Matter.Body.setAngularVelocity(this, 0.11)
// sort the vertices by the distance to the player
const sorted = [...this.vertices].sort(dist2)
// spawn the bullets based on how close they are to the player.
// the way it works is it picks the fittest three orbs and clones them.
// but start by taking old mobs and checking if they are better than the new ones
let next
while ((next = this.deadOrbs.pop())) {
// material costs are calculated as a contra to the score
const cost = calculateGenomeCost(next.genome) * 500 // normalize via multiplication
const totalScore = next.score - cost
// try to insert itself into the best orbs, if it can
for (let i = 0; i < this.fittestOrbs.length; i++) {
const fitEntry = this.fittestOrbs[i]
if (fitEntry.score < totalScore) {
this.fittestOrbs[i] = next
break
}
}
}
// finally sort them using their score
this.fittestOrbs.sort((a, b) => a.score - b.score)
// only take the genome, the score doesn't matter here
const bestOrbs = this.fittestOrbs.map(it => it.genome)
for (let vertex of sorted) {
// pick a random fit orb and try to spawn it. If the cost is too high, it'll attempt
// to generate a new random orb instead. If that orb is too expensive too, just ignore this vertex.
// the evolution part comes here, as the genome is mutated first.
let randGenome = mutateGenome(bestOrbs[~~(Math.random() * bestOrbs.length)])
const cost = calculateGenomeCost(randGenome) * 2
if (this.energy - cost < 0) {
// okay this orb is too expensive for the boss to spawn,
// make a new orb from scratch
randGenome = generateGenome()
const secondCost = calculateGenomeCost(randGenome)
if (this.energy - secondCost < 0) {
// that was too expensive too, heh
continue
}
} else {
// alright the boss can afford that
this.energy -= Math.abs(cost) // TODO: Fix this, why the heck can it even be negative??
}
geneticSeeker(vertex.x, vertex.y, randGenome, this)
// give the bullet a rotational velocity as if they were attached to a vertex
const velocity = Vector.mult(
Vector.perp(Vector.normalise(Vector.sub(this.position, vertex))),
-10
)
Matter.Body.setVelocity(mob[mob.length - 1], {
x: this.velocity.x + velocity.x,
y: this.velocity.y + velocity.y
})
}
}
me.do = function () {
this.seePlayerCheck()
this.checkStatus()
this.attraction()
this.repulsion()
// draw laser arcs if it sees the player
this.harmZone()
//
const regularChance = Math.random() > 0.99
const biggerChance = Math.random() > 0.95 && this.energy > 0.25
// start by making sure there is always at least one beacon
if (this.energyBeacons.length === 0) {
this.spawnBeacon()
}
// then, spawn some energy beacons if there are less than the maximum.
// small chance if there's no energy, bigger chance if there is at least 10% (which is drained)
if ((this.energyBeacons.length < MAX_BEACONS && biggerChance) || regularChance) {
if (biggerChance) {
// if the spawn was a selection of bigger chance, reduce 10% energy
this.energy -= 0.10
}
this.spawnBeacon()
}
// then, spawn genetic seekers
if (this.seePlayer.recall && !(simulation.cycle % this.fireFreq)) {
// fire a bullet from each vertex if there's enough energy
if (this.energy > 0.15) {
this.spawnOrbs()
}
}
if (this.energy > 1) {
// clean excess energy
this.energy -= 0.003
} else {
// or slowly generate energy
this.energy += 0.001
}
// the boss will ionize every bullet in its radius, but that will cause its energy to deplete
if (!(simulation.cycle % this.ionizeFreq)) {
for (let i = 0; i < bullet.length; i++) {
const it = bullet[i]
// if it's not a bot and it's close
if (!it.botType && dist(this.position, it.position) < this.laserRange) {
// add it to the ionized list
this.ionized.push({
target: it,
ticks: 0
})
}
}
}
for (let i = 0; i < this.ionized.length; i++) {
const entry = this.ionized[i]
// skip if there's not enough energy
if (this.energy <= 0) break
// terminate if it's no longer in the radius
if (dist(this.position, entry.target.position) > this.laserRange) {
this.ionized.splice(i, 1)
continue
}
// terminate after some ticks
if (++entry.ticks === 10) {
entry.target.endCycle = 0
// draw nice popping graphics
simulation.drawList.push({
x: entry.target.position.x,
y: entry.target.position.y,
radius: 5,
color: '#f24',
time: ~~(Math.random() * 20 + 10)
})
// and remove
this.ionized.splice(i, 1)
continue
}
// draw line
ctx.beginPath()
ctx.moveTo(this.position.x, this.position.y)
ctx.lineTo(entry.target.position.x, entry.target.position.y)
ctx.lineWidth = 7
ctx.strokeStyle = `rgb(${60 - entry.ticks * 2}, 50, 50)`
ctx.stroke()
// reduce energy, as it's hard to ionize
this.energy -= entry.target.mass / 25
}
// if it has energy, shield itself and drain energy
if (!this.isShielded && this.energy > 0.5) {
spawn.shield(this, this.position.x, this.position.y, Infinity)
this.energy -= 0.25
}
drawEnergyBar(this)
// change fill color
this.fill = `rgb(${((Math.sin(simulation.cycle / 100) + 1) / 2) * 100}, 32, 58)`
}
// start by spawning several beacons to gain initial energy
const amount = Math.ceil(2 + Math.random() * simulation.difficulty / 5)
for (let i = 0; i < amount; i++)
me.spawnBeacon()
}
// LEVEL SETUP
spawn.mapRect(level.enter.x, level.enter.y + 20, 100, 20) //don't change this
level.exit.x = 5700 //you exit at x
level.exit.y = -130 //you exit at y
spawn.mapRect(5800, -110, -100, 10)
level.defaultZoom = 2000 //how far out you want the image to be zoomed at (lower = zoom in, higher = zoom out)
simulation.zoomTransition(level.defaultZoom) //makes the level transition to have the zoom at the start of a level
document.body.style.backgroundColor = 'hsl(138, 3%, 74%)' //sets background color
//LEVEL STRUCTURE
spawn.mapRect(-3100, -100, 9200, 100)
spawn.mapRect(-3100, -600, 100, 500)
spawn.mapRect(-3100, -600, 1100, 100)
spawn.mapRect(-2100, -3100, 100, 2700)
spawn.mapRect(4800, -3100, 100, 2600)
spawn.mapRect(4800, -600, 1300, 100)
spawn.mapRect(6000, -600, 100, 600)
spawn.mapRect(400, -200, 2000, 100)
spawn.mapRect(600, -300, 1600, 100)
spawn.mapRect(800, -400, 1200, 100)
spawn.mapRect(1000, -500, 800, 100)
spawn.mapRect(1200, -600, 400, 100)
// roof
spawn.mapRect(-2100, -4350, 650, 1250)
spawn.mapRect(-1300, -4350, 1250, 1250)
spawn.mapRect(100, -4350, 1225, 1250)
spawn.mapRect(1475, -4350, 1325, 1250)
spawn.mapRect(2950, -4350, 1325, 1250)
spawn.mapRect(4425, -4350, 475, 1250)
// arc
// spawn.mapVertex(1400, -892, '700, -800, 700, -900, 1000, -1000, 1800, -1000, 2100, -900, 2100, -800')
//PLATFORMS
platformShadow(-1200, -500, 300, 100, shadows)
platformShadow(-400, -700, 300, 100, shadows)
platformShadow(400, -900, 300, 100, shadows)
platformShadow(-2000, -800, 300, 100, shadows)
platformShadow(-1000, -1000, 300, 100, shadows)
platformShadow(-400, -1300, 300, 100, shadows)
platformShadow(-1600, -1300, 300, 100, shadows)
platformShadow(-1300, -1600, 300, 100, shadows)
platformShadow(-2000, -1700, 300, 100, shadows)
platformShadow(-700, -1800, 300, 100, shadows)
platformShadow(-1500, -2100, 300, 100, shadows)
platformShadow(-600, -2200, 300, 100, shadows)
platformShadow(-2000, -2500, 300, 100, shadows)
platformShadow(-1100, -2400, 300, 100, shadows)
platformShadow(-500, -2700, 300, 100, shadows)
platformShadow(100, -2400, 300, 100, shadows)
platformShadow(700, -2700, 300, 100, shadows)
platformShadow(3700, -500, 300, 100, shadows)
platformShadow(2900, -700, 300, 100, shadows)
platformShadow(2100, -900, 300, 100, shadows)
platformShadow(4500, -800, 300, 100, shadows)
platformShadow(3500, -1000, 300, 100, shadows)
platformShadow(4100, -1300, 300, 100, shadows)
platformShadow(2900, -1300, 300, 100, shadows)
platformShadow(3800, -1600, 300, 100, shadows)
platformShadow(4500, -1700, 300, 100, shadows)
platformShadow(3200, -1800, 300, 100, shadows)
platformShadow(4000, -2100, 300, 100, shadows)
platformShadow(3100, -2200, 300, 100, shadows)
platformShadow(4500, -2500, 300, 100, shadows)
platformShadow(3600, -2400, 300, 100, shadows)
platformShadow(3000, -2700, 300, 100, shadows)
platformShadow(2400, -2400, 300, 100, shadows)
platformShadow(1800, -2700, 300, 100, shadows)
// cages
cage(-1492, -1200, 100, cages)
cage(-875, -2300, 300, cages)
cage(-1600, -3100, 1000, cages)
cage(225, -2300, 1000, cages)
cage(-750, -3100, 700, cages)
cage(-625, -1700, 1200, cages)
cage(2200, -3100, 500, cages)
cage(3275, -1700, 500, cages)
cage(3650, -900, 300, cages)
cage(2500, -2300, 300, cages)
cage(3625, -2300, 300, cages)
cage(3875, -1500, 300, cages)
cage(4025, -3100, 300, cages)
// boss cage
platformShadow(1275, -2150, 250, 100, shadows)
cage(1400, -2050, 500, cages, 'starter', true)
map[map.length] = Bodies.trapezoid(1400, -2193, 250, 100, 0.5)
//DEBRIS
//idk just put the debris wherever you want
spawn.debris(-550, -225, 100)
spawn.debris(-1150, -1725, 75)
spawn.debris(-275, -1400, 50)
spawn.debris(2850, -2075, 150)
spawn.debris(4250, -2250, 150)
//BOSS
// geneticBoss(1400, -3800)
anotherBoss(0, 0) //will only spawn historyBoss if there is an additional boss
},
stereoMadness() {
simulation.makeTextLog(`<strong>stereoMadness</strong> by <span class='color-var'>Richard0820</span>`);
let totalCoin = 0;
const hunter = function (x, y, radius = 30) { //doesn't stop chasing until past 105000
mobs.spawn(x, y, 6, radius, "black");
let me = mob[mob.length - 1];
me.stroke = "transparent";
me.collisionFilter.mask = cat.player | cat.bullet;
me.accelMag = 0.0006 * Math.min(simulation.difficulty + 1, 4);
me.showHealthBar = false;
me.isUnblockable = true;
me.isShielded = true;
me.memory = Infinity;
me.seeAtDistance2 = Infinity;
Matter.Body.setDensity(me, 1)
simulation.makeTextLog(`<b style="color: #3498DB;">Ω:</b><em style="color: #141414;"><b> Intruder Detected</b></em>`);
me.boost = 10;
me.do = function () {
if (me.boost == 1 && m.fieldMode == 3 || m.fieldMode == 9 && me.boost == 1) {
me.accelMag *= 1.5;
me.boost--;
}
this.attraction();
this.checkStatus();
this.repelBullets();
this.locatePlayer();
this.alwaysSeePlayer()
if (player.position.x > 105000) {
this.death()
}
};
me.onHit = function () {
for (let i = 0; i < 10; i++) {
spawn.spawns(this.position.x + Math.random() * 1000 - Math.random() * 1000, this.position.y - Math.random() * 1000)
}
}
}
const coin = function (x, y, radius = 50) {
mobs.spawn(x, y, 40, radius, "yellow");
let me = mob[mob.length - 1];
me.stroke = "#00000055"
me.isShielded = true;
me.leaveBody = false;
me.isBadTarget = true;
me.isUnblockable = true;
me.isDropPowerUp = false;
me.showHealthBar = false;
me.collisionFilter.category = 0;
Matter.Body.setDensity(me, 0.0045);
me.onDeath = function () {
totalCoin++;
};
me.damageReduction = 0.35 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1)
me.do = function () {
ctx.save()
ctx.translate(this.position.x, this.position.y)
ctx.rotate(Math.PI / 2 + 0.5)
ctx.strokeStyle = "#000000";
ctx.beginPath()
ctx.arc(0, 0, 30, -1, Math.PI, false)
ctx.moveTo(20, -5)
ctx.arc(0, 0, 20, -1, Math.PI, false)
ctx.lineWidth = 5;
ctx.stroke()
ctx.restore()
if (!simulation.isTimeSkipping) {
const sine = Math.sin(simulation.cycle * 0.015)
this.radius = 50 * (1 + 0.1 * sine)
const sub = Vector.sub(player.position, this.position)
const mag = Vector.magnitude(sub)
const force = Vector.mult(Vector.normalise(sub), 0.000000003)
if (mag < this.radius) { //heal player when inside radius
if (m.health < 0.7) {
m.damage(-0.001);
} else if (m.health == 0.7 || m.health > 0.7) {
this.death()
}
ctx.strokeStyle = "#00FFDD55";
ctx.beginPath();
ctx.arc(m.pos.x, m.pos.y, 34, 0, 2 * Math.PI);
ctx.lineWidth = 6;
ctx.stroke();
}
ctx.beginPath();
ctx.arc(this.position.x, this.position.y, this.radius + 15, 0, 2 * Math.PI);
ctx.strokeStyle = "#000"
ctx.lineWidth = 1;
ctx.stroke();
};
}
}
const spike = function (x, y, angle = Math.PI * 0.5, radius = 50) {
mobs.spawn(x, y, 3, radius, "#454545");
let me = mob[mob.length - 1];
me.stroke = "transparent";
me.isDropPowerUp = false;
me.showHealthBar = false;
Matter.Body.setDensity(me, 50)
me.collisionFilter.mask = cat.player | cat.mob | cat.bullet;
me.constraint = Constraint.create({
pointA: {
x: me.position.x,
y: me.position.y
},
bodyB: me,
stiffness: 0,
damping: 0
});
me.do = function () {
if (this.health < 1) {
this.health += 0.001; //regen
simulation.drawList.push({
x: this.position.x,
y: this.position.y,
radius: this.radius / 1.5,
color: `rgba(0, 255, 20, ${Math.random() * 0.1})`,
time: simulation.drawTime
});
}
this.checkStatus();
Matter.Body.setAngle(me, angle);
};
me.onHit = function () {
m.damage(0.01) //extra damage
me.collisionFilter.mask = 0;
setTimeout(() => {
me.collisionFilter.mask = cat.player | cat.mob | cat.bullet;
}, 1000);
}
me.onDeath = function () {
tech.addJunkTechToPool(0.1)
}
Composite.add(engine.world, me.constraint);
}
function drawStar(cx, cy, spikes, outerRadius, innerRadius) {
outerRadius *= (1 + 0.1 * Math.sin(simulation.cycle * 0.15));
innerRadius *= (1 + 0.1 * Math.sin(simulation.cycle * 0.15));
var rot = Math.PI / 2 * 3;
var xs = cx;
var y = cy;
var step = Math.PI / spikes;
ctx.strokeSyle = "#000";
ctx.beginPath();
ctx.moveTo(cx, cy - outerRadius)
for (i = 0; i < spikes; i++) {
xs = cx + Math.cos(rot) * outerRadius;
y = cy + Math.sin(rot) * outerRadius;
ctx.lineTo(xs, y)
rot += step
xs = cx + Math.cos(rot) * innerRadius;
y = cy + Math.sin(rot) * innerRadius;
ctx.lineTo(xs, y)
rot += step
}
ctx.lineTo(cx, cy - outerRadius)
ctx.closePath();
ctx.lineWidth = 5;
ctx.strokeStyle = 'red';
ctx.stroke();
ctx.fillStyle = 'darkred';
ctx.fill();
}
const slimePit1 = level.hazard(7475, -75, 475, 100, 0.01)
const slimePit2 = level.hazard(11275, -75, 1450, 100, 0.01)
const slimePit3 = level.hazard(13400, -150, 3500, 200, 0.1)
const slimePit4 = level.hazard(20275, -400, 3475, 450, 0.01)
const slimePit5 = level.hazard(25300, -800, 20000, 650, 0.003)
const slimePit6 = level.hazard(47725, -425, 2500, 475, 0.01)
const slimePit7 = level.hazard(50975, -575, 4050, 650, 0.01)
const slimePit8 = level.hazard(54950, -950, 6650, 1050, 0.01)
const slimePit9 = level.hazard(63550, -75, 2150, 100, 0.01)
const slimePit10 = level.hazard(70875, -75, 1200, 100, 0.01)
const slimePit11 = level.hazard(72075, -50, 900, 75, 1)
const slimePit12 = level.hazard(75900, -75, 2575, 100, 0.01)
const slimePit13 = level.hazard(78475, -35, 2300, 70, 0.01)
const slimePit14 = level.hazard(82875, -75, 5100, 100, 0.1)
const drip1 = level.drip(32474, -2165, -800, 100);
const drip2 = level.drip(37750, -2165, -800, 100);
const drip3 = level.drip(43525, -2165, -800, 100);
const drip4 = level.drip(20475, -830, -375, 100);
const drip5 = level.drip(49960, -2315, -423, 200)
let textlogOne = 0;
let textlogTwo = 0;
let barThere = true;
let bar = document.createElement("div");
bar.style.cssText = `position: absolute; top: 80px; background-color: black; width: 80vw; z-index: 1; border-radius: 10px; height: 20px; left: 5em; right: 5em;`;
bar.innerHTML += `<div id="innerBar" style="height: 12px; border-radius: 10px; margin-top: 3px; margin-left: 4px; border: 1px solid gray;"></div>`
document.body.appendChild(bar);
let innerBar = document.getElementById("innerBar");
level.custom = () => {
level.exit.drawAndCheck();
if (barThere == true) {
innerBar.style.width = "calc(" + `${Math.max(0, Math.min(player.position.x / 1310, 80))}` + "vw - 10px)";
innerBar.style.backgroundColor = m.eyeFillColor;
}
if (m.pos.x > 25360 && textlogOne == 0) {
simulation.makeTextLog(`<div><em>A stong force pushes you forward...</em></div>`)
textlogOne++;
}
if (m.pos.x < -3000) {
Matter.Body.setVelocity(player, {
x: 99,
y: 19
});
if (textlogTwo == 0)
simulation.makeTextLog(`<div><em>A strong force pushes you away...</em></div>`);
textlogTwo++;
}
if (m.pos.y > 1055) {
Matter.Body.setPosition(player, { x: 0, y: -150 });
simulation.makeTextLog(`<div><em>There is nowhere to run...</em></div>`);
m.damage(0.1 * simulation.difficultyMode);
}
if (m.alive == false && barThere == true) {
document.body.removeChild(bar);
barThere = false;
}
ctx.beginPath()
ctx.lineWidth = 5;
ctx.strokeStyle = "#000000";
ctx.moveTo(40, -1000)
ctx.arc(0, -1000, 40, 0, 2 * Math.PI)
ctx.stroke()
ctx.fillStyle = "#FF00FF55"
ctx.fillRect(89750, -1300, 800, 200)
ctx.fillRect(89750, -200, 800, 200)
ctx.fillRect(92050, -200, 800, 200)
ctx.fillRect(92050, -1675, 800, 575)
ctx.fillRect(93950, -350, 200, 350);
ctx.fillRect(95100, -1350, 150, 375);
ctx.fillRect(100900, -1325, 1175, 250);
ctx.fillRect(100900, -225, 1200, 250);
ctx.fillRect(98725, -1325, 450, 150);
ctx.fillRect(98725, -125, 450, 150);
ctx.beginPath()
//lines!
ctx.lineWidth = 10;
ctx.strokeStyle = "#000000";
ctx.moveTo(7462.5, -250)
ctx.lineTo(7462.5, -170)
ctx.moveTo(7710, -330)
ctx.lineTo(7710, -250)
ctx.moveTo(7960, -420)
ctx.lineTo(7960, -320)
ctx.moveTo(13725, -250)
ctx.lineTo(13725, -180)
ctx.moveTo(14025, -350)
ctx.lineTo(14025, -280)
ctx.moveTo(14325, -450)
ctx.lineTo(14325, -380)
ctx.moveTo(14625, -550)
ctx.lineTo(14625, -480)
ctx.moveTo(14925, -650)
ctx.lineTo(14925, -580)
ctx.moveTo(15225, -750)
ctx.lineTo(15225, -680)
ctx.moveTo(15525, -850)
ctx.lineTo(15525, -780)
ctx.moveTo(15825, -950)
ctx.lineTo(15825, -880)
ctx.moveTo(16125, -1050)
ctx.lineTo(16125, -980)
ctx.moveTo(16425, -1150)
ctx.lineTo(16425, -1080)
ctx.moveTo(22600, -650)
ctx.lineTo(22600, -580)
ctx.moveTo(22800, -750)
ctx.lineTo(22800, -680)
ctx.moveTo(23000, -850)
ctx.lineTo(23000, -780)
ctx.moveTo(23200, -950)
ctx.lineTo(23200, -880)
ctx.moveTo(23400, -1050)
ctx.lineTo(23400, -980)
ctx.moveTo(23600, -1150)
ctx.lineTo(23600, -1080)
ctx.moveTo(29550, -1625)
ctx.lineTo(29550, -1425)
ctx.moveTo(32275, -2125)
ctx.lineTo(32275, -1925)
ctx.moveTo(33775, -2125)
ctx.lineTo(33775, -1925)
ctx.moveTo(32275, -350)
ctx.lineTo(32275, -550)
ctx.moveTo(33775, -350)
ctx.lineTo(33775, -550)
ctx.moveTo(35475, -650)
ctx.lineTo(35475, -450)
ctx.moveTo(37650, -2000)
ctx.lineTo(37650, -1800)
ctx.moveTo(39675, -400)
ctx.lineTo(39675, -600)
ctx.moveTo(40375, -500)
ctx.lineTo(40375, -700)
ctx.moveTo(41075, -600)
ctx.lineTo(41075, -800)
ctx.moveTo(43625, -1925)
ctx.lineTo(43625, -1725)
ctx.moveTo(50975, -1125)
ctx.lineTo(50975, -925)
ctx.moveTo(51387.5, -1325)
ctx.lineTo(51387.5, -1125)
ctx.moveTo(51787.5, -1525)
ctx.lineTo(51787.5, -1325)
ctx.moveTo(52187.5, -1725)
ctx.lineTo(52187.5, -1525)
ctx.moveTo(52587.5, -1725)
ctx.lineTo(52587.5, -1925)
ctx.moveTo(52987.5, -2125)
ctx.lineTo(52987.5, -1925)
ctx.moveTo(53387.5, -2325)
ctx.lineTo(53387.5, -2125)
ctx.moveTo(53787.5, -2525)
ctx.lineTo(53787.5, -2325)
ctx.moveTo(54187.5, -2725)
ctx.lineTo(54187.5, -2525)
ctx.moveTo(54587.5, -2925)
ctx.lineTo(54587.5, -2725)
ctx.moveTo(54987.5, -3125)
ctx.lineTo(54987.5, -2925)
ctx.moveTo(57500, -1925)
ctx.lineTo(57650, -1925)
ctx.moveTo(57520, -1845)
ctx.lineTo(57650, -1845)
ctx.moveTo(57500, -1925)
ctx.lineTo(57895 + 300, -1925)
ctx.moveTo(57520, -1845)
ctx.lineTo(57895 + 300, -1845)
ctx.moveTo(58525, -1725)
ctx.lineTo(58525 + 125, -1725)
ctx.moveTo(58525, -1625)
ctx.lineTo(58525 + 125, -1625)
ctx.moveTo(59000, -1725)
ctx.lineTo(59150, -1725)
ctx.moveTo(59150, -1625)
ctx.lineTo(59000, -1625)
ctx.moveTo(70875, -200)
ctx.lineTo(70875, -100)
ctx.moveTo(63700, -200)
ctx.lineTo(63800, -200)
ctx.moveTo(64000, -200)
ctx.lineTo(64100, -200)
ctx.moveTo(64675, -200)
ctx.lineTo(64575, -200)
ctx.moveTo(64875, -200)
ctx.lineTo(64975, -200)
ctx.moveTo(65025, -300)
ctx.lineTo(64925, -300)
ctx.moveTo(65225, -300)
ctx.lineTo(65325, -300)
ctx.moveTo(71275, -200)
ctx.lineTo(71275, -300)
ctx.moveTo(71675, -300)
ctx.lineTo(71675, -400)
ctx.moveTo(72075, -400)
ctx.lineTo(72075, -500)
ctx.moveTo(72425, -325)
ctx.lineTo(72425, -425)
ctx.moveTo(72675, -200)
ctx.lineTo(72675, -300)
ctx.moveTo(72925, -75)
ctx.lineTo(72925, -175)
ctx.moveTo(75225, -100)
ctx.lineTo(75225, -200)
ctx.moveTo(76600, -125)
ctx.lineTo(76600, -225)
ctx.moveTo(76900, -200)
ctx.lineTo(76900, -300)
ctx.moveTo(77175, -275)
ctx.lineTo(77175, -375)
ctx.moveTo(77475, -350)
ctx.lineTo(77475, -450)
ctx.moveTo(85575, -275)
ctx.lineTo(85575, -375)
ctx.moveTo(86000, -275)
ctx.lineTo(86000, -375)
ctx.moveTo(86275, -275)
ctx.lineTo(86275, -375)
ctx.moveTo(86950, -425)
ctx.lineTo(86950, -525)
ctx.moveTo(89700, -175)
ctx.lineTo(89700, -75)
ctx.moveTo(89700, -1125)
ctx.lineTo(89700, -1225)
ctx.moveTo(90600, -1225)
ctx.lineTo(90600, -1125)
ctx.moveTo(90600, -100)
ctx.lineTo(90600, -175)
ctx.moveTo(92000, -100)
ctx.lineTo(92000, -175)
ctx.moveTo(92900, -100)
ctx.lineTo(92900, -175)
ctx.moveTo(92900, -1225)
ctx.lineTo(92900, -1125)
ctx.moveTo(94500, -1475)
ctx.lineTo(94500, -1575)
ctx.moveTo(94700, -1475)
ctx.lineTo(94700, -1575)
ctx.moveTo(94900, -1475)
ctx.lineTo(94900, -1575)
ctx.moveTo(96125, -1500)
ctx.lineTo(96125, -1575)
ctx.moveTo(96550, -1575)
ctx.lineTo(96550, -1500)
ctx.moveTo(97000, -1475)
ctx.lineTo(97000, -1575)
ctx.stroke()
ctx.beginPath()
ctx.strokeStyle = "#FFFFFF";
ctx.fillStyle = document.body.style.backgroundColor;
let cycles = Math.sin(simulation.cycle * 0.15)
ctx.moveTo(7482.5, -270)
ctx.arc(7462.5, -270, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI);
ctx.moveTo(7730, -350)
ctx.arc(7710, -350, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI);
ctx.moveTo(7980, -420)
ctx.arc(7960, -420, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI)
ctx.moveTo(13745, -180)
ctx.arc(13725, -180, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI)
ctx.moveTo(14045, -280)
ctx.arc(14025, -280, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI)
ctx.moveTo(14345, -380)
ctx.arc(14325, -380, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI)
ctx.moveTo(14645, -480)
ctx.arc(14625, -480, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI)
ctx.moveTo(14945, -580)
ctx.arc(14925, -580, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI)
ctx.moveTo(15245, -680)
ctx.arc(15225, -680, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI)
ctx.moveTo(15545, -780)
ctx.arc(15525, -780, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI)
ctx.moveTo(15845, -880)
ctx.arc(15825, -880, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI)
ctx.moveTo(16145, -980)
ctx.arc(16125, -980, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI)
ctx.moveTo(16445, -1080)
ctx.arc(16425, -1080, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI)
ctx.moveTo(22620, -580);
ctx.arc(22600, -580, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI)
ctx.moveTo(22820, -680);
ctx.arc(22800, -680, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI)
ctx.moveTo(23020, -780);
ctx.arc(23000, -780, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI)
ctx.moveTo(23220, -880);
ctx.arc(23200, -880, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI)
ctx.moveTo(23420, -980);
ctx.arc(23400, -980, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI)
ctx.moveTo(23620, -1080);
ctx.arc(23600, -1080, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI)
ctx.moveTo(29570, -1425)
ctx.arc(29550, -1425, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI)
ctx.moveTo(32295, -1925)
ctx.arc(32275, -1925, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI)
ctx.moveTo(33795, -1925)
ctx.arc(33775, -1925, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI)
ctx.moveTo(32295, -550)
ctx.arc(32275, -550, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI)
ctx.moveTo(33795, -550)
ctx.arc(33775, -550, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI)
ctx.moveTo(35495, -650)
ctx.arc(35475, -650, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI)
ctx.moveTo(37670, -1800)
ctx.arc(37650, -1800, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI)
ctx.moveTo(39695, -600)
ctx.arc(39675, -600, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI)
ctx.moveTo(40395, -700)
ctx.arc(40375, -700, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI)
ctx.moveTo(41095, -800)
ctx.arc(41075, -800, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI)
ctx.moveTo(43645, -1725)
ctx.arc(43625, -1725, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI)
ctx.moveTo(50995, -1125)
ctx.arc(50975, -1125, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI)
ctx.moveTo(51407.5, -1325)
ctx.arc(51387.5, -1325, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI)
ctx.moveTo(51807.5, -1525)
ctx.arc(51787.5, -1525, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI)
ctx.moveTo(52207.5, -1725)
ctx.arc(52187.5, -1725, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI)
ctx.moveTo(52607.5, -1925)
ctx.arc(52587.5, -1925, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI)
ctx.moveTo(53007.5, -2125)
ctx.arc(52987.5, -2125, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI)
ctx.moveTo(53407.5, -2325)
ctx.arc(53387.5, -2325, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI)
ctx.moveTo(53807.5, -2525)
ctx.arc(53787.5, -2525, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI)
ctx.moveTo(54207.5, -2725)
ctx.arc(54187.5, -2725, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI)
ctx.moveTo(54607.5, -2925)
ctx.arc(54587.5, -2925, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI)
ctx.moveTo(55007.5, -3125)
ctx.arc(54987.5, -3125, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI)
ctx.moveTo(57520, -1925)
ctx.arc(57500, -1925, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI)
ctx.moveTo(57520, -1845)
ctx.arc(57500, -1845, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI)
ctx.moveTo(58525, -1725)
ctx.arc(58505, -1725, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI)
ctx.moveTo(57895 + 300, -1925)
ctx.arc(57875 + 300, -1925, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI)
ctx.moveTo(57895 + 300, -1845)
ctx.arc(57875 + 300, -1845, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI)
ctx.moveTo(58525, -1625)
ctx.arc(58505, -1625, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI)
ctx.moveTo(58690 + 375 + 125, -1625)
ctx.arc(58670 + 375 + 125, -1625, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI)
ctx.moveTo(59190, -1725)
ctx.arc(59170, -1725, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI)
ctx.moveTo(70895, -200)
ctx.arc(70875, -200, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI)
ctx.moveTo(63720, -200)
ctx.arc(63700, -200, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI)
ctx.moveTo(64120, -200)
ctx.arc(64100, -200, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI)
ctx.moveTo(64595, -200)
ctx.arc(64575, -200, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI)
ctx.moveTo(64995, -200)
ctx.arc(64975, -200, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI)
ctx.moveTo(64945, -300)
ctx.arc(64925, -300, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI)
ctx.moveTo(65345, -300)
ctx.arc(65325, -300, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI)
ctx.moveTo(71295, -300)
ctx.arc(71275, -300, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI)
ctx.moveTo(71695, -400)
ctx.arc(71675, -400, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI)
ctx.moveTo(72095, -500)
ctx.arc(72075, -500, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI)
ctx.moveTo(72445, -425)
ctx.arc(72425, -425, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI)
ctx.moveTo(72695, -300)
ctx.arc(72675, -300, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI)
ctx.moveTo(72945, -175)
ctx.arc(72925, -175, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI)
ctx.moveTo(75245, -200)
ctx.arc(75225, -200, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI)
ctx.moveTo(76620, -125)
ctx.arc(76600, -125, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI)
ctx.moveTo(76920, -200)
ctx.arc(76900, -200, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI)
ctx.moveTo(77195, -275)
ctx.arc(77175, -275, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI)
ctx.moveTo(77495, -350)
ctx.arc(77475, -350, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI)
ctx.moveTo(85595, -375)
ctx.arc(85575, -375, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI)
ctx.moveTo(86020, -375)
ctx.arc(86000, -375, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI)
ctx.moveTo(86295, -375)
ctx.arc(86275, -375, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI)
ctx.moveTo(86970, -525)
ctx.arc(86950, -525, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI)
ctx.moveTo(89720, -175)
ctx.arc(89700, -175, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI)
ctx.moveTo(89720, -1125)
ctx.arc(89700, -1125, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI)
ctx.moveTo(90620, -1125)
ctx.arc(90600, -1125, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI)
ctx.moveTo(90620, -175)
ctx.arc(90600, -175, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI)
ctx.moveTo(92020, -175)
ctx.arc(92000, -175, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI)
ctx.moveTo(92920, -175)
ctx.arc(92900, -175, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI)
ctx.moveTo(92920, -1125)
ctx.arc(92900, -1125, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI)
ctx.moveTo(94520, -1575)
ctx.arc(94500, -1575, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI)
ctx.moveTo(94720, -1575)
ctx.arc(94700, -1575, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI)
ctx.moveTo(94920, -1575)
ctx.arc(94900, -1575, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI)
ctx.moveTo(96145, -1575)
ctx.arc(96125, -1575, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI)
ctx.moveTo(96570, -1500)
ctx.arc(96550, -1500, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI)
ctx.moveTo(97020, -1575)
ctx.arc(97000, -1575, 20 * (1 + 0.1 * cycles), 0, 2 * Math.PI)
ctx.fill()
ctx.stroke()
slimePit1.query()
slimePit2.query()
slimePit3.query()
slimePit4.query()
slimePit5.query()
slimePit5.query()
slimePit6.query()
slimePit7.query()
slimePit8.query()
slimePit9.query()
slimePit10.query()
slimePit11.query()
slimePit12.query()
slimePit13.query()
slimePit14.query()
drip1.draw()
drip2.draw()
drip3.draw()
drip4.draw()
drip5.draw()
ctx.fillStyle = "rgba(0,255,255,0.9)"
ctx.fillRect(25325, -1375, 75, 400)
ctx.fillRect(46425, -1350, 100, 500)
ctx.fillRect(87925, -725, 75, 450)
/*
if (player.position.x < 46470) {
document.body.style.backgroundColor = "#DD00DD";
}
*/
if (player.position.x > 25360 && player.position.x < 46470) {
Matter.Body.setVelocity(player, {
x: player.velocity.x, //+ 0.2,
y: player.velocity.y,
});
if (input.up) {
Matter.Body.setVelocity(player, {
x: player.velocity.x,
y: player.velocity.y, //- 1,
});
}
document.body.style.backgroundColor = "#fb3310"
} else if (player.position.x > 46470 && player.position.x < 61675) {
document.body.style.backgroundColor = "#7704FF"
} else if (player.position.x > 9700 && player.position.x < 46470) {
document.body.style.backgroundColor = "#7704FF"
} else if (player.position.x > 61675 && player.position.x < 87950) {
document.body.style.backgroundColor = "#DD1111"
} else if (player.position.x > 87950) {
document.body.style.backgroundColor = "#7704FF"
}
if (m.pos.y > -200 && 20350 < m.pos.x && m.pos.x < 23635) {
Matter.Body.setVelocity(player, {
x: 0,
y: 0
});
Matter.Body.setPosition(player, {
x: 20250,
y: -1000
});
// move bots
for (let i = 0; i < bullet.length; i++) {
if (bullet[i].botType) {
Matter.Body.setPosition(bullet[i], Vector.add(player.position, {
x: 250 * (Math.random() - 0.5),
y: 250 * (Math.random() - 0.5)
}));
Matter.Body.setVelocity(bullet[i], {
x: 0,
y: 0
});
}
}
m.damage(0.1 * simulation.difficultyMode)
m.energy -= 0.1 * simulation.difficultyMode
}
if (m.pos.y > -150 && m.pos.x > 47770 && m.pos.x < 50130) {
Matter.Body.setVelocity(player, {
x: 0,
y: 0
});
Matter.Body.setPosition(player, {
x: 47640,
y: -900
});
// move bots
for (let i = 0; i < bullet.length; i++) {
if (bullet[i].botType) {
Matter.Body.setPosition(bullet[i], Vector.add(player.position, {
x: 250 * (Math.random() - 0.5),
y: 250 * (Math.random() - 0.5)
}));
Matter.Body.setVelocity(bullet[i], {
x: 0,
y: 0
});
}
}
m.damage(0.1 * simulation.difficultyMode)
m.energy -= 0.1 * simulation.difficultyMode
}
if (m.pos.y > -150 && 50975 < m.pos.x && m.pos.x < 54925) {
Matter.Body.setVelocity(player, {
x: 0,
y: 0
});
Matter.Body.setPosition(player, {
x: 50965,
y: -1100
});
// move bots
for (let i = 0; i < bullet.length; i++) {
if (bullet[i].botType) {
Matter.Body.setPosition(bullet[i], Vector.add(player.position, {
x: 250 * (Math.random() - 0.5),
y: 250 * (Math.random() - 0.5)
}));
Matter.Body.setVelocity(bullet[i], {
x: 0,
y: 0
});
}
}
m.damage(0.1 * simulation.difficultyMode)
m.energy -= 0.1 * simulation.difficultyMode
}
if (m.pos.y > -150 && 55025 < m.pos.x && m.pos.x < 57675) {
Matter.Body.setVelocity(player, {
x: 0,
y: 0
});
Matter.Body.setPosition(player, {
x: 55148,
y: -3072
});
// move bots
for (let i = 0; i < bullet.length; i++) {
if (bullet[i].botType) {
Matter.Body.setPosition(bullet[i], Vector.add(player.position, {
x: 250 * (Math.random() - 0.5),
y: 250 * (Math.random() - 0.5)
}));
Matter.Body.setVelocity(bullet[i], {
x: 0,
y: 0
});
}
}
m.damage(0.1 * simulation.difficultyMode)
m.energy -= 0.1 * simulation.difficultyMode
}
if (m.pos.y > -150 && 57875 < m.pos.x && m.pos.x < 58700) {
Matter.Body.setVelocity(player, {
x: 0,
y: 0
});
Matter.Body.setPosition(player, {
x: 57800,
y: -2200
});
// move bots
for (let i = 0; i < bullet.length; i++) {
if (bullet[i].botType) {
Matter.Body.setPosition(bullet[i], Vector.add(player.position, {
x: 250 * (Math.random() - 0.5),
y: 250 * (Math.random() - 0.5)
}));
Matter.Body.setVelocity(bullet[i], {
x: 0,
y: 0
});
}
}
m.damage(0.1 * simulation.difficultyMode)
m.energy -= 0.1 * simulation.difficultyMode
}
if (m.pos.y > -150 && 58875 < m.pos.x && m.pos.x < 61650) {
Matter.Body.setVelocity(player, {
x: 0,
y: 0
});
Matter.Body.setPosition(player, {
x: 58850,
y: -2025
});
// move bots
for (let i = 0; i < bullet.length; i++) {
if (bullet[i].botType) {
Matter.Body.setPosition(bullet[i], Vector.add(player.position, {
x: 250 * (Math.random() - 0.5),
y: 250 * (Math.random() - 0.5)
}));
Matter.Body.setVelocity(bullet[i], {
x: 0,
y: 0
});
}
}
m.damage(0.1 * simulation.difficultyMode)
m.energy -= 0.1 * simulation.difficultyMode
}
if (m.pos.y > -1677 && 104650 < m.pos.x && m.pos.x < 105000 && barThere == true) {
Matter.Body.setVelocity(player, {
x: 0,
y: 0
});
Matter.Body.setPosition(player, {
x: 132577,
y: -300
});
// move bots
for (let i = 0; i < bullet.length; i++) {
if (bullet[i].botType) {
Matter.Body.setPosition(bullet[i], Vector.add(player.position, {
x: 250 * (Math.random() - 0.5),
y: 250 * (Math.random() - 0.5)
}));
Matter.Body.setVelocity(bullet[i], {
x: 0,
y: 0
});
}
}
document.body.style.transitionDuration = "0ms";
document.body.style.backgroundColor = "#696969";
simulation.makeTextLog(`<div><em>You have earned: </em><b>` + Math.min(3, totalCoin) + `</b><em> tech</em></div>`)
if (barThere == true) { //only runs once
document.body.removeChild(bar);
for (let i = 0, len = Math.min(3, totalCoin); i < len; i++) powerUps.directSpawn(player.position.x, player.position.y, "tech");
barThere = false;
}
}
ctx.fillStyle = "#FFFFFF53"
ctx.fillRect(57645, -2055, 385, 85)
ctx.fillRect(58645, -1880, 385, 85)
//chains
ctx.strokeStyle = "#FF0000"
ctx.strokeRect(66975, -725, 25, 50)
ctx.strokeRect(67050, -725, 25, 50)
ctx.strokeRect(66975 + 1150, -725, 25, 50)
ctx.strokeRect(67050 + 1250, -725, 25, 50)
ctx.strokeRect(69162, -725, 25, 50)
ctx.strokeRect(69862, -725, 25, 50)
ctx.strokeRect(74412.5, -412.5, 25, 50)
ctx.strokeRect(74612.5, -412.5, 25, 50)
ctx.strokeRect(77962.5, -900, 25, 50)
ctx.strokeRect(78212.5, -775, 25, 50)
ctx.strokeRect(78462.5, -650, 25, 50)
ctx.strokeRect(81587.5, -725, 25, 50)
ctx.strokeRect(81687.5, -725, 25, 50)
ctx.strokeRect(81787.5, -725, 25, 50)
ctx.strokeRect(83037.5, -215, 25, 50)
ctx.strokeRect(83362.5, -215, 25, 50)
ctx.strokeRect(83687.5, -215, 25, 50)
ctx.strokeRect(84187.5, -315, 25, 50)
ctx.strokeStyle = "#FF000088"
ctx.strokeRect(66975, -850, 25, 50)
ctx.strokeRect(67050, -850, 25, 50)
ctx.strokeRect(66975 + 1150, -850, 25, 50)
ctx.strokeRect(67050 + 1250, -850, 25, 50)
ctx.strokeRect(69162, -850, 25, 50)
ctx.strokeRect(69862, -850, 25, 50)
ctx.strokeRect(74412.5, -525, 25, 50)
ctx.strokeRect(74612.5, -525, 25, 50)
ctx.strokeRect(77962.5, -985, 25, 50)
ctx.strokeRect(78212.5, -860, 25, 50)
ctx.strokeRect(78462.5, -735, 25, 50)
ctx.strokeRect(81587.5, -850, 25, 50)
ctx.strokeRect(81687.5, -850, 25, 50)
ctx.strokeRect(81787.5, -850, 25, 50)
ctx.strokeRect(83037.5, -315, 25, 50)
ctx.strokeRect(83362.5, -315, 25, 50)
ctx.strokeRect(83687.5, -315, 25, 50)
ctx.strokeRect(84187.5, -415, 25, 50)
ctx.strokeStyle = "#FF000044"
ctx.strokeRect(66975, -975, 25, 50)
ctx.strokeRect(67050, -975, 25, 50)
ctx.strokeRect(66975 + 1150, -975, 25, 50)
ctx.strokeRect(67050 + 1250, -975, 25, 50)
ctx.strokeRect(69162, -975, 25, 50)
ctx.strokeRect(69862, -975, 25, 50)
ctx.strokeRect(74412.5, -633, 25, 50)
ctx.strokeRect(74612.5, -633, 25, 50)
ctx.strokeRect(77962.5, -1075, 25, 50)
ctx.strokeRect(78212.5, -950, 25, 50)
ctx.strokeRect(78462.5, -825, 25, 50)
ctx.strokeRect(81587.5, -975, 25, 50)
ctx.strokeRect(81687.5, -975, 25, 50)
ctx.strokeRect(81787.5, -975, 25, 50)
ctx.strokeRect(83037.5, -415, 25, 50)
ctx.strokeRect(83362.5, -415, 25, 50)
ctx.strokeRect(83687.5, -415, 25, 50)
ctx.strokeRect(84187.5, -515, 25, 50)
ctx.strokeStyle = "#FF000011"
ctx.strokeRect(66975, -1100, 25, 50)
ctx.strokeRect(67050, -1100, 25, 50)
ctx.strokeRect(66975 + 1150, -1100, 25, 50)
ctx.strokeRect(67050 + 1250, -1100, 25, 50)
ctx.strokeRect(69162, -1100, 25, 50)
ctx.strokeRect(69862, -1100, 25, 50)
ctx.strokeRect(74412.5, -741, 25, 50)
ctx.strokeRect(74612.5, -741, 25, 50)
ctx.strokeRect(77962.5, -1165, 25, 50)
ctx.strokeRect(78212.5, -1040, 25, 50)
ctx.strokeRect(78462.5, -915, 25, 50)
ctx.strokeRect(81587.5, -1100, 25, 50)
ctx.strokeRect(81687.5, -1100, 25, 50)
ctx.strokeRect(81787.5, -1100, 25, 50)
ctx.strokeRect(83037.5, -515, 25, 50)
ctx.strokeRect(83362.5, -515, 25, 50)
ctx.strokeRect(83687.5, -515, 25, 50)
ctx.strokeRect(84187.5, -615, 25, 50)
ctx.stroke()
ctx.beginPath()
ctx.strokeStyle = "#FF0000"
ctx.moveTo(66987.5, -680)
ctx.lineTo(66987.5, -625)
ctx.moveTo(67062.5, -680)
ctx.lineTo(67062.5, -625)
ctx.moveTo(66987.5 + 1150, -680)
ctx.lineTo(66987.5 + 1150, -625)
ctx.moveTo(67062.5 + 1250, -680)
ctx.lineTo(67062.5 + 1250, -625)
ctx.moveTo(69175, -680)
ctx.lineTo(69175, -625)
ctx.moveTo(69875, -680)
ctx.lineTo(69875, -625)
ctx.moveTo(74425, -290)
ctx.lineTo(74425, -370)
ctx.moveTo(74625, -290)
ctx.lineTo(74625, -370)
ctx.moveTo(77975, -790)
ctx.lineTo(77975, -855)
ctx.moveTo(78225, -665)
ctx.lineTo(78225, -730)
ctx.moveTo(78475, -540)
ctx.lineTo(78475, -605)
ctx.moveTo(81600, -680)
ctx.lineTo(81600, -625)
ctx.moveTo(81700, -680)
ctx.lineTo(81700, -625)
ctx.moveTo(81800, -680)
ctx.lineTo(81800, -625)
ctx.moveTo(83050, -100)
ctx.lineTo(83050, -170)
ctx.moveTo(83375, -100)
ctx.lineTo(83375, -170)
ctx.moveTo(83700, -100)
ctx.lineTo(83700, -170)
ctx.moveTo(84200, -200)
ctx.lineTo(84200, -270)
ctx.stroke()
ctx.strokeStyle = "#FF000099"
ctx.moveTo(66987.5, -810)
ctx.lineTo(66987.5, -715)
ctx.moveTo(67062.5, -810)
ctx.lineTo(67062.5, -715)
ctx.moveTo(66987.5 + 1150, -810)
ctx.lineTo(66987.5 + 1150, -715)
ctx.moveTo(67062.5 + 1250, -810)
ctx.lineTo(67062.5 + 1250, -715)
ctx.moveTo(69175, -810)
ctx.lineTo(69175, -715)
ctx.moveTo(69875, -810)
ctx.lineTo(69875, -715)
ctx.moveTo(74425, -405)
ctx.lineTo(74425, -480)
ctx.moveTo(74625, -405)
ctx.lineTo(74625, -480)
ctx.moveTo(77975, -890)
ctx.lineTo(77975, -940)
ctx.moveTo(78225, -765)
ctx.lineTo(78225, -815)
ctx.moveTo(78475, -640)
ctx.lineTo(78475, -690)
ctx.moveTo(81600, -810)
ctx.lineTo(81600, -715)
ctx.moveTo(81700, -810)
ctx.lineTo(81700, -715)
ctx.moveTo(81800, -810)
ctx.lineTo(81800, -715)
ctx.moveTo(83050, -210)
ctx.lineTo(83050, -270)
ctx.moveTo(83375, -210)
ctx.lineTo(83375, -270)
ctx.moveTo(83700, -210)
ctx.lineTo(83700, -270)
ctx.moveTo(84200, -310)
ctx.lineTo(84200, -370)
ctx.stroke()
ctx.strokeStyle = "#FF000044"
ctx.moveTo(66987.5, -930)
ctx.lineTo(66987.5, -845)
ctx.moveTo(67062.5, -930)
ctx.lineTo(67062.5, -845)
ctx.moveTo(66987.5 + 1150, -930)
ctx.lineTo(66987.5 + 1150, -845)
ctx.moveTo(67062.5 + 1250, -930)
ctx.lineTo(67062.5 + 1250, -845)
ctx.moveTo(69175, -930)
ctx.lineTo(69175, -845)
ctx.moveTo(69875, -930)
ctx.lineTo(69875, -845)
ctx.moveTo(74425, -515)
ctx.lineTo(74425, -590)
ctx.moveTo(74625, -515)
ctx.lineTo(74625, -590)
ctx.moveTo(77975, -975)
ctx.lineTo(77975, -1040)
ctx.moveTo(78225, -850)
ctx.lineTo(78225, -915)
ctx.moveTo(78475, -725)
ctx.lineTo(78475, -790)
ctx.moveTo(81600, -930)
ctx.lineTo(81600, -845)
ctx.moveTo(81700, -930)
ctx.lineTo(81700, -845)
ctx.moveTo(81800, -930)
ctx.lineTo(81800, -845)
ctx.moveTo(83050, -305)
ctx.lineTo(83050, -370)
ctx.moveTo(83375, -305)
ctx.lineTo(83375, -370)
ctx.moveTo(83700, -305)
ctx.lineTo(83700, -370)
ctx.moveTo(84200, -405)
ctx.lineTo(84200, -470)
ctx.stroke()
ctx.strokeStyle = "#FF000022"
ctx.moveTo(66987.5, -1060)
ctx.lineTo(66987.5, -965)
ctx.moveTo(67062.5, -1060)
ctx.lineTo(67062.5, -965)
ctx.moveTo(66987.5 + 1150, -1060)
ctx.lineTo(66987.5 + 1150, -965)
ctx.moveTo(67062.5 + 1250, -1060)
ctx.lineTo(67062.5 + 1250, -965)
ctx.moveTo(69175, -1060)
ctx.lineTo(69175, -965)
ctx.moveTo(69875, -1060)
ctx.lineTo(69875, -965)
ctx.moveTo(74425, -620)
ctx.lineTo(74425, -712.5)
ctx.moveTo(74625, -620)
ctx.lineTo(74625, -712.5)
ctx.moveTo(77975, -1075)
ctx.lineTo(77975, -1120)
ctx.moveTo(78225, -950)
ctx.lineTo(78225, -995)
ctx.moveTo(78475, -825)
ctx.lineTo(78475, -870)
ctx.moveTo(81600, -1060)
ctx.lineTo(81600, -965)
ctx.moveTo(81700, -1060)
ctx.lineTo(81700, -965)
ctx.moveTo(81800, -1060)
ctx.lineTo(81800, -965)
ctx.moveTo(83050, -405)
ctx.lineTo(83050, -470)
ctx.moveTo(83375, -405)
ctx.lineTo(83375, -470)
ctx.moveTo(83700, -405)
ctx.lineTo(83700, -470)
ctx.moveTo(84200, -505)
ctx.lineTo(84200, -570)
ctx.stroke()
let star = 95525;
for (let i = 0; i < 3; i++) {
drawStar(star, -1540, 5, 40, 15);
star += 200;
}
drawStar(97375, -1540, 5, 25, 10)
drawStar(97675, -1540, 5, 25, 10)
drawStar(97975, -1540, 5, 25, 10)
drawStar(98275, -1540, 5, 25, 10)
drawStar(98575, -1540, 5, 25, 10)
};
var gradient = ctx.createLinearGradient(0, 0, 175, 0);
gradient.addColorStop(0, "#7704FF00");
gradient.addColorStop(1, "#00FFFF");
level.customTopLayer = () => {
if (player.position.x > 25360 && player.position.x < 46470 && player.position.y > -2348 || player.position.x > 87995 && player.position.x < 103950 && player.position.y > -1350) {
player.force.x += 0.0045
m.airControl = () => {
if (input.up) {
player.force.y -= 0.02
}
}
m.draw = () => {
ctx.save();
ctx.globalAlpha = (m.immuneCycle < m.cycle) ? 1 : 0.5
ctx.translate(player.position.x, player.position.y);
ctx.rotate(player.angle);
if (input.up) { //forward thrust drawing
ctx.rotate(`${Math.max(-0.5, Math.min(m.angle, 0.5))}`)
} else {
ctx.rotate(`${Math.max(0.5, Math.min(m.angle, -0.5))}`)
}
ctx.beginPath();
ctx.arc(0, 0, 30, 0, 2 * Math.PI);
ctx.fillStyle = m.bodyGradient
ctx.fill();
ctx.arc(15, 0, 4, 0, 2 * Math.PI);
ctx.strokeStyle = "#333";
ctx.lineWidth = 2;
ctx.stroke();
ctx.beginPath()
ctx.moveTo(30, 0)
ctx.lineTo(60, 10)
ctx.lineTo(60, 30)
ctx.lineTo(20, 40)
ctx.lineTo(0, 40)
ctx.lineTo(-50, 60)
ctx.lineTo(-50, 0)
ctx.lineTo(-40, -40)
ctx.lineTo(-40, -40)
ctx.lineTo(-30, 10)
ctx.lineTo(30, 10)
ctx.lineTo(30, 0)
ctx.fill();
ctx.moveTo(-50, 30)
ctx.lineTo(-60, 30)
ctx.lineTo(-60, 0)
ctx.lineTo(-50, 0)
ctx.fill()
ctx.stroke()
ctx.restore();
}
} else {
m.resetSkin()
m.airControl = () => {
if (input.up && m.buttonCD_jump + 20 < m.cycle && m.yOffWhen.stand > 23 && m.lastOnGroundCycle + 5 > m.cycle) m.jump()
if (m.buttonCD_jump + 60 > m.cycle && !(input.up) && m.Vy < 0) {
Matter.Body.setVelocity(player, {
x: player.velocity.x,
y: player.velocity.y * 0.94
});
}
if (input.left) {
if (player.velocity.x > -m.airSpeedLimit / player.mass / player.mass) player.force.x -= m.FxAir; // move player left / a
} else if (input.right) {
if (player.velocity.x < m.airSpeedLimit / player.mass / player.mass) player.force.x += m.FxAir; //move player right / d
}
}
}
ctx.fillStyle = `rgba(68, 68, 68, ${Math.max(0.1, Math.min((-1400 - m.pos.y) / -100, 0.99))})`;
ctx.fillRect(91900, -1675, 12050, 375)
ctx.save()
ctx.translate(104700, -1675);
ctx.fillStyle = gradient;
ctx.fillRect(0, 0, 175, 1675)
ctx.restore()
};
level.setPosToSpawn(0, -150); //spawn
level.defaultZoom = 1800 //default I think
simulation.zoomTransition(level.defaultZoom)
document.body.style.backgroundColor = "#0150FF";
document.body.style.transitionDuration = "1500ms"; //smoother transitions, so that people don't complain
level.exit.x = 133150;
level.exit.y = -260;
spawn.mapRect(level.exit.x, level.exit.y + 25, 100, 20);
//ground? I forgot
spawn.mapRect(-5000, 0, 110000, 1000);
//*mountains to prevent player from running away*
spawn.mapVertex(-5775, -1330, "0 0 1000 7000 -1000 7000");
spawn.mapVertex(-7000, -330, "0 0 1000 4000 -1000 4000");
spawn.mapVertex(-10000, -330, "0 0 1000 4000 -1000 4000");
spawn.mapVertex(-9000, -330, "0 0 1000 4000 -1000 4000");
spawn.mapVertex(-7500, -330, "0 0 1000 4000 -1000 4000");
spawn.mapRect(-10000, 0, 3500, 1000);
//spawn.mapRect(-10000, -10000, 1000, 11000);
//hunter(-600, -150)
//stage one --> 0%
//spikes
spike(3000, -30);
spike(5000, -30);
spike(4925, 0);
spike(7275, -30);
spike(7375, -30);
spike(9806, -30);
spike(9900, -30);
spike(12000, -130);
spike(13050, -255);
spike(17300, -1105);
spike(17400, -1105);
spike(17200, -1105);
spike(17500, -1105);
spike(18200, -1105);
spike(18100, -1105);
spike(18300, -1105);
spike(18400, -1105);
spike(19875, -1108);
spike(19975, -930);
spike(21000, -930);
spike(21725, -780);
spike(23600, -1230);
spawn.mapVertex(62000, -300, "0 1000 1000 1000 0 0");
//upside down spikes
let u = Math.PI * 1.5;
spike(18825, -1170, u);
spike(18925, -1170, u);
spike(19025, -1170, u);
spike(19125, -1170, u);
spike(19225, -1170, u);
spike(19325, -1170, u);
spike(49962.5, -2370, u);
spike(50062.5, -2370, u);
spike(50162.5, -2370, u);
spike(50262.5, -2370, u);
spike(50362.5, -2370, u);
//bottom of the flying part
spike(32375, -280);
spike(32475, -280);
spike(32575, -280);
spike(32675, -280);
spike(32775, -280);
spike(32875, -280);
spike(32975, -280);
spike(33075, -280);
spike(33175, -280);
spike(33275, -280);
spike(33375, -280);
spike(33475, -280);
spike(33575, -280);
spike(33675, -280);
spike(35575, -280);
spike(35675, -280);
spike(35775, -280);
spike(35875, -280);
spike(39775, -280);
spike(39875, -280);
spike(39975, -280);
spike(40075, -280);
spike(40175, -280);
spike(40275, -280);
spike(40475, -280);
spike(40575, -280);
spike(40675, -280);
spike(40775, -280);
spike(40875, -280);
spike(40975, -280);
//top of the flying part
spike(32375, -2193, u);
spike(32475, -2193, u);
spike(32575, -2193, u);
spike(32675, -2193, u);
spike(32775, -2193, u);
spike(32875, -2193, u);
spike(32975, -2193, u);
spike(33075, -2193, u);
spike(33175, -2193, u);
spike(33275, -2193, u);
spike(33375, -2193, u);
spike(33475, -2193, u);
spike(33575, -2193, u);
spike(33675, -2193, u);
spike(37750, -2193, u);
spike(37850, -2193, u);
spike(37950, -2193, u);
spike(38050, -2193, u);
spike(43025, -2193, u);
spike(43125, -2193, u);
spike(43225, -2193, u);
spike(43325, -2193, u);
spike(43425, -2193, u);
spike(43525, -2193, u);
spike(43725, -2193, u);
spike(43825, -2193, u);
spike(43925, -2193, u);
spike(44025, -2193, u);
spike(44125, -2193, u);
spike(44225, -2193, u);
spike(44325, -2193, u);
spike(44425, -2193, u);
spike(44525, -2193, u);
spike(44625, -2193, u);
spike(44725, -2193, u);
spike(44825, -2193, u);
//about 55% of the map
spike(63375, -30);
spike(63475, -30);
spike(65775, -30);
spike(65875, -30);
spike(66975, -30);
spike(67075, -30);
spike(66975, -500, u);
spike(67075, -500, u);
spike(68125, -30);
spike(68225, -30);
spike(68325, -30);
spike(68125, -500, u);
spike(68225, -500, u);
spike(68325, -500, u);
spike(69175, -500, u);
spike(69175, -30);
spike(69875, -500, u);
spike(69875, -30);
spike(70675, -30);
spike(70775, -30);
spike(73725, -30);
spike(73825, -30);
spike(74425, -195, u);
spike(74525, -195, u);
spike(74625, -195, u);
spike(75125, -30);
spawn.mapVertex(78725, -466, "0 50 100 50 50 0"); //ocd still triggers from -467
spike(79300, -180);
spike(80800, -30);
spike(80900, -30);
spike(81600, -30);
spike(81700, -30);
spike(81800, -30);
spike(81600, -500, u);
spike(81700, -500, u);
spike(81800, -500, u);
spike(93425, -1430);
spike(93525, -1430);
spike(85800, -305);
spike(86475, -305);
spike(87150, -455);
spike(94025, -1570, u);
spike(94125, -1570, u);
spike(94500, -1430);
spike(94700, -1430);
spike(94900, -1430);
spike(94600, -1400);
spike(94800, -1400);
spike(94212.5, -1675, u);
spike(94287.5, -1675, u);
//even more 90%
spike(95525, -1400)
spike(95525, -1675, u)
spike(95625, -1400)
spike(95625, -1675, u)
spike(95725, -1400)
spike(95725, -1675, u)
spike(95825, -1400)
spike(95825, -1675, u)
spike(95925, -1400)
spike(95925, -1675, u)
spike(96025, -1400)
spike(96225, -1400)
spike(96650, -1675, u)
spike(96900, -1400)
spike(97150, -1675, u)
spike(98900, -1400)
spike(96975, -155)
spike(97075, -155)
spike(97175, -155)
spike(97275, -155)
spike(97375, -155)
spike(97475, -155)
spike(96975, -1170, u)
spike(97075, -1170, u)
spike(97175, -1170, u)
spike(97275, -1170, u)
spike(97375, -1170, u)
spike(97475, -1170, u)
spike(98700, -1070, u)
spike(98800, -1070, u)
spike(98900, -1070, u)
spike(99000, -1070, u)
spike(99100, -1070, u)
spike(99200, -1070, u)
spike(98700, -230)
spike(98800, -230)
spike(98900, -230)
spike(99000, -230)
spike(99100, -230)
spike(99200, -230)
spike(98975, -1400)
spike(99375, -1675, u)
spike(99300, -1675, u)
spike(99575, -1675, u)
spike(100000, -1400)
//actual stuff
spawn.mapRect(7425, -175, 75, 175);
spawn.mapRect(7675, -250, 75, 250);
spawn.mapRect(7925, -325, 75, 325);
spawn.mapRect(10625, -100, 725, 100);
spawn.mapRect(11625, -100, 725, 100);
spawn.mapRect(12650, -225, 800, 225);
spawn.mapRect(13675, -300, 100, 50);
spawn.mapRect(13975, -400, 100, 50);
spawn.mapRect(14275, -500, 100, 50);
spawn.mapRect(14575, -600, 100, 50);
spawn.mapRect(14875, -700, 100, 50);
spawn.mapRect(15175, -800, 100, 50);
spawn.mapRect(15475, -900, 100, 50);
spawn.mapRect(15775, -1000, 100, 50);
spawn.mapRect(16075, -1100, 100, 50);
spawn.mapRect(16375, -1200, 100, 50);
spawn.mapRect(16625, -1075, 350, 100);
spawn.mapRect(16800, -1075, 1825, 1125);
spawn.mapRect(17250, -1225, 200, 50);
spawn.mapRect(18150, -1225, 200, 50);
spawn.mapRect(18550, -975, 1050, 1025);
spawn.mapRect(19525, -1075, 400, 1125);
spawn.mapRect(18775, -1275, 600, 75);
spawn.mapRect(19825, -900, 500, 950);
spawn.mapRect(20150, -900, 375, 125);
spawn.mapRect(20750, -900, 300, 50);
spawn.mapRect(21225, -750, 550, 50);
spawn.mapRect(21950, -625, 450, 50);
spawn.mapRect(22550, -700, 100, 50);
spawn.mapRect(22750, -800, 100, 50);
spawn.mapRect(22950, -900, 100, 50);
spawn.mapRect(23150, -1000, 100, 50);
spawn.mapRect(23350, -1100, 100, 50);
spawn.mapRect(23550, -1200, 100, 50);
spawn.mapRect(23525, -975, 400, 100);
spawn.mapRect(23650, -975, 1825, 1025);
spawn.mapRect(23750, -2500, 625, 1125);
spawn.mapRect(24000, -2500, 1200, 1300);
spawn.mapRect(24900, -2500, 575, 1125);
spawn.mapRect(25425, -2500, 20000, 275);
spawn.mapRect(25425, -250, 20000, 300);
spawn.mapRect(29450, -2300, 200, 675);
spawn.mapRect(32225, -2225, 100, 100);
spawn.mapRect(33725, -2225, 100, 100);
spawn.mapRect(32225, -350, 100, 100);
spawn.mapRect(33725, -350, 100, 100);
spawn.mapRect(37600, -2225, 100, 225);
spawn.mapRect(35425, -475, 100, 225);
spawn.mapRect(39625, -400, 100, 150);
spawn.mapRect(40325, -500, 100, 250);
spawn.mapRect(41025, -600, 100, 350);
spawn.mapRect(43575, -2225, 100, 300);
spawn.mapRect(44875, -2500, 1675, 1225);
spawn.mapRect(44875, -950, 1675, 1000);
spawn.mapRect(46425, -825, 1400, 875);
spawn.mapRect(48075, -1100, 175, 1150);
spawn.mapRect(48575, -1300, 175, 1375);
spawn.mapRect(49075, -1500, 175, 1550);
spawn.mapRect(49575, -1700, 175, 975);
spawn.mapRect(49575, -500, 175, 550);
spawn.mapRect(50075, -1900, 175, 700);
spawn.mapRect(50075, -1000, 1000, 1000);
spawn.mapRect(49912.5, -2525, 500, 125);
spawn.mapRect(51300, -1200, 175, 1225);
spawn.mapRect(51700, -1400, 175, 1425);
spawn.mapRect(52100, -1600, 175, 1625);
spawn.mapRect(52500, -1800, 175, 1825);
spawn.mapRect(52900, -2000, 175, 2025);
spawn.mapRect(53300, -2200, 175, 2225);
spawn.mapRect(53700, -2400, 175, 2425);
spawn.mapRect(54100, -2600, 175, 2625);
spawn.mapRect(54500, -2800, 175, 2825);
spawn.mapRect(54900, -3000, 175, 3025);
spawn.mapRect(55050, -3000, 175, 75);
spawn.mapRect(55475, -2875, 250, 75);
spawn.mapRect(55900, -2625, 250, 75);
spawn.mapRect(56500, -2400, 375, 75);
spawn.mapRect(57200, -2200, 250, 75);
spawn.mapRect(57650, -2050, 375, 75);
spawn.mapRect(57650, -1970, 375, 1975);
spawn.mapRect(58650, -1875, 375, 75);
spawn.mapRect(58650, -1795, 375, 1975);
spawn.mapRect(59525, -1750, 175, 75);
spawn.mapRect(60125, -1600, 175, 75);
spawn.mapRect(60750, -1425, 175, 75);
spawn.mapRect(61250, -1225, 175, 75);
spawn.mapRect(61550, -1025, 225, 1125);
spawn.mapRect(63525, -100, 100, 100);
spawn.mapRect(63800, -225, 200, 50);
spawn.mapRect(64175, -100, 100, 100);
spawn.mapRect(64275, -100, 100, 100);
spawn.mapRect(64375, -100, 100, 100);
spawn.mapRect(64675, -225, 200, 50);
spawn.mapRect(65025, -325, 200, 50);
spawn.mapRect(65425, -100, 300, 100);
spawn.mapRect(66925, -650, 200, 120);
spawn.mapRect(68075, -650, 300, 120);
spawn.mapRect(69125, -650, 100, 120);
spawn.mapRect(69825, -650, 100, 120);
spawn.mapRect(70825, -100, 100, 100);
spawn.mapRect(71225, -200, 100, 200);
spawn.mapRect(71625, -300, 100, 300);
spawn.mapRect(72025, -400, 100, 400);
spawn.mapRect(72125, -400, 100, 50);
spawn.mapRect(72375, -325, 100, 50);
spawn.mapRect(72625, -200, 100, 50);
spawn.mapRect(72875, -75, 100, 50);
spawn.mapRect(74375, -300, 300, 75);
spawn.mapRect(75175, -100, 100, 100);
spawn.mapRect(75900, -150, 400, 50);
spawn.mapRect(76550, -250, 100, 50);
spawn.mapRect(76850, -325, 100, 50);
spawn.mapRect(77125, -400, 100, 50);
spawn.mapRect(77425, -475, 300, 50);
spawn.mapRect(77925, -400, 100, 100);
spawn.mapRect(78175, -275, 100, 100);
spawn.mapRect(78425, -150, 100, 100);
spawn.mapRect(78675, -50, 100, 100);
spawn.mapRect(77925, -800, 100, 100);
spawn.mapRect(78175, -675, 100, 100);
spawn.mapRect(78425, -550, 100, 100);
spawn.mapRect(78675, -450, 100, 100);
spawn.mapRect(78450, -100, 50, 125);
spawn.mapRect(79025, -150, 100, 50);
spawn.mapRect(79250, -150, 100, 50);
spawn.mapRect(79475, -150, 100, 50);
spawn.mapRect(79800, -225, 300, 50);
spawn.mapRect(80250, -150, 100, 50);
spawn.mapRect(80450, -100, 300, 50);
spawn.mapRect(81550, -650, 300, 120);
spawn.mapRect(82800, -100, 100, 100);
spawn.mapRect(82900, -100, 200, 50);
spawn.mapRect(83325, -100, 100, 50);
spawn.mapRect(83650, -100, 200, 50);
spawn.mapRect(83850, -100, 100, 100);
spawn.mapRect(83950, -200, 100, 200);
spawn.mapRect(84050, -200, 200, 50);
spawn.mapRect(84500, -350, 100, 50);
spawn.mapRect(84725, -250, 100, 50);
spawn.mapRect(84950, -150, 300, 50);
spawn.mapRect(85525, -275, 100, 50);
spawn.mapRect(85750, -275, 100, 50);
spawn.mapRect(85950, -275, 375, 50);
spawn.mapRect(86425, -275, 100, 50);
spawn.mapRect(86625, -275, 100, 50);
spawn.mapRect(86900, -425, 300, 50);
spawn.mapRect(87375, -275, 300, 50);
spawn.mapRect(87900, -300, 125, 300);
spawn.mapRect(87900, -1850, 125, 1150);
spawn.mapRect(87900, -1850, 17000, 175);
spawn.mapRect(104875, -1850, 125, 2850); //Last part
spawn.mapRect(87900, -1850, 4000, 550);
spawn.mapRect(89650, -100, 100, 100);
spawn.mapRect(89750, -200, 100, 100);
spawn.mapRect(89850, -300, 600, 100);
spawn.mapRect(90450, -200, 100, 100);
spawn.mapRect(90550, -100, 100, 100);
spawn.mapRect(89650, -1300, 100, 100);
spawn.mapRect(89750, -1200, 100, 100);
spawn.mapRect(89850, -1100, 600, 100);
spawn.mapRect(90450, -1200, 100, 100);
spawn.mapRect(90550, -1300, 100, 100);
spawn.mapRect(91950, -100, 100, 100);
spawn.mapRect(92050, -200, 100, 100);
spawn.mapRect(92150, -300, 600, 100);
spawn.mapRect(92750, -200, 100, 100);
spawn.mapRect(92850, -100, 100, 100);
spawn.mapRect(92050, -1200, 100, 100);
spawn.mapRect(92150, -1100, 600, 100);
spawn.mapRect(92750, -1200, 100, 100);
spawn.mapRect(92850, -1300, 100, 100);
spawn.mapRect(92950, -1400, 11000, 100);
spawn.mapRect(93975, -1700, 200, 100);
spawn.mapRect(96075, -1500, 100, 100);
spawn.mapRect(96500, -1675, 100, 100);
spawn.mapRect(96950, -1490, 1900, 100);
spawn.mapRect(97200, -1685, 1650, 100);
spawn.mapRect(93900, -300, 100, 300);
spawn.mapRect(93900, -400, 300, 100);
spawn.mapRect(94100, -300, 100, 300);
spawn.mapRect(95025, -1300, 100, 300);
spawn.mapRect(95025, -1000, 300, 100);
spawn.mapRect(95225, -1300, 100, 300);
spawn.mapRect(96925, -1300, 600, 100);
spawn.mapRect(96925, -125, 600, 125);
spawn.mapRect(98650, -1300, 100, 200);
spawn.mapRect(98650, -1200, 600, 100);
spawn.mapRect(99150, -1300, 100, 200);
spawn.mapRect(98650, -200, 100, 200);
spawn.mapRect(99150, -200, 100, 200);
spawn.mapRect(98650, -200, 600, 100);
spawn.mapRect(100825, -1300, 100, 300);
spawn.mapRect(100825, -1100, 1325, 100);
spawn.mapRect(102050, -1300, 100, 300);
spawn.mapRect(100825, -300, 100, 300);
spawn.mapRect(100825, -300, 1350, 100);
spawn.mapRect(102075, -300, 100, 300);
spawn.mapRect(99425, -1675, 100, 125);
spawn.mapRect(100050, -1525, 100, 125);
spawn.mapRect(132025, -225, 2325, 525);
spawn.mapRect(132025, -1450, 500, 1750);
spawn.mapRect(133875, -1475, 475, 1775);
spawn.mapRect(132025, -1925, 2325, 475);
// simulation.enableConstructMode() //also remove when done
coin(50165.9, -1090)
coin(78725.4, -600)
coin(103830.0, -1473)
hunter(0, -1000)
},
yingYang() {
simulation.makeTextLog(`<strong>yingYang</strong> by <span class='color-var'>Richard0820</span>`);
let destroyed = false;
const lock = level.door(425, -1400, 50, 300, 300);
const core = function (x, y, radius = 100 + Math.ceil(Math.random() * 25)) {
radius = 9 + radius / 8; //extra small
mobs.spawn(x, y, 6, radius, "transparent");
let me = mob[mob.length - 1];
me.constraint = Constraint.create({
pointA: {
x: me.position.x,
y: me.position.y
},
bodyB: me,
stiffness: 1,
damping: 1
});
Composite.add(engine.world, me.constraint);
me.stroke = "transparent"; //used for drawSneaker
me.eventHorizon = radius * 40;
me.seeAtDistance2 = (me.eventHorizon + 400) * (me.eventHorizon + 400); //vision limit is event horizon
me.accelMag = 0.00012 * simulation.accelScale;
me.frictionAir = 0.025;
me.collisionFilter.mask = cat.player | cat.bullet //| cat.body
me.showHealthBar = false;
me.memory = Infinity;
me.isBoss = true;
Matter.Body.setDensity(me, 1); //extra dense //normal is 0.001 //makes effective life much larger
me.onDeath = function () {
destroyed = true;
powerUps.spawnBossPowerUp(this.position.x, this.position.y);
}
me.do = function () {
if (this.health < 1) {
this.health += 0.001; //regen
simulation.drawList.push({
x: this.position.x,
y: this.position.y,
radius: this.radius / 1.5,
color: `rgba(0, 255, 20, ${Math.random() * 0.1})`,
time: simulation.drawTime
});
}
this.curl()
this.repelBullets()
this.seePlayerCheckByDistance()
this.checkStatus();
const eventHorizon = this.eventHorizon * (0.93 + 0.17 * Math.sin(simulation.cycle * 0.011))
//draw darkness
ctx.beginPath();
ctx.arc(this.position.x, this.position.y, eventHorizon * 0.25, 0, 2 * Math.PI);
ctx.fillStyle = "rgba(250,250,250,0.9)";
ctx.fill();
ctx.beginPath();
ctx.arc(this.position.x, this.position.y, eventHorizon * 0.55, 0, 2 * Math.PI);
ctx.fillStyle = "rgba(250,250,250,0.5)";
ctx.fill();
ctx.beginPath();
ctx.arc(this.position.x, this.position.y, eventHorizon, 0, 2 * Math.PI);
ctx.fillStyle = "rgba(250,250,250,0.1)";
ctx.fill();
//when player is inside event horizon
if (Vector.magnitude(Vector.sub(this.position, player.position)) < eventHorizon) {
if (m.immuneCycle < m.cycle) {
if (m.energy > 0) m.energy -= 0.005
if (m.energy < 0.1) m.damage(0.0001 * simulation.dmgScale);
}
const angle = Math.atan2(player.position.y - this.position.y, player.position.x - this.position.x);
player.force.x += 0.00125 * player.mass * Math.cos(angle) * (m.onGround ? 1.8 : 1);
player.force.y += 0.0001 * player.mass * Math.sin(angle);
//draw line to player
ctx.beginPath();
ctx.moveTo(this.position.x, this.position.y);
ctx.lineTo(m.pos.x, m.pos.y);
ctx.lineWidth = Math.min(60, this.radius * 2);
ctx.strokeStyle = "rgba(250,250,250,0.5)";
ctx.stroke();
ctx.beginPath();
ctx.arc(m.pos.x, m.pos.y, 40, 0, 2 * Math.PI);
ctx.fillStyle = "rgba(250,250,250,0.3)";
ctx.fill();
}
}
}
const sniper = function (x, y, radius = 35 + Math.ceil(Math.random() * 30)) { //same, just white so that we can seen
mobs.spawn(x, y, 3, radius, "transparent"); //"rgb(25,0,50)")
let me = mob[mob.length - 1];
me.vertices = Matter.Vertices.rotate(me.vertices, Math.PI, me.position); //make the pointy side of triangle the front
me.isVerticesChange = true
// Matter.Body.rotate(me, Math.PI)
me.stroke = "transparent"; //used for drawSneaker
me.alpha = 1; //used in drawSneaker
me.showHealthBar = false;
me.frictionStatic = 0;
me.friction = 0;
me.canTouchPlayer = false; //used in drawSneaker
me.isBadTarget = true;
me.collisionFilter.mask = cat.map | cat.body | cat.bullet | cat.mob //can't touch player
me.memory = 30 //140;
me.fireFreq = 0.005 + Math.random() * 0.002 + 0.0005 * simulation.difficulty; //larger = fire more often
me.noseLength = 0;
me.fireAngle = 0;
me.accelMag = 0.0005 * simulation.accelScale;
me.frictionAir = 0.05;
me.torque = 0.0001 * me.inertia;
me.fireDir = {
x: 0,
y: 0
};
me.onDeath = function () { //helps collisions functions work better after vertex have been changed
// this.vertices = Matter.Vertices.hull(Matter.Vertices.clockwiseSort(this.vertices))
}
// spawn.shield(me, x, y);
me.do = function () {
// this.seePlayerByLookingAt();
this.seePlayerCheck();
this.checkStatus();
const setNoseShape = () => {
const mag = this.radius + this.radius * this.noseLength;
this.vertices[1].x = this.position.x + Math.cos(this.angle) * mag;
this.vertices[1].y = this.position.y + Math.sin(this.angle) * mag;
};
//throw a mob/bullet at player
if (this.seePlayer.recall) {
//set direction to turn to fire
if (!(simulation.cycle % this.seePlayerFreq)) {
this.fireDir = Vector.normalise(Vector.sub(this.seePlayer.position, this.position));
// this.fireDir.y -= Math.abs(this.seePlayer.position.x - this.position.x) / 1600; //gives the bullet an arc
}
//rotate towards fireAngle
const angle = this.angle + Math.PI / 2;
// c = Math.cos(angle) * this.fireDir.x + Math.sin(angle) * this.fireDir.y;
//rotate towards fireAngle
const dot = Vector.dot({
x: Math.cos(angle),
y: Math.sin(angle)
}, this.fireDir)
const threshold = 0.03;
if (dot > threshold) {
this.torque += 0.000004 * this.inertia;
} else if (dot < -threshold) {
this.torque -= 0.000004 * this.inertia;
} else if (this.noseLength > 1.5 && dot > -0.2 && dot < 0.2) {
//fire
spawn.sniperBullet(this.vertices[1].x, this.vertices[1].y, 7 + Math.ceil(this.radius / 15), 5);
const v = 10 + 8 * simulation.accelScale;
Matter.Body.setVelocity(mob[mob.length - 1], {
x: this.velocity.x + this.fireDir.x * v + Math.random(),
y: this.velocity.y + this.fireDir.y * v + Math.random()
});
this.noseLength = 0;
// recoil
this.force.x -= 0.005 * this.fireDir.x * this.mass;
this.force.y -= 0.005 * this.fireDir.y * this.mass;
}
if (this.noseLength < 1.5) this.noseLength += this.fireFreq;
setNoseShape();
} else if (this.noseLength > 0.1) {
this.noseLength -= this.fireFreq / 2;
setNoseShape();
}
// else if (this.noseLength < -0.1) {
// this.noseLength += this.fireFreq / 4;
// setNoseShape();
// }
if (this.seePlayer.recall) {
if (this.alpha < 1) this.alpha += 0.01;
} else {
if (this.alpha > 0) this.alpha -= 0.03;
}
//draw
if (this.alpha > 0) {
if (this.alpha > 0.95) {
this.healthBar();
if (!this.canTouchPlayer) {
this.canTouchPlayer = true;
this.isBadTarget = false;
this.collisionFilter.mask = cat.player | cat.map | cat.body | cat.bullet | cat.mob; //can touch player
}
}
//draw body
ctx.beginPath();
const vertices = this.vertices;
ctx.moveTo(vertices[0].x, vertices[0].y);
for (let j = 1, len = vertices.length; j < len; ++j) {
ctx.lineTo(vertices[j].x, vertices[j].y);
}
ctx.lineTo(vertices[0].x, vertices[0].y);
ctx.fillStyle = `rgba(250,250,250,${this.alpha * this.alpha})`;
ctx.fill();
} else if (this.canTouchPlayer) {
this.canTouchPlayer = false;
this.isBadTarget = true
this.collisionFilter.mask = cat.map | cat.body | cat.bullet | cat.mob //can't touch player
}
};
}
const portal = level.portal({
x: 650,
y: -1000
}, Math.PI * 1.5, {
x: 525,
y: 2625
}, -Math.PI)
document.body.style.transition = '0ms'
document.body.style.backgroundColor = "#061026" //"#061026";
var yy = new Image();
yy.src = 'https://raw.githubusercontent.com/Whyisthisnotavalable/image-yy/main/Hotpot6.png';
color.map = "#FFFFFF11";
color.bullet = "#FFFFFF";
level.custom = () => {
level.enter.draw();
level.exit.drawAndCheck();
ctx.drawImage(yy, 0 - 500, 0 - 500, 1000, 1000)
portal[0].draw();
portal[1].draw();
portal[2].query();
portal[3].query();
if (destroyed == false) {
lock.isClosing = true;
} else {
lock.isClosing = false;
}
lock.openClose();
};
level.customTopLayer = () => {
lock.draw()
/*
ctx.beginPath()
ctx.strokeStyle = "transparent";
ctx.fillStyle = "#FFFFFF22"
ctx.arc(m.pos.x, m.pos.y, 500, 0, Math.PI * 2)
ctx.fill()
ctx.fillStyle = "#FFFFFF55"
ctx.arc(m.pos.x, m.pos.y, 1000, 0, Math.PI * 2)
ctx.fill();
ctx.stroke(); */
ctx.beginPath();
ctx.moveTo(m.pos.x, m.pos.y)
const arc = Math.PI / 4
ctx.arc(m.pos.x, m.pos.y, 100, m.angle + arc, m.angle - arc)
ctx.arc(m.pos.x, m.pos.y, 4000, m.angle - arc, m.angle + arc)
ctx.fillStyle = "rgba(255,255,255,0.7)";
ctx.globalCompositeOperation = "destination-in";
ctx.fill();
ctx.globalCompositeOperation = "source-over";
ctx.clip();
};
level.setPosToSpawn(0, -50);
level.exit.x = -275;
level.exit.y = 2900;
level.defaultZoom = 1800
simulation.zoomTransition(level.defaultZoom)
//map
spawn.mapRect(-125, -325, 225, 25);
spawn.mapRect(300, -150, 25, 325);
spawn.mapRect(-325, -125, 25, 300);
spawn.mapRect(-150, 300, 275, 25);
spawn.mapRect(125, -300, 25, 25);
spawn.mapRect(175, -275, 25, 25);
spawn.mapRect(225, -250, 25, 25);
spawn.mapRect(250, -200, 25, 25);
spawn.mapRect(-175, -300, 25, 25);
spawn.mapRect(-225, -275, 25, 25);
spawn.mapRect(-300, -200, 25, 25);
spawn.mapRect(150, 275, 25, 25);
spawn.mapRect(200, 225, 25, 25);
spawn.mapRect(250, 200, 25, 25);
spawn.mapRect(250, -225, 25, 25);
spawn.mapRect(275, -175, 25, 25);
spawn.mapRect(200, -275, 25, 25);
spawn.mapRect(150, -300, 25, 25);
spawn.mapRect(100, -325, 25, 25);
spawn.mapRect(-150, -300, 25, 25);
spawn.mapRect(-200, -300, 25, 25);
spawn.mapRect(-250, -250, 25, 25);
spawn.mapRect(-275, -225, 25, 25);
spawn.mapRect(-300, -175, 25, 50);
spawn.mapRect(275, 175, 25, 25);
spawn.mapRect(250, 200, 25, 25);
spawn.mapRect(225, 225, 25, 25);
spawn.mapRect(175, 250, 25, 25);
spawn.mapRect(125, 300, 25, 25);
spawn.mapRect(-300, 325, 200, 150);
spawn.mapRect(-400, 425, 225, 150);
spawn.mapRect(-4450, 2900, 1550, 150);
spawn.mapRect(-4500, 2525, 150, 525);
spawn.mapRect(-4800, 2150, 150, 400);
spawn.mapRect(-4400, 2025, 650, 150);
spawn.mapRect(-2425, 50, 2125, 150);
spawn.mapRect(-2425, 50, 150, 1300);
spawn.mapRect(-4600, 1175, 2325, 175);
spawn.mapRect(-5075, 1650, 450, 150);
spawn.mapRect(-4650, 1225, 75, 125);
spawn.mapRect(-4700, 1275, 75, 75);
spawn.mapRect(-425, 2925, 425, 125);
spawn.mapRect(-450, 2375, 450, 100);
spawn.mapRect(-3050, 550, 150, 450);
spawn.mapRect(-2925, 825, 100, 175);
spawn.mapRect(-2650, 375, 275, 125);
spawn.mapRect(-75, 2950, 300, 100);
spawn.mapRect(-625, -500, 125, 575);
spawn.mapRect(-1050, -325, 275, 100);
spawn.mapRect(-1075, -775, 100, 550);
spawn.mapRect(-1075, -775, 300, 100);
spawn.mapRect(-525, -1100, 1025, 625);
spawn.mapRect(450, -1000, 450, 1500);
spawn.mapRect(-300, 500, 1200, 75);
spawn.mapRect(-200, 425, 725, 100);
spawn.mapRect(525, 2450, 275, 600);
spawn.mapRect(-25, 2375, 825, 125);
spawn.mapRect(400, -1500, 500, 100);
spawn.mapRect(800, -1500, 100, 525);
spawn.mapRect(-400, 500, 1250, 1900);
spawn.mapRect(-300, 2910, 150, 25);
spawn.mapRect(-625, -1000, 125, 175);
spawn.spawnStairs(-400, 3000, 25, 2500, 2500, 250);
spawn.spawnStairs(500, 3000, 5, 250, 250, 250);
spawn.debris(-1550, -250, 100);
spawn.debris(-1100, 850, 100);
spawn.debris(-3700, 1025, 100);
spawn.debris(-3525, 2725, 100);
spawn.debris(-4750, 2050, 100);
spawn.debris(-4000, 1900, 100);
spawn.debris(225, -1225, 100);
//mobs
spawn.sneaker(-1350, 1350);
spawn.sneaker(-2275, 2275);
sniper(-3050 + Math.floor(Math.random() * 100) - Math.floor(Math.random() * 100), 1475 + Math.floor(Math.random() * 100) - Math.floor(Math.random() * 100));
sniper(-2925 + Math.floor(Math.random() * 100) - Math.floor(Math.random() * 100), 1775 + Math.floor(Math.random() * 100) - Math.floor(Math.random() * 100));
sniper(-3075 + Math.floor(Math.random() * 100) - Math.floor(Math.random() * 100), 1600 + Math.floor(Math.random() * 100) - Math.floor(Math.random() * 100));
sniper(-3100 + Math.floor(Math.random() * 100) - Math.floor(Math.random() * 100), 1975 + Math.floor(Math.random() * 100) - Math.floor(Math.random() * 100));
sniper(-3075 + Math.floor(Math.random() * 100) - Math.floor(Math.random() * 100), 1750 + Math.floor(Math.random() * 100) - Math.floor(Math.random() * 100));
sniper(-3350, 425);
sniper(-3550, 600);
sniper(-3325, 775);
sniper(-5525, 1975);
sniper(-50, -1300);
for (let i = 0; i < 10 + simulation.difficulty; i++) {
spawn.ghoster(0 + Math.floor(Math.random() * 5000) - Math.floor(Math.random() * 5000), 0 + Math.floor(Math.random() * 5000) - Math.floor(Math.random() * 5000))
}
core(-2000, -1000);
powerUps.spawnStartingPowerUps(0, 0)
powerUps.addResearchToLevel()
},
staircase() {
simulation.makeTextLog(`<strong>staircase</strong> by <span class='color-var'>ryanbear</span>`);
level.custom = () => {
level.exit.drawAndCheck();
level.enter.draw();
};
level.customTopLayer = () => {
aaa.query();
bbb.query();
ccc.query();
ddd.move();
eee.query();
fff.query();
ggg.query();
hhh.query();
iii.query();
jjj.query();
kk.query();
lll.query();
mmm.query();
nnn.query();
ooo.query();
ppp.query();
};
level.setPosToSpawn(0, -50); //normal spawn
level.exit.x = 7300;
level.exit.y = -5154;
// spawn.mapRect(level.enter.x, level.enter.y + 20, 100, 20);
level.defaultZoom = 1800
simulation.zoomTransition(level.defaultZoom)
document.body.style.backgroundColor = "#d8dadf";
// powerUps.spawnStartingPowerUps(1475, -1175);
// spawn.debris(750, -2200, 3700, 16); //16 debris per level
spawn.mapRect(-100, 0, 2100, 100);
spawn.mapRect(1984, 17, 100, 500);
spawn.mapRect(2013, 522, 1618, 100);
spawn.bodyRect(2090, 328, 100, 100);
spawn.mapRect(3619, 14, 100, 500)
var aaa = level.hazard(1999, 10, 1618, 500);
var bbb = level.vanish(2320, -345, 234, 20);
var ccc = level.vanish(2862, -324, 234, 20);
var eee = level.vanish(3002, -1100, 234, 20);
var ddd = level.elevator(3399, -420, 200, 200, -950, 0.003, { up: 0.1, down: 0.2 }) //x, y, width, height, maxHeight, force = 0.003, friction = { up: 0.01, down: 0.2 }) {
var fff = level.vanish(3359, -1300, 234, 20);
var ggg = level.boost(3020, -1600, 700);
var hhh = level.vanish(2700, -1940, 1147, 20);
var iii = level.boost(5038, -2000, 700);
var jjj = level.vanish(5092, -3498, 100, 100);
var kk = level.boost(5092, -3772, 700);
var lll = level.boost(5372, -2824, 700);
var mmm = level.vanish(5112, -3000, 100, 100);
var nnn = level.vanish(5367, -3000, 100, 100);
var ooo = level.boost(4810, -3161, 700);
var ppp = level.vanish(5383, -3485, 100, 100);
spawn.mapRect(5377, -4198, 1000, 100);
spawn.mapRect(6390, -4359, 200, 200);
spawn.mapRect(6605, -4563, 200, 200);
spawn.mapRect(6809, -4758, 200, 200);
spawn.mapRect(7014, -4962, 200, 200);
spawn.mapRect(7212, -5158, 200, 200);
spawn.mapRect(4156, -1898, 1000, 100);
// spawn.bodyRect(1540, -1110, 300, 25, 0.9);
// spawn.randomSmallMob(1300, -70);
spawn.randomMob(590, -315);
spawn.randomMob(1343, -757);
spawn.randomMob(4037, -926);
spawn.randomMob(3621, -2376);
spawn.randomMob(5026, -2441);
spawn.randomMob(4253, -2863);
spawn.randomMob(4355, -2430);
spawn.randomMob(5316, -3265);
spawn.randomMob(5885, -4427);
spawn.randomMob(6666, -4979);
spawn.laserBoss(6128, -4905);
// spawn.randomGroup(1700, -900, 0.4);
// if (simulation.difficulty > 1) spawn.randomLevelBoss(2200, -1300);
powerUps.addResearchToLevel() //needs to run after mobs are spawned
},
fortress() {
simulation.makeTextLog(`<strong>fortress</strong> by <span class='color-var'>Desboot</span>`);
const boost1 = level.boost(3600, -250, 1000)
const boost2 = level.boost(60, -604, 1000)
const boost3 = level.boost(2160, -1260, 1000)
powerUps.spawnStartingPowerUps(1033.3, -121.4)
level.custom = () => {
boost1.query();
boost2.query();
boost3.query();
level.exit.drawAndCheck();
level.enter.draw();
};
level.setPosToSpawn(0, -50); //normal spawn
level.exit.x = 3586; //3586.5, -1464.0
level.exit.y = -1494;
spawn.mapRect(level.enter.x, level.enter.y + 20, 100, 20);
level.defaultZoom = 1800
simulation.zoomTransition(level.defaultZoom)
document.body.style.backgroundColor = "#d8dadf";
level.customTopLayer = () => {
ctx.fillStyle = "rgba(0,0,0,0.3)"
ctx.fillRect(-272, -580, 1700, 600)
ctx.fillRect(1427.5, -487, 1280, 600)
ctx.fillRect(2707.3, -580, 1200, 600)
ctx.fillStyle = "rgba(0,0,0,0.2)"
ctx.fillRect(2752, -1744, 1075, 1164)
ctx.fillRect(937, -1590, 339, 550)
ctx.fillRect(1158, -1040, 118, 550)
ctx.fillRect(3049, -1063, 339, 500)
ctx.fillRect(1439, -1281, 297, 800)
ctx.fillRect(2130, -1182, 167, 800)
ctx.fillRect(1892, -2073, 238, 1593)
ctx.fillRect(2297, -2073, 238, 1593)
ctx.fillStyle = "rgba(0,0,0,0.15)"
ctx.fillRect(483, -1277, 350, 700)
ctx.fillRect(833, -1000, 325, 450)
ctx.fillStyle = "rgba(64,64,64,0.97)" //hidden section
ctx.fillRect(2800, -1712, 730, 300)
};
spawn.debris(2700, -120, 180, 3);
spawn.debris(1350, -100, 280, 3);
spawn.debris(2300, -700, 380, 5);
spawn.debris(976, -775, 38, 5);
spawn.debris(840, -1424, 3080, 5);
spawn.debris(2300, -700, 3080, 5);
spawn.mapRect(-272, 0, 4198, 123);
spawn.mapRect(-272, -581, 132, 581);
spawn.mapRect(-272, -581, 572, 326);
spawn.mapRect(1462, -229, 92, 229);
spawn.mapRect(1462, -229, 352, 57);
spawn.mapRect(2872, -220, 1056, 330);
spawn.mapRect(170, -260, 484, 80);
spawn.mapRect(476, -581, 1162, 75);
spawn.mapRect(951, -519, 1760, 132);
spawn.mapRect(1747, -492, 506, 66);
spawn.mapRect(2462, -581, 1074, 75);
spawn.mapRect(1136, -616, 510, 100);
spawn.mapRect(3815.6, -1461, 114, 1300); //far right wall
spawn.mapRect(480, -1456, 106, 651); //far left wall
spawn.mapRect(1426, -906, 106, 400);
spawn.mapRect(480, -1302, 374, 57);
spawn.mapRect(788, -1302, 75, 308);
spawn.mapRect(788, -1055, 370, 62);
spawn.mapRect(3049, -1170, 471, 106);
spawn.mapRect(3348, -1170, 188, 663);
spawn.mapRect(2751, -1461, 1088, 53); //roof under the exit
spawn.mapRect(2751, -1743, 92, 915); //wall on left or far right side
spawn.mapRect(937, -1667, 339, 84); //upper left platform
spawn.mapRect(1135, -3239, 119, 1450);
spawn.mapRect(1440, -1346, 295, 66); //center left platform
spawn.mapRect(2090, -1240, 242, 57); //center righ platform
spawn.mapRect(1892, -2214, 88, 220); //vertical part of left L
spawn.mapRect(1892, -2073, 238, 84); //flat part of left L
spawn.mapRect(2447, -2214, 88, 220); //vertical part of right L
spawn.mapRect(2297, -2073, 238, 84); //flat part of right L
spawn.mapRect(2751, -1743, 1078, 57); //exit roof //3587.2, -1470.0
spawn.mapRect(3584, -1470, 103, 57); //wall below door3689
spawn.mapRect(3428, -1735, 103, 173); //wall covering secret
spawn.mapRect(-11000, -1000, 100, 10); //SAL
spawn.mapRect(-11000, -1000, 10, 100); //SAL
spawn.mapRect(-10900, -900, 10, 100); //SAL
spawn.mapRect(-11000, -900, 100, 10); //SAL
spawn.mapRect(-11000, -800, 100, 10); //SAL
spawn.mapRect(-10800, -1000, 10, 200); //SAL
spawn.mapRect(-10700, -1000, 10, 200); //SAL
spawn.mapRect(-10800, -1000, 100, 10); //SAL
spawn.mapRect(-10800, -900, 100, 10); //SAL
spawn.mapRect(-10600, -1000, 10, 200); //SAL
spawn.mapRect(-10600, -800, 100, 10); //SAL
spawn.mapRect(-11000, -91000, 100, 10); //SAL
spawn.mapRect(-11000, -91000, 10, 100); //SAL
spawn.mapRect(-10900, -90900, 10, 100); //SAL
spawn.mapRect(-11000, -90900, 100, 10); //SAL
spawn.mapRect(-11000, -90800, 100, 10); //SAL
spawn.mapRect(-10800, -91000, 10, 200); //SAL
spawn.mapRect(-10700, -91000, 10, 200); //SAL
spawn.mapRect(-10800, -91000, 100, 10); //SAL
spawn.mapRect(-10800, -90900, 100, 10); //SAL
spawn.mapRect(-10600, -91000, 10, 200); //SAL
spawn.mapRect(-10600, -90800, 100, 10); //SAL
//mobs
spawn.randomMob(3104.9, -1284.9, 0.2);
spawn.randomMob(1784.7, -95.9, 0.2);
spawn.randomMob(3474.2, -406.7, 0.1);
spawn.randomMob(1603.2, -1493.5, 0.4);
spawn.randomMob(772.4, -1505.2, 0.2);
spawn.randomMob(824.6, -781.3, 0.2);
spawn.randomMob(818.8, -1468.9, 0.2);
spawn.randomMob(-124.7, -853, 0.2);
spawn.randomMob(3011.1, -1978.0, -0.2);
spawn.randomMob(2428.0, -236.8, 0.1);
spawn.randomSmallMob(694.3, -385.3);
spawn.randomSmallMob(1142.0, -808.4);
spawn.randomSmallMob(791.5, -803.7);
spawn.randomSmallMob(3175.8, -830.8);
spawn.randomSmallMob(1558.5, -1940.8);
spawn.randomSmallMob(2700, -475);
spawn.randomSmallMob(2700, -475);
spawn.pulsar(1762.9, -2768.3)
spawn.pulsar(3821.5, -2373.9)
let randomBoss = Math.floor(Math.random() * 5);
spawn[["laserBoss", "blinkBoss", "shooterBoss", "launcherBoss", "pulsarBoss", "beetleBoss", "bladeBoss", "revolutionBoss", "dragonFlyBoss", "spiderBoss"][randomBoss]](2058.5, -711.4, 100, false);
//spawn powerups
// powerUps.spawn(3167.6, -1300, "tech")
powerUps.spawn(3125.8, -1543.4, "tech")
powerUps.spawn(3125.8, -1543.4, "heal")
powerUps.spawn(3125.8, -1543.4, "ammo")
powerUps.spawn(3125.8, -1543.4, "ammo")
powerUps.spawn(3137.6, -1300, "ammo")
powerUps.spawn(1605.2, -1436.9, "heal")
powerUps.spawn(2912.9, -1940.9, "ammo")
powerUps.spawn(3167.6, -1300, "heal")
powerUps.spawn(1, 1, "ammo")
powerUps.addResearchToLevel() //needs to run after mobs are spawned
},
commandeer() {
simulation.makeTextLog(`<strong>commandeer</strong> by <span class='color-var'>Desboot</span>`);
let waterFallWidth = 400
let waterFallX = 15900
let waterFallSmoothX = 0
const elevator = level.elevator(-80.4, -931.6, 180, 50, -1550)
15900 && player.position.x < 16300 && player.position.y > -960.2
//const slime = level.hazard(15900, -960, 400, 6000);
const slime2 = level.hazard(15147.2, -1782.4, 2000, 822);
const boost1 = level.boost(5950, -20, 700)
const boost2 = level.boost(21088, -1672, 700)
const boost3 = level.boost(19390, -31, 1700)
const boost4 = level.boost(19390, -31, 1700)
const boost5 = level.boost(17274, -1242, 1000)
const portal = level.portal({ x: 443, y: -1636 }, Math.PI, { x: 21391.9, y: -1806.3 }, -Math.PI)
const portal2 = level.portal({ x: 16838.3, y: -626.7 }, Math.PI, { x: 16882.8, y: -2566.5 }, -Math.PI)
const buttonDoor = level.button(21889, -10)
const door = level.door(19119, -2133, 110, 510, 480)
const buttonDoor2 = level.button(18711, -2210)
const door2 = level.door(17041, -412, 110, 510, 480)
const buttonDoor3 = level.button(20456.6, -1636.2)
const door3 = level.door(20238, -781.4, 88, 452, 412)
const hazard2 = level.hazard(2550, -150, 10, 0.4) //y=-1485
simulation.enableConstructMode()
level.setPosToSpawn(0, -50); //normal spawn
level.exit.x = 15316;
level.exit.y = -30;
spawn.mapRect(level.enter.x, level.enter.y + 20, 100, 20); //bump for level entrance
spawn.mapRect(level.exit.x, level.exit.y + 20, 100, 20); //bump for level exit
level.defaultZoom = 1800
simulation.zoomTransition(level.defaultZoom)
document.body.style.backgroundColor = "#001738";
color.map = "#444" //custom map color
level.custom = () => {
//spawn.mapRect(22330, -2688.75, 400, 800);
//spawn.mapRect(22330, -1793.5, 400, 800);//-46.25*2=-92.5
//spawn.mapRect(22330, -804.25, 400, 800);//-46.25*3
ctx.fillStyle = "rgba(250,250,250,0.8)"//lights
ctx.beginPath()
ctx.moveTo(1124, -628)
ctx.lineTo(496, 0)
ctx.lineTo(1852, 0)
ctx.lineTo(1224, -628)
ctx.fill()
ctx.beginPath()
ctx.moveTo(906, -1365)
ctx.lineTo(206, -690)
ctx.lineTo(1706, -690)
ctx.lineTo(1006, -1365)
ctx.fill()
ctx.beginPath()
ctx.moveTo(3330, -1905)//-700
ctx.lineTo(2815.6, -1405.8)
ctx.lineTo(2815.6, -1230)
ctx.lineTo(4022.9, -1283.9)
ctx.lineTo(4023.5, -1405.8)
ctx.lineTo(3430, -1905)
ctx.fill()
ctx.fillStyle = "rgba(63,247,251,0.8)"
ctx.fillRect(22330, -2713.75, 550, 700)//15845.0, -1262.2
ctx.fillRect(22330, -1743.5, 550, 700)
ctx.fillRect(22330, -754.25, 550, 700)
ctx.fillRect(6237, -1830.7, 550, 700)
ctx.fillRect(6237, -840.4, 550, 700)
ctx.fillStyle = "rgba(200,200,200,0.8)"
ctx.fillRect(-192, -1973, 6484, 2071)
ctx.fillStyle = "rgba(240,240,240,0.8)"
ctx.fillRect(15109.5, -2867.5, 7284, 2971)
ctx.fillStyle = "rgba(35,35,35,0.8)"
ctx.fillRect(15145.9, -960, 200, 25)
ctx.fillStyle = "rgba(255,255,255,0.9)"
buttonDoor.query();
buttonDoor.draw();
buttonDoor2.query();
buttonDoor2.draw();
buttonDoor3.query();
buttonDoor3.draw();
//slime.query();
slime2.query();
if (buttonDoor.isUp) {
door.isClosing = true
} else {
door.isClosing = false
}
if (buttonDoor2.isUp) {
door2.isClosing = true
} else {
door2.isClosing = false
}
if (buttonDoor3.isUp) {
door3.isClosing = true
} else {
door3.isClosing = false
}
door.openClose();
door2.openClose();
door3.openClose();
portal[2].query()
portal[3].query()
portal2[2].query()
portal2[3].query()
boost1.query();
boost2.query();
boost3.query();
boost4.query();
boost5.query();
level.exit.drawAndCheck();
level.enter.draw();
ctx.fillStyle = "rgba(0,0,0,0.2)"//shadows
ctx.fillRect(2773, -682, 469, 500)
ctx.fillRect(3947, -851, 469, 700)
ctx.fillRect(4818, -1006, 400, 400)
ctx.fillRect(5313, -1309, 1000, 700)
ctx.fillRect(16705, -2831, 40, 700)
ctx.fillRect(16140, -2812, 40, 400)
ctx.fillRect(15559, -2855, 40, 800)
ctx.fillRect(16530, -2855, 30, 200)
ctx.beginPath()
ctx.moveTo(18254.7, -2194.1)
ctx.lineTo(18554.6, -1952.7)
ctx.lineTo(18554.6, -1992.7)
ctx.lineTo(18294.7, -2194.1)
ctx.fill()
ctx.beginPath()
ctx.moveTo(18154.7, -1004.1)
ctx.lineTo(18554.6, -762.7)
ctx.lineTo(18554.6, -802.7)
ctx.lineTo(18214.7, -1004.1)
ctx.fill()
ctx.beginPath()
ctx.moveTo(17585.2, -1123.8)
ctx.lineTo(17151.2, -781.7)
ctx.lineTo(17151.2, -741.7)
ctx.lineTo(17625.2, -1123.8)
ctx.fill()
ctx.fillRect(20540, -1103, 610, 300)
ctx.fillRect(20820, -243, 410, 300)
ctx.fillRect(5772, -609, 469, 700)
ctx.fillRect(5772, -609, 469, 700)
ctx.fillStyle = "rgba(48,184,140,255)"
ctx.fillRect(waterFallX, -960, waterFallWidth, 6000)
ctx.fillStyle = `hsla(160, 100%, 43%,${0.3 + 0.07 * Math.random()})`
ctx.fillRect(waterFallX + waterFallWidth * Math.random(), -900 - Math.random() * 400, Math.random() * 5 + 8, 6000)
ctx.fillRect(waterFallX + waterFallWidth * Math.random(), -900 - Math.random() * 400, Math.random() * 5 + 5, 6000)
waterFallWidth = 0.995 * waterFallWidth + 4 * Math.random()//4.7
waterFalSmoothlX = 0.96 * waterFallSmoothX + 20 * Math.random()//3.5
waterFallX = waterFallSmoothX + 15900
ctx.fillStyle = "rgba(0,0,0,0.4)"//wires
ctx.fillRect(20990, -2672, 20, 112)
ctx.fillRect(21090, -2506, 72, 20)
ctx.fillRect(21090, -1970, 72, 20)
ctx.fillRect(16901.8, -2497.7, 25, 100)
ctx.fillRect(16901.8, -2397.7, 50, 25)
ctx.fillRect(16951.8, -2397.7, 25, 1640)
ctx.fillRect(16901.8, -782.7, 50, 25)
ctx.fillRect(16901.8, -757.7, 25, 100)
ctx.fillRect(20900, -2666, 500, 9)
ctx.fillRect(20900, -2651, 1315, 9)
ctx.fillRect(20900, -2636, 1300, 9)
ctx.fillRect(20900, -2621, 245, 9)
ctx.fillRect(20900, -2606, 230, 9)
ctx.fillRect(20900, -2591, 215, 9)
ctx.fillRect(20900, -2576, 200, 9)
ctx.fillRect(21145, -2621, 9, 700)
ctx.fillRect(21130, -2606, 9, 1000)
ctx.fillRect(21115, -2591, 9, 1000)
ctx.fillRect(21100, -2576, 9, 850)
ctx.fillRect(21400, -3066, 9, 409)
ctx.fillRect(20900, -1726, 209, 9)
ctx.fillRect(21145, -1921, 270, 9)
ctx.fillRect(21415, -1921, 9, 50)
ctx.fillRect(22200, -2636, 9, 1300)
ctx.fillRect(22215, -2651, 9, 300)
ctx.fillRect(22200, -1336, 300, 9)
ctx.fillRect(22215, -2351, 300, 9)
//943.9, -1698.0
ctx.fillRect(916.5, -1725, 80, 80)//+55 // 55/2=27.5
ctx.fillRect(1204, -1706, 25, 40)//179
ctx.fillRect(1354, -1706, 25, 40)
ctx.fillRect(1504, -1885, 25, 40)
ctx.fillRect(3504, -1885, 25, 40)
ctx.fillRect(5504, -1885, 25, 40)
ctx.fillRect(1019, -1718, 9, 20)
ctx.fillRect(1019, -1674, 9, 20)
ctx.fillRect(996, -1718, 23, 9)
ctx.fillRect(996, -1663, 23, 9)
ctx.fillRect(1019, -1698, 425, 9)
ctx.fillRect(1444, -1868, 9, 179)
ctx.fillRect(1444, -1877, 4700, 9)
ctx.fillRect(1019, -1683, 440, 9)
ctx.fillRect(1459, -1853, 9, 179)
ctx.fillRect(1459, -1862, 4670, 9)
ctx.fillRect(6144, -1877, 9, 100)
ctx.fillRect(6144, -1777, 100, 9)
ctx.fillRect(6129, -1862, 9, 1100)
ctx.fillRect(6129, -762, 150, 9)
};
level.customTopLayer = () => {
door.draw();
door2.draw();
door3.draw();
portal[0].draw();
portal[1].draw();
portal[2].draw();
portal[3].draw();
portal2[0].draw();
portal2[1].draw();
portal2[2].draw();
portal2[3].draw();
elevator.move()
// if (player.position.x > 15900 && player.position.x < 16300 && player.position.y > -1360.2) {
// Matter.Body.setVelocity(player, {
// x: player.velocity.x,
// y: player.velocity.y + 10
// });
// }else{
// if (Math.abs(player.velocity.x) > 0.5){
// if (m.onGround){
// Matter.Body.setVelocity(player, {
// x: player.velocity.x + (0.07 * (Math.abs(player.velocity.x) / player.velocity.x)),
// y: player.velocity.y - 0.2
// });
// }else{
// Matter.Body.setVelocity(player, {
// x: player.velocity.x,
// y: player.velocity.y - 0.2
// });
// }
// }else{
// Matter.Body.setVelocity(player, {
// x: player.velocity.x,
// y: player.velocity.y - 0.2
// });
// }
// }
if (player.position.x > 15900 && player.position.x < 16300 && player.position.y > -1360.2) {
Matter.Body.setVelocity(player, {
x: player.velocity.x,
y: player.velocity.y + 2
});
} else {
if (Math.abs(player.velocity.x) > 0.5) {
if (m.onGround) {
Matter.Body.setVelocity(player, {
x: player.velocity.x + (0.07 * (Math.abs(player.velocity.x) / player.velocity.x)),
y: player.velocity.y - 0.2
});
} else {
Matter.Body.setVelocity(player, {
x: player.velocity.x,
y: player.velocity.y - 0.2
});
}
} else {
Matter.Body.setVelocity(player, {
x: player.velocity.x,
y: player.velocity.y - 0.2
});
}
}
hazard2.opticalQuery();
};
//1273.2, -1404.7
spawn.mapRect(1124, -653, 100, 25);
spawn.mapRect(906, -1390, 100, 25);
spawn.mapRect(3330, -1930, 100, 25);
//first ship base
spawn.mapRect(-300, 0, 6684, 100);//lower floor
spawn.mapRect(-300, -2071, 154, 2071);//left right wall
spawn.mapRect(2511, -300, 1309, 308);//left big block
spawn.mapRect(3820, -184, 1309, 184);//right big block
spawn.mapRect(-300, -739, 2549, 100);//upper right floor
spawn.mapRect(2056, -1309, 2764, 169);//upper center floor
spawn.mapRect(2056, -1309, 193, 650);//upper left floor wall
spawn.mapRect(4636, -1309, 193, 793);//upper right floor wall
spawn.mapRect(4821, -654, 955, 138);//upper right floor
spawn.mapRect(6237, -2071, 147, 2071);//far right wall
spawn.mapRect(-300, -2071, 6684, 154);//roof
//first ship details
spawn.mapRect(245, -360, 70, 400);//start room wall
spawn.mapRect(500, -1929, 154, 462);
spawn.mapRect(185, -1517, 469, 77);
spawn.mapRect(2773, -682, 469, 77);//walls in 1st room
spawn.mapRect(3743, -566, 77, 469);
spawn.mapRect(3947, -851, 469, 77);
spawn.mapRect(5313, -1309, 1000, 70);//walls in second area
spawn.mapRect(4818, -1006, 400, 70);
spawn.mapRect(4768, -1626, 800, 70);
spawn.mapRect(4760, -1626, 70, 400);
spawn.mapRect(645.1, -1480.8, 700, 100);//room for shielding boss
spawn.mapVertex(515, -1447, "0 0 0 100 -400 0");
spawn.mapRect(1245.1, -1980.8, 100, 500);
spawn.mapRect(2346.9, -1658.8, 469, 77);
spawn.mapRect(4023.6, -1723.7, 469, 77);
//engines //y -2972 -> 0
spawn.mapRect(6237, -1880.7, 400, 800);
spawn.mapRect(6237, -890.4, 400, 800);
//first ship blocks/debris
spawn.debris(3267.6, -797.1, 700, 5); //16 debris per level
spawn.debris(1626.0, -372.5, 1700, 8); //16 debris per level
spawn.debris(1880.1, -1508.9, 3700, 16); //16 debris per level
spawn.debris(5335.3, -1431.6, 3700, 16); //16 debris per level
spawn.debris(1563.8, -1087.9, 700, 5); //16 debris per level
spawn.bodyRect(1540, -1110, 218, 125, 0.9);
//first ship mobs
spawn.randomSmallMob(893.5, -120.8);
// spawn.randomMob(2903.9, -754.5, 0.4);
// spawn.randomMob(5577.0, -217.0, 0.2);
// spawn.randomMob(765.8, -1029.7, 0.5);
// spawn.randomMob(2680.1, -1779.2, 0.6);
// spawn.randomMob(20079.4, -2219.7, 0.4);
// spawn.randomMob(3924.9, -1504.1, 0.5);
// spawn.randomMob(21284.2, -983.1, 0.3);
// spawn.randomMob(20381.0, -254.2, 0.5);
// spawn.randomMob(18375.6, -1574.4, 0.6);
// spawn.randomMob(19448.2, -1323.3, 0.3);
// spawn.randomMob(18397.7, -711.2, 0.3);
// spawn.randomMob(15547.2, -2249.6, 0.5);
// spawn.randomSmallMob(16114.6, -2524.2);
// spawn.randomSmallMob(15378.9, -2549.6);
// spawn.randomSmallMob(3266.4, -1578.4);
// spawn.randomSmallMob(4386.2, -439.6);
// spawn.randomSmallMob(5667.0, -847.8);
// spawn.randomSmallMob(3158.5, -1581.8);
// spawn.randomSmallMob(3866.7, -1483.2);
// spawn.randomSmallMob(4652.3, -1729.4);
// spawn.randomSmallMob(1068.7, -106.1);
// spawn.randomSmallMob(3382.5, -1590.6);//3545.0, -413.0
// spawn.randomSmallMob(5099.7, -1204.2);
// spawn.randomSmallMob(1456.4, -1014.8);
// spawn.randomSmallMob(20432.4, -1374.3);
// spawn.randomSmallMob(20381.0, -254.2);
// spawn.randomSmallMob(3505.1, -1531.1);
// spawn.randomSmallMob(20648.1, -136.8);
// spawn.randomSmallMob(17502.8, -1520.6);
// spawn.randomSmallMob(17438.7, -876.7);
spawn.randomMob(18375.6, -1574.4, 0.2);
spawn.randomSmallMob(15378.9, -2549.6);
spawn.randomSmallMob(5820.2, -1545.2);
spawn.randomMob(765.8, -1029.7, 0.2);
spawn.randomMob(21284.2, -983.1, 0.3);
spawn.randomSmallMob(3382.5, -1590.6);
spawn.randomSmallMob(3545.0, -413.0);
spawn.randomMob(20381.0, -254.2, 0.6);
spawn.randomSmallMob(20432.4, -1374.3);
spawn.randomSmallMob(5667.0, -847.8);
spawn.randomMob(2903.9, -754.5, 0.2);
spawn.randomSmallMob(3266.4, -1578.4);
spawn.randomSmallMob(20648.1, -136.8);
spawn.randomSmallMob(16114.6, -2524.2);
spawn.randomSmallMob(20381.0, -254.2);
spawn.randomMob(5577.0, -217.0, 0.3);
spawn.randomSmallMob(1456.4, -1014.8);
spawn.randomSmallMob(1068.7, -106.1);
spawn.randomSmallMob(5099.7, -1204.2);
spawn.randomSmallMob(17502.8, -1520.6);
spawn.randomMob(15547.2, -2249.6, 0.2);
spawn.randomMob(19448.2, -1323.3, 0.7);
spawn.randomSmallMob(3158.5, -1581.8);
spawn.randomSmallMob(17438.7, -876.7);
spawn.randomMob(20079.4, -2219.7, 0.2);
spawn.randomMob(2680.1, -1779.2, 0.6);
spawn.randomMob(3924.9, -1504.1, 0.3);
spawn.randomSmallMob(4652.3, -1729.4);
spawn.randomMob(18397.7, -711.2, 0.3);
spawn.randomSmallMob(4386.2, -439.6);
spawn.randomSmallMob(3505.1, -1531.1);
spawn.randomSmallMob(3866.7, -1483.2);
//second ship mobs
spawn.debris(17732.3, -550.0, 700, 5); //16 debris per level
spawn.debris(17827.2, -2357.1, 700, 5); //16 debris per level
spawn.debris(16108.6, -2621.1, 700, 5); //16 debris per level
spawn.debris(20823.6, -1332.1, 1300, 5); //16 debris per level
spawn.debris(21095.5, -423.4, 700, 5); //16 debris per level
spawn.debris(20534.5, -1282.1, 700, 5); //16 debris per level
spawn.randomSmallMob(1300, -70);
spawn.shieldingBoss(943.9, -1698.0)
//second ship base
spawn.mapRect(15000, 0, 515, 185);//lower floor 1
spawn.mapRect(17015, 0, 5500, 185);//lower floor 2
spawn.mapRect(15000, -2972, 185, 2972);//left wall
spawn.mapRect(15000, -2972, 7515, 185);//roof
spawn.mapRect(22330, -2972, 185, 2972);//right wall
spawn.mapRect(17002, -2972, 169, 2564);//left middle wall
spawn.mapRect(19089, -2972, 169, 855);//right middle wall upper
spawn.mapRect(19089, -1625, 169, 1800);//right middle wall lower
spawn.mapRect(20760, -2972, 169, 1350);//medium wall left of portal
spawn.mapRect(19720, -1625, 1725, 162);//right room upper floor
spawn.mapRect(21440, -2325, 169, 863);//medium wall right of portal
spawn.mapRect(19720, -855, 2725, 162);//right room lower floor
//engines //y -2972 -> 0
spawn.mapRect(22330, -2763.75, 400, 800);
spawn.mapRect(22330, -1793.5, 400, 800);
spawn.mapRect(22330, -804.25, 400, 800);
//second ship details
spawn.mapRect(19904, -1465, 85, 362);//upper L
spawn.mapRect(19542, -1191, 412, 88);//lower L
spawn.mapRect(18546, -2199, 600, 82);//2nd room enternce wall
spawn.mapRect(18546, -2499, 82, 2300);
spawn.mapRect(18108, -326, 500, 82);//walls/floors in middle room
spawn.mapRect(17750, -682, 300, 82);
spawn.mapRect(17156, -468, 500, 60);
spawn.mapRect(18022, -1082, 600, 82);
spawn.mapRect(17151, -1196, 500, 82);
spawn.mapRect(17453, -2060, 500, 82);
spawn.mapRect(18197, -2269, 400, 82);
spawn.mapRect(18108, -326, 500, 82);
spawn.mapRect(20542, -1191, 612, 88);
spawn.mapRect(20238, -1191, 88, 412);
spawn.mapRect(21520, -1468, 88, 412);
spawn.mapRect(20238, -330.2, 88, 412);
spawn.mapRect(20819, -328.3, 412, 88);
spawn.mapRect(21532, -708, 88, 412);
spawn.mapRect(15483.8, 12.5, 388, 30);//broken floor
spawn.mapRect(15487.6, 76.6, 488, 24);
spawn.mapRect(15506.5, 134.2, 288, 45);
spawn.mapVertex(16758.6, 135.3, "400 -30 -350 -40 -400 30 400 30");
spawn.mapVertex(16758.6, 55.3, "423 -30 -408 -20 -400 20 400 20");
//tank
spawn.mapRect(15310, -960, 600, 135);
spawn.mapRect(16290, -960, 800, 135);
//in tank
spawn.mapRect(16524.8, -2726.8, 40, 400);
spawn.mapRect(16524.8, -2130.9, 400, 40);
spawn.mapRect(16010.2, -2412.2, 300, 40);
spawn.mapRect(15379.2, -2055.1, 400, 40);
spawn.mapVertex(17626.3, -3035, "-245 0 -220 -110 -173 -173 -110 -220 0 -250 110 -220 173 -173 220 -110 245 0");
spawn.mapRect(17226.3, -3035, 400, 40);
spawn.mapVertex(17626.3, 225, "-245 0 -220 110 -173 173 -110 220 0 250 110 220 173 173 220 110 245 0");
spawn.mapRect(17226.3, 225, 400, 40);
spawn.mapVertex(19626.3, -3035, "-245 0 -220 -110 -173 -173 -110 -220 0 -250 110 -220 173 -173 220 -110 245 0");
spawn.mapRect(19226.3, -3035, 400, 40);
spawn.mapVertex(19626.3, 225, "-245 0 -220 110 -173 173 -110 220 0 250 110 220 173 173 220 110 245 0");
spawn.mapRect(19226.3, 225, 400, 40);
spawn.mapVertex(21626.3, -3035, "-245 0 -220 -110 -173 -173 -110 -220 0 -250 110 -220 173 -173 220 -110 245 0");
spawn.mapRect(21226.3, -3035, 400, 40);
spawn.mapVertex(21626.3, 225, "-245 0 -220 110 -173 173 -110 220 0 250 110 220 173 173 220 110 245 0");
spawn.mapRect(21226.3, 225, 400, 40);
//add fuel tanks in the last room
spawn.mapRect(21531.9, -707.8, 488, 8);
//22185.5, -114.8
spawn.mapVertex(22207.8, -103, "325 -200 100 -200 325 -300");
spawn.mapRect(22056.6, -70, 225, 212);
spawn.mapVertex(20723.1, -1734, "325 -200 100 -200 325 -300");
spawn.mapRect(20571.9, -1701.0, 225, 212);
spawn.mapVertex(22207.8, -103, "325 -200 100 -200 325 -300");
spawn.mapRect(22056.6, -70, 225, 212);
//spawn.mapVertex(x,y, "coordinates")
//the parts in quotes is "x y x y x y x y x y" x and y need to be the coordinates of points that define the shape in a concave clockwise direction
//second ship blocks/debris
spawn.bodyRect(21525, -113, 50, 50, 9);//first button block
spawn.bodyRect(18993, -2283, 50, 50, 9);//second button block
spawn.bodyRect(20303, -1736, 50, 50, 9);//third button block
// spawn.randomLevelBoss(17902, -1689, ["blinkBoss", "shooterBoss", "launcherBoss", "pulsarBoss", "blockBoss", "bladeBoss", "revolutionBoss", "spawnerBossCulture", "spiderBoss", "sneakBoss", "snakeSpitBoss"])
spawn.randomLevelBoss(17902, -1689, ["launcherBoss", "laserTargetingBoss", "blinkBoss", "streamBoss", "historyBoss", "grenadierBoss", "blockBoss", "revolutionBoss", "slashBoss"]);
// powerUps.spawnStartingPowerUps(1475, -1175);
// spawn.debris(750, -2200, 3700, 16); //16 debris per level
// spawn.bodyRect(1540, -1110, 300, 25, 0.9);
// spawn.randomSmallMob(1300, -70);
// spawn.randomMob(2650, -975, 0.8);
// spawn.randomGroup(1700, -900, 0.4);
// if (simulation.difficulty > 1) spawn.randomLevelBoss(2200, -1300);
// spawn.secondaryBossChance(100, -1500)
powerUps.addResearchToLevel() //needs to run after mobs are spawned
},
clock() {
simulation.makeTextLog(`<strong>clock</strong> by <span class='color-var'>Cornbread 2100</span>`);
function drawBackgroundGear(x, y, r1, r2, rot, color, speed, numTeeth = 5, toothWidth = 75, linew = 2) {
var vertices = getGearVertices(x, y, r1, r2, numTeeth, simulation.cycle * speed + rot, toothWidth / 100);
// draw gear
ctx.beginPath();
ctx.moveTo(vertices[0].x, vertices[0].y);
for (var i = 1; i < vertices.length; i++) {
ctx.lineTo(vertices[i].x, vertices[i].y);
}
ctx.lineTo(vertices[0].x, vertices[0].y);
ctx.lineWidth = 2;
ctx.fillStyle = color;
ctx.fill();
ctx.strokeStyle = "#3a3f20";
ctx.lineWidth = linew;
ctx.stroke();
}
function drawFallingBackgroundGear(x, y, r1, r2, rot, color, speed, fallSpeed, startCycle, numTeeth = 5, linew = 2) {
rot *= speed;
numTeeth *= 2;
const gearInc = (2 * Math.PI) / numTeeth;
ctx.beginPath()
for (var i = 0; i <= numTeeth; i++) {
var gear_r = r2;
if (i % 2 == 1) gear_r = r1;
ctx.arc(x, y + (simulation.cycle - startCycle) * fallSpeed, gear_r, (i * gearInc) + rot, ((i + 1) * gearInc) + rot);
}
ctx.fillStyle = color;
ctx.fill();
ctx.strokeStyle = "#3a3f20";
ctx.lineWidth = linew;
ctx.stroke();
}
function getGearVertices(x, y, r1, r2, numTeeth, rot = 0, teethWidth = 0) {
if (teethWidth == 0) {
teethWidth = (2 * Math.PI) / (2 * numTeeth);
}
const gearInc = (2 * Math.PI) / numTeeth;
var vertices = [];
for (var i = 0; i < numTeeth; i++) {
//inner vertices of gear teeth
var distance = i * gearInc + rot;
var vX = Math.sin(distance + teethWidth / 2) * r1;
var vY = Math.cos(distance + teethWidth / 2) * r1;
var point1 = { x: vX, y: vY, point: 1 };
vX = Math.sin(distance - teethWidth / 2) * r1;
vY = Math.cos(distance - teethWidth / 2) * r1;
var point4 = { x: vX, y: vY, point: 4 };
vX = Math.sin(distance) * r1;
vY = Math.cos(distance) * r1;
if (vX == 0) {
vX = 0.0001
}
var slope = vY / vX;
var angle = Math.atan2(vY, vX);
//outer vertices of gear teeth
var point2 = { x: point1.x, y: point1.y, point: 2 };
point2.x += Math.cos(angle) * (r2 - r1);
point2.y += Math.sin(angle) * (r2 - r1);
var point3 = { x: point4.x, y: point4.y, point: 3 };
point3.x += Math.cos(angle) * (r2 - r1);
point3.y += Math.sin(angle) * (r2 - r1);
vertices.push(point4);
vertices.push(point3);
vertices.push(point2);
vertices.push(point1);
}
for (var i = 0; i < vertices.length; i++) {
vertices[i].x += x;
vertices[i].y += y;
}
return vertices;
}
function getGearTeethVertices(x, y, r1, r2, numTeeth, toothIndex, teethWidth = 0) {
if (teethWidth == 0) {
teethWidth = (2 * Math.PI) / (2 * numTeeth);
}
const gearInc = (2 * Math.PI) / numTeeth;
var vertices = [];
for (var i = 0; i < numTeeth; i++) {
//inner vertices of gear teeth
var distance = i * gearInc;
var vX = Math.sin(distance + teethWidth / 2) * r1;
var vY = Math.cos(distance + teethWidth / 2) * r1;
var point1 = { x: vX, y: vY, point: 1 };
vX = Math.sin(distance - teethWidth / 2) * r1;
vY = Math.cos(distance - teethWidth / 2) * r1;
var point4 = { x: vX, y: vY, point: 4 };
vX = Math.sin(distance) * r1;
vY = Math.cos(distance) * r1;
if (vX == 0) {
vX = 0.0001
}
var slope = vY / vX;
var angle = Math.atan2(vY, vX);
//outer vertices of gear teeth
var point2 = { x: point1.x, y: point1.y, point: 2 };
point2.x += Math.cos(angle) * (r2 - r1);
point2.y += Math.sin(angle) * (r2 - r1);
var point3 = { x: point4.x, y: point4.y, point: 3 };
point3.x += Math.cos(angle) * (r2 - r1);
point3.y += Math.sin(angle) * (r2 - r1);
if (i == toothIndex) {
vertices.push(point4);
vertices.push(point3);
vertices.push(point2);
vertices.push(point1);
}
}
for (var i = 0; i < vertices.length; i++) {
vertices[i].x += x;
vertices[i].y += y;
}
return vertices;
}
function mapGear(x, y, r1, r2, rot, speed, numTeeth = 5, toothWidth = 50, additionalCircleRadius = 10) {
const part1 = body[body.length] = Bodies.polygon(x, y, 0, r1 + additionalCircleRadius, {
collisionFilter: {
category: cat.body,
mask: cat.player | cat.body | cat.bullet | cat.powerUp | cat.mob | cat.mobBullet
},
isNotHoldable: true,
frictionAir: 0,
friction: 1,
frictionStatic: 1,
restitution: 0
});
var parts = [part1];
for (var i = 0; i < numTeeth; i++) {
var toothVertices = getGearTeethVertices(0, 0, r2 - r1, toothWidth + r2 - r1, numTeeth, i, 70); // for some reason the teeth are sideways
var center = { // the center of the inner line of the gear
x: toothVertices[3].x - toothVertices[0].x,
y: toothVertices[3].y - toothVertices[0].y
};
distanceToCenter = Math.sqrt((center.x ** 2) + (center.y ** 2));
var radiusScale = (r1 + ((r2 - r1) / 2)) / distanceToCenter;
gearToothSlope = center.y / center.x;
var newPart = body[body.length] = Bodies.fromVertices(x + center.x * radiusScale, y + center.y * radiusScale, toothVertices, {
collisionFilter: {
category: cat.body,
mask: cat.player | cat.body | cat.bullet | cat.powerUp | cat.mob | cat.mobBullet
},
isNotHoldable: true,
frictionAir: 0.01,
friction: 1,
frictionStatic: 1,
restitution: 0
});
parts.push(newPart);
}
const who = Body.create({
parts: parts
});
Composite.add(engine.world, who);
composite[composite.length] = who;
who.collisionFilter.category = cat.body;
who.collisionFilter.mask = cat.body | cat.player | cat.bullet | cat.mob | cat.mobBullet | cat.map
const constraint = Constraint.create({
pointA: {
x: x,
y: y
},
bodyB: who,
stiffness: 1,
damping: 1
});
Matter.Body.setDensity(who, 0.0001)
Composite.add(engine.world, constraint);
Matter.Body.setAngle(who, 0)
Matter.Body.setAngularVelocity(who, 0);
who.center = { x: x, y: y }
who.rotate = function () {
var rotation = simulation.cycle * speed + rot;
Matter.Body.setAngle(who, rotation);
}
who.gearSettings = {
x: x,
y: y,
r1: r1,
r2: r2,
rot: rot,
speed: speed,
numTeeth: numTeeth,
toothWidth: toothWidth
}
return who;
}
function clockHand(x, y, width, height, speed = 15 * Math.PI / 180, angle = 0, density = 0.001) {
var who1 = body[body.length] = Bodies.rectangle(x, y + height / 2, width, height, {
collisionFilter: {
category: cat.body,
mask: cat.player | cat.body | cat.bullet | cat.powerUp | cat.mob | cat.mobBullet
},
isNotHoldable: true,
friction: 1,
frictionStatic: 1,
restitution: 0
});
const who = Body.create({
parts: [who1],
handRotation: 0
});
Composite.add(engine.world, who);
composite[composite.length] = who;
who.collisionFilter.category = cat.body;
who.collisionFilter.mask = cat.body | cat.player | cat.bullet | cat.mob | cat.mobBullet | cat.map
who.position.y = y;
const constraint = Constraint.create({
pointA: {
x: who.position.x,
y: who.position.y
},
bodyB: who,
stiffness: 1,
damping: 1
});
Matter.Body.setDensity(who, density)
Composite.add(engine.world, constraint);
who.center = { x: who.position.x, y: who.position.y }
who.rotate = function () {
if (simulation.cycle % 60 == 0) {
who.handRotation += speed;
if (Math.abs(who.handRotation % (Math.PI * 2) - Math.PI) < 0.2) {
// spawn random mob at exit door
const pick = spawn.fullPickList[Math.floor(Math.random() * spawn.fullPickList.length)];
spawn[pick](300, 600);
}
if (Matter.Query.collides(player, [this]).length != 0) {
var playerAngle = Math.atan((m.pos.y - y) / (m.pos.x - x));
if (m.pos.x - x < 0) playerAngle += Math.PI;
const playerDistance = Math.sqrt((m.pos.x - x) ** 2 + (m.pos.y - y) ** 2);
Matter.Body.setPosition(player, {
x: x + Math.cos(playerAngle + speed) * playerDistance,
y: y + Math.sin(playerAngle + speed) * playerDistance
})
}
}
Matter.Body.setAngle(who, who.handRotation + angle);
}
return who
}
function pendulum(x, y, width, height, swingTime = 50, swingDistanceMultiplier = 0.5, bobSides = 0, bobRadius = 200, density = 100, angle = 0, frictionAir = 0, angularVelocity = 0) {
const who1 = body[body.length] = Bodies.rectangle(x, y + height / 2, width, height, {
collisionFilter: {
category: cat.body,
mask: cat.player | cat.body | cat.bullet | cat.powerUp | cat.mob | cat.mobBullet
},
isNotHoldable: true,
frictionAir: frictionAir,
friction: 1,
frictionStatic: 1,
restitution: 0
});
const who2 = body[body.length] = Bodies.polygon(x, y + height, bobSides, bobRadius, {
collisionFilter: {
category: cat.body,
mask: cat.player | cat.body | cat.bullet | cat.powerUp | cat.mob | cat.mobBullet
},
isNotHoldable: true,
frictionAir: 0.01,
friction: 1,
frictionStatic: 1,
restitution: 0
});
const who = Body.create({
parts: [who1, who2],
});
Composite.add(engine.world, who);
composite[composite.length] = who;
who.collisionFilter.category = cat.body;
who.collisionFilter.mask = cat.body | cat.player | cat.bullet | cat.mob | cat.mobBullet | cat.map
who.position.y = y;
const constraint = Constraint.create({
pointA: {
x: x,
y: y
},
bodyB: who,
stiffness: 1,
damping: 1
});
Matter.Body.setDensity(who, density)
Composite.add(engine.world, constraint);
Matter.Body.setAngle(who, angle)
Matter.Body.setAngularVelocity(who, angularVelocity);
who.center = { x: x, y: y + height / 2 }
who.rotate = function () {
var rotation = Math.sin(simulation.cycle / swingTime) * swingDistanceMultiplier;
if (Matter.Query.collides(player, [this]).length != 0) {
var playerAngle = Math.atan((player.position.y - y) / (player.position.x - x)) + rotation - Math.sin((simulation.cycle - 1) / swingTime) * swingDistanceMultiplier;
if (player.position.x - x < 0) playerAngle += Math.PI;
const playerDistance = Math.sqrt((player.position.x - x) ** 2 + (player.position.y - y) ** 2);
Matter.Body.setPosition(player, {
x: x + Math.cos(playerAngle) * playerDistance,
y: y + Math.sin(playerAngle) * playerDistance
})
}
Matter.Body.setAngle(who, rotation);
}
return who;
}
function gearMob(x, y, leaveBody = true, autoFindPlayer = false, radius = Math.floor(25 + 40 * Math.random()), teethRadius = 0) {
if (teethRadius == 0) {
teethRadius = radius + 15 + Math.floor(Math.random() * 20);
}
mobs.spawn(x, y, 0, teethRadius, "transparent");
let me = mob[mob.length - 1];
me.stroke = "transparent";
me.delay = 100 + 40 * simulation.CDScale;
me.accelMag = Math.PI / 10000;
me.memory = 120;
me.seeAtDistance2 = 2000000; // 140
Matter.Body.setDensity(me, 0.001);
me.leaveBody = leaveBody;
const numTeeth = Math.round(5 + Math.random() * 3);
me.gearRotation = 0;
me.gearSpeed = Math.round(-0.1 + Math.random() * 0.2);
me.gearAccelerating = true;
me.do = function () {
if (autoFindPlayer) {
me.locatePlayer();
}
this.seePlayerByLookingAt();
this.checkStatus();
this.attraction();
if (me.gearAccelerating && (Math.random() > 0.99 || me.gearSpeed >= 0.1)) {
me.gearAccelerating = false;
} else if (!me.gearAccelerating && (Math.random() > 0.99 || me.gearSpeed <= -0.1)) {
me.gearAccelerating = true;
}
if (me.gearAccelerating) {
me.gearSpeed += 0.001;
} else {
me.gearSpeed -= 0.001;
}
me.gearRotation += me.gearSpeed;
var newVertices = getGearVertices(me.position.x, me.position.y, radius, teethRadius, numTeeth, me.gearRotation);
// draw body
ctx.beginPath();
ctx.moveTo(newVertices[0].x, newVertices[0].y);
for (let i = 1; i < newVertices.length; i++) {
ctx.lineTo(newVertices[i].x, newVertices[i].y);
}
ctx.lineTo(newVertices[0].x, newVertices[0].y);
ctx.fillStyle = "#7b3f00";
ctx.fill();
ctx.strokeStyle = "#000";
ctx.lineWidth = 2;
ctx.stroke();
}
}
function customDoor(x, y, width, height) {
x = x + width / 2
y = y + height / 2
const doorBlock = body[body.length] = Bodies.rectangle(x, y, width, height, {
collisionFilter: {
category: cat.map,
mask: cat.player | cat.body | cat.bullet | cat.powerUp | cat.mob | cat.mobBullet //cat.player | cat.map | cat.body | cat.bullet | cat.powerUp | cat.mob | cat.mobBullet
},
inertia: Infinity, //prevents rotation
isNotHoldable: true,
friction: 1,
frictionStatic: 1,
restitution: 0,
isClosing: false,
setPos(x, y) {
if (y - this.position.y <= 0 || ( // only move down if clear of stuff
Matter.Query.ray([player], Matter.Vector.create(this.position.x - width / 2, this.position.y + height / 2), Matter.Vector.create(this.position.x + width / 2, this.position.y + height / 2), 5).length === 0 &&
Matter.Query.ray(body, Matter.Vector.create(this.position.x - width / 2, this.position.y + height / 2), Matter.Vector.create(this.position.x + width / 2, this.position.y + height / 2), 5).length <= 1 &&
Matter.Query.ray(mob, Matter.Vector.create(this.position.x - width / 2, this.position.y + height / 2), Matter.Vector.create(this.position.x + width / 2, this.position.y + height / 2), 5).length === 0)
) {
const position = {
x: x,
y: y
}
Matter.Body.setPosition(this, position);
}
},
draw() {
ctx.fillStyle = "#555"
ctx.beginPath();
const v = this.vertices;
ctx.moveTo(v[0].x, v[0].y);
for (let i = 1; i < v.length; ++i) {
ctx.lineTo(v[i].x, v[i].y);
}
ctx.lineTo(v[0].x, v[0].y);
ctx.fill();
}
});
Matter.Body.setStatic(doorBlock, true); //make static
Composite.add(engine.world, doorBlock); //add to world
doorBlock.classType = "body"
return doorBlock
}
function horizontalDoor(x, y, width, height, distance, speed = 1) {
x = x + width / 2
y = y + height / 2
const doorBlock = body[body.length] = Bodies.rectangle(x, y, width, height, {
collisionFilter: {
category: cat.map,
mask: cat.player | cat.body | cat.bullet | cat.powerUp | cat.mob | cat.mobBullet //cat.player | cat.map | cat.body | cat.bullet | cat.powerUp | cat.mob | cat.mobBullet
},
inertia: Infinity, //prevents rotation
isNotHoldable: true,
friction: 1,
frictionStatic: 1,
restitution: 0,
isClosing: false,
openClose() {
if (!m.isBodiesAsleep) {
if (this.isClosing) {
if (this.position.x > x) { //try to close
if ( //if clear of stuff
Matter.Query.collides(this, [player]).length === 0 &&
Matter.Query.collides(this, body).length < 2 &&
Matter.Query.collides(this, mob).length === 0
) {
const position = {
x: this.position.x - speed,
y: this.position.y
}
Matter.Body.setPosition(this, position)
}
}
} else {
if (this.position.x < x + distance) { //try to open
const position = {
x: this.position.x + speed,
y: this.position.y
}
Matter.Body.setPosition(this, position)
}
}
}
},
isClosed() {
return this.position.x < x + 1
},
draw() {
ctx.fillStyle = "#555"
ctx.beginPath();
const v = this.vertices;
ctx.moveTo(v[0].x, v[0].y);
for (let i = 1; i < v.length; ++i) {
ctx.lineTo(v[i].x, v[i].y);
}
ctx.lineTo(v[0].x, v[0].y);
ctx.fill();
}
});
Matter.Body.setStatic(doorBlock, true); //make static
Composite.add(engine.world, doorBlock); //add to world
doorBlock.classType = "body"
return doorBlock
}
function drawBelt(circle1, circle2) {
// circle 1
const distance = Math.sqrt((circle2.x - circle1.x) ** 2 + (circle2.y - circle1.y) ** 2);
const distanceToIntersection = (-circle1.radius * distance) / (-circle1.radius + circle2.radius);
const slopeAngle = Math.atan((circle2.y - circle1.y) / (circle2.x - circle1.x));
const angleToTangent = Math.acos(-circle1.radius / distanceToIntersection);
const tangentIntersection = {
x: Math.cos(slopeAngle) * distanceToIntersection + circle1.x,
y: Math.sin(slopeAngle) * distanceToIntersection + circle1.y
}
const tangentPoint = {
x: Math.cos(angleToTangent + slopeAngle) * -circle1.radius + circle1.x,
y: Math.sin(angleToTangent + slopeAngle) * -circle1.radius + circle1.y
}
const invertedTangentPoint = {
x: Math.cos(-angleToTangent + slopeAngle) * -circle1.radius + circle1.x,
y: Math.sin(-angleToTangent + slopeAngle) * -circle1.radius + circle1.y
}
// circle 2
const tangentPoint2 = {
x: Math.cos(angleToTangent + slopeAngle) * -circle2.radius + circle2.x,
y: Math.sin(angleToTangent + slopeAngle) * -circle2.radius + circle2.y
}
const invertedTangentPoint2 = {
x: Math.cos(-angleToTangent + slopeAngle) * -circle2.radius + circle2.x,
y: Math.sin(-angleToTangent + slopeAngle) * -circle2.radius + circle2.y
}
// draw
ctx.beginPath();
ctx.moveTo(tangentPoint.x, tangentPoint.y);
ctx.lineTo(tangentPoint2.x, tangentPoint2.y);
const newAngle = Math.atan((tangentPoint2.y - circle2.y) / (tangentPoint2.x - circle2.x));
const newAngle2 = Math.atan((invertedTangentPoint2.y - circle2.y) / (invertedTangentPoint2.x - circle2.x));
ctx.arc(circle2.x, circle2.y, circle2.radius, newAngle, newAngle2 + Math.PI);
ctx.lineTo(invertedTangentPoint.x, invertedTangentPoint.y);
const newAngle3 = Math.atan((invertedTangentPoint.y - circle1.y) / (invertedTangentPoint.x - circle1.x));
const newAngle4 = Math.atan((tangentPoint.y - circle1.y) / (tangentPoint.x - circle1.x));
ctx.arc(circle1.x, circle1.y, circle1.radius, newAngle3 + Math.PI, newAngle4);
ctx.strokeStyle = '#000';
ctx.lineWidth = 5;
ctx.stroke();
}
function drawDiagonalBelt(circle1, circle2) {
// circle 1
const distance = Math.sqrt((circle2.x - circle1.x) ** 2 + (circle2.y - circle1.y) ** 2);
const distanceToIntersection = (circle1.radius * distance) / (circle1.radius + circle2.radius);
const slopeAngle = Math.atan((circle2.y - circle1.y) / (circle2.x - circle1.x));
const angleToTangent = Math.acos(circle1.radius / distanceToIntersection);
const tangentIntersection = {
x: Math.cos(slopeAngle) * distanceToIntersection + circle1.x,
y: Math.sin(slopeAngle) * distanceToIntersection + circle1.y
}
const tangentPoint = {
x: Math.cos(angleToTangent + slopeAngle) * circle1.radius + circle1.x,
y: Math.sin(angleToTangent + slopeAngle) * circle1.radius + circle1.y
}
const invertedTangentPoint = {
x: Math.cos(-angleToTangent + slopeAngle) * circle1.radius + circle1.x,
y: Math.sin(-angleToTangent + slopeAngle) * circle1.radius + circle1.y
}
// circle 2
const tangentPoint2 = {
x: Math.cos(angleToTangent + slopeAngle) * -circle2.radius + circle2.x,
y: Math.sin(angleToTangent + slopeAngle) * -circle2.radius + circle2.y
}
const invertedTangentPoint2 = {
x: Math.cos(-angleToTangent + slopeAngle) * -circle2.radius + circle2.x,
y: Math.sin(-angleToTangent + slopeAngle) * -circle2.radius + circle2.y
}
// draw
ctx.beginPath();
ctx.moveTo(tangentPoint.x, tangentPoint.y);
ctx.lineTo(tangentPoint2.x, tangentPoint2.y);
const newAngle = Math.atan((tangentPoint2.y - circle2.y) / (tangentPoint2.x - circle2.x));
const newAngle2 = Math.atan((invertedTangentPoint2.y - circle2.y) / (invertedTangentPoint2.x - circle2.x));
ctx.arc(circle2.x, circle2.y, circle2.radius, newAngle, newAngle2 + Math.PI);
ctx.lineTo(invertedTangentPoint.x, invertedTangentPoint.y);
const newAngle3 = Math.atan((invertedTangentPoint.y - circle1.y) / (invertedTangentPoint.x - circle1.x));
const newAngle4 = Math.atan((tangentPoint.y - circle1.y) / (tangentPoint.x - circle1.x));
ctx.arc(circle1.x, circle1.y, circle1.radius, newAngle3, newAngle4 + Math.PI, true);
ctx.strokeStyle = '#000';
ctx.lineWidth = 5;
ctx.stroke();
}
function getIntersection(v1, v1End, domain) {
const intersections = getIntersections(v1, v1End, domain);
var best = {
x: v1End.x,
y: v1End.y,
dist: Math.sqrt((v1End.x - v1.x) ** 2 + (v1End.y - v1.y) ** 2)
}
for (const intersection of intersections) {
const dist = Math.sqrt((intersection.x - v1.x) ** 2 + (intersection.y - v1.y) ** 2);
if (dist < best.dist) {
best = {
x: intersection.x,
y: intersection.y,
dist: dist
};
}
}
return best;
}
function getIntersections(v1, v1End, domain) {
const intersections = [];
for (const obj of domain) {
for (var i = 0; i < obj.vertices.length - 1; i++) {
results = simulation.checkLineIntersection(v1, v1End, obj.vertices[i], obj.vertices[i + 1]);
if (results.onLine1 && results.onLine2) intersections.push({ x: results.x, y: results.y });
}
results = simulation.checkLineIntersection(v1, v1End, obj.vertices[obj.vertices.length - 1], obj.vertices[0]);
if (results.onLine1 && results.onLine2) intersections.push({ x: results.x, y: results.y });
}
return intersections;
}
function circleLoS(pos, radius, domain) {
function allCircleLineCollisions(c, radius, domain) {
var lines = [];
for (const obj of domain) {
//const obj = domain[0]
for (var i = 0; i < obj.vertices.length - 1; i++) {
lines.push(circleLineCollisions(obj.vertices[i], obj.vertices[i + 1], c, radius));
}
lines.push(circleLineCollisions(obj.vertices[obj.vertices.length - 1], obj.vertices[0], c, radius));
}
const collisionLines = [];
for (const line of lines) {
if (line.length == 2) {
const distance1 = Math.sqrt((line[0].x - c.x) ** 2 + (line[0].y - c.y) ** 2)
const angle1 = Math.atan2(line[0].y - c.y, line[0].x - c.x);
const queryPoint1 = {
x: Math.cos(angle1) * (distance1 - 1) + c.x,
y: Math.sin(angle1) * (distance1 - 1) + c.y
}
const distance2 = Math.sqrt((line[1].x - c.x) ** 2 + (line[1].y - c.y) ** 2)
const angle2 = Math.atan2(line[1].y - c.y, line[1].x - c.x);
const queryPoint2 = {
x: Math.cos(angle2) * (distance2 - 1) + c.x,
y: Math.sin(angle2) * (distance2 - 1) + c.y
}
collisionLines.push(line)
}
}
return collisionLines;
}
function circleLineCollisions(a, b, c, radius) {
// calculate distances
const angleOffset = Math.atan2(b.y - a.y, b.x - a.x);
const sideB = Math.sqrt((a.x - c.x) ** 2 + (a.y - c.y) ** 2);
const sideC = Math.sqrt((b.x - a.x) ** 2 + (b.y - a.y) ** 2);
const sideA = Math.sqrt((c.x - b.x) ** 2 + (c.y - b.y) ** 2);
// calculate the closest point on line AB to point C
const angleA = Math.acos((sideB ** 2 + sideC ** 2 - sideA ** 2) / (2 * sideB * sideC)) * (a.x - c.x) / -Math.abs(a.x - c.x)
const sideAD = Math.cos(angleA) * sideB;
const d = { // closest point
x: Math.cos(angleOffset) * sideAD + a.x,
y: Math.sin(angleOffset) * sideAD + a.y
}
const distance = Math.sqrt((d.x - c.x) ** 2 + (d.y - c.y) ** 2);
if (distance == radius) {
// tangent
return [d];
} else if (distance < radius) {
// secant
const angleOffset = Math.atan2(d.y - c.y, d.x - c.x);
const innerAngle = Math.acos(distance / radius);
const intersection1 = {
x: Math.cos(angleOffset + innerAngle) * radius + c.x,
y: Math.sin(angleOffset + innerAngle) * radius + c.y
}
const intersection2 = {
x: Math.cos(angleOffset - innerAngle) * radius + c.x,
y: Math.sin(angleOffset - innerAngle) * radius + c.y
}
const distance1 = {
a: Math.sqrt((intersection1.x - a.x) ** 2 + (intersection1.y - a.y) ** 2),
b: Math.sqrt((intersection1.x - b.x) ** 2 + (intersection1.y - b.y) ** 2)
}
const distance2 = {
a: Math.sqrt((intersection2.x - a.x) ** 2 + (intersection2.y - a.y) ** 2),
b: Math.sqrt((intersection2.x - b.x) ** 2 + (intersection2.y - b.y) ** 2)
}
const result = [];
if (Math.abs(sideC - (distance1.a + distance1.b)) < 0.01) {
result.push(intersection1);
} else {
if (distance1.a < distance1.b) {
if (sideB <= radius) result.push(a);
} else {
if (sideA <= radius) result.push(b)
}
}
if (Math.abs(sideC - (distance2.a + distance2.b)) < 0.01) {
result.push(intersection2);
} else {
if (distance2.a <= distance2.b) {
if (sideB <= radius) result.push(a);
} else {
if (sideA <= radius) result.push(b)
}
}
return result;
} else {
// no intersection
return [];
}
}
var vertices = [];
for (const obj of losDomain) {
for (var i = 0; i < obj.vertices.length; i++) {
const vertex = obj.vertices[i];
const angleToVertex = Math.atan2(vertex.y - pos.y, vertex.x - pos.x);
const queryPoint = {
x: Math.cos(angleToVertex + Math.PI) + vertex.x,
y: Math.sin(angleToVertex + Math.PI) + vertex.y
}
if (Matter.Query.ray(domain, pos, queryPoint).length == 0) {
var distance = Math.sqrt((vertex.x - pos.x) ** 2 + (vertex.y - pos.y) ** 2);
var endPoint = {
x: vertex.x,
y: vertex.y
}
if (distance > radius) {
const angle = Math.atan2(vertex.y - pos.y, vertex.x - pos.x);
endPoint = {
x: Math.cos(angle) * radius + pos.x,
y: Math.sin(angle) * radius + pos.y
}
distance = radius
}
var best = getIntersection(pos, endPoint, domain);
if (best.dist >= distance) {
best = {
x: endPoint.x,
y: endPoint.y,
dist: distance
}
}
vertices.push(best)
var angle = Math.atan2(vertex.y - pos.y, vertex.x - pos.x);
endPoint = {
x: Math.cos(angle + 0.001) * radius + pos.x,
y: Math.sin(angle + 0.001) * radius + pos.y
}
best = getIntersection(pos, endPoint, domain);
if (best.dist >= radius) {
best = {
x: endPoint.x,
y: endPoint.y,
dist: radius
}
}
vertices.push(best)
angle = Math.atan2(vertex.y - pos.y, vertex.x - pos.x);
endPoint = {
x: Math.cos(angle - 0.001) * radius + pos.x,
y: Math.sin(angle - 0.001) * radius + pos.y
}
best = getIntersection(pos, endPoint, domain);
if (best.dist >= radius) {
best = {
x: endPoint.x,
y: endPoint.y,
dist: radius
}
}
vertices.push(best)
}
}
}
const outerCollisions = allCircleLineCollisions(pos, radius, domain);
const circleCollisions = [];
for (const line of outerCollisions) {
for (const vertex of line) {
const distance = Math.sqrt((vertex.x - pos.x) ** 2 + (vertex.y - pos.y) ** 2)
const angle = Math.atan2(vertex.y - pos.y, vertex.x - pos.x);
const queryPoint = {
x: Math.cos(angle) * (distance - 1) + pos.x,
y: Math.sin(angle) * (distance - 1) + pos.y
}
if (Math.abs(distance - radius) < 1 && Matter.Query.ray(domain, pos, queryPoint).length == 0) circleCollisions.push(vertex)
}
}
for (var i = 0; i < circleCollisions.length; i++) {
const vertex = circleCollisions[i];
var nextIndex = i + 1;
if (nextIndex == circleCollisions.length) nextIndex = 0;
const nextVertex = circleCollisions[nextIndex];
const angle1 = Math.atan2(vertex.y - pos.y, vertex.x - pos.x);
const angle2 = Math.atan2(nextVertex.y - pos.y, nextVertex.x - pos.x);
var newAngle;
if (Math.abs(angle1) > Math.PI / 2 && Math.abs(angle2) > Math.PI / 2 && angle1 / Math.abs(angle1) != angle2 / Math.abs(angle2)) {
// if the arc between the to points crosses over the left side (+/- pi radians)
const newAngle1 = (Math.PI - Math.abs(angle1)) * (angle1 / Math.abs(angle1));
const newAngle2 = (Math.PI - Math.abs(angle2)) * (angle2 / Math.abs(angle2));
newAngle = (newAngle1 + newAngle2) / 2;
var multiplier;
if (newAngle == 0) {
multiplier = 1;
} else {
multiplier = newAngle / Math.abs(newAngle);
}
newAngle = Math.PI * multiplier - newAngle * multiplier;
test = true;
} else {
newAngle = (angle1 + angle2) / 2;
}
// shoot ray between them
var endPoint = {
x: Math.cos(newAngle) * radius + pos.x,
y: Math.sin(newAngle) * radius + pos.y
}
var best = getIntersection(pos, endPoint, domain);
vertices.push(vertex);
if (best.dist <= radius) vertices.push({ x: best.x, y: best.y })
}
vertices.sort((a, b) => Math.atan2(a.y - pos.y, a.x - pos.x) - Math.atan2(b.y - pos.y, b.x - pos.x));
return vertices;
}
function compareArrays(array1, array2) {
for (var i = 0; i < array1.length; i++) {
if (array1[i] != array2[i]) return false;
}
return true;
}
function generateIntersectMap() {
// include intersections in map elements to avoid LoS issues with overlapping
const intersectMap = [];
for (var i = 0; i < map.length; i++) {
const obj = map[i];
const newVertices = [];
const restOfMap = [...map].slice(0, i).concat([...map].slice(i + 1))
for (var j = 0; j < obj.vertices.length - 1; j++) {
var intersections = getIntersections(obj.vertices[j], obj.vertices[j + 1], restOfMap);
newVertices.push(obj.vertices[j]);
for (const vertex of intersections) {
newVertices.push({ x: vertex.x, y: vertex.y });
}
}
intersections = getIntersections(obj.vertices[obj.vertices.length - 1], obj.vertices[0], restOfMap);
newVertices.push(obj.vertices[obj.vertices.length - 1]);
for (const vertex of intersections) {
newVertices.push({ x: vertex.x, y: vertex.y });
}
intersectMap.push({ vertices: newVertices });
}
return intersectMap;
}
function addPartToMap(len) { // from "run" map
map[len].collisionFilter.category = cat.map;
map[len].collisionFilter.mask = cat.player | cat.map | cat.body | cat.bullet | cat.powerUp | cat.mob | cat.mobBullet;
Matter.Body.setStatic(map[len], true);
Composite.add(engine.world, map[len]);
}
level.setPosToSpawn(-500, -50);
spawn.mapRect(level.enter.x, level.enter.y + 20, 100, 20);
level.exit.x = 250;
level.exit.y = 720;
spawn.mapRect(level.exit.x, level.exit.y + 20, 100, 20);
level.defaultZoom = 1800;
simulation.zoomTransition(level.defaultZoom);
document.body.style.backgroundColor = "#d8dadf";
spawn.mapRect(-925, 0, 2650, 100);
spawn.mapRect(-925, -1700, 325, 1800);
spawn.mapRect(-650, -325, 325, 50);
spawn.mapRect(-650, -1400, 325, 50);
spawn.mapRect(-1700, -1700, 1100, 200);
spawn.mapRect(-1700, -4600, 300, 3100);
spawn.mapRect(-1700, -4600, 1250, 200);
spawn.mapRect(200, -4600, 2750, 200);
spawn.mapRect(-400, -4225, 200, 50);
spawn.mapRect(2800, -4600, 150, 1400);
spawn.mapRect(1350, -325, 100, 50);
spawn.mapRect(1400, -1500, 325, 1600);
spawn.mapRect(1400, -1500, 1550, 50);
spawn.mapRect(1400, -1900, 900, 50);
spawn.mapRect(1400, -2900, 100, 1050);
spawn.mapRect(-600, -2900, 3550, 100);
spawn.mapRect(2850, -2900, 100, 700);
spawn.mapRect(2850, -2200, 100, 350);
map[map.length - 1].fallsOff2 = true; // this piece will fall off in the middle of cutscene
spawn.mapRect(2300, -1900, 500, 50);
map[map.length - 1].fallsOff = true; // this piece wall fall off at the start of cutscene
spawn.mapRect(2800, -1900, 200, 50);
spawn.mapRect(2900, -1900, 50, 450);
powerUps.directSpawn(2700, -1675, "tech");
spawn.mapRect(2800, -3300, 825, 100);
spawn.mapRect(3525, -3300, 100, 3000);
spawn.mapRect(3400, -2850, 225, 50);
spawn.mapRect(2875, -2525, 175, 25);
spawn.mapRect(3325, -2675, 150, 25);
spawn.mapRect(3400, -2850, 75, 200);
spawn.mapRect(3150, -2225, 100, 25);
spawn.mapRect(-2300, 750, 5450, 100);
pendulum1 = pendulum(400, -2500, 75, 1700, 50, 0.3, 0, 300);
const gear1 = mapGear(-1200, -2000, 100, 200, 0, -0.05, 5, 75);
const gear2 = mapGear(-700, -2500, 150, 270, -0.5, 0.05, 5, 50);
const gear3 = mapGear(-3500, -1000, 1100, 1500, -0.5, 0.005, 10, 150, 40);
const piston1 = customDoor(1650, -1850, 100, 350); // x, y, width, height, distance, speed = 1
const piston2 = customDoor(1950, -1850, 100, 350);
const piston3 = horizontalDoor(-2000, -4200, 300, 100, 300, 20);
const piston4 = horizontalDoor(-2000, -3800, 300, 100, 300, 20);
const piston5 = horizontalDoor(-2000, -3400, 300, 100, 300, 20);
const piston6 = horizontalDoor(-2000, -3000, 300, 100, 300, 20);
const piston7 = horizontalDoor(-2000, -2600, 300, 100, 300, 20);
const hand1 = clockHand(400, -3700, 75, 600);
const elevator1 = level.elevator(3200, 0, 150, 50, -1750, 0.0025, { up: 0.05, down: 0.2 });
spawn.debris(-300, 0, 1300, 6);
spawn.debris(0, -2900, 2500, 8);
spawn.randomSmallMob(-500, -500, 1);
spawn.randomMob(190, -1300, 1);
spawn.randomMob(200, -320, 0.3);
spawn.randomMob(1000, -1100, 1);
spawn.randomMob(-160, -2050, 1);
spawn.randomMob(-1100, -2900, 0.5);
spawn.randomLevelBoss(1900, -3800, spawn.randomBossList.splice(0, spawn.randomBossList.indexOf("shieldingBoss"), 1).concat(spawn.randomBossList.splice(spawn.randomBossList.indexOf("shieldingBoss") + 1))); // shieldingBoss lags out the lighting system for some reason
spawn.randomMob(2500, -3500, 0.3);
spawn.randomMob(1300, -4100, 0.5);
spawn.randomMob(3400, -2450, 1);
spawn.randomMob(2850, -2050, 0.4);
spawn.randomGroup(-150, -2400, 0.5);
spawn.randomMob(-1250, -5150, 1);
spawn.randomMob(-2900, -4000, 0.4);
spawn.randomMob(-1350, -950, 1);
spawn.randomMob(2700, -850, 0.4);
spawn.randomMob(2500, -50, 0.4);
powerUps.addResearchToLevel() // needs to run after mobs are spawned
var dealtPiston1Damage = false;
var dealtPiston2Damage = false;
var dealtPiston1MobDamage = false;
var dealtPiston2MobDamage = false;
var lastPistonDirection = false;
var pistonsLocked = false;
var finishedGearFight = false;
var roofReadyToFall = false;
var roofFallCycle = 0;
var drawGear = false;
var gearCycle = simulation.cycle;
var gearPositions = [];
var pistonUnlockCycle = 0;
for (var i = 0; i < 15; i++) {
gearPositions.push({
x: 2400 + Math.random() * 200,
y: -3300 - Math.random() * 3000
});
}
var gearSizes = [];
for (var i = 0; i < 15; i++) {
const r1 = 30 + Math.random() * 50;
gearSizes.push({
r1: r1,
r2: r1 + 15 + Math.random() * 30
})
}
var circleHead = Matter.Bodies.polygon(m.pos.x, m.pos.y, 0, 31);
var losDomain = generateIntersectMap().concat(mob.filter((obj) => { return obj.isNotCloaked == null && (obj.isBoss || obj.label != 'Circle Body') }), [pendulum1, gear1, gear2, piston1, player, circleHead]);
var oldMap = [...map];
var oldMob = [...mob];
var spawnGearMobCycle = 0;
var gearsSpawned = 0;
var lastSmallGearRot = 0;
var smallGearRot = 0;
var smallGearPosRot = 0;
var bigGearRot = 0;
var finalGearRot;
var lastFinalGearRot;
var startCycle = simulation.cycle; // used to offset simulation.cycle to avoid the swing starting halfway through at the start of the level and messing up syncronization
level.custom = () => {
Matter.Body.setPosition(circleHead, m.pos)
if (!(compareArrays(oldMap, map) && compareArrays(oldMob, mob))) {
losDomain = generateIntersectMap().concat(mob.filter((obj) => { return obj.isNotCloaked == null && (obj.isBoss || obj.label != 'Circle Body') }), [pendulum1, gear1, gear2, piston1, player, circleHead]);
}
oldMap = [...map];
oldMob = [...mob];
ctx.fillStyle = "#b0b0b2";
ctx.fillRect(-600, -1700, 2000, 1700);
ctx.fillRect(1350, -1851, 1550, 350);
ctx.fillRect(-1400, -2950, 4250, 1450);
ctx.fillRect(-1400, -4400, 4350, 1500);
ctx.fillRect(-450, -4600, 650, 250);
ctx.fillRect(2750, -3200, 200, 1300);
ctx.fillRect(2750, -3200, 200, 1300);
ctx.fillStyle = "#000";
ctx.fillRect(350, -2800, 100, 25);
// light
var lightPos = { x: 400, y: -2775 };
var lightRadius = 2950;
const vertices = circleLoS(lightPos, lightRadius, map.concat(mob.filter((obj) => { return obj.isNotCloaked == null && (obj.isBoss || obj.label != 'Circle Body') }), [pendulum1, gear1, gear2, piston1, player, circleHead])); if (vertices.length > 0 && vertices[0].x) {
ctx.beginPath();
ctx.moveTo(vertices[0].x, vertices[0].y);
for (var i = 1; i < vertices.length; i++) {
var currentDistance = Math.sqrt((vertices[i - 1].x - lightPos.x) ** 2 + (vertices[i - 1].y - lightPos.y) ** 2);
var newDistance = Math.sqrt((vertices[i].x - lightPos.x) ** 2 + (vertices[i].y - lightPos.y) ** 2);
if (Math.abs(currentDistance - lightRadius) < 1 && Math.abs(newDistance - lightRadius) < 1) {
const currentAngle = Math.atan2(vertices[i - 1].y - lightPos.y, vertices[i - 1].x - lightPos.x);
const newAngle = Math.atan2(vertices[i].y - lightPos.y, vertices[i].x - lightPos.x);
ctx.arc(lightPos.x, lightPos.y, lightRadius, currentAngle, newAngle);
} else {
ctx.lineTo(vertices[i].x, vertices[i].y)
}
}
newDistance = Math.sqrt((vertices[0].x - lightPos.x) ** 2 + (vertices[0].y - lightPos.y) ** 2);
currentDistance = Math.sqrt((vertices[vertices.length - 1].x - lightPos.x) ** 2 + (vertices[vertices.length - 1].y - lightPos.y) ** 2);
if (Math.abs(currentDistance - lightRadius) < 1 && Math.abs(newDistance - lightRadius) < 1) {
const currentAngle = Math.atan2(vertices[vertices.length - 1].y - lightPos.y, vertices[vertices.length - 1].x - lightPos.x);
const newAngle = Math.atan2(vertices[0].y - lightPos.y, vertices[0].x - lightPos.x);
ctx.arc(lightPos.x, lightPos.y, lightRadius, currentAngle, newAngle);
} else {
ctx.lineTo(vertices[0].x, vertices[0].y)
}
ctx.fillStyle = "rgba(216, 218, 223, 0.5)";
ctx.fill();
}
ctx.beginPath();
ctx.moveTo(425, -2775);
ctx.arc(400, -2775, 25, 0, Math.PI);
ctx.fillStyle = "#c6aa12";
ctx.fill();
ctx.strokeStyle = "#000000";
ctx.lineWidth = 1;
ctx.stroke();
pendulum1.rotate();
gear1.rotate();
gear2.rotate();
gear3.rotate();
hand1.rotate();
drawBackgroundGear(-1200, -2300, 75, 150, 0.3, "#ccc", -0.05);
drawBackgroundGear(-1010, -2380, 30, 100, -0.1, "#ccc", 0.05);
// pendulum gears
if (!m.isBodiesAsleep) smallGearPosRot += Math.sin((simulation.cycle - startCycle) / 50) * 0.3 - Math.sin((simulation.cycle - startCycle - 1) / 50) * 0.3;
if (smallGearPosRot > 0.1) smallGearPosRot = 0.1;
if (smallGearPosRot < -0.1) smallGearPosRot = -0.1;
var circ = 2 * Math.PI * 150;
var arcLength = ((smallGearPosRot - Math.sin((simulation.cycle - startCycle) / 50) * 0.2) / (Math.PI * 2)) * circ;
lastSmallGearRot = smallGearRot;
smallGearRot = arcLength / (2 * Math.PI * 50) * Math.PI * -2 + 0.6;
if (Math.abs(smallGearPosRot) == 0.1) {
bigGearRot += Math.abs((smallGearRot - lastSmallGearRot) * (50 / 75));
}
drawBackgroundGear(740, -2625, 270, 330, bigGearRot, "#d2d3d4", 0, 15, 20); // the big one in the background
drawBackgroundGear(400, -2500, 100, 150, Math.sin((simulation.cycle - startCycle) / 50) * -0.3, "#ccc", 0, 8, 20); // attached to pendulum
drawBackgroundGear(400 + Math.cos(smallGearPosRot) * 200, -2500 + Math.sin(smallGearPosRot) * 200, 50, 75, smallGearRot, "#ccc", 0, 7, 20);
ctx.beginPath();
ctx.arc(400 + Math.cos(smallGearPosRot) * 200, -2500 + Math.sin(smallGearPosRot) * 200, 10, 0, 2 * Math.PI);
ctx.strokeStyle = "#444";
ctx.lineWidth = 10;
ctx.stroke();
ctx.beginPath();
ctx.arc(400, -2500, 200, -0.1, 0.1);
ctx.strokeStyle = "#444";
ctx.lineWidth = 10;
ctx.stroke();
drawBackgroundGear(740, -2625, 75, 110, bigGearRot, "#ccc", 0, 8, 20);
ctx.beginPath();
ctx.arc(740, -2625, 40, 0, 2 * Math.PI);
ctx.fillStyle = "#bbb";
ctx.fill();
ctx.strokeStyle = "#000";
ctx.lineWidth = 2;
ctx.stroke();
drawBackgroundGear(740, -2375, 75, 110, bigGearRot * -1, "#ccc", 0, 8, 20);
ctx.beginPath();
ctx.arc(740, -2375, 40, 0, 2 * Math.PI);
ctx.fillStyle = "#bbb";
ctx.fill();
ctx.strokeStyle = "#000";
ctx.lineWidth = 2;
ctx.stroke();
drawDiagonalBelt({ x: 740, y: -2625, radius: 40 }, { x: 740, y: -2375, radius: 40 })
if (finalGearRot != null) lastFinalGearRot = finalGearRot;
finalGearRot = Math.round((-bigGearRot * 294.72 / 25) * 100) / 100 + Math.PI / 2;
drawBackgroundGear(1080, -2650, 10, 20, finalGearRot, "#ccc", 0, 5, 50);
ctx.beginPath();
ctx.arc(1080, -2650, 10, 0, 2 * Math.PI);
ctx.fillStyle = "#bbb";
ctx.fill();
ctx.strokeStyle = "#000";
ctx.lineWidth = 2;
ctx.stroke();
drawBackgroundGear(1650, -2550, 300, 360, finalGearRot, "#ccc", 0, 6, 50);
ctx.beginPath();
ctx.arc(1650, -2550, 100, 0, 2 * Math.PI);
ctx.fillStyle = "#bbb";
ctx.fill();
ctx.strokeStyle = "#000";
ctx.lineWidth = 2;
ctx.stroke();
drawBelt({ x: 1080, y: -2650, radius: 10 }, { x: 1650, y: -2550, radius: 100 });
ctx.beginPath();
ctx.arc(Math.cos(-finalGearRot) * 294 + 1650, Math.sin(-finalGearRot) * 294 - 2550, 25, 0, 2 * Math.PI);
ctx.fillStyle = "#000";
ctx.fill();
drawBackgroundGear(2300, -2550, 300, 360, -finalGearRot + 0.5, "#ccc", 0, 6, 50);
ctx.beginPath();
ctx.arc(Math.cos(finalGearRot) * 294 + 2300, Math.sin(finalGearRot) * 294 - 2550, 25, 0, 2 * Math.PI);
ctx.fillStyle = "#000";
ctx.fill();
ctx.beginPath();
ctx.arc(1630, -2215, 15, 0, 2 * Math.PI);
ctx.fillStyle = "#000";
ctx.fill();
ctx.beginPath();
ctx.arc(1670, -2215, 15, 0, 2 * Math.PI);
ctx.fillStyle = "#000";
ctx.fill();
ctx.beginPath();
ctx.arc(1940, -2250, 15, 0, 2 * Math.PI);
ctx.fillStyle = "#000";
ctx.fill();
ctx.beginPath();
ctx.arc(2300, -2215, 15, 0, 2 * Math.PI);
ctx.fillStyle = "#000";
ctx.fill();
if (!finishedGearFight && !pistonsLocked && m.pos.x > 2100 && m.pos.x < 2900 && m.pos.y > -1850 && m.pos.y < -1500) {
pistonsLocked = true;
roofFallCycle = simulation.cycle + 250;
roofReadyToFall = true;
}
if (roofReadyToFall && simulation.cycle >= roofFallCycle) {
// section of roof is deleted
for (var i = 0; i < map.length; i++) {
if (map[i].fallsOff) {
Matter.Composite.remove(engine.world, map[i]);
map.splice(i, 1);
}
}
// replace it with a block
spawn.bodyRect(2310, -1900, 480, 50);
roofReadyToFall = false;
drawGear = true;
gearCycle = simulation.cycle + 100;
}
//draw some background gears falling when roof falls
if (drawGear && simulation.cycle >= gearCycle) {
for (var i = 0; i < 15; i++) {
drawFallingBackgroundGear(gearPositions[i].x, gearPositions[i].y, gearSizes[i].r1, gearSizes[i].r2, simulation.cycle, "#ccc", 0.1, 25, gearCycle);
}
if (spawnGearMobCycle == 0) {
spawnGearMobCycle = simulation.cycle + 100;
}
}
if (spawnGearMobCycle > 0 && simulation.cycle >= spawnGearMobCycle) {
if (gearsSpawned < 40) {
gearMob(1600 + Math.random() * 1000, -2300 - Math.random() * 300, false, true);
gearsSpawned++;
spawnGearMobCycle = simulation.cycle + 25 - (simulation.difficulty - simulation.difficultyMode) / 2;
} else if (pistonUnlockCycle == 0) {
pistonUnlockCycle = simulation.cycle + 50;
}
}
if (!finishedGearFight && pistonUnlockCycle > 0 && simulation.cycle > pistonUnlockCycle) {
pistonsLocked = false;
finishedGearFight = true;
for (var i = 0; i < map.length; i++) {
if (map[i].fallsOff2) {
Matter.Composite.remove(engine.world, map[i]);
map.splice(i, 1);
}
}
spawn.bodyRect(2850, -2180, 100, 280);
Matter.Body.setAngularVelocity(body[body.length - 1], 0.025);
}
if (Math.sin((simulation.cycle + 15) / 25) < 0 && !lastPistonDirection) { // 15 cycles early to line up better with pendulum
piston3.isClosing = true;
piston4.isClosing = false;
piston5.isClosing = true;
piston6.isClosing = false;
piston7.isClosing = true;
} else if (Math.sin((simulation.cycle + 15) / 25) > 0 && lastPistonDirection) {
piston3.isClosing = false;
piston4.isClosing = true;
piston5.isClosing = false;
piston6.isClosing = true;
piston7.isClosing = false;
}
if (Math.sin(-finalGearRot) - Math.sin(-lastFinalGearRot) < -0.01) {
dealtPiston1Damage = false;
dealtPiston1MobDamage = false;
}
if (Math.sin(-finalGearRot) - Math.sin(-lastFinalGearRot) > 0.01) {
dealtPiston2Damage = false;
dealtPiston2MobDamage = false;
}
piston3.openClose();
piston4.openClose();
piston5.openClose();
piston6.openClose();
piston7.openClose();
if (!pistonsLocked) {
piston1.isLocked = false;
}
if (!pistonsLocked || ((Math.sin(-finalGearRot) - Math.sin(-lastFinalGearRot) != 0 || piston1.position.y < -1850) && !piston1.isLocked)) {
piston1.setPos(1650, -1850 + Math.sin(-finalGearRot) * 175);
} else {
piston1.isLocked = true;
}
piston2.setPos(1950, -1850 + Math.sin(finalGearRot) * 175);
ctx.beginPath();
ctx.moveTo(Math.cos(-finalGearRot) * 294 + 1650, Math.sin(-finalGearRot) * 294 - 2550);
ctx.lineTo(1650, -2230);
ctx.lineTo(piston1.position.x, piston1.position.y - 175)
ctx.strokeStyle = "#777";
ctx.lineWidth = 10;
ctx.stroke();
var circle1;
var circle2;
if (Math.cos(finalGearRot) * 294 > 0) {
circle1 = {
x: Math.cos(finalGearRot) * 294 + 2300,
y: Math.sin(finalGearRot) * 294 - 2550,
radius: -25
}
circle2 = {
x: 2300,
y: -2215,
radius: -15
}
} else {
circle1 = {
x: Math.cos(finalGearRot) * 294 + 2300,
y: Math.sin(finalGearRot) * 294 - 2550,
radius: 25
}
circle2 = {
x: 2300,
y: -2215,
radius: 15
}
}
// same method used in drawBelt()
var distance = Math.sqrt((circle2.x - circle1.x) ** 2 + (circle2.y - circle1.y) ** 2);
var distanceToIntersection = (-circle1.radius * distance) / (-circle1.radius + circle2.radius);
var slopeAngle = Math.atan((circle2.y - circle1.y) / (circle2.x - circle1.x));
var angleToTangent = Math.acos(-circle1.radius / distanceToIntersection);
const tangentPoint = {
x: Math.cos(angleToTangent + slopeAngle) * -circle2.radius + circle2.x,
y: Math.sin(angleToTangent + slopeAngle) * -circle2.radius + circle2.y
}
// same method used in drawDiagonalBelt()
const circle3 = {
x: 1940,
y: -2250,
radius: 15
}
distance = Math.sqrt((circle2.x - circle3.x) ** 2 + (circle2.y - circle3.y) ** 2);
distanceToIntersection = (circle3.radius * distance) / (circle3.radius + circle2.radius);
slopeAngle = Math.atan((circle2.y - circle3.y) / (circle2.x - circle3.x));
angleToTangent = Math.acos(circle3.radius / distanceToIntersection);
const tangentPoint2 = {
x: Math.cos(angleToTangent + slopeAngle) * circle3.radius + circle3.x,
y: Math.sin(angleToTangent + slopeAngle) * circle3.radius + circle3.y
}
const invertedTangentPoint2 = {
x: Math.cos(-angleToTangent + slopeAngle) * circle3.radius + circle3.x,
y: Math.sin(-angleToTangent + slopeAngle) * circle3.radius + circle3.y
}
const tangentPoint3 = {
x: Math.cos(angleToTangent + slopeAngle) * -circle2.radius + circle2.x,
y: Math.sin(angleToTangent + slopeAngle) * -circle2.radius + circle2.y
}
const invertedTangentPoint3 = {
x: Math.cos(-angleToTangent + slopeAngle) * -circle2.radius + circle2.x,
y: Math.sin(-angleToTangent + slopeAngle) * -circle2.radius + circle2.y
}
distance = Math.sqrt((piston2.position.y - 175 - circle3.y) ** 2 + (piston2.position.x - 50 - circle3.x) ** 2);
slopeAngle = Math.atan((piston2.position.y - 175 - circle3.y) / (piston2.position.x - 50 - circle3.x));
angleToTangent = Math.acos(circle3.radius / distance);
const tangentPoint4 = {
x: Math.cos(angleToTangent) * distance + circle3.x,
y: Math.sin(angleToTangent) * distance + circle3.y
}
// draw
ctx.beginPath();
ctx.moveTo(circle1.x, circle1.y);
ctx.lineTo(tangentPoint.x, tangentPoint.y);
const newAngle = Math.atan((tangentPoint.y - circle2.y) / (tangentPoint.x - circle2.x));
const newAngle2 = Math.atan((tangentPoint3.y - circle2.y) / (tangentPoint3.x - circle2.x));
ctx.arc(circle2.x, circle2.y, Math.abs(circle2.radius), newAngle, -newAngle2);
ctx.lineTo(invertedTangentPoint2.x, invertedTangentPoint2.y);
const newAngle3 = Math.atan((invertedTangentPoint2.y - circle3.y) / (invertedTangentPoint2.x - circle3.x));
ctx.arc(circle3.x, circle3.y, circle3.radius, newAngle3, Math.PI / 2 + angleToTangent, true);
ctx.lineTo(tangentPoint4.x, tangentPoint4.y);
ctx.strokeStyle = '#777';
ctx.lineWidth = 10;
ctx.stroke();
lastPistonDirection = Math.sin((simulation.cycle + 15) / 25) < 0;
if (Matter.Query.ray([player], Matter.Vector.create(piston1.position.x - 50, piston1.position.y + 175), Matter.Vector.create(piston1.position.x + 50, piston1.position.y + 175), 5).length > 0 && !dealtPiston1Damage && Math.sin(-finalGearRot) - Math.sin(-lastFinalGearRot) > 0.01) {
m.damage(0.1);
dealtPiston1Damage = true;
}
var piston1MobCollisions = Matter.Query.ray(mob, Matter.Vector.create(piston1.position.x - 50, piston1.position.y + 175), Matter.Vector.create(piston1.position.x + 50, piston1.position.y + 175), 5);
if (piston1MobCollisions.length > 0 && !dealtPiston1MobDamage && Math.sin(-finalGearRot) - Math.sin(-lastFinalGearRot) > 0.01) {
for (var mobHit of piston1MobCollisions) {
mobHit.body.damage(1);
}
dealtPiston1MobDamage = true;
}
if (Matter.Query.ray([player], Matter.Vector.create(piston2.position.x - 50, piston2.position.y + 175), Matter.Vector.create(piston2.position.x + 50, piston2.position.y + 175), 5).length > 0 && !dealtPiston2Damage && Math.sin(-finalGearRot) - Math.sin(-lastFinalGearRot) < -0.01) {
m.damage(0.1);
dealtPiston2Damage = true;
}
var piston2MobCollisions = Matter.Query.ray(mob, Matter.Vector.create(piston2.position.x - 50, piston2.position.y + 175), Matter.Vector.create(piston2.position.x + 50, piston2.position.y + 175), 5);
if (piston2MobCollisions.length > 0 && !dealtPiston2MobDamage && Math.sin(-finalGearRot) - Math.sin(-lastFinalGearRot) > 0.01) {
for (var mobHit of piston2MobCollisions) {
mobHit.body.damage(1);
}
dealtPiston2MobDamage = true;
}
// clock
ctx.beginPath();
ctx.arc(400, -3700, 600, 0, 2 * Math.PI);
ctx.fillStyle = "#e9e9e9";
ctx.fill();
ctx.strokeStyle = "#3a3f20";
ctx.lineWidth = 2;
ctx.stroke();
ctx.lineCap = "butt";
ctx.beginPath();
ctx.moveTo(350, -4275);
ctx.lineTo(390, -4150);
ctx.strokeStyle = "#000";
ctx.lineWidth = 20;
ctx.stroke();
ctx.beginPath();
ctx.moveTo(390, -4275);
ctx.lineTo(350, -4150);
ctx.stroke();
ctx.beginPath();
ctx.moveTo(420, -4275);
ctx.lineTo(420, -4150);
ctx.stroke();
ctx.beginPath();
ctx.moveTo(450, -4275);
ctx.lineTo(450, -4150);
ctx.stroke();
var numberOffset = {
x: 400 + Math.cos(-Math.PI / 3) * 510,
y: -3700 + Math.sin(-Math.PI / 3) * 510
}
ctx.beginPath();
ctx.moveTo(numberOffset.x, numberOffset.y - 62);
ctx.lineTo(numberOffset.x, numberOffset.y + 63);
ctx.stroke();
var numberOffset = {
x: 400 + Math.cos(-Math.PI / 6) * 510,
y: -3700 + Math.sin(-Math.PI / 6) * 510
}
ctx.beginPath();
ctx.moveTo(numberOffset.x - 20, numberOffset.y - 62);
ctx.lineTo(numberOffset.x - 20, numberOffset.y + 63);
ctx.stroke();
ctx.beginPath();
ctx.moveTo(numberOffset.x + 20, numberOffset.y - 62);
ctx.lineTo(numberOffset.x + 20, numberOffset.y + 63);
ctx.stroke();
ctx.beginPath();
ctx.moveTo(870, -3762);
ctx.lineTo(870, -3637);
ctx.stroke();
ctx.beginPath();
ctx.moveTo(910, -3762);
ctx.lineTo(910, -3637);
ctx.stroke();
ctx.beginPath();
ctx.moveTo(950, -3762);
ctx.lineTo(950, -3637);
ctx.stroke();
var numberOffset = {
x: 400 + Math.cos(Math.PI / 6) * 535,
y: -3700 + Math.sin(Math.PI / 6) * 535
}
ctx.beginPath();
ctx.moveTo(numberOffset.x - 50, numberOffset.y - 62);
ctx.lineTo(numberOffset.x - 50, numberOffset.y + 63);
ctx.stroke();
ctx.beginPath();
ctx.moveTo(numberOffset.x - 20, numberOffset.y - 62);
ctx.lineTo(numberOffset.x, numberOffset.y + 53);
ctx.lineTo(numberOffset.x + 20, numberOffset.y - 62);
ctx.stroke();
var numberOffset = {
x: 400 + Math.cos(Math.PI / 3) * 515,
y: -3700 + Math.sin(Math.PI / 3) * 515
}
ctx.beginPath();
ctx.moveTo(numberOffset.x - 20, numberOffset.y - 62);
ctx.lineTo(numberOffset.x, numberOffset.y + 53);
ctx.lineTo(numberOffset.x + 20, numberOffset.y - 62);
ctx.stroke();
var numberOffset = {
x: 400 + Math.cos(Math.PI / 2) * 515,
y: -3700 + Math.sin(Math.PI / 2) * 515
}
ctx.beginPath();
ctx.moveTo(numberOffset.x - 35, numberOffset.y - 62);
ctx.lineTo(numberOffset.x - 15, numberOffset.y + 53);
ctx.lineTo(numberOffset.x + 5, numberOffset.y - 62);
ctx.stroke();
ctx.beginPath();
ctx.moveTo(numberOffset.x + 35, numberOffset.y - 62);
ctx.lineTo(numberOffset.x + 35, numberOffset.y + 63);
ctx.stroke();
var numberOffset = {
x: 400 + Math.cos(Math.PI * 2 / 3) * 500,
y: -3700 + Math.sin(Math.PI * 2 / 3) * 500
}
ctx.beginPath();
ctx.moveTo(numberOffset.x - 65, numberOffset.y - 62);
ctx.lineTo(numberOffset.x - 45, numberOffset.y + 53);
ctx.lineTo(numberOffset.x - 25, numberOffset.y - 62);
ctx.stroke();
ctx.beginPath();
ctx.moveTo(numberOffset.x + 5, numberOffset.y - 62);
ctx.lineTo(numberOffset.x + 5, numberOffset.y + 63);
ctx.stroke();
ctx.beginPath();
ctx.moveTo(numberOffset.x + 35, numberOffset.y - 62);
ctx.lineTo(numberOffset.x + 35, numberOffset.y + 63);
ctx.stroke();
var numberOffset = {
x: 400 + Math.cos(Math.PI * 5 / 6) * 500,
y: -3700 + Math.sin(Math.PI * 5 / 6) * 500
}
ctx.beginPath();
ctx.moveTo(numberOffset.x - 65, numberOffset.y - 62);
ctx.lineTo(numberOffset.x - 45, numberOffset.y + 53);
ctx.lineTo(numberOffset.x - 25, numberOffset.y - 62);
ctx.stroke();
ctx.beginPath();
ctx.moveTo(numberOffset.x + 5, numberOffset.y - 62);
ctx.lineTo(numberOffset.x + 5, numberOffset.y + 63);
ctx.stroke();
ctx.beginPath();
ctx.moveTo(numberOffset.x + 35, numberOffset.y - 62);
ctx.lineTo(numberOffset.x + 35, numberOffset.y + 63);
ctx.stroke();
ctx.beginPath();
ctx.moveTo(numberOffset.x + 65, numberOffset.y - 62);
ctx.lineTo(numberOffset.x + 65, numberOffset.y + 63);
ctx.stroke();
var numberOffset = {
x: 400 + Math.cos(Math.PI) * 500,
y: -3700 + Math.sin(Math.PI) * 500
}
ctx.beginPath();
ctx.moveTo(numberOffset.x - 5, numberOffset.y - 62);
ctx.lineTo(numberOffset.x + 35, numberOffset.y + 63);
ctx.stroke();
ctx.beginPath();
ctx.moveTo(numberOffset.x - 5, numberOffset.y + 63);
ctx.lineTo(numberOffset.x + 35, numberOffset.y - 62);
ctx.stroke();
ctx.beginPath();
ctx.moveTo(numberOffset.x - 35, numberOffset.y - 62);
ctx.lineTo(numberOffset.x - 35, numberOffset.y + 63);
ctx.stroke();
var numberOffset = {
x: 400 + Math.cos(-Math.PI * 5 / 6) * 500,
y: -3700 + Math.sin(-Math.PI * 5 / 6) * 500
}
ctx.beginPath();
ctx.moveTo(numberOffset.x - 25, numberOffset.y - 62);
ctx.lineTo(numberOffset.x + 25, numberOffset.y + 63);
ctx.stroke();
ctx.beginPath();
ctx.moveTo(numberOffset.x - 25, numberOffset.y + 63);
ctx.lineTo(numberOffset.x + 25, numberOffset.y - 62);
ctx.stroke();
var numberOffset = {
x: 400 + Math.cos(-Math.PI * 2 / 3) * 500,
y: -3700 + Math.sin(-Math.PI * 2 / 3) * 500
}
ctx.beginPath();
ctx.moveTo(numberOffset.x - 10, numberOffset.y - 62);
ctx.lineTo(numberOffset.x + 40, numberOffset.y + 63);
ctx.stroke();
ctx.beginPath();
ctx.moveTo(numberOffset.x - 10, numberOffset.y + 63);
ctx.lineTo(numberOffset.x + 40, numberOffset.y - 62);
ctx.stroke();
ctx.beginPath();
ctx.moveTo(numberOffset.x - 40, numberOffset.y + 63);
ctx.lineTo(numberOffset.x - 40, numberOffset.y - 62);
ctx.stroke();
ctx.lineCap = "round";
level.exit.drawAndCheck();
level.enter.draw();
}
var lastBlock = Math.sin(simulation.cycle / 50) < 0;
level.customTopLayer = () => {
elevator1.move();
ctx.beginPath();
ctx.moveTo(pendulum1.parts[2].vertices[0].x, pendulum1.parts[2].vertices[0].y);
for (var i = 0; i < pendulum1.parts[2].vertices.length; i++) {
ctx.lineTo(pendulum1.parts[2].vertices[i].x, pendulum1.parts[2].vertices[i].y);
}
ctx.lineTo(pendulum1.parts[2].vertices[0].x, pendulum1.parts[2].vertices[0].y);
ctx.fillStyle = "#999";
ctx.fill();
ctx.lineWidth = 2
ctx.strokeStyle = color.blockS;
ctx.stroke();
ctx.beginPath();
ctx.moveTo(gear3.parts[1].vertices[0].x, gear3.parts[1].vertices[0].y);
for (var i = 0; i < gear3.parts[1].vertices.length; i++) {
ctx.lineTo(gear3.parts[1].vertices[i].x, gear3.parts[1].vertices[i].y);
}
ctx.lineTo(gear3.parts[1].vertices[0].x, gear3.parts[1].vertices[0].y);
ctx.fillStyle = "#999";
ctx.fill();
ctx.lineWidth = 2
ctx.strokeStyle = color.blockS;
ctx.stroke();
ctx.fillStyle = "#444";
ctx.fillRect(3275, -1750, 1, 1750);
ctx.fillStyle = "#888";
if (Math.sin(simulation.cycle / 50) < 0 && !lastBlock) {
// remove old elements
for (var i = 0; i < map.length; i++) {
if (map[i].isRemove) {
Matter.Composite.remove(engine.world, map[i]);
map.splice(i, 1);
}
}
// add new element
spawn.mapRect(-200, -600, 275, 50);
addPartToMap(map.length - 1);
map[map.length - 1].isRemove = true;
} else if (Math.sin(simulation.cycle / 50) * 0.3 >= 0 && lastBlock) {
for (var i = 0; i < map.length; i++) {
if (map[i].isRemove) {
Matter.Composite.remove(engine.world, map[i]);
map.splice(i, 1);
}
}
spawn.mapRect(825, -600, 275, 50);
addPartToMap(map.length - 1);
map[map.length - 1].isRemove = true;
}
simulation.draw.setPaths();
lastBlock = Math.sin(simulation.cycle / 50) * 0.3 < 0;
}
},
buttonbutton() {
simulation.makeTextLog(`<strong>buttonbutton</strong> by <span class='color-var'>||Destabilized E||</span>`);
const mover = level.mover(1425, -1949, 600, 25); //x,y,width.height,VxGoal,force
let portal
portal = level.portal({
x: -146,
y: 131
}, 2 * Math.PI, {
x: 1805,
y: -2295
}, 90)
const button = level.button(-456, -1320)
spawn.bodyRect(-400, -1475, 75, 75);
const button2 = level.button(1781, -61)
spawn.bodyRect(1781, (-61) - 100, 75, 75);
const boost1 = level.boost(1366, -1942, 1300)
button.isUp = true
button2.isUp = true
const train = level.transport(-250, 1151, 400, 50, 8 + simulation.difficultyMode)
level.custom = () => {
if (train.position.x < -244) {
train.changeDirection(true) //go right
} else if (train.position.x > 1700) {
train.changeDirection(false) //go left
}
if (button.isUp && button2.isUp) train.move();
mover.push();
ctx.fillStyle = "rgba(0,255,255,0.1)";
ctx.fillRect(6400, -550, 300, 350);
level.exit.drawAndCheck();
level.enter.draw();
};
level.customTopLayer = () => {
button.query();
button.draw();
button2.query();
button2.draw();
boost1.query();
train.draw()
portal[2].query()
portal[3].query()
portal[0].draw();
portal[1].draw();
mover.draw();
ctx.fillStyle = "rgba(0,0,0,0.1)"
ctx.fillRect(-150, -650, 900, 250)
};
level.setPosToSpawn(0, -450); //normal spawn
spawn.mapRect(level.enter.x, level.enter.y + 20, 100, 20);
level.exit.x = -525;
level.exit.y = 1128;
level.defaultZoom = 1500
simulation.zoomTransition(level.defaultZoom)
document.body.style.backgroundColor = "#ddd";
spawn.mapRect(-725, -1325, 575, 1900);
spawn.mapRect(1425, -1925, 600, 1550);
spawn.mapRect(1450, -50, 500, 425);
spawn.mapRect(1950, 75, 325, 300);
spawn.mapRect(2275, 200, 200, 175);
spawn.mapRect(-150, -400, 900, 250);
spawn.mapRect(-150, 300, 900, 275);
spawn.mapRect(1700, 900, 450, 275);
spawn.mapRect(1800, 1600, 450, 250);
spawn.mapRect(1675, 1675, 275, 175);
spawn.mapRect(1575, 1675, 275, 175);
spawn.mapRect(-550, 1150, 150, 100);
spawn.bodyRect(-1475, -225, 50, 50);
spawn.bodyRect(2450, 1525, 925, 850);
spawn.mapRect(2275, 1400, 300, 150);
spawn.mapRect(2125, 1025, 125, 150);
spawn.mapRect(2250, 1175, 175, 75);
spawn.mapRect(2150, 1175, 175, 75);
spawn.mapRect(1725, 1150, 475, 100);
spawn.mapRect(2225, 675, 650, 50);
spawn.bodyRect(2400, 500, 150, 175);
spawn.nodeGroup(326, 85, "grenadier", 6)
spawn.mapRect(-225, -1325, 625, 225);
spawn.randomMob(151, -1500)
spawn.randomMob(-88, -1829)
spawn.randomMob(2339, 896)
spawn.randomMob(1907, 1381)
spawn.randomMob(2398, 1301)
spawn.randomMob(1839, 811)
spawn.randomMob(2282, 1103)
spawn.randomMob(8, 124)
spawn.randomMob(629, 111)
spawn.randomMob(43, 831)
spawn.randomMob(168, 1002)
spawn.randomMob(2956, 1006)
spawn.randomMob(2713, 535)
spawn.randomMob(2396, 117)
spawn.randomMob(1498, -121)
spawn.nodeGroup(2030, -16, "grower", 6)
spawn.randomLevelBoss(1840, 675)
},
movers() {
simulation.makeTextLog(`<strong>movers</strong> by <span class='color-var'>ryanbear</span>`);
level.custom = () => {
level.exit.drawAndCheck();
level.enter.draw();
for (var i = 0; i < trains.length; i++) {
//oscillate back and forth
if (trains[i].position.x < 5075) {
trains[i].changeDirection(true) //go right
} else if (trains[i].position.x > 7875) {
trains[i].changeDirection(false) //go left
}
trains[i].draw();
trains[i].move();
}
for (var j = 0; j < zzz.length; j++) {
zzz[j][0].query();
}
mvr.push();
v3.query();
ctx.fillStyle = "rgba(68,68,68,1)";
ctx.fillRect(1725, -2400, 1000, 150);
ctx.fillRect(2175, -2775, 250, 450);
ctx.fillRect(2200, -2825, 225, 200);
ctx.fillRect(2075, -2575, 150, 200);
ctx.fillRect(2075, -2700, 150, 150);
ctx.fillRect(1875, -2525, 300, 125);
ctx.fillRect(1975, -2575, 150, 75);
ctx.fillRect(1800, -2475, 175, 100);
ctx.fillRect(2150, -2725, 350, 375);
ctx.fillRect(2475, -2575, 175, 200);
ctx.fillRect(2675, -2550, 25, 175);
ctx.fillRect(2625, -2550, 75, 200);
ctx.fillRect(2025, -2600, 200, 175);
ctx.fillRect(2025, -2675, 225, 225);
ctx.fillRect(2125, -2800, 250, 375);
ctx.fillRect(2400, -2625, 175, 175);
ctx.fillRect(2450, -2700, 100, 225);
ctx.fillRect(1950, -2600, 150, 200);
ctx.fillRect(1675, -2325, 250, 75);
ctx.fillRect(2700, -2525, 25, 150);
};
simulation.enableConstructMode()
level.setPosToSpawn(0, -50); //normal spawn
level.exit.x = 23885;
level.exit.y = 800;
spawn.mapRect(-98, -8, 1000, 20); //bump for level entrance
spawn.mapRect(972, -287, 200, 20); //x, y, width, height, maxHeight, force = 0.003, friction = { up: 0.01, down: 0.2 }) {
level.defaultZoom = 1800
simulation.zoomTransition(level.defaultZoom)
var trains = [];
var zzz = [];
spawn.mapRect(9850, 475, 200, 75);
for (var i = 0; i < 6; i++) {
trains.push(level.transport(6275, -2100 + 525 * i, 600, 50, (2 * i % 2 - 1) * 4 * Math.min(simulation.difficulty / 2, 2) * (1 + Math.random())))
zzz.push([level.boost(6275, -2100 + 525 * i, 100), 6275]);
}
document.body.style.backgroundColor = "#d8dadf";
const portal1 = level.portal({
x: 3984,
y: 1293
}, -2 * Math.PI, { //right
x: 23863,
y: 82
}, 2 * Math.PI) //right
spawn.mapRect(1825, -2250, 3300, 300); spawn.mapRect(3250, -2875, 150, 625);
spawn.mapRect(3250, -2875, 425, 125);
spawn.mapRect(3425, -2725, 150, 300);
spawn.mapRect(3400, -2750, 175, 350);
spawn.mapRect(3575, -2625, 150, 375);
spawn.mapRect(3175, -2750, 75, 300);
spawn.mapRect(3100, -2750, 275, 300);
spawn.mapRect(3675, -2875, 75, 125);
spawn.mapRect(3675, -2625, 75, 400);
spawn.mapRect(3350, -2425, 100, 175);
spawn.mapRect(8350, 825, 1825, 250);
spawn.mapRect(3950, 800, 800, 375);
var hzd = level.hazard(3750, -2625, 1375, 375);
// spawn.mapRect(3750, -2625, 1375, 375);
var v1 = level.vanish(3975, -2600, 225, 25);
var v2 = level.vanish(4275, -2975, 225, 25);
var mvr = level.mover(2585, 1928, 2375, 100);
//spawn.mapRect(4925, 1725, 300, 25);
var v3 = level.vanish(4925, 1725, 100, 25);
for (var i = 0; i < 16; i++) {
if (i < 10) {
level.boost(1600 + 62 * i, -2307 - 62 * i, 100);
}
else {
level.boost(1600 + 62 * i, -2307 - 62 * 20 + 62 * i, 100);
}
}
for (var i = -1; i < 10; i++) {
level.boost(3847 - 62 * i, 879 + 62 * i, 100);
}
spawn.mapRect(3050, 1500, 1600, 200);
spawn.mapRect(1850, -1950, 3275, 1275); spawn.mapRect(1850, -675, 3275, 1300);
spawn.mapRect(2700, -2525, 25, 175);
spawn.mapRect(3825, 925, 125, 575); spawn.mapRect(3600, 1100, 350, 400); spawn.mapRect(3375, 1350, 275, 150); spawn.mapRect(3550, 1300, 100, 50); spawn.mapRect(3800, 1000, 100, 150); spawn.mapRect(3725, 1075, 150, 125); spawn.mapRect(3725, 1025, 150, 125); spawn.mapRect(3550, 1225, 150, 125); spawn.mapRect(3500, 1275, 175, 125);
// color.map = "#444" //custom map color
bosses = ["laserBoss", "blinkBoss", "shooterBoss", "launcherBoss", "pulsarBoss", "beetleBoss", "bladeBoss", "revolutionBoss", "dragonFlyBoss", "spiderBoss"];
let randomBoss = Math.floor(Math.random() * bosses.length);
spawn[bosses[randomBoss]](2240, -2499, 100, false);
var btn = level.button(9889, 747);
btn.isUp = true;
spawn.randomMob(475, -725, 0.7); spawn.randomMob(825, -1825, 0.7); spawn.randomMob(3275, -3475, 0.7); spawn.randomMob(8550, 350, 0.7); spawn.randomMob(9350, -175, 0.7); spawn.randomMob(1575, 225, 0.7); spawn.randomMob(22825, 250, 0.7);
spawn.mapRect(-100, 0, 1000, 100);
var ddd = level.elevator(1326, -447, 200, 200, -2131, 0.003, { up: 0.1, down: 0.2 });
/// transport(x, y, width, height, VxGoal = -6, force = VxGoal > 0 ? 0.0005 : -0.0005) {
spawn.mapRect(9500, 750, 675, 75);
spawn.mapRect(22350, 825, 3000, 150);
powerUps.spawn(4246, 1335, "tech")
powerUps.spawn(4246.8, 1335, "heal")
powerUps.spawn(4246.8, 1335.4, "ammo")
spawn.bodyRect(9200, 725, 50, 25); spawn.mapRect(12200, 675, 125, 50); spawn.mapRect(12925, 675, 100, 100); spawn.mapRect(13675, 650, 150, 150); spawn.mapRect(14200, 750, 25, 25); spawn.mapRect(14200, 675, 25, 75); spawn.mapRect(14550, 675, 125, 50); spawn.mapRect(15850, 675, 125, 100); spawn.mapRect(17175, 600, 25, 200); spawn.mapRect(17725, 700, 175, 50); spawn.mapRect(18775, 675, 175, 75);
spawn.bodyRect(8975, 700, 25, 25); spawn.bodyRect(8850, 575, 50, 50); spawn.bodyRect(9050, 650, 50, 50); spawn.bodyRect(8625, 575, 100, 75); spawn.bodyRect(8475, 675, 75, 25);
var train1 = level.transport(10250 - 700, 775, Math.max(1200 / simulation.difficulty, 200), 1350, 8);
level.customTopLayer = () => {
ddd.move();
hzd.query();
v1.query();
v2.query();
btn.draw();
portal1[2].query();
portal1[2].draw();
portal1[3].query();
portal1[3].draw();
btn.query();
if (!btn.isUp) {
spawn.mapRect(4050, 1175, 600, 325);
}
if (!btn.isUp && train1.position.x < 23785) {
train1.draw();
train1.move();
}
// if (trains[i].position.x < 5075) {
// trains[i].changeDirection(true) //go right
};
// powerUps.spawnStartingPowerUps(1475, -1175);
// spawn.debris(750, -2200, 3700, 16); //16 debris per level
// spawn.bodyRect(1540, -1110, 300, 25, 0.9);
// spawn.randomSmallMob(1300, -70);
// spawn.randomMob(2650, -975, 0.8);
// spawn.randomGroup(1700, -900, 0.4);
// if (simulation.difficulty > 1) spawn.randomLevelBoss(2200, -1300);
// spawn.secondaryBossChance(100, -1500)
powerUps.addResearchToLevel() //needs to run after mobs are spawned
},
downpour() {
simulation.makeTextLog(`<strong>Downpour</strong> by <span class='color-var'>DesBoot</span>`);
let mobsspawned = 0
const laser = level.hazard(7492, -2612, 10, 500, 0.3) //laserintro
//5381, -3268, 10, 0.4
spawn.mapRect(340, -2032.5, 20, 25); //laser nose //laserintro
const laserbutton = level.button(5485, -2510)
const doorbutton = level.button(7618, -3204)
const doortoggle = level.toggle(5088.4, 1226.7)
const door = level.door(6500, -1200, 100, 350, 100)
const bunkerdoor = level.door(10700, -2500, 100, 500, 200)
const boost1 = level.boost(7300, 1209, 2200)
const boost2 = level.boost(6232.6, -832.8, 1400)
const portal = level.portal({ x: 4886.4, y: 1050.7 }, 2 * Math.PI, { x: 7686, y: -2121 }, 2 * Math.PI)
//let portal
const slime = level.hazard(-1800, 10, 4200, 400);
const slime2 = level.hazard(2400, -2100, 200, 2100);
const slime3 = level.hazard(2600, -2100, 3600, 200);
const slime4 = level.hazard(6400, -2100, 3600, 200);
simulation.enableConstructMode()
level.setPosToSpawn(0, -50); //normal spawn
level.exit.x = 13130.3;
let rainCount = 1
level.exit.y = -370;
spawn.mapRect(level.enter.x, level.enter.y + 20, 100, 20); //bump for level entrance
spawn.mapRect(level.exit.x, level.exit.y + 20, 100, 20); //bump for level exit
level.defaultZoom = 1800
let stopcycle = 0
let flashcycle = Math.round(Math.random() * 25 + 260)
simulation.zoomTransition(level.defaultZoom)
document.body.style.backgroundColor = "#2e416e";//d8dadf
// color.map = "#444" //custom map color
//simulation.makeTextLog(stopcycle)
level.custom = () => {
do {
ctx.beginPath()
ctx.fillStyle = "rgba(30,150,117,255)"
ctx.rect(Math.random() * 4500 - 2000, -5000, Math.random() * 3 + 2.5, 5000)
ctx.rect(Math.random() * 4500 - 2000, -5000, Math.random() * 3 + 2.5, 5000)
ctx.rect(Math.random() * 4500 - 2000, -5000, Math.random() * 3 + 2.5, 5000)
ctx.rect(Math.random() * 2000 + 2500, -5000, Math.random() * 3 + 2.5, 3000)
ctx.rect(Math.random() * 2000 + 2500, -5000, Math.random() * 3 + 2.5, 3000)
ctx.rect(Math.random() * 1300 + 4500, -5000, Math.random() * 3 + 2.5, 2500)
ctx.rect(Math.random() * 1300 + 7500, -5000, Math.random() * 3 + 2.5, 1800)
ctx.rect(Math.random() * 1800 + 5700, -5000, Math.random() * 3 + 2.5, 3000)
ctx.rect(Math.random() * 1800 + 5700, -5000, Math.random() * 3 + 2.5, 3000)
ctx.rect(Math.random() * 1800 + 8400, -5000, Math.random() * 3 + 2.5, 3000)
ctx.rect(Math.random() * 1800 + 8400, -5000, Math.random() * 3 + 2.5, 3000)
ctx.rect(Math.random() * 4500 - 2000, -5000, Math.random() * 3 + 2.5, 5000)
ctx.rect(Math.random() * 1800 + 10200, -5000, Math.random() * 3 + 2.5, 3000)
ctx.rect(Math.random() * 1800 + 10200, -5000, Math.random() * 3 + 2.5, 3000)
ctx.rect(Math.random() * 1800 + 12000, -5000, Math.random() * 3 + 2.5, 3000)
ctx.rect(Math.random() * 1800 + 12000, -5000, Math.random() * 3 + 2.5, 3000)
ctx.fillStyle = "rgba(30,150,117,255)"
ctx.fill()
// }
// if (rainCount > 12) {
// rainCount = 1
// simulation.makeTextLog(rainCount)
// } else {
// rainCount = rainCount + 1
// simulation.makeTextLog(rainCount)
// }
} while (Math.random() < 0.8);
//simulation.makeTextLog(stopcycle)
//simulation.makeTextLog(m.cycle)
// ctx.fillStyle = "rgba(228,255,0,0.8)"
// //simulation.makeTextLog(stopcycle)
// ctx.fillRect(50.4, -1210.0, 100, 100)
// stopcycle = m.cycle + Math.random * 600;
//stopcycle = m.cycles + Math.random * 600
if (stopcycle > 300) {
stopcycle = 0
flashcycle = Math.round(Math.random() * 25 + 260)
document.body.style.backgroundColor = "#2e416e";
} else {
if (stopcycle > flashcycle) {
document.body.style.backgroundColor = "#7391ff";
for (let i = 0; i < mob.length; i++) mobs.statusStun(mob[i], Math.random() * 20 + 30)
}
stopcycle = stopcycle + 1
}
ctx.fillStyle = "#d4f4f4"
ctx.fillRect(12984, -704, 420, 450)
ctx.fillStyle = "rgba(0,0,0,0.5)"
ctx.fillRect(4703, -2362, 100, 100)
ctx.fillRect(5053, -2362, 100, 100)
ctx.fillRect(5403, -2362, 100, 100)
ctx.fillRect(4703, -2062, 100, 100)
ctx.fillRect(5053, -2062, 100, 100)
ctx.fillRect(5403, -2062, 100, 100)
ctx.fillRect(4523, -2512, 1150, 800)
ctx.fillRect(4735, -1233, 100, 500)//tree
ctx.beginPath()
ctx.moveTo(4487, -1195)//slope of -1/3
ctx.lineTo(4736, -792)
ctx.lineTo(4736, -852)
ctx.lineTo(4527, -1195)
ctx.moveTo(5087, -1195)//slope of -1/3
ctx.lineTo(4836, -792)
ctx.lineTo(4836, -852)
ctx.lineTo(5047, -1195)
ctx.fill()
ctx.moveTo(5252.4, -2483.5)
ctx.lineTo(5141.2, -2507.8)
ctx.lineTo(5209.2, -2625.2)
ctx.lineTo(5290.2, -2626.6)
ctx.lineTo(5361.2, -2697.9)
ctx.lineTo(5410.6, -2717.0)
ctx.lineTo(5680.2, -2648.7)
ctx.lineTo(5687.7, -2471.5)
ctx.fill()
//building 2 spawn.mapRect(8473, -2513, 50, 50);
ctx.fillRect(8673, -2137, 50, 175)
ctx.fillRect(7630, -2540, 100, 100)
ctx.fillRect(7930, -2540, 100, 100)
ctx.fillRect(8230, -2540, 100, 100)
ctx.fillRect(8530, -2765, 100, 100)
ctx.fillRect(7630, -2990, 100, 100)
ctx.fillRect(7930, -2990, 100, 100)
ctx.fillRect(8230, -2990, 100, 100)
ctx.beginPath()
ctx.moveTo(7475, -3213)
ctx.lineTo(8100, -3213)
ctx.lineTo(8191.2, -3334.7)
ctx.lineTo(8318.0, -3388.3)
ctx.lineTo(8348.5, -3496.9)
ctx.lineTo(8480.0, -3512.6)
ctx.lineTo(8670, -3482)
ctx.lineTo(8725, -3213)
ctx.lineTo(8725, -1463)
ctx.lineTo(7475, -1463)
ctx.fill()
//stairs spawn.mapRect(7523, -2313, 800, 75);
ctx.fillRect(8523, -2563, 50, 50)
ctx.fillRect(8473, -2613, 50, 50)
ctx.fillRect(8423, -2663, 50, 50)
ctx.fillRect(8373, -2713, 50, 50)
ctx.fillRect(8323, -2763, 50, 50)
ctx.fillRect(8323, -2813, 50, 50)
ctx.fillRect(8373, -2863, 50, 50)
ctx.fillRect(8423, -2913, 50, 50)
ctx.fillRect(8473, -2963, 50, 50)
ctx.fillRect(8523, -3013, 50, 50)//make block
ctx.fillRect(8473, -3063, 50, 50)//make block
ctx.fillRect(8423, -3113, 50, 50)//make block
ctx.fillRect(8373, -3163, 50, 50)
ctx.fillRect(8323, -3213, 50, 50)
//caves
ctx.fillStyle = "rgba(30,150,117,255)"//fake slime
//87,189,146,255
ctx.fillRect(6100, -1900, 100, 1050)
ctx.fillRect(6400, -1900, 100, 1050)
ctx.fillRect(2600, -850, 4700, 200)
ctx.fillRect(7200, -650, 100, 1900)
ctx.fillRect(2399, -1, 200, 400)
//bunker
ctx.fillStyle = "rgba(0,0,0,0.5)"
ctx.beginPath()
ctx.moveTo(10800, -2400)//slope of -1/3
ctx.lineTo(10800, -340)
ctx.lineTo(12980, -340)
ctx.lineTo(12980, -700)
ctx.lineTo(13465, -700)
ctx.lineTo(13541, -1737)
ctx.lineTo(11864.6, -1967.0)
ctx.lineTo(11003, -2400)
ctx.fill()
ctx.fillRect(6100, -2000, 400, 50)
// -2000 -> 2500
// Math.random() * 5000 -2500
ctx.fillStyle = "rgba(0,0,0,0.6)"
ctx.beginPath()
ctx.moveTo(6100, -1700)
ctx.lineTo(5799.5, -800)
ctx.lineTo(2600, -800)
ctx.lineTo(2600, -1700)
ctx.lineTo(5799.5, -1700)
ctx.moveTo(6500, -1200)
ctx.lineTo(7600, -1200)
ctx.lineTo(8000, 1400)
ctx.lineTo(4600, 1500)
ctx.lineTo(4500.5, 0)
ctx.lineTo(6500, -200)
ctx.lineTo(6500, -1200)
ctx.fill()
portal[2].query()
portal[3].query()
if (laserbutton.isUp) {
laser.isOn = true;
} else {
laser.isOn = false;
}
ctx.fillStyle = "rgba(0,0,0,0.6)"
ctx.fillRect(2113, -791, 500, 75)
ctx.fillRect(1766, -1091, 250, 310)
ctx.fillRect(4473, -2912, 50, 1000)
ctx.fillRect(5673, -2712, 50, 800)
ctx.fillStyle = "rgba(0,0,0,0.2)"
ctx.fillRect(4523, -2512, 350, 75)
ctx.fillRect(5273, -2212, 400, 75)
level.exit.drawAndCheck();
slime.query();
slime2.query();
slime3.query();
slime4.query();
// spawn.mapRect(4873, -2512, 800, 75);
// spawn.mapRect(4473, -2212, 800, 75);
//setTimeout(function(){/*YourCode*/},1000);
//water falling/flowing effect
ctx.fillStyle = `hsla(160, 100%, 26%,${0.5 + 0.07 * Math.random()})`//lower river
ctx.fillRect(-1800 + Math.random() * 100, 10 + 400 * Math.random(), 3900, 5)
ctx.fillRect(-1800, 10 + 400 * Math.random(), 4400, 5)
ctx.fillRect(2400 + 200 * Math.random(), Math.random() * - 100 - 2000, 5, 2000)//first waterfall
ctx.fillRect(6100 + 100 * Math.random(), Math.random() * - 100 - 1900, 5, 1050)//twin waterfalls
ctx.fillRect(6400 + 100 * Math.random(), Math.random() * - 100 - 1900, 5, 1050)
ctx.fillRect(7200 + 100 * Math.random(), -800 - 50 * Math.random(), 5, 2032)
level.enter.draw();
laserbutton.query();
laserbutton.draw();
doortoggle.query();
if (!doortoggle.isOn) {
door.isClosing = true
bunkerdoor.isClosing = true
} else {
door.isClosing = false
bunkerdoor.isClosing = false
if (mobsspawned == 0) {
spawn.randomSmallMob(6128.0, 822.6);
spawn.randomSmallMob(6854.8, 560.2);
spawn.randomSmallMob(8320.7, -3402.4);
spawn.randomMob(6629.0, 711.3, 0.8);
spawn.randomMob(8199.2, -2545.5, 0.8);
spawn.randomMob(8067.7, -2957.2, 0.8);
spawn.randomMob(5149.6, -1444.1, 0.8);
mobsspawned = 1
}
}
door.openClose();
bunkerdoor.openClose();
};
level.customTopLayer = () => {
door.draw();
bunkerdoor.draw();
laser.opticalQuery();
if (player.position.y > -70 && player.position.x < 2785) {
if (m.onGround) {
Matter.Body.setVelocity(player, {
x: player.velocity.x - (2 + m.pos.y / 150),
y: player.velocity.y
});
} else {
Matter.Body.setVelocity(player, {
x: player.velocity.x - (1 + m.pos.y / 150),
y: player.velocity.y
});
}
}
if (player.position.x > 2400 && player.position.x < 2600) {
Matter.Body.setVelocity(player, {
x: player.velocity.x,
y: player.velocity.y + 4
});
}
boost1.query();
boost2.query();
if (player.position.x > 2600 && player.position.x < 4500 && player.position.y < -1900 && player.position.y > -2121.3) {
Matter.Body.setVelocity(player, {
x: player.velocity.x - 2,
y: player.velocity.y
});
}
if (player.position.x > 4500 && player.position.x < 6000 && player.position.y < -1900 && player.position.y > -2121.3) {
if (input.left) {
Matter.Body.setVelocity(player, {
x: player.velocity.x + 0.1,
y: player.velocity.y
});
} else {
Matter.Body.setVelocity(player, {
x: player.velocity.x + 0.5,
y: player.velocity.y
});
}
}
if (player.position.x > 6500 && player.position.x < 7500 && player.position.y < -1900 && player.position.y > -2121.3) {
Matter.Body.setVelocity(player, {
x: player.velocity.x - 1,
y: player.velocity.y
});
}
if (player.position.x > 7500 && player.position.x < 10000 && player.position.y < -1900 && player.position.y > -2121.3) {
Matter.Body.setVelocity(player, {
x: player.velocity.x - 1,
y: player.velocity.y
});
}
if (player.position.x > 2600 && player.position.x < 6100 && player.position.y < -650 && player.position.y > -920) {
if (input.right) {
Matter.Body.setVelocity(player, {
x: player.velocity.x - 0.2,
y: player.velocity.y
});
} else {
Matter.Body.setVelocity(player, {
x: player.velocity.x - 0.4,
y: player.velocity.y
});
}
}
if (player.position.x > 6500 && player.position.x < 7300 && player.position.y < -650 && player.position.y > -920 && m.onGround) {
if (input.left) {
Matter.Body.setVelocity(player, {
x: player.velocity.x + 0.2,
y: player.velocity.y
});
} else {
Matter.Body.setVelocity(player, {
x: player.velocity.x + 0.4,
y: player.velocity.y
});
}
}
if (player.position.x > 7200 && player.position.x < 7350 && player.position.y > -950 && player.position.y < 1250) {
Matter.Body.setVelocity(player, {
x: player.velocity.x,
y: player.velocity.y + 0.8
});
}
if (player.position.x > 6100 && player.position.x < 6200 && player.position.y < -800 && player.position.y > -2000) {
Matter.Body.setVelocity(player, {
x: player.velocity.x,
y: player.velocity.y + 0.3
});
}
if (player.position.x > 6400 && player.position.x < 6500 && player.position.y < -800 && player.position.y > -2000) {
Matter.Body.setVelocity(player, {
x: player.velocity.x,
y: player.velocity.y + 0.3
});
}
// ctx.fillRect(7200, -650, 100, 1900)
portal[0].draw();
portal[1].draw();
portal[2].draw();
portal[3].draw();
};
spawn.mapRect(-100, 0, 1000, 100);
spawn.mapRect(-1800, 400, 4400, 1300);
spawn.mapRect(-1800, 0, 100, 400);
spawn.mapRect(2600, -2000, 3500, 300);
spawn.mapRect(2600, -2000, 500, 800);
spawn.mapRect(2955, -1779, 800, 300);
spawn.mapRect(2600, -800, 2300, 2500);
spawn.mapRect(-460, 100, 1570, 400);
spawn.mapVertex(965, 67, "0 -100 220 0 0 0");
spawn.mapVertex(-185, 67, "0 -100 -420 0 0 0");
spawn.mapVertex(1210, 365, "0 -400 300 0 0 0");
spawn.mapRect(217.5, -358.5, 50, 360);
spawn.mapRect(-83, -358.5, 300, 50);
//blocks in river/waterfall
spawn.mapRect(1275, 0, 450, 75);
spawn.mapRect(2027, -388, 600, 75);
spawn.mapRect(1666, -791, 450, 75);
spawn.mapRect(1666, -1091, 450, 75);
//buildings
spawn.mapRect(4873, -2512, 800, 75);
spawn.mapRect(4473, -2212, 800, 75);
spawn.mapRect(4473, -2912, 50, 800);
spawn.mapRect(5673, -2712, 50, 575);
spawn.mapRect(6671.5, -2401.4, 500, 50);
spawn.mapRect(6105.1, -2354.1, 400, 50);
spawn.mapRect(4473, -2952, 8, 75);//1,3,2
spawn.mapRect(4493, -3032, 15, 150);
spawn.mapRect(4513, -2982, 7, 75);
spawn.mapRect(5673, -2742, 12, 50);
spawn.mapRect(5703, -2772, 8, 100);
//building 2
// ctx.fillRect(8323, -2363, 50, 50)
spawn.mapRect(7473, -3412, 50, 800);
spawn.mapRect(7473, -2312, 50, 500);
spawn.mapRect(8673, -3212, 50, 1075);
spawn.mapRect(7523, -2313, 800, 75);
spawn.mapRect(7523, -2763, 800, 75);
spawn.mapRect(7523, -3213, 800, 75);
spawn.mapRect(8725, -2340, 400, 50);
spawn.mapRect(8925, -2640, 200, 50);
spawn.mapRect(8725, -2940, 200, 50);
//stairs
spawn.mapRect(8323, -2363, 50, 50);
spawn.mapRect(8373, -2413, 50, 50);
spawn.mapRect(8423, -2463, 50, 50);
spawn.mapRect(8473, -2513, 250, 50);
//stairs 2
spawn.mapRect(8523, -3013, 50, 50)//make block
spawn.mapRect(8473, -3063, 50, 50)//make block
spawn.mapRect(8423, -3113, 50, 50)//make block
//trees in tunnel
spawn.mapRect(4485, -1243, 600, 50)
spawn.mapRect(3967, -1056, 400, 50)
spawn.mapRect(5453, -1150, 50, 300)
spawn.mapRect(5453, -1700, 50, 300)
//tunnels and boss
spawn.mapRect(6500, -2000, 3100, 800);
spawn.mapRect(7500, -2000, 3300, 3700);
spawn.mapRect(4900, -800, 2300, 1000);
spawn.mapRect(4354, 1230, 4000, 470);
spawn.mapRect(5388, 863, 100, 500);
spawn.mapRect(5388, 63, 100, 500);
spawn.mapRect(5834, 549, 500, 80);
spawn.mapRect(6756, 897, 400, 80);
//extra boss
spawn.mapRect(9196, -11492, 500, 100);
spawn.mapRect(9196, -11492, 500, 100);
//bunker
spawn.mapRect(11500, -2000, 1900, 500);
spawn.mapRect(10800, -900, 800, 2600);
spawn.mapRect(11600, -340, 1800, 2600);
spawn.mapRect(13400, -2000, 1800, 3600);
spawn.mapRect(10800, -2500, 200, 100);
spawn.mapVertex(11400, -2235, "0 10 900 510 800 510 750 510 0 110");
spawn.mapVertex(10100, -2000, "0 0 0 -250 400 0");
spawn.mapRect(12945.0, -741.9, 600, 50);
spawn.mapRect(12945.0, -741.9, 50, 250);
//stairs
spawn.mapRect(11600, -850, 50, 550);
spawn.mapRect(11650, -800, 50, 500);
spawn.mapRect(11700, -750, 50, 450);
spawn.mapRect(11750, -700, 50, 400);
spawn.mapRect(11800, -650, 50, 350);
spawn.mapRect(11850, -600, 50, 300);
spawn.mapRect(11900, -550, 50, 250);
spawn.mapRect(11950, -500, 50, 200);
spawn.mapRect(12000, -450, 50, 150);
spawn.mapRect(12050, -400, 50, 100);
spawn.mapRect(12100, -350, 50, 50);
//mobs
//spawn.tetherBoss(6480, 992, { x: 6480, y: 210 })
if (Math.random() < 0.5) {
spawn.tetherBoss(6480, 992, { x: 6480, y: 210 })
} else {
spawn.randomLevelBoss(5977, 992)
}
//mobs for waterfall and first cavern
//spawn.randomSmallMob(1999.2, -487.4);
spawn.randomMob(1999.2, -487.4, 0.8);
//spawn.randomSmallMob(2080.0, -1206.4);
spawn.randomMob(2080.0, -1206.4, 0.8);
spawn.randomSmallMob(3287.5, -1021.1);
//spawn.randomSmallMob(3992.2, -1223.9);
spawn.randomSmallMob(5018.1, -1483.5);
spawn.randomGroup(6776.2, -3054.5, 0.4);
spawn.randomGroup(4217.4, -1403.6, 0.4);
//surface area mobs
spawn.randomSmallMob(5089.0, -2284.1);
spawn.randomSmallMob(6988.3, -2580.2);
spawn.randomSmallMob(7975.0, -2920.3);
spawn.randomMob(5132.0, -2646.2, 0.8);
spawn.randomMob(6365.2, -2459.2, 0.8);
spawn.randomMob(8129.0, -2406.7, 0.8);
spawn.randomMob(8129.0, -2406.7, 0.8);
spawn.randomGroup(2225.3, -1543.2, 0.4);
spawn.debris(4426.9, -1433.8, 700, 1); //16 debris per level
spawn.debris(4651.2, -2597.3, 700, 1); //16 debris per level
spawn.debris(9920.9, -2378.3, 700, 2); //16 debris per level
spawn.debris(8298.5, -2883.8, 700, 1); //16 debris per level
spawn.debris(6779.2, -2662.9, 700, 1); //16 debris per level
spawn.debris(6371.5, 442.3, 700, 2); //16 debris per level
spawn.debris(1873.5, -1297.5, 700, 1); //16 debris per level
spawn.bodyRect(6457.9, -2541.5, 300, 25, 0.9);
//spawn.bodyRect(5685, -2140, 25, 140, 0.9);
spawn.bodyRect(4473, -2110, 50, 110, 0.9);
//spawn.bodyRect(5292.1, -2617.2, 50, 50, 0.9);
spawn.bodyRect(6370.1, -2408.4, 50, 50, 0.9);
//spawn.bodyRect(5467, -1400, 25, 250, 0.9);
spawn.bodyRect(4509.0, -1425.7, 30 + 45 * Math.random(), 30 + 45 * Math.random(), 0.9);
//spawn.bodyRect(8082.9, -2488.1, 30 + 45 * Math.random(), 30 + 45 * Math.random(), 0.9);
spawn.bodyRect(7859.6, -2883.6, 30 + 45 * Math.random(), 30 + 45 * Math.random(), 0.9);
//spawn.bodyRect(5609.5, 948.5, 30 + 45 * Math.random(), 30 + 45 * Math.random(), 0.9);
spawn.bodyRect(5803.7, 1125.5, 30 + 45 * Math.random(), 30 + 45 * Math.random(), 0.9);
//spawn.bodyRect(5492.1, 1061.7, 90, 169, 0.9);
spawn.bodyRect(5582.1, 1061.7, 110, 70, 0.9);
//spawn.bodyRect(5582.1, 961.7, 50, 30, 0.9);
// spawn.randomSmallMob(1300, -70);
// spawn.randomSmallMob(1300, -70);
// spawn.randomSmallMob(1300, -70);
// spawn.randomSmallMob(1300, -70);
// powerUps.spawnStartingPowerUps(1475, -1175);
// spawn.debris(750, -2200, 3700, 16); //16 debris per level
// spawn.bodyRect(1540, -1110, 300, 25, 0.9);
// spawn.randomSmallMob(1300, -70);
// spawn.randomMob(2650, -975, 0.8);
// spawn.randomGroup(1700, -900, 0.4);
// if (simulation.difficulty > 1) spawn.randomLevelBoss(2200, -1300);
// spawn.secondaryBossChance(100, -1500)
powerUps.addResearchToLevel() //needs to run after mobs are spawned
},
dungeon() {
let destroyed = false;
const door = level.door(2650, -825, 50, 250, 250, 10);
const elevator = level.elevator(-11050, -650, 450, 75, -2975, 0.003, { up: 0.1, down: 0.1 })
const slimePit = level.hazard(-4775, -350, 1975, 175);
const boost = level.boost(137.5, -600, 75, 25);
let base = Matter.Bodies.rectangle(-4375, -1000, 100, 100, {
density: 0.05,
isNotHoldable: true,
restitution: 1.05,
isStatic: false
}, true, [true], 0);
let left = Matter.Bodies.rectangle(-4375 + 50, -1000 - 50, 50, 50, {//not actually left/right
density: 0.05,
isNotHoldable: true,
isStatic: false
});
let right = Matter.Bodies.rectangle(-4375 - 50, -1000 - 50, 50, 50, {
density: 0.05,
isNotHoldable: true,
isStatic: false
});
let left2 = Matter.Bodies.rectangle(-4375 - 50, -1000 + 50, 50, 50, {
density: 0.05,
isNotHoldable: true,
isStatic: false
});
let right2 = Matter.Bodies.rectangle(-4375 + 50, -1000 + 50, 50, 50, {
density: 0.05,
isNotHoldable: true,
isStatic: false
});
dong = Matter.Body.create({
parts: [base, left, right, left2, right2]
});
body[body.length] = base;
body[body.length] = left;
body[body.length] = right;
body[body.length] = left2;
body[body.length] = right2;
Matter.Composite.add(engine.world, dong)
Matter.Composite.add(engine.world, Constraint.create({
pointA: { x: -3825, y: -975 },
bodyB: dong,
stiffness: 0.2,
damping: 0.1
}));
composite[composite.length] = dong;
setTimeout(function () {
dong.collisionFilter.category = cat.body;
dong.collisionFilter.mask = cat.body | cat.player | cat.bullet | cat.mobBullet | cat.mob //| cat.map
}, 1000);
level.custom = () => {
ctx.save()
ctx.beginPath()
ctx.fillStyle = "#80808077";
ctx.strokeStyle = "#80808022";
ctx.fillRect(225, -1025, 2400, 450);
ctx.fillRect(-2950, -1025, 3100, 450);
ctx.fillRect(-7050, -1025, 2400, 450);
ctx.fillRect(-10575, -3975, 4525, 1025);
ctx.fillRect(-4650, -1700, 1700, 1100);
ctx.fillRect(-11150, -3575, 575, 3050);
ctx.fillRect(-11900, -1000, 750, 475);
ctx.fill()
ctx.stroke()
ctx.restore()
ctx.save()
ctx.beginPath()
ctx.fillStyle = "#d8dadf";
ctx.strokeStyle = "#d8dadf";
ctx.moveTo(-2950, -600);
ctx.lineTo(-3730, -1725);
ctx.lineTo(-3730, -600);
ctx.moveTo(-4650, -600);
ctx.lineTo(-3925, -1725);
ctx.lineTo(-3925, -575);
ctx.moveTo(-10575, -3425); //NE section
ctx.lineTo(-10100, -2975);
ctx.lineTo(-10575, -2975);
// ctx.moveTo(-7625, -3800);
// ctx.lineTo(-6750, -2975);
// ctx.lineTo(-7625, -2975);
ctx.moveTo(-7975, -2975);
ctx.lineTo(-7625, -3800);
ctx.lineTo(-7350, -2950);
ctx.moveTo(-6750, -2975);
ctx.lineTo(-7075, -3800);
ctx.lineTo(-7350, -2950);
// ctx.moveTo(-7975, -2975);
// ctx.lineTo(-7075, -3800);
// ctx.lineTo(-7075, -2975);
ctx.moveTo(-11900, -950);
ctx.lineTo(-11900, -550);
ctx.lineTo(-11500, -550);
ctx.fillRect(-3925, -1675, 200, 1075);
ctx.fillRect(-7625, -3800, 550, 875);
ctx.clearRect(-10600, -4000, 525, 475);
ctx.clearRect(-10100, -4000, 500, 300);
ctx.clearRect(-9625, -4000, 500, 175);
ctx.fillRect(-11125, -3600, 550, 50);
ctx.fillRect(-10600, -3400, 50, 425);
ctx.fillRect(-11925, -925, 45, 375);
ctx.fillRect(-3950, -1675, 75, 1100);
ctx.fillRect(-3925, -625, 950, 50);
ctx.fillRect(-4650, -600, 1700, 375);
ctx.fillRect(-14550, -2400, 2650, 2050);
//ctx.clearRect(-11050, -3000, 475, 50);
ctx.moveTo(-11150, -3575);
ctx.lineTo(-10575, -2150);
ctx.lineTo(-10575, -3575);
ctx.stroke()
ctx.fill()
ctx.restore()
boost.query()
slimePit.query()
if (Matter.Query.collides(dong, [player]).length > 0 && !(m.isCloak && tech.isIntangible) && m.immuneCycle < m.cycle) {
m.immuneCycle = m.cycle + m.collisionImmuneCycles; //player is immune to damage for 30 cycles
const dmg = 0.05 * Math.min(simulation.dmgScale, simulation.difficulty);
m.damage(dmg);
simulation.drawList.push({ //add dmg to draw queue
x: dong.position.x,
y: dong.position.y,
radius: Math.sqrt(dmg) * 200,
color: simulation.mobDmgColor,
time: simulation.drawTime
});
}
for (let i = 0; i < mob.length; i++) {
if (Matter.Query.collides(dong, [mob[i]]).length > 0) {
const dmg = 1;
mob[i].damage(dmg, true);
simulation.drawList.push({ //add dmg to draw queue
x: dong.position.x,
y: dong.position.y,
radius: Math.sqrt(dmg) * 50,
color: simulation.mobDmgColor,
time: simulation.drawTime
});
break
}
}
level.exit.drawAndCheck();
ctx.beginPath()
ctx.fillStyle = '#68686822';
ctx.fillRect(-25, -2175, 100, 200);
ctx.fill()
ctx.setLineDash([125 * Math.random(), 125 * Math.random()]);
ctx.moveTo(-3825, -975)
ctx.lineTo(dong.position.x, dong.position.y)
ctx.stroke();
ctx.setLineDash([]);
simulation.drawList.push({ //add dmg to draw queue
x: dong.position.x,
y: dong.position.y,
radius: 10,
color: color.block,
time: 20
});
ctx.beginPath()
ctx.fillStyle = `rgba(68,68,68, ${3 * Math.sin(simulation.cycle * 0.015)})`
ctx.fillRect(-3000, -2175, 175, 25);
ctx.fillRect(-2850, -2300, 25, 150);
ctx.fillRect(-3000, -2300, 175, 25);
ctx.fillRect(-3000, -2425, 25, 150);
ctx.fillRect(-3000, -2425, 175, 25);
ctx.fill()
ctx.fillStyle = `rgba(68,68,68, ${5 * Math.sin(simulation.cycle * 0.015)})`
ctx.fillRect(-2725, -2425, 25, 275);
ctx.fillRect(-2725, -2425, 175, 25);
ctx.fillRect(-2575, -2425, 25, 275);
ctx.fillRect(-2725, -2300, 175, 25);
ctx.fill()
ctx.fillStyle = `rgba(68,68,68, ${7 * Math.sin(simulation.cycle * 0.015)})`
ctx.fillRect(-2450, -2425, 25, 275);
ctx.fillRect(-2450, -2175, 175, 25);
ctx.fill()
ctx.stroke();
ctx.fillStyle = `#00FFFF22`;
ctx.fillRect(-7650, -2975 - 100, 600, 2375 + 100)
ctx.fill()
ctx.fillStyle = `#00FFFF66`
ctx.fillRect(-7650 + Math.floor(Math.random() * 600), -2975 - 100, 5, 2375 + 100)
ctx.fillRect(-7650 + Math.floor(Math.random() * 600), -2975 - 100, 5, 2375 + 100)
ctx.fillStyle = `rgba(68, 68, 68)`
ctx.fillRect(-7675, -3075, 50, 125);
ctx.fillRect(-7075, -3075, 50, 125);
ctx.fillRect(-7725, -3025, 75, 75);
ctx.fillRect(-7050, -3025, 75, 75);
ctx.fill()
for (let i = 0, len = body.length; i < len; ++i) { //push blocks away vertically
if (body[i].position.x > -7625 && body[i].position.x < -7075 && body[i].position.y > -2975 - 100 && body[i].position.y < -625) {
body[i].force.y -= simulation.g * body[i].mass + 0.012;
}
}
for (let i = 0, len = bullet.length; i < len; ++i) { //push bullets away vertically
if (bullet[i].position.x > -7625 && bullet[i].position.x < -7075 && bullet[i].position.y > -2975 - 100 && bullet[i].position.y < -625) {
bullet[i].force.y -= simulation.g * bullet[i].mass;
}
}
for (let i = 0, len = powerUp.length; i < len; ++i) { //push powerups away vertically
if (powerUp[i].position.x > -7625 && powerUp[i].position.x < -7075 && powerUp[i].position.y > -2975 - 100 && powerUp[i].position.y < -625) {
powerUp[i].force.y -= simulation.g * powerUp[i].mass + 0.12;
}
}
for (let i = 0, len = mob.length; i < len; ++i) { //push mobs away vertically
if (mob[i].position.x > -7625 && mob[i].position.x < -7075 && mob[i].position.y > -2975 - 100 && mob[i].position.y < -625) {
mob[i].force.y -= simulation.g * mob[i].mass + 0.0012;
}
}
if (m.pos.x > -7625 && m.pos.x < -7075 && m.pos.y > -2975 - 100 && m.pos.y < -625) {
player.force.y -= m.mass * simulation.g + (input.down ? 0 : 0.012 * 2);
}
elevator.move()
};
level.setPosToSpawn(30, -2000); //normal spawn
level.exit.x = 2775;
level.exit.y = -650;
level.defaultZoom = 1800
simulation.zoomTransition(level.defaultZoom + 800)
document.body.style.backgroundColor = "#d8dadf";
spawn.mapRect(-225, -1950, 350, 75);
spawn.mapRect(225, -1950, 50, 75);
spawn.mapRect(-250, -2025, 50, 150);
spawn.mapRect(250, -2025, 50, 150);
spawn.mapRect(-250, -2250, 50, 125);
spawn.mapRect(-225, -2325, 500, 100);
spawn.mapRect(250, -2250, 50, 125);
spawn.mapRect(-100, -2400, 250, 100);
spawn.mapRect(-25, -2475, 100, 100);
spawn.mapRect(125, -2350, 50, 50);
spawn.mapRect(-125, -2350, 50, 50);
spawn.mapRect(-50, -2425, 50, 50);
spawn.mapRect(50, -2425, 50, 50);
spawn.mapRect(-250, -2350, 50, 50);
spawn.mapRect(250, -2350, 50, 50);
spawn.mapRect(-75, -1975, 200, 50);
spawn.mapRect(-50, -2000, 150, 50);
spawn.mapRect(100, -1950, 50, 75);
spawn.mapRect(-75, -2250, 200, 50);
spawn.mapRect(-50, -2225, 150, 50);
spawn.mapRect(-2950, -1900, 3100, 900);
spawn.mapRect(225, -1900, 2875, 900);
spawn.mapRect(-2950, -600, 6050, 450);
spawn.mapRect(-3050, -500, 200, 350);
spawn.mapRect(-3150, -400, 200, 250);
spawn.mapRect(-3250, -300, 200, 150);
spawn.mapRect(2950, -1050, 150, 500);
spawn.mapRect(-4675, -1900, 1825, 200);
spawn.mapRect(-5325, -1900, 675, 900);
spawn.mapRect(-5325, -250, 2100, 100);
spawn.mapRect(-5325, -600, 675, 450); // -
spawn.mapRect(-4700, -500, 150, 350);
spawn.mapRect(-4650, -400, 200, 250);
spawn.mapRect(-4550, -300, 200, 150);
spawn.mapRect(-3875, -1025, 100, 100);
spawn.mapRect(-3800, -1050, 50, 50);
spawn.mapRect(-3900, -1050, 50, 50);
spawn.mapRect(-3800, -950, 50, 50);
spawn.mapRect(-3900, -950, 50, 50);
spawn.mapRect(-6925, -1175, 1700, 175);
spawn.mapRect(-6925, -600, 1725, 175);
spawn.mapRect(-7700, -600, 800, 425);// -
spawn.mapRect(-7800, -2950, 175, 2775);
spawn.mapRect(-7075, -2950, 175, 1950);
spawn.mapRect(-9150, -2975, 1525, 175);
spawn.mapRect(-7075, -2975, 1150, 175);
spawn.mapRect(-6100, -3900, 175, 1100);
spawn.mapRect(-9150, -3975, 3225, 175);
spawn.mapRect(-9175, -3850, 75, 75);
spawn.mapRect(-9625, -3825, 500, 150);
spawn.mapRect(-9650, -3725, 75, 75);
spawn.mapRect(-10100, -3700, 500, 150);
spawn.mapRect(-10100, -2975, 975, 175);
spawn.mapRect(-10125, -3600, 75, 75);
spawn.mapRect(-10575, -3575, 500, 150);
spawn.mapRect(-10575, -2975, 500, 175);
spawn.mapRect(-11325, -2975, 250, 175);
spawn.mapRect(-11325, -3575, 175, 775);
// spawn.mapRect(-11325, -3575, 800, 150);
spawn.mapRect(-11225, -2975, 150, 2000);
spawn.mapRect(-10575, -2975, 150, 2500);
spawn.mapRect(-11650, -550, 1225, 150);
spawn.mapRect(-11650, -1100, 575, 150);
spawn.mapRect(-14675, -2525, 2925, 150);
spawn.mapRect(-11900, -2525, 150, 1575);
spawn.mapRect(-11850, -1100, 250, 150);
spawn.mapRect(-11875, -550, 275, 150);
spawn.mapRect(-11900, -550, 150, 350);
spawn.mapRect(-14675, -2525, 150, 2300);
spawn.mapRect(-14675, -375, 2925, 175);
spawn.mapRect(2725, -625, 250, 50);
spawn.mapRect(2625, -1025, 100, 225);
spawn.mapRect(2700, -1025, 300, 125);
spawn.mapRect(2625, -612.5, 125, 50);
spawn.mapRect(-3950, -1725, 250, 50);
spawn.mapRect(-7650, -3825, 600, 50);
spawn.mapRect(-13900, -2400, 200, 50);
spawn.mapVertex(-11957, -430, '-175 175 0 175 0 0');
spawn.mapVertex(-14470, -430, '175 175 0 175 0 0');
spawn.mapVertex(-11957, -2319, '-175 -175 0 -175 0 0');
spawn.mapVertex(-14470, -2319, '0 0 0 -175 175 -175');
//spawn.mapRect(-13900, -2150, 1375, 125);
const sword = function () { //the ultimate blade of las destruction
mobs.spawn(player.position.x, player.position.y, 5, 30, "transparent");
let me = mob[mob.length - 1];
Matter.Body.setDensity(me, 1);
me.vertices = Matter.Vertices.rotate(me.vertices, Math.PI, me.position);
me.collisionFilter.category = cat.bullet;
me.collisionFilter.mask = cat.mob | cat.mobBullet;
me.isDropPowerUp = false;
me.isShielded = true;
me.showHealthBar = false;
me.isUnblockable = true;
me.leaveBody = false;
me.isBadTarget = true;
me.stroke = "transparent";
me.isSword = true;
let index = 0;
let radius = 50;
me.do = function () {
this.health = Infinity;//just in case
for (let i = 0; i < mob.length; i++) {
if (Matter.Query.collides(this, [mob[i]]).length > 0 && !mob[i].isSword) {
const dmg = 0.25;//do not nerf
mob[i].damage(dmg, true);
simulation.drawList.push({ //add dmg to draw queue
x: mob[i].position.x,
y: mob[i].position.y,
radius: Math.sqrt(dmg) * 50,
color: simulation.mobDmgColor,
time: simulation.drawTime
});
break
}
}
Matter.Body.setPosition(this, {
x: player.position.x + Math.cos(m.angle) * 100,
y: player.position.y - (input.down ? 0 : 30) + Math.sin(m.angle) * 100
})
Matter.Body.setAngle(this, m.angle + Math.PI * 2);
const setNoseShape = () => {
const mag = radius + radius * 10;
this.vertices[2].x = this.position.x + Math.cos(this.angle) * mag;
this.vertices[2].y = this.position.y + Math.sin(this.angle) * mag;
this.vertices[4].x = this.position.x + Math.cos(this.angle) * mag;
this.vertices[4].y = this.position.y + Math.sin(this.angle) * mag;
this.vertices[0].x = this.position.x + Math.cos(this.angle) * mag;
this.vertices[0].y = this.position.y + Math.sin(this.angle) * mag;
};
const spike = Vector.mult(Vector.normalise(Vector.sub(this.vertices[2], this.position)), radius * 100)
const spike2 = Vector.mult(Vector.normalise(Vector.sub(this.vertices[4], this.position)), radius * 500)
const spike3 = Vector.mult(Vector.normalise(Vector.sub(this.vertices[0], this.position)), radius * 500)
this.vertices[2].x = this.position.x + spike.x / 100
this.vertices[2].y = this.position.y + spike.y / 100
this.vertices[4].x = this.position.x + spike2.x / 75
this.vertices[4].y = this.position.y + spike2.y / 75
this.vertices[0].x = this.position.x + spike3.x / 75
this.vertices[0].y = this.position.y + spike3.y / 75
if (index == 0) {
setNoseShape();
index++;
}
ctx.save()
ctx.beginPath();
const vertices = this.vertices;
ctx.lineWidth = 100;
ctx.moveTo(vertices[0].x, vertices[0].y);
for (let j = 1, len = vertices.length; j < len; ++j) ctx.lineTo(vertices[j].x, vertices[j].y);
ctx.lineTo(vertices[0].x, vertices[0].y);
const gradient = ctx.createRadialGradient(this.position.x, this.position.y, 15, this.position.x, this.position.y, Math.abs(275 * Math.sin(simulation.cycle / 50)) + 15);
// Add three color stops
gradient.addColorStop(0, m.eyeFillColor);
gradient.addColorStop(0.9, "white");
gradient.addColorStop(1, "darkgray");
ctx.fillStyle = gradient;
ctx.strokeStyle = "transparent";
ctx.shadowBlur = 10;
ctx.shadowColor = m.eyeFillColor;
ctx.fill();
ctx.stroke();
ctx.restore()
const Dx = Math.cos(m.angle);
const Dy = Math.sin(m.angle);
let xElec = this.position.x + 10 * Dx;
let yElec = this.position.y + 10 * Dy;
ctx.beginPath();
ctx.moveTo(xElec, yElec);
const step = 40
for (let i = 0; i < 6; i++) {
xElec += step * (Dx + 1.5 * (Math.random() - 0.5))
yElec += step * (Dy + 1.5 * (Math.random() - 0.5))
ctx.lineTo(xElec, yElec);
}
ctx.strokeStyle = m.eyeFillColor;
ctx.lineWidth = 1.5;
ctx.setLineDash([70 + 300 * Math.random(), 55 * Math.random()]);
ctx.stroke(); // Draw it
ctx.setLineDash([]);
if (this.alive && m.energy > 0) {
const gradient = ctx.createRadialGradient(this.position.x, this.position.y, 0, this.position.x, this.position.y, Math.abs(20 * Math.cos(simulation.cycle / 50)));
// Add three color stops
gradient.addColorStop(0, m.eyeFillColor);
gradient.addColorStop(0.9, "white");
gradient.addColorStop(1, "gray");
ctx.save()
ctx.beginPath()
ctx.moveTo(this.position.x, this.position.y)
ctx.arc(this.position.x, this.position.y, 20, 0, 2 * Math.PI)
ctx.fillStyle = gradient;
ctx.strokeStyle = "transparent";
ctx.shadowBlur = 10;
ctx.shadowColor = m.eyeFillColor;
ctx.fill()
ctx.stroke()
ctx.restore()
m.energy -= 0.002;
ctx.save()
ctx.translate(this.vertices[2].x, this.vertices[2].y)
ctx.rotate(m.angle + Math.PI / 2)
ctx.beginPath()
ctx.font = "16px Arial";
ctx.fillStyle = "black";
ctx.strokeStyle = "black";
// ctx.fillText("Θ", 0,0 - 110)
// ctx.fillText("ά", 0,15 - 110)
// ctx.fillText("ν", 0,30 - 110)
// ctx.fillText("α", 0,45 - 110)
// ctx.fillText("τ", 0,60 - 110)
// ctx.fillText("ο", 0,75 - 110)
// ctx.fillText("ς", 0,90 - 110)
ctx.fillText("Ω", 0, 55)
ctx.fill()
ctx.stroke()
ctx.restore()
simulation.drawList.push({
x: this.position.x + Math.floor(Math.random() * 300 - Math.random() * 300),
y: this.position.y + Math.floor(Math.random() * 300 - Math.random() * 300),
radius: 2,
color: m.eyeFillColor,
time: simulation.drawTime
});
} else {
this.death()
powerUps.activated = false
}
}
}
//setTimeout(function() {sword();}, 100);
const wire = function () {
const breakingPoint = -1600;
const spawnx = -13800 + Math.floor(Math.random() * 100 - Math.random() * 100);
mobs.spawn(spawnx, -2375, 0, 2, "transparent");
let me = mob[mob.length - 1];
let boss = mob[0];
me.collisionFilter.category = cat.body;
me.collisionFilter.mask = cat.map;
me.g = 0.0003; //required for gravity
me.restitution = 0;
me.stroke = "transparent"
me.freeOfWires = false;
me.frictionAir = 0.01;
me.isDropPowerUp = false;
me.showHealthBar = false;
me.isBadTarget = true;
me.isUnblockable = true;
const wireX = spawnx;
const wireY = -2375;
//const randomw = Math.floor(Math.random() * 100 - Math.random() * 100);
const width = Math.abs(10 + Math.floor(Math.random() * 10 - Math.random() * 10));
const randomx = Math.floor(30 * Math.random() - 30 * Math.random());
const randomy = Math.floor(10 * Math.random() - 10 * Math.random())
me.do = function () {
if (this.freeOfWires) {
this.gravity();
} else {
if (boss.position.y > breakingPoint) {
this.freeOfWires = true;
this.force.y -= -0.0006;
this.force.x += Math.random() * boss.velocity.x / 10000;
this.fill = "#111";
}
//move mob to mob
Matter.Body.setPosition(this, {
x: boss.position.x + randomx,
y: boss.position.y + randomy
})
}
//draw wire
ctx.beginPath();
ctx.moveTo(wireX, wireY);
ctx.quadraticCurveTo(wireX, -100, this.position.x, this.position.y);
ctx.lineWidth = width;
ctx.lineCap = "butt";
ctx.strokeStyle = "#111";
ctx.stroke();
ctx.lineCap = "round";
};
}
const ball = function (x, y, radius = 11 * tech.bulletSize, sides = 70) {//superball //also, why is it called superballs?
mobs.spawn(x, y, sides, radius, "rgba(0,0,0)");
let me = mob[mob.length - 1];
me.stroke = "transparent";
me.onHit = function () {
simulation.drawList.push({ //add dmg to draw queue
x: this.position.x,
y: this.position.y,
radius: 20,
color: simulation.mobDmgColor,
time: simulation.drawTime
});
};
Matter.Body.setDensity(me, 0.00001); //normal is 0.001
me.timeLeft = 500;
me.friction = 0;
me.restitution = 1;
me.leaveBody = false;
me.isDropPowerUp = false;
//me.inertia = Infinity;
me.isBadTarget = true;
me.isMobBullet = true;
me.showHealthBar = false;
me.collisionFilter.category = cat.mobBullet;
me.collisionFilter.mask = cat.bullet | cat.player | cat.map | cat.body;
let index = 0;
me.do = function () {
this.timeLimit();
this.alwaysSeePlayer()
this.force.y += this.mass * 0.0012;
}
}
const normalBullet = function (x, y, radius = 9, sides = 3) {
//bullets
mobs.spawn(x, y, sides, radius, "rgba(0,0,0)");
let me = mob[mob.length - 1];
me.stroke = "transparent";
me.vertices = Matter.Vertices.rotate(me.vertices, Math.PI, me.position);
me.onHit = function () {
simulation.drawList.push({ //add dmg to draw queue
x: this.position.x,
y: this.position.y,
radius: 20,
color: simulation.mobDmgColor,
time: simulation.drawTime
});
};
Matter.Body.setDensity(me, 0.00004); //normal is 0.001
me.timeLeft = 220;
me.frictionAir = -0.01;
me.restitution = -1;
me.leaveBody = false;
me.isDropPowerUp = false;
//me.inertia = Infinity;
me.isBadTarget = true;
me.isMobBullet = true;
me.showHealthBar = false;
me.collisionFilter.category = cat.mobBullet;
me.collisionFilter.mask = null;
let index = 0;
me.do = function () {
this.timeLimit();
this.alwaysSeePlayer()
const setNoseShape = () => {
const mag = this.radius + this.radius * 10;
this.vertices[1].x = this.position.x + Math.cos(this.angle) * mag;
this.vertices[1].y = this.position.y + Math.sin(this.angle) * mag;
const angle = Math.atan2(player.position.y - this.position.y, player.position.x - this.position.x);
Matter.Body.setAngle(this, angle);
};
const spike = Vector.mult(Vector.normalise(Vector.sub(this.vertices[1], this.position)), radius * 1000)
this.vertices[1].x = this.position.x + spike.x / 100
this.vertices[1].y = this.position.y + spike.y / 100
if (index == 0) {
setNoseShape();
index++;
}
if (Matter.Query.collides(this, map).length > 0 || Matter.Query.collides(this, body).length > 0) {
const slow = 0.69 //im sorry it looks cool though
Matter.Body.setVelocity(this, {
x: this.velocity.x * slow,
y: this.velocity.y * slow
});
simulation.drawList.push({ //add dmg to draw queue
x: this.position.x,
y: this.position.y,
radius: 10,
color: '#000000',
time: simulation.drawTime
});
if (this.velocity.x == 0 && this.velocity.y == 0) {
this.death();
}
this.frictionAir += 0.0001;
Matter.Body.setAngularVelocity(this, 0)
}
if (Matter.Query.collides(this, [player]).length > 0 && !(m.isCloak && tech.isIntangible) && m.immuneCycle < m.cycle) {
m.immuneCycle = m.cycle + m.collisionImmuneCycles; //player is immune to damage for 30 cycles // I wasnt gonna add this but since ya'll would have killed me if I didn't I added this
const dmg = 0.013 * simulation.dmgScale;
m.damage(dmg);
simulation.drawList.push({ //add dmg to draw queue
x: this.position.x,
y: this.position.y,
radius: Math.sqrt(dmg) * 200,
color: '#000000',
time: simulation.drawTime
});
}
};
}
const foamBullet = function (x, y, radius = 9, sides = 70) { //bullets
mobs.spawn(x, y, sides, radius, "rgb(0,0,0)");
let me = mob[mob.length - 1];
me.stroke = "transparent";
Matter.Body.setDensity(me, 0.00005); //normal is 0.001
me.timeLeft = 120;
// me.g = 0.0005; //required if using this.gravity
me.accelMag = 0.00006;
me.isVerticesChange = true
me.delay = 360 * simulation.CDScale;
me.spikeVertex = 0;
me.spikeLength = 0;
me.isSpikeGrowing = false;
me.spikeGrowth = 0;
me.isSpikeReset = false;
me.frictionAir = 0;
me.restitution = 0;
me.leaveBody = false;
me.isDropPowerUp = false;
me.isBadTarget = true;
me.isMobBullet = true;
me.showHealthBar = false;
me.isUnblockable = true;
me.collisionFilter.category = cat.mobBullet;
me.collisionFilter.mask = cat.body //| cat.bullet;// | cat.player;
me.do = function () {
if (this.distanceToPlayer2() < 40000) {
this.force = Vector.mult(Vector.normalise(Vector.sub(player.position, this.position)), this.mass * 0.004)
const slow = 0.99999999999999999;
Matter.Body.setVelocity(this, {
x: this.velocity.x * slow,
y: this.velocity.y * slow
});
}
// this.gravity();
this.timeLimit();
// for (let i = 0, len = this.vertices.length; i < len; i++) {
// const dist = Vector.sub(this.seePlayer.position, this.vertices[i]);
// const distMag = Vector.magnitude(dist);
// const spike = Vector.mult(Vector.normalise(Vector.sub(this.vertices[i], this.position)), radius * distMag)
// this.vertices[i].x = this.position.x + spike.x / 100
// this.vertices[i].y = this.position.y + spike.y / 100
// }
if (this.radius < 50) {
const scale = 1.05;
Matter.Body.scale(this, scale, scale);
this.radius *= scale;
}
if (Matter.Query.collides(this, map).length > 0 || Matter.Query.collides(this, body).length > 0 && this.speed < 10) {
const slow = 0.97
Matter.Body.setVelocity(this, {
x: this.velocity.x * slow,
y: this.velocity.y * slow
});
const SCALE = 0.9
Matter.Body.scale(this, SCALE, SCALE);
this.radius *= SCALE;
if (this.radius < 1) {
this.death()
}
} else {
this.attach();
}
};
me.attach = function () {
if (Matter.Query.collides(this, [player]).length > 0) {
Matter.Body.setPosition(this, player.position)
if (player.speed > 2.5) Matter.Body.setVelocity(player, Vector.mult(player.velocity, 0.94))
Matter.Body.setAngularVelocity(player, player.angularVelocity * 0.9);
m.damage(0.00003); //balanced? not sure
}
}
};
const orbital = function (who, radius, phase, speed, radius2) {//basically orbitBot
mobs.spawn(who.position.x, who.position.y, 8, 12, "rgba(0,0,0, 1)");
let me = mob[mob.length - 1];
me.stroke = "transparent";
Matter.Body.setDensity(me, 0.01); //normal is 0.001
me.leaveBody = false;
me.isDropPowerUp = false;
me.isBadTarget = true;
me.showHealthBar = false;
me.isOrbital = true;
me.isShielded = true
me.collisionFilter.category = cat.mobBullet;
me.collisionFilter.mask = cat.bullet; //cat.player | cat.map | cat.body
me.do = function () {
//if host is gone
if (!who || !who.alive) {
this.death();
return
}
//set orbit
const time = simulation.cycle * speed + phase
const orbit = {
x: Math.cos(time),
y: Math.sin(time)
}
Matter.Body.setPosition(this, Vector.add(Vector.add(who.position, who.velocity), Vector.mult(orbit, radius + radius2)))
//damage player
if (Matter.Query.collides(this, [player]).length > 0 && !(m.isCloak && tech.isIntangible) && m.immuneCycle < m.cycle) {
m.immuneCycle = m.cycle + m.collisionImmuneCycles; //player is immune to damage for 30 cycles
const dmg = 0.013 * simulation.dmgScale
m.damage(dmg);
simulation.drawList.push({ //add dmg to draw queue
x: this.position.x,
y: this.position.y,
radius: Math.sqrt(dmg) * 200,
color: simulation.mobDmgColor,
time: simulation.drawTime
});
//this.death();
}
};
}
const sniper = function (x, y, radius = 30) {
mobs.spawn(x, y, 8, radius, '#00000000');
let me = mob[mob.length - 1];
me.accelMag = 0.0003
me.stroke = 'transparent';
//me.isBoss = true;
me.searchTarget = map[Math.floor(Math.random() * (map.length - 1))].position;
me.frictionStatic = 0;
me.friction = 0;
me.seeAtDistance2 = 20000000 //14000 vision range
me.vertices = Matter.Vertices.rotate(me.vertices, Math.PI, me.position);
Matter.Body.rotate(me, Math.random() * Math.PI * 2);
me.showHealthBar = false
Matter.Body.setDensity(me, 0.01)
me.fireDir = { x: 0, y: 0 }
me.seePlayerFreq = 0
me.repulsionRange = 400000 + radius * radius;
me.collisionFilter.mask = cat.bullet | cat.player | cat.body | cat.map | cat.mob | cat.mobBullet
me.do = function () {
this.seePlayerCheck();
this.attraction();
this.repulsion();
this.search()
if (this.seePlayer.recall) {
const h = this.radius * 0.3;
const w = this.radius * 2;
const x = this.position.x - w / 2;
const y = this.position.y - w * 0.7;
ctx.fillStyle = "rgba(100, 100, 100, 0.3)";
ctx.fillRect(x, y, w, h);
ctx.fillStyle = "rgba(0,255,255,0.7)";
ctx.fillRect(x, y, w * this.health, h);
}
if (this.health < 1) {
this.health += 0.0005; //regen
}
ctx.save()
ctx.translate(this.position.x, this.position.y)
ctx.rotate(Math.atan2(this.seePlayer.position.y - this.position.y, this.seePlayer.position.x - this.position.x))
if (this.seePlayer.recall > 0 && this.distanceToPlayer2() > this.repulsionRange) {
var grd2 = ctx.createLinearGradient(0, 0, -150, 0);
// grd2.addColorStop(0, 'rgba(255, 255, 155, 0.8)');
// grd2.addColorStop(1, 'rgba(255, 200, 0, 0.1)');
grd2.addColorStop(0, 'rgba(150, 200, 255, 0.7)');
grd2.addColorStop(1, 'rgba(150, 200, 255, 0)');
ctx.fillStyle = grd2;
ctx.beginPath();
ctx.moveTo(-18, -25);
//10 * (Math.random() - 0.5), 10 * (Math.random() - 0.5)
ctx.lineTo(-18, 25);
ctx.lineTo(-50 - 100 * Math.random(), 0);
ctx.fill();
} else if (this.distanceToPlayer2() < this.repulsionRange) {
var grd2 = ctx.createLinearGradient(0, 0, 80, 0);
grd2.addColorStop(0, 'rgba(150, 200, 255, 0.7)');
grd2.addColorStop(1, 'rgba(150, 200, 255, 0)');
ctx.fillStyle = grd2;
ctx.beginPath();
ctx.moveTo(20, -16);
//10 * (Math.random() - 0.5), 10 * (Math.random() - 0.5)
ctx.lineTo(20, 16);
ctx.lineTo(35 + 43 * Math.random(), 0);
ctx.fill();
}
ctx.restore()
ctx.save()
ctx.translate(this.position.x, this.position.y)
ctx.rotate(Math.atan2(this.seePlayer.position.y - this.position.y, this.seePlayer.position.x - this.position.x))
ctx.beginPath();
ctx.arc(0, 0, 30, 0, 2 * Math.PI);
ctx.fillStyle = m.bodyGradient
ctx.fill();
ctx.arc(15, 0, 4, 0, 2 * Math.PI);
ctx.strokeStyle = "#333";
ctx.lineWidth = 2;
ctx.stroke();
ctx.beginPath(); //eye
ctx.arc(15, 0, 3.5, 0, 2 * Math.PI);
ctx.fillStyle = `rgba(255, 0, 0, ${this.health * this.health})`;
ctx.fill()
ctx.restore()
//set direction to turn to fire
if (this.seePlayer.recall && !(simulation.cycle % 30)) {
this.seePlayer.recall -= 10;
this.fireDir = Vector.normalise(Vector.sub(this.seePlayer.position, this.position));
spawn.sniperBullet(this.position.x, this.position.y, 7 + Math.ceil(this.radius / 15), 5);
const v = 10 + 8 * simulation.accelScale;
Matter.Body.setVelocity(mob[mob.length - 1], {
x: this.velocity.x + this.fireDir.x * v,
y: this.velocity.y + this.fireDir.y * v
});
}
};
}
const laserEM = function (x, y, radius = 30) {
mobs.spawn(x, y, 8, radius, '#00000000');
let me = mob[mob.length - 1];
me.accelMag = 0.0003
me.stroke = 'transparent';
//me.isBoss = true;
me.frictionStatic = 0;
me.friction = 0;
me.seeAtDistance2 = 20000000 //14000 vision range
me.vertices = Matter.Vertices.rotate(me.vertices, Math.PI, me.position);
Matter.Body.rotate(me, Math.random() * Math.PI * 2);
me.showHealthBar = false
Matter.Body.setDensity(me, 0.01)
me.seePlayerFreq = 0
me.searchTarget = map[Math.floor(Math.random() * (map.length - 1))].position;
me.swordDamage = 0.025 * simulation.dmgScale
me.collisionFilter.mask = cat.bullet | cat.player | cat.body | cat.map | cat.mob | cat.mobBullet
me.repulsionRange = 50000;
me.do = function () {
this.repulsion();
this.search()
if (this.seePlayer.recall) {
const h = this.radius * 0.3;
const w = this.radius * 2;
const x = this.position.x - w / 2;
const y = this.position.y - w * 0.7;
ctx.fillStyle = "rgba(100, 100, 100, 0.3)";
ctx.fillRect(x, y, w, h);
ctx.fillStyle = "rgba(0,255,255,0.7)";
ctx.fillRect(x, y, w * this.health, h);
}
if (this.health < 1) {
this.health += 0.0005; //regen
}
if (this.seePlayer.recall) {
this.laserSword(this.position, Math.atan2(this.seePlayer.position.y - this.position.y, this.seePlayer.position.x - this.position.x), 500 * Math.random());
}
ctx.save()
ctx.translate(this.position.x, this.position.y)
ctx.rotate(Math.atan2(this.seePlayer.position.y - this.position.y, this.seePlayer.position.x - this.position.x))
if (this.seePlayer.recall > 0 && this.distanceToPlayer2() > this.repulsionRange) {
var grd2 = ctx.createLinearGradient(0, 0, -150, 0);
// grd2.addColorStop(0, 'rgba(255, 255, 155, 0.8)');
// grd2.addColorStop(1, 'rgba(255, 200, 0, 0.1)');
grd2.addColorStop(0, 'rgba(150, 200, 255, 0.7)');
grd2.addColorStop(1, 'rgba(150, 200, 255, 0)');
ctx.fillStyle = grd2;
ctx.beginPath();
ctx.moveTo(-18, -25);
//10 * (Math.random() - 0.5), 10 * (Math.random() - 0.5)
ctx.lineTo(-18, 25);
ctx.lineTo(-50 - 100 * Math.random(), 0);
ctx.fill();
} else if (this.distanceToPlayer2() < this.repulsionRange) {
var grd2 = ctx.createLinearGradient(0, 0, 80, 0);
grd2.addColorStop(0, 'rgba(150, 200, 255, 0.7)');
grd2.addColorStop(1, 'rgba(150, 200, 255, 0)');
ctx.fillStyle = grd2;
ctx.beginPath();
ctx.moveTo(20, -16);
//10 * (Math.random() - 0.5), 10 * (Math.random() - 0.5)
ctx.lineTo(20, 16);
ctx.lineTo(35 + 43 * Math.random(), 0);
ctx.fill();
}
ctx.restore()
ctx.save()
ctx.translate(this.position.x, this.position.y)
ctx.rotate(Math.atan2(this.seePlayer.position.y - this.position.y, this.seePlayer.position.x - this.position.x))
ctx.beginPath();
ctx.arc(0, 0, 30, 0, 2 * Math.PI);
ctx.fillStyle = m.bodyGradient
ctx.fill();
ctx.arc(15, 0, 4, 0, 2 * Math.PI);
ctx.strokeStyle = "#333";
ctx.lineWidth = 2;
ctx.stroke();
ctx.beginPath(); //eye
ctx.arc(15, 0, 3.5, 0, 2 * Math.PI);
ctx.fillStyle = `rgba(255, 0, 0, ${this.health * this.health})`;
ctx.fill()
ctx.restore()
this.seePlayerCheck();
this.attraction();
}
me.laserSword = function (where, angle, length) {
const sub = Vector.sub(this.seePlayer.position, this.position)
const unit = Vector.normalise(sub)
const path = [{
x: this.position.x + 20 * Math.cos(this.angle),
y: this.position.y + 20 * Math.sin(this.angle)
},
{
x: this.position.x + (120 + 400) * Math.sqrt(Math.random()) * Math.cos(this.angle),
y: this.position.y + (120 + 400) * Math.sqrt(Math.random()) * Math.sin(this.angle)
}
];
this.seePlayer.recall -= 3;
const vertexCollision = function (v1, v1End, domain) {
for (let i = 0; i < domain.length; ++i) {
let vertices = domain[i].vertices;
const len = vertices.length - 1;
for (let j = 0; j < len; j++) {
results = simulation.checkLineIntersection(v1, v1End, vertices[j], vertices[j + 1]);
if (results.onLine1 && results.onLine2) {
const dx = v1.x - results.x;
const dy = v1.y - results.y;
const dist2 = dx * dx + dy * dy;
if (dist2 < best.dist2 && (!domain[i].mob || domain[i].alive)) best = { x: results.x, y: results.y, dist2: dist2, who: domain[i], v1: vertices[j], v2: vertices[j + 1] };
}
}
results = simulation.checkLineIntersection(v1, v1End, vertices[0], vertices[len]);
if (results.onLine1 && results.onLine2) {
const dx = v1.x - results.x;
const dy = v1.y - results.y;
const dist2 = dx * dx + dy * dy;
if (dist2 < best.dist2) best = { x: results.x, y: results.y, dist2: dist2, who: domain[i], v1: vertices[0], v2: vertices[len] };
}
}
};
best = { x: null, y: null, dist2: Infinity, who: null, v1: null, v2: null };
const look = { x: where.x + length * Math.cos(angle), y: where.y + length * Math.sin(angle) };
// vertexCollision(where, look, body); // vertexCollision(where, look, mob);
vertexCollision(where, look, map);
if (!m.isCloak) vertexCollision(where, look, [player]);
if (best.who && (best.who === player) && m.immuneCycle < m.cycle) {
m.immuneCycle = m.cycle + m.collisionImmuneCycles; //player is immune to damage for an extra second
m.damage(this.swordDamage);
simulation.drawList.push({ //add dmg to draw queue
x: best.x,
y: best.y,
radius: this.swordDamage * 1500,
color: "rgba(80,0,255,0.5)",
time: 20
});
}
if (best.dist2 === Infinity) best = look;
ctx.beginPath(); //draw beam
ctx.moveTo(where.x, where.y);
ctx.lineTo(best.x, best.y);
ctx.strokeStyle = "#50f";
ctx.lineWidth = 1.5;
ctx.setLineDash([70 + 300 * Math.random(), 55 * Math.random()]);
ctx.stroke(); // Draw it
ctx.setLineDash([]);
ctx.lineWidth = 20;
ctx.strokeStyle = "rgba(80,0,255,0.07)";
ctx.stroke(); // Draw it
const Dx = Math.cos(Math.atan2(this.seePlayer.position.y - this.position.y, this.seePlayer.position.x - this.position.x));
const Dy = Math.sin(Math.atan2(this.seePlayer.position.y - this.position.y, this.seePlayer.position.x - this.position.x));
let xElec = this.position.x + 40 * Dx;
let yElec = this.position.y + 40 * Dy;
ctx.beginPath();
ctx.moveTo(xElec, yElec);
const step = 40
for (let i = 0; i < 6; i++) {
xElec += step * (Dx + 1.5 * (Math.random() - 0.5))
yElec += step * (Dy + 1.5 * (Math.random() - 0.5))
ctx.lineTo(xElec, yElec);
}
ctx.strokeStyle = "#50f";
ctx.lineWidth = 1.5;
ctx.setLineDash([70 + 300 * Math.random(), 55 * Math.random()]);
ctx.stroke(); // Draw it
ctx.setLineDash([]);
}
};
if (powerUps.pass == undefined) {
let pass = { pass: true, activated: false };
Object.assign(powerUps, pass)
}
const loadOut = {
loadOut: {
name: "loadOut",
color: "#000000", //"hsl(248,100%,65%)",
size() { return 40 },
effect() {
if (m.alive) {
// tech.damage *= 2;
let text = "";
if (!tech.isSuperDeterminism) { text += `<div class='cancel' onclick='powerUps.endDraft("buff",true)'>${tech.isCancelTech ? "?" : "✕"}</div>`; };
text += `<h3 style = 'color:#fff; text-align:left; margin: 0px;'>Blessing Of Sal</h3>`;
text += `<div class="choose-grid-module" onclick="powerUps.loadOut.choose(1)"><div class="grid-title"><div class="circle-grid tech" style="background-image: linear-gradient(lightyellow, yellow);"></div> &nbsp; Speed Boost</div>Increase speed by 5%</div>`;
text += `<div class="choose-grid-module" onclick="powerUps.loadOut.choose(2)"><div class="grid-title"><div class="circle-grid tech" style="background-image: linear-gradient(gray, lightgray);"></div> &nbsp; Defense Boost</div>Reduce damage by 5%</div>`;
text += `<div class="choose-grid-module" onclick="powerUps.loadOut.choose(3)"><div class="grid-title"><div class="circle-grid tech" style="background-image: linear-gradient(red, orange);"></div> &nbsp; Damage Boost</div>Increase damage by 10%</div>`;
if (powerUps.pass == true) {
text += `<div class="choose-grid-module" onclick="powerUps.loadOut.choose(4)"><div class="grid-title"><div class="circle-grid tech" style="background-image: radial-gradient(black, gray);"></div> &nbsp; Blade of Sal</div>Press Shift to summon the <b style="color: ${m.eyeFillColor};">Mythical</b> <em style="color: lightblue; text-shadow: ${m.eyeFillColor} 0px 0 5px;">Las Slayer</em><div>Drains <strong class='color-f'>Energy</strong></div></div>`;
}
document.getElementById("choose-grid").innerHTML = text;
powerUps.showDraft();//no known bugs ig idk, im keep this as it is
}
},
choose(index) {
if (index == 1) {
tech.squirrelFx += 0.25;
tech.squirrelJump += 0.1;
m.setMovement();
powerUps.endDraft("buff");
} else if (index == 2) {
simulation.dmgScale *= 0.95;
powerUps.endDraft("buff");
} else if (index == 3) {
m.dmgScale *= 1.1;
powerUps.endDraft("buff");
} else if (index == 4) { //sword!
powerUps.pass = false;
addEventListener("keydown", function (event) {
if (event.key == "Shift" && powerUps.activated == false) {
sword()
powerUps.activated = true;
} else if (event.key == "Shift" && powerUps.activated == true) {
for (let i = 0; i < mob.length; i++) {
if (mob[i].isSword) {
mob[i].death()
}
powerUps.activated = false;
}
}
})
powerUps.endDraft("buff");
}
}
}
}
Object.assign(powerUps, loadOut)
const restoreBoss = function (x, y, radius = 30) {
mobs.spawn(x, y, 8, radius, 'transparent');
let me = mob[mob.length - 1];
me.stroke = 'transparent';
let aim = '#FFFFFF';
me.accelMag = 0.0006
me.isBoss = true;
me.restoreBoss = true;
me.frictionStatic = 0;
me.friction = 0;
me.seeAtDistance2 = 20000000 //14000 vision range
me.vertices = Matter.Vertices.rotate(me.vertices, Math.PI, me.position); //make the pointy side of triangle the front
Matter.Body.rotate(me, Math.random() * Math.PI * 2);
me.showHealthBar = false
Matter.Body.setDensity(me, m.maxHealth / (simulation.difficulty < 5 ? 0.5 : simulation.difficulty / simulation.difficultyMode))
me.seePlayerFreq = 0
me.swordDamage = 0.025 * simulation.dmgScale
me.collisionFilter.mask = cat.bullet | cat.player | cat.body | cat.map | cat.mob | cat.mobBullet
me.repulsionRange = 500000;
me.isDropPowerUp = false;
//Matter.Body.setVelocity(me, { x: 10 * (Math.random() - 0.5), y: 10 * (Math.random() - 0.5) });
let index = 0;
me.energy = 1;
me.maxEnergy = 1;
me.immuneCycle = 0;
me.cycle = 0;
me.onDeath = function () {
powerUps.spawn(this.position.x, this.position.y, "loadOut");
}
for (let i = 0; i < b.totalBots(); i++) { //normal orbitals look too boring, so...
orbital(me, 190 + 130 * tech.isOrbitBotUpgrade, (index / b.totalBots()) * 2 * Math.PI, 0.05, Math.floor(Math.sin(simulation.cycle / 10) * 100)); //who, radius, phase, speed
index++;
}
me.do = function () {
this.cycle++;
if (this.seePlayer.recall) { //fields
if (m.fieldMode == 0 && this.distanceToPlayer2() < 200000) {
if (Vector.magnitude(Vector.sub(m.pos, this.position)) - this.radius < m.fieldRange && Matter.Query.ray(map, m.pos, this.position).length === 0) {
this.pushM();
}
this.drawField();
// this.repel();
}
if (m.fieldMode == 2) {
if (this.distanceToPlayer2() < 200000) {
if (Vector.magnitude(Vector.sub(m.pos, this.position)) - this.radius < m.fieldRange && Matter.Query.ray(map, m.pos, this.position).length === 0) {
this.pushM();
}
this.drawField()
}
if (tech.isPerfectBrake) { //cap player and bullet speed around restoreBoss //mobs basically can't hit you when you have this, so to make it fair...
const wave = Math.cos(m.cycle * 0.022);
const range = 200 + 140 * wave + 150 * m.energy
const distance = Vector.magnitude(Vector.sub(this.position, m.pos))
const cap = this.immuneCycle > this.cycle ? 8 : 4
if (distance < range) {
if (player.speed > cap && Vector.dot(player.velocity, Vector.sub(this.position, m.pos)) > 0) { // if velocity is directed towards player
Matter.Body.setVelocity(player, Vector.mult(Vector.normalise(player.velocity), cap)); //set velocity to cap, but keep the direction
}
}
for (let i = 0; i < bullet.length; i++) {
const distance2 = Vector.magnitude(Vector.sub(this.position, bullet[i].position))
if (distance2 < range) {
if (bullet[i].speed > cap && Vector.dot(bullet[i].velocity, Vector.sub(this.position, bullet[i].position)) > 0) { // if velocity is directed towards player
Matter.Body.setVelocity(bullet[i], Vector.mult(Vector.normalise(bullet[i].velocity), cap)); //set velocity to cap, but keep the direction
}
}
}
ctx.beginPath();
ctx.arc(this.position.x, this.position.y, range, 0, 2 * Math.PI);
ctx.fillStyle = "rgba(0,0,0,0.08)";
ctx.fill();
}
}
if (m.fieldMode == 5 && this.distanceToPlayer2() < 200000) {
this.laserSword(this.position, Math.atan2(this.seePlayer.position.y - this.position.y, this.seePlayer.position.x - this.position.x), 500 * Math.random());
}
if (m.fieldMode == 6) {
this.timeAttack();
}
if (m.fieldMode == 9) {
if (this.distanceToPlayer2() < 300000) {
this.teleportAway() //blink but reversed
}
}
}
if (m.immuneCycle > m.cycle) {
me.damageReduction = 0;
} else {
me.damageReduction = 1;
}
this.repulsion();
this.seePlayerCheck();
this.attraction();
this.lostPlayer();
if (this.seePlayer.recall) {
const h = this.radius * 0.3;
const w = this.radius * 2;
const x = this.position.x - w / 2;
const y = this.position.y - w * 0.7;
ctx.fillStyle = "rgba(10, 10, 10, 0.3)";
ctx.fillRect(x, y, w, h);
ctx.fillStyle = "rgba(0,0,0,0.7)";
ctx.fillRect(x, y, w * this.energy, h);
}
if (this.health < 1) {
this.health += 0.0001; //regen
} else if (this.health < 1) {
this.health += 0.00005; //reduced regen
}
if (this.energy < 0) {//energy thingy
this.energy = 0;
} else if (this.energy > this.maxEnergy) {
this.energy = this.maxEnergy;
} else if (this.energy < this.maxEnergy) {
this.energy += 0.001;
}
ctx.save()
ctx.translate(this.position.x, this.position.y)
ctx.rotate(Math.atan2(this.seePlayer.position.y - this.position.y, this.seePlayer.position.x - this.position.x))
if (this.seePlayer.recall > 0 && this.distanceToPlayer2() > this.repulsionRange) {
ctx.globalAlpha = (this.immuneCycle < this.cycle) ? 1 : 0.5
var grd2 = ctx.createLinearGradient(0, 0, -150, 0);
// grd2.addColorStop(0, 'rgba(255, 255, 155, 0.8)');
// grd2.addColorStop(1, 'rgba(255, 200, 0, 0.1)');
grd2.addColorStop(0, 'rgba(150, 200, 255, 0.7)');
grd2.addColorStop(1, 'rgba(150, 200, 255, 0)');
ctx.fillStyle = grd2;
ctx.beginPath();
ctx.moveTo(-18, -25);
//10 * (Math.random() - 0.5), 10 * (Math.random() - 0.5)
ctx.lineTo(-18, 25);
ctx.lineTo(-50 - 100 * Math.random(), 0);
ctx.fill();
} else if (this.distanceToPlayer2() < this.repulsionRange) {
ctx.globalAlpha = (this.immuneCycle < this.cycle) ? 1 : 0.5
var grd2 = ctx.createLinearGradient(0, 0, 80, 0);
grd2.addColorStop(0, 'rgba(150, 200, 255, 0.7)');
grd2.addColorStop(1, 'rgba(150, 200, 255, 0)');
ctx.fillStyle = grd2;
ctx.beginPath();
ctx.moveTo(20, -16);
//10 * (Math.random() - 0.5), 10 * (Math.random() - 0.5)
ctx.lineTo(20, 16);
ctx.lineTo(35 + 43 * Math.random(), 0);
ctx.fill();
}
ctx.restore()
ctx.save()
ctx.globalAlpha = (this.immuneCycle < this.cycle) ? 1 : 0.5
ctx.translate(this.position.x, this.position.y)
ctx.rotate(Math.atan2(this.seePlayer.position.y - this.position.y, this.seePlayer.position.x - this.position.x))
if (this.health > 0.5) {
ctx.beginPath();
ctx.arc(0, 0, 30, 0, 2 * Math.PI);
ctx.fillStyle = m.bodyGradient
ctx.fill();
ctx.arc(15, 0, 4, 0, 2 * Math.PI);
ctx.strokeStyle = "#333";
ctx.lineWidth = 2;
ctx.stroke();
ctx.beginPath(); //eye
ctx.arc(15, 0, 3.5, 0, 2 * Math.PI);
ctx.fillStyle = `rgba(255, 0, 0, ${this.health * this.health})`;
ctx.fill()
ctx.restore()
} else {
ctx.beginPath();
ctx.arc(0, 0, 30, 0, 2 * Math.PI);
ctx.fillStyle = m.bodyGradient
ctx.fill();
ctx.strokeStyle = "#333";
ctx.lineWidth = 2;
if (!(Math.atan2(this.seePlayer.position.y - this.position.y, this.seePlayer.position.x - this.position.x) > -Math.PI / 2 && Math.atan2(this.seePlayer.position.y - this.position.y, this.seePlayer.position.x - this.position.x) < Math.PI / 2)) ctx.scale(1, -1); //here is the flip
ctx.stroke();
ctx.beginPath();
ctx.arc(2, -6, 7, 0, 2 * Math.PI);
ctx.stroke();
ctx.beginPath();
ctx.arc(25, -6, 7, 0.25 * Math.PI, 1.6 * Math.PI);
ctx.stroke();
ctx.beginPath();
ctx.arc(2, -10, 9, 1.25 * Math.PI, 1.75 * Math.PI);
ctx.stroke();
ctx.beginPath();
ctx.arc(25, -10, 9, 1.25 * Math.PI, 1.4 * Math.PI);
ctx.stroke();
ctx.beginPath();
ctx.arc(18, 13, 10, 0, 2 * Math.PI);
ctx.fillStyle = m.bodyGradient;
ctx.fill();
ctx.stroke();
ctx.beginPath();
ctx.arc(18, 13, 6, 0, 2 * Math.PI);
ctx.fillStyle = "#555";
ctx.fill();
ctx.stroke();
ctx.beginPath();
ctx.arc(3, -6, 3, 0, 2 * Math.PI);
ctx.fill();
ctx.stroke();
ctx.beginPath();
ctx.arc(26, -6, 3, 0, 2 * Math.PI);
ctx.fill();
ctx.stroke();
ctx.restore();
}
if (m.fieldMode == 1) { //render over I think
if (this.energy > 0.1) {
this.harmonic3Phase();
}
}
if (m.fieldMode == 3) {
const angle = Math.atan2(this.seePlayer.position.y - this.position.y, this.seePlayer.position.x - this.position.x);
me.damageReduction = 0.5;
me.accelMag = 0.0012;
if (!(simulation.cycle % Math.floor(100 + 90 * Math.random() * simulation.CDScale))) {
this.diveAttack()
}
ctx.beginPath();
ctx.arc(this.position.x, this.position.y, 1000, 0, 2 * Math.PI);
ctx.fillStyle = "#f5f5ff";
ctx.strokeStyle = "#f5f5ff55";
ctx.setLineDash([125 * Math.random(), 125 * Math.random()]);
ctx.globalCompositeOperation = "difference";
ctx.fill();
ctx.globalCompositeOperation = "source-over";
ctx.stroke()
ctx.setLineDash([]);
} else {
me.accelMag = 0.0006;
}
if (this.immuneCycle > this.cycle) {
this.damageReduction = 0;
} else {
if (m.fieldMode == 3) {
this.damageReduction = 0.5;
} else {
this.damageReduction = 1;
}
}
if (this.seePlayer.recall) { //fields
this.gun()
}
}
me.laserSword = function (where, angle, length) {
const sub = Vector.sub(this.seePlayer.position, this.position)
const unit = Vector.normalise(sub)
const path = [{
x: this.position.x + 20 * Math.cos(this.angle),
y: this.position.y + 20 * Math.sin(this.angle)
},
{
x: this.position.x + (120 + 400) * Math.sqrt(Math.random()) * Math.cos(this.angle),
y: this.position.y + (120 + 400) * Math.sqrt(Math.random()) * Math.sin(this.angle)
}
];
this.seePlayer.recall -= 3;
const vertexCollision = function (v1, v1End, domain) {
for (let i = 0; i < domain.length; ++i) {
let vertices = domain[i].vertices;
const len = vertices.length - 1;
for (let j = 0; j < len; j++) {
results = simulation.checkLineIntersection(v1, v1End, vertices[j], vertices[j + 1]);
if (results.onLine1 && results.onLine2) {
const dx = v1.x - results.x;
const dy = v1.y - results.y;
const dist2 = dx * dx + dy * dy;
if (dist2 < best.dist2 && (!domain[i].mob || domain[i].alive)) best = { x: results.x, y: results.y, dist2: dist2, who: domain[i], v1: vertices[j], v2: vertices[j + 1] };
}
}
results = simulation.checkLineIntersection(v1, v1End, vertices[0], vertices[len]);
if (results.onLine1 && results.onLine2) {
const dx = v1.x - results.x;
const dy = v1.y - results.y;
const dist2 = dx * dx + dy * dy;
if (dist2 < best.dist2) best = { x: results.x, y: results.y, dist2: dist2, who: domain[i], v1: vertices[0], v2: vertices[len] };
}
}
};
best = { x: null, y: null, dist2: Infinity, who: null, v1: null, v2: null };
const look = { x: where.x + length * Math.cos(angle), y: where.y + length * Math.sin(angle) };
// vertexCollision(where, look, body); // vertexCollision(where, look, mob);
vertexCollision(where, look, map);
if (!m.isCloak) vertexCollision(where, look, [player]);
if (best.who && (best.who === player) && m.immuneCycle < m.cycle) {
m.immuneCycle = m.cycle + m.collisionImmuneCycles; //player is immune to damage for an extra second
m.damage(this.swordDamage);
simulation.drawList.push({ //add dmg to draw queue
x: best.x,
y: best.y,
radius: this.swordDamage * 1500,
color: "rgba(0,0,0,0.5)",
time: 20
});
}
if (best.dist2 === Infinity) best = look;
ctx.beginPath(); //draw beam
ctx.moveTo(where.x, where.y);
ctx.lineTo(best.x, best.y);
ctx.strokeStyle = "#000";
ctx.lineWidth = 1.5;
ctx.setLineDash([70 + 300 * Math.random(), 55 * Math.random()]);
ctx.stroke(); // Draw it
ctx.setLineDash([]);
ctx.lineWidth = 20;
ctx.strokeStyle = "rgba(0,0,0,0.07)";
ctx.stroke(); // Draw it
const Dx = Math.cos(Math.atan2(this.seePlayer.position.y - this.position.y, this.seePlayer.position.x - this.position.x));
const Dy = Math.sin(Math.atan2(this.seePlayer.position.y - this.position.y, this.seePlayer.position.x - this.position.x));
let xElec = this.position.x + 40 * Dx;
let yElec = this.position.y + 40 * Dy;
ctx.beginPath();
ctx.moveTo(xElec, yElec);
const step = 40
for (let i = 0; i < 6; i++) {
xElec += step * (Dx + 1.5 * (Math.random() - 0.5))
yElec += step * (Dy + 1.5 * (Math.random() - 0.5))
ctx.lineTo(xElec, yElec);
}
ctx.strokeStyle = "#000";
ctx.lineWidth = 1.5;
ctx.setLineDash([70 + 300 * Math.random(), 55 * Math.random()]);
ctx.stroke(); // Draw it
ctx.setLineDash([]);
}
me.drawField = function () {
if (m.fieldMode != 2) {
const angle = Math.atan2(this.seePlayer.position.y - this.position.y, this.seePlayer.position.x - this.position.x)
const range = m.fieldRange;
ctx.beginPath();
ctx.arc(this.position.x, this.position.y, range, angle - Math.PI * m.fieldArc, angle + Math.PI * m.fieldArc, false);
ctx.lineWidth = 2;
ctx.stroke();
let eye = 13;
if (m.fieldMode == 2) {
eye = 30
}
let aMag = 0.75 * Math.PI * m.fieldArc
let a = angle + aMag
let cp1x = this.position.x + 0.6 * range * Math.cos(a)
let cp1y = this.position.y + 0.6 * range * Math.sin(a)
ctx.quadraticCurveTo(cp1x, cp1y, this.position.x + eye * Math.cos(angle), this.position.y + eye * Math.sin(angle))
a = angle - aMag
cp1x = this.position.x + 0.6 * range * Math.cos(a)
cp1y = this.position.y + 0.6 * range * Math.sin(a)
ctx.quadraticCurveTo(cp1x, cp1y, this.position.x + 1 * range * Math.cos(angle - Math.PI * m.fieldArc), this.position.y + 1 * range * Math.sin(angle - Math.PI * m.fieldArc))
ctx.fill();
// ctx.lineTo(this.position.x + eye * Math.cos(angle), this.position.y + eye * Math.sin(angle));
//draw random lines in field for cool effect
let offAngle = angle + 1.7 * Math.PI * m.fieldArc * (Math.random() - 0.5);
ctx.beginPath();
eye = 15;
ctx.moveTo(this.position.x + eye * Math.cos(angle), this.position.y + eye * Math.sin(angle));
ctx.lineTo(this.position.x + range * Math.cos(offAngle), this.position.y + range * Math.sin(offAngle));
ctx.strokeStyle = "rgba(0,0,0,0.6)";
ctx.lineWidth = 1;
ctx.stroke();
} else {
ctx.beginPath();
const wave = Math.cos(m.cycle * 0.022);
const angle = Math.atan2(this.seePlayer.position.y - this.position.y, this.seePlayer.position.x - this.position.x)
ctx.arc(this.position.x, this.position.y, m.fieldRange, angle - Math.PI * m.fieldArc, angle + Math.PI * m.fieldArc, false);
ctx.lineWidth = 2.5 - 1.5 * wave;
ctx.stroke();
const curve = 0.57 + 0.04 * wave
const aMag = (1 - curve * 1.2) * Math.PI * m.fieldArc
let a = angle + aMag
let cp1x = this.position.x + curve * m.fieldRange * Math.cos(a)
let cp1y = this.position.y + curve * m.fieldRange * Math.sin(a)
ctx.quadraticCurveTo(cp1x, cp1y, this.position.x + 30 * Math.cos(angle), this.position.y + 30 * Math.sin(angle))
a = angle - aMag
cp1x = this.position.x + curve * m.fieldRange * Math.cos(a)
cp1y = this.position.y + curve * m.fieldRange * Math.sin(a)
ctx.quadraticCurveTo(cp1x, cp1y, this.position.x + 1 * m.fieldRange * Math.cos(angle - Math.PI * m.fieldArc), this.position.y + 1 * m.fieldRange * Math.sin(angle - Math.PI * m.fieldArc))
ctx.fill();
}
}
me.pushM = function () {
const unit = Vector.normalise(Vector.sub(this.position, player.position))
if (tech.blockDmg) {
Matter.Body.setVelocity(player, { x: 0.5 * player.velocity.x, y: 0.5 * player.velocity.y });
//draw electricity
const step = 40
ctx.beginPath();
for (let i = 0, len = 0.8 * tech.blockDmg; i < len; i++) {
let x = this.position.x - 20 * unit.x;
let y = this.position.y - 20 * unit.y;
ctx.moveTo(x, y);
for (let i = 0; i < 8; i++) {
x += step * (-unit.x + 1.5 * (Math.random() - 0.5))
y += step * (-unit.y + 1.5 * (Math.random() - 0.5))
ctx.lineTo(x, y);
}
}
if (m.immuneCycle < m.cycle) {
m.immuneCycle = m.cycle + m.collisionImmuneCycles
m.damage(0.025 * simulation.dmgScale)
}
ctx.lineWidth = 3;
ctx.strokeStyle = "#000";
ctx.stroke();
}
const massRoot = Math.sqrt(Math.min(12, Math.max(0.15, player.mass))); // masses above 12 can start to overcome the push back //idk
Matter.Body.setVelocity(player, {
x: this.velocity.x - (15 * unit.x) / massRoot,
y: this.velocity.y - (15 * unit.y) / massRoot
});
}
me.diveAttack = function () {
const forceMag = this.accelMag * this.mass;
const angle = Math.atan2(this.seePlayer.position.y - this.position.y, this.seePlayer.position.x - this.position.x);
this.force.x += 150 * forceMag * Math.cos(angle);
this.force.y += 150 * forceMag * Math.sin(angle);
ctx.beginPath()
ctx.moveTo(this.position.x + Math.sin(angle), this.position.y + Math.cos(angle))
ctx.lineTo(this.seePlayer.position.x, this.seePlayer.position.y)
aim = '#000000';
ctx.stroke()
}
me.teleportAway = function () {//hehe
ctx.beginPath();
ctx.moveTo(this.position.x, this.position.y);
if (this.seePlayer.recall && !(simulation.cycle % 17)) {
const dist = Vector.sub(this.position, this.seePlayer.position);
const distMag = Vector.magnitude(dist);
const unitVector = Vector.normalise(dist);
const rando = (Math.random() - 0.5) * 50;
if (distMag < 20000) {
Matter.Body.translate(this, Vector.mult(unitVector, distMag + rando));
} else {
Matter.Body.translate(this, Vector.mult(unitVector, 20000 + rando));
}
}
ctx.lineTo(this.position.x, this.position.y);
ctx.lineWidth = radius * 2;
ctx.strokeStyle = "rgba(0,0,0,0.08)";
ctx.stroke();
if (!this.seePlayer.yes) {
ctx.beginPath();
ctx.moveTo(this.position.x, this.position.y);
if (this.seePlayer.recall && !(simulation.cycle % 17)) {
const dist = Vector.sub(this.seePlayer.position, this.position);
const distMag = Vector.magnitude(dist);
const unitVector = Vector.normalise(dist);
const rando = (Math.random() - 0.5) * 50;
if (distMag < 200000) {
Matter.Body.translate(this, Vector.mult(unitVector, distMag + rando));
} else {
Matter.Body.translate(this, Vector.mult(unitVector, 200000 + rando));
}
}
ctx.lineTo(this.position.x, this.position.y);
ctx.lineWidth = radius * 2;
ctx.strokeStyle = "rgba(0,0,0,0.08)";
ctx.stroke();
}
}
me.timeAttack = function () {
if (!simulation.isTimeSkipping || input.field) {
requestAnimationFrame(() => {
simulation.timePlayerSkip(5)
m.walk_cycle += m.flipLegs * m.Vx * 5
}); //just doing what lilgreenland did
}
}
me.harmonic3Phase = function () { //normal standard 3 different 2-d circles
if (tech.harmonics === 2) {
const fieldRange1 = (0.75 + 0.3 * Math.cos(m.cycle / 23)) * m.fieldRange * m.harmonicRadius
const fieldRange2 = (0.68 + 0.37 * Math.cos(m.cycle / 37)) * m.fieldRange * m.harmonicRadius
const fieldRange3 = (0.7 + 0.35 * Math.cos(m.cycle / 47)) * m.fieldRange * m.harmonicRadius
const netfieldRange = Math.max(fieldRange1, fieldRange2, fieldRange3)
ctx.fillStyle = "rgba(0,0,0," + Math.min(0.6, (0.04 + m.energy * (0.1 + 0.11 * Math.random()))) + ")";
ctx.beginPath();
ctx.arc(this.position.x, this.position.y, fieldRange1, 0, 2 * Math.PI);
ctx.fill();
ctx.beginPath();
ctx.arc(this.position.x, this.position.y, fieldRange2, 0, 2 * Math.PI);
ctx.fill();
ctx.beginPath();
ctx.arc(this.position.x, this.position.y, fieldRange3, 0, 2 * Math.PI);
ctx.fill();
//360 block
if (Vector.magnitude(Vector.sub(player.position, this.position)) - this.radius < netfieldRange) {
me.pushM();
}
for (let i = 0; i < bullet.length; i++) {
if (Vector.magnitude(Vector.sub(bullet[i].position, this.position)) - this.radius < netfieldRange) {
const dx = bullet[i].position.x - this.position.x;
const dy = bullet[i].position.y - this.position.y;
const dist = Math.sqrt(dx * dx + dy * dy);
if (dist < m.fieldRange) {
const angle = Math.atan2(dy, dx);
const mag = (1500 * bullet[i].mass * simulation.g) / dist;
bullet[i].force.x += mag * Math.cos(angle);
bullet[i].force.y += mag * Math.sin(angle);
}
this.energy -= 0.0012;
}
}
} else {
const rotation = simulation.cycle * 0.0031
const phase = simulation.cycle * 0.023
const radius = m.fieldRange * m.harmonicRadius
ctx.lineWidth = 1;
ctx.strokeStyle = "rgba(0,0,0,0.5)"
ctx.fillStyle = `rgba(0,0,0,${Math.min(0.6, m.energy * (0.11 + 0.1 * Math.random()) * (3 / tech.harmonics))})`;
// ctx.fillStyle = "rgba(0,0,0," + Math.min(0.7, m.energy * (0.22 - 0.01 * tech.harmonics) * (0.5 + 0.5 * Math.random())) + ")";
for (let i = 0; i < tech.harmonics; i++) {
ctx.beginPath();
ctx.ellipse(this.position.x, this.position.y, radius * Math.abs(Math.sin(phase + i / tech.harmonics * Math.PI)), radius, rotation + i / tech.harmonics * Math.PI, 0, 2 * Math.PI);
ctx.fill();
ctx.stroke();
}
//360 block
if (Vector.magnitude(Vector.sub(player.position, this.position)) - this.radius < radius) {
me.pushM();
}
for (let i = 0; i < bullet.length; i++) {
if (Vector.magnitude(Vector.sub(bullet[i].position, this.position)) - this.radius < radius) {
const dx = bullet[i].position.x - this.position.x;
const dy = bullet[i].position.y - this.position.y;
const dist = Math.sqrt(dx * dx + dy * dy);
if (dist < m.fieldRange) {
const angle = Math.atan2(dy, dx);
const mag = (1500 * bullet[i].mass * simulation.g) / dist;
bullet[i].force.x += mag * Math.cos(angle);
bullet[i].force.y += mag * Math.sin(angle);
}
this.energy -= 0.0012;
}
}
}
}
me.railGun = function () {
const angle = Math.atan2(this.seePlayer.position.y - this.position.y, this.seePlayer.position.x - this.position.x);
const X = this.position.x
const Y = this.position.y
const unitVector = { x: Math.cos(angle), y: Math.sin(angle) }
const unitVectorPerp = Vector.perp(unitVector)
function magField(mag, arc) {
ctx.moveTo(X, Y);
ctx.bezierCurveTo(
X + unitVector.x * mag, Y + unitVector.y * mag,
X + unitVector.x * mag + unitVectorPerp.x * arc, Y + unitVector.y * mag + unitVectorPerp.y * arc,
X + unitVectorPerp.x * arc, Y + unitVectorPerp.y * arc)
ctx.bezierCurveTo(
X - unitVector.x * mag + unitVectorPerp.x * arc, Y - unitVector.y * mag + unitVectorPerp.y * arc,
X - unitVector.x * mag, Y - unitVector.y * mag,
X, Y)
}
ctx.fillStyle = `rgba(50,0,100,0.05)`;
const magSize = 8 * c * tech.railChargeRate ** 3
const arcSize = 6 * c * tech.railChargeRate ** 3
for (let i = 3; i < 7; i++) {
const MAG = magSize * i * i * (0.93 + 0.07 * Math.random())
const ARC = arcSize * i * i * (0.93 + 0.07 * Math.random())
ctx.beginPath();
magField(MAG, ARC)
magField(MAG, -ARC)
ctx.fill();
}
}
me.waves = [];
me.doLongitudinal = function () {
if (!m.isBodiesAsleep) {
ctx.strokeStyle = "rgba(0,0,0,0.6)" //"000";
ctx.lineWidth = 2 * tech.wavePacketDamage
ctx.beginPath();
// const end = 1100 * tech.bulletsLastLonger / Math.sqrt(tech.waveReflections * 0.5) //should equal about 1767
const end = 1100 * tech.bulletsLastLonger * Math.pow(0.93, tech.waveReflections) //should equal about 1767
const damage = 0.0005 * simulation.dmgScale//normal damage for m basically shreds m, so had to nerf this
for (let i = this.waves.length - 1; i > -1; i--) {
const v1 = Vector.add(this.waves[i].position, Vector.mult(this.waves[i].unit1, this.waves[i].radius))
const v2 = Vector.add(this.waves[i].position, Vector.mult(this.waves[i].unit2, this.waves[i].radius))
//draw wave
ctx.moveTo(v1.x, v1.y)
ctx.arc(this.waves[i].position.x, this.waves[i].position.y, this.waves[i].radius, this.waves[i].angle, this.waves[i].angle + this.waves[i].arc);
//using small angle linear approximation of circle arc, this will not work if the arc gets large // https://stackoverflow.com/questions/13652518/efficiently-find-points-inside-a-circle-sector
let hits = Matter.Query.ray([player], v1, v2, 50) //Matter.Query.ray(bodies, startPoint, endPoint, [rayWidth])
for (let j = 0, len = Math.min(30, hits.length); j < len; j++) {
player.force.x += 0.01 * (Math.random() - 0.5) * player.mass
player.force.y += (0.01 * (Math.random() - 0.5) - simulation.g * 0.25) * player.mass //remove force of gravity
Matter.Body.setVelocity(player, { //friction
x: player.velocity.x * 0.95,
y: player.velocity.y * 0.95
});
m.damage(damage)
}
hits = Matter.Query.ray(body, v1, v2, 50)
for (let j = 0, len = Math.min(30, hits.length); j < len; j++) {
const who = hits[j].body
//make them shake around
who.force.x += 0.01 * (Math.random() - 0.5) * who.mass
who.force.y += (0.01 * (Math.random() - 0.5) - simulation.g * 0.25) * who.mass //remove force of gravity
let vertices = who.vertices;
const vibe = 25
ctx.moveTo(vertices[0].x + vibe * (Math.random() - 0.5), vertices[0].y + vibe * (Math.random() - 0.5));
for (let j = 1; j < vertices.length; j++) {
ctx.lineTo(vertices[j].x + vibe * (Math.random() - 0.5), vertices[j].y + vibe * (Math.random() - 0.5));
}
ctx.lineTo(vertices[0].x + vibe * (Math.random() - 0.5), vertices[0].y + vibe * (Math.random() - 0.5));
if (tech.isPhononBlock && !who.isNotHoldable && who.speed < 5 && who.angularSpeed < 0.1) {
if (Math.random() < 0.5) b.targetedBlock(who, 50 - Math.min(25, who.mass * 3)) // targetedBlock(who, speed = 50 - Math.min(20, who.mass * 2), range = 1600) {
// Matter.Body.setAngularVelocity(who, (0.25 + 0.12 * Math.random()) * (Math.random() < 0.5 ? -1 : 1));
who.torque += who.inertia * 0.001 * (Math.random() - 0.5)
}
}
// ctx.stroke(); //draw vibes
this.waves[i].radius += tech.waveBeamSpeed * 1.8 * this.waves[i].expanding //expand / move
if (this.waves[i].radius > end - 30) {
this.waves[i].expanding = -1
if (this.waves[i].reflection < 1) this.waves.splice(i, 1) //end
} else if (this.waves[i].radius < 25) {
this.waves[i].expanding = 1
if (this.waves[i].reflection < 1) this.waves.splice(i, 1) //end
}
}
ctx.stroke();
}
}
me.lasers = function (where, angle) {
const vertexCollision = function (v1, v1End, domain) {
for (let i = 0; i < domain.length; ++i) {
let vertices = domain[i].vertices;
const len = vertices.length - 1;
for (let j = 0; j < len; j++) {
results = simulation.checkLineIntersection(v1, v1End, vertices[j], vertices[j + 1]);
if (results.onLine1 && results.onLine2) {
const dx = v1.x - results.x;
const dy = v1.y - results.y;
const dist2 = dx * dx + dy * dy;
if (dist2 < best.dist2 && (!domain[i].mob || domain[i].alive)) best = {
x: results.x,
y: results.y,
dist2: dist2,
who: domain[i],
v1: vertices[j],
v2: vertices[j + 1]
};
}
}
results = simulation.checkLineIntersection(v1, v1End, vertices[0], vertices[len]);
if (results.onLine1 && results.onLine2) {
const dx = v1.x - results.x;
const dy = v1.y - results.y;
const dist2 = dx * dx + dy * dy;
if (dist2 < best.dist2) best = {
x: results.x,
y: results.y,
dist2: dist2,
who: domain[i],
v1: vertices[0],
v2: vertices[len]
};
}
}
};
const seeRange = 7000;
best = {
x: null,
y: null,
dist2: Infinity,
who: null,
v1: null,
v2: null
};
const look = {
x: where.x + seeRange * Math.cos(angle),
y: where.y + seeRange * Math.sin(angle)
};
// vertexCollision(where, look, mob);
vertexCollision(where, look, map);
vertexCollision(where, look, body);
if (!m.isCloak) vertexCollision(where, look, [player]);
if (best.who && (best.who === player) && m.immuneCycle < m.cycle) {
const dmg = 0.0011 * simulation.dmgScale;
m.damage(dmg);
simulation.drawList.push({ //add dmg to draw queue
x: best.x,
y: best.y,
radius: dmg * 1500,
color: "rgba(80,0,255,0.5)",
time: 20
});
}
//draw beam
if (best.dist2 === Infinity) best = look;
ctx.moveTo(where.x, where.y);
ctx.lineTo(best.x, best.y);
ctx.lineWidth = 10;
ctx.stroke();
}
me.pulse = function (charge, angle, where = this.position) {
let best;
angle = Math.atan2(this.seePlayer.position.y - this.position.y, this.seePlayer.position.x - this.position.x)
let explosionRadius = 5.5 * charge
let range = 5000
const path = [{
x: where.x + 20 * Math.cos(angle),
y: where.y + 20 * Math.sin(angle)
},
{
x: where.x + range * Math.cos(angle),
y: where.y + range * Math.sin(angle)
}
];
const vertexCollision = function (v1, v1End, domain) {
for (let i = 0; i < domain.length; ++i) {
let vertices = domain[i].vertices;
const len = vertices.length - 1;
for (let j = 0; j < len; j++) {
results = simulation.checkLineIntersection(v1, v1End, vertices[j], vertices[j + 1]);
if (results.onLine1 && results.onLine2) {
const dx = v1.x - results.x;
const dy = v1.y - results.y;
const dist2 = dx * dx + dy * dy;
if (dist2 < best.dist2 && (!domain[i].mob || domain[i].alive)) best = {
x: results.x,
y: results.y,
dist2: dist2,
who: domain[i],
v1: vertices[j],
v2: vertices[j + 1]
};
}
}
results = simulation.checkLineIntersection(v1, v1End, vertices[0], vertices[len]);
if (results.onLine1 && results.onLine2) {
const dx = v1.x - results.x;
const dy = v1.y - results.y;
const dist2 = dx * dx + dy * dy;
if (dist2 < best.dist2) best = {
x: results.x,
y: results.y,
dist2: dist2,
who: domain[i],
v1: vertices[0],
v2: vertices[len]
};
}
}
};
//check for collisions
best = {
x: null,
y: null,
dist2: Infinity,
who: null,
v1: null,
v2: null
};
if (!best.who) {
vertexCollision(path[0], path[1], body);
vertexCollision(path[0], path[1], [player]);
vertexCollision(path[0], path[1], map);
if (best.dist2 != Infinity) { //if hitting something
path[path.length - 1] = {
x: best.x,
y: best.y
};
}
}
if (best.who) {
b.explosion(path[1], explosionRadius, "rgba(0,0,0,0)")
const off = explosionRadius * 1.2
b.explosion({ x: path[1].x + off * (Math.random() - 0.5), y: path[1].y + off * (Math.random() - 0.5) }, explosionRadius, "rgba(0,0,0,0.7)")
b.explosion({ x: path[1].x + off * (Math.random() - 0.5), y: path[1].y + off * (Math.random() - 0.5) }, explosionRadius, "rgba(0,0,0,0.7)")
}
//draw laser beam
ctx.beginPath();
ctx.moveTo(path[0].x, path[0].y);
ctx.lineTo(path[1].x, path[1].y);
if (charge > 50) {
ctx.strokeStyle = "rgba(0,0,0,0.10)"
ctx.lineWidth = 70
ctx.stroke();
}
ctx.strokeStyle = "rgba(0,0,0,0.25)"
ctx.lineWidth = 20
ctx.stroke();
ctx.strokeStyle = "#f00";
ctx.lineWidth = 4
ctx.stroke();
//draw little dots along the laser path
const sub = Vector.sub(path[1], path[0])
const mag = Vector.magnitude(sub)
for (let i = 0, len = Math.floor(mag * 0.0005 * charge); i < len; i++) {
const dist = Math.random()
simulation.drawList.push({
x: path[0].x + sub.x * dist + 10 * (Math.random() - 0.5),
y: path[0].y + sub.y * dist + 10 * (Math.random() - 0.5),
radius: 1.5 + 5 * Math.random(),
color: "rgba(0,0,0,0.5)",
time: Math.floor(9 + 25 * Math.random() * Math.random())
});
}
}
let c = 0
me.gun = function () {
if (b.activeGun == 0) {// nailgun
if (this.seePlayer.recall && !(simulation.cycle % 20)) {
this.fireDir = Vector.normalise(Vector.sub(this.seePlayer.position, this.position));
normalBullet(this.position.x, this.position.y);
const v = 10 + 8 * simulation.accelScale;
Matter.Body.setVelocity(mob[mob.length - 1], {
x: this.velocity.x + this.fireDir.x * v,
y: this.velocity.y + this.fireDir.y * v
});
}
}
if (b.activeGun == 1) {// shotgun
if (this.seePlayer.recall && !(simulation.cycle % 90)) {
const side = 22
for (let i = 0; i < 12; i++) {
const angle = Math.atan2(this.seePlayer.position.y - this.position.y, this.seePlayer.position.x - this.position.x);
const dir = angle + (Math.random() - 0.5) * 1
const SPEED = 52 + Math.random() * 8
normalBullet(this.position.x + 35 * Math.cos(angle) + 15 * (Math.random() - 0.5), this.position.y + 35 * Math.sin(angle) + 15 * (Math.random() - 0.5))
Matter.Body.setVelocity(mob[mob.length - 1], {
x: SPEED * Math.cos(dir),
y: SPEED * Math.sin(dir)
});
}
}
} else if (b.activeGun == 2) { // super balls
if (this.seePlayer.recall && !(simulation.cycle % 20)) {
const num = 3;
const SPREAD = 0.13;
const angle = Math.atan2(this.seePlayer.position.y - this.position.y, this.seePlayer.position.x - this.position.x);
let dir = angle - SPREAD * (num - 1) / 2;
const SPEED = 33
for (let i = 0; i < num; i++) {
ball(this.position.x + 30 * Math.cos(angle), this.position.y + 30 * Math.sin(angle))
Matter.Body.setVelocity(mob[mob.length - 1], {
x: SPEED * Math.cos(dir),
y: SPEED * Math.sin(dir)
});
dir += SPREAD
}
}
} else if (b.activeGun == 3) { // wave
this.doLongitudinal()
const halfArc = 0.275
const anglex = Math.atan2(this.seePlayer.position.y - this.position.y, this.seePlayer.position.x - this.position.x);
const angle = anglex + 0.3 * (Math.random() - 0.5)
this.waves.push({
position: {
x: this.position.x + 25 * Math.cos(anglex),
y: this.position.y + 25 * Math.sin(anglex),
},
angle: angle - halfArc, //used in drawing ctx.arc
unit1: { x: Math.cos(angle - halfArc), y: Math.sin(angle - halfArc) }, //used for collision
unit2: { x: Math.cos(angle + halfArc), y: Math.sin(angle + halfArc) }, //used for collision
arc: halfArc * 2,
radius: 25,
reflection: 0,
expanding: 1,
resonanceCount: 0
})
} else if (b.activeGun == 4) { // missiles
} else if (b.activeGun == 5) { // grenades
} else if (b.activeGun == 6) { // spores
if (this.seePlayer.recall && !(simulation.cycle % 30)) {
this.fireDir = Vector.normalise(Vector.sub(this.seePlayer.position, this.position));
me.drop(this.position.x, this.position.y)
const v = 10 + 8 * simulation.accelScale + (Math.random() * 20 - Math.random() * 20);
Matter.Body.setVelocity(mob[mob.length - 1], {
x: this.velocity.x + this.fireDir.x * v,
y: this.velocity.y + this.fireDir.y * v
});
}
} else if (b.activeGun == 7) { // drones
} else if (b.activeGun == 8) { // foam
if (this.seePlayer.recall && !(simulation.cycle % 1)) {
this.fireDir = Vector.normalise(Vector.sub(this.seePlayer.position, this.position));
foamBullet(this.position.x, this.position.y, 7 + Math.ceil(this.radius / 15), 69);
const v = 10 + 8 * simulation.accelScale + (Math.random() * 20 - Math.random() * 20);
Matter.Body.setVelocity(mob[mob.length - 1], {
x: this.velocity.x + this.fireDir.x * v,
y: this.velocity.y + this.fireDir.y * v
});
}
} else if (b.activeGun == 9) { // harpoon - railgun
if (c > 1) {
this.fireDir = Vector.normalise(Vector.sub(this.seePlayer.position, this.position));
this.railBullet(this.position.x, this.position.y);
const v = 10 + 80 * simulation.accelScale;
Matter.Body.setVelocity(mob[mob.length - 1], {
x: this.velocity.x + this.fireDir.x * v,
y: this.velocity.y + this.fireDir.y * v
});
c = 0;
} else {
c += 0.02;
this.railGun();
}
} else if (b.activeGun == 10) { // laserMines
if (this.seePlayer.recall && !(simulation.cycle % 100)) {
this.fireDir = Vector.normalise(Vector.sub(this.seePlayer.position, this.position));
me.laserMine(this.position.x, this.position.y)
const v = 10 + 8 * simulation.accelScale + (Math.random() * 20 - Math.random() * 20);
Matter.Body.setVelocity(mob[mob.length - 1], {
x: this.velocity.x + this.fireDir.x * v,
y: this.velocity.y + this.fireDir.y * v
});
}
} else if (b.activeGun == 11) { // laser - pulse
//this.lasers(this.position, Math.atan2(this.seePlayer.position.y - this.position.y, this.seePlayer.position.x - this.position.x))
//if (this.seePlayer.recall && !(simulation.cycle % 20)) {
if (c > 1) {
this.pulse(c * 100)
c = 0;
} else {
if (this.energy < 1 || this.energy > 0.5) {
c += 0.01;
ctx.beginPath();
const mag = Math.sqrt(c)
ctx.arc(this.position.x, this.position.y, c * 30, 0, 2 * Math.PI)
ctx.fillStyle = '#000000'
ctx.strokeStyle = 'transparent'
ctx.fill();
ctx.stroke();
this.energy -= 0.01;
ctx.strokeStyle = "#000000";
ctx.lineWidth = 1.5
// ctx.globalAlpha = 1;
} else {
c = 0;
this.energy += 0.1
}
}
//}
}
}
me.railBullet = function (x, y) {
mobs.spawn(x, y, 5, 20, "black");
let xy = mob[mob.length - 1];
xy.stroke = "black";
xy.vertices = Matter.Vertices.rotate(xy.vertices, Math.PI, xy.position);
xy.onHit = function () {
simulation.drawList.push({ //add dmg to draw queue
x: this.position.x,
y: this.position.y,
radius: 20,
color: simulation.mobDmgColor,
time: simulation.drawTime
});
};
Matter.Body.setDensity(xy, 0.00004); //normal is 0.001
xy.timeLeft = 220;
xy.frictionAir = -0.01;
xy.restitution = -1;
xy.leaveBody = false;
xy.isDropPowerUp = false;
//xy.inertia = Infinity;
xy.isBadTarget = true;
xy.isMobBullet = true;
xy.showHealthBar = false;
xy.collisionFilter.category = cat.mobBullet;
xy.collisionFilter.mask = cat.player;
let index = 0;
xy.do = function () {
this.timeLimit();
this.alwaysSeePlayer()
const setNoseShape = () => {
const mag = this.radius + this.radius * 10;
const angle = Math.atan2(me.seePlayer.position.y - this.position.y, me.seePlayer.position.x - this.position.x);
this.vertices[2].x = this.position.x + Math.cos(this.angle) * mag;
this.vertices[2].y = this.position.y + Math.sin(this.angle) * mag;
this.vertices[4].x = this.position.x + Math.cos(this.angle) * mag;
this.vertices[4].y = this.position.y + Math.sin(this.angle) * mag;
this.vertices[0].x = this.position.x + Math.cos(this.angle) * mag;
this.vertices[0].y = this.position.y + Math.sin(this.angle) * mag;
Matter.Body.setAngle(this, angle);
};
const spike = Vector.mult(Vector.normalise(Vector.sub(this.vertices[2], this.position)), radius * 1000)
const spike2 = Vector.mult(Vector.normalise(Vector.sub(this.vertices[4], this.position)), radius * 1000)
const spike3 = Vector.mult(Vector.normalise(Vector.sub(this.vertices[0], this.position)), radius * 1000)
this.vertices[2].x = this.position.x + spike.x / 100
this.vertices[2].y = this.position.y + spike.y / 100
this.vertices[4].x = this.position.x + spike2.x / 75
this.vertices[4].y = this.position.y + spike2.y / 75
this.vertices[0].x = this.position.x + spike3.x / 75
this.vertices[0].y = this.position.y + spike3.y / 75
if (index == 0) {
setNoseShape();
index++;
}
if (Matter.Query.collides(this, map).length > 0 || Matter.Query.collides(this, body).length > 0) {
const slow = 0.69 //im sorry it looks cool though
Matter.Body.setVelocity(this, {
x: this.velocity.x * slow,
y: this.velocity.y * slow
});
// simulation.drawList.push({ //add dmg to draw queue
// x: this.position.x,
// y: this.position.y,
// radius: 10,
// color: '#000000',
// time: simulation.drawTime
// });
if (this.velocity.x == 0 && this.velocity.y == 0) {
this.death();
}
this.frictionAir += 0.0001;
Matter.Body.setAngularVelocity(this, 0)
}
if (Matter.Query.collides(this, [player]).length > 0 && !(m.isCloak && tech.isIntangible) && m.immuneCycle < m.cycle) {
m.immuneCycle = m.cycle + m.collisionImmuneCycles; //player is immune to damage for 30 cycles // I wasnt gonna add this but since ya'll would have killed me if I didn't I added this
const dmg = 0.013 * simulation.dmgScale;
m.damage(dmg);
// simulation.drawList.push({ //add dmg to draw queue
// x: this.position.x,
// y: this.position.y,
// radius: Math.sqrt(dmg) * 200,
// color: '#000000',
// time: simulation.drawTime
// });
}
};
}
me.laserMine = function (x, y) {
mobs.spawn(x, y, 3, 20, "#000000");
let xx = mob[mob.length - 1];
xx.stroke = "#00000000";
Matter.Body.setDensity(xx, 0.000005) //one tap
xx.isUnstable = true;
xx.timeLeft = 40 + Math.floor(180 * Math.random())
xx.leaveBody = false;
xx.isDropPowerUp = false;
xx.collisionFilter.mask = cat.bullet | cat.player
xx.showHealthBar = false;
//xx.vertices = Matter.Vertices.rotate(xx.vertices, Math.PI, xx.position);
me.onHit = function () {
this.death();
};
xx.do = function () {
this.timeLimit();
Matter.Body.setAngularVelocity(this, 0.01)
ctx.beginPath();
ctx.lineWidth = 1;
ctx.strokeStyle = "#00000000"
for (let i = 0; i < this.vertices.length; i++) {
const where = this.vertices[i]
const endPoint = Vector.add(where, Vector.mult(Vector.normalise(Vector.sub(where, this.position)), 2500))
me.lasers(this.vertices[0], this.angle + Math.PI / 3);
me.lasers(this.vertices[1], this.angle + Math.PI);
me.lasers(this.vertices[2], this.angle - Math.PI / 3);
}
ctx.strokeStyle = randomColor({
hue: "#FF00FF"
});
ctx.stroke();
ctx.save()
ctx.beginPath();
ctx.moveTo(this.vertices[0].x, this.vertices[0].y);
ctx.lineTo(this.vertices[1].x, this.vertices[1].y);
ctx.lineTo(this.vertices[2].x, this.vertices[2].y);
ctx.fillStyle = "#000000";
ctx.strokeStyle = "transparent";
ctx.fill();
ctx.closePath();
ctx.stroke();
ctx.restore()
}
}
me.seeker = function (x, y) {
mobs.spawn(x, y, sides = 5, radius = 5, "rgb(0,0,0)");
let yy = mob[mob.length - 1];
yy.stroke = "transparent";
yy.onHit = function () {
this.explode(this.mass * 20);
};
Matter.Body.setDensity(yy, 0.000015); //normal is 0.001
yy.timeLeft = 420 //* (0.8 + 0.4 * Math.random());
yy.accelMag = 0.00017 * simulation.accelScale; //* (0.8 + 0.4 * Math.random())
yy.frictionAir = 0.01 //* (0.8 + 0.4 * Math.random());
yy.restitution = 0.5;
yy.leaveBody = false;
yy.isDropPowerUp = false;
yy.isBadTarget = true;
yy.isMobBullet = true;
yy.showHealthBar = false;
yy.collisionFilter.category = cat.mobBullet;
yy.collisionFilter.mask = cat.player | cat.map | cat.body | cat.bullet;
let index = 0;
yy.do = function () {
this.alwaysSeePlayer()
this.timeLimit();
this.attraction();
};
}
me.drop = function (x, y) {
mobs.spawn(x, y, sides = 90, radius = 30, "rgb(0,255,100,0.7)");
let yyy = mob[mob.length - 1];
yyy.stroke = "transparent";
yyy.onDeath = function () {
for (let i = 0, len = 5; i < len; i++) {
me.seeker(this.position.x, this.position.y)
Matter.Body.setVelocity(mob[mob.length - 1], {
x: Math.random() * 30 - Math.random() * 30,
y: Math.random() * 30 - Math.random() * 30
});
}
};
Matter.Body.setDensity(yyy, 0.000015); //normal is 0.001
yyy.timeLeft = 60 //* (0.8 + 0.4 * Math.random());
yyy.frictionAir = 0.01 //* (0.8 + 0.4 * Math.random());
yyy.restitution = 0.5;
yyy.leaveBody = false;
yyy.isDropPowerUp = false;
yyy.isBadTarget = true;
yyy.isMobBullet = true;
yyy.showHealthBar = false;
yyy.collisionFilter.category = cat.mobBullet;
yyy.collisionFilter.mask = null;
yyy.maxRadius = 30;
let index = 0;
yyy.do = function () {
if (Matter.Query.collides(this, [player]).length > 0 && !(m.isCloak && tech.isIntangible) && m.immuneCycle < m.cycle) {
Matter.Body.setPosition(this, player.position)
if (player.speed > 2.5) Matter.Body.setVelocity(player, Vector.mult(player.velocity, 0.94))
}
this.alwaysSeePlayer()
this.timeLimit();
ctx.save()
ctx.beginPath();
ctx.moveTo(this.position.x, this.position.y)
ctx.fillStyle = "black";
ctx.arc(this.position.x, this.position.y, this.maxRadius, 0, 2 * Math.PI)
ctx.stroke()
ctx.fill()
ctx.restore()
if (this.maxRadius > 0) {
this.maxRadius -= 0.5;
}
};
}
};
restoreBoss(-13350, -1800);
laserEM(-6500 + Math.floor(Math.random() * 200) - Math.floor(Math.random() * 200), -3400 + Math.floor(Math.random() * 200) - Math.floor(Math.random() * 200));
sniper(-9275 + Math.floor(Math.random() * 200) - Math.floor(Math.random() * 200), -3325 + Math.floor(Math.random() * 200) - Math.floor(Math.random() * 200));
laserEM(-5750 + Math.floor(Math.random() * 200) - Math.floor(Math.random() * 200), -850 + Math.floor(Math.random() * 200) - Math.floor(Math.random() * 200));
sniper(-3600 + Math.floor(Math.random() * 200) - Math.floor(Math.random() * 200), -1325 + Math.floor(Math.random() * 200) - Math.floor(Math.random() * 200));
laserEM(1425 + Math.floor(Math.random() * 200) - Math.floor(Math.random() * 200), -800 + Math.floor(Math.random() * 200) - Math.floor(Math.random() * 200));
//restoreBoss(-350, -3225);
wire();
wire();
wire();
wire();
wire();
color.map = '#00000000';
level.customTopLayer = () => {
if (dong.position.x > -3825) {
dong.force.y -= dong.mass * simulation.g;
} else {
dong.force.y += dong.mass * simulation.g;
}
Matter.Body.setAngularVelocity(dong, -0.5)
if (destroyed == false) {
door.isClosing = false;
} else {
door.isClosing = true;
}
door.openClose();
door.draw()
for (let i = 0, len = map.length; i < len; i++) { //so boss bar renders over the map
// ctx.beginPath()
// //ctx.setLineDash([125 * Math.random(), 125 * Math.random()]);
// ctx.fillRect(map[i].vertices[0].x , map[i].vertices[0].y, Math.abs(map[i].vertices[0].x - map[i].vertices[1].x), Math.abs(map[i].vertices[1].y - map[i].vertices[2].y))
// //ctx.strokeRect(map[i].vertices[0].x , map[i].vertices[0].y, Math.abs(map[i].vertices[0].x - map[i].vertices[1].x), Math.abs(map[i].vertices[1].y - map[i].vertices[2].y))
// ctx.fillStyle = "rgba(68,68,68)"
// //ctx.strokeStyle = "transparent"
// ctx.stroke()
// ctx.fill()
ctx.beginPath();
ctx.moveTo(map[i].vertices[0].x, map[i].vertices[0].y);
for (let j = 0, length = map[i].vertices.length; j < length; j++) {
ctx.lineTo(map[i].vertices[j].x, map[i].vertices[j].y);
}
ctx.lineTo(map[i].vertices[0].x, map[i].vertices[0].y);
ctx.fillStyle = "rgba(68,68,68)";
ctx.strokeStyle = "transparent";
ctx.fill();
ctx.stroke();
// ctx.setLineDash([]);
}
for (let i = 0, len = mob.length; i < len; i++) {
if (mob[i].restoreBoss) {
ctx.save();
ctx.setTransform(1, 0, 0.5, 1, 0, 0); //slanted
ctx.fillStyle = "rgba(100, 100, 100, 0.3)";
ctx.fillRect(canvas.width2 / 2, canvas.height2 / 10, canvas.width2, 30);
ctx.fillStyle = "rgba(0,0,0,0.7)";
ctx.fillRect(canvas.width2 / 2, canvas.height2 / 10, canvas.width2 * mob[i].health, 30);
ctx.restore();
}
}
};
simulation.enableConstructMode() //landgreen if you see this can you remove im probably gonna forget
for (let i = 0; i < spawn.bossTypeSpawnOrder.length * Math.random(); i++) {
spawn.bossTypeSpawnOrder.splice(i * Math.floor(Math.random() * spawn.bossTypeSpawnOrder.length), 1, "restoreBoss") //meh good enough
}
const obj = { restoreBoss };
Object.assign(spawn, obj); //ez
},
superNgonBros() {
simulation.makeTextLog(`<strong>Super N-gon Bros</strong> by <span class='color-var'>DesBoot</span>`);
let bowserKilled = 0
let flagY = -750
let flagReached = 0
const elevator1 = level.elevator(3975, -11650, 450, 50, -13100, 0.003)
const elevator2 = level.elevator(5575, -11650, 450, 50, -13100, 0.003)
let firstElevatorY = -11650
const portal = level.portal({ x: 3990, y: 100 }, 1.5 * Math.PI, { x: 100, y: -13500 }, -1.5 * Math.PI)
const portal2 = level.portal({ x: 7135, y: -12270 }, -1 * Math.PI, { x: 12325, y: -2000 }, -1.5 * Math.PI)
const bowser = function (x, y, radius = 150) { //define the mob the same as spawn mob code
mobs.spawn(x, y, 5, radius, "rgb(0,200,180)");
let me = mob[mob.length - 1];
me.accelMag = 0.05;
me.g = 0.002; //required if using this.gravity
me.frictionAir = 0.01;
me.friction = 1
me.frictionStatic = 1
me.restitution = 0;
me.delay = 80 * simulation.CDScale;
me.randomHopFrequency = 200 + Math.floor(Math.random() * 150);
me.randomHopCD = simulation.cycle + me.randomHopFrequency;
Matter.Body.rotate(me, Math.random() * Math.PI);
spawn.shield(me, x, y);
me.do = function () {
//spawn.grenade(me.position.x, me.position.y);
// //const v = 5 * simulation.accelScale;
// Matter.Body.setVelocity(mob[mob.length - 1], {
// x: this.velocity.x + this.fireDir.x * v + Math.random(),
// y: this.velocity.y + this.fireDir.y * v + Math.random()
// });
this.gravity();
this.seePlayerCheck();
this.checkStatus();
if (this.seePlayer.recall) {
if (this.cd < simulation.cycle && (Matter.Query.collides(this, map).length || Matter.Query.collides(this, body).length)) {
this.cd = simulation.cycle + this.delay;
const forceMag = (this.accelMag + this.accelMag * Math.random()) * this.mass;
const angle = Math.atan2(this.seePlayer.position.y - this.position.y, this.seePlayer.position.x - this.position.x);
this.force.x += forceMag * Math.cos(angle) * 0.5;
this.force.y += (forceMag * Math.sin(angle) - (Math.random() * 0.07 + 0.1) * this.mass) * 0.7; //antigravity
if (Math.random() < 0.5) {
spawn.grenade(me.position.x, me.position.y - 250 * Math.random(), 500);
Matter.Body.setVelocity(mob[mob.length - 1], {
x: -5,
y: 0
});
} else {
spawn.bullet(this.position.x, this.position.y, 25);
Matter.Body.setVelocity(mob[mob.length - 1], {
x: -25,
y: -25
});
}
}
} else {
//randomly hob if not aware of player
if (this.randomHopCD < simulation.cycle && (Matter.Query.collides(this, map).length || Matter.Query.collides(this, body).length)) {
this.randomHopCD = simulation.cycle + this.randomHopFrequency;
//slowly change randomHopFrequency after each hop
this.randomHopFrequency = Math.max(100, this.randomHopFrequency + (0.5 - Math.random()) * 200);
const forceMag = (this.accelMag + this.accelMag * Math.random()) * this.mass * (0.1 + Math.random() * 0.3);
const angle = -Math.PI / 2 + (Math.random() - 0.5) * Math.PI;
this.force.x += forceMag * Math.cos(angle);
this.force.y += forceMag * Math.sin(angle) - 0.07 * this.mass; //antigravity
spawn.grenade(me.position.x, me.position.y - 250 * Math.random(), 500);
Matter.Body.setVelocity(mob[mob.length - 1], {
x: -5,
y: 0
});
}
}
};
me.onDeath = function () {
powerUps.spawnBossPowerUp(this.position.x, this.position.y)
bowserKilled = 1
}
}
bowser(20500, -400) //call the mob
//fire(15300, -200)
const brick = function (x, y, angle = Math.PI * 0.5, radius = 53) {//credit to Richard0820 for the code
mobs.spawn(x, y, 4, radius, "#ab6101");
let me = mob[mob.length - 1];
me.stroke = "transparent";
me.isDropPowerUp = false;
me.showHealthBar = false;
Matter.Body.setDensity(me, 999999)
me.collisionFilter.mask = cat.player | cat.mob | cat.bullet;
me.constraint = Constraint.create({
pointA: {
x: me.position.x,
y: me.position.y
},
bodyB: me,
stiffness: 0,
damping: 0
});
me.do = function () {
this.isStunned = true;
if (this.health < 1) {
this.health += 0.001; //regen
}
this.checkStatus();
Matter.Body.setAngle(me, angle);
if ((player.velocity.y < 0 && player.position.y > me.position.y || player.velocity.y > 30 && player.position.y < me.position.y) && Math.abs(player.position.x - me.position.x) < 50 && Math.abs(player.position.y - me.position.y) < 150) {
me.death()
}
};
me.onHit = function () {
if (player.velocity.y < 0 && player.position.y > me.position.y || player.velocity.y > 30 && player.position.y < me.position.y) {
me.death()
}
}
me.onDeath = function () {
if (Math.random() < 0.1) {
spawn.randomSmallMob(me.position.x, me.position.y - 75);
simulation.makeTextLog('mob')
} else {
if (Math.random() < 0.07) {
powerUps.spawn(me.position.x, me.position.y + (75 * (player.velocity.y / Math.abs(player.velocity.y))), "tech", true);
simulation.makeTextLog('tech')
} else {
if (Math.random() < 0.4) {
powerUps.spawn(me.position.x, me.position.y + (75 * (player.velocity.y / Math.abs(player.velocity.y))), "heal", true);
simulation.makeTextLog('heal')
} else {
//if (Math.random() < 0.8){
powerUps.spawn(me.position.x, me.position.y + (75 * (player.velocity.y / Math.abs(player.velocity.y))), "ammo", true);
simulation.makeTextLog('ammo')
//}
}
}
}
}
Composite.add(engine.world, me.constraint);
}
simulation.enableConstructMode()
let firstMobsSpawned = 1
let secondMobsSpawned = 0
let thirdMobsSpawned = 0
let fourthMobsSpawned = 0
let firstMobsReached = 0
let secondMobsReached = 0
let thirdMobsReached = 0
let fourthMobsReached = 0
let finalRoomReached = 0
let undergroundMobsSpawned = 0
let undergroundMobsReached = 0
level.setPosToSpawn(0, -50); //normal spawn
level.exit.x = 22100;
level.exit.y = -40;
spawn.mapRect(level.enter.x, level.enter.y + 20, 100, 20); //bump for level entrance
spawn.mapRect(level.exit.x, level.exit.y + 20, 100, 20); //bump for level exit
level.defaultZoom = 1800
simulation.zoomTransition(level.defaultZoom)
document.body.style.backgroundColor = "#a2a5ff";
// color.map = "#444" //custom map color
level.custom = () => {
if (player.position.x > 14950 && flagReached == 0) {
flagReached = 1
}
if (flagReached == 1 && flagY < -150) {
flagY += 5
}
ctx.fillStyle = "rgba(64,64,64,0.97)"
ctx.fillRect(4200, -13100, 2, 1450)
ctx.fillRect(5800, -13100, 2, 1450)
if (firstElevatorY < -12099) {
firstElevatorY = -11650
} else {
firstElevatorY -= 5
}
//simulation.makeTextLog(firstElevatorY)
elevator1.move();
elevator2.move();
if (player.position.x > 0 && player.position.y < -9000 && player.position.y > -10000) {
//m.death()
m.damage(0.05 * simulation.difficultyMode)
Matter.Body.setPosition(player, {
x: 275,
y: -12175
});
}
portal[2].query()
portal[3].query()
portal2[2].query()
portal2[3].query()
//simulation.makeTextLog(firstBlockBroken)
level.exit.drawAndCheck();
if (player.position.x > 4100 && secondMobsReached == 0) {
secondMobsSpawned = 1
}
if (player.position.x > 7000 && thirdMobsReached == 0) {
thirdMobsSpawned = 1
}
if (player.position.x > 14300 && fourthMobsReached == 0) {
fourthMobsSpawned = 1
}
if (player.position.y < -11000 && undergroundMobsReached == 0) {
undergroundMobsSpawned = 1
}//player.position.x > 14300 &&
if (m.onGround) {
if (Math.abs(player.velocity.x) > 0.3) {
Matter.Body.setVelocity(player, {
x: player.velocity.x + (0.1 * (Math.abs(player.velocity.x) / player.velocity.x)),
y: player.velocity.y + 0.2
});
} else {
Matter.Body.setVelocity(player, {
x: player.velocity.x,
y: player.velocity.y + 0.2
});
}
} else {
if (input.down) {
Matter.Body.setVelocity(player, {
x: player.velocity.x,
y: player.velocity.y + 2
});
} else {
Matter.Body.setVelocity(player, {
x: player.velocity.x,
y: player.velocity.y + 0.2
});
}
}
level.enter.draw();
if (finalRoomReached == 0 && player.position.x > 21150) {
finalRoomReached = 1
simulation.makeTextLog('Thank you M, but our techs are in another castle!')
}
//mobs
if (firstMobsSpawned == 1 && firstMobsReached == 0) {
spawn.randomSmallMob(1260, -75);
spawn.randomMob(2100, -75, 0.4);
spawn.randomSmallMob(2400, -75);
spawn.randomSmallMob(2500, -75);
spawn.randomMob(2900, -75, 0.2);
spawn.randomMob(3400, -75, 0.4);
spawn.randomMob(3400, -75, 0.4);
firstMobsReached = 1
}
if (secondMobsSpawned == 1 && secondMobsReached == 0) {
spawn.randomSmallMob(4400, -75);
spawn.randomSmallMob(5500, -75);
spawn.randomSmallMob(5835.6, -402.4);
spawn.randomSmallMob(5835.6, -402.4);
spawn.randomSmallMob(6543.2, -730.0);
spawn.randomMob(6795.4, -162.4, 0.1);
spawn.randomMob(6795.4, -162.4, 0.1);
secondMobsReached = 1
}
if (thirdMobsSpawned == 1 && thirdMobsReached == 0) {
spawn.randomMob(8465.6, -469.9, 0.1);
spawn.randomMob(9839.6, -444.5, 0.4);
spawn.randomSmallMob(11033.2, -155.3);
spawn.randomMob(12161.3, -85.1, 0.3);
spawn.randomMob(12161.3, -85.1, 0.3);
spawn.randomMob(13399.8, -93.4, 0.4);
thirdMobsReached = 1
}
if (fourthMobsSpawned == 1 && fourthMobsReached == 0) {
spawn.randomSmallMob(16500, -400);
spawn.randomSmallMob(19278.9, -211.1);
spawn.randomMob(18839.0, -463.2, 0.3);
spawn.randomMob(18036.9, -205.9, 0.3);
spawn.randomMob(16950.4, -365.2, 0.4);
spawn.randomMob(16355.6, -390.8, 0.1);
fourthMobsReached = 1
}
if (undergroundMobsSpawned == 1 && undergroundMobsReached == 0) {
spawn.randomSmallMob(1140.0, -12228.0);
spawn.randomSmallMob(2429.9, -12371.2);
spawn.randomSmallMob(4899.4, -12139.6);
spawn.randomMob(18839.0, -463.2, 0.3);
spawn.randomMob(2844.5, -12281.0, 0.2);
spawn.randomMob(4967.5, -12550.8, 0.4);
spawn.randomMob(6696.9, -12437.9, 0.1);
undergroundMobsReached = 1
}
portal[0].draw();
portal[1].draw();
portal[2].draw();
portal[3].draw();
portal2[0].draw();
portal2[1].draw();
portal2[2].draw();
portal2[3].draw();
};
level.customTopLayer = () => {
//spawn.mapRect(886, firstElevatorY + 10000, 75, 5);
ctx.fillStyle = "rgba(64,64,64,0.97)"
ctx.fillRect(3928, -300, 120, 500)
//6940, -12360, 200, 5
ctx.fillRect(6940, -12350, 170, 120)
ctx.fillRect(7090, -12380, 120, 200)
ctx.fillRect(14980, -750, 10, 750)//flagpole
ctx.beginPath()
ctx.moveTo(14980, flagY)
ctx.lineTo(14905, flagY)
ctx.lineTo(14980, flagY + 75)
ctx.fill()
};
brick(923.5, -262);
spawn.mapRect(886, -304, 75, 5);
spawn.mapRect(886, -229, 75, 5);
spawn.mapRect(883, -304, 5, 80);
spawn.mapRect(958, -304, 5, 80);
brick(1250.5, -262);
spawn.mapRect(1138, -304, 375, 5);
spawn.mapRect(1138, -229, 375, 5);
brick(1400.5, -262);
brick(1325.5, -562);
spawn.mapRect(1288, -604, 75, 5);
spawn.mapRect(1288, -529, 75, 5);
spawn.mapRect(1285, -604, 5, 80);
spawn.mapRect(1360, -604, 5, 80);
brick(5787.5, -262);
spawn.mapRect(5675, -304, 225, 5);
spawn.mapRect(5675, -229, 225, 5);
brick(6987.5, -562);
spawn.mapRect(6725, -604, 300, 5);
spawn.mapRect(6725, -529, 300, 5);
spawn.mapRect(7025, -604, 5, 80);
brick(7887.5, -262);//4 separated blocks in the middle
spawn.mapRect(7850, -304, 75, 5);
spawn.mapRect(7850, -225, 75, 5);
spawn.mapRect(7850, -304, 5, 80);
spawn.mapRect(7925, -304, 5, 84);
brick(8112.5, -262);
spawn.mapRect(8075, -304, 75, 5);
spawn.mapRect(8075, -225, 75, 5);
spawn.mapRect(8075, -304, 5, 80);
spawn.mapRect(8150, -304, 5, 84);
brick(8337.5, -262);
spawn.mapRect(8300, -304, 75, 5);
spawn.mapRect(8300, -225, 75, 5);
spawn.mapRect(8300, -304, 5, 80);
spawn.mapRect(8375, -304, 5, 84);
brick(8112.5, -562);
spawn.mapRect(8075, -604, 75, 5);
spawn.mapRect(8075, -525, 75, 5);
spawn.mapRect(8075, -604, 5, 80);
spawn.mapRect(8150, -604, 5, 84);
brick(9612.5, -562);
spawn.mapRect(9500, -604, 300, 5);
spawn.mapRect(9500, -525, 300, 5);
spawn.mapRect(9647.5, -600, 5, 75);
brick(9687.5, -562);
brick(12887.5, -262);
spawn.mapRect(12700, -304, 300, 5);
spawn.mapRect(12700, -225, 300, 5);
brick(5212.5, -12337.5);
spawn.mapRect(4725, -12377, 525, 5);
spawn.mapRect(4725, -12303, 525, 5);
spawn.mapRect(5250, -12377, 5, 79);
spawn.mapRect(-100, 0, 4033, 2000);
spawn.mapRect(4043, 0, 882, 2000);
spawn.mapRect(3909.5, 203.6, 150, 2000);
spawn.mapRect(1138, -300, 75, 75);
spawn.mapRect(1288, -300, 75, 75);
spawn.mapRect(1438, -300, 75, 75);
spawn.mapRect(1738, -150, 150, 75);//pipe 1
spawn.mapRect(1753, -150, 120, 150);
spawn.mapRect(2488, -225, 150, 75);//pipe 2
spawn.mapRect(2503, -225, 120, 225);
spawn.mapRect(3088, -300, 150, 75);//pipe 3
spawn.mapRect(3103, -300, 120, 300);
spawn.mapRect(3913, -300, 20, 75);//pipe 4
spawn.mapRect(3928, -300, 5, 300);
spawn.mapRect(4043, -300, 20, 75);
spawn.mapRect(4043, -300, 5, 300);
spawn.mapRect(5225, 0, 1125, 2000);
spawn.mapRect(6575, 0, 4900, 2000);
spawn.mapRect(5675, -300, 75, 75);
spawn.mapRect(5825, -300, 75, 75);
spawn.mapRect(5900, -600, 600, 75);
spawn.mapRect(6725, -600, 225, 75);
spawn.mapRect(6950, -300, 75, 75);
spawn.mapRect(7400, -300, 150, 75);
spawn.mapRect(8750, -300, 75, 75);
spawn.mapRect(8975, -600, 225, 75);//raised platform
spawn.mapRect(9575, -300, 150, 75);
spawn.mapRect(9500, -600, 75, 75);//upper block with double bricks
spawn.mapRect(9725, -600, 75, 75);
spawn.mapRect(9950, -75, 300, 75);//staircase
spawn.mapRect(10025, -150, 225, 75);
spawn.mapRect(10100, -225, 150, 75);
spawn.mapRect(10175, -300, 75, 75);
spawn.mapRect(10475, -75, 300, 75);
spawn.mapRect(10475, -150, 225, 75);
spawn.mapRect(10475, -225, 150, 75);
spawn.mapRect(10475, -300, 75, 75);
spawn.mapRect(11100, -75, 375, 75);//staircase 2
spawn.mapRect(11175, -150, 300, 75);
spawn.mapRect(11250, -225, 225, 75);
spawn.mapRect(11325, -300, 150, 75);
spawn.mapRect(11725, -75, 300, 75);
spawn.mapRect(11725, -150, 225, 75);
spawn.mapRect(11725, -225, 150, 75);
spawn.mapRect(11725, -300, 75, 75);
spawn.mapRect(11725, 0, 5975, 2000);//platform after the staircase
spawn.mapRect(12325, -150, 150, 75);//exit pipe
spawn.mapRect(12340, -150, 120, 300);
spawn.mapRect(12700, -300, 150, 75);
spawn.mapRect(12925, -300, 75, 75);
spawn.mapRect(13525, -150, 150, 72);//final pipe
spawn.mapRect(13540, -150, 120, 150);
spawn.mapRect(13675, -75, 675, 75);//final staircase
spawn.mapRect(13750, -150, 600, 75);
spawn.mapRect(13825, -225, 525, 75);
spawn.mapRect(13900, -300, 450, 75);
spawn.mapRect(13975, -375, 375, 75);
spawn.mapRect(14050, -450, 300, 75);
spawn.mapRect(14125, -525, 225, 75);
spawn.mapRect(14200, -600, 150, 75);
//flag
spawn.mapRect(14950, -75, 75, 75);
spawn.mapRect(1750, -4600, 500, 25);//loss
spawn.mapRect(2000, -4850, 25, 500);
spawn.mapRect(1800, -4800, 25, 150);
spawn.mapRect(2075, -4800, 25, 150);
spawn.mapRect(2150, -4775, 25, 125);
spawn.mapRect(1875, -4550, 25, 150);
spawn.mapRect(1800, -4550, 25, 150);
spawn.mapRect(2075, -4550, 25, 150);
spawn.mapRect(2123, -4430, 100, 25);
// spawn.mapRect(-250, -600, 500, 25);
// spawn.mapRect(-250, -600, 500, 25);
//underground area
spawn.mapRect(0, -12000, 2025, 2000);
spawn.mapRect(2325, -12225, 150, 2000);
spawn.mapRect(2775, -12000, 900, 2000);
spawn.mapRect(3525, -12300, 150, 300);
spawn.mapRect(3450, -12225, 75, 300);
spawn.mapRect(3375, -12150, 75, 300);
spawn.mapRect(3300, -12075, 75, 300);
spawn.mapRect(4725, -12375, 450, 75);
spawn.mapRect(4725, -12000, 600, 2000);
spawn.mapRect(-100, -13500, 100, 3500);
spawn.mapRect(-100, -13500, 100, 3500);
spawn.mapRect(6375, -12225, 1650, 2000);
spawn.mapRect(7225, -13225, 2850, 3000);
spawn.mapRect(-100, -13700, 100, 200);//roof
spawn.mapRect(-100, -13700, 3775, 100);
spawn.mapRect(6450, -13225, 1000, 100);
spawn.mapRect(7090, -13225, 120, 885);//pipe
//spawn.mapRect(6940, -12360, 200, 120);
spawn.mapRect(6940, -12350, 200, 5);
spawn.mapRect(6950, -12240, 140, 5);
spawn.mapRect(6940, -12365, 75, 15);
spawn.mapRect(6940, -12235, 75, 15);
//castle
spawn.mapRect(17700, 0, 4975, 2000);
spawn.mapRect(18600, -225, 375, 2225);
spawn.mapRect(19500, -225, 450, 2225);
spawn.mapRect(19500, -825, 450, 225);
spawn.mapRect(15924, -1575, 6751, 750);
spawn.mapRect(19950, -225, 975, 75);
spawn.mapRect(20925, -300, 225, 300);
spawn.mapRect(21000, -825, 150, 300);
spawn.mapRect(15924, -225, 1776, 2225);
spawn.mapRect(17175, -825, 525, 225);
spawn.mapRect(22600, -825, 75, 825);
// powerUps.spawnStartingPowerUps(1475, -1175);
// spawn.debris(750, -2200, 3700, 16); //16 debris per level
// spawn.bodyRect(1540, -1110, 300, 25, 0.9);
// spawn.randomSmallMob(1300, -70);
// spawn.randomMob(2650, -975, 0.8);
// spawn.randomGroup(1700, -900, 0.4);
// if (simulation.difficulty > 1) spawn.randomLevelBoss(2200, -1300);
// spawn.secondaryBossChance(100, -1500)
powerUps.addResearchToLevel() //needs to run after mobs are spawned
},
underpass() {
simulation.makeTextLog(`<strong>underpass</strong> by <span class='color-var'>Richard0820</span>`);
let key = false;
const door = level.door(2650, -825, 50, 250, 250, 10);
const elevator = level.elevator(-11050, -650, 450, 75, -2975, 0.003, { up: 0.1, down: 0.1 })
const slimePit = level.hazard(-4775, -350, 1975, 175);
const boost = level.boost(137.5, -600, 75, 25);
let base = Matter.Bodies.rectangle(-4375, -1000, 100, 100, {
density: 0.05,
isNotHoldable: true,
restitution: 1.05,
isStatic: false
}, true, [true], 0);
let left = Matter.Bodies.rectangle(-4375 + 50, -1000 - 50, 50, 50, {//not actually left/right
density: 0.05,
isNotHoldable: true,
isStatic: false
});
let right = Matter.Bodies.rectangle(-4375 - 50, -1000 - 50, 50, 50, {
density: 0.05,
isNotHoldable: true,
isStatic: false
});
let left2 = Matter.Bodies.rectangle(-4375 - 50, -1000 + 50, 50, 50, {
density: 0.05,
isNotHoldable: true,
isStatic: false
});
let right2 = Matter.Bodies.rectangle(-4375 + 50, -1000 + 50, 50, 50, {
density: 0.05,
isNotHoldable: true,
isStatic: false
});
dong = Matter.Body.create({
parts: [base, left, right, left2, right2]
});
body[body.length] = base;
body[body.length] = left;
body[body.length] = right;
body[body.length] = left2;
body[body.length] = right2;
Matter.Composite.add(engine.world, dong)
Matter.Composite.add(engine.world, Constraint.create({
pointA: { x: -3825, y: -975 },
bodyB: dong,
stiffness: 0.2,
damping: 0.1
}));
composite[composite.length] = dong;
setTimeout(function () {
dong.collisionFilter.category = cat.body;
dong.collisionFilter.mask = cat.body | cat.player | cat.bullet | cat.mobBullet | cat.mob //| cat.map
}, 1000);
level.custom = () => {
ctx.save()
ctx.beginPath()
ctx.fillStyle = "#80808077";
ctx.strokeStyle = "#80808022";
ctx.fillRect(225, -1025, 2400, 450);
ctx.fillRect(-2950, -1025, 3100, 450);
ctx.fillRect(-7050, -1025, 2400, 450);
ctx.fillRect(-10575, -3975, 4525, 1025);
ctx.fillRect(-4650, -1700, 1700, 1100);
ctx.fillRect(-11150, -3575, 575, 3050);
ctx.fillRect(-11900, -1000, 750, 475);
ctx.fill()
ctx.stroke()
ctx.restore()
ctx.save()
ctx.beginPath()
ctx.fillStyle = "#d8dadf";
ctx.strokeStyle = "#d8dadf";
ctx.moveTo(-2950, -600);
ctx.lineTo(-3730, -1725);
ctx.lineTo(-3730, -600);
ctx.moveTo(-4650, -600);
ctx.lineTo(-3925, -1725);
ctx.lineTo(-3925, -575);
ctx.moveTo(-10575, -3425); //NE section
ctx.lineTo(-10100, -2975);
ctx.lineTo(-10575, -2975);
// ctx.moveTo(-7625, -3800);
// ctx.lineTo(-6750, -2975);
// ctx.lineTo(-7625, -2975);
ctx.moveTo(-7975, -2975);
ctx.lineTo(-7625, -3800);
ctx.lineTo(-7350, -2950);
ctx.moveTo(-6750, -2975);
ctx.lineTo(-7075, -3800);
ctx.lineTo(-7350, -2950);
// ctx.moveTo(-7975, -2975);
// ctx.lineTo(-7075, -3800);
// ctx.lineTo(-7075, -2975);
ctx.moveTo(-11900, -950);
ctx.lineTo(-11900, -550);
ctx.lineTo(-11500, -550);
ctx.fillRect(-3925, -1675, 200, 1075);
ctx.fillRect(-7625, -3800, 550, 875);
ctx.clearRect(-10600, -4000, 525, 475);
ctx.clearRect(-10100, -4000, 500, 300);
ctx.clearRect(-9625, -4000, 500, 175);
ctx.fillRect(-11125, -3600, 550, 50);
ctx.fillRect(-10600, -3400, 50, 425);
ctx.fillRect(-11925, -925, 45, 375);
ctx.fillRect(-3950, -1675, 75, 1100);
ctx.fillRect(-3925, -625, 950, 50);
ctx.fillRect(-4650, -600, 1700, 375);
ctx.fillRect(-14550, -2400, 2650, 2050);
//ctx.clearRect(-11050, -3000, 475, 50);
ctx.moveTo(-11150, -3575);
ctx.lineTo(-10575, -2150);
ctx.lineTo(-10575, -3575);
ctx.stroke()
ctx.fill()
ctx.restore()
boost.query()
slimePit.query()
if (Matter.Query.collides(dong, [player]).length > 0 && !(m.isCloak && tech.isIntangible) && m.immuneCycle < m.cycle) {
m.immuneCycle = m.cycle + m.collisionImmuneCycles; //player is immune to damage for 30 cycles
const dmg = 0.05 * Math.min(simulation.dmgScale, simulation.difficulty);
m.damage(dmg);
simulation.drawList.push({ //add dmg to draw queue
x: dong.position.x,
y: dong.position.y,
radius: Math.sqrt(dmg) * 200,
color: simulation.mobDmgColor,
time: simulation.drawTime
});
}
for (let i = 0; i < mob.length; i++) {
if (Matter.Query.collides(dong, [mob[i]]).length > 0) {
const dmg = 1;
mob[i].damage(dmg, true);
simulation.drawList.push({ //add dmg to draw queue
x: dong.position.x,
y: dong.position.y,
radius: Math.sqrt(dmg) * 50,
color: simulation.mobDmgColor,
time: simulation.drawTime
});
break
}
}
level.exit.drawAndCheck();
ctx.beginPath()
ctx.fillStyle = '#68686822';
ctx.fillRect(-25, -2175, 100, 200);
ctx.fill()
ctx.setLineDash([125 * Math.random(), 125 * Math.random()]);
ctx.moveTo(-3825, -975)
ctx.lineTo(dong.position.x, dong.position.y)
ctx.stroke();
ctx.setLineDash([]);
simulation.drawList.push({ //add dmg to draw queue
x: dong.position.x,
y: dong.position.y,
radius: 10,
color: color.block,
time: 20
});
ctx.beginPath()
ctx.fillStyle = `rgba(68,68,68, ${3 * Math.sin(simulation.cycle * 0.015)})`
ctx.fillRect(-3000, -2175, 175, 25);
ctx.fillRect(-2850, -2300, 25, 150);
ctx.fillRect(-3000, -2300, 175, 25);
ctx.fillRect(-3000, -2425, 25, 150);
ctx.fillRect(-3000, -2425, 175, 25);
ctx.fill()
ctx.fillStyle = `rgba(68,68,68, ${5 * Math.sin(simulation.cycle * 0.015)})`
ctx.fillRect(-2725, -2425, 25, 275);
ctx.fillRect(-2725, -2425, 175, 25);
ctx.fillRect(-2575, -2425, 25, 275);
ctx.fillRect(-2725, -2300, 175, 25);
ctx.fill()
ctx.fillStyle = `rgba(68,68,68, ${7 * Math.sin(simulation.cycle * 0.015)})`
ctx.fillRect(-2450, -2425, 25, 275);
ctx.fillRect(-2450, -2175, 175, 25);
ctx.fill()
ctx.stroke();
ctx.fillStyle = `#00FFFF22`;
ctx.fillRect(-7650, -2975 - 100, 600, 2375 + 100)
ctx.fill()
ctx.fillStyle = `#00FFFF66`
ctx.fillRect(-7650 + Math.floor(Math.random() * 600), -2975 - 100, 5, 2375 + 100)
ctx.fillRect(-7650 + Math.floor(Math.random() * 600), -2975 - 100, 5, 2375 + 100)
ctx.fillStyle = `rgba(68, 68, 68)`
ctx.fillRect(-7675, -3075, 50, 125);
ctx.fillRect(-7075, -3075, 50, 125);
ctx.fillRect(-7725, -3025, 75, 75);
ctx.fillRect(-7050, -3025, 75, 75);
ctx.fill()
for (let i = 0, len = body.length; i < len; ++i) { //push blocks away vertically
if (body[i].position.x > -7625 && body[i].position.x < -7075 && body[i].position.y > -2975 - 100 && body[i].position.y < -625) {
body[i].force.y -= simulation.g * body[i].mass + 0.012;
}
}
for (let i = 0, len = bullet.length; i < len; ++i) { //push bullets away vertically
if (bullet[i].position.x > -7625 && bullet[i].position.x < -7075 && bullet[i].position.y > -2975 - 100 && bullet[i].position.y < -625) {
bullet[i].force.y -= simulation.g * bullet[i].mass;
}
}
for (let i = 0, len = powerUp.length; i < len; ++i) { //push powerups away vertically
if (powerUp[i].position.x > -7625 && powerUp[i].position.x < -7075 && powerUp[i].position.y > -2975 - 100 && powerUp[i].position.y < -625) {
powerUp[i].force.y -= simulation.g * powerUp[i].mass + 0.12;
}
}
for (let i = 0, len = mob.length; i < len; ++i) { //push mobs away vertically
if (mob[i].position.x > -7625 && mob[i].position.x < -7075 && mob[i].position.y > -2975 - 100 && mob[i].position.y < -625) {
mob[i].force.y -= simulation.g * mob[i].mass + 0.0012;
}
}
if (m.pos.x > -7625 && m.pos.x < -7075 && m.pos.y > -2975 - 100 && m.pos.y < -625) {
player.force.y -= m.mass * simulation.g + (input.down ? 0 : 0.012 * 2);
}
ctx.save()
ctx.beginPath();
ctx.fillStyle = `rgba(0,250,250, 0.05)`;
ctx.fillRect(2625, -1000, 350, 450);
ctx.fill()
ctx.restore()
elevator.move()
if (mob.length <= 5) {
key = true;
}
if (key) {
level.exit.x = 2775;
level.exit.y = -650;
} else {
ctx.save()
ctx.beginPath();
ctx.moveTo(2775, -650 + 30);
ctx.lineTo(2775, -650 - 80);
ctx.bezierCurveTo(2775, -650 - 170, 2775 + 100, -650 - 170, 2775 + 100, -650 - 80);
ctx.lineTo(2775 + 100, -650 + 30);
ctx.lineTo(2775, -650 + 30);
ctx.fillStyle = "#00ffff";
ctx.fill();
ctx.restore()
const omega = new Image(); //the seal hehe
let width = Math.random() * 40; //Math.max(Math.sin(m.cycle * 0.05), Math.cos(m.cycle * 0.05)) *
let length = Math.random() * 40; //Math.max(Math.sin(m.cycle * 0.05), Math.cos(m.cycle * 0.05)) *
omega.src = "https://raw.githubusercontent.com/Whyisthisnotavalable/image-yy/main/MOSHED-2023-4-20-12-55-58-removebg-preview.png"; //"https://raw.githubusercontent.com/Whyisthisnotavalable/image-yy/main/omegafr.png";
ctx.drawImage(omega, 2775 + 50 - width, -650 - 50 - length, width * 2, length * 2)
ctx.save()
ctx.setLineDash([70 + 300 * Math.random(), 55 * Math.random()]);
ctx.beginPath()
// ctx.font = "80px monospace";
// ctx.fillText("Ω", 2775 + 50, -650 - 50);
ctx.strokeStyle = "gray";
ctx.arc(2775 + 50, -650 - 50, 100 * Math.abs(Math.sin(m.cycle * 0.07)) + 10, 0 + Math.sin(m.cycle * 0.1 * Math.PI), 0 + Math.cos(m.cycle * 0.1 + Math.PI))
ctx.shadowBlur = 10;
ctx.shadowColor = "black";
ctx.stroke()
//ctx.fill()
ctx.restore()
ctx.save()
ctx.setLineDash([70 + 300 * Math.random(), 55 * Math.random()]);
ctx.beginPath()
// ctx.font = "80px monospace";
// ctx.fillText("Ω", 2775 + 50, -650 - 50);
ctx.strokeStyle = "gray";
ctx.arc(2775 + 50, -650 - 50, 90 * Math.abs(Math.sin(m.cycle * 0.08)) + 10, 0 + Math.cos(m.cycle * 0.1 * Math.PI), 0 + Math.sin(m.cycle * 0.1 + Math.PI))
ctx.shadowBlur = 10;
ctx.shadowColor = "black";
ctx.stroke()
//ctx.fill()
ctx.restore()
ctx.save()
ctx.setLineDash([70 + 300 * Math.random(), 55 * Math.random()]);
ctx.beginPath()
// ctx.font = "80px monospace";
// ctx.fillText("Ω", 2775 + 50, -650 - 50);
ctx.strokeStyle = "gray";
ctx.arc(2775 + 50, -650 - 50, 80 * Math.abs(Math.sin(m.cycle * 0.09)) + 10, 0 + Math.tan(m.cycle * 0.1 * Math.PI), 0 + Math.tan(m.cycle * 0.1 + Math.PI))
ctx.shadowBlur = 10;
ctx.shadowColor = "black";
ctx.stroke()
//ctx.fill()
ctx.setLineDash([]);
ctx.restore()
}
};
level.setPosToSpawn(30, -2000); //normal spawn
level.exit.x = 1375;
level.exit.y = -1500;
level.defaultZoom = 1800
simulation.zoomTransition(level.defaultZoom + 800)
document.body.style.backgroundColor = "#d8dadf";
spawn.mapRect(-225, -1950, 350, 75);
spawn.mapRect(225, -1950, 50, 75);
spawn.mapRect(-250, -2025, 50, 150);
spawn.mapRect(250, -2025, 50, 150);
spawn.mapRect(-250, -2250, 50, 125);
spawn.mapRect(-225, -2325, 500, 100);
spawn.mapRect(250, -2250, 50, 125);
spawn.mapRect(-100, -2400, 250, 100);
spawn.mapRect(-25, -2475, 100, 100);
spawn.mapRect(125, -2350, 50, 50);
spawn.mapRect(-125, -2350, 50, 50);
spawn.mapRect(-50, -2425, 50, 50);
spawn.mapRect(50, -2425, 50, 50);
spawn.mapRect(-250, -2350, 50, 50);
spawn.mapRect(250, -2350, 50, 50);
spawn.mapRect(-75, -1975, 200, 50);
spawn.mapRect(-50, -2000, 150, 50);
spawn.mapRect(100, -1950, 50, 75);
spawn.mapRect(-75, -2250, 200, 50);
spawn.mapRect(-50, -2225, 150, 50);
spawn.mapRect(-2950, -1900, 3100, 900);
spawn.mapRect(225, -1900, 2875, 900);
spawn.mapRect(-2950, -600, 6050, 450);
spawn.mapRect(-3050, -500, 200, 350);
spawn.mapRect(-3150, -400, 200, 250);
spawn.mapRect(-3250, -300, 200, 150);
spawn.mapRect(2950, -1050, 150, 500);
spawn.mapRect(-4675, -1900, 1825, 200);
spawn.mapRect(-5325, -1900, 675, 900);
spawn.mapRect(-5325, -250, 2100, 100);
spawn.mapRect(-5325, -600, 675, 450); // -
spawn.mapRect(-4700, -500, 150, 350);
spawn.mapRect(-4650, -400, 200, 250);
spawn.mapRect(-4550, -300, 200, 150);
spawn.mapRect(-3875, -1025, 100, 100);
spawn.mapRect(-3800, -1050, 50, 50);
spawn.mapRect(-3900, -1050, 50, 50);
spawn.mapRect(-3800, -950, 50, 50);
spawn.mapRect(-3900, -950, 50, 50);
spawn.mapRect(-6925, -1175, 1700, 175);
spawn.mapRect(-6925, -600, 1725, 175);
spawn.mapRect(-7700, -600, 800, 425);// -
spawn.mapRect(-7800, -2950, 175, 2775);
spawn.mapRect(-7075, -2950, 175, 1950);
spawn.mapRect(-9150, -2975, 1525, 175);
spawn.mapRect(-7075, -2975, 1150, 175);
spawn.mapRect(-6100, -3900, 175, 1100);
spawn.mapRect(-9150, -3975, 3225, 175);
spawn.mapRect(-9175, -3850, 75, 75);
spawn.mapRect(-9625, -3825, 500, 150);
spawn.mapRect(-9650, -3725, 75, 75);
spawn.mapRect(-10100, -3700, 500, 150);
spawn.mapRect(-10100, -2975, 975, 175);
spawn.mapRect(-10125, -3600, 75, 75);
spawn.mapRect(-10575, -3575, 500, 150);
spawn.mapRect(-10575, -2975, 500, 175);
spawn.mapRect(-11325, -2975, 250, 175);
spawn.mapRect(-11325, -3575, 175, 775);
// spawn.mapRect(-11325, -3575, 800, 150);
spawn.mapRect(-11225, -2975, 150, 2000);
spawn.mapRect(-10575, -2975, 150, 2500);
spawn.mapRect(-11650, -550, 1225, 150);
spawn.mapRect(-11650, -1100, 575, 150);
spawn.mapRect(-14675, -2525, 2925, 150);
spawn.mapRect(-11900, -2525, 150, 1575);
spawn.mapRect(-11850, -1100, 250, 150);
spawn.mapRect(-11875, -550, 275, 150);
spawn.mapRect(-11900, -550, 150, 350);
spawn.mapRect(-14675, -2525, 150, 2300);
spawn.mapRect(-14675, -375, 2925, 175);
spawn.mapRect(2725, -625, 250, 50);
spawn.mapRect(2625, -1025, 100, 225);
spawn.mapRect(2700, -1025, 300, 125);
spawn.mapRect(2625, -612.5, 125, 50);
spawn.mapRect(-3950, -1725, 250, 50);
spawn.mapRect(-7650, -3825, 600, 50);
spawn.mapRect(-13900, -2400, 200, 50);
spawn.mapVertex(-11957, -430, '-175 175 0 175 0 0');
spawn.mapVertex(-14470, -430, '175 175 0 175 0 0');
spawn.mapVertex(-11957, -2319, '-175 -175 0 -175 0 0');
spawn.mapVertex(-14470, -2319, '0 0 0 -175 175 -175');
//spawn.mapRect(-13900, -2150, 1375, 125);
// const sword = function() { //the ultimate blade of las destruction // NO SWORD NO DROPS >:(
// mobs.spawn(player.position.x, player.position.y, 5, 30, "transparent");
// let me = mob[mob.length - 1];
// Matter.Body.setDensity(me, 1);
// me.vertices = Matter.Vertices.rotate(me.vertices, Math.PI, me.position);
// me.collisionFilter.category = cat.bullet;
// me.collisionFilter.mask = cat.mob | cat.mobBullet;
// me.isDropPowerUp = false;
// me.isShielded = true;
// me.showHealthBar = false;
// me.isUnblockable = true;
// me.leaveBody = false;
// me.isBadTarget = true;
// me.stroke = "transparent";
// me.isSword = true;
// let index = 0;
// let radius = 50;
// // const len = cons.length;
// // cons[len] = Constraint.create({
// // pointA: player.position,
// // bodyB: me,
// // stiffness: 0.14,
// // damping: 0.5
// // });
// // setTimeout(function(){
// // Composite.add(engine.world, cons[cons.length - 1]);
// // }, 1)
// // cons[len].length = 100 + 1.5 * radius;
// me.do = function() {
// // this.health = Infinity;//just in case
// // for(let i = 0; i < mob.length; i++) {
// // if(Matter.Query.collides(this, [mob[i]]).length > 0 && !mob[i].isSword) {
// // const dmg = 0.25;//do not nerf
// // mob[i].damage(dmg, true);
// // simulation.drawList.push({ //add dmg to draw queue
// // x: mob[i].position.x,
// // y: mob[i].position.y,
// // radius: Math.sqrt(dmg) * 50,
// // color: simulation.mobDmgColor,
// // time: simulation.drawTime
// // });
// // break
// // }
// // }
// Matter.Body.setPosition(this, {
// x: player.position.x + Math.cos(m.angle) * 100,
// y: player.position.y - (input.down ? 0 : 30) + Math.sin(m.angle) * 100
// })
// // this.force.x = Math.cos(m.angle) * 350;
// // this.force.y = Math.sin(m.angle) * 350;
// Matter.Body.setAngle(this, m.angle + Math.PI * 2);
// const setNoseShape = () => {
// const mag = radius + radius * 10;
// this.vertices[2].x = this.position.x + Math.cos(this.angle) * mag;
// this.vertices[2].y = this.position.y + Math.sin(this.angle) * mag;
// this.vertices[4].x = this.position.x + Math.cos(this.angle) * mag;
// this.vertices[4].y = this.position.y + Math.sin(this.angle) * mag;
// this.vertices[0].x = this.position.x + Math.cos(this.angle) * mag;
// this.vertices[0].y = this.position.y + Math.sin(this.angle) * mag;
// };
// const spike = Vector.mult(Vector.normalise(Vector.sub(this.vertices[2], this.position)), radius * 100)
// const spike2 = Vector.mult(Vector.normalise(Vector.sub(this.vertices[4], this.position)), radius * 500)
// const spike3 = Vector.mult(Vector.normalise(Vector.sub(this.vertices[0], this.position)), radius * 500)
// this.vertices[2].x = this.position.x + spike.x / 100
// this.vertices[2].y = this.position.y + spike.y / 100
// this.vertices[4].x = this.position.x + spike2.x / 75
// this.vertices[4].y = this.position.y + spike2.y / 75
// this.vertices[0].x = this.position.x + spike3.x / 75
// this.vertices[0].y = this.position.y + spike3.y / 75
// if(index == 0) {
// setNoseShape();
// index++;
// }
// ctx.save()
// ctx.beginPath();
// const vertices = this.vertices;
// ctx.lineWidth = 100;
// ctx.moveTo(vertices[0].x, vertices[0].y);
// for (let j = 1, len = vertices.length; j < len; ++j) ctx.lineTo(vertices[j].x, vertices[j].y);
// ctx.lineTo(vertices[0].x, vertices[0].y);
// const gradient = ctx.createRadialGradient(this.position.x, this.position.y, 15, this.position.x, this.position.y, Math.abs(275 * Math.sin(simulation.cycle / 50)) + 15);
// // Add three color stops
// gradient.addColorStop(0, m.eyeFillColor);
// gradient.addColorStop(0.9, "white");
// gradient.addColorStop(1, "darkgray");
// ctx.fillStyle = gradient;
// ctx.strokeStyle = "transparent";
// ctx.shadowBlur = 10;
// ctx.shadowColor = m.eyeFillColor;
// ctx.fill();
// ctx.stroke();
// ctx.restore()
// const Dx = Math.cos(m.angle);
// const Dy = Math.sin(m.angle);
// let xElec = this.position.x + 10 * Dx;
// let yElec = this.position.y + 10 * Dy;
// ctx.beginPath();
// ctx.moveTo(xElec, yElec);
// const step = 40
// for (let i = 0; i < 6; i++) {
// xElec += step * (Dx + 1.5 * (Math.random() - 0.5))
// yElec += step * (Dy + 1.5 * (Math.random() - 0.5))
// ctx.lineTo(xElec, yElec);
// }
// ctx.strokeStyle = m.eyeFillColor;
// ctx.lineWidth = 1.5;
// ctx.setLineDash([70 + 300 * Math.random(), 55 * Math.random()]);
// ctx.stroke(); // Draw it
// ctx.setLineDash([]);
// if(this.alive && m.energy > 0) {
// const gradient = ctx.createRadialGradient(this.position.x, this.position.y, 0, this.position.x, this.position.y, Math.abs(20 * Math.cos(simulation.cycle / 50)));
// // Add three color stops
// gradient.addColorStop(0, m.eyeFillColor);
// gradient.addColorStop(0.9, "white");
// gradient.addColorStop(1, "gray");
// ctx.save()
// ctx.beginPath()
// ctx.moveTo(this.position.x, this.position.y)
// ctx.arc(this.position.x, this.position.y, 20, 0, 2 * Math.PI)
// ctx.fillStyle = gradient;
// ctx.strokeStyle = "transparent";
// ctx.shadowBlur = 10;
// ctx.shadowColor = m.eyeFillColor;
// ctx.fill()
// ctx.stroke()
// ctx.restore()
// m.energy -= 0.002;
// ctx.save()
// ctx.translate(this.vertices[2].x, this.vertices[2].y)
// ctx.rotate(m.angle + Math.PI / 2)
// ctx.beginPath()
// ctx.font = "16px Arial";
// ctx.fillStyle = "black";
// ctx.strokeStyle = "black";
// // ctx.fillText("Θ", 0,0 - 110)
// // ctx.fillText("ά", 0,15 - 110)
// // ctx.fillText("ν", 0,30 - 110)
// // ctx.fillText("α", 0,45 - 110)
// // ctx.fillText("τ", 0,60 - 110)
// // ctx.fillText("ο", 0,75 - 110)
// // ctx.fillText("ς", 0,90 - 110)
// ctx.fillText("Ω", 0,55)
// ctx.fill()
// ctx.stroke()
// ctx.restore()
// simulation.drawList.push({
// x: this.position.x + Math.floor(Math.random() * 300 - Math.random() * 300),
// y: this.position.y + Math.floor(Math.random() * 300 - Math.random() * 300),
// radius: 2,
// color: m.eyeFillColor,
// time: simulation.drawTime
// });
// } else {
// this.death()
// powerUps.activated = false
// }
// // me.onDeath = function() {
// // this.removeCons();
// // };
// }
// }
//setTimeout(function() {sword();}, 100);
const wire = function () {
const breakingPoint = -1600;
const spawnx = -13800 + Math.floor(Math.random() * 100 - Math.random() * 100);
mobs.spawn(spawnx, -2375, 0, 2, "transparent");
let me = mob[mob.length - 1];
let boss = mob[0];
me.collisionFilter.category = cat.body;
me.collisionFilter.mask = cat.map;
me.g = 0.0003; //required for gravity
me.restitution = 0;
me.stroke = "transparent"
me.freeOfWires = false;
me.frictionAir = 0.01;
me.isDropPowerUp = false;
me.showHealthBar = false;
me.isBadTarget = true;
me.isUnblockable = true;
const wireX = spawnx;
const wireY = -2375;
//const randomw = Math.floor(Math.random() * 100 - Math.random() * 100);
const width = Math.abs(10 + Math.floor(Math.random() * 10 - Math.random() * 10));
const randomx = Math.floor(30 * Math.random() - 30 * Math.random());
const randomy = Math.floor(10 * Math.random() - 10 * Math.random())
me.do = function () {
if (this.freeOfWires) {
this.gravity();
} else {
if (boss.position.y > breakingPoint) {
this.freeOfWires = true;
this.force.y -= -0.0006;
this.force.x += Math.random() * boss.velocity.x / 10000;
this.fill = "#111";
}
//move mob to mob
Matter.Body.setPosition(this, {
x: boss.position.x + randomx,
y: boss.position.y + randomy
})
}
//draw wire
ctx.beginPath();
ctx.moveTo(wireX, wireY);
ctx.quadraticCurveTo(wireX, -100, this.position.x, this.position.y);
ctx.lineWidth = width;
ctx.lineCap = "butt";
ctx.strokeStyle = "#111";
ctx.stroke();
ctx.lineCap = "round";
};
}
const ball = function (x, y, radius = 11 * tech.bulletSize, sides = 70) {//superball //also, why is it called superballs?
mobs.spawn(x, y, sides, radius, "rgba(0,0,0)");
let me = mob[mob.length - 1];
me.stroke = "transparent";
me.onHit = function () {
simulation.drawList.push({ //add dmg to draw queue
x: this.position.x,
y: this.position.y,
radius: 20,
color: simulation.mobDmgColor,
time: simulation.drawTime
});
};
Matter.Body.setDensity(me, 0.00001); //normal is 0.001
me.timeLeft = 500;
me.friction = 0;
me.restitution = 1;
me.leaveBody = false;
me.isDropPowerUp = false;
//me.inertia = Infinity;
me.isBadTarget = true;
me.isMobBullet = true;
me.showHealthBar = false;
me.collisionFilter.category = cat.mobBullet;
me.collisionFilter.mask = cat.bullet | cat.player | cat.map | cat.body;
let index = 0;
me.do = function () {
this.timeLimit();
this.alwaysSeePlayer()
this.force.y += this.mass * 0.0012;
}
}
const normalBullet = function (x, y, radius = 9, sides = 3) {
//bullets
mobs.spawn(x, y, sides, radius, "rgba(0,0,0)");
let me = mob[mob.length - 1];
me.stroke = "transparent";
me.vertices = Matter.Vertices.rotate(me.vertices, Math.PI, me.position);
me.onHit = function () {
simulation.drawList.push({ //add dmg to draw queue
x: this.position.x,
y: this.position.y,
radius: 20,
color: simulation.mobDmgColor,
time: simulation.drawTime
});
};
Matter.Body.setDensity(me, 0.00004); //normal is 0.001
me.timeLeft = 220;
me.frictionAir = -0.01;
me.restitution = -1;
me.leaveBody = false;
me.isDropPowerUp = false;
//me.inertia = Infinity;
me.isBadTarget = true;
me.isMobBullet = true;
me.showHealthBar = false;
me.collisionFilter.category = cat.mobBullet;
me.collisionFilter.mask = null;
me.boss = null;
for (let i = 0, len = mob.length; i < len; i++) {
if (mob[i].restoreBoss) {
me.boss = mob[i];
}
}
let index = 0;
me.do = function () {
this.timeLimit();
this.alwaysSeePlayer()
const setNoseShape = () => {
const mag = this.radius + this.radius * 10;
this.vertices[1].x = this.position.x + Math.cos(this.angle) * mag;
this.vertices[1].y = this.position.y + Math.sin(this.angle) * mag;
const angle = Math.atan2(this.position.y - me.boss.position.y, this.position.x - me.boss.position.x);
Matter.Body.setAngle(this, angle);
};
const spike = Vector.mult(Vector.normalise(Vector.sub(this.vertices[1], this.position)), radius * 1000)
this.vertices[1].x = this.position.x + spike.x / 100
this.vertices[1].y = this.position.y + spike.y / 100
if (index == 0) {
setNoseShape();
index++;
}
if (Matter.Query.collides(this, map).length > 0 || Matter.Query.collides(this, body).length > 0) {
const slow = 0.69 //im sorry it looks cool though
Matter.Body.setVelocity(this, {
x: this.velocity.x * slow,
y: this.velocity.y * slow
});
simulation.drawList.push({ //add dmg to draw queue
x: this.position.x,
y: this.position.y,
radius: 10,
color: '#000000',
time: simulation.drawTime
});
if (this.velocity.x == 0 && this.velocity.y == 0) {
this.death();
}
this.frictionAir += 0.0001;
Matter.Body.setAngularVelocity(this, 0)
}
if (Matter.Query.collides(this, [player]).length > 0 && !(m.isCloak && tech.isIntangible) && m.immuneCycle < m.cycle) {
m.immuneCycle = m.cycle + m.collisionImmuneCycles; //player is immune to damage for 30 cycles // I wasnt gonna add this but since ya'll would have killed me if I didn't I added this
const dmg = 0.013 * simulation.dmgScale;
m.damage(dmg);
simulation.drawList.push({ //add dmg to draw queue
x: this.position.x,
y: this.position.y,
radius: Math.sqrt(dmg) * 200,
color: '#000000',
time: simulation.drawTime
});
}
};
}
const foamBullet = function (x, y, radius = 9, sides = 70) { //bullets
mobs.spawn(x, y, sides, radius, "rgb(0,0,0)");
let me = mob[mob.length - 1];
me.stroke = "transparent";
Matter.Body.setDensity(me, 0.00005); //normal is 0.001
me.timeLeft = 120;
// me.g = 0.0005; //required if using this.gravity
me.accelMag = 0.00006;
me.isVerticesChange = true
me.delay = 360 * simulation.CDScale;
me.spikeVertex = 0;
me.spikeLength = 0;
me.isSpikeGrowing = false;
me.spikeGrowth = 0;
me.isSpikeReset = false;
me.frictionAir = 0;
me.restitution = 0;
me.leaveBody = false;
me.isDropPowerUp = false;
me.isBadTarget = true;
me.isMobBullet = true;
me.showHealthBar = false;
me.isUnblockable = true;
me.collisionFilter.category = cat.mobBullet;
me.collisionFilter.mask = cat.body //| cat.bullet;// | cat.player;
me.do = function () {
if (this.distanceToPlayer2() < 40000) {
this.force = Vector.mult(Vector.normalise(Vector.sub(player.position, this.position)), this.mass * 0.004)
const slow = 0.99999999999999999;
Matter.Body.setVelocity(this, {
x: this.velocity.x * slow,
y: this.velocity.y * slow
});
}
// this.gravity();
this.timeLimit();
// for (let i = 0, len = this.vertices.length; i < len; i++) {
// const dist = Vector.sub(this.seePlayer.position, this.vertices[i]);
// const distMag = Vector.magnitude(dist);
// const spike = Vector.mult(Vector.normalise(Vector.sub(this.vertices[i], this.position)), radius * distMag)
// this.vertices[i].x = this.position.x + spike.x / 100
// this.vertices[i].y = this.position.y + spike.y / 100
// }
if (this.radius < 50) {
const scale = 1.05;
Matter.Body.scale(this, scale, scale);
this.radius *= scale;
}
if (Matter.Query.collides(this, map).length > 0 || Matter.Query.collides(this, body).length > 0 && this.speed < 10) {
const slow = 0.97
Matter.Body.setVelocity(this, {
x: this.velocity.x * slow,
y: this.velocity.y * slow
});
const SCALE = 0.9
Matter.Body.scale(this, SCALE, SCALE);
this.radius *= SCALE;
if (this.radius < 1) {
this.death()
}
} else {
this.attach();
}
};
me.attach = function () {
if (Matter.Query.collides(this, [player]).length > 0) {
Matter.Body.setPosition(this, player.position)
if (player.speed > 2.5) Matter.Body.setVelocity(player, Vector.mult(player.velocity, 0.94))
Matter.Body.setAngularVelocity(player, player.angularVelocity * 0.9);
m.damage(0.00003); //balanced? not sure
}
}
};
const orbital = function (who, radius, phase, speed, radius2) {//basically orbitBot
mobs.spawn(who.position.x, who.position.y, 8, 12, "rgba(0,0,0, 1)");
let me = mob[mob.length - 1];
me.stroke = "transparent";
Matter.Body.setDensity(me, 0.01); //normal is 0.001
me.leaveBody = false;
me.isDropPowerUp = false;
me.isBadTarget = true;
me.showHealthBar = false;
me.isOrbital = true;
me.isShielded = true
me.collisionFilter.category = cat.mobBullet;
me.collisionFilter.mask = cat.bullet; //cat.player | cat.map | cat.body
me.do = function () {
//if host is gone
if (!who || !who.alive) {
this.death();
return
}
//set orbit
const time = simulation.cycle * speed + phase
const orbit = {
x: Math.cos(time),
y: Math.sin(time)
}
Matter.Body.setPosition(this, Vector.add(Vector.add(who.position, who.velocity), Vector.mult(orbit, radius + radius2)))
//damage player
if (Matter.Query.collides(this, [player]).length > 0 && !(m.isCloak && tech.isIntangible) && m.immuneCycle < m.cycle) {
m.immuneCycle = m.cycle + m.collisionImmuneCycles; //player is immune to damage for 30 cycles
const dmg = 0.013 * simulation.dmgScale
m.damage(dmg);
simulation.drawList.push({ //add dmg to draw queue
x: this.position.x,
y: this.position.y,
radius: Math.sqrt(dmg) * 200,
color: simulation.mobDmgColor,
time: simulation.drawTime
});
//this.death();
}
};
}
const missile = function (where, speed = 0.007, size = 1) {
mobs.spawn(where.x, where.y, 3, 20, '#000000');
let me = mob[mob.length - 1];
me.accelMag = speed;
Matter.Body.setDensity(me, 0.0000000000001);
me.stroke = 'transparent';
me.showHealthBar = false;
me.collisionFilter.category = cat.mobBullet;
me.collisionFilter.mask = cat.bullet | cat.player | cat.map | cat.body;
me.leaveBody = false;
me.isDropPowerUp = false;
me.onHit = function () {
b.explosion(this.position, (tech.isMissileBig ? 230 : 180) + 60 * Math.random())
this.death()
}
me.do = function () {
this.seePlayerByHistory();
this.alwaysSeePlayer();
this.attraction();
const angle = Math.atan2(this.seePlayer.position.y - this.position.y, this.seePlayer.position.x - this.position.x);
Matter.Body.setAngle(this, angle);
// ctx.beginPath()
// ctx.strokeStyle = "rgba(255,155,0,0.5)";
// ctx.lineWidth = 15;
// ctx.arc(this.vertices[0].x, this.vertices[0].x, 15, 0, 2 * Math.PI);
// ctx.stroke()
simulation.drawList.push({ //add dmg to draw queue
x: this.position.x,
y: this.position.y,
radius: 10,
color: `rgba(255,155,0,${Math.random()})`,
time: simulation.drawTime
});
}
}
const railBullet = function (x, y) {
mobs.spawn(x, y, 5, 20, "black");
let me = mob[mob.length - 1];
me.stroke = "black";
me.vertices = Matter.Vertices.rotate(me.vertices, Math.PI, me.position);
me.onHit = function () {
simulation.drawList.push({ //add dmg to draw queue
x: this.position.x,
y: this.position.y,
radius: 20,
color: simulation.mobDmgColor,
time: simulation.drawTime
});
};
Matter.Body.setDensity(me, 0.00004); //normal is 0.001
me.timeLeft = 220;
me.frictionAir = -0.01;
me.restitution = -1;
me.leaveBody = false;
me.isDropPowerUp = false;
//me.inertia = Infinity;
me.isBadTarget = true;
me.isMobBullet = true;
me.showHealthBar = false;
me.collisionFilter.category = cat.mobBullet;
me.collisionFilter.mask = cat.player;
const radius = 30;
let index = 0;
me.do = function () {
this.timeLimit();
this.alwaysSeePlayer()
const setNoseShape = () => {
const mag = me.radius + me.radius * 10;
const angle = Math.atan2(me.seePlayer.position.y - this.position.y, me.seePlayer.position.x - this.position.x);
me.vertices[2].x = this.position.x + Math.cos(this.angle) * mag;
me.vertices[2].y = this.position.y + Math.sin(this.angle) * mag;
me.vertices[4].x = this.position.x + Math.cos(this.angle) * mag;
me.vertices[4].y = this.position.y + Math.sin(this.angle) * mag;
me.vertices[0].x = this.position.x + Math.cos(this.angle) * mag;
me.vertices[0].y = this.position.y + Math.sin(this.angle) * mag;
Matter.Body.setAngle(this, angle);
};
const spike = Vector.mult(Vector.normalise(Vector.sub(this.vertices[2], me.position)), radius * 1000)
const spike2 = Vector.mult(Vector.normalise(Vector.sub(this.vertices[4], me.position)), radius * 1000)
const spike3 = Vector.mult(Vector.normalise(Vector.sub(this.vertices[0], me.position)), radius * 1000)
me.vertices[2].x = this.position.x + spike.x / 100
me.vertices[2].y = this.position.y + spike.y / 100
me.vertices[4].x = this.position.x + spike2.x / 75
me.vertices[4].y = this.position.y + spike2.y / 75
me.vertices[0].x = this.position.x + spike3.x / 75
me.vertices[0].y = this.position.y + spike3.y / 75
if (index == 0) {
setNoseShape();
index++;
}
if (Matter.Query.collides(this, map).length > 0 || Matter.Query.collides(this, body).length > 0) {
const slow = 0.69 //im sorry it looks cool though
Matter.Body.setVelocity(this, {
x: this.velocity.x * slow,
y: this.velocity.y * slow
});
// simulation.drawList.push({ //add dmg to draw queue
// x: this.position.x,
// y: this.position.y,
// radius: 10,
// color: '#000000',
// time: simulation.drawTime
// });
if (this.velocity.x == 0 && this.velocity.y == 0) {
this.death();
}
this.frictionAir += 0.0001;
Matter.Body.setAngularVelocity(this, 0)
}
if (Matter.Query.collides(this, [player]).length > 0 && !(m.isCloak && tech.isIntangible) && m.immuneCycle < m.cycle) {
m.immuneCycle = m.cycle + m.collisionImmuneCycles; //player is immune to damage for 30 cycles // I wasnt gonna add this but since ya'll would have killed me if I didn't I added this
const dmg = 0.013 * simulation.dmgScale;
m.damage(dmg);
// simulation.drawList.push({ //add dmg to draw queue
// x: this.position.x,
// y: this.position.y,
// radius: Math.sqrt(dmg) * 200,
// color: '#000000',
// time: simulation.drawTime
// });
}
};
}
const grenade = function (x, y, lifeSpan = 90 + Math.ceil(60 / simulation.accelScale), pulseRadius = Math.min(550, 250 + simulation.difficulty * 3), size = 3) {
mobs.spawn(x, y, 4, size, "rgb(0,0,0)"); //rgb(215,80,190)
let me = mob[mob.length - 1];
me.stroke = "transparent";
me.onHit = function () {
this.explode(this.mass);
};
Matter.Body.setDensity(me, 0.00004); //normal is 0.001
me.lifeSpan = lifeSpan;
me.timeLeft = me.lifeSpan;
// me.g = 0.0002; //required if using this.gravity
me.frictionAir = 0;
me.restitution = 0.8;
me.leaveBody = false;
me.isDropPowerUp = false;
me.isBadTarget = true;
me.isMobBullet = true;
me.onDeath = function () {
//damage player if in range
if (Vector.magnitude(Vector.sub(player.position, this.position)) < pulseRadius && m.immuneCycle < m.cycle) {
m.immuneCycle = m.cycle + m.collisionImmuneCycles; //player is immune to damage
m.damage(0.015 * simulation.dmgScale);
}
simulation.drawList.push({ //add dmg to draw queue
x: this.position.x,
y: this.position.y,
radius: pulseRadius,
color: "rgba(0,0,0,0.3)",
time: simulation.drawTime
});
};
me.showHealthBar = false;
me.collisionFilter.category = cat.mobBullet;
me.collisionFilter.mask = cat.map | cat.body | cat.player
// me.collisionFilter.mask = 0
me.do = function () {
this.timeLimit();
ctx.save()
ctx.beginPath(); //draw explosion outline
ctx.arc(this.position.x, this.position.y, pulseRadius * (1.01 - this.timeLeft / this.lifeSpan), 0, 2 * Math.PI); //* this.fireCycle / this.fireDelay
ctx.fillStyle = "rgba(0,0,0,0.05)";
ctx.fill();
ctx.restore()
};
}
const sniper = function (x, y, radius = 30) {
mobs.spawn(x, y, 8, radius, '#00000000');
let me = mob[mob.length - 1];
me.accelMag = 0.0003
me.stroke = 'transparent';
//me.isBoss = true;
me.searchTarget = map[Math.floor(Math.random() * (map.length - 1))].position;
me.frictionStatic = 0;
me.friction = 0;
me.seeAtDistance2 = 20000000 //14000 vision range
me.vertices = Matter.Vertices.rotate(me.vertices, Math.PI, me.position);
Matter.Body.rotate(me, Math.random() * Math.PI * 2);
me.showHealthBar = false
Matter.Body.setDensity(me, 0.01)
me.fireDir = { x: 0, y: 0 }
me.seePlayerFreq = 0
me.repulsionRange = 400000 + radius * radius;
me.collisionFilter.mask = cat.bullet | cat.player | cat.body | cat.map | cat.mob | cat.mobBullet
me.do = function () {
this.seePlayerCheck();
this.attraction();
this.repulsion();
this.search()
if (this.seePlayer.recall) {
const h = this.radius * 0.3;
const w = this.radius * 2;
const x = this.position.x - w / 2;
const y = this.position.y - w * 0.7;
ctx.fillStyle = "rgba(100, 100, 100, 0.3)";
ctx.fillRect(x, y, w, h);
ctx.fillStyle = "rgba(0,255,255,0.7)";
ctx.fillRect(x, y, w * this.health, h);
}
if (this.health < 1) {
this.health += 0.0005; //regen
}
ctx.save()
ctx.translate(this.position.x, this.position.y)
ctx.rotate(Math.atan2(this.seePlayer.position.y - this.position.y, this.seePlayer.position.x - this.position.x))
if (this.seePlayer.recall > 0 && this.distanceToPlayer2() > this.repulsionRange) {
var grd2 = ctx.createLinearGradient(0, 0, -150, 0);
// grd2.addColorStop(0, 'rgba(255, 255, 155, 0.8)');
// grd2.addColorStop(1, 'rgba(255, 200, 0, 0.1)');
grd2.addColorStop(0, 'rgba(150, 200, 255, 0.7)');
grd2.addColorStop(1, 'rgba(150, 200, 255, 0)');
ctx.fillStyle = grd2;
ctx.beginPath();
ctx.moveTo(-18, -25);
//10 * (Math.random() - 0.5), 10 * (Math.random() - 0.5)
ctx.lineTo(-18, 25);
ctx.lineTo(-50 - 100 * Math.random(), 0);
ctx.fill();
} else if (this.distanceToPlayer2() < this.repulsionRange) {
var grd2 = ctx.createLinearGradient(0, 0, 80, 0);
grd2.addColorStop(0, 'rgba(150, 200, 255, 0.7)');
grd2.addColorStop(1, 'rgba(150, 200, 255, 0)');
ctx.fillStyle = grd2;
ctx.beginPath();
ctx.moveTo(20, -16);
//10 * (Math.random() - 0.5), 10 * (Math.random() - 0.5)
ctx.lineTo(20, 16);
ctx.lineTo(35 + 43 * Math.random(), 0);
ctx.fill();
}
ctx.restore()
ctx.save()
ctx.translate(this.position.x, this.position.y)
ctx.rotate(Math.atan2(this.seePlayer.position.y - this.position.y, this.seePlayer.position.x - this.position.x))
ctx.beginPath();
ctx.arc(0, 0, 30, 0, 2 * Math.PI);
ctx.fillStyle = m.bodyGradient
ctx.fill();
ctx.arc(15, 0, 4, 0, 2 * Math.PI);
ctx.strokeStyle = "#333";
ctx.lineWidth = 2;
ctx.stroke();
ctx.beginPath(); //eye
ctx.arc(15, 0, 3.5, 0, 2 * Math.PI);
ctx.fillStyle = `rgba(255, 0, 0, ${this.health * this.health})`;
ctx.fill()
ctx.restore()
//set direction to turn to fire
if (this.seePlayer.recall && !(simulation.cycle % 30)) {
this.seePlayer.recall -= 10;
this.fireDir = Vector.normalise(Vector.sub(this.seePlayer.position, this.position));
spawn.sniperBullet(this.position.x, this.position.y, 7 + Math.ceil(this.radius / 15), 5);
const v = 10 + 8 * simulation.accelScale;
Matter.Body.setVelocity(mob[mob.length - 1], {
x: this.velocity.x + this.fireDir.x * v,
y: this.velocity.y + this.fireDir.y * v
});
}
};
}
const laserEM = function (x, y, radius = 30) {
mobs.spawn(x, y, 8, radius, '#00000000');
let me = mob[mob.length - 1];
me.accelMag = 0.0003
me.stroke = 'transparent';
//me.isBoss = true;
me.frictionStatic = 0;
me.friction = 0;
me.seeAtDistance2 = 20000000 //14000 vision range
me.vertices = Matter.Vertices.rotate(me.vertices, Math.PI, me.position);
Matter.Body.rotate(me, Math.random() * Math.PI * 2);
me.showHealthBar = false
Matter.Body.setDensity(me, 0.01)
me.seePlayerFreq = 0
me.searchTarget = map[Math.floor(Math.random() * (map.length - 1))].position;
me.swordDamage = 0.025 * simulation.dmgScale
me.collisionFilter.mask = cat.bullet | cat.player | cat.body | cat.map | cat.mob | cat.mobBullet
me.repulsionRange = 50000;
me.do = function () {
this.repulsion();
this.search()
if (this.seePlayer.recall) {
const h = this.radius * 0.3;
const w = this.radius * 2;
const x = this.position.x - w / 2;
const y = this.position.y - w * 0.7;
ctx.fillStyle = "rgba(100, 100, 100, 0.3)";
ctx.fillRect(x, y, w, h);
ctx.fillStyle = "rgba(0,255,255,0.7)";
ctx.fillRect(x, y, w * this.health, h);
}
if (this.health < 1) {
this.health += 0.0005; //regen
}
if (this.seePlayer.recall) {
this.laserSword(this.position, Math.atan2(this.seePlayer.position.y - this.position.y, this.seePlayer.position.x - this.position.x), 500 * Math.random());
}
ctx.save()
ctx.translate(this.position.x, this.position.y)
ctx.rotate(Math.atan2(this.seePlayer.position.y - this.position.y, this.seePlayer.position.x - this.position.x))
if (this.seePlayer.recall > 0 && this.distanceToPlayer2() > this.repulsionRange) {
var grd2 = ctx.createLinearGradient(0, 0, -150, 0);
// grd2.addColorStop(0, 'rgba(255, 255, 155, 0.8)');
// grd2.addColorStop(1, 'rgba(255, 200, 0, 0.1)');
grd2.addColorStop(0, 'rgba(150, 200, 255, 0.7)');
grd2.addColorStop(1, 'rgba(150, 200, 255, 0)');
ctx.fillStyle = grd2;
ctx.beginPath();
ctx.moveTo(-18, -25);
//10 * (Math.random() - 0.5), 10 * (Math.random() - 0.5)
ctx.lineTo(-18, 25);
ctx.lineTo(-50 - 100 * Math.random(), 0);
ctx.fill();
} else if (this.distanceToPlayer2() < this.repulsionRange) {
var grd2 = ctx.createLinearGradient(0, 0, 80, 0);
grd2.addColorStop(0, 'rgba(150, 200, 255, 0.7)');
grd2.addColorStop(1, 'rgba(150, 200, 255, 0)');
ctx.fillStyle = grd2;
ctx.beginPath();
ctx.moveTo(20, -16);
//10 * (Math.random() - 0.5), 10 * (Math.random() - 0.5)
ctx.lineTo(20, 16);
ctx.lineTo(35 + 43 * Math.random(), 0);
ctx.fill();
}
ctx.restore()
ctx.save()
ctx.translate(this.position.x, this.position.y)
ctx.rotate(Math.atan2(this.seePlayer.position.y - this.position.y, this.seePlayer.position.x - this.position.x))
ctx.beginPath();
ctx.arc(0, 0, 30, 0, 2 * Math.PI);
ctx.fillStyle = m.bodyGradient
ctx.fill();
ctx.arc(15, 0, 4, 0, 2 * Math.PI);
ctx.strokeStyle = "#333";
ctx.lineWidth = 2;
ctx.stroke();
ctx.beginPath(); //eye
ctx.arc(15, 0, 3.5, 0, 2 * Math.PI);
ctx.fillStyle = `rgba(255, 0, 0, ${this.health * this.health})`;
ctx.fill()
ctx.restore()
this.seePlayerCheck();
this.attraction();
}
me.laserSword = function (where, angle, length) {
const sub = Vector.sub(this.seePlayer.position, this.position)
const unit = Vector.normalise(sub)
const path = [{
x: this.position.x + 20 * Math.cos(this.angle),
y: this.position.y + 20 * Math.sin(this.angle)
},
{
x: this.position.x + (120 + 400) * Math.sqrt(Math.random()) * Math.cos(this.angle),
y: this.position.y + (120 + 400) * Math.sqrt(Math.random()) * Math.sin(this.angle)
}
];
this.seePlayer.recall -= 3;
const vertexCollision = function (v1, v1End, domain) {
for (let i = 0; i < domain.length; ++i) {
let vertices = domain[i].vertices;
const len = vertices.length - 1;
for (let j = 0; j < len; j++) {
results = simulation.checkLineIntersection(v1, v1End, vertices[j], vertices[j + 1]);
if (results.onLine1 && results.onLine2) {
const dx = v1.x - results.x;
const dy = v1.y - results.y;
const dist2 = dx * dx + dy * dy;
if (dist2 < best.dist2 && (!domain[i].mob || domain[i].alive)) best = { x: results.x, y: results.y, dist2: dist2, who: domain[i], v1: vertices[j], v2: vertices[j + 1] };
}
}
results = simulation.checkLineIntersection(v1, v1End, vertices[0], vertices[len]);
if (results.onLine1 && results.onLine2) {
const dx = v1.x - results.x;
const dy = v1.y - results.y;
const dist2 = dx * dx + dy * dy;
if (dist2 < best.dist2) best = { x: results.x, y: results.y, dist2: dist2, who: domain[i], v1: vertices[0], v2: vertices[len] };
}
}
};
best = { x: null, y: null, dist2: Infinity, who: null, v1: null, v2: null };
const look = { x: where.x + length * Math.cos(angle), y: where.y + length * Math.sin(angle) };
// vertexCollision(where, look, body); // vertexCollision(where, look, mob);
vertexCollision(where, look, map);
if (!m.isCloak) vertexCollision(where, look, [player]);
if (best.who && (best.who === player) && m.immuneCycle < m.cycle) {
m.immuneCycle = m.cycle + m.collisionImmuneCycles; //player is immune to damage for an extra second
m.damage(this.swordDamage);
simulation.drawList.push({ //add dmg to draw queue
x: best.x,
y: best.y,
radius: this.swordDamage * 1500,
color: "rgba(80,0,255,0.5)",
time: 20
});
}
if (best.dist2 === Infinity) best = look;
ctx.beginPath(); //draw beam
ctx.moveTo(where.x, where.y);
ctx.lineTo(best.x, best.y);
ctx.strokeStyle = "#50f";
ctx.lineWidth = 1.5;
ctx.setLineDash([70 + 300 * Math.random(), 55 * Math.random()]);
ctx.stroke(); // Draw it
ctx.setLineDash([]);
ctx.lineWidth = 20;
ctx.strokeStyle = "rgba(80,0,255,0.07)";
ctx.stroke(); // Draw it
const Dx = Math.cos(Math.atan2(this.seePlayer.position.y - this.position.y, this.seePlayer.position.x - this.position.x));
const Dy = Math.sin(Math.atan2(this.seePlayer.position.y - this.position.y, this.seePlayer.position.x - this.position.x));
let xElec = this.position.x + 40 * Dx;
let yElec = this.position.y + 40 * Dy;
ctx.beginPath();
ctx.moveTo(xElec, yElec);
const step = 40
for (let i = 0; i < 6; i++) {
xElec += step * (Dx + 1.5 * (Math.random() - 0.5))
yElec += step * (Dy + 1.5 * (Math.random() - 0.5))
ctx.lineTo(xElec, yElec);
}
ctx.strokeStyle = "#50f";
ctx.lineWidth = 1.5;
ctx.setLineDash([70 + 300 * Math.random(), 55 * Math.random()]);
ctx.stroke(); // Draw it
ctx.setLineDash([]);
}
};
// if(powerUps.pass == undefined) {
// let pass = {pass:true, activated:false};
// Object.assign(powerUps, pass)
// }
// const loadOut = {
// loadOut: {
// name: "loadOut",
// color: "#000000", //"hsl(248,100%,65%)",
// size() { return 40 },
// effect() {
// if(m.alive) {
// // tech.damage *= 2;
// let text = "";
// if (!tech.isSuperDeterminism) { text += `<div class='cancel' onclick='powerUps.endDraft("buff",true)'>${tech.isCancelTech ? "?":"✕"}</div>`; };
// text += `<h3 style = 'color:#fff; text-align:left; margin: 0px;'>Blessing Of Sal</h3>`;
// text += `<div class="choose-grid-module" onclick="powerUps.loadOut.choose(1)"><div class="grid-title"><div class="circle-grid tech" style="background-image: linear-gradient(lightyellow, yellow);"></div> &nbsp; Speed Boost</div>Increase speed by 5%</div>`;
// text += `<div class="choose-grid-module" onclick="powerUps.loadOut.choose(2)"><div class="grid-title"><div class="circle-grid tech" style="background-image: linear-gradient(gray, lightgray);"></div> &nbsp; Defense Boost</div>Reduce damage by 5%</div>`;
// text += `<div class="choose-grid-module" onclick="powerUps.loadOut.choose(3)"><div class="grid-title"><div class="circle-grid tech" style="background-image: linear-gradient(red, orange);"></div> &nbsp; Damage Boost</div>Increase damage by 10%</div>`;
// if(powerUps.pass == true) {
// text += `<div disabled class="choose-grid-module" onclick="powerUps.loadOut.choose(4)"><div class="grid-title"><div class="circle-grid tech" style="background-image: radial-gradient(black, gray);"></div> &nbsp; Blade of Sal</div>Press Shift to summon the <b style="color: ${m.eyeFillColor};">Mythical</b> <em style="color: lightblue; text-shadow: ${m.eyeFillColor} 0px 0 5px;">Las Slayer</em><div>Drains <strong class='color-f'>Energy</strong></div></div>`;
// }
// document.getElementById("choose-grid").innerHTML = text;
// powerUps.showDraft();//no known bugs ig idk, im keep this as it is
// }
// },
// choose(index) {
// if(index == 1) {
// tech.squirrelFx += 0.25;
// tech.squirrelJump += 0.1;
// m.setMovement();
// powerUps.endDraft("buff");
// } else if(index == 2) {
// simulation.dmgScale *= 0.95;
// powerUps.endDraft("buff");
// } else if(index == 3) {
// m.dmgScale *= 1.1;
// powerUps.endDraft("buff");
// } else if(index == 4) { //sword!
// powerUps.pass = false;
// addEventListener("keydown", function(event) {
// if(event.key == "Shift" && powerUps.activated == false) {
// sword()
// powerUps.activated = true;
// } else if(event.key == "Shift" && powerUps.activated == true) {
// for(let i = 0; i < mob.length; i++) {
// if(mob[i].isSword) {
// mob[i].death()
// }
// powerUps.activated = false;
// }
// }
// })
// powerUps.endDraft("buff");
// }
// }
// }
// }
// Object.assign(powerUps, loadOut)
const restoreBoss = function (x, y, radius = 30) { //ATTENTION LANDGREEN: RESTOREBOSS WILL NOT DROP ANY TECH, NOR WILL THERE BE ANY IN THE MAP. DO NOT ADD ANY TECH TO MY MAP
mobs.spawn(x, y, 8, radius, 'transparent');
let me = mob[mob.length - 1];
me.stroke = 'transparent';
let aim = '#FFFFFF';
me.accelMag = 0.0006
me.isBoss = true;
me.restoreBoss = true;
me.frictionStatic = 0;
me.friction = 0;
me.seeAtDistance2 = 20000000 //14000 vision range
me.vertices = Matter.Vertices.rotate(me.vertices, Math.PI, me.position); //make the pointy side of triangle the front
Matter.Body.rotate(me, Math.random() * Math.PI * 2);
me.showHealthBar = false
Matter.Body.setDensity(me, m.maxHealth / (simulation.difficulty < 5 ? 0.5 : simulation.difficulty / simulation.difficultyMode))
me.seePlayerFreq = 0
me.swordDamage = 0.025 * simulation.dmgScale
me.collisionFilter.mask = cat.bullet | cat.player | cat.body | cat.map | cat.mob | cat.mobBullet
me.repulsionRange = 500000;
me.isDropPowerUp = false;
//Matter.Body.setVelocity(me, { x: 10 * (Math.random() - 0.5), y: 10 * (Math.random() - 0.5) });
let index = 0;
me.curlRange = 10000; //xd
me.fieldPhase = 0;
me.energy = 1;
me.maxEnergy = 1;
me.immuneCycle = 0;
me.cycle = 0;
// me.onDeath = function() {
// powerUps.spawn(this.position.x, this.position.y, "loadOut");
// }
for (let i = 0; i < b.totalBots(); i++) { //normal orbitals look too boring, so...
orbital(me, 190 + 130 * tech.isOrbitBotUpgrade, (index / b.totalBots()) * 2 * Math.PI, 0.05, Math.floor(Math.sin(simulation.cycle / 10) * 100)); //who, radius, phase, speed
index++;
}
me.do = function () {
if (this.position.x > -11500 && this.position.x < 10510) {// doesn't get one tapped by the elevator
me.collisionFilter.mask = cat.bullet | cat.player | cat.map | cat.mob | cat.mobBullet;
} else {
me.collisionFilter.mask = cat.bullet | cat.player | cat.body | cat.map | cat.mob | cat.mobBullet;
}
this.cycle++;
if ((Matter.Query.ray(map, player.position, this.position).length === 0) == false) {
this.seePlayer.recall = null;
}
if (this.seePlayer.recall) { //fields
if (m.fieldMode == 0 && this.distanceToPlayer2() < 200000) {
if (Vector.magnitude(Vector.sub(m.pos, this.position)) - this.radius < m.fieldRange) {
this.pushM();
}
this.drawField();
// this.repel();
}
if (m.fieldMode == 2) {
if (this.distanceToPlayer2() < 200000) {
if (Vector.magnitude(Vector.sub(m.pos, this.position)) - this.radius < m.fieldRange) {
this.pushM();
}
this.drawField()
}
if (tech.isPerfectBrake) { //cap player and bullet speed around restoreBoss //mobs basically can't hit you when you have this, so to make it fair...
const wave = Math.cos(m.cycle * 0.022);
const range = 200 + 140 * wave + 150 * m.energy
const distance = Vector.magnitude(Vector.sub(this.position, m.pos))
const cap = this.immuneCycle > this.cycle ? 8 : 4
if (distance < range) {
if (player.speed > cap && Vector.dot(player.velocity, Vector.sub(this.position, m.pos)) > 0) { // if velocity is directed towards player
Matter.Body.setVelocity(player, Vector.mult(Vector.normalise(player.velocity), cap)); //set velocity to cap, but keep the direction
}
}
for (let i = 0; i < bullet.length; i++) {
const distance2 = Vector.magnitude(Vector.sub(this.position, bullet[i].position))
if (distance2 < range) {
if (bullet[i].speed > cap && Vector.dot(bullet[i].velocity, Vector.sub(this.position, bullet[i].position)) > 0) { // if velocity is directed towards player
Matter.Body.setVelocity(bullet[i], Vector.mult(Vector.normalise(bullet[i].velocity), cap)); //set velocity to cap, but keep the direction
}
}
}
ctx.beginPath();
ctx.arc(this.position.x, this.position.y, range, 0, 2 * Math.PI);
ctx.fillStyle = "rgba(0,0,0,0.08)";
ctx.fill();
}
}
if (m.fieldMode == 5 && this.distanceToPlayer2() < 200000) {
this.laserSword(this.position, Math.atan2(this.seePlayer.position.y - this.position.y, this.seePlayer.position.x - this.position.x), 500 * Math.random());
}
if (m.fieldMode == 9) {
if (this.distanceToPlayer2() < 300000) {
this.teleportAway() //blink but reversed
}
}
}
if (m.immuneCycle > m.cycle) {
me.damageReduction = 0;
} else {
me.damageReduction = 1;
}
this.repulsion();
this.seePlayerCheck();
this.attraction();
this.lostPlayer();
if (this.speed > 10) { // speed cap
Matter.Body.setVelocity(this, {
x: this.velocity.x * 0.99,
y: this.velocity.y * 0.99
});
}
if (this.seePlayer.recall) {
const angle = Math.atan2(this.position.y - player.position.y, this.position.x - player.position.x);
let positionR = { x: 500 * Math.cos(angle) + mob[0].position.x, y: 500 * Math.sin(angle) + mob[0].position.y };
let isWall = (Matter.Query.ray(map, mob[0].position, positionR).length === 0);
let isBlock = (Matter.Query.ray(body, mob[0].position, positionR).length === 0);
if (isWall == false || isBlock == false) {
this.force.x -= Math.cos(angle) * 10;
this.force.y -= Math.sin(angle) * 10;
}
}
if (this.seePlayer.recall) {
const angle = Math.atan2(this.position.y - player.position.y, this.position.x - player.position.x) - Math.PI * 1.5;
let positionR = { x: 5000 * Math.cos(angle) + mob[0].position.x, y: 5000 * Math.sin(angle) + mob[0].position.y };
let isBullet = (Matter.Query.ray(bullet, mob[0].position, positionR).length === 0);
if (isBullet == false) {
this.force.x -= Math.cos(angle) * 30;
this.force.y -= Math.sin(angle) * 30;
}
}
if (this.seePlayer.recall) {
const angle = Math.atan2(this.position.y - player.position.y, this.position.x - player.position.x) - Math.PI * 2.5;
let positionR = { x: 5000 * Math.cos(angle) + mob[0].position.x, y: 5000 * Math.sin(angle) + mob[0].position.y };
let isBullet = (Matter.Query.ray(bullet, mob[0].position, positionR).length === 0);
if (isBullet == false) {
this.force.x -= Math.cos(angle) * 30;
this.force.y -= Math.sin(angle) * 30;
}
}
if (this.seePlayer.recall) {
const h = this.radius * 0.3;
const w = this.radius * 2;
const x = this.position.x - w / 2;
const y = this.position.y - w * 0.7;
ctx.fillStyle = "rgba(10, 10, 10, 0.3)";
ctx.fillRect(x, y, w, h);
ctx.fillStyle = "rgba(0,0,0,0.7)";
ctx.fillRect(x, y, w * this.energy, h);
}
if (this.health < 1) {
this.health += 0.0001; //regen
} else if (this.health < 1) {
this.health += 0.00005; //reduced regen
}
if (this.energy < 0) {//energy thingy
this.energy = 0;
} else if (this.energy > this.maxEnergy) {
this.energy = this.maxEnergy;
} else if (this.energy < this.maxEnergy) {
this.energy += 0.001;
}
ctx.save()
ctx.translate(this.position.x, this.position.y)
ctx.rotate(Math.atan2(this.seePlayer.position.y - this.position.y, this.seePlayer.position.x - this.position.x))
if (this.seePlayer.recall > 0 && this.distanceToPlayer2() > this.repulsionRange) {
ctx.globalAlpha = (this.immuneCycle < this.cycle) ? 1 : 0.5
var grd2 = ctx.createLinearGradient(0, 0, -150, 0);
// grd2.addColorStop(0, 'rgba(255, 255, 155, 0.8)');
// grd2.addColorStop(1, 'rgba(255, 200, 0, 0.1)');
grd2.addColorStop(0, 'rgba(150, 200, 255, 0.7)');
grd2.addColorStop(1, 'rgba(150, 200, 255, 0)');
ctx.fillStyle = grd2;
ctx.beginPath();
ctx.moveTo(-18, -25);
//10 * (Math.random() - 0.5), 10 * (Math.random() - 0.5)
ctx.lineTo(-18, 25);
ctx.lineTo(-50 - 100 * Math.random(), 0);
ctx.fill();
} else if (this.distanceToPlayer2() < this.repulsionRange) {
ctx.globalAlpha = (this.immuneCycle < this.cycle) ? 1 : 0.5
var grd2 = ctx.createLinearGradient(0, 0, 80, 0);
grd2.addColorStop(0, 'rgba(150, 200, 255, 0.7)');
grd2.addColorStop(1, 'rgba(150, 200, 255, 0)');
ctx.fillStyle = grd2;
ctx.beginPath();
ctx.moveTo(20, -16);
//10 * (Math.random() - 0.5), 10 * (Math.random() - 0.5)
ctx.lineTo(20, 16);
ctx.lineTo(35 + 43 * Math.random(), 0);
ctx.fill();
}
ctx.restore()
ctx.save()
ctx.globalAlpha = (this.immuneCycle < this.cycle) ? 1 : 0.5
ctx.translate(this.position.x, this.position.y)
ctx.rotate(Math.atan2(this.seePlayer.position.y - this.position.y, this.seePlayer.position.x - this.position.x))
if (this.health > 0.5) {
ctx.beginPath();
ctx.arc(0, 0, 30, 0, 2 * Math.PI);
ctx.fillStyle = m.bodyGradient
ctx.fill();
ctx.arc(15, 0, 4, 0, 2 * Math.PI);
ctx.strokeStyle = "#333";
ctx.lineWidth = 2;
ctx.stroke();
ctx.beginPath(); //eye
ctx.arc(15, 0, 3.5, 0, 2 * Math.PI);
ctx.fillStyle = `rgba(255, 0, 0, ${this.health * this.health})`;
ctx.fill()
ctx.restore()
} else {
ctx.beginPath();
ctx.arc(0, 0, 30, 0, 2 * Math.PI);
ctx.fillStyle = m.bodyGradient
ctx.fill();
ctx.strokeStyle = "#333";
ctx.lineWidth = 2;
if (!(Math.atan2(this.seePlayer.position.y - this.position.y, this.seePlayer.position.x - this.position.x) > -Math.PI / 2 && Math.atan2(this.seePlayer.position.y - this.position.y, this.seePlayer.position.x - this.position.x) < Math.PI / 2)) ctx.scale(1, -1); //here is the flip
ctx.stroke();
ctx.beginPath();
ctx.arc(2, -6, 7, 0, 2 * Math.PI);
ctx.stroke();
ctx.beginPath();
ctx.arc(25, -6, 7, 0.25 * Math.PI, 1.6 * Math.PI);
ctx.stroke();
ctx.beginPath();
ctx.arc(2, -10, 9, 1.25 * Math.PI, 1.75 * Math.PI);
ctx.stroke();
ctx.beginPath();
ctx.arc(25, -10, 9, 1.25 * Math.PI, 1.4 * Math.PI);
ctx.stroke();
ctx.beginPath();
ctx.arc(18, 13, 10, 0, 2 * Math.PI);
ctx.fillStyle = m.bodyGradient;
ctx.fill();
ctx.stroke();
ctx.beginPath();
ctx.arc(18, 13, 6, 0, 2 * Math.PI);
ctx.fillStyle = "#555";
ctx.fill();
ctx.stroke();
ctx.beginPath();
ctx.arc(3, -6, 3, 0, 2 * Math.PI);
ctx.fill();
ctx.stroke();
ctx.beginPath();
ctx.arc(26, -6, 3, 0, 2 * Math.PI);
ctx.fill();
ctx.stroke();
ctx.restore();
}
if (m.fieldMode == 1) { //render over I think
if (this.energy > 0.1) {
this.harmonic3Phase();
}
}
if (m.fieldMode == 7) {
this.locatePlayer();
}
if (m.fieldMode == 8) {
me.pilotWave()
}
if (m.fieldMode == 3) {
const angle = Math.atan2(this.seePlayer.position.y - this.position.y, this.seePlayer.position.x - this.position.x);
me.damageReduction = 0.5;
me.accelMag = 0.0012;
if (!(simulation.cycle % Math.floor(100 + 90 * Math.random() * simulation.CDScale))) {
this.diveAttack()
}
ctx.beginPath();
ctx.arc(this.position.x, this.position.y, 1000, 0, 2 * Math.PI);
ctx.fillStyle = "#f5f5ff";
ctx.strokeStyle = "#f5f5ff55";
ctx.setLineDash([125 * Math.random(), 125 * Math.random()]);
ctx.globalCompositeOperation = "difference";
ctx.fill();
ctx.globalCompositeOperation = "source-over";
ctx.stroke()
ctx.setLineDash([]);
} else {
me.accelMag = 0.0006;
}
if (this.immuneCycle > this.cycle) {
this.damageReduction = 0;
} else {
if (m.fieldMode == 3) {
this.damageReduction = 0.5;
} else {
this.damageReduction = 1;
}
}
if (m.fieldMode == 6) {
this.timeAttack();
ctx.globalCompositeOperation = "saturation"
ctx.fillStyle = "#ccc";
ctx.fillRect(-50000, -50000, 100000, 100000)
ctx.globalCompositeOperation = "source-over"
// stop time
// m.isBodiesAsleep = true;
// function sleep(who) {
// for (let i = 0, len = who.length; i < len; ++i) {
// if (!who[i].isSleeping) {
// who[i].storeVelocity = who[i].velocity
// who[i].storeAngularVelocity = who[i].angularVelocity
// }
// Matter.Sleeping.set(who[i], true)
// }
// }
// sleep(mob);
// sleep(body);
// sleep(bullet);
// sleep([player]);
// simulation.cycle--;
}
if (this.seePlayer.recall) { //fields
this.gun()
}
}
me.laserSword = function (where, angle, length) {
const sub = Vector.sub(this.seePlayer.position, this.position)
const unit = Vector.normalise(sub)
const path = [{
x: this.position.x + 20 * Math.cos(this.angle),
y: this.position.y + 20 * Math.sin(this.angle)
},
{
x: this.position.x + (120 + 400) * Math.sqrt(Math.random()) * Math.cos(this.angle),
y: this.position.y + (120 + 400) * Math.sqrt(Math.random()) * Math.sin(this.angle)
}
];
const vertexCollision = function (v1, v1End, domain) {
for (let i = 0; i < domain.length; ++i) {
let vertices = domain[i].vertices;
const len = vertices.length - 1;
for (let j = 0; j < len; j++) {
results = simulation.checkLineIntersection(v1, v1End, vertices[j], vertices[j + 1]);
if (results.onLine1 && results.onLine2) {
const dx = v1.x - results.x;
const dy = v1.y - results.y;
const dist2 = dx * dx + dy * dy;
if (dist2 < best.dist2 && (!domain[i].mob || domain[i].alive)) best = { x: results.x, y: results.y, dist2: dist2, who: domain[i], v1: vertices[j], v2: vertices[j + 1] };
}
}
results = simulation.checkLineIntersection(v1, v1End, vertices[0], vertices[len]);
if (results.onLine1 && results.onLine2) {
const dx = v1.x - results.x;
const dy = v1.y - results.y;
const dist2 = dx * dx + dy * dy;
if (dist2 < best.dist2) best = { x: results.x, y: results.y, dist2: dist2, who: domain[i], v1: vertices[0], v2: vertices[len] };
}
}
};
best = { x: null, y: null, dist2: Infinity, who: null, v1: null, v2: null };
const look = { x: where.x + length * Math.cos(angle), y: where.y + length * Math.sin(angle) };
// vertexCollision(where, look, body); // vertexCollision(where, look, mob);
vertexCollision(where, look, map);
if (!m.isCloak) vertexCollision(where, look, [player]);
if (best.who && (best.who === player) && m.immuneCycle < m.cycle) {
m.immuneCycle = m.cycle + m.collisionImmuneCycles; //player is immune to damage for an extra second
m.damage(this.swordDamage);
simulation.drawList.push({ //add dmg to draw queue
x: best.x,
y: best.y,
radius: this.swordDamage * 1500,
color: "rgba(0,0,0,0.5)",
time: 20
});
}
if (best.dist2 === Infinity) best = look;
ctx.beginPath(); //draw beam
ctx.moveTo(where.x, where.y);
ctx.lineTo(best.x, best.y);
ctx.strokeStyle = "#000";
ctx.lineWidth = 1.5;
ctx.setLineDash([70 + 300 * Math.random(), 55 * Math.random()]);
ctx.stroke(); // Draw it
ctx.setLineDash([]);
ctx.lineWidth = 20;
ctx.strokeStyle = "rgba(0,0,0,0.07)";
ctx.stroke(); // Draw it
const Dx = Math.cos(Math.atan2(this.seePlayer.position.y - this.position.y, this.seePlayer.position.x - this.position.x));
const Dy = Math.sin(Math.atan2(this.seePlayer.position.y - this.position.y, this.seePlayer.position.x - this.position.x));
let xElec = this.position.x + 40 * Dx;
let yElec = this.position.y + 40 * Dy;
ctx.beginPath();
ctx.moveTo(xElec, yElec);
const step = 40
for (let i = 0; i < 6; i++) {
xElec += step * (Dx + 1.5 * (Math.random() - 0.5))
yElec += step * (Dy + 1.5 * (Math.random() - 0.5))
ctx.lineTo(xElec, yElec);
}
ctx.strokeStyle = "#000";
ctx.lineWidth = 1.5;
ctx.setLineDash([70 + 300 * Math.random(), 55 * Math.random()]);
ctx.stroke(); // Draw it
ctx.setLineDash([]);
}
me.drawField = function () {
if (m.fieldMode != 2) {
const angle = Math.atan2(this.seePlayer.position.y - this.position.y, this.seePlayer.position.x - this.position.x)
const range = m.fieldRange;
ctx.save()
ctx.beginPath();
ctx.fillStyle = "rgba(0,0,0," + Math.min(0.6, (0.04 + m.energy * (0.1 + 0.11 * Math.random()))) + ")";
ctx.arc(this.position.x, this.position.y, range, angle - Math.PI * m.fieldArc, angle + Math.PI * m.fieldArc, false);
ctx.lineWidth = 2;
ctx.stroke();
let eye = 13;
if (m.fieldMode == 2) {
eye = 30
}
let aMag = 0.75 * Math.PI * m.fieldArc
let a = angle + aMag
let cp1x = this.position.x + 0.6 * range * Math.cos(a)
let cp1y = this.position.y + 0.6 * range * Math.sin(a)
ctx.quadraticCurveTo(cp1x, cp1y, this.position.x + eye * Math.cos(angle), this.position.y + eye * Math.sin(angle))
a = angle - aMag
cp1x = this.position.x + 0.6 * range * Math.cos(a)
cp1y = this.position.y + 0.6 * range * Math.sin(a)
ctx.quadraticCurveTo(cp1x, cp1y, this.position.x + 1 * range * Math.cos(angle - Math.PI * m.fieldArc), this.position.y + 1 * range * Math.sin(angle - Math.PI * m.fieldArc))
ctx.fill();
// ctx.lineTo(this.position.x + eye * Math.cos(angle), this.position.y + eye * Math.sin(angle));
//draw random lines in field for cool effect
let offAngle = angle + 1.7 * Math.PI * m.fieldArc * (Math.random() - 0.5);
ctx.beginPath();
eye = 15;
ctx.moveTo(this.position.x + eye * Math.cos(angle), this.position.y + eye * Math.sin(angle));
ctx.lineTo(this.position.x + range * Math.cos(offAngle), this.position.y + range * Math.sin(offAngle));
ctx.strokeStyle = "rgba(0,0,0,0.6)";
ctx.lineWidth = 1;
ctx.stroke();
ctx.restore()
} else {
ctx.save()
ctx.beginPath();
const wave = Math.cos(m.cycle * 0.022);
const angle = Math.atan2(this.seePlayer.position.y - this.position.y, this.seePlayer.position.x - this.position.x)
ctx.arc(this.position.x, this.position.y, m.fieldRange, angle - Math.PI * m.fieldArc, angle + Math.PI * m.fieldArc, false);
ctx.lineWidth = 2.5 - 1.5 * wave;
ctx.stroke();
const curve = 0.57 + 0.04 * wave
const aMag = (1 - curve * 1.2) * Math.PI * m.fieldArc
let a = angle + aMag
let cp1x = this.position.x + curve * m.fieldRange * Math.cos(a)
let cp1y = this.position.y + curve * m.fieldRange * Math.sin(a)
ctx.quadraticCurveTo(cp1x, cp1y, this.position.x + 30 * Math.cos(angle), this.position.y + 30 * Math.sin(angle))
a = angle - aMag
cp1x = this.position.x + curve * m.fieldRange * Math.cos(a)
cp1y = this.position.y + curve * m.fieldRange * Math.sin(a)
ctx.quadraticCurveTo(cp1x, cp1y, this.position.x + 1 * m.fieldRange * Math.cos(angle - Math.PI * m.fieldArc), this.position.y + 1 * m.fieldRange * Math.sin(angle - Math.PI * m.fieldArc))
ctx.fill();
ctx.restore()
}
}
me.pushM = function () {
const unit = Vector.normalise(Vector.sub(this.position, player.position))
if (tech.blockDmg) {
Matter.Body.setVelocity(player, { x: 0.5 * player.velocity.x, y: 0.5 * player.velocity.y });
//draw electricity
const step = 40
ctx.beginPath();
for (let i = 0, len = 0.8 * tech.blockDmg; i < len; i++) {
let x = this.position.x - 20 * unit.x;
let y = this.position.y - 20 * unit.y;
ctx.moveTo(x, y);
for (let i = 0; i < 8; i++) {
x += step * (-unit.x + 1.5 * (Math.random() - 0.5))
y += step * (-unit.y + 1.5 * (Math.random() - 0.5))
ctx.lineTo(x, y);
}
}
if (m.immuneCycle < m.cycle) {
m.immuneCycle = m.cycle + m.collisionImmuneCycles
m.damage(0.025 * simulation.dmgScale)
}
ctx.lineWidth = 3;
ctx.strokeStyle = "#000";
ctx.stroke();
}
const massRoot = Math.sqrt(Math.min(12, Math.max(0.15, player.mass))); // masses above 12 can start to overcome the push back //idk
Matter.Body.setVelocity(player, {
x: this.velocity.x - (15 * unit.x) / massRoot,
y: this.velocity.y - (15 * unit.y) / massRoot
});
}
me.diveAttack = function () {
const forceMag = this.accelMag * this.mass;
const angle = Math.atan2(this.seePlayer.position.y - this.position.y, this.seePlayer.position.x - this.position.x);
this.force.x += 150 * forceMag * Math.cos(angle);
this.force.y += 150 * forceMag * Math.sin(angle);
ctx.beginPath()
ctx.moveTo(this.position.x + Math.sin(angle), this.position.y + Math.cos(angle))
ctx.lineTo(this.seePlayer.position.x, this.seePlayer.position.y)
aim = '#000000';
ctx.stroke()
}
me.phase = 2 * Math.PI * Math.random();
me.index2 = 0;
me.pilotWave = function () {
const rotate = this.cycle * 0.008;
this.fieldPhase += 0.002;
const off1 = 1 + 0.01 * Math.sin(this.fieldPhase);
const off2 = 1 - 0.01 * Math.cos(this.fieldPhase);
ctx.save()
ctx.beginPath();
ctx.ellipse(player.position.x, player.position.y, 1.2 * 200 * off1, 1.2 * 150 * off2, rotate, 0, 2 * Math.PI);
ctx.globalCompositeOperation = "exclusion"; //"exclusion" "difference";
ctx.fillStyle = "#ffffff"; //"#eef";
ctx.fill();
ctx.globalCompositeOperation = "source-over";
ctx.beginPath();
ctx.ellipse(player.position.x, player.position.y, 1.2 * 200 * off1, 1.2 * 150 * off2, rotate, 0, 2 * Math.PI * m.energy / m.maxEnergy);
ctx.strokeStyle = "#000000";
ctx.lineWidth = 4;
ctx.stroke();
ctx.restore()
const range = this.curlRange / 15, mag = -50;
const angle = Math.atan2(this.seePlayer.position.y - this.position.y, this.seePlayer.position.x - this.position.x);
const applyCurl = function (center, array, isAntiGravity = true) {
for (let i = 0; i < array.length; ++i) {
if (!array[i].isNotHoldable) {
const sub = Vector.sub(center, array[i].position)
const radius2 = Vector.magnitudeSquared(sub);
//if too close, like center mob or shield, don't curl // if too far don't curl
if (radius2 < range * range && radius2 > 10000) {
const curlVector = Vector.mult(Vector.perp(Vector.normalise(sub)), mag)
//apply curl force
if (array[i].isMobBullet) {
Matter.Body.setVelocity(array[i], {
x: array[i].velocity.x * 0.97 + curlVector.x * 0.06 - (Math.cos(angle) * 5),
y: array[i].velocity.y * 0.97 + curlVector.y * 0.06 - (Math.sin(angle) * 5)
})
} else {
Matter.Body.setVelocity(array[i], {
x: array[i].velocity.x * 0.94 + curlVector.x * 0.06 - (Math.cos(angle) * 5),
y: array[i].velocity.y * 0.94 + curlVector.y * 0.06 - (Math.sin(angle) * 5)
})
}
if (isAntiGravity) array[i].force.y -= 0.8 * simulation.g * array[i].mass
}
}
}
}
applyCurl(this.position, [player]);
}
me.teleportAway = function () {//hehe
ctx.beginPath();
ctx.moveTo(this.position.x, this.position.y);
if (this.seePlayer.recall && !(simulation.cycle % 17)) {
const dist = Vector.sub(this.position, this.seePlayer.position);
const distMag = Vector.magnitude(dist);
const unitVector = Vector.normalise(dist);
const rando = (Math.random() - 0.5) * 50;
if (distMag < 20000) {
Matter.Body.translate(this, Vector.mult(unitVector, distMag + rando));
} else {
Matter.Body.translate(this, Vector.mult(unitVector, 20000 + rando));
}
}
ctx.lineTo(this.position.x, this.position.y);
ctx.lineWidth = radius * 2;
ctx.strokeStyle = "rgba(0,0,0,0.08)";
ctx.stroke();
if (!this.seePlayer.yes) {
ctx.beginPath();
ctx.moveTo(this.position.x, this.position.y);
if (this.seePlayer.recall && !(simulation.cycle % 17)) {
const dist = Vector.sub(this.seePlayer.position, this.position);
const distMag = Vector.magnitude(dist);
const unitVector = Vector.normalise(dist);
const rando = (Math.random() - 0.5) * 50;
if (distMag < 200000) {
Matter.Body.translate(this, Vector.mult(unitVector, distMag + rando));
} else {
Matter.Body.translate(this, Vector.mult(unitVector, 200000 + rando));
}
}
ctx.lineTo(this.position.x, this.position.y);
ctx.lineWidth = radius * 2;
ctx.strokeStyle = "rgba(0,0,0,0.08)";
ctx.stroke();
}
}
me.timeAttack = function () {
if (this.seePlayer.recall && !(simulation.cycle % 30) || this.distanceToPlayer2() < 300) {
requestAnimationFrame(() => {
simulation.timePlayerSkip(45)
simulation.loop(); //ending with a wipe and normal loop fixes some very minor graphical issues where things are draw in the wrong locations
}); //wrapping in animation frame prevents errors, probably
}
}
me.harmonic3Phase = function () { //normal standard 3 different 2-d circles
if (tech.harmonics === 2) {
const fieldRange1 = (0.75 + 0.3 * Math.cos(m.cycle / 23)) * m.fieldRange * m.harmonicRadius
const fieldRange2 = (0.68 + 0.37 * Math.cos(m.cycle / 37)) * m.fieldRange * m.harmonicRadius
const fieldRange3 = (0.7 + 0.35 * Math.cos(m.cycle / 47)) * m.fieldRange * m.harmonicRadius
const netfieldRange = Math.max(fieldRange1, fieldRange2, fieldRange3)
ctx.fillStyle = "rgba(0,0,0," + Math.min(0.6, (0.04 + m.energy * (0.1 + 0.11 * Math.random()))) + ")";
ctx.beginPath();
ctx.arc(this.position.x, this.position.y, fieldRange1, 0, 2 * Math.PI);
ctx.fill();
ctx.beginPath();
ctx.arc(this.position.x, this.position.y, fieldRange2, 0, 2 * Math.PI);
ctx.fill();
ctx.beginPath();
ctx.arc(this.position.x, this.position.y, fieldRange3, 0, 2 * Math.PI);
ctx.fill();
//360 block
if (Vector.magnitude(Vector.sub(player.position, this.position)) - this.radius < netfieldRange) {
me.pushM();
}
for (let i = 0; i < bullet.length; i++) {
if (Vector.magnitude(Vector.sub(bullet[i].position, this.position)) - this.radius < netfieldRange) {
const dx = bullet[i].position.x - this.position.x;
const dy = bullet[i].position.y - this.position.y;
const dist = Math.sqrt(dx * dx + dy * dy);
if (dist < m.fieldRange) {
const angle = Math.atan2(dy, dx);
const mag = (1500 * bullet[i].mass * simulation.g) / dist;
bullet[i].force.x += mag * Math.cos(angle);
bullet[i].force.y += mag * Math.sin(angle);
}
this.energy -= 0.0012;
}
}
} else {
const rotation = simulation.cycle * 0.0031
const phase = simulation.cycle * 0.023
const radius = m.fieldRange * m.harmonicRadius
ctx.lineWidth = 1;
ctx.strokeStyle = "rgba(0,0,0,0.5)"
ctx.fillStyle = `rgba(0,0,0,${Math.min(0.6, m.energy * (0.11 + 0.1 * Math.random()) * (3 / tech.harmonics))})`;
// ctx.fillStyle = "rgba(0,0,0," + Math.min(0.7, m.energy * (0.22 - 0.01 * tech.harmonics) * (0.5 + 0.5 * Math.random())) + ")";
for (let i = 0; i < tech.harmonics; i++) {
ctx.beginPath();
ctx.ellipse(this.position.x, this.position.y, radius * Math.abs(Math.sin(phase + i / tech.harmonics * Math.PI)), radius, rotation + i / tech.harmonics * Math.PI, 0, 2 * Math.PI);
ctx.fill();
ctx.stroke();
}
//360 block
if (Vector.magnitude(Vector.sub(player.position, this.position)) - this.radius < radius) {
me.pushM();
}
for (let i = 0; i < bullet.length; i++) {
if (Vector.magnitude(Vector.sub(bullet[i].position, this.position)) - this.radius < radius) {
const dx = bullet[i].position.x - this.position.x;
const dy = bullet[i].position.y - this.position.y;
const dist = Math.sqrt(dx * dx + dy * dy);
if (dist < m.fieldRange) {
const angle = Math.atan2(dy, dx);
const mag = (1500 * bullet[i].mass * simulation.g) / dist;
bullet[i].force.x += mag * Math.cos(angle);
bullet[i].force.y += mag * Math.sin(angle);
}
this.energy -= 0.0012;
}
}
}
}
me.railGun = function () {
const angle = Math.atan2(this.seePlayer.position.y - this.position.y, this.seePlayer.position.x - this.position.x);
const X = this.position.x
const Y = this.position.y
const unitVector = { x: Math.cos(angle), y: Math.sin(angle) }
const unitVectorPerp = Vector.perp(unitVector)
function magField(mag, arc) {
ctx.moveTo(X, Y);
ctx.bezierCurveTo(
X + unitVector.x * mag, Y + unitVector.y * mag,
X + unitVector.x * mag + unitVectorPerp.x * arc, Y + unitVector.y * mag + unitVectorPerp.y * arc,
X + unitVectorPerp.x * arc, Y + unitVectorPerp.y * arc)
ctx.bezierCurveTo(
X - unitVector.x * mag + unitVectorPerp.x * arc, Y - unitVector.y * mag + unitVectorPerp.y * arc,
X - unitVector.x * mag, Y - unitVector.y * mag,
X, Y)
}
ctx.fillStyle = `rgba(50,20,100,0.05)`;
const magSize = 8 * c * tech.railChargeRate ** 3
const arcSize = 6 * c * tech.railChargeRate ** 3
for (let i = 3; i < 7; i++) {
const MAG = magSize * i * i * (0.93 + 0.07 * Math.random())
const ARC = arcSize * i * i * (0.93 + 0.07 * Math.random())
ctx.beginPath();
magField(MAG, ARC)
magField(MAG, -ARC)
ctx.fill();
}
}
me.waves = [];
me.doLongitudinal = function () {
if (!m.isBodiesAsleep) {
ctx.strokeStyle = "rgba(0,0,0,0.6)" //"000";
ctx.lineWidth = 2 * tech.wavePacketDamage
ctx.beginPath();
// const end = 1100 * tech.bulletsLastLonger / Math.sqrt(tech.waveReflections * 0.5) //should equal about 1767
const end = 1100 * tech.bulletsLastLonger * Math.pow(0.93, tech.waveReflections) //should equal about 1767
const damage = 0.0005 * simulation.dmgScale//normal damage for m basically shreds m, so had to nerf this
for (let i = this.waves.length - 1; i > -1; i--) {
const v1 = Vector.add(this.waves[i].position, Vector.mult(this.waves[i].unit1, this.waves[i].radius))
const v2 = Vector.add(this.waves[i].position, Vector.mult(this.waves[i].unit2, this.waves[i].radius))
//draw wave
ctx.moveTo(v1.x, v1.y)
ctx.arc(this.waves[i].position.x, this.waves[i].position.y, this.waves[i].radius, this.waves[i].angle, this.waves[i].angle + this.waves[i].arc);
//using small angle linear approximation of circle arc, this will not work if the arc gets large // https://stackoverflow.com/questions/13652518/efficiently-find-points-inside-a-circle-sector
let hits = Matter.Query.ray([player], v1, v2, 50) //Matter.Query.ray(bodies, startPoint, endPoint, [rayWidth])
for (let j = 0, len = Math.min(30, hits.length); j < len; j++) {
player.force.x += 0.01 * (Math.random() - 0.5) * player.mass
player.force.y += (0.01 * (Math.random() - 0.5) - simulation.g * 0.25) * player.mass //remove force of gravity
Matter.Body.setVelocity(player, { //friction
x: player.velocity.x * 0.95,
y: player.velocity.y * 0.95
});
m.damage(damage)
}
hits = Matter.Query.ray(body, v1, v2, 50)
for (let j = 0, len = Math.min(30, hits.length); j < len; j++) {
const who = hits[j].body
//make them shake around
who.force.x += 0.01 * (Math.random() - 0.5) * who.mass
who.force.y += (0.01 * (Math.random() - 0.5) - simulation.g * 0.25) * who.mass //remove force of gravity
let vertices = who.vertices;
const vibe = 25
ctx.moveTo(vertices[0].x + vibe * (Math.random() - 0.5), vertices[0].y + vibe * (Math.random() - 0.5));
for (let j = 1; j < vertices.length; j++) {
ctx.lineTo(vertices[j].x + vibe * (Math.random() - 0.5), vertices[j].y + vibe * (Math.random() - 0.5));
}
ctx.lineTo(vertices[0].x + vibe * (Math.random() - 0.5), vertices[0].y + vibe * (Math.random() - 0.5));
if (tech.isPhononBlock && !who.isNotHoldable && who.speed < 5 && who.angularSpeed < 0.1) {
if (Math.random() < 0.5) b.targetedBlock(who, 50 - Math.min(25, who.mass * 3)) // targetedBlock(who, speed = 50 - Math.min(20, who.mass * 2), range = 1600) {
// Matter.Body.setAngularVelocity(who, (0.25 + 0.12 * Math.random()) * (Math.random() < 0.5 ? -1 : 1));
who.torque += who.inertia * 0.001 * (Math.random() - 0.5)
}
}
// ctx.stroke(); //draw vibes
this.waves[i].radius += tech.waveBeamSpeed * 1.8 * this.waves[i].expanding //expand / move
if (this.waves[i].radius > end - 30) {
this.waves[i].expanding = -1
if (this.waves[i].reflection < 1) this.waves.splice(i, 1) //end
} else if (this.waves[i].radius < 25) {
this.waves[i].expanding = 1
if (this.waves[i].reflection < 1) this.waves.splice(i, 1) //end
}
}
ctx.stroke();
}
}
me.lasers = function (where, angle) {
const vertexCollision = function (v1, v1End, domain) {
for (let i = 0; i < domain.length; ++i) {
let vertices = domain[i].vertices;
const len = vertices.length - 1;
for (let j = 0; j < len; j++) {
results = simulation.checkLineIntersection(v1, v1End, vertices[j], vertices[j + 1]);
if (results.onLine1 && results.onLine2) {
const dx = v1.x - results.x;
const dy = v1.y - results.y;
const dist2 = dx * dx + dy * dy;
if (dist2 < best.dist2 && (!domain[i].mob || domain[i].alive)) best = {
x: results.x,
y: results.y,
dist2: dist2,
who: domain[i],
v1: vertices[j],
v2: vertices[j + 1]
};
}
}
results = simulation.checkLineIntersection(v1, v1End, vertices[0], vertices[len]);
if (results.onLine1 && results.onLine2) {
const dx = v1.x - results.x;
const dy = v1.y - results.y;
const dist2 = dx * dx + dy * dy;
if (dist2 < best.dist2) best = {
x: results.x,
y: results.y,
dist2: dist2,
who: domain[i],
v1: vertices[0],
v2: vertices[len]
};
}
}
};
const seeRange = 7000;
best = {
x: null,
y: null,
dist2: Infinity,
who: null,
v1: null,
v2: null
};
const look = {
x: where.x + seeRange * Math.cos(angle),
y: where.y + seeRange * Math.sin(angle)
};
// vertexCollision(where, look, mob);
vertexCollision(where, look, map);
vertexCollision(where, look, body);
if (!m.isCloak) vertexCollision(where, look, [player]);
if (best.who && (best.who === player) && m.immuneCycle < m.cycle) {
const dmg = 0.0011 * simulation.dmgScale;
m.damage(dmg);
simulation.drawList.push({ //add dmg to draw queue
x: best.x,
y: best.y,
radius: dmg * 1500,
color: "rgba(80,0,255,0.5)",
time: 20
});
}
//draw beam
if (best.dist2 === Infinity) best = look;
ctx.moveTo(where.x, where.y);
ctx.lineTo(best.x, best.y);
ctx.lineWidth = 10;
ctx.stroke();
}
me.pulse = function (charge, angle, where = this.position) {
let best;
angle = Math.atan2(this.seePlayer.position.y - this.position.y, this.seePlayer.position.x - this.position.x)
let explosionRadius = 5.5 * charge
let range = 5000
const path = [{
x: where.x + 20 * Math.cos(angle),
y: where.y + 20 * Math.sin(angle)
},
{
x: where.x + range * Math.cos(angle),
y: where.y + range * Math.sin(angle)
}
];
const vertexCollision = function (v1, v1End, domain) {
for (let i = 0; i < domain.length; ++i) {
let vertices = domain[i].vertices;
const len = vertices.length - 1;
for (let j = 0; j < len; j++) {
results = simulation.checkLineIntersection(v1, v1End, vertices[j], vertices[j + 1]);
if (results.onLine1 && results.onLine2) {
const dx = v1.x - results.x;
const dy = v1.y - results.y;
const dist2 = dx * dx + dy * dy;
if (dist2 < best.dist2 && (!domain[i].mob || domain[i].alive)) best = {
x: results.x,
y: results.y,
dist2: dist2,
who: domain[i],
v1: vertices[j],
v2: vertices[j + 1]
};
}
}
results = simulation.checkLineIntersection(v1, v1End, vertices[0], vertices[len]);
if (results.onLine1 && results.onLine2) {
const dx = v1.x - results.x;
const dy = v1.y - results.y;
const dist2 = dx * dx + dy * dy;
if (dist2 < best.dist2) best = {
x: results.x,
y: results.y,
dist2: dist2,
who: domain[i],
v1: vertices[0],
v2: vertices[len]
};
}
}
};
//check for collisions
best = {
x: null,
y: null,
dist2: Infinity,
who: null,
v1: null,
v2: null
};
if (!best.who) {
vertexCollision(path[0], path[1], body);
vertexCollision(path[0], path[1], [player]);
vertexCollision(path[0], path[1], map);
if (best.dist2 != Infinity) { //if hitting something
path[path.length - 1] = {
x: best.x,
y: best.y
};
}
}
if (best.who) {
b.explosion(path[1], explosionRadius, "rgba(0,0,0,0)")
const off = explosionRadius * 1.2
b.explosion({ x: path[1].x + off * (Math.random() - 0.5), y: path[1].y + off * (Math.random() - 0.5) }, explosionRadius, "rgba(0,0,0,0.7)")
b.explosion({ x: path[1].x + off * (Math.random() - 0.5), y: path[1].y + off * (Math.random() - 0.5) }, explosionRadius, "rgba(0,0,0,0.7)")
}
//draw laser beam
ctx.beginPath();
ctx.moveTo(path[0].x, path[0].y);
ctx.lineTo(path[1].x, path[1].y);
if (charge > 50) {
ctx.strokeStyle = "rgba(0,0,0,0.10)"
ctx.lineWidth = 70
ctx.stroke();
}
ctx.strokeStyle = "rgba(0,0,0,0.25)"
ctx.lineWidth = 20
ctx.stroke();
ctx.strokeStyle = "#f00";
ctx.lineWidth = 4
ctx.stroke();
//draw little dots along the laser path
const sub = Vector.sub(path[1], path[0])
const mag = Vector.magnitude(sub)
for (let i = 0, len = Math.floor(mag * 0.0005 * charge); i < len; i++) {
const dist = Math.random()
simulation.drawList.push({
x: path[0].x + sub.x * dist + 10 * (Math.random() - 0.5),
y: path[0].y + sub.y * dist + 10 * (Math.random() - 0.5),
radius: 1.5 + 5 * Math.random(),
color: "rgba(0,0,0,0.5)",
time: Math.floor(9 + 25 * Math.random() * Math.random())
});
}
}
let c = 0
me.gun = function () {
if (b.activeGun == 0) {// nailgun
if (this.seePlayer.recall && !(simulation.cycle % 20)) {
this.fireDir = Vector.normalise(Vector.sub(this.seePlayer.position, this.position));
const dist = Vector.magnitudeSquared(Vector.sub(this.position, player.position));
const unit = Vector.normalise(Vector.sub(Vector.add(player.position, Vector.mult(player.velocity, Math.sqrt(dist) / 60)), this.position))
normalBullet(this.position.x, this.position.y);
const v = 10 + 8 * simulation.accelScale;
Matter.Body.setVelocity(mob[mob.length - 1], Vector.mult(unit, 0.009 * this.mass))
// Matter.Body.setVelocity(mob[mob.length - 1], {
// x: this.velocity.x + this.fireDir.x * v,
// y: this.velocity.y + this.fireDir.y * v
// });
}
}
if (b.activeGun == 1) {// shotgun
if (this.seePlayer.recall && !(simulation.cycle % 90)) {
const side = 22
for (let i = 0; i < 12; i++) {
const angle = Math.atan2(this.seePlayer.position.y - this.position.y, this.seePlayer.position.x - this.position.x);
const dir = angle + (Math.random() - 0.5) * 1
const SPEED = 52 + Math.random() * 8
normalBullet(this.position.x + 35 * Math.cos(angle) + 15 * (Math.random() - 0.5), this.position.y + 35 * Math.sin(angle) + 15 * (Math.random() - 0.5))
Matter.Body.setVelocity(mob[mob.length - 1], {
x: SPEED * Math.cos(dir),
y: SPEED * Math.sin(dir)
});
}
}
} else if (b.activeGun == 2) { // super balls
if (this.seePlayer.recall && !(simulation.cycle % 20)) {
const num = 3;
const SPREAD = 0.13;
const angle = Math.atan2(this.seePlayer.position.y - this.position.y, this.seePlayer.position.x - this.position.x);
let dir = angle - SPREAD * (num - 1) / 2;
const SPEED = 33
for (let i = 0; i < num; i++) {
ball(this.position.x + 30 * Math.cos(angle), this.position.y + 30 * Math.sin(angle))
Matter.Body.setVelocity(mob[mob.length - 1], {
x: SPEED * Math.cos(dir),
y: SPEED * Math.sin(dir)
});
dir += SPREAD
}
}
} else if (b.activeGun == 3) { // wave
this.doLongitudinal()
const halfArc = 0.275
const anglex = Math.atan2(this.seePlayer.position.y - this.position.y, this.seePlayer.position.x - this.position.x);
const angle = anglex + 0.3 * (Math.random() - 0.5)
this.waves.push({
position: {
x: this.position.x + 25 * Math.cos(anglex),
y: this.position.y + 25 * Math.sin(anglex),
},
angle: angle - halfArc, //used in drawing ctx.arc
unit1: { x: Math.cos(angle - halfArc), y: Math.sin(angle - halfArc) }, //used for collision
unit2: { x: Math.cos(angle + halfArc), y: Math.sin(angle + halfArc) }, //used for collision
arc: halfArc * 2,
radius: 25,
reflection: 0,
expanding: 1,
resonanceCount: 0
})
} else if (b.activeGun == 4) { // missiles
if (this.seePlayer.recall && !(simulation.cycle % 30)) {
this.fireDir = Vector.normalise(Vector.sub(this.seePlayer.position, this.position));
missile(this.position);
const v = 10 + 8 * simulation.accelScale + (Math.random() * 20 - Math.random() * 20);
Matter.Body.setVelocity(mob[mob.length - 1], {
x: this.velocity.x + this.fireDir.x * v,
y: this.velocity.y + this.fireDir.y * v
});
}
} else if (b.activeGun == 5) { // grenades
if (this.seePlayer.recall && !(simulation.cycle % 30)) {
this.fireDir = Vector.normalise(Vector.sub(this.seePlayer.position, this.position));
grenade(this.position.x, this.position.y)
const v = 10 + 8 * simulation.accelScale + (Math.random() * 20 - Math.random() * 20);
Matter.Body.setVelocity(mob[mob.length - 1], {
x: this.velocity.x + this.fireDir.x * v,
y: this.velocity.y + this.fireDir.y * v
});
}
} else if (b.activeGun == 6) { // spores
if (this.seePlayer.recall && !(simulation.cycle % 30)) {
this.fireDir = Vector.normalise(Vector.sub(this.seePlayer.position, this.position));
me.drop(this.position.x, this.position.y)
const v = 10 + 8 * simulation.accelScale + (Math.random() * 20 - Math.random() * 20);
Matter.Body.setVelocity(mob[mob.length - 1], {
x: this.velocity.x + this.fireDir.x * v,
y: this.velocity.y + this.fireDir.y * v
});
}
} else if (b.activeGun == 7) { // drones
ctx.save()
ctx.lineWidth = "8";
ctx.strokeStyle = "rgba(100, 0, 150, 0.1)";
ctx.beginPath();
for (let i = 0, len = bullet.length; i < len; ++i) {
const dx = bullet[i].position.x - this.position.x;
const dy = bullet[i].position.y - this.position.y;
const dist = Math.sqrt(dx * dx + dy * dy);
if (dist < 500) {
ctx.moveTo(this.position.x + dist, this.position.y);
ctx.arc(this.position.x, this.position.y, dist, 0, 2 * Math.PI)
//ctx.lineTo(bullet[i].position.x, bullet[i].position.y);
const angle = Math.atan2(dy, dx);
const mag = (1500 * bullet[i].mass * simulation.g) / (dist * 0.05);
bullet[i].force.x += mag * Math.cos(angle);
bullet[i].force.y += mag * Math.sin(angle);
}
}
ctx.stroke();
ctx.restore()
} else if (b.activeGun == 8) { // foam
if (this.seePlayer.recall && !(simulation.cycle % 1)) {
this.fireDir = Vector.normalise(Vector.sub(this.seePlayer.position, this.position));
foamBullet(this.position.x, this.position.y, 7 + Math.ceil(this.radius / 15), 69);
const v = 10 + 8 * simulation.accelScale + (Math.random() * 20 - Math.random() * 20);
Matter.Body.setVelocity(mob[mob.length - 1], {
x: this.velocity.x + this.fireDir.x * v,
y: this.velocity.y + this.fireDir.y * v
});
}
} else if (b.activeGun == 9) { // harpoon - railgun
if (c > 1) {
this.fireDir = Vector.normalise(Vector.sub(this.seePlayer.position, this.position));
railBullet(this.position.x, this.position.y);
const v = 10 + 80 * simulation.accelScale;
Matter.Body.setVelocity(mob[mob.length - 1], {
x: this.velocity.x + this.fireDir.x * v,
y: this.velocity.y + this.fireDir.y * v
});
c = 0;
} else {
c += 0.02;
this.railGun();
}
} else if (b.activeGun == 10) { // laserMines
if (this.seePlayer.recall && !(simulation.cycle % 100)) {
this.fireDir = Vector.normalise(Vector.sub(this.seePlayer.position, this.position));
me.laserMine(this.position.x, this.position.y)
const v = 10 + 8 * simulation.accelScale + (Math.random() * 20 - Math.random() * 20);
Matter.Body.setVelocity(mob[mob.length - 1], {
x: this.velocity.x + this.fireDir.x * v,
y: this.velocity.y + this.fireDir.y * v
});
}
} else if (b.activeGun == 11) { // laser - pulse
//this.lasers(this.position, Math.atan2(this.seePlayer.position.y - this.position.y, this.seePlayer.position.x - this.position.x))
//if (this.seePlayer.recall && !(simulation.cycle % 20)) {
if (c > 1) {
this.pulse(c * 100)
c = 0;
} else {
if (this.energy < 1 || this.energy > 0.5) {
c += 0.01;
ctx.beginPath();
const mag = Math.sqrt(c)
ctx.arc(this.position.x, this.position.y, c * 30, 0, 2 * Math.PI)
ctx.fillStyle = '#000000'
ctx.strokeStyle = 'transparent'
ctx.fill();
ctx.stroke();
this.energy -= 0.01;
ctx.strokeStyle = "#000000";
ctx.lineWidth = 1.5
// ctx.globalAlpha = 1;
} else {
c = 0;
this.energy += 0.1
}
}
//}
}
}
me.laserMine = function (x, y) {
mobs.spawn(x, y, 3, 20, "#000000");
let xx = mob[mob.length - 1];
xx.stroke = "#00000000";
Matter.Body.setDensity(xx, 0.000005) //one tap
xx.isUnstable = true;
xx.timeLeft = 40 + Math.floor(180 * Math.random())
xx.leaveBody = false;
xx.isDropPowerUp = false;
xx.collisionFilter.mask = cat.bullet | cat.player | cat.map
xx.showHealthBar = false;
//xx.vertices = Matter.Vertices.rotate(xx.vertices, Math.PI, xx.position);
me.onHit = function () {
this.death();
};
xx.do = function () {
this.timeLimit();
Matter.Body.setAngularVelocity(this, 0.01)
ctx.beginPath();
ctx.lineWidth = 1;
ctx.strokeStyle = "#00000000"
for (let i = 0; i < this.vertices.length; i++) {
const where = this.vertices[i]
const endPoint = Vector.add(where, Vector.mult(Vector.normalise(Vector.sub(where, this.position)), 2500))
me.lasers(this.vertices[0], this.angle + Math.PI / 3);
me.lasers(this.vertices[1], this.angle + Math.PI);
me.lasers(this.vertices[2], this.angle - Math.PI / 3);
}
ctx.strokeStyle = "black";
ctx.stroke();
ctx.save()
ctx.beginPath();
ctx.moveTo(this.vertices[0].x, this.vertices[0].y);
ctx.lineTo(this.vertices[1].x, this.vertices[1].y);
ctx.lineTo(this.vertices[2].x, this.vertices[2].y);
ctx.fillStyle = "#000000";
ctx.strokeStyle = "transparent";
ctx.fill();
ctx.closePath();
ctx.stroke();
ctx.restore()
}
}
me.seeker = function (x, y) {
mobs.spawn(x, y, sides = 5, radius = 5, "rgb(0,0,0)");
let yy = mob[mob.length - 1];
yy.stroke = "transparent";
yy.onHit = function () {
this.explode(this.mass * 20);
};
Matter.Body.setDensity(yy, 0.000015); //normal is 0.001
yy.timeLeft = 420 //* (0.8 + 0.4 * Math.random());
yy.accelMag = 0.00017 * simulation.accelScale; //* (0.8 + 0.4 * Math.random())
yy.frictionAir = 0.01 //* (0.8 + 0.4 * Math.random());
yy.restitution = 0.5;
yy.leaveBody = false;
yy.isDropPowerUp = false;
yy.isBadTarget = true;
yy.isMobBullet = true;
yy.showHealthBar = false;
yy.collisionFilter.category = cat.mobBullet;
yy.collisionFilter.mask = cat.player | cat.map | cat.body | cat.bullet;
let index = 0;
yy.do = function () {
this.alwaysSeePlayer()
this.timeLimit();
this.attraction();
};
}
me.drop = function (x, y) {
mobs.spawn(x, y, sides = 90, radius = 30, "rgb(0,255,100,0.7)");
let yyy = mob[mob.length - 1];
yyy.stroke = "transparent";
yyy.onDeath = function () {
for (let i = 0, len = 5; i < len; i++) {
me.seeker(this.position.x, this.position.y)
Matter.Body.setVelocity(mob[mob.length - 1], {
x: Math.random() * 30 - Math.random() * 30,
y: Math.random() * 30 - Math.random() * 30
});
}
};
Matter.Body.setDensity(yyy, 0.000015); //normal is 0.001
yyy.timeLeft = 60 //* (0.8 + 0.4 * Math.random());
yyy.frictionAir = 0.01 //* (0.8 + 0.4 * Math.random());
yyy.restitution = 0.5;
yyy.leaveBody = false;
yyy.isDropPowerUp = false;
yyy.isBadTarget = true;
yyy.isMobBullet = true;
yyy.showHealthBar = false;
yyy.collisionFilter.category = cat.mobBullet;
yyy.collisionFilter.mask = null;
yyy.maxRadius = 30;
let index = 0;
yyy.do = function () {
if (Matter.Query.collides(this, [player]).length > 0 && !(m.isCloak && tech.isIntangible) && m.immuneCycle < m.cycle) {
Matter.Body.setPosition(this, player.position)
if (player.speed > 2.5) Matter.Body.setVelocity(player, Vector.mult(player.velocity, 0.94))
}
if (Matter.Query.collides(this, map).length > 0) {
Matter.Body.setVelocity(this, Vector.mult(this.velocity, 0.1))
}
this.alwaysSeePlayer()
this.timeLimit();
ctx.save()
ctx.beginPath();
ctx.moveTo(this.position.x, this.position.y)
ctx.fillStyle = "black";
ctx.arc(this.position.x, this.position.y, this.maxRadius, 0, 2 * Math.PI)
ctx.stroke()
ctx.fill()
ctx.restore()
if (this.maxRadius > 0) {
this.maxRadius -= 0.5;
}
};
}
};
restoreBoss(-13350, -1800);
laserEM(-6500 + Math.floor(Math.random() * 200) - Math.floor(Math.random() * 200), -3400 + Math.floor(Math.random() * 200) - Math.floor(Math.random() * 200));
sniper(-9275 + Math.floor(Math.random() * 200) - Math.floor(Math.random() * 200), -3325 + Math.floor(Math.random() * 200) - Math.floor(Math.random() * 200));
laserEM(-5750 + Math.floor(Math.random() * 200) - Math.floor(Math.random() * 200), -850 + Math.floor(Math.random() * 200) - Math.floor(Math.random() * 200));
sniper(-3600 + Math.floor(Math.random() * 200) - Math.floor(Math.random() * 200), -1325 + Math.floor(Math.random() * 200) - Math.floor(Math.random() * 200));
laserEM(1425 + Math.floor(Math.random() * 200) - Math.floor(Math.random() * 200), -800 + Math.floor(Math.random() * 200) - Math.floor(Math.random() * 200));
//restoreBoss(-350, -3225);
wire();
wire();
wire();
wire();
wire();
color.map = '#00000000';
level.customTopLayer = () => {
if (dong.position.x > -3825) {
dong.force.y -= dong.mass * simulation.g;
} else {
dong.force.y += dong.mass * simulation.g;
}
Matter.Body.setAngularVelocity(dong, -0.5)
if (key == true) {
door.isClosing = false;
} else {
door.isClosing = true;
}
door.openClose();
door.draw()
for (let i = 0, len = map.length; i < len; i++) { //so boss bar renders over the map
ctx.beginPath();
ctx.moveTo(map[i].vertices[0].x, map[i].vertices[0].y);
for (let j = 0, length = map[i].vertices.length; j < length; j++) {
ctx.lineTo(map[i].vertices[j].x, map[i].vertices[j].y);
}
ctx.lineTo(map[i].vertices[0].x, map[i].vertices[0].y);
ctx.fillStyle = "rgba(68,68,68)";
ctx.strokeStyle = "transparent";
ctx.fill();
ctx.stroke();
// ctx.setLineDash([]);
}
for (let i = 0, len = mob.length; i < len; i++) {
if (mob[i].restoreBoss) {
ctx.save();
ctx.setTransform(1, 0, 0.5, 1, 0, 0); //slanted
ctx.fillStyle = "rgba(100, 100, 100, 0.3)";
ctx.fillRect(canvas.width2 / 2, canvas.height2 / 10, canvas.width2, 30);
ctx.fillStyle = "rgba(0,0,0,0.7)";
ctx.fillRect(canvas.width2 / 2, canvas.height2 / 10, canvas.width2 * mob[i].health, 30);
ctx.restore();
}
}
};
const obj = { restoreBoss };
Object.assign(spawn, obj); //for next map, gonna be a rpg-like thingy I think
},
map() {
const elevator = level.elevator(-80.4, -931.6, 180, 50, -1550)
15900 && player.position.x < 16300 && player.position.y > -960.2
const slime = level.hazard(15900, -960, 400, 6000);
const slime2 = level.hazard(15147.2, -1782.4, 2000, 822);
const boost1 = level.boost(5950, -20, 700)
const boost2 = level.boost(21088, -1672, 700)
const boost3 = level.boost(19390, -31, 1700)
const boost4 = level.boost(19390, -31, 1700)
const boost5 = level.boost(17274, -1242, 1000)
const portal = level.portal({ x: 443, y: -1636 }, Math.PI, { x: 21391.9, y: -1806.3 }, -Math.PI)
const portal2 = level.portal({ x: 16838.3, y: -626.7 }, Math.PI, { x: 16882.8, y: -2566.5 }, -Math.PI)
const buttonDoor = level.button(21889, -10)
const door = level.door(19119, -2133, 110, 510, 480)
const buttonDoor2 = level.button(18711, -2210)
const door2 = level.door(17041, -412, 110, 510, 480)
const buttonDoor3 = level.button(20456.6, -1636.2)
const door3 = level.door(20238, -781.4, 88, 452, 412)
//y=-1485
simulation.enableConstructMode()
level.setPosToSpawn(0, -50); //normal spawn
level.exit.x = 15316;
level.exit.y = -84;
spawn.mapRect(level.enter.x, level.enter.y + 20, 100, 20); //bump for level entrance
spawn.mapRect(level.exit.x, level.exit.y + 20, 100, 20); //bump for level exit
level.defaultZoom = 1800
simulation.zoomTransition(level.defaultZoom)
document.body.style.backgroundColor = "#001738";
color.map = "#444" //custom map color
level.custom = () => {
//spawn.mapRect(22330, -2688.75, 400, 800);
//spawn.mapRect(22330, -1793.5, 400, 800);//-46.25*2=-92.5
//spawn.mapRect(22330, -804.25, 400, 800);//-46.25*3
ctx.fillStyle = "rgba(63,247,251,0.8)"
ctx.fillRect(22330, -2713.75, 550, 700) //15845.0, -1262.2
ctx.fillRect(22330, -1743.5, 550, 700)
ctx.fillRect(22330, -754.25, 550, 700)
ctx.fillRect(15845.0, -1262.2, 550, 300)
ctx.fillStyle = "rgba(235,235,235,0.9)"
ctx.fillRect(-192, -1973, 6484, 2071)
ctx.fillRect(15109.5, -2867.5, 7284, 2971)
ctx.fillStyle = "rgba(35,35,35,0.8)"
ctx.fillRect(15145.9, -960, 200, 25)
ctx.fillStyle = "rgba(255,255,255,0.9)"
ctx.fillRect(-677.3, -610.9, 15, 15)
ctx.fillRect(-910.4, 458.3, 15, 15)
ctx.fillRect(-1029.0, 713.7, 15, 15)
ctx.fillRect(42.6, 1332.2, 15, 15)
ctx.fillRect(277.3, 751.8, 15, 15)
ctx.fillRect(797.1, 553.2, 15, 15)
ctx.fillRect(-1458.9, 340.9, 15, 15)
ctx.fillRect(-1780.0, -54.6, 15, 15)
ctx.fillRect(-1260.3, -686.4, 15, 15)
ctx.fillRect(-2064.3, -394.6, 15, 15)
ctx.fillRect(-1815.7, 1156.2, 15, 15)
ctx.fillRect(-1998.1, 1118.4, 15, 15)
buttonDoor.query();
buttonDoor.draw();
buttonDoor2.query();
buttonDoor2.draw();
buttonDoor3.query();
buttonDoor3.draw();
slime.query();
slime2.query();
ctx.fillStyle = `hsla(160, 100%, 43%,${0.3 + 0.07 * Math.random()})`
ctx.fillRect(15900 + 400 * Math.random(), -1360, 2, 6000)
if (buttonDoor.isUp) {
door.isClosing = true
} else {
door.isClosing = false
}
if (buttonDoor2.isUp) {
door2.isClosing = true
} else {
door2.isClosing = false
}
if (buttonDoor3.isUp) {
door3.isClosing = true
} else {
door3.isClosing = false
}
door.openClose();
door2.openClose();
door3.openClose();
portal[2].query()
portal[3].query()
portal2[2].query()
portal2[3].query()
boost1.query();
boost2.query();
boost3.query();
boost4.query();
boost5.query();
level.exit.drawAndCheck();
level.enter.draw();
};
level.customTopLayer = () => {
door.draw();
door2.draw();
door3.draw();
portal[0].draw();
portal[1].draw();
portal[2].draw();
portal[3].draw();
portal2[0].draw();
portal2[1].draw();
portal2[2].draw();
portal2[3].draw();
elevator.move()
if (player.position.x > 15900 && player.position.x < 16300 && player.position.y > -1360.2) {
Matter.Body.setVelocity(player, {
x: player.velocity.x,
y: player.velocity.y + 10
});
} else {
Matter.Body.setVelocity(player, {
x: player.velocity.x,
y: player.velocity.y - 0.2
});
}
};
//1273.2, -1404.7
//first ship base
spawn.mapRect(-300, 0, 6684, 100); //lower floor
spawn.mapRect(-300, -2071, 154, 2071); //far right wall
spawn.mapRect(2511, -300, 1309, 308); //left big block
spawn.mapRect(3820, -184, 1309, 184); //right big block
spawn.mapRect(-300, -739, 2549, 100); //upper right floor
spawn.mapRect(2056, -1309, 2764, 169); //upper center floor
spawn.mapRect(2056, -1309, 193, 650); //upper left floor wall
spawn.mapRect(4636, -1309, 193, 793); //upper right floor wall
spawn.mapRect(4821, -654, 955, 138); //upper right floor
spawn.mapRect(6237, -2071, 147, 2071); //far right wall
spawn.mapRect(-300, -2071, 6684, 154); //roof
//first ship details
spawn.mapRect(245, -360, 70, 400); //start room wall
spawn.mapRect(500, -1929, 154, 462);
spawn.mapRect(185, -1517, 469, 77);
spawn.mapRect(2773, -682, 469, 77); //walls in 1st room
spawn.mapRect(3743, -566, 77, 469);
spawn.mapRect(3947, -851, 469, 77);
spawn.mapRect(5313, -1309, 1000, 70); //walls in second area
spawn.mapRect(4818, -1006, 400, 70);
spawn.mapRect(4768, -1626, 800, 70);
spawn.mapRect(4760, -1626, 70, 400);
//first ship blocks/debris
spawn.debris(3267.6, -797.1, 700, 5); //16 debris per level
spawn.debris(1626.0, -372.5, 1700, 8); //16 debris per level
spawn.debris(1880.1, -1508.9, 3700, 16); //16 debris per level
spawn.debris(5335.3, -1431.6, 3700, 16); //16 debris per level
spawn.debris(1563.8, -1087.9, 700, 5); //16 debris per level
spawn.bodyRect(1540, -1110, 218, 125, 0.9);
//first ship mobs
spawn.randomMob(2903.9, -754.5, 0.7);
spawn.randomMob(5577.0, -217.0, 0.6);
spawn.randomMob(765.8, -1029.7, 0.5);
spawn.randomMob(20079.4, -2219.7, 0.6);
spawn.randomMob(20079.4, -2219.7, 0.7);
spawn.randomMob(20890.9, -1306.0, 0.5);
spawn.randomMob(21284.2, -983.1, 0.5);
spawn.randomMob(20381.0, -254.2, 0.7);
spawn.randomMob(21027.8, -473.8, 0.6);
spawn.randomMob(19448.2, -1323.3, 0.6);
spawn.randomMob(18397.7, -711.2, 0.6);
spawn.randomMob(15547.2, -2249.6, 0.6);
spawn.randomSmallMob(16114.6, -2524.2);
spawn.randomSmallMob(15378.9, -2549.6);
spawn.randomSmallMob(893.5, -120.8);
spawn.randomSmallMob(3521.8, -419.6);
spawn.randomSmallMob(4386.2, -439.6);
spawn.randomSmallMob(5667.0, -847.8);
spawn.randomSmallMob(3158.5, -1581.8);
spawn.randomSmallMob(3866.7, -1483.2);
spawn.randomSmallMob(4652.3, -1729.4);
spawn.randomSmallMob(1068.7, -106.1);
spawn.randomSmallMob(3545.0, -413.0);
spawn.randomSmallMob(4231.7, -446.3);
spawn.randomSmallMob(1456.4, -1014.8);
spawn.randomSmallMob(20432.4, -1374.3);
spawn.randomSmallMob(20381.0, -254.2);
spawn.randomSmallMob(20353.4, -1845.8);
spawn.randomSmallMob(20353.4, -1845.8);
spawn.randomSmallMob(20648.1, -136.8);
spawn.randomSmallMob(20024.4, -2213.1);
spawn.randomSmallMob(17438.7, -876.7);
//second ship mobs
spawn.debris(17732.3, -550.0, 700, 5); //16 debris per level
spawn.debris(18006.4, -2181.3, 700, 5); //16 debris per level
spawn.debris(16108.6, -2621.1, 700, 5); //16 debris per level
spawn.debris(20823.6, -1332.1, 1300, 5); //16 debris per level
spawn.debris(21095.5, -423.4, 700, 5); //16 debris per level
spawn.randomSmallMob(1300, -70);
// const index = mob.length
spawn.shieldingBoss(769.8, -1119.0)
// console.log(mob[index].onDeath)
// requestAnimationFrame(() => mob[index].onDeath = function() {});
// console.log(mob[index].onDeath)
//second ship base
spawn.mapRect(15000, 0, 515, 185); //lower floor 1
spawn.mapRect(17015, 0, 5500, 185); //lower floor 2
spawn.mapRect(15000, -2972, 185, 2972); //left wall
spawn.mapRect(15000, -2972, 7515, 185); //roof
spawn.mapRect(22330, -2972, 185, 2972); //right wall
spawn.mapRect(17002, -2972, 169, 2564); //left middle wall
spawn.mapRect(19089, -2972, 169, 855); //right middle wall upper
spawn.mapRect(19089, -1625, 169, 1800); //right middle wall lower
spawn.mapRect(20760, -2972, 169, 1350); //medium wall left of portal
spawn.mapRect(19720, -1625, 1725, 162); //right room upper floor
spawn.mapRect(21440, -2325, 169, 863); //medium wall right of portal
spawn.mapRect(19720, -855, 2725, 162); //right room lower floor
//engines //y -2972 -> 0
spawn.mapRect(22330, -2763.75, 400, 800);
spawn.mapRect(22330, -1793.5, 400, 800);
spawn.mapRect(22330, -804.25, 400, 800);
//second ship details
spawn.mapRect(19904, -1465, 85, 362); //upper L
spawn.mapRect(19542, -1191, 412, 88); //lower L
spawn.mapRect(18546, -2199, 600, 82); //2nd room enternce wall
spawn.mapRect(18546, -2499, 82, 2300);
spawn.mapRect(18108, -326, 500, 82); //walls/floors in middle room
spawn.mapRect(17750, -682, 300, 82);
spawn.mapRect(17156, -468, 500, 60);
spawn.mapRect(18022, -1082, 600, 82);
spawn.mapRect(17151, -1196, 500, 82);
spawn.mapRect(17453, -2060, 500, 82);
spawn.mapRect(18197, -2269, 400, 82);
spawn.mapRect(18108, -326, 500, 82);
spawn.mapRect(20542, -1191, 612, 88);
spawn.mapRect(20238, -1191, 88, 412);
spawn.mapRect(21520, -1468, 88, 412);
spawn.mapRect(20238, -330.2, 88, 412);
spawn.mapRect(20819, -328.3, 412, 88);
spawn.mapRect(21532, -708, 88, 412);
spawn.mapRect(15483.8, 12.5, 388, 30); //broken floor
spawn.mapRect(15487.6, 76.6, 488, 24);
spawn.mapRect(15506.5, 134.2, 288, 45);
spawn.mapVertex(16758.6, 135.3, "400 -30 -350 -40 -400 30 400 30");
spawn.mapVertex(16758.6, 55.3, "423 -30 -408 -20 -400 20 400 20");
//tank
spawn.mapRect(15310, -960, 600, 135);
spawn.mapRect(16290, -960, 800, 135);
//in tank
spawn.mapRect(16524.8, -2726.8, 40, 400);
spawn.mapRect(16524.8, -2130.9, 400, 40);
spawn.mapRect(16010.2, -2412.2, 300, 40);
spawn.mapRect(15379.2, -2055.1, 400, 40);
//add fuel tanks in the last room
spawn.mapRect(21531.9, -707.8, 488, 8);
//22185.5, -114.8
spawn.mapVertex(22207.8, -103, "325 -200 100 -200 325 -300");
spawn.mapRect(22056.6, -70, 225, 212);
spawn.mapVertex(20723.1, -1734, "325 -200 100 -200 325 -300");
spawn.mapRect(20571.9, -1701.0, 225, 212);
spawn.mapVertex(22207.8, -103, "325 -200 100 -200 325 -300");
spawn.mapRect(22056.6, -70, 225, 212);
//spawn.mapVertex(x,y, "coordinates")
//the parts in quotes is "x y x y x y x y x y" x and y need to be the coordinates of points that define the shape in a concave clockwise direction
//second ship blocks/debris
spawn.bodyRect(21525, -113, 50, 50, 9); //first button block
spawn.bodyRect(18993, -2283, 50, 50, 9); //second button block
spawn.bodyRect(20303, -1736, 50, 50, 9); //third button block
let randomBoss = Math.floor(Math.random() * 5); //change the bosses
spawn[["blinkBoss", "shooterBoss", "launcherBoss", "pulsarBoss", "beetleBoss", "bladeBoss", "revolutionBoss", "dragonFlyBoss", "spiderBoss"][randomBoss]](17902, -1689, 100, false);
// powerUps.spawnStartingPowerUps(1475, -1175);
// spawn.debris(750, -2200, 3700, 16); //16 debris per level
// spawn.bodyRect(1540, -1110, 300, 25, 0.9);
// spawn.randomSmallMob(1300, -70);
// spawn.randomMob(2650, -975, 0.8);
// spawn.randomGroup(1700, -900, 0.4);
// if (simulation.difficulty > 1) spawn.randomLevelBoss(2200, -1300);
// spawn.secondaryBossChance(100, -1500)
powerUps.addResearchToLevel() //needs to run after mobs are spawned
},
cantilever() { // made by Eclipse#7932 on discord, (TheSpudguy)(@PurpleSunsetGames on github)
// simulation.enableConstructMode();
simulation.makeTextLog(`<strong>underpass</strong> by <span class='color-var'>Eclipse#7932</span>`);
level.setPosToSpawn(0, -50); //normal spawn
level.exit.x = 5500;
level.exit.y = 950;
spawn.mapRect(level.enter.x, level.enter.y + 20, 100, 20); //bump for level entrance
spawn.mapRect(level.exit.x, level.exit.y + 20, 100, 20); //bump for level exit
spawn.mapRect(level.exit.x - 50, level.exit.y + 30, 200, 100); // exit platform
spawn.mapRect(level.exit.x - 50, level.exit.y - 300, 200, 100); // exit platform roof
const endElevator = level.elevator(level.exit.x - 150, level.exit.y - 300, 100, 425, level.exit.y - 300); // end access door
spawn.randomMob(-200, 350, Infinity); // random mob at the beginning
spawn.mapRect(-100, 0, 600, 100); // main platform at start
spawn.bodyRect(0, 300, 50, 50); // little squares at start (one of these should be taken while crossing the cantilever to complete the level more easily)
spawn.bodyRect(100, 200, 50, 50);
spawn.bodyRect(50, 250, 50, 50);
spawn.mapRect(450, -20, 50, 20); // main platform ledge
spawn.mapRect(-200, 500, 2200, 100); // lower platform
spawn.mapRect(1850, 380, 100, 50); // cantilever block
spawn.bodyRect(80, -1300, 70, 1300, 1, { friction: .03, frictionAir: .001 }); // cantilever
spawn.mapRect(3400, 500, 300, 100); // lever platform
spawn.mapRect(3650, 500, 100, 800); // pit
spawn.mapRect(3650, 1300, 2600, 100);
spawn.mapRect(6150, 600, 100, 800);
spawn.mapRect(5650, 600, 100, 650);
spawn.randomMob(4700, 550, Infinity);
spawn.randomMob(4700, 450, Infinity);
spawn.randomMob(4600, 550, Infinity);
const toggle = level.toggle(3500, 500, false); // first lever
const button = level.button(5900, 1300);
const slidingWall = level.elevator(3750, -1200, 100, 1800, -1200); // first sliding wall
level.defaultZoom = 1500;
simulation.zoomTransition(level.defaultZoom);
document.body.style.backgroundColor = "#d8badf";
// color.map = "#444" //custom map color
level.custom = () => {
level.exit.drawAndCheck();
level.enter.draw();
};
level.customTopLayer = () => {
toggle.query();
button.query();
button.draw();
if (toggle.isOn) {
slidingWall.force.y -= 400;
}
if (!button.isUp) {
endElevator.force.y -= 100;
}
slidingWall.move();
endElevator.move()
};
powerUps.addResearchToLevel(); //needs to run after mobs are spawned
},
dojo() { // By
simulation.makeTextLog(`<strong>underpass</strong> by <span class='color-var'>weird_pusheen</span>`);
const vanishes = [];
const smoofes = [];
const leftRotor = level.rotor(-550, 900, 950, 25);
leftRotor.frictionAir = 0.01;
var leftSchwoof = level.boost(-20, -60, -2000);
var rightSchwoof = level.button(2550, -50);
var rightSchwoofState = false;
var rightSchwoofLive = true;
spawn.mapRect(2513, -39, 200, 100);
var pathPoints = [
[0, 0], // Index 0 is owned by M and is set to M's position during play
// this means that occasionally the boss will bonk M on the way to somewhere else, which gives it a chance to hurt M and gives the player a chance to hurt it
[250, -750], /* Left bases */
[250, -2500],
[350, -1500], // Left doorway
[1150, -1500], // Home base
[1150, -2750], // Upper base
[1950, -1500], // Right doorway
[2050, -750], /* Right bases */
[2050, -2500],
[-150, -250], // Left porthole
];
function isntIn(point, array) {
for (var x = 0; x < array.length; x++) {
if (point[0] == array[x][0] && point[1] == array[x][1]) {
return false;
}
}
return true;
}
function isObstructed(v1, v2) {
var ret = Matter.Query.ray(map,
{
x: v1[0],
y: v1[1],
},
{
x: v2[0],
y: v2[1]
}).length != 0;
return ret; // Kinda-ish stolen from mob.js
}
function pythag(p1, p2) {
var dx = p1[0] - p2[0];
var dy = p1[1] - p2[1];
return Math.sqrt(dx * dx + dy * dy);
}
var path = undefined; // This is a stupid way to go about pathfinding code. I might even clean it up!
function pathFind(goalPoint, startPoint, curPath = []) {
var myPoint = startPoint;
if (curPath.length) {
myPoint = curPath[curPath.length - 1];
}
if (path && (curPath.length >= path.length)) { // If we've already found a shorter or equal path, no reason to continue and waste CPU time
return; // Minimizes for HOP COUNT, not PATH LENGTH - path length was buggy
}
if (!isObstructed(myPoint, goalPoint)) { // If the line to the goal point ain't blocked by a map object, we've arrived!
path = [...curPath];
path.push(goalPoint);
return;
}
pathPoints.forEach(testPoint => {
if (isntIn(testPoint, curPath)) { // If it's reusing points, there's clearly something wrong
if (!isObstructed(myPoint, testPoint)) { // If the line to the test point ain't blocked by a map object
var thing = [...curPath];
thing.push(testPoint);
pathFind(goalPoint, startPoint, thing); // Branch to a valid test point
}
}
});
}
level.setPosToSpawn(1200, 500);
level.exit.x = 51500;
level.exit.y = -1875;
spawn.mapRect(level.enter.x, level.enter.y + 20, 100, 20);
level.defaultZoom = 1500;
simulation.zoomTransition(level.defaultZoom)
document.body.style.backgroundColor = "#d8dadf";
spawn.mapRect(-500, 0, 3300, 300); // Floor
spawn.mapRect(-100, -3000, 2500, 100); // Ceiling
spawn.mapRect(-200, -3000, 100, 2600); // Left wall
spawn.mapRect(2400, -3000, 100, 3000); // Right wall
spawn.mapRect(500, -1000, 100, 500); /* obstruction blocks */
smoofes.push(map[map.length - 1]);
spawn.mapRect(500, -2500, 100, 500);
smoofes.push(map[map.length - 1]);
spawn.mapRect(1700, -1000, 100, 500);
smoofes.push(map[map.length - 1]);
spawn.mapRect(1700, -2500, 100, 500);
smoofes.push(map[map.length - 1]);
spawn.mapRect(-1000, 550, 200, 50); // Left chonky stepppp low
spawn.mapRect(-800, 300, 200, 50); // Left chonky stepppp high
spawn.mapVertex(-1000, 1200, "0 0 100 0 700 500 700 700 0 700"); // Left chonky
spawn.mapRect(3100, 550, 200, 50); // Right chonky stepppp low
spawn.mapRect(2900, 300, 200, 50); // Right chonky stepppp high
spawn.mapVertex(3300, 1200, "0 0 -100 0 -700 500 -700 700 0 700"); // Right chonky
const leftElevator = level.elevator(-1400 - 300, 1450, 300, 100, 500);
const rightElevator = level.elevator(-1400 + 5100, 1450, 300, 100, 500);
spawn.mapRect(-150, -1700, 200, 50);
spawn.mapRect(400, -2050, 200, 50);
spawn.mapRect(1600, -1000, 200, 50);
spawn.randomMob(1200, 700);
spawn.randomMob(600, 1000);
spawn.randomMob(1800, 1000);
spawn.randomMob(3200, 400);
spawn.randomMob(3000, 200);
spawn.randomMob(-900, 400);
spawn.randomMob(-700, 200);
spawn.randomMob(1200, 1000);
for (var i = 0; i < 4; i++) {
spawn.randomSmallMob(Math.random() * 600 - 600, Math.random() * 3000 - 400);
}
spawn.grenadier(-300, -1000);
spawn.grenadier(2600, -1000);
spawn.mapRect(-1400, 1450, 5100, 100); // The True Floor
const slime = level.hazard(-1250, 1400, 4800, 50);
slime.maxHeight = 600;
simulation.draw.body = function () {
ctx.beginPath();
for (let i = 0, len = body.length; i < len; ++i) {
if (!body[i].hidden) {
let vertices = body[i].vertices;
ctx.moveTo(vertices[0].x, vertices[0].y);
for (let j = 1; j < vertices.length; j++) {
ctx.lineTo(vertices[j].x, vertices[j].y);
}
ctx.lineTo(vertices[0].x, vertices[0].y);
}
}
ctx.lineWidth = 2;
ctx.fillStyle = color.block;
ctx.fill();
ctx.strokeStyle = color.blockS;
ctx.stroke();
} // Override the old draw code to allow intelligent hiding of blocks - preferably this becomes official code because it's just a single added if statement and makes a lot of things cleaner and more intelligent
const vanish = function (x, y, width, height) { // normal vanishes don't work well on my map for some reason, so I rewrote
x += width / 2;
y += height / 2;
const getVertices = function (bX, bY, bW, bH) { return [{ x: bX, y: bY, index: 0, isInternal: false }, { x: bX + bW, y: bY, index: 1, isInternal: false }, { x: bX + bW, y: bY + bH, index: 4, isInternal: false }, { x: bX, y: bY + bH, index: 3, isInternal: false }] };
const cMask = cat.player | cat.body | cat.bullet | cat.powerUp | cat.mob | cat.mobBullet;
const vertices = getVertices(x, y, width, height);
const block = body[body.length] = Bodies.fromVertices(x, y, vertices, {
collisionFilter: {
category: cat.map,
mask: cMask
},
isNoSetCollision: true,
inertia: Infinity, //prevents rotation
isNotHoldable: true,
isNonStick: true, //this keep sporangium from sticking
isTouched: false,
cWidth: width,
hiddenCycle: 0,
isStatic: true,
query() {
if (this.cWidth <= 0) {
if (this.cWidth > -100) {
this.cWidth = -100;
Matter.Body.setVertices(this, vertices);
}
this.isTouched = false;
this.collisionFilter.mask = undefined;
this.hidden = true;
this.hiddenCycle++;
if (this.hiddenCycle > 100) {
if (Matter.Query.collides(this, [player]).length) {
this.hiddenCycle = 50;
}
else {
this.hiddenCycle = 0;
this.cWidth = width;
this.collisionFilter.mask = cMask;
this.hidden = false;
}
}
}
else if (this.isTouched) {
Matter.Body.setVertices(this, getVertices(x, y, this.cWidth, height * (this.cWidth / width)));
this.cWidth -= 3;
}
else if (Matter.Query.collides(this, [player]).length) { // Elseif short circuit avoids expensive collision detection
this.isTouched = true;
}
}
});
return block;
};
vanishes.push(vanish(800, 800, 800, 50));
vanishes.push(vanish(400, 1100, 400, 50));
vanishes.push(vanish(1600, 1100, 400, 50));
spawn.bodyRect(1700, 812, 300, 25, 1, {
collisionFilter: {
category: cat.body,
mask: cat.player | cat.body | cat.bullet | cat.powerUp | cat.mob | cat.mobBullet | cat.map
},
isNoSetCollision: true,
isNotHoldable: true,
isNonStick: true, //this keep sporangium from sticking
restitution: 1,
friction: 0,
frictionAir: 0,
frictionStatic: 0,
query() {
Matter.Body.setAngularVelocity(this, 0);
Matter.Body.applyForce(this, this.position, {
x: 0,
y: -(this.position.y - 812) * 0.002
});
}
});
const zigzag = body[body.length - 1];
Matter.Body.applyForce(zigzag, zigzag.position, {
x: 0.1,
y: 0
});
var buttonWasDown = false;
level.customTopLayer = () => {
}
level.custom = () => {
rightSchwoof.isUp = false;
level.exit.drawAndCheck();
leftSchwoof.query();
level.enter.draw();
pathPoints[0][0] = m.pos.x;
pathPoints[0][1] = m.pos.y;
leftElevator.move();
rightElevator.move();
slime.query();
zigzag.query();
slime.levelRise(0.2);
for (var i = 0; i < vanishes.length; i++) {
vanishes[i].query();
}
if (!rightSchwoofState) {
var math = m.pos.y < leftRotor.position.y;
Matter.Body.setAngularVelocity(leftRotor, (math ? 1 : -1) * Math.PI / 45);
}
if (rightSchwoofLive) {
rightSchwoof.query();
rightSchwoof.draw();
if (rightSchwoofState) {
ctx.fillStyle = "lightgreen";
}
else {
ctx.fillStyle = "red";
}
ctx.beginPath();
ctx.arc(2615, -220, 40, 0, Math.PI * 2);
ctx.fill();
}
if (rightSchwoof.isUp) {
buttonWasDown = true;
}
else if (buttonWasDown) {
buttonWasDown = false;
rightSchwoofState = !rightSchwoofState;
}
if (Matter.Query.collides(player, smoofes).length) {
Matter.Body.applyForce(player, player.position, {
x: 0,
y: -0.015
});
}
};
mobs.spawn(500, -500, 10, 100, "yellow"); /* TacticalBoss
Modes:
Spawn:
Pathfinds to a point above M and starts dropping mobs. Learns which mobs to drop to cause the most damage, of course.
Occasionally strikes at M.
Hide:
Pathfinds to the point furthest from M
Strike:
Pathfind really, really fast to M
Recharge:
Stop moving for a bit to "recharge" (this is so the player has a chance to hit it)
It must always Hide or Recharge after Spawning or Striking. Which one it does is based on some factor I'll figure out.
Pathfinding is a hypersimplified algorithm with hard-coded "points" that it can travel between. M is one of these.
*/
var boss = mob[mob.length - 1];
boss.isBoss = true;
boss.damageReduction = 0.2 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1)
boss.onDeath = function () {
powerUps.spawnBossPowerUp(this.position.x, this.position.y);
level.exit.x = 2560;
level.exit.y = -90;
rightSchwoofLive = false;
};
var spawnables = {};
["hopper", "stabber", "springer", "striker", "sneaker", "grower"].forEach((m) => { /* Used to be spawn.fullPickList, but some of those mobs don't do collision-only damage and would thus never be properly selected for */
if (spawn[m]) {
spawnables[m] = {
fun: spawn[m],
name: m,
weight: 1
}
}
});
boss.stabCycle = 0;
boss.spawnCycle = 0;
function spawny() {
var totalWeight = 0;
Object.keys(spawnables).forEach(key => {
totalWeight += spawnables[key].weight;
});
var cursorWeight = 0;
var choice = Math.random();
var mC = undefined;
Object.values(spawnables).forEach((thing) => {
var lower = cursorWeight / totalWeight;
cursorWeight += thing.weight;
var upper = cursorWeight / totalWeight;
if ((choice > lower && choice <= upper) || !mC) {
mC = thing;
}
});
mC.fun(boss.position.x, boss.position.y);
var sp = mob[mob.length - 1];
sp.typeName = mC.name;
sp.onHit = () => {
spawnables[sp.typeName].weight += 1;
};
var oldFun = sp.onDeath;
sp.onDeath = () => { /* Mobs that die are worth less */
oldFun.call(sp);
spawnables[sp.typeName].weight -= 0.3; /* But not too much less */
};
}
boss.spawnDelay = 40;
boss.mode = "hide";
boss.modeSwitch = -1; // Randomize mode immediately
boss.damageReduction = 0.1;
var oldOnHit = boss.onHit;
boss.onHit = () => {
boss.modeSwitch = -1; // After striking the player, always switch modes
oldOnHit.call(boss);
};
boss.do = () => {
path = undefined;
var pfGoal = [0, 0];
boss.modeSwitch--;
if (boss.modeSwitch < 0) {
if (!boss.isShielded) {
spawn.shield(boss, boss.position.x, boss.position.y, 0.75); // Every time the mode switches, have a 75% chance to gain a new shield
}
if (boss.mode == "hide" || boss.mode == "recharge") {
if (Math.random() > 0.5) {
boss.mode = "spawn";
}
else {
boss.mode = "strike";
}
boss.modeSwitch = 600;
}
else {
if (boss.mode == "strike") {
boss.mode = "hide"; // Always hides after striking
}
else {
if (Math.random() > 0.5) {
boss.mode = "hide";
}
else {
boss.mode = "recharge"; // same when it goes into recharge mode
spawn.shield(boss, boss.position.x, boss.position.y, 1);
}
}
boss.modeSwitch = 200;
}
}
if (boss.mode == "hide") { /* Find the furthest point from M and get to it */
var longest = 0;
pathPoints.forEach(item => {
if (item[0] == 1150) {
return;
}
var iL = pythag(item, [m.pos.x, m.pos.y]);
if (iL > longest) {
longest = iL;
pfGoal = item;
}
});
}
else if (boss.mode == "strike") {
pfGoal = pathPoints[0]; // Target M
}
else if (boss.mode == "spawn") {
pfGoal = pathPoints[4]; // Go to Home Base to spawn
}
if (boss.mode != "recharge") {
if (m.pos.x > 2350 || m.pos.x < -150 || m.pos.y > 50) {
boss.mode = "hide";
}
pathFind(pfGoal, [boss.position.x, boss.position.y]);
if (!path) {
return; // If it couldn't pathfind, just drift
}
var goalX = path[0][0];
var goalY = path[0][1];
var dX = goalX - boss.position.x;
var dY = goalY - boss.position.y;
var hyp = Math.sqrt(dX * dX + dY * dY);
Matter.Body.applyForce(boss, {
x: goalX,
y: goalY
}, {
x: dX / hyp * 0.04 * (boss.mode == "strike" ? 2 : 1),
y: dY / hyp * 0.04 * (boss.mode == "strike" ? 2 : 1)// - 0.005
});
}
if (boss.mode == "spawn") {
boss.stabCycle++;
if (boss.stabCycle > 25) {
if (Math.abs(dX) < 200 && dY > 0) {
Matter.Body.applyForce(boss, {
x: player.position.x,
y: player.position.y
}, {
x: 0,
y: 5
});
}
boss.stabCycle = 0;
}
boss.spawnCycle++;
if (boss.spawnCycle > boss.spawnDelay) {
spawny();
boss.spawnDelay += 4;
boss.spawnCycle = 0;
}
}
};
boss.showHealthBar = true;
powerUps.addResearchToLevel() //needs to run after mobs are spawned
},
tlinat() { // _Destined_ formerly Richard0820#2652
simulation.makeTextLog(`<strong>tlinat</strong> by <span class='color-var'>Richard0820</span>`);
simulation.fallHeight = 1 / 0, level.setPosToSpawn(0, -1e3), level.exit.x = 5100, level.exit.y = 3770, spawn.mapRect(level.enter.x, level.enter.y + 20, 100, 20), spawn.mapRect(level.exit.x, level.exit.y + 20, 100, 20), level.defaultZoom = 3000, simulation.zoomTransition(level.defaultZoom), document.body.style.backgroundColor = "#d8dadf";
let e = 0,
t = 0;
const boidsFlocking = function (mob, otherMobs) {
const cohesionFactor = 0.01;
const separationFactor = 0.0001;
const alignmentFactor = 0.04;
let averagePosition = { x: 0, y: 0 };
let averageVelocity = { x: 0, y: 0 };
let nearbyMobsCount = 0;
for (const otherMob of otherMobs) {
if (otherMob !== mob) {
const distanceSquared = Vector.magnitudeSquared(Vector.sub(mob.position, otherMob.position));
const boidRangeSquared = 300 * 300; // Adjust boid range as needed
if (distanceSquared < boidRangeSquared) {
averagePosition = Vector.add(averagePosition, otherMob.position);
averageVelocity = Vector.add(averageVelocity, otherMob.velocity);
nearbyMobsCount++;
}
}
}
if (nearbyMobsCount > 0) {
averagePosition = Vector.div(averagePosition, nearbyMobsCount);
averageVelocity = Vector.div(averageVelocity, nearbyMobsCount);
const cohesionForce = Vector.mult(Vector.sub(averagePosition, mob.position), cohesionFactor);
mob.force = Vector.add(mob.force, cohesionForce);
const separationForce = Vector.mult(Vector.sub(mob.position, averagePosition), separationFactor);
mob.force = Vector.add(mob.force, separationForce);
const alignmentForce = Vector.mult(Vector.sub(averageVelocity, mob.velocity), alignmentFactor);
mob.force = Vector.add(mob.force, alignmentForce);
}
};
function ghoster(x, y, radius = 50 + Math.ceil(Math.random() * 90)) {
mobs.spawn(x, y, 7, radius, "transparent");
let me = mob[mob.length - 1];
me.seeAtDistance2 = 300000;
me.accelMag = 0.00004 + 0.00015 * simulation.accelScale;
if (map.length) me.searchTarget = map[Math.floor(Math.random() * (map.length - 1))].position; //required for search
// Matter.Body.setDensity(me, 0.0015); //normal is 0.001
me.damageReduction = 0.5
me.stroke = "transparent"; //used for drawGhost
me.alpha = 1; //used in drawGhost
me.isNotCloaked = false; //used in drawGhost
me.isBadTarget = true;
// me.leaveBody = false;
me.collisionFilter.mask = cat.bullet //| cat.body
me.showHealthBar = false;
me.memory = 600;
me.do = function () {
boidsFlocking(me, mob);//Stack, increase power.
if (this.speed > 7) {
Matter.Body.setVelocity(this, {
x: this.velocity.x * 0.8,
y: this.velocity.y * 0.8
});
}
this.seePlayerCheckByDistance();
this.checkStatus();
this.attraction();
this.search();
//draw
if (this.distanceToPlayer2() < this.seeAtDistance2) {
if (this.alpha < 1) this.alpha += 0.011 * simulation.CDScale; //near player go solid
} else {
if (this.alpha > 0) this.alpha -= 0.05; ///away from player, hide
}
if (this.alpha > 0) {
if (this.alpha > 0.7 && this.seePlayer.recall) {
this.healthBar();
if (!this.isNotCloaked) {
this.isNotCloaked = true;
this.isBadTarget = false;
this.collisionFilter.mask = cat.player | cat.bullet
}
}
//draw body
ctx.beginPath();
const vertices = this.vertices;
ctx.moveTo(vertices[0].x, vertices[0].y);
for (let j = 1, len = vertices.length; j < len; ++j) {
ctx.lineTo(vertices[j].x, vertices[j].y);
}
ctx.lineTo(vertices[0].x, vertices[0].y);
// ctx.lineWidth = 1;
ctx.fillStyle = `rgba(255,255,255,${this.alpha * this.alpha})`;
ctx.fill();
} else if (this.isNotCloaked) {
this.isNotCloaked = false;
this.isBadTarget = true;
this.collisionFilter.mask = cat.bullet; //can't touch player or walls
}
};
}
function o(e, t, o) {
const l = {
J: [" #### ", " # ", " # ", " # ", " # # ", " # # ", " ## "],
I: [" # ", " # ", " # ", " # ", " # ", " # ", " # "],
N: [" # # ", " ## # ", " ## # ", " # ## ", " # ## ", " # # ", " # # "],
" ": [" ", " ", " ", " ", " ", " ", " "],
O: [" ## ", " # # ", " # # ", " # # ", " # # ", " # # ", " ## "],
U: [" # # ", " # # ", " # # ", " # # ", " # # ", " # # ", " ### "],
R: [" #### ", " # #", " #### ", " # # ", " # # ", " # #", " # #"],
D: [" ### ", " # ## ", " # # ", " # # ", " # # ", " # ## ", " ### "],
S: [" #### ", " # ", " # ", " ### ", " ## ", " # ", " ##### "],
C: [" ##### ", " # ", " # ", " # ", " # ", " # ", " ##### "],
V: [" # # ", " # # ", " # # ", " # # ", " # # ", " # # ", " # "],
E: [" ##### ", " # ", " # ", " ##### ", " # ", " # ", " ##### "],
".": [" ", " ", " ", " ", " ", " ## ", " ## "],
"/": [" #", " # ", " # ", " # ", " # ", " # ", "# "],
G: [" ###### ", " # ", " # ", " # ### ", " # # ", " # # ", " ###### "],
Q: [" ###### ", " # # ", " # # ", " # # ", " # # # ", " # # ", " #### # ", " # "],
8: [" ##### ", " # # ", " # # ", " ##### ", " # # ", " # # ", " ##### "],
g: [" ##### ", " # # ", " # # ", " ##### ", " # ", " # ", " ###### "],
Y: [" # # ", " # # ", " # # ", " # ", " # ", " # ", " # "],
4: [" # ", " # # ", " # # ", " # # ", " ###### ", " # ", " # "],
W: [" # # ", " # # ", " # # ", " # # # ", " # # # # ", " ## ## ", " # # "],
e: [" ###### ", " # # ", " # # ", " ####### ", " # ", " # ", " ###### "],
c: [" ###### ", "# ", "# ", "# ", "# ", "# ", " ###### ", " "],
m: [" # ", " ### ### ", " # # # ", " # # # ", " # # # ", " # # # ", " # # # "]
},
a = (e, t) => {
ctx.fillStyle = "black", ctx.fillRect(e, t, 50, 50)
},
n = (e, t, o) => {
const n = l[e];
if (n)
for (let e = 0; e < n.length; e++) {
const l = n[e];
for (let n = 0; n < l.length; n++) {
if ("#" === l[n]) {
a(t + 20 * n, o + 20 * e)
}
}
}
};
for (let l = 0; l < o.length; l++) {
n(o[l], e + 250 * l - Math.abs(1.5 * e), t)
}
}
simulation.makeTextLog(`<img src="https://raw.githubusercontent.com/Whyisthisnotavalable/image-yy/main/Hotpot-removed.png" width="100" height="100" style="background-image: radial-gradient(circle, gray, black, transparent)"><br>Look up<br><em>Walk right to tp to maze</em><br><b>Exit is at the bottom left</b>`), Matter.Body.scale(player.parts[3], 2, 2), level.custom = () => {
if (level.exit.drawAndCheck(), level.enter.draw(), player.position.y > 1e5 && Matter.Body.setPosition(player, {
x: 5100,
y: -5925
}), player.position.x > 2500 && 0 == e) {
Matter.Body.setPosition(player, {
x: 5100,
y: -5925
}), e++;
for (let e = 0; e < map.length; e++) Math.random() < .75 && ghoster(map[e].position.x, map[e].position.y);
simulation.makeTextLog("Watch out for <b>ghosters</b><br>Peace ✌️")
}
player.position.x > level.exit.x && player.position.x < level.exit.x + 100 && player.position.y > level.exit.y - 150 && player.position.y < level.exit.y - 0 && player.velocity.y < .15 && 0 == t && (t++, Matter.Body.scale(player.parts[3], .5, .5))
}, level.customTopLayer = () => {
player.position.x > -1200 && player.position.x < 4500 && (o(2e3, -3e3, "JOIN OUR DISCORD SERVER"), o(1500, -2700, "DISCORD.GG/Q8gY4WeUcm"))
}, spawn.mapRect(-1e3, -950, 5950, 100), spawn.mapRect(-1325, -3450, 100, 2575), spawn.mapRect(-1325, -950, 350, 100), spawn.mapRect(4850, -3400, 100, 2550), spawn.mapRect(-1325, -3450, 6275, 100),
function (e, t, o, l, a) {
const n = o / a,
s = l / a,
i = e - o / 2,
p = t - l / 2,
r = [];
for (let e = 0; e < a; e++) {
r[e] = [];
for (let t = 0; t < a; t++) r[e][t] = 1
}
const c = [];
(function e(t, o) {
r[t][o] = 0;
const l = [{
dx: 0,
dy: -1
}, {
dx: 1,
dy: 0
}, {
dx: 0,
dy: 1
}, {
dx: -1,
dy: 0
}];
l.sort((() => Math.random() - .5));
for (const n of l) {
const l = t + 2 * n.dx,
s = o + 2 * n.dy;
l >= 0 && l < a && s >= 0 && s < a && 1 === r[l][s] && (r[t + n.dx][o + n.dy] = 0, r[l][s] = 0, c.push({
x: t + n.dx,
y: o + n.dy
}), e(l, s))
}
})(0, 0), r[a - 1][a - 1] = 1;
for (let e = -1; e < a + 1; e++) {
let t = -1,
o = -1;
for (let l = -1; l < a + 1; l++)
if (e >= 0 && e < a && l >= 0 && l < a && 1 === r[e][l]) - 1 === t && (t = l), o = l;
else if (-1 !== t) {
const l = i + e * n,
a = p + t * s,
r = n,
c = (o - t + 1) * s;
c !== s && spawn.mapRect(l, a, r, c), t = -1, o = -1
}
}
for (let e = -1; e < a + 1; e++) {
let t = -1,
o = -1;
for (let l = -1; l < a + 1; l++)
if (l >= 0 && l < a && e >= 0 && e < a && 1 === r[l][e]) - 1 === t && (t = l), o = l;
else if (-1 !== t) {
const l = i + t * n,
a = p + e * s,
r = (o - t + 1) * n,
c = s;
r !== n && spawn.mapRect(l, a, r, c), t = -1, o = -1
}
}
spawn.mapRect(i - n, p - s, n * a, s), spawn.mapRect(i - n, p - s, n, s * a), spawn.mapRect(i + (a - 1) * n, p - s, n, s * (a + 1)), spawn.mapRect(i - n, p + (a - 1) * s, n * (a + 1), s)
}(1e4, -1e3, 1e4, 1e4, 50);
},
ruins() { // by SiddhUPe
// simulation.enableConstructMode()
simulation.makeTextLog(`<strong>ruins</strong> by <span class='color-var'>SiddhUPe</span>`);
level.setPosToSpawn(0, -50); //normal spawn
level.exit.x = 19531;
level.exit.y = 882 + 70;
spawn.mapRect(level.enter.x, level.enter.y + 20, 100, 20); //bump for level entrance
spawn.mapRect(level.exit.x, level.exit.y + 20, 100, 20); //bump for level exit
level.defaultZoom = 1800
simulation.zoomTransition(level.defaultZoom)
document.body.style.backgroundColor = "#d8dadf";
// color.map = "#444" //custom map color
level.customTopLayer = () => { };
spawn.mapRect(875, 0, 1000, 100);
spawn.mapRect(1825, -400, 50, 225);
spawn.mapRect(1825, -400, 675, 50);
spawn.mapRect(1825, 0, 675, 100);
spawn.mapRect(2500, -575, 50, 225);
spawn.mapRect(2500, -575, 850, 50);
spawn.mapRect(2500, -100, 875, 175);
spawn.mapRect(2500, 75, 875, 25);
spawn.mapRect(3350, -575, 25, 50);
spawn.mapRect(2450, -50, 75, 75);
spawn.mapRect(2425, -25, 25, 25);
spawn.mapRect(2475, -75, 25, 25);
spawn.mapRect(3375, -575, 75, 325);
spawn.mapRect(3375, -100, 75, 300);
spawn.mapRect(3450, -50, 25, 250);
spawn.mapRect(3475, -25, 25, 225);
spawn.mapRect(3500, 0, 25, 200);
spawn.mapRect(3525, 25, 25, 175);
spawn.mapRect(3550, 75, 25, 125);
spawn.mapRect(3550, 50, 25, 150);
spawn.mapRect(3575, 75, 25, 125);
spawn.mapRect(3600, 100, 25, 100);
spawn.mapRect(3625, 150, 25, 50);
spawn.mapRect(2875, -1375, 350, 75);
spawn.mapRect(3150, -1375, 75, 350);
spawn.mapRect(3100, -1300, 50, 50);
spawn.mapRect(3075, -1300, 25, 25);
spawn.mapRect(3125, -1250, 25, 25);
spawn.mapRect(2825, -1375, 50, 125);
spawn.mapRect(3100, -1025, 125, 50);
spawn.mapRect(2800, -1350, 25, 75);
spawn.mapRect(3125, -975, 75, 25);
spawn.mapRect(3225, -1350, 25, 300);
spawn.mapRect(2875, -1400, 275, 25);
spawn.mapRect(2900, -1425, 225, 25);
spawn.mapRect(3250, -1325, 25, 250);
spawn.mapRect(2875, -1300, 25, 25);
spawn.mapRect(3125, -1050, 50, 25);
spawn.mapRect(5325, 800, 50, 225);
spawn.mapRect(5325, 975, 300, 50);
spawn.mapRect(5375, 925, 50, 50);
spawn.mapRect(5375, 900, 25, 25);
spawn.mapRect(5425, 950, 25, 25);
spawn.mapRect(5325, 775, 100, 25);
spawn.mapRect(5625, 925, 25, 100);
spawn.mapRect(5350, 800, 50, 25);
spawn.mapRect(5600, 950, 25, 50);
spawn.mapRect(5300, 800, 25, 175);
spawn.mapRect(5400, 1025, 225, 25);
spawn.mapRect(5450, 1050, 125, 25);
spawn.mapRect(5275, 850, 25, 100);
spawn.mapRect(5350, 750, 50, 25);
spawn.mapRect(5650, 950, 25, 50);
spawn.mapRect(16775, -975, 275, 50);
spawn.mapRect(17000, -975, 50, 200);
spawn.mapRect(16775, -975, 25, 100);
spawn.mapRect(17000, -775, 50, 50);
spawn.mapRect(16975, -725, 75, 25);
spawn.mapRect(16950, -925, 50, 50);
spawn.mapRect(16925, -925, 75, 25);
spawn.mapRect(17000, -925, 25, 75);
spawn.mapRect(16975, -925, 25, 50);
spawn.mapRect(16975, -925, 25, 75);
spawn.mapRect(16800, -1000, 200, 25);
spawn.mapRect(16850, -1025, 100, 25);
spawn.mapRect(17050, -925, 25, 200);
spawn.mapRect(17075, -925, 25, 150);
spawn.mapRect(16775, -925, 50, 25);
spawn.mapRect(17000, -750, 25, 25);
spawn.mapRect(16975, -750, 25, 50);
spawn.mapRect(16950, -725, 75, 25);
spawn.mapRect(9475, -1150, 50, 200);
spawn.mapRect(9475, -1150, 25, 25);
spawn.mapRect(9475, -1150, 300, 50);
spawn.mapRect(9725, -1150, 25, 25);
spawn.mapRect(9725, -1150, 50, 200);
spawn.mapRect(9500, -975, 25, 25);
spawn.mapRect(9500, -975, 75, 25);
spawn.mapRect(9700, -975, 25, 25);
spawn.mapRect(9675, -975, 75, 25);
spawn.mapRect(9525, -1175, 200, 25);
spawn.mapRect(9550, -1200, 150, 100);
spawn.mapRect(9450, -1125, 50, 150);
spawn.mapRect(9750, -1125, 50, 150);
spawn.mapRect(9525, -1100, 50, 50);
spawn.mapRect(9675, -1100, 50, 50);
spawn.mapRect(9575, -1100, 25, 25);
spawn.mapRect(9650, -1100, 25, 25);
spawn.mapRect(9500, -1050, 50, 25);
spawn.mapRect(9700, -1100, 25, 75);
spawn.mapRect(11925, -1175, 75, 275);
spawn.mapRect(11925, -1175, 475, 75);
spawn.mapRect(12325, -1175, 75, 275);
spawn.mapRect(11925, -925, 175, 25);
spawn.mapRect(12225, -925, 175, 25);
spawn.mapRect(11950, -925, 125, 50);
spawn.mapRect(12275, -925, 100, 50);
spawn.mapRect(11925, -1200, 475, 25);
spawn.mapRect(11975, -1225, 375, 25);
spawn.mapRect(12000, -1225, 50, 25);
spawn.mapRect(12000, -1275, 325, 75);
spawn.mapRect(11900, -1175, 50, 250);
spawn.mapRect(12375, -1175, 50, 250);
spawn.mapRect(11900, -1150, 50, 150);
spawn.mapRect(11875, -1150, 50, 200);
spawn.mapRect(12375, -1150, 75, 200);
spawn.mapRect(11975, -1100, 50, 25);
spawn.mapRect(12300, -1100, 75, 25);
spawn.mapRect(12300, -950, 25, 50);
spawn.mapRect(12000, -950, 25, 75);
spawn.mapRect(3625, 125, 25, 50);
spawn.mapRect(3650, 150, 25, 50);
spawn.mapRect(3675, 175, 25, 25);
spawn.mapRect(3450, -75, 25, 50);
spawn.mapRect(3475, -50, 25, 125);
spawn.mapRect(3500, -25, 25, 125);
spawn.mapRect(3500, 0, 50, 125);
spawn.mapRect(3550, 25, 25, 125);
spawn.mapRect(3575, 50, 25, 125);
spawn.mapRect(3600, 75, 25, 75);
spawn.mapRect(3600, 100, 50, 75);
spawn.mapRect(3650, 125, 25, 75);
spawn.mapRect(3675, 150, 25, 50);
spawn.mapRect(3675, 150, 75, 450);
spawn.mapRect(3675, 525, 700, 75);
spawn.mapRect(4300, 150, 75, 450);
mover = level.mover(3375, -100, 75, 100);
pool = level.hazard(3750, 200, 550, 325);
spawn.mapRect(-150, -225, 75, 325);
spawn.mapRect(-150, -325, 425, 100);
spawn.mapRect(-100, -400, 300, 75);
spawn.mapRect(-25, -475, 150, 75);
spawn.mapRect(200, -350, 25, 50);
spawn.mapRect(-50, -425, 25, 100);
spawn.mapRect(-125, -350, 25, 100);
spawn.mapRect(100, -425, 50, 125);
spawn.mapRect(1875, -450, 625, 50);
spawn.mapRect(1950, -500, 550, 50);
spawn.mapRect(2025, -525, 475, 25);
spawn.mapRect(2025, -550, 525, 25);
spawn.mapRect(2125, -575, 400, 25);
spawn.mapRect(2125, -600, 1325, 50);
spawn.mapRect(2475, -550, 950, 200);
spawn.mapRect(1825, -350, 100, 175);
spawn.mapRect(3350, -375, 25, 125);
spawn.mapRect(1850, -425, 50, 25);
spawn.mapRect(1925, -475, 125, 25);
spawn.mapRect(2000, -525, 125, 25);
spawn.mapRect(2100, -575, 200, 25);
spawn.mapRect(2400, -650, 725, 75);
spawn.mapRect(2500, -675, 475, 75);
spawn.mapRect(2625, -725, 225, 100);
spawn.mapRect(2675, -750, 125, 25);
spawn.mapRect(2600, -700, 25, 50);
spawn.mapRect(2850, -700, 25, 75);
spawn.mapRect(3075, -625, 75, 50);
spawn.mapRect(2375, -625, 50, 75);
spawn.mapRect(1900, -350, 100, 75);
spawn.mapRect(1925, -300, 50, 50);
spawn.mapRect(1975, -350, 75, 50);
spawn.mapRect(3325, -350, 50, 25);
spawn.mapRect(150, -25, 1425, 50);
spawn.mapRect(175, 75, 1200, 50);
spawn.mapRect(400, -25, 575, 25);
spawn.mapRect(425, -50, 750, 75);
spawn.mapRect(1250, -50, 125, 100);
spawn.mapRect(1175, -50, 100, 25);
spawn.mapRect(725, 100, 500, 50);
spawn.mapRect(625, -75, 300, 50);
spawn.mapRect(250, -25, 225, 25);
spawn.mapRect(1400, 75, 225, 50);
spawn.mapRect(950, -75, 200, 25);
spawn.mapRect(1200, -75, 125, 25);
spawn.mapRect(425, -50, 100, 25);
spawn.mapRect(450, -75, 100, 50);
spawn.mapRect(250, -50, 125, 50);
spawn.mapRect(250, 125, 125, 25);
spawn.mapRect(475, 100, 100, 50);
spawn.mapRect(650, 125, 25, 25);
spawn.mapRect(675, 100, 75, 50);
spawn.mapRect(825, 125, 200, 50);
spawn.mapRect(-75, 75, 325, 50);
spawn.mapRect(0, 100, 175, 50);
spawn.mapRect(775, -100, 275, 50);
spawn.mapRect(2475, 0, 925, 125);
spawn.mapRect(2500, 50, 875, 100);
spawn.mapRect(2550, 100, 775, 75);
spawn.mapRect(2625, 150, 600, 50);
spawn.mapRect(3225, 125, 275, 75);
spawn.mapRect(1750, -225, 150, 50);
spawn.mapRect(1800, -275, 50, 75);
spawn.mapRect(1775, -250, 75, 75);
spawn.mapRect(3200, -625, 250, 50);
spawn.mapRect(3275, -650, 75, 25);
spawn.mapRect(3175, -625, 25, 25);
spawn.mapRect(3250, -700, 100, 100);
spawn.mapRect(3200, -650, 75, 50);
spawn.mapRect(3225, -675, 75, 100);
spawn.mapRect(3325, -675, 50, 100);
spawn.mapRect(3375, -650, 25, 75);
spawn.mapRect(1575, -25, 100, 25);
spawn.mapRect(1450, 100, 125, 50);
spawn.mapRect(250, -300, 50, 50);
spawn.mapRect(275, -275, 50, 25);
spawn.mapRect(200, -275, 125, 50);
spawn.mapRect(3725, 200, 50, 375);
spawn.mapRect(3750, 275, 50, 300);
spawn.mapRect(3800, 350, 25, 200);
spawn.mapRect(3825, 425, 25, 150);
spawn.mapRect(3850, 500, 25, 75);
spawn.mapRect(4275, 250, 50, 325);
spawn.mapRect(4250, 300, 50, 300);
spawn.mapRect(4225, 375, 75, 200);
spawn.mapRect(4200, 450, 75, 150);
spawn.mapRect(4175, 500, 75, 75);
spawn.mapRect(3950, 500, 150, 50);
spawn.mapRect(4000, 500, 50, 25);
spawn.mapRect(3875, 500, 425, 25);
spawn.mapRect(3625, 200, 50, 75);
spawn.mapRect(3575, 200, 50, 25);
spawn.mapRect(3675, 275, 25, 25);
spawn.mapRect(3650, 275, 25, 25);
spawn.mapRect(3600, 200, 25, 50);
spawn.mapRect(2600, 175, 25, 25);
spawn.mapRect(2700, 175, 425, 100);
spawn.mapRect(2650, 200, 75, 50);
spawn.mapRect(3100, 200, 75, 50);
spawn.mapRect(2675, 250, 25, 25);
spawn.mapRect(2625, 200, 100, 25);
spawn.mapRect(3150, 200, 75, 25);
spawn.mapRect(3175, 225, 25, 25);
spawn.mapRect(3775, 250, 25, 50);
spawn.mapRect(3800, 275, 25, 125);
spawn.mapRect(3800, 325, 50, 225);
spawn.mapRect(3875, 400, 25, 200);
spawn.mapRect(3825, 425, 50, 125);
spawn.mapRect(3850, 375, 25, 100);
spawn.mapRect(3900, 450, 25, 75);
spawn.mapRect(3925, 475, 25, 50);
spawn.mapRect(3450, -600, 25, 325);
spawn.mapRect(3475, -575, 25, 275);
spawn.mapRect(3500, -525, 25, 200);
spawn.mapRect(3525, -500, 25, 150);
spawn.mapRect(3550, -475, 25, 100);
spawn.mapRect(2725, 250, 350, 50);
spawn.mapRect(2750, 275, 300, 50);
spawn.mapRect(3150, 250, 25, 25);
spawn.mapRect(4325, 150, 975, 75);
spawn.mapRect(4375, 225, 900, 25);
spawn.mapRect(4375, 175, 875, 100);
spawn.mapRect(4375, 225, 850, 75);
spawn.mapRect(4375, 225, 825, 100);
spawn.mapRect(4375, 275, 800, 75);
spawn.mapRect(4350, 325, 800, 50);
spawn.mapRect(4375, 350, 750, 50);
spawn.mapRect(4375, 350, 725, 75);
spawn.mapRect(4350, 375, 725, 75);
spawn.mapRect(4350, 400, 700, 75);
spawn.mapRect(4350, 425, 675, 75);
spawn.mapRect(4350, 475, 650, 50);
spawn.mapRect(4375, 500, 600, 50);
spawn.mapRect(4375, 500, 575, 75);
spawn.mapRect(4375, 550, 550, 50);
spawn.mapRect(4425, 125, 775, 75);
spawn.mapRect(5300, 175, 75, 50);
spawn.mapRect(4475, 100, 150, 25);
spawn.mapRect(4825, 125, 300, 25);
spawn.mapRect(4800, 100, 250, 25);
spawn.mapRect(5100, 100, 50, 25);
spawn.mapRect(4650, 100, 75, 25);
spawn.mapRect(5475, 225, 125, 300);
spawn.mapRect(5450, 275, 25, 125);
spawn.mapRect(5450, 450, 25, 75);
spawn.mapRect(5425, 325, 25, 75);
spawn.mapRect(5425, 475, 25, 50);
spawn.mapRect(5575, 250, 50, 150);
spawn.mapRect(5575, 450, 50, 50);
spawn.mapRect(5475, 525, 125, 25);
spawn.mapRect(5500, 550, 75, 25);
spawn.mapRect(5525, 575, 25, 25);
spawn.mapRect(3675, 575, 1050, 50);
spawn.mapRect(4175, 600, 250, 50);
spawn.mapRect(3850, 625, 100, 25);
spawn.mapRect(3700, 625, 75, 25);
spawn.mapRect(4050, 625, 50, 25);
spawn.mapRect(4500, 625, 225, 25);
spawn.mapRect(5725, 150, 75, 225);
spawn.mapRect(5700, 175, 25, 150);
spawn.mapRect(5775, 250, 50, 100);
spawn.mapRect(5950, 325, 75, 75);
spawn.mapRect(5925, 375, 25, 25);
spawn.mapRect(6000, 350, 50, 50);
spawn.mapRect(6125, 425, 1050, 75);
spawn.mapRect(6425, 0, 750, 75);
spawn.mapRect(6400, 25, 50, 50);
spawn.mapRect(6500, -25, 675, 75);
spawn.mapRect(6550, -25, 275, 25);
spawn.mapRect(6475, -25, 125, 75);
spawn.mapRect(6450, -25, 100, 100);
spawn.mapRect(6475, -75, 700, 75);
spawn.mapRect(6500, -75, 75, 25);
spawn.mapRect(6500, -125, 675, 125);
spawn.mapRect(6525, -150, 650, 125);
spawn.mapRect(6550, -175, 625, 50);
spawn.mapRect(6900, -200, 275, 75);
spawn.mapRect(6925, -250, 250, 175);
spawn.mapRect(6950, -275, 225, 75);
spawn.mapRect(6975, -300, 200, 50);
spawn.mapRect(7025, -325, 125, 125);
spawn.mapRect(6400, 50, 75, 175);
spawn.mapRect(6450, 50, 100, 100);
spawn.mapRect(6475, 150, 25, 25);
spawn.mapRect(6550, 75, 25, 25);
spawn.mapRect(6375, 75, 25, 150);
spawn.mapRect(6350, 100, 25, 75);
spawn.mapRect(6650, 50, 225, 50);
spawn.mapRect(6975, 75, 75, 25);
spawn.mapRect(6625, -175, 225, 25);
spawn.mapRect(6675, -200, 275, 25);
spawn.mapRect(6750, -225, 250, 25);
spawn.mapRect(6200, 475, 575, 50);
spawn.mapRect(6925, 500, 125, 25);
spawn.mapRect(6325, 400, 475, 25);
spawn.mapRect(6950, 400, 200, 25);
spawn.mapRect(7100, 75, 75, 100);
spawn.mapRect(7075, 75, 25, 25);
spawn.mapRect(7175, -300, 1650, 350);
spawn.mapRect(7325, -700, 100, 450);
spawn.mapRect(7600, -700, 100, 450);
spawn.mapRect(7900, -700, 100, 450);
spawn.mapRect(8200, -700, 100, 425);
spawn.mapRect(8500, -700, 100, 425);
spawn.mapRect(7275, -825, 1375, 125);
spawn.mapRect(7400, -700, 50, 25);
spawn.mapRect(7575, -700, 50, 25);
spawn.mapRect(7875, -700, 150, 25);
spawn.mapRect(8175, -700, 150, 25);
spawn.mapRect(8475, -700, 150, 25);
spawn.mapRect(7300, -325, 150, 75);
spawn.mapRect(7575, -325, 150, 75);
spawn.mapRect(7875, -325, 150, 75);
spawn.mapRect(8175, -325, 150, 75);
spawn.mapRect(8475, -325, 150, 75);
spawn.mapRect(7700, -700, 25, 25);
spawn.mapRect(7300, -700, 75, 25);
spawn.mapRect(7150, 50, 75, 50);
spawn.mapRect(7175, 100, 25, 25);
spawn.mapRect(7225, 50, 25, 25);
spawn.mapRect(7300, -850, 1325, 75);
spawn.mapRect(7325, -875, 1275, 50);
spawn.mapRect(7375, -900, 1200, 25);
spawn.mapRect(7350, -900, 25, 25);
spawn.mapRect(7375, -900, 200, 25);
spawn.mapRect(7375, -925, 1175, 25);
spawn.mapRect(7400, -950, 1125, 25);
spawn.mapRect(7425, -975, 1075, 25);
spawn.mapRect(7450, -1000, 1025, 25);
spawn.mapRect(7675, -1050, 525, 50);
spawn.mapRect(7700, -1050, 100, 25);
spawn.mapRect(7700, -1075, 450, 25);
spawn.mapRect(7725, -1100, 400, 25);
spawn.mapRect(7775, -1125, 300, 25);
spawn.mapRect(7650, -1025, 75, 25);
spawn.mapRect(8200, -1025, 25, 25);
spawn.mapRect(8825, -275, 25, 300);
spawn.mapRect(8825, -225, 50, 125);
spawn.mapRect(8850, -50, 25, 75);
spawn.mapRect(7150, 425, 100, 425);
spawn.mapRect(7150, 775, 1600, 100);
spawn.mapRect(8750, 400, 75, 475);
spawn.mapRect(8825, 400, 25, 475);
spawn.mapRect(7225, 450, 50, 325);
spawn.mapRect(7250, 500, 50, 275);
spawn.mapRect(7300, 550, 25, 225);
spawn.mapRect(7325, 600, 25, 175);
spawn.mapRect(7350, 650, 25, 125);
spawn.mapRect(7375, 675, 25, 100);
spawn.mapRect(7400, 700, 25, 75);
spawn.mapRect(7425, 725, 25, 50);
spawn.mapRect(7450, 750, 25, 25);
spawn.mapRect(8725, 425, 50, 375);
spawn.mapRect(8700, 450, 75, 375);
spawn.mapRect(8675, 475, 100, 375);
spawn.mapRect(8650, 500, 125, 375);
spawn.mapRect(8600, 525, 100, 350);
spawn.mapRect(8575, 550, 100, 275);
spawn.mapRect(8550, 575, 150, 250);
spawn.mapRect(8525, 625, 100, 225);
spawn.mapRect(8500, 675, 100, 125);
spawn.mapRect(8625, 825, 25, 25);
spawn.mapRect(8475, 700, 75, 75);
spawn.mapRect(8450, 725, 50, 50);
spawn.mapRect(8425, 750, 100, 25);
wastepool = level.hazard(7250, 575, 1450, 200);
spawn.mapRect(7375, 0, 250, 75);
spawn.mapRect(7700, 0, 725, 75);
spawn.mapRect(8575, 50, 150, 25);
spawn.mapRect(7750, 50, 475, 50);
spawn.mapRect(7425, 50, 175, 50);
spawn.mapRect(8600, 50, 50, 50);
spawn.mapRect(7200, 850, 875, 50);
spawn.mapRect(8225, 850, 125, 50);
spawn.mapRect(8475, 850, 275, 50);
spawn.mapRect(7300, 875, 375, 50);
spawn.mapRect(7925, 875, 100, 50);
spawn.mapRect(8525, 875, 125, 50);
spawn.mapRect(8250, 875, 75, 50);
spawn.mapRect(7800, 900, 50, 25);
spawn.mapRect(8125, 875, 50, 50);
spawn.mapRect(8075, 875, 50, 50);
spawn.mapRect(7125, 500, 25, 325);
spawn.mapRect(7100, 475, 25, 300);
spawn.mapRect(7075, 600, 50, 125);
spawn.mapRect(7075, 500, 50, 25);
spawn.mapRect(8850, 425, 75, 450);
spawn.mapRect(8925, 475, 75, 400);
spawn.mapRect(9000, 550, 75, 325);
spawn.mapRect(9075, 650, 75, 25);
spawn.mapRect(9075, 625, 75, 250);
spawn.mapRect(9150, 675, 75, 200);
spawn.mapRect(9225, 750, 75, 125);
spawn.mapRect(8925, 450, 25, 25);
spawn.mapRect(9000, 500, 25, 75);
spawn.mapRect(9000, 525, 25, 100);
spawn.mapRect(9000, 525, 50, 100);
spawn.mapRect(9050, 575, 50, 100);
spawn.mapRect(9075, 600, 50, 75);
spawn.mapRect(9150, 650, 25, 100);
spawn.mapRect(9225, 725, 50, 100);
spawn.mapRect(9225, 700, 25, 100);
spawn.mapRect(9300, 800, 1375, 75);
spawn.mapRect(9300, 775, 25, 50);
spawn.mapRect(9425, 775, 200, 25);
spawn.mapRect(9500, 875, 200, 25);
spawn.mapRect(9725, 825, 200, 75);
spawn.mapRect(10000, 850, 125, 50);
spawn.mapRect(10225, 850, 400, 50);
spawn.mapRect(9775, 775, 125, 25);
spawn.mapRect(10100, 775, 75, 75);
spawn.mapRect(10275, 750, 225, 75);
spawn.mapRect(9975, 775, 25, 50);
spawn.mapRect(10000, 775, 25, 50);
spawn.mapRect(10025, 775, 25, 75);
spawn.mapRect(10675, 775, 50, 100);
spawn.mapRect(10725, 725, 50, 150);
spawn.mapRect(10775, 650, 50, 225);
spawn.mapRect(10825, 575, 50, 300);
spawn.mapRect(10875, 500, 50, 375);
spawn.mapRect(10925, 425, 275, 450);
spawn.mapRect(11200, 500, 50, 375);
spawn.mapRect(11250, 575, 50, 300);
spawn.mapRect(11300, 650, 50, 225);
spawn.mapRect(11350, 725, 50, 150);
spawn.mapRect(11400, 775, 50, 100);
spawn.mapRect(10700, 750, 75, 75);
spawn.mapRect(10775, 700, 25, 25);
spawn.mapRect(10750, 700, 75, 75);
spawn.mapRect(10800, 625, 125, 75);
spawn.mapRect(10850, 550, 125, 50);
spawn.mapRect(10900, 475, 150, 75);
spawn.mapRect(11125, 475, 100, 50);
spawn.mapRect(11200, 550, 75, 50);
spawn.mapRect(11275, 625, 50, 75);
spawn.mapRect(11325, 700, 50, 75);
spawn.mapRect(11375, 750, 50, 75);
spawn.mapRect(11550, 225, 1525, 75);
spawn.mapRect(11450, 825, 1625, 50);
spawn.mapRect(11450, 800, 1625, 75);
spawn.mapRect(11525, -350, 50, 650);
spawn.mapRect(11850, -350, 50, 650);
spawn.mapRect(12225, -350, 50, 650);
spawn.mapRect(12600, -350, 50, 650);
spawn.mapRect(13000, -350, 75, 650);
spawn.mapRect(11525, -200, 1525, 50);
spawn.mapRect(11525, 50, 1550, 50);
spawn.mapRect(11525, -400, 1550, 50);
spawn.mapRect(11575, -425, 1450, 50);
spawn.mapRect(11625, -450, 1325, 75);
spawn.mapRect(11700, -475, 1175, 75);
spawn.mapRect(11725, -500, 1125, 75);
spawn.mapRect(11825, -400, 100, 75);
spawn.mapRect(11825, 200, 100, 75);
spawn.mapRect(12200, -375, 100, 50);
spawn.mapRect(12200, 200, 100, 75);
spawn.mapRect(12575, 200, 100, 75);
spawn.mapRect(12575, -375, 100, 50);
spawn.mapRect(11500, 825, 50, 25);
spawn.mapRect(11550, 775, 175, 25);
spawn.mapRect(11525, 875, 250, 25);
spawn.mapRect(11875, 750, 225, 50);
spawn.mapRect(11950, 850, 375, 50);
spawn.mapRect(12500, 775, 250, 75);
spawn.mapRect(12750, 850, 175, 50);
// books
spawn.bodyRect(11575, -300, 25, 100);
spawn.bodyRect(11600, -300, 25, 100);
spawn.bodyRect(11625, -300, 25, 100);
spawn.bodyRect(11650, -300, 25, 100);
spawn.bodyRect(11675, -300, 25, 100);
spawn.bodyRect(11700, -300, 25, 100);
spawn.bodyRect(11725, -300, 25, 100);
spawn.bodyRect(11750, -300, 25, 100);
spawn.bodyRect(11775, -300, 25, 100);
spawn.bodyRect(11800, -300, 25, 100);
spawn.bodyRect(11825, -300, 25, 100);
spawn.bodyRect(11900, -50, 25, 100);
spawn.bodyRect(11925, -50, 25, 100);
spawn.bodyRect(11950, -50, 25, 100);
spawn.bodyRect(11975, -50, 50, 100);
spawn.bodyRect(12025, -50, 50, 100);
spawn.bodyRect(12075, -50, 25, 100);
spawn.bodyRect(12100, -50, 50, 100);
spawn.bodyRect(12150, -50, 25, 100);
spawn.bodyRect(12175, -50, 25, 100);
spawn.bodyRect(12200, -50, 25, 100);
spawn.bodyRect(11900, -300, 25, 100);
spawn.bodyRect(11925, -300, 25, 100);
spawn.bodyRect(11950, -225, 75, 25);
spawn.bodyRect(12650, -50, 25, 100);
spawn.bodyRect(12675, -50, 25, 100);
spawn.bodyRect(12725, -50, 25, 100);
spawn.bodyRect(12750, -50, 50, 100);
spawn.bodyRect(12650, -275, 25, 75);
spawn.bodyRect(12675, -275, 25, 75);
spawn.bodyRect(12700, -275, 50, 75);
spawn.bodyRect(12750, -275, 25, 75);
spawn.bodyRect(12775, -275, 25, 75);
spawn.bodyRect(12800, -275, 25, 75);
spawn.bodyRect(12825, -275, 25, 75);
spawn.bodyRect(12850, -275, 50, 75);
spawn.bodyRect(12900, -275, 50, 75);
spawn.bodyRect(12950, -275, 50, 75);
spawn.mapRect(12200, 775, 175, 50);
spawn.mapRect(11550, 250, 1500, 75);
spawn.mapRect(11575, 275, 1450, 75);
spawn.mapRect(11600, 325, 1400, 50);
spawn.mapRect(11625, 350, 1350, 50);
spawn.mapRect(11725, 375, 1150, 50);
spawn.mapRect(11900, 400, 800, 50);
spawn.mapRect(12100, 425, 425, 50);
spawn.mapRect(12125, 475, 375, 25);
spawn.mapRect(12200, 475, 225, 50);
spawn.mapRect(11475, -400, 50, 700);
spawn.mapRect(11450, -375, 50, 625);
spawn.mapRect(11425, -350, 100, 600);
spawn.mapRect(11400, -300, 75, 525);
spawn.mapRect(11375, -250, 100, 400);
spawn.mapRect(11350, -150, 50, 200);
spawn.mapRect(13075, 825, 25, 50);
spawn.mapRect(13100, 850, 25, 25);
spawn.mapRect(13200, 700, 100, 225);
spawn.mapRect(13300, 775, 25, 100);
spawn.mapRect(13325, 825, 25, 50);
spawn.mapRect(13175, 775, 25, 125);
spawn.mapRect(13225, 675, 50, 25);
spawn.mapRect(13225, 925, 50, 25);
spawn.mapRect(9250, 75, 1400, 150);
spawn.mapRect(9250, -225, 150, 300);
spawn.mapRect(9250, -275, 575, 50);
spawn.mapRect(9675, -225, 150, 300);
spawn.mapRect(9325, -325, 400, 50);
spawn.mapRect(9400, -350, 250, 25);
spawn.mapRect(9475, -375, 125, 25);
spawn.mapRect(9825, -225, 150, 300);
spawn.mapRect(10225, -225, 150, 300);
spawn.mapRect(9825, -275, 550, 50);
spawn.mapRect(9900, -325, 375, 50);
spawn.mapRect(9950, -350, 275, 25);
spawn.mapRect(10000, -375, 175, 25);
spawn.mapRect(10350, -275, 50, 375);
spawn.mapRect(10400, -250, 25, 325);
spawn.mapRect(10425, -225, 25, 375);
spawn.mapRect(10450, -200, 25, 325);
spawn.mapRect(10475, -175, 25, 350);
spawn.mapRect(10500, -150, 25, 300);
spawn.mapRect(10525, -125, 25, 300);
spawn.mapRect(10550, -100, 25, 225);
spawn.mapRect(10575, -75, 25, 200);
spawn.mapRect(10600, -50, 25, 150);
spawn.mapRect(10625, -25, 25, 175);
spawn.mapRect(9225, -225, 25, 450);
spawn.mapRect(9200, -175, 25, 400);
spawn.mapRect(9175, -150, 50, 375);
spawn.mapRect(9150, -125, 50, 350);
spawn.mapRect(9400, -175, 25, 275);
spawn.mapRect(9425, -125, 25, 200);
spawn.mapRect(9650, -175, 25, 250);
spawn.mapRect(9625, -125, 25, 275);
spawn.mapRect(9975, -175, 25, 300);
spawn.mapRect(10000, -125, 25, 250);
spawn.mapRect(10200, -175, 25, 300);
spawn.mapRect(10175, -125, 25, 225);
spawn.mapRect(9325, 225, 225, 25);
spawn.mapRect(9675, 225, 250, 50);
spawn.mapRect(10075, 225, 200, 25);
spawn.mapRect(10400, 200, 175, 50);
spawn.mapRect(13425, 675, 1425, 100);
spawn.mapRect(13450, 725, 375, 75);
spawn.mapRect(13850, 775, 225, 50);
spawn.mapRect(14150, 750, 300, 50);
spawn.mapRect(14575, 750, 200, 75);
spawn.mapRect(13550, 800, 150, 25);
spawn.mapRect(14250, 800, 225, 25);
spawn.mapRect(13425, 275, 1425, 100);
spawn.mapRect(13475, 250, 1325, 75);
spawn.mapRect(13550, 225, 1125, 75);
spawn.mapRect(13600, 200, 1025, 25);
spawn.mapRect(13650, 150, 925, 50);
spawn.mapRect(13725, 100, 775, 100);
spawn.mapRect(13825, 50, 525, 100);
spawn.mapRect(13900, 0, 350, 75);
spawn.mapRect(13975, -25, 175, 75);
spawn.mapRect(13875, 25, 50, 50);
spawn.mapRect(13800, 75, 75, 50);
spawn.mapRect(13700, 125, 75, 50);
spawn.mapRect(13625, 200, 50, 25);
spawn.mapRect(13650, 175, 25, 25);
spawn.mapRect(13625, 175, 125, 75);
spawn.mapRect(14350, 75, 25, 50);
spawn.mapRect(14250, 0, 25, 75);
spawn.mapRect(14275, 50, 25, 50);
spawn.mapRect(14275, 25, 25, 75);
spawn.mapRect(14500, 125, 25, 75);
spawn.mapRect(14575, 175, 25, 75);
spawn.mapRect(13475, 650, 400, 50);
spawn.mapRect(13975, 675, 75, 25);
spawn.mapRect(14000, 650, 50, 50);
spawn.mapRect(14150, 625, 675, 100);
spawn.mapRect(14325, 625, 100, 25);
spawn.mapRect(14300, 600, 325, 25);
spawn.mapRect(13525, 325, 375, 100);
spawn.mapRect(13975, 375, 400, 25);
spawn.mapRect(14500, 325, 100, 75);
spawn.mapRect(14850, 675, 50, 200);
spawn.mapRect(14875, 700, 50, 175);
spawn.mapRect(14925, 725, 50, 150);
spawn.mapRect(14975, 750, 50, 125);
spawn.mapRect(15025, 775, 50, 100);
spawn.mapRect(15075, 750, 1150, 100);
spawn.mapRect(15100, 825, 225, 50);
spawn.mapRect(15500, 850, 225, 25);
spawn.mapRect(15925, 775, 275, 100);
spawn.mapRect(15775, 825, 50, 50);
spawn.mapRect(15225, 250, 1050, 125);
spawn.mapRect(15250, 200, 1000, 50);
spawn.mapRect(15275, 175, 950, 50);
spawn.mapRect(15300, 150, 900, 50);
spawn.mapRect(15325, 125, 850, 25);
spawn.mapRect(15350, 100, 800, 25);
spawn.mapRect(15375, 75, 750, 25);
spawn.mapRect(15400, 50, 700, 100);
spawn.mapRect(15425, 25, 650, 75);
spawn.mapRect(15450, 0, 600, 50);
spawn.mapRect(15475, -25, 550, 75);
spawn.mapRect(15500, -50, 500, 75);
spawn.mapRect(15525, -75, 450, 75);
spawn.mapRect(15550, -100, 400, 75);
spawn.mapRect(15575, -125, 350, 75);
spawn.mapRect(15600, -150, 300, 50);
spawn.mapRect(15625, -175, 250, 50);
spawn.mapRect(15650, -200, 200, 25);
spawn.mapRect(15675, -225, 150, 75);
spawn.mapRect(15700, -250, 100, 75);
spawn.mapRect(16275, 250, 25, 125);
spawn.mapRect(16250, 225, 25, 25);
spawn.mapRect(15200, 250, 25, 125);
spawn.mapRect(15225, 225, 25, 25);
spawn.mapRect(15275, 350, 175, 50);
spawn.mapRect(15550, 350, 425, 75);
spawn.mapRect(16100, 375, 175, 25);
spawn.mapRect(14700, -375, 50, 325);
spawn.mapRect(14700, -425, 375, 50);
spawn.mapRect(14750, -375, 125, 100);
spawn.mapRect(14750, -275, 75, 75);
spawn.mapRect(14850, -375, 100, 50);
spawn.mapRect(14825, -275, 25, 25);
spawn.mapRect(14950, -375, 25, 25);
spawn.mapRect(14875, -325, 25, 25);
spawn.mapRect(14725, -200, 50, 25);
spawn.mapRect(14700, -75, 100, 25);
spawn.mapRect(15050, -425, 25, 100);
spawn.mapRect(14725, -450, 325, 25);
spawn.mapRect(14775, -475, 225, 25);
spawn.mapRect(14825, -500, 125, 25);
spawn.mapRect(14675, -350, 25, 100);
spawn.mapRect(14675, -175, 25, 75);
spawn.mapRect(14850, 325, 25, 50);
spawn.mapRect(5700, -725, 375, 50);
spawn.mapRect(6025, -725, 50, 325);
spawn.mapRect(5775, -675, 250, 25);
spawn.mapRect(6000, -675, 25, 225);
spawn.mapRect(5950, -650, 50, 75);
spawn.mapRect(5900, -650, 75, 25);
spawn.mapRect(6000, -575, 25, 25);
spawn.mapRect(6050, -700, 50, 275);
spawn.mapRect(5925, -625, 75, 25);
spawn.mapRect(5775, -750, 100, 25);
spawn.mapRect(5950, -750, 100, 25);
spawn.mapRect(5675, -725, 25, 150);
spawn.mapRect(5975, -400, 100, 25);
spawn.mapRect(5650, -700, 25, 75);
spawn.mapRect(5700, -675, 25, 50);
spawn.mapRect(5700, -600, 25, 25);
spawn.mapRect(15275, 750, 225, 25);
spawn.mapRect(15225, 725, 250, 25);
spawn.mapRect(15675, 725, 275, 100);
spawn.mapRect(16075, 725, 125, 50);
spawn.pulsar(5775.349354333542, -594.9058498351887)
spawn.pulsar(5852.915433009502, -545.5679375496002)
spawn.pulsar(5921.99534574469, -480.69487503053097)
spawn.mapRect(3725, -975, 1525, 100);
spawn.mapRect(3750, -650, 300, 75);
spawn.mapRect(4300, -650, 300, 75);
spawn.mapRect(4950, -650, 300, 75);
spawn.mapRect(5250, -975, 75, 400);
spawn.mapRect(3725, -975, 75, 400);
spawn.mapRect(4325, -600, 250, 50);
spawn.mapRect(4350, -550, 200, 25);
spawn.mapRect(3800, -575, 225, 25);
spawn.mapRect(3825, -550, 175, 25);
spawn.mapRect(4975, -600, 275, 50);
spawn.mapRect(5025, -550, 175, 25);
spawn.mapRect(3800, -1025, 1450, 50);
spawn.mapRect(3875, -1075, 1275, 50);
spawn.mapRect(3975, -1125, 1000, 50);
spawn.mapRect(3950, -1100, 50, 25);
spawn.mapRect(3850, -1050, 150, 25);
spawn.mapRect(3775, -1000, 200, 25);
spawn.mapRect(5225, -975, 75, 25);
spawn.mapRect(4950, -1100, 75, 125);
spawn.mapRect(5100, -1050, 75, 75);
spawn.mapRect(5225, -1000, 50, 100);
spawn.mapRect(4350, -675, 150, 75);
spawn.mapRect(4525, -650, 50, 25);
spawn.mapRect(4550, -675, 50, 75);
spawn.mapRect(3825, -650, 50, 25);
spawn.mapRect(3825, -675, 150, 50);
spawn.mapRect(4025, -675, 25, 100);
spawn.mapRect(4950, -675, 75, 50);
spawn.mapRect(5075, -675, 75, 75);
spawn.mapRect(5200, -675, 75, 50);
spawn.pulsar(4068.196906578167, -653.550201356403)
spawn.pulsar(4147.672553167598, -651.0093457935446)
spawn.pulsar(4228.863663369247, -653.4768859607283)
spawn.pulsar(4619.092688319791, -657.3942377732394)
spawn.pulsar(4724.821759138369, -653.4213864043036)
spawn.pulsar(4873.583205330713, -657.4103118310311)
spawn.pulsar(3871.920598597728, -804.0595760512573)
spawn.pulsar(4053.019377134256, -778.0061810623848)
spawn.pulsar(4211.732836201937, -780.4633597161928)
spawn.pulsar(4380.7768131190005, -776.3400515412312)
spawn.pulsar(4533.031170401828, -791.1397513503708)
spawn.pulsar(4663.577749297493, -789.0488615794887)
spawn.pulsar(4965.48351658387, -809.0025104385204)
spawn.pulsar(5122.782442346123, -810.2526936643312)
spawn.mapRect(3700, -875, 25, 250);
spawn.mapRect(5325, -900, 25, 250);
spawn.mapRect(5325, -850, 50, 150);
spawn.mapRect(5375, -825, 25, 75);
spawn.pulsar(14786.058375868968, -140.5759223979466)
spawn.pulsar(14862.320083571307, -177.02507110101413)
spawn.pulsar(14888.982047411475, -216.4856450590454)
spawn.pulsar(14950.503812885598, -280.9333882582806)
spawn.pulsar(15003.202057456116, -316.6767970823471)
spawn.spinner(759.4093972764956, -356.0541595435453)
spawn.spinner(1467.1412487475097, -617.4326431210314)
spawn.mapRect(11850, 775, 50, 50);
spawn.mapRect(12075, 775, 50, 50);
spawn.mapRect(16225, 750, 75, 325);
spawn.mapRect(16300, 775, 50, 325);
spawn.mapRect(16350, 800, 50, 275);
spawn.mapRect(16375, 825, 50, 200);
spawn.mapRect(16450, 875, 25, 150);
spawn.mapRect(16450, 875, 25, 225);
spawn.mapRect(16400, 875, 50, 150);
spawn.mapRect(16225, 1025, 250, 75);
spawn.mapRect(16475, 925, 25, 175);
spawn.mapRect(16500, 975, 25, 125);
spawn.mapRect(16525, 1025, 25, 50);
spawn.mapRect(16425, 1075, 150, 25);
spawn.mapRect(16225, 1100, 1175, 75);
spawn.mapRect(17200, 1050, 25, 50);
spawn.mapRect(17225, 950, 25, 200);
spawn.mapRect(17250, 800, 25, 300);
spawn.mapRect(17275, 725, 25, 400);
spawn.mapRect(17300, 750, 75, 400);
spawn.mapRect(17300, 725, 100, 450);
spawn.mapRect(16300, 250, 1075, 125);
spawn.mapRect(16450, -75, 100, 400);
spawn.mapRect(17100, -75, 100, 400);
spawn.mapRect(16425, 200, 150, 50);
spawn.mapRect(17075, 200, 150, 50);
spawn.mapRect(16425, -75, 150, 25);
spawn.mapRect(17075, -75, 150, 50);
spawn.mapRect(16425, -50, 150, 50);
spawn.mapRect(16575, -75, 525, 50);
spawn.mapRect(17075, -50, 150, 50);
spawn.mapRect(16550, -100, 550, 25);
spawn.mapRect(16575, -125, 500, 75);
spawn.mapRect(16600, -150, 450, 75);
spawn.mapRect(16625, -175, 400, 75);
spawn.mapRect(16675, -200, 275, 50);
spawn.mapRect(16750, -225, 125, 100);
spawn.mapRect(19700, 675, 50, 325);
spawn.mapRect(19725, 700, 50, 250);
spawn.mapRect(19750, 750, 25, 175);
spawn.mapRect(16775, -25, 100, 275);
spawn.mapRect(16750, -25, 150, 25);
spawn.mapRect(16750, 225, 150, 50);
spawn.pulsar(3037.797768861211, -1242.9871362505041)
spawn.pulsar(3070.307596879197, -1219.5627538123044)
spawn.pulsar(3111.2633762820287, -1107.7297980154415)
spawn.pulsar(5417.516810698634, 842.824851834252)
spawn.pulsar(5484.672534515589, 883.9519420960905)
spawn.pulsar(5588.5457723826075, 907.389646857348)
spawn.pulsar(16845.139047921595, -885.6942536135854)
spawn.pulsar(16892.187197333486, -849.5235136465661)
spawn.pulsar(16912.323783455256, -764.5275187038021)
powerUps.spawn(2571.591711269197, -145.6717604789277, 'heal')
powerUps.spawn(4415.693974666946, -15.077304620299628, 'heal')
powerUps.spawn(7505.795753124317, -360.0330849392626, 'heal')
powerUps.spawn(7809.5145838152075, -388.5517653996709, 'heal')
powerUps.spawn(8049.726318297545, 534.4543327703304, 'heal')
powerUps.spawn(8514.444440033667, 551.0268033205841, 'heal')
powerUps.spawn(8927.146055851512, 407.25359241772685, 'heal')
powerUps.spawn(9730.170170158563, 463.5594890235955, 'ammo')
powerUps.spawn(9998.34942087522, 434.9511651200589, 'ammo')
powerUps.spawn(10119.083720019844, 437.4195779326937, 'ammo')
powerUps.spawn(10346.197135080345, 423.1868836972815, 'ammo')
powerUps.spawn(1853.3194789931017, -36.87254038474242, 'ammo')
powerUps.spawn(4491.396397908616, 40.2862012621236, 'ammo')
powerUps.spawn(4954.207518897743, 50.27790416201856, 'ammo')
spawn.mapRect(9125, -50, 75, 275);
spawn.mapRect(9100, 0, 50, 225);
spawn.mapRect(9075, 75, 75, 150);
spawn.mapRect(9050, 150, 125, 50);
spawn.mapRect(9050, 200, 225, 25);
mover1 = level.mover(4000, -1125, 975, 25);
mover2 = level.mover(15675, 725, 275, 25);
spawn.mapRect(15025, -375, 25, 25);
spawn.mapRect(12200, -150, 100, 25);
spawn.mapRect(11825, -150, 100, 25);
spawn.mapRect(11825, 75, 100, 50);
spawn.mapRect(12200, 75, 100, 50);
spawn.mapRect(12575, 100, 75, 25);
spawn.mapRect(12625, 50, 50, 75);
spawn.mapRect(12600, -175, 75, 50);
spawn.mapRect(12575, -175, 75, 50);
spawn.mapRect(14125, 650, 75, 25);
spawn.mapRect(13875, 375, 50, 25);
spawn.mapRect(13300, -525, 325, 50);
spawn.mapRect(13575, -525, 50, 250);
spawn.mapRect(13550, -300, 75, 25);
spawn.mapRect(13300, -525, 25, 75);
spawn.mapRect(13525, -475, 50, 50);
spawn.mapRect(13500, -475, 100, 25);
spawn.mapRect(13550, -475, 25, 100);
spawn.mapRect(13325, -550, 275, 25);
spawn.mapRect(13350, -575, 200, 25);
spawn.mapRect(13625, -500, 25, 175);
spawn.mapRect(13650, -450, 25, 75);
spawn.mapRect(13500, 375, 75, 25);
spawn.mapRect(15550, -950, 50, 225);
spawn.mapRect(15575, -750, 75, 25);
spawn.mapRect(15550, -950, 375, 50);
spawn.mapRect(15925, -950, 50, 225);
spawn.mapRect(15875, -750, 100, 25);
spawn.mapRect(15575, -1000, 375, 75);
spawn.mapRect(15625, -1050, 250, 100);
spawn.mapRect(15600, -1025, 75, 50);
spawn.mapRect(15875, -1000, 25, 25);
spawn.mapRect(15825, -1025, 75, 75);
spawn.mapRect(15700, -1100, 125, 50);
spawn.mapRect(15650, -1075, 75, 50);
spawn.mapRect(15800, -1075, 50, 75);
spawn.mapRect(15575, -725, 50, 25);
spawn.mapRect(15900, -725, 50, 25);
spawn.mapRect(15525, -925, 25, 175);
spawn.mapRect(15950, -925, 50, 200);
spawn.mapRect(15500, -875, 25, 75);
spawn.mapRect(16000, -875, 25, 75);
spawn.mapRect(15600, -900, 50, 75);
spawn.mapRect(15650, -900, 25, 25);
spawn.mapRect(15600, -825, 25, 25);
spawn.mapRect(15875, -900, 50, 50);
spawn.mapRect(15850, -925, 75, 50);
spawn.mapRect(15925, -875, 25, 25);
spawn.mapRect(15925, -850, 25, 25);
spawn.mapRect(15900, -875, 50, 50);
bigpool = level.hazard(9075, 575, 1950, 250);
spawn.mapRect(16000, -775, 25, 50);
spawn.mapRect(16000, -800, 25, 25);
spawn.mapRect(15500, -825, 25, 75);
spawn.mapRect(15475, -850, 75, 75);
spawn.mapRect(16000, -850, 50, 100);
spawn.mapRect(10775, -450, 50, 200);
spawn.mapRect(10775, -275, 100, 25);
spawn.mapRect(11100, -450, 50, 200);
spawn.mapRect(11050, -275, 75, 25);
spawn.mapRect(10775, -450, 375, 25);
spawn.mapRect(10825, -475, 275, 25);
spawn.mapRect(10875, -500, 150, 25);
spawn.mapRect(10900, -525, 100, 25);
spawn.mapRect(10750, -425, 25, 150);
spawn.mapRect(11150, -400, 25, 125);
spawn.mapRect(10725, -400, 50, 100);
spawn.mapRect(11150, -375, 50, 75);
spawn.mapRect(10800, -250, 50, 25);
spawn.mapRect(10825, -425, 50, 50);
spawn.mapRect(10875, -425, 25, 25);
spawn.mapRect(10825, -375, 25, 25);
spawn.mapRect(11050, -425, 50, 50);
spawn.mapRect(11025, -425, 25, 25);
spawn.mapRect(11075, -375, 25, 25);
spawn.mapRect(950, -1075, 50, 200);
spawn.mapRect(950, -1125, 300, 50);
spawn.mapRect(1000, -1075, 50, 50);
spawn.mapRect(1050, -1075, 25, 25);
spawn.mapRect(975, -1025, 50, 25);
spawn.mapRect(975, -1150, 250, 25);
spawn.mapRect(1000, -1175, 200, 25);
spawn.mapRect(900, -1075, 50, 175);
spawn.mapRect(875, -1050, 25, 125);
spawn.mapRect(950, -875, 125, 25);
spawn.mapRect(1250, -1125, 25, 125);
spawn.mapRect(975, -850, 75, 25);
spawn.mapRect(1250, -1100, 50, 75);
spawn.mapRect(925, -900, 50, 25);
spawn.mapRect(1050, -1200, 100, 25);
spawn.mapRect(1225, -1000, 50, 25);
spawn.mapRect(16375, 350, 900, 50);
spawn.mapRect(16400, 375, 850, 50);
spawn.mapRect(16425, 400, 800, 50);
spawn.mapRect(16475, 425, 675, 50);
spawn.mapRect(16625, 475, 375, 25);
spawn.mapRect(16650, 500, 325, 25);
spawn.mapRect(16675, 500, 275, 50);
spawn.mapRect(17400, 775, 25, 325);
spawn.mapRect(17425, 825, 25, 225);
spawn.mapRect(16200, 900, 25, 225);
spawn.mapRect(16175, 925, 25, 125);
spawn.mapRect(16150, 975, 25, 25);
spawn.mapRect(16400, 1150, 850, 50);
spawn.mapRect(16475, 1175, 650, 50);
spawn.mapRect(16575, 1225, 450, 25);
spawn.sneaker(7895.471733263175, 257.75477496554186)
spawn.sneaker(8109.4934675858085, 349.44686618726473)
spawn.sneaker(7525.886813944122, 391.9511482895349)
spawn.sneaker(8076.43795816953, 441.14947363958373)
spawn.pulsar(1064.583377612331, -976.2077284446908)
spawn.pulsar(1158.3436115513837, -1054.4975368803214)
spawn.pulsar(10966.055009228428, -373.8481911663377)
spawn.pulsar(10913.989668763379, -261.59108542627166)
spawn.pulsar(13454.158594286884, -402.8270664336466)
spawn.pulsar(13360.079608974078, -246.97797933698774)
spawn.pulsar(13497.913481830354, -251.68317759640576)
spawn.pulsar(15687.09056963911, -850.8426925141155)
spawn.pulsar(15829.058084589731, -785.4134546702737)
spawn.pulsar(15674.313958480483, -685.0594164868394)
spawn.pulsar(15819.881465281747, -686.4370174238113)
spawn.sneakBoss(18189.441342796745, 537.6633241821036)
thirdpool = level.hazard(16425, 925, 925, 200);
spawn.mapRect(17675, -525, 75, 725);
spawn.mapRect(17625, -475, 75, 650);
spawn.mapRect(17575, -425, 75, 575);
spawn.mapRect(17700, -525, 1125, 75);
spawn.mapRect(17675, 175, 1125, 75);
spawn.mapRect(18775, -525, 75, 775);
spawn.mapRect(18825, -475, 75, 675);
spawn.mapRect(18900, -450, 50, 625);
spawn.mapRect(18950, -400, 50, 500);
spawn.mapRect(17750, -575, 1000, 50);
spawn.mapRect(17775, -625, 950, 50);
spawn.mapRect(17800, -675, 900, 75);
spawn.mapRect(17825, -725, 850, 125);
spawn.mapRect(17850, -750, 800, 25);
spawn.mapRect(17750, 125, 50, 50);
spawn.mapRect(17750, 100, 25, 25);
spawn.mapRect(17800, 150, 25, 25);
spawn.mapRect(17750, -450, 75, 75);
spawn.mapRect(17750, -400, 25, 50);
spawn.mapRect(17800, -450, 50, 25);
spawn.mapRect(18750, -450, 25, 25);
spawn.mapRect(18725, -450, 50, 50);
spawn.mapRect(18700, -475, 25, 25);
spawn.mapRect(18725, -450, 25, 25);
spawn.mapRect(18700, -450, 75, 25);
spawn.mapRect(18750, -425, 25, 50);
spawn.mapRect(18725, 125, 75, 75);
spawn.mapRect(18700, 150, 50, 50);
spawn.mapRect(18750, 100, 75, 50);
spawn.mapRect(17850, 150, 850, 50);
spawn.mapRect(17825, 150, 25, 50);
spawn.mapRect(17550, -350, 25, 450);
spawn.mapRect(19000, -325, 25, 400);
spawn.mapRect(18000, -775, 475, 25);
spawn.mapRect(18025, -800, 425, 75);
spawn.mapRect(18050, -825, 375, 75);
spawn.mapRect(18075, -850, 325, 50);
spawn.mapRect(18100, -875, 275, 100);
spawn.mapRect(18125, -900, 225, 75);
spawn.mapRect(18150, -925, 175, 75);
spawn.mapRect(17275, 750, 1775, 125);
spawn.mapRect(17475, 725, 450, 50);
spawn.mapRect(18200, 725, 200, 50);
spawn.mapRect(18650, 725, 225, 75);
spawn.shieldingBoss(18253.51035871325, -131.1707821125636)
// spawn.blockBoss(12604.846253470663, 607.6074958800299)
spawn.mapRect(17725, 250, 1025, 25);
spawn.mapRect(17775, 275, 925, 25);
spawn.mapRect(17800, 300, 875, 25);
spawn.mapRect(17850, 325, 775, 25);
spawn.mapRect(17375, 275, 25, 75);
spawn.mapRect(19050, 750, 25, 275);
spawn.mapRect(19075, 775, 25, 250);
spawn.mapRect(19100, 800, 25, 225);
spawn.mapRect(19125, 850, 25, 175);
spawn.mapRect(19150, 875, 25, 150);
spawn.mapRect(19175, 925, 25, 100);
spawn.mapRect(19200, 950, 25, 75);
spawn.mapRect(19000, 850, 100, 175);
spawn.mapRect(19050, 975, 650, 50);
spawn.mapRect(19425, 650, 275, 50);
spawn.mapRect(19675, 650, 50, 375);
spawn.mapRect(19050, 1025, 625, 25);
spawn.mapRect(19075, 1050, 575, 25);
spawn.mapRect(19250, 1100, 200, 25);
spawn.mapRect(19175, 1075, 375, 25);
spawn.mapRect(19450, 625, 225, 25);
spawn.mapRect(19500, 600, 150, 50);
spawn.mapRect(19625, 700, 50, 50);
spawn.mapRect(19600, 700, 25, 25);
spawn.mapRect(19650, 750, 25, 25);
spawn.mapRect(19400, 650, 25, 100);
spawn.mapRect(19375, 675, 25, 50);
spawn.mapRect(17600, 875, 250, 25);
spawn.mapRect(18100, 850, 375, 50);
spawn.mapRect(18650, 875, 325, 25);
pooldunker = level.mover(7175, 425, 50, 25);
level.custom = () => {
level.exit.drawAndCheck();
pooldunker.VxGoal = 90;
pooldunker.push();
mover.VxGoal = 45;
mover.push();
level.enter.draw();
pool.query();
wastepool.query();
thirdpool.query();
mover1.VxGoal = 12;
mover1.push();
mover2.VxGoal = 24;
mover2.push();
bigpool.query();
for (i = 0; i < mob.length; i++) { if (mob[i].isBoss == false) { mob[i].damageReduction = 0.13 } }
};
spawn.mapRect(-100, 0, 1000, 100);
powerUps.addResearchToLevel() //needs to run after mobs are spawned
},
ace() { //join us at discord.gg/Q8gY4WeUcm
simulation.makeTextLog(`<strong>ace</strong> by <span class='color-var'>Richard0820</span>`);
let isDestroyed = false;
const ace = {
spawnOrbitals(who, radius, chance = Math.min(0.25 + simulation.difficulty * 0.005)) {
if (Math.random() < chance) {
// simulation.difficulty = 50
const len = Math.floor(Math.min(15, 3 + Math.sqrt(simulation.difficulty))) // simulation.difficulty = 40 on hard mode level 10
const speed = (0.003 + 0.004 * Math.random() + 0.002 * Math.sqrt(simulation.difficulty)) * ((Math.random() < 0.5) ? 1 : -1)
const offSet = 6.28 * Math.random()
for (let i = 0; i < len; i++) ace.orbital(who, radius, i / len * 2 * Math.PI + offSet, speed)
}
},
orbital(who, radius, phase, speed) {
// for (let i = 0, len = 7; i < len; i++) spawn.orbital(me, radius + 250, 2 * Math.PI / len * i)
mobs.spawn(who.position.x, who.position.y, 8, 12, "rgb(0,0,0)");
let me = mob[mob.length - 1];
me.stroke = "transparent";
Matter.Body.setDensity(me, 0.01); //normal is 0.001
me.leaveBody = false;
me.isDropPowerUp = false;
me.isBadTarget = true;
me.isUnstable = true; //dies when blocked
me.showHealthBar = false;
me.isOrbital = true;
// me.isShielded = true
me.collisionFilter.category = cat.mobBullet;
me.collisionFilter.mask = cat.bullet; //cat.player | cat.map | cat.body
me.do = function () {
//if host is gone
if (!who || !who.alive) {
this.death();
return
}
//set orbit
const time = simulation.cycle * speed + phase
const orbit = {
x: Math.cos(time),
y: Math.sin(time)
}
Matter.Body.setPosition(this, Vector.add(Vector.add(who.position, who.velocity), Vector.mult(orbit, radius)))
//damage player
if (Matter.Query.collides(this, [player]).length > 0 && !(m.isCloak && tech.isIntangible) && m.immuneCycle < m.cycle) {
m.immuneCycle = m.cycle + m.collisionImmuneCycles; //player is immune to damage for 30 cycles
const dmg = 0.03 * simulation.dmgScale
m.damage(dmg);
simulation.drawList.push({ //add dmg to draw queue
x: this.position.x,
y: this.position.y,
radius: Math.sqrt(dmg) * 200,
color: simulation.mobDmgColor,
time: simulation.drawTime
});
this.death();
}
};
},
shield(target, x, y, chance = Math.min(0.02 + simulation.difficulty * 0.005, 0.2) + tech.duplicationChance(), isExtraShield = false) {
if (this.allowShields && Math.random() < chance) {
mobs.spawn(x, y, 9, target.radius + 30, "rgba(255,255,255,0.9)");
let me = mob[mob.length - 1];
me.stroke = "rgb(0,0,0)";
Matter.Body.setDensity(me, 0.00001) //very low density to not mess with the original mob's motion
me.shield = true;
me.damageReduction = 0.05 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1)
me.isUnblockable = true
me.isExtraShield = isExtraShield //this prevents spamming with tech.isShieldAmmo
me.collisionFilter.category = cat.mobShield
me.collisionFilter.mask = cat.bullet;
consBB[consBB.length] = Constraint.create({
bodyA: me,
bodyB: target, //attach shield to target
stiffness: 0.4,
damping: 0.1
});
Composite.add(engine.world, consBB[consBB.length - 1]);
me.onDamage = function () {
//make sure the mob that owns the shield can tell when damage is done
this.alertNearByMobs();
this.fill = `rgba(255,255,255,${0.3 + 0.6 * this.health})`
};
me.leaveBody = false;
me.isDropPowerUp = false;
me.showHealthBar = false;
me.shieldTargetID = target.id
target.isShielded = true;
target.shieldID = me.id
me.onDeath = function () {
//clear isShielded status from target
for (let i = 0, len = mob.length; i < len; i++) {
if (mob[i].id === this.shieldTargetID) mob[i].isShielded = false;
}
};
me.do = function () {
this.checkStatus();
};
mob.unshift(me); //move shield to the front of the array, so that mob is behind shield graphically
//swap order of shield and mob, so that mob is behind shield graphically
// mob[mob.length - 1] = mob[mob.length - 2];
// mob[mob.length - 2] = me;
}
},
groupShield(targets, x, y, radius, stiffness = 0.4) {
const nodes = targets.length
mobs.spawn(x, y, 9, radius, "rgba(255,255,255,0.9)");
let me = mob[mob.length - 1];
me.stroke = "rgb(0,0,0)";
Matter.Body.setDensity(me, 0.00001) //very low density to not mess with the original mob's motion
me.frictionAir = 0;
me.shield = true;
me.damageReduction = 0.075 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1)
me.collisionFilter.category = cat.mobShield
me.collisionFilter.mask = cat.bullet;
for (let i = 0; i < nodes; ++i) {
mob[mob.length - i - 2].isShielded = true;
//constrain to all mob nodes in group
consBB[consBB.length] = Constraint.create({
bodyA: me,
bodyB: mob[mob.length - i - 2],
stiffness: stiffness,
damping: 0.1
});
Composite.add(engine.world, consBB[consBB.length - 1]);
}
me.onDamage = function () {
this.alertNearByMobs(); //makes sure the mob that owns the shield can tell when damage is done
this.fill = `rgba(255,255,255,${0.3 + 0.6 * this.health})`
};
me.onDeath = function () {
//clear isShielded status from target
for (let j = 0; j < targets.length; j++) {
for (let i = 0, len = mob.length; i < len; i++) {
if (mob[i].id === targets[j]) mob[i].isShielded = false;
}
}
};
me.leaveBody = false;
me.isDropPowerUp = false;
me.showHealthBar = false;
mob[mob.length - 1] = mob[mob.length - 1 - nodes];
mob[mob.length - 1 - nodes] = me;
me.do = function () {
this.checkStatus();
};
},
slasher2(x, y, radius = 33 + Math.ceil(Math.random() * 30)) {
mobs.spawn(x, y, 6, radius, "rgb(0,0,0)");
let me = mob[mob.length - 1];
Matter.Body.rotate(me, 2 * Math.PI * Math.random());
me.accelMag = 0.0009 * simulation.accelScale;
me.torqueMagnitude = 0.000012 * me.inertia //* (Math.random() > 0.5 ? -1 : 1);
me.frictionStatic = 0;
me.friction = 0;
me.frictionAir = 0.035;
me.delay = 140 * simulation.CDScale;
me.cd = 0;
me.swordRadius = 0;
me.swordVertex = 1
me.swordRadiusMax = 275 + 3.5 * simulation.difficulty;
me.swordRadiusGrowRate = me.swordRadiusMax * (0.011 + 0.0002 * simulation.difficulty)
me.isSlashing = false;
me.swordDamage = 0.03 * simulation.dmgScale
me.laserAngle = 3 * Math.PI / 5
const seeDistance2 = 200000
ace.shield(me, x, y);
me.onDamage = function () { };
me.do = function () {
this.checkStatus();
this.seePlayerByHistory(15);
this.attraction();
this.sword() //does various things depending on what stage of the sword swing
};
me.swordWaiting = function () {
if (
this.seePlayer.recall &&
this.cd < simulation.cycle &&
this.distanceToPlayer2() < seeDistance2 &&
Matter.Query.ray(map, this.position, this.playerPosRandomY()).length === 0 &&
Matter.Query.ray(body, this.position, this.playerPosRandomY()).length === 0
) {
this.laserAngle = -Math.PI / 6
this.sword = this.swordGrow
this.accelMag = 0
}
}
me.sword = me.swordWaiting //base function that changes during different aspects of the sword swing
me.swordGrow = function () {
this.laserSword(this.vertices[0], this.angle + this.laserAngle);
this.laserSword(this.vertices[1], this.angle + this.laserAngle + (Math.PI / 3));
this.laserSword(this.vertices[2], this.angle + this.laserAngle + (Math.PI * 2 / 3));
this.laserSword(this.vertices[3], this.angle + this.laserAngle + Math.PI);
this.laserSword(this.vertices[4], this.angle + this.laserAngle + (Math.PI * 4 / 3));
this.laserSword(this.vertices[5], this.angle + this.laserAngle + (Math.PI * 5 / 3));
this.swordRadius += this.swordRadiusGrowRate
if (this.swordRadius > this.swordRadiusMax || this.isStunned) {
this.sword = this.swordSlash
this.spinCount = 0
}
}
me.swordSlash = function () {
this.laserSword(this.vertices[0], this.angle + this.laserAngle);
this.laserSword(this.vertices[1], this.angle + this.laserAngle + (Math.PI / 3));
this.laserSword(this.vertices[2], this.angle + this.laserAngle + (Math.PI * 2 / 3));
this.laserSword(this.vertices[3], this.angle + this.laserAngle + Math.PI);
this.laserSword(this.vertices[4], this.angle + this.laserAngle + (Math.PI * 4 / 3));
this.laserSword(this.vertices[5], this.angle + this.laserAngle + (Math.PI * 5 / 3));
this.torque += this.torqueMagnitude;
this.spinCount++
if (this.spinCount > 100 || this.isStunned) {
this.sword = this.swordWaiting
this.swordRadius = 0
this.accelMag = 0.001 * simulation.accelScale;
this.cd = simulation.cycle + this.delay;
}
}
me.laserSword = function (where, angle) {
const vertexCollision = function (v1, v1End, domain) {
for (let i = 0; i < domain.length; ++i) {
let v = domain[i].vertices;
const len = v.length - 1;
for (let j = 0; j < len; j++) {
results = simulation.checkLineIntersection(v1, v1End, v[j], v[j + 1]);
if (results.onLine1 && results.onLine2) {
const dx = v1.x - results.x;
const dy = v1.y - results.y;
const dist2 = dx * dx + dy * dy;
if (dist2 < best.dist2 && (!domain[i].mob || domain[i].alive)) best = { x: results.x, y: results.y, dist2: dist2, who: domain[i], v1: v[j], v2: v[j + 1] };
}
}
results = simulation.checkLineIntersection(v1, v1End, v[0], v[len]);
if (results.onLine1 && results.onLine2) {
const dx = v1.x - results.x;
const dy = v1.y - results.y;
const dist2 = dx * dx + dy * dy;
if (dist2 < best.dist2) best = { x: results.x, y: results.y, dist2: dist2, who: domain[i], v1: v[0], v2: v[len] };
}
}
};
best = { x: null, y: null, dist2: Infinity, who: null, v1: null, v2: null };
const look = { x: where.x + this.swordRadius * Math.cos(angle), y: where.y + this.swordRadius * Math.sin(angle) };
vertexCollision(where, look, body); // vertexCollision(where, look, mob);
vertexCollision(where, look, map);
if (!m.isCloak) vertexCollision(where, look, [playerBody, playerHead]);
if (best.who && (best.who === playerBody || best.who === playerHead) && m.immuneCycle < m.cycle) {
m.immuneCycle = m.cycle + m.collisionImmuneCycles + 60; //player is immune to damage for an extra second
m.damage(this.swordDamage);
simulation.drawList.push({ //add dmg to draw queue
x: best.x,
y: best.y,
radius: this.swordDamage * 1500,
color: "rgba(80,0,255,0.5)",
time: 20
});
}
if (best.dist2 === Infinity) best = look;
ctx.beginPath(); //draw beam
ctx.moveTo(where.x, where.y);
ctx.lineTo(best.x, best.y);
ctx.strokeStyle = "rgba(0,0,0,0.1)"; // 0 path
ctx.lineWidth = 15;
ctx.stroke();
ctx.strokeStyle = "rgba(0,0,0,0.5)"; // 0 path
ctx.lineWidth = 4;
ctx.setLineDash([70 + 300 * Math.random(), 55 * Math.random()]);
ctx.stroke(); // Draw it
ctx.setLineDash([]);
}
},
slasher3(x, y, radius = 33 + Math.ceil(Math.random() * 30)) {
const sides = 6
mobs.spawn(x, y, sides, radius, "rgb(0,0,0)");
let me = mob[mob.length - 1];
Matter.Body.rotate(me, 2 * Math.PI * Math.random());
me.accelMag = 0.0005 * simulation.accelScale;
me.frictionStatic = 0;
me.friction = 0;
me.frictionAir = 0.02;
me.delay = 150 * simulation.CDScale;
me.cd = 0;
me.cycle = 0;
me.swordVertex = 1
me.swordRadiusInitial = radius / 2;
me.swordRadius = me.swordRadiusInitial;
me.swordRadiusMax = 750 + 6 * simulation.difficulty;
me.swordRadiusGrowRateInitial = 1.08
me.swordRadiusGrowRate = me.swordRadiusGrowRateInitial//me.swordRadiusMax * (0.009 + 0.0002 * simulation.difficulty)
me.isSlashing = false;
me.swordDamage = 0.04 * simulation.dmgScale
me.laserAngle = 3 * Math.PI / 5
const seeDistance2 = me.swordRadiusMax * me.swordRadiusMax
ace.shield(me, x, y);
me.onDamage = function () { };
me.do = function () {
this.checkStatus();
this.seePlayerByHistory(15);
this.sword() //does various things depending on what stage of the sword swing
};
me.swordWaiting = function () {
this.attraction();
if (
this.seePlayer.recall &&
this.cd < simulation.cycle &&
this.distanceToPlayer2() < seeDistance2 &&
Matter.Query.ray(map, this.position, this.playerPosRandomY()).length === 0 &&
Matter.Query.ray(body, this.position, this.playerPosRandomY()).length === 0
) {
//find vertex closest to the player
let dist = Infinity
for (let i = 0, len = this.vertices.length; i < len; i++) {
const D = Vector.magnitudeSquared(Vector.sub({ x: this.vertices[i].x, y: this.vertices[i].y }, m.pos))
if (D < dist) {
dist = D
this.swordVertex = i
}
}
this.laserAngle = this.swordVertex / sides * 2 * Math.PI + Math.PI / sides
this.sword = this.swordGrow
this.cycle = 0
this.swordRadius = this.swordRadiusInitial
//slow velocity but don't stop
Matter.Body.setVelocity(this, Vector.mult(this.velocity, 0.5))
//set angular velocity to 50%
// Matter.Body.setAngularVelocity(this, this.angularVelocity * 0.5)
//gently rotate towards the player with a torque, use cross product to decided clockwise or counterclockwise
const laserStartVector = Vector.sub(this.position, this.vertices[this.swordVertex])
const playerVector = Vector.sub(this.position, m.pos)
const cross = Matter.Vector.cross(laserStartVector, playerVector)
this.torque = 0.00002 * this.inertia * (cross > 0 ? 1 : -1)
}
}
me.sword = me.swordWaiting //base function that changes during different aspects of the sword swing
me.swordGrow = function () {
const angle = this.angle + this.laserAngle;
const end = {
x: this.vertices[this.swordVertex].x + this.swordRadiusMax * Math.cos(angle),
y: this.vertices[this.swordVertex].y + this.swordRadiusMax * Math.sin(angle)
};
const dx = end.x - this.vertices[this.swordVertex + 1 > (sides - 1) ? 0 : this.swordVertex + 1].x;
const dy = end.y - this.vertices[this.swordVertex + 1 > (sides - 1) ? 0 : this.swordVertex + 1].y;
const angle1 = Math.atan2(dy, dx) * (180 / Math.PI);
const dx1 = end.x - this.vertices[this.swordVertex - 1 < 0 ? (sides - 1) : this.swordVertex - 1].x;
const dy1 = end.y - this.vertices[this.swordVertex - 1 < 0 ? (sides - 1) : this.swordVertex - 1].y;
const angle2 = Math.atan2(dy1, dx1) * (180 / Math.PI);
this.laserSpear(this.vertices[this.swordVertex], this.angle + this.laserAngle);
this.laserSpear(this.vertices[this.swordVertex + 1 > (sides - 1) ? 0 : this.swordVertex + 1], angle1 * (Math.PI / 180))
this.laserSpear(this.vertices[this.swordVertex - 1 < 0 ? (sides - 1) : this.swordVertex - 1], angle2 * (Math.PI / 180))
Matter.Body.setVelocity(this, Vector.mult(this.velocity, 0.9))
// this.swordRadius += this.swordRadiusGrowRate
this.cycle++
// this.swordRadius = this.swordRadiusMax * Math.sin(this.cycle * 0.03)
this.swordRadius *= this.swordRadiusGrowRate
if (this.swordRadius > this.swordRadiusMax) this.swordRadiusGrowRate = 1 / this.swordRadiusGrowRateInitial
// if (this.swordRadius > this.swordRadiusMax) this.swordRadiusGrowRate = -Math.abs(this.swordRadiusGrowRate)
if (this.swordRadius < this.swordRadiusInitial || this.isStunned) {
// this.swordRadiusGrowRate = Math.abs(this.swordRadiusGrowRate)
this.swordRadiusGrowRate = this.swordRadiusGrowRateInitial
this.sword = this.swordWaiting
this.swordRadius = 0
this.cd = simulation.cycle + this.delay;
}
}
me.laserSpear = function (where, angle) {
const vertexCollision = function (v1, v1End, domain) {
for (let i = 0; i < domain.length; ++i) {
let v = domain[i].vertices;
const len = v.length - 1;
for (let j = 0; j < len; j++) {
results = simulation.checkLineIntersection(v1, v1End, v[j], v[j + 1]);
if (results.onLine1 && results.onLine2) {
const dx = v1.x - results.x;
const dy = v1.y - results.y;
const dist2 = dx * dx + dy * dy;
if (dist2 < best.dist2 && (!domain[i].mob || domain[i].alive)) best = { x: results.x, y: results.y, dist2: dist2, who: domain[i], v1: v[j], v2: v[j + 1] };
}
}
results = simulation.checkLineIntersection(v1, v1End, v[0], v[len]);
if (results.onLine1 && results.onLine2) {
const dx = v1.x - results.x;
const dy = v1.y - results.y;
const dist2 = dx * dx + dy * dy;
if (dist2 < best.dist2) best = { x: results.x, y: results.y, dist2: dist2, who: domain[i], v1: v[0], v2: v[len] };
}
}
};
best = { x: null, y: null, dist2: Infinity, who: null, v1: null, v2: null };
const look = { x: where.x + this.swordRadius * Math.cos(angle), y: where.y + this.swordRadius * Math.sin(angle) };
vertexCollision(where, look, body); // vertexCollision(where, look, mob);
vertexCollision(where, look, map);
if (!m.isCloak) vertexCollision(where, look, [playerBody, playerHead]);
if (best.who && (best.who === playerBody || best.who === playerHead)) {
this.swordRadiusGrowRate = 1 / this.swordRadiusGrowRateInitial //!!!! this retracts the sword if it hits the player
if (m.immuneCycle < m.cycle) {
m.immuneCycle = m.cycle + m.collisionImmuneCycles + 60; //player is immune to damage for an extra second
m.damage(this.swordDamage);
simulation.drawList.push({ //add dmg to draw queue
x: best.x,
y: best.y,
radius: this.swordDamage * 1500,
color: "rgba(80,0,255,0.5)",
time: 20
});
}
}
if (best.dist2 === Infinity) best = look;
ctx.beginPath(); //draw beam
ctx.moveTo(where.x, where.y);
ctx.lineTo(best.x, best.y);
ctx.strokeStyle = "rgba(0,0,0,0.1)"; // 0 path
ctx.lineWidth = 15;
ctx.stroke();
ctx.strokeStyle = "rgba(0,0,0,0.5)"; // 0 path
ctx.lineWidth = 4;
ctx.setLineDash([70 + 300 * Math.random(), 55 * Math.random()]);
ctx.stroke(); // Draw it
ctx.setLineDash([]);
}
},
stabber(x, y, radius = 25 + Math.ceil(Math.random() * 12), spikeMax = 7) {
if (radius > 80) radius = 65;
mobs.spawn(x, y, 6, radius, "rgb(0,0,0)"); //can't have sides above 6 or collision events don't work (probably because of a convex problem)
let me = mob[mob.length - 1];
me.isVerticesChange = true
me.accelMag = 0.0006 * simulation.accelScale;
// me.g = 0.0002; //required if using this.gravity
me.isInvulnerable = false
me.delay = 360 * simulation.CDScale;
me.spikeVertex = 0;
me.spikeLength = 0;
me.isSpikeGrowing = false;
me.spikeGrowth = 0;
me.isSpikeReset = true;
me.collisionFilter.mask = cat.map | cat.body | cat.bullet | cat.player //can't touch other mobs
Matter.Body.rotate(me, Math.PI * 0.1);
ace.shield(me, x, y);
// me.onDamage = function () {};
// me.onHit = function() { //run this function on hitting player
// };
me.onDeath = function () {
if (this.spikeLength > 4) {
this.spikeLength = 4
const spike = Vector.mult(Vector.normalise(Vector.sub(this.vertices[this.spikeVertex], this.position)), this.radius * this.spikeLength)
this.vertices[this.spikeVertex].x = this.position.x + spike.x
this.vertices[this.spikeVertex].y = this.position.y + spike.y
// this.vertices = Matter.Vertices.hull(Matter.Vertices.clockwiseSort(this.vertices))
}
};
me.do = function () {
this.seePlayerByLookingAt();
this.checkStatus();
this.attraction();
if (this.isSpikeReset) {
if (this.seePlayer.recall) {
const dist = Vector.sub(this.seePlayer.position, this.position);
const distMag = Vector.magnitude(dist);
if (distMag < radius * spikeMax) {
//find nearest vertex
let nearestDistance = Infinity
for (let i = 0, len = this.vertices.length; i < len; i++) {
//find distance to player for each vertex
const dist = Vector.sub(this.seePlayer.position, this.vertices[i]);
const distMag = Vector.magnitude(dist);
//save the closest distance
if (distMag < nearestDistance) {
this.spikeVertex = i
nearestDistance = distMag
}
}
this.spikeLength = 1
this.isSpikeGrowing = true;
this.isSpikeReset = false;
Matter.Body.setAngularVelocity(this, 0)
}
me.isInvulnerable = false
}
} else {
if (this.isSpikeGrowing) {
this.spikeLength += Math.pow(this.spikeGrowth += 0.02, 8)
// if (this.spikeLength < 2) {
// this.spikeLength += 0.035
// } else {
// this.spikeLength += 1
// }
if (this.spikeLength > spikeMax) {
this.isSpikeGrowing = false;
this.spikeGrowth = 0
}
} else {
Matter.Body.setAngularVelocity(this, this.angularVelocity * 0.8) //reduce rotation
this.spikeLength -= 0.3
if (this.spikeLength < 1) {
this.spikeLength = 1
this.isSpikeReset = true
this.radius = radius
}
}
const spike = Vector.mult(Vector.normalise(Vector.sub(this.vertices[this.spikeVertex], this.position)), radius * this.spikeLength)
this.vertices[this.spikeVertex].x = this.position.x + spike.x
this.vertices[this.spikeVertex].y = this.position.y + spike.y
me.isInvulnerable = true
// this.radius = radius * this.spikeLength;
}
if (this.isInvulnerable) {
ctx.beginPath();
let vertices = this.vertices;
ctx.moveTo(vertices[0].x, vertices[0].y);
for (let j = 1; j < vertices.length; j++) ctx.lineTo(vertices[j].x, vertices[j].y);
ctx.lineTo(vertices[0].x, vertices[0].y);
ctx.lineWidth = 13 + 5 * Math.random();
ctx.strokeStyle = `rgba(255,255,255,${0.5 + 0.2 * Math.random()})`;
ctx.stroke();
me.damageReduction = 0;
} else {
me.damageReduction = 1;
}
};
},
slash(x, y, radius = 80) {
let targets = []
const sides = 6;
mobs.spawn(x, y, 6, radius, "#000000");
let me = mob[mob.length - 1];
Matter.Body.rotate(me, 2 * Math.PI * Math.random());
targets.push(me.id) //add to shield protection
const nodeBalance = Math.random()
const nodes2 = Math.min(15, Math.floor(2 + 4 * nodeBalance + 0.75 * Math.sqrt(simulation.difficulty)))
me.isBoss = true;
me.isSlashBoss = true;
me.showHealthBar = false;
me.damageReduction = 0.1 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1)
me.startingDamageReduction = me.damageReduction
me.isInvulnerable = false
me.frictionAir = 0.02
me.seeAtDistance2 = 1000000;
me.accelMag = 0.0004 + 0.00015 * simulation.accelScale;
Matter.Body.setDensity(me, 0.0005); //normal is 0.001
me.collisionFilter.mask = cat.bullet | cat.player | cat.body | cat.map
me.memory = Infinity;
me.seePlayerFreq = 20
me.lockedOn = null;
me.laserRange = 500;
me.torqueMagnitude = 0.00024 * me.inertia * (Math.random() > 0.5 ? -1 : 1);
me.delay = 70 + 70 * simulation.CDScale;
me.cd = 0;
me.swordRadius = 0;
me.swordVertex = 1
me.swordRadiusMax = 1100 + 20 * simulation.difficulty;
me.swordRadiusGrowRate = me.swordRadiusMax * (0.005 + 0.0003 * simulation.difficulty)
me.isSlashing = false;
me.swordDamage = 0.07 * simulation.dmgScale
me.laserAngle = 3 * Math.PI / 5
me.eventHorizon = 550;
const seeDistance2 = 200000
ace.shield(me, x, y);
const rangeInnerVsOuter = Math.random()
let speed = (0.006 + 0.001 * Math.sqrt(simulation.difficulty)) * ((Math.random() < 0.5) ? 1 : -1)
let range = radius + 350 + 200 * rangeInnerVsOuter + nodes2 * 7
for (let i = 0; i < nodes2; i++) ace.orbital(me, range, i / nodes2 * 2 * Math.PI, speed)
const orbitalIndexes = [] //find indexes for all the current nodes2
for (let i = 0; i < nodes2; i++) orbitalIndexes.push(mob.length - 1 - i)
// add orbitals for each orbital
range = Math.max(60, 100 + 100 * Math.random() - nodes2 * 3 - rangeInnerVsOuter * 80)
speed = speed * (1.25 + 2 * Math.random())
const subNodes = Math.max(2, Math.floor(6 - 5 * nodeBalance + 0.5 * Math.sqrt(simulation.difficulty)))
for (let j = 0; j < nodes2; j++) {
for (let i = 0, len = subNodes; i < len; i++) ace.orbital(mob[orbitalIndexes[j]], range, i / len * 2 * Math.PI, speed)
}
for (let i = 0, len = 3 + 0.5 * Math.sqrt(simulation.difficulty); i < len; i++) ace.spawnOrbitals(me, radius + 40 + 10 * i, 1);
const springStiffness = 0.00014;
const springDampening = 0.0005;
me.springTarget = {
x: me.position.x,
y: me.position.y
};
const len = cons.length;
cons[len] = Constraint.create({
pointA: me.springTarget,
bodyB: me,
stiffness: springStiffness,
damping: springDampening
});
Composite.add(engine.world, cons[cons.length - 1]);
cons[len].length = 100 + 1.5 * radius;
me.cons = cons[len];
me.springTarget2 = {
x: me.position.x,
y: me.position.y
};
const len2 = cons.length;
cons[len2] = Constraint.create({
pointA: me.springTarget2,
bodyB: me,
stiffness: springStiffness,
damping: springDampening,
length: 0
});
Composite.add(engine.world, cons[cons.length - 1]);
cons[len2].length = 100 + 1.5 * radius;
me.cons2 = cons[len2];
me.onDamage = function () { };
me.onDeath = function () {
isDestroyed = true;
this.removeCons();
powerUps.spawnBossPowerUp(this.position.x, this.position.y);
};
me.do = function () {
for (let i = 0; i < this.vertices.length; i++) {
this.harmField(this.vertices[i].x, this.vertices[i].y);
}
this.seePlayerByHistory(40);
this.springAttack();
this.checkStatus();
this.sword() //does various things depending on what stage of the sword swing
const eventHorizon = this.eventHorizon * (1 + 0.2 * Math.sin(simulation.cycle * 0.008))
me.laserRange = eventHorizon;
};
me.swordWaiting = function () {
if (
this.seePlayer.recall &&
this.cd < simulation.cycle &&
this.distanceToPlayer2() < seeDistance2 &&
!m.isCloak &&
Matter.Query.ray(map, this.position, this.playerPosRandomY()).length === 0 &&
Matter.Query.ray(body, this.position, this.playerPosRandomY()).length === 0
) {
//find vertex farthest away from player
let dist = 0
for (let i = 0, len = this.vertices.length; i < len; i++) {
const D = Vector.magnitudeSquared(Vector.sub({ x: this.vertices[i].x, y: this.vertices[i].y }, m.pos))
if (D > dist) {
dist = D
this.swordVertex = i
}
}
this.laserAngle = this.swordVertex / 6 * 2 * Math.PI + 0.6283
this.sword = this.swordGrow
Matter.Body.setAngularVelocity(this, 0)
this.accelMag = 0.0004 + 0.00015 * simulation.accelScale;
this.damageReduction = 0
this.isInvulnerable = true
this.frictionAir = 1
}
}
me.sword = me.swordWaiting //base function that changes during different aspects of the sword swing
me.swordGrow = function () {
const angle = this.angle + this.laserAngle;
const end = {
x: this.vertices[this.swordVertex].x + this.swordRadiusMax * Math.cos(angle),
y: this.vertices[this.swordVertex].y + this.swordRadiusMax * Math.sin(angle)
};
const dx = end.x - this.vertices[this.swordVertex + 1 > (sides - 1) ? 0 : this.swordVertex + 1].x;
const dy = end.y - this.vertices[this.swordVertex + 1 > (sides - 1) ? 0 : this.swordVertex + 1].y;
const angle1 = Math.atan2(dy, dx) * (180 / Math.PI);
const dx1 = end.x - this.vertices[this.swordVertex - 1 < 0 ? (sides - 1) : this.swordVertex - 1].x;
const dy1 = end.y - this.vertices[this.swordVertex - 1 < 0 ? (sides - 1) : this.swordVertex - 1].y;
const angle2 = Math.atan2(dy1, dx1) * (180 / Math.PI);
this.laserSword(this.vertices[this.swordVertex], this.angle + this.laserAngle);
this.laserSword(this.vertices[this.swordVertex + 1 > (sides - 1) ? 0 : this.swordVertex + 1], angle1 * (Math.PI / 180))
this.laserSword(this.vertices[this.swordVertex - 1 < 0 ? (sides - 1) : this.swordVertex - 1], angle2 * (Math.PI / 180))
this.swordRadius += this.swordRadiusGrowRate
if (this.swordRadius > this.swordRadiusMax) {
this.sword = this.swordSlash
this.spinCount = 0
}
ctx.beginPath();
let vertices = this.vertices;
ctx.moveTo(vertices[0].x, vertices[0].y);
for (let j = 1; j < vertices.length; j++) ctx.lineTo(vertices[j].x, vertices[j].y);
ctx.lineTo(vertices[0].x, vertices[0].y);
ctx.lineWidth = 13 + 5 * Math.random();
ctx.strokeStyle = `rgba(255,255,255,${0.5 + 0.2 * Math.random()})`;
ctx.stroke();
}
me.swordSlash = function () {
const angle = this.angle + this.laserAngle;
const end = {
x: this.vertices[this.swordVertex].x + this.swordRadiusMax * Math.cos(angle),
y: this.vertices[this.swordVertex].y + this.swordRadiusMax * Math.sin(angle)
};
const dx = end.x - this.vertices[this.swordVertex + 1 > (sides - 1) ? 0 : this.swordVertex + 1].x;
const dy = end.y - this.vertices[this.swordVertex + 1 > (sides - 1) ? 0 : this.swordVertex + 1].y;
const angle1 = Math.atan2(dy, dx) * (180 / Math.PI);
const dx1 = end.x - this.vertices[this.swordVertex - 1 < 0 ? (sides - 1) : this.swordVertex - 1].x;
const dy1 = end.y - this.vertices[this.swordVertex - 1 < 0 ? (sides - 1) : this.swordVertex - 1].y;
const angle2 = Math.atan2(dy1, dx1) * (180 / Math.PI);
this.laserSword(this.vertices[this.swordVertex], this.angle + this.laserAngle);
this.laserSword(this.vertices[this.swordVertex + 1 > (sides - 1) ? 0 : this.swordVertex + 1], angle1 * (Math.PI / 180))
this.laserSword(this.vertices[this.swordVertex - 1 < 0 ? (sides - 1) : this.swordVertex - 1], angle2 * (Math.PI / 180))
this.torque += this.torqueMagnitude;
this.spinCount++
if (this.spinCount > 80) {
this.sword = this.swordWaiting
this.swordRadius = 0
this.accelMag = 0.0004 + 0.00015 * simulation.accelScale;
this.cd = simulation.cycle + this.delay;
this.damageReduction = this.startingDamageReduction
this.isInvulnerable = false
this.frictionAir = 0.01
}
ctx.beginPath();
let vertices = this.vertices;
ctx.moveTo(vertices[0].x, vertices[0].y);
for (let j = 1; j < vertices.length; j++) ctx.lineTo(vertices[j].x, vertices[j].y);
ctx.lineTo(vertices[0].x, vertices[0].y);
ctx.lineWidth = 13 + 5 * Math.random();
ctx.strokeStyle = `rgba(255,255,255,${0.5 + 0.2 * Math.random()})`;
ctx.stroke();
}
me.laserSword = function (where, angle) {
const vertexCollision = function (v1, v1End, domain) {
for (let i = 0; i < domain.length; ++i) {
let v = domain[i].vertices;
const len = v.length - 1;
for (let j = 0; j < len; j++) {
results = simulation.checkLineIntersection(v1, v1End, v[j], v[j + 1]);
if (results.onLine1 && results.onLine2) {
const dx = v1.x - results.x;
const dy = v1.y - results.y;
const dist2 = dx * dx + dy * dy;
if (dist2 < best.dist2 && (!domain[i].mob || domain[i].alive)) best = { x: results.x, y: results.y, dist2: dist2, who: domain[i], v1: v[j], v2: v[j + 1] };
}
}
results = simulation.checkLineIntersection(v1, v1End, v[0], v[len]);
if (results.onLine1 && results.onLine2) {
const dx = v1.x - results.x;
const dy = v1.y - results.y;
const dist2 = dx * dx + dy * dy;
if (dist2 < best.dist2) best = { x: results.x, y: results.y, dist2: dist2, who: domain[i], v1: v[0], v2: v[len] };
}
}
};
best = { x: null, y: null, dist2: Infinity, who: null, v1: null, v2: null };
const look = { x: where.x + this.swordRadius * Math.cos(angle), y: where.y + this.swordRadius * Math.sin(angle) };
vertexCollision(where, look, body); // vertexCollision(where, look, mob);
vertexCollision(where, look, map);
if (!m.isCloak) vertexCollision(where, look, [playerBody, playerHead]);
if (best.who && (best.who === playerBody || best.who === playerHead) && m.immuneCycle < m.cycle) {
m.immuneCycle = m.cycle + m.collisionImmuneCycles + 60; //player is immune to damage for an extra second
m.damage(this.swordDamage);
simulation.drawList.push({ //add dmg to draw queue
x: best.x,
y: best.y,
radius: this.swordDamage * 1500,
color: "rgba(0,0,0,0.5)",
time: 20
});
}
if (best.dist2 === Infinity) best = look;
ctx.beginPath(); //draw beam
ctx.moveTo(where.x, where.y);
ctx.lineTo(best.x, best.y);
ctx.strokeStyle = "rgba(0,0,0,0.1)"; // Black path
ctx.lineWidth = 25;
ctx.stroke();
ctx.strokeStyle = "rgba(0,0,0,0.5)"; // Black path
ctx.lineWidth = 5;
ctx.setLineDash([70 + 300 * Math.random(), 55 * Math.random()]);
ctx.stroke(); // Draw it
ctx.setLineDash([]);
}
me.harmField = function (x, y) {
ctx.setLineDash([125 * Math.random(), 125 * Math.random()]);
// ctx.lineDashOffset = 6*(simulation.cycle % 215);
if (this.distanceToPlayer3(x, y) < this.laserRange) {
if (m.immuneCycle < m.cycle) {
m.damage(0.0003 * simulation.dmgScale);
if (m.energy > 0.1) m.energy -= 0.003
}
ctx.beginPath();
ctx.moveTo(x, y);
ctx.lineTo(m.pos.x, m.pos.y);
ctx.lineTo(m.pos.x + (Math.random() - 0.5) * 3000, m.pos.y + (Math.random() - 0.5) * 3000);
ctx.lineWidth = 2;
ctx.strokeStyle = "rgb(0,0,0)";
ctx.stroke();
ctx.beginPath();
ctx.arc(m.pos.x, m.pos.y, 40, 0, 2 * Math.PI);
ctx.fillStyle = "rgba(0,0,0,0.15)";
ctx.fill();
}
ctx.beginPath();
ctx.arc(x, y, this.laserRange * 0.9, 0, 2 * Math.PI);
ctx.strokeStyle = "rgba(0,0,0,0.5)";
ctx.lineWidth = 1;
ctx.stroke();
ctx.setLineDash([]);
ctx.fillStyle = "rgba(0,0,0,0.03)";
ctx.fill();
}
me.distanceToPlayer3 = function (x, y) {
const dx = x - player.position.x;
const dy = y - player.position.y;
return Math.sqrt(dx * dx + dy * dy);
}
radius = 22 // radius of each node mob
const sideLength = 100 // distance between each node mob
const nodes = 6
const angle = 2 * Math.PI / nodes
spawn.allowShields = false; //don't want shields on individual mobs
for (let i = 0; i < nodes; ++i) {
ace.stabber(x + sideLength * Math.sin(i * angle), y + sideLength * Math.cos(i * angle), radius, 12);
Matter.Body.setDensity(mob[mob.length - 1], 0.003); //extra dense //normal is 0.001 //makes effective life much larger
mob[mob.length - 1].damageReduction = 0.12
mob[mob.length - 1].showHealthBar = false;
mob[mob.length - 1].isBoss = true;
targets.push(mob[mob.length - 1].id) //track who is in the node boss, for shields
}
const attachmentStiffness = 0.02
spawn.constrain2AdjacentMobs(nodes, attachmentStiffness, true); //loop mobs together
for (let i = 0; i < nodes; ++i) { //attach to center mob
consBB[consBB.length] = Constraint.create({
bodyA: me,
bodyB: mob[mob.length - i - 1],
stiffness: attachmentStiffness,
damping: 0.03
});
Composite.add(engine.world, consBB[consBB.length - 1]);
}
//spawn shield around all nodes
ace.groupShield(targets, x, y, sideLength + 1 * radius + nodes * 5 - 25);
spawn.allowShields = true;
},
}
level.setPosToSpawn(-625, -100); //normal spawn
level.exit.x = -23650;
level.exit.y = 11100;
simulation.fallHeight = 20000;
const door = level.door(350, -200, 25, 225, 225, 10)
const door2 = level.door(6325, -200, 25, 225, 225, 10);
// spawn.mapRect(level.enter.x, level.enter.y + 20, 100, 20); //bump for level entrance
spawn.mapRect(level.exit.x, level.exit.y + 20, 100, 20); //bump for level exit
level.defaultZoom = 1800
simulation.zoomTransition(level.defaultZoom)
document.body.style.backgroundColor = "#d8dadf";
ace.stabber(425, -100);
ace.stabber(725, -100);
ace.stabber(1000, -100);
ace.stabber(1300, -100);
ace.stabber(1550, -100);
ace.stabber(1850, -100);
ace.stabber(2125, -100);
ace.stabber(2400, -100);
ace.stabber(2675, -100);
ace.stabber(2975, -100);
ace.stabber(3225, -100);
ace.stabber(3525, -100);
ace.stabber(3800, -100);
ace.stabber(4100, -100);
ace.stabber(4375, -100);
ace.stabber(4650, -100);
ace.stabber(4925, -100);
ace.stabber(5200, -100);
ace.stabber(5500, -100);
ace.stabber(5775, -100);
spawn.mapRect(-200, -450, 2825, 75);
spawn.mapRect(-200, 0, 2825, 75);
spawn.mapRect(-300, -400, 150, 50);
spawn.mapRect(-575, -375, 325, 50);
spawn.mapRect(-1175, 0, 700, 75);
spawn.mapRect(-1100, 50, 675, 75);
spawn.mapRect(-1100, -50, 225, 75);
spawn.mapRect(-1025, -75, 200, 50);
spawn.mapRect(-875, -50, 150, 75);
spawn.mapRect(-700, -350, 325, 50);
spawn.mapRect(-950, -100, 150, 50);
spawn.mapRect(-675, -50, 125, 25);
spawn.mapRect(-575, -150, 25, 125);
spawn.mapRect(-650, -50, 25, 75);
spawn.mapRect(-600, -50, 25, 75);
spawn.mapRect(-800, -325, 250, 50);
spawn.mapRect(-1450, 50, 500, 75);
spawn.mapRect(-1550, 100, 475, 100);
spawn.mapRect(-1650, 175, 525, 375);
spawn.mapRect(-1700, 275, 200, 175);
spawn.mapRect(-1550, 525, 475, 100);
spawn.mapRect(-1475, 600, 4125, 100);
spawn.mapRect(-50, 50, 75, 75);
level.chain(-450, 75, 0, false, 13)
level.chain(7475, 600, -0.5498531827, false, 200)
spawn.mapRect(325, -425, 75, 275);
spawn.mapRect(-850, -300, 100, 50);
spawn.mapRect(-900, -125, 75, 50);
spawn.mapRect(-875, -275, 50, 50);
spawn.mapRect(425, -400, 125, 100);
spawn.mapRect(600, -400, 125, 100);
spawn.mapRect(775, -400, 125, 100);
spawn.mapRect(950, -400, 125, 100);
spawn.mapRect(375, -350, 2250, 25);
spawn.mapRect(1125, -400, 125, 100);
spawn.mapRect(1300, -400, 125, 100);
spawn.mapRect(1475, -400, 125, 100);
spawn.mapRect(1650, -400, 125, 100);
spawn.mapRect(1825, -400, 125, 100);
spawn.mapRect(2000, -400, 125, 100);
spawn.mapRect(2175, -400, 125, 100);
spawn.mapRect(2350, -400, 125, 100);
spawn.mapRect(2525, -400, 125, 100);
spawn.mapRect(-1350, 650, 4000, 150);
spawn.mapRect(-1400, 675, 125, 75);
spawn.mapRect(-1325, 25, 200, 50);
spawn.mapRect(2600, 600, 4350, 200);
spawn.mapRect(2550, 0, 4400, 75);
spawn.mapRect(6875, 0, 875, 75);
spawn.mapRect(2500, -450, 5300, 50);
spawn.mapRect(7575, -425, 575, 475);
spawn.mapRect(6825, 600, 650, 125);
spawn.mapRect(6875, 675, 475, 100);
spawn.mapRect(7050, -525, 1175, 175);
spawn.mapRect(7650, -25, 575, 150);
spawn.mapRect(6075, -500, 1125, 75);
spawn.mapRect(2550, -350, 3450, 25);
spawn.mapRect(2700, -400, 125, 100);
spawn.mapRect(2875, -400, 125, 100);
spawn.mapRect(3050, -400, 125, 100);
spawn.mapRect(3225, -400, 125, 100);
spawn.mapRect(3400, -400, 125, 100);
spawn.mapRect(3575, -400, 125, 100);
spawn.mapRect(3750, -400, 125, 100);
spawn.mapRect(3925, -400, 125, 100);
spawn.mapRect(4100, -400, 125, 100);
spawn.mapRect(4275, -400, 125, 100);
spawn.mapRect(4450, -400, 125, 100);
spawn.mapRect(4625, -400, 125, 100);
spawn.mapRect(4800, -400, 125, 100);
spawn.mapRect(4975, -400, 125, 100);
spawn.mapRect(5150, -400, 125, 100);
spawn.mapRect(5325, -400, 125, 100);
spawn.mapRect(5500, -400, 125, 100);
spawn.mapRect(5675, -400, 125, 100);
spawn.mapRect(5850, -400, 125, 100);
spawn.mapRect(6000, -400, 125, 100);
spawn.mapRect(6100, -425, 125, 100);
spawn.mapRect(6150, -450, 200, 75);
spawn.mapRect(2575, -400, 3575, 25);
spawn.mapRect(6300, -425, 75, 250);
spawn.mapRect(-200, -175, 50, 50);
spawn.bodyRect(-950, 475, 150, 125);
spawn.bodyRect(-650, 475, 150, 125);
spawn.bodyRect(-1000, 350, 550, 125);
spawn.bodyRect(-650, 150, 225, 200);
spawn.bodyRect(-1050, 150, 400, 200);
spawn.bodyRect(-1125, 225, 25, 275);
spawn.bodyRect(-1100, 350, 125, 200);
spawn.bodyRect(-800, 475, 150, 125);
spawn.bodyRect(-500, 475, 125, 125);
spawn.mapRect(-3325, -50, 600, 75);
spawn.mapRect(-3250, 0, 450, 75);
spawn.mapRect(-2950, -100, 350, 75);
spawn.mapRect(-2975, -150, 325, 75);
spawn.mapRect(-3150, -250, 125, 25);
spawn.mapRect(-3050, -225, 100, 25);
spawn.mapRect(-3000, -200, 125, 25);
spawn.mapRect(-3425, -75, 325, 50);
spawn.mapRect(-3250, -225, 125, 25);
spawn.mapRect(-3325, -200, 100, 25);
spawn.mapRect(-3100, -300, 25, 50);
spawn.mapRect(-3725, -325, 1300, 25);
spawn.mapRect(-2925, -175, 125, 25);
spawn.mapRect(-3375, -175, 100, 25);
spawn.mapRect(-3550, -150, 250, 75);
spawn.mapRect(-3725, -150, 250, 50);
spawn.mapRect(-3725, -200, 125, 75);
spawn.mapRect(-3625, -175, 50, 25);
spawn.mapRect(-3750, -125, 50, 25);
spawn.mapRect(-3750, -50, 125, 50);
spawn.mapRect(-3650, -125, 75, 100);
spawn.mapRect(-2750, 0, 100, 75);
spawn.mapRect(-2675, 25, 175, 25);
spawn.mapRect(-2850, 0, 150, 50);
spawn.mapRect(-3150, 50, 25, 75);
spawn.mapRect(-2900, 50, 25, 75);
spawn.mapRect(-3300, 100, 575, 25);
spawn.mapRect(-24300, 11125, 51525, 6250);
spawn.mapVertex(7900, -675, "0 0 500 -200 500 300 -450 300")
spawn.mapRect(-24300, 9575, 475, 1750);
spawn.mapRect(26800, 9575, 425, 1750);
spawn.hopper(-3100, -150);
spawn.mapRect(11625, 8375, 11200, 225);
spawn.mapRect(22600, 8375, 225, 3225);
spawn.mapRect(11625, 8375, 225, 1800);
spawn.mapRect(12425, 9825, 225, 875);
spawn.mapRect(13150, 10725, 225, 575);
spawn.mapRect(14125, 10450, 5025, 200);
spawn.mapRect(21775, 10625, 1050, 225);
spawn.mapRect(20325, 10925, 1300, 200);
spawn.mapRect(20825, 10250, 750, 225);
spawn.mapRect(19500, 10000, 1000, 225);
ace.slasher2(-22725, 10325);
ace.slasher3(-23425, 10250);
ace.slasher2(-23350, 10700);
ace.slasher3(-21725, 11075);
ace.slasher2(-21525, 10025);
ace.slasher3(-20950, 9750);
ace.slasher2(-19975, 9700);
ace.slasher3(-18850, 9650);
ace.slasher2(-18675, 9700);
ace.slasher3(-18250, 9125);
ace.slasher2(-17775, 8925);
ace.slasher3(-16975, 8875);
ace.slasher2(-16475, 9125);
ace.slasher3(-16125, 9275);
ace.slasher2(-15650, 9225);
ace.slasher3(-15200, 9175);
ace.slasher2(-16800, 9325);
ace.slasher3(-17450, 9525);
ace.slasher2(-18375, 9625);
ace.slasher3(-19650, 9375);
ace.slasher2(-20600, 9225);
ace.slasher3(-21625, 9400);
ace.slasher2(-22450, 9775);
ace.slasher3(-22900, 10000);
ace.slasher2(-23275, 9300);
ace.slasher3(-23125, 9150);
ace.slasher2(2800, 9350);
ace.slasher3(4925, 9825);
ace.slasher2(3725, 10525);
ace.slasher3(1850, 10450);
ace.slash(16850, 10075);
spawn.mapVertex(-14325, 11000, "0 0 4000 -2000 10000 -3000 16000 -2000 20000 0");
let q = Matter.Bodies.rectangle(7525 + (1100 / 2), 10675 + (150 / 2), 1100, 150, {
density: 0.05,
isNotHoldable: false,
restitution: 1.05,
isStatic: false
}, true, [true], 0);
let qq = Matter.Bodies.rectangle(7375 + (150 / 2), 10550 + (200 / 2), 150, 200, {
density: 0.05,
isNotHoldable: true,
restitution: 1.05,
isStatic: false
}, true, [true], 0);
let qqq = Matter.Bodies.rectangle(7450 + (1250 / 2), 10500 + (100 / 2), 1250, 100, {
density: 0.05,
isNotHoldable: true,
restitution: 1.05,
isStatic: false
}, true, [true], 0);
let qqqq = Matter.Bodies.rectangle(8625 + (150 / 2), 10550 + (200 / 2), 150, 200, {
density: 0.05,
isNotHoldable: true,
restitution: 1.05,
isStatic: false
}, true, [true], 0);
let qqqqq = Matter.Bodies.rectangle(7600 + (100 / 2), 10350 + (200 / 2), 100, 200, {
density: 0.05,
isNotHoldable: true,
restitution: 1.05,
isStatic: false
}, true, [true], 0);
let qqqqqq = Matter.Bodies.rectangle(8475 + (100 / 2), 10350 + (200 / 2), 100, 200, {
density: 0.05,
isNotHoldable: true,
restitution: 1.05,
isStatic: false
}, true, [true], 0);
let qqqqqqq = Matter.Bodies.rectangle(7650 + (725 / 2), 10325 + (75 / 2), 725, 75, {
density: 0.05,
isNotHoldable: true,
restitution: 1.05,
isStatic: false
}, true, [true], 0);
let qqqqqqqq = Matter.Bodies.rectangle(8000 + 100, 10200 + (150 / 2), 200, 150, {
density: 0.05,
isNotHoldable: true,
restitution: 1.05,
isStatic: false
}, true, [true], 0);
let qqqqqqqqq = Matter.Bodies.rectangle(6975 + (1125 / 2), 10250 + 25, 1125, 50, {
density: 0.05,
isNotHoldable: true,
restitution: 1.05,
isStatic: false
}, true, [true], 0);
let qqqqqqqqqq = Matter.Bodies.rectangle(7600 + 50, 10575 + (125 / 2), 100, 125, {
density: 0.05,
isNotHoldable: true,
restitution: 1.05,
isStatic: false
}, true, [true], 0);
let qqqqqqqqqqq = Matter.Bodies.rectangle(8475 + 50, 10575 + (125 / 2), 100, 125, {
density: 0.05,
isNotHoldable: true,
restitution: 1.05,
isStatic: false
}, true, [true], 0);
let qqqqqqqqqqqq = Matter.Bodies.rectangle(8025 + 50, 10575 + (125 / 2), 100, 125, {
density: 0.05,
isNotHoldable: true,
restitution: 1.05,
isStatic: false
}, true, [true], 0);
wasd = Matter.Body.create({
parts: [q, qq, qqq, qqqq, qqqqq, qqqqqq, qqqqqqq, qqqqqqqq, qqqqqqqqq, qqqqqqqqqq, qqqqqqqqqqq, qqqqqqqqqqqq]
});
body[body.length] = q;
body[body.length] = qq;
body[body.length] = qqq;
body[body.length] = qqqq;
body[body.length] = qqqqq;
body[body.length] = qqqqqq;
body[body.length] = qqqqqqq;
body[body.length] = qqqqqqqq;
body[body.length] = qqqqqqqqq;
body[body.length] = qqqqqqqqqq;
body[body.length] = qqqqqqqqqqq;
body[body.length] = qqqqqqqqqqqq;
// body[body.length] = wasd;
Matter.Composite.add(engine.world, wasd)
composite[composite.length] = wasd;
// wasd.friction -= 0.5
setTimeout(function () {
wasd.collisionFilter.category = cat.map;
wasd.collisionFilter.mask = cat.body | cat.player | cat.bullet | cat.mobBullet | cat.mob | cat.map
}, 100);
let Vx = 0;
var gradient = ctx.createLinearGradient(0, 0, 10975 / 2, 0);
gradient.addColorStop(0, "#00000000");
gradient.addColorStop(1, "#686868");
level.custom = () => {
wasd.force.y += simulation.g * wasd.mass;
if (Matter.Query.collides(wasd, [player]).length > 0 && !(m.isCloak && tech.isIntangible) && input.down && isDestroyed) {
wasd.force.x += Math.cos(m.angle) * 75;
Matter.Body.setVelocity(player, wasd.velocity)
m.Vx = player.velocity.x - wasd.velocity.x;
}
for (let i = 0; i < mob.length; i++) {
if (Matter.Query.collides(wasd, [mob[i]]).length > 0 && !mob[i].isBoss && isDestroyed) {
const dmg = 1;
mob[i].damage(dmg, true);
simulation.drawList.push({ //add dmg to draw queue
x: mob[i].position.x,
y: mob[i].position.y,
radius: Math.sqrt(dmg) * 50,
color: simulation.mobDmgColor,
time: simulation.drawTime
});
break
}
}
Vx = wasd.velocity.x / 5;
level.exit.drawAndCheck();
drawSeats(475, -50, 5600, 125, 20, "darkgray");
door.openClose()
door2.openClose()
if (player.position.y < 25) {
door.isClosing = false;
door2.isClosing = false;
} else {
door.isClosing = true;
door2.isClosing = true;
}
ctx.fillStyle = "red";
ctx.fillRect(-825, -75, 50, 50);
b.pulse(30, 0, { x: -2500, y: (25 + (25 / 2)) });
ctx.save()
ctx.translate(11750, 8475)
ctx.fillStyle = gradient;
ctx.fillRect(0, 0, 10975, 2800);
ctx.restore()
drawHead(7400, 0, Math.PI * 0.1);
drawHead(7460, 0, Math.PI * 0.5);
drawHead(7520, 0, Math.PI * 0.3);
drawHead(22400, 11125, Math.PI * 0.3);
drawHead(21925, 10625, Math.PI * 0.5);
drawHead(21175, 10250, Math.PI * 0.1);
drawHead(22525, 10625, Math.PI * 0.7);
drawHead(22525, 11125, Math.PI * 0.9);
drawHead(22225, 11125, Math.PI * 1.5);
};
level.customTopLayer = () => {
drawSeats(500, -50, 5600, 125);
ctx.strokeStyle = 'red';
ctx.lineWidth = 20;
ctx.beginPath();
ctx.setLineDash([40, 40]);
ctx.lineDashOffset = (-simulation.cycle * Vx) % 80;
ctx.moveTo(q.vertices[0].x, q.vertices[0].y);
for (let i = 1; i < q.vertices.length; i++) {
ctx.lineTo(q.vertices[i].x, q.vertices[i].y);
}
ctx.lineTo(q.vertices[0].x, q.vertices[0].y);
ctx.closePath();
ctx.stroke();
ctx.setLineDash([0, 0]);
};
function drawSeats(x, y, w, h, num = 20, c = "gray") {
const seatWidth = w / num;
const seatHeight = h / num;
for (let i = 0; i < num; i++) {
const seatX = x + i * seatWidth;
const seatY = y;
// Draw the seat parts
ctx.fillStyle = c;
ctx.fillRect(seatX - 100, seatY, 125, 25);
ctx.fillRect(seatX, seatY - 125, 25, 150);
ctx.fillRect(seatX - 75, seatY, 25, 75);
ctx.fillRect(seatX - 25, seatY, 25, 75);
}
}
function drawHead(x, y, angle) {
ctx.save();
ctx.translate(x, y - 30);
ctx.rotate(angle);
ctx.beginPath();
ctx.arc(0, 0, 30, 0, 2 * Math.PI);
ctx.fillStyle = m.bodyGradient
ctx.fill();
ctx.arc(15, 0, 4, 0, 2 * Math.PI);
ctx.strokeStyle = "#333";
ctx.lineWidth = 2;
ctx.stroke();
ctx.restore();
}
for (let i = 0, len = mob.length; i < len; i++) {
if (mob[i].isSlashBoss) {
simulation.ephemera.push({
name: "bossBar",
do() {
if (level.levels[level.onLevel] == "ace" && !isDestroyed) {
ctx.save();
ctx.setTransform(1, 0, -0.5, 1, 0, 0); //slanted
ctx.fillStyle = "rgba(100, 100, 100, 0.3)";
ctx.fillRect(canvas.width2 / 2, canvas.height2 / 10, canvas.width2, 30);
ctx.fillStyle = "rgba(0,0,0,0.7)";
ctx.fillRect(canvas.width2 / 2, canvas.height2 / 10, canvas.width2 * mob[i].health, 30);
ctx.restore();
}
},
})
}
}
},
crimsonTowers() {
simulation.makeTextLog(`crimsonTowers by Richard0820. Thank you desboot for the video: <a href="https://www.youtube.com/watch?v=hkdY0mDF2SY&feature=youtu.be&ab_channel=DesBoot">Source</a>`)
const ace = {
spawnOrbitals(who, radius, chance = Math.min(0.25 + simulation.difficulty * 0.005)) {
if (Math.random() < chance) {
// simulation.difficulty = 50
const len = Math.floor(Math.min(15, 3 + Math.sqrt(simulation.difficulty))) // simulation.difficulty = 40 on hard mode level 10
const speed = (0.003 + 0.004 * Math.random() + 0.002 * Math.sqrt(simulation.difficulty)) * ((Math.random() < 0.5) ? 1 : -1)
const offSet = 6.28 * Math.random()
for (let i = 0; i < len; i++) ace.orbital(who, radius, i / len * 2 * Math.PI + offSet, speed)
}
},
orbital(who, radius, phase, speed) {
// for (let i = 0, len = 7; i < len; i++) spawn.orbital(me, radius + 250, 2 * Math.PI / len * i)
mobs.spawn(who.position.x, who.position.y, 8, 12, "rgb(0,0,0)");
let me = mob[mob.length - 1];
me.stroke = "transparent";
Matter.Body.setDensity(me, 0.01); //normal is 0.001
me.leaveBody = false;
me.isDropPowerUp = false;
me.isBadTarget = true;
me.isUnstable = true; //dies when blocked
me.showHealthBar = false;
me.isOrbital = true;
// me.isShielded = true
me.collisionFilter.category = cat.mobBullet;
me.collisionFilter.mask = cat.bullet; //cat.player | cat.map | cat.body
me.do = function () {
//if host is gone
if (!who || !who.alive) {
this.death();
return
}
//set orbit
const time = simulation.cycle * speed + phase
const orbit = {
x: Math.cos(time),
y: Math.sin(time)
}
Matter.Body.setPosition(this, Vector.add(Vector.add(who.position, who.velocity), Vector.mult(orbit, radius)))
//damage player
if (Matter.Query.collides(this, [player]).length > 0 && !(m.isCloak && tech.isIntangible) && m.immuneCycle < m.cycle) {
m.immuneCycle = m.cycle + m.collisionImmuneCycles; //player is immune to damage for 30 cycles
const dmg = 0.03 * simulation.dmgScale
m.damage(dmg);
simulation.drawList.push({ //add dmg to draw queue
x: this.position.x,
y: this.position.y,
radius: Math.sqrt(dmg) * 200,
color: simulation.mobDmgColor,
time: simulation.drawTime
});
this.death();
}
};
},
shield(target, x, y, chance = Math.min(0.02 + simulation.difficulty * 0.005, 0.2) + tech.duplicationChance(), isExtraShield = false) {
if (this.allowShields && Math.random() < chance) {
mobs.spawn(x, y, 9, target.radius + 30, "rgba(255,255,255,0.9)");
let me = mob[mob.length - 1];
me.stroke = "rgb(0,0,0)";
Matter.Body.setDensity(me, 0.00001) //very low density to not mess with the original mob's motion
me.shield = true;
me.damageReduction = 0.05 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1)
me.isUnblockable = true
me.isExtraShield = isExtraShield //this prevents spamming with tech.isShieldAmmo
me.collisionFilter.category = cat.mobShield
me.collisionFilter.mask = cat.bullet;
consBB[consBB.length] = Constraint.create({
bodyA: me,
bodyB: target, //attach shield to target
stiffness: 0.4,
damping: 0.1
});
Composite.add(engine.world, consBB[consBB.length - 1]);
me.onDamage = function () {
//make sure the mob that owns the shield can tell when damage is done
this.alertNearByMobs();
this.fill = `rgba(255,255,255,${0.3 + 0.6 * this.health})`
};
me.leaveBody = false;
me.isDropPowerUp = false;
me.showHealthBar = false;
me.shieldTargetID = target.id
target.isShielded = true;
target.shieldID = me.id
me.onDeath = function () {
//clear isShielded status from target
for (let i = 0, len = mob.length; i < len; i++) {
if (mob[i].id === this.shieldTargetID) mob[i].isShielded = false;
}
};
me.do = function () {
this.checkStatus();
};
mob.unshift(me); //move shield to the front of the array, so that mob is behind shield graphically
//swap order of shield and mob, so that mob is behind shield graphically
// mob[mob.length - 1] = mob[mob.length - 2];
// mob[mob.length - 2] = me;
}
},
groupShield(targets, x, y, radius, stiffness = 0.4) {
const nodes = targets.length
mobs.spawn(x, y, 9, radius, "rgba(255,255,255,0.9)");
let me = mob[mob.length - 1];
me.stroke = "rgb(0,0,0)";
Matter.Body.setDensity(me, 0.00001) //very low density to not mess with the original mob's motion
me.frictionAir = 0;
me.shield = true;
me.damageReduction = 0.075 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1)
me.collisionFilter.category = cat.mobShield
me.collisionFilter.mask = cat.bullet;
for (let i = 0; i < nodes; ++i) {
mob[mob.length - i - 2].isShielded = true;
//constrain to all mob nodes in group
consBB[consBB.length] = Constraint.create({
bodyA: me,
bodyB: mob[mob.length - i - 2],
stiffness: stiffness,
damping: 0.1
});
Composite.add(engine.world, consBB[consBB.length - 1]);
}
me.onDamage = function () {
this.alertNearByMobs(); //makes sure the mob that owns the shield can tell when damage is done
this.fill = `rgba(255,255,255,${0.3 + 0.6 * this.health})`
};
me.onDeath = function () {
//clear isShielded status from target
for (let j = 0; j < targets.length; j++) {
for (let i = 0, len = mob.length; i < len; i++) {
if (mob[i].id === targets[j]) mob[i].isShielded = false;
}
}
};
me.leaveBody = false;
me.isDropPowerUp = false;
me.showHealthBar = false;
mob[mob.length - 1] = mob[mob.length - 1 - nodes];
mob[mob.length - 1 - nodes] = me;
me.do = function () {
this.checkStatus();
};
},
slasher2(x, y, radius = 33 + Math.ceil(Math.random() * 30)) {
mobs.spawn(x, y, 6, radius, "rgb(0,0,0)");
let me = mob[mob.length - 1];
Matter.Body.rotate(me, 2 * Math.PI * Math.random());
me.accelMag = 0.0009 * simulation.accelScale;
me.torqueMagnitude = 0.000012 * me.inertia //* (Math.random() > 0.5 ? -1 : 1);
me.frictionStatic = 0;
me.friction = 0;
me.frictionAir = 0.035;
me.delay = 140 * simulation.CDScale;
me.cd = 0;
me.swordRadius = 0;
me.swordVertex = 1
me.swordRadiusMax = 275 + 3.5 * simulation.difficulty;
me.swordRadiusGrowRate = me.swordRadiusMax * (0.011 + 0.0002 * simulation.difficulty)
me.isSlashing = false;
me.swordDamage = 0.03 * simulation.dmgScale
me.laserAngle = 3 * Math.PI / 5
const seeDistance2 = 200000
ace.shield(me, x, y);
me.onDamage = function () { };
me.do = function () {
this.checkStatus();
this.seePlayerByHistory(15);
this.attraction();
this.sword() //does various things depending on what stage of the sword swing
};
me.swordWaiting = function () {
if (
this.seePlayer.recall &&
this.cd < simulation.cycle &&
this.distanceToPlayer2() < seeDistance2 &&
Matter.Query.ray(map, this.position, this.playerPosRandomY()).length === 0 &&
Matter.Query.ray(body, this.position, this.playerPosRandomY()).length === 0
) {
this.laserAngle = -Math.PI / 6
this.sword = this.swordGrow
this.accelMag = 0
}
}
me.sword = me.swordWaiting //base function that changes during different aspects of the sword swing
me.swordGrow = function () {
this.laserSword(this.vertices[0], this.angle + this.laserAngle);
this.laserSword(this.vertices[1], this.angle + this.laserAngle + (Math.PI / 3));
this.laserSword(this.vertices[2], this.angle + this.laserAngle + (Math.PI * 2 / 3));
this.laserSword(this.vertices[3], this.angle + this.laserAngle + Math.PI);
this.laserSword(this.vertices[4], this.angle + this.laserAngle + (Math.PI * 4 / 3));
this.laserSword(this.vertices[5], this.angle + this.laserAngle + (Math.PI * 5 / 3));
this.swordRadius += this.swordRadiusGrowRate
if (this.swordRadius > this.swordRadiusMax || this.isStunned) {
this.sword = this.swordSlash
this.spinCount = 0
}
}
me.swordSlash = function () {
this.laserSword(this.vertices[0], this.angle + this.laserAngle);
this.laserSword(this.vertices[1], this.angle + this.laserAngle + (Math.PI / 3));
this.laserSword(this.vertices[2], this.angle + this.laserAngle + (Math.PI * 2 / 3));
this.laserSword(this.vertices[3], this.angle + this.laserAngle + Math.PI);
this.laserSword(this.vertices[4], this.angle + this.laserAngle + (Math.PI * 4 / 3));
this.laserSword(this.vertices[5], this.angle + this.laserAngle + (Math.PI * 5 / 3));
this.torque += this.torqueMagnitude;
this.spinCount++
if (this.spinCount > 100 || this.isStunned) {
this.sword = this.swordWaiting
this.swordRadius = 0
this.accelMag = 0.001 * simulation.accelScale;
this.cd = simulation.cycle + this.delay;
}
}
me.laserSword = function (where, angle) {
const vertexCollision = function (v1, v1End, domain) {
for (let i = 0; i < domain.length; ++i) {
let v = domain[i].vertices;
const len = v.length - 1;
for (let j = 0; j < len; j++) {
results = simulation.checkLineIntersection(v1, v1End, v[j], v[j + 1]);
if (results.onLine1 && results.onLine2) {
const dx = v1.x - results.x;
const dy = v1.y - results.y;
const dist2 = dx * dx + dy * dy;
if (dist2 < best.dist2 && (!domain[i].mob || domain[i].alive)) best = { x: results.x, y: results.y, dist2: dist2, who: domain[i], v1: v[j], v2: v[j + 1] };
}
}
results = simulation.checkLineIntersection(v1, v1End, v[0], v[len]);
if (results.onLine1 && results.onLine2) {
const dx = v1.x - results.x;
const dy = v1.y - results.y;
const dist2 = dx * dx + dy * dy;
if (dist2 < best.dist2) best = { x: results.x, y: results.y, dist2: dist2, who: domain[i], v1: v[0], v2: v[len] };
}
}
};
best = { x: null, y: null, dist2: Infinity, who: null, v1: null, v2: null };
const look = { x: where.x + this.swordRadius * Math.cos(angle), y: where.y + this.swordRadius * Math.sin(angle) };
vertexCollision(where, look, body); // vertexCollision(where, look, mob);
vertexCollision(where, look, map);
if (!m.isCloak) vertexCollision(where, look, [playerBody, playerHead]);
if (best.who && (best.who === playerBody || best.who === playerHead) && m.immuneCycle < m.cycle) {
m.immuneCycle = m.cycle + m.collisionImmuneCycles + 60; //player is immune to damage for an extra second
m.damage(this.swordDamage);
simulation.drawList.push({ //add dmg to draw queue
x: best.x,
y: best.y,
radius: this.swordDamage * 1500,
color: "rgba(80,0,255,0.5)",
time: 20
});
}
if (best.dist2 === Infinity) best = look;
ctx.beginPath(); //draw beam
ctx.moveTo(where.x, where.y);
ctx.lineTo(best.x, best.y);
ctx.strokeStyle = "rgba(0,0,0,0.1)"; // 0 path
ctx.lineWidth = 15;
ctx.stroke();
ctx.strokeStyle = "rgba(0,0,0,0.5)"; // 0 path
ctx.lineWidth = 4;
ctx.setLineDash([70 + 300 * Math.random(), 55 * Math.random()]);
ctx.stroke(); // Draw it
ctx.setLineDash([]);
}
},
slasher3(x, y, radius = 33 + Math.ceil(Math.random() * 30)) {
const sides = 6
mobs.spawn(x, y, sides, radius, "rgb(0,0,0)");
let me = mob[mob.length - 1];
Matter.Body.rotate(me, 2 * Math.PI * Math.random());
me.accelMag = 0.0005 * simulation.accelScale;
me.frictionStatic = 0;
me.friction = 0;
me.frictionAir = 0.02;
me.delay = 150 * simulation.CDScale;
me.cd = 0;
me.cycle = 0;
me.swordVertex = 1
me.swordRadiusInitial = radius / 2;
me.swordRadius = me.swordRadiusInitial;
me.swordRadiusMax = 750 + 6 * simulation.difficulty;
me.swordRadiusGrowRateInitial = 1.08
me.swordRadiusGrowRate = me.swordRadiusGrowRateInitial//me.swordRadiusMax * (0.009 + 0.0002 * simulation.difficulty)
me.isSlashing = false;
me.swordDamage = 0.04 * simulation.dmgScale
me.laserAngle = 3 * Math.PI / 5
const seeDistance2 = me.swordRadiusMax * me.swordRadiusMax
ace.shield(me, x, y);
me.onDamage = function () { };
me.do = function () {
this.checkStatus();
this.seePlayerByHistory(15);
this.sword() //does various things depending on what stage of the sword swing
};
me.swordWaiting = function () {
this.attraction();
if (
this.seePlayer.recall &&
this.cd < simulation.cycle &&
this.distanceToPlayer2() < seeDistance2 &&
Matter.Query.ray(map, this.position, this.playerPosRandomY()).length === 0 &&
Matter.Query.ray(body, this.position, this.playerPosRandomY()).length === 0
) {
//find vertex closest to the player
let dist = Infinity
for (let i = 0, len = this.vertices.length; i < len; i++) {
const D = Vector.magnitudeSquared(Vector.sub({ x: this.vertices[i].x, y: this.vertices[i].y }, m.pos))
if (D < dist) {
dist = D
this.swordVertex = i
}
}
this.laserAngle = this.swordVertex / sides * 2 * Math.PI + Math.PI / sides
this.sword = this.swordGrow
this.cycle = 0
this.swordRadius = this.swordRadiusInitial
//slow velocity but don't stop
Matter.Body.setVelocity(this, Vector.mult(this.velocity, 0.5))
//set angular velocity to 50%
// Matter.Body.setAngularVelocity(this, this.angularVelocity * 0.5)
//gently rotate towards the player with a torque, use cross product to decided clockwise or counterclockwise
const laserStartVector = Vector.sub(this.position, this.vertices[this.swordVertex])
const playerVector = Vector.sub(this.position, m.pos)
const cross = Matter.Vector.cross(laserStartVector, playerVector)
this.torque = 0.00002 * this.inertia * (cross > 0 ? 1 : -1)
}
}
me.sword = me.swordWaiting //base function that changes during different aspects of the sword swing
me.swordGrow = function () {
const angle = this.angle + this.laserAngle;
const end = {
x: this.vertices[this.swordVertex].x + this.swordRadiusMax * Math.cos(angle),
y: this.vertices[this.swordVertex].y + this.swordRadiusMax * Math.sin(angle)
};
const dx = end.x - this.vertices[this.swordVertex + 1 > (sides - 1) ? 0 : this.swordVertex + 1].x;
const dy = end.y - this.vertices[this.swordVertex + 1 > (sides - 1) ? 0 : this.swordVertex + 1].y;
const angle1 = Math.atan2(dy, dx) * (180 / Math.PI);
const dx1 = end.x - this.vertices[this.swordVertex - 1 < 0 ? (sides - 1) : this.swordVertex - 1].x;
const dy1 = end.y - this.vertices[this.swordVertex - 1 < 0 ? (sides - 1) : this.swordVertex - 1].y;
const angle2 = Math.atan2(dy1, dx1) * (180 / Math.PI);
this.laserSpear(this.vertices[this.swordVertex], this.angle + this.laserAngle);
this.laserSpear(this.vertices[this.swordVertex + 1 > (sides - 1) ? 0 : this.swordVertex + 1], angle1 * (Math.PI / 180))
this.laserSpear(this.vertices[this.swordVertex - 1 < 0 ? (sides - 1) : this.swordVertex - 1], angle2 * (Math.PI / 180))
Matter.Body.setVelocity(this, Vector.mult(this.velocity, 0.9))
// this.swordRadius += this.swordRadiusGrowRate
this.cycle++
// this.swordRadius = this.swordRadiusMax * Math.sin(this.cycle * 0.03)
this.swordRadius *= this.swordRadiusGrowRate
if (this.swordRadius > this.swordRadiusMax) this.swordRadiusGrowRate = 1 / this.swordRadiusGrowRateInitial
// if (this.swordRadius > this.swordRadiusMax) this.swordRadiusGrowRate = -Math.abs(this.swordRadiusGrowRate)
if (this.swordRadius < this.swordRadiusInitial || this.isStunned) {
// this.swordRadiusGrowRate = Math.abs(this.swordRadiusGrowRate)
this.swordRadiusGrowRate = this.swordRadiusGrowRateInitial
this.sword = this.swordWaiting
this.swordRadius = 0
this.cd = simulation.cycle + this.delay;
}
}
me.laserSpear = function (where, angle) {
const vertexCollision = function (v1, v1End, domain) {
for (let i = 0; i < domain.length; ++i) {
let v = domain[i].vertices;
const len = v.length - 1;
for (let j = 0; j < len; j++) {
results = simulation.checkLineIntersection(v1, v1End, v[j], v[j + 1]);
if (results.onLine1 && results.onLine2) {
const dx = v1.x - results.x;
const dy = v1.y - results.y;
const dist2 = dx * dx + dy * dy;
if (dist2 < best.dist2 && (!domain[i].mob || domain[i].alive)) best = { x: results.x, y: results.y, dist2: dist2, who: domain[i], v1: v[j], v2: v[j + 1] };
}
}
results = simulation.checkLineIntersection(v1, v1End, v[0], v[len]);
if (results.onLine1 && results.onLine2) {
const dx = v1.x - results.x;
const dy = v1.y - results.y;
const dist2 = dx * dx + dy * dy;
if (dist2 < best.dist2) best = { x: results.x, y: results.y, dist2: dist2, who: domain[i], v1: v[0], v2: v[len] };
}
}
};
best = { x: null, y: null, dist2: Infinity, who: null, v1: null, v2: null };
const look = { x: where.x + this.swordRadius * Math.cos(angle), y: where.y + this.swordRadius * Math.sin(angle) };
vertexCollision(where, look, body); // vertexCollision(where, look, mob);
vertexCollision(where, look, map);
if (!m.isCloak) vertexCollision(where, look, [playerBody, playerHead]);
if (best.who && (best.who === playerBody || best.who === playerHead)) {
this.swordRadiusGrowRate = 1 / this.swordRadiusGrowRateInitial //!!!! this retracts the sword if it hits the player
if (m.immuneCycle < m.cycle) {
m.immuneCycle = m.cycle + m.collisionImmuneCycles + 60; //player is immune to damage for an extra second
m.damage(this.swordDamage);
simulation.drawList.push({ //add dmg to draw queue
x: best.x,
y: best.y,
radius: this.swordDamage * 1500,
color: "rgba(80,0,255,0.5)",
time: 20
});
}
}
if (best.dist2 === Infinity) best = look;
ctx.beginPath(); //draw beam
ctx.moveTo(where.x, where.y);
ctx.lineTo(best.x, best.y);
ctx.strokeStyle = "rgba(0,0,0,0.1)"; // 0 path
ctx.lineWidth = 15;
ctx.stroke();
ctx.strokeStyle = "rgba(0,0,0,0.5)"; // 0 path
ctx.lineWidth = 4;
ctx.setLineDash([70 + 300 * Math.random(), 55 * Math.random()]);
ctx.stroke(); // Draw it
ctx.setLineDash([]);
}
},
stabber(x, y, radius = 25 + Math.ceil(Math.random() * 12), spikeMax = 7) {
if (radius > 80) radius = 65;
mobs.spawn(x, y, 6, radius, "rgb(0,0,0)"); //can't have sides above 6 or collision events don't work (probably because of a convex problem)
let me = mob[mob.length - 1];
me.isVerticesChange = true
me.accelMag = 0.0006 * simulation.accelScale;
// me.g = 0.0002; //required if using this.gravity
me.isInvulnerable = false
me.delay = 360 * simulation.CDScale;
me.spikeVertex = 0;
me.spikeLength = 0;
me.isSpikeGrowing = false;
me.spikeGrowth = 0;
me.isSpikeReset = true;
me.collisionFilter.mask = cat.map | cat.body | cat.bullet | cat.player //can't touch other mobs
Matter.Body.rotate(me, Math.PI * 0.1);
ace.shield(me, x, y);
// me.onDamage = function () {};
// me.onHit = function() { //run this function on hitting player
// };
me.onDeath = function () {
if (this.spikeLength > 4) {
this.spikeLength = 4
const spike = Vector.mult(Vector.normalise(Vector.sub(this.vertices[this.spikeVertex], this.position)), this.radius * this.spikeLength)
this.vertices[this.spikeVertex].x = this.position.x + spike.x
this.vertices[this.spikeVertex].y = this.position.y + spike.y
// this.vertices = Matter.Vertices.hull(Matter.Vertices.clockwiseSort(this.vertices))
}
};
me.do = function () {
this.seePlayerByLookingAt();
this.checkStatus();
this.attraction();
if (this.isSpikeReset) {
if (this.seePlayer.recall) {
const dist = Vector.sub(this.seePlayer.position, this.position);
const distMag = Vector.magnitude(dist);
if (distMag < radius * spikeMax) {
//find nearest vertex
let nearestDistance = Infinity
for (let i = 0, len = this.vertices.length; i < len; i++) {
//find distance to player for each vertex
const dist = Vector.sub(this.seePlayer.position, this.vertices[i]);
const distMag = Vector.magnitude(dist);
//save the closest distance
if (distMag < nearestDistance) {
this.spikeVertex = i
nearestDistance = distMag
}
}
this.spikeLength = 1
this.isSpikeGrowing = true;
this.isSpikeReset = false;
Matter.Body.setAngularVelocity(this, 0)
}
me.isInvulnerable = false
}
} else {
if (this.isSpikeGrowing) {
this.spikeLength += Math.pow(this.spikeGrowth += 0.02, 8)
// if (this.spikeLength < 2) {
// this.spikeLength += 0.035
// } else {
// this.spikeLength += 1
// }
if (this.spikeLength > spikeMax) {
this.isSpikeGrowing = false;
this.spikeGrowth = 0
}
} else {
Matter.Body.setAngularVelocity(this, this.angularVelocity * 0.8) //reduce rotation
this.spikeLength -= 0.3
if (this.spikeLength < 1) {
this.spikeLength = 1
this.isSpikeReset = true
this.radius = radius
}
}
const spike = Vector.mult(Vector.normalise(Vector.sub(this.vertices[this.spikeVertex], this.position)), radius * this.spikeLength)
this.vertices[this.spikeVertex].x = this.position.x + spike.x
this.vertices[this.spikeVertex].y = this.position.y + spike.y
me.isInvulnerable = true
// this.radius = radius * this.spikeLength;
}
if (this.isInvulnerable) {
ctx.beginPath();
let vertices = this.vertices;
ctx.moveTo(vertices[0].x, vertices[0].y);
for (let j = 1; j < vertices.length; j++) ctx.lineTo(vertices[j].x, vertices[j].y);
ctx.lineTo(vertices[0].x, vertices[0].y);
ctx.lineWidth = 13 + 5 * Math.random();
ctx.strokeStyle = `rgba(255,255,255,${0.5 + 0.2 * Math.random()})`;
ctx.stroke();
me.damageReduction = 0;
} else {
me.damageReduction = 1;
}
};
},
slash(x, y, radius = 80) {
let targets = []
const sides = 6;
mobs.spawn(x, y, 6, radius, "#000000");
let me = mob[mob.length - 1];
Matter.Body.rotate(me, 2 * Math.PI * Math.random());
targets.push(me.id) //add to shield protection
const nodeBalance = Math.random()
const nodes2 = Math.min(15, Math.floor(2 + 4 * nodeBalance + 0.75 * Math.sqrt(simulation.difficulty)))
me.isBoss = true;
me.isSlashBoss = true;
me.showHealthBar = false;
me.damageReduction = 0.1 / (tech.isScaleMobsWithDuplication ? 1 + tech.duplicationChance() : 1)
me.startingDamageReduction = me.damageReduction
me.isInvulnerable = false
me.frictionAir = 0.02
me.seeAtDistance2 = 1000000;
me.accelMag = 0.0004 + 0.00015 * simulation.accelScale;
Matter.Body.setDensity(me, 0.0005); //normal is 0.001
me.collisionFilter.mask = cat.bullet | cat.player | cat.body | cat.map
me.memory = Infinity;
me.seePlayerFreq = 20
me.lockedOn = null;
me.laserRange = 500;
me.torqueMagnitude = 0.00024 * me.inertia * (Math.random() > 0.5 ? -1 : 1);
me.delay = 70 + 70 * simulation.CDScale;
me.cd = 0;
me.swordRadius = 0;
me.swordVertex = 1
me.swordRadiusMax = 1100 + 20 * simulation.difficulty;
me.swordRadiusGrowRate = me.swordRadiusMax * (0.005 + 0.0003 * simulation.difficulty)
me.isSlashing = false;
me.swordDamage = 0.07 * simulation.dmgScale
me.laserAngle = 3 * Math.PI / 5
me.eventHorizon = 550;
const seeDistance2 = 200000
ace.shield(me, x, y);
const rangeInnerVsOuter = Math.random()
let speed = (0.006 + 0.001 * Math.sqrt(simulation.difficulty)) * ((Math.random() < 0.5) ? 1 : -1)
let range = radius + 350 + 200 * rangeInnerVsOuter + nodes2 * 7
for (let i = 0; i < nodes2; i++) ace.orbital(me, range, i / nodes2 * 2 * Math.PI, speed)
const orbitalIndexes = [] //find indexes for all the current nodes2
for (let i = 0; i < nodes2; i++) orbitalIndexes.push(mob.length - 1 - i)
// add orbitals for each orbital
range = Math.max(60, 100 + 100 * Math.random() - nodes2 * 3 - rangeInnerVsOuter * 80)
speed = speed * (1.25 + 2 * Math.random())
const subNodes = Math.max(2, Math.floor(6 - 5 * nodeBalance + 0.5 * Math.sqrt(simulation.difficulty)))
for (let j = 0; j < nodes2; j++) {
for (let i = 0, len = subNodes; i < len; i++) ace.orbital(mob[orbitalIndexes[j]], range, i / len * 2 * Math.PI, speed)
}
for (let i = 0, len = 3 + 0.5 * Math.sqrt(simulation.difficulty); i < len; i++) ace.spawnOrbitals(me, radius + 40 + 10 * i, 1);
const springStiffness = 0.00014;
const springDampening = 0.0005;
me.springTarget = {
x: me.position.x,
y: me.position.y
};
const len = cons.length;
cons[len] = Constraint.create({
pointA: me.springTarget,
bodyB: me,
stiffness: springStiffness,
damping: springDampening
});
Composite.add(engine.world, cons[cons.length - 1]);
cons[len].length = 100 + 1.5 * radius;
me.cons = cons[len];
me.springTarget2 = {
x: me.position.x,
y: me.position.y
};
const len2 = cons.length;
cons[len2] = Constraint.create({
pointA: me.springTarget2,
bodyB: me,
stiffness: springStiffness,
damping: springDampening,
length: 0
});
Composite.add(engine.world, cons[cons.length - 1]);
cons[len2].length = 100 + 1.5 * radius;
me.cons2 = cons[len2];
me.onDamage = function () { };
me.onDeath = function () {
isDestroyed = true;
this.removeCons();
powerUps.spawnBossPowerUp(this.position.x, this.position.y);
};
me.do = function () {
for (let i = 0; i < this.vertices.length; i++) {
this.harmField(this.vertices[i].x, this.vertices[i].y);
}
this.seePlayerByHistory(40);
this.springAttack();
this.checkStatus();
this.sword() //does various things depending on what stage of the sword swing
const eventHorizon = this.eventHorizon * (1 + 0.2 * Math.sin(simulation.cycle * 0.008))
me.laserRange = eventHorizon;
};
me.swordWaiting = function () {
if (
this.seePlayer.recall &&
this.cd < simulation.cycle &&
this.distanceToPlayer2() < seeDistance2 &&
!m.isCloak &&
Matter.Query.ray(map, this.position, this.playerPosRandomY()).length === 0 &&
Matter.Query.ray(body, this.position, this.playerPosRandomY()).length === 0
) {
//find vertex farthest away from player
let dist = 0
for (let i = 0, len = this.vertices.length; i < len; i++) {
const D = Vector.magnitudeSquared(Vector.sub({ x: this.vertices[i].x, y: this.vertices[i].y }, m.pos))
if (D > dist) {
dist = D
this.swordVertex = i
}
}
this.laserAngle = this.swordVertex / 6 * 2 * Math.PI + 0.6283
this.sword = this.swordGrow
Matter.Body.setAngularVelocity(this, 0)
this.accelMag = 0.0004 + 0.00015 * simulation.accelScale;
this.damageReduction = 0
this.isInvulnerable = true
this.frictionAir = 1
}
}
me.sword = me.swordWaiting //base function that changes during different aspects of the sword swing
me.swordGrow = function () {
const angle = this.angle + this.laserAngle;
const end = {
x: this.vertices[this.swordVertex].x + this.swordRadiusMax * Math.cos(angle),
y: this.vertices[this.swordVertex].y + this.swordRadiusMax * Math.sin(angle)
};
const dx = end.x - this.vertices[this.swordVertex + 1 > (sides - 1) ? 0 : this.swordVertex + 1].x;
const dy = end.y - this.vertices[this.swordVertex + 1 > (sides - 1) ? 0 : this.swordVertex + 1].y;
const angle1 = Math.atan2(dy, dx) * (180 / Math.PI);
const dx1 = end.x - this.vertices[this.swordVertex - 1 < 0 ? (sides - 1) : this.swordVertex - 1].x;
const dy1 = end.y - this.vertices[this.swordVertex - 1 < 0 ? (sides - 1) : this.swordVertex - 1].y;
const angle2 = Math.atan2(dy1, dx1) * (180 / Math.PI);
this.laserSword(this.vertices[this.swordVertex], this.angle + this.laserAngle);
this.laserSword(this.vertices[this.swordVertex + 1 > (sides - 1) ? 0 : this.swordVertex + 1], angle1 * (Math.PI / 180))
this.laserSword(this.vertices[this.swordVertex - 1 < 0 ? (sides - 1) : this.swordVertex - 1], angle2 * (Math.PI / 180))
this.swordRadius += this.swordRadiusGrowRate
if (this.swordRadius > this.swordRadiusMax) {
this.sword = this.swordSlash
this.spinCount = 0
}
ctx.beginPath();
let vertices = this.vertices;
ctx.moveTo(vertices[0].x, vertices[0].y);
for (let j = 1; j < vertices.length; j++) ctx.lineTo(vertices[j].x, vertices[j].y);
ctx.lineTo(vertices[0].x, vertices[0].y);
ctx.lineWidth = 13 + 5 * Math.random();
ctx.strokeStyle = `rgba(255,255,255,${0.5 + 0.2 * Math.random()})`;
ctx.stroke();
}
me.swordSlash = function () {
const angle = this.angle + this.laserAngle;
const end = {
x: this.vertices[this.swordVertex].x + this.swordRadiusMax * Math.cos(angle),
y: this.vertices[this.swordVertex].y + this.swordRadiusMax * Math.sin(angle)
};
const dx = end.x - this.vertices[this.swordVertex + 1 > (sides - 1) ? 0 : this.swordVertex + 1].x;
const dy = end.y - this.vertices[this.swordVertex + 1 > (sides - 1) ? 0 : this.swordVertex + 1].y;
const angle1 = Math.atan2(dy, dx) * (180 / Math.PI);
const dx1 = end.x - this.vertices[this.swordVertex - 1 < 0 ? (sides - 1) : this.swordVertex - 1].x;
const dy1 = end.y - this.vertices[this.swordVertex - 1 < 0 ? (sides - 1) : this.swordVertex - 1].y;
const angle2 = Math.atan2(dy1, dx1) * (180 / Math.PI);
this.laserSword(this.vertices[this.swordVertex], this.angle + this.laserAngle);
this.laserSword(this.vertices[this.swordVertex + 1 > (sides - 1) ? 0 : this.swordVertex + 1], angle1 * (Math.PI / 180))
this.laserSword(this.vertices[this.swordVertex - 1 < 0 ? (sides - 1) : this.swordVertex - 1], angle2 * (Math.PI / 180))
this.torque += this.torqueMagnitude;
this.spinCount++
if (this.spinCount > 80) {
this.sword = this.swordWaiting
this.swordRadius = 0
this.accelMag = 0.0004 + 0.00015 * simulation.accelScale;
this.cd = simulation.cycle + this.delay;
this.damageReduction = this.startingDamageReduction
this.isInvulnerable = false
this.frictionAir = 0.01
}
ctx.beginPath();
let vertices = this.vertices;
ctx.moveTo(vertices[0].x, vertices[0].y);
for (let j = 1; j < vertices.length; j++) ctx.lineTo(vertices[j].x, vertices[j].y);
ctx.lineTo(vertices[0].x, vertices[0].y);
ctx.lineWidth = 13 + 5 * Math.random();
ctx.strokeStyle = `rgba(255,255,255,${0.5 + 0.2 * Math.random()})`;
ctx.stroke();
}
me.laserSword = function (where, angle) {
const vertexCollision = function (v1, v1End, domain) {
for (let i = 0; i < domain.length; ++i) {
let v = domain[i].vertices;
const len = v.length - 1;
for (let j = 0; j < len; j++) {
results = simulation.checkLineIntersection(v1, v1End, v[j], v[j + 1]);
if (results.onLine1 && results.onLine2) {
const dx = v1.x - results.x;
const dy = v1.y - results.y;
const dist2 = dx * dx + dy * dy;
if (dist2 < best.dist2 && (!domain[i].mob || domain[i].alive)) best = { x: results.x, y: results.y, dist2: dist2, who: domain[i], v1: v[j], v2: v[j + 1] };
}
}
results = simulation.checkLineIntersection(v1, v1End, v[0], v[len]);
if (results.onLine1 && results.onLine2) {
const dx = v1.x - results.x;
const dy = v1.y - results.y;
const dist2 = dx * dx + dy * dy;
if (dist2 < best.dist2) best = { x: results.x, y: results.y, dist2: dist2, who: domain[i], v1: v[0], v2: v[len] };
}
}
};
best = { x: null, y: null, dist2: Infinity, who: null, v1: null, v2: null };
const look = { x: where.x + this.swordRadius * Math.cos(angle), y: where.y + this.swordRadius * Math.sin(angle) };
vertexCollision(where, look, body); // vertexCollision(where, look, mob);
vertexCollision(where, look, map);
if (!m.isCloak) vertexCollision(where, look, [playerBody, playerHead]);
if (best.who && (best.who === playerBody || best.who === playerHead) && m.immuneCycle < m.cycle) {
m.immuneCycle = m.cycle + m.collisionImmuneCycles + 60; //player is immune to damage for an extra second
m.damage(this.swordDamage);
simulation.drawList.push({ //add dmg to draw queue
x: best.x,
y: best.y,
radius: this.swordDamage * 1500,
color: "rgba(0,0,0,0.5)",
time: 20
});
}
if (best.dist2 === Infinity) best = look;
ctx.beginPath(); //draw beam
ctx.moveTo(where.x, where.y);
ctx.lineTo(best.x, best.y);
ctx.strokeStyle = "rgba(0,0,0,0.1)"; // Black path
ctx.lineWidth = 25;
ctx.stroke();
ctx.strokeStyle = "rgba(0,0,0,0.5)"; // Black path
ctx.lineWidth = 5;
ctx.setLineDash([70 + 300 * Math.random(), 55 * Math.random()]);
ctx.stroke(); // Draw it
ctx.setLineDash([]);
}
me.harmField = function (x, y) {
ctx.setLineDash([125 * Math.random(), 125 * Math.random()]);
// ctx.lineDashOffset = 6*(simulation.cycle % 215);
if (this.distanceToPlayer3(x, y) < this.laserRange) {
if (m.immuneCycle < m.cycle) {
m.damage(0.0003 * simulation.dmgScale);
if (m.energy > 0.1) m.energy -= 0.003
}
ctx.beginPath();
ctx.moveTo(x, y);
ctx.lineTo(m.pos.x, m.pos.y);
ctx.lineTo(m.pos.x + (Math.random() - 0.5) * 3000, m.pos.y + (Math.random() - 0.5) * 3000);
ctx.lineWidth = 2;
ctx.strokeStyle = "rgb(0,0,0)";
ctx.stroke();
ctx.beginPath();
ctx.arc(m.pos.x, m.pos.y, 40, 0, 2 * Math.PI);
ctx.fillStyle = "rgba(0,0,0,0.15)";
ctx.fill();
}
ctx.beginPath();
ctx.arc(x, y, this.laserRange * 0.9, 0, 2 * Math.PI);
ctx.strokeStyle = "rgba(0,0,0,0.5)";
ctx.lineWidth = 1;
ctx.stroke();
ctx.setLineDash([]);
ctx.fillStyle = "rgba(0,0,0,0.03)";
ctx.fill();
}
me.distanceToPlayer3 = function (x, y) {
const dx = x - player.position.x;
const dy = y - player.position.y;
return Math.sqrt(dx * dx + dy * dy);
}
radius = 22 // radius of each node mob
const sideLength = 100 // distance between each node mob
const nodes = 6
const angle = 2 * Math.PI / nodes
spawn.allowShields = false; //don't want shields on individual mobs
for (let i = 0; i < nodes; ++i) {
ace.stabber(x + sideLength * Math.sin(i * angle), y + sideLength * Math.cos(i * angle), radius, 12);
Matter.Body.setDensity(mob[mob.length - 1], 0.003); //extra dense //normal is 0.001 //makes effective life much larger
mob[mob.length - 1].damageReduction = 0.12
mob[mob.length - 1].showHealthBar = false;
mob[mob.length - 1].isBoss = true;
targets.push(mob[mob.length - 1].id) //track who is in the node boss, for shields
}
const attachmentStiffness = 0.02
spawn.constrain2AdjacentMobs(nodes, attachmentStiffness, true); //loop mobs together
for (let i = 0; i < nodes; ++i) { //attach to center mob
consBB[consBB.length] = Constraint.create({
bodyA: me,
bodyB: mob[mob.length - i - 1],
stiffness: attachmentStiffness,
damping: 0.03
});
Composite.add(engine.world, consBB[consBB.length - 1]);
}
//spawn shield around all nodes
ace.groupShield(targets, x, y, sideLength + 1 * radius + nodes * 5 - 25);
spawn.allowShields = true;
},
}
level.setPosToSpawn(0, -50);
color.map = "crimson";
spawn.mapRect(level.enter.x, level.enter.y + 20, 100, 20);
spawn.mapRect(0, 0, 1, 1);
level.defaultZoom = 1800;
simulation.zoomTransition(level.defaultZoom);
document.body.style.backgroundColor = "#d8dadf";
const isSus = Math.random() < 0.001; //A very lucky person gets rickrolled
const mediaSource = isSus ? "https://ia801509.us.archive.org/10/items/Rick_Astley_Never_Gonna_Give_You_Up/Rick_Astley_Never_Gonna_Give_You_Up.ogv" : "https://cdn.glitch.me/b559a783-c0cb-4369-92e3-0c0a5556ba01/n-gon%20evangelion%20-%20Made%20with%20Clipchamp%20(8).mp4?v=1692134040246"
let videoContainer;
let video = document.createElement("video");
video.src = mediaSource;
video.autoPlay = true;
video.loop = true;
video.muted = true;
videoContainer = {
video: video,
ready: true,
};
video.play();
const boost1 = level.boost(8835, -3675, 7500);
const boost2 = level.boost(-8935, -3675, 7500);
ace.slash(0, -15000 + 1800);
function Raindrop(minX, minY, maxX, maxY) {
this.x = minX + Math.random() * (maxX - minX);
this.y = minY + Math.random() * (maxY - minY);
this.speed = Math.random() * 5 + 25;
this.length = Math.random() * 20 + 30;
}
function forceField(x, y, width, height) {
return {
min: { x: x, y: y },
max: { x: x + width, y: y + height },
width: width,
height: height,
maxHeight: height,
raindrops: [],
drawRaindrop(drop) {
if (Math.sqrt(Math.pow(player.position.x - drop.x, 2) + Math.pow(player.position.y - drop.y, 2)) + Math.PI < 5000) {
ctx.beginPath();
ctx.moveTo(drop.x, drop.y);
ctx.lineTo(drop.x, drop.y + drop.length);
ctx.strokeStyle = '#00FFFF';
ctx.lineWidth = 10;
ctx.lineCap = 'butt';
ctx.stroke();
}
},
updateRaindrop(drop) {
drop.y += drop.speed;
if ((Matter.Query.ray(map, { x: drop.x, y: drop.y }, { x: drop.x, y: drop.y - drop.length }).length === 0) == false) {
simulation.drawList.push({
x: drop.x,
y: drop.y - drop.length,
radius: 10,
color: "rgb(0,100,250,0.3)",
time: 8
});
do {
drop.y = this.min.y + this.height * Math.random();
drop.x = this.min.x + this.width * Math.random();
} while (drop.x > this.min.x && drop.x < this.max.x && drop.y > this.min.y && drop.y < this.max.y)
}
},
isOn: true,
query() {
if (this.isOn) {
ctx.fillStyle = `rgba(200, 20, 10, 0.55)`
ctx.fillRect(this.min.x, this.min.y, this.width, this.height)
if (this.height > 0 && Matter.Query.region([player], this).length) {
player.force.y -= 0.015;
m.energy = m.maxEnergy;
}
// if(this.raindrops.length < 300) { // too many (like 900) can cause a little bit of lag minus 5 ~ 10 fps, but it really just depends on your computer
// this.raindrops.push(new Raindrop());
// }
// for (let i = 0; i < this.raindrops.length; i++) {
// const drop = this.raindrops[i];
// this.drawRaindrop(drop);
// this.updateRaindrop(drop);
// }
}
},
}
}
const forceField1 = forceField(-750, -30000, 1500, 20000);
level.custom = () => {
if (player.position.y < -20000) {
level.nextLevel();
}
forceField1.query();
boost1.query();
boost2.query();
level.exit.drawAndCheck();
level.enter.draw();
ctx.beginPath();
ctx.strokeStyle = "rgba(220, 20, 10, 0.55)";
ctx.lineWidth = 1500;
ctx.lineJoin = "miter"
ctx.miterLimit = 100;
ctx.moveTo(map[272].vertices[0].x, map[272].vertices[0].y);
for (let i = 0; i < map[272].vertices.length; i++) {
ctx.lineTo(map[272].vertices[i].x, map[272].vertices[i].y);
}
ctx.closePath();
ctx.stroke();
};
let checkVid = () => {
if (simulation.paused && !videoContainer.paused) {
videoContainer.paused = true;
video.pause();
} else if (!simulation.paused && videoContainer.paused) {
videoContainer.paused = false;
video.play();
}
requestAnimationFrame(checkVid);
}
checkVid();
simulation.ephemera.push({
name: "vid",
do() {
if (level.levels[level.onLevel] !== "crimsonTowers") simulation.removeEphemera(this.name);
if (mediaSource && !isSus) {
ctx.drawImage(videoContainer.video, -1600, -15000, 3200, 1800);
} else if (mediaSource) {
ctx.drawImage(videoContainer.video, -1920 / 2, -15000, 1920, 1080);
}
}
});
level.customTopLayer = () => {
ctx.fillStyle = "rgba(220, 20, 10, 0.1)";
ctx.fillRect(-6725, -3500, 475, 2925);
ctx.fillRect(-8725, -3700, 450, 2925);
ctx.fillRect(-4725, -3300, 450, 2925);
ctx.fillRect(-2725, -3100, 450, 2925);
ctx.fillRect(-725, -2900, 450, 2925);
ctx.fillRect(275, -2900, 450, 2925);
ctx.fillRect(2275, -3100, 450, 2925);
ctx.fillRect(4275, -3300, 450, 2925);
ctx.fillRect(6275, -3500, 450, 2925);
ctx.fillRect(8275, -3700, 450, 2925);
};
spawn.mapRect(-10000, 0, 20000, 2000);
spawn.mapRect(-9050, -3650, 350, 50);
spawn.mapRect(8700, -3650, 350, 50);
spawn.mapRect(-275, -2825, 550, 50);
spawn.mapRect(-225, -500, 450, 50);
spawn.mapRect(-250, -1575, 500, 50);
function spawnTower(index, y = 0) {
const x = index - 1325;
spawn.mapRect(x + 1025, y + -950, 125, 750);
spawn.mapRect(x + 1125, y + -225, 50, 50);
spawn.mapRect(x + 1500, y + -950, 125, 750);
spawn.mapRect(x + 1475, y + -225, 50, 50);
spawn.mapRect(x + 1600, y + -225, 50, 50);
spawn.mapRect(x + 1000, y + -225, 50, 50);
spawn.mapRect(x + 1475, y + -475, 50, 50);
spawn.mapRect(x + 1125, y + -750, 50, 50);
spawn.mapRect(x + 1050, y + -2025, 100, 1125);
spawn.mapRect(x + 1500, y + -2025, 100, 1125);
spawn.mapRect(x + 1475, y + -1050, 50, 50);
spawn.mapRect(x + 1125, y + -1325, 50, 50);
spawn.mapRect(x + 1475, y + -1550, 50, 50);
spawn.mapRect(x + 1125, y + -1875, 50, 50);
spawn.mapRect(x + 1075, y + -2900, 75, 925);
spawn.mapRect(x + 1500, y + -2900, 75, 925);
spawn.mapRect(x + 1475, y + -2150, 50, 50);
spawn.mapRect(x + 1125, y + -2475, 50, 50);
spawn.mapRect(x + 1475, y + -2800, 50, 50);
spawn.mapRect(x + 1000, y + -975, 50, 50);
spawn.mapRect(x + 1025, y + -2050, 50, 50);
spawn.mapRect(x + 1050, y + -2925, 50, 50);
spawn.mapRect(x + 1550, y + -2925, 50, 50);
spawn.mapRect(x + 1600, y + -975, 50, 50);
spawn.mapRect(x + 1575, y + -2050, 50, 50);
for (let i = 0; i < 5; i++) {
if (Math.random() > 0.5) { ace.slasher2(index, y - (i * 500) - 500) } else { ace.slasher3(index, y - (i * 500) - 500) };
}
}
// ace.slash(0, -5000);
function spawnChain(x, y, x1, y1, length = 39) {
const angle = Math.atan2(y1 - y, x1 - x);
chain(x, y, angle, true, length);
}
function chain(x, y, angle = 0, isAttached = true, len = 15, radius = 20, stiffness = 1, damping = 1) {
const gap = 2 * radius
const unit = {
x: Math.cos(angle),
y: Math.sin(angle)
}
for (let i = 0; i < len; i++) {
bullet[bullet.length] = Bodies.polygon(x + gap * unit.x * i, y + gap * unit.y * i, 12, radius, {
inertia: Infinity,
isNotHoldable: true
});
const who = bullet[bullet.length - 1];
who.do = () => { };
who.collisionFilter.category = cat.body;
who.collisionFilter.mask = cat.player | cat.bullet | cat.body | cat.bullet | cat.bullet | cat.bulletBullet
Composite.add(engine.world, who); //add to world
who.classType = "bullet"
}
for (let i = 1; i < len; i++) {
consBB[consBB.length] = Constraint.create({
bodyA: bullet[bullet.length - i],
bodyB: bullet[bullet.length - i - 1],
stiffness: stiffness,
damping: damping
});
Composite.add(engine.world, consBB[consBB.length - 1]);
}
cons[cons.length] = Constraint.create({
pointA: {
x: x,
y: y
},
bodyB: bullet[bullet.length - len],
stiffness: 1,
damping: damping
});
Composite.add(engine.world, cons[cons.length - 1]);
if (isAttached) {
cons[cons.length] = Constraint.create({
pointA: {
x: x + gap * unit.x * (len - 1),
y: y + gap * unit.y * (len - 1)
},
bodyB: bullet[bullet.length - 1],
stiffness: 1,
damping: damping
});
Composite.add(engine.world, cons[cons.length - 1]);
}
}
spawnChain(-2250, -3100, -750, -2900);
spawnChain(-4250, -3300, -2750, -3100);
spawnChain(-6250, -3500, -4750, -3300);
spawnChain(-8250, -3700, -6750, -3500);
spawnChain(750, -2900, 2250, -3100);
spawnChain(2750, -3100, 4250, -3300);
spawnChain(4750, -3300, 6250, -3500);
spawnChain(6750, -3500, 8250, -3700);
// spawnChain(-3000, -30000, -9500, -20400, 291);
// spawnChain(3000, -30000, 9500, -20400, 291);
spawnTower(500);
spawnTower(2500, -200);
spawn.mapRect(2000, -200, 7000, 300);
spawnTower(4500, -400);
spawn.mapRect(4000, -400, 5000, 300);
spawnTower(6500, -600);
spawn.mapRect(6000, -600, 5000, 300);
spawnTower(8500, -800);
spawn.mapRect(8000, -800, 3000, 300);
spawnTower(-500);
spawnTower(-2500, -200);
spawn.mapRect(-10000, -200, 8000, 300);
spawnTower(-4500, -400);
spawn.mapRect(-10000, -400, 6000, 300);
spawnTower(-6500, -600);
spawn.mapRect(-10000, -600, 4000, 300);
spawnTower(-8500, -800);
spawn.mapRect(-10000, -800, 2000, 300);
spawn.mapVertex(10000, -9450, "-1000 0 1000 0 1000 -10000 500 -20000 -500 -20000 -1000 -10000");
spawn.mapVertex(-10000, -9450, "-1000 0 1000 0 1000 -10000 500 -20000 -500 -20000 -1000 -10000");
spawn.mapRect(-11000, -675, 2000, 2675);
spawn.mapRect(9000, -675, 2000, 2675);
spawn.mapVertex(0, -30000, "0 0 3000 -10000 6000 0 3000 10000");
spawn.mapRect(-8750, -10000, 8000, 100);
spawn.mapRect(750, -10000, 8000, 100);
spawn.mapVertex(0, -10020, "-1000 0 -5000 300 5000 300 1000 0");
spawn.mapRect(-800, -10250, 100, 350);
spawn.mapRect(700, -10250, 100, 350);
const a = 200;
const maxTheta = 10 * Math.PI;
const spiralX = (theta) => a * theta * Math.cos(theta);
const spiralY = (theta) => a * theta * Math.sin(theta);
for (let i = 1; i <= maxTheta; i += 0.2) {
const x = spiralX(i);
const y = spiralY(i) + (-15000 + 1800 / 2);
spawn.mapRect(x, y, 100, 100);
}
level.exit.y = map[272].position.y;
level.exit.x = map[272].position.x;
},
// ********************************************************************************************************
// ********************************************************************************************************
// ***************************************** training levels **********************************************
// ********************************************************************************************************
// ********************************************************************************************************
walk() { //learn to walk
if (localSettings.isHideHUD) localSettings.isHideHUD = false
m.addHealth(Infinity)
document.getElementById("health").style.display = "none" //hide your health bar
document.getElementById("health-bg").style.display = "none"
document.getElementById("defense-bar").style.display = "none"
document.getElementById("damage-bar").style.display = "none"
level.setPosToSpawn(60, -50); //normal spawn
spawn.mapRect(10, -10, 100, 20); //small platform for player
level.exit.x = 1775;
level.exit.y = -35;
spawn.mapRect(level.exit.x, level.exit.y + 25, 100, 100); //exit bump
simulation.zoomScale = 1400 //1400 is normal
level.defaultZoom = 1400
simulation.zoomTransition(level.defaultZoom, 1)
document.body.style.backgroundColor = level.trainingBackgroundColor
simulation.lastLogTime = 0; //clear previous messages
let instruction = 0
level.trainingText(`move <strong>↔</strong> with <strong class="key-input-train">${input.key.left.replace('Key', '').replace('Digit', '')}</strong> and <strong class="key-input-train">${input.key.right.replace('Key', '').replace('Digit', '')}</strong>`)
level.custom = () => {
if (instruction === 0 && input.right) {
instruction++
level.trainingText(`<s>move <strong>↔</strong> with <strong class="key-input-train">${input.key.left.replace('Key', '').replace('Digit', '')}</strong> and <strong class="key-input-train">${input.key.right.replace('Key', '').replace('Digit', '')}</strong></s>
<br>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 <strong class="key-input-train">${input.key.down.replace('Key', '').replace('Digit', '')}</strong> to crouch`)
level.custom = () => {
if (instruction === 0 && input.down) {
instruction++
level.trainingText(`<s>press <strong class="key-input-train">${input.key.down.replace('Key', '').replace('Digit', '')}</strong> to crouch</s>`)
}
//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 <strong class="key-input-train">${input.key.up.replace('Key', '').replace('Digit', '')}</strong> longer to jump higher`)
level.custom = () => {
if (instruction === 0 && m.pos.x > 300) {
instruction++
level.trainingText(`<s>hold down <strong class="key-input-train">${input.key.up.replace('Key', '').replace('Digit', '')}</strong> longer to jump higher</s>`)
}
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 <strong class='color-f'>field</strong> with <strong class="key-input-train">${input.key.field.replace('Key', '').replace('Digit', '')}</strong> or <strong>right mouse</strong>`)
level.custom = () => {
if (instruction === 0 && input.field) {
instruction++
level.trainingText(`<s>activate your <strong class='color-f'>field</strong> with <strong class="key-input-train">${input.key.field.replace('Key', '').replace('Digit', '')}</strong> or <strong>right mouse</strong></s><br>release your <strong class='color-f'>field</strong> on a <strong class='color-block'>block</strong> to pick it up`)
} else if (instruction === 1 && m.isHolding) {
instruction++
level.trainingText(`<s>activate your <strong class='color-f'>field</strong> with <strong class="key-input-train">${input.key.field.replace('Key', '').replace('Digit', '')}</strong> or <strong>right mouse</strong><br>release your <strong class='color-f'>field</strong> on a <strong class='color-block'>block</strong> to pick it up</s><br>drop the <strong class='color-block'>block</strong> 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(`<s>activate your <strong class='color-f'>field</strong> with <strong class="key-input-train">${input.key.field.replace('Key', '').replace('Digit', '')}</strong> or <strong>right mouse</strong><br>release your <strong class='color-f'>field</strong> on a <strong class='color-block'>block</strong> to pick it up<br>drop the <strong class='color-block'>block</strong> on the red button to open the door</s>`)
}
//exit room
ctx.fillStyle = "#f2f2f2"
ctx.fillRect(1600, -400, 400, 400)
level.enter.draw();
level.exit.drawAndCheck();
};
level.customTopLayer = () => {
buttonDoor.query();
buttonDoor.draw();
if (buttonDoor.isUp) {
door.isClosing = true
} else {
door.isClosing = false
}
door.openClose();
door.draw();
//exit room glow
ctx.fillStyle = "rgba(0,255,255,0.05)"
ctx.fillRect(1600, -400, 400, 400)
};
spawn.mapRect(-2750, -2800, 2600, 4600); //left wall
spawn.mapRect(2000, -2800, 2600, 4600); //right wall
spawn.mapRect(-250, 50, 3500, 1750); //floor
spawn.mapRect(-200, 0, 950, 100);
spawn.mapRect(1575, 0, 500, 100);
spawn.mapRect(-250, -2800, 3500, 2200); //roof
spawn.mapRect(725, 12, 50, 25);
spawn.mapRect(725, 25, 75, 25);
spawn.mapRect(750, 38, 75, 25);
spawn.mapRect(1525, 25, 75, 50);
spawn.mapRect(1500, 38, 50, 25);
spawn.mapRect(1550, 12, 50, 25);
spawn.mapRect(1600, -1200, 500, 850); //exit roof
spawn.mapRect(1600, -400, 50, 225); //exit room left upper wall
},
throw() { //throw a block on button to open door
m.addHealth(Infinity)
level.setPosToSpawn(60, -50); //normal spawn
spawn.mapRect(10, -10, 100, 20); //small platform for player
level.exit.x = 1775;
level.exit.y = -35;
spawn.mapRect(level.exit.x, level.exit.y + 25, 100, 100); //exit bump
simulation.zoomScale = 1400 //1400 is normal
level.defaultZoom = 1400
simulation.zoomTransition(level.defaultZoom, 1)
document.body.style.backgroundColor = level.trainingBackgroundColor
spawn.bodyRect(1025, -75, 50, 50); //block to go on button
const buttonDoor = level.button(1635, -400)
const door = level.door(1612.5, -175, 25, 190, 185, 3)
// activate your <strong class='color-f'>field</strong> with <strong class="key-input-train">${input.key.field.replace('Key', '').replace('Digit', '')}</strong> or <strong>right mouse</strong>
let instruction = 0
level.trainingText(`pick up the <strong class='color-block'>block</strong> with your <strong class='color-f'>field</strong>`)
level.custom = () => {
if (instruction === 0 && m.isHolding) {
instruction++
level.trainingText(`<s>pick up the <strong class='color-block'>block</strong> with your <strong class='color-f'>field</strong></s>
<br>hold your <strong class='color-f'>field</strong> down to charge up then release to throw a <strong class='color-block'>block</strong>`)
} else if (instruction === 1 && m.throwCharge > 2) {
instruction++
level.trainingText(`<s>pick up the <strong class='color-block'>block</strong> with your <strong class='color-f'>field</strong>
<br>hold your <strong class='color-f'>field</strong> down to charge up then release to throw a <strong class='color-block'>block</strong></s>
<br>throw the <strong class='color-block'>block</strong> onto the button`)
// the <strong class='color-block'>block</strong> at the button
} else if (instruction === 2 && !buttonDoor.isUp && Vector.magnitudeSquared(Vector.sub(body[0].position, buttonDoor.min)) < 10000) {
instruction++
level.trainingText(`<s>pick up the <strong class='color-block'>block</strong> with your <strong class='color-f'>field</strong>
<br>hold your <strong class='color-f'>field</strong> down to charge up then release to throw a <strong class='color-block'>block</strong>
<br>throw the <strong class='color-block'>block</strong> onto the button</s>`)
}
//exit room
ctx.fillStyle = "#f2f2f2"
ctx.fillRect(1600, -400, 400, 400)
level.enter.draw();
level.exit.drawAndCheck();
};
level.customTopLayer = () => {
buttonDoor.query();
buttonDoor.draw();
if (buttonDoor.isUp) {
door.isClosing = true
} else {
door.isClosing = false
}
door.openClose();
door.draw();
//exit room glow
ctx.fillStyle = "rgba(0,255,255,0.05)"
ctx.fillRect(1600, -400, 400, 400)
};
spawn.mapRect(-2750, -2800, 2600, 4600); //left wall
spawn.mapRect(2000, -2800, 2600, 4600); //right wall
spawn.mapRect(-250, 50, 3500, 1750); //floor
spawn.mapRect(-200, 0, 950, 100);
spawn.mapRect(1575, 0, 500, 100);
spawn.mapRect(-250, -2800, 3500, 2200); //roof
spawn.mapRect(725, 12, 50, 25);
spawn.mapRect(725, 25, 75, 25);
spawn.mapRect(750, 38, 75, 25);
spawn.mapRect(1525, 25, 75, 50);
spawn.mapRect(1500, 38, 50, 25);
spawn.mapRect(1550, 12, 50, 25);
// spawn.mapRect(1600, -1200, 500, 850); //exit roof
spawn.mapRect(1790, -600, 250, 225); //button left wall
spawn.mapRect(1625, -400, 400, 50);
spawn.mapRect(1600, -400, 50, 225); //exit room left upper wall
},
throwAt() { //throw a block at mob to open door
m.addHealth(Infinity)
level.setPosToSpawn(60, -50); //normal spawn
spawn.mapRect(10, -10, 100, 20); //small platform for player
level.exit.x = 1775;
level.exit.y = -35;
spawn.mapRect(level.exit.x, level.exit.y + 25, 100, 100); //exit bump
simulation.zoomScale = 1400 //1400 is normal
level.defaultZoom = 1400
simulation.zoomTransition(level.defaultZoom, 1)
document.body.style.backgroundColor = level.trainingBackgroundColor
const door = level.door(1612.5, -175, 25, 190, 185, 3)
let instruction = 0
level.trainingText(`throw the <strong class='color-block'>block</strong> at the <strong>mobs</strong> to open the door`)
level.custom = () => {
if (instruction === 0 && !mob.length) {
instruction++
level.trainingText(`<s>throw the <strong class='color-block'>block</strong> at the <strong>mobs</strong> to open the door</s>`)
}
//exit room
ctx.fillStyle = "#f2f2f2"
ctx.fillRect(1600, -400, 400, 400)
level.enter.draw();
level.exit.drawAndCheck();
};
level.customTopLayer = () => {
if (mob.length > 0) {
door.isClosing = true
} else {
door.isClosing = false
}
door.openClose();
door.draw();
//exit room glow
ctx.fillStyle = "rgba(0,255,255,0.05)"
ctx.fillRect(1600, -400, 400, 400)
};
spawn.mapRect(-2750, -2800, 2600, 4600); //left wall
spawn.mapRect(2000, -2800, 2600, 4600); //right wall
spawn.mapRect(-250, 50, 3500, 1750); //floor
spawn.mapRect(-200, 0, 950, 100);
spawn.mapRect(1575, 0, 500, 100);
spawn.mapRect(-250, -2800, 3500, 2200); //roof
spawn.mapRect(725, 12, 50, 25);
spawn.mapRect(725, 25, 75, 25);
spawn.mapRect(750, 38, 75, 25);
spawn.mapRect(1525, 25, 75, 50);
spawn.mapRect(1500, 38, 50, 25);
spawn.mapRect(1550, 12, 50, 25);
// spawn.mapRect(1600, -1200, 500, 850); //exit roof
// spawn.mapRect(1790, -600, 250, 225); //button left wall
// spawn.mapRect(1625, -400, 400, 50);
spawn.mapRect(1600, -400, 50, 225); //exit room left upper wall
spawn.mapRect(1600, -600, 425, 250);
spawn.bodyRect(1025, -75, 50, 50); //block to go on button
spawn.starter(425, -350, 35)
spawn.starter(800, -350, 44)
},
fire() { //throw a block at mob to open door
level.setPosToSpawn(60, -50); //normal spawn
spawn.mapRect(10, -10, 100, 20); //small platform for player
level.exit.x = 1775;
level.exit.y = 15;
spawn.mapRect(level.exit.x, level.exit.y + 25, 100, 100); //exit bump
simulation.zoomScale = 1400 //1400 is normal
level.defaultZoom = 1400
simulation.zoomTransition(level.defaultZoom, 1)
document.body.style.backgroundColor = level.trainingBackgroundColor
const door = level.door(1612.5, -125, 25, 190, 185, 3)
const buttonDoor = level.button(400, 0)
let instruction = 0
level.trainingText(`use your <strong class='color-f'>field</strong> to pick up the gun power up`)
level.custom = () => {
if (instruction === 0 && simulation.isChoosing) {
instruction++
level.trainingText(`<s>use your <strong class='color-f'>field</strong> to pick up the gun power up</s>
<br>choose a <strong class='color-g'>gun</strong>`)
} else if (instruction === 1 && !simulation.isChoosing) {
instruction++
level.trainingText(`<s>use your <strong class='color-f'>field</strong> to pick up the gun power up
<br>choose a <strong class='color-g'>gun</strong></s>
<br>use the <strong>left mouse</strong> button to shoot the <strong>mobs</strong>`)
} else if (instruction === 2 && mob.length === 0) {
instruction++
level.trainingText(`<s>use your <strong class='color-f'>field</strong> to pick up the gun power up
<br>choose a <strong class='color-g'>gun</strong>
<br>use the <strong>left mouse</strong> button to shoot the <strong>mobs</strong></s>
<br>drop a <strong class='color-block'>block</strong> on the red button to open the door`)
} else if (instruction === 3 && !door.isClosing) {
instruction++
level.trainingText(`<s>use your <strong class='color-f'>field</strong> to pick up the gun power up
<br>choose a <strong class='color-g'>gun</strong>
<br>use the <strong>left mouse</strong> button to shoot the <strong>mobs</strong>
<br>put a <strong class='color-block'>block</strong> on the red button to open the door</s>`)
}
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 <strong class='color-f'>field</strong> with <strong>${input.key.field.replace('Key', '').replace('Digit', '')}</strong> or <strong>right mouse</strong>
level.trainingText(`use your <strong class='color-f'>field</strong> to <strong>deflect</strong> the <strong style="color:rgb(215,0,145);">mobs</strong>`)
level.custom = () => {
if (instruction === 0 && m.pos.x > 1350) {
instruction++
level.trainingText(`<s>use your <strong class='color-f'>field</strong> to <strong>deflect</strong> the <strong style="color:rgb(215,0,145);">mobs</strong></s>`)
}
//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 <strong>health</strong> is displayed in the top left corner
<br>use your <strong class='color-f'>field</strong> to pick up <div class="heal-circle" style = "border: none;"></div> until your <strong>health</strong> is full`)
level.custom = () => {
if (instruction === 0 && m.health === 1) {
instruction++
level.trainingText(`<s>use your <strong class='color-f'>field</strong> to pick up <div class="heal-circle" style = "border: none;"></div> until your <strong>health</strong> is full</s>`)
}
//exit room
ctx.fillStyle = "#f2f2f2"
ctx.fillRect(1600, -400, 400, 400)
level.enter.draw();
level.exit.drawAndCheck();
};
level.customTopLayer = () => {
if (m.health !== 1) {
door.isClosing = true
} else {
door.isClosing = false
}
door.openClose();
door.draw();
//exit room glow
ctx.fillStyle = "rgba(0,255,255,0.05)"
ctx.fillRect(1600, -400, 400, 400)
};
spawn.mapRect(-2750, -2800, 2600, 4600); //left wall
spawn.mapRect(2000, -2800, 2600, 4600); //right wall
spawn.mapRect(-250, 0, 3500, 1800); //floor
spawn.mapRect(1575, 0, 500, 100);
spawn.mapRect(-250, -2800, 3500, 2200); //roof
spawn.mapRect(700, -8, 50, 25);
spawn.mapRect(725, -16, 75, 25);
spawn.mapRect(1375, -16, 50, 50);
spawn.mapRect(1400, -8, 50, 25);
spawn.mapRect(750, -24, 650, 100);
powerUps.directSpawn(875, -40, "heal", false, null, 15);
powerUps.directSpawn(1075, -50, "heal", false, null, 25);
powerUps.directSpawn(1275, -65, "heal", false, null, 35);
const door = level.door(1612.5, -175, 25, 190, 185, 3)
spawn.mapRect(1600, -1200, 500, 850); //exit roof
spawn.mapRect(1600, -400, 50, 225); //exit room left upper wall
},
nailGun() {
level.difficultyIncrease(1) //difficulty on training mode resets to zero with each new level
level.setPosToSpawn(60, -50); //normal spawn
spawn.mapRect(10, -10, 100, 20); //small platform for player
level.exit.x = 1775;
level.exit.y = -35;
spawn.mapRect(level.exit.x, level.exit.y + 25, 100, 100); //exit bump
simulation.zoomScale = 1400 //1400 is normal
level.defaultZoom = 1400
simulation.zoomTransition(level.defaultZoom, 1)
document.body.style.backgroundColor = level.trainingBackgroundColor
b.removeAllGuns();
b.giveGuns("nail gun")
b.guns[b.activeGun].ammo = 0
simulation.updateGunHUD();
const door = level.door(1612.5, -175, 25, 190, 185, 3)
let instruction = 0
level.trainingText(`use your <strong class='color-f'>field</strong> to pick up <div class="ammo-circle" style = "border: none;"></div> for your <strong class='color-g'>nail gun</strong>`)
level.custom = () => {
if (instruction === 0 && b.inventory.length && b.guns[b.activeGun].ammo > 0) {
instruction++
level.trainingText(`<s>use your <strong class='color-f'>field</strong> to pick up <div class="ammo-circle" style = "border: none;"></div> for your <strong class='color-g'>nail gun</strong></s>
<br>use the <strong>left mouse</strong> button to shoot the <strong>mobs</strong>`)
} else if (instruction === 1 && mob.length === 0) {
instruction++
level.trainingText(`<s>use your <strong class='color-f'>field</strong> to pick up <div class="ammo-circle" style = "border: none;"></div> for your <strong class='color-g'>nail gun</strong>
<br>use the <strong>left mouse</strong> button to shoot the <strong>mobs</strong></s>`)
}
//spawn ammo if you run out
let isAmmo = false
for (let i = 0; i < powerUp.length; i++) {
if (powerUp[i].name === 'ammo') isAmmo = true
}
if (!isAmmo && b.inventory.length && b.guns[b.activeGun].ammo === 0) {
powerUps.directSpawn(1300, -2000, "ammo", false);
powerUps.directSpawn(1301, -2200, "ammo", false);
}
//exit room
ctx.fillStyle = "#f2f2f2"
ctx.fillRect(1600, -400, 400, 400)
level.enter.draw();
level.exit.drawAndCheck();
};
level.customTopLayer = () => {
if (mob.length > 0) {
door.isClosing = true
} else {
door.isClosing = false
}
door.openClose();
door.draw();
//exit room glow
ctx.fillStyle = "rgba(0,255,255,0.05)"
ctx.fillRect(1600, -400, 400, 400)
//ammo tunnel shadow
ctx.fillStyle = "rgba(0,0,0,0.4)"
ctx.fillRect(1250, -2800, 100, 2200)
};
if (m.health < 1) {
powerUps.directSpawn(1298, -3500, "heal", false, 23);
powerUps.directSpawn(1305, -3000, "heal", false, 35);
}
for (let i = 0; i < 2; i++) {
spawn.spinner(1300 + i, -3000 - 200 * i, 25 + 5 * i)
Matter.Body.setVelocity(mob[mob.length - 1], {
x: 0,
y: 62
});
}
spawn.mapRect(-2750, -2800, 2600, 4600); //left wall
spawn.mapRect(2000, -2800, 2600, 4600); //right wall
spawn.mapRect(-250, 50, 3500, 1750); //floor
spawn.mapRect(-200, 0, 950, 100);
spawn.mapRect(1575, 0, 500, 100);
spawn.mapRect(-150, -2800, 1400, 2200); //roof with tunnel for ammo
spawn.mapRect(1350, -2800, 675, 2200);
spawn.mapRect(725, 12, 50, 25);
spawn.mapRect(725, 25, 75, 25);
spawn.mapRect(750, 38, 75, 25);
spawn.mapRect(1525, 25, 75, 50);
spawn.mapRect(1500, 38, 50, 25);
spawn.mapRect(1550, 12, 50, 25);
// spawn.mapRect(1600, -1200, 500, 850); //exit roof
// spawn.mapRect(1790, -600, 250, 225); //button left wall
// spawn.mapRect(1625, -400, 400, 50);
spawn.mapRect(1600, -400, 50, 225); //exit room left upper wall
spawn.mapRect(1600, -600, 425, 250);
},
shotGun() {
level.difficultyIncrease(1) //difficulty on training mode resets to zero with each new level
level.setPosToSpawn(60, -50); //normal spawn
spawn.mapRect(10, -10, 100, 20); //small platform for player
level.exit.x = 1775;
level.exit.y = -35;
spawn.mapRect(level.exit.x, level.exit.y + 25, 100, 100); //exit bump
simulation.zoomScale = 1400 //1400 is normal
level.defaultZoom = 1400
simulation.zoomTransition(level.defaultZoom, 1)
document.body.style.backgroundColor = level.trainingBackgroundColor
b.removeAllGuns();
b.giveGuns("shotgun")
// b.guns[b.activeGun].ammo = 0
// simulation.updateGunHUD();
const door = level.door(1612.5, -175, 25, 190, 185, 3)
let instruction = 0
level.trainingText(`use your <strong class='color-g'>shotgun</strong> to clear the room of mobs`)
level.custom = () => {
if (instruction === 0 && mob.length === 0) {
instruction++
level.trainingText(`<s>use your <strong class='color-g'>shotgun</strong> to clear the room of mobs</s>`)
}
//spawn ammo if you run out
let isAmmo = false
for (let i = 0; i < powerUp.length; i++) {
if (powerUp[i].name === 'ammo') isAmmo = true
}
if (!isAmmo && b.inventory.length && b.guns[b.activeGun].ammo === 0) {
powerUps.directSpawn(1300, -2000, "ammo", false);
powerUps.directSpawn(1301, -2200, "ammo", false);
}
//exit room
ctx.fillStyle = "#f2f2f2"
ctx.fillRect(1600, -400, 400, 400)
level.enter.draw();
level.exit.drawAndCheck();
};
level.customTopLayer = () => {
if (mob.length > 0) {
door.isClosing = true
} else {
door.isClosing = false
}
door.openClose();
door.draw();
//exit room glow
ctx.fillStyle = "rgba(0,255,255,0.05)"
ctx.fillRect(1600, -400, 400, 400)
//ammo tunnel shadow
ctx.fillStyle = "rgba(0,0,0,0.4)"
ctx.fillRect(1250, -2800, 100, 2200)
};
if (m.health < 1) {
powerUps.directSpawn(1298, -3500, "heal", false, 23);
powerUps.directSpawn(1305, -3000, "heal", false, 35);
}
for (let i = 0; i < 3; i++) {
spawn.hopper(1300 + i, -3000 - 2000 * i, 25 + 5 * i)
// Matter.Body.setVelocity(mob[mob.length - 1], { x: 0, y: 0 });
}
spawn.mapRect(-2750, -2800, 2600, 4600); //left wall
spawn.mapRect(2000, -2800, 2600, 4600); //right wall
spawn.mapRect(-250, 50, 3500, 1750); //floor
spawn.mapRect(-200, 0, 950, 100);
spawn.mapRect(1575, 0, 500, 100);
spawn.mapRect(-150, -2800, 1400, 2200); //roof with tunnel for ammo
spawn.mapRect(1350, -2800, 675, 2200);
spawn.mapRect(725, 12, 50, 25);
spawn.mapRect(725, 25, 75, 25);
spawn.mapRect(750, 38, 75, 25);
spawn.mapRect(1525, 25, 75, 50);
spawn.mapRect(1500, 38, 50, 25);
spawn.mapRect(1550, 12, 50, 25);
spawn.mapRect(1600, -400, 50, 225); //exit room left upper wall
spawn.mapRect(1600, -600, 425, 250);
},
superBall() {
level.difficultyIncrease(1) //difficulty on training mode resets to zero with each new level
level.setPosToSpawn(60, -50); //normal spawn
spawn.mapRect(10, -10, 100, 20); //small platform for player
level.exit.x = 1775;
level.exit.y = -35;
spawn.mapRect(level.exit.x, level.exit.y + 25, 100, 100); //exit bump
simulation.zoomScale = 1400 //1400 is normal
level.defaultZoom = 1400
simulation.zoomTransition(level.defaultZoom, 1)
document.body.style.backgroundColor = level.trainingBackgroundColor
b.removeAllGuns();
b.giveGuns("super balls")
// b.guns[b.activeGun].ammo = 0
// simulation.updateGunHUD();
const door = level.door(1612.5, -175, 25, 190, 185, 3)
let instruction = 0
level.trainingText(`use <strong class='color-g'>super balls</strong> to clear the room of mobs`)
level.custom = () => {
if (instruction === 0 && mob.length === 0) {
instruction++
level.trainingText(`<s>use <strong class='color-g'>super balls</strong> to clear the room of mobs</s>`)
}
//spawn ammo if you run out
let isAmmo = false
for (let i = 0; i < powerUp.length; i++) {
if (powerUp[i].name === 'ammo') isAmmo = true
}
if (!isAmmo && b.inventory.length && b.guns[b.activeGun].ammo === 0) {
powerUps.directSpawn(1300, -2000, "ammo", false);
powerUps.directSpawn(1301, -2200, "ammo", false);
}
//exit room
ctx.fillStyle = "#f2f2f2"
ctx.fillRect(1600, -400, 400, 400)
level.enter.draw();
level.exit.drawAndCheck();
};
level.customTopLayer = () => {
if (mob.length > 0) {
door.isClosing = true
} else {
door.isClosing = false
}
door.openClose();
door.draw();
//exit room glow
ctx.fillStyle = "rgba(0,255,255,0.05)"
ctx.fillRect(1600, -400, 400, 400)
//ammo tunnel shadow
ctx.fillStyle = "rgba(0,0,0,0.2)"
// ctx.fillRect(1225, -2800, 125, 2450)
ctx.fillRect(-150, -2800, 1500, 2450);
};
if (m.health < 1) {
powerUps.directSpawn(1298, -3500, "heal", false, 23);
powerUps.directSpawn(1305, -3000, "heal", false, 35);
}
for (let i = 0; i < 6; i++) {
spawn.spawner(i * 230, -800)
// Matter.Body.setVelocity(mob[mob.length - 1], { x: 0, y: 0 });
}
spawn.mapVertex(510, -430, "725 0 725 80 -650 80 -650 -80 650 -80"); //upper room with mobs
spawn.mapRect(-225, -2800, 1450, 2000);
spawn.mapRect(1350, -2800, 675, 2450);
spawn.mapRect(-2750, -2800, 2600, 4600); //left wall
spawn.mapRect(2000, -2800, 2600, 4600); //right wall
spawn.mapRect(-250, 50, 3500, 1750); //floor
spawn.mapRect(-200, 0, 950, 100);
spawn.mapRect(1575, 0, 500, 100);
spawn.mapRect(725, 12, 50, 25);
spawn.mapRect(725, 25, 75, 25);
spawn.mapRect(750, 38, 75, 25);
spawn.mapRect(1525, 25, 75, 50);
spawn.mapRect(1500, 38, 50, 25);
spawn.mapRect(1550, 12, 50, 25);
spawn.mapRect(1600, -400, 50, 225); //exit room left upper wall
},
matterWave() { //fire wave through the map to kill mosb
level.difficultyIncrease(1) //difficulty on training mode resets to zero with each new level
level.setPosToSpawn(60, -50); //normal spawn
spawn.mapRect(10, -10, 100, 20); //small platform for player
level.exit.x = 1775;
level.exit.y = -35;
spawn.mapRect(level.exit.x, level.exit.y + 25, 100, 100); //exit bump
simulation.zoomScale = 1400 //1400 is normal
level.defaultZoom = 1400
simulation.zoomTransition(level.defaultZoom, 1)
document.body.style.backgroundColor = level.trainingBackgroundColor
b.removeAllGuns();
b.giveGuns("wave")
// b.guns[b.activeGun].ammo = 0
// simulation.updateGunHUD();
const door = level.door(1612.5, -175, 25, 190, 185, 3)
let instruction = 0
level.trainingText(`use <strong class='color-g'>wave</strong> to clear the room of mobs`)
level.custom = () => {
if (instruction === 0 && mob.length === 0) {
instruction++
level.trainingText(`<s>use <strong class='color-g'>wave</strong> to clear the room of mobs</s>`)
}
//spawn ammo if you run out
let isAmmo = false
for (let i = 0; i < powerUp.length; i++) {
if (powerUp[i].name === 'ammo') isAmmo = true
}
if (!isAmmo && b.inventory.length && b.guns[b.activeGun].ammo === 0) {
powerUps.directSpawn(1300, -2000, "ammo", false);
powerUps.directSpawn(1301, -2200, "ammo", false);
}
//exit room
ctx.fillStyle = "#f2f2f2"
ctx.fillRect(1600, -400, 400, 400)
level.enter.draw();
level.exit.drawAndCheck();
};
level.customTopLayer = () => {
if (mob.length > 0) {
door.isClosing = true
} else {
door.isClosing = false
}
door.openClose();
door.draw();
//exit room glow
ctx.fillStyle = "rgba(0,255,255,0.05)"
ctx.fillRect(1600, -400, 400, 400)
//ammo tunnel shadow
ctx.fillStyle = "rgba(0,0,0,0.2)"
// ctx.fillRect(1225, -2800, 125, 2450)
ctx.fillRect(-150, -2800, 1500, 2450);
};
if (m.health < 1) {
powerUps.directSpawn(1298, -3500, "heal", false, 23);
powerUps.directSpawn(1305, -3000, "heal", false, 35);
}
for (let i = 0; i < 6; i++) {
spawn.springer(i * 200, -800)
// Matter.Body.setVelocity(mob[mob.length - 1], { x: 0, y: 0 });
}
spawn.springer(1825, -330, 20);
spawn.mapRect(1175, -850, 50, 500); //upper room with mobs
spawn.mapRect(-225, -400, 1450, 50);
spawn.mapRect(-225, -2800, 1450, 2000);
spawn.mapRect(1350, -2800, 675, 2450);
spawn.mapRect(-2750, -2800, 2600, 4600); //left wall
spawn.mapRect(2000, -2800, 2600, 4600); //right wall
spawn.mapRect(-250, 50, 3500, 1750); //floor
spawn.mapRect(-200, 0, 950, 100);
spawn.mapRect(1575, 0, 500, 100);
spawn.mapRect(725, 12, 50, 25);
spawn.mapRect(725, 25, 75, 25);
spawn.mapRect(750, 38, 75, 25);
spawn.mapRect(1525, 25, 75, 50);
spawn.mapRect(1500, 38, 50, 25);
spawn.mapRect(1550, 12, 50, 25);
spawn.mapRect(1600, -400, 50, 225); //exit room left upper wall
},
missile() { //fire a missile to kill mobs and trigger button
level.difficultyIncrease(1) //difficulty on training mode resets to zero with each new level
level.setPosToSpawn(60, -50); //normal spawn
spawn.mapRect(10, -10, 100, 20); //small platform for player
level.exit.x = 1775;
level.exit.y = -35;
spawn.mapRect(level.exit.x, level.exit.y + 25, 100, 30); //exit bump
simulation.zoomScale = 1400 //1400 is normal
level.defaultZoom = 1400
simulation.zoomTransition(level.defaultZoom, 1)
document.body.style.backgroundColor = level.trainingBackgroundColor
b.removeAllGuns();
b.giveGuns("missiles")
// b.guns[b.activeGun].ammo = 0
// simulation.updateGunHUD();
const buttonDoor = level.button(2500, 50)
const door = level.door(1612.5, -175, 25, 190, 185, 3)
let instruction = 0
level.trainingText(`use <strong class='color-g'>missiles</strong> to drop a <strong class='color-block'>block</strong> on the button`)
level.custom = () => {
if (instruction === 0 && mob.length === 0) {
instruction++
level.trainingText(`<s>use <strong class='color-g'>missiles</strong> to drop a <strong class='color-block'>block</strong> on the button</s>`)
}
//spawn ammo if you run out
let isAmmo = false
for (let i = 0; i < powerUp.length; i++) {
if (powerUp[i].name === 'ammo') isAmmo = true
}
if (!isAmmo && b.inventory.length && b.guns[b.activeGun].ammo === 0) {
powerUps.directSpawn(1300, -2000, "ammo", false);
powerUps.directSpawn(1301, -2200, "ammo", false);
}
//exit room
ctx.fillStyle = "#f2f2f2"
ctx.fillRect(1600, -400, 400, 400)
level.enter.draw();
level.exit.drawAndCheck();
};
level.customTopLayer = () => {
buttonDoor.query();
buttonDoor.draw();
if (buttonDoor.isUp) {
door.isClosing = true
} else {
door.isClosing = false
}
door.openClose();
door.draw();
//exit room glow
ctx.fillStyle = "rgba(0,255,255,0.05)"
ctx.fillRect(1600, -400, 400, 400)
//tunnel shadow
ctx.fillStyle = "rgba(0,0,0,0.4)"
ctx.fillRect(1250, -2800, 100, 2200)
ctx.fillRect(1550, 25, 475, 25);
};
if (m.health < 1) {
powerUps.directSpawn(1298, -3500, "heal", false, 23);
powerUps.directSpawn(1305, -3000, "heal", false, 35);
}
for (let i = 0; i < 10; i++) {
spawn.springer(2100 + i * 100, -250)
// Matter.Body.setVelocity(mob[mob.length - 1], { x: 0, y: 0 });
}
spawn.mapRect(-2750, -2800, 2600, 4600); //left wall
// spawn.mapRect(2000, -2800, 2600, 4600); //right wall
spawn.mapRect(3050, -2800, 1550, 4600);
spawn.mapRect(-250, 50, 3500, 1750); //floor
spawn.mapRect(-200, 0, 950, 100);
spawn.mapRect(-150, -2800, 1400, 2200); //roof with tunnel for ammo
spawn.mapRect(1350, -2800, 675, 2200);
spawn.mapRect(725, 12, 50, 25);
spawn.mapRect(725, 25, 75, 25);
spawn.mapRect(750, 38, 75, 25);
// spawn.mapRect(1350, 0, 675, 30);
spawn.mapRect(1550, 0, 475, 35);
spawn.mapRect(1600, -400, 50, 225); //exit room left upper wall
spawn.mapRect(1600, -600, 425, 250);
spawn.mapRect(1975, -600, 50, 625);
spawn.mapRect(2025, -2800, 1075, 2450);
},
stack() { //stack blocks to get to exit
level.difficultyIncrease(1) //difficulty on training mode resets to zero with each new level
level.setPosToSpawn(60, -50); //normal spawn
spawn.mapRect(10, -10, 100, 20); //small platform for player
level.exit.x = 1775;
level.exit.y = -685;
spawn.mapRect(level.exit.x, level.exit.y + 25, 100, 100); //exit bump
simulation.zoomScale = 1400 //1400 is normal
level.defaultZoom = 1400
simulation.zoomTransition(level.defaultZoom, 1)
document.body.style.backgroundColor = level.trainingBackgroundColor
b.removeAllGuns();
let instruction = 0
level.trainingText(`use your <strong class='color-f'>field</strong> to stack the <strong class='color-block'>blocks</strong>`)
level.custom = () => {
if (instruction === 0 && m.pos.x > 1635) {
instruction++
level.trainingText(`<s>use your <strong class='color-f'>field</strong> to stack the <strong class='color-block'>blocks</strong></s>`)
}
//exit room
ctx.fillStyle = "#f2f2f2"
ctx.fillRect(1600, -1050, 400, 400)
level.enter.draw();
level.exit.drawAndCheck();
};
level.customTopLayer = () => {
//exit room glow
ctx.fillStyle = "rgba(0,255,255,0.05)"
ctx.fillRect(1600, -1050, 400, 400)
//ammo tunnel shadow
ctx.fillStyle = "rgba(0,0,0,0.4)"
ctx.fillRect(250, -2800, 200, 1800)
};
if (m.health < 1) {
powerUps.directSpawn(298, -3500, "heal", false, 23);
powerUps.directSpawn(305, -3000, "heal", false, 35);
}
for (let i = 0; i < 15; i++) {
spawn.bodyRect(280, -2000 - 500 * i, 30 + 80 * Math.random(), 30 + 80 * Math.random());
}
spawn.mapRect(-2750, -2800, 2600, 4600); //left wall
spawn.mapRect(2000, -2800, 2600, 4600); //right wall
spawn.mapRect(-250, 0, 3500, 1800); //floor
spawn.mapRect(1600, -650, 450, 775);
spawn.mapRect(-150, -2800, 400, 1800); //roof with tunnel for ammo
spawn.mapRect(450, -2800, 1675, 1800);
spawn.mapVertex(1300, 0, "400 0 -500 0 -300 -125 400 -125"); //base
},
mine() { //kill mobs and tack their bodies
level.difficultyIncrease(1) //difficulty on training mode resets to zero with each new level
level.setPosToSpawn(300, -50); //normal spawn
spawn.mapRect(250, -10, 100, 20); //small platform for player
level.exit.x = 1775;
level.exit.y = -685;
spawn.mapRect(level.exit.x, level.exit.y + 25, 100, 100); //exit bump
simulation.zoomScale = 1400 //1400 is normal
level.defaultZoom = 1400
simulation.zoomTransition(level.defaultZoom, 1)
document.body.style.backgroundColor = level.trainingBackgroundColor
b.removeAllGuns();
b.giveGuns("mine")
let instruction = 0
level.trainingText(`press the red <strong>button</strong> to spawn a <strong>mob</strong>`)
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(`<s>press the red <strong>button</strong> to spawn a <strong>mob</strong></s><br>turn the <strong>mobs</strong> into <strong class='color-block'>blocks</strong>`)
} else if (instruction === 1 && body.length > 2) {
instruction++
level.trainingText(`<s>press the red <strong>button</strong> to spawn a <strong>mob</strong><br>turn the <strong>mobs</strong> into <strong class='color-block'>blocks</strong></s><br>use your <strong class='color-f'>field</strong> to stack the <strong class='color-block'>blocks</strong>`)
} else if (instruction === 2 && m.pos.x > 1635) {
instruction++
level.trainingText(`<s>press the red <strong>button</strong> to spawn a <strong>mob</strong><br>turn the <strong>mobs</strong> into <strong class='color-block'>blocks</strong><br>use your <strong class='color-f'>field</strong> to stack the <strong class='color-block'>blocks</strong></s>`)
}
//spawn ammo if you run out
let isAmmo = false
for (let i = 0; i < powerUp.length; i++) {
if (powerUp[i].name === 'ammo') isAmmo = true
}
if (!isAmmo && b.inventory.length && b.guns[b.activeGun].ammo === 0) {
powerUps.directSpawn(1300, -2000, "ammo", false);
powerUps.directSpawn(1301, -2200, "ammo", false);
}
//exit room
ctx.fillStyle = "#f2f2f2"
ctx.fillRect(1600, -1050, 400, 400)
level.enter.draw();
level.exit.drawAndCheck();
};
level.customTopLayer = () => {
button.query();
button.draw();
if (!button.isUp) {
if (button.isReady) {
button.isReady = false
spawn.exploder(335, -1700)
Matter.Body.setVelocity(mob[mob.length - 1], {
x: 0,
y: 20
});
ctx.fillStyle = "rgba(255,0,0,0.9)"
ctx.fillRect(550, -2800, 200, 1800)
}
} else {
button.isReady = true
}
//exit room glow
ctx.fillStyle = "rgba(0,255,255,0.05)"
ctx.fillRect(1600, -1050, 400, 400)
//ammo tunnel shadow
ctx.fillStyle = "rgba(0,0,0,0.4)"
ctx.fillRect(550, -2800, 200, 1800)
};
if (m.health < 1) {
powerUps.directSpawn(298, -3500, "heal", false, 23);
powerUps.directSpawn(305, -3000, "heal", false, 35);
}
spawn.mapRect(-2750, -2800, 2600, 4600); //left wall
spawn.mapRect(2000, -2800, 2600, 4600); //right wall
spawn.mapRect(-250, 0, 3500, 1800); //floor
spawn.mapRect(1600, -650, 450, 775);
spawn.mapRect(-150, -2800, 700, 1800); //roof with tunnel for ammo
spawn.mapRect(750, -2800, 1675, 1800);
spawn.mapVertex(1300, 0, "400 0 -600 0 -300 -125 400 -125"); //base
},
grenades() { //jump at the top of the elevator's path to go extra high
level.difficultyIncrease(1) //difficulty on training mode resets to zero with each new level
level.setPosToSpawn(0, -50); //normal spawn
spawn.mapRect(-50, -10, 100, 20); //small platform for player
level.exit.x = 1900;
level.exit.y = -2835;
spawn.mapRect(level.exit.x, level.exit.y + 25, 100, 100); //exit bump
simulation.zoomScale = 1400 //1400 is normal
level.defaultZoom = 1400
simulation.zoomTransition(level.defaultZoom, 1)
document.body.style.backgroundColor = level.trainingBackgroundColor
b.removeAllGuns();
b.giveGuns("grenades")
const elevator1 = level.elevator(550, -100, 180, 25, -840, 0.003, {
up: 0.05,
down: 0.2
}) // x, y, width, height, maxHeight, force = 0.003, friction = { up: 0.01, down: 0.2 }) {
elevator1.addConstraint();
const toggle1 = level.toggle(275, 0) //(x,y,isOn,isLockOn = true/false)
const elevator2 = level.elevator(1400, -950, 180, 25, -2400, 0.0025) // x, y, width, height, maxHeight, force = 0.003, friction = { up: 0.01, down: 0.2 }) {
elevator2.addConstraint();
const button2 = level.button(1000, -850)
let instruction = 0
level.trainingText(`flip the <strong>switch</strong> to turn on the <strong>elevator</strong>`)
level.custom = () => {
if (instruction === 0 && elevator1.isOn) {
instruction++
level.trainingText(`<s>flip the <strong>switch</strong> to turn on the <strong>elevator</strong></s>
<br>put a <strong class='color-block'>block</strong> on the <strong>button</strong> to active the <strong>elevator</strong>`)
} else if (instruction === 1 && elevator2.isOn) {
instruction++
level.trainingText(`<s>flip the <strong>switch</strong> to turn on the <strong>elevator</strong><br>put a <strong class='color-block'>block</strong> on the <strong>button</strong> to active the <strong>elevator</strong></s>
<br>hold <strong>jump</strong> before the <strong>elevator's</strong> <strong>apex</strong> to reach the <strong>exit</strong>`)
} else if (instruction === 2 && m.pos.x > 1635) {
instruction++
level.trainingText(`<s>flip the <strong>switch</strong> to turn on the <strong>elevator</strong><br>put a <strong class='color-block'>block</strong> on the <strong>button</strong> to active the <strong>elevator</strong><br>hold <strong>jump</strong> before the <strong>elevator's</strong> <strong>apex</strong> to reach the <strong>exit</strong></s>`)
}
//exit room
ctx.fillStyle = "#f2f2f2"
ctx.fillRect(1725, -3100, 375, 300);
level.enter.draw();
level.exit.drawAndCheck();
};
level.customTopLayer = () => {
toggle1.query();
if (!toggle1.isOn) {
if (elevator1.isOn) {
elevator1.isOn = false
elevator1.frictionAir = 0.2
elevator1.addConstraint();
}
} else if (!elevator1.isOn) {
elevator1.isOn = true
elevator1.isUp = false
elevator1.removeConstraint();
elevator1.frictionAir = 0.2 //elevator.isUp ? 0.01 : 0.2
}
if (elevator1.isOn) {
elevator1.move();
ctx.fillStyle = "#444"
} else {
ctx.fillStyle = "#aaa"
}
ctx.fillRect(640, -825, 1, 745)
button2.query();
button2.draw();
if (button2.isUp) {
if (elevator2.isOn) {
elevator2.isOn = false
elevator2.frictionAir = 0.2
elevator2.addConstraint();
}
} else if (!elevator2.isOn) {
elevator2.isOn = true
elevator2.isUp = false
elevator2.removeConstraint();
elevator2.frictionAir = 0.2 //elevator.isUp ? 0.01 : 0.2
}
if (elevator2.isOn) {
elevator2.move();
ctx.fillStyle = "#444"
} else {
ctx.fillStyle = "#aaa"
}
ctx.fillRect(1490, -2300, 1, 1375)
//exit room glow
ctx.fillStyle = "rgba(0,255,255,0.05)"
ctx.fillRect(1725, -3100, 375, 300);
//shadows
ctx.fillStyle = "rgba(0,0,0,0.05)"
ctx.fillRect(-150, -250, 300, 250);
let grd = ctx.createLinearGradient(0, -150, 0, -2300);
grd.addColorStop(0, "rgba(0,0,0,0.35)");
grd.addColorStop(1, "rgba(0,0,0,0)");
ctx.fillStyle = grd //"rgba(0,0,100,0.01)"
ctx.fillRect(-200, -2300, 1825, 2300);
};
if (m.health < 1) {
powerUps.directSpawn(298, -3500, "heal", false, 23);
powerUps.directSpawn(305, -3000, "heal", false, 35);
}
spawn.mapRect(-2750, -4800, 2600, 6600); //left wall
spawn.mapRect(1600, -2800, 3000, 4600); //right wall
spawn.mapRect(-150, -4800, 300, 4550);
spawn.mapRect(2125, -4775, 2475, 2050);
spawn.mapRect(-250, 0, 3500, 1800); //floor
spawn.mapRect(750, -850, 950, 950);
spawn.mapRect(125, -275, 25, 100);
spawn.mapRect(2100, -3150, 50, 350);
spawn.mapRect(1725, -3150, 50, 175);
spawn.mapRect(1725, -3150, 425, 50);
spawn.nodeGroup(1200, -1500, "grenadier", 7);
},
harpoon() { //jump at the top of the elevator's path to go extra high
level.difficultyIncrease(1) //difficulty on training mode resets to zero with each new level
level.setPosToSpawn(0, -50); //normal spawn
spawn.mapRect(-50, -10, 100, 20); //small platform for player
level.exit.x = 1900;
level.exit.y = -2835;
spawn.mapRect(level.exit.x, level.exit.y + 25, 100, 100); //exit bump
simulation.zoomScale = 1400 //1400 is normal
level.defaultZoom = 1400
simulation.zoomTransition(level.defaultZoom, 1)
document.body.style.backgroundColor = level.trainingBackgroundColor
b.removeAllGuns();
b.giveGuns("harpoon")
let instruction = 0
level.trainingText(`climb up to the exit`)
level.custom = () => {
if (instruction === 0 && m.pos.x > 1635) {
instruction++
level.trainingText(`<s>climb up to the exit</s>`)
}
//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(`<strong>diamagnetism</strong> by <span class='color-var'>Richard0820</span><br><strong>Don't get hit.</strong><br> 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 <strong class="key-input-train">${input.key.down.replace('Key', '').replace('Digit', '')}</strong> to crouch`)
level.custom = () => {
if (instruction === 0 && input.down) {
instruction++
level.trainingText(`<s>press <strong class="key-input-train">${input.key.down.replace('Key', '').replace('Digit', '')}</strong> to crouch</s>`)
}
//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
},
};