dopamine effects

This commit is contained in:
Michal Vrba
2026-08-28 16:57:34 +02:00
parent 02bca7c34a
commit 16bc48ab34
5 changed files with 489 additions and 8 deletions
+274
View File
@@ -617,6 +617,271 @@ window.FaraonFx = (function () {
}
}
// --- DOPAMINE PROCEDURAL AUDIO ---
function playSelectStep(step = 1) {
if (isMuted) return;
const ctx = getAudioContext();
if (!ctx) return;
const now = ctx.currentTime;
// Pentatonic rising scale (C5, E5, G5, C6, E6)
const freqs = [523.25, 659.25, 783.99, 1046.50, 1318.51];
const freq = freqs[Math.min(step - 1, freqs.length - 1)] || 523.25;
const osc = ctx.createOscillator();
const harm = ctx.createOscillator();
const gain = ctx.createGain();
osc.type = 'triangle';
osc.frequency.setValueAtTime(freq, now);
osc.frequency.exponentialRampToValueAtTime(freq * 1.02, now + 0.12);
harm.type = 'sine';
harm.frequency.setValueAtTime(freq * 2, now);
gain.gain.setValueAtTime(0.28, now);
gain.gain.exponentialRampToValueAtTime(0.001, now + 0.14);
osc.connect(gain);
harm.connect(gain);
gain.connect(ctx.destination);
osc.start(now);
harm.start(now);
osc.stop(now + 0.15);
harm.stop(now + 0.15);
}
function playComboSlam(count = 2) {
if (isMuted) return;
const ctx = getAudioContext();
if (!ctx) return;
const now = ctx.currentTime;
// Heavy 808 Sub-bass drop
const sub = ctx.createOscillator();
const subGain = ctx.createGain();
sub.type = 'sine';
sub.frequency.setValueAtTime(140 + count * 20, now);
sub.frequency.exponentialRampToValueAtTime(32, now + 0.45);
subGain.gain.setValueAtTime(0.7, now);
subGain.gain.exponentialRampToValueAtTime(0.001, now + 0.45);
sub.connect(subGain);
subGain.connect(ctx.destination);
sub.start(now);
sub.stop(now + 0.46);
// Metallic punch noise
const bufferSize = ctx.sampleRate * 0.12;
const buffer = ctx.createBuffer(1, bufferSize, ctx.sampleRate);
const data = buffer.getChannelData(0);
for (let i = 0; i < bufferSize; i++) {
data[i] = (Math.random() * 2 - 1) * Math.exp(-i / (ctx.sampleRate * 0.03));
}
const noise = ctx.createBufferSource();
noise.buffer = buffer;
const filter = ctx.createBiquadFilter();
filter.type = 'bandpass';
filter.frequency.setValueAtTime(1800, now);
filter.Q.setValueAtTime(4, now);
const noiseGain = ctx.createGain();
noiseGain.gain.setValueAtTime(0.5, now);
noiseGain.gain.exponentialRampToValueAtTime(0.001, now + 0.12);
noise.connect(filter);
filter.connect(noiseGain);
noiseGain.connect(ctx.destination);
noise.start(now);
// Escalating screen shake
triggerScreenShake(count >= 4 ? 16 : (count === 3 ? 10 : 6));
}
function playJackpotCoins() {
if (isMuted) return;
const ctx = getAudioContext();
if (!ctx) return;
const now = ctx.currentTime;
const notes = [987.77, 1174.66, 1318.51, 1567.98, 1760.00, 2093.00, 2349.32, 2793.83];
notes.forEach((freq, idx) => {
const time = now + idx * 0.06;
const osc = ctx.createOscillator();
const gain = ctx.createGain();
osc.type = 'sine';
osc.frequency.setValueAtTime(freq, time);
gain.gain.setValueAtTime(0.22, time);
gain.gain.exponentialRampToValueAtTime(0.001, time + 0.22);
osc.connect(gain);
gain.connect(ctx.destination);
osc.start(time);
osc.stop(time + 0.23);
});
}
function playReviveSound() {
if (isMuted) return;
const ctx = getAudioContext();
if (!ctx) return;
const now = ctx.currentTime;
const chord = [440, 554.37, 659.25, 880, 1108.73];
chord.forEach((freq, i) => {
const osc = ctx.createOscillator();
const gain = ctx.createGain();
osc.type = 'sawtooth';
osc.frequency.setValueAtTime(freq, now + i * 0.04);
osc.frequency.linearRampToValueAtTime(freq * 1.5, now + 0.5);
const filter = ctx.createBiquadFilter();
filter.type = 'lowpass';
filter.frequency.setValueAtTime(800, now);
filter.frequency.exponentialRampToValueAtTime(3500, now + 0.4);
gain.gain.setValueAtTime(0.25, now + i * 0.04);
gain.gain.exponentialRampToValueAtTime(0.001, now + 0.6);
osc.connect(filter);
filter.connect(gain);
gain.connect(ctx.destination);
osc.start(now + i * 0.04);
osc.stop(now + 0.62);
});
triggerScreenShake(8);
}
// --- DOPAMINE BANNER SYSTEM ---
function triggerDopamineBanner(title, subtitle = '', type = 'combo') {
const container = document.getElementById('game-table-container') || document.body;
// Remove existing banner if any
const oldBanner = document.querySelector('.dopamine-banner');
if (oldBanner) oldBanner.remove();
const banner = document.createElement('div');
banner.className = `dopamine-banner dopamine-${type}`;
banner.innerHTML = `
<div class="dopamine-halo"></div>
<div class="dopamine-content">
<div class="dopamine-title">${title}</div>
${subtitle ? `<div class="dopamine-sub">${subtitle}</div>` : ''}
</div>
`;
container.appendChild(banner);
// Sound & Particles based on type
if (type === 'quad' || type === 'mega') {
playComboSlam(4);
triggerGoldStarExplosion();
triggerConfetti();
} else if (type === 'triple') {
playComboSlam(3);
triggerFireParticles(window.innerWidth / 2, window.innerHeight / 2, 45);
} else if (type === 'double') {
playComboSlam(2);
triggerLightningSparks();
} else if (type === 'freeze') {
playAceFreeze();
triggerIceParticles(window.innerWidth / 2, window.innerHeight / 2, 50);
} else if (type === 'revive') {
playReviveSound();
triggerLightningSparks(window.innerWidth / 2, window.innerHeight / 2, 60);
} else if (type === 'jackpot') {
playJackpotCoins();
triggerCoinJackpot();
triggerConfetti();
}
// Auto remove banner after animation
setTimeout(() => {
banner.classList.add('dopamine-fadeout');
setTimeout(() => banner.remove(), 400);
}, 1800);
}
// --- ADVANCED PARTICLES ---
function triggerCoinJackpot(x, y, count = 70) {
if (!particleCanvas) return;
const startX = x || window.innerWidth / 2;
const startY = y || window.innerHeight / 2;
for (let i = 0; i < count; i++) {
const angle = -Math.PI / 2 + (Math.random() - 0.5) * 2.2;
const speed = 6 + Math.random() * 12;
particles.push({
x: startX,
y: startY,
vx: Math.cos(angle) * speed,
vy: Math.sin(angle) * speed,
gravity: 0.28,
size: 10 + Math.random() * 6,
aspect: 0.5 + Math.random() * 0.5,
color: Math.random() > 0.3 ? '#ffd700' : '#f5b041',
shape: 'rect',
life: 1.2,
decay: 0.012 + Math.random() * 0.008,
rotation: Math.random() * Math.PI * 2,
vRot: (Math.random() - 0.5) * 0.25
});
}
}
function triggerLightningSparks(x, y, count = 40) {
if (!particleCanvas) return;
const colors = ['#00e5ff', '#ffd700', '#ffffff', '#e040fb'];
const startX = x || window.innerWidth / 2;
const startY = y || window.innerHeight / 2;
for (let i = 0; i < count; i++) {
const angle = Math.random() * Math.PI * 2;
const speed = 4 + Math.random() * 10;
particles.push({
x: startX,
y: startY,
vx: Math.cos(angle) * speed,
vy: Math.sin(angle) * speed,
gravity: 0,
size: 3 + Math.random() * 4,
color: colors[Math.floor(Math.random() * colors.length)],
shape: 'spark',
life: 0.8,
decay: 0.035,
rotation: Math.random() * Math.PI
});
}
}
function triggerGoldStarExplosion(x, y, count = 60) {
if (!particleCanvas) return;
const startX = x || window.innerWidth / 2;
const startY = y || window.innerHeight / 2;
for (let i = 0; i < count; i++) {
const angle = Math.random() * Math.PI * 2;
const speed = 3 + Math.random() * 11;
particles.push({
x: startX,
y: startY,
vx: Math.cos(angle) * speed,
vy: Math.sin(angle) * speed,
gravity: 0.06,
size: 6 + Math.random() * 6,
color: '#ffd700',
shape: 'circle',
life: 1.1,
decay: 0.02,
rotation: 0
});
}
}
function toggleMute() {
isMuted = !isMuted;
return isMuted;
@@ -631,14 +896,23 @@ window.FaraonFx = (function () {
playBurnExplosion,
playVictory,
playHeartbeat,
playSelectStep,
playComboSlam,
playJackpotCoins,
playReviveSound,
triggerDopamineBanner,
triggerScreenShake,
initParticles,
triggerFireParticles,
triggerIceParticles,
triggerBurnExplosion,
triggerConfetti,
triggerCoinJackpot,
triggerLightningSparks,
triggerGoldStarExplosion,
animateCardPlay,
animateCardDraw,
toggleMute
};
})();