Update script.js
This commit is contained in:
parent
4c353781b1
commit
aa2166af12
225
script.js
225
script.js
@ -1,72 +1,189 @@
|
||||
const game = document.getElementById("game");
|
||||
const movesText = document.getElementById("moves");
|
||||
const scoreText = document.getElementById("score");
|
||||
const canvas = document.getElementById('gameCanvas');
|
||||
const ctx = canvas.getContext('2d');
|
||||
const uiLayer = document.getElementById('ui-layer');
|
||||
const finalScoreSpan = document.getElementById('final-score');
|
||||
const inGameScore = document.getElementById('ingame-score');
|
||||
const highScoreDisplay = document.getElementById('high-score-display');
|
||||
|
||||
// Setup UI Flexbox
|
||||
uiLayer.style.display = 'none';
|
||||
|
||||
let moves = 0;
|
||||
let score = 0;
|
||||
let firstCard = null;
|
||||
let lock = false;
|
||||
let gameRunning = true;
|
||||
let speed = 2;
|
||||
let blockHeight = 35;
|
||||
let initialWidth = 200;
|
||||
let blocks = [];
|
||||
let currentBlock = {};
|
||||
let direction = 1;
|
||||
let hue = 0;
|
||||
|
||||
let icons = ["🍎", "🍌", "🍇", "🍓", "🍒", "🍑", "🍍", "🥝"]; // 8 pairs
|
||||
let cards = [...icons, ...icons];
|
||||
cards.sort(() => Math.random() - 0.5);
|
||||
function initGame() {
|
||||
score = 0;
|
||||
blocks = [];
|
||||
speed = 3;
|
||||
hue = Math.random() * 360;
|
||||
|
||||
cards.forEach(icon => {
|
||||
const div = document.createElement("div");
|
||||
div.className = "card";
|
||||
div.dataset.icon = icon;
|
||||
div.textContent = "?";
|
||||
game.appendChild(div);
|
||||
inGameScore.innerText = score;
|
||||
|
||||
// Base block
|
||||
blocks.push({
|
||||
x: (canvas.width - initialWidth) / 2,
|
||||
y: canvas.height - 100,
|
||||
width: initialWidth,
|
||||
color: `hsl(${hue}, 100%, 50%)`
|
||||
});
|
||||
|
||||
game.querySelectorAll(".card").forEach(card => {
|
||||
card.addEventListener("click", () => {
|
||||
if(lock || card.classList.contains("open") || card.classList.contains("matched")) return;
|
||||
spawnBlock();
|
||||
|
||||
card.classList.add("open");
|
||||
card.textContent = card.dataset.icon;
|
||||
// Perubahan: Jangan panggil draw() di sini jika ini reset,
|
||||
// biarkan resetGame yang mengontrol loop
|
||||
if(gameRunning) draw();
|
||||
}
|
||||
|
||||
if(!firstCard){
|
||||
firstCard = card;
|
||||
function spawnBlock() {
|
||||
const prevBlock = blocks[blocks.length - 1];
|
||||
hue += 10;
|
||||
|
||||
currentBlock = {
|
||||
x: 0,
|
||||
y: prevBlock.y - blockHeight,
|
||||
width: prevBlock.width,
|
||||
color: `hsl(${hue}, 100%, 50%)`
|
||||
};
|
||||
|
||||
if (currentBlock.y < 150) {
|
||||
blocks.forEach(b => b.y += blockHeight);
|
||||
currentBlock.y += blockHeight;
|
||||
}
|
||||
}
|
||||
|
||||
function drawBackground() {
|
||||
ctx.strokeStyle = 'rgba(255, 255, 255, 0.05)';
|
||||
ctx.lineWidth = 1;
|
||||
for (let i = 0; i < canvas.width; i += 40) {
|
||||
ctx.beginPath(); ctx.moveTo(i, 0); ctx.lineTo(i, canvas.height); ctx.stroke();
|
||||
}
|
||||
for (let i = 0; i < canvas.height; i += 40) {
|
||||
ctx.beginPath(); ctx.moveTo(0, i); ctx.lineTo(canvas.width, i); ctx.stroke();
|
||||
}
|
||||
}
|
||||
|
||||
function draw() {
|
||||
if (!gameRunning) return;
|
||||
|
||||
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
||||
drawBackground();
|
||||
|
||||
currentBlock.x += speed * direction;
|
||||
if (currentBlock.x + currentBlock.width > canvas.width || currentBlock.x < 0) {
|
||||
direction *= -1;
|
||||
}
|
||||
|
||||
ctx.shadowBlur = 20;
|
||||
blocks.forEach(b => {
|
||||
ctx.fillStyle = b.color;
|
||||
ctx.shadowColor = b.color;
|
||||
ctx.fillRect(b.x, b.y, b.width, blockHeight);
|
||||
ctx.fillStyle = 'rgba(255,255,255,0.2)';
|
||||
ctx.fillRect(b.x, b.y, b.width, 5);
|
||||
});
|
||||
|
||||
ctx.fillStyle = currentBlock.color;
|
||||
ctx.shadowColor = currentBlock.color;
|
||||
ctx.fillRect(currentBlock.x, currentBlock.y, currentBlock.width, blockHeight);
|
||||
ctx.fillStyle = 'rgba(255,255,255,0.4)';
|
||||
ctx.fillRect(currentBlock.x, currentBlock.y, currentBlock.width, 5);
|
||||
ctx.shadowBlur = 0;
|
||||
|
||||
requestAnimationFrame(draw);
|
||||
}
|
||||
|
||||
function placeBlock() {
|
||||
if (!gameRunning) return;
|
||||
|
||||
const prevBlock = blocks[blocks.length - 1];
|
||||
let overlap = currentBlock.width - Math.abs(currentBlock.x - prevBlock.x);
|
||||
|
||||
if (overlap <= 0) {
|
||||
gameOver();
|
||||
return;
|
||||
}
|
||||
|
||||
moves++;
|
||||
movesText.textContent = moves;
|
||||
lock = true;
|
||||
|
||||
if(firstCard.dataset.icon === card.dataset.icon){
|
||||
score += 100;
|
||||
scoreText.textContent = score;
|
||||
|
||||
firstCard.classList.add("matched");
|
||||
card.classList.add("matched");
|
||||
|
||||
firstCard = null;
|
||||
lock = false;
|
||||
|
||||
saveScore(score);
|
||||
if (currentBlock.x < prevBlock.x) {
|
||||
currentBlock.width = overlap;
|
||||
currentBlock.x = prevBlock.x;
|
||||
} else {
|
||||
score -= 20;
|
||||
scoreText.textContent = score;
|
||||
currentBlock.width = overlap;
|
||||
}
|
||||
|
||||
blocks.push(currentBlock);
|
||||
score++;
|
||||
inGameScore.innerText = score;
|
||||
speed += 0.15;
|
||||
spawnBlock();
|
||||
}
|
||||
|
||||
function gameOver() {
|
||||
gameRunning = false;
|
||||
uiLayer.style.display = 'flex';
|
||||
finalScoreSpan.innerText = score;
|
||||
saveScore(score);
|
||||
}
|
||||
|
||||
// --- PERBAIKAN UTAMA ADA DI SINI ---
|
||||
function resetGame() {
|
||||
uiLayer.style.display = 'none';
|
||||
|
||||
// Matikan gameRunning sementara agar klik tombol tidak terbaca sebagai 'placeBlock'
|
||||
gameRunning = false;
|
||||
|
||||
initGame();
|
||||
|
||||
// Beri jeda 300ms. Setelah tombol dilepas, baru game aktif.
|
||||
setTimeout(() => {
|
||||
firstCard.classList.remove("open");
|
||||
firstCard.textContent = "?";
|
||||
card.classList.remove("open");
|
||||
card.textContent = "?";
|
||||
|
||||
firstCard = null;
|
||||
lock = false;
|
||||
}, 700);
|
||||
gameRunning = true;
|
||||
draw(); // Mulai loop animasi
|
||||
}, 300);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
function saveScore(score){
|
||||
fetch("save_score.php", {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
||||
body: "score=" + score
|
||||
// Input Handler yang lebih aman
|
||||
document.body.onkeydown = (e) => {
|
||||
if (e.code === 'Space') {
|
||||
if (gameRunning) {
|
||||
placeBlock();
|
||||
} else if (uiLayer.style.display === 'flex') {
|
||||
// Cegah spasi merestart terlalu cepat
|
||||
resetGame();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
document.body.onclick = (e) => {
|
||||
// PENTING: Jika yang diklik adalah tombol atau bagian dari UI Layer, JANGAN jalankan game
|
||||
if (e.target.closest('#ui-layer') || e.target.closest('.btn')) return;
|
||||
|
||||
if (gameRunning) placeBlock();
|
||||
};
|
||||
|
||||
function saveScore(newScore) {
|
||||
const formData = new FormData();
|
||||
formData.append('score', newScore);
|
||||
fetch('save_score.php', { method: 'POST', body: formData })
|
||||
.then(response => response.json())
|
||||
.then(data => fetchHighScore());
|
||||
}
|
||||
|
||||
function fetchHighScore() {
|
||||
fetch('save_score.php?action=get')
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
highScoreDisplay.innerText = data.high_score || "0";
|
||||
});
|
||||
}
|
||||
|
||||
// Start pertama kali
|
||||
initGame();
|
||||
// Paksa draw pertama kali
|
||||
gameRunning = true;
|
||||
draw();
|
||||
Loading…
x
Reference in New Issue
Block a user