(function(){ "use strict"; /* ====== STATE ====== */ var G = { status: 'loading', gameId: 0, startTime: 0, duration: 10, speed: 0.10, crashPoint: 0, mult: 1.00, bets: {}, history: [], pollTimer: null, animFrame: null, flyStart: 0, localFlyStart: 0, _alreadyCrashed: false }; var API = 'api/lucky_jet_api.php'; var CS = (typeof window.CS !== 'undefined') ? window.CS : '₹'; /* ====== SAFE HELPERS ====== */ function safeUpdateBal(v){ if (typeof window.updateHeaderBalance === 'function') { try { window.updateHeaderBalance(v); } catch(e){} } } /* ====== TOAST ====== */ function showToast(msg) { var t = document.createElement('div'); t.style.cssText = 'position:fixed;top:20px;left:50%;transform:translateX(-50%);z-index:9999;background:#EF4444;color:#fff;padding:10px 24px;border-radius:10px;font-size:13px;font-weight:700;box-shadow:0 4px 20px rgba(0,0,0,.4);pointer-events:none;text-align:center;max-width:320px;'; t.textContent = msg; document.body.appendChild(t); setTimeout(function(){ t.style.transition='opacity .3s'; t.style.opacity='0'; setTimeout(function(){t.remove();},300); }, 2500); } /* ====== CANVAS ====== */ var cvs, ctx, W, H; var jetImg, fireImg; var jetLoaded = false, fireLoaded = false; function initCanvas() { cvs = document.getElementById('ljCvs'); if (!cvs) return; ctx = cvs.getContext('2d'); resize(); window.addEventListener('resize', resize); jetImg = document.getElementById('_ji'); fireImg = document.getElementById('_fi'); if (jetImg && jetImg.complete && jetImg.naturalWidth > 0) jetLoaded = true; else if (jetImg) jetImg.onload = function(){ jetLoaded = true; }; if (fireImg && fireImg.complete && fireImg.naturalWidth > 0) fireLoaded = true; else if (fireImg) fireImg.onload = function(){ fireLoaded = true; }; } function resize() { if (!cvs || !ctx) return; var r = cvs.parentElement.getBoundingClientRect(); var dpr = window.devicePixelRatio || 1; cvs.width = r.width * dpr; cvs.height = r.height * dpr; cvs.style.width = r.width + 'px'; cvs.style.height = r.height + 'px'; ctx.setTransform(dpr, 0, 0, dpr, 0, 0); W = r.width; H = r.height; } function clearCanvas() { if(ctx) ctx.clearRect(0,0,W,H); } function drawFlying() { if (!ctx) return; clearCanvas(); var padL=2, padB=2, padT=15, padR=12; var gW = W - padL - padR, gH = H - padT - padB; var originX = padL, originY = H - padB; var now = Date.now()/1000; var jSize = 74; var hoverX = W*0.60, hoverY = H*0.38; var jetX, jetY; if (G.localFlyStart) { var introT = Math.min(1, (now - G.localFlyStart)/0.65); var ease = 1 - Math.pow(1-introT,3); jetX = originX + (hoverX-originX)*ease; jetY = originY + (hoverY-originY)*ease; if (introT >= 1) jetY = hoverY + Math.sin(now*4.5)*7; } else { jetX = originX; jetY = originY; } var tJet = Math.max(0, Math.min(1, (jetX-originX)/gW)); var steps = 80, curveEndX = originX, curveEndY = originY, i, t, x, yN; for (i=1;i<=steps;i++){ t = (i/steps)*tJet; curveEndX = originX + t*gW; yN = Math.pow(t, 0.68); curveEndY = originY - yN*gH*0.85; } var grad = ctx.createLinearGradient(0, originY, 0, padT); grad.addColorStop(0,'rgba(126,58,242,0.03)'); grad.addColorStop(1,'rgba(126,58,242,0.22)'); ctx.beginPath(); ctx.moveTo(originX, originY); for (i=1;i<=steps;i++){ t=(i/steps)*tJet; x=originX+t*gW; yN=Math.pow(t,0.68); ctx.lineTo(x, originY - yN*gH*0.85); } ctx.lineTo(curveEndX, originY); ctx.closePath(); ctx.fillStyle = grad; ctx.fill(); ctx.beginPath(); ctx.moveTo(originX, originY); for (i=1;i<=steps;i++){ t=(i/steps)*tJet; x=originX+t*gW; yN=Math.pow(t,0.68); ctx.lineTo(x, originY - yN*gH*0.85); } ctx.strokeStyle = '#A78BFA'; ctx.lineWidth = 2.5; ctx.shadowColor = '#7E3AF2'; ctx.shadowBlur = 14; ctx.stroke(); ctx.shadowBlur = 0; if (jetLoaded && window.isAnimOn) { var ff1 = 0.80 + 0.20*Math.abs(Math.sin(now*10)); var ff2 = 0.85 + 0.15*Math.abs(Math.cos(now*13)); var fireW = 44*ff1, fireH = 30*ff2; ctx.save(); ctx.translate(jetX, jetY); if (fireLoaded) { ctx.save(); ctx.globalAlpha = 0.75 + 0.25*ff1; ctx.translate(-jSize*0.30, -jSize*0.15); ctx.rotate(2.50); ctx.drawImage(fireImg, -fireW*0.15, -fireH*0.45, fireW*1.2, fireH*1.2); ctx.restore(); } ctx.save(); ctx.rotate(-0.12); ctx.drawImage(jetImg, -jSize*0.35, -jSize*0.62, jSize, jSize); ctx.restore(); ctx.restore(); } else { ctx.beginPath(); ctx.arc(jetX, jetY, 13, 0, Math.PI*2); ctx.fillStyle = '#A78BFA'; ctx.fill(); ctx.fillStyle = '#fff'; ctx.font = 'bold 10px Montserrat'; ctx.textAlign='center'; ctx.textBaseline='middle'; ctx.fillText('JET', jetX, jetY); } } /* ====== HISTORY ====== */ function pillClass(v){ if(v>=10) return 'lj-pill-gold'; if(v>=2) return 'lj-pill-purple'; return 'lj-pill-blue'; } function renderHistory(arr){ if (!arr || !arr.length) return; G.history = arr; var strip = document.getElementById('ljPills'); var grid = document.getElementById('ljHistGrid'); var h = ''; arr.forEach(function(v){ h += '
'+v.toFixed(2)+'x
'; }); if (strip) strip.innerHTML = h; if (grid) grid.innerHTML = h; } /* ====== BET UI ====== */ function showBtn(panel, which) { ['Bet','Cancel','Wait','Wd'].forEach(function(b){ var el = document.getElementById('ljBtn'+b+panel); if (el) el.style.display = 'none'; }); var map = {bet:'Bet', cancel:'Cancel', wait:'Wait', withdraw:'Wd'}; var el = document.getElementById('ljBtn'+(map[which]||'Bet')+panel); if (el) el.style.display = 'flex'; } function updateWdAmount(panel, amount) { var el = document.getElementById('ljWA'+panel); if (el) el.innerHTML = amount.toFixed(2) + ' ' + CS; } /* FIX: don't delete local bet that server hasn't ack'd yet */ function restoreBets(serverBets) { var mapped = {}; if (serverBets && serverBets.length) { serverBets.forEach(function(b){ var sid = b.section_id || 1; mapped[sid] = { bet_id: b.bet_id, amount: parseFloat(b.amount), game_id: parseInt(b.game_id,10) || 0 }; }); } [1,2].forEach(function(p){ if (mapped[p]) { G.bets[p] = mapped[p]; } else if (G.bets[p] && G.bets[p]._pending) { // keep pending bet until server ack } else { delete G.bets[p]; } }); } /* ====== FAKE ALL-BETS ====== */ var fakeColors = ['#e11d48','#d97706','#0891b2','#16a34a','#7c3aed','#9333ea','#be123c','#b45309']; function rName(n){ var r='',c='ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'; for(var i=0;i 50) allBetsData.pop(); renderFakeBets(); } function startFakeBetPlacement(){ stopFakeTimers(); fakeAddTimer = setInterval(function(){ if (G.status !== 'loading') { clearInterval(fakeAddTimer); fakeAddTimer=null; return; } addOneFakeBet(); }, 400 + Math.floor(Math.random()*500)); } function simulateFakeCashouts(){ if (G.status !== 'flying') return; var changed = false; allBetsData.forEach(function(p){ if (!p.is_won && G.mult >= p.target_x) { p.is_won = true; p.cashout_x = p.target_x; p.win = p.bet * p.target_x; changed = true; } }); if (changed) renderFakeBets(); } function startFakeCashouts(){ if (fakeCashTimer) clearInterval(fakeCashTimer); fakeCashTimer = setInterval(function(){ if (G.status !== 'flying') { clearInterval(fakeCashTimer); fakeCashTimer=null; return; } simulateFakeCashouts(); }, 200); } function stopFakeTimers(){ if (fakeAddTimer){ clearInterval(fakeAddTimer); fakeAddTimer=null; } if (fakeCashTimer){ clearInterval(fakeCashTimer); fakeCashTimer=null; } } function renderFakeBets(){ var list = document.getElementById('allBetsList'); var countEl = document.getElementById('ljTotalBets'); if (!list) return; if (countEl) countEl.textContent = fakeRoundTotal.toLocaleString(); var h = ''; allBetsData.forEach(function(p){ var pCls = p.is_won ? pillClass(p.cashout_x) : 'lj-pill-blue'; var multTxt = p.is_won ? p.cashout_x.toFixed(2)+'x' : '0.00x'; var winTxt = p.is_won ? p.win.toFixed(2)+' '+CS : '-'; var winCls = p.is_won ? '' : ' none'; h += '
' + '
'+p.name.substring(0,2)+'
'+p.name+'
' + '
'+p.bet.toFixed(2)+'
' + '
'+multTxt+'
' + '
'+winTxt+'
'; }); list.innerHTML = h; } var _lastFakeState = ''; setInterval(function(){ if (G.status==='loading' && _lastFakeState!=='loading'){ generateInitialFakeBets(); startFakeBetPlacement(); } if (G.status==='flying' && _lastFakeState!=='flying') { stopFakeTimers(); startFakeCashouts(); } if (G.status==='crashed' && _lastFakeState!=='crashed'){ stopFakeTimers(); renderFakeBets(); } _lastFakeState = G.status; }, 500); /* ====== WIN POPUP ====== */ function showWinPop(amount, mult){ var box = document.getElementById('ljPops'); if (!box) return; var div = document.createElement('div'); div.className = 'lj-pop'; div.innerHTML = '
Withdrawal at
x'+mult.toFixed(2)+'
'+amount.toFixed(2)+' '+CS+'
You Win!
'; box.appendChild(div); setTimeout(function(){ if(div.parentNode) div.parentNode.removeChild(div); }, 3200); if (window.isSoundOn){ var sn = document.getElementById('sWn'); if(sn){ sn.currentTime=0; sn.play().catch(function(){}); } } } /* ====== ACTIONS ====== */ window.ljAction = function(panel, type){ if (type === 'bet') { var amt = parseFloat(document.getElementById('ljA'+panel).value) || 8; if (amt < 8) amt = 8; if (amt > 25000) amt = 25000; // optimistic pending bet so restoreBets won't wipe it before server ack G.bets[panel] = { bet_id:0, amount:amt, game_id:0, _pending:true }; $.post(API, {action:'bet', amount:amt, section_id:panel}, function(r){ if (r.status==='success') { G.bets[panel] = {bet_id:r.bet_id, amount:amt, game_id:r.game_id}; safeUpdateBal(r.new_balance); if (G.status === 'flying' && r.game_id === 0) showBtn(panel,'wait'); else showBtn(panel,'cancel'); } else { delete G.bets[panel]; showBtn(panel,'bet'); showToast(r.message==='insufficient_balance' ? 'Insufficient balance!' : (r.message||'Bet failed')); } }, 'json').fail(function(xhr){ delete G.bets[panel]; showBtn(panel,'bet'); try { var r=JSON.parse(xhr.responseText); showToast(r.message==='insufficient_balance'?'Insufficient balance!':(r.message||'Bet failed')); } catch(e){ showToast('Network error'); } }); } else if (type === 'cancel') { var b = G.bets[panel]; if (!b || !b.bet_id) return; $.post(API, {action:'cancel_bet', bet_id:b.bet_id}, function(r){ if (r.status==='success'){ delete G.bets[panel]; safeUpdateBal(r.new_balance); showBtn(panel,'bet'); } else { showToast(r.message||'Cancel failed'); } }, 'json').fail(function(xhr){ try { var r=JSON.parse(xhr.responseText); showToast(r.message||'Cancel failed'); } catch(e){ showToast('Network error'); } }); } else if (type === 'withdraw') { var b2 = G.bets[panel]; if (!b2 || !b2.bet_id || b2._cashing) return; b2._cashing = true; $.post(API, {action:'cashout', bet_id:b2.bet_id}, function(r){ if (r.status==='success'){ showWinPop(parseFloat(r.win_amount), parseFloat(r.multiplier)); delete G.bets[panel]; safeUpdateBal(r.new_balance); showBtn(panel,'bet'); } else { if (b2) b2._cashing = false; if (r.message!=='crashed' && r.message!=='not_flying') showToast(r.message||'Cashout failed'); } }, 'json').fail(function(xhr){ if (b2) b2._cashing = false; try { var r=JSON.parse(xhr.responseText); showToast(r.message||'Cashout failed'); } catch(e){ showToast('Network error'); } }); } }; /* ====== POLL ====== */ var prevStatus = ''; function poll(){ $.post(API, {action:'check_game'}, function(r){ if (r && r.status){ G.status = r.status; G.gameId = r.game_id; G.startTime = r.start_time; G.duration = r.duration; G.speed = r.speed; G.crashPoint = r.crash_point; if (r.history) renderHistory(r.history); restoreBets(r.my_bets || []); if (r.status==='loading') handleLoading(r); else if (r.status==='flying') handleFlying(r); else if (r.status==='crashed') handleCrashed(r); prevStatus = r.status; } if (r && r.new_balance !== undefined) safeUpdateBal(r.new_balance); }, 'json').always(function(){ G.pollTimer = setTimeout(poll, 1000); }); } function handleLoading(r){ window._ljRoundStatus = 'loading'; if (typeof window.syncBgMusic === 'function') window.syncBgMusic(); G._alreadyCrashed = false; $('#ljLoader').show(); $('#ljMult').hide(); $('#ljFlew').hide(); clearCanvas(); var pct = Math.min(100, ((r.elapsed||0) / r.duration) * 100); $('#ljBar').css('width', pct+'%'); [1,2].forEach(function(p){ if (G.bets[p]) showBtn(p, G.bets[p]._pending ? 'cancel' : 'cancel'); else showBtn(p,'bet'); }); [1,2].forEach(function(p){ var ab = document.getElementById('ljAb'+p); if (ab && ab.checked && !G.bets[p] && prevStatus === 'crashed') ljAction(p,'bet'); }); } function handleFlying(r){ window._ljRoundStatus = 'flying'; if (typeof window.syncBgMusic === 'function') window.syncBgMusic(); $('#ljLoader').hide(); $('#ljFlew').hide(); $('#ljMult').show(); if (prevStatus === 'loading' || prevStatus === '') { var serverElapsed = Math.max(0, (Date.now()/1000) - r.start_time); if (prevStatus === '' && serverElapsed > 1) { G.localFlyStart = Date.now()/1000 - 10; } else { G.localFlyStart = Date.now()/1000; } if (window.isSoundOn && prevStatus === 'loading') { var s = document.getElementById('sSt'); if(s){ s.currentTime=0; s.play().catch(function(){}); } } } var elapsed = Math.max(0, (Date.now()/1000) - r.start_time); G.mult = Math.exp(G.speed * elapsed); if (G.crashPoint > 0 && G.mult >= G.crashPoint) G.mult = G.crashPoint; $('#ljMult').text('x' + G.mult.toFixed(2)); drawFlying(); [1,2].forEach(function(p){ var b = G.bets[p]; if (b){ if (b.game_id == G.gameId){ var wa = b.amount * G.mult; updateWdAmount(p, wa); showBtn(p,'withdraw'); var ac = document.getElementById('ljAc'+p); var acI = document.getElementById('ljAcI'+p); if (ac && ac.checked && acI && !b._autoTried){ var target = parseFloat(acI.value) || 2; if (G.mult >= target){ b._autoTried = true; ljAction(p,'withdraw'); } } } else { showBtn(p,'wait'); } } else { showBtn(p,'bet'); } }); if (G.animFrame) cancelAnimationFrame(G.animFrame); flyAnimate(); } function flyAnimate(){ if (G.status !== 'flying') return; var elapsed = Math.max(0, (Date.now()/1000) - G.startTime); G.mult = Math.exp(G.speed * elapsed); if (G.crashPoint > 0 && G.mult >= G.crashPoint){ if (G._alreadyCrashed) return; G._alreadyCrashed = true; G.mult = G.crashPoint; G.status = 'crashed'; window._ljRoundStatus = 'crashed'; if (typeof window.syncBgMusic === 'function') window.syncBgMusic(); if (G.animFrame){ cancelAnimationFrame(G.animFrame); G.animFrame=null; } G.localFlyStart = 0; $('#ljLoader').hide(); $('#ljMult').hide(); $('#ljFlew').show(); $('#ljFlewM').text('x' + G.crashPoint.toFixed(2)); clearCanvas(); if (window.isSoundOn){ var sn = document.getElementById('sAw'); if(sn){ sn.currentTime=0; sn.play().catch(function(){}); } } [1,2].forEach(function(p){ if (G.bets[p] && G.bets[p].game_id == G.gameId) delete G.bets[p]; showBtn(p,'bet'); }); return; } $('#ljMult').text('x' + G.mult.toFixed(2)); drawFlying(); [1,2].forEach(function(p){ if (G.bets[p] && G.bets[p].game_id == G.gameId){ updateWdAmount(p, G.bets[p].amount * G.mult); } }); G.animFrame = requestAnimationFrame(flyAnimate); } function handleCrashed(r){ window._ljRoundStatus = 'crashed'; if (typeof window.syncBgMusic === 'function') window.syncBgMusic(); if (G.animFrame){ cancelAnimationFrame(G.animFrame); G.animFrame=null; } G.localFlyStart = 0; $('#ljLoader').hide(); $('#ljMult').hide(); $('#ljFlew').show(); $('#ljFlewM').text('x' + r.crash_point.toFixed(2)); clearCanvas(); if (prevStatus === 'flying' && !G._alreadyCrashed && window.isSoundOn){ var sn = document.getElementById('sAw'); if(sn){ sn.currentTime=0; sn.play().catch(function(){}); } } G._alreadyCrashed = true; [1,2].forEach(function(p){ if (G.bets[p] && G.bets[p].game_id == G.gameId) delete G.bets[p]; showBtn(p,'bet'); }); } /* ====== INIT ====== */ $(function(){ initCanvas(); generateInitialFakeBets(); showBtn(1,'bet'); showBtn(2,'bet'); poll(); }); })();