**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
246 lines
7.0 KiB
JavaScript
246 lines
7.0 KiB
JavaScript
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;
|
|
}
|