330 lines
		
	
	
	
		
			10 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			330 lines
		
	
	
	
		
			10 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
const HEX_VALUES = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F"];
 | 
						|
const NUM_ROWS = 9;
 | 
						|
const NUM_COLS = 18;
 | 
						|
const PENALTY = 5000;
 | 
						|
 | 
						|
let solutions = ["", ""];
 | 
						|
let solutionPos = [
 | 
						|
    [1, 1],
 | 
						|
    [1, 1],
 | 
						|
];
 | 
						|
let userSolPos = [
 | 
						|
    [1, 1],
 | 
						|
    [1, 15],
 | 
						|
];
 | 
						|
let gameStarted = false;
 | 
						|
let solved = [false, false];
 | 
						|
let endTime = 0;
 | 
						|
let thread;
 | 
						|
let mistakes = 0;
 | 
						|
let lastBeep = 0;
 | 
						|
let lastUpdateTime = 0;
 | 
						|
let lastBeepTime = 0;
 | 
						|
let gameTable = Array.from({ length: NUM_ROWS }, () => []);
 | 
						|
let lastReseed = Array.from({ length: NUM_ROWS }, () => []);
 | 
						|
 | 
						|
const game = document.getElementById("game");
 | 
						|
const hackingContainer = document.querySelector(".hacking-container");
 | 
						|
const infobox = document.getElementById("infobox");
 | 
						|
const screentext = document.getElementById("screentext");
 | 
						|
const screen = document.getElementById("screen");
 | 
						|
 | 
						|
const resetGameState = () => {
 | 
						|
    hackingContainer.style.display = "none";
 | 
						|
    if (thread) {
 | 
						|
        cancelAnimationFrame(thread);
 | 
						|
        thread = null;
 | 
						|
    }
 | 
						|
    solutions = ["", ""];
 | 
						|
    solutionPos = [
 | 
						|
        [1, 1],
 | 
						|
        [1, 1],
 | 
						|
    ];
 | 
						|
    userSolPos = [
 | 
						|
        [1, 1],
 | 
						|
        [1, 15],
 | 
						|
    ];
 | 
						|
    solved = [false, false];
 | 
						|
    gameStarted = false;
 | 
						|
    mistakes = 0;
 | 
						|
    document.querySelectorAll(".sol2").forEach((el) => el.classList.remove("sol2"));
 | 
						|
};
 | 
						|
 | 
						|
const getRandomInt = (min, max) => Math.floor(Math.random() * (max - min + 1)) + min;
 | 
						|
 | 
						|
const wrongSolution = () => {
 | 
						|
    mistakes++;
 | 
						|
    endTime -= mistakes * PENALTY;
 | 
						|
};
 | 
						|
 | 
						|
const playSound = (snd, vol) => {
 | 
						|
    document.getElementById(snd).load();
 | 
						|
    document.getElementById(snd).volume = vol;
 | 
						|
    document.getElementById(snd).play();
 | 
						|
};
 | 
						|
 | 
						|
const tryFinish = () => {
 | 
						|
    if (solved.every((val) => val)) {
 | 
						|
        document.getElementById("infobox").textContent = "Success";
 | 
						|
        setTimeout(() => {
 | 
						|
            playSound("audiofinish", 1);
 | 
						|
            fetch(`https://${GetParentResourceName()}/hackSuccess`, {
 | 
						|
                method: "POST",
 | 
						|
                headers: { "Content-Type": "application/json" },
 | 
						|
                body: JSON.stringify({}),
 | 
						|
            });
 | 
						|
            resetGameState();
 | 
						|
        }, 2000);
 | 
						|
    }
 | 
						|
};
 | 
						|
 | 
						|
