Music and sfx

This commit is contained in:
Santiago Fisela 2026-03-14 18:47:44 -03:00
parent 3d6a32e227
commit 2140eb53c7
14 changed files with 141 additions and 9 deletions

View file

@ -26,6 +26,9 @@
<div class="menu-container"> <div class="menu-container">
<header class="menu-header"> <header class="menu-header">
<img src="/MenuTitle.png" alt="Emerald Legacy" class="menu-title"> <img src="/MenuTitle.png" alt="Emerald Legacy" class="menu-title">
<button id="audio-toggle" class="audio-btn" title="Toggle Music">
<i class="fas fa-volume-up"></i>
</button>
</header> </header>
<nav class="menu-tabs"> <nav class="menu-tabs">

BIN
public/music/music1.ogg Normal file

Binary file not shown.

BIN
public/music/music2.ogg Normal file

Binary file not shown.

BIN
public/music/music3.ogg Normal file

Binary file not shown.

BIN
public/music/music4.ogg Normal file

Binary file not shown.

BIN
public/music/music5.ogg Normal file

Binary file not shown.

BIN
public/sounds/back.ogg Normal file

Binary file not shown.

BIN
public/sounds/click.wav Normal file

Binary file not shown.

BIN
public/sounds/levelup.ogg Normal file

Binary file not shown.

BIN
public/sounds/orb.ogg Normal file

Binary file not shown.

BIN
public/sounds/pop.wav Normal file

Binary file not shown.

Binary file not shown.

View file

