This commit is contained in:
Nathan 2025-12-08 13:08:16 +07:00
commit 8c601f5db4
35 changed files with 901 additions and 104 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 29 KiB

View File

@ -9,7 +9,8 @@
html, body {
margin: 0;
height: 100%;
overflow: hidden;
overflow-x: hidden;
overflow-y: auto;
font-family: "Poppins", sans-serif;
}
@ -73,6 +74,7 @@ body {
width: 100%;
height: 100%;
perspective: 1000px;
max-height: 160px;
cursor: pointer;
}
@ -198,14 +200,50 @@ body {
font-weight: 600;
}
.gameboard {
flex: 1;
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 16px;
padding: 25px 40px;
box-sizing: border-box;
place-items: center;
}
.card {
width: 100%;
aspect-ratio: 4 / 3;
perspective: 1000px;
cursor: pointer;
}
.play-again { background: #b700ff; color: white; }
.leaderboard { background: gold; }
.back-menu { background: #444; color: white; } /* ← DITAMBAHKAN */
.back-menu { background: #444; color: white; }
@keyframes fadeIn {
from { opacity:0; transform: scale(0.8); }
to { opacity:1; transform: scale(1); }
}
@media (max-width: 700px) {
.gameboard {
grid-template-columns: repeat(3, 1fr);
padding: 12px;
gap: 10px;
}
}
@media (max-width: 700px) {
.gameboard {
grid-template-columns: repeat(2, 1fr);
padding: 12px;
gap: 10px;
}
}
</style>
</head>
<body>
@ -244,12 +282,12 @@ body {
<script>
const images = [
"asset/alpukat.jpg",
"asset/anggur.jpg",
"asset/apel.jpg",
"asset/buah naga.jpg",
"asset/jambu.jpg",
"asset/jeruk.jpg"
"images/fruit1.png",
"images/fruit2.png",
"images/fruit3.png",
"images/fruit4.png",
"images/fruit5.png",
"images/fruit6.png",
];
let cards = [...images, ...images];

411
gameboard-hard.html Normal file
View File

@ -0,0 +1,411 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Memory Card Premium 5x4</title>
<style>
html, body {
margin: 0;
height: 100%;
overflow: hidden;
font-family: "Poppins", sans-serif;
}
body {
background: linear-gradient(135deg, #8929ff 30%, #ff419b 100%);
display: flex;
flex-direction: column;
}
.topbar {
display: flex;
justify-content: space-between;
align-items: center;
padding: 8px 14px;
background: rgb(255, 255, 255);
border-bottom: 2px solid rgba(39, 35, 35, 0.6);
backdrop-filter: blur(14px);
z-index: 50;
}
.back-btn {
font-size: 28px;
background: none;
border: none;
cursor: pointer;
color: #222;
}
.right-info {
display: flex;
gap: 10px;
}
.pill {
display: flex;
align-items: center;
gap: 6px;
padding: 5px 12px;
background: white;
border-radius: 20px;
border: 2px solid rgba(255,255,255,0.8);
font-weight: 600;
box-shadow: 0 3px 6px rgba(0,0,0,0.2), inset 0 0 6px rgba(255,255,255,0.6);
}
.gameboard {
flex: 1;
display: grid;
grid-template-columns: repeat(5, 1fr);
grid-template-rows: repeat(4, 1fr);
gap: 8px;
padding: 10px;
box-sizing: border-box;
}
.card {
width: 100%;
height: 100%;
perspective: 1000px;
cursor: pointer;
}
.inner {
width: 100%;
height: 100%;
position: relative;
transform-style: preserve-3d;
transition: transform 0.55s ease;
border-radius: 10px;
}
.front, .back {
position: absolute;
width: 100%;
height: 100%;
backface-visibility: hidden;
display: flex;
justify-content: center;
align-items: center;
border-radius: 10px;
background: linear-gradient(135deg, #7a28ff 20%, #eb2bbf 80%);
border: 2px solid rgba(255,255,255,0.55);
box-shadow: 0 3px 6px rgba(0,0,0,0.25);
}
.front {
font-size: calc(10px + 2vw);
font-weight: 700;
color: #ff6a4d;
}
.back {
transform: rotateY(180deg);
}
.back img {
width: 85%;
height: 85%;
object-fit: contain;
filter: drop-shadow(0 3px 4px rgba(0,0,0,0.45));
}
.card:not(.flipped):hover .front {
transform: scale(1.05);
transition: 0.25s;
}
.card.flipped .inner { transform: rotateY(180deg); }
.card.matched .front,
.card.matched .back {
border-color: #7affd6;
box-shadow: 0 0 15px #7affd6, 0 0 30px rgba(122,255,214,0.8);
}
.combo-popup {
position: absolute;
padding: 12px 20px;
background: linear-gradient(135deg, rgba(255,95,109,0.92), rgba(255,0,102,0.92));
border-radius: 14px;
color: white;
font-weight: 800;
text-align: center;
font-size: 20px;
box-shadow: 0 0 18px rgba(255, 75, 43, 0.9), 0 0 35px rgba(255, 0, 85, 0.7);
animation: comboFade 1.4s ease forwards;
pointer-events: none;
border: 2px solid rgba(255,255,255,0.35);
}
@keyframes comboFade {
0% { opacity: 0; transform: translate(-50%, -10px) scale(0.7); }
20% { opacity: 1; transform: translate(-50%, -30px) scale(1); }
100% { opacity: 0; transform: translate(-50%, -80px) scale(1.1); }
}
.end-screen {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0,0,0,0.45);
backdrop-filter: blur(7px);
display: none;
justify-content: center;
align-items: center;
z-index: 200;
}
.end-box {
width: 80%;
max-width: 380px;
background: white;
border-radius: 22px;
padding: 20px;
text-align: center;
}
.end-title {
font-size: 28px;
font-weight: 700;
margin-bottom: 4px;
}
.score-row {
display: flex;
justify-content: space-between;
margin: 8px 0;
font-size: 18px;
}
.end-btn {
width: 100%;
padding: 12px;
border-radius: 16px;
border: none;
font-size: 18px;
margin-top: 10px;
cursor: pointer;
font-weight: 600;
}
.play-again { background: #b700ff; color: white; }
.leaderboard { background: gold; }
.back-menu { background: #444; color: white; }
</style>
</head>
<body>
<header class="topbar">
<button class="back-btn" onclick="window.location.href='mainboard.html'"></button>
<div class="right-info">
<div class="pill"><span class="icon"></span><span id="timer">60</span>s</div>
<div class="pill"><span class="icon"></span><span id="score">0</span></div>
<div class="pill"><span class="icon">🎯</span><span id="moves">0</span></div>
</div>
</header>
<div id="game-board" class="gameboard"></div>
<div id="endScreen" class="end-screen">
<div class="end-box">
<div class="end-title">🎉 Selamat!</div>
<p>Anda berhasil menyelesaikan permainan!</p>
<div class="score-row"><span>Skor Base:</span> <span id="baseScoreEnd">0</span></div>
<div class="score-row"><span>Time Bonus:</span> <span id="timeBonusEnd">0</span></div>
<div class="score-row"><span>Move Bonus:</span> <span id="moveBonusEnd">0</span></div>
<hr>
<div class="score-row" style="font-weight:700;">
<span>Total Skor:</span>
<span id="totalScoreEnd">0</span>
</div>
<button class="end-btn play-again" onclick="startGame()">🔁 Main Lagi</button>
<button class="end-btn leaderboard">🏆 Lihat Leaderboard</button>
<button class="end-btn back-menu" onclick="window.location.href='mainboard.html'">⬅ Kembali</button>
</div>
</div>
<script>
const images = [
"images/fruit1.png",
"images/fruit2.png",
"images/fruit3.png",
"images/fruit4.png",
"images/fruit5.png",
"images/fruit6.png",
"images/fruit7.png",
"images/fruit8.png",
"images/fruit9.png",
"images/fruit10.png"
];
let cards = [...images, ...images];
let flipped = [];
let timerStarted = false;
let time = 60;
let moves = 0;
let score = 0;
let countdown;
let combo = 1;
let pendingMatch = false;
let lastMatchTime = 0;
function showComboPopup(targetCard, combo, bonus) {
const popup = document.createElement("div");
popup.className = "combo-popup";
popup.innerHTML = `
COMBO X${combo}<br>
<span style="font-size:14px;">+${bonus} Bonus</span>
`;
const rect = targetCard.getBoundingClientRect();
popup.style.left = rect.left + rect.width / 2 + "px";
popup.style.top = rect.top + "px";
popup.style.position = "fixed";
document.body.appendChild(popup);
setTimeout(() => popup.remove(), 1500);
}
function shuffleDistant(cards, minDistance = 4) {
let valid = false;
let result;
while (!valid) {
result = [...cards].sort(() => Math.random() - 0.5);
valid = true;
const checked = new Set();
for (let i = 0; i < result.length; i++) {
const value = result[i];
if (checked.has(value)) continue;
checked.add(value);
const firstIndex = result.indexOf(value);
const lastIndex = result.lastIndexOf(value);
const distance = Math.abs(lastIndex - firstIndex);
if (distance < minDistance) {
valid = false;
break;
}
}
}
return result;
}
function startTimer() {
timerStarted = true;
countdown = setInterval(() => {
document.getElementById("timer").textContent = --time;
if (time <= 0) {
clearInterval(countdown);
document.getElementById("endScreen").style.display = "flex";
}
}, 1000);
}
function flipCard(card) {
if (!timerStarted) startTimer();
if (flipped.length === 2 || card.classList.contains("matched") || card.classList.contains("flipped"))
return;
card.classList.add("flipped");
flipped.push(card);
if (flipped.length === 2) {
moves++;
document.getElementById("moves").textContent = moves;
let img1 = flipped[0].querySelector("img").src;
let img2 = flipped[1].querySelector("img").src;
if (img1 === img2) {
const now = Date.now();
if (!pendingMatch) {
pendingMatch = true;
combo = 1;
} else {
if (now - lastMatchTime <= 3000) {
combo++;
let comboBonus = combo * 10;
score += comboBonus;
showComboPopup(flipped[0], combo, comboBonus);
} else combo = 1;
}
lastMatchTime = now;
flipped.forEach(c => c.classList.add("matched"));
score += 50;
document.getElementById("score").textContent = score;
time += 5;
document.getElementById("timer").textContent = time;
flipped = [];
if (document.querySelectorAll(".matched").length === cards.length) {
setTimeout(() => document.getElementById("endScreen").style.display = "flex", 500);
}
} else {
setTimeout(() => {
flipped.forEach(c => c.classList.remove("flipped"));
flipped = [];
}, 800);
}
}
}
function startGame() {
document.getElementById("endScreen").style.display = "none";
const board = document.getElementById("game-board");
board.innerHTML = "";
shuffleDistant(cards, 4).forEach(image => {
const card = document.createElement("div");
card.className = "card";
card.innerHTML = `
<div class="inner">
<div class="front">?</div>
<div class="back"><img src="${image}"></div>
</div>
`;
card.onclick = () => flipCard(card);
board.appendChild(card);
});
time = 60;
moves = 0;
score = 0;
combo = 1;
pendingMatch = false;
flipped = [];
timerStarted = false;
document.getElementById("timer").textContent = time;
document.getElementById("moves").textContent = moves;
document.getElementById("score").textContent = score;
}
startGame();
</script>
</body>
</html>

View File

@ -56,7 +56,6 @@ body {
box-shadow: 0 3px 6px rgba(0,0,0,0.2), inset 0 0 6px rgba(255,255,255,0.6);
}
/* === 4×4 GRID === */
.gameboard {
flex: 1;
display: grid;
@ -67,7 +66,6 @@ body {
box-sizing: border-box;
}
/* === CARDS === */
.card {
width: 100%;
height: 100%;
@ -84,7 +82,6 @@ body {
border-radius: 10px;
}
/* Face */
.front, .back {
position: absolute;
width: 100%;
@ -116,7 +113,6 @@ body {
filter: drop-shadow(0 3px 4px rgba(0,0,0,0.45));
}
/* Hover & Flip */
.card:not(.flipped):hover .front {
transform: scale(1.05);
transition: 0.25s;
@ -124,14 +120,12 @@ body {
.card.flipped .inner { transform: rotateY(180deg); }
/* Match Shine */
.card.matched .front,
.card.matched .back {
border-color: #7affd6;
box-shadow: 0 0 15px #7affd6, 0 0 30px rgba(122,255,214,0.8);
}
/* === COMBO POPUP === */
.combo-popup {
position: absolute;
padding: 12px 20px;
@ -200,10 +194,36 @@ body {
font-weight: 600;
}
.gameboard {
flex: 1;
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 16px;
padding: 25px 40px;
box-sizing: border-box;
place-items: center;
}
.play-again { background: #b700ff; color: white; }
.leaderboard { background: gold; }
.back-menu { background: #444; color: white; }
@media (max-width: 900px) {
.gameboard {
grid-template-columns: repeat(3, 1fr);
padding: 20px;
gap: 12px;
}
}
@media (max-width: 600px) {
.gameboard {
grid-template-columns: repeat(2, 1fr);
padding: 14px;
gap: 10px;
}
}
</style>
</head>
<body>
@ -222,7 +242,6 @@ body {
<div class="end-box">
<div class="end-title">🎉 Selamat!</div>
<p>Anda berhasil menyelesaikan permainan!</p>
<div class="score-row"><span>Skor Base:</span> <span id="baseScoreEnd">0</span></div>
<div class="score-row"><span>Time Bonus:</span> <span id="timeBonusEnd">0</span></div>
<div class="score-row"><span>Move Bonus:</span> <span id="moveBonusEnd">0</span></div>
@ -241,16 +260,16 @@ body {
</div>
<script>
/* === 8 GAMBAR (16 kartu) === */
const images = [
"asset/alpukat.jpg",
"asset/anggur.jpg",
"asset/apel.jpg",
"asset/buah naga.jpg",
"asset/jambu.jpg",
"asset/jeruk.jpg",
"asset/lemon.jpg",
"asset/nanas.jpg"
"images/fruit1.png",
"images/fruit2.png",
"images/fruit3.png",
"images/fruit4.png",
"images/fruit5.png",
"images/fruit6.png",
"images/fruit7.png",
"images/fruit8.png"
];
let cards = [...images, ...images];
@ -260,7 +279,6 @@ let time = 60;
let moves = 0;
let score = 0;
let countdown;
let combo = 1;
let pendingMatch = false;
let lastMatchTime = 0;

39
home.html Normal file
View File

@ -0,0 +1,39 @@
<!DOCTYPE html>
<html lang="id">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Login Memory Cards</title>
<link rel="stylesheet" href="homestyle.css" />
</head>
<body>
<!-- Floating Fruits Memory Cards -->
<img src="images/fruit1.png" class="fruit f1">
<img src="images/fruit2.png" class="fruit f2">
<img src="images/fruit3.png" class="fruit f3">
<img src="images/fruit4.png" class="fruit f4">
<img src="images/fruit5.png" class="fruit f5">
<img src="images/fruit6.png" class="fruit f6">
<img src="images/fruit7.png" class="fruit f7">
<img src="images/fruit8.png" class="fruit f8">
<img src="images/fruit9.png" class="fruit f9">
<img src="images/fruit10.png" class="fruit f10">
<!-- HOME / MEMORY SCREEN -->
<div class="container memory">
<h1 class="title">MEMORY<br>CARDS</h1>
<button class="btn start" onclick="goToLoginCard()">Start Game</button>
<div class="or-line">
<div class="line"></div>
<span>OR</span>
<div class="line"></div>
</div>
<button class="btn how-to-play">❓ Cara bermain</button>
</div>
</body>
<script src="home.js"></script>
</html>

8
home.js Normal file
View File

@ -0,0 +1,8 @@
function goToLoginCard() {
document.querySelector(".memory").classList.add("hidden");
document.querySelector(".login-wrapper").classList.remove("hidden");
}
function goToLoginCard() {
window.location.href = "login.html"; // atau page tujuan lain
}

148
homestyle.css Normal file
View File

@ -0,0 +1,148 @@
/* === BACKGROUND === */
body {
margin: 0;
font-family: Poppins, Arial, sans-serif;
height: 100vh;
overflow: hidden;
display: flex;
justify-content: center;
align-items: center;
/* Pink → Ungu Gradient */
background: linear-gradient(135deg, #ff9ed1, #d784ff);
}
/* === ANIMATED FLOATING FRUITS === */
.fruit {
position: absolute;
width: 95px;
opacity: 0.85;
animation: float 7s infinite ease-in-out;
pointer-events: none; /* supaya tidak ganggu klik */
filter: drop-shadow(0 4px 6px rgba(0,0,0,0.12));
}
/* Posisi lebih seimbang & tidak menutupi teks */
.f1 { top: 6%; left: 12%; animation-delay: .3s; }
.f2 { top: 12%; right: 18%; animation-delay: 1.2s; }
.f3 { top: 48%; right: 8%; animation-delay: .7s; }
.f4 { top: 72%; left: 20%; animation-delay: 1.6s; }
.f5 { top: 28%; left: 60%; animation-delay: .5s; }
.f6 { top: 68%; right: 22%; animation-delay: 1s; }
.f7 { top: 40%; left: 10%; animation-delay: .9s; }
.f8 { top: 82%; left: 70%; animation-delay: 1.4s; }
.f9 { top: 18%; right: 46%; animation-delay: .6s; }
.f10 { top: 80%; right: 10%; animation-delay: 1.8s; }
/* Smooth floating animation */
@keyframes float {
0% { transform: translateY(0) rotate(0deg); }
50% { transform: translateY(-18px) rotate(6deg); }
100% { transform: translateY(0) rotate(0deg); }
}
@keyframes drift {
0% { transform: translateX(0); }
50% { transform: translateX(22px); }
100% { transform: translateX(0); }
}
.fruit {
width: clamp(70px, 7vw, 105px);
}
/* === CENTRAL CONTAINER === */
.container {
text-align: center;
}
/* === TITLE WITH GLOW ANIMATION === */
.title {
font-size: 70px;
font-weight: 900;
color: #ffffff;
line-height: 0.9;
text-shadow:
0 0 10px #ab2cff,
0 0 25px #ff54de,
0 0 35px #a639ff;
animation: glow 2s infinite alternate;
}
@keyframes glow {
0% {
text-shadow:
0 0 10px #ab2cff,
0 0 25px #ff54de,
0 0 40px #a639ff;
}
100% {
text-shadow:
0 0 20px #ffb3f9,
0 0 45px #ff7df3,
0 0 60px #bb5bff;
}
}
/* === BUTTONS === */
.btn {
width: 260px;
padding: 18px 0;
font-size: 22px;
font-weight: bold;
border: none;
border-radius: 40px;
cursor: pointer;
background: linear-gradient(45deg, purple, hotpink);
box-shadow: 0 6px 0 rgb(90, 1, 90);
transition: transform .2s ease-in-out;
color: white;
}
/* === BUTTON SHAKE ON HOVER === */
.btn:hover {
animation: shake 0.4s ease-in-out;
}
@keyframes shake {
0% { transform: translateX(0); }
25% { transform: translateX(-3px); }
50% { transform: translateX(3px); }
75% { transform: translateX(-2px); }
100% { transform: translateX(0); }
}
/* === OR LINE === */
.or-line {
display: flex;
align-items: center;
justify-content: center;
margin: 25px 0;
}
.line {
width: 80px;
height: 2px;
background: rgba(255,255,255,0.8);
}
.or-line span {
margin: 0 10px;
font-weight: bold;
}
/* === HOW TO PLAY === */
.how {
margin-top: 5px;
font-size: 22px;
color: #fff;
opacity: 0.9;
padding: 2px 0;
cursor: pointer;
}
.hidden { display: none !important;
}

BIN
images/fruit1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 195 KiB

BIN
images/fruit10.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 192 KiB

BIN
images/fruit2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 259 KiB

BIN
images/fruit3.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 254 KiB

BIN
images/fruit4.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 139 KiB

BIN
images/fruit5.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 125 KiB

BIN
images/fruit6.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 157 KiB

BIN
images/fruit7.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 146 KiB

BIN
images/fruit8.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 147 KiB

BIN
images/fruit9.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 155 KiB

View File

@ -60,6 +60,7 @@ body {
text-align: center;
color: gray;
margin-bottom: 20px;
animation:appear 3s ease-out;
}
label {
@ -76,6 +77,7 @@ input {
margin-bottom: 15px;
}
input:focus {
outline: none;
border-color: purple;
@ -191,6 +193,11 @@ input:focus {
opacity: 1;
}
.hidden {
display: none !important;
}
@keyframes glitter-burst {
0% {
@ -209,3 +216,41 @@ input:focus {
0% { opacity: 0.2; }
100% { opacity: 0.55; }
}
.fruit {
position: absolute;
width: 95px;
opacity: .85;
animation: float 6s infinite ease-in-out, drift 15s infinite linear;
pointer-events: none;
}
.f1 { top: 8%; left: 10%; animation-delay: .3s; }
.f2 { top: 65%; right: 12%; animation-delay: .7s; }
.f3 { top: 22%; right: 55%; animation-delay: 1.1s; }
.f4 { bottom: 15%; left: 28%; animation-delay: .9s; }
.f5 { bottom: 10%; right: 30%; animation-delay: 1.4s; }
.f6 { top: 40%; left: 50%; animation-delay: .5s; }
.f7 { bottom: 30%; left: 15%; animation-delay: 1.2s; }
.f8 { top: 75%; right: 40%; animation-delay: .8s; }
.f9 { top: 50%; left: 80%; animation-delay: 1s; }
.f10 { bottom: 20%; right: 5%; animation-delay: 1.3s; }
@keyframes float {
0% { transform: translateY(0) rotate(0deg); }
50% { transform: translateY(-20px) rotate(6deg); }
100% { transform: translateY(0) rotate(0deg); }
}
@keyframes drift {
0% { transform: translateX(0); }
50% { transform: translateX(15px); }
100% { transform: translateX(0); }
}
.login-card, .container {
transition: transform .4s ease;
}
.login-card:hover, .container:hover {
transform: scale(1.012);
}

View File

@ -8,7 +8,6 @@
</head>
<body>
<div class="min-h-screen flex items-center justify-center p-4 relative overflow-hidden">
<!-- Animated Background -->
<div class="bg-animated">
@ -17,6 +16,17 @@
<div class="bg-circle three"></div>
</div>
<img src="images/fruit1.png" class="fruit f1">
<img src="images/fruit2.png" class="fruit f2">
<img src="images/fruit3.png" class="fruit f3">
<img src="images/fruit4.png" class="fruit f4">
<img src="images/fruit5.png" class="fruit f5">
<img src="images/fruit6.png" class="fruit f6">
<img src="images/fruit7.png" class="fruit f7">
<img src="images/fruit8.png" class="fruit f8">
<img src="images/fruit9.png" class="fruit f9">
<img src="images/fruit10.png" class="fruit f10">
<!-- Login Card -->
<div class="login-card">
<div class="icon-wrapper">
@ -45,24 +55,6 @@
<a href="register.html">Daftar Sekarang</a>
</p>
<button class="demo-toggle" onclick="toggleDemo()">Lihat Demo Akun ▼</button>
<div class="demo-box" id="demoBox">
<p><strong>🎮 Demo Akun</strong></p>
<div class="demo-item">
<p><strong>Player:</strong></p>
<p>Username: <code>player</code></p>
<p>Password: <code>player123</code></p>
</div>
<div class="demo-item">
<p><strong>Admin:</strong></p>
<p>Username: <code>admin</code></p>
<p>Password: <code>admin123</code></p>
</div>
</div>
<script src="login.js"></script>
</body>
</html>

View File

@ -84,3 +84,12 @@ document.addEventListener("DOMContentLoaded", function () {
card.appendChild(g);
}
});
setInterval(() => {
document.querySelectorAll(".glitter").forEach(g => {
g.style.setProperty("--x", (Math.random() * 200 - 100) + "px");
g.style.setProperty("--y", (Math.random() * 200 - 100) + "px");
g.style.left = Math.random() * window.innerWidth + "px";
g.style.top = Math.random() * window.innerHeight + "px";
});
}, 900);

View File

@ -1,5 +1,5 @@
/* ================================
🌈 GLOBAL THEME (SAMA DENGAN LOGIN/REGISTER)
BODY & BACKGROUND STYLES
================================== */
body {
@ -15,6 +15,7 @@ body {
position: relative;
}
/* Glow Background Floating */
.bg-circle {
position: absolute;
@ -34,7 +35,7 @@ body {
}
/* ================================
🌟 CONTAINER CARD
CONTAINER CARD
================================== */
.container {
@ -54,7 +55,7 @@ body {
}
/* ================================
👤 HEADER PROFILE
HEADER PROFILE
================================== */
.header {
@ -171,3 +172,34 @@ body {
display: block;
margin-bottom: 12px;
}
.fruit {
position: absolute;
width: 95px;
opacity: .85;
animation: float 6s infinite ease-in-out, drift 15s infinite linear;
pointer-events: none;
}
.f1 { top: 8%; left: 10%; animation-delay: .3s; }
.f2 { top: 65%; right: 12%; animation-delay: .7s; }
.f3 { top: 22%; right: 55%; animation-delay: 1.1s; }
.f4 { bottom: 15%; left: 28%; animation-delay: .9s; }
.f5 { bottom: 10%; right: 30%; animation-delay: 1.4s; }
.f6 { top: 40%; left: 50%; animation-delay: .5s; }
.f7 { bottom: 30%; left: 15%; animation-delay: 1.2s; }
.f8 { top: 75%; right: 40%; animation-delay: .8s; }
.f9 { top: 50%; left: 80%; animation-delay: 1s; }
.f10 { bottom: 20%; right: 5%; animation-delay: 1.3s; }
@keyframes float {
0% { transform: translateY(0) rotate(0deg); }
50% { transform: translateY(-20px) rotate(6deg); }
100% { transform: translateY(0) rotate(0deg); }
}
@keyframes drift {
0% { transform: translateX(0); }
50% { transform: translateX(15px); }
100% { transform: translateX(0); }
}

View File

@ -10,6 +10,18 @@
<body>
<div class="container">
<!-- animasi -->
<img src="images/fruit1.png" class="fruit f1">
<img src="images/fruit2.png" class="fruit f2">
<img src="images/fruit3.png" class="fruit f3">
<img src="images/fruit4.png" class="fruit f4">
<img src="images/fruit5.png" class="fruit f5">
<img src="images/fruit6.png" class="fruit f6">
<img src="images/fruit7.png" class="fruit f7">
<img src="images/fruit8.png" class="fruit f8">
<img src="images/fruit9.png" class="fruit f9">
<img src="images/fruit10.png" class="fruit f10">
<!-- HEADER -->
<header class="header glass">
<div class="user-info">

View File

@ -4,56 +4,56 @@ if (!isset($_SESSION['user'])) {
header("Location: login.php");
exit();
}
$user = $_SESSION['user']; // id, username, role
$user = $_SESSION['user']; // username, role, id
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>MainBoard</title>
<link rel="stylesheet" href="style.css">
<title>MainBoard - Memory Card Game</title>
<link rel="stylesheet" href="mainboard.css">
</head>
<body>
<div class="container">
<!-- Floating Fruits -->
<img src="img/banana.png" class="fruit f1">
<img src="img/apple.png" class="fruit f2">
<img src="img/watermelon.png" class="fruit f3">
<img src="img/orange.png" class="fruit f4">
<img src="img/pineapple.png" class="fruit f5">
<img src="img/pear.png" class="fruit f6">
<img src="img/dragonfruit.png" class="fruit f7">
<div class="container glass">
<header class="header">
<div class="user-info">
<div class="avatar"><?php echo strtoupper(substr($user['username'], 0, 1)); ?></div>
<div class="user-profile">
<div class="avatar">
<?php echo strtoupper(substr($user['username'], 0, 1)); ?>
</div>
<div>
<h2><?php echo $user['username']; ?>
<?php if ($user['role'] === 'admin') echo "👑"; ?>
</h2>
<h2><?php echo $user['username']; ?> <?php echo $user['role'] === 'admin' ? '👑' : '🎮'; ?></h2>
<p><?php echo $user['role'] === 'admin' ? "Administrator" : "Player"; ?></p>
</div>
</div>
<div class="btn-group">
<button onclick="location.href='leaderboard.php'">🏆 Leaderboard</button>
<button onclick="location.href='logout.php'">🚪 Logout</button>
<div class="actions">
<button id="leaderboardBtn" class="btn gold">🏆 Leaderboard</button>
<button id="logoutBtn" class="btn gray">🚪 Logout</button>
</div>
</header>
<h1>🎮 Memory Card Game</h1>
<p>Pilih tingkat kesulitan untuk memulai permainan</p>
<h1 class="page-title">Pilih Tingkat Kesulitan</h1>
<div class="stage-grid">
<button class="stage" onclick="window.location.href='gameboard-easy.html'">
<h3>Easy Mode</h3>
<p>Grid 3x4 (12 kartu)</p>
</button>
<button class>"stage" onclick="selectStage('medium')">
<h3>Medium Mode</h3>
<p>Grid 4x4 (16 kartu)</p>
</button>
<button class="stage" onclick="selectStage('hard')">
<h3>Hard Mode</h3>
<p>Grid 5x4 (20 kartu)</p>
</button>
<button class="stage-btn" onclick="selectStage('easy')">😊 Easy<br>3 x 4</button>
<button class="stage-btn" onclick="selectStage('medium')">🤔 Medium<br>4 x 4</button>
<button class="stage-btn" onclick="selectStage('hard')">😤 Hard<br>4 x 5</button>
</div>
</div>
<script src="mainboard.js"></script>
</body>
</html>

View File

@ -95,10 +95,16 @@ body {
text-align: center;
font-size: 22px;
margin-bottom: 10px;
background: linear-gradient(45deg, purple, hotpink);
color: transparent;
color: white;
padding: 10px;
border-radius: 40px;
background: linear-gradient(45deg, purple, hotpink);;
font-family: 'Segoe UI', Tahoma, Verdana, sans-serif;
text-shadow:
0 0 10px #000000,
}
.subtitle {
text-align: center;
color: gray;
@ -161,3 +167,34 @@ input:focus {
color: purple;
text-decoration: underline;
}
.fruit {
position: absolute;
width: 95px;
opacity: .85;
animation: float 6s infinite ease-in-out, drift 15s infinite linear;
pointer-events: none;
}
.f1 { top: 8%; left: 10%; animation-delay: .3s; }
.f2 { top: 65%; right: 12%; animation-delay: .7s; }
.f3 { top: 22%; right: 55%; animation-delay: 1.1s; }
.f4 { bottom: 15%; left: 28%; animation-delay: .9s; }
.f5 { bottom: 10%; right: 30%; animation-delay: 1.4s; }
.f6 { top: 40%; left: 50%; animation-delay: .5s; }
.f7 { bottom: 30%; left: 15%; animation-delay: 1.2s; }
.f8 { top: 75%; right: 40%; animation-delay: .8s; }
.f9 { top: 50%; left: 80%; animation-delay: 1s; }
.f10 { bottom: 20%; right: 5%; animation-delay: 1.3s; }
@keyframes float {
0% { transform: translateY(0) rotate(0deg); }
50% { transform: translateY(-20px) rotate(6deg); }
100% { transform: translateY(0) rotate(0deg); }
}
@keyframes drift {
0% { transform: translateX(0); }
50% { transform: translateX(15px); }
100% { transform: translateX(0); }
}

View File

@ -9,16 +9,19 @@
<body>
<div class="page">
<!-- Background animasi -->
<div class="bg-1"></div>
<div class="bg-2"></div>
<div class="bg-3"></div>
<img src="images/fruit1.png" class="fruit f1">
<img src="images/fruit2.png" class="fruit f2">
<img src="images/fruit3.png" class="fruit f3">
<img src="images/fruit4.png" class="fruit f4">
<img src="images/fruit5.png" class="fruit f5">
<img src="images/fruit6.png" class="fruit f6">
<img src="images/fruit7.png" class="fruit f7">
<img src="images/fruit8.png" class="fruit f8">
<img src="images/fruit9.png" class="fruit f9">
<img src="images/fruit10.png" class="fruit f10">
<div class="container">
<div class="icon-wrapper">
<div class="icon-bg"></div>
<div class="icon">
</div>
</div>
<h2 class="title">Buat Akun Baru ✨</h2>

View File

@ -1,8 +1,8 @@
<?php
if ($_SERVER["REQUEST_METHOD"] === "POST") {
$username = $_POST["username"];
$email = $_POST["email"];
$username = trim($_POST["username"]);
$email = trim($_POST["email"]);
$password = $_POST["password"];
$confirm = $_POST["confirm"];
@ -21,27 +21,32 @@ if ($_SERVER["REQUEST_METHOD"] === "POST") {
exit;
}
// contoh simpan ke file JSON
$file = "users.json";
$users = file_exists($file) ? json_decode(file_get_contents($file), true) : [];
// 🔍 Cek username & email duplikat
foreach ($users as $u) {
if ($u["username"] === $username) {
if (strtolower($u["username"]) === strtolower($username)) {
echo "Username sudah digunakan";
exit;
}
if ($u["email"] === $email) {
if (strtolower($u["email"]) === strtolower($email)) {
echo "Email sudah digunakan";
exit;
}
}
// 🔐 Enkripsi password
$hashedPassword = password_hash($password, PASSWORD_BCRYPT);
// 📌 tambah user baru
$users[] = [
"id" => time(),
"id" => uniqid(),
"username" => $username,
"email" => $email,
"password" => $password,
"role" => "player"
"password" => $hashedPassword,
"role" => "player",
"created_at" => date("Y-m-d H:i:s")
];
file_put_contents($file, json_encode($users, JSON_PRETTY_PRINT));