footer fixed, games small fix
This commit is contained in:
parent
e0623117d7
commit
ae42c7248e
@ -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>
|
||||||
|
|||||||
@ -1,21 +1,35 @@
|
|||||||
body, html{
|
html, body {
|
||||||
|
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;
|
font-family: Inter, ui-sans-serif, system-ui, -apple-system, 'Segoe UI', Roboto, 'Helvetica Neue', Arial;
|
||||||
margin:0;
|
|
||||||
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 {
|
||||||
display:flex;
|
flex: 1;
|
||||||
align-items:center;
|
display: flex;
|
||||||
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 */
|
||||||
@ -268,4 +282,195 @@ input::-webkit-inner-spin-button {
|
|||||||
input[type="number"] {
|
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;
|
||||||
|
}
|
||||||
|
|||||||
@ -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;
|
||||||
list.forEach((el, i)=>{
|
const startY = (wrapper.clientHeight - cardH) / 2;
|
||||||
|
|
||||||
|
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 = '';
|
||||||
}
|
}
|
||||||
},420);
|
animLock = false;
|
||||||
|
updateButtonState();
|
||||||
|
}, 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,83 +250,97 @@ 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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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();
|
||||||
|
|||||||
175
public/board.php
175
public/board.php
@ -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,94 +28,95 @@
|
|||||||
<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="container w80">
|
<div class="main-wrapper">
|
||||||
<div class="header">
|
<div class="container w80">
|
||||||
<div class="logo">🂡</div>
|
<div class="header">
|
||||||
<div class="title">
|
<div class="logo">🂡</div>
|
||||||
<h2>Hit Or Run — Leaderboard</h2>
|
<div class="title">
|
||||||
|
<h2>Hit Or Run — Leaderboard</h2>
|
||||||
|
</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">
|
||||||
<table id="leaderboard">
|
<table id="leaderboard">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
|
||||||
<th class="rank">#</th>
|
|
||||||
<th>Player</th>
|
|
||||||
<th>Earnings</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody id="board-body">
|
|
||||||
<?php foreach ($users as $index => $user): ?>
|
|
||||||
<tr>
|
<tr>
|
||||||
<td><?= $index + 1 ?></td>
|
<th class="rank">#</th>
|
||||||
<td><?= htmlspecialchars($user['username']) ?></td>
|
<th>Player</th>
|
||||||
<td><?= htmlspecialchars($user['money']) ?></td>
|
<th>Earnings</th>
|
||||||
</tr>
|
</tr>
|
||||||
<?php endforeach; ?>
|
</thead>
|
||||||
</tbody>
|
<tbody>
|
||||||
</table>
|
<?php foreach ($users as $index => $user): ?>
|
||||||
|
<tr>
|
||||||
|
<td class="rank"><?= $index + 1 ?></td>
|
||||||
|
<td><?= htmlspecialchars($user['username']) ?></td>
|
||||||
|
<td><?= htmlspecialchars($user['money']) ?></td>
|
||||||
|
</tr>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="board mb24">
|
||||||
|
<div class="panel">
|
||||||
|
<h3>Top Crazy Mans</h3>
|
||||||
|
<div id="leaderboard-wrap">
|
||||||
|
<table id="leaderboard">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th class="rank">#</th>
|
||||||
|
<th>Player</th>
|
||||||
|
<th>Bets</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<?php foreach ($bets as $index => $bet): ?>
|
||||||
|
<tr>
|
||||||
|
<td class="rank"><?= $index + 1 ?></td>
|
||||||
|
<td><?= htmlspecialchars($bet['username']) ?></td>
|
||||||
|
<td><?= htmlspecialchars($bet['bet']) ?></td>
|
||||||
|
</tr>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="board mb24">
|
||||||
|
<div class="panel">
|
||||||
|
<h3>Top Lucky Mans</h3>
|
||||||
|
<div id="leaderboard-wrap">
|
||||||
|
<table id="leaderboard">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th class="rank">#</th>
|
||||||
|
<th>Player</th>
|
||||||
|
<th>Earnings</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<?php foreach ($wins as $index => $win): ?>
|
||||||
|
<tr>
|
||||||
|
<td class="rank"><?= $index + 1 ?></td>
|
||||||
|
<td><?= htmlspecialchars($win['username']) ?></td>
|
||||||
|
<td><?= htmlspecialchars($win['win']) ?></td>
|
||||||
|
</tr>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="board mb16">
|
|
||||||
<div class="panel">
|
|
||||||
<h3>Top Crazy Mans</h3>
|
|
||||||
<div id="leaderboard-wrap">
|
|
||||||
<table id="leaderboard">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th class="rank">#</th>
|
|
||||||
<th>Player</th>
|
|
||||||
<th>Bets</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody id="board-body">
|
|
||||||
<?php foreach ($bets as $index => $bet): ?>
|
|
||||||
<tr>
|
|
||||||
<td><?= $index + 1 ?></td>
|
|
||||||
<td><?= htmlspecialchars($bet['username']) ?></td>
|
|
||||||
<td><?= htmlspecialchars($bet['bet']) ?></td>
|
|
||||||
</tr>
|
|
||||||
<?php endforeach; ?>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="board mb16">
|
|
||||||
<div class="panel">
|
|
||||||
<h3>Top Lucky Mans</h3>
|
|
||||||
<div id="leaderboard-wrap">
|
|
||||||
<table id="leaderboard">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th class="rank">#</th>
|
|
||||||
<th>Player</th>
|
|
||||||
<th>Earnings</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody id="board-body">
|
|
||||||
<?php foreach ($wins as $index => $win): ?>
|
|
||||||
<tr>
|
|
||||||
<td><?= $index + 1 ?></td>
|
|
||||||
<td><?= htmlspecialchars($win['username']) ?></td>
|
|
||||||
<td><?= htmlspecialchars($win['win']) ?></td>
|
|
||||||
</tr>
|
|
||||||
<?php endforeach; ?>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<footer>© 2025 Hit Or Run Leaderboard</footer>
|
|
||||||
</div>
|
</div>
|
||||||
|
<footer>© 2025 Hit Or Run by Rafael 5803025007, Stephen 5803025009, Biema 5803025018</footer>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
273
public/game.php
273
public/game.php
@ -1,247 +1,40 @@
|
|||||||
<!doctype html>
|
<!doctype html>
|
||||||
<html lang="id">
|
<html lang="id">
|
||||||
<head>
|
<head>
|
||||||
<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;
|
<body>
|
||||||
--card-h:154px;
|
<div class="stage" id="stage">
|
||||||
--deck-x: 800px;
|
<div class="overlay">
|
||||||
--deck-y: 100px;
|
<div id="deck" class="deck" aria-hidden="true">DECK</div>
|
||||||
}
|
<div class="table-area">
|
||||||
*{box-sizing:border-box}
|
<div class="dealer-zone" aria-live="polite">
|
||||||
body{
|
<div class="dealer-label">DEALER</div>
|
||||||
margin:0;
|
<div class="dealer-cards" id="dealerWrapper" aria-hidden="false"></div>
|
||||||
min-height:100vh;
|
<div class="dealer-label">Total Dealer: <span id="dealerTotalUI">0</span></div>
|
||||||
display:flex;
|
</div>
|
||||||
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>
|
|
||||||
<div class="stage" id="stage">
|
|
||||||
<div class="bg"></div>
|
|
||||||
<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 class="player-zone">
|
||||||
|
<div class="dealer-label">PLAYER</div>
|
||||||
|
<div class="cards-wrapper" id="cardsWrapper" aria-live="polite"></div>
|
||||||
|
<div>Total Card: <strong id="playerTotalUI">0</strong></div>
|
||||||
|
<div class="controls">
|
||||||
|
<button id="stand" disabled>STAND</button>
|
||||||
|
<button id="hit" disabled>HIT</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="table-area">
|
<div class="end-screen" id="endScreen" role="dialog" aria-modal="true">
|
||||||
<div class="dealer-zone" aria-live="polite">
|
<div id="endMessage">RESULT</div>
|
||||||
<div class="dealer-label">DEALER</div>
|
</div>
|
||||||
<div class="dealer-cards" id="dealerWrapper" aria-hidden="false"></div>
|
</div>
|
||||||
<div class="dealer-label">Total Dealer: <span id="dealerTotalUI">0</span></div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<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="cards-wrapper" id="cardsWrapper" aria-live="polite"></div>
|
|
||||||
|
|
||||||
<div class="controls">
|
|
||||||
<button id="stand" disabled>STAND</button>
|
|
||||||
<button id="hit" disabled>HIT</button>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="end-screen" id="endScreen" role="dialog" aria-modal="true">
|
<script src="assets/js/game.js"></script>
|
||||||
<div id="endMessage">RESULT</div>
|
</body>
|
||||||
<button id="playAgainBtn">PLAY AGAIN</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<script src="assets/js/game.js"></script>
|
|
||||||
</body>
|
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@ -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>
|
||||||
|
|||||||
@ -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,23 +36,26 @@
|
|||||||
<link rel="stylesheet" href="assets/css/style.css">
|
<link rel="stylesheet" href="assets/css/style.css">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div class="card">
|
<div class="main-wrapper">
|
||||||
<div class="logo">🂡</div>
|
<div class="card">
|
||||||
<h2>Hit or Run</h2>
|
<div class="logo">🂡</div>
|
||||||
|
<h2>Hit or Run</h2>
|
||||||
|
|
||||||
<?php if (!empty($error)) echo "<p style='color:red;'>$error</p>"; ?>
|
<?php if (!empty($error)) echo "<p style='color:red;'>$error</p>"; ?>
|
||||||
|
|
||||||
<form action="" method="post">
|
<form action="" method="post">
|
||||||
<label>Username</label>
|
<label>Username</label>
|
||||||
<input type="text" id="username" name="username" placeholder="Masukkan username" />
|
<input type="text" id="username" name="username" placeholder="Masukkan username" />
|
||||||
|
|
||||||
<label>Password</label>
|
<label>Password</label>
|
||||||
<input type="password" id="password" name="password" placeholder="Masukkan password" />
|
<input type="password" id="password" name="password" placeholder="Masukkan password" />
|
||||||
|
|
||||||
<button type="submit">Sign In</button>
|
<button type="submit">Sign In</button>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
<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>
|
||||||
@ -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,23 +33,26 @@
|
|||||||
<link rel="stylesheet" href="assets/css/style.css">
|
<link rel="stylesheet" href="assets/css/style.css">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div class="card">
|
<div class="main-wrapper">
|
||||||
<div class="logo">🂡</div>
|
<div class="card">
|
||||||
<h2>Hit or Run</h2>
|
<div class="logo">🂡</div>
|
||||||
|
<h2>Hit or Run</h2>
|
||||||
|
|
||||||
<?php if (!empty($error)) echo "<p style='color:red;'>$error</p>"; ?>
|
<?php if (!empty($error)) echo "<p style='color:red;'>$error</p>"; ?>
|
||||||
|
|
||||||
<form action="" method="post">
|
<form action="" method="post">
|
||||||
<label>Username</label>
|
<label>Username</label>
|
||||||
<input type="text" id="username" name="username" placeholder="Masukkan username" />
|
<input type="text" id="username" name="username" placeholder="Masukkan username" />
|
||||||
|
|
||||||
<label>Password</label>
|
<label>Password</label>
|
||||||
<input type="password" id="password" name="password" placeholder="Masukkan password" />
|
<input type="password" id="password" name="password" placeholder="Masukkan password" />
|
||||||
|
|
||||||
<button type="submit">Sign Up</button>
|
<button type="submit">Sign Up</button>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
<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>
|
||||||
Loading…
x
Reference in New Issue
Block a user