Files
n-gon-improved/js/game.js
landgreen f6238ff882 perfect diamagnetism balance
perfect diamagnetism balance and graphics
unique energy colors for fields
game info on pause screen
falling off the map, on easy and normal difficulty
  sets the player to the entrance
  sets power ups to the exit
2020-03-15 19:31:47 -07:00

1146 lines
42 KiB
JavaScript

// game Object ********************************************************
//*********************************************************************
const game = {
loop() {}, //main game loop, gets se tto normal or testing loop
testingLoop() {
game.gravity();
Engine.update(engine, game.delta);
game.wipe();
game.textLog();
mech.keyMove();
level.checkZones();
level.checkQuery();
mech.move();
mech.look();
game.checks();
ctx.save();
game.camera();
mech.draw();
game.draw.wireFrame();
game.draw.cons();
game.draw.testing();
game.drawCircle();
ctx.restore();
game.testingOutput();
game.drawCursor();
},
normalLoop() {
game.gravity();
Engine.update(engine, game.delta);
game.wipe();
game.textLog();
mech.keyMove();
level.checkZones();
level.checkQuery();
mech.move();
mech.look();
game.checks();
ctx.save();
game.camera();
level.drawFillBGs();
level.exit.draw();
level.enter.draw();
game.draw.powerUp();
mobs.draw();
game.draw.cons();
game.draw.body();
mobs.loop();
mobs.healthBar();
mech.draw();
mech.hold();
level.drawFills();
game.draw.drawMapPath();
b.fire();
b.bulletActions();
game.drawCircle();
ctx.restore();
game.drawCursor();
},
mouse: {
x: canvas.width / 2,
y: canvas.height / 2
},
mouseInGame: {
x: 0,
y: 0
},
g: 0.001,
onTitlePage: true,
paused: false,
isChoosing: false,
testing: false, //testing mode: shows wire frame and some variables
cycle: 0, //total cycles, 60 per second
fpsCap: null, //limits frames per second to 144/2=72, on most monitors the fps is capped at 60fps by the hardware
fpsCapDefault: 72, //use to change fpsCap back to normal after a hit from a mob
isEasyToAimMode: true, //removes power ups that don't work well with a track pad
cyclePaused: 0,
fallHeight: 3000, //below this y position the player dies
lastTimeStamp: 0, //tracks time stamps for measuring delta
delta: 1000 / 60, //speed of game engine //looks like it has to be 16 to match player input
buttonCD: 0,
isBodyDamage: true,
levelsCleared: 0,
difficultyMode: 1,
isEasyMode: false,
difficulty: 0,
dmgScale: null, //set in levels.setDifficulty
healScale: 1,
accelScale: null, //set in levels.setDifficulty
CDScale: null, //set in levels.setDifficulty
lookFreqScale: null, //set in levels.setDifficulty
// dropFPS(cap = 40, time = 15) {
// game.fpsCap = cap
// game.fpsInterval = 1000 / game.fpsCap;
// game.defaultFPSCycle = game.cycle + time
// const normalFPS = function () {
// if (game.defaultFPSCycle < game.cycle) {
// game.fpsCap = 72
// game.fpsInterval = 1000 / game.fpsCap;
// } else {
// requestAnimationFrame(normalFPS);
// }
// };
// requestAnimationFrame(normalFPS);
// },
drawCursor() {
const size = 10;
ctx.beginPath();
ctx.moveTo(game.mouse.x - size, game.mouse.y);
ctx.lineTo(game.mouse.x + size, game.mouse.y);
ctx.moveTo(game.mouse.x, game.mouse.y - size);
ctx.lineTo(game.mouse.x, game.mouse.y + size);
ctx.lineWidth = 2;
ctx.strokeStyle = "#000"; //'rgba(0,0,0,0.4)'
ctx.stroke(); // Draw it
},
drawList: [], //so you can draw a first frame of explosions.. I know this is bad
drawTime: 8, //how long circles are drawn. use to push into drawlist.time
mobDmgColor: "rgba(255,0,0,0.7)", //used top push into drawList.color
playerDmgColor: "rgba(0,0,0,0.7)", //used top push into drawList.color
drawCircle() {
//draws a circle for two cycles, used for showing damage mostly
let i = game.drawList.length;
while (i--) {
ctx.beginPath(); //draw circle
ctx.arc(game.drawList[i].x, game.drawList[i].y, game.drawList[i].radius, 0, 2 * Math.PI);
ctx.fillStyle = game.drawList[i].color;
ctx.fill();
if (game.drawList[i].time) {
//remove when timer runs out
game.drawList[i].time--;
} else {
game.drawList.splice(i, 1);
}
}
},
lastLogTime: 0,
lastLogTimeBig: 0,
boldActiveGunHUD() {
if (b.inventory.length > 0) {
for (let i = 0, len = b.inventory.length; i < len; ++i) {
// document.getElementById(b.inventory[i]).style.fontSize = "25px";
document.getElementById(b.inventory[i]).style.opacity = "0.3";
}
// document.getElementById(b.activeGun).style.fontSize = "30px";
if (document.getElementById(b.activeGun)) document.getElementById(b.activeGun).style.opacity = "1";
}
},
updateGunHUD() {
for (let i = 0, len = b.inventory.length; i < len; ++i) {
document.getElementById(b.inventory[i]).innerHTML = b.guns[b.inventory[i]].name + " - " + b.guns[b.inventory[i]].ammo;
}
},
makeGunHUD() {
//remove all nodes
const myNode = document.getElementById("guns");
while (myNode.firstChild) {
myNode.removeChild(myNode.firstChild);
}
//add nodes
for (let i = 0, len = b.inventory.length; i < len; ++i) {
const node = document.createElement("div");
node.setAttribute("id", b.inventory[i]);
let textnode = document.createTextNode(b.guns[b.inventory[i]].name + " - " + b.guns[b.inventory[i]].ammo);
node.appendChild(textnode);
document.getElementById("guns").appendChild(node);
}
game.boldActiveGunHUD();
},
updateModHUD() {
let text = ""
for (let i = 0, len = b.mods.length; i < len; i++) { //add mods
if (b.mods[i].count > 0) {
if (text) text += "<br>" //add a new line, but not on the first line
text += b.mods[i].name
if (b.mods[i].count > 1) text += ` (${b.mods[i].count}x)`
}
}
document.getElementById("mods").innerHTML = text
},
replaceTextLog: true,
// <!-- <path d="M832.41,106.64 V323.55 H651.57 V256.64 c0-82.5,67.5-150,150-150 Z" fill="#789" stroke="none" />
// <path d="M827,112 h30 a140,140,0,0,1,140,140 v68 h-167 z" fill="#7ce" stroke="none" /> -->
SVGleftMouse: '<svg viewBox="750 0 200 765" class="mouse-icon" width="40px" height = "60px" stroke-linecap="round" stroke-linejoin="round" stroke-width="25px" stroke="#000" fill="none"> <path fill="#fff" stroke="none" d="M827,112 h30 a140,140,0,0,1,140,140 v268 a140,140,0,0,1-140,140 h-60 a140,140,0,0,1-140-140v-268 a140,140,0,0,1,140-140h60" /> <path d="M832.41,106.64 V323.55 H651.57 V256.64 c0-82.5,67.5-150,150-150 Z" fill="#149" stroke="none" /> <path fill="none" d="M827,112 h30 a140,140,0,0,1,140,140 v268 a140,140,0,0,1-140,140 h-60 a140,140,0,0,1-140-140v-268 a140,140,0,0,1,140-140h60" /> <path d="M657 317 h 340 h-170 v-207" /> <ellipse fill="#fff" cx="827.57" cy="218.64" rx="29" ry="68" /> </svg>',
SVGrightMouse: '<svg viewBox="750 0 200 765" class="mouse-icon" width="40px" height = "60px" stroke-linecap="round" stroke-linejoin="round" stroke-width="25px" stroke="#000" fill="none"> <path fill="#fff" stroke="none" d="M827,112 h30 a140,140,0,0,1,140,140 v268 a140,140,0,0,1-140,140 h-60 a140,140,0,0,1-140-140v-268 a140,140,0,0,1,140-140h60" /> <path d="M827,112 h30 a140,140,0,0,1,140,140 v68 h-167 z" fill="#0cf" stroke="none" /> <path fill="none" d="M827,112 h30 a140,140,0,0,1,140,140 v268 a140,140,0,0,1-140,140 h-60 a140,140,0,0,1-140-140v-268 a140,140,0,0,1,140-140h60" /> <path d="M657 317 h 340 h-170 v-207" /> <ellipse fill="#fff" cx="827.57" cy="218.64" rx="29" ry="68" /> </svg>',
makeTextLog(text, time = 180) {
if (game.replaceTextLog) {
document.getElementById("text-log").innerHTML = text;
document.getElementById("text-log").style.opacity = 1;
game.lastLogTime = mech.cycle + time;
}
},
textLog() {
if (game.lastLogTime && game.lastLogTime < mech.cycle) {
game.lastLogTime = 0;
game.replaceTextLog = true
// document.getElementById("text-log").innerHTML = " ";
document.getElementById("text-log").style.opacity = 0;
}
},
nextGun() {
if (b.inventory.length > 0) {
b.inventoryGun++;
if (b.inventoryGun > b.inventory.length - 1) b.inventoryGun = 0;
game.switchGun();
}
},
previousGun() {
if (b.inventory.length > 0) {
b.inventoryGun--;
if (b.inventoryGun < 0) b.inventoryGun = b.inventory.length - 1;
game.switchGun();
}
},
switchGun() {
if (b.modNoAmmo) b.modNoAmmo = 1 //this prevents hacking the mod by switching guns
b.activeGun = b.inventory[b.inventoryGun];
game.updateGunHUD();
game.boldActiveGunHUD();
// mech.drop();
},
// tips = [
// "You can throw blocks at dangerous speeds by holding the right mouse or spacebar.",
// "You can use your field to block damage. (right mouse or spacebar)",
// "Explosive weapons, like the grenade are good at clearing groups.",
// "Larger and faster bullets do more damage.",
// "You take more damage from colliding with larger baddies",
// "Holding large blocks slows down the player.",
// "holding blocks prevents the field energy from regenerating.",
// `There are ${mech.fieldUpgrades.length-1} possible field upgrades.`,
// `There are ${b.guns.length} different guns.`,
// "You keep field upgrades after you die.",
// "Unique level bosses always drop a field upgrade if you don't have one.",
// "You jump higher if you hold down the jump button.",
// "Crouching while firing makes bullets go faster, but slows the rate of fire.",
// ]
keyPress() { //runs on key down event
if (keys[189]) {
// - key
game.isAutoZoom = false;
game.zoomScale /= 0.9;
game.setZoom();
} else if (keys[187]) {
// = key
game.isAutoZoom = false;
game.zoomScale *= 0.9;
game.setZoom();
}
//full screen toggle
// if (keys[13]) {
// //enter key
// var doc = window.document;
// var docEl = doc.documentElement;
// var requestFullScreen = docEl.requestFullscreen || docEl.mozRequestFullScreen || docEl.webkitRequestFullScreen || docEl.msRequestFullscreen;
// var cancelFullScreen = doc.exitFullscreen || doc.mozCancelFullScreen || doc.webkitExitFullscreen || doc.msExitFullscreen;
// if (!doc.fullscreenElement && !doc.mozFullScreenElement && !doc.webkitFullscreenElement && !doc.msFullscreenElement) {
// requestFullScreen.call(docEl);
// } else {
// cancelFullScreen.call(doc);
// }
// setupCanvas();
// }
if (keys[69]) {
// e swap to next active gun
game.nextGun();
} else if (keys[81]) {
//q swap to previous active gun
game.previousGun();
}
if (keys[80] && !game.isChoosing) { //p for pause
if (game.paused) {
build.unPauseGrid()
game.paused = false;
level.levelAnnounce();
document.body.style.cursor = "none";
requestAnimationFrame(cycle);
} else {
game.paused = true;
game.replaceTextLog = true;
// game.makeTextLog("<h1>PAUSED</h1>", 1);
//display grid
// document.title = "PAUSED: press P to resume";
build.pauseGrid()
document.body.style.cursor = "auto";
}
}
//toggle testing mode
if (keys[84]) {
// 84 = t
if (game.testing) {
game.testing = false;
game.loop = game.normalLoop
} else {
game.testing = true;
game.loop = game.testingLoop
}
}
//in testing mode
if (game.testing) {
if (keys[49]) { // give power ups with 1
powerUps.spawn(game.mouseInGame.x, game.mouseInGame.y, "heal");
} else if (keys[50]) { // 2
powerUps.spawn(game.mouseInGame.x, game.mouseInGame.y, "ammo");
} else if (keys[51]) { // 3
powerUps.spawn(game.mouseInGame.x, game.mouseInGame.y, "gun");
} else if (keys[52]) { // 4
powerUps.spawn(game.mouseInGame.x, game.mouseInGame.y, "field");
} else if (keys[53]) { // 5
powerUps.spawn(game.mouseInGame.x, game.mouseInGame.y, "mod");
} else if (keys[54]) { // 6 spawn mob
const pick = spawn.fullPickList[Math.floor(Math.random() * spawn.fullPickList.length)];
spawn.allowShields = false;
spawn[pick](game.mouseInGame.x, game.mouseInGame.y);
spawn.allowShields = true;
} else if (keys[55]) { // 7 spawn body
index = body.length
spawn.bodyRect(game.mouseInGame.x, game.mouseInGame.y, 50, 50);
body[index].collisionFilter.category = cat.body;
body[index].collisionFilter.mask = cat.player | cat.map | cat.body | cat.bullet | cat.mob | cat.mobBullet
body[index].classType = "body";
World.add(engine.world, body[index]); //add to world
} else if (keys[70]) { //cycle fields with F
const mode = (mech.fieldMode === mech.fieldUpgrades.length - 1) ? 0 : mech.fieldMode + 1
mech.setField(mode)
} else if (keys[71]) { // give all guns with G
b.giveGuns("all", 1000)
} else if (keys[72]) { // heal with H
mech.addHealth(Infinity)
mech.energy = mech.fieldEnergyMax;
} else if (keys[89]) { //add mods with y
b.giveMod()
} else if (keys[82]) { // teleport to mouse with R
Matter.Body.setPosition(player, game.mouseInGame);
Matter.Body.setVelocity(player, {
x: 0,
y: 0
});
// game.noCameraScroll()
} else if (keys[85]) { // next level with U
level.zoneActions.nextLevel();
}
}
},
zoom: null,
zoomScale: 1000,
isAutoZoom: true,
setZoom(zoomScale = game.zoomScale) { //use in window resize in index.js
game.zoomScale = zoomScale
game.zoom = canvas.height / zoomScale; //sets starting zoom scale
},
noCameraScroll() {
// makes the camera not scroll after changing locations
mech.pos.x = player.position.x;
mech.pos.y = playerBody.position.y - mech.yOff;
const scale = 0.8;
mech.transSmoothX = canvas.width2 - mech.pos.x - (game.mouse.x - canvas.width2) * scale;
mech.transSmoothY = canvas.height2 - mech.pos.y - (game.mouse.y - canvas.height2) * scale;
mech.transX += (mech.transSmoothX - mech.transX) * 1;
mech.transY += (mech.transSmoothY - mech.transY) * 1;
},
zoomTransition(newZoomScale, step = 2) {
if (game.isAutoZoom) {
const isBigger = (newZoomScale - game.zoomScale > 0) ? true : false;
requestAnimationFrame(zLoop);
const currentLevel = level.onLevel
function zLoop() {
if (currentLevel !== level.onLevel || game.isAutoZoom === false) return //stop the zoom if player goes to a new level
if (isBigger) {
game.zoomScale += step
if (game.zoomScale >= newZoomScale) {
game.setZoom(newZoomScale);
return
}
} else {
game.zoomScale -= step
if (game.zoomScale <= newZoomScale) {
game.setZoom(newZoomScale);
return
}
}
game.setZoom();
requestAnimationFrame(zLoop);
}
}
},
camera() {
ctx.save();
ctx.translate(canvas.width2, canvas.height2); //center
ctx.scale(game.zoom, game.zoom); //zoom in once centered
ctx.translate(-canvas.width2 + mech.transX, -canvas.height2 + mech.transY); //translate
//calculate in game mouse position by undoing the zoom and translations
game.mouseInGame.x = (game.mouse.x - canvas.width2) / game.zoom + canvas.width2 - mech.transX;
game.mouseInGame.y = (game.mouse.y - canvas.height2) / game.zoom + canvas.height2 - mech.transY;
},
restoreCamera() {
ctx.restore();
},
zoomInFactor: 0,
startZoomIn(time = 180) {
game.zoom = 0;
let count = 0;
requestAnimationFrame(zLoop);
function zLoop() {
game.zoom += canvas.height / game.zoomScale / time;
count++;
if (count < time) {
requestAnimationFrame(zLoop);
} else {
game.setZoom();
}
}
},
wipe() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
},
gravity() {
function addGravity(bodies, magnitude) {
for (var i = 0; i < bodies.length; i++) {
bodies[i].force.y += bodies[i].mass * magnitude;
}
}
addGravity(powerUp, game.g);
addGravity(body, game.g);
player.force.y += player.mass * mech.gravity;
},
reset() { //run on first run, and each later run after you die
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.setupAllMods(); //sets mods to default values
game.updateModHUD();
mech.maxHealth = 1
mech.fieldEnergyMax = 1
game.paused = false;
engine.timing.timeScale = 1;
game.fpsCap = game.fpsCapDefault;
game.isAutoZoom = true;
game.makeGunHUD();
mech.drop();
mech.holdingTarget = null
mech.addHealth(Infinity);
mech.alive = true;
level.onLevel = 0;
level.levelsCleared = 0;
//resetting difficulty
game.dmgScale = 1;
b.dmgScale = 0.7;
game.accelScale = 1;
game.lookFreqScale = 1;
game.CDScale = 1;
game.difficulty = 0;
game.difficultyMode = Number(document.getElementById("difficulty-select").value)
level.isBuildRun = false;
if (game.difficultyMode === 0) {
game.isEasyMode = true;
game.difficultyMode = 1
level.difficultyDecrease(6); //if this stops being -6 change in build.calculateCustomDifficulty()
}
if (game.difficultyMode === 4) level.difficultyIncrease(6)
game.clearNow = true;
document.getElementById("text-log").style.opacity = 0;
document.getElementById("fade-out").style.opacity = 0;
document.title = "n-gon";
//set to default field
mech.fieldMode = 0;
game.replaceTextLog = true;
game.makeTextLog(`${game.SVGrightMouse}<strong style='font-size:30px;'> ${mech.fieldUpgrades[mech.fieldMode].name}</strong><br><span class='faded'></span><br>${mech.fieldUpgrades[mech.fieldMode].description}`, 600);
mech.setField(mech.fieldMode)
},
firstRun: true,
splashReturn() {
game.onTitlePage = true;
// document.getElementById('splash').onclick = 'run(this)';
document.getElementById("splash").onclick = function () {
game.startGame();
};
document.getElementById("choose-grid").style.display = "none"
document.getElementById("info").style.display = "inline";
document.getElementById("build-button").style.display = "inline"
document.getElementById("build-grid").style.display = "none"
document.getElementById("pause-grid-left").style.display = "none"
document.getElementById("pause-grid-right").style.display = "none"
document.getElementById("splash").style.display = "inline";
document.getElementById("dmg").style.display = "none";
document.getElementById("health-bg").style.display = "none";
document.body.style.cursor = "auto";
},
fpsInterval: 0, //set in startGame
then: null,
startGame() {
if (!level.isBuildRun) { //if a build run logic flow returns to "build-button").addEventListener
document.body.style.cursor = "none";
document.body.style.overflow = "hidden"
}
game.onTitlePage = false;
document.getElementById("choose-grid").style.display = "none"
document.getElementById("build-grid").style.display = "none"
document.getElementById("info").style.display = "none";
document.getElementById("build-button").style.display = "none";
document.getElementById("splash").onclick = null; //removes the onclick effect so the function only runs once
document.getElementById("splash").style.display = "none"; //hides the element that spawned the function
document.getElementById("dmg").style.display = "inline";
document.getElementById("health-bg").style.display = "inline";
// window.onmousedown = function (e) {
// //mouse up event in set in index.js
// // game.mouseDown = true;
// if (e.which === 3) {
// game.mouseDownRight = true;
// } else {
// game.mouseDown = true;
// }
// // keep this disabled unless building maps
// // if (!game.mouseDown){
// // game.getCoords.pos1.x = Math.round(game.mouseInGame.x / 25) * 25;
// // game.getCoords.pos1.y = Math.round(game.mouseInGame.y / 25) * 25;
// // }
// // mech.throwBlock();
// };
if (game.firstRun) {
mech.spawn(); //spawns the player
b.setupAllMods(); //doesn't run on reset so that gun mods carry over to new runs
function shuffle(array) {
var currentIndex = array.length,
temporaryValue,
randomIndex;
// While there remain elements to shuffle...
while (0 !== currentIndex) {
// Pick a remaining element...
randomIndex = Math.floor(Math.random() * currentIndex);
currentIndex -= 1;
// And swap it with the current element.
temporaryValue = array[currentIndex];
array[currentIndex] = array[randomIndex];
array[randomIndex] = temporaryValue;
}
return array;
}
level.levels = shuffle(level.levels); //shuffles order of maps
level.levels.unshift("bosses"); //add bosses level to the end of the randomized levels list
}
game.reset();
game.firstRun = false;
//setup FPS cap
game.fpsInterval = 1000 / game.fpsCap;
game.then = Date.now();
requestAnimationFrame(cycle); //starts game loop
},
clearNow: false,
clearMap() {
if (b.isModMineAmmoBack) {
let count = 0;
for (i = 0, len = bullet.length; i < len; i++) { //count mines left on map
if (bullet[i].bulletType === "mine") count++
}
for (i = 0, len = b.guns.length; i < len; i++) { //find which gun is mine
if (b.guns[i].name === "mine") {
b.guns[i].ammo += count
game.updateGunHUD();
break;
}
}
}
//if player is holding something this remembers it before it gets deleted
let holdTarget;
if (mech.holdingTarget) {
holdTarget = mech.holdingTarget;
}
mech.fireCDcycle = 0
mech.drop();
level.fill = [];
level.fillBG = [];
level.zones = [];
level.queryList = [];
game.drawList = [];
function removeAll(array) {
for (let i = 0; i < array.length; ++i) Matter.World.remove(engine.world, array[i]);
}
removeAll(map);
map = [];
removeAll(body);
body = [];
removeAll(mob);
mob = [];
removeAll(powerUp);
powerUp = [];
removeAll(cons);
cons = [];
removeAll(consBB);
consBB = [];
removeAll(bullet);
bullet = [];
// if player was holding something this makes a new copy to hold
if (holdTarget) {
len = body.length;
body[len] = Matter.Bodies.fromVertices(0, 0, holdTarget.vertices, {
friction: holdTarget.friction,
frictionAir: holdTarget.frictionAir,
frictionStatic: holdTarget.frictionStatic
});
mech.holdingTarget = body[len];
}
},
getCoords: {
//used when building maps, outputs a draw rect command to console, only works in testing mode
pos1: {
x: 0,
y: 0
},
pos2: {
x: 0,
y: 0
},
out() {
if (keys[49]) {
game.getCoords.pos1.x = Math.round(game.mouseInGame.x / 25) * 25;
game.getCoords.pos1.y = Math.round(game.mouseInGame.y / 25) * 25;
}
if (keys[50]) {
//press 1 in the top left; press 2 in the bottom right;copy command from console
game.getCoords.pos2.x = Math.round(game.mouseInGame.x / 25) * 25;
game.getCoords.pos2.y = Math.round(game.mouseInGame.y / 25) * 25;
window.getSelection().removeAllRanges();
var range = document.createRange();
range.selectNode(document.getElementById("test"));
window.getSelection().addRange(range);
document.execCommand("copy");
window.getSelection().removeAllRanges();
console.log(`spawn.mapRect(${game.getCoords.pos1.x}, ${game.getCoords.pos1.y}, ${game.getCoords.pos2.x - game.getCoords.pos1.x}, ${game.getCoords.pos2.y - game.getCoords.pos1.y}); //`);
}
}
},
checks() {
if (mech.pos.y > game.fallHeight) { // if 4000px deep
if (game.difficultyMode > 2) {
mech.death();
} else {
Matter.Body.setVelocity(player, {
x: 0,
y: 0
});
Matter.Body.setPosition(player, {
x: level.enter.x + 50,
y: level.enter.y - 20
});
mech.energy = 0;
if (game.difficultyMode === 2) mech.damage(0.3);
if (game.difficultyMode === 1) mech.damage(0.1);
}
}
if (!(mech.cycle % 60)) { //once a second
if (mech.lastKillCycle + 300 > mech.cycle) { //effects active for 5 seconds after killing a mob
if (b.isModEnergyRecovery) {
mech.energy += mech.fieldEnergyMax * 0.07
if (mech.energy > mech.fieldEnergyMax) mech.energy = mech.fieldEnergyMax;
}
if (b.isModHealthRecovery) {
mech.addHealth(0.01)
}
if (b.isModEnergyLoss) {
mech.energy = 0.05;
}
} else { //haven't killed a mob in the last 5 seconds
}
if (!(game.cycle % 420)) { //once every 7 seconds
fallCheck = function (who, save = false) {
let i = who.length;
while (i--) {
if (who[i].position.y > game.fallHeight) {
if (save && game.difficultyMode < 2) {
Matter.Body.setVelocity(who[i], {
x: 0,
y: 0
});
Matter.Body.setPosition(who[i], {
x: level.exit.x + 30 * (Math.random() - 0.5),
y: level.exit.y + 30 * (Math.random() - 0.5)
});
} else {
Matter.World.remove(engine.world, who[i]);
who.splice(i, 1);
}
}
}
};
fallCheck(mob);
fallCheck(body);
fallCheck(powerUp, true);
}
}
},
testingOutput() {
ctx.textAlign = "right";
ctx.fillStyle = "#000";
let line = 500;
const x = canvas.width - 5;
ctx.fillText("T: exit testing mode", x, line);
line += 20;
ctx.fillText("Y: give all mods", x, line);
line += 20;
ctx.fillText("R: teleport to mouse", x, line);
line += 20;
ctx.fillText("F: cycle field", x, line);
line += 20;
ctx.fillText("G: give all guns", x, line);
line += 20;
ctx.fillText("H: heal", x, line);
line += 20;
ctx.fillText("U: next level", x, line);
line += 20;
ctx.fillText("1-7: spawn things", x, line);
line += 30;
// ctx.fillText("cycle: " + game.cycle, x, line);
// line += 20;
// ctx.fillText("player cycle: " + mech.cycle, x, line);
// line += 20;
// ctx.fillText("x: " + player.position.x.toFixed(0), x, line);
// line += 20;
// ctx.fillText("y: " + player.position.y.toFixed(0), x, line);
// line += 20;
// ctx.fillText("Vx: " + mech.Vx.toFixed(2), x, line);
// line += 20;
// ctx.fillText("Vy: " + mech.Vy.toFixed(2), x, line);
// line += 20;
// ctx.fillText("Fx: " + player.force.x.toFixed(3), x, line);
// line += 20;
// ctx.fillText("Fy: " + player.force.y.toFixed(3), x, line);
// line += 20;
// ctx.fillText("yOff: " + mech.yOff.toFixed(1), x, line);
// line += 20;
// ctx.fillText("mass: " + player.mass.toFixed(1), x, line);
// line += 20;
// ctx.fillText("onGround: " + mech.onGround, x, line);
// line += 20;
// ctx.fillText("crouch: " + mech.crouch, x, line);
// line += 20;
// ctx.fillText("isHeadClear: " + mech.isHeadClear, x, line);
// line += 20;
// ctx.fillText("frictionAir: " + player.frictionAir.toFixed(3), x, line);
// line += 20;
// ctx.fillText("stepSize: " + mech.stepSize.toFixed(2), x, line);
// line += 20;
// ctx.fillText("zoom: " + game.zoom.toFixed(4), x, line);
// line += 20;
ctx.textAlign = "center";
ctx.fillText(`(${game.mouseInGame.x.toFixed(1)}, ${game.mouseInGame.y.toFixed(1)})`, game.mouse.x, game.mouse.y - 20);
},
draw: {
powerUp() {
// draw power up
// ctx.globalAlpha = 0.4 * Math.sin(mech.cycle * 0.15) + 0.6;
// for (let i = 0, len = powerUp.length; i < len; ++i) {
// let vertices = powerUp[i].vertices;
// ctx.beginPath();
// ctx.moveTo(vertices[0].x, vertices[0].y);
// for (let j = 1; j < vertices.length; j += 1) {
// ctx.lineTo(vertices[j].x, vertices[j].y);
// }
// ctx.lineTo(vertices[0].x, vertices[0].y);
// ctx.fillStyle = powerUp[i].color;
// ctx.fill();
// }
// ctx.globalAlpha = 1;
ctx.globalAlpha = 0.4 * Math.sin(mech.cycle * 0.15) + 0.6;
for (let i = 0, len = powerUp.length; i < len; ++i) {
ctx.beginPath();
ctx.arc(powerUp[i].position.x, powerUp[i].position.y, powerUp[i].size, 0, 2 * Math.PI);
ctx.fillStyle = powerUp[i].color;
ctx.fill();
}
ctx.globalAlpha = 1;
},
// map: function() {
// ctx.beginPath();
// for (let i = 0, len = map.length; i < len; ++i) {
// let vertices = map[i].vertices;
// ctx.moveTo(vertices[0].x, vertices[0].y);
// for (let j = 1; j < vertices.length; j += 1) {
// ctx.lineTo(vertices[j].x, vertices[j].y);
// }
// ctx.lineTo(vertices[0].x, vertices[0].y);
// }
// ctx.fillStyle = "#444";
// ctx.fill();
// },
mapPath: null, //holds the path for the map to speed up drawing
setPaths() {
//runs at each new level to store the path for the map since the map doesn't change
game.draw.mapPath = new Path2D();
for (let i = 0, len = map.length; i < len; ++i) {
let vertices = map[i].vertices;
game.draw.mapPath.moveTo(vertices[0].x, vertices[0].y);
for (let j = 1; j < vertices.length; j += 1) {
game.draw.mapPath.lineTo(vertices[j].x, vertices[j].y);
}
game.draw.mapPath.lineTo(vertices[0].x, vertices[0].y);
}
},
mapFill: "#444",
bodyFill: "rgba(140,140,140,0.85)", //"#999",
bodyStroke: "#222",
drawMapPath() {
ctx.fillStyle = game.draw.mapFill;
ctx.fill(game.draw.mapPath);
},
// seeEdges() {
// const eye = {
// x: mech.pos.x + 20 * Math.cos(mech.angle),
// y: mech.pos.y + 20 * Math.sin(mech.angle)
// };
// //find all vertex nodes in range and in LOS
// findNodes = function (domain, center) {
// let nodes = [];
// for (let i = 0; i < domain.length; ++i) {
// let vertices = domain[i].vertices;
// for (let j = 0, len = vertices.length; j < len; j++) {
// //calculate distance to player
// const dx = vertices[j].x - center.x;
// const dy = vertices[j].y - center.y;
// if (dx * dx + dy * dy < 800 * 800 && Matter.Query.ray(domain, center, vertices[j]).length === 0) {
// nodes.push(vertices[j]);
// }
// }
// }
// return nodes;
// };
// let nodes = findNodes(map, eye);
// //sort node list by angle to player
// nodes.sort(function (a, b) {
// //sub artan2 from player loc
// const dx = a.x - eye.x;
// const dy = a.y - eye.y;
// return Math.atan2(dy, dx) - Math.atan2(dy, dx);
// });
// //draw nodes
// ctx.lineWidth = 2;
// ctx.strokeStyle = "#000";
// ctx.beginPath();
// for (let i = 0; i < nodes.length; ++i) {
// ctx.lineTo(nodes[i].x, nodes[i].y);
// }
// ctx.stroke();
// },
// see() {
// const vertexCollision = function (
// v1,
// v1End,
// domain,
// best = {
// x: null,
// y: null,
// dist2: Infinity,
// who: null,
// v1: null,
// v2: null
// }
// ) {
// for (let i = 0; i < domain.length; ++i) {
// let vertices = domain[i].vertices;
// const len = vertices.length - 1;
// for (let j = 0; j < len; j++) {
// results = game.checkLineIntersection(v1, v1End, vertices[j], vertices[j + 1]);
// if (results.onLine1 && results.onLine2) {
// const dx = v1.x - results.x;
// const dy = v1.y - results.y;
// const dist2 = dx * dx + dy * dy;
// if (dist2 < best.dist2 && (!domain[i].mob || domain[i].alive)) {
// best = {
// x: results.x,
// y: results.y,
// dist2: dist2,
// who: domain[i],
// v1: vertices[j],
// v2: vertices[j + 1]
// };
// }
// }
// }
// results = game.checkLineIntersection(v1, v1End, vertices[0], vertices[len]);
// if (results.onLine1 && results.onLine2) {
// const dx = v1.x - results.x;
// const dy = v1.y - results.y;
// const dist2 = dx * dx + dy * dy;
// if (dist2 < best.dist2 && (!domain[i].mob || domain[i].alive)) {
// best = {
// x: results.x,
// y: results.y,
// dist2: dist2,
// who: domain[i],
// v1: vertices[0],
// v2: vertices[len]
// };
// }
// }
// }
// return best;
// };
// const range = 3000;
// ctx.beginPath();
// for (let i = 0; i < Math.PI * 2; i += Math.PI / 2 / 100) {
// const cosAngle = Math.cos(mech.angle + i);
// const sinAngle = Math.sin(mech.angle + i);
// const start = {
// x: mech.pos.x + 20 * cosAngle,
// y: mech.pos.y + 20 * sinAngle
// };
// const end = {
// x: mech.pos.x + range * cosAngle,
// y: mech.pos.y + range * sinAngle
// };
// let result = vertexCollision(start, end, map);
// result = vertexCollision(start, end, body, result);
// result = vertexCollision(start, end, mob, result);
// if (result.dist2 < range * range) {
// // ctx.arc(result.x, result.y, 2, 0, 2 * Math.PI);
// ctx.lineTo(result.x, result.y);
// } else {
// // ctx.arc(end.x, end.y, 2, 0, 2 * Math.PI);
// ctx.lineTo(end.x, end.y);
// }
// }
// // ctx.lineWidth = 1;
// // ctx.strokeStyle = "#000";
// // ctx.stroke();
// ctx.fillStyle = "rgba(0,0,0,0.3)";
// ctx.fillStyle = "#fff";
// ctx.fill();
// ctx.clip();
// },
body() {
ctx.beginPath();
for (let i = 0, len = body.length; i < len; ++i) {
let vertices = body[i].vertices;
ctx.moveTo(vertices[0].x, vertices[0].y);
for (let j = 1; j < vertices.length; j++) {
ctx.lineTo(vertices[j].x, vertices[j].y);
}
ctx.lineTo(vertices[0].x, vertices[0].y);
}
ctx.lineWidth = 2;
ctx.fillStyle = game.draw.bodyFill;
ctx.fill();
ctx.strokeStyle = game.draw.bodyStroke;
ctx.stroke();
},
cons() {
ctx.beginPath();
for (let i = 0, len = cons.length; i < len; ++i) {
ctx.moveTo(cons[i].pointA.x, cons[i].pointA.y);
ctx.lineTo(cons[i].bodyB.position.x, cons[i].bodyB.position.y);
}
for (let i = 0, len = consBB.length; i < len; ++i) {
ctx.moveTo(consBB[i].bodyA.position.x, consBB[i].bodyA.position.y);
ctx.lineTo(consBB[i].bodyB.position.x, consBB[i].bodyB.position.y);
}
ctx.lineWidth = 2;
// ctx.strokeStyle = "#999";
ctx.strokeStyle = "rgba(0,0,0,0.15)";
ctx.stroke();
},
wireFrame() {
ctx.textAlign = "center";
ctx.textBaseline = "middle";
ctx.fillStyle = "#999";
const bodies = Composite.allBodies(engine.world);
ctx.beginPath();
for (let i = 0; i < bodies.length; ++i) {
//ctx.fillText(bodies[i].id,bodies[i].position.x,bodies[i].position.y); //shows the id of every body
let vertices = bodies[i].vertices;
ctx.moveTo(vertices[0].x, vertices[0].y);
for (let j = 1; j < vertices.length; j += 1) {
ctx.lineTo(vertices[j].x, vertices[j].y);
}
ctx.lineTo(vertices[0].x, vertices[0].y);
}
ctx.lineWidth = 1;
ctx.strokeStyle = "#000";
ctx.stroke();
},
testing() {
//zones
ctx.beginPath();
for (let i = 0, len = level.zones.length; i < len; ++i) {
ctx.rect(level.zones[i].x1, level.zones[i].y1 + 70, level.zones[i].x2 - level.zones[i].x1, level.zones[i].y2 - level.zones[i].y1);
}
ctx.fillStyle = "rgba(0, 255, 0, 0.3)";
ctx.fill();
//query zones
ctx.beginPath();
for (let i = 0, len = level.queryList.length; i < len; ++i) {
ctx.rect(
level.queryList[i].bounds.max.x,
level.queryList[i].bounds.max.y,
level.queryList[i].bounds.min.x - level.queryList[i].bounds.max.x,
level.queryList[i].bounds.min.y - level.queryList[i].bounds.max.y
);
}
ctx.fillStyle = "rgba(0, 0, 255, 0.2)";
ctx.fill();
//jump
ctx.beginPath();
let bodyDraw = jumpSensor.vertices;
ctx.moveTo(bodyDraw[0].x, bodyDraw[0].y);
for (let j = 1; j < bodyDraw.length; ++j) {
ctx.lineTo(bodyDraw[j].x, bodyDraw[j].y);
}
ctx.lineTo(bodyDraw[0].x, bodyDraw[0].y);
ctx.fillStyle = "rgba(255, 0, 0, 0.3)";
ctx.fill();
ctx.strokeStyle = "#000";
ctx.stroke();
//main body
ctx.beginPath();
bodyDraw = playerBody.vertices;
ctx.moveTo(bodyDraw[0].x, bodyDraw[0].y);
for (let j = 1; j < bodyDraw.length; ++j) {
ctx.lineTo(bodyDraw[j].x, bodyDraw[j].y);
}
ctx.lineTo(bodyDraw[0].x, bodyDraw[0].y);
ctx.fillStyle = "rgba(0, 255, 255, 0.3)";
ctx.fill();
ctx.stroke();
//head
ctx.beginPath();
bodyDraw = playerHead.vertices;
ctx.moveTo(bodyDraw[0].x, bodyDraw[0].y);
for (let j = 1; j < bodyDraw.length; ++j) {
ctx.lineTo(bodyDraw[j].x, bodyDraw[j].y);
}
ctx.lineTo(bodyDraw[0].x, bodyDraw[0].y);
ctx.fillStyle = "rgba(255, 255, 0, 0.3)";
ctx.fill();
ctx.stroke();
//head sensor
ctx.beginPath();
bodyDraw = headSensor.vertices;
ctx.moveTo(bodyDraw[0].x, bodyDraw[0].y);
for (let j = 1; j < bodyDraw.length; ++j) {
ctx.lineTo(bodyDraw[j].x, bodyDraw[j].y);
}
ctx.lineTo(bodyDraw[0].x, bodyDraw[0].y);
ctx.fillStyle = "rgba(0, 0, 255, 0.3)";
ctx.fill();
ctx.stroke();
}
},
checkLineIntersection(v1, v1End, v2, v2End) {
// if the lines intersect, the result contains the x and y of the intersection (treating the lines as infinite) and booleans for whether line segment 1 or line segment 2 contain the point
let denominator, a, b, numerator1, numerator2;
let result = {
x: null,
y: null,
onLine1: false,
onLine2: false
};
denominator = (v2End.y - v2.y) * (v1End.x - v1.x) - (v2End.x - v2.x) * (v1End.y - v1.y);
if (denominator == 0) {
return result;
}
a = v1.y - v2.y;
b = v1.x - v2.x;
numerator1 = (v2End.x - v2.x) * a - (v2End.y - v2.y) * b;
numerator2 = (v1End.x - v1.x) * a - (v1End.y - v1.y) * b;
a = numerator1 / denominator;
b = numerator2 / denominator;
// if we cast these lines infinitely in both directions, they intersect here:
result.x = v1.x + a * (v1End.x - v1.x);
result.y = v1.y + a * (v1End.y - v1.y);
// if line1 is a segment and line2 is infinite, they intersect if:
if (a > 0 && a < 1) result.onLine1 = true;
// if line2 is a segment and line1 is infinite, they intersect if:
if (b > 0 && b < 1) result.onLine2 = true;
// if line1 and line2 are segments, they intersect if both of the above are true
return result;
},
//was used in level design
buildingUp(e) {
if (game.mouseDown) {
game.getCoords.pos2.x = Math.round(game.mouseInGame.x / 25) * 25;
game.getCoords.pos2.y = Math.round(game.mouseInGame.y / 25) * 25;
let out;
//body rect mode
out = `spawn.mapRect(${game.getCoords.pos1.x}, ${game.getCoords.pos1.y}, ${game.getCoords.pos2.x - game.getCoords.pos1.x}, ${game.getCoords.pos2.y -
game.getCoords.pos1.y});`;
//mob spawn
//out = `spawn.randomMob(${game.getCoords.pos1.x}, ${game.getCoords.pos1.y}, 0.3);`
//draw foreground
//out = `level.fill.push({ x: ${game.getCoords.pos1.x}, y: ${game.getCoords.pos1.y}, width: ${game.getCoords.pos2.x-game.getCoords.pos1.x}, height: ${game.getCoords.pos2.y-game.getCoords.pos1.y}, color: "rgba(0,0,0,0.1)"});`;
//draw background fill
//out = `level.fillBG.push({ x: ${game.getCoords.pos1.x}, y: ${game.getCoords.pos1.y}, width: ${game.getCoords.pos2.x-game.getCoords.pos1.x}, height: ${game.getCoords.pos2.y-game.getCoords.pos1.y}, color: "#ccc"});`;
//svg mode
//out = 'rect x="'+game.getCoords.pos1.x+'" y="'+ game.getCoords.pos1.y+'" width="'+(game.getCoords.pos2.x-game.getCoords.pos1.x)+'" height="'+(game.getCoords.pos2.y-game.getCoords.pos1.y)+'"';
console.log(out);
// document.getElementById("copy-this").innerHTML = out
//
// window.getSelection().removeAllRanges();
// var range = document.createRange();
// range.selectNode(document.getElementById('copy-this'));
// window.getSelection().addRange(range);
// document.execCommand('copy')
// window.getSelection().removeAllRanges();
}
}
};