@nice2dev/game-engine 1.1.6 → 1.1.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -160,7 +160,7 @@ const TUTORIAL_MULTIPLAYER_ONLINE = {
160
160
  de: 'Verwende `SnapshotBuffer` zum Speichern von Spielzustand-Snapshots für Interpolation. Der Host sendet Snapshots, Clients interpolieren dazwischen.',
161
161
  },
162
162
  codeTemplate: '// Create snapshot buffer\nconst snapshots = ',
163
- codeSolution: "const snapshots = new SnapshotBuffer(60);\n\n// Host creates and broadcasts snapshots\nconst snapshot = {\n tick: net.tick,\n timestamp: performance.now(),\n states: new Map(),\n};\nsnapshots.push(snapshot);\n\n// Client interpolates\nconst pair = snapshots.getInterpolationPair(serverTime);\nif (pair) {\n const [a, b] = pair;\n const t = (serverTime - a.timestamp) / (b.timestamp - a.timestamp);\n // interpolateState(stateA, stateB, t)\n}",
163
+ codeSolution: 'const snapshots = new SnapshotBuffer(60);\n\n// Host creates and broadcasts snapshots\nconst snapshot = {\n tick: net.tick,\n timestamp: performance.now(),\n states: new Map(),\n};\nsnapshots.push(snapshot);\n\n// Client interpolates\nconst pair = snapshots.getInterpolationPair(serverTime);\nif (pair) {\n const [a, b] = pair;\n const t = (serverTime - a.timestamp) / (b.timestamp - a.timestamp);\n // interpolateState(stateA, stateB, t)\n}',
164
164
  codeValidation: {
165
165
  containsAll: ['SnapshotBuffer', 'push', 'getInterpolationPair'],
166
166
  },
@@ -185,7 +185,7 @@ const TUTORIAL_MULTIPLAYER_ONLINE = {
185
185
  de: 'Verwende `ClientPrediction` zum Speichern lokaler Eingaben und Abgleich nach Server-Bestätigung. Dies eliminiert Input-Lag für den lokalen Spieler.',
186
186
  },
187
187
  codeTemplate: '// Set up prediction\nconst prediction = ',
188
- codeSolution: "const prediction = new ClientPrediction();\n\n// Every frame: record local input + simulated result\nprediction.addInput({\n tick: net.tick,\n inputActions: { moveRight: true, jump: false },\n resultPosition: { x: player.x + 5, y: player.y },\n});\n\n// When server confirms a tick:\nprediction.acknowledge(serverTick);\n\n// Replay unconfirmed inputs after correction\nconst pending = prediction.getPendingInputs();",
188
+ codeSolution: 'const prediction = new ClientPrediction();\n\n// Every frame: record local input + simulated result\nprediction.addInput({\n tick: net.tick,\n inputActions: { moveRight: true, jump: false },\n resultPosition: { x: player.x + 5, y: player.y },\n});\n\n// When server confirms a tick:\nprediction.acknowledge(serverTick);\n\n// Replay unconfirmed inputs after correction\nconst pending = prediction.getPendingInputs();',
189
189
  codeValidation: {
190
190
  containsAll: ['ClientPrediction', 'addInput', 'acknowledge'],
191
191
  },
@@ -210,10 +210,38 @@ const TUTORIAL_MULTIPLAYER_ONLINE = {
210
210
  de: 'Warum ist clientseitige Vorhersage in Online-Spielen wichtig?',
211
211
  },
212
212
  quizOptions: [
213
- { id: 'a', text: { en: 'To reduce server load', pl: 'Aby zmniejszyć obciążenie serwera', de: 'Um die Serverlast zu reduzieren' } },
214
- { id: 'b', text: { en: 'To eliminate perceived input lag', pl: 'Aby wyeliminować odczuwane opóźnienie inputu', de: 'Um wahrgenommene Eingabeverzögerung zu eliminieren' } },
215
- { id: 'c', text: { en: 'To improve graphics', pl: 'Aby poprawić grafikę', de: 'Um die Grafik zu verbessern' } },
216
- { id: 'd', text: { en: 'To save bandwidth', pl: 'Aby oszczędzić transfer', de: 'Um Bandbreite zu sparen' } },
213
+ {
214
+ id: 'a',
215
+ text: {
216
+ en: 'To reduce server load',
217
+ pl: 'Aby zmniejszyć obciążenie serwera',
218
+ de: 'Um die Serverlast zu reduzieren',
219
+ },
220
+ },
221
+ {
222
+ id: 'b',
223
+ text: {
224
+ en: 'To eliminate perceived input lag',
225
+ pl: 'Aby wyeliminować odczuwane opóźnienie inputu',
226
+ de: 'Um wahrgenommene Eingabeverzögerung zu eliminieren',
227
+ },
228
+ },
229
+ {
230
+ id: 'c',
231
+ text: {
232
+ en: 'To improve graphics',
233
+ pl: 'Aby poprawić grafikę',
234
+ de: 'Um die Grafik zu verbessern',
235
+ },
236
+ },
237
+ {
238
+ id: 'd',
239
+ text: {
240
+ en: 'To save bandwidth',
241
+ pl: 'Aby oszczędzić transfer',
242
+ de: 'Um Bandbreite zu sparen',
243
+ },
244
+ },
217
245
  ],
218
246
  quizCorrectId: 'b',
219
247
  },
@@ -252,7 +280,7 @@ const TUTORIAL_MULTIPLAYER_ONLINE = {
252
280
  de: 'Online-Mehrspieler fertig! 🎉',
253
281
  },
254
282
  content: {
255
- en: 'You\'ve learned how to build networked multiplayer games! You can now:\n• Connect peers via WebRTC\n• Create and manage lobby rooms\n• Synchronize game state with snapshots\n• Implement client-side prediction\n• Spawn/destroy entities across the network',
283
+ en: "You've learned how to build networked multiplayer games! You can now:\n• Connect peers via WebRTC\n• Create and manage lobby rooms\n• Synchronize game state with snapshots\n• Implement client-side prediction\n• Spawn/destroy entities across the network",
256
284
  pl: 'Nauczyłeś się budować gry multiplayer online! Teraz potrafisz:\n• Łączyć peerów przez WebRTC\n• Tworzyć i zarządzać pokojami lobby\n• Synchronizować stan gry snapshotami\n• Implementować predykcję po stronie klienta\n• Tworzyć/niszczyć encje w sieci',
257
285
  de: 'Du hast gelernt, vernetzte Multiplayer-Spiele zu bauen! Du kannst jetzt:\n• Peers über WebRTC verbinden\n• Lobby-Räume erstellen und verwalten\n• Spielzustand mit Snapshots synchronisieren\n• Clientseitige Vorhersage implementieren\n• Entities über das Netzwerk spawnen/zerstören',
258
286
  },
@@ -293,7 +321,7 @@ const TUTORIAL_PROCEDURAL_WORLD = {
293
321
  },
294
322
  content: {
295
323
  en: 'Procedural generation creates content using algorithms instead of manual design.\n\nYou will learn:\n• Seeded random numbers (reproducible worlds)\n• Perlin noise (natural-looking terrain)\n• Enemy wave scaling\n• Loot tables with weighted drops\n• Procedural pixel art sprites',
296
- pl: 'Generowanie proceduralne tworzy zawartość za pomocą algorytmów zamiast ręcznego projektowania.\n\nNauczysz się:\n• Losowość z seedem (odtwarzalne światy)\n• Szum Perlina (naturalnie wyglądający teren)\n• Skalowanie fal wrogów\n• Tabele łupów z ważonymi dropami\n• Proceduralne pikselo-artowe sprite\'y',
324
+ pl: "Generowanie proceduralne tworzy zawartość za pomocą algorytmów zamiast ręcznego projektowania.\n\nNauczysz się:\n• Losowość z seedem (odtwarzalne światy)\n• Szum Perlina (naturalnie wyglądający teren)\n• Skalowanie fal wrogów\n• Tabele łupów z ważonymi dropami\n• Proceduralne pikselo-artowe sprite'y",
297
325
  de: 'Prozedurale Generierung erstellt Inhalte mit Algorithmen statt manueller Gestaltung.\n\nDu lernst:\n• Zufallszahlen mit Seed (reproduzierbare Welten)\n• Perlin-Noise (natürlich aussehende Landschaft)\n• Feindwellen-Skalierung\n• Loot-Tabellen mit gewichteten Drops\n• Prozedurale Pixel-Art-Sprites',
298
326
  },
