RPU v2, promo/bank система, админка, оптимизации
— RPU v2: бюджет удачи, потолок (ceiling), комбек после проигрышей, hot/cold анализ, интеграция с promo-фазами — Bank API: mock-банк для карты (депозит/вывод с балансом 1000₽) — Promo-сервис: автогенерация промокодов каждый час, типы (card_to_site, luck_boost, ceiling_boost, free_case, reset_streak) — Promo-фазы: LUCKY/NEUTRAL/UNLUCKY, переключение каждые 1-6ч — Admin: история транзакций (user_history), управление промокодами, управление promo-фазами, API фильтров/коллекций, RPU info — Database: WAL mode, новые поля RPU v2, TransactionLog, карточный баланс, статистики (streaks, ceiling, budget) — Frontend: /deposit, /promo/activate, /withdraw, /card-balance, /online-count, /rpu-info, lifespan вместо startup event — UI: user_history.html, улучшения профиля, кейсов, навигации — Cases: обновление cases.json (1892 строки новых данных) — Achievements: кэш коллекций, оптимизация is_custom проверок — Sounds: дополнительные звуковые эффекты — Upgrade: интеграция с RPU (множители, streaks) — .gitignore: исключены .db, .bak, __pycache__
This commit is contained in:
+88
-49
@@ -1,8 +1,9 @@
|
||||
// Sound effects system using Web Audio API - no external files needed
|
||||
const SoundManager = {
|
||||
_ctx: null,
|
||||
_enabled: true,
|
||||
_volume: 0.3,
|
||||
_activeNodes: new Set(),
|
||||
_timeouts: [],
|
||||
|
||||
init() {
|
||||
try {
|
||||
@@ -25,10 +26,28 @@ const SoundManager = {
|
||||
return this._enabled;
|
||||
},
|
||||
|
||||
stopAll() {
|
||||
this._timeouts.forEach(clearTimeout);
|
||||
this._timeouts = [];
|
||||
this._activeNodes.forEach(node => {
|
||||
try { node.stop(); } catch (e) {}
|
||||
try { node.disconnect(); } catch (e) {}
|
||||
});
|
||||
this._activeNodes.clear();
|
||||
},
|
||||
|
||||
_trackNode(node, duration) {
|
||||
this._activeNodes.add(node);
|
||||
node.addEventListener('ended', () => this._activeNodes.delete(node));
|
||||
setTimeout(() => {
|
||||
this._activeNodes.delete(node);
|
||||
try { node.disconnect(); } catch (e) {}
|
||||
}, (duration + 0.1) * 1000);
|
||||
},
|
||||
|
||||
_play(freq, duration, type = 'sine', volume = 1) {
|
||||
if (!this._enabled || !this._ctx) return;
|
||||
if (this._ctx.state === 'suspended') this._ctx.resume();
|
||||
|
||||
const osc = this._ctx.createOscillator();
|
||||
const gain = this._ctx.createGain();
|
||||
osc.type = type;
|
||||
@@ -39,12 +58,13 @@ const SoundManager = {
|
||||
gain.connect(this._ctx.destination);
|
||||
osc.start();
|
||||
osc.stop(this._ctx.currentTime + duration);
|
||||
this._trackNode(osc, duration);
|
||||
this._trackNode(gain, duration);
|
||||
},
|
||||
|
||||
_noise(duration, volume = 1) {
|
||||
if (!this._enabled || !this._ctx) return;
|
||||
if (this._ctx.state === 'suspended') this._ctx.resume();
|
||||
|
||||
const bufferSize = this._ctx.sampleRate * duration;
|
||||
const buffer = this._ctx.createBuffer(1, bufferSize, this._ctx.sampleRate);
|
||||
const data = buffer.getChannelData(0);
|
||||
@@ -59,91 +79,113 @@ const SoundManager = {
|
||||
source.connect(gain);
|
||||
gain.connect(this._ctx.destination);
|
||||
source.start();
|
||||
this._trackNode(source, duration);
|
||||
this._trackNode(gain, duration);
|
||||
},
|
||||
|
||||
_delay(fn, ms) {
|
||||
const id = setTimeout(() => {
|
||||
this._timeouts = this._timeouts.filter(t => t !== id);
|
||||
fn();
|
||||
}, ms);
|
||||
this._timeouts.push(id);
|
||||
return id;
|
||||
},
|
||||
|
||||
caseOpen() {
|
||||
// Descending sweep with click
|
||||
this._play(800, 0.1, 'square', 0.3);
|
||||
setTimeout(() => this._play(400, 0.15, 'sawtooth', 0.2), 80);
|
||||
setTimeout(() => this._play(200, 0.2, 'sine', 0.15), 180);
|
||||
this._delay(() => this._play(400, 0.15, 'sawtooth', 0.2), 80);
|
||||
this._delay(() => this._play(200, 0.2, 'sine', 0.15), 180);
|
||||
this._noise(0.05, 0.4);
|
||||
},
|
||||
|
||||
caseRare() {
|
||||
// Ascending bright chime for rare items
|
||||
this._play(523, 0.15, 'sine', 0.3);
|
||||
setTimeout(() => this._play(659, 0.15, 'sine', 0.3), 100);
|
||||
setTimeout(() => this._play(784, 0.15, 'sine', 0.3), 200);
|
||||
setTimeout(() => this._play(1047, 0.3, 'sine', 0.4), 300);
|
||||
this._delay(() => this._play(659, 0.15, 'sine', 0.3), 100);
|
||||
this._delay(() => this._play(784, 0.15, 'sine', 0.3), 200);
|
||||
this._delay(() => this._play(1047, 0.3, 'sine', 0.4), 300);
|
||||
},
|
||||
|
||||
caseCovert() {
|
||||
// Epic sound for covert/knife
|
||||
this._play(392, 0.2, 'sawtooth', 0.3);
|
||||
setTimeout(() => this._play(523, 0.2, 'sawtooth', 0.3), 150);
|
||||
setTimeout(() => this._play(659, 0.2, 'sawtooth', 0.3), 300);
|
||||
setTimeout(() => this._play(784, 0.4, 'sine', 0.5), 450);
|
||||
setTimeout(() => this._play(1047, 0.6, 'sine', 0.6), 600);
|
||||
this._delay(() => this._play(523, 0.2, 'sawtooth', 0.3), 150);
|
||||
this._delay(() => this._play(659, 0.2, 'sawtooth', 0.3), 300);
|
||||
this._delay(() => this._play(784, 0.4, 'sine', 0.5), 450);
|
||||
this._delay(() => this._play(1047, 0.6, 'sine', 0.6), 600);
|
||||
},
|
||||
|
||||
slotSpin() {
|
||||
// Ratcheting sound for slot reels
|
||||
for (let i = 0; i < 8; i++) {
|
||||
setTimeout(() => {
|
||||
this._delay(() => {
|
||||
this._play(300 + Math.random() * 400, 0.05, 'square', 0.15);
|
||||
}, i * 80);
|
||||
}
|
||||
},
|
||||
|
||||
slotMatch() {
|
||||
// Win jingle
|
||||
this._play(523, 0.1, 'sine', 0.3);
|
||||
setTimeout(() => this._play(659, 0.1, 'sine', 0.3), 100);
|
||||
setTimeout(() => this._play(784, 0.1, 'sine', 0.3), 200);
|
||||
setTimeout(() => this._play(1047, 0.4, 'sine', 0.5), 300);
|
||||
this._delay(() => this._play(659, 0.1, 'sine', 0.3), 100);
|
||||
this._delay(() => this._play(784, 0.1, 'sine', 0.3), 200);
|
||||
this._delay(() => this._play(1047, 0.4, 'sine', 0.5), 300);
|
||||
},
|
||||
|
||||
contractSubmit() {
|
||||
// Mechanical sound
|
||||
this._play(150, 0.3, 'sawtooth', 0.2);
|
||||
setTimeout(() => this._play(200, 0.2, 'square', 0.2), 200);
|
||||
setTimeout(() => this._play(250, 0.1, 'square', 0.15), 350);
|
||||
this._delay(() => this._play(200, 0.2, 'square', 0.2), 200);
|
||||
this._delay(() => this._play(250, 0.1, 'square', 0.15), 350);
|
||||
},
|
||||
|
||||
contractResult() {
|
||||
// Rising success
|
||||
this._play(400, 0.15, 'sine', 0.3);
|
||||
setTimeout(() => this._play(600, 0.15, 'sine', 0.3), 120);
|
||||
setTimeout(() => this._play(800, 0.3, 'sine', 0.4), 240);
|
||||
this._delay(() => this._play(600, 0.15, 'sine', 0.3), 120);
|
||||
this._delay(() => this._play(800, 0.3, 'sine', 0.4), 240);
|
||||
},
|
||||
|
||||
wheelSpin() {
|
||||
// Ticking as wheel rotates
|
||||
this._play(200, 0.03, 'square', 0.1);
|
||||
this._play(600 + Math.random() * 400, 0.04, 'triangle', 0.12);
|
||||
this._play(200, 0.02, 'square', 0.06);
|
||||
},
|
||||
|
||||
wheelWin() {
|
||||
// Celebration
|
||||
this._play(523, 0.15, 'sine', 0.3);
|
||||
setTimeout(() => this._play(659, 0.15, 'sine', 0.3), 120);
|
||||
setTimeout(() => this._play(784, 0.2, 'sine', 0.35), 240);
|
||||
setTimeout(() => this._play(1047, 0.5, 'sine', 0.5), 360);
|
||||
const t = this._ctx.currentTime;
|
||||
if (!this._enabled || !this._ctx) return;
|
||||
if (this._ctx.state === 'suspended') this._ctx.resume();
|
||||
[523, 659, 784, 1047].forEach((freq, i) => {
|
||||
const osc = this._ctx.createOscillator();
|
||||
const gain = this._ctx.createGain();
|
||||
osc.type = 'sine';
|
||||
osc.frequency.setValueAtTime(freq, t + i * 0.1);
|
||||
gain.gain.setValueAtTime(0, t + i * 0.1);
|
||||
gain.gain.linearRampToValueAtTime(this._volume * 0.35, t + i * 0.1 + 0.05);
|
||||
gain.gain.exponentialRampToValueAtTime(0.001, t + i * 0.1 + 0.5);
|
||||
osc.connect(gain);
|
||||
gain.connect(this._ctx.destination);
|
||||
osc.start(t + i * 0.1);
|
||||
osc.stop(t + i * 0.1 + 0.5);
|
||||
this._trackNode(osc, 0.6);
|
||||
this._trackNode(gain, 0.6);
|
||||
});
|
||||
this._delay(() => this._play(1568, 0.4, 'sine', 0.25), 350);
|
||||
this._delay(() => this._play(2093, 0.6, 'sine', 0.2), 500);
|
||||
},
|
||||
|
||||
wheelLose() {
|
||||
// Sad trombone
|
||||
this._play(400, 0.2, 'sine', 0.2);
|
||||
setTimeout(() => this._play(350, 0.2, 'sine', 0.2), 150);
|
||||
setTimeout(() => this._play(300, 0.3, 'sine', 0.15), 300);
|
||||
this._play(300, 0.15, 'sawtooth', 0.25);
|
||||
this._delay(() => this._play(200, 0.2, 'sawtooth', 0.2), 100);
|
||||
this._delay(() => this._play(120, 0.3, 'sawtooth', 0.15), 220);
|
||||
this._noise(0.15, 0.3);
|
||||
this._delay(() => {
|
||||
this._play(60, 0.5, 'sine', 0.2);
|
||||
this._play(45, 0.6, 'sine', 0.15);
|
||||
}, 350);
|
||||
},
|
||||
|
||||
achievement() {
|
||||
// Achievement unlock
|
||||
this._play(659, 0.1, 'sine', 0.3);
|
||||
setTimeout(() => this._play(523, 0.1, 'sine', 0.3), 100);
|
||||
setTimeout(() => this._play(784, 0.1, 'sine', 0.3), 200);
|
||||
setTimeout(() => this._play(659, 0.1, 'sine', 0.3), 300);
|
||||
setTimeout(() => this._play(1047, 0.4, 'sine', 0.5), 400);
|
||||
this._delay(() => this._play(523, 0.1, 'sine', 0.3), 100);
|
||||
this._delay(() => this._play(784, 0.1, 'sine', 0.3), 200);
|
||||
this._delay(() => this._play(659, 0.1, 'sine', 0.3), 300);
|
||||
this._delay(() => this._play(1047, 0.4, 'sine', 0.5), 400);
|
||||
},
|
||||
|
||||
crashBet() {
|
||||
@@ -155,17 +197,15 @@ const SoundManager = {
|
||||
},
|
||||
|
||||
crashCrashed() {
|
||||
// Explosion
|
||||
this._noise(0.3, 0.6);
|
||||
this._play(80, 0.5, 'sawtooth', 0.4);
|
||||
setTimeout(() => this._play(50, 0.8, 'sine', 0.3), 100);
|
||||
this._delay(() => this._play(50, 0.8, 'sine', 0.3), 100);
|
||||
},
|
||||
|
||||
crashCashout() {
|
||||
// Cashout success
|
||||
this._play(600, 0.1, 'sine', 0.3);
|
||||
setTimeout(() => this._play(800, 0.1, 'sine', 0.3), 80);
|
||||
setTimeout(() => this._play(1000, 0.2, 'sine', 0.4), 160);
|
||||
this._delay(() => this._play(800, 0.1, 'sine', 0.3), 80);
|
||||
this._delay(() => this._play(1000, 0.2, 'sine', 0.4), 160);
|
||||
},
|
||||
|
||||
click() {
|
||||
@@ -181,5 +221,4 @@ const SoundManager = {
|
||||
}
|
||||
};
|
||||
|
||||
// Auto-init
|
||||
document.addEventListener('DOMContentLoaded', () => SoundManager.init());
|
||||
|
||||
Reference in New Issue
Block a user