diff --git a/web/assets/css/game.css b/web/assets/css/game.css new file mode 100644 index 0000000..c057872 --- /dev/null +++ b/web/assets/css/game.css @@ -0,0 +1,422 @@ +* { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +html, +body { + width: 100vw; + height: 100vh; + overflow: hidden; +} + +body { + background: #FFF6E5; + display: flex; + align-items: center; + justify-content: center; + font-family: Arial, sans-serif; + user-select: none; +} + +main { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + padding: 20px; + border-radius: 150px; + min-width: fit-content; + flex-shrink: 0; + gap: 16px; +} + +.game-layout { + width: min(96vw, 1200px); +} + +.game-title { + font-size: clamp(2rem, 4vw, 3rem); + font-weight: 700; + color: #223; + letter-spacing: 0.04em; + text-align: center; +} + +.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; +} + +.map { + display: flex; + flex-direction: column; + padding: 10px; + background: #dadeef; + border-radius: 15px; +} + +.lign { + display: flex; +} + +.cell { + width: clamp(28px, 6.2vmin, 72px); + height: clamp(28px, 6.2vmin, 72px); + transition: all 0.2s ease; + display: flex; + align-items: center; + justify-content: center; + position: relative; + background-color: #2a2a2a; + user-select: none; + overflow: hidden; +} + +.cell.can-drop { + outline: 2px dashed rgba(0, 0, 0, 0.2); +} + +.empty { + background-color: #dadeef; +} + +.empty:hover { + background-color: #333333; +} + +.laser { + background-color: #DADEEF; + background-image: url("../img/tiles/Laser.svg"); + background-size: 80%; + background-repeat: no-repeat; + background-position: center; + transform: rotate(180deg); +} + +.colored-laser { + background-color: #DADEEF; + background-image: url("../img/tiles/Prisme.svg"); + background-size: 80%; + background-repeat: no-repeat; + background-position: center; +} + +.mirror { + background-color: #DADEEF; + position: relative; + overflow: hidden; + -moz-transform: scaleX(-1); + -webkit-transform: scaleX(-1); + -o-transform: scaleX(-1); + transform: scaleX(-1); + -ms-filter: fliph; /*IE*/ + filter: fliph; /*IE*/ +} + +.wall { + background-color: #DADEEF; + background-image: url("../img/tiles/Tuile.svg"); + background-size: 100%; + background-repeat: no-repeat; + background-position: center; +} + +.captor { + background-color: #DADEEF; + background-image: url("../img/tiles/Capteur-1.svg"); + background-size: 100%; + background-repeat: no-repeat; + background-position: center; + transform: rotate(180deg); +} + +.captor-turn { + background-color: #DADEEF; + background-image: url("../img/tiles/Capteur-2.svg"); + background-size: 100%; + background-repeat: no-repeat; + background-position: center; +} + +.cable { + background-color: #DADEEF; + background-image: url("../img/tiles/CableV.svg"); + background-size: 100%; + background-repeat: no-repeat; + background-position: center; + transform: rotate(90deg); +} + +.cable-vertical { + background-color: #DADEEF; + background-image: url("../img/tiles/CableV.svg"); + background-size: 100%; + background-repeat: no-repeat; + background-position: center; +} + +.door { + background-color: #DADEEF; + background-image: url("../img/tiles/WoodenDoor.svg"); + background-size: contain; + background-repeat: no-repeat; + background-position: end; +} + +.door-open { + background-image: url("../img/tiles/WoodenDoor_openned.svg"); + background-size: contain; + background-repeat: no-repeat; + background-position: end; +} + +.button { + background-color: #DADEEF; + background-image: url("../img/tiles/ButtonComplete.svg"), url("../img/tiles/Tuile.svg"); + background-size: 100% 100%; + background-repeat: no-repeat; + background-position: center; +} + +.button-2 { + background-color: #DADEEF; + background-image: url("../img/tiles/ButtonQuarter.svg"), url("../img/tiles/Tuile.svg"); + background-size: 100% 100%; + background-repeat: no-repeat; + background-position: center; +} + +.button-rotator { + background-color: #DADEEF; + background-image: url("../img/tiles/ButtonProfile.svg"), url("../img/tiles/Tuile.svg"); + background-size: 100% 100%; + background-repeat: no-repeat; + background-position: center; +} + +.button-active { + opacity: 0.7; +} + +.target { + background-color: #DADEEF; + background-image: url("../img/tiles/Trigger.svg"); + background-size: 80%; + background-repeat: no-repeat; + background-position: center; +} + +.demi-wall-corner-up-left { + background-color: #DADEEF; + background-image: url("../img/tiles/TopLeft.svg"); + background-size: 100%; + background-repeat: no-repeat; + background-position: center; +} + +.demi-wall-corner-up-right { + background-color: #DADEEF; + background-image: url("../img/tiles/TopRight.svg"); + background-size: 100%; + background-repeat: no-repeat; + background-position: center; +} + +.demi-wall-corner-down-left { + background-color: #DADEEF; + background-image: url("../img/tiles/BottomLeft.svg"); + background-size: 100%; + background-repeat: no-repeat; + background-position: center; +} + +.demi-wall-corner-down-right { + background-color: #DADEEF; + background-image: url("../img/tiles/BottomRight.svg"); + background-size: 100%; + background-repeat: no-repeat; + background-position: center; +} + +.laser-overlay { + 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; +} + +.btn-mirror { + background: none; + border: none; + cursor: pointer; + width: 100%; + height: 100%; + position: relative; + z-index: 3; + display: flex; + align-items: center; + justify-content: center; +} + +.btn-mirror-locked { + cursor: default; +} + +.mirror-img { + width: 80%; + height: 80%; + object-fit: contain; + pointer-events: none; +} + +.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; + color: #223; + font-size: 0.8rem; + font-weight: bold; +} + +.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; +} + +@media (max-width: 600px) { + .map { + padding: 5px; + } + + main { + padding: 8px; + } +} + +.win-overlay { + position: absolute; + inset: 0; + background: rgba(0, 0, 0, 0.5); + z-index: 1000; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + visibility: hidden; + gap: 20px; + backdrop-filter: blur(6px); + +} + +.win-overlay h1 { + font-size: 2rem; + font-weight: 700; + color: #fff; + text-align: center; +} + +.win-overlay p { + font-size: 1.5rem; + font-weight: 400; + color: #fff; + text-align: center; +} + +.win-overlay button { + font-size: 1.5rem; + padding: 10px 20px; + border-radius: 5px; + background-color: #fff; + color: #000; + border: none; + cursor: pointer; +} diff --git a/web/assets/css/index.css b/web/assets/css/index.css deleted file mode 100644 index c14b521..0000000 --- a/web/assets/css/index.css +++ /dev/null @@ -1,244 +0,0 @@ -@import url('https://fonts.googleapis.com/css2?family=Inter:wght@100..900&display=swap'); - -* { - --clr-light-a0: #ffffff; - --clr-light-a10: #f4f4f4; - --clr-light-a20: #e0e0e0; - --clr-light-a30: #c2c2c2; - --clr-light-a40: #a3a3a3; - --clr-light-a50: #858585; - --clr-dark: #000000; - - --clr-surface-a0: #FFF6E5; - --clr-surface-a10: #f7f7f7; - --clr-surface-a20: #DADEEF; - --clr-surface-a30: #e0e0e0; - --clr-surface-a40: #d1d1d1; - --clr-surface-a50: #c2c2c2; - box-sizing: border-box; -} - -html { - font-size: 100%; -} - -body { - background-color: var(--clr-surface-a0); - font-family: 'Inter', sans-serif; - margin: 0; - padding: 0; -} - -.hero { - position: relative; - display: flex; - flex-direction: row; - justify-content: center; - align-items: center; - min-height: 100vh; - gap: 3rem; - padding: 2rem clamp(1rem, 4vw, 3rem); - box-sizing: border-box; - background: var(--clr-surface-a10); -} - -.adsbanner { - position: absolute; - top: 50%; - left: clamp(0.75rem, 3vw, 2.5rem); - transform: translateY(-50%); - z-index: 5; - width: clamp(160px, 22vw, 300px); - aspect-ratio: 9 / 16; - box-shadow: 0 18px 40px rgba(0,0,0,0.18); -} - -.adsbanner.is-hidden { - display: none; -} - -.leftadsbanner { - position: absolute; - top: 50%; - right: clamp(0.75rem, 3vw, 2.5rem); - transform: translateY(-50%); - z-index: 5; - width: clamp(160px, 22vw, 300px); - aspect-ratio: 9 / 16; -} - -.hero-content { - max-width: 90vw; - text-align: left; - display: flex; - flex-direction: column; - align-items: center; - background: var(--clr-surface-a20); - border-radius: 1.5rem; - padding: 2rem clamp(2rem, 8vw, 10rem); - box-shadow: 0px 0px 27px 14px rgba(0,0,0,0.2); -} - -.hero-content-text { - width: 100%; -} - -.hero h1 { - color: var(--clr-dark); - font-size: clamp(2.5rem, 7vw, 4rem); - margin: 0 0 1rem 0; - line-height: 1.1; - word-break: break-word; - text-shadow: none; -} - -.hero-content button { - margin-top: 2rem; - align-self: center; -} - -.hero img { - max-width: 400px; - width: 35vw; - min-width: 180px; - height: auto; - border-radius: 1.5rem; - box-shadow: 0 6px 32px rgba(0,0,0,.10); - flex-shrink: 1; - background: var(--clr-surface-a0); -} - -/* --- RESPONSIVE HERO-PLAY-BUTTON FIXES --- */ - -@media (max-width: 900px) { - .hero { - flex-direction: column; - text-align: center; - gap: 2rem; - padding: 1.5rem; - } - .hero-content { - max-width: 100vw; - text-align: center; - padding: 2rem; - } - .hero-content { - align-items: center; - } - .adsbanner { - left: 1rem; - width: clamp(130px, 24vw, 220px); - } - .leftadsbanner { - right: 1rem; - width: clamp(130px, 24vw, 220px); - } - .hero img { - max-width: 70vw; - min-width: 0; - } - .hero-play-button { - padding: 1rem 4vw; - font-size: 1.1rem; - min-width: min(220px, 70vw); - max-width: 90vw; - } - .hero-play-button svg { - width: 2em; - height: 1em; - min-width: 1.25em; - min-height: 1em; - margin-right: 0.5em; - flex-shrink: 0; - } -} - -@media (max-width: 600px) { - .hero { - min-height: 70vh; - padding: 0.5rem; - } - .hero-content { - padding: 1.5rem; - } - .hero h1 { - font-size: clamp(2rem, 9vw, 2.5rem); - } - .hero p { - font-size: clamp(1rem, 5vw, 1.15rem); - } - .adsbanner { - top: 1rem; - left: 0.75rem; - transform: none; - width: clamp(110px, 30vw, 170px); - border-radius: 1rem; - } - .leftadsbanner { - right: 0.75rem; - width: clamp(110px, 30vw, 170px); - border-radius: 1rem; - } - .hero img { - max-width: 100%; - border-radius: 1rem; - box-shadow: 0 2px 12px rgba(0,0,0,.07); - } - .hero-play-button { - padding: 0.85rem 3vw; - min-width: min(120px, 92vw); - max-width: 96vw; - font-size: 1rem; - } - .hero-play-button svg { - width: 1.3em; - height: 1em; - min-width: 1em; - min-height: 1em; - margin-right: 0.45em; - flex-shrink: 0; - } -} - -.hero-play-button { - font-family: inherit; - font-size: 20px; - background: #8ea3fd; - color: white; - padding: 1rem 10rem; - display: flex; - align-items: center; - justify-content: center; /* Ensure SVG/button content is horizontally centered */ - border: none; - border-radius: 50px; - overflow: hidden; - transition: all 0.2s; - cursor: pointer; - box-shadow: 0 2px 8px rgba(30, 80, 255, 0.10); - /* Button grows/shrinks with container at low widths */ - width: 100%; - max-width: 500px; - min-width: 180px; - box-sizing: border-box; -} -.hero-play-button:hover { - background: #7286e0; - transform: translateY(-2px); -} - -.hero-play-button:active { - transform: scale(0.95); -} - -.hero-play-button svg { - width: 2em; - height: 1em; - min-width: 1.2em; - min-height: 1em; - margin-right: 0.6em; - color: var(--clr-surface-a0); - flex-shrink: 0; - flex-grow: 0; - display: inline-block; - vertical-align: middle; -} diff --git a/web/assets/img/img_test_main_menu.png b/web/assets/img/img_test_main_menu.png new file mode 100644 index 0000000..5951742 Binary files /dev/null and b/web/assets/img/img_test_main_menu.png differ diff --git a/web/assets/img/tiles/BlueMirror.svg b/web/assets/img/tiles/BlueMirror.svg new file mode 100644 index 0000000..f86033a --- /dev/null +++ b/web/assets/img/tiles/BlueMirror.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/web/assets/img/tiles/BottomLeft-1.svg b/web/assets/img/tiles/BottomLeft-1.svg new file mode 100644 index 0000000..dbbb4da --- /dev/null +++ b/web/assets/img/tiles/BottomLeft-1.svg @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + diff --git a/web/assets/img/tiles/BottomLeft-2.svg b/web/assets/img/tiles/BottomLeft-2.svg new file mode 100644 index 0000000..b6c8f15 --- /dev/null +++ b/web/assets/img/tiles/BottomLeft-2.svg @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + diff --git a/web/assets/img/tiles/BottomLeft.svg b/web/assets/img/tiles/BottomLeft.svg new file mode 100644 index 0000000..15a864e --- /dev/null +++ b/web/assets/img/tiles/BottomLeft.svg @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + diff --git a/web/assets/img/tiles/BottomRight-1.svg b/web/assets/img/tiles/BottomRight-1.svg new file mode 100644 index 0000000..c316dc7 --- /dev/null +++ b/web/assets/img/tiles/BottomRight-1.svg @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + diff --git a/web/assets/img/tiles/BottomRight-2.svg b/web/assets/img/tiles/BottomRight-2.svg new file mode 100644 index 0000000..a1e5bcb --- /dev/null +++ b/web/assets/img/tiles/BottomRight-2.svg @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + diff --git a/web/assets/img/tiles/BottomRight.svg b/web/assets/img/tiles/BottomRight.svg new file mode 100644 index 0000000..3e8e4df --- /dev/null +++ b/web/assets/img/tiles/BottomRight.svg @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + diff --git a/web/assets/img/tiles/ButtonComplete.svg b/web/assets/img/tiles/ButtonComplete.svg new file mode 100644 index 0000000..440195e --- /dev/null +++ b/web/assets/img/tiles/ButtonComplete.svg @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/web/assets/img/tiles/ButtonProfile.svg b/web/assets/img/tiles/ButtonProfile.svg new file mode 100644 index 0000000..9f0380d --- /dev/null +++ b/web/assets/img/tiles/ButtonProfile.svg @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/web/assets/img/tiles/ButtonQuarter.svg b/web/assets/img/tiles/ButtonQuarter.svg new file mode 100644 index 0000000..9bb3afa --- /dev/null +++ b/web/assets/img/tiles/ButtonQuarter.svg @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/web/assets/img/tiles/CableBottomLeft.svg b/web/assets/img/tiles/CableBottomLeft.svg new file mode 100644 index 0000000..2de6044 --- /dev/null +++ b/web/assets/img/tiles/CableBottomLeft.svg @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/web/assets/img/tiles/CableBottomRight.svg b/web/assets/img/tiles/CableBottomRight.svg new file mode 100644 index 0000000..0435974 --- /dev/null +++ b/web/assets/img/tiles/CableBottomRight.svg @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/web/assets/img/tiles/CableH.svg b/web/assets/img/tiles/CableH.svg new file mode 100644 index 0000000..440786e --- /dev/null +++ b/web/assets/img/tiles/CableH.svg @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/web/assets/img/tiles/CableTopLeft.svg b/web/assets/img/tiles/CableTopLeft.svg new file mode 100644 index 0000000..66da788 --- /dev/null +++ b/web/assets/img/tiles/CableTopLeft.svg @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/web/assets/img/tiles/CableTopRight.svg b/web/assets/img/tiles/CableTopRight.svg new file mode 100644 index 0000000..6c7f58a --- /dev/null +++ b/web/assets/img/tiles/CableTopRight.svg @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/web/assets/img/tiles/CableV.svg b/web/assets/img/tiles/CableV.svg new file mode 100644 index 0000000..3b091be --- /dev/null +++ b/web/assets/img/tiles/CableV.svg @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/web/assets/img/tiles/Capteur-1.svg b/web/assets/img/tiles/Capteur-1.svg new file mode 100644 index 0000000..963bdb5 --- /dev/null +++ b/web/assets/img/tiles/Capteur-1.svg @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/web/assets/img/tiles/Capteur-2.svg b/web/assets/img/tiles/Capteur-2.svg new file mode 100644 index 0000000..8e27567 --- /dev/null +++ b/web/assets/img/tiles/Capteur-2.svg @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/web/assets/img/tiles/Capteur.svg b/web/assets/img/tiles/Capteur.svg new file mode 100644 index 0000000..931ae79 --- /dev/null +++ b/web/assets/img/tiles/Capteur.svg @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/web/assets/img/tiles/HorizontaleSemi.svg b/web/assets/img/tiles/HorizontaleSemi.svg new file mode 100644 index 0000000..d08d2bb --- /dev/null +++ b/web/assets/img/tiles/HorizontaleSemi.svg @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/web/assets/img/tiles/Laser.svg b/web/assets/img/tiles/Laser.svg new file mode 100644 index 0000000..354d5b8 --- /dev/null +++ b/web/assets/img/tiles/Laser.svg @@ -0,0 +1,44 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/web/assets/img/tiles/MetalDoor.svg b/web/assets/img/tiles/MetalDoor.svg new file mode 100644 index 0000000..c28b339 --- /dev/null +++ b/web/assets/img/tiles/MetalDoor.svg @@ -0,0 +1,46 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/web/assets/img/tiles/MetalMirror.svg b/web/assets/img/tiles/MetalMirror.svg new file mode 100644 index 0000000..8db4eb9 --- /dev/null +++ b/web/assets/img/tiles/MetalMirror.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/web/assets/img/tiles/Mirror.svg b/web/assets/img/tiles/Mirror.svg new file mode 100644 index 0000000..56cc5f1 --- /dev/null +++ b/web/assets/img/tiles/Mirror.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/web/assets/img/tiles/Prisme.svg b/web/assets/img/tiles/Prisme.svg new file mode 100644 index 0000000..1c05d06 --- /dev/null +++ b/web/assets/img/tiles/Prisme.svg @@ -0,0 +1,3 @@ + + + diff --git a/web/assets/img/tiles/RedMirror.svg b/web/assets/img/tiles/RedMirror.svg new file mode 100644 index 0000000..acbbba0 --- /dev/null +++ b/web/assets/img/tiles/RedMirror.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/web/assets/img/tiles/TopLeft-1.svg b/web/assets/img/tiles/TopLeft-1.svg new file mode 100644 index 0000000..8a3dbc1 --- /dev/null +++ b/web/assets/img/tiles/TopLeft-1.svg @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + diff --git a/web/assets/img/tiles/TopLeft-2.svg b/web/assets/img/tiles/TopLeft-2.svg new file mode 100644 index 0000000..026a2ee --- /dev/null +++ b/web/assets/img/tiles/TopLeft-2.svg @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + diff --git a/web/assets/img/tiles/TopLeft.svg b/web/assets/img/tiles/TopLeft.svg new file mode 100644 index 0000000..ea2e90d --- /dev/null +++ b/web/assets/img/tiles/TopLeft.svg @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + diff --git a/web/assets/img/tiles/TopRight-1.svg b/web/assets/img/tiles/TopRight-1.svg new file mode 100644 index 0000000..d01eafe --- /dev/null +++ b/web/assets/img/tiles/TopRight-1.svg @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + diff --git a/web/assets/img/tiles/TopRight-2.svg b/web/assets/img/tiles/TopRight-2.svg new file mode 100644 index 0000000..c4e54a5 --- /dev/null +++ b/web/assets/img/tiles/TopRight-2.svg @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + diff --git a/web/assets/img/tiles/TopRight.svg b/web/assets/img/tiles/TopRight.svg new file mode 100644 index 0000000..81e21f4 --- /dev/null +++ b/web/assets/img/tiles/TopRight.svg @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + diff --git a/web/assets/img/tiles/Trigger.svg b/web/assets/img/tiles/Trigger.svg new file mode 100644 index 0000000..324fefa --- /dev/null +++ b/web/assets/img/tiles/Trigger.svg @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/web/assets/img/tiles/Tuile.svg b/web/assets/img/tiles/Tuile.svg new file mode 100644 index 0000000..2b7bce9 --- /dev/null +++ b/web/assets/img/tiles/Tuile.svg @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/web/assets/img/tiles/VerticaleSemi.svg b/web/assets/img/tiles/VerticaleSemi.svg new file mode 100644 index 0000000..3fa0309 --- /dev/null +++ b/web/assets/img/tiles/VerticaleSemi.svg @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/web/assets/img/tiles/WoodenDoor.svg b/web/assets/img/tiles/WoodenDoor.svg new file mode 100644 index 0000000..bead2b3 --- /dev/null +++ b/web/assets/img/tiles/WoodenDoor.svg @@ -0,0 +1,49 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/web/assets/img/tiles/WoodenDoor_openned.svg b/web/assets/img/tiles/WoodenDoor_openned.svg new file mode 100644 index 0000000..0c07c55 --- /dev/null +++ b/web/assets/img/tiles/WoodenDoor_openned.svg @@ -0,0 +1,53 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/web/assets/img/tiles/WoodenMirror.svg b/web/assets/img/tiles/WoodenMirror.svg new file mode 100644 index 0000000..56cc5f1 --- /dev/null +++ b/web/assets/img/tiles/WoodenMirror.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/web/assets/img/tiles/YellowMirror.svg b/web/assets/img/tiles/YellowMirror.svg new file mode 100644 index 0000000..268a7a9 --- /dev/null +++ b/web/assets/img/tiles/YellowMirror.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/web/assets/js/game.js b/web/assets/js/game.js new file mode 100644 index 0000000..49ee375 --- /dev/null +++ b/web/assets/js/game.js @@ -0,0 +1,839 @@ +// 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, + captorTurn: 17, + cableVertical: 18, +}; + +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, 6, 6, 6, 6, 6, 6, 6, 6, 11, 0, 0, 0, 0], + [0, 0, 1, 0, 0, 0, 17, 0, 0, 3, 6, 0, 0, 0, 0], + [0, 0, 6, 6, 6, 6, 18, 6, 6, 0, 6, 0, 0, 0, 0], + [0, 0, 7, 0, 0, 0, 4, 0, 0, 12, 6, 0, 0, 0, 0], + [0, 0, 6, 6, 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, 0, 0, 0, 0, 0, 0, 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, 3, 0, 16, 16, 20, 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, 3, 0, 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, + }, + {}, + { + "3,4": 315, + "7,8": 0, + }, +]; + +const buttonGroups = [ + { + "4,6": 1, + }, + {}, + {}, +]; + +const doorGroups = [ + { + "4,7": 1, + }, + { + "4,6": 1, + }, + {}, +]; + +const captorGroups = [ + {}, + { + "2,6": 1, + }, + {}, +]; + +const rotatorButtons = [ + {}, + {}, + { + "3,7": { mirrorX: 7, mirrorY: 7, step: -22.5, intervalMs: 1000 }, + }, +]; + +let laserDirection = { dx: 0, dy: 0 }; +let laserSegments = {}; +let mirrorOrientations = {}; +let glassPlacements = {}; +let activatedButtons = {}; +let openedDoors = {}; +let isLevelFinished = false; +let activeRotatorButtons = {}; +let rotatorIntervals = {}; +let toggledDoors = {}; +let poweredCaptors = {}; + +function getCurrentLevel() { + return levels[currentLevelIndex]; +} + +function getCurrentLevelConfig(configByLevel) { + return configByLevel[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 getCurrentLevelConfig(buttonGroups)[`${y},${x}`] || 1; +} + +function getDoorGroup(x, y) { + return getCurrentLevelConfig(doorGroups)[`${y},${x}`] || 1; +} + +function getCaptorGroup(x, y) { + return getCurrentLevelConfig(captorGroups)[`${y},${x}`] || getDoorGroup(x, y); +} + +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 toggleDoorsFromCaptor(x, y) { + const captorGroup = getCaptorGroup(x, y); + const level = getCurrentLevel(); + + for (let doorY = 0; doorY < level.length; doorY++) { + for (let doorX = 0; doorX < level[doorY].length; doorX++) { + const coordKey = `${doorY},${doorX}`; + + if (level[doorY][doorX] === legend.door && getDoorGroup(doorX, doorY) === captorGroup) { + toggledDoors[coordKey] = !toggledDoors[coordKey]; + } + } + } +} + +function getRotatorButtonConfig(x, y) { + return getCurrentLevelConfig(rotatorButtons)[`${y},${x}`]; +} + +function isMirrorControlledByButton(x, y) { + const rotatorEntries = Object.values(getCurrentLevelConfig(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 currentLevelRotatorButtons = getCurrentLevelConfig(rotatorButtons); + const rotatorKeys = Object.keys(currentLevelRotatorButtons); + + for (let i = 0; i < rotatorKeys.length; i++) { + const key = rotatorKeys[i]; + const config = currentLevelRotatorButtons[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}`] = getCurrentLevelConfig(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; + case legend.captorTurn: + cell.classList.add("captor-turn"); + break; + case legend.cableVertical: + cell.classList.add("cable-vertical"); + 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 = { ...toggledDoors }; + activeRotatorButtons = {}; + isLevelFinished = false; + const nextPoweredCaptors = {}; + + 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.captor: + case legend.captorTurn: + if (currentLaserColor === laserColors.red) { + const captorKey = `${currentY},${currentX}`; + activatedButtons[captorKey] = true; + nextPoweredCaptors[captorKey] = true; + + if (!poweredCaptors[captorKey]) { + toggleDoorsFromCaptor(currentX, currentY); + openedDoors = { ...toggledDoors }; + } + + saveLaserSegment(currentX, currentY, laserDirection, currentLaserColor); + laserActive = false; + }else if(currentLaserColor === laserColors.yellow) { + saveLaserSegment(currentX, currentY, laserDirection, currentLaserColor); + } 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); + laserActive = false; + } else if (currentLaserColor === laserColors.yellow) { + saveLaserSegment(currentX, currentY, laserDirection, currentLaserColor); + } else if (currentLaserColor === laserColors.blue) { + laserDirection = reverseLaser(laserDirection); + laserActive = false; + } else { + laserActive = false; + } + break; + + case legend.demiWallCornerUpLeft: + if(currentLaserColor === laserColors.blue) { + laserDirection = reflectLaser(laserDirection, 135); + } + break; + + case legend.demiWallCornerUpRight: + if(currentLaserColor === laserColors.blue) { + laserDirection = reflectLaser(laserDirection, 45); + } + break; + + case legend.demiWallCornerDownLeft: + if(currentLaserColor === laserColors.blue) { + laserDirection = reflectLaser(laserDirection, 225); + } + break; + + case legend.demiWallCornerDownRight: + if(currentLaserColor === laserColors.blue) { + laserDirection = reflectLaser(laserDirection, 315); + } + break; + + default: + saveLaserSegment(currentX, currentY, laserDirection, currentLaserColor); + break; + } + } + + poweredCaptors = nextPoweredCaptors; + 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 = {}; + toggledDoors = {}; + poweredCaptors = {}; + traceLaser(); + + const winOverlay = document.querySelector(".win-overlay"); + winOverlay.style.visibility = "hidden"; +} + +createPalette(); +initializeMirrorOrientations(); +blockBrowserDrop(); +traceLaser(); diff --git a/web/assets/js/index.js b/web/assets/js/index.js index e69de29..42e3a2c 100644 --- a/web/assets/js/index.js +++ b/web/assets/js/index.js @@ -0,0 +1,9 @@ +function rotateMirror(mirror) { + let angle = 0; + if (mirror.style.transform == "") { + angle = 0; + } else { + angle = parseInt(mirror.style.transform.split("(")[1].split("deg")[0])%360; + } + mirror.style.transform = `rotate(${angle+45}deg)`; +} \ No newline at end of file diff --git a/web/templates/view/game.html b/web/templates/view/game.html new file mode 100644 index 0000000..8ee4b57 --- /dev/null +++ b/web/templates/view/game.html @@ -0,0 +1,51 @@ + + + + + + + Game + + + + +
+

You win!

+

You have completed the level.

+ +
+ + + + +
+

Mirror Game

+
+ +
+

Vitres tintées

+
+
+
+ + + + + + diff --git a/web/templates/view/index.html b/web/templates/view/index.html deleted file mode 100644 index c5075c6..0000000 --- a/web/templates/view/index.html +++ /dev/null @@ -1,28 +0,0 @@ - - - - - - MirorGame - - - -
- -
-

MirrorGame

- MirrorGame - -
-
- - -