299
327
  skippable: true,
@@ -339,7 +367,7 @@ const TUTORIAL_PROCEDURAL_WORLD = {
339
367
  de: '`PerlinNoise` erzeugt glatte, natürlich aussehende Werte. Verwende `noise2D(x, y)` für Heightmaps und `fbm()` für detaillierte Landschaften.',
340
368
  },
341
369
  codeTemplate: '// Generate a height map\nconst noise = ',
342
- codeSolution: "const noise = new PerlinNoise(42);\n\n// Generate a 64x64 terrain\nfor (let y = 0; y < 64; y++) {\n for (let x = 0; x < 64; x++) {\n // noise2D returns -1 to 1 — scale to 0-1\n const height = (noise.noise2D(x * 0.05, y * 0.05) + 1) * 0.5;\n\n // Use fbm for more detail (4 octaves)\n const detail = noise.fbm(x * 0.1, y * 0.1, 4);\n\n // Map to tile type\n if (height < 0.3) { /* water */ }\n else if (height < 0.6) { /* grass */ }\n else { /* mountain */ }\n }\n}",
370
+ codeSolution: 'const noise = new PerlinNoise(42);\n\n// Generate a 64x64 terrain\nfor (let y = 0; y < 64; y++) {\n for (let x = 0; x < 64; x++) {\n // noise2D returns -1 to 1 — scale to 0-1\n const height = (noise.noise2D(x * 0.05, y * 0.05) + 1) * 0.5;\n\n // Use fbm for more detail (4 octaves)\n const detail = noise.fbm(x * 0.1, y * 0.1, 4);\n\n // Map to tile type\n if (height < 0.3) { /* water */ }\n else if (height < 0.6) { /* grass */ }\n else { /* mountain */ }\n }\n}',
343
371
  codeValidation: {
344
372
  containsAll: ['PerlinNoise', 'noise2D'],
345
373
  },
@@ -405,7 +433,7 @@ const TUTORIAL_PROCEDURAL_WORLD = {
405
433
  type: 'code',
406
434
  title: {
407
435
  en: 'Generate Pixel Art Sprites',
408
- pl: 'Generuj pikselo-artowe sprite\'y',
436
+ pl: "Generuj pikselo-artowe sprite'y",
409
437
  de: 'Pixel-Art-Sprites generieren',
410
438
  },
411
439
  content: {
@@ -414,7 +442,7 @@ const TUTORIAL_PROCEDURAL_WORLD = {
414
442
  de: '`ProceduralSprite` generiert symmetrische Pixel-Art aus einer Palette. Jeder Seed ergibt einen einzigartigen Charakter!',
415
443
  },
416
444
  codeTemplate: '// Generate a procedural sprite\nconst sprite = ',
417
- codeSolution: "const sprite = new ProceduralSprite(8, 8, [\n { r: 0.2, g: 0.5, b: 1, a: 1 }, // blue\n { r: 0.8, g: 0.2, b: 0.3, a: 1 }, // red\n { r: 0.9, g: 0.9, b: 0.1, a: 1 }, // yellow\n]);\n\n// Generate with seed\nconst pixels = sprite.generate(42);\n// Each seed = unique character sprite!",
445
+ codeSolution: 'const sprite = new ProceduralSprite(8, 8, [\n { r: 0.2, g: 0.5, b: 1, a: 1 }, // blue\n { r: 0.8, g: 0.2, b: 0.3, a: 1 }, // red\n { r: 0.9, g: 0.9, b: 0.1, a: 1 }, // yellow\n]);\n\n// Generate with seed\nconst pixels = sprite.generate(42);\n// Each seed = unique character sprite!',
418
446
  codeValidation: {
419
447
  containsAll: ['ProceduralSprite', 'generate'],
420
448
  },
@@ -439,10 +467,34 @@ const TUTORIAL_PROCEDURAL_WORLD = {
439
467
  de: 'Was macht Perlin-Noise besser als rein zufällige Werte für Terrain?',
440
468
  },
441
469
  quizOptions: [
442
- { id: 'a', text: { en: 'It\'s faster to compute', pl: 'Jest szybszy do obliczenia', de: 'Es ist schneller zu berechnen' } },
443
- { id: 'b', text: { en: 'It produces smooth, continuous gradients', pl: 'Produkuje gładkie, ciągłe gradienty', de: 'Es erzeugt glatte, kontinuierliche Gradienten' } },
444
- { id: 'c', text: { en: 'It uses less memory', pl: 'Zużywa mniej pamięci', de: 'Es verbraucht weniger Speicher' } },
445
- { id: 'd', text: { en: 'It\'s more random', pl: 'Jest bardziej losowy', de: 'Es ist zufälliger' } },
470
+ {
471
+ id: 'a',
472
+ text: {
473
+ en: "It's faster to compute",
474
+ pl: 'Jest szybszy do obliczenia',
475
+ de: 'Es ist schneller zu berechnen',
476
+ },
477
+ },
478
+ {
479
+ id: 'b',
480
+ text: {
481
+ en: 'It produces smooth, continuous gradients',
482
+ pl: 'Produkuje gładkie, ciągłe gradienty',
483
+ de: 'Es erzeugt glatte, kontinuierliche Gradienten',
484
+ },
485
+ },
486
+ {
487
+ id: 'c',
488
+ text: {
489
+ en: 'It uses less memory',
490
+ pl: 'Zużywa mniej pamięci',
491
+ de: 'Es verbraucht weniger Speicher',
492
+ },
493
+ },
494
+ {
495
+ id: 'd',
496
+ text: { en: "It's more random", pl: 'Jest bardziej losowy', de: 'Es ist zufälliger' },
497
+ },
446
498
  ],
447
499
  quizCorrectId: 'b',
448
500
  },
@@ -457,7 +509,7 @@ const TUTORIAL_PROCEDURAL_WORLD = {
457
509
  },
458
510
  content: {
459
511
  en: 'You can now generate infinite, unique games! Skills learned:\n• Seeded random for reproducible worlds\n• Perlin noise for natural terrain\n• Wave scaling for enemy difficulty\n• Loot tables for balanced drops\n• Procedural pixel art sprites',
460
- pl: 'Teraz możesz generować nieskończone, unikalne gry! Umiejętności:\n• Losowość z seedem dla odtwarzalnych światów\n• Szum Perlina dla naturalnego terenu\n• Skalowanie fal dla trudności wrogów\n• Tabele łupów dla zbalansowanych dropów\n• Proceduralne pikselo-artowe sprite\'y',
512
+ pl: "Teraz możesz generować nieskończone, unikalne gry! Umiejętności:\n• Losowość z seedem dla odtwarzalnych światów\n• Szum Perlina dla naturalnego terenu\n• Skalowanie fal dla trudności wrogów\n• Tabele łupów dla zbalansowanych dropów\n• Proceduralne pikselo-artowe sprite'y",
461
513
  de: 'Du kannst jetzt unendliche, einzigartige Spiele generieren! Gelernte Fähigkeiten:\n• Seed-Zufall für reproduzierbare Welten\n• Perlin-Noise für natürliches Terrain\n• Wellen-Skalierung für Feind-Schwierigkeit\n• Loot-Tabellen für ausgewogene Drops\n• Prozedurale Pixel-Art-Sprites',
462
514
  },
463
515
  },
@@ -496,7 +548,7 @@ const TUTORIAL_PLATFORMER = {
496
548
  de: 'Baue einen Platformer! 🏃',
497
549
  },
498
550
  content: {
499
- en: 'In this tutorial you\'ll build a complete platformer game!\n\n• Player movement & acceleration\n• Jumping with coyote time\n• Double-jump & wall-slide\n• Dash mechanic\n• Enemy AI (patrol + chase)\n• Coins & collectibles\n• Level with tilemap\n\nLet\'s make Mario-like magic!',
551
+ en: "In this tutorial you'll build a complete platformer game!\n\n• Player movement & acceleration\n• Jumping with coyote time\n• Double-jump & wall-slide\n• Dash mechanic\n• Enemy AI (patrol + chase)\n• Coins & collectibles\n• Level with tilemap\n\nLet's make Mario-like magic!",
500
552
  pl: 'W tym tutorialu zbudujesz kompletną grę platformową!\n\n• Ruch gracza i przyspieszenie\n• Skakanie z coyote time\n• Podwójny skok i wall-slide\n• Mechanika dashu\n• AI wrogów (patrol + pościg)\n• Monety i zbieranie\n• Poziom z tilemapy\n\nZróbmy magię jak Mario!',
501
553
  de: 'In diesem Tutorial baust du ein komplettes Plattformspiel!\n\n• Spielerbewegung & Beschleunigung\n• Springen mit Coyote-Time\n• Doppelsprung & Wall-Slide\n• Dash-Mechanik\n• Feind-KI (Patrouille + Verfolgung)\n• Münzen & Sammelobjekte\n• Level mit Tilemap\n\nLass uns Mario-Magie machen!',
502
554
  },
