"use strict"; //convert text into numbers for seed Math.hash = s => { for (var i = 0, h = 9; i < s.length;) h = Math.imul(h ^ s.charCodeAt(i++), 9 ** 9); return h ^ h >>> 9 } // const date1 = new Date() // console.log(date1.getUTCHours()) // document.getElementById("seed").placeholder = Math.initialSeed = String(date1.getUTCDate() * date1.getUTCFullYear()) // daily seed, day + year // document.getElementById("seed").placeholder = Math.initialSeed = Math.floor(Date.now() % 100000) //random every time: just the time in milliseconds UTC document.getElementById("seed").placeholder = Math.initialSeed = String(Math.floor(Date.now() % 100000)) Math.seed = Math.abs(Math.hash(Math.initialSeed)) //update randomizer seed in case the player changed it Math.seededRandom = function (min = 0, max = 1) { // in order to work 'Math.seed' must NOT be undefined Math.seed = (Math.seed * 9301 + 49297) % 233280; return min + Math.seed / 233280 * (max - min); } //Math.seed is set to document.getElementById("seed").value in level.populate level at the start of runs // console.log(Math.seed) 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); randomIndex = Math.floor(Math.seededRandom(0, currentIndex)) //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; } //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, } let color = { //light // background: "#ddd", // used instead: document.body.style.backgroundColor block: "rgba(140,140,140,0.85)", blockS: "#222", map: "#444", bullet: "#000" } // const color = { //dark // background: "#333", // block: "#444", // blockS: "#aab", // map: "#556", // bullet: "#fff" // } // const color = { //dark // background: "#999", // block: "#888", // blockS: "#111", // map: "#444", // } // shrink power up selection menu // 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', () => { const set = getUrlVars() if (Object.keys(set).length !== 0) { // build.populateGrid() //trying to solve a bug with this, but maybe it doesn't help 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(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(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(i, 'tech', true) break; } } } if (property === "difficulty") { simulation.difficultyMode = Number(set[property]) lore.setTechGoal() document.getElementById("difficulty-select-experiment").value = Number(set[property]) } if (property === "molMode") { simulation.molecularMode = Number(set[property]) const i = 4 //update experiment text m.fieldUpgrades[i].description = m.fieldUpgrades[i].setDescription() document.getElementById(`field-${i}`).innerHTML = `
  ${build.nameLink(m.fieldUpgrades[i].name)}
