Merge branch 'main' of https://git-eng.ukwms.ac.id/2526-web/Kelompok02-Memory-Card
|
Before Width: | Height: | Size: 28 KiB |
BIN
asset/anggur.jpg
|
Before Width: | Height: | Size: 26 KiB |
BIN
asset/apel.jpg
|
Before Width: | Height: | Size: 32 KiB |
|
Before Width: | Height: | Size: 56 KiB |
BIN
asset/jambu.jpg
|
Before Width: | Height: | Size: 8.7 KiB |
BIN
asset/jeruk.jpg
|
Before Width: | Height: | Size: 31 KiB |
BIN
asset/lemon.jpg
|
Before Width: | Height: | Size: 48 KiB |
BIN
asset/nanas.jpg
|
Before Width: | Height: | Size: 20 KiB |
BIN
asset/pisang.jpg
|
Before Width: | Height: | Size: 37 KiB |
|
Before Width: | Height: | Size: 29 KiB |
@ -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
@ -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>
|
||||
@ -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
@ -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
@ -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
@ -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
|
After Width: | Height: | Size: 195 KiB |
BIN
images/fruit10.png
Normal file
|
After Width: | Height: | Size: 192 KiB |
BIN
images/fruit2.png
Normal file
|
After Width: | Height: | Size: 259 KiB |
BIN
images/fruit3.png
Normal file
|
After Width: | Height: | Size: 254 KiB |
BIN
images/fruit4.png
Normal file
|
After Width: | Height: | Size: 139 KiB |
BIN
images/fruit5.png
Normal file
|
After Width: | Height: | Size: 125 KiB |
BIN
images/fruit6.png
Normal file
|
After Width: | Height: | Size: 157 KiB |
BIN
images/fruit7.png
Normal file
|
After Width: | Height: | Size: 146 KiB |
BIN
images/fruit8.png
Normal file
|
After Width: | Height: | Size: 147 KiB |
BIN
images/fruit9.png
Normal file
|
After Width: | Height: | Size: 155 KiB |
45
login.css
@ -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);
|
||||
}
|
||||
|
||||
30
login.html
@ -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>
|
||||
9
login.js
@ -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);
|
||||
|
||||
@ -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); }
|
||||
}
|
||||
@ -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">
|
||||
|
||||
@ -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>
|
||||
</div>
|
||||
|
||||
<script src="mainboard.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
41
register.css
@ -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); }
|
||||
}
|
||||
@ -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>
|
||||
|
||||
21
register.php
@ -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));
|
||||
|
||||