forked from sent/waves
363 lines
13 KiB
JavaScript
363 lines
13 KiB
JavaScript
let levels = ["Easy 1", "Easy 2", "Easy 3", "Easy 4", "Easy 5", "Easy 6"];
|
|
let levelsShorthand = ["e1", "e2", "e3", "e4", "e5", "e6"];
|
|
let level = "e1";
|
|
let finish = false;
|
|
let oldState;
|
|
let playerSize = 20;
|
|
let speed = 5;
|
|
let mouseX = 0;
|
|
let mouseY = 0;
|
|
let clickX = 0;
|
|
let clickY = 0;
|
|
let playerX = 0;
|
|
let playerY = 0;
|
|
let state = 0;
|
|
let hitFlags = [];
|
|
let arrowRight = new Image();
|
|
arrowRight.src = "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' height='24' viewBox='0 -960 960 960' width='24'%3E%3Cpath fill='%23ffffff' d='m380-300 280-180-280-180v360ZM480-80q-83 0-156-31.5T197-197q-54-54-85.5-127T80-480q0-83 31.5-156T197-763q54-54 127-85.5T480-880q83 0 156 31.5T763-763q54 54 85.5 127T880-480q0 83-31.5 156T763-197q-54 54-127 85.5T480-80Zm0-80q134 0 227-93t93-227q0-134-93-227t-227-93q-134 0-227 93t-93 227q0 134 93 227t227 93Zm0-320Z'/%3E%3C/svg%3E";
|
|
let arrowLeft = new Image();
|
|
arrowLeft.src = "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' height='24' viewBox='0 -960 960 960' width='24' transform='rotate(180)'%3E%3Cpath fill='%23ffffff' d='m380-300 280-180-280-180v360ZM480-80q-83 0-156-31.5T197-197q-54-54-85.5-127T80-480q0-83 31.5-156T197-763q54-54 127-85.5T480-880q83 0 156 31.5T763-763q54 54 85.5 127T880-480q0 83-31.5 156T763-197q-54 54-127 85.5T480-80Zm0-80q134 0 227-93t93-227q0-134-93-227t-227-93q-134 0-227 93t-93 227q0 134 93 227t227 93Zm0-320Z'/%3E%3C/svg%3E";
|
|
let arrowUp = new Image();
|
|
arrowUp.src = "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' height='24' viewBox='0 -960 960 960' width='24' transform='rotate(270)'%3E%3Cpath fill='%23ffffff' d='m380-300 280-180-280-180v360ZM480-80q-83 0-156-31.5T197-197q-54-54-85.5-127T80-480q0-83 31.5-156T197-763q54-54 127-85.5T480-880q83 0 156 31.5T763-763q54 54 85.5 127T880-480q0 83-31.5 156T763-197q-54 54-127 85.5T480-80Zm0-80q134 0 227-93t93-227q0-134-93-227t-227-93q-134 0-227 93t-93 227q0 134 93 227t227 93Zm0-320Z'/%3E%3C/svg%3E";
|
|
let arrowDown = new Image();
|
|
arrowDown.src = "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' height='24' viewBox='0 -960 960 960' width='24' transform='rotate(90)'%3E%3Cpath fill='%23ffffff' d='m380-300 280-180-280-180v360ZM480-80q-83 0-156-31.5T197-197q-54-54-85.5-127T80-480q0-83 31.5-156T197-763q54-54 127-85.5T480-880q83 0 156 31.5T763-763q54 54 85.5 127T880-480q0 83-31.5 156T763-197q-54 54-127 85.5T480-80Zm0-80q134 0 227-93t93-227q0-134-93-227t-227-93q-134 0-227 93t-93 227q0 134 93 227t227 93Zm0-320Z'/%3E%3C/svg%3E";
|
|
let restartBtn = new Image();
|
|
restartBtn.src = "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' height='24' viewBox='0 -960 960 960' width='24'%3E%3Cpath fill='%23ffffff' d='M440-122q-121-15-200.5-105.5T160-440q0-66 26-126.5T260-672l57 57q-38 34-57.5 79T240-440q0 88 56 155.5T440-202v80Zm80 0v-80q87-16 143.5-83T720-440q0-100-70-170t-170-70h-3l44 44-56 56-140-140 140-140 56 56-44 44h3q134 0 227 93t93 227q0 121-79.5 211.5T520-122Z'/%3E%3C/svg%3E";
|
|
let home = new Image();
|
|
home.src = "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' height='24' viewBox='0 -960 960 960' width='24'%3E%3Cpath fill='%23ffffff' d='M240-200h120v-240h240v240h120v-360L480-740 240-560v360Zm-80 80v-480l320-240 320 240v480H520v-240h-80v240H160Zm320-350Z'/%3E%3C/svg%3E";
|
|
|
|
// state: 0 = main page, 1 = levels page, -1 = level
|
|
var frameTime = 0,
|
|
lastLoop = new Date(),
|
|
thisLoop;
|
|
let currentTime,
|
|
firstTime = new Date();
|
|
var filterStrength = 20;
|
|
let keyUp,
|
|
keyDown,
|
|
keyLeft,
|
|
keyRight,
|
|
restart = false;
|
|
let curLevel;
|
|
document.addEventListener("DOMContentLoaded", () => {
|
|
const canvas = document.getElementById("canvas");
|
|
const ctx = canvas.getContext("2d");
|
|
document.addEventListener("mousemove", (e) => {
|
|
mouseX = e.clientX;
|
|
mouseY = e.clientY;
|
|
});
|
|
function draw() {
|
|
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
|
ctx.beginPath();
|
|
ctx.fillStyle = "#111111";
|
|
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
|
stateHandler();
|
|
if (state == -1) {
|
|
drawLevel(ctx);
|
|
handleInput(canvas);
|
|
drawPlayer(ctx);
|
|
timer(ctx);
|
|
controls(ctx);
|
|
}
|
|
if (state == 0) {
|
|
loadMainPage(ctx);
|
|
}
|
|
if (state == 1) {
|
|
loadLevelsPage(ctx);
|
|
}
|
|
|
|
var thisFrameTime = (thisLoop = new Date()) - lastLoop;
|
|
frameTime += (thisFrameTime - frameTime) / filterStrength;
|
|
lastLoop = thisLoop;
|
|
if (restart) {
|
|
restart = false;
|
|
}
|
|
}
|
|
setInterval(draw, 1000 / 30);
|
|
|
|
setInterval(function () {
|
|
// console.log((1000 / frameTime).toFixed(1) + " fps");
|
|
}, 1000);
|
|
});
|
|
function drawPlayer(ctx) {
|
|
ctx.beginPath();
|
|
ctx.arc(playerX, playerY, playerSize, 0, Math.PI * 2);
|
|
ctx.fillStyle = "#0095DD";
|
|
ctx.fill();
|
|
ctx.closePath();
|
|
}
|
|
function timer(ctx) {
|
|
if (!finish) {
|
|
if (restart) {
|
|
time = 0;
|
|
}
|
|
time = Math.round(time + 1000 / 30);
|
|
}
|
|
ctx.fillStyle = "#00000088";
|
|
ctx.font = "bold 30px Arial";
|
|
ctx.fillRect(5, 5, ctx.measureText(time / 1000).width + 10, 30);
|
|
ctx.fillStyle = "white";
|
|
ctx.fillText(time / 1000, 10, 30);
|
|
}
|
|
async function drawLevel(ctx) {
|
|
if (!curLevel || !(curLevel["levelName"] == level)) {
|
|
await downloadLevel();
|
|
restart = true;
|
|
}
|
|
if (!(curLevel.then && typeof curLevel.then === "function")) {
|
|
for (let i = 0; i < curLevel["levelData"]["rectangles"].length; i++) {
|
|
ctx.fillStyle = "red";
|
|
v1 = curLevel["levelData"]["rectangles"][i][0];
|
|
v2 = curLevel["levelData"]["rectangles"][i][1];
|
|
v3 = curLevel["levelData"]["rectangles"][i][2];
|
|
v4 = curLevel["levelData"]["rectangles"][i][3];
|
|
ctx.fillRect(v1, v2, v3, v4);
|
|
if (RectCircleColliding({ x: playerX, y: playerY, r: playerSize }, { x: v1, y: v2, w: v3, h: v4 })) {
|
|
restart = true;
|
|
}
|
|
}
|
|
ctx.fillStyle = "blue";
|
|
flag1 = curLevel["levelData"]["endFlag"][0];
|
|
flag2 = curLevel["levelData"]["endFlag"][1];
|
|
flag3 = curLevel["levelData"]["endFlag"][2];
|
|
flag4 = curLevel["levelData"]["endFlag"][3];
|
|
ctx.fillRect(flag1, flag2, flag3, flag4);
|
|
if (RectCircleColliding({ x: playerX, y: playerY, r: playerSize }, { x: flag1, y: flag2, w: flag3, h: flag4 })) {
|
|
finish = true;
|
|
}
|
|
}
|
|
if (curLevel["levelData"]["teleportPortal"]) {
|
|
for (let i = 0; i < curLevel["levelData"]["teleportPortal"].length; i++) {
|
|
ctx.fillStyle = "green";
|
|
v1 = curLevel["levelData"]["teleportPortal"][i][0][0];
|
|
v2 = curLevel["levelData"]["teleportPortal"][i][0][1];
|
|
v3 = curLevel["levelData"]["teleportPortal"][i][0][2];
|
|
v4 = curLevel["levelData"]["teleportPortal"][i][0][3];
|
|
ctx.fillRect(v1, v2, v3, v4);
|
|
if (RectCircleColliding({ x: playerX, y: playerY, r: playerSize }, { x: v1, y: v2, w: v3, h: v4 })) {
|
|
playerX = curLevel["levelData"]["teleportPortal"][0][2][0];
|
|
playerY = curLevel["levelData"]["teleportPortal"][0][2][1];
|
|
}
|
|
v1 = curLevel["levelData"]["teleportPortal"][i][1][0];
|
|
v2 = curLevel["levelData"]["teleportPortal"][i][1][1];
|
|
v3 = curLevel["levelData"]["teleportPortal"][i][1][2];
|
|
v4 = curLevel["levelData"]["teleportPortal"][i][1][3];
|
|
ctx.fillRect(v1, v2, v3, v4);
|
|
if (RectCircleColliding({ x: playerX, y: playerY, r: playerSize }, { x: v1, y: v2, w: v3, h: v4 })) {
|
|
playerX = curLevel["levelData"]["teleportPortal"][0][2][0];
|
|
playerY = curLevel["levelData"]["teleportPortal"][0][2][1];
|
|
}
|
|
}
|
|
}
|
|
for (let i = 0; i < curLevel["levelData"]["endFlag"].length; i++) {
|
|
if (hitFlags.includes(i)) {
|
|
ctx.fillStyle = "darkblue";
|
|
} else {
|
|
ctx.fillStyle = "blue";
|
|
}
|
|
flag1 = curLevel["levelData"]["endFlag"][i][0];
|
|
flag2 = curLevel["levelData"]["endFlag"][i][1];
|
|
flag3 = curLevel["levelData"]["endFlag"][i][2];
|
|
flag4 = curLevel["levelData"]["endFlag"][i][3];
|
|
ctx.fillRect(flag1, flag2, flag3, flag4);
|
|
if (RectCircleColliding({ x: playerX, y: playerY, r: playerSize }, { x: flag1, y: flag2, w: flag3, h: flag4 }) && !hitFlags.includes(i)) {
|
|
hitFlags.push(i);
|
|
if (hitFlags.length == curLevel["levelData"]["endFlag"].length) {
|
|
finish = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
document.addEventListener("keydown", (e) => {
|
|
if (e.key == "w" || e.key == "ArrowUp") {
|
|
keyUp = true;
|
|
}
|
|
if (e.key == "s" || e.key == "ArrowDown") {
|
|
keyDown = true;
|
|
}
|
|
if (e.key == "a" || e.key == "ArrowLeft") {
|
|
keyLeft = true;
|
|
}
|
|
if (e.key == "d" || e.key == "ArrowRight") {
|
|
keyRight = true;
|
|
}
|
|
if (e.key == "r") {
|
|
restart = true;
|
|
}
|
|
});
|
|
document.addEventListener("keyup", (e) => {
|
|
if (e.key == "w" || e.key == "ArrowUp") {
|
|
keyUp = false;
|
|
}
|
|
if (e.key == "s" || e.key == "ArrowDown") {
|
|
keyDown = false;
|
|
}
|
|
if (e.key == "a" || e.key == "ArrowLeft") {
|
|
keyLeft = false;
|
|
}
|
|
if (e.key == "d" || e.key == "ArrowRight") {
|
|
keyRight = false;
|
|
}
|
|
});
|
|
|
|
function handleInput(canvas) {
|
|
if (restart && curLevel) {
|
|
playerX = curLevel["levelData"]["startPos"][0] ?? 0;
|
|
playerY = curLevel["levelData"]["startPos"][1] ?? 0;
|
|
hitFlags = [];
|
|
finish = false;
|
|
}
|
|
if (!finish) {
|
|
if (keyUp) {
|
|
playerY = playerY -= speed;
|
|
}
|
|
if (keyDown) {
|
|
playerY = playerY += speed;
|
|
}
|
|
if (keyLeft) {
|
|
playerX = playerX -= speed;
|
|
}
|
|
if (keyRight) {
|
|
playerX = playerX += speed;
|
|
}
|
|
if (playerY > canvas.height - playerSize) {
|
|
playerY = canvas.height - playerSize;
|
|
}
|
|
if (playerY < playerSize) {
|
|
playerY = playerSize;
|
|
}
|
|
if (playerX > canvas.width - playerSize) {
|
|
playerX = canvas.width - playerSize;
|
|
}
|
|
if (playerX < playerSize) {
|
|
playerX = playerSize;
|
|
}
|
|
}
|
|
}
|
|
async function downloadLevel() {
|
|
let response = await fetch(`levels/${level}.json`);
|
|
curLevel = await response.json();
|
|
}
|
|
|
|
function RectCircleColliding(circle, rect) {
|
|
var distX = Math.abs(circle.x - rect.x - rect.w / 2);
|
|
var distY = Math.abs(circle.y - rect.y - rect.h / 2);
|
|
|
|
if (distX > rect.w / 2 + circle.r) {
|
|
return false;
|
|
}
|
|
if (distY > rect.h / 2 + circle.r) {
|
|
return false;
|
|
}
|
|
|
|
if (distX <= rect.w / 2) {
|
|
return true;
|
|
}
|
|
if (distY <= rect.h / 2) {
|
|
return true;
|
|
}
|
|
|
|
var dx = distX - rect.w / 2;
|
|
var dy = distY - rect.h / 2;
|
|
return dx * dx + dy * dy <= circle.r * circle.r;
|
|
}
|
|
function stateHandler() {
|
|
if (!(state == oldState) && state == -1) {
|
|
restart = true;
|
|
}
|
|
oldState = state;
|
|
}
|
|
function loadMainPage(ctx) {
|
|
ctx.lineWidth = 5;
|
|
ctx.fillStyle = "#101010";
|
|
ctx.strokeStyle = "#7a7a7a";
|
|
ctx.rect(150, 300, 300, 100);
|
|
ctx.fill();
|
|
ctx.stroke();
|
|
ctx.font = "bold 50px Arial";
|
|
ctx.fillStyle = "white";
|
|
ctx.fillText("Levels", 220, 368);
|
|
if (150 < clickX && clickX < 450 && 300 < clickY && clickY < 400) {
|
|
state = 1;
|
|
}
|
|
}
|
|
|
|
function loadLevelsPage(ctx) {
|
|
ctx.lineWidth = 5;
|
|
for (let i = 0; i < levels.length; i++) {
|
|
ctx.fillStyle = "#101010";
|
|
ctx.strokeStyle = "#7a7a7a";
|
|
ctx.rect(50 + (i % 3) * 195, 50 + Math.floor(i / 3) * 150, 150, 100);
|
|
ctx.fill();
|
|
ctx.stroke();
|
|
if (50 + (i % 3) * 195 < clickX && clickX < 200 + (i % 3) * 195 && 50 + Math.floor(i / 3) * 150 < clickY && clickY < 150 + Math.floor(i / 3) * 150) {
|
|
state = -1;
|
|
level = levelsShorthand[i];
|
|
}
|
|
}
|
|
for (let i = 0; i < levels.length; i++) {
|
|
ctx.font = "bold 40px Arial";
|
|
ctx.fillStyle = "white";
|
|
ctx.fillText(levels[i], 60 + (i % 3) * 195, 115 + Math.floor(i / 3) * 150);
|
|
}
|
|
}
|
|
document.addEventListener("mousedown", (e) => {
|
|
clickX = e.clientX;
|
|
clickY = e.clientY;
|
|
if (580 < clickX && clickX < 640 && 0 < clickY && clickY < 60) {
|
|
restart = true;
|
|
}
|
|
if (580 < clickX && clickX < 640 && 60 < clickY && clickY < 120) {
|
|
state = 0;
|
|
}
|
|
});
|
|
document.addEventListener("touchstart", (e) => {
|
|
clickX = e.touches[0].clientX;
|
|
clickY = e.touches[0].clientY;
|
|
if (state == -1) {
|
|
if (580 < clickX && clickX < 640 && 420 < clickY && clickY < 480) {
|
|
keyRight = true;
|
|
}
|
|
if (480 < clickX && clickX < 540 && 420 < clickY && clickY < 480) {
|
|
keyLeft = true;
|
|
}
|
|
if (530 < clickX && clickX < 590 && 370 < clickY && clickY < 430) {
|
|
keyUp = true;
|
|
}
|
|
if (530 < clickX && clickX < 590 && 420 < clickY && clickY < 480) {
|
|
keyDown = true;
|
|
}
|
|
if (580 < clickX && clickX < 640 && 0 < clickY && clickY < 60) {
|
|
restart = true;
|
|
}
|
|
if (580 < clickX && clickX < 640 && 60 < clickY && clickY < 120) {
|
|
state = 0;
|
|
}
|
|
}
|
|
});
|
|
document.addEventListener("touchend", (e) => {
|
|
if (state == -1) {
|
|
if (580 < clickX && clickX < 640 && 420 < clickY && clickY < 480) {
|
|
keyRight = false;
|
|
}
|
|
if (480 < clickX && clickX < 540 && 420 < clickY && clickY < 480) {
|
|
keyLeft = false;
|
|
}
|
|
if (530 < clickX && clickX < 590 && 370 < clickY && clickY < 430) {
|
|
keyUp = false;
|
|
}
|
|
if (530 < clickX && clickX < 590 && 420 < clickY && clickY < 480) {
|
|
keyDown = false;
|
|
}
|
|
}
|
|
});
|
|
|
|
function controls(ctx) {
|
|
ctx.drawImage(arrowRight, 580, 420, 60, 60);
|
|
ctx.drawImage(arrowLeft, 480, 420, 60, 60);
|
|
ctx.drawImage(arrowUp, 530, 370, 60, 60);
|
|
ctx.drawImage(arrowDown, 530, 420, 60, 60);
|
|
ctx.drawImage(restartBtn, 580, 0, 60, 60);
|
|
ctx.drawImage(home, 580, 60, 60, 60);
|
|
}
|