${m.fieldUpgrades[i].description}
` } // if (property === "seed") { // document.getElementById("seed").placeholder = Math.initialSeed = String(set[property]) // Math.seed = Math.abs(Math.hash(Math.initialSeed)) // level.populateLevels() // } requestAnimationFrame(() => { build.sortTech('have', true) }); } } else if (localSettings.isTrainingNotAttempted && localSettings.runCount < 30) { //make training button more obvious for new players // document.getElementById("training-button").style.border = "0px #333 solid"; // document.getElementById("training-button").style.fill = "rgb(0, 150, 235)" //"#fff"; // document.getElementById("training-button").style.background = "rgb(0, 200, 255)"; //css classes not working for some reason // document.getElementById("training-button").classList.add('lore-text'); let myanim = document.createElementNS("http://www.w3.org/2000/svg", 'animate'); myanim.setAttribute("id", "myAnimation"); myanim.setAttribute("attributeType", "XML"); myanim.setAttribute("attributeName", "fill"); // myanim.setAttribute("values", "#f55;#cc5;#5c5;#5dd;#66f;#5dd;#5c5;#cc5;#f55"); //rainbow myanim.setAttribute("values", "#5dd;#66f;#5dd"); // myanim.setAttribute("values", "#333;rgb(0, 170, 255);#333"); myanim.setAttribute("dur", "3s"); myanim.setAttribute("repeatCount", "indefinite"); document.getElementById("training-button").appendChild(myanim); document.getElementById("myAnimation").beginElement(); } }); //********************************************************************** //set up canvas //********************************************************************** const canvas = document.getElementById("canvas"); //using "const" causes problems in safari when an ID shares the same name. const ctx = canvas.getContext("2d"); // const ctx = canvas.getContext('2d', { alpha: false }); //optimization, this works if you wipe with the background color of each level 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; ctx.font = "25px Arial"; ctx.lineJoin = "round"; ctx.lineCap = "round"; simulation.setZoom(); } setupCanvas(); window.onresize = () => { setupCanvas(); }; //********************************************************************** // experimental build grid display and pause //********************************************************************** //set wikipedia link for (let i = 0, len = tech.tech.length; i < len; i++) { if (!tech.tech[i].link) tech.tech[i].link = `${tech.tech[i].name}` } const build = { pixelDraw() { let count = 0 let imgData = ctx.getImageData(0, 0, canvas.width, canvas.height); let data = imgData.data; function loop() { count++ if (!(count % 2)) { for (let y = 0; y < canvas.height; ++y) { for (let x = 0; x < canvas.width; x += 1) { const index = (y * canvas.width + x) * 4; // let mag = 0; // for (let j = 0, len = who.length; j < len; j++) { // const dx = who[j].position.x - x; // const dy = who[j].position.y - y; // mag -= who[j].charge / (Math.sqrt(dx * dx + dy * dy) + 1); // } //get dark // data[index + 0] *= 0.96 // data[index + 1] *= 0.96 // data[index + 2] *= 0.96 // data[index + 3] -= 1; // alpha //invert data[index + 0] = 255 - data[index + 0] // red data[index + 1] = 255 - data[index + 1] // green data[index + 2] = 255 - data[index + 2] // blue } } // fade alpha for all pixels // for (let i = 0; i < data.length; i += 4) { // if (data[i + 3] > 0) { // data[i + 3]--; // } // } //add random speckles // for (let i = 0, len = Math.floor(data.length / 15000); i < len; ++i) { // const index = Math.floor((Math.random() * data.length) / 4) * 4; // data[index + 0] = 255; // red // data[index + 1] = 255; // green // data[index + 2] = 255; // blue // data[index + 3] = Math.floor(Math.random() * Math.random() * 155); // alpha // } // ctx.putImageData(imgData, 0, 1); //pixels fall because of the 1 in third parameter ctx.putImageData(imgData, 0, 0); } if (simulation.paused && m.alive) requestAnimationFrame(loop); } requestAnimationFrame(loop); }, showImages(from) { //on click event: from all 3 different places to hide / show images localSettings.isHideImages = !localSettings.isHideImages if (localSettings.isAllowed) localStorage.setItem("localSettings", JSON.stringify(localSettings)); //update local storage if (from === 'experiment') { build.reset(); } else if (from === 'pause') { build.unPauseGrid() build.pauseGrid() //redraw pause text with images } if (localSettings.isHideImages) { document.getElementById("choose-grid").classList.add('choose-grid-no-images'); document.getElementById("choose-grid").classList.remove('choose-grid'); } else { document.getElementById("choose-grid").classList.add('choose-grid'); document.getElementById("choose-grid").classList.remove('choose-grid-no-images'); } document.getElementById("hide-images").checked = localSettings.isHideImages // console.log(localSettings.isHideImages, from) }, hideHUD() { if (simulation.isTraining) { localSettings.isHideHUD = false } else { localSettings.isHideHUD = !localSettings.isHideHUD } if (localSettings.isAllowed) localStorage.setItem("localSettings", JSON.stringify(localSettings)); //update local storage document.getElementById("hide-hud").checked = localSettings.isHideHUD document.getElementById("hide-hud").classList.toggle("ticked") simulation.removeEphemera("dmgDefBars") if (!localSettings.isHideHUD) { simulation.ephemera.push({ name: "dmgDefBars", count: 0, do() { if (!(m.cycle % 15)) { //4 times a second const defense = m.defense() //update defense bar if (m.lastCalculatedDefense !== defense) { document.getElementById("defense-bar").style.width = Math.floor(300 * m.maxHealth * (1 - defense)) + "px"; m.lastCalculatedDefense = defense } const damage = tech.damageFromTech() //update damage bar if (m.lastCalculatedDamage !== damage) { document.getElementById("damage-bar").style.height = Math.floor((Math.atan(0.25 * damage - 0.25) + 0.25) * 0.53 * canvas.height) + "px"; m.lastCalculatedDamage = damage } } }, }) } }, pauseGrid() { // build.pixelDraw(); build.generatePauseLeft() //makes the left side of the pause menu with the tech build.generatePauseRight() //makes the right side of the pause menu with the tech document.getElementById("tech").style.display = "none" document.getElementById("guns").style.display = "none" document.getElementById("field").style.display = "none" 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" //show in game console // document.getElementById("text-log").style.display = "inline" simulation.lastLogTime = m.cycle //hide in game console }, generatePauseLeft() { //used for junk estimation let junkCount = 0 let totalCount = 1 //start at one to avoid NaN issues for (let i = 0; i < tech.tech.length; i++) { if (tech.tech[i].count < tech.tech[i].maxCount && tech.tech[i].allowed() && !tech.tech[i].isBanished) { totalCount += tech.tech[i].frequency if (tech.tech[i].isJunk) junkCount += tech.tech[i].frequency } } //left side let botText = "" if (tech.nailBotCount) botText += `
nail-bots: ${tech.nailBotCount}` if (tech.orbitBotCount) botText += `
orbital-bots: ${tech.orbitBotCount}` if (tech.boomBotCount) botText += `
boom-bots: ${tech.boomBotCount}` if (tech.laserBotCount) botText += `
laser-bots: ${tech.laserBotCount}` if (tech.foamBotCount) botText += `
foam-bots: ${tech.foamBotCount}` if (tech.soundBotCount) botText += `
sound-bots: ${tech.soundBotCount}` if (tech.dynamoBotCount) botText += `
dynamo-bots: ${tech.dynamoBotCount}` if (tech.plasmaBotCount) botText += `
plasma-bots: ${tech.plasmaBotCount}` if (tech.missileBotCount) botText += `
missile-bots: ${tech.missileBotCount}` let text = `
PAUSED press ${input.key.pause} to resume




damage: ${((tech.damageFromTech())).toPrecision(4)}     difficulty: ${((m.dmgScale)).toPrecision(4)}
defense: ${tech.isEnergyHealth ? (1 - Math.pow(m.defense(), 0.13)).toPrecision(5) : (1 - m.defense()).toPrecision(5)}     difficulty: ${(1 / simulation.dmgScale).toPrecision(4)}
fire rate: ${((1 - b.fireCDscale) * 100).toFixed(b.fireCDscale < 0.1 ? 2 : 0)}% ${tech.duplicationChance() ? `
duplication: ${(tech.duplicationChance() * 100).toFixed(0)}%` : ""} ${m.coupling ? `
` + m.couplingDescription(m.coupling) + ` from ${(m.coupling).toFixed(0)} ${powerUps.orb.coupling(1)}` : ""} ${botText}

health: (${(m.health * 100).toFixed(0)} / ${(m.maxHealth * 100).toFixed(0)}) mass: ${player.mass.toFixed(1)}
energy: (${(m.energy * 100).toFixed(0)} / ${(m.maxEnergy * 100).toFixed(0)}) + (${(m.fieldRegen * 6000).toFixed(0)}/s) position: (${player.position.x.toFixed(1)}, ${player.position.y.toFixed(1)})
gun: ${b.activeGun === null || b.activeGun === undefined ? "undefined" : b.guns[b.activeGun].name}   ammo: ${b.activeGun === null || b.activeGun === undefined ? "0" : b.guns[b.activeGun].ammo} mouse: (${simulation.mouseInGame.x.toFixed(1)}, ${simulation.mouseInGame.y.toFixed(1)})
tech: ${tech.totalCount}   research: ${powerUps.research.count} velocity: (${player.velocity.x.toFixed(3)}, ${player.velocity.y.toFixed(3)}) ${junkCount ? `
JUNK: ${(junkCount / totalCount * 100).toFixed(1)}% ` : ""}

