add lvl 4
This commit is contained in:
@@ -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;
|
||||
@@ -265,6 +330,14 @@ main {
|
||||
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;
|
||||
}
|
||||
@@ -365,6 +438,10 @@ main {
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.btn-mirror-locked {
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.mirror-img {
|
||||
width: 80%;
|
||||
height: 80%;
|
||||
@@ -386,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: "";
|
||||
|
||||
@@ -21,6 +21,7 @@ const legend = {
|
||||
captorTurn: 17,
|
||||
cableVertical: 18,
|
||||
captorTurnReturn: 19,
|
||||
rotatorButton: 20,
|
||||
cableTurn: 21,
|
||||
horizontalSemi: 22,
|
||||
cableTurnHorizontale : 23,
|
||||
@@ -37,36 +38,48 @@ 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: 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],
|
||||
],
|
||||
|
||||
[
|
||||
[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],
|
||||
[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],
|
||||
],
|
||||
|
||||
[
|
||||
@@ -86,11 +99,11 @@ let levels = [
|
||||
[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, 0, 3, 16, 16, 15, 0, 3, 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, 0, 3, 0, 7, 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],
|
||||
],
|
||||
@@ -112,20 +125,50 @@ let levels = [
|
||||
|
||||
let currentLevelIndex = 0;
|
||||
|
||||
const initialMirrorAngles = {
|
||||
"6,4": 315,
|
||||
"4,3": 315,
|
||||
};
|
||||
const initialMirrorAngles = [
|
||||
{
|
||||
"6,4": 315,
|
||||
},
|
||||
{},
|
||||
{
|
||||
"3,4": 315,
|
||||
"7,8": 0,
|
||||
},
|
||||
];
|
||||
|
||||
const buttonGroups = {
|
||||
"4,6": 1,
|
||||
"4,7": 2,
|
||||
};
|
||||
const buttonGroups = [
|
||||
{
|
||||
"4,6": 1,
|
||||
},
|
||||
{},
|
||||
{},
|
||||
];
|
||||
|
||||
const doorGroups = {
|
||||
"4,7": 1,
|
||||
"4,8": 2,
|
||||
};
|
||||
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 = {};
|
||||
@@ -134,11 +177,147 @@ let glassPlacements = {};
|
||||
let activatedButtons = {};
|
||||
let openedDoors = {};
|
||||
let isLevelFinished = false;
|
||||
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];
|
||||
}
|
||||
|
||||
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);
|
||||
@@ -196,11 +375,15 @@ function getButtonGroup(x, y) {
|
||||
return 2;
|
||||
}
|
||||
|
||||
return buttonGroups[`${y},${x}`] || 1;
|
||||
return getCurrentLevelConfig(buttonGroups)[`${y},${x}`] || 1;
|
||||
}
|
||||
|
||||
function getDoorGroup(x, y) {
|
||||
return doorGroups[`${y},${x}`] || 1;
|
||||
return getCurrentLevelConfig(doorGroups)[`${y},${x}`] || 1;
|
||||
}
|
||||
|
||||
function getCaptorGroup(x, y) {
|
||||
return getCurrentLevelConfig(captorGroups)[`${y},${x}`] || getDoorGroup(x, y);
|
||||
}
|
||||
|
||||
function openDoorsFromButton(x, y) {
|
||||
@@ -216,6 +399,90 @@ function openDoorsFromButton(x, y) {
|
||||
}
|
||||
}
|
||||
|
||||
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 },
|
||||
@@ -260,17 +527,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);
|
||||
}
|
||||
}
|
||||
@@ -281,7 +571,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;
|
||||
}
|
||||
|
||||
@@ -290,6 +581,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");
|
||||
});
|
||||
@@ -303,7 +601,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) {
|
||||
@@ -314,8 +613,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();
|
||||
});
|
||||
|
||||
@@ -325,7 +637,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();
|
||||
}
|
||||
});
|
||||
@@ -348,7 +669,7 @@ function initializeMirrorOrientations() {
|
||||
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}`] = initialMirrorAngles[`${y},${x}`] || 0;
|
||||
mirrorOrientations[`${y},${x}`] = getCurrentLevelConfig(initialMirrorAngles)[`${y},${x}`] || 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -381,23 +702,31 @@ function loadGrid() {
|
||||
case legend.mirror:
|
||||
cell.classList.add("mirror");
|
||||
const currentAngle = mirrorOrientations[`${y},${x}`] || 0;
|
||||
const btnMirror = document.createElement("button");
|
||||
btnMirror.classList.add("btn-mirror");
|
||||
btnMirror.type = "button";
|
||||
const img = document.createElement("img");
|
||||
img.src = "../../assets/img/tiles/Mirror.svg";
|
||||
img.classList.add("mirror-img");
|
||||
img.style.transform = `rotate(${currentAngle}deg)`;
|
||||
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);
|
||||
|
||||
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");
|
||||
@@ -420,6 +749,12 @@ function loadGrid() {
|
||||
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;
|
||||
@@ -483,28 +818,17 @@ function loadGrid() {
|
||||
}
|
||||
|
||||
function rotateMirror(x, y, isRightClick) {
|
||||
const coordKey = `${y},${x}`;
|
||||
|
||||
if (getCurrentLevel()[y][x] !== legend.mirror) {
|
||||
return;
|
||||
}
|
||||
|
||||
let currentAngle = mirrorOrientations[coordKey] || 0;
|
||||
currentAngle = (currentAngle + (isRightClick ? 22.5 : -22.5)) % 360;
|
||||
|
||||
if (currentAngle < 0) {
|
||||
currentAngle += 360;
|
||||
}
|
||||
|
||||
mirrorOrientations[coordKey] = currentAngle;
|
||||
rotateMirrorStep(x, y, isRightClick ? 22.5 : -22.5);
|
||||
traceLaser();
|
||||
}
|
||||
|
||||
function traceLaser() {
|
||||
laserSegments = {};
|
||||
activatedButtons = {};
|
||||
openedDoors = {};
|
||||
openedDoors = { ...toggledDoors };
|
||||
activeRotatorButtons = {};
|
||||
isLevelFinished = false;
|
||||
const nextPoweredCaptors = {};
|
||||
|
||||
const level = getCurrentLevel();
|
||||
let startLaserX;
|
||||
@@ -553,6 +877,17 @@ 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:
|
||||
@@ -600,7 +935,6 @@ function traceLaser() {
|
||||
laserActive = false;
|
||||
}
|
||||
break;
|
||||
|
||||
case legend.button:
|
||||
case legend.button2:
|
||||
if (currentLaserColor === laserColors.red) {
|
||||
@@ -616,20 +950,111 @@ function traceLaser() {
|
||||
}
|
||||
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.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}`;
|
||||
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:
|
||||
laserDirection = reflectLaser(laserDirection, 135);
|
||||
if (currentLaserColor === laserColors.yellow) {
|
||||
saveLaserSegment(currentX, currentY, laserDirection, currentLaserColor);
|
||||
} else if (currentLaserColor === laserColors.blue) {
|
||||
laserDirection = reflectLaser(laserDirection, 135);
|
||||
}
|
||||
break;
|
||||
|
||||
case legend.demiWallCornerUpRight:
|
||||
laserDirection = reflectLaser(laserDirection, 45);
|
||||
if (currentLaserColor === laserColors.yellow) {
|
||||
saveLaserSegment(currentX, currentY, laserDirection, currentLaserColor);
|
||||
} else if (currentLaserColor === laserColors.blue) {
|
||||
laserDirection = reflectLaser(laserDirection, 45);
|
||||
}
|
||||
break;
|
||||
|
||||
case legend.demiWallCornerDownLeft:
|
||||
laserDirection = reflectLaser(laserDirection, 225);
|
||||
if (currentLaserColor === laserColors.yellow) {
|
||||
saveLaserSegment(currentX, currentY, laserDirection, currentLaserColor);
|
||||
} else if (currentLaserColor === laserColors.blue) {
|
||||
laserDirection = reflectLaser(laserDirection, 225);
|
||||
}
|
||||
break;
|
||||
|
||||
case legend.demiWallCornerDownRight:
|
||||
laserDirection = reflectLaser(laserDirection, 315);
|
||||
if (currentLaserColor === laserColors.yellow) {
|
||||
saveLaserSegment(currentX, currentY, laserDirection, currentLaserColor);
|
||||
} else if (currentLaserColor === laserColors.blue) {
|
||||
laserDirection = reflectLaser(laserDirection, 315);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
@@ -638,6 +1063,8 @@ function traceLaser() {
|
||||
}
|
||||
}
|
||||
|
||||
poweredCaptors = nextPoweredCaptors;
|
||||
syncRotatorButtons();
|
||||
loadGrid();
|
||||
|
||||
if (isLevelFinished) {
|
||||
@@ -646,35 +1073,46 @@ function traceLaser() {
|
||||
}
|
||||
|
||||
function finish() {
|
||||
unlockLevel(currentLevelIndex + 1);
|
||||
|
||||
setTimeout(() => {
|
||||
const winOverlay = document.querySelector(".win-overlay");
|
||||
winOverlay.style.visibility = "visible";
|
||||
}, 100);
|
||||
}
|
||||
|
||||
function nextLevel() {
|
||||
currentLevelIndex++;
|
||||
|
||||
function goToLevel(levelIndex) {
|
||||
currentLevelIndex = levelIndex;
|
||||
isLevelFinished = false;
|
||||
|
||||
if (currentLevelIndex >= levels.length) {
|
||||
currentLevelIndex = 0;
|
||||
}
|
||||
stopAllRotatorButtons();
|
||||
|
||||
initializeMirrorOrientations();
|
||||
glassPlacements = {};
|
||||
resetGlassInventory();
|
||||
createPalette();
|
||||
loadGrid();
|
||||
laserSegments = {};
|
||||
mirrorOrientations = {};
|
||||
glassPlacements = {};
|
||||
activatedButtons = {};
|
||||
openedDoors = {};
|
||||
toggledDoors = {};
|
||||
poweredCaptors = {};
|
||||
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();
|
||||
|
||||
Reference in New Issue
Block a user