"use strict"; //collision groups // cat.player | cat.map | cat.body | cat.bullet | cat.powerUp | cat.mob | cat.mobBullet | cat.mobShield | cat.phased const cat = { player: 0x1, map: 0x10, body: 0x100, bullet: 0x1000, powerUp: 0x10000, mob: 0x100000, mobBullet: 0x1000000, mobShield: 0x10000000, phased: 0x100000000, } function shuffle(array) { var currentIndex = array.length, temporaryValue, randomIndex; // While there remain elements to shuffle... while (0 !== currentIndex) { // Pick a remaining element... randomIndex = Math.floor(Math.random() * currentIndex); currentIndex -= 1; // And swap it with the current element. temporaryValue = array[currentIndex]; array[currentIndex] = array[randomIndex]; array[randomIndex] = temporaryValue; } return array; } // shrink power up selection menu to find window height if (screen.height < 800) { document.getElementById("choose-grid").style.fontSize = "1em"; //1.3em is normal if (screen.height < 600) document.getElementById("choose-grid").style.fontSize = "0.8em"; //1.3em is normal } //********************************************************************** // check for URL parameters to load an experimental game //********************************************************************** //example https://landgreen.github.io/sidescroller/index.html? // &gun1=minigun&gun2=laser // &tech1=laser-bot&tech2=mass%20driver&tech3=overcharge&tech4=laser-bot&tech5=laser-bot&field=phase%20decoherence%20field&difficulty=2 //add ? to end of url then for each power up add // &gun1=name&gun2=name // &tech1=laser-bot&tech2=mass%20driver&tech3=overcharge&tech4=laser-bot&tech5=laser-bot // &field=phase%20decoherence%20field // &difficulty=2 //use %20 for spaces //difficulty is 0 easy, 1 normal, 2 hard, 4 why function getUrlVars() { let vars = {}; window.location.href.replace(/[?&]+([^=&]+)=([^&]*)/gi, function(m, k, v) { vars[k] = v; }); return vars; } window.addEventListener('load', (event) => { const set = getUrlVars() if (Object.keys(set).length !== 0) { openExperimentMenu(); //add experimental selections based on url for (const property in set) { set[property] = set[property].replace(/%20/g, " ") set[property] = set[property].replace(/%27/g, "'") set[property] = set[property].replace(/%CE%A8/g, "Ψ") if (property === "field") { let found = false let index for (let i = 0; i < m.fieldUpgrades.length; i++) { if (set[property] === m.fieldUpgrades[i].name) { index = i; found = true; break; } } if (found) build.choosePowerUp(document.getElementById(`field-${index}`), index, 'field') } if (property.substring(0, 3) === "gun") { let found = false let index for (let i = 0; i < b.guns.length; i++) { if (set[property] === b.guns[i].name) { index = i; found = true; break; } } if (found) build.choosePowerUp(document.getElementById(`gun-${index}`), index, 'gun') } if (property.substring(0, 4) === "tech") { for (let i = 0; i < tech.tech.length; i++) { if (set[property] === tech.tech[i].name) { build.choosePowerUp(document.getElementById(`tech-${i}`), i, 'tech', true) break; } } } if (property === "difficulty") { simulation.difficultyMode = Number(set[property]) document.getElementById("difficulty-select-experiment").value = Number(set[property]) } if (property === "level") { document.getElementById("starting-level").value = Number(set[property]) } if (property === "noPower") { document.getElementById("no-power-ups").checked = Number(set[property]) } } } }); //********************************************************************** //set up canvas //********************************************************************** var canvas = document.getElementById("canvas"); //using "const" causes problems in safari when an ID shares the same name. const ctx = canvas.getContext("2d"); document.body.style.backgroundColor = "#fff"; //disable pop up menu on right click document.oncontextmenu = function() { return false; } function setupCanvas() { canvas.width = window.innerWidth; canvas.height = window.innerHeight; canvas.width2 = canvas.width / 2; //precalculated because I use this often (in mouse look) canvas.height2 = canvas.height / 2; canvas.diagonal = Math.sqrt(canvas.width2 * canvas.width2 + canvas.height2 * canvas.height2); // ctx.font = "18px Arial"; // ctx.textAlign = "center"; ctx.font = "25px Arial"; ctx.lineJoin = "round"; ctx.lineCap = "round"; // ctx.lineCap='square'; simulation.setZoom(); } setupCanvas(); window.onresize = () => { setupCanvas(); }; //********************************************************************** // experimental build grid display and pause //********************************************************************** const build = { onLoadPowerUps() { const set = getUrlVars() if (Object.keys(set).length !== 0) { for (const property in set) { set[property] = set[property].replace(/%20/g, " ") if (property.substring(0, 3) === "gun") b.giveGuns(set[property]) if (property.substring(0, 3) === "tech") tech.giveTech(set[property]) if (property === "field") m.setField(set[property]) if (property === "difficulty") { simulation.difficultyMode = Number(set[property]) document.getElementById("difficulty-select").value = Number(set[property]) } if (property === "level") { level.levelsCleared += Number(set[property]); level.difficultyIncrease(Number(set[property]) * simulation.difficultyMode) //increase difficulty based on modes spawn.setSpawnList(); //picks a couple mobs types for a themed random mob spawns level.onLevel++ } } for (let i = 0; i < bullet.length; ++i) Matter.World.remove(engine.world, bullet[i]); bullet = []; //remove any bullets that might have spawned from tech if (b.inventory.length > 0) { b.activeGun = b.inventory[0] //set first gun to active gun simulation.makeGunHUD(); } } }, pauseGrid() { const harm = (1 - m.harmReduction()) * 100 let text = "" if (!simulation.isChoosing) text += `
PAUSED               press P to resume
` text += `
${simulation.isCheating? "lore disabled

": ""} damage increase: ${((tech.damageFromTech()-1)*100).toFixed(0)}%
harm reduction: ${harm.toFixed(harm > 90 ? 2 : 0)}%
fire delay decrease: ${((1-b.fireCD)*100).toFixed(b.fireCD < 0.1 ? 2 : 0)}%
duplication chance: ${(Math.min(1,tech.duplicationChance())*100).toFixed(0)}%

tech: ${tech.totalCount}   research: ${powerUps.research.count}
health: (${(m.health*100).toFixed(0)} / ${(m.maxHealth*100).toFixed(0)})   energy: (${(m.energy*100).toFixed(0)} / ${(m.maxEnergy*100).toFixed(0)})
position: (${player.position.x.toFixed(1)}, ${player.position.y.toFixed(1)})   velocity: (${player.velocity.x.toFixed(1)}, ${player.velocity.y.toFixed(1)})
mouse: (${simulation.mouseInGame.x.toFixed(1)}, ${simulation.mouseInGame.y.toFixed(1)})   mass: ${player.mass.toFixed(1)}

level: ${level.levels[level.onLevel]} (${level.difficultyText()})   ${m.cycle} cycles
${mob.length} mobs,   ${body.length} blocks,   ${bullet.length} bullets,   ${powerUp.length} power ups
damage difficulty scale: ${(b.dmgScale*100).toFixed(2) }%
harm difficulty scale: ${(simulation.dmgScale*100).toFixed(0)}%
heal difficulty scale: ${(simulation.healScale*100).toFixed(1)}%
copy build url
`; for (let i = 0, len = b.inventory.length; i < len; i++) { text += `
  ${b.guns[b.inventory[i]].name} - ${b.guns[b.inventory[i]].ammo}
${b.guns[b.inventory[i]].description}
` } let el = document.getElementById("pause-grid-left") el.style.display = "grid" el.innerHTML = text text = ""; text += `
  ${m.fieldUpgrades[m.fieldMode].name}
${m.fieldUpgrades[m.fieldMode].description}
` let countTech = 0 for (let i = 0, len = tech.tech.length; i < len; i++) { if (tech.tech[i].count > 0) { const isCount = tech.tech[i].count > 1 ? `(${tech.tech[i].count}x)` : ""; if (tech.tech[i].isFieldTech) { text += `
        ${tech.tech[i].name} ${isCount}
${tech.tech[i].description}
` } else if (tech.tech[i].isGunTech) { text += `
        ${tech.tech[i].name} ${isCount}
${tech.tech[i].description}
` } else if (tech.tech[i].isLore) { text += `
  ${tech.tech[i].name} ${isCount}
${tech.tech[i].description}
` } else if (tech.tech[i].isJunk) { text += `
  ${tech.tech[i].name} ${isCount}
${tech.tech[i].description}
` } else { text += `
  ${tech.tech[i].name} ${isCount}
${tech.tech[i].description}
` } countTech++ } } el = document.getElementById("pause-grid-right") el.style.display = "grid" el.innerHTML = text if (countTech > 5 || b.inventory.length > 6) { document.body.style.overflowY = "scroll"; document.body.style.overflowX = "hidden"; } }, unPauseGrid() { document.body.style.overflow = "hidden" document.getElementById("pause-grid-left").style.display = "none" document.getElementById("pause-grid-right").style.display = "none" window.scrollTo(0, 0); }, isExperimentSelection: true, choosePowerUp(who, index, type, isAllowed = false) { if (type === "gun") { let isDeselect = false for (let i = 0, len = b.inventory.length; i < len; i++) { //look for selection in inventory if (b.guns[b.inventory[i]].name === b.guns[index].name) { //if already clicked, remove gun isDeselect = true who.classList.remove("build-gun-selected"); //remove gun b.inventory.splice(i, 1) b.guns[index].count = 0; b.guns[index].have = false; if (b.guns[index].ammo != Infinity) b.guns[index].ammo = 0; if (b.inventory.length === 0) b.activeGun = null; simulation.makeGunHUD(); break } } if (!isDeselect) { //add gun who.classList.add("build-gun-selected"); b.giveGuns(index) } } else if (type === "field") { if (m.fieldMode !== index) { document.getElementById("field-" + m.fieldMode).classList.remove("build-field-selected"); m.setField(index) who.classList.add("build-field-selected"); } } else if (type === "tech") { //remove tech if you have too many if (tech.tech[index].count < tech.tech[index].maxCount) { if (!who.classList.contains("build-tech-selected")) who.classList.add("build-tech-selected"); tech.giveTech(index) } else { tech.removeTech(index); who.classList.remove("build-tech-selected"); } } //update tech text //disable not allowed tech for (let i = 0, len = tech.tech.length; i < len; i++) { const techID = document.getElementById("tech-" + i) if (!tech.tech[i].isExperimentHide) { if (tech.tech[i].allowed() || isAllowed || tech.tech[i].count > 0) { const isCount = tech.tech[i].count > 1 ? `(${tech.tech[i].count}x)` : ""; if (tech.tech[i].isFieldTech) { techID.innerHTML = `
        ${tech.tech[i].name} ${isCount}
${tech.tech[i].description}` //
//
// border: #fff solid 0px; } else if (tech.tech[i].isGunTech) { techID.innerHTML = `
        ${tech.tech[i].name} ${isCount}
${tech.tech[i].description}` } else if (tech.tech[i].isJunk) { // text += `
  ${tech.tech[i].name} ${isCount}
${tech.tech[i].description}
` techID.innerHTML = `
  ${tech.tech[i].name} ${isCount}
${tech.tech[i].description}` } else if (tech.tech[i].isExperimentalMode) { techID.innerHTML = `${tech.tech[i].description}` } else { techID.innerHTML = `
  ${tech.tech[i].name} ${isCount}
${tech.tech[i].description}` } if (techID.classList.contains("experiment-grid-disabled")) { techID.classList.remove("experiment-grid-disabled"); techID.setAttribute("onClick", `javascript: build.choosePowerUp(this,${i},'tech')`); } } else { techID.innerHTML = `
${tech.tech[i].name}
requires: ${tech.tech[i].requires}` if (!techID.classList.contains("experiment-grid-disabled")) { techID.classList.add("experiment-grid-disabled"); techID.onclick = null } if (tech.tech[i].count > 0) tech.removeTech(i) if (techID.classList.contains("build-tech-selected")) techID.classList.remove("build-tech-selected"); } } } }, populateGrid() { let text = `
start reset share
starting level:
` for (let i = 0, len = m.fieldUpgrades.length; i < len; i++) { text += `
  ${m.fieldUpgrades[i].name}
${m.fieldUpgrades[i].description}
` } for (let i = 0, len = b.guns.length; i < len; i++) { text += `
  ${b.guns[i].name}
${b.guns[i].description}
` } for (let i = 0, len = tech.tech.length; i < len; i++) { if (!tech.tech[i].isExperimentHide) { if (tech.tech[i].allowed()) { // || tech.tech[i].name === "+1 cardinality") { //|| tech.tech[i].name === "leveraged investment" if (tech.tech[i].isExperimentalMode) { text += `
${tech.tech[i].description}
` } else { text += `
  ${tech.tech[i].name}
${tech.tech[i].description}
` } } else { text += `
${tech.tech[i].name}
requires: ${tech.tech[i].requires}
` } } } document.getElementById("experiment-grid").innerHTML = text document.getElementById("difficulty-select-experiment").value = document.getElementById("difficulty-select").value document.getElementById("difficulty-select-experiment").addEventListener("input", () => { simulation.difficultyMode = Number(document.getElementById("difficulty-select-experiment").value) localSettings.difficultyMode = Number(document.getElementById("difficulty-select-experiment").value) document.getElementById("difficulty-select").value = document.getElementById("difficulty-select-experiment").value localStorage.setItem("localSettings", JSON.stringify(localSettings)); //update local storage }); }, reset() { build.isExperimentSelection = true; m.setField(0) b.inventory = []; //removes guns and ammo for (let i = 0, len = b.guns.length; i < len; ++i) { b.guns[i].count = 0; b.guns[i].have = false; if (b.guns[i].ammo != Infinity) b.guns[i].ammo = 0; } b.activeGun = null; simulation.makeGunHUD(); tech.setupAllTech(); build.populateGrid(); document.getElementById("field-0").classList.add("build-field-selected"); document.getElementById("experiment-grid").style.display = "grid" }, shareURL(isCustom = false) { let url = "https://landgreen.github.io/sidescroller/index.html?" let count = 0; for (let i = 0; i < b.inventory.length; i++) { if (b.guns[b.inventory[i]].have) { url += `&gun${count}=${encodeURIComponent(b.guns[b.inventory[i]].name.trim())}` count++ } } count = 0; for (let i = 0; i < tech.tech.length; i++) { for (let j = 0; j < tech.tech[i].count; j++) { url += `&tech${count}=${encodeURIComponent(tech.tech[i].name.trim())}` count++ } } url += `&field=${encodeURIComponent(m.fieldUpgrades[m.fieldMode].name.trim())}` url += `&difficulty=${simulation.difficultyMode}` if (isCustom) { url += `&level=${Math.abs(Number(document.getElementById("starting-level").value))}` url += `&noPower=${Number(document.getElementById("no-power-ups").checked)}` alert('n-gon build URL copied to clipboard.\nPaste into browser address bar.') } else { simulation.makeTextLog("n-gon build URL copied to clipboard.
Paste into browser address bar.") } console.log('n-gon build URL copied to clipboard.\nPaste into browser address bar.') console.log(url) simulation.copyToClipBoard(url) }, startExperiment() { build.isExperimentSelection = false; spawn.setSpawnList(); //gives random mobs, not starter mobs spawn.setSpawnList(); if (b.inventory.length > 0) { b.activeGun = b.inventory[0] //set first gun to active gun simulation.makeGunHUD(); } for (let i = 0; i < bullet.length; ++i) Matter.World.remove(engine.world, bullet[i]); bullet = []; //remove any bullets that might have spawned from tech const levelsCleared = Math.abs(Number(document.getElementById("starting-level").value)) level.difficultyIncrease(Math.min(99, levelsCleared * simulation.difficultyMode)) //increase difficulty based on modes level.levelsCleared += levelsCleared; simulation.isNoPowerUps = document.getElementById("no-power-ups").checked if (simulation.isNoPowerUps) { //remove tech, guns, and fields function removeOne() { //recursive remove one at a time to avoid array problems for (let i = 0; i < powerUp.length; i++) { if (powerUp[i].name === "tech" || powerUp[i].name === "gun" || powerUp[i].name === "field") { Matter.World.remove(engine.world, powerUp[i]); powerUp.splice(i, 1); removeOne(); break } } } removeOne(); } simulation.isCheating = true; tech.removeLoreTechFromPool(); document.body.style.cursor = "none"; document.body.style.overflow = "hidden" document.getElementById("experiment-grid").style.display = "none" simulation.paused = false; requestAnimationFrame(cycle); } } function openExperimentMenu() { document.getElementById("experiment-button").style.display = "none"; const el = document.getElementById("experiment-grid") el.style.display = "grid" document.body.style.overflowY = "scroll"; document.body.style.overflowX = "hidden"; document.getElementById("info").style.display = 'none' simulation.startGame(true); //starts game, but pauses it build.isExperimentSelection = true; simulation.paused = true; build.reset(); } //record settings so they can be reproduced in the experimental menu document.getElementById("experiment-button").addEventListener("click", () => { //setup build run let field = 0; let inventory = []; let techList = []; if (!simulation.firstRun) { field = m.fieldMode inventory = [...b.inventory] for (let i = 0; i < tech.tech.length; i++) { techList.push(tech.tech[i].count) } } openExperimentMenu(); }); // ************************************************************************************************ // inputs // ************************************************************************************************ const input = { fire: false, // left mouse field: false, // right mouse up: false, // jump down: false, // crouch left: false, right: false, isPauseKeyReady: true, key: { // fire: "ShiftLeft", field: "Space", up: "KeyW", // jump down: "KeyS", // crouch left: "KeyA", right: "KeyD", pause: "KeyP", nextGun: "KeyE", previousGun: "KeyQ", testing: "KeyT" }, setDefault() { input.key = { // fire: "ShiftLeft", field: "Space", up: "KeyW", // jump down: "KeyS", // crouch left: "KeyA", right: "KeyD", pause: "KeyP", nextGun: "KeyE", previousGun: "KeyQ", testing: "KeyT" } input.controlTextUpdate() }, controlTextUpdate() { function cleanText(text) { return text.replace('Key', '').replace('Digit', '') } document.getElementById("key-field").innerHTML = cleanText(input.key.field) document.getElementById("key-up").innerHTML = cleanText(input.key.up) document.getElementById("key-down").innerHTML = cleanText(input.key.down) document.getElementById("key-left").innerHTML = cleanText(input.key.left) document.getElementById("key-right").innerHTML = cleanText(input.key.right) document.getElementById("key-pause").innerHTML = cleanText(input.key.pause) document.getElementById("key-next-gun").innerHTML = cleanText(input.key.nextGun) document.getElementById("key-previous-gun").innerHTML = cleanText(input.key.previousGun) document.getElementById("key-testing").innerHTML = cleanText(input.key.testing) //if (localSettings.loreCount > 0) document.getElementById("splash-up").innerHTML = cleanText(input.key.up)[0] document.getElementById("splash-down").innerHTML = cleanText(input.key.down)[0] document.getElementById("splash-left").innerHTML = cleanText(input.key.left)[0] document.getElementById("splash-right").innerHTML = cleanText(input.key.right)[0] document.getElementById("splash-next-gun").innerHTML = cleanText(input.key.nextGun)[0] document.getElementById("splash-previous-gun").innerHTML = cleanText(input.key.previousGun)[0] localSettings.key = input.key localStorage.setItem("localSettings", JSON.stringify(localSettings)); //update local storage }, focus: null, setTextFocus() { const backgroundColor = "#fff" document.getElementById("key-field").style.background = backgroundColor document.getElementById("key-up").style.background = backgroundColor document.getElementById("key-down").style.background = backgroundColor document.getElementById("key-left").style.background = backgroundColor document.getElementById("key-right").style.background = backgroundColor document.getElementById("key-pause").style.background = backgroundColor document.getElementById("key-next-gun").style.background = backgroundColor document.getElementById("key-previous-gun").style.background = backgroundColor document.getElementById("key-testing").style.background = backgroundColor if (input.focus) input.focus.style.background = 'rgb(0, 200, 255)'; }, setKeys(event) { //check for duplicate keys if (event.code && !( event.code === "ArrowRight" || event.code === "ArrowLeft" || event.code === "ArrowUp" || event.code === "ArrowDown" || event.code === input.key.field || event.code === input.key.up || event.code === input.key.down || event.code === input.key.left || event.code === input.key.right || event.code === input.key.pause || event.code === input.key.nextGun || event.code === input.key.previousGun || event.code === input.key.testing )) { switch (input.focus.id) { case "key-field": input.key.field = event.code break; case "key-up": input.key.up = event.code break; case "key-down": input.key.down = event.code break; case "key-left": input.key.left = event.code break; case "key-right": input.key.right = event.code break; case "key-pause": input.key.pause = event.code break; case "key-next-gun": input.key.nextGun = event.code break; case "key-previous-gun": input.key.previousGun = event.code break; case "key-testing": input.key.testing = event.code break; } } input.controlTextUpdate() input.endKeySensing() }, endKeySensing() { window.removeEventListener("keydown", input.setKeys); input.focus = null input.setTextFocus() } } document.getElementById("control-table").addEventListener('click', (event) => { if (event.target.className === 'key-input') { input.focus = event.target input.setTextFocus() window.addEventListener("keydown", input.setKeys); } }); document.getElementById("control-details").addEventListener("toggle", function() { input.controlTextUpdate() input.endKeySensing(); }) document.getElementById("control-reset").addEventListener('click', input.setDefault); window.addEventListener("keyup", function(event) { switch (event.code) { case input.key.right: case "ArrowRight": input.right = false break; case input.key.left: case "ArrowLeft": input.left = false break; case input.key.up: case "ArrowUp": input.up = false break; case input.key.down: case "ArrowDown": input.down = false break; case input.key.field: input.field = false break } }); window.addEventListener("keydown", function(event) { switch (event.code) { case input.key.right: case "ArrowRight": input.right = true break; case input.key.left: case "ArrowLeft": input.left = true break; case input.key.up: case "ArrowUp": input.up = true break; case input.key.down: case "ArrowDown": input.down = true break; case input.key.field: event.preventDefault(); input.field = true break case input.key.nextGun: simulation.nextGun(); break case input.key.previousGun: simulation.previousGun(); break case input.key.pause: if (!simulation.isChoosing && input.isPauseKeyReady && m.alive) { input.isPauseKeyReady = false setTimeout(function() { input.isPauseKeyReady = true }, 300); if (simulation.paused) { build.unPauseGrid() simulation.paused = false; // level.levelAnnounce(); document.body.style.cursor = "none"; requestAnimationFrame(cycle); } else { simulation.paused = true; build.pauseGrid() document.body.style.cursor = "auto"; if (tech.isGunSwitchField || simulation.testing) { document.getElementById("pause-field").addEventListener("click", () => { const energy = m.energy m.setField((m.fieldMode === m.fieldUpgrades.length - 1) ? 1 : m.fieldMode + 1) //cycle to next field m.energy = energy document.getElementById("pause-field").innerHTML = `
  ${m.fieldUpgrades[m.fieldMode].name}
${m.fieldUpgrades[m.fieldMode].description}` }); } if (simulation.testing) { } } } break case input.key.testing: if (m.alive && localSettings.loreCount > 0) { if (simulation.testing) { simulation.testing = false; simulation.loop = simulation.normalLoop if (simulation.isConstructionMode) document.getElementById("construct").style.display = 'none' simulation.makeTextLog("", 0); } else { //if (keys[191]) simulation.testing = true; simulation.loop = simulation.testingLoop if (simulation.isConstructionMode) document.getElementById("construct").style.display = 'inline' if (!simulation.isCheating) { simulation.isCheating = true; tech.removeLoreTechFromPool(); } simulation.makeTextLog( `
T toggle testing
R teleport to mouse
F cycle field
G all guns
H brief harm immunity
N fill health, energy
Y random tech
U next level
J clear mobs
I/O zoom in / out
1-8 spawn things
⇧X restart
`, Infinity); } } break } if (simulation.testing) { if (event.key === "X") m.death(); //only uppercase switch (event.key.toLowerCase()) { case "o": simulation.isAutoZoom = false; simulation.zoomScale /= 0.9; simulation.setZoom(); break; case "i": simulation.isAutoZoom = false; simulation.zoomScale *= 0.9; simulation.setZoom(); break case "`": powerUps.directSpawn(simulation.mouseInGame.x, simulation.mouseInGame.y, "research"); break case "1": powerUps.directSpawn(simulation.mouseInGame.x, simulation.mouseInGame.y, "heal"); break case "2": powerUps.directSpawn(simulation.mouseInGame.x, simulation.mouseInGame.y, "ammo"); break case "3": powerUps.directSpawn(simulation.mouseInGame.x, simulation.mouseInGame.y, "gun"); break case "4": powerUps.directSpawn(simulation.mouseInGame.x, simulation.mouseInGame.y, "field"); break case "5": powerUps.directSpawn(simulation.mouseInGame.x, simulation.mouseInGame.y, "tech"); break case "6": const index = body.length spawn.bodyRect(simulation.mouseInGame.x, simulation.mouseInGame.y, 50, 50); body[index].collisionFilter.category = cat.body; body[index].collisionFilter.mask = cat.player | cat.map | cat.body | cat.bullet | cat.mob | cat.mobBullet body[index].classType = "body"; World.add(engine.world, body[index]); //add to world break case "7": const pick = spawn.fullPickList[Math.floor(Math.random() * spawn.fullPickList.length)]; spawn[pick](simulation.mouseInGame.x, simulation.mouseInGame.y); break case "8": spawn.randomLevelBoss(simulation.mouseInGame.x, simulation.mouseInGame.y); break case "f": const mode = (m.fieldMode === m.fieldUpgrades.length - 1) ? 0 : m.fieldMode + 1 m.setField(mode) break case "g": b.giveGuns("all", 1000) break case "h": // m.health = Infinity m.immuneCycle = Infinity // m.energy = Infinity // document.getElementById("health").style.display = "none" // document.getElementById("health-bg").style.display = "none" break case "n": m.addHealth(Infinity) m.energy = m.maxEnergy break case "y": tech.giveTech() break case "r": m.resetHistory(); Matter.Body.setPosition(player, simulation.mouseInGame); Matter.Body.setVelocity(player, { x: 0, y: 0 }); // move bots to player 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 }); } } break case "u": level.nextLevel(); break case "j": for (let i = 0, len = mob.length; i < len; ++i) mob[i].damage(Infinity, true) setTimeout(() => { for (let i = 0, len = mob.length; i < len; ++i) mob[i].damage(Infinity, true) }, 100); setTimeout(() => { for (let i = 0, len = mob.length; i < len; ++i) mob[i].damage(Infinity, true) }, 200); break } } }); //mouse move input document.body.addEventListener("mousemove", (e) => { simulation.mouse.x = e.clientX; simulation.mouse.y = e.clientY; }); document.body.addEventListener("mouseup", (e) => { // input.fire = false; // console.log(e) if (e.which === 3) { input.field = false; } else { input.fire = false; } }); document.body.addEventListener("mousedown", (e) => { if (e.which === 3) { input.field = true; } else { input.fire = true; } }); document.body.addEventListener("mouseenter", (e) => { //prevents mouse getting stuck when leaving the window if (e.button === 1) { input.fire = true; } else { input.fire = false; } if (e.button === 3) { input.field = true; } else { input.field = false; } }); document.body.addEventListener("mouseleave", (e) => { //prevents mouse getting stuck when leaving the window if (e.button === 1) { input.fire = true; } else { input.fire = false; } if (e.button === 3) { input.field = true; } else { input.field = false; } }); document.body.addEventListener("wheel", (e) => { if (!simulation.paused) { if (e.deltaY > 0) { simulation.nextGun(); } else { simulation.previousGun(); } } }, { passive: true }); //********************************************************************** // local storage //********************************************************************** let localSettings = JSON.parse(localStorage.getItem("localSettings")); if (localSettings) { if (localSettings.key) { input.key = localSettings.key } else { input.setDefault() } if (localSettings.loreCount === undefined) { localSettings.loreCount = 0 localStorage.setItem("localSettings", JSON.stringify(localSettings)); //update local storage } simulation.isCommunityMaps = localSettings.isCommunityMaps document.getElementById("community-maps").checked = localSettings.isCommunityMaps simulation.difficultyMode = localSettings.difficultyMode document.getElementById("difficulty-select").value = localSettings.difficultyMode if (localSettings.fpsCapDefault === 'max') { simulation.fpsCapDefault = 999999999; } else { simulation.fpsCapDefault = Number(localSettings.fpsCapDefault) } document.getElementById("fps-select").value = localSettings.fpsCapDefault } else { localSettings = { isCommunityMaps: false, difficultyMode: '2', fpsCapDefault: 'max', runCount: 0, levelsClearedLastGame: 0, loreCount: 0, key: undefined }; input.setDefault() localStorage.setItem("localSettings", JSON.stringify(localSettings)); //update local storage document.getElementById("community-maps").checked = localSettings.isCommunityMaps simulation.isCommunityMaps = localSettings.isCommunityMaps document.getElementById("difficulty-select").value = localSettings.difficultyMode document.getElementById("fps-select").value = localSettings.fpsCapDefault } document.getElementById("control-testing").style.visibility = (localSettings.loreCount === 0) ? "hidden" : "visible" // document.getElementById("experiment-button").style.visibility = (localSettings.loreCount === 0) ? "hidden" : "visible" input.controlTextUpdate() //********************************************************************** // settings //********************************************************************** document.getElementById("fps-select").addEventListener("input", () => { let value = document.getElementById("fps-select").value if (value === 'max') { simulation.fpsCapDefault = 999999999; } else { simulation.fpsCapDefault = Number(value) } localSettings.fpsCapDefault = value localStorage.setItem("localSettings", JSON.stringify(localSettings)); //update local storage }); document.getElementById("community-maps").addEventListener("input", () => { simulation.isCommunityMaps = document.getElementById("community-maps").checked localSettings.isCommunityMaps = simulation.isCommunityMaps localStorage.setItem("localSettings", JSON.stringify(localSettings)); //update local storage }); // difficulty-select-experiment event listener is set in build.makeGrid document.getElementById("difficulty-select").addEventListener("input", () => { simulation.difficultyMode = Number(document.getElementById("difficulty-select").value) localSettings.difficultyMode = simulation.difficultyMode localSettings.levelsClearedLastGame = 0 //after changing difficulty, reset run history localStorage.setItem("localSettings", JSON.stringify(localSettings)); //update local storage }); document.getElementById("updates").addEventListener("toggle", function() { function loadJSON(path, success, error) { //generic function to get JSON var xhr = new XMLHttpRequest(); xhr.onreadystatechange = function() { if (xhr.readyState === XMLHttpRequest.DONE) { if (xhr.status === 200) { if (success) success(JSON.parse(xhr.responseText)); } else { if (error) error(xhr); } } }; xhr.open("GET", path, true); xhr.send(); } let text = `n-gon: todo list and complete change-log
` document.getElementById("updates-div").innerHTML = text /// https://api.github.com/repos/landgreen/n-gon/stats/commit_activity loadJSON('https://api.github.com/repos/landgreen/n-gon/commits', function(data) { // console.log(data) for (let i = 0, len = 3; i < len; i++) { text += "" + data[i].commit.author.date.substr(0, 10) + " - "; //+ "
" text += data[i].commit.message if (i < len - 1) text += "
" } document.getElementById("updates-div").innerHTML = text.replace(/\n/g, "
") }, function(xhr) { console.error(xhr); } ); }) //********************************************************************** // main loop //********************************************************************** simulation.loop = simulation.normalLoop; function cycle() { if (!simulation.paused) requestAnimationFrame(cycle); const now = Date.now(); const elapsed = now - simulation.then; // calc elapsed time since last loop if (elapsed > simulation.fpsInterval) { // if enough time has elapsed, draw the next frame simulation.then = now - (elapsed % simulation.fpsInterval); // Get ready for next frame by setting then=now. Also, adjust for fpsInterval not being multiple of 16.67 simulation.cycle++; //tracks game cycles m.cycle++; //tracks player cycles //used to alow time to stop for everything, but the player if (simulation.clearNow) { simulation.clearNow = false; simulation.clearMap(); level.start(); } simulation.loop(); // if (isNaN(m.health) || isNaN(m.energy)) { // console.log(`m.health = ${m.health}`) // simulation.paused = true; // build.pauseGrid() // document.body.style.cursor = "auto"; // alert("health is NaN, please report this bug to the discord \n https://discordapp.com/invite/2eC9pgJ") // } // for (let i = 0, len = loop.length; i < len; i++) { // loop[i]() // } } }