@nice2dev/game-engine 1.3.15 → 1.3.16

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.
Files changed (434) hide show
  1. package/CHANGELOG.md +9 -0
  2. package/dist/cjs/accessibility/Accessibility.js +152 -165
  3. package/dist/cjs/ai/AI.js +292 -294
  4. package/dist/cjs/ai/BehaviorTree.js +1029 -1113
  5. package/dist/cjs/ai/StateMachine.js +694 -756
  6. package/dist/cjs/animation/Animation.js +232 -241
  7. package/dist/cjs/audio/AudioAdvanced.js +221 -236
  8. package/dist/cjs/audio/AudioBridge.js +441 -435
  9. package/dist/cjs/audio/AudioManager.js +242 -259
  10. package/dist/cjs/build-cloud/BuildOrchestration.js +1059 -1098
  11. package/dist/cjs/build-cloud/GodotBuildCloud.js +641 -670
  12. package/dist/cjs/build-cloud/UnityBuildCloud.js +563 -610
  13. package/dist/cjs/build-cloud/UnrealBuildCloud.js +610 -635
  14. package/dist/cjs/core/EventBus.js +51 -56
  15. package/dist/cjs/core/GameClock.js +62 -66
  16. package/dist/cjs/core/GameConfig.js +72 -79
  17. package/dist/cjs/core/GameLoop.js +80 -89
  18. package/dist/cjs/core/ServiceLocator.js +61 -65
  19. package/dist/cjs/core/Validation.js +72 -82
  20. package/dist/cjs/core/math.js +108 -112
  21. package/dist/cjs/devtools/DevTools.js +542 -563
  22. package/dist/cjs/devtools/DeveloperExperience.js +521 -559
  23. package/dist/cjs/devtools/GameplayAnalytics.js +636 -646
  24. package/dist/cjs/dialogue/DialogueSystem.js +984 -986
  25. package/dist/cjs/docs/DocGenerator.js +339 -364
  26. package/dist/cjs/ecs/World.js +355 -371
  27. package/dist/cjs/editor/AchievementEditor.js +580 -636
  28. package/dist/cjs/editor/AdvancedEditor.js +184 -191
  29. package/dist/cjs/editor/AnimationRetargeting.js +696 -741
  30. package/dist/cjs/editor/AssetBrowser.js +1072 -1160
  31. package/dist/cjs/editor/AssetManager.js +187 -192
  32. package/dist/cjs/editor/ConsolePro.js +971 -1061
  33. package/dist/cjs/editor/CraftingEditor.js +680 -730
  34. package/dist/cjs/editor/DebugTools.js +187 -185
  35. package/dist/cjs/editor/DialogueSystem.js +844 -901
  36. package/dist/cjs/editor/LeaderboardSystem.js +429 -483
  37. package/dist/cjs/editor/NiceGameEditor.js +1903 -1201
  38. package/dist/cjs/editor/PrefabSystem.js +772 -838
  39. package/dist/cjs/editor/ProfilerPanel.js +649 -700
  40. package/dist/cjs/editor/QuestEditor.js +792 -839
  41. package/dist/cjs/editor/ReplaySystem.js +585 -634
  42. package/dist/cjs/editor/SceneEditor.js +176 -200
  43. package/dist/cjs/editor/ScreenshotMode.js +621 -688
  44. package/dist/cjs/editor/ShaderGraph.js +1556 -1582
  45. package/dist/cjs/editor/TimelineEditor.js +715 -778
  46. package/dist/cjs/editor/UndoRedoPro.js +830 -904
  47. package/dist/cjs/editor/VRARMode.js +582 -645
  48. package/dist/cjs/engine/NiceGameEngine.js +141 -164
  49. package/dist/cjs/engine/SaveSystem.js +441 -495
  50. package/dist/cjs/enterprise/Enterprise.js +254 -261
  51. package/dist/cjs/export/BevyExporter.js +927 -967
  52. package/dist/cjs/export/CocosExporter.js +644 -673
  53. package/dist/cjs/export/Construct3Exporter.js +783 -794
  54. package/dist/cjs/export/DefoldExporter.js +1028 -1069
  55. package/dist/cjs/export/GDevelopExporter.js +674 -708
  56. package/dist/cjs/export/GameMakerExporter.js +791 -805
  57. package/dist/cjs/export/GodotExporter.js +1003 -1045
  58. package/dist/cjs/export/MinecraftBedrockExporter.js +573 -578
  59. package/dist/cjs/export/MonoGameExporter.js +1265 -1293
  60. package/dist/cjs/export/Pico8Exporter.js +766 -812
  61. package/dist/cjs/export/PlatformExporter.js +188 -206
  62. package/dist/cjs/export/RPGMakerExporter.js +860 -879
  63. package/dist/cjs/export/RobloxExporter.js +883 -919
  64. package/dist/cjs/export/Solar2DExporter.js +1218 -1256
  65. package/dist/cjs/export/ThreeJSExporter.js +1006 -1073
  66. package/dist/cjs/export/Tic80Exporter.js +1210 -1267
  67. package/dist/cjs/export/UnityExporter.js +1116 -1131
  68. package/dist/cjs/export/UnrealExporter.js +889 -919
  69. package/dist/cjs/export/WebExporter.js +931 -970
  70. package/dist/cjs/export/index.js +42 -65
  71. package/dist/cjs/godot-integration/GodotAssetLibrary.js +659 -692
  72. package/dist/cjs/godot-integration/GodotImport.js +929 -1027
  73. package/dist/cjs/godot-integration/GodotLiveLink.js +915 -945
  74. package/dist/cjs/godot-integration/components/GodotProjectBrowser.js +911 -611
  75. package/dist/cjs/i18n/I18n.js +860 -900
  76. package/dist/cjs/i18n/useTranslation.js +84 -89
  77. package/dist/cjs/import/AsepriteImporter.js +658 -716
  78. package/dist/cjs/import/DragonBonesImporter.js +437 -461
  79. package/dist/cjs/import/GameMakerImporter.js +511 -521
  80. package/dist/cjs/import/GodotSceneImporter.js +788 -822
  81. package/dist/cjs/import/LDtkImporter.js +436 -454
  82. package/dist/cjs/import/Live2DImporter.js +497 -518
  83. package/dist/cjs/import/NdgFormat.js +424 -457
  84. package/dist/cjs/import/OgmoImporter.js +466 -493
  85. package/dist/cjs/import/RPGMakerImporter.js +451 -477
  86. package/dist/cjs/import/SceneImporter.js +378 -423
  87. package/dist/cjs/import/SpineImporter.js +511 -546
  88. package/dist/cjs/import/SpriterImporter.js +590 -613
  89. package/dist/cjs/import/TiledMapImporter.js +811 -846
  90. package/dist/cjs/import/UnitySceneImporter.js +687 -703
  91. package/dist/cjs/import/index.js +206 -251
  92. package/dist/cjs/index.js +275 -278
  93. package/dist/cjs/input/GamepadNavigation.js +185 -205
  94. package/dist/cjs/input/InputManager.js +397 -421
  95. package/dist/cjs/input/useGamepads.js +70 -81
  96. package/dist/cjs/integration/IconSprite.js +129 -256
  97. package/dist/cjs/inventory/InventorySystem.js +920 -995
  98. package/dist/cjs/kids/KidMode.js +997 -1022
  99. package/dist/cjs/kids/KidTools.js +570 -594
  100. package/dist/cjs/monetization/Monetization.js +170 -177
  101. package/dist/cjs/multiplayer/LocalMultiplayer.js +287 -287
  102. package/dist/cjs/multiplayer/MiniGameTypes.js +49 -59
  103. package/dist/cjs/native-engine/AnimationSystem.js +731 -806
  104. package/dist/cjs/native-engine/AssetPipeline.js +454 -515
  105. package/dist/cjs/native-engine/AudioEngine.js +763 -804
  106. package/dist/cjs/native-engine/InputSystem.js +1075 -1145
  107. package/dist/cjs/native-engine/NetworkingSystem.js +733 -793
  108. package/dist/cjs/native-engine/PhysicsEngine.js +583 -607
  109. package/dist/cjs/native-engine/RenderingEngine.js +302 -350
  110. package/dist/cjs/native-engine/SceneGraph.js +769 -808
  111. package/dist/cjs/network/MultiplayerTransport.js +97 -132
  112. package/dist/cjs/network/Networking.js +549 -564
  113. package/dist/cjs/node_modules/@microsoft/signalr/dist/esm/AbortController.js +21 -29
  114. package/dist/cjs/node_modules/@microsoft/signalr/dist/esm/AccessTokenHttpClient.js +40 -48
  115. package/dist/cjs/node_modules/@microsoft/signalr/dist/esm/DefaultHttpClient.js +35 -43
  116. package/dist/cjs/node_modules/@microsoft/signalr/dist/esm/DefaultReconnectPolicy.js +10 -15
  117. package/dist/cjs/node_modules/@microsoft/signalr/dist/esm/Errors.js +103 -135
  118. package/dist/cjs/node_modules/@microsoft/signalr/dist/esm/FetchHttpClient.js +131 -158
  119. package/dist/cjs/node_modules/@microsoft/signalr/dist/esm/HandshakeProtocol.js +39 -53
  120. package/dist/cjs/node_modules/@microsoft/signalr/dist/esm/HeaderNames.js +5 -8
  121. package/dist/cjs/node_modules/@microsoft/signalr/dist/esm/HttpClient.js +40 -48
  122. package/dist/cjs/node_modules/@microsoft/signalr/dist/esm/HttpConnection.js +494 -572
  123. package/dist/cjs/node_modules/@microsoft/signalr/dist/esm/HubConnection.js +826 -953
  124. package/dist/cjs/node_modules/@microsoft/signalr/dist/esm/HubConnectionBuilder.js +125 -146
  125. package/dist/cjs/node_modules/@microsoft/signalr/dist/esm/IHubProtocol.js +13 -23
  126. package/dist/cjs/node_modules/@microsoft/signalr/dist/esm/ILogger.js +11 -25
  127. package/dist/cjs/node_modules/@microsoft/signalr/dist/esm/ITransport.js +13 -24
  128. package/dist/cjs/node_modules/@microsoft/signalr/dist/esm/JsonHubProtocol.js +108 -121
  129. package/dist/cjs/node_modules/@microsoft/signalr/dist/esm/Loggers.js +10 -14
  130. package/dist/cjs/node_modules/@microsoft/signalr/dist/esm/LongPollingTransport.js +150 -185
  131. package/dist/cjs/node_modules/@microsoft/signalr/dist/esm/MessageBuffer.js +158 -196
  132. package/dist/cjs/node_modules/@microsoft/signalr/dist/esm/ServerSentEventsTransport.js +90 -105
  133. package/dist/cjs/node_modules/@microsoft/signalr/dist/esm/Subject.js +30 -35
  134. package/dist/cjs/node_modules/@microsoft/signalr/dist/esm/TextMessageFormat.js +16 -21
  135. package/dist/cjs/node_modules/@microsoft/signalr/dist/esm/Utils.js +215 -248
  136. package/dist/cjs/node_modules/@microsoft/signalr/dist/esm/WebSocketTransport.js +135 -157
  137. package/dist/cjs/node_modules/@microsoft/signalr/dist/esm/XhrHttpClient.js +76 -85
  138. package/dist/cjs/node_modules/@microsoft/signalr/dist/esm/index.js +24 -27
  139. package/dist/cjs/node_modules/@microsoft/signalr/dist/esm/pkg-version.js +3 -4
  140. package/dist/cjs/pathfinding/Pathfinding.js +245 -263
  141. package/dist/cjs/performance/AssetStreaming.js +561 -635
  142. package/dist/cjs/performance/BenchmarkSuite.js +283 -311
  143. package/dist/cjs/performance/DeltaCompression.js +467 -526
  144. package/dist/cjs/performance/DeterministicPhysics.js +450 -498
  145. package/dist/cjs/performance/ECSOptimization.js +644 -695
  146. package/dist/cjs/performance/GameSaveSystem.js +509 -556
  147. package/dist/cjs/performance/HotReload.js +447 -500
  148. package/dist/cjs/performance/InputRecording.js +511 -561
  149. package/dist/cjs/performance/InterestManagement.js +453 -500
  150. package/dist/cjs/performance/LocalizationRuntime.js +376 -423
  151. package/dist/cjs/performance/MemoryManagement.js +410 -470
  152. package/dist/cjs/performance/ModSupport.js +445 -496
  153. package/dist/cjs/performance/MultiThreadedPhysics.js +400 -443
  154. package/dist/cjs/performance/NetworkPrediction.js +424 -482
  155. package/dist/cjs/performance/ObjectPooling.js +569 -617
  156. package/dist/cjs/performance/Performance.js +174 -190
  157. package/dist/cjs/performance/PerformanceAdvanced.js +614 -637
  158. package/dist/cjs/performance/WebGPURenderer.js +656 -683
  159. package/dist/cjs/physics/PhysicsAdvanced.js +345 -379
  160. package/dist/cjs/physics/PhysicsEngine2D.js +391 -427
  161. package/dist/cjs/plugins/PluginSDK.js +432 -466
  162. package/dist/cjs/plugins/PluginTestKit.js +307 -330
  163. package/dist/cjs/procedural/Procedural.js +243 -254
  164. package/dist/cjs/procedural/WorldBuilding.js +297 -314
  165. package/dist/cjs/quest/QuestSystem.js +975 -932
  166. package/dist/cjs/release/MobileExport.js +157 -163
  167. package/dist/cjs/release/Release.js +225 -238
  168. package/dist/cjs/rendering/Camera2D.js +121 -124
  169. package/dist/cjs/rendering/Renderer2D.js +321 -335
  170. package/dist/cjs/rendering/WebGPURenderPipeline.js +632 -651
  171. package/dist/cjs/runtime3d/Runtime3D.js +359 -378
  172. package/dist/cjs/runtime3d/SceneEditor3D.js +252 -263
  173. package/dist/cjs/scene/SceneManager.js +177 -193
  174. package/dist/cjs/scripting/GraphToAST.js +574 -547
  175. package/dist/cjs/scripting/LanguageExporter.js +254 -279
  176. package/dist/cjs/scripting/NodeGraph.js +546 -570
  177. package/dist/cjs/scripting/ScriptAST.js +31 -39
  178. package/dist/cjs/scripting/VisualScripting2.js +1016 -1065
  179. package/dist/cjs/scripting/exporters/CSharpExporter.js +464 -496
  180. package/dist/cjs/scripting/exporters/GDScriptExporter.js +390 -442
  181. package/dist/cjs/scripting/exporters/LuaExporter.js +406 -447
  182. package/dist/cjs/scripting/exporters/PythonExporter.js +484 -549
  183. package/dist/cjs/scripting/exporters/RustExporter.js +459 -512
  184. package/dist/cjs/scripting/exporters/TypeScriptExporter.js +505 -556
  185. package/dist/cjs/social/Social.js +72 -80
  186. package/dist/cjs/systems/LightingSystem.js +844 -931
  187. package/dist/cjs/systems/ParticleSystem2.js +1447 -1451
  188. package/dist/cjs/systems/PhysicsDebugSystem.js +1070 -1141
  189. package/dist/cjs/systems/TerrainSystem.js +985 -1098
  190. package/dist/cjs/templates/ActionTemplates.js +296 -309
  191. package/dist/cjs/templates/FightingTemplate.js +417 -463
  192. package/dist/cjs/templates/MetroidvaniaTemplate.js +353 -405
  193. package/dist/cjs/templates/PartyTemplates.js +190 -202
  194. package/dist/cjs/templates/PuzzleTemplates.js +245 -261
  195. package/dist/cjs/templates/RPGTemplates.js +160 -178
  196. package/dist/cjs/templates/SportsTemplates.js +237 -250
  197. package/dist/cjs/templates/StrategyTemplates.js +143 -153
  198. package/dist/cjs/templates/SurvivalTemplate.js +336 -380
  199. package/dist/cjs/templates/WaveDefense.js +499 -526
  200. package/dist/cjs/tilemap/Tilemap.js +207 -214
  201. package/dist/cjs/tutorials/TutorialBranching.js +396 -417
  202. package/dist/cjs/tutorials/TutorialEngine.js +888 -931
  203. package/dist/cjs/tutorials/TutorialOverlay.js +602 -640
  204. package/dist/cjs/tutorials/content/Tutorials_T05_T08.js +787 -809
  205. package/dist/cjs/tutorials/content/Tutorials_T09_T11.js +736 -756
  206. package/dist/cjs/tutorials/content/Tutorials_T12_T15.js +754 -778
  207. package/dist/cjs/ui/GameUI.js +295 -312
  208. package/dist/cjs/unity-integration/UnityAssetStore.js +700 -741
  209. package/dist/cjs/unity-integration/UnityImport.js +1066 -1176
  210. package/dist/cjs/unity-integration/UnityLiveLink.js +934 -981
  211. package/dist/cjs/unity-integration/components/UnityAssetStoreBrowser.js +1233 -757
  212. package/dist/cjs/unity-integration/components/UnityProjectBrowser.js +1240 -768
  213. package/dist/cjs/unreal-integration/UnrealImport.js +391 -412
  214. package/dist/cjs/unreal-integration/UnrealLiveLink.js +1102 -1137
  215. package/dist/cjs/unreal-integration/UnrealMarketplace.js +436 -473
  216. package/dist/cjs/unreal-integration/components/UnrealProjectBrowser.js +1150 -767
  217. package/dist/cjs/xr/ARVR.js +167 -175
  218. package/dist/esm/accessibility/Accessibility.js +160 -164
  219. package/dist/esm/ai/AI.js +310 -293
  220. package/dist/esm/ai/BehaviorTree.js +1057 -1112
  221. package/dist/esm/ai/StateMachine.js +709 -755
  222. package/dist/esm/animation/Animation.js +237 -240
  223. package/dist/esm/audio/AudioAdvanced.js +239 -235
  224. package/dist/esm/audio/AudioBridge.js +448 -434
  225. package/dist/esm/audio/AudioManager.js +243 -258
  226. package/dist/esm/build-cloud/BuildOrchestration.js +1071 -1097
  227. package/dist/esm/build-cloud/GodotBuildCloud.js +647 -669
  228. package/dist/esm/build-cloud/UnityBuildCloud.js +571 -609
  229. package/dist/esm/build-cloud/UnrealBuildCloud.js +615 -634
  230. package/dist/esm/core/EventBus.js +52 -55
  231. package/dist/esm/core/GameClock.js +63 -65
  232. package/dist/esm/core/GameConfig.js +77 -78
  233. package/dist/esm/core/GameLoop.js +81 -88
  234. package/dist/esm/core/ServiceLocator.js +63 -64
  235. package/dist/esm/core/Validation.js +82 -81
  236. package/dist/esm/core/math.js +109 -111
  237. package/dist/esm/devtools/DevTools.js +546 -562
  238. package/dist/esm/devtools/DeveloperExperience.js +535 -558
  239. package/dist/esm/devtools/GameplayAnalytics.js +647 -645
  240. package/dist/esm/dialogue/DialogueSystem.js +998 -985
  241. package/dist/esm/docs/DocGenerator.js +342 -363
  242. package/dist/esm/ecs/World.js +358 -370
  243. package/dist/esm/editor/AchievementEditor.js +584 -635
  244. package/dist/esm/editor/AdvancedEditor.js +191 -190
  245. package/dist/esm/editor/AnimationRetargeting.js +700 -740
  246. package/dist/esm/editor/AssetBrowser.js +1078 -1159
  247. package/dist/esm/editor/AssetManager.js +188 -191
  248. package/dist/esm/editor/ConsolePro.js +977 -1060
  249. package/dist/esm/editor/CraftingEditor.js +684 -729
  250. package/dist/esm/editor/DebugTools.js +194 -184
  251. package/dist/esm/editor/DialogueSystem.js +849 -900
  252. package/dist/esm/editor/LeaderboardSystem.js +433 -482
  253. package/dist/esm/editor/NiceGameEditor.js +1908 -1200
  254. package/dist/esm/editor/PrefabSystem.js +777 -837
  255. package/dist/esm/editor/ProfilerPanel.js +654 -699
  256. package/dist/esm/editor/QuestEditor.js +795 -838
  257. package/dist/esm/editor/ReplaySystem.js +591 -633
  258. package/dist/esm/editor/SceneEditor.js +189 -199
  259. package/dist/esm/editor/ScreenshotMode.js +628 -687
  260. package/dist/esm/editor/ShaderGraph.js +1565 -1581
  261. package/dist/esm/editor/TimelineEditor.js +733 -777
  262. package/dist/esm/editor/UndoRedoPro.js +835 -903
  263. package/dist/esm/editor/VRARMode.js +586 -644
  264. package/dist/esm/engine/NiceGameEngine.js +142 -163
  265. package/dist/esm/engine/SaveSystem.js +447 -494
  266. package/dist/esm/enterprise/Enterprise.js +265 -260
  267. package/dist/esm/export/BevyExporter.js +929 -966
  268. package/dist/esm/export/CocosExporter.js +646 -672
  269. package/dist/esm/export/Construct3Exporter.js +785 -793
  270. package/dist/esm/export/DefoldExporter.js +1030 -1068
  271. package/dist/esm/export/GDevelopExporter.js +676 -707
  272. package/dist/esm/export/GameMakerExporter.js +793 -804
  273. package/dist/esm/export/GodotExporter.js +1004 -1044
  274. package/dist/esm/export/MinecraftBedrockExporter.js +575 -577
  275. package/dist/esm/export/MonoGameExporter.js +1267 -1292
  276. package/dist/esm/export/Pico8Exporter.js +768 -811
  277. package/dist/esm/export/PlatformExporter.js +193 -205
  278. package/dist/esm/export/RPGMakerExporter.js +862 -878
  279. package/dist/esm/export/RobloxExporter.js +885 -918
  280. package/dist/esm/export/Solar2DExporter.js +1220 -1255
  281. package/dist/esm/export/ThreeJSExporter.js +1007 -1072
  282. package/dist/esm/export/Tic80Exporter.js +1212 -1266
  283. package/dist/esm/export/UnityExporter.js +1117 -1130
  284. package/dist/esm/export/UnrealExporter.js +891 -918
  285. package/dist/esm/export/WebExporter.js +933 -969
  286. package/dist/esm/export/index.js +86 -71
  287. package/dist/esm/godot-integration/GodotAssetLibrary.js +663 -691
  288. package/dist/esm/godot-integration/GodotImport.js +935 -1026
  289. package/dist/esm/godot-integration/GodotLiveLink.js +918 -944
  290. package/dist/esm/godot-integration/components/GodotProjectBrowser.js +912 -610
  291. package/dist/esm/i18n/I18n.js +871 -899
  292. package/dist/esm/i18n/useTranslation.js +88 -88
  293. package/dist/esm/import/AsepriteImporter.js +637 -715
  294. package/dist/esm/import/DragonBonesImporter.js +417 -460
  295. package/dist/esm/import/GameMakerImporter.js +491 -520
  296. package/dist/esm/import/GodotSceneImporter.js +767 -821
  297. package/dist/esm/import/LDtkImporter.js +415 -453
  298. package/dist/esm/import/Live2DImporter.js +477 -517
  299. package/dist/esm/import/NdgFormat.js +410 -456
  300. package/dist/esm/import/OgmoImporter.js +446 -492
  301. package/dist/esm/import/RPGMakerImporter.js +431 -476
  302. package/dist/esm/import/SceneImporter.js +385 -422
  303. package/dist/esm/import/SpineImporter.js +491 -545
  304. package/dist/esm/import/SpriterImporter.js +570 -612
  305. package/dist/esm/import/TiledMapImporter.js +790 -845
  306. package/dist/esm/import/UnitySceneImporter.js +666 -702
  307. package/dist/esm/import/index.js +247 -258
  308. package/dist/esm/index.js +1332 -188
  309. package/dist/esm/input/GamepadNavigation.js +187 -204
  310. package/dist/esm/input/InputManager.js +401 -420
  311. package/dist/esm/input/useGamepads.js +72 -80
  312. package/dist/esm/integration/IconSprite.js +143 -255
  313. package/dist/esm/inventory/InventorySystem.js +925 -994
  314. package/dist/esm/kids/KidMode.js +1003 -1021
  315. package/dist/esm/kids/KidTools.js +573 -593
  316. package/dist/esm/monetization/Monetization.js +180 -176
  317. package/dist/esm/multiplayer/LocalMultiplayer.js +290 -286
  318. package/dist/esm/multiplayer/MiniGameTypes.js +58 -58
  319. package/dist/esm/native-engine/AnimationSystem.js +735 -805
  320. package/dist/esm/native-engine/AssetPipeline.js +456 -514
  321. package/dist/esm/native-engine/AudioEngine.js +765 -803
  322. package/dist/esm/native-engine/InputSystem.js +1079 -1144
  323. package/dist/esm/native-engine/NetworkingSystem.js +740 -792
  324. package/dist/esm/native-engine/PhysicsEngine.js +585 -606
  325. package/dist/esm/native-engine/RenderingEngine.js +304 -349
  326. package/dist/esm/native-engine/SceneGraph.js +773 -807
  327. package/dist/esm/network/MultiplayerTransport.js +99 -131
  328. package/dist/esm/network/Networking.js +556 -563
  329. package/dist/esm/node_modules/@microsoft/signalr/dist/esm/AbortController.js +22 -28
  330. package/dist/esm/node_modules/@microsoft/signalr/dist/esm/AccessTokenHttpClient.js +41 -47
  331. package/dist/esm/node_modules/@microsoft/signalr/dist/esm/DefaultHttpClient.js +36 -42
  332. package/dist/esm/node_modules/@microsoft/signalr/dist/esm/DefaultReconnectPolicy.js +11 -14
  333. package/dist/esm/node_modules/@microsoft/signalr/dist/esm/Errors.js +111 -134
  334. package/dist/esm/node_modules/@microsoft/signalr/dist/esm/FetchHttpClient.js +132 -157
  335. package/dist/esm/node_modules/@microsoft/signalr/dist/esm/HandshakeProtocol.js +40 -52
  336. package/dist/esm/node_modules/@microsoft/signalr/dist/esm/HeaderNames.js +6 -7
  337. package/dist/esm/node_modules/@microsoft/signalr/dist/esm/HttpClient.js +42 -47
  338. package/dist/esm/node_modules/@microsoft/signalr/dist/esm/HttpConnection.js +496 -571
  339. package/dist/esm/node_modules/@microsoft/signalr/dist/esm/HubConnection.js +828 -952
  340. package/dist/esm/node_modules/@microsoft/signalr/dist/esm/HubConnectionBuilder.js +126 -145
  341. package/dist/esm/node_modules/@microsoft/signalr/dist/esm/IHubProtocol.js +14 -23
  342. package/dist/esm/node_modules/@microsoft/signalr/dist/esm/ILogger.js +12 -25
  343. package/dist/esm/node_modules/@microsoft/signalr/dist/esm/ITransport.js +15 -24
  344. package/dist/esm/node_modules/@microsoft/signalr/dist/esm/JsonHubProtocol.js +109 -120
  345. package/dist/esm/node_modules/@microsoft/signalr/dist/esm/Loggers.js +11 -13
  346. package/dist/esm/node_modules/@microsoft/signalr/dist/esm/LongPollingTransport.js +151 -184
  347. package/dist/esm/node_modules/@microsoft/signalr/dist/esm/MessageBuffer.js +159 -195
  348. package/dist/esm/node_modules/@microsoft/signalr/dist/esm/ServerSentEventsTransport.js +91 -104
  349. package/dist/esm/node_modules/@microsoft/signalr/dist/esm/Subject.js +31 -34
  350. package/dist/esm/node_modules/@microsoft/signalr/dist/esm/TextMessageFormat.js +17 -20
  351. package/dist/esm/node_modules/@microsoft/signalr/dist/esm/Utils.js +230 -248
  352. package/dist/esm/node_modules/@microsoft/signalr/dist/esm/WebSocketTransport.js +136 -156
  353. package/dist/esm/node_modules/@microsoft/signalr/dist/esm/XhrHttpClient.js +77 -84
  354. package/dist/esm/node_modules/@microsoft/signalr/dist/esm/index.js +31 -12
  355. package/dist/esm/node_modules/@microsoft/signalr/dist/esm/pkg-version.js +4 -3
  356. package/dist/esm/pathfinding/Pathfinding.js +249 -262
  357. package/dist/esm/performance/AssetStreaming.js +567 -634
  358. package/dist/esm/performance/BenchmarkSuite.js +285 -310
  359. package/dist/esm/performance/DeltaCompression.js +478 -525
  360. package/dist/esm/performance/DeterministicPhysics.js +479 -497
  361. package/dist/esm/performance/ECSOptimization.js +653 -694
  362. package/dist/esm/performance/GameSaveSystem.js +515 -555
  363. package/dist/esm/performance/HotReload.js +454 -499
  364. package/dist/esm/performance/InputRecording.js +520 -560
  365. package/dist/esm/performance/InterestManagement.js +460 -499
  366. package/dist/esm/performance/LocalizationRuntime.js +382 -422
  367. package/dist/esm/performance/MemoryManagement.js +415 -469
  368. package/dist/esm/performance/ModSupport.js +455 -495
  369. package/dist/esm/performance/MultiThreadedPhysics.js +408 -442
  370. package/dist/esm/performance/NetworkPrediction.js +436 -481
  371. package/dist/esm/performance/ObjectPooling.js +582 -616
  372. package/dist/esm/performance/Performance.js +182 -189
  373. package/dist/esm/performance/PerformanceAdvanced.js +621 -636
  374. package/dist/esm/performance/WebGPURenderer.js +660 -682
  375. package/dist/esm/physics/PhysicsAdvanced.js +359 -378
  376. package/dist/esm/physics/PhysicsEngine2D.js +393 -426
  377. package/dist/esm/plugins/PluginSDK.js +435 -465
  378. package/dist/esm/plugins/PluginTestKit.js +315 -329
  379. package/dist/esm/procedural/Procedural.js +248 -253
  380. package/dist/esm/procedural/WorldBuilding.js +316 -313
  381. package/dist/esm/quest/QuestSystem.js +982 -931
  382. package/dist/esm/release/MobileExport.js +163 -162
  383. package/dist/esm/release/Release.js +234 -237
  384. package/dist/esm/rendering/Camera2D.js +122 -123
  385. package/dist/esm/rendering/Renderer2D.js +323 -334
  386. package/dist/esm/rendering/WebGPURenderPipeline.js +647 -650
  387. package/dist/esm/runtime3d/Runtime3D.js +368 -377
  388. package/dist/esm/runtime3d/SceneEditor3D.js +268 -262
  389. package/dist/esm/scene/SceneManager.js +178 -192
  390. package/dist/esm/scripting/GraphToAST.js +576 -546
  391. package/dist/esm/scripting/LanguageExporter.js +263 -278
  392. package/dist/esm/scripting/NodeGraph.js +550 -569
  393. package/dist/esm/scripting/ScriptAST.js +45 -38
  394. package/dist/esm/scripting/VisualScripting2.js +1025 -1064
  395. package/dist/esm/scripting/exporters/CSharpExporter.js +465 -495
  396. package/dist/esm/scripting/exporters/GDScriptExporter.js +391 -441
  397. package/dist/esm/scripting/exporters/LuaExporter.js +407 -446
  398. package/dist/esm/scripting/exporters/PythonExporter.js +485 -548
  399. package/dist/esm/scripting/exporters/RustExporter.js +460 -511
  400. package/dist/esm/scripting/exporters/TypeScriptExporter.js +506 -555
  401. package/dist/esm/social/Social.js +77 -79
  402. package/dist/esm/systems/LightingSystem.js +855 -930
  403. package/dist/esm/systems/ParticleSystem2.js +1453 -1450
  404. package/dist/esm/systems/PhysicsDebugSystem.js +1078 -1140
  405. package/dist/esm/systems/TerrainSystem.js +996 -1097
  406. package/dist/esm/templates/ActionTemplates.js +307 -308
  407. package/dist/esm/templates/FightingTemplate.js +439 -462
  408. package/dist/esm/templates/MetroidvaniaTemplate.js +391 -404
  409. package/dist/esm/templates/PartyTemplates.js +206 -201
  410. package/dist/esm/templates/PuzzleTemplates.js +260 -260
  411. package/dist/esm/templates/RPGTemplates.js +172 -177
  412. package/dist/esm/templates/SportsTemplates.js +251 -249
  413. package/dist/esm/templates/StrategyTemplates.js +158 -152
  414. package/dist/esm/templates/SurvivalTemplate.js +367 -379
  415. package/dist/esm/templates/WaveDefense.js +501 -525
  416. package/dist/esm/tilemap/Tilemap.js +209 -213
  417. package/dist/esm/tutorials/TutorialBranching.js +399 -416
  418. package/dist/esm/tutorials/TutorialEngine.js +893 -930
  419. package/dist/esm/tutorials/TutorialOverlay.js +603 -639
  420. package/dist/esm/tutorials/content/Tutorials_T05_T08.js +792 -808
  421. package/dist/esm/tutorials/content/Tutorials_T09_T11.js +740 -755
  422. package/dist/esm/tutorials/content/Tutorials_T12_T15.js +759 -777
  423. package/dist/esm/ui/GameUI.js +296 -311
  424. package/dist/esm/unity-integration/UnityAssetStore.js +704 -740
  425. package/dist/esm/unity-integration/UnityImport.js +1071 -1175
  426. package/dist/esm/unity-integration/UnityLiveLink.js +938 -980
  427. package/dist/esm/unity-integration/components/UnityAssetStoreBrowser.js +1234 -756
  428. package/dist/esm/unity-integration/components/UnityProjectBrowser.js +1241 -767
  429. package/dist/esm/unreal-integration/UnrealImport.js +395 -411
  430. package/dist/esm/unreal-integration/UnrealLiveLink.js +1105 -1136
  431. package/dist/esm/unreal-integration/UnrealMarketplace.js +442 -472
  432. package/dist/esm/unreal-integration/components/UnrealProjectBrowser.js +1151 -766
  433. package/dist/esm/xr/ARVR.js +181 -174
  434. package/package.json +7 -12
