520 lines
26 KiB
HTML
520 lines
26 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="ru">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
<title>Crash - CS2 Simulator</title>
|
||
<link rel="stylesheet" href="/static/css/style.css">
|
||
<style>
|
||
.crash-layout { max-width: 960px; margin: 0 auto; display: flex; flex-direction: column; gap: 1rem; }
|
||
.crash-canvas-container {
|
||
background: var(--bg-card);
|
||
border: 1px solid var(--border-color);
|
||
border-radius: 12px;
|
||
padding: 1rem;
|
||
position: relative;
|
||
overflow: hidden;
|
||
}
|
||
#crashCanvas { width: 100%; height: 350px; display: block; border-radius: 8px; }
|
||
.crash-overlay {
|
||
position: absolute; top: 0; left: 0; right: 0; bottom: 0;
|
||
display: flex; flex-direction: column; align-items: center; justify-content: center;
|
||
pointer-events: none;
|
||
}
|
||
.crash-multiplier { font-size: 4rem; font-weight: 800; text-shadow: 0 0 40px rgba(34,197,94,0.5); transition: all 0.1s; }
|
||
.crash-multiplier.crashed { color: #ef4444; text-shadow: 0 0 40px rgba(239,68,68,0.5); animation: crashShake 0.5s ease; }
|
||
.crash-multiplier.waiting { font-size: 2rem; color: var(--text-secondary); text-shadow: none; }
|
||
.crash-multiplier.betting { font-size: 3rem; color: var(--primary-color); text-shadow: 0 0 30px rgba(59,130,246,0.3); }
|
||
@keyframes crashShake {
|
||
0%,100%{transform:translateX(0)}10%{transform:translateX(-10px)rotate(-2deg)}30%{transform:translateX(10px)rotate(2deg)}50%{transform:translateX(-8px)rotate(-1deg)}70%{transform:translateX(8px)rotate(1deg)}90%{transform:translateX(-4px)}
|
||
}
|
||
.crash-status-text { font-size: 1rem; color: var(--text-secondary); margin-top: 0.5rem; }
|
||
.crash-controls { display: grid; grid-template-columns: 1fr 1fr; gap: 1rem; }
|
||
.crash-bet-panel { background: var(--bg-card); border: 1px solid var(--border-color); border-radius: 12px; padding: 1.5rem; }
|
||
.crash-players-panel { background: var(--bg-card); border: 1px solid var(--border-color); border-radius: 12px; padding: 1.5rem; max-height: 300px; overflow-y: auto; }
|
||
.crash-bet-input { display: flex; gap: 0.5rem; margin-bottom: 1rem; }
|
||
.crash-bet-input input { flex: 1; padding: 0.75rem; background: var(--bg-dark); border: 1px solid var(--border-color); border-radius: 8px; color: var(--text-color); font-size: 1.1rem; }
|
||
.quick-bet-btns { display: grid; grid-template-columns: repeat(4,1fr); gap: 0.5rem; margin-bottom: 1rem; }
|
||
.quick-bet-btn { padding: 0.5rem; background: var(--bg-dark); border: 1px solid var(--border-color); border-radius: 6px; color: var(--text-color); cursor: pointer; font-size: 0.85rem; transition: all 0.2s; }
|
||
.quick-bet-btn:hover { border-color: var(--primary-color); }
|
||
.crash-btn { width: 100%; padding: 0.75rem; border: none; border-radius: 8px; font-size: 1rem; font-weight: 600; cursor: pointer; transition: all 0.2s; }
|
||
.crash-btn.bet { background: var(--primary-color); color: white; }
|
||
.crash-btn.bet:hover { filter: brightness(1.1); }
|
||
.crash-btn.bet.placed { background: #6366f1; }
|
||
.crash-btn.cashout { background: var(--success-color); color: white; }
|
||
.crash-btn.cashout:hover { filter: brightness(1.1); }
|
||
.crash-btn:disabled { opacity: 0.5; cursor: not-allowed; }
|
||
.crash-btn.hidden { display: none; }
|
||
.player-row { display: flex; justify-content: space-between; align-items: center; padding: 0.4rem 0; border-bottom: 1px solid var(--border-color); font-size: 0.85rem; }
|
||
.player-row:last-child { border-bottom: none; }
|
||
.player-row .payout { color: var(--success-color); font-weight: 600; }
|
||
.player-row .bet-amount { color: var(--text-secondary); }
|
||
.crash-history { display: flex; gap: 0.4rem; flex-wrap: wrap; padding: 0.5rem 0; }
|
||
.crash-history-item { padding: 0.2rem 0.5rem; border-radius: 4px; font-size: 0.75rem; font-weight: 600; }
|
||
.crash-history-item.win { background: rgba(34,197,94,0.15); color: var(--success-color); }
|
||
.crash-history-item.loss { background: rgba(239,68,68,0.15); color: #ef4444; }
|
||
.cashout-progress { margin-top: 0.75rem; display: none; }
|
||
.cashout-progress.active { display: block; }
|
||
.cashout-slider { width: 100%; height: 6px; background: var(--bg-dark); border-radius: 3px; position: relative; overflow: hidden; }
|
||
.cashout-slider-fill { height: 100%; background: linear-gradient(90deg, var(--success-color), #f59e0b, #ef4444); border-radius: 3px; transition: width 0.1s; }
|
||
.cashout-label { display: flex; justify-content: space-between; font-size: 0.75rem; color: var(--text-secondary); margin-top: 0.25rem; }
|
||
.crash-my-bet { margin-top: 0.5rem; padding: 0.5rem; background: var(--bg-dark); border-radius: 6px; text-align: center; font-size: 0.85rem; }
|
||
.crash-my-bet .bet-highlight { color: var(--primary-color); font-weight: 600; }
|
||
.crash-my-bet .win-highlight { color: var(--success-color); font-weight: 600; }
|
||
.crash-my-bet .lose-highlight { color: #ef4444; font-weight: 600; }
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<nav class="navbar">
|
||
<div class="container">
|
||
<a href="/" class="logo">🎮 CS2 Simulator</a>
|
||
<div class="nav-links">
|
||
<a href="/cases" class="nav-link">Кейсы</a>
|
||
<a href="/contracts" class="nav-link">Контракты</a>
|
||
<a href="/upgrade" class="nav-link">🆙 Апгрейд</a>
|
||
<a href="/crash" class="nav-link active">💥 Crash</a>
|
||
<a href="/profile" class="nav-link">Профиль</a>
|
||
<a href="/achievements" class="nav-link">🏆 Достижения</a>
|
||
<a href="/activity" class="nav-link">📰 Лента</a>
|
||
<a href="/profile" class="nav-link">Профиль</a>
|
||
<span class="user-balance">{{ "%.0f"|format(user.balance) }} ₽</span>
|
||
<button onclick="toggleSound()" class="btn btn-outline" id="soundToggle">🔊</button>
|
||
<button onclick="toggleSafeMode()" class="btn btn-outline" id="safeModeToggle">🎙️</button>
|
||
<button onclick="logout()" class="btn btn-outline">Выйти</button>
|
||
</div>
|
||
</div>
|
||
</nav>
|
||
|
||
<main class="container" style="padding: 2rem 1rem;">
|
||
<div class="crash-layout">
|
||
<div class="crash-canvas-container">
|
||
<canvas id="crashCanvas"></canvas>
|
||
<div class="crash-overlay">
|
||
<div class="crash-multiplier waiting" id="crashMultiplier">Ожидание...</div>
|
||
<div class="crash-status-text" id="crashStatus">Следующий раунд скоро начнётся</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="crash-history" id="crashHistory"></div>
|
||
|
||
<div class="crash-controls">
|
||
<div class="crash-bet-panel">
|
||
<h3>💰 Сделать ставку</h3>
|
||
<div class="crash-bet-input">
|
||
<input type="number" id="betAmount" value="100" min="1" step="1">
|
||
</div>
|
||
<div class="quick-bet-btns">
|
||
<button class="quick-bet-btn" onclick="setBet(50)">50</button>
|
||
<button class="quick-bet-btn" onclick="setBet(100)">100</button>
|
||
<button class="quick-bet-btn" onclick="setBet(500)">500</button>
|
||
<button class="quick-bet-btn" onclick="setBet(1000)">1K</button>
|
||
</div>
|
||
<button class="crash-btn bet" id="betBtn" onclick="placeBet()">Сделать ставку</button>
|
||
<button class="crash-btn cashout hidden" id="cashoutBtn" onclick="cashOut()">💰 Забрать</button>
|
||
<div id="myBetDisplay" class="crash-my-bet" style="display:none;"></div>
|
||
<div class="cashout-progress" id="cashoutProgress">
|
||
<div class="cashout-slider">
|
||
<div class="cashout-slider-fill" id="cashoutSliderFill" style="width:0%"></div>
|
||
</div>
|
||
<div class="cashout-label">
|
||
<span>1.00x</span>
|
||
<span id="cashoutMultLabel">1.00x</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="crash-players-panel">
|
||
<h3>👥 Ставки</h3>
|
||
<div id="playersList">
|
||
<div style="color: var(--text-secondary); text-align: center; padding: 1rem;">
|
||
Ожидание ставок...
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</main>
|
||
|
||
<script src="/static/js/sounds.js"></script>
|
||
<script src="/static/js/websocket.js"></script>
|
||
<script src="/static/js/notifications.js"></script>
|
||
<script src="/static/js/safemode.js"></script>
|
||
<script>
|
||
const canvas = document.getElementById('crashCanvas');
|
||
const ctx = canvas.getContext('2d');
|
||
const multiplierEl = document.getElementById('crashMultiplier');
|
||
const statusEl = document.getElementById('crashStatus');
|
||
const betBtn = document.getElementById('betBtn');
|
||
const cashoutBtn = document.getElementById('cashoutBtn');
|
||
const playersList = document.getElementById('playersList');
|
||
const historyEl = document.getElementById('crashHistory');
|
||
const betInput = document.getElementById('betAmount');
|
||
const myBetDisplay = document.getElementById('myBetDisplay');
|
||
const cashoutProgress = document.getElementById('cashoutProgress');
|
||
const cashoutSliderFill = document.getElementById('cashoutSliderFill');
|
||
const cashoutMultLabel = document.getElementById('cashoutMultLabel');
|
||
|
||
let gameState = null;
|
||
let myActiveBet = null; // текущая ставка в этом раунде
|
||
let myCashoutMult = null; // на каком множителе вывел (null = не вывел)
|
||
let lastPhase = null;
|
||
|
||
function resizeCanvas() {
|
||
const rect = canvas.parentElement.getBoundingClientRect();
|
||
canvas.width = canvas.clientWidth || rect.width - 32;
|
||
canvas.height = canvas.clientHeight || 350;
|
||
}
|
||
resizeCanvas();
|
||
window.addEventListener('resize', resizeCanvas);
|
||
|
||
function setBet(amount) { betInput.value = amount; SoundManager.click(); }
|
||
|
||
function expMult(t) { return Math.exp(t * 0.08); }
|
||
function timeForMult(m) { return Math.log(m) / 0.08; }
|
||
|
||
let graphPoints = [];
|
||
const GRAPH_WINDOW = 15; // секунд в окне
|
||
|
||
function drawGraph() {
|
||
const w = canvas.width, h = canvas.height, pad = 40;
|
||
const drawW = w - pad * 2, drawH = h - pad * 2;
|
||
ctx.clearRect(0, 0, w, h);
|
||
|
||
if (!gameState || gameState.phase === 'waiting') {
|
||
ctx.fillStyle = 'rgba(255,255,255,0.08)';
|
||
ctx.font = '20px sans-serif';
|
||
ctx.textAlign = 'center';
|
||
ctx.fillText('💥 Crash', w/2, h/2);
|
||
return;
|
||
}
|
||
|
||
const phase = gameState.phase;
|
||
const curMult = gameState.current_multiplier || 1;
|
||
const crashPoint = gameState.crash_point || 100;
|
||
const isCrashed = phase === 'crashed';
|
||
const isRunning = phase === 'running';
|
||
|
||
// Добавляем текущую точку в историю
|
||
if (isRunning && curMult > 1) {
|
||
const now = performance.now() / 1000;
|
||
graphPoints.push({ t: now, m: curMult });
|
||
// Отсекаем точки за пределами окна
|
||
const cutoff = now - GRAPH_WINDOW;
|
||
graphPoints = graphPoints.filter(p => p.t >= cutoff);
|
||
if (graphPoints.length > 500) graphPoints = graphPoints.slice(-500);
|
||
} else if (isCrashed) {
|
||
graphPoints = [];
|
||
}
|
||
|
||
// Определяем диапазон мультипликаторов для оси Y
|
||
let maxY = 2;
|
||
if (graphPoints.length > 0) {
|
||
maxY = Math.max(...graphPoints.map(p => p.m));
|
||
}
|
||
maxY = Math.max(maxY, curMult + 1, crashPoint, 3);
|
||
maxY = Math.min(maxY, 200); // не растягивать до небес
|
||
maxY = Math.ceil(maxY);
|
||
|
||
// Сетка
|
||
ctx.strokeStyle = 'rgba(255,255,255,0.04)';
|
||
ctx.lineWidth = 1;
|
||
const gridLines = 5;
|
||
ctx.font = '10px sans-serif';
|
||
ctx.textAlign = 'right';
|
||
for (let i = 1; i <= gridLines; i++) {
|
||
const y = h - pad - (i / gridLines) * drawH;
|
||
ctx.beginPath(); ctx.moveTo(pad, y); ctx.lineTo(w - pad, y); ctx.stroke();
|
||
const multLabel = 1 + (maxY - 1) * (i / gridLines);
|
||
ctx.fillStyle = 'rgba(255,255,255,0.12)';
|
||
ctx.fillText(`x${multLabel.toFixed(1)}`, pad - 6, y + 3);
|
||
}
|
||
|
||
// Рисуем кривую
|
||
if (graphPoints.length < 2) {
|
||
if (isCrashed) {
|
||
ctx.fillStyle = 'rgba(239,68,68,0.5)';
|
||
ctx.font = '36px sans-serif';
|
||
ctx.textAlign = 'center';
|
||
ctx.fillText(`💥 x${crashPoint.toFixed(2)}`, w/2, h/2);
|
||
}
|
||
return;
|
||
}
|
||
|
||
const color = isCrashed ? '#ef4444' : '#22c55e';
|
||
const now = performance.now() / 1000;
|
||
const windowStart = now - GRAPH_WINDOW;
|
||
|
||
ctx.beginPath();
|
||
ctx.strokeStyle = color;
|
||
ctx.lineWidth = 3;
|
||
ctx.shadowColor = color;
|
||
ctx.shadowBlur = 10;
|
||
|
||
let first = true;
|
||
for (const p of graphPoints) {
|
||
const x = pad + ((p.t - windowStart) / GRAPH_WINDOW) * drawW;
|
||
const y = h - pad - ((p.m - 1) / (maxY - 1)) * drawH;
|
||
if (first) { ctx.moveTo(x, y); first = false; }
|
||
else ctx.lineTo(x, y);
|
||
}
|
||
|
||
ctx.stroke();
|
||
ctx.shadowBlur = 0;
|
||
|
||
// Текущая точка (справа, на последнем значении)
|
||
if (isRunning && curMult > 1) {
|
||
const lastPt = graphPoints[graphPoints.length - 1];
|
||
const x = pad + ((lastPt.t - windowStart) / GRAPH_WINDOW) * drawW;
|
||
const y = h - pad - ((lastPt.m - 1) / (maxY - 1)) * drawH;
|
||
|
||
ctx.beginPath();
|
||
ctx.arc(x, y, 5, 0, Math.PI * 2);
|
||
ctx.fillStyle = '#22c55e';
|
||
ctx.fill();
|
||
ctx.shadowColor = '#22c55e';
|
||
ctx.shadowBlur = 20;
|
||
ctx.beginPath();
|
||
ctx.arc(x, y, 8, 0, Math.PI * 2);
|
||
ctx.strokeStyle = 'rgba(34,197,94,0.4)';
|
||
ctx.lineWidth = 2;
|
||
ctx.stroke();
|
||
ctx.shadowBlur = 0;
|
||
}
|
||
|
||
// Беттинг фаза — таймер
|
||
if (phase === 'betting') {
|
||
ctx.fillStyle = 'rgba(255,255,255,0.06)';
|
||
ctx.font = '40px sans-serif';
|
||
ctx.textAlign = 'center';
|
||
ctx.fillText(`⏱ ${gameState.timer}`, w/2, h/2 + 10);
|
||
}
|
||
}
|
||
|
||
function updateUI() {
|
||
if (!gameState) return;
|
||
const phase = gameState.phase;
|
||
|
||
// Обновляем множитель и статус
|
||
if (phase === 'waiting') {
|
||
multiplierEl.className = 'crash-multiplier waiting';
|
||
multiplierEl.textContent = 'Ожидание...';
|
||
statusEl.textContent = 'Следующий раунд скоро начнётся';
|
||
} else if (phase === 'betting') {
|
||
multiplierEl.className = 'crash-multiplier betting';
|
||
multiplierEl.textContent = `${gameState.timer}`;
|
||
statusEl.textContent = 'Делайте ставки!';
|
||
} else if (phase === 'running') {
|
||
const mult = gameState.current_multiplier;
|
||
multiplierEl.className = 'crash-multiplier';
|
||
multiplierEl.textContent = `x${mult.toFixed(2)}`;
|
||
statusEl.textContent = '🚀 Взлетаем!';
|
||
if (myActiveBet && myCashoutMult === null) {
|
||
const prog = Math.min(100, ((mult - 1) / 15) * 100);
|
||
cashoutSliderFill.style.width = `${prog}%`;
|
||
cashoutMultLabel.textContent = `x${mult.toFixed(2)}`;
|
||
}
|
||
} else if (phase === 'crashed') {
|
||
multiplierEl.className = 'crash-multiplier crashed';
|
||
multiplierEl.textContent = `💥 x${gameState.crash_point.toFixed(2)}`;
|
||
statusEl.textContent = 'Краш!';
|
||
}
|
||
|
||
// Кнопки
|
||
const canBet = phase === 'betting';
|
||
const canCashout = phase === 'running' && myActiveBet !== null && myCashoutMult === null;
|
||
|
||
betBtn.classList.toggle('hidden', !canBet && !(phase === 'betting' && myActiveBet));
|
||
if (canBet) {
|
||
betBtn.disabled = false;
|
||
betBtn.textContent = myActiveBet ? `Ставка: ${myActiveBet} ₽` : '💰 Сделать ставку';
|
||
betBtn.className = 'crash-btn bet' + (myActiveBet ? ' placed' : '');
|
||
}
|
||
|
||
cashoutBtn.classList.toggle('hidden', !canCashout);
|
||
if (canCashout) {
|
||
cashoutBtn.disabled = false;
|
||
cashoutBtn.textContent = `💰 Забрать (x${gameState.current_multiplier.toFixed(2)})`;
|
||
}
|
||
|
||
cashoutProgress.classList.toggle('active', phase === 'running' && myActiveBet !== null && myCashoutMult === null);
|
||
|
||
// Статус моей ставки
|
||
if (myActiveBet !== null) {
|
||
if (myCashoutMult !== null) {
|
||
const payout = myActiveBet * myCashoutMult;
|
||
myBetDisplay.style.display = 'block';
|
||
myBetDisplay.innerHTML = `
|
||
✅ Вывел(а) <span class="win-highlight">${payout.toFixed(0)} ₽</span>
|
||
на x${myCashoutMult.toFixed(2)}
|
||
`;
|
||
} else if (phase === 'crashed') {
|
||
myBetDisplay.style.display = 'block';
|
||
myBetDisplay.innerHTML = `
|
||
❌ Проиграно <span class="lose-highlight">${myActiveBet.toFixed(0)} ₽</span>
|
||
`;
|
||
} else if (phase === 'running') {
|
||
const potential = myActiveBet * gameState.current_multiplier;
|
||
myBetDisplay.style.display = 'block';
|
||
myBetDisplay.innerHTML = `
|
||
Ставка: <span class="bet-highlight">${myActiveBet.toFixed(0)} ₽</span>
|
||
→ ${potential.toFixed(0)} ₽
|
||
`;
|
||
} else {
|
||
myBetDisplay.style.display = 'block';
|
||
myBetDisplay.innerHTML = `
|
||
Ставка: <span class="bet-highlight">${myActiveBet.toFixed(0)} ₽</span>
|
||
`;
|
||
}
|
||
} else {
|
||
myBetDisplay.style.display = 'none';
|
||
}
|
||
|
||
// Звуки на смену фазы
|
||
if (lastPhase !== phase) {
|
||
if (phase === 'running') SoundManager.crashBet();
|
||
if (phase === 'crashed') SoundManager.crashCrashed();
|
||
lastPhase = phase;
|
||
}
|
||
|
||
updatePlayers();
|
||
updateHistory();
|
||
drawGraph();
|
||
}
|
||
|
||
function updatePlayers() {
|
||
if (!gameState) return;
|
||
const bc = gameState.bet_count || 0;
|
||
if (bc === 0) {
|
||
playersList.innerHTML = '<div style="color:var(--text-secondary);text-align:center;padding:1rem;">Пока нет ставок</div>';
|
||
return;
|
||
}
|
||
|
||
const cashed = gameState.cashed_out || {};
|
||
const cashCount = Object.keys(cashed).length;
|
||
|
||
let html = `<div class="player-row" style="font-weight:600;border-bottom:2px solid var(--border-color);">
|
||
<span>Ставок: ${bc}</span>
|
||
<span>Сумма: ${(gameState.total_bets || 0).toFixed(0)} ₽</span>
|
||
<span>Вывели: ${cashCount}</span>
|
||
</div>`;
|
||
|
||
if (gameState.phase === 'running' || gameState.phase === 'crashed') {
|
||
const mult = gameState.current_multiplier || gameState.crash_point;
|
||
for (const [uid, m] of Object.entries(cashed)) {
|
||
const isMe = myActiveBet !== null && parseInt(uid) === 0;
|
||
html += `<div class="player-row" style="${isMe ? 'color:var(--success-color)' : ''}">
|
||
<span>${isMe ? 'Вы' : 'Игрок #' + uid.slice(-4)}</span>
|
||
<span class="payout">✅ x${m.toFixed(2)}</span>
|
||
</div>`;
|
||
}
|
||
}
|
||
|
||
playersList.innerHTML = html;
|
||
}
|
||
|
||
function updateHistory() {
|
||
if (!gameState || !gameState.history) return;
|
||
historyEl.innerHTML = gameState.history.slice().reverse().map(m => {
|
||
const isWin = m > 2;
|
||
return `<span class="crash-history-item ${isWin ? 'win' : 'loss'}">x${m.toFixed(2)}</span>`;
|
||
}).join('');
|
||
}
|
||
|
||
// WebSocket events
|
||
WS.on('crash_state', (data) => {
|
||
if (!data.game) return;
|
||
gameState = data.game;
|
||
// При краше сбрасываем мою ставку т.к. раунд закончился
|
||
if (gameState.phase === 'crashed' && lastPhase === 'running') {
|
||
// Если не вывел и краш — ставка проиграна, но стейт остается
|
||
}
|
||
if (gameState.phase === 'waiting' || gameState.phase === 'betting') {
|
||
// Новый раунд — если у меня была ставка, её уже нет (проиграна/выведена)
|
||
if (lastPhase === 'crashed' || lastPhase === 'running') {
|
||
if (myCashoutMult === null && myActiveBet !== null && gameState.phase === 'betting') {
|
||
// Ставка проиграна в прошлом раунде
|
||
}
|
||
}
|
||
if (gameState.phase === 'betting') {
|
||
// Если начался новый раунд betting и мы не обнулились —
|
||
// это новый раунд. Старая ставка аннулируется.
|
||
if (lastPhase === 'crashed' || lastPhase === 'waiting') {
|
||
myActiveBet = null;
|
||
myCashoutMult = null;
|
||
}
|
||
}
|
||
}
|
||
updateUI();
|
||
});
|
||
|
||
async function placeBet() {
|
||
const amount = parseInt(betInput.value);
|
||
if (!amount || amount <= 0) {
|
||
SoundManager.error();
|
||
return;
|
||
}
|
||
|
||
const formData = new FormData();
|
||
formData.append('amount', amount);
|
||
|
||
try {
|
||
const res = await fetch('/web/api/crash/bet', { method: 'POST', body: formData });
|
||
const data = await res.json();
|
||
if (data.success) {
|
||
myActiveBet = data.bet;
|
||
myCashoutMult = null;
|
||
SoundManager.crashBet();
|
||
document.querySelectorAll('.user-balance').forEach(el => {
|
||
el.textContent = `${Math.floor(data.new_balance)} ₽`;
|
||
});
|
||
updateUI();
|
||
} else {
|
||
SoundManager.error();
|
||
Notify.error(data.error);
|
||
}
|
||
} catch (e) {
|
||
SoundManager.error();
|
||
Notify.error('Ошибка соединения');
|
||
}
|
||
}
|
||
|
||
async function cashOut() {
|
||
if (!myActiveBet || myCashoutMult !== null) return;
|
||
|
||
try {
|
||
const res = await fetch('/web/api/crash/cashout', { method: 'POST' });
|
||
const data = await res.json();
|
||
if (data.success) {
|
||
myCashoutMult = data.multiplier;
|
||
SoundManager.crashCashout();
|
||
document.querySelectorAll('.user-balance').forEach(el => {
|
||
el.textContent = `${Math.floor(data.new_balance)} ₽`;
|
||
});
|
||
updateUI();
|
||
} else {
|
||
SoundManager.error();
|
||
Notify.error(data.error);
|
||
}
|
||
} catch (e) {
|
||
SoundManager.error();
|
||
Notify.error('Ошибка соединения');
|
||
}
|
||
}
|
||
|
||
async function logout() {
|
||
await fetch('/web/api/auth/logout', { method: 'POST' });
|
||
window.location.href = '/';
|
||
}
|
||
|
||
function toggleSound() {
|
||
const enabled = SoundManager.toggle();
|
||
document.getElementById('soundToggle').textContent = enabled ? '🔊' : '🔇';
|
||
}
|
||
document.addEventListener('DOMContentLoaded', () => {
|
||
const btn = document.getElementById('soundToggle');
|
||
if (btn) btn.textContent = localStorage.getItem('sound_enabled') !== 'false' ? '🔊' : '🔇';
|
||
});
|
||
</script>
|
||
{% include '_activity_sidebar.html' %}
|
||
</body>
|
||
</html>
|