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.
This commit is contained in:
@@ -341,6 +341,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: "";
|
||||
|
||||
@@ -31,9 +31,21 @@ 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 = [
|
||||
@@ -149,6 +161,7 @@ let activeRotatorButtons = {};
|
||||
let rotatorIntervals = {};
|
||||
let toggledDoors = {};
|
||||
let poweredCaptors = {};
|
||||
let draggedGlassColor = null;
|
||||
|
||||
function getCurrentLevel() {
|
||||
return levels[currentLevelIndex];
|
||||
@@ -158,6 +171,30 @@ function getCurrentLevelConfig(configByLevel) {
|
||||
return configByLevel[currentLevelIndex] || {};
|
||||
}
|
||||
|
||||
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);
|
||||
@@ -367,17 +404,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);
|
||||
}
|
||||
}
|
||||
@@ -388,7 +448,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;
|
||||
}
|
||||
|
||||
@@ -397,6 +458,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");
|
||||
});
|
||||
@@ -410,7 +478,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) {
|
||||
@@ -421,8 +490,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();
|
||||
});
|
||||
|
||||
@@ -432,7 +514,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();
|
||||
}
|
||||
});
|
||||
@@ -642,6 +733,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:
|
||||
@@ -743,25 +845,33 @@ function traceLaser() {
|
||||
break;
|
||||
|
||||
case legend.demiWallCornerUpLeft:
|
||||
if(currentLaserColor === laserColors.blue) {
|
||||
if (currentLaserColor === laserColors.yellow) {
|
||||
saveLaserSegment(currentX, currentY, laserDirection, currentLaserColor);
|
||||
} else if (currentLaserColor === laserColors.blue) {
|
||||
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) {
|
||||
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) {
|
||||
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) {
|
||||
laserDirection = reflectLaser(laserDirection, 315);
|
||||
}
|
||||
break;
|
||||
@@ -799,10 +909,12 @@ function nextLevel() {
|
||||
}
|
||||
|
||||
initializeMirrorOrientations();
|
||||
glassPlacements = {};
|
||||
resetGlassInventory();
|
||||
createPalette();
|
||||
loadGrid();
|
||||
laserSegments = {};
|
||||
mirrorOrientations = {};
|
||||
glassPlacements = {};
|
||||
activatedButtons = {};
|
||||
openedDoors = {};
|
||||
toggledDoors = {};
|
||||
@@ -813,6 +925,7 @@ function nextLevel() {
|
||||
winOverlay.style.visibility = "hidden";
|
||||
}
|
||||
|
||||
resetGlassInventory();
|
||||
createPalette();
|
||||
initializeMirrorOrientations();
|
||||
blockBrowserDrop();
|
||||
|
||||
Reference in New Issue
Block a user