40 Commits

Author SHA1 Message Date
Sysy's
954af3cded Fix relative paths 2026-03-31 14:55:00 +02:00
Sysy's
cfeded5079 X 2026-03-31 14:53:26 +02:00
Sysy's
1eb3aec524 Update game.js 2026-03-31 14:35:01 +02:00
1145d26e9d add lvl 4 2026-03-31 14:27:06 +02:00
Sysy's
f4c821ae97 Replace void with walls 2026-03-31 14:16:05 +02:00
Sysy's
90a0de0429 Add level selection menu and persistence
Introduce a level selection UI and persistence for unlocked levels. Adds CSS styles and HTML markup for a floating level menu, and JS to load/save highest unlocked level to localStorage (key: mirror-game-highest-unlocked-level). Implements functions to render/toggle the menu, unlock levels on finish, and navigate to arbitrary levels via goToLevel. nextLevel now delegates to goToLevel, and setup calls loadUnlockedLevels() and setupLevelMenu() so the menu reflects progress immediately.
2026-03-31 14:10:37 +02:00
Sysy's
34d213d5f4 Support limited glass inventory and yellow laser
Introduce per-level glass inventory and UI for draggable glass pieces, including disabled state and count labels. glassOptions now hold objects with color, maxAmount and currentAmount; palette creation uses these values to enable/disable dragging, show remaining counts, and update on place/remove. Drag-and-drop logic now tracks a draggedGlassColor fallback, prevents placing when inventory is empty, decrements/increments inventory on place/remove, and rebuilds the palette. Reset inventory on level start and when advancing levels. Also adjust laser tracing: yellow lasers are saved as segments and terminate on target, and yellow interacts with demi-wall corners by saving the segment instead of reflecting (blue still reflects). Add CSS for .glass-item:disabled and .glass-item-label. Overall fixes inventory handling and yellow-laser behavior.
2026-03-31 14:05:35 +02:00
Sysy's
ab4043defa Merge branch 'feature/grid' of https://git.ninolbt.com/Nono/Projet_48h into feature/grid 2026-03-31 14:04:04 +02:00
6229fe7b9e Level 3 2026-03-31 13:49:02 +02:00
Sysy's
a9d2a0c9a5 Support per-level game element configs
Convert global config maps (initialMirrorAngles, buttonGroups, doorGroups, captorGroups, rotatorButtons) into per-level arrays and add getCurrentLevelConfig(levelConfigs) helper. Update getButtonGroup, getDoorGroup, getCaptorGroup, getRotatorButtonConfig, isMirrorControlledByButton, syncRotatorButtons and initializeMirrorOrientations to pull configs for the current level. Also tweak a couple of level tiles and add a rotator button config for the third level ("3,7": { mirrorX: 7, mirrorY: 7, step: -22.5, intervalMs: 1000 }) while keeping empty objects for levels without overrides. These changes enable per-level customization of mirrors, buttons, doors, captors and rotators.
2026-03-31 13:34:27 +02:00
Sysy's
9c7c2ddc1d Implement captors and color-dependent laser effects
Add captor support and related door-toggling state plus color-specific laser interactions. Changes include: replace one level tile value (15 -> 20), add captorGroups/getCaptorGroup, add toggleDoorsFromCaptor, and new state (toggledDoors, poweredCaptors). traceLaser now initializes openedDoors from toggledDoors, tracks nextPoweredCaptors, handles captor tiles (red lasers power/toggle door groups, yellow passes, others stop), updates poweredCaptors, and applies color-based behavior for rotator buttons and demi-wall reflections (only reflect for blue). Reset toggledDoors and poweredCaptors on next level. These changes implement captor mechanics that toggle door groups and ensure correct color-dependent laser effects.
2026-03-31 13:27:08 +02:00
Sysy's
edea5e0972 Remove rotatorButton on lvl 1 2026-03-31 13:15:38 +02:00
Sysy's
f3809db72b Merge branch 'feature/grid' of https://git.ninolbt.com/Nono/Projet_48h into feature/grid 2026-03-31 13:12:34 +02:00
Sysy's
68b6493e50 Add rotator buttons and auto-rotating mirrors
Introduce a new rotator button tile and mirror auto-rotation feature. CSS adds styles for rotator buttons and locked mirror display. JS: add legend entry and rotatorButtons config, track activeRotatorButtons and rotatorIntervals, implement rotateMirrorStep, isMirrorControlledByButton, syncRotatorButtons and stopAllRotatorButtons; wire rotator behavior into traceLaser and loadGrid so rotator buttons activate intervals that rotate target mirrors automatically and lock out manual rotation. Refactor rotateMirror to reuse rotation step logic and ensure rotator intervals are cleared when advancing levels.
2026-03-31 13:11:30 +02:00
a267884dbe Level 3 2026-03-31 12:59:59 +02:00
64b0e5e770 Level 2 2026-03-31 12:19:52 +02:00
57b37d0139 Level 2 2026-03-31 12:16:45 +02:00
4939b74fad Level 2 2026-03-31 12:11:39 +02:00
7e5de16a02 Level 2 2026-03-31 12:09:01 +02:00
Sysy's
49045e4d76 Add win overlay 2026-03-31 12:07:37 +02:00
Sysy's
43100e65e9 Add game title and update layout styles
Adjust UI visuals: change page background to #FFF6E5, increase main container border-radius from 10px to 150px, and soften toolbox corners (5px → 15px). Add a new .game-title CSS rule (responsive font-size, weight, color, letter-spacing, centered) and insert an <h1 class="game-title">Mirror Game</h1> into the game template. These are visual polish changes only; no gameplay logic was modified.
2026-03-31 12:00:03 +02:00
Sysy's
94ddc4a00f Make size bigger + add ads 2026-03-31 11:55:20 +02:00
Sysy's
abf097e117 Fix textures/button/lasers & add ads 2026-03-31 11:52:30 +02:00
4fc21e7876 Resolution merge conflicts 2026-03-31 11:32:40 +02:00
37aa7b5067 Map modified + sprites 2026-03-31 10:57:19 +02:00
Sysy's
09d54aa525 Add diagonal lasers 2026-03-31 09:46:29 +02:00
Sysy's
76de2a5a5c Remove player + right click change rotation mirror 2026-03-31 09:40:25 +02:00
Sysy's
284e396d5f Make mirror rotate on click 2026-03-31 09:32:59 +02:00
Sysy's
4ba42f6566 Track laser segments and update UI theme
Add per-cell laserSegments tracking and use it to render correct laser orientation: introduce laserSegments global, reset it at trace start, populate entries when tracing, and consult it in loadGrid to choose horizontal vs vertical classes. Update UI styling from a dark to a lighter theme (body and main backgrounds, cell/empty/mirror/wall colors and laser gradients), remove some borders/fit-content sizing, and add a "map" class to the map container in the HTML. These changes fix laser orientation rendering and refresh the game's visual theme.
2026-03-31 09:20:28 +02:00
M1n-0
d0e282b41c add .gitignore 2026-03-31 09:17:02 +02:00
024b80d393 Upgrade of design of light of laser + add posibility to move a cursor and rotate mirror with keyboard 2026-03-30 22:06:44 +02:00
34fbb797c1 Laser print and reflect 2026-03-30 16:48:53 +02:00
b10b6475d8 First version of button for mirrors 2026-03-30 15:33:49 +02:00
2dbfff770f Grid enlargement 2026-03-30 15:29:25 +02:00
a791415bd3 Rebase of feature/mirror + merge with the grid program 2026-03-30 15:05:57 +02:00
Sysy's
577357b090 Add rotateMirror to rotate element 45°
Add a small utility function in web/assets/js/index.js that increments an element's CSS rotation by 45 degrees. The function reads the element's inline transform (handling an empty value), parses the current rotation angle modulo 360, and sets the new rotate(angle+45) value.
2026-03-30 14:54:43 +02:00
1be3750672 Change index.js and index.css to game.js and game.css 2026-03-30 14:51:37 +02:00
5668d21b0d End of grid 2026-03-30 14:16:30 +02:00
001adb89bd First version of laser 2026-03-30 14:10:19 +02:00
04a0e1a912 Print grid on html + minimalist css (which will not be used) 2026-03-30 12:22:56 +02:00
8 changed files with 615 additions and 111 deletions