@@ -1,972 +1,929 @@
1
- 'use strict';
2
-
3
- var EventBus = require('../core/EventBus.js');
4
-
5
- /* ────────────────────────────────────────────────────────────────
6
- TutorialEngine — LEGENDARY interactive tutorial system
7
-
8
- Features:
9
- • Step-by-step tutorials with progress tracking
10
- • Multiple step types: text, code, highlight, interactive, quiz
11
- • Multi-language support via I18nManager
12
- • Overlay UI with arrows, spotlights, tooltips
13
- • Progress persistence (localStorage)
14
- • Tutorial packs as JSON
15
- • Code sandboxes with live validation
16
- • Kid-friendly mode with simplified language
17
- ──────────────────────────────────────────────────────────────── */
18
- /* ── TutorialEngine ───────────────────────────────────────────── */
1
+ "use strict";
2
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
+ const EventBus = require("../core/EventBus.js");
19
4
  class TutorialEngine {
20
- constructor(i18n, persistProgress = true) {
21
- this.i18n = i18n;
22
- this.persistProgress = persistProgress;
23
- this.tutorials = new Map();
24
- this.progress = new Map();
25
- this.validators = new Map();
26
- this.activeTutorialId = null;
27
- this.storagePrefix = 'nice2dev_tutorial_';
28
- this.events = new EventBus.EventBus();
29
- if (this.persistProgress) {
30
- this.loadAllProgress();
31
- }
32
- }
33
- /* ── Tutorial Registration ──────────────────────────────────── */
34
- /** Register a tutorial definition */
35
- registerTutorial(definition) {
36
- this.tutorials.set(definition.id, definition);
37
- }
38
- /** Register multiple tutorials at once */
39
- registerTutorials(definitions) {
40
- for (const def of definitions) {
41
- this.registerTutorial(def);
42
- }
43
- }
44
- /** Register a custom code validator */
45
- registerValidator(name, fn) {
46
- this.validators.set(name, fn);
47
- }
48
- /** Get all registered tutorials */
49
- getAllTutorials() {
50
- return Array.from(this.tutorials.values());
51
- }
52
- /** Get tutorials filtered by category */
53
- getTutorialsByCategory(category) {
54
- return this.getAllTutorials().filter((t) => t.category === category);
55
- }
56
- /** Get tutorials filtered by difficulty */
57
- getTutorialsByDifficulty(difficulty) {
58
- return this.getAllTutorials().filter((t) => t.difficulty === difficulty);
59
- }
60
- /** Get tutorials suitable for kids */
61
- getKidFriendlyTutorials() {
62
- return this.getAllTutorials().filter((t) => t.kidFriendly === true);
63
- }
64
- /** Search tutorials by tag or title */
65
- searchTutorials(query) {
66
- const lowerQuery = query.toLowerCase();
67
- const locale = this.i18n.getLocale();
68
- return this.getAllTutorials().filter((t) => {
69
- const title = (t.title[locale] ?? t.title['en'] ?? '').toLowerCase();
70
- const desc = (t.description[locale] ?? t.description['en'] ?? '').toLowerCase();
71
- const tagMatch = t.tags?.some((tag) => tag.toLowerCase().includes(lowerQuery));
72
- return title.includes(lowerQuery) || desc.includes(lowerQuery) || tagMatch;
73
- });
74
- }
75
- /* ── Tutorial Execution ─────────────────────────────────────── */
76
- /** Start a tutorial */
77
- startTutorial(tutorialId) {
78
- const tutorial = this.tutorials.get(tutorialId);
79
- if (!tutorial || tutorial.steps.length === 0) {
80
- return null;
81
- }
82
- // Check prerequisites
83
- if (tutorial.prerequisites) {
84
- for (const preReqId of tutorial.prerequisites) {
85
- const preReqProgress = this.progress.get(preReqId);
86
- if (!preReqProgress?.completed) {
87
- return null; // Prerequisite not met
88
- }
89
- }
90
- }
91
- // Get or create progress
92
- let prog = this.progress.get(tutorialId);
93
- if (!prog || prog.completed) {
94
- prog = {
95
- tutorialId,
96
- currentStepIndex: 0,
97
- completedSteps: [],
98
- startedAt: Date.now(),
99
- lastAccessedAt: Date.now(),
100
- completed: false,
101
- quizScores: {},
102
- codeSubmissions: {},
103
- };
104
- this.progress.set(tutorialId, prog);
105
- }
106
- this.activeTutorialId = tutorialId;
107
- this.events.emit('tutorial:started', { tutorialId });
108
- const step = tutorial.steps[prog.currentStepIndex];
109
- this.events.emit('tutorial:step:entered', {
110
- tutorialId,
111
- stepId: step.id,
112
- stepIndex: prog.currentStepIndex,
113
- });
114
- this.saveProgress(tutorialId);
115
- return step;
116
- }
117
- /** Get the current step of the active tutorial */
118
- getCurrentStep() {
119
- if (!this.activeTutorialId) {
120
- return null;
121
- }
122
- const tutorial = this.tutorials.get(this.activeTutorialId);
123
- const prog = this.progress.get(this.activeTutorialId);
124
- if (!tutorial || !prog) {
125
- return null;
126
- }
127
- return tutorial.steps[prog.currentStepIndex] ?? null;
128
- }
129
- /** Advance to the next step */
130
- nextStep() {
131
- if (!this.activeTutorialId) {
132
- return null;
133
- }
134
- const tutorial = this.tutorials.get(this.activeTutorialId);
135
- const prog = this.progress.get(this.activeTutorialId);
136
- if (!tutorial || !prog) {
137
- return null;
138
- }
139
- // Mark current step completed
140
- const currentStep = tutorial.steps[prog.currentStepIndex];
141
- if (currentStep && !prog.completedSteps.includes(currentStep.id)) {
142
- prog.completedSteps.push(currentStep.id);
143
- this.events.emit('tutorial:step:completed', {
144
- tutorialId: this.activeTutorialId,
145
- stepId: currentStep.id,
146
- });
147
- }
148
- // Advance
149
- prog.currentStepIndex++;
150
- prog.lastAccessedAt = Date.now();
151
- // Check if tutorial is completed
152
- if (prog.currentStepIndex >= tutorial.steps.length) {
153
- prog.completed = true;
154
- prog.completedAt = Date.now();
155
- this.events.emit('tutorial:completed', {
156
- tutorialId: this.activeTutorialId,
157
- durationMs: prog.completedAt - prog.startedAt,
158
- });
159
- this.saveProgress(this.activeTutorialId);
160
- this.activeTutorialId = null;
161
- return null;
162
- }
163
- const nextStep = tutorial.steps[prog.currentStepIndex];
164
- this.events.emit('tutorial:step:entered', {
165
- tutorialId: this.activeTutorialId,
166
- stepId: nextStep.id,
167
- stepIndex: prog.currentStepIndex,
168
- });
169
- this.saveProgress(this.activeTutorialId);
170
- return nextStep;
171
- }
172
- /** Go to a previous step */
173
- previousStep() {
174
- if (!this.activeTutorialId) {
175
- return null;
176
- }
177
- const tutorial = this.tutorials.get(this.activeTutorialId);
178
- const prog = this.progress.get(this.activeTutorialId);
179
- if (!tutorial || !prog || prog.currentStepIndex <= 0) {
180
- return null;
181
- }
182
- prog.currentStepIndex--;
183
- prog.lastAccessedAt = Date.now();
184
- const step = tutorial.steps[prog.currentStepIndex];
185
- this.events.emit('tutorial:step:entered', {
186
- tutorialId: this.activeTutorialId,
187
- stepId: step.id,
188
- stepIndex: prog.currentStepIndex,
189
- });
190
- this.saveProgress(this.activeTutorialId);
191
- return step;
192
- }
193
- /** Jump to a specific step by index */
194
- goToStep(index) {
195
- if (!this.activeTutorialId) {
196
- return null;
197
- }
198
- const tutorial = this.tutorials.get(this.activeTutorialId);
199
- const prog = this.progress.get(this.activeTutorialId);
200
- if (!tutorial || !prog) {
201
- return null;
202
- }
203
- if (index < 0 || index >= tutorial.steps.length) {
204
- return null;
205
- }
206
- prog.currentStepIndex = index;
207
- prog.lastAccessedAt = Date.now();
208
- const step = tutorial.steps[index];
209
- this.events.emit('tutorial:step:entered', {
210
- tutorialId: this.activeTutorialId,
211
- stepId: step.id,
212
- stepIndex: index,
5
+ constructor(i18n, persistProgress = true) {
6
+ this.i18n = i18n;
7
+ this.persistProgress = persistProgress;
8
+ this.tutorials = /* @__PURE__ */ new Map();
9
+ this.progress = /* @__PURE__ */ new Map();
10
+ this.validators = /* @__PURE__ */ new Map();
11
+ this.activeTutorialId = null;
12
+ this.storagePrefix = "nice2dev_tutorial_";
13
+ this.events = new EventBus.EventBus();
14
+ if (this.persistProgress) {
15
+ this.loadAllProgress();
16
+ }
17
+ }
18
+ /* ── Tutorial Registration ──────────────────────────────────── */
19
+ /** Register a tutorial definition */
20
+ registerTutorial(definition) {
21
+ this.tutorials.set(definition.id, definition);
22
+ }
23
+ /** Register multiple tutorials at once */
24
+ registerTutorials(definitions) {
25
+ for (const def of definitions) {
26
+ this.registerTutorial(def);
27
+ }
28
+ }
29
+ /** Register a custom code validator */
30
+ registerValidator(name, fn) {
31
+ this.validators.set(name, fn);
32
+ }
33
+ /** Get all registered tutorials */
34
+ getAllTutorials() {
35
+ return Array.from(this.tutorials.values());
36
+ }
37
+ /** Get tutorials filtered by category */
38
+ getTutorialsByCategory(category) {
39
+ return this.getAllTutorials().filter((t) => t.category === category);
40
+ }
41
+ /** Get tutorials filtered by difficulty */
42
+ getTutorialsByDifficulty(difficulty) {
43
+ return this.getAllTutorials().filter((t) => t.difficulty === difficulty);
44
+ }
45
+ /** Get tutorials suitable for kids */
46
+ getKidFriendlyTutorials() {
47
+ return this.getAllTutorials().filter((t) => t.kidFriendly === true);
48
+ }
49
+ /** Search tutorials by tag or title */
50
+ searchTutorials(query) {
51
+ const lowerQuery = query.toLowerCase();
52
+ const locale = this.i18n.getLocale();
53
+ return this.getAllTutorials().filter((t) => {
54
+ const title = (t.title[locale] ?? t.title["en"] ?? "").toLowerCase();
55
+ const desc = (t.description[locale] ?? t.description["en"] ?? "").toLowerCase();
56
+ const tagMatch = t.tags?.some((tag) => tag.toLowerCase().includes(lowerQuery));
57
+ return title.includes(lowerQuery) || desc.includes(lowerQuery) || tagMatch;
58
+ });
59
+ }
60
+ /* ── Tutorial Execution ─────────────────────────────────────── */
61
+ /** Start a tutorial */
62
+ startTutorial(tutorialId) {
63
+ const tutorial = this.tutorials.get(tutorialId);
64
+ if (!tutorial || tutorial.steps.length === 0) {
65
+ return null;
66
+ }
67
+ if (tutorial.prerequisites) {
68
+ for (const preReqId of tutorial.prerequisites) {
69
+ const preReqProgress = this.progress.get(preReqId);
70
+ if (!preReqProgress?.completed) {
71
+ return null;
72
+ }
73
+ }
74
+ }
75
+ let prog = this.progress.get(tutorialId);
76
+ if (!prog || prog.completed) {
77
+ prog = {
78
+ tutorialId,
79
+ currentStepIndex: 0,
80
+ completedSteps: [],
81
+ startedAt: Date.now(),
82
+ lastAccessedAt: Date.now(),
83
+ completed: false,
84
+ quizScores: {},
85
+ codeSubmissions: {}
86
+ };
87
+ this.progress.set(tutorialId, prog);
88
+ }
89
+ this.activeTutorialId = tutorialId;
90
+ this.events.emit("tutorial:started", { tutorialId });
91
+ const step = tutorial.steps[prog.currentStepIndex];
92
+ this.events.emit("tutorial:step:entered", {
93
+ tutorialId,
94
+ stepId: step.id,
95
+ stepIndex: prog.currentStepIndex
96
+ });
97
+ this.saveProgress(tutorialId);
98
+ return step;
99
+ }
100
+ /** Get the current step of the active tutorial */
101
+ getCurrentStep() {
102
+ if (!this.activeTutorialId) {
103
+ return null;
104
+ }
105
+ const tutorial = this.tutorials.get(this.activeTutorialId);
106
+ const prog = this.progress.get(this.activeTutorialId);
107
+ if (!tutorial || !prog) {
108
+ return null;
109
+ }
110
+ return tutorial.steps[prog.currentStepIndex] ?? null;
111
+ }
112
+ /** Advance to the next step */
113
+ nextStep() {
114
+ if (!this.activeTutorialId) {
115
+ return null;
116
+ }
117
+ const tutorial = this.tutorials.get(this.activeTutorialId);
118
+ const prog = this.progress.get(this.activeTutorialId);
119
+ if (!tutorial || !prog) {
120
+ return null;
121
+ }
122
+ const currentStep = tutorial.steps[prog.currentStepIndex];
123
+ if (currentStep && !prog.completedSteps.includes(currentStep.id)) {
124
+ prog.completedSteps.push(currentStep.id);
125
+ this.events.emit("tutorial:step:completed", {
126
+ tutorialId: this.activeTutorialId,
127
+ stepId: currentStep.id
128
+ });
129
+ }
130
+ prog.currentStepIndex++;
131
+ prog.lastAccessedAt = Date.now();
132
+ if (prog.currentStepIndex >= tutorial.steps.length) {
133
+ prog.completed = true;
134
+ prog.completedAt = Date.now();
135
+ this.events.emit("tutorial:completed", {
136
+ tutorialId: this.activeTutorialId,
137
+ durationMs: prog.completedAt - prog.startedAt
138
+ });
139
+ this.saveProgress(this.activeTutorialId);
140
+ this.activeTutorialId = null;
141
+ return null;
142
+ }
143
+ const nextStep = tutorial.steps[prog.currentStepIndex];
144
+ this.events.emit("tutorial:step:entered", {
145
+ tutorialId: this.activeTutorialId,
146
+ stepId: nextStep.id,
147
+ stepIndex: prog.currentStepIndex
148
+ });
149
+ this.saveProgress(this.activeTutorialId);
150
+ return nextStep;
151
+ }
152
+ /** Go to a previous step */
153
+ previousStep() {
154
+ if (!this.activeTutorialId) {
155
+ return null;
156
+ }
157
+ const tutorial = this.tutorials.get(this.activeTutorialId);
158
+ const prog = this.progress.get(this.activeTutorialId);
159
+ if (!tutorial || !prog || prog.currentStepIndex <= 0) {
160
+ return null;
161
+ }
162
+ prog.currentStepIndex--;
163
+ prog.lastAccessedAt = Date.now();
164
+ const step = tutorial.steps[prog.currentStepIndex];
165
+ this.events.emit("tutorial:step:entered", {
166
+ tutorialId: this.activeTutorialId,
167
+ stepId: step.id,
168
+ stepIndex: prog.currentStepIndex
169
+ });
170
+ this.saveProgress(this.activeTutorialId);
171
+ return step;
172
+ }
173
+ /** Jump to a specific step by index */
174
+ goToStep(index) {
175
+ if (!this.activeTutorialId) {
176
+ return null;
177
+ }
178
+ const tutorial = this.tutorials.get(this.activeTutorialId);
179
+ const prog = this.progress.get(this.activeTutorialId);
180
+ if (!tutorial || !prog) {
181
+ return null;
182
+ }
183
+ if (index < 0 || index >= tutorial.steps.length) {
184
+ return null;
185
+ }
186
+ prog.currentStepIndex = index;
187
+ prog.lastAccessedAt = Date.now();
188
+ const step = tutorial.steps[index];
189
+ this.events.emit("tutorial:step:entered", {
190
+ tutorialId: this.activeTutorialId,
191
+ stepId: step.id,
192
+ stepIndex: index
193
+ });
194
+ this.saveProgress(this.activeTutorialId);
195
+ return step;
196
+ }
197
+ /** Stop the current tutorial */
198
+ stopTutorial() {
199
+ if (this.activeTutorialId) {
200
+ this.saveProgress(this.activeTutorialId);
201
+ this.activeTutorialId = null;
202
+ }
203
+ }
204
+ /* ── Step Interaction ───────────────────────────────────────── */
205
+ /** Submit a quiz answer */
206
+ submitQuizAnswer(answerId) {
207
+ if (!this.activeTutorialId) {
208
+ return false;
209
+ }
210
+ const step = this.getCurrentStep();
211
+ const prog = this.progress.get(this.activeTutorialId);
212
+ if (!step || !prog || step.type !== "quiz") {
213
+ return false;
214
+ }
215
+ const correct = answerId === step.quizCorrectId;
216
+ prog.quizScores[step.id] = correct;
217
+ this.events.emit("tutorial:quiz:answered", {
218
+ tutorialId: this.activeTutorialId,
219
+ stepId: step.id,
220
+ correct
221
+ });
222
+ this.saveProgress(this.activeTutorialId);
223
+ return correct;
224
+ }
225
+ /** Submit code for validation */
226
+ validateCode(code) {
227
+ if (!this.activeTutorialId) {
228
+ return { valid: false, message: "No active tutorial" };
229
+ }
230
+ const step = this.getCurrentStep();
231
+ const prog = this.progress.get(this.activeTutorialId);
232
+ if (!step || !prog || step.type !== "code") {
233
+ return { valid: false, message: "Not a code step" };
234
+ }
235
+ prog.codeSubmissions[step.id] = code;
236
+ const validation = step.codeValidation;
237
+ if (!validation) {
238
+ this.events.emit("tutorial:code:validated", {
239
+ tutorialId: this.activeTutorialId,
240
+ stepId: step.id,
241
+ valid: true
242
+ });
243
+ return { valid: true };
244
+ }
245
+ if (validation.pattern) {
246
+ const regex = new RegExp(validation.pattern);
247
+ if (!regex.test(code)) {
248
+ this.events.emit("tutorial:code:validated", {
249
+ tutorialId: this.activeTutorialId,
250
+ stepId: step.id,
251
+ valid: false
213
252
  });
214
- this.saveProgress(this.activeTutorialId);
215
- return step;
216
- }
217
- /** Stop the current tutorial */
218
- stopTutorial() {
219
- if (this.activeTutorialId) {
220
- this.saveProgress(this.activeTutorialId);
221
- this.activeTutorialId = null;
222
- }
253
+ return { valid: false, message: "Code does not match the expected pattern" };
254
+ }
223
255
  }
224
- /* ── Step Interaction ───────────────────────────────────────── */
225
- /** Submit a quiz answer */
226
- submitQuizAnswer(answerId) {
227
- if (!this.activeTutorialId) {
228
- return false;
229
- }
230
- const step = this.getCurrentStep();
231
- const prog = this.progress.get(this.activeTutorialId);
232
- if (!step || !prog || step.type !== 'quiz') {
233
- return false;
234
- }
235
- const correct = answerId === step.quizCorrectId;
236
- prog.quizScores[step.id] = correct;
237
- this.events.emit('tutorial:quiz:answered', {
256
+ if (validation.containsAll) {
257
+ for (const s of validation.containsAll) {
258
+ if (!code.includes(s)) {
259
+ this.events.emit("tutorial:code:validated", {
238
260
  tutorialId: this.activeTutorialId,
239
261
  stepId: step.id,
240
- correct,
262
+ valid: false
263
+ });
264
+ return { valid: false, message: `Missing required code: "${s}"` };
265
+ }
266
+ }
267
+ }
268
+ if (validation.containsAny) {
269
+ const found = validation.containsAny.some((s) => code.includes(s));
270
+ if (!found) {
271
+ this.events.emit("tutorial:code:validated", {
272
+ tutorialId: this.activeTutorialId,
273
+ stepId: step.id,
274
+ valid: false
241
275
  });
242
- this.saveProgress(this.activeTutorialId);
243
- return correct;
244
- }
245
- /** Submit code for validation */
246
- validateCode(code) {
247
- if (!this.activeTutorialId) {
248
- return { valid: false, message: 'No active tutorial' };
249
- }
250
- const step = this.getCurrentStep();
251
- const prog = this.progress.get(this.activeTutorialId);
252
- if (!step || !prog || step.type !== 'code') {
253
- return { valid: false, message: 'Not a code step' };
254
- }
255
- // Store submission
256
- prog.codeSubmissions[step.id] = code;
257
- const validation = step.codeValidation;
258
- if (!validation) {
259
- // No validation rules = always valid
260
- this.events.emit('tutorial:code:validated', {
261
- tutorialId: this.activeTutorialId,
262
- stepId: step.id,
263
- valid: true,
264
- });
265
- return { valid: true };
266
- }
267
- // Pattern match
268
- if (validation.pattern) {
269
- const regex = new RegExp(validation.pattern);
270
- if (!regex.test(code)) {
271
- this.events.emit('tutorial:code:validated', {
272
- tutorialId: this.activeTutorialId,
273
- stepId: step.id,
274
- valid: false,
275
- });
276
- return { valid: false, message: 'Code does not match the expected pattern' };
277
- }
278
- }
279
- // Contains all
280
- if (validation.containsAll) {
281
- for (const s of validation.containsAll) {
282
- if (!code.includes(s)) {
283
- this.events.emit('tutorial:code:validated', {
284
- tutorialId: this.activeTutorialId,
285
- stepId: step.id,
286
- valid: false,
287
- });
288
- return { valid: false, message: `Missing required code: "${s}"` };
289
- }
290
- }
291
- }
292
- // Contains any
293
- if (validation.containsAny) {
294
- const found = validation.containsAny.some((s) => code.includes(s));
295
- if (!found) {
296
- this.events.emit('tutorial:code:validated', {
297
- tutorialId: this.activeTutorialId,
298
- stepId: step.id,
299
- valid: false,
300
- });
301
- return { valid: false, message: 'Code is missing a required element' };
302
- }
303
- }
304
- // Max lines
305
- if (validation.maxLines) {
306
- const lineCount = code.split('\n').length;
307
- if (lineCount > validation.maxLines) {
308
- this.events.emit('tutorial:code:validated', {
309
- tutorialId: this.activeTutorialId,
310
- stepId: step.id,
311
- valid: false,
312
- });
313
- return { valid: false, message: `Code exceeds ${validation.maxLines} lines` };
314
- }
315
- }
316
- // Custom validator
317
- if (validation.customValidator) {
318
- const validatorFn = this.validators.get(validation.customValidator);
319
- if (validatorFn) {
320
- const result = validatorFn(code);
321
- this.events.emit('tutorial:code:validated', {
322
- tutorialId: this.activeTutorialId,
323
- stepId: step.id,
324
- valid: result.valid,
325
- });
326
- return result;
327
- }
328
- }
329
- this.events.emit('tutorial:code:validated', {
330
- tutorialId: this.activeTutorialId,
331
- stepId: step.id,
332
- valid: true,
276
+ return { valid: false, message: "Code is missing a required element" };
277
+ }
278
+ }
279
+ if (validation.maxLines) {
280
+ const lineCount = code.split("\n").length;
281
+ if (lineCount > validation.maxLines) {
282
+ this.events.emit("tutorial:code:validated", {
283
+ tutorialId: this.activeTutorialId,
284
+ stepId: step.id,
285
+ valid: false
333
286
  });
334
- return { valid: true };
335
- }
336
- /** Request a hint for the current step */
337
- getHint() {
338
- if (!this.activeTutorialId) {
339
- return null;
340
- }
341
- const step = this.getCurrentStep();
342
- if (!step?.hint) {
343
- return null;
344
- }
345
- this.events.emit('tutorial:hint:shown', {
346
- tutorialId: this.activeTutorialId,
347
- stepId: step.id,
287
+ return { valid: false, message: `Code exceeds ${validation.maxLines} lines` };
288
+ }
289
+ }
290
+ if (validation.customValidator) {
291
+ const validatorFn = this.validators.get(validation.customValidator);
292
+ if (validatorFn) {
293
+ const result = validatorFn(code);
294
+ this.events.emit("tutorial:code:validated", {
295
+ tutorialId: this.activeTutorialId,
296
+ stepId: step.id,
297
+ valid: result.valid
348
298
  });
349
- const locale = this.i18n.getLocale();
350
- return step.hint[locale] ?? step.hint['en'] ?? null;
351
- }
352
- /** Get localized content for current step */
353
- getStepContent(step) {
354
- const locale = this.i18n.getLocale();
355
- return {
356
- title: step.title[locale] ?? step.title['en'] ?? step.id,
357
- content: step.content[locale] ?? step.content['en'] ?? '',
358
- };
359
- }
360
- /* ── Progress Management ────────────────────────────────────── */
361
- /** Get progress for a tutorial */
362
- getProgress(tutorialId) {
363
- return this.progress.get(tutorialId);
364
- }
365
- /** Get progress percentage (0 to 1) */
366
- getProgressPercent(tutorialId) {
367
- const tutorial = this.tutorials.get(tutorialId);
368
- const prog = this.progress.get(tutorialId);
369
- if (!tutorial || !prog || tutorial.steps.length === 0) {
370
- return 0;
371
- }
372
- if (prog.completed) {
373
- return 1;
374
- }
375
- return prog.completedSteps.length / tutorial.steps.length;
376
- }
377
- /** Check if a tutorial is completed */
378
- isCompleted(tutorialId) {
379
- return this.progress.get(tutorialId)?.completed === true;
380
- }
381
- /** Get quiz score summary for a completed tutorial */
382
- getQuizScore(tutorialId) {
383
- const prog = this.progress.get(tutorialId);
384
- if (!prog) {
385
- return { correct: 0, total: 0, percentage: 0 };
386
- }
387
- const entries = Object.values(prog.quizScores);
388
- const total = entries.length;
389
- const correct = entries.filter(Boolean).length;
390
- return {
391
- correct,
392
- total,
393
- percentage: total > 0 ? (correct / total) * 100 : 0,
394
- };
395
- }
396
- /** Get all tutorials with their completion status */
397
- getAllProgress() {
398
- return this.getAllTutorials().map((tutorial) => ({
399
- tutorial,
400
- progress: this.progress.get(tutorial.id),
401
- percent: this.getProgressPercent(tutorial.id),
402
- }));
403
- }
404
- /** Reset progress for a tutorial */
405
- resetProgress(tutorialId) {
406
- this.progress.delete(tutorialId);
407
- if (this.persistProgress) {
408
- this.removeStoredProgress(tutorialId);
409
- }
410
- }
411
- /** Reset all progress */
412
- resetAllProgress() {
413
- const ids = Array.from(this.progress.keys());
414
- this.progress.clear();
415
- if (this.persistProgress) {
416
- for (const id of ids) {
417
- this.removeStoredProgress(id);
418
- }
419
- }
420
- }
421
- /* ── Persistence ────────────────────────────────────────────── */
422
- saveProgress(tutorialId) {
423
- if (!this.persistProgress) {
424
- return;
425
- }
426
- const prog = this.progress.get(tutorialId);
427
- if (!prog) {
428
- return;
429
- }
430
- try {
431
- if (typeof localStorage !== 'undefined') {
432
- localStorage.setItem(`${this.storagePrefix}${tutorialId}`, JSON.stringify(prog));
433
- this.events.emit('tutorial:progress:saved', { tutorialId });
434
- }
435
- }
436
- catch {
437
- // localStorage might not be available or quota exceeded
438
- }
439
- }
440
- loadAllProgress() {
441
- try {
442
- if (typeof localStorage === 'undefined') {
443
- return;
444
- }
445
- for (let i = 0; i < localStorage.length; i++) {
446
- const key = localStorage.key(i);
447
- if (!key?.startsWith(this.storagePrefix)) {
448
- continue;
449
- }
450
- const raw = localStorage.getItem(key);
451
- if (!raw) {
452
- continue;
453
- }
454
- const parsed = JSON.parse(raw);
455
- if (isProgressData(parsed)) {
456
- this.progress.set(parsed.tutorialId, parsed);
457
- }
458
- }
459
- }
460
- catch {
461
- // Ignore parse/access errors
462
- }
463
- }
464
- removeStoredProgress(tutorialId) {
465
- try {
466
- if (typeof localStorage !== 'undefined') {
467
- localStorage.removeItem(`${this.storagePrefix}${tutorialId}`);
468
- }
469
- }
470
- catch {
471
- // Ignore
472
- }
473
- }
474
- /* ── State ──────────────────────────────────────────────────── */
475
- getActiveTutorialId() {
476
- return this.activeTutorialId;
477
- }
478
- getActiveTutorial() {
479
- if (!this.activeTutorialId) {
480
- return null;
481
- }
482
- return this.tutorials.get(this.activeTutorialId) ?? null;
483
- }
484
- getTotalSteps() {
485
- if (!this.activeTutorialId) {
486
- return 0;
487
- }
488
- const tutorial = this.tutorials.get(this.activeTutorialId);
489
- return tutorial?.steps.length ?? 0;
490
- }
491
- getCurrentStepIndex() {
492
- if (!this.activeTutorialId) {
493
- return -1;
494
- }
495
- const prog = this.progress.get(this.activeTutorialId);
496
- return prog?.currentStepIndex ?? -1;
497
- }
498
- dispose() {
499
- this.stopTutorial();
500
- this.tutorials.clear();
501
- this.progress.clear();
502
- this.validators.clear();
503
- }
299
+ return result;
300
+ }
301
+ }
302
+ this.events.emit("tutorial:code:validated", {
303
+ tutorialId: this.activeTutorialId,
304
+ stepId: step.id,
305
+ valid: true
306
+ });
307
+ return { valid: true };
308
+ }
309
+ /** Request a hint for the current step */
310
+ getHint() {
311
+ if (!this.activeTutorialId) {
312
+ return null;
313
+ }
314
+ const step = this.getCurrentStep();
315
+ if (!step?.hint) {
316
+ return null;
317
+ }
318
+ this.events.emit("tutorial:hint:shown", {
319
+ tutorialId: this.activeTutorialId,
320
+ stepId: step.id
321
+ });
322
+ const locale = this.i18n.getLocale();
323
+ return step.hint[locale] ?? step.hint["en"] ?? null;
324
+ }
325
+ /** Get localized content for current step */
326
+ getStepContent(step) {
327
+ const locale = this.i18n.getLocale();
328
+ return {
329
+ title: step.title[locale] ?? step.title["en"] ?? step.id,
330
+ content: step.content[locale] ?? step.content["en"] ?? ""
331
+ };
332
+ }
333
+ /* ── Progress Management ────────────────────────────────────── */
334
+ /** Get progress for a tutorial */
335
+ getProgress(tutorialId) {
336
+ return this.progress.get(tutorialId);
337
+ }
338
+ /** Get progress percentage (0 to 1) */
339
+ getProgressPercent(tutorialId) {
340
+ const tutorial = this.tutorials.get(tutorialId);
341
+ const prog = this.progress.get(tutorialId);
342
+ if (!tutorial || !prog || tutorial.steps.length === 0) {
343
+ return 0;
344
+ }
345
+ if (prog.completed) {
346
+ return 1;
347
+ }
348
+ return prog.completedSteps.length / tutorial.steps.length;
349
+ }
350
+ /** Check if a tutorial is completed */
351
+ isCompleted(tutorialId) {
352
+ return this.progress.get(tutorialId)?.completed === true;
353
+ }
354
+ /** Get quiz score summary for a completed tutorial */
355
+ getQuizScore(tutorialId) {
356
+ const prog = this.progress.get(tutorialId);
357
+ if (!prog) {
358
+ return { correct: 0, total: 0, percentage: 0 };
359
+ }
360
+ const entries = Object.values(prog.quizScores);
361
+ const total = entries.length;
362
+ const correct = entries.filter(Boolean).length;
363
+ return {
364
+ correct,
365
+ total,
366
+ percentage: total > 0 ? correct / total * 100 : 0
367
+ };
368
+ }
369
+ /** Get all tutorials with their completion status */
370
+ getAllProgress() {
371
+ return this.getAllTutorials().map((tutorial) => ({
372
+ tutorial,
373
+ progress: this.progress.get(tutorial.id),
374
+ percent: this.getProgressPercent(tutorial.id)
375
+ }));
376
+ }
377
+ /** Reset progress for a tutorial */
378
+ resetProgress(tutorialId) {
379
+ this.progress.delete(tutorialId);
380
+ if (this.persistProgress) {
381
+ this.removeStoredProgress(tutorialId);
382
+ }
383
+ }
384
+ /** Reset all progress */
385
+ resetAllProgress() {
386
+ const ids = Array.from(this.progress.keys());
387
+ this.progress.clear();
388
+ if (this.persistProgress) {
389
+ for (const id of ids) {
390
+ this.removeStoredProgress(id);
391
+ }
392
+ }
393
+ }
394
+ /* ── Persistence ────────────────────────────────────────────── */
395
+ saveProgress(tutorialId) {
396
+ if (!this.persistProgress) {
397
+ return;
398
+ }
399
+ const prog = this.progress.get(tutorialId);
400
+ if (!prog) {
401
+ return;
402
+ }
403
+ try {
404
+ if (typeof localStorage !== "undefined") {
405
+ localStorage.setItem(`${this.storagePrefix}${tutorialId}`, JSON.stringify(prog));
406
+ this.events.emit("tutorial:progress:saved", { tutorialId });
407
+ }
408
+ } catch {
409
+ }
410
+ }
411
+ loadAllProgress() {
412
+ try {
413
+ if (typeof localStorage === "undefined") {
414
+ return;
415
+ }
416
+ for (let i = 0; i < localStorage.length; i++) {
417
+ const key = localStorage.key(i);
418
+ if (!key?.startsWith(this.storagePrefix)) {
419
+ continue;
420
+ }
421
+ const raw = localStorage.getItem(key);
422
+ if (!raw) {
423
+ continue;
424
+ }
425
+ const parsed = JSON.parse(raw);
426
+ if (isProgressData(parsed)) {
427
+ this.progress.set(parsed.tutorialId, parsed);
428
+ }
429
+ }
430
+ } catch {
431
+ }
432
+ }
433
+ removeStoredProgress(tutorialId) {
434
+ try {
435
+ if (typeof localStorage !== "undefined") {
436
+ localStorage.removeItem(`${this.storagePrefix}${tutorialId}`);
437
+ }
438
+ } catch {
439
+ }
440
+ }
441
+ /* ── State ──────────────────────────────────────────────────── */
442
+ getActiveTutorialId() {
443
+ return this.activeTutorialId;
444
+ }
445
+ getActiveTutorial() {
446
+ if (!this.activeTutorialId) {
447
+ return null;
448
+ }
449
+ return this.tutorials.get(this.activeTutorialId) ?? null;
450
+ }
451
+ getTotalSteps() {
452
+ if (!this.activeTutorialId) {
453
+ return 0;
454
+ }
455
+ const tutorial = this.tutorials.get(this.activeTutorialId);
456
+ return tutorial?.steps.length ?? 0;
457
+ }
458
+ getCurrentStepIndex() {
459
+ if (!this.activeTutorialId) {
460
+ return -1;
461
+ }
462
+ const prog = this.progress.get(this.activeTutorialId);
463
+ return prog?.currentStepIndex ?? -1;
464
+ }
465
+ dispose() {
466
+ this.stopTutorial();
467
+ this.tutorials.clear();
468
+ this.progress.clear();
469
+ this.validators.clear();
470
+ }
504
471
  }
505
- /* ── Type guard for loaded progress ──────────────────────────── */
506
472
  function isProgressData(v) {
507
- if (typeof v !== 'object' || v === null) {
508
- return false;
509
- }
510
- const obj = v;
511
- return (typeof obj.tutorialId === 'string' &&
512
- typeof obj.currentStepIndex === 'number' &&
513
- Array.isArray(obj.completedSteps) &&
514
- typeof obj.startedAt === 'number');
473
+ if (typeof v !== "object" || v === null) {
474
+ return false;
475
+ }
476
+ const obj = v;
477
+ return typeof obj.tutorialId === "string" && typeof obj.currentStepIndex === "number" && Array.isArray(obj.completedSteps) && typeof obj.startedAt === "number";
515
478
  }
516
- /* ── Built-in Tutorials ──────────────────────────────────────── */
517
- /** Tutorial: First Game — Bouncing Ball */
518
479
  const TUTORIAL_FIRST_GAME = {
519
- id: 'first-game-bouncing-ball',
520
- title: {
521
- en: 'Your First Game: Bouncing Ball',
522
- pl: 'Twoja pierwsza gra: Odbijająca się piłka',
523
- de: 'Dein erstes Spiel: Hüpfender Ball',
480
+ id: "first-game-bouncing-ball",
481
+ title: {
482
+ en: "Your First Game: Bouncing Ball",
483
+ pl: "Twoja pierwsza gra: Odbijająca się piłka",
484
+ de: "Dein erstes Spiel: Hüpfender Ball"
485
+ },
486
+ description: {
487
+ en: "Learn to create a simple bouncing ball game. Perfect for beginners!",
488
+ pl: "Naucz się tworzyć prostą grę z odbijającą się piłką. Idealne na początek!",
489
+ de: "Lerne, ein einfaches Hüpfball-Spiel zu erstellen. Perfekt für Anfänger!"
490
+ },
491
+ category: "game-basics",
492
+ difficulty: "beginner",
493
+ estimatedMinutes: 15,
494
+ kidFriendly: true,
495
+ ageRange: { min: 8, max: 99 },
496
+ tags: ["ball", "physics", "beginner", "first-game"],
497
+ steps: [
498
+ {
499
+ id: "welcome",
500
+ type: "text",
501
+ title: {
502
+ en: "Welcome!",
503
+ pl: "Witaj!",
504
+ de: "Willkommen!"
505
+ },
506
+ content: {
507
+ en: "In this tutorial, you will create a bouncing ball game!\n\nYou will learn:\n• How to create entities\n• How to add physics\n• How to handle collisions\n\nLet's get started!",
508
+ pl: "W tym samouczku stworzysz grę z odbijającą się piłką!\n\nNauczysz się:\n• Jak tworzyć obiekty\n• Jak dodawać fizykę\n• Jak obsługiwać kolizje\n\nZaczynajmy!",
509
+ de: "In diesem Tutorial erstellst du ein Hüpfball-Spiel!\n\nDu lernst:\n• Wie man Entities erstellt\n• Wie man Physik hinzufügt\n• Wie man Kollisionen behandelt\n\nLos geht's!"
510
+ },
511
+ skippable: true,
512
+ tooltipPosition: "center"
524
513
  },
525
- description: {
526
- en: 'Learn to create a simple bouncing ball game. Perfect for beginners!',
527
- pl: 'Naucz się tworzyć prostą grę z odbijającą się piłką. Idealne na początek!',
528
- de: 'Lerne, ein einfaches Hüpfball-Spiel zu erstellen. Perfekt für Anfänger!',
514
+ {
515
+ id: "create-engine",
516
+ type: "code",
517
+ title: {
518
+ en: "Create the Game Engine",
519
+ pl: "Utwórz silnik gry",
520
+ de: "Erstelle die Game-Engine"
521
+ },
522
+ content: {
523
+ en: "First, let's create a game engine instance and set up the canvas.\n\nType the code below to initialize your game:",
524
+ pl: "Najpierw utwórzmy instancję silnika gry i skonfigurujmy canvas.\n\nWpisz poniższy kod, aby zainicjować grę:",
525
+ de: "Zuerst erstellen wir eine Game-Engine-Instanz und richten das Canvas ein.\n\nGib den folgenden Code ein, um dein Spiel zu initialisieren:"
526
+ },
527
+ codeTemplate: "// Create a game engine\nconst engine = ",
528
+ codeSolution: 'const engine = new Engine({\n canvas: document.getElementById("game"),\n width: 800,\n height: 600,\n});',
529
+ codeValidation: {
530
+ containsAll: ["new Engine", "canvas"]
531
+ },
532
+ hint: {
533
+ en: "Use: new Engine({ canvas: ..., width: 800, height: 600 })",
534
+ pl: "Użyj: new Engine({ canvas: ..., width: 800, height: 600 })",
535
+ de: "Verwende: new Engine({ canvas: ..., width: 800, height: 600 })"
536
+ }
529
537
  },
530
- category: 'game-basics',
531
- difficulty: 'beginner',
532
- estimatedMinutes: 15,
533
- kidFriendly: true,
534
- ageRange: { min: 8, max: 99 },
535
- tags: ['ball', 'physics', 'beginner', 'first-game'],
536
- steps: [
537
- {
538
- id: 'welcome',
539
- type: 'text',
540
- title: {
541
- en: 'Welcome!',
542
- pl: 'Witaj!',
543
- de: 'Willkommen!',
544
- },
545
- content: {
546
- en: "In this tutorial, you will create a bouncing ball game!\n\nYou will learn:\n• How to create entities\n• How to add physics\n• How to handle collisions\n\nLet's get started!",
547
- pl: 'W tym samouczku stworzysz grę z odbijającą się piłką!\n\nNauczysz się:\n• Jak tworzyć obiekty\n• Jak dodawać fizykę\n• Jak obsługiwać kolizje\n\nZaczynajmy!',
548
- de: "In diesem Tutorial erstellst du ein Hüpfball-Spiel!\n\nDu lernst:\n• Wie man Entities erstellt\n• Wie man Physik hinzufügt\n• Wie man Kollisionen behandelt\n\nLos geht's!",
549
- },
550
- skippable: true,
551
- tooltipPosition: 'center',
552
- },
553
- {
554
- id: 'create-engine',
555
- type: 'code',
556
- title: {
557
- en: 'Create the Game Engine',
558
- pl: 'Utwórz silnik gry',
559
- de: 'Erstelle die Game-Engine',
560
- },
561
- content: {
562
- en: "First, let's create a game engine instance and set up the canvas.\n\nType the code below to initialize your game:",
563
- pl: 'Najpierw utwórzmy instancję silnika gry i skonfigurujmy canvas.\n\nWpisz poniższy kod, aby zainicjować grę:',
564
- de: 'Zuerst erstellen wir eine Game-Engine-Instanz und richten das Canvas ein.\n\nGib den folgenden Code ein, um dein Spiel zu initialisieren:',
565
- },
566
- codeTemplate: '// Create a game engine\nconst engine = ',
567
- codeSolution: 'const engine = new Engine({\n canvas: document.getElementById("game"),\n width: 800,\n height: 600,\n});',
568
- codeValidation: {
569
- containsAll: ['new Engine', 'canvas'],
570
- },
571
- hint: {
572
- en: 'Use: new Engine({ canvas: ..., width: 800, height: 600 })',
573
- pl: 'Użyj: new Engine({ canvas: ..., width: 800, height: 600 })',
574
- de: 'Verwende: new Engine({ canvas: ..., width: 800, height: 600 })',
575
- },
576
- },
577
- {
578
- id: 'create-ball',
579
- type: 'code',
580
- title: {
581
- en: 'Create the Ball',
582
- pl: 'Utwórz piłkę',
583
- de: 'Erstelle den Ball',
584
- },
585
- content: {
586
- en: "Now let's create a ball entity with a transform and a circle shape!",
587
- pl: 'Teraz stwórzmy obiekt piłki z transformacją i kształtem koła!',
588
- de: 'Jetzt erstellen wir eine Ball-Entity mit Transform und Kreisform!',
589
- },
590
- codeTemplate: '// Create a ball entity\nconst ball = engine.world.createEntity();\n// Add components here...',
591
- codeSolution: 'const ball = engine.world.createEntity();\nengine.world.addComponent(ball, "Transform", { x: 400, y: 100 });\nengine.world.addComponent(ball, "Sprite", { shape: "circle", radius: 20, color: "red" });',
592
- codeValidation: {
593
- containsAll: ['createEntity', 'Transform', 'addComponent'],
594
- },
595
- hint: {
596
- en: 'Create an entity, then add Transform and Sprite components.',
597
- pl: 'Utwórz obiekt, a następnie dodaj komponenty Transform i Sprite.',
598
- de: 'Erstelle eine Entity und füge Transform- und Sprite-Komponenten hinzu.',
599
- },
600
- },
601
- {
602
- id: 'add-physics',
603
- type: 'code',
604
- title: {
605
- en: 'Add Physics!',
606
- pl: 'Dodaj fizykę!',
607
- de: 'Füge Physik hinzu!',
608
- },
609
- content: {
610
- en: "Let's make the ball fall and bounce! Add a RigidBody and a Collider.",
611
- pl: 'Sprawmy, żeby piłka spadała i odbijała się! Dodaj RigidBody i Collider.',
612
- de: 'Lass den Ball fallen und abprallen! Füge einen RigidBody und einen Collider hinzu.',
613
- },
614
- codeTemplate: '// Add physics to the ball\n',
615
- codeSolution: 'engine.world.addComponent(ball, "RigidBody2D", { type: "dynamic", gravityScale: 1 });\nengine.world.addComponent(ball, "Collider2D", { shape: "circle", radius: 20, restitution: 0.8 });',
616
- codeValidation: {
617
- containsAll: ['RigidBody2D', 'Collider2D', 'restitution'],
618
- },
619
- hint: {
620
- en: 'RigidBody2D makes it move, Collider2D detects hits. Set restitution > 0 for bounce!',
621
- pl: 'RigidBody2D porusza obiekt, Collider2D wykrywa zderzenia. Ustaw restitution > 0 dla odbicia!',
622
- de: 'RigidBody2D bewegt es, Collider2D erkennt Treffer. Setze restitution > 0 für Abprallen!',
623
- },
624
- },
625
- {
626
- id: 'quiz-physics',
627
- type: 'quiz',
628
- title: {
629
- en: 'Quick Quiz!',
630
- pl: 'Szybki quiz!',
631
- de: 'Schnelles Quiz!',
632
- },
633
- content: {
634
- en: 'What does the "restitution" value control?',
635
- pl: 'Co kontroluje wartość "restitution"?',
636
- de: 'Was steuert der "restitution"-Wert?',
637
- },
638
- quizOptions: [
639
- { id: 'a', text: { en: "The ball's color", pl: 'Kolor piłki', de: 'Die Farbe des Balls' } },
640
- {
641
- id: 'b',
642
- text: {
643
- en: 'How bouncy the ball is',
644
- pl: 'Jak bardzo piłka się odbija',
645
- de: 'Wie stark der Ball abprallt',
646
- },
647
- },
648
- {
649
- id: 'c',
650
- text: {
651
- en: "The ball's speed",
652
- pl: 'Prędkość piłki',
653
- de: 'Die Geschwindigkeit des Balls',
654
- },
655
- },
656
- {
657
- id: 'd',
658
- text: { en: "The ball's size", pl: 'Rozmiar piłki', de: 'Die Größe des Balls' },
659
- },
660
- ],
661
- quizCorrectId: 'b',
662
- },
538
+ {
539
+ id: "create-ball",
540
+ type: "code",
541
+ title: {
542
+ en: "Create the Ball",
543
+ pl: "Utwórz piłkę",
544
+ de: "Erstelle den Ball"
545
+ },
546
+ content: {
547
+ en: "Now let's create a ball entity with a transform and a circle shape!",
548
+ pl: "Teraz stwórzmy obiekt piłki z transformacją i kształtem koła!",
549
+ de: "Jetzt erstellen wir eine Ball-Entity mit Transform und Kreisform!"
550
+ },
551
+ codeTemplate: "// Create a ball entity\nconst ball = engine.world.createEntity();\n// Add components here...",
552
+ codeSolution: 'const ball = engine.world.createEntity();\nengine.world.addComponent(ball, "Transform", { x: 400, y: 100 });\nengine.world.addComponent(ball, "Sprite", { shape: "circle", radius: 20, color: "red" });',
553
+ codeValidation: {
554
+ containsAll: ["createEntity", "Transform", "addComponent"]
555
+ },
556
+ hint: {
557
+ en: "Create an entity, then add Transform and Sprite components.",
558
+ pl: "Utwórz obiekt, a następnie dodaj komponenty Transform i Sprite.",
559
+ de: "Erstelle eine Entity und füge Transform- und Sprite-Komponenten hinzu."
560
+ }
561
+ },
562
+ {
563
+ id: "add-physics",
564
+ type: "code",
565
+ title: {
566
+ en: "Add Physics!",
567
+ pl: "Dodaj fizykę!",
568
+ de: "Füge Physik hinzu!"
569
+ },
570
+ content: {
571
+ en: "Let's make the ball fall and bounce! Add a RigidBody and a Collider.",
572
+ pl: "Sprawmy, żeby piłka spadała i odbijała się! Dodaj RigidBody i Collider.",
573
+ de: "Lass den Ball fallen und abprallen! Füge einen RigidBody und einen Collider hinzu."
574
+ },
575
+ codeTemplate: "// Add physics to the ball\n",
576
+ codeSolution: 'engine.world.addComponent(ball, "RigidBody2D", { type: "dynamic", gravityScale: 1 });\nengine.world.addComponent(ball, "Collider2D", { shape: "circle", radius: 20, restitution: 0.8 });',
577
+ codeValidation: {
578
+ containsAll: ["RigidBody2D", "Collider2D", "restitution"]
579
+ },
580
+ hint: {
581
+ en: "RigidBody2D makes it move, Collider2D detects hits. Set restitution > 0 for bounce!",
582
+ pl: "RigidBody2D porusza obiekt, Collider2D wykrywa zderzenia. Ustaw restitution > 0 dla odbicia!",
583
+ de: "RigidBody2D bewegt es, Collider2D erkennt Treffer. Setze restitution > 0 für Abprallen!"
584
+ }
585
+ },
586
+ {
587
+ id: "quiz-physics",
588
+ type: "quiz",
589
+ title: {
590
+ en: "Quick Quiz!",
591
+ pl: "Szybki quiz!",
592
+ de: "Schnelles Quiz!"
593
+ },
594
+ content: {
595
+ en: 'What does the "restitution" value control?',
596
+ pl: 'Co kontroluje wartość "restitution"?',
597
+ de: 'Was steuert der "restitution"-Wert?'
598
+ },
599
+ quizOptions: [
600
+ { id: "a", text: { en: "The ball's color", pl: "Kolor piłki", de: "Die Farbe des Balls" } },
663
601
  {
664
- id: 'create-floor',
665
- type: 'code',
666
- title: {
667
- en: 'Create the Floor',
668
- pl: 'Utwórz podłogę',
669
- de: 'Erstelle den Boden',
670
- },
671
- content: {
672
- en: 'The ball needs something to bounce off! Create a static floor at the bottom.',
673
- pl: 'Piłka potrzebuje czegoś, od czego się odbije! Utwórz statyczną podłogę na dole.',
674
- de: 'Der Ball braucht etwas zum Abprallen! Erstelle einen statischen Boden unten.',
675
- },
676
- codeTemplate: '// Create a floor\nconst floor = engine.world.createEntity();\n',
677
- codeSolution: 'const floor = engine.world.createEntity();\nengine.world.addComponent(floor, "Transform", { x: 400, y: 580 });\nengine.world.addComponent(floor, "Collider2D", { shape: "box", width: 800, height: 20 });\nengine.world.addComponent(floor, "RigidBody2D", { type: "static" });',
678
- codeValidation: {
679
- containsAll: ['createEntity', 'static', 'Collider2D'],
680
- },
681
- hint: {
682
- en: 'Use RigidBody2D type: "static" — the floor shouldn\'t move!',
683
- pl: 'Użyj RigidBody2D type: "static" — podłoga nie powinna się ruszać!',
684
- de: 'Verwende RigidBody2D type: "static" — der Boden soll sich nicht bewegen!',
685
- },
602
+ id: "b",
603
+ text: {
604
+ en: "How bouncy the ball is",
605
+ pl: "Jak bardzo piłka się odbija",
606
+ de: "Wie stark der Ball abprallt"
607
+ }
686
608
  },
687
609
  {
688
- id: 'start-game',
689
- type: 'code',
690
- title: {
691
- en: 'Start the Game!',
692
- pl: 'Uruchom grę!',
693
- de: 'Starte das Spiel!',
694
- },
695
- content: {
696
- en: 'Everything is ready! Start the game loop to see your bouncing ball in action!',
697
- pl: 'Wszystko gotowe! Uruchom pętlę gry, aby zobaczyć swoją odbijającą się piłkę w akcji!',
698
- de: 'Alles bereit! Starte die Game-Loop, um deinen hüpfenden Ball in Aktion zu sehen!',
699
- },
700
- codeTemplate: '// Start the game\n',
701
- codeSolution: 'engine.start();',
702
- codeValidation: {
703
- containsAll: ['engine.start'],
704
- },
610
+ id: "c",
611
+ text: {
612
+ en: "The ball's speed",
613
+ pl: "Prędkość piłki",
614
+ de: "Die Geschwindigkeit des Balls"
615
+ }
705
616
  },
706
617
  {
707
- id: 'congratulations',
708
- type: 'text',
709
- title: {
710
- en: 'Congratulations!',
711
- pl: 'Gratulacje!',
712
- de: 'Herzlichen Glückwunsch!',
713
- },
714
- content: {
715
- en: 'You just created your first game with Nice2Dev Game Engine!\n\nYou learned:\n• Creating entities\n• Adding components (Transform, Sprite, RigidBody2D, Collider2D)\n• Physics simulation with restitution\n• Starting the game loop\n\nNext up: Try the "Space Shooter" tutorial!',
716
- pl: 'Właśnie stworzyłeś swoją pierwszą grę z Nice2Dev Game Engine!\n\nNauczyłeś się:\n• Tworzenia obiektów\n• Dodawania komponentów (Transform, Sprite, RigidBody2D, Collider2D)\n• Symulacji fizyki z restitution\n• Uruchamiania pętli gry\n\nNastępnie: Spróbuj samouczka "Kosmiczny Strzelec"!',
717
- de: 'Du hast gerade dein erstes Spiel mit Nice2Dev Game Engine erstellt!\n\nDu hast gelernt:\n• Entities erstellen\n• Komponenten hinzufügen (Transform, Sprite, RigidBody2D, Collider2D)\n• Physiksimulation mit Restitution\n• Game-Loop starten\n\nAls nächstes: Probiere das "Space Shooter"-Tutorial!',
718
- },
719
- tooltipPosition: 'center',
720
- },
721
- ],
618
+ id: "d",
619
+ text: { en: "The ball's size", pl: "Rozmiar piłki", de: "Die Größe des Balls" }
620
+ }
621
+ ],
622
+ quizCorrectId: "b"
623
+ },
624
+ {
625
+ id: "create-floor",
626
+ type: "code",
627
+ title: {
628
+ en: "Create the Floor",
629
+ pl: "Utwórz podłogę",
630
+ de: "Erstelle den Boden"
631
+ },
632
+ content: {
633
+ en: "The ball needs something to bounce off! Create a static floor at the bottom.",
634
+ pl: "Piłka potrzebuje czegoś, od czego się odbije! Utwórz statyczną podłogę na dole.",
635
+ de: "Der Ball braucht etwas zum Abprallen! Erstelle einen statischen Boden unten."
636
+ },
637
+ codeTemplate: "// Create a floor\nconst floor = engine.world.createEntity();\n",
638
+ codeSolution: 'const floor = engine.world.createEntity();\nengine.world.addComponent(floor, "Transform", { x: 400, y: 580 });\nengine.world.addComponent(floor, "Collider2D", { shape: "box", width: 800, height: 20 });\nengine.world.addComponent(floor, "RigidBody2D", { type: "static" });',
639
+ codeValidation: {
640
+ containsAll: ["createEntity", "static", "Collider2D"]
641
+ },
642
+ hint: {
643
+ en: `Use RigidBody2D type: "static" — the floor shouldn't move!`,
644
+ pl: 'Użyj RigidBody2D type: "static" — podłoga nie powinna się ruszać!',
645
+ de: 'Verwende RigidBody2D type: "static" — der Boden soll sich nicht bewegen!'
646
+ }
647
+ },
648
+ {
649
+ id: "start-game",
650
+ type: "code",
651
+ title: {
652
+ en: "Start the Game!",
653
+ pl: "Uruchom grę!",
654
+ de: "Starte das Spiel!"
655
+ },
656
+ content: {
657
+ en: "Everything is ready! Start the game loop to see your bouncing ball in action!",
658
+ pl: "Wszystko gotowe! Uruchom pętlę gry, aby zobaczyć swoją odbijającą się piłkę w akcji!",
659
+ de: "Alles bereit! Starte die Game-Loop, um deinen hüpfenden Ball in Aktion zu sehen!"
660
+ },
661
+ codeTemplate: "// Start the game\n",
662
+ codeSolution: "engine.start();",
663
+ codeValidation: {
664
+ containsAll: ["engine.start"]
665
+ }
666
+ },
667
+ {
668
+ id: "congratulations",
669
+ type: "text",
670
+ title: {
671
+ en: "Congratulations!",
672
+ pl: "Gratulacje!",
673
+ de: "Herzlichen Glückwunsch!"
674
+ },
675
+ content: {
676
+ en: 'You just created your first game with Nice2Dev Game Engine!\n\nYou learned:\n• Creating entities\n• Adding components (Transform, Sprite, RigidBody2D, Collider2D)\n• Physics simulation with restitution\n• Starting the game loop\n\nNext up: Try the "Space Shooter" tutorial!',
677
+ pl: 'Właśnie stworzyłeś swoją pierwszą grę z Nice2Dev Game Engine!\n\nNauczyłeś się:\n• Tworzenia obiektów\n• Dodawania komponentów (Transform, Sprite, RigidBody2D, Collider2D)\n• Symulacji fizyki z restitution\n• Uruchamiania pętli gry\n\nNastępnie: Spróbuj samouczka "Kosmiczny Strzelec"!',
678
+ de: 'Du hast gerade dein erstes Spiel mit Nice2Dev Game Engine erstellt!\n\nDu hast gelernt:\n• Entities erstellen\n• Komponenten hinzufügen (Transform, Sprite, RigidBody2D, Collider2D)\n• Physiksimulation mit Restitution\n• Game-Loop starten\n\nAls nächstes: Probiere das "Space Shooter"-Tutorial!'
679
+ },
680
+ tooltipPosition: "center"
681
+ }
682
+ ]
722
683
  };
723
- /** Tutorial: Space Shooter */
724
684
  const TUTORIAL_SPACE_SHOOTER = {
725
- id: 'space-shooter',
726
- title: {
727
- en: 'Space Shooter',
728
- pl: 'Kosmiczny Strzelec',
729
- de: 'Weltraum-Shooter',
685
+ id: "space-shooter",
686
+ title: {
687
+ en: "Space Shooter",
688
+ pl: "Kosmiczny Strzelec",
689
+ de: "Weltraum-Shooter"
690
+ },
691
+ description: {
692
+ en: "Build a classic space shooter! Learn input handling, spawning enemies, and scoring.",
693
+ pl: "Zbuduj klasyczną kosmiczną strzelankę! Naucz się obsługi sterowania, tworzenia wrogów i punktacji.",
694
+ de: "Baue einen klassischen Weltraum-Shooter! Lerne Eingabeverarbeitung, Gegner-Spawning und Punktestand."
695
+ },
696
+ category: "game-basics",
697
+ difficulty: "beginner",
698
+ estimatedMinutes: 25,
699
+ kidFriendly: true,
700
+ ageRange: { min: 10, max: 99 },
701
+ tags: ["shooter", "space", "input", "spawning", "scoring"],
702
+ prerequisites: ["first-game-bouncing-ball"],
703
+ steps: [
704
+ {
705
+ id: "intro",
706
+ type: "text",
707
+ title: { en: "Space Shooter", pl: "Kosmiczny Strzelec", de: "Weltraum-Shooter" },
708
+ content: {
709
+ en: 'Welcome to the Space Shooter tutorial!\n\nYou will learn:\n• Keyboard input handling\n• Spawning entities dynamically\n• Collision-based scoring\n• Game over logic\n\nPrerequisite: "Your First Game: Bouncing Ball"',
710
+ pl: 'Witaj w samouczku Kosmiczny Strzelec!\n\nNauczysz się:\n• Obsługi sterowania z klawiatury\n• Dynamicznego tworzenia obiektów\n• Punktacji na podstawie kolizji\n• Logiki końca gry\n\nWymagany: "Twoja pierwsza gra: Odbijająca się piłka"',
711
+ de: 'Willkommen beim Weltraum-Shooter Tutorial!\n\nDu lernst:\n• Tastatureingabe-Verarbeitung\n• Dynamisches Entity-Spawning\n• Kollisionsbasiertes Scoring\n• Game-Over-Logik\n\nVoraussetzung: "Dein erstes Spiel: Hüpfender Ball"'
712
+ },
713
+ tooltipPosition: "center"
730
714
  },
731
- description: {
732
- en: 'Build a classic space shooter! Learn input handling, spawning enemies, and scoring.',
733
- pl: 'Zbuduj klasyczną kosmiczną strzelankę! Naucz się obsługi sterowania, tworzenia wrogów i punktacji.',
734
- de: 'Baue einen klassischen Weltraum-Shooter! Lerne Eingabeverarbeitung, Gegner-Spawning und Punktestand.',
715
+ {
716
+ id: "setup-scene",
717
+ type: "code",
718
+ title: { en: "Setup the Scene", pl: "Przygotuj scenę", de: "Szene einrichten" },
719
+ content: {
720
+ en: "Create the engine and a spaceship entity. The ship should be at the bottom center of the screen.",
721
+ pl: "Utwórz silnik i obiekt statku kosmicznego. Statek powinien być na dole, pośrodku ekranu.",
722
+ de: "Erstelle die Engine und eine Raumschiff-Entity. Das Schiff soll unten in der Mitte des Bildschirms sein."
723
+ },
724
+ codeTemplate: '// Setup engine and player ship\nconst engine = new Engine({ canvas: document.getElementById("game"), width: 800, height: 600 });\n\n// Create player ship\n',
725
+ codeSolution: 'const ship = engine.world.createEntity();\nengine.world.addComponent(ship, "Transform", { x: 400, y: 550 });\nengine.world.addComponent(ship, "Sprite", { shape: "triangle", width: 30, height: 40, color: "cyan" });',
726
+ codeValidation: { containsAll: ["createEntity", "Transform", "Sprite"] }
735
727
  },
736
- category: 'game-basics',
737
- difficulty: 'beginner',
738
- estimatedMinutes: 25,
739
- kidFriendly: true,
740
- ageRange: { min: 10, max: 99 },
741
- tags: ['shooter', 'space', 'input', 'spawning', 'scoring'],
742
- prerequisites: ['first-game-bouncing-ball'],
743
- steps: [
744
- {
745
- id: 'intro',
746
- type: 'text',
747
- title: { en: 'Space Shooter', pl: 'Kosmiczny Strzelec', de: 'Weltraum-Shooter' },
748
- content: {
749
- en: 'Welcome to the Space Shooter tutorial!\n\nYou will learn:\n• Keyboard input handling\n• Spawning entities dynamically\n• Collision-based scoring\n• Game over logic\n\nPrerequisite: "Your First Game: Bouncing Ball"',
750
- pl: 'Witaj w samouczku Kosmiczny Strzelec!\n\nNauczysz się:\n• Obsługi sterowania z klawiatury\n• Dynamicznego tworzenia obiektów\n• Punktacji na podstawie kolizji\n• Logiki końca gry\n\nWymagany: "Twoja pierwsza gra: Odbijająca się piłka"',
751
- de: 'Willkommen beim Weltraum-Shooter Tutorial!\n\nDu lernst:\n• Tastatureingabe-Verarbeitung\n• Dynamisches Entity-Spawning\n• Kollisionsbasiertes Scoring\n• Game-Over-Logik\n\nVoraussetzung: "Dein erstes Spiel: Hüpfender Ball"',
752
- },
753
- tooltipPosition: 'center',
754
- },
755
- {
756
- id: 'setup-scene',
757
- type: 'code',
758
- title: { en: 'Setup the Scene', pl: 'Przygotuj scenę', de: 'Szene einrichten' },
759
- content: {
760
- en: 'Create the engine and a spaceship entity. The ship should be at the bottom center of the screen.',
761
- pl: 'Utwórz silnik i obiekt statku kosmicznego. Statek powinien być na dole, pośrodku ekranu.',
762
- de: 'Erstelle die Engine und eine Raumschiff-Entity. Das Schiff soll unten in der Mitte des Bildschirms sein.',
763
- },
764
- codeTemplate: '// Setup engine and player ship\nconst engine = new Engine({ canvas: document.getElementById("game"), width: 800, height: 600 });\n\n// Create player ship\n',
765
- codeSolution: 'const ship = engine.world.createEntity();\nengine.world.addComponent(ship, "Transform", { x: 400, y: 550 });\nengine.world.addComponent(ship, "Sprite", { shape: "triangle", width: 30, height: 40, color: "cyan" });',
766
- codeValidation: { containsAll: ['createEntity', 'Transform', 'Sprite'] },
767
- },
768
- {
769
- id: 'add-input',
770
- type: 'code',
771
- title: { en: 'Add Controls', pl: 'Dodaj sterowanie', de: 'Steuerung hinzufügen' },
772
- content: {
773
- en: "Let's make the ship move with arrow keys! Create a system that reads keyboard input.",
774
- pl: 'Sprawmy, żeby statek poruszał się strzałkami! Stwórz system odczytujący klawiaturę.',
775
- de: 'Lass das Schiff mit Pfeiltasten bewegen! Erstelle ein System, das Tastatureingaben liest.',
776
- },
777
- codeTemplate: '// Movement system\nengine.world.addSystem("ShipMovement", (world) => {\n // Your code here\n});\n',
778
- codeSolution: 'engine.world.addSystem("ShipMovement", (world) => {\n const input = engine.input;\n const transform = world.getComponent(ship, "Transform");\n const speed = 5;\n if (input.isKeyDown("ArrowLeft")) transform.x -= speed;\n if (input.isKeyDown("ArrowRight")) transform.x += speed;\n});',
779
- codeValidation: { containsAll: ['isKeyDown', 'ArrowLeft', 'ArrowRight'] },
780
- hint: {
781
- en: 'Use engine.input.isKeyDown("ArrowLeft/ArrowRight") to check keys.',
782
- pl: 'Użyj engine.input.isKeyDown("ArrowLeft/ArrowRight") do sprawdzenia klawiszy.',
783
- de: 'Verwende engine.input.isKeyDown("ArrowLeft/ArrowRight") um Tasten zu prüfen.',
784
- },
785
- },
786
- {
787
- id: 'quiz-input',
788
- type: 'quiz',
789
- title: { en: 'Input Quiz', pl: 'Quiz o sterowaniu', de: 'Eingabe-Quiz' },
790
- content: {
791
- en: 'What is the difference between isKeyDown and isKeyPressed?',
792
- pl: 'Jaka jest różnica między isKeyDown i isKeyPressed?',
793
- de: 'Was ist der Unterschied zwischen isKeyDown und isKeyPressed?',
794
- },
795
- quizOptions: [
796
- {
797
- id: 'a',
798
- text: { en: 'They are the same thing', pl: 'To to samo', de: 'Sie sind dasselbe' },
799
- },
800
- {
801
- id: 'b',
802
- text: {
803
- en: 'isKeyDown is continuous, isKeyPressed is single-frame',
804
- pl: 'isKeyDown jest ciągły, isKeyPressed jednoklatkowo',
805
- de: 'isKeyDown ist kontinuierlich, isKeyPressed einmalig',
806
- },
807
- },
808
- {
809
- id: 'c',
810
- text: {
811
- en: 'isKeyDown works only with arrows',
812
- pl: 'isKeyDown działa tylko ze strzałkami',
813
- de: 'isKeyDown funktioniert nur mit Pfeiltasten',
814
- },
815
- },
816
- ],
817
- quizCorrectId: 'b',
818
- },
728
+ {
729
+ id: "add-input",
730
+ type: "code",
731
+ title: { en: "Add Controls", pl: "Dodaj sterowanie", de: "Steuerung hinzufügen" },
732
+ content: {
733
+ en: "Let's make the ship move with arrow keys! Create a system that reads keyboard input.",
734
+ pl: "Sprawmy, żeby statek poruszał się strzałkami! Stwórz system odczytujący klawiaturę.",
735
+ de: "Lass das Schiff mit Pfeiltasten bewegen! Erstelle ein System, das Tastatureingaben liest."
736
+ },
737
+ codeTemplate: '// Movement system\nengine.world.addSystem("ShipMovement", (world) => {\n // Your code here\n});\n',
738
+ codeSolution: 'engine.world.addSystem("ShipMovement", (world) => {\n const input = engine.input;\n const transform = world.getComponent(ship, "Transform");\n const speed = 5;\n if (input.isKeyDown("ArrowLeft")) transform.x -= speed;\n if (input.isKeyDown("ArrowRight")) transform.x += speed;\n});',
739
+ codeValidation: { containsAll: ["isKeyDown", "ArrowLeft", "ArrowRight"] },
740
+ hint: {
741
+ en: 'Use engine.input.isKeyDown("ArrowLeft/ArrowRight") to check keys.',
742
+ pl: 'Użyj engine.input.isKeyDown("ArrowLeft/ArrowRight") do sprawdzenia klawiszy.',
743
+ de: 'Verwende engine.input.isKeyDown("ArrowLeft/ArrowRight") um Tasten zu prüfen.'
744
+ }
745
+ },
746
+ {
747
+ id: "quiz-input",
748
+ type: "quiz",
749
+ title: { en: "Input Quiz", pl: "Quiz o sterowaniu", de: "Eingabe-Quiz" },
750
+ content: {
751
+ en: "What is the difference between isKeyDown and isKeyPressed?",
752
+ pl: "Jaka jest różnica między isKeyDown i isKeyPressed?",
753
+ de: "Was ist der Unterschied zwischen isKeyDown und isKeyPressed?"
754
+ },
755
+ quizOptions: [
819
756
  {
820
- id: 'shooting',
821
- type: 'code',
822
- title: { en: 'Shoot Bullets!', pl: 'Strzelaj pociskami!', de: 'Schieße Kugeln!' },
823
- content: {
824
- en: 'Add shooting! When Space is pressed, create a bullet entity that moves upward.',
825
- pl: 'Dodaj strzelanie! Po naciśnięciu Spacji utwórz pocisk, który leci w górę.',
826
- de: 'Füge Schießen hinzu! Bei Leertaste einen Kugel-Entity erstellen, der nach oben fliegt.',
827
- },
828
- codeTemplate: '// Shooting system\nengine.world.addSystem("Shooting", (world) => {\n const input = engine.input;\n // Your shooting code here\n});\n',
829
- codeSolution: 'if (input.isKeyPressed("Space")) {\n const bullet = world.createEntity();\n const shipPos = world.getComponent(ship, "Transform");\n world.addComponent(bullet, "Transform", { x: shipPos.x, y: shipPos.y - 20 });\n world.addComponent(bullet, "Velocity", { x: 0, y: -10 });\n world.addComponent(bullet, "Sprite", { shape: "rect", width: 4, height: 10, color: "yellow" });\n}',
830
- codeValidation: { containsAll: ['isKeyPressed', 'Space', 'createEntity', 'bullet'] },
831
- hint: {
832
- en: 'Use isKeyPressed (not isKeyDown!) for single-shot per press.',
833
- pl: 'Użyj isKeyPressed (nie isKeyDown!) dla jednego strzału na naciśnięcie.',
834
- de: 'Verwende isKeyPressed (nicht isKeyDown!) für einen Schuss pro Tastendruck.',
835
- },
757
+ id: "a",
758
+ text: { en: "They are the same thing", pl: "To to samo", de: "Sie sind dasselbe" }
836
759
  },
837
760
  {
838
- id: 'checkpoint-halfway',
839
- type: 'checkpoint',
840
- title: { en: 'Halfway There!', pl: 'Połowa drogi!', de: 'Halb geschafft!' },
841
- content: {
842
- en: "Great progress! You have a moving ship that can shoot. Let's add enemies next!",
843
- pl: 'Świetne postępy! Masz poruszający się statek, który potrafi strzelać. Dodajmy teraz wrogów!',
844
- de: 'Toller Fortschritt! Du hast ein bewegliches Schiff, das schießen kann. Fügen wir als nächstes Gegner hinzu!',
845
- },
761
+ id: "b",
762
+ text: {
763
+ en: "isKeyDown is continuous, isKeyPressed is single-frame",
764
+ pl: "isKeyDown jest ciągły, isKeyPressed jednoklatkowo",
765
+ de: "isKeyDown ist kontinuierlich, isKeyPressed einmalig"
766
+ }
846
767
  },
847
768
  {
848
- id: 'complete',
849
- type: 'text',
850
- title: {
851
- en: 'Mission Complete!',
852
- pl: 'Misja zakończona!',
853
- de: 'Mission abgeschlossen!',
854
- },
855
- content: {
856
- en: 'You built a Space Shooter!\n\nSkills mastered:\n• Input handling (keyboard)\n• Dynamic entity spawning\n• Entity removal\n• Systems architecture\n\nChallenge: Add a scoring system and display it on screen!',
857
- pl: 'Zbudowałeś Kosmicznego Strzelca!\n\nOpanowane umiejętności:\n• Obsługa sterowania (klawiatura)\n• Dynamiczne tworzenie obiektów\n• Usuwanie obiektów\n• Architektura systemów\n\nWyzwanie: Dodaj system punktacji i wyświetl go na ekranie!',
858
- de: 'Du hast einen Weltraum-Shooter gebaut!\n\nGelernte Fähigkeiten:\n• Eingabeverarbeitung (Tastatur)\n• Dynamisches Entity-Spawning\n• Entity-Entfernung\n• System-Architektur\n\nHerausforderung: Füge ein Punktesystem hinzu und zeige es auf dem Bildschirm an!',
859
- },
860
- tooltipPosition: 'center',
861
- },
862
- ],
769
+ id: "c",
770
+ text: {
771
+ en: "isKeyDown works only with arrows",
772
+ pl: "isKeyDown działa tylko ze strzałkami",
773
+ de: "isKeyDown funktioniert nur mit Pfeiltasten"
774
+ }
775
+ }
776
+ ],
777
+ quizCorrectId: "b"
778
+ },
779
+ {
780
+ id: "shooting",
781
+ type: "code",
782
+ title: { en: "Shoot Bullets!", pl: "Strzelaj pociskami!", de: "Schieße Kugeln!" },
783
+ content: {
784
+ en: "Add shooting! When Space is pressed, create a bullet entity that moves upward.",
785
+ pl: "Dodaj strzelanie! Po naciśnięciu Spacji utwórz pocisk, który leci w górę.",
786
+ de: "Füge Schießen hinzu! Bei Leertaste einen Kugel-Entity erstellen, der nach oben fliegt."
787
+ },
788
+ codeTemplate: '// Shooting system\nengine.world.addSystem("Shooting", (world) => {\n const input = engine.input;\n // Your shooting code here\n});\n',
789
+ codeSolution: 'if (input.isKeyPressed("Space")) {\n const bullet = world.createEntity();\n const shipPos = world.getComponent(ship, "Transform");\n world.addComponent(bullet, "Transform", { x: shipPos.x, y: shipPos.y - 20 });\n world.addComponent(bullet, "Velocity", { x: 0, y: -10 });\n world.addComponent(bullet, "Sprite", { shape: "rect", width: 4, height: 10, color: "yellow" });\n}',
790
+ codeValidation: { containsAll: ["isKeyPressed", "Space", "createEntity", "bullet"] },
791
+ hint: {
792
+ en: "Use isKeyPressed (not isKeyDown!) for single-shot per press.",
793
+ pl: "Użyj isKeyPressed (nie isKeyDown!) dla jednego strzału na naciśnięcie.",
794
+ de: "Verwende isKeyPressed (nicht isKeyDown!) für einen Schuss pro Tastendruck."
795
+ }
796
+ },
797
+ {
798
+ id: "checkpoint-halfway",
799
+ type: "checkpoint",
800
+ title: { en: "Halfway There!", pl: "Połowa drogi!", de: "Halb geschafft!" },
801
+ content: {
802
+ en: "Great progress! You have a moving ship that can shoot. Let's add enemies next!",
803
+ pl: "Świetne postępy! Masz poruszający się statek, który potrafi strzelać. Dodajmy teraz wrogów!",
804
+ de: "Toller Fortschritt! Du hast ein bewegliches Schiff, das schießen kann. Fügen wir als nächstes Gegner hinzu!"
805
+ }
806
+ },
807
+ {
808
+ id: "complete",
809
+ type: "text",
810
+ title: {
811
+ en: "Mission Complete!",
812
+ pl: "Misja zakończona!",
813
+ de: "Mission abgeschlossen!"
814
+ },
815
+ content: {
816
+ en: "You built a Space Shooter!\n\nSkills mastered:\n• Input handling (keyboard)\n• Dynamic entity spawning\n• Entity removal\n• Systems architecture\n\nChallenge: Add a scoring system and display it on screen!",
817
+ pl: "Zbudowałeś Kosmicznego Strzelca!\n\nOpanowane umiejętności:\n• Obsługa sterowania (klawiatura)\n• Dynamiczne tworzenie obiektów\n• Usuwanie obiektów\n• Architektura systemów\n\nWyzwanie: Dodaj system punktacji i wyświetl go na ekranie!",
818
+ de: "Du hast einen Weltraum-Shooter gebaut!\n\nGelernte Fähigkeiten:\n• Eingabeverarbeitung (Tastatur)\n• Dynamisches Entity-Spawning\n• Entity-Entfernung\n• System-Architektur\n\nHerausforderung: Füge ein Punktesystem hinzu und zeige es auf dem Bildschirm an!"
819
+ },
820
+ tooltipPosition: "center"
821
+ }
822
+ ]
863
823
  };
864
- /** Tutorial: NiceDataGrid basics (for nice2dev-ui) */
865
824
  const TUTORIAL_DATAGRID_BASICS = {
866
- id: 'ui-datagrid-basics',
867
- title: {
868
- en: 'NiceDataGrid: Master the Grid',
869
- pl: 'NiceDataGrid: Opanuj siatkę',
870
- de: 'NiceDataGrid: Das Grid meistern',
825
+ id: "ui-datagrid-basics",
826
+ title: {
827
+ en: "NiceDataGrid: Master the Grid",
828
+ pl: "NiceDataGrid: Opanuj siatkę",
829
+ de: "NiceDataGrid: Das Grid meistern"
830
+ },
831
+ description: {
832
+ en: "Learn to use NiceDataGrid for sorting, filtering, grouping, and editing data.",
833
+ pl: "Naucz się używać NiceDataGrid do sortowania, filtrowania, grupowania i edycji danych.",
834
+ de: "Lerne NiceDataGrid für Sortieren, Filtern, Gruppieren und Bearbeiten von Daten."
835
+ },
836
+ category: "ui-controls",
837
+ difficulty: "beginner",
838
+ estimatedMinutes: 20,
839
+ kidFriendly: false,
840
+ tags: ["grid", "data", "filtering", "sorting", "nice2dev-ui"],
841
+ steps: [
842
+ {
843
+ id: "intro",
844
+ type: "text",
845
+ title: {
846
+ en: "Welcome to NiceDataGrid",
847
+ pl: "Witaj w NiceDataGrid",
848
+ de: "Willkommen bei NiceDataGrid"
849
+ },
850
+ content: {
851
+ en: "NiceDataGrid is a powerful data table component.\n\nYou will learn:\n• Creating a basic grid\n• Adding columns\n• Sorting & filtering\n• Grouping data\n• Cell editing",
852
+ pl: "NiceDataGrid to potężny komponent tabeli danych.\n\nNauczysz się:\n• Tworzenia podstawowej siatki\n• Dodawania kolumn\n• Sortowania i filtrowania\n• Grupowania danych\n• Edycji komórek",
853
+ de: "NiceDataGrid ist eine leistungsstarke Datentabellen-Komponente.\n\nDu lernst:\n• Ein einfaches Grid erstellen\n• Spalten hinzufügen\n• Sortieren & Filtern\n• Daten gruppieren\n• Zellenbearbeitung"
854
+ }
871
855
  },
872
- description: {
873
- en: 'Learn to use NiceDataGrid for sorting, filtering, grouping, and editing data.',
874
- pl: 'Naucz się używać NiceDataGrid do sortowania, filtrowania, grupowania i edycji danych.',
875
- de: 'Lerne NiceDataGrid für Sortieren, Filtern, Gruppieren und Bearbeiten von Daten.',
856
+ {
857
+ id: "basic-grid",
858
+ type: "code",
859
+ title: { en: "Create a Grid", pl: "Utwórz siatkę", de: "Grid erstellen" },
860
+ content: {
861
+ en: "Create a basic NiceDataGrid with a data source and columns.",
862
+ pl: "Utwórz podstawową NiceDataGrid ze źródłem danych i kolumnami.",
863
+ de: "Erstelle ein einfaches NiceDataGrid mit Datenquelle und Spalten."
864
+ },
865
+ codeTemplate: 'import { NiceDataGrid } from "@nice2dev/ui";\n\n// Create your grid\n',
866
+ codeSolution: '<NiceDataGrid\n dataSource={employees}\n columns={[\n { dataField: "name", caption: "Name" },\n { dataField: "department", caption: "Department" },\n { dataField: "salary", caption: "Salary", dataType: "number" },\n ]}\n sorting={{ enabled: true }}\n filtering={{ enabled: true }}\n/>',
867
+ codeValidation: { containsAll: ["NiceDataGrid", "dataSource", "columns"] }
876
868
  },
877
- category: 'ui-controls',
878
- difficulty: 'beginner',
879
- estimatedMinutes: 20,
880
- kidFriendly: false,
881
- tags: ['grid', 'data', 'filtering', 'sorting', 'nice2dev-ui'],
882
- steps: [
883
- {
884
- id: 'intro',
885
- type: 'text',
886
- title: {
887
- en: 'Welcome to NiceDataGrid',
888
- pl: 'Witaj w NiceDataGrid',
889
- de: 'Willkommen bei NiceDataGrid',
890
- },
891
- content: {
892
- en: 'NiceDataGrid is a powerful data table component.\n\nYou will learn:\n• Creating a basic grid\n• Adding columns\n• Sorting & filtering\n• Grouping data\n• Cell editing',
893
- pl: 'NiceDataGrid to potężny komponent tabeli danych.\n\nNauczysz się:\n• Tworzenia podstawowej siatki\n• Dodawania kolumn\n• Sortowania i filtrowania\n• Grupowania danych\n• Edycji komórek',
894
- de: 'NiceDataGrid ist eine leistungsstarke Datentabellen-Komponente.\n\nDu lernst:\n• Ein einfaches Grid erstellen\n• Spalten hinzufügen\n• Sortieren & Filtern\n• Daten gruppieren\n• Zellenbearbeitung',
895
- },
896
- },
897
- {
898
- id: 'basic-grid',
899
- type: 'code',
900
- title: { en: 'Create a Grid', pl: 'Utwórz siatkę', de: 'Grid erstellen' },
901
- content: {
902
- en: 'Create a basic NiceDataGrid with a data source and columns.',
903
- pl: 'Utwórz podstawową NiceDataGrid ze źródłem danych i kolumnami.',
904
- de: 'Erstelle ein einfaches NiceDataGrid mit Datenquelle und Spalten.',
905
- },
906
- codeTemplate: 'import { NiceDataGrid } from "@nice2dev/ui";\n\n// Create your grid\n',
907
- codeSolution: '<NiceDataGrid\n dataSource={employees}\n columns={[\n { dataField: "name", caption: "Name" },\n { dataField: "department", caption: "Department" },\n { dataField: "salary", caption: "Salary", dataType: "number" },\n ]}\n sorting={{ enabled: true }}\n filtering={{ enabled: true }}\n/>',
908
- codeValidation: { containsAll: ['NiceDataGrid', 'dataSource', 'columns'] },
909
- },
910
- {
911
- id: 'filtering',
912
- type: 'code',
913
- title: { en: 'Add Filtering', pl: 'Dodaj filtrowanie', de: 'Filter hinzufügen' },
914
- content: {
915
- en: 'Enable header filter and filter row for your grid.',
916
- pl: 'Włącz filtr nagłówka i wiersz filtrowania w siatce.',
917
- de: 'Aktiviere Header-Filter und Filter-Zeile für dein Grid.',
918
- },
919
- codeTemplate: '// Add filtering props to your grid\n',
920
- codeSolution: '<NiceDataGrid\n dataSource={employees}\n columns={columns}\n filtering={{ enabled: true, filterRow: true, headerFilter: true }}\n/>',
921
- codeValidation: { containsAll: ['filtering', 'enabled', 'true'] },
922
- },
869
+ {
870
+ id: "filtering",
871
+ type: "code",
872
+ title: { en: "Add Filtering", pl: "Dodaj filtrowanie", de: "Filter hinzufügen" },
873
+ content: {
874
+ en: "Enable header filter and filter row for your grid.",
875
+ pl: "Włącz filtr nagłówka i wiersz filtrowania w siatce.",
876
+ de: "Aktiviere Header-Filter und Filter-Zeile für dein Grid."
877
+ },
878
+ codeTemplate: "// Add filtering props to your grid\n",
879
+ codeSolution: "<NiceDataGrid\n dataSource={employees}\n columns={columns}\n filtering={{ enabled: true, filterRow: true, headerFilter: true }}\n/>",
880
+ codeValidation: { containsAll: ["filtering", "enabled", "true"] }
881
+ },
882
+ {
883
+ id: "quiz-filtering",
884
+ type: "quiz",
885
+ title: { en: "Filtering Quiz", pl: "Quiz o filtrowaniu", de: "Filter-Quiz" },
886
+ content: {
887
+ en: "What is the difference between headerFilter and filterRow?",
888
+ pl: "Jaka jest różnica między headerFilter a filterRow?",
889
+ de: "Was ist der Unterschied zwischen headerFilter und filterRow?"
890
+ },
891
+ quizOptions: [
923
892
  {
924
- id: 'quiz-filtering',
925
- type: 'quiz',
926
- title: { en: 'Filtering Quiz', pl: 'Quiz o filtrowaniu', de: 'Filter-Quiz' },
927
- content: {
928
- en: 'What is the difference between headerFilter and filterRow?',
929
- pl: 'Jaka jest różnica między headerFilter a filterRow?',
930
- de: 'Was ist der Unterschied zwischen headerFilter und filterRow?',
931
- },
932
- quizOptions: [
933
- {
934
- id: 'a',
935
- text: {
936
- en: 'headerFilter shows a dropdown, filterRow shows a text input',
937
- pl: 'headerFilter pokazuje dropdown, filterRow pokazuje pole tekstowe',
938
- de: 'headerFilter zeigt Dropdown, filterRow zeigt Texteingabe',
939
- },
940
- },
941
- { id: 'b', text: { en: 'They are the same', pl: 'Są identyczne', de: 'Sie sind gleich' } },
942
- {
943
- id: 'c',
944
- text: {
945
- en: 'filterRow is faster',
946
- pl: 'filterRow jest szybszy',
947
- de: 'filterRow ist schneller',
948
- },
949
- },
950
- ],
951
- quizCorrectId: 'a',
893
+ id: "a",
894
+ text: {
895
+ en: "headerFilter shows a dropdown, filterRow shows a text input",
896
+ pl: "headerFilter pokazuje dropdown, filterRow pokazuje pole tekstowe",
897
+ de: "headerFilter zeigt Dropdown, filterRow zeigt Texteingabe"
898
+ }
952
899
  },
900
+ { id: "b", text: { en: "They are the same", pl: "Są identyczne", de: "Sie sind gleich" } },
953
901
  {
954
- id: 'complete',
955
- type: 'text',
956
- title: { en: 'Grid Master!', pl: 'Mistrz Siatki!', de: 'Grid-Meister!' },
957
- content: {
958
- en: 'You now know the basics of NiceDataGrid!\n\nSkills:\n• Data binding\n• Column configuration\n• Sorting & filtering\n\nNext: Try grouping, editing, and custom cell templates!',
959
- pl: 'Znasz już podstawy NiceDataGrid!\n\nUmiejętności:\n• Wiązanie danych\n• Konfiguracja kolumn\n• Sortowanie i filtrowanie\n\nDalej: Spróbuj grupowania, edycji i własnych szablonów komórek!',
960
- de: 'Du kennst jetzt die Grundlagen von NiceDataGrid!\n\nFähigkeiten:\n• Datenbindung\n• Spaltenkonfiguration\n• Sortieren & Filtern\n\nWeiter: Probiere Gruppieren, Bearbeiten und benutzerdefinierte Zellenvorlagen!',
961
- },
962
- },
963
- ],
902
+ id: "c",
903
+ text: {
904
+ en: "filterRow is faster",
905
+ pl: "filterRow jest szybszy",
906
+ de: "filterRow ist schneller"
907
+ }
908
+ }
909
+ ],
910
+ quizCorrectId: "a"
911
+ },
912
+ {
913
+ id: "complete",
914
+ type: "text",
915
+ title: { en: "Grid Master!", pl: "Mistrz Siatki!", de: "Grid-Meister!" },
916
+ content: {
917
+ en: "You now know the basics of NiceDataGrid!\n\nSkills:\n• Data binding\n• Column configuration\n• Sorting & filtering\n\nNext: Try grouping, editing, and custom cell templates!",
918
+ pl: "Znasz już podstawy NiceDataGrid!\n\nUmiejętności:\n• Wiązanie danych\n• Konfiguracja kolumn\n• Sortowanie i filtrowanie\n\nDalej: Spróbuj grupowania, edycji i własnych szablonów komórek!",
919
+ de: "Du kennst jetzt die Grundlagen von NiceDataGrid!\n\nFähigkeiten:\n• Datenbindung\n• Spaltenkonfiguration\n• Sortieren & Filtern\n\nWeiter: Probiere Gruppieren, Bearbeiten und benutzerdefinierte Zellenvorlagen!"
920
+ }
921
+ }
922
+ ]
964
923
  };
965
- /** Get all built-in tutorials */
966
924
  function getBuiltInTutorials() {
967
- return [TUTORIAL_FIRST_GAME, TUTORIAL_SPACE_SHOOTER, TUTORIAL_DATAGRID_BASICS];
925
+ return [TUTORIAL_FIRST_GAME, TUTORIAL_SPACE_SHOOTER, TUTORIAL_DATAGRID_BASICS];
968
926
  }
969
-
970
927
  exports.TUTORIAL_DATAGRID_BASICS = TUTORIAL_DATAGRID_BASICS;
971
928
  exports.TUTORIAL_FIRST_GAME = TUTORIAL_FIRST_GAME;
972
929
  exports.TUTORIAL_SPACE_SHOOTER = TUTORIAL_SPACE_SHOOTER;