From e0623117d7b435b556848524811380358c39fb08 Mon Sep 17 00:00:00 2001 From: Aloneinsky Date: Mon, 1 Dec 2025 11:19:26 +0700 Subject: [PATCH] footer --- public/assets/js/game.js | 275 +++++++++++++++++++++++++++++++++++++++ public/game.php | 247 +++++++++++++++++++++++++++++++++++ public/signin.php | 2 +- 3 files changed, 523 insertions(+), 1 deletion(-) create mode 100644 public/assets/js/game.js create mode 100644 public/game.php diff --git a/public/assets/js/game.js b/public/assets/js/game.js new file mode 100644 index 0000000..6826907 --- /dev/null +++ b/public/assets/js/game.js @@ -0,0 +1,275 @@ + +const ranks = ['A','2','3','4','5','6','7','8','9','10','J','Q','K']; +const suits = ['♠','♥','♦','♣']; + +const cardsWrapper = document.getElementById('cardsWrapper'); +const dealerWrapper = document.getElementById('dealerWrapper'); +const deckEl = document.getElementById('deck'); + +const hitBtn = document.getElementById('hit'); +const standBtn = document.getElementById('stand'); +const endScreen = document.getElementById('endScreen'); +const endMessage = document.getElementById('endMessage'); +const playAgainBtn = document.getElementById('playAgainBtn'); + +let playerCards = []; +let dealerCards = []; +let playerEls = []; +let dealerEls = []; +let dealerHiddenCards = []; +let dealerHiddenEls = []; +let gamePhase = 'PLAYING'; // langsung start + +/* ---------- helpers ---------- */ +function randomCard(){ + const r = ranks[Math.floor(Math.random()*ranks.length)]; + const s = suits[Math.floor(Math.random()*suits.length)]; + return {rank:r, suit:s, color:(s==='♥'||s==='♦')?'red':'black'}; +} + +function createCardEl(card){ + const el = document.createElement('div'); + el.className = 'card' + (card.color==='red' ? ' red' : ''); + el.innerHTML = ` +
${card.rank}
+
${card.suit}
+
${card.rank}
+ `; + return el; +} + +function createBackCardEl(){ + const el = document.createElement('div'); + el.className = 'card back-card'; + el.innerText = 'HIT'; + return el; +} + +function wrapCardInContainer(cardEl, isDealer = false){ + const wrapper = document.createElement('div'); + wrapper.className = 'card-wrapper'; + if (!isDealer) wrapper.style.perspective = '1000px'; + wrapper.appendChild(cardEl); + return wrapper; +} + +function calc(cards){ + let total=0, ace=0; + for(const c of cards){ + if(c.rank==='A'){ total+=11; ace++; } + else if(['J','Q','K'].includes(c.rank)) total+=10; + else total+=Number(c.rank); + } + while(total>21 && ace>0){ total-=10; ace--; } + return total; +} + +/* ---------- UI updates ---------- */ +function updateTotals(){ + document.getElementById('playerTotalUI').innerText = calc(playerCards); + const hiddenExists = dealerHiddenEls.length > 0; + if(hiddenExists) document.getElementById('dealerTotalUI').innerText = '??'; + else document.getElementById('dealerTotalUI').innerText = calc(dealerCards); +} + +/* layout overlap */ +function layoutOverlap(list, wrapper){ + const isDealer = (wrapper === dealerWrapper); + const cardW = 110; + const cardH = 154; + const overlap = 28; + const count = list.length; + const totalWidth = cardW + Math.max(0, count-1)*overlap; + const startX = (wrapper.clientWidth - totalWidth)/2; + list.forEach((el, i)=>{ + const x = startX + i*overlap; + let y = (wrapper.clientHeight - cardH) / 2; + el.style.transform = `translate3d(${x}px, ${y}px, 0)`; + el.style.zIndex = 100 + i; + }); +} + +/* animation from deck */ +function animateFromDeck(cardWrapper, isInitial = true){ + const startX = deckEl.offsetLeft; + const startY = deckEl.offsetTop; + const targetTransformMatch = cardWrapper.style.transform.match(/translate3d\((.*?)px,\s*(.*?)px/); + const targetX = parseFloat(targetTransformMatch ? targetTransformMatch[1] : startX); + const targetY = parseFloat(targetTransformMatch ? targetTransformMatch[2] : startY); + cardWrapper.style.transition = 'none'; + cardWrapper.style.transform = `translate3d(${startX}px, ${startY}px, 0) scale(0.4)`; + const inner = cardWrapper.querySelector('.card'); + if(inner) inner.classList.add('is-moving'); + requestAnimationFrame(()=>{ + requestAnimationFrame(()=>{ + cardWrapper.style.transition = ''; + cardWrapper.style.transform = `translate3d(${targetX}px, ${targetY}px, 0) scale(1)`; + const rotZ = (Math.random() - 0.5) * 12; + const rotX = isInitial ? 0 : (Math.random() - 0.5) * 10; + if(inner){ + inner.style.transition = 'transform .4s cubic-bezier(.68,-0.55,.27,1.55), filter .3s ease'; + inner.style.transform = `rotateZ(${rotZ}deg) rotateX(${rotX}deg)`; + } + setTimeout(()=>{ + if(inner){ + inner.classList.remove('is-moving'); + inner.style.transform = `rotateZ(0deg) rotateX(0deg)`; + inner.style.transition = ''; + } + },420); + }); + }); +} + +/* ---------- dealing ---------- */ +function dealPlayer(){ + const card = randomCard(); + playerCards.push(card); + const cardEl = createCardEl(card); + const wrapper = wrapCardInContainer(cardEl, false); + cardsWrapper.appendChild(wrapper); + playerEls.push(wrapper); + layoutOverlap(playerEls, cardsWrapper); + animateFromDeck(wrapper); + updateTotals(); +} + +function dealDealer(faceDown=false){ + const card = randomCard(); + dealerCards.push(card); + let wrapper; + if(faceDown){ + const backEl = createBackCardEl(); + wrapper = wrapCardInContainer(backEl, true); + dealerHiddenCards.push(card); + dealerHiddenEls.push(wrapper); + } else { + const cardEl = createCardEl(card); + wrapper = wrapCardInContainer(cardEl, true); + } + dealerEls.push(wrapper); + dealerWrapper.appendChild(wrapper); + layoutOverlap(dealerEls, dealerWrapper); + animateFromDeck(wrapper); + updateTotals(); +} + +function flipAllDealerHidden(){ + const hiddenWrappers = Array.from(dealerHiddenEls); + hiddenWrappers.forEach((wrapper, i) => { + const backEl = wrapper.querySelector('.back-card'); + const cardObj = dealerHiddenCards[i]; + const realEl = createCardEl(cardObj); + setTimeout(()=>{ + if(backEl){ backEl.style.transform = 'rotateY(90deg)'; backEl.style.opacity = '0'; } + realEl.style.transform = 'rotateY(-90deg)'; realEl.style.opacity = '0'; + wrapper.appendChild(realEl); + setTimeout(()=>{ + if(backEl) backEl.remove(); + realEl.style.transform = 'rotateY(0deg)'; + realEl.style.opacity = '1'; + updateTotals(); + },220); + },300 + i*300); + }); + setTimeout(()=>{ + dealerHiddenEls = []; + dealerHiddenCards = []; + }, 300 + hiddenWrappers.length*300 + 100); +} + +/* ---------- dealer play & game end ---------- */ +function dealerPlay(){ + gamePhase = 'DEALER_TURN'; + const cycle = setInterval(()=>{ + if(calc(dealerCards) < 17){ + dealDealer(false); + } else { + clearInterval(cycle); + finishResult(); + } + }, 900); +} + +function finishResult(){ + gamePhase = 'END'; + const p = calc(playerCards); + const d = calc(dealerCards); + let msg = ''; + if(p > 21) msg = 'PLAYER BUST — YOU LOSE'; + else if(d > 21) msg = 'DEALER BUST — YOU WIN!'; + else if(p === 21 && playerCards.length === 2 && d !== 21) msg = 'BLACKJACK! — YOU WIN! (3:2)'; + else if(p > d) msg = 'YOU WIN!'; + else if(p < d) msg = 'YOU LOSE!'; + else msg = 'PUSH (DRAW) — TARUHAN KEMBALI'; + showEnd(msg); +} + +/* ---------- end-screen handling ---------- */ +function showEnd(msg){ + document.getElementById('status').innerText = msg; + hitBtn.disabled = true; + standBtn.disabled = true; + endMessage.innerText = msg; + endScreen.style.display = 'flex'; + playAgainBtn.focus(); +} + +/* ---------- controls ---------- */ +function hit(){ + if(gamePhase !== 'PLAYING' || hitBtn.disabled) return; + dealPlayer(); + if(calc(playerCards) > 21){ + setTimeout(() => finishResult(), 500); + } +} + +function stand(){ + if(gamePhase !== 'PLAYING' || standBtn.disabled) return; + gamePhase = 'DEALER_TURN'; + hitBtn.disabled = true; + standBtn.disabled = true; + document.getElementById('status').innerText = 'DEALER TURN'; + flipAllDealerHidden(); + setTimeout(()=> dealerPlay(), 300 + dealerHiddenEls.length*300); +} + +/* ---------- start / restart ---------- */ +function startGame(){ + // disable during dealing to prevent accidental clicks + hitBtn.disabled = true; + standBtn.disabled = true; + playerCards = []; dealerCards = []; playerEls.forEach(e=>e.remove()); dealerEls.forEach(e=>e.remove()); + playerEls = []; dealerEls = []; dealerHiddenCards = []; dealerHiddenEls = []; + endScreen.style.display = 'none'; + + // dealing sequence + setTimeout(()=> dealPlayer(), 80); + setTimeout(()=> dealDealer(false), 280); // dealer first open + setTimeout(()=> dealPlayer(), 480); + setTimeout(()=> dealDealer(true), 680); // dealer second hidden + + // after dealing finished, enable buttons and set phase to PLAYING + setTimeout(()=>{ + gamePhase = 'PLAYING'; + document.getElementById('status').innerText = 'YOUR TURN'; + hitBtn.disabled = false; + standBtn.disabled = false; + // auto-stand on natural + if(calc(playerCards) === 21) stand(); + }, 900); +} + +function restart(){ + // reset and start again + startGame(); +} + +/* ---------- events ---------- */ +hitBtn.addEventListener('click', hit); +standBtn.addEventListener('click', stand); +playAgainBtn.addEventListener('click', restart); +window.addEventListener('resize', ()=>{ layoutOverlap(playerEls, cardsWrapper); layoutOverlap(dealerEls, dealerWrapper); }); + +/* start immediately */ +startGame(); diff --git a/public/game.php b/public/game.php new file mode 100644 index 0000000..154ced2 --- /dev/null +++ b/public/game.php @@ -0,0 +1,247 @@ + + + + + +Hit or Run + + + +
+
+
+
+
Your Total Card: 0
+
PLACE YOUR BET
+
+ + + +
+
+
DEALER
+
+
Total Dealer: 0
+
+ +
+
+
+ +
+ + +
+
+
+ + +
+
+
+ + + + diff --git a/public/signin.php b/public/signin.php index 94bf352..d25fe9f 100644 --- a/public/signin.php +++ b/public/signin.php @@ -19,7 +19,7 @@ header("Location: home"); exit; } else { - $error = "Wrong Username or Password."; + $error = "Wrong Username or Password.";s } } else { $error = "Username not found.";