const writeSolution = () => {
 | 
						|
    for (let i = 0; i < solutions[0].length; i++) {
 | 
						|
        const cell1 = document.getElementById(`${solutionPos[0][0]}${solutionPos[0][1] + i}`);
 | 
						|
        if (cell1) {
 | 
						|
            cell1.textContent = solutions[0].charAt(i);
 | 
						|
        }
 | 
						|
 | 
						|
        const cell2 = document.getElementById(`${solutionPos[1][0]}${solutionPos[1][1] + i}`);
 | 
						|
        if (cell2) {
 | 
						|
            cell2.textContent = solutions[1].charAt(i);
 | 
						|
        }
 | 
						|
    }
 | 
						|
};
 | 
						|
 | 
						|
const writeUserSolution = () => {
 | 
						|
    document.querySelectorAll(".sol").forEach((el) => el.classList.remove("sol"));
 | 
						|
    for (let i = 0; i < solutions[0].length; i++) {
 | 
						|
        const el1 = document.getElementById(`${userSolPos[0][0]}${userSolPos[0][1] + i}`);
 | 
						|
        if (el1) {
 | 
						|
            el1.textContent = solutions[0].charAt(i);
 | 
						|
            el1.classList.add("sol");
 | 
						|
        }
 | 
						|
 | 
						|
        const el2 = document.getElementById(`${userSolPos[1][0]}${userSolPos[1][1] + i}`);
 | 
						|
        if (el2) {
 | 
						|
            el2.textContent = solutions[1].charAt(i);
 | 
						|
            el2.classList.add("sol");
 | 
						|
        }
 | 
						|
    }
 | 
						|
};
 | 
						|
 | 
						|
const writeTime = () => {
 | 
						|
    if (!solved.every((val) => val)) {
 | 
						|
        document.getElementById("infobox").textContent = `${((endTime - new Date().getTime()) / 1000.0).toFixed(2)}s`;
 | 
						|
    }
 | 
						|
};
 | 
						|
 | 
						|
const seedTable = (timestamp) => {
 | 
						|
    now = new Date().getTime();
 | 
						|
    if (now >= endTime) {
 | 
						|
        infobox.textContent = "Failure";
 | 
						|
        setTimeout(() => {
 | 
						|
            playSound("audiofail", 1);
 | 
						|
            fetch(`https://${GetParentResourceName()}/hackFail`, {
 | 
						|
                method: "POST",
 | 
						|
                headers: {
 | 
						|
                    "Content-Type": "application/json",
 | 
						|
                },
 | 
						|
                body: JSON.stringify({}),
 | 
						|
            });
 | 
						|
            resetGameState();
 | 
						|
        }, 2000);
 | 
						|
        return;
 | 
						|
    }
 | 
						|
 | 
						|
    writeTime();
 | 
						|
 | 
						|
    if (timestamp - lastUpdateTime > 200) {
 | 
						|
        for (let i = 0; i < NUM_ROWS; i++) {
 | 
						|
            for (let j = 0; j < NUM_COLS; j++) {
 | 
						|
                const cellId = `${i + 1}${j + 1}`;
 | 
						|
                const cell = document.getElementById(cellId);
 | 
						|
                if (cell) {
 | 
						|
                    gameTable[i][j] = HEX_VALUES[getRandomInt(0, 15)];
 | 
						|
                    cell.textContent = gameTable[i][j];
 | 
						|
                }
 | 
						|
            }
 | 
						|
        }
 | 
						|
        writeSolution();
 | 
						|
        writeUserSolution();
 | 
						|
        lastUpdateTime = timestamp;
 | 
						|
    }
 | 
						|
 | 
						|
    if (timestamp - lastBeepTime > 600) {
 | 
						|
        playSound("audiobeep", 0.08);
 | 
						|
        lastBeepTime = timestamp;
 | 
						|
    }
 | 
						|
 | 
						|
    thread = requestAnimationFrame(seedTable);
 | 
						|
};
 | 
						|
 | 
						|
