Files
Projet_48h/web/assets/js/game.js
2026-03-31 13:38:24 +02:00

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