Merge origin/main into chore/harmonize-zacus
This commit is contained in:
+13
@@ -3,3 +3,16 @@
|
||||
|
||||
# Local IDE settings
|
||||
.vscode/
|
||||
|
||||
# Generated game exports
|
||||
game/exports/*
|
||||
!game/exports/.gitkeep
|
||||
!game/exports/previews/
|
||||
|
||||
# Locally generated audio/render outputs (not versioned)
|
||||
audio/generated/*
|
||||
!audio/generated/README.md
|
||||
|
||||
# Locally generated printable PDFs (not versioned)
|
||||
printables/export/pdf/zacus_v1/*
|
||||
!printables/export/pdf/zacus_v1/README.md
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
# Pipeline audio Zacus v1
|
||||
|
||||
Objectif: pipeline local IA-friendly sans dépendance cloud obligatoire.
|
||||
|
||||
## Outils cibles
|
||||
- TTS: `espeak`, `mbrola`, ou `piper`
|
||||
- Conversion: `ffmpeg` (MP3 mono faible débit)
|
||||
|
||||
## Convention export
|
||||
- Mono, 22.05 kHz ou 44.1 kHz
|
||||
- 48–96 kbps
|
||||
- Nommage depuis `audio/manifests/zacus_v1_audio.yaml`
|
||||
|
||||
## Étapes
|
||||
1. Générer voix (hotline + messages U-SON).
|
||||
2. Générer textures/musiques via prompts (voir `game/prompts/audio/`).
|
||||
3. Normaliser volume.
|
||||
4. Valider présence des fichiers avec `tools/audio/validate_manifest.py`.
|
||||
|
||||
## Génération locale rapide (placeholder)
|
||||
Si vous voulez débloquer rapidement des tests sans TTS externe:
|
||||
- `python3 tools/audio/generate_local_assets.py`
|
||||
|
||||
Ce script crée `audio/generated/*` en réutilisant des MP3 présents dans le repo (placeholders techniques).
|
||||
@@ -0,0 +1,11 @@
|
||||
# audio/generated
|
||||
|
||||
Ce dossier contient des artefacts audio générés localement (MP3), non versionnés.
|
||||
|
||||
Pourquoi: plusieurs workflows d'extraction/patch (et revues diff textuelles) ne gèrent pas bien les binaires.
|
||||
|
||||
Génération locale:
|
||||
- `python3 tools/audio/generate_local_assets.py`
|
||||
|
||||
Validation:
|
||||
- `python3 tools/audio/validate_manifest.py`
|
||||
@@ -0,0 +1,8 @@
|
||||
# Glossaire
|
||||
|
||||
- **U-SON**: module audio narratif principal.
|
||||
- **U-LOCK**: verrouillage/déverrouillage (souvent via LA).
|
||||
- **LA**: note de référence à produire à l’étape 5.
|
||||
- **HOTLINE**: canal Brigade Z (Indice / Validation / Relance).
|
||||
- **Brigade Z**: cellule fictive de support enquête.
|
||||
- **Morse partiel**: indice transcodé via oscilloscope/table.
|
||||
@@ -0,0 +1,4 @@
|
||||
# Prompts IA du jeu
|
||||
|
||||
- Audio: `game/prompts/audio/`
|
||||
- Printables: voir `printables/src/prompts/`
|
||||
@@ -0,0 +1,10 @@
|
||||
# Prompts musique / ambiance
|
||||
|
||||
## Ambiance labo enquête 20–30 min
|
||||
"Musique électronique légère, rétro-futuriste, tempo modéré, textures de laboratoire, petits bips scientifiques, sans voix, répétition douce, adaptée à un jeu d’enquête enfant 9–11 ans."
|
||||
|
||||
## Boucle 1h
|
||||
"Longue boucle ambient techno-lab, peu intrusive, variations subtiles toutes 2–4 minutes, pas de rupture brutale, mix mono-compatible."
|
||||
|
||||
## Message radio brouillé zone 5
|
||||
"Texture radio AM bruitée + voix distante répétant 'au-dessus du sol...', avec motifs fréquentiels et sensation d’indice caché."
|
||||
@@ -0,0 +1,9 @@
|
||||
# Prompt TTS Hotline Brigade Z
|
||||
|
||||
Voix: française claire, rassurante, dynamique.
|
||||
Contraintes: phrases 6–12 mots, articulation nette, pas d'effet robotique exagéré.
|
||||
|
||||
Lignes types:
|
||||
1. "Brigade Z. Hypothèse reçue. Continuez l’analyse."
|
||||
2. "Indice confirmé. Cherchez la lettre cachée dans le signal."
|
||||
3. "Relance Brigade Z. Reprenez QUI, OU, COMMENT."
|
||||
@@ -0,0 +1,3 @@
|
||||
# Schema
|
||||
|
||||
Réservé pour futurs schémas YAML/JSON du canon scénario.
|
||||
@@ -0,0 +1,24 @@
|
||||
# Intégration scénario Zacus v1 <-> hardware
|
||||
|
||||
## Mapping gameplay -> devices
|
||||
- **U-LOCK**: verrou d’entrée mission; déverrouillage vocal/ton LA.
|
||||
- **U-SON**: boot corrompu, progression, jingle victoire.
|
||||
- **Oscilloscope**: sinus ETAPE 1, morse partiel ETAPE 4.
|
||||
- **Téléphones zones**: indices tokens L / AFO / EFO / LE / U.
|
||||
- **Téléphone hotline (salon)**: validation/indice/relance Brigade Z.
|
||||
|
||||
## Préparer la SD
|
||||
Exemple structure:
|
||||
- `/story/` specs story v2 compilées
|
||||
- `/audio/uson/` fichiers U-SON
|
||||
- `/audio/hotline/` répliques courtes
|
||||
- `/audio/zones/` radios brouillées
|
||||
- `/audio/ambiances/` longues boucles
|
||||
|
||||
Nommage conseillé: `zacus_v1_<device>_<cue>_v01.mp3`
|
||||
|
||||
## ETAPE 2 (delay story)
|
||||
- Départ: état verrouillé.
|
||||
- Après déverrouillage + délai paramétré: step `ETAPE2_HINT`.
|
||||
- Action: jouer hint audio salon + garder gate MP3 fermé.
|
||||
- Puis passage `STEP_DONE` pour ouvrir gate MP3 (selon scénario choisi).
|
||||
@@ -0,0 +1,68 @@
|
||||
id: ZACUS_V1_UNLOCK_ETAPE2
|
||||
version: 2
|
||||
initial_step: STEP_WAIT_UNLOCK
|
||||
|
||||
app_bindings:
|
||||
- id: APP_LA
|
||||
app: LA_DETECTOR
|
||||
config:
|
||||
hold_ms: 3000
|
||||
unlock_event: UNLOCK
|
||||
require_listening: true
|
||||
- id: APP_AUDIO
|
||||
app: AUDIO_PACK
|
||||
- id: APP_SCREEN
|
||||
app: SCREEN_SCENE
|
||||
- id: APP_GATE
|
||||
app: MP3_GATE
|
||||
|
||||
steps:
|
||||
- step_id: STEP_WAIT_UNLOCK
|
||||
screen_scene_id: SCENE_LOCKED
|
||||
audio_pack_id: ""
|
||||
actions: [ACTION_TRACE_STEP]
|
||||
apps: [APP_LA, APP_SCREEN, APP_GATE]
|
||||
mp3_gate_open: false
|
||||
transitions:
|
||||
- trigger: on_event
|
||||
event_type: unlock
|
||||
event_name: UNLOCK
|
||||
target_step_id: STEP_UNLOCK_WIN
|
||||
after_ms: 0
|
||||
priority: 100
|
||||
|
||||
- step_id: STEP_UNLOCK_WIN
|
||||
screen_scene_id: SCENE_REWARD
|
||||
audio_pack_id: PACK_WIN
|
||||
actions: [ACTION_TRACE_STEP]
|
||||
apps: [APP_AUDIO, APP_SCREEN, APP_GATE]
|
||||
mp3_gate_open: false
|
||||
transitions:
|
||||
- trigger: on_event
|
||||
event_type: audio_done
|
||||
event_name: AUDIO_DONE
|
||||
target_step_id: ETAPE2_HINT
|
||||
after_ms: 0
|
||||
priority: 100
|
||||
|
||||
- step_id: ETAPE2_HINT
|
||||
screen_scene_id: SCENE_INFO
|
||||
audio_pack_id: PACK_ETAPE2_HINT
|
||||
actions: [ACTION_TRACE_STEP]
|
||||
apps: [APP_AUDIO, APP_SCREEN, APP_GATE]
|
||||
mp3_gate_open: false
|
||||
transitions:
|
||||
- trigger: after_ms
|
||||
event_type: timer
|
||||
event_name: DELAY_ETAPE2
|
||||
target_step_id: STEP_DONE
|
||||
after_ms: 12000
|
||||
priority: 90
|
||||
|
||||
- step_id: STEP_DONE
|
||||
screen_scene_id: SCENE_READY
|
||||
audio_pack_id: ""
|
||||
actions: [ACTION_TRACE_STEP, ACTION_REFRESH_SD]
|
||||
apps: [APP_SCREEN, APP_GATE]
|
||||
mp3_gate_open: true
|
||||
transitions: []
|
||||
@@ -0,0 +1,8 @@
|
||||
# printables/export/pdf/zacus_v1
|
||||
|
||||
Ce dossier contient des PDF générés localement, non versionnés.
|
||||
|
||||
Pourquoi: les binaires PDF ne sont pas adaptés aux diff/patch textuels et aux étapes d'extraction automatisée.
|
||||
|
||||
Génération locale:
|
||||
- `python3 tools/printables/generate_local_pdf_placeholders.py`
|
||||
@@ -0,0 +1,7 @@
|
||||
# Placeholder cartes (structure)
|
||||
|
||||
+---------------------------+
|
||||
| [ZONE Z3] Atelier |
|
||||
| Responsable: Dr Oscilla |
|
||||
| Indice: signal sinus |
|
||||
+---------------------------+
|
||||
@@ -0,0 +1,6 @@
|
||||
# Placeholder feuille enquête
|
||||
|
||||
QUI : ______________________
|
||||
OU : ______________________
|
||||
COMMENT: _____________________
|
||||
PREUVE: ______________________
|
||||
@@ -0,0 +1,7 @@
|
||||
# Prompt packs IA — printables Zacus v1
|
||||
|
||||
Tous les prompts imposent:
|
||||
- style "Cluedo premium × labo rétro-futuriste"
|
||||
- lisibilité N&B
|
||||
- sans logos/marques externes
|
||||
- marges de sécurité + fond perdu
|
||||
@@ -0,0 +1,3 @@
|
||||
Variables: ZONE_NAME, COLOR, ICON, SHORT_DESC
|
||||
Format: A4 portrait.
|
||||
Prompt: Affiche zone immersive avec titre massif, code couleur adaptable, texture labo, lisible de loin, version N&B sans perte d'information.
|
||||
@@ -0,0 +1,3 @@
|
||||
Variables: DETECTIVE_NAME, TEAM_COLOR, BADGE_ID
|
||||
Format: A6 (2 badges par page A4 possible).
|
||||
Prompt: Badge détective rétro-futuriste avec zone prénom manuscrite, code badge mono, contraste imprimable.
|
||||
@@ -0,0 +1,3 @@
|
||||
Variables: ZONE_ID, ZONE_NAME, COLOR, HOME_LOCATION, FEATURE
|
||||
Format: A6 paysage.
|
||||
Prompt: Carte lieu avec code zone, texture papier technique, cadre acier, icônes de station, lisibilité forte en impression monochrome.
|
||||
@@ -0,0 +1,3 @@
|
||||
Variables: ITEM_NAME, ITEM_TYPE, CLUE_TEXT, COLOR
|
||||
Format: A6 portrait.
|
||||
Prompt: Carte objet/indice style dossier d'enquête premium, encarts codés, numéro de preuve, sans marque tierce.
|
||||
@@ -0,0 +1,3 @@
|
||||
Variables: SUSPECT_NAME, ROLE, GIMMICK, ALIBI, SUSPICION_LEVEL, COLOR
|
||||
Format: A6 portrait.
|
||||
Prompt: Carte suspect avec portrait stylisé non-marqué, bandeau nom, bloc alibi, pictogrammes labo, trame circuits fine, déclinable N&B.
|
||||
@@ -0,0 +1,3 @@
|
||||
Variables: TEAM_NAME, STEP_LABEL
|
||||
Format: A4 portrait.
|
||||
Prompt: Feuille enquête QUI/OU/COMMENT avec grilles claires, zones de notes, rappel hotline, hiérarchie typographique robuste N&B.
|
||||
@@ -0,0 +1,3 @@
|
||||
Variables: HOTLINE_NAME, CALL_TYPES, TOKEN_SET
|
||||
Format: A4 portrait.
|
||||
Prompt: Affiche hotline Brigade Z avec script court (Indice/Validation/Relance), tableau tokens téléphones L-AFO-EFO-LE-U, interdiction logos externes.
|
||||
@@ -0,0 +1,3 @@
|
||||
Variables: EVENT_DATE, PLAYER_NAME, LOCATION, DRESSCODE
|
||||
Format: A6 recto/verso, fond perdu 3 mm, marge de sécurité 5 mm.
|
||||
Prompt: Carte invitation rétro-futuriste, cadre métal, vis de coin, bandeau noir/doré, couleurs or discrètes. Inclure zone texte lisible en N&B, contraste élevé, typo titrage Bebas Neue, corps Inter. Interdiction de logos/marques.
|
||||
@@ -0,0 +1,3 @@
|
||||
Variables: DURATION, TEAM_COUNT, HOTLINE_RULE
|
||||
Format: A4 1 page.
|
||||
Prompt: Règles de jeu enfant 9-11 ans, étapes simples, icônes claires, encadré sécurité, design premium mais sobre N&B.
|
||||
Executable
+27
@@ -0,0 +1,27 @@
|
||||
#!/usr/bin/env python3
|
||||
from pathlib import Path
|
||||
import shutil
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[2]
|
||||
out = ROOT / 'audio' / 'generated'
|
||||
out.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
source_pool = {
|
||||
'uson_boot_corrompu_30s.mp3': ROOT / 'hardware/firmware/esp32/data/uson_boot_corrompu_lowmono.mp3',
|
||||
'uson_win_15s.mp3': ROOT / 'hardware/firmware/esp32/data/uson_mode_hero.mp3',
|
||||
'zone5_radio_brouille_v1_30s.mp3': ROOT / 'hardware/firmware/esp32/data/uson_etape1_radio_lowmono.mp3',
|
||||
'zone5_radio_brouille_v2_45s.mp3': ROOT / 'hardware/firmware/esp32/sonGPT/uson_etape1_radio_lointaine_audible.mp3',
|
||||
'hotline_validation_01.mp3': ROOT / 'hardware/firmware/esp32/data/uson_unlock_rapport_imprime.mp3',
|
||||
'hotline_indice_01.mp3': ROOT / 'hardware/firmware/esp32/data/uson_unlock_rapport_imprime.mp3',
|
||||
'hotline_relance_01.mp3': ROOT / 'hardware/firmware/esp32/data/uson_unlock_rapport_imprime.mp3',
|
||||
'hotline_validation_02.mp3': ROOT / 'hardware/firmware/esp32/data/uson_unlock_rapport_imprime.mp3',
|
||||
'hotline_indice_02.mp3': ROOT / 'hardware/firmware/esp32/data/uson_unlock_rapport_imprime.mp3',
|
||||
'hotline_relance_02.mp3': ROOT / 'hardware/firmware/esp32/data/uson_unlock_rapport_imprime.mp3',
|
||||
}
|
||||
|
||||
for name, src in source_pool.items():
|
||||
if not src.exists():
|
||||
raise SystemExit(f"Missing source asset: {src}")
|
||||
dst = out / name
|
||||
shutil.copyfile(src, dst)
|
||||
print(f"generated {dst.relative_to(ROOT)} <- {src.relative_to(ROOT)}")
|
||||
+72
@@ -0,0 +1,72 @@
|
||||
#!/usr/bin/env python3
|
||||
from pathlib import Path
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[2]
|
||||
OUT = ROOT / 'printables' / 'export' / 'pdf' / 'zacus_v1'
|
||||
OUT.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
# Minimal PDF writer (no external dependency)
|
||||
def pdf_escape(s: str) -> str:
|
||||
return s.replace('\\', '\\\\').replace('(', '\\(').replace(')', '\\)')
|
||||
|
||||
def write_simple_pdf(path: Path, title: str, lines: list[str]):
|
||||
content_lines = [
|
||||
'BT',
|
||||
'/F1 18 Tf',
|
||||
'50 790 Td',
|
||||
f'({pdf_escape(title)}) Tj',
|
||||
'/F1 11 Tf',
|
||||
]
|
||||
y = 760
|
||||
for ln in lines:
|
||||
content_lines.append(f'1 0 0 1 50 {y} Tm ({pdf_escape(ln)}) Tj')
|
||||
y -= 16
|
||||
if y < 60:
|
||||
break
|
||||
content_lines.append('ET')
|
||||
stream = '\n'.join(content_lines).encode('latin-1', errors='replace')
|
||||
|
||||
objs = []
|
||||
objs.append(b'1 0 obj << /Type /Catalog /Pages 2 0 R >> endobj\n')
|
||||
objs.append(b'2 0 obj << /Type /Pages /Count 1 /Kids [3 0 R] >> endobj\n')
|
||||
objs.append(b'3 0 obj << /Type /Page /Parent 2 0 R /MediaBox [0 0 595 842] /Resources << /Font << /F1 4 0 R >> >> /Contents 5 0 R >> endobj\n')
|
||||
objs.append(b'4 0 obj << /Type /Font /Subtype /Type1 /BaseFont /Helvetica >> endobj\n')
|
||||
objs.append(f'5 0 obj << /Length {len(stream)} >> stream\n'.encode() + stream + b'\nendstream endobj\n')
|
||||
|
||||
header = b'%PDF-1.4\n%\xe2\xe3\xcf\xd3\n'
|
||||
xref_positions = [0]
|
||||
body = b''
|
||||
offset = len(header)
|
||||
for obj in objs:
|
||||
xref_positions.append(offset)
|
||||
body += obj
|
||||
offset += len(obj)
|
||||
xref = f'xref\n0 {len(xref_positions)}\n'.encode()
|
||||
xref += b'0000000000 65535 f \n'
|
||||
for pos in xref_positions[1:]:
|
||||
xref += f'{pos:010d} 00000 n \n'.encode()
|
||||
trailer = f'trailer << /Size {len(xref_positions)} /Root 1 0 R >>\nstartxref\n{offset}\n%%EOF\n'.encode()
|
||||
path.write_bytes(header + body + xref + trailer)
|
||||
|
||||
cards = [
|
||||
('zacus_v1_regles_a4_v01.pdf', 'Regles Zacus v1', [
|
||||
'Duree 60-90 min | Age 9-11 ans | 6-14 enfants',
|
||||
'1) Enquete en equipes 2) Hotline Brigade Z 3) Resolution finale',
|
||||
'Lisibilite N&B prioritaire, sans logos externes.'
|
||||
]),
|
||||
('zacus_v1_hotline_a4_v01.pdf', 'Hotline Brigade Z', [
|
||||
'Reponses autorisees: Indice / Validation / Relance',
|
||||
'Tokens telephones: L - AFO - EFO - LE - U',
|
||||
'Solution finale unique: Professeur Electron'
|
||||
]),
|
||||
('zacus_v1_feuille_enquete_a4_v01.pdf', 'Feuille Enquete QUI/OU/COMMENT', [
|
||||
'QUI : ______________________',
|
||||
'OU : _______________________',
|
||||
'COMMENT : __________________',
|
||||
'PREUVE : ___________________'
|
||||
]),
|
||||
]
|
||||
|
||||
for filename, title, lines in cards:
|
||||
write_simple_pdf(OUT / filename, title, lines)
|
||||
print('generated', (OUT / filename).relative_to(ROOT))
|
||||
Reference in New Issue
Block a user