# Système sonore BIG EGG — analyse et mandat d'implémentation

État : **plan à implémenter**, préparé le 19 septembre 2026. Analyse du projet
`big-egg-3d/`, schéma 70, HEAD observé `00302ab`. Ce document et ses annexes
préparent le travail du prochain agent. Aucun lecteur audio, changement de données
de jeu ou import de WAV n'a été réalisé pendant cette préparation.

## 1. Mandat et démarrage

Implémenter un système sonore complet, configurable dans le gestionnaire PHP :
effets d'interface et de restaurant, joueur, armes, monstres et **chaque attaque
du monstre**, musique d'accueil et de restaurant, musique et ambiance des salles
avec valeurs par défaut par biome. Chaque événement propose plusieurs sons avec
poids et volume. Ensuite importer les fichiers pertinents de la banque fournie
et peupler les configurations : livrer un jeu effectivement sonorisé.

Travailler uniquement dans `big-egg-3d/`. `big-egg/` reste une référence en lecture
seule. Lire le `CLAUDE.md` concis à la racine de BIG EGG, les instructions locales,
puis ce document. Commencer par `python tools/dev/dev.py resume` depuis le projet.
Les agents se relaient **séquentiellement** ; aucun dispositif de travail simultané
n'est nécessaire pour ce chantier.

Des modifications utilisateur du maître, du brouillon et des sauvegardes PHP
étaient déjà présentes à l'analyse. Les conserver. Avant la migration de schéma,
faire la sauvegarde prescrite par `CLAUDE.md`, relever les hashes des fichiers
concernés et établir une baseline ciblée. Recontrôler la version du schéma au
moment d'agir : ne pas supposer qu'elle sera encore 70.

Livrables attendus : données et migration, éditeurs PHP/JS, lecture Godot, sons
importés avec provenance, tests, rapport des événements couverts/manquants et
relève à jour. Les candidats annexés sont un point de départ, pas une limite au
peuplement demandé. Aucun MCP spécialisé ou service audio payant n'est requis.

## 2. Constat vérifié dans le projet

| Sujet | Situation et conséquence |
| --- | --- |
| Simulation | Logique 2D conservée, présentation par `Bridge3D`. Les sons de gameplay doivent suivre les événements de simulation, indépendamment du modèle 3D et de ses animations. |
| Audio actuel | Aucun système de lecture/catalogue audio trouvé dans les scripts du jeu. `GameState.master_volume = 0.8`, `apply_audio_settings()` et le curseur Options existent déjà. Les étendre. |
| Données | `data/game_data_master.json`, miroir brouillon PHP, normalisation PHP dans `config.php` et Godot dans `scripts/core/data_catalog.gd`. Version observée : 70 dans les trois emplacements. |
| Contenu | 48 monstres, 34 patterns d'attaque, 2 armes, 1 configuration joueur, 108 salles, 7 biomes. Certains monstres ont des variantes `levels`. |
| Attaques | `enemies[id].attacks` porte pattern, animation, dégâts, cooldown et poids. L'attaque choisie doit aussi porter ses propres sons. Les lignes n'ont pas actuellement d'identifiant stable. |
| Synchronisation | `AttackTimeline` exécute les patterns simples et avancés. `plan.hits` contient aussi les zones continues republiées à chaque image : ce n'est pas un signal « nouvelle attaque ». |
| Éditeur pondéré | `weightGrid()` et `waveCard()` gèrent `waves[index].enemies`. Réutiliser cette expérience d'ajout/retrait, mais créer un composant adapté aux lignes audio. |
| Assets PHP | `ASSET_EXTENSIONS` dans `api.php` ne contient actuellement aucun format audio. Le catalogue, la préécoute et les validations doivent être étendus. |
| Chargement PHP | L'initialisation appelle `reset_draft` et repart du maître publié. Une simple ouverture de l'éditeur réel peut remplacer un brouillon : utiliser le harnais de prévisualisation pour développer/tester. |
| Réglages | `GameState.load_settings/save_settings/apply_settings` reste l'autorité. Aucun second fichier de préférences audio. |

Les documents anciens peuvent encore décrire les biomes comme de simples palettes.
Mettre cette description à jour quand les paramètres audio seront ajoutés.

## 3. Architecture recommandée

Créer trois responsabilités petites et distinctes :

1. **Contrat audio** : événements autorisés, valeurs par défaut, formes de données
   et validations cohérentes PHP/Godot ; pas de logique de combat.
2. **Résolution pure**, par exemple `scripts/core/audio_resolver.gd` : héritage,
   validation des références, sélection pondérée avec RNG injecté. Testable sans
   périphérique audio ni scène complète.
3. **`AudioDirector` autoload**, par exemple `autoload/audio_director.gd` : lecteurs,
   cache borné, buses, transitions, boucles et nettoyage. Les acteurs lui signalent
   des événements ; il ne décide jamais des dégâts, mouvements ou récompenses.

API indicative : `play_event(event_key, context)`, `start_loop(event_key, context)`,
`stop_loop(handle)`, `set_context(context)`, `release_scene(scene_token)`.
Le contexte contient les définitions déjà résolues de l'acteur/attaque/arme,
position 2D, identifiant d'émetteur, jeton de scène et identifiant d'action.
Éviter des signatures différentes pour chaque monstre. Retourner un handle
facultatif, jamais une condition nécessaire à la réussite de l'action.

**Règles impératives :**