@@ -517,7 +569,7 @@ const TUTORIAL_PLATFORMER = {
517
569
  pl: 'Skonfiguruj encję gracza z komponentami Transform, Sprite, Collider2D i RigidBody2D.',
518
570
  de: 'Erstelle die Spieler-Entity mit Transform, Sprite, Collider2D und RigidBody2D Komponenten.',
519
571
  },
520
- codeTemplate: "// Create the player\nconst player = world.createEntity();\n",
572
+ codeTemplate: '// Create the player\nconst player = world.createEntity();\n',
521
573
  codeSolution: "const player = world.createEntity();\nworld.addComponent(player, 'Transform', { x: 100, y: 300, scaleX: 1, scaleY: 1, rotation: 0 });\nworld.addComponent(player, 'Sprite', { textureId: 'player', width: 32, height: 32 });\nworld.addComponent(player, 'Collider2D', { type: 'aabb', width: 28, height: 30, offsetX: 2, offsetY: 2, isTrigger: false });\nworld.addComponent(player, 'RigidBody2D', {\n velocityX: 0, velocityY: 0,\n mass: 1, gravityScale: 1,\n isKinematic: false, restitution: 0,\n});",
522
574
  codeValidation: {
523
575
  containsAll: ['createEntity', 'Transform', 'Collider2D', 'RigidBody2D'],
@@ -542,7 +594,7 @@ const TUTORIAL_PLATFORMER = {
542
594
  pl: 'Ruch w platformerze używa przyspieszenia i hamowania dla płynnego wrażenia. Sprawdź akcje `moveRight` i `moveLeft` i przyłóż prędkość.',
543
595
  de: 'Platformer-Bewegung nutzt Beschleunigung und Verzögerung für ein flüssiges Gefühl. Prüfe `moveRight` und `moveLeft` Aktionen und wende Geschwindigkeit an.',
544
596
  },
545
- codeTemplate: "// Movement system\nconst MOVE_SPEED = 200;\nconst ACCEL = 800;\nconst FRICTION = 600;\n",
597
+ codeTemplate: '// Movement system\nconst MOVE_SPEED = 200;\nconst ACCEL = 800;\nconst FRICTION = 600;\n',
546
598
  codeSolution: "const MOVE_SPEED = 200;\nconst ACCEL = 800;\nconst FRICTION = 600;\n\nfunction updateMovement(input: InputManager, rb: any, dt: number) {\n let moveDir = 0;\n if (input.getAction('moveRight')) moveDir += 1;\n if (input.getAction('moveLeft')) moveDir -= 1;\n\n if (moveDir !== 0) {\n // Accelerate toward target speed\n rb.velocityX += moveDir * ACCEL * dt;\n // Clamp to max speed\n if (Math.abs(rb.velocityX) > MOVE_SPEED) {\n rb.velocityX = Math.sign(rb.velocityX) * MOVE_SPEED;\n }\n } else {\n // Apply friction\n if (Math.abs(rb.velocityX) > FRICTION * dt) {\n rb.velocityX -= Math.sign(rb.velocityX) * FRICTION * dt;\n } else {\n rb.velocityX = 0;\n }\n }\n}",
547
599
  codeValidation: {
548
600
  containsAll: ['ACCEL', 'FRICTION', 'velocityX', 'getAction'],
@@ -567,7 +619,7 @@ const TUTORIAL_PLATFORMER = {
567
619
  pl: 'Coyote time pozwala graczowi skoczyć chwilę po opuszczeniu platformy — co sprawia, że gra jest bardziej wybaczalna. Śledź czas ostatniego kontaktu z podłożem.',
568
620
  de: 'Coyote-Time erlaubt dem Spieler kurz nach Verlassen einer Plattform zu springen — das Spiel fühlt sich nachsichtiger an. Verfolge die letzte Bodenberührungs-Zeit.',
569
621
  },
570
- codeTemplate: "// Jump system with coyote time\nconst JUMP_FORCE = -350;\nconst COYOTE_TIME = 0.1; // 100ms grace period\n",
622
+ codeTemplate: '// Jump system with coyote time\nconst JUMP_FORCE = -350;\nconst COYOTE_TIME = 0.1; // 100ms grace period\n',
571
623
  codeSolution: "const JUMP_FORCE = -350;\nconst COYOTE_TIME = 0.1; // 100ms grace period\nlet lastGroundedTime = 0;\nlet hasJumped = false;\n\nfunction updateJump(input: InputManager, rb: any, isGrounded: boolean, time: number) {\n if (isGrounded) {\n lastGroundedTime = time;\n hasJumped = false;\n }\n\n const canJump = !hasJumped && (time - lastGroundedTime) < COYOTE_TIME;\n\n if (input.getAction('jump') && canJump) {\n rb.velocityY = JUMP_FORCE;\n hasJumped = true;\n }\n\n // Variable jump height — release early = lower jump\n if (!input.getAction('jump') && rb.velocityY < 0) {\n rb.velocityY *= 0.5;\n }\n}",
572
624
  codeValidation: {
573
625
  containsAll: ['COYOTE_TIME', 'JUMP_FORCE', 'lastGroundedTime', 'canJump'],
@@ -592,7 +644,7 @@ const TUTORIAL_PLATFORMER = {
592
644
  pl: 'Dodaj dodatkowy skok w powietrzu (podwójny skok) i pozwól graczowi powoli zsuwać się po ścianach gdy ich dotyka.',
593
645
  de: 'Füge einen zusätzlichen Sprung in der Luft (Doppelsprung) hinzu und lass den Spieler langsam an Wänden herunterrutschen.',
594
646
  },
595
- codeTemplate: "// Double jump + wall slide\nconst MAX_AIR_JUMPS = 1;\nconst WALL_SLIDE_SPEED = 50;\n",
647
+ codeTemplate: '// Double jump + wall slide\nconst MAX_AIR_JUMPS = 1;\nconst WALL_SLIDE_SPEED = 50;\n',
596
648
  codeSolution: "const MAX_AIR_JUMPS = 1;\nconst WALL_SLIDE_SPEED = 50;\nlet airJumpsLeft = MAX_AIR_JUMPS;\n\nfunction updateAirMoves(input: InputManager, rb: any, isGrounded: boolean, isTouchingWall: boolean) {\n if (isGrounded) {\n airJumpsLeft = MAX_AIR_JUMPS;\n }\n\n // Double jump (only when not grounded)\n if (input.getAction('jump') && !isGrounded && airJumpsLeft > 0) {\n rb.velocityY = JUMP_FORCE;\n airJumpsLeft--;\n }\n\n // Wall slide — slow down fall when touching wall\n if (isTouchingWall && !isGrounded && rb.velocityY > 0) {\n rb.velocityY = Math.min(rb.velocityY, WALL_SLIDE_SPEED);\n }\n}",
597
649
  codeValidation: {
598
650
  containsAll: ['MAX_AIR_JUMPS', 'WALL_SLIDE_SPEED', 'airJumpsLeft'],
@@ -617,7 +669,7 @@ const TUTORIAL_PLATFORMER = {
617
669
  pl: 'Dash daje graczowi nagły przypływ prędkości w aktualnym kierunku. Dodaj cooldown, żeby nie spamować.',
618
670
  de: 'Ein Dash gibt dem Spieler einen Geschwindigkeitsschub in die aktuelle Richtung. Füge einen Cooldown hinzu.',
619
671
  },
620
- codeTemplate: "// Dash system\nconst DASH_SPEED = 500;\nconst DASH_DURATION = 0.15;\nconst DASH_COOLDOWN = 0.5;\n",
672
+ codeTemplate: '// Dash system\nconst DASH_SPEED = 500;\nconst DASH_DURATION = 0.15;\nconst DASH_COOLDOWN = 0.5;\n',
621
673
  codeSolution: "const DASH_SPEED = 500;\nconst DASH_DURATION = 0.15;\nconst DASH_COOLDOWN = 0.5;\nlet dashTimer = 0;\nlet dashCooldown = 0;\nlet dashDir = 1;\n\nfunction updateDash(input: InputManager, rb: any, dt: number, facingRight: boolean) {\n dashCooldown = Math.max(0, dashCooldown - dt);\n\n if (input.getAction('dash') && dashCooldown <= 0 && dashTimer <= 0) {\n dashTimer = DASH_DURATION;\n dashCooldown = DASH_COOLDOWN;\n dashDir = facingRight ? 1 : -1;\n }\n\n if (dashTimer > 0) {\n dashTimer -= dt;\n rb.velocityX = DASH_SPEED * dashDir;\n rb.velocityY = 0; // freeze vertical during dash\n }\n}",
622
674
  codeValidation: {
623
675
  containsAll: ['DASH_SPEED', 'DASH_COOLDOWN', 'dashTimer'],
@@ -642,7 +694,7 @@ const TUTORIAL_PLATFORMER = {
642
694
  pl: 'Utwórz wrogów patrolujących między dwoma punktami. Gdy gracz się zbliży, przechodzą w tryb pościgu!',
643
695
  de: 'Erstelle Feinde, die zwischen zwei Punkten patrouillieren. Wenn der Spieler nah kommt, wechseln sie in den Verfolgungsmodus!',
644
696
  },
645
- codeTemplate: "// Enemy patrol + chase AI\nconst PATROL_SPEED = 60;\nconst CHASE_SPEED = 120;\nconst DETECT_RANGE = 150;\n",
697
+ codeTemplate: '// Enemy patrol + chase AI\nconst PATROL_SPEED = 60;\nconst CHASE_SPEED = 120;\nconst DETECT_RANGE = 150;\n',
646
698
  codeSolution: "const PATROL_SPEED = 60;\nconst CHASE_SPEED = 120;\nconst DETECT_RANGE = 150;\n\nfunction updateEnemy(enemy: any, playerPos: any, dt: number) {\n const dx = playerPos.x - enemy.x;\n const dy = playerPos.y - enemy.y;\n const dist = Math.sqrt(dx * dx + dy * dy);\n\n if (dist < DETECT_RANGE) {\n // Chase mode — move toward player\n const dir = dx > 0 ? 1 : -1;\n enemy.velocityX = CHASE_SPEED * dir;\n enemy.state = 'chase';\n } else {\n // Patrol mode — walk between patrolLeft and patrolRight\n if (enemy.x <= enemy.patrolLeft) enemy.patrolDir = 1;\n if (enemy.x >= enemy.patrolRight) enemy.patrolDir = -1;\n enemy.velocityX = PATROL_SPEED * enemy.patrolDir;\n enemy.state = 'patrol';\n }\n}",
647
699
  codeValidation: {
648
700
  containsAll: ['PATROL_SPEED', 'CHASE_SPEED', 'DETECT_RANGE'],
@@ -674,7 +726,7 @@ const TUTORIAL_PLATFORMER = {
674
726
  containsAll: ['createEntity', 'isTrigger', 'destroyEntity'],
675
727
  },
676
728
  hint: {
677
- en: 'Set isTrigger: true on coins so they don\'t push the player',
729
+ en: "Set isTrigger: true on coins so they don't push the player",
678
730
  pl: 'Ustaw isTrigger: true na monetach, aby nie odpychały gracza',
679
731
  de: 'Setze isTrigger: true auf Münzen, damit sie den Spieler nicht wegstoßen',
680
732
  },
@@ -693,7 +745,7 @@ const TUTORIAL_PLATFORMER = {
693
745
  pl: 'Użyj `Tilemap` do zbudowania poziomu. Zdefiniuj podłoże, platformy i ściany z kafelków.',
694
746
  de: 'Verwende eine `Tilemap` um das Level zu bauen. Definiere Boden, Plattformen und Wände mit Kacheln.',
695
747
  },
696
- codeTemplate: "// Create the level tilemap\nconst level = new Tilemap(20, 15, 32);\n",
748
+ codeTemplate: '// Create the level tilemap\nconst level = new Tilemap(20, 15, 32);\n',
697
749
  codeSolution: "const level = new Tilemap(20, 15, 32);\n\n// Add a ground layer\nlevel.addLayer('ground', 20, 15);\n\n// Fill the ground row\nlevel.fillRect('ground', 0, 14, 20, 1, 1); // bottom row = solid\n\n// Add some platforms\nlevel.setTile('ground', 5, 10, 1); // floating block\nlevel.setTile('ground', 6, 10, 1);\nlevel.setTile('ground', 7, 10, 1);\n\nlevel.fillRect('ground', 12, 8, 4, 1, 1); // higher platform\n\n// Check collisions with tilemap\nconst isSolid = level.isSolid(5, 14); // true — ground!",
698
750
  codeValidation: {
699
751
  containsAll: ['Tilemap', 'addLayer', 'fillRect'],
@@ -714,7 +766,7 @@ const TUTORIAL_PLATFORMER = {
714
766
  de: 'Platformer fertig! 🎮',
715
767
  },
716
768
  content: {
717
- en: 'Congratulations! You\'ve built a complete platformer with:\n• Smooth acceleration-based movement\n• Jumping with coyote time\n• Double jump & wall slide\n• Dash mechanic\n• Enemy AI (patrol & chase)\n• Coins with trigger colliders\n• Tilemap level design\n\nChallenge: Add moving platforms and a boss fight!',
769
+ en: "Congratulations! You've built a complete platformer with:\n• Smooth acceleration-based movement\n• Jumping with coyote time\n• Double jump & wall slide\n• Dash mechanic\n• Enemy AI (patrol & chase)\n• Coins with trigger colliders\n• Tilemap level design\n\nChallenge: Add moving platforms and a boss fight!",
718
770
  pl: 'Gratulacje! Zbudowałeś kompletnego platformera z:\n• Płynny ruch z przyspieszeniem\n• Skakanie z coyote time\n• Podwójny skok i wall-slide\n• Mechanika dashu\n• AI wrogów (patrol i pościg)\n• Monety z trigger koliderami\n• Design poziomu z tilemapy\n\nWyzwanie: Dodaj ruchome platformy i walkę z bossem!',
719
771
  de: 'Herzlichen Glückwunsch! Du hast einen kompletten Platformer gebaut mit:\n• Flüssige beschleunigungsbasierte Bewegung\n• Springen mit Coyote-Time\n• Doppelsprung & Wall-Slide\n• Dash-Mechanik\n• Feind-KI (Patrouille & Verfolgung)\n• Münzen mit Trigger-Kollidern\n• Tilemap Level-Design\n\nHerausforderung: Füge bewegliche Plattformen und einen Bosskampf hinzu!',
720
772
  },
@@ -725,11 +777,7 @@ const TUTORIAL_PLATFORMER = {
725
777
  Convenience export
726
778
  ══════════════════════════════════════════════════════════════ */
727
779
  function getTutorials_T09_T11() {
728
- return [
729
- TUTORIAL_MULTIPLAYER_ONLINE,
730
- TUTORIAL_PROCEDURAL_WORLD,
731
- TUTORIAL_PLATFORMER,
732
- ];
780
+ return [TUTORIAL_MULTIPLAYER_ONLINE, TUTORIAL_PROCEDURAL_WORLD, TUTORIAL_PLATFORMER];
733
781
  }
734
782
 
735
783
  export { TUTORIAL_MULTIPLAYER_ONLINE, TUTORIAL_PLATFORMER, TUTORIAL_PROCEDURAL_WORLD, getTutorials_T09_T11 };
@@ -39,7 +39,7 @@ const TUTORIAL_RPG = {
39
39
  de: 'Baue dein eigenes RPG! ⚔️',
40
40
  },
41
41
  content: {
42
- en: 'In this tutorial you\'ll build a classic top-down RPG!\n\n• Character stats (HP, MP, ATK, DEF, SPD)\n• XP and leveling system\n• Inventory and items\n• NPCs with dialogue\n• Turn-based battle system\n• Tilemap world with warps\n\nReady for an adventure?',
42
+ en: "In this tutorial you'll build a classic top-down RPG!\n\n• Character stats (HP, MP, ATK, DEF, SPD)\n• XP and leveling system\n• Inventory and items\n• NPCs with dialogue\n• Turn-based battle system\n• Tilemap world with warps\n\nReady for an adventure?",
43
43
  pl: 'W tym tutorialu zbudujesz klasyczne RPG z widokiem z góry!\n\n• Statystyki postaci (HP, MP, ATK, DEF, SPD)\n• System XP i poziomów\n• Ekwipunek i przedmioty\n• NPC z dialogami\n• System walki turowej\n• Świat z tilemapy z przejściami\n\nGotowy na przygodę?',
44
44
  de: 'In diesem Tutorial baust du ein klassisches Top-Down-RPG!\n\n• Charakterwerte (HP, MP, ATK, DEF, SPD)\n• XP- und Levelsystem\n• Inventar und Items\n• NPCs mit Dialogen\n• Rundenbasiertes Kampfsystem\n• Tilemap-Welt mit Warps\n\nBereit für ein Abenteuer?',
45
45
  },
@@ -56,7 +56,7 @@ const TUTORIAL_RPG = {
56
56
  de: 'Charakterwerte & Leveling',
57
57
  },
58
58
  content: {
59
- en: 'Define your hero\'s stats. Use the RPG template config for stat growth curves and XP requirements.',
59
+ en: "Define your hero's stats. Use the RPG template config for stat growth curves and XP requirements.",
60
60
  pl: 'Zdefiniuj statystyki bohatera. Użyj konfiguracji szablonu RPG dla krzywych wzrostu statystyk i wymogów XP.',
61
61
  de: 'Definiere die Werte deines Helden. Verwende die RPG-Template-Konfiguration für Wachstumskurven und XP-Anforderungen.',
62
62
  },
@@ -110,7 +110,7 @@ const TUTORIAL_RPG = {
110
110
  pl: 'Stwórz NPC z drzewami dialogów. Gracz może wybierać odpowiedzi, które rozgałęziają rozmowę.',
111
111
  de: 'Erstelle NPCs mit Dialogbäumen. Spieler können Antworten wählen, die das Gespräch verzweigen.',
112
112
  },
113
- codeTemplate: "// Dialogue system\ninterface DialogNode {\n id: string;\n text: string;\n",
113
+ codeTemplate: '// Dialogue system\ninterface DialogNode {\n id: string;\n text: string;\n',
114
114
  codeSolution: "interface DialogNode {\n id: string;\n text: string;\n speaker: string;\n choices?: { text: string; next: string; condition?: string }[];\n next?: string; // auto-advance to next node\n}\n\nconst innkeeperDialog: DialogNode[] = [\n {\n id: 'greet',\n speaker: 'Innkeeper',\n text: 'Welcome, traveler! Would you like to rest?',\n choices: [\n { text: 'Yes, I need rest. (10 gold)', next: 'rest' },\n { text: 'No thanks.', next: 'bye' },\n { text: 'Any rumors?', next: 'rumors' },\n ],\n },\n {\n id: 'rest',\n speaker: 'Innkeeper',\n text: 'Sleep well! *HP and MP fully restored*',\n next: 'greet',\n },\n {\n id: 'rumors',\n speaker: 'Innkeeper',\n text: 'I heard monsters in the eastern cave grew stronger...',\n next: 'greet',\n },\n {\n id: 'bye',\n speaker: 'Innkeeper',\n text: 'Safe travels!',\n },\n];",
115
115
  codeValidation: {
116
116
  containsAll: ['DialogNode', 'speaker', 'choices', 'next'],
@@ -161,7 +161,7 @@ const TUTORIAL_RPG = {
161
161
  de: 'Erstelle mehrere Räume verbunden durch Warps. Verwende Tilemaps für jeden Bereich (Stadt, Wald, Höhle, Burg).',
162
162
  },
163
163
  requiredAction: 'create-world-map',
164
- codeTemplate: "// Define world areas\nconst areas = [\n",
164
+ codeTemplate: '// Define world areas\nconst areas = [\n',
165
165
  codeSolution: "const areas = [\n { id: 'town', name: 'Starter Town', tilemap: 'town-map', npcs: ['innkeeper', 'shopkeeper'] },\n { id: 'forest', name: 'Dark Forest', tilemap: 'forest-map', enemies: ['wolf', 'slime'] },\n { id: 'cave', name: 'Crystal Cave', tilemap: 'cave-map', enemies: ['bat', 'golem'] },\n { id: 'castle', name: 'Demon Castle', tilemap: 'castle-map', enemies: ['skeleton', 'demon-lord'] },\n];\n\nconst warps = [\n { from: 'town', to: 'forest', x: 19, y: 7 },\n { from: 'forest', to: 'cave', x: 15, y: 2 },\n { from: 'forest', to: 'town', x: 0, y: 7 },\n { from: 'cave', to: 'castle', x: 10, y: 0 },\n];",
166
166
  codeValidation: {
167
167
  containsAll: ['areas', 'warps', 'tilemap'],
@@ -199,7 +199,7 @@ const TUTORIAL_RPG = {
199
199
  de: 'RPG fertig! 🏰',
200
200
  },
201
201
  content: {
202
- en: 'Amazing! You\'ve built a full RPG with:\n• Character stats & leveling\n• Inventory with rarity system\n• NPCs with branching dialogue\n• Turn-based combat\n• Multi-area world map with warps\n\nChallenge: Add a party system with 4 heroes!',
202
+ en: "Amazing! You've built a full RPG with:\n• Character stats & leveling\n• Inventory with rarity system\n• NPCs with branching dialogue\n• Turn-based combat\n• Multi-area world map with warps\n\nChallenge: Add a party system with 4 heroes!",
203
203
  pl: 'Niesamowite! Zbudowałeś pełne RPG z:\n• Statystyki i poziomy postaci\n• Ekwipunek z systemem rzadkości\n• NPC z rozgałęzionymi dialogami\n• Walki turowe\n• Mapa świata z wieloma obszarami\n\nWyzwanie: Dodaj system drużyny z 4 bohaterami!',
204
204
  de: 'Fantastisch! Du hast ein vollständiges RPG gebaut mit:\n• Charakterwerte & Leveling\n• Inventar mit Seltenheitssystem\n• NPCs mit verzweigten Dialogen\n• Rundenbasierter Kampf\n• Weltkarte mit mehreren Bereichen\n\nHerausforderung: Füge ein Gruppensystem mit 4 Helden hinzu!',
205
205
  },
@@ -239,7 +239,7 @@ const TUTORIAL_TOWER_DEFENSE = {
239
239
  de: 'Baue Tower Defense! 🗼',
240
240
  },
241
241
  content: {
242
- en: 'In this tutorial you\'ll build a tower defense game!\n\n• Map with path for enemies\n• Tower types (arrow, cannon, ice, laser)\n• Enemy waves with scaling difficulty\n• Tower targeting modes\n• Upgrade system\n• Gold & economy\n\nDefend your base!',
242
+ en: "In this tutorial you'll build a tower defense game!\n\n• Map with path for enemies\n• Tower types (arrow, cannon, ice, laser)\n• Enemy waves with scaling difficulty\n• Tower targeting modes\n• Upgrade system\n• Gold & economy\n\nDefend your base!",
243
243
  pl: 'W tym tutorialu zbudujesz grę tower defense!\n\n• Mapa ze ścieżką dla wrogów\n• Typy wież (strzały, armata, lód, laser)\n• Fale wrogów ze skalującą się trudnością\n• Tryby celowania wież\n• System ulepszeń\n• Złoto i ekonomia\n\nBroń swojej bazy!',
244
244
  de: 'In diesem Tutorial baust du ein Tower-Defense-Spiel!\n\n• Karte mit Feindpfad\n• Turmtypen (Pfeil, Kanone, Eis, Laser)\n• Feindwellen mit steigender Schwierigkeit\n• Turm-Zielmodi\n• Upgrade-System\n• Gold & Wirtschaft\n\nVerteidige deine Basis!',
245
245
  },
@@ -286,7 +286,7 @@ const TUTORIAL_TOWER_DEFENSE = {
286
286
  de: 'Feinde folgen einem vordefinierten Pfad vom Start zum Ende. Definiere Wegpunkte, zwischen denen Feinde laufen.',
287
287
  },
288
288
  codeTemplate: '// Enemy path waypoints\nconst path = [\n',
289
- codeSolution: "const path = [\n { x: 0, y: 200 }, // start (left edge)\n { x: 200, y: 200 }, // turn down\n { x: 200, y: 400 },\n { x: 400, y: 400 }, // turn up\n { x: 400, y: 100 },\n { x: 600, y: 100 }, // final stretch\n { x: 600, y: 300 },\n { x: 800, y: 300 }, // end (base)\n];\n\nfunction moveAlongPath(enemy: any, dt: number) {\n const target = path[enemy.waypointIndex];\n const dx = target.x - enemy.x;\n const dy = target.y - enemy.y;\n const dist = Math.sqrt(dx * dx + dy * dy);\n\n if (dist < 5) {\n enemy.waypointIndex++;\n if (enemy.waypointIndex >= path.length) {\n // Enemy reached the base!\n enemy.reachedEnd = true;\n }\n } else {\n enemy.x += (dx / dist) * enemy.speed * dt;\n enemy.y += (dy / dist) * enemy.speed * dt;\n }\n}",
289
+ codeSolution: 'const path = [\n { x: 0, y: 200 }, // start (left edge)\n { x: 200, y: 200 }, // turn down\n { x: 200, y: 400 },\n { x: 400, y: 400 }, // turn up\n { x: 400, y: 100 },\n { x: 600, y: 100 }, // final stretch\n { x: 600, y: 300 },\n { x: 800, y: 300 }, // end (base)\n];\n\nfunction moveAlongPath(enemy: any, dt: number) {\n const target = path[enemy.waypointIndex];\n const dx = target.x - enemy.x;\n const dy = target.y - enemy.y;\n const dist = Math.sqrt(dx * dx + dy * dy);\n\n if (dist < 5) {\n enemy.waypointIndex++;\n if (enemy.waypointIndex >= path.length) {\n // Enemy reached the base!\n enemy.reachedEnd = true;\n }\n } else {\n enemy.x += (dx / dist) * enemy.speed * dt;\n enemy.y += (dy / dist) * enemy.speed * dt;\n }\n}',
290
290
  codeValidation: {
291
291
  containsAll: ['path', 'waypointIndex', 'moveAlongPath'],
292
292
  },
@@ -361,7 +361,7 @@ const TUTORIAL_TOWER_DEFENSE = {
361
361
  de: 'Türme können aufgewertet werden für mehr Schaden und Reichweite. Verdiene Gold durch Feind-Kills. Verkaufe Türme für Rückerstattung.',
362
362
  },
363
363
  codeTemplate: '// Upgrade system\nlet gold = 200;\n',
364
- codeSolution: "let gold = 200;\n\nfunction upgradeTower(tower: any): boolean {\n const upgradeCost = Math.floor(tower.cost * 0.75 * tower.level);\n if (gold < upgradeCost) return false;\n\n gold -= upgradeCost;\n tower.level++;\n tower.damage = Math.floor(tower.damage * 1.3);\n tower.range += 10;\n tower.fireRate *= 1.1;\n return true;\n}\n\nfunction sellTower(tower: any): number {\n const refund = Math.floor(tower.cost * 0.6);\n gold += refund;\n return refund;\n}\n\nfunction onEnemyKilled(enemy: any) {\n gold += enemy.goldReward;\n}",
364
+ codeSolution: 'let gold = 200;\n\nfunction upgradeTower(tower: any): boolean {\n const upgradeCost = Math.floor(tower.cost * 0.75 * tower.level);\n if (gold < upgradeCost) return false;\n\n gold -= upgradeCost;\n tower.level++;\n tower.damage = Math.floor(tower.damage * 1.3);\n tower.range += 10;\n tower.fireRate *= 1.1;\n return true;\n}\n\nfunction sellTower(tower: any): number {\n const refund = Math.floor(tower.cost * 0.6);\n gold += refund;\n return refund;\n}\n\nfunction onEnemyKilled(enemy: any) {\n gold += enemy.goldReward;\n}',
365
365
  codeValidation: {
366
366
  containsAll: ['upgradeTower', 'sellTower', 'gold'],
367
367
  },
@@ -381,7 +381,7 @@ const TUTORIAL_TOWER_DEFENSE = {
381
381
  de: 'Tower Defense fertig! 🏆',
382
382
  },
383
383
  content: {
384
- en: 'You\'ve built a full tower defense game!\n• 4 tower types with different targeting\n• Enemy pathfinding along waypoints\n• Escalating wave system\n• Upgrade & sell mechanics\n• Gold economy\n\nChallenge: Add flying enemies that ignore the path!',
384
+ en: "You've built a full tower defense game!\n• 4 tower types with different targeting\n• Enemy pathfinding along waypoints\n• Escalating wave system\n• Upgrade & sell mechanics\n• Gold economy\n\nChallenge: Add flying enemies that ignore the path!",
385
385
  pl: 'Zbudowałeś kompletną grę tower defense!\n• 4 typy wież z różnym celowaniem\n• Pathfinding wrogów po waypointach\n• Eskalujący system fal\n• Mechaniki ulepszania i sprzedaży\n• Ekonomia złota\n\nWyzwanie: Dodaj latających wrogów ignorujących ścieżkę!',
386
386
  de: 'Du hast ein komplettes Tower-Defense-Spiel gebaut!\n• 4 Turmtypen mit verschiedenem Zielen\n• Feind-Pathfinding entlang Wegpunkten\n• Eskalierendes Wellensystem\n• Upgrade- & Verkaufsmechaniken\n• Gold-Wirtschaft\n\nHerausforderung: Füge fliegende Feinde hinzu, die den Pfad ignorieren!',
387
387
  },
@@ -421,7 +421,7 @@ const TUTORIAL_3D_GAME = {
421
421
  de: 'Willkommen in 3D! 🎮',
422
422
  },
423
423
  content: {
424
- en: 'In this tutorial you\'ll enter the third dimension!\n\n• Set up a 3D world (World3D)\n• Add meshes (cube, sphere, plane)\n• Lighting (directional, point, spot)\n• Camera controllers (FPS, orbit)\n• 3D physics (rigid bodies, colliders)\n• Character controller (walking, jumping)\n\nLet\'s go 3D!',
424
+ en: "In this tutorial you'll enter the third dimension!\n\n• Set up a 3D world (World3D)\n• Add meshes (cube, sphere, plane)\n• Lighting (directional, point, spot)\n• Camera controllers (FPS, orbit)\n• 3D physics (rigid bodies, colliders)\n• Character controller (walking, jumping)\n\nLet's go 3D!",
425
425
  pl: 'W tym tutorialu wejdziesz w trzeci wymiar!\n\n• Skonfiguruj świat 3D (World3D)\n• Dodaj meshe (sześcian, sfera, płaszczyzna)\n• Oświetlenie (kierunkowe, punktowe, spot)\n• Kontrolery kamery (FPS, orbita)\n• Fizyka 3D (rigid body, kolidery)\n• Kontroler postaci (chodzenie, skakanie)\n\nWejdźmy w 3D!',
426
426
  de: 'In diesem Tutorial betrittst du die dritte Dimension!\n\n• 3D-Welt einrichten (World3D)\n• Meshes hinzufügen (Würfel, Kugel, Ebene)\n• Beleuchtung (Richtung, Punkt, Spot)\n• Kamera-Controller (FPS, Orbit)\n• 3D-Physik (Starrkörper, Kollider)\n• Character-Controller (Laufen, Springen)\n\nAb in die 3. Dimension!',
427
427
  },
@@ -588,7 +588,7 @@ const TUTORIAL_3D_GAME = {
588
588
  de: '3D-Spiel fertig! 🌟',
589
589
  },
590
590
  content: {
591
- en: 'You\'ve built your first 3D game!\n• World3D scene with multiple objects\n• Primitive meshes (cube, sphere, plane)\n• Directional and point lighting\n• Orbit camera controller\n• 3D physics with rigid bodies\n• Character controller with jumping\n\nChallenge: Add a skybox and third-person camera!',
591
+ en: "You've built your first 3D game!\n• World3D scene with multiple objects\n• Primitive meshes (cube, sphere, plane)\n• Directional and point lighting\n• Orbit camera controller\n• 3D physics with rigid bodies\n• Character controller with jumping\n\nChallenge: Add a skybox and third-person camera!",
592
592
  pl: 'Zbudowałeś swoją pierwszą grę 3D!\n• Scena World3D z wieloma obiektami\n• Prymitywne meshe (sześcian, sfera, płaszczyzna)\n• Oświetlenie kierunkowe i punktowe\n• Kontroler kamery orbitalnej\n• Fizyka 3D z ciałami sztywnymi\n• Kontroler postaci ze skakaniem\n\nWyzwanie: Dodaj skybox i kamerę trzeciej osoby!',
593
593
  de: 'Du hast dein erstes 3D-Spiel gebaut!\n• World3D-Szene mit mehreren Objekten\n• Primitive Meshes (Würfel, Kugel, Ebene)\n• Richtungs- und Punktbeleuchtung\n• Orbit-Kamera-Controller\n• 3D-Physik mit Starrkörpern\n• Character-Controller mit Springen\n\nHerausforderung: Füge eine Skybox und Third-Person-Kamera hinzu!',
594
594
  },
@@ -628,9 +628,9 @@ const TUTORIAL_PUBLISHING = {
628
628
  de: 'Veröffentliche dein Spiel! 🚀',
629
629
  },
630
630
  content: {
631
- en: 'Your game is ready — now let\'s share it with the world!\n\n• Feature detection (check browser support)\n• Build optimization (minify, tree-shake)\n• PWA export (installable web app)\n• Electron export (Windows/Mac/Linux)\n• Capacitor export (iOS/Android)\n• Performance benchmarking\n\nLet\'s deploy!',
631
+ en: "Your game is ready — now let's share it with the world!\n\n• Feature detection (check browser support)\n• Build optimization (minify, tree-shake)\n• PWA export (installable web app)\n• Electron export (Windows/Mac/Linux)\n• Capacitor export (iOS/Android)\n• Performance benchmarking\n\nLet's deploy!",
632
632
  pl: 'Twoja gra jest gotowa — czas podzielić się nią ze światem!\n\n• Wykrywanie funkcji (sprawdź wsparcie przeglądarki)\n• Optymalizacja buildu (minifikacja, tree-shaking)\n• Eksport PWA (instalowalna web app)\n• Eksport Electron (Windows/Mac/Linux)\n• Eksport Capacitor (iOS/Android)\n• Benchmarkowanie wydajności\n\nWdrażamy!',
633
- de: 'Dein Spiel ist bereit — teile es mit der Welt!\n\n• Feature-Erkennung (Browser-Support)\n• Build-Optimierung (Minifizierung, Tree-Shaking)\n• PWA-Export (installierbare Web-App)\n• Electron-Export (Windows/Mac/Linux)\n• Capacitor-Export (iOS/Android)\n• Performance-Benchmarking\n\nLos geht\'s!',
633
+ de: "Dein Spiel ist bereit — teile es mit der Welt!\n\n• Feature-Erkennung (Browser-Support)\n• Build-Optimierung (Minifizierung, Tree-Shaking)\n• PWA-Export (installierbare Web-App)\n• Electron-Export (Windows/Mac/Linux)\n• Capacitor-Export (iOS/Android)\n• Performance-Benchmarking\n\nLos geht's!",
634
634
  },
635
635
  skippable: true,
636
636
  tooltipPosition: 'center',
@@ -740,10 +740,38 @@ const TUTORIAL_PUBLISHING = {
740
740
  de: 'Was ist eine PWA (Progressive Web App)?',
741
741
  },
742
742
  quizOptions: [
743
- { id: 'a', text: { en: 'A native mobile app', pl: 'Natywna aplikacja mobilna', de: 'Eine native mobile App' } },
744
- { id: 'b', text: { en: 'A web app that can be installed and works offline', pl: 'Web app instalowalna i działająca offline', de: 'Eine Web-App die installierbar ist und offline funktioniert' } },
745
- { id: 'c', text: { en: 'A special browser plugin', pl: 'Specjalna wtyczka przeglądarki', de: 'Ein spezielles Browser-Plugin' } },
746
- { id: 'd', text: { en: 'A server-side application', pl: 'Aplikacja serwerowa', de: 'Eine serverseitige Anwendung' } },
743
+ {
744
+ id: 'a',
745
+ text: {
746
+ en: 'A native mobile app',
747
+ pl: 'Natywna aplikacja mobilna',
748
+ de: 'Eine native mobile App',
749
+ },
750
+ },
751
+ {
752
+ id: 'b',
753
+ text: {
754
+ en: 'A web app that can be installed and works offline',
755
+ pl: 'Web app instalowalna i działająca offline',
756
+ de: 'Eine Web-App die installierbar ist und offline funktioniert',
757
+ },
758
+ },
759
+ {
760
+ id: 'c',
761
+ text: {
762
+ en: 'A special browser plugin',
763
+ pl: 'Specjalna wtyczka przeglądarki',
764
+ de: 'Ein spezielles Browser-Plugin',
765
+ },
766
+ },
767
+ {
768
+ id: 'd',
769
+ text: {
770
+ en: 'A server-side application',
771
+ pl: 'Aplikacja serwerowa',
772
+ de: 'Eine serverseitige Anwendung',
773
+ },
774
+ },
747
775
  ],
748
776
  quizCorrectId: 'b',
749
777
  },
@@ -768,12 +796,7 @@ const TUTORIAL_PUBLISHING = {
768
796
  Convenience export
769
797
  ══════════════════════════════════════════════════════════════ */
770
798
  function getTutorials_T12_T15() {
771
- return [
772
- TUTORIAL_RPG,
773
- TUTORIAL_TOWER_DEFENSE,
774
- TUTORIAL_3D_GAME,
775
- TUTORIAL_PUBLISHING,
776
- ];
799
+ return [TUTORIAL_RPG, TUTORIAL_TOWER_DEFENSE, TUTORIAL_3D_GAME, TUTORIAL_PUBLISHING];
777
800
  }
778
801
 
779
802
  export { TUTORIAL_3D_GAME, TUTORIAL_PUBLISHING, TUTORIAL_RPG, TUTORIAL_TOWER_DEFENSE, getTutorials_T12_T15 };
@@ -1,7 +1,7 @@
1
1
  export * from './types';
2
2
  export { GameLoop, type GameLoopCallbacks } from './GameLoop';
3
3
  export { GameClock } from './GameClock';
4
- export { GameConfig, DEFAULT_SETTINGS, DEFAULT_PHYSICS, DEFAULT_AUDIO, DEFAULT_RENDERING } from './GameConfig';
4
+ export { GameConfig, DEFAULT_SETTINGS, DEFAULT_PHYSICS, DEFAULT_AUDIO, DEFAULT_RENDERING, } from './GameConfig';
5
5
  export { EventBus, type EventHandler } from './EventBus';
6
6
  export { ServiceLocator, SERVICE_KEYS } from './ServiceLocator';
7
7
  export { v2 } from './math';
@@ -1,3 +1,3 @@
1
1
  export { InputManager, PRESET_PLATFORMER, PRESET_TOPDOWN, PRESET_TWIN_STICK, type GamepadButton, type GamepadAxis, type ActionState, type ActionBinding, type InputPreset, } from './InputManager';
2
2
  export { GamepadNavigationProvider, useGamepadNavigation, type FocusableMeta, type GamepadNavigationContextType, } from './GamepadNavigation';
3
- export { useGamepads, useGamepadEdges, type GamepadSnapshot, } from './useGamepads';
3
+ export { useGamepads, useGamepadEdges, type GamepadSnapshot } from './useGamepads';
@@ -2,7 +2,7 @@ export { I18nManager, getI18n, initI18n, t, registerEngineTranslations, ENGINE_T
2
2
  export type { Locale, TranslationDict, TranslationNamespace, InterpolationValues, PluralCategory, PluralRule, I18nConfig, } from '../i18n/I18n';
3
3
  export { TutorialEngine, getBuiltInTutorials, TUTORIAL_FIRST_GAME, TUTORIAL_SPACE_SHOOTER, TUTORIAL_DATAGRID_BASICS, } from '../tutorials/TutorialEngine';
4
4
  export type { TutorialStepType, TutorialDifficulty, TutorialCategory, QuizOption, CodeValidation, TutorialStep, TutorialDefinition, TutorialProgress, TutorialEvents, CodeValidatorFn, } from '../tutorials/TutorialEngine';
5
- export { useTranslation, I18nProvider, TranslatedText, LocaleSelect, } from '../i18n/useTranslation';
5
+ export { useTranslation, I18nProvider, TranslatedText, LocaleSelect } from '../i18n/useTranslation';
6
6
  export type { UseTranslationResult } from '../i18n/useTranslation';
7
7
  export { KidModeManager, DEFAULT_KID_MODE_CONFIG, KID_DIFFICULTY_PRESETS, VISUAL_BLOCKS, KID_BADGES, KID_SPRITES, } from '../kids/KidMode';
8
8
  export type { AgeGroup, KidDifficulty, BlockCategory, VisualBlock, BlockParam, KidSprite, KidBadge, BadgeCondition, KidProfile, ParentConfig, ClassroomConfig, KidModeConfig, KidDifficultySettings, } from '../kids/KidMode';
@@ -11,7 +11,7 @@ export type { EngineError, APIHint, PerfSnapshot, PerfDashboard, HealthCheckResu
11
11
  export { TUTORIAL_ANIMATIONS, TUTORIAL_TILEMAPS, TUTORIAL_VISUAL_SCRIPTING, TUTORIAL_LOCAL_MULTIPLAYER, getTutorials_T05_T08, } from '../tutorials/content/Tutorials_T05_T08';
12
12
  export { clamp, clampPositive, assertFinite, assertPositive, assertNonNegative, assertInRange, assertNonEmpty, assertCanvasElement, validateGameSettings, validatePhysicsSettings, } from '../core/Validation';
13
13
  export type { ValidationResult } from '../core/Validation';
14
- export { PluginManager, PluginMarketplace, PLUGIN_TEMPLATES, } from '../plugins/PluginSDK';
14
+ export { PluginManager, PluginMarketplace, PLUGIN_TEMPLATES } from '../plugins/PluginSDK';
15
15
  export type { PluginState, PluginType, PluginManifest, PluginContext, PluginDefinition, ExtensionPoint, PluginStorage, PluginInstance, PluginTemplate, MarketplaceEntry, MarketplaceSearchResult, } from '../plugins/PluginSDK';
16
16
  export { TestPluginStorage, TestPluginContext, PluginAssertionError, pluginAssert, PluginTestRunner, createTestManifest, testPluginLifecycle, formatTestResults, } from '../plugins/PluginTestKit';
17
17
  export type { TestStatus, TestResult, TestSuiteResult, PluginTestCase, } from '../plugins/PluginTestKit';
@@ -20,16 +20,16 @@ export { TUTORIAL_RPG, TUTORIAL_TOWER_DEFENSE, TUTORIAL_3D_GAME, TUTORIAL_PUBLIS
20
20
  export { FrameBudgetManager, MemoryLeakDetector, AutoLODManager, WorkerPool, PerfTimer, ObjectPool, QUALITY_PRESETS, } from '../performance/PerformanceAdvanced';
21
21
  export type { FrameBudgetConfig, BudgetReport, FrameBudgetSnapshot, MemoryTracker, MemorySnapshot, LeakSuspect, QualityLevel, AutoLODConfig, QualitySettings, WorkerTaskStatus, WorkerTask, WorkerPoolConfig, } from '../performance/PerformanceAdvanced';
22
22
  export { TutorialOverlay } from '../tutorials/TutorialOverlay';
23
- export type { OverlayConfig, TooltipPosition, OverlayAction, } from '../tutorials/TutorialOverlay';
24
- export { DocRegistry, DOCS_ECS, DOCS_RENDERING, } from '../docs/DocGenerator';
23
+ export type { OverlayConfig, TooltipPosition, OverlayAction } from '../tutorials/TutorialOverlay';
24
+ export { DocRegistry, DOCS_ECS, DOCS_RENDERING } from '../docs/DocGenerator';
25
25
  export type { DocCategory, DocMemberKind, DocParam, DocMember, DocModule, DocSearchResult, } from '../docs/DocGenerator';
26
26
  export { BranchingEngine, AchievementEngine, BUILT_IN_ACHIEVEMENTS, } from '../tutorials/TutorialBranching';
27
27
  export type { BranchConditionType, BranchCondition, BranchRule, BranchingStep, BranchingConfig, BranchContext, AchievementTier, AchievementCondition, AchievementDefinition, AchievementProgress, } from '../tutorials/TutorialBranching';
28
28
  export { HotModuleReplacer, AssetPipeline, DeployManager, CollaborationManager, } from '../devtools/DevTools';
29
29
  export type { HMRModule, HMRUpdate, AssetType, AssetMeta, AssetProcessorOptions, DeployTarget, DeployConfig, DeployResult, CollaboratorCursor, } from '../devtools/DevTools';
30
- export { GuidedWizard, BlockEditor, KID_BLOCK_PALETTES, } from '../kids/KidTools';
30
+ export { GuidedWizard, BlockEditor, KID_BLOCK_PALETTES } from '../kids/KidTools';
31
31
  export type { WizardChoice, WizardStep, WizardResult, BlockShape, DragBlock, BlockPalette, DragDropState, } from '../kids/KidTools';
32
- export { BenchmarkRunner, BUILTIN_BENCHMARKS, } from '../performance/BenchmarkSuite';
32
+ export { BenchmarkRunner, BUILTIN_BENCHMARKS } from '../performance/BenchmarkSuite';
33
33
  export type { BenchmarkScenario, BenchmarkTimings, BenchmarkMemory, BenchmarkResult, BenchmarkReport, RegressionResult, } from '../performance/BenchmarkSuite';
34
34
  export type { BuiltInComponentType, ComponentTypeMap, AnyBuiltInComponent, EngineEventName, EngineEventMap, TimeDelta, StrictRecord, JsonValue, EventCallback, StrictPartial, AtLeastOne, } from '../core/TypeSafety';
35
35
  export { isVec2, isVec3, isRect, isColor, isComponent, isEntityId, isComponentOfType, getTypedComponent, requireComponent, assertNever, } from '../core/TypeSafety';
@@ -49,7 +49,7 @@ export { GUIDE_ARCHITECTURE, GUIDE_MIGRATION, GUIDE_PERFORMANCE, GUIDE_TROUBLESH
49
49
  export type { Guide, GuideSection } from '../docs/Guides';
50
50
  export { WebGPUContext, GPUPhysicsBroadphase, GPUParticleSystem, GPUPathfinder, isWebGPUAvailable, getWebGPUShaders, } from '../performance/WebGPUCompute';
51
51
  export type { WebGPUConfig, ComputeBuffer, ComputePipeline, PhysicsBroadphaseResult, } from '../performance/WebGPUCompute';
52
- export { WASMAccelerator, benchmarkWASMvsJS, } from '../performance/WASMModules';
52
+ export { WASMAccelerator, benchmarkWASMvsJS } from '../performance/WASMModules';
53
53
  export type { WASMModuleConfig, WASMExports, CollisionResult, PathResult, } from '../performance/WASMModules';
54
54
  export { TextureCompressor, AudioCompressor, MeshCompressor, AssetStreamer, AssetPipeline as CompressionPipeline, } from '../performance/AssetCompression';
55
55
  export type { TextureCompressionConfig, AudioCompressionConfig, MeshCompressionConfig, StreamingConfig, AssetManifest, AssetEntry, CompressionStats, } from '../performance/AssetCompression';
@@ -1,2 +1,2 @@
1
- export { SeededRandom, PerlinNoise, WaveGenerator, LootTable, ProceduralSprite } from './Procedural';
1
+ export { SeededRandom, PerlinNoise, WaveGenerator, LootTable, ProceduralSprite, } from './Procedural';
2
2
  export type { WaveEntry, WaveConfig, LootDrop } from './Procedural';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nice2dev/game-engine",
3
- "version": "1.1.6",
3
+ "version": "1.1.7",
4
4
  "description": "Nice2Dev Game Engine — No-code game engine and editor for React",
5
5
  "author": "NiceToDev <contact@nicetodev.com>",
6
6
  "license": "SEE LICENSE IN LICENSE",
@@ -72,7 +72,7 @@
72
72
  },
73
73
  "devDependencies": {
74
74
  "@microsoft/signalr": "^10.0.11",
75
- "@nice2dev/ui": "^1.1.6",
75
+ "@nice2dev/ui": "^1.1.7",
76
76
  "@rollup/plugin-commonjs": "^29.0.3",
77
77
  "@rollup/plugin-node-resolve": "^16.0.3",
78
78
  "@rollup/plugin-typescript": "^12.3.0",