level: ${level.levelsCleared} ${level.levels[level.onLevel]} (${level.difficultyText()})
seed: ${Math.initialSeed}   ${m.cycle} cycles
mobs: ${mob.length}   blocks: ${body.length}   bullets: ${bullet.length}   power ups: ${powerUp.length} ${simulation.isCheating ? "

lore disabled" : ""}
`; // deaths: ${mobs.mobDeaths}   if (tech.isPauseSwitchField && !simulation.isChoosing) { const style = localSettings.isHideImages ? `style="height:auto;"` : `style="background-image: url('img/field/${m.fieldUpgrades[m.fieldMode].name}${m.fieldMode === 0 ? m.fieldUpgrades[0].imageNumber : ""}.webp');"` text += `
  ${build.nameLink(m.fieldUpgrades[m.fieldMode].name)}
${m.fieldUpgrades[m.fieldMode].description}
` } else { const style = localSettings.isHideImages ? `style="height:auto;"` : `style="background-image: url('img/field/${m.fieldUpgrades[m.fieldMode].name}${m.fieldMode === 0 ? m.fieldUpgrades[0].imageNumber : ""}.webp');"` text += `
  ${build.nameLink(m.fieldUpgrades[m.fieldMode].name)}
${m.fieldUpgrades[m.fieldMode].description}
` } // for (let i = 0, len = b.inventory.length; i < len; i++) { // text += `
  ${build.nameLink(b.guns[b.inventory[i]].name)} - ${b.guns[b.inventory[i]].ammo}
${b.guns[b.inventory[i]].description}
` // } for (let i = 0, len = b.inventory.length; i < len; i++) { const style = localSettings.isHideImages ? `style="height:auto;"` : `style="background-image: url('img/gun/${b.guns[b.inventory[i]].name}.webp');"` text += `
  ${build.nameLink(b.guns[b.inventory[i]].name)} - ${b.guns[b.inventory[i]].ammo}
${b.guns[b.inventory[i]].description}
` } if (!localSettings.isHideHUD) text += `
${document.getElementById("text-log").innerHTML}
` //show last in game console message let el = document.getElementById("pause-grid-left") el.style.display = "grid" el.innerHTML = text }, generatePauseRight() { //right side // // // // // let text = `
`; // const style = (tech.isPauseEjectTech && !simulation.isChoosing) ? 'style="animation: techColorCycle 1s linear infinite alternate;"' : '' const ejectClass = (tech.isPauseEjectTech && !simulation.isChoosing) ? 'pause-eject' : '' for (let i = 0, len = tech.tech.length; i < len; i++) { if (tech.tech[i].count > 0) { // const techCountText = tech.tech[i].count > 1 ? `(${tech.tech[i].count}x)` : ""; // if (tech.tech[i].isNonRefundable) { // text += `
${tech.tech[i].link} ${techCountText}
${tech.tech[i].descriptionFunction ? tech.tech[i].descriptionFunction() :tech.tech[i].description}
` // } else if (tech.tech[i].isFieldTech) { // text += `
// //
//
//
//         ${tech.tech[i].link} ${techCountText}
${tech.tech[i].descriptionFunction ? tech.tech[i].descriptionFunction() :tech.tech[i].description}
` // } else if (tech.tech[i].isGunTech) { // text += `
// //
//
//
//         ${tech.tech[i].link} ${techCountText}
${tech.tech[i].descriptionFunction ? tech.tech[i].descriptionFunction() :tech.tech[i].description}
` // } else if (tech.tech[i].isLore) { // text += `
  ${tech.tech[i].name} ${techCountText}
${tech.tech[i].descriptionFunction ? tech.tech[i].descriptionFunction() :tech.tech[i].description}
` // } else { // text += `
  ${tech.tech[i].link} ${techCountText}
${tech.tech[i].descriptionFunction ? tech.tech[i].descriptionFunction() :tech.tech[i].description}
` // } const style = (localSettings.isHideImages || tech.tech[i].isJunk || tech.tech[i].isLore) ? `style="height:auto;"` : `style = "background-image: url('img/${tech.tech[i].name}.webp');"` const techCountText = tech.tech[i].count > 1 ? `(${tech.tech[i].count}x)` : ""; if (tech.tech[i].isNonRefundable) { text += `
${tech.tech[i].link} ${techCountText}
${tech.tech[i].descriptionFunction ? tech.tech[i].descriptionFunction() : tech.tech[i].description}
` // } else if (tech.tech[i].isLore) { // text += `
  ${tech.tech[i].name} ${techCountText}
${tech.tech[i].descriptionFunction ? tech.tech[i].descriptionFunction() :tech.tech[i].description}
` } else if (tech.tech[i].isFieldTech) { text += `
` text += build.fieldTechText(i) + "
" } else if (tech.tech[i].isGunTech) { text += `
` text += build.gunTechText(i) + "
" } else if (tech.tech[i].isSkin) { text += `
` text += build.skinTechText(i) + "
" } else { text += `
` text += build.techText(i) + "
" } } else if (tech.tech[i].isLost) { text += `
${tech.tech[i].link}
${tech.tech[i].descriptionFunction ? tech.tech[i].descriptionFunction() : tech.tech[i].description}
` } } const el = document.getElementById("pause-grid-right") el.style.display = "grid" el.innerHTML = text }, sortTech(find, isExperiment = false) { const sortKeyword = (a, b) => { let aHasKeyword = (a.descriptionFunction ? a.descriptionFunction() : a.description).includes(find) || a.name.includes(find) let bHasKeyword = (b.descriptionFunction ? b.descriptionFunction() : b.description).includes(find) || b.name.includes(find) if ((aHasKeyword) && !bHasKeyword) return -1; if (!aHasKeyword && bHasKeyword) return 1; return 0; } if (find === 'guntech') { tech.tech.sort((a, b) => { if (a.isGunTech && b.isGunTech) { if (a.allowed() > b.allowed()) return -1; //sort to the top if (!a.allowed() < b.allowed()) return 1; //sort to the bottom } if (a.isGunTech && !b.isGunTech) return -1; //sort to the top if (!a.isGunTech && b.isGunTech) return 1; //sort to the bottom return 0; }); } else if (find === 'fieldtech') { tech.tech.sort((a, b) => { if (a.isFieldTech && b.isFieldTech) { if (a.allowed() > b.allowed()) return -1; //sort to the top if (!a.allowed() < b.allowed()) return 1; //sort to the bottom } if (a.isFieldTech && !b.isFieldTech) return -1; //sort to the top if (!a.isFieldTech && b.isFieldTech) return 1; //sort to the bottom return 0; }); } else if (find === 'allowed') { tech.tech.sort((a, b) => { if (a.allowed() > b.allowed()) return -1; //sort to the top if (!a.allowed() < b.allowed()) return 1; //sort to the bottom return 0; }); } else if (find === 'have') { tech.tech.sort((a, b) => { if (a.count > b.count) return -1; //sort to the top if (!a.count < b.count) return 1; //sort to the bottom return 0; }); } else if (find === 'heal') { tech.tech.sort((a, b) => { if (a.isHealTech && b.isHealTech) { if (a.allowed() > b.allowed()) return -1; //sort to the top if (!a.allowed() < b.allowed()) return 1; //sort to the bottom } if (a.isHealTech && !b.isHealTech) return -1; //sort to the top if (!a.isHealTech && b.isHealTech) return 1; //sort to the bottom return 0; }); } else if (find === 'bot') { tech.tech.sort((a, b) => { if (a.isBotTech && b.isBotTech) { if (a.allowed() > b.allowed()) return -1; //sort to the top if (!a.allowed() < b.allowed()) return 1; //sort to the bottom } if (a.isBotTech && !b.isBotTech) return -1; //sort to the top if (!a.isBotTech && b.isBotTech) return 1; //sort to the bottom return 0; }); } else if (document.getElementById("sort-input").value === 'skin') { tech.tech.sort((a, b) => { if (a.isSkin && b.isSkin) { if (a.allowed() > b.allowed()) return -1; //sort to the top if (!a.allowed() < b.allowed()) return 1; //sort to the bottom } if (a.isSkin && !b.isSkin) return -1; //sort to the top if (!a.isSkin && b.isSkin) return 1; //sort to the bottom return 0; }); } else if (document.getElementById("sort-input").value === 'junk') { tech.tech.sort((a, b) => { if (a.isJunk && !b.isJunk) return -1; //sort to the top if (!a.isJunk && b.isJunk) return 1; //sort to the bottom return 0; }); } else if (find === 'damage') { tech.tech.sort(sortKeyword); } else if (find === 'defense') { tech.tech.sort(sortKeyword); } else if (find === 'energy') { tech.tech.sort(sortKeyword); } else if (find === 'input') { find = document.getElementById("sort-input").value; tech.tech.sort(sortKeyword); } if (isExperiment) { build.populateGrid() // build.updateExperimentText() document.getElementById("tech-0").scrollIntoView(); //scroll to the first tech after sorting } else { build.generatePauseRight() //makes the right side of the pause menu with the tech } document.getElementById("sort-input").value = find; //make the sorted string display in the keyword search input field }, unPauseGrid() { document.getElementById("guns").style.display = "inline" document.getElementById("field").style.display = "inline" if (tech.isEnergyHealth) { document.getElementById("health").style.display = "none" document.getElementById("health-bg").style.display = "none" } else { document.getElementById("health").style.display = "inline" document.getElementById("health-bg").style.display = "inline" } if (!localSettings.isHideHUD) { document.getElementById("tech").style.display = "inline" document.getElementById("defense-bar").style.display = "inline" document.getElementById("damage-bar").style.display = "inline" } // document.body.style.overflow = "hidden" document.getElementById("pause-grid-left").style.display = "none" document.getElementById("pause-grid-right").style.display = "none" document.getElementById("pause-grid-right").style.opacity = "1" document.getElementById("pause-grid-left").style.opacity = "1" window.scrollTo(0, 0); }, isExperimentSelection: false, isExperimentRun: false, techText(i) { return `
  ${build.nameLink(tech.tech[i].name)} ${tech.tech[i].count > 1 ? `(${tech.tech[i].count}x)` : ""}
${tech.tech[i].descriptionFunction ? tech.tech[i].descriptionFunction() : tech.tech[i].description}
` }, skinTechText(i) { return `
       ${build.nameLink(tech.tech[i].name)} ${tech.tech[i].count > 1 ? `(${tech.tech[i].count}x)` : ""}
${tech.tech[i].descriptionFunction ? tech.tech[i].descriptionFunction() : tech.tech[i].description}
` }, gunTechText(i) { return `
          ${build.nameLink(tech.tech[i].name)} ${tech.tech[i].count > 1 ? `(${tech.tech[i].count}x)` : ""}
${tech.tech[i].descriptionFunction ? tech.tech[i].descriptionFunction() : tech.tech[i].description}
` }, fieldTechText(i) { return `
          ${build.nameLink(tech.tech[i].name)} ${tech.tech[i].count > 1 ? `(${tech.tech[i].count}x)` : ""}
${tech.tech[i].descriptionFunction ? tech.tech[i].descriptionFunction() : tech.tech[i].description}
` }, junkTechText(i) { return `
  ${build.nameLink(tech.tech[i].name)} ${tech.tech[i].count > 1 ? `(${tech.tech[i].count}x)` : ""}
${tech.tech[i].descriptionFunction ? tech.tech[i].descriptionFunction() : tech.tech[i].description}
` }, choosePowerUp(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 document.getElementById("gun-" + b.inventory[i]).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; b.inventoryGun = 0; } simulation.makeGunHUD(); break } } if (!isDeselect) { //add gun document.getElementById("gun-" + index).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) document.getElementById("field-" + index).classList.add("build-field-selected"); document.getElementById("tech-150").focus(); } else if (m.fieldMode === 4) { const i = 4 //update experiment text simulation.molecularMode++ if (simulation.molecularMode > i - 1) simulation.molecularMode = 0 m.fieldUpgrades[i].description = m.fieldUpgrades[i].setDescription() // document.getElementById(`field-${i}`).innerHTML = `
  ${build.nameLink(m.fieldUpgrades[i].name)}
${m.fieldUpgrades[i].description}` document.getElementById(`field-${i}`).innerHTML = `
  ${build.nameLink(m.fieldUpgrades[i].name)}
${m.fieldUpgrades[i].description}
` } } else if (type === "tech") { if (tech.tech[index].count < tech.tech[index].maxCount) { // if (!tech.tech[index].isLore && !tech.tech[index].isNonRefundable && !who.classList.contains("build-tech-selected")) who.classList.add("build-tech-selected"); if (!document.getElementById("tech-" + index).classList.contains("build-tech-selected")) document.getElementById("tech-" + index).classList.add("build-tech-selected"); tech.giveTech(index) } else if (!tech.tech[index].isNonRefundable) { // tech.totalCount -= tech.tech[index].count tech.removeTech(index); document.getElementById("tech-" + index).classList.remove("build-tech-selected"); } else { // for non refundable tech this makes it flash off for a second, but return to on to show that it can't be set off document.getElementById("tech-" + index).classList.remove("build-tech-selected") setTimeout(() => { document.getElementById("tech-" + index).classList.add("build-tech-selected") }, 50); } } build.updateExperimentText(isAllowed) }, updateExperimentText(isAllowed = false) { for (let i = 0, len = tech.tech.length; i < len; i++) { const techID = document.getElementById("tech-" + i) if ((!tech.tech[i].isJunk || localSettings.isJunkExperiment) && !tech.tech[i].isLore) { if (tech.tech[i].allowed() || isAllowed || tech.tech[i].count > 0) { if (tech.tech[i].isFieldTech) { techID.classList.remove('experiment-grid-hide'); techID.innerHTML = build.fieldTechText(i) } else if (tech.tech[i].isGunTech) { techID.classList.remove('experiment-grid-hide'); techID.innerHTML = build.gunTechText(i) } else if (tech.tech[i].isJunk) { techID.innerHTML = build.junkTechText(i) // `
  ${tech.tech[i].link} ${techCountText}
${tech.tech[i].descriptionFunction ? tech.tech[i].descriptionFunction() : tech.tech[i].description}` } else if (tech.tech[i].isSkin) { techID.classList.remove('experiment-grid-hide'); techID.innerHTML = build.skinTechText(i) } else { techID.innerHTML = build.techText(i) } //deselect selected tech options if you don't have the tech any more // for example: when bot techs are converted after a bot upgrade tech is taken if (tech.tech[i].count === 0 && techID.classList.contains("build-tech-selected")) techID.classList.remove("build-tech-selected"); if (techID.classList.contains("experiment-grid-disabled")) { techID.classList.remove("experiment-grid-disabled"); techID.setAttribute("onClick", `javascript: build.choosePowerUp(${i},'tech')`); } } else { //disabled color for disabled tech techID.innerHTML = `
${tech.tech[i].name}
${tech.tech[i].descriptionFunction ? tech.tech[i].descriptionFunction() : tech.tech[i].description}` 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"); if (tech.tech[i].isFieldTech) { techID.innerHTML = build.fieldTechText(i) } else if (tech.tech[i].isGunTech) { techID.innerHTML = build.gunTechText(i) } else if (tech.tech[i].isJunk) { techID.innerHTML = build.junkTechText(i) } else if (tech.tech[i].isSkin) { techID.innerHTML = build.skinTechText(i) } else { techID.innerHTML = build.techText(i) } } } } }, populateGrid() { //background-color:var(--build-bg-color); let text = `
   
start
reset
share
` const hideStyle = `style="height:auto; border: none; background-color: transparent;"` for (let i = 0, len = m.fieldUpgrades.length; i < len; i++) { const style = localSettings.isHideImages ? hideStyle : `style="background-image: url('img/field/${m.fieldUpgrades[i].name}${i === 0 ? m.fieldUpgrades[0].imageNumber : ""}.webp');"` text += `
  ${build.nameLink(m.fieldUpgrades[i].name)}
${m.fieldUpgrades[i].description}
` } for (let i = 0, len = b.guns.length; i < len; i++) { const style = localSettings.isHideImages ? hideStyle : `style="background-image: url('img/gun/${b.guns[i].name}.webp');"` text += `
  ${build.nameLink(b.guns[i].name)}
${b.guns[i].description}
` } for (let i = 0, len = tech.tech.length; i < len; i++) { if ((!tech.tech[i].isJunk || localSettings.isJunkExperiment) && !tech.tech[i].isLore) { const style = (localSettings.isHideImages || tech.tech[i].isJunk) ? hideStyle : `style="background-image: url('img/${tech.tech[i].name}.webp');"` if ((tech.tech[i].allowed() || tech.tech[i].count > 0) && (!tech.tech[i].isNonRefundable || localSettings.isJunkExperiment)) { // || tech.tech[i].name === "+1 cardinality") { //|| tech.tech[i].name === "leveraged investment" text += `
` } else { //disabled text += `
` } if (tech.tech[i].isFieldTech) { text += build.fieldTechText(i) } else if (tech.tech[i].isGunTech) { text += build.gunTechText(i) } else if (tech.tech[i].isSkin) { text += build.skinTechText(i) } else if (tech.tech[i].isJunk) { text += build.junkTechText(i) } else { text += build.techText(i) } text += '
' } } document.getElementById("experiment-grid").innerHTML = text // for (let i = 0, len = tech.tech.length; i < len; i++) { // if (tech.tech[i].count) // document.getElementById("tech-" + i).classList.add("build-tech-selected") // } 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) lore.setTechGoal() localSettings.difficultyMode = Number(document.getElementById("difficulty-select-experiment").value) document.getElementById("difficulty-select").value = document.getElementById("difficulty-select-experiment").value if (localSettings.isAllowed) localStorage.setItem("localSettings", JSON.stringify(localSettings)); //update local storage }); //add tooltips for (let i = 0, len = tech.tech.length; i < len; i++) { if (document.getElementById(`tech-${i}`)) { document.getElementById(`tech-${i}`).setAttribute('data-descr', tech.tech[i].requires); //add tooltip // document.getElementById(`tech-${i}`).setAttribute('title', tech.tech[i].requires); //add tooltip } } //highlight selected }, nameLink(text) { //converts text into a clickable wikipedia search return `${text}` }, reset() { build.isExperimentSelection = true; build.isExperimentRun = true; simulation.startGame(true); //starts game, but pauses it build.isExperimentSelection = true; build.isExperimentRun = true; simulation.paused = true; 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; b.inventoryGun = 0; simulation.makeGunHUD(); m.resetSkin() 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?" url += `&seed=${Math.initialSeed}` 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++) { if (!tech.tech[i].isLore && !tech.tech[i].isJunk && !tech.tech[i].isNonRefundable) { url += `&tech${count}=${encodeURIComponent(tech.tech[i].name.trim())}` count++ } } } url += `&molMode=${encodeURIComponent(simulation.molecularMode)}` // if (property === "molMode") { // simulation.molecularMode = Number(set[property]) // m.fieldUpgrades[i].description = m.fieldUpgrades[i].setDescription() // document.getElementById(`field-${i}`).innerHTML = `
  ${build.nameLink(m.fieldUpgrades[i].name)}
${m.fieldUpgrades[i].description}` // } 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))}` // 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) navigator.clipboard.writeText(url).then(function () { /* clipboard successfully set */ if (isCustom) { setTimeout(function () { alert('n-gon build URL copied to clipboard.\nPaste into browser address bar.') }, 300); } }, function () { /* clipboard write failed */ if (isCustom) { setTimeout(function () { alert('copy failed') }, 300); } console.log('copy failed') }); }, hasExperimentalMode: false, startExperiment() { //start playing the game after exiting the experiment menu 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 b.inventoryGun = 0; simulation.makeGunHUD(); } for (let i = 0; i < bullet.length; ++i) Matter.Composite.remove(engine.world, bullet[i]); bullet = []; //remove any bullets that might have spawned from tech build.hasExperimentalMode = false if (!simulation.isCheating) { for (let i = 0, len = tech.tech.length; i < len; i++) { if (tech.tech[i].count > 0 && !tech.tech[i].isLore) simulation.isCheating = true; } if (b.inventory.length !== 0 || m.fieldMode !== 0) simulation.isCheating = true; } if (simulation.isCheating) { //if you are cheating remove any lore you might have gotten lore.techCount = 0; for (let i = 0, len = tech.tech.length; i < len; i++) { if (tech.tech[i].isLore) { tech.tech[i].frequency = 0; //remove lore power up chance tech.tech[i].count = 0; //remove lore power up chance } } simulation.updateTechHUD(); } else { //if you have no tech (not cheating) remove all power ups that might have spawned from tech for (let i = 0; i < powerUp.length; ++i) Matter.Composite.remove(engine.world, powerUp[i]); powerUp = []; // if (build.hasExperimentalMode) { // for (let i = 0; i < 7; i++) tech.giveTech("undefined") // } } 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"; document.getElementById("training-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' 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: "KeyF", field: "Space", up: "KeyW", // jump down: "KeyS", // crouch left: "KeyA", right: "KeyD", pause: "KeyP", nextGun: "KeyE", previousGun: "KeyQ", testing: "KeyT" }, setDefault() { input.key = { fire: "KeyF", 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', '') } if (!input.key.fire) input.key.fire = "KeyF" document.getElementById("key-fire").innerHTML = cleanText(input.key.fire) 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 if (localSettings.isAllowed) localStorage.setItem("localSettings", JSON.stringify(localSettings)); //update local storage }, focus: null, setTextFocus() { const backgroundColor = "#fff" document.getElementById("key-fire").style.background = backgroundColor 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)'; document.getElementById("key-num").style.background = backgroundColor //always not highlighted }, 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.fire || 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 === "Escape" || event.code === input.key.nextGun || event.code === input.key.previousGun || event.code === input.key.testing || event.code === "Digit1" || event.code === "Digit2" || event.code === "Digit3" || event.code === "Digit4" || event.code === "Digit5" || event.code === "Digit6" || event.code === "Digit7" || event.code === "Digit8" || event.code === "Digit9" || event.code === "Digit0" || event.code === "Minus" || event.code === "Equal" )) { switch (input.focus.id) { case "key-fire": input.key.fire = event.code break; 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.fire: input.fire = false break case input.key.field: input.field = false break } }); window.addEventListener("keydown", function (event) { // console.log(event.code) 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.fire: input.fire = true break case input.key.field: 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 if (!tech.isNoDraftPause) { simulation.paused = true; build.pauseGrid() document.body.style.cursor = "auto"; if (tech.isPauseSwitchField || simulation.testing) { document.getElementById("pause-field").addEventListener("click", () => { const energy = m.energy //save current energy if (m.fieldMode === 4 && simulation.molecularMode < 3) { simulation.molecularMode++ m.fieldUpgrades[4].description = m.fieldUpgrades[4].setDescription() } else { m.setField((m.fieldMode === m.fieldUpgrades.length - 1) ? 0 : m.fieldMode + 1) //cycle to next field if (m.fieldMode === 4) { simulation.molecularMode = 0 m.fieldUpgrades[4].description = m.fieldUpgrades[4].setDescription() } } m.energy = energy //return to current energy // document.getElementById("pause-field").innerHTML = `
  ${m.fieldUpgrades[m.fieldMode].name}
${m.fieldUpgrades[m.fieldMode].description}` document.getElementById("pause-field").style.backgroundImage = `url('img/field/${m.fieldUpgrades[m.fieldMode].name}${m.fieldMode === 0 ? Math.floor(Math.random() * 10) : ""}.webp')` document.getElementById("pause-field").innerHTML = `
  ${build.nameLink(m.fieldUpgrades[m.fieldMode].name)}
${m.fieldUpgrades[m.fieldMode].description}
` }); } } } break case input.key.testing: if (m.alive && localSettings.loreCount > 0 && !simulation.paused) { if (simulation.difficultyMode > 4) { simulation.makeTextLog("testing mode disabled for this difficulty"); break } 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.testing) tech.setCheating(); simulation.makeTextLog( `
T toggle testing
R teleport to mouse
F cycle field
G all guns
H +100% defense
B damage, research
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 (b.inventory.length > 1 && !simulation.testing) { switch (event.code) { case "Digit1": simulation.switchToGunInInventory(0); break case "Digit2": simulation.switchToGunInInventory(1); break case "Digit3": simulation.switchToGunInInventory(2); break case "Digit4": simulation.switchToGunInInventory(3); break case "Digit5": simulation.switchToGunInInventory(4); break case "Digit6": simulation.switchToGunInInventory(5); break case "Digit7": simulation.switchToGunInInventory(6); break case "Digit8": simulation.switchToGunInInventory(7); break case "Digit9": simulation.switchToGunInInventory(8); break case "Digit0": simulation.switchToGunInInventory(9); break case "Minus": simulation.switchToGunInInventory(10); break case "Equal": simulation.switchToGunInInventory(11); 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": spawn.bodyRect(simulation.mouseInGame.x, simulation.mouseInGame.y, 50, 50); 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 if (m.immuneCycle === Infinity) { m.immuneCycle = 0 //you can't take damage } else { m.immuneCycle = Infinity //you can't take damage } // 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 "b": tech.isRerollDamage = true powerUps.research.changeRerolls(1000000) 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 case "l": document.getElementById("field").style.display = "none" document.getElementById("guns").style.display = "none" document.getElementById("tech").style.display = "none" 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.button === 0) { input.fire = false; } else if (e.button === 2) { input.field = false; } }); document.body.addEventListener("mousedown", (e) => { if (e.button === 0) { input.fire = true; } else if (e.button === 2) { input.field = 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 function localStorageCheck() { try { return 'localStorage' in window && window['localStorage'] !== null; } catch (e) { return false; } } if (localStorageCheck()) { localSettings = JSON.parse(localStorage.getItem("localSettings")) if (localSettings) { console.log('localStorage is enabled') localSettings.isAllowed = true localSettings.isEmpty = false } else { console.log('localStorage is enabled, local settings empty') localSettings = { isAllowed: true, isEmpty: true } } } else { console.log("localStorage is disabled") localSettings = { isAllowed: false } } if (localSettings.isAllowed && !localSettings.isEmpty) { console.log('restoring previous settings') 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 if (localSettings.difficultyMode === undefined) localSettings.difficultyMode = "2" simulation.difficultyMode = localSettings.difficultyMode lore.setTechGoal() document.getElementById("difficulty-select").value = localSettings.difficultyMode if (localSettings.fpsCapDefault === undefined) localSettings.fpsCapDefault = 'max' if (localSettings.personalSeeds === undefined) localSettings.personalSeeds = []; if (localSettings.fpsCapDefault === 'max') { simulation.fpsCapDefault = 999999999; } else { simulation.fpsCapDefault = Number(localSettings.fpsCapDefault) } document.getElementById("fps-select").value = localSettings.fpsCapDefault if (!localSettings.banList) localSettings.banList = "" if (localSettings.banList.length === 0 || localSettings.banList === "undefined") { localSettings.banList = "" localStorage.setItem("localSettings", JSON.stringify(localSettings)); //update local storage } document.getElementById("banned").value = localSettings.banList if (!localSettings.isLoreDoesNotNeedReset) { localSettings.isLoreDoesNotNeedReset = true localSettings.loreCount = 0; //this sets what conversation is heard if (localSettings.isAllowed) localStorage.setItem("localSettings", JSON.stringify(localSettings)); //update local storage } if (localSettings.isHideImages === undefined) localSettings.isHideImages = true //default to hide images document.getElementById("hide-images").checked = localSettings.isHideImages if (localSettings.isHideHUD === undefined) localSettings.isHideHUD = false document.getElementById("hide-hud").checked = localSettings.isHideHUD } else { console.log('setting default localSettings') const isAllowed = localSettings.isAllowed //don't overwrite isAllowed value localSettings = { banList: "", isAllowed: isAllowed, personalSeeds: [], isJunkExperiment: false, isCommunityMaps: false, difficultyMode: '2', fpsCapDefault: 'max', runCount: 0, isTrainingNotAttempted: true, levelsClearedLastGame: 0, loreCount: 0, isLoreDoesNotNeedReset: false, isHuman: false, key: undefined, isHideImages: true, //default to hide images isHideHUD: false, }; input.setDefault() if (localSettings.isAllowed) localStorage.setItem("localSettings", JSON.stringify(localSettings)); //update local storage document.getElementById("community-maps").checked = localSettings.isCommunityMaps simulation.isCommunityMaps = localSettings.isCommunityMaps document.getElementById("hide-images").checked = localSettings.isHideImages document.getElementById("difficulty-select").value = localSettings.difficultyMode document.getElementById("fps-select").value = localSettings.fpsCapDefault document.getElementById("banned").value = localSettings.banList } 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 if (localSettings.isAllowed) localStorage.setItem("localSettings", JSON.stringify(localSettings)); //update local storage }); document.getElementById("banned").addEventListener("input", () => { localSettings.banList = document.getElementById("banned").value if (localSettings.isAllowed) 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 if (localSettings.isAllowed) 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) lore.setTechGoal() localSettings.difficultyMode = simulation.difficultyMode localSettings.levelsClearedLastGame = 0 //after changing difficulty, reset run history localSettings.entanglement = undefined //after changing difficulty, reset stored tech if (localSettings.isAllowed) 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 = 20; 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); } ); }) const sound = { tone(frequency, end = 1000, gain = 0.05) { const audioCtx = new (window.AudioContext || window.webkitAudioContext)(); //setup audio context const oscillator = audioCtx.createOscillator(); const gainNode = audioCtx.createGain(); gainNode.gain.value = gain; //controls volume oscillator.connect(gainNode); gainNode.connect(audioCtx.destination); oscillator.type = "sine"; // 'sine' 'square', 'sawtooth', 'triangle' and 'custom' oscillator.frequency.value = frequency; // value in hertz oscillator.start(); setTimeout(() => { audioCtx.suspend() audioCtx.close() }, end) // return audioCtx }, portamento(frequency, end = 1000, shiftRate = 10, gain = 0.05) { const audioCtx = new (window.AudioContext || window.webkitAudioContext)(); //setup audio context const oscillator = audioCtx.createOscillator(); const gainNode = audioCtx.createGain(); gainNode.gain.value = gain; //controls volume oscillator.connect(gainNode); gainNode.connect(audioCtx.destination); oscillator.type = "sine"; // 'sine' 'square', 'sawtooth', 'triangle' and 'custom' oscillator.frequency.value = frequency; // value in hertz oscillator.start(); for (let i = 0, len = end * 0.1; i < len; i++) oscillator.frequency.setValueAtTime(frequency + i * shiftRate, audioCtx.currentTime + i * 0.01); setTimeout(() => { audioCtx.suspend() audioCtx.close() }, end) // return audioCtx } } // preload images so they load cleaner // MDN Scripting and preloads - https://developer.mozilla.org/en-US/docs/Web/HTML/Link_types/preload // if (!localSettings.isHideImages) { // for (let i = 0, len = b.guns.length; i < len; i++) { // const preloadLink = document.createElement("link"); // preloadLink.href = "img/gun/" + b.guns[i].name + ".webp"; // preloadLink.rel = "preload"; // preloadLink.as = "image"; // document.head.appendChild(preloadLink); // } // for (let i = 1, len = m.fieldUpgrades.length; i < len; i++) { // const preloadLink = document.createElement("link"); // preloadLink.href = "img/field/" + m.fieldUpgrades[i].name + ".webp"; // preloadLink.rel = "preload"; // preloadLink.as = "image"; // document.head.appendChild(preloadLink); // } // for (let i = 0, len = tech.tech.length; i < len; i++) { // if (!tech.tech[i].isJunk) { // const preloadLink = document.createElement("link"); // preloadLink.href = "img/" + tech.tech[i].name + ".webp"; // preloadLink.rel = "preload"; // preloadLink.as = "image"; // document.head.appendChild(preloadLink); // } // } // } //preload images early if (!localSettings.isHideImages) { addEventListener("load", () => { let urls = new Array() for (let i = 0, len = b.guns.length; i < len; i++) urls.push("img/gun/" + b.guns[i].name + ".webp") for (let i = 1, len = m.fieldUpgrades.length; i < len; i++) urls.push("img/field/" + m.fieldUpgrades[i].name + ".webp") for (let i = 0, len = tech.tech.length; i < len; i++) { if (!tech.tech[i].isJunk && !tech.tech[i].isLore) urls.push("img/" + tech.tech[i].name + ".webp") } let images = new Array() for (let i = 0; i < urls.length; i++) { images[i] = new Image() images[i].src = urls[i] } // console.log(urls, images) }); document.getElementById("choose-grid").classList.add('choose-grid'); } else { document.getElementById("choose-grid").classList.add('choose-grid-no-images'); } //********************************************************************** // 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(); } } // 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(); // } // } // let timeStart = performance.now() // //0, 16.6666666666, 33.333333333333, 50.000000000 // function cycle(timestamp) { // if (!simulation.paused) requestAnimationFrame(cycle); // if (timestamp - timeStart > 0) { //simulation.fpsInterval) { // if enough time has elapsed, draw the next frame // console.log(timestamp - timeStart) // timeStart = timestamp // 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(); // } // } // let count = 1 // let timeStart = performance.now() // const cycle = (timestamp) => { // // if (timeStart === undefined) timeStart = timestamp // // console.log(timestamp, timeStart) // if (timestamp - timeStart > tech.brainStormDelay * count) { // count++ // powerUps.tech.effect(); // document.getElementById("choose-grid").style.pointerEvents = "auto"; //turn off the normal 500ms delay // document.body.style.cursor = "auto"; // document.getElementById("choose-grid").style.transitionDuration = "0s"; // } // if (count < 5 && simulation.isChoosing) { // requestAnimationFrame(cycle); // } else { // tech.isBrainstormActive = false // } // } // requestAnimationFrame(cycle);