"use strict";
/* TODO: *******************************************
*****************************************************
mod - energy recharges fast when not moving
lore - a mech gains self awareness
each mod/gun/field is a new tech
all the technology leads to the singularity
each game run is actually the mech simulating a possible escape
this is why the graphics are so bad, its just a simulation
final mod is "this is just a simulation"
you get immortality and Infinity damage
the next level is the final level
atmosphere levels that explore lore
give the user a rest, between combat
low combat more graphics
large rotating fan that the player has to move through
nonaggressive mobs one mob attacking the passive mobs
in the final level you see your self at the starting level, with the wires
you shoot your self to wake up?
wave beam - needs a serious buff, its not fun, and it needs more damage
shorter range, higher dmg, faster wiggle
mod: wave beam - speed up the wave beam and reduce the wave amplitude to improve targeting at range
shotgun - even more short range effects
mod: flechettes - each burst fires an extra flechette
mod: flechettes - stick into mobs and do poison damage, like foam
mod: foam - does extra poison dmg (to get past shields)
mod: vacuum bomb - does a constant suck
mod: shield harmonics - large field radius
mod: shotgun - fire extra shot
but also increase spread?
speed up movement
higher gravity, larger jump force
faster horizontal acceleration
only increase top speed a bit
mob: targeting laser, then a high speed, no gravity bullet
add difficulty slider to custom?
add recursive mod counts to pause screen
key required to open the exit to some levels
must hold key like a block
could the key be any block?
css transition for pause menu
field that pushes everything back, and can destroy smaller blocks
converts blocks into ammo power ups
mod: make player invisible when...
use the flag from phase field
when health is low?
field: a larger radius that attracted enemies
still deflected them near the robot
convert the health of mobs into energy when they are being attracted
mod: chance to not die from fatal damage
also push mobs and bodies away?
also heal?
mod: bot that fires minigun bullets
only fires when your mouse is held down
mod: do extra damage based on your speed
do more damage when not moving?
take less damage when not moving?
gun/field: portals
use the code from mines to get them to stick to walls
or lasers
alternate red and blue portals
missiles don't explode reliably enough
they can bounce, which is cool, but they should still explode right after a bounce
weekly random challenge where everyone playing each week gets the same random setup.
The randomness would be determined by the date so it would sync everyone.
power ups still drop, but the drops are determined by a preset list that changes each week.
mod: do something at the end of each level
heal to full
should still be effected by the heal reduction at higher difficulty
give ammo to current gun
give goals/quests for each level
how to track goals?
take no damage
don't shoot
gun: Spirit Bomb (singularity)
use charge up like rail gun
electricity graphics like plasma torch
suck in nearby mobs, power ups?, blocks?
sucked in stuff increase size
uses energy
hold above the player's head
Boss levels
sensor that locks you in after you enter the boss room
boss that eats other mobs and gains stats from them
chance to spawn on any level (past level 5)
add a key that player picks up and needs to set on the exit door to open it
make power ups keep moving to player if the pickup field is turned off before they get picked up
not sure how to do this without adding a constant check
animate new level spawn by having the map aspects randomly fly into place
new map with repeating endlessness
get ideas from Manifold Garden game
if falling, get teleported above the map
I tried it, but had trouble getting the camera to adjust to the teleportation
this can apply to blocks mobs, and power ups as well
field power up effects
field allows player to hold and throw living mobs
and hack mobs
give mobs more animal-like behaviors
like rain world
give mobs something to do when they don't see player
explore map
eat power ups
drop power up (if killed after eating one)
mobs some times aren't aggressive
when low on life or after taking a large hit
mobs can fight each other
this might be hard to code
isolated mobs try to group up.
game mechanics
mechanics that support the physics engine
add rope/constraint
get ideas from game: limbo / inside
environmental hazards
laser
lava
button / switch
door
fizzler
moving platform
map zones
water
low friction ground
bouncy ground
*/
//collision groups
// cat.player | cat.map | cat.body | cat.bullet | cat.powerUp | cat.mob | cat.mobBullet | cat.mobShield
const cat = {
player: 0x1,
map: 0x10,
body: 0x100,
bullet: 0x1000,
powerUp: 0x10000,
mob: 0x100000,
mobBullet: 0x1000000,
mobShield: 0x10000000,
}
//build build grid display
const build = {
isShowingBuilds: false,
list: [],
choosePowerUp(who, index, type) {
if (type === "field" || type === "gun") {
let isDeselect = false
//if already click, toggle off
for (let i = 0; i < build.list.length; i++) {
if (build.list[i].index === index && build.list[i].type === type) {
build.list.splice(i, 1);
who.style.backgroundColor = "#fff"
isDeselect = true
break
}
}
//check if trying to get a second field
if (type === "field") {
for (let i = 0; i < build.list.length; i++) {
if (build.list[i].type === "field") { //if already click, toggle off
build.list[i].who.style.backgroundColor = "#fff"
build.list.splice(i, 1);
}
}
}
if (!isDeselect) {
who.style.backgroundColor = "#919ba8" //"#868f9a"
build.list[build.list.length] = {
who: who,
index: index,
type: type,
}
}
} else if (type === "mod") {
if (who.style.backgroundColor !== "#919ba8") who.style.backgroundColor = "#919ba8" //"#868f9a"
//if already clicked graphically indicate recursive clicks
let count = 0
for (let i = 0; i < build.list.length; i++) {
if (build.list[i].type === "mod" && build.list[i].index === index) {
count++
}
}
if (count < b.mods[index].maxCount) {
//add mod to build list
build.list[build.list.length] = {
who: who,
index: index,
type: type,
}
count++
//display mod count in grid box text
if (count > 1) who.innerHTML = `
${b.mods[index].name} (${count}x)
${b.mods[index].description}`
} else {
//when above the mod limit remove all of that mod
for (let i = build.list.length - 1; i > -1; i--) {
if (build.list[i].index === index && build.list[i].type === type) {
build.list.splice(i, 1);
}
}
//and reset the text
who.style.backgroundColor = "#fff"
who.innerHTML = `
${b.mods[index].name}
${b.mods[index].description}`
}
}
// document.title = `effective starting level: ${build.list.length * game.difficultyMode}`
build.calculateCustomDifficulty()
},
makeGrid() {
let text = `
`
for (let i = 1, 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 += `
${b.guns[i].name}
${b.guns[i].description}
`
}
for (let i = 0, len = b.mods.length; i < len; i++) {
if (b.mods[i].name === "Born rule" || b.mods[i].name === "+1 cardinality" || b.mods[i].name === "leveraged investment") {
text += `
${b.mods[i].name}
${b.mods[i].description}
`
} else {
text += `
${b.mods[i].name}
${b.mods[i].description}
`
}
}
const el = document.getElementById("build-grid")
el.innerHTML = text
el.style.display = "none"
document.getElementById("difficulty-select-custom").addEventListener("input", () => {
document.getElementById("difficulty-select").value = document.getElementById("difficulty-select-custom").value
game.difficultyMode = Number(document.getElementById("difficulty-select-custom").value)
localSettings.difficultyMode = game.difficultyMode
localStorage.setItem("localSettings", JSON.stringify(localSettings)); //update local storage
build.calculateCustomDifficulty()
});
},
reset() {
build.list = []
build.makeGrid();
document.getElementById("build-grid").style.display = "grid"
build.calculateCustomDifficulty()
document.getElementById("difficulty-select-custom").value = localSettings.difficultyMode
},
pauseGrid() {
// let text = ``
let text = `
PAUSED press P to resume
`;
//
// ${game.SVGleftMouse} fire gun
// ${game.SVGrightMouse} use field
//
let countGuns = 0
let countMods = 0
for (let i = 0, len = b.guns.length; i < len; i++) {
if (b.guns[i].have) {
text += `
${b.guns[i].name}
${b.guns[i].description}
`
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) {
text += `
${b.mods[i].name}
${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"
},
calculateCustomDifficulty() {
let difficulty = build.list.length * game.difficultyMode
if (game.difficultyMode === 0) difficulty = build.list.length * 1 - 6
if (game.difficultyMode === 4) difficulty = build.list.length * 4 + 8
document.getElementById("starting-level").innerHTML = `starting difficulty: ${difficulty}`
},
startBuildRun() {
spawn.setSpawnList(); //gives random mobs, not starter mobs
spawn.setSpawnList();
game.startGame();
let difficulty = build.list.length * game.difficultyMode - 1
if (game.difficultyMode === 0) {
difficulty = build.list.length * 1 - 6 - 1
game.isEasyMode = true;
}
if (game.difficultyMode === 4) level.difficultyIncrease(6)
level.difficultyIncrease(difficulty)
level.isBuildRun = true;
build.givePowerUps();
},
givePowerUps() {
for (let i = 0; i < build.list.length; i++) {
if (build.list[i].type === "field") {
mech.setField(build.list[i].index)
} else if (build.list[i].type === "gun") {
b.giveGuns(build.list[i].index)
} else if (build.list[i].type === "mod") {
b.giveMod(build.list[i].index)
}
}
}
}
build.makeGrid();
document.getElementById("build-button").addEventListener("click", () => {
document.getElementById("build-button").style.display = "none";
const el = document.getElementById("build-grid")
if (build.isShowingBuilds) {
el.style.display = "none"
build.isShowingBuilds = false
document.body.style.overflow = "hidden"
document.getElementById("info").style.display = 'inline'
} else {
build.list = []
build.reset()
// let text = '
The difficulty increases by one level for each power up you choose.
'
build.isShowingBuilds = true
el.style.display = "grid"
document.body.style.overflowY = "scroll";
document.body.style.overflowX = "hidden";
document.getElementById("info").style.display = 'none'
}
build.calculateCustomDifficulty()
});
// 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.difficultyMode = localSettings.difficultyMode
document.getElementById("difficulty-select").value = localSettings.difficultyMode
document.getElementById("difficulty-select-custom").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,
difficultyMode: '1',
fpsCapDefault: '72',
};
localStorage.setItem("localSettings", JSON.stringify(localSettings)); //update local storage
document.getElementById("body-damage").checked = localSettings.isBodyDamage
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;
}
});
//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
});
// difficulty-select-custom event listener is set in build.makeGrid
document.getElementById("difficulty-select").addEventListener("input", () => {
document.getElementById("difficulty-select-custom").value = document.getElementById("difficulty-select").value
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();
// for (let i = 0, len = loop.length; i < len; i++) {
// loop[i]()
// }
}
}