feat: UI sounds, live LFO visualization, wire fix, worlds 7-12, bug fixes

- Add procedural UI sound effects (connect/disconnect, engine start/stop,
  level complete/fail, star earned, hint, navigation) via Tone.js
- Live LFO modulation visualization: knobs animate in real-time showing
  modulated value, ghost dot shows base value, number glows cyan
- Fix wire recalculation on zoom/pan/level re-entry (post-layout refresh)
- Fix retry button to keep current patch instead of reloading level
- Fix default param detection: newly added modules now populate all
  default params so level checkers work without manual param changes
- Add worlds 7-12: Secuencias y Ritmos, Texturas de Ruido, Síntesis
  Sustractiva, Espacio y Stereo, Técnicas Avanzadas, Gran Final
  (48 new levels, 144 new possible stars, 288 total stars)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Jose Luis
2026-03-21 03:03:29 +01:00
parent f0100eb64f
commit a1be6df355
17 changed files with 3317 additions and 24 deletions

View File

@@ -7,6 +7,7 @@ import WireLayer from '../components/WireLayer.jsx';
import { playTarget, stopTarget, isTargetPlaying } from './targetAudio.js';
import LevelComplete from './LevelComplete.jsx';
import { completeLevel, saveLevelPatch, getLevelPatch, markHintUsed, wasHintUsed } from './gameState.js';
import { playLevelComplete, playFail, playHint, playEngineStart, playEngineStop, playNav } from '../engine/uiSounds.js';
export default function PuzzleView({ level, levelIndex, worldLevels, onBack, onNextLevel }) {
const [, forceUpdate] = useState(0);
@@ -230,8 +231,10 @@ export default function PuzzleView({ level, levelIndex, worldLevels, onBack, onN
const handleToggleAudio = async () => {
if (state.isRunning) {
stopAudio();
playEngineStop();
} else {
await startAudio();
playEngineStart();
}
emit();
};
@@ -252,6 +255,7 @@ export default function PuzzleView({ level, levelIndex, worldLevels, onBack, onN
setHintUsed(true);
setShowHint(true);
markHintUsed(level.id);
playHint();
};
const handleCheck = () => {
@@ -275,6 +279,9 @@ export default function PuzzleView({ level, levelIndex, worldLevels, onBack, onN
if (stars >= 1) {
completeLevel(level.id, stars);
playLevelComplete();
} else {
playFail();
}
};
@@ -284,7 +291,7 @@ export default function PuzzleView({ level, levelIndex, worldLevels, onBack, onN
<div className="gm-puzzle">
{/* Top bar */}
<div className="gm-puzzle-bar">
<button className="gm-btn icon" onClick={onBack}> Mapa</button>
<button className="gm-btn icon" onClick={() => { playNav(); onBack(); }}> Mapa</button>
<div className="gm-puzzle-title">
<span className="gm-puzzle-num">{levelIndex + 1}/{worldLevels.length}</span>
<span className="gm-puzzle-name">{level.title}</span>
@@ -450,7 +457,7 @@ export default function PuzzleView({ level, levelIndex, worldLevels, onBack, onN
levelTitle={level.title}
isLastLevel={isLastLevel}
hintPenalty={result.hintPenalty}
onRetry={loadLevel}
onRetry={() => setResult(null)}
onMap={onBack}
onNext={onNextLevel}
/>