401 lines
10 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// =========================
// SET MESSAGE
// =========================
function setMessage(text, status) {
if (messageEl) {
messageEl.textContent = text;
messageEl.className = status;
}
}
// =========================
// KONSTANTA KARTU
// =========================
const SUITS = ['♠', '♥', '♦', '♣'];
const RANKS = ['A', '2', '3', '4', '5', '6', '7', '8', '9', '10', 'J', 'Q', 'K'];
// =========================
// DOM ELEMENTS
// =========================
const dealerHandEl = document.getElementById('dealer-hand');
const playerHandEl = document.getElementById('player-hand');
const dealerValueEl = document.getElementById('dealer-value');
const playerValueEl = document.getElementById('player-value');
const balanceEl = document.getElementById('balance');
const betInput = document.getElementById('bet-input');
const betBtn = document.getElementById('bet-btn');
const currentBetEl = document.getElementById('current-bet');
const hitBtn = document.getElementById('hit');
const standBtn = document.getElementById('stand');
const doubleBtn = document.getElementById('double');
const newRoundBtn = document.getElementById('new-round');
const messageEl = document.getElementById('message');
// TOPUP DOM
const topupBtn = document.getElementById('topup-btn');
const topupModal = document.getElementById('topup-modal');
const topupForm = document.getElementById('topup-form');
const topupMessage = document.getElementById('topup-message');
const modalAmount = document.getElementById('modal-amount');
// =========================
// VARIABEL GAME
// =========================
let deck = [];
let dealer = [];
let player = [];
let dealerHidden = true;
let balance = 0;
let currentBet = 0;
let inRound = false;
// =========================================
// AMBIL VALUE DARI PHP (HIDDEN INPUT)
// =========================================
let gameBalance = parseInt(document.getElementById('php-balance').value);
let userId = parseInt(document.getElementById('php-userid').value);
// =========================
// UPDATE BALANCE DISPLAY
// =========================
function updateBalanceDisplay() {
if (balanceEl) balanceEl.textContent = gameBalance.toLocaleString('id-ID');
const modalBalance = document.getElementById('modal-balance');
if (modalBalance) modalBalance.textContent = gameBalance.toLocaleString('id-ID');
}
// =========================
// SYNC KE SERVER
// =========================
async function syncBalanceToServer() {
try {
const response = await fetch('update_balance.php', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ userId, balance: gameBalance })
});
const data = await response.json();
return data.success;
} catch (err) {
console.error("Error:", err);
return false;
}
}
// =========================
// UPDATE SESSION PHP
// =========================
async function updateSessionBalance() {
try {
await fetch('update_session.php', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ balance: gameBalance })
});
} catch (err) {
console.error("Error:", err);
}
}
// =========================
// UPDATE BALANCE LOGIC
// =========================
function updateGameBalance(amount, type) {
if (type === 'win') gameBalance += amount;
else if (type === 'loss') gameBalance -= amount;
else if (type === 'bet') gameBalance -= amount;
else if (type === 'refund') gameBalance += amount;
updateBalanceDisplay();
syncBalanceToServer();
updateSessionBalance();
}
window.updateGameBalance = updateGameBalance;
window.gameBalance = gameBalance;
window.userId = userId;
// =========================
// FUNGSI KARTU
// =========================
function makeDeck() {
deck = [];
for (const s of SUITS) {
for (const r of RANKS) deck.push({ suit: s, rank: r });
}
}
function shuffle() {
for (let i = deck.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[deck[i], deck[j]] = [deck[j], deck[i]];
}
}
function cardValue(card) {
if (card.rank === 'A') return [1, 11];
if (['J', 'Q', 'K'].includes(card.rank)) return [10];
return [parseInt(card.rank)];
}
function handValues(hand) {
let totals = [0];
for (const card of hand) {
const cv = cardValue(card);
const newTotals = [];
totals.forEach(t => cv.forEach(v => newTotals.push(t + v)));
totals = [...new Set(newTotals)];
}
const valid = totals.filter(t => t <= 21);
return valid.length ? Math.max(...valid) : Math.min(...totals);
}
function renderHand(el, hand, hideFirst = false) {
el.innerHTML = '';
hand.forEach((c, i) => {
const div = document.createElement('div');
if (hideFirst && i === 0) {
div.className = 'card back';
div.textContent = 'HIDE';
} else {
div.className = 'card' + ((c.suit === '♥' || c.suit === '♦') ? ' red' : '');
const top = document.createElement('div');
const bot = document.createElement('div');
top.textContent = c.rank + ' ' + c.suit;
bot.textContent = c.rank + ' ' + c.suit;
bot.style.alignSelf = 'flex-end';
div.appendChild(top);
div.appendChild(bot);
}
el.appendChild(div);
});
}
// =========================
// UPDATE UI
// =========================
function updateUI() {
renderHand(dealerHandEl, dealer, dealerHidden);
renderHand(playerHandEl, player);
dealerValueEl.textContent = dealerHidden ? '??' : 'Nilai: ' + handValues(dealer);
playerValueEl.textContent = 'Nilai: ' + handValues(player);
updateBalanceDisplay();
if (currentBetEl) currentBetEl.textContent = currentBet.toLocaleString('id-ID');
}
// =========================
// START ROUND
// =========================
function startRound() {
if (inRound) return;
const bet = Number(betInput.value);
if (bet <= 0) return alert("Masukkan jumlah taruhan!");
if (bet > gameBalance) return alert("BANK TIDAK CUKUP!");
currentBet = bet;
updateGameBalance(bet, 'bet');
inRound = true;
dealerHidden = true;
setMessage("", "");
makeDeck();
shuffle();
dealer = [deck.pop(), deck.pop()];
player = [deck.pop(), deck.pop()];
updateUI();
// NATURAL BLACKJACK
if (handValues(player) === 21) {
dealerHidden = false;
updateUI();
if (handValues(dealer) === 21) {
updateGameBalance(currentBet, 'refund');
setMessage("Tie!", "");
} else {
updateGameBalance(Math.floor(currentBet * 2.5), 'win');
setMessage("Blackjack! You win!", "win");
}
inRound = false;
currentBet = 0;
}
}
// =========================
// HIT
// =========================
function playerHit() {
if (!inRound) return;
player.push(deck.pop());
updateUI();
if (handValues(player) > 21) {
dealerHidden = false;
setMessage("Bust! You Lose!", "lose");
inRound = false;
currentBet = 0;
}
}
// =========================
// STAND
// =========================
function playerStand() {
if (!inRound) return;
dealerHidden = false;
while (handValues(dealer) < 17) dealer.push(deck.pop());
updateUI();
const pv = handValues(player);
const dv = handValues(dealer);
if (dv > 21 || pv > dv) {
updateGameBalance(currentBet * 2, 'win');
setMessage("You Win!", "win");
} else if (pv === dv) {
updateGameBalance(currentBet, 'refund');
setMessage("Tie!", "");
} else {
setMessage("You Lose!", "lose");
}
inRound = false;
currentBet = 0;
}
// =========================
// DOUBLE
// =========================
function playerDouble() {
if (!inRound) return;
if (gameBalance < currentBet) return alert("Bank tidak cukup untuk double.");
updateGameBalance(currentBet, 'bet');
currentBet *= 2;
player.push(deck.pop());
updateUI();
if (handValues(player) > 21) {
dealerHidden = false;
setMessage("Bust! You Lose!", "lose");
inRound = false;
currentBet = 0;
return;
}
playerStand();
}
// =========================
// TOP UP MODAL
// =========================
topupBtn.addEventListener('click', () => {
topupModal.style.display = 'flex';
topupForm.reset();
topupMessage.style.display = 'none';
updateBalanceDisplay();
});
function closeTopUpModal() {
topupModal.style.display = 'none';
topupMessage.style.display = 'none';
}
window.addEventListener('click', e => {
if (e.target === topupModal) closeTopUpModal();
});
function setModalAmount(amount) {
modalAmount.value = amount;
}
function showTopUpMessage(msg, type) {
topupMessage.textContent = msg;
topupMessage.className = 'topup-message ' + type;
topupMessage.style.display = 'block';
}
topupForm.addEventListener('submit', e => {
e.preventDefault();
const bankMethod = document.querySelector('input[name="bank_method"]:checked');
const amount = parseInt(modalAmount.value);
if (!bankMethod) return showTopUpMessage("Pilih metode pembayaran!", "error");
if (amount <= 0 || amount > 1000000) return showTopUpMessage("Jumlah harus 11.000.000", "error");
const formData = new FormData(topupForm);
formData.append('userId', userId);
fetch('process_topup.php', { method: 'POST', body: formData })
.then(res => res.json())
.then(data => {
if (data.success) {
gameBalance = data.new_balance;
updateBalanceDisplay();
showTopUpMessage("✓ Top up berhasil!", "success");
setTimeout(closeTopUpModal, 2000);
} else {
showTopUpMessage(data.message, "error");
}
})
.catch(() => showTopUpMessage("Terjadi kesalahan.", "error"));
});
// =========================
// EVENT LISTENERS
// =========================
betBtn.addEventListener('click', startRound);
hitBtn.addEventListener('click', playerHit);
standBtn.addEventListener('click', playerStand);
doubleBtn.addEventListener('click', playerDouble);
newRoundBtn.addEventListener('click', () => {
if (inRound && !confirm("Masih dalam ronde. Reset?")) return;
dealer = [];
player = [];
deck = [];
inRound = false;
dealerHidden = true;
currentBet = 0;
setMessage("Game di-reset.", "");
updateUI();
});
window.addEventListener('keydown', e => {
if (e.key === 'h') playerHit();
if (e.key === 's') playerStand();
if (e.key === 'd') playerDouble();
if (e.key === 'Enter') startRound();
});
// =========================
// INIT
// =========================
document.addEventListener('DOMContentLoaded', () => {
updateBalanceDisplay();
});