const generateSolutions = (s) => {
 | 
						|
    solutionPos[0] = [getRandomInt(1, 9), getRandomInt(1, 18 - s)];
 | 
						|
    let goodsolution = false;
 | 
						|
    while (!goodsolution) {
 | 
						|
        solutionPos[1] = [getRandomInt(1, 9), getRandomInt(1, 18 - s)];
 | 
						|
        if (solutionPos[0][0] == solutionPos[1][0]) {
 | 
						|
            if (solutionPos[0][1] + s < solutionPos[1][1]) {
 | 
						|
                goodsolution = true;
 | 
						|
            } else if (solutionPos[0][1] > solutionPos[1][1] + s) {
 | 
						|
                goodsolution = true;
 | 
						|
            }
 | 
						|
        } else {
 | 
						|
            goodsolution = true;
 | 
						|
        }
 | 
						|
    }
 | 
						|
    for (let i = 0; i < s; i++) {
 | 
						|
        solutions[0] = solutions[0] + HEX_VALUES[getRandomInt(0, 15)];
 | 
						|
        solutions[1] = solutions[1] + HEX_VALUES[getRandomInt(0, 15)];
 | 
						|
    }
 | 
						|
    userSolPos = [
 | 
						|
        [1, 1],
 | 
						|
        [1, 19 - s],
 | 
						|
    ];
 | 
						|
};
 | 
						|
 | 
						|
const startGame = (solutionsize, timeout) => {
 | 
						|
    initializeGameTable();
 | 
						|
    for (let i = 0; i < NUM_ROWS; i++) {
 | 
						|
        for (let j = 0; j < NUM_COLS; j++) {
 | 
						|
            lastReseed[i][j] = 0;
 | 
						|
        }
 | 
						|
    }
 | 
						|
    generateSolutions(solutionsize);
 | 
						|
    gameStarted = true;
 | 
						|
    endTime = new Date().getTime() + timeout * 1000;
 | 
						|
    if (thread) {
 | 
						|
        cancelAnimationFrame(thread);
 | 
						|
    }
 | 
						|
    thread = requestAnimationFrame(seedTable);
 | 
						|
};
 | 
						|
 | 
						|
const initializeGameTable = () => {
 | 
						|
    const existingTable = document.getElementById("gametable");
 | 
						|
    if (existingTable) {
 | 
						|
        existingTable.remove();
 | 
						|
    }
 | 
						|
    const gametable = document.createElement("table");
 | 
						|
    gametable.id = "gametable";
 | 
						|
 | 
						|
    for (let i = 0; i < 9; i++) {
 | 
						|
        const row = document.createElement("tr");
 | 
						|
        row.setAttribute("id", `row${i + 1}`);
 | 
						|
        gametable.appendChild(row);
 | 
						|
 | 
						|
        for (let j = 0; j < 18; j++) {
 | 
						|
            const cell = document.createElement("td");
 | 
						|
            cell.setAttribute("id", `${i + 1}${j + 1}`);
 | 
						|
            cell.innerHTML = " ";
 | 
						|
            row.appendChild(cell);
 | 
						|
        }
 | 
						|
    }
 | 
						|
    const game = document.getElementById("game");
 | 
						|
    game.appendChild(gametable);
 | 
						|
};
 | 
						|
 | 
						|
