footer fixed, games small fix

This commit is contained in:
Aloneinsky 2025-12-01 12:38:32 +07:00
parent e0623117d7
commit ae42c7248e
8 changed files with 564 additions and 493 deletions

View File

@ -211,7 +211,6 @@
<div class="overlay"> <div class="overlay">
<div class="header"> <div class="header">
<div>Your Total Card: <strong id="playerTotalUI">0</strong></div> <div>Your Total Card: <strong id="playerTotalUI">0</strong></div>
<div id="status">PLACE YOUR BET</div>
</div> </div>
<div id="deck" class="deck" aria-hidden="true">DECK</div> <div id="deck" class="deck" aria-hidden="true">DECK</div>

View File

@ -1,21 +1,35 @@
body, html{ html, body {
font-family: Inter, ui-sans-serif, system-ui, -apple-system, 'Segoe UI', Roboto, 'Helvetica Neue', Arial;
margin: 0; margin: 0;
padding: 0;
height: 100%;
}
body {
display: flex;
flex-direction: column;
min-height: 100vh;
font-family: Inter, ui-sans-serif, system-ui, -apple-system, 'Segoe UI', Roboto, 'Helvetica Neue', Arial;
background: radial-gradient(circle at 10% 10%, rgba(255,255,255,0.03), transparent 5%), background: radial-gradient(circle at 10% 10%, rgba(255,255,255,0.03), transparent 5%),
linear-gradient(180deg, #000 0%, #071827 100%), linear-gradient(180deg, #000 0%, #071827 100%),
var(--bg); var(--bg);
color: var(--muted); color: var(--muted);
-webkit-font-smoothing:antialiased; }
-moz-osx-font-smoothing:grayscale;
padding:24px; .main-wrapper {
flex: 1;
display: flex; display: flex;
align-items:center;
justify-content: center; justify-content: center;
height:100%; align-items: center;
overflow: hidden; padding: 24px;
width: 100%;
} }
:root{ :root{
--card-w:110px;
--card-h:154px;
--deck-x: 800px;
--deck-y: 100px;
--bg:#071827; --bg:#071827;
--card:#071827; --card:#071827;
--accent:#ffd54a; --accent:#ffd54a;
@ -253,8 +267,8 @@ footer{
margin-top: 0 !important; margin-top: 0 !important;
} }
.mb16 { .mb24 {
margin-bottom: 16px; margin-bottom: 24px;
} }
/* Chrome, Edge, Safari */ /* Chrome, Edge, Safari */
@ -269,3 +283,194 @@ input[type="number"] {
-moz-appearance: textfield; /* old syntax */ -moz-appearance: textfield; /* old syntax */
appearance: textfield; /* standard property */ appearance: textfield; /* standard property */
} }
footer {
margin-top: auto; /* pushes footer to bottom */
color: #fff;
padding: 1rem;
}
table#leaderboard thead tr th.rank, table#leaderboard tbody tr td.rank{
width: 20%;
font-size: 16px;
}
table#leaderboard thead tr th, table#leaderboard tbody tr td{
width: 40%;
font-size: 20px;
}
:root{
}
.stage{
width:960px;
height:540px;
border-radius:12px;
overflow:hidden;
position:relative;
box-shadow:0 18px 50px rgba(0,0,0,.6)
}
.bg{
position:absolute;
inset:0; background:#153f28
}
.overlay{
position:absolute; inset:0;
background:linear-gradient(180deg, rgba(11,31,20,.45), rgba(8,24,16,.65));
display:flex; flex-direction:column; padding:18px
}
.header{
color:#dff6e9;
font-weight:700;
display:flex;
justify-content:space-between
}
.table-area{
flex:1; display:flex;
align-items:center;
justify-content:center;
position:relative
}
.deck {
position: absolute;
display: hidden;
left: var(--deck-x);
top: var(--deck-y);
width: 82px;
height: 120px;
border-radius: 10px;
background: #113625;
box-shadow: 0 0 0 2px #0d291c inset, 0 10px 20px rgba(0,0,0,0.55);
display:flex; align-items:center; justify-content:center;
color:#b9e4d0;
font-weight:900;
font-size:14px;
letter-spacing:2px;
z-index:500;
}
.player-zone{
position:absolute;
left:37%;
bottom:18px;
transform:translateX(-50%);
width:78%;
display:flex;
flex-direction:column;
align-items:center
}
.cards-wrapper{
position:relative;
width:580px;
height:170px;
display:flex;
justify-content:center;
align-items:center;
}
.dealer-zone{
position:absolute;
left:50%;
top:18px;
transform:translateX(-50%);
width:78%;
display:flex;
flex-direction:column;
align-items:center;
gap:8px;
color:#dff6e9;
font-weight:700
}
.dealer-cards{
position:relative;
width:580px;
height:170px
}
.card-wrapper {
position: absolute;
width: var(--card-w);
height: var(--card-h);
transition: transform .4s cubic-bezier(.68,-0.55,.27,1.55);
align-items: center;
transform-origin: center;
z-index: 100;
}
.card{
width:var(--card-w);
height:var(--card-h);
border-radius:10px;
background:#fff;
display:flex;
flex-direction:column;
justify-content:space-between;
padding:10px;
font-weight:800;
box-shadow:0 10px 22px rgba(0,0,0,.45);
position:absolute;
transition: transform .4s cubic-bezier(.68,-0.55,.27,1.55), opacity .3s ease, filter .3s ease;
backface-visibility:hidden;
transform-origin:center center;
color: #000;
}
.card.is-moving {
filter: drop-shadow(0 0 10px rgba(255, 255, 255, 0.4));
}
.card.red{
color:#c42b2b
}
.card .corner{
font-size:18px
}
.card .center{
font-size:40px;
display:flex;
align-items:center;
justify-content:center;
}
.back-card{
background:#122f21;
display:flex;
align-items:center;
justify-content:center;
font-size:20px;
color:#bddfcf;
letter-spacing:4px;
font-weight:900;
border-radius:10px;
}
.controls{
display:flex;
gap:12px;
margin-top:8px
}
button{
background:#0f5436;
color:#e6fff2;
border:0;
padding:10px 16px;
border-radius:8px;
cursor:pointer;
font-weight:700
}
button:disabled{
background:#2d2d2d;
opacity:.5;
cursor:not-allowed
}
.end-screen {
position: absolute;
inset: 0;
background: rgba(0,0,0,0.7);
backdrop-filter: blur(6px);
display: none;
align-items: center;
justify-content: center;
flex-direction: column;
color: white;
font-weight: 800;
font-size: 48px;
letter-spacing: 2px;
text-align: center;
z-index: 999;
}