@ -1,6 +1,80 @@
import './style.css'; import './style.css';
class AudioManager {
private musicFiles = ['/music/music1.ogg', '/music/music2.ogg', '/music/music3.ogg', '/music/music4.ogg', '/music/music5.ogg'];
private musicIndex = Math.floor(Math.random() * 5);
private music: HTMLAudioElement;
private isMuted = false;
private hasInteracted = false;
constructor() {
this.music = new Audio(this.musicFiles[this.musicIndex]);
this.music.volume = 0.3;
this.music.preload = 'auto';
this.music.addEventListener('ended', () => this.nextTrack());
const savedMute = localStorage.getItem('emerald-muted') === 'true';
if (savedMute) {
this.isMuted = true;
this.music.pause();
}
}
private nextTrack() {
this.musicIndex = (this.musicIndex + 1) % this.musicFiles.length;
this.music.src = this.musicFiles[this.musicIndex];
this.music.preload = 'auto';
if (!this.isMuted) this.music.play().catch(() => { });
}
public start() {
if (this.hasInteracted || this.isMuted) return;
this.hasInteracted = true;
this.music.play().catch(() => { });
}
public toggleMute() {
this.isMuted = !this.isMuted;
localStorage.setItem('emerald-muted', String(this.isMuted));
if (this.isMuted) this.music.pause();
else {
if (this.hasInteracted) this.music.play().catch(() => { });
else {
this.hasInteracted = true;
this.music.play().catch(() => { });
}
}
this.updateUI();
}
public updateUI() {
const btn = document.getElementById('audio-toggle');
if (btn) {
btn.classList.toggle('muted', this.isMuted);
const icon = btn.querySelector('i');
if (icon) icon.className = this.isMuted ? 'fas fa-volume-mute' : 'fas fa-volume-up';
}
}
public playSFX(file: string) {
const sfx = new Audio(`/sounds/${file}`);
sfx.volume = 0.4;
sfx.play().catch(() => { });
}
}
document.addEventListener('DOMContentLoaded', () => { document.addEventListener('DOMContentLoaded', () => {
const audio = new AudioManager();
audio.updateUI();
const startAudio = () => audio.start();
document.addEventListener('click', startAudio, { once: true });
document.getElementById('audio-toggle')?.addEventListener('click', () => {
audio.playSFX('click.wav');
audio.toggleMute();
});
const tabs = document.querySelectorAll('.tab-item'); const tabs = document.querySelectorAll('.tab-item');
const contents = document.querySelectorAll('.tab-content'); const contents = document.querySelectorAll('.tab-content');
@ -10,6 +84,7 @@ document.addEventListener('DOMContentLoaded', () => {
if (index < 0) index = tabs.length - 1; if (index < 0) index = tabs.length - 1;
if (index >= tabs.length) index = 0; if (index >= tabs.length) index = 0;
if (activeIndex !== index) audio.playSFX('click.wav');
activeIndex = index; activeIndex = index;
tabs.forEach((tab, i) => { tabs.forEach((tab, i) => {
@ -44,7 +119,8 @@ document.addEventListener('DOMContentLoaded', () => {
]; ];
let screenIndex = 0; let screenIndex = 0;
const rotateScreenshot = (dir: number) => { const rotateScreenshot = (dir: number, isManual = false) => {
if (isManual) audio.playSFX('wood click.wav');
screenIndex = (screenIndex + dir + screenshots.length) % screenshots.length; screenIndex = (screenIndex + dir + screenshots.length) % screenshots.length;
if (carouselImg) { if (carouselImg) {
carouselImg.style.opacity = '0'; carouselImg.style.opacity = '0';
@ -55,8 +131,8 @@ document.addEventListener('DOMContentLoaded', () => {
} }
}; };
document.querySelector('.carousel-arrow.left')?.addEventListener('click', () => rotateScreenshot(-1)); document.querySelector('.carousel-arrow.left')?.addEventListener('click', () => rotateScreenshot(-1, true));
document.querySelector('.carousel-arrow.right')?.addEventListener('click', () => rotateScreenshot(1)); document.querySelector('.carousel-arrow.right')?.addEventListener('click', () => rotateScreenshot(1, true));
const repo = 'Emerald-Legacy-Launcher/Emerald-Legacy-Launcher'; const repo = 'Emerald-Legacy-Launcher/Emerald-Legacy-Launcher';
const modal = document.getElementById('download-modal'); const modal = document.getElementById('download-modal');
@ -102,7 +178,6 @@ document.addEventListener('DOMContentLoaded', () => {
const specific = assets.find(a => a.name.endsWith('.AppImage') && a.name.includes(archTag)); const specific = assets.find(a => a.name.endsWith('.AppImage') && a.name.includes(archTag));
if (specific) return specific; if (specific) return specific;
// Fallback to any AppImage
return assets.find(a => a.name.endsWith('.AppImage')); return assets.find(a => a.name.endsWith('.AppImage'));
} }
return null; return null;
@ -111,6 +186,7 @@ document.addEventListener('DOMContentLoaded', () => {
const openDownloadModal = (osType: string) => { const openDownloadModal = (osType: string) => {
if (!modal || !modalOptions || !modalTitle) return; if (!modal || !modalOptions || !modalTitle) return;
audio.playSFX('click.wav');
const { arch } = detectOS(); const { arch } = detectOS();
let filteredAssets = []; let filteredAssets = [];
let title = 'Select Downloader'; let title = 'Select Downloader';
@ -133,7 +209,7 @@ document.addEventListener('DOMContentLoaded', () => {
const isRecommended = asset.name === recommended?.name; const isRecommended = asset.name === recommended?.name;
return ` return `
<div class="modal-btn-container"> <div class="modal-btn-container">
<a href="${asset.browser_download_url}" class="main-btn"> <a href="${asset.browser_download_url}" class="main-btn" onclick="window.playAudioSFX('levelup.ogg')">
<span>${asset.name.split('_').pop()?.split('-').pop() || asset.name}</span> <span>${asset.name.split('_').pop()?.split('-').pop() || asset.name}</span>
</a> </a>
${isRecommended ? '<span class="splash-text">Recommended!</span>' : ''} ${isRecommended ? '<span class="splash-text">Recommended!</span>' : ''}
@ -144,6 +220,8 @@ document.addEventListener('DOMContentLoaded', () => {
modal.classList.add('active'); modal.classList.add('active');
}; };
(window as any).playAudioSFX = (file: string) => audio.playSFX(file);
const updateDownloadButtons = async () => { const updateDownloadButtons = async () => {
try { try {
const response = await fetch(`https://api.github.com/repos/${repo}/releases/latest`); const response = await fetch(`https://api.github.com/repos/${repo}/releases/latest`);
@ -174,11 +252,24 @@ document.addEventListener('DOMContentLoaded', () => {
} }
}; };
closeModal?.addEventListener('click', () => modal?.classList.remove('active')); closeModal?.addEventListener('click', () => {
modal?.addEventListener('click', (e) => { if (e.target === modal) modal.classList.remove('active'); }); audio.playSFX('back.ogg');
window.addEventListener('keydown', (e) => { if (e.key === 'Escape') modal?.classList.remove('active'); }); modal?.classList.remove('active');
});
modal?.addEventListener('click', (e) => {
if (e.target === modal) {
audio.playSFX('back.ogg');
modal.classList.remove('active');
}
});
window.addEventListener('keydown', (e) => {
if (e.key === 'Escape') {
if (modal?.classList.contains('active')) audio.playSFX('back.ogg');
modal?.classList.remove('active');
}
});
updateDownloadButtons(); updateDownloadButtons();
setInterval(() => rotateScreenshot(1), 5000); setInterval(() => rotateScreenshot(1, false), 5000);
}); });

View file

@ -89,8 +89,46 @@ body {
.menu-header { .menu-header {
margin-bottom: 20px; margin-bottom: 20px;
position: relative;
display: flex;
justify-content: center;
width: 100%;
} }
.audio-btn {
position: absolute;
right: 40px;
top: 50%;
transform: translateY(-50%);
background: #000;
border: 4px solid #7c7c7c;
border-right-color: #262626;
border-bottom-color: #262626;
color: #fff;
padding: 8px 12px;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
transition: transform 0.1s;
z-index: 100;
font-size: 1.2rem;
}
.audio-btn:hover {
transform: translateY(-50%) scale(1.1);
outline: 4px solid var(--highlight-yellow);
}
.audio-btn i {
transition: color 0.2s;
}
.audio-btn.muted i {
color: #666;
}
.menu-title { .menu-title {
max-width: 400px; max-width: 400px;
width: 90%; width: 90%;