document.addEventListener("keydown", (event) => {
 | 
						|
    if (!gameStarted) return;
 | 
						|
 | 
						|
    const key = event.key;
 | 
						|
 | 
						|
    if (gameStarted && key === "Escape") {
 | 
						|
        gameStarted = false;
 | 
						|
        fetch(`https://${GetParentResourceName()}/hackClosed`, {
 | 
						|
            method: "POST",
 | 
						|
            headers: { "Content-Type": "application/json" },
 | 
						|
            body: JSON.stringify({ userCancelled: true }),
 | 
						|
        });
 | 
						|
        resetGameState();
 | 
						|
        return;
 | 
						|
    }
 | 
						|
 | 
						|
    if (!solved[0]) {
 | 
						|
        switch (key) {
 | 
						|
            case "w":
 | 
						|
                if (userSolPos[0][0] > 1) userSolPos[0][0]--;
 | 
						|
                break;
 | 
						|
            case "a":
 | 
						|
                if (userSolPos[0][1] > 1) userSolPos[0][1]--;
 | 
						|
                break;
 | 
						|
            case "s":
 | 
						|
                if (userSolPos[0][0] < NUM_ROWS) userSolPos[0][0]++;
 | 
						|
                break;
 | 
						|
            case "d":
 | 
						|
                if (userSolPos[0][1] < NUM_COLS) userSolPos[0][1]++;
 | 
						|
                break;
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    if (!solved[1]) {
 | 
						|
        switch (key) {
 | 
						|
            case "ArrowUp":
 | 
						|
                if (userSolPos[1][0] > 1) userSolPos[1][0]--;
 | 
						|
                break;
 | 
						|
            case "ArrowLeft":
 | 
						|
                if (userSolPos[1][1] > 1) userSolPos[1][1]--;
 | 
						|
                break;
 | 
						|
            case "ArrowDown":
 | 
						|
                if (userSolPos[1][0] < NUM_ROWS) userSolPos[1][0]++;
 | 
						|
                break;
 | 
						|
            case "ArrowRight":
 | 
						|
                if (userSolPos[1][1] < NUM_COLS - solutions[1].length + 1) userSolPos[1][1]++;
 | 
						|
                break;
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    if (!solved[0] && key === " ") {
 | 
						|
        if (userSolPos[0][0] === solutionPos[0][0] && userSolPos[0][1] === solutionPos[0][1]) {
 | 
						|
            solved[0] = true;
 | 
						|
            for (let i = 0; i < solutions[0].length; i++) {
 | 
						|
                const cellId = userSolPos[0][0] + "" + (userSolPos[0][1] + i);
 | 
						|
                const cell = document.getElementById(cellId);
 | 
						|
                cell.classList.add("sol2");
 | 
						|
            }
 | 
						|
            const sol2Elements = document.querySelectorAll(".sol2");
 | 
						|
            sol2Elements.forEach((el) => {
 | 
						|
                el.style.transition = "opacity 0.5s";
 | 
						|
                el.style.opacity = "0";
 | 
						|
                setTimeout(() => (el.style.opacity = "1"), 500);
 | 
						|
            });
 | 
						|
            playSound("audiocorrect", 1);
 | 
						|
            tryFinish();
 | 
						|
        } else {
 | 
						|
            playSound("audiowrong", 1);
 | 
						|
            wrongSolution();
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    if (!solved[1] && key === "Enter") {
 | 
						|
        if (userSolPos[1][0] === solutionPos[1][0] && userSolPos[1][1] === solutionPos[1][1]) {
 | 
						|
            solved[1] = true;
 | 
						|
            for (let i = 0; i < solutions[1].length; i++) {
 | 
						|
                const cellId = userSolPos[1][0] + "" + (userSolPos[1][1] + i);
 | 
						|
                const cell = document.getElementById(cellId);
 | 
						|
                cell.classList.add("sol2");
 | 
						|
            }
 | 
						|
            const sol2Elements = document.querySelectorAll(".sol2");
 | 
						|
            sol2Elements.forEach((el) => {
 | 
						|
                el.style.transition = "opacity 0.5s";
 | 
						|
                el.style.opacity = "0";
 | 
						|
                setTimeout(() => (el.style.opacity = "1"), 500);
 | 
						|
            });
 | 
						|
            playSound("audiocorrect", 1);
 | 
						|
            tryFinish();
 | 
						|
        } else {
 | 
						|
            playSound("audiowrong", 1);
 | 
						|
            wrongSolution();
 | 
						|
        }
 | 
						|
    }
 | 
						|
});
 | 
						|
 | 
						|
window.addEventListener("message", (event) => {
 | 
						|
    let data = event.data;
 | 
						|
    if (data.action === "startHack") {
 | 
						|
        if (!gameStarted) {
 | 
						|
            hackingContainer.style.display = "flex";
 | 
						|
            startGame(data.solutionsize, data.timeout);
 | 
						|
        }
 | 
						|
    }
 | 
						|
});
 |