feat: Zacus V3 complete

Phase 1: zacus_v3_complete.yaml, constants, phrases
Phase 3: ESP-NOW framework, 4 puzzle firmwares, master
Phase 4: NPC V3 profiling + game coordinator
Phase 5: XTTS-v2 Docker + voice clone generator
Phase 6: AudioCraft + BLE audio control
Phase 9: One-pager, video script, pricing, guide
This commit is contained in:
L'électron rare
2026-04-03 00:42:34 +02:00
parent 454b814584
commit 9f4501b9b9
11 changed files with 1696 additions and 0 deletions
+178
View File
@@ -475,3 +475,181 @@ endings:
key: "endings.many_hints.0"
- text: "Les indices sont là pour aider, et vous les avez bien utilisés !"
key: "endings.many_hints.1"
# ============================================================
# V3 PUZZLE-SPECIFIC HINTS (P1P7)
# ============================================================
P1_SON:
level_1:
- text: "La musique a une mémoire, dit-on. Le professeur en est convaincu. Écoutez bien… puis répétez."
key: "hints.P1_SON.level_1.0"
- text: "Quatre boutons. Quatre notes. Un seul ordre correct. Votre oreille est votre meilleur instrument."
key: "hints.P1_SON.level_1.1"
level_2:
- text: "La séquence commence toujours par le bouton ROUGE. Le reste suit une logique musicale simple."
key: "hints.P1_SON.level_2.0"
- text: "Vous avez entendu la mélodie ? Elle se rejoue si vous attendez 10 secondes. Prenez des notes !"
key: "hints.P1_SON.level_2.1"
level_3:
- text: "Rouge, Bleu, Jaune, Rouge, Vert. Dans cet ordre exact. Appuyez lentement et attendez le bip de confirmation."
key: "hints.P1_SON.level_3.0"
P2_CIRCUIT:
level_1:
- text: "L'électricité aime les chemins fermés. Si la LED ne brille pas… le circuit n'est pas complet."
key: "hints.P2_CIRCUIT.level_1.0"
- text: "Regardez les picots magnétiques. Certains sont ronds, d'autres sont carrés. Ce n'est pas un hasard."
key: "hints.P2_CIRCUIT.level_1.1"
level_2:
- text: "Il faut une résistance entre la pile et la LED. Sans quoi… *explosion dramatique* … enfin, presque."
key: "hints.P2_CIRCUIT.level_2.0"
- text: "Le schéma est inscrit sous le panneau en petits caractères. Cherchez bien. J'y ai mis des heures !"
key: "hints.P2_CIRCUIT.level_2.1"
level_3:
- text: "Pile → fil rouge → résistance 220Ω → LED (longue patte vers la résistance) → fil noir → pile. C'est tout !"
key: "hints.P2_CIRCUIT.level_3.0"
P3_QR:
level_1:
- text: "Six enveloppes. Six secrets. Mais l'ordre… l'ordre est gravé dans les étoiles. Ou dans ce laboratoire."
key: "hints.P3_QR.level_1.0"
- text: "Chaque QR code porte un numéro invisible à l'œil nu. Regardez sous chaque carte."
key: "hints.P3_QR.level_1.1"
level_2:
- text: "L'ordre est: numéros croissants des cachettes. La cachette 1 est toujours visible depuis l'entrée."
key: "hints.P3_QR.level_2.0"
- text: "Commencez par scanner le QR code collé sur la porte d'entrée. C'est le numéro 1."
key: "hints.P3_QR.level_2.1"
level_3:
- text: "Ordre: porte → étagère gauche → tiroir rouge → sous la table → fenêtre → boîte bleue. Scannez avec BOX-3."
key: "hints.P3_QR.level_3.0"
P4_RADIO:
level_1:
- text: "Les ondes radio… fascinantes ! Ma fréquence préférée se trouve quelque part entre 800 et 1800 hertz."
key: "hints.P4_RADIO.level_1.0"
- text: "Quand vous approchez de la bonne fréquence, un petit indicateur s'allume. Chaud… froid… chaud !"
key: "hints.P4_RADIO.level_1.1"
level_2:
- text: "1337. C'est un nombre que les informaticiens connaissent bien. Un nombre légendaire dans les cercles geek."
key: "hints.P4_RADIO.level_2.0"
- text: "La fréquence est un nombre à 4 chiffres. Commençant par 1. Très précis. Tournez doucement."
key: "hints.P4_RADIO.level_2.1"
level_3:
- text: "1337 hertz ! LEET en langage de hacker. Tournez jusqu'à ce que l'écran affiche 1337. Exactement."
key: "hints.P4_RADIO.level_3.0"
P5_MORSE:
level_1:
- text: "Le code morse… inventé en 1836 ! Long-court-long. Tiret-point-tiret. Écoutez le rythme."
key: "hints.P5_MORSE.level_1.0"
- text: "Vous avez une fiche de référence quelque part dans cette salle. Cherchez-la. Je l'y ai laissée exprès."
key: "hints.P5_MORSE.level_1.1"
level_2:
- text: "Le message contient cinq lettres. C'est mon nom… enfin, mon nom de code. Commençant par Z."
key: "hints.P5_MORSE.level_2.0"
- text: "Z=--.. A=.- C=-.-. U=..- S=... Voilà l'alphabet morse pour ces lettres précises."
key: "hints.P5_MORSE.level_2.1"
level_3:
- text: "ZACUS en morse: TIRET-TIRET-POINT-POINT POINT-TIRET TIRET-POINT-TIRET-POINT POINT-POINT-TIRET POINT-POINT-POINT. Tapez sur le télégraphe !"
key: "hints.P5_MORSE.level_3.0"
P6_SYMBOLES:
level_1:
- text: "L'alchimie obéit à une logique ancienne. Terre, Eau, Feu, Air… puis les métaux. Il y a un ordre naturel."
key: "hints.P6_SYMBOLES.level_1.0"
- text: "Chaque symbole a un poids. Les légers en haut, les lourds en bas. Comme dans la nature."
key: "hints.P6_SYMBOLES.level_1.1"
level_2:
- text: "L'ordre suit la table alchimique classique. Elle est affichée dans le couloir sous un cadre doré."
key: "hints.P6_SYMBOLES.level_2.0"
- text: "Commencez par placer les quatre éléments primordiaux dans les coins. Puis remplissez le centre."
key: "hints.P6_SYMBOLES.level_2.1"
level_3:
- text: "Feu (coin haut-gauche) → Eau → Terre → Air → Or → Argent → Mercure → Soufre → Sel → Antimoine → Arsenic → Plomb. De gauche à droite, haut en bas."
key: "hints.P6_SYMBOLES.level_3.0"
P7_COFFRE:
level_1:
- text: "Vous avez tous les fragments ? Chaque puzzle vous a donné un ou deux chiffres. Assemblez-les dans l'ordre des puzzles."
key: "hints.P7_COFFRE.level_1.0"
# ============================================================
# V3 ADAPTATION PHRASES (profiling + dynamic parcours)
# ============================================================
v3_adaptation:
group_tech_detected:
- text: "Hmm. Groupe technique détecté ! Excellente nouvelle. Je vais sortir mes puzzles… avancés."
key: "v3_adaptation.group_tech_detected.0"
- text: "Vous avez résolu le circuit en un temps record. Intéressant ! Passons aux choses sérieuses."
key: "v3_adaptation.group_tech_detected.1"
group_non_tech_detected:
- text: "Ah ! Des artistes, des créatifs. Parfait. J'ai préparé des énigmes qui correspondent à votre sensibilité."
key: "v3_adaptation.group_non_tech_detected.0"
- text: "Le circuit n'était pas votre fort ? Pas grave ! La logique prend mille formes. Voici la vôtre."
key: "v3_adaptation.group_non_tech_detected.1"
group_mixed_detected:
- text: "Groupe éclectique ! Moitié techniciens, moitié artistes. Mon scénario préféré. Tout le monde travaille !"
key: "v3_adaptation.group_mixed_detected.0"
bonus_puzzle_added:
- text: "Impressionnant ! Vous allez trop vite. J'ai décidé d'ajouter… une petite surprise."
key: "v3_adaptation.bonus_puzzle_added.0"
- text: "Trop facile ? Très bien. Voici un défi supplémentaire que je réserve aux meilleurs groupes."
key: "v3_adaptation.bonus_puzzle_added.1"
puzzle_skipped:
- text: "Hmm. Le temps passe. Je vais vous faire grâce de cette épreuve. Pour aujourd'hui."
key: "v3_adaptation.puzzle_skipped.0"
- text: "Je saute une étape. Non par pitié — mais parce que le temps est une expérience en soi."
key: "v3_adaptation.puzzle_skipped.1"
duration_warning:
- text: "Attention ! Vous avez consommé 80% du temps imparti. Le Professeur s'impatiente légèrement."
key: "v3_adaptation.duration_warning.0"
# ============================================================
# V3 AMBIANCE (intro / outro per profile type)
# ============================================================
v3_ambiance:
intro:
- text: "Bienvenue dans mon laboratoire… portable. Aujourd'hui, c'est VOUS qui êtes l'expérience."
key: "v3_ambiance.intro.0"
- text: "Je suis le Professeur Zacus. Scientifique, inventeur, et… testeur de groupes humains depuis 1987."
key: "v3_ambiance.intro.1"
- text: "Vous avez soixante minutes. Soixante petites minutes pour prouver que votre espèce mérite de continuer."
key: "v3_ambiance.intro.2"
outro_success:
- text: "Extraordinaire ! Vous avez passé mon test. Je dois admettre… je suis impressionné. Légèrement."
key: "v3_ambiance.outro_success.0"
- text: "Félicitations ! Le diplôme du Professeur Zacus vous est accordé. Avec mention… passable."
key: "v3_ambiance.outro_success.1"
outro_success_tech:
- text: "Groupe technique remarquable ! Vous avez décodé chaque signal avec une précision chirurgicale. Respect."
key: "v3_ambiance.outro_success_tech.0"
- text: "Les ingénieurs du futur sont parmi nous. J'en suis certain maintenant. Bravo !"
key: "v3_ambiance.outro_success_tech.1"
outro_success_non_tech:
- text: "Qui a dit que la technologie était réservée aux ingénieurs ? Vous venez de prouver le contraire !"
key: "v3_ambiance.outro_success_non_tech.0"
- text: "L'intuition et la créativité ont triomphé. Le Professeur Zacus est bluffé. Vraiment."
key: "v3_ambiance.outro_success_non_tech.1"
outro_success_mixed:
- text: "Un groupe mixte en parfaite harmonie. Techniciens et créatifs côte à côte. C'est ce qui sauve le monde."
key: "v3_ambiance.outro_success_mixed.0"
outro_partial:
- text: "Hmm. Vous n'avez pas tout résolu. Mais vous avez essayé. C'est… acceptable. Pour un humain."
key: "v3_ambiance.outro_partial.0"
outro_timeout:
- text: "Le temps est écoulé ! L'expérience se termine. Ne vous inquiétez pas — j'ai noté tout ce que vous avez fait."
key: "v3_ambiance.outro_timeout.0"
- text: "Soixante minutes, et vous avez donné tout ce que vous aviez. C'est tout ce que je demandais."
key: "v3_ambiance.outro_timeout.1"
+120
View File
@@ -0,0 +1,120 @@
# game/scenarios/v3_constants.yaml
# V3 runtime constants — NPC engine, scoring, profiling, hint escalation.
# All thresholds centralized here. Referenced by npc_engine.cpp and
# the adaptive parcours logic in compile_runtime3.py.
# ============================================================
# NPC ENGINE THRESHOLDS
# ============================================================
npc:
# Adaptive mood triggers (% of expected puzzle duration elapsed)
fast_threshold_pct: 60 # < 60% elapsed → mood IMPRESSED, add bonus puzzle
slow_threshold_pct: 130 # > 130% elapsed → mood WORRIED, proactive hint
critical_threshold_pct: 160 # > 160% elapsed + hints maxed → skip_to_next
# Stuck detection
stuck_timeout_seconds: 120 # no progress for 2 min → trigger hint offer
proactive_hint_interval_seconds: 90 # re-offer if still stuck after 90s
max_hint_level: 3
# Time warning trigger
duration_warning_pct: 80 # warn when 80% of target_minutes consumed
# ============================================================
# SCORING CONSTANTS
# ============================================================
scoring:
base: 1000
hint_penalty: 50 # per hint given (any level)
time_penalty_per_minute: 10 # each full minute over target_minutes
bonus_fast: 200 # finished before fast_threshold_pct of target
bonus_no_hints: 300 # zero hints used across entire game
bonus_perfect_morse: 100 # P5 decoded on first attempt (no retries)
max_score: 1600
# ============================================================
# PROFILING PHASE THRESHOLDS
# ============================================================
profiling:
fast_seconds: 180 # < 3 min on a profiling puzzle → "fast" tag
slow_seconds: 480 # > 8 min on a profiling puzzle → "slow" tag
# "normal" = between fast_seconds and slow_seconds
tech_time_seconds: 180 # PROFILING_TECH_TIME: P2_CIRCUIT fast threshold
# ============================================================
# TARGET DURATIONS PER MODE
# ============================================================
duration_modes:
mode_30:
target_minutes: 30
profiling_minutes: 5
adaptive_minutes: 15
climax_minutes: 5
outro_minutes: 5
puzzles_required: 4
mode_45:
target_minutes: 45
profiling_minutes: 8
adaptive_minutes: 25
climax_minutes: 7
outro_minutes: 5
puzzles_required: 5
mode_60:
target_minutes: 60
profiling_minutes: 10
adaptive_minutes: 35
climax_minutes: 10
outro_minutes: 5
puzzles_required: 6
mode_90:
target_minutes: 90
profiling_minutes: 10
adaptive_minutes: 60
climax_minutes: 10
outro_minutes: 10
puzzles_required: 7
# ============================================================
# HINT ESCALATION TIMERS
# ============================================================
hint_escalation:
first_hint_after_seconds: 120 # level_1 offered after 2 min stuck
second_hint_after_seconds: 240 # level_2 offered after 4 min stuck
third_hint_after_seconds: 360 # level_3 offered after 6 min stuck
auto_skip_after_seconds: 540 # auto-skip after 9 min (if game_master enabled)
# ============================================================
# PER-PUZZLE EXPECTED DURATIONS AND LIMITS
# ============================================================
puzzles:
P1_SON:
expected_s: 300
max_hints: 3
phase: profiling
P2_CIRCUIT:
expected_s: 360
max_hints: 3
phase: profiling
P3_QR:
expected_s: 240
max_hints: 3
phase: adaptive
P4_RADIO:
expected_s: 300
max_hints: 3
phase: adaptive
P5_MORSE:
expected_s: 420
max_hints: 3
phase: adaptive
P6_SYMBOLES:
expected_s: 360
max_hints: 3
phase: adaptive
P7_COFFRE:
expected_s: 300
max_hints: 1
phase: climax
+313
View File
@@ -0,0 +1,313 @@
# game/scenarios/zacus_v3_complete.yaml
# V3 Complete — 7 puzzle objects, adaptive NPC, ESP-NOW mesh
id: zacus_v3_complete
version: "3.1"
title: "Le Mystère du Professeur Zacus — V3 Complete"
theme: "Escape room mobile adaptatif, laboratoire du Professeur"
players:
min: 4
max: 15
ages: "8+ ans (corporate et enfants)"
duration:
target_minutes: 60 # game master configures at boot (3090)
min_minutes: 30
max_minutes: 90
profiling_minutes: 10
climax_minutes: 10
outro_minutes: 5
# ============================================================
# HARDWARE TOPOLOGY
# ============================================================
hardware:
hub:
- id: RTC_PHONE
type: custom_esp32_slic
role: voice_hub
comm: esp_now
- id: BOX_3
type: esp32s3_box3
role: orchestrator_display_camera
comm: esp_now_master
puzzles:
- id: P1_SON
name: "Séquence Sonore"
type: esp32s3_devkit
profile: BOTH
phase: profiling
- id: P2_CIRCUIT
name: "Circuit LED"
type: esp32_devkit
profile: TECH
phase: profiling
- id: P3_QR
name: "QR Treasure"
type: box3_camera
profile: BOTH
phase: adaptive
- id: P4_RADIO
name: "Fréquence Radio"
type: esp32_devkit
profile: TECH
phase: adaptive
- id: P5_MORSE
name: "Code Morse"
type: esp32_devkit
profile: MIXED
phase: adaptive
- id: P6_SYMBOLES
name: "Symboles Alchimiques"
type: esp32_devkit
profile: NON_TECH
phase: adaptive
- id: P7_COFFRE
name: "Coffre Final"
type: esp32_devkit
profile: BOTH
phase: climax
# ============================================================
# SCORING SYSTEM
# ============================================================
scoring:
base_score: 1000
time_penalty_per_minute: 10 # deducted after target_minutes
hint_penalty: 50 # per hint given
bonus_fast_completion: 200 # if finished < 80% of target
bonus_no_hints: 300 # if zero hints used
bonus_perfect_morse: 100 # P5 decoded on first attempt
max_score: 1600
# ============================================================
# PHASES
# ============================================================
phases:
intro:
id: PHASE_INTRO
duration_minutes: 5
trigger: game_start
npc_audio: "intro/welcome"
description: >
Phone rings. Professor introduces himself. Players learn
the rules through the Professor's monologue.
profiling:
id: PHASE_PROFILING
duration_minutes: 10
puzzles: [P1_SON, P2_CIRCUIT]
expected_time_each_seconds: 300
npc_audio: "profiling/start"
profiling_thresholds:
fast_seconds: 180 # < 3 min = fast on this puzzle
slow_seconds: 480 # > 8 min = slow on this puzzle
group_classification:
TECH:
condition: "P2_fast AND (P1_normal OR P1_slow)"
parcours: [P2_CIRCUIT, P4_RADIO, P5_MORSE, P3_QR, P7_COFFRE]
NON_TECH:
condition: "P1_fast AND (P2_normal OR P2_slow)"
parcours: [P1_SON, P6_SYMBOLES, P3_QR, P5_MORSE_LIGHT, P7_COFFRE]
MIXED:
condition: "default"
parcours: [P1_SON, P2_CIRCUIT, P3_QR, P5_MORSE, P6_SYMBOLES, P7_COFFRE]
adaptive:
id: PHASE_ADAPTIVE
duration_minutes: 20 # to 60, dynamically adjusted
fast_threshold_pct: 60
slow_threshold_pct: 130
max_bonus_puzzles: 2
npc_mood_rules:
- condition: "pct < 60"
mood: IMPRESSED
action: add_bonus_puzzle
- condition: "pct > 130"
mood: WORRIED
action: proactive_hint
- condition: "pct > 160 AND hints_maxed"
mood: WORRIED
action: skip_to_next
climax:
id: PHASE_CLIMAX
duration_minutes: 10
puzzle: P7_COFFRE
requires_all_codes: true
description: >
Final code assembled from all previous puzzle solutions.
All players must cooperate to enter 8-digit code.
code_assembly:
P1_SON: digits 1-2
P2_CIRCUIT: digit 3
P4_RADIO: digit 4
P5_MORSE: digit 5
P6_SYMBOLES: digits 6-7
P3_QR: digit 8
outro:
id: PHASE_OUTRO
duration_minutes: 5
npc_audio: "outro/verdict"
outputs:
- diplome_pdf: true
- stats_display: true # score, time, hints, rating
# ============================================================
# PUZZLE DEFINITIONS (timing, expected durations)
# ============================================================
puzzles:
P1_SON:
id: P1_SON
name: "Séquence Sonore"
object: "Boîte avec haut-parleur + 4 boutons arcade"
interaction: "Écouter la mélodie, reproduire la séquence"
expected_duration_seconds: 300
max_hints: 3
code_contribution: "digits 1-2 (index de la séquence correcte, base-10)"
difficulty_variants:
TECH: sequence_length_5
NON_TECH: sequence_length_3
MIXED: sequence_length_4
hint_keys:
level_1: "hints.P1_SON.level_1"
level_2: "hints.P1_SON.level_2"
level_3: "hints.P1_SON.level_3"
P2_CIRCUIT:
id: P2_CIRCUIT
name: "Circuit LED"
object: "Breadboard magnétique 60×40cm pliable"
interaction: "Placer les composants pour allumer la LED"
expected_duration_seconds: 360
max_hints: 3
code_contribution: "digit 3 (numéro de la configuration validée, 1-9)"
difficulty_variants:
TECH: full_circuit_5_components
NON_TECH: guided_circuit_3_components
hint_keys:
level_1: "hints.P2_CIRCUIT.level_1"
level_2: "hints.P2_CIRCUIT.level_2"
level_3: "hints.P2_CIRCUIT.level_3"
P3_QR:
id: P3_QR
name: "QR Treasure"
object: "6 QR codes plastifiés A5 cachés dans la salle"
interaction: "Scanner dans l'ordre correct via BOX-3"
expected_duration_seconds: 240
max_hints: 3
code_contribution: "digit 8 (checksum des QR scannés)"
hint_keys:
level_1: "hints.P3_QR.level_1"
level_2: "hints.P3_QR.level_2"
level_3: "hints.P3_QR.level_3"
P4_RADIO:
id: P4_RADIO
name: "Fréquence Radio"
object: "Boîtier radio rétro 3D imprimé"
interaction: "Trouver la bonne fréquence au codeur rotatif"
expected_duration_seconds: 300
max_hints: 3
code_contribution: "digit 4 (fréquence cible encodée sur 1 chiffre)"
target_frequency_hz: 1337
frequency_range: [800, 1800]
tolerance_hz: 10
hint_keys:
level_1: "hints.P4_RADIO.level_1"
level_2: "hints.P4_RADIO.level_2"
level_3: "hints.P4_RADIO.level_3"
P5_MORSE:
id: P5_MORSE
name: "Code Morse"
object: "Télégraphe en bois + laiton"
interaction: "Décoder le message morse du Professeur"
expected_duration_seconds: 420
max_hints: 3
code_contribution: "digit 5 (dernier chiffre du message morse)"
morse_message: "ZACUS" # Z=--.. A=.- C=-.-. U=..- S=...
difficulty_variants:
TECH: full_morse_decode
NON_TECH: morse_light_mode # visual light pulses instead of sound
hint_keys:
level_1: "hints.P5_MORSE.level_1"
level_2: "hints.P5_MORSE.level_2"
level_3: "hints.P5_MORSE.level_3"
P6_SYMBOLES:
id: P6_SYMBOLES
name: "Symboles Alchimiques"
object: "Tablette en bois découpée laser 30×20cm"
interaction: "Placer les 12 symboles NFC dans l'ordre correct"
expected_duration_seconds: 360
max_hints: 3
code_contribution: "digits 6-7 (index de la configuration, 10-99)"
nfc_tags: 12
correct_order: [7, 2, 11, 4, 9, 1, 8, 3, 12, 6, 10, 5]
hint_keys:
level_1: "hints.P6_SYMBOLES.level_1"
level_2: "hints.P6_SYMBOLES.level_2"
level_3: "hints.P6_SYMBOLES.level_3"
P7_COFFRE:
id: P7_COFFRE
name: "Coffre Final"
object: "Coffre en bois 25×20×15cm avec verrou électronique"
interaction: "Entrer le code 8 chiffres assemblé des puzzles précédents"
expected_duration_seconds: 300
max_hints: 1 # minimal hints for final puzzle
hint_keys:
level_1: "hints.P7_COFFRE.level_1"
# ============================================================
# DURATION MODES (game master selects at boot)
# ============================================================
duration_modes:
mode_30:
target_minutes: 30
profiling_minutes: 5
adaptive_minutes: 15
climax_minutes: 5
outro_minutes: 5
puzzles_required: 4 # skip lowest-priority adaptive puzzles
mode_45:
target_minutes: 45
profiling_minutes: 8
adaptive_minutes: 25
climax_minutes: 7
outro_minutes: 5
puzzles_required: 5
mode_60:
target_minutes: 60
profiling_minutes: 10
adaptive_minutes: 35
climax_minutes: 10
outro_minutes: 5
puzzles_required: 6
mode_90:
target_minutes: 90
profiling_minutes: 10
adaptive_minutes: 60
climax_minutes: 10
outro_minutes: 10
puzzles_required: 7 # all puzzles + bonus possible
# ============================================================
# HINT ESCALATION TIMERS
# ============================================================
hint_escalation:
first_hint_after_seconds: 120 # offer level_1 after 2 min stuck
second_hint_after_seconds: 240 # offer level_2 after 4 min stuck
third_hint_after_seconds: 360 # offer level_3 after 6 min stuck
auto_skip_after_seconds: 540 # skip puzzle after 9 min (if enabled)
proactive_hint_interval_seconds: 90