424 lines
14 KiB
JavaScript
424 lines
14 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,
|
|
}
|
|
|
|
const laserColors = {
|
|
white: "white",
|
|
red: "red",
|
|
blue: "blue",
|
|
yellow: "yellow",
|
|
};
|
|
|
|
const glassOptions = [
|
|
laserColors.red,
|
|
laserColors.blue,
|
|
laserColors.yellow,
|
|
];
|
|
|
|
// Grid test
|
|
|
|
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, 0, 4, 0, 10, 6, 0, 0, 0, 0],
|
|
[0, 0, 0, 0, 7, 6, 6, 5, 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],
|
|
]
|
|
|
|
|
|
];
|
|
|
|
let currentLevelIndex = 0;
|
|
|
|
// Function to save initial orientation of mirrors
|
|
let laserDirection = { dx: 0, dy: 0 };
|
|
let laserSegments = {};
|
|
let mirrorOrientations = {};
|
|
let activatedButtons = {};
|
|
let openedDoors = {};
|
|
|
|
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 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 openAdjacentDoors(x, y) {
|
|
// Open adjacent doors (up, down, left, right)
|
|
const directions = [
|
|
{ dx: 0, dy: -1 },
|
|
{ dx: 0, dy: 1 },
|
|
{ dx: -1, dy: 0 },
|
|
{ dx: 1, dy: 0 }
|
|
];
|
|
|
|
for (let dir of directions) {
|
|
const newX = x + dir.dx;
|
|
const newY = y + dir.dy;
|
|
|
|
if (newX >= 0 && newX < levels[currentLevelIndex][0].length &&
|
|
newY >= 0 && newY < levels[currentLevelIndex].length) {
|
|
if (levels[currentLevelIndex][newY][newX] === legend.door) {
|
|
openedDoors[`${newY},${newX}`] = true;
|
|
levels[currentLevelIndex][newY][newX] = legend.doorOpen; // Change state to open
|
|
}
|
|
}
|
|
}
|
|
|
|
loadGrid(); // Refresh grid to show opened doors
|
|
}
|
|
|
|
function initializeMirrorOrientations() {
|
|
mirrorOrientations = {}; // Reset
|
|
for (let y = 0; y < levels[currentLevelIndex].length; y++) {
|
|
for (let x = 0; x < levels[currentLevelIndex][y].length; x++) {
|
|
if (levels[currentLevelIndex][y][x] === legend.mirror) {
|
|
mirrorOrientations[`${y},${x}`] = 0; // Default angle
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// Function to print grid
|
|
let mirrorCoordinates = [];
|
|
|
|
function loadGrid() {
|
|
const mapDiv = document.getElementById("map"); // Div with map in DOM
|
|
mapDiv.innerHTML = "";
|
|
|
|
for (let y = 0; y < levels[currentLevelIndex].length; y++) {
|
|
const lign = document.createElement("div");
|
|
lign.classList.add("lign");
|
|
|
|
for (let x = 0; x < levels[currentLevelIndex][y].length; x++) {
|
|
const cell = document.createElement("div");
|
|
cell.classList.add("cell");
|
|
|
|
switch (levels[currentLevelIndex][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:
|
|
const currentAngle = mirrorOrientations[`${y},${x}`] || 0;
|
|
const btnMirror = document.createElement("button");
|
|
btnMirror.classList.add("btn-mirror");
|
|
btnMirror.type = "button";
|
|
btnMirror.style.transform = `rotate(${currentAngle}deg)`;
|
|
const img = document.createElement("img");
|
|
img.src = "../../assets/img/tiles/Mirror.svg";
|
|
img.style.rotate = `${currentAngle}deg`;
|
|
img.className = "mirror-img";
|
|
btnMirror.appendChild(img);
|
|
btnMirror.onmousedown = (e) => {
|
|
e.preventDefault();
|
|
e.stopPropagation();
|
|
rotateMirror(x, y, e.button === 2);
|
|
};
|
|
btnMirror.oncontextmenu = (e) => e.preventDefault();
|
|
|
|
cell.appendChild(btnMirror);
|
|
cell.classList.add("mirror");
|
|
break;
|
|
case legend.door:
|
|
cell.classList.add("door");
|
|
if (openedDoors[`${y},${x}`]) {
|
|
cell.classList.add("door-open");
|
|
}
|
|
break;
|
|
case legend.button:
|
|
cell.classList.add("button");
|
|
if (activatedButtons[`${y},${x}`]) {
|
|
cell.classList.add("button-active");
|
|
}
|
|
let button = document.createElement("button");
|
|
button.classList.add("button-door");
|
|
button.type = "button";
|
|
button.onclick = () => {
|
|
activatedButtons[`${y},${x}`] = !activatedButtons[`${y},${x}`];
|
|
if (activatedButtons[`${y},${x}`]) {
|
|
openAdjacentDoors(x, y);
|
|
}
|
|
traceLaser();
|
|
};
|
|
cell.appendChild(button);
|
|
break;
|
|
case legend.wall:
|
|
cell.classList.add("wall");
|
|
break;
|
|
case legend.target:
|
|
cell.classList.add("target");
|
|
break;
|
|
case legend.ligthLaser:
|
|
cell.classList.add("light-laser");
|
|
const segmentDirection = laserSegments[`${y},${x}`];
|
|
cell.classList.add(getLaserSegmentClass(segmentDirection));
|
|
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.doorOpen:
|
|
cell.classList.add("door-open");
|
|
break;
|
|
}
|
|
|
|
lign.appendChild(cell);
|
|
}
|
|
|
|
mapDiv.appendChild(lign);
|
|
}
|
|
}
|
|
|
|
loadGrid();
|
|
|
|
// Function to rotate mirror
|
|
|
|
function rotateMirror(x, y, isRightClick) {
|
|
const coordKey = `${y},${x}`;
|
|
|
|
if (levels[currentLevelIndex][y][x] !== legend.mirror) {
|
|
return;
|
|
}
|
|
|
|
let currentAngle = mirrorOrientations[coordKey] || 0;
|
|
|
|
// Rotation and normalize negative angles to [0, 360)
|
|
currentAngle = (currentAngle + (isRightClick ? 22.5 : -22.5)) % 360;
|
|
if (currentAngle < 0) {
|
|
currentAngle += 360;
|
|
}
|
|
|
|
// Save
|
|
mirrorOrientations[coordKey] = currentAngle;
|
|
|
|
// Print laser light
|
|
traceLaser(true);
|
|
}
|
|
|
|
|
|
// Function to trace
|
|
|
|
let isLevelFinished = false;
|
|
|
|
function traceLaser() {
|
|
// Reset light laser from previous trace
|
|
laserSegments = {};
|
|
// Ne pas réinitialiser activatedButtons et openedDoors pour préserver l'état des boutons manuels
|
|
for (let y = 0; y < levels[currentLevelIndex].length; y++) {
|
|
for (let x = 0; x < levels[currentLevelIndex][y].length; x++) {
|
|
if (levels[currentLevelIndex][y][x] === legend.ligthLaser) {
|
|
levels[currentLevelIndex][y][x] = legend.empty;
|
|
}
|
|
}
|
|
}
|
|
|
|
let startLaserX;
|
|
let startLaserY;
|
|
|
|
// Search laser
|
|
for (let y = 0; y < levels[currentLevelIndex].length; y++) {
|
|
for (let x = 0; x < levels[currentLevelIndex][y].length; x++) {
|
|
if (levels[currentLevelIndex][y][x] === legend.laser) {
|
|
startLaserX = x;
|
|
startLaserY = y;
|
|
laserDirection = { dx: 1, dy: 0 };
|
|
break;
|
|
}
|
|
}
|
|
if (startLaserX !== undefined) break;
|
|
}
|
|
|
|
// If laser not found -> return
|
|
if (startLaserX === undefined) {
|
|
return;
|
|
}
|
|
|
|
let currentX = startLaserX;
|
|
let currentY = startLaserY;
|
|
let laserActive = true;
|
|
const maxIterations = 1000; // Prevent infinite loops
|
|
let iterations = 0;
|
|
|
|
while (laserActive && iterations < maxIterations) {
|
|
iterations++;
|
|
|
|
currentX += laserDirection.dx;
|
|
currentY += laserDirection.dy;
|
|
|
|
// Out of bounds
|
|
if (currentX < 0 || currentX >= levels[currentLevelIndex][0].length || currentY < 0 || currentY >= levels[currentLevelIndex].length) {
|
|
laserActive = false;
|
|
break;
|
|
}
|
|
|
|
const cellType = levels[currentLevelIndex][currentY][currentX];
|
|
|
|
switch (cellType) {
|
|
case legend.laser:
|
|
case legend.coloredLaser:
|
|
laserActive = false;
|
|
break;
|
|
|
|
case legend.empty:
|
|
levels[currentLevelIndex][currentY][currentX] = legend.ligthLaser;
|
|
laserSegments[`${currentY},${currentX}`] = { ...laserDirection };
|
|
break;
|
|
|
|
case legend.target:
|
|
levels[currentLevelIndex][currentY][currentX] = legend.ligthLaser;
|
|
laserSegments[`${currentY},${currentX}`] = { ...laserDirection };
|
|
laserActive = false;
|
|
isLevelFinished = true;
|
|
break;
|
|
|
|
case legend.mirror:
|
|
const mirrorAngle = mirrorOrientations[`${currentY},${currentX}`] || 0;
|
|
laserDirection = reflectLaser(laserDirection, mirrorAngle);
|
|
break;
|
|
|
|
case legend.wall:
|
|
laserActive = false;
|
|
break;
|
|
|
|
case legend.door:
|
|
if (openedDoors[`${currentY},${currentX}`]) {
|
|
// La porte est ouverte, le laser la traverse
|
|
levels[currentLevelIndex][currentY][currentX] = legend.ligthLaser;
|
|
laserSegments[`${currentY},${currentX}`] = { ...laserDirection };
|
|
} else {
|
|
// La porte est fermée, le laser s'arrête
|
|
laserActive = false;
|
|
}
|
|
break;
|
|
|
|
case legend.doorOpen:
|
|
// Porte ouverte - le laser la traverse
|
|
levels[currentLevelIndex][currentY][currentX] = legend.ligthLaser;
|
|
laserSegments[`${currentY},${currentX}`] = { ...laserDirection };
|
|
break;
|
|
|
|
case legend.button:
|
|
// Activer le bouton et ouvrir les portes adjacentes
|
|
activatedButtons[`${currentY},${currentX}`] = true;
|
|
openAdjacentDoors(currentX, currentY);
|
|
levels[currentLevelIndex][currentY][currentX] = legend.ligthLaser;
|
|
laserSegments[`${currentY},${currentX}`] = { ...laserDirection };
|
|
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, 45);
|
|
break;
|
|
case legend.demiWallCornerDownRight:
|
|
laserDirection = reflectLaser(laserDirection, 315);
|
|
break;
|
|
|
|
default:
|
|
levels[currentLevelIndex][currentY][currentX] = legend.ligthLaser;
|
|
laserSegments[`${currentY},${currentX}`] = { ...laserDirection };
|
|
break;
|
|
}
|
|
}
|
|
|
|
loadGrid();
|
|
|
|
if (isLevelFinished) {
|
|
finish();
|
|
}
|
|
}
|
|
|
|
traceLaser();
|
|
|
|
// Reset level state
|
|
function resetLevel() {
|
|
activatedButtons = {};
|
|
openedDoors = {};
|
|
laserSegments = {};
|
|
laserDirection = { dx: 0, dy: 0 };
|
|
isLevelFinished = false;
|
|
initializeMirrorOrientations();
|
|
traceLaser();
|
|
}
|
|
|
|
// If level finishh -> call this function
|
|
function finish() {
|
|
setTimeout(() => {
|
|
alert("Réussi !");
|
|
}, 100);
|
|
}
|