"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,
}
//example https://landgreen.github.io/sidescroller/index.html?
// &gun1=minigun&gun2=laser
// &mod1=laser-bot&mod2=mass%20driver&mod3=overcharge&mod4=laser-bot&mod5=laser-bot&field=phase%20decoherence%20field&difficulty=2
//add ? to end of url then for each power up add
// &gun1=name&gun2=name
// &mod1=laser-bot&mod2=mass%20driver&mod3=overcharge&mod4=laser-bot&mod5=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) {
build.isURLBuild = true;
game.startGame()
}
});
//build build grid display
const build = {
isURLBuild: false,
onLoadPowerUps() {
const set = getUrlVars()
if (Object.keys(set).length !== 0) {
for (const property in set) {
// console.log(`${property}: ${give[property]}`);
set[property] = set[property].replace(/%20/g, " ")
if (property.substring(0, 3) === "gun") b.giveGuns(set[property])
if (property.substring(0, 3) === "mod") b.giveMod(set[property])
if (property === "field") mech.setField(set[property])
if (property === "difficulty") {
game.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]) * game.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 mods
if (b.inventory.length > 0) {
b.activeGun = b.inventory[0] //set first gun to active gun
game.makeGunHUD();
}
b.modOnHealthChange();
}
},
pauseGrid() {
let text = `
PAUSED press P to resume
level: ${level.levelsCleared} - ${level.levels[level.onLevel]} (${level.difficultyText()})
${mob.length} mobs, ${body.length} blocks, ${bullet.length} bullets, ${powerUp.length} power ups
mouse: (${game.mouseInGame.x.toFixed(1)}, ${game.mouseInGame.y.toFixed(1)}) ${mech.cycle} cycles
health: ${(mech.health*100).toFixed(0)}% energy: ${(mech.energy*100).toFixed(0)}% mass: ${player.mass.toFixed(1)}
position: (${player.position.x.toFixed(1)}, ${player.position.y.toFixed(1)}) velocity: (${player.velocity.x.toFixed(1)}, ${player.velocity.y.toFixed(1)})
`;
let countGuns = 0
let countMods = 0
for (let i = 0, len = b.guns.length; i < len; i++) {
if (b.guns[i].have) {
text += ``
countGuns++
}
}
let el = document.getElementById("pause-grid-left")
el.style.display = "grid"
el.innerHTML = text
text = "";
text += `
${mech.fieldUpgrades[mech.fieldMode].name}
${mech.fieldUpgrades[mech.fieldMode].description}
`
for (let i = 0, len = b.mods.length; i < len; i++) {
if (b.mods[i].count > 0) {
if (b.mods[i].count === 1) {
text += ``
} else {
text += `
${b.mods[i].name} (${b.mods[i].count}x)
${b.mods[i].description}
`
}
countMods++
}
}
el = document.getElementById("pause-grid-right")
el.style.display = "grid"
el.innerHTML = text
if (countMods > 5 || countGuns > 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);
},
isCustomSelection: true,
choosePowerUp(who, index, type) {
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;
game.makeGunHUD();
break
}
}
if (!isDeselect) { //add gun
who.classList.add("build-gun-selected");
b.giveGuns(index)
}
} else if (type === "field") {
if (mech.fieldMode !== index) {
document.getElementById("field-" + mech.fieldMode).classList.remove("build-field-selected");
mech.setField(index)
who.classList.add("build-field-selected");
}
} else if (type === "mod") { //remove mod if you have too many
if (b.mods[index].count < b.mods[index].maxCount) {
if (!who.classList.contains("build-mod-selected")) who.classList.add("build-mod-selected");
b.giveMod(index)
// if (b.mods[index].count > 1) who.innerHTML = `
${b.mods[index].name} (${b.mods[index].count}x)
${b.mods[index].description}`
} else {
b.removeMod(index);
// who.innerHTML = ` ${b.mods[index].description}`
who.classList.remove("build-mod-selected");
}
}
//update mod text //disable not allowed mods
for (let i = 0, len = b.mods.length; i < len; i++) {
const modID = document.getElementById("mod-" + i)
if (b.mods[i].allowed()) {
if (b.mods[i].count > 1) {
modID.innerHTML = `
${b.mods[i].name} (${b.mods[i].count}x)
${b.mods[i].description}`
} else {
modID.innerHTML = `${b.mods[i].description}`
}
if (modID.classList.contains("build-grid-disabled")) {
modID.classList.remove("build-grid-disabled");
modID.setAttribute("onClick", `javascript: build.choosePowerUp(this,${i},'mod')`);
}
} else {
modID.innerHTML = `requires: ${b.mods[i].requires} `
if (!modID.classList.contains("build-grid-disabled")) {
modID.classList.add("build-grid-disabled");
modID.onclick = null
}
if (b.mods[i].count > 0) {
b.removeMod(i)
}
if (modID.classList.contains("build-mod-selected")) {
modID.classList.remove("build-mod-selected");
}
}
}
},
populateGrid() {
let text = `
start
reset
share
`
for (let i = 0, len = mech.fieldUpgrades.length; i < len; i++) {
text += `
${mech.fieldUpgrades[i].name}
${mech.fieldUpgrades[i].description}
`
}
for (let i = 0, len = b.guns.length; i < len; i++) {
text += ``
}
for (let i = 0, len = b.mods.length; i < len; i++) {
if (!b.mods[i].allowed()) { // || b.mods[i].name === "+1 cardinality") { //|| b.mods[i].name === "leveraged investment"
text += `requires: ${b.mods[i].requires} `
} else if (b.mods[i].count > 1) {
text += `
${b.mods[i].name} (${b.mods[i].count}x)
${b.mods[i].description}
`
} else {
text += ``
}
}
document.getElementById("build-grid").innerHTML = text
document.getElementById("difficulty-select-custom").value = document.getElementById("difficulty-select").value
document.getElementById("difficulty-select-custom").addEventListener("input", () => {
game.difficultyMode = Number(document.getElementById("difficulty-select-custom").value)
localSettings.difficultyMode = Number(document.getElementById("difficulty-select-custom").value)
document.getElementById("difficulty-select").value = document.getElementById("difficulty-select-custom").value
localStorage.setItem("localSettings", JSON.stringify(localSettings)); //update local storage
});
b.resetModText();
},
reset() {
build.isCustomSelection = true;
mech.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;
game.makeGunHUD();
b.setupAllMods();
build.populateGrid();
document.getElementById("field-0").classList.add("build-field-selected");
document.getElementById("build-grid").style.display = "grid"
},
shareURL() {
// Create a fake textarea
const textAreaEle = document.createElement('textarea');
// Reset styles
textAreaEle.style.border = '0';
textAreaEle.style.padding = '0';
textAreaEle.style.margin = '0';
// Set the absolute position
// User won't see the element
textAreaEle.style.position = 'absolute';
textAreaEle.style.left = '-9999px';
textAreaEle.style.top = `0px`;
// Set the value
textAreaEle.value = build.generateURL();
// Append the textarea to body
document.body.appendChild(textAreaEle);
// Focus and select the text
textAreaEle.focus();
textAreaEle.select();
// Execute the "copy" command
try {
document.execCommand('copy');
} catch (err) {
// Unable to copy
} finally {
// Remove the textarea
document.body.removeChild(textAreaEle);
}
alert('n-gon build URL copied to clipboard.\nPaste into browser address bar.')
},
generateURL() {
let url = "https://landgreen.github.io/sidescroller/index.html?"
let count = 0;
for (let i = 0; i < b.guns.length; i++) {
if (b.guns[i].have) {
url += `&gun${count}=${encodeURIComponent(b.guns[i].name.trim())}`
count++
}
}
count = 0;
for (let i = 0; i < b.mods.length; i++) {
for (let j = 0; j < b.mods[i].count; j++) {
url += `&mod${count}=${encodeURIComponent(b.mods[i].name.trim())}`
count++
}
}
url += `&field=${encodeURIComponent(mech.fieldUpgrades[mech.fieldMode].name.trim())}`
url += `&difficulty=${game.difficultyMode}`
url += `&level=${Number(document.getElementById("starting-level").value)}`
console.log(url)
return url
},
startBuildRun() {
build.isCustomSelection = false;
b.modOnHealthChange()
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
game.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 mods
const levelsCleared = Number(document.getElementById("starting-level").value)
level.difficultyIncrease(Math.min(99, levelsCleared * game.difficultyMode)) //increase difficulty based on modes
level.levelsCleared += levelsCleared;
document.body.style.cursor = "none";
document.body.style.overflow = "hidden"
document.getElementById("build-grid").style.display = "none"
game.paused = false;
requestAnimationFrame(cycle);
}
}
document.getElementById("build-button").addEventListener("click", () => { //setup build run
build.isURLBuild = false;
document.getElementById("build-button").style.display = "none";
const el = document.getElementById("build-grid")
el.style.display = "grid"
document.body.style.overflowY = "scroll";
document.body.style.overflowX = "hidden";
document.getElementById("info").style.display = 'none'
level.isBuildRun = true;
game.startGame(); //starts game, but pauses it
build.isCustomSelection = true;
game.paused = true;
build.reset();
});
// local storage
let localSettings = JSON.parse(localStorage.getItem("localSettings"));
// console.log(localSettings)
if (localSettings) {
// game.isBodyDamage = localSettings.isBodyDamage
// document.getElementById("body-damage").checked = localSettings.isBodyDamage
game.isEasyToAimMode = localSettings.isEasyToAimMode
document.getElementById("track-pad-mode").checked = localSettings.isEasyToAimMode
game.isCommunityMaps = localSettings.isCommunityMaps
document.getElementById("community-maps").checked = localSettings.isCommunityMaps
game.difficultyMode = localSettings.difficultyMode
document.getElementById("difficulty-select").value = localSettings.difficultyMode
if (localSettings.fpsCapDefault === 'max') {
game.fpsCapDefault = 999999999;
} else {
game.fpsCapDefault = Number(localSettings.fpsCapDefault)
}
document.getElementById("fps-select").value = localSettings.fpsCapDefault
} else {
localSettings = {
// isBodyDamage: true,
isEasyToAimMode: false,
isCommunityMaps: false,
difficultyMode: '1',
fpsCapDefault: 'max',
};
localStorage.setItem("localSettings", JSON.stringify(localSettings)); //update local storage
// document.getElementById("body-damage").checked = localSettings.isBodyDamage
document.getElementById("track-pad-mode").checked = localSettings.isEasyToAimMode
game.isEasyToAimMode = localSettings.isEasyToAimMode
document.getElementById("community-maps").checked = localSettings.isEasyToAimMode
game.isCommunityMaps = localSettings.isCommunityMaps
document.getElementById("difficulty-select").value = localSettings.difficultyMode
document.getElementById("fps-select").value = localSettings.fpsCapDefault
}
//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 = "15px Arial";
ctx.lineJoin = "round";
ctx.lineCap = "round";
// ctx.lineCap='square';
game.setZoom();
}
setupCanvas();
window.onresize = () => {
setupCanvas();
};
//mouse move input
document.body.addEventListener("mousemove", (e) => {
game.mouse.x = e.clientX;
game.mouse.y = e.clientY;
});
document.body.addEventListener("mouseup", (e) => {
// game.buildingUp(e); //uncomment when building levels
// game.mouseDown = false;
// console.log(e)
if (e.which === 3) {
game.mouseDownRight = false;
} else {
game.mouseDown = false;
}
});
document.body.addEventListener("mousedown", (e) => {
if (e.which === 3) {
game.mouseDownRight = true;
} else {
game.mouseDown = true;
}
});
document.body.addEventListener("mouseenter", (e) => { //prevents mouse getting stuck when leaving the window
// game.mouseDown = false;
// game.mouseDownRight = false;
if (e.button === 1) {
game.mouseDown = true;
} else {
game.mouseDown = false;
}
if (e.button === 3) {
game.mouseDownRight = true;
} else {
game.mouseDownRight = false;
}
});
document.body.addEventListener("mouseleave", (e) => { //prevents mouse getting stuck when leaving the window
// game.mouseDown = false;
// game.mouseDownRight = false;
// console.log(e)
if (e.button === 1) {
game.mouseDown = true;
} else {
game.mouseDown = false;
}
if (e.button === 3) {
game.mouseDownRight = true;
} else {
game.mouseDownRight = false;
}
});
//keyboard input
const keys = [];
document.body.addEventListener("keydown", (e) => {
keys[e.keyCode] = true;
if (mech.alive) game.keyPress();
});
document.body.addEventListener("keyup", (e) => {
keys[e.keyCode] = false;
});
document.body.addEventListener("wheel", (e) => {
if (!game.paused) {
if (e.deltaY > 0) {
game.nextGun();
} else {
game.previousGun();
}
}
}, {
passive: true
});
document.getElementById("fps-select").addEventListener("input", () => {
let value = document.getElementById("fps-select").value
if (value === 'max') {
game.fpsCapDefault = 999999999;
} else {
game.fpsCapDefault = Number(value)
}
localSettings.fpsCapDefault = value
localStorage.setItem("localSettings", JSON.stringify(localSettings)); //update local storage
});
// document.getElementById("body-damage").addEventListener("input", () => {
// game.isBodyDamage = document.getElementById("body-damage").checked
// localSettings.isBodyDamage = game.isBodyDamage
// localStorage.setItem("localSettings", JSON.stringify(localSettings)); //update local storage
// });
document.getElementById("track-pad-mode").addEventListener("input", () => {
game.isEasyToAimMode = document.getElementById("track-pad-mode").checked
localSettings.isEasyToAimMode = game.isEasyToAimMode
localStorage.setItem("localSettings", JSON.stringify(localSettings)); //update local storage
});
document.getElementById("community-maps").addEventListener("input", () => {
game.isCommunityMaps = document.getElementById("community-maps").checked
localSettings.isCommunityMaps = game.isCommunityMaps
localStorage.setItem("localSettings", JSON.stringify(localSettings)); //update local storage
});
// difficulty-select-custom event listener is set in build.makeGrid
document.getElementById("difficulty-select").addEventListener("input", () => {
game.difficultyMode = Number(document.getElementById("difficulty-select").value)
localSettings.difficultyMode = game.difficultyMode
localStorage.setItem("localSettings", JSON.stringify(localSettings)); //update local storage
});
//main loop ************************************************************
//**********************************************************************
game.loop = game.normalLoop;
function cycle() {
if (!game.paused) requestAnimationFrame(cycle);
const now = Date.now();
const elapsed = now - game.then; // calc elapsed time since last loop
if (elapsed > game.fpsInterval) { // if enough time has elapsed, draw the next frame
game.then = now - (elapsed % game.fpsInterval); // Get ready for next frame by setting then=now. Also, adjust for fpsInterval not being multiple of 16.67
game.cycle++; //tracks game cycles
mech.cycle++; //tracks player cycles //used to alow time to stop for everything, but the player
if (game.clearNow) {
game.clearNow = false;
game.clearMap();
level.start();
}
game.loop();
// if (isNaN(mech.health) || isNaN(mech.energy)) {
// console.log(`mech.health = ${mech.health}`)
// game.paused = true;
// game.replaceTextLog = 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]()
// }
}
}