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);
 | |
|         }
 | |
|     }
 | |
| });
 | 
