Initial commit
**Motivations:** - Initialisation du versionning git pour le projet **Root causes:** - N/A (Nouveau projet) **Correctifs:** - N/A **Evolutions:** - Structure initiale du projet - Ajout du .gitignore **Pages affectées:** - Tous les fichiers
This commit is contained in:
245
web/js/audio.js
Normal file
245
web/js/audio.js
Normal file
@@ -0,0 +1,245 @@
|
||||
let audioCtx = null;
|
||||
|
||||
function getCtx() {
|
||||
if (audioCtx === null || audioCtx === undefined) audioCtx = new (window.AudioContext || window.webkitAudioContext)();
|
||||
return audioCtx;
|
||||
}
|
||||
|
||||
function applySoundUpgrade(osc, now) {
|
||||
osc.frequency.setValueAtTime(659, now);
|
||||
osc.frequency.setValueAtTime(784, now + 0.06);
|
||||
}
|
||||
|
||||
function applySoundHatch(osc, now) {
|
||||
osc.type = "sine";
|
||||
osc.frequency.setValueAtTime(392, now);
|
||||
osc.frequency.setValueAtTime(523, now + 0.08);
|
||||
osc.frequency.setValueAtTime(659, now + 0.16);
|
||||
}
|
||||
|
||||
function applySoundPlace(osc, now) {
|
||||
osc.type = "sine";
|
||||
osc.frequency.setValueAtTime(659, now);
|
||||
osc.frequency.setValueAtTime(523, now + 0.05);
|
||||
osc.frequency.setValueAtTime(784, now + 0.1);
|
||||
}
|
||||
|
||||
function applySoundBuy(osc, now) {
|
||||
osc.type = "sine";
|
||||
osc.frequency.setValueAtTime(523, now);
|
||||
osc.frequency.setValueAtTime(659, now + 0.05);
|
||||
osc.frequency.setValueAtTime(784, now + 0.1);
|
||||
}
|
||||
|
||||
function applySoundSell(osc, now) {
|
||||
osc.type = "triangle";
|
||||
osc.frequency.setValueAtTime(440, now);
|
||||
osc.frequency.setValueAtTime(349, now + 0.06);
|
||||
osc.frequency.setValueAtTime(262, now + 0.12);
|
||||
}
|
||||
|
||||
function applySoundPlotUpgrade(osc, now) {
|
||||
osc.type = "sine";
|
||||
osc.frequency.setValueAtTime(330, now);
|
||||
osc.frequency.setValueAtTime(440, now + 0.06);
|
||||
osc.frequency.setValueAtTime(554, now + 0.12);
|
||||
}
|
||||
|
||||
function applySoundTruckUpgrade(osc, now) {
|
||||
osc.type = "square";
|
||||
osc.frequency.setValueAtTime(220, now);
|
||||
osc.frequency.setValueAtTime(277, now + 0.05);
|
||||
osc.frequency.setValueAtTime(330, now + 0.1);
|
||||
}
|
||||
|
||||
function applySoundSchoolUpgrade(osc, now) {
|
||||
osc.type = "sine";
|
||||
osc.frequency.setValueAtTime(523, now);
|
||||
osc.frequency.setValueAtTime(659, now + 0.05);
|
||||
osc.frequency.setValueAtTime(784, now + 0.1);
|
||||
osc.frequency.setValueAtTime(1047, now + 0.15);
|
||||
}
|
||||
|
||||
function applySoundWorldMapUpgrade(osc, now) {
|
||||
osc.type = "sine";
|
||||
osc.frequency.setValueAtTime(440, now);
|
||||
osc.frequency.setValueAtTime(554, now + 0.06);
|
||||
osc.frequency.setValueAtTime(698, now + 0.12);
|
||||
}
|
||||
|
||||
function applySoundQuest(osc, now) {
|
||||
osc.frequency.setValueAtTime(659, now);
|
||||
osc.frequency.setValueAtTime(784, now + 0.06);
|
||||
}
|
||||
|
||||
function applySoundError(osc, now) {
|
||||
osc.frequency.setValueAtTime(200, now);
|
||||
osc.frequency.setValueAtTime(180, now + 0.08);
|
||||
}
|
||||
|
||||
function applySoundDefault(osc, now) {
|
||||
osc.frequency.setValueAtTime(440, now);
|
||||
}
|
||||
|
||||
const SOUND_APPLIERS = {
|
||||
upgrade: applySoundUpgrade,
|
||||
hatch: applySoundHatch,
|
||||
place: applySoundPlace,
|
||||
buy: applySoundBuy,
|
||||
sell: applySoundSell,
|
||||
plotUpgrade: applySoundPlotUpgrade,
|
||||
truckUpgrade: applySoundTruckUpgrade,
|
||||
schoolUpgrade: applySoundSchoolUpgrade,
|
||||
worldMapUpgrade: applySoundWorldMapUpgrade,
|
||||
quest: applySoundQuest,
|
||||
error: applySoundError,
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {string} type One of: hatch, place, buy, sell, plotUpgrade, truckUpgrade, schoolUpgrade, worldMapUpgrade, upgrade, quest, error
|
||||
* @returns {void}
|
||||
*/
|
||||
export function playSound(type) {
|
||||
try {
|
||||
const ctx = getCtx();
|
||||
const now = ctx.currentTime;
|
||||
const osc = ctx.createOscillator();
|
||||
const gain = ctx.createGain();
|
||||
osc.connect(gain);
|
||||
gain.connect(ctx.destination);
|
||||
gain.gain.setValueAtTime(0.15, now);
|
||||
gain.gain.exponentialRampToValueAtTime(0.01, now + 0.15);
|
||||
osc.start(now);
|
||||
osc.stop(now + 0.15);
|
||||
const applier = SOUND_APPLIERS[type] || applySoundDefault;
|
||||
applier(osc, now);
|
||||
} catch (e) {
|
||||
console.warn("playSound failed", e);
|
||||
}
|
||||
}
|
||||
|
||||
let musicEnabled = false;
|
||||
let musicIntervalId = null;
|
||||
let musicGetState = () => null;
|
||||
const MUSIC_BEAT_MS = 400;
|
||||
|
||||
/**
|
||||
* Register state getter so music can switch by day/night/truck.
|
||||
* @param {() => { timeOfDay?: number, truckSale?: { startAt: number } }} getState
|
||||
*/
|
||||
export function setMusicGetState(getState) {
|
||||
musicGetState = getState || (() => null);
|
||||
}
|
||||
|
||||
function isNight(timeOfDay) {
|
||||
const t = (timeOfDay ?? 6) % 24;
|
||||
return t < 6 || t >= 20;
|
||||
}
|
||||
|
||||
function isTruckMoving(state) {
|
||||
const sale = state.truckSale;
|
||||
if (!sale || !sale.startAt) return false;
|
||||
const truckMs = 2500;
|
||||
return (Date.now() - sale.startAt) < truckMs;
|
||||
}
|
||||
|
||||
function playMusicVoliere() {
|
||||
if (!musicEnabled || (audioCtx === null || audioCtx === undefined)) return;
|
||||
try {
|
||||
const ctx = getCtx();
|
||||
const now = ctx.currentTime;
|
||||
const gain = ctx.createGain();
|
||||
gain.connect(ctx.destination);
|
||||
gain.gain.setValueAtTime(0, now);
|
||||
gain.gain.linearRampToValueAtTime(0.04, now + 0.02);
|
||||
gain.gain.exponentialRampToValueAtTime(0.004, now + 0.25);
|
||||
const freq = 600 + Math.random() * 800;
|
||||
const osc = ctx.createOscillator();
|
||||
osc.type = "sine";
|
||||
osc.frequency.setValueAtTime(freq, now);
|
||||
osc.connect(gain);
|
||||
osc.start(now);
|
||||
osc.stop(now + 0.25);
|
||||
} catch (e) {
|
||||
console.warn("playMusicVoliere failed", e);
|
||||
}
|
||||
}
|
||||
|
||||
function playMusicBrahms() {
|
||||
if (!musicEnabled || (audioCtx === null || audioCtx === undefined)) return;
|
||||
try {
|
||||
const ctx = getCtx();
|
||||
const now = ctx.currentTime;
|
||||
const gain = ctx.createGain();
|
||||
gain.connect(ctx.destination);
|
||||
gain.gain.setValueAtTime(0, now);
|
||||
gain.gain.linearRampToValueAtTime(0.05, now + 0.03);
|
||||
gain.gain.exponentialRampToValueAtTime(0.005, now + 0.5);
|
||||
const waltz = [415.3, 523.25, 622.25];
|
||||
waltz.forEach((freq, i) => {
|
||||
const osc = ctx.createOscillator();
|
||||
osc.type = "sine";
|
||||
osc.frequency.setValueAtTime(freq, now + i * 0.15);
|
||||
osc.connect(gain);
|
||||
osc.start(now + i * 0.15);
|
||||
osc.stop(now + 0.5);
|
||||
});
|
||||
} catch (e) {
|
||||
console.warn("playMusicBrahms failed", e);
|
||||
}
|
||||
}
|
||||
|
||||
function playMusicTrepak() {
|
||||
if (!musicEnabled || (audioCtx === null || audioCtx === undefined)) return;
|
||||
try {
|
||||
const ctx = getCtx();
|
||||
const now = ctx.currentTime;
|
||||
const gain = ctx.createGain();
|
||||
gain.connect(ctx.destination);
|
||||
gain.gain.setValueAtTime(0, now);
|
||||
gain.gain.linearRampToValueAtTime(0.08, now + 0.02);
|
||||
gain.gain.exponentialRampToValueAtTime(0.006, now + 0.12);
|
||||
const osc = ctx.createOscillator();
|
||||
osc.type = "triangle";
|
||||
osc.frequency.setValueAtTime(280, now);
|
||||
osc.frequency.setValueAtTime(350, now + 0.06);
|
||||
osc.connect(gain);
|
||||
osc.start(now);
|
||||
osc.stop(now + 0.12);
|
||||
} catch (e) {
|
||||
console.warn("playMusicTrepak failed", e);
|
||||
}
|
||||
}
|
||||
|
||||
function playMusicTick() {
|
||||
if (!musicEnabled || (audioCtx === null || audioCtx === undefined)) return;
|
||||
const state = musicGetState();
|
||||
if (state && isTruckMoving(state)) {
|
||||
playMusicTrepak();
|
||||
return;
|
||||
}
|
||||
const timeOfDay = state && state.timeOfDay !== null && state.timeOfDay !== undefined ? state.timeOfDay : 6;
|
||||
if (isNight(timeOfDay)) {
|
||||
playMusicBrahms();
|
||||
} else {
|
||||
playMusicVoliere();
|
||||
}
|
||||
}
|
||||
|
||||
/** Call to enable/disable background music (procedural: Volière / Brahms / Trepak). */
|
||||
export function setMusicEnabled(enabled) {
|
||||
if (musicIntervalId !== null && musicIntervalId !== undefined) {
|
||||
clearInterval(musicIntervalId);
|
||||
musicIntervalId = null;
|
||||
}
|
||||
musicEnabled = Boolean(enabled);
|
||||
if (musicEnabled) {
|
||||
getCtx();
|
||||
playMusicTick();
|
||||
musicIntervalId = setInterval(playMusicTick, MUSIC_BEAT_MS);
|
||||
}
|
||||
}
|
||||
|
||||
export function isMusicEnabled() {
|
||||
return musicEnabled;
|
||||
}
|
||||
Reference in New Issue
Block a user