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:
2026-03-03 22:24:17 +01:00
commit e031c9a1d2
155 changed files with 22334 additions and 0 deletions

245
web/js/audio.js Normal file
View 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;
}