Introduce a new rotator button tile and mirror auto-rotation feature. CSS adds styles for rotator buttons and locked mirror display. JS: add legend entry and rotatorButtons config, track activeRotatorButtons and rotatorIntervals, implement rotateMirrorStep, isMirrorControlledByButton, syncRotatorButtons and stopAllRotatorButtons; wire rotator behavior into traceLaser and loadGrid so rotator buttons activate intervals that rotate target mirrors automatically and lock out manual rotation. Refactor rotateMirror to reuse rotation step logic and ensure rotator intervals are cleared when advancing levels.
733 lines
22 KiB
JavaScript
733 lines
22 KiB
JavaScript
// Legend of grid case
|
|
|
|
const legend = {
|
|
empty: 0,
|
|
laser: 1,
|
|
coloredLaser: 2,
|
|
mirror: 3,
|
|
door: 4,
|
|
button: 5,
|
|
wall: 6,
|
|
target: 7,
|
|
ligthLaser: 8,
|
|
demiWallCornerUpLeft: 9,
|
|
demiWallCornerUpRight: 10,
|
|
demiWallCornerDownLeft: 11,
|
|
demiWallCornerDownRight: 12,
|
|
doorOpen: 13,
|
|
button2: 14,
|
|
captor: 15,
|
|
cable: 16,
|
|
rotatorButton: 17,
|
|
};
|
|
|
|
const laserColors = {
|
|
white: "white",
|
|
red: "red",
|
|
blue: "blue",
|
|
yellow: "yellow",
|
|
};
|
|
|
|
const glassOptions = [
|
|
laserColors.red,
|
|
laserColors.blue,
|
|
laserColors.yellow,
|
|
];
|
|
|
|
let levels = [
|
|
[
|
|
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
|
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
|
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
|
[0, 0, 0, 0, 6, 6, 6, 6, 6, 6, 11, 0, 0, 0, 0],
|
|
[0, 0, 0, 0, 1, 0, 5, 4, 0, 10, 6, 0, 0, 0, 0],
|
|
[0, 0, 0, 0, 7, 6, 6, 6, 6, 0, 6, 0, 0, 0, 0],
|
|
[0, 0, 0, 0, 3, 0, 0, 0, 0, 12, 6, 0, 0, 0, 0],
|
|
[0, 0, 0, 0, 6, 6, 6, 6, 6, 6, 9, 0, 0, 0, 0],
|
|
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
|
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
|
],
|
|
|
|
[
|
|
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
|
[0, 0, 0, 0, 6, 6, 6, 6, 6, 6, 11, 0, 0, 0, 0],
|
|
[0, 0, 0, 0, 1, 0, 0, 0, 0, 10, 6, 0, 0, 0, 0],
|
|
[0, 0, 0, 0, 3, 16, 16, 15, 0, 3, 6, 0, 0, 0, 0],
|
|
[0, 0, 0, 0, 0, 0, 0, 0, 0, 12, 6, 0, 0, 0, 0],
|
|
[0, 0, 0, 0, 12, 6, 6, 6, 6, 6, 9, 0, 0, 0, 0],
|
|
[0, 0, 0, 0, 6, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
|
[0, 0, 0, 0, 6, 0, 0, 0, 3, 0, 7, 0, 0, 0, 0],
|
|
[0, 0, 0, 0, 6, 11, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
|
[0, 0, 0, 0, 10, 6, 6, 6, 6, 6, 0, 0, 0, 0, 0],
|
|
],
|
|
|
|
[
|
|
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
|
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
|
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
|
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
|
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
|
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
|
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
|
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
|
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
|
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
|
],
|
|
];
|
|
|
|
let currentLevelIndex = 0;
|
|
|
|
const initialMirrorAngles = {
|
|
"6,4": 315,
|
|
"4,3": 315,
|
|
};
|
|
|
|
const buttonGroups = {
|
|
"4,6": 1,
|
|
"4,7": 2,
|
|
};
|
|
|
|
const doorGroups = {
|
|
"4,7": 1,
|
|
"4,8": 2,
|
|
};
|
|
|
|
const rotatorButtons = {
|
|
"4,6": { mirrorX: 4, mirrorY: 6 },
|
|
};
|
|
|
|
let laserDirection = { dx: 0, dy: 0 };
|
|
let laserSegments = {};
|
|
let mirrorOrientations = {};
|
|
let glassPlacements = {};
|
|
let activatedButtons = {};
|
|
let openedDoors = {};
|
|
let isLevelFinished = false;
|
|
let activeRotatorButtons = {};
|
|
let rotatorIntervals = {};
|
|
|
|
function getCurrentLevel() {
|
|
return levels[currentLevelIndex];
|
|
}
|
|
|
|
function normalizeLaserDirection(dx, dy) {
|
|
const epsilon = 0.0001;
|
|
const normalizedDx = Math.abs(dx) < epsilon ? 0 : Math.sign(dx);
|
|
const normalizedDy = Math.abs(dy) < epsilon ? 0 : Math.sign(dy);
|
|
|
|
return { dx: normalizedDx, dy: normalizedDy };
|
|
}
|
|
|
|
function reflectLaser(direction, mirrorAngle) {
|
|
const mirrorRadians = mirrorAngle * (Math.PI / 180);
|
|
const mirrorVectorX = Math.cos(mirrorRadians);
|
|
const mirrorVectorY = Math.sin(mirrorRadians);
|
|
const dotProduct = (direction.dx * mirrorVectorX) + (direction.dy * mirrorVectorY);
|
|
const reflectedDx = (2 * dotProduct * mirrorVectorX) - direction.dx;
|
|
const reflectedDy = (2 * dotProduct * mirrorVectorY) - direction.dy;
|
|
|
|
return normalizeLaserDirection(reflectedDx, reflectedDy);
|
|
}
|
|
|
|
function reverseLaser(direction) {
|
|
return {
|
|
dx: direction.dx * -1,
|
|
dy: direction.dy * -1,
|
|
};
|
|
}
|
|
|
|
function getLaserSegmentClass(segmentDirection) {
|
|
if (!segmentDirection) {
|
|
return "laser-horizontal";
|
|
}
|
|
|
|
if (segmentDirection.dx === 0) {
|
|
return "laser-vertical";
|
|
}
|
|
|
|
if (segmentDirection.dy === 0) {
|
|
return "laser-horizontal";
|
|
}
|
|
|
|
if (segmentDirection.dx === segmentDirection.dy) {
|
|
return "laser-diagonal-down";
|
|
}
|
|
|
|
return "laser-diagonal-up";
|
|
}
|
|
|
|
function getLaserColorClass(color) {
|
|
return `laser-color-${color || laserColors.white}`;
|
|
}
|
|
|
|
function getButtonGroup(x, y) {
|
|
const cellType = getCurrentLevel()[y][x];
|
|
|
|
if (cellType === legend.button2) {
|
|
return 2;
|
|
}
|
|
|
|
return buttonGroups[`${y},${x}`] || 1;
|
|
}
|
|
|
|
function getDoorGroup(x, y) {
|
|
return doorGroups[`${y},${x}`] || 1;
|
|
}
|
|
|
|
function openDoorsFromButton(x, y) {
|
|
const buttonGroup = getButtonGroup(x, y);
|
|
const level = getCurrentLevel();
|
|
|
|
for (let doorY = 0; doorY < level.length; doorY++) {
|
|
for (let doorX = 0; doorX < level[doorY].length; doorX++) {
|
|
if (level[doorY][doorX] === legend.door && getDoorGroup(doorX, doorY) === buttonGroup) {
|
|
openedDoors[`${doorY},${doorX}`] = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
function getRotatorButtonConfig(x, y) {
|
|
return rotatorButtons[`${y},${x}`];
|
|
}
|
|
|
|
function isMirrorControlledByButton(x, y) {
|
|
const rotatorEntries = Object.values(rotatorButtons);
|
|
|
|
for (let i = 0; i < rotatorEntries.length; i++) {
|
|
const rotatorConfig = rotatorEntries[i];
|
|
|
|
if (rotatorConfig.mirrorX === x && rotatorConfig.mirrorY === y) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
function rotateMirrorStep(x, y, angleStep) {
|
|
const coordKey = `${y},${x}`;
|
|
|
|
if (getCurrentLevel()[y][x] !== legend.mirror) {
|
|
return;
|
|
}
|
|
|
|
let currentAngle = mirrorOrientations[coordKey] || 0;
|
|
currentAngle = (currentAngle + angleStep) % 360;
|
|
|
|
if (currentAngle < 0) {
|
|
currentAngle += 360;
|
|
}
|
|
|
|
mirrorOrientations[coordKey] = currentAngle;
|
|
}
|
|
|
|
function syncRotatorButtons() {
|
|
const rotatorKeys = Object.keys(rotatorButtons);
|
|
|
|
for (let i = 0; i < rotatorKeys.length; i++) {
|
|
const key = rotatorKeys[i];
|
|
const config = rotatorButtons[key];
|
|
const isActive = activeRotatorButtons[key] === true;
|
|
|
|
if (isActive && !rotatorIntervals[key]) {
|
|
rotatorIntervals[key] = window.setInterval(() => {
|
|
rotateMirrorStep(config.mirrorX, config.mirrorY, -22.5);
|
|
traceLaser();
|
|
}, 1000);
|
|
}
|
|
|
|
if (!isActive && rotatorIntervals[key]) {
|
|
window.clearInterval(rotatorIntervals[key]);
|
|
delete rotatorIntervals[key];
|
|
}
|
|
}
|
|
}
|
|
|
|
function stopAllRotatorButtons() {
|
|
const intervalKeys = Object.keys(rotatorIntervals);
|
|
|
|
for (let i = 0; i < intervalKeys.length; i++) {
|
|
const key = intervalKeys[i];
|
|
window.clearInterval(rotatorIntervals[key]);
|
|
}
|
|
|
|
rotatorIntervals = {};
|
|
}
|
|
|
|
function saveLaserSegment(x, y, direction, color) {
|
|
laserSegments[`${y},${x}`] = {
|
|
direction: { ...direction },
|
|
color: color,
|
|
};
|
|
}
|
|
|
|
function isGlassOnCell(x, y) {
|
|
return glassPlacements[`${y},${x}`] !== undefined;
|
|
}
|
|
|
|
function drawGlassInCell(cell, x, y) {
|
|
const glassColor = glassPlacements[`${y},${x}`];
|
|
|
|
if (!glassColor) {
|
|
return;
|
|
}
|
|
|
|
const glassDiv = document.createElement("div");
|
|
glassDiv.classList.add("cell-glass", `glass-${glassColor}`);
|
|
cell.appendChild(glassDiv);
|
|
}
|
|
|
|
function drawLaserInCell(cell, segmentData) {
|
|
if (!segmentData) {
|
|
return;
|
|
}
|
|
|
|
const laserDiv = document.createElement("div");
|
|
laserDiv.classList.add("laser-overlay");
|
|
laserDiv.classList.add(getLaserSegmentClass(segmentData.direction));
|
|
laserDiv.classList.add(getLaserColorClass(segmentData.color));
|
|
cell.appendChild(laserDiv);
|
|
}
|
|
|
|
function createPalette() {
|
|
const palette = document.getElementById("glass-palette");
|
|
|
|
if (!palette) {
|
|
return;
|
|
}
|
|
|
|
palette.innerHTML = "";
|
|
|
|
for (let i = 0; i < glassOptions.length; i++) {
|
|
const glassColor = glassOptions[i];
|
|
const glassButton = document.createElement("button");
|
|
glassButton.type = "button";
|
|
glassButton.classList.add("glass-item", `glass-${glassColor}`);
|
|
glassButton.draggable = true;
|
|
glassButton.addEventListener("dragstart", (event) => {
|
|
event.dataTransfer.effectAllowed = "copy";
|
|
event.dataTransfer.setData("application/x-glass-color", glassColor);
|
|
});
|
|
|
|
palette.appendChild(glassButton);
|
|
}
|
|
}
|
|
|
|
function addDropEvents(cell, x, y) {
|
|
cell.addEventListener("dragover", (event) => {
|
|
if (isLevelFinished) {
|
|
return;
|
|
}
|
|
|
|
if (!event.dataTransfer.types.includes("application/x-glass-color")) {
|
|
return;
|
|
}
|
|
|
|
const cellType = getCurrentLevel()[y][x];
|
|
if (cellType !== legend.empty) {
|
|
return;
|
|
}
|
|
|
|
event.preventDefault();
|
|
cell.classList.add("can-drop");
|
|
});
|
|
|
|
cell.addEventListener("dragleave", () => {
|
|
cell.classList.remove("can-drop");
|
|
});
|
|
|
|
cell.addEventListener("drop", (event) => {
|
|
if (isLevelFinished) {
|
|
return;
|
|
}
|
|
|
|
const selectedColor = event.dataTransfer.getData("application/x-glass-color");
|
|
cell.classList.remove("can-drop");
|
|
|
|
if (!selectedColor) {
|
|
return;
|
|
}
|
|
|
|
if (getCurrentLevel()[y][x] !== legend.empty) {
|
|
return;
|
|
}
|
|
|
|
event.preventDefault();
|
|
glassPlacements[`${y},${x}`] = selectedColor;
|
|
traceLaser();
|
|
});
|
|
|
|
cell.addEventListener("dblclick", () => {
|
|
if (isLevelFinished) {
|
|
return;
|
|
}
|
|
|
|
if (isGlassOnCell(x, y)) {
|
|
delete glassPlacements[`${y},${x}`];
|
|
traceLaser();
|
|
}
|
|
});
|
|
}
|
|
|
|
function blockBrowserDrop() {
|
|
document.addEventListener("dragover", (event) => {
|
|
event.preventDefault();
|
|
});
|
|
|
|
document.addEventListener("drop", (event) => {
|
|
event.preventDefault();
|
|
});
|
|
}
|
|
|
|
function initializeMirrorOrientations() {
|
|
mirrorOrientations = {};
|
|
const level = getCurrentLevel();
|
|
|
|
for (let y = 0; y < level.length; y++) {
|
|
for (let x = 0; x < level[y].length; x++) {
|
|
if (level[y][x] === legend.mirror) {
|
|
mirrorOrientations[`${y},${x}`] = initialMirrorAngles[`${y},${x}`] || 0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
function loadGrid() {
|
|
const mapDiv = document.getElementById("map");
|
|
const level = getCurrentLevel();
|
|
mapDiv.innerHTML = "";
|
|
|
|
for (let y = 0; y < level.length; y++) {
|
|
const lign = document.createElement("div");
|
|
lign.classList.add("lign");
|
|
|
|
for (let x = 0; x < level[y].length; x++) {
|
|
const cell = document.createElement("div");
|
|
cell.classList.add("cell");
|
|
addDropEvents(cell, x, y);
|
|
|
|
switch (level[y][x]) {
|
|
case legend.empty:
|
|
cell.classList.add("empty");
|
|
break;
|
|
case legend.laser:
|
|
cell.classList.add("laser");
|
|
break;
|
|
case legend.coloredLaser:
|
|
cell.classList.add("colored-laser");
|
|
break;
|
|
case legend.mirror:
|
|
cell.classList.add("mirror");
|
|
const currentAngle = mirrorOrientations[`${y},${x}`] || 0;
|
|
const img = document.createElement("img");
|
|
img.src = "../../assets/img/tiles/Mirror.svg";
|
|
img.classList.add("mirror-img");
|
|
img.style.transform = `rotate(${currentAngle}deg)`;
|
|
|
|
if (isMirrorControlledByButton(x, y)) {
|
|
const mirrorDisplay = document.createElement("div");
|
|
mirrorDisplay.classList.add("btn-mirror", "btn-mirror-locked");
|
|
mirrorDisplay.appendChild(img);
|
|
cell.appendChild(mirrorDisplay);
|
|
} else {
|
|
const btnMirror = document.createElement("button");
|
|
btnMirror.classList.add("btn-mirror");
|
|
btnMirror.type = "button";
|
|
btnMirror.appendChild(img);
|
|
btnMirror.onmousedown = (event) => {
|
|
event.preventDefault();
|
|
event.stopPropagation();
|
|
if (!isLevelFinished) {
|
|
rotateMirror(x, y, event.button === 2);
|
|
}
|
|
};
|
|
btnMirror.oncontextmenu = (event) => event.preventDefault();
|
|
cell.appendChild(btnMirror);
|
|
}
|
|
break;
|
|
case legend.door:
|
|
cell.classList.add("door");
|
|
if (openedDoors[`${y},${x}`]) {
|
|
cell.classList.add("door-open");
|
|
}
|
|
break;
|
|
case legend.doorOpen:
|
|
cell.classList.add("door", "door-open");
|
|
break;
|
|
case legend.button:
|
|
cell.classList.add("button");
|
|
if (activatedButtons[`${y},${x}`]) {
|
|
cell.classList.add("button-active");
|
|
}
|
|
break;
|
|
case legend.button2:
|
|
cell.classList.add("button", "button-2");
|
|
if (activatedButtons[`${y},${x}`]) {
|
|
cell.classList.add("button-active");
|
|
}
|
|
break;
|
|
case legend.rotatorButton:
|
|
cell.classList.add("button", "button-rotator");
|
|
if (activatedButtons[`${y},${x}`]) {
|
|
cell.classList.add("button-active");
|
|
}
|
|
break;
|
|
case legend.wall:
|
|
cell.classList.add("wall");
|
|
break;
|
|
case legend.target:
|
|
cell.classList.add("target");
|
|
break;
|
|
case legend.demiWallCornerUpLeft:
|
|
cell.classList.add("demi-wall-corner-up-left");
|
|
break;
|
|
case legend.demiWallCornerUpRight:
|
|
cell.classList.add("demi-wall-corner-up-right");
|
|
break;
|
|
case legend.demiWallCornerDownLeft:
|
|
cell.classList.add("demi-wall-corner-down-left");
|
|
break;
|
|
case legend.demiWallCornerDownRight:
|
|
cell.classList.add("demi-wall-corner-down-right");
|
|
break;
|
|
case legend.captor:
|
|
cell.classList.add("captor");
|
|
break;
|
|
case legend.cable:
|
|
cell.classList.add("cable");
|
|
break;
|
|
}
|
|
|
|
drawLaserInCell(cell, laserSegments[`${y},${x}`]);
|
|
drawGlassInCell(cell, x, y);
|
|
lign.appendChild(cell);
|
|
}
|
|
|
|
mapDiv.appendChild(lign);
|
|
}
|
|
}
|
|
|
|
function rotateMirror(x, y, isRightClick) {
|
|
rotateMirrorStep(x, y, isRightClick ? 22.5 : -22.5);
|
|
traceLaser();
|
|
}
|
|
|
|
function traceLaser() {
|
|
laserSegments = {};
|
|
activatedButtons = {};
|
|
openedDoors = {};
|
|
activeRotatorButtons = {};
|
|
isLevelFinished = false;
|
|
|
|
const level = getCurrentLevel();
|
|
let startLaserX;
|
|
let startLaserY;
|
|
|
|
for (let y = 0; y < level.length; y++) {
|
|
for (let x = 0; x < level[y].length; x++) {
|
|
if (level[y][x] === legend.laser) {
|
|
startLaserX = x;
|
|
startLaserY = y;
|
|
laserDirection = { dx: 1, dy: 0 };
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (startLaserX !== undefined) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (startLaserX === undefined) {
|
|
return;
|
|
}
|
|
|
|
let currentX = startLaserX;
|
|
let currentY = startLaserY;
|
|
let currentLaserColor = laserColors.white;
|
|
let laserActive = true;
|
|
let iterations = 0;
|
|
const maxIterations = 1000;
|
|
|
|
while (laserActive && iterations < maxIterations) {
|
|
iterations++;
|
|
currentX += laserDirection.dx;
|
|
currentY += laserDirection.dy;
|
|
|
|
if (currentX < 0 || currentX >= level[0].length || currentY < 0 || currentY >= level.length) {
|
|
laserActive = false;
|
|
break;
|
|
}
|
|
|
|
const cellType = level[currentY][currentX];
|
|
const glassColor = glassPlacements[`${currentY},${currentX}`];
|
|
|
|
if (glassColor) {
|
|
currentLaserColor = glassColor;
|
|
}
|
|
|
|
switch (cellType) {
|
|
case legend.laser:
|
|
case legend.coloredLaser:
|
|
laserActive = false;
|
|
break;
|
|
|
|
case legend.empty:
|
|
saveLaserSegment(currentX, currentY, laserDirection, currentLaserColor);
|
|
break;
|
|
|
|
case legend.target:
|
|
saveLaserSegment(currentX, currentY, laserDirection, currentLaserColor);
|
|
laserActive = false;
|
|
isLevelFinished = true;
|
|
break;
|
|
|
|
case legend.mirror:
|
|
if (currentLaserColor === laserColors.yellow) {
|
|
saveLaserSegment(currentX, currentY, laserDirection, currentLaserColor);
|
|
} else {
|
|
const mirrorAngle = mirrorOrientations[`${currentY},${currentX}`] || 0;
|
|
laserDirection = reflectLaser(laserDirection, mirrorAngle);
|
|
}
|
|
break;
|
|
|
|
case legend.wall:
|
|
if (currentLaserColor === laserColors.blue) {
|
|
laserDirection = reverseLaser(laserDirection);
|
|
} else if (currentLaserColor === laserColors.yellow) {
|
|
saveLaserSegment(currentX, currentY, laserDirection, currentLaserColor);
|
|
} else {
|
|
laserActive = false;
|
|
}
|
|
break;
|
|
|
|
case legend.door:
|
|
case legend.doorOpen:
|
|
if (openedDoors[`${currentY},${currentX}`] || cellType === legend.doorOpen) {
|
|
saveLaserSegment(currentX, currentY, laserDirection, currentLaserColor);
|
|
} else if (currentLaserColor === laserColors.blue) {
|
|
laserDirection = reverseLaser(laserDirection);
|
|
} else if (currentLaserColor === laserColors.yellow) {
|
|
saveLaserSegment(currentX, currentY, laserDirection, currentLaserColor);
|
|
} else {
|
|
laserActive = false;
|
|
}
|
|
break;
|
|
|
|
case legend.button:
|
|
case legend.button2:
|
|
if (currentLaserColor === laserColors.red) {
|
|
activatedButtons[`${currentY},${currentX}`] = true;
|
|
openDoorsFromButton(currentX, currentY);
|
|
saveLaserSegment(currentX, currentY, laserDirection, currentLaserColor);
|
|
} else if (currentLaserColor === laserColors.yellow) {
|
|
saveLaserSegment(currentX, currentY, laserDirection, currentLaserColor);
|
|
} else if (currentLaserColor === laserColors.blue) {
|
|
laserDirection = reverseLaser(laserDirection);
|
|
} else {
|
|
laserActive = false;
|
|
}
|
|
break;
|
|
|
|
case legend.rotatorButton:
|
|
if (currentLaserColor === laserColors.red) {
|
|
const rotatorKey = `${currentY},${currentX}`;
|
|
activatedButtons[rotatorKey] = true;
|
|
activeRotatorButtons[rotatorKey] = true;
|
|
saveLaserSegment(currentX, currentY, laserDirection, currentLaserColor);
|
|
} else if (currentLaserColor === laserColors.yellow) {
|
|
saveLaserSegment(currentX, currentY, laserDirection, currentLaserColor);
|
|
} else if (currentLaserColor === laserColors.blue) {
|
|
laserDirection = reverseLaser(laserDirection);
|
|
} else {
|
|
laserActive = false;
|
|
}
|
|
break;
|
|
|
|
case legend.demiWallCornerUpLeft:
|
|
laserDirection = reflectLaser(laserDirection, 135);
|
|
break;
|
|
|
|
case legend.demiWallCornerUpRight:
|
|
laserDirection = reflectLaser(laserDirection, 45);
|
|
break;
|
|
|
|
case legend.demiWallCornerDownLeft:
|
|
laserDirection = reflectLaser(laserDirection, 225);
|
|
break;
|
|
|
|
case legend.demiWallCornerDownRight:
|
|
laserDirection = reflectLaser(laserDirection, 315);
|
|
break;
|
|
|
|
default:
|
|
saveLaserSegment(currentX, currentY, laserDirection, currentLaserColor);
|
|
break;
|
|
}
|
|
}
|
|
|
|
syncRotatorButtons();
|
|
loadGrid();
|
|
|
|
if (isLevelFinished) {
|
|
finish();
|
|
}
|
|
}
|
|
|
|
function finish() {
|
|
setTimeout(() => {
|
|
const winOverlay = document.querySelector(".win-overlay");
|
|
winOverlay.style.visibility = "visible";
|
|
}, 100);
|
|
|
|
nextLevel();
|
|
}
|
|
|
|
function nextLevel () {
|
|
currentLevelIndex++;
|
|
|
|
isLevelFinished = false;
|
|
|
|
if (currentLevelIndex >= levels.length) {
|
|
currentLevelIndex = 0;
|
|
}
|
|
|
|
initializeMirrorOrientations();
|
|
loadGrid();
|
|
laserSegments = {};
|
|
mirrorOrientations = {};
|
|
glassPlacements = {};
|
|
activatedButtons = {};
|
|
openedDoors = {};
|
|
traceLaser();
|
|
}
|
|
|
|
function nextLevel() {
|
|
currentLevelIndex++;
|
|
|
|
isLevelFinished = false;
|
|
stopAllRotatorButtons();
|
|
|
|
if (currentLevelIndex >= levels.length) {
|
|
currentLevelIndex = 0;
|
|
}
|
|
|
|
initializeMirrorOrientations();
|
|
loadGrid();
|
|
laserSegments = {};
|
|
mirrorOrientations = {};
|
|
glassPlacements = {};
|
|
activatedButtons = {};
|
|
openedDoors = {};
|
|
traceLaser();
|
|
|
|
const winOverlay = document.querySelector(".win-overlay");
|
|
winOverlay.style.visibility = "hidden";
|
|
}
|
|
|
|
createPalette();
|
|
initializeMirrorOrientations();
|
|
blockBrowserDrop();
|
|
traceLaser();
|