feat: add Worlds 2-3, patch persistence, and zoom controls

- World 2 (Filtros): 8 levels teaching filters, resonance, LFO modulation, acid bass
- World 3 (Envelopes): 8 levels teaching VCA, ADSR, pluck, tremolo, full synth lead
- Star-based world unlock system (12 stars for W2, 24 for W3)
- Level patch persistence: auto-saves player patches, restores on revisit
- Google Maps-style zoom controls (+/−/reset) in both puzzle and sandbox views
- Multi-world navigation in GameApp and WorldMap
- Target audio now supports filter chain for World 2 levels

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Jose Luis
2026-03-21 02:28:36 +01:00
parent 00c4ec8e00
commit 41d993183f
9 changed files with 1292 additions and 95 deletions

View File

@@ -174,6 +174,22 @@ export default function App({ onSwitchToGame }) {
const handleContextMenu = useCallback((e) => e.preventDefault(), []);
// Zoom controls (Google Maps style)
const handleZoomIn = useCallback(() => {
state.zoom = Math.min(3, state.zoom * 1.25);
emit();
}, []);
const handleZoomOut = useCallback(() => {
state.zoom = Math.max(0.3, state.zoom / 1.25);
emit();
}, []);
const handleZoomReset = useCallback(() => {
state.zoom = 1;
state.camX = 0;
state.camY = 0;
emit();
}, []);
const handleToggleAudio = async () => {
if (state.isRunning) {
stopAudio();
@@ -279,6 +295,15 @@ export default function App({ onSwitchToGame }) {
</div>
</div>
{/* Zoom controls — top right of canvas */}
<div className="zoom-controls">
<button className="zoom-btn" onClick={handleZoomIn} title="Zoom in">+</button>
<button className="zoom-btn zoom-label" onClick={handleZoomReset} title="Reset zoom">
{(state.zoom * 100).toFixed(0)}%
</button>
<button className="zoom-btn" onClick={handleZoomOut} title="Zoom out"></button>
</div>
{/* Module palette */}
<ModulePalette onAddModule={handleAddModule} />
</div>