- Audio purement cosmétique : aucune attente de fin de son dans le gameplay.
- RNG indépendant via `RandomNumberGenerator`, jamais `randf()` global pour les
  variantes, la probabilité, le pitch ou les ambiances. Activer/couper l'audio
  doit laisser identiques les tirages de combat et de butin.
- Jouer aux événements effectivement validés, pas dans `_draw`, sur chaque
  `state_changed`, ni à chaque tick de collision. Charger une sauvegarde ne
  rejoue pas les sons historiques d'achats, dégâts ou plats terminés.
- Cache des résolutions invalidé lorsque le catalogue est rechargé ; aucun
  parcours des 108 salles ou de toute la banque à chaque attaque.
- Échec audio toléré : fichier manquant/codec invalide donne un diagnostic
  limité et aucun crash, aucune mutation des statistiques.

### Espace sonore, buses et limites

Employer `AudioStreamPlayer` pour UI, musique et ambiance globale. Pour les
effets du monde, privilégier `AudioStreamPlayer2D` avec les positions de simulation
et un `AudioListener2D` explicite suivant le joueur. Le listener et les émetteurs
doivent partager le bon Viewport/World2D ; vérifier le panoramique avec le pont 3D
actif et dans une salle qui défile. Le centre implicite d'écran ne suffit pas à
garantir cette cohérence. Cette proposition suit la séparation actuelle simulation
2D/rendu 3D. Godot décrit cette atténuation et le remplacement du point d'écoute
dans [AudioStreamPlayer2D](https://docs.godotengine.org/en/stable/classes/class_audiostreamplayer2d.html)
et [AudioListener2D](https://docs.godotengine.org/en/stable/classes/class_audiolistener2d.html).

Ne pas calibrer une atténuation 3D en mètres sur les coordonnées actuelles
« 1 pixel = 1 unité ». Si le test du Viewport révèle un obstacle réel, documenter
une adaptation des positions ; conserver une seule autorité de déclenchement.

Buses proposées : `Master` ← `Music`, `Ambience`, `SFX`, `UI`. Ajouter un layout
audio versionné et garantir son chargement avant l'application des préférences.
Ajouter aux Options les quatre volumes de catégorie, en conservant le volume
principal actuel et les anciennes préférences. Nouvelles clés traduites via `Loc`.
Le mute d'un bus ne doit pas écraser le volume de ses enfants. Un gain de fondu
est distinct du gain du clip et du volume utilisateur.

Budget initial à mesurer : 32 voix SFX, 4 UI, 2 lecteurs musique et 2 ambiance
pour les fondus. Les boucles locales de cuisine occupent le budget SFX. Prévoir
des réservations/priorités pour UI, joueur touché et mort ; plafonds par événement
et par émetteur. Écarter un son peu prioritaire ou interrompre proprement une
ancienne voix plutôt qu'accumuler une file d'attente. Ces nombres sont des
réglages de départ, pas une performance prétendument mesurée.

Les lecteurs appartiennent au directeur, pas au monstre détruit : une mort peut
finir de sonner après `queue_free()`. Les boucles, elles, ont un propriétaire
faible et un jeton de scène ; leur libération les arrête. Précharger les petits
sons nécessaires à la scène suivante, avec cache borné et libération des sons
inactifs. Ne pas précharger les 14 803 fichiers. Ne pas lire le disque à chaque hit.

## 4. Contrat de données proposé

Les noms suivants sont recommandés pour limiter les ambiguïtés entre agents.
La migration devra les intégrer aux normaliseurs existants ; ils **n'existent
pas encore**. Garder les identifiants d'événements en `snake_case`, sans point
littéral, car le gestionnaire utilise les points pour adresser les champs imbriqués.

| Emplacement | Contenu |
| --- | --- |
| `audio_clips[id]` | Catalogue des fichiers : `display_name`, `path`, `kind` (`sfx`, `music`, `ambience`), `tags`. Un identifiant réutilisable, pas une copie par affectation. |
| `audio_defaults.events[event_key]` | Sons généraux et valeurs terminales, par exemple `ui_confirm`, `player_hurt`, `enemy_attack_release`, `restaurant_serve`. |
| `audio_defaults.contexts.main_menu/restaurant/dungeon` | Deux canaux indépendants `music` et `ambience`. Prévoir `intro` héritant du menu si aucun choix propre. |
| `player_configs[id].sounds[event]` | Joueur : pas, dash, blessure, mort et valeurs d'attaque par défaut. |
| `weapons[id].sounds[event]` | Surcharge de l'attaque/équipement de cette arme, principale comme secondaire. |
| `enemies[id].sounds[event]` | Apparition, blessure, mort, alerte, bruit d'inactivité, pas et valeurs d'attaque par défaut. Préserver les variantes `levels`. |
| `enemies[id].attacks[index].sounds[event]` | Sons de **cette ligne d'attaque** : préparation, déclenchement, impact, interruption et fin éventuelle. |
| `rooms[id].audio` et `biomes[id].audio` | Canaux `music`, `ambience`. Les champs visuels existants `ambient_color/energy` gardent leur sens visuel. |
| `rooms[id].sounds` et `biomes[id].sounds` | Sons ponctuels de salle : entrée, vague, salle terminée, accès ouvert/bloqué, récompense. |

Pas de configuration sonore portée principalement par `attack_patterns` : deux
monstres partageant un pattern doivent pouvoir avoir des sons différents, et deux
attaques du même monstre aussi. Ne pas placer ces réglages dans `game_variables`,
les modificateurs ou les listes d'effets de gameplay `on_enter/on_hurt`.

### Un événement ponctuel et ses variantes

Exemple de valeur de `enemies.crazy_squirrel.attacks[0].sounds.attack_release`.
Les IDs de clips ci-dessous sont illustratifs, à créer lors de l'import :

```json
{
  "mode": "custom",
  "variants": [
    {"clip": "bite_short_a", "weight": 3, "volume_db": -8, "pitch_min": 0.97, "pitch_max": 1.03},
    {"clip": "bite_short_b", "weight": 1, "volume_db": -10, "pitch_min": 0.98, "pitch_max": 1.02}
  ],
  "probability": 1,
  "cooldown_seconds": 0.06,
  "max_instances": 2,
  "avoid_repeat": true
}
```

Le poids choisit la variante ; il n'est ni un volume ni une chance de déclencher
l'événement. `probability` s'applique une seule fois à l'événement logique.
`avoid_repeat` exclut le dernier clip s'il reste une autre variante éligible ;
avec un seul clip, il le conserve, sans boucle de recherche. L'historique et le
cooldown sont portés par `(scene, emitter, event)` ; l'UI emploie un émetteur global.
Les limites globales du directeur s'ajoutent à `max_instances` par émetteur/événement.

Valeurs par défaut : poids 1, volume -6 dB, pitch 1, probabilité 1, cooldown 0,
2 instances, anti-répétition activée. Les politiques adaptées aux événements
fréquents peuvent être proposées lors du peuplement.

Valider des nombres finis : poids >= 0, somme des poids actifs > 0 pour `custom`,
volume entre -60 et +6 dB, `0.5 <= pitch_min <= pitch_max <= 2`, probabilité [0,1],
cooldown [0,60] secondes, instances entier [1,16]. Éviter les doublons de `clip`
dans une même liste ; augmenter le poids pour augmenter sa fréquence.
Tout champ rejeté doit être signalé précisément dans le gestionnaire.

### Héritage et silence : contrat strict

| Configuration | Résultat |
| --- | --- |
| Champ absent ou `{"mode":"inherit"}` | Chercher au niveau suivant. |
| `{"mode":"silent"}` | Silence volontaire ; arrêter la résolution. |
| `custom` valide | Utiliser cette configuration complète ; ne pas fusionner ses variantes avec celles du parent. |
| `custom` vide, clip inexistant, poids tous nuls | Erreur de validation à la publication ; message local précis. |
| Probabilité 0, poids d'une ligne 0, cooldown ou limite atteinte | Ne pas jouer ; ne pas réactiver un son hérité. |
| Fichier devenu introuvable à l'exécution | Exclure la variante défectueuse, avertir une fois ; si aucune n'est jouable, silence, sans cascade imprévisible. |

Un `custom` hérite uniquement des valeurs techniques par défaut de son propre
type de configuration ; `silent` ne dépend jamais de l'existence d'un fichier.
Le registre d'événements détermine le bus, la priorité, la spatialisation et si
l'événement est une boucle ou un son ponctuel. Éviter ces choix techniques dans
chaque ligne d'édition.

| Événement demandé | Ordre de résolution, du plus précis au défaut terminal |
| --- | --- |
| Attaque de monstre | `attaque.sounds.attack_*` → `monstre.sounds.attack_*` → `audio_defaults.events.enemy_attack_*` |
| Monstre blessé/mort/alerte… | `monstre.sounds.hurt/death/alert/...` → `audio_defaults.events.enemy_hurt/death/alert/...` |
| Attaque du joueur | `arme.sounds.attack_*` → `joueur.sounds.attack_*` → `audio_defaults.events.player_attack_*` |
| Joueur blessé/mort/dash/pas | `joueur.sounds.hurt/death/dash_start/footstep/...` → défaut global `player_*` correspondant ; aucune surcharge par l'arme. |
| Attaque sans arme/monstre à contact direct | Section de l'acteur → défaut global de sa catégorie. |
| Son ponctuel de salle | `salle.sounds[event]` → `biome.sounds[event]` → `audio_defaults.events.dungeon_[event]` |
| Musique ou ambiance de salle | `salle.audio[channel]` → `biome.audio[channel]` → `audio_defaults.contexts.dungeon[channel]` |
| UI et restaurant | `audio_defaults.events.ui_*` ou `restaurant_*`. Une surcharge par meuble peut être ajoutée ensuite si nécessaire. |

Résoudre d'abord la variante de niveau du monstre avec les règles actuelles du
catalogue. Une surcharge sonore de niveau doit conserver l'héritage de la base
quand le champ manque ; ne pas effacer ses attaques ou ses autres champs.
La provenance de la valeur effective doit pouvoir être affichée dans l'éditeur.

### Musiques, ambiances et boucles locales

Configuration de canal continu, indépendante des paramètres de déclenchement :

```json
{
  "mode": "custom",
  "variants": [
    {"clip": "restaurant_theme_a", "weight": 1, "volume_db": -14}
  ],
  "playback": "loop_one",
  "fade_seconds": 1.2,
  "avoid_repeat": true
}
```

Même triade `inherit/silent/custom`, même composant de variantes ; pitch fixé à 1
pour ces canaux. `loop_one` choisit un clip à l'entrée du profil et le boucle.
`weighted_playlist` désactive la boucle du clip et choisit le suivant à sa fin.
Valider `fade_seconds` dans [0,10]. Une ambiance et une musique peuvent jouer
ensemble ; laisser l'ambiance vide dans une salle doit hériter même si sa musique
est surchargée. Ne pas stocker un unique champ mélangeant les deux.

Les boucles locales (`cooking_loop`, `washing_loop`, par exemple) utilisent la
sélection d'un événement SFX et `start_loop/stop_loop`. Sélection une seule fois
au démarrage du travail, jamais à chaque tick. Une instance par station/travail,
arrêt explicite à la fin, annulation, disparition du meuble ou changement de scène.
Pas de minuteur caché dans une ressource d'attaque pour programmer des effets.

## 5. Gestionnaire PHP : expérience attendue

Ajouter une entrée principale **Sons** avec catalogue filtrable/recherchable,
préécoute et affectations globales regroupées : interface, joueur par défaut,
monstres par défaut, restaurant, donjon, accueil/musiques/ambiances. Il doit être
possible de trouver les utilisations d'un clip et d'empêcher sa suppression s'il
est encore référencé, ou de le remplacer explicitement partout.

Sur monstre, joueur et arme : nouvelle section **Sons**. Dans chaque carte d'attaque
du monstre : sous-section **Sons de cette attaque**, toujours éditée dans cette
fiche et dans la variante de niveau actuellement sélectionnée. Biomes et salles :
section **Audio**, deux canaux et événements de salle distincts.

Interaction commune :

- Sélecteur d'état/événement avec libellé français, explication de son déclenchement.
- Choix **Hériter / Personnaliser / Silencieux** ; afficher la source héritée.
- Dans Personnaliser : bouton +, choix du clip, bouton écouter/stop, poids, volume
  en dB, bouton retirer. Comme l'ajout de monstres à une vague, sans imposer la
  forme `{id: poids}` qui ne peut pas porter les autres paramètres audio.
- Pitch, probabilité, délai minimal, plafond et anti-répétition dans les options
  avancées ; masquer les champs non applicables aux canaux continus.
- Filtrer SFX/musique/ambiance ; afficher durée, nom original, tags et fichier
  manquant. Une liste vide personnalisée montre une erreur, pas un héritage tacite.
- Une seule préécoute active, arrêt à la fermeture/changement de fiche. Le navigateur
  démarre l'audio sur un geste utilisateur. Le gain de préécoute doit correspondre
  aux dB configurés ; si Web Audio est utilisé, conserver un unique AudioContext.
- Ne jamais lancer les sons du jeu automatiquement pendant l'édition des données.

Points techniques à traiter ensemble :

| Fichier existant | Travail requis |
| --- | --- |
| `tools/game-data-manager/config.php` | `DATA_SCHEMA_VERSION`, `validate_and_normalize`, nouveaux validateurs de clips/cues/canaux ; conserver les champs dans `normalize_player_configs`, `normalize_weapons`, `normalize_enemies` et ses niveaux/attaques, `normalize_biomes`, `normalize_rooms`. |
| `scripts/core/data_catalog.gd` | `SCHEMA_VERSION`, chargement/collections, normaliseurs miroirs dont `_normalize_enemy_attacks`. Charger le catalogue audio avant de valider ses références. |
| `tools/game-data-manager/assets/app.js` | Collections, `MAP_COLLECTIONS`/restauration des formes, constructeur de nouvel objet, clonage, rendu/bindings/sauvegarde ; étendre `enemyAttackEditor`, `enemyEditor`, `playerConfigEditor`, `weaponEditor`, `biomeEditor`, `roomEditor`. |
| Même JS : `weightGrid`, `weightEntries`, `writeWeightEntries` | Référence UX. Créer `audioCueEditor`/`audioVariantsEditor` dédiés : ne pas transformer les poids numériques des monstres de salle en objets audio. |
| `tools/game-data-manager/api.php` | Ajouter WAV/OGG/MP3 aux formats audio pris en charge ; catalogue filtré, informations mises en cache et préécoute. Conserver les contrôles `realpath` et de périmètre du projet. |
| `tools/game-data-manager/bulk_workbook.php` | Garantir qu'un aller-retour Excel ne supprime pas les nouveaux champs imbriqués. S'ils ne sont pas éditables dans Excel, les préserver explicitement et documenter ce choix. |
| Aide/README du gestionnaire | Documenter sons, héritage, paramètres et nouvelle responsabilité des biomes. |

Respecter `json_map`/la représentation objet des dictionnaires vides ; ne pas
transformer `sounds: {}` en `[]`. Vérifier ajout, clonage, variantes de niveau,
export/import JSON, Excel, publication et rechargement. Les identifiants des
nouvelles sections doivent être stables avec le rangement local des pages.

Les chemins du maître sont uniquement `res://assets/audio/...`, jamais le dossier
Downloads. Valider les chemins canoniques, extension/type, références et nombres.
La préécoute ne doit pas devenir une lecture arbitraire de fichiers Windows ;
réutiliser le routage d'assets sûr, les bons MIME et une réponse adaptée à l'audio.
Le catalogue PHP ne scanne que les assets sélectionnés du projet. L'inventaire
externe est une opération d'import séparée, jamais une dépendance à chaque requête.
Vérifier aussi les presets d'export Godot : les assets audio référencés par chaînes
dans le JSON doivent être inclus dans le jeu exporté, même sans référence directe
depuis une scène. Tester au moins un export avec lecture réelle d'un de ces clips.

Migration : incrémenter le schéma en synchronisant maître et constantes PHP/Godot,
avec traitement explicite des anciens documents sans champs audio. Le validateur
actuel refuse une version différente : il faut une migration versionnée avant
la validation stricte, pas seulement ajouter des champs au JSON. Traiter maître
et brouillon séparément, préserver leurs modifications propres, et conserver les
gardes de version/hash contre les écritures périmées. Ne pas invoquer `reset_draft`
pour « simplifier » cette migration. Aucune configuration de clips dans les
sauvegardes de partie ; les préférences de volumes restent dans le fichier
de réglages déjà géré par `GameState`.

## 6. Synchronisation des attaques et des dégâts

### Attaques des monstres

`enemy.gd::_try_begin_weighted_attack()` choisit une ligne et
`_apply_attack_definition()` copie ses propriétés. Copier à cet instant un contexte
audio immuable de la ligne sélectionnée, incluant la variante de niveau résolue.
Le poids qui choisit une attaque reste indépendant du poids de ses sons.

Émettre `attack_windup` une seule fois après acceptation de l'attaque.
Émettre `attack_release` une seule fois par début de bloc de frappe effectif.
Pour un projectile, ce déclenchement correspond au tir de la volée ; ne pas le
doubler dans chaque projectile. Une attaque à plusieurs blocs peut avoir plusieurs
sons de déclenchement, mais jamais un son à chaque frame d'une zone continue.

`AttackTimeline.advance()` fournit déjà `_hit_index` sur les blocs. Conserver un
ensemble « blocs déjà sonorisés » par séquence chez l'acteur, ou exposer des
transitions pures supplémentaires dans le plan. En cas d'image longue, respecter
les blocs effectivement exécutés et les passages d'instants de la timeline ;
ne pas corriger subrepticement la détection de dégâts dans le chantier audio.
Un bloc continu entièrement sauté qui n'a produit aucun hit effectif ne doit pas
inventer une frappe audible. Garder un test de ce cas.

La timeline reste un calcul sans lecture audio. Pas de nouvelle horloge qui
recopie approximativement `windup/active/recovery`. Incrémenter un jeton de séquence
à chaque attaque ; une interruption/mort/invalidation annule les événements
différés et boucles de cette séquence. `attack_end` correspond à la fin normale,
`attack_interrupt` à l'annulation, jamais les deux pour la même fin.

### Joueur, armes, projectiles

Capturer l'arme réellement utilisée par `player.gd::attack(weapon_id_override)`
après les vérifications de stamina/slot/cooldown ; prendre en compte l'arme
secondaire. Conserver ce contexte sur chaque projectile : changer d'arme avant
l'impact ne doit pas changer le son du projectile déjà tiré.

Séparer l'impact de l'attaque (`attack_impact`, source arme/attaque) et la réaction
de la victime (`hurt`/`death`, source victime). Les deux peuvent coexister, avec
gains modérés et plafonds. Décider le résultat au point où les dégâts sont acceptés.
Les méthodes `take_damage` actuelles ne doivent pas être traitées comme si elles
retournaient déjà un booléen. Au besoin ajouter un signal/résultat de dégâts
explicite compatible avec les appels actuels ; ne pas jouer « touché » aveuglément
après chaque appel, puisque les i-frames peuvent refuser les dégâts.

Sur coup létal, jouer la mort une fois et supprimer la réaction vocale `hurt`
par défaut ; un bruit d'impact physique peut rester. Les impacts continus sont
limités par action/cible et cadence audio : un plafond global protège aussi les
attaques de zone. Les collisions murales et parades utilisent leurs événements
propres. La libération d'un projectile par portée maximale n'est pas un impact.

## 7. Matrice de couverture et points d'accroche

Cette matrice est à convertir en checklist suivie. Inspecter les branches de
succès de chaque fonction avant insertion ; les ancres sont des points d'entrée,
pas une instruction de jouer au début de toutes les fonctions.

| Domaine | Événements à couvrir | Points d'entrée existants |
| --- | --- | --- |
| UI | Confirmation/clic, retour, navigation clavier/manette, ouverture/fermeture, erreur, changement de curseur de volume limité en cadence | `scripts/core/main_menu.gd`, `options_menu.gd`, `pause_menu.gd`, menus recettes/récompenses/console ; `scripts/core/action_slots.gd` pour les contrôles dessinés. |
| Joueur | Pas/courses selon déplacement réel, dash accepté, blessure, mort, soin/consommable, équipement, interaction réussie/refusée | `scripts/combat/player.gd` : `attack`, `_advance_attack_pattern`, `_try_dash`, `interact`, `take_damage`. |
| Monstres | Apparition, alerte/perte d'aggro, sons d'inactivité espacés, pas pertinents, blessure, mort ; préparation/déclenchement/impact/interruption de chaque attaque, contact direct | `scripts/combat/enemy.gd` : `awareness_state_changed`, `_apply_attack_definition`, `_advance_attack`, `_perform_direct_hit`, `_try_attack_hit`, `_interrupt_attack`, `take_damage`, `die`. |
| Projectiles | Tir de volée, collision cible, collision mur, explosion si présente | `_spawn_projectile_volley` dans joueur/ennemi, `scripts/combat/player_projectile.gd`, `enemy_projectile.gd::_hit`. Ne pas sonoriser chaque `queue_free`. |
| Restaurant : cuisine | Prise/dépôt d'objet, démarrage/annulation cuisson, cuisson en boucle, plat prêt, service, lavage en boucle et terminé, débarrassage, déchets | `scripts/restaurant/restaurant.gd` : `handle_interaction`, `handle_station_interaction`, `start_cooking`, `cancel_cooking`, `serve_customer`, `_wash_held_plate`, `_advance_washing`, `_finish_washing` ; retracer la fin de cuisson dans la simulation. |
| Restaurant : activité | Client qui entre/commande/part mécontent, pourboire, vente à emporter, achat/équipement/recrutement, placement/déplacement/rotation/retrait de meuble, portes existantes | Même restaurant, `customer.gd`, `tip_pickup.gd::collect`, appels GameState des opérations correspondantes. Utiliser les opérations confirmées, y compris celles des employés visibles. |
| Donjon | Entrée/sortie de salle, vague, salle terminée, warp ouvert/bloqué/emprunté, récompense offerte/choisie, ingrédient ramassé, fin de plongée | `scripts/combat/dungeon.gd` : `_enter_room`, `_leave_room`, `_release_wave`, `_offer_rewards`, `_on_reward_chosen`, `_on_player_died`, `advance_to_next_room` ; `ingredient_pickup.gd`. |
| Départ comique | Chauffe, sprint, objets projetés limités, explosion du mur | `scripts/core/dungeon_departure_3d.gd` : `_wind_up`, `_sprint`, `_launch_crossed`, `_break_wall`, `_complete`, `_restore`, `_exit_tree`. Hooks de cette mise en scène cosmétique ; pas de son par brique. |
| Musique/ambiance | Accueil/intro, restaurant, salle/biome, pause, mort et retour | `main_menu.gd`, `intro_comic.gd`, scène restaurant et `_enter_room/_leave_room` du donjon ; transitions confiées au directeur. |

Règles de couverture :

- Les `BaseButton` peuvent être raccordés automatiquement une seule fois, avec
  exclusion des contrôles déjà sonorisés. Éviter les doublons `pressed/toggled`
  et clic générique + confirmation métier. Les contrôles dessinés ont un hook
  explicite. Pas de tick au premier focus programmatique. Hover discret et
  désactivable, aucune obligation de le jouer à chaque mouvement de souris.
- Les pas suivent la distance réellement parcourue/la cadence de marche, pas
  une répétition par frame. Immobile contre un mur ou en pause : pas de pas.
- `interactive_prop.gd::interact()` n'atteste pas à lui seul qu'une action a
  réussi. Sonoriser la branche métier, pas tous les essais identiquement.
- Les opérations de restaurant simulées pendant une plongée restent inaudibles
  hors scène. Au retour, reprendre éventuellement une boucle en cours ; ne pas
  rejouer les ventes/clients/fin de cuisson de tout le temps écoulé.
- Les sons du warp sont attachés à son état réel et à son utilisation, jamais
  à son halo vaporeux. La restauration cosmétique du départ reste silencieuse.
- Ne pas inventer de nouvelles mécaniques pour remplir la liste : noter « non
  applicable » si un événement n'existe pas. En revanche couvrir toutes les
  branches existantes d'une interaction, pas uniquement son bouton principal.

## 8. Musique et ambiance : comportement déterministe

Le directeur résout séparément les deux canaux à chaque changement réel de
contexte. Un profil inchangé conserve son morceau et sa position. Passer entre
deux salles héritant du même biome ne relance pas la musique ni le tirage.
Un changement de volume n'est pas un changement de morceau.

Deux lecteurs par canal suffisent pour un fondu entre ancien et nouveau. Un
jeton de transition invalide les anciens chargements/fondus en cas de passages
rapides ; ne jamais ajouter un troisième lecteur durable. Le silence explicite
fait disparaître progressivement le canal concerné seulement. Si une playlist
se termine naturellement, sélectionner la piste suivante avec le RNG audio.

Politique initiale recommandée : en pause, SFX du monde et boucles locales
suspendus, UI utilisable, musique et ambiance maintenues à gain réduit. À la
reprise, restaurer le gain calculé à partir des réglages utilisateur actuels,
pas une ancienne valeur mémorisée. À la mort/changement de scène, annuler les
événements futurs, fermer les boucles de scène et fondre vers le nouveau contexte.

Ne pas modifier une ressource `AudioStream` partagée pour changer la boucle d'une
seule utilisation. Préparer les modes de ressource adaptés ou dupliquer la
ressource quand nécessaire. Une playlist utilise des morceaux non bouclés ; un
fond `loop_one` utilise une vraie boucle. Le signal de fin n'est pas un minuteur
de boucle. Godot documente les options de boucle et les formats WAV, Ogg Vorbis
et MP3 dans [Importing audio samples](https://docs.godotengine.org/en/stable/tutorials/assets_pipeline/importing_audio_samples.html).

## 9. Banque fournie : résultats et méthode de peuplement

Source : `C:\Users\ELR17\Downloads\RAREVGSFX 1+2`.
Inventaire des en-têtes réalisé le 19 septembre 2026, sans écoute ni copie audio :

- **14 803 WAV**, 1 400 875 975 octets (environ 1,40 Go décimal).
- 14 802 PCM, un IMA ADPCM (code 17) ; 13 488 mono et 1 315 stéréo.
- 9 160 fichiers ont des métadonnées RIFF INFO ; les champs les plus fréquents
  sont `ISFT` (logiciel, 7 313 fichiers) et `INAM` (titre, 1 849). La présence de
  métadonnées ne signifie donc pas qu'elles décrivent le son.
- 608 fichiers ont une boucle déclarée dans un chunk `smpl` : forcer la boucle
  désactivée pour les effets ponctuels, même si le fichier en suggère une.
- 2 005 sons durent moins de 0,25 s ; 6 709 de 0,25 à moins de 1 s ;
  5 371 de 1 à moins de 5 s ; 688 de 5 à moins de 20 s ; 30 au moins 20 s.
- Aucune erreur de lecture d'en-tête. Cela ne garantit ni la pertinence du son,
  ni la qualité auditive, ni sa réussite d'import dans Godot.

Annexes à **filtrer**, sans les charger entièrement dans le contexte de l'agent :

- [Inventaire CSV](audio/banque-inventaire.csv) : chemin relatif, taille, durée,
  méthode de calcul, format, canaux, fréquence, bits, boucles et INFO JSON.
- [Résumé mesuré](audio/banque-resume.json).
- [Présélection commentée de 73 fichiers](audio/selection-proposee.csv) : événement suggéré,
  fichier exact, durée, indices et niveau de confiance sémantique. Tous les
  candidats restent marqués comme non écoutés et non importés.

Les durées PCM proviennent du nombre d'octets `data` divisé par le débit du
chunk `fmt`. Le fichier ADPCM utilise le nombre d'échantillons `fact` et la
fréquence. Le catalogue ne calcule pas encore les hashes audio, les crêtes,
le silence initial ou le niveau perçu : les mesurer seulement sur la sélection.

### Procédure d'import à réaliser après raccordement

1. **Plan sans écriture du maître.** Reprendre l'inventaire, vérifier les fichiers
   sélectionnés et produire un manifeste : source relative, nom original, SHA-256,
   durée/codec, usages proposés, nouveau chemin/ID, gain, éventuelle conversion,
   raison du choix et état de vérification. Dédupliquer par contenu, pas par nom.
2. **Choix sémantique.** Croiser nom, sous-dossier, titre/commentaire INFO et durée.
   Un mot dans le nom du jeu n'est pas un indice du son : `firered_*` n'identifie
   pas un feu ; les `music_*.wav` de Metal Slug comprennent des voix. Écarter les
   paroles explicites des clics UI/impacts. Ne pas attribuer une vocalisation
   ambiguë à tous les monstres seulement pour augmenter le taux de couverture.
3. **Importer uniquement les candidats retenus** dans `assets/audio/sfx/`,
   `assets/audio/ambience/`, `assets/audio/music/`. IDs ASCII sémantiques stables,
   suffixe de hash si nécessaire ; aucune dépendance runtime à Downloads.
   Garder la provenance dans `assets/audio/import-manifest.json`, hors catalogue
   chargé par le jeu. Une réexécution ne recopie pas les fichiers identiques et
   n'écrase pas les réglages modifiés à la main.
4. **Compatibilité et niveaux.** Les sources sont presque toutes PCM : commencer
   par la copie. Le seul ADPCM observé est `RAREVGSFX 2/XBOX/BASS PRO SHOPS TROPHY
   BASS 2007 (XBOX)/Sound Effects/OVER_SND.WAV` ; convertir seulement s'il est retenu.
   WAV courts pour les sons fréquents ; Ogg Vorbis à envisager pour les longues
   ambiances/musiques. Vérifier l'import réel et la préécoute navigateur, pas
   seulement l'extension. Ces choix correspondent aux usages décrits dans la
   [documentation d'import Godot](https://docs.godotengine.org/en/stable/tutorials/assets_pipeline/importing_audio_samples.html).
5. **Réglages prudents.** Ne pas normaliser aveuglément toutes les crêtes à 0 dB.
   Mesurer les extraits retenus, conserver de la marge, démarrer les impacts
   modérément et les ambiances plus bas. Le gain proposé dans la présélection
   n'est pas un résultat de mesure de niveau. Tester les superpositions réelles.
   Réduire le silence initial des sons réactifs si nécessaire, sans couper leur
   attaque. Tester les raccords des boucles.
6. **Peupler par ordre utile.** Défauts globaux/UI → joueur et deux armes → attaques
   et états des 48 monstres → restaurant → salles/biomes/ambiances → musique
   disponible. Employer 2–4 variantes quand de bons candidats existent, sans
   quota artificiel. Adapter morsure/griffe/charge/tir/frappe au pattern réel.
   Les voix `hurt/death` doivent rester cohérentes avec l'entité.
7. **Limiter la duplication.** Configurer les valeurs communes globalement ou
   par biome ; laisser les salles hériter, sauf ambiance particulière. Le rapport
   compte les événements couverts par héritage aussi bien que par surcharge.
   Ne pas remplir 108 salles avec les mêmes copies de configuration.
8. **Valider puis publier par le flux existant**, avec sauvegarde et gardes de
   concurrence. Vérifier un redémarrage du jeu et une réouverture contrôlée du
   gestionnaire. Produire le rapport final des usages, lacunes et choix incertains.

Repères de tri, non règles absolues : clics de quelques dizaines à 250 ms,
pas de 50–300 ms, attaques/impacts généralement inférieurs à 1 s, réactions de
0,2–3 s. Les longueurs supérieures servent surtout à repérer des ambiances ou
des séquences à examiner ; une longue sirène n'est pas de la musique.

**Limite de la banque :** aucun thème complet approprié n'est confirmé dans cette
analyse. Ne pas annoncer la musique comme peuplée en transformant du bruit ou
des voix en « thème ». Construire et tester tous les canaux et héritages, rechercher
les candidats plausibles, puis indiquer explicitement les morceaux restant à
fournir si la banque ne convient pas. Une audition de la sélection et des scènes
reste nécessaire pour valider le résultat artistique ; les noms/métadonnées/durées
permettent la présélection demandée, pas une certification auditive.

## 10. Ordre d'implémentation et critères de validation

Faire des étapes terminées et reprenables. Utiliser Git local avec des chemins
explicites, conserver les changements utilisateur et ne pas publier de remote.
Ne pas multiplier les outils ou relire tout le projet pour chaque étape.

| Étape | Résultat concret avant passage à la suivante |
| --- | --- |
| 1. Contrat et migration | Schéma audio, migration sauvegardée et normaliseurs PHP/Godot cohérents ; anciens documents chargés sans perte. Fixtures minimales héritage/silence/attaques/niveaux. |
| 2. Directeur et tranche fonctionnelle | Buses, réglages, lecteur borné et RNG séparé ; un clic UI, un dash, un hit fonctionnent avec quelques assets importés. |
| 3. Édition complète | Catalogue et sections joueur/arme/monstre/attaque/biome/salle ; ajout/retrait, poids, volume, héritage, préécoute et publication fiables. |
| 4. Gameplay complet | Attaques simples/avancées, projectiles, états des entités, interactions restaurant, donjon et départ comique raccordés selon la matrice. |
| 5. Contextes continus | Musique/ambiance, héritage salle/biome/global, fondus, pause et nettoyage des boucles ; vérifier le pont 3D. |
| 6. Peuplement | Import idempotent d'une sélection substantielle, affectations cohérentes, niveaux réglés et rapport de couverture incluant les manques. |
| 7. Livraison | Tests ciblés puis vérification transversale adaptée, parcours audible, documentation, checkpoint local et relève. |

### Tests automatisés qui apportent une preuve

- PHP/Godot : mêmes résultats normalisés sur fixtures partagées ; anciennes
  données, dictionnaires vides, silence explicite, custom vide refusé, poids
  invalides, chemin hors projet, référence absente, champs de niveau/attaque.
- JS/PHP : ajout/retrait/édition des variantes, changement de niveau, clonage,
  sauvegarde/rechargement/publication et aller-retour Excel préservent les sons.
  Aucune copie involontaire entre deux attaques partageant un pattern.
- Résolveur : tous les ordres d'héritage, indépendance musique/ambiance, arrêt
  sur `silent`, aucun repli lorsque probabilité 0 ou cooldown atteint. RNG à seed
  fixe pour tirages ; poids nuls jamais choisis, anti-répétition sans blocage.
- Événements : une zone continue produit un déclenchement par bloc, pas par frame ;
  plusieurs blocs restent distincts ; instant traversé par grand delta ; bloc
  continu totalement sauté ; attaque refusée/annulée, mort pendant préparation.
- Dégâts : i-frames sans son de blessure, mort une seule fois, impacts limités,
  queue de son conservée après suppression de la victime. Projectile tiré avec
  une arme puis changement d'arme : ancien contexte conservé à l'impact.
- Isolation : même scénario/seed gameplay avec audio activé et désactivé donne
  mêmes tirages, dégâts et butins. Utiliser un backend audio factice enregistré
  pour vérifier les événements sans exiger un périphérique en headless.
- Cycle de vie : aucune boucle persistante après changement de salle/menu,
  aucun son historique après chargement, plafonds respectés sous combat dense.
- Musique : continuité entre salles héritant du même profil, override/silence,
  transition rapide A→B→C sans retour tardif à B, playlist à un seul morceau,
  fin et interruption explicite nettoyées même sans signal de fin de boucle.
- Réglages : anciens settings, mute, volumes par catégorie, pause/reprise après
  changement de volume, sauvegarde/rechargement.

Enregistrer les nouveaux checks Godot dans le runner partagé `tools/dev/checks.py`
et sa sélection de domaines. Ajouter les tests PHP/JS audio à la sélection manager.
Utiliser `python tools/dev/dev.py check --help` pour les commandes disponibles ;
ne pas présenter un filtre vide comme un test passé. Les scènes brutes peuvent
écrire la sauvegarde réelle : employer l'isolation du runner. Ne pas ouvrir un
éditeur Godot headless concurrent de l'éditeur graphique/MCP.

Après les tests ciblés, faire une seule vérification transversale pertinente aux
changements de schéma/autoload/combat/restaurant. Comparer à une baseline prise
pour ce chantier : les échecs historiques consignés le 17 septembre ne prouvent
pas l'état actuel. Rapport honnête : distinguer nouvelle régression, défaut déjà
présent, test non exécuté et cas non applicable.

### Vérification manuelle et définition de « terminé »

Parcours : accueil → options/mute → restaurant (cuisson, service, lavage, achat)
→ départ comique → salle (plusieurs attaques, dash, dégâts, projectile, mort)
→ autre salle même biome → surcharge de salle/autre biome → retour restaurant.
Ajouter pause/reprise, clavier/manette si disponible, et un combat dense.

Vérifier à l'écoute : clic sans doublon, sons synchronisés, volume équilibré,
pas de saturation, boucle sans clic, panoramique cohérent en 3D, aucune cuisine
audible depuis le donjon. Les logs headless ne prouvent pas ces qualités.

La livraison est terminée lorsque les écrans PHP permettent réellement tous les
réglages demandés, les événements existants sont raccordés, l'héritage est testé,
les assets sélectionnés sont copiés et référencés, et la couverture/les éventuels
manques de musique sont consignés précisément. Un directeur audio vide ou une
collection de fichiers sans affectations ne suffit pas.

Actualiser `docs/current-task.md`, exécuter `python tools/dev/dev.py handoff`, puis
créer un checkpoint des seuls fichiers du chantier avec `dev.py checkpoint`.
La relève indique l'étape achevée, les chemins modifiés, les commandes réellement
passées et leurs résultats, les sons importés, les lacunes et la prochaine action.