View File

@ -1,4 +1,6 @@
/* ===========================================
GLOBALS & SETUP
=========================================== */
const ranks = ['A','2','3','4','5','6','7','8','9','10','J','Q','K']; const ranks = ['A','2','3','4','5','6','7','8','9','10','J','Q','K'];
const suits = ['♠','♥','♦','♣']; const suits = ['♠','♥','♦','♣'];
@ -10,7 +12,6 @@ const hitBtn = document.getElementById('hit');
const standBtn = document.getElementById('stand'); const standBtn = document.getElementById('stand');
const endScreen = document.getElementById('endScreen'); const endScreen = document.getElementById('endScreen');
const endMessage = document.getElementById('endMessage'); const endMessage = document.getElementById('endMessage');
const playAgainBtn = document.getElementById('playAgainBtn');
let playerCards = []; let playerCards = [];
let dealerCards = []; let dealerCards = [];
@ -18,9 +19,14 @@ let playerEls = [];
let dealerEls = []; let dealerEls = [];
let dealerHiddenCards = []; let dealerHiddenCards = [];
let dealerHiddenEls = []; let dealerHiddenEls = [];
let gamePhase = 'PLAYING'; // langsung start
/* ---------- helpers ---------- */ let gamePhase = 'INIT';
let animLock = false; // prevents button spam
/* ===========================================
HELPERS
=========================================== */
function randomCard(){ function randomCard(){
const r = ranks[Math.floor(Math.random()*ranks.length)]; const r = ranks[Math.floor(Math.random()*ranks.length)];
const s = suits[Math.floor(Math.random()*suits.length)]; const s = suits[Math.floor(Math.random()*suits.length)];
@ -45,147 +51,198 @@ function createBackCardEl(){
return el; return el;
} }
function wrapCardInContainer(cardEl, isDealer = false){ function wrapCardInContainer(cardEl){
const wrapper = document.createElement('div'); const wrapper = document.createElement('div');
wrapper.className = 'card-wrapper'; wrapper.className = 'card-wrapper';
if (!isDealer) wrapper.style.perspective = '1000px'; wrapper.style.perspective = '1000px';
wrapper.appendChild(cardEl); wrapper.appendChild(cardEl);
return wrapper; return wrapper;
} }
function calc(cards){ function calc(cards){
let total=0, ace=0; let t = 0, aces = 0;
for(const c of cards){ for(const c of cards){
if(c.rank==='A'){ total+=11; ace++; } if(c.rank === 'A'){ t += 11; aces++; }
else if(['J','Q','K'].includes(c.rank)) total+=10; else if(['J','Q','K'].includes(c.rank)) t += 10;
else total+=Number(c.rank); else t += Number(c.rank);
} }
while(total>21 && ace>0){ total-=10; ace--; } while(t > 21 && aces > 0){
return total; t -= 10; aces--;
}
return t;
} }
/* ---------- UI updates ---------- */
function updateTotals(){ function updateTotals(){
document.getElementById('playerTotalUI').innerText = calc(playerCards); document.getElementById('playerTotalUI').innerText = calc(playerCards);
const hiddenExists = dealerHiddenEls.length > 0; if(dealerHiddenEls.length){
if(hiddenExists) document.getElementById('dealerTotalUI').innerText = '??'; document.getElementById('dealerTotalUI').innerText = '??';
else document.getElementById('dealerTotalUI').innerText = calc(dealerCards); } else {
document.getElementById('dealerTotalUI').innerText = calc(dealerCards);
}
} }
/* layout overlap */
/* ===========================================
BUTTON ENABLE/DISABLE HANDLING
=========================================== */
function updateButtonState(){
if(gamePhase !== 'PLAYING'){
hitBtn.disabled = true;
standBtn.disabled = true;
return;
}
hitBtn.disabled = animLock;
standBtn.disabled = animLock;
}
/* ===========================================
LAYOUT
=========================================== */
function layoutOverlap(list, wrapper){ function layoutOverlap(list, wrapper){
const isDealer = (wrapper === dealerWrapper);
const cardW = 110; const cardW = 110;
const cardH = 154; const cardH = 154;
const overlap = 28; const overlap = 28;
const count = list.length; const count = list.length;
const totalWidth = cardW + Math.max(0, count-1)*overlap; const totalW = cardW + Math.max(0, count - 1)*overlap;
const startX = (wrapper.clientWidth - totalWidth)/2; const startX = (wrapper.clientWidth - totalW) / 2;
const startY = (wrapper.clientHeight - cardH) / 2;
list.forEach((el, i) => { list.forEach((el, i) => {
const x = startX + i*overlap; const x = startX + i*overlap;
let y = (wrapper.clientHeight - cardH) / 2; el.style.transform = `translate3d(${x}px, ${startY}px, 0)`;
el.style.transform = `translate3d(${x}px, ${y}px, 0)`;
el.style.zIndex = 100 + i; el.style.zIndex = 100 + i;
}); });
} }
/* animation from deck */
function animateFromDeck(cardWrapper, isInitial = true){ /* ===========================================
ANIMATION FROM DECK
=========================================== */
function animateFromDeck(wrapper){
animLock = true;
updateButtonState();
const targetX = parseFloat(wrapper.style.transform.match(/translate3d\((.*?)px/)?.[1] || 0);
const targetY = parseFloat(wrapper.style.transform.match(/,\s*(.*?)px/)?.[1] || 0);
const startX = deckEl.offsetLeft; const startX = deckEl.offsetLeft;
const startY = deckEl.offsetTop; const startY = deckEl.offsetTop;
const targetTransformMatch = cardWrapper.style.transform.match(/translate3d\((.*?)px,\s*(.*?)px/);
const targetX = parseFloat(targetTransformMatch ? targetTransformMatch[1] : startX); wrapper.style.transition = 'none';
const targetY = parseFloat(targetTransformMatch ? targetTransformMatch[2] : startY); wrapper.style.transform = `translate3d(${startX}px, ${startY}px, 0) scale(0.4)`;
cardWrapper.style.transition = 'none';
cardWrapper.style.transform = `translate3d(${startX}px, ${startY}px, 0) scale(0.4)`; const inner = wrapper.querySelector('.card');
const inner = cardWrapper.querySelector('.card');
if(inner) inner.classList.add('is-moving');
requestAnimationFrame(()=>{ requestAnimationFrame(()=>{
requestAnimationFrame(()=>{ requestAnimationFrame(()=>{
cardWrapper.style.transition = ''; wrapper.style.transition = '';
cardWrapper.style.transform = `translate3d(${targetX}px, ${targetY}px, 0) scale(1)`; wrapper.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){ if(inner){
inner.style.transition = 'transform .4s cubic-bezier(.68,-0.55,.27,1.55), filter .3s ease'; inner.style.transition = 'transform .4s cubic-bezier(.68,-0.55,.27,1.55)';
inner.style.transform = `rotateZ(${rotZ}deg) rotateX(${rotX}deg)`; inner.style.transform = `rotateZ(${(Math.random()-0.5)*12}deg)`;
} }
setTimeout(()=>{ setTimeout(()=>{
if(inner){ if(inner){
inner.classList.remove('is-moving'); inner.style.transform = '';
inner.style.transform = `rotateZ(0deg) rotateX(0deg)`;
inner.style.transition = ''; inner.style.transition = '';
} }
animLock = false;
updateButtonState();
}, 420); }, 420);
}); });
}); });
} }
/* ---------- dealing ---------- */
function dealPlayer(){ function dealPlayer(){
const card = randomCard(); const card = randomCard();
playerCards.push(card); playerCards.push(card);
const cardEl = createCardEl(card);
const wrapper = wrapCardInContainer(cardEl, false); const el = createCardEl(card);
cardsWrapper.appendChild(wrapper); const wrap = wrapCardInContainer(el);
playerEls.push(wrapper); cardsWrapper.appendChild(wrap);
playerEls.push(wrap);
layoutOverlap(playerEls, cardsWrapper); layoutOverlap(playerEls, cardsWrapper);
animateFromDeck(wrapper); animateFromDeck(wrap);
updateTotals(); updateTotals();
} }
function dealDealer(faceDown=false){ function dealDealer(faceDown=false){
const card = randomCard(); const card = randomCard();
dealerCards.push(card); dealerCards.push(card);
let wrapper;
let wrap;
if(faceDown){ if(faceDown){
const backEl = createBackCardEl(); const back = createBackCardEl();
wrapper = wrapCardInContainer(backEl, true); wrap = wrapCardInContainer(back);
dealerHiddenCards.push(card); dealerHiddenCards.push(card);
dealerHiddenEls.push(wrapper); dealerHiddenEls.push(wrap);
} else { } else {
const cardEl = createCardEl(card); const el = createCardEl(card);
wrapper = wrapCardInContainer(cardEl, true); wrap = wrapCardInContainer(el);
} }
dealerEls.push(wrapper);
dealerWrapper.appendChild(wrapper); dealerEls.push(wrap);
dealerWrapper.appendChild(wrap);
layoutOverlap(dealerEls, dealerWrapper); layoutOverlap(dealerEls, dealerWrapper);
animateFromDeck(wrapper); animateFromDeck(wrap);
updateTotals(); updateTotals();
} }
function flipAllDealerHidden(){ function flipAllDealerHidden(){
const hiddenWrappers = Array.from(dealerHiddenEls); animLock = true;
hiddenWrappers.forEach((wrapper, i) => { updateButtonState();
const backEl = wrapper.querySelector('.back-card');
const cardObj = dealerHiddenCards[i]; const hidden = Array.from(dealerHiddenEls);
const realEl = createCardEl(cardObj);
hidden.forEach((wrapper, i)=>{
const back = wrapper.querySelector('.back-card');
const card = dealerHiddenCards[i];
const real = createCardEl(card);
setTimeout(()=>{ setTimeout(()=>{
if(backEl){ backEl.style.transform = 'rotateY(90deg)'; backEl.style.opacity = '0'; } if(back){
realEl.style.transform = 'rotateY(-90deg)'; realEl.style.opacity = '0'; back.style.transform = 'rotateY(90deg)';
wrapper.appendChild(realEl); back.style.opacity = '0';
}
real.style.transform = 'rotateY(-90deg)';
real.style.opacity = '0';
wrapper.appendChild(real);
setTimeout(()=>{ setTimeout(()=>{
if(backEl) backEl.remove(); if(back) back.remove();
realEl.style.transform = 'rotateY(0deg)'; real.style.transform = 'rotateY(0deg)';
realEl.style.opacity = '1'; real.style.opacity = '1';
updateTotals(); updateTotals();
}, 220); }, 220);
},300 + i*300);
}, 250 + i*250);
}); });
setTimeout(()=>{ setTimeout(()=>{
dealerHiddenEls = [];
dealerHiddenCards = []; dealerHiddenCards = [];
}, 300 + hiddenWrappers.length*300 + 100); dealerHiddenEls = [];
animLock = false;
updateButtonState();
}, 250 + hidden.length*250 + 300);
} }
/* ---------- dealer play & game end ---------- */
function dealerPlay(){ function dealerPlay(){
gamePhase = 'DEALER_TURN'; gamePhase = 'DEALER_TURN';
const cycle = setInterval(()=>{ updateButtonState();
const interval = setInterval(()=>{
if(calc(dealerCards) < 17){ if(calc(dealerCards) < 17){
dealDealer(false); dealDealer(false);
} else { } else {
clearInterval(cycle); clearInterval(interval);
finishResult(); finishResult();
} }
}, 900); }, 900);
@ -193,31 +250,34 @@ function dealerPlay(){
function finishResult(){ function finishResult(){
gamePhase = 'END'; gamePhase = 'END';
updateButtonState();
const p = calc(playerCards); const p = calc(playerCards);
const d = calc(dealerCards); const d = calc(dealerCards);
const playerBJ = (p === 21 && playerCards.length === 2);
const dealerBJ = (d === 21 && dealerCards.length === 2);
let msg = ''; let msg = '';
if(p > 21) msg = 'PLAYER BUST — YOU LOSE';
if(playerBJ && dealerBJ) msg = 'PUSH — BOTH BLACKJACK';
else if(playerBJ) msg = 'BLACKJACK! YOU WIN (3:2)';
else if(dealerBJ) msg = 'DEALER BLACKJACK — YOU LOSE';
else if(p > 21) msg = 'PLAYER BUST — YOU LOSE';
else if(d > 21) msg = 'DEALER BUST — YOU WIN!'; 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 WIN!';
else if(p < d) msg = 'YOU LOSE!'; else if(p < d) msg = 'YOU LOSE!';
else msg = 'PUSH (DRAW) — TARUHAN KEMBALI'; else msg = 'PUSH (DRAW)';
showEnd(msg);
}
/* ---------- end-screen handling ---------- */
function showEnd(msg){
document.getElementById('status').innerText = msg;
hitBtn.disabled = true;
standBtn.disabled = true;
endMessage.innerText = msg; endMessage.innerText = msg;
endScreen.style.display = 'flex'; endScreen.style.display = 'flex';
playAgainBtn.focus(); document.getElementById('status').innerText = msg;
} }
/* ---------- controls ---------- */
function hit(){ function hit(){
if(gamePhase !== 'PLAYING' || hitBtn.disabled) return; if(gamePhase !== 'PLAYING' || animLock) return;
updateButtonState();
dealPlayer(); dealPlayer();
if(calc(playerCards) > 21){ if(calc(playerCards) > 21){
setTimeout(()=> finishResult(), 500); setTimeout(()=> finishResult(), 500);
@ -225,51 +285,62 @@ function hit(){
} }
function stand(){ function stand(){
if(gamePhase !== 'PLAYING' || standBtn.disabled) return; if(gamePhase !== 'PLAYING' || animLock) return;
gamePhase = 'DEALER_TURN'; gamePhase = 'DEALER_TURN';
hitBtn.disabled = true; updateButtonState();
standBtn.disabled = true;
document.getElementById('status').innerText = 'DEALER TURN'; document.getElementById('status').innerText = 'DEALER TURN';
const hiddenCount = dealerHiddenEls.length;
flipAllDealerHidden(); flipAllDealerHidden();
setTimeout(()=> dealerPlay(), 300 + dealerHiddenEls.length*300); setTimeout(()=> dealerPlay(), 300 + hiddenCount*250);
} }
/* ---------- start / restart ---------- */
function startGame(){ function startGame(){
// disable during dealing to prevent accidental clicks gamePhase = 'DEALING';
hitBtn.disabled = true; updateButtonState();
standBtn.disabled = true;
playerCards = []; dealerCards = []; playerEls.forEach(e=>e.remove()); dealerEls.forEach(e=>e.remove()); // cleanup previous if any
playerEls = []; dealerEls = []; dealerHiddenCards = []; dealerHiddenEls = []; playerCards = [];
dealerCards = [];
playerEls.forEach(e=>e.remove());
dealerEls.forEach(e=>e.remove());
playerEls = [];
dealerEls = [];
dealerHiddenCards = [];
dealerHiddenEls = [];
endScreen.style.display = 'none'; endScreen.style.display = 'none';
// dealing sequence setTimeout(()=>dealPlayer(), 100);
setTimeout(()=> dealPlayer(), 80); setTimeout(()=>dealDealer(false), 350);
setTimeout(()=> dealDealer(false), 280); // dealer first open setTimeout(()=>dealPlayer(), 600);
setTimeout(()=> dealPlayer(), 480); setTimeout(()=>dealDealer(true), 850);
setTimeout(()=> dealDealer(true), 680); // dealer second hidden
// after dealing finished, enable buttons and set phase to PLAYING
setTimeout(()=>{ setTimeout(()=>{
gamePhase = 'PLAYING'; gamePhase = 'PLAYING';
updateButtonState();
document.getElementById('status').innerText = 'YOUR TURN'; document.getElementById('status').innerText = 'YOUR TURN';
hitBtn.disabled = false;
standBtn.disabled = false; const p = calc(playerCards);
// auto-stand on natural const playerBJ = (p === 21 && playerCards.length === 2);
if(calc(playerCards) === 21) stand();
}, 900); if(playerBJ) stand(); // natural blackjack auto-resolve
}, 1000);
} }
function restart(){
// reset and start again
startGame();
}
/* ---------- events ---------- */ /* ===========================================
EVENTS
=========================================== */
hitBtn.addEventListener('click', hit); hitBtn.addEventListener('click', hit);
standBtn.addEventListener('click', stand); standBtn.addEventListener('click', stand);
playAgainBtn.addEventListener('click', restart);
window.addEventListener('resize', ()=>{ layoutOverlap(playerEls, cardsWrapper); layoutOverlap(dealerEls, dealerWrapper); });
/* start immediately */ window.addEventListener('resize', ()=>{
layoutOverlap(playerEls, cardsWrapper);
layoutOverlap(dealerEls, dealerWrapper);
});
/* START GAME ONLY ONCE */
startGame(); startGame();

View File

@ -1,25 +1,19 @@
<?php <?php
ini_set('display_errors', 1);
ini_set('display_startup_errors', 1);
error_reporting(E_ALL);
require_once "../includes/config.php"; require_once "../includes/config.php";
// Top 10 bets $stmt = $conn->prepare("SELECT b.bet, u.username FROM bets b JOIN users u ON b.uid = u.uid ORDER BY b.bet DESC LIMIT 5;");
$stmt = $conn->prepare("SELECT b.bet, u.username FROM bets b JOIN users u ON b.uid = u.uid ORDER BY b.bet DESC LIMIT 10;");
$stmt->execute(); $stmt->execute();
$result = $stmt->get_result(); $result = $stmt->get_result();
$bets = $result->fetch_all(MYSQLI_ASSOC); $bets = $result->fetch_all(MYSQLI_ASSOC);
$stmt->close(); $stmt->close();
// Top 10 wins $stmt = $conn->prepare("SELECT b.win, u.username FROM wins b JOIN users u ON b.uid = u.uid ORDER BY b.win DESC LIMIT 5;");
$stmt = $conn->prepare("SELECT b.win, u.username FROM wins b JOIN users u ON b.uid = u.uid ORDER BY b.win DESC LIMIT 10;");
$stmt->execute(); $stmt->execute();
$result = $stmt->get_result(); $result = $stmt->get_result();
$wins = $result->fetch_all(MYSQLI_ASSOC); $wins = $result->fetch_all(MYSQLI_ASSOC);
$stmt->close(); $stmt->close();
// Top 10 users by money $stmt = $conn->prepare("SELECT * FROM users ORDER BY money DESC LIMIT 5");
$stmt = $conn->prepare("SELECT * FROM users ORDER BY money DESC LIMIT 10");
$stmt->execute(); $stmt->execute();
$result = $stmt->get_result(); $result = $stmt->get_result();
$users = $result->fetch_all(MYSQLI_ASSOC); $users = $result->fetch_all(MYSQLI_ASSOC);
@ -34,7 +28,8 @@
<title>Hit and Run Leaderboard</title> <title>Hit and Run Leaderboard</title>
<link rel="stylesheet" href="assets/css/style.css"> <link rel="stylesheet" href="assets/css/style.css">
</head> </head>
<body style="width: 100%;"> <body>
<div class="main-wrapper">
<div class="container w80"> <div class="container w80">
<div class="header"> <div class="header">
<div class="logo">🂡</div> <div class="logo">🂡</div>
@ -43,7 +38,7 @@
</div> </div>
</div> </div>
<div class="board mb16"> <div class="board mb24">
<div class="panel"> <div class="panel">
<h3>Top Rich Mans</h3> <h3>Top Rich Mans</h3>
<div id="leaderboard-wrap"> <div id="leaderboard-wrap">
@ -55,10 +50,10 @@
<th>Earnings</th> <th>Earnings</th>
</tr> </tr>
</thead> </thead>
<tbody id="board-body"> <tbody>
<?php foreach ($users as $index => $user): ?> <?php foreach ($users as $index => $user): ?>
<tr> <tr>
<td><?= $index + 1 ?></td> <td class="rank"><?= $index + 1 ?></td>
<td><?= htmlspecialchars($user['username']) ?></td> <td><?= htmlspecialchars($user['username']) ?></td>
<td><?= htmlspecialchars($user['money']) ?></td> <td><?= htmlspecialchars($user['money']) ?></td>
</tr> </tr>
@ -69,7 +64,7 @@
</div> </div>
</div> </div>
<div class="board mb16"> <div class="board mb24">
<div class="panel"> <div class="panel">
<h3>Top Crazy Mans</h3> <h3>Top Crazy Mans</h3>
<div id="leaderboard-wrap"> <div id="leaderboard-wrap">
@ -81,10 +76,10 @@
<th>Bets</th> <th>Bets</th>
</tr> </tr>
</thead> </thead>
<tbody id="board-body"> <tbody>
<?php foreach ($bets as $index => $bet): ?> <?php foreach ($bets as $index => $bet): ?>
<tr> <tr>
<td><?= $index + 1 ?></td> <td class="rank"><?= $index + 1 ?></td>
<td><?= htmlspecialchars($bet['username']) ?></td> <td><?= htmlspecialchars($bet['username']) ?></td>
<td><?= htmlspecialchars($bet['bet']) ?></td> <td><?= htmlspecialchars($bet['bet']) ?></td>
</tr> </tr>
@ -95,7 +90,7 @@
</div> </div>
</div> </div>
<div class="board mb16"> <div class="board mb24">
<div class="panel"> <div class="panel">
<h3>Top Lucky Mans</h3> <h3>Top Lucky Mans</h3>
<div id="leaderboard-wrap"> <div id="leaderboard-wrap">
@ -107,10 +102,10 @@
<th>Earnings</th> <th>Earnings</th>
</tr> </tr>
</thead> </thead>
<tbody id="board-body"> <tbody>
<?php foreach ($wins as $index => $win): ?> <?php foreach ($wins as $index => $win): ?>
<tr> <tr>
<td><?= $index + 1 ?></td> <td class="rank"><?= $index + 1 ?></td>
<td><?= htmlspecialchars($win['username']) ?></td> <td><?= htmlspecialchars($win['username']) ?></td>
<td><?= htmlspecialchars($win['win']) ?></td> <td><?= htmlspecialchars($win['win']) ?></td>
</tr> </tr>
@ -120,8 +115,8 @@
</div> </div>
</div> </div>
</div> </div>
<footer>© 2025 Hit Or Run Leaderboard</footer>
</div> </div>
</div>
<footer>© 2025 Hit Or Run by Rafael 5803025007, Stephen 5803025009, Biema 5803025018</footer>
</body> </body>
</html> </html>

View File

@ -4,218 +4,12 @@
<meta charset="utf-8" /> <meta charset="utf-8" />
<meta name="viewport" content="width=device-width,initial-scale=1" /> <meta name="viewport" content="width=device-width,initial-scale=1" />
<title>Hit or Run</title> <title>Hit or Run</title>
<style> <link rel="stylesheet" href="assets/css/style.css">
:root{ </head>
--card-w:110px;
--card-h:154px;
--deck-x: 800px;
--deck-y: 100px;
}
*{box-sizing:border-box}
body{
margin:0;
min-height:100vh;
display:flex;
align-items:center;
justify-content:center;
font-family:Inter,system-ui,Arial;
background:#071827
}
.stage{
width:960px;
height:540px;
border-radius:12px;
overflow:hidden;
position:relative;
box-shadow:0 18px 50px rgba(0,0,0,.6)
}
.bg{
position:absolute;
inset:0; background:#153f28
}
.overlay{
position:absolute; inset:0;
background:linear-gradient(180deg, rgba(11,31,20,.45), rgba(8,24,16,.65));
display:flex; flex-direction:column; padding:18px
}
.header{
color:#dff6e9;
font-weight:700;
display:flex;
justify-content:space-between
}
.table-area{
flex:1; display:flex;
align-items:center;
justify-content:center;
position:relative
}
.deck {
position: absolute;
left: var(--deck-x);
top: var(--deck-y);
width: 82px;
height: 120px;
border-radius: 10px;
background: #113625;
box-shadow: 0 0 0 2px #0d291c inset, 0 10px 20px rgba(0,0,0,0.55);
display:flex; align-items:center; justify-content:center;
color:#b9e4d0;
font-weight:900;
font-size:14px;
letter-spacing:2px;
z-index:500;
}
.player-zone{
position:absolute;
left:37%;
bottom:18px;
transform:translateX(-50%);
width:78%;
display:flex;
flex-direction:column;
align-items:center
}
.cards-wrapper{
position:relative;
width:580px;
height:170px;
display:flex;
justify-content:center;
align-items:center;
}
.dealer-zone{
position:absolute;
left:50%;
top:18px;
transform:translateX(-50%);
width:78%;
display:flex;
flex-direction:column;
align-items:center;
gap:8px;
color:#dff6e9;
font-weight:700
}
.dealer-cards{
position:relative;
width:580px;
height:170px
}
.card-wrapper {
position: absolute;
width: var(--card-w);
height: var(--card-h);
transition: transform .4s cubic-bezier(.68,-0.55,.27,1.55);
align-items: center;
transform-origin: center;
z-index: 100;
}
.card{
width:var(--card-w);
height:var(--card-h);
border-radius:10px;
background:#fff;
display:flex;
flex-direction:column;
justify-content:space-between;
padding:10px;
font-weight:800;
box-shadow:0 10px 22px rgba(0,0,0,.45);
position:absolute;
transition: transform .4s cubic-bezier(.68,-0.55,.27,1.55), opacity .3s ease, filter .3s ease;
backface-visibility:hidden;
transform-origin:center center;
color: #000;
}
.card.is-moving {
filter: drop-shadow(0 0 10px rgba(255, 255, 255, 0.4));
}
.card.red{
color:#c42b2b
}
.card .corner{
font-size:18px
}
.card .center{
font-size:40px;
display:flex;
align-items:center;
justify-content:center;
}
.back-card{
background:#122f21;
display:flex;
align-items:center;
justify-content:center;
font-size:20px;
color:#bddfcf;
letter-spacing:4px;
font-weight:900;
border-radius:10px;
}
.controls{
display:flex;
gap:12px;
margin-top:8px
}
button{
background:#0f5436;
color:#e6fff2;
border:0;
padding:10px 16px;
border-radius:8px;
cursor:pointer;
font-weight:700
}
button:disabled{
background:#2d2d2d;
opacity:.5;
cursor:not-allowed
}
.end-screen {
position: absolute;
inset: 0;
background: rgba(0,0,0,0.7);
backdrop-filter: blur(6px);
display: none;
align-items: center;
justify-content: center;
flex-direction: column;
color: white;
font-weight: 800;
font-size: 48px;
letter-spacing: 2px;
text-align: center;
z-index: 999;
}
.end-screen button {
margin-top: 22px;
font-size: 20px;
padding: 12px 26px;
border: 0;
border-radius: 10px;
background: #18a56d;
color: white;
cursor: pointer;
font-weight: 700;
}
.end-screen button:hover {
background: #1ccf86;
}
</style>
</head></script>
<body> <body>
<div class="stage" id="stage"> <div class="stage" id="stage">
<div class="bg"></div>
<div class="overlay"> <div class="overlay">
<div class="header">
<div>Your Total Card: <strong id="playerTotalUI">0</strong></div>
<div id="status">PLACE YOUR BET</div>
</div>
<div id="deck" class="deck" aria-hidden="true">DECK</div> <div id="deck" class="deck" aria-hidden="true">DECK</div>
<div class="table-area"> <div class="table-area">
<div class="dealer-zone" aria-live="polite"> <div class="dealer-zone" aria-live="polite">
<div class="dealer-label">DEALER</div> <div class="dealer-label">DEALER</div>
@ -224,9 +18,9 @@
</div> </div>
<div class="player-zone"> <div class="player-zone">
<div style="display:flex;justify-content:center;align-items:center;width:100%;color:#dff6e9;font-weight:700;margin-bottom:8px;gap:40px"> <div class="dealer-label">PLAYER</div>
<div class="cards-wrapper" id="cardsWrapper" aria-live="polite"></div> <div class="cards-wrapper" id="cardsWrapper" aria-live="polite"></div>
<div>Total Card: <strong id="playerTotalUI">0</strong></div>
<div class="controls"> <div class="controls">
<button id="stand" disabled>STAND</button> <button id="stand" disabled>STAND</button>
<button id="hit" disabled>HIT</button> <button id="hit" disabled>HIT</button>
@ -236,7 +30,6 @@
<div class="end-screen" id="endScreen" role="dialog" aria-modal="true"> <div class="end-screen" id="endScreen" role="dialog" aria-modal="true">
<div id="endMessage">RESULT</div> <div id="endMessage">RESULT</div>
<button id="playAgainBtn">PLAY AGAIN</button>
</div> </div>
</div> </div>
</div> </div>

View File

@ -6,7 +6,6 @@
exit; exit;
} }
// Get money
$stmt = $conn->prepare("SELECT money FROM users WHERE uid = ?"); $stmt = $conn->prepare("SELECT money FROM users WHERE uid = ?");
$stmt->bind_param("i", $_SESSION['data']['uid']); $stmt->bind_param("i", $_SESSION['data']['uid']);
$stmt->execute(); $stmt->execute();
@ -14,7 +13,6 @@
$user = $result->fetch_assoc(); $user = $result->fetch_assoc();
$stmt->close(); $stmt->close();
// Update session
$_SESSION['data']['money'] = $user['money']; $_SESSION['data']['money'] = $user['money'];
?> ?>
@ -34,6 +32,10 @@
Hit or you Lose. Hit or you Lose.
</div> </div>
<div style="padding-top: 32px; display:flex; flex-direction:column; align-items:center;"> <div style="padding-top: 32px; display:flex; flex-direction:column; align-items:center;">
<div>
Place your bet here
</div>
<div class="num-box"> <div class="num-box">
<button class="btn minus">-</button> <button class="btn minus">-</button>
<input min="1000" max="<?php echo $_SESSION['data']['money']; ?>"type="number" value="1000" style="margin-bottom: -1px;"> <input min="1000" max="<?php echo $_SESSION['data']['money']; ?>"type="number" value="1000" style="margin-bottom: -1px;">
@ -61,5 +63,6 @@
console.log("Startgame"); console.log("Startgame");
</script> </script>
</body> </body>
<footer>© 2025 Hit Or Run by Rafael 5803025007, Stephen 5803025009, Biema 5803025018</footer>
<script src="assets/js/menu.js"></script> <script src="assets/js/menu.js"></script>
</html> </html>

View File

@ -19,7 +19,7 @@
header("Location: home"); header("Location: home");
exit; exit;
} else { } else {
$error = "Wrong Username or Password.";s $error = "Wrong Username or Password.";
} }
} else { } else {
$error = "Username not found."; $error = "Username not found.";
@ -36,6 +36,7 @@
<link rel="stylesheet" href="assets/css/style.css"> <link rel="stylesheet" href="assets/css/style.css">
</head> </head>
<body> <body>
<div class="main-wrapper">
<div class="card"> <div class="card">
<div class="logo">🂡</div> <div class="logo">🂡</div>
<h2>Hit or Run</h2> <h2>Hit or Run</h2>
@ -54,5 +55,7 @@
<div class="note">Belum punya akun? <a href="signup.php">Sign Up!</a></div> <div class="note">Belum punya akun? <a href="signup.php">Sign Up!</a></div>
</div> </div>
</div>
<footer>© 2025 Hit Or Run by Rafael 5803025007, Stephen 5803025009, Biema 5803025018</footer>
</body> </body>
</html> </html>

View File

@ -8,7 +8,6 @@
$username = trim($_POST["username"]); $username = trim($_POST["username"]);
$password = password_hash($_POST["password"], PASSWORD_DEFAULT); $password = password_hash($_POST["password"], PASSWORD_DEFAULT);
// Insert using prepared statement (safe)
$stmt = $conn->prepare("INSERT INTO users (username, password) VALUES (?, ?)"); $stmt = $conn->prepare("INSERT INTO users (username, password) VALUES (?, ?)");
$stmt->bind_param("ss", $username, $password); $stmt->bind_param("ss", $username, $password);
@ -34,6 +33,7 @@
<link rel="stylesheet" href="assets/css/style.css"> <link rel="stylesheet" href="assets/css/style.css">
</head> </head>
<body> <body>
<div class="main-wrapper">
<div class="card"> <div class="card">
<div class="logo">🂡</div> <div class="logo">🂡</div>
<h2>Hit or Run</h2> <h2>Hit or Run</h2>
@ -52,5 +52,7 @@
<div class="note">Sudah punya akun? <a href="signin.php">Sign In!</a></div> <div class="note">Sudah punya akun? <a href="signin.php">Sign In!</a></div>
</div> </div>
</div>
<footer>© 2025 Hit Or Run by Rafael 5803025007, Stephen 5803025009, Biema 5803025018</footer>
</body> </body>
</html> </html>