View File

@@ -20,6 +20,71 @@ body {
user-select: none;
}
.level-menu-shell {
position: fixed;
top: 24px;
right: 24px;
z-index: 1100;
display: flex;
align-items: flex-start;
gap: 10px;
}
.level-menu-toggle {
border: none;
border-radius: 999px;
background: #223;
color: #fff;
padding: 12px 18px;
font-size: 0.95rem;
font-weight: 700;
cursor: pointer;
box-shadow: 0 8px 20px rgba(34, 51, 68, 0.18);
}
.level-menu-panel {
min-width: 180px;
background: rgba(255, 255, 255, 0.96);
border-radius: 18px;
padding: 14px;
box-shadow: 0 18px 40px rgba(34, 51, 68, 0.18);
border: 1px solid rgba(34, 51, 68, 0.08);
}
.level-menu-panel h2 {
font-size: 0.95rem;
color: #223;
margin-bottom: 10px;
}
.level-menu-list {
display: flex;
flex-direction: column;
gap: 8px;
}
.level-menu-item {
border: none;
border-radius: 10px;
padding: 10px 12px;
text-align: left;
background: #dfe5f8;
color: #223;
font-weight: 700;
cursor: pointer;
}
.level-menu-item.is-current {
background: #223;
color: #fff;
}
.level-menu-item:disabled {
background: #eceff7;
color: #8a92a3;
cursor: not-allowed;
}
main {
display: flex;
flex-direction: column;
@@ -108,7 +173,7 @@ main {
.laser {
background-color: #DADEEF;
background-image: url("../img/tiles/Laser.svg");
background-image: url("/web/assets/img/tiles/Laser.svg");
background-size: 80%;
background-repeat: no-repeat;
background-position: center;
@@ -117,7 +182,7 @@ main {
.colored-laser {
background-color: #DADEEF;
background-image: url("../img/tiles/Prisme.svg");
background-image: url("/web/assets/img/tiles/Prisme.svg");
background-size: 80%;
background-repeat: no-repeat;
background-position: center;
@@ -137,15 +202,31 @@ main {
.wall {
background-color: #DADEEF;
background-image: url("../img/tiles/Tuile.svg");
background-image: url("/web/assets/img/tiles/Tuile.svg");
background-size: 100%;
background-repeat: no-repeat;
background-position: center;
}
.wall-semi-angle {
background-color: #DADEEF;
background-image: url("/web/assets/img/tiles/BottomLeft.svg");
background-size: 100%;
background-repeat: no-repeat;
background-position: center;
}
.horizontal-semi {
background-color: #DADEEF;
background-image: url("/web/assets/img/tiles/HorizontaleSemi.svg");
background-size: 100%;
background-repeat: no-repeat;
background-position: bottom;
}
.captor {
background-color: #DADEEF;
background-image: url("../img/tiles/Capteur-1.svg");
background-image: url("/web/assets/img/tiles/Capteur-1.svg");
background-size: 100%;
background-repeat: no-repeat;
background-position: center;
@@ -154,7 +235,24 @@ main {
.captor-turn {
background-color: #DADEEF;
background-image: url("../img/tiles/Capteur-2.svg");
background-image: url("/web/assets/img/tiles/Capteur-2.svg");
background-size: 100%;
background-repeat: no-repeat;
background-position: center;
}
.captor-turn-reverse {
background-color: #DADEEF;
background-image: url("/web/assets/img/tiles/Capteur-2.svg");
background-size: 100%;
background-repeat: no-repeat;
background-position: center;
transform: rotate(180deg);
}
.captor-turn-horizontale2 {
background-color: #DADEEF;
background-image: url("/web/assets/img/tiles/Capteur-1.svg");
background-size: 100%;
background-repeat: no-repeat;
background-position: center;
@@ -162,31 +260,55 @@ main {
.cable {
background-color: #DADEEF;
background-image: url("../img/tiles/CableV.svg");
background-image: url("/web/assets/img/tiles/CableH.svg");
background-size: 100%;
background-repeat: no-repeat;
background-position: center;
}
.cable-turn {
background-color: #DADEEF;
background-image: url("/web/assets/img/tiles/CableBottomLeft.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-image: url("/web/assets/img/tiles/CableV.svg");
background-size: 100%;
background-repeat: no-repeat;
background-position: center;
}
.cable-turn-horizontale {
background-color: #DADEEF;
background-image: url("/web/assets/img/tiles/CableTopLeft.svg");
background-size: 100%;
background-repeat: no-repeat;
background-position: center;
}
.cable-turn-horizontale2 {
background-color: #DADEEF;
background-image: url("/web/assets/img/tiles/CableTopLeft.svg");
background-size: 100%;
background-repeat: no-repeat;
background-position: center;
transform: rotate(180deg);
}
.door {
background-color: #DADEEF;
background-image: url("../img/tiles/WoodenDoor.svg");
background-image: url("/web/assets/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-image: url("/web/assets/img/tiles/WoodenDoor_openned.svg");
background-size: contain;
background-repeat: no-repeat;
background-position: end;
@@ -194,7 +316,7 @@ main {
.button {
background-color: #DADEEF;
background-image: url("../img/tiles/ButtonComplete.svg"), url("../img/tiles/Tuile.svg");
background-image: url("/web/assets/img/tiles/ButtonComplete.svg"), url("/web/assets/img/tiles/Tuile.svg");
background-size: 100% 100%;
background-repeat: no-repeat;
background-position: center;
@@ -202,7 +324,7 @@ main {
.button-2 {
background-color: #DADEEF;
background-image: url("../img/tiles/ButtonQuarter.svg"), url("../img/tiles/Tuile.svg");
background-image: url("/web/assets/img/tiles/ButtonQuarter.svg"), url("/web/assets/img/tiles/Tuile.svg");
background-size: 100% 100%;
background-repeat: no-repeat;
background-position: center;
@@ -210,7 +332,7 @@ main {
.button-rotator {
background-color: #DADEEF;
background-image: url("../img/tiles/ButtonProfile.svg"), url("../img/tiles/Tuile.svg");
background-image: url("/web/assets/img/tiles/ButtonProfile.svg"), url("/web/assets/img/tiles/Tuile.svg");
background-size: 100% 100%;
background-repeat: no-repeat;
background-position: center;
@@ -222,7 +344,7 @@ main {
.target {
background-color: #DADEEF;
background-image: url("../img/tiles/Trigger.svg");
background-image: url("/web/assets/img/tiles/Trigger.svg");
background-size: 80%;
background-repeat: no-repeat;
background-position: center;
@@ -230,7 +352,7 @@ main {
.demi-wall-corner-up-left {
background-color: #DADEEF;
background-image: url("../img/tiles/TopLeft.svg");
background-image: url("/web/assets/img/tiles/TopLeft.svg");
background-size: 100%;
background-repeat: no-repeat;
background-position: center;
@@ -238,7 +360,7 @@ main {
.demi-wall-corner-up-right {
background-color: #DADEEF;
background-image: url("../img/tiles/TopRight.svg");
background-image: url("/web/assets/img/tiles/TopRight.svg");
background-size: 100%;
background-repeat: no-repeat;
background-position: center;
@@ -246,7 +368,7 @@ main {
.demi-wall-corner-down-left {
background-color: #DADEEF;
background-image: url("../img/tiles/BottomLeft.svg");
background-image: url("/web/assets/img/tiles/BottomLeftAngle.svg");
background-size: 100%;
background-repeat: no-repeat;
background-position: center;
@@ -254,7 +376,7 @@ main {
.demi-wall-corner-down-right {
background-color: #DADEEF;
background-image: url("../img/tiles/BottomRight.svg");
background-image: url("/web/assets/img/tiles/BottomRightAngle.svg");
background-size: 100%;
background-repeat: no-repeat;
background-position: center;
@@ -341,6 +463,24 @@ main {
font-weight: bold;
}
.glass-item:disabled {
cursor: not-allowed;
opacity: 0.45;
}
.glass-item-label {
position: absolute;
right: 4px;
bottom: 3px;
z-index: 2;
font-size: 0.7rem;
font-weight: 700;
color: #223;
background: rgba(255, 255, 255, 0.88);
padding: 1px 5px;
border-radius: 999px;
}
.glass-item::after,
.cell-glass::after {
content: "";

View File

@@ -1,17 +1,19 @@
<svg width="100" height="100" viewBox="0 0 100 100" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M1 99L1 2.41406L97.5859 99L1 99Z" fill="url(#paint0_linear_17_89)" stroke="url(#paint1_linear_17_89)" stroke-width="2"/>
<path d="M1 99L1 1.61816L99 50.6182V99L1 99Z" fill="url(#paint0_linear_35_19)" stroke="url(#paint1_linear_35_19)" stroke-width="2"/>
<circle cx="5.5" cy="94.5" r="1.5" transform="rotate(-180 5.5 94.5)" fill="#4A4A4A"/>
<circle cx="5.5" cy="94.5" r="1" transform="rotate(-180 5.5 94.5)" fill="#898989"/>
<circle cx="5.5" cy="13.5" r="1.5" transform="rotate(-180 5.5 13.5)" fill="#4A4A4A"/>
<circle cx="5.5" cy="13.5" r="1" transform="rotate(-180 5.5 13.5)" fill="#898989"/>
<circle cx="86.5" cy="94.5" r="1.5" transform="rotate(-180 86.5 94.5)" fill="#4A4A4A"/>
<circle cx="86.5" cy="94.5" r="1" transform="rotate(-180 86.5 94.5)" fill="#898989"/>
<circle cx="4.5" cy="8.5" r="1.5" transform="rotate(-180 4.5 8.5)" fill="#4A4A4A"/>
<circle cx="4.5" cy="8.5" r="1" transform="rotate(-180 4.5 8.5)" fill="#898989"/>
<circle cx="94.5" cy="94.5" r="1.5" transform="rotate(-180 94.5 94.5)" fill="#4A4A4A"/>
<circle cx="94.5" cy="94.5" r="1" transform="rotate(-180 94.5 94.5)" fill="#898989"/>
<circle cx="94.5" cy="53.5" r="1.5" transform="rotate(-180 94.5 53.5)" fill="#4A4A4A"/>
<circle cx="94.5" cy="53.5" r="1" transform="rotate(-180 94.5 53.5)" fill="#898989"/>
<defs>
<linearGradient id="paint0_linear_17_89" x1="0" y1="0" x2="100" y2="100" gradientUnits="userSpaceOnUse">
<linearGradient id="paint0_linear_35_19" x1="0" y1="0" x2="40" y2="80" gradientUnits="userSpaceOnUse">
<stop stop-color="white"/>
<stop offset="1" stop-color="#747474"/>
</linearGradient>
<linearGradient id="paint1_linear_17_89" x1="100" y1="100" x2="0" y2="0" gradientUnits="userSpaceOnUse">
<linearGradient id="paint1_linear_35_19" x1="100" y1="50" x2="60" y2="-30" gradientUnits="userSpaceOnUse">
<stop stop-color="#A8A8A8"/>
<stop offset="1" stop-color="#848484"/>
</linearGradient>

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@@ -0,0 +1,19 @@
<svg width="100" height="100" viewBox="0 0 100 100" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M1 99L1 2.41406L97.5859 99L1 99Z" fill="url(#paint0_linear_17_89)" stroke="url(#paint1_linear_17_89)" stroke-width="2"/>
<circle cx="5.5" cy="94.5" r="1.5" transform="rotate(-180 5.5 94.5)" fill="#4A4A4A"/>
<circle cx="5.5" cy="94.5" r="1" transform="rotate(-180 5.5 94.5)" fill="#898989"/>
<circle cx="5.5" cy="13.5" r="1.5" transform="rotate(-180 5.5 13.5)" fill="#4A4A4A"/>
<circle cx="5.5" cy="13.5" r="1" transform="rotate(-180 5.5 13.5)" fill="#898989"/>
<circle cx="86.5" cy="94.5" r="1.5" transform="rotate(-180 86.5 94.5)" fill="#4A4A4A"/>
<circle cx="86.5" cy="94.5" r="1" transform="rotate(-180 86.5 94.5)" fill="#898989"/>
<defs>
<linearGradient id="paint0_linear_17_89" x1="0" y1="0" x2="100" y2="100" gradientUnits="userSpaceOnUse">
<stop stop-color="white"/>
<stop offset="1" stop-color="#747474"/>
</linearGradient>
<linearGradient id="paint1_linear_17_89" x1="100" y1="100" x2="0" y2="0" gradientUnits="userSpaceOnUse">
<stop stop-color="#A8A8A8"/>
<stop offset="1" stop-color="#848484"/>
</linearGradient>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@@ -1,17 +1,19 @@
<svg width="100" height="100" viewBox="0 0 100 100" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M99 99H2.41406L99 2.41406V99Z" fill="url(#paint0_linear_19_190)" stroke="url(#paint1_linear_19_190)" stroke-width="2"/>
<path d="M99 99H1V50.6182L99 1.61816V99Z" fill="url(#paint0_linear_35_30)" stroke="url(#paint1_linear_35_30)" stroke-width="2"/>
<circle cx="94.5" cy="94.5" r="1.5" transform="rotate(90 94.5 94.5)" fill="#4A4A4A"/>
<circle cx="94.5" cy="94.5" r="1" transform="rotate(90 94.5 94.5)" fill="#898989"/>
<circle cx="13.5" cy="94.5" r="1.5" transform="rotate(90 13.5 94.5)" fill="#4A4A4A"/>
<circle cx="13.5" cy="94.5" r="1" transform="rotate(90 13.5 94.5)" fill="#898989"/>
<circle cx="94.5" cy="13.5" r="1.5" transform="rotate(90 94.5 13.5)" fill="#4A4A4A"/>
<circle cx="94.5" cy="13.5" r="1" transform="rotate(90 94.5 13.5)" fill="#898989"/>
<circle cx="5.5" cy="94.5" r="1.5" transform="rotate(90 5.5 94.5)" fill="#4A4A4A"/>
<circle cx="5.5" cy="94.5" r="1" transform="rotate(90 5.5 94.5)" fill="#898989"/>
<circle cx="5.5" cy="53.5" r="1.5" transform="rotate(90 5.5 53.5)" fill="#4A4A4A"/>
<circle cx="5.5" cy="53.5" r="1" transform="rotate(90 5.5 53.5)" fill="#898989"/>
<circle cx="94.5" cy="9.5" r="1.5" transform="rotate(90 94.5 9.5)" fill="#4A4A4A"/>
<circle cx="94.5" cy="9.5" r="1" transform="rotate(90 94.5 9.5)" fill="#898989"/>
<defs>
<linearGradient id="paint0_linear_19_190" x1="0" y1="0" x2="100" y2="100" gradientUnits="userSpaceOnUse">
<linearGradient id="paint0_linear_35_30" x1="8.9049e-06" y1="1.50801e-06" x2="40" y2="80" gradientUnits="userSpaceOnUse">
<stop stop-color="white"/>
<stop offset="1" stop-color="#747474"/>
</linearGradient>
<linearGradient id="paint1_linear_19_190" x1="100" y1="100" x2="0" y2="0" gradientUnits="userSpaceOnUse">
<linearGradient id="paint1_linear_35_30" x1="100" y1="50" x2="60" y2="-30" gradientUnits="userSpaceOnUse">
<stop stop-color="#A8A8A8"/>
<stop offset="1" stop-color="#848484"/>
</linearGradient>

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@@ -0,0 +1,19 @@
<svg width="100" height="100" viewBox="0 0 100 100" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M99 99H2.41406L99 2.41406V99Z" fill="url(#paint0_linear_19_190)" stroke="url(#paint1_linear_19_190)" stroke-width="2"/>
<circle cx="94.5" cy="94.5" r="1.5" transform="rotate(90 94.5 94.5)" fill="#4A4A4A"/>
<circle cx="94.5" cy="94.5" r="1" transform="rotate(90 94.5 94.5)" fill="#898989"/>
<circle cx="13.5" cy="94.5" r="1.5" transform="rotate(90 13.5 94.5)" fill="#4A4A4A"/>
<circle cx="13.5" cy="94.5" r="1" transform="rotate(90 13.5 94.5)" fill="#898989"/>
<circle cx="94.5" cy="13.5" r="1.5" transform="rotate(90 94.5 13.5)" fill="#4A4A4A"/>
<circle cx="94.5" cy="13.5" r="1" transform="rotate(90 94.5 13.5)" fill="#898989"/>
<defs>
<linearGradient id="paint0_linear_19_190" x1="0" y1="0" x2="100" y2="100" gradientUnits="userSpaceOnUse">
<stop stop-color="white"/>
<stop offset="1" stop-color="#747474"/>
</linearGradient>
<linearGradient id="paint1_linear_19_190" x1="100" y1="100" x2="0" y2="0" gradientUnits="userSpaceOnUse">
<stop stop-color="#A8A8A8"/>
<stop offset="1" stop-color="#848484"/>
</linearGradient>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@@ -20,6 +20,14 @@ const legend = {
cable: 16,
captorTurn: 17,
cableVertical: 18,
captorTurnReturn: 19,
rotatorButton: 20,
cableTurn: 21,
horizontalSemi: 22,
cableTurnHorizontale : 23,
cableTurnHorizontale2 : 24,
captorTurnHorizontal : 25,
wallSemiAngle: 26,
};
const laserColors = {
@@ -30,34 +38,64 @@ const laserColors = {
};
const glassOptions = [
laserColors.red,
laserColors.blue,
laserColors.yellow,
[
{ color: laserColors.red, maxAmount: 1, currentAmount: 1 },
{ color: laserColors.blue, maxAmount: 1, currentAmount: 1 },
{ color: laserColors.yellow, maxAmount: 1, currentAmount: 1 },
],
[
{ color: laserColors.red, maxAmount: 1, currentAmount: 1 },
{ color: laserColors.blue, maxAmount: 1, currentAmount: 1 },
{ color: laserColors.yellow, maxAmount: 1, currentAmount: 1 },
],
[
{ color: laserColors.red, maxAmount: 2, currentAmount: 2 },
{ color: laserColors.blue, maxAmount: 2, currentAmount:2},
{ color: laserColors.yellow, maxAmount: 2, currentAmount: 2 },
],
[
{ color: laserColors.red, maxAmount: 2, currentAmount: 2 },
{ color: laserColors.blue, maxAmount: 1, currentAmount: 1 },
{ color: laserColors.yellow, maxAmount: 2, currentAmount: 2 },
],
];
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],
[6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6],
[6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6],
[6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6],
[6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6],
[6, 6, 6, 6, 1, 0, 5, 4, 0, 10, 6, 6, 6, 6, 6],
[6, 6, 6, 6, 7, 6, 6, 6, 6, 0, 6, 6, 6, 6, 6],
[6, 6, 6, 6, 3, 0, 0, 0, 0, 12, 6, 6, 6, 6, 6],
[6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6],
[6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6],
[6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6],
],
[
[6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6],
[6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6],
[6, 6, 1, 0, 0, 0, 17, 0, 0, 3, 6, 6, 6, 6, 6],
[6, 6, 6, 6, 6, 6, 18, 6, 6, 0, 6, 6, 6, 6, 6],
[6, 6, 7, 0, 0, 0, 4, 0, 0, 12, 6, 6, 6, 6, 6],
[6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6],
[6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6],
[6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6],
[6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6],
[6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6],
],
[
[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, 6, 6, 6, 6, 6, 0, 10, 6, 6, 11, 0, 0, 0, 0],
[7, 1, 0, 0, 0, 4, 0, 0, 0, 3, 21, 0, 0, 0, 0],
[0, 6, 6, 6, 6, 18, 6, 26, 22, 0, 18, 0, 0, 0, 0],
[0, 6, 9, 0, 0, 19, 0, 0, 0, 12, 18, 0, 0, 0, 0],
[0, 6, 0, 6, 6, 6, 6, 6, 6, 24, 23, 0, 0, 0, 0],
[0, 6, 11, 0, 0, 0, 0, 0, 20, 23, 9, 0, 0, 0, 0],
[0, 10, 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],
],
@@ -87,7 +125,7 @@ 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],
],
], */
];
let currentLevelIndex = 0;
@@ -98,9 +136,9 @@ const initialMirrorAngles = [
},
{},
{
"3,4": 315,
"7,8": 0,
"2,9": 225,
},
{}
];
const buttonGroups = [
@@ -108,7 +146,9 @@ const buttonGroups = [
"4,6": 1,
},
{},
{},
{
"9,4": 1,
},
];
const doorGroups = [
@@ -132,6 +172,9 @@ const captorGroups = [
const rotatorButtons = [
{},
{},
{
"6,8": { mirrorX: 9, mirrorY: 2, step: 22.5, intervalMs: 1000 },
},
{
"3,7": { mirrorX: 7, mirrorY: 7, step: -22.5, intervalMs: 1000 },
},
@@ -148,6 +191,9 @@ let activeRotatorButtons = {};
let rotatorIntervals = {};
let toggledDoors = {};
let poweredCaptors = {};
let draggedGlassColor = null;
let highestUnlockedLevelIndex = 0;
const unlockedLevelsStorageKey = "mirror-game-highest-unlocked-level";
function getCurrentLevel() {
return levels[currentLevelIndex];
@@ -157,6 +203,131 @@ function getCurrentLevelConfig(configByLevel) {
return configByLevel[currentLevelIndex] || {};
}
function loadUnlockedLevels() {
const savedValue = window.localStorage.getItem(unlockedLevelsStorageKey);
const parsedValue = Number.parseInt(savedValue || "0", 10);
if (Number.isNaN(parsedValue)) {
highestUnlockedLevelIndex = 0;
return;
}
highestUnlockedLevelIndex = Math.max(0, Math.min(parsedValue, levels.length - 1));
}
function saveUnlockedLevels() {
window.localStorage.setItem(unlockedLevelsStorageKey, String(highestUnlockedLevelIndex));
}
function unlockLevel(levelIndex) {
if (levelIndex <= highestUnlockedLevelIndex || levelIndex >= levels.length) {
return;
}
highestUnlockedLevelIndex = levelIndex;
saveUnlockedLevels();
renderLevelMenu();
}
function toggleLevelMenu() {
const levelMenuPanel = document.getElementById("level-menu-panel");
const levelMenuToggle = document.getElementById("level-menu-toggle");
if (!levelMenuPanel || !levelMenuToggle) {
return;
}
const isOpening = levelMenuPanel.hasAttribute("hidden");
if (isOpening) {
levelMenuPanel.removeAttribute("hidden");
} else {
levelMenuPanel.setAttribute("hidden", "");
}
levelMenuToggle.setAttribute("aria-expanded", String(isOpening));
}
function renderLevelMenu() {
const levelMenuList = document.getElementById("level-menu-list");
if (!levelMenuList) {
return;
}
levelMenuList.innerHTML = "";
for (let levelIndex = 0; levelIndex < levels.length; levelIndex++) {
const levelButton = document.createElement("button");
const isUnlocked = levelIndex <= highestUnlockedLevelIndex;
const isCurrentLevel = levelIndex === currentLevelIndex;
levelButton.type = "button";
levelButton.classList.add("level-menu-item");
levelButton.textContent = isUnlocked ? `Level ${levelIndex + 1}` : `Level ${levelIndex + 1} - Locked`;
levelButton.disabled = !isUnlocked;
if (isCurrentLevel) {
levelButton.classList.add("is-current");
}
levelButton.addEventListener("click", () => {
if (!isUnlocked) {
return;
}
goToLevel(levelIndex);
const levelMenuPanel = document.getElementById("level-menu-panel");
const levelMenuToggle = document.getElementById("level-menu-toggle");
if (levelMenuPanel) {
levelMenuPanel.setAttribute("hidden", "");
}
if (levelMenuToggle) {
levelMenuToggle.setAttribute("aria-expanded", "false");
}
});
levelMenuList.appendChild(levelButton);
}
}
function setupLevelMenu() {
const levelMenuToggle = document.getElementById("level-menu-toggle");
if (!levelMenuToggle) {
return;
}
levelMenuToggle.addEventListener("click", toggleLevelMenu);
renderLevelMenu();
}
function getCurrentGlassOptions() {
return glassOptions[currentLevelIndex] || [];
}
function getGlassOptionByColor(color) {
const currentGlassOptions = getCurrentGlassOptions();
for (let i = 0; i < currentGlassOptions.length; i++) {
if (currentGlassOptions[i].color === color) {
return currentGlassOptions[i];
}
}
return null;
}
function resetGlassInventory() {
const currentGlassOptions = getCurrentGlassOptions();
for (let i = 0; i < currentGlassOptions.length; i++) {
currentGlassOptions[i].currentAmount = currentGlassOptions[i].maxAmount;
}
}
function normalizeLaserDirection(dx, dy) {
const epsilon = 0.0001;
const normalizedDx = Math.abs(dx) < epsilon ? 0 : Math.sign(dx);
@@ -366,17 +537,40 @@ function createPalette() {
palette.innerHTML = "";
for (let i = 0; i < glassOptions.length; i++) {
const glassColor = glassOptions[i];
const currentGlassOptions = getCurrentGlassOptions();
for (let i = 0; i < currentGlassOptions.length; i++) {
const glassOption = currentGlassOptions[i];
const glassColor = glassOption.color;
const glassButton = document.createElement("button");
glassButton.type = "button";
glassButton.classList.add("glass-item", `glass-${glassColor}`);
glassButton.draggable = true;
glassButton.draggable = glassOption.currentAmount > 0;
if (glassOption.currentAmount <= 0) {
glassButton.disabled = true;
}
const glassLabel = document.createElement("span");
glassLabel.classList.add("glass-item-label");
glassLabel.textContent = `${glassOption.currentAmount}/${glassOption.maxAmount}`;
glassButton.appendChild(glassLabel);
glassButton.addEventListener("dragstart", (event) => {
if (glassOption.currentAmount <= 0) {
event.preventDefault();
return;
}
draggedGlassColor = glassColor;
event.dataTransfer.effectAllowed = "copy";
event.dataTransfer.setData("application/x-glass-color", glassColor);
});
glassButton.addEventListener("dragend", () => {
draggedGlassColor = null;
});
palette.appendChild(glassButton);
}
}
@@ -387,7 +581,8 @@ function addDropEvents(cell, x, y) {
return;
}
if (!event.dataTransfer.types.includes("application/x-glass-color")) {
const transferTypes = event.dataTransfer ? Array.from(event.dataTransfer.types || []) : [];
if (!draggedGlassColor) {
return;
}
@@ -396,6 +591,13 @@ function addDropEvents(cell, x, y) {
return;
}
const selectedColor = draggedGlassColor;
const glassOption = getGlassOptionByColor(selectedColor);
if (!glassOption || glassOption.currentAmount <= 0) {
return;
}
event.preventDefault();
cell.classList.add("can-drop");
});
@@ -409,7 +611,8 @@ function addDropEvents(cell, x, y) {
return;
}
const selectedColor = event.dataTransfer.getData("application/x-glass-color");
const transferredColor = event.dataTransfer ? event.dataTransfer.getData("application/x-glass-color") : "";
const selectedColor = transferredColor || draggedGlassColor;
cell.classList.remove("can-drop");
if (!selectedColor) {
@@ -420,8 +623,21 @@ function addDropEvents(cell, x, y) {
return;
}
if (isGlassOnCell(x, y)) {
return;
}
const glassOption = getGlassOptionByColor(selectedColor);
if (!glassOption || glassOption.currentAmount <= 0) {
return;
}
event.preventDefault();
glassPlacements[`${y},${x}`] = selectedColor;
glassOption.currentAmount--;
draggedGlassColor = null;
createPalette();
traceLaser();
});
@@ -431,7 +647,16 @@ function addDropEvents(cell, x, y) {
}
if (isGlassOnCell(x, y)) {
const glassColor = glassPlacements[`${y},${x}`];
const glassOption = getGlassOptionByColor(glassColor);
delete glassPlacements[`${y},${x}`];
if (glassOption) {
glassOption.currentAmount = Math.min(glassOption.currentAmount + 1, glassOption.maxAmount);
}
createPalette();
traceLaser();
}
});
@@ -488,7 +713,7 @@ function loadGrid() {
cell.classList.add("mirror");
const currentAngle = mirrorOrientations[`${y},${x}`] || 0;
const img = document.createElement("img");
img.src = "../../assets/img/tiles/Mirror.svg";
img.src = "/web/assets/img/tiles/Mirror.svg";
img.classList.add("mirror-img");
img.style.transform = `rotate(${currentAngle}deg)`;
@@ -570,6 +795,27 @@ function loadGrid() {
case legend.cableVertical:
cell.classList.add("cable-vertical");
break;
case legend.captorTurnReturn:
cell.classList.add("captor-turn-reverse");
break;
case legend.cableTurn:
cell.classList.add("cable-turn");
break;
case legend.horizontalSemi:
cell.classList.add("horizontal-semi");
break;
case legend.cableTurnHorizontale:
cell.classList.add("cable-turn-horizontale");
break;
case legend.cableTurnHorizontale2:
cell.classList.add("cable-turn-horizontale2");
break;
case legend.captorTurnHorizontal:
cell.classList.add("captor-turn-horizontale2");
break;
case legend.wallSemiAngle:
cell.classList.add("wall-semi-angle");
break;
}
drawLaserInCell(cell, laserSegments[`${y},${x}`]);
@@ -641,10 +887,21 @@ function traceLaser() {
currentLaserColor = glassColor;
}
if (currentLaserColor === laserColors.yellow) {
saveLaserSegment(currentX, currentY, laserDirection, currentLaserColor);
if (cellType === legend.target) {
laserActive = false;
isLevelFinished = true;
}
continue;
}
switch (cellType) {
case legend.laser:
case legend.coloredLaser:
laserActive = false;
saveLaserSegment(currentX, currentY, laserDirection, currentLaserColor);
break;
case legend.empty:
@@ -675,7 +932,27 @@ function traceLaser() {
laserActive = false;
}
break;
case legend.wallSemiAngle:
if (currentLaserColor === laserColors.blue) {
laserDirection = reverseLaser(laserDirection);
} else if (currentLaserColor === laserColors.yellow) {
saveLaserSegment(currentX, currentY, laserDirection, currentLaserColor);
} else {
laserActive = false;
}
break;
case legend.horizontalSemi:
if (currentLaserColor === laserColors.blue) {
saveLaserSegment(currentX, currentY, laserDirection, currentLaserColor);
laserDirection = reflectLaser(laserDirection, 0);
} else if (currentLaserColor === laserColors.yellow) {
saveLaserSegment(currentX, currentY, laserDirection, currentLaserColor);
} else {
laserActive = false;
}
break;
case legend.cableVertical:
case legend.cableTurn:
case legend.door:
case legend.doorOpen:
if (openedDoors[`${currentY},${currentX}`] || cellType === legend.doorOpen) {
@@ -704,6 +981,14 @@ function traceLaser() {
break;
case legend.captor:
if (currentLaserColor === laserColors.blue) {
laserDirection = reverseLaser(laserDirection);
} else if (currentLaserColor === laserColors.yellow) {
saveLaserSegment(currentX, currentY, laserDirection, currentLaserColor);
} else {
laserActive = false;
}
break;
case legend.captorTurn:
if (currentLaserColor === laserColors.red) {
const captorKey = `${currentY},${currentX}`;
@@ -723,7 +1008,44 @@ function traceLaser() {
laserActive = false;
}
break;
case legend.captorTurnReturn:
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.captorTurnHorizontal:
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}`;
@@ -742,25 +1064,37 @@ function traceLaser() {
break;
case legend.demiWallCornerUpLeft:
if(currentLaserColor === laserColors.blue) {
if (currentLaserColor === laserColors.yellow) {
saveLaserSegment(currentX, currentY, laserDirection, currentLaserColor);
} else if (currentLaserColor === laserColors.blue) {
saveLaserSegment(currentX, currentY, laserDirection, currentLaserColor);
laserDirection = reflectLaser(laserDirection, 135);
}
break;
case legend.demiWallCornerUpRight:
if(currentLaserColor === laserColors.blue) {
if (currentLaserColor === laserColors.yellow) {
saveLaserSegment(currentX, currentY, laserDirection, currentLaserColor);
} else if (currentLaserColor === laserColors.blue) {
saveLaserSegment(currentX, currentY, laserDirection, currentLaserColor);
laserDirection = reflectLaser(laserDirection, 45);
}
break;
case legend.demiWallCornerDownLeft:
if(currentLaserColor === laserColors.blue) {
if (currentLaserColor === laserColors.yellow) {
saveLaserSegment(currentX, currentY, laserDirection, currentLaserColor);
} else if (currentLaserColor === laserColors.blue) {
saveLaserSegment(currentX, currentY, laserDirection, currentLaserColor);
laserDirection = reflectLaser(laserDirection, 225);
}
break;
case legend.demiWallCornerDownRight:
if(currentLaserColor === laserColors.blue) {
if (currentLaserColor === laserColors.yellow) {
saveLaserSegment(currentX, currentY, laserDirection, currentLaserColor);
} else if (currentLaserColor === laserColors.blue) {
saveLaserSegment(currentX, currentY, laserDirection, currentLaserColor);
laserDirection = reflectLaser(laserDirection, 315);
}
break;
@@ -781,59 +1115,46 @@ function traceLaser() {
}
function finish() {
unlockLevel(currentLevelIndex + 1);
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++;
function goToLevel(levelIndex) {
currentLevelIndex = levelIndex;
isLevelFinished = false;
stopAllRotatorButtons();
if (currentLevelIndex >= levels.length) {
currentLevelIndex = 0;
}
initializeMirrorOrientations();
loadGrid();
laserSegments = {};
mirrorOrientations = {};
glassPlacements = {};
laserSegments = {};
activatedButtons = {};
openedDoors = {};
toggledDoors = {};
poweredCaptors = {};
initializeMirrorOrientations();
glassPlacements = {};
resetGlassInventory();
createPalette();
loadGrid();
traceLaser();
const winOverlay = document.querySelector(".win-overlay");
winOverlay.style.visibility = "hidden";
renderLevelMenu();
}
function nextLevel() {
const nextLevelIndex = currentLevelIndex + 1 >= levels.length ? 0 : currentLevelIndex + 1;
goToLevel(nextLevelIndex);
}
loadUnlockedLevels();
resetGlassInventory();
createPalette();
initializeMirrorOrientations();
blockBrowserDrop();
setupLevelMenu();
traceLaser();

View File

@@ -1,9 +0,0 @@
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)`;
}

View File

@@ -3,7 +3,7 @@
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="../../assets/css/game.css">
<link rel="stylesheet" href="/web/assets/web/assets/assets/css/game.css">
<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>
@@ -15,6 +15,16 @@
<button class="win-button" onclick="nextLevel()">Next Level</button>
</div>
<aside class="level-menu-shell">
<button id="level-menu-toggle" class="level-menu-toggle" type="button" aria-expanded="false" aria-controls="level-menu-panel">
Levels
</button>
<div id="level-menu-panel" class="level-menu-panel" hidden>
<h2>Choose Level</h2>
<div id="level-menu-list" class="level-menu-list"></div>
</div>
</aside>
<script>
atOptions = {
'key' : '72b6ba1a1c26b9671167b66063c7e699',
@@ -46,6 +56,6 @@
</script>
<script src="https://www.highperformanceformat.com/72b6ba1a1c26b9671167b66063c7e699/invoke.js"></script>
<script src="../../assets/js/game.js" defer></script>
<script src="/web/assets//eb/assets/assets/js/game.js" defer></script>
</body>
</html>