Labyrinth Game
*, *::before, *::after {
box-sizing: border-box;
}
/* GAME SCENES */
body {
background-color: #222;
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
width: 100vw;
margin: 0;
font-family: sans-serif;
}
#HealthBar {
background-color: darkred;
height: 25px;
width: 150px;
font-size: 14px;
color: white;
position: absolute;
top: 20px;
left: 20px;
padding: 5px;
text-align: center;
border-radius: 5px;
}
canvas {
border: 40px solid;
border-image-source: url(“ASSETS/BorderWall.png”);
border-image-slice: 40;
border-image-repeat: repeat;
border-image-width: 40px;
background-color: #ddd;
}
/* GAME MENU */
#MenuButton {
position: absolute;
bottom: 50px;
left: 50px;
z-index: 10;
}
#PlayerMenu {
display: none;
grid-template-columns: 1fr 2fr;
grid-template-rows: 1fr 1fr;
position: absolute;
top: 50px;
right: 50px;
width: 300px;
background-color: #444;
color: white;
padding: 10px;
gap: 5px;
border: 2px solid white;
border-radius: 10px;
z-index: 10;
}
#PlayerDisplay {
grid-column: 1;
grid-row: 1;
background-color: #444;
padding: 10px;
border: 2px solid white;
border-radius: 10px;
}
#PlayerDisplay img {
width: 100%;
border: 2px solid;
border-radius: 10px;
}
#ArmourGrid {
grid-column: 1;
grid-row: 2;
display: grid;
grid-template-columns: repeat(2, 1fr);
padding: 5px;
gap: 5px;
border: 2px solid;
border-radius: 10px;
}
#ArmourGrid img {
width: 100%;
border: 2px solid white;
background-color: lightgrey;
border-radius: 10px;
}
#ItemGrid {
grid-column: 2;
grid-row: 1 / 3;
display: grid;
grid-template-columns: repeat(2, 1fr);
padding: 5px;
gap: 5px;
border: 2px solid;
border-radius: 10px;
}
#ItemGrid img {
width: 100%;
border: 2px solid white;
background-color: lightgrey;
border-radius: 10px;
}
/* GAME ELEMENTS */
/* CHEST POP UP */
#ChestOverlay {
position: fixed;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
background: rgba(0, 0, 0, 0.8);
display: flex;
justify-content: center;
align-items: center;
z-index: 9999;
animation: fadeIn 0.5s ease-in-out;
}
#ChestContent {
width: 50vw;
height: 50vh;
background-color: #222;
color: white;
padding: 20px;
border: 4px solid gold;
border-radius: 15px;
text-align: center;
animation: popUp 0.4s ease-out;
}
#ChestContent img {
width: 100px;
margin-bottom: 10px;
}
@keyframes popUp {
0% {
transform: scale(0.2);
opacity: 0;
}
100% {
transform: scale(1);
opacity: 1;
}
}
@keyframes fadeIn {
from {opacity: 0;}
to {opacity: 1;}
}
/* start test movement from mobile */
.MobileButton {
display: grid;
grid-template-columns: repeat(3, 1fr);
height: 100px;
width: 100px;
position: absolute;
bottom: 50px;
right: 50px;
}
.MobileButton Button {
height: 100%;
width: 100%;
background-color: lightgrey;
}
.invisible {
visibility: hidden;
}
/* end test movement from mobile */
<!-- PRELOAD SPRITES -->
<!-- GAME SCENES -->
<!-- INVENTORY MENU -->
<div id="PlayerDisplay"></div>
<div id="ArmourGrid"></div>
<div id="ItemGrid"></div>
<!-- start test movement from mobile -->
↑
←
→
↓
<!-- end test movement from mobile -->
// GET ELEMENTS IN VARIABLE
const canvas = document.getElementById(“game”);
const healthBar = document.getElementById(“HealthBar”);
const menuButton = document.getElementById(“MenuButton”)
const playerMenu = document.getElementById(“PlayerMenu”);
const playerDisplay = document.getElementById(“PlayerDisplay”);
const armourGrid = document.getElementById(“ArmourGrid”);
const itemGrid = document.getElementById(“ItemGrid”);
const wallImg = document.getElementById(“wall”);
const floorImg = document.getElementById(“floor”);
const exitImg = document.getElementById(“exit”);
const playerImg = document.getElementById(“player”);
const itemImg = document.getElementById(“item”);
const trapImg = document.getElementById(“trap”);
const chestImg = document.getElementById(“chest”);
const overlay = document.getElementById(“ChestOverlay”);
// CREATE THE MAZE
const ctx = canvas.getContext(“2d”);
const tileSize = 40;
const maze = [
[‘#’, ‘#’, ‘#’, ‘#’, ‘#’, ‘#’, ‘#’, ‘#’, ‘#’, ‘#’],
[‘#’, ‘P’, ‘ ‘, ‘T’, ‘#’, ‘ ‘, ‘ ‘, ‘ ‘, ‘ ‘, ‘#’],
[‘#’, ‘C’, ‘#’, ‘I’, ‘#’, ‘ ‘, ‘#’, ‘#’, ‘ ‘, ‘#’],
[‘#’, ‘ ‘, ‘#’, ‘ ‘, ‘ ‘, ‘ ‘, ‘#’, ‘ ‘, ‘ ‘, ‘#’],
[‘#’, ‘ ‘, ‘#’, ‘#’, ‘#’, ‘ ‘, ‘#’, ‘ ‘, ‘#’, ‘#’],
[‘#’, ‘ ‘, ‘C’, ‘ ‘, ‘#’, ‘ ‘, ‘ ‘, ‘ ‘, ‘ ‘, ‘#’],
[‘#’, ‘#’, ‘#’, ‘ ‘, ‘#’, ‘#’, ‘#’, ‘#’, ‘ ‘, ‘#’],
[‘#’, ‘ ‘, ‘#’, ‘ ‘, ‘S’, ‘ ‘, ‘ ‘, ‘#’, ‘ ‘, ‘#’],
[‘#’, ‘ ‘, ‘#’, ‘#’, ‘#’, ‘#’, ‘ ‘, ‘#’, ‘E’, ‘#’],
[‘#’, ‘#’, ‘#’, ‘#’, ‘#’, ‘#’, ‘#’, ‘#’, ‘#’, ‘#’],
];
function drawMaze() {
for (let y = 0; y {playerMenu.style.display =
(playerMenu.style.display === “none”) ?
“grid” : “none”;
});
function updatePlayerMenu() {
playerDisplay.innerHTML = ` `;
armourGrid.innerHTML = “”;
currentPlayer.armour?.forEach(armour => { const armourElement = document.createElement(“img”);
armourElement.src = armour.img;
armourElement.alt = “armour”;
armourElement.title = armour.name;
armourGrid.appendChild(armourElement);});
itemGrid.innerHTML = “”;
currentPlayer.item?.forEach((item, index) => {
const itemElement = document.createElement(“img”);
itemElement.src = item.img;
itemElement.alt = “item”;
itemElement.title = item.name;
itemElement.addEventListener(“click”, () => {
if (confirm(`Use or remove “${item.name}”?`)) {removeItemFromInventory(index);
}
});
itemGrid.appendChild(itemElement);
});
// make a bestiary for encountered enemies
}
function addItemToInventory(item) {
currentPlayer.item.push(item);
updatePlayerMenu();
showChestPopup(item);
}
function removeItemFromInventory(index) {
if (index >= 0 && index = trapAnim.frameSpeed) {
trapAnim.totalTime = 0;
trapAnim.currentFrame = trapAnim.active ?
Math.min(trapAnim.currentFrame + 1, trapAnim.totalFrame – 1) :
Math.max(trapAnim.currentFrame – 1, 0);
}
}
//CHEST
const chestAnim = {
totalFrames: 3,
currentFrame: 0,
totalTime: 0,
frameSpeed: 150,
active: false
};
const openedChest = new Set();
function openChest() {
// check what chests have been opened
const openedChestPosition = `${currentPlayer.x},${currentPlayer.y}`;
if (maze[currentPlayer.y][currentPlayer.x] === ‘C’ && !openedChest.has(openedChestPosition)) {
openedChest.add(openedChestPosition);
// play the chest opening audio and animatiom
document.getElementById(“chestaudio”).play();
chestAnim.active = true;
// Pick a random item
const randomItem = allItems[Math.floor(Math.random() * allItems.length)];
alert(“you found me”);
// call fucntion when chest is opened
health(30);
addItemToInventory(randomItem);
// give item in inventory near health bar(later on game menu like botw) and get rid of item posiotion x:3 y:3
}
// if maze player x === S
}
function showChestPopup(item) {
// Clear any previous content instead of removing the entire element
overlay.innerHTML = “”;
overlay.style.display = “flex”;
const content = document.createElement(“div”);
content.id = “ChestContent”;
content.innerHTML = `
${item.name}
${item.description}
(Click anywhere to close)
`;
overlay.appendChild(content);
// Click anywhere to close
overlay.onclick = () => {
overlay.style.display = “none”;
overlay.innerHTML = “”;
};
// Auto-remove after 5 seconds
setTimeout(() => {
overlay.style.display = “none”;
overlay.innerHTML = “”;
}, 5000);
}
function AnimationChest(lastTimeChest) {
chestAnim.totalTime += lastTimeChest;
if (chestAnim.totalTime >= chestAnim.frameSpeed) {
chestAnim.totalTime = 0;
chestAnim.currentFrame = chestAnim.active ?
Math.min(chestAnim.currentFrame + 1, chestAnim.totalFrame – 1) :
Math.max(chestAnim.currentFrame – 1, 0);
}
}
// PLAYER
const basePlayer = {
maxHealth: 100,
health: 100,
armour: [],
item: []
};
let currentPlayer = {
x: 1,
y: 1,
…structuredClone(basePlayer)
};
healthBar.textContent = “HEALTH: ” + currentPlayer.health;
function health(number) {
currentPlayer.health += number;
healthBar.textContent = “HEALTH: ” + currentPlayer.health;
if (currentPlayer.health > currentPlayer.maxHealth) {
currentPlayer.health = currentPlayer.maxHealth;
}
if (currentPlayer.health alert(“You win!”), 100);
}
if (maze[newY][newX] === ‘T’) {
trapAnim.active= true;
health(-20);
alert(“You got hurt!”);
}
}
}
function resetPlayer() {
currentPlayer = {
x: 1,
y: 1,
…structuredClone(basePlayer)
};
}
/*player death
const totalFrameDeath = 3;
let currentFrameDeath = 0;
let totalTimeDeath = 0;
const frameSpeedDeath = 150;
let activeDeath = false;
function Death() {
alert(“YOU DIED”);
resetPlayer();
activeDeath = true;
– death animation
– stop movement
– lose display
}
*/
// ITEMS
const allItems = [
{
name: “Golden Fleece”,
description: “Argonaut’s story”,
img: “ASSETS/SPRITES/GoldenFleece.png”
//give 1 extra life
//if health = 0 then health = maxHealth (only once)
},
{
name: “Potion of Healing”,
description: “Restores 50 health.”,
img: “ASSETS/SPRITES/HealingPotion.png”
}
]
/* {skull = gain 1 throwable skull from skeleton chest and throw it in an arch( use THE trick to make items or enemies jump in 2d games simulating the arch – youtube link with explanations : https://youtu.be/46TL8AEL4yY?si=yqMJ9-BDJeMvc-_G)
– randomize between skull, bones or rocks when looting a skeleton chest }, */
/* { door = {make a closed door and find keys in chest or when defeating enemies. if key then activeDoor = true
},*/
// KEY AND BUTTON
document.addEventListener(“keydown”, (e) => {
switch (e.key) {
case “ArrowUp”: move(0, -1); break;
case “ArrowDown”: move(0, 1); break;
case “ArrowLeft”: move(-1, 0); break;
case “ArrowRight”: move(1, 0); break;
case “e”: case “E”: openChest(); break}
});
/*start test (movement from mobile)*/
document.getElementById(“topcenter”).addEventListener(“click”, () => move(0, -1));
document.getElementById(“bottomcenter”).addEventListener(“click”, () => move(0, 1));
document.getElementById(“centerleft”).addEventListener(“click”, () => move(-1, 0));
document.getElementById(“centerright”).addEventListener(“click”, () => move(1, 0));
/*end test (movement from mobile)*/
document.getElementById(“centercenter”).addEventListener(“click”, () =>
openChest());
// ANIMATIONS LOOP
let startTimeChest= performance.now(); let startTimeTrap = performance.now();
function AnimationLoop(time) {
const lastTimeTrap = time – startTimeTrap; startTimeTrap = time;
AnimationTrap(lastTimeTrap);
const lastTimeChest = time – startTimeChest; startTimeChest = time;
AnimationChest(lastTimeChest);
updatePlayerMenu();
drawMaze();
requestAnimationFrame(AnimationLoop);}
requestAnimationFrame(AnimationLoop);
// TO DO LIST
// gain god’s favour through challenges and sacrifice (example : kill 1000 enemies and Ares will make you stronger, solve 1000 puzzles and will gain Athena’s favour, Hermes will make you faster, Apollo will make your bow stronger) (level up favours)
I was expecting the chest to animate with the frameshhet but it only disappear. I tried to block test but nothing. I’m posting the entire script because I don’t know if the problem is in the CSS, html or JavaScript.