Compare commits
16 Commits
1be3750672
...
feature/la
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
65bd05d47c | ||
|
|
e90a1fc497 | ||
|
|
75a68a7c75 | ||
|
|
731d040e89 | ||
|
|
5620fade9c | ||
|
|
09d54aa525 | ||
|
|
76de2a5a5c | ||
|
|
284e396d5f | ||
|
|
4ba42f6566 | ||
|
|
d0e282b41c | ||
| 024b80d393 | |||
| 34fbb797c1 | |||
| b10b6475d8 | |||
| 2dbfff770f | |||
| a791415bd3 | |||
|
|
577357b090 |
1
backend/.gitignore
vendored
Normal file
1
backend/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
/target
|
||||||
@@ -4,18 +4,20 @@
|
|||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
|
|
||||||
html, body {
|
html,
|
||||||
width: 100%;
|
body {
|
||||||
height: 100%;
|
width: 100vw;
|
||||||
|
height: 100vh;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
body {
|
body {
|
||||||
background: #1a1a1a;
|
background: #f7f7f7;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
font-family: 'Arial', sans-serif;
|
font-family: 'Arial', sans-serif;
|
||||||
|
user-select: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
main {
|
main {
|
||||||
@@ -27,17 +29,49 @@ main {
|
|||||||
border-radius: 10px;
|
border-radius: 10px;
|
||||||
min-width: fit-content;
|
min-width: fit-content;
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
|
gap: 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.game-layout {
|
||||||
|
width: min(95vw, 1000px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.toolbox {
|
||||||
|
width: 100%;
|
||||||
|
background: #dfe5f8;
|
||||||
|
border-radius: 10px;
|
||||||
|
padding: 14px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toolbox h2 {
|
||||||
|
font-size: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toolbox p {
|
||||||
|
font-size: 0.9rem;
|
||||||
|
color: #334;
|
||||||
|
}
|
||||||
|
|
||||||
|
.glass-palette {
|
||||||
|
display: flex;
|
||||||
|
gap: 10px;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================
|
||||||
|
GRID
|
||||||
|
================================ */
|
||||||
|
|
||||||
.map {
|
.map {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 0px;
|
gap: 0px;
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
background: #222222;
|
background: #DADEEF;
|
||||||
border-radius: 5px;
|
border-radius: 5px;
|
||||||
width: fit-content;
|
|
||||||
height: fit-content;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.lign {
|
.lign {
|
||||||
@@ -46,10 +80,13 @@ main {
|
|||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ================================
|
||||||
|
CELLS
|
||||||
|
================================ */
|
||||||
|
|
||||||
.cell {
|
.cell {
|
||||||
border: 1px solid #333333;
|
width: clamp(28px, 5.5vmin, 60px);
|
||||||
width: 30px;
|
height: clamp(28px, 5.5vmin, 60px);
|
||||||
height: 30px;
|
|
||||||
transition: all 0.2s ease;
|
transition: all 0.2s ease;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
@@ -57,11 +94,20 @@ main {
|
|||||||
margin: 0;
|
margin: 0;
|
||||||
position: relative;
|
position: relative;
|
||||||
background-color: #2a2a2a;
|
background-color: #2a2a2a;
|
||||||
|
user-select: none;
|
||||||
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.cell.can-drop {
|
||||||
|
outline: 2px dashed rgba(0, 0, 0, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================
|
||||||
|
CASES TYPE
|
||||||
|
================================ */
|
||||||
|
|
||||||
.empty {
|
.empty {
|
||||||
background-color: #2a2a2a;
|
background-color: #DADEEF;
|
||||||
border-color: #333333;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.empty:hover {
|
.empty:hover {
|
||||||
@@ -69,70 +115,183 @@ main {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.laser {
|
.laser {
|
||||||
background-color: #FFD700;
|
background-color: #f5f5f5;
|
||||||
border-color: #FFA500;
|
border: 2px solid #d8d8d8;
|
||||||
}
|
}
|
||||||
|
|
||||||
.colored-laser {
|
.colored-laser {
|
||||||
background: linear-gradient(135deg, #FF1493 0%, #00CED1 100%);
|
background-color: #ffa726;
|
||||||
border-color: #FF1493;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.mirror {
|
.mirror {
|
||||||
background-color: #1a1a1a;
|
background-color: #DADEEF;
|
||||||
border-color: #444444;
|
border-color: #444444;
|
||||||
position: relative;
|
position: relative;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mirror::before {
|
.wall {
|
||||||
content: '';
|
background-color: #0729c0;
|
||||||
position: absolute;
|
|
||||||
width: 100%;
|
|
||||||
height: 60%;
|
|
||||||
background: linear-gradient(45deg, transparent 48%, #CCCCCC 48%, #CCCCCC 52%, transparent 52%);
|
|
||||||
top: 50%;
|
|
||||||
transform: translateY(-50%);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.door {
|
.door {
|
||||||
background: linear-gradient(135deg, #8B4513 0%, #654321 100%);
|
background-color: #6d4c41;
|
||||||
border-color: #654321;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.door::after {
|
.door-open {
|
||||||
content: '';
|
background-color: #bca89c;
|
||||||
position: absolute;
|
|
||||||
width: 60%;
|
|
||||||
height: 60%;
|
|
||||||
border: 2px solid #FFD700;
|
|
||||||
border-radius: 3px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.button {
|
.button {
|
||||||
background: radial-gradient(circle at 35% 35%, #FF4444 0%, #CC0000 100%);
|
background-color: #7cb342;
|
||||||
border-color: #990000;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.wall {
|
.button-2 {
|
||||||
background: linear-gradient(135deg, #1a1a1a 0%, #0f0f0f 100%);
|
background-color: #ff8f00;
|
||||||
border-color: #000000;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.demi-wall {
|
.button-active {
|
||||||
background: linear-gradient(90deg, #0f0f0f 0%, #0f0f0f 50%, #2a2a2a 50%, #2a2a2a 100%);
|
background-color: #c6ff00;
|
||||||
border-color: #333333;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.target {
|
.target {
|
||||||
background: radial-gradient(circle at 35% 35%, #00FF00 0%, #00CC00 50%, rgba(0, 255, 0, 0.2) 100%);
|
background: #00FF00;
|
||||||
border-color: #00FF00;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ================================
|
||||||
|
LIGHT LASER
|
||||||
|
================================ */
|
||||||
|
|
||||||
.light-laser {
|
.light-laser {
|
||||||
margin-top: 14px;
|
position: relative;
|
||||||
height: 7px;
|
}
|
||||||
width: 35px;
|
|
||||||
background-color: red;
|
.laser-overlay {
|
||||||
display: flex;
|
position: absolute;
|
||||||
|
inset: 0;
|
||||||
|
pointer-events: none;
|
||||||
|
z-index: 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.laser-horizontal {
|
||||||
|
--laser-color: red;
|
||||||
|
background: linear-gradient(to bottom, transparent 0%, transparent 45%, var(--laser-color) 45%, var(--laser-color) 55%, transparent 55%, transparent 100%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.laser-vertical {
|
||||||
|
--laser-color: red;
|
||||||
|
background: linear-gradient(to right, transparent 0%, transparent 45%, var(--laser-color) 45%, var(--laser-color) 55%, transparent 55%, transparent 100%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.laser-diagonal-down {
|
||||||
|
--laser-color: red;
|
||||||
|
background: linear-gradient(45deg, transparent 0%, transparent 46%, var(--laser-color) 46%, var(--laser-color) 54%, transparent 54%, transparent 100%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.laser-diagonal-up {
|
||||||
|
--laser-color: red;
|
||||||
|
background: linear-gradient(135deg, transparent 0%, transparent 46%, var(--laser-color) 46%, var(--laser-color) 54%, transparent 54%, transparent 100%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.laser-color-white {
|
||||||
|
--laser-color: #f8f8f8;
|
||||||
|
}
|
||||||
|
|
||||||
|
.laser-color-red {
|
||||||
|
--laser-color: #ff3b30;
|
||||||
|
}
|
||||||
|
|
||||||
|
.laser-color-blue {
|
||||||
|
--laser-color: #2d7ff9;
|
||||||
|
}
|
||||||
|
|
||||||
|
.laser-color-yellow {
|
||||||
|
--laser-color: #ffd400;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================
|
||||||
|
MIRROR
|
||||||
|
================================ */
|
||||||
|
|
||||||
|
.btn-mirror {
|
||||||
|
background: none;
|
||||||
|
border: none;
|
||||||
|
cursor: pointer;
|
||||||
|
height: 100%;
|
||||||
|
position: relative;
|
||||||
|
z-index: 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-mirror::after {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
left: 10%;
|
||||||
|
width: 80%;
|
||||||
|
height: 2px;
|
||||||
|
background-color: #aaaaaa;
|
||||||
|
transform-origin: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.glass-item {
|
||||||
|
width: 54px;
|
||||||
|
height: 54px;
|
||||||
|
border: none;
|
||||||
|
border-radius: 10px;
|
||||||
|
cursor: grab;
|
||||||
|
position: relative;
|
||||||
|
box-shadow: inset 0 0 0 2px rgba(0, 0, 0, 0.1);
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.glass-item::after,
|
||||||
|
.cell-glass::after {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
inset: 10px;
|
||||||
|
border-radius: 8px;
|
||||||
|
background: rgba(255, 255, 255, 0.45);
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.8);
|
||||||
|
}
|
||||||
|
|
||||||
|
.glass-red {
|
||||||
|
background: rgba(255, 59, 48, 0.85);
|
||||||
|
}
|
||||||
|
|
||||||
|
.glass-blue {
|
||||||
|
background: rgba(45, 127, 249, 0.85);
|
||||||
|
}
|
||||||
|
|
||||||
|
.glass-yellow {
|
||||||
|
background: rgba(255, 212, 0, 0.9);
|
||||||
|
}
|
||||||
|
|
||||||
|
.cell-glass {
|
||||||
|
position: absolute;
|
||||||
|
inset: 5px;
|
||||||
|
border-radius: 8px;
|
||||||
|
opacity: 0.9;
|
||||||
|
pointer-events: none;
|
||||||
|
z-index: 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================
|
||||||
|
RESPONSIVE
|
||||||
|
================================ */
|
||||||
|
|
||||||
|
@media (max-width: 600px) {
|
||||||
|
.map {
|
||||||
|
padding: 5px;
|
||||||
|
border-radius: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
main {
|
||||||
|
padding: 8px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-height: 500px) {
|
||||||
|
.map {
|
||||||
|
padding: 4px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -11,38 +11,283 @@ const legend = {
|
|||||||
demiWall: 7,
|
demiWall: 7,
|
||||||
target: 8,
|
target: 8,
|
||||||
ligthLaser: 9,
|
ligthLaser: 9,
|
||||||
|
button2: 10,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const laserColors = {
|
||||||
|
white: "white",
|
||||||
|
red: "red",
|
||||||
|
blue: "blue",
|
||||||
|
yellow: "yellow",
|
||||||
|
};
|
||||||
|
|
||||||
|
const glassOptions = [
|
||||||
|
laserColors.red,
|
||||||
|
laserColors.blue,
|
||||||
|
laserColors.yellow,
|
||||||
|
];
|
||||||
|
|
||||||
// Grid test
|
// Grid test
|
||||||
|
|
||||||
let grid = [
|
let level1 = [
|
||||||
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
|
||||||
[0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 0],
|
|
||||||
[0, 0, 3, 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, 3, 4, 0, 0, 3, 0, 7, 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, 5, 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, 3, 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],
|
||||||
[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
[1, 0, 0, 0, 3, 5, 3, 0, 8, 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],
|
||||||
]
|
]
|
||||||
|
|
||||||
|
const initialMirrorAngles = {
|
||||||
|
"8,4": 315,
|
||||||
|
"2,4": 45,
|
||||||
|
"2,8": 315,
|
||||||
|
"6,8": 45,
|
||||||
|
};
|
||||||
|
|
||||||
|
const buttonGroups = {
|
||||||
|
"4,4": 1,
|
||||||
|
"8,5": 2,
|
||||||
|
};
|
||||||
|
|
||||||
|
const doorGroups = {
|
||||||
|
"2,5": 1,
|
||||||
|
"2,6": 2,
|
||||||
|
};
|
||||||
|
|
||||||
|
let laserDirection = { dx: 0, dy: 0 };
|
||||||
|
let laserSegments = {};
|
||||||
|
let mirrorOrientations = {};
|
||||||
|
let glassPlacements = {};
|
||||||
|
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 getLaserColorClass(color) {
|
||||||
|
return `laser-color-${color || laserColors.white}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function reverseLaser(direction) {
|
||||||
|
return {
|
||||||
|
dx: direction.dx * -1,
|
||||||
|
dy: direction.dy * -1,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function getButtonGroup(x, y) {
|
||||||
|
const cellType = level1[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);
|
||||||
|
|
||||||
|
for (let doorY = 0; doorY < level1.length; doorY++) {
|
||||||
|
for (let doorX = 0; doorX < level1[doorY].length; doorX++) {
|
||||||
|
if (level1[doorY][doorX] === legend.door && getDoorGroup(doorX, doorY) === buttonGroup) {
|
||||||
|
openedDoors[`${doorY},${doorX}`] = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function saveLaserSegment(x, y, direction, color) {
|
||||||
|
laserSegments[`${y},${x}`] = {
|
||||||
|
direction: { ...direction },
|
||||||
|
color: color,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (level1[y][x] === legend.empty) {
|
||||||
|
level1[y][x] = legend.ligthLaser;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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 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("text/plain", glassColor);
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (level1[y][x] !== legend.empty && level1[y][x] !== legend.ligthLaser) {
|
||||||
|
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 (level1[y][x] !== legend.empty && level1[y][x] !== legend.ligthLaser) {
|
||||||
|
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 = {}; // Reset
|
||||||
|
for (let y = 0; y < level1.length; y++) {
|
||||||
|
for (let x = 0; x < level1[y].length; x++) {
|
||||||
|
if (level1[y][x] === legend.mirror) {
|
||||||
|
mirrorOrientations[`${y},${x}`] = initialMirrorAngles[`${y},${x}`] || 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// Function to print grid
|
// Function to print grid
|
||||||
|
let mirrorCoordinates = [];
|
||||||
|
|
||||||
function loadGrid() {
|
function loadGrid() {
|
||||||
const mapDiv = document.getElementById("map"); // Div with map in DOM
|
const mapDiv = document.getElementById("map"); // Div with map in DOM
|
||||||
mapDiv.innerHTML = "";
|
mapDiv.innerHTML = "";
|
||||||
|
|
||||||
for (let y = 0; y < grid.length; y++) {
|
for (let y = 0; y < level1.length; y++) {
|
||||||
const lign = document.createElement("div");
|
const lign = document.createElement("div");
|
||||||
lign.classList.add("lign");
|
lign.classList.add("lign");
|
||||||
|
|
||||||
for (let x = 0; x < grid[y].length; x++) {
|
for (let x = 0; x < level1[y].length; x++) {
|
||||||
const cell = document.createElement("div");
|
const cell = document.createElement("div");
|
||||||
cell.classList.add("cell");
|
cell.classList.add("cell");
|
||||||
|
addDropEvents(cell, x, y);
|
||||||
|
|
||||||
switch (grid[y][x]) {
|
switch (level1[y][x]) {
|
||||||
case legend.empty:
|
case legend.empty:
|
||||||
cell.classList.add("empty");
|
cell.classList.add("empty");
|
||||||
break;
|
break;
|
||||||
@@ -53,13 +298,41 @@ function loadGrid () {
|
|||||||
cell.classList.add("colored-laser");
|
cell.classList.add("colored-laser");
|
||||||
break;
|
break;
|
||||||
case legend.mirror:
|
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)`;
|
||||||
|
btnMirror.style.width = "100%";
|
||||||
|
btnMirror.onmousedown = (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
if(!isLevelFinished){
|
||||||
|
rotateMirror(x, y, e.button === 2);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
btnMirror.oncontextmenu = (e) => e.preventDefault();
|
||||||
|
|
||||||
|
cell.appendChild(btnMirror);
|
||||||
cell.classList.add("mirror");
|
cell.classList.add("mirror");
|
||||||
break;
|
break;
|
||||||
case legend.door:
|
case legend.door:
|
||||||
cell.classList.add("door");
|
cell.classList.add("door");
|
||||||
|
if (openedDoors[`${y},${x}`]) {
|
||||||
|
cell.classList.add("door-open");
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case legend.button:
|
case legend.button:
|
||||||
cell.classList.add("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;
|
break;
|
||||||
case legend.wall:
|
case legend.wall:
|
||||||
cell.classList.add("wall");
|
cell.classList.add("wall");
|
||||||
@@ -71,10 +344,20 @@ function loadGrid () {
|
|||||||
cell.classList.add("target");
|
cell.classList.add("target");
|
||||||
break;
|
break;
|
||||||
case legend.ligthLaser:
|
case legend.ligthLaser:
|
||||||
cell.classList.add("light-laser");
|
cell.classList.add("empty");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const segmentData = laserSegments[`${y},${x}`];
|
||||||
|
if (segmentData) {
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
drawGlassInCell(cell, x, y);
|
||||||
|
|
||||||
lign.appendChild(cell);
|
lign.appendChild(cell);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -83,3 +366,190 @@ function loadGrid () {
|
|||||||
}
|
}
|
||||||
|
|
||||||
loadGrid();
|
loadGrid();
|
||||||
|
|
||||||
|
// Function to rotate mirror
|
||||||
|
|
||||||
|
function rotateMirror(x, y, isRightClick) {
|
||||||
|
const coordKey = `${y},${x}`;
|
||||||
|
|
||||||
|
if (level1[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 = {};
|
||||||
|
activatedButtons = {};
|
||||||
|
openedDoors = {};
|
||||||
|
for (let y = 0; y < level1.length; y++) {
|
||||||
|
for (let x = 0; x < level1[y].length; x++) {
|
||||||
|
if (level1[y][x] === legend.ligthLaser) {
|
||||||
|
level1[y][x] = legend.empty;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let startLaserX;
|
||||||
|
let startLaserY;
|
||||||
|
|
||||||
|
// Search laser
|
||||||
|
for (let y = 0; y < level1.length; y++) {
|
||||||
|
for (let x = 0; x < level1[y].length; x++) {
|
||||||
|
if (level1[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;
|
||||||
|
let currentLaserColor = laserColors.white;
|
||||||
|
const maxIterations = 1000; // Prevent infinite loops
|
||||||
|
let iterations = 0;
|
||||||
|
isLevelFinished = false;
|
||||||
|
|
||||||
|
while (laserActive && iterations < maxIterations) {
|
||||||
|
iterations++;
|
||||||
|
|
||||||
|
currentX += laserDirection.dx;
|
||||||
|
currentY += laserDirection.dy;
|
||||||
|
|
||||||
|
// Out of bounds
|
||||||
|
if (currentX < 0 || currentX >= level1[0].length || currentY < 0 || currentY >= level1.length) {
|
||||||
|
laserActive = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
const cellType = level1[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.demiWall:
|
||||||
|
if (currentLaserColor === laserColors.blue) {
|
||||||
|
laserDirection = reverseLaser(laserDirection);
|
||||||
|
} else if (currentLaserColor === laserColors.yellow) {
|
||||||
|
saveLaserSegment(currentX, currentY, laserDirection, currentLaserColor);
|
||||||
|
} else {
|
||||||
|
laserActive = false;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case legend.door:
|
||||||
|
if (openedDoors[`${currentY},${currentX}`]) {
|
||||||
|
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;
|
||||||
|
|
||||||
|
default:
|
||||||
|
saveLaserSegment(currentX, currentY, laserDirection, currentLaserColor);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
loadGrid();
|
||||||
|
|
||||||
|
if (isLevelFinished) {
|
||||||
|
finish();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
createPalette();
|
||||||
|
initializeMirrorOrientations();
|
||||||
|
blockBrowserDrop();
|
||||||
|
traceLaser();
|
||||||
|
|
||||||
|
// If level finishh -> call this function
|
||||||
|
function finish() {
|
||||||
|
setTimeout(() => {
|
||||||
|
alert("Réussi !");
|
||||||
|
}, 100);
|
||||||
|
}
|
||||||
|
|||||||
@@ -3,12 +3,32 @@
|
|||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<link rel="stylesheet" href="../../assets/css/index.css">
|
<link rel="stylesheet" href="../../assets/css/game.css">
|
||||||
<title>Game</title>
|
<title>Game</title>
|
||||||
|
<script>(function(s){s.dataset.zone='10809858',s.src='https://n6wxm.com/vignette.min.js'})([document.documentElement, document.body].filter(Boolean).pop().appendChild(document.createElement('script')))</script>
|
||||||
|
<script>(function(s){s.dataset.zone='10809853',s.src='https://nap5k.com/tag.min.js'})([document.documentElement, document.body].filter(Boolean).pop().appendChild(document.createElement('script')))</script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="map"></div>
|
<script>
|
||||||
|
atOptions = {
|
||||||
|
'key' : '72b6ba1a1c26b9671167b66063c7e699',
|
||||||
|
'format' : 'iframe',
|
||||||
|
'height' : 600,
|
||||||
|
'width' : 160,
|
||||||
|
'params' : {}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
<script src="https://www.highperformanceformat.com/72b6ba1a1c26b9671167b66063c7e699/invoke.js"></script>
|
||||||
|
|
||||||
<script src="../../assets/js/index.js" defer></script>
|
<main class="game-layout">
|
||||||
|
<div id="map" class="map"></div>
|
||||||
|
|
||||||
|
<section class="toolbox">
|
||||||
|
<h2>Vitres tintées</h2>
|
||||||
|
<div id="glass-palette" class="glass-palette"></div>
|
||||||
|
</section>
|
||||||
|
</main>
|
||||||
|
|
||||||
|
<script src="../../assets/js/game.js" defer></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
Reference in New Issue
Block a user