Update script.js
This commit is contained in:
parent
4c353781b1
commit
aa2166af12
245
script.js
245
script.js
@ -1,72 +1,189 @@
|
|||||||
const game = document.getElementById("game");
|
const canvas = document.getElementById('gameCanvas');
|
||||||
const movesText = document.getElementById("moves");
|
const ctx = canvas.getContext('2d');
|
||||||
const scoreText = document.getElementById("score");
|
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 score = 0;
|
||||||
let firstCard = null;
|
let gameRunning = true;
|
||||||
let lock = false;
|
let speed = 2;
|
||||||
|
let blockHeight = 35;
|
||||||
|
let initialWidth = 200;
|
||||||
|
let blocks = [];
|
||||||
|
let currentBlock = {};
|
||||||
|
let direction = 1;
|
||||||
|
let hue = 0;
|
||||||
|
|
||||||
let icons = ["🍎", "🍌", "🍇", "🍓", "🍒", "🍑", "🍍", "🥝"]; // 8 pairs
|
function initGame() {
|
||||||
let cards = [...icons, ...icons];
|
score = 0;
|
||||||
cards.sort(() => Math.random() - 0.5);
|
blocks = [];
|
||||||
|
speed = 3;
|
||||||
|
hue = Math.random() * 360;
|
||||||
|
|
||||||
|
inGameScore.innerText = score;
|
||||||
|
|
||||||
cards.forEach(icon => {
|
// Base block
|
||||||
const div = document.createElement("div");
|
blocks.push({
|
||||||
div.className = "card";
|
x: (canvas.width - initialWidth) / 2,
|
||||||
div.dataset.icon = icon;
|
y: canvas.height - 100,
|
||||||
div.textContent = "?";
|
width: initialWidth,
|
||||||
game.appendChild(div);
|
color: `hsl(${hue}, 100%, 50%)`
|
||||||
});
|
|
||||||
|
|
||||||
game.querySelectorAll(".card").forEach(card => {
|
|
||||||
card.addEventListener("click", () => {
|
|
||||||
if(lock || card.classList.contains("open") || card.classList.contains("matched")) return;
|
|
||||||
|
|
||||||
card.classList.add("open");
|
|
||||||
card.textContent = card.dataset.icon;
|
|
||||||
|
|
||||||
if(!firstCard){
|
|
||||||
firstCard = card;
|
|
||||||
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);
|
|
||||||
} else {
|
|
||||||
score -= 20;
|
|
||||||
scoreText.textContent = score;
|
|
||||||
|
|
||||||
setTimeout(() => {
|
|
||||||
firstCard.classList.remove("open");
|
|
||||||
firstCard.textContent = "?";
|
|
||||||
card.classList.remove("open");
|
|
||||||
card.textContent = "?";
|
|
||||||
|
|
||||||
firstCard = null;
|
|
||||||
lock = false;
|
|
||||||
}, 700);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
});
|
|
||||||
|
|
||||||
function saveScore(score){
|
spawnBlock();
|
||||||
fetch("save_score.php", {
|
|
||||||
method: "POST",
|
// Perubahan: Jangan panggil draw() di sini jika ini reset,
|
||||||
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
// biarkan resetGame yang mengontrol loop
|
||||||
body: "score=" + score
|
if(gameRunning) draw();
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (currentBlock.x < prevBlock.x) {
|
||||||
|
currentBlock.width = overlap;
|
||||||
|
currentBlock.x = prevBlock.x;
|
||||||
|
} else {
|
||||||
|
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(() => {
|
||||||
|
gameRunning = true;
|
||||||
|
draw(); // Mulai loop animasi
|
||||||
|
}, 300);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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