diff --git a/Extensions/3D/A_RuntimeObject3D.ts b/Extensions/3D/A_RuntimeObject3D.ts
index 0e59e94c4945..fa8741d6740c 100644
--- a/Extensions/3D/A_RuntimeObject3D.ts
+++ b/Extensions/3D/A_RuntimeObject3D.ts
@@ -5,8 +5,6 @@ namespace gdjs {
   type Object3DNetworkSyncDataType = {
     // z is position on the Z axis, different from zo, which is Z order
     z: number;
-    w: number;
-    h: number;
     d: number;
     rx: number;
     ry: number;
@@ -112,12 +110,12 @@ namespace gdjs {
       return true;
     }
 
-    getNetworkSyncData(): Object3DNetworkSyncData {
+    getNetworkSyncData(
+      syncOptions: GetNetworkSyncDataOptions
+    ): Object3DNetworkSyncData {
       return {
-        ...super.getNetworkSyncData(),
+        ...super.getNetworkSyncData(syncOptions),
         z: this.getZ(),
-        w: this.getWidth(),
-        h: this.getHeight(),
         d: this.getDepth(),
         rx: this.getRotationX(),
         ry: this.getRotationY(),
@@ -127,11 +125,12 @@ namespace gdjs {
       };
     }
 
-    updateFromNetworkSyncData(networkSyncData: Object3DNetworkSyncData) {
-      super.updateFromNetworkSyncData(networkSyncData);
+    updateFromNetworkSyncData(
+      networkSyncData: Object3DNetworkSyncData,
+      options: UpdateFromNetworkSyncDataOptions
+    ) {
+      super.updateFromNetworkSyncData(networkSyncData, options);
       if (networkSyncData.z !== undefined) this.setZ(networkSyncData.z);
-      if (networkSyncData.w !== undefined) this.setWidth(networkSyncData.w);
-      if (networkSyncData.h !== undefined) this.setHeight(networkSyncData.h);
       if (networkSyncData.d !== undefined) this.setDepth(networkSyncData.d);
       if (networkSyncData.rx !== undefined)
         this.setRotationX(networkSyncData.rx);
diff --git a/Extensions/3D/Cube3DRuntimeObject.ts b/Extensions/3D/Cube3DRuntimeObject.ts
index 1e4af4c771a9..d94144a59f3c 100644
--- a/Extensions/3D/Cube3DRuntimeObject.ts
+++ b/Extensions/3D/Cube3DRuntimeObject.ts
@@ -434,9 +434,11 @@ namespace gdjs {
       return true;
     }
 
-    getNetworkSyncData(): Cube3DObjectNetworkSyncData {
+    getNetworkSyncData(
+      syncOptions: GetNetworkSyncDataOptions
+    ): Cube3DObjectNetworkSyncData {
       return {
-        ...super.getNetworkSyncData(),
+        ...super.getNetworkSyncData(syncOptions),
         mt: this._materialType,
         fo: this._facesOrientation,
         bfu: this._backFaceUpThroughWhichAxisRotation,
@@ -448,9 +450,10 @@ namespace gdjs {
     }
 
     updateFromNetworkSyncData(
-      networkSyncData: Cube3DObjectNetworkSyncData
+      networkSyncData: Cube3DObjectNetworkSyncData,
+      options: UpdateFromNetworkSyncDataOptions
     ): void {
-      super.updateFromNetworkSyncData(networkSyncData);
+      super.updateFromNetworkSyncData(networkSyncData, options);
 
       if (networkSyncData.mt !== undefined) {
         this._materialType = networkSyncData.mt;
diff --git a/Extensions/3D/CustomRuntimeObject3D.ts b/Extensions/3D/CustomRuntimeObject3D.ts
index 5ac15870af0c..d0ce583871e0 100644
--- a/Extensions/3D/CustomRuntimeObject3D.ts
+++ b/Extensions/3D/CustomRuntimeObject3D.ts
@@ -1,4 +1,12 @@
 namespace gdjs {
+  type Custom3DObjectNetworkSyncDataType = CustomObjectNetworkSyncDataType & {
+    z: float;
+    d: float;
+    rx: float;
+    ry: float;
+    ifz: boolean;
+  };
+
   /**
    * Base class for 3D custom objects.
    */
@@ -78,6 +86,33 @@ namespace gdjs {
       }
     }
 
+    getNetworkSyncData(
+      syncOptions: GetNetworkSyncDataOptions
+    ): Custom3DObjectNetworkSyncDataType {
+      return {
+        ...super.getNetworkSyncData(syncOptions),
+        z: this.getZ(),
+        d: this.getDepth(),
+        rx: this.getRotationX(),
+        ry: this.getRotationY(),
+        ifz: this.isFlippedZ(),
+      };
+    }
+
+    updateFromNetworkSyncData(
+      networkSyncData: Custom3DObjectNetworkSyncDataType,
+      options: UpdateFromNetworkSyncDataOptions
+    ): void {
+      super.updateFromNetworkSyncData(networkSyncData, options);
+      if (networkSyncData.z !== undefined) this.setZ(networkSyncData.z);
+      if (networkSyncData.d !== undefined) this.setDepth(networkSyncData.d);
+      if (networkSyncData.rx !== undefined)
+        this.setRotationX(networkSyncData.rx);
+      if (networkSyncData.ry !== undefined)
+        this.setRotationY(networkSyncData.ry);
+      if (networkSyncData.ifz !== undefined) this.flipZ(networkSyncData.ifz);
+    }
+
     /**
      * Set the object position on the Z axis.
      */
diff --git a/Extensions/3D/Model3DRuntimeObject.ts b/Extensions/3D/Model3DRuntimeObject.ts
index a15404debcb3..eee5d70de028 100644
--- a/Extensions/3D/Model3DRuntimeObject.ts
+++ b/Extensions/3D/Model3DRuntimeObject.ts
@@ -20,6 +20,7 @@ namespace gdjs {
     /** The base parameters of the Model3D object */
     content: Object3DDataContent & {
       modelResourceName: string;
+      depth: number;
       rotationX: number;
       rotationY: number;
       rotationZ: number;
@@ -198,9 +199,11 @@ namespace gdjs {
       return true;
     }
 
-    getNetworkSyncData(): Model3DObjectNetworkSyncData {
+    getNetworkSyncData(
+      syncOptions: GetNetworkSyncDataOptions
+    ): Model3DObjectNetworkSyncData {
       return {
-        ...super.getNetworkSyncData(),
+        ...super.getNetworkSyncData(syncOptions),
         mt: this._materialType,
         op: this._originPoint,
         cp: this._centerPoint,
@@ -209,13 +212,15 @@ namespace gdjs {
         ass: this._animationSpeedScale,
         ap: this._animationPaused,
         cfd: this._crossfadeDuration,
+        d: this.getDepth(),
       };
     }
 
     updateFromNetworkSyncData(
-      networkSyncData: Model3DObjectNetworkSyncData
+      networkSyncData: Model3DObjectNetworkSyncData,
+      options: UpdateFromNetworkSyncDataOptions
     ): void {
-      super.updateFromNetworkSyncData(networkSyncData);
+      super.updateFromNetworkSyncData(networkSyncData, options);
 
       if (networkSyncData.mt !== undefined) {
         this._materialType = networkSyncData.mt;
@@ -243,6 +248,9 @@ namespace gdjs {
       if (networkSyncData.cfd !== undefined) {
         this._crossfadeDuration = networkSyncData.cfd;
       }
+      if (networkSyncData.d !== undefined) {
+        this.setDepth(networkSyncData.d);
+      }
     }
 
     _reloadModel(objectData: Model3DObjectData) {
diff --git a/Extensions/BBText/bbtextruntimeobject.ts b/Extensions/BBText/bbtextruntimeobject.ts
index 6ebfeb53503b..0c1215fd41eb 100644
--- a/Extensions/BBText/bbtextruntimeobject.ts
+++ b/Extensions/BBText/bbtextruntimeobject.ts
@@ -132,9 +132,11 @@ namespace gdjs {
       return true;
     }
 
-    getNetworkSyncData(): BBTextObjectNetworkSyncData {
+    getNetworkSyncData(
+      syncOptions: GetNetworkSyncDataOptions
+    ): BBTextObjectNetworkSyncData {
       return {
-        ...super.getNetworkSyncData(),
+        ...super.getNetworkSyncData(syncOptions),
         text: this._text,
         o: this._opacity,
         c: this._color,
@@ -148,9 +150,10 @@ namespace gdjs {
     }
 
     updateFromNetworkSyncData(
-      networkSyncData: BBTextObjectNetworkSyncData
+      networkSyncData: BBTextObjectNetworkSyncData,
+      options: UpdateFromNetworkSyncDataOptions
     ): void {
-      super.updateFromNetworkSyncData(networkSyncData);
+      super.updateFromNetworkSyncData(networkSyncData, options);
       if (this._text !== undefined) {
         this.setBBText(networkSyncData.text);
       }
diff --git a/Extensions/BitmapText/bitmaptextruntimeobject.ts b/Extensions/BitmapText/bitmaptextruntimeobject.ts
index 45d09c38678c..18d3641770ea 100644
--- a/Extensions/BitmapText/bitmaptextruntimeobject.ts
+++ b/Extensions/BitmapText/bitmaptextruntimeobject.ts
@@ -148,9 +148,11 @@ namespace gdjs {
       return true;
     }
 
-    getNetworkSyncData(): BitmapTextObjectNetworkSyncData {
+    getNetworkSyncData(
+      syncOptions: GetNetworkSyncDataOptions
+    ): BitmapTextObjectNetworkSyncData {
       return {
-        ...super.getNetworkSyncData(),
+        ...super.getNetworkSyncData(syncOptions),
         text: this._text,
         opa: this._opacity,
         tint: this._tint,
@@ -164,9 +166,10 @@ namespace gdjs {
     }
 
     updateFromNetworkSyncData(
-      networkSyncData: BitmapTextObjectNetworkSyncData
+      networkSyncData: BitmapTextObjectNetworkSyncData,
+      options: UpdateFromNetworkSyncDataOptions
     ): void {
-      super.updateFromNetworkSyncData(networkSyncData);
+      super.updateFromNetworkSyncData(networkSyncData, options);
       if (this._text !== undefined) {
         this.setText(networkSyncData.text);
       }
diff --git a/Extensions/Lighting/lightruntimeobject.ts b/Extensions/Lighting/lightruntimeobject.ts
index 60c87973bf76..1c3c08f42ee2 100644
--- a/Extensions/Lighting/lightruntimeobject.ts
+++ b/Extensions/Lighting/lightruntimeobject.ts
@@ -87,16 +87,21 @@ namespace gdjs {
       return true;
     }
 
-    getNetworkSyncData(): LightNetworkSyncData {
+    getNetworkSyncData(
+      syncOptions: GetNetworkSyncDataOptions
+    ): LightNetworkSyncData {
       return {
-        ...super.getNetworkSyncData(),
+        ...super.getNetworkSyncData(syncOptions),
         rad: this.getRadius(),
         col: this.getColor(),
       };
     }
 
-    updateFromNetworkSyncData(networkSyncData: LightNetworkSyncData): void {
-      super.updateFromNetworkSyncData(networkSyncData);
+    updateFromNetworkSyncData(
+      networkSyncData: LightNetworkSyncData,
+      options: UpdateFromNetworkSyncDataOptions
+    ): void {
+      super.updateFromNetworkSyncData(networkSyncData, options);
 
       if (networkSyncData.rad !== undefined) {
         this.setRadius(networkSyncData.rad);
diff --git a/Extensions/Multiplayer/messageManager.ts b/Extensions/Multiplayer/messageManager.ts
index 7a067bdb501b..3ece30ea888b 100644
--- a/Extensions/Multiplayer/messageManager.ts
+++ b/Extensions/Multiplayer/messageManager.ts
@@ -729,7 +729,9 @@ namespace gdjs {
             behavior.playerNumber = ownerPlayerNumber;
           }
 
-          instance.updateFromNetworkSyncData(messageData);
+          instance.updateFromNetworkSyncData(messageData, {
+            forceInputClear: false,
+          });
 
           setLastClockReceivedForInstanceOnScene({
             sceneNetworkId,
diff --git a/Extensions/Multiplayer/multiplayerobjectruntimebehavior.ts b/Extensions/Multiplayer/multiplayerobjectruntimebehavior.ts
index 61394b68122d..9c60eb80b82e 100644
--- a/Extensions/Multiplayer/multiplayerobjectruntimebehavior.ts
+++ b/Extensions/Multiplayer/multiplayerobjectruntimebehavior.ts
@@ -278,7 +278,9 @@ namespace gdjs {
 
       const instanceNetworkId = this._getOrCreateInstanceNetworkId();
       const objectName = this.owner.getName();
-      const objectNetworkSyncData = this.owner.getNetworkSyncData();
+      const objectNetworkSyncData = this.owner.getNetworkSyncData({
+        forceSyncEverything: false,
+      });
 
       // this._logToConsoleWithThrottle(
       //   `Synchronizing object ${this.owner.getName()} (instance ${
@@ -293,6 +295,8 @@ namespace gdjs {
           x: objectNetworkSyncData.x,
           y: objectNetworkSyncData.y,
           z: objectNetworkSyncData.z,
+          w: objectNetworkSyncData.w,
+          h: objectNetworkSyncData.h,
           zo: objectNetworkSyncData.zo,
           a: objectNetworkSyncData.a,
           hid: objectNetworkSyncData.hid,
@@ -300,6 +304,7 @@ namespace gdjs {
           if: objectNetworkSyncData.if,
           pfx: objectNetworkSyncData.pfx,
           pfy: objectNetworkSyncData.pfy,
+          n: objectNetworkSyncData.n,
         });
       const shouldSyncObjectBasicInfo =
         !this._hasObjectBasicInfoBeenSyncedRecently() ||
@@ -369,6 +374,8 @@ namespace gdjs {
         this._lastSentBasicObjectSyncData = {
           x: objectNetworkSyncData.x,
           y: objectNetworkSyncData.y,
+          w: objectNetworkSyncData.w,
+          h: objectNetworkSyncData.h,
           zo: objectNetworkSyncData.zo,
           a: objectNetworkSyncData.a,
           hid: objectNetworkSyncData.hid,
@@ -376,6 +383,7 @@ namespace gdjs {
           if: objectNetworkSyncData.if,
           pfx: objectNetworkSyncData.pfx,
           pfy: objectNetworkSyncData.pfy,
+          n: objectNetworkSyncData.n,
         };
         this._numberOfForcedBasicObjectUpdates = Math.max(
           this._numberOfForcedBasicObjectUpdates - 1,
@@ -443,7 +451,9 @@ namespace gdjs {
           objectOwner: this.playerNumber,
           objectName,
           instanceNetworkId,
-          objectNetworkSyncData: this.owner.getNetworkSyncData(),
+          objectNetworkSyncData: this.owner.getNetworkSyncData({
+            forceSyncEverything: false,
+          }),
           sceneNetworkId,
         });
       this._sendDataToPeersWithIncreasedClock(
@@ -593,7 +603,9 @@ namespace gdjs {
         debugLogger.info(
           'Sending update message to move the object immediately.'
         );
-        const objectNetworkSyncData = this.owner.getNetworkSyncData();
+        const objectNetworkSyncData = this.owner.getNetworkSyncData({
+          forceSyncEverything: false,
+        });
         const {
           messageName: updateMessageName,
           messageData: updateMessageData,
diff --git a/Extensions/PanelSpriteObject/panelspriteruntimeobject.ts b/Extensions/PanelSpriteObject/panelspriteruntimeobject.ts
index 974c4e724d7c..cf0e5e2859da 100644
--- a/Extensions/PanelSpriteObject/panelspriteruntimeobject.ts
+++ b/Extensions/PanelSpriteObject/panelspriteruntimeobject.ts
@@ -25,8 +25,6 @@ namespace gdjs {
   export type PanelSpriteObjectData = ObjectData & PanelSpriteObjectDataType;
 
   export type PanelSpriteNetworkSyncDataType = {
-    wid: number;
-    hei: number;
     op: number;
     color: string;
   };
@@ -86,12 +84,6 @@ namespace gdjs {
       oldObjectData: PanelSpriteObjectData,
       newObjectData: PanelSpriteObjectData
     ): boolean {
-      if (oldObjectData.width !== newObjectData.width) {
-        this.setWidth(newObjectData.width);
-      }
-      if (oldObjectData.height !== newObjectData.height) {
-        this.setHeight(newObjectData.height);
-      }
       let updateTexture = false;
       if (oldObjectData.rightMargin !== newObjectData.rightMargin) {
         this._rBorder = newObjectData.rightMargin;
@@ -121,29 +113,24 @@ namespace gdjs {
       return true;
     }
 
-    getNetworkSyncData(): PanelSpriteNetworkSyncData {
+    getNetworkSyncData(
+      syncOptions: GetNetworkSyncDataOptions
+    ): PanelSpriteNetworkSyncData {
       return {
-        ...super.getNetworkSyncData(),
-        wid: this.getWidth(),
-        hei: this.getHeight(),
+        ...super.getNetworkSyncData(syncOptions),
         op: this.getOpacity(),
         color: this.getColor(),
       };
     }
 
     updateFromNetworkSyncData(
-      networkSyncData: PanelSpriteNetworkSyncData
+      networkSyncData: PanelSpriteNetworkSyncData,
+      options: UpdateFromNetworkSyncDataOptions
     ): void {
-      super.updateFromNetworkSyncData(networkSyncData);
+      super.updateFromNetworkSyncData(networkSyncData, options);
 
       // Texture is not synchronized, see if this is asked or not.
 
-      if (networkSyncData.wid !== undefined) {
-        this.setWidth(networkSyncData.wid);
-      }
-      if (networkSyncData.hei !== undefined) {
-        this.setHeight(networkSyncData.hei);
-      }
       if (networkSyncData.op !== undefined) {
         this.setOpacity(networkSyncData.op);
       }
diff --git a/Extensions/ParticleSystem/particleemitterobject.ts b/Extensions/ParticleSystem/particleemitterobject.ts
index f174369f6747..5182676dfab6 100644
--- a/Extensions/ParticleSystem/particleemitterobject.ts
+++ b/Extensions/ParticleSystem/particleemitterobject.ts
@@ -370,9 +370,11 @@ namespace gdjs {
       return true;
     }
 
-    getNetworkSyncData(): ParticleEmitterObjectNetworkSyncData {
+    getNetworkSyncData(
+      syncOptions: GetNetworkSyncDataOptions
+    ): ParticleEmitterObjectNetworkSyncData {
       return {
-        ...super.getNetworkSyncData(),
+        ...super.getNetworkSyncData(syncOptions),
         prms: this.particleRotationMinSpeed,
         prmx: this.particleRotationMaxSpeed,
         mpc: this.maxParticlesCount,
@@ -399,9 +401,10 @@ namespace gdjs {
     }
 
     updateFromNetworkSyncData(
-      syncData: ParticleEmitterObjectNetworkSyncData
+      syncData: ParticleEmitterObjectNetworkSyncData,
+      options: UpdateFromNetworkSyncDataOptions
     ): void {
-      super.updateFromNetworkSyncData(syncData);
+      super.updateFromNetworkSyncData(syncData, options);
       if (syncData.x !== undefined) {
         this.setX(syncData.x);
       }
diff --git a/Extensions/PathfindingBehavior/pathfindingruntimebehavior.ts b/Extensions/PathfindingBehavior/pathfindingruntimebehavior.ts
index 3a7b646384da..19c8916ffad7 100644
--- a/Extensions/PathfindingBehavior/pathfindingruntimebehavior.ts
+++ b/Extensions/PathfindingBehavior/pathfindingruntimebehavior.ts
@@ -150,9 +150,10 @@ namespace gdjs {
     }
 
     updateFromNetworkSyncData(
-      networkSyncData: PathfindingNetworkSyncData
+      networkSyncData: PathfindingNetworkSyncData,
+      options?: UpdateFromNetworkSyncDataOptions
     ): void {
-      super.updateFromNetworkSyncData(networkSyncData);
+      super.updateFromNetworkSyncData(networkSyncData, options);
       const behaviorSpecificProps = networkSyncData.props;
       if (behaviorSpecificProps.path !== undefined) {
         this._path = behaviorSpecificProps.path;
diff --git a/Extensions/Physics2Behavior/physics2runtimebehavior.ts b/Extensions/Physics2Behavior/physics2runtimebehavior.ts
index 266109ce5763..ccaf1feafe29 100644
--- a/Extensions/Physics2Behavior/physics2runtimebehavior.ts
+++ b/Extensions/Physics2Behavior/physics2runtimebehavior.ts
@@ -529,8 +529,11 @@ namespace gdjs {
       };
     }
 
-    updateFromNetworkSyncData(networkSyncData: Physics2NetworkSyncData) {
-      super.updateFromNetworkSyncData(networkSyncData);
+    updateFromNetworkSyncData(
+      networkSyncData: Physics2NetworkSyncData,
+      options?: UpdateFromNetworkSyncDataOptions
+    ) {
+      super.updateFromNetworkSyncData(networkSyncData, options);
 
       const behaviorSpecificProps = networkSyncData.props;
       if (
diff --git a/Extensions/Physics3DBehavior/Physics3DRuntimeBehavior.ts b/Extensions/Physics3DBehavior/Physics3DRuntimeBehavior.ts
index 1b0bc2ff7c73..a8b91c1b46e8 100644
--- a/Extensions/Physics3DBehavior/Physics3DRuntimeBehavior.ts
+++ b/Extensions/Physics3DBehavior/Physics3DRuntimeBehavior.ts
@@ -528,8 +528,11 @@ namespace gdjs {
       };
     }
 
-    updateFromNetworkSyncData(networkSyncData: Physics3DNetworkSyncData) {
-      super.updateFromNetworkSyncData(networkSyncData);
+    updateFromNetworkSyncData(
+      networkSyncData: Physics3DNetworkSyncData,
+      options?: UpdateFromNetworkSyncDataOptions
+    ) {
+      super.updateFromNetworkSyncData(networkSyncData, options);
 
       const behaviorSpecificProps = networkSyncData.props;
       if (
diff --git a/Extensions/Physics3DBehavior/PhysicsCharacter3DRuntimeBehavior.ts b/Extensions/Physics3DBehavior/PhysicsCharacter3DRuntimeBehavior.ts
index 915c38898769..870cc2d0160b 100644
--- a/Extensions/Physics3DBehavior/PhysicsCharacter3DRuntimeBehavior.ts
+++ b/Extensions/Physics3DBehavior/PhysicsCharacter3DRuntimeBehavior.ts
@@ -303,9 +303,10 @@ namespace gdjs {
     }
 
     updateFromNetworkSyncData(
-      networkSyncData: PhysicsCharacter3DNetworkSyncData
+      networkSyncData: PhysicsCharacter3DNetworkSyncData,
+      options?: UpdateFromNetworkSyncDataOptions
     ) {
-      super.updateFromNetworkSyncData(networkSyncData);
+      super.updateFromNetworkSyncData(networkSyncData, options);
 
       const behaviorSpecificProps = networkSyncData.props;
       this._forwardAngle = behaviorSpecificProps.fwa;
@@ -325,8 +326,12 @@ namespace gdjs {
       this._timeSinceCurrentJumpStart = behaviorSpecificProps.tscjs;
       this._jumpKeyHeldSinceJumpStart = behaviorSpecificProps.jkhsjs;
 
-      // When the object is synchronized from the network, the inputs must not be cleared.
-      this._dontClearInputsBetweenFrames = true;
+      // When the object is synchronized from the network, the inputs must not be cleared UNLESS we have to force clear them for loading a savestate.
+      if (!options || !options.forceInputClear) {
+        this._dontClearInputsBetweenFrames = true;
+      } else {
+        this._dontClearInputsBetweenFrames = false;
+      }
     }
 
     getPhysicsPosition(result: Jolt.RVec3): Jolt.RVec3 {
diff --git a/Extensions/PlatformBehavior/platformerobjectruntimebehavior.ts b/Extensions/PlatformBehavior/platformerobjectruntimebehavior.ts
index d08f327afec4..a1f81006d312 100644
--- a/Extensions/PlatformBehavior/platformerobjectruntimebehavior.ts
+++ b/Extensions/PlatformBehavior/platformerobjectruntimebehavior.ts
@@ -221,14 +221,16 @@ namespace gdjs {
       this._state = this._falling;
     }
 
-    getNetworkSyncData(): PlatformerObjectNetworkSyncData {
+    getNetworkSyncData(
+      syncOptions: GetNetworkSyncDataOptions
+    ): PlatformerObjectNetworkSyncData {
       // This method is called, so we are synchronizing this object.
       // Let's clear the inputs between frames as we control it.
       this._dontClearInputsBetweenFrames = false;
       this._ignoreDefaultControlsAsSyncedByNetwork = false;
 
       return {
-        ...super.getNetworkSyncData(),
+        ...super.getNetworkSyncData(syncOptions),
         props: {
           cs: this._currentSpeed,
 
@@ -256,9 +258,10 @@ namespace gdjs {
     }
 
     updateFromNetworkSyncData(
-      networkSyncData: PlatformerObjectNetworkSyncData
+      networkSyncData: PlatformerObjectNetworkSyncData,
+      options?: UpdateFromNetworkSyncDataOptions
     ) {
-      super.updateFromNetworkSyncData(networkSyncData);
+      super.updateFromNetworkSyncData(networkSyncData, options);
 
       const behaviorSpecificProps = networkSyncData.props;
       if (behaviorSpecificProps.cs !== this._currentSpeed) {
@@ -336,8 +339,12 @@ namespace gdjs {
         this._state.updateFromNetworkSyncData(behaviorSpecificProps.ssd);
       }
 
-      // When the object is synchronized from the network, the inputs must not be cleared.
-      this._dontClearInputsBetweenFrames = true;
+      // When the object is synchronized from the network, the inputs must not be cleared UNLESS we have to force clear them for loading a savestate.
+      if (!options || !options.forceInputClear) {
+        this._dontClearInputsBetweenFrames = true;
+      } else {
+        this._dontClearInputsBetweenFrames = false;
+      }
       // And we are not using the default controls.
       this._ignoreDefaultControlsAsSyncedByNetwork = true;
     }
diff --git a/Extensions/SaveState/JsExtension.js b/Extensions/SaveState/JsExtension.js
new file mode 100644
index 000000000000..7d9db396a2f7
--- /dev/null
+++ b/Extensions/SaveState/JsExtension.js
@@ -0,0 +1,90 @@
+//@ts-check
+/// <reference path="../JsExtensionTypes.d.ts" />
+/**
+ * This is a declaration of an extension for GDevelop 5.
+ *
+ * ℹ️ Changes in this file are watched and automatically imported if the editor
+ * is running. You can also manually run `node import-GDJS-Runtime.js` (in newIDE/app/scripts).
+ *
+ * The file must be named "JsExtension.js", otherwise GDevelop won't load it.
+ * ⚠️ If you make a change and the extension is not loaded, open the developer console
+ * and search for any errors.
+ *
+ * More information on https://github.com/4ian/GDevelop/blob/master/newIDE/README-extensions.md
+ */
+
+/** @type {ExtensionModule} */
+module.exports = {
+  createExtension: function (_, gd) {
+    const extension = new gd.PlatformExtension();
+    extension
+      .setExtensionInformation(
+        'SaveState',
+        _('Save State'),
+
+        'This allows to save and load whole games.',
+        'Neyl Mahfouf',
+        'Gdevelop'
+      )
+      .setExtensionHelpPath('/all-features/save-state')
+      .setCategory('Save & Load');
+    extension
+      .addInstructionOrExpressionGroupMetadata(_('Save State'))
+      .setIcon('JsPlatform/Extensions/snapshotsave.svg');
+    extension
+      .addAction(
+        'SaveGame',
+        _('Save the whole game'),
+        _('Save the whole game to a scene variable or storage name.'),
+        _('Save the game to _PARAM1_ (scene variable) or to storage _PARAM2_.'),
+        '',
+        'res/actions/Save-single-action-down.svg',
+        'res/actions/Save-single-action-down.svg'
+      )
+      .addCodeOnlyParameter('currentScene', '')
+      .addParameter(
+        'scenevar',
+        _('Scene variable to store the save (optional)'),
+        '',
+        true
+      )
+      .addParameter('string', _('Storage name to save to (optional)'), '', true)
+      .getCodeExtraInformation()
+      .setIncludeFile('Extensions/SaveState/savestatetools.js')
+      .setFunctionName('gdjs.saveState.saveGamSnapshot');
+
+    extension
+      .addAction(
+        'LoadGame',
+        _('Load game save.'),
+        _('Load a game save from a scene variable or storage.'),
+        _(
+          'Load the game save from _PARAM1_ (scene variable) or storage _PARAM2_.'
+        ),
+        '',
+        'res/actions/Save-single-action-up.svg',
+        'res/actions/Save-single-action-up.svg'
+      )
+      .addCodeOnlyParameter('currentScene', '')
+      .addParameter(
+        'scenevar',
+        _('Scene variable to load from (optional)'),
+        '',
+        true
+      )
+      .addParameter(
+        'string',
+        _('Storage name to load from (optional)'),
+        '',
+        true
+      )
+      .getCodeExtraInformation()
+      .setIncludeFile('Extensions/SaveState/savestatetools.js')
+      .setFunctionName('gdjs.saveState.loadGameFromSnapshot');
+
+    return extension;
+  },
+  runExtensionSanityTests: function (gd, extension) {
+    return [];
+  },
+};
diff --git a/Extensions/SaveState/savestatetools.ts b/Extensions/SaveState/savestatetools.ts
new file mode 100644
index 000000000000..bc1687bbe821
--- /dev/null
+++ b/Extensions/SaveState/savestatetools.ts
@@ -0,0 +1,78 @@
+namespace gdjs {
+  export namespace saveState {
+    export const INDEXED_DB_NAME: string = 'gameSaveDB';
+    export const INDEXED_DB_KEY: string = 'game_save';
+    export const INDEXED_DB_OBJECT_STORE: string = 'saves';
+
+    export const saveGamSnapshot = async function (
+      currentScene: RuntimeScene,
+      sceneVar?: gdjs.Variable,
+      storageName?: string
+    ) {
+      let allSyncData: GameSaveState = {
+        gameNetworkSyncData: {},
+        layoutNetworkSyncDatas: [],
+      };
+      const gameData = currentScene
+        .getGame()
+        .getNetworkSyncData({ forceSyncEverything: true });
+      const sceneStack = currentScene.getGame()._sceneStack._stack;
+      if (gameData) {
+        allSyncData.gameNetworkSyncData = gameData;
+      }
+      if (sceneStack) {
+        sceneStack.forEach((scene, index) => {
+          const sceneDatas = scene.getNetworkSyncData({
+            forceSyncEverything: true,
+          });
+
+          if (sceneDatas) {
+            allSyncData.layoutNetworkSyncDatas[index] = {
+              sceneData: {} as LayoutNetworkSyncData,
+              objectDatas: {},
+            };
+            allSyncData.layoutNetworkSyncDatas[index].sceneData = sceneDatas;
+            const sceneRuntimeObjects = scene.getAdhocListOfAllInstances();
+            const syncOptions: GetNetworkSyncDataOptions = {
+              forceSyncEverything: true,
+            };
+            for (const key in sceneRuntimeObjects) {
+              if (sceneRuntimeObjects.hasOwnProperty(key)) {
+                const object = sceneRuntimeObjects[key];
+                const syncData = object.getNetworkSyncData(syncOptions);
+                allSyncData.layoutNetworkSyncDatas[index].objectDatas[
+                  object.id
+                ] = syncData;
+              }
+            }
+          }
+        });
+      }
+      const syncDataJson = JSON.stringify(allSyncData);
+      if (sceneVar && sceneVar !== gdjs.VariablesContainer.badVariable) {
+        sceneVar.fromJSObject(JSON.parse(syncDataJson));
+      } else {
+        await gdjs.saveToIndexedDB(
+          INDEXED_DB_NAME,
+          INDEXED_DB_OBJECT_STORE,
+          storageName || INDEXED_DB_KEY,
+          syncDataJson
+        );
+      }
+    };
+
+    export const loadGameFromSnapshot = async function (
+      currentScene: RuntimeScene,
+      sceneVar?: gdjs.Variable,
+      storageName?: string
+    ) {
+      currentScene.requestLoadSnapshot({
+        loadVariable:
+          sceneVar && sceneVar !== gdjs.VariablesContainer.badVariable
+            ? sceneVar
+            : null,
+        loadStorageName: storageName || INDEXED_DB_KEY,
+      });
+    };
+  }
+}
diff --git a/Extensions/Spine/spineruntimeobject.ts b/Extensions/Spine/spineruntimeobject.ts
index f9575e0b7f29..7332b2390d62 100644
--- a/Extensions/Spine/spineruntimeobject.ts
+++ b/Extensions/Spine/spineruntimeobject.ts
@@ -113,9 +113,11 @@ namespace gdjs {
       return true;
     }
 
-    getNetworkSyncData(): SpineNetworkSyncData {
+    getNetworkSyncData(
+      syncOptions: GetNetworkSyncDataOptions
+    ): SpineNetworkSyncData {
       return {
-        ...super.getNetworkSyncData(),
+        ...super.getNetworkSyncData(syncOptions),
         opa: this._opacity,
         wid: this.getWidth(),
         hei: this.getHeight(),
@@ -131,8 +133,11 @@ namespace gdjs {
       };
     }
 
-    updateFromNetworkSyncData(syncData: SpineNetworkSyncData): void {
-      super.updateFromNetworkSyncData(syncData);
+    updateFromNetworkSyncData(
+      syncData: SpineNetworkSyncData,
+      options: UpdateFromNetworkSyncDataOptions
+    ): void {
+      super.updateFromNetworkSyncData(syncData, options);
 
       if (syncData.opa !== undefined && syncData.opa !== this._opacity) {
         this.setOpacity(syncData.opa);
diff --git a/Extensions/TextInput/textinputruntimeobject.ts b/Extensions/TextInput/textinputruntimeobject.ts
index 5aaa9c1bf6c8..4050ae883895 100644
--- a/Extensions/TextInput/textinputruntimeobject.ts
+++ b/Extensions/TextInput/textinputruntimeobject.ts
@@ -256,9 +256,11 @@ namespace gdjs {
       return true;
     }
 
-    getNetworkSyncData(): TextInputNetworkSyncData {
+    getNetworkSyncData(
+      syncOptions: GetNetworkSyncDataOptions
+    ): TextInputNetworkSyncData {
       return {
-        ...super.getNetworkSyncData(),
+        ...super.getNetworkSyncData(syncOptions),
         opa: this.getOpacity(),
         wid: this.getWidth(),
         hei: this.getHeight(),
@@ -278,8 +280,11 @@ namespace gdjs {
       };
     }
 
-    updateFromNetworkSyncData(syncData: TextInputNetworkSyncData): void {
-      super.updateFromNetworkSyncData(syncData);
+    updateFromNetworkSyncData(
+      syncData: TextInputNetworkSyncData,
+      options: UpdateFromNetworkSyncDataOptions
+    ): void {
+      super.updateFromNetworkSyncData(syncData, options);
 
       if (syncData.opa !== undefined) this.setOpacity(syncData.opa);
       if (syncData.wid !== undefined) this.setWidth(syncData.wid);
diff --git a/Extensions/TextObject/textruntimeobject.ts b/Extensions/TextObject/textruntimeobject.ts
index abd6ea72d914..1bcc21ef1d85 100644
--- a/Extensions/TextObject/textruntimeobject.ts
+++ b/Extensions/TextObject/textruntimeobject.ts
@@ -214,9 +214,11 @@ namespace gdjs {
       return true;
     }
 
-    getNetworkSyncData(): TextObjectNetworkSyncData {
+    getNetworkSyncData(
+      syncOptions: GetNetworkSyncDataOptions
+    ): TextObjectNetworkSyncData {
       return {
-        ...super.getNetworkSyncData(),
+        ...super.getNetworkSyncData(syncOptions),
         str: this._str,
         o: this.opacity,
         cs: this._characterSize,
@@ -243,9 +245,10 @@ namespace gdjs {
     }
 
     updateFromNetworkSyncData(
-      networkSyncData: TextObjectNetworkSyncData
+      networkSyncData: TextObjectNetworkSyncData,
+      options: UpdateFromNetworkSyncDataOptions
     ): void {
-      super.updateFromNetworkSyncData(networkSyncData);
+      super.updateFromNetworkSyncData(networkSyncData, options);
       if (networkSyncData.str !== undefined) {
         this.setText(networkSyncData.str);
       }
diff --git a/Extensions/TileMap/simpletilemapruntimeobject.ts b/Extensions/TileMap/simpletilemapruntimeobject.ts
index 589301bf6e14..ae3bda1c5a8e 100644
--- a/Extensions/TileMap/simpletilemapruntimeobject.ts
+++ b/Extensions/TileMap/simpletilemapruntimeobject.ts
@@ -17,8 +17,6 @@ namespace gdjs {
   export type SimpleTileMapNetworkSyncDataType = {
     op: number;
     ai: string;
-    wid: number;
-    hei: number;
     // TODO: Support tilemap synchronization. Find an efficient way to send tiles changes.
   };
 
@@ -165,20 +163,21 @@ namespace gdjs {
       return true;
     }
 
-    getNetworkSyncData(): SimpleTileMapNetworkSyncData {
+    getNetworkSyncData(
+      syncOptions: GetNetworkSyncDataOptions
+    ): SimpleTileMapNetworkSyncData {
       return {
-        ...super.getNetworkSyncData(),
+        ...super.getNetworkSyncData(syncOptions),
         op: this._opacity,
         ai: this._atlasImage,
-        wid: this.getWidth(),
-        hei: this.getHeight(),
       };
     }
 
     updateFromNetworkSyncData(
-      networkSyncData: SimpleTileMapNetworkSyncData
+      networkSyncData: SimpleTileMapNetworkSyncData,
+      options: UpdateFromNetworkSyncDataOptions
     ): void {
-      super.updateFromNetworkSyncData(networkSyncData);
+      super.updateFromNetworkSyncData(networkSyncData, options);
 
       if (
         networkSyncData.op !== undefined &&
@@ -186,18 +185,6 @@ namespace gdjs {
       ) {
         this.setOpacity(networkSyncData.op);
       }
-      if (
-        networkSyncData.wid !== undefined &&
-        networkSyncData.wid !== this.getWidth()
-      ) {
-        this.setWidth(networkSyncData.wid);
-      }
-      if (
-        networkSyncData.hei !== undefined &&
-        networkSyncData.hei !== this.getHeight()
-      ) {
-        this.setHeight(networkSyncData.hei);
-      }
       if (networkSyncData.ai !== undefined) {
         // TODO: support changing the atlas texture
       }
diff --git a/Extensions/TileMap/tilemapcollisionmaskruntimeobject.ts b/Extensions/TileMap/tilemapcollisionmaskruntimeobject.ts
index 25b7613f5a5c..122bc31e3ebe 100644
--- a/Extensions/TileMap/tilemapcollisionmaskruntimeobject.ts
+++ b/Extensions/TileMap/tilemapcollisionmaskruntimeobject.ts
@@ -26,8 +26,6 @@ namespace gdjs {
     os: float;
     fo: float;
     oo: float;
-    wid: float;
-    hei: float;
   };
 
   export type TilemapCollisionMaskNetworkSyncData = ObjectNetworkSyncData &
@@ -191,9 +189,11 @@ namespace gdjs {
       return true;
     }
 
-    getNetworkSyncData(): TilemapCollisionMaskNetworkSyncData {
+    getNetworkSyncData(
+      syncOptions: GetNetworkSyncDataOptions
+    ): TilemapCollisionMaskNetworkSyncData {
       return {
-        ...super.getNetworkSyncData(),
+        ...super.getNetworkSyncData(syncOptions),
         tmjf: this.getTilemapJsonFile(),
         tsjf: this.getTilesetJsonFile(),
         dm: this.getDebugMode(),
@@ -202,15 +202,14 @@ namespace gdjs {
         os: this.getOutlineSize(),
         fo: this.getFillOpacity(),
         oo: this.getOutlineOpacity(),
-        wid: this.getWidth(),
-        hei: this.getHeight(),
       };
     }
 
     updateFromNetworkSyncData(
-      networkSyncData: TilemapCollisionMaskNetworkSyncData
+      networkSyncData: TilemapCollisionMaskNetworkSyncData,
+      options: UpdateFromNetworkSyncDataOptions
     ): void {
-      super.updateFromNetworkSyncData(networkSyncData);
+      super.updateFromNetworkSyncData(networkSyncData, options);
 
       if (networkSyncData.tmjf !== undefined) {
         this.setTilemapJsonFile(networkSyncData.tmjf);
@@ -236,12 +235,6 @@ namespace gdjs {
       if (networkSyncData.oo !== undefined) {
         this.setOutlineOpacity(networkSyncData.oo);
       }
-      if (networkSyncData.wid !== undefined) {
-        this.setWidth(networkSyncData.wid);
-      }
-      if (networkSyncData.hei !== undefined) {
-        this.setHeight(networkSyncData.hei);
-      }
     }
 
     extraInitializationFromInitialInstance(initialInstanceData): void {
diff --git a/Extensions/TileMap/tilemapruntimeobject.ts b/Extensions/TileMap/tilemapruntimeobject.ts
index 4c477800ee05..0deadaecb42b 100644
--- a/Extensions/TileMap/tilemapruntimeobject.ts
+++ b/Extensions/TileMap/tilemapruntimeobject.ts
@@ -25,8 +25,6 @@ namespace gdjs {
     lai: number;
     lei: number;
     asps: number;
-    wid: number;
-    hei: number;
   };
 
   export type TilemapNetworkSyncData = ObjectNetworkSyncData &
@@ -147,9 +145,11 @@ namespace gdjs {
       return true;
     }
 
-    getNetworkSyncData(): TilemapNetworkSyncData {
+    getNetworkSyncData(
+      syncOptions: GetNetworkSyncDataOptions
+    ): TilemapNetworkSyncData {
       return {
-        ...super.getNetworkSyncData(),
+        ...super.getNetworkSyncData(syncOptions),
         op: this._opacity,
         tmjf: this._tilemapJsonFile,
         tsjf: this._tilesetJsonFile,
@@ -158,13 +158,14 @@ namespace gdjs {
         lai: this._layerIndex,
         lei: this._levelIndex,
         asps: this._animationSpeedScale,
-        wid: this.getWidth(),
-        hei: this.getHeight(),
       };
     }
 
-    updateFromNetworkSyncData(networkSyncData: TilemapNetworkSyncData): void {
-      super.updateFromNetworkSyncData(networkSyncData);
+    updateFromNetworkSyncData(
+      networkSyncData: TilemapNetworkSyncData,
+      options: UpdateFromNetworkSyncDataOptions
+    ): void {
+      super.updateFromNetworkSyncData(networkSyncData, options);
 
       if (networkSyncData.op !== undefined) {
         this.setOpacity(networkSyncData.op);
@@ -190,12 +191,6 @@ namespace gdjs {
       if (networkSyncData.asps !== undefined) {
         this.setAnimationSpeedScale(networkSyncData.asps);
       }
-      if (networkSyncData.wid !== undefined) {
-        this.setWidth(networkSyncData.wid);
-      }
-      if (networkSyncData.hei !== undefined) {
-        this.setHeight(networkSyncData.hei);
-      }
     }
 
     extraInitializationFromInitialInstance(initialInstanceData): void {
diff --git a/Extensions/TiledSpriteObject/tiledspriteruntimeobject.ts b/Extensions/TiledSpriteObject/tiledspriteruntimeobject.ts
index bc0499651724..24cbede27ba6 100644
--- a/Extensions/TiledSpriteObject/tiledspriteruntimeobject.ts
+++ b/Extensions/TiledSpriteObject/tiledspriteruntimeobject.ts
@@ -15,8 +15,6 @@ namespace gdjs {
   export type TiledSpriteObjectData = ObjectData & TiledSpriteObjectDataType;
 
   export type TiledSpriteNetworkSyncDataType = {
-    wid: number;
-    hei: number;
     xo: number;
     yo: number;
     op: number;
@@ -80,11 +78,11 @@ namespace gdjs {
       return true;
     }
 
-    getNetworkSyncData(): TiledSpriteNetworkSyncData {
+    getNetworkSyncData(
+      syncOptons: GetNetworkSyncDataOptions
+    ): TiledSpriteNetworkSyncData {
       return {
-        ...super.getNetworkSyncData(),
-        wid: this.getWidth(),
-        hei: this.getHeight(),
+        ...super.getNetworkSyncData(syncOptons),
         xo: this.getXOffset(),
         yo: this.getYOffset(),
         op: this.getOpacity(),
@@ -93,18 +91,13 @@ namespace gdjs {
     }
 
     updateFromNetworkSyncData(
-      networkSyncData: TiledSpriteNetworkSyncData
+      networkSyncData: TiledSpriteNetworkSyncData,
+      options: UpdateFromNetworkSyncDataOptions
     ): void {
-      super.updateFromNetworkSyncData(networkSyncData);
+      super.updateFromNetworkSyncData(networkSyncData, options);
 
       // Texture is not synchronized, see if this is asked or not.
 
-      if (networkSyncData.wid !== undefined) {
-        this.setWidth(networkSyncData.wid);
-      }
-      if (networkSyncData.hei !== undefined) {
-        this.setHeight(networkSyncData.hei);
-      }
       if (networkSyncData.xo !== undefined) {
         this.setXOffset(networkSyncData.xo);
       }
diff --git a/Extensions/TopDownMovementBehavior/topdownmovementruntimebehavior.ts b/Extensions/TopDownMovementBehavior/topdownmovementruntimebehavior.ts
index 045f8bb3e2bd..1653e827efd5 100644
--- a/Extensions/TopDownMovementBehavior/topdownmovementruntimebehavior.ts
+++ b/Extensions/TopDownMovementBehavior/topdownmovementruntimebehavior.ts
@@ -129,9 +129,10 @@ namespace gdjs {
     }
 
     updateFromNetworkSyncData(
-      networkSyncData: TopDownMovementNetworkSyncData
+      networkSyncData: TopDownMovementNetworkSyncData,
+      options?: UpdateFromNetworkSyncDataOptions
     ): void {
-      super.updateFromNetworkSyncData(networkSyncData);
+      super.updateFromNetworkSyncData(networkSyncData, options);
 
       const behaviorSpecificProps = networkSyncData.props;
       if (behaviorSpecificProps.a !== undefined) {
@@ -168,8 +169,12 @@ namespace gdjs {
         this._stickForce = behaviorSpecificProps.sf;
       }
 
-      // When the object is synchronized from the network, the inputs must not be cleared.
-      this._dontClearInputsBetweenFrames = true;
+      // When the object is synchronized from the network, the inputs must not be cleared UNLESS we have to force clear them for loading a savestate.
+      if (!options || !options.forceInputClear) {
+        this._dontClearInputsBetweenFrames = true;
+      } else {
+        this._dontClearInputsBetweenFrames = false;
+      }
       // And we are not using the default controls.
       this._ignoreDefaultControlsAsSyncedByNetwork = true;
     }
diff --git a/Extensions/Video/videoruntimeobject.ts b/Extensions/Video/videoruntimeobject.ts
index 41a7679af457..b18627af496c 100644
--- a/Extensions/Video/videoruntimeobject.ts
+++ b/Extensions/Video/videoruntimeobject.ts
@@ -101,9 +101,11 @@ namespace gdjs {
       return true;
     }
 
-    getNetworkSyncData(): VideoNetworkSyncData {
+    getNetworkSyncData(
+      syncOptions: GetNetworkSyncDataOptions
+    ): VideoNetworkSyncData {
       return {
-        ...super.getNetworkSyncData(),
+        ...super.getNetworkSyncData(syncOptions),
         op: this._opacity,
         wid: this.getWidth(),
         hei: this.getHeight(),
@@ -114,8 +116,11 @@ namespace gdjs {
       };
     }
 
-    updateFromNetworkSyncData(syncData: VideoNetworkSyncData): void {
-      super.updateFromNetworkSyncData(syncData);
+    updateFromNetworkSyncData(
+      syncData: VideoNetworkSyncData,
+      options: UpdateFromNetworkSyncDataOptions
+    ): void {
+      super.updateFromNetworkSyncData(syncData, options);
 
       if (this._opacity !== undefined && this._opacity && syncData.op) {
         this.setOpacity(syncData.op);
diff --git a/GDJS/GDJS/IDE/ExporterHelper.cpp b/GDJS/GDJS/IDE/ExporterHelper.cpp
index 94c01387b749..faf9a1ccf569 100644
--- a/GDJS/GDJS/IDE/ExporterHelper.cpp
+++ b/GDJS/GDJS/IDE/ExporterHelper.cpp
@@ -844,6 +844,7 @@ void ExporterHelper::AddLibsInclude(bool pixiRenderers,
   InsertUnique(includesFiles, "CustomRuntimeObjectInstanceContainer.js");
   InsertUnique(includesFiles, "CustomRuntimeObject.js");
   InsertUnique(includesFiles, "CustomRuntimeObject2D.js");
+  InsertUnique(includesFiles, "indexeddb.js");
 
   // Common includes for events only.
   InsertUnique(includesFiles, "events-tools/commontools.js");
diff --git a/GDJS/Runtime/CustomRuntimeObject.ts b/GDJS/Runtime/CustomRuntimeObject.ts
index 3c597d8ce06c..1da5e855d6ba 100644
--- a/GDJS/Runtime/CustomRuntimeObject.ts
+++ b/GDJS/Runtime/CustomRuntimeObject.ts
@@ -15,6 +15,11 @@ namespace gdjs {
     childrenContent: { [objectName: string]: ObjectConfiguration & any };
   };
 
+  export type CustomObjectNetworkSyncDataType = ObjectNetworkSyncData & {
+    ifx: boolean;
+    ify: boolean;
+  };
+
   /**
    * An object that contains other object.
    *
@@ -151,6 +156,29 @@ namespace gdjs {
       return true;
     }
 
+    getNetworkSyncData(
+      syncOptions: GetNetworkSyncDataOptions
+    ): CustomObjectNetworkSyncDataType {
+      return {
+        ...super.getNetworkSyncData(syncOptions),
+        ifx: this.isFlippedX(),
+        ify: this.isFlippedY(),
+      };
+    }
+
+    updateFromNetworkSyncData(
+      networkSyncData: CustomObjectNetworkSyncDataType,
+      options: UpdateFromNetworkSyncDataOptions
+    ) {
+      super.updateFromNetworkSyncData(networkSyncData, options);
+      if (networkSyncData.ifx !== undefined) {
+        this.flipX(networkSyncData.ifx);
+      }
+      if (networkSyncData.ify !== undefined) {
+        this.flipY(networkSyncData.ify);
+      }
+    }
+
     override extraInitializationFromInitialInstance(
       initialInstanceData: InstanceData
     ) {
diff --git a/GDJS/Runtime/howler-sound-manager/howler-sound-manager.ts b/GDJS/Runtime/howler-sound-manager/howler-sound-manager.ts
index 5435c3b3b4b1..07762b0f8f4d 100644
--- a/GDJS/Runtime/howler-sound-manager/howler-sound-manager.ts
+++ b/GDJS/Runtime/howler-sound-manager/howler-sound-manager.ts
@@ -84,11 +84,31 @@ namespace gdjs {
      */
     private _onPlay: Array<HowlCallback> = [];
 
-    constructor(howl: Howl, volume: float, loop: boolean, rate: float) {
+    /**
+     * The filepath to the resource
+     */
+
+    private _audioResourceName;
+
+    /**
+     * The chanel number on which the sound is played (only necessary for save/loading)
+     */
+    private _channel: float | undefined;
+
+    constructor(
+      howl: Howl,
+      volume: float,
+      loop: boolean,
+      rate: float,
+      audioResourceName: string,
+      channel: float | undefined
+    ) {
       this._howl = howl;
       this._initialVolume = clampVolume(volume);
       this._loop = loop;
       this._rate = rate;
+      this._audioResourceName = audioResourceName;
+      this._channel = channel;
     }
 
     /**
@@ -357,10 +377,21 @@ namespace gdjs {
       if (this._id !== null) this._howl.off(event, handler, this._id);
       return this;
     }
+
+    getNetworkSyncData(): SoundSyncData {
+      return {
+        resourceName: this._audioResourceName,
+        loop: this._loop,
+        volume: this.getVolume(),
+        rate: this._rate,
+        position: this.getSeek(),
+        channel: this._channel || undefined,
+      };
+    }
   }
 
   /**
-   * HowlerSoundManager is used to manage the sounds and musics of a RuntimeScene.
+   * HowlerSoundManager is used to manage the sounds and musics of a RuntimeGame.
    *
    * It is basically a container to associate channels to sounds and keep a list
    * of all sounds being played.
@@ -528,7 +559,8 @@ namespace gdjs {
       isMusic: boolean,
       volume: float,
       loop: boolean,
-      rate: float
+      rate: float,
+      channel?: float
     ): HowlerSound {
       const cacheContainer = isMusic ? this._loadedMusics : this._loadedSounds;
       const resource = this._getAudioResource(soundName);
@@ -554,8 +586,7 @@ namespace gdjs {
         );
         cacheContainer.set(resource, howl);
       }
-
-      return new gdjs.HowlerSound(howl, volume, loop, rate);
+      return new gdjs.HowlerSound(howl, volume, loop, rate, soundName, channel);
     }
 
     /**
@@ -653,7 +684,13 @@ namespace gdjs {
       this._loadedSounds.clear();
     }
 
-    playSound(soundName: string, loop: boolean, volume: float, pitch: float) {
+    playSound(
+      soundName: string,
+      loop: boolean,
+      volume: float,
+      pitch: float,
+      position?: float
+    ) {
       const sound = this.createHowlerSound(
         soundName,
         /* isMusic= */ false,
@@ -669,6 +706,9 @@ namespace gdjs {
         }
       });
       sound.play();
+      if (position) {
+        sound.setSeek(position);
+      }
     }
 
     playSoundOnChannel(
@@ -676,7 +716,8 @@ namespace gdjs {
       channel: integer,
       loop: boolean,
       volume: float,
-      pitch: float
+      pitch: float,
+      position?: float
     ) {
       if (this._sounds[channel]) this._sounds[channel].stop();
 
@@ -685,7 +726,8 @@ namespace gdjs {
         /* isMusic= */ false,
         volume / 100,
         loop,
-        pitch
+        pitch,
+        channel
       );
       const spatialPosition = this._cachedSpatialPosition[channel];
       if (spatialPosition) {
@@ -701,13 +743,22 @@ namespace gdjs {
         }
       });
       sound.play();
+      if (position) {
+        sound.setSeek(position);
+      }
     }
 
     getSoundOnChannel(channel: integer): HowlerSound | null {
       return this._sounds[channel] || null;
     }
 
-    playMusic(soundName: string, loop: boolean, volume: float, pitch: float) {
+    playMusic(
+      soundName: string,
+      loop: boolean,
+      volume: float,
+      pitch: float,
+      position?: float
+    ) {
       const music = this.createHowlerSound(
         soundName,
         /* isMusic= */ true,
@@ -723,6 +774,9 @@ namespace gdjs {
         }
       });
       music.play();
+      if (position) {
+        music.setSeek(position);
+      }
     }
 
     playMusicOnChannel(
@@ -730,7 +784,8 @@ namespace gdjs {
       channel: integer,
       loop: boolean,
       volume: float,
-      pitch: float
+      pitch: float,
+      position?: float
     ) {
       if (this._musics[channel]) this._musics[channel].stop();
 
@@ -739,7 +794,8 @@ namespace gdjs {
         /* isMusic= */ true,
         volume / 100,
         loop,
-        pitch
+        pitch,
+        channel
       );
       // Musics are played with the html5 backend, that is not compatible with spatialization.
       this._musics[channel] = music;
@@ -750,6 +806,9 @@ namespace gdjs {
         }
       });
       music.play();
+      if (position) {
+        music.setSeek(position);
+      }
     }
 
     getMusicOnChannel(channel: integer): HowlerSound | null {
@@ -901,6 +960,91 @@ namespace gdjs {
       }
     }
 
+    getNetworkSyncData(): SoundManagerSyncData {
+      const freeMusicsDatas: SoundSyncData[] = [];
+      for (const sound of Object.values(this._freeMusics)) {
+        freeMusicsDatas.push(sound.getNetworkSyncData());
+      }
+      const freeSoundsDatas: SoundSyncData[] = [];
+      for (const sound of Object.values(this._freeSounds)) {
+        freeSoundsDatas.push(sound.getNetworkSyncData());
+      }
+      const musicsDatas: SoundSyncData[] = [];
+      for (const sound of Object.values(this._musics)) {
+        musicsDatas.push(sound.getNetworkSyncData());
+      }
+      const soundsDatas: SoundSyncData[] = [];
+      for (const sound of Object.values(this._sounds)) {
+        soundsDatas.push(sound.getNetworkSyncData());
+      }
+      return {
+        globalVolume: this._globalVolume,
+        cachedSpatialPosition: this._cachedSpatialPosition,
+        freeMusics: freeMusicsDatas,
+        freeSounds: freeSoundsDatas,
+        musics: musicsDatas,
+        sounds: soundsDatas,
+      };
+    }
+
+    updateFromNetworkSyncData(syncData: SoundManagerSyncData): void {
+      this.clearAll();
+      if (syncData.globalVolume !== undefined) {
+        this._globalVolume = syncData.globalVolume;
+      }
+
+      if (syncData.cachedSpatialPosition !== undefined) {
+        this._cachedSpatialPosition = syncData.cachedSpatialPosition;
+      }
+
+      for (let i = 0; i < syncData.freeSounds.length; i++) {
+        const freeSoundsSyncData: SoundSyncData = syncData.freeSounds[i];
+
+        this.playSound(
+          freeSoundsSyncData.resourceName,
+          freeSoundsSyncData.loop,
+          freeSoundsSyncData.volume * 100,
+          freeSoundsSyncData.rate,
+          freeSoundsSyncData.position
+        );
+      }
+
+      for (let i = 0; i < syncData.freeMusics.length; i++) {
+        const freeMusicsSyncData: SoundSyncData = syncData.freeMusics[i];
+        this.playMusic(
+          freeMusicsSyncData.resourceName,
+          freeMusicsSyncData.loop,
+          freeMusicsSyncData.volume * 100,
+          freeMusicsSyncData.rate,
+          freeMusicsSyncData.position
+        );
+      }
+
+      for (let i = 0; i < syncData.sounds.length; i++) {
+        const soundsSyncData: SoundSyncData = syncData.sounds[i];
+        this.playSoundOnChannel(
+          soundsSyncData.resourceName,
+          soundsSyncData.channel || 0,
+          soundsSyncData.loop,
+          soundsSyncData.volume * 100,
+          soundsSyncData.rate,
+          soundsSyncData.position
+        );
+      }
+
+      for (let i = 0; i < syncData.musics.length; i++) {
+        const musicsSyncData: SoundSyncData = syncData.musics[i];
+        this.playMusicOnChannel(
+          musicsSyncData.resourceName,
+          musicsSyncData.channel || 0,
+          musicsSyncData.loop,
+          musicsSyncData.volume * 100,
+          musicsSyncData.rate,
+          musicsSyncData.position
+        );
+      }
+    }
+
     /**
      * To be called when the game is disposed.
      * Unloads all audio from memory, clear Howl cache and stop all audio.
diff --git a/GDJS/Runtime/indexeddb.ts b/GDJS/Runtime/indexeddb.ts
new file mode 100644
index 000000000000..6cfed664c138
--- /dev/null
+++ b/GDJS/Runtime/indexeddb.ts
@@ -0,0 +1,102 @@
+/*
+ * GDevelop JS Platform
+ * Copyright 2013-2016 Florian Rival (Florian.Rival@gmail.com). All rights reserved.
+ * This project is released under the MIT License.
+ */
+namespace gdjs {
+  export const loadFromIndexedDB = async function (
+    dbName: string,
+    objectStoreName: string,
+    key: string
+  ): Promise<any> {
+    return new Promise((resolve, reject) => {
+      let request: IDBOpenDBRequest;
+
+      try {
+        request = indexedDB.open(dbName, 1);
+      } catch (err) {
+        console.error('Exception thrown while opening IndexedDB:', err);
+        reject(err);
+        return;
+      }
+
+      request.onupgradeneeded = function () {
+        const db = request.result;
+        if (!db.objectStoreNames.contains(objectStoreName)) {
+          db.createObjectStore(objectStoreName);
+        }
+      };
+
+      request.onsuccess = function () {
+        const db = request.result;
+
+        const tx = db.transaction(objectStoreName, 'readonly');
+        const store = tx.objectStore(objectStoreName);
+        const getRequest = store.get(key);
+
+        getRequest.onsuccess = function () {
+          if (getRequest.result !== undefined) {
+            resolve(getRequest.result);
+          } else {
+            resolve(null);
+          }
+        };
+
+        getRequest.onerror = function () {
+          console.error('Error loading game from IndexedDB:', getRequest.error);
+          reject(getRequest.error);
+        };
+      };
+
+      request.onerror = function () {
+        console.error('Error opening IndexedDB:', request.error);
+        reject(request.error);
+      };
+    });
+  };
+
+  export const saveToIndexedDB = async function (
+    dbName: string,
+    objectStoreName: string,
+    key: string,
+    data: any
+  ): Promise<void> {
+    return new Promise((resolve, reject) => {
+      let request: IDBOpenDBRequest;
+      try {
+        request = indexedDB.open(dbName, 1);
+      } catch (err) {
+        console.error('Exception thrown while opening IndexedDB:', err);
+        reject(err);
+        return;
+      }
+
+      request.onupgradeneeded = function (event) {
+        const db = request.result;
+        if (!db.objectStoreNames.contains(objectStoreName)) {
+          db.createObjectStore(objectStoreName);
+        }
+      };
+      request.onsuccess = function () {
+        const db = request.result;
+        const tx = db.transaction(objectStoreName, 'readwrite');
+        const store = tx.objectStore(objectStoreName);
+        const putRequest = store.put(data, key);
+
+        putRequest.onsuccess = function () {
+          resolve();
+        };
+
+        putRequest.onerror = function () {
+          console.error('Error saving game in IndexedDB:', putRequest.error);
+          reject(putRequest.error);
+        };
+      };
+
+      request.onerror = function () {
+        console.error('Error opening IndexedDB:', request.error);
+        reject(request.error);
+      };
+    });
+  };
+}
diff --git a/GDJS/Runtime/runtimebehavior.ts b/GDJS/Runtime/runtimebehavior.ts
index 7f4dd8037b2c..01d0ca9b423c 100644
--- a/GDJS/Runtime/runtimebehavior.ts
+++ b/GDJS/Runtime/runtimebehavior.ts
@@ -77,7 +77,9 @@ namespace gdjs {
       return false;
     }
 
-    getNetworkSyncData(): BehaviorNetworkSyncData {
+    getNetworkSyncData(
+      options?: GetNetworkSyncDataOptions
+    ): BehaviorNetworkSyncData {
       // To be redefined by behaviors that need to synchronize properties
       // while calling super() to get the common properties.
       return {
@@ -90,7 +92,10 @@ namespace gdjs {
      * Update the behavior properties using the provided data.
      * @param networkSyncData The new properties of the behavior.
      */
-    updateFromNetworkSyncData(networkSyncData: BehaviorNetworkSyncData): void {
+    updateFromNetworkSyncData(
+      networkSyncData: BehaviorNetworkSyncData,
+      options?: UpdateFromNetworkSyncDataOptions
+    ): void {
       // Must be redefined by behaviors that need to synchronize properties
       // while calling super() to get the common properties.
       if (networkSyncData.act !== this._activated) {
diff --git a/GDJS/Runtime/runtimegame.ts b/GDJS/Runtime/runtimegame.ts
index 7bb9cceabd01..e80db6207707 100644
--- a/GDJS/Runtime/runtimegame.ts
+++ b/GDJS/Runtime/runtimegame.ts
@@ -1353,8 +1353,8 @@ namespace gdjs {
       const syncData: GameNetworkSyncData = {
         var: this._variables.getNetworkSyncData(syncOptions),
         ss: this._sceneStack.getNetworkSyncData(syncOptions) || undefined,
+        sm: this.getSoundManager().getNetworkSyncData() || undefined,
       };
-
       const extensionsVariablesSyncData = {};
       this._variablesByExtensionName.forEach((variables, extensionName) => {
         const extensionVariablesSyncData =
@@ -1368,6 +1368,7 @@ namespace gdjs {
       syncData.extVar = extensionsVariablesSyncData;
 
       if (
+        !syncOptions.forceSyncEverything &&
         (!syncData.var || syncData.var.length === 0) &&
         !syncData.ss &&
         (!syncData.extVar || Object.keys(syncData.extVar).length === 0)
@@ -1379,14 +1380,20 @@ namespace gdjs {
       return syncData;
     }
 
-    updateFromNetworkSyncData(syncData: GameNetworkSyncData) {
+    updateFromNetworkSyncData(
+      syncData: GameNetworkSyncData,
+      options?: UpdateFromNetworkSyncDataOptions
+    ) {
       this._throwIfDisposed();
       if (syncData.var) {
-        this._variables.updateFromNetworkSyncData(syncData.var);
+        this._variables.updateFromNetworkSyncData(syncData.var, options);
       }
       if (syncData.ss) {
         this._sceneStack.updateFromNetworkSyncData(syncData.ss);
       }
+      if (syncData.sm) {
+        this.getSoundManager().updateFromNetworkSyncData(syncData.sm);
+      }
       if (syncData.extVar) {
         for (const extensionName in syncData.extVar) {
           if (!syncData.extVar.hasOwnProperty(extensionName)) {
@@ -1397,7 +1404,8 @@ namespace gdjs {
             this.getVariablesForExtension(extensionName);
           if (extensionVariables) {
             extensionVariables.updateFromNetworkSyncData(
-              extensionVariablesData
+              extensionVariablesData,
+              options
             );
           }
         }
diff --git a/GDJS/Runtime/runtimeobject.ts b/GDJS/Runtime/runtimeobject.ts
index 17090a3b4178..ac4e6a5ec922 100644
--- a/GDJS/Runtime/runtimeobject.ts
+++ b/GDJS/Runtime/runtimeobject.ts
@@ -454,14 +454,19 @@ namespace gdjs {
      * This can be redefined by objects to send more information.
      * @returns The full network sync data.
      */
-    getNetworkSyncData(): ObjectNetworkSyncData {
+    getNetworkSyncData(
+      syncOptions: GetNetworkSyncDataOptions
+    ): ObjectNetworkSyncData {
       const behaviorNetworkSyncData = {};
       this._behaviors.forEach((behavior) => {
-        if (!behavior.isSyncedOverNetwork()) {
+        if (
+          !behavior.isSyncedOverNetwork() &&
+          (!syncOptions || !syncOptions.forceSyncEverything)
+        ) {
           return;
         }
 
-        const networkSyncData = behavior.getNetworkSyncData();
+        const networkSyncData = behavior.getNetworkSyncData(syncOptions);
         if (networkSyncData) {
           behaviorNetworkSyncData[behavior.getName()] = networkSyncData;
         }
@@ -486,6 +491,8 @@ namespace gdjs {
       return {
         x: this.x,
         y: this.y,
+        w: this.getWidth(),
+        h: this.getHeight(),
         zo: this.zOrder,
         a: this.angle,
         hid: this.hidden,
@@ -493,6 +500,10 @@ namespace gdjs {
         if: this._instantForces.map((force) => force.getNetworkSyncData()),
         pfx: this._permanentForceX,
         pfy: this._permanentForceY,
+        n:
+          syncOptions && syncOptions.forceSyncEverything
+            ? this.name
+            : undefined,
         beh: behaviorNetworkSyncData,
         var: variablesNetworkSyncData,
         eff: effectsNetworkSyncData,
@@ -507,13 +518,22 @@ namespace gdjs {
      * @param networkSyncData The new data for the object.
      * @returns true if the object was updated, false if it could not (i.e: network sync is not supported).
      */
-    updateFromNetworkSyncData(networkSyncData: ObjectNetworkSyncData) {
+    updateFromNetworkSyncData(
+      networkSyncData: ObjectNetworkSyncData,
+      options: UpdateFromNetworkSyncDataOptions
+    ) {
       if (networkSyncData.x !== undefined) {
         this.setX(networkSyncData.x);
       }
       if (networkSyncData.y !== undefined) {
         this.setY(networkSyncData.y);
       }
+      if (networkSyncData.w !== undefined) {
+        this.setWidth(networkSyncData.w);
+      }
+      if (networkSyncData.h !== undefined) {
+        this.setHeight(networkSyncData.h);
+      }
       if (networkSyncData.zo !== undefined) {
         this.setZOrder(networkSyncData.zo);
       }
@@ -561,13 +581,13 @@ namespace gdjs {
         const behaviorNetworkSyncData = networkSyncData.beh[behaviorName];
         const behavior = this.getBehavior(behaviorName);
         if (behavior) {
-          behavior.updateFromNetworkSyncData(behaviorNetworkSyncData);
+          behavior.updateFromNetworkSyncData(behaviorNetworkSyncData, options);
         }
       }
 
       // If variables are synchronized, update them.
       if (networkSyncData.var) {
-        this._variables.updateFromNetworkSyncData(networkSyncData.var);
+        this._variables.updateFromNetworkSyncData(networkSyncData.var, options);
       }
 
       // If effects are synchronized, update them.
diff --git a/GDJS/Runtime/runtimescene.ts b/GDJS/Runtime/runtimescene.ts
index c32de70f2599..5e4f8b85930a 100644
--- a/GDJS/Runtime/runtimescene.ts
+++ b/GDJS/Runtime/runtimescene.ts
@@ -23,6 +23,7 @@ namespace gdjs {
     _timeManager: TimeManager;
     _gameStopRequested: boolean = false;
     _requestedScene: string = '';
+    _loadRequestOptions: LoadRequestOptions | null = null;
     private _asyncTasksManager = new gdjs.AsyncTasksManager();
 
     /** True if loadFromScene was called and the scene is being played. */
@@ -124,7 +125,10 @@ namespace gdjs {
      * @param sceneAndExtensionsData An object containing the scene data.
      * @see gdjs.RuntimeGame#getSceneAndExtensionsData
      */
-    loadFromScene(sceneAndExtensionsData: SceneAndExtensionsData | null) {
+    loadFromScene(
+      sceneAndExtensionsData: SceneAndExtensionsData | null,
+      options?: UpdateFromNetworkSyncDataOptions
+    ) {
       if (!sceneAndExtensionsData) {
         logger.error('loadFromScene was called without a scene');
         return;
@@ -182,14 +186,15 @@ namespace gdjs {
       }
 
       //Create initial instances of objects
-      this.createObjectsFrom(
-        sceneData.instances,
-        0,
-        0,
-        0,
-        /*trackByPersistentUuid=*/
-        true
-      );
+      if (!options || !options.forceInputClear)
+        this.createObjectsFrom(
+          sceneData.instances,
+          0,
+          0,
+          0,
+          /*trackByPersistentUuid=*/
+          true
+        );
 
       // Set up the default z order (for objects created from events)
       this._setLayerDefaultZOrders();
@@ -207,7 +212,11 @@ namespace gdjs {
       for (let i = 0; i < gdjs.callbacksRuntimeSceneLoaded.length; ++i) {
         gdjs.callbacksRuntimeSceneLoaded[i](this);
       }
-      if (sceneData.stopSoundsOnStartup && this._runtimeGame) {
+      if (
+        sceneData.stopSoundsOnStartup &&
+        this._runtimeGame &&
+        (!options || !options.forceInputClear)
+      ) {
         this._runtimeGame.getSoundManager().clearAll();
       }
       this._isLoaded = true;
@@ -649,7 +658,6 @@ namespace gdjs {
     getViewportOriginY(): float {
       return this._cachedGameResolutionHeight / 2;
     }
-
     convertCoords(x: float, y: float, result: FloatPoint): FloatPoint {
       // The result parameter used to be optional.
       const point = result || [0, 0];
@@ -748,6 +756,20 @@ namespace gdjs {
       if (sceneName) this._requestedScene = sceneName;
     }
 
+    requestLoadSnapshot(requestOptions: LoadRequestOptions | null): void {
+      if (requestOptions) {
+        this._loadRequestOptions = {
+          loadVariable: null,
+          loadStorageName: gdjs.saveState.INDEXED_DB_OBJECT_STORE,
+        };
+        this._loadRequestOptions.loadStorageName =
+          requestOptions.loadStorageName;
+        this._loadRequestOptions.loadVariable = requestOptions.loadVariable;
+      } else {
+        this._loadRequestOptions = null;
+      }
+    }
+
     /**
      * Get the profiler associated with the scene, or null if none.
      */
@@ -836,12 +858,16 @@ namespace gdjs {
         var: variablesNetworkSyncData,
         extVar: extensionsVariablesSyncData,
         id: this.getOrCreateNetworkId(),
+        timeManager: this._timeManager.getNetworkSyncData(),
       };
     }
 
-    updateFromNetworkSyncData(syncData: LayoutNetworkSyncData) {
+    updateFromNetworkSyncData(
+      syncData: LayoutNetworkSyncData,
+      options?: UpdateFromNetworkSyncDataOptions
+    ) {
       if (syncData.var) {
-        this._variables.updateFromNetworkSyncData(syncData.var);
+        this._variables.updateFromNetworkSyncData(syncData.var, options);
       }
       if (syncData.extVar) {
         for (const extensionName in syncData.extVar) {
@@ -853,11 +879,15 @@ namespace gdjs {
             this._variablesByExtensionName.get(extensionName);
           if (extensionVariables) {
             extensionVariables.updateFromNetworkSyncData(
-              extensionVariablesData
+              extensionVariablesData,
+              options
             );
           }
         }
       }
+      if (syncData.timeManager && options && options.forceInputClear) {
+        this._timeManager.updateFromNetworkSyncData(syncData.timeManager);
+      }
     }
 
     getOrCreateNetworkId(): string {
@@ -867,6 +897,10 @@ namespace gdjs {
       }
       return this.networkId;
     }
+
+    getLoadRequestOptions(): LoadRequestOptions | null {
+      return this._loadRequestOptions;
+    }
   }
 
   //The flags to describe the change request by a scene:
diff --git a/GDJS/Runtime/scenestack.ts b/GDJS/Runtime/scenestack.ts
index b04bd5976187..d32389367e1c 100644
--- a/GDJS/Runtime/scenestack.ts
+++ b/GDJS/Runtime/scenestack.ts
@@ -67,11 +67,123 @@ namespace gdjs {
           this.replace(currentScene.getRequestedScene());
         } else if (request === gdjs.SceneChangeRequest.CLEAR_SCENES) {
           this.replace(currentScene.getRequestedScene(), true);
-        } else {
-          logger.error('Unrecognized change in scene stack: ' + request);
         }
       }
 
+      const loadRequestionOptions: LoadRequestOptions | null =
+        currentScene.getLoadRequestOptions();
+      if (loadRequestionOptions) {
+        if (
+          loadRequestionOptions.loadVariable &&
+          loadRequestionOptions.loadVariable !==
+            gdjs.VariablesContainer.badVariable
+        ) {
+          try {
+            const allSyncData =
+              loadRequestionOptions.loadVariable.toJSObject() as GameSaveState;
+            currentScene.requestLoadSnapshot(null);
+            if (allSyncData) {
+              const options: UpdateFromNetworkSyncDataOptions = {
+                forceInputClear: true,
+              };
+              currentScene
+                .getGame()
+                .updateFromNetworkSyncData(
+                  allSyncData.gameNetworkSyncData,
+                  options
+                );
+
+              this.applyUpdateFromNetworkSyncDataIfAny(options);
+
+              const sceneStack = this._stack;
+              sceneStack.forEach((scene, index) => {
+                const layoutSyncData =
+                  allSyncData.layoutNetworkSyncDatas[index];
+                if (!layoutSyncData) return;
+                scene.updateFromNetworkSyncData(
+                  layoutSyncData.sceneData,
+                  options
+                );
+
+                const objectDatas = layoutSyncData.objectDatas;
+                for (const id in objectDatas) {
+                  const objectNetworkSyncData = objectDatas[id];
+                  const object = scene.createObject(
+                    objectNetworkSyncData.n || ''
+                  );
+                  if (object) {
+                    object.updateFromNetworkSyncData(
+                      objectNetworkSyncData,
+                      options
+                    );
+                  }
+                }
+              });
+            }
+          } catch (error) {
+            logger.error('Failed to load from variable:', error);
+          }
+        } else {
+          const storageKey =
+            loadRequestionOptions.loadStorageName ||
+            gdjs.saveState.INDEXED_DB_KEY;
+          currentScene.requestLoadSnapshot(null);
+
+          gdjs
+            .loadFromIndexedDB(
+              gdjs.saveState.INDEXED_DB_NAME,
+              gdjs.saveState.INDEXED_DB_OBJECT_STORE,
+              storageKey
+            )
+            .then((jsonData) => {
+              const allSyncData = JSON.parse(jsonData) as GameSaveState;
+
+              if (allSyncData) {
+                const options: UpdateFromNetworkSyncDataOptions = {
+                  forceInputClear: true,
+                };
+
+                currentScene
+                  .getGame()
+                  .updateFromNetworkSyncData(
+                    allSyncData.gameNetworkSyncData,
+                    options
+                  );
+
+                this.applyUpdateFromNetworkSyncDataIfAny(options);
+
+                const sceneStack = this._stack;
+                sceneStack.forEach((scene, index) => {
+                  const layoutSyncData =
+                    allSyncData.layoutNetworkSyncDatas[index];
+                  if (!layoutSyncData) return;
+
+                  scene.updateFromNetworkSyncData(
+                    layoutSyncData.sceneData,
+                    options
+                  );
+
+                  const objectDatas = layoutSyncData.objectDatas;
+                  for (const id in objectDatas) {
+                    const objectNetworkSyncData = objectDatas[id];
+                    const object = scene.createObject(
+                      objectNetworkSyncData.n || ''
+                    );
+                    if (object) {
+                      object.updateFromNetworkSyncData(
+                        objectNetworkSyncData,
+                        options
+                      );
+                    }
+                  }
+                });
+              }
+            })
+            .catch((error) => {
+              logger.error('Error loading from IndexedDB:', error);
+            });
+        }
+      }
       return true;
     }
 
@@ -119,7 +231,8 @@ namespace gdjs {
      */
     push(
       newSceneName: string,
-      externalLayoutName?: string
+      externalLayoutName?: string,
+      options?: UpdateFromNetworkSyncDataOptions
     ): gdjs.RuntimeScene | null {
       this._throwIfDisposed();
 
@@ -132,12 +245,12 @@ namespace gdjs {
       // Avoid a risk of displaying an intermediate loading screen
       // during 1 frame.
       if (this._runtimeGame.areSceneAssetsReady(newSceneName)) {
-        return this._loadNewScene(newSceneName, externalLayoutName);
+        return this._loadNewScene(newSceneName, externalLayoutName, options);
       }
 
       this._isNextLayoutLoading = true;
       this._runtimeGame.loadSceneAssets(newSceneName).then(() => {
-        this._loadNewScene(newSceneName);
+        this._loadNewScene(newSceneName, undefined, options);
         this._isNextLayoutLoading = false;
       });
       return null;
@@ -145,14 +258,15 @@ namespace gdjs {
 
     private _loadNewScene(
       newSceneName: string,
-      externalLayoutName?: string
+      externalLayoutName?: string,
+      options?: UpdateFromNetworkSyncDataOptions
     ): gdjs.RuntimeScene {
       this._throwIfDisposed();
-
       // Load the new one
       const newScene = new gdjs.RuntimeScene(this._runtimeGame);
       newScene.loadFromScene(
-        this._runtimeGame.getSceneAndExtensionsData(newSceneName)
+        this._runtimeGame.getSceneAndExtensionsData(newSceneName),
+        options
       );
       this._wasFirstSceneLoaded = true;
 
@@ -160,7 +274,7 @@ namespace gdjs {
       if (externalLayoutName) {
         const externalLayoutData =
           this._runtimeGame.getExternalLayoutData(externalLayoutName);
-        if (externalLayoutData) {
+        if (externalLayoutData && (!options || !options.forceInputClear)) {
           newScene.createObjectsFrom(
             externalLayoutData.instances,
             0,
@@ -179,8 +293,13 @@ namespace gdjs {
      * Start the specified scene, replacing the one currently being played.
      * If `clear` is set to true, all running scenes are also removed from the stack of scenes.
      */
-    replace(newSceneName: string, clear?: boolean): gdjs.RuntimeScene | null {
+    replace(
+      newSceneName: string,
+      clear?: boolean,
+      options?: UpdateFromNetworkSyncDataOptions
+    ): gdjs.RuntimeScene | null {
       this._throwIfDisposed();
+
       if (!!clear) {
         // Unload all the scenes
         while (this._stack.length !== 0) {
@@ -198,7 +317,7 @@ namespace gdjs {
           }
         }
       }
-      return this.push(newSceneName);
+      return this.push(newSceneName, undefined, options);
     }
 
     /**
@@ -259,7 +378,9 @@ namespace gdjs {
       this._sceneStackSyncDataToApply = sceneStackSyncData;
     }
 
-    applyUpdateFromNetworkSyncDataIfAny(): boolean {
+    applyUpdateFromNetworkSyncDataIfAny(
+      options?: UpdateFromNetworkSyncDataOptions
+    ): boolean {
       this._throwIfDisposed();
       const sceneStackSyncData = this._sceneStackSyncDataToApply;
       let hasMadeChangeToStack = false;
@@ -267,6 +388,23 @@ namespace gdjs {
 
       this._sceneStackSyncDataToApply = null;
 
+      if (options && options.forceInputClear) {
+        while (this._stack.length !== 0) {
+          let scene = this._stack.pop();
+          if (scene) {
+            scene.unloadScene();
+          }
+        }
+        for (let i = 0; i < sceneStackSyncData.length; ++i) {
+          const sceneSyncData = sceneStackSyncData[i];
+          const newScene = this.push(sceneSyncData.name, undefined, options);
+          if (newScene) {
+            newScene.networkId = sceneSyncData.networkId;
+          }
+        }
+        hasMadeChangeToStack = true;
+        return hasMadeChangeToStack;
+      }
       // If this method is called, we are a client.
       // We trust the host to be the source of truth for the scene stack.
       // So we loop through the scenes in the stack given by the host and either:
@@ -276,12 +414,13 @@ namespace gdjs {
       for (let i = 0; i < sceneStackSyncData.length; ++i) {
         const sceneSyncData = sceneStackSyncData[i];
         const sceneAtThisPositionInOurStack = this._stack[i];
+
         if (!sceneAtThisPositionInOurStack) {
           debugLogger.info(
             `Scene at position ${i} with name ${sceneSyncData.name} is missing from the stack, adding it.`
           );
           // We have fewer scenes in the stack than the host, let's add the scene.
-          const newScene = this.push(sceneSyncData.name);
+          const newScene = this.push(sceneSyncData.name, undefined, options);
           if (newScene) {
             newScene.networkId = sceneSyncData.networkId;
           }
@@ -298,9 +437,11 @@ namespace gdjs {
           );
           // The scene does not correspond to the scene at this position in our stack
           // Let's unload everything after this position to recreate the stack.
+
           const newScene = this.replace(
             sceneSyncData.name,
-            true // Clear the stack
+            true, // Clear the stack
+            options
           );
           if (newScene) {
             newScene.networkId = sceneSyncData.networkId;
@@ -343,7 +484,8 @@ namespace gdjs {
           // We need to replace it with a new scene
           const newScene = this.replace(
             sceneSyncData.name,
-            false // Don't clear the stack
+            false, // Don't clear the stack
+            options
           );
           if (newScene) {
             newScene.networkId = sceneSyncData.networkId;
diff --git a/GDJS/Runtime/spriteruntimeobject.ts b/GDJS/Runtime/spriteruntimeobject.ts
index efaf233f5c2e..09aee1701b1b 100644
--- a/GDJS/Runtime/spriteruntimeobject.ts
+++ b/GDJS/Runtime/spriteruntimeobject.ts
@@ -115,9 +115,11 @@ namespace gdjs {
       return true;
     }
 
-    getNetworkSyncData(): SpriteNetworkSyncData {
+    getNetworkSyncData(
+      syncOptions: GetNetworkSyncDataOptions
+    ): SpriteNetworkSyncData {
       return {
-        ...super.getNetworkSyncData(),
+        ...super.getNetworkSyncData(syncOptions),
         anim: this._animator.getNetworkSyncData(),
         ifx: this.isFlippedX(),
         ify: this.isFlippedY(),
@@ -128,8 +130,11 @@ namespace gdjs {
       };
     }
 
-    updateFromNetworkSyncData(newNetworkSyncData: SpriteNetworkSyncData) {
-      super.updateFromNetworkSyncData(newNetworkSyncData);
+    updateFromNetworkSyncData(
+      newNetworkSyncData: SpriteNetworkSyncData,
+      options: UpdateFromNetworkSyncDataOptions
+    ) {
+      super.updateFromNetworkSyncData(newNetworkSyncData, options);
       if (newNetworkSyncData.ifx !== undefined) {
         this.flipX(newNetworkSyncData.ifx);
       }
diff --git a/GDJS/Runtime/timemanager.ts b/GDJS/Runtime/timemanager.ts
index b74f63a7eddd..2c94c41b2ebf 100644
--- a/GDJS/Runtime/timemanager.ts
+++ b/GDJS/Runtime/timemanager.ts
@@ -9,6 +9,15 @@ namespace gdjs {
    * frame, since the beginning of the scene and other time related values.
    * All durations are expressed in milliseconds.
    */
+
+  declare interface TimeManagerSyncData {
+    elapsedTime: float;
+    timeScale: float;
+    timeFromStart: float;
+    firstFrame: boolean;
+    timers: Hashtable<TimerNetworkSyncData>;
+    firstUpdateDone: boolean;
+  }
   export class TimeManager {
     _elapsedTime: float = 0;
     _timeScale: float = 1;
@@ -59,6 +68,47 @@ namespace gdjs {
       }
     }
 
+    getNetworkSyncData(): TimeManagerSyncData {
+      const timerNetworkSyncDatas = new Hashtable<TimerNetworkSyncData>();
+      Object.entries(this._timers.items).forEach(([key, timer]) => {
+        timerNetworkSyncDatas.put(key, timer.getNetworkSyncData());
+      });
+
+      return {
+        elapsedTime: this._elapsedTime,
+        timeScale: this._timeScale,
+        timeFromStart: this._timeFromStart,
+        firstFrame: this._firstFrame,
+        timers: timerNetworkSyncDatas,
+        firstUpdateDone: this._firstUpdateDone,
+      };
+    }
+
+    updateFromNetworkSyncData(syncData: TimeManagerSyncData): void {
+      if (syncData.elapsedTime !== undefined) {
+        this._elapsedTime = syncData.elapsedTime;
+      }
+      if (syncData.timeScale !== undefined) {
+        this._timeScale = syncData.timeScale;
+      }
+      if (syncData.timeFromStart !== undefined) {
+        this._timeFromStart = syncData.timeFromStart;
+      }
+      if (syncData.firstFrame !== undefined) {
+        this._firstFrame = syncData.firstFrame;
+      }
+      if (syncData.timers !== undefined) {
+        Object.entries(syncData.timers.items).forEach(([key, timerData]) => {
+          const newTimer = new gdjs.Timer(timerData.name as string);
+          newTimer.updateFromNetworkSyncData(timerData);
+          this._timers.put(key, newTimer);
+        });
+      }
+
+      if (syncData.firstUpdateDone !== undefined) {
+        this._firstUpdateDone = syncData.firstUpdateDone;
+      }
+    }
     /**
      * Get the time scale.
      * @return The time scale (positive, 1 is normal speed).
diff --git a/GDJS/Runtime/timer.ts b/GDJS/Runtime/timer.ts
index f43e9fe483d9..99d74eb9872d 100644
--- a/GDJS/Runtime/timer.ts
+++ b/GDJS/Runtime/timer.ts
@@ -77,6 +77,7 @@ namespace gdjs {
 
     getNetworkSyncData(): TimerNetworkSyncData {
       return {
+        name: this._name,
         time: this._time,
         paused: this._paused,
       };
diff --git a/GDJS/Runtime/types/project-data.d.ts b/GDJS/Runtime/types/project-data.d.ts
index 8f4947b17911..fe5336b82120 100644
--- a/GDJS/Runtime/types/project-data.d.ts
+++ b/GDJS/Runtime/types/project-data.d.ts
@@ -42,6 +42,11 @@ declare type ObjectData = {
 declare type GetNetworkSyncDataOptions = {
   playerNumber?: number;
   isHost?: boolean;
+  forceSyncEverything?: boolean;
+};
+
+declare type UpdateFromNetworkSyncDataOptions = {
+  forceInputClear?: boolean;
 };
 
 /** Object containing basic properties for all objects synchronizing over the network. */
@@ -52,6 +57,10 @@ declare type BasicObjectNetworkSyncData = {
   y: number;
   /** The position of the instance on the Z axis. Defined only for 3D games */
   z?: number;
+  /** The width of the instance */
+  w: number;
+  /** The height of the instance */
+  h: number;
   /** Z order of the instance */
   zo: number;
   /** The angle of the instance. */
@@ -66,6 +75,8 @@ declare type BasicObjectNetworkSyncData = {
   pfx: number;
   /** Permanent force on Y */
   pfy: number;
+  /* name :*/
+  n?: string;
 };
 
 /**
@@ -98,6 +109,7 @@ declare type ForceNetworkSyncData = {
 };
 
 declare type TimerNetworkSyncData = {
+  name?: string;
   time: float;
   paused: boolean;
 };
@@ -177,6 +189,7 @@ declare interface LayoutNetworkSyncData {
   extVar?: {
     [extensionName: string]: VariableNetworkSyncData[];
   };
+  timeManager?: TimeManagerSyncData;
 }
 
 declare interface SceneStackSceneNetworkSyncData {
@@ -186,12 +199,36 @@ declare interface SceneStackSceneNetworkSyncData {
 
 declare type SceneStackNetworkSyncData = SceneStackSceneNetworkSyncData[];
 
+declare type SoundManagerSyncData = {
+  globalVolume: float;
+  cachedSpatialPosition: Record<integer, [number, number, number]>;
+  freeSounds: SoundSyncData[];
+  freeMusics: SoundSyncData[];
+  musics: SoundSyncData[];
+  sounds: SoundSyncData[];
+};
+
+declare type SoundSyncData = {
+  loop: boolean;
+  volume: float;
+  rate: float;
+  resourceName: string;
+  position: float;
+  channel?: float;
+};
+
+declare type LoadRequestOptions = {
+  loadStorageName: string;
+  loadVariable: gdjs.Variable | null;
+};
+
 declare interface GameNetworkSyncData {
   var?: VariableNetworkSyncData[];
   ss?: SceneStackNetworkSyncData;
   extVar?: {
     [extensionName: string]: VariableNetworkSyncData[];
   };
+  sm?: SoundManagerSyncData;
 }
 
 declare interface EventsFunctionsExtensionData {
diff --git a/GDJS/Runtime/types/save-state.d.ts b/GDJS/Runtime/types/save-state.d.ts
new file mode 100644
index 000000000000..cbab19f0f33c
--- /dev/null
+++ b/GDJS/Runtime/types/save-state.d.ts
@@ -0,0 +1,9 @@
+declare type SceneSaveState = {
+  sceneData: LayoutNetworkSyncData;
+  objectDatas: { [objectId: integer]: ObjectNetworkSyncData };
+};
+
+declare type GameSaveState = {
+  gameNetworkSyncData: GameNetworkSyncData;
+  layoutNetworkSyncDatas: SceneSaveState[];
+};
diff --git a/GDJS/Runtime/variablescontainer.ts b/GDJS/Runtime/variablescontainer.ts
index 20836d3b6c00..1a87cdea9d9f 100644
--- a/GDJS/Runtime/variablescontainer.ts
+++ b/GDJS/Runtime/variablescontainer.ts
@@ -242,8 +242,9 @@ namespace gdjs {
         const variable = this._variables.get(variableName);
         const variableOwner = variable.getPlayerOwnership();
         if (
-          // Variable undefined.
-          variable.isUndefinedInContainer() ||
+          (!syncOptions.forceSyncEverything &&
+            // Variable undefined.
+            variable.isUndefinedInContainer()) ||
           // Variable marked as not to be synchronized.
           variableOwner === null ||
           // Getting sync data for a specific player:
@@ -352,7 +353,10 @@ namespace gdjs {
       return undefined;
     }
 
-    updateFromNetworkSyncData(networkSyncData: VariableNetworkSyncData[]) {
+    updateFromNetworkSyncData(
+      networkSyncData: VariableNetworkSyncData[],
+      options?: UpdateFromNetworkSyncDataOptions
+    ) {
       const that = this;
       for (let j = 0; j < networkSyncData.length; ++j) {
         const variableSyncData = networkSyncData[j];
@@ -370,20 +374,23 @@ namespace gdjs {
         // - If we are not the owner of the variable, then assume that we missed the ownership change message, so update the variable's
         //   ownership and then update the variable.
         const syncedVariableOwner = variableSyncData.owner;
-        const currentPlayerNumber = gdjs.multiplayer.getCurrentPlayerNumber();
-        const currentVariableOwner = variable.getPlayerOwnership();
-        if (currentPlayerNumber === currentVariableOwner) {
-          console.info(
-            `Variable ${variableName} is owned by us ${gdjs.multiplayer.playerNumber}, ignoring update message from ${syncedVariableOwner}.`
-          );
-          return;
-        }
+        if (!options || !options.forceInputClear) {
+          const currentPlayerNumber = gdjs.multiplayer.getCurrentPlayerNumber();
+
+          const currentVariableOwner = variable.getPlayerOwnership();
+          if (currentPlayerNumber === currentVariableOwner) {
+            console.info(
+              `Variable ${variableName} is owned by us ${gdjs.multiplayer.playerNumber}, ignoring update message from ${syncedVariableOwner}.`
+            );
+            return;
+          }
 
-        if (syncedVariableOwner !== currentVariableOwner) {
-          console.info(
-            `Variable ${variableName} is owned by ${currentVariableOwner} on our game, changing ownership to ${syncedVariableOwner} as part of the update event.`
-          );
-          variable.setPlayerOwnership(syncedVariableOwner);
+          if (syncedVariableOwner !== currentVariableOwner) {
+            console.info(
+              `Variable ${variableName} is owned by ${currentVariableOwner} on our game, changing ownership to ${syncedVariableOwner} as part of the update event.`
+            );
+            variable.setPlayerOwnership(syncedVariableOwner);
+          }
         }
 
         variable.reinitialize(variableData);
diff --git a/GDJS/tests/games/savestate/3dCarCoinHuntSaveLoad/3d car coin hunt with save & load.json b/GDJS/tests/games/savestate/3dCarCoinHuntSaveLoad/3d car coin hunt with save & load.json
new file mode 100644
index 000000000000..098e01f91079
--- /dev/null
+++ b/GDJS/tests/games/savestate/3dCarCoinHuntSaveLoad/3d car coin hunt with save & load.json	
@@ -0,0 +1,46540 @@
+{
+  "firstLayout": "Menu",
+  "gdVersion": {
+    "build": 224,
+    "major": 5,
+    "minor": 5,
+    "revision": 0
+  },
+  "properties": {
+    "adaptGameResolutionAtRuntime": true,
+    "antialiasingMode": "MSAA",
+    "antialisingEnabledOnMobile": false,
+    "folderProject": false,
+    "orientation": "landscape",
+    "packageName": "com.example.gamename",
+    "pixelsRounding": false,
+    "projectUuid": "6e9f85d5-e2ff-4f3b-9d52-2fcbd4c1471a",
+    "scaleMode": "linear",
+    "sizeOnStartupMode": "adaptWidth",
+    "templateSlug": "3d-car-coin-hunt",
+    "version": "1.0.0",
+    "name": "3d car coin hunt with save & load",
+    "description": "A top-down 3D racing game with 360° controls where players chase coins that are spread in a city. \nIn this version you can save and load at any time while in game. The goal is to showcase the simplicity of the single action save & load system.",
+    "author": "",
+    "windowWidth": 1280,
+    "windowHeight": 720,
+    "latestCompilationDirectory": "",
+    "maxFPS": 60,
+    "minFPS": 20,
+    "verticalSync": false,
+    "platformSpecificAssets": {
+      "android-icon-144": "android-icon-144.png",
+      "android-icon-192": "android-icon-192.png",
+      "android-icon-36": "android-icon-36.png",
+      "android-icon-48": "android-icon-48.png",
+      "android-icon-72": "android-icon-72.png",
+      "android-icon-96": "android-icon-96.png",
+      "android-windowSplashScreenAnimatedIcon": "android-windowSplashScreenAnimatedIcon.png",
+      "desktop-icon-512": "desktop-icon-512.png",
+      "ios-icon-100": "ios-icon-100.png",
+      "ios-icon-1024": "ios-icon-1024.png",
+      "ios-icon-114": "ios-icon-114.png",
+      "ios-icon-120": "ios-icon-120.png",
+      "ios-icon-144": "ios-icon-144.png",
+      "ios-icon-152": "ios-icon-152.png",
+      "ios-icon-167": "ios-icon-167.png",
+      "ios-icon-180": "ios-icon-180.png",
+      "ios-icon-20": "ios-icon-20.png",
+      "ios-icon-29": "ios-icon-29.png",
+      "ios-icon-40": "ios-icon-40.png",
+      "ios-icon-50": "ios-icon-50.png",
+      "ios-icon-57": "ios-icon-57.png",
+      "ios-icon-58": "ios-icon-58.png",
+      "ios-icon-60": "ios-icon-60.png",
+      "ios-icon-72": "ios-icon-72.png",
+      "ios-icon-76": "ios-icon-76.png",
+      "ios-icon-80": "ios-icon-80.png",
+      "ios-icon-87": "ios-icon-87.png",
+      "liluo-thumbnail": "assets/thumbnail-game.png"
+    },
+    "loadingScreen": {
+      "backgroundColor": 0,
+      "backgroundFadeInDuration": 0.2,
+      "backgroundImageResourceName": "",
+      "gdevelopLogoStyle": "light",
+      "logoAndProgressFadeInDuration": 0.2,
+      "logoAndProgressLogoFadeInDelay": 0.2,
+      "minDuration": 1.5,
+      "progressBarColor": 16777215,
+      "progressBarHeight": 20,
+      "progressBarMaxWidth": 200,
+      "progressBarMinWidth": 40,
+      "progressBarWidthPercent": 30,
+      "showGDevelopSplash": true,
+      "showProgressBar": true
+    },
+    "watermark": {
+      "placement": "bottom-left",
+      "showWatermark": true
+    },
+    "authorIds": [],
+    "authorUsernames": [],
+    "categories": [
+      "racing",
+      "leaderboard"
+    ],
+    "playableDevices": [
+      "keyboard",
+      "mobile"
+    ],
+    "extensionProperties": [],
+    "platforms": [
+      {
+        "name": "GDevelop JS platform"
+      }
+    ],
+    "currentPlatform": "GDevelop JS platform"
+  },
+  "resources": {
+    "resources": [
+      {
+        "file": "assets/Chango-Regular.ttf",
+        "kind": "font",
+        "metadata": "",
+        "name": "27d4da0f7767cf3fbf14eb2c8da758dbcfc7b5038c9214d5e6ed62db6476a6e5_Chango-Regular.ttf",
+        "userAdded": true,
+        "origin": {
+          "identifier": "https://asset-resources.gdevelop.io/public-resources/Open Font License/27d4da0f7767cf3fbf14eb2c8da758dbcfc7b5038c9214d5e6ed62db6476a6e5_Chango-Regular.ttf",
+          "name": "gdevelop-asset-store"
+        }
+      },
+      {
+        "file": "assets/Collision.wav",
+        "kind": "audio",
+        "metadata": "{\"extension\":\".wav\",\"jfxr\":{\"data\":\"{\\\"_version\\\":1,\\\"_name\\\":\\\"Jump 1\\\",\\\"_locked\\\":[],\\\"sampleRate\\\":44100,\\\"attack\\\":0,\\\"sustain\\\":0.07,\\\"sustainPunch\\\":20,\\\"decay\\\":0.14343251891524278,\\\"tremoloDepth\\\":0,\\\"tremoloFrequency\\\":10,\\\"frequency\\\":7700,\\\"frequencySweep\\\":-4600,\\\"frequencyDeltaSweep\\\":-3100,\\\"repeatFrequency\\\":0,\\\"frequencyJump1Onset\\\":33,\\\"frequencyJump1Amount\\\":0,\\\"frequencyJump2Onset\\\":66,\\\"frequencyJump2Amount\\\":0,\\\"harmonics\\\":0,\\\"harmonicsFalloff\\\":0.5,\\\"waveform\\\":\\\"brownnoise\\\",\\\"interpolateNoise\\\":false,\\\"vibratoDepth\\\":0,\\\"vibratoFrequency\\\":10,\\\"squareDuty\\\":50,\\\"squareDutySweep\\\":0,\\\"flangerOffset\\\":0,\\\"flangerOffsetSweep\\\":0,\\\"bitCrush\\\":16,\\\"bitCrushSweep\\\":0,\\\"lowPassCutoff\\\":22050,\\\"lowPassCutoffSweep\\\":0,\\\"highPassCutoff\\\":0,\\\"highPassCutoffSweep\\\":0,\\\"compression\\\":1.7000000000000002,\\\"normalization\\\":true,\\\"amplification\\\":100}\",\"name\":\"Collision\"}}",
+        "name": "Collision",
+        "preloadAsMusic": false,
+        "preloadAsSound": true,
+        "preloadInCache": false,
+        "userAdded": false
+      },
+      {
+        "file": "assets/CarEngine.mp3",
+        "kind": "audio",
+        "metadata": "",
+        "name": "assets\\CarEngine.mp3",
+        "preloadAsMusic": false,
+        "preloadAsSound": true,
+        "preloadInCache": false,
+        "userAdded": true
+      },
+      {
+        "file": "assets/Line light joystick thumb.png",
+        "kind": "image",
+        "metadata": "",
+        "name": "Line light joystick thumb.png",
+        "smoothed": true,
+        "userAdded": false,
+        "origin": {
+          "identifier": "https://asset-resources.gdevelop.io/public-resources/Multitouch joysticks/6e451170154c7ae31c25495ad2d2ac48ba8c8d62e93e90bfcfe3c7e509c9ce66_Line light joystick thumb.png",
+          "name": "Line light joystick thumb.png"
+        }
+      },
+      {
+        "file": "assets/Line light joystick border UpDown.png",
+        "kind": "image",
+        "metadata": "",
+        "name": "assets\\Line light joystick border UpDown.png",
+        "smoothed": true,
+        "userAdded": true
+      },
+      {
+        "file": "assets/Line light joystick border LeftRightt.png",
+        "kind": "image",
+        "metadata": "",
+        "name": "assets\\Line light joystick border LeftRightt.png",
+        "smoothed": true,
+        "userAdded": true
+      },
+      {
+        "file": "assets/CoinPickUP.wav",
+        "kind": "audio",
+        "metadata": "{\"extension\":\".wav\",\"jfxr\":{\"data\":\"{\\\"_version\\\":1,\\\"_name\\\":\\\"Jump 1\\\",\\\"_locked\\\":[],\\\"sampleRate\\\":44100,\\\"attack\\\":0,\\\"sustain\\\":0.02,\\\"sustainPunch\\\":70,\\\"decay\\\":0.1,\\\"tremoloDepth\\\":0,\\\"tremoloFrequency\\\":10,\\\"frequency\\\":700,\\\"frequencySweep\\\":0,\\\"frequencyDeltaSweep\\\":0,\\\"repeatFrequency\\\":0,\\\"frequencyJump1Onset\\\":25,\\\"frequencyJump1Amount\\\":25,\\\"frequencyJump2Onset\\\":66,\\\"frequencyJump2Amount\\\":0,\\\"harmonics\\\":0,\\\"harmonicsFalloff\\\":0.5,\\\"waveform\\\":\\\"whistle\\\",\\\"interpolateNoise\\\":true,\\\"vibratoDepth\\\":0,\\\"vibratoFrequency\\\":10,\\\"squareDuty\\\":70,\\\"squareDutySweep\\\":65,\\\"flangerOffset\\\":0,\\\"flangerOffsetSweep\\\":0,\\\"bitCrush\\\":16,\\\"bitCrushSweep\\\":0,\\\"lowPassCutoff\\\":22050,\\\"lowPassCutoffSweep\\\":0,\\\"highPassCutoff\\\":0,\\\"highPassCutoffSweep\\\":0,\\\"compression\\\":1,\\\"normalization\\\":true,\\\"amplification\\\":100}\",\"name\":\"CoinPickUP\"},\"localFilePath\":\"assets/CoinPickUP.wav\"}",
+        "name": "CoinPickUp",
+        "preloadAsMusic": false,
+        "preloadAsSound": false,
+        "preloadInCache": false,
+        "userAdded": true
+      },
+      {
+        "file": "assets/Large Building A.glb",
+        "kind": "model3D",
+        "metadata": "",
+        "name": "Large Building A.glb",
+        "userAdded": true
+      },
+      {
+        "file": "assets/Sedan Sports.glb",
+        "kind": "model3D",
+        "metadata": "",
+        "name": "Sedan Sports.glb",
+        "userAdded": true
+      },
+      {
+        "file": "assets/Coin.glb",
+        "kind": "model3D",
+        "metadata": "",
+        "name": "Coin.glb",
+        "userAdded": true
+      },
+      {
+        "file": "assets/Suv Luxury5.glb",
+        "kind": "model3D",
+        "metadata": "",
+        "name": "Suv Luxury5.glb",
+        "userAdded": true
+      },
+      {
+        "file": "assets/Large Building F2.glb",
+        "kind": "model3D",
+        "metadata": "",
+        "name": "Large Building F2.glb",
+        "userAdded": true
+      },
+      {
+        "file": "assets/Van2.glb",
+        "kind": "model3D",
+        "metadata": "",
+        "name": "Van2.glb",
+        "userAdded": true
+      },
+      {
+        "file": "assets/desktop-icon-512.png",
+        "kind": "image",
+        "metadata": "",
+        "name": "desktop-icon-512.png",
+        "smoothed": true,
+        "userAdded": true
+      },
+      {
+        "file": "assets/android-icon-192.png",
+        "kind": "image",
+        "metadata": "",
+        "name": "android-icon-192.png",
+        "smoothed": true,
+        "userAdded": true
+      },
+      {
+        "file": "assets/android-icon-144.png",
+        "kind": "image",
+        "metadata": "",
+        "name": "android-icon-144.png",
+        "smoothed": true,
+        "userAdded": true
+      },
+      {
+        "file": "assets/android-icon-96.png",
+        "kind": "image",
+        "metadata": "",
+        "name": "android-icon-96.png",
+        "smoothed": true,
+        "userAdded": true
+      },
+      {
+        "file": "assets/android-icon-72.png",
+        "kind": "image",
+        "metadata": "",
+        "name": "android-icon-72.png",
+        "smoothed": true,
+        "userAdded": true
+      },
+      {
+        "file": "assets/android-icon-48.png",
+        "kind": "image",
+        "metadata": "",
+        "name": "android-icon-48.png",
+        "smoothed": true,
+        "userAdded": true
+      },
+      {
+        "file": "assets/android-icon-36.png",
+        "kind": "image",
+        "metadata": "",
+        "name": "android-icon-36.png",
+        "smoothed": true,
+        "userAdded": true
+      },
+      {
+        "file": "assets/android-windowSplashScreenAnimatedIcon.png",
+        "kind": "image",
+        "metadata": "",
+        "name": "android-windowSplashScreenAnimatedIcon.png",
+        "smoothed": true,
+        "userAdded": true
+      },
+      {
+        "file": "assets/ios-icon-1024.png",
+        "kind": "image",
+        "metadata": "",
+        "name": "ios-icon-1024.png",
+        "smoothed": true,
+        "userAdded": true
+      },
+      {
+        "file": "assets/ios-icon-180.png",
+        "kind": "image",
+        "metadata": "",
+        "name": "ios-icon-180.png",
+        "smoothed": true,
+        "userAdded": true
+      },
+      {
+        "file": "assets/ios-icon-167.png",
+        "kind": "image",
+        "metadata": "",
+        "name": "ios-icon-167.png",
+        "smoothed": true,
+        "userAdded": true
+      },
+      {
+        "file": "assets/ios-icon-152.png",
+        "kind": "image",
+        "metadata": "",
+        "name": "ios-icon-152.png",
+        "smoothed": true,
+        "userAdded": true
+      },
+      {
+        "file": "assets/ios-icon-144.png",
+        "kind": "image",
+        "metadata": "",
+        "name": "ios-icon-144.png",
+        "smoothed": true,
+        "userAdded": true
+      },
+      {
+        "file": "assets/ios-icon-120.png",
+        "kind": "image",
+        "metadata": "",
+        "name": "ios-icon-120.png",
+        "smoothed": true,
+        "userAdded": true
+      },
+      {
+        "file": "assets/ios-icon-114.png",
+        "kind": "image",
+        "metadata": "",
+        "name": "ios-icon-114.png",
+        "smoothed": true,
+        "userAdded": true
+      },
+      {
+        "file": "assets/ios-icon-100.png",
+        "kind": "image",
+        "metadata": "",
+        "name": "ios-icon-100.png",
+        "smoothed": true,
+        "userAdded": true
+      },
+      {
+        "file": "assets/ios-icon-87.png",
+        "kind": "image",
+        "metadata": "",
+        "name": "ios-icon-87.png",
+        "smoothed": true,
+        "userAdded": true
+      },
+      {
+        "file": "assets/ios-icon-80.png",
+        "kind": "image",
+        "metadata": "",
+        "name": "ios-icon-80.png",
+        "smoothed": true,
+        "userAdded": true
+      },
+      {
+        "file": "assets/ios-icon-76.png",
+        "kind": "image",
+        "metadata": "",
+        "name": "ios-icon-76.png",
+        "smoothed": true,
+        "userAdded": true
+      },
+      {
+        "file": "assets/ios-icon-72.png",
+        "kind": "image",
+        "metadata": "",
+        "name": "ios-icon-72.png",
+        "smoothed": true,
+        "userAdded": true
+      },
+      {
+        "file": "assets/ios-icon-60.png",
+        "kind": "image",
+        "metadata": "",
+        "name": "ios-icon-60.png",
+        "smoothed": true,
+        "userAdded": true
+      },
+      {
+        "file": "assets/ios-icon-58.png",
+        "kind": "image",
+        "metadata": "",
+        "name": "ios-icon-58.png",
+        "smoothed": true,
+        "userAdded": true
+      },
+      {
+        "file": "assets/ios-icon-57.png",
+        "kind": "image",
+        "metadata": "",
+        "name": "ios-icon-57.png",
+        "smoothed": true,
+        "userAdded": true
+      },
+      {
+        "file": "assets/ios-icon-50.png",
+        "kind": "image",
+        "metadata": "",
+        "name": "ios-icon-50.png",
+        "smoothed": true,
+        "userAdded": true
+      },
+      {
+        "file": "assets/ios-icon-40.png",
+        "kind": "image",
+        "metadata": "",
+        "name": "ios-icon-40.png",
+        "smoothed": true,
+        "userAdded": true
+      },
+      {
+        "file": "assets/ios-icon-29.png",
+        "kind": "image",
+        "metadata": "",
+        "name": "ios-icon-29.png",
+        "smoothed": true,
+        "userAdded": true
+      },
+      {
+        "file": "assets/ios-icon-20.png",
+        "kind": "image",
+        "metadata": "",
+        "name": "ios-icon-20.png",
+        "smoothed": true,
+        "userAdded": true
+      },
+      {
+        "file": "assets/Grey Button_Idle.png",
+        "kind": "image",
+        "metadata": "",
+        "name": "assets\\Grey Button_Idle.png",
+        "smoothed": true,
+        "userAdded": true
+      },
+      {
+        "file": "assets/Yellow Button_Hovered.png",
+        "kind": "image",
+        "metadata": "",
+        "name": "Yellow Button_Hovered.png",
+        "smoothed": true,
+        "userAdded": false,
+        "origin": {
+          "identifier": "https://asset-resources.gdevelop.io/public-resources/Menu buttons/c1e14acb64b9963341656c997853154dc4c86cd1c46fb12bfbe012d99712ef61_Yellow Button_Hovered.png",
+          "name": "Yellow Button_Hovered.png"
+        }
+      },
+      {
+        "file": "assets/Yellow Button_Idle.png",
+        "kind": "image",
+        "metadata": "",
+        "name": "Yellow Button_Idle.png",
+        "smoothed": true,
+        "userAdded": false,
+        "origin": {
+          "identifier": "https://asset-resources.gdevelop.io/public-resources/Menu buttons/1cacfd123a1eb708837337c27982f6871f5127c5890dbf0775e0f411958fcbf1_Yellow Button_Idle.png",
+          "name": "Yellow Button_Idle.png"
+        }
+      },
+      {
+        "file": "assets/Yellow Button_Pressed.png",
+        "kind": "image",
+        "metadata": "",
+        "name": "Yellow Button_Pressed.png",
+        "smoothed": true,
+        "userAdded": false,
+        "origin": {
+          "identifier": "https://asset-resources.gdevelop.io/public-resources/Menu buttons/928c3ea4db1da835c9f8cc5b7089a47228350209d2ff489e1484c31e5ce41b3d_Yellow Button_Pressed.png",
+          "name": "Yellow Button_Pressed.png"
+        }
+      },
+      {
+        "file": "assets/Road Straight.glb",
+        "kind": "model3D",
+        "metadata": "",
+        "name": "Road Straight.glb",
+        "userAdded": false,
+        "origin": {
+          "identifier": "https://asset-resources.gdevelop.io/public-resources/3D City Kit (Roads)/Ground/01e0d2aa154aaca671e074c6fb5f92e37a75cf9606a88e889a0a2b0934e64122_Road Straight.glb",
+          "name": "Road Straight.glb"
+        }
+      },
+      {
+        "file": "assets/Road Crossroad Path.glb",
+        "kind": "model3D",
+        "metadata": "",
+        "name": "Road Crossroad Path.glb",
+        "userAdded": false,
+        "origin": {
+          "identifier": "https://asset-resources.gdevelop.io/public-resources/3D City Kit (Roads)/Ground/002f076f02776d316382227980bfd080c3c30c663c7846f6f4207a006735ae7c_Road Crossroad Path.glb",
+          "name": "Road Crossroad Path.glb"
+        }
+      },
+      {
+        "file": "assets/Road Intersection Path.glb",
+        "kind": "model3D",
+        "metadata": "",
+        "name": "Road Intersection Path.glb",
+        "userAdded": false,
+        "origin": {
+          "identifier": "https://asset-resources.gdevelop.io/public-resources/3D City Kit (Roads)/Ground/b9771937f69cff0ec5a74cf13ded2017b02b25294684c87bbbf5ca44519f2a51_Road Intersection Path.glb",
+          "name": "Road Intersection Path.glb"
+        }
+      },
+      {
+        "file": "assets/Road Bend.glb",
+        "kind": "model3D",
+        "metadata": "",
+        "name": "Road Bend.glb",
+        "userAdded": false,
+        "origin": {
+          "identifier": "https://asset-resources.gdevelop.io/public-resources/3D City Kit (Roads)/Ground/12e5d612ecde68cdda96d3e48b7c7de88221b35721c4af03607eecbf691d9f99_Road Bend.glb",
+          "name": "Road Bend.glb"
+        }
+      },
+      {
+        "file": "assets/Common Tree 1.glb",
+        "kind": "model3D",
+        "metadata": "",
+        "name": "Common Tree 1.glb",
+        "userAdded": false,
+        "origin": {
+          "identifier": "https://asset-resources.gdevelop.io/public-resources/3D Rocks and Trees/Plant/7c4f3d2324ecd1f8910652478dd7bcb1ac714b260c66a562318c3f72c37bb404_Common Tree 1.glb",
+          "name": "Common Tree 1.glb"
+        }
+      },
+      {
+        "file": "assets/Grass.png",
+        "kind": "image",
+        "metadata": "",
+        "name": "assets\\Grass.png",
+        "smoothed": true,
+        "userAdded": true
+      },
+      {
+        "file": "assets/Arrow.png",
+        "kind": "image",
+        "metadata": "{\"extension\":\".png\",\"pskl\":{}}",
+        "name": "Arrow.png",
+        "smoothed": true,
+        "userAdded": true
+      },
+      {
+        "file": "assets/Particle.png",
+        "kind": "image",
+        "metadata": "",
+        "name": "assets\\Particle.png",
+        "smoothed": true,
+        "userAdded": true
+      },
+      {
+        "file": "assets/tiled_Background Blue Grass.png",
+        "kind": "image",
+        "metadata": "",
+        "name": "tiled_Background Blue Grass.png",
+        "smoothed": true,
+        "userAdded": false,
+        "origin": {
+          "identifier": "https://asset-resources.gdevelop.io/public-resources/Breakable Physics/Background/0be4244f5e3dec39db250063a915ce0f51da28c625c89750e8bdceb6c1e33c60_tiled_Background Blue Grass.png",
+          "name": "tiled_Background Blue Grass.png"
+        }
+      },
+      {
+        "file": "assets/thumbnail-game.png",
+        "kind": "image",
+        "metadata": "",
+        "name": "assets/thumbnail-game.png",
+        "smoothed": true,
+        "userAdded": true
+      },
+      {
+        "file": "assets/Orange Bubble Button_Hovered.png",
+        "kind": "image",
+        "metadata": "",
+        "name": "Orange Bubble Button_Hovered.png",
+        "smoothed": true,
+        "userAdded": true,
+        "origin": {
+          "identifier": "https://asset-resources.gdevelop.io/public-resources/Menu buttons/c7108719eeb51696e6b9bcca1280e5403f7867491f3b02101f759d0e55b112fe_Orange Bubble Button_Hovered.png",
+          "name": "Orange Bubble Button_Hovered.png"
+        }
+      },
+      {
+        "file": "assets/Orange Bubble Button_Idle.png",
+        "kind": "image",
+        "metadata": "",
+        "name": "Orange Bubble Button_Idle.png",
+        "smoothed": true,
+        "userAdded": true,
+        "origin": {
+          "identifier": "https://asset-resources.gdevelop.io/public-resources/Menu buttons/c97e04689076f4c138d10a0dde322d982b332595bce0fe8007ce14bb7a22af21_Orange Bubble Button_Idle.png",
+          "name": "Orange Bubble Button_Idle.png"
+        }
+      },
+      {
+        "file": "assets/Orange Bubble Button_Pressed.png",
+        "kind": "image",
+        "metadata": "",
+        "name": "Orange Bubble Button_Pressed.png",
+        "smoothed": true,
+        "userAdded": true,
+        "origin": {
+          "identifier": "https://asset-resources.gdevelop.io/public-resources/Menu buttons/4c35e1cf340a1121bb63e0d0ef834876be8b9427c5a30bafb2e043640d27aab4_Orange Bubble Button_Pressed.png",
+          "name": "Orange Bubble Button_Pressed.png"
+        }
+      },
+      {
+        "file": "assets/Poppins-Medium.ttf",
+        "kind": "font",
+        "metadata": "",
+        "name": "Poppins-Medium.ttf",
+        "userAdded": false,
+        "origin": {
+          "identifier": "https://asset-resources.gdevelop.io/public-resources/Menu buttons/b81b0bcafdd4279f3bf8d4d3865f51b9961292dad8b5ccbe88807c8acfb6b11d_Poppins-Medium.ttf",
+          "name": "Poppins-Medium.ttf"
+        }
+      },
+      {
+        "file": "assets/rotate-screen-icon.png",
+        "kind": "image",
+        "metadata": "",
+        "name": "rotate-screen-icon.png",
+        "smoothed": true,
+        "userAdded": false
+      },
+      {
+        "file": "assets/rotate-screen-icon2.png",
+        "kind": "image",
+        "metadata": "",
+        "name": "rotate-screen-icon.png2",
+        "smoothed": true,
+        "userAdded": false,
+        "origin": {
+          "identifier": "https://asset-resources.gdevelop.io/public-resources/Mobile Layouts/assets/6a8102ce3340bf9f62300ee1a81ef0b2327b2c6e8d62d7dda68d547f5e2b5969_rotate-screen-icon.png",
+          "name": "rotate-screen-icon.png"
+        }
+      },
+      {
+        "file": "assets/37 - Reason of the Itch.aac",
+        "kind": "audio",
+        "metadata": "",
+        "name": "aac53b22a50f8ec3aa3c1ffd40ce57ca19762470dc7685b58104392545bee49b_37 - Reason of the Itch.aac",
+        "preloadAsMusic": false,
+        "preloadAsSound": false,
+        "preloadInCache": false,
+        "userAdded": false,
+        "origin": {
+          "identifier": "https://asset-resources.gdevelop.io/public-resources/Ragnar Random/Fakebit Chiptune Music/aac53b22a50f8ec3aa3c1ffd40ce57ca19762470dc7685b58104392545bee49b_37 - Reason of the Itch.aac",
+          "name": "gdevelop-asset-store"
+        }
+      }
+    ],
+    "resourceFolders": []
+  },
+  "objects": [],
+  "objectsFolderStructure": {
+    "folderName": "__ROOT"
+  },
+  "objectsGroups": [],
+  "variables": [
+    {
+      "folded": true,
+      "name": "PlayerName",
+      "type": "string",
+      "value": ""
+    },
+    {
+      "folded": true,
+      "name": "Score",
+      "type": "number",
+      "value": 0
+    }
+  ],
+  "layouts": [
+    {
+      "b": 247,
+      "disableInputWhenNotFocused": true,
+      "mangledName": "Menu",
+      "name": "Menu",
+      "r": 208,
+      "standardSortMethod": true,
+      "stopSoundsOnStartup": false,
+      "title": "",
+      "v": 244,
+      "uiSettings": {
+        "grid": false,
+        "gridType": "rectangular",
+        "gridWidth": 32,
+        "gridHeight": 32,
+        "gridOffsetX": 0,
+        "gridOffsetY": 0,
+        "gridColor": 10401023,
+        "gridAlpha": 0.8,
+        "snap": true,
+        "zoomFactor": 0.8294934955937774,
+        "windowMask": false
+      },
+      "objectsGroups": [],
+      "variables": [
+        {
+          "name": "Rect_black_opacity",
+          "type": "string",
+          "value": ""
+        }
+      ],
+      "instances": [
+        {
+          "angle": 0,
+          "customSize": true,
+          "depth": 1,
+          "height": 99,
+          "keepRatio": true,
+          "layer": "",
+          "name": "Title",
+          "persistentUuid": "b9105131-9ab4-4687-8eb0-45777fa4b4e8",
+          "width": 1280,
+          "x": 1,
+          "y": 62,
+          "zOrder": 11,
+          "numberProperties": [],
+          "stringProperties": [],
+          "initialVariables": []
+        },
+        {
+          "angle": 0,
+          "customSize": true,
+          "depth": 1,
+          "height": 716,
+          "keepRatio": true,
+          "layer": "",
+          "name": "Background",
+          "persistentUuid": "9b748168-adac-41e4-8dce-4ae577de1cb5",
+          "width": 1277,
+          "x": -1,
+          "y": -4,
+          "zOrder": 0,
+          "numberProperties": [],
+          "stringProperties": [],
+          "initialVariables": []
+        },
+        {
+          "angle": 0,
+          "customSize": true,
+          "depth": 48,
+          "height": 128,
+          "keepRatio": true,
+          "layer": "",
+          "name": "Start",
+          "persistentUuid": "49b3ba23-ef76-4405-a594-79db8b0ee491",
+          "width": 331,
+          "x": 467,
+          "y": 454,
+          "zOrder": 12,
+          "numberProperties": [],
+          "stringProperties": [],
+          "initialVariables": []
+        },
+        {
+          "angle": 0,
+          "customSize": false,
+          "height": 0,
+          "keepRatio": true,
+          "layer": "Orientation Checker",
+          "name": "ScreenOrientationChecker",
+          "persistentUuid": "ffd04519-826d-4f11-9acc-696a127724a9",
+          "width": 0,
+          "x": 0,
+          "y": 0,
+          "zOrder": 13,
+          "numberProperties": [],
+          "stringProperties": [],
+          "initialVariables": []
+        },
+        {
+          "angle": 0,
+          "customSize": false,
+          "height": 0,
+          "keepRatio": true,
+          "layer": "",
+          "name": "NewText",
+          "persistentUuid": "f3aa3ecc-c7c3-4f40-8f1e-bf2a34f7f84a",
+          "width": 0,
+          "x": 127,
+          "y": 218,
+          "zOrder": 14,
+          "numberProperties": [],
+          "stringProperties": [],
+          "initialVariables": []
+        }
+      ],
+      "objects": [
+        {
+          "assetStoreId": "",
+          "bold": false,
+          "italic": false,
+          "name": "Title",
+          "smoothed": true,
+          "type": "TextObject::Text",
+          "underlined": false,
+          "variables": [],
+          "effects": [],
+          "behaviors": [
+            {
+              "name": "Anchor",
+              "type": "AnchorBehavior::AnchorBehavior",
+              "bottomEdgeAnchor": 0,
+              "leftEdgeAnchor": 1,
+              "relativeToOriginalWindowSize": true,
+              "rightEdgeAnchor": 2,
+              "topEdgeAnchor": 0,
+              "useLegacyBottomAndRightAnchors": false
+            }
+          ],
+          "string": "CAR COIN HUNT",
+          "font": "27d4da0f7767cf3fbf14eb2c8da758dbcfc7b5038c9214d5e6ed62db6476a6e5_Chango-Regular.ttf",
+          "textAlignment": "center",
+          "characterSize": 100,
+          "color": {
+            "b": 28,
+            "g": 231,
+            "r": 248
+          },
+          "content": {
+            "bold": false,
+            "isOutlineEnabled": true,
+            "isShadowEnabled": false,
+            "italic": false,
+            "outlineColor": "0;0;0",
+            "outlineThickness": 5,
+            "shadowAngle": 90,
+            "shadowBlurRadius": 2,
+            "shadowColor": "0;0;0",
+            "shadowDistance": 4,
+            "shadowOpacity": 127,
+            "smoothed": true,
+            "underlined": false,
+            "text": "CAR COIN HUNT",
+            "font": "27d4da0f7767cf3fbf14eb2c8da758dbcfc7b5038c9214d5e6ed62db6476a6e5_Chango-Regular.ttf",
+            "textAlignment": "center",
+            "verticalTextAlignment": "top",
+            "characterSize": 100,
+            "color": "248;231;28"
+          }
+        },
+        {
+          "assetStoreId": "6919e59fd6511d5318a35ad447b4844b53c3500ff92e4a52efe8beb8e95177bc",
+          "height": 1024,
+          "name": "Background",
+          "texture": "tiled_Background Blue Grass.png",
+          "type": "TiledSpriteObject::TiledSprite",
+          "width": 1024,
+          "variables": [],
+          "effects": [],
+          "behaviors": [
+            {
+              "name": "Anchor",
+              "type": "AnchorBehavior::AnchorBehavior",
+              "bottomEdgeAnchor": 0,
+              "leftEdgeAnchor": 1,
+              "relativeToOriginalWindowSize": true,
+              "rightEdgeAnchor": 2,
+              "topEdgeAnchor": 0,
+              "useLegacyBottomAndRightAnchors": false
+            }
+          ]
+        },
+        {
+          "assetStoreId": "72c8adcfb9187ebb5fcfbcd75bc18c74d6af5507e7c482cadcfad28f8011b387",
+          "name": "Start",
+          "type": "PanelSpriteButton::PanelSpriteButton",
+          "variables": [],
+          "effects": [],
+          "behaviors": [
+            {
+              "name": "Anchor",
+              "type": "AnchorBehavior::AnchorBehavior",
+              "bottomEdgeAnchor": 0,
+              "leftEdgeAnchor": 4,
+              "relativeToOriginalWindowSize": true,
+              "rightEdgeAnchor": 0,
+              "topEdgeAnchor": 0,
+              "useLegacyBottomAndRightAnchors": false
+            }
+          ],
+          "content": {
+            "BottomPadding": 0,
+            "TopPadding": 0,
+            "LeftPadding": 10,
+            "RightPadding": 10,
+            "PressedLabelOffsetY": 2
+          },
+          "childrenContent": {
+            "Hovered": {
+              "bottomMargin": 35,
+              "height": 128,
+              "leftMargin": 56,
+              "rightMargin": 35,
+              "texture": "Orange Bubble Button_Hovered.png",
+              "tiled": false,
+              "topMargin": 35,
+              "width": 256
+            },
+            "Idle": {
+              "bottomMargin": 35,
+              "height": 128,
+              "leftMargin": 56,
+              "rightMargin": 35,
+              "texture": "Orange Bubble Button_Idle.png",
+              "tiled": false,
+              "topMargin": 35,
+              "width": 256
+            },
+            "Label": {
+              "bold": true,
+              "italic": false,
+              "smoothed": true,
+              "underlined": false,
+              "string": "Start",
+              "font": "Poppins-Medium.ttf",
+              "textAlignment": "center",
+              "characterSize": 60,
+              "color": {
+                "b": 0,
+                "g": 42,
+                "r": 117
+              },
+              "content": {
+                "bold": true,
+                "isOutlineEnabled": false,
+                "isShadowEnabled": false,
+                "italic": false,
+                "outlineColor": "255;255;255",
+                "outlineThickness": 2,
+                "shadowAngle": 90,
+                "shadowBlurRadius": 2,
+                "shadowColor": "0;0;0",
+                "shadowDistance": 4,
+                "shadowOpacity": 127,
+                "smoothed": true,
+                "underlined": false,
+                "text": "Start",
+                "font": "Poppins-Medium.ttf",
+                "textAlignment": "center",
+                "verticalTextAlignment": "top",
+                "characterSize": 60,
+                "color": "117;42;0"
+              }
+            },
+            "Pressed": {
+              "bottomMargin": 35,
+              "height": 128,
+              "leftMargin": 35,
+              "rightMargin": 35,
+              "texture": "Orange Bubble Button_Pressed.png",
+              "tiled": false,
+              "topMargin": 35,
+              "width": 256
+            }
+          }
+        },
+        {
+          "assetStoreId": "",
+          "name": "ScreenOrientationChecker",
+          "type": "ScreenOrientationChecker::ScreenOrientationChecker",
+          "variables": [],
+          "effects": [],
+          "behaviors": [],
+          "content": {
+            "IsForceShown": false
+          }
+        },
+        {
+          "assetStoreId": "",
+          "bold": true,
+          "italic": false,
+          "name": "NewText",
+          "smoothed": true,
+          "type": "TextObject::Text",
+          "underlined": false,
+          "variables": [],
+          "effects": [],
+          "behaviors": [],
+          "string": "(With Save and Load !)",
+          "font": "",
+          "textAlignment": "center",
+          "characterSize": 50,
+          "color": {
+            "b": 35,
+            "g": 236,
+            "r": 248
+          },
+          "content": {
+            "bold": true,
+            "isOutlineEnabled": true,
+            "isShadowEnabled": false,
+            "italic": false,
+            "outlineColor": "0;0;0",
+            "outlineThickness": 1,
+            "shadowAngle": 90,
+            "shadowBlurRadius": 2,
+            "shadowColor": "0;0;0",
+            "shadowDistance": 4,
+            "shadowOpacity": 127,
+            "smoothed": true,
+            "underlined": false,
+            "text": "(With Save and Load !)",
+            "font": "",
+            "textAlignment": "center",
+            "verticalTextAlignment": "top",
+            "characterSize": 50,
+            "color": "248;236;35"
+          }
+        }
+      ],
+      "objectsFolderStructure": {
+        "folderName": "__ROOT",
+        "children": [
+          {
+            "folderName": "UI",
+            "quickCustomizationVisibility": "hidden",
+            "children": [
+              {
+                "objectName": "ScreenOrientationChecker"
+              }
+            ]
+          },
+          {
+            "folderName": "Background",
+            "quickCustomizationVisibility": "hidden",
+            "children": [
+              {
+                "objectName": "Background"
+              }
+            ]
+          },
+          {
+            "objectName": "Title"
+          },
+          {
+            "objectName": "NewText"
+          },
+          {
+            "objectName": "Start"
+          }
+        ]
+      },
+      "events": [
+        {
+          "colorB": 33,
+          "colorG": 211,
+          "colorR": 126,
+          "creationTime": 0,
+          "name": "Scene transition",
+          "source": "",
+          "type": "BuiltinCommonInstructions::Group",
+          "events": [
+            {
+              "type": "BuiltinCommonInstructions::Standard",
+              "conditions": [
+                {
+                  "type": {
+                    "value": "PanelSpriteButton::PanelSpriteButton::IsClicked"
+                  },
+                  "parameters": [
+                    "Start",
+                    ""
+                  ]
+                }
+              ],
+              "actions": [
+                {
+                  "type": {
+                    "value": "Scene"
+                  },
+                  "parameters": [
+                    "",
+                    "\"Game\"",
+                    ""
+                  ]
+                }
+              ]
+            }
+          ],
+          "parameters": []
+        }
+      ],
+      "layers": [
+        {
+          "ambientLightColorB": 200,
+          "ambientLightColorG": 200,
+          "ambientLightColorR": 200,
+          "camera3DFarPlaneDistance": 10000,
+          "camera3DFieldOfView": 45,
+          "camera3DNearPlaneDistance": 0.1,
+          "cameraType": "perspective",
+          "followBaseLayerCamera": false,
+          "isLightingLayer": false,
+          "isLocked": false,
+          "name": "",
+          "renderingType": "",
+          "visibility": true,
+          "cameras": [
+            {
+              "defaultSize": true,
+              "defaultViewport": true,
+              "height": 0,
+              "viewportBottom": 1,
+              "viewportLeft": 0,
+              "viewportRight": 1,
+              "viewportTop": 0,
+              "width": 0
+            }
+          ],
+          "effects": []
+        },
+        {
+          "ambientLightColorB": 200,
+          "ambientLightColorG": 200,
+          "ambientLightColorR": 200,
+          "camera3DFarPlaneDistance": 10000,
+          "camera3DFieldOfView": 45,
+          "camera3DNearPlaneDistance": 3,
+          "cameraType": "",
+          "followBaseLayerCamera": false,
+          "isLightingLayer": false,
+          "isLocked": false,
+          "name": "Orientation Checker",
+          "renderingType": "",
+          "visibility": true,
+          "cameras": [],
+          "effects": [
+            {
+              "effectType": "Scene3D::HemisphereLight",
+              "name": "3D Light",
+              "doubleParameters": {
+                "elevation": 45,
+                "intensity": 1,
+                "rotation": 0
+              },
+              "stringParameters": {
+                "groundColor": "64;64;64",
+                "skyColor": "255;255;255",
+                "top": "Y-"
+              },
+              "booleanParameters": {}
+            }
+          ]
+        }
+      ],
+      "behaviorsSharedData": [
+        {
+          "name": "Anchor",
+          "type": "AnchorBehavior::AnchorBehavior"
+        },
+        {
+          "name": "Effect",
+          "type": "EffectCapability::EffectBehavior"
+        },
+        {
+          "name": "Flippable",
+          "type": "FlippableCapability::FlippableBehavior"
+        },
+        {
+          "name": "Opacity",
+          "type": "OpacityCapability::OpacityBehavior"
+        },
+        {
+          "name": "Resizable",
+          "type": "ResizableCapability::ResizableBehavior"
+        },
+        {
+          "name": "Scale",
+          "type": "ScalableCapability::ScalableBehavior"
+        },
+        {
+          "name": "Text",
+          "type": "TextContainerCapability::TextContainerBehavior"
+        }
+      ]
+    },
+    {
+      "b": 216,
+      "disableInputWhenNotFocused": true,
+      "mangledName": "Game",
+      "name": "Game",
+      "r": 135,
+      "standardSortMethod": true,
+      "stopSoundsOnStartup": true,
+      "title": "",
+      "v": 179,
+      "uiSettings": {
+        "grid": false,
+        "gridType": "rectangular",
+        "gridWidth": 128,
+        "gridHeight": 128,
+        "gridOffsetX": 0,
+        "gridOffsetY": 0,
+        "gridColor": 10401023,
+        "gridAlpha": 0.8,
+        "snap": false,
+        "zoomFactor": 0.546875,
+        "windowMask": false
+      },
+      "objectsGroups": [
+        {
+          "name": "Obstacle",
+          "objects": [
+            {
+              "name": "SuvLuxury"
+            },
+            {
+              "name": "LargeBuildingA"
+            },
+            {
+              "name": "LargeBuildingF"
+            },
+            {
+              "name": "Van"
+            }
+          ]
+        },
+        {
+          "name": "Road",
+          "objects": [
+            {
+              "name": "RoadStraight"
+            },
+            {
+              "name": "RoadCrossroadPath"
+            },
+            {
+              "name": "RoadIntersectionPath"
+            },
+            {
+              "name": "RoadBend"
+            }
+          ]
+        }
+      ],
+      "variables": [
+        {
+          "folded": true,
+          "name": "CameraAngle",
+          "type": "number",
+          "value": 0
+        },
+        {
+          "folded": true,
+          "name": "GameState",
+          "type": "string",
+          "value": "Playing"
+        },
+        {
+          "folded": true,
+          "name": "HighScore",
+          "type": "number",
+          "value": 0
+        },
+        {
+          "name": "RunTime",
+          "type": "number",
+          "value": 0
+        },
+        {
+          "folded": true,
+          "name": "RisingPitch",
+          "type": "number",
+          "value": 0.9
+        },
+        {
+          "folded": true,
+          "name": "DirectionSpeed",
+          "type": "number",
+          "value": 8
+        },
+        {
+          "folded": true,
+          "name": "SlippingRatio",
+          "type": "number",
+          "value": 0.0005
+        }
+      ],
+      "instances": [
+        {
+          "angle": 0,
+          "customSize": false,
+          "height": 46,
+          "layer": "UI",
+          "name": "BestTimeText",
+          "persistentUuid": "30596d44-7cf8-4fee-8fdd-c76c86c9e777",
+          "width": 187,
+          "x": 5,
+          "y": 50,
+          "zOrder": 23,
+          "numberProperties": [],
+          "stringProperties": [],
+          "initialVariables": []
+        },
+        {
+          "angle": 0,
+          "customSize": false,
+          "height": 0,
+          "layer": "UI",
+          "name": "TimeText",
+          "persistentUuid": "cec96115-b593-411f-93ad-8ed85b3ea2eb",
+          "width": 0,
+          "x": 5,
+          "y": 0,
+          "zOrder": 24,
+          "numberProperties": [],
+          "stringProperties": [],
+          "initialVariables": []
+        },
+        {
+          "angle": 0,
+          "customSize": true,
+          "depth": 1,
+          "height": 44,
+          "layer": "UI",
+          "name": "TutorialText",
+          "persistentUuid": "10449ac5-6445-4470-a25a-84dbe88687ba",
+          "width": 1055,
+          "x": 4,
+          "y": 688,
+          "zOrder": 27,
+          "numberProperties": [],
+          "stringProperties": [],
+          "initialVariables": []
+        },
+        {
+          "angle": 0,
+          "customSize": false,
+          "height": 0,
+          "layer": "UI",
+          "name": "RemainingText",
+          "persistentUuid": "2b76bb1d-a62d-49db-ba85-d80a0aeb0572",
+          "width": 0,
+          "x": 5,
+          "y": 110,
+          "zOrder": 41,
+          "numberProperties": [],
+          "stringProperties": [],
+          "initialVariables": []
+        },
+        {
+          "angle": 0,
+          "customSize": false,
+          "height": 0,
+          "layer": "",
+          "name": "Arrow",
+          "persistentUuid": "bc0dd56c-75c6-4ec9-b4d9-e392704ada45",
+          "width": 0,
+          "x": 1332,
+          "y": -58,
+          "z": 10,
+          "zOrder": 42,
+          "numberProperties": [],
+          "stringProperties": [],
+          "initialVariables": []
+        },
+        {
+          "angle": 0,
+          "customSize": false,
+          "height": 0,
+          "layer": "",
+          "name": "Dust",
+          "persistentUuid": "5d1a7195-aff2-40cf-bbf1-b056f8bf487a",
+          "width": 0,
+          "x": 1349,
+          "y": -111,
+          "z": 15,
+          "zOrder": 43,
+          "numberProperties": [],
+          "stringProperties": [],
+          "initialVariables": []
+        }
+      ],
+      "objects": [
+        {
+          "assetStoreId": "",
+          "name": "Dust",
+          "type": "ParticleEmitter3D::ParticleEmitter3D",
+          "variables": [],
+          "effects": [],
+          "behaviors": [],
+          "content": {
+            "StartColor": "255;255;255",
+            "EndColor": "177;177;177",
+            "Blending": "Normal",
+            "Flow": 20,
+            "Duration": 123456,
+            "StartSizeMin": 12,
+            "StartSizeMax": 12,
+            "EndScale": 5,
+            "StartSpeedMin": 30,
+            "StartSpeedMax": 50,
+            "GravityTop": "Z+",
+            "Gravity": -100,
+            "EndOpacity": 0,
+            "StartOpacity": 220,
+            "SpayConeAngle": 45,
+            "Z": 9,
+            "RotationX": 0,
+            "LifespanMin": 0.5,
+            "LifespanMax": 1,
+            "RotationY": -22.5
+          },
+          "childrenContent": {
+            "Particle": {
+              "adaptCollisionMaskAutomatically": true,
+              "updateIfNotVisible": false,
+              "animations": [
+                {
+                  "name": "Particle",
+                  "useMultipleDirections": false,
+                  "directions": [
+                    {
+                      "looping": false,
+                      "metadata": "{\"pskl\":{}}",
+                      "timeBetweenFrames": 0.08,
+                      "sprites": [
+                        {
+                          "hasCustomCollisionMask": true,
+                          "image": "assets\\Particle.png",
+                          "points": [],
+                          "originPoint": {
+                            "name": "origine",
+                            "x": 0,
+                            "y": 0
+                          },
+                          "centerPoint": {
+                            "automatic": true,
+                            "name": "centre",
+                            "x": 0,
+                            "y": 0
+                          },
+                          "customCollisionMask": [
+                            [
+                              {
+                                "x": 0,
+                                "y": 0
+                              },
+                              {
+                                "x": 32,
+                                "y": 0
+                              },
+                              {
+                                "x": 32,
+                                "y": 32
+                              },
+                              {
+                                "x": 0,
+                                "y": 32
+                              }
+                            ]
+                          ]
+                        }
+                      ]
+                    }
+                  ]
+                }
+              ]
+            }
+          }
+        },
+        {
+          "assetStoreId": "",
+          "bold": false,
+          "italic": false,
+          "name": "BestTimeText",
+          "smoothed": true,
+          "type": "TextObject::Text",
+          "underlined": false,
+          "variables": [],
+          "effects": [],
+          "behaviors": [],
+          "string": "Best: 0",
+          "font": "27d4da0f7767cf3fbf14eb2c8da758dbcfc7b5038c9214d5e6ed62db6476a6e5_Chango-Regular.ttf",
+          "textAlignment": "left",
+          "characterSize": 25,
+          "color": {
+            "b": 255,
+            "g": 255,
+            "r": 255
+          },
+          "content": {
+            "bold": false,
+            "isOutlineEnabled": false,
+            "isShadowEnabled": false,
+            "italic": false,
+            "outlineColor": "255;255;255",
+            "outlineThickness": 2,
+            "shadowAngle": 90,
+            "shadowBlurRadius": 2,
+            "shadowColor": "0;0;0",
+            "shadowDistance": 4,
+            "shadowOpacity": 127,
+            "smoothed": true,
+            "underlined": false,
+            "text": "Best: 0",
+            "font": "27d4da0f7767cf3fbf14eb2c8da758dbcfc7b5038c9214d5e6ed62db6476a6e5_Chango-Regular.ttf",
+            "textAlignment": "left",
+            "verticalTextAlignment": "top",
+            "characterSize": 25,
+            "color": "255;255;255"
+          }
+        },
+        {
+          "assetStoreId": "",
+          "bold": false,
+          "italic": false,
+          "name": "RemainingText",
+          "smoothed": true,
+          "type": "TextObject::Text",
+          "underlined": false,
+          "variables": [],
+          "effects": [],
+          "behaviors": [],
+          "string": "Remaining: 0",
+          "font": "27d4da0f7767cf3fbf14eb2c8da758dbcfc7b5038c9214d5e6ed62db6476a6e5_Chango-Regular.ttf",
+          "textAlignment": "left",
+          "characterSize": 25,
+          "color": {
+            "b": 255,
+            "g": 255,
+            "r": 255
+          },
+          "content": {
+            "bold": false,
+            "isOutlineEnabled": false,
+            "isShadowEnabled": false,
+            "italic": false,
+            "outlineColor": "255;255;255",
+            "outlineThickness": 2,
+            "shadowAngle": 90,
+            "shadowBlurRadius": 2,
+            "shadowColor": "0;0;0",
+            "shadowDistance": 4,
+            "shadowOpacity": 127,
+            "smoothed": true,
+            "underlined": false,
+            "text": "Remaining: 0",
+            "font": "27d4da0f7767cf3fbf14eb2c8da758dbcfc7b5038c9214d5e6ed62db6476a6e5_Chango-Regular.ttf",
+            "textAlignment": "left",
+            "verticalTextAlignment": "top",
+            "characterSize": 25,
+            "color": "255;255;255"
+          }
+        },
+        {
+          "assetStoreId": "",
+          "bold": false,
+          "italic": false,
+          "name": "TimeText",
+          "smoothed": true,
+          "type": "TextObject::Text",
+          "underlined": false,
+          "variables": [],
+          "effects": [],
+          "behaviors": [],
+          "string": "Time:",
+          "font": "27d4da0f7767cf3fbf14eb2c8da758dbcfc7b5038c9214d5e6ed62db6476a6e5_Chango-Regular.ttf",
+          "textAlignment": "left",
+          "characterSize": 40,
+          "color": {
+            "b": 255,
+            "g": 255,
+            "r": 255
+          },
+          "content": {
+            "bold": false,
+            "isOutlineEnabled": false,
+            "isShadowEnabled": false,
+            "italic": false,
+            "outlineColor": "255;255;255",
+            "outlineThickness": 2,
+            "shadowAngle": 90,
+            "shadowBlurRadius": 2,
+            "shadowColor": "0;0;0",
+            "shadowDistance": 4,
+            "shadowOpacity": 127,
+            "smoothed": true,
+            "underlined": false,
+            "text": "Time:",
+            "font": "27d4da0f7767cf3fbf14eb2c8da758dbcfc7b5038c9214d5e6ed62db6476a6e5_Chango-Regular.ttf",
+            "textAlignment": "left",
+            "verticalTextAlignment": "top",
+            "characterSize": 40,
+            "color": "255;255;255"
+          }
+        },
+        {
+          "assetStoreId": "",
+          "bold": false,
+          "italic": false,
+          "name": "TutorialText",
+          "smoothed": true,
+          "type": "TextObject::Text",
+          "underlined": false,
+          "variables": [],
+          "effects": [],
+          "behaviors": [],
+          "string": "Arrow keys to drive. Press \"S\" to save. \"L\" to load your save. Collect all coins.",
+          "font": "27d4da0f7767cf3fbf14eb2c8da758dbcfc7b5038c9214d5e6ed62db6476a6e5_Chango-Regular.ttf",
+          "textAlignment": "left",
+          "characterSize": 20,
+          "color": {
+            "b": 255,
+            "g": 255,
+            "r": 255
+          },
+          "content": {
+            "bold": false,
+            "isOutlineEnabled": false,
+            "isShadowEnabled": false,
+            "italic": false,
+            "outlineColor": "255;255;255",
+            "outlineThickness": 2,
+            "shadowAngle": 90,
+            "shadowBlurRadius": 2,
+            "shadowColor": "0;0;0",
+            "shadowDistance": 4,
+            "shadowOpacity": 127,
+            "smoothed": true,
+            "underlined": false,
+            "text": "Arrow keys to drive. Press \"S\" to save. \"L\" to load your save. Collect all coins.",
+            "font": "27d4da0f7767cf3fbf14eb2c8da758dbcfc7b5038c9214d5e6ed62db6476a6e5_Chango-Regular.ttf",
+            "textAlignment": "left",
+            "verticalTextAlignment": "top",
+            "characterSize": 20,
+            "color": "255;255;255"
+          }
+        },
+        {
+          "assetStoreId": "112c69f62e03fa6df7716cc6b8f174c17857fbf71c8d3fffd16fa8fdbfa49bf1",
+          "name": "SteeringJoystick",
+          "type": "SpriteMultitouchJoystick::SpriteMultitouchJoystick",
+          "variables": [],
+          "effects": [],
+          "behaviors": [],
+          "content": {
+            "DeadZoneRadius": 0.33
+          },
+          "childrenContent": {
+            "Border": {
+              "adaptCollisionMaskAutomatically": false,
+              "updateIfNotVisible": false,
+              "animations": [
+                {
+                  "name": "Idle",
+                  "useMultipleDirections": false,
+                  "directions": [
+                    {
+                      "looping": false,
+                      "timeBetweenFrames": 0.08,
+                      "sprites": [
+                        {
+                          "hasCustomCollisionMask": false,
+                          "image": "assets\\Line light joystick border LeftRightt.png",
+                          "points": [],
+                          "originPoint": {
+                            "name": "origine",
+                            "x": 0,
+                            "y": 0
+                          },
+                          "centerPoint": {
+                            "automatic": true,
+                            "name": "centre",
+                            "x": 0,
+                            "y": 0
+                          },
+                          "customCollisionMask": []
+                        }
+                      ]
+                    }
+                  ]
+                }
+              ]
+            },
+            "Thumb": {
+              "adaptCollisionMaskAutomatically": false,
+              "updateIfNotVisible": false,
+              "animations": [
+                {
+                  "name": "Idle",
+                  "useMultipleDirections": false,
+                  "directions": [
+                    {
+                      "looping": false,
+                      "timeBetweenFrames": 0.08,
+                      "sprites": [
+                        {
+                          "hasCustomCollisionMask": false,
+                          "image": "Line light joystick thumb.png",
+                          "points": [],
+                          "originPoint": {
+                            "name": "origine",
+                            "x": 0,
+                            "y": 0
+                          },
+                          "centerPoint": {
+                            "automatic": true,
+                            "name": "centre",
+                            "x": 0,
+                            "y": 0
+                          },
+                          "customCollisionMask": []
+                        }
+                      ]
+                    }
+                  ]
+                }
+              ]
+            }
+          }
+        },
+        {
+          "assetStoreId": "112c69f62e03fa6df7716cc6b8f174c17857fbf71c8d3fffd16fa8fdbfa49bf1",
+          "name": "PedalJoystick",
+          "type": "SpriteMultitouchJoystick::SpriteMultitouchJoystick",
+          "variables": [],
+          "effects": [],
+          "behaviors": [],
+          "content": {
+            "DeadZoneRadius": 0.33
+          },
+          "childrenContent": {
+            "Border": {
+              "adaptCollisionMaskAutomatically": false,
+              "updateIfNotVisible": false,
+              "animations": [
+                {
+                  "name": "Idle",
+                  "useMultipleDirections": false,
+                  "directions": [
+                    {
+                      "looping": false,
+                      "timeBetweenFrames": 0.08,
+                      "sprites": [
+                        {
+                          "hasCustomCollisionMask": false,
+                          "image": "assets\\Line light joystick border UpDown.png",
+                          "points": [],
+                          "originPoint": {
+                            "name": "origine",
+                            "x": 0,
+                            "y": 0
+                          },
+                          "centerPoint": {
+                            "automatic": true,
+                            "name": "centre",
+                            "x": 0,
+                            "y": 0
+                          },
+                          "customCollisionMask": []
+                        }
+                      ]
+                    }
+                  ]
+                }
+              ]
+            },
+            "Thumb": {
+              "adaptCollisionMaskAutomatically": false,
+              "updateIfNotVisible": false,
+              "animations": [
+                {
+                  "name": "Idle",
+                  "useMultipleDirections": false,
+                  "directions": [
+                    {
+                      "looping": false,
+                      "timeBetweenFrames": 0.08,
+                      "sprites": [
+                        {
+                          "hasCustomCollisionMask": false,
+                          "image": "Line light joystick thumb.png",
+                          "points": [],
+                          "originPoint": {
+                            "name": "origine",
+                            "x": 0,
+                            "y": 0
+                          },
+                          "centerPoint": {
+                            "automatic": true,
+                            "name": "centre",
+                            "x": 0,
+                            "y": 0
+                          },
+                          "customCollisionMask": []
+                        }
+                      ]
+                    }
+                  ]
+                }
+              ]
+            }
+          }
+        },
+        {
+          "assetStoreId": "cddab55130dbd5a0b36f763200e876fb514aa608e1b057a136ebdb5f80006e2c",
+          "name": "Player",
+          "type": "Scene3D::Model3DObject",
+          "variables": [
+            {
+              "folded": true,
+              "name": "Force",
+              "type": "number",
+              "value": 0
+            },
+            {
+              "folded": true,
+              "name": "Direction",
+              "type": "number",
+              "value": 0
+            }
+          ],
+          "effects": [],
+          "behaviors": [
+            {
+              "name": "AdvancedWheelGrip",
+              "type": "AdvancedWheelGrip::AdvancedWheelGrip",
+              "PhysicsCar": "PhysicsCar",
+              "Physics2": "Physics2",
+              "ShakeModel3D": "ShakeModel3D",
+              "GripFactor": 1,
+              "GripRatioMin": 0.075,
+              "GrassGripRatioFactor": 0.5
+            },
+            {
+              "name": "Physics2",
+              "type": "Physics2::Physics2Behavior",
+              "bodyType": "Dynamic",
+              "bullet": false,
+              "fixedRotation": false,
+              "canSleep": true,
+              "shape": "Box",
+              "shapeDimensionA": 0,
+              "shapeDimensionB": 0,
+              "shapeOffsetX": 0,
+              "shapeOffsetY": 0,
+              "polygonOrigin": "Center",
+              "vertices": [],
+              "density": 1,
+              "friction": 2,
+              "restitution": 0.5,
+              "linearDamping": 0.1,
+              "angularDamping": 0,
+              "gravityScale": 0,
+              "layers": 1,
+              "masks": 1,
+              "propertiesQuickCustomizationVisibilities": {
+                "density": "hidden",
+                "friction": "hidden",
+                "linearDamping": "hidden",
+                "restitution": "hidden"
+              }
+            },
+            {
+              "name": "PhysicsCar",
+              "type": "PhysicsCar::PhysicsCar",
+              "Physics2": "Physics2",
+              "Acceleration": 700,
+              "SlippingRatio": 0.5,
+              "Direction": 0,
+              "IsRightPressed": false,
+              "IsLeftPressed": false,
+              "DirectionSpeed": 8,
+              "SteeringSpeed": 40,
+              "SpeedMax": 700,
+              "FrontWheelsPosition": 0.8,
+              "RearWheelsPosition": 0.8,
+              "SteeringAngleMax": 27,
+              "SteeringBackSpeed": 300,
+              "WheelGripRatio": 0.5,
+              "propertiesQuickCustomizationVisibilities": {
+                "SteeringBackSpeed": "hidden"
+              }
+            },
+            {
+              "name": "ShakeModel3D",
+              "type": "ShakeObject3D::ShakeModel3D",
+              "TranslationAmplitudeX": 0,
+              "TranslationAmplitudeY": 0,
+              "TranslationAmplitudeZ": 0,
+              "RotationAmplitudeX": 5,
+              "RotationAmplitudeY": 5,
+              "RotationAmplitudeZ": 5,
+              "Time": 5.00000000000364,
+              "Duration": 5.00000000000364,
+              "StartEasingDuration": 5.00000000000364,
+              "StopEasingDuration": 5.00000000000364,
+              "Frequency": 5.00000000000364,
+              "DeltaX": 5.00000000000364,
+              "DeltaY": 5.00000000000364,
+              "DeltaZ": 5.00000000000364,
+              "DeltaAngleX": 5.00000000000364,
+              "DeltaAngleY": 5.00000000000364,
+              "DeltaAngleZ": 5.00000000000364,
+              "NoiseTime": 5.00000000000364,
+              "Object3D": "Object3D",
+              "IsStartingAtCreation": false
+            },
+            {
+              "name": "ThirdPersonCamera",
+              "type": "ThirdPersonCamera::ThirdPersonCamera",
+              "Object3D": "Object3D",
+              "RotationHalfwayDuration": 0.125,
+              "TranslationZHalfwayDuration": 0.125,
+              "Distance": 800,
+              "LateralOffset": 0,
+              "AheadOffset": 200,
+              "OffsetZ": 0,
+              "RotationAngleOffset": 0,
+              "ElevationAngleOffset": 80,
+              "FollowFreeAreaZMax": 0,
+              "FollowFreeAreaZMin": 0,
+              "RotationLogSpeed": 2.0247e-320,
+              "TranslationZLogSpeed": 2.0247e-320,
+              "IsCalledManually": false,
+              "CameraZ": 0,
+              "LocalOffsetY": 200,
+              "LocalOffsetX": 0,
+              "OffsetY": 200
+            }
+          ],
+          "content": {
+            "centerLocation": "ObjectCenter",
+            "crossfadeDuration": 0,
+            "depth": 74,
+            "height": 74,
+            "keepAspectRatio": true,
+            "materialType": "StandardWithoutMetalness",
+            "modelResourceName": "Sedan Sports.glb",
+            "originLocation": "ModelOrigin",
+            "rotationX": 90,
+            "rotationY": 0,
+            "rotationZ": -90,
+            "width": 74,
+            "animations": []
+          }
+        },
+        {
+          "assetStoreId": "541d82997e1d0e79b20a617cfcc249d1169d838db8512612894f18d64ea7374f",
+          "name": "LargeBuildingF",
+          "type": "Scene3D::Model3DObject",
+          "variables": [],
+          "effects": [],
+          "behaviors": [
+            {
+              "name": "Physics2",
+              "type": "Physics2::Physics2Behavior",
+              "bodyType": "Static",
+              "bullet": false,
+              "fixedRotation": false,
+              "canSleep": true,
+              "shape": "Box",
+              "shapeDimensionA": 0,
+              "shapeDimensionB": 0,
+              "shapeOffsetX": 0,
+              "shapeOffsetY": 0,
+              "polygonOrigin": "Center",
+              "vertices": [],
+              "density": 1,
+              "friction": 0.3,
+              "restitution": 0.1,
+              "linearDamping": 0.1,
+              "angularDamping": 0.1,
+              "gravityScale": 1,
+              "layers": 1,
+              "masks": 1
+            }
+          ],
+          "content": {
+            "centerLocation": "ModelOrigin",
+            "crossfadeDuration": 0,
+            "depth": 245,
+            "height": 245,
+            "keepAspectRatio": true,
+            "materialType": "StandardWithoutMetalness",
+            "modelResourceName": "Large Building F2.glb",
+            "originLocation": "ModelOrigin",
+            "rotationX": 90,
+            "rotationY": 0,
+            "rotationZ": 0,
+            "width": 245,
+            "animations": []
+          }
+        },
+        {
+          "assetStoreId": "479c508ead309e45d684605c3539fd2b3a3a3a1ea8a277f102e8366873aad249",
+          "name": "LargeBuildingA",
+          "type": "Scene3D::Model3DObject",
+          "variables": [],
+          "effects": [],
+          "behaviors": [
+            {
+              "name": "Physics2",
+              "type": "Physics2::Physics2Behavior",
+              "bodyType": "Static",
+              "bullet": false,
+              "fixedRotation": false,
+              "canSleep": true,
+              "shape": "Box",
+              "shapeDimensionA": 0,
+              "shapeDimensionB": 0,
+              "shapeOffsetX": 0,
+              "shapeOffsetY": 0,
+              "polygonOrigin": "Center",
+              "vertices": [],
+              "density": 1,
+              "friction": 0.3,
+              "restitution": 0.1,
+              "linearDamping": 0.1,
+              "angularDamping": 0.1,
+              "gravityScale": 1,
+              "layers": 1,
+              "masks": 1
+            }
+          ],
+          "content": {
+            "centerLocation": "ModelOrigin",
+            "crossfadeDuration": 0,
+            "depth": 245,
+            "height": 245,
+            "keepAspectRatio": true,
+            "materialType": "StandardWithoutMetalness",
+            "modelResourceName": "Large Building A.glb",
+            "originLocation": "ModelOrigin",
+            "rotationX": 90,
+            "rotationY": 0,
+            "rotationZ": 0,
+            "width": 245,
+            "animations": []
+          }
+        },
+        {
+          "assetStoreId": "fcb1a484380a00837bfc40406681f434dd57cce8bdf94694b237fc809bf7b4fb",
+          "name": "SuvLuxury",
+          "type": "Scene3D::Model3DObject",
+          "variables": [],
+          "effects": [],
+          "behaviors": [
+            {
+              "name": "Physics2",
+              "type": "Physics2::Physics2Behavior",
+              "bodyType": "Dynamic",
+              "bullet": false,
+              "fixedRotation": false,
+              "canSleep": true,
+              "shape": "Box",
+              "shapeDimensionA": 0,
+              "shapeDimensionB": 0,
+              "shapeOffsetX": 0,
+              "shapeOffsetY": 0,
+              "polygonOrigin": "Center",
+              "vertices": [],
+              "density": 1,
+              "friction": 0.5,
+              "restitution": 0.2,
+              "linearDamping": 7,
+              "angularDamping": 4,
+              "gravityScale": 0,
+              "layers": 1,
+              "masks": 1
+            }
+          ],
+          "content": {
+            "centerLocation": "ModelOrigin",
+            "crossfadeDuration": 0,
+            "depth": 84,
+            "height": 84,
+            "keepAspectRatio": true,
+            "materialType": "StandardWithoutMetalness",
+            "modelResourceName": "Suv Luxury5.glb",
+            "originLocation": "ModelOrigin",
+            "rotationX": 90,
+            "rotationY": 0,
+            "rotationZ": -90,
+            "width": 84,
+            "animations": []
+          }
+        },
+        {
+          "assetStoreId": "0d7e7a700fe654df077830ff50844449cdaddbb46dbe23de65f970569e6a983b",
+          "name": "Van",
+          "type": "Scene3D::Model3DObject",
+          "variables": [],
+          "effects": [],
+          "behaviors": [
+            {
+              "name": "Physics2",
+              "type": "Physics2::Physics2Behavior",
+              "bodyType": "Dynamic",
+              "bullet": false,
+              "fixedRotation": false,
+              "canSleep": true,
+              "shape": "Box",
+              "shapeDimensionA": 0,
+              "shapeDimensionB": 0,
+              "shapeOffsetX": 0,
+              "shapeOffsetY": 0,
+              "polygonOrigin": "Center",
+              "vertices": [],
+              "density": 1,
+              "friction": 0.5,
+              "restitution": 0.2,
+              "linearDamping": 7,
+              "angularDamping": 4,
+              "gravityScale": 0,
+              "layers": 1,
+              "masks": 1
+            }
+          ],
+          "content": {
+            "centerLocation": "ModelOrigin",
+            "crossfadeDuration": 0,
+            "depth": 79,
+            "height": 79,
+            "keepAspectRatio": true,
+            "materialType": "StandardWithoutMetalness",
+            "modelResourceName": "Van2.glb",
+            "originLocation": "ModelOrigin",
+            "rotationX": 90,
+            "rotationY": 0,
+            "rotationZ": -90,
+            "width": 79,
+            "animations": []
+          }
+        },
+        {
+          "assetStoreId": "",
+          "name": "Arrow",
+          "type": "Scene3D::Cube3DObject",
+          "variables": [],
+          "effects": [],
+          "behaviors": [],
+          "content": {
+            "width": 32,
+            "height": 32,
+            "depth": 1,
+            "enableTextureTransparency": true,
+            "facesOrientation": "Y",
+            "frontFaceResourceName": "Arrow.png",
+            "backFaceResourceName": "",
+            "backFaceUpThroughWhichAxisRotation": "X",
+            "leftFaceResourceName": "",
+            "rightFaceResourceName": "",
+            "topFaceResourceName": "",
+            "bottomFaceResourceName": "",
+            "frontFaceVisible": true,
+            "backFaceVisible": false,
+            "leftFaceVisible": false,
+            "rightFaceVisible": false,
+            "topFaceVisible": false,
+            "bottomFaceVisible": false,
+            "frontFaceResourceRepeat": false,
+            "backFaceResourceRepeat": false,
+            "leftFaceResourceRepeat": false,
+            "rightFaceResourceRepeat": false,
+            "topFaceResourceRepeat": false,
+            "bottomFaceResourceRepeat": false,
+            "materialType": "Basic"
+          }
+        },
+        {
+          "assetStoreId": "bbdd1d6c7d452e1847310eb327cddabe057cb893886f3a7a1af1c38e353eac92",
+          "name": "RoadStraight",
+          "type": "Scene3D::Model3DObject",
+          "variables": [],
+          "effects": [],
+          "behaviors": [],
+          "content": {
+            "centerLocation": "ObjectCenter",
+            "crossfadeDuration": 0,
+            "depth": 384,
+            "height": 384,
+            "keepAspectRatio": true,
+            "materialType": "StandardWithoutMetalness",
+            "modelResourceName": "Road Straight.glb",
+            "originLocation": "TopLeft",
+            "rotationX": 90,
+            "rotationY": 0,
+            "rotationZ": 0,
+            "width": 384,
+            "animations": []
+          }
+        },
+        {
+          "assetStoreId": "ae09642f549122b398405ffda9bd787c20f0fdfcb555a4535c710a3f218c8130",
+          "name": "RoadCrossroadPath",
+          "type": "Scene3D::Model3DObject",
+          "variables": [],
+          "effects": [],
+          "behaviors": [],
+          "content": {
+            "centerLocation": "ObjectCenter",
+            "crossfadeDuration": 0,
+            "depth": 384,
+            "height": 384,
+            "keepAspectRatio": true,
+            "materialType": "StandardWithoutMetalness",
+            "modelResourceName": "Road Crossroad Path.glb",
+            "originLocation": "TopLeft",
+            "rotationX": 90,
+            "rotationY": 0,
+            "rotationZ": 0,
+            "width": 384,
+            "animations": []
+          }
+        },
+        {
+          "assetStoreId": "fad6af3a34db69705540270898681716e47359bbb1b474b27104f823098ef0aa",
+          "name": "RoadIntersectionPath",
+          "type": "Scene3D::Model3DObject",
+          "variables": [],
+          "effects": [],
+          "behaviors": [],
+          "content": {
+            "centerLocation": "ObjectCenter",
+            "crossfadeDuration": 0,
+            "depth": 384,
+            "height": 384,
+            "keepAspectRatio": true,
+            "materialType": "StandardWithoutMetalness",
+            "modelResourceName": "Road Intersection Path.glb",
+            "originLocation": "TopLeft",
+            "rotationX": 90,
+            "rotationY": 0,
+            "rotationZ": 0,
+            "width": 384,
+            "animations": []
+          }
+        },
+        {
+          "assetStoreId": "79a334f5403b6c2daa0625e37c715ede02c9283c06721ee917767f36efff8570",
+          "name": "RoadBend",
+          "type": "Scene3D::Model3DObject",
+          "variables": [],
+          "effects": [],
+          "behaviors": [],
+          "content": {
+            "centerLocation": "ObjectCenter",
+            "crossfadeDuration": 0,
+            "depth": 384,
+            "height": 384,
+            "keepAspectRatio": true,
+            "materialType": "StandardWithoutMetalness",
+            "modelResourceName": "Road Bend.glb",
+            "originLocation": "TopLeft",
+            "rotationX": 90,
+            "rotationY": 0,
+            "rotationZ": 0,
+            "width": 384,
+            "animations": []
+          }
+        },
+        {
+          "assetStoreId": "16fac9714ba0a9eb3c83984910c70b11affca1b4e6a8bed49c22af1d30f957da",
+          "name": "CommonTree",
+          "type": "Scene3D::Model3DObject",
+          "variables": [],
+          "effects": [],
+          "behaviors": [],
+          "content": {
+            "centerLocation": "ModelOrigin",
+            "crossfadeDuration": 0,
+            "depth": 250,
+            "height": 250,
+            "keepAspectRatio": true,
+            "materialType": "StandardWithoutMetalness",
+            "modelResourceName": "Common Tree 1.glb",
+            "originLocation": "ModelOrigin",
+            "rotationX": 90,
+            "rotationY": 0,
+            "rotationZ": 0,
+            "width": 250,
+            "animations": []
+          }
+        },
+        {
+          "assetStoreId": "",
+          "name": "Grass",
+          "type": "Scene3D::Cube3DObject",
+          "variables": [],
+          "effects": [],
+          "behaviors": [],
+          "content": {
+            "width": 100,
+            "height": 100,
+            "depth": 0,
+            "enableTextureTransparency": false,
+            "facesOrientation": "Y",
+            "frontFaceResourceName": "assets\\Grass.png",
+            "backFaceResourceName": "",
+            "backFaceUpThroughWhichAxisRotation": "X",
+            "leftFaceResourceName": "",
+            "rightFaceResourceName": "",
+            "topFaceResourceName": "",
+            "bottomFaceResourceName": "",
+            "frontFaceVisible": true,
+            "backFaceVisible": false,
+            "leftFaceVisible": false,
+            "rightFaceVisible": false,
+            "topFaceVisible": false,
+            "bottomFaceVisible": false,
+            "frontFaceResourceRepeat": false,
+            "backFaceResourceRepeat": false,
+            "leftFaceResourceRepeat": false,
+            "rightFaceResourceRepeat": false,
+            "topFaceResourceRepeat": false,
+            "bottomFaceResourceRepeat": false,
+            "materialType": "Basic"
+          }
+        },
+        {
+          "assetStoreId": "",
+          "name": "CoinPickUp",
+          "type": "ParticleEmitter3D::ParticleEmitter3D",
+          "variables": [],
+          "effects": [],
+          "behaviors": [],
+          "content": {
+            "StartColor": "255;230;145",
+            "EndOpacity": 255,
+            "StartOpacity": 128,
+            "EndScale": 0,
+            "SpayConeAngle": 180,
+            "RotationY": 90,
+            "StartSpeedMin": 400,
+            "StartSpeedMax": 600,
+            "LifespanMax": 0.5,
+            "LifespanMin": 0.25,
+            "Duration": 0.0625,
+            "Flow": 200,
+            "StartSizeMin": 15,
+            "EndColor": "255;230;142",
+            "Blending": "Normal",
+            "StartSizeMax": 30
+          },
+          "childrenContent": {
+            "Particle": {
+              "adaptCollisionMaskAutomatically": true,
+              "updateIfNotVisible": false,
+              "animations": [
+                {
+                  "name": "Image",
+                  "useMultipleDirections": false,
+                  "directions": [
+                    {
+                      "looping": false,
+                      "timeBetweenFrames": 0.08,
+                      "sprites": [
+                        {
+                          "hasCustomCollisionMask": true,
+                          "image": "assets\\Particle.png",
+                          "points": [],
+                          "originPoint": {
+                            "name": "origine",
+                            "x": 0,
+                            "y": 0
+                          },
+                          "centerPoint": {
+                            "automatic": true,
+                            "name": "centre",
+                            "x": 0,
+                            "y": 0
+                          },
+                          "customCollisionMask": [
+                            [
+                              {
+                                "x": 0,
+                                "y": 0
+                              },
+                              {
+                                "x": 32,
+                                "y": 0
+                              },
+                              {
+                                "x": 32,
+                                "y": 32
+                              },
+                              {
+                                "x": 0,
+                                "y": 32
+                              }
+                            ]
+                          ]
+                        }
+                      ]
+                    }
+                  ]
+                }
+              ]
+            }
+          }
+        },
+        {
+          "assetStoreId": "e5919fb86a559b00001678c710efe55f4e9f2e032a9ff9610087370f998d57de",
+          "name": "ScreenOrientationChecker",
+          "type": "ScreenOrientationChecker::ScreenOrientationChecker",
+          "variables": [],
+          "effects": [],
+          "behaviors": [
+            {
+              "name": "Anchor",
+              "type": "AnchorBehavior::AnchorBehavior",
+              "bottomEdgeAnchor": 2,
+              "leftEdgeAnchor": 1,
+              "relativeToOriginalWindowSize": true,
+              "rightEdgeAnchor": 2,
+              "topEdgeAnchor": 1,
+              "useLegacyBottomAndRightAnchors": false
+            }
+          ],
+          "content": {
+            "IsForceShown": false,
+            "BackgroundColor": "24;24;24",
+            "CornerRadius": 8,
+            "Padding": 5
+          },
+          "childrenContent": {
+            "BackgroundPainter": {
+              "fillOpacity": 255,
+              "outlineSize": 0,
+              "outlineOpacity": 255,
+              "absoluteCoordinates": true,
+              "clearBetweenFrames": true,
+              "antialiasing": "none",
+              "fillColor": {
+                "r": 0,
+                "g": 0,
+                "b": 0
+              },
+              "outlineColor": {
+                "r": 0,
+                "g": 0,
+                "b": 0
+              }
+            },
+            "Icon": {
+              "adaptCollisionMaskAutomatically": true,
+              "updateIfNotVisible": false,
+              "animations": [
+                {
+                  "name": "",
+                  "useMultipleDirections": false,
+                  "directions": [
+                    {
+                      "looping": false,
+                      "timeBetweenFrames": 0.08,
+                      "sprites": [
+                        {
+                          "hasCustomCollisionMask": true,
+                          "image": "rotate-screen-icon.png2",
+                          "points": [],
+                          "originPoint": {
+                            "name": "origine",
+                            "x": 52.5,
+                            "y": 56.5
+                          },
+                          "centerPoint": {
+                            "automatic": true,
+                            "name": "centre",
+                            "x": 0,
+                            "y": 0
+                          },
+                          "customCollisionMask": [
+                            [
+                              {
+                                "x": 0,
+                                "y": 1
+                              },
+                              {
+                                "x": 105,
+                                "y": 1
+                              },
+                              {
+                                "x": 105,
+                                "y": 113
+                              },
+                              {
+                                "x": 0,
+                                "y": 113
+                              }
+                            ]
+                          ]
+                        }
+                      ]
+                    }
+                  ]
+                }
+              ]
+            },
+            "Text": {
+              "bold": true,
+              "italic": false,
+              "smoothed": true,
+              "underlined": false,
+              "string": "Rotate screen to play",
+              "font": "",
+              "textAlignment": "center",
+              "characterSize": 30,
+              "color": {
+                "b": 255,
+                "g": 255,
+                "r": 255
+              },
+              "content": {
+                "bold": true,
+                "isOutlineEnabled": false,
+                "isShadowEnabled": false,
+                "italic": false,
+                "outlineColor": "255;255;255",
+                "outlineThickness": 2,
+                "shadowAngle": 90,
+                "shadowBlurRadius": 2,
+                "shadowColor": "0;0;0",
+                "shadowDistance": 4,
+                "shadowOpacity": 127,
+                "smoothed": true,
+                "underlined": false,
+                "text": "Rotate screen to play",
+                "font": "",
+                "textAlignment": "center",
+                "verticalTextAlignment": "top",
+                "characterSize": 30,
+                "color": "255;255;255"
+              }
+            }
+          }
+        },
+        {
+          "assetStoreId": "0e3ee52ba8369c68fe75e3b34a056876ba39b1d0dbb9de6e83e1f2ea0e6f94ac",
+          "name": "Coin",
+          "type": "Scene3D::Model3DObject",
+          "variables": [],
+          "effects": [],
+          "behaviors": [
+            {
+              "name": "Collectible",
+              "type": "Collectible::Collectible",
+              "RotationSpeed": 50
+            },
+            {
+              "name": "InOnScreen",
+              "type": "IsOnScreen::InOnScreen"
+            }
+          ],
+          "content": {
+            "centerLocation": "ModelOrigin",
+            "crossfadeDuration": 0,
+            "depth": 31,
+            "height": 31,
+            "keepAspectRatio": true,
+            "materialType": "StandardWithoutMetalness",
+            "modelResourceName": "Coin.glb",
+            "originLocation": "ModelOrigin",
+            "rotationX": 0,
+            "rotationY": 0,
+            "rotationZ": 0,
+            "width": 31,
+            "animations": []
+          }
+        }
+      ],
+      "objectsFolderStructure": {
+        "folderName": "__ROOT",
+        "children": [
+          {
+            "folderName": "Obstacles",
+            "quickCustomizationVisibility": "visible",
+            "children": [
+              {
+                "objectName": "CommonTree"
+              },
+              {
+                "objectName": "Van"
+              },
+              {
+                "objectName": "SuvLuxury"
+              },
+              {
+                "objectName": "LargeBuildingA"
+              },
+              {
+                "objectName": "LargeBuildingF"
+              }
+            ]
+          },
+          {
+            "folderName": "UI",
+            "quickCustomizationVisibility": "hidden",
+            "children": [
+              {
+                "objectName": "Arrow"
+              },
+              {
+                "objectName": "PedalJoystick"
+              },
+              {
+                "objectName": "SteeringJoystick"
+              },
+              {
+                "objectName": "ScreenOrientationChecker"
+              },
+              {
+                "objectName": "TutorialText"
+              },
+              {
+                "objectName": "TimeText"
+              },
+              {
+                "objectName": "RemainingText"
+              },
+              {
+                "objectName": "BestTimeText"
+              }
+            ]
+          },
+          {
+            "folderName": "VFX",
+            "quickCustomizationVisibility": "hidden",
+            "children": [
+              {
+                "objectName": "Dust"
+              },
+              {
+                "objectName": "CoinPickUp"
+              }
+            ]
+          },
+          {
+            "folderName": "Floor",
+            "quickCustomizationVisibility": "hidden",
+            "children": [
+              {
+                "objectName": "RoadStraight"
+              },
+              {
+                "objectName": "RoadCrossroadPath"
+              },
+              {
+                "objectName": "RoadIntersectionPath"
+              },
+              {
+                "objectName": "RoadBend"
+              },
+              {
+                "objectName": "Grass"
+              }
+            ]
+          },
+          {
+            "objectName": "Player"
+          },
+          {
+            "objectName": "Coin"
+          }
+        ]
+      },
+      "events": [
+        {
+          "type": "BuiltinCommonInstructions::Comment",
+          "color": {
+            "b": 109,
+            "g": 230,
+            "r": 255,
+            "textB": 0,
+            "textG": 0,
+            "textR": 0
+          },
+          "comment": "Game Set Up and controls for mobile."
+        },
+        {
+          "type": "BuiltinCommonInstructions::Standard",
+          "conditions": [
+            {
+              "type": {
+                "value": "DepartScene"
+              },
+              "parameters": [
+                ""
+              ]
+            }
+          ],
+          "actions": [
+            {
+              "type": {
+                "value": "BuiltinExternalLayouts::CreateObjectsFromExternalLayout"
+              },
+              "parameters": [
+                "",
+                "\"Level1\"",
+                "0",
+                "0"
+              ]
+            }
+          ]
+        },
+        {
+          "type": "BuiltinCommonInstructions::Standard",
+          "conditions": [
+            {
+              "type": {
+                "value": "DepartScene"
+              },
+              "parameters": [
+                ""
+              ]
+            }
+          ],
+          "actions": [
+            {
+              "type": {
+                "value": "CentreCamera"
+              },
+              "parameters": [
+                "",
+                "Player",
+                "",
+                "\"\"",
+                ""
+              ]
+            },
+            {
+              "type": {
+                "value": "ReadNumberFromStorage"
+              },
+              "parameters": [
+                "\"HighScore\"",
+                "\"HighScore\"",
+                "",
+                "HighScore"
+              ]
+            },
+            {
+              "type": {
+                "value": "TextContainerCapability::TextContainerBehavior::SetValue"
+              },
+              "parameters": [
+                "BestTimeText",
+                "Text",
+                "=",
+                "\"Best: \" + TimeFormatter::SecondsToHHMMSS000(Variable(HighScore))"
+              ]
+            },
+            {
+              "type": {
+                "value": "TextContainerCapability::TextContainerBehavior::SetValue"
+              },
+              "parameters": [
+                "RemainingText",
+                "Text",
+                "=",
+                "\"Remaining: \" + ToString(SceneInstancesCount(Coin))"
+              ]
+            },
+            {
+              "type": {
+                "value": "PlaySoundCanal"
+              },
+              "parameters": [
+                "",
+                "assets\\CarEngine.mp3",
+                "0",
+                "yes",
+                "30",
+                "1"
+              ]
+            },
+            {
+              "type": {
+                "value": "PlayMusicCanal"
+              },
+              "parameters": [
+                "",
+                "aac53b22a50f8ec3aa3c1ffd40ce57ca19762470dc7685b58104392545bee49b_37 - Reason of the Itch.aac",
+                "1",
+                "",
+                "50",
+                "1"
+              ]
+            }
+          ],
+          "events": [
+            {
+              "type": "BuiltinCommonInstructions::Standard",
+              "conditions": [
+                {
+                  "type": {
+                    "value": "SystemInfo::HasTouchScreen"
+                  },
+                  "parameters": [
+                    ""
+                  ]
+                }
+              ],
+              "actions": [
+                {
+                  "type": {
+                    "value": "Delete"
+                  },
+                  "parameters": [
+                    "TutorialText",
+                    ""
+                  ]
+                }
+              ]
+            }
+          ]
+        },
+        {
+          "colorB": 228,
+          "colorG": 176,
+          "colorR": 74,
+          "creationTime": 0,
+          "name": "When the game is playing",
+          "source": "",
+          "type": "BuiltinCommonInstructions::Group",
+          "events": [
+            {
+              "type": "BuiltinCommonInstructions::Standard",
+              "conditions": [
+                {
+                  "type": {
+                    "value": "StringVariable"
+                  },
+                  "parameters": [
+                    "GameState",
+                    "=",
+                    "\"Playing\""
+                  ]
+                }
+              ],
+              "actions": [],
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Comment",
+                  "color": {
+                    "b": 109,
+                    "g": 230,
+                    "r": 255,
+                    "textB": 0,
+                    "textG": 0,
+                    "textR": 0
+                  },
+                  "comment": "Start timer only when the SedanPlayer starts moving."
+                },
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [
+                    {
+                      "type": {
+                        "value": "Physics2::LinearVelocityLength"
+                      },
+                      "parameters": [
+                        "Player",
+                        "Physics2",
+                        ">",
+                        "0"
+                      ]
+                    },
+                    {
+                      "type": {
+                        "inverted": true,
+                        "value": "CompareTimer"
+                      },
+                      "parameters": [
+                        "",
+                        "\"Time\"",
+                        ">",
+                        "0"
+                      ]
+                    }
+                  ],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "ResetTimer"
+                      },
+                      "parameters": [
+                        "",
+                        "\"Time\""
+                      ]
+                    }
+                  ]
+                },
+                {
+                  "type": "BuiltinCommonInstructions::Comment",
+                  "color": {
+                    "b": 109,
+                    "g": 230,
+                    "r": 255,
+                    "textB": 0,
+                    "textG": 0,
+                    "textR": 0
+                  },
+                  "comment": "Display the timer."
+                },
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "TextContainerCapability::TextContainerBehavior::SetValue"
+                      },
+                      "parameters": [
+                        "TimeText",
+                        "Text",
+                        "=",
+                        "\"Time: \" + TimeFormatter::SecondsToHHMMSS000(TimerElapsedTime(\"Time\"))"
+                      ]
+                    }
+                  ]
+                },
+                {
+                  "colorB": 228,
+                  "colorG": 176,
+                  "colorR": 74,
+                  "creationTime": 0,
+                  "name": "Player controls",
+                  "source": "",
+                  "type": "BuiltinCommonInstructions::Group",
+                  "events": [
+                    {
+                      "type": "BuiltinCommonInstructions::Standard",
+                      "conditions": [
+                        {
+                          "type": {
+                            "value": "KeyFromTextPressed"
+                          },
+                          "parameters": [
+                            "",
+                            "\"Down\""
+                          ]
+                        }
+                      ],
+                      "actions": [
+                        {
+                          "type": {
+                            "value": "PhysicsCar::PhysicsCar::SimulateDownKey"
+                          },
+                          "parameters": [
+                            "Player",
+                            "PhysicsCar",
+                            ""
+                          ]
+                        }
+                      ]
+                    },
+                    {
+                      "type": "BuiltinCommonInstructions::Standard",
+                      "conditions": [
+                        {
+                          "type": {
+                            "value": "KeyFromTextPressed"
+                          },
+                          "parameters": [
+                            "",
+                            "\"Up\""
+                          ]
+                        }
+                      ],
+                      "actions": [
+                        {
+                          "type": {
+                            "value": "PhysicsCar::PhysicsCar::SimulateUpKey"
+                          },
+                          "parameters": [
+                            "Player",
+                            "PhysicsCar",
+                            ""
+                          ]
+                        }
+                      ]
+                    },
+                    {
+                      "type": "BuiltinCommonInstructions::Standard",
+                      "conditions": [
+                        {
+                          "type": {
+                            "value": "KeyFromTextPressed"
+                          },
+                          "parameters": [
+                            "",
+                            "\"Left\""
+                          ]
+                        }
+                      ],
+                      "actions": [
+                        {
+                          "type": {
+                            "value": "PhysicsCar::PhysicsCar::SimulateLeftKey"
+                          },
+                          "parameters": [
+                            "Player",
+                            "PhysicsCar",
+                            ""
+                          ]
+                        }
+                      ]
+                    },
+                    {
+                      "type": "BuiltinCommonInstructions::Standard",
+                      "conditions": [
+                        {
+                          "type": {
+                            "value": "KeyFromTextPressed"
+                          },
+                          "parameters": [
+                            "",
+                            "\"Right\""
+                          ]
+                        }
+                      ],
+                      "actions": [
+                        {
+                          "type": {
+                            "value": "PhysicsCar::PhysicsCar::SimulateRightKey"
+                          },
+                          "parameters": [
+                            "Player",
+                            "PhysicsCar",
+                            ""
+                          ]
+                        }
+                      ]
+                    },
+                    {
+                      "type": "BuiltinCommonInstructions::Standard",
+                      "conditions": [
+                        {
+                          "type": {
+                            "value": "BuiltinCommonInstructions::Or"
+                          },
+                          "parameters": [],
+                          "subInstructions": [
+                            {
+                              "type": {
+                                "value": "Gamepads::C_Button_pressed"
+                              },
+                              "parameters": [
+                                "",
+                                "1",
+                                "\"LT\"",
+                                "\"Up\""
+                              ]
+                            },
+                            {
+                              "type": {
+                                "value": "Gamepads::C_Button_pressed"
+                              },
+                              "parameters": [
+                                "",
+                                "1",
+                                "\"RT\"",
+                                "\"Up\""
+                              ]
+                            }
+                          ]
+                        }
+                      ],
+                      "actions": [
+                        {
+                          "type": {
+                            "value": "PhysicsCar::PhysicsCar::SimulateAccelerationStick"
+                          },
+                          "parameters": [
+                            "Player",
+                            "PhysicsCar",
+                            "Gamepads::TriggerPressure(1, \"RT\") - Gamepads::TriggerPressure(1, \"LT\")",
+                            ""
+                          ]
+                        }
+                      ]
+                    },
+                    {
+                      "type": "BuiltinCommonInstructions::Standard",
+                      "conditions": [
+                        {
+                          "type": {
+                            "value": "Gamepads::C_Axis_pushed"
+                          },
+                          "parameters": [
+                            "",
+                            "1",
+                            "\"Left\"",
+                            "\"Any\"",
+                            ""
+                          ]
+                        }
+                      ],
+                      "actions": [
+                        {
+                          "type": {
+                            "value": "PhysicsCar::PhysicsCar::SimulateSteeringStick"
+                          },
+                          "parameters": [
+                            "Player",
+                            "PhysicsCar",
+                            "Gamepads::StickForceX(1, \"Left\")",
+                            ""
+                          ]
+                        }
+                      ]
+                    },
+                    {
+                      "type": "BuiltinCommonInstructions::Standard",
+                      "conditions": [
+                        {
+                          "type": {
+                            "value": "SystemInfo::HasTouchScreen"
+                          },
+                          "parameters": [
+                            ""
+                          ]
+                        }
+                      ],
+                      "actions": [],
+                      "events": [
+                        {
+                          "type": "BuiltinCommonInstructions::Standard",
+                          "conditions": [
+                            {
+                              "type": {
+                                "value": "DepartScene"
+                              },
+                              "parameters": [
+                                ""
+                              ]
+                            }
+                          ],
+                          "actions": [
+                            {
+                              "type": {
+                                "value": "BuiltinExternalLayouts::CreateObjectsFromExternalLayout"
+                              },
+                              "parameters": [
+                                "",
+                                "\"Touch controls\"",
+                                "0",
+                                "0",
+                                "0"
+                              ]
+                            }
+                          ],
+                          "events": [
+                            {
+                              "type": "BuiltinCommonInstructions::Standard",
+                              "conditions": [],
+                              "actions": [
+                                {
+                                  "type": {
+                                    "value": "Cache"
+                                  },
+                                  "parameters": [
+                                    "SteeringJoystick"
+                                  ]
+                                },
+                                {
+                                  "type": {
+                                    "value": "Cache"
+                                  },
+                                  "parameters": [
+                                    "PedalJoystick"
+                                  ]
+                                },
+                                {
+                                  "type": {
+                                    "value": "SpriteMultitouchJoystick::SpriteMultitouchJoystick::ActivateControl"
+                                  },
+                                  "parameters": [
+                                    "SteeringJoystick",
+                                    "",
+                                    ""
+                                  ]
+                                },
+                                {
+                                  "type": {
+                                    "value": "SpriteMultitouchJoystick::SpriteMultitouchJoystick::ActivateControl"
+                                  },
+                                  "parameters": [
+                                    "PedalJoystick",
+                                    "",
+                                    ""
+                                  ]
+                                }
+                              ]
+                            }
+                          ]
+                        },
+                        {
+                          "type": "BuiltinCommonInstructions::Standard",
+                          "conditions": [
+                            {
+                              "type": {
+                                "value": "SpriteMultitouchJoystick::HasTouchStartedOnScreenSide"
+                              },
+                              "parameters": [
+                                "",
+                                "SteeringJoystick",
+                                "\"Left\"",
+                                ""
+                              ]
+                            }
+                          ],
+                          "actions": [
+                            {
+                              "type": {
+                                "value": "SpriteMultitouchJoystick::SpriteMultitouchJoystick::TeleportAndPress"
+                              },
+                              "parameters": [
+                                "SteeringJoystick",
+                                "CursorX(\"UI\")",
+                                "CursorY(\"UI\")",
+                                ""
+                              ]
+                            }
+                          ]
+                        },
+                        {
+                          "type": "BuiltinCommonInstructions::Standard",
+                          "conditions": [
+                            {
+                              "type": {
+                                "value": "SpriteMultitouchJoystick::HasTouchStartedOnScreenSide"
+                              },
+                              "parameters": [
+                                "",
+                                "PedalJoystick",
+                                "\"Right\"",
+                                ""
+                              ]
+                            }
+                          ],
+                          "actions": [
+                            {
+                              "type": {
+                                "value": "SpriteMultitouchJoystick::SpriteMultitouchJoystick::TeleportAndPress"
+                              },
+                              "parameters": [
+                                "PedalJoystick",
+                                "CursorX(\"UI\")",
+                                "CursorY(\"UI\")",
+                                ""
+                              ]
+                            }
+                          ]
+                        },
+                        {
+                          "type": "BuiltinCommonInstructions::Standard",
+                          "conditions": [
+                            {
+                              "type": {
+                                "value": "SpriteMultitouchJoystick::SpriteMultitouchJoystick::IsPressed"
+                              },
+                              "parameters": [
+                                "PedalJoystick",
+                                ""
+                              ]
+                            }
+                          ],
+                          "actions": [
+                            {
+                              "type": {
+                                "value": "PhysicsCar::PhysicsCar::SimulateAccelerationStick"
+                              },
+                              "parameters": [
+                                "Player",
+                                "PhysicsCar",
+                                "-PedalJoystick.StickForceY()",
+                                ""
+                              ]
+                            }
+                          ]
+                        },
+                        {
+                          "type": "BuiltinCommonInstructions::Standard",
+                          "conditions": [
+                            {
+                              "type": {
+                                "value": "SpriteMultitouchJoystick::SpriteMultitouchJoystick::IsPressed"
+                              },
+                              "parameters": [
+                                "SteeringJoystick",
+                                ""
+                              ]
+                            }
+                          ],
+                          "actions": [
+                            {
+                              "type": {
+                                "value": "PhysicsCar::PhysicsCar::SimulateSteeringStick"
+                              },
+                              "parameters": [
+                                "Player",
+                                "PhysicsCar",
+                                "SteeringJoystick.StickForceX()",
+                                ""
+                              ]
+                            }
+                          ]
+                        }
+                      ]
+                    },
+                    {
+                      "type": "BuiltinCommonInstructions::Standard",
+                      "conditions": [],
+                      "actions": [],
+                      "events": [
+                        {
+                          "type": "BuiltinCommonInstructions::Standard",
+                          "conditions": [
+                            {
+                              "type": {
+                                "value": "CollisionNP"
+                              },
+                              "parameters": [
+                                "Player",
+                                "Road",
+                                "",
+                                "",
+                                ""
+                              ]
+                            }
+                          ],
+                          "actions": [
+                            {
+                              "type": {
+                                "value": "SetBooleanVariable"
+                              },
+                              "parameters": [
+                                "IsOnTheRoad",
+                                "True",
+                                ""
+                              ]
+                            },
+                            {
+                              "type": {
+                                "value": "AdvancedWheelGrip::AdvancedWheelGrip::SetSurface"
+                              },
+                              "parameters": [
+                                "Player",
+                                "AdvancedWheelGrip",
+                                "\"Asphalt\"",
+                                ""
+                              ]
+                            }
+                          ]
+                        },
+                        {
+                          "type": "BuiltinCommonInstructions::Standard",
+                          "conditions": [
+                            {
+                              "type": {
+                                "value": "BooleanVariable"
+                              },
+                              "parameters": [
+                                "IsOnTheRoad",
+                                "False",
+                                ""
+                              ]
+                            }
+                          ],
+                          "actions": [
+                            {
+                              "type": {
+                                "value": "AdvancedWheelGrip::AdvancedWheelGrip::SetSurface"
+                              },
+                              "parameters": [
+                                "Player",
+                                "AdvancedWheelGrip",
+                                "\"Grass\"",
+                                ""
+                              ]
+                            }
+                          ]
+                        }
+                      ],
+                      "variables": [
+                        {
+                          "folded": true,
+                          "name": "IsOnTheRoad",
+                          "type": "boolean",
+                          "value": false
+                        }
+                      ]
+                    }
+                  ],
+                  "parameters": []
+                }
+              ]
+            }
+          ],
+          "parameters": []
+        },
+        {
+          "colorB": 228,
+          "colorG": 176,
+          "colorR": 74,
+          "creationTime": 0,
+          "name": "Particles and car rev volume",
+          "source": "",
+          "type": "BuiltinCommonInstructions::Group",
+          "events": [
+            {
+              "type": "BuiltinCommonInstructions::Standard",
+              "conditions": [],
+              "actions": [
+                {
+                  "type": {
+                    "value": "SetAngle"
+                  },
+                  "parameters": [
+                    "Dust",
+                    "=",
+                    "Player.Angle() + 180"
+                  ]
+                },
+                {
+                  "type": {
+                    "value": "MettreAutour"
+                  },
+                  "parameters": [
+                    "Dust",
+                    "Player",
+                    "10",
+                    "Player.Angle() - 180"
+                  ]
+                },
+                {
+                  "type": {
+                    "value": "MettreXY"
+                  },
+                  "parameters": [
+                    "Dust",
+                    "=",
+                    "Dust.X() + RandomFloatInRange(-6, 6)",
+                    "=",
+                    "Dust.Y() + RandomFloatInRange(-6, 6)"
+                  ]
+                },
+                {
+                  "type": {
+                    "value": "ModPitchSoundChannel"
+                  },
+                  "parameters": [
+                    "",
+                    "0",
+                    "=",
+                    "min((Player.Physics2::LinearVelocity() / 300), 1)"
+                  ]
+                },
+                {
+                  "type": {
+                    "value": "ModVolumeSoundCanal"
+                  },
+                  "parameters": [
+                    "",
+                    "0",
+                    "=",
+                    "min((Player.Physics2::LinearVelocity()) * 10, 25)"
+                  ]
+                }
+              ]
+            }
+          ],
+          "parameters": []
+        },
+        {
+          "colorB": 228,
+          "colorG": 176,
+          "colorR": 74,
+          "creationTime": 0,
+          "name": "Coins",
+          "source": "",
+          "type": "BuiltinCommonInstructions::Group",
+          "events": [
+            {
+              "type": "BuiltinCommonInstructions::Comment",
+              "color": {
+                "b": 109,
+                "g": 230,
+                "r": 255,
+                "textB": 0,
+                "textG": 0,
+                "textR": 0
+              },
+              "comment": "Coins indicator"
+            },
+            {
+              "type": "BuiltinCommonInstructions::Standard",
+              "conditions": [
+                {
+                  "type": {
+                    "value": "PickNearest"
+                  },
+                  "parameters": [
+                    "Coin",
+                    "Player.CenterX()",
+                    "Player.CenterY()",
+                    ""
+                  ]
+                }
+              ],
+              "actions": [
+                {
+                  "type": {
+                    "value": "SetAngle"
+                  },
+                  "parameters": [
+                    "Arrow",
+                    "=",
+                    "AngleBetweenPositions(Player.CenterX(), Player.CenterY(), Coin.CenterX(), Coin.CenterY())"
+                  ]
+                },
+                {
+                  "type": {
+                    "value": "MettreAutour"
+                  },
+                  "parameters": [
+                    "Arrow",
+                    "Player",
+                    "160",
+                    "Arrow.Angle()"
+                  ]
+                }
+              ],
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [
+                    {
+                      "type": {
+                        "value": "IsOnScreen::InOnScreen::IsOnScreen"
+                      },
+                      "parameters": [
+                        "Coin",
+                        "InOnScreen",
+                        "0",
+                        ""
+                      ]
+                    }
+                  ],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "Cache"
+                      },
+                      "parameters": [
+                        "Arrow"
+                      ]
+                    }
+                  ]
+                },
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [
+                    {
+                      "type": {
+                        "inverted": true,
+                        "value": "IsOnScreen::InOnScreen::IsOnScreen"
+                      },
+                      "parameters": [
+                        "Coin",
+                        "InOnScreen",
+                        "10",
+                        ""
+                      ]
+                    }
+                  ],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "Montre"
+                      },
+                      "parameters": [
+                        "Arrow",
+                        ""
+                      ]
+                    }
+                  ]
+                }
+              ]
+            },
+            {
+              "type": "BuiltinCommonInstructions::Standard",
+              "conditions": [
+                {
+                  "type": {
+                    "value": "CollisionNP"
+                  },
+                  "parameters": [
+                    "Coin",
+                    "Player",
+                    "",
+                    "",
+                    ""
+                  ]
+                }
+              ],
+              "actions": [
+                {
+                  "type": {
+                    "value": "Create"
+                  },
+                  "parameters": [
+                    "",
+                    "CoinPickUp",
+                    "Coin.CenterX()",
+                    "Coin.CenterY()",
+                    ""
+                  ]
+                },
+                {
+                  "type": {
+                    "value": "Scene3D::Base3DBehavior::SetCenterZ"
+                  },
+                  "parameters": [
+                    "CoinPickUp",
+                    "Object3D",
+                    "=",
+                    "Coin.Object3D::CenterZ()"
+                  ]
+                },
+                {
+                  "type": {
+                    "value": "Delete"
+                  },
+                  "parameters": [
+                    "Coin",
+                    ""
+                  ]
+                },
+                {
+                  "type": {
+                    "value": "TextContainerCapability::TextContainerBehavior::SetValue"
+                  },
+                  "parameters": [
+                    "RemainingText",
+                    "Text",
+                    "=",
+                    "\"Remaining: \" + ToString(SceneInstancesCount(Coin))"
+                  ]
+                }
+              ],
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Comment",
+                  "color": {
+                    "b": 109,
+                    "g": 230,
+                    "r": 255,
+                    "textB": 0,
+                    "textG": 0,
+                    "textR": 0
+                  },
+                  "comment": "Collect all coins"
+                },
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [
+                    {
+                      "type": {
+                        "value": "SceneInstancesCount"
+                      },
+                      "parameters": [
+                        "",
+                        "Coin",
+                        "=",
+                        "0"
+                      ]
+                    }
+                  ],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SetStringVariable"
+                      },
+                      "parameters": [
+                        "GameState",
+                        "=",
+                        "\"GameOver\""
+                      ]
+                    },
+                    {
+                      "type": {
+                        "value": "ChangeTimeScale"
+                      },
+                      "parameters": [
+                        "",
+                        "0.5"
+                      ]
+                    },
+                    {
+                      "type": {
+                        "value": "Physics2::TimeScale"
+                      },
+                      "parameters": [
+                        "Player",
+                        "Physics2",
+                        "0.5"
+                      ]
+                    },
+                    {
+                      "type": {
+                        "value": "SetNumberVariable"
+                      },
+                      "parameters": [
+                        "Score",
+                        "=",
+                        "TimerElapsedTime(\"Time\")"
+                      ]
+                    }
+                  ],
+                  "events": [
+                    {
+                      "type": "BuiltinCommonInstructions::Standard",
+                      "conditions": [],
+                      "actions": [
+                        {
+                          "type": {
+                            "value": "Wait"
+                          },
+                          "parameters": [
+                            "1.5"
+                          ]
+                        },
+                        {
+                          "type": {
+                            "value": "Scene"
+                          },
+                          "parameters": [
+                            "",
+                            "\"Leaderboard\"",
+                            ""
+                          ]
+                        }
+                      ]
+                    },
+                    {
+                      "type": "BuiltinCommonInstructions::Standard",
+                      "conditions": [
+                        {
+                          "type": {
+                            "value": "BuiltinCommonInstructions::Or"
+                          },
+                          "parameters": [],
+                          "subInstructions": [
+                            {
+                              "type": {
+                                "value": "CompareTimer"
+                              },
+                              "parameters": [
+                                "",
+                                "\"Time\"",
+                                "<",
+                                "HighScore"
+                              ]
+                            },
+                            {
+                              "type": {
+                                "value": "NumberVariable"
+                              },
+                              "parameters": [
+                                "HighScore",
+                                "=",
+                                "0"
+                              ]
+                            }
+                          ]
+                        }
+                      ],
+                      "actions": [
+                        {
+                          "type": {
+                            "value": "SetNumberVariable"
+                          },
+                          "parameters": [
+                            "HighScore",
+                            "=",
+                            "Score"
+                          ]
+                        },
+                        {
+                          "type": {
+                            "value": "EcrireFichierExp"
+                          },
+                          "parameters": [
+                            "\"HighScore\"",
+                            "\"HighScore\"",
+                            "HighScore"
+                          ]
+                        },
+                        {
+                          "type": {
+                            "value": "TextObject::ChangeColor"
+                          },
+                          "parameters": [
+                            "BestTimeText",
+                            "\"248;231;28\""
+                          ]
+                        }
+                      ]
+                    }
+                  ]
+                }
+              ]
+            }
+          ],
+          "parameters": []
+        },
+        {
+          "colorB": 228,
+          "colorG": 176,
+          "colorR": 74,
+          "creationTime": 0,
+          "name": "Collision sound effects",
+          "source": "",
+          "type": "BuiltinCommonInstructions::Group",
+          "events": [
+            {
+              "type": "BuiltinCommonInstructions::Standard",
+              "conditions": [
+                {
+                  "type": {
+                    "value": "Physics2::Collision"
+                  },
+                  "parameters": [
+                    "Player",
+                    "Physics2",
+                    "Obstacle",
+                    ""
+                  ]
+                },
+                {
+                  "type": {
+                    "value": "BuiltinCommonInstructions::Once"
+                  },
+                  "parameters": []
+                }
+              ],
+              "actions": [
+                {
+                  "type": {
+                    "value": "PlaySound"
+                  },
+                  "parameters": [
+                    "",
+                    "Collision",
+                    "",
+                    "50",
+                    "RandomFloatInRange(0.8,1)"
+                  ]
+                }
+              ]
+            }
+          ],
+          "parameters": []
+        },
+        {
+          "type": "BuiltinCommonInstructions::Standard",
+          "conditions": [
+            {
+              "type": {
+                "value": "KeyFromTextPressed"
+              },
+              "parameters": [
+                "",
+                "\"l\""
+              ]
+            },
+            {
+              "type": {
+                "value": "BuiltinCommonInstructions::Once"
+              },
+              "parameters": []
+            }
+          ],
+          "actions": [
+            {
+              "type": {
+                "value": "SaveState::LoadGame"
+              },
+              "parameters": [
+                "",
+                "",
+                ""
+              ]
+            }
+          ]
+        },
+        {
+          "type": "BuiltinCommonInstructions::Standard",
+          "conditions": [
+            {
+              "type": {
+                "value": "KeyFromTextPressed"
+              },
+              "parameters": [
+                "",
+                "\"s\""
+              ]
+            },
+            {
+              "type": {
+                "value": "BuiltinCommonInstructions::Once"
+              },
+              "parameters": []
+            }
+          ],
+          "actions": [
+            {
+              "type": {
+                "value": "SaveState::SaveGame"
+              },
+              "parameters": [
+                "",
+                "",
+                ""
+              ]
+            }
+          ]
+        }
+      ],
+      "layers": [
+        {
+          "ambientLightColorB": 200,
+          "ambientLightColorG": 200,
+          "ambientLightColorR": 200,
+          "camera3DFarPlaneDistance": 10000,
+          "camera3DFieldOfView": 55,
+          "camera3DNearPlaneDistance": 0.1,
+          "cameraType": "perspective",
+          "followBaseLayerCamera": false,
+          "isLightingLayer": false,
+          "isLocked": false,
+          "name": "",
+          "renderingType": "2d+3d",
+          "visibility": true,
+          "cameras": [
+            {
+              "defaultSize": true,
+              "defaultViewport": true,
+              "height": 0,
+              "viewportBottom": 1,
+              "viewportLeft": 0,
+              "viewportRight": 1,
+              "viewportTop": 0,
+              "width": 0
+            }
+          ],
+          "effects": [
+            {
+              "effectType": "Scene3D::HemisphereLight",
+              "name": "Effect3",
+              "doubleParameters": {
+                "elevation": 90,
+                "intensity": 0.75,
+                "rotation": 0
+              },
+              "stringParameters": {
+                "groundColor": "177;177;177",
+                "skyColor": "255;255;255",
+                "top": "Z+"
+              },
+              "booleanParameters": {}
+            },
+            {
+              "effectType": "Scene3D::DirectionalLight",
+              "name": "Effect",
+              "doubleParameters": {
+                "elevation": 45,
+                "intensity": 0.5,
+                "rotation": 45
+              },
+              "stringParameters": {
+                "color": "255;255;255",
+                "top": "Z+"
+              },
+              "booleanParameters": {}
+            }
+          ]
+        },
+        {
+          "ambientLightColorB": 200,
+          "ambientLightColorG": 200,
+          "ambientLightColorR": 200,
+          "camera3DFarPlaneDistance": 10000,
+          "camera3DFieldOfView": 45,
+          "camera3DNearPlaneDistance": 0.1,
+          "cameraType": "perspective",
+          "followBaseLayerCamera": false,
+          "isLightingLayer": false,
+          "isLocked": false,
+          "name": "UI",
+          "renderingType": "2d",
+          "visibility": true,
+          "cameras": [],
+          "effects": [
+            {
+              "effectType": "Outline",
+              "name": "Effect",
+              "doubleParameters": {
+                "padding": 3,
+                "thickness": 3
+              },
+              "stringParameters": {
+                "color": "0;0;0"
+              },
+              "booleanParameters": {}
+            }
+          ]
+        },
+        {
+          "ambientLightColorB": 200,
+          "ambientLightColorG": 200,
+          "ambientLightColorR": 200,
+          "camera3DFarPlaneDistance": 10000,
+          "camera3DFieldOfView": 45,
+          "camera3DNearPlaneDistance": 0.1,
+          "cameraType": "perspective",
+          "followBaseLayerCamera": false,
+          "isLightingLayer": false,
+          "isLocked": false,
+          "name": "MobileControls",
+          "renderingType": "2d",
+          "visibility": true,
+          "cameras": [],
+          "effects": []
+        },
+        {
+          "ambientLightColorB": 200,
+          "ambientLightColorG": 200,
+          "ambientLightColorR": 200,
+          "camera3DFarPlaneDistance": 10000,
+          "camera3DFieldOfView": 45,
+          "camera3DNearPlaneDistance": 3,
+          "cameraType": "",
+          "followBaseLayerCamera": false,
+          "isLightingLayer": false,
+          "isLocked": false,
+          "name": "Debug",
+          "renderingType": "",
+          "visibility": true,
+          "cameras": [],
+          "effects": [
+            {
+              "effectType": "Scene3D::HemisphereLight",
+              "name": "3D Light",
+              "doubleParameters": {
+                "elevation": 45,
+                "intensity": 1,
+                "rotation": 0
+              },
+              "stringParameters": {
+                "groundColor": "64;64;64",
+                "skyColor": "255;255;255",
+                "top": "Y-"
+              },
+              "booleanParameters": {}
+            }
+          ]
+        }
+      ],
+      "behaviorsSharedData": [
+        {
+          "name": "AdvancedWheelGrip",
+          "type": "AdvancedWheelGrip::AdvancedWheelGrip"
+        },
+        {
+          "name": "Anchor",
+          "type": "AnchorBehavior::AnchorBehavior"
+        },
+        {
+          "name": "Animation",
+          "type": "AnimatableCapability::AnimatableBehavior"
+        },
+        {
+          "name": "Collectible",
+          "type": "Collectible::Collectible"
+        },
+        {
+          "name": "Effect",
+          "type": "EffectCapability::EffectBehavior"
+        },
+        {
+          "name": "Flippable",
+          "type": "FlippableCapability::FlippableBehavior"
+        },
+        {
+          "name": "InOnScreen",
+          "type": "IsOnScreen::InOnScreen"
+        },
+        {
+          "name": "Object3D",
+          "type": "Scene3D::Base3DBehavior"
+        },
+        {
+          "name": "Opacity",
+          "type": "OpacityCapability::OpacityBehavior"
+        },
+        {
+          "name": "Physics2",
+          "type": "Physics2::Physics2Behavior",
+          "gravityX": 0,
+          "gravityY": 0,
+          "scaleX": 100,
+          "scaleY": 100
+        },
+        {
+          "name": "PhysicsCar",
+          "type": "PhysicsCar::PhysicsCar"
+        },
+        {
+          "name": "Resizable",
+          "type": "ResizableCapability::ResizableBehavior"
+        },
+        {
+          "name": "Scale",
+          "type": "ScalableCapability::ScalableBehavior"
+        },
+        {
+          "name": "ShakeModel3D",
+          "type": "ShakeObject3D::ShakeModel3D",
+          "EasingFactor": 7.8167074e-317
+        },
+        {
+          "name": "Text",
+          "type": "TextContainerCapability::TextContainerBehavior"
+        },
+        {
+          "name": "ThirdPersonCamera",
+          "type": "ThirdPersonCamera::ThirdPersonCamera"
+        }
+      ]
+    },
+    {
+      "b": 0,
+      "disableInputWhenNotFocused": true,
+      "mangledName": "Leaderboard",
+      "name": "Leaderboard",
+      "r": 0,
+      "standardSortMethod": true,
+      "stopSoundsOnStartup": true,
+      "title": "",
+      "v": 0,
+      "uiSettings": {
+        "grid": true,
+        "gridType": "rectangular",
+        "gridWidth": 16,
+        "gridHeight": 16,
+        "gridOffsetX": 0,
+        "gridOffsetY": 0,
+        "gridColor": 16777215,
+        "gridAlpha": 0.21,
+        "snap": true,
+        "zoomFactor": 0.6022931537048508,
+        "windowMask": false
+      },
+      "objectsGroups": [],
+      "variables": [
+        {
+          "name": "LeaderboardScore",
+          "type": "string",
+          "value": ""
+        }
+      ],
+      "instances": [
+        {
+          "angle": 0,
+          "customSize": true,
+          "height": 64,
+          "layer": "",
+          "name": "Platform",
+          "persistentUuid": "c2c6a11c-e8f8-4ee6-aedb-369f62303136",
+          "width": 1152,
+          "x": -96,
+          "y": 512,
+          "zOrder": 14,
+          "numberProperties": [],
+          "stringProperties": [],
+          "initialVariables": []
+        },
+        {
+          "angle": 0,
+          "customSize": true,
+          "height": 896,
+          "layer": "3D",
+          "name": "Background",
+          "persistentUuid": "968c9c47-08c4-44eb-917a-906d2ecee36b",
+          "width": 1024,
+          "x": -32,
+          "y": -320,
+          "zOrder": 15,
+          "numberProperties": [],
+          "stringProperties": [],
+          "initialVariables": []
+        },
+        {
+          "angle": 0,
+          "customSize": false,
+          "height": 496,
+          "keepRatio": true,
+          "layer": "",
+          "name": "LeaderboardDialog",
+          "persistentUuid": "24fb9077-19e6-49ff-b3c3-65ff89f4364f",
+          "width": 704,
+          "x": 304,
+          "y": 128,
+          "zOrder": 38,
+          "numberProperties": [],
+          "stringProperties": [],
+          "initialVariables": []
+        }
+      ],
+      "objects": [
+        {
+          "assetStoreId": "",
+          "name": "LeaderboardDialog",
+          "type": "LeaderboardDialog::LeaderboardDialog",
+          "variables": [],
+          "effects": [],
+          "behaviors": [
+            {
+              "name": "Anchor",
+              "type": "AnchorBehavior::AnchorBehavior",
+              "bottomEdgeAnchor": 0,
+              "leftEdgeAnchor": 4,
+              "relativeToOriginalWindowSize": true,
+              "rightEdgeAnchor": 4,
+              "topEdgeAnchor": 0,
+              "useLegacyBottomAndRightAnchors": false
+            }
+          ],
+          "content": {
+            "LeaderboardId": "0d5ac561-afa4-45dc-bf58-561d557f7954",
+            "DefaultPlayerName": ""
+          }
+        },
+        {
+          "assetStoreId": "0e3ee52ba8369c68fe75e3b34a056876ba39b1d0dbb9de6e83e1f2ea0e6f94ac",
+          "name": "Coin",
+          "type": "Scene3D::Model3DObject",
+          "variables": [
+            {
+              "folded": true,
+              "name": "RotationX",
+              "type": "number",
+              "value": 0
+            },
+            {
+              "folded": true,
+              "name": "RotationY",
+              "type": "number",
+              "value": 0
+            },
+            {
+              "folded": true,
+              "name": "RotationZ",
+              "type": "number",
+              "value": 0
+            }
+          ],
+          "effects": [],
+          "behaviors": [
+            {
+              "name": "ScreenWrap",
+              "type": "ScreenWrap::ScreenWrap",
+              "HorizontalWrapping": false,
+              "VerticalWrapping": true,
+              "BorderTop": 0,
+              "BorderLeft": 0,
+              "BorderRight": 0,
+              "BorderBottom": 0,
+              "TriggerOffset": 500
+            }
+          ],
+          "content": {
+            "centerLocation": "ModelOrigin",
+            "crossfadeDuration": 0,
+            "depth": 64,
+            "height": 64,
+            "keepAspectRatio": true,
+            "materialType": "StandardWithoutMetalness",
+            "modelResourceName": "Coin.glb",
+            "originLocation": "ModelOrigin",
+            "rotationX": 0,
+            "rotationY": 0,
+            "rotationZ": 0,
+            "width": 64,
+            "animations": []
+          }
+        }
+      ],
+      "objectsFolderStructure": {
+        "folderName": "__ROOT",
+        "children": [
+          {
+            "folderName": "UI",
+            "quickCustomizationVisibility": "hidden",
+            "children": [
+              {
+                "objectName": "LeaderboardDialog"
+              }
+            ]
+          },
+          {
+            "objectName": "Coin"
+          }
+        ]
+      },
+      "events": [
+        {
+          "type": "BuiltinCommonInstructions::Comment",
+          "color": {
+            "b": 109,
+            "g": 230,
+            "r": 255,
+            "textB": 0,
+            "textG": 0,
+            "textR": 0
+          },
+          "comment": "Handle leaderboards.\nTo create a leaderboard, make sure your game is registered in Home > Profile > Games Dashboard and then, click on \"Manage game\" > Leaderboards. When a leaderboard is created, it should be available in the actions."
+        },
+        {
+          "type": "BuiltinCommonInstructions::Standard",
+          "conditions": [
+            {
+              "type": {
+                "value": "DepartScene"
+              },
+              "parameters": [
+                ""
+              ]
+            }
+          ],
+          "actions": [
+            {
+              "type": {
+                "value": "LeaderboardDialog::LeaderboardDialog::SetScore"
+              },
+              "parameters": [
+                "LeaderboardDialog",
+                "=",
+                "Score",
+                ""
+              ]
+            },
+            {
+              "type": {
+                "value": "LeaderboardDialog::LeaderboardDialog::SetDefaultPlayerName"
+              },
+              "parameters": [
+                "LeaderboardDialog",
+                "=",
+                "PlayerName",
+                ""
+              ]
+            }
+          ]
+        },
+        {
+          "type": "BuiltinCommonInstructions::Standard",
+          "conditions": [
+            {
+              "type": {
+                "value": "LeaderboardDialog::LeaderboardDialog::IsRestartClicked"
+              },
+              "parameters": [
+                "LeaderboardDialog",
+                ""
+              ]
+            }
+          ],
+          "actions": [
+            {
+              "type": {
+                "value": "Scene"
+              },
+              "parameters": [
+                "",
+                "\"Game\"",
+                ""
+              ]
+            }
+          ]
+        },
+        {
+          "type": "BuiltinCommonInstructions::Standard",
+          "conditions": [
+            {
+              "type": {
+                "value": "LeaderboardDialog::LeaderboardDialog::IsScoreSubmitted"
+              },
+              "parameters": [
+                "LeaderboardDialog",
+                ""
+              ]
+            }
+          ],
+          "actions": [
+            {
+              "type": {
+                "value": "SetStringVariable"
+              },
+              "parameters": [
+                "PlayerName",
+                "=",
+                "LeaderboardDialog.PlayerName()"
+              ]
+            },
+            {
+              "type": {
+                "value": "Scene"
+              },
+              "parameters": [
+                "",
+                "\"Game\"",
+                ""
+              ]
+            }
+          ]
+        },
+        {
+          "colorB": 228,
+          "colorG": 176,
+          "colorR": 74,
+          "creationTime": 0,
+          "name": "Coins animation",
+          "source": "",
+          "type": "BuiltinCommonInstructions::Group",
+          "events": [
+            {
+              "type": "BuiltinCommonInstructions::Standard",
+              "conditions": [
+                {
+                  "type": {
+                    "value": "DepartScene"
+                  },
+                  "parameters": [
+                    ""
+                  ]
+                }
+              ],
+              "actions": [],
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Repeat",
+                  "repeatExpression": "100",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "Create"
+                      },
+                      "parameters": [
+                        "",
+                        "Coin",
+                        "RandomInRange(CameraBorderLeft(), CameraBorderRight())",
+                        "RandomInRange(-500 + CameraBorderTop(), CameraBorderBottom() + 500)",
+                        "\"3D\""
+                      ]
+                    },
+                    {
+                      "type": {
+                        "value": "SetNumberObjectVariable"
+                      },
+                      "parameters": [
+                        "Coin",
+                        "RotationX",
+                        "=",
+                        "RandomFloatInRange(20, 45)"
+                      ]
+                    },
+                    {
+                      "type": {
+                        "value": "SetNumberObjectVariable"
+                      },
+                      "parameters": [
+                        "Coin",
+                        "RotationY",
+                        "=",
+                        "RandomFloatInRange(20, 45)"
+                      ]
+                    },
+                    {
+                      "type": {
+                        "value": "SetNumberObjectVariable"
+                      },
+                      "parameters": [
+                        "Coin",
+                        "RotationZ",
+                        "=",
+                        "RandomFloatInRange(20, 45)"
+                      ]
+                    },
+                    {
+                      "type": {
+                        "value": "AddForceXY"
+                      },
+                      "parameters": [
+                        "Coin",
+                        "0",
+                        "200",
+                        "1"
+                      ]
+                    },
+                    {
+                      "type": {
+                        "value": "Scene3D::Base3DBehavior::SetZ"
+                      },
+                      "parameters": [
+                        "Coin",
+                        "Object3D",
+                        "=",
+                        "RandomFloatInRange(-1000, -32)"
+                      ]
+                    },
+                    {
+                      "type": {
+                        "value": "Scene3D::Base3DBehavior::SetRotationX"
+                      },
+                      "parameters": [
+                        "Coin",
+                        "Object3D",
+                        "=",
+                        "RandomFloat(360)"
+                      ]
+                    },
+                    {
+                      "type": {
+                        "value": "Scene3D::Base3DBehavior::SetRotationY"
+                      },
+                      "parameters": [
+                        "Coin",
+                        "Object3D",
+                        "=",
+                        "RandomFloat(360)"
+                      ]
+                    }
+                  ]
+                }
+              ]
+            },
+            {
+              "type": "BuiltinCommonInstructions::Standard",
+              "conditions": [],
+              "actions": [
+                {
+                  "type": {
+                    "value": "Scene3D::Base3DBehavior::SetRotationX"
+                  },
+                  "parameters": [
+                    "Coin",
+                    "Object3D",
+                    "+",
+                    "Coin.RotationX * TimeDelta()"
+                  ]
+                },
+                {
+                  "type": {
+                    "value": "Scene3D::Base3DBehavior::SetRotationY"
+                  },
+                  "parameters": [
+                    "Coin",
+                    "Object3D",
+                    "+",
+                    "Coin.RotationY * TimeDelta()"
+                  ]
+                },
+                {
+                  "type": {
+                    "value": "SetAngle"
+                  },
+                  "parameters": [
+                    "Coin",
+                    "+",
+                    "Coin.RotationZ * TimeDelta()"
+                  ]
+                }
+              ]
+            }
+          ],
+          "parameters": []
+        }
+      ],
+      "layers": [
+        {
+          "ambientLightColorB": 167797870,
+          "ambientLightColorG": 6032144,
+          "ambientLightColorR": 8563600,
+          "camera3DFarPlaneDistance": 10000,
+          "camera3DFieldOfView": 45,
+          "camera3DNearPlaneDistance": 0.1,
+          "cameraType": "perspective",
+          "followBaseLayerCamera": false,
+          "isLightingLayer": false,
+          "isLocked": false,
+          "name": "3D",
+          "renderingType": "3d",
+          "visibility": true,
+          "cameras": [],
+          "effects": [
+            {
+              "effectType": "Scene3D::HemisphereLight",
+              "name": "Light",
+              "doubleParameters": {
+                "elevation": 90,
+                "intensity": 0.5,
+                "rotation": 0
+              },
+              "stringParameters": {
+                "groundColor": "127;127;127",
+                "skyColor": "255;255;255",
+                "top": "Y-"
+              },
+              "booleanParameters": {}
+            }
+          ]
+        },
+        {
+          "ambientLightColorB": 3,
+          "ambientLightColorG": 134217728,
+          "ambientLightColorR": 1597197633,
+          "camera3DFarPlaneDistance": 10000,
+          "camera3DFieldOfView": 45,
+          "camera3DNearPlaneDistance": 0.1,
+          "cameraType": "perspective",
+          "followBaseLayerCamera": false,
+          "isLightingLayer": false,
+          "isLocked": false,
+          "name": "",
+          "renderingType": "2d",
+          "visibility": true,
+          "cameras": [
+            {
+              "defaultSize": true,
+              "defaultViewport": true,
+              "height": 0,
+              "viewportBottom": 1,
+              "viewportLeft": 0,
+              "viewportRight": 1,
+              "viewportTop": 0,
+              "width": 0
+            }
+          ],
+          "effects": []
+        }
+      ],
+      "behaviorsSharedData": [
+        {
+          "name": "Anchor",
+          "type": "AnchorBehavior::AnchorBehavior"
+        },
+        {
+          "name": "Animation",
+          "type": "AnimatableCapability::AnimatableBehavior"
+        },
+        {
+          "name": "Effect",
+          "type": "EffectCapability::EffectBehavior"
+        },
+        {
+          "name": "Flippable",
+          "type": "FlippableCapability::FlippableBehavior"
+        },
+        {
+          "name": "Object3D",
+          "type": "Scene3D::Base3DBehavior"
+        },
+        {
+          "name": "Opacity",
+          "type": "OpacityCapability::OpacityBehavior"
+        },
+        {
+          "name": "Resizable",
+          "type": "ResizableCapability::ResizableBehavior"
+        },
+        {
+          "name": "Scale",
+          "type": "ScalableCapability::ScalableBehavior"
+        },
+        {
+          "name": "ScreenWrap",
+          "type": "ScreenWrap::ScreenWrap"
+        }
+      ]
+    }
+  ],
+  "externalEvents": [],
+  "eventsFunctionsExtensions": [
+    {
+      "author": "",
+      "category": "Input",
+      "extensionNamespace": "",
+      "fullName": "Screen Orientation Checker",
+      "helpPath": "",
+      "iconUrl": "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz48IURPQ1RZUEUgc3ZnIFBVQkxJQyAiLS8vVzNDLy9EVEQgU1ZHIDEuMS8vRU4iICJodHRwOi8vd3d3LnczLm9yZy9HcmFwaGljcy9TVkcvMS4xL0RURC9zdmcxMS5kdGQiPjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgdmVyc2lvbj0iMS4xIiBpZD0ibWRpLXNjcmVlbi1yb3RhdGlvbiIgd2lkdGg9IjI0IiBoZWlnaHQ9IjI0IiB2aWV3Qm94PSIwIDAgMjQgMjQiPjxwYXRoIGQ9Ik03LjUsMjEuNUM0LjI1LDE5Ljk0IDEuOTEsMTYuNzYgMS41NSwxM0gwLjA1QzAuNTYsMTkuMTYgNS43MSwyNCAxMiwyNEwxMi42NiwyMy45N0w4Ljg1LDIwLjE2TTE0LjgzLDIxLjE5TDIuODEsOS4xN0w5LjE3LDIuODFMMjEuMTksMTQuODNNMTAuMjMsMS43NUM5LjY0LDEuMTYgOC42OSwxLjE2IDguMTEsMS43NUwxLjc1LDguMTFDMS4xNiw4LjcgMS4xNiw5LjY1IDEuNzUsMTAuMjNMMTMuNzcsMjIuMjVDMTQuMzYsMjIuODQgMTUuMzEsMjIuODQgMTUuODksMjIuMjVMMjIuMjUsMTUuODlDMjIuODQsMTUuMyAyMi44NCwxNC4zNSAyMi4yNSwxMy43N0wxMC4yMywxLjc1TTE2LjUsMi41QzE5Ljc1LDQuMDcgMjIuMDksNy4yNCAyMi40NSwxMUgyMy45NUMyMy40NCw0Ljg0IDE4LjI5LDAgMTIsMEwxMS4zNCwwLjAzTDE1LjE1LDMuODRMMTYuNSwyLjVaIiAvPjwvc3ZnPg==",
+      "name": "ScreenOrientationChecker",
+      "previewIconUrl": "https://asset-resources.gdevelop.io/public-resources/Icons/0126888931a4a4f82bb2824df9f096347ace1c47f510c44df42aa8dc9e49e24a_screen-rotation.svg",
+      "shortDescription": "Display a customizable screen asking the user to rotate their phone/tablet if not in the right orientation.",
+      "version": "0.0.2",
+      "description": "Display a customizable screen asking the user to rotate their phone/tablet if not in the right orientation.",
+      "origin": {
+        "identifier": "ScreenOrientationChecker",
+        "name": "gdevelop-extension-store"
+      },
+      "tags": [
+        "screen",
+        "orientation"
+      ],
+      "authorIds": [
+        "wWP8BSlAW0UP4NeaHa2LcmmDzmH2"
+      ],
+      "dependencies": [],
+      "globalVariables": [
+        {
+          "name": "TargetOrientation",
+          "type": "string",
+          "value": ""
+        }
+      ],
+      "sceneVariables": [],
+      "eventsFunctions": [
+        {
+          "fullName": "Get game target orientation",
+          "functionType": "StringExpression",
+          "name": "ProjectOrientation",
+          "private": true,
+          "sentence": "",
+          "events": [
+            {
+              "type": "BuiltinCommonInstructions::JsCode",
+              "inlineCode": "eventsFunctionContext.returnValue = runtimeScene.getGame().getGameData().properties.orientation;",
+              "parameterObjects": "",
+              "useStrict": true,
+              "eventsSheetExpanded": false
+            }
+          ],
+          "expressionType": {
+            "type": "string"
+          },
+          "parameters": [],
+          "objectGroups": []
+        },
+        {
+          "fullName": "",
+          "functionType": "Action",
+          "name": "onFirstSceneLoaded",
+          "sentence": "",
+          "events": [
+            {
+              "type": "BuiltinCommonInstructions::Standard",
+              "conditions": [],
+              "actions": [
+                {
+                  "type": {
+                    "value": "SetStringVariable"
+                  },
+                  "parameters": [
+                    "TargetOrientation",
+                    "=",
+                    "ScreenOrientationChecker::ProjectOrientation()"
+                  ]
+                }
+              ]
+            }
+          ],
+          "parameters": [],
+          "objectGroups": []
+        }
+      ],
+      "eventsBasedBehaviors": [],
+      "eventsBasedObjects": [
+        {
+          "areaMaxX": 400,
+          "areaMaxY": 200,
+          "areaMaxZ": 0,
+          "areaMinX": 0,
+          "areaMinY": 0,
+          "areaMinZ": 0,
+          "defaultName": "",
+          "description": "Automatically display a screen asking the player to rotate their screen if needed - on mobile phones and tablets only. Set up Anchor behavior on this object so that top/bottom/left/right edges are anchored to the screen top/bottom/left/right.",
+          "fullName": "Screen Orientation Checker",
+          "isInnerAreaFollowingParentSize": true,
+          "isUsingLegacyInstancesRenderer": false,
+          "name": "ScreenOrientationChecker",
+          "eventsFunctions": [
+            {
+              "fullName": "",
+              "functionType": "Action",
+              "name": "doStepPostEvents",
+              "sentence": "",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Comment",
+                  "color": {
+                    "b": 109,
+                    "g": 230,
+                    "r": 255,
+                    "textB": 0,
+                    "textG": 0,
+                    "textR": 0
+                  },
+                  "comment": "Check if the screen must be shown"
+                },
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [
+                    {
+                      "type": {
+                        "value": "SystemInfo::IsMobile"
+                      },
+                      "parameters": []
+                    }
+                  ],
+                  "actions": [],
+                  "events": [
+                    {
+                      "type": "BuiltinCommonInstructions::Standard",
+                      "conditions": [
+                        {
+                          "type": {
+                            "value": "BuiltinCommonInstructions::CompareNumbers"
+                          },
+                          "parameters": [
+                            "SceneWindowWidth()",
+                            ">",
+                            "SceneWindowHeight()"
+                          ]
+                        }
+                      ],
+                      "actions": [],
+                      "events": [
+                        {
+                          "type": "BuiltinCommonInstructions::Standard",
+                          "conditions": [
+                            {
+                              "type": {
+                                "value": "StringVariable"
+                              },
+                              "parameters": [
+                                "TargetOrientation",
+                                "=",
+                                "\"portrait\""
+                              ]
+                            }
+                          ],
+                          "actions": [
+                            {
+                              "type": {
+                                "value": "ScreenOrientationChecker::ScreenOrientationChecker::SetPropertyIsShown"
+                              },
+                              "parameters": [
+                                "Object",
+                                "yes"
+                              ]
+                            }
+                          ]
+                        },
+                        {
+                          "type": "BuiltinCommonInstructions::Standard",
+                          "conditions": [
+                            {
+                              "type": {
+                                "value": "StringVariable"
+                              },
+                              "parameters": [
+                                "TargetOrientation",
+                                "!=",
+                                "\"portrait\""
+                              ]
+                            }
+                          ],
+                          "actions": [
+                            {
+                              "type": {
+                                "value": "ScreenOrientationChecker::ScreenOrientationChecker::SetPropertyIsShown"
+                              },
+                              "parameters": [
+                                "Object",
+                                "no"
+                              ]
+                            }
+                          ]
+                        }
+                      ]
+                    },
+                    {
+                      "type": "BuiltinCommonInstructions::Standard",
+                      "conditions": [
+                        {
+                          "type": {
+                            "value": "BuiltinCommonInstructions::CompareNumbers"
+                          },
+                          "parameters": [
+                            "SceneWindowWidth()",
+                            "<=",
+                            "SceneWindowHeight()"
+                          ]
+                        }
+                      ],
+                      "actions": [],
+                      "events": [
+                        {
+                          "type": "BuiltinCommonInstructions::Standard",
+                          "conditions": [
+                            {
+                              "type": {
+                                "value": "StringVariable"
+                              },
+                              "parameters": [
+                                "TargetOrientation",
+                                "=",
+                                "\"landscape\""
+                              ]
+                            }
+                          ],
+                          "actions": [
+                            {
+                              "type": {
+                                "value": "ScreenOrientationChecker::ScreenOrientationChecker::SetPropertyIsShown"
+                              },
+                              "parameters": [
+                                "Object",
+                                "yes"
+                              ]
+                            }
+                          ]
+                        },
+                        {
+                          "type": "BuiltinCommonInstructions::Standard",
+                          "conditions": [
+                            {
+                              "type": {
+                                "value": "StringVariable"
+                              },
+                              "parameters": [
+                                "TargetOrientation",
+                                "=",
+                                "\"portrait\""
+                              ]
+                            }
+                          ],
+                          "actions": [
+                            {
+                              "type": {
+                                "value": "ScreenOrientationChecker::ScreenOrientationChecker::SetPropertyIsShown"
+                              },
+                              "parameters": [
+                                "Object",
+                                "no"
+                              ]
+                            }
+                          ]
+                        }
+                      ]
+                    }
+                  ]
+                },
+                {
+                  "type": "BuiltinCommonInstructions::Comment",
+                  "color": {
+                    "b": 109,
+                    "g": 230,
+                    "r": 255,
+                    "textB": 0,
+                    "textG": 0,
+                    "textR": 0
+                  },
+                  "comment": "Show/hide the screen as needed"
+                },
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [
+                    {
+                      "type": {
+                        "value": "BuiltinCommonInstructions::Or"
+                      },
+                      "parameters": [],
+                      "subInstructions": [
+                        {
+                          "type": {
+                            "value": "ScreenOrientationChecker::ScreenOrientationChecker::PropertyIsShown"
+                          },
+                          "parameters": [
+                            "Object"
+                          ]
+                        },
+                        {
+                          "type": {
+                            "value": "ScreenOrientationChecker::ScreenOrientationChecker::PropertyIsForceShown"
+                          },
+                          "parameters": [
+                            "Object"
+                          ]
+                        }
+                      ]
+                    }
+                  ],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "PrimitiveDrawing::FillColor"
+                      },
+                      "parameters": [
+                        "BackgroundPainter",
+                        "BackgroundColor"
+                      ]
+                    },
+                    {
+                      "type": {
+                        "value": "PrimitiveDrawing::RoundedRectangle"
+                      },
+                      "parameters": [
+                        "BackgroundPainter",
+                        "Padding",
+                        "Padding",
+                        "Object.Width()-Padding",
+                        "Object.Height()-Padding",
+                        "CornerRadius"
+                      ]
+                    },
+                    {
+                      "type": {
+                        "value": "Montre"
+                      },
+                      "parameters": [
+                        "Object",
+                        ""
+                      ]
+                    }
+                  ]
+                },
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [
+                    {
+                      "type": {
+                        "inverted": true,
+                        "value": "ScreenOrientationChecker::ScreenOrientationChecker::PropertyIsShown"
+                      },
+                      "parameters": [
+                        "Object"
+                      ]
+                    },
+                    {
+                      "type": {
+                        "inverted": true,
+                        "value": "ScreenOrientationChecker::ScreenOrientationChecker::PropertyIsForceShown"
+                      },
+                      "parameters": [
+                        "Object"
+                      ]
+                    }
+                  ],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "Cache"
+                      },
+                      "parameters": [
+                        "Object"
+                      ]
+                    }
+                  ]
+                },
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "MettreY"
+                      },
+                      "parameters": [
+                        "Icon",
+                        "=",
+                        "Text.Y() - 100"
+                      ]
+                    }
+                  ]
+                },
+                {
+                  "type": "BuiltinCommonInstructions::Comment",
+                  "color": {
+                    "b": 109,
+                    "g": 230,
+                    "r": 255,
+                    "textB": 0,
+                    "textG": 0,
+                    "textR": 0
+                  },
+                  "comment": "Animate the icon"
+                },
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [
+                    {
+                      "type": {
+                        "inverted": true,
+                        "value": "Tween::Exists"
+                      },
+                      "parameters": [
+                        "Icon",
+                        "Tween",
+                        "\"Rotate\""
+                      ]
+                    },
+                    {
+                      "type": {
+                        "inverted": true,
+                        "value": "Tween::Exists"
+                      },
+                      "parameters": [
+                        "Icon",
+                        "Tween",
+                        "\"RotateBack\""
+                      ]
+                    }
+                  ],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "Tween::TweenBehavior::AddObjectAngleTween2"
+                      },
+                      "parameters": [
+                        "Icon",
+                        "Tween",
+                        "\"Rotate\"",
+                        "8",
+                        "\"easeInOutQuad\"",
+                        "2",
+                        ""
+                      ]
+                    }
+                  ]
+                },
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [
+                    {
+                      "type": {
+                        "value": "Tween::HasFinished"
+                      },
+                      "parameters": [
+                        "Icon",
+                        "Tween",
+                        "\"RotateBack\""
+                      ]
+                    }
+                  ],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "Tween::TweenBehavior::AddObjectAngleTween2"
+                      },
+                      "parameters": [
+                        "Icon",
+                        "Tween",
+                        "\"Rotate\"",
+                        "8",
+                        "\"easeInOutQuad\"",
+                        "2",
+                        ""
+                      ]
+                    },
+                    {
+                      "type": {
+                        "value": "Tween::RemoveTween"
+                      },
+                      "parameters": [
+                        "Icon",
+                        "Tween",
+                        "\"RotateBack\""
+                      ]
+                    }
+                  ]
+                },
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [
+                    {
+                      "type": {
+                        "value": "Tween::HasFinished"
+                      },
+                      "parameters": [
+                        "Icon",
+                        "Tween",
+                        "\"Rotate\""
+                      ]
+                    }
+                  ],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "Tween::TweenBehavior::AddObjectAngleTween2"
+                      },
+                      "parameters": [
+                        "Icon",
+                        "Tween",
+                        "\"RotateBack\"",
+                        "-8",
+                        "\"easeInOutQuad\"",
+                        "2",
+                        ""
+                      ]
+                    },
+                    {
+                      "type": {
+                        "value": "Tween::RemoveTween"
+                      },
+                      "parameters": [
+                        "Icon",
+                        "Tween",
+                        "\"Rotate\""
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "supplementaryInformation": "ScreenOrientationChecker::ScreenOrientationChecker",
+                  "type": "object"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "Check if the screen should be forced to be shown. Use this to test the screen in your game.",
+              "fullName": "Force show the screen",
+              "functionType": "Condition",
+              "group": "Screen Orientation Checker configuration",
+              "name": "IsForceShown",
+              "sentence": "_PARAM0_ is forced to be shown",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [
+                    {
+                      "type": {
+                        "value": "ScreenOrientationChecker::ScreenOrientationChecker::PropertyIsForceShown"
+                      },
+                      "parameters": [
+                        "Object"
+                      ]
+                    }
+                  ],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SetReturnBoolean"
+                      },
+                      "parameters": [
+                        "True"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "supplementaryInformation": "ScreenOrientationChecker::ScreenOrientationChecker",
+                  "type": "object"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "Change if the screen should be forced to be shown. Use this to test the screen in your game.",
+              "fullName": "Force show the screen",
+              "functionType": "Action",
+              "group": "Screen Orientation Checker configuration",
+              "name": "SetIsForceShown",
+              "sentence": "Force _PARAM0_ to be shown: _PARAM1_",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [
+                    {
+                      "type": {
+                        "inverted": true,
+                        "value": "GetArgumentAsBoolean"
+                      },
+                      "parameters": [
+                        "\"Value\""
+                      ]
+                    }
+                  ],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "ScreenOrientationChecker::ScreenOrientationChecker::SetPropertyIsForceShown"
+                      },
+                      "parameters": [
+                        "Object",
+                        "no"
+                      ]
+                    }
+                  ]
+                },
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [
+                    {
+                      "type": {
+                        "value": "GetArgumentAsBoolean"
+                      },
+                      "parameters": [
+                        "\"Value\""
+                      ]
+                    }
+                  ],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "ScreenOrientationChecker::ScreenOrientationChecker::SetPropertyIsForceShown"
+                      },
+                      "parameters": [
+                        "Object",
+                        "yes"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "supplementaryInformation": "ScreenOrientationChecker::ScreenOrientationChecker",
+                  "type": "object"
+                },
+                {
+                  "defaultValue": "yes",
+                  "description": "Force show the screen?",
+                  "name": "Value",
+                  "optional": true,
+                  "type": "yesorno"
+                }
+              ],
+              "objectGroups": []
+            }
+          ],
+          "propertyDescriptors": [
+            {
+              "value": "",
+              "type": "Boolean",
+              "label": "",
+              "description": "",
+              "group": "",
+              "extraInformation": [],
+              "hidden": true,
+              "name": "IsShown"
+            },
+            {
+              "value": "",
+              "type": "Boolean",
+              "label": "Force show the screen",
+              "description": "Use this to test the screen in your game",
+              "group": "",
+              "extraInformation": [],
+              "name": "IsForceShown"
+            },
+            {
+              "value": "5",
+              "type": "Number",
+              "unit": "Pixel",
+              "label": "Padding",
+              "description": "",
+              "group": "Appearance",
+              "extraInformation": [],
+              "name": "Padding"
+            },
+            {
+              "value": "10",
+              "type": "Number",
+              "unit": "Pixel",
+              "label": "Corner radius",
+              "description": "Corner radius for the background",
+              "group": "Appearance",
+              "extraInformation": [],
+              "name": "CornerRadius"
+            },
+            {
+              "value": "0;0;0",
+              "type": "Color",
+              "label": "Background color",
+              "description": "",
+              "group": "Appearance",
+              "extraInformation": [],
+              "name": "BackgroundColor"
+            }
+          ],
+          "objects": [
+            {
+              "assetStoreId": "",
+              "bold": true,
+              "italic": false,
+              "name": "Text",
+              "smoothed": true,
+              "type": "TextObject::Text",
+              "underlined": false,
+              "variables": [],
+              "effects": [],
+              "behaviors": [
+                {
+                  "name": "Anchor",
+                  "type": "AnchorBehavior::AnchorBehavior",
+                  "bottomEdgeAnchor": 0,
+                  "leftEdgeAnchor": 4,
+                  "relativeToOriginalWindowSize": true,
+                  "rightEdgeAnchor": 0,
+                  "topEdgeAnchor": 4,
+                  "useLegacyBottomAndRightAnchors": false
+                },
+                {
+                  "name": "Tween",
+                  "type": "Tween::TweenBehavior"
+                }
+              ],
+              "string": "Rotate screen to play",
+              "font": "",
+              "textAlignment": "center",
+              "characterSize": 30,
+              "color": {
+                "b": 255,
+                "g": 255,
+                "r": 255
+              },
+              "content": {
+                "bold": true,
+                "isOutlineEnabled": false,
+                "isShadowEnabled": false,
+                "italic": false,
+                "outlineColor": "255;255;255",
+                "outlineThickness": 2,
+                "shadowAngle": 90,
+                "shadowBlurRadius": 2,
+                "shadowColor": "0;0;0",
+                "shadowDistance": 4,
+                "shadowOpacity": 127,
+                "smoothed": true,
+                "underlined": false,
+                "text": "Rotate screen to play",
+                "font": "",
+                "textAlignment": "center",
+                "verticalTextAlignment": "top",
+                "characterSize": 30,
+                "color": "255;255;255"
+              }
+            },
+            {
+              "assetStoreId": "",
+              "name": "BackgroundPainter",
+              "type": "PrimitiveDrawing::Drawer",
+              "variables": [],
+              "effects": [],
+              "behaviors": [],
+              "fillOpacity": 255,
+              "outlineSize": 0,
+              "outlineOpacity": 255,
+              "absoluteCoordinates": true,
+              "clearBetweenFrames": true,
+              "antialiasing": "none",
+              "fillColor": {
+                "r": 0,
+                "g": 0,
+                "b": 0
+              },
+              "outlineColor": {
+                "r": 0,
+                "g": 0,
+                "b": 0
+              }
+            },
+            {
+              "adaptCollisionMaskAutomatically": true,
+              "assetStoreId": "",
+              "name": "Icon",
+              "type": "Sprite",
+              "updateIfNotVisible": false,
+              "variables": [],
+              "effects": [],
+              "behaviors": [
+                {
+                  "name": "Anchor",
+                  "type": "AnchorBehavior::AnchorBehavior",
+                  "bottomEdgeAnchor": 0,
+                  "leftEdgeAnchor": 4,
+                  "topEdgeAnchor": 4,
+                  "relativeToOriginalWindowSize": true,
+                  "rightEdgeAnchor": 0,
+                  "useLegacyBottomAndRightAnchors": false
+                },
+                {
+                  "name": "Tween",
+                  "type": "Tween::TweenBehavior"
+                }
+              ],
+              "animations": [
+                {
+                  "name": "",
+                  "useMultipleDirections": false,
+                  "directions": [
+                    {
+                      "looping": false,
+                      "timeBetweenFrames": 0.08,
+                      "sprites": [
+                        {
+                          "hasCustomCollisionMask": true,
+                          "image": "rotate-screen-icon.png",
+                          "points": [],
+                          "originPoint": {
+                            "name": "origine",
+                            "x": 52.5,
+                            "y": 56.5
+                          },
+                          "centerPoint": {
+                            "automatic": true,
+                            "name": "centre",
+                            "x": 0,
+                            "y": 0
+                          },
+                          "customCollisionMask": [
+                            [
+                              {
+                                "x": 0,
+                                "y": 1
+                              },
+                              {
+                                "x": 105,
+                                "y": 1
+                              },
+                              {
+                                "x": 105,
+                                "y": 113
+                              },
+                              {
+                                "x": 0,
+                                "y": 113
+                              }
+                            ]
+                          ]
+                        }
+                      ]
+                    }
+                  ]
+                }
+              ]
+            }
+          ],
+          "objectsFolderStructure": {
+            "folderName": "__ROOT",
+            "children": [
+              {
+                "objectName": "Text"
+              },
+              {
+                "objectName": "BackgroundPainter"
+              },
+              {
+                "objectName": "Icon"
+              }
+            ]
+          },
+          "objectsGroups": [],
+          "layers": [
+            {
+              "ambientLightColorB": 200,
+              "ambientLightColorG": 200,
+              "ambientLightColorR": 200,
+              "camera3DFarPlaneDistance": 10000,
+              "camera3DFieldOfView": 45,
+              "camera3DNearPlaneDistance": 3,
+              "cameraType": "",
+              "followBaseLayerCamera": false,
+              "isLightingLayer": false,
+              "isLocked": false,
+              "name": "",
+              "renderingType": "",
+              "visibility": true,
+              "cameras": [
+                {
+                  "defaultSize": true,
+                  "defaultViewport": true,
+                  "height": 0,
+                  "viewportBottom": 1,
+                  "viewportLeft": 0,
+                  "viewportRight": 1,
+                  "viewportTop": 0,
+                  "width": 0
+                }
+              ],
+              "effects": []
+            }
+          ],
+          "instances": [
+            {
+              "angle": 0,
+              "customSize": true,
+              "depth": 1,
+              "height": 35,
+              "keepRatio": true,
+              "layer": "",
+              "name": "Text",
+              "persistentUuid": "65c003ea-19c0-4f18-a189-a02a24378f35",
+              "width": 207,
+              "x": 104,
+              "y": 120,
+              "zOrder": 15,
+              "numberProperties": [],
+              "stringProperties": [],
+              "initialVariables": []
+            },
+            {
+              "angle": 0,
+              "customSize": false,
+              "height": 0,
+              "keepRatio": true,
+              "layer": "",
+              "name": "BackgroundPainter",
+              "persistentUuid": "49ff4576-6406-41de-a43b-1355529d61fe",
+              "width": 0,
+              "x": 38,
+              "y": 17,
+              "zOrder": 14,
+              "numberProperties": [],
+              "stringProperties": [],
+              "initialVariables": []
+            },
+            {
+              "angle": 0,
+              "customSize": false,
+              "height": 0,
+              "keepRatio": true,
+              "layer": "",
+              "name": "Icon",
+              "persistentUuid": "39cbfc66-13f4-43dc-b6ef-2bda83153277",
+              "width": 0,
+              "x": 204,
+              "y": 64,
+              "zOrder": 16,
+              "numberProperties": [],
+              "stringProperties": [],
+              "initialVariables": []
+            }
+          ]
+        }
+      ]
+    },
+    {
+      "author": "Bouh",
+      "category": "Input",
+      "extensionNamespace": "",
+      "fullName": "Gamepads (controllers)",
+      "helpPath": "/all-features/gamepad",
+      "iconUrl": "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz48IURPQ1RZUEUgc3ZnIFBVQkxJQyAiLS8vVzNDLy9EVEQgU1ZHIDEuMS8vRU4iICJodHRwOi8vd3d3LnczLm9yZy9HcmFwaGljcy9TVkcvMS4xL0RURC9zdmcxMS5kdGQiPjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgdmVyc2lvbj0iMS4xIiBpZD0ibWRpLWdhbWVwYWQtdmFyaWFudC1vdXRsaW5lIiB3aWR0aD0iMjQiIGhlaWdodD0iMjQiIHZpZXdCb3g9IjAgMCAyNCAyNCI+PHBhdGggZD0iTTYsOUg4VjExSDEwVjEzSDhWMTVINlYxM0g0VjExSDZWOU0xOC41LDlBMS41LDEuNSAwIDAsMSAyMCwxMC41QTEuNSwxLjUgMCAwLDEgMTguNSwxMkExLjUsMS41IDAgMCwxIDE3LDEwLjVBMS41LDEuNSAwIDAsMSAxOC41LDlNMTUuNSwxMkExLjUsMS41IDAgMCwxIDE3LDEzLjVBMS41LDEuNSAwIDAsMSAxNS41LDE1QTEuNSwxLjUgMCAwLDEgMTQsMTMuNUExLjUsMS41IDAgMCwxIDE1LjUsMTJNMTcsNUE3LDcgMCAwLDEgMjQsMTJBNyw3IDAgMCwxIDE3LDE5QzE1LjA0LDE5IDEzLjI3LDE4LjIgMTIsMTYuOUMxMC43MywxOC4yIDguOTYsMTkgNywxOUE3LDcgMCAwLDEgMCwxMkE3LDcgMCAwLDEgNyw1SDE3TTcsN0E1LDUgMCAwLDAgMiwxMkE1LDUgMCAwLDAgNywxN0M4LjY0LDE3IDEwLjA5LDE2LjIxIDExLDE1SDEzQzEzLjkxLDE2LjIxIDE1LjM2LDE3IDE3LDE3QTUsNSAwIDAsMCAyMiwxMkE1LDUgMCAwLDAgMTcsN0g3WiIgLz48L3N2Zz4=",
+      "name": "Gamepads",
+      "previewIconUrl": "https://resources.gdevelop-app.com/assets/Icons/gamepad-variant-outline.svg",
+      "shortDescription": "Add support for gamepads (or other controllers) to your game, giving access to information such as button presses, axis positions, trigger pressure, etc...",
+      "version": "0.6.3",
+      "description": [
+        "Add support for gamepads (or other controllers).",
+        "",
+        "It gives access to:",
+        "- button presses",
+        "- axis positions and force",
+        "- trigger pressure",
+        "- configurable deadzone",
+        "- vibration",
+        "- automatic mappers for platformer characters and top-down movement",
+        "",
+        "The Bomberman-like example handles 4 players with gamepads ([open the project online](https://editor.gdevelop.io/?project=example://goose-bomberman))."
+      ],
+      "origin": {
+        "identifier": "Gamepads",
+        "name": "gdevelop-extension-store"
+      },
+      "tags": [
+        "controllers",
+        "gamepads",
+        "joysticks",
+        "axis",
+        "xbox",
+        "ps4",
+        "platformer",
+        "platform",
+        "top-down"
+      ],
+      "authorIds": [
+        "2OwwM8ToR9dx9RJ2sAKTcrLmCB92",
+        "taRwmWxwAFYFL9yyBwB3cwBw0BO2",
+        "mnImQKdn8nQxwzkS5D6a1JB27V23"
+      ],
+      "dependencies": [],
+      "globalVariables": [],
+      "sceneVariables": [],
+      "eventsFunctions": [
+        {
+          "fullName": "",
+          "functionType": "Action",
+          "name": "onFirstSceneLoaded",
+          "sentence": "",
+          "events": [
+            {
+              "type": "BuiltinCommonInstructions::Standard",
+              "conditions": [],
+              "actions": []
+            },
+            {
+              "type": "BuiltinCommonInstructions::JsCode",
+              "inlineCode": [
+                "//Define an new private object javascript for the gamepad extension\r",
+                "gdjs._extensionController = {\r",
+                "    players: {\r",
+                "        0: { mapping: 'DEFAULT', lastButtonUsed: -1, deadzone: 0.2, previousFrameStateButtons: {}, rumble: {} },\r",
+                "        1: { mapping: 'DEFAULT', lastButtonUsed: -1, deadzone: 0.2, previousFrameStateButtons: {}, rumble: {} },\r",
+                "        2: { mapping: 'DEFAULT', lastButtonUsed: -1, deadzone: 0.2, previousFrameStateButtons: {}, rumble: {} },\r",
+                "        3: { mapping: 'DEFAULT', lastButtonUsed: -1, deadzone: 0.2, previousFrameStateButtons: {}, rumble: {} },\r",
+                "    },\r",
+                "    lastActiveController: -1, // Last active controller\r",
+                "    controllerButtonNames: { //Map associating controller button ids to button names\r",
+                "        \"XBOX\": {\r",
+                "            0: \"A\",\r",
+                "            1: \"B\",\r",
+                "            2: \"X\",\r",
+                "            3: \"Y\",\r",
+                "            4: \"LB\",\r",
+                "            5: \"RB\",\r",
+                "            6: \"LT\",\r",
+                "            7: \"RT\",\r",
+                "            8: \"BACK\",\r",
+                "            9: \"START\",\r",
+                "            10: \"CLICK_STICK_LEFT\",\r",
+                "            11: \"CLICK_STICK_RIGHT\",\r",
+                "            12: \"UP\",\r",
+                "            13: \"DOWN\",\r",
+                "            14: \"LEFT\",\r",
+                "            15: \"RIGHT\",\r",
+                "            16: \"NONE\",\r",
+                "            17: \"NONE\"\r",
+                "        },\r",
+                "        \"PS4\": {\r",
+                "            0: \"CROSS\",\r",
+                "            1: \"CIRCLE\",\r",
+                "            2: \"SQUARE\",\r",
+                "            3: \"TRIANGLE\",\r",
+                "            4: \"L1\",\r",
+                "            5: \"R1\",\r",
+                "            6: \"L2\",\r",
+                "            7: \"R2\",\r",
+                "            8: \"SHARE\",\r",
+                "            9: \"OPTIONS\",\r",
+                "            10: \"CLICK_STICK_LEFT\",\r",
+                "            11: \"CLICK_STICK_RIGHT\",\r",
+                "            12: \"UP\",\r",
+                "            13: \"DOWN\",\r",
+                "            14: \"LEFT\",\r",
+                "            15: \"RIGHT\",\r",
+                "            16: \"PS_BUTTON\",\r",
+                "            17: \"CLICK_TOUCHPAD\"\r",
+                "        }\r",
+                "    }\r",
+                "};\r",
+                "\r",
+                "gdjs._extensionController.getInputString = function (type, buttonId) {\r",
+                "    const controllerButtonNames = gdjs._extensionController.controllerButtonNames;\r",
+                "    if (controllerButtonNames[type] !== undefined) {\r",
+                "        return controllerButtonNames[type][buttonId];\r",
+                "    }\r",
+                "\r",
+                "    return \"UNKNOWN_BUTTON\";\r",
+                "}\r",
+                "\r",
+                "gdjs._extensionController.axisToAngle = function (deltaX, deltaY) {\r",
+                "    const rad = Math.atan2(deltaY, deltaX);\r",
+                "    const deg = rad * (180 / Math.PI);\r",
+                "    return deg;\r",
+                "}\r",
+                "\r",
+                "gdjs._extensionController.isXbox = function (gamepad) {\r",
+                "    return (gamepad ? (\r",
+                "        gamepad.id.toUpperCase().indexOf(\"XBOX\") !== -1\r",
+                "        // \"XINPUT\" cannot be used to check if it is a xbox controller is just a generic\r",
+                "        // name reported in Firefox corresponding to the driver being used by the controller\r",
+                "        // https://gamefaqs.gamespot.com/boards/916373-pc/73341312?page=1\r",
+                "    ) : false);\r",
+                "}\r",
+                "\r",
+                "//Returns the new value taking into account the dead zone for the player_ID given\r",
+                "gdjs._extensionController.getNormalizedAxisValue = function (v, player_ID) {\r",
+                "    //    gdjs._extensionController = gdjs._extensionController || { deadzone: 0.2 };\r",
+                "\r",
+                "    // Anything smaller than this is assumed to be 0,0\r",
+                "    const DEADZONE = gdjs._extensionController.players[player_ID].deadzone;\r",
+                "\r",
+                "    if (Math.abs(v) < DEADZONE) {\r",
+                "        // In the dead zone, set to 0\r",
+                "        v = 0;\r",
+                "\r",
+                "        if (v == null) {\r",
+                "            return 0;\r",
+                "        } else {\r",
+                "            return v;\r",
+                "        }\r",
+                "\r",
+                "    } else {\r",
+                "        // We're outside the dead zone, but we'd like to smooth\r",
+                "        // this value out so it still runs nicely between 0..1.\r",
+                "        // That is, we don't want it to jump suddenly from 0 to\r",
+                "        // DEADZONE.\r",
+                "\r",
+                "        // Remap v from\r",
+                "        //    DEADZONE..1 to 0..(1-DEADZONE)\r",
+                "        // or from\r",
+                "        //    -1..-DEADZONE to -(1-DEADZONE)..0\r",
+                "\r",
+                "        v = v - Math.sign(v) * DEADZONE;\r",
+                "\r",
+                "        // Remap v from\r",
+                "        //    0..(1-DEADZONE) to 0..1\r",
+                "        // or from\r",
+                "        //    -(1-DEADZONE)..0 to -1..0\r",
+                "\r",
+                "        return v / (1 - DEADZONE);\r",
+                "    }\r",
+                "};"
+              ],
+              "parameterObjects": "",
+              "useStrict": true,
+              "eventsSheetExpanded": true
+            }
+          ],
+          "parameters": [],
+          "objectGroups": []
+        },
+        {
+          "fullName": "",
+          "functionType": "Action",
+          "name": "onScenePostEvents",
+          "sentence": "",
+          "events": [
+            {
+              "type": "BuiltinCommonInstructions::Standard",
+              "conditions": [],
+              "actions": []
+            },
+            {
+              "type": "BuiltinCommonInstructions::JsCode",
+              "inlineCode": [
+                "//Each time a player press a button i save the last button pressed for the next frame",
+                "/** @type {Gamepad[]} */",
+                "const gamepads = navigator.getGamepads ? navigator.getGamepads() : (navigator.webkitGetGamepads ? navigator.webkitGetGamepads() : []);",
+                "",
+                "//Get function parameter",
+                "let countPlayers = Object.keys(gdjs._extensionController.players).length;",
+                "",
+                "//Repeat for each players",
+                "for (let i = 0; i < countPlayers; i++) {",
+                "    let gamepad = gamepads[i]; // Get the gamepad of the player",
+                "",
+                "    //We have to keep this condition because if the user hasn't plugged in his controller yet, we can't get the controller in the gamepad variable.",
+                "    if (gamepad == null) {",
+                "        continue;",
+                "    }",
+                "",
+                "    for (let b = 0; b < Object.keys(gamepad.buttons).length; b++) { //For each buttons",
+                "        if (gamepad.buttons[b].pressed) { //One of them is pressed",
+                "            gdjs._extensionController.players[i].lastButtonUsed = b; //Save the button pressed",
+                "",
+                "            //Save the state of the button for the next frame.",
+                "            gdjs._extensionController.players[i].previousFrameStateButtons[b] = { pressed: true };",
+                "",
+                "            // Update Last Active Controller",
+                "            gdjs._extensionController.lastActiveController = i;",
+                "        } else {",
+                "            gdjs._extensionController.players[i].previousFrameStateButtons[b] = { pressed: false };",
+                "        }",
+                "    }",
+                "",
+                "",
+                "    gdjs._extensionController.players[i].rumble.elapsedTime += runtimeScene.getElapsedTime(runtimeScene) / 1000;",
+                "    if (",
+                "        gdjs._extensionController.players[i].rumble.duration - gdjs._extensionController.players[i].rumble.elapsedTime <= 0 &&",
+                "        (gdjs._extensionController.players[i].rumble.weakMagnitude || gdjs._extensionController.players[i].rumble.strongMagnitude)",
+                "    ) {",
+                "        gdjs._extensionController.players[i].rumble.weakMagnitude = 0;",
+                "        gdjs._extensionController.players[i].rumble.strongMagnitude = 0;",
+                "    }",
+                "",
+                "",
+                "}",
+                ""
+              ],
+              "parameterObjects": "",
+              "useStrict": true,
+              "eventsSheetExpanded": true
+            }
+          ],
+          "parameters": [],
+          "objectGroups": []
+        },
+        {
+          "fullName": "Accelerated speed",
+          "functionType": "Expression",
+          "name": "AcceleratedSpeed",
+          "private": true,
+          "sentence": "",
+          "events": [
+            {
+              "type": "BuiltinCommonInstructions::Standard",
+              "conditions": [],
+              "actions": [
+                {
+                  "type": {
+                    "value": "SetNumberVariable"
+                  },
+                  "parameters": [
+                    "AcceleratedSpeed",
+                    "=",
+                    "CurrentSpeed"
+                  ]
+                }
+              ],
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [
+                    {
+                      "type": {
+                        "value": "CompareArgumentAsNumber"
+                      },
+                      "parameters": [
+                        "\"TargetedSpeed\"",
+                        "<",
+                        "0"
+                      ]
+                    }
+                  ],
+                  "actions": [],
+                  "events": [
+                    {
+                      "type": "BuiltinCommonInstructions::Comment",
+                      "color": {
+                        "b": 109,
+                        "g": 230,
+                        "r": 255,
+                        "textB": 0,
+                        "textG": 0,
+                        "textR": 0
+                      },
+                      "comment": "Reduce the speed to match the stick force."
+                    },
+                    {
+                      "type": "BuiltinCommonInstructions::Standard",
+                      "conditions": [
+                        {
+                          "type": {
+                            "value": "CompareArgumentAsNumber"
+                          },
+                          "parameters": [
+                            "\"CurrentSpeed\"",
+                            "<",
+                            "TargetedSpeed"
+                          ]
+                        }
+                      ],
+                      "actions": [
+                        {
+                          "type": {
+                            "value": "SetNumberVariable"
+                          },
+                          "parameters": [
+                            "AcceleratedSpeed",
+                            "=",
+                            "min(TargetedSpeed, CurrentSpeed + Acceleration * TimeDelta())"
+                          ]
+                        }
+                      ]
+                    },
+                    {
+                      "type": "BuiltinCommonInstructions::Standard",
+                      "conditions": [
+                        {
+                          "type": {
+                            "value": "CompareArgumentAsNumber"
+                          },
+                          "parameters": [
+                            "\"CurrentSpeed\"",
+                            ">",
+                            "TargetedSpeed"
+                          ]
+                        },
+                        {
+                          "type": {
+                            "value": "CompareArgumentAsNumber"
+                          },
+                          "parameters": [
+                            "\"CurrentSpeed\"",
+                            "<",
+                            "0"
+                          ]
+                        }
+                      ],
+                      "actions": [
+                        {
+                          "type": {
+                            "value": "SetNumberVariable"
+                          },
+                          "parameters": [
+                            "AcceleratedSpeed",
+                            "-",
+                            "Acceleration * TimeDelta()"
+                          ]
+                        }
+                      ]
+                    },
+                    {
+                      "type": "BuiltinCommonInstructions::Comment",
+                      "color": {
+                        "b": 109,
+                        "g": 230,
+                        "r": 255,
+                        "textB": 0,
+                        "textG": 0,
+                        "textR": 0
+                      },
+                      "comment": "Turn back at least as fast as it would stop."
+                    },
+                    {
+                      "type": "BuiltinCommonInstructions::Standard",
+                      "conditions": [
+                        {
+                          "type": {
+                            "value": "CompareArgumentAsNumber"
+                          },
+                          "parameters": [
+                            "\"CurrentSpeed\"",
+                            ">=",
+                            "0"
+                          ]
+                        }
+                      ],
+                      "actions": [
+                        {
+                          "type": {
+                            "value": "SetNumberVariable"
+                          },
+                          "parameters": [
+                            "AcceleratedSpeed",
+                            "=",
+                            "max(TargetedSpeed, CurrentSpeed - max(Acceleration , Deceleration) * TimeDelta())"
+                          ]
+                        }
+                      ]
+                    }
+                  ]
+                },
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [
+                    {
+                      "type": {
+                        "value": "CompareArgumentAsNumber"
+                      },
+                      "parameters": [
+                        "\"TargetedSpeed\"",
+                        ">",
+                        "0"
+                      ]
+                    }
+                  ],
+                  "actions": [],
+                  "events": [
+                    {
+                      "type": "BuiltinCommonInstructions::Comment",
+                      "color": {
+                        "b": 109,
+                        "g": 230,
+                        "r": 255,
+                        "textB": 0,
+                        "textG": 0,
+                        "textR": 0
+                      },
+                      "comment": "Reduce the speed to match the stick force."
+                    },
+                    {
+                      "type": "BuiltinCommonInstructions::Standard",
+                      "conditions": [
+                        {
+                          "type": {
+                            "value": "CompareArgumentAsNumber"
+                          },
+                          "parameters": [
+                            "\"CurrentSpeed\"",
+                            ">",
+                            "TargetedSpeed"
+                          ]
+                        }
+                      ],
+                      "actions": [
+                        {
+                          "type": {
+                            "value": "SetNumberVariable"
+                          },
+                          "parameters": [
+                            "AcceleratedSpeed",
+                            "=",
+                            "max(TargetedSpeed, CurrentSpeed - Acceleration * TimeDelta())"
+                          ]
+                        }
+                      ]
+                    },
+                    {
+                      "type": "BuiltinCommonInstructions::Standard",
+                      "conditions": [
+                        {
+                          "type": {
+                            "value": "CompareArgumentAsNumber"
+                          },
+                          "parameters": [
+                            "\"CurrentSpeed\"",
+                            "<",
+                            "TargetedSpeed"
+                          ]
+                        },
+                        {
+                          "type": {
+                            "value": "CompareArgumentAsNumber"
+                          },
+                          "parameters": [
+                            "\"CurrentSpeed\"",
+                            ">",
+                            "0"
+                          ]
+                        }
+                      ],
+                      "actions": [
+                        {
+                          "type": {
+                            "value": "SetNumberVariable"
+                          },
+                          "parameters": [
+                            "AcceleratedSpeed",
+                            "+",
+                            "Acceleration * TimeDelta()"
+                          ]
+                        }
+                      ]
+                    },
+                    {
+                      "type": "BuiltinCommonInstructions::Comment",
+                      "color": {
+                        "b": 109,
+                        "g": 230,
+                        "r": 255,
+                        "textB": 0,
+                        "textG": 0,
+                        "textR": 0
+                      },
+                      "comment": "Turn back at least as fast as it would stop."
+                    },
+                    {
+                      "type": "BuiltinCommonInstructions::Standard",
+                      "conditions": [
+                        {
+                          "type": {
+                            "value": "CompareArgumentAsNumber"
+                          },
+                          "parameters": [
+                            "\"CurrentSpeed\"",
+                            "<=",
+                            "0"
+                          ]
+                        }
+                      ],
+                      "actions": [
+                        {
+                          "type": {
+                            "value": "SetNumberVariable"
+                          },
+                          "parameters": [
+                            "AcceleratedSpeed",
+                            "=",
+                            "min(TargetedSpeed, CurrentSpeed + max(Acceleration , Deceleration) * TimeDelta())"
+                          ]
+                        }
+                      ]
+                    }
+                  ]
+                },
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [
+                    {
+                      "type": {
+                        "value": "CompareArgumentAsNumber"
+                      },
+                      "parameters": [
+                        "\"TargetedSpeed\"",
+                        "=",
+                        "0"
+                      ]
+                    }
+                  ],
+                  "actions": [],
+                  "events": [
+                    {
+                      "type": "BuiltinCommonInstructions::Standard",
+                      "conditions": [
+                        {
+                          "type": {
+                            "value": "CompareArgumentAsNumber"
+                          },
+                          "parameters": [
+                            "\"CurrentSpeed\"",
+                            "<",
+                            "0"
+                          ]
+                        }
+                      ],
+                      "actions": [
+                        {
+                          "type": {
+                            "value": "SetNumberVariable"
+                          },
+                          "parameters": [
+                            "AcceleratedSpeed",
+                            "=",
+                            "min(0, CurrentSpeed + Acceleration * TimeDelta())"
+                          ]
+                        }
+                      ]
+                    },
+                    {
+                      "type": "BuiltinCommonInstructions::Standard",
+                      "conditions": [
+                        {
+                          "type": {
+                            "value": "CompareArgumentAsNumber"
+                          },
+                          "parameters": [
+                            "\"CurrentSpeed\"",
+                            ">",
+                            "0"
+                          ]
+                        }
+                      ],
+                      "actions": [
+                        {
+                          "type": {
+                            "value": "SetNumberVariable"
+                          },
+                          "parameters": [
+                            "AcceleratedSpeed",
+                            "=",
+                            "max(0, CurrentSpeed - Acceleration * TimeDelta())"
+                          ]
+                        }
+                      ]
+                    }
+                  ]
+                },
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SetReturnNumber"
+                      },
+                      "parameters": [
+                        "clamp(AcceleratedSpeed, -SpeedMax, SpeedMax)"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "variables": [
+                {
+                  "name": "AcceleratedSpeed",
+                  "type": "number",
+                  "value": 0
+                }
+              ]
+            }
+          ],
+          "expressionType": {
+            "type": "expression"
+          },
+          "parameters": [
+            {
+              "description": "Current speed",
+              "name": "CurrentSpeed",
+              "type": "expression"
+            },
+            {
+              "description": "Targeted speed",
+              "name": "TargetedSpeed",
+              "type": "expression"
+            },
+            {
+              "description": "Max speed",
+              "name": "SpeedMax",
+              "type": "expression"
+            },
+            {
+              "description": "Acceleration",
+              "name": "Acceleration",
+              "type": "expression"
+            },
+            {
+              "description": "Deceleration",
+              "name": "Deceleration",
+              "type": "expression"
+            }
+          ],
+          "objectGroups": []
+        },
+        {
+          "description": "Get the value of the pressure on a gamepad trigger.",
+          "fullName": "Pressure on a gamepad trigger",
+          "functionType": "Expression",
+          "name": "TriggerPressure",
+          "sentence": "Player _PARAM1_ push axis _PARAM2_ to _PARAM3_",
+          "events": [
+            {
+              "type": "BuiltinCommonInstructions::JsCode",
+              "inlineCode": [
+                "/** @type {Gamepad[]} */\r",
+                "const gamepads = navigator.getGamepads ? navigator.getGamepads() : (navigator.webkitGetGamepads ? navigator.webkitGetGamepads() : []);\r",
+                "\r",
+                "//Get function parameters\r",
+                "const playerId = eventsFunctionContext.getArgument(\"player_ID\") - 1;\r",
+                "const trigger = eventsFunctionContext.getArgument(\"trigger\").toUpperCase();\r",
+                "\r",
+                "if (playerId < 0 || playerId > 4) {\r",
+                "    console.error('Parameter gamepad identifier in expression: \"Pressure on a gamepad trigger\", is not valid number, must be between 0 and 4.');\r",
+                "    return;\r",
+                "}\r",
+                "if (trigger != \"LT\" && trigger != \"RT\" && trigger != \"L2\" && trigger != \"R2\") {\r",
+                "    console.error('Parameter trigger is not valid in expression: \"Pressure on a gamepad trigger\"');\r",
+                "    return;\r",
+                "}\r",
+                "\r",
+                "const gamepad = gamepads[playerId];\r",
+                "\r",
+                "//we need keep this condition because when use have not yet plug her controller we can't get the controller in the gamepad variable.\r",
+                "if (gamepad == null) return;\r",
+                "\r",
+                "switch (trigger) {\r",
+                "    case 'LT':\r",
+                "    case 'L2':\r",
+                "        eventsFunctionContext.returnValue = gamepad.buttons[6].value;\r",
+                "        break;\r",
+                "\r",
+                "    case 'RT':\r",
+                "    case 'R2':\r",
+                "        eventsFunctionContext.returnValue = gamepad.buttons[7].value;\r",
+                "        break;\r",
+                "\r",
+                "    default:\r",
+                "        eventsFunctionContext.returnValue = -1;\r",
+                "        break;\r",
+                "}"
+              ],
+              "parameterObjects": "",
+              "useStrict": true,
+              "eventsSheetExpanded": true
+            }
+          ],
+          "expressionType": {
+            "type": "expression"
+          },
+          "parameters": [
+            {
+              "description": "The gamepad identifier: 1, 2, 3 or 4",
+              "name": "player_ID",
+              "type": "expression"
+            },
+            {
+              "description": "Trigger button",
+              "name": "trigger",
+              "supplementaryInformation": "[\"LT\",\"RT\",\"L2\",\"R2\"]",
+              "type": "stringWithSelector"
+            }
+          ],
+          "objectGroups": []
+        },
+        {
+          "description": "the force of gamepad stick (from 0 to 1).",
+          "fullName": "Stick force",
+          "functionType": "ExpressionAndCondition",
+          "name": "StickForce",
+          "sentence": "the gamepad _PARAM1_ _PARAM2_ stick force",
+          "events": [
+            {
+              "type": "BuiltinCommonInstructions::JsCode",
+              "inlineCode": [
+                "/** @type {Gamepad[]} */\r",
+                "const gamepads = navigator.getGamepads ? navigator.getGamepads() : (navigator.webkitGetGamepads ? navigator.webkitGetGamepads() : []);\r",
+                "\r",
+                "//Get function parameters\r",
+                "const playerId = eventsFunctionContext.getArgument(\"player_ID\") - 1;\r",
+                "const stick = eventsFunctionContext.getArgument(\"stick\").toUpperCase();\r",
+                "\r",
+                "\r",
+                "if (playerId < 0 || playerId > 4) {\r",
+                "    console.error('Parameter gamepad identifier is not valid in expression: \"Value of a stick force\"');\r",
+                "    return;\r",
+                "}\r",
+                "\r",
+                "if (stick !== \"LEFT\" && stick !== \"RIGHT\") {\r",
+                "    console.error('Parameter stick is not valid in expression: \"Value of a stick force\"');\r",
+                "    return;\r",
+                "}\r",
+                "\r",
+                "const gamepad = gamepads[playerId];\r",
+                "\r",
+                "//we need keep this condition because when use have not yet plug her controller we can't get the controller in the gamepad variable.\r",
+                "if (gamepad == null) return;\r",
+                "\r",
+                "\r",
+                "switch (stick) {\r",
+                "    case 'LEFT':\r",
+                "        eventsFunctionContext.returnValue = gdjs.evtTools.common.clamp(Math.abs(gdjs._extensionController.getNormalizedAxisValue(gamepad.axes[0], playerId)) + Math.abs(gdjs._extensionController.getNormalizedAxisValue(gamepad.axes[1], playerId)), 0, 1);\r",
+                "        break;\r",
+                "\r",
+                "    case 'RIGHT':\r",
+                "        eventsFunctionContext.returnValue = gdjs.evtTools.common.clamp(Math.abs(gdjs._extensionController.getNormalizedAxisValue(gamepad.axes[2], playerId)) + Math.abs(gdjs._extensionController.getNormalizedAxisValue(gamepad.axes[3], playerId)), 0, 1);\r",
+                "        break;\r",
+                "\r",
+                "    default:\r",
+                "        eventsFunctionContext.returnValue = -1;\r",
+                "        break;\r",
+                "}"
+              ],
+              "parameterObjects": "",
+              "useStrict": true,
+              "eventsSheetExpanded": true
+            }
+          ],
+          "expressionType": {
+            "type": "expression"
+          },
+          "parameters": [
+            {
+              "description": "The gamepad identifier: 1, 2, 3 or 4",
+              "name": "player_ID",
+              "type": "expression"
+            },
+            {
+              "description": "Stick: \"Left\" or \"Right\"",
+              "name": "stick",
+              "supplementaryInformation": "[\"Left\",\"Right\"]",
+              "type": "stringWithSelector"
+            }
+          ],
+          "objectGroups": []
+        },
+        {
+          "description": "Get the rotation value of a gamepad stick.\nIf the deadzone value is high, the angle value is rounded to main axes, left, left, up, down.\nAn zero deadzone value give a total freedom on the angle value.",
+          "fullName": "Value of a stick rotation (deprecated)",
+          "functionType": "Expression",
+          "name": "StickRotationValue",
+          "private": true,
+          "sentence": "Player _PARAM1_ push axis _PARAM2_ to _PARAM3_",
+          "events": [
+            {
+              "type": "BuiltinCommonInstructions::Standard",
+              "conditions": [],
+              "actions": [
+                {
+                  "type": {
+                    "value": "SetReturnNumber"
+                  },
+                  "parameters": [
+                    "Gamepads::StickAngle(player_ID, stick)"
+                  ]
+                }
+              ]
+            }
+          ],
+          "expressionType": {
+            "type": "expression"
+          },
+          "parameters": [
+            {
+              "description": "The gamepad identifier: 1, 2, 3 or 4",
+              "name": "player_ID",
+              "type": "expression"
+            },
+            {
+              "description": "Stick: \"Left\" or \"Right\"",
+              "name": "stick",
+              "supplementaryInformation": "[\"Left\",\"Right\"]",
+              "type": "stringWithSelector"
+            }
+          ],
+          "objectGroups": []
+        },
+        {
+          "description": "Return the angle of a gamepad stick.\nIf the deadzone value is high, the angle value is rounded to main axes, left, left, up, down.\nAn zero deadzone value give a total freedom on the angle value.",
+          "fullName": "Stick angle",
+          "functionType": "Expression",
+          "name": "StickAngle",
+          "sentence": "",
+          "events": [
+            {
+              "type": "BuiltinCommonInstructions::JsCode",
+              "inlineCode": [
+                "/** @type {Gamepad[]} */\r",
+                "const gamepads = navigator.getGamepads ? navigator.getGamepads() : (navigator.webkitGetGamepads ? navigator.webkitGetGamepads() : []);\r",
+                "\r",
+                "//Get function parameters\r",
+                "const playerId = eventsFunctionContext.getArgument(\"player_ID\") - 1;\r",
+                "const stick = eventsFunctionContext.getArgument(\"stick\").toUpperCase();\r",
+                "\r",
+                "\r",
+                "if (playerId < 0 || playerId > 4) {\r",
+                "    console.error('Parameter gamepad identifier is not valid in expression: \"Value of a stick rotation\"');\r",
+                "    return;\r",
+                "}\r",
+                "if (stick !== \"LEFT\" && stick !== \"RIGHT\") {\r",
+                "    console.error('Parameter stick is not valid in expression: \"Value of a stick rotation\"');\r",
+                "    return;\r",
+                "}\r",
+                "const gamepad = gamepads[playerId];\r",
+                "\r",
+                "//we need keep this condition because when use have not yet plug her controller we can't get the controller in the gamepad variable.\r",
+                "if (gamepad == null) return;\r",
+                "\r",
+                "switch (stick) {\r",
+                "    case 'LEFT':\r",
+                "        eventsFunctionContext.returnValue = gdjs._extensionController.axisToAngle(gdjs._extensionController.getNormalizedAxisValue(gamepad.axes[0], playerId), gdjs._extensionController.getNormalizedAxisValue(gamepad.axes[1], playerId));\r",
+                "        break;\r",
+                "\r",
+                "    case 'RIGHT':\r",
+                "        eventsFunctionContext.returnValue = gdjs._extensionController.axisToAngle(gdjs._extensionController.getNormalizedAxisValue(gamepad.axes[2], playerId), gdjs._extensionController.getNormalizedAxisValue(gamepad.axes[3], playerId));\r",
+                "        break;\r",
+                "\r",
+                "    default:\r",
+                "        eventsFunctionContext.returnValue = -1;\r",
+                "        break;\r",
+                "}"
+              ],
+              "parameterObjects": "",
+              "useStrict": true,
+              "eventsSheetExpanded": true
+            }
+          ],
+          "expressionType": {
+            "type": "expression"
+          },
+          "parameters": [
+            {
+              "description": "The gamepad identifier: 1, 2, 3 or 4",
+              "name": "player_ID",
+              "type": "expression"
+            },
+            {
+              "description": "Stick: \"Left\" or \"Right\"",
+              "name": "stick",
+              "supplementaryInformation": "[\"Left\",\"Right\"]",
+              "type": "stringWithSelector"
+            }
+          ],
+          "objectGroups": []
+        },
+        {
+          "description": "Get the value of axis of a gamepad stick.",
+          "fullName": "Value of a gamepad axis (deprecated)",
+          "functionType": "Expression",
+          "name": "AxisValue",
+          "private": true,
+          "sentence": "Player _PARAM1_ push axis _PARAM2_ to _PARAM3_",
+          "events": [
+            {
+              "type": "BuiltinCommonInstructions::JsCode",
+              "inlineCode": [
+                "/** @type {Gamepad[]} */\r",
+                "const gamepads = navigator.getGamepads ? navigator.getGamepads() : (navigator.webkitGetGamepads ? navigator.webkitGetGamepads() : []);\r",
+                "\r",
+                "//Get function parameters\r",
+                "const playerId = eventsFunctionContext.getArgument(\"player_ID\") - 1;\r",
+                "const stick = eventsFunctionContext.getArgument(\"stick\").toUpperCase();\r",
+                "const direction = eventsFunctionContext.getArgument(\"direction\").toUpperCase();\r",
+                "\r",
+                "if (playerId < 0 || playerId > 4) {\r",
+                "    console.error('Parameter gamepad identifier is not valid in expression: \"Value of a gamepad axis\"');\r",
+                "    return;\r",
+                "}\r",
+                "if (stick != \"LEFT\" && stick != \"RIGHT\") {\r",
+                "    console.error('Parameter stick is not valid in expression: \"Value of a gamepad axis\"');\r",
+                "    return;\r",
+                "}\r",
+                "if (direction != \"UP\" && direction != \"DOWN\" && direction != \"LEFT\" && direction != \"RIGHT\" && direction != \"HORIZONTAL\" && direction != \"VERTICAL\") {\r",
+                "    console.error('Parameter direction is not valid in expression: \"Value of a gamepad axis\"');\r",
+                "    return;\r",
+                "}\r",
+                "const gamepad = gamepads[playerId];\r",
+                "\r",
+                "//we need keep this condition because when use have not yet plug her controller we can't get the controller in the gamepad variable.\r",
+                "if (gamepad == null) return;\r",
+                "\r",
+                "let parameterError = false;\r",
+                "switch (stick) {\r",
+                "    case 'LEFT':\r",
+                "        switch (direction) {\r",
+                "            case 'LEFT':\r",
+                "                if (gdjs._extensionController.getNormalizedAxisValue(gamepad.axes[0], playerId) < 0) {\r",
+                "                    eventsFunctionContext.returnValue = -gdjs._extensionController.getNormalizedAxisValue(gamepad.axes[0], playerId);\r",
+                "                }\r",
+                "                break;\r",
+                "\r",
+                "            case 'RIGHT':\r",
+                "                if (gdjs._extensionController.getNormalizedAxisValue(gamepad.axes[0], playerId) > 0) {\r",
+                "                    eventsFunctionContext.returnValue = gdjs._extensionController.getNormalizedAxisValue(gamepad.axes[0], playerId);\r",
+                "                }\r",
+                "                break;\r",
+                "\r",
+                "            case 'UP':\r",
+                "                if (gdjs._extensionController.getNormalizedAxisValue(gamepad.axes[1], playerId) < 0) {\r",
+                "                    eventsFunctionContext.returnValue = -gdjs._extensionController.getNormalizedAxisValue(gamepad.axes[1], playerId);\r",
+                "                }\r",
+                "                break;\r",
+                "\r",
+                "            case 'DOWN':\r",
+                "                if (gdjs._extensionController.getNormalizedAxisValue(gamepad.axes[1], playerId) > 0) {\r",
+                "                    eventsFunctionContext.returnValue = gdjs._extensionController.getNormalizedAxisValue(gamepad.axes[1], playerId);\r",
+                "                }\r",
+                "                break;\r",
+                "\r",
+                "            case \"HORIZONTAL\":\r",
+                "                eventsFunctionContext.returnValue = gdjs._extensionController.getNormalizedAxisValue(gamepad.axes[0], playerId);\r",
+                "                break;\r",
+                "\r",
+                "            case \"VERTICAL\":\r",
+                "                eventsFunctionContext.returnValue = gdjs._extensionController.getNormalizedAxisValue(gamepad.axes[1], playerId);\r",
+                "                break;\r",
+                "\r",
+                "            default:\r",
+                "                break;\r",
+                "        }\r",
+                "        break;\r",
+                "\r",
+                "    case 'RIGHT':\r",
+                "        switch (direction) {\r",
+                "            case 'LEFT':\r",
+                "                if (gdjs._extensionController.getNormalizedAxisValue(gamepad.axes[2], playerId) < 0) {\r",
+                "                    eventsFunctionContext.returnValue = -gdjs._extensionController.getNormalizedAxisValue(gamepad.axes[2], playerId);\r",
+                "                }\r",
+                "                break;\r",
+                "\r",
+                "            case 'RIGHT':\r",
+                "                if (gdjs._extensionController.getNormalizedAxisValue(gamepad.axes[2], playerId) > 0) {\r",
+                "                    eventsFunctionContext.returnValue = gdjs._extensionController.getNormalizedAxisValue(gamepad.axes[2], playerId);\r",
+                "                }\r",
+                "                break;\r",
+                "\r",
+                "            case 'UP':\r",
+                "                if (gdjs._extensionController.getNormalizedAxisValue(gamepad.axes[3], playerId) < 0) {\r",
+                "                    eventsFunctionContext.returnValue = -gdjs._extensionController.getNormalizedAxisValue(gamepad.axes[3], playerId);\r",
+                "                }\r",
+                "                break;\r",
+                "\r",
+                "            case 'DOWN':\r",
+                "                if (gdjs._extensionController.getNormalizedAxisValue(gamepad.axes[3], playerId) > 0) {\r",
+                "                    eventsFunctionContext.returnValue = gdjs._extensionController.getNormalizedAxisValue(gamepad.axes[3], playerId);\r",
+                "                }\r",
+                "                break;\r",
+                "\r",
+                "            case \"HORIZONTAL\":\r",
+                "                eventsFunctionContext.returnValue = gdjs._extensionController.getNormalizedAxisValue(gamepad.axes[2], playerId);\r",
+                "                break;\r",
+                "\r",
+                "            case \"VERTICAL\":\r",
+                "                eventsFunctionContext.returnValue = gdjs._extensionController.getNormalizedAxisValue(gamepad.axes[3], playerId);\r",
+                "                break;\r",
+                "\r",
+                "            default:\r",
+                "                break;\r",
+                "        }\r",
+                "        break;\r",
+                "\r",
+                "    default:\r",
+                "        break;\r",
+                "}\r",
+                ""
+              ],
+              "parameterObjects": "",
+              "useStrict": true,
+              "eventsSheetExpanded": true
+            }
+          ],
+          "expressionType": {
+            "type": "expression"
+          },
+          "parameters": [
+            {
+              "description": "The gamepad identifier: 1, 2, 3 or 4",
+              "name": "player_ID",
+              "type": "expression"
+            },
+            {
+              "description": "Stick: \"Left\" or \"Right\"",
+              "name": "stick",
+              "supplementaryInformation": "[\"Left\",\"Right\"]",
+              "type": "stringWithSelector"
+            },
+            {
+              "description": "Direction",
+              "name": "direction",
+              "supplementaryInformation": "[\"Up\",\"Down\",\"Left\",\"Right\",\"Horizontal\",\"Vertical\"]",
+              "type": "stringWithSelector"
+            }
+          ],
+          "objectGroups": []
+        },
+        {
+          "description": "Return the gamepad stick force on X axis (from -1 at the left to 1 at the right).",
+          "fullName": "Stick X force",
+          "functionType": "Expression",
+          "name": "StickForceX",
+          "sentence": "",
+          "events": [
+            {
+              "type": "BuiltinCommonInstructions::JsCode",
+              "inlineCode": [
+                "/** @type {Gamepad[]} */\r",
+                "const gamepads = navigator.getGamepads ? navigator.getGamepads() : (navigator.webkitGetGamepads ? navigator.webkitGetGamepads() : []);\r",
+                "\r",
+                "//Get function parameters\r",
+                "const playerId = eventsFunctionContext.getArgument(\"Gamepad\") - 1;\r",
+                "const stick = eventsFunctionContext.getArgument(\"Stick\").toLowerCase();\r",
+                "\r",
+                "if (playerId < 0 || playerId > 4) {\r",
+                "    console.error('Parameter gamepad identifier is not valid in expression: \"Value of a gamepad axis\"');\r",
+                "    return;\r",
+                "}\r",
+                "if (stick != \"left\" && stick != \"right\") {\r",
+                "    console.error('Parameter stick is not valid in expression: \"Value of a gamepad axis\"');\r",
+                "    return;\r",
+                "}\r",
+                "const gamepad = gamepads[playerId];\r",
+                "\r",
+                "//we need keep this condition because when use have not yet plug her controller we can't get the controller in the gamepad variable.\r",
+                "if (gamepad == null) return;\r",
+                "\r",
+                "const axisIndex = stick === 'right' ? 2 : 0;\r",
+                "eventsFunctionContext.returnValue = gdjs._extensionController.getNormalizedAxisValue(gamepad.axes[axisIndex], playerId);\r",
+                ""
+              ],
+              "parameterObjects": "",
+              "useStrict": true,
+              "eventsSheetExpanded": true
+            }
+          ],
+          "expressionType": {
+            "type": "expression"
+          },
+          "parameters": [
+            {
+              "description": "The gamepad identifier: 1, 2, 3 or 4",
+              "name": "Gamepad",
+              "type": "expression"
+            },
+            {
+              "description": "Stick: \"Left\" or \"Right\"",
+              "name": "Stick",
+              "supplementaryInformation": "[\"Left\",\"Right\"]",
+              "type": "stringWithSelector"
+            }
+          ],
+          "objectGroups": []
+        },
+        {
+          "description": "Return the gamepad stick force on Y axis (from -1 at the top to 1 at the bottom).",
+          "fullName": "Stick Y force",
+          "functionType": "Expression",
+          "name": "StickForceY",
+          "sentence": "",
+          "events": [
+            {
+              "type": "BuiltinCommonInstructions::JsCode",
+              "inlineCode": [
+                "/** @type {Gamepad[]} */\r",
+                "const gamepads = navigator.getGamepads ? navigator.getGamepads() : (navigator.webkitGetGamepads ? navigator.webkitGetGamepads() : []);\r",
+                "\r",
+                "//Get function parameters\r",
+                "const playerId = eventsFunctionContext.getArgument(\"Gamepad\") - 1;\r",
+                "const stick = eventsFunctionContext.getArgument(\"Stick\").toLowerCase();\r",
+                "\r",
+                "if (playerId < 0 || playerId > 4) {\r",
+                "    console.error('Parameter gamepad identifier is not valid in expression: \"Value of a gamepad axis\"');\r",
+                "    return;\r",
+                "}\r",
+                "if (stick != \"left\" && stick != \"right\") {\r",
+                "    console.error('Parameter stick is not valid in expression: \"Value of a gamepad axis\"');\r",
+                "    return;\r",
+                "}\r",
+                "const gamepad = gamepads[playerId];\r",
+                "\r",
+                "//we need keep this condition because when use have not yet plug her controller we can't get the controller in the gamepad variable.\r",
+                "if (gamepad == null) return;\r",
+                "\r",
+                "const axisIndex = stick === 'right' ? 3 : 1;\r",
+                "eventsFunctionContext.returnValue = gdjs._extensionController.getNormalizedAxisValue(gamepad.axes[axisIndex], playerId);\r",
+                ""
+              ],
+              "parameterObjects": "",
+              "useStrict": true,
+              "eventsSheetExpanded": true
+            }
+          ],
+          "expressionType": {
+            "type": "expression"
+          },
+          "parameters": [
+            {
+              "description": "The gamepad identifier: 1, 2, 3 or 4",
+              "name": "Gamepad",
+              "type": "expression"
+            },
+            {
+              "description": "Stick: \"Left\" or \"Right\"",
+              "name": "Stick",
+              "supplementaryInformation": "[\"Left\",\"Right\"]",
+              "type": "stringWithSelector"
+            }
+          ],
+          "objectGroups": []
+        },
+        {
+          "description": "Test if a button is released on a gamepad. Buttons can be:\n* Xbox: \"A\", \"B\", \"X\", \"Y\", \"LB\", \"RB\", \"LT\", \"RT\", \"BACK\", \"START\",\n* PS4: \"CROSS\", \"SQUARE\", \"CIRCLE\", \"TRIANGLE\", \"L1\", \"L2\", \"R1\", \"R2\", \"SHARE\", \"OPTIONS\", \"PS_BUTTON\", \"CLICK_TOUCHPAD\",\n* Other: \"UP\", \"DOWN\", \"LEFT\", \"RIGHT\", \"CLICK_STICK_LEFT\", \"CLICK_STICK_RIGHT\".",
+          "fullName": "Gamepad button released",
+          "functionType": "Condition",
+          "name": "C_Button_released",
+          "sentence": "Button _PARAM2_ of gamepad _PARAM1_ is released",
+          "events": [
+            {
+              "type": "BuiltinCommonInstructions::JsCode",
+              "inlineCode": [
+                "/** @type {Gamepad[]} */\r",
+                "const gamepads = navigator.getGamepads ? navigator.getGamepads() : (navigator.webkitGetGamepads ? navigator.webkitGetGamepads() : []);\r",
+                "\r",
+                "//Get function parameters\r",
+                "const playerId = eventsFunctionContext.getArgument(\"player_ID\") - 1;\r",
+                "const button = eventsFunctionContext.getArgument(\"button\").toUpperCase();\r",
+                "\r",
+                "if (playerId < 0 || playerId > 4) {\r",
+                "    console.error('Parameter gamepad identifier in condition: \"Gamepad button released\", is not valid number, must be between 0 and 4.');\r",
+                "    return;\r",
+                "}\r",
+                "if (button === \"\") {\r",
+                "    console.error('Parameter button is not valid in condition: \"Gamepad button released\"');\r",
+                "    return;\r",
+                "}\r",
+                "\r",
+                "const gamepad = gamepads[playerId];\r",
+                "\r",
+                "//we need keep this condition because when use have not yet plug her controller we can't get the controller in the gamepad variable.\r",
+                "if (gamepad == null) return;\r",
+                "\r",
+                "let buttonId;\r",
+                "\r",
+                "switch (button) {\r",
+                "    case 'A':\r",
+                "    case 'CROSS':\r",
+                "        buttonId = 0;\r",
+                "        break;\r",
+                "    case 'B':\r",
+                "    case 'CIRCLE':\r",
+                "        buttonId = 1;\r",
+                "        break;\r",
+                "    case 'X':\r",
+                "    case 'SQUARE':\r",
+                "        buttonId = 2;\r",
+                "        break;\r",
+                "    case 'Y':\r",
+                "    case 'TRIANGLE':\r",
+                "        buttonId = 3;\r",
+                "        break;\r",
+                "    case 'LB':\r",
+                "    case 'L1':\r",
+                "        buttonId = 4;\r",
+                "        break;\r",
+                "    case 'RB':\r",
+                "    case 'R1':\r",
+                "        buttonId = 5;\r",
+                "        break;\r",
+                "    case 'LT':\r",
+                "    case 'L2':\r",
+                "        buttonId = 6;\r",
+                "        break;\r",
+                "    case 'RT':\r",
+                "    case 'R2':\r",
+                "        buttonId = 7;\r",
+                "        break;\r",
+                "\r",
+                "    case 'UP':\r",
+                "        buttonId = 12;\r",
+                "        break;\r",
+                "    case 'DOWN':\r",
+                "        buttonId = 13;\r",
+                "        break;\r",
+                "    case 'LEFT':\r",
+                "        buttonId = 14;\r",
+                "        break;\r",
+                "    case 'RIGHT':\r",
+                "        buttonId = 15;\r",
+                "        break;\r",
+                "\r",
+                "    case 'BACK':\r",
+                "    case 'SHARE':\r",
+                "        buttonId = 8;\r",
+                "        break;\r",
+                "    case 'START':\r",
+                "    case 'OPTIONS':\r",
+                "        buttonId = 9;\r",
+                "        break;\r",
+                "\r",
+                "    case 'CLICK_STICK_LEFT':\r",
+                "        buttonId = 10;\r",
+                "        break;\r",
+                "    case 'CLICK_STICK_RIGHT':\r",
+                "        buttonId = 11;\r",
+                "        break;\r",
+                "\r",
+                "    //PS4\r",
+                "    case 'PS_BUTTON':\r",
+                "        buttonId = 16;\r",
+                "        break;\r",
+                "    case 'CLICK_TOUCHPAD':\r",
+                "        buttonId = 17;\r",
+                "        break;\r",
+                "\r",
+                "    default:\r",
+                "        console.error('The button: ' + button + ' in condition: \"Gamepad button released\" is not valid.');\r",
+                "        break;\r",
+                "}\r",
+                "\r",
+                "if (buttonId === undefined) {\r",
+                "    console.error('There is no buttons valid in condition: \"Gamepad button released\"');\r",
+                "    eventsFunctionContext.returnValue = false;\r",
+                "    return;\r",
+                "}\r",
+                "\r",
+                "if (gamepad.buttons == null || gamepad.buttons[buttonId] == null) {\r",
+                "    console.error('Buttons on the gamepad are not accessible in condition: \"Gamepad button released\"');\r",
+                "    eventsFunctionContext.returnValue = false;\r",
+                "    return;\r",
+                "}\r",
+                "\r",
+                "//Define default value on pressed button or use previous value\r",
+                "gdjs._extensionController.players[playerId].previousFrameStateButtons[buttonId] = gdjs._extensionController.players[playerId].previousFrameStateButtons[buttonId] || { pressed: false };\r",
+                "\r",
+                "//Get state of button at previous frame\r",
+                "const previousStateButton = gdjs._extensionController.players[playerId].previousFrameStateButtons[buttonId].pressed;\r",
+                "\r",
+                "//When previousStateButton is true and actual button state is not pressed\r",
+                "//Player have release the button\r",
+                "if (previousStateButton === true && gamepad.buttons[buttonId].pressed === false) {\r",
+                "    // Save the last button used for the player    \r",
+                "    gdjs._extensionController.players[playerId].lastButtonUsed = buttonId;\r",
+                "    gdjs._extensionController.players[playerId].previousFrameStateButtons[buttonId].pressed = true;\r",
+                "    eventsFunctionContext.returnValue = true;\r",
+                "\r",
+                "} else {\r",
+                "    gdjs._extensionController.players[playerId].previousFrameStateButtons[buttonId].pressed = false;\r",
+                "    eventsFunctionContext.returnValue = false;\r",
+                "}\r",
+                ""
+              ],
+              "parameterObjects": "",
+              "useStrict": true,
+              "eventsSheetExpanded": true
+            }
+          ],
+          "parameters": [
+            {
+              "description": "The gamepad identifier: 1, 2, 3 or 4",
+              "name": "player_ID",
+              "type": "expression"
+            },
+            {
+              "description": "Name of the button",
+              "name": "button",
+              "supplementaryInformation": "[\"A\",\"Cross\",\"B\",\"Circle\",\"X\",\"Square\",\"Y\",\"Triangle\",\"LB\",\"L1\",\"RB\",\"R1\",\"LT\",\"L2\",\"RT\",\"R2\",\"Up\",\"Down\",\"Left\",\"Right\",\"Back\",\"Share\",\"Start\",\"Options\",\"Click_Stick_Left\",\"Click_Stick_Right\",\"PS_Button\",\"Click_Touchpad\"]",
+              "type": "stringWithSelector"
+            }
+          ],
+          "objectGroups": []
+        },
+        {
+          "description": "Return the index of the last pressed button of a gamepad.",
+          "fullName": "Last pressed button (id)",
+          "functionType": "Expression",
+          "name": "LastButtonID",
+          "sentence": "",
+          "events": [
+            {
+              "type": "BuiltinCommonInstructions::JsCode",
+              "inlineCode": [
+                "//Get function parameter\r",
+                "const playerId = eventsFunctionContext.getArgument(\"player_ID\") - 1;\r",
+                "\r",
+                "//Player id is not valid\r",
+                "if (playerId < 0 || playerId > 4) {\r",
+                "    console.error('Parameter gamepad identifier in expression: \"Last pressed button (id)\", is not valid number, must be between 0 and 4.');\r",
+                "    return;\r",
+                "}\r",
+                "\r",
+                "//Return the last button used by the player\r",
+                "eventsFunctionContext.returnValue = gdjs._extensionController.players[playerId].lastButtonUsed;"
+              ],
+              "parameterObjects": "",
+              "useStrict": true,
+              "eventsSheetExpanded": true
+            }
+          ],
+          "expressionType": {
+            "type": "expression"
+          },
+          "parameters": [
+            {
+              "description": "The gamepad identifier: 1, 2, 3 or 4",
+              "name": "player_ID",
+              "type": "expression"
+            }
+          ],
+          "objectGroups": []
+        },
+        {
+          "description": "Check if any button is pressed on a gamepad.",
+          "fullName": "Any gamepad button pressed",
+          "functionType": "Condition",
+          "name": "C_Any_Button_pressed",
+          "sentence": "Any button of gamepad _PARAM1_ is pressed",
+          "events": [
+            {
+              "type": "BuiltinCommonInstructions::JsCode",
+              "inlineCode": [
+                "/** @type {Gamepad[]} */\r",
+                "const gamepads = navigator.getGamepads ? navigator.getGamepads() : (navigator.webkitGetGamepads ? navigator.webkitGetGamepads() : []);\r",
+                "\r",
+                "//Get function parameter\r",
+                "const playerId = eventsFunctionContext.getArgument(\"player_ID\") - 1;\r",
+                "\r",
+                "if (playerId < 0 || playerId > 4) {\r",
+                "    console.error('Parameter gamepad identifier in condition: \"Any gamepad button pressed\", is not valid number, must be between 0 and 4.');\r",
+                "    return;\r",
+                "}\r",
+                "const gamepad = gamepads[playerId];\r",
+                "\r",
+                "//we need keep this condition because when use have not yet plug her controller we can't get the controller in the gamepad variable.\r",
+                "if (gamepad == null) return;\r",
+                "\r",
+                "let buttonId;\r",
+                "for (let i = 0; i < gamepad.buttons.length; i++) { //For each buttons\r",
+                "    if (gamepad.buttons[i].pressed) { //One of them is pressed\r",
+                "        buttonId = i; //Save the button pressed\r",
+                "        break;\r",
+                "    }\r",
+                "}\r",
+                "\r",
+                "if (buttonId === undefined) {\r",
+                "    // No buttons are pressed.\r",
+                "    eventsFunctionContext.returnValue = false;\r",
+                "    return;\r",
+                "}\r",
+                "\r",
+                "if (gamepad.buttons == null || gamepad.buttons[buttonId] == null) {\r",
+                "    console.error('Buttons on the gamepad are not accessible in condition: \"Any gamepad button pressed\"');\r",
+                "    eventsFunctionContext.returnValue = false;\r",
+                "    return;\r",
+                "}\r",
+                "\r",
+                "//When a button is pressed, save the button in lastButtonUsed for each players\r",
+                "if (gamepad.buttons[buttonId].pressed) gdjs._extensionController.players[playerId].lastButtonUsed = buttonId;\r",
+                "eventsFunctionContext.returnValue = gamepad.buttons[buttonId].pressed;\r",
+                "\r",
+                "\r",
+                ""
+              ],
+              "parameterObjects": "",
+              "useStrict": true,
+              "eventsSheetExpanded": true
+            }
+          ],
+          "parameters": [
+            {
+              "description": "The gamepad identifier: 1, 2, 3 or 4",
+              "name": "player_ID",
+              "type": "expression"
+            }
+          ],
+          "objectGroups": []
+        },
+        {
+          "description": "Return the last button pressed. \nButtons for Xbox and PS4 can be:\n* Xbox: \"A\", \"B\", \"X\", \"Y\", \"LB\", \"RB\", \"LT\", \"RT\", \"BACK\", \"START\",\n* PS4: \"CROSS\", \"SQUARE\", \"CIRCLE\", \"TRIANGLE\", \"L1\", \"L2\", \"R1\", \"R2\", \"SHARE\", \"OPTIONS\", \"PS_BUTTON\", \"CLICK_TOUCHPAD\",\n* Both: \"UP\", \"DOWN\", \"LEFT\", \"RIGHT\", \"CLICK_STICK_LEFT\", \"CLICK_STICK_RIGHT\".",
+          "fullName": "Last pressed button (string)",
+          "functionType": "StringExpression",
+          "name": "LastButtonString",
+          "sentence": "Button _PARAM2_ of gamepad _PARAM1_ is pressed",
+          "events": [
+            {
+              "type": "BuiltinCommonInstructions::JsCode",
+              "inlineCode": [
+                "/** @type {Gamepad[]} */\r",
+                "const gamepads = navigator.getGamepads ? navigator.getGamepads() : (navigator.webkitGetGamepads ? navigator.webkitGetGamepads() : []);\r",
+                "\r",
+                "//Get function parameters\r",
+                "const playerId = eventsFunctionContext.getArgument(\"player_ID\") - 1;\r",
+                "const controllerType = eventsFunctionContext.getArgument(\"controller_type\").toUpperCase();\r",
+                "\r",
+                "if (playerId < 0 || playerId > 4) {\r",
+                "    console.error('Parameter gamepad identifier in string expression: \"Last pressed button (LastButtonString)\", is not valid number, must be between 0 and 4.');\r",
+                "    return;\r",
+                "}\r",
+                "if (controllerType === \"\") {\r",
+                "    console.error('Parameter controller type is not valid in string expression: \"Last pressed button (LastButtonString)\"');\r",
+                "    return;\r",
+                "}\r",
+                "\r",
+                "const gamepad = gamepads[playerId];\r",
+                "\r",
+                "if (gamepad !== null) { //Gamepad exist\r",
+                "    //Get last btn id\r",
+                "    const lastButtonUsedID = gdjs._extensionController.players[playerId].lastButtonUsed;\r",
+                "\r",
+                "    //Return last button as string    \r",
+                "    eventsFunctionContext.returnValue = gdjs._extensionController.getInputString(controllerType, lastButtonUsedID);\r",
+                "\r",
+                "} else { //Gamepad dosen't exist\r",
+                "    console.error('Your controller is not supported or the gamepad wasn\\'t detected in string expression: \"Last pressed button (LastButtonString)\"');\r",
+                "    eventsFunctionContext.returnValue = \"Gamepad not connected\";\r",
+                "}"
+              ],
+              "parameterObjects": "",
+              "useStrict": true,
+              "eventsSheetExpanded": true
+            }
+          ],
+          "expressionType": {
+            "type": "string"
+          },
+          "parameters": [
+            {
+              "description": "The gamepad identifier: 1, 2, 3 or 4",
+              "name": "player_ID",
+              "type": "expression"
+            },
+            {
+              "description": "Controller type",
+              "name": "controller_type",
+              "supplementaryInformation": "[\"Xbox\",\"PS4\"]",
+              "type": "stringWithSelector"
+            }
+          ],
+          "objectGroups": []
+        },
+        {
+          "description": "Return the number of gamepads.",
+          "fullName": "Gamepad count",
+          "functionType": "Expression",
+          "name": "GamepadCount",
+          "sentence": "",
+          "events": [
+            {
+              "type": "BuiltinCommonInstructions::JsCode",
+              "inlineCode": [
+                "const gamepads = navigator.getGamepads ? navigator.getGamepads() : (navigator.webkitGetGamepads ? navigator.webkitGetGamepads() : []);\r",
+                "\r",
+                "//Get the last activated controller\r",
+                "const controllerId = gdjs._extensionController.lastActiveController;\r",
+                "\r",
+                "// Check if controller is active\r",
+                "const gamepad = gamepads[controllerId];\r",
+                "if (gamepad == null) {\r",
+                "    eventsFunctionContext.returnValue = 0;\r",
+                "} else {\r",
+                "    // Return active controller id\r",
+                "    eventsFunctionContext.returnValue = controllerId + 1;\r",
+                "}\r",
+                ""
+              ],
+              "parameterObjects": "",
+              "useStrict": true,
+              "eventsSheetExpanded": true
+            }
+          ],
+          "expressionType": {
+            "type": "expression"
+          },
+          "parameters": [],
+          "objectGroups": []
+        },
+        {
+          "description": "Check if a button is pressed on a gamepad. \nButtons can be:\n* Xbox: \"A\", \"B\", \"X\", \"Y\", \"LB\", \"RB\", \"LT\", \"RT\", \"BACK\", \"START\",\n* PS4: \"CROSS\", \"SQUARE\", \"CIRCLE\", \"TRIANGLE\", \"L1\", \"L2\", \"R1\", \"R2\", \"SHARE\", \"OPTIONS\", \"PS_BUTTON\", \"CLICK_TOUCHPAD\",\n* Other: \"UP\", \"DOWN\", \"LEFT\", \"RIGHT\", \"CLICK_STICK_LEFT\", \"CLICK_STICK_RIGHT\".",
+          "fullName": "Gamepad button pressed",
+          "functionType": "Condition",
+          "name": "C_Button_pressed",
+          "sentence": "Button _PARAM2_ of gamepad _PARAM1_ is pressed",
+          "events": [
+            {
+              "type": "BuiltinCommonInstructions::JsCode",
+              "inlineCode": [
+                "/** @type {Gamepad[]} */\r",
+                "const gamepads = navigator.getGamepads ? navigator.getGamepads() : (navigator.webkitGetGamepads ? navigator.webkitGetGamepads() : []);\r",
+                "\r",
+                "//Get function parameters\r",
+                "const playerId = eventsFunctionContext.getArgument(\"player_ID\") - 1;\r",
+                "const button = eventsFunctionContext.getArgument(\"button\").toUpperCase();\r",
+                "\r",
+                "if (playerId < 0 || playerId > 4) {\r",
+                "    console.error('Parameter gamepad identifier in condition: \"Gamepad button pressed\", is not valid number, must be between 0 and 4.');\r",
+                "    return;\r",
+                "}\r",
+                "if (button === \"\") {\r",
+                "    console.error('Parameter button is not valid in condition: \"Gamepad button pressed\"');\r",
+                "    eventsFunctionContext.returnValue = false;\r",
+                "    return;\r",
+                "}\r",
+                "\r",
+                "const gamepad = gamepads[playerId];\r",
+                "\r",
+                "//we need keep this condition because when use have not yet plug her controller we can't get the controller in the gamepad variable.\r",
+                "if (gamepad == null) return;\r",
+                "\r",
+                "let buttonId;\r",
+                "\r",
+                "switch (button) {\r",
+                "    case 'A':\r",
+                "    case 'CROSS':\r",
+                "        buttonId = 0;\r",
+                "        break;\r",
+                "    case 'B':\r",
+                "    case 'CIRCLE':\r",
+                "        buttonId = 1;\r",
+                "        break;\r",
+                "    case 'X':\r",
+                "    case 'SQUARE':\r",
+                "        buttonId = 2;\r",
+                "        break;\r",
+                "    case 'Y':\r",
+                "    case 'TRIANGLE':\r",
+                "        buttonId = 3;\r",
+                "        break;\r",
+                "    case 'LB':\r",
+                "    case 'L1':\r",
+                "        buttonId = 4;\r",
+                "        break;\r",
+                "    case 'RB':\r",
+                "    case 'R1':\r",
+                "        buttonId = 5;\r",
+                "        break;\r",
+                "    case 'LT':\r",
+                "    case 'L2':\r",
+                "        buttonId = 6;\r",
+                "        break;\r",
+                "    case 'RT':\r",
+                "    case 'R2':\r",
+                "        buttonId = 7;\r",
+                "        break;\r",
+                "\r",
+                "    case 'UP':\r",
+                "        buttonId = 12;\r",
+                "        break;\r",
+                "    case 'DOWN':\r",
+                "        buttonId = 13;\r",
+                "        break;\r",
+                "    case 'LEFT':\r",
+                "        buttonId = 14;\r",
+                "        break;\r",
+                "    case 'RIGHT':\r",
+                "        buttonId = 15;\r",
+                "        break;\r",
+                "\r",
+                "    case 'BACK':\r",
+                "    case 'SHARE':\r",
+                "        buttonId = 8;\r",
+                "        break;\r",
+                "    case 'START':\r",
+                "    case 'OPTIONS':\r",
+                "        buttonId = 9;\r",
+                "        break;\r",
+                "\r",
+                "    case 'CLICK_STICK_LEFT':\r",
+                "        buttonId = 10;\r",
+                "        break;\r",
+                "    case 'CLICK_STICK_RIGHT':\r",
+                "        buttonId = 11;\r",
+                "        break;\r",
+                "\r",
+                "    //PS4\r",
+                "    case 'PS_BUTTON':\r",
+                "        buttonId = 16;\r",
+                "        break;\r",
+                "    case 'CLICK_TOUCHPAD':\r",
+                "        buttonId = 17;\r",
+                "        break;\r",
+                "\r",
+                "    default:\r",
+                "        console.error('The button: ' + button + ' in condition: \"Gamepad button pressed\" is not valid.');\r",
+                "        eventsFunctionContext.returnValue = false;\r",
+                "        break;\r",
+                "}\r",
+                "\r",
+                "\r",
+                "\r",
+                "if (buttonId === undefined) {\r",
+                "    console.error('There is no buttons valid in condition: \"Gamepad button pressed\"');\r",
+                "    eventsFunctionContext.returnValue = false;\r",
+                "    return;\r",
+                "}\r",
+                "\r",
+                "if (gamepad.buttons == null || gamepad.buttons[buttonId] == null) {\r",
+                "    console.error('Buttons on the gamepad are not accessible in condition: \"Gamepad button pressed\"');\r",
+                "    eventsFunctionContext.returnValue = false;\r",
+                "    return;\r",
+                "}\r",
+                "\r",
+                "//When a button is pressed, save the button in lastButtonUsed for each players\r",
+                "if (gamepad.buttons[buttonId].pressed) gdjs._extensionController.players[playerId].lastButtonUsed = buttonId;\r",
+                "eventsFunctionContext.returnValue = gamepad.buttons[buttonId].pressed;\r",
+                "\r",
+                "\r",
+                "\r",
+                ""
+              ],
+              "parameterObjects": "",
+              "useStrict": true,
+              "eventsSheetExpanded": true
+            }
+          ],
+          "parameters": [
+            {
+              "description": "The gamepad identifier: 1, 2, 3 or 4",
+              "name": "player_ID",
+              "type": "expression"
+            },
+            {
+              "description": "Name of the button",
+              "name": "button",
+              "supplementaryInformation": "[\"A\",\"Cross\",\"B\",\"Circle\",\"X\",\"Square\",\"Y\",\"Triangle\",\"LB\",\"L1\",\"RB\",\"R1\",\"LT\",\"L2\",\"RT\",\"R2\",\"Up\",\"Down\",\"Left\",\"Right\",\"Back\",\"Share\",\"Start\",\"Options\",\"Click_Stick_Left\",\"Click_Stick_Right\",\"PS_Button\",\"Click_Touchpad\"]",
+              "type": "stringWithSelector"
+            }
+          ],
+          "objectGroups": []
+        },
+        {
+          "description": "Return the value of the deadzone applied to a gamepad sticks, between 0 and 1.",
+          "fullName": "Gamepad deadzone for sticks",
+          "functionType": "Expression",
+          "name": "Deadzone",
+          "sentence": "",
+          "events": [
+            {
+              "type": "BuiltinCommonInstructions::Standard",
+              "conditions": [],
+              "actions": []
+            },
+            {
+              "type": "BuiltinCommonInstructions::JsCode",
+              "inlineCode": [
+                "//Get function parameter\r",
+                "const playerId = eventsFunctionContext.getArgument(\"player_ID\") - 1;\r",
+                "\r",
+                "if (playerId < 0 || playerId > 4) {\r",
+                "    console.error('Parameter gamepad identifier in expression: \"Gamepad deadzone for sticks\", is not valid number, must be between 0 and 4.');\r",
+                "    return;\r",
+                "}\r",
+                "///Return the deadzone value for a given player\r",
+                "eventsFunctionContext.returnValue = gdjs._extensionController.players[playerId].deadzone;"
+              ],
+              "parameterObjects": "",
+              "useStrict": true,
+              "eventsSheetExpanded": true
+            }
+          ],
+          "expressionType": {
+            "type": "expression"
+          },
+          "parameters": [
+            {
+              "description": "The gamepad identifier: 1, 2, 3 or 4",
+              "name": "player_ID",
+              "type": "expression"
+            }
+          ],
+          "objectGroups": []
+        },
+        {
+          "description": "Set the deadzone for sticks of the gamepad. The deadzone is an area for which movement on sticks won't be taken into account (instead, the stick will be considered as not moved). Deadzone is between 0 and 1, and is by default 0.2.",
+          "fullName": "Set gamepad deadzone for sticks",
+          "functionType": "Action",
+          "name": "A_Set_deadzone",
+          "sentence": "Set deadzone for sticks on gamepad: _PARAM1_ to _PARAM2_",
+          "events": [
+            {
+              "type": "BuiltinCommonInstructions::JsCode",
+              "inlineCode": [
+                "//Get function parameter\r",
+                "const playerId = eventsFunctionContext.getArgument(\"player_ID\") - 1;\r",
+                "const newDeadzone = eventsFunctionContext.getArgument(\"deadzone\");\r",
+                "\r",
+                "if (playerId < 0 || playerId > 4) {\r",
+                "    console.error('Parameter gamepad identifier in action: \"Set gamepad deadzone for sticks\", is not valid, must be between 0 and 4.');\r",
+                "    return;\r",
+                "}\r",
+                "\r",
+                "// clamp the newDeadzone in range [0, 1].\r",
+                "// https://github.com/4ian/GDevelop-extensions/pull/33#issuecomment-618224857\r",
+                "gdjs._extensionController.players[playerId].deadzone = gdjs.evtTools.common.clamp(newDeadzone, 0, 1);\r",
+                ""
+              ],
+              "parameterObjects": "",
+              "useStrict": true,
+              "eventsSheetExpanded": true
+            }
+          ],
+          "parameters": [
+            {
+              "description": "The gamepad identifier: 1, 2, 3 or 4",
+              "name": "player_ID",
+              "type": "expression"
+            },
+            {
+              "description": "Deadzone for sticks, 0.2 by default (0 to 1)",
+              "name": "deadzone",
+              "type": "expression"
+            }
+          ],
+          "objectGroups": []
+        },
+        {
+          "description": "Check if a stick of a gamepad is pushed in a given direction.",
+          "fullName": "Gamepad stick pushed (axis)",
+          "functionType": "Condition",
+          "name": "C_Axis_pushed",
+          "sentence": "_PARAM2_ stick of gamepad _PARAM1_ is pushed in direction _PARAM3_",
+          "events": [
+            {
+              "type": "BuiltinCommonInstructions::JsCode",
+              "inlineCode": [
+                "/** @type {Gamepad[]} */\r",
+                "const gamepads = navigator.getGamepads ? navigator.getGamepads() : (navigator.webkitGetGamepads ? navigator.webkitGetGamepads() : []);\r",
+                "\r",
+                "//Get function parameters\r",
+                "const playerId = eventsFunctionContext.getArgument(\"player_ID\") - 1;\r",
+                "const stick = eventsFunctionContext.getArgument(\"stick\").toUpperCase();\r",
+                "const direction = eventsFunctionContext.getArgument(\"direction\").toUpperCase();\r",
+                "\r",
+                "if (playerId < 0 || playerId > 4) {\r",
+                "    console.error('Parameter gamepad identifier in condition: \"Gamepad stick pushed (axis)\", is not valid number, must be between 0 and 4.');\r",
+                "    return;\r",
+                "}\r",
+                "if (stick != \"LEFT\" && stick != \"RIGHT\") {\r",
+                "    console.error('Parameter stick in condition: \"Gamepad stick pushed (axis)\", is not valid, must be LEFT or RIGHT');\r",
+                "    return;\r",
+                "}\r",
+                "if (direction != \"UP\" && direction != \"DOWN\" && direction != \"LEFT\" && direction != \"RIGHT\" && direction != \"ANY\") {\r",
+                "    console.error('Parameter deadzone in condition: \"Gamepad stick pushed (axis)\", is not valid, must be UP, DOWN, LEFT or RIGHT');\r",
+                "    return;\r",
+                "}\r",
+                "\r",
+                "const gamepad = gamepads[playerId];\r",
+                "\r",
+                "//we need keep this condition because when use have not yet plug her controller we can't get the controller in the gamepad variable.\r",
+                "if (gamepad == null) {\r",
+                "    eventsFunctionContext.returnValue = false;\r",
+                "    return;\r",
+                "}\r",
+                "\r",
+                "\r",
+                "//Define in onFirstSceneLoaded function\r",
+                "const getNormalizedAxisValue = gdjs._extensionController.getNormalizedAxisValue;\r",
+                "\r",
+                "switch (stick) {\r",
+                "    case 'LEFT':\r",
+                "        switch (direction) {\r",
+                "            case 'LEFT':\r",
+                "                if (getNormalizedAxisValue(gamepad.axes[0], playerId) < 0) {\r",
+                "                    eventsFunctionContext.returnValue = true;\r",
+                "                    return;\r",
+                "                }\r",
+                "                break;\r",
+                "\r",
+                "            case 'RIGHT':\r",
+                "                if (getNormalizedAxisValue(gamepad.axes[0], playerId) > 0) {\r",
+                "                    eventsFunctionContext.returnValue = true;\r",
+                "                    return;\r",
+                "                }\r",
+                "                break;\r",
+                "\r",
+                "            case 'UP':\r",
+                "                if (getNormalizedAxisValue(gamepad.axes[1], playerId) < 0) {\r",
+                "                    eventsFunctionContext.returnValue = true;\r",
+                "                    return;\r",
+                "                }\r",
+                "                break;\r",
+                "\r",
+                "            case 'DOWN':\r",
+                "                if (getNormalizedAxisValue(gamepad.axes[1], playerId) > 0) {\r",
+                "                    eventsFunctionContext.returnValue = true;\r",
+                "                    return;\r",
+                "                }\r",
+                "                break;\r",
+                "\r",
+                "            case 'ANY':\r",
+                "                if (   getNormalizedAxisValue(gamepad.axes[0], playerId) < 0\r",
+                "                    || getNormalizedAxisValue(gamepad.axes[0], playerId) > 0\r",
+                "                    || getNormalizedAxisValue(gamepad.axes[1], playerId) < 0 \r",
+                "                    || getNormalizedAxisValue(gamepad.axes[1], playerId) > 0) {\r",
+                "                    eventsFunctionContext.returnValue = true;\r",
+                "                    return;\r",
+                "                }\r",
+                "                break;\r",
+                "\r",
+                "            default:\r",
+                "                console.error('The value Direction on stick Left on the condition: \"Gamepad stick pushed (axis)\" is not valid.');\r",
+                "                eventsFunctionContext.returnValue = false;\r",
+                "                break;\r",
+                "        }\r",
+                "        break;\r",
+                "\r",
+                "    case 'RIGHT':\r",
+                "        switch (direction) {\r",
+                "            case 'LEFT':\r",
+                "                if (getNormalizedAxisValue(gamepad.axes[2], playerId) < 0) {\r",
+                "                    eventsFunctionContext.returnValue = true;\r",
+                "                    return;\r",
+                "                }\r",
+                "                break;\r",
+                "\r",
+                "            case 'RIGHT':\r",
+                "                if (getNormalizedAxisValue(gamepad.axes[2], playerId) > 0) {\r",
+                "                    eventsFunctionContext.returnValue = true;\r",
+                "                    return;\r",
+                "                }\r",
+                "                break;\r",
+                "\r",
+                "            case 'UP':\r",
+                "                if (getNormalizedAxisValue(gamepad.axes[3], playerId) < 0) {\r",
+                "                    eventsFunctionContext.returnValue = true;\r",
+                "                    return;\r",
+                "                }\r",
+                "                break;\r",
+                "\r",
+                "            case 'DOWN':\r",
+                "                if (getNormalizedAxisValue(gamepad.axes[3], playerId) > 0) {\r",
+                "                    eventsFunctionContext.returnValue = true;\r",
+                "                    return;\r",
+                "                }\r",
+                "                break;\r",
+                "\r",
+                "            case 'ANY':\r",
+                "                if (   getNormalizedAxisValue(gamepad.axes[2], playerId) < 0\r",
+                "                    || getNormalizedAxisValue(gamepad.axes[2], playerId) > 0\r",
+                "                    || getNormalizedAxisValue(gamepad.axes[3], playerId) < 0 \r",
+                "                    || getNormalizedAxisValue(gamepad.axes[3], playerId) > 0) {\r",
+                "                    eventsFunctionContext.returnValue = true;\r",
+                "                    return;\r",
+                "                }\r",
+                "                break;\r",
+                "\r",
+                "            default:\r",
+                "                console.error('The value Direction on stick Right on the condition: \"Gamepad stick pushed (axis)\" is not valid.');\r",
+                "                eventsFunctionContext.returnValue = false;\r",
+                "                break;\r",
+                "        }\r",
+                "        break;\r",
+                "\r",
+                "    default:\r",
+                "        console.error('The value Stick on the condition: \"Gamepad stick pushed (axis)\" is not valid.');\r",
+                "        eventsFunctionContext.returnValue = false;\r",
+                "        break;\r",
+                "}\r",
+                "\r",
+                "eventsFunctionContext.returnValue = false;\r",
+                ""
+              ],
+              "parameterObjects": "",
+              "useStrict": true,
+              "eventsSheetExpanded": true
+            }
+          ],
+          "parameters": [
+            {
+              "description": "The gamepad identifier: 1, 2, 3 or 4",
+              "name": "player_ID",
+              "type": "expression"
+            },
+            {
+              "description": "Stick: \"Left\" or \"Right\"",
+              "name": "stick",
+              "supplementaryInformation": "[\"Left\",\"Right\"]",
+              "type": "stringWithSelector"
+            },
+            {
+              "description": "Direction",
+              "name": "direction",
+              "supplementaryInformation": "[\"Up\",\"Down\",\"Left\",\"Right\",\"Any\"]",
+              "type": "stringWithSelector"
+            }
+          ],
+          "objectGroups": []
+        },
+        {
+          "description": "Return the number of connected gamepads.",
+          "fullName": "Connected gamepads number",
+          "functionType": "Expression",
+          "name": "ConnectedGamepadsCount",
+          "sentence": "",
+          "events": [
+            {
+              "type": "BuiltinCommonInstructions::Standard",
+              "conditions": [],
+              "actions": []
+            },
+            {
+              "type": "BuiltinCommonInstructions::JsCode",
+              "inlineCode": [
+                "/** @type {Gamepad[]} */\r",
+                "const gamepads = navigator.getGamepads ? navigator.getGamepads() : (navigator.webkitGetGamepads ? navigator.webkitGetGamepads() : []);\r",
+                "\r",
+                "// Gamepads can be disconnected and become null, so we have to filter them.\r",
+                "eventsFunctionContext.returnValue = Object.keys(gamepads).filter(key => !!gamepads[key]).length;\r",
+                ""
+              ],
+              "parameterObjects": "",
+              "useStrict": true,
+              "eventsSheetExpanded": true
+            }
+          ],
+          "expressionType": {
+            "type": "expression"
+          },
+          "parameters": [],
+          "objectGroups": []
+        },
+        {
+          "description": "Return a string containing informations about the specified gamepad.",
+          "fullName": "Gamepad type",
+          "functionType": "StringExpression",
+          "name": "GamepadType",
+          "sentence": "Player _PARAM1_ use _PARAM2_ controller",
+          "events": [
+            {
+              "type": "BuiltinCommonInstructions::JsCode",
+              "inlineCode": [
+                "/** @type {Gamepad[]} */",
+                "const gamepads = navigator.getGamepads ? navigator.getGamepads() : (navigator.webkitGetGamepads ? navigator.webkitGetGamepads() : []);",
+                "",
+                "//Get function parameter",
+                "const playerId = eventsFunctionContext.getArgument(\"player_ID\") - 1;",
+                "",
+                "if (playerId < 0 || playerId > 4) {",
+                "    console.error('Parameter gamepad identifier in string expression: \"Gamepad type\", is not valid number, must be between 0 and 4');",
+                "    return;",
+                "}",
+                "",
+                "const gamepad = gamepads[playerId];",
+                "",
+                "//we need keep this condition because when use have not yet plug her controller we can't get the controller in the gamepad variable.",
+                "if (gamepad == null) return;",
+                "",
+                "eventsFunctionContext.returnValue = (gamepad && gamepad.id) ? gamepad.id : \"No information for player \" + (playerId + 1)",
+                ""
+              ],
+              "parameterObjects": "",
+              "useStrict": true,
+              "eventsSheetExpanded": true
+            }
+          ],
+          "expressionType": {
+            "type": "string"
+          },
+          "parameters": [
+            {
+              "description": "The gamepad identifier: 1, 2, 3 or 4",
+              "name": "player_ID",
+              "type": "expression"
+            }
+          ],
+          "objectGroups": []
+        },
+        {
+          "description": "Check if the specified gamepad has the specified information in its description. Useful to know if the gamepad is a Xbox or PS4 controller.",
+          "fullName": "Gamepad type",
+          "functionType": "Condition",
+          "name": "C_Controller_type",
+          "sentence": "Gamepad _PARAM1_ is a _PARAM2_ controller",
+          "events": [
+            {
+              "type": "BuiltinCommonInstructions::JsCode",
+              "inlineCode": [
+                "/** @type {Gamepad[]} */",
+                "const gamepads = navigator.getGamepads ? navigator.getGamepads() : (navigator.webkitGetGamepads ? navigator.webkitGetGamepads() : []);",
+                "",
+                "//Get function parameters",
+                "const playerId = eventsFunctionContext.getArgument(\"player_ID\") - 1;",
+                "const controllerType = eventsFunctionContext.getArgument(\"controller_type\").toUpperCase();",
+                "",
+                "if (playerId < 0 || playerId > 4) {",
+                "    console.error('Parameter gamepad identifier in condition: \"Gamepad type\", is not valid number, must be between 0 and 4.');",
+                "    return;",
+                "}",
+                "if (controllerType === \"\") {",
+                "    console.error('Parameter type in condition: \"Gamepad type\", is not a string.');",
+                "    return;",
+                "}",
+                "",
+                "const gamepad = gamepads[playerId];",
+                "",
+                "//we need keep this condition because when use have not yet plug her controller we can't get the controller in the gamepad variable.",
+                "if (gamepad == null) return;",
+                "",
+                "",
+                "if (controllerType == \"XBOX\") {",
+                "    eventsFunctionContext.returnValue = gdjs._extensionController.isXbox(gamepad);",
+                "} else {",
+                "    eventsFunctionContext.returnValue = gamepad ? gamepad.id.toUpperCase().indexOf(controllerType) !== -1 : false;",
+                "}"
+              ],
+              "parameterObjects": "",
+              "useStrict": true,
+              "eventsSheetExpanded": true
+            }
+          ],
+          "parameters": [
+            {
+              "description": "The gamepad identifier: 1, 2, 3 or 4",
+              "name": "player_ID",
+              "type": "expression"
+            },
+            {
+              "description": "Type: \"Xbox\", \"PS4\", \"Steam\" or \"PS3\" (among other)",
+              "name": "controller_type",
+              "type": "string"
+            }
+          ],
+          "objectGroups": []
+        },
+        {
+          "description": "Check if a gamepad is connected.",
+          "fullName": "Gamepad connected",
+          "functionType": "Condition",
+          "name": "C_Controller_X_is_connected",
+          "sentence": "Gamepad _PARAM1_ is plugged and connected",
+          "events": [
+            {
+              "type": "BuiltinCommonInstructions::JsCode",
+              "inlineCode": [
+                "/** @type {Gamepad[]} */",
+                "const gamepads = navigator.getGamepads ? navigator.getGamepads() : (navigator.webkitGetGamepads ? navigator.webkitGetGamepads() : []);",
+                "",
+                "//Get function parameter",
+                "const playerId = eventsFunctionContext.getArgument(\"player_ID\") - 1;",
+                "",
+                "if (playerId < 0 || playerId > 4) {",
+                "    console.error('Parameter gamepad identifier in condition: \"Gamepad connected\", is not valid number, must be between 0 and 4.');",
+                "    return;",
+                "}",
+                "",
+                "// If gamepad was disconnected it will be null (so this will return false)",
+                "// If gamepad was never connected it will be undefined (so this will return false)",
+                "eventsFunctionContext.returnValue = !!gamepads[playerId];"
+              ],
+              "parameterObjects": "",
+              "useStrict": true,
+              "eventsSheetExpanded": true
+            }
+          ],
+          "parameters": [
+            {
+              "description": "The gamepad identifier: 1, 2, 3 or 4",
+              "name": "player_ID",
+              "type": "expression"
+            }
+          ],
+          "objectGroups": []
+        },
+        {
+          "description": "Generate a vibration on the specified controller. Might only work if the game is running in a recent web browser.",
+          "fullName": "Gamepad vibration",
+          "functionType": "Action",
+          "name": "A_Vibrate_controller",
+          "sentence": "Make gamepad _PARAM1_ vibrate for _PARAM2_ seconds",
+          "events": [
+            {
+              "type": "BuiltinCommonInstructions::JsCode",
+              "inlineCode": [
+                "/** @type {Gamepad[]} */",
+                "//Vibration work only on game in browser.",
+                "const gamepads = navigator.getGamepads ? navigator.getGamepads() : (navigator.webkitGetGamepads ? navigator.webkitGetGamepads() : []);",
+                "",
+                "//Get function parameters",
+                "const playerId = eventsFunctionContext.getArgument(\"Player_ID\") - 1;",
+                "const duration = eventsFunctionContext.getArgument(\"Duration\") || 1;",
+                "",
+                "if (playerId < 0 || playerId > 4) {",
+                "  console.error('Parameter gamepad identifier in action: \"Gamepad connected\", is not valid number, must be between 0 and 4.');",
+                "  return;",
+                "}",
+                "",
+                "const gamepad = gamepads[playerId];",
+                "",
+                "//we need keep this condition because when use have not yet plug her controller we can't get the controller in the gamepad variable.",
+                "if (gamepad == null) return;",
+                "",
+                "if (gamepad && gamepad.vibrationActuator) {",
+                "  gamepad.vibrationActuator.playEffect(\"dual-rumble\", {",
+                "    startDelay: 0,",
+                "    duration: duration * 1000,",
+                "    weakMagnitude: 1.0,",
+                "    strongMagnitude: 1.0",
+                "  });",
+                "}"
+              ],
+              "parameterObjects": "",
+              "useStrict": true,
+              "eventsSheetExpanded": true
+            }
+          ],
+          "parameters": [
+            {
+              "description": "The gamepad identifier: 1, 2, 3 or 4",
+              "name": "Player_ID",
+              "type": "expression"
+            },
+            {
+              "description": "Time of the vibration, in seconds (optional, default value is 1)",
+              "name": "Duration",
+              "type": "expression"
+            }
+          ],
+          "objectGroups": []
+        },
+        {
+          "description": "Generate an advanced vibration on the specified controller. Incompatible with Firefox.",
+          "fullName": "Advanced gamepad vibration",
+          "functionType": "Action",
+          "name": "A_Advanced_Vibration_Controller",
+          "sentence": "Make gamepad _PARAM1_ vibrate for _PARAM2_ seconds with the vibration magnitude of _PARAM3_ and _PARAM4_",
+          "events": [
+            {
+              "type": "BuiltinCommonInstructions::JsCode",
+              "inlineCode": [
+                "/** @type {Gamepad[]} */",
+                "//Vibration work only on game in browser.",
+                "const gamepads = navigator.getGamepads ? navigator.getGamepads() : (navigator.webkitGetGamepads ? navigator.webkitGetGamepads() : []);",
+                "",
+                "//Get function parameters",
+                "const playerId = eventsFunctionContext.getArgument(\"Player_ID\") - 1;",
+                "const duration = eventsFunctionContext.getArgument(\"Duration\") || 1;",
+                "const strongRumbleMagnitude = eventsFunctionContext.getArgument(\"StrongMagnitude\");",
+                "const weakRumbleMagnitude = eventsFunctionContext.getArgument(\"WeakMagnitude\");",
+                "",
+                "if (playerId < 0 || playerId > 4) {",
+                "  console.error('Parameter gamepad identifier in action: \"Advanced gamepad vibration\", is not valid number, must be between 0 and 4.');",
+                "  return;",
+                "}",
+                "if (weakRumbleMagnitude < 0 || weakRumbleMagnitude > 1) {",
+                "  console.error('Parameter weakRumble identifier in action: \"Advanced gamepad vibration\", is not valid number, must be between 0 and 1.');",
+                "  return;",
+                "}",
+                "if (strongRumbleMagnitude < 0 || strongRumbleMagnitude > 1) {",
+                "  console.error('Parameter strongRumble identifier in action: \"Advanced gamepad vibration\", is not valid number, must be between 0 and 1.');",
+                "  return;",
+                "}",
+                "",
+                "const gamepad = gamepads[playerId];",
+                "",
+                "//we need keep this condition because when use have not yet plug the controller we can't get the controller in the gamepad variable.",
+                "if (gamepad == null) return;",
+                "",
+                "if (gamepad && gamepad.vibrationActuator) {",
+                "  gamepad.vibrationActuator.playEffect(\"dual-rumble\", {",
+                "    startDelay: 0,",
+                "    duration: duration * 1000,",
+                "    weakMagnitude: weakRumbleMagnitude,",
+                "    strongMagnitude: strongRumbleMagnitude",
+                "  });",
+                "}",
+                "",
+                "gdjs._extensionController.players[playerId].rumble.duration = duration;",
+                "gdjs._extensionController.players[playerId].rumble.elapsedTime = 0;",
+                "gdjs._extensionController.players[playerId].rumble.weakMagnitude = weakRumbleMagnitude;",
+                "gdjs._extensionController.players[playerId].rumble.strongMagnitude = strongRumbleMagnitude;"
+              ],
+              "parameterObjects": "",
+              "useStrict": true,
+              "eventsSheetExpanded": true
+            }
+          ],
+          "parameters": [
+            {
+              "description": "The gamepad identifier: 1, 2, 3 or 4",
+              "name": "Player_ID",
+              "type": "expression"
+            },
+            {
+              "description": "Time of the vibration, in seconds (optional, default value is 1)",
+              "name": "Duration",
+              "type": "expression"
+            },
+            {
+              "description": "Strong rumble magnitude (from 0 to 1)",
+              "name": "StrongMagnitude",
+              "type": "expression"
+            },
+            {
+              "description": "Weak rumble magnitude (from 0 to 1)",
+              "name": "WeakMagnitude",
+              "type": "expression"
+            }
+          ],
+          "objectGroups": []
+        },
+        {
+          "description": "Change a vibration on the specified controller. Incompatible with Firefox.",
+          "fullName": "Change gamepad active vibration",
+          "functionType": "Action",
+          "name": "A_Change_Vibration_Magnitude",
+          "sentence": "Change the vibration magnitude of _PARAM2_ & _PARAM3_ on gamepad _PARAM1_",
+          "events": [
+            {
+              "type": "BuiltinCommonInstructions::JsCode",
+              "inlineCode": [
+                "/** @type {Gamepad[]} */",
+                "//Vibration work only on game in browser.",
+                "const gamepads = navigator.getGamepads ? navigator.getGamepads() : (navigator.webkitGetGamepads ? navigator.webkitGetGamepads() : []);",
+                "",
+                "//Get function parameters",
+                "const playerId = eventsFunctionContext.getArgument(\"Player_ID\") - 1;",
+                "const elapsedTime = gdjs._extensionController.players[playerId].rumble.elapsedTime || 0;",
+                "const originalDuration = gdjs._extensionController.players[playerId].rumble.duration || 1;",
+                "const strongRumbleMagnitude = eventsFunctionContext.getArgument(\"StrongMagnitude\");",
+                "const weakRumbleMagnitude = eventsFunctionContext.getArgument(\"WeakMagnitude\");",
+                "",
+                "",
+                "if (playerId < 0 || playerId > 4) {",
+                "  console.error('Parameter gamepad identifier in action: \"Change gamepad active vibration\", is not valid number, must be between 0 and 4.');",
+                "  return;",
+                "}",
+                "if (weakRumbleMagnitude < 0 || weakRumbleMagnitude > 1) {",
+                "  console.error('Parameter weakRumble identifier in action: \"Change gamepad active vibration\", is not valid number, must be between 0 and 1.');",
+                "  return;",
+                "}",
+                "if (strongRumbleMagnitude < 0 || strongRumbleMagnitude > 1) {",
+                "  console.error('Parameter strongRumble identifier in action: \"Change gamepad active vibration\", is not valid number, must be between 0 and 1.');",
+                "  return;",
+                "}",
+                "",
+                "const gamepad = gamepads[playerId];",
+                "",
+                "//we need keep this condition because when use have not yet plug the controller we can't get the controller in the gamepad variable.",
+                "if (gamepad == null) return;",
+                "",
+                "if (originalDuration - elapsedTime <= 0) return;",
+                "",
+                "if (gamepad && gamepad.vibrationActuator) {",
+                "  gamepad.vibrationActuator.playEffect(\"dual-rumble\", {",
+                "    startDelay: 0,",
+                "    duration: 1000 * (originalDuration - elapsedTime),",
+                "    weakMagnitude: weakRumbleMagnitude,",
+                "    strongMagnitude: strongRumbleMagnitude",
+                "  });",
+                "}",
+                "",
+                "gdjs._extensionController.players[playerId].rumble.weakMagnitude = weakRumbleMagnitude;",
+                "gdjs._extensionController.players[playerId].rumble.strongMagnitude = strongRumbleMagnitude;"
+              ],
+              "parameterObjects": "",
+              "useStrict": true,
+              "eventsSheetExpanded": true
+            }
+          ],
+          "parameters": [
+            {
+              "description": "The gamepad identifier: 1, 2, 3 or 4",
+              "name": "Player_ID",
+              "type": "expression"
+            },
+            {
+              "description": "Strong rumble magnitude (from 0 to 1)",
+              "name": "StrongMagnitude",
+              "type": "expression"
+            },
+            {
+              "description": "Weak rumble magnitude (from 0 to 1)",
+              "name": "WeakMagnitude",
+              "type": "expression"
+            }
+          ],
+          "objectGroups": []
+        },
+        {
+          "description": "Check if any button is released on a gamepad.",
+          "fullName": "Any gamepad button released",
+          "functionType": "Condition",
+          "name": "C_any_button_released",
+          "sentence": "Any button of gamepad _PARAM1_ is released",
+          "events": [
+            {
+              "type": "BuiltinCommonInstructions::Standard",
+              "conditions": [],
+              "actions": []
+            },
+            {
+              "type": "BuiltinCommonInstructions::JsCode",
+              "inlineCode": [
+                "/** @type {Gamepad[]} */\r",
+                "const gamepads = navigator.getGamepads ? navigator.getGamepads() : (navigator.webkitGetGamepads ? navigator.webkitGetGamepads() : []);\r",
+                "\r",
+                "//Get function parameters\r",
+                "const playerId = eventsFunctionContext.getArgument(\"player_ID\") - 1;\r",
+                "\r",
+                "if (playerId < 0 || playerId > 4) {\r",
+                "\tconsole.error('Parameter gamepad identifier in condition: \"Any gamepad button released\", is not valid number, must be between 0 and 4.');\r",
+                "\treturn;\r",
+                "}\r",
+                "\r",
+                "const gamepad = gamepads[playerId];\r",
+                "\r",
+                "//we need keep this condition because when use have not yet plug her controller we can't get the controller in the gamepad variable.\r",
+                "if (gamepad == null) return;\r",
+                "\r",
+                "for (let buttonId = 0; buttonId < gamepad.buttons.length; buttonId++) { //For each buttons on current frame.\r",
+                "\r",
+                "\tif (buttonId === undefined) {\r",
+                "\t\teventsFunctionContext.returnValue = false;\r",
+                "\t\treturn;\r",
+                "\t}\r",
+                "\r",
+                "\t//Get previous value or define value by default for the current button\r",
+                "\tgdjs._extensionController.players[playerId].previousFrameStateButtons[buttonId] = gdjs._extensionController.players[playerId].previousFrameStateButtons[buttonId] || { pressed: false };\r",
+                "\r",
+                "\t//Get state of the button at previous frame\r",
+                "\tconst previousStateButtonIsPressed = gdjs._extensionController.players[playerId].previousFrameStateButtons[buttonId].pressed;\r",
+                "\r",
+                "\t//Get the state of the button on the current frame.\r",
+                "\tconst currentFrameStateButtonIsPressed = gamepad.buttons[buttonId].pressed;\r",
+                "\r",
+                "\t//When previousStateButtonIsPressed is true and actual button state is not pressed\r",
+                "\t//Player have release the button\r",
+                "\tif (previousStateButtonIsPressed === true && currentFrameStateButtonIsPressed === false) {\r",
+                "\t\tgdjs._extensionController.players[playerId].previousFrameStateButtons[buttonId].pressed = true;\r",
+                "\t\teventsFunctionContext.returnValue = true;\r",
+                "\t\t//break;\r",
+                "\t\treturn;\r",
+                "\t} else {\r",
+                "\t\t//The player didn't released the button yet, the previous frame state is still true\r",
+                "\t\tgdjs._extensionController.players[playerId].previousFrameStateButtons[buttonId].pressed = false;\r",
+                "\t\teventsFunctionContext.returnValue = false;\r",
+                "\t}\r",
+                "\r",
+                "\tif (currentFrameStateButtonIsPressed) gdjs._extensionController.players[playerId].lastButtonUsed = buttonId;\r",
+                "}\r",
+                ""
+              ],
+              "parameterObjects": "",
+              "useStrict": true,
+              "eventsSheetExpanded": true
+            }
+          ],
+          "parameters": [
+            {
+              "description": "The gamepad identifier: 1, 2, 3 or 4",
+              "name": "player_ID",
+              "type": "expression"
+            }
+          ],
+          "objectGroups": []
+        },
+        {
+          "description": "Return the strength of the weak vibration motor on the gamepad of a player.",
+          "fullName": "Weak rumble magnitude",
+          "functionType": "Expression",
+          "name": "WeakVibrationMagnitude",
+          "sentence": "",
+          "events": [
+            {
+              "type": "BuiltinCommonInstructions::JsCode",
+              "inlineCode": [
+                "const playerId = eventsFunctionContext.getArgument(\"Player_ID\") - 1;\r",
+                "eventsFunctionContext.returnValue = gdjs._extensionController.players[playerId].rumble.weakMagnitude;"
+              ],
+              "parameterObjects": "",
+              "useStrict": true,
+              "eventsSheetExpanded": false
+            }
+          ],
+          "expressionType": {
+            "type": "expression"
+          },
+          "parameters": [
+            {
+              "description": "The gamepad identifier: 1, 2, 3 or 4",
+              "name": "Player_ID",
+              "type": "expression"
+            }
+          ],
+          "objectGroups": []
+        },
+        {
+          "description": "Return the strength of the strong vibration motor on the gamepad of a player.",
+          "fullName": "Strong rumble magnitude",
+          "functionType": "Expression",
+          "name": "StrongVibrationMagnitude",
+          "sentence": "",
+          "events": [
+            {
+              "type": "BuiltinCommonInstructions::JsCode",
+              "inlineCode": [
+                "const playerId = eventsFunctionContext.getArgument(\"Player_ID\") - 1;\r",
+                "eventsFunctionContext.returnValue = gdjs._extensionController.players[playerId].rumble.strongMagnitude;"
+              ],
+              "parameterObjects": "",
+              "useStrict": true,
+              "eventsSheetExpanded": false
+            }
+          ],
+          "expressionType": {
+            "type": "expression"
+          },
+          "parameters": [
+            {
+              "description": "The gamepad identifier: 1, 2, 3 or 4",
+              "name": "Player_ID",
+              "type": "expression"
+            }
+          ],
+          "objectGroups": []
+        }
+      ],
+      "eventsBasedBehaviors": [
+        {
+          "description": "Control a platformer character with a gamepad.",
+          "fullName": "Platformer gamepad mapper",
+          "name": "PlatformerGamepadMapper",
+          "objectType": "",
+          "eventsFunctions": [
+            {
+              "fullName": "",
+              "functionType": "Action",
+              "name": "doStepPreEvents",
+              "sentence": "",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [
+                    {
+                      "type": {
+                        "value": "Gamepads::C_Controller_X_is_connected"
+                      },
+                      "parameters": [
+                        "",
+                        "GamepadIdentifier",
+                        ""
+                      ]
+                    }
+                  ],
+                  "actions": [],
+                  "events": [
+                    {
+                      "type": "BuiltinCommonInstructions::Standard",
+                      "conditions": [
+                        {
+                          "type": {
+                            "value": "Gamepads::PlatformerGamepadMapper::PropertyUseArrows"
+                          },
+                          "parameters": [
+                            "Object",
+                            "Behavior"
+                          ]
+                        }
+                      ],
+                      "actions": [],
+                      "events": [
+                        {
+                          "type": "BuiltinCommonInstructions::Standard",
+                          "conditions": [
+                            {
+                              "type": {
+                                "value": "Gamepads::C_Button_pressed"
+                              },
+                              "parameters": [
+                                "",
+                                "GamepadIdentifier",
+                                "\"Left\"",
+                                "\"Left\""
+                              ]
+                            }
+                          ],
+                          "actions": [
+                            {
+                              "type": {
+                                "value": "PlatformBehavior::SimulateLeftKey"
+                              },
+                              "parameters": [
+                                "Object",
+                                "PlatformerCharacter"
+                              ]
+                            }
+                          ]
+                        },
+                        {
+                          "type": "BuiltinCommonInstructions::Standard",
+                          "conditions": [
+                            {
+                              "type": {
+                                "value": "Gamepads::C_Button_pressed"
+                              },
+                              "parameters": [
+                                "",
+                                "GamepadIdentifier",
+                                "\"Right\"",
+                                "\"Left\""
+                              ]
+                            }
+                          ],
+                          "actions": [
+                            {
+                              "type": {
+                                "value": "PlatformBehavior::SimulateRightKey"
+                              },
+                              "parameters": [
+                                "Object",
+                                "PlatformerCharacter"
+                              ]
+                            }
+                          ]
+                        },
+                        {
+                          "type": "BuiltinCommonInstructions::Standard",
+                          "conditions": [
+                            {
+                              "type": {
+                                "value": "Gamepads::C_Button_pressed"
+                              },
+                              "parameters": [
+                                "",
+                                "GamepadIdentifier",
+                                "\"Up\"",
+                                "\"Left\""
+                              ]
+                            }
+                          ],
+                          "actions": [
+                            {
+                              "type": {
+                                "value": "PlatformBehavior::SimulateUpKey"
+                              },
+                              "parameters": [
+                                "Object",
+                                "PlatformerCharacter"
+                              ]
+                            },
+                            {
+                              "type": {
+                                "value": "PlatformBehavior::SimulateLadderKey"
+                              },
+                              "parameters": [
+                                "Object",
+                                "PlatformerCharacter"
+                              ]
+                            }
+                          ]
+                        },
+                        {
+                          "type": "BuiltinCommonInstructions::Standard",
+                          "conditions": [
+                            {
+                              "type": {
+                                "value": "Gamepads::C_Button_pressed"
+                              },
+                              "parameters": [
+                                "",
+                                "GamepadIdentifier",
+                                "\"Down\"",
+                                "\"Left\""
+                              ]
+                            }
+                          ],
+                          "actions": [
+                            {
+                              "type": {
+                                "value": "PlatformBehavior::SimulateDownKey"
+                              },
+                              "parameters": [
+                                "Object",
+                                "PlatformerCharacter"
+                              ]
+                            }
+                          ]
+                        }
+                      ]
+                    },
+                    {
+                      "type": "BuiltinCommonInstructions::Standard",
+                      "conditions": [
+                        {
+                          "type": {
+                            "value": "Gamepads::PlatformerGamepadMapper::PropertyUseLeftStick"
+                          },
+                          "parameters": [
+                            "Object",
+                            "Behavior"
+                          ]
+                        }
+                      ],
+                      "actions": [],
+                      "events": [
+                        {
+                          "type": "BuiltinCommonInstructions::Standard",
+                          "conditions": [
+                            {
+                              "type": {
+                                "value": "Gamepads::C_Axis_pushed"
+                              },
+                              "parameters": [
+                                "",
+                                "GamepadIdentifier",
+                                "\"Left\"",
+                                "\"Left\"",
+                                ""
+                              ]
+                            }
+                          ],
+                          "actions": [
+                            {
+                              "type": {
+                                "value": "PlatformBehavior::SimulateLeftKey"
+                              },
+                              "parameters": [
+                                "Object",
+                                "PlatformerCharacter"
+                              ]
+                            }
+                          ]
+                        },
+                        {
+                          "type": "BuiltinCommonInstructions::Standard",
+                          "conditions": [
+                            {
+                              "type": {
+                                "value": "Gamepads::C_Axis_pushed"
+                              },
+                              "parameters": [
+                                "",
+                                "GamepadIdentifier",
+                                "\"Left\"",
+                                "\"Right\"",
+                                ""
+                              ]
+                            }
+                          ],
+                          "actions": [
+                            {
+                              "type": {
+                                "value": "PlatformBehavior::SimulateRightKey"
+                              },
+                              "parameters": [
+                                "Object",
+                                "PlatformerCharacter"
+                              ]
+                            }
+                          ]
+                        },
+                        {
+                          "type": "BuiltinCommonInstructions::Standard",
+                          "conditions": [
+                            {
+                              "type": {
+                                "value": "Gamepads::C_Axis_pushed"
+                              },
+                              "parameters": [
+                                "",
+                                "GamepadIdentifier",
+                                "\"Left\"",
+                                "\"Up\"",
+                                ""
+                              ]
+                            }
+                          ],
+                          "actions": [
+                            {
+                              "type": {
+                                "value": "PlatformBehavior::SimulateUpKey"
+                              },
+                              "parameters": [
+                                "Object",
+                                "PlatformerCharacter"
+                              ]
+                            },
+                            {
+                              "type": {
+                                "value": "PlatformBehavior::SimulateLadderKey"
+                              },
+                              "parameters": [
+                                "Object",
+                                "PlatformerCharacter"
+                              ]
+                            }
+                          ]
+                        },
+                        {
+                          "type": "BuiltinCommonInstructions::Standard",
+                          "conditions": [
+                            {
+                              "type": {
+                                "value": "Gamepads::C_Axis_pushed"
+                              },
+                              "parameters": [
+                                "",
+                                "GamepadIdentifier",
+                                "\"Left\"",
+                                "\"Down\"",
+                                ""
+                              ]
+                            }
+                          ],
+                          "actions": [
+                            {
+                              "type": {
+                                "value": "PlatformBehavior::SimulateDownKey"
+                              },
+                              "parameters": [
+                                "Object",
+                                "PlatformerCharacter"
+                              ]
+                            }
+                          ]
+                        }
+                      ]
+                    },
+                    {
+                      "type": "BuiltinCommonInstructions::Standard",
+                      "conditions": [
+                        {
+                          "type": {
+                            "value": "Gamepads::PlatformerGamepadMapper::PropertyUseRightStick"
+                          },
+                          "parameters": [
+                            "Object",
+                            "Behavior"
+                          ]
+                        }
+                      ],
+                      "actions": [],
+                      "events": [
+                        {
+                          "type": "BuiltinCommonInstructions::Standard",
+                          "conditions": [
+                            {
+                              "type": {
+                                "value": "Gamepads::C_Axis_pushed"
+                              },
+                              "parameters": [
+                                "",
+                                "GamepadIdentifier",
+                                "\"Right\"",
+                                "\"Left\"",
+                                ""
+                              ]
+                            }
+                          ],
+                          "actions": [
+                            {
+                              "type": {
+                                "value": "PlatformBehavior::SimulateLeftKey"
+                              },
+                              "parameters": [
+                                "Object",
+                                "PlatformerCharacter"
+                              ]
+                            }
+                          ]
+                        },
+                        {
+                          "type": "BuiltinCommonInstructions::Standard",
+                          "conditions": [
+                            {
+                              "type": {
+                                "value": "Gamepads::C_Axis_pushed"
+                              },
+                              "parameters": [
+                                "",
+                                "GamepadIdentifier",
+                                "\"Right\"",
+                                "\"Right\"",
+                                ""
+                              ]
+                            }
+                          ],
+                          "actions": [
+                            {
+                              "type": {
+                                "value": "PlatformBehavior::SimulateRightKey"
+                              },
+                              "parameters": [
+                                "Object",
+                                "PlatformerCharacter"
+                              ]
+                            }
+                          ]
+                        },
+                        {
+                          "type": "BuiltinCommonInstructions::Standard",
+                          "conditions": [
+                            {
+                              "type": {
+                                "value": "Gamepads::C_Axis_pushed"
+                              },
+                              "parameters": [
+                                "",
+                                "GamepadIdentifier",
+                                "\"Right\"",
+                                "\"Up\"",
+                                ""
+                              ]
+                            }
+                          ],
+                          "actions": [
+                            {
+                              "type": {
+                                "value": "PlatformBehavior::SimulateUpKey"
+                              },
+                              "parameters": [
+                                "Object",
+                                "PlatformerCharacter"
+                              ]
+                            },
+                            {
+                              "type": {
+                                "value": "PlatformBehavior::SimulateLadderKey"
+                              },
+                              "parameters": [
+                                "Object",
+                                "PlatformerCharacter"
+                              ]
+                            }
+                          ]
+                        },
+                        {
+                          "type": "BuiltinCommonInstructions::Standard",
+                          "conditions": [
+                            {
+                              "type": {
+                                "value": "Gamepads::C_Axis_pushed"
+                              },
+                              "parameters": [
+                                "",
+                                "GamepadIdentifier",
+                                "\"Right\"",
+                                "\"Down\"",
+                                ""
+                              ]
+                            }
+                          ],
+                          "actions": [
+                            {
+                              "type": {
+                                "value": "PlatformBehavior::SimulateDownKey"
+                              },
+                              "parameters": [
+                                "Object",
+                                "PlatformerCharacter"
+                              ]
+                            }
+                          ]
+                        }
+                      ]
+                    },
+                    {
+                      "type": "BuiltinCommonInstructions::Standard",
+                      "conditions": [
+                        {
+                          "type": {
+                            "value": "Gamepads::PlatformerGamepadMapper::PropertyJumpButton"
+                          },
+                          "parameters": [
+                            "Object",
+                            "Behavior",
+                            "=",
+                            "\"A or Cross\""
+                          ]
+                        },
+                        {
+                          "type": {
+                            "value": "Gamepads::C_Button_pressed"
+                          },
+                          "parameters": [
+                            "",
+                            "GamepadIdentifier",
+                            "\"A\"",
+                            "\"Left\""
+                          ]
+                        }
+                      ],
+                      "actions": [
+                        {
+                          "type": {
+                            "value": "PlatformBehavior::SimulateJumpKey"
+                          },
+                          "parameters": [
+                            "Object",
+                            "PlatformerCharacter"
+                          ]
+                        }
+                      ]
+                    },
+                    {
+                      "type": "BuiltinCommonInstructions::Standard",
+                      "conditions": [
+                        {
+                          "type": {
+                            "value": "Gamepads::PlatformerGamepadMapper::PropertyJumpButton"
+                          },
+                          "parameters": [
+                            "Object",
+                            "Behavior",
+                            "=",
+                            "\"B or Circle\""
+                          ]
+                        },
+                        {
+                          "type": {
+                            "value": "Gamepads::C_Button_pressed"
+                          },
+                          "parameters": [
+                            "",
+                            "GamepadIdentifier",
+                            "\"B\"",
+                            "\"Left\""
+                          ]
+                        }
+                      ],
+                      "actions": [
+                        {
+                          "type": {
+                            "value": "PlatformBehavior::SimulateJumpKey"
+                          },
+                          "parameters": [
+                            "Object",
+                            "PlatformerCharacter"
+                          ]
+                        }
+                      ]
+                    },
+                    {
+                      "type": "BuiltinCommonInstructions::Standard",
+                      "conditions": [
+                        {
+                          "type": {
+                            "value": "Gamepads::PlatformerGamepadMapper::PropertyJumpButton"
+                          },
+                          "parameters": [
+                            "Object",
+                            "Behavior",
+                            "=",
+                            "\"X or Square\""
+                          ]
+                        },
+                        {
+                          "type": {
+                            "value": "Gamepads::C_Button_pressed"
+                          },
+                          "parameters": [
+                            "",
+                            "GamepadIdentifier",
+                            "\"X\"",
+                            "\"Left\""
+                          ]
+                        }
+                      ],
+                      "actions": [
+                        {
+                          "type": {
+                            "value": "PlatformBehavior::SimulateJumpKey"
+                          },
+                          "parameters": [
+                            "Object",
+                            "PlatformerCharacter"
+                          ]
+                        }
+                      ]
+                    },
+                    {
+                      "type": "BuiltinCommonInstructions::Standard",
+                      "conditions": [
+                        {
+                          "type": {
+                            "value": "Gamepads::PlatformerGamepadMapper::PropertyJumpButton"
+                          },
+                          "parameters": [
+                            "Object",
+                            "Behavior",
+                            "=",
+                            "\"Y or Triangle\""
+                          ]
+                        },
+                        {
+                          "type": {
+                            "value": "Gamepads::C_Button_pressed"
+                          },
+                          "parameters": [
+                            "",
+                            "GamepadIdentifier",
+                            "\"Y\"",
+                            "\"Left\""
+                          ]
+                        }
+                      ],
+                      "actions": [
+                        {
+                          "type": {
+                            "value": "PlatformBehavior::SimulateJumpKey"
+                          },
+                          "parameters": [
+                            "Object",
+                            "PlatformerCharacter"
+                          ]
+                        }
+                      ]
+                    },
+                    {
+                      "type": "BuiltinCommonInstructions::Standard",
+                      "conditions": [
+                        {
+                          "type": {
+                            "value": "Gamepads::PlatformerGamepadMapper::PropertyJumpButton"
+                          },
+                          "parameters": [
+                            "Object",
+                            "Behavior",
+                            "=",
+                            "\"LB or L1\""
+                          ]
+                        },
+                        {
+                          "type": {
+                            "value": "Gamepads::C_Button_pressed"
+                          },
+                          "parameters": [
+                            "",
+                            "GamepadIdentifier",
+                            "\"LB\"",
+                            "\"Left\""
+                          ]
+                        }
+                      ],
+                      "actions": [
+                        {
+                          "type": {
+                            "value": "PlatformBehavior::SimulateJumpKey"
+                          },
+                          "parameters": [
+                            "Object",
+                            "PlatformerCharacter"
+                          ]
+                        }
+                      ]
+                    },
+                    {
+                      "type": "BuiltinCommonInstructions::Standard",
+                      "conditions": [
+                        {
+                          "type": {
+                            "value": "Gamepads::PlatformerGamepadMapper::PropertyJumpButton"
+                          },
+                          "parameters": [
+                            "Object",
+                            "Behavior",
+                            "=",
+                            "\"RB or R1\""
+                          ]
+                        },
+                        {
+                          "type": {
+                            "value": "Gamepads::C_Button_pressed"
+                          },
+                          "parameters": [
+                            "",
+                            "GamepadIdentifier",
+                            "\"RB\"",
+                            "\"Left\""
+                          ]
+                        }
+                      ],
+                      "actions": [
+                        {
+                          "type": {
+                            "value": "PlatformBehavior::SimulateJumpKey"
+                          },
+                          "parameters": [
+                            "Object",
+                            "PlatformerCharacter"
+                          ]
+                        }
+                      ]
+                    },
+                    {
+                      "type": "BuiltinCommonInstructions::Standard",
+                      "conditions": [
+                        {
+                          "type": {
+                            "value": "Gamepads::PlatformerGamepadMapper::PropertyJumpButton"
+                          },
+                          "parameters": [
+                            "Object",
+                            "Behavior",
+                            "=",
+                            "\"LT or L2\""
+                          ]
+                        },
+                        {
+                          "type": {
+                            "value": "Gamepads::C_Button_pressed"
+                          },
+                          "parameters": [
+                            "",
+                            "GamepadIdentifier",
+                            "\"LT\"",
+                            "\"Left\""
+                          ]
+                        }
+                      ],
+                      "actions": [
+                        {
+                          "type": {
+                            "value": "PlatformBehavior::SimulateJumpKey"
+                          },
+                          "parameters": [
+                            "Object",
+                            "PlatformerCharacter"
+                          ]
+                        }
+                      ]
+                    },
+                    {
+                      "type": "BuiltinCommonInstructions::Standard",
+                      "conditions": [
+                        {
+                          "type": {
+                            "value": "Gamepads::PlatformerGamepadMapper::PropertyJumpButton"
+                          },
+                          "parameters": [
+                            "Object",
+                            "Behavior",
+                            "=",
+                            "\"RT or R2\""
+                          ]
+                        },
+                        {
+                          "type": {
+                            "value": "Gamepads::C_Button_pressed"
+                          },
+                          "parameters": [
+                            "",
+                            "GamepadIdentifier",
+                            "\"RT\"",
+                            "\"Left\""
+                          ]
+                        }
+                      ],
+                      "actions": [
+                        {
+                          "type": {
+                            "value": "PlatformBehavior::SimulateJumpKey"
+                          },
+                          "parameters": [
+                            "Object",
+                            "PlatformerCharacter"
+                          ]
+                        }
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "Gamepads::PlatformerGamepadMapper",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            }
+          ],
+          "propertyDescriptors": [
+            {
+              "value": "",
+              "type": "Behavior",
+              "label": "Platformer character behavior",
+              "description": "",
+              "group": "",
+              "extraInformation": [
+                "PlatformBehavior::PlatformerObjectBehavior"
+              ],
+              "name": "PlatformerCharacter"
+            },
+            {
+              "value": "1",
+              "type": "Number",
+              "label": "Gamepad identifier (1, 2, 3 or 4)",
+              "description": "",
+              "group": "",
+              "extraInformation": [],
+              "name": "GamepadIdentifier"
+            },
+            {
+              "value": "true",
+              "type": "Boolean",
+              "label": "Use directional pad",
+              "description": "",
+              "group": "Controls",
+              "extraInformation": [],
+              "name": "UseArrows"
+            },
+            {
+              "value": "true",
+              "type": "Boolean",
+              "label": "Use left stick",
+              "description": "",
+              "group": "Controls",
+              "extraInformation": [],
+              "name": "UseLeftStick"
+            },
+            {
+              "value": "",
+              "type": "Boolean",
+              "label": "Use right stick",
+              "description": "",
+              "group": "Controls",
+              "extraInformation": [],
+              "name": "UseRightStick"
+            },
+            {
+              "value": "A or Cross",
+              "type": "Choice",
+              "label": "Jump button",
+              "description": "",
+              "group": "Controls",
+              "extraInformation": [
+                "A or Cross",
+                "B or Circle",
+                "X or Square",
+                "Y or Triangle",
+                "LB or L1",
+                "RB or R1",
+                "LT or L2",
+                "RT or R2"
+              ],
+              "name": "JumpButton"
+            }
+          ],
+          "sharedPropertyDescriptors": []
+        },
+        {
+          "description": "Control a 3D physics character with a gamepad.",
+          "fullName": "3D platformer gamepad mapper",
+          "name": "Platformer3DGamepadMapper",
+          "objectType": "",
+          "eventsFunctions": [
+            {
+              "fullName": "",
+              "functionType": "Action",
+              "name": "doStepPreEvents",
+              "sentence": "",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [
+                    {
+                      "type": {
+                        "value": "Gamepads::C_Controller_X_is_connected"
+                      },
+                      "parameters": [
+                        "",
+                        "GamepadIdentifier",
+                        ""
+                      ]
+                    }
+                  ],
+                  "actions": [],
+                  "events": [
+                    {
+                      "type": "BuiltinCommonInstructions::Standard",
+                      "conditions": [
+                        {
+                          "type": {
+                            "value": "Gamepads::StickForce"
+                          },
+                          "parameters": [
+                            "",
+                            ">",
+                            "0",
+                            "GamepadIdentifier",
+                            "JoystickIdentifier",
+                            ""
+                          ]
+                        }
+                      ],
+                      "actions": [
+                        {
+                          "type": {
+                            "value": "Physics3D::PhysicsCharacter3D::SetForwardAngle"
+                          },
+                          "parameters": [
+                            "Object",
+                            "PhysicsCharacter3D",
+                            "=",
+                            "Gamepads::StickAngle(GamepadIdentifier, JoystickIdentifier) + CameraAngle(Object.Layer())"
+                          ]
+                        },
+                        {
+                          "type": {
+                            "value": "Physics3D::PhysicsCharacter3D::SimulateStick"
+                          },
+                          "parameters": [
+                            "Object",
+                            "PhysicsCharacter3D",
+                            "-90",
+                            "Gamepads::StickForce(GamepadIdentifier, JoystickIdentifier)"
+                          ]
+                        }
+                      ]
+                    },
+                    {
+                      "type": "BuiltinCommonInstructions::Standard",
+                      "conditions": [
+                        {
+                          "type": {
+                            "value": "Gamepads::Platformer3DGamepadMapper::PropertyJumpButton"
+                          },
+                          "parameters": [
+                            "Object",
+                            "Behavior",
+                            "=",
+                            "\"A or Cross\""
+                          ]
+                        },
+                        {
+                          "type": {
+                            "value": "Gamepads::C_Button_pressed"
+                          },
+                          "parameters": [
+                            "",
+                            "GamepadIdentifier",
+                            "\"A\"",
+                            "\"Left\""
+                          ]
+                        }
+                      ],
+                      "actions": [
+                        {
+                          "type": {
+                            "value": "Physics3D::PhysicsCharacter3D::SimulateJumpKey"
+                          },
+                          "parameters": [
+                            "Object",
+                            "PhysicsCharacter3D"
+                          ]
+                        }
+                      ]
+                    },
+                    {
+                      "type": "BuiltinCommonInstructions::Standard",
+                      "conditions": [
+                        {
+                          "type": {
+                            "value": "Gamepads::Platformer3DGamepadMapper::PropertyJumpButton"
+                          },
+                          "parameters": [
+                            "Object",
+                            "Behavior",
+                            "=",
+                            "\"B or Circle\""
+                          ]
+                        },
+                        {
+                          "type": {
+                            "value": "Gamepads::C_Button_pressed"
+                          },
+                          "parameters": [
+                            "",
+                            "GamepadIdentifier",
+                            "\"B\"",
+                            "\"Left\""
+                          ]
+                        }
+                      ],
+                      "actions": [
+                        {
+                          "type": {
+                            "value": "Physics3D::PhysicsCharacter3D::SimulateJumpKey"
+                          },
+                          "parameters": [
+                            "Object",
+                            "PhysicsCharacter3D"
+                          ]
+                        }
+                      ]
+                    },
+                    {
+                      "type": "BuiltinCommonInstructions::Standard",
+                      "conditions": [
+                        {
+                          "type": {
+                            "value": "Gamepads::Platformer3DGamepadMapper::PropertyJumpButton"
+                          },
+                          "parameters": [
+                            "Object",
+                            "Behavior",
+                            "=",
+                            "\"X or Square\""
+                          ]
+                        },
+                        {
+                          "type": {
+                            "value": "Gamepads::C_Button_pressed"
+                          },
+                          "parameters": [
+                            "",
+                            "GamepadIdentifier",
+                            "\"X\"",
+                            "\"Left\""
+                          ]
+                        }
+                      ],
+                      "actions": [
+                        {
+                          "type": {
+                            "value": "Physics3D::PhysicsCharacter3D::SimulateJumpKey"
+                          },
+                          "parameters": [
+                            "Object",
+                            "PhysicsCharacter3D"
+                          ]
+                        }
+                      ]
+                    },
+                    {
+                      "type": "BuiltinCommonInstructions::Standard",
+                      "conditions": [
+                        {
+                          "type": {
+                            "value": "Gamepads::Platformer3DGamepadMapper::PropertyJumpButton"
+                          },
+                          "parameters": [
+                            "Object",
+                            "Behavior",
+                            "=",
+                            "\"Y or Triangle\""
+                          ]
+                        },
+                        {
+                          "type": {
+                            "value": "Gamepads::C_Button_pressed"
+                          },
+                          "parameters": [
+                            "",
+                            "GamepadIdentifier",
+                            "\"Y\"",
+                            "\"Left\""
+                          ]
+                        }
+                      ],
+                      "actions": [
+                        {
+                          "type": {
+                            "value": "Physics3D::PhysicsCharacter3D::SimulateJumpKey"
+                          },
+                          "parameters": [
+                            "Object",
+                            "PhysicsCharacter3D"
+                          ]
+                        }
+                      ]
+                    },
+                    {
+                      "type": "BuiltinCommonInstructions::Standard",
+                      "conditions": [
+                        {
+                          "type": {
+                            "value": "Gamepads::Platformer3DGamepadMapper::PropertyJumpButton"
+                          },
+                          "parameters": [
+                            "Object",
+                            "Behavior",
+                            "=",
+                            "\"LB or L1\""
+                          ]
+                        },
+                        {
+                          "type": {
+                            "value": "Gamepads::C_Button_pressed"
+                          },
+                          "parameters": [
+                            "",
+                            "GamepadIdentifier",
+                            "\"LB\"",
+                            "\"Left\""
+                          ]
+                        }
+                      ],
+                      "actions": [
+                        {
+                          "type": {
+                            "value": "Physics3D::PhysicsCharacter3D::SimulateJumpKey"
+                          },
+                          "parameters": [
+                            "Object",
+                            "PhysicsCharacter3D"
+                          ]
+                        }
+                      ]
+                    },
+                    {
+                      "type": "BuiltinCommonInstructions::Standard",
+                      "conditions": [
+                        {
+                          "type": {
+                            "value": "Gamepads::Platformer3DGamepadMapper::PropertyJumpButton"
+                          },
+                          "parameters": [
+                            "Object",
+                            "Behavior",
+                            "=",
+                            "\"RB or R1\""
+                          ]
+                        },
+                        {
+                          "type": {
+                            "value": "Gamepads::C_Button_pressed"
+                          },
+                          "parameters": [
+                            "",
+                            "GamepadIdentifier",
+                            "\"RB\"",
+                            "\"Left\""
+                          ]
+                        }
+                      ],
+                      "actions": [
+                        {
+                          "type": {
+                            "value": "Physics3D::PhysicsCharacter3D::SimulateJumpKey"
+                          },
+                          "parameters": [
+                            "Object",
+                            "PhysicsCharacter3D"
+                          ]
+                        }
+                      ]
+                    },
+                    {
+                      "type": "BuiltinCommonInstructions::Standard",
+                      "conditions": [
+                        {
+                          "type": {
+                            "value": "Gamepads::Platformer3DGamepadMapper::PropertyJumpButton"
+                          },
+                          "parameters": [
+                            "Object",
+                            "Behavior",
+                            "=",
+                            "\"LT or L2\""
+                          ]
+                        },
+                        {
+                          "type": {
+                            "value": "Gamepads::C_Button_pressed"
+                          },
+                          "parameters": [
+                            "",
+                            "GamepadIdentifier",
+                            "\"LT\"",
+                            "\"Left\""
+                          ]
+                        }
+                      ],
+                      "actions": [
+                        {
+                          "type": {
+                            "value": "Physics3D::PhysicsCharacter3D::SimulateJumpKey"
+                          },
+                          "parameters": [
+                            "Object",
+                            "PhysicsCharacter3D"
+                          ]
+                        }
+                      ]
+                    },
+                    {
+                      "type": "BuiltinCommonInstructions::Standard",
+                      "conditions": [
+                        {
+                          "type": {
+                            "value": "Gamepads::Platformer3DGamepadMapper::PropertyJumpButton"
+                          },
+                          "parameters": [
+                            "Object",
+                            "Behavior",
+                            "=",
+                            "\"RT or R2\""
+                          ]
+                        },
+                        {
+                          "type": {
+                            "value": "Gamepads::C_Button_pressed"
+                          },
+                          "parameters": [
+                            "",
+                            "GamepadIdentifier",
+                            "\"RT\"",
+                            "\"Left\""
+                          ]
+                        }
+                      ],
+                      "actions": [
+                        {
+                          "type": {
+                            "value": "Physics3D::PhysicsCharacter3D::SimulateJumpKey"
+                          },
+                          "parameters": [
+                            "Object",
+                            "PhysicsCharacter3D"
+                          ]
+                        }
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "Gamepads::Platformer3DGamepadMapper",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            }
+          ],
+          "propertyDescriptors": [
+            {
+              "value": "",
+              "type": "Behavior",
+              "label": "3D physics character",
+              "description": "",
+              "group": "",
+              "extraInformation": [
+                "Physics3D::PhysicsCharacter3D"
+              ],
+              "name": "PhysicsCharacter3D"
+            },
+            {
+              "value": "1",
+              "type": "Number",
+              "label": "Gamepad identifier (1, 2, 3 or 4)",
+              "description": "",
+              "group": "",
+              "extraInformation": [],
+              "name": "GamepadIdentifier"
+            },
+            {
+              "value": "Left",
+              "type": "Choice",
+              "label": "Walk joystick",
+              "description": "",
+              "group": "Controls",
+              "extraInformation": [
+                "Left",
+                "Right"
+              ],
+              "name": "JoystickIdentifier"
+            },
+            {
+              "value": "A or Cross",
+              "type": "Choice",
+              "label": "Jump button",
+              "description": "",
+              "group": "Controls",
+              "extraInformation": [
+                "A or Cross",
+                "B or Circle",
+                "X or Square",
+                "Y or Triangle",
+                "LB or L1",
+                "RB or R1",
+                "LT or L2",
+                "RT or R2"
+              ],
+              "name": "JumpButton"
+            }
+          ],
+          "sharedPropertyDescriptors": []
+        },
+        {
+          "description": "Control a 3D physics character with a gamepad.",
+          "fullName": "3D shooter gamepad mapper",
+          "name": "Shooter3DGamepadMapper",
+          "objectType": "",
+          "eventsFunctions": [
+            {
+              "fullName": "",
+              "functionType": "Action",
+              "name": "doStepPreEvents",
+              "sentence": "",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [
+                    {
+                      "type": {
+                        "value": "Gamepads::C_Controller_X_is_connected"
+                      },
+                      "parameters": [
+                        "",
+                        "GamepadIdentifier",
+                        ""
+                      ]
+                    }
+                  ],
+                  "actions": [],
+                  "events": [
+                    {
+                      "type": "BuiltinCommonInstructions::Standard",
+                      "conditions": [
+                        {
+                          "type": {
+                            "value": "Gamepads::StickForce"
+                          },
+                          "parameters": [
+                            "",
+                            ">",
+                            "0",
+                            "GamepadIdentifier",
+                            "WalkStick",
+                            ""
+                          ]
+                        }
+                      ],
+                      "actions": [
+                        {
+                          "type": {
+                            "value": "Physics3D::PhysicsCharacter3D::SimulateStick"
+                          },
+                          "parameters": [
+                            "Object",
+                            "PhysicsCharacter3D",
+                            "Gamepads::StickAngle(GamepadIdentifier, WalkStick)",
+                            "Gamepads::StickForce(GamepadIdentifier, WalkStick)"
+                          ]
+                        }
+                      ]
+                    },
+                    {
+                      "type": "BuiltinCommonInstructions::Standard",
+                      "conditions": [
+                        {
+                          "type": {
+                            "value": "Gamepads::Shooter3DGamepadMapper::PropertyJumpButton"
+                          },
+                          "parameters": [
+                            "Object",
+                            "Behavior",
+                            "=",
+                            "\"A or Cross\""
+                          ]
+                        },
+                        {
+                          "type": {
+                            "value": "Gamepads::C_Button_pressed"
+                          },
+                          "parameters": [
+                            "",
+                            "GamepadIdentifier",
+                            "\"A\"",
+                            "\"Left\""
+                          ]
+                        }
+                      ],
+                      "actions": [
+                        {
+                          "type": {
+                            "value": "Physics3D::PhysicsCharacter3D::SimulateJumpKey"
+                          },
+                          "parameters": [
+                            "Object",
+                            "PhysicsCharacter3D"
+                          ]
+                        }
+                      ]
+                    },
+                    {
+                      "type": "BuiltinCommonInstructions::Standard",
+                      "conditions": [
+                        {
+                          "type": {
+                            "value": "Gamepads::Shooter3DGamepadMapper::PropertyJumpButton"
+                          },
+                          "parameters": [
+                            "Object",
+                            "Behavior",
+                            "=",
+                            "\"B or Circle\""
+                          ]
+                        },
+                        {
+                          "type": {
+                            "value": "Gamepads::C_Button_pressed"
+                          },
+                          "parameters": [
+                            "",
+                            "GamepadIdentifier",
+                            "\"B\"",
+                            "\"Left\""
+                          ]
+                        }
+                      ],
+                      "actions": [
+                        {
+                          "type": {
+                            "value": "Physics3D::PhysicsCharacter3D::SimulateJumpKey"
+                          },
+                          "parameters": [
+                            "Object",
+                            "PhysicsCharacter3D"
+                          ]
+                        }
+                      ]
+                    },
+                    {
+                      "type": "BuiltinCommonInstructions::Standard",
+                      "conditions": [
+                        {
+                          "type": {
+                            "value": "Gamepads::Shooter3DGamepadMapper::PropertyJumpButton"
+                          },
+                          "parameters": [
+                            "Object",
+                            "Behavior",
+                            "=",
+                            "\"X or Square\""
+                          ]
+                        },
+                        {
+                          "type": {
+                            "value": "Gamepads::C_Button_pressed"
+                          },
+                          "parameters": [
+                            "",
+                            "GamepadIdentifier",
+                            "\"X\"",
+                            "\"Left\""
+                          ]
+                        }
+                      ],
+                      "actions": [
+                        {
+                          "type": {
+                            "value": "Physics3D::PhysicsCharacter3D::SimulateJumpKey"
+                          },
+                          "parameters": [
+                            "Object",
+                            "PhysicsCharacter3D"
+                          ]
+                        }
+                      ]
+                    },
+                    {
+                      "type": "BuiltinCommonInstructions::Standard",
+                      "conditions": [
+                        {
+                          "type": {
+                            "value": "Gamepads::Shooter3DGamepadMapper::PropertyJumpButton"
+                          },
+                          "parameters": [
+                            "Object",
+                            "Behavior",
+                            "=",
+                            "\"Y or Triangle\""
+                          ]
+                        },
+                        {
+                          "type": {
+                            "value": "Gamepads::C_Button_pressed"
+                          },
+                          "parameters": [
+                            "",
+                            "GamepadIdentifier",
+                            "\"Y\"",
+                            "\"Left\""
+                          ]
+                        }
+                      ],
+                      "actions": [
+                        {
+                          "type": {
+                            "value": "Physics3D::PhysicsCharacter3D::SimulateJumpKey"
+                          },
+                          "parameters": [
+                            "Object",
+                            "PhysicsCharacter3D"
+                          ]
+                        }
+                      ]
+                    },
+                    {
+                      "type": "BuiltinCommonInstructions::Standard",
+                      "conditions": [
+                        {
+                          "type": {
+                            "value": "Gamepads::Shooter3DGamepadMapper::PropertyJumpButton"
+                          },
+                          "parameters": [
+                            "Object",
+                            "Behavior",
+                            "=",
+                            "\"LB or L1\""
+                          ]
+                        },
+                        {
+                          "type": {
+                            "value": "Gamepads::C_Button_pressed"
+                          },
+                          "parameters": [
+                            "",
+                            "GamepadIdentifier",
+                            "\"LB\"",
+                            "\"Left\""
+                          ]
+                        }
+                      ],
+                      "actions": [
+                        {
+                          "type": {
+                            "value": "Physics3D::PhysicsCharacter3D::SimulateJumpKey"
+                          },
+                          "parameters": [
+                            "Object",
+                            "PhysicsCharacter3D"
+                          ]
+                        }
+                      ]
+                    },
+                    {
+                      "type": "BuiltinCommonInstructions::Standard",
+                      "conditions": [
+                        {
+                          "type": {
+                            "value": "Gamepads::Shooter3DGamepadMapper::PropertyJumpButton"
+                          },
+                          "parameters": [
+                            "Object",
+                            "Behavior",
+                            "=",
+                            "\"RB or R1\""
+                          ]
+                        },
+                        {
+                          "type": {
+                            "value": "Gamepads::C_Button_pressed"
+                          },
+                          "parameters": [
+                            "",
+                            "GamepadIdentifier",
+                            "\"RB\"",
+                            "\"Left\""
+                          ]
+                        }
+                      ],
+                      "actions": [
+                        {
+                          "type": {
+                            "value": "Physics3D::PhysicsCharacter3D::SimulateJumpKey"
+                          },
+                          "parameters": [
+                            "Object",
+                            "PhysicsCharacter3D"
+                          ]
+                        }
+                      ]
+                    },
+                    {
+                      "type": "BuiltinCommonInstructions::Standard",
+                      "conditions": [
+                        {
+                          "type": {
+                            "value": "Gamepads::Shooter3DGamepadMapper::PropertyJumpButton"
+                          },
+                          "parameters": [
+                            "Object",
+                            "Behavior",
+                            "=",
+                            "\"LT or L2\""
+                          ]
+                        },
+                        {
+                          "type": {
+                            "value": "Gamepads::C_Button_pressed"
+                          },
+                          "parameters": [
+                            "",
+                            "GamepadIdentifier",
+                            "\"LT\"",
+                            "\"Left\""
+                          ]
+                        }
+                      ],
+                      "actions": [
+                        {
+                          "type": {
+                            "value": "Physics3D::PhysicsCharacter3D::SimulateJumpKey"
+                          },
+                          "parameters": [
+                            "Object",
+                            "PhysicsCharacter3D"
+                          ]
+                        }
+                      ]
+                    },
+                    {
+                      "type": "BuiltinCommonInstructions::Standard",
+                      "conditions": [
+                        {
+                          "type": {
+                            "value": "Gamepads::Shooter3DGamepadMapper::PropertyJumpButton"
+                          },
+                          "parameters": [
+                            "Object",
+                            "Behavior",
+                            "=",
+                            "\"RT or R2\""
+                          ]
+                        },
+                        {
+                          "type": {
+                            "value": "Gamepads::C_Button_pressed"
+                          },
+                          "parameters": [
+                            "",
+                            "GamepadIdentifier",
+                            "\"RT\"",
+                            "\"Left\""
+                          ]
+                        }
+                      ],
+                      "actions": [
+                        {
+                          "type": {
+                            "value": "Physics3D::PhysicsCharacter3D::SimulateJumpKey"
+                          },
+                          "parameters": [
+                            "Object",
+                            "PhysicsCharacter3D"
+                          ]
+                        }
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "Gamepads::Shooter3DGamepadMapper",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            }
+          ],
+          "propertyDescriptors": [
+            {
+              "value": "",
+              "type": "Behavior",
+              "label": "3D physics character",
+              "description": "",
+              "group": "",
+              "extraInformation": [
+                "Physics3D::PhysicsCharacter3D"
+              ],
+              "name": "PhysicsCharacter3D"
+            },
+            {
+              "value": "1",
+              "type": "Number",
+              "label": "Gamepad identifier (1, 2, 3 or 4)",
+              "description": "",
+              "group": "",
+              "extraInformation": [],
+              "name": "GamepadIdentifier"
+            },
+            {
+              "value": "Left",
+              "type": "Choice",
+              "label": "Walk joystick",
+              "description": "",
+              "group": "Controls",
+              "extraInformation": [
+                "Left",
+                "Right"
+              ],
+              "name": "WalkStick"
+            },
+            {
+              "value": "Right",
+              "type": "Choice",
+              "label": "Camera joystick",
+              "description": "",
+              "group": "Controls",
+              "extraInformation": [
+                "Left",
+                "Right"
+              ],
+              "name": "CameraStick"
+            },
+            {
+              "value": "A or Cross",
+              "type": "Choice",
+              "label": "Jump button",
+              "description": "",
+              "group": "Controls",
+              "extraInformation": [
+                "A or Cross",
+                "B or Circle",
+                "X or Square",
+                "Y or Triangle",
+                "LB or L1",
+                "RB or R1",
+                "LT or L2",
+                "RT or R2"
+              ],
+              "name": "JumpButton"
+            }
+          ],
+          "sharedPropertyDescriptors": []
+        },
+        {
+          "description": "Control camera rotations with a gamepad.",
+          "fullName": "First person camera gamepad mapper",
+          "name": "FirstPersonGamepadMapper",
+          "objectType": "",
+          "eventsFunctions": [
+            {
+              "fullName": "",
+              "functionType": "Action",
+              "name": "doStepPreEvents",
+              "sentence": "",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Comment",
+                  "color": {
+                    "b": 109,
+                    "g": 230,
+                    "r": 255,
+                    "textB": 0,
+                    "textG": 0,
+                    "textR": 0
+                  },
+                  "comment": "TODO It's probably a bad idea to rotate the object around Y."
+                },
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "Gamepads::FirstPersonGamepadMapper::SetPropertyCurrentRotationSpeedZ"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "=",
+                        "Gamepads::AcceleratedSpeed(CurrentRotationSpeedZ, Gamepads::StickForceX(GamepadIdentifier, CameraStick) * HorizontalRotationSpeedMax, HorizontalRotationSpeedMax, HorizontalRotationAcceleration, HorizontalRotationDeceleration)"
+                      ]
+                    },
+                    {
+                      "type": {
+                        "value": "SetAngle"
+                      },
+                      "parameters": [
+                        "Object",
+                        "+",
+                        "CurrentRotationSpeedZ * TimeDelta()"
+                      ]
+                    },
+                    {
+                      "type": {
+                        "value": "Gamepads::FirstPersonGamepadMapper::SetPropertyCurrentRotationSpeedY"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "=",
+                        "Gamepads::AcceleratedSpeed(CurrentRotationSpeedY, Gamepads::StickForceY(GamepadIdentifier, CameraStick) * VerticalRotationSpeedMax, VerticalRotationSpeedMax, VerticalRotationAcceleration, VerticalRotationDeceleration)"
+                      ]
+                    },
+                    {
+                      "type": {
+                        "value": "Scene3D::Base3DBehavior::SetRotationY"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Object3D",
+                        "+",
+                        "CurrentRotationSpeedY * TimeDelta()"
+                      ]
+                    },
+                    {
+                      "type": {
+                        "value": "Scene3D::Base3DBehavior::SetRotationY"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Object3D",
+                        "=",
+                        "clamp(Object.Object3D::RotationY(), VerticalAngleMin, VerticalAngleMax)"
+                      ]
+                    },
+                    {
+                      "type": {
+                        "value": "Gamepads::FirstPersonGamepadMapper::LookFromObjectEyes"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        ""
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "Gamepads::FirstPersonGamepadMapper",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "Move the camera to look though _PARAM1_ eyes. The object must look to the right when all its angles are 0 and the top of its head be toward Z+.",
+              "fullName": "Look through object eyes",
+              "functionType": "Action",
+              "group": "Layers and cameras",
+              "name": "LookFromObjectEyes",
+              "private": true,
+              "sentence": "Move the camera to look though _PARAM0_ eyes",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "CentreCamera"
+                      },
+                      "parameters": [
+                        "",
+                        "Object",
+                        "",
+                        "Object.Layer()",
+                        ""
+                      ]
+                    },
+                    {
+                      "type": {
+                        "value": "Scene3D::SetCameraZ"
+                      },
+                      "parameters": [
+                        "",
+                        "=",
+                        "Object.Object3D::Z() + Object.Object3D::Depth() + OffsetZ",
+                        "",
+                        ""
+                      ]
+                    },
+                    {
+                      "type": {
+                        "value": "Scene3D::SetCameraRotationX"
+                      },
+                      "parameters": [
+                        "",
+                        "=",
+                        "- Object.Object3D::RotationY() + 90",
+                        "GetArgumentAsString(\"Layer\")",
+                        ""
+                      ]
+                    },
+                    {
+                      "type": {
+                        "value": "Scene3D::SetCameraRotationY"
+                      },
+                      "parameters": [
+                        "",
+                        "=",
+                        "Object.Object3D::RotationX()",
+                        "GetArgumentAsString(\"Layer\")",
+                        ""
+                      ]
+                    },
+                    {
+                      "type": {
+                        "value": "SetCameraAngle"
+                      },
+                      "parameters": [
+                        "",
+                        "=",
+                        "Object.Angle() + 90",
+                        "Object.Layer()",
+                        ""
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "Gamepads::FirstPersonGamepadMapper",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "the maximum horizontal rotation speed of the object.",
+              "fullName": "Maximum horizontal rotation speed",
+              "functionType": "ExpressionAndCondition",
+              "group": "First person camera gamepad mapper horizontal rotation configuration",
+              "name": "HorizontalRotationSpeedMax",
+              "sentence": "the maximum horizontal rotation speed",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SetReturnNumber"
+                      },
+                      "parameters": [
+                        "HorizontalRotationSpeedMax"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "expressionType": {
+                "type": "expression"
+              },
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "Gamepads::FirstPersonGamepadMapper",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "fullName": "",
+              "functionType": "ActionWithOperator",
+              "getterName": "HorizontalRotationSpeedMax",
+              "name": "SetHorizontalRotationSpeedMax",
+              "sentence": "",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "Gamepads::FirstPersonGamepadMapper::SetPropertyHorizontalRotationSpeedMax"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "=",
+                        "Value"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "Gamepads::FirstPersonGamepadMapper",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "the horizontal rotation acceleration of the object.",
+              "fullName": "Horizontal rotation acceleration",
+              "functionType": "ExpressionAndCondition",
+              "group": "First person camera gamepad mapper horizontal rotation configuration",
+              "name": "HorizontalRotationAcceleration",
+              "sentence": "the horizontal rotation acceleration",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SetReturnNumber"
+                      },
+                      "parameters": [
+                        "HorizontalRotationAcceleration"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "expressionType": {
+                "type": "expression"
+              },
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "Gamepads::FirstPersonGamepadMapper",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "fullName": "",
+              "functionType": "ActionWithOperator",
+              "getterName": "HorizontalRotationAcceleration",
+              "name": "SetHorizontalRotationAcceleration",
+              "sentence": "",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "Gamepads::FirstPersonGamepadMapper::SetPropertyHorizontalRotationAcceleration"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "=",
+                        "Value"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "Gamepads::FirstPersonGamepadMapper",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "the horizontal rotation deceleration of the object.",
+              "fullName": "Horizontal rotation deceleration",
+              "functionType": "ExpressionAndCondition",
+              "group": "First person camera gamepad mapper horizontal rotation configuration",
+              "name": "HorizontalRotationDeceleration",
+              "sentence": "the horizontal rotation deceleration",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SetReturnNumber"
+                      },
+                      "parameters": [
+                        "HorizontalRotationDeceleration"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "expressionType": {
+                "type": "expression"
+              },
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "Gamepads::FirstPersonGamepadMapper",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "fullName": "",
+              "functionType": "ActionWithOperator",
+              "getterName": "HorizontalRotationDeceleration",
+              "name": "SetHorizontalRotationDeceleration",
+              "sentence": "",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "Gamepads::FirstPersonGamepadMapper::SetPropertyHorizontalRotationDeceleration"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "=",
+                        "Value"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "Gamepads::FirstPersonGamepadMapper",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "the maximum vertical rotation speed of the object.",
+              "fullName": "Maximum vertical rotation speed",
+              "functionType": "ExpressionAndCondition",
+              "group": "First person camera gamepad mapper vertical rotation configuration",
+              "name": "VerticalRotationSpeedMax",
+              "sentence": "the maximum vertical rotation speed",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SetReturnNumber"
+                      },
+                      "parameters": [
+                        "VerticalRotationSpeedMax"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "expressionType": {
+                "type": "expression"
+              },
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "Gamepads::FirstPersonGamepadMapper",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "fullName": "",
+              "functionType": "ActionWithOperator",
+              "getterName": "VerticalRotationSpeedMax",
+              "name": "SetVerticalRotationSpeedMax",
+              "sentence": "",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "Gamepads::FirstPersonGamepadMapper::SetPropertyVerticalRotationSpeedMax"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "=",
+                        "Value"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "Gamepads::FirstPersonGamepadMapper",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "the vertical rotation acceleration of the object.",
+              "fullName": "Vertical rotation acceleration",
+              "functionType": "ExpressionAndCondition",
+              "group": "First person camera gamepad mapper vertical rotation configuration",
+              "name": "VerticalRotationAcceleration",
+              "sentence": "the vertical rotation acceleration",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SetReturnNumber"
+                      },
+                      "parameters": [
+                        "VerticalRotationAcceleration"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "expressionType": {
+                "type": "expression"
+              },
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "Gamepads::FirstPersonGamepadMapper",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "fullName": "",
+              "functionType": "ActionWithOperator",
+              "getterName": "VerticalRotationAcceleration",
+              "name": "SetVerticalRotationAcceleration",
+              "sentence": "",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "Gamepads::FirstPersonGamepadMapper::SetPropertyVerticalRotationAcceleration"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "=",
+                        "Value"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "Gamepads::FirstPersonGamepadMapper",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "the vertical rotation deceleration of the object.",
+              "fullName": "Vertical rotation deceleration",
+              "functionType": "ExpressionAndCondition",
+              "group": "First person camera gamepad mapper vertical rotation configuration",
+              "name": "VerticalRotationDeceleration",
+              "sentence": "the vertical rotation deceleration",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SetReturnNumber"
+                      },
+                      "parameters": [
+                        "VerticalRotationDeceleration"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "expressionType": {
+                "type": "expression"
+              },
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "Gamepads::FirstPersonGamepadMapper",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "fullName": "",
+              "functionType": "ActionWithOperator",
+              "getterName": "VerticalRotationDeceleration",
+              "name": "SetVerticalRotationDeceleration",
+              "sentence": "",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "Gamepads::FirstPersonGamepadMapper::SetPropertyVerticalRotationDeceleration"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "=",
+                        "Value"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "Gamepads::FirstPersonGamepadMapper",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "the minimum vertical camera angle of the object.",
+              "fullName": "Minimum vertical camera angle",
+              "functionType": "ExpressionAndCondition",
+              "group": "First person camera gamepad mapper vertical rotation configuration",
+              "name": "VerticalAngleMin",
+              "sentence": "the minimum vertical camera angle",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SetReturnNumber"
+                      },
+                      "parameters": [
+                        "VerticalAngleMin"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "expressionType": {
+                "type": "expression"
+              },
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "Gamepads::FirstPersonGamepadMapper",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "fullName": "",
+              "functionType": "ActionWithOperator",
+              "getterName": "VerticalAngleMin",
+              "name": "SetVerticalAngleMin",
+              "sentence": "",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "Gamepads::FirstPersonGamepadMapper::SetPropertyVerticalAngleMin"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "=",
+                        "Value"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "Gamepads::FirstPersonGamepadMapper",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "the maximum vertical camera angle of the object.",
+              "fullName": "Maximum vertical camera angle",
+              "functionType": "ExpressionAndCondition",
+              "group": "First person camera gamepad mapper vertical rotation configuration",
+              "name": "VerticalAngleMax",
+              "sentence": "the maximum vertical camera angle",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SetReturnNumber"
+                      },
+                      "parameters": [
+                        "VerticalAngleMax"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "expressionType": {
+                "type": "expression"
+              },
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "Gamepads::FirstPersonGamepadMapper",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "fullName": "",
+              "functionType": "ActionWithOperator",
+              "getterName": "VerticalAngleMax",
+              "name": "SetVerticalAngleMax",
+              "sentence": "",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "Gamepads::FirstPersonGamepadMapper::SetPropertyVerticalAngleMax"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "=",
+                        "Value"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "Gamepads::FirstPersonGamepadMapper",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "the z position offset of the object.",
+              "fullName": "Z position offset",
+              "functionType": "ExpressionAndCondition",
+              "group": "First person camera gamepad mapper position configuration",
+              "name": "OffsetZ",
+              "sentence": "the z position offset",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SetReturnNumber"
+                      },
+                      "parameters": [
+                        "OffsetZ"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "expressionType": {
+                "type": "expression"
+              },
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "Gamepads::FirstPersonGamepadMapper",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "fullName": "",
+              "functionType": "ActionWithOperator",
+              "getterName": "OffsetZ",
+              "name": "SetOffsetZ",
+              "sentence": "",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "Gamepads::FirstPersonGamepadMapper::SetPropertyOffsetZ"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "=",
+                        "Value"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "Gamepads::FirstPersonGamepadMapper",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            }
+          ],
+          "propertyDescriptors": [
+            {
+              "value": "",
+              "type": "Behavior",
+              "label": "3D capability",
+              "description": "",
+              "group": "",
+              "extraInformation": [
+                "Scene3D::Base3DBehavior"
+              ],
+              "name": "Object3D"
+            },
+            {
+              "value": "1",
+              "type": "Number",
+              "label": "Gamepad identifier (1, 2, 3 or 4)",
+              "description": "",
+              "group": "",
+              "extraInformation": [],
+              "name": "GamepadIdentifier"
+            },
+            {
+              "value": "Right",
+              "type": "Choice",
+              "label": "Camera joystick",
+              "description": "",
+              "group": "",
+              "extraInformation": [
+                "Left",
+                "Right"
+              ],
+              "name": "CameraStick"
+            },
+            {
+              "value": "180",
+              "type": "Number",
+              "unit": "AngularSpeed",
+              "label": "Maximum rotation speed",
+              "description": "",
+              "group": "Horizontal rotation",
+              "extraInformation": [],
+              "name": "HorizontalRotationSpeedMax"
+            },
+            {
+              "value": "360",
+              "type": "Number",
+              "label": "Rotation acceleration",
+              "description": "",
+              "group": "Horizontal rotation",
+              "extraInformation": [],
+              "name": "HorizontalRotationAcceleration"
+            },
+            {
+              "value": "720",
+              "type": "Number",
+              "label": "Rotation deceleration",
+              "description": "",
+              "group": "Horizontal rotation",
+              "extraInformation": [],
+              "name": "HorizontalRotationDeceleration"
+            },
+            {
+              "value": "120",
+              "type": "Number",
+              "unit": "AngularSpeed",
+              "label": "Maximum rotation speed",
+              "description": "",
+              "group": "Vertical rotation",
+              "extraInformation": [],
+              "name": "VerticalRotationSpeedMax"
+            },
+            {
+              "value": "240",
+              "type": "Number",
+              "label": "Rotation acceleration",
+              "description": "",
+              "group": "Vertical rotation",
+              "extraInformation": [],
+              "name": "VerticalRotationAcceleration"
+            },
+            {
+              "value": "480",
+              "type": "Number",
+              "label": "Rotation deceleration",
+              "description": "",
+              "group": "Vertical rotation",
+              "extraInformation": [],
+              "name": "VerticalRotationDeceleration"
+            },
+            {
+              "value": "-90",
+              "type": "Number",
+              "unit": "DegreeAngle",
+              "label": "Minimum angle",
+              "description": "",
+              "group": "Vertical rotation",
+              "extraInformation": [],
+              "name": "VerticalAngleMin"
+            },
+            {
+              "value": "90",
+              "type": "Number",
+              "unit": "DegreeAngle",
+              "label": "Maximum angle",
+              "description": "",
+              "group": "Vertical rotation",
+              "extraInformation": [],
+              "name": "VerticalAngleMax"
+            },
+            {
+              "value": "0",
+              "type": "Number",
+              "unit": "Pixel",
+              "label": "Z position offset",
+              "description": "",
+              "group": "Position",
+              "extraInformation": [],
+              "name": "OffsetZ"
+            },
+            {
+              "value": "0",
+              "type": "Number",
+              "unit": "AngularSpeed",
+              "label": "Current rotation speed Z",
+              "description": "",
+              "group": "",
+              "extraInformation": [],
+              "hidden": true,
+              "name": "CurrentRotationSpeedZ"
+            },
+            {
+              "value": "0",
+              "type": "Number",
+              "unit": "AngularSpeed",
+              "label": "Current rotation speed Y",
+              "description": "",
+              "group": "",
+              "extraInformation": [],
+              "hidden": true,
+              "name": "CurrentRotationSpeedY"
+            }
+          ],
+          "sharedPropertyDescriptors": []
+        },
+        {
+          "description": "Control a top-down character with a gamepad.",
+          "fullName": "Top-down gamepad mapper",
+          "name": "TopDownGamepadMapper",
+          "objectType": "",
+          "eventsFunctions": [
+            {
+              "fullName": "",
+              "functionType": "Action",
+              "name": "doStepPreEvents",
+              "sentence": "",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [
+                    {
+                      "type": {
+                        "value": "Gamepads::C_Controller_X_is_connected"
+                      },
+                      "parameters": [
+                        "",
+                        "GamepadIdentifier",
+                        ""
+                      ]
+                    }
+                  ],
+                  "actions": [],
+                  "events": [
+                    {
+                      "type": "BuiltinCommonInstructions::Standard",
+                      "conditions": [
+                        {
+                          "type": {
+                            "value": "Gamepads::TopDownGamepadMapper::PropertyUseArrows"
+                          },
+                          "parameters": [
+                            "Object",
+                            "Behavior"
+                          ]
+                        }
+                      ],
+                      "actions": [],
+                      "events": [
+                        {
+                          "type": "BuiltinCommonInstructions::Standard",
+                          "conditions": [
+                            {
+                              "type": {
+                                "value": "Gamepads::C_Button_pressed"
+                              },
+                              "parameters": [
+                                "",
+                                "GamepadIdentifier",
+                                "\"Left\"",
+                                "\"Left\""
+                              ]
+                            }
+                          ],
+                          "actions": [
+                            {
+                              "type": {
+                                "value": "TopDownMovementBehavior::SimulateLeftKey"
+                              },
+                              "parameters": [
+                                "Object",
+                                "TopDownMovement"
+                              ]
+                            }
+                          ]
+                        },
+                        {
+                          "type": "BuiltinCommonInstructions::Standard",
+                          "conditions": [
+                            {
+                              "type": {
+                                "value": "Gamepads::C_Button_pressed"
+                              },
+                              "parameters": [
+                                "",
+                                "GamepadIdentifier",
+                                "\"Right\"",
+                                "\"Left\""
+                              ]
+                            }
+                          ],
+                          "actions": [
+                            {
+                              "type": {
+                                "value": "TopDownMovementBehavior::SimulateRightKey"
+                              },
+                              "parameters": [
+                                "Object",
+                                "TopDownMovement"
+                              ]
+                            }
+                          ]
+                        },
+                        {
+                          "type": "BuiltinCommonInstructions::Standard",
+                          "conditions": [
+                            {
+                              "type": {
+                                "value": "Gamepads::C_Button_pressed"
+                              },
+                              "parameters": [
+                                "",
+                                "GamepadIdentifier",
+                                "\"Up\"",
+                                "\"Left\""
+                              ]
+                            }
+                          ],
+                          "actions": [
+                            {
+                              "type": {
+                                "value": "TopDownMovementBehavior::SimulateUpKey"
+                              },
+                              "parameters": [
+                                "Object",
+                                "TopDownMovement"
+                              ]
+                            }
+                          ]
+                        },
+                        {
+                          "type": "BuiltinCommonInstructions::Standard",
+                          "conditions": [
+                            {
+                              "type": {
+                                "value": "Gamepads::C_Button_pressed"
+                              },
+                              "parameters": [
+                                "",
+                                "GamepadIdentifier",
+                                "\"Down\"",
+                                "\"Left\""
+                              ]
+                            }
+                          ],
+                          "actions": [
+                            {
+                              "type": {
+                                "value": "TopDownMovementBehavior::SimulateDownKey"
+                              },
+                              "parameters": [
+                                "Object",
+                                "TopDownMovement"
+                              ]
+                            }
+                          ]
+                        }
+                      ]
+                    },
+                    {
+                      "type": "BuiltinCommonInstructions::Standard",
+                      "conditions": [
+                        {
+                          "type": {
+                            "value": "Gamepads::TopDownGamepadMapper::PropertyUseLeftStick"
+                          },
+                          "parameters": [
+                            "Object",
+                            "Behavior"
+                          ]
+                        }
+                      ],
+                      "actions": [],
+                      "events": [
+                        {
+                          "type": "BuiltinCommonInstructions::Standard",
+                          "conditions": [
+                            {
+                              "type": {
+                                "value": "Gamepads::TopDownGamepadMapper::PropertyStickMode"
+                              },
+                              "parameters": [
+                                "Object",
+                                "Behavior",
+                                "=",
+                                "\"Analog\""
+                              ]
+                            }
+                          ],
+                          "actions": [
+                            {
+                              "type": {
+                                "value": "TopDownMovementBehavior::SimulateStick"
+                              },
+                              "parameters": [
+                                "Object",
+                                "TopDownMovement",
+                                "Gamepads::StickRotationValue(GamepadIdentifier, \"Left\")",
+                                "Gamepads::StickForce(GamepadIdentifier, \"Left\")"
+                              ]
+                            }
+                          ]
+                        },
+                        {
+                          "type": "BuiltinCommonInstructions::Standard",
+                          "conditions": [
+                            {
+                              "type": {
+                                "value": "Gamepads::TopDownGamepadMapper::PropertyStickMode"
+                              },
+                              "parameters": [
+                                "Object",
+                                "Behavior",
+                                "=",
+                                "\"360°\""
+                              ]
+                            }
+                          ],
+                          "actions": [
+                            {
+                              "type": {
+                                "value": "TopDownMovementBehavior::SimulateStick"
+                              },
+                              "parameters": [
+                                "Object",
+                                "TopDownMovement",
+                                "Gamepads::StickRotationValue(GamepadIdentifier, \"Left\")",
+                                "sign(Gamepads::StickForce(GamepadIdentifier, \"Left\"))"
+                              ]
+                            }
+                          ]
+                        },
+                        {
+                          "type": "BuiltinCommonInstructions::Standard",
+                          "conditions": [
+                            {
+                              "type": {
+                                "value": "Gamepads::TopDownGamepadMapper::PropertyStickMode"
+                              },
+                              "parameters": [
+                                "Object",
+                                "Behavior",
+                                "=",
+                                "\"8 Directions\""
+                              ]
+                            }
+                          ],
+                          "actions": [],
+                          "events": [
+                            {
+                              "type": "BuiltinCommonInstructions::Standard",
+                              "conditions": [
+                                {
+                                  "type": {
+                                    "value": "Gamepads::C_Axis_pushed"
+                                  },
+                                  "parameters": [
+                                    "",
+                                    "GamepadIdentifier",
+                                    "\"Left\"",
+                                    "\"Left\"",
+                                    ""
+                                  ]
+                                }
+                              ],
+                              "actions": [
+                                {
+                                  "type": {
+                                    "value": "TopDownMovementBehavior::SimulateLeftKey"
+                                  },
+                                  "parameters": [
+                                    "Object",
+                                    "TopDownMovement"
+                                  ]
+                                }
+                              ]
+                            },
+                            {
+                              "type": "BuiltinCommonInstructions::Standard",
+                              "conditions": [
+                                {
+                                  "type": {
+                                    "value": "Gamepads::C_Axis_pushed"
+                                  },
+                                  "parameters": [
+                                    "",
+                                    "GamepadIdentifier",
+                                    "\"Left\"",
+                                    "\"Right\"",
+                                    ""
+                                  ]
+                                }
+                              ],
+                              "actions": [
+                                {
+                                  "type": {
+                                    "value": "TopDownMovementBehavior::SimulateRightKey"
+                                  },
+                                  "parameters": [
+                                    "Object",
+                                    "TopDownMovement"
+                                  ]
+                                }
+                              ]
+                            },
+                            {
+                              "type": "BuiltinCommonInstructions::Standard",
+                              "conditions": [
+                                {
+                                  "type": {
+                                    "value": "Gamepads::C_Axis_pushed"
+                                  },
+                                  "parameters": [
+                                    "",
+                                    "GamepadIdentifier",
+                                    "\"Left\"",
+                                    "\"Up\"",
+                                    ""
+                                  ]
+                                }
+                              ],
+                              "actions": [
+                                {
+                                  "type": {
+                                    "value": "TopDownMovementBehavior::SimulateUpKey"
+                                  },
+                                  "parameters": [
+                                    "Object",
+                                    "TopDownMovement"
+                                  ]
+                                }
+                              ]
+                            },
+                            {
+                              "type": "BuiltinCommonInstructions::Standard",
+                              "conditions": [
+                                {
+                                  "type": {
+                                    "value": "Gamepads::C_Axis_pushed"
+                                  },
+                                  "parameters": [
+                                    "",
+                                    "GamepadIdentifier",
+                                    "\"Left\"",
+                                    "\"Down\"",
+                                    ""
+                                  ]
+                                }
+                              ],
+                              "actions": [
+                                {
+                                  "type": {
+                                    "value": "TopDownMovementBehavior::SimulateDownKey"
+                                  },
+                                  "parameters": [
+                                    "Object",
+                                    "TopDownMovement"
+                                  ]
+                                }
+                              ]
+                            }
+                          ]
+                        }
+                      ]
+                    },
+                    {
+                      "type": "BuiltinCommonInstructions::Standard",
+                      "conditions": [
+                        {
+                          "type": {
+                            "value": "Gamepads::TopDownGamepadMapper::PropertyUseRightStick"
+                          },
+                          "parameters": [
+                            "Object",
+                            "Behavior"
+                          ]
+                        }
+                      ],
+                      "actions": [],
+                      "events": [
+                        {
+                          "type": "BuiltinCommonInstructions::Standard",
+                          "conditions": [
+                            {
+                              "type": {
+                                "value": "Gamepads::TopDownGamepadMapper::PropertyStickMode"
+                              },
+                              "parameters": [
+                                "Object",
+                                "Behavior",
+                                "=",
+                                "\"Analog\""
+                              ]
+                            }
+                          ],
+                          "actions": [
+                            {
+                              "type": {
+                                "value": "TopDownMovementBehavior::SimulateStick"
+                              },
+                              "parameters": [
+                                "Object",
+                                "TopDownMovement",
+                                "Gamepads::StickRotationValue(GamepadIdentifier, \"Right\")",
+                                "Gamepads::StickForce(GamepadIdentifier, \"Right\")"
+                              ]
+                            }
+                          ]
+                        },
+                        {
+                          "type": "BuiltinCommonInstructions::Standard",
+                          "conditions": [
+                            {
+                              "type": {
+                                "value": "Gamepads::TopDownGamepadMapper::PropertyStickMode"
+                              },
+                              "parameters": [
+                                "Object",
+                                "Behavior",
+                                "=",
+                                "\"360°\""
+                              ]
+                            }
+                          ],
+                          "actions": [
+                            {
+                              "type": {
+                                "value": "TopDownMovementBehavior::SimulateStick"
+                              },
+                              "parameters": [
+                                "Object",
+                                "TopDownMovement",
+                                "sign(Gamepads::StickForce(GamepadIdentifier, \"Right\"))",
+                                "1"
+                              ]
+                            }
+                          ]
+                        },
+                        {
+                          "type": "BuiltinCommonInstructions::Standard",
+                          "conditions": [
+                            {
+                              "type": {
+                                "value": "Gamepads::TopDownGamepadMapper::PropertyStickMode"
+                              },
+                              "parameters": [
+                                "Object",
+                                "Behavior",
+                                "=",
+                                "\"8 Directions\""
+                              ]
+                            }
+                          ],
+                          "actions": [],
+                          "events": [
+                            {
+                              "type": "BuiltinCommonInstructions::Standard",
+                              "conditions": [
+                                {
+                                  "type": {
+                                    "value": "Gamepads::C_Axis_pushed"
+                                  },
+                                  "parameters": [
+                                    "",
+                                    "GamepadIdentifier",
+                                    "\"Right\"",
+                                    "\"Left\"",
+                                    ""
+                                  ]
+                                }
+                              ],
+                              "actions": [
+                                {
+                                  "type": {
+                                    "value": "TopDownMovementBehavior::SimulateLeftKey"
+                                  },
+                                  "parameters": [
+                                    "Object",
+                                    "TopDownMovement"
+                                  ]
+                                }
+                              ]
+                            },
+                            {
+                              "type": "BuiltinCommonInstructions::Standard",
+                              "conditions": [
+                                {
+                                  "type": {
+                                    "value": "Gamepads::C_Axis_pushed"
+                                  },
+                                  "parameters": [
+                                    "",
+                                    "GamepadIdentifier",
+                                    "\"Right\"",
+                                    "\"Right\"",
+                                    ""
+                                  ]
+                                }
+                              ],
+                              "actions": [
+                                {
+                                  "type": {
+                                    "value": "TopDownMovementBehavior::SimulateRightKey"
+                                  },
+                                  "parameters": [
+                                    "Object",
+                                    "TopDownMovement"
+                                  ]
+                                }
+                              ]
+                            },
+                            {
+                              "type": "BuiltinCommonInstructions::Standard",
+                              "conditions": [
+                                {
+                                  "type": {
+                                    "value": "Gamepads::C_Axis_pushed"
+                                  },
+                                  "parameters": [
+                                    "",
+                                    "GamepadIdentifier",
+                                    "\"Right\"",
+                                    "\"Up\"",
+                                    ""
+                                  ]
+                                }
+                              ],
+                              "actions": [
+                                {
+                                  "type": {
+                                    "value": "TopDownMovementBehavior::SimulateUpKey"
+                                  },
+                                  "parameters": [
+                                    "Object",
+                                    "TopDownMovement"
+                                  ]
+                                }
+                              ]
+                            },
+                            {
+                              "type": "BuiltinCommonInstructions::Standard",
+                              "conditions": [
+                                {
+                                  "type": {
+                                    "value": "Gamepads::C_Axis_pushed"
+                                  },
+                                  "parameters": [
+                                    "",
+                                    "GamepadIdentifier",
+                                    "\"Right\"",
+                                    "\"Down\"",
+                                    ""
+                                  ]
+                                }
+                              ],
+                              "actions": [
+                                {
+                                  "type": {
+                                    "value": "TopDownMovementBehavior::SimulateDownKey"
+                                  },
+                                  "parameters": [
+                                    "Object",
+                                    "TopDownMovement"
+                                  ]
+                                }
+                              ]
+                            }
+                          ]
+                        }
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "Gamepads::TopDownGamepadMapper",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            }
+          ],
+          "propertyDescriptors": [
+            {
+              "value": "",
+              "type": "Behavior",
+              "label": "Top-down movement behavior",
+              "description": "",
+              "group": "",
+              "extraInformation": [
+                "TopDownMovementBehavior::TopDownMovementBehavior"
+              ],
+              "name": "TopDownMovement"
+            },
+            {
+              "value": "1",
+              "type": "Number",
+              "label": "Gamepad identifier (1, 2, 3 or 4)",
+              "description": "",
+              "group": "",
+              "extraInformation": [],
+              "name": "GamepadIdentifier"
+            },
+            {
+              "value": "true",
+              "type": "Boolean",
+              "label": "Use directional pad",
+              "description": "",
+              "group": "Controls",
+              "extraInformation": [],
+              "name": "UseArrows"
+            },
+            {
+              "value": "true",
+              "type": "Boolean",
+              "label": "Use left stick",
+              "description": "",
+              "group": "Controls",
+              "extraInformation": [],
+              "name": "UseLeftStick"
+            },
+            {
+              "value": "",
+              "type": "Boolean",
+              "label": "Use right stick",
+              "description": "",
+              "group": "Controls",
+              "extraInformation": [],
+              "name": "UseRightStick"
+            },
+            {
+              "value": "Analog",
+              "type": "Choice",
+              "label": "Stick mode",
+              "description": "",
+              "group": "Controls",
+              "extraInformation": [
+                "Analog",
+                "360°",
+                "8 Directions"
+              ],
+              "name": "StickMode"
+            }
+          ],
+          "sharedPropertyDescriptors": []
+        }
+      ],
+      "eventsBasedObjects": []
+    },
+    {
+      "author": "",
+      "category": "Visual effect",
+      "extensionNamespace": "",
+      "fullName": "3D object shake",
+      "helpPath": "",
+      "iconUrl": "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz48IURPQ1RZUEUgc3ZnIFBVQkxJQyAiLS8vVzNDLy9EVEQgU1ZHIDEuMS8vRU4iICJodHRwOi8vd3d3LnczLm9yZy9HcmFwaGljcy9TVkcvMS4xL0RURC9zdmcxMS5kdGQiPjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgdmVyc2lvbj0iMS4xIiBpZD0ibWRpLXZlY3Rvci1kaWZmZXJlbmNlLWFiIiB3aWR0aD0iMjQiIGhlaWdodD0iMjQiIHZpZXdCb3g9IjAgMCAyNCAyNCI+PHBhdGggZD0iTTMsMUMxLjg5LDEgMSwxLjg5IDEsM1Y1SDNWM0g1VjFIM003LDFWM0gxMFYxSDdNMTIsMVYzSDE0VjVIMTZWM0MxNiwxLjg5IDE1LjExLDEgMTQsMUgxMk0xLDdWMTBIM1Y3SDFNMTQsN0MxNCw3IDE0LDExLjY3IDE0LDE0QzExLjY3LDE0IDcsMTQgNywxNEM3LDE0IDcsMTggNywyMEM3LDIxLjExIDcuODksMjIgOSwyMkgyMEMyMS4xMSwyMiAyMiwyMS4xMSAyMiwyMFY5QzIyLDcuODkgMjEuMTEsNyAyMCw3QzE4LDcgMTQsNyAxNCw3TTE2LDlIMjBWMjBIOVYxNkgxNEMxNS4xMSwxNiAxNiwxNS4xMSAxNiwxNFY5TTEsMTJWMTRDMSwxNS4xMSAxLjg5LDE2IDMsMTZINVYxNEgzVjEySDFaIiAvPjwvc3ZnPg==",
+      "name": "ShakeObject3D",
+      "previewIconUrl": "https://resources.gdevelop-app.com/assets/Icons/vector-difference-ab.svg",
+      "shortDescription": "Shake 3D objects.",
+      "version": "2.0.4",
+      "description": [
+        "Shake 3D objects with translation and rotation.",
+        "",
+        "The 3D racing game example uses this extension ([open the project online](https://editor.gdevelop.io/?project=example://3d-racing-game)).",
+        "",
+        "Breaking changes from 2.0.0",
+        "- The behavior for 3D box has been removed. The other behavior can be used for both models and boxes."
+      ],
+      "origin": {
+        "identifier": "ShakeObject3D",
+        "name": "gdevelop-extension-store"
+      },
+      "tags": [
+        "3d",
+        "shaking",
+        "effect",
+        "shake",
+        "translate",
+        "rotate"
+      ],
+      "authorIds": [
+        "IWykYNRvhCZBN3vEgKEbBPOR3Oc2"
+      ],
+      "dependencies": [],
+      "globalVariables": [],
+      "sceneVariables": [],
+      "eventsFunctions": [
+        {
+          "fullName": "",
+          "functionType": "Action",
+          "name": "onFirstSceneLoaded",
+          "sentence": "",
+          "events": [
+            {
+              "type": "BuiltinCommonInstructions::Standard",
+              "conditions": [],
+              "actions": [
+                {
+                  "type": {
+                    "value": "ShakeObject3D::DefineHelperClasses"
+                  },
+                  "parameters": [
+                    "",
+                    ""
+                  ]
+                }
+              ]
+            }
+          ],
+          "parameters": [],
+          "objectGroups": []
+        },
+        {
+          "description": "Define helper classes JavaScript code.",
+          "fullName": "Define helper classes",
+          "functionType": "Action",
+          "name": "DefineHelperClasses",
+          "private": true,
+          "sentence": "Define helper classes JavaScript code",
+          "events": [
+            {
+              "type": "BuiltinCommonInstructions::JsCode",
+              "inlineCode": [
+                "if (gdjs._shakeObjectExtension) {",
+                "    return;",
+                "}",
+                "",
+                "/** Noise generator manager. */",
+                "class NoiseManager {",
+                "    /**",
+                "     * Create the manager of noise generators.",
+                "     */",
+                "    constructor() {",
+                "        this.seed = gdjs.randomInRange(1, Number.MAX_SAFE_INTEGER);",
+                "        /** @type {Map<string, NoiseGenerator>} */",
+                "        this.generators = new Map();",
+                "    }",
+                "",
+                "    /**",
+                "     * @param name {string}",
+                "     * @return {NoiseGenerator}",
+                "     */",
+                "    getGenerator(name) {",
+                "        let generator = this.generators.get(name);",
+                "        if (!generator) {",
+                "            generator = new NoiseGenerator(name + this.seed);",
+                "            this.generators.set(name, generator);",
+                "        }",
+                "        return generator;",
+                "    }",
+                "",
+                "    /**",
+                "     * @param seed {number}",
+                "     */",
+                "    setSeed(seed) {",
+                "        this.seed = seed;",
+                "        this.generators.forEach(generator => generator.setSeed(name + this.seed));",
+                "    }",
+                "",
+                "    /**",
+                "     * @param name {string}",
+                "     */",
+                "    deleteGenerator(name) {",
+                "        this.generators.delete(name);",
+                "    }",
+                "",
+                "    /**",
+                "     */",
+                "    deleteAllGenerators() {",
+                "        this.generators.clear();",
+                "    }",
+                "}",
+                "",
+                "/** Noise generator with octaves. */",
+                "class NoiseGenerator {",
+                "    /**",
+                "     * Create a noise generator with a seed.",
+                "     * @param seed {string}",
+                "     */",
+                "    constructor(seed) {",
+                "        this.simplexNoise = new SimplexNoise(seed);",
+                "        this.frequency = 1;",
+                "        this.octaves = 1;",
+                "        this.persistence = 0.5;",
+                "        this.lacunarity = 2;",
+                "        this.xLoopPeriod = 0;",
+                "        this.yLoopPeriod = 0;",
+                "    }",
+                "",
+                "    /**",
+                "     * @param seed {string}",
+                "     */",
+                "    setSeed(seed) {",
+                "        this.simplexNoise = new SimplexNoise(seed);",
+                "    }",
+                "",
+                "    /**",
+                "     * @param x {float}",
+                "     * @param y {float}",
+                "     * @param z {float} optionnal",
+                "     * @param w {float} optionnal",
+                "     * @return {float}",
+                "     */",
+                "    noise(x, y, z, w) {",
+                "        if (this.xLoopPeriod && this.yLoopPeriod) {",
+                "            const circleRatioX = 2 * Math.PI / this.xLoopPeriod;",
+                "            const circleRatioY = 2 * Math.PI / this.yLoopPeriod;",
+                "            const angleX = circleRatioX * x;",
+                "            const angleY = circleRatioY * y;",
+                "            x = Math.cos(angleX) / circleRatioX;",
+                "            y = Math.sin(angleX) / circleRatioX;",
+                "            z = Math.cos(angleY) / circleRatioY;",
+                "            w = Math.sin(angleY) / circleRatioY;",
+                "        }",
+                "        else if (this.xLoopPeriod) {",
+                "            const circleRatio = 2 * Math.PI / this.xLoopPeriod;",
+                "            const angleX = circleRatio * x;",
+                "            w = z;",
+                "            z = y;",
+                "            x = Math.cos(angleX) / circleRatio;",
+                "            y = Math.sin(angleX) / circleRatio;",
+                "        }",
+                "        else if (this.yLoopPeriod) {",
+                "            const circleRatio = 2 * Math.PI / this.xLoopPeriod;",
+                "            const angleX = circleRatio * x;",
+                "            w = z;",
+                "            // Make the circle perimeter equals to the looping period",
+                "            // to keep the same perceived frequency with or without looping.",
+                "            y = Math.cos(angleX) / circleRatio;",
+                "            z = Math.sin(angleX) / circleRatio;",
+                "        }",
+                "        let noiseFunction = this.simplexNoise.noise4D.bind(this.simplexNoise);",
+                "        if (z === undefined) {",
+                "            noiseFunction = this.simplexNoise.noise2D.bind(this.simplexNoise);",
+                "        }",
+                "        else if (w === undefined) {",
+                "            noiseFunction = this.simplexNoise.noise3D.bind(this.simplexNoise);",
+                "        }",
+                "        let frequency = this.frequency;",
+                "        let noiseSum = 0;",
+                "        let amplitudeSum = 0;",
+                "        let amplitude = 1;",
+                "        for (let i = 0; i < this.octaves; i++) {",
+                "            noiseSum += noiseFunction(x * frequency, y * frequency, z * frequency, w * frequency) * amplitude;",
+                "            amplitudeSum += Math.abs(amplitude);",
+                "            amplitude *= this.persistence;",
+                "            frequency *= this.lacunarity;",
+                "        }",
+                "        return noiseSum / amplitudeSum;",
+                "    }",
+                "}",
+                "",
+                "/*",
+                "A fast javascript implementation of simplex noise by Jonas Wagner",
+                "https://github.com/jwagner/simplex-noise.js",
+                "",
+                "Based on a speed-improved simplex noise algorithm for 2D, 3D and 4D in Java.",
+                "Which is based on example code by Stefan Gustavson (stegu@itn.liu.se).",
+                "With Optimisations by Peter Eastman (peastman@drizzle.stanford.edu).",
+                "Better rank ordering method by Stefan Gustavson in 2012.",
+                "",
+                " Copyright (c) 2021 Jonas Wagner",
+                "",
+                " Permission is hereby granted, free of charge, to any person obtaining a copy",
+                " of this software and associated documentation files (the \"Software\"), to deal",
+                " in the Software without restriction, including without limitation the rights",
+                " to use, copy, modify, merge, publish, distribute, sublicense, and/or sell",
+                " copies of the Software, and to permit persons to whom the Software is",
+                " furnished to do so, subject to the following conditions:",
+                "",
+                " The above copyright notice and this permission notice shall be included in all",
+                " copies or substantial portions of the Software.",
+                "",
+                " THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR",
+                " IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,",
+                " FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE",
+                " AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER",
+                " LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,",
+                " OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE",
+                " SOFTWARE.",
+                " */",
+                "",
+                "const F2 = 0.5 * (Math.sqrt(3.0) - 1.0);",
+                "const G2 = (3.0 - Math.sqrt(3.0)) / 6.0;",
+                "const F3 = 1.0 / 3.0;",
+                "const G3 = 1.0 / 6.0;",
+                "const F4 = (Math.sqrt(5.0) - 1.0) / 4.0;",
+                "const G4 = (5.0 - Math.sqrt(5.0)) / 20.0;",
+                "const grad3 = new Float32Array([1, 1, 0,",
+                "    -1, 1, 0,",
+                "    1, -1, 0,",
+                "    -1, -1, 0,",
+                "    1, 0, 1,",
+                "    -1, 0, 1,",
+                "    1, 0, -1,",
+                "    -1, 0, -1,",
+                "    0, 1, 1,",
+                "    0, -1, 1,",
+                "    0, 1, -1,",
+                "    0, -1, -1]);",
+                "const grad4 = new Float32Array([0, 1, 1, 1, 0, 1, 1, -1, 0, 1, -1, 1, 0, 1, -1, -1,",
+                "    0, -1, 1, 1, 0, -1, 1, -1, 0, -1, -1, 1, 0, -1, -1, -1,",
+                "    1, 0, 1, 1, 1, 0, 1, -1, 1, 0, -1, 1, 1, 0, -1, -1,",
+                "    -1, 0, 1, 1, -1, 0, 1, -1, -1, 0, -1, 1, -1, 0, -1, -1,",
+                "    1, 1, 0, 1, 1, 1, 0, -1, 1, -1, 0, 1, 1, -1, 0, -1,",
+                "    -1, 1, 0, 1, -1, 1, 0, -1, -1, -1, 0, 1, -1, -1, 0, -1,",
+                "    1, 1, 1, 0, 1, 1, -1, 0, 1, -1, 1, 0, 1, -1, -1, 0,",
+                "    -1, 1, 1, 0, -1, 1, -1, 0, -1, -1, 1, 0, -1, -1, -1, 0]);",
+                "",
+                "",
+                "/**",
+                " * Builds a random permutation table.",
+                " * This is exported only for (internal) testing purposes.",
+                " * Do not rely on this export.",
+                " * @param {() => number} random",
+                " * @private",
+                " */",
+                "function buildPermutationTable(random) {",
+                "    const p = new Uint8Array(256);",
+                "    for (let i = 0; i < 256; i++) {",
+                "        p[i] = i;",
+                "    }",
+                "    for (let i = 0; i < 255; i++) {",
+                "        const r = i + ~~(random() * (256 - i));",
+                "        const aux = p[i];",
+                "        p[i] = p[r];",
+                "        p[r] = aux;",
+                "    }",
+                "    return p;",
+                "}",
+                "",
+                "/*",
+                "The ALEA PRNG and masher code used by simplex-noise.js",
+                "is based on code by Johannes Baagøe, modified by Jonas Wagner.",
+                "See alea.md for the full license.",
+                "@param {string|number} seed",
+                "*/",
+                "function alea(seed) {",
+                "    let s0 = 0;",
+                "    let s1 = 0;",
+                "    let s2 = 0;",
+                "    let c = 1;",
+                "    const mash = masher();",
+                "    s0 = mash(' ');",
+                "    s1 = mash(' ');",
+                "    s2 = mash(' ');",
+                "    s0 -= mash(seed);",
+                "    if (s0 < 0) {",
+                "        s0 += 1;",
+                "    }",
+                "    s1 -= mash(seed);",
+                "    if (s1 < 0) {",
+                "        s1 += 1;",
+                "    }",
+                "    s2 -= mash(seed);",
+                "    if (s2 < 0) {",
+                "        s2 += 1;",
+                "    }",
+                "    return function () {",
+                "        const t = 2091639 * s0 + c * 2.3283064365386963e-10; // 2^-32",
+                "        s0 = s1;",
+                "        s1 = s2;",
+                "        return s2 = t - (c = t | 0);",
+                "    };",
+                "}",
+                "",
+                "function masher() {",
+                "    let n = 0xefc8249d;",
+                "    return function (data) {",
+                "        data = data.toString();",
+                "        for (let i = 0; i < data.length; i++) {",
+                "            n += data.charCodeAt(i);",
+                "            let h = 0.02519603282416938 * n;",
+                "            n = h >>> 0;",
+                "            h -= n;",
+                "            h *= n;",
+                "            n = h >>> 0;",
+                "            h -= n;",
+                "            n += h * 0x100000000; // 2^32",
+                "        }",
+                "        return (n >>> 0) * 2.3283064365386963e-10; // 2^-32",
+                "    };",
+                "}",
+                "",
+                "/** Deterministic simplex noise generator suitable for 2D, 3D and 4D spaces. */",
+                "class SimplexNoise {",
+                "    /**",
+                "     * Creates a new `SimplexNoise` instance.",
+                "     * This involves some setup. You can save a few cpu cycles by reusing the same instance.",
+                "     * @param {(() => number)|string|number} randomOrSeed A random number generator or a seed (string|number).",
+                "     * Defaults to Math.random (random irreproducible initialization).",
+                "     */",
+                "    constructor(randomOrSeed) {",
+                "        if (randomOrSeed === void 0) { randomOrSeed = Math.random; }",
+                "        const random = typeof randomOrSeed == 'function' ? randomOrSeed : alea(randomOrSeed);",
+                "        this.p = buildPermutationTable(random);",
+                "        this.perm = new Uint8Array(512);",
+                "        this.permMod12 = new Uint8Array(512);",
+                "        for (let i = 0; i < 512; i++) {",
+                "            this.perm[i] = this.p[i & 255];",
+                "            this.permMod12[i] = this.perm[i] % 12;",
+                "        }",
+                "    }",
+                "",
+                "    /**",
+                "     * Samples the noise field in 2 dimensions",
+                "     * @param {number} x",
+                "     * @param {number} y",
+                "     * @returns a number in the interval [-1, 1]",
+                "     */",
+                "    noise2D(x, y) {",
+                "        const permMod12 = this.permMod12;",
+                "        const perm = this.perm;",
+                "        let n0 = 0; // Noise contributions from the three corners",
+                "        let n1 = 0;",
+                "        let n2 = 0;",
+                "        // Skew the input space to determine which simplex cell we're in",
+                "        const s = (x + y) * F2; // Hairy factor for 2D",
+                "        const i = Math.floor(x + s);",
+                "        const j = Math.floor(y + s);",
+                "        const t = (i + j) * G2;",
+                "        const X0 = i - t; // Unskew the cell origin back to (x,y) space",
+                "        const Y0 = j - t;",
+                "        const x0 = x - X0; // The x,y distances from the cell origin",
+                "        const y0 = y - Y0;",
+                "        // For the 2D case, the simplex shape is an equilateral triangle.",
+                "        // Determine which simplex we are in.",
+                "        let i1, j1; // Offsets for second (middle) corner of simplex in (i,j) coords",
+                "        if (x0 > y0) {",
+                "            i1 = 1;",
+                "            j1 = 0;",
+                "        } // lower triangle, XY order: (0,0)->(1,0)->(1,1)",
+                "        else {",
+                "            i1 = 0;",
+                "            j1 = 1;",
+                "        } // upper triangle, YX order: (0,0)->(0,1)->(1,1)",
+                "        // A step of (1,0) in (i,j) means a step of (1-c,-c) in (x,y), and",
+                "        // a step of (0,1) in (i,j) means a step of (-c,1-c) in (x,y), where",
+                "        // c = (3-sqrt(3))/6",
+                "        const x1 = x0 - i1 + G2; // Offsets for middle corner in (x,y) unskewed coords",
+                "        const y1 = y0 - j1 + G2;",
+                "        const x2 = x0 - 1.0 + 2.0 * G2; // Offsets for last corner in (x,y) unskewed coords",
+                "        const y2 = y0 - 1.0 + 2.0 * G2;",
+                "        // Work out the hashed gradient indices of the three simplex corners",
+                "        const ii = i & 255;",
+                "        const jj = j & 255;",
+                "        // Calculate the contribution from the three corners",
+                "        let t0 = 0.5 - x0 * x0 - y0 * y0;",
+                "        if (t0 >= 0) {",
+                "            const gi0 = permMod12[ii + perm[jj]] * 3;",
+                "            t0 *= t0;",
+                "            n0 = t0 * t0 * (grad3[gi0] * x0 + grad3[gi0 + 1] * y0); // (x,y) of grad3 used for 2D gradient",
+                "        }",
+                "        let t1 = 0.5 - x1 * x1 - y1 * y1;",
+                "        if (t1 >= 0) {",
+                "            const gi1 = permMod12[ii + i1 + perm[jj + j1]] * 3;",
+                "            t1 *= t1;",
+                "            n1 = t1 * t1 * (grad3[gi1] * x1 + grad3[gi1 + 1] * y1);",
+                "        }",
+                "        let t2 = 0.5 - x2 * x2 - y2 * y2;",
+                "        if (t2 >= 0) {",
+                "            const gi2 = permMod12[ii + 1 + perm[jj + 1]] * 3;",
+                "            t2 *= t2;",
+                "            n2 = t2 * t2 * (grad3[gi2] * x2 + grad3[gi2 + 1] * y2);",
+                "        }",
+                "        // Add contributions from each corner to get the final noise value.",
+                "        // The result is scaled to return values in the interval [-1,1].",
+                "        return 70.0 * (n0 + n1 + n2);",
+                "    }",
+                "",
+                "    /**",
+                "     * Samples the noise field in 3 dimensions",
+                "     * @param {number} x",
+                "     * @param {number} y",
+                "     * @param {number} z",
+                "     * @returns a number in the interval [-1, 1]",
+                "     */",
+                "    noise3D(x, y, z) {",
+                "        const permMod12 = this.permMod12;",
+                "        const perm = this.perm;",
+                "        let n0, n1, n2, n3; // Noise contributions from the four corners",
+                "        // Skew the input space to determine which simplex cell we're in",
+                "        const s = (x + y + z) * F3; // Very nice and simple skew factor for 3D",
+                "        const i = Math.floor(x + s);",
+                "        const j = Math.floor(y + s);",
+                "        const k = Math.floor(z + s);",
+                "        const t = (i + j + k) * G3;",
+                "        const X0 = i - t; // Unskew the cell origin back to (x,y,z) space",
+                "        const Y0 = j - t;",
+                "        const Z0 = k - t;",
+                "        const x0 = x - X0; // The x,y,z distances from the cell origin",
+                "        const y0 = y - Y0;",
+                "        const z0 = z - Z0;",
+                "        // For the 3D case, the simplex shape is a slightly irregular tetrahedron.",
+                "        // Determine which simplex we are in.",
+                "        let i1, j1, k1; // Offsets for second corner of simplex in (i,j,k) coords",
+                "        let i2, j2, k2; // Offsets for third corner of simplex in (i,j,k) coords",
+                "        if (x0 >= y0) {",
+                "            if (y0 >= z0) {",
+                "                i1 = 1;",
+                "                j1 = 0;",
+                "                k1 = 0;",
+                "                i2 = 1;",
+                "                j2 = 1;",
+                "                k2 = 0;",
+                "            } // X Y Z order",
+                "            else if (x0 >= z0) {",
+                "                i1 = 1;",
+                "                j1 = 0;",
+                "                k1 = 0;",
+                "                i2 = 1;",
+                "                j2 = 0;",
+                "                k2 = 1;",
+                "            } // X Z Y order",
+                "            else {",
+                "                i1 = 0;",
+                "                j1 = 0;",
+                "                k1 = 1;",
+                "                i2 = 1;",
+                "                j2 = 0;",
+                "                k2 = 1;",
+                "            } // Z X Y order",
+                "        }",
+                "        else { // x0<y0",
+                "            if (y0 < z0) {",
+                "                i1 = 0;",
+                "                j1 = 0;",
+                "                k1 = 1;",
+                "                i2 = 0;",
+                "                j2 = 1;",
+                "                k2 = 1;",
+                "            } // Z Y X order",
+                "            else if (x0 < z0) {",
+                "                i1 = 0;",
+                "                j1 = 1;",
+                "                k1 = 0;",
+                "                i2 = 0;",
+                "                j2 = 1;",
+                "                k2 = 1;",
+                "            } // Y Z X order",
+                "            else {",
+                "                i1 = 0;",
+                "                j1 = 1;",
+                "                k1 = 0;",
+                "                i2 = 1;",
+                "                j2 = 1;",
+                "                k2 = 0;",
+                "            } // Y X Z order",
+                "        }",
+                "        // A step of (1,0,0) in (i,j,k) means a step of (1-c,-c,-c) in (x,y,z),",
+                "        // a step of (0,1,0) in (i,j,k) means a step of (-c,1-c,-c) in (x,y,z), and",
+                "        // a step of (0,0,1) in (i,j,k) means a step of (-c,-c,1-c) in (x,y,z), where",
+                "        // c = 1/6.",
+                "        const x1 = x0 - i1 + G3; // Offsets for second corner in (x,y,z) coords",
+                "        const y1 = y0 - j1 + G3;",
+                "        const z1 = z0 - k1 + G3;",
+                "        const x2 = x0 - i2 + 2.0 * G3; // Offsets for third corner in (x,y,z) coords",
+                "        const y2 = y0 - j2 + 2.0 * G3;",
+                "        const z2 = z0 - k2 + 2.0 * G3;",
+                "        const x3 = x0 - 1.0 + 3.0 * G3; // Offsets for last corner in (x,y,z) coords",
+                "        const y3 = y0 - 1.0 + 3.0 * G3;",
+                "        const z3 = z0 - 1.0 + 3.0 * G3;",
+                "        // Work out the hashed gradient indices of the four simplex corners",
+                "        const ii = i & 255;",
+                "        const jj = j & 255;",
+                "        const kk = k & 255;",
+                "        // Calculate the contribution from the four corners",
+                "        let t0 = 0.6 - x0 * x0 - y0 * y0 - z0 * z0;",
+                "        if (t0 < 0)",
+                "            n0 = 0.0;",
+                "        else {",
+                "            const gi0 = permMod12[ii + perm[jj + perm[kk]]] * 3;",
+                "            t0 *= t0;",
+                "            n0 = t0 * t0 * (grad3[gi0] * x0 + grad3[gi0 + 1] * y0 + grad3[gi0 + 2] * z0);",
+                "        }",
+                "        let t1 = 0.6 - x1 * x1 - y1 * y1 - z1 * z1;",
+                "        if (t1 < 0)",
+                "            n1 = 0.0;",
+                "        else {",
+                "            const gi1 = permMod12[ii + i1 + perm[jj + j1 + perm[kk + k1]]] * 3;",
+                "            t1 *= t1;",
+                "            n1 = t1 * t1 * (grad3[gi1] * x1 + grad3[gi1 + 1] * y1 + grad3[gi1 + 2] * z1);",
+                "        }",
+                "        let t2 = 0.6 - x2 * x2 - y2 * y2 - z2 * z2;",
+                "        if (t2 < 0)",
+                "            n2 = 0.0;",
+                "        else {",
+                "            const gi2 = permMod12[ii + i2 + perm[jj + j2 + perm[kk + k2]]] * 3;",
+                "            t2 *= t2;",
+                "            n2 = t2 * t2 * (grad3[gi2] * x2 + grad3[gi2 + 1] * y2 + grad3[gi2 + 2] * z2);",
+                "        }",
+                "        let t3 = 0.6 - x3 * x3 - y3 * y3 - z3 * z3;",
+                "        if (t3 < 0)",
+                "            n3 = 0.0;",
+                "        else {",
+                "            const gi3 = permMod12[ii + 1 + perm[jj + 1 + perm[kk + 1]]] * 3;",
+                "            t3 *= t3;",
+                "            n3 = t3 * t3 * (grad3[gi3] * x3 + grad3[gi3 + 1] * y3 + grad3[gi3 + 2] * z3);",
+                "        }",
+                "        // Add contributions from each corner to get the final noise value.",
+                "        // The result is scaled to stay just inside [-1,1]",
+                "        return 32.0 * (n0 + n1 + n2 + n3);",
+                "    }",
+                "",
+                "    /**",
+                "     * Samples the noise field in 4 dimensions",
+                "     * @param {number} x",
+                "     * @param {number} y",
+                "     * @param {number} z",
+                "     * @returns a number in the interval [-1, 1]",
+                "     */",
+                "    noise4D(x, y, z, w) {",
+                "        const perm = this.perm;",
+                "        let n0, n1, n2, n3, n4; // Noise contributions from the five corners",
+                "        // Skew the (x,y,z,w) space to determine which cell of 24 simplices we're in",
+                "        const s = (x + y + z + w) * F4; // Factor for 4D skewing",
+                "        const i = Math.floor(x + s);",
+                "        const j = Math.floor(y + s);",
+                "        const k = Math.floor(z + s);",
+                "        const l = Math.floor(w + s);",
+                "        const t = (i + j + k + l) * G4; // Factor for 4D unskewing",
+                "        const X0 = i - t; // Unskew the cell origin back to (x,y,z,w) space",
+                "        const Y0 = j - t;",
+                "        const Z0 = k - t;",
+                "        const W0 = l - t;",
+                "        const x0 = x - X0; // The x,y,z,w distances from the cell origin",
+                "        const y0 = y - Y0;",
+                "        const z0 = z - Z0;",
+                "        const w0 = w - W0;",
+                "        // For the 4D case, the simplex is a 4D shape I won't even try to describe.",
+                "        // To find out which of the 24 possible simplices we're in, we need to",
+                "        // determine the magnitude ordering of x0, y0, z0 and w0.",
+                "        // Six pair-wise comparisons are performed between each possible pair",
+                "        // of the four coordinates, and the results are used to rank the numbers.",
+                "        let rankx = 0;",
+                "        let ranky = 0;",
+                "        let rankz = 0;",
+                "        let rankw = 0;",
+                "        if (x0 > y0)",
+                "            rankx++;",
+                "        else",
+                "            ranky++;",
+                "        if (x0 > z0)",
+                "            rankx++;",
+                "        else",
+                "            rankz++;",
+                "        if (x0 > w0)",
+                "            rankx++;",
+                "        else",
+                "            rankw++;",
+                "        if (y0 > z0)",
+                "            ranky++;",
+                "        else",
+                "            rankz++;",
+                "        if (y0 > w0)",
+                "            ranky++;",
+                "        else",
+                "            rankw++;",
+                "        if (z0 > w0)",
+                "            rankz++;",
+                "        else",
+                "            rankw++;",
+                "        // simplex[c] is a 4-vector with the numbers 0, 1, 2 and 3 in some order.",
+                "        // Many values of c will never occur, since e.g. x>y>z>w makes x<z, y<w and x<w",
+                "        // impossible. Only the 24 indices which have non-zero entries make any sense.",
+                "        // We use a thresholding to set the coordinates in turn from the largest magnitude.",
+                "        // Rank 3 denotes the largest coordinate.",
+                "        // Rank 2 denotes the second largest coordinate.",
+                "        // Rank 1 denotes the second smallest coordinate.",
+                "        // The integer offsets for the second simplex corner",
+                "        const i1 = rankx >= 3 ? 1 : 0;",
+                "        const j1 = ranky >= 3 ? 1 : 0;",
+                "        const k1 = rankz >= 3 ? 1 : 0;",
+                "        const l1 = rankw >= 3 ? 1 : 0;",
+                "        // The integer offsets for the third simplex corner",
+                "        const i2 = rankx >= 2 ? 1 : 0;",
+                "        const j2 = ranky >= 2 ? 1 : 0;",
+                "        const k2 = rankz >= 2 ? 1 : 0;",
+                "        const l2 = rankw >= 2 ? 1 : 0;",
+                "        // The integer offsets for the fourth simplex corner",
+                "        const i3 = rankx >= 1 ? 1 : 0;",
+                "        const j3 = ranky >= 1 ? 1 : 0;",
+                "        const k3 = rankz >= 1 ? 1 : 0;",
+                "        const l3 = rankw >= 1 ? 1 : 0;",
+                "        // The fifth corner has all coordinate offsets = 1, so no need to compute that.",
+                "        const x1 = x0 - i1 + G4; // Offsets for second corner in (x,y,z,w) coords",
+                "        const y1 = y0 - j1 + G4;",
+                "        const z1 = z0 - k1 + G4;",
+                "        const w1 = w0 - l1 + G4;",
+                "        const x2 = x0 - i2 + 2.0 * G4; // Offsets for third corner in (x,y,z,w) coords",
+                "        const y2 = y0 - j2 + 2.0 * G4;",
+                "        const z2 = z0 - k2 + 2.0 * G4;",
+                "        const w2 = w0 - l2 + 2.0 * G4;",
+                "        const x3 = x0 - i3 + 3.0 * G4; // Offsets for fourth corner in (x,y,z,w) coords",
+                "        const y3 = y0 - j3 + 3.0 * G4;",
+                "        const z3 = z0 - k3 + 3.0 * G4;",
+                "        const w3 = w0 - l3 + 3.0 * G4;",
+                "        const x4 = x0 - 1.0 + 4.0 * G4; // Offsets for last corner in (x,y,z,w) coords",
+                "        const y4 = y0 - 1.0 + 4.0 * G4;",
+                "        const z4 = z0 - 1.0 + 4.0 * G4;",
+                "        const w4 = w0 - 1.0 + 4.0 * G4;",
+                "        // Work out the hashed gradient indices of the five simplex corners",
+                "        const ii = i & 255;",
+                "        const jj = j & 255;",
+                "        const kk = k & 255;",
+                "        const ll = l & 255;",
+                "        // Calculate the contribution from the five corners",
+                "        let t0 = 0.6 - x0 * x0 - y0 * y0 - z0 * z0 - w0 * w0;",
+                "        if (t0 < 0)",
+                "            n0 = 0.0;",
+                "        else {",
+                "            const gi0 = (perm[ii + perm[jj + perm[kk + perm[ll]]]] % 32) * 4;",
+                "            t0 *= t0;",
+                "            n0 = t0 * t0 * (grad4[gi0] * x0 + grad4[gi0 + 1] * y0 + grad4[gi0 + 2] * z0 + grad4[gi0 + 3] * w0);",
+                "        }",
+                "        let t1 = 0.6 - x1 * x1 - y1 * y1 - z1 * z1 - w1 * w1;",
+                "        if (t1 < 0)",
+                "            n1 = 0.0;",
+                "        else {",
+                "            const gi1 = (perm[ii + i1 + perm[jj + j1 + perm[kk + k1 + perm[ll + l1]]]] % 32) * 4;",
+                "            t1 *= t1;",
+                "            n1 = t1 * t1 * (grad4[gi1] * x1 + grad4[gi1 + 1] * y1 + grad4[gi1 + 2] * z1 + grad4[gi1 + 3] * w1);",
+                "        }",
+                "        let t2 = 0.6 - x2 * x2 - y2 * y2 - z2 * z2 - w2 * w2;",
+                "        if (t2 < 0)",
+                "            n2 = 0.0;",
+                "        else {",
+                "            const gi2 = (perm[ii + i2 + perm[jj + j2 + perm[kk + k2 + perm[ll + l2]]]] % 32) * 4;",
+                "            t2 *= t2;",
+                "            n2 = t2 * t2 * (grad4[gi2] * x2 + grad4[gi2 + 1] * y2 + grad4[gi2 + 2] * z2 + grad4[gi2 + 3] * w2);",
+                "        }",
+                "        let t3 = 0.6 - x3 * x3 - y3 * y3 - z3 * z3 - w3 * w3;",
+                "        if (t3 < 0)",
+                "            n3 = 0.0;",
+                "        else {",
+                "            const gi3 = (perm[ii + i3 + perm[jj + j3 + perm[kk + k3 + perm[ll + l3]]]] % 32) * 4;",
+                "            t3 *= t3;",
+                "            n3 = t3 * t3 * (grad4[gi3] * x3 + grad4[gi3 + 1] * y3 + grad4[gi3 + 2] * z3 + grad4[gi3 + 3] * w3);",
+                "        }",
+                "        let t4 = 0.6 - x4 * x4 - y4 * y4 - z4 * z4 - w4 * w4;",
+                "        if (t4 < 0)",
+                "            n4 = 0.0;",
+                "        else {",
+                "            const gi4 = (perm[ii + 1 + perm[jj + 1 + perm[kk + 1 + perm[ll + 1]]]] % 32) * 4;",
+                "            t4 *= t4;",
+                "            n4 = t4 * t4 * (grad4[gi4] * x4 + grad4[gi4 + 1] * y4 + grad4[gi4 + 2] * z4 + grad4[gi4 + 3] * w4);",
+                "        }",
+                "        // Sum up and scale the result to cover the range [-1,1]",
+                "        return 27.0 * (n0 + n1 + n2 + n3 + n4);",
+                "    };",
+                "}",
+                "",
+                "gdjs._shakeObjectExtension = {",
+                "    noiseManager: new NoiseManager(),",
+                "};",
+                ""
+              ],
+              "parameterObjects": "",
+              "useStrict": true,
+              "eventsSheetExpanded": true
+            }
+          ],
+          "parameters": [],
+          "objectGroups": []
+        },
+        {
+          "description": "Generate a number from 2 dimensional simplex noise.",
+          "fullName": "2D noise",
+          "functionType": "Expression",
+          "name": "Noise2d",
+          "private": true,
+          "sentence": "",
+          "events": [
+            {
+              "type": "BuiltinCommonInstructions::JsCode",
+              "inlineCode": [
+                "const name = eventsFunctionContext.getArgument(\"Name\");\r",
+                "const x = eventsFunctionContext.getArgument(\"X\");\r",
+                "const y = eventsFunctionContext.getArgument(\"Y\");\r",
+                "\r",
+                "eventsFunctionContext.returnValue = gdjs._shakeObjectExtension.noiseManager.getGenerator(name).noise(x, y);"
+              ],
+              "parameterObjects": "",
+              "useStrict": true,
+              "eventsSheetExpanded": true
+            }
+          ],
+          "expressionType": {
+            "type": "expression"
+          },
+          "parameters": [
+            {
+              "description": "Generator name",
+              "name": "Name",
+              "type": "string"
+            },
+            {
+              "description": "X coordinate",
+              "name": "X",
+              "type": "expression"
+            },
+            {
+              "description": "Y coordinate",
+              "name": "Y",
+              "type": "expression"
+            }
+          ],
+          "objectGroups": []
+        },
+        {
+          "description": "Generate a number from 3 dimensional simplex noise.",
+          "fullName": "3D noise",
+          "functionType": "Expression",
+          "name": "Noise3d",
+          "private": true,
+          "sentence": "",
+          "events": [
+            {
+              "type": "BuiltinCommonInstructions::JsCode",
+              "inlineCode": [
+                "const name = eventsFunctionContext.getArgument(\"Name\");\r",
+                "const x = eventsFunctionContext.getArgument(\"X\");\r",
+                "const y = eventsFunctionContext.getArgument(\"Y\");\r",
+                "const z = eventsFunctionContext.getArgument(\"Z\");\r",
+                "\r",
+                "eventsFunctionContext.returnValue = gdjs._shakeObjectExtension.noiseManager.getGenerator(name).noise(x, y, z);"
+              ],
+              "parameterObjects": "",
+              "useStrict": true,
+              "eventsSheetExpanded": true
+            }
+          ],
+          "expressionType": {
+            "type": "expression"
+          },
+          "parameters": [
+            {
+              "description": "Generator name",
+              "name": "Name",
+              "type": "string"
+            },
+            {
+              "description": "X coordinate",
+              "name": "X",
+              "type": "expression"
+            },
+            {
+              "description": "Y coordinate",
+              "name": "Y",
+              "type": "expression"
+            },
+            {
+              "description": "Z coordinate",
+              "name": "Z",
+              "type": "expression"
+            }
+          ],
+          "objectGroups": []
+        },
+        {
+          "description": "Generate a number from 4 dimensional simplex noise.",
+          "fullName": "4D noise",
+          "functionType": "Expression",
+          "name": "Noise4d",
+          "private": true,
+          "sentence": "",
+          "events": [
+            {
+              "type": "BuiltinCommonInstructions::JsCode",
+              "inlineCode": [
+                "const name = eventsFunctionContext.getArgument(\"Name\");\r",
+                "const x = eventsFunctionContext.getArgument(\"X\");\r",
+                "const y = eventsFunctionContext.getArgument(\"Y\");\r",
+                "const z = eventsFunctionContext.getArgument(\"Z\");\r",
+                "const w = eventsFunctionContext.getArgument(\"W\");\r",
+                "\r",
+                "eventsFunctionContext.returnValue = gdjs._shakeObjectExtension.noiseManager.getGenerator(name).noise(x, y, z, w);"
+              ],
+              "parameterObjects": "",
+              "useStrict": true,
+              "eventsSheetExpanded": true
+            }
+          ],
+          "expressionType": {
+            "type": "expression"
+          },
+          "parameters": [
+            {
+              "description": "Generator name",
+              "name": "Name",
+              "type": "string"
+            },
+            {
+              "description": "X coordinate",
+              "name": "X",
+              "type": "expression"
+            },
+            {
+              "description": "Y coordinate",
+              "name": "Y",
+              "type": "expression"
+            },
+            {
+              "description": "Z coordinate",
+              "name": "Z",
+              "type": "expression"
+            },
+            {
+              "description": "W coordinate",
+              "name": "W",
+              "type": "expression"
+            }
+          ],
+          "objectGroups": []
+        },
+        {
+          "description": "Create a noise generator with default settings (frequency = 1,  octaves = 1, persistence = 0.5, lacunarity = 2).",
+          "fullName": "Create a noise generator",
+          "functionType": "Action",
+          "name": "Create",
+          "private": true,
+          "sentence": "Create a noise generator named _PARAM1_",
+          "events": [
+            {
+              "type": "BuiltinCommonInstructions::JsCode",
+              "inlineCode": [
+                "const name = eventsFunctionContext.getArgument(\"Name\");\r",
+                "\r",
+                "gdjs._shakeObjectExtension.noiseManager.getGenerator(name);"
+              ],
+              "parameterObjects": "",
+              "useStrict": true,
+              "eventsSheetExpanded": false
+            }
+          ],
+          "parameters": [
+            {
+              "description": "Generator name",
+              "name": "Name",
+              "type": "string"
+            }
+          ],
+          "objectGroups": []
+        },
+        {
+          "description": "Delete a noise generator and loose its settings.",
+          "fullName": "Delete a noise generator",
+          "functionType": "Action",
+          "name": "Delete",
+          "private": true,
+          "sentence": "Delete _PARAM1_ noise generator",
+          "events": [
+            {
+              "type": "BuiltinCommonInstructions::JsCode",
+              "inlineCode": [
+                "const name = eventsFunctionContext.getArgument(\"Name\");\r",
+                "\r",
+                "gdjs._shakeObjectExtension.noiseManager.deleteGenerator(name);"
+              ],
+              "parameterObjects": "",
+              "useStrict": true,
+              "eventsSheetExpanded": false
+            }
+          ],
+          "parameters": [
+            {
+              "description": "Generator name",
+              "name": "Name",
+              "type": "string"
+            }
+          ],
+          "objectGroups": []
+        },
+        {
+          "description": "Delete all noise generators and loose their settings.",
+          "fullName": "Delete all noise generators",
+          "functionType": "Action",
+          "name": "DeleteAll",
+          "private": true,
+          "sentence": "Delete all noise generators",
+          "events": [
+            {
+              "type": "BuiltinCommonInstructions::JsCode",
+              "inlineCode": "gdjs._shakeObjectExtension.noiseManager.deleteAllGenerators();",
+              "parameterObjects": "",
+              "useStrict": true,
+              "eventsSheetExpanded": false
+            }
+          ],
+          "parameters": [],
+          "objectGroups": []
+        },
+        {
+          "description": "The seed is a number used to generate the random noise. Setting the same seed will result in the same random noise generation. It's for example useful to generate the same world, by saving this seed value and reusing it later to generate again a world.",
+          "fullName": "Noise seed",
+          "functionType": "Action",
+          "name": "SetSeed",
+          "private": true,
+          "sentence": "Change the noise seed to _PARAM1_",
+          "events": [
+            {
+              "type": "BuiltinCommonInstructions::JsCode",
+              "inlineCode": "gdjs._shakeObjectExtension.noiseManager.setSeed(eventsFunctionContext.getArgument(\"Seed\"));",
+              "parameterObjects": "",
+              "useStrict": true,
+              "eventsSheetExpanded": false
+            }
+          ],
+          "parameters": [
+            {
+              "description": "Seed",
+              "longDescription": "15 digits numbers maximum",
+              "name": "Seed",
+              "type": "expression"
+            }
+          ],
+          "objectGroups": []
+        },
+        {
+          "description": "Change the looping period on X used for noise generation. The noise will wrap-around on X.",
+          "fullName": "Noise looping period on X",
+          "functionType": "Action",
+          "name": "SetLoopPeriodX",
+          "private": true,
+          "sentence": "Change the looping period on X of _PARAM2_: _PARAM1_",
+          "events": [
+            {
+              "type": "BuiltinCommonInstructions::JsCode",
+              "inlineCode": [
+                "const name = eventsFunctionContext.getArgument(\"Name\");\r",
+                "\r",
+                "gdjs._shakeObjectExtension.noiseManager.getGenerator(name).xLoopPeriod = eventsFunctionContext.getArgument(\"LoopPeriod\");"
+              ],
+              "parameterObjects": "",
+              "useStrict": true,
+              "eventsSheetExpanded": false
+            }
+          ],
+          "parameters": [
+            {
+              "description": "Looping period on X",
+              "name": "LoopPeriod",
+              "type": "expression"
+            },
+            {
+              "description": "Generator name",
+              "name": "Name",
+              "type": "string"
+            }
+          ],
+          "objectGroups": []
+        },
+        {
+          "description": "Change the looping period on Y used for noise generation. The noise will wrap-around on Y.",
+          "fullName": "Noise looping period on Y",
+          "functionType": "Action",
+          "name": "SetLoopPeriodY",
+          "private": true,
+          "sentence": "Change the looping period on Y of _PARAM2_: _PARAM1_",
+          "events": [
+            {
+              "type": "BuiltinCommonInstructions::JsCode",
+              "inlineCode": [
+                "const name = eventsFunctionContext.getArgument(\"Name\");\r",
+                "\r",
+                "gdjs._shakeObjectExtension.noiseManager.getGenerator(name).yLoopPeriod = eventsFunctionContext.getArgument(\"LoopPeriod\");"
+              ],
+              "parameterObjects": "",
+              "useStrict": true,
+              "eventsSheetExpanded": true
+            }
+          ],
+          "parameters": [
+            {
+              "description": "Looping period on Y",
+              "name": "LoopPeriod",
+              "type": "expression"
+            },
+            {
+              "description": "Generator name",
+              "name": "Name",
+              "type": "string"
+            }
+          ],
+          "objectGroups": []
+        },
+        {
+          "description": "Change the base frequency used for noise generation. A lower frequency will zoom in the noise.",
+          "fullName": "Noise base frequency",
+          "functionType": "Action",
+          "name": "SetFrequency",
+          "private": true,
+          "sentence": "Change the noise frequency of _PARAM2_: _PARAM1_",
+          "events": [
+            {
+              "type": "BuiltinCommonInstructions::JsCode",
+              "inlineCode": [
+                "const name = eventsFunctionContext.getArgument(\"Name\");\r",
+                "\r",
+                "gdjs._shakeObjectExtension.noiseManager.getGenerator(name).frequency = eventsFunctionContext.getArgument(\"Frequency\");"
+              ],
+              "parameterObjects": "",
+              "useStrict": true,
+              "eventsSheetExpanded": false
+            }
+          ],
+          "parameters": [
+            {
+              "description": "Frequency",
+              "name": "Frequency",
+              "type": "expression"
+            },
+            {
+              "description": "Generator name",
+              "name": "Name",
+              "type": "string"
+            }
+          ],
+          "objectGroups": []
+        },
+        {
+          "description": "Change the number of octaves used for noise generation. It can be seen as layers of noise with different zoom.",
+          "fullName": "Noise octaves",
+          "functionType": "Action",
+          "name": "SetOctaves",
+          "private": true,
+          "sentence": "Change the number of noise octaves of _PARAM2_: _PARAM1_",
+          "events": [
+            {
+              "type": "BuiltinCommonInstructions::JsCode",
+              "inlineCode": [
+                "const name = eventsFunctionContext.getArgument(\"Name\");\r",
+                "\r",
+                "gdjs._shakeObjectExtension.noiseManager.getGenerator(name).octaves = eventsFunctionContext.getArgument(\"Octaves\");"
+              ],
+              "parameterObjects": "",
+              "useStrict": true,
+              "eventsSheetExpanded": false
+            }
+          ],
+          "parameters": [
+            {
+              "description": "Octaves",
+              "name": "Octaves",
+              "type": "expression"
+            },
+            {
+              "description": "Generator name",
+              "name": "Name",
+              "type": "string"
+            }
+          ],
+          "objectGroups": []
+        },
+        {
+          "description": "Change the persistence used for noise generation. At its default value \"0.5\", it halves the noise amplitude at each octave.",
+          "fullName": "Noise persistence",
+          "functionType": "Action",
+          "name": "SetPersistence",
+          "private": true,
+          "sentence": "Change the noise persistence of _PARAM2_: _PARAM1_",
+          "events": [
+            {
+              "type": "BuiltinCommonInstructions::JsCode",
+              "inlineCode": [
+                "const name = eventsFunctionContext.getArgument(\"Name\");\r",
+                "\r",
+                "gdjs._shakeObjectExtension.noiseManager.getGenerator(name).persistence = eventsFunctionContext.getArgument(\"Persistence\");"
+              ],
+              "parameterObjects": "",
+              "useStrict": true,
+              "eventsSheetExpanded": false
+            }
+          ],
+          "parameters": [
+            {
+              "description": "Persistence",
+              "name": "Persistence",
+              "type": "expression"
+            },
+            {
+              "description": "Generator name",
+              "name": "Name",
+              "type": "string"
+            }
+          ],
+          "objectGroups": []
+        },
+        {
+          "description": "Change the lacunarity used for noise generation. At its default value \"2\", it doubles the frequency at each octave.",
+          "fullName": "Noise lacunarity",
+          "functionType": "Action",
+          "name": "SetLacunarity",
+          "private": true,
+          "sentence": "Change the noise lacunarity of _PARAM2_: _PARAM1_",
+          "events": [
+            {
+              "type": "BuiltinCommonInstructions::JsCode",
+              "inlineCode": [
+                "const name = eventsFunctionContext.getArgument(\"Name\");\r",
+                "\r",
+                "gdjs._shakeObjectExtension.noiseManager.getGenerator(name).lacunarity = eventsFunctionContext.getArgument(\"Lacunarity\");"
+              ],
+              "parameterObjects": "",
+              "useStrict": true,
+              "eventsSheetExpanded": false
+            }
+          ],
+          "parameters": [
+            {
+              "description": "Lacunarity",
+              "name": "Lacunarity",
+              "type": "expression"
+            },
+            {
+              "description": "Generator name",
+              "name": "Name",
+              "type": "string"
+            }
+          ],
+          "objectGroups": []
+        },
+        {
+          "description": "The seed used for noise generation.",
+          "fullName": "Noise seed",
+          "functionType": "Expression",
+          "name": "Seed",
+          "private": true,
+          "sentence": "",
+          "events": [
+            {
+              "type": "BuiltinCommonInstructions::JsCode",
+              "inlineCode": "eventsFunctionContext.returnValue = gdjs._shakeObjectExtension.noiseManager.seed;",
+              "parameterObjects": "",
+              "useStrict": true,
+              "eventsSheetExpanded": false
+            }
+          ],
+          "expressionType": {
+            "type": "expression"
+          },
+          "parameters": [],
+          "objectGroups": []
+        },
+        {
+          "description": "The base frequency used for noise generation.",
+          "fullName": "Noise base frequency",
+          "functionType": "Expression",
+          "name": "Frequency",
+          "private": true,
+          "sentence": "",
+          "events": [
+            {
+              "type": "BuiltinCommonInstructions::JsCode",
+              "inlineCode": [
+                "const name = eventsFunctionContext.getArgument(\"Name\");\r",
+                "\r",
+                "eventsFunctionContext.returnValue = gdjs._shakeObjectExtension.noiseManager.getGenerator(name).frequency;"
+              ],
+              "parameterObjects": "",
+              "useStrict": true,
+              "eventsSheetExpanded": false
+            }
+          ],
+          "expressionType": {
+            "type": "expression"
+          },
+          "parameters": [
+            {
+              "description": "Generator name",
+              "name": "Name",
+              "type": "string"
+            }
+          ],
+          "objectGroups": []
+        },
+        {
+          "description": "The number of octaves used for noise generation.",
+          "fullName": "Noise octaves number",
+          "functionType": "Expression",
+          "name": "Octaves",
+          "private": true,
+          "sentence": "",
+          "events": [
+            {
+              "type": "BuiltinCommonInstructions::JsCode",
+              "inlineCode": [
+                "const name = eventsFunctionContext.getArgument(\"Name\");\r",
+                "\r",
+                "eventsFunctionContext.returnValue = gdjs._shakeObjectExtension.noiseManager.getGenerator(name).octaves;"
+              ],
+              "parameterObjects": "",
+              "useStrict": true,
+              "eventsSheetExpanded": false
+            }
+          ],
+          "expressionType": {
+            "type": "expression"
+          },
+          "parameters": [
+            {
+              "description": "Generator name",
+              "name": "Name",
+              "type": "string"
+            }
+          ],
+          "objectGroups": []
+        },
+        {
+          "description": "The persistence used for noise generation.",
+          "fullName": "Noise persistence",
+          "functionType": "Expression",
+          "name": "Persistence",
+          "private": true,
+          "sentence": "",
+          "events": [
+            {
+              "type": "BuiltinCommonInstructions::JsCode",
+              "inlineCode": [
+                "const name = eventsFunctionContext.getArgument(\"Name\");\r",
+                "\r",
+                "eventsFunctionContext.returnValue = gdjs._shakeObjectExtension.noiseManager.getGenerator(name).persistence;"
+              ],
+              "parameterObjects": "",
+              "useStrict": true,
+              "eventsSheetExpanded": false
+            }
+          ],
+          "expressionType": {
+            "type": "expression"
+          },
+          "parameters": [
+            {
+              "description": "Generator name",
+              "name": "Name",
+              "type": "string"
+            }
+          ],
+          "objectGroups": []
+        },
+        {
+          "description": "The lacunarity used for noise generation.",
+          "fullName": "Noise lacunarity",
+          "functionType": "Expression",
+          "name": "Lacunarity",
+          "private": true,
+          "sentence": "",
+          "events": [
+            {
+              "type": "BuiltinCommonInstructions::JsCode",
+              "inlineCode": [
+                "const name = eventsFunctionContext.getArgument(\"Name\");\r",
+                "\r",
+                "eventsFunctionContext.returnValue = gdjs._shakeObjectExtension.noiseManager.getGenerator(name).lacunarity;"
+              ],
+              "parameterObjects": "",
+              "useStrict": true,
+              "eventsSheetExpanded": false
+            }
+          ],
+          "expressionType": {
+            "type": "expression"
+          },
+          "parameters": [
+            {
+              "description": "Generator name",
+              "name": "Name",
+              "type": "string"
+            }
+          ],
+          "objectGroups": []
+        }
+      ],
+      "eventsBasedBehaviors": [
+        {
+          "description": "Shake 3D objects with translation and rotation.",
+          "fullName": "3D shake",
+          "name": "ShakeModel3D",
+          "objectType": "",
+          "quickCustomizationVisibility": "hidden",
+          "eventsFunctions": [
+            {
+              "fullName": "",
+              "functionType": "Action",
+              "name": "onCreated",
+              "sentence": "",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [
+                    {
+                      "type": {
+                        "value": "ShakeObject3D::ShakeModel3D::PropertyIsStartingAtCreation"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior"
+                      ]
+                    }
+                  ],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "ShakeObject3D::DefineHelperClasses"
+                      },
+                      "parameters": [
+                        "",
+                        ""
+                      ]
+                    },
+                    {
+                      "type": {
+                        "value": "ShakeObject3D::ShakeModel3D::StartShaking"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "0",
+                        ""
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "ShakeObject3D::ShakeModel3D",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "fullName": "",
+              "functionType": "Action",
+              "name": "doStepPostEvents",
+              "sentence": "",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Comment",
+                  "color": {
+                    "b": 109,
+                    "g": 230,
+                    "r": 255,
+                    "textB": 0,
+                    "textG": 0,
+                    "textR": 0
+                  },
+                  "comment": "Step time counters."
+                },
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "ShakeObject3D::ShakeModel3D::SetPropertyTime"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "+",
+                        "TimeDelta()"
+                      ]
+                    }
+                  ]
+                },
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [
+                    {
+                      "type": {
+                        "value": "ShakeObject3D::ShakeModel3D::IsShaking"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        ""
+                      ]
+                    }
+                  ],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "ShakeObject3D::ShakeModel3D::SetPropertyNoiseTime"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "+",
+                        "TimeDelta() * Frequency"
+                      ]
+                    },
+                    {
+                      "type": {
+                        "value": "ShakeObject3D::ShakeModel3D::SetSharedPropertyEasingFactor"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "=",
+                        "min(Object.Behavior::StartEasingFactor(), Object.Behavior::StopEasingFactor())"
+                      ]
+                    }
+                  ],
+                  "events": [
+                    {
+                      "type": "BuiltinCommonInstructions::Comment",
+                      "color": {
+                        "b": 109,
+                        "g": 230,
+                        "r": 255,
+                        "textB": 0,
+                        "textG": 0,
+                        "textR": 0
+                      },
+                      "comment": "Shake the object.\nSave the object displacement to revert it in onScenePostEvents."
+                    },
+                    {
+                      "type": "BuiltinCommonInstructions::Standard",
+                      "conditions": [
+                        {
+                          "type": {
+                            "value": "ShakeObject3D::ShakeModel3D::PropertyTranslationAmplitudeX"
+                          },
+                          "parameters": [
+                            "Object",
+                            "Behavior",
+                            "!=",
+                            "0"
+                          ]
+                        }
+                      ],
+                      "actions": [
+                        {
+                          "type": {
+                            "value": "ShakeObject3D::ShakeModel3D::SetPropertyDeltaX"
+                          },
+                          "parameters": [
+                            "Object",
+                            "Behavior",
+                            "=",
+                            "ShakeObject3D::Noise2d(\"\", NoiseTime, 1000) * TranslationAmplitudeX * EasingFactor"
+                          ]
+                        },
+                        {
+                          "type": {
+                            "value": "MettreX"
+                          },
+                          "parameters": [
+                            "Object",
+                            "+",
+                            "DeltaX"
+                          ]
+                        }
+                      ]
+                    },
+                    {
+                      "type": "BuiltinCommonInstructions::Standard",
+                      "conditions": [
+                        {
+                          "type": {
+                            "value": "ShakeObject3D::ShakeModel3D::PropertyTranslationAmplitudeY"
+                          },
+                          "parameters": [
+                            "Object",
+                            "Behavior",
+                            "!=",
+                            "0"
+                          ]
+                        }
+                      ],
+                      "actions": [
+                        {
+                          "type": {
+                            "value": "ShakeObject3D::ShakeModel3D::SetPropertyDeltaY"
+                          },
+                          "parameters": [
+                            "Object",
+                            "Behavior",
+                            "=",
+                            "ShakeObject3D::Noise2d(\"\", NoiseTime, 2000) * TranslationAmplitudeY * EasingFactor"
+                          ]
+                        },
+                        {
+                          "type": {
+                            "value": "MettreY"
+                          },
+                          "parameters": [
+                            "Object",
+                            "+",
+                            "DeltaY"
+                          ]
+                        }
+                      ]
+                    },
+                    {
+                      "type": "BuiltinCommonInstructions::Standard",
+                      "conditions": [
+                        {
+                          "type": {
+                            "value": "ShakeObject3D::ShakeModel3D::PropertyTranslationAmplitudeZ"
+                          },
+                          "parameters": [
+                            "Object",
+                            "Behavior",
+                            "!=",
+                            "0"
+                          ]
+                        }
+                      ],
+                      "actions": [
+                        {
+                          "type": {
+                            "value": "ShakeObject3D::ShakeModel3D::SetPropertyDeltaZ"
+                          },
+                          "parameters": [
+                            "Object",
+                            "Behavior",
+                            "=",
+                            "ShakeObject3D::Noise2d(\"\", NoiseTime, 3000) * TranslationAmplitudeZ * EasingFactor"
+                          ]
+                        },
+                        {
+                          "type": {
+                            "value": "Scene3D::Base3DBehavior::SetZ"
+                          },
+                          "parameters": [
+                            "Object",
+                            "Object3D",
+                            "+",
+                            "DeltaZ"
+                          ]
+                        }
+                      ]
+                    },
+                    {
+                      "type": "BuiltinCommonInstructions::Standard",
+                      "conditions": [
+                        {
+                          "type": {
+                            "value": "ShakeObject3D::ShakeModel3D::PropertyRotationAmplitudeX"
+                          },
+                          "parameters": [
+                            "Object",
+                            "Behavior",
+                            "!=",
+                            "0"
+                          ]
+                        }
+                      ],
+                      "actions": [
+                        {
+                          "type": {
+                            "value": "ShakeObject3D::ShakeModel3D::SetPropertyDeltaAngleX"
+                          },
+                          "parameters": [
+                            "Object",
+                            "Behavior",
+                            "=",
+                            "ShakeObject3D::Noise2d(\"\", NoiseTime, 4000) * RotationAmplitudeX * EasingFactor"
+                          ]
+                        },
+                        {
+                          "type": {
+                            "value": "Scene3D::Base3DBehavior::SetRotationX"
+                          },
+                          "parameters": [
+                            "Object",
+                            "Object3D",
+                            "+",
+                            "DeltaAngleX"
+                          ]
+                        }
+                      ]
+                    },
+                    {
+                      "type": "BuiltinCommonInstructions::Standard",
+                      "conditions": [
+                        {
+                          "type": {
+                            "value": "ShakeObject3D::ShakeModel3D::PropertyRotationAmplitudeY"
+                          },
+                          "parameters": [
+                            "Object",
+                            "Behavior",
+                            "!=",
+                            "0"
+                          ]
+                        }
+                      ],
+                      "actions": [
+                        {
+                          "type": {
+                            "value": "ShakeObject3D::ShakeModel3D::SetPropertyDeltaAngleY"
+                          },
+                          "parameters": [
+                            "Object",
+                            "Behavior",
+                            "=",
+                            "ShakeObject3D::Noise2d(\"\", NoiseTime, 5000) * RotationAmplitudeY * EasingFactor"
+                          ]
+                        },
+                        {
+                          "type": {
+                            "value": "Scene3D::Base3DBehavior::SetRotationY"
+                          },
+                          "parameters": [
+                            "Object",
+                            "Object3D",
+                            "+",
+                            "DeltaAngleY"
+                          ]
+                        }
+                      ]
+                    },
+                    {
+                      "type": "BuiltinCommonInstructions::Standard",
+                      "conditions": [
+                        {
+                          "type": {
+                            "value": "ShakeObject3D::ShakeModel3D::PropertyRotationAmplitudeZ"
+                          },
+                          "parameters": [
+                            "Object",
+                            "Behavior",
+                            "!=",
+                            "0"
+                          ]
+                        }
+                      ],
+                      "actions": [
+                        {
+                          "type": {
+                            "value": "ShakeObject3D::ShakeModel3D::SetPropertyDeltaAngleZ"
+                          },
+                          "parameters": [
+                            "Object",
+                            "Behavior",
+                            "=",
+                            "ShakeObject3D::Noise2d(\"\", NoiseTime, 6000) * RotationAmplitudeZ * EasingFactor"
+                          ]
+                        },
+                        {
+                          "type": {
+                            "value": "SetAngle"
+                          },
+                          "parameters": [
+                            "Object",
+                            "+",
+                            "DeltaAngleZ"
+                          ]
+                        }
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "ShakeObject3D::ShakeModel3D",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "fullName": "",
+              "functionType": "Action",
+              "name": "doStepPreEvents",
+              "sentence": "",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Comment",
+                  "color": {
+                    "b": 109,
+                    "g": 230,
+                    "r": 255,
+                    "textB": 0,
+                    "textG": 0,
+                    "textR": 0
+                  },
+                  "comment": "Revert the shaking."
+                },
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [
+                    {
+                      "type": {
+                        "value": "ShakeObject3D::ShakeModel3D::IsShaking"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        ""
+                      ]
+                    }
+                  ],
+                  "actions": [],
+                  "events": [
+                    {
+                      "type": "BuiltinCommonInstructions::Standard",
+                      "conditions": [
+                        {
+                          "type": {
+                            "value": "ShakeObject3D::ShakeModel3D::PropertyTranslationAmplitudeX"
+                          },
+                          "parameters": [
+                            "Object",
+                            "Behavior",
+                            "!=",
+                            "0"
+                          ]
+                        }
+                      ],
+                      "actions": [
+                        {
+                          "type": {
+                            "value": "MettreX"
+                          },
+                          "parameters": [
+                            "Object",
+                            "-",
+                            "DeltaX"
+                          ]
+                        }
+                      ]
+                    },
+                    {
+                      "type": "BuiltinCommonInstructions::Standard",
+                      "conditions": [
+                        {
+                          "type": {
+                            "value": "ShakeObject3D::ShakeModel3D::PropertyTranslationAmplitudeY"
+                          },
+                          "parameters": [
+                            "Object",
+                            "Behavior",
+                            "!=",
+                            "0"
+                          ]
+                        }
+                      ],
+                      "actions": [
+                        {
+                          "type": {
+                            "value": "MettreY"
+                          },
+                          "parameters": [
+                            "Object",
+                            "-",
+                            "DeltaY"
+                          ]
+                        }
+                      ]
+                    },
+                    {
+                      "type": "BuiltinCommonInstructions::Standard",
+                      "conditions": [
+                        {
+                          "type": {
+                            "value": "ShakeObject3D::ShakeModel3D::PropertyTranslationAmplitudeZ"
+                          },
+                          "parameters": [
+                            "Object",
+                            "Behavior",
+                            "!=",
+                            "0"
+                          ]
+                        }
+                      ],
+                      "actions": [
+                        {
+                          "type": {
+                            "value": "Scene3D::Base3DBehavior::SetZ"
+                          },
+                          "parameters": [
+                            "Object",
+                            "Object3D",
+                            "-",
+                            "DeltaZ"
+                          ]
+                        }
+                      ]
+                    },
+                    {
+                      "type": "BuiltinCommonInstructions::Standard",
+                      "conditions": [
+                        {
+                          "type": {
+                            "value": "ShakeObject3D::ShakeModel3D::PropertyRotationAmplitudeX"
+                          },
+                          "parameters": [
+                            "Object",
+                            "Behavior",
+                            "!=",
+                            "0"
+                          ]
+                        }
+                      ],
+                      "actions": [
+                        {
+                          "type": {
+                            "value": "Scene3D::Base3DBehavior::SetRotationX"
+                          },
+                          "parameters": [
+                            "Object",
+                            "Object3D",
+                            "-",
+                            "DeltaAngleX"
+                          ]
+                        }
+                      ]
+                    },
+                    {
+                      "type": "BuiltinCommonInstructions::Standard",
+                      "conditions": [
+                        {
+                          "type": {
+                            "value": "ShakeObject3D::ShakeModel3D::PropertyRotationAmplitudeY"
+                          },
+                          "parameters": [
+                            "Object",
+                            "Behavior",
+                            "!=",
+                            "0"
+                          ]
+                        }
+                      ],
+                      "actions": [
+                        {
+                          "type": {
+                            "value": "Scene3D::Base3DBehavior::SetRotationY"
+                          },
+                          "parameters": [
+                            "Object",
+                            "Object3D",
+                            "-",
+                            "DeltaAngleY"
+                          ]
+                        }
+                      ]
+                    },
+                    {
+                      "type": "BuiltinCommonInstructions::Standard",
+                      "conditions": [
+                        {
+                          "type": {
+                            "value": "ShakeObject3D::ShakeModel3D::PropertyRotationAmplitudeZ"
+                          },
+                          "parameters": [
+                            "Object",
+                            "Behavior",
+                            "!=",
+                            "0"
+                          ]
+                        }
+                      ],
+                      "actions": [
+                        {
+                          "type": {
+                            "value": "SetAngle"
+                          },
+                          "parameters": [
+                            "Object",
+                            "-",
+                            "DeltaAngleZ"
+                          ]
+                        }
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "ShakeObject3D::ShakeModel3D",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "Shake the object with a linear easing at the start and the end.",
+              "fullName": "Shake",
+              "functionType": "Action",
+              "name": "Shake",
+              "sentence": "Shake _PARAM0_ for _PARAM2_ seconds with _PARAM3_ seconds of easing to start and _PARAM4_ seconds to stop",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "ShakeObject3D::ShakeModel3D::SetPropertyTime"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "=",
+                        "0"
+                      ]
+                    },
+                    {
+                      "type": {
+                        "value": "ShakeObject3D::ShakeModel3D::SetPropertyNoiseTime"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "=",
+                        "TimeFromStart() * Frequency"
+                      ]
+                    },
+                    {
+                      "type": {
+                        "value": "ShakeObject3D::ShakeModel3D::SetPropertyDuration"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "=",
+                        "NewDuration"
+                      ]
+                    },
+                    {
+                      "type": {
+                        "value": "ShakeObject3D::ShakeModel3D::SetPropertyStartEasingDuration"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "=",
+                        "StartEaseDuration"
+                      ]
+                    },
+                    {
+                      "type": {
+                        "value": "ShakeObject3D::ShakeModel3D::SetPropertyStopEasingDuration"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "=",
+                        "StopEaseDuration"
+                      ]
+                    }
+                  ]
+                },
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [
+                    {
+                      "type": {
+                        "value": "CompareArgumentAsNumber"
+                      },
+                      "parameters": [
+                        "\"NewDuration\"",
+                        "<",
+                        "StartEaseDuration + StopEaseDuration"
+                      ]
+                    }
+                  ],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "ShakeObject3D::ShakeModel3D::SetPropertyStartEasingDuration"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "=",
+                        "StartEaseDuration * NewDuration / (StartEaseDuration + StopEaseDuration)"
+                      ]
+                    },
+                    {
+                      "type": {
+                        "value": "ShakeObject3D::ShakeModel3D::SetPropertyStopEasingDuration"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "=",
+                        "StopEaseDuration * NewDuration / (StartEaseDuration + StopEaseDuration)"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "ShakeObject3D::ShakeModel3D",
+                  "type": "behavior"
+                },
+                {
+                  "description": "Duration (in seconds)",
+                  "name": "NewDuration",
+                  "type": "expression"
+                },
+                {
+                  "description": "Ease duration to start (in seconds)",
+                  "name": "StartEaseDuration",
+                  "type": "expression"
+                },
+                {
+                  "description": "Ease duration to stop (in seconds)",
+                  "name": "StopEaseDuration",
+                  "type": "expression"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "Shake the object with a linear easing at the start and keep shaking until the stop action is used.",
+              "fullName": "Start shaking",
+              "functionType": "Action",
+              "name": "StartShaking",
+              "sentence": "Start shaking _PARAM0_ with _PARAM2_ seconds of easing",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "ShakeObject3D::ShakeModel3D::SetPropertyTime"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "=",
+                        "0"
+                      ]
+                    },
+                    {
+                      "type": {
+                        "value": "ShakeObject3D::ShakeModel3D::SetPropertyNoiseTime"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "=",
+                        "TimeFromStart() * Frequency"
+                      ]
+                    },
+                    {
+                      "type": {
+                        "value": "ShakeObject3D::ShakeModel3D::SetPropertyDuration"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "=",
+                        "1234567890"
+                      ]
+                    },
+                    {
+                      "type": {
+                        "value": "ShakeObject3D::ShakeModel3D::SetPropertyStartEasingDuration"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "=",
+                        "EaseDuration"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "ShakeObject3D::ShakeModel3D",
+                  "type": "behavior"
+                },
+                {
+                  "description": "Ease duration (in seconds)",
+                  "name": "EaseDuration",
+                  "type": "expression"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "Stop shaking the object with a linear easing.",
+              "fullName": "Stop shaking",
+              "functionType": "Action",
+              "name": "StopShaking",
+              "sentence": "Stop shaking _PARAM0_ with _PARAM2_ seconds of easing",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [
+                    {
+                      "type": {
+                        "value": "ShakeObject3D::ShakeModel3D::IsShaking"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        ""
+                      ]
+                    }
+                  ],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "ShakeObject3D::ShakeModel3D::SetPropertyTime"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "=",
+                        "0"
+                      ]
+                    },
+                    {
+                      "type": {
+                        "value": "ShakeObject3D::ShakeModel3D::SetPropertyDuration"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "=",
+                        "EaseDuration"
+                      ]
+                    },
+                    {
+                      "type": {
+                        "value": "ShakeObject3D::ShakeModel3D::SetPropertyStopEasingDuration"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "=",
+                        "EaseDuration"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "ShakeObject3D::ShakeModel3D",
+                  "type": "behavior"
+                },
+                {
+                  "description": "Ease duration (in seconds)",
+                  "name": "EaseDuration",
+                  "type": "expression"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "Check if the object is shaking.",
+              "fullName": "Is shaking",
+              "functionType": "Condition",
+              "name": "IsShaking",
+              "sentence": "_PARAM0_ is shaking",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [
+                    {
+                      "type": {
+                        "value": "ShakeObject3D::ShakeModel3D::PropertyTime"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "<",
+                        "Duration"
+                      ]
+                    },
+                    {
+                      "type": {
+                        "value": "ShakeObject3D::ShakeModel3D::PropertyTime"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        ">",
+                        "0"
+                      ]
+                    }
+                  ],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SetReturnBoolean"
+                      },
+                      "parameters": [
+                        "True"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "ShakeObject3D::ShakeModel3D",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "Check if the object is stopping to shake.",
+              "fullName": "Is stopping to shake",
+              "functionType": "Condition",
+              "name": "IsStopping",
+              "sentence": "_PARAM0_ is stopping to shake",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [
+                    {
+                      "type": {
+                        "value": "ShakeObject3D::ShakeModel3D::IsShaking"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        ""
+                      ]
+                    },
+                    {
+                      "type": {
+                        "value": "ShakeObject3D::ShakeModel3D::StopEasingFactor"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "<",
+                        "1",
+                        ""
+                      ]
+                    }
+                  ],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SetReturnBoolean"
+                      },
+                      "parameters": [
+                        "True"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "ShakeObject3D::ShakeModel3D",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "the shaking frequency of the object.",
+              "fullName": "Shaking frequency",
+              "functionType": "ExpressionAndCondition",
+              "group": "ShakeObject3D configuration",
+              "name": "Frequency",
+              "sentence": "the shaking frequency",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SetReturnNumber"
+                      },
+                      "parameters": [
+                        "Frequency"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "expressionType": {
+                "type": "expression"
+              },
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "ShakeObject3D::ShakeModel3D",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "fullName": "",
+              "functionType": "ActionWithOperator",
+              "getterName": "Frequency",
+              "name": "SetFrequency",
+              "sentence": "",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "ShakeObject3D::ShakeModel3D::SetPropertyFrequency"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "=",
+                        "Value"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "ShakeObject3D::ShakeModel3D",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "Return the easing factor according to start properties.",
+              "fullName": "Start easing factor",
+              "functionType": "Expression",
+              "name": "StartEasingFactor",
+              "private": true,
+              "sentence": "",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [
+                    {
+                      "type": {
+                        "value": "ShakeObject3D::ShakeModel3D::PropertyStartEasingDuration"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "<=",
+                        "0"
+                      ]
+                    }
+                  ],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SetReturnNumber"
+                      },
+                      "parameters": [
+                        "1"
+                      ]
+                    }
+                  ]
+                },
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [
+                    {
+                      "type": {
+                        "value": "ShakeObject3D::ShakeModel3D::PropertyStartEasingDuration"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        ">",
+                        "0"
+                      ]
+                    }
+                  ],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SetReturnNumber"
+                      },
+                      "parameters": [
+                        "clamp(Time / StartEasingDuration, 0, 1)"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "expressionType": {
+                "type": "expression"
+              },
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "ShakeObject3D::ShakeModel3D",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "Return the easing factor according to stop properties.",
+              "fullName": "Stop easing factor",
+              "functionType": "ExpressionAndCondition",
+              "name": "StopEasingFactor",
+              "private": true,
+              "sentence": "stop easing factor",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [
+                    {
+                      "type": {
+                        "value": "ShakeObject3D::ShakeModel3D::PropertyStopEasingDuration"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "<=",
+                        "0"
+                      ]
+                    }
+                  ],
+                  "actions": [],
+                  "events": [
+                    {
+                      "type": "BuiltinCommonInstructions::Standard",
+                      "conditions": [
+                        {
+                          "type": {
+                            "value": "ShakeObject3D::ShakeModel3D::PropertyTime"
+                          },
+                          "parameters": [
+                            "Object",
+                            "Behavior",
+                            "<",
+                            "Duration"
+                          ]
+                        }
+                      ],
+                      "actions": [
+                        {
+                          "type": {
+                            "value": "SetReturnNumber"
+                          },
+                          "parameters": [
+                            "1"
+                          ]
+                        }
+                      ]
+                    },
+                    {
+                      "type": "BuiltinCommonInstructions::Standard",
+                      "conditions": [
+                        {
+                          "type": {
+                            "value": "ShakeObject3D::ShakeModel3D::PropertyTime"
+                          },
+                          "parameters": [
+                            "Object",
+                            "Behavior",
+                            ">=",
+                            "Duration"
+                          ]
+                        }
+                      ],
+                      "actions": [
+                        {
+                          "type": {
+                            "value": "SetReturnNumber"
+                          },
+                          "parameters": [
+                            "0"
+                          ]
+                        }
+                      ]
+                    }
+                  ]
+                },
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [
+                    {
+                      "type": {
+                        "value": "ShakeObject3D::ShakeModel3D::PropertyStopEasingDuration"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        ">",
+                        "0"
+                      ]
+                    }
+                  ],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SetReturnNumber"
+                      },
+                      "parameters": [
+                        "clamp((Duration - Time) / StopEasingDuration, 0, 1)"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "expressionType": {
+                "type": "expression"
+              },
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "ShakeObject3D::ShakeModel3D",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            }
+          ],
+          "propertyDescriptors": [
+            {
+              "value": "0",
+              "type": "Number",
+              "unit": "Pixel",
+              "label": "Translation amplitude on X axis",
+              "description": "",
+              "group": "",
+              "extraInformation": [],
+              "name": "TranslationAmplitudeX"
+            },
+            {
+              "value": "0",
+              "type": "Number",
+              "unit": "Pixel",
+              "label": "Translation amplitude on Y axis",
+              "description": "",
+              "group": "",
+              "extraInformation": [],
+              "name": "TranslationAmplitudeY"
+            },
+            {
+              "value": "0",
+              "type": "Number",
+              "unit": "Pixel",
+              "label": "Translation amplitude on Z axis",
+              "description": "",
+              "group": "",
+              "extraInformation": [],
+              "name": "TranslationAmplitudeZ"
+            },
+            {
+              "value": "5",
+              "type": "Number",
+              "unit": "DegreeAngle",
+              "label": "Rotation amplitude around X axis",
+              "description": "",
+              "group": "",
+              "extraInformation": [],
+              "name": "RotationAmplitudeX"
+            },
+            {
+              "value": "5",
+              "type": "Number",
+              "unit": "DegreeAngle",
+              "label": "Rotation amplitude around Y axis",
+              "description": "",
+              "group": "",
+              "extraInformation": [],
+              "name": "RotationAmplitudeY"
+            },
+            {
+              "value": "5",
+              "type": "Number",
+              "unit": "DegreeAngle",
+              "label": "Rotation amplitude around Z axis",
+              "description": "",
+              "group": "",
+              "extraInformation": [],
+              "name": "RotationAmplitudeZ"
+            },
+            {
+              "value": "",
+              "type": "Number",
+              "label": "",
+              "description": "",
+              "group": "",
+              "extraInformation": [],
+              "hidden": true,
+              "name": "Time"
+            },
+            {
+              "value": "",
+              "type": "Number",
+              "label": "",
+              "description": "",
+              "group": "",
+              "extraInformation": [],
+              "hidden": true,
+              "name": "Duration"
+            },
+            {
+              "value": "",
+              "type": "Number",
+              "label": "",
+              "description": "",
+              "group": "",
+              "extraInformation": [],
+              "hidden": true,
+              "name": "StartEasingDuration"
+            },
+            {
+              "value": "",
+              "type": "Number",
+              "label": "",
+              "description": "",
+              "group": "",
+              "extraInformation": [],
+              "hidden": true,
+              "name": "StopEasingDuration"
+            },
+            {
+              "value": "",
+              "type": "Number",
+              "label": "Frequency",
+              "description": "",
+              "group": "",
+              "extraInformation": [],
+              "name": "Frequency"
+            },
+            {
+              "value": "",
+              "type": "Number",
+              "label": "",
+              "description": "",
+              "group": "",
+              "extraInformation": [],
+              "hidden": true,
+              "name": "DeltaX"
+            },
+            {
+              "value": "",
+              "type": "Number",
+              "label": "",
+              "description": "",
+              "group": "",
+              "extraInformation": [],
+              "hidden": true,
+              "name": "DeltaY"
+            },
+            {
+              "value": "",
+              "type": "Number",
+              "label": "",
+              "description": "",
+              "group": "",
+              "extraInformation": [],
+              "hidden": true,
+              "name": "DeltaZ"
+            },
+            {
+              "value": "",
+              "type": "Number",
+              "label": "",
+              "description": "",
+              "group": "",
+              "extraInformation": [],
+              "hidden": true,
+              "name": "DeltaAngleX"
+            },
+            {
+              "value": "",
+              "type": "Number",
+              "label": "",
+              "description": "",
+              "group": "",
+              "extraInformation": [],
+              "hidden": true,
+              "name": "DeltaAngleY"
+            },
+            {
+              "value": "",
+              "type": "Number",
+              "label": "",
+              "description": "",
+              "group": "",
+              "extraInformation": [],
+              "hidden": true,
+              "name": "DeltaAngleZ"
+            },
+            {
+              "value": "",
+              "type": "Number",
+              "label": "",
+              "description": "",
+              "group": "",
+              "extraInformation": [],
+              "hidden": true,
+              "name": "NoiseTime"
+            },
+            {
+              "value": "",
+              "type": "Behavior",
+              "label": "3D capability",
+              "description": "",
+              "group": "",
+              "extraInformation": [
+                "Scene3D::Base3DBehavior"
+              ],
+              "name": "Object3D"
+            },
+            {
+              "value": "",
+              "type": "Boolean",
+              "label": "Start to shake at the object creation",
+              "description": "",
+              "group": "",
+              "extraInformation": [],
+              "name": "IsStartingAtCreation"
+            }
+          ],
+          "sharedPropertyDescriptors": [
+            {
+              "value": "",
+              "type": "Number",
+              "label": "",
+              "description": "",
+              "group": "",
+              "extraInformation": [],
+              "hidden": true,
+              "name": "EasingFactor"
+            }
+          ]
+        }
+      ],
+      "eventsBasedObjects": []
+    },
+    {
+      "author": "",
+      "category": "Camera",
+      "extensionNamespace": "",
+      "fullName": "Third person camera",
+      "helpPath": "",
+      "iconUrl": "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4NCjwhLS0gR2VuZXJhdG9yOiBBZG9iZSBJbGx1c3RyYXRvciAyMy4wLjMsIFNWRyBFeHBvcnQgUGx1Zy1JbiAuIFNWRyBWZXJzaW9uOiA2LjAwIEJ1aWxkIDApICAtLT4NCjxzdmcgdmVyc2lvbj0iMS4xIiBpZD0iSWNvbnMiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4Ig0KCSB2aWV3Qm94PSIwIDAgMzIgMzIiIHN0eWxlPSJlbmFibGUtYmFja2dyb3VuZDpuZXcgMCAwIDMyIDMyOyIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+DQo8c3R5bGUgdHlwZT0idGV4dC9jc3MiPg0KCS5zdDB7ZmlsbDpub25lO3N0cm9rZTojMDAwMDAwO3N0cm9rZS13aWR0aDoyO3N0cm9rZS1saW5lY2FwOnJvdW5kO3N0cm9rZS1saW5lam9pbjpyb3VuZDtzdHJva2UtbWl0ZXJsaW1pdDoxMDt9DQo8L3N0eWxlPg0KPHBvbHlnb24gY2xhc3M9InN0MCIgcG9pbnRzPSI3LDEwIDEsMTMgNywxNiAxMywxMyAiLz4NCjxwb2x5bGluZSBjbGFzcz0ic3QwIiBwb2ludHM9IjEsMTMgMSwyMCA3LDIzIDEzLDIwIDEzLDEzICIvPg0KPGxpbmUgY2xhc3M9InN0MCIgeDE9IjciIHkxPSIxNiIgeDI9IjciIHkyPSIyMyIvPg0KPGxpbmUgY2xhc3M9InN0MCIgeDE9IjMxIiB5MT0iMTYiIHgyPSIyMSIgeTI9IjgiLz4NCjxsaW5lIGNsYXNzPSJzdDAiIHgxPSIyMSIgeTE9IjI0IiB4Mj0iMzEiIHkyPSIxNiIvPg0KPHBhdGggY2xhc3M9InN0MCIgZD0iTTIyLjcsMjIuNkMyMCwyMS43LDE4LDE5LjEsMTgsMTZjMC0zLjEsMi01LjcsNC43LTYuNiIvPg0KPHBhdGggY2xhc3M9InN0MCIgZD0iTTE5LjgsMTEuM2MxLjQsMS4xLDIuMiwyLjgsMi4yLDQuN2MwLDEuOS0wLjksMy42LTIuMiw0LjciLz4NCjwvc3ZnPg0K",
+      "name": "ThirdPersonCamera",
+      "previewIconUrl": "https://asset-resources.gdevelop.io/public-resources/Icons/Line Hero Pack/Master/SVG/Virtual Reality/94e95d2c318e1f3dc7151a351024e13c574e1e44669c6696aa107d60230073f6_Virtual Reality_3d_vision_eye_vr.svg",
+      "shortDescription": "Move the camera  to look at an object from a given distance.",
+      "version": "1.5.0",
+      "description": [
+        "Move the camera  to look at an object from a given distance with a rotation and an elevation angles.",
+        "",
+        "It can be useful for:",
+        "- Third person camera",
+        "- Isometric-like point of view",
+        ""
+      ],
+      "origin": {
+        "identifier": "ThirdPersonCamera",
+        "name": "gdevelop-extension-store"
+      },
+      "tags": [
+        "3d",
+        "camera"
+      ],
+      "authorIds": [
+        "IWykYNRvhCZBN3vEgKEbBPOR3Oc2"
+      ],
+      "dependencies": [],
+      "globalVariables": [],
+      "sceneVariables": [],
+      "eventsFunctions": [
+        {
+          "description": "Move the camera to look at a position from a distance.",
+          "fullName": "Look at a position from a distance (deprecated)",
+          "functionType": "Action",
+          "name": "LookFromDistanceAtPosition",
+          "private": true,
+          "sentence": "Move the camera of _PARAM6_ to look at _PARAM1_; _PARAM2_ from _PARAM3_ pixels with a rotation of _PARAM4_° and an elevation of _PARAM5_°",
+          "events": [
+            {
+              "type": "BuiltinCommonInstructions::Standard",
+              "conditions": [],
+              "actions": [
+                {
+                  "type": {
+                    "value": "SetCameraCenterX"
+                  },
+                  "parameters": [
+                    "",
+                    "=",
+                    "PositionX + Distance * cos(ToRad(RotationAngle + 90)) * cos(ToRad(ElevationAngle))",
+                    "Layer",
+                    ""
+                  ]
+                },
+                {
+                  "type": {
+                    "value": "SetCameraCenterY"
+                  },
+                  "parameters": [
+                    "",
+                    "=",
+                    "PositionY + Distance * sin(ToRad(RotationAngle + 90)) * cos(ToRad(ElevationAngle))",
+                    "Layer",
+                    ""
+                  ]
+                },
+                {
+                  "type": {
+                    "value": "Scene3D::SetCameraZ"
+                  },
+                  "parameters": [
+                    "",
+                    "=",
+                    "Distance * sin(ToRad(ElevationAngle))",
+                    "Layer",
+                    ""
+                  ]
+                },
+                {
+                  "type": {
+                    "value": "Scene3D::TurnCameraTowardPosition"
+                  },
+                  "parameters": [
+                    "",
+                    "PositionX",
+                    "PositionY",
+                    "0",
+                    "Layer",
+                    "",
+                    ""
+                  ]
+                }
+              ]
+            }
+          ],
+          "parameters": [
+            {
+              "description": "Position on X axis",
+              "name": "PositionX",
+              "type": "expression"
+            },
+            {
+              "description": "Position on Y axis",
+              "name": "PositionY",
+              "type": "expression"
+            },
+            {
+              "description": "Distance",
+              "name": "Distance",
+              "type": "expression"
+            },
+            {
+              "description": "Rotation angle (around Z axis)",
+              "name": "RotationAngle",
+              "type": "expression"
+            },
+            {
+              "description": "Elevation angle (around Y axis)",
+              "name": "ElevationAngle",
+              "type": "expression"
+            },
+            {
+              "description": "Layer",
+              "name": "Layer",
+              "type": "layer"
+            }
+          ],
+          "objectGroups": []
+        },
+        {
+          "description": "Move the camera to look at an object from a distance.",
+          "fullName": "Look at an object from a distance (deprecated)",
+          "functionType": "Action",
+          "name": "LookFromDistanceAtObject",
+          "private": true,
+          "sentence": "Move the camera of _PARAM5_ to look at _PARAM1_ from _PARAM2_ pixels with a rotation of _PARAM3_° and an elevation of _PARAM4_°",
+          "events": [
+            {
+              "type": "BuiltinCommonInstructions::Standard",
+              "conditions": [],
+              "actions": [
+                {
+                  "type": {
+                    "value": "ThirdPersonCamera::LookFromDistanceAtPosition"
+                  },
+                  "parameters": [
+                    "",
+                    "Object.CenterX()",
+                    "Object.CenterY()",
+                    "Distance",
+                    "RotationAngle",
+                    "ElevationAngle",
+                    "Layer",
+                    ""
+                  ]
+                }
+              ]
+            }
+          ],
+          "parameters": [
+            {
+              "description": "Object",
+              "name": "Object",
+              "type": "objectList"
+            },
+            {
+              "description": "Distance",
+              "name": "Distance",
+              "type": "expression"
+            },
+            {
+              "description": "Rotation angle (around Z axis)",
+              "name": "RotationAngle",
+              "type": "expression"
+            },
+            {
+              "description": "Elevation angle (around Y axis)",
+              "name": "ElevationAngle",
+              "type": "expression"
+            },
+            {
+              "description": "Layer",
+              "name": "Layer",
+              "type": "layer"
+            }
+          ],
+          "objectGroups": []
+        },
+        {
+          "description": "Move the camera to look at a position from a distance.",
+          "fullName": "Look at a position from a distance",
+          "functionType": "Action",
+          "name": "LookFromDistanceAtPosition3D",
+          "sentence": "Move the camera of _PARAM7_ to look at _PARAM1_; _PARAM2_; _PARAM3_ from _PARAM4_ pixels with a rotation of _PARAM5_° and an elevation of _PARAM6_°",
+          "events": [
+            {
+              "type": "BuiltinCommonInstructions::Standard",
+              "conditions": [],
+              "actions": [
+                {
+                  "type": {
+                    "value": "SetCameraCenterX"
+                  },
+                  "parameters": [
+                    "",
+                    "=",
+                    "PositionX + Distance * cos(ToRad(RotationAngle + 90)) * cos(ToRad(ElevationAngle))",
+                    "Layer",
+                    ""
+                  ]
+                },
+                {
+                  "type": {
+                    "value": "SetCameraCenterY"
+                  },
+                  "parameters": [
+                    "",
+                    "=",
+                    "PositionY + Distance * sin(ToRad(RotationAngle + 90)) * cos(ToRad(ElevationAngle))",
+                    "Layer",
+                    ""
+                  ]
+                },
+                {
+                  "type": {
+                    "value": "Scene3D::SetCameraZ"
+                  },
+                  "parameters": [
+                    "",
+                    "=",
+                    "PositionZ + Distance * sin(ToRad(ElevationAngle))",
+                    "Layer",
+                    ""
+                  ]
+                },
+                {
+                  "type": {
+                    "value": "Scene3D::SetCameraRotationX"
+                  },
+                  "parameters": [
+                    "",
+                    "=",
+                    "90 - ElevationAngle",
+                    "Layer",
+                    ""
+                  ]
+                },
+                {
+                  "type": {
+                    "value": "Scene3D::SetCameraRotationY"
+                  },
+                  "parameters": [
+                    "",
+                    "=",
+                    "0",
+                    "Layer",
+                    ""
+                  ]
+                },
+                {
+                  "type": {
+                    "value": "SetCameraAngle"
+                  },
+                  "parameters": [
+                    "",
+                    "=",
+                    "RotationAngle",
+                    "Layer",
+                    ""
+                  ]
+                }
+              ]
+            }
+          ],
+          "parameters": [
+            {
+              "description": "Position on X axis",
+              "name": "PositionX",
+              "type": "expression"
+            },
+            {
+              "description": "Position on Y axis",
+              "name": "PositionY",
+              "type": "expression"
+            },
+            {
+              "description": "Position on Z axis",
+              "name": "PositionZ",
+              "type": "expression"
+            },
+            {
+              "description": "Distance",
+              "name": "Distance",
+              "type": "expression"
+            },
+            {
+              "description": "Rotation angle (around Z axis)",
+              "name": "RotationAngle",
+              "type": "expression"
+            },
+            {
+              "description": "Elevation angle (around Y axis)",
+              "name": "ElevationAngle",
+              "type": "expression"
+            },
+            {
+              "description": "Layer",
+              "name": "Layer",
+              "type": "layer"
+            }
+          ],
+          "objectGroups": []
+        },
+        {
+          "description": "Move the camera to look at an object from a distance.",
+          "fullName": "Look at an object from a distance",
+          "functionType": "Action",
+          "group": "Layers and cameras",
+          "name": "LookFromDistanceAtObject3D",
+          "sentence": "Move the camera of _PARAM6_ to look at _PARAM1_ from _PARAM3_ pixels with a rotation of _PARAM4_° and an elevation of _PARAM5_°",
+          "events": [
+            {
+              "type": "BuiltinCommonInstructions::Standard",
+              "conditions": [],
+              "actions": [
+                {
+                  "type": {
+                    "value": "ThirdPersonCamera::LookFromDistanceAtPosition3D"
+                  },
+                  "parameters": [
+                    "",
+                    "Object.CenterX()",
+                    "Object.CenterY()",
+                    "Object.Object3D::CenterZ()",
+                    "Distance",
+                    "RotationAngle",
+                    "ElevationAngle",
+                    "Layer",
+                    ""
+                  ]
+                }
+              ]
+            }
+          ],
+          "parameters": [
+            {
+              "description": "Object",
+              "name": "Object",
+              "type": "objectList"
+            },
+            {
+              "description": "3D capability",
+              "name": "Object3D",
+              "supplementaryInformation": "Scene3D::Base3DBehavior",
+              "type": "behavior"
+            },
+            {
+              "description": "Distance",
+              "name": "Distance",
+              "type": "expression"
+            },
+            {
+              "description": "Rotation angle (around Z axis)",
+              "name": "RotationAngle",
+              "type": "expression"
+            },
+            {
+              "description": "Elevation angle (around Y axis)",
+              "name": "ElevationAngle",
+              "type": "expression"
+            },
+            {
+              "description": "Layer",
+              "name": "Layer",
+              "type": "layer"
+            }
+          ],
+          "objectGroups": []
+        },
+        {
+          "fullName": "",
+          "functionType": "Expression",
+          "name": "RotatedX",
+          "private": true,
+          "sentence": "",
+          "events": [
+            {
+              "type": "BuiltinCommonInstructions::Standard",
+              "conditions": [],
+              "actions": [
+                {
+                  "type": {
+                    "value": "SetNumberVariable"
+                  },
+                  "parameters": [
+                    "Cos",
+                    "=",
+                    "cos(ToRad(RotationAngle))"
+                  ]
+                },
+                {
+                  "type": {
+                    "value": "SetNumberVariable"
+                  },
+                  "parameters": [
+                    "Sin",
+                    "=",
+                    "sin(ToRad(RotationAngle))"
+                  ]
+                },
+                {
+                  "type": {
+                    "value": "SetReturnNumber"
+                  },
+                  "parameters": [
+                    "Cos * X - Sin * Y"
+                  ]
+                }
+              ],
+              "variables": [
+                {
+                  "folded": true,
+                  "name": "Cos",
+                  "type": "number",
+                  "value": 0
+                },
+                {
+                  "folded": true,
+                  "name": "Sin",
+                  "type": "number",
+                  "value": 0
+                }
+              ]
+            }
+          ],
+          "expressionType": {
+            "type": "expression"
+          },
+          "parameters": [
+            {
+              "description": "Rotation angle",
+              "name": "RotationAngle",
+              "supplementaryInformation": "Tank::Tank",
+              "type": "expression"
+            },
+            {
+              "description": "",
+              "name": "X",
+              "type": "expression"
+            },
+            {
+              "description": "",
+              "name": "Y",
+              "type": "expression"
+            }
+          ],
+          "objectGroups": []
+        },
+        {
+          "fullName": "",
+          "functionType": "Expression",
+          "name": "RotatedY",
+          "private": true,
+          "sentence": "",
+          "events": [
+            {
+              "type": "BuiltinCommonInstructions::Standard",
+              "conditions": [],
+              "actions": [
+                {
+                  "type": {
+                    "value": "SetNumberVariable"
+                  },
+                  "parameters": [
+                    "Cos",
+                    "=",
+                    "cos(ToRad(RotationAngle))"
+                  ]
+                },
+                {
+                  "type": {
+                    "value": "SetNumberVariable"
+                  },
+                  "parameters": [
+                    "Sin",
+                    "=",
+                    "sin(ToRad(RotationAngle))"
+                  ]
+                },
+                {
+                  "type": {
+                    "value": "SetReturnNumber"
+                  },
+                  "parameters": [
+                    "Sin * X + Cos * Y"
+                  ]
+                }
+              ],
+              "variables": [
+                {
+                  "folded": true,
+                  "name": "Cos",
+                  "type": "number",
+                  "value": 0
+                },
+                {
+                  "folded": true,
+                  "name": "Sin",
+                  "type": "number",
+                  "value": 0
+                }
+              ]
+            }
+          ],
+          "expressionType": {
+            "type": "expression"
+          },
+          "parameters": [
+            {
+              "description": "Rotation angle",
+              "name": "RotationAngle",
+              "supplementaryInformation": "Tank::Tank",
+              "type": "expression"
+            },
+            {
+              "description": "",
+              "name": "X",
+              "type": "expression"
+            },
+            {
+              "description": "",
+              "name": "Y",
+              "type": "expression"
+            }
+          ],
+          "objectGroups": []
+        }
+      ],
+      "eventsBasedBehaviors": [
+        {
+          "description": "Smoothly follow an object at a distance.",
+          "fullName": "Third person camera",
+          "name": "ThirdPersonCamera",
+          "objectType": "",
+          "eventsFunctions": [
+            {
+              "fullName": "",
+              "functionType": "Action",
+              "name": "onCreated",
+              "sentence": "",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Comment",
+                  "color": {
+                    "b": 109,
+                    "g": 230,
+                    "r": 255,
+                    "textB": 0,
+                    "textG": 0,
+                    "textR": 0
+                  },
+                  "comment": "Update private properties through setters to check their values and initialize state."
+                },
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "ThirdPersonCamera::ThirdPersonCamera::SetRotationHalfwayDuration"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "=",
+                        "RotationHalfwayDuration",
+                        ""
+                      ]
+                    },
+                    {
+                      "type": {
+                        "value": "ThirdPersonCamera::ThirdPersonCamera::SetTranslationZHalfwayDuration"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "=",
+                        "TranslationZHalfwayDuration",
+                        ""
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "ThirdPersonCamera::ThirdPersonCamera",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "fullName": "",
+              "functionType": "Action",
+              "name": "doStepPreEvents",
+              "sentence": "",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [
+                    {
+                      "type": {
+                        "value": "ThirdPersonCamera::ThirdPersonCamera::PropertyHasJustBeenCreated"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior"
+                      ]
+                    }
+                  ],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "ThirdPersonCamera::ThirdPersonCamera::SetPropertyHasJustBeenCreated"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        ""
+                      ]
+                    },
+                    {
+                      "type": {
+                        "value": "SetCameraAngle"
+                      },
+                      "parameters": [
+                        "",
+                        "=",
+                        "Object.Angle() + 90",
+                        "Object.Layer()",
+                        "0"
+                      ]
+                    },
+                    {
+                      "type": {
+                        "value": "ThirdPersonCamera::ThirdPersonCamera::SetPropertyCameraZ"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "=",
+                        "Object.Object3D::CenterZ()"
+                      ]
+                    }
+                  ]
+                },
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [
+                    {
+                      "type": {
+                        "inverted": true,
+                        "value": "ThirdPersonCamera::ThirdPersonCamera::PropertyIsCalledManually"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior"
+                      ]
+                    }
+                  ],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "ThirdPersonCamera::ThirdPersonCamera::DoMoveCameraCloser"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        ""
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "ThirdPersonCamera::ThirdPersonCamera",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "Move the camera closer to the object. This action must be called after the object has moved for the frame.",
+              "fullName": "Move the camera closer",
+              "functionType": "Action",
+              "name": "MoveCameraCloser",
+              "sentence": "Move the camera closer to _PARAM0_",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Comment",
+                  "color": {
+                    "b": 109,
+                    "g": 230,
+                    "r": 255,
+                    "textB": 0,
+                    "textG": 0,
+                    "textR": 0
+                  },
+                  "comment": "The camera following is called with an action, the call from doStepPreEvents must be disabled to avoid to do it twice."
+                },
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [
+                    {
+                      "type": {
+                        "value": "BehaviorActivated"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior"
+                      ]
+                    }
+                  ],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "ThirdPersonCamera::ThirdPersonCamera::SetPropertyIsCalledManually"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        ""
+                      ]
+                    },
+                    {
+                      "type": {
+                        "value": "ThirdPersonCamera::ThirdPersonCamera::DoMoveCameraCloser"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        ""
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "ThirdPersonCamera::ThirdPersonCamera",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "Move the camera closer to the object.",
+              "fullName": "Do move the camera closer",
+              "functionType": "Action",
+              "name": "DoMoveCameraCloser",
+              "private": true,
+              "sentence": "Do move the camera closer _PARAM0_",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Comment",
+                  "color": {
+                    "b": 109,
+                    "g": 230,
+                    "r": 255,
+                    "textB": 0,
+                    "textG": 0,
+                    "textR": 0
+                  },
+                  "comment": "At each frame, the camera must catchup the target by a given ratio (speed)\ncameraX(t) - targetX = (cameraX(t - 1) - targetX) * (1 - speed)\n\nThe frame rate must not impact on the catch-up speed, we don't want a speed in ratio per frame but a speed ratio per second, like this:\ncameraX(t) - targetX = (cameraX(t - 1s) - targetX) * (1 - speed)\n\nOk, but we still need to process each frame, we can use a exponent for this:\ncameraX(t) - targetX = (cameraX(t - timeDelta) - targetX) * (1 - speed)^timeDelta\ncameraX(t) = targetX + (cameraX(t - timeDelta) - targetX) * exp(timeDelta * ln((1 - speed)))\n\npow is probably more efficient than precalculated log if the speed is changed continuously but this might be rare enough."
+                },
+                {
+                  "colorB": 228,
+                  "colorG": 176,
+                  "colorR": 74,
+                  "creationTime": 0,
+                  "name": "Z translation",
+                  "source": "",
+                  "type": "BuiltinCommonInstructions::Group",
+                  "events": [
+                    {
+                      "type": "BuiltinCommonInstructions::Standard",
+                      "conditions": [],
+                      "actions": [],
+                      "events": [
+                        {
+                          "type": "BuiltinCommonInstructions::Standard",
+                          "conditions": [
+                            {
+                              "type": {
+                                "value": "ThirdPersonCamera::ThirdPersonCamera::PropertyCameraZ"
+                              },
+                              "parameters": [
+                                "Object",
+                                "Behavior",
+                                "<",
+                                "Object.Behavior::FreeAreaZMin()"
+                              ]
+                            }
+                          ],
+                          "actions": [
+                            {
+                              "type": {
+                                "value": "ThirdPersonCamera::ThirdPersonCamera::SetPropertyCameraZ"
+                              },
+                              "parameters": [
+                                "Object",
+                                "Behavior",
+                                "=",
+                                "Object.Behavior::FreeAreaZMin() + (CameraZ - Object.Behavior::FreeAreaZMin()) * exp(TimeDelta() * TranslationZLogSpeed)"
+                              ]
+                            }
+                          ]
+                        },
+                        {
+                          "type": "BuiltinCommonInstructions::Standard",
+                          "conditions": [
+                            {
+                              "type": {
+                                "value": "ThirdPersonCamera::ThirdPersonCamera::PropertyCameraZ"
+                              },
+                              "parameters": [
+                                "Object",
+                                "Behavior",
+                                ">",
+                                "Object.Behavior::FreeAreaZMax()"
+                              ]
+                            }
+                          ],
+                          "actions": [
+                            {
+                              "type": {
+                                "value": "ThirdPersonCamera::ThirdPersonCamera::SetPropertyCameraZ"
+                              },
+                              "parameters": [
+                                "Object",
+                                "Behavior",
+                                "=",
+                                "Object.Behavior::FreeAreaZMax() + (CameraZ - Object.Behavior::FreeAreaZMax()) * exp(TimeDelta() * TranslationZLogSpeed)"
+                              ]
+                            }
+                          ]
+                        }
+                      ]
+                    }
+                  ],
+                  "parameters": []
+                },
+                {
+                  "colorB": 228,
+                  "colorG": 176,
+                  "colorR": 74,
+                  "creationTime": 0,
+                  "name": "Z rotation",
+                  "source": "",
+                  "type": "BuiltinCommonInstructions::Group",
+                  "events": [
+                    {
+                      "type": "BuiltinCommonInstructions::Standard",
+                      "conditions": [
+                        {
+                          "type": {
+                            "value": "ThirdPersonCamera::ThirdPersonCamera::PropertyIsRotatingWithObject"
+                          },
+                          "parameters": [
+                            "Object",
+                            "Behavior"
+                          ]
+                        }
+                      ],
+                      "actions": [
+                        {
+                          "type": {
+                            "value": "ThirdPersonCamera::ThirdPersonCamera::SetPropertyTargetedRotationAngle"
+                          },
+                          "parameters": [
+                            "Object",
+                            "Behavior",
+                            "=",
+                            "Object.Angle()"
+                          ]
+                        }
+                      ]
+                    },
+                    {
+                      "type": "BuiltinCommonInstructions::Standard",
+                      "conditions": [],
+                      "actions": [
+                        {
+                          "type": {
+                            "value": "SetNumberVariable"
+                          },
+                          "parameters": [
+                            "CameraAngle",
+                            "=",
+                            "TargetedRotationAngle + 90 + RotationAngleOffset + AngleDifference(CameraAngle(Object.Layer()), TargetedRotationAngle + 90 + RotationAngleOffset) * exp(TimeDelta() * RotationLogSpeed)"
+                          ]
+                        },
+                        {
+                          "type": {
+                            "value": "ThirdPersonCamera::LookFromDistanceAtPosition3D"
+                          },
+                          "parameters": [
+                            "",
+                            "Object.CenterX() + ThirdPersonCamera::RotatedX(CameraAngle, OffsetX, -OffsetY)",
+                            "Object.CenterY() + ThirdPersonCamera::RotatedY(CameraAngle, OffsetX, -OffsetY)",
+                            "CameraZ + OffsetZ",
+                            "Distance",
+                            "CameraAngle",
+                            "Object.Object3D::RotationY() + ElevationAngleOffset",
+                            "",
+                            ""
+                          ]
+                        }
+                      ],
+                      "variables": [
+                        {
+                          "folded": true,
+                          "name": "CameraAngle",
+                          "type": "number",
+                          "value": 0
+                        }
+                      ]
+                    }
+                  ],
+                  "parameters": []
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "ThirdPersonCamera::ThirdPersonCamera",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "Rotate the camera all the way to the targeted angle.",
+              "fullName": "Rotate the camera all the way",
+              "functionType": "Action",
+              "name": "JumpToTargetedRotation",
+              "private": true,
+              "sentence": "Rotate the camera all the way to the targeted angle of _PARAM0_",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [
+                    {
+                      "type": {
+                        "value": "ThirdPersonCamera::ThirdPersonCamera::PropertyIsRotatingWithObject"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior"
+                      ]
+                    }
+                  ],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "ThirdPersonCamera::ThirdPersonCamera::SetPropertyTargetedRotationAngle"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "=",
+                        "Object.Angle()"
+                      ]
+                    }
+                  ]
+                },
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SetNumberVariable"
+                      },
+                      "parameters": [
+                        "CameraAngle",
+                        "=",
+                        "TargetedRotationAngle + 90 + RotationAngleOffset"
+                      ]
+                    },
+                    {
+                      "type": {
+                        "value": "ThirdPersonCamera::LookFromDistanceAtPosition3D"
+                      },
+                      "parameters": [
+                        "",
+                        "Object.CenterX() + ThirdPersonCamera::RotatedX(CameraAngle, OffsetX, -OffsetY)",
+                        "Object.CenterY() + ThirdPersonCamera::RotatedY(CameraAngle, OffsetX, -OffsetY)",
+                        "CameraZ + OffsetZ",
+                        "Distance",
+                        "CameraAngle",
+                        "Object.Object3D::RotationY() + ElevationAngleOffset",
+                        "",
+                        ""
+                      ]
+                    }
+                  ],
+                  "variables": [
+                    {
+                      "folded": true,
+                      "name": "CameraAngle",
+                      "type": "number",
+                      "value": 0
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "ThirdPersonCamera::ThirdPersonCamera",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "the camera rotation.",
+              "fullName": "Camera rotation",
+              "functionType": "ExpressionAndCondition",
+              "name": "RotationAngle",
+              "private": true,
+              "sentence": "the camera rotation",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SetReturnNumber"
+                      },
+                      "parameters": [
+                        "CameraAngle(Object.Layer())"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "expressionType": {
+                "type": "expression"
+              },
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "ThirdPersonCamera::ThirdPersonCamera",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "Rotate the camera all the way to the targeted angle.",
+              "fullName": "Rotate the camera all the way",
+              "functionType": "ActionWithOperator",
+              "getterName": "RotationAngle",
+              "name": "SetCameraRotation",
+              "sentence": "Rotate the camera all the way to the targeted angle of _PARAM0_",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "ThirdPersonCamera::LookFromDistanceAtPosition3D"
+                      },
+                      "parameters": [
+                        "",
+                        "Object.CenterX() + ThirdPersonCamera::RotatedX(Value, OffsetX, -OffsetY)",
+                        "Object.CenterY() + ThirdPersonCamera::RotatedY(Value, OffsetX, -OffsetY)",
+                        "CameraZ + OffsetZ",
+                        "Distance",
+                        "Value",
+                        "Object.Object3D::RotationY() + ElevationAngleOffset",
+                        "",
+                        ""
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "ThirdPersonCamera::ThirdPersonCamera",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "the halfway time for rotation of the object.",
+              "fullName": "Halfway time for rotation",
+              "functionType": "ExpressionAndCondition",
+              "group": "Third person camera configuration",
+              "name": "RotationHalfwayDuration",
+              "sentence": "the halfway time for rotation",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SetReturnNumber"
+                      },
+                      "parameters": [
+                        "RotationHalfwayDuration"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "expressionType": {
+                "type": "expression"
+              },
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "ThirdPersonCamera::ThirdPersonCamera",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "fullName": "",
+              "functionType": "ActionWithOperator",
+              "getterName": "RotationHalfwayDuration",
+              "name": "SetRotationHalfwayDuration",
+              "sentence": "",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Comment",
+                  "color": {
+                    "b": 109,
+                    "g": 230,
+                    "r": 255,
+                    "textB": 0,
+                    "textG": 0,
+                    "textR": 0
+                  },
+                  "comment": "ln((1 - speed)) = ln(1 / 2) / halfwatTime"
+                },
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "ThirdPersonCamera::ThirdPersonCamera::SetPropertyRotationHalfwayDuration"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "=",
+                        "Value"
+                      ]
+                    },
+                    {
+                      "type": {
+                        "value": "ThirdPersonCamera::ThirdPersonCamera::SetPropertyRotationLogSpeed"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "=",
+                        "log(0.5) / Value"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "ThirdPersonCamera::ThirdPersonCamera",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "the halfway time on Z axis of the object.",
+              "fullName": "Halfway time on Z axis",
+              "functionType": "ExpressionAndCondition",
+              "group": "Third person camera configuration",
+              "name": "TranslationZHalfwayDuration",
+              "sentence": "the halfway time on Z axis",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SetReturnNumber"
+                      },
+                      "parameters": [
+                        "TranslationZHalfwayDuration"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "expressionType": {
+                "type": "expression"
+              },
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "ThirdPersonCamera::ThirdPersonCamera",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "fullName": "",
+              "functionType": "ActionWithOperator",
+              "getterName": "TranslationZHalfwayDuration",
+              "name": "SetTranslationZHalfwayDuration",
+              "sentence": "",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Comment",
+                  "color": {
+                    "b": 109,
+                    "g": 230,
+                    "r": 255,
+                    "textB": 0,
+                    "textG": 0,
+                    "textR": 0
+                  },
+                  "comment": "ln((1 - speed)) = ln(1 / 2) / halfwatTime"
+                },
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "ThirdPersonCamera::ThirdPersonCamera::SetPropertyTranslationZHalfwayDuration"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "=",
+                        "Value"
+                      ]
+                    },
+                    {
+                      "type": {
+                        "value": "ThirdPersonCamera::ThirdPersonCamera::SetPropertyTranslationZLogSpeed"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "=",
+                        "log(0.5) / Value"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "ThirdPersonCamera::ThirdPersonCamera",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "Return follow free area bottom border Z.",
+              "fullName": "Free area Z min",
+              "functionType": "Expression",
+              "group": "Private",
+              "name": "FreeAreaZMin",
+              "private": true,
+              "sentence": "",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SetReturnNumber"
+                      },
+                      "parameters": [
+                        "Object.Object3D::CenterZ() + OffsetZ - FollowFreeAreaZMin"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "expressionType": {
+                "type": "expression"
+              },
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "ThirdPersonCamera::ThirdPersonCamera",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "Return follow free area top border Z.",
+              "fullName": "Free area Z max",
+              "functionType": "Expression",
+              "group": "Private",
+              "name": "FreeAreaZMax",
+              "private": true,
+              "sentence": "",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SetReturnNumber"
+                      },
+                      "parameters": [
+                        "Object.Object3D::CenterZ() + OffsetZ + FollowFreeAreaZMax"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "expressionType": {
+                "type": "expression"
+              },
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "ThirdPersonCamera::ThirdPersonCamera",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "the follow free area top border on Z axis of the object.",
+              "fullName": "Follow free area top border on Z axis",
+              "functionType": "ExpressionAndCondition",
+              "group": "Third person camera configuration",
+              "name": "FollowFreeAreaZMax",
+              "sentence": "the follow free area top border on Z axis",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SetReturnNumber"
+                      },
+                      "parameters": [
+                        "FollowFreeAreaZMax"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "expressionType": {
+                "type": "expression"
+              },
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "ThirdPersonCamera::ThirdPersonCamera",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "fullName": "",
+              "functionType": "ActionWithOperator",
+              "getterName": "FollowFreeAreaZMax",
+              "name": "SetFollowFreeAreaZMax",
+              "sentence": "",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "ThirdPersonCamera::ThirdPersonCamera::SetPropertyFollowFreeAreaZMax"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "=",
+                        "Value"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "ThirdPersonCamera::ThirdPersonCamera",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "the follow free area bottom border on Z axis of the object.",
+              "fullName": "Follow free area bottom border on Z axis",
+              "functionType": "ExpressionAndCondition",
+              "group": "Third person camera configuration",
+              "name": "FollowFreeAreaZMin",
+              "sentence": "the follow free area bottom border on Z axis",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SetReturnNumber"
+                      },
+                      "parameters": [
+                        "FollowFreeAreaZMin"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "expressionType": {
+                "type": "expression"
+              },
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "ThirdPersonCamera::ThirdPersonCamera",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "fullName": "",
+              "functionType": "ActionWithOperator",
+              "getterName": "FollowFreeAreaZMin",
+              "name": "SetFollowFreeAreaZMin",
+              "sentence": "",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "ThirdPersonCamera::ThirdPersonCamera::SetPropertyFollowFreeAreaZMin"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "=",
+                        "Value"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "ThirdPersonCamera::ThirdPersonCamera",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "the camera distance of the object.",
+              "fullName": "Camera distance",
+              "functionType": "ExpressionAndCondition",
+              "group": "Third person camera configuration",
+              "name": "Distance",
+              "sentence": "the camera distance",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SetReturnNumber"
+                      },
+                      "parameters": [
+                        "Distance"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "expressionType": {
+                "type": "expression"
+              },
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "ThirdPersonCamera::ThirdPersonCamera",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "fullName": "",
+              "functionType": "ActionWithOperator",
+              "getterName": "Distance",
+              "name": "SetDistance",
+              "sentence": "",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "ThirdPersonCamera::ThirdPersonCamera::SetPropertyDistance"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "=",
+                        "Value"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "ThirdPersonCamera::ThirdPersonCamera",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "the lateral distance offset of the object.",
+              "fullName": "Lateral distance offset",
+              "functionType": "ExpressionAndCondition",
+              "group": "Third person camera configuration",
+              "name": "OffsetX",
+              "sentence": "the lateral distance offset",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SetReturnNumber"
+                      },
+                      "parameters": [
+                        "OffsetX"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "expressionType": {
+                "type": "expression"
+              },
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "ThirdPersonCamera::ThirdPersonCamera",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "fullName": "",
+              "functionType": "ActionWithOperator",
+              "getterName": "OffsetX",
+              "name": "SetOffsetX",
+              "sentence": "",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "ThirdPersonCamera::ThirdPersonCamera::SetPropertyOffsetX"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "=",
+                        "Value"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "ThirdPersonCamera::ThirdPersonCamera",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "the ahead distance offset of the object.",
+              "fullName": "Ahead distance offset",
+              "functionType": "ExpressionAndCondition",
+              "group": "Third person camera configuration",
+              "name": "OffsetY",
+              "sentence": "the ahead distance offset",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SetReturnNumber"
+                      },
+                      "parameters": [
+                        "OffsetY"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "expressionType": {
+                "type": "expression"
+              },
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "ThirdPersonCamera::ThirdPersonCamera",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "fullName": "",
+              "functionType": "ActionWithOperator",
+              "getterName": "OffsetY",
+              "name": "SetOffsetY",
+              "sentence": "",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "ThirdPersonCamera::ThirdPersonCamera::SetPropertyOffsetY"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "=",
+                        "Value"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "ThirdPersonCamera::ThirdPersonCamera",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "the z offset of the object.",
+              "fullName": "Z offset",
+              "functionType": "ExpressionAndCondition",
+              "group": "Third person camera configuration",
+              "name": "OffsetZ",
+              "sentence": "the z offset",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SetReturnNumber"
+                      },
+                      "parameters": [
+                        "OffsetZ"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "expressionType": {
+                "type": "expression"
+              },
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "ThirdPersonCamera::ThirdPersonCamera",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "fullName": "",
+              "functionType": "ActionWithOperator",
+              "getterName": "OffsetZ",
+              "name": "SetOffsetZ",
+              "sentence": "",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "ThirdPersonCamera::ThirdPersonCamera::SetPropertyOffsetZ"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "=",
+                        "Value"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "ThirdPersonCamera::ThirdPersonCamera",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "the rotation angle offset of the object.",
+              "fullName": "Rotation angle offset",
+              "functionType": "ExpressionAndCondition",
+              "group": "Third person camera configuration",
+              "name": "RotationAngleOffset",
+              "sentence": "the rotation angle offset",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SetReturnNumber"
+                      },
+                      "parameters": [
+                        "RotationAngleOffset"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "expressionType": {
+                "type": "expression"
+              },
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "ThirdPersonCamera::ThirdPersonCamera",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "fullName": "",
+              "functionType": "ActionWithOperator",
+              "getterName": "RotationAngleOffset",
+              "name": "SetRotationAngleOffset",
+              "sentence": "",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "ThirdPersonCamera::ThirdPersonCamera::SetPropertyRotationAngleOffset"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "=",
+                        "Value"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "ThirdPersonCamera::ThirdPersonCamera",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "the elevation angle offset of the object.",
+              "fullName": "Elevation angle offset",
+              "functionType": "ExpressionAndCondition",
+              "group": "Third person camera configuration",
+              "name": "ElevationAngleOffset",
+              "sentence": "the elevation angle offset",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SetReturnNumber"
+                      },
+                      "parameters": [
+                        "ElevationAngleOffset"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "expressionType": {
+                "type": "expression"
+              },
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "ThirdPersonCamera::ThirdPersonCamera",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "fullName": "",
+              "functionType": "ActionWithOperator",
+              "getterName": "ElevationAngleOffset",
+              "name": "SetElevationAngleOffset",
+              "sentence": "",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "ThirdPersonCamera::ThirdPersonCamera::SetPropertyElevationAngleOffset"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "=",
+                        "Value"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "ThirdPersonCamera::ThirdPersonCamera",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "the targeted camera rotation angle of the object. When this angle is set, the camera follow this value instead of the object angle.",
+              "fullName": "Targeted rotation angle",
+              "functionType": "ExpressionAndCondition",
+              "group": "Third person camera configuration",
+              "name": "TargetedRotationAngle",
+              "sentence": "the targeted camera rotation angle",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SetReturnNumber"
+                      },
+                      "parameters": [
+                        "TargetedRotationAngle"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "expressionType": {
+                "type": "expression"
+              },
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "ThirdPersonCamera::ThirdPersonCamera",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "fullName": "",
+              "functionType": "ActionWithOperator",
+              "getterName": "TargetedRotationAngle",
+              "name": "SetTargetedRotationAngle",
+              "sentence": "",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "ThirdPersonCamera::ThirdPersonCamera::SetPropertyTargetedRotationAngle"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "=",
+                        "Value"
+                      ]
+                    },
+                    {
+                      "type": {
+                        "value": "ThirdPersonCamera::ThirdPersonCamera::SetPropertyIsRotatingWithObject"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        ""
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "ThirdPersonCamera::ThirdPersonCamera",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            }
+          ],
+          "propertyDescriptors": [
+            {
+              "value": "",
+              "type": "Behavior",
+              "label": "3D capability",
+              "description": "",
+              "group": "",
+              "extraInformation": [
+                "Scene3D::Base3DBehavior"
+              ],
+              "name": "Object3D"
+            },
+            {
+              "value": "0.125",
+              "type": "Number",
+              "unit": "Second",
+              "label": "Halfway  time for rotation",
+              "description": "",
+              "group": "Catch-up speed",
+              "extraInformation": [],
+              "name": "RotationHalfwayDuration"
+            },
+            {
+              "value": "0.125",
+              "type": "Number",
+              "unit": "Second",
+              "label": "Halfway time on Z axis",
+              "description": "",
+              "group": "Catch-up speed",
+              "extraInformation": [],
+              "name": "TranslationZHalfwayDuration"
+            },
+            {
+              "value": "500",
+              "type": "Number",
+              "unit": "Pixel",
+              "label": "Camera distance",
+              "description": "",
+              "group": "Position",
+              "extraInformation": [],
+              "name": "Distance"
+            },
+            {
+              "value": "0",
+              "type": "Number",
+              "unit": "Pixel",
+              "label": "Lateral distance offset",
+              "description": "",
+              "group": "Position",
+              "extraInformation": [],
+              "name": "OffsetX"
+            },
+            {
+              "value": "0",
+              "type": "Number",
+              "unit": "Pixel",
+              "label": "Ahead distance offset",
+              "description": "",
+              "group": "Position",
+              "extraInformation": [],
+              "name": "OffsetY"
+            },
+            {
+              "value": "0",
+              "type": "Number",
+              "unit": "Pixel",
+              "label": "Z offset",
+              "description": "",
+              "group": "Position",
+              "extraInformation": [],
+              "name": "OffsetZ"
+            },
+            {
+              "value": "0",
+              "type": "Number",
+              "unit": "DegreeAngle",
+              "label": "Rotation angle offset",
+              "description": "",
+              "group": "Position",
+              "extraInformation": [],
+              "name": "RotationAngleOffset"
+            },
+            {
+              "value": "20",
+              "type": "Number",
+              "unit": "DegreeAngle",
+              "label": "Elevation angle offset",
+              "description": "",
+              "group": "Position",
+              "extraInformation": [],
+              "name": "ElevationAngleOffset"
+            },
+            {
+              "value": "0",
+              "type": "Number",
+              "unit": "Pixel",
+              "label": "Follow free area top border on Z axis",
+              "description": "",
+              "group": "Position",
+              "extraInformation": [],
+              "advanced": true,
+              "name": "FollowFreeAreaZMax"
+            },
+            {
+              "value": "0",
+              "type": "Number",
+              "unit": "Pixel",
+              "label": "Follow free area bottom border on Z axis",
+              "description": "",
+              "group": "Position",
+              "extraInformation": [],
+              "advanced": true,
+              "name": "FollowFreeAreaZMin"
+            },
+            {
+              "value": "",
+              "type": "Number",
+              "label": "",
+              "description": "",
+              "group": "",
+              "extraInformation": [],
+              "hidden": true,
+              "name": "RotationLogSpeed"
+            },
+            {
+              "value": "",
+              "type": "Number",
+              "label": "",
+              "description": "",
+              "group": "",
+              "extraInformation": [],
+              "hidden": true,
+              "name": "TranslationZLogSpeed"
+            },
+            {
+              "value": "",
+              "type": "Boolean",
+              "label": "",
+              "description": "",
+              "group": "",
+              "extraInformation": [],
+              "hidden": true,
+              "name": "IsCalledManually"
+            },
+            {
+              "value": "0",
+              "type": "Number",
+              "unit": "Pixel",
+              "label": "",
+              "description": "",
+              "group": "",
+              "extraInformation": [],
+              "hidden": true,
+              "name": "CameraZ"
+            },
+            {
+              "value": "true",
+              "type": "Boolean",
+              "label": "",
+              "description": "",
+              "group": "",
+              "extraInformation": [],
+              "hidden": true,
+              "name": "HasJustBeenCreated"
+            },
+            {
+              "value": "true",
+              "type": "Boolean",
+              "label": "Automatically rotate the camera with the object",
+              "description": "",
+              "group": "",
+              "extraInformation": [],
+              "name": "IsRotatingWithObject"
+            },
+            {
+              "value": "0",
+              "type": "Number",
+              "unit": "DegreeAngle",
+              "label": "Targeted camera rotation angle",
+              "description": "When this angle is set, the camera follow this value instead of the object angle.",
+              "group": "",
+              "extraInformation": [],
+              "hidden": true,
+              "name": "TargetedRotationAngle"
+            }
+          ],
+          "sharedPropertyDescriptors": []
+        }
+      ],
+      "eventsBasedObjects": []
+    },
+    {
+      "author": "",
+      "category": "Visual effect",
+      "extensionNamespace": "",
+      "fullName": "3D particle emitter",
+      "helpPath": "",
+      "iconUrl": "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz48IURPQ1RZUEUgc3ZnIFBVQkxJQyAiLS8vVzNDLy9EVEQgU1ZHIDEuMS8vRU4iICJodHRwOi8vd3d3LnczLm9yZy9HcmFwaGljcy9TVkcvMS4xL0RURC9zdmcxMS5kdGQiPjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgdmVyc2lvbj0iMS4xIiBpZD0ibWRpLWZpcmUiIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgdmlld0JveD0iMCAwIDI0IDI0Ij48cGF0aCBkPSJNMTcuNjYgMTEuMkMxNy40MyAxMC45IDE3LjE1IDEwLjY0IDE2Ljg5IDEwLjM4QzE2LjIyIDkuNzggMTUuNDYgOS4zNSAxNC44MiA4LjcyQzEzLjMzIDcuMjYgMTMgNC44NSAxMy45NSAzQzEzIDMuMjMgMTIuMTcgMy43NSAxMS40NiA0LjMyQzguODcgNi40IDcuODUgMTAuMDcgOS4wNyAxMy4yMkM5LjExIDEzLjMyIDkuMTUgMTMuNDIgOS4xNSAxMy41NUM5LjE1IDEzLjc3IDkgMTMuOTcgOC44IDE0LjA1QzguNTcgMTQuMTUgOC4zMyAxNC4wOSA4LjE0IDEzLjkzQzguMDggMTMuODggOC4wNCAxMy44MyA4IDEzLjc2QzYuODcgMTIuMzMgNi42OSAxMC4yOCA3LjQ1IDguNjRDNS43OCAxMCA0Ljg3IDEyLjMgNSAxNC40N0M1LjA2IDE0Ljk3IDUuMTIgMTUuNDcgNS4yOSAxNS45N0M1LjQzIDE2LjU3IDUuNyAxNy4xNyA2IDE3LjdDNy4wOCAxOS40MyA4Ljk1IDIwLjY3IDEwLjk2IDIwLjkyQzEzLjEgMjEuMTkgMTUuMzkgMjAuOCAxNy4wMyAxOS4zMkMxOC44NiAxNy42NiAxOS41IDE1IDE4LjU2IDEyLjcyTDE4LjQzIDEyLjQ2QzE4LjIyIDEyIDE3LjY2IDExLjIgMTcuNjYgMTEuMk0xNC41IDE3LjVDMTQuMjIgMTcuNzQgMTMuNzYgMTggMTMuNCAxOC4xQzEyLjI4IDE4LjUgMTEuMTYgMTcuOTQgMTAuNSAxNy4yOEMxMS42OSAxNyAxMi40IDE2LjEyIDEyLjYxIDE1LjIzQzEyLjc4IDE0LjQzIDEyLjQ2IDEzLjc3IDEyLjMzIDEzQzEyLjIxIDEyLjI2IDEyLjIzIDExLjYzIDEyLjUgMTAuOTRDMTIuNjkgMTEuMzIgMTIuODkgMTEuNyAxMy4xMyAxMkMxMy45IDEzIDE1LjExIDEzLjQ0IDE1LjM3IDE0LjhDMTUuNDEgMTQuOTQgMTUuNDMgMTUuMDggMTUuNDMgMTUuMjNDMTUuNDYgMTYuMDUgMTUuMSAxNi45NSAxNC41IDE3LjVIMTQuNVoiIC8+PC9zdmc+",
+      "name": "ParticleEmitter3D",
+      "previewIconUrl": "https://asset-resources.gdevelop.io/public-resources/Icons/f2e5a34bf465f781866677762d385d6c8e9e8d203383f2df9a3b7e0fad6a2cb5_fire.svg",
+      "shortDescription": "Display a large number of particles to create visual effects.",
+      "version": "2.0.5",
+      "description": [
+        "Particle emitters can be used to display:",
+        "- Fire",
+        "- Smoke",
+        "- Splashes",
+        "- Lights",
+        "",
+        "Breaking change",
+        "- 2.0.0",
+        "  - Object properties for position and rotation have been removed. They must be set with the instance editor or the action.",
+        "- 1.0.0",
+        "  - Particles were 3 times too small"
+      ],
+      "origin": {
+        "identifier": "ParticleEmitter3D",
+        "name": "gdevelop-extension-store"
+      },
+      "tags": [
+        "3d",
+        "particle",
+        "explosion",
+        "fire",
+        "smoke",
+        "splash",
+        "light"
+      ],
+      "authorIds": [
+        "IWykYNRvhCZBN3vEgKEbBPOR3Oc2"
+      ],
+      "dependencies": [],
+      "globalVariables": [],
+      "sceneVariables": [],
+      "eventsFunctions": [
+        {
+          "description": "Define helper classes JavaScript code.",
+          "fullName": "Define helper classes",
+          "functionType": "Action",
+          "name": "DefineHelperClasses",
+          "private": true,
+          "sentence": "Define helper classes JavaScript code",
+          "events": [
+            {
+              "type": "BuiltinCommonInstructions::JsCode",
+              "inlineCode": [
+                "if (gdjs.__particleEmmiter3DExtension) {",
+                "    return;",
+                "}",
+                "",
+                "class ParticleEmitter3DRenderer extends gdjs.CustomRuntimeObject3DRenderer {",
+                "    constructor(",
+                "        object,",
+                "        instanceContainer,",
+                "        parent",
+                "    ) {",
+                "        super(object, instanceContainer, parent);",
+                "    }",
+                "",
+                "    _updateThreeGroup() {",
+                "        const threeObject3D = this.get3DRendererObject();",
+                "",
+                "        threeObject3D.rotation.set(",
+                "            gdjs.toRad(this._object.getRotationX()),",
+                "            gdjs.toRad(this._object.getRotationY()),",
+                "            -gdjs.toRad(this._object.angle)",
+                "        );",
+                "",
+                "        threeObject3D.position.set(",
+                "            this._object.getX(),",
+                "            -this._object.getY(),",
+                "            this._object.getZ()",
+                "        );",
+                "",
+                "        threeObject3D.scale.set(",
+                "            this._object.isFlippedX() ? -1 : 1,",
+                "            this._object.isFlippedY() ? -1 : 1,",
+                "            this._object.isFlippedZ() ? -1 : 1",
+                "        );",
+                "",
+                "        threeObject3D.visible = !this._object.hidden;",
+                "",
+                "        this._isContainerDirty = true;",
+                "    }",
+                "}",
+                "",
+                "/**",
+                " * @param {string} colorString",
+                " * @param {THREE.Vector4} threeColor",
+                " */",
+                "const setThreeColor = (colorString, threeColor = new THREE.Vector4()) => {",
+                "    const integerColor = gdjs.rgbOrHexToRGBColor(colorString);",
+                "    threeColor.x = integerColor[0] / 255;",
+                "    threeColor.y = integerColor[1] / 255;",
+                "    threeColor.z = integerColor[2] / 255;",
+                "};",
+                "",
+                "/**",
+                " * @param {string} integerOpacity",
+                " * @param {THREE.Vector4} threeColor",
+                " */",
+                "const setThreeOpacity = (integerOpacity, threeColor = new THREE.Vector4()) => {",
+                "    threeColor.w = integerOpacity / 255;",
+                "};",
+                "",
+                "class ParticleEmitterAdapter {",
+                "    /**",
+                "     * @param particleSystem {ParticleSystem}",
+                "     * @param colorOverLife {ColorOverLife}",
+                "     * @param sizeOverLife {SizeOverLife}",
+                "     * @param applyForce {ApplyForce}",
+                "     */",
+                "    constructor(particleSystem, colorOverLife, sizeOverLife, applyForce) {",
+                "        this.particleSystem = particleSystem;",
+                "        this.colorOverLife = colorOverLife;",
+                "        this.sizeOverLife = sizeOverLife;",
+                "        this.applyForce = applyForce;",
+                "    }",
+                "",
+                "    /**",
+                "     * @param startColor {string}",
+                "     */",
+                "    setStartColor(startColor) {",
+                "        setThreeColor(startColor, this.colorOverLife.color.color.keys[0][0]);",
+                "    }",
+                "",
+                "    /**",
+                "     * @param endColor {string}",
+                "     */",
+                "    setEndColor(endColor) {",
+                "        setThreeColor(endColor, this.colorOverLife.color.color.keys[1][0]);",
+                "    }",
+                "",
+                "    /**",
+                "     * @param startOpacity {number}",
+                "     */",
+                "    setStartOpacity(startOpacity) {",
+                "        this.colorOverLife.color.alpha.keys[0][0] = startOpacity / 255;",
+                "    }",
+                "",
+                "    /**",
+                "     * @param endOpacity {number}",
+                "     */",
+                "    setEndOpacity(endOpacity) {",
+                "        this.colorOverLife.color.alpha.keys[1][0] = endOpacity / 255;",
+                "    }",
+                "",
+                "    /**",
+                "     * @param flow {number}",
+                "     */",
+                "    setFlow(flow) {",
+                "        this.particleSystem.emissionOverTime.value = flow;",
+                "    }",
+                "",
+                "    /**",
+                "     * @param startMinSize {number}",
+                "     */",
+                "    setStartMinSize(startMinSize) {",
+                "        this.particleSystem.startSize.a = startMinSize;",
+                "    }",
+                "",
+                "    /**",
+                "     * @param startMaxSize {number}",
+                "     */",
+                "    setStartMaxSize(startMaxSize) {",
+                "        this.particleSystem.startSize.b = startMaxSize;",
+                "    }",
+                "",
+                "    /**",
+                "     * @param endScale {number}",
+                "     */",
+                "    setEndScale(endScale) {",
+                "        /** @type Bezier */",
+                "        const bezier = this.sizeOverLife.size.functions[0][0];",
+                "        bezier.p[0] = 1;",
+                "        bezier.p[1] = gdjs.evtTools.common.lerp(1, endScale, 1 / 3);",
+                "        bezier.p[2] = gdjs.evtTools.common.lerp(1, endScale, 2 / 3);",
+                "        bezier.p[3] = endScale;",
+                "    }",
+                "",
+                "    /**",
+                "     * @param startSpeedMin {number}",
+                "     */",
+                "    setStartSpeedMin(startSpeedMin) {",
+                "        this.particleSystem.startSpeed.a = startSpeedMin;",
+                "    }",
+                "",
+                "    /**",
+                "     * @param startSpeedMax {number}",
+                "     */",
+                "    setStartSpeedMax(startSpeedMax) {",
+                "        this.particleSystem.startSpeed.b = startSpeedMax;",
+                "    }",
+                "",
+                "    /**",
+                "     * @param lifespanMin {number}",
+                "     */",
+                "    setLifespanMin(lifespanMin) {",
+                "        this.particleSystem.startLife.a = lifespanMin;",
+                "    }",
+                "",
+                "    /**",
+                "     * @param lifespanMax {number}",
+                "     */",
+                "    setLifespanMax(lifespanMax) {",
+                "        this.particleSystem.startLife.b = lifespanMax;",
+                "    }",
+                "",
+                "    /**",
+                "     * @param duration {number}",
+                "     */",
+                "    setDuration(duration) {",
+                "        this.particleSystem.duration = duration || Number.POSITIVE_INFINITY;",
+                "    }",
+                "",
+                "    /**",
+                "     * @param areParticlesRelative {boolean}",
+                "     */",
+                "    setParticlesRelative(areParticlesRelative) {",
+                "        this.particleSystem.worldSpace = !areParticlesRelative;",
+                "    }",
+                "",
+                "    /**",
+                "     * @param sprayConeAngle {number}",
+                "     */",
+                "    setSprayConeAngle(sprayConeAngle) {",
+                "        if (0 < sprayConeAngle && sprayConeAngle <= 180) {",
+                "            if (!this.particleSystem.emitterShape.angle) {",
+                "                this.particleSystem.emitterShape = new ConeEmitter({ radius: 0.1, angle: Math.PI / 8 });",
+                "            }",
+                "            this.particleSystem.emitterShape.angle = sprayConeAngle * Math.PI / 180;",
+                "        }",
+                "        else {",
+                "            if (this.particleSystem.emitterShape.angle) {",
+                "                this.particleSystem.emitterShape = new PointEmitter();",
+                "            }",
+                "        }",
+                "    }",
+                "",
+                "    /**",
+                "     * @param blending {string}",
+                "     */",
+                "    setBlending(blendingString) {",
+                "        const blending =",
+                "            blendingString === \"Additive\" ? THREE.AdditiveBlending :",
+                "                blendingString === \"Normal\" ? THREE.NormalBlending :",
+                "                    blendingString === \"Subtractive\" ? THREE.SubtractiveBlending :",
+                "                        blendingString === \"Multiply\" ? THREE.MultiplyBlending :",
+                "                            blendingString === \"None\" ? THREE.NoBlending :",
+                "                                THREE.AdditiveBlending;",
+                "        // TODO This doesn't work.",
+                "        this.particleSystem.blending = blending;",
+                "    }",
+                "",
+                "    /**",
+                "     * @param gravity {number}",
+                "     */",
+                "    setGravity(gravity) {",
+                "        this.applyForce.magnitude.value = gravity;",
+                "    }",
+                "",
+                "    /**",
+                "     * @param gravityTop {string}",
+                "     */",
+                "    setGravityTop(gravityTop) {",
+                "        // TODO Make gravity absolute even when \"relative\" is enabled. ",
+                "        switch (gravityTop) {",
+                "            case \"Z+\":",
+                "                this.applyForce.direction.set(0, 0, -1);",
+                "                break;",
+                "",
+                "            case \"Y-\":",
+                "                this.applyForce.direction.set(0, 1, 0);",
+                "                break;",
+                "        }",
+                "    }",
+                "}",
+                "",
+                "",
+                "",
+                "/**",
+                " * three.quarks v0.11.2 build Mon Jan 22 2024",
+                " * https://github.com/Alchemist0823/three.quarks#readme",
+                " * Copyright 2024 Alchemist0823 <the.forrest.sun@gmail.com>, MIT",
+                " */",
+                "",
+                "// The migration to addUpdateRange was backported without updating the library",
+                "// because GDevelop still uses Three.js 0.160.0.",
+                "",
+                "class ParticleEmitter extends THREE.Object3D {",
+                "    constructor(system) {",
+                "        super();",
+                "        this.type = 'ParticleEmitter';",
+                "        this.system = system;",
+                "    }",
+                "    clone() {",
+                "        const system = this.system.clone();",
+                "        system.emitter.copy(this, true);",
+                "        return system.emitter;",
+                "    }",
+                "    dispose() { }",
+                "    extractFromCache(cache) {",
+                "        const values = [];",
+                "        for (const key in cache) {",
+                "            const data = cache[key];",
+                "            delete data.metadata;",
+                "            values.push(data);",
+                "        }",
+                "        return values;",
+                "    }",
+                "    toJSON(meta, options = {}) {",
+                "        const isRootObject = meta === undefined || typeof meta === 'string';",
+                "        const output = {};",
+                "        if (isRootObject) {",
+                "            meta = {",
+                "                geometries: {},",
+                "                materials: {},",
+                "                textures: {},",
+                "                images: {},",
+                "                shapes: {},",
+                "                skeletons: {},",
+                "                animations: {},",
+                "                nodes: {},",
+                "            };",
+                "            output.metadata = {",
+                "                version: 4.5,",
+                "                type: 'Object',",
+                "                generator: 'Object3D.toJSON',",
+                "            };",
+                "        }",
+                "        const object = {};",
+                "        object.uuid = this.uuid;",
+                "        object.type = this.type;",
+                "        if (this.name !== '')",
+                "            object.name = this.name;",
+                "        if (this.castShadow === true)",
+                "            object.castShadow = true;",
+                "        if (this.receiveShadow === true)",
+                "            object.receiveShadow = true;",
+                "        if (this.visible === false)",
+                "            object.visible = false;",
+                "        if (this.frustumCulled === false)",
+                "            object.frustumCulled = false;",
+                "        if (this.renderOrder !== 0)",
+                "            object.renderOrder = this.renderOrder;",
+                "        if (JSON.stringify(this.userData) !== '{}')",
+                "            object.userData = this.userData;",
+                "        object.layers = this.layers.mask;",
+                "        object.matrix = this.matrix.toArray();",
+                "        if (this.matrixAutoUpdate === false)",
+                "            object.matrixAutoUpdate = false;",
+                "        if (this.system !== null)",
+                "            object.ps = this.system.toJSON(meta, options);",
+                "        if (this.children.length > 0) {",
+                "            object.children = [];",
+                "            for (let i = 0; i < this.children.length; i++) {",
+                "                if (this.children[i].type !== 'ParticleSystemPreview') {",
+                "                    object.children.push(this.children[i].toJSON(meta).object);",
+                "                }",
+                "            }",
+                "        }",
+                "        if (isRootObject) {",
+                "            const geometries = this.extractFromCache(meta.geometries);",
+                "            const materials = this.extractFromCache(meta.materials);",
+                "            const textures = this.extractFromCache(meta.textures);",
+                "            const images = this.extractFromCache(meta.images);",
+                "            if (geometries.length > 0)",
+                "                output.geometries = geometries;",
+                "            if (materials.length > 0)",
+                "                output.materials = materials;",
+                "            if (textures.length > 0)",
+                "                output.textures = textures;",
+                "            if (images.length > 0)",
+                "                output.images = images;",
+                "        }",
+                "        output.object = object;",
+                "        return output;",
+                "    }",
+                "}",
+                "",
+                "class LinkedListNode {",
+                "    constructor(data) {",
+                "        this.data = data;",
+                "        this.next = null;",
+                "        this.prev = null;",
+                "    }",
+                "    hasPrev() {",
+                "        return this.prev !== null;",
+                "    }",
+                "    hasNext() {",
+                "        return this.next !== null;",
+                "    }",
+                "}",
+                "class LinkedList {",
+                "    constructor() {",
+                "        this.length = 0;",
+                "        this.head = this.tail = null;",
+                "    }",
+                "    isEmpty() {",
+                "        return this.head === null;",
+                "    }",
+                "    clear() {",
+                "        this.length = 0;",
+                "        this.head = this.tail = null;",
+                "    }",
+                "    front() {",
+                "        if (this.head === null)",
+                "            return null;",
+                "        return this.head.data;",
+                "    }",
+                "    back() {",
+                "        if (this.tail === null)",
+                "            return null;",
+                "        return this.tail.data;",
+                "    }",
+                "    dequeue() {",
+                "        if (this.head) {",
+                "            const value = this.head.data;",
+                "            this.head = this.head.next;",
+                "            if (!this.head) {",
+                "                this.tail = null;",
+                "            }",
+                "            else {",
+                "                this.head.prev = null;",
+                "            }",
+                "            this.length--;",
+                "            return value;",
+                "        }",
+                "        return undefined;",
+                "    }",
+                "    pop() {",
+                "        if (this.tail) {",
+                "            const value = this.tail.data;",
+                "            this.tail = this.tail.prev;",
+                "            if (!this.tail) {",
+                "                this.head = null;",
+                "            }",
+                "            else {",
+                "                this.tail.next = null;",
+                "            }",
+                "            this.length--;",
+                "            return value;",
+                "        }",
+                "        return undefined;",
+                "    }",
+                "    queue(data) {",
+                "        const node = new LinkedListNode(data);",
+                "        if (!this.tail) {",
+                "            this.tail = node;",
+                "        }",
+                "        if (this.head) {",
+                "            this.head.prev = node;",
+                "            node.next = this.head;",
+                "        }",
+                "        this.head = node;",
+                "        this.length++;",
+                "    }",
+                "    push(data) {",
+                "        const node = new LinkedListNode(data);",
+                "        if (!this.head) {",
+                "            this.head = node;",
+                "        }",
+                "        if (this.tail) {",
+                "            this.tail.next = node;",
+                "            node.prev = this.tail;",
+                "        }",
+                "        this.tail = node;",
+                "        this.length++;",
+                "    }",
+                "    insertBefore(node, data) {",
+                "        const newNode = new LinkedListNode(data);",
+                "        newNode.next = node;",
+                "        newNode.prev = node.prev;",
+                "        if (newNode.prev !== null) {",
+                "            newNode.prev.next = newNode;",
+                "        }",
+                "        newNode.next.prev = newNode;",
+                "        if (node == this.head) {",
+                "            this.head = newNode;",
+                "        }",
+                "        this.length++;",
+                "    }",
+                "    remove(data) {",
+                "        if (this.head === null || this.tail === null) {",
+                "            return;",
+                "        }",
+                "        let tempNode = this.head;",
+                "        if (data === this.head.data) {",
+                "            this.head = this.head.next;",
+                "        }",
+                "        if (data === this.tail.data) {",
+                "            this.tail = this.tail.prev;",
+                "        }",
+                "        while (tempNode.next !== null && tempNode.data !== data) {",
+                "            tempNode = tempNode.next;",
+                "        }",
+                "        if (tempNode.data === data) {",
+                "            if (tempNode.prev !== null)",
+                "                tempNode.prev.next = tempNode.next;",
+                "            if (tempNode.next !== null)",
+                "                tempNode.next.prev = tempNode.prev;",
+                "            this.length--;",
+                "        }",
+                "    }",
+                "    *values() {",
+                "        let current = this.head;",
+                "        while (current !== null) {",
+                "            yield current.data;",
+                "            current = current.next;",
+                "        }",
+                "    }",
+                "}",
+                "",
+                "class NodeParticle {",
+                "    constructor() {",
+                "        this.position = new THREE.Vector3();",
+                "        this.velocity = new THREE.Vector3();",
+                "        this.age = 0;",
+                "        this.life = 1;",
+                "        this.size = 1;",
+                "        this.rotation = 0;",
+                "        this.color = new THREE.Vector4(1, 1, 1, 1);",
+                "        this.uvTile = 0;",
+                "    }",
+                "    get died() {",
+                "        return this.age >= this.life;",
+                "    }",
+                "    reset() {",
+                "        this.position.set(0, 0, 0);",
+                "        this.velocity.set(0, 0, 0);",
+                "        this.age = 0;",
+                "        this.life = 1;",
+                "        this.size = 1;",
+                "        this.rotation = 0;",
+                "        this.color.set(1, 1, 1, 1);",
+                "        this.uvTile = 0;",
+                "    }",
+                "}",
+                "class SpriteParticle {",
+                "    constructor() {",
+                "        this.startSpeed = 0;",
+                "        this.startColor = new THREE.Vector4();",
+                "        this.startSize = 1;",
+                "        this.position = new THREE.Vector3();",
+                "        this.velocity = new THREE.Vector3();",
+                "        this.age = 0;",
+                "        this.life = 1;",
+                "        this.size = 1;",
+                "        this.speedModifier = 1;",
+                "        this.rotation = 0;",
+                "        this.color = new THREE.Vector4();",
+                "        this.uvTile = 0;",
+                "    }",
+                "    get died() {",
+                "        return this.age >= this.life;",
+                "    }",
+                "}",
+                "class RecordState {",
+                "    constructor(position, size, color) {",
+                "        this.position = position;",
+                "        this.size = size;",
+                "        this.color = color;",
+                "    }",
+                "}",
+                "class TrailParticle {",
+                "    constructor() {",
+                "        this.startSpeed = 0;",
+                "        this.startColor = new THREE.Vector4();",
+                "        this.startSize = 1;",
+                "        this.position = new THREE.Vector3();",
+                "        this.velocity = new THREE.Vector3();",
+                "        this.age = 0;",
+                "        this.life = 1;",
+                "        this.size = 1;",
+                "        this.length = 100;",
+                "        this.speedModifier = 1;",
+                "        this.color = new THREE.Vector4();",
+                "        this.previous = new LinkedList();",
+                "        this.uvTile = 0;",
+                "    }",
+                "    update() {",
+                "        if (this.age <= this.life) {",
+                "            this.previous.push(new RecordState(this.position.clone(), this.size, this.color.clone()));",
+                "        }",
+                "        else {",
+                "            if (this.previous.length > 0) {",
+                "                this.previous.dequeue();",
+                "            }",
+                "        }",
+                "        while (this.previous.length > this.length) {",
+                "            this.previous.dequeue();",
+                "        }",
+                "    }",
+                "    get died() {",
+                "        return this.age >= this.life;",
+                "    }",
+                "    reset() {",
+                "        this.previous.clear();",
+                "    }",
+                "}",
+                "",
+                "class Bezier {",
+                "    constructor(p1, p2, p3, p4) {",
+                "        this.p = [p1, p2, p3, p4];",
+                "    }",
+                "    genValue(t) {",
+                "        const t2 = t * t;",
+                "        const t3 = t * t * t;",
+                "        const mt = 1 - t;",
+                "        const mt2 = mt * mt;",
+                "        const mt3 = mt2 * mt;",
+                "        return this.p[0] * mt3 + this.p[1] * mt2 * t * 3 + this.p[2] * mt * t2 * 3 + this.p[3] * t3;",
+                "    }",
+                "    derivativeCoefficients(points) {",
+                "        const dpoints = [];",
+                "        for (let p = points, c = p.length - 1; c > 0; c--) {",
+                "            const list = [];",
+                "            for (let j = 0; j < c; j++) {",
+                "                const dpt = c * (p[j + 1] - p[j]);",
+                "                list.push(dpt);",
+                "            }",
+                "            dpoints.push(list);",
+                "            p = list;",
+                "        }",
+                "        return dpoints;",
+                "    }",
+                "    getSlope(t) {",
+                "        const p = this.derivativeCoefficients(this.p)[0];",
+                "        const mt = 1 - t;",
+                "        const a = mt * mt;",
+                "        const b = mt * t * 2;",
+                "        const c = t * t;",
+                "        return a * p[0] + b * p[1] + c * p[2];",
+                "    }",
+                "    controlCurve(d0, d1) {",
+                "        this.p[1] = d0 / 3 + this.p[0];",
+                "        this.p[2] = this.p[3] - d1 / 3;",
+                "    }",
+                "    hull(t) {",
+                "        let p = this.p;",
+                "        let _p = [], pt, idx = 0, i = 0, l = 0;",
+                "        const q = [];",
+                "        q[idx++] = p[0];",
+                "        q[idx++] = p[1];",
+                "        q[idx++] = p[2];",
+                "        q[idx++] = p[3];",
+                "        while (p.length > 1) {",
+                "            _p = [];",
+                "            for (i = 0, l = p.length - 1; i < l; i++) {",
+                "                pt = t * p[i] + (1 - t) * p[i + 1];",
+                "                q[idx++] = pt;",
+                "                _p.push(pt);",
+                "            }",
+                "            p = _p;",
+                "        }",
+                "        return q;",
+                "    }",
+                "    split(t) {",
+                "        const q = this.hull(t);",
+                "        const result = {",
+                "            left: new Bezier(q[0], q[4], q[7], q[9]),",
+                "            right: new Bezier(q[9], q[8], q[6], q[3]),",
+                "            span: q",
+                "        };",
+                "        return result;",
+                "    }",
+                "    clone() {",
+                "        return new Bezier(this.p[0], this.p[1], this.p[2], this.p[3]);",
+                "    }",
+                "    toJSON() {",
+                "        return {",
+                "            p0: this.p[0],",
+                "            p1: this.p[1],",
+                "            p2: this.p[2],",
+                "            p3: this.p[3],",
+                "        };",
+                "    }",
+                "    static fromJSON(json) {",
+                "        return new Bezier(json.p0, json.p1, json.p2, json.p3);",
+                "    }",
+                "}",
+                "",
+                "const ColorToJSON = (color) => {",
+                "    return { r: color.x, g: color.y, b: color.z, a: color.w };",
+                "};",
+                "const JSONToColor = (json) => {",
+                "    return new THREE.Vector4(json.r, json.g, json.b, json.a);",
+                "};",
+                "const JSONToValue = (json, type) => {",
+                "    switch (type) {",
+                "        case 'Vector3':",
+                "            return new THREE.Vector3(json.x, json.y, json.z);",
+                "        case 'Vector4':",
+                "            return new THREE.Vector4(json.x, json.y, json.z, json.w);",
+                "        case 'Color':",
+                "            return new THREE.Vector3(json.r, json.g, json.b);",
+                "        case 'Number':",
+                "            return json;",
+                "        default:",
+                "            return json;",
+                "    }",
+                "};",
+                "const ValueToJSON = (value, type) => {",
+                "    switch (type) {",
+                "        case 'Vector3':",
+                "            return { x: value.x, y: value.y, z: value.z };",
+                "        case 'Vector4':",
+                "            return { x: value.x, y: value.y, z: value.z, w: value.w };",
+                "        case 'Color':",
+                "            return { r: value.x, g: value.y, b: value.z };",
+                "        case 'Number':",
+                "            return value;",
+                "        default:",
+                "            return value;",
+                "    }",
+                "};",
+                "",
+                "class RandomColor {",
+                "    constructor(a, b) {",
+                "        this.a = a;",
+                "        this.b = b;",
+                "        this.type = \"value\";",
+                "    }",
+                "    genColor(color) {",
+                "        const rand = Math.random();",
+                "        return color.copy(this.a).lerp(this.b, rand);",
+                "    }",
+                "    toJSON() {",
+                "        return {",
+                "            type: \"RandomColor\",",
+                "            a: ColorToJSON(this.a),",
+                "            b: ColorToJSON(this.b),",
+                "        };",
+                "    }",
+                "    static fromJSON(json) {",
+                "        return new RandomColor(JSONToColor(json.a), JSONToColor(json.b));",
+                "    }",
+                "    clone() {",
+                "        return new RandomColor(this.a.clone(), this.b.clone());",
+                "    }",
+                "}",
+                "",
+                "class ColorRange {",
+                "    constructor(a, b) {",
+                "        this.a = a;",
+                "        this.b = b;",
+                "        this.type = 'value';",
+                "    }",
+                "    genColor(color, t) {",
+                "        return color.copy(this.a).lerp(this.b, Math.random());",
+                "    }",
+                "    toJSON() {",
+                "        return {",
+                "            type: 'ColorRange',",
+                "            a: ColorToJSON(this.a),",
+                "            b: ColorToJSON(this.b),",
+                "        };",
+                "    }",
+                "    static fromJSON(json) {",
+                "        return new ColorRange(JSONToColor(json.a), JSONToColor(json.b));",
+                "    }",
+                "    clone() {",
+                "        return new ColorRange(this.a.clone(), this.b.clone());",
+                "    }",
+                "}",
+                "",
+                "class ContinuousLinearFunction {",
+                "    constructor(keys, subType) {",
+                "        this.subType = subType;",
+                "        this.type = 'function';",
+                "        this.keys = keys;",
+                "    }",
+                "    findKey(t) {",
+                "        let mid = 0;",
+                "        let left = 0, right = this.keys.length - 1;",
+                "        while (left + 1 < right) {",
+                "            mid = Math.floor((left + right) / 2);",
+                "            if (t < this.getStartX(mid))",
+                "                right = mid - 1;",
+                "            else if (t > this.getEndX(mid))",
+                "                left = mid + 1;",
+                "            else",
+                "                return mid;",
+                "        }",
+                "        for (let i = left; i <= right; i++) {",
+                "            if (t >= this.getStartX(i) && t <= this.getEndX(i))",
+                "                return i;",
+                "        }",
+                "        return -1;",
+                "    }",
+                "    getStartX(index) {",
+                "        return this.keys[index][1];",
+                "    }",
+                "    getEndX(index) {",
+                "        if (index + 1 < this.keys.length)",
+                "            return this.keys[index + 1][1];",
+                "        return 1;",
+                "    }",
+                "    genValue(value, t) {",
+                "        const index = this.findKey(t);",
+                "        if (this.subType === 'Number') {",
+                "            if (index === -1) {",
+                "                return this.keys[0][0];",
+                "            }",
+                "            else if (index + 1 >= this.keys.length) {",
+                "                return this.keys[this.keys.length - 1][0];",
+                "            }",
+                "            return ((this.keys[index + 1][0] - this.keys[index][0]) *",
+                "                ((t - this.getStartX(index)) / (this.getEndX(index) - this.getStartX(index))) +",
+                "                this.keys[index][0]);",
+                "        }",
+                "        else {",
+                "            if (index === -1) {",
+                "                return value.copy(this.keys[0][0]);",
+                "            }",
+                "            if (index + 1 >= this.keys.length) {",
+                "                return value.copy(this.keys[this.keys.length - 1][0]);",
+                "            }",
+                "            return value",
+                "                .copy(this.keys[index][0])",
+                "                .lerp(this.keys[index + 1][0], (t - this.getStartX(index)) / (this.getEndX(index) - this.getStartX(index)));",
+                "        }",
+                "    }",
+                "    toJSON() {",
+                "        this.keys[0][0].constructor.name;",
+                "        return {",
+                "            type: 'CLinearFunction',",
+                "            subType: this.subType,",
+                "            keys: this.keys.map(([color, pos]) => ({ value: ValueToJSON(color, this.subType), pos: pos })),",
+                "        };",
+                "    }",
+                "    static fromJSON(json) {",
+                "        return new ContinuousLinearFunction(json.keys.map((pair) => [JSONToValue(pair.value, json.subType), pair.pos]), json.subType);",
+                "    }",
+                "    clone() {",
+                "        if (this.subType === 'Number') {",
+                "            return new ContinuousLinearFunction(this.keys.map(([value, pos]) => [value, pos]), this.subType);",
+                "        }",
+                "        else {",
+                "            return new ContinuousLinearFunction(this.keys.map(([value, pos]) => [value.clone(), pos]), this.subType);",
+                "        }",
+                "    }",
+                "}",
+                "",
+                "const tempVec3 = new THREE.Vector3();",
+                "class Gradient {",
+                "    constructor(color = [",
+                "        [new THREE.Vector3(0, 0, 0), 0],",
+                "        [new THREE.Vector3(1, 1, 1), 0],",
+                "    ], alpha = [",
+                "        [1, 0],",
+                "        [1, 1],",
+                "    ]) {",
+                "        this.type = 'function';",
+                "        this.color = new ContinuousLinearFunction(color, 'Color');",
+                "        this.alpha = new ContinuousLinearFunction(alpha, 'Number');",
+                "    }",
+                "    genColor(color, t) {",
+                "        this.color.genValue(tempVec3, t);",
+                "        return color.set(tempVec3.x, tempVec3.y, tempVec3.z, this.alpha.genValue(1, t));",
+                "    }",
+                "    toJSON() {",
+                "        return {",
+                "            type: 'Gradient',",
+                "            color: this.color.toJSON(),",
+                "            alpha: this.alpha.toJSON(),",
+                "        };",
+                "    }",
+                "    static fromJSON(json) {",
+                "        if (json.functions) {",
+                "            const keys = json.functions.map((func) => [ColorRange.fromJSON(func.function).a, func.start]);",
+                "            if (json.functions.length > 0) {",
+                "                keys.push([ColorRange.fromJSON(json.functions[json.functions.length - 1].function).b, 1]);",
+                "            }",
+                "            return new Gradient(keys.map((key) => [new THREE.Vector3(key[0].x, key[0].y, key[0].z), key[1]]), keys.map((key) => [key[0].w, key[1]]));",
+                "        }",
+                "        else {",
+                "            const gradient = new Gradient();",
+                "            gradient.alpha = ContinuousLinearFunction.fromJSON(json.alpha);",
+                "            gradient.color = ContinuousLinearFunction.fromJSON(json.color);",
+                "            return gradient;",
+                "        }",
+                "    }",
+                "    clone() {",
+                "        const gradient = new Gradient();",
+                "        gradient.alpha = this.alpha.clone();",
+                "        gradient.color = this.color.clone();",
+                "        return gradient;",
+                "    }",
+                "}",
+                "",
+                "const tempColor = new THREE.Vector4();",
+                "class RandomColorBetweenGradient {",
+                "    constructor(gradient1, gradient2) {",
+                "        this.type = 'memorizedFunction';",
+                "        this.gradient1 = gradient1;",
+                "        this.gradient2 = gradient2;",
+                "    }",
+                "    startGen(memory) {",
+                "        memory.rand = Math.random();",
+                "    }",
+                "    genColor(color, t, memory) {",
+                "        this.gradient1.genColor(color, t);",
+                "        this.gradient2.genColor(tempColor, t);",
+                "        if (memory && memory.rand) {",
+                "            color.lerp(tempColor, memory.rand);",
+                "        }",
+                "        else {",
+                "            color.lerp(tempColor, Math.random());",
+                "        }",
+                "        return color;",
+                "    }",
+                "    toJSON() {",
+                "        return {",
+                "            type: 'RandomColorBetweenGradient',",
+                "            gradient1: this.gradient1.toJSON(),",
+                "            gradient2: this.gradient2.toJSON(),",
+                "        };",
+                "    }",
+                "    static fromJSON(json) {",
+                "        return new RandomColorBetweenGradient(Gradient.fromJSON(json.gradient1), Gradient.fromJSON(json.gradient2));",
+                "    }",
+                "    clone() {",
+                "        return new RandomColorBetweenGradient(this.gradient1.clone(), this.gradient2.clone());",
+                "    }",
+                "}",
+                "",
+                "class ConstantColor {",
+                "    constructor(color) {",
+                "        this.color = color;",
+                "        this.type = 'value';",
+                "    }",
+                "    genColor(color) {",
+                "        return color.copy(this.color);",
+                "    }",
+                "    toJSON() {",
+                "        return {",
+                "            type: 'ConstantColor',",
+                "            color: ColorToJSON(this.color),",
+                "        };",
+                "    }",
+                "    static fromJSON(json) {",
+                "        return new ConstantColor(JSONToColor(json.color));",
+                "    }",
+                "    clone() {",
+                "        return new ConstantColor(this.color.clone());",
+                "    }",
+                "}",
+                "function ColorGeneratorFromJSON(json) {",
+                "    switch (json.type) {",
+                "        case 'ConstantColor':",
+                "            return ConstantColor.fromJSON(json);",
+                "        case 'ColorRange':",
+                "            return ColorRange.fromJSON(json);",
+                "        case 'RandomColor':",
+                "            return RandomColor.fromJSON(json);",
+                "        case 'Gradient':",
+                "            return Gradient.fromJSON(json);",
+                "        case 'RandomColorBetweenGradient':",
+                "            return RandomColorBetweenGradient.fromJSON(json);",
+                "        default:",
+                "            return new ConstantColor(new THREE.Vector4(1, 1, 1, 1));",
+                "    }",
+                "}",
+                "",
+                "class ConstantValue {",
+                "    constructor(value) {",
+                "        this.value = value;",
+                "        this.type = 'value';",
+                "    }",
+                "    genValue() {",
+                "        return this.value;",
+                "    }",
+                "    toJSON() {",
+                "        return {",
+                "            type: \"ConstantValue\",",
+                "            value: this.value",
+                "        };",
+                "    }",
+                "    static fromJSON(json) {",
+                "        return new ConstantValue(json.value);",
+                "    }",
+                "    clone() {",
+                "        return new ConstantValue(this.value);",
+                "    }",
+                "}",
+                "",
+                "class IntervalValue {",
+                "    constructor(a, b) {",
+                "        this.a = a;",
+                "        this.b = b;",
+                "        this.type = 'value';",
+                "    }",
+                "    genValue() {",
+                "        return THREE.MathUtils.lerp(this.a, this.b, Math.random());",
+                "    }",
+                "    toJSON() {",
+                "        return {",
+                "            type: 'IntervalValue',",
+                "            a: this.a,",
+                "            b: this.b,",
+                "        };",
+                "    }",
+                "    static fromJSON(json) {",
+                "        return new IntervalValue(json.a, json.b);",
+                "    }",
+                "    clone() {",
+                "        return new IntervalValue(this.a, this.b);",
+                "    }",
+                "}",
+                "",
+                "class PiecewiseFunction {",
+                "    constructor() {",
+                "        this.functions = new Array();",
+                "    }",
+                "    findFunction(t) {",
+                "        let mid = 0;",
+                "        let left = 0, right = this.functions.length - 1;",
+                "        while (left + 1 < right) {",
+                "            mid = Math.floor((left + right) / 2);",
+                "            if (t < this.getStartX(mid))",
+                "                right = mid - 1;",
+                "            else if (t > this.getEndX(mid))",
+                "                left = mid + 1;",
+                "            else",
+                "                return mid;",
+                "        }",
+                "        for (let i = left; i <= right; i++) {",
+                "            if (t >= this.functions[i][1] && t <= this.getEndX(i))",
+                "                return i;",
+                "        }",
+                "        return -1;",
+                "    }",
+                "    getStartX(index) {",
+                "        return this.functions[index][1];",
+                "    }",
+                "    setStartX(index, x) {",
+                "        if (index > 0)",
+                "            this.functions[index][1] = x;",
+                "    }",
+                "    getEndX(index) {",
+                "        if (index + 1 < this.functions.length)",
+                "            return this.functions[index + 1][1];",
+                "        return 1;",
+                "    }",
+                "    setEndX(index, x) {",
+                "        if (index + 1 < this.functions.length)",
+                "            this.functions[index + 1][1] = x;",
+                "    }",
+                "    insertFunction(t, func) {",
+                "        const index = this.findFunction(t);",
+                "        this.functions.splice(index + 1, 0, [func, t]);",
+                "    }",
+                "    removeFunction(index) {",
+                "        return this.functions.splice(index, 1)[0][0];",
+                "    }",
+                "    getFunction(index) {",
+                "        return this.functions[index][0];",
+                "    }",
+                "    setFunction(index, func) {",
+                "        this.functions[index][0] = func;",
+                "    }",
+                "    get numOfFunctions() {",
+                "        return this.functions.length;",
+                "    }",
+                "}",
+                "",
+                "class PiecewiseBezier extends PiecewiseFunction {",
+                "    constructor(curves = [[new Bezier(0, 1.0 / 3, 1.0 / 3 * 2, 1), 0]]) {",
+                "        super();",
+                "        this.type = \"function\";",
+                "        this.functions = curves;",
+                "    }",
+                "    genValue(t = 0) {",
+                "        const index = this.findFunction(t);",
+                "        if (index === -1) {",
+                "            return 0;",
+                "        }",
+                "        return this.functions[index][0].genValue((t - this.getStartX(index)) / (this.getEndX(index) - this.getStartX(index)));",
+                "    }",
+                "    toSVG(length, segments) {",
+                "        if (segments < 1)",
+                "            return \"\";",
+                "        let result = [\"M\", 0, this.functions[0][0].p[0]].join(\" \");",
+                "        for (let i = 1.0 / segments; i <= 1; i += 1.0 / segments) {",
+                "            result = [result, \"L\", i * length, this.genValue(i)].join(\" \");",
+                "        }",
+                "        return result;",
+                "    }",
+                "    toJSON() {",
+                "        return {",
+                "            type: \"PiecewiseBezier\",",
+                "            functions: this.functions.map(([bezier, start]) => ({ function: bezier.toJSON(), start: start })),",
+                "        };",
+                "    }",
+                "    static fromJSON(json) {",
+                "        return new PiecewiseBezier(json.functions.map((piecewiseFunction) => ([Bezier.fromJSON(piecewiseFunction.function), piecewiseFunction.start])));",
+                "    }",
+                "    clone() {",
+                "        return new PiecewiseBezier(this.functions.map(([bezier, start]) => ([bezier.clone(), start])));",
+                "    }",
+                "}",
+                "",
+                "function ValueGeneratorFromJSON(json) {",
+                "    switch (json.type) {",
+                "        case 'ConstantValue':",
+                "            return ConstantValue.fromJSON(json);",
+                "        case 'IntervalValue':",
+                "            return IntervalValue.fromJSON(json);",
+                "        case 'PiecewiseBezier':",
+                "            return PiecewiseBezier.fromJSON(json);",
+                "        default:",
+                "            return new ConstantValue(0);",
+                "    }",
+                "}",
+                "",
+                "class RandomQuatGenerator {",
+                "    constructor() {",
+                "        this.type = \"rotation\";",
+                "    }",
+                "    genValue(quat, t) {",
+                "        let x, y, z, u, v, w;",
+                "        do {",
+                "            x = Math.random() * 2 - 1;",
+                "            y = Math.random() * 2 - 1;",
+                "            z = x * x + y * y;",
+                "        } while (z > 1);",
+                "        do {",
+                "            u = Math.random() * 2 - 1;",
+                "            v = Math.random() * 2 - 1;",
+                "            w = u * u + v * v;",
+                "        } while (w > 1);",
+                "        const s = Math.sqrt((1 - z) / w);",
+                "        quat.set(x, y, s * u, s * v);",
+                "        return quat;",
+                "    }",
+                "    toJSON() {",
+                "        return {",
+                "            type: \"RandomQuat\"",
+                "        };",
+                "    }",
+                "    static fromJSON(json) {",
+                "        return new RandomQuatGenerator();",
+                "    }",
+                "    clone() {",
+                "        return new RandomQuatGenerator();",
+                "    }",
+                "}",
+                "",
+                "class AxisAngleGenerator {",
+                "    constructor(axis, angle) {",
+                "        this.axis = axis;",
+                "        this.angle = angle;",
+                "        this.type = 'rotation';",
+                "    }",
+                "    genValue(quat, t) {",
+                "        return quat.setFromAxisAngle(this.axis, this.angle.genValue(t));",
+                "    }",
+                "    toJSON() {",
+                "        return {",
+                "            type: 'AxisAngle',",
+                "            axis: { x: this.axis.x, y: this.axis.y, z: this.axis.z },",
+                "            angle: this.angle.toJSON(),",
+                "        };",
+                "    }",
+                "    static fromJSON(json) {",
+                "        return new AxisAngleGenerator(new THREE.Vector3(json.axis.x, json.axis.y, json.axis.z), ValueGeneratorFromJSON(json.angle));",
+                "    }",
+                "    clone() {",
+                "        return new AxisAngleGenerator(this.axis.clone(), this.angle.clone());",
+                "    }",
+                "}",
+                "",
+                "class EulerGenerator {",
+                "    constructor(angleX, angleY, angleZ, eulerOrder) {",
+                "        this.angleX = angleX;",
+                "        this.angleY = angleY;",
+                "        this.angleZ = angleZ;",
+                "        this.type = 'rotation';",
+                "        this.eular = new THREE.Euler(0, 0, 0, eulerOrder);",
+                "    }",
+                "    genValue(quat, t) {",
+                "        this.eular.set(this.angleX.genValue(t), this.angleY.genValue(t), this.angleZ.genValue(t));",
+                "        return quat.setFromEuler(this.eular);",
+                "    }",
+                "    toJSON() {",
+                "        return {",
+                "            type: 'Euler',",
+                "            angleX: this.angleX.toJSON(),",
+                "            angleY: this.angleY.toJSON(),",
+                "            angleZ: this.angleZ.toJSON(),",
+                "            eulerOrder: this.eular.order,",
+                "        };",
+                "    }",
+                "    static fromJSON(json) {",
+                "        return new EulerGenerator(ValueGeneratorFromJSON(json.angleX), ValueGeneratorFromJSON(json.angleY), ValueGeneratorFromJSON(json.angleZ), json.eulerOrder);",
+                "    }",
+                "    clone() {",
+                "        return new EulerGenerator(this.angleX, this.angleY, this.angleZ, this.eular.order);",
+                "    }",
+                "}",
+                "",
+                "function RotationGeneratorFromJSON(json) {",
+                "    switch (json.type) {",
+                "        case 'AxisAngle':",
+                "            return AxisAngleGenerator.fromJSON(json);",
+                "        case 'Euler':",
+                "            return EulerGenerator.fromJSON(json);",
+                "        case 'RandomQuat':",
+                "            return RandomQuatGenerator.fromJSON(json);",
+                "        default:",
+                "            return new RandomQuatGenerator();",
+                "    }",
+                "}",
+                "",
+                "function GeneratorFromJSON(json) {",
+                "    switch (json.type) {",
+                "        case 'ConstantValue':",
+                "        case 'IntervalValue':",
+                "        case 'PiecewiseBezier':",
+                "            return ValueGeneratorFromJSON(json);",
+                "        case 'AxisAngle':",
+                "        case 'RandomQuat':",
+                "        case 'Euler':",
+                "            return RotationGeneratorFromJSON(json);",
+                "        default:",
+                "            return new ConstantValue(0);",
+                "    }",
+                "}",
+                "",
+                "class ColorOverLife {",
+                "    constructor(color) {",
+                "        this.color = color;",
+                "        this.type = 'ColorOverLife';",
+                "    }",
+                "    initialize(particle) {",
+                "        if (this.color.type === 'memorizedFunction') {",
+                "            particle.colorOverLifeMemory = {};",
+                "            this.color.startGen(particle.colorOverLifeMemory);",
+                "        }",
+                "    }",
+                "    update(particle, delta) {",
+                "        if (this.color.type === 'memorizedFunction') {",
+                "            this.color.genColor(particle.color, particle.age / particle.life, particle.colorOverLifeMemory);",
+                "        }",
+                "        else {",
+                "            this.color.genColor(particle.color, particle.age / particle.life);",
+                "        }",
+                "        particle.color.x *= particle.startColor.x;",
+                "        particle.color.y *= particle.startColor.y;",
+                "        particle.color.z *= particle.startColor.z;",
+                "        particle.color.w *= particle.startColor.w;",
+                "    }",
+                "    frameUpdate(delta) { }",
+                "    toJSON() {",
+                "        return {",
+                "            type: this.type,",
+                "            color: this.color.toJSON(),",
+                "        };",
+                "    }",
+                "    static fromJSON(json) {",
+                "        return new ColorOverLife(ColorGeneratorFromJSON(json.color));",
+                "    }",
+                "    clone() {",
+                "        return new ColorOverLife(this.color.clone());",
+                "    }",
+                "    reset() { }",
+                "}",
+                "",
+                "class RotationOverLife {",
+                "    constructor(angularVelocity) {",
+                "        this.angularVelocity = angularVelocity;",
+                "        this.type = 'RotationOverLife';",
+                "        this.dynamic = !(angularVelocity instanceof ConstantValue || angularVelocity instanceof IntervalValue);",
+                "    }",
+                "    initialize(particle) {",
+                "        this.dynamic = !(this.angularVelocity instanceof ConstantValue || this.angularVelocity instanceof IntervalValue);",
+                "        if (!this.dynamic && typeof particle.rotation === 'number') {",
+                "            particle.angularVelocity = this.angularVelocity.genValue();",
+                "        }",
+                "    }",
+                "    update(particle, delta) {",
+                "        if (!this.dynamic) {",
+                "            if (typeof particle.rotation === 'number') {",
+                "                particle.rotation += delta * particle.angularVelocity;",
+                "            }",
+                "        }",
+                "        else {",
+                "            if (typeof particle.rotation === 'number') {",
+                "                particle.rotation +=",
+                "                    delta * this.angularVelocity.genValue(particle.age / particle.life);",
+                "            }",
+                "        }",
+                "    }",
+                "    toJSON() {",
+                "        return {",
+                "            type: this.type,",
+                "            angularVelocity: this.angularVelocity.toJSON(),",
+                "        };",
+                "    }",
+                "    static fromJSON(json) {",
+                "        return new RotationOverLife(ValueGeneratorFromJSON(json.angularVelocity));",
+                "    }",
+                "    frameUpdate(delta) { }",
+                "    clone() {",
+                "        return new RotationOverLife(this.angularVelocity.clone());",
+                "    }",
+                "    reset() { }",
+                "}",
+                "",
+                "const IdentityQuaternion = new THREE.Quaternion();",
+                "class Rotation3DOverLife {",
+                "    constructor(angularVelocity) {",
+                "        this.angularVelocity = angularVelocity;",
+                "        this.type = 'Rotation3DOverLife';",
+                "        this.tempQuat = new THREE.Quaternion();",
+                "        this.dynamic = !(angularVelocity instanceof RandomQuatGenerator);",
+                "    }",
+                "    initialize(particle) {",
+                "        this.dynamic = !(this.angularVelocity instanceof RandomQuatGenerator);",
+                "        if (particle.rotation instanceof THREE.Quaternion) {",
+                "            particle.angularVelocity = new THREE.Quaternion();",
+                "            this.angularVelocity.genValue(particle.angularVelocity);",
+                "        }",
+                "    }",
+                "    update(particle, delta) {",
+                "        if (particle.rotation instanceof THREE.Quaternion) {",
+                "            if (!this.dynamic) {",
+                "                this.tempQuat.slerpQuaternions(IdentityQuaternion, particle.angularVelocity, delta);",
+                "                particle.rotation.multiply(this.tempQuat);",
+                "            }",
+                "            else {",
+                "                this.angularVelocity.genValue(this.tempQuat, particle.age / particle.life);",
+                "                this.tempQuat.slerpQuaternions(IdentityQuaternion, this.tempQuat, delta);",
+                "                particle.rotation.multiply(this.tempQuat);",
+                "            }",
+                "        }",
+                "    }",
+                "    toJSON() {",
+                "        return {",
+                "            type: this.type,",
+                "            angularVelocity: this.angularVelocity.toJSON(),",
+                "        };",
+                "    }",
+                "    static fromJSON(json) {",
+                "        return new Rotation3DOverLife(RotationGeneratorFromJSON(json.angularVelocity));",
+                "    }",
+                "    frameUpdate(delta) { }",
+                "    clone() {",
+                "        return new Rotation3DOverLife(this.angularVelocity.clone());",
+                "    }",
+                "    reset() { }",
+                "}",
+                "",
+                "class ForceOverLife {",
+                "    initialize(particle) { }",
+                "    constructor(x, y, z) {",
+                "        this.x = x;",
+                "        this.y = y;",
+                "        this.z = z;",
+                "        this.type = 'ForceOverLife';",
+                "        this._temp = new THREE.Vector3();",
+                "    }",
+                "    update(particle, delta) {",
+                "        this._temp.set(this.x.genValue(particle.age / particle.life), this.y.genValue(particle.age / particle.life), this.z.genValue(particle.age / particle.life));",
+                "        particle.velocity.addScaledVector(this._temp, delta);",
+                "    }",
+                "    toJSON() {",
+                "        return {",
+                "            type: this.type,",
+                "            x: this.x.toJSON(),",
+                "            y: this.y.toJSON(),",
+                "            z: this.z.toJSON(),",
+                "        };",
+                "    }",
+                "    static fromJSON(json) {",
+                "        return new ForceOverLife(ValueGeneratorFromJSON(json.x), ValueGeneratorFromJSON(json.y), ValueGeneratorFromJSON(json.z));",
+                "    }",
+                "    frameUpdate(delta) { }",
+                "    clone() {",
+                "        return new ForceOverLife(this.x.clone(), this.y.clone(), this.z.clone());",
+                "    }",
+                "    reset() { }",
+                "}",
+                "",
+                "class SizeOverLife {",
+                "    initialize(particle) {",
+                "    }",
+                "    constructor(size) {",
+                "        this.size = size;",
+                "        this.type = 'SizeOverLife';",
+                "    }",
+                "    update(particle) {",
+                "        particle.size = particle.startSize * this.size.genValue(particle.age / particle.life);",
+                "    }",
+                "    toJSON() {",
+                "        return {",
+                "            type: this.type,",
+                "            size: this.size.toJSON(),",
+                "        };",
+                "    }",
+                "    static fromJSON(json) {",
+                "        return new SizeOverLife(ValueGeneratorFromJSON(json.size));",
+                "    }",
+                "    frameUpdate(delta) {",
+                "    }",
+                "    clone() {",
+                "        return new SizeOverLife(this.size.clone());",
+                "    }",
+                "    reset() {",
+                "    }",
+                "}",
+                "",
+                "class SpeedOverLife {",
+                "    initialize(particle) {",
+                "    }",
+                "    constructor(speed) {",
+                "        this.speed = speed;",
+                "        this.type = 'SpeedOverLife';",
+                "    }",
+                "    update(particle) {",
+                "        particle.speedModifier = this.speed.genValue(particle.age / particle.life);",
+                "    }",
+                "    toJSON() {",
+                "        return {",
+                "            type: this.type,",
+                "            speed: this.speed.toJSON(),",
+                "        };",
+                "    }",
+                "    static fromJSON(json) {",
+                "        return new SpeedOverLife(ValueGeneratorFromJSON(json.speed));",
+                "    }",
+                "    frameUpdate(delta) {",
+                "    }",
+                "    clone() {",
+                "        return new SpeedOverLife(this.speed.clone());",
+                "    }",
+                "    reset() {",
+                "    }",
+                "}",
+                "",
+                "class FrameOverLife {",
+                "    constructor(frame) {",
+                "        this.frame = frame;",
+                "        this.type = 'FrameOverLife';",
+                "    }",
+                "    initialize(particle) {",
+                "        if (!(this.frame instanceof PiecewiseBezier)) {",
+                "            particle.uvTile = Math.floor(this.frame.genValue(0) + 0.001);",
+                "        }",
+                "    }",
+                "    update(particle, delta) {",
+                "        if (this.frame instanceof PiecewiseBezier) {",
+                "            particle.uvTile = Math.floor(this.frame.genValue(particle.age / particle.life) + 0.001);",
+                "        }",
+                "    }",
+                "    frameUpdate(delta) { }",
+                "    toJSON() {",
+                "        return {",
+                "            type: this.type,",
+                "            frame: this.frame.toJSON(),",
+                "        };",
+                "    }",
+                "    static fromJSON(json) {",
+                "        return new FrameOverLife(ValueGeneratorFromJSON(json.frame));",
+                "    }",
+                "    clone() {",
+                "        return new FrameOverLife(this.frame.clone());",
+                "    }",
+                "    reset() { }",
+                "}",
+                "",
+                "new THREE.Vector3(0, 0, 1);",
+                "class OrbitOverLife {",
+                "    constructor(orbitSpeed, axis = new THREE.Vector3(0, 1, 0)) {",
+                "        this.orbitSpeed = orbitSpeed;",
+                "        this.axis = axis;",
+                "        this.type = 'OrbitOverLife';",
+                "        this.temp = new THREE.Vector3();",
+                "        this.rotation = new THREE.Quaternion();",
+                "        this.line = new THREE.Line3();",
+                "    }",
+                "    initialize(particle) {",
+                "    }",
+                "    update(particle, delta) {",
+                "        this.line.set(new THREE.Vector3(0, 0, 0), this.axis);",
+                "        this.line.closestPointToPoint(particle.position, false, this.temp);",
+                "        this.rotation.setFromAxisAngle(this.axis, this.orbitSpeed.genValue(particle.age / particle.life) * delta);",
+                "        particle.position.sub(this.temp);",
+                "        particle.position.applyQuaternion(this.rotation);",
+                "        particle.position.add(this.temp);",
+                "    }",
+                "    frameUpdate(delta) {",
+                "    }",
+                "    toJSON() {",
+                "        return {",
+                "            type: this.type,",
+                "            orbitSpeed: this.orbitSpeed.toJSON(),",
+                "            axis: [this.axis.x, this.axis.y, this.axis.z],",
+                "        };",
+                "    }",
+                "    static fromJSON(json) {",
+                "        return new OrbitOverLife(ValueGeneratorFromJSON(json.orbitSpeed), json.axis ? new THREE.Vector3(json.axis[0], json.axis[1], json.axis[2]) : undefined);",
+                "    }",
+                "    clone() {",
+                "        return new OrbitOverLife(this.orbitSpeed.clone());",
+                "    }",
+                "    reset() {",
+                "    }",
+                "}",
+                "",
+                "class WidthOverLength {",
+                "    initialize(particle) {",
+                "    }",
+                "    constructor(width) {",
+                "        this.width = width;",
+                "        this.type = 'WidthOverLength';",
+                "    }",
+                "    update(particle) {",
+                "        if (particle instanceof TrailParticle) {",
+                "            const iter = particle.previous.values();",
+                "            for (let i = 0; i < particle.previous.length; i++) {",
+                "                const cur = iter.next();",
+                "                cur.value.size = this.width.genValue((particle.previous.length - i) / particle.length);",
+                "            }",
+                "        }",
+                "    }",
+                "    frameUpdate(delta) {",
+                "    }",
+                "    toJSON() {",
+                "        return {",
+                "            type: this.type,",
+                "            width: this.width.toJSON(),",
+                "        };",
+                "    }",
+                "    static fromJSON(json) {",
+                "        return new WidthOverLength(ValueGeneratorFromJSON(json.width));",
+                "    }",
+                "    clone() {",
+                "        return new WidthOverLength(this.width.clone());",
+                "    }",
+                "    reset() {",
+                "    }",
+                "}",
+                "",
+                "class ApplyForce {",
+                "    constructor(direction, magnitude) {",
+                "        this.direction = direction;",
+                "        this.magnitude = magnitude;",
+                "        this.type = 'ApplyForce';",
+                "        this.magnitudeValue = this.magnitude.genValue();",
+                "    }",
+                "    initialize(particle) {",
+                "    }",
+                "    update(particle, delta) {",
+                "        particle.velocity.addScaledVector(this.direction, this.magnitudeValue * delta);",
+                "    }",
+                "    frameUpdate(delta) {",
+                "        this.magnitudeValue = this.magnitude.genValue();",
+                "    }",
+                "    toJSON() {",
+                "        return {",
+                "            type: this.type,",
+                "            direction: [this.direction.x, this.direction.y, this.direction.z],",
+                "            magnitude: this.magnitude.toJSON(),",
+                "        };",
+                "    }",
+                "    static fromJSON(json) {",
+                "        var _a;",
+                "        return new ApplyForce(new THREE.Vector3(json.direction[0], json.direction[1], json.direction[2]), ValueGeneratorFromJSON((_a = json.magnitude) !== null && _a !== void 0 ? _a : json.force));",
+                "    }",
+                "    clone() {",
+                "        return new ApplyForce(this.direction.clone(), this.magnitude.clone());",
+                "    }",
+                "    reset() {",
+                "    }",
+                "}",
+                "",
+                "class GravityForce {",
+                "    constructor(center, magnitude) {",
+                "        this.center = center;",
+                "        this.magnitude = magnitude;",
+                "        this.type = 'GravityForce';",
+                "        this.temp = new THREE.Vector3();",
+                "    }",
+                "    initialize(particle) {",
+                "    }",
+                "    update(particle, delta) {",
+                "        this.temp.copy(this.center).sub(particle.position).normalize();",
+                "        particle.velocity.addScaledVector(this.temp, this.magnitude / particle.position.distanceToSquared(this.center) * delta);",
+                "    }",
+                "    frameUpdate(delta) {",
+                "    }",
+                "    toJSON() {",
+                "        return {",
+                "            type: this.type,",
+                "            center: [this.center.x, this.center.y, this.center.z],",
+                "            magnitude: this.magnitude,",
+                "        };",
+                "    }",
+                "    static fromJSON(json) {",
+                "        return new GravityForce(new THREE.Vector3(json.center[0], json.center[1], json.center[2]), json.magnitude);",
+                "    }",
+                "    clone() {",
+                "        return new GravityForce(this.center.clone(), this.magnitude);",
+                "    }",
+                "    reset() {",
+                "    }",
+                "}",
+                "",
+                "new THREE.Vector3(0, 0, 1);",
+                "class ChangeEmitDirection {",
+                "    constructor(angle) {",
+                "        this.angle = angle;",
+                "        this.type = 'ChangeEmitDirection';",
+                "        this._temp = new THREE.Vector3();",
+                "        this._q = new THREE.Quaternion();",
+                "    }",
+                "    initialize(particle) {",
+                "        const len = particle.velocity.length();",
+                "        if (len == 0)",
+                "            return;",
+                "        particle.velocity.normalize();",
+                "        if (particle.velocity.x === 0 && particle.velocity.y === 0) {",
+                "            this._temp.set(0, particle.velocity.z, 0);",
+                "        }",
+                "        else {",
+                "            this._temp.set(-particle.velocity.y, particle.velocity.x, 0);",
+                "        }",
+                "        this._q.setFromAxisAngle(this._temp.normalize(), this.angle.genValue());",
+                "        this._temp.copy(particle.velocity);",
+                "        particle.velocity.applyQuaternion(this._q);",
+                "        this._q.setFromAxisAngle(this._temp, Math.random() * Math.PI * 2);",
+                "        particle.velocity.applyQuaternion(this._q);",
+                "        particle.velocity.setLength(len);",
+                "    }",
+                "    update(particle, delta) {",
+                "    }",
+                "    frameUpdate(delta) {",
+                "    }",
+                "    toJSON() {",
+                "        return {",
+                "            type: this.type,",
+                "            angle: this.angle.toJSON(),",
+                "        };",
+                "    }",
+                "    static fromJSON(json) {",
+                "        return new ChangeEmitDirection(ValueGeneratorFromJSON(json.angle));",
+                "    }",
+                "    clone() {",
+                "        return new ChangeEmitDirection(this.angle);",
+                "    }",
+                "    reset() {",
+                "    }",
+                "}",
+                "",
+                "const VECTOR_ONE = new THREE.Vector3(1, 1, 1);",
+                "const VECTOR_Z = new THREE.Vector3(0, 0, 1);",
+                "let SubParticleEmitMode = void 0;",
+                "(function (SubParticleEmitMode) {",
+                "    SubParticleEmitMode[SubParticleEmitMode[\"Death\"] = 0] = \"Death\";",
+                "    SubParticleEmitMode[SubParticleEmitMode[\"Birth\"] = 1] = \"Birth\";",
+                "    SubParticleEmitMode[SubParticleEmitMode[\"Frame\"] = 2] = \"Frame\";",
+                "})(SubParticleEmitMode || (SubParticleEmitMode = {}));",
+                "class EmitSubParticleSystem {",
+                "    constructor(particleSystem, useVelocityAsBasis, subParticleSystem, mode = SubParticleEmitMode.Frame, emitProbability = 1) {",
+                "        this.particleSystem = particleSystem;",
+                "        this.useVelocityAsBasis = useVelocityAsBasis;",
+                "        this.subParticleSystem = subParticleSystem;",
+                "        this.mode = mode;",
+                "        this.emitProbability = emitProbability;",
+                "        this.type = 'EmitSubParticleSystem';",
+                "        this.q_ = new THREE.Quaternion();",
+                "        this.v_ = new THREE.Vector3();",
+                "        this.v2_ = new THREE.Vector3();",
+                "        this.subEmissions = new Array();",
+                "        if (this.subParticleSystem && this.subParticleSystem.system) {",
+                "            this.subParticleSystem.system.onlyUsedByOther = true;",
+                "        }",
+                "    }",
+                "    initialize(particle) {",
+                "    }",
+                "    update(particle, delta) {",
+                "        if (this.mode === SubParticleEmitMode.Frame) {",
+                "            this.emit(particle, delta);",
+                "        }",
+                "        else if (this.mode === SubParticleEmitMode.Birth && particle.age === 0) {",
+                "            this.emit(particle, delta);",
+                "        }",
+                "        else if (this.mode === SubParticleEmitMode.Death && particle.age + delta >= particle.life) {",
+                "            this.emit(particle, delta);",
+                "        }",
+                "    }",
+                "    emit(particle, delta) {",
+                "        if (!this.subParticleSystem)",
+                "            return;",
+                "        if (Math.random() > this.emitProbability) {",
+                "            return;",
+                "        }",
+                "        const m = new THREE.Matrix4();",
+                "        this.setMatrixFromParticle(m, particle);",
+                "        this.subEmissions.push({",
+                "            burstIndex: 0,",
+                "            burstWaveIndex: 0,",
+                "            time: 0,",
+                "            waitEmiting: 0,",
+                "            matrix: m,",
+                "            travelDistance: 0,",
+                "            particle: particle,",
+                "        });",
+                "    }",
+                "    frameUpdate(delta) {",
+                "        if (!this.subParticleSystem)",
+                "            return;",
+                "        for (let i = 0; i < this.subEmissions.length; i++) {",
+                "            if (this.subEmissions[i].time >= this.subParticleSystem.system.duration) {",
+                "                this.subEmissions[i] = this.subEmissions[this.subEmissions.length - 1];",
+                "                this.subEmissions.length = this.subEmissions.length - 1;",
+                "                i--;",
+                "            }",
+                "            else {",
+                "                let subEmissionState = this.subEmissions[i];",
+                "                if (subEmissionState.particle && subEmissionState.particle.age < subEmissionState.particle.life) {",
+                "                    this.setMatrixFromParticle(subEmissionState.matrix, subEmissionState.particle);",
+                "                }",
+                "                else {",
+                "                    subEmissionState.particle = undefined;",
+                "                }",
+                "                this.subParticleSystem.system.emit(delta, subEmissionState, subEmissionState.matrix);",
+                "            }",
+                "        }",
+                "    }",
+                "    toJSON() {",
+                "        return {",
+                "            type: this.type,",
+                "            subParticleSystem: this.subParticleSystem ? this.subParticleSystem.uuid : '',",
+                "            useVelocityAsBasis: this.useVelocityAsBasis,",
+                "            mode: this.mode,",
+                "            emitProbability: this.emitProbability,",
+                "        };",
+                "    }",
+                "    static fromJSON(json, particleSystem) {",
+                "        return new EmitSubParticleSystem(particleSystem, json.useVelocityAsBasis, json.subParticleSystem, json.mode, json.emitProbability);",
+                "    }",
+                "    clone() {",
+                "        return new EmitSubParticleSystem(this.particleSystem, this.useVelocityAsBasis, this.subParticleSystem, this.mode, this.emitProbability);",
+                "    }",
+                "    reset() { }",
+                "    setMatrixFromParticle(m, particle) {",
+                "        let rotation;",
+                "        if (particle.rotation === undefined || this.useVelocityAsBasis) {",
+                "            if (particle.velocity.x === 0 &&",
+                "                particle.velocity.y === 0 &&",
+                "                (particle.velocity.z === 1 || particle.velocity.z === 0)) {",
+                "                m.set(1, 0, 0, particle.position.x, 0, 1, 0, particle.position.y, 0, 0, 1, particle.position.z, 0, 0, 0, 1);",
+                "            }",
+                "            else {",
+                "                this.v_.copy(VECTOR_Z).cross(particle.velocity);",
+                "                this.v2_.copy(particle.velocity).cross(this.v_);",
+                "                const len = this.v_.length();",
+                "                const len2 = this.v2_.length();",
+                "                m.set(this.v_.x / len, this.v2_.x / len2, particle.velocity.x, particle.position.x, this.v_.y / len, this.v2_.y / len2, particle.velocity.y, particle.position.y, this.v_.z / len, this.v2_.z / len2, particle.velocity.z, particle.position.z, 0, 0, 0, 1);",
+                "            }",
+                "        }",
+                "        else {",
+                "            if (particle.rotation instanceof THREE.Quaternion) {",
+                "                rotation = particle.rotation;",
+                "            }",
+                "            else {",
+                "                this.q_.setFromAxisAngle(VECTOR_Z, particle.rotation);",
+                "                rotation = this.q_;",
+                "            }",
+                "            m.compose(particle.position, rotation, VECTOR_ONE);",
+                "        }",
+                "        if (!this.particleSystem.worldSpace) {",
+                "            m.multiplyMatrices(this.particleSystem.emitter.matrixWorld, m);",
+                "        }",
+                "    }",
+                "}",
+                "",
+                "const F2 = 0.5 * (Math.sqrt(3.0) - 1.0);",
+                "const G2 = (3.0 - Math.sqrt(3.0)) / 6.0;",
+                "const F3 = 1.0 / 3.0;",
+                "const G3 = 1.0 / 6.0;",
+                "const F4 = (Math.sqrt(5.0) - 1.0) / 4.0;",
+                "const G4 = (5.0 - Math.sqrt(5.0)) / 20.0;",
+                "const grad3 = new Float32Array([1, 1, 0,",
+                "    -1, 1, 0,",
+                "    1, -1, 0,",
+                "    -1, -1, 0,",
+                "    1, 0, 1,",
+                "    -1, 0, 1,",
+                "    1, 0, -1,",
+                "    -1, 0, -1,",
+                "    0, 1, 1,",
+                "    0, -1, 1,",
+                "    0, 1, -1,",
+                "    0, -1, -1]);",
+                "const grad4 = new Float32Array([0, 1, 1, 1, 0, 1, 1, -1, 0, 1, -1, 1, 0, 1, -1, -1,",
+                "    0, -1, 1, 1, 0, -1, 1, -1, 0, -1, -1, 1, 0, -1, -1, -1,",
+                "    1, 0, 1, 1, 1, 0, 1, -1, 1, 0, -1, 1, 1, 0, -1, -1,",
+                "    -1, 0, 1, 1, -1, 0, 1, -1, -1, 0, -1, 1, -1, 0, -1, -1,",
+                "    1, 1, 0, 1, 1, 1, 0, -1, 1, -1, 0, 1, 1, -1, 0, -1,",
+                "    -1, 1, 0, 1, -1, 1, 0, -1, -1, -1, 0, 1, -1, -1, 0, -1,",
+                "    1, 1, 1, 0, 1, 1, -1, 0, 1, -1, 1, 0, 1, -1, -1, 0,",
+                "    -1, 1, 1, 0, -1, 1, -1, 0, -1, -1, 1, 0, -1, -1, -1, 0]);",
+                "class SimplexNoise {",
+                "    constructor(randomOrSeed = Math.random) {",
+                "        const random = typeof randomOrSeed == 'function' ? randomOrSeed : alea(randomOrSeed);",
+                "        this.p = buildPermutationTable(random);",
+                "        this.perm = new Uint8Array(512);",
+                "        this.permMod12 = new Uint8Array(512);",
+                "        for (let i = 0; i < 512; i++) {",
+                "            this.perm[i] = this.p[i & 255];",
+                "            this.permMod12[i] = this.perm[i] % 12;",
+                "        }",
+                "    }",
+                "    noise2D(x, y) {",
+                "        const permMod12 = this.permMod12;",
+                "        const perm = this.perm;",
+                "        let n0 = 0;",
+                "        let n1 = 0;",
+                "        let n2 = 0;",
+                "        const s = (x + y) * F2;",
+                "        const i = Math.floor(x + s);",
+                "        const j = Math.floor(y + s);",
+                "        const t = (i + j) * G2;",
+                "        const X0 = i - t;",
+                "        const Y0 = j - t;",
+                "        const x0 = x - X0;",
+                "        const y0 = y - Y0;",
+                "        let i1, j1;",
+                "        if (x0 > y0) {",
+                "            i1 = 1;",
+                "            j1 = 0;",
+                "        }",
+                "        else {",
+                "            i1 = 0;",
+                "            j1 = 1;",
+                "        }",
+                "        const x1 = x0 - i1 + G2;",
+                "        const y1 = y0 - j1 + G2;",
+                "        const x2 = x0 - 1.0 + 2.0 * G2;",
+                "        const y2 = y0 - 1.0 + 2.0 * G2;",
+                "        const ii = i & 255;",
+                "        const jj = j & 255;",
+                "        let t0 = 0.5 - x0 * x0 - y0 * y0;",
+                "        if (t0 >= 0) {",
+                "            const gi0 = permMod12[ii + perm[jj]] * 3;",
+                "            t0 *= t0;",
+                "            n0 = t0 * t0 * (grad3[gi0] * x0 + grad3[gi0 + 1] * y0);",
+                "        }",
+                "        let t1 = 0.5 - x1 * x1 - y1 * y1;",
+                "        if (t1 >= 0) {",
+                "            const gi1 = permMod12[ii + i1 + perm[jj + j1]] * 3;",
+                "            t1 *= t1;",
+                "            n1 = t1 * t1 * (grad3[gi1] * x1 + grad3[gi1 + 1] * y1);",
+                "        }",
+                "        let t2 = 0.5 - x2 * x2 - y2 * y2;",
+                "        if (t2 >= 0) {",
+                "            const gi2 = permMod12[ii + 1 + perm[jj + 1]] * 3;",
+                "            t2 *= t2;",
+                "            n2 = t2 * t2 * (grad3[gi2] * x2 + grad3[gi2 + 1] * y2);",
+                "        }",
+                "        return 70.0 * (n0 + n1 + n2);",
+                "    }",
+                "    noise3D(x, y, z) {",
+                "        const permMod12 = this.permMod12;",
+                "        const perm = this.perm;",
+                "        let n0, n1, n2, n3;",
+                "        const s = (x + y + z) * F3;",
+                "        const i = Math.floor(x + s);",
+                "        const j = Math.floor(y + s);",
+                "        const k = Math.floor(z + s);",
+                "        const t = (i + j + k) * G3;",
+                "        const X0 = i - t;",
+                "        const Y0 = j - t;",
+                "        const Z0 = k - t;",
+                "        const x0 = x - X0;",
+                "        const y0 = y - Y0;",
+                "        const z0 = z - Z0;",
+                "        let i1, j1, k1;",
+                "        let i2, j2, k2;",
+                "        if (x0 >= y0) {",
+                "            if (y0 >= z0) {",
+                "                i1 = 1;",
+                "                j1 = 0;",
+                "                k1 = 0;",
+                "                i2 = 1;",
+                "                j2 = 1;",
+                "                k2 = 0;",
+                "            }",
+                "            else if (x0 >= z0) {",
+                "                i1 = 1;",
+                "                j1 = 0;",
+                "                k1 = 0;",
+                "                i2 = 1;",
+                "                j2 = 0;",
+                "                k2 = 1;",
+                "            }",
+                "            else {",
+                "                i1 = 0;",
+                "                j1 = 0;",
+                "                k1 = 1;",
+                "                i2 = 1;",
+                "                j2 = 0;",
+                "                k2 = 1;",
+                "            }",
+                "        }",
+                "        else {",
+                "            if (y0 < z0) {",
+                "                i1 = 0;",
+                "                j1 = 0;",
+                "                k1 = 1;",
+                "                i2 = 0;",
+                "                j2 = 1;",
+                "                k2 = 1;",
+                "            }",
+                "            else if (x0 < z0) {",
+                "                i1 = 0;",
+                "                j1 = 1;",
+                "                k1 = 0;",
+                "                i2 = 0;",
+                "                j2 = 1;",
+                "                k2 = 1;",
+                "            }",
+                "            else {",
+                "                i1 = 0;",
+                "                j1 = 1;",
+                "                k1 = 0;",
+                "                i2 = 1;",
+                "                j2 = 1;",
+                "                k2 = 0;",
+                "            }",
+                "        }",
+                "        const x1 = x0 - i1 + G3;",
+                "        const y1 = y0 - j1 + G3;",
+                "        const z1 = z0 - k1 + G3;",
+                "        const x2 = x0 - i2 + 2.0 * G3;",
+                "        const y2 = y0 - j2 + 2.0 * G3;",
+                "        const z2 = z0 - k2 + 2.0 * G3;",
+                "        const x3 = x0 - 1.0 + 3.0 * G3;",
+                "        const y3 = y0 - 1.0 + 3.0 * G3;",
+                "        const z3 = z0 - 1.0 + 3.0 * G3;",
+                "        const ii = i & 255;",
+                "        const jj = j & 255;",
+                "        const kk = k & 255;",
+                "        let t0 = 0.6 - x0 * x0 - y0 * y0 - z0 * z0;",
+                "        if (t0 < 0)",
+                "            n0 = 0.0;",
+                "        else {",
+                "            const gi0 = permMod12[ii + perm[jj + perm[kk]]] * 3;",
+                "            t0 *= t0;",
+                "            n0 = t0 * t0 * (grad3[gi0] * x0 + grad3[gi0 + 1] * y0 + grad3[gi0 + 2] * z0);",
+                "        }",
+                "        let t1 = 0.6 - x1 * x1 - y1 * y1 - z1 * z1;",
+                "        if (t1 < 0)",
+                "            n1 = 0.0;",
+                "        else {",
+                "            const gi1 = permMod12[ii + i1 + perm[jj + j1 + perm[kk + k1]]] * 3;",
+                "            t1 *= t1;",
+                "            n1 = t1 * t1 * (grad3[gi1] * x1 + grad3[gi1 + 1] * y1 + grad3[gi1 + 2] * z1);",
+                "        }",
+                "        let t2 = 0.6 - x2 * x2 - y2 * y2 - z2 * z2;",
+                "        if (t2 < 0)",
+                "            n2 = 0.0;",
+                "        else {",
+                "            const gi2 = permMod12[ii + i2 + perm[jj + j2 + perm[kk + k2]]] * 3;",
+                "            t2 *= t2;",
+                "            n2 = t2 * t2 * (grad3[gi2] * x2 + grad3[gi2 + 1] * y2 + grad3[gi2 + 2] * z2);",
+                "        }",
+                "        let t3 = 0.6 - x3 * x3 - y3 * y3 - z3 * z3;",
+                "        if (t3 < 0)",
+                "            n3 = 0.0;",
+                "        else {",
+                "            const gi3 = permMod12[ii + 1 + perm[jj + 1 + perm[kk + 1]]] * 3;",
+                "            t3 *= t3;",
+                "            n3 = t3 * t3 * (grad3[gi3] * x3 + grad3[gi3 + 1] * y3 + grad3[gi3 + 2] * z3);",
+                "        }",
+                "        return 32.0 * (n0 + n1 + n2 + n3);",
+                "    }",
+                "    noise4D(x, y, z, w) {",
+                "        const perm = this.perm;",
+                "        let n0, n1, n2, n3, n4;",
+                "        const s = (x + y + z + w) * F4;",
+                "        const i = Math.floor(x + s);",
+                "        const j = Math.floor(y + s);",
+                "        const k = Math.floor(z + s);",
+                "        const l = Math.floor(w + s);",
+                "        const t = (i + j + k + l) * G4;",
+                "        const X0 = i - t;",
+                "        const Y0 = j - t;",
+                "        const Z0 = k - t;",
+                "        const W0 = l - t;",
+                "        const x0 = x - X0;",
+                "        const y0 = y - Y0;",
+                "        const z0 = z - Z0;",
+                "        const w0 = w - W0;",
+                "        let rankx = 0;",
+                "        let ranky = 0;",
+                "        let rankz = 0;",
+                "        let rankw = 0;",
+                "        if (x0 > y0)",
+                "            rankx++;",
+                "        else",
+                "            ranky++;",
+                "        if (x0 > z0)",
+                "            rankx++;",
+                "        else",
+                "            rankz++;",
+                "        if (x0 > w0)",
+                "            rankx++;",
+                "        else",
+                "            rankw++;",
+                "        if (y0 > z0)",
+                "            ranky++;",
+                "        else",
+                "            rankz++;",
+                "        if (y0 > w0)",
+                "            ranky++;",
+                "        else",
+                "            rankw++;",
+                "        if (z0 > w0)",
+                "            rankz++;",
+                "        else",
+                "            rankw++;",
+                "        const i1 = rankx >= 3 ? 1 : 0;",
+                "        const j1 = ranky >= 3 ? 1 : 0;",
+                "        const k1 = rankz >= 3 ? 1 : 0;",
+                "        const l1 = rankw >= 3 ? 1 : 0;",
+                "        const i2 = rankx >= 2 ? 1 : 0;",
+                "        const j2 = ranky >= 2 ? 1 : 0;",
+                "        const k2 = rankz >= 2 ? 1 : 0;",
+                "        const l2 = rankw >= 2 ? 1 : 0;",
+                "        const i3 = rankx >= 1 ? 1 : 0;",
+                "        const j3 = ranky >= 1 ? 1 : 0;",
+                "        const k3 = rankz >= 1 ? 1 : 0;",
+                "        const l3 = rankw >= 1 ? 1 : 0;",
+                "        const x1 = x0 - i1 + G4;",
+                "        const y1 = y0 - j1 + G4;",
+                "        const z1 = z0 - k1 + G4;",
+                "        const w1 = w0 - l1 + G4;",
+                "        const x2 = x0 - i2 + 2.0 * G4;",
+                "        const y2 = y0 - j2 + 2.0 * G4;",
+                "        const z2 = z0 - k2 + 2.0 * G4;",
+                "        const w2 = w0 - l2 + 2.0 * G4;",
+                "        const x3 = x0 - i3 + 3.0 * G4;",
+                "        const y3 = y0 - j3 + 3.0 * G4;",
+                "        const z3 = z0 - k3 + 3.0 * G4;",
+                "        const w3 = w0 - l3 + 3.0 * G4;",
+                "        const x4 = x0 - 1.0 + 4.0 * G4;",
+                "        const y4 = y0 - 1.0 + 4.0 * G4;",
+                "        const z4 = z0 - 1.0 + 4.0 * G4;",
+                "        const w4 = w0 - 1.0 + 4.0 * G4;",
+                "        const ii = i & 255;",
+                "        const jj = j & 255;",
+                "        const kk = k & 255;",
+                "        const ll = l & 255;",
+                "        let t0 = 0.6 - x0 * x0 - y0 * y0 - z0 * z0 - w0 * w0;",
+                "        if (t0 < 0)",
+                "            n0 = 0.0;",
+                "        else {",
+                "            const gi0 = (perm[ii + perm[jj + perm[kk + perm[ll]]]] % 32) * 4;",
+                "            t0 *= t0;",
+                "            n0 = t0 * t0 * (grad4[gi0] * x0 + grad4[gi0 + 1] * y0 + grad4[gi0 + 2] * z0 + grad4[gi0 + 3] * w0);",
+                "        }",
+                "        let t1 = 0.6 - x1 * x1 - y1 * y1 - z1 * z1 - w1 * w1;",
+                "        if (t1 < 0)",
+                "            n1 = 0.0;",
+                "        else {",
+                "            const gi1 = (perm[ii + i1 + perm[jj + j1 + perm[kk + k1 + perm[ll + l1]]]] % 32) * 4;",
+                "            t1 *= t1;",
+                "            n1 = t1 * t1 * (grad4[gi1] * x1 + grad4[gi1 + 1] * y1 + grad4[gi1 + 2] * z1 + grad4[gi1 + 3] * w1);",
+                "        }",
+                "        let t2 = 0.6 - x2 * x2 - y2 * y2 - z2 * z2 - w2 * w2;",
+                "        if (t2 < 0)",
+                "            n2 = 0.0;",
+                "        else {",
+                "            const gi2 = (perm[ii + i2 + perm[jj + j2 + perm[kk + k2 + perm[ll + l2]]]] % 32) * 4;",
+                "            t2 *= t2;",
+                "            n2 = t2 * t2 * (grad4[gi2] * x2 + grad4[gi2 + 1] * y2 + grad4[gi2 + 2] * z2 + grad4[gi2 + 3] * w2);",
+                "        }",
+                "        let t3 = 0.6 - x3 * x3 - y3 * y3 - z3 * z3 - w3 * w3;",
+                "        if (t3 < 0)",
+                "            n3 = 0.0;",
+                "        else {",
+                "            const gi3 = (perm[ii + i3 + perm[jj + j3 + perm[kk + k3 + perm[ll + l3]]]] % 32) * 4;",
+                "            t3 *= t3;",
+                "            n3 = t3 * t3 * (grad4[gi3] * x3 + grad4[gi3 + 1] * y3 + grad4[gi3 + 2] * z3 + grad4[gi3 + 3] * w3);",
+                "        }",
+                "        let t4 = 0.6 - x4 * x4 - y4 * y4 - z4 * z4 - w4 * w4;",
+                "        if (t4 < 0)",
+                "            n4 = 0.0;",
+                "        else {",
+                "            const gi4 = (perm[ii + 1 + perm[jj + 1 + perm[kk + 1 + perm[ll + 1]]]] % 32) * 4;",
+                "            t4 *= t4;",
+                "            n4 = t4 * t4 * (grad4[gi4] * x4 + grad4[gi4 + 1] * y4 + grad4[gi4 + 2] * z4 + grad4[gi4 + 3] * w4);",
+                "        }",
+                "        return 27.0 * (n0 + n1 + n2 + n3 + n4);",
+                "    }",
+                "}",
+                "function buildPermutationTable(random) {",
+                "    const p = new Uint8Array(256);",
+                "    for (let i = 0; i < 256; i++) {",
+                "        p[i] = i;",
+                "    }",
+                "    for (let i = 0; i < 255; i++) {",
+                "        const r = i + ~~(random() * (256 - i));",
+                "        const aux = p[i];",
+                "        p[i] = p[r];",
+                "        p[r] = aux;",
+                "    }",
+                "    return p;",
+                "}",
+                "function alea(seed) {",
+                "    let s0 = 0;",
+                "    let s1 = 0;",
+                "    let s2 = 0;",
+                "    let c = 1;",
+                "    const mash = masher();",
+                "    s0 = mash(' ');",
+                "    s1 = mash(' ');",
+                "    s2 = mash(' ');",
+                "    s0 -= mash(seed);",
+                "    if (s0 < 0) {",
+                "        s0 += 1;",
+                "    }",
+                "    s1 -= mash(seed);",
+                "    if (s1 < 0) {",
+                "        s1 += 1;",
+                "    }",
+                "    s2 -= mash(seed);",
+                "    if (s2 < 0) {",
+                "        s2 += 1;",
+                "    }",
+                "    return function () {",
+                "        const t = 2091639 * s0 + c * 2.3283064365386963e-10;",
+                "        s0 = s1;",
+                "        s1 = s2;",
+                "        return s2 = t - (c = t | 0);",
+                "    };",
+                "}",
+                "function masher() {",
+                "    let n = 0xefc8249d;",
+                "    return function (data) {",
+                "        data = data.toString();",
+                "        for (let i = 0; i < data.length; i++) {",
+                "            n += data.charCodeAt(i);",
+                "            let h = 0.02519603282416938 * n;",
+                "            n = h >>> 0;",
+                "            h -= n;",
+                "            h *= n;",
+                "            n = h >>> 0;",
+                "            h -= n;",
+                "            n += h * 0x100000000;",
+                "        }",
+                "        return (n >>> 0) * 2.3283064365386963e-10;",
+                "    };",
+                "}",
+                "",
+                "class TurbulenceField {",
+                "    constructor(scale, octaves, velocityMultiplier, timeScale) {",
+                "        this.scale = scale;",
+                "        this.octaves = octaves;",
+                "        this.velocityMultiplier = velocityMultiplier;",
+                "        this.timeScale = timeScale;",
+                "        this.type = 'TurbulenceField';",
+                "        this.generator = new SimplexNoise();",
+                "        this.timeOffset = new THREE.Vector3();",
+                "        this.temp = new THREE.Vector3();",
+                "        this.temp2 = new THREE.Vector3();",
+                "        this.timeOffset.x = Math.random() / this.scale.x * this.timeScale.x;",
+                "        this.timeOffset.y = Math.random() / this.scale.y * this.timeScale.y;",
+                "        this.timeOffset.z = Math.random() / this.scale.z * this.timeScale.z;",
+                "    }",
+                "    initialize(particle) {",
+                "    }",
+                "    update(particle, delta) {",
+                "        const x = particle.position.x / this.scale.x;",
+                "        const y = particle.position.y / this.scale.y;",
+                "        const z = particle.position.z / this.scale.z;",
+                "        this.temp.set(0, 0, 0);",
+                "        let lvl = 1;",
+                "        for (let i = 0; i < this.octaves; i++) {",
+                "            this.temp2.set(this.generator.noise4D(x * lvl, y * lvl, z * lvl, this.timeOffset.x * lvl) / lvl, this.generator.noise4D(x * lvl, y * lvl, z * lvl, this.timeOffset.y * lvl) / lvl, this.generator.noise4D(x * lvl, y * lvl, z * lvl, this.timeOffset.z * lvl) / lvl);",
+                "            this.temp.add(this.temp2);",
+                "            lvl *= 2;",
+                "        }",
+                "        this.temp.multiply(this.velocityMultiplier);",
+                "        particle.velocity.addScaledVector(this.temp, delta);",
+                "    }",
+                "    toJSON() {",
+                "        return {",
+                "            type: this.type,",
+                "            scale: [this.scale.x, this.scale.y, this.scale.z],",
+                "            octaves: this.octaves,",
+                "            velocityMultiplier: [this.velocityMultiplier.x, this.velocityMultiplier.y, this.velocityMultiplier.z],",
+                "            timeScale: [this.timeScale.x, this.timeScale.y, this.timeScale.z],",
+                "        };",
+                "    }",
+                "    frameUpdate(delta) {",
+                "        this.timeOffset.x += delta * this.timeScale.x;",
+                "        this.timeOffset.y += delta * this.timeScale.y;",
+                "        this.timeOffset.z += delta * this.timeScale.z;",
+                "    }",
+                "    static fromJSON(json) {",
+                "        return new TurbulenceField(new THREE.Vector3(json.scale[0], json.scale[1], json.scale[2]), json.octaves, new THREE.Vector3(json.velocityMultiplier[0], json.velocityMultiplier[1], json.velocityMultiplier[2]), new THREE.Vector3(json.timeScale[0], json.timeScale[1], json.timeScale[2]));",
+                "    }",
+                "    clone() {",
+                "        return new TurbulenceField(this.scale.clone(), this.octaves, this.velocityMultiplier.clone(), this.timeScale.clone());",
+                "    }",
+                "    reset() {",
+                "    }",
+                "}",
+                "",
+                "function randomInt(a, b) {",
+                "    return Math.floor(Math.random() * (b - a)) + a;",
+                "}",
+                "",
+                "const generators = [];",
+                "const tempV$2 = new THREE.Vector3();",
+                "const tempQ$2 = new THREE.Quaternion();",
+                "class Noise {",
+                "    constructor(frequency, power, positionAmount = new ConstantValue(1), rotationAmount = new ConstantValue(0)) {",
+                "        this.frequency = frequency;",
+                "        this.power = power;",
+                "        this.positionAmount = positionAmount;",
+                "        this.rotationAmount = rotationAmount;",
+                "        this.type = 'Noise';",
+                "        this.duration = 0;",
+                "        if (generators.length === 0) {",
+                "            for (let i = 0; i < 100; i++) {",
+                "                generators.push(new SimplexNoise());",
+                "            }",
+                "        }",
+                "    }",
+                "    initialize(particle) {",
+                "        particle.lastPosNoise = new THREE.Vector3();",
+                "        if (typeof particle.rotation === 'number') {",
+                "            particle.lastRotNoise = 0;",
+                "        }",
+                "        else {",
+                "            particle.lastRotNoise = new THREE.Quaternion();",
+                "        }",
+                "        particle.generatorIndex = [randomInt(0, 100), randomInt(0, 100), randomInt(0, 100), randomInt(0, 100)];",
+                "    }",
+                "    update(particle, _) {",
+                "        let frequency = this.frequency.genValue(particle.age / particle.life);",
+                "        let power = this.power.genValue(particle.age / particle.life);",
+                "        let positionAmount = this.positionAmount.genValue(particle.age / particle.life);",
+                "        let rotationAmount = this.rotationAmount.genValue(particle.age / particle.life);",
+                "        if (positionAmount > 0 && particle.lastPosNoise !== undefined) {",
+                "            particle.position.sub(particle.lastPosNoise);",
+                "            tempV$2.set(generators[particle.generatorIndex[0]].noise2D(0, particle.age * frequency) *",
+                "                power *",
+                "                positionAmount, generators[particle.generatorIndex[1]].noise2D(0, particle.age * frequency) *",
+                "                power *",
+                "                positionAmount, generators[particle.generatorIndex[2]].noise2D(0, particle.age * frequency) *",
+                "                power *",
+                "                positionAmount);",
+                "            particle.position.add(tempV$2);",
+                "            particle.lastPosNoise.copy(tempV$2);",
+                "        }",
+                "        if (rotationAmount > 0 && particle.lastRotNoise !== undefined) {",
+                "            if (typeof particle.rotation === 'number') {",
+                "                particle.rotation -= particle.lastRotNoise;",
+                "                particle.rotation +=",
+                "                    generators[particle.generatorIndex[3]].noise2D(0, particle.age * frequency) *",
+                "                    Math.PI *",
+                "                    power *",
+                "                    rotationAmount;",
+                "            }",
+                "            else {",
+                "                particle.lastRotNoise.invert();",
+                "                particle.rotation.multiply(particle.lastRotNoise);",
+                "                tempQ$2",
+                "                    .set(generators[particle.generatorIndex[0]].noise2D(0, particle.age * frequency) *",
+                "                        power *",
+                "                        rotationAmount, generators[particle.generatorIndex[1]].noise2D(0, particle.age * frequency) *",
+                "                        power *",
+                "                        rotationAmount, generators[particle.generatorIndex[2]].noise2D(0, particle.age * frequency) *",
+                "                        power *",
+                "                        rotationAmount, generators[particle.generatorIndex[3]].noise2D(0, particle.age * frequency) *",
+                "                        power *",
+                "                        rotationAmount)",
+                "                    .normalize();",
+                "                particle.rotation.multiply(tempQ$2);",
+                "                particle.lastRotNoise.copy(tempQ$2);",
+                "            }",
+                "        }",
+                "    }",
+                "    toJSON() {",
+                "        return {",
+                "            type: this.type,",
+                "            frequency: this.frequency.toJSON(),",
+                "            power: this.power.toJSON(),",
+                "            positionAmount: this.positionAmount.toJSON(),",
+                "            rotationAmount: this.rotationAmount.toJSON(),",
+                "        };",
+                "    }",
+                "    frameUpdate(delta) {",
+                "        this.duration += delta;",
+                "    }",
+                "    static fromJSON(json) {",
+                "        return new Noise(ValueGeneratorFromJSON(json.frequency), ValueGeneratorFromJSON(json.power), ValueGeneratorFromJSON(json.positionAmount), ValueGeneratorFromJSON(json.rotationAmount));",
+                "    }",
+                "    clone() {",
+                "        return new Noise(this.frequency.clone(), this.power.clone(), this.positionAmount.clone(), this.rotationAmount.clone());",
+                "    }",
+                "    reset() { }",
+                "}",
+                "",
+                "class TextureSequencer {",
+                "    constructor(scaleX = 0, scaleY = 0, position = new THREE.Vector3()) {",
+                "        this.scaleX = scaleX;",
+                "        this.scaleY = scaleY;",
+                "        this.position = position;",
+                "        this.locations = [];",
+                "    }",
+                "    transform(position, index) {",
+                "        position.x = this.locations[index % this.locations.length].x * this.scaleX + this.position.x;",
+                "        position.y = this.locations[index % this.locations.length].y * this.scaleY + this.position.y;",
+                "        position.z = this.position.z;",
+                "    }",
+                "    static fromJSON(json) {",
+                "        const textureSequencer = new TextureSequencer(json.scaleX, json.scaleY, new THREE.Vector3(json.position[0], json.position[1], json.position[2]));",
+                "        textureSequencer.locations = json.locations.map((loc) => new THREE.Vector2(loc.x, loc.y));",
+                "        return textureSequencer;",
+                "    }",
+                "    clone() {",
+                "        const textureSequencer = new TextureSequencer(this.scaleX, this.scaleY, this.position.clone());",
+                "        textureSequencer.locations = this.locations.map(loc => loc.clone());",
+                "        return textureSequencer;",
+                "    }",
+                "    toJSON() {",
+                "        return {",
+                "            scaleX: this.scaleX,",
+                "            scaleY: this.scaleY,",
+                "            position: this.position,",
+                "            locations: this.locations.map(loc => ({",
+                "                x: loc.x,",
+                "                y: loc.y,",
+                "            }))",
+                "        };",
+                "    }",
+                "    fromImage(img, threshold) {",
+                "        const canvas = document.createElement(\"canvas\");",
+                "        canvas.width = img.width;",
+                "        canvas.height = img.height;",
+                "        const ctx = canvas.getContext(\"2d\");",
+                "        if (!ctx) {",
+                "            return;",
+                "        }",
+                "        ctx.drawImage(img, 0, 0);",
+                "        const data = ctx.getImageData(0, 0, canvas.width, canvas.height, { colorSpace: \"srgb\" });",
+                "        canvas.remove();",
+                "        this.locations.length = 0;",
+                "        for (let i = 0; i < data.height; i++) {",
+                "            for (let j = 0; j < data.width; j++) {",
+                "                if (data.data[(i * data.width + j) * 4 + 3] > threshold) {",
+                "                    this.locations.push(new THREE.Vector2(j, data.height - i));",
+                "                }",
+                "            }",
+                "        }",
+                "    }",
+                "}",
+                "",
+                "function SequencerFromJSON(json) {",
+                "    switch (json.type) {",
+                "        case 'TextureSequencer':",
+                "            return TextureSequencer.fromJSON(json);",
+                "        default:",
+                "            return new TextureSequencer();",
+                "    }",
+                "}",
+                "",
+                "class ApplySequences {",
+                "    constructor(delayBetweenParticles) {",
+                "        this.type = 'ApplySequences';",
+                "        this.sequencers = [];",
+                "        this.time = 0;",
+                "        this.index = 0;",
+                "        this.pCount = 0;",
+                "        this.tempV = new THREE.Vector3();",
+                "        this.delay = delayBetweenParticles;",
+                "    }",
+                "    initialize(particle) {",
+                "        particle.id = this.pCount;",
+                "        particle.dst = new THREE.Vector3();",
+                "        particle.begin = new THREE.Vector3();",
+                "        particle.inMotion = false;",
+                "        this.pCount++;",
+                "    }",
+                "    reset() {",
+                "        this.time = 0;",
+                "        this.index = 0;",
+                "        this.pCount = 0;",
+                "    }",
+                "    update(particle, delta) {",
+                "        const sequencer = this.sequencers[this.index];",
+                "        const delay = particle.id * this.delay;",
+                "        if (this.time >= sequencer[0].a + delay && this.time <= sequencer[0].b + delay) {",
+                "            if (!particle.inMotion) {",
+                "                particle.inMotion = true;",
+                "                particle.begin.copy(particle.position);",
+                "                sequencer[1].transform(particle.dst, particle.id);",
+                "            }",
+                "            particle.position.lerpVectors(particle.begin, particle.dst, ApplySequences.BEZIER.genValue((this.time - sequencer[0].a - delay) / (sequencer[0].b - sequencer[0].a)));",
+                "        }",
+                "        else if (this.time > sequencer[0].b + delay) {",
+                "            particle.inMotion = false;",
+                "        }",
+                "    }",
+                "    frameUpdate(delta) {",
+                "        while (this.index + 1 < this.sequencers.length && this.time >= this.sequencers[this.index + 1][0].a) {",
+                "            this.index++;",
+                "        }",
+                "        this.time += delta;",
+                "    }",
+                "    appendSequencer(range, sequencer) {",
+                "        this.sequencers.push([range, sequencer]);",
+                "    }",
+                "    toJSON() {",
+                "        return {",
+                "            type: this.type,",
+                "            delay: this.delay,",
+                "            sequencers: this.sequencers.map(([range, sequencer]) => ({",
+                "                range: range.toJSON(),",
+                "                sequencer: sequencer.toJSON(),",
+                "            })),",
+                "        };",
+                "    }",
+                "    static fromJSON(json) {",
+                "        const seq = new ApplySequences(json.delay);",
+                "        json.sequencers.forEach((sequencerJson) => {",
+                "            seq.sequencers.push([ValueGeneratorFromJSON(sequencerJson.range), SequencerFromJSON(sequencerJson.sequencer)]);",
+                "        });",
+                "        return seq;",
+                "    }",
+                "    clone() {",
+                "        const applySequences = new ApplySequences(this.delay);",
+                "        applySequences.sequencers = this.sequencers.map(seq => [seq[0].clone(), seq[1].clone()]);",
+                "        return applySequences;",
+                "    }",
+                "}",
+                "ApplySequences.BEZIER = new Bezier(0, 0, 1, 1);",
+                "",
+                "let physicsResolver;",
+                "function setPhysicsResolver(resolver) {",
+                "    physicsResolver = resolver;",
+                "}",
+                "function getPhysicsResolver() {",
+                "    return physicsResolver;",
+                "}",
+                "class ApplyCollision {",
+                "    constructor(resolver, bounce) {",
+                "        this.resolver = resolver;",
+                "        this.bounce = bounce;",
+                "        this.type = 'ApplyCollision';",
+                "        this.tempV = new THREE.Vector3();",
+                "    }",
+                "    initialize(particle) {",
+                "    }",
+                "    update(particle, delta) {",
+                "        if (this.resolver.resolve(particle.position, this.tempV)) {",
+                "            particle.velocity.reflect(this.tempV).multiplyScalar(this.bounce);",
+                "        }",
+                "    }",
+                "    frameUpdate(delta) {",
+                "    }",
+                "    toJSON() {",
+                "        return {",
+                "            type: this.type,",
+                "            bounce: this.bounce,",
+                "        };",
+                "    }",
+                "    static fromJSON(json) {",
+                "        return new ApplyCollision(getPhysicsResolver(), json.bounce);",
+                "    }",
+                "    clone() {",
+                "        return new ApplyCollision(this.resolver, this.bounce);",
+                "    }",
+                "    reset() {",
+                "    }",
+                "}",
+                "",
+                "class ColorBySpeed {",
+                "    constructor(color, speedRange) {",
+                "        this.color = color;",
+                "        this.speedRange = speedRange;",
+                "        this.type = 'ColorBySpeed';",
+                "    }",
+                "    initialize(particle) {",
+                "        if (this.color.type === 'memorizedFunction') {",
+                "            particle.colorOverLifeMemory = {};",
+                "            this.color.startGen(particle.colorOverLifeMemory);",
+                "        }",
+                "    }",
+                "    update(particle, delta) {",
+                "        const t = (particle.startSpeed - this.speedRange.a) / (this.speedRange.b - this.speedRange.a);",
+                "        if (this.color.type === 'memorizedFunction') {",
+                "            this.color.genColor(particle.color, t, particle.colorOverLifeMemory);",
+                "        }",
+                "        else {",
+                "            this.color.genColor(particle.color, t);",
+                "        }",
+                "        particle.color.x *= particle.startColor.x;",
+                "        particle.color.y *= particle.startColor.y;",
+                "        particle.color.z *= particle.startColor.z;",
+                "        particle.color.w *= particle.startColor.w;",
+                "    }",
+                "    frameUpdate(delta) { }",
+                "    toJSON() {",
+                "        return {",
+                "            type: this.type,",
+                "            color: this.color.toJSON(),",
+                "            speedRange: this.speedRange.toJSON(),",
+                "        };",
+                "    }",
+                "    static fromJSON(json) {",
+                "        return new ColorBySpeed(ColorGeneratorFromJSON(json.color), IntervalValue.fromJSON(json.speedRange));",
+                "    }",
+                "    clone() {",
+                "        return new ColorBySpeed(this.color.clone(), this.speedRange.clone());",
+                "    }",
+                "    reset() { }",
+                "}",
+                "",
+                "class SizeBySpeed {",
+                "    initialize(particle) { }",
+                "    constructor(size, speedRange) {",
+                "        this.size = size;",
+                "        this.speedRange = speedRange;",
+                "        this.type = 'SizeBySpeed';",
+                "    }",
+                "    update(particle) {",
+                "        const t = (particle.startSpeed - this.speedRange.a) / (this.speedRange.b - this.speedRange.a);",
+                "        particle.size = particle.startSize * this.size.genValue(t);",
+                "    }",
+                "    toJSON() {",
+                "        return {",
+                "            type: this.type,",
+                "            size: this.size.toJSON(),",
+                "            speedRange: this.speedRange.toJSON(),",
+                "        };",
+                "    }",
+                "    static fromJSON(json) {",
+                "        return new SizeBySpeed(ValueGeneratorFromJSON(json.size), IntervalValue.fromJSON(json.speedRange));",
+                "    }",
+                "    frameUpdate(delta) { }",
+                "    clone() {",
+                "        return new SizeBySpeed(this.size.clone(), this.speedRange.clone());",
+                "    }",
+                "    reset() { }",
+                "}",
+                "",
+                "class RotationBySpeed {",
+                "    constructor(angularVelocity, speedRange) {",
+                "        this.angularVelocity = angularVelocity;",
+                "        this.speedRange = speedRange;",
+                "        this.type = 'RotationBySpeed';",
+                "        this.tempQuat = new THREE.Quaternion();",
+                "    }",
+                "    initialize(particle) { }",
+                "    update(particle, delta) {",
+                "        const t = (particle.startSpeed - this.speedRange.a) / (this.speedRange.b - this.speedRange.a);",
+                "        particle.rotation += delta * this.angularVelocity.genValue(t);",
+                "    }",
+                "    toJSON() {",
+                "        return {",
+                "            type: this.type,",
+                "            angularVelocity: this.angularVelocity.toJSON(),",
+                "            speedRange: this.speedRange.toJSON(),",
+                "        };",
+                "    }",
+                "    static fromJSON(json) {",
+                "        return new RotationBySpeed(ValueGeneratorFromJSON(json.angularVelocity), IntervalValue.fromJSON(json.speedRange));",
+                "    }",
+                "    frameUpdate(delta) { }",
+                "    clone() {",
+                "        return new RotationBySpeed(this.angularVelocity.clone(), this.speedRange.clone());",
+                "    }",
+                "    reset() { }",
+                "}",
+                "",
+                "class LimitSpeedOverLife {",
+                "    initialize(particle) { }",
+                "    constructor(speed, dampen) {",
+                "        this.speed = speed;",
+                "        this.dampen = dampen;",
+                "        this.type = 'LimitSpeedOverLife';",
+                "    }",
+                "    update(particle, delta) {",
+                "        let speed = particle.velocity.length();",
+                "        let limit = this.speed.genValue(particle.age / particle.life);",
+                "        if (speed > limit) {",
+                "            const percent = (speed - limit) / speed;",
+                "            particle.velocity.multiplyScalar(1 - percent * this.dampen * delta * 20);",
+                "        }",
+                "    }",
+                "    toJSON() {",
+                "        return {",
+                "            type: this.type,",
+                "            speed: this.speed.toJSON(),",
+                "            dampen: this.dampen,",
+                "        };",
+                "    }",
+                "    static fromJSON(json) {",
+                "        return new LimitSpeedOverLife(ValueGeneratorFromJSON(json.speed), json.dampen);",
+                "    }",
+                "    frameUpdate(delta) { }",
+                "    clone() {",
+                "        return new LimitSpeedOverLife(this.speed.clone(), this.dampen);",
+                "    }",
+                "    reset() { }",
+                "}",
+                "",
+                "const BehaviorTypes = {",
+                "    ApplyForce: {",
+                "        type: 'ApplyForce',",
+                "        constructor: ApplyForce,",
+                "        params: [",
+                "            ['direction', 'vec3'],",
+                "            ['magnitude', 'value'],",
+                "        ],",
+                "        loadJSON: ApplyForce.fromJSON,",
+                "    },",
+                "    Noise: {",
+                "        type: 'Noise',",
+                "        constructor: Noise,",
+                "        params: [",
+                "            ['frequency', 'value'],",
+                "            ['power', 'value'],",
+                "            ['positionAmount', 'value'],",
+                "            ['rotationAmount', 'value'],",
+                "        ],",
+                "        loadJSON: Noise.fromJSON,",
+                "    },",
+                "    TurbulenceField: {",
+                "        type: 'TurbulenceField',",
+                "        constructor: TurbulenceField,",
+                "        params: [",
+                "            ['scale', 'vec3'],",
+                "            ['octaves', 'number'],",
+                "            ['velocityMultiplier', 'vec3'],",
+                "            ['timeScale', 'vec3'],",
+                "        ],",
+                "        loadJSON: TurbulenceField.fromJSON,",
+                "    },",
+                "    GravityForce: {",
+                "        type: 'GravityForce',",
+                "        constructor: GravityForce,",
+                "        params: [",
+                "            ['center', 'vec3'],",
+                "            ['magnitude', 'number'],",
+                "        ],",
+                "        loadJSON: GravityForce.fromJSON,",
+                "    },",
+                "    ColorOverLife: {",
+                "        type: 'ColorOverLife',",
+                "        constructor: ColorOverLife,",
+                "        params: [['color', 'colorFunc']],",
+                "        loadJSON: ColorOverLife.fromJSON,",
+                "    },",
+                "    RotationOverLife: {",
+                "        type: 'RotationOverLife',",
+                "        constructor: RotationOverLife,",
+                "        params: [['angularVelocity', 'valueFunc']],",
+                "        loadJSON: RotationOverLife.fromJSON,",
+                "    },",
+                "    Rotation3DOverLife: {",
+                "        type: 'Rotation3DOverLife',",
+                "        constructor: Rotation3DOverLife,",
+                "        params: [['angularVelocity', 'rotationFunc']],",
+                "        loadJSON: Rotation3DOverLife.fromJSON,",
+                "    },",
+                "    SizeOverLife: {",
+                "        type: 'SizeOverLife',",
+                "        constructor: SizeOverLife,",
+                "        params: [['size', 'valueFunc']],",
+                "        loadJSON: SizeOverLife.fromJSON,",
+                "    },",
+                "    ColorBySpeed: {",
+                "        type: 'ColorBySpeed',",
+                "        constructor: ColorBySpeed,",
+                "        params: [",
+                "            ['color', 'colorFunc'],",
+                "            ['speedRange', 'range'],",
+                "        ],",
+                "        loadJSON: ColorBySpeed.fromJSON,",
+                "    },",
+                "    RotationBySpeed: {",
+                "        type: 'RotationBySpeed',",
+                "        constructor: RotationBySpeed,",
+                "        params: [",
+                "            ['angularVelocity', 'valueFunc'],",
+                "            ['speedRange', 'range'],",
+                "        ],",
+                "        loadJSON: RotationBySpeed.fromJSON,",
+                "    },",
+                "    SizeBySpeed: {",
+                "        type: 'SizeBySpeed',",
+                "        constructor: SizeBySpeed,",
+                "        params: [",
+                "            ['size', 'valueFunc'],",
+                "            ['speedRange', 'range'],",
+                "        ],",
+                "        loadJSON: SizeBySpeed.fromJSON,",
+                "    },",
+                "    SpeedOverLife: {",
+                "        type: 'SpeedOverLife',",
+                "        constructor: SpeedOverLife,",
+                "        params: [['speed', 'valueFunc']],",
+                "        loadJSON: SpeedOverLife.fromJSON,",
+                "    },",
+                "    FrameOverLife: {",
+                "        type: 'FrameOverLife',",
+                "        constructor: FrameOverLife,",
+                "        params: [['frame', 'valueFunc']],",
+                "        loadJSON: FrameOverLife.fromJSON,",
+                "    },",
+                "    ForceOverLife: {",
+                "        type: 'ForceOverLife',",
+                "        constructor: ForceOverLife,",
+                "        params: [",
+                "            ['x', 'valueFunc'],",
+                "            ['y', 'valueFunc'],",
+                "            ['z', 'valueFunc'],",
+                "        ],",
+                "        loadJSON: ForceOverLife.fromJSON,",
+                "    },",
+                "    OrbitOverLife: {",
+                "        type: 'OrbitOverLife',",
+                "        constructor: OrbitOverLife,",
+                "        params: [",
+                "            ['orbitSpeed', 'valueFunc'],",
+                "            ['axis', 'vec3'],",
+                "        ],",
+                "        loadJSON: OrbitOverLife.fromJSON,",
+                "    },",
+                "    WidthOverLength: {",
+                "        type: 'WidthOverLength',",
+                "        constructor: WidthOverLength,",
+                "        params: [['width', 'valueFunc']],",
+                "        loadJSON: WidthOverLength.fromJSON,",
+                "    },",
+                "    ChangeEmitDirection: {",
+                "        type: 'ChangeEmitDirection',",
+                "        constructor: ChangeEmitDirection,",
+                "        params: [['angle', 'value']],",
+                "        loadJSON: ChangeEmitDirection.fromJSON,",
+                "    },",
+                "    EmitSubParticleSystem: {",
+                "        type: 'EmitSubParticleSystem',",
+                "        constructor: EmitSubParticleSystem,",
+                "        params: [",
+                "            ['particleSystem', 'self'],",
+                "            ['useVelocityAsBasis', 'boolean'],",
+                "            ['subParticleSystem', 'particleSystem'],",
+                "            ['mode', 'number'],",
+                "            ['emitProbability', 'number'],",
+                "        ],",
+                "        loadJSON: EmitSubParticleSystem.fromJSON,",
+                "    },",
+                "    LimitSpeedOverLife: {",
+                "        type: 'LimitSpeedOverLife',",
+                "        constructor: LimitSpeedOverLife,",
+                "        params: [",
+                "            ['speed', 'valueFunc'],",
+                "            ['dampen', 'number'],",
+                "        ],",
+                "        loadJSON: LimitSpeedOverLife.fromJSON,",
+                "    },",
+                "};",
+                "function BehaviorFromJSON(json, particleSystem) {",
+                "    return BehaviorTypes[json.type].loadJSON(json, particleSystem);",
+                "}",
+                "",
+                "let EmitterMode = void 0;",
+                "(function (EmitterMode) {",
+                "    EmitterMode[EmitterMode[\"Random\"] = 0] = \"Random\";",
+                "    EmitterMode[EmitterMode[\"Loop\"] = 1] = \"Loop\";",
+                "    EmitterMode[EmitterMode[\"PingPong\"] = 2] = \"PingPong\";",
+                "    EmitterMode[EmitterMode[\"Burst\"] = 3] = \"Burst\";",
+                "})(EmitterMode || (EmitterMode = {}));",
+                "function getValueFromEmitterMode(mode, currentValue, spread) {",
+                "    let u;",
+                "    if (EmitterMode.Random === mode) {",
+                "        currentValue = Math.random();",
+                "    }",
+                "    if (spread > 0) {",
+                "        u = Math.floor(currentValue / spread) * spread;",
+                "    }",
+                "    else {",
+                "        u = currentValue;",
+                "    }",
+                "    switch (mode) {",
+                "        case EmitterMode.Loop:",
+                "            u = u % 1;",
+                "            break;",
+                "        case EmitterMode.PingPong:",
+                "            u = Math.abs((u % 2) - 1);",
+                "            break;",
+                "    }",
+                "    return u;",
+                "}",
+                "",
+                "class ConeEmitter {",
+                "    constructor(parameters = {}) {",
+                "        var _a, _b, _c, _d, _e, _f, _g;",
+                "        this.type = 'cone';",
+                "        this.currentValue = 0;",
+                "        this.radius = (_a = parameters.radius) !== null && _a !== void 0 ? _a : 10;",
+                "        this.arc = (_b = parameters.arc) !== null && _b !== void 0 ? _b : 2.0 * Math.PI;",
+                "        this.thickness = (_c = parameters.thickness) !== null && _c !== void 0 ? _c : 1;",
+                "        this.angle = (_d = parameters.angle) !== null && _d !== void 0 ? _d : Math.PI / 6;",
+                "        this.mode = (_e = parameters.mode) !== null && _e !== void 0 ? _e : EmitterMode.Random;",
+                "        this.spread = (_f = parameters.spread) !== null && _f !== void 0 ? _f : 0;",
+                "        this.speed = (_g = parameters.speed) !== null && _g !== void 0 ? _g : new ConstantValue(1);",
+                "    }",
+                "    update(system, delta) {",
+                "        if (EmitterMode.Random != this.mode) {",
+                "            this.currentValue += this.speed.genValue(system.emissionState.time / system.duration) * delta;",
+                "        }",
+                "    }",
+                "    initialize(p) {",
+                "        const u = getValueFromEmitterMode(this.mode, this.currentValue, this.spread);",
+                "        const rand = THREE.MathUtils.lerp(1 - this.thickness, 1, Math.random());",
+                "        const theta = u * this.arc;",
+                "        const r = Math.sqrt(rand);",
+                "        const sinTheta = Math.sin(theta);",
+                "        const cosTheta = Math.cos(theta);",
+                "        p.position.x = r * cosTheta;",
+                "        p.position.y = r * sinTheta;",
+                "        p.position.z = 0;",
+                "        const angle = this.angle * r;",
+                "        p.velocity.set(0, 0, Math.cos(angle)).addScaledVector(p.position, Math.sin(angle)).multiplyScalar(p.startSpeed);",
+                "        p.position.multiplyScalar(this.radius);",
+                "    }",
+                "    toJSON() {",
+                "        return {",
+                "            type: 'cone',",
+                "            radius: this.radius,",
+                "            arc: this.arc,",
+                "            thickness: this.thickness,",
+                "            angle: this.angle,",
+                "            mode: this.mode,",
+                "            spread: this.spread,",
+                "            speed: this.speed.toJSON(),",
+                "        };",
+                "    }",
+                "    static fromJSON(json) {",
+                "        return new ConeEmitter({",
+                "            radius: json.radius,",
+                "            arc: json.arc,",
+                "            thickness: json.thickness,",
+                "            angle: json.angle,",
+                "            mode: json.mode,",
+                "            speed: json.speed ? ValueGeneratorFromJSON(json.speed) : undefined,",
+                "            spread: json.spread,",
+                "        });",
+                "    }",
+                "    clone() {",
+                "        return new ConeEmitter({",
+                "            radius: this.radius,",
+                "            arc: this.arc,",
+                "            thickness: this.thickness,",
+                "            angle: this.angle,",
+                "            mode: this.mode,",
+                "            speed: this.speed.clone(),",
+                "            spread: this.spread,",
+                "        });",
+                "    }",
+                "}",
+                "",
+                "class CircleEmitter {",
+                "    constructor(parameters = {}) {",
+                "        var _a, _b, _c, _d, _e, _f;",
+                "        this.type = 'circle';",
+                "        this.currentValue = 0;",
+                "        this.radius = (_a = parameters.radius) !== null && _a !== void 0 ? _a : 10;",
+                "        this.arc = (_b = parameters.arc) !== null && _b !== void 0 ? _b : 2.0 * Math.PI;",
+                "        this.thickness = (_c = parameters.thickness) !== null && _c !== void 0 ? _c : 1;",
+                "        this.mode = (_d = parameters.mode) !== null && _d !== void 0 ? _d : EmitterMode.Random;",
+                "        this.spread = (_e = parameters.spread) !== null && _e !== void 0 ? _e : 0;",
+                "        this.speed = (_f = parameters.speed) !== null && _f !== void 0 ? _f : new ConstantValue(1);",
+                "    }",
+                "    update(system, delta) {",
+                "        this.currentValue += this.speed.genValue(system.emissionState.time / system.duration) * delta;",
+                "    }",
+                "    initialize(p) {",
+                "        const u = getValueFromEmitterMode(this.mode, this.currentValue, this.spread);",
+                "        const r = THREE.MathUtils.lerp(1 - this.thickness, 1, Math.random());",
+                "        const theta = u * this.arc;",
+                "        p.position.x = Math.cos(theta);",
+                "        p.position.y = Math.sin(theta);",
+                "        p.position.z = 0;",
+                "        p.velocity.copy(p.position).multiplyScalar(p.startSpeed);",
+                "        p.position.multiplyScalar(this.radius * r);",
+                "    }",
+                "    toJSON() {",
+                "        return {",
+                "            type: 'circle',",
+                "            radius: this.radius,",
+                "            arc: this.arc,",
+                "            thickness: this.thickness,",
+                "            mode: this.mode,",
+                "            spread: this.spread,",
+                "            speed: this.speed.toJSON(),",
+                "        };",
+                "    }",
+                "    static fromJSON(json) {",
+                "        return new CircleEmitter({",
+                "            radius: json.radius,",
+                "            arc: json.arc,",
+                "            thickness: json.thickness,",
+                "            mode: json.mode,",
+                "            speed: json.speed ? ValueGeneratorFromJSON(json.speed) : undefined,",
+                "            spread: json.spread,",
+                "        });",
+                "    }",
+                "    clone() {",
+                "        return new CircleEmitter({",
+                "            radius: this.radius,",
+                "            arc: this.arc,",
+                "            thickness: this.thickness,",
+                "            mode: this.mode,",
+                "            speed: this.speed.clone(),",
+                "            spread: this.spread,",
+                "        });",
+                "    }",
+                "}",
+                "",
+                "class DonutEmitter {",
+                "    constructor(parameters = {}) {",
+                "        var _a, _b, _c, _d, _e, _f, _g;",
+                "        this.type = 'donut';",
+                "        this.currentValue = 0;",
+                "        this.radius = (_a = parameters.radius) !== null && _a !== void 0 ? _a : 10;",
+                "        this.arc = (_b = parameters.arc) !== null && _b !== void 0 ? _b : 2.0 * Math.PI;",
+                "        this.thickness = (_c = parameters.thickness) !== null && _c !== void 0 ? _c : 1;",
+                "        this.donutRadius = (_d = parameters.donutRadius) !== null && _d !== void 0 ? _d : this.radius * 0.2;",
+                "        this.mode = (_e = parameters.mode) !== null && _e !== void 0 ? _e : EmitterMode.Random;",
+                "        this.spread = (_f = parameters.spread) !== null && _f !== void 0 ? _f : 0;",
+                "        this.speed = (_g = parameters.speed) !== null && _g !== void 0 ? _g : new ConstantValue(1);",
+                "    }",
+                "    update(system, delta) {",
+                "        if (EmitterMode.Random != this.mode) {",
+                "            this.currentValue += this.speed.genValue(system.emissionState.time / system.duration) * delta;",
+                "        }",
+                "    }",
+                "    initialize(p) {",
+                "        const u = getValueFromEmitterMode(this.mode, this.currentValue, this.spread);",
+                "        const v = Math.random();",
+                "        const rand = THREE.MathUtils.lerp(1 - this.thickness, 1, Math.random());",
+                "        const theta = u * this.arc;",
+                "        const phi = v * Math.PI * 2;",
+                "        const sinTheta = Math.sin(theta);",
+                "        const cosTheta = Math.cos(theta);",
+                "        p.position.x = this.radius * cosTheta;",
+                "        p.position.y = this.radius * sinTheta;",
+                "        p.position.z = 0;",
+                "        p.velocity.z = this.donutRadius * rand * Math.sin(phi);",
+                "        p.velocity.x = this.donutRadius * rand * Math.cos(phi) * cosTheta;",
+                "        p.velocity.y = this.donutRadius * rand * Math.cos(phi) * sinTheta;",
+                "        p.position.add(p.velocity);",
+                "        p.velocity.normalize().multiplyScalar(p.startSpeed);",
+                "    }",
+                "    toJSON() {",
+                "        return {",
+                "            type: 'donut',",
+                "            radius: this.radius,",
+                "            arc: this.arc,",
+                "            thickness: this.thickness,",
+                "            donutRadius: this.donutRadius,",
+                "            mode: this.mode,",
+                "            spread: this.spread,",
+                "            speed: this.speed.toJSON(),",
+                "        };",
+                "    }",
+                "    static fromJSON(json) {",
+                "        return new DonutEmitter({",
+                "            radius: json.radius,",
+                "            arc: json.arc,",
+                "            thickness: json.thickness,",
+                "            donutRadius: json.donutRadius,",
+                "            mode: json.mode,",
+                "            speed: json.speed ? ValueGeneratorFromJSON(json.speed) : undefined,",
+                "            spread: json.spread,",
+                "        });",
+                "    }",
+                "    clone() {",
+                "        return new DonutEmitter({",
+                "            radius: this.radius,",
+                "            arc: this.arc,",
+                "            thickness: this.thickness,",
+                "            donutRadius: this.donutRadius,",
+                "            mode: this.mode,",
+                "            speed: this.speed.clone(),",
+                "            spread: this.spread,",
+                "        });",
+                "    }",
+                "}",
+                "",
+                "class PointEmitter {",
+                "    constructor() {",
+                "        this.type = 'point';",
+                "    }",
+                "    update(system, delta) { }",
+                "    initialize(p) {",
+                "        const u = Math.random();",
+                "        const v = Math.random();",
+                "        const theta = u * Math.PI * 2;",
+                "        const phi = Math.acos(2.0 * v - 1.0);",
+                "        const r = Math.cbrt(Math.random());",
+                "        const sinTheta = Math.sin(theta);",
+                "        const cosTheta = Math.cos(theta);",
+                "        const sinPhi = Math.sin(phi);",
+                "        const cosPhi = Math.cos(phi);",
+                "        p.velocity.x = r * sinPhi * cosTheta;",
+                "        p.velocity.y = r * sinPhi * sinTheta;",
+                "        p.velocity.z = r * cosPhi;",
+                "        p.velocity.multiplyScalar(p.startSpeed);",
+                "        p.position.setScalar(0);",
+                "    }",
+                "    toJSON() {",
+                "        return {",
+                "            type: 'point',",
+                "        };",
+                "    }",
+                "    static fromJSON(json) {",
+                "        return new PointEmitter();",
+                "    }",
+                "    clone() {",
+                "        return new PointEmitter();",
+                "    }",
+                "}",
+                "",
+                "class SphereEmitter {",
+                "    constructor(parameters = {}) {",
+                "        var _a, _b, _c, _d, _e, _f;",
+                "        this.type = 'sphere';",
+                "        this.currentValue = 0;",
+                "        this.radius = (_a = parameters.radius) !== null && _a !== void 0 ? _a : 10;",
+                "        this.arc = (_b = parameters.arc) !== null && _b !== void 0 ? _b : 2.0 * Math.PI;",
+                "        this.thickness = (_c = parameters.thickness) !== null && _c !== void 0 ? _c : 1;",
+                "        this.mode = (_d = parameters.mode) !== null && _d !== void 0 ? _d : EmitterMode.Random;",
+                "        this.spread = (_e = parameters.spread) !== null && _e !== void 0 ? _e : 0;",
+                "        this.speed = (_f = parameters.speed) !== null && _f !== void 0 ? _f : new ConstantValue(1);",
+                "    }",
+                "    update(system, delta) {",
+                "        if (EmitterMode.Random != this.mode) {",
+                "            this.currentValue += this.speed.genValue(system.emissionState.time / system.duration) * delta;",
+                "        }",
+                "    }",
+                "    initialize(p) {",
+                "        const u = getValueFromEmitterMode(this.mode, this.currentValue, this.spread);",
+                "        const v = Math.random();",
+                "        const rand = THREE.MathUtils.lerp(1 - this.thickness, 1, Math.random());",
+                "        const theta = u * this.arc;",
+                "        const phi = Math.acos(2.0 * v - 1.0);",
+                "        const sinTheta = Math.sin(theta);",
+                "        const cosTheta = Math.cos(theta);",
+                "        const sinPhi = Math.sin(phi);",
+                "        const cosPhi = Math.cos(phi);",
+                "        p.position.x = sinPhi * cosTheta;",
+                "        p.position.y = sinPhi * sinTheta;",
+                "        p.position.z = cosPhi;",
+                "        p.velocity.copy(p.position).multiplyScalar(p.startSpeed);",
+                "        p.position.multiplyScalar(this.radius * rand);",
+                "    }",
+                "    toJSON() {",
+                "        return {",
+                "            type: 'sphere',",
+                "            radius: this.radius,",
+                "            arc: this.arc,",
+                "            thickness: this.thickness,",
+                "            mode: this.mode,",
+                "            spread: this.spread,",
+                "            speed: this.speed.toJSON(),",
+                "        };",
+                "    }",
+                "    static fromJSON(json) {",
+                "        return new SphereEmitter({",
+                "            radius: json.radius,",
+                "            arc: json.arc,",
+                "            thickness: json.thickness,",
+                "            mode: json.mode,",
+                "            speed: json.speed ? ValueGeneratorFromJSON(json.speed) : undefined,",
+                "            spread: json.spread,",
+                "        });",
+                "    }",
+                "    clone() {",
+                "        return new SphereEmitter({",
+                "            radius: this.radius,",
+                "            arc: this.arc,",
+                "            thickness: this.thickness,",
+                "            mode: this.mode,",
+                "            speed: this.speed.clone(),",
+                "            spread: this.spread,",
+                "        });",
+                "    }",
+                "}",
+                "",
+                "class HemisphereEmitter {",
+                "    constructor(parameters = {}) {",
+                "        var _a, _b, _c, _d, _e, _f;",
+                "        this.type = 'sphere';",
+                "        this.currentValue = 0;",
+                "        this.radius = (_a = parameters.radius) !== null && _a !== void 0 ? _a : 10;",
+                "        this.arc = (_b = parameters.arc) !== null && _b !== void 0 ? _b : 2.0 * Math.PI;",
+                "        this.thickness = (_c = parameters.thickness) !== null && _c !== void 0 ? _c : 1;",
+                "        this.mode = (_d = parameters.mode) !== null && _d !== void 0 ? _d : EmitterMode.Random;",
+                "        this.spread = (_e = parameters.spread) !== null && _e !== void 0 ? _e : 0;",
+                "        this.speed = (_f = parameters.speed) !== null && _f !== void 0 ? _f : new ConstantValue(1);",
+                "    }",
+                "    update(system, delta) {",
+                "        if (EmitterMode.Random != this.mode) {",
+                "            this.currentValue += this.speed.genValue(system.emissionState.time / system.duration) * delta;",
+                "        }",
+                "    }",
+                "    initialize(p) {",
+                "        const u = getValueFromEmitterMode(this.mode, this.currentValue, this.spread);",
+                "        const v = Math.random();",
+                "        const rand = THREE.MathUtils.lerp(1 - this.thickness, 1, Math.random());",
+                "        const theta = u * this.arc;",
+                "        const phi = Math.acos(v);",
+                "        const sinTheta = Math.sin(theta);",
+                "        const cosTheta = Math.cos(theta);",
+                "        const sinPhi = Math.sin(phi);",
+                "        const cosPhi = Math.cos(phi);",
+                "        p.position.x = sinPhi * cosTheta;",
+                "        p.position.y = sinPhi * sinTheta;",
+                "        p.position.z = cosPhi;",
+                "        p.velocity.copy(p.position).multiplyScalar(p.startSpeed);",
+                "        p.position.multiplyScalar(this.radius * rand);",
+                "    }",
+                "    toJSON() {",
+                "        return {",
+                "            type: 'hemisphere',",
+                "            radius: this.radius,",
+                "            arc: this.arc,",
+                "            thickness: this.thickness,",
+                "            mode: this.mode,",
+                "            spread: this.spread,",
+                "            speed: this.speed.toJSON(),",
+                "        };",
+                "    }",
+                "    static fromJSON(json) {",
+                "        return new HemisphereEmitter({",
+                "            radius: json.radius,",
+                "            arc: json.arc,",
+                "            thickness: json.thickness,",
+                "            mode: json.mode,",
+                "            speed: json.speed ? ValueGeneratorFromJSON(json.speed) : undefined,",
+                "            spread: json.spread,",
+                "        });",
+                "    }",
+                "    clone() {",
+                "        return new HemisphereEmitter({",
+                "            radius: this.radius,",
+                "            arc: this.arc,",
+                "            thickness: this.thickness,",
+                "            mode: this.mode,",
+                "            speed: this.speed.clone(),",
+                "            spread: this.spread,",
+                "        });",
+                "    }",
+                "}",
+                "",
+                "class GridEmitter {",
+                "    constructor(parameters = {}) {",
+                "        var _a, _b, _c, _d;",
+                "        this.type = 'grid';",
+                "        this.width = (_a = parameters.width) !== null && _a !== void 0 ? _a : 1;",
+                "        this.height = (_b = parameters.height) !== null && _b !== void 0 ? _b : 1;",
+                "        this.column = (_c = parameters.column) !== null && _c !== void 0 ? _c : 10;",
+                "        this.row = (_d = parameters.row) !== null && _d !== void 0 ? _d : 10;",
+                "    }",
+                "    initialize(p) {",
+                "        const r = Math.floor(Math.random() * this.row);",
+                "        const c = Math.floor(Math.random() * this.column);",
+                "        p.position.x = (c * this.width) / this.column - this.width / 2;",
+                "        p.position.y = (r * this.height) / this.row - this.height / 2;",
+                "        p.position.z = 0;",
+                "        p.velocity.set(0, 0, p.startSpeed);",
+                "    }",
+                "    toJSON() {",
+                "        return {",
+                "            type: 'grid',",
+                "            width: this.width,",
+                "            height: this.height,",
+                "            column: this.column,",
+                "            row: this.row,",
+                "        };",
+                "    }",
+                "    static fromJSON(json) {",
+                "        return new GridEmitter(json);",
+                "    }",
+                "    clone() {",
+                "        return new GridEmitter({",
+                "            width: this.width,",
+                "            height: this.height,",
+                "            column: this.column,",
+                "            row: this.row,",
+                "        });",
+                "    }",
+                "    update(system, delta) { }",
+                "}",
+                "",
+                "class MeshSurfaceEmitter {",
+                "    get geometry() {",
+                "        return this._geometry;",
+                "    }",
+                "    set geometry(geometry) {",
+                "        this._geometry = geometry;",
+                "        if (geometry === undefined) {",
+                "            return;",
+                "        }",
+                "        if (typeof geometry === 'string') {",
+                "            return;",
+                "        }",
+                "        const tri = new THREE.Triangle();",
+                "        this._triangleIndexToArea.length = 0;",
+                "        let area = 0;",
+                "        if (!geometry.getIndex()) {",
+                "            return;",
+                "        }",
+                "        const array = geometry.getIndex().array;",
+                "        const triCount = array.length / 3;",
+                "        this._triangleIndexToArea.push(0);",
+                "        for (let i = 0; i < triCount; i++) {",
+                "            tri.setFromAttributeAndIndices(geometry.getAttribute('position'), array[i * 3], array[i * 3 + 1], array[i * 3 + 2]);",
+                "            area += tri.getArea();",
+                "            this._triangleIndexToArea.push(area);",
+                "        }",
+                "        geometry.userData.triangleIndexToArea = this._triangleIndexToArea;",
+                "    }",
+                "    constructor(geometry) {",
+                "        this.type = 'mesh_surface';",
+                "        this._triangleIndexToArea = [];",
+                "        this._tempA = new THREE.Vector3();",
+                "        this._tempB = new THREE.Vector3();",
+                "        this._tempC = new THREE.Vector3();",
+                "        if (!geometry) {",
+                "            return;",
+                "        }",
+                "        this.geometry = geometry;",
+                "    }",
+                "    initialize(p) {",
+                "        const geometry = this._geometry;",
+                "        if (!geometry || geometry.getIndex() === null) {",
+                "            p.position.set(0, 0, 0);",
+                "            p.velocity.set(0, 0, 1).multiplyScalar(p.startSpeed);",
+                "            return;",
+                "        }",
+                "        const triCount = this._triangleIndexToArea.length - 1;",
+                "        let left = 0, right = triCount;",
+                "        const target = Math.random() * this._triangleIndexToArea[triCount];",
+                "        while (left + 1 < right) {",
+                "            const mid = Math.floor((left + right) / 2);",
+                "            if (target < this._triangleIndexToArea[mid]) {",
+                "                right = mid;",
+                "            }",
+                "            else {",
+                "                left = mid;",
+                "            }",
+                "        }",
+                "        let u1 = Math.random();",
+                "        let u2 = Math.random();",
+                "        if (u1 + u2 > 1) {",
+                "            u1 = 1 - u1;",
+                "            u2 = 1 - u2;",
+                "        }",
+                "        const index1 = geometry.getIndex().array[left * 3];",
+                "        const index2 = geometry.getIndex().array[left * 3 + 1];",
+                "        const index3 = geometry.getIndex().array[left * 3 + 2];",
+                "        const positionBuffer = geometry.getAttribute('position');",
+                "        this._tempA.fromBufferAttribute(positionBuffer, index1);",
+                "        this._tempB.fromBufferAttribute(positionBuffer, index2);",
+                "        this._tempC.fromBufferAttribute(positionBuffer, index3);",
+                "        this._tempB.sub(this._tempA);",
+                "        this._tempC.sub(this._tempA);",
+                "        this._tempA.addScaledVector(this._tempB, u1).addScaledVector(this._tempC, u2);",
+                "        p.position.copy(this._tempA);",
+                "        this._tempA.copy(this._tempB).cross(this._tempC).normalize();",
+                "        p.velocity.copy(this._tempA).normalize().multiplyScalar(p.startSpeed);",
+                "    }",
+                "    toJSON() {",
+                "        return {",
+                "            type: 'mesh_surface',",
+                "            mesh: this._geometry ? this._geometry.uuid : '',",
+                "        };",
+                "    }",
+                "    static fromJSON(json, meta) {",
+                "        return new MeshSurfaceEmitter(meta.geometries[json.geometry]);",
+                "    }",
+                "    clone() {",
+                "        return new MeshSurfaceEmitter(this._geometry);",
+                "    }",
+                "    update(system, delta) { }",
+                "}",
+                "",
+                "const EmitterShapes = {",
+                "    circle: {",
+                "        type: 'circle',",
+                "        params: [",
+                "            ['radius', 'number'],",
+                "            ['arc', 'radian'],",
+                "            ['thickness', 'number'],",
+                "            ['mode', 'emitterMode'],",
+                "            ['spread', 'number'],",
+                "            ['speed', 'valueFunc'],",
+                "        ],",
+                "        constructor: CircleEmitter,",
+                "        loadJSON: CircleEmitter.fromJSON,",
+                "    },",
+                "    cone: {",
+                "        type: 'cone',",
+                "        params: [",
+                "            ['radius', 'number'],",
+                "            ['arc', 'radian'],",
+                "            ['thickness', 'number'],",
+                "            ['angle', 'radian'],",
+                "            ['mode', 'emitterMode'],",
+                "            ['spread', 'number'],",
+                "            ['speed', 'valueFunc'],",
+                "        ],",
+                "        constructor: ConeEmitter,",
+                "        loadJSON: ConeEmitter.fromJSON,",
+                "    },",
+                "    donut: {",
+                "        type: 'donut',",
+                "        params: [",
+                "            ['radius', 'number'],",
+                "            ['arc', 'radian'],",
+                "            ['thickness', 'number'],",
+                "            ['donutRadius', 'number'],",
+                "            ['mode', 'emitterMode'],",
+                "            ['spread', 'number'],",
+                "            ['speed', 'valueFunc'],",
+                "        ],",
+                "        constructor: DonutEmitter,",
+                "        loadJSON: DonutEmitter.fromJSON,",
+                "    },",
+                "    point: { type: 'point', params: [], constructor: PointEmitter, loadJSON: PointEmitter.fromJSON },",
+                "    sphere: {",
+                "        type: 'sphere',",
+                "        params: [",
+                "            ['radius', 'number'],",
+                "            ['arc', 'radian'],",
+                "            ['thickness', 'number'],",
+                "            ['angle', 'radian'],",
+                "            ['mode', 'emitterMode'],",
+                "            ['spread', 'number'],",
+                "            ['speed', 'valueFunc'],",
+                "        ],",
+                "        constructor: SphereEmitter,",
+                "        loadJSON: SphereEmitter.fromJSON,",
+                "    },",
+                "    hemisphere: {",
+                "        type: 'hemisphere',",
+                "        params: [",
+                "            ['radius', 'number'],",
+                "            ['arc', 'radian'],",
+                "            ['thickness', 'number'],",
+                "            ['angle', 'radian'],",
+                "            ['mode', 'emitterMode'],",
+                "            ['spread', 'number'],",
+                "            ['speed', 'valueFunc'],",
+                "        ],",
+                "        constructor: HemisphereEmitter,",
+                "        loadJSON: HemisphereEmitter.fromJSON,",
+                "    },",
+                "    grid: {",
+                "        type: 'grid',",
+                "        params: [",
+                "            ['width', 'number'],",
+                "            ['height', 'number'],",
+                "            ['rows', 'number'],",
+                "            ['column', 'number'],",
+                "        ],",
+                "        constructor: GridEmitter,",
+                "        loadJSON: GridEmitter.fromJSON,",
+                "    },",
+                "    mesh_surface: {",
+                "        type: 'mesh_surface',",
+                "        params: [['geometry', 'geometry']],",
+                "        constructor: MeshSurfaceEmitter,",
+                "        loadJSON: MeshSurfaceEmitter.fromJSON,",
+                "    },",
+                "};",
+                "function EmitterFromJSON(json, meta) {",
+                "    return EmitterShapes[json.type].loadJSON(json, meta);",
+                "}",
+                "",
+                "let RenderMode = void 0;",
+                "(function (RenderMode) {",
+                "    RenderMode[RenderMode[\"BillBoard\"] = 0] = \"BillBoard\";",
+                "    RenderMode[RenderMode[\"StretchedBillBoard\"] = 1] = \"StretchedBillBoard\";",
+                "    RenderMode[RenderMode[\"Mesh\"] = 2] = \"Mesh\";",
+                "    RenderMode[RenderMode[\"Trail\"] = 3] = \"Trail\";",
+                "    RenderMode[RenderMode[\"HorizontalBillBoard\"] = 4] = \"HorizontalBillBoard\";",
+                "    RenderMode[RenderMode[\"VerticalBillBoard\"] = 5] = \"VerticalBillBoard\";",
+                "})(RenderMode || (RenderMode = {}));",
+                "class VFXBatch extends THREE.Mesh {",
+                "    constructor(settings) {",
+                "        super();",
+                "        this.type = 'VFXBatch';",
+                "        this.maxParticles = 1000;",
+                "        this.systems = new Set();",
+                "        const layers = new THREE.Layers();",
+                "        layers.mask = settings.layers.mask;",
+                "        const newMat = settings.material.clone();",
+                "        newMat.defines = {};",
+                "        Object.assign(newMat.defines, settings.material.defines);",
+                "        this.settings = {",
+                "            instancingGeometry: settings.instancingGeometry,",
+                "            renderMode: settings.renderMode,",
+                "            renderOrder: settings.renderOrder,",
+                "            material: newMat,",
+                "            uTileCount: settings.uTileCount,",
+                "            vTileCount: settings.vTileCount,",
+                "            layers: layers,",
+                "        };",
+                "        this.frustumCulled = false;",
+                "        this.renderOrder = this.settings.renderOrder;",
+                "    }",
+                "    addSystem(system) {",
+                "        this.systems.add(system);",
+                "    }",
+                "    removeSystem(system) {",
+                "        this.systems.delete(system);",
+                "    }",
+                "}",
+                "",
+                "const UP = new THREE.Vector3(0, 0, 1);",
+                "const tempQ$1 = new THREE.Quaternion();",
+                "const tempV$1 = new THREE.Vector3();",
+                "const tempV2$1 = new THREE.Vector3();",
+                "new THREE.Vector3();",
+                "const PREWARM_FPS$1 = 60;",
+                "const DEFAULT_GEOMETRY$1 = new THREE.PlaneGeometry(1, 1, 1, 1);",
+                "class ParticleSystem {",
+                "    set time(time) {",
+                "        this.emissionState.time = time;",
+                "    }",
+                "    get time() {",
+                "        return this.emissionState.time;",
+                "    }",
+                "    get layers() {",
+                "        return this.rendererSettings.layers;",
+                "    }",
+                "    get texture() {",
+                "        return this.rendererSettings.material.map;",
+                "    }",
+                "    set texture(texture) {",
+                "        this.rendererSettings.material.map = texture;",
+                "        this.neededToUpdateRender = true;",
+                "    }",
+                "    get material() {",
+                "        return this.rendererSettings.material;",
+                "    }",
+                "    set material(material) {",
+                "        this.rendererSettings.material = material;",
+                "        this.neededToUpdateRender = true;",
+                "    }",
+                "    get uTileCount() {",
+                "        return this.rendererSettings.uTileCount;",
+                "    }",
+                "    set uTileCount(u) {",
+                "        this.rendererSettings.uTileCount = u;",
+                "        this.neededToUpdateRender = true;",
+                "    }",
+                "    get vTileCount() {",
+                "        return this.rendererSettings.vTileCount;",
+                "    }",
+                "    set vTileCount(v) {",
+                "        this.rendererSettings.vTileCount = v;",
+                "        this.neededToUpdateRender = true;",
+                "    }",
+                "    get instancingGeometry() {",
+                "        return this.rendererSettings.instancingGeometry;",
+                "    }",
+                "    set instancingGeometry(geometry) {",
+                "        this.restart();",
+                "        this.particles.length = 0;",
+                "        this.rendererSettings.instancingGeometry = geometry;",
+                "        this.neededToUpdateRender = true;",
+                "    }",
+                "    get renderMode() {",
+                "        return this.rendererSettings.renderMode;",
+                "    }",
+                "    set renderMode(renderMode) {",
+                "        if ((this.rendererSettings.renderMode != RenderMode.Trail && renderMode === RenderMode.Trail) ||",
+                "            (this.rendererSettings.renderMode == RenderMode.Trail && renderMode !== RenderMode.Trail)) {",
+                "            this.restart();",
+                "            this.particles.length = 0;",
+                "        }",
+                "        if (this.rendererSettings.renderMode !== renderMode) {",
+                "            switch (renderMode) {",
+                "                case RenderMode.Trail:",
+                "                    this.rendererEmitterSettings = {",
+                "                        startLength: new ConstantValue(30),",
+                "                        followLocalOrigin: false,",
+                "                    };",
+                "                    break;",
+                "                case RenderMode.Mesh:",
+                "                    this.rendererEmitterSettings = {",
+                "                        geometry: new THREE.PlaneGeometry(1, 1),",
+                "                    };",
+                "                    this.startRotation = new AxisAngleGenerator(new THREE.Vector3(0, 1, 0), new ConstantValue(0));",
+                "                    break;",
+                "                case RenderMode.StretchedBillBoard:",
+                "                    this.rendererEmitterSettings = { speedFactor: 0, lengthFactor: 2 };",
+                "                    if (this.rendererSettings.renderMode === RenderMode.Mesh) {",
+                "                        this.startRotation = new ConstantValue(0);",
+                "                    }",
+                "                    break;",
+                "                case RenderMode.BillBoard:",
+                "                case RenderMode.VerticalBillBoard:",
+                "                case RenderMode.HorizontalBillBoard:",
+                "                    this.rendererEmitterSettings = {};",
+                "                    if (this.rendererSettings.renderMode === RenderMode.Mesh) {",
+                "                        this.startRotation = new ConstantValue(0);",
+                "                    }",
+                "                    break;",
+                "            }",
+                "        }",
+                "        this.rendererSettings.renderMode = renderMode;",
+                "        this.neededToUpdateRender = true;",
+                "    }",
+                "    get renderOrder() {",
+                "        return this.rendererSettings.renderOrder;",
+                "    }",
+                "    set renderOrder(renderOrder) {",
+                "        this.rendererSettings.renderOrder = renderOrder;",
+                "        this.neededToUpdateRender = true;",
+                "    }",
+                "    get blending() {",
+                "        return this.rendererSettings.material.blending;",
+                "    }",
+                "    set blending(blending) {",
+                "        this.rendererSettings.material.blending = blending;",
+                "        this.neededToUpdateRender = true;",
+                "    }",
+                "    constructor(parameters) {",
+                "        var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x;",
+                "        this.temp = new THREE.Vector3();",
+                "        this.travelDistance = 0;",
+                "        this.normalMatrix = new THREE.Matrix3();",
+                "        this.firstTimeUpdate = true;",
+                "        this.autoDestroy = parameters.autoDestroy === undefined ? false : parameters.autoDestroy;",
+                "        this.duration = (_a = parameters.duration) !== null && _a !== void 0 ? _a : 1;",
+                "        this.looping = parameters.looping === undefined ? true : parameters.looping;",
+                "        this.prewarm = parameters.prewarm === undefined ? false : parameters.prewarm;",
+                "        this.startLife = (_b = parameters.startLife) !== null && _b !== void 0 ? _b : new ConstantValue(5);",
+                "        this.startSpeed = (_c = parameters.startSpeed) !== null && _c !== void 0 ? _c : new ConstantValue(0);",
+                "        this.startRotation = (_d = parameters.startRotation) !== null && _d !== void 0 ? _d : new ConstantValue(0);",
+                "        this.startSize = (_e = parameters.startSize) !== null && _e !== void 0 ? _e : new ConstantValue(1);",
+                "        this.startColor = (_f = parameters.startColor) !== null && _f !== void 0 ? _f : new ConstantColor(new THREE.Vector4(1, 1, 1, 1));",
+                "        this.emissionOverTime = (_g = parameters.emissionOverTime) !== null && _g !== void 0 ? _g : new ConstantValue(10);",
+                "        this.emissionOverDistance = (_h = parameters.emissionOverDistance) !== null && _h !== void 0 ? _h : new ConstantValue(0);",
+                "        this.emissionBursts = (_j = parameters.emissionBursts) !== null && _j !== void 0 ? _j : [];",
+                "        this.onlyUsedByOther = (_k = parameters.onlyUsedByOther) !== null && _k !== void 0 ? _k : false;",
+                "        this.emitterShape = (_l = parameters.shape) !== null && _l !== void 0 ? _l : new SphereEmitter();",
+                "        this.behaviors = (_m = parameters.behaviors) !== null && _m !== void 0 ? _m : new Array();",
+                "        this.worldSpace = (_o = parameters.worldSpace) !== null && _o !== void 0 ? _o : false;",
+                "        this.rendererEmitterSettings = (_p = parameters.rendererEmitterSettings) !== null && _p !== void 0 ? _p : {};",
+                "        if (parameters.renderMode === RenderMode.StretchedBillBoard) {",
+                "            const stretchedBillboardSettings = this.rendererEmitterSettings;",
+                "            if (parameters.speedFactor !== undefined) {",
+                "                stretchedBillboardSettings.speedFactor = parameters.speedFactor;",
+                "            }",
+                "            stretchedBillboardSettings.speedFactor = (_q = stretchedBillboardSettings.speedFactor) !== null && _q !== void 0 ? _q : 0;",
+                "            stretchedBillboardSettings.lengthFactor = (_r = stretchedBillboardSettings.lengthFactor) !== null && _r !== void 0 ? _r : 0;",
+                "        }",
+                "        this.rendererSettings = {",
+                "            instancingGeometry: (_s = parameters.instancingGeometry) !== null && _s !== void 0 ? _s : DEFAULT_GEOMETRY$1,",
+                "            renderMode: (_t = parameters.renderMode) !== null && _t !== void 0 ? _t : RenderMode.BillBoard,",
+                "            renderOrder: (_u = parameters.renderOrder) !== null && _u !== void 0 ? _u : 0,",
+                "            material: parameters.material,",
+                "            uTileCount: (_v = parameters.uTileCount) !== null && _v !== void 0 ? _v : 1,",
+                "            vTileCount: (_w = parameters.vTileCount) !== null && _w !== void 0 ? _w : 1,",
+                "            layers: (_x = parameters.layers) !== null && _x !== void 0 ? _x : new THREE.Layers(),",
+                "        };",
+                "        this.neededToUpdateRender = true;",
+                "        this.particles = new Array();",
+                "        this.startTileIndex = parameters.startTileIndex || new ConstantValue(0);",
+                "        this.emitter = new ParticleEmitter(this);",
+                "        this.paused = false;",
+                "        this.particleNum = 0;",
+                "        this.emissionState = {",
+                "            burstIndex: 0,",
+                "            burstWaveIndex: 0,",
+                "            time: 0,",
+                "            waitEmiting: 0,",
+                "            travelDistance: 0,",
+                "        };",
+                "        this.emitEnded = false;",
+                "        this.markForDestroy = false;",
+                "        this.prewarmed = false;",
+                "    }",
+                "    pause() {",
+                "        this.paused = true;",
+                "    }",
+                "    play() {",
+                "        this.paused = false;",
+                "    }",
+                "    spawn(count, emissionState, matrix) {",
+                "        tempQ$1.setFromRotationMatrix(matrix);",
+                "        const translation = tempV$1;",
+                "        const quaternion = tempQ$1;",
+                "        const scale = tempV2$1;",
+                "        matrix.decompose(translation, quaternion, scale);",
+                "        for (let i = 0; i < count; i++) {",
+                "            this.particleNum++;",
+                "            while (this.particles.length < this.particleNum) {",
+                "                if (this.rendererSettings.renderMode === RenderMode.Trail) {",
+                "                    this.particles.push(new TrailParticle());",
+                "                }",
+                "                else {",
+                "                    this.particles.push(new SpriteParticle());",
+                "                }",
+                "            }",
+                "            const particle = this.particles[this.particleNum - 1];",
+                "            particle.speedModifier = 1;",
+                "            this.startColor.genColor(particle.startColor, this.emissionState.time, {});",
+                "            particle.color.copy(particle.startColor);",
+                "            particle.startSpeed = this.startSpeed.genValue(emissionState.time / this.duration);",
+                "            particle.life = this.startLife.genValue(emissionState.time / this.duration);",
+                "            particle.age = 0;",
+                "            particle.startSize = this.startSize.genValue(emissionState.time / this.duration);",
+                "            particle.uvTile = Math.floor(this.startTileIndex.genValue() + 0.001);",
+                "            particle.size = particle.startSize;",
+                "            if (this.rendererSettings.renderMode === RenderMode.Mesh ||",
+                "                this.rendererSettings.renderMode === RenderMode.BillBoard ||",
+                "                this.rendererSettings.renderMode === RenderMode.VerticalBillBoard ||",
+                "                this.rendererSettings.renderMode === RenderMode.HorizontalBillBoard ||",
+                "                this.rendererSettings.renderMode === RenderMode.StretchedBillBoard) {",
+                "                const sprite = particle;",
+                "                if (this.rendererSettings.renderMode === RenderMode.Mesh) {",
+                "                    if (!(sprite.rotation instanceof THREE.Quaternion)) {",
+                "                        sprite.rotation = new THREE.Quaternion();",
+                "                    }",
+                "                    if (this.startRotation.type === 'rotation') {",
+                "                        this.startRotation.genValue(sprite.rotation, emissionState.time / this.duration);",
+                "                    }",
+                "                    else {",
+                "                        sprite.rotation.setFromAxisAngle(UP, this.startRotation.genValue((emissionState.time / this.duration)));",
+                "                    }",
+                "                }",
+                "                else {",
+                "                    if (this.startRotation.type === 'rotation') {",
+                "                        sprite.rotation = 0;",
+                "                    }",
+                "                    else {",
+                "                        sprite.rotation = this.startRotation.genValue(emissionState.time / this.duration);",
+                "                    }",
+                "                }",
+                "            }",
+                "            else if (this.rendererSettings.renderMode === RenderMode.Trail) {",
+                "                const trail = particle;",
+                "                trail.length = this.rendererEmitterSettings.startLength.genValue(emissionState.time / this.duration);",
+                "            }",
+                "            this.emitterShape.initialize(particle);",
+                "            if (this.rendererSettings.renderMode === RenderMode.Trail &&",
+                "                this.rendererEmitterSettings.followLocalOrigin) {",
+                "                const trail = particle;",
+                "                trail.localPosition = new THREE.Vector3().copy(trail.position);",
+                "            }",
+                "            if (this.worldSpace) {",
+                "                particle.position.applyMatrix4(matrix);",
+                "                particle.startSize =",
+                "                    (particle.startSize * (Math.abs(scale.x) + Math.abs(scale.y) + Math.abs(scale.z))) / 3;",
+                "                particle.size = particle.startSize;",
+                "                particle.velocity.multiply(scale).applyMatrix3(this.normalMatrix);",
+                "                if (particle.rotation && particle.rotation instanceof THREE.Quaternion) {",
+                "                    particle.rotation.multiplyQuaternions(tempQ$1, particle.rotation);",
+                "                }",
+                "            }",
+                "            else {",
+                "                if (this.onlyUsedByOther) {",
+                "                    particle.parentMatrix = matrix;",
+                "                }",
+                "            }",
+                "            for (let j = 0; j < this.behaviors.length; j++) {",
+                "                this.behaviors[j].initialize(particle);",
+                "            }",
+                "        }",
+                "    }",
+                "    endEmit() {",
+                "        this.emitEnded = true;",
+                "        if (this.autoDestroy) {",
+                "            this.markForDestroy = true;",
+                "        }",
+                "    }",
+                "    dispose() {",
+                "        if (this._renderer)",
+                "            this._renderer.deleteSystem(this);",
+                "        this.emitter.dispose();",
+                "        if (this.emitter.parent)",
+                "            this.emitter.parent.remove(this.emitter);",
+                "    }",
+                "    restart() {",
+                "        this.paused = false;",
+                "        this.particleNum = 0;",
+                "        this.emissionState.burstIndex = 0;",
+                "        this.emissionState.burstWaveIndex = 0;",
+                "        this.emissionState.time = 0;",
+                "        this.emissionState.waitEmiting = 0;",
+                "        this.behaviors.forEach((behavior) => {",
+                "            behavior.reset();",
+                "        });",
+                "        this.emitEnded = false;",
+                "        this.markForDestroy = false;",
+                "        this.prewarmed = false;",
+                "    }",
+                "    update(delta) {",
+                "        if (this.paused)",
+                "            return;",
+                "        let currentParent = this.emitter;",
+                "        while (currentParent.parent) {",
+                "            currentParent = currentParent.parent;",
+                "        }",
+                "        if (currentParent.type !== 'Scene') {",
+                "            this.dispose();",
+                "            return;",
+                "        }",
+                "        if (this.firstTimeUpdate) {",
+                "            this.firstTimeUpdate = false;",
+                "            this.emitter.updateWorldMatrix(true, false);",
+                "        }",
+                "        if (this.emitEnded && this.particleNum === 0) {",
+                "            if (this.markForDestroy && this.emitter.parent)",
+                "                this.dispose();",
+                "            return;",
+                "        }",
+                "        if (this.looping && this.prewarm && !this.prewarmed) {",
+                "            this.prewarmed = true;",
+                "            for (let i = 0; i < this.duration * PREWARM_FPS$1; i++) {",
+                "                this.update(1.0 / PREWARM_FPS$1);",
+                "            }",
+                "        }",
+                "        if (delta > 0.1) {",
+                "            delta = 0.1;",
+                "        }",
+                "        if (this.neededToUpdateRender) {",
+                "            if (this._renderer)",
+                "                this._renderer.updateSystem(this);",
+                "            this.neededToUpdateRender = false;",
+                "        }",
+                "        if (!this.onlyUsedByOther) {",
+                "            this.emit(delta, this.emissionState, this.emitter.matrixWorld);",
+                "        }",
+                "        this.emitterShape.update(this, delta);",
+                "        for (let j = 0; j < this.behaviors.length; j++) {",
+                "            for (let i = 0; i < this.particleNum; i++) {",
+                "                if (!this.particles[i].died) {",
+                "                    this.behaviors[j].update(this.particles[i], delta);",
+                "                }",
+                "            }",
+                "            this.behaviors[j].frameUpdate(delta);",
+                "        }",
+                "        for (let i = 0; i < this.particleNum; i++) {",
+                "            if (this.rendererEmitterSettings.followLocalOrigin &&",
+                "                this.particles[i].localPosition) {",
+                "                this.particles[i].position.copy(this.particles[i].localPosition);",
+                "                if (this.particles[i].parentMatrix) {",
+                "                    this.particles[i].position.applyMatrix4(this.particles[i].parentMatrix);",
+                "                }",
+                "                else {",
+                "                    this.particles[i].position.applyMatrix4(this.emitter.matrixWorld);",
+                "                }",
+                "            }",
+                "            else {",
+                "                this.particles[i].position.addScaledVector(this.particles[i].velocity, delta * this.particles[i].speedModifier);",
+                "            }",
+                "            this.particles[i].age += delta;",
+                "        }",
+                "        if (this.rendererSettings.renderMode === RenderMode.Trail) {",
+                "            for (let i = 0; i < this.particleNum; i++) {",
+                "                const particle = this.particles[i];",
+                "                particle.update();",
+                "            }",
+                "        }",
+                "        for (let i = 0; i < this.particleNum; i++) {",
+                "            const particle = this.particles[i];",
+                "            if (particle.died && (!(particle instanceof TrailParticle) || particle.previous.length === 0)) {",
+                "                this.particles[i] = this.particles[this.particleNum - 1];",
+                "                this.particles[this.particleNum - 1] = particle;",
+                "                this.particleNum--;",
+                "                i--;",
+                "            }",
+                "        }",
+                "    }",
+                "    emit(delta, emissionState, emitterMatrix) {",
+                "        if (emissionState.time > this.duration) {",
+                "            if (this.looping) {",
+                "                emissionState.time -= this.duration;",
+                "                emissionState.burstIndex = 0;",
+                "                this.behaviors.forEach((behavior) => {",
+                "                    behavior.reset();",
+                "                });",
+                "            }",
+                "            else {",
+                "                if (!this.emitEnded && !this.onlyUsedByOther) {",
+                "                    this.endEmit();",
+                "                }",
+                "            }",
+                "        }",
+                "        this.normalMatrix.getNormalMatrix(emitterMatrix);",
+                "        const totalSpawn = Math.ceil(emissionState.waitEmiting);",
+                "        this.spawn(totalSpawn, emissionState, emitterMatrix);",
+                "        emissionState.waitEmiting -= totalSpawn;",
+                "        while (emissionState.burstIndex < this.emissionBursts.length &&",
+                "            this.emissionBursts[emissionState.burstIndex].time <= emissionState.time) {",
+                "            if (Math.random() < this.emissionBursts[emissionState.burstIndex].probability) {",
+                "                const count = this.emissionBursts[emissionState.burstIndex].count.genValue(this.time);",
+                "                this.spawn(count, emissionState, emitterMatrix);",
+                "            }",
+                "            emissionState.burstIndex++;",
+                "        }",
+                "        if (!this.emitEnded) {",
+                "            emissionState.waitEmiting += delta * this.emissionOverTime.genValue(emissionState.time / this.duration);",
+                "            if (emissionState.previousWorldPos != undefined) {",
+                "                this.temp.set(emitterMatrix.elements[12], emitterMatrix.elements[13], emitterMatrix.elements[14]);",
+                "                emissionState.travelDistance += emissionState.previousWorldPos.distanceTo(this.temp);",
+                "                const emitPerMeter = this.emissionOverDistance.genValue(emissionState.time / this.duration);",
+                "                if (emissionState.travelDistance * emitPerMeter > 0) {",
+                "                    const count = Math.floor(emissionState.travelDistance * emitPerMeter);",
+                "                    emissionState.travelDistance -= count / emitPerMeter;",
+                "                    emissionState.waitEmiting += count;",
+                "                }",
+                "            }",
+                "        }",
+                "        if (emissionState.previousWorldPos === undefined)",
+                "            emissionState.previousWorldPos = new THREE.Vector3();",
+                "        emissionState.previousWorldPos.set(emitterMatrix.elements[12], emitterMatrix.elements[13], emitterMatrix.elements[14]);",
+                "        emissionState.time += delta;",
+                "    }",
+                "    toJSON(meta, options = {}) {",
+                "        const isRootObject = meta === undefined || typeof meta === 'string';",
+                "        if (isRootObject) {",
+                "            meta = {",
+                "                geometries: {},",
+                "                materials: {},",
+                "                textures: {},",
+                "                images: {},",
+                "                shapes: {},",
+                "                skeletons: {},",
+                "                animations: {},",
+                "                nodes: {},",
+                "            };",
+                "        }",
+                "        meta.materials[this.rendererSettings.material.uuid] = this.rendererSettings.material.toJSON(meta);",
+                "        if (options.useUrlForImage) {",
+                "            if (this.texture.source !== undefined) {",
+                "                const image = this.texture.source;",
+                "                meta.images[image.uuid] = {",
+                "                    uuid: image.uuid,",
+                "                    url: this.texture.image.url,",
+                "                };",
+                "            }",
+                "        }",
+                "        let rendererSettingsJSON;",
+                "        if (this.renderMode === RenderMode.Trail) {",
+                "            rendererSettingsJSON = {",
+                "                startLength: this.rendererEmitterSettings.startLength.toJSON(),",
+                "                followLocalOrigin: this.rendererEmitterSettings.followLocalOrigin,",
+                "            };",
+                "        }",
+                "        else if (this.renderMode === RenderMode.Mesh) {",
+                "            rendererSettingsJSON = {};",
+                "        }",
+                "        else if (this.renderMode === RenderMode.StretchedBillBoard) {",
+                "            rendererSettingsJSON = {",
+                "                speedFactor: this.rendererEmitterSettings.speedFactor,",
+                "                lengthFactor: this.rendererEmitterSettings.lengthFactor,",
+                "            };",
+                "        }",
+                "        else {",
+                "            rendererSettingsJSON = {};",
+                "        }",
+                "        const geometry = this.rendererSettings.instancingGeometry;",
+                "        if (meta.geometries && !meta.geometries[geometry.uuid]) {",
+                "            meta.geometries[geometry.uuid] = geometry.toJSON();",
+                "        }",
+                "        return {",
+                "            version: '2.0',",
+                "            autoDestroy: this.autoDestroy,",
+                "            looping: this.looping,",
+                "            prewarm: this.prewarm,",
+                "            duration: this.duration,",
+                "            shape: this.emitterShape.toJSON(),",
+                "            startLife: this.startLife.toJSON(),",
+                "            startSpeed: this.startSpeed.toJSON(),",
+                "            startRotation: this.startRotation.toJSON(),",
+                "            startSize: this.startSize.toJSON(),",
+                "            startColor: this.startColor.toJSON(),",
+                "            emissionOverTime: this.emissionOverTime.toJSON(),",
+                "            emissionOverDistance: this.emissionOverDistance.toJSON(),",
+                "            emissionBursts: this.emissionBursts.map((burst) => ({",
+                "                time: burst.time,",
+                "                count: burst.count.toJSON(),",
+                "                probability: burst.probability,",
+                "                interval: burst.interval,",
+                "                cycle: burst.cycle,",
+                "            })),",
+                "            onlyUsedByOther: this.onlyUsedByOther,",
+                "            instancingGeometry: this.rendererSettings.instancingGeometry.uuid,",
+                "            renderOrder: this.renderOrder,",
+                "            renderMode: this.renderMode,",
+                "            rendererEmitterSettings: rendererSettingsJSON,",
+                "            material: this.rendererSettings.material.uuid,",
+                "            layers: this.layers.mask,",
+                "            startTileIndex: this.startTileIndex.toJSON(),",
+                "            uTileCount: this.uTileCount,",
+                "            vTileCount: this.vTileCount,",
+                "            behaviors: this.behaviors.map((behavior) => behavior.toJSON()),",
+                "            worldSpace: this.worldSpace,",
+                "        };",
+                "    }",
+                "    static fromJSON(json, meta, dependencies) {",
+                "        var _a, _b;",
+                "        const shape = EmitterFromJSON(json.shape, meta);",
+                "        let rendererEmitterSettings;",
+                "        if (json.renderMode === RenderMode.Trail) {",
+                "            let trailSettings = json.rendererEmitterSettings;",
+                "            rendererEmitterSettings = {",
+                "                startLength: trailSettings.startLength != undefined",
+                "                    ? ValueGeneratorFromJSON(trailSettings.startLength)",
+                "                    : new ConstantValue(30),",
+                "                followLocalOrigin: trailSettings.followLocalOrigin,",
+                "            };",
+                "        }",
+                "        else if (json.renderMode === RenderMode.Mesh) {",
+                "            rendererEmitterSettings = {};",
+                "        }",
+                "        else if (json.renderMode === RenderMode.StretchedBillBoard) {",
+                "            rendererEmitterSettings = json.rendererEmitterSettings;",
+                "            if (json.speedFactor != undefined) {",
+                "                rendererEmitterSettings.speedFactor = json.speedFactor;",
+                "            }",
+                "        }",
+                "        else {",
+                "            rendererEmitterSettings = {};",
+                "        }",
+                "        const layers = new THREE.Layers();",
+                "        if (json.layers) {",
+                "            layers.mask = json.layers;",
+                "        }",
+                "        const ps = new ParticleSystem({",
+                "            autoDestroy: json.autoDestroy,",
+                "            looping: json.looping,",
+                "            prewarm: json.prewarm,",
+                "            duration: json.duration,",
+                "            shape: shape,",
+                "            startLife: ValueGeneratorFromJSON(json.startLife),",
+                "            startSpeed: ValueGeneratorFromJSON(json.startSpeed),",
+                "            startRotation: GeneratorFromJSON(json.startRotation),",
+                "            startSize: ValueGeneratorFromJSON(json.startSize),",
+                "            startColor: ColorGeneratorFromJSON(json.startColor),",
+                "            emissionOverTime: ValueGeneratorFromJSON(json.emissionOverTime),",
+                "            emissionOverDistance: ValueGeneratorFromJSON(json.emissionOverDistance),",
+                "            emissionBursts: (_a = json.emissionBursts) === null || _a === void 0 ? void 0 : _a.map((burst) => ({",
+                "                time: burst.time,",
+                "                count: typeof burst.count === 'number'",
+                "                    ? new ConstantValue(burst.count)",
+                "                    : ValueGeneratorFromJSON(burst.count),",
+                "                probability: burst.probability,",
+                "                interval: burst.interval,",
+                "                cycle: burst.cycle,",
+                "            })),",
+                "            onlyUsedByOther: json.onlyUsedByOther,",
+                "            instancingGeometry: meta.geometries[json.instancingGeometry],",
+                "            renderMode: json.renderMode,",
+                "            rendererEmitterSettings: rendererEmitterSettings,",
+                "            renderOrder: json.renderOrder,",
+                "            layers: layers,",
+                "            material: json.material",
+                "                ? meta.materials[json.material]",
+                "                : json.texture",
+                "                    ? new THREE.MeshBasicMaterial({",
+                "                        map: meta.textures[json.texture],",
+                "                        transparent: (_b = json.transparent) !== null && _b !== void 0 ? _b : true,",
+                "                        blending: json.blending,",
+                "                        side: THREE.DoubleSide,",
+                "                    })",
+                "                    : new THREE.MeshBasicMaterial({",
+                "                        color: 0xffffff,",
+                "                        transparent: true,",
+                "                        blending: THREE.AdditiveBlending,",
+                "                        side: THREE.DoubleSide,",
+                "                    }),",
+                "            startTileIndex: typeof json.startTileIndex === 'number'",
+                "                ? new ConstantValue(json.startTileIndex)",
+                "                : ValueGeneratorFromJSON(json.startTileIndex),",
+                "            uTileCount: json.uTileCount,",
+                "            vTileCount: json.vTileCount,",
+                "            behaviors: [],",
+                "            worldSpace: json.worldSpace,",
+                "        });",
+                "        ps.behaviors = json.behaviors.map((behaviorJson) => {",
+                "            const behavior = BehaviorFromJSON(behaviorJson, ps);",
+                "            if (behavior.type === 'EmitSubParticleSystem') {",
+                "                dependencies[behaviorJson.subParticleSystem] = behavior;",
+                "            }",
+                "            return behavior;",
+                "        });",
+                "        return ps;",
+                "    }",
+                "    addBehavior(behavior) {",
+                "        this.behaviors.push(behavior);",
+                "    }",
+                "    getRendererSettings() {",
+                "        return this.rendererSettings;",
+                "    }",
+                "    clone() {",
+                "        const newEmissionBursts = [];",
+                "        for (const emissionBurst of this.emissionBursts) {",
+                "            const newEmissionBurst = {};",
+                "            Object.assign(newEmissionBurst, emissionBurst);",
+                "            newEmissionBursts.push(newEmissionBurst);",
+                "        }",
+                "        const newBehaviors = [];",
+                "        for (const behavior of this.behaviors) {",
+                "            newBehaviors.push(behavior.clone());",
+                "        }",
+                "        let rendererEmitterSettings;",
+                "        if (this.renderMode === RenderMode.Trail) {",
+                "            rendererEmitterSettings = {",
+                "                startLength: this.rendererEmitterSettings.startLength.clone(),",
+                "                followLocalOrigin: this.rendererEmitterSettings.followLocalOrigin,",
+                "            };",
+                "        }",
+                "        else {",
+                "            rendererEmitterSettings = {};",
+                "        }",
+                "        const layers = new THREE.Layers();",
+                "        layers.mask = this.layers.mask;",
+                "        return new ParticleSystem({",
+                "            autoDestroy: this.autoDestroy,",
+                "            looping: this.looping,",
+                "            duration: this.duration,",
+                "            shape: this.emitterShape.clone(),",
+                "            startLife: this.startLife.clone(),",
+                "            startSpeed: this.startSpeed.clone(),",
+                "            startRotation: this.startRotation.clone(),",
+                "            startSize: this.startSize.clone(),",
+                "            startColor: this.startColor.clone(),",
+                "            emissionOverTime: this.emissionOverTime.clone(),",
+                "            emissionOverDistance: this.emissionOverDistance.clone(),",
+                "            emissionBursts: newEmissionBursts,",
+                "            onlyUsedByOther: this.onlyUsedByOther,",
+                "            instancingGeometry: this.rendererSettings.instancingGeometry,",
+                "            renderMode: this.renderMode,",
+                "            renderOrder: this.renderOrder,",
+                "            rendererEmitterSettings: rendererEmitterSettings,",
+                "            material: this.rendererSettings.material,",
+                "            startTileIndex: this.startTileIndex,",
+                "            uTileCount: this.uTileCount,",
+                "            vTileCount: this.vTileCount,",
+                "            behaviors: newBehaviors,",
+                "            worldSpace: this.worldSpace,",
+                "            layers: layers,",
+                "        });",
+                "    }",
+                "}",
+                "",
+                "var particle_frag = `",
+                "",
+                "#include <common>",
+                "#include <uv_pars_fragment>",
+                "#include <color_pars_fragment>",
+                "#include <map_pars_fragment>",
+                "#include <logdepthbuf_pars_fragment>",
+                "#include <clipping_planes_pars_fragment>",
+                "#include <alphatest_pars_fragment>",
+                "",
+                "void main() {",
+                "",
+                "    #include <clipping_planes_fragment>",
+                "    ",
+                "    vec3 outgoingLight = vec3( 0.0 );",
+                "    vec4 diffuseColor = vColor;",
+                "    ",
+                "    #include <logdepthbuf_fragment>",
+                "    ",
+                "    #ifdef USE_MAP",
+                "    diffuseColor *= texture2D( map, vMapUv);",
+                "    #endif",
+                "    ",
+                "    #include <alphatest_fragment>",
+                "",
+                "    outgoingLight = diffuseColor.rgb;",
+                "    ",
+                "    #ifdef USE_COLOR_AS_ALPHA",
+                "    gl_FragColor = vec4( outgoingLight, diffuseColor.r );",
+                "    #else",
+                "    gl_FragColor = vec4( outgoingLight, diffuseColor.a );",
+                "    #endif",
+                "    ",
+                "    ",
+                "    #include <tonemapping_fragment>",
+                "",
+                "}",
+                "`;",
+                "",
+                "var particle_physics_frag = `",
+                "#define STANDARD",
+                "#ifdef PHYSICAL",
+                "#define IOR",
+                "#define SPECULAR",
+                "#endif",
+                "uniform vec3 diffuse;",
+                "uniform vec3 emissive;",
+                "uniform float roughness;",
+                "uniform float metalness;",
+                "uniform float opacity;",
+                "#ifdef IOR",
+                "uniform float ior;",
+                "#endif",
+                "#ifdef SPECULAR",
+                "uniform float specularIntensity;",
+                "uniform vec3 specularColor;",
+                "#ifdef USE_SPECULARINTENSITYMAP",
+                "uniform sampler2D specularIntensityMap;",
+                "#endif",
+                "#ifdef USE_SPECULARCOLORMAP",
+                "uniform sampler2D specularColorMap;",
+                "#endif",
+                "#endif",
+                "#ifdef USE_CLEARCOAT",
+                "uniform float clearcoat;",
+                "uniform float clearcoatRoughness;",
+                "#endif",
+                "#ifdef USE_IRIDESCENCE",
+                "uniform float iridescence;",
+                "uniform float iridescenceIOR;",
+                "uniform float iridescenceThicknessMinimum;",
+                "uniform float iridescenceThicknessMaximum;",
+                "#endif",
+                "#ifdef USE_SHEEN",
+                "uniform vec3 sheenColor;",
+                "uniform float sheenRoughness;",
+                "#ifdef USE_SHEENCOLORMAP",
+                "uniform sampler2D sheenColorMap;",
+                "#endif",
+                "#ifdef USE_SHEENROUGHNESSMAP",
+                "uniform sampler2D sheenRoughnessMap;",
+                "#endif",
+                "#endif",
+                "",
+                "varying vec3 vViewPosition;",
+                "#include <common>",
+                "#include <packing>",
+                "#include <dithering_pars_fragment>",
+                "#include <color_pars_fragment>",
+                "#include <uv_pars_fragment>",
+                "#include <map_pars_fragment>",
+                "#include <alphamap_pars_fragment>",
+                "#include <alphatest_pars_fragment>",
+                "#include <aomap_pars_fragment>",
+                "#include <lightmap_pars_fragment>",
+                "#include <emissivemap_pars_fragment>",
+                "#include <bsdfs>",
+                "#include <iridescence_fragment>",
+                "#include <cube_uv_reflection_fragment>",
+                "#include <envmap_common_pars_fragment>",
+                "#include <envmap_physical_pars_fragment>",
+                "#include <fog_pars_fragment>",
+                "#include <lights_pars_begin>",
+                "#include <normal_pars_fragment>",
+                "#include <lights_physical_pars_fragment>",
+                "#include <transmission_pars_fragment>",
+                "#include <shadowmap_pars_fragment>",
+                "#include <bumpmap_pars_fragment>",
+                "#include <normalmap_pars_fragment>",
+                "#include <clearcoat_pars_fragment>",
+                "#include <iridescence_pars_fragment>",
+                "#include <roughnessmap_pars_fragment>",
+                "#include <metalnessmap_pars_fragment>",
+                "#include <logdepthbuf_pars_fragment>",
+                "#include <clipping_planes_pars_fragment>",
+                "",
+                "void main() {",
+                "    #include <clipping_planes_fragment>",
+                "    vec4 diffuseColor = vec4( diffuse, opacity );",
+                "    ReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) );",
+                "    vec3 totalEmissiveRadiance = emissive;",
+                "    #include <logdepthbuf_fragment>",
+                "    #include <map_fragment>",
+                "    #include <color_fragment>",
+                "    #include <alphamap_fragment>",
+                "    #include <alphatest_fragment>",
+                "    #include <roughnessmap_fragment>",
+                "    #include <metalnessmap_fragment>",
+                "    #include <normal_fragment_begin>",
+                "    #include <normal_fragment_maps>",
+                "    #include <clearcoat_normal_fragment_begin>",
+                "    #include <clearcoat_normal_fragment_maps>",
+                "    #include <emissivemap_fragment>",
+                "    // accumulation",
+                "    #include <lights_physical_fragment>",
+                "    #include <lights_fragment_begin>",
+                "    #include <lights_fragment_maps>",
+                "    #include <lights_fragment_end>",
+                "    // modulation",
+                "    #include <aomap_fragment>",
+                "    vec3 totalDiffuse = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse;",
+                "    vec3 totalSpecular = reflectedLight.directSpecular + reflectedLight.indirectSpecular;",
+                "    #include <transmission_fragment>",
+                "    vec3 outgoingLight = totalDiffuse + totalSpecular + totalEmissiveRadiance;",
+                "    #ifdef USE_SHEEN",
+                "    // Sheen energy compensation approximation calculation can be found at the end of",
+                "        // https://drive.google.com/file/d/1T0D1VSyR4AllqIJTQAraEIzjlb5h4FKH/view?usp=sharing",
+                "        float sheenEnergyComp = 1.0 - 0.157 * max3( material.sheenColor );",
+                "        outgoingLight = outgoingLight * sheenEnergyComp + sheenSpecular;",
+                "    #endif",
+                "    #ifdef USE_CLEARCOAT",
+                "        float dotNVcc = saturate( dot( geometry.clearcoatNormal, geometry.viewDir ) );",
+                "        vec3 Fcc = F_Schlick( material.clearcoatF0, material.clearcoatF90, dotNVcc );",
+                "        outgoingLight = outgoingLight * ( 1.0 - material.clearcoat * Fcc ) + clearcoatSpecular * material.clearcoat;",
+                "    #endif",
+                "    #include <output_fragment>",
+                "    #include <tonemapping_fragment>",
+                "    #include <encodings_fragment>",
+                "    #include <fog_fragment>",
+                "    #include <premultiplied_alpha_fragment>",
+                "    #include <dithering_fragment>",
+                "}`;",
+                "",
+                "var uv_vertex_tile = `",
+                "",
+                "    #ifdef UV_TILE",
+                "        float col = mod(uvTile, tileCount.x);",
+                "        float row = (tileCount.y - floor(uvTile / tileCount.x) - 1.0);",
+                "        ",
+                "        mat3 tileTransform = mat3(",
+                "          1.0 / tileCount.x, 0.0, 0.0,",
+                "          0.0, 1.0 / tileCount.y, 0.0, ",
+                "          col / tileCount.x, row / tileCount.y, 1.0);",
+                "    #else",
+                "        mat3 tileTransform = mat3(1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0);",
+                "    #endif",
+                "",
+                "#if defined( USE_UV ) || defined( USE_ANISOTROPY )",
+                "",
+                "vUv = (tileTransform *vec3( uv, 1 )).xy;",
+                "",
+                "#endif",
+                "#ifdef USE_MAP",
+                "",
+                "vMapUv = ( tileTransform * (mapTransform * vec3( MAP_UV, 1 ) )).xy;",
+                "",
+                "#endif",
+                "#ifdef USE_ALPHAMAP",
+                "",
+                "vAlphaMapUv = ( tileTransform * (alphaMapTransform * vec3( ALPHAMAP_UV, 1 ) )).xy;",
+                "",
+                "#endif",
+                "#ifdef USE_LIGHTMAP",
+                "",
+                "vLightMapUv = ( tileTransform * (lightMapTransform * vec3( LIGHTMAP_UV, 1 ) )).xy;",
+                "",
+                "#endif",
+                "#ifdef USE_AOMAP",
+                "",
+                "vAoMapUv = ( tileTransform * (aoMapTransform * vec3( AOMAP_UV, 1 ) )).xy;",
+                "",
+                "#endif",
+                "#ifdef USE_BUMPMAP",
+                "",
+                "vBumpMapUv = ( tileTransform * (bumpMapTransform * vec3( BUMPMAP_UV, 1 ) )).xy;",
+                "",
+                "#endif",
+                "#ifdef USE_NORMALMAP",
+                "",
+                "vNormalMapUv = ( tileTransform * (normalMapTransform * vec3( NORMALMAP_UV, 1 ) )).xy;",
+                "",
+                "#endif",
+                "#ifdef USE_DISPLACEMENTMAP",
+                "",
+                "vDisplacementMapUv = ( tileTransform * (displacementMapTransform * vec3( DISPLACEMENTMAP_UV, 1 ) )).xy;",
+                "",
+                "#endif",
+                "#ifdef USE_EMISSIVEMAP",
+                "",
+                "vEmissiveMapUv = ( tileTransform * (emissiveMapTransform * vec3( EMISSIVEMAP_UV, 1 ) )).xy;",
+                "",
+                "#endif",
+                "#ifdef USE_METALNESSMAP",
+                "",
+                "vMetalnessMapUv = ( tileTransform * (metalnessMapTransform * vec3( METALNESSMAP_UV, 1 ) )).xy;",
+                "",
+                "#endif",
+                "#ifdef USE_ROUGHNESSMAP",
+                "",
+                "vRoughnessMapUv = ( tileTransform * (roughnessMapTransform * vec3( ROUGHNESSMAP_UV, 1 ) )).xy;",
+                "",
+                "#endif",
+                "#ifdef USE_ANISOTROPYMAP",
+                "",
+                "vAnisotropyMapUv = ( tileTransform * (anisotropyMapTransform * vec3( ANISOTROPYMAP_UV, 1 ) )).xy;",
+                "",
+                "#endif",
+                "#ifdef USE_CLEARCOATMAP",
+                "",
+                "vClearcoatMapUv = ( tileTransform * (clearcoatMapTransform * vec3( CLEARCOATMAP_UV, 1 ) )).xy;",
+                "",
+                "#endif",
+                "#ifdef USE_CLEARCOAT_NORMALMAP",
+                "",
+                "vClearcoatNormalMapUv = ( tileTransform * (clearcoatNormalMapTransform * vec3( CLEARCOAT_NORMALMAP_UV, 1 ) )).xy;",
+                "",
+                "#endif",
+                "#ifdef USE_CLEARCOAT_ROUGHNESSMAP",
+                "",
+                "vClearcoatRoughnessMapUv = ( tileTransform * (clearcoatRoughnessMapTransform * vec3( CLEARCOAT_ROUGHNESSMAP_UV, 1 ) )).xy;",
+                "",
+                "#endif",
+                "#ifdef USE_IRIDESCENCEMAP",
+                "",
+                "vIridescenceMapUv = ( tileTransform * (iridescenceMapTransform * vec3( IRIDESCENCEMAP_UV, 1 ) )).xy;",
+                "",
+                "#endif",
+                "#ifdef USE_IRIDESCENCE_THICKNESSMAP",
+                "",
+                "vIridescenceThicknessMapUv = ( tileTransform * (iridescenceThicknessMapTransform * vec3( IRIDESCENCE_THICKNESSMAP_UV, 1 ) )).xy;",
+                "",
+                "#endif",
+                "#ifdef USE_SHEEN_COLORMAP",
+                "",
+                "vSheenColorMapUv = ( tileTransform * (sheenColorMapTransform * vec3( SHEEN_COLORMAP_UV, 1 ) )).xy;",
+                "",
+                "#endif",
+                "#ifdef USE_SHEEN_ROUGHNESSMAP",
+                "",
+                "vSheenRoughnessMapUv = ( tileTransform * (sheenRoughnessMapTransform * vec3( SHEEN_ROUGHNESSMAP_UV, 1 ) )).xy;",
+                "",
+                "#endif",
+                "#ifdef USE_SPECULARMAP",
+                "",
+                "vSpecularMapUv = ( tileTransform * (specularMapTransform * vec3( SPECULARMAP_UV, 1 ) )).xy;",
+                "",
+                "#endif",
+                "#ifdef USE_SPECULAR_COLORMAP",
+                "",
+                "vSpecularColorMapUv = ( tileTransform * (specularColorMapTransform * vec3( SPECULAR_COLORMAP_UV, 1 ) )).xy;",
+                "",
+                "#endif",
+                "#ifdef USE_SPECULAR_INTENSITYMAP",
+                "",
+                "vSpecularIntensityMapUv = ( tileTransform * (specularIntensityMapTransform * vec3( SPECULAR_INTENSITYMAP_UV, 1 ) )).xy;",
+                "",
+                "#endif",
+                "#ifdef USE_TRANSMISSIONMAP",
+                "",
+                "vTransmissionMapUv = ( tileTransform * transmissionMapTransform * vec3( TRANSMISSIONMAP_UV, 1 ) )).xy;",
+                "",
+                "#endif",
+                "#ifdef USE_THICKNESSMAP",
+                "",
+                "vThicknessMapUv = ( tileTransform * thicknessMapTransform * vec3( THICKNESSMAP_UV, 1 ) )).xy;",
+                "",
+                "#endif",
+                "`;",
+                "",
+                "var particle_vert = `",
+                "#include <common>",
+                "#include <color_pars_vertex>",
+                "#include <uv_pars_vertex>",
+                "#include <logdepthbuf_pars_vertex>",
+                "#include <clipping_planes_pars_vertex>",
+                "",
+                "attribute vec3 offset;",
+                "attribute float rotation;",
+                "attribute float size;",
+                "attribute float uvTile;",
+                "",
+                "#ifdef UV_TILE",
+                "uniform vec2 tileCount;",
+                "#endif",
+                "",
+                "void main() {",
+                "",
+                "    ${uv_vertex_tile}",
+                "\t",
+                "    vec2 alignedPosition = ( position.xy ) * size;",
+                "    ",
+                "    vec2 rotatedPosition;",
+                "    rotatedPosition.x = cos( rotation ) * alignedPosition.x - sin( rotation ) * alignedPosition.y;",
+                "    rotatedPosition.y = sin( rotation ) * alignedPosition.x + cos( rotation ) * alignedPosition.y;",
+                "#ifdef HORIZONTAL",
+                "    vec4 mvPosition = modelMatrix * vec4( offset, 1.0 );",
+                "    mvPosition.x += rotatedPosition.x;",
+                "    mvPosition.z -= rotatedPosition.y;",
+                "    mvPosition = viewMatrix * mvPosition;",
+                "#elif defined(VERTICAL)",
+                "    vec4 mvPosition = modelMatrix * vec4( offset, 1.0 );",
+                "    mvPosition.y += rotatedPosition.y;",
+                "    mvPosition = viewMatrix * mvPosition;",
+                "    mvPosition.x += rotatedPosition.x;",
+                "#else",
+                "    vec4 mvPosition = modelViewMatrix * vec4( offset, 1.0 );",
+                "    mvPosition.xy += rotatedPosition;",
+                "#endif",
+                "",
+                "\tvColor = color;",
+                "",
+                "\tgl_Position = projectionMatrix * mvPosition;",
+                "",
+                "\t#include <logdepthbuf_vertex>",
+                "\t#include <clipping_planes_vertex>",
+                "",
+                "}",
+                "`;",
+                "",
+                "var local_particle_vert = `",
+                "#include <common>",
+                "#include <uv_pars_vertex>",
+                "#include <color_pars_vertex>",
+                "#include <logdepthbuf_pars_vertex>",
+                "#include <clipping_planes_pars_vertex>",
+                "",
+                "attribute vec3 offset;",
+                "attribute vec4 rotation;",
+                "attribute float size;",
+                "// attribute vec4 color;",
+                "attribute float uvTile;",
+                "",
+                "#ifdef UV_TILE",
+                "uniform vec2 tileCount;",
+                "#endif",
+                "",
+                "void main() {",
+                "",
+                "    ${uv_vertex_tile}",
+                "    ",
+                "    float x2 = rotation.x + rotation.x, y2 = rotation.y + rotation.y, z2 = rotation.z + rotation.z;",
+                "    float xx = rotation.x * x2, xy = rotation.x * y2, xz = rotation.x * z2;",
+                "    float yy = rotation.y * y2, yz = rotation.y * z2, zz = rotation.z * z2;",
+                "    float wx = rotation.w * x2, wy = rotation.w * y2, wz = rotation.w * z2;",
+                "    float sx = size, sy = size, sz = size;",
+                "    ",
+                "    mat4 matrix = mat4(( 1.0 - ( yy + zz ) ) * sx, ( xy + wz ) * sx, ( xz - wy ) * sx, 0.0,  // 1. column",
+                "                      ( xy - wz ) * sy, ( 1.0 - ( xx + zz ) ) * sy, ( yz + wx ) * sy, 0.0,  // 2. column",
+                "                      ( xz + wy ) * sz, ( yz - wx ) * sz, ( 1.0 - ( xx + yy ) ) * sz, 0.0,  // 3. column",
+                "                      offset.x, offset.y, offset.z, 1.0);",
+                "    ",
+                "    vec4 mvPosition = modelViewMatrix * (matrix * vec4( position, 1.0 ));",
+                "",
+                "\tvColor = color;",
+                "",
+                "\tgl_Position = projectionMatrix * mvPosition;",
+                "",
+                "\t#include <logdepthbuf_vertex>",
+                "\t#include <clipping_planes_vertex>",
+                "",
+                "}",
+                "`;",
+                "",
+                "var local_particle_physics_vert = `",
+                "#define STANDARD",
+                "varying vec3 vViewPosition;",
+                "#ifdef USE_TRANSMISSION",
+                "\tvarying vec3 vWorldPosition;",
+                "#endif",
+                "#include <common>",
+                "#include <uv_pars_vertex>",
+                "",
+                "attribute vec3 offset;",
+                "attribute vec4 rotation;",
+                "attribute float size;",
+                "attribute float uvTile;",
+                "",
+                "#ifdef UV_TILE",
+                "uniform vec2 tileCount;",
+                "#endif",
+                "",
+                "#include <displacementmap_pars_vertex>",
+                "#include <color_pars_vertex>",
+                "#include <fog_pars_vertex>",
+                "#include <normal_pars_vertex>",
+                "#include <morphtarget_pars_vertex>",
+                "#include <skinning_pars_vertex>",
+                "#include <shadowmap_pars_vertex>",
+                "#include <logdepthbuf_pars_vertex>",
+                "#include <clipping_planes_pars_vertex>",
+                "",
+                "void main() {",
+                "    ${uv_vertex_tile}",
+                "",
+                "    float x2 = rotation.x + rotation.x, y2 = rotation.y + rotation.y, z2 = rotation.z + rotation.z;",
+                "    float xx = rotation.x * x2, xy = rotation.x * y2, xz = rotation.x * z2;",
+                "    float yy = rotation.y * y2, yz = rotation.y * z2, zz = rotation.z * z2;",
+                "    float wx = rotation.w * x2, wy = rotation.w * y2, wz = rotation.w * z2;",
+                "    float sx = size, sy = size, sz = size;",
+                "",
+                "    mat4 particleMatrix = mat4(( 1.0 - ( yy + zz ) ) * sx, ( xy + wz ) * sx, ( xz - wy ) * sx, 0.0,  // 1. column",
+                "                      ( xy - wz ) * sy, ( 1.0 - ( xx + zz ) ) * sy, ( yz + wx ) * sy, 0.0,  // 2. column",
+                "                      ( xz + wy ) * sz, ( yz - wx ) * sz, ( 1.0 - ( xx + yy ) ) * sz, 0.0,  // 3. column",
+                "                      offset.x, offset.y, offset.z, 1.0);",
+                "",
+                "\t#include <color_vertex>",
+                "\t#include <morphcolor_vertex>",
+                "\t#include <beginnormal_vertex>",
+                "\t#include <morphnormal_vertex>",
+                "\t#include <skinbase_vertex>",
+                "\t#include <skinnormal_vertex>",
+                "",
+                "\t// replace defaultnormal_vertex",
+                "\tvec3 transformedNormal = objectNormal;",
+                "    mat3 m = mat3( particleMatrix );",
+                "    transformedNormal /= vec3( dot( m[ 0 ], m[ 0 ] ), dot( m[ 1 ], m[ 1 ] ), dot( m[ 2 ], m[ 2 ] ) );",
+                "    transformedNormal = m * transformedNormal;",
+                "    transformedNormal = normalMatrix * transformedNormal;",
+                "    #ifdef FLIP_SIDED",
+                "        transformedNormal = - transformedNormal;",
+                "    #endif",
+                "    #ifdef USE_TANGENT",
+                "        vec3 transformedTangent = ( modelViewMatrix * vec4( objectTangent, 0.0 ) ).xyz;",
+                "        #ifdef FLIP_SIDED",
+                "        transformedTangent = - transformedTangent;",
+                "        #endif",
+                "    #endif",
+                "",
+                "\t#include <normal_vertex>",
+                "\t#include <begin_vertex>",
+                "\t#include <morphtarget_vertex>",
+                "\t#include <skinning_vertex>",
+                "\t#include <displacementmap_vertex>",
+                "",
+                "\t// replace include <project_vertex>",
+                "  vec4 mvPosition = vec4( transformed, 1.0 );",
+                "  mvPosition = modelViewMatrix * (particleMatrix * mvPosition);",
+                "\tgl_Position = projectionMatrix * mvPosition;",
+                "",
+                "\t#include <logdepthbuf_vertex>",
+                "\t#include <clipping_planes_vertex>",
+                "\t",
+                "\tvViewPosition = - mvPosition.xyz;",
+                "\t",
+                "\t#include <worldpos_vertex>",
+                "\t#include <shadowmap_vertex>",
+                "\t#include <fog_vertex>",
+                "#ifdef USE_TRANSMISSION",
+                "\tvWorldPosition = worldPosition.xyz;",
+                "#endif",
+                "}",
+                "`;",
+                "",
+                "var stretched_bb_particle_vert = `",
+                "#include <common>",
+                "#include <uv_pars_vertex>",
+                "#include <color_pars_vertex>",
+                "#include <logdepthbuf_pars_vertex>",
+                "#include <clipping_planes_pars_vertex>",
+                "",
+                "attribute vec3 offset;",
+                "attribute float rotation;",
+                "attribute float size;",
+                "attribute vec4 velocity;",
+                "attribute float uvTile;",
+                "",
+                "#ifdef UV_TILE",
+                "uniform vec2 tileCount;",
+                "#endif",
+                "",
+                "uniform float speedFactor;",
+                "",
+                "void main() {",
+                "",
+                "    ${uv_vertex_tile}",
+                "    ",
+                "    float lengthFactor = velocity.w;",
+                "#ifdef USE_SKEW",
+                "    vec4 mvPosition = modelViewMatrix * vec4( offset, 1.0 );",
+                "    vec3 viewVelocity = normalMatrix * velocity.xyz;",
+                "",
+                "    vec3 scaledPos = vec3(position.xy * size, position.z);",
+                "    float vlength = length(viewVelocity);",
+                "    vec3 projVelocity =  dot(scaledPos, viewVelocity) * viewVelocity / vlength;",
+                "    mvPosition.xyz += scaledPos + projVelocity * (speedFactor / size + lengthFactor / vlength);",
+                "#else",
+                "    vec4 mvPosition = modelViewMatrix * vec4( offset, 1.0 );",
+                "    vec3 viewVelocity = normalMatrix * velocity.xyz;",
+                "    float vlength = length(viewVelocity); ",
+                "    mvPosition.xyz += position.y * normalize(cross(mvPosition.xyz, viewVelocity)) * size; // switch the cross to  match unity implementation",
+                "    mvPosition.xyz -= (position.x + 0.5) * viewVelocity * (1.0 + lengthFactor / vlength) * size; // minus position.x to match unity implementation",
+                "#endif",
+                "\tvColor = color;",
+                "\tgl_Position = projectionMatrix * mvPosition;",
+                "\t#include <logdepthbuf_vertex>",
+                "\t#include <clipping_planes_vertex>",
+                "}",
+                "`;",
+                "",
+                "function getMaterialUVChannelName(value) {",
+                "    if (value === 0)",
+                "        return 'uv';",
+                "    return `uv${value}`;",
+                "}",
+                "",
+                "new THREE.Vector3(0, 0, 1);",
+                "class SpriteBatch extends VFXBatch {",
+                "    constructor(settings) {",
+                "        super(settings);",
+                "        this.vector_ = new THREE.Vector3();",
+                "        this.vector2_ = new THREE.Vector3();",
+                "        this.vector3_ = new THREE.Vector3();",
+                "        this.quaternion_ = new THREE.Quaternion();",
+                "        this.quaternion2_ = new THREE.Quaternion();",
+                "        this.quaternion3_ = new THREE.Quaternion();",
+                "        this.rotationMat_ = new THREE.Matrix3();",
+                "        this.rotationMat2_ = new THREE.Matrix3();",
+                "        this.maxParticles = 1000;",
+                "        this.setupBuffers();",
+                "        this.rebuildMaterial();",
+                "    }",
+                "    buildExpandableBuffers() {",
+                "        this.offsetBuffer = new THREE.InstancedBufferAttribute(new Float32Array(this.maxParticles * 3), 3);",
+                "        this.offsetBuffer.setUsage(THREE.DynamicDrawUsage);",
+                "        this.geometry.setAttribute('offset', this.offsetBuffer);",
+                "        this.colorBuffer = new THREE.InstancedBufferAttribute(new Float32Array(this.maxParticles * 4), 4);",
+                "        this.colorBuffer.setUsage(THREE.DynamicDrawUsage);",
+                "        this.geometry.setAttribute('color', this.colorBuffer);",
+                "        if (this.settings.renderMode === RenderMode.Mesh) {",
+                "            this.rotationBuffer = new THREE.InstancedBufferAttribute(new Float32Array(this.maxParticles * 4), 4);",
+                "            this.rotationBuffer.setUsage(THREE.DynamicDrawUsage);",
+                "            this.geometry.setAttribute('rotation', this.rotationBuffer);",
+                "        }",
+                "        else if (this.settings.renderMode === RenderMode.BillBoard ||",
+                "            this.settings.renderMode === RenderMode.HorizontalBillBoard ||",
+                "            this.settings.renderMode === RenderMode.VerticalBillBoard ||",
+                "            this.settings.renderMode === RenderMode.StretchedBillBoard) {",
+                "            this.rotationBuffer = new THREE.InstancedBufferAttribute(new Float32Array(this.maxParticles), 1);",
+                "            this.rotationBuffer.setUsage(THREE.DynamicDrawUsage);",
+                "            this.geometry.setAttribute('rotation', this.rotationBuffer);",
+                "        }",
+                "        this.sizeBuffer = new THREE.InstancedBufferAttribute(new Float32Array(this.maxParticles), 1);",
+                "        this.sizeBuffer.setUsage(THREE.DynamicDrawUsage);",
+                "        this.geometry.setAttribute('size', this.sizeBuffer);",
+                "        this.uvTileBuffer = new THREE.InstancedBufferAttribute(new Float32Array(this.maxParticles), 1);",
+                "        this.uvTileBuffer.setUsage(THREE.DynamicDrawUsage);",
+                "        this.geometry.setAttribute('uvTile', this.uvTileBuffer);",
+                "        if (this.settings.renderMode === RenderMode.StretchedBillBoard) {",
+                "            this.velocityBuffer = new THREE.InstancedBufferAttribute(new Float32Array(this.maxParticles * 4), 4);",
+                "            this.velocityBuffer.setUsage(THREE.DynamicDrawUsage);",
+                "            this.geometry.setAttribute('velocity', this.velocityBuffer);",
+                "        }",
+                "    }",
+                "    setupBuffers() {",
+                "        if (this.geometry)",
+                "            this.geometry.dispose();",
+                "        this.geometry = new THREE.InstancedBufferGeometry();",
+                "        this.geometry.setIndex(this.settings.instancingGeometry.getIndex());",
+                "        if (this.settings.instancingGeometry.hasAttribute('normal')) {",
+                "            this.geometry.setAttribute('normal', this.settings.instancingGeometry.getAttribute('normal'));",
+                "        }",
+                "        this.geometry.setAttribute('position', this.settings.instancingGeometry.getAttribute('position'));",
+                "        this.geometry.setAttribute('uv', this.settings.instancingGeometry.getAttribute('uv'));",
+                "        this.buildExpandableBuffers();",
+                "    }",
+                "    expandBuffers(target) {",
+                "        while (target >= this.maxParticles) {",
+                "            this.maxParticles *= 2;",
+                "        }",
+                "        this.setupBuffers();",
+                "    }",
+                "    rebuildMaterial() {",
+                "        this.layers.mask = this.settings.layers.mask;",
+                "        let uniforms;",
+                "        const defines = {};",
+                "        if (this.settings.material.type === 'MeshStandardMaterial' ||",
+                "            this.settings.material.type === 'MeshPhysicalMaterial') {",
+                "            const mat = this.settings.material;",
+                "            uniforms = THREE.UniformsUtils.merge([",
+                "                THREE.UniformsLib.common,",
+                "                THREE.UniformsLib.envmap,",
+                "                THREE.UniformsLib.aomap,",
+                "                THREE.UniformsLib.lightmap,",
+                "                THREE.UniformsLib.emissivemap,",
+                "                THREE.UniformsLib.bumpmap,",
+                "                THREE.UniformsLib.normalmap,",
+                "                THREE.UniformsLib.displacementmap,",
+                "                THREE.UniformsLib.roughnessmap,",
+                "                THREE.UniformsLib.metalnessmap,",
+                "                THREE.UniformsLib.fog,",
+                "                THREE.UniformsLib.lights,",
+                "                {",
+                "                    emissive: { value: new THREE.Color(0x000000) },",
+                "                    roughness: { value: 1.0 },",
+                "                    metalness: { value: 0.0 },",
+                "                    envMapIntensity: { value: 1 },",
+                "                },",
+                "            ]);",
+                "            uniforms['diffuse'].value = mat.color;",
+                "            uniforms['opacity'].value = mat.opacity;",
+                "            uniforms['emissive'].value = mat.emissive;",
+                "            uniforms['roughness'].value = mat.roughness;",
+                "            uniforms['metalness'].value = mat.metalness;",
+                "            if (mat.envMap) {",
+                "                uniforms['envMap'].value = mat.envMap;",
+                "                uniforms['envMapIntensity'].value = mat.envMapIntensity;",
+                "            }",
+                "            if (mat.normalMap) {",
+                "                uniforms['normalMap'].value = mat.normalMap;",
+                "                uniforms['normalScale'].value = mat.normalScale;",
+                "            }",
+                "            if (mat.roughnessMap) {",
+                "                uniforms['roughnessMap'].value = mat.roughnessMap;",
+                "            }",
+                "            if (mat.metalnessMap) {",
+                "                uniforms['metalnessMap'].value = mat.metalnessMap;",
+                "            }",
+                "            if (mat.map) {",
+                "                uniforms['map'] = new THREE.Uniform(mat.map);",
+                "            }",
+                "        }",
+                "        else {",
+                "            uniforms = {};",
+                "            uniforms['map'] = new THREE.Uniform(this.settings.material.map);",
+                "        }",
+                "        if (this.settings.material.alphaTest) {",
+                "            defines['USE_ALPHATEST'] = '';",
+                "            uniforms['alphaTest'] = new THREE.Uniform(this.settings.material.alphaTest);",
+                "        }",
+                "        defines['USE_UV'] = '';",
+                "        const uTileCount = this.settings.uTileCount;",
+                "        const vTileCount = this.settings.vTileCount;",
+                "        if (uTileCount > 1 || vTileCount > 1) {",
+                "            defines['UV_TILE'] = '';",
+                "            uniforms['tileCount'] = new THREE.Uniform(new THREE.Vector2(uTileCount, vTileCount));",
+                "        }",
+                "        if (this.settings.material.defines &&",
+                "            this.settings.material.defines['USE_COLOR_AS_ALPHA'] !== undefined) {",
+                "            defines['USE_COLOR_AS_ALPHA'] = '';",
+                "        }",
+                "        if (this.settings.material.normalMap) {",
+                "            defines['USE_NORMALMAP'] = '';",
+                "            defines['NORMALMAP_UV'] = getMaterialUVChannelName(this.settings.material.normalMap.channel);",
+                "            uniforms['normalMapTransform'] = new THREE.Uniform(new THREE.Matrix3().copy(this.settings.material.normalMap.matrix));",
+                "        }",
+                "        if (this.settings.material.map) {",
+                "            defines['USE_MAP'] = '';",
+                "            defines['MAP_UV'] = getMaterialUVChannelName(this.settings.material.map.channel);",
+                "            uniforms['mapTransform'] = new THREE.Uniform(new THREE.Matrix3().copy(this.settings.material.map.matrix));",
+                "        }",
+                "        defines['USE_COLOR_ALPHA'] = '';",
+                "        let needLights = false;",
+                "        if (this.settings.renderMode === RenderMode.BillBoard ||",
+                "            this.settings.renderMode === RenderMode.VerticalBillBoard ||",
+                "            this.settings.renderMode === RenderMode.HorizontalBillBoard ||",
+                "            this.settings.renderMode === RenderMode.Mesh) {",
+                "            let vertexShader;",
+                "            let fragmentShader;",
+                "            if (this.settings.renderMode === RenderMode.Mesh) {",
+                "                if (this.settings.material.type === 'MeshStandardMaterial' ||",
+                "                    this.settings.material.type === 'MeshPhysicalMaterial') {",
+                "                    defines['USE_COLOR'] = '';",
+                "                    vertexShader = local_particle_physics_vert;",
+                "                    fragmentShader = particle_physics_frag;",
+                "                    needLights = true;",
+                "                }",
+                "                else {",
+                "                    vertexShader = local_particle_vert;",
+                "                    fragmentShader = particle_frag;",
+                "                }",
+                "            }",
+                "            else {",
+                "                vertexShader = particle_vert;",
+                "                fragmentShader = particle_frag;",
+                "            }",
+                "            if (this.settings.renderMode === RenderMode.VerticalBillBoard) {",
+                "                defines['VERTICAL'] = '';",
+                "            }",
+                "            else if (this.settings.renderMode === RenderMode.HorizontalBillBoard) {",
+                "                defines['HORIZONTAL'] = '';",
+                "            }",
+                "            this.material = new THREE.ShaderMaterial({",
+                "                uniforms: uniforms,",
+                "                defines: defines,",
+                "                vertexShader: vertexShader,",
+                "                fragmentShader: fragmentShader,",
+                "                transparent: this.settings.material.transparent,",
+                "                depthWrite: !this.settings.material.transparent,",
+                "                blending: this.settings.material.blending,",
+                "                side: this.settings.material.side,",
+                "                alphaTest: this.settings.material.alphaTest,",
+                "                lights: needLights,",
+                "            });",
+                "        }",
+                "        else if (this.settings.renderMode === RenderMode.StretchedBillBoard) {",
+                "            uniforms['speedFactor'] = new THREE.Uniform(1.0);",
+                "            this.material = new THREE.ShaderMaterial({",
+                "                uniforms: uniforms,",
+                "                defines: defines,",
+                "                vertexShader: stretched_bb_particle_vert,",
+                "                fragmentShader: particle_frag,",
+                "                transparent: this.settings.material.transparent,",
+                "                depthWrite: !this.settings.material.transparent,",
+                "                blending: this.settings.material.blending,",
+                "                side: this.settings.material.side,",
+                "                alphaTest: this.settings.material.alphaTest,",
+                "            });",
+                "        }",
+                "        else {",
+                "            throw new Error('render mode unavailable');",
+                "        }",
+                "    }",
+                "    update() {",
+                "        let index = 0;",
+                "        let particleCount = 0;",
+                "        this.systems.forEach((system) => {",
+                "            particleCount += system.particleNum;",
+                "        });",
+                "        if (particleCount > this.maxParticles) {",
+                "            this.expandBuffers(particleCount);",
+                "        }",
+                "        this.systems.forEach((system) => {",
+                "            const particles = system.particles;",
+                "            const particleNum = system.particleNum;",
+                "            const rotation = this.quaternion2_;",
+                "            const translation = this.vector2_;",
+                "            const scale = this.vector3_;",
+                "            system.emitter.matrixWorld.decompose(translation, rotation, scale);",
+                "            this.rotationMat_.setFromMatrix4(system.emitter.matrixWorld);",
+                "            for (let j = 0; j < particleNum; j++ , index++) {",
+                "                const particle = particles[j];",
+                "                if (this.settings.renderMode === RenderMode.Mesh) {",
+                "                    let q;",
+                "                    if (system.worldSpace) {",
+                "                        q = particle.rotation;",
+                "                    }",
+                "                    else {",
+                "                        let parentQ;",
+                "                        if (particle.parentMatrix) {",
+                "                            parentQ = this.quaternion3_.setFromRotationMatrix(particle.parentMatrix);",
+                "                        }",
+                "                        else {",
+                "                            parentQ = rotation;",
+                "                        }",
+                "                        q = this.quaternion_;",
+                "                        q.copy(particle.rotation).multiply(parentQ);",
+                "                    }",
+                "                    this.rotationBuffer.setXYZW(index, q.x, q.y, q.z, q.w);",
+                "                }",
+                "                else if (this.settings.renderMode === RenderMode.StretchedBillBoard ||",
+                "                    this.settings.renderMode === RenderMode.VerticalBillBoard ||",
+                "                    this.settings.renderMode === RenderMode.HorizontalBillBoard ||",
+                "                    this.settings.renderMode === RenderMode.BillBoard) {",
+                "                    this.rotationBuffer.setX(index, particle.rotation);",
+                "                }",
+                "                let vec;",
+                "                if (system.worldSpace) {",
+                "                    vec = particle.position;",
+                "                }",
+                "                else {",
+                "                    vec = this.vector_;",
+                "                    if (particle.parentMatrix) {",
+                "                        vec.copy(particle.position).applyMatrix4(particle.parentMatrix);",
+                "                    }",
+                "                    else {",
+                "                        vec.copy(particle.position).applyMatrix4(system.emitter.matrixWorld);",
+                "                    }",
+                "                }",
+                "                this.offsetBuffer.setXYZ(index, vec.x, vec.y, vec.z);",
+                "                this.colorBuffer.setXYZW(index, particle.color.x, particle.color.y, particle.color.z, particle.color.w);",
+                "                if (system.worldSpace) {",
+                "                    this.sizeBuffer.setX(index, particle.size);",
+                "                }",
+                "                else {",
+                "                    if (particle.parentMatrix) {",
+                "                        this.sizeBuffer.setX(index, particle.size);",
+                "                    }",
+                "                    else {",
+                "                        this.sizeBuffer.setX(index, (particle.size * (Math.abs(scale.x) + Math.abs(scale.y) + Math.abs(scale.z))) / 3);",
+                "                    }",
+                "                }",
+                "                this.uvTileBuffer.setX(index, particle.uvTile);",
+                "                if (this.settings.renderMode === RenderMode.StretchedBillBoard && this.velocityBuffer) {",
+                "                    let speedFactor = system.rendererEmitterSettings.speedFactor;",
+                "                    if (speedFactor === 0)",
+                "                        speedFactor = 0.001;",
+                "                    const lengthFactor = system.rendererEmitterSettings.lengthFactor;",
+                "                    let vec;",
+                "                    if (system.worldSpace) {",
+                "                        vec = particle.velocity;",
+                "                    }",
+                "                    else {",
+                "                        vec = this.vector_;",
+                "                        if (particle.parentMatrix) {",
+                "                            this.rotationMat2_.setFromMatrix4(particle.parentMatrix);",
+                "                            vec.copy(particle.velocity).applyMatrix3(this.rotationMat2_);",
+                "                        }",
+                "                        else {",
+                "                            vec.copy(particle.velocity).applyMatrix3(this.rotationMat_);",
+                "                        }",
+                "                    }",
+                "                    this.velocityBuffer.setXYZW(index, vec.x * speedFactor, vec.y * speedFactor, vec.z * speedFactor, lengthFactor);",
+                "                }",
+                "            }",
+                "        });",
+                "        this.geometry.instanceCount = index;",
+                "        if (index > 0) {",
+                "            this.offsetBuffer.clearUpdateRanges();",
+                "            this.offsetBuffer.addUpdateRange(0, index * 3);",
+                "            this.offsetBuffer.needsUpdate = true;",
+                "            this.sizeBuffer.clearUpdateRanges();",
+                "            this.sizeBuffer.addUpdateRange(0, index);",
+                "            this.sizeBuffer.needsUpdate = true;",
+                "            this.colorBuffer.clearUpdateRanges();",
+                "            this.colorBuffer.addUpdateRange(0, index * 4);",
+                "            this.colorBuffer.needsUpdate = true;",
+                "            this.uvTileBuffer.clearUpdateRanges();",
+                "            this.uvTileBuffer.addUpdateRange(0, index);",
+                "            this.uvTileBuffer.needsUpdate = true;",
+                "            if (this.settings.renderMode === RenderMode.StretchedBillBoard && this.velocityBuffer) {",
+                "                this.velocityBuffer.clearUpdateRanges();",
+                "                this.velocityBuffer.addUpdateRange(0, index * 4);",
+                "                this.velocityBuffer.needsUpdate = true;",
+                "            }",
+                "            if (this.settings.renderMode === RenderMode.Mesh) {",
+                "                this.rotationBuffer.clearUpdateRanges();",
+                "                this.rotationBuffer.addUpdateRange(0, index * 4);",
+                "                this.rotationBuffer.needsUpdate = true;",
+                "            }",
+                "            else if (this.settings.renderMode === RenderMode.StretchedBillBoard ||",
+                "                this.settings.renderMode === RenderMode.HorizontalBillBoard ||",
+                "                this.settings.renderMode === RenderMode.VerticalBillBoard ||",
+                "                this.settings.renderMode === RenderMode.BillBoard) {",
+                "                this.rotationBuffer.clearUpdateRanges();",
+                "                this.rotationBuffer.addUpdateRange(0, index);",
+                "                this.rotationBuffer.needsUpdate = true;",
+                "            }",
+                "        }",
+                "    }",
+                "    dispose() {",
+                "        this.geometry.dispose();",
+                "    }",
+                "}",
+                "",
+                "var trail_frag = `",
+                "",
+                "#include <common>",
+                "#include <uv_pars_fragment>",
+                "#include <map_pars_fragment>",
+                "#include <fog_pars_fragment>",
+                "#include <logdepthbuf_pars_fragment>",
+                "#include <clipping_planes_pars_fragment>",
+                "",
+                "uniform sampler2D alphaMap;",
+                "uniform float useAlphaMap;",
+                "uniform float visibility;",
+                "uniform float alphaTest;",
+                "uniform vec2 repeat;",
+                "",
+                "varying vec4 vColor;",
+                "    ",
+                "void main() {",
+                "    #include <clipping_planes_fragment>",
+                "    #include <logdepthbuf_fragment>",
+                "",
+                "    vec4 c = vColor;",
+                "    ",
+                "    #ifdef USE_MAP",
+                "    #ifdef USE_COLOR_AS_ALPHA",
+                "    vec4 tex = texture2D( map, vUv * repeat );",
+                "    c *= vec4(tex.rgb, tex.r);",
+                "    #else",
+                "    c *= texture2D( map, vUv * repeat );",
+                "    #endif",
+                "    #endif",
+                "    if( useAlphaMap == 1. ) c.a *= texture2D( alphaMap, vUv * repeat ).a;",
+                "    if( c.a < alphaTest ) discard;",
+                "    gl_FragColor = c;",
+                "",
+                "    #include <fog_fragment>",
+                "    #include <tonemapping_fragment>",
+                "}`;",
+                "",
+                "var trail_vert = `",
+                "#include <common>",
+                "#include <uv_pars_vertex>",
+                "#include <color_pars_vertex>",
+                "#include <clipping_planes_pars_vertex>",
+                "#include <logdepthbuf_pars_vertex>",
+                "#include <fog_pars_vertex>",
+                "",
+                "attribute vec3 previous;",
+                "attribute vec3 next;",
+                "attribute float side;",
+                "attribute float width;",
+                "",
+                "uniform vec2 resolution;",
+                "uniform float lineWidth;",
+                "uniform float sizeAttenuation;",
+                "    ",
+                "vec2 fix(vec4 i, float aspect) {",
+                "    vec2 res = i.xy / i.w;",
+                "    res.x *= aspect;",
+                "    return res;",
+                "}",
+                "    ",
+                "void main() {",
+                "",
+                "    ${uv_vertex_tile}",
+                "    ",
+                "    float aspect = resolution.x / resolution.y;",
+                "",
+                "    vColor = color;",
+                "",
+                "    mat4 m = projectionMatrix * modelViewMatrix;",
+                "    vec4 finalPosition = m * vec4( position, 1.0 );",
+                "    vec4 prevPos = m * vec4( previous, 1.0 );",
+                "    vec4 nextPos = m * vec4( next, 1.0 );",
+                "",
+                "    vec2 currentP = fix( finalPosition, aspect );",
+                "    vec2 prevP = fix( prevPos, aspect );",
+                "    vec2 nextP = fix( nextPos, aspect );",
+                "",
+                "    float w = lineWidth * width;",
+                "",
+                "    vec2 dir;",
+                "    if( nextP == currentP ) dir = normalize( currentP - prevP );",
+                "    else if( prevP == currentP ) dir = normalize( nextP - currentP );",
+                "    else {",
+                "        vec2 dir1 = normalize( currentP - prevP );",
+                "        vec2 dir2 = normalize( nextP - currentP );",
+                "        dir = normalize( dir1 + dir2 );",
+                "",
+                "        vec2 perp = vec2( -dir1.y, dir1.x );",
+                "        vec2 miter = vec2( -dir.y, dir.x );",
+                "        //w = clamp( w / dot( miter, perp ), 0., 4., * lineWidth * width );",
+                "",
+                "    }",
+                "",
+                "    //vec2 normal = ( cross( vec3( dir, 0. ) vec3( 0., 0., 1. ) ) ).xy;",
+                "    vec4 normal = vec4( -dir.y, dir.x, 0., 1. );",
+                "    normal.xy *= .5 * w;",
+                "    normal *= projectionMatrix;",
+                "    if( sizeAttenuation == 0. ) {",
+                "        normal.xy *= finalPosition.w;",
+                "        normal.xy /= ( vec4( resolution, 0., 1. ) * projectionMatrix ).xy;",
+                "    }",
+                "",
+                "    finalPosition.xy += normal.xy * side;",
+                "",
+                "    gl_Position = finalPosition;",
+                "",
+                "\t#include <logdepthbuf_vertex>",
+                "\t#include <clipping_planes_vertex>",
+                "\t",
+                "    vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );",
+                "    ",
+                "\t#include <fog_vertex>",
+                "}`;",
+                "",
+                "new THREE.Vector3(0, 0, 1);",
+                "class TrailBatch extends VFXBatch {",
+                "    constructor(settings) {",
+                "        super(settings);",
+                "        this.vector_ = new THREE.Vector3();",
+                "        this.vector2_ = new THREE.Vector3();",
+                "        this.vector3_ = new THREE.Vector3();",
+                "        this.quaternion_ = new THREE.Quaternion();",
+                "        this.maxParticles = 10000;",
+                "        this.setupBuffers();",
+                "        this.rebuildMaterial();",
+                "    }",
+                "    setupBuffers() {",
+                "        if (this.geometry)",
+                "            this.geometry.dispose();",
+                "        this.geometry = new THREE.BufferGeometry();",
+                "        this.indexBuffer = new THREE.BufferAttribute(new Uint32Array(this.maxParticles * 6), 1);",
+                "        this.indexBuffer.setUsage(THREE.DynamicDrawUsage);",
+                "        this.geometry.setIndex(this.indexBuffer);",
+                "        this.positionBuffer = new THREE.BufferAttribute(new Float32Array(this.maxParticles * 6), 3);",
+                "        this.positionBuffer.setUsage(THREE.DynamicDrawUsage);",
+                "        this.geometry.setAttribute('position', this.positionBuffer);",
+                "        this.previousBuffer = new THREE.BufferAttribute(new Float32Array(this.maxParticles * 6), 3);",
+                "        this.previousBuffer.setUsage(THREE.DynamicDrawUsage);",
+                "        this.geometry.setAttribute('previous', this.previousBuffer);",
+                "        this.nextBuffer = new THREE.BufferAttribute(new Float32Array(this.maxParticles * 6), 3);",
+                "        this.nextBuffer.setUsage(THREE.DynamicDrawUsage);",
+                "        this.geometry.setAttribute('next', this.nextBuffer);",
+                "        this.widthBuffer = new THREE.BufferAttribute(new Float32Array(this.maxParticles * 2), 1);",
+                "        this.widthBuffer.setUsage(THREE.DynamicDrawUsage);",
+                "        this.geometry.setAttribute('width', this.widthBuffer);",
+                "        this.sideBuffer = new THREE.BufferAttribute(new Float32Array(this.maxParticles * 2), 1);",
+                "        this.sideBuffer.setUsage(THREE.DynamicDrawUsage);",
+                "        this.geometry.setAttribute('side', this.sideBuffer);",
+                "        this.uvBuffer = new THREE.BufferAttribute(new Float32Array(this.maxParticles * 4), 2);",
+                "        this.uvBuffer.setUsage(THREE.DynamicDrawUsage);",
+                "        this.geometry.setAttribute('uv', this.uvBuffer);",
+                "        this.colorBuffer = new THREE.BufferAttribute(new Float32Array(this.maxParticles * 8), 4);",
+                "        this.colorBuffer.setUsage(THREE.DynamicDrawUsage);",
+                "        this.geometry.setAttribute('color', this.colorBuffer);",
+                "    }",
+                "    expandBuffers(target) {",
+                "        while (target >= this.maxParticles) {",
+                "            this.maxParticles *= 2;",
+                "        }",
+                "        this.setupBuffers();",
+                "    }",
+                "    rebuildMaterial() {",
+                "        this.layers.mask = this.settings.layers.mask;",
+                "        const uniforms = {",
+                "            lineWidth: { value: 1 },",
+                "            map: { value: null },",
+                "            useMap: { value: 0 },",
+                "            alphaMap: { value: null },",
+                "            useAlphaMap: { value: 0 },",
+                "            resolution: { value: new THREE.Vector2(1, 1) },",
+                "            sizeAttenuation: { value: 1 },",
+                "            visibility: { value: 1 },",
+                "            alphaTest: { value: 0 },",
+                "            repeat: { value: new THREE.Vector2(1, 1) },",
+                "        };",
+                "        const defines = {};",
+                "        defines['USE_UV'] = '';",
+                "        defines['USE_COLOR_ALPHA'] = '';",
+                "        if (this.settings.material.map) {",
+                "            defines['USE_MAP'] = '';",
+                "            defines['MAP_UV'] = getMaterialUVChannelName(this.settings.material.map.channel);",
+                "            uniforms['map'] = new THREE.Uniform(this.settings.material.map);",
+                "            uniforms['mapTransform'] = new THREE.Uniform(new THREE.Matrix3().copy(this.settings.material.map.matrix));",
+                "        }",
+                "        if (this.settings.material.defines &&",
+                "            this.settings.material.defines['USE_COLOR_AS_ALPHA'] !== undefined) {",
+                "            defines['USE_COLOR_AS_ALPHA'] = '';",
+                "        }",
+                "        if (this.settings.renderMode === RenderMode.Trail) {",
+                "            this.material = new THREE.ShaderMaterial({",
+                "                uniforms: uniforms,",
+                "                defines: defines,",
+                "                vertexShader: trail_vert,",
+                "                fragmentShader: trail_frag,",
+                "                transparent: this.settings.material.transparent,",
+                "                depthWrite: !this.settings.material.transparent,",
+                "                side: this.settings.material.side,",
+                "                blending: this.settings.material.blending || THREE.AdditiveBlending,",
+                "            });",
+                "        }",
+                "        else {",
+                "            throw new Error('render mode unavailable');",
+                "        }",
+                "    }",
+                "    update() {",
+                "        let index = 0;",
+                "        let triangles = 0;",
+                "        let particleCount = 0;",
+                "        this.systems.forEach((system) => {",
+                "            for (let j = 0; j < system.particleNum; j++) {",
+                "                particleCount += system.particles[j].previous.length * 2;",
+                "            }",
+                "        });",
+                "        if (particleCount > this.maxParticles) {",
+                "            this.expandBuffers(particleCount);",
+                "        }",
+                "        this.systems.forEach((system) => {",
+                "            const rotation = this.quaternion_;",
+                "            const translation = this.vector2_;",
+                "            const scale = this.vector3_;",
+                "            system.emitter.matrixWorld.decompose(translation, rotation, scale);",
+                "            const particles = system.particles;",
+                "            const particleNum = system.particleNum;",
+                "            const uTileCount = this.settings.uTileCount;",
+                "            const vTileCount = this.settings.vTileCount;",
+                "            const tileWidth = 1 / uTileCount;",
+                "            const tileHeight = 1 / vTileCount;",
+                "            for (let j = 0; j < particleNum; j++) {",
+                "                const particle = particles[j];",
+                "                const col = particle.uvTile % vTileCount;",
+                "                const row = Math.floor(particle.uvTile / vTileCount + 0.001);",
+                "                const iter = particle.previous.values();",
+                "                let curIter = iter.next();",
+                "                let previous = curIter.value;",
+                "                let current = previous;",
+                "                if (!curIter.done)",
+                "                    curIter = iter.next();",
+                "                let next;",
+                "                if (curIter.value !== undefined) {",
+                "                    next = curIter.value;",
+                "                }",
+                "                else {",
+                "                    next = current;",
+                "                }",
+                "                for (let i = 0; i < particle.previous.length; i++ , index += 2) {",
+                "                    this.positionBuffer.setXYZ(index, current.position.x, current.position.y, current.position.z);",
+                "                    this.positionBuffer.setXYZ(index + 1, current.position.x, current.position.y, current.position.z);",
+                "                    if (system.worldSpace) {",
+                "                        this.positionBuffer.setXYZ(index, current.position.x, current.position.y, current.position.z);",
+                "                        this.positionBuffer.setXYZ(index + 1, current.position.x, current.position.y, current.position.z);",
+                "                    }",
+                "                    else {",
+                "                        if (particle.parentMatrix) {",
+                "                            this.vector_.copy(current.position).applyMatrix4(particle.parentMatrix);",
+                "                        }",
+                "                        else {",
+                "                            this.vector_.copy(current.position).applyMatrix4(system.emitter.matrixWorld);",
+                "                        }",
+                "                        this.positionBuffer.setXYZ(index, this.vector_.x, this.vector_.y, this.vector_.z);",
+                "                        this.positionBuffer.setXYZ(index + 1, this.vector_.x, this.vector_.y, this.vector_.z);",
+                "                    }",
+                "                    if (system.worldSpace) {",
+                "                        this.previousBuffer.setXYZ(index, previous.position.x, previous.position.y, previous.position.z);",
+                "                        this.previousBuffer.setXYZ(index + 1, previous.position.x, previous.position.y, previous.position.z);",
+                "                    }",
+                "                    else {",
+                "                        if (particle.parentMatrix) {",
+                "                            this.vector_.copy(previous.position).applyMatrix4(particle.parentMatrix);",
+                "                        }",
+                "                        else {",
+                "                            this.vector_.copy(previous.position).applyMatrix4(system.emitter.matrixWorld);",
+                "                        }",
+                "                        this.previousBuffer.setXYZ(index, this.vector_.x, this.vector_.y, this.vector_.z);",
+                "                        this.previousBuffer.setXYZ(index + 1, this.vector_.x, this.vector_.y, this.vector_.z);",
+                "                    }",
+                "                    if (system.worldSpace) {",
+                "                        this.nextBuffer.setXYZ(index, next.position.x, next.position.y, next.position.z);",
+                "                        this.nextBuffer.setXYZ(index + 1, next.position.x, next.position.y, next.position.z);",
+                "                    }",
+                "                    else {",
+                "                        if (particle.parentMatrix) {",
+                "                            this.vector_.copy(next.position).applyMatrix4(particle.parentMatrix);",
+                "                        }",
+                "                        else {",
+                "                            this.vector_.copy(next.position).applyMatrix4(system.emitter.matrixWorld);",
+                "                        }",
+                "                        this.nextBuffer.setXYZ(index, this.vector_.x, this.vector_.y, this.vector_.z);",
+                "                        this.nextBuffer.setXYZ(index + 1, this.vector_.x, this.vector_.y, this.vector_.z);",
+                "                    }",
+                "                    this.sideBuffer.setX(index, -1);",
+                "                    this.sideBuffer.setX(index + 1, 1);",
+                "                    if (system.worldSpace) {",
+                "                        this.widthBuffer.setX(index, current.size);",
+                "                        this.widthBuffer.setX(index + 1, current.size);",
+                "                    }",
+                "                    else {",
+                "                        if (particle.parentMatrix) {",
+                "                            this.widthBuffer.setX(index, current.size);",
+                "                            this.widthBuffer.setX(index + 1, current.size);",
+                "                        }",
+                "                        else {",
+                "                            const objectScale = (Math.abs(scale.x) + Math.abs(scale.y) + Math.abs(scale.z)) / 3;",
+                "                            this.widthBuffer.setX(index, current.size * objectScale);",
+                "                            this.widthBuffer.setX(index + 1, current.size * objectScale);",
+                "                        }",
+                "                    }",
+                "                    this.uvBuffer.setXY(index, (i / particle.previous.length + col) * tileWidth, (vTileCount - row - 1) * tileHeight);",
+                "                    this.uvBuffer.setXY(index + 1, (i / particle.previous.length + col) * tileWidth, (vTileCount - row) * tileHeight);",
+                "                    this.colorBuffer.setXYZW(index, current.color.x, current.color.y, current.color.z, current.color.w);",
+                "                    this.colorBuffer.setXYZW(index + 1, current.color.x, current.color.y, current.color.z, current.color.w);",
+                "                    if (i + 1 < particle.previous.length) {",
+                "                        this.indexBuffer.setX(triangles * 3, index);",
+                "                        this.indexBuffer.setX(triangles * 3 + 1, index + 1);",
+                "                        this.indexBuffer.setX(triangles * 3 + 2, index + 2);",
+                "                        triangles++;",
+                "                        this.indexBuffer.setX(triangles * 3, index + 2);",
+                "                        this.indexBuffer.setX(triangles * 3 + 1, index + 1);",
+                "                        this.indexBuffer.setX(triangles * 3 + 2, index + 3);",
+                "                        triangles++;",
+                "                    }",
+                "                    previous = current;",
+                "                    current = next;",
+                "                    if (!curIter.done) {",
+                "                        curIter = iter.next();",
+                "                        if (curIter.value !== undefined) {",
+                "                            next = curIter.value;",
+                "                        }",
+                "                    }",
+                "                }",
+                "            }",
+                "        });",
+                "        this.positionBuffer.clearUpdateRanges();",
+                "        this.positionBuffer.addUpdateRange(0, index * 3);",
+                "        this.positionBuffer.needsUpdate = true;",
+                "        this.previousBuffer.clearUpdateRanges();",
+                "        this.previousBuffer.addUpdateRange(0, index * 3);",
+                "        this.previousBuffer.needsUpdate = true;",
+                "        this.nextBuffer.clearUpdateRanges();",
+                "        this.nextBuffer.addUpdateRange(0, index * 3);",
+                "        this.nextBuffer.needsUpdate = true;",
+                "        this.sideBuffer.clearUpdateRanges();",
+                "        this.sideBuffer.addUpdateRange(0, index);",
+                "        this.sideBuffer.needsUpdate = true;",
+                "        this.widthBuffer.clearUpdateRanges();",
+                "        this.widthBuffer.addUpdateRange(0, index);",
+                "        this.widthBuffer.needsUpdate = true;",
+                "        this.uvBuffer.clearUpdateRanges();",
+                "        this.uvBuffer.addUpdateRange(0, index * 2);",
+                "        this.uvBuffer.needsUpdate = true;",
+                "        this.colorBuffer.clearUpdateRanges();",
+                "        this.colorBuffer.addUpdateRange(0, index * 4);",
+                "        this.colorBuffer.needsUpdate = true;",
+                "        this.indexBuffer.clearUpdateRanges();",
+                "        this.indexBuffer.addUpdateRange(0, triangles * 3);",
+                "        this.indexBuffer.needsUpdate = true;",
+                "        this.geometry.setDrawRange(0, triangles * 3);",
+                "    }",
+                "    dispose() {",
+                "        this.geometry.dispose();",
+                "    }",
+                "}",
+                "",
+                "class BatchedRenderer extends THREE.Object3D {",
+                "    constructor() {",
+                "        super();",
+                "        this.batches = [];",
+                "        this.systemToBatchIndex = new Map();",
+                "        this.type = 'BatchedRenderer';",
+                "    }",
+                "    static equals(a, b) {",
+                "        return (a.material.side === b.material.side &&",
+                "            a.material.blending === b.material.blending &&",
+                "            a.material.transparent === b.material.transparent &&",
+                "            a.material.type === b.material.type &&",
+                "            a.material.alphaTest === b.material.alphaTest &&",
+                "            a.material.map === b.material.map &&",
+                "            a.renderMode === b.renderMode &&",
+                "            a.uTileCount === b.uTileCount &&",
+                "            a.vTileCount === b.vTileCount &&",
+                "            a.instancingGeometry === b.instancingGeometry &&",
+                "            a.renderOrder === b.renderOrder &&",
+                "            a.layers.mask === b.layers.mask);",
+                "    }",
+                "    addSystem(system) {",
+                "        system._renderer = this;",
+                "        const settings = system.getRendererSettings();",
+                "        for (let i = 0; i < this.batches.length; i++) {",
+                "            if (BatchedRenderer.equals(this.batches[i].settings, settings)) {",
+                "                this.batches[i].addSystem(system);",
+                "                this.systemToBatchIndex.set(system, i);",
+                "                return;",
+                "            }",
+                "        }",
+                "        let batch;",
+                "        switch (settings.renderMode) {",
+                "            case RenderMode.Trail:",
+                "                batch = new TrailBatch(settings);",
+                "                break;",
+                "            case RenderMode.Mesh:",
+                "            case RenderMode.BillBoard:",
+                "            case RenderMode.VerticalBillBoard:",
+                "            case RenderMode.HorizontalBillBoard:",
+                "            case RenderMode.StretchedBillBoard:",
+                "                batch = new SpriteBatch(settings);",
+                "                break;",
+                "        }",
+                "        batch.addSystem(system);",
+                "        this.batches.push(batch);",
+                "        this.systemToBatchIndex.set(system, this.batches.length - 1);",
+                "        this.add(batch);",
+                "    }",
+                "    deleteSystem(system) {",
+                "        const batchIndex = this.systemToBatchIndex.get(system);",
+                "        if (batchIndex != undefined) {",
+                "            this.batches[batchIndex].removeSystem(system);",
+                "            this.systemToBatchIndex.delete(system);",
+                "        }",
+                "    }",
+                "    updateSystem(system) {",
+                "        this.deleteSystem(system);",
+                "        this.addSystem(system);",
+                "    }",
+                "    update(delta) {",
+                "        this.systemToBatchIndex.forEach((value, ps) => {",
+                "            ps.update(delta);",
+                "        });",
+                "        for (let i = 0; i < this.batches.length; i++) {",
+                "            this.batches[i].update();",
+                "        }",
+                "    }",
+                "}",
+                "",
+                "const BatchedParticleRenderer = BatchedRenderer;",
+                "",
+                "class QuarksLoader extends THREE.ObjectLoader {",
+                "    constructor(manager) {",
+                "        super(manager);",
+                "    }",
+                "    linkReference(object) {",
+                "        const objectsMap = {};",
+                "        object.traverse(function (child) {",
+                "            objectsMap[child.uuid] = child;",
+                "        });",
+                "        object.traverse(function (child) {",
+                "            if (child.type === 'ParticleEmitter') {",
+                "                const system = child.system;",
+                "                system.emitterShape;",
+                "                for (let i = 0; i < system.behaviors.length; i++) {",
+                "                    if (system.behaviors[i] instanceof EmitSubParticleSystem) {",
+                "                        system.behaviors[i].subParticleSystem = objectsMap[system.behaviors[i].subParticleSystem];",
+                "                    }",
+                "                }",
+                "            }",
+                "        });",
+                "    }",
+                "    parse(json, onLoad) {",
+                "        const object = super.parse(json, onLoad);",
+                "        this.linkReference(object);",
+                "        return object;",
+                "    }",
+                "    parseObject(data, geometries, materials, textures, animations) {",
+                "        let object;",
+                "        function getGeometry(name) {",
+                "            if (geometries[name] === undefined) {",
+                "                console.warn('THREE.ObjectLoader: Undefined geometry', name);",
+                "            }",
+                "            return geometries[name];",
+                "        }",
+                "        function getMaterial(name) {",
+                "            if (name === undefined)",
+                "                return undefined;",
+                "            if (Array.isArray(name)) {",
+                "                const array = [];",
+                "                for (let i = 0, l = name.length; i < l; i++) {",
+                "                    const uuid = name[i];",
+                "                    if (materials[uuid] === undefined) {",
+                "                        console.warn('THREE.ObjectLoader: Undefined material', uuid);",
+                "                    }",
+                "                    array.push(materials[uuid]);",
+                "                }",
+                "                return array;",
+                "            }",
+                "            if (materials[name] === undefined) {",
+                "                console.warn('THREE.ObjectLoader: Undefined material', name);",
+                "            }",
+                "            return materials[name];",
+                "        }",
+                "        function getTexture(uuid) {",
+                "            if (textures[uuid] === undefined) {",
+                "                console.warn('THREE.ObjectLoader: Undefined texture', uuid);",
+                "            }",
+                "            return textures[uuid];",
+                "        }",
+                "        let geometry, material;",
+                "        const meta = {",
+                "            textures: textures,",
+                "            geometries: geometries,",
+                "            materials: materials,",
+                "        };",
+                "        const dependencies = {};",
+                "        switch (data.type) {",
+                "            case 'ParticleEmitter':",
+                "                object = ParticleSystem.fromJSON(data.ps, meta, dependencies).emitter;",
+                "                break;",
+                "            case 'Scene':",
+                "                object = new THREE.Scene();",
+                "                if (data.background !== undefined) {",
+                "                    if (Number.isInteger(data.background)) {",
+                "                        object.background = new THREE.Color(data.background);",
+                "                    }",
+                "                    else {",
+                "                        object.background = getTexture(data.background);",
+                "                    }",
+                "                }",
+                "                if (data.environment !== undefined) {",
+                "                    object.environment = getTexture(data.environment);",
+                "                }",
+                "                if (data.fog !== undefined) {",
+                "                    if (data.fog.type === 'Fog') {",
+                "                        object.fog = new THREE.Fog(data.fog.color, data.fog.near, data.fog.far);",
+                "                    }",
+                "                    else if (data.fog.type === 'FogExp2') {",
+                "                        object.fog = new THREE.FogExp2(data.fog.color, data.fog.density);",
+                "                    }",
+                "                }",
+                "                if (data.backgroundBlurriness !== undefined)",
+                "                    object.backgroundBlurriness = data.backgroundBlurriness;",
+                "                break;",
+                "            case 'PerspectiveCamera':",
+                "                object = new THREE.PerspectiveCamera(data.fov, data.aspect, data.near, data.far);",
+                "                if (data.focus !== undefined)",
+                "                    object.focus = data.focus;",
+                "                if (data.zoom !== undefined)",
+                "                    object.zoom = data.zoom;",
+                "                if (data.filmGauge !== undefined)",
+                "                    object.filmGauge = data.filmGauge;",
+                "                if (data.filmOffset !== undefined)",
+                "                    object.filmOffset = data.filmOffset;",
+                "                if (data.view !== undefined)",
+                "                    object.view = Object.assign({}, data.view);",
+                "                break;",
+                "            case 'OrthographicCamera':",
+                "                object = new THREE.OrthographicCamera(data.left, data.right, data.top, data.bottom, data.near, data.far);",
+                "                if (data.zoom !== undefined)",
+                "                    object.zoom = data.zoom;",
+                "                if (data.view !== undefined)",
+                "                    object.view = Object.assign({}, data.view);",
+                "                break;",
+                "            case 'AmbientLight':",
+                "                object = new THREE.AmbientLight(data.color, data.intensity);",
+                "                break;",
+                "            case 'DirectionalLight':",
+                "                object = new THREE.DirectionalLight(data.color, data.intensity);",
+                "                break;",
+                "            case 'PointLight':",
+                "                object = new THREE.PointLight(data.color, data.intensity, data.distance, data.decay);",
+                "                break;",
+                "            case 'RectAreaLight':",
+                "                object = new THREE.RectAreaLight(data.color, data.intensity, data.width, data.height);",
+                "                break;",
+                "            case 'SpotLight':",
+                "                object = new THREE.SpotLight(data.color, data.intensity, data.distance, data.angle, data.penumbra, data.decay);",
+                "                break;",
+                "            case 'HemisphereLight':",
+                "                object = new THREE.HemisphereLight(data.color, data.groundColor, data.intensity);",
+                "                break;",
+                "            case 'LightProbe':",
+                "                object = new THREE.LightProbe().fromJSON(data);",
+                "                break;",
+                "            case 'SkinnedMesh':",
+                "                geometry = getGeometry(data.geometry);",
+                "                material = getMaterial(data.material);",
+                "                object = new THREE.SkinnedMesh(geometry, material);",
+                "                if (data.bindMode !== undefined)",
+                "                    object.bindMode = data.bindMode;",
+                "                if (data.bindMatrix !== undefined)",
+                "                    object.bindMatrix.fromArray(data.bindMatrix);",
+                "                if (data.skeleton !== undefined)",
+                "                    object.skeleton = data.skeleton;",
+                "                break;",
+                "            case 'Mesh':",
+                "                geometry = getGeometry(data.geometry);",
+                "                material = getMaterial(data.material);",
+                "                object = new THREE.Mesh(geometry, material);",
+                "                break;",
+                "            case 'InstancedMesh': {",
+                "                geometry = getGeometry(data.geometry);",
+                "                material = getMaterial(data.material);",
+                "                const count = data.count;",
+                "                const instanceMatrix = data.instanceMatrix;",
+                "                const instanceColor = data.instanceColor;",
+                "                object = new THREE.InstancedMesh(geometry, material, count);",
+                "                object.instanceMatrix = new THREE.InstancedBufferAttribute(new Float32Array(instanceMatrix.array), 16);",
+                "                if (instanceColor !== undefined)",
+                "                    object.instanceColor = new THREE.InstancedBufferAttribute(new Float32Array(instanceColor.array), instanceColor.itemSize);",
+                "                break;",
+                "            }",
+                "            case 'LOD':",
+                "                object = new THREE.LOD();",
+                "                break;",
+                "            case 'Line':",
+                "                object = new THREE.Line(getGeometry(data.geometry), getMaterial(data.material));",
+                "                break;",
+                "            case 'LineLoop':",
+                "                object = new THREE.LineLoop(getGeometry(data.geometry), getMaterial(data.material));",
+                "                break;",
+                "            case 'LineSegments':",
+                "                object = new THREE.LineSegments(getGeometry(data.geometry), getMaterial(data.material));",
+                "                break;",
+                "            case 'PointCloud':",
+                "            case 'Points':",
+                "                object = new THREE.Points(getGeometry(data.geometry), getMaterial(data.material));",
+                "                break;",
+                "            case 'Sprite':",
+                "                object = new THREE.Sprite(getMaterial(data.material));",
+                "                break;",
+                "            case 'Group':",
+                "                object = new THREE.Group();",
+                "                break;",
+                "            case 'Bone':",
+                "                object = new THREE.Bone();",
+                "                break;",
+                "            default:",
+                "                object = new THREE.Object3D();",
+                "        }",
+                "        object.uuid = data.uuid;",
+                "        if (data.name !== undefined)",
+                "            object.name = data.name;",
+                "        if (data.matrix !== undefined) {",
+                "            object.matrix.fromArray(data.matrix);",
+                "            if (data.matrixAutoUpdate !== undefined)",
+                "                object.matrixAutoUpdate = data.matrixAutoUpdate;",
+                "            if (object.matrixAutoUpdate)",
+                "                object.matrix.decompose(object.position, object.quaternion, object.scale);",
+                "        }",
+                "        else {",
+                "            if (data.position !== undefined)",
+                "                object.position.fromArray(data.position);",
+                "            if (data.rotation !== undefined)",
+                "                object.rotation.fromArray(data.rotation);",
+                "            if (data.quaternion !== undefined)",
+                "                object.quaternion.fromArray(data.quaternion);",
+                "            if (data.scale !== undefined)",
+                "                object.scale.fromArray(data.scale);",
+                "        }",
+                "        if (data.castShadow !== undefined)",
+                "            object.castShadow = data.castShadow;",
+                "        if (data.receiveShadow !== undefined)",
+                "            object.receiveShadow = data.receiveShadow;",
+                "        if (data.shadow) {",
+                "            if (data.shadow.bias !== undefined)",
+                "                object.shadow.bias = data.shadow.bias;",
+                "            if (data.shadow.normalBias !== undefined)",
+                "                object.normalBias = data.shadow.normalBias;",
+                "            if (data.shadow.radius !== undefined)",
+                "                object.radius = data.shadow.radius;",
+                "            if (data.shadow.mapSize !== undefined)",
+                "                object.mapSize.fromArray(data.shadow.mapSize);",
+                "            if (data.shadow.camera !== undefined) {",
+                "                object.camera = this.parseObject(data.shadow.camera);",
+                "            }",
+                "        }",
+                "        if (data.visible !== undefined)",
+                "            object.visible = data.visible;",
+                "        if (data.frustumCulled !== undefined)",
+                "            object.frustumCulled = data.frustumCulled;",
+                "        if (data.renderOrder !== undefined)",
+                "            object.renderOrder = data.renderOrder;",
+                "        if (data.userData !== undefined)",
+                "            object.userData = data.userData;",
+                "        if (data.layers !== undefined)",
+                "            object.layers.mask = data.layers;",
+                "        if (data.children !== undefined) {",
+                "            const children = data.children;",
+                "            for (let i = 0; i < children.length; i++) {",
+                "                object.add(this.parseObject(children[i], geometries, materials, textures, animations));",
+                "            }",
+                "        }",
+                "        if (data.animations !== undefined) {",
+                "            const objectAnimations = data.animations;",
+                "            for (let i = 0; i < objectAnimations.length; i++) {",
+                "                const uuid = objectAnimations[i];",
+                "                object.animations.push(animations[uuid]);",
+                "            }",
+                "        }",
+                "        if (data.type === 'LOD') {",
+                "            if (data.autoUpdate !== undefined)",
+                "                object.autoUpdate = data.autoUpdate;",
+                "            const levels = data.levels;",
+                "            for (let l = 0; l < levels.length; l++) {",
+                "                const level = levels[l];",
+                "                const child = object.getObjectByProperty('uuid', level.object);",
+                "                if (child !== undefined) {",
+                "                    object.addLevel(child, level.distance);",
+                "                }",
+                "            }",
+                "        }",
+                "        return object;",
+                "    }",
+                "}",
+                "",
+                "const Plugins = [];",
+                "function loadPlugin(plugin) {",
+                "    const existing = Plugins.find((item) => item.id === plugin.id);",
+                "    if (!existing) {",
+                "        plugin.initialize();",
+                "        for (const emitterShape of plugin.emitterShapes) {",
+                "            if (!EmitterShapes[emitterShape.type]) {",
+                "                EmitterShapes[emitterShape.type] = emitterShape;",
+                "            }",
+                "        }",
+                "        for (const behavior of plugin.behaviors) {",
+                "            if (!BehaviorTypes[behavior.type]) {",
+                "                BehaviorTypes[behavior.type] = behavior;",
+                "            }",
+                "        }",
+                "    }",
+                "}",
+                "function unloadPlugin(pluginId) {",
+                "    const plugin = Plugins.find((item) => item.id === pluginId);",
+                "    if (plugin) {",
+                "        for (const emitterShape of plugin.emitterShapes) {",
+                "            if (EmitterShapes[emitterShape.type]) {",
+                "                delete EmitterShapes[emitterShape.type];",
+                "            }",
+                "        }",
+                "        for (const behavior of plugin.behaviors) {",
+                "            if (BehaviorTypes[behavior.type]) {",
+                "                delete BehaviorTypes[behavior.type];",
+                "            }",
+                "        }",
+                "    }",
+                "}",
+                "",
+                "let NodeValueType = void 0;",
+                "(function (NodeValueType) {",
+                "    NodeValueType[NodeValueType[\"Number\"] = 0] = \"Number\";",
+                "    NodeValueType[NodeValueType[\"Vec2\"] = 1] = \"Vec2\";",
+                "    NodeValueType[NodeValueType[\"Vec3\"] = 2] = \"Vec3\";",
+                "    NodeValueType[NodeValueType[\"Vec4\"] = 3] = \"Vec4\";",
+                "    NodeValueType[NodeValueType[\"Boolean\"] = 4] = \"Boolean\";",
+                "    NodeValueType[NodeValueType[\"AnyType\"] = 5] = \"AnyType\";",
+                "    NodeValueType[NodeValueType[\"NullableAnyType\"] = 6] = \"NullableAnyType\";",
+                "    NodeValueType[NodeValueType[\"EventStream\"] = 7] = \"EventStream\";",
+                "})(NodeValueType || (NodeValueType = {}));",
+                "const genDefaultForNodeValueType = (type) => {",
+                "    switch (type) {",
+                "        case NodeValueType.Boolean:",
+                "            return false;",
+                "        case NodeValueType.Number:",
+                "            return 0;",
+                "        case NodeValueType.Vec2:",
+                "            return new THREE.Vector2();",
+                "        case NodeValueType.Vec3:",
+                "            return new THREE.Vector3();",
+                "        case NodeValueType.Vec4:",
+                "            return new THREE.Vector4();",
+                "        case NodeValueType.AnyType:",
+                "            return 0;",
+                "        case NodeValueType.NullableAnyType:",
+                "            return undefined;",
+                "    }",
+                "};",
+                "",
+                "class Node {",
+                "    constructor(type, signatureIndex = -1, data = {}) {",
+                "        this.inputs = [];",
+                "        this.outputs = [];",
+                "        this.signatureIndex = -1;",
+                "        this.position = new THREE.Vector2();",
+                "        this.outputValues = [];",
+                "        this.id = '' + Math.round(Math.random() * 100000);",
+                "        this.type = type;",
+                "        this.signatureIndex = signatureIndex;",
+                "        this.data = data;",
+                "        const realIndex = signatureIndex === -1 ? 0 : signatureIndex;",
+                "        for (let i = 0; i < type.nodeTypeSignatures[realIndex].inputTypes.length; i++) {",
+                "            this.inputs.push(undefined);",
+                "        }",
+                "        for (let i = 0; i < type.nodeTypeSignatures[realIndex].outputTypes.length; i++) {",
+                "            this.outputs.push([]);",
+                "            this.outputValues.push(genDefaultForNodeValueType(type.nodeTypeSignatures[realIndex].outputTypes[i]));",
+                "        }",
+                "    }",
+                "    get inputTypes() {",
+                "        const signatureIndex = this.signatureIndex === -1 ? 0 : this.signatureIndex;",
+                "        return this.type.nodeTypeSignatures[signatureIndex].inputTypes;",
+                "    }",
+                "    get outputTypes() {",
+                "        const signatureIndex = this.signatureIndex === -1 ? 0 : this.signatureIndex;",
+                "        return this.type.nodeTypeSignatures[signatureIndex].outputTypes;",
+                "    }",
+                "    func(context, inputs, outputs) {",
+                "        const signatureIndex = this.signatureIndex === -1 ? 0 : this.signatureIndex;",
+                "        this.type.nodeTypeSignatures[signatureIndex].func(context, this.data, inputs, outputs);",
+                "    }",
+                "}",
+                "class Wire {",
+                "    constructor(input, inputIndex, output, outputIndex) {",
+                "        this.input = input;",
+                "        this.inputIndex = inputIndex;",
+                "        this.input.outputs[inputIndex].push(this);",
+                "        this.output = output;",
+                "        this.outputIndex = outputIndex;",
+                "        this.output.inputs[outputIndex] = this;",
+                "    }",
+                "}",
+                "",
+                "class BaseCompiler {",
+                "    constructor() {",
+                "        this.visited = new Set();",
+                "        BaseCompiler.Instance = this;",
+                "    }",
+                "    buildExecutionOrder(graph, context) {",
+                "        graph.nodesInOrder.length = 0;",
+                "        this.visited.clear();",
+                "        for (let i = 0; i < graph.outputNodes.length; i++) {",
+                "            const node = graph.outputNodes[i];",
+                "            if (node.inputs[0] !== undefined) {",
+                "                this._traverse(node, graph, context);",
+                "            }",
+                "        }",
+                "        graph.compiled = true;",
+                "    }",
+                "    _traverse(node, graph, context) {",
+                "        this.visited.add(node.id);",
+                "        for (let i = 0; i < node.inputs.length; i++) {",
+                "            if (node.inputs[i] instanceof Wire) {",
+                "                const inputNode = node.inputs[i].input;",
+                "                if (!this.visited.has(inputNode.id)) {",
+                "                    this._traverse(inputNode, graph, context);",
+                "                }",
+                "            }",
+                "            else if (node.inputs[i] !== undefined);",
+                "            else;",
+                "        }",
+                "        graph.nodesInOrder.push(node);",
+                "    }",
+                "}",
+                "",
+                "class Interpreter extends BaseCompiler {",
+                "    constructor() {",
+                "        super();",
+                "    }",
+                "    executeCompiledGraph(graph, context) {",
+                "        const nodes = graph.nodesInOrder;",
+                "        for (let i = 0; i < nodes.length; i++) {",
+                "            const inputValues = [];",
+                "            const node = nodes[i];",
+                "            for (let j = 0; j < node.inputs.length; j++) {",
+                "                if (node.inputs[j] instanceof Wire) {",
+                "                    inputValues.push(node.inputs[j].input.outputValues[node.inputs[j].inputIndex]);",
+                "                }",
+                "                else if (node.inputs[j] !== undefined) {",
+                "                    inputValues.push(node.inputs[j].getValue(context));",
+                "                }",
+                "                else {",
+                "                    inputValues.push(undefined);",
+                "                }",
+                "            }",
+                "            node.func(context, inputValues, node.outputValues);",
+                "        }",
+                "    }",
+                "    run(graph, context) {",
+                "        if (!graph.compiled) {",
+                "            this.buildExecutionOrder(graph, context);",
+                "        }",
+                "        this.executeCompiledGraph(graph, context);",
+                "    }",
+                "}",
+                "",
+                "class NodeType {",
+                "    constructor(name) {",
+                "        this.nodeTypeSignatures = [];",
+                "        this.name = name;",
+                "    }",
+                "    addSignature(inputTypes, outputTypes, func) {",
+                "        this.nodeTypeSignatures.push({",
+                "            inputTypes: inputTypes,",
+                "            outputTypes: outputTypes,",
+                "            func: func,",
+                "        });",
+                "    }",
+                "}",
+                "class GraphNodeType extends NodeType {",
+                "    constructor(nodeGraph) {",
+                "        const inputTypes = [];",
+                "        for (let i = 0; i < nodeGraph.inputNodes.length; i++) {",
+                "            if (nodeGraph.inputNodes[i].type.name === 'input') {",
+                "                inputTypes.push(nodeGraph.inputNodes[i].data.type);",
+                "            }",
+                "        }",
+                "        const outputTypes = [];",
+                "        for (let i = 0; i < nodeGraph.outputNodes.length; i++) {",
+                "            if (nodeGraph.outputNodes[i].type.name === 'output') {",
+                "                outputTypes.push(nodeGraph.outputNodes[i].data.type);",
+                "            }",
+                "        }",
+                "        super(nodeGraph.name);",
+                "        this.addSignature(inputTypes, outputTypes, (context, data, inputs, outputs) => {",
+                "            context.inputs = inputs;",
+                "            context.outputs = outputs;",
+                "            Interpreter.Instance.run(nodeGraph, context);",
+                "        });",
+                "        this.nodeGraph = nodeGraph;",
+                "    }",
+                "}",
+                "",
+                "const NodeTypes = {};",
+                "const addNode = new NodeType('add');",
+                "addNode.addSignature([NodeValueType.Number, NodeValueType.Number], [NodeValueType.Number], (context, data, inputs, outputs) => {",
+                "    outputs[0] = inputs[0] + inputs[1];",
+                "});",
+                "addNode.addSignature([NodeValueType.Vec2, NodeValueType.Vec2], [NodeValueType.Vec2], (context, data, inputs, outputs) => {",
+                "    outputs[0].addVectors(inputs[0], inputs[1]);",
+                "});",
+                "addNode.addSignature([NodeValueType.Vec3, NodeValueType.Vec3], [NodeValueType.Vec3], (context, data, inputs, outputs) => {",
+                "    outputs[0].addVectors(inputs[0], inputs[1]);",
+                "});",
+                "addNode.addSignature([NodeValueType.Vec4, NodeValueType.Vec4], [NodeValueType.Vec4], (context, data, inputs, outputs) => {",
+                "    outputs[0].addVectors(inputs[0], inputs[1]);",
+                "});",
+                "NodeTypes['add'] = addNode;",
+                "const subNode = new NodeType('sub');",
+                "subNode.addSignature([NodeValueType.Number, NodeValueType.Number], [NodeValueType.Number], (context, data, inputs, outputs) => {",
+                "    outputs[0] = inputs[0] - inputs[1];",
+                "});",
+                "subNode.addSignature([NodeValueType.Vec2, NodeValueType.Vec2], [NodeValueType.Vec2], (context, data, inputs, outputs) => {",
+                "    outputs[0].subVectors(inputs[0], inputs[1]);",
+                "});",
+                "subNode.addSignature([NodeValueType.Vec3, NodeValueType.Vec3], [NodeValueType.Vec3], (context, data, inputs, outputs) => {",
+                "    outputs[0].subVectors(inputs[0], inputs[1]);",
+                "});",
+                "subNode.addSignature([NodeValueType.Vec4, NodeValueType.Vec4], [NodeValueType.Vec4], (context, data, inputs, outputs) => {",
+                "    outputs[0].subVectors(inputs[0], inputs[1]);",
+                "});",
+                "NodeTypes['sub'] = subNode;",
+                "const mulNode = new NodeType('mul');",
+                "mulNode.addSignature([NodeValueType.Number, NodeValueType.Number], [NodeValueType.Number], (context, data, inputs, outputs) => {",
+                "    outputs[0] = inputs[0] * inputs[1];",
+                "});",
+                "mulNode.addSignature([NodeValueType.Vec2, NodeValueType.Number], [NodeValueType.Vec2], (context, data, inputs, outputs) => {",
+                "    outputs[0].copy(inputs[0]).multiplyScalar(inputs[1]);",
+                "});",
+                "mulNode.addSignature([NodeValueType.Vec3, NodeValueType.Number], [NodeValueType.Vec3], (context, data, inputs, outputs) => {",
+                "    outputs[0].copy(inputs[0]).multiplyScalar(inputs[1]);",
+                "});",
+                "mulNode.addSignature([NodeValueType.Vec4, NodeValueType.Number], [NodeValueType.Vec4], (context, data, inputs, outputs) => {",
+                "    outputs[0].copy(inputs[0]).multiplyScalar(inputs[1]);",
+                "});",
+                "NodeTypes['mul'] = mulNode;",
+                "const divNode = new NodeType('div');",
+                "divNode.addSignature([NodeValueType.Number, NodeValueType.Number], [NodeValueType.Number], (context, data, inputs, outputs) => {",
+                "    outputs[0] = inputs[0] / inputs[1];",
+                "});",
+                "divNode.addSignature([NodeValueType.Vec2, NodeValueType.Number], [NodeValueType.Vec2], (context, data, inputs, outputs) => {",
+                "    outputs[0].copy(inputs[0]).divideScalar(inputs[1]);",
+                "});",
+                "divNode.addSignature([NodeValueType.Vec3, NodeValueType.Number], [NodeValueType.Vec3], (context, data, inputs, outputs) => {",
+                "    outputs[0].copy(inputs[0]).divideScalar(inputs[1]);",
+                "});",
+                "divNode.addSignature([NodeValueType.Vec4, NodeValueType.Number], [NodeValueType.Vec4], (context, data, inputs, outputs) => {",
+                "    outputs[0].copy(inputs[0]).divideScalar(inputs[1]);",
+                "});",
+                "NodeTypes['div'] = divNode;",
+                "const sinNode = new NodeType('sin');",
+                "sinNode.addSignature([NodeValueType.Number], [NodeValueType.Number], (context, data, inputs, outputs) => {",
+                "    outputs[0] = Math.sin(inputs[0]);",
+                "});",
+                "NodeTypes['sin'] = sinNode;",
+                "const cosNode = new NodeType('cos');",
+                "cosNode.addSignature([NodeValueType.Number], [NodeValueType.Number], (context, data, inputs, outputs) => {",
+                "    outputs[0] = Math.cos(inputs[0]);",
+                "});",
+                "NodeTypes['cos'] = cosNode;",
+                "const tanNode = new NodeType('tan');",
+                "tanNode.addSignature([NodeValueType.Number], [NodeValueType.Number], (context, data, inputs, outputs) => {",
+                "    outputs[0] = Math.tan(inputs[0]);",
+                "});",
+                "NodeTypes['tan'] = tanNode;",
+                "const absNode = new NodeType('abs');",
+                "absNode.addSignature([NodeValueType.Number], [NodeValueType.Number], (context, data, inputs, outputs) => {",
+                "    outputs[0] = Math.abs(inputs[0]);",
+                "});",
+                "NodeTypes['abs'] = absNode;",
+                "const minNode = new NodeType('min');",
+                "minNode.addSignature([NodeValueType.Number, NodeValueType.Number], [NodeValueType.Number], (context, data, inputs, outputs) => {",
+                "    outputs[0] = Math.min(inputs[0], inputs[1]);",
+                "});",
+                "NodeTypes['min'] = minNode;",
+                "const maxNode = new NodeType('max');",
+                "maxNode.addSignature([NodeValueType.Number, NodeValueType.Number], [NodeValueType.Number], (context, data, inputs, outputs) => {",
+                "    outputs[0] = Math.max(inputs[0], inputs[1]);",
+                "});",
+                "NodeTypes['max'] = maxNode;",
+                "const dot = new NodeType('dot');",
+                "dot.addSignature([NodeValueType.Vec2, NodeValueType.Vec2], [NodeValueType.Number], (context, data, inputs, outputs) => {",
+                "    outputs[0] = inputs[0].dot(inputs[1]);",
+                "});",
+                "dot.addSignature([NodeValueType.Vec3, NodeValueType.Vec3], [NodeValueType.Number], (context, data, inputs, outputs) => {",
+                "    outputs[0] = inputs[0].dot(inputs[1]);",
+                "});",
+                "dot.addSignature([NodeValueType.Vec4, NodeValueType.Vec4], [NodeValueType.Number], (context, data, inputs, outputs) => {",
+                "    outputs[0] = inputs[0].dot(inputs[1]);",
+                "});",
+                "NodeTypes['dot'] = dot;",
+                "const cross = new NodeType('cross');",
+                "cross.addSignature([NodeValueType.Vec3, NodeValueType.Vec3], [NodeValueType.Vec3], (context, data, inputs, outputs) => {",
+                "    outputs[0].crossVectors(inputs[0], inputs[1]);",
+                "});",
+                "NodeTypes['cross'] = cross;",
+                "const length = new NodeType('length');",
+                "length.addSignature([NodeValueType.Vec2], [NodeValueType.Number], (context, data, inputs, outputs) => {",
+                "    outputs[0] = inputs[0].length();",
+                "});",
+                "length.addSignature([NodeValueType.Vec3], [NodeValueType.Number], (context, data, inputs, outputs) => {",
+                "    outputs[0] = inputs[0].length();",
+                "});",
+                "length.addSignature([NodeValueType.Vec4], [NodeValueType.Number], (context, data, inputs, outputs) => {",
+                "    outputs[0] = inputs[0].length();",
+                "});",
+                "NodeTypes['length'] = length;",
+                "const lengthSq = new NodeType('lengthSq');",
+                "lengthSq.addSignature([NodeValueType.Vec2], [NodeValueType.Number], (context, data, inputs, outputs) => {",
+                "    outputs[0] = inputs[0].lengthSq();",
+                "});",
+                "lengthSq.addSignature([NodeValueType.Vec3], [NodeValueType.Number], (context, data, inputs, outputs) => {",
+                "    outputs[0] = inputs[0].lengthSq();",
+                "});",
+                "lengthSq.addSignature([NodeValueType.Vec4], [NodeValueType.Number], (context, data, inputs, outputs) => {",
+                "    outputs[0] = inputs[0].lengthSq();",
+                "});",
+                "NodeTypes['lengthSq'] = lengthSq;",
+                "const normalize = new NodeType('normalize');",
+                "normalize.addSignature([NodeValueType.Vec2], [NodeValueType.Vec2], (context, data, inputs, outputs) => {",
+                "    outputs[0].copy(inputs[0]).normalize();",
+                "});",
+                "normalize.addSignature([NodeValueType.Vec3], [NodeValueType.Vec3], (context, data, inputs, outputs) => {",
+                "    outputs[0].copy(inputs[0]).normalize();",
+                "});",
+                "normalize.addSignature([NodeValueType.Vec4], [NodeValueType.Vec4], (context, data, inputs, outputs) => {",
+                "    outputs[0].copy(inputs[0]).normalize();",
+                "});",
+                "NodeTypes['normalize'] = normalize;",
+                "const distance = new NodeType('distance');",
+                "distance.addSignature([NodeValueType.Vec2, NodeValueType.Vec2], [NodeValueType.Number], (context, data, inputs, outputs) => {",
+                "    outputs[0] = inputs[0].distanceTo(inputs[1]);",
+                "});",
+                "distance.addSignature([NodeValueType.Vec3, NodeValueType.Vec3], [NodeValueType.Number], (context, data, inputs, outputs) => {",
+                "    outputs[0] = inputs[0].distanceTo(inputs[1]);",
+                "});",
+                "NodeTypes['distance'] = distance;",
+                "const andNode = new NodeType('and');",
+                "andNode.addSignature([NodeValueType.Boolean, NodeValueType.Boolean], [NodeValueType.Boolean], (context, data, inputs, outputs) => {",
+                "    outputs[0] = inputs[0] && inputs[1];",
+                "});",
+                "NodeTypes['and'] = andNode;",
+                "const orNode = new NodeType('or');",
+                "orNode.addSignature([NodeValueType.Boolean, NodeValueType.Boolean], [NodeValueType.Boolean], (context, data, inputs, outputs) => {",
+                "    outputs[0] = inputs[0] || inputs[1];",
+                "});",
+                "NodeTypes['or'] = orNode;",
+                "const notNode = new NodeType('not');",
+                "notNode.addSignature([NodeValueType.Boolean], [NodeValueType.Boolean], (context, data, inputs, outputs) => {",
+                "    outputs[0] = !inputs[0];",
+                "});",
+                "NodeTypes['not'] = notNode;",
+                "const equalNode = new NodeType('equal');",
+                "equalNode.addSignature([NodeValueType.Number, NodeValueType.Number], [NodeValueType.Boolean], (context, data, inputs, outputs) => {",
+                "    outputs[0] = inputs[0] === inputs[1];",
+                "});",
+                "equalNode.addSignature([NodeValueType.Vec2, NodeValueType.Vec2], [NodeValueType.Boolean], (context, data, inputs, outputs) => {",
+                "    outputs[0] = inputs[0].equals(inputs[1]);",
+                "});",
+                "equalNode.addSignature([NodeValueType.Vec3, NodeValueType.Vec3], [NodeValueType.Boolean], (context, data, inputs, outputs) => {",
+                "    outputs[0] = inputs[0].equals(inputs[1]);",
+                "});",
+                "equalNode.addSignature([NodeValueType.Vec4, NodeValueType.Vec4], [NodeValueType.Boolean], (context, data, inputs, outputs) => {",
+                "    outputs[0] = inputs[0].equals(inputs[1]);",
+                "});",
+                "NodeTypes['equal'] = equalNode;",
+                "const lessThanNode = new NodeType('lessThan');",
+                "lessThanNode.addSignature([NodeValueType.Number, NodeValueType.Number], [NodeValueType.Boolean], (context, data, inputs, outputs) => {",
+                "    outputs[0] = inputs[0] < inputs[1];",
+                "});",
+                "NodeTypes['lessThan'] = lessThanNode;",
+                "const greaterThanNode = new NodeType('greaterThan');",
+                "greaterThanNode.addSignature([NodeValueType.Number, NodeValueType.Number], [NodeValueType.Boolean], (context, data, inputs, outputs) => {",
+                "    outputs[0] = inputs[0] > inputs[1];",
+                "});",
+                "NodeTypes['greaterThan'] = greaterThanNode;",
+                "const lessThanOrEqualNode = new NodeType('lessThanOrEqual');",
+                "lessThanOrEqualNode.addSignature([NodeValueType.Number, NodeValueType.Number], [NodeValueType.Boolean], (context, data, inputs, outputs) => {",
+                "    outputs[0] = inputs[0] <= inputs[1];",
+                "});",
+                "NodeTypes['lessThanOrEqual'] = lessThanOrEqualNode;",
+                "const greaterThanOrEqualNode = new NodeType('greaterThanOrEqual');",
+                "greaterThanOrEqualNode.addSignature([NodeValueType.Number, NodeValueType.Number], [NodeValueType.Boolean], (context, data, inputs, outputs) => {",
+                "    outputs[0] = inputs[0] >= inputs[1];",
+                "});",
+                "NodeTypes['greaterThanOrEqual'] = greaterThanOrEqualNode;",
+                "const ifNode = new NodeType('if');",
+                "ifNode.addSignature([NodeValueType.Boolean, NodeValueType.AnyType, NodeValueType.AnyType], [NodeValueType.AnyType], (context, data, inputs, outputs) => {",
+                "    outputs[0] = inputs[0] ? inputs[1] : inputs[2];",
+                "});",
+                "NodeTypes['if'] = ifNode;",
+                "const numberNode = new NodeType('number');",
+                "numberNode.addSignature([], [NodeValueType.Number], (context, data, inputs, outputs) => {",
+                "    outputs[0] = data.value;",
+                "});",
+                "NodeTypes['number'] = numberNode;",
+                "const vec2Node = new NodeType('vec2');",
+                "vec2Node.addSignature([NodeValueType.Number, NodeValueType.Number], [NodeValueType.Vec2], (context, data, inputs, outputs) => {",
+                "    outputs[0].x = inputs[0];",
+                "    outputs[0].y = inputs[1];",
+                "});",
+                "NodeTypes['vec2'] = vec2Node;",
+                "const vec3Node = new NodeType('vec3');",
+                "vec3Node.addSignature([NodeValueType.Number, NodeValueType.Number, NodeValueType.Number], [NodeValueType.Vec3], (context, data, inputs, outputs) => {",
+                "    outputs[0].x = inputs[0];",
+                "    outputs[0].y = inputs[1];",
+                "    outputs[0].z = inputs[2];",
+                "});",
+                "NodeTypes['vec3'] = vec3Node;",
+                "const vec4Node = new NodeType('vec4');",
+                "vec4Node.addSignature([NodeValueType.Number, NodeValueType.Number, NodeValueType.Number, NodeValueType.Number], [NodeValueType.Vec4], (context, data, inputs, outputs) => {",
+                "    outputs[0].x = inputs[0];",
+                "    outputs[0].y = inputs[1];",
+                "    outputs[0].z = inputs[2];",
+                "    outputs[0].w = inputs[3];",
+                "});",
+                "NodeTypes['vec4'] = vec4Node;",
+                "const splitVec2Node = new NodeType('splitVec2');",
+                "splitVec2Node.addSignature([NodeValueType.Vec2], [NodeValueType.Number, NodeValueType.Number], (context, data, inputs, outputs) => {",
+                "    outputs[0] = inputs[0].x;",
+                "    outputs[1] = inputs[0].y;",
+                "});",
+                "NodeTypes['splitVec2'] = splitVec2Node;",
+                "const splitVec3Node = new NodeType('splitVec3');",
+                "splitVec3Node.addSignature([NodeValueType.Vec3], [NodeValueType.Number, NodeValueType.Number, NodeValueType.Number], (context, data, inputs, outputs) => {",
+                "    outputs[0] = inputs[0].x;",
+                "    outputs[1] = inputs[0].y;",
+                "    outputs[2] = inputs[0].z;",
+                "});",
+                "NodeTypes['splitVec3'] = splitVec3Node;",
+                "const splitVec4Node = new NodeType('splitVec4');",
+                "splitVec4Node.addSignature([NodeValueType.Vec4], [NodeValueType.Number, NodeValueType.Number, NodeValueType.Number, NodeValueType.Number], (context, data, inputs, outputs) => {",
+                "    outputs[0] = inputs[0].x;",
+                "    outputs[1] = inputs[0].y;",
+                "    outputs[2] = inputs[0].z;",
+                "    outputs[3] = inputs[0].w;",
+                "});",
+                "NodeTypes['splitVec4'] = splitVec4Node;",
+                "const boolNode = new NodeType('bool');",
+                "boolNode.addSignature([], [NodeValueType.Boolean], (context, data, inputs, outputs) => {",
+                "    outputs[0] = data.value;",
+                "});",
+                "NodeTypes['bool'] = boolNode;",
+                "const particlePropertyNode = new NodeType('particleProperty');",
+                "particlePropertyNode.addSignature([NodeValueType.NullableAnyType], [NodeValueType.NullableAnyType], (context, data, inputs, outputs) => {",
+                "    if (inputs[0] !== undefined) {",
+                "        if (typeof inputs[0] === 'object') {",
+                "            context.particle[data.property].copy(inputs[0]);",
+                "        }",
+                "        else {",
+                "            context.particle[data.property] = inputs[0];",
+                "        }",
+                "    }",
+                "    if (context.particle[data.property] !== undefined) {",
+                "        if (typeof outputs[0] === 'object') {",
+                "            outputs[0].copy(context.particle[data.property]);",
+                "        }",
+                "        else {",
+                "            outputs[0] = context.particle[data.property];",
+                "        }",
+                "    }",
+                "});",
+                "NodeTypes['particleProperty'] = particlePropertyNode;",
+                "const emitNode = new NodeType('emit');",
+                "emitNode.addSignature([NodeValueType.EventStream], [], (context, data, inputs, outputs) => {",
+                "    const arr = inputs[0];",
+                "    for (let i = 0; i < arr.length; i++) {",
+                "        context.signal(i, arr[i]);",
+                "    }",
+                "});",
+                "NodeTypes['emit'] = emitNode;",
+                "const graphPropertyNode = new NodeType('graphProperty');",
+                "graphPropertyNode.addSignature([NodeValueType.NullableAnyType], [NodeValueType.NullableAnyType], (context, data, inputs, outputs) => {",
+                "    if (inputs[0] !== undefined) {",
+                "        if (typeof inputs[0] === 'object') {",
+                "            context.graph[data.property].copy(inputs[0]);",
+                "        }",
+                "        else {",
+                "            context.graph[data.property] = inputs[0];",
+                "        }",
+                "    }",
+                "    if (context.graph[data.property] !== undefined) {",
+                "        if (typeof outputs[0] === 'object') {",
+                "            outputs[0].copy(context.graph[data.property]);",
+                "        }",
+                "        else {",
+                "            outputs[0] = context.graph[data.property];",
+                "        }",
+                "    }",
+                "});",
+                "NodeTypes['graphProperty'] = graphPropertyNode;",
+                "const startEventNode = new NodeType('startEvent');",
+                "startEventNode.addSignature([], [NodeValueType.EventStream], (context, data, inputs, outputs) => {",
+                "    outputs[0] = [{ type: 'start' }];",
+                "});",
+                "NodeTypes['startEvent'] = startEventNode;",
+                "const repeaterNode = new NodeType('repeater');",
+                "repeaterNode.addSignature([NodeValueType.EventStream, NodeValueType.Number], [NodeValueType.EventStream], (context, data, inputs, outputs) => {",
+                "    const arr = inputs[0];",
+                "    const count = inputs[1];",
+                "    const result = [];",
+                "    for (let j = 0; j < arr.length; j++) {",
+                "        for (let i = 0; i < count; i++) {",
+                "            result.push(arr[j]);",
+                "        }",
+                "    }",
+                "    outputs[0] = result;",
+                "});",
+                "NodeTypes['repeater'] = repeaterNode;",
+                "const timeNode = new NodeType('time');",
+                "timeNode.addSignature([], [NodeValueType.Number], (context, data, inputs, outputs) => {",
+                "    outputs[0] = context.emissionState.time;",
+                "});",
+                "NodeTypes['time'] = timeNode;",
+                "const deltaNode = new NodeType('delta');",
+                "deltaNode.addSignature([], [NodeValueType.Number], (context, data, inputs, outputs) => {",
+                "    outputs[0] = context.delta;",
+                "});",
+                "NodeTypes['delta'] = deltaNode;",
+                "const outputNode = new NodeType('output');",
+                "outputNode.addSignature([NodeValueType.Number], [NodeValueType.Number], (context, data, inputs, outputs) => {",
+                "    outputs[0] = inputs[0];",
+                "});",
+                "outputNode.addSignature([NodeValueType.Vec2], [NodeValueType.Vec2], (context, data, inputs, outputs) => {",
+                "    outputs[0] = inputs[0];",
+                "});",
+                "outputNode.addSignature([NodeValueType.Vec3], [NodeValueType.Vec3], (context, data, inputs, outputs) => {",
+                "    outputs[0] = inputs[0];",
+                "});",
+                "outputNode.addSignature([NodeValueType.Vec4], [NodeValueType.Vec4], (context, data, inputs, outputs) => {",
+                "    outputs[0] = inputs[0];",
+                "});",
+                "outputNode.addSignature([NodeValueType.Boolean], [NodeValueType.Boolean], (context, data, inputs, outputs) => {",
+                "    outputs[0] = inputs[0];",
+                "});",
+                "NodeTypes['output'] = outputNode;",
+                "const lerpNode = new NodeType('lerp');",
+                "lerpNode.addSignature([NodeValueType.Number, NodeValueType.Number, NodeValueType.Number], [NodeValueType.Number], (context, data, inputs, outputs) => {",
+                "    outputs[0] =",
+                "        inputs[0] * (1 - inputs[2]) + inputs[1] * inputs[2];",
+                "});",
+                "lerpNode.addSignature([NodeValueType.Vec2, NodeValueType.Vec2, NodeValueType.Number], [NodeValueType.Vec2], (context, data, inputs, outputs) => {",
+                "    outputs[0].lerpVectors(inputs[0], inputs[1], inputs[2]);",
+                "});",
+                "lerpNode.addSignature([NodeValueType.Vec3, NodeValueType.Vec3, NodeValueType.Number], [NodeValueType.Vec3], (context, data, inputs, outputs) => {",
+                "    outputs[0].lerpVectors(inputs[0], inputs[1], inputs[2]);",
+                "});",
+                "lerpNode.addSignature([NodeValueType.Vec4, NodeValueType.Vec4, NodeValueType.Number], [NodeValueType.Vec4], (context, data, inputs, outputs) => {",
+                "    outputs[0].lerpVectors(inputs[0], inputs[1], inputs[2]);",
+                "});",
+                "NodeTypes['lerp'] = lerpNode;",
+                "const normalD = (x) => {",
+                "    return (1 / Math.sqrt(2 * Math.PI)) * Math.exp(x * x * -0.5);",
+                "};",
+                "const normalDistributionNode = new NodeType('normDistrib');",
+                "normalDistributionNode.addSignature([NodeValueType.Number], [NodeValueType.Number], (context, data, inputs, outputs) => {",
+                "    outputs[0] = normalD(inputs[0]);",
+                "});",
+                "NodeTypes['normDistrib'] = normalDistributionNode;",
+                "const normcdfNode = new NodeType('normcdf');",
+                "normcdfNode.addSignature([NodeValueType.Number], [NodeValueType.Number], (context, data, inputs, outputs) => {",
+                "    let x = inputs[0];",
+                "    const a1 = 0.254829592;",
+                "    const a2 = -0.284496736;",
+                "    const a3 = 1.421413741;",
+                "    const a4 = -1.453152027;",
+                "    const a5 = 1.061405429;",
+                "    const p = 0.3275911;",
+                "    let sign = 1;",
+                "    if (x < 0)",
+                "        sign = -1;",
+                "    x = Math.abs(x) / Math.sqrt(2.0);",
+                "    const t = 1.0 / (1.0 + p * x);",
+                "    const y = 1.0 - ((((a5 * t + a4) * t + a3) * t + a2) * t + a1) * t * Math.exp(-x * x);",
+                "    outputs[0] = 0.5 * (1.0 + sign * y);",
+                "});",
+                "NodeTypes['normcdf'] = normcdfNode;",
+                "const normcdfInvNode = new NodeType('normcdfInv');",
+                "const rationalApproximation = (t) => {",
+                "    const c = [2.515517, 0.802853, 0.010328];",
+                "    const d = [1.432788, 0.189269, 0.001308];",
+                "    return t - ((c[2] * t + c[1]) * t + c[0]) / (((d[2] * t + d[1]) * t + d[0]) * t + 1.0);",
+                "};",
+                "const normcdfInv = (p) => {",
+                "    if (p < 0.5) {",
+                "        return -rationalApproximation(Math.sqrt(-2.0 * Math.log(p)));",
+                "    }",
+                "    else {",
+                "        return rationalApproximation(Math.sqrt(-2.0 * Math.log(1 - p)));",
+                "    }",
+                "};",
+                "normcdfInvNode.addSignature([NodeValueType.Number], [NodeValueType.Number], (context, data, inputs, outputs) => {",
+                "    outputs[0] = normcdfInv(inputs[0]);",
+                "});",
+                "NodeTypes['normcdfInv'] = normcdfInvNode;",
+                "const clampNode = new NodeType('clamp');",
+                "clampNode.addSignature([NodeValueType.Number, NodeValueType.Number, NodeValueType.Number], [NodeValueType.Number], (context, data, inputs, outputs) => {",
+                "    outputs[0] = Math.max(Math.min(inputs[0], inputs[2]), inputs[1]);",
+                "});",
+                "NodeTypes['clamp'] = clampNode;",
+                "const smoothstepNode = new NodeType('smoothstep');",
+                "smoothstepNode.addSignature([NodeValueType.Number, NodeValueType.Number, NodeValueType.Number], [NodeValueType.Number], (context, data, inputs, outputs) => {",
+                "    const x = Math.max(Math.min(inputs[0], inputs[2]), inputs[1]);",
+                "    outputs[0] = x * x * (3 - 2 * x);",
+                "});",
+                "NodeTypes['smoothstep'] = smoothstepNode;",
+                "const randomNode = new NodeType('random');",
+                "randomNode.addSignature([NodeValueType.Number, NodeValueType.Number], [NodeValueType.Number], (context, data, inputs, outputs) => {",
+                "    outputs[0] = Math.random() * (inputs[1] - inputs[0]) + inputs[0];",
+                "});",
+                "randomNode.addSignature([NodeValueType.Vec2, NodeValueType.Vec2], [NodeValueType.Vec2], (context, data, inputs, outputs) => {",
+                "    let random = Math.random();",
+                "    outputs[0].lerpVectors(inputs[0], inputs[1], random);",
+                "});",
+                "randomNode.addSignature([NodeValueType.Vec3, NodeValueType.Vec3], [NodeValueType.Vec3], (context, data, inputs, outputs) => {",
+                "    let random = Math.random();",
+                "    outputs[0].lerpVectors(inputs[0], inputs[1], random);",
+                "});",
+                "randomNode.addSignature([NodeValueType.Vec4, NodeValueType.Vec4], [NodeValueType.Vec4], (context, data, inputs, outputs) => {",
+                "    let random = Math.random();",
+                "    outputs[0].lerpVectors(inputs[0], inputs[1], random);",
+                "});",
+                "NodeTypes['random'] = randomNode;",
+                "const vrandNode = new NodeType('vrand');",
+                "vrandNode.addSignature([], [NodeValueType.Vec2], (context, data, inputs, outputs) => {",
+                "    let x = normcdfInv(Math.random());",
+                "    let y = normcdfInv(Math.random());",
+                "    const mag = Math.sqrt(x * x + y * y);",
+                "    outputs[0].set(x / mag, y / mag);",
+                "});",
+                "vrandNode.addSignature([], [NodeValueType.Vec3], (context, data, inputs, outputs) => {",
+                "    let x = normcdfInv(Math.random());",
+                "    let y = normcdfInv(Math.random());",
+                "    let z = normcdfInv(Math.random());",
+                "    const mag = Math.sqrt(x * x + y * y + z * z);",
+                "    outputs[0].set(x / mag, y / mag, z / mag);",
+                "});",
+                "vrandNode.addSignature([], [NodeValueType.Vec4], (context, data, inputs, outputs) => {",
+                "    let x = normcdfInv(Math.random());",
+                "    let y = normcdfInv(Math.random());",
+                "    let z = normcdfInv(Math.random());",
+                "    let w = normcdfInv(Math.random());",
+                "    const mag = Math.sqrt(x * x + y * y + z * z + w * w);",
+                "    outputs[0].set(x / mag, y / mag, z / mag, w / mag);",
+                "});",
+                "NodeTypes['vrand'] = vrandNode;",
+                "const bsdfNode = new NodeType('bsdf');",
+                "bsdfNode.addSignature([NodeValueType.Vec3, NodeValueType.Vec3, NodeValueType.Vec3, NodeValueType.Number], [], (context, data, inputs, outputs) => { });",
+                "NodeTypes['bsdf'] = bsdfNode;",
+                "const OutputNodeTypeNames = new Set(['output', 'particleProperty', 'graphProperty', 'emit']);",
+                "",
+                "class NodeGraph {",
+                "    constructor(name) {",
+                "        this.inputNodes = [];",
+                "        this.outputNodes = [];",
+                "        this.allNodes = new Map();",
+                "        this.wires = [];",
+                "        this.compiled = false;",
+                "        this.nodesInOrder = [];",
+                "        this.version = '1.0';",
+                "        this.uuid = THREE.MathUtils.generateUUID();",
+                "        this.name = name;",
+                "        this.revision = 0;",
+                "    }",
+                "    addWire(wire) {",
+                "        this.wires.push(wire);",
+                "        this.revision++;",
+                "    }",
+                "    addNode(node) {",
+                "        this.allNodes.set(node.id, node);",
+                "        if (node.type === NodeTypes['input']) {",
+                "            this.inputNodes.push(node);",
+                "        }",
+                "        else if (OutputNodeTypeNames.has(node.type.name)) {",
+                "            this.outputNodes.push(node);",
+                "        }",
+                "        this.revision++;",
+                "    }",
+                "    getNode(id) {",
+                "        return this.allNodes.get(id);",
+                "    }",
+                "    deleteNode(node) {",
+                "        this.allNodes.delete(node.id);",
+                "        this.revision++;",
+                "    }",
+                "    deleteWire(wire) {",
+                "        let index = wire.input.outputs[wire.inputIndex].indexOf(wire);",
+                "        if (index !== -1) {",
+                "            wire.input.outputs[wire.inputIndex].splice(index, 1);",
+                "        }",
+                "        wire.output.inputs[wire.outputIndex] = undefined;",
+                "        index = this.wires.indexOf(wire);",
+                "        if (index != -1) {",
+                "            this.wires[index] = this.wires[this.wires.length - 1];",
+                "            this.wires.pop();",
+                "        }",
+                "        this.revision++;",
+                "    }",
+                "    toJSON() {",
+                "        throw new Error('not implemented');",
+                "    }",
+                "    clone() {",
+                "        throw new Error('not implemented');",
+                "    }",
+                "}",
+                "",
+                "new THREE.Vector3(0, 0, 1);",
+                "const tempQ = new THREE.Quaternion();",
+                "const tempV = new THREE.Vector3();",
+                "const tempV2 = new THREE.Vector3();",
+                "const PREWARM_FPS = 60;",
+                "const DEFAULT_GEOMETRY = new THREE.PlaneGeometry(1, 1, 1, 1);",
+                "class NodeVFX {",
+                "    set time(time) {",
+                "        this.emissionState.time = time;",
+                "    }",
+                "    get time() {",
+                "        return this.emissionState.time;",
+                "    }",
+                "    get layers() {",
+                "        return this.rendererSettings.layers;",
+                "    }",
+                "    get texture() {",
+                "        return this.rendererSettings.material.map;",
+                "    }",
+                "    set texture(texture) {",
+                "        this.rendererSettings.material.map = texture;",
+                "        this.neededToUpdateRender = true;",
+                "    }",
+                "    get material() {",
+                "        return this.rendererSettings.material;",
+                "    }",
+                "    set material(material) {",
+                "        this.rendererSettings.material = material;",
+                "        this.neededToUpdateRender = true;",
+                "    }",
+                "    get instancingGeometry() {",
+                "        return this.rendererSettings.instancingGeometry;",
+                "    }",
+                "    set instancingGeometry(geometry) {",
+                "        this.restart();",
+                "        this.particles.length = 0;",
+                "        this.rendererSettings.instancingGeometry = geometry;",
+                "        this.neededToUpdateRender = true;",
+                "    }",
+                "    get renderMode() {",
+                "        return this.rendererSettings.renderMode;",
+                "    }",
+                "    set renderMode(renderMode) {",
+                "        if ((this.rendererSettings.renderMode != RenderMode.Trail && renderMode === RenderMode.Trail) ||",
+                "            (this.rendererSettings.renderMode == RenderMode.Trail && renderMode !== RenderMode.Trail)) {",
+                "            this.restart();",
+                "            this.particles.length = 0;",
+                "        }",
+                "        if (this.rendererSettings.renderMode !== renderMode) {",
+                "            switch (renderMode) {",
+                "                case RenderMode.Trail:",
+                "                    this.rendererEmitterSettings = {",
+                "                        startLength: 30,",
+                "                        followLocalOrigin: false,",
+                "                    };",
+                "                    break;",
+                "                case RenderMode.Mesh:",
+                "                    this.rendererEmitterSettings = {",
+                "                        geometry: new THREE.PlaneGeometry(1, 1),",
+                "                    };",
+                "                    break;",
+                "                case RenderMode.BillBoard:",
+                "                case RenderMode.VerticalBillBoard:",
+                "                case RenderMode.HorizontalBillBoard:",
+                "                case RenderMode.StretchedBillBoard:",
+                "                    this.rendererEmitterSettings = {};",
+                "                    break;",
+                "            }",
+                "        }",
+                "        this.rendererSettings.renderMode = renderMode;",
+                "        this.neededToUpdateRender = true;",
+                "    }",
+                "    get renderOrder() {",
+                "        return this.rendererSettings.renderOrder;",
+                "    }",
+                "    set renderOrder(renderOrder) {",
+                "        this.rendererSettings.renderOrder = renderOrder;",
+                "        this.neededToUpdateRender = true;",
+                "    }",
+                "    get blending() {",
+                "        return this.rendererSettings.material.blending;",
+                "    }",
+                "    set blending(blending) {",
+                "        this.rendererSettings.material.blending = blending;",
+                "        this.neededToUpdateRender = true;",
+                "    }",
+                "    constructor(parameters) {",
+                "        var _a, _b, _c, _d, _e, _f, _g;",
+                "        this.temp = new THREE.Vector3();",
+                "        this.travelDistance = 0;",
+                "        this.normalMatrix = new THREE.Matrix3();",
+                "        this.speedFactor = 0;",
+                "        this.autoDestroy = parameters.autoDestroy === undefined ? false : parameters.autoDestroy;",
+                "        this.duration = (_a = parameters.duration) !== null && _a !== void 0 ? _a : 1;",
+                "        this.looping = parameters.looping === undefined ? true : parameters.looping;",
+                "        this.prewarm = parameters.prewarm === undefined ? false : parameters.prewarm;",
+                "        this.worldSpace = (_b = parameters.worldSpace) !== null && _b !== void 0 ? _b : false;",
+                "        this.rendererEmitterSettings = (_c = parameters.rendererEmitterSettings) !== null && _c !== void 0 ? _c : {};",
+                "        this.emissionGraph = parameters.emissionGraph;",
+                "        this.updateGraph = parameters.updateGraph;",
+                "        this.interpreter = new Interpreter();",
+                "        this.rendererSettings = {",
+                "            instancingGeometry: (_d = parameters.instancingGeometry) !== null && _d !== void 0 ? _d : DEFAULT_GEOMETRY,",
+                "            renderMode: (_e = parameters.renderMode) !== null && _e !== void 0 ? _e : RenderMode.BillBoard,",
+                "            renderOrder: (_f = parameters.renderOrder) !== null && _f !== void 0 ? _f : 0,",
+                "            material: parameters.material,",
+                "            layers: (_g = parameters.layers) !== null && _g !== void 0 ? _g : new THREE.Layers(),",
+                "            uTileCount: 1,",
+                "            vTileCount: 1,",
+                "        };",
+                "        this.neededToUpdateRender = true;",
+                "        this.particles = new Array();",
+                "        this.emitter = new ParticleEmitter(this);",
+                "        this.paused = false;",
+                "        this.particleNum = 0;",
+                "        this.emissionState = {",
+                "            time: 0,",
+                "        };",
+                "        this.emitEnded = false;",
+                "        this.markForDestroy = false;",
+                "        this.prewarmed = false;",
+                "    }",
+                "    pause() {",
+                "        this.paused = true;",
+                "    }",
+                "    play() {",
+                "        this.paused = false;",
+                "    }",
+                "    spawn(emissionState, matrix) {",
+                "        tempQ.setFromRotationMatrix(matrix);",
+                "        const translation = tempV;",
+                "        const quaternion = tempQ;",
+                "        const scale = tempV2;",
+                "        matrix.decompose(translation, quaternion, scale);",
+                "        this.particleNum++;",
+                "        while (this.particles.length < this.particleNum) {",
+                "            this.particles.push(new NodeParticle());",
+                "        }",
+                "        const particle = this.particles[this.particleNum - 1];",
+                "        particle.reset();",
+                "        this.interpreter.run(this.updateGraph, { particle: particle, emissionState: this.emissionState });",
+                "        if (this.rendererSettings.renderMode === RenderMode.Trail &&",
+                "            this.rendererEmitterSettings.followLocalOrigin) {",
+                "            const trail = particle;",
+                "            trail.localPosition = new THREE.Vector3().copy(trail.position);",
+                "        }",
+                "        if (this.worldSpace) {",
+                "            particle.position.applyMatrix4(matrix);",
+                "            particle.size *= (Math.abs(scale.x) + Math.abs(scale.y) + Math.abs(scale.z)) / 3;",
+                "            particle.velocity.multiply(scale).applyMatrix3(this.normalMatrix);",
+                "            if (particle.rotation && particle.rotation instanceof THREE.Quaternion) {",
+                "                particle.rotation.multiplyQuaternions(tempQ, particle.rotation);",
+                "            }",
+                "        }",
+                "    }",
+                "    endEmit() {",
+                "        this.emitEnded = true;",
+                "        if (this.autoDestroy) {",
+                "            this.markForDestroy = true;",
+                "        }",
+                "    }",
+                "    dispose() {",
+                "        if (this._renderer)",
+                "            this._renderer.deleteSystem(this);",
+                "        this.emitter.dispose();",
+                "        if (this.emitter.parent)",
+                "            this.emitter.parent.remove(this.emitter);",
+                "    }",
+                "    restart() {",
+                "        this.paused = false;",
+                "        this.particleNum = 0;",
+                "        this.emissionState.time = 0;",
+                "        this.emitEnded = false;",
+                "        this.markForDestroy = false;",
+                "        this.prewarmed = false;",
+                "    }",
+                "    update(delta) {",
+                "        if (this.paused)",
+                "            return;",
+                "        let currentParent = this.emitter;",
+                "        while (currentParent.parent) {",
+                "            currentParent = currentParent.parent;",
+                "        }",
+                "        if (currentParent.type !== 'Scene') {",
+                "            this.dispose();",
+                "            return;",
+                "        }",
+                "        if (this.emitEnded && this.particleNum === 0) {",
+                "            if (this.markForDestroy && this.emitter.parent)",
+                "                this.dispose();",
+                "            return;",
+                "        }",
+                "        if (this.looping && this.prewarm && !this.prewarmed) {",
+                "            this.prewarmed = true;",
+                "            for (let i = 0; i < this.duration * PREWARM_FPS; i++) {",
+                "                this.update(1.0 / PREWARM_FPS);",
+                "            }",
+                "        }",
+                "        if (delta > 0.1) {",
+                "            delta = 0.1;",
+                "        }",
+                "        if (this.neededToUpdateRender) {",
+                "            if (this._renderer)",
+                "                this._renderer.updateSystem(this);",
+                "            this.neededToUpdateRender = false;",
+                "        }",
+                "        this.emit(delta, this.emissionState, this.emitter.matrixWorld);",
+                "        const context = { particle: undefined, emissionState: this.emissionState, delta };",
+                "        for (let i = 0; i < this.particleNum; i++) {",
+                "            context.particle = this.particles[i];",
+                "            this.interpreter.run(this.updateGraph, context);",
+                "        }",
+                "        for (let i = 0; i < this.particleNum; i++) {",
+                "            if (this.rendererEmitterSettings.followLocalOrigin &&",
+                "                this.particles[i].localPosition) {",
+                "                this.particles[i].position.copy(this.particles[i].localPosition);",
+                "                if (this.particles[i].parentMatrix) {",
+                "                    this.particles[i].position.applyMatrix4(this.particles[i].parentMatrix);",
+                "                }",
+                "                else {",
+                "                    this.particles[i].position.applyMatrix4(this.emitter.matrixWorld);",
+                "                }",
+                "            }",
+                "            else {",
+                "                this.particles[i].position.addScaledVector(this.particles[i].velocity, delta);",
+                "            }",
+                "            this.particles[i].age += delta;",
+                "        }",
+                "        if (this.rendererSettings.renderMode === RenderMode.Trail) {",
+                "            for (let i = 0; i < this.particleNum; i++) {",
+                "                const particle = this.particles[i];",
+                "                particle.update();",
+                "            }",
+                "        }",
+                "        for (let i = 0; i < this.particleNum; i++) {",
+                "            const particle = this.particles[i];",
+                "            if (particle.died && (!(particle instanceof TrailParticle) || particle.previous.length === 0)) {",
+                "                this.particles[i] = this.particles[this.particleNum - 1];",
+                "                this.particles[this.particleNum - 1] = particle;",
+                "                this.particleNum--;",
+                "                i--;",
+                "            }",
+                "        }",
+                "    }",
+                "    emit(delta, emissionState, emitterMatrix) {",
+                "        if (emissionState.time > this.duration) {",
+                "            if (this.looping) {",
+                "                emissionState.time -= this.duration;",
+                "            }",
+                "            else {",
+                "                if (!this.emitEnded) {",
+                "                    this.endEmit();",
+                "                }",
+                "            }",
+                "        }",
+                "        this.normalMatrix.getNormalMatrix(emitterMatrix);",
+                "        const context = {",
+                "            signal: () => {",
+                "                this.spawn(emissionState, emitterMatrix);",
+                "            },",
+                "            emissionState,",
+                "            delta,",
+                "        };",
+                "        if (!this.emitEnded) {",
+                "            this.interpreter.run(this.emissionGraph, context);",
+                "        }",
+                "        if (this.previousWorldPos === undefined)",
+                "            this.previousWorldPos = new THREE.Vector3();",
+                "        this.emitter.getWorldPosition(this.previousWorldPos);",
+                "        emissionState.time += delta;",
+                "    }",
+                "    toJSON(meta, options = {}) {",
+                "        return {};",
+                "    }",
+                "    getRendererSettings() {",
+                "        return this.rendererSettings;",
+                "    }",
+                "    clone() {",
+                "        return this;",
+                "    }",
+                "}",
+                "",
+                "gdjs.__particleEmmiter3DExtension = {",
+                "    ParticleEmitter3DRenderer,",
+                "    ParticleEmitterAdapter,",
+                "",
+                "    ApplyCollision,",
+                "    ApplyForce,",
+                "    ApplySequences,",
+                "    AxisAngleGenerator,",
+                "    BatchedParticleRenderer,",
+                "    BatchedRenderer,",
+                "    BehaviorFromJSON,",
+                "    BehaviorTypes,",
+                "    Bezier,",
+                "    ChangeEmitDirection,",
+                "    ColorGeneratorFromJSON,",
+                "    ColorOverLife,",
+                "    ColorRange,",
+                "    ConeEmitter,",
+                "    ConstantColor,",
+                "    ConstantValue,",
+                "    DonutEmitter,",
+                "    EmitSubParticleSystem,",
+                "    EmitterFromJSON,",
+                "    EmitterMode,",
+                "    EmitterShapes,",
+                "    EulerGenerator,",
+                "    ForceOverLife,",
+                "    FrameOverLife,",
+                "    GeneratorFromJSON,",
+                "    Gradient,",
+                "    GraphNodeType,",
+                "    GravityForce,",
+                "    GridEmitter,",
+                "    Interpreter,",
+                "    IntervalValue,",
+                "    MeshSurfaceEmitter,",
+                "    Node,",
+                "    NodeGraph,",
+                "    NodeType,",
+                "    NodeTypes,",
+                "    NodeValueType,",
+                "    Noise,",
+                "    OrbitOverLife,",
+                "    ParticleEmitter,",
+                "    ParticleSystem,",
+                "    PiecewiseBezier,",
+                "    PiecewiseFunction,",
+                "    Plugins,",
+                "    PointEmitter,",
+                "    QuarksLoader,",
+                "    RandomColor,",
+                "    RandomColorBetweenGradient,",
+                "    RandomQuatGenerator,",
+                "    RecordState,",
+                "    RenderMode,",
+                "    Rotation3DOverLife,",
+                "    RotationGeneratorFromJSON,",
+                "    RotationOverLife,",
+                "    SequencerFromJSON,",
+                "    SizeOverLife,",
+                "    SpeedOverLife,",
+                "    SphereEmitter,",
+                "    SpriteBatch,",
+                "    SpriteParticle,",
+                "    SubParticleEmitMode,",
+                "    TextureSequencer,",
+                "    TrailBatch,",
+                "    TrailParticle,",
+                "    TurbulenceField,",
+                "    VFXBatch,",
+                "    ValueGeneratorFromJSON,",
+                "    WidthOverLength,",
+                "    Wire,",
+                "    genDefaultForNodeValueType,",
+                "    getPhysicsResolver,",
+                "    loadPlugin,",
+                "    setPhysicsResolver,",
+                "    unloadPlugin,",
+                "}"
+              ],
+              "parameterObjects": "",
+              "useStrict": true,
+              "eventsSheetExpanded": true
+            }
+          ],
+          "parameters": [],
+          "objectGroups": []
+        },
+        {
+          "fullName": "",
+          "functionType": "Action",
+          "name": "onScenePreEvents",
+          "sentence": "",
+          "events": [
+            {
+              "type": "BuiltinCommonInstructions::JsCode",
+              "inlineCode": [
+                "\r",
+                "// See doStepPostEvents\r",
+                "runtimeScene.__particleEmmiter3DExtension = runtimeScene.__particleEmmiter3DExtension || {};\r",
+                "runtimeScene.__particleEmmiter3DExtension.emittersStepped = 0;"
+              ],
+              "parameterObjects": "",
+              "useStrict": true,
+              "eventsSheetExpanded": true
+            }
+          ],
+          "parameters": [],
+          "objectGroups": []
+        }
+      ],
+      "eventsBasedBehaviors": [],
+      "eventsBasedObjects": [
+        {
+          "areaMaxX": 64,
+          "areaMaxY": 64,
+          "areaMaxZ": 64,
+          "areaMinX": 0,
+          "areaMinY": 0,
+          "areaMinZ": 0,
+          "defaultName": "ParticleEmitter",
+          "description": "Display a large number of particles to create visual effects.",
+          "fullName": "3D particle emitter",
+          "is3D": true,
+          "isUsingLegacyInstancesRenderer": true,
+          "name": "ParticleEmitter3D",
+          "eventsFunctions": [
+            {
+              "fullName": "",
+              "functionType": "Action",
+              "name": "onCreated",
+              "sentence": "",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "ParticleEmitter3D::DefineHelperClasses"
+                      },
+                      "parameters": [
+                        "",
+                        ""
+                      ]
+                    }
+                  ]
+                },
+                {
+                  "type": "BuiltinCommonInstructions::JsCode",
+                  "inlineCode": [
+                    "const BatchedRenderer = gdjs.__particleEmmiter3DExtension.BatchedRenderer;",
+                    "const ParticleSystem = gdjs.__particleEmmiter3DExtension.ParticleSystem;",
+                    "const TextureLoader = gdjs.__particleEmmiter3DExtension.TextureLoader;",
+                    "const IntervalValue = gdjs.__particleEmmiter3DExtension.IntervalValue;",
+                    "const ConstantValue = gdjs.__particleEmmiter3DExtension.ConstantValue;",
+                    "const ConstantColor = gdjs.__particleEmmiter3DExtension.ConstantColor;",
+                    "const ColorOverLife = gdjs.__particleEmmiter3DExtension.ColorOverLife;",
+                    "const SizeOverLife = gdjs.__particleEmmiter3DExtension.SizeOverLife;",
+                    "const ApplyForce = gdjs.__particleEmmiter3DExtension.ApplyForce;",
+                    "const Gradient = gdjs.__particleEmmiter3DExtension.Gradient;",
+                    "const PiecewiseBezier = gdjs.__particleEmmiter3DExtension.PiecewiseBezier;",
+                    "const Bezier = gdjs.__particleEmmiter3DExtension.Bezier;",
+                    "const PointEmitter = gdjs.__particleEmmiter3DExtension.PointEmitter;",
+                    "const ConeEmitter = gdjs.__particleEmmiter3DExtension.ConeEmitter;",
+                    "const RenderMode = gdjs.__particleEmmiter3DExtension.RenderMode;",
+                    "",
+                    "const { ParticleEmitterAdapter, ParticleEmitter3DRenderer } = gdjs.__particleEmmiter3DExtension;",
+                    "",
+                    "/** @type {gdjs.CustomRuntimeObject} */",
+                    "const object = objects[0];",
+                    "",
+                    "// Here runtimeScene is the gdjs.CustomRuntimeObjectInstanceContainer inside the custom object.",
+                    "const gameScene = object.getRuntimeScene();",
+                    "",
+                    "/** @type {SpriteObjectDataType} */",
+                    "const particleSpriteData = object._instanceContainer._objects.get(\"Particle\");",
+                    "const resourceName = particleSpriteData.animations[0].directions[0].sprites[0].image;",
+                    "const texture = object",
+                    "    .getInstanceContainer()",
+                    "    .getGame()",
+                    "    .getImageManager().getThreeTexture(resourceName);",
+                    "",
+                    "// Set the blending here because changes are not applied after the emitter creation.",
+                    "const blendingString = object._getBlending();",
+                    "const blending =",
+                    "    blendingString === \"Additive\" ? THREE.AdditiveBlending :",
+                    "        blendingString === \"Normal\" ? THREE.NormalBlending :",
+                    "            blendingString === \"Subtractive\" ? THREE.SubtractiveBlending :",
+                    "                blendingString === \"Multiply\" ? THREE.MultiplyBlending :",
+                    "                    blendingString === \"None\" ? THREE.NoBlending :",
+                    "                        THREE.AdditiveBlending;",
+                    "",
+                    "",
+                    "// Build a configuration with the right kind of objects.",
+                    "// These values are not important as they are overrided by the object properties values.",
+                    "const muzzle = {",
+                    "    duration: 10,",
+                    "    looping: false,",
+                    "    startLife: new IntervalValue(1, 2),",
+                    "    startSpeed: new IntervalValue(100, 200),",
+                    "    startSize: new IntervalValue(20, 50),",
+                    "    startColor: new ConstantColor(new THREE.Vector4(1, 1, 1, 1)),",
+                    "    worldSpace: true,",
+                    "",
+                    "    maxParticle: 5,",
+                    "    emissionOverTime: new ConstantValue(50),",
+                    "    emissionBursts: [{",
+                    "        time: 0,",
+                    "        count: new ConstantValue(1),",
+                    "        cycle: 1,",
+                    "        interval: 0.01,",
+                    "        probability: 1,",
+                    "    }],",
+                    "",
+                    "    shape: new PointEmitter(),",
+                    "    material: new THREE.MeshBasicMaterial({",
+                    "        map: texture,",
+                    "        blending: blending,",
+                    "        transparent: true,",
+                    "        side: THREE.DoubleSide",
+                    "    }),",
+                    "    startTileIndex: 0,",
+                    "    uTileCount: 1,",
+                    "    vTileCount: 1,",
+                    "    renderOrder: 2,",
+                    "    renderMode: RenderMode.BillBoard",
+                    "};",
+                    "const particleSystem = new ParticleSystem(muzzle);",
+                    "",
+                    "const colorOverLife = new ColorOverLife(",
+                    "    new Gradient(",
+                    "        [",
+                    "            [new THREE.Vector3(1, 0, 0), 0],",
+                    "            [new THREE.Vector3(1, 0, 0), 1],",
+                    "        ],",
+                    "        [",
+                    "            [1, 0],",
+                    "            [1, 1],",
+                    "        ]",
+                    "    ));",
+                    "particleSystem.addBehavior(colorOverLife);",
+                    "const sizeOverLife = new SizeOverLife(new PiecewiseBezier([[new Bezier(1, 2 / 3, 1 / 3, 0), 0]]));",
+                    "particleSystem.addBehavior(sizeOverLife);",
+                    "const applyForce = new ApplyForce(new THREE.Vector3(0, 0, -1), new ConstantValue(0));",
+                    "particleSystem.addBehavior(applyForce);",
+                    "",
+                    "particleSystem.emitter.name = object.getName() + object.getNameId();",
+                    "",
+                    "object.__particleEmitterAdapter = new ParticleEmitterAdapter(particleSystem, colorOverLife, sizeOverLife, applyForce);",
+                    "object.__particleSystem = particleSystem;",
+                    "",
+                    "// This is a hack that may break in future releases.",
+                    "// Replace the group that would hold children objects by the emmiter.",
+                    "const layer = gameScene.getLayer(object.getLayer());",
+                    "const group = object.getRenderer()._threeGroup;",
+                    "layer.getRenderer().remove3DRendererObject(group);",
+                    "particleSystem.emitter.position.copy(group.position);",
+                    "particleSystem.emitter.rotation.order = 'ZYX';",
+                    "particleSystem.emitter.rotation.copy(group.rotation);",
+                    "",
+                    "const particleEmitter3DRenderer = new ParticleEmitter3DRenderer(object, object._instanceContainer, object.getInstanceContainer());",
+                    "object._renderer = particleEmitter3DRenderer;",
+                    "particleEmitter3DRenderer._threeGroup = particleSystem.emitter;",
+                    "layer.getRenderer().add3DRendererObject(particleSystem.emitter);",
+                    "",
+                    "particleSystem.emitter.updateMatrixWorld(true);",
+                    "",
+                    "",
+                    "// See doStepPostEvents",
+                    "gameScene.__particleEmmiter3DExtension = gameScene.__particleEmmiter3DExtension || {};",
+                    "gameScene.__particleEmmiter3DExtension.emittersCount = gameScene.__particleEmmiter3DExtension.emittersCount || 0;",
+                    "gameScene.__particleEmmiter3DExtension.emittersCount++;",
+                    "",
+                    ""
+                  ],
+                  "parameterObjects": "Object",
+                  "useStrict": true,
+                  "eventsSheetExpanded": true
+                },
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "ParticleEmitter3D::ParticleEmitter3D::UpdateFromProperties"
+                      },
+                      "parameters": [
+                        "Object",
+                        ""
+                      ]
+                    },
+                    {
+                      "type": {
+                        "value": "ParticleEmitter3D::ParticleEmitter3D::SetRotationCenter"
+                      },
+                      "parameters": [
+                        "Object",
+                        "0",
+                        "0",
+                        "0"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D",
+                  "type": "object"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "fullName": "",
+              "functionType": "Action",
+              "name": "onDestroy",
+              "sentence": "",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::JsCode",
+                  "inlineCode": [
+                    "/** @type {gdjs.CustomRuntimeObject} */",
+                    "const object = objects[0];",
+                    "// Here runtimeScene is the gdjs.CustomRuntimeObjectInstanceContainer inside the custom object.",
+                    "const gameScene = object.getRuntimeScene();",
+                    "",
+                    "object.__particleSystem.dispose();",
+                    "",
+                    "// See doStepPostEvents",
+                    "gameScene.__particleEmmiter3DExtension.emittersCount--;",
+                    "",
+                    "if (gameScene.__particleEmmiter3DExtension.emittersCount === 0) {",
+                    "    // Update batch system now because doStepPostEvents won't be called.",
+                    "    gameScene.__particleEmmiter3DExtension.layerNames = gameScene.__particleEmmiter3DExtension.layerNames || [];",
+                    "    const layerNames = gameScene.__particleEmmiter3DExtension.layerNames;",
+                    "    gameScene.getAllLayerNames(layerNames);",
+                    "    for (const layerName of layerNames) {",
+                    "        const layer = gameScene.getLayer(layerName);",
+                    "        if (layer.__particleEmmiter3DExtension) {",
+                    "            layer.__particleEmmiter3DExtension.batchSystem.update(object.getElapsedTime() / 1000);",
+                    "        }",
+                    "    }",
+                    "}"
+                  ],
+                  "parameterObjects": "Object",
+                  "useStrict": true,
+                  "eventsSheetExpanded": true
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D",
+                  "type": "object"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "fullName": "",
+              "functionType": "Action",
+              "name": "onHotReloading",
+              "sentence": "",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "ParticleEmitter3D::ParticleEmitter3D::UpdateFromProperties"
+                      },
+                      "parameters": [
+                        "Object",
+                        ""
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D",
+                  "type": "object"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "Update from properties.",
+              "fullName": "Update from properties",
+              "functionType": "Action",
+              "name": "UpdateFromProperties",
+              "private": true,
+              "sentence": "Update from properties of _PARAM0_",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "ParticleEmitter3D::ParticleEmitter3D::SetZ"
+                      },
+                      "parameters": [
+                        "Object",
+                        "=",
+                        "Object.Z()",
+                        ""
+                      ]
+                    },
+                    {
+                      "type": {
+                        "value": "ParticleEmitter3D::ParticleEmitter3D::SetRotationX"
+                      },
+                      "parameters": [
+                        "Object",
+                        "=",
+                        "Object.PropertyRotationX()",
+                        ""
+                      ]
+                    },
+                    {
+                      "type": {
+                        "value": "ParticleEmitter3D::ParticleEmitter3D::SetRotationY"
+                      },
+                      "parameters": [
+                        "Object",
+                        "=",
+                        "Object.PropertyRotationY()",
+                        ""
+                      ]
+                    },
+                    {
+                      "type": {
+                        "value": "ParticleEmitter3D::ParticleEmitter3D::SetStartColor"
+                      },
+                      "parameters": [
+                        "Object",
+                        "=",
+                        "Object.StartColor()",
+                        ""
+                      ]
+                    },
+                    {
+                      "type": {
+                        "value": "ParticleEmitter3D::ParticleEmitter3D::SetEndColor"
+                      },
+                      "parameters": [
+                        "Object",
+                        "=",
+                        "Object.EndColor()",
+                        ""
+                      ]
+                    },
+                    {
+                      "type": {
+                        "value": "ParticleEmitter3D::ParticleEmitter3D::SetStartOpacity"
+                      },
+                      "parameters": [
+                        "Object",
+                        "=",
+                        "Object.StartOpacity()",
+                        ""
+                      ]
+                    },
+                    {
+                      "type": {
+                        "value": "ParticleEmitter3D::ParticleEmitter3D::SetEndOpacity"
+                      },
+                      "parameters": [
+                        "Object",
+                        "=",
+                        "Object.EndOpacity()",
+                        ""
+                      ]
+                    },
+                    {
+                      "type": {
+                        "value": "ParticleEmitter3D::ParticleEmitter3D::SetFlow"
+                      },
+                      "parameters": [
+                        "Object",
+                        "=",
+                        "Object.Flow()",
+                        ""
+                      ]
+                    },
+                    {
+                      "type": {
+                        "value": "ParticleEmitter3D::ParticleEmitter3D::SetStartSizeMin"
+                      },
+                      "parameters": [
+                        "Object",
+                        "=",
+                        "Object.StartSizeMin()",
+                        ""
+                      ]
+                    },
+                    {
+                      "type": {
+                        "value": "ParticleEmitter3D::ParticleEmitter3D::SetStartSizeMax"
+                      },
+                      "parameters": [
+                        "Object",
+                        "=",
+                        "Object.StartSizeMax()",
+                        ""
+                      ]
+                    },
+                    {
+                      "type": {
+                        "value": "ParticleEmitter3D::ParticleEmitter3D::SetEndScale"
+                      },
+                      "parameters": [
+                        "Object",
+                        "=",
+                        "Object.EndScale()",
+                        ""
+                      ]
+                    },
+                    {
+                      "type": {
+                        "value": "ParticleEmitter3D::ParticleEmitter3D::SetStartSpeedMin"
+                      },
+                      "parameters": [
+                        "Object",
+                        "=",
+                        "Object.StartSpeedMin()",
+                        ""
+                      ]
+                    },
+                    {
+                      "type": {
+                        "value": "ParticleEmitter3D::ParticleEmitter3D::SetStartSpeedMax"
+                      },
+                      "parameters": [
+                        "Object",
+                        "=",
+                        "Object.StartSpeedMax()",
+                        ""
+                      ]
+                    },
+                    {
+                      "type": {
+                        "value": "ParticleEmitter3D::ParticleEmitter3D::SetLifespanMin"
+                      },
+                      "parameters": [
+                        "Object",
+                        "=",
+                        "Object.LifespanMin()",
+                        ""
+                      ]
+                    },
+                    {
+                      "type": {
+                        "value": "ParticleEmitter3D::ParticleEmitter3D::SetLifespanMax"
+                      },
+                      "parameters": [
+                        "Object",
+                        "=",
+                        "Object.LifespanMax()",
+                        ""
+                      ]
+                    },
+                    {
+                      "type": {
+                        "value": "ParticleEmitter3D::ParticleEmitter3D::SetDuration"
+                      },
+                      "parameters": [
+                        "Object",
+                        "=",
+                        "Object.Duration()",
+                        ""
+                      ]
+                    },
+                    {
+                      "type": {
+                        "value": "ParticleEmitter3D::ParticleEmitter3D::SetSpayConeAngle"
+                      },
+                      "parameters": [
+                        "Object",
+                        "=",
+                        "Object.SpayConeAngle()",
+                        ""
+                      ]
+                    },
+                    {
+                      "type": {
+                        "value": "ParticleEmitter3D::ParticleEmitter3D::SetGravity"
+                      },
+                      "parameters": [
+                        "Object",
+                        "=",
+                        "Object.Gravity()",
+                        ""
+                      ]
+                    },
+                    {
+                      "type": {
+                        "value": "ParticleEmitter3D::ParticleEmitter3D::SetGravityTop"
+                      },
+                      "parameters": [
+                        "Object",
+                        "=",
+                        "Object.GravityTop()",
+                        ""
+                      ]
+                    }
+                  ],
+                  "events": [
+                    {
+                      "type": "BuiltinCommonInstructions::Standard",
+                      "conditions": [
+                        {
+                          "type": {
+                            "inverted": true,
+                            "value": "ParticleEmitter3D::ParticleEmitter3D::PropertyAreParticlesRelative"
+                          },
+                          "parameters": [
+                            "Object"
+                          ]
+                        }
+                      ],
+                      "actions": [
+                        {
+                          "type": {
+                            "value": "ParticleEmitter3D::ParticleEmitter3D::SetAreParticlesRelative"
+                          },
+                          "parameters": [
+                            "Object",
+                            "no",
+                            ""
+                          ]
+                        }
+                      ]
+                    },
+                    {
+                      "type": "BuiltinCommonInstructions::Standard",
+                      "conditions": [
+                        {
+                          "type": {
+                            "value": "ParticleEmitter3D::ParticleEmitter3D::PropertyAreParticlesRelative"
+                          },
+                          "parameters": [
+                            "Object"
+                          ]
+                        }
+                      ],
+                      "actions": [
+                        {
+                          "type": {
+                            "value": "ParticleEmitter3D::ParticleEmitter3D::SetAreParticlesRelative"
+                          },
+                          "parameters": [
+                            "Object",
+                            "yes",
+                            ""
+                          ]
+                        }
+                      ]
+                    },
+                    {
+                      "type": "BuiltinCommonInstructions::Comment",
+                      "color": {
+                        "b": 109,
+                        "g": 230,
+                        "r": 255,
+                        "textB": 0,
+                        "textG": 0,
+                        "textR": 0
+                      },
+                      "comment": "TODO: Blending can't be changed."
+                    },
+                    {
+                      "disabled": true,
+                      "type": "BuiltinCommonInstructions::Standard",
+                      "conditions": [],
+                      "actions": [
+                        {
+                          "type": {
+                            "value": "ParticleEmitter3D::ParticleEmitter3D::SetBlending"
+                          },
+                          "parameters": [
+                            "Object",
+                            "=",
+                            "Object.Blending()",
+                            ""
+                          ]
+                        }
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D",
+                  "type": "object"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "fullName": "",
+              "functionType": "Action",
+              "name": "doStepPostEvents",
+              "sentence": "",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [
+                    {
+                      "type": {
+                        "value": "ParticleEmitter3D::ParticleEmitter3D::HasEnded"
+                      },
+                      "parameters": [
+                        "Object",
+                        ""
+                      ]
+                    }
+                  ],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "ParticleEmitter3D::ParticleEmitter3D::Delete"
+                      },
+                      "parameters": [
+                        "Object",
+                        ""
+                      ]
+                    }
+                  ]
+                },
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [
+                    {
+                      "type": {
+                        "inverted": true,
+                        "value": "ParticleEmitter3D::ParticleEmitter3D::HasEnded"
+                      },
+                      "parameters": [
+                        "Object",
+                        ""
+                      ]
+                    }
+                  ],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "ParticleEmitter3D::ParticleEmitter3D::RegisterInLayer"
+                      },
+                      "parameters": [
+                        "Object",
+                        ""
+                      ]
+                    }
+                  ],
+                  "events": [
+                    {
+                      "type": "BuiltinCommonInstructions::JsCode",
+                      "inlineCode": [
+                        "/** @type {gdjs.CustomRuntimeObject3D} */",
+                        "const object = objects[0];",
+                        "// Here runtimeScene is the gdjs.CustomRuntimeObjectInstanceContainer inside the custom object.",
+                        "const gameScene = object.getRuntimeScene();",
+                        "",
+                        "// Update batch system after having moved every emitter.",
+                        "// See onScenePreEvents",
+                        "gameScene.__particleEmmiter3DExtension.emittersStepped++;",
+                        "if (gameScene.__particleEmmiter3DExtension.emittersStepped === gameScene.__particleEmmiter3DExtension.emittersCount) {",
+                        "    gameScene.__particleEmmiter3DExtension.layerNames = gameScene.__particleEmmiter3DExtension.layerNames || [];",
+                        "    const layerNames = gameScene.__particleEmmiter3DExtension.layerNames;",
+                        "    gameScene.getAllLayerNames(layerNames);",
+                        "    for (const layerName of layerNames) {",
+                        "        const layer = gameScene.getLayer(layerName);",
+                        "        if (layer.__particleEmmiter3DExtension) {",
+                        "            layer.__particleEmmiter3DExtension.batchSystem.update(object.getElapsedTime() / 1000);",
+                        "        }",
+                        "    }",
+                        "}"
+                      ],
+                      "parameterObjects": "Object",
+                      "useStrict": true,
+                      "eventsSheetExpanded": true
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D",
+                  "type": "object"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "fullName": "Register in layer",
+              "functionType": "Action",
+              "name": "RegisterInLayer",
+              "private": true,
+              "sentence": "Register _PARAM0_ in layer",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::JsCode",
+                  "inlineCode": [
+                    "/** @type {gdjs.CustomRuntimeObject} */",
+                    "const object = objects[0];",
+                    "",
+                    "// TODO Handle layer changes?",
+                    "if (object.__emitterLayerName === undefined) {",
+                    "    // Here runtimeScene is the gdjs.CustomRuntimeObjectInstanceContainer inside the custom object.",
+                    "    const gameScene = object.getRuntimeScene();",
+                    "",
+                    "    const layer = gameScene.getLayer(object.getLayer());",
+                    "    layer.__particleEmmiter3DExtension = layer.__particleEmmiter3DExtension || {};",
+                    "    if (!layer.__particleEmmiter3DExtension.batchSystem) {",
+                    "        const batchSystem = new gdjs.__particleEmmiter3DExtension.BatchedRenderer();",
+                    "        const threeScene = layer.getRenderer().getThreeScene();",
+                    "        if (threeScene) {",
+                    "            threeScene.add(batchSystem);",
+                    "        }",
+                    "        layer.__particleEmmiter3DExtension.batchSystem = batchSystem;",
+                    "    }",
+                    "    layer.__particleEmmiter3DExtension.batchSystem.addSystem(object.__particleSystem);",
+                    "    object.__emitterLayerName = layer.getName();",
+                    "}"
+                  ],
+                  "parameterObjects": "Object",
+                  "useStrict": true,
+                  "eventsSheetExpanded": true
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D",
+                  "type": "object"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "Delete itself",
+              "fullName": "Delete itself",
+              "functionType": "Action",
+              "name": "Delete",
+              "private": true,
+              "sentence": "Delete _PARAM0_",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::JsCode",
+                  "inlineCode": [
+                    "const object = objects[0];",
+                    "",
+                    "object.deleteFromScene(object.getParent());",
+                    ""
+                  ],
+                  "parameterObjects": "Object",
+                  "useStrict": true,
+                  "eventsSheetExpanded": false
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D",
+                  "type": "object"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "Check that emission has ended and no particle is alive anymore.",
+              "fullName": "Emission has ended",
+              "functionType": "Condition",
+              "name": "HasEnded",
+              "sentence": "Emission from _PARAM0_ has ended",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::JsCode",
+                  "inlineCode": [
+                    "const object = objects[0];\r",
+                    "\r",
+                    "eventsFunctionContext.returnValue =\r",
+                    "       object._getShouldAutodestruct()\r",
+                    "    && object.__particleSystem.emitEnded\r",
+                    "    && object.__particleSystem.particleNum === 0;\r",
+                    ""
+                  ],
+                  "parameterObjects": "Object",
+                  "useStrict": true,
+                  "eventsSheetExpanded": true
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D",
+                  "type": "object"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "Restart particule emission from the beginning.",
+              "fullName": "Restart",
+              "functionType": "Action",
+              "name": "Restart",
+              "sentence": "Restart particule emission from _PARAM0_",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::JsCode",
+                  "inlineCode": [
+                    "/** @type {gdjs.CustomRuntimeObject} */\r",
+                    "const object = objects[0];\r",
+                    "\r",
+                    "object.__particleSystem.restart();\r",
+                    ""
+                  ],
+                  "parameterObjects": "Object",
+                  "useStrict": true,
+                  "eventsSheetExpanded": false
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D",
+                  "type": "object"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "the Z position of the emitter.",
+              "fullName": "Z elevaltion (deprecated)",
+              "functionType": "ExpressionAndCondition",
+              "group": "Position",
+              "name": "Z",
+              "private": true,
+              "sentence": "the Z position",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SetReturnNumber"
+                      },
+                      "parameters": [
+                        "Object.PropertyZ()"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "expressionType": {
+                "type": "expression"
+              },
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D",
+                  "type": "object"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "fullName": "",
+              "functionType": "ActionWithOperator",
+              "getterName": "Z",
+              "name": "SetZ",
+              "private": true,
+              "sentence": "",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "ParticleEmitter3D::ParticleEmitter3D::SetPropertyZ"
+                      },
+                      "parameters": [
+                        "Object",
+                        "=",
+                        "Value"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D",
+                  "type": "object"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "the rotation on X axis of the emitter.",
+              "fullName": "Rotation on X axis (deprecated)",
+              "functionType": "ExpressionAndCondition",
+              "group": "Angle",
+              "name": "RotationX",
+              "private": true,
+              "sentence": "the rotation on X axis",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SetReturnNumber"
+                      },
+                      "parameters": [
+                        "Object.PropertyRotationX()"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "expressionType": {
+                "type": "expression"
+              },
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D",
+                  "type": "object"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "fullName": "",
+              "functionType": "ActionWithOperator",
+              "getterName": "RotationX",
+              "name": "SetRotationX",
+              "private": true,
+              "sentence": "",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "ParticleEmitter3D::ParticleEmitter3D::SetPropertyRotationX"
+                      },
+                      "parameters": [
+                        "Object",
+                        "=",
+                        "Value"
+                      ]
+                    }
+                  ]
+                },
+                {
+                  "type": "BuiltinCommonInstructions::JsCode",
+                  "inlineCode": [
+                    "const object = objects[0];\r",
+                    "\r",
+                    "object.__particleSystem.emitter.rotation.x = eventsFunctionContext.getArgument(\"Value\") * Math.PI / 180;"
+                  ],
+                  "parameterObjects": "Object",
+                  "useStrict": true,
+                  "eventsSheetExpanded": false
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D",
+                  "type": "object"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "the rotation on Y axis of the emitter.",
+              "fullName": "Rotation on Y axis (deprecated)",
+              "functionType": "ExpressionAndCondition",
+              "group": "Angle",
+              "name": "RotationY",
+              "private": true,
+              "sentence": "the rotation on Y axis",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SetReturnNumber"
+                      },
+                      "parameters": [
+                        "Object.PropertyRotationY()"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "expressionType": {
+                "type": "expression"
+              },
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D",
+                  "type": "object"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "fullName": "",
+              "functionType": "ActionWithOperator",
+              "getterName": "RotationY",
+              "name": "SetRotationY",
+              "private": true,
+              "sentence": "",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "ParticleEmitter3D::ParticleEmitter3D::SetPropertyRotationY"
+                      },
+                      "parameters": [
+                        "Object",
+                        "=",
+                        "Value"
+                      ]
+                    }
+                  ]
+                },
+                {
+                  "type": "BuiltinCommonInstructions::JsCode",
+                  "inlineCode": [
+                    "const object = objects[0];\r",
+                    "\r",
+                    "object.__particleSystem.emitter.rotation.y = (90 + eventsFunctionContext.getArgument(\"Value\")) * Math.PI / 180;"
+                  ],
+                  "parameterObjects": "Object",
+                  "useStrict": true,
+                  "eventsSheetExpanded": false
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D",
+                  "type": "object"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "the start color of the object.",
+              "fullName": "Start color",
+              "functionType": "ExpressionAndCondition",
+              "group": "3D particle emitter color configuration",
+              "name": "StartColor",
+              "sentence": "the start color",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SetReturnString"
+                      },
+                      "parameters": [
+                        "Object.PropertyStartColor()"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "expressionType": {
+                "type": "color"
+              },
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D",
+                  "type": "object"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "fullName": "",
+              "functionType": "ActionWithOperator",
+              "getterName": "StartColor",
+              "name": "SetStartColor",
+              "sentence": "",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "ParticleEmitter3D::ParticleEmitter3D::SetPropertyStartColor"
+                      },
+                      "parameters": [
+                        "Object",
+                        "=",
+                        "Value"
+                      ]
+                    }
+                  ]
+                },
+                {
+                  "type": "BuiltinCommonInstructions::JsCode",
+                  "inlineCode": [
+                    "const object = objects[0];\r",
+                    "const startColor = eventsFunctionContext.getArgument(\"Value\");\r",
+                    "\r",
+                    "object.__particleEmitterAdapter.setStartColor(startColor);"
+                  ],
+                  "parameterObjects": "Object",
+                  "useStrict": true,
+                  "eventsSheetExpanded": false
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D",
+                  "type": "object"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "the end color of the object.",
+              "fullName": "End color",
+              "functionType": "ExpressionAndCondition",
+              "group": "3D particle emitter color configuration",
+              "name": "EndColor",
+              "sentence": "the end color",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SetReturnString"
+                      },
+                      "parameters": [
+                        "Object.PropertyEndColor()"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "expressionType": {
+                "type": "color"
+              },
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D",
+                  "type": "object"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "fullName": "",
+              "functionType": "ActionWithOperator",
+              "getterName": "EndColor",
+              "name": "SetEndColor",
+              "sentence": "",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "ParticleEmitter3D::ParticleEmitter3D::SetPropertyEndColor"
+                      },
+                      "parameters": [
+                        "Object",
+                        "=",
+                        "Value"
+                      ]
+                    }
+                  ]
+                },
+                {
+                  "type": "BuiltinCommonInstructions::JsCode",
+                  "inlineCode": [
+                    "const object = objects[0];\r",
+                    "const endColor = eventsFunctionContext.getArgument(\"Value\");\r",
+                    "\r",
+                    "object.__particleEmitterAdapter.setEndColor(endColor);"
+                  ],
+                  "parameterObjects": "Object",
+                  "useStrict": true,
+                  "eventsSheetExpanded": false
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D",
+                  "type": "object"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "the start opacity of the object.",
+              "fullName": "Start opacity",
+              "functionType": "ExpressionAndCondition",
+              "group": "3D particle emitter color configuration",
+              "name": "StartOpacity",
+              "sentence": "the start opacity",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SetReturnNumber"
+                      },
+                      "parameters": [
+                        "Object.PropertyStartOpacity()"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "expressionType": {
+                "type": "expression"
+              },
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D",
+                  "type": "object"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "fullName": "",
+              "functionType": "ActionWithOperator",
+              "getterName": "StartOpacity",
+              "name": "SetStartOpacity",
+              "sentence": "",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "ParticleEmitter3D::ParticleEmitter3D::SetPropertyStartOpacity"
+                      },
+                      "parameters": [
+                        "Object",
+                        "=",
+                        "Value"
+                      ]
+                    }
+                  ]
+                },
+                {
+                  "type": "BuiltinCommonInstructions::JsCode",
+                  "inlineCode": [
+                    "const object = objects[0];\r",
+                    "const startOpacity = eventsFunctionContext.getArgument(\"Value\");\r",
+                    "\r",
+                    "object.__particleEmitterAdapter.setStartOpacity(startOpacity);"
+                  ],
+                  "parameterObjects": "Object",
+                  "useStrict": true,
+                  "eventsSheetExpanded": false
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D",
+                  "type": "object"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "the end opacity of the object.",
+              "fullName": "End opacity",
+              "functionType": "ExpressionAndCondition",
+              "group": "3D particle emitter color configuration",
+              "name": "EndOpacity",
+              "sentence": "the end opacity",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SetReturnNumber"
+                      },
+                      "parameters": [
+                        "Object.PropertyEndOpacity()"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "expressionType": {
+                "type": "expression"
+              },
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D",
+                  "type": "object"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "fullName": "",
+              "functionType": "ActionWithOperator",
+              "getterName": "EndOpacity",
+              "name": "SetEndOpacity",
+              "sentence": "",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "ParticleEmitter3D::ParticleEmitter3D::SetPropertyEndOpacity"
+                      },
+                      "parameters": [
+                        "Object",
+                        "=",
+                        "Value"
+                      ]
+                    }
+                  ]
+                },
+                {
+                  "type": "BuiltinCommonInstructions::JsCode",
+                  "inlineCode": [
+                    "const object = objects[0];\r",
+                    "const endOpacity = eventsFunctionContext.getArgument(\"Value\");\r",
+                    "\r",
+                    "object.__particleEmitterAdapter.setEndOpacity(endOpacity);"
+                  ],
+                  "parameterObjects": "Object",
+                  "useStrict": true,
+                  "eventsSheetExpanded": false
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D",
+                  "type": "object"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "the flow of particles of the object  (particles per second).",
+              "fullName": "Flow of particles",
+              "functionType": "ExpressionAndCondition",
+              "group": "3D particle emitter configuration",
+              "name": "Flow",
+              "sentence": "the flow of particles",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SetReturnNumber"
+                      },
+                      "parameters": [
+                        "Object.PropertyFlow()"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "expressionType": {
+                "type": "expression"
+              },
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D",
+                  "type": "object"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "fullName": "",
+              "functionType": "ActionWithOperator",
+              "getterName": "Flow",
+              "name": "SetFlow",
+              "sentence": "",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "ParticleEmitter3D::ParticleEmitter3D::SetPropertyFlow"
+                      },
+                      "parameters": [
+                        "Object",
+                        "=",
+                        "Value"
+                      ]
+                    }
+                  ]
+                },
+                {
+                  "type": "BuiltinCommonInstructions::JsCode",
+                  "inlineCode": [
+                    "const object = objects[0];\r",
+                    "const flow = eventsFunctionContext.getArgument(\"Value\");\r",
+                    "\r",
+                    "object.__particleEmitterAdapter.setFlow(flow);\r",
+                    ""
+                  ],
+                  "parameterObjects": "Object",
+                  "useStrict": true,
+                  "eventsSheetExpanded": false
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D",
+                  "type": "object"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "the start min size of the object.",
+              "fullName": "Start min size",
+              "functionType": "ExpressionAndCondition",
+              "group": "3D particle emitter configuration",
+              "name": "StartSizeMin",
+              "sentence": "the start min size",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SetReturnNumber"
+                      },
+                      "parameters": [
+                        "Object.PropertyStartSizeMin()"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "expressionType": {
+                "type": "expression"
+              },
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D",
+                  "type": "object"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "fullName": "",
+              "functionType": "ActionWithOperator",
+              "getterName": "StartSizeMin",
+              "name": "SetStartSizeMin",
+              "sentence": "",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "ParticleEmitter3D::ParticleEmitter3D::SetPropertyStartSizeMin"
+                      },
+                      "parameters": [
+                        "Object",
+                        "=",
+                        "Value"
+                      ]
+                    }
+                  ]
+                },
+                {
+                  "type": "BuiltinCommonInstructions::JsCode",
+                  "inlineCode": [
+                    "const object = objects[0];\r",
+                    "const startMinSize = eventsFunctionContext.getArgument(\"Value\");\r",
+                    "\r",
+                    "object.__particleEmitterAdapter.setStartMinSize(startMinSize);"
+                  ],
+                  "parameterObjects": "Object",
+                  "useStrict": true,
+                  "eventsSheetExpanded": false
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D",
+                  "type": "object"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "the start max size of the object.",
+              "fullName": "Start max size",
+              "functionType": "ExpressionAndCondition",
+              "group": "3D particle emitter configuration",
+              "name": "StartSizeMax",
+              "sentence": "the start max size",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SetReturnNumber"
+                      },
+                      "parameters": [
+                        "Object.PropertyStartSizeMax()"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "expressionType": {
+                "type": "expression"
+              },
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D",
+                  "type": "object"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "fullName": "",
+              "functionType": "ActionWithOperator",
+              "getterName": "StartSizeMax",
+              "name": "SetStartSizeMax",
+              "sentence": "",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "ParticleEmitter3D::ParticleEmitter3D::SetPropertyStartSizeMax"
+                      },
+                      "parameters": [
+                        "Object",
+                        "=",
+                        "Value"
+                      ]
+                    }
+                  ]
+                },
+                {
+                  "type": "BuiltinCommonInstructions::JsCode",
+                  "inlineCode": [
+                    "const object = objects[0];\r",
+                    "const startMaxSize = eventsFunctionContext.getArgument(\"Value\");\r",
+                    "\r",
+                    "object.__particleEmitterAdapter.setStartMaxSize(startMaxSize);\r",
+                    ""
+                  ],
+                  "parameterObjects": "Object",
+                  "useStrict": true,
+                  "eventsSheetExpanded": false
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D",
+                  "type": "object"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "the end scale of the object.",
+              "fullName": "End scale",
+              "functionType": "ExpressionAndCondition",
+              "group": "3D particle emitter configuration",
+              "name": "EndScale",
+              "sentence": "the end scale",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SetReturnNumber"
+                      },
+                      "parameters": [
+                        "Object.PropertyEndScale()"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "expressionType": {
+                "type": "expression"
+              },
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D",
+                  "type": "object"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "fullName": "",
+              "functionType": "ActionWithOperator",
+              "getterName": "EndScale",
+              "name": "SetEndScale",
+              "sentence": "",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "ParticleEmitter3D::ParticleEmitter3D::SetPropertyEndScale"
+                      },
+                      "parameters": [
+                        "Object",
+                        "=",
+                        "Value"
+                      ]
+                    }
+                  ]
+                },
+                {
+                  "type": "BuiltinCommonInstructions::JsCode",
+                  "inlineCode": [
+                    "const object = objects[0];\r",
+                    "const endScale = eventsFunctionContext.getArgument(\"Value\");\r",
+                    "\r",
+                    "object.__particleEmitterAdapter.setEndScale(endScale);\r",
+                    ""
+                  ],
+                  "parameterObjects": "Object",
+                  "useStrict": true,
+                  "eventsSheetExpanded": false
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D",
+                  "type": "object"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "the min start speed of the object.",
+              "fullName": "Min start speed",
+              "functionType": "ExpressionAndCondition",
+              "group": "3D particle emitter speed configuration",
+              "name": "StartSpeedMin",
+              "sentence": "the min start speed",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SetReturnNumber"
+                      },
+                      "parameters": [
+                        "Object.PropertyStartSpeedMin()"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "expressionType": {
+                "type": "expression"
+              },
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D",
+                  "type": "object"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "fullName": "",
+              "functionType": "ActionWithOperator",
+              "getterName": "StartSpeedMin",
+              "name": "SetStartSpeedMin",
+              "sentence": "",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "ParticleEmitter3D::ParticleEmitter3D::SetPropertyStartSpeedMin"
+                      },
+                      "parameters": [
+                        "Object",
+                        "=",
+                        "Value"
+                      ]
+                    }
+                  ]
+                },
+                {
+                  "type": "BuiltinCommonInstructions::JsCode",
+                  "inlineCode": [
+                    "const object = objects[0];\r",
+                    "const startSpeedMin = eventsFunctionContext.getArgument(\"Value\");\r",
+                    "\r",
+                    "object.__particleEmitterAdapter.setStartSpeedMin(startSpeedMin);"
+                  ],
+                  "parameterObjects": "Object",
+                  "useStrict": true,
+                  "eventsSheetExpanded": false
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D",
+                  "type": "object"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "the max start speed of the object.",
+              "fullName": "Max start speed",
+              "functionType": "ExpressionAndCondition",
+              "group": "3D particle emitter speed configuration",
+              "name": "StartSpeedMax",
+              "sentence": "the max start speed",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SetReturnNumber"
+                      },
+                      "parameters": [
+                        "Object.PropertyStartSpeedMax()"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "expressionType": {
+                "type": "expression"
+              },
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D",
+                  "type": "object"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "fullName": "",
+              "functionType": "ActionWithOperator",
+              "getterName": "StartSpeedMax",
+              "name": "SetStartSpeedMax",
+              "sentence": "",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "ParticleEmitter3D::ParticleEmitter3D::SetPropertyStartSpeedMax"
+                      },
+                      "parameters": [
+                        "Object",
+                        "=",
+                        "Value"
+                      ]
+                    }
+                  ]
+                },
+                {
+                  "type": "BuiltinCommonInstructions::JsCode",
+                  "inlineCode": [
+                    "const object = objects[0];\r",
+                    "const startSpeedMax = eventsFunctionContext.getArgument(\"Value\");\r",
+                    "\r",
+                    "object.__particleEmitterAdapter.setStartSpeedMax(startSpeedMax);"
+                  ],
+                  "parameterObjects": "Object",
+                  "useStrict": true,
+                  "eventsSheetExpanded": false
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D",
+                  "type": "object"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "the min lifespan of the object.",
+              "fullName": "Min lifespan",
+              "functionType": "ExpressionAndCondition",
+              "group": "3D particle emitter configuration",
+              "name": "LifespanMin",
+              "sentence": "the min lifespan",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SetReturnNumber"
+                      },
+                      "parameters": [
+                        "Object.PropertyLifespanMin()"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "expressionType": {
+                "type": "expression"
+              },
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D",
+                  "type": "object"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "fullName": "",
+              "functionType": "ActionWithOperator",
+              "getterName": "LifespanMin",
+              "name": "SetLifespanMin",
+              "sentence": "",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "ParticleEmitter3D::ParticleEmitter3D::SetPropertyLifespanMin"
+                      },
+                      "parameters": [
+                        "Object",
+                        "=",
+                        "Value"
+                      ]
+                    }
+                  ]
+                },
+                {
+                  "type": "BuiltinCommonInstructions::JsCode",
+                  "inlineCode": [
+                    "const object = objects[0];\r",
+                    "const lifespanMin = eventsFunctionContext.getArgument(\"Value\");\r",
+                    "\r",
+                    "object.__particleEmitterAdapter.setLifespanMin(lifespanMin);\r",
+                    ""
+                  ],
+                  "parameterObjects": "Object",
+                  "useStrict": true,
+                  "eventsSheetExpanded": false
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D",
+                  "type": "object"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "the max lifespan of the object.",
+              "fullName": "Max lifespan",
+              "functionType": "ExpressionAndCondition",
+              "group": "3D particle emitter configuration",
+              "name": "LifespanMax",
+              "sentence": "the max lifespan",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SetReturnNumber"
+                      },
+                      "parameters": [
+                        "Object.PropertyLifespanMax()"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "expressionType": {
+                "type": "expression"
+              },
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D",
+                  "type": "object"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "fullName": "",
+              "functionType": "ActionWithOperator",
+              "getterName": "LifespanMax",
+              "name": "SetLifespanMax",
+              "sentence": "",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "ParticleEmitter3D::ParticleEmitter3D::SetPropertyLifespanMax"
+                      },
+                      "parameters": [
+                        "Object",
+                        "=",
+                        "Value"
+                      ]
+                    }
+                  ]
+                },
+                {
+                  "type": "BuiltinCommonInstructions::JsCode",
+                  "inlineCode": [
+                    "const object = objects[0];\r",
+                    "const lifespanMax = eventsFunctionContext.getArgument(\"Value\");\r",
+                    "\r",
+                    "object.__particleEmitterAdapter.setLifespanMax(lifespanMax);\r",
+                    ""
+                  ],
+                  "parameterObjects": "Object",
+                  "useStrict": true,
+                  "eventsSheetExpanded": false
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D",
+                  "type": "object"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "the emission duration of the object.",
+              "fullName": "Emission duration",
+              "functionType": "ExpressionAndCondition",
+              "group": "3D particle emitter configuration",
+              "name": "Duration",
+              "sentence": "the emission duration",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SetReturnNumber"
+                      },
+                      "parameters": [
+                        "Object.PropertyDuration()"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "expressionType": {
+                "type": "expression"
+              },
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D",
+                  "type": "object"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "fullName": "",
+              "functionType": "ActionWithOperator",
+              "getterName": "Duration",
+              "name": "SetDuration",
+              "sentence": "",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "ParticleEmitter3D::ParticleEmitter3D::SetPropertyDuration"
+                      },
+                      "parameters": [
+                        "Object",
+                        "=",
+                        "Value"
+                      ]
+                    }
+                  ]
+                },
+                {
+                  "type": "BuiltinCommonInstructions::JsCode",
+                  "inlineCode": [
+                    "const object = objects[0];\r",
+                    "const duration = eventsFunctionContext.getArgument(\"Value\");\r",
+                    "\r",
+                    "object.__particleEmitterAdapter.setDuration(duration);\r",
+                    ""
+                  ],
+                  "parameterObjects": "Object",
+                  "useStrict": true,
+                  "eventsSheetExpanded": false
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D",
+                  "type": "object"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "Check if particles move with the emitter.",
+              "fullName": "Particles move with the emitter",
+              "functionType": "Condition",
+              "group": "3D particle emitter speed configuration",
+              "name": "AreParticlesRelative",
+              "sentence": "_PARAM0_ particles move with the emitter",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [
+                    {
+                      "type": {
+                        "value": "ParticleEmitter3D::ParticleEmitter3D::PropertyAreParticlesRelative"
+                      },
+                      "parameters": [
+                        "Object"
+                      ]
+                    }
+                  ],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SetReturnBoolean"
+                      },
+                      "parameters": [
+                        "True"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D",
+                  "type": "object"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "Change if particles move with the emitter.",
+              "fullName": "Particles move with the emitter",
+              "functionType": "Action",
+              "group": "3D particle emitter speed configuration",
+              "name": "SetAreParticlesRelative",
+              "sentence": "_PARAM0_ particles move with the emitter: _PARAM1_",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [
+                    {
+                      "type": {
+                        "inverted": true,
+                        "value": "GetArgumentAsBoolean"
+                      },
+                      "parameters": [
+                        "\"Value\""
+                      ]
+                    }
+                  ],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "ParticleEmitter3D::ParticleEmitter3D::SetPropertyAreParticlesRelative"
+                      },
+                      "parameters": [
+                        "Object",
+                        "no"
+                      ]
+                    }
+                  ]
+                },
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [
+                    {
+                      "type": {
+                        "value": "GetArgumentAsBoolean"
+                      },
+                      "parameters": [
+                        "\"Value\""
+                      ]
+                    }
+                  ],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "ParticleEmitter3D::ParticleEmitter3D::SetPropertyAreParticlesRelative"
+                      },
+                      "parameters": [
+                        "Object",
+                        "yes"
+                      ]
+                    }
+                  ]
+                },
+                {
+                  "type": "BuiltinCommonInstructions::JsCode",
+                  "inlineCode": [
+                    "const object = objects[0];\r",
+                    "const areParticlesRelative = eventsFunctionContext.getArgument(\"Value\");\r",
+                    "\r",
+                    "object.__particleEmitterAdapter.setParticlesRelative(areParticlesRelative);\r",
+                    ""
+                  ],
+                  "parameterObjects": "Object",
+                  "useStrict": true,
+                  "eventsSheetExpanded": false
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D",
+                  "type": "object"
+                },
+                {
+                  "defaultValue": "yes",
+                  "description": "AreParticlesRelative",
+                  "name": "Value",
+                  "optional": true,
+                  "type": "yesorno"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "the spay cone angle of the object.",
+              "fullName": "Spay cone angle",
+              "functionType": "ExpressionAndCondition",
+              "group": "3D particle emitter configuration",
+              "name": "SpayConeAngle",
+              "sentence": "the spay cone angle",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SetReturnNumber"
+                      },
+                      "parameters": [
+                        "Object.PropertySpayConeAngle()"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "expressionType": {
+                "type": "expression"
+              },
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D",
+                  "type": "object"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "fullName": "",
+              "functionType": "ActionWithOperator",
+              "getterName": "SpayConeAngle",
+              "name": "SetSpayConeAngle",
+              "sentence": "",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "ParticleEmitter3D::ParticleEmitter3D::SetPropertySpayConeAngle"
+                      },
+                      "parameters": [
+                        "Object",
+                        "=",
+                        "Value"
+                      ]
+                    }
+                  ]
+                },
+                {
+                  "type": "BuiltinCommonInstructions::JsCode",
+                  "inlineCode": [
+                    "const object = objects[0];\r",
+                    "const sprayConeAngle = eventsFunctionContext.getArgument(\"Value\");\r",
+                    "\r",
+                    "object.__particleEmitterAdapter.setSprayConeAngle(sprayConeAngle);\r",
+                    ""
+                  ],
+                  "parameterObjects": "Object",
+                  "useStrict": true,
+                  "eventsSheetExpanded": true
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D",
+                  "type": "object"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "the blending of the object.",
+              "fullName": "Blending",
+              "functionType": "ExpressionAndCondition",
+              "group": "3D particle emitter color configuration",
+              "name": "Blending",
+              "sentence": "the blending",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SetReturnString"
+                      },
+                      "parameters": [
+                        "Object.PropertyBlending()"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "expressionType": {
+                "supplementaryInformation": "[\"Normal\",\"Additive\",\"Substractive\",\"Multiply\",\"None\"]",
+                "type": "stringWithSelector"
+              },
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D",
+                  "type": "object"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "fullName": "",
+              "functionType": "ActionWithOperator",
+              "getterName": "Blending",
+              "name": "SetBlending",
+              "private": true,
+              "sentence": "",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "ParticleEmitter3D::ParticleEmitter3D::SetPropertyBlending"
+                      },
+                      "parameters": [
+                        "Object",
+                        "=",
+                        "Value"
+                      ]
+                    }
+                  ]
+                },
+                {
+                  "type": "BuiltinCommonInstructions::JsCode",
+                  "inlineCode": [
+                    "const object = objects[0];\r",
+                    "const blending = eventsFunctionContext.getArgument(\"Value\");\r",
+                    "\r",
+                    "object.__particleEmitterAdapter.setBlending(blending);\r",
+                    ""
+                  ],
+                  "parameterObjects": "Object",
+                  "useStrict": true,
+                  "eventsSheetExpanded": true
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D",
+                  "type": "object"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "the gravity top of the object.",
+              "fullName": "Gravity top",
+              "functionType": "ExpressionAndCondition",
+              "group": "3D particle emitter speed configuration",
+              "name": "GravityTop",
+              "sentence": "the gravity top",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SetReturnString"
+                      },
+                      "parameters": [
+                        "Object.PropertyGravityTop()"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "expressionType": {
+                "supplementaryInformation": "[\"Y-\",\"Z+\"]",
+                "type": "stringWithSelector"
+              },
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D",
+                  "type": "object"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "fullName": "",
+              "functionType": "ActionWithOperator",
+              "getterName": "GravityTop",
+              "name": "SetGravityTop",
+              "sentence": "",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "ParticleEmitter3D::ParticleEmitter3D::SetPropertyGravityTop"
+                      },
+                      "parameters": [
+                        "Object",
+                        "=",
+                        "Value"
+                      ]
+                    }
+                  ]
+                },
+                {
+                  "type": "BuiltinCommonInstructions::JsCode",
+                  "inlineCode": [
+                    "const object = objects[0];\r",
+                    "const gravityTop = eventsFunctionContext.getArgument(\"Value\");\r",
+                    "\r",
+                    "object.__particleEmitterAdapter.setGravityTop(gravityTop);\r",
+                    ""
+                  ],
+                  "parameterObjects": "Object",
+                  "useStrict": true,
+                  "eventsSheetExpanded": true
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D",
+                  "type": "object"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "the gravity of the object.",
+              "fullName": "Gravity",
+              "functionType": "ExpressionAndCondition",
+              "group": "3D particle emitter speed configuration",
+              "name": "Gravity",
+              "sentence": "the gravity",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SetReturnNumber"
+                      },
+                      "parameters": [
+                        "Object.PropertyGravity()"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "expressionType": {
+                "type": "expression"
+              },
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D",
+                  "type": "object"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "fullName": "",
+              "functionType": "ActionWithOperator",
+              "getterName": "Gravity",
+              "name": "SetGravity",
+              "sentence": "",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "ParticleEmitter3D::ParticleEmitter3D::SetPropertyGravity"
+                      },
+                      "parameters": [
+                        "Object",
+                        "=",
+                        "Value"
+                      ]
+                    }
+                  ]
+                },
+                {
+                  "type": "BuiltinCommonInstructions::JsCode",
+                  "inlineCode": [
+                    "const object = objects[0];\r",
+                    "const gravity = eventsFunctionContext.getArgument(\"Value\");\r",
+                    "\r",
+                    "object.__particleEmitterAdapter.setGravity(gravity);\r",
+                    ""
+                  ],
+                  "parameterObjects": "Object",
+                  "useStrict": true,
+                  "eventsSheetExpanded": true
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D",
+                  "type": "object"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "Check if delete when emission ends.",
+              "fullName": "Delete when emission ends",
+              "functionType": "Condition",
+              "group": "3D particle emitter configuration",
+              "name": "ShouldAutodestruct",
+              "sentence": "_PARAM0_ delete when emission ends",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [
+                    {
+                      "type": {
+                        "value": "ParticleEmitter3D::ParticleEmitter3D::PropertyShouldAutodestruct"
+                      },
+                      "parameters": [
+                        "Object"
+                      ]
+                    }
+                  ],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SetReturnBoolean"
+                      },
+                      "parameters": [
+                        "True"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D",
+                  "type": "object"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "Change if delete when emission ends.",
+              "fullName": "Delete when emission ends",
+              "functionType": "Action",
+              "group": "3D particle emitter configuration",
+              "name": "SetShouldAutodestruct",
+              "sentence": "_PARAM0_ delete when emission ends: _PARAM1_",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [
+                    {
+                      "type": {
+                        "inverted": true,
+                        "value": "GetArgumentAsBoolean"
+                      },
+                      "parameters": [
+                        "\"Value\""
+                      ]
+                    }
+                  ],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "ParticleEmitter3D::ParticleEmitter3D::SetPropertyShouldAutodestruct"
+                      },
+                      "parameters": [
+                        "Object",
+                        "no"
+                      ]
+                    }
+                  ]
+                },
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [
+                    {
+                      "type": {
+                        "value": "GetArgumentAsBoolean"
+                      },
+                      "parameters": [
+                        "\"Value\""
+                      ]
+                    }
+                  ],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "ParticleEmitter3D::ParticleEmitter3D::SetPropertyShouldAutodestruct"
+                      },
+                      "parameters": [
+                        "Object",
+                        "yes"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "supplementaryInformation": "ParticleEmitter3D::ParticleEmitter3D",
+                  "type": "object"
+                },
+                {
+                  "defaultValue": "yes",
+                  "description": "ShouldAutodestruct",
+                  "name": "Value",
+                  "optional": true,
+                  "type": "yesorno"
+                }
+              ],
+              "objectGroups": []
+            }
+          ],
+          "propertyDescriptors": [
+            {
+              "value": "255;0;0",
+              "type": "Color",
+              "label": "Start color",
+              "description": "",
+              "group": "Color",
+              "extraInformation": [],
+              "name": "StartColor"
+            },
+            {
+              "value": "255;255;0",
+              "type": "Color",
+              "label": "End color",
+              "description": "",
+              "group": "Color",
+              "extraInformation": [],
+              "name": "EndColor"
+            },
+            {
+              "value": "255",
+              "type": "Number",
+              "unit": "Dimensionless",
+              "label": "Start opacity",
+              "description": "",
+              "group": "Color",
+              "extraInformation": [],
+              "name": "StartOpacity"
+            },
+            {
+              "value": "0",
+              "type": "Number",
+              "unit": "Dimensionless",
+              "label": "End opacity",
+              "description": "",
+              "group": "Color",
+              "extraInformation": [],
+              "name": "EndOpacity"
+            },
+            {
+              "value": "50",
+              "type": "Number",
+              "label": "Flow of particles (particles per second)",
+              "description": "",
+              "group": "",
+              "extraInformation": [],
+              "name": "Flow"
+            },
+            {
+              "value": "10",
+              "type": "Number",
+              "unit": "Pixel",
+              "label": "Start min size",
+              "description": "",
+              "group": "Size",
+              "extraInformation": [],
+              "name": "StartSizeMin"
+            },
+            {
+              "value": "20",
+              "type": "Number",
+              "unit": "Pixel",
+              "label": "Start max size",
+              "description": "",
+              "group": "Size",
+              "extraInformation": [],
+              "name": "StartSizeMax"
+            },
+            {
+              "value": "0",
+              "type": "Number",
+              "unit": "Dimensionless",
+              "label": "End scale",
+              "description": "",
+              "group": "Size",
+              "extraInformation": [],
+              "name": "EndScale"
+            },
+            {
+              "value": "100",
+              "type": "Number",
+              "unit": "PixelSpeed",
+              "label": "Start min speed",
+              "description": "",
+              "group": "Speed",
+              "extraInformation": [],
+              "name": "StartSpeedMin"
+            },
+            {
+              "value": "100",
+              "type": "Number",
+              "unit": "PixelSpeed",
+              "label": "Start max speed",
+              "description": "",
+              "group": "Speed",
+              "extraInformation": [],
+              "name": "StartSpeedMax"
+            },
+            {
+              "value": "1",
+              "type": "Number",
+              "unit": "Second",
+              "label": "Min lifespan",
+              "description": "",
+              "group": "",
+              "extraInformation": [],
+              "name": "LifespanMin"
+            },
+            {
+              "value": "2",
+              "type": "Number",
+              "unit": "Second",
+              "label": "Max lifespan",
+              "description": "",
+              "group": "",
+              "extraInformation": [],
+              "name": "LifespanMax"
+            },
+            {
+              "value": "5",
+              "type": "Number",
+              "unit": "Second",
+              "label": "Emission duration",
+              "description": "",
+              "group": "",
+              "extraInformation": [],
+              "name": "Duration"
+            },
+            {
+              "value": "",
+              "type": "Boolean",
+              "label": "Particles move with the emitter",
+              "description": "",
+              "group": "Speed",
+              "extraInformation": [],
+              "name": "AreParticlesRelative"
+            },
+            {
+              "value": "30",
+              "type": "Number",
+              "unit": "DegreeAngle",
+              "label": "Spay cone angle",
+              "description": "",
+              "group": "Position",
+              "extraInformation": [],
+              "name": "SpayConeAngle"
+            },
+            {
+              "value": "Additive",
+              "type": "Choice",
+              "label": "Blending",
+              "description": "",
+              "group": "Color",
+              "extraInformation": [
+                "Normal",
+                "Additive",
+                "Subtractive",
+                "Multiply",
+                "None"
+              ],
+              "name": "Blending"
+            },
+            {
+              "value": "Y-",
+              "type": "Choice",
+              "label": "Gravity top",
+              "description": "",
+              "group": "Speed",
+              "extraInformation": [
+                "Y-",
+                "Z+"
+              ],
+              "name": "GravityTop"
+            },
+            {
+              "value": "0",
+              "type": "Number",
+              "unit": "PixelAcceleration",
+              "label": "Gravity",
+              "description": "",
+              "group": "Speed",
+              "extraInformation": [],
+              "name": "Gravity"
+            },
+            {
+              "value": "true",
+              "type": "Boolean",
+              "label": "Delete when emission ends",
+              "description": "",
+              "group": "",
+              "extraInformation": [],
+              "name": "ShouldAutodestruct"
+            },
+            {
+              "value": "Center-center",
+              "type": "String",
+              "label": "",
+              "description": "Only used by the scene editor.",
+              "group": "",
+              "extraInformation": [],
+              "hidden": true,
+              "name": "ParentOrigin"
+            },
+            {
+              "value": "0",
+              "type": "Number",
+              "unit": "Pixel",
+              "label": "Z (elevation)",
+              "description": "Deprecated",
+              "group": "Position",
+              "extraInformation": [],
+              "hidden": true,
+              "name": "Z"
+            },
+            {
+              "value": "0",
+              "type": "Number",
+              "unit": "DegreeAngle",
+              "label": "Rotation on X axis",
+              "description": "",
+              "group": "Position",
+              "extraInformation": [],
+              "hidden": true,
+              "name": "RotationX"
+            },
+            {
+              "value": "0",
+              "type": "Number",
+              "unit": "DegreeAngle",
+              "label": "Rotation on Y axis",
+              "description": "",
+              "group": "Position",
+              "extraInformation": [],
+              "hidden": true,
+              "name": "RotationY"
+            }
+          ],
+          "objects": [
+            {
+              "adaptCollisionMaskAutomatically": true,
+              "assetStoreId": "",
+              "name": "Particle",
+              "type": "Sprite",
+              "updateIfNotVisible": false,
+              "variables": [],
+              "effects": [],
+              "behaviors": [],
+              "animations": [
+                {
+                  "name": "Image",
+                  "useMultipleDirections": false,
+                  "directions": [
+                    {
+                      "looping": false,
+                      "timeBetweenFrames": 0.08,
+                      "sprites": []
+                    }
+                  ]
+                }
+              ]
+            }
+          ],
+          "objectsFolderStructure": {
+            "folderName": "__ROOT",
+            "children": [
+              {
+                "objectName": "Particle"
+              }
+            ]
+          },
+          "objectsGroups": [],
+          "layers": [
+            {
+              "ambientLightColorB": 200,
+              "ambientLightColorG": 200,
+              "ambientLightColorR": 200,
+              "camera3DFarPlaneDistance": 10000,
+              "camera3DFieldOfView": 45,
+              "camera3DNearPlaneDistance": 3,
+              "cameraType": "",
+              "followBaseLayerCamera": false,
+              "isLightingLayer": false,
+              "isLocked": false,
+              "name": "",
+              "renderingType": "",
+              "visibility": true,
+              "cameras": [
+                {
+                  "defaultSize": true,
+                  "defaultViewport": true,
+                  "height": 0,
+                  "viewportBottom": 1,
+                  "viewportLeft": 0,
+                  "viewportRight": 1,
+                  "viewportTop": 0,
+                  "width": 0
+                }
+              ],
+              "effects": []
+            }
+          ],
+          "instances": []
+        }
+      ]
+    },
+    {
+      "author": "@Bouh",
+      "category": "User interface",
+      "extensionNamespace": "",
+      "fullName": "Time formatting",
+      "helpPath": "",
+      "iconUrl": "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz48IURPQ1RZUEUgc3ZnIFBVQkxJQyAiLS8vVzNDLy9EVEQgU1ZHIDEuMS8vRU4iICJodHRwOi8vd3d3LnczLm9yZy9HcmFwaGljcy9TVkcvMS4xL0RURC9zdmcxMS5kdGQiPjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgdmVyc2lvbj0iMS4xIiBpZD0ibWRpLWNsb2NrLWRpZ2l0YWwiIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgdmlld0JveD0iMCAwIDI0IDI0Ij48cGF0aCBkPSJNMiw2QTIsMiAwIDAsMCAwLDhWMTZBMiwyIDAgMCwwIDIsMThIMjJBMiwyIDAgMCwwIDI0LDE2VjhBMiwyIDAgMCwwIDIyLDZNMiw4SDIyVjE2SDJNMyw5VjEwLjVINi4yNUwzLDE1SDQuNzVMOCwxMC41VjlNOS4yNSw5VjEwLjVIMTAuNzVWOU0xMiw5VjEwLjVIMTMuNVYxNUgxNVY5TTE3LDlBMSwxIDAgMCwwIDE2LDEwVjE0QTEsMSAwIDAsMCAxNywxNUgyMEExLDEgMCAwLDAgMjEsMTRWMTBBMSwxIDAgMCwwIDIwLDlNMTcuNSwxMC41SDE5LjVWMTMuNUgxNy41TTkuMjUsMTMuNVYxNUgxMC43NVYxMy41IiAvPjwvc3ZnPg==",
+      "name": "TimeFormatter",
+      "previewIconUrl": "https://resources.gdevelop-app.com/assets/Icons/clock-digital.svg",
+      "shortDescription": "Converts seconds into standard time formats, such as HH:MM:SS. ",
+      "version": "0.0.2",
+      "description": [
+        "Ideal for displaying timers on screen.",
+        "",
+        "Formats included:",
+        "* HH:MM:SS",
+        "* HH:MM:SS.000 (displays milliseconds)"
+      ],
+      "origin": {
+        "identifier": "TimeFormatter",
+        "name": "gdevelop-extension-store"
+      },
+      "tags": [
+        "time",
+        "timer",
+        "format",
+        "hours",
+        "minutes",
+        "seconds",
+        "milliseconds"
+      ],
+      "authorIds": [
+        "2OwwM8ToR9dx9RJ2sAKTcrLmCB92"
+      ],
+      "dependencies": [],
+      "globalVariables": [],
+      "sceneVariables": [],
+      "eventsFunctions": [
+        {
+          "description": "Format time in seconds to HH:MM:SS.",
+          "fullName": "Format time in seconds to HH:MM:SS",
+          "functionType": "StringExpression",
+          "name": "SecondsToHHMMSS",
+          "sentence": "Format time _PARAM1_ to HH:MM:SS in _PARAM2_",
+          "events": [
+            {
+              "type": "BuiltinCommonInstructions::JsCode",
+              "inlineCode": [
+                "var format_time = function (time_second) {\r",
+                "    date = new Date(null);\r",
+                "    date.setSeconds(time_second);\r",
+                "    if (time_second >= 3600) {\r",
+                "        return date.toISOString().substr(11, 8); // MM:SS\r",
+                "    } else {\r",
+                "        return date.toISOString().substr(14, 5); // HH:MM:SS\r",
+                "    }\r",
+                "}\r",
+                "\r",
+                "eventsFunctionContext.returnValue = format_time(eventsFunctionContext.getArgument(\"TimeInSeconds\"));"
+              ],
+              "parameterObjects": "",
+              "useStrict": false,
+              "eventsSheetExpanded": false
+            }
+          ],
+          "expressionType": {
+            "type": "string"
+          },
+          "parameters": [
+            {
+              "description": "Time, in seconds",
+              "name": "TimeInSeconds",
+              "type": "expression"
+            }
+          ],
+          "objectGroups": []
+        },
+        {
+          "description": "Format time in seconds to HH:MM:SS.000, including milliseconds.",
+          "fullName": "Format time in seconds to HH:MM:SS.000",
+          "functionType": "StringExpression",
+          "name": "SecondsToHHMMSS000",
+          "sentence": "Format time _PARAM1_ to HH:MM:SS in _PARAM2_",
+          "events": [
+            {
+              "type": "BuiltinCommonInstructions::JsCode",
+              "inlineCode": [
+                "var format_time = function (time_second) {\r",
+                "    date = new Date(null);\r",
+                "    date.setMilliseconds(1000*time_second);\r",
+                "    if (time_second >= 3600) {\r",
+                "        return date.toISOString().substr(11, 12); // MM:SS.000\r",
+                "    } else {\r",
+                "        return date.toISOString().substr(14, 9); // HH:MM:SS.000\r",
+                "    }\r",
+                "}\r",
+                "\r",
+                "eventsFunctionContext.returnValue = format_time(eventsFunctionContext.getArgument(\"TimeInSeconds\"));"
+              ],
+              "parameterObjects": "",
+              "useStrict": false,
+              "eventsSheetExpanded": false
+            }
+          ],
+          "expressionType": {
+            "type": "string"
+          },
+          "parameters": [
+            {
+              "description": "Time, in seconds",
+              "name": "TimeInSeconds",
+              "type": "expression"
+            }
+          ],
+          "objectGroups": []
+        }
+      ],
+      "eventsBasedBehaviors": [],
+      "eventsBasedObjects": []
+    },
+    {
+      "author": "",
+      "category": "User interface",
+      "extensionNamespace": "",
+      "fullName": "Panel sprite button",
+      "helpPath": "/objects/button",
+      "iconUrl": "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4NCjwhLS0gR2VuZXJhdG9yOiBBZG9iZSBJbGx1c3RyYXRvciAyMy4wLjMsIFNWRyBFeHBvcnQgUGx1Zy1JbiAuIFNWRyBWZXJzaW9uOiA2LjAwIEJ1aWxkIDApICAtLT4NCjxzdmcgdmVyc2lvbj0iMS4xIiBpZD0iSWNvbnMiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4Ig0KCSB2aWV3Qm94PSIwIDAgMzIgMzIiIHN0eWxlPSJlbmFibGUtYmFja2dyb3VuZDpuZXcgMCAwIDMyIDMyOyIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+DQo8c3R5bGUgdHlwZT0idGV4dC9jc3MiPg0KCS5zdDB7ZmlsbDpub25lO3N0cm9rZTojMDAwMDAwO3N0cm9rZS13aWR0aDoyO3N0cm9rZS1saW5lY2FwOnJvdW5kO3N0cm9rZS1saW5lam9pbjpyb3VuZDtzdHJva2UtbWl0ZXJsaW1pdDoxMDt9DQo8L3N0eWxlPg0KPHBhdGggY2xhc3M9InN0MCIgZD0iTTI5LDIzSDNjLTEuMSwwLTItMC45LTItMlYxMWMwLTEuMSwwLjktMiwyLTJoMjZjMS4xLDAsMiwwLjksMiwydjEwQzMxLDIyLjEsMzAuMSwyMywyOSwyM3oiLz4NCjxwYXRoIGNsYXNzPSJzdDAiIGQ9Ik0xMywxOUwxMywxOWMtMS4xLDAtMi0wLjktMi0ydi0yYzAtMS4xLDAuOS0yLDItMmgwYzEuMSwwLDIsMC45LDIsMnYyQzE1LDE4LjEsMTQuMSwxOSwxMywxOXoiLz4NCjxsaW5lIGNsYXNzPSJzdDAiIHgxPSIxOCIgeTE9IjEzIiB4Mj0iMTgiIHkyPSIxOSIvPg0KPGxpbmUgY2xhc3M9InN0MCIgeDE9IjIxIiB5MT0iMTMiIHgyPSIxOCIgeTI9IjE3Ii8+DQo8bGluZSBjbGFzcz0ic3QwIiB4MT0iMjEiIHkxPSIxOSIgeDI9IjE5IiB5Mj0iMTYiLz4NCjwvc3ZnPg0K",
+      "name": "PanelSpriteButton",
+      "previewIconUrl": "https://resources.gdevelop-app.com/assets/Icons/Line Hero Pack/Master/SVG/Interface Elements/Interface Elements_interface_ui_button_ok_cta_clock_tap.svg",
+      "shortDescription": "A button that can be customized.",
+      "version": "1.4.6",
+      "description": [
+        "The button can be customized with a background for each state and a label. It handles user interactions and a simple condition can be used to check if it is clicked.",
+        "",
+        "There are ready-to-use buttons in the asset-store [menu buttons pack](https://editor.gdevelop.io/?initial-dialog=asset-store&asset-pack=menu-buttons-menu-buttons)."
+      ],
+      "origin": {
+        "identifier": "PanelSpriteButton",
+        "name": "gdevelop-extension-store"
+      },
+      "tags": [
+        "button",
+        "ui"
+      ],
+      "authorIds": [
+        "IWykYNRvhCZBN3vEgKEbBPOR3Oc2"
+      ],
+      "dependencies": [],
+      "globalVariables": [],
+      "sceneVariables": [],
+      "eventsFunctions": [],
+      "eventsBasedBehaviors": [
+        {
+          "description": "The finite state machine used internally by the button object.",
+          "fullName": "Button finite state machine",
+          "name": "ButtonFSM",
+          "objectType": "",
+          "private": true,
+          "eventsFunctions": [
+            {
+              "fullName": "",
+              "functionType": "Action",
+              "name": "doStepPostEvents",
+              "sentence": "",
+              "events": [
+                {
+                  "colorB": 228,
+                  "colorG": 176,
+                  "colorR": 74,
+                  "creationTime": 0,
+                  "name": "Finite state machine",
+                  "source": "",
+                  "type": "BuiltinCommonInstructions::Group",
+                  "events": [
+                    {
+                      "type": "BuiltinCommonInstructions::Comment",
+                      "color": {
+                        "b": 109,
+                        "g": 230,
+                        "r": 255,
+                        "textB": 0,
+                        "textG": 0,
+                        "textR": 0
+                      },
+                      "comment": "The \"Validated\" state only last one frame."
+                    },
+                    {
+                      "type": "BuiltinCommonInstructions::Standard",
+                      "conditions": [
+                        {
+                          "type": {
+                            "value": "PanelSpriteButton::ButtonFSM::PropertyState"
+                          },
+                          "parameters": [
+                            "Object",
+                            "Behavior",
+                            "=",
+                            "\"Validated\""
+                          ]
+                        }
+                      ],
+                      "actions": [
+                        {
+                          "type": {
+                            "value": "PanelSpriteButton::ButtonFSM::SetPropertyState"
+                          },
+                          "parameters": [
+                            "Object",
+                            "Behavior",
+                            "=",
+                            "\"Idle\""
+                          ]
+                        }
+                      ]
+                    },
+                    {
+                      "colorB": 228,
+                      "colorG": 176,
+                      "colorR": 74,
+                      "creationTime": 0,
+                      "name": "Check position",
+                      "source": "",
+                      "type": "BuiltinCommonInstructions::Group",
+                      "events": [
+                        {
+                          "type": "BuiltinCommonInstructions::Comment",
+                          "color": {
+                            "b": 109,
+                            "g": 230,
+                            "r": 255,
+                            "textB": 0,
+                            "textG": 0,
+                            "textR": 0
+                          },
+                          "comment": "Make sure the cursor position is only checked once per frame."
+                        },
+                        {
+                          "type": "BuiltinCommonInstructions::Standard",
+                          "conditions": [],
+                          "actions": [
+                            {
+                              "type": {
+                                "value": "PanelSpriteButton::ButtonFSM::SetPropertyMouseIsInside"
+                              },
+                              "parameters": [
+                                "Object",
+                                "Behavior",
+                                "no"
+                              ]
+                            }
+                          ]
+                        },
+                        {
+                          "type": "BuiltinCommonInstructions::Standard",
+                          "conditions": [
+                            {
+                              "type": {
+                                "value": "PanelSpriteButton::ButtonFSM::PropertyShouldCheckHovering"
+                              },
+                              "parameters": [
+                                "Object",
+                                "Behavior"
+                              ]
+                            },
+                            {
+                              "type": {
+                                "value": "CollisionPoint"
+                              },
+                              "parameters": [
+                                "Object",
+                                "MouseOnlyCursorX(Object.Layer(), 0)",
+                                "MouseOnlyCursorY(Object.Layer(), 0)"
+                              ]
+                            }
+                          ],
+                          "actions": [
+                            {
+                              "type": {
+                                "value": "PanelSpriteButton::ButtonFSM::SetPropertyMouseIsInside"
+                              },
+                              "parameters": [
+                                "Object",
+                                "Behavior",
+                                "yes"
+                              ]
+                            }
+                          ]
+                        },
+                        {
+                          "type": "BuiltinCommonInstructions::Comment",
+                          "color": {
+                            "b": 109,
+                            "g": 230,
+                            "r": 255,
+                            "textB": 0,
+                            "textG": 0,
+                            "textR": 0
+                          },
+                          "comment": "Touches are always pressed, so ShouldCheckHovering doesn't matter."
+                        },
+                        {
+                          "type": "BuiltinCommonInstructions::Standard",
+                          "conditions": [],
+                          "actions": [
+                            {
+                              "type": {
+                                "value": "PanelSpriteButton::ButtonFSM::SetPropertyTouchIsInside"
+                              },
+                              "parameters": [
+                                "Object",
+                                "Behavior",
+                                "no"
+                              ]
+                            }
+                          ]
+                        },
+                        {
+                          "type": "BuiltinCommonInstructions::Standard",
+                          "conditions": [
+                            {
+                              "type": {
+                                "value": "PanelSpriteButton::ButtonFSM::PropertyTouchId"
+                              },
+                              "parameters": [
+                                "Object",
+                                "Behavior",
+                                "!=",
+                                "0"
+                              ]
+                            },
+                            {
+                              "type": {
+                                "value": "CollisionPoint"
+                              },
+                              "parameters": [
+                                "Object",
+                                "TouchX(TouchId, Object.Layer(), 0)",
+                                "TouchY(TouchId, Object.Layer(), 0)"
+                              ]
+                            }
+                          ],
+                          "actions": [
+                            {
+                              "type": {
+                                "value": "PanelSpriteButton::ButtonFSM::SetPropertyTouchIsInside"
+                              },
+                              "parameters": [
+                                "Object",
+                                "Behavior",
+                                "yes"
+                              ]
+                            }
+                          ]
+                        }
+                      ],
+                      "parameters": []
+                    },
+                    {
+                      "colorB": 228,
+                      "colorG": 176,
+                      "colorR": 74,
+                      "creationTime": 0,
+                      "name": "Handle touch start",
+                      "source": "",
+                      "type": "BuiltinCommonInstructions::Group",
+                      "events": [
+                        {
+                          "type": "BuiltinCommonInstructions::Standard",
+                          "conditions": [
+                            {
+                              "type": {
+                                "value": "HasAnyTouchOrMouseStarted"
+                              },
+                              "parameters": [
+                                ""
+                              ]
+                            }
+                          ],
+                          "actions": [
+                            {
+                              "type": {
+                                "value": "PanelSpriteButton::ButtonFSM::SetPropertyIndex"
+                              },
+                              "parameters": [
+                                "Object",
+                                "Behavior",
+                                "=",
+                                "0"
+                              ]
+                            }
+                          ],
+                          "events": [
+                            {
+                              "type": "BuiltinCommonInstructions::Repeat",
+                              "repeatExpression": "StartedTouchOrMouseCount()",
+                              "conditions": [],
+                              "actions": [],
+                              "events": [
+                                {
+                                  "type": "BuiltinCommonInstructions::Standard",
+                                  "conditions": [
+                                    {
+                                      "type": {
+                                        "value": "CollisionPoint"
+                                      },
+                                      "parameters": [
+                                        "Object",
+                                        "TouchX(StartedTouchOrMouseId(Index), Object.Layer(), 0)",
+                                        "TouchY(StartedTouchOrMouseId(Index), Object.Layer(), 0)"
+                                      ]
+                                    }
+                                  ],
+                                  "actions": [
+                                    {
+                                      "type": {
+                                        "value": "PanelSpriteButton::ButtonFSM::SetPropertyTouchId"
+                                      },
+                                      "parameters": [
+                                        "Object",
+                                        "Behavior",
+                                        "=",
+                                        "StartedTouchOrMouseId(Index)"
+                                      ]
+                                    },
+                                    {
+                                      "type": {
+                                        "value": "PanelSpriteButton::ButtonFSM::SetPropertyTouchIsInside"
+                                      },
+                                      "parameters": [
+                                        "Object",
+                                        "Behavior",
+                                        "yes"
+                                      ]
+                                    }
+                                  ],
+                                  "events": [
+                                    {
+                                      "type": "BuiltinCommonInstructions::Standard",
+                                      "conditions": [
+                                        {
+                                          "type": {
+                                            "value": "BuiltinCommonInstructions::Or"
+                                          },
+                                          "parameters": [],
+                                          "subInstructions": [
+                                            {
+                                              "type": {
+                                                "value": "PanelSpriteButton::ButtonFSM::PropertyState"
+                                              },
+                                              "parameters": [
+                                                "Object",
+                                                "Behavior",
+                                                "=",
+                                                "\"Hovered\""
+                                              ]
+                                            },
+                                            {
+                                              "type": {
+                                                "value": "PanelSpriteButton::ButtonFSM::PropertyState"
+                                              },
+                                              "parameters": [
+                                                "Object",
+                                                "Behavior",
+                                                "=",
+                                                "\"Idle\""
+                                              ]
+                                            }
+                                          ]
+                                        }
+                                      ],
+                                      "actions": [
+                                        {
+                                          "type": {
+                                            "value": "PanelSpriteButton::ButtonFSM::SetPropertyState"
+                                          },
+                                          "parameters": [
+                                            "Object",
+                                            "Behavior",
+                                            "=",
+                                            "\"PressedInside\""
+                                          ]
+                                        }
+                                      ]
+                                    }
+                                  ]
+                                },
+                                {
+                                  "type": "BuiltinCommonInstructions::Standard",
+                                  "conditions": [],
+                                  "actions": [
+                                    {
+                                      "type": {
+                                        "value": "PanelSpriteButton::ButtonFSM::SetPropertyIndex"
+                                      },
+                                      "parameters": [
+                                        "Object",
+                                        "Behavior",
+                                        "+",
+                                        "1"
+                                      ]
+                                    }
+                                  ]
+                                }
+                              ]
+                            }
+                          ]
+                        }
+                      ],
+                      "parameters": []
+                    },
+                    {
+                      "colorB": 228,
+                      "colorG": 176,
+                      "colorR": 74,
+                      "creationTime": 0,
+                      "name": "Apply position changes",
+                      "source": "",
+                      "type": "BuiltinCommonInstructions::Group",
+                      "events": [
+                        {
+                          "type": "BuiltinCommonInstructions::Standard",
+                          "conditions": [
+                            {
+                              "type": {
+                                "inverted": true,
+                                "value": "PanelSpriteButton::ButtonFSM::PropertyMouseIsInside"
+                              },
+                              "parameters": [
+                                "Object",
+                                "Behavior"
+                              ]
+                            },
+                            {
+                              "type": {
+                                "value": "PanelSpriteButton::ButtonFSM::PropertyState"
+                              },
+                              "parameters": [
+                                "Object",
+                                "Behavior",
+                                "=",
+                                "\"Hovered\""
+                              ]
+                            }
+                          ],
+                          "actions": [
+                            {
+                              "type": {
+                                "value": "PanelSpriteButton::ButtonFSM::SetPropertyState"
+                              },
+                              "parameters": [
+                                "Object",
+                                "Behavior",
+                                "=",
+                                "\"Idle\""
+                              ]
+                            }
+                          ]
+                        },
+                        {
+                          "type": "BuiltinCommonInstructions::Standard",
+                          "conditions": [
+                            {
+                              "type": {
+                                "value": "PanelSpriteButton::ButtonFSM::PropertyMouseIsInside"
+                              },
+                              "parameters": [
+                                "Object",
+                                "Behavior"
+                              ]
+                            },
+                            {
+                              "type": {
+                                "value": "PanelSpriteButton::ButtonFSM::PropertyState"
+                              },
+                              "parameters": [
+                                "Object",
+                                "Behavior",
+                                "=",
+                                "\"Idle\""
+                              ]
+                            }
+                          ],
+                          "actions": [
+                            {
+                              "type": {
+                                "value": "PanelSpriteButton::ButtonFSM::SetPropertyState"
+                              },
+                              "parameters": [
+                                "Object",
+                                "Behavior",
+                                "=",
+                                "\"Hovered\""
+                              ]
+                            }
+                          ]
+                        },
+                        {
+                          "type": "BuiltinCommonInstructions::Standard",
+                          "conditions": [
+                            {
+                              "type": {
+                                "inverted": true,
+                                "value": "PanelSpriteButton::ButtonFSM::PropertyTouchIsInside"
+                              },
+                              "parameters": [
+                                "Object",
+                                "Behavior"
+                              ]
+                            },
+                            {
+                              "type": {
+                                "value": "PanelSpriteButton::ButtonFSM::PropertyState"
+                              },
+                              "parameters": [
+                                "Object",
+                                "Behavior",
+                                "=",
+                                "\"PressedInside\""
+                              ]
+                            }
+                          ],
+                          "actions": [
+                            {
+                              "type": {
+                                "value": "PanelSpriteButton::ButtonFSM::SetPropertyState"
+                              },
+                              "parameters": [
+                                "Object",
+                                "Behavior",
+                                "=",
+                                "\"PressedOutside\""
+                              ]
+                            }
+                          ]
+                        },
+                        {
+                          "type": "BuiltinCommonInstructions::Standard",
+                          "conditions": [
+                            {
+                              "type": {
+                                "value": "PanelSpriteButton::ButtonFSM::PropertyTouchIsInside"
+                              },
+                              "parameters": [
+                                "Object",
+                                "Behavior"
+                              ]
+                            },
+                            {
+                              "type": {
+                                "value": "PanelSpriteButton::ButtonFSM::PropertyState"
+                              },
+                              "parameters": [
+                                "Object",
+                                "Behavior",
+                                "=",
+                                "\"PressedOutside\""
+                              ]
+                            }
+                          ],
+                          "actions": [
+                            {
+                              "type": {
+                                "value": "PanelSpriteButton::ButtonFSM::SetPropertyState"
+                              },
+                              "parameters": [
+                                "Object",
+                                "Behavior",
+                                "=",
+                                "\"PressedInside\""
+                              ]
+                            }
+                          ]
+                        }
+                      ],
+                      "parameters": []
+                    },
+                    {
+                      "colorB": 228,
+                      "colorG": 176,
+                      "colorR": 74,
+                      "creationTime": 0,
+                      "name": "Handle touch end",
+                      "source": "",
+                      "type": "BuiltinCommonInstructions::Group",
+                      "events": [
+                        {
+                          "type": "BuiltinCommonInstructions::Standard",
+                          "conditions": [
+                            {
+                              "type": {
+                                "value": "HasTouchEnded"
+                              },
+                              "parameters": [
+                                "",
+                                "TouchId"
+                              ]
+                            }
+                          ],
+                          "actions": [
+                            {
+                              "type": {
+                                "value": "PanelSpriteButton::ButtonFSM::SetPropertyTouchId"
+                              },
+                              "parameters": [
+                                "Object",
+                                "Behavior",
+                                "=",
+                                "0"
+                              ]
+                            }
+                          ],
+                          "events": [
+                            {
+                              "type": "BuiltinCommonInstructions::Standard",
+                              "conditions": [
+                                {
+                                  "type": {
+                                    "value": "PanelSpriteButton::ButtonFSM::PropertyState"
+                                  },
+                                  "parameters": [
+                                    "Object",
+                                    "Behavior",
+                                    "=",
+                                    "\"PressedInside\""
+                                  ]
+                                }
+                              ],
+                              "actions": [
+                                {
+                                  "type": {
+                                    "value": "PanelSpriteButton::ButtonFSM::SetPropertyState"
+                                  },
+                                  "parameters": [
+                                    "Object",
+                                    "Behavior",
+                                    "=",
+                                    "\"Validated\""
+                                  ]
+                                }
+                              ]
+                            },
+                            {
+                              "type": "BuiltinCommonInstructions::Standard",
+                              "conditions": [
+                                {
+                                  "type": {
+                                    "inverted": true,
+                                    "value": "PanelSpriteButton::ButtonFSM::PropertyState"
+                                  },
+                                  "parameters": [
+                                    "Object",
+                                    "Behavior",
+                                    "=",
+                                    "\"PressedInside\""
+                                  ]
+                                },
+                                {
+                                  "type": {
+                                    "inverted": true,
+                                    "value": "PanelSpriteButton::ButtonFSM::PropertyState"
+                                  },
+                                  "parameters": [
+                                    "Object",
+                                    "Behavior",
+                                    "=",
+                                    "\"Validated\""
+                                  ]
+                                }
+                              ],
+                              "actions": [
+                                {
+                                  "type": {
+                                    "value": "PanelSpriteButton::ButtonFSM::SetPropertyState"
+                                  },
+                                  "parameters": [
+                                    "Object",
+                                    "Behavior",
+                                    "=",
+                                    "\"Idle\""
+                                  ]
+                                }
+                              ]
+                            }
+                          ]
+                        }
+                      ],
+                      "parameters": []
+                    }
+                  ],
+                  "parameters": []
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "PanelSpriteButton::ButtonFSM",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "fullName": "",
+              "functionType": "Action",
+              "name": "onDeActivate",
+              "sentence": "",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "PanelSpriteButton::ButtonFSM::ResetState"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        ""
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "PanelSpriteButton::ButtonFSM",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "Reset the state of the button.",
+              "fullName": "Reset state",
+              "functionType": "Action",
+              "name": "ResetState",
+              "private": true,
+              "sentence": "Reset the button state of _PARAM0_",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "PanelSpriteButton::ButtonFSM::SetPropertyState"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "=",
+                        "\"Idle\""
+                      ]
+                    },
+                    {
+                      "type": {
+                        "value": "PanelSpriteButton::ButtonFSM::SetPropertyTouchId"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "=",
+                        "0"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "PanelSpriteButton::ButtonFSM",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "Check if the button is not used.",
+              "fullName": "Is idle",
+              "functionType": "Condition",
+              "name": "IsIdle",
+              "sentence": "_PARAM0_ is idle",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [
+                    {
+                      "type": {
+                        "value": "PanelSpriteButton::ButtonFSM::PropertyState"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "=",
+                        "\"Idle\""
+                      ]
+                    }
+                  ],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SetReturnBoolean"
+                      },
+                      "parameters": [
+                        "True"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "PanelSpriteButton::ButtonFSM",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "Check if the button was just clicked.",
+              "fullName": "Is clicked",
+              "functionType": "Condition",
+              "name": "IsClicked",
+              "sentence": "_PARAM0_ is clicked",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [
+                    {
+                      "type": {
+                        "value": "PanelSpriteButton::ButtonFSM::PropertyState"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "=",
+                        "\"Validated\""
+                      ]
+                    }
+                  ],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SetReturnBoolean"
+                      },
+                      "parameters": [
+                        "True"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "PanelSpriteButton::ButtonFSM",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "Check if the cursor is hovered over the button.",
+              "fullName": "Is hovered",
+              "functionType": "Condition",
+              "name": "IsHovered",
+              "sentence": "_PARAM0_ is hovered",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [
+                    {
+                      "type": {
+                        "value": "PanelSpriteButton::ButtonFSM::PropertyState"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "=",
+                        "\"Hovered\""
+                      ]
+                    }
+                  ],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SetReturnBoolean"
+                      },
+                      "parameters": [
+                        "True"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "PanelSpriteButton::ButtonFSM",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "Check if the button is either hovered or pressed but not hovered.",
+              "fullName": "Is focused",
+              "functionType": "Condition",
+              "name": "IsFocused",
+              "sentence": "_PARAM0_ is focused",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [
+                    {
+                      "type": {
+                        "value": "PanelSpriteButton::ButtonFSM::PropertyState"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "=",
+                        "\"Hovered\""
+                      ]
+                    }
+                  ],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SetReturnBoolean"
+                      },
+                      "parameters": [
+                        "True"
+                      ]
+                    }
+                  ]
+                },
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [
+                    {
+                      "type": {
+                        "value": "PanelSpriteButton::ButtonFSM::PropertyState"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "=",
+                        "\"PressedOutside\""
+                      ]
+                    }
+                  ],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SetReturnBoolean"
+                      },
+                      "parameters": [
+                        "True"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "PanelSpriteButton::ButtonFSM",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "Check if the button is currently being pressed with mouse or touch.",
+              "fullName": "Is pressed",
+              "functionType": "Condition",
+              "name": "IsPressed",
+              "sentence": "_PARAM0_ is pressed",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [
+                    {
+                      "type": {
+                        "value": "PanelSpriteButton::ButtonFSM::PropertyState"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "=",
+                        "\"PressedInside\""
+                      ]
+                    }
+                  ],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SetReturnBoolean"
+                      },
+                      "parameters": [
+                        "True"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "PanelSpriteButton::ButtonFSM",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "Check if the button is currently being pressed outside with mouse or touch.",
+              "fullName": "Is held outside",
+              "functionType": "Condition",
+              "name": "IsPressedOutside",
+              "sentence": "_PARAM0_ is held outside",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [
+                    {
+                      "type": {
+                        "value": "PanelSpriteButton::ButtonFSM::PropertyState"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "=",
+                        "\"PressedOutside\""
+                      ]
+                    }
+                  ],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SetReturnBoolean"
+                      },
+                      "parameters": [
+                        "True"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "PanelSpriteButton::ButtonFSM",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "the touch id that is using the button or 0 if none.",
+              "fullName": "Touch id",
+              "functionType": "ExpressionAndCondition",
+              "name": "TouchId",
+              "sentence": "the touch id",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SetReturnNumber"
+                      },
+                      "parameters": [
+                        "TouchId"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "expressionType": {
+                "type": "expression"
+              },
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "PanelSpriteButton::ButtonFSM",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            }
+          ],
+          "propertyDescriptors": [
+            {
+              "value": "true",
+              "type": "Boolean",
+              "label": "",
+              "description": "Should check hovering",
+              "group": "",
+              "extraInformation": [],
+              "hidden": true,
+              "name": "ShouldCheckHovering"
+            },
+            {
+              "value": "Idle",
+              "type": "Choice",
+              "label": "State",
+              "description": "",
+              "group": "",
+              "extraInformation": [
+                "Idle",
+                "Hovered",
+                "PressedInside",
+                "PressedOutside",
+                "Validated"
+              ],
+              "hidden": true,
+              "name": "State"
+            },
+            {
+              "value": "0",
+              "type": "Number",
+              "label": "Touch id",
+              "description": "",
+              "group": "",
+              "extraInformation": [],
+              "hidden": true,
+              "name": "TouchId"
+            },
+            {
+              "value": "",
+              "type": "Boolean",
+              "label": "Touch is inside",
+              "description": "",
+              "group": "",
+              "extraInformation": [],
+              "hidden": true,
+              "name": "TouchIsInside"
+            },
+            {
+              "value": "",
+              "type": "Boolean",
+              "label": "Mouse is inside",
+              "description": "",
+              "group": "",
+              "extraInformation": [],
+              "hidden": true,
+              "name": "MouseIsInside"
+            },
+            {
+              "value": "",
+              "type": "Number",
+              "label": "",
+              "description": "",
+              "group": "",
+              "extraInformation": [],
+              "hidden": true,
+              "name": "Index"
+            }
+          ],
+          "sharedPropertyDescriptors": []
+        }
+      ],
+      "eventsBasedObjects": [
+        {
+          "areaMaxX": 64,
+          "areaMaxY": 64,
+          "areaMaxZ": 64,
+          "areaMinX": 0,
+          "areaMinY": 0,
+          "areaMinZ": 0,
+          "defaultName": "Button",
+          "description": "A button that can be customized.",
+          "fullName": "Button (panel sprite)",
+          "isUsingLegacyInstancesRenderer": true,
+          "name": "PanelSpriteButton",
+          "eventsFunctions": [
+            {
+              "fullName": "",
+              "functionType": "Action",
+              "name": "onCreated",
+              "sentence": "",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Comment",
+                  "color": {
+                    "b": 109,
+                    "g": 230,
+                    "r": 255,
+                    "textB": 0,
+                    "textG": 0,
+                    "textR": 0
+                  },
+                  "comment": "Create one background instance for of each state.\nOnly the instance for the current state is shown."
+                },
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "Create"
+                      },
+                      "parameters": [
+                        "",
+                        "Idle",
+                        "0",
+                        "0",
+                        ""
+                      ]
+                    },
+                    {
+                      "type": {
+                        "value": "Create"
+                      },
+                      "parameters": [
+                        "",
+                        "Hovered",
+                        "0",
+                        "0",
+                        ""
+                      ]
+                    },
+                    {
+                      "type": {
+                        "value": "Create"
+                      },
+                      "parameters": [
+                        "",
+                        "Pressed",
+                        "0",
+                        "0",
+                        ""
+                      ]
+                    },
+                    {
+                      "type": {
+                        "value": "Cache"
+                      },
+                      "parameters": [
+                        "Hovered"
+                      ]
+                    },
+                    {
+                      "type": {
+                        "value": "Cache"
+                      },
+                      "parameters": [
+                        "Pressed"
+                      ]
+                    },
+                    {
+                      "type": {
+                        "value": "ChangePlan"
+                      },
+                      "parameters": [
+                        "Hovered",
+                        "=",
+                        "1"
+                      ]
+                    }
+                  ]
+                },
+                {
+                  "type": "BuiltinCommonInstructions::Comment",
+                  "color": {
+                    "b": 109,
+                    "g": 230,
+                    "r": 255,
+                    "textB": 0,
+                    "textG": 0,
+                    "textR": 0
+                  },
+                  "comment": "Place the label over the backgrounds."
+                },
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "Create"
+                      },
+                      "parameters": [
+                        "",
+                        "Label",
+                        "0",
+                        "0",
+                        ""
+                      ]
+                    },
+                    {
+                      "type": {
+                        "value": "ChangePlan"
+                      },
+                      "parameters": [
+                        "Label",
+                        "=",
+                        "2"
+                      ]
+                    },
+                    {
+                      "type": {
+                        "value": "TextObject::SetWrapping"
+                      },
+                      "parameters": [
+                        "Label",
+                        "yes"
+                      ]
+                    },
+                    {
+                      "type": {
+                        "value": "PanelSpriteButton::PanelSpriteButton::CenterLabel"
+                      },
+                      "parameters": [
+                        "Object",
+                        ""
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "supplementaryInformation": "PanelSpriteButton::PanelSpriteButton",
+                  "type": "object"
+                }
+              ],
+              "objectGroups": [
+                {
+                  "name": "Background",
+                  "objects": [
+                    {
+                      "name": "Idle"
+                    },
+                    {
+                      "name": "Hovered"
+                    },
+                    {
+                      "name": "Pressed"
+                    }
+                  ]
+                }
+              ]
+            },
+            {
+              "fullName": "",
+              "functionType": "Action",
+              "name": "onHotReloading",
+              "sentence": "",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "PanelSpriteButton::PanelSpriteButton::CenterLabel"
+                      },
+                      "parameters": [
+                        "Object",
+                        ""
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "supplementaryInformation": "PanelSpriteButton::PanelSpriteButton",
+                  "type": "object"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "fullName": "",
+              "functionType": "Action",
+              "name": "doStepPostEvents",
+              "sentence": "",
+              "events": [
+                {
+                  "colorB": 228,
+                  "colorG": 176,
+                  "colorR": 74,
+                  "creationTime": 0,
+                  "name": "Apply states",
+                  "source": "",
+                  "type": "BuiltinCommonInstructions::Group",
+                  "events": [
+                    {
+                      "type": "BuiltinCommonInstructions::Comment",
+                      "color": {
+                        "b": 109,
+                        "g": 230,
+                        "r": 255,
+                        "textB": 0,
+                        "textG": 0,
+                        "textR": 0
+                      },
+                      "comment": "Show the right background accordingly to the new state."
+                    },
+                    {
+                      "type": "BuiltinCommonInstructions::Standard",
+                      "conditions": [
+                        {
+                          "type": {
+                            "value": "PanelSpriteButton::PanelSpriteButton::IsIdle"
+                          },
+                          "parameters": [
+                            "Object",
+                            "ButtonFSM"
+                          ]
+                        },
+                        {
+                          "type": {
+                            "value": "BuiltinCommonInstructions::Once"
+                          },
+                          "parameters": []
+                        }
+                      ],
+                      "actions": [
+                        {
+                          "type": {
+                            "value": "SetCenterY"
+                          },
+                          "parameters": [
+                            "Label",
+                            "=",
+                            "Object.CenterWithPaddingY()"
+                          ]
+                        },
+                        {
+                          "type": {
+                            "value": "Montre"
+                          },
+                          "parameters": [
+                            "Idle",
+                            ""
+                          ]
+                        },
+                        {
+                          "type": {
+                            "value": "Cache"
+                          },
+                          "parameters": [
+                            "Pressed"
+                          ]
+                        }
+                      ],
+                      "events": [
+                        {
+                          "type": "BuiltinCommonInstructions::Standard",
+                          "conditions": [
+                            {
+                              "type": {
+                                "value": "Visible"
+                              },
+                              "parameters": [
+                                "Hovered"
+                              ]
+                            },
+                            {
+                              "type": {
+                                "value": "PanelSpriteButton::PanelSpriteButton::PropertyHoveredFadeOutDuration"
+                              },
+                              "parameters": [
+                                "Object",
+                                ">",
+                                "0"
+                              ]
+                            }
+                          ],
+                          "actions": [
+                            {
+                              "type": {
+                                "value": "Tween::AddObjectOpacityTween"
+                              },
+                              "parameters": [
+                                "Hovered",
+                                "Tween",
+                                "\"Fadeout\"",
+                                "0",
+                                "\"linear\"",
+                                "Object.PropertyHoveredFadeOutDuration() * 1000",
+                                ""
+                              ]
+                            }
+                          ]
+                        },
+                        {
+                          "type": "BuiltinCommonInstructions::Standard",
+                          "conditions": [
+                            {
+                              "type": {
+                                "value": "PanelSpriteButton::PanelSpriteButton::PropertyHoveredFadeOutDuration"
+                              },
+                              "parameters": [
+                                "Object",
+                                "=",
+                                "0"
+                              ]
+                            }
+                          ],
+                          "actions": [
+                            {
+                              "type": {
+                                "value": "Cache"
+                              },
+                              "parameters": [
+                                "Hovered"
+                              ]
+                            }
+                          ]
+                        }
+                      ]
+                    },
+                    {
+                      "type": "BuiltinCommonInstructions::Standard",
+                      "conditions": [
+                        {
+                          "type": {
+                            "value": "PanelSpriteObject::Opacity"
+                          },
+                          "parameters": [
+                            "Hovered",
+                            "=",
+                            "0"
+                          ]
+                        }
+                      ],
+                      "actions": [
+                        {
+                          "type": {
+                            "value": "Cache"
+                          },
+                          "parameters": [
+                            "Hovered"
+                          ]
+                        },
+                        {
+                          "type": {
+                            "value": "PanelSpriteObject::SetOpacity"
+                          },
+                          "parameters": [
+                            "Hovered",
+                            "=",
+                            "255"
+                          ]
+                        }
+                      ]
+                    },
+                    {
+                      "type": "BuiltinCommonInstructions::Standard",
+                      "conditions": [
+                        {
+                          "type": {
+                            "value": "PanelSpriteButton::PanelSpriteButton::IsHovered"
+                          },
+                          "parameters": [
+                            "Object",
+                            "ButtonFSM"
+                          ]
+                        },
+                        {
+                          "type": {
+                            "value": "BuiltinCommonInstructions::Once"
+                          },
+                          "parameters": []
+                        }
+                      ],
+                      "actions": [
+                        {
+                          "type": {
+                            "value": "SetCenterY"
+                          },
+                          "parameters": [
+                            "Label",
+                            "=",
+                            "Object.CenterWithPaddingY()"
+                          ]
+                        },
+                        {
+                          "type": {
+                            "value": "Cache"
+                          },
+                          "parameters": [
+                            "Idle"
+                          ]
+                        },
+                        {
+                          "type": {
+                            "value": "Montre"
+                          },
+                          "parameters": [
+                            "Hovered",
+                            ""
+                          ]
+                        },
+                        {
+                          "type": {
+                            "value": "Cache"
+                          },
+                          "parameters": [
+                            "Pressed"
+                          ]
+                        },
+                        {
+                          "type": {
+                            "value": "Tween::RemoveTween"
+                          },
+                          "parameters": [
+                            "Hovered",
+                            "Tween",
+                            "\"Fadeout\""
+                          ]
+                        },
+                        {
+                          "type": {
+                            "value": "PanelSpriteObject::SetOpacity"
+                          },
+                          "parameters": [
+                            "Hovered",
+                            "=",
+                            "255"
+                          ]
+                        }
+                      ]
+                    },
+                    {
+                      "type": "BuiltinCommonInstructions::Standard",
+                      "conditions": [
+                        {
+                          "type": {
+                            "value": "PanelSpriteButton::PanelSpriteButton::IsPressed"
+                          },
+                          "parameters": [
+                            "Object",
+                            "ButtonFSM"
+                          ]
+                        },
+                        {
+                          "type": {
+                            "value": "BuiltinCommonInstructions::Once"
+                          },
+                          "parameters": []
+                        }
+                      ],
+                      "actions": [
+                        {
+                          "type": {
+                            "value": "SetCenterY"
+                          },
+                          "parameters": [
+                            "Label",
+                            "=",
+                            "Object.CenterWithPaddingY() + Object.PropertyPressedLabelOffsetY()"
+                          ]
+                        },
+                        {
+                          "type": {
+                            "value": "Cache"
+                          },
+                          "parameters": [
+                            "Idle"
+                          ]
+                        },
+                        {
+                          "type": {
+                            "value": "Cache"
+                          },
+                          "parameters": [
+                            "Hovered"
+                          ]
+                        },
+                        {
+                          "type": {
+                            "value": "Montre"
+                          },
+                          "parameters": [
+                            "Pressed",
+                            ""
+                          ]
+                        }
+                      ]
+                    },
+                    {
+                      "type": "BuiltinCommonInstructions::Standard",
+                      "conditions": [
+                        {
+                          "type": {
+                            "value": "PanelSpriteButton::PanelSpriteButton::IsFocused"
+                          },
+                          "parameters": [
+                            "Object",
+                            "ButtonFSM"
+                          ]
+                        },
+                        {
+                          "type": {
+                            "value": "BuiltinCommonInstructions::Once"
+                          },
+                          "parameters": []
+                        }
+                      ],
+                      "actions": [
+                        {
+                          "type": {
+                            "value": "SetCenterY"
+                          },
+                          "parameters": [
+                            "Label",
+                            "=",
+                            "Object.CenterWithPaddingY()"
+                          ]
+                        },
+                        {
+                          "type": {
+                            "value": "Cache"
+                          },
+                          "parameters": [
+                            "Idle"
+                          ]
+                        },
+                        {
+                          "type": {
+                            "value": "Montre"
+                          },
+                          "parameters": [
+                            "Hovered",
+                            ""
+                          ]
+                        },
+                        {
+                          "type": {
+                            "value": "Cache"
+                          },
+                          "parameters": [
+                            "Pressed"
+                          ]
+                        }
+                      ]
+                    }
+                  ],
+                  "parameters": []
+                },
+                {
+                  "colorB": 228,
+                  "colorG": 176,
+                  "colorR": 74,
+                  "creationTime": 0,
+                  "name": "Resize",
+                  "source": "",
+                  "type": "BuiltinCommonInstructions::Group",
+                  "events": [
+                    {
+                      "type": "BuiltinCommonInstructions::Comment",
+                      "color": {
+                        "b": 109,
+                        "g": 230,
+                        "r": 255,
+                        "textB": 0,
+                        "textG": 0,
+                        "textR": 0
+                      },
+                      "comment": "Children instances must be resized when the button size change:\n- backgrounds for each state are resized to take the full dimensions of the button\n- the label is put back at the center of the button\n\nThe scale is set back to 1 because it means that the parent instance has the same dimensions as the union of its children instances."
+                    },
+                    {
+                      "type": "BuiltinCommonInstructions::Standard",
+                      "conditions": [
+                        {
+                          "type": {
+                            "value": "BuiltinCommonInstructions::Or"
+                          },
+                          "parameters": [],
+                          "subInstructions": [
+                            {
+                              "type": {
+                                "value": "BuiltinCommonInstructions::CompareNumbers"
+                              },
+                              "parameters": [
+                                "Object.Width()",
+                                "!=",
+                                "max(Idle.BoundingBoxRight(), Label.BoundingBoxRight()) - min(Idle.BoundingBoxLeft(), Label.BoundingBoxLeft())"
+                              ]
+                            },
+                            {
+                              "type": {
+                                "value": "BuiltinCommonInstructions::CompareNumbers"
+                              },
+                              "parameters": [
+                                "Object.Height()",
+                                "!=",
+                                "max(Idle.BoundingBoxBottom(), Label.BoundingBoxBottom()) - min(Idle.BoundingBoxTop(), Label.BoundingBoxTop())"
+                              ]
+                            }
+                          ]
+                        }
+                      ],
+                      "actions": [],
+                      "events": [
+                        {
+                          "type": "BuiltinCommonInstructions::Standard",
+                          "conditions": [],
+                          "actions": [
+                            {
+                              "type": {
+                                "value": "SetNumberVariable"
+                              },
+                              "parameters": [
+                                "Width",
+                                "=",
+                                "Object.Width()"
+                              ]
+                            },
+                            {
+                              "type": {
+                                "value": "SetNumberVariable"
+                              },
+                              "parameters": [
+                                "Height",
+                                "=",
+                                "Object.Height()"
+                              ]
+                            },
+                            {
+                              "type": {
+                                "value": "PanelSpriteButton::Scale"
+                              },
+                              "parameters": [
+                                "Object",
+                                "=",
+                                "1"
+                              ]
+                            },
+                            {
+                              "type": {
+                                "value": "PanelSpriteObject::Width"
+                              },
+                              "parameters": [
+                                "Background",
+                                "=",
+                                "Width"
+                              ]
+                            },
+                            {
+                              "type": {
+                                "value": "PanelSpriteObject::Height"
+                              },
+                              "parameters": [
+                                "Background",
+                                "=",
+                                "Height"
+                              ]
+                            },
+                            {
+                              "type": {
+                                "value": "PanelSpriteButton::PanelSpriteButton::CenterLabel"
+                              },
+                              "parameters": [
+                                "Object",
+                                ""
+                              ]
+                            }
+                          ],
+                          "variables": [
+                            {
+                              "name": "Width",
+                              "type": "number",
+                              "value": 0
+                            },
+                            {
+                              "name": "Height",
+                              "type": "number",
+                              "value": 0
+                            }
+                          ]
+                        }
+                      ]
+                    }
+                  ],
+                  "parameters": []
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "supplementaryInformation": "PanelSpriteButton::PanelSpriteButton",
+                  "type": "object"
+                }
+              ],
+              "objectGroups": [
+                {
+                  "name": "Background",
+                  "objects": [
+                    {
+                      "name": "Idle"
+                    },
+                    {
+                      "name": "Hovered"
+                    },
+                    {
+                      "name": "Pressed"
+                    }
+                  ]
+                }
+              ]
+            },
+            {
+              "description": "Check if the button is not used.",
+              "fullName": "Is idle",
+              "functionType": "Condition",
+              "name": "IsIdle",
+              "sentence": "_PARAM0_ is idle",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [
+                    {
+                      "type": {
+                        "value": "PanelSpriteButton::ButtonFSM::IsIdle"
+                      },
+                      "parameters": [
+                        "Idle",
+                        "ButtonFSM",
+                        ""
+                      ]
+                    }
+                  ],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SetReturnBoolean"
+                      },
+                      "parameters": [
+                        "True"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "supplementaryInformation": "PanelSpriteButton::PanelSpriteButton",
+                  "type": "object"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "Check if the button was just clicked.",
+              "fullName": "Is clicked",
+              "functionType": "Condition",
+              "name": "IsClicked",
+              "sentence": "_PARAM0_ is clicked",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [
+                    {
+                      "type": {
+                        "value": "PanelSpriteButton::ButtonFSM::IsClicked"
+                      },
+                      "parameters": [
+                        "Idle",
+                        "ButtonFSM",
+                        ""
+                      ]
+                    }
+                  ],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SetReturnBoolean"
+                      },
+                      "parameters": [
+                        "True"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "supplementaryInformation": "PanelSpriteButton::PanelSpriteButton",
+                  "type": "object"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "Check if the cursor is hovered over the button.",
+              "fullName": "Is hovered",
+              "functionType": "Condition",
+              "name": "IsHovered",
+              "sentence": "_PARAM0_ is hovered",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [
+                    {
+                      "type": {
+                        "value": "PanelSpriteButton::ButtonFSM::IsHovered"
+                      },
+                      "parameters": [
+                        "Idle",
+                        "ButtonFSM",
+                        ""
+                      ]
+                    }
+                  ],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SetReturnBoolean"
+                      },
+                      "parameters": [
+                        "True"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "supplementaryInformation": "PanelSpriteButton::PanelSpriteButton",
+                  "type": "object"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "Check if the button is either hovered or pressed but not hovered.",
+              "fullName": "Is focused",
+              "functionType": "Condition",
+              "name": "IsFocused",
+              "sentence": "_PARAM0_ is focused",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [
+                    {
+                      "type": {
+                        "value": "PanelSpriteButton::ButtonFSM::IsFocused"
+                      },
+                      "parameters": [
+                        "Idle",
+                        "ButtonFSM",
+                        ""
+                      ]
+                    }
+                  ],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SetReturnBoolean"
+                      },
+                      "parameters": [
+                        "True"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "supplementaryInformation": "PanelSpriteButton::PanelSpriteButton",
+                  "type": "object"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "Check if the button is currently being pressed with mouse or touch.",
+              "fullName": "Is pressed",
+              "functionType": "Condition",
+              "name": "IsPressed",
+              "sentence": "_PARAM0_ is pressed",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [
+                    {
+                      "type": {
+                        "value": "PanelSpriteButton::ButtonFSM::IsPressed"
+                      },
+                      "parameters": [
+                        "Idle",
+                        "ButtonFSM",
+                        ""
+                      ]
+                    }
+                  ],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SetReturnBoolean"
+                      },
+                      "parameters": [
+                        "True"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "supplementaryInformation": "PanelSpriteButton::PanelSpriteButton",
+                  "type": "object"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "Change the text of the button label.",
+              "fullName": "Label text",
+              "functionType": "Action",
+              "name": "SetLabelText",
+              "sentence": "Change the text of _PARAM0_ to _PARAM1_",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "TextObject::String"
+                      },
+                      "parameters": [
+                        "Label",
+                        "=",
+                        "LabelText"
+                      ]
+                    },
+                    {
+                      "type": {
+                        "value": "PanelSpriteButton::PanelSpriteButton::CenterLabel"
+                      },
+                      "parameters": [
+                        "Object",
+                        ""
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "supplementaryInformation": "PanelSpriteButton::PanelSpriteButton",
+                  "type": "object"
+                },
+                {
+                  "description": "Text",
+                  "name": "LabelText",
+                  "type": "string"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "Return the label text.",
+              "fullName": "Label text",
+              "functionType": "StringExpression",
+              "name": "LabelText",
+              "sentence": "",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SetReturnString"
+                      },
+                      "parameters": [
+                        "Label.String()"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "expressionType": {
+                "type": "string"
+              },
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "supplementaryInformation": "PanelSpriteButton::PanelSpriteButton",
+                  "type": "object"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "Return the label center Y according to the button configuration. This expression is used in doStepPostEvents when the button is pressed or released.",
+              "fullName": "",
+              "functionType": "Expression",
+              "name": "CenterWithPaddingY",
+              "private": true,
+              "sentence": "",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SetReturnNumber"
+                      },
+                      "parameters": [
+                        "Idle.CenterY() + (Object.PropertyTopPadding() - Object.PropertyBottomPadding()) / 2"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "expressionType": {
+                "type": "expression"
+              },
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "supplementaryInformation": "PanelSpriteButton::PanelSpriteButton",
+                  "type": "object"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "Center the label according to the button configuration. This is used in doStepPostEvents when the button is resized.",
+              "fullName": "",
+              "functionType": "Action",
+              "name": "CenterLabel",
+              "private": true,
+              "sentence": "Center the label of _PARAM0_",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "MettreXY"
+                      },
+                      "parameters": [
+                        "Label",
+                        "=",
+                        "Object.PropertyLeftPadding()",
+                        "=",
+                        "Object.PropertyTopPadding()"
+                      ]
+                    },
+                    {
+                      "type": {
+                        "value": "TextObject::WrappingWidth"
+                      },
+                      "parameters": [
+                        "Label",
+                        "=",
+                        "Idle.Width() - Object.PropertyLeftPadding() - Object.PropertyRightPadding()"
+                      ]
+                    },
+                    {
+                      "type": {
+                        "value": "SetCenterY"
+                      },
+                      "parameters": [
+                        "Label",
+                        "=",
+                        "Object.CenterWithPaddingY()"
+                      ]
+                    }
+                  ],
+                  "events": [
+                    {
+                      "type": "BuiltinCommonInstructions::Standard",
+                      "conditions": [],
+                      "actions": [
+                        {
+                          "type": {
+                            "value": "SetCenterX"
+                          },
+                          "parameters": [
+                            "Label",
+                            "=",
+                            "Background.CenterX() +  (Object.PropertyLeftPadding() - Object.PropertyRightPadding()) / 2"
+                          ]
+                        }
+                      ]
+                    }
+                  ]
+                },
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [
+                    {
+                      "type": {
+                        "value": "PanelSpriteButton::PanelSpriteButton::IsPressed"
+                      },
+                      "parameters": [
+                        "Object",
+                        ""
+                      ]
+                    }
+                  ],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "MettreY"
+                      },
+                      "parameters": [
+                        "Label",
+                        "+",
+                        "Object.PropertyPressedLabelOffsetY()"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "supplementaryInformation": "PanelSpriteButton::PanelSpriteButton",
+                  "type": "object"
+                }
+              ],
+              "objectGroups": [
+                {
+                  "name": "Background",
+                  "objects": [
+                    {
+                      "name": "Idle"
+                    },
+                    {
+                      "name": "Hovered"
+                    },
+                    {
+                      "name": "Pressed"
+                    }
+                  ]
+                }
+              ]
+            },
+            {
+              "description": "De/activate interactions with the button.",
+              "fullName": "De/activate interactions",
+              "functionType": "Action",
+              "name": "Activate",
+              "sentence": "Activate interactions with _PARAM0_: _PARAM1_",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [
+                    {
+                      "type": {
+                        "value": "GetArgumentAsBoolean"
+                      },
+                      "parameters": [
+                        "\"ShouldActivate\""
+                      ]
+                    }
+                  ],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "ActivateBehavior"
+                      },
+                      "parameters": [
+                        "Idle",
+                        "ButtonFSM",
+                        "yes"
+                      ]
+                    }
+                  ]
+                },
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [
+                    {
+                      "type": {
+                        "inverted": true,
+                        "value": "GetArgumentAsBoolean"
+                      },
+                      "parameters": [
+                        "\"ShouldActivate\""
+                      ]
+                    }
+                  ],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "ActivateBehavior"
+                      },
+                      "parameters": [
+                        "Idle",
+                        "ButtonFSM",
+                        "no"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "supplementaryInformation": "PanelSpriteButton::PanelSpriteButton",
+                  "type": "object"
+                },
+                {
+                  "description": "Activate",
+                  "name": "ShouldActivate",
+                  "type": "yesorno"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "Check if interactions are activated on the button.",
+              "fullName": "Interactions activated",
+              "functionType": "Condition",
+              "name": "IsActivated",
+              "sentence": "Interactions on _PARAM0_ are activated",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [
+                    {
+                      "type": {
+                        "value": "BehaviorActivated"
+                      },
+                      "parameters": [
+                        "Idle",
+                        "ButtonFSM"
+                      ]
+                    }
+                  ],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SetReturnBoolean"
+                      },
+                      "parameters": [
+                        "True"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "supplementaryInformation": "PanelSpriteButton::PanelSpriteButton",
+                  "type": "object"
+                }
+              ],
+              "objectGroups": []
+            }
+          ],
+          "propertyDescriptors": [
+            {
+              "value": "0",
+              "type": "Number",
+              "label": "Label offset on Y axis when pressed",
+              "description": "",
+              "group": "",
+              "extraInformation": [],
+              "name": "PressedLabelOffsetY"
+            },
+            {
+              "value": "0",
+              "type": "Number",
+              "label": "Left padding",
+              "description": "",
+              "group": "Padding",
+              "extraInformation": [
+                "Label"
+              ],
+              "name": "LeftPadding"
+            },
+            {
+              "value": "0",
+              "type": "Number",
+              "label": "Right padding",
+              "description": "",
+              "group": "Padding",
+              "extraInformation": [
+                "Label"
+              ],
+              "name": "RightPadding"
+            },
+            {
+              "value": "0",
+              "type": "Number",
+              "label": "Top padding",
+              "description": "",
+              "group": "Padding",
+              "extraInformation": [
+                "Label"
+              ],
+              "name": "TopPadding"
+            },
+            {
+              "value": "0",
+              "type": "Number",
+              "label": "Bottom padding",
+              "description": "",
+              "group": "Padding",
+              "extraInformation": [
+                "Label"
+              ],
+              "name": "BottomPadding"
+            },
+            {
+              "value": "0.25",
+              "type": "Number",
+              "label": "Hovered fade out duration (in seconds)",
+              "description": "",
+              "group": "",
+              "extraInformation": [],
+              "name": "HoveredFadeOutDuration"
+            }
+          ],
+          "objects": [
+            {
+              "assetStoreId": "",
+              "bold": false,
+              "italic": false,
+              "name": "Label",
+              "smoothed": true,
+              "type": "TextObject::Text",
+              "underlined": false,
+              "variables": [],
+              "effects": [],
+              "behaviors": [],
+              "string": "Text",
+              "font": "",
+              "textAlignment": "",
+              "characterSize": 20,
+              "color": {
+                "b": 0,
+                "g": 0,
+                "r": 0
+              },
+              "content": {
+                "bold": false,
+                "isOutlineEnabled": false,
+                "isShadowEnabled": false,
+                "italic": false,
+                "outlineColor": "255;255;255",
+                "outlineThickness": 2,
+                "shadowAngle": 90,
+                "shadowBlurRadius": 2,
+                "shadowColor": "0;0;0",
+                "shadowDistance": 4,
+                "shadowOpacity": 127,
+                "smoothed": true,
+                "underlined": false,
+                "text": "Text",
+                "font": "",
+                "textAlignment": "",
+                "verticalTextAlignment": "top",
+                "characterSize": 20,
+                "color": "0;0;0"
+              }
+            },
+            {
+              "assetStoreId": "",
+              "bottomMargin": 0,
+              "height": 32,
+              "leftMargin": 0,
+              "name": "Idle",
+              "rightMargin": 0,
+              "texture": "",
+              "tiled": false,
+              "topMargin": 0,
+              "type": "PanelSpriteObject::PanelSprite",
+              "width": 32,
+              "variables": [
+                {
+                  "folded": true,
+                  "name": "State",
+                  "type": "string",
+                  "value": "Idle"
+                }
+              ],
+              "effects": [],
+              "behaviors": [
+                {
+                  "name": "ButtonFSM",
+                  "type": "PanelSpriteButton::ButtonFSM",
+                  "ShouldCheckHovering": true
+                }
+              ]
+            },
+            {
+              "assetStoreId": "",
+              "bottomMargin": 0,
+              "height": 32,
+              "leftMargin": 0,
+              "name": "Hovered",
+              "rightMargin": 0,
+              "texture": "",
+              "tiled": false,
+              "topMargin": 0,
+              "type": "PanelSpriteObject::PanelSprite",
+              "width": 32,
+              "variables": [],
+              "effects": [],
+              "behaviors": [
+                {
+                  "name": "Tween",
+                  "type": "Tween::TweenBehavior"
+                }
+              ]
+            },
+            {
+              "assetStoreId": "",
+              "bottomMargin": 0,
+              "height": 32,
+              "leftMargin": 0,
+              "name": "Pressed",
+              "rightMargin": 0,
+              "texture": "",
+              "tiled": false,
+              "topMargin": 0,
+              "type": "PanelSpriteObject::PanelSprite",
+              "width": 32,
+              "variables": [],
+              "effects": [],
+              "behaviors": []
+            }
+          ],
+          "objectsFolderStructure": {
+            "folderName": "__ROOT",
+            "children": [
+              {
+                "objectName": "Label"
+              },
+              {
+                "objectName": "Idle"
+              },
+              {
+                "objectName": "Hovered"
+              },
+              {
+                "objectName": "Pressed"
+              }
+            ]
+          },
+          "objectsGroups": [],
+          "layers": [
+            {
+              "ambientLightColorB": 200,
+              "ambientLightColorG": 200,
+              "ambientLightColorR": 200,
+              "camera3DFarPlaneDistance": 10000,
+              "camera3DFieldOfView": 45,
+              "camera3DNearPlaneDistance": 3,
+              "cameraType": "",
+              "followBaseLayerCamera": false,
+              "isLightingLayer": false,
+              "isLocked": false,
+              "name": "",
+              "renderingType": "",
+              "visibility": true,
+              "cameras": [
+                {
+                  "defaultSize": true,
+                  "defaultViewport": true,
+                  "height": 0,
+                  "viewportBottom": 1,
+                  "viewportLeft": 0,
+                  "viewportRight": 1,
+                  "viewportTop": 0,
+                  "width": 0
+                }
+              ],
+              "effects": []
+            }
+          ],
+          "instances": []
+        }
+      ]
+    },
+    {
+      "author": "D8H",
+      "category": "Movement",
+      "extensionNamespace": "",
+      "fullName": "Stick objects to others",
+      "helpPath": "",
+      "iconUrl": "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz48IURPQ1RZUEUgc3ZnIFBVQkxJQyAiLS8vVzNDLy9EVEQgU1ZHIDEuMS8vRU4iICJodHRwOi8vd3d3LnczLm9yZy9HcmFwaGljcy9TVkcvMS4xL0RURC9zdmcxMS5kdGQiPjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgdmVyc2lvbj0iMS4xIiBpZD0ibWRpLXN0aWNrZXItb3V0bGluZSIgd2lkdGg9IjI0IiBoZWlnaHQ9IjI0IiB2aWV3Qm94PSIwIDAgMjQgMjQiPjxwYXRoIGQ9Ik01LjUgMkMzLjYgMiAyIDMuNiAyIDUuNVYxOC41QzIgMjAuNCAzLjYgMjIgNS41IDIySDE2TDIyIDE2VjUuNUMyMiAzLjYgMjAuNCAyIDE4LjUgMkg1LjVNNS44IDRIMTguM0MxOS4zIDQgMjAuMSA0LjggMjAuMSA1LjhWMTVIMTguNkMxNi43IDE1IDE1LjEgMTYuNiAxNS4xIDE4LjVWMjBINS44QzQuOCAyMCA0IDE5LjIgNCAxOC4yVjUuOEM0IDQuOCA0LjggNCA1LjggNCIgLz48L3N2Zz4=",
+      "name": "Sticker",
+      "previewIconUrl": "https://resources.gdevelop-app.com/assets/Icons/sticker-outline.svg",
+      "shortDescription": "Make objects follow the position and rotation of the object they are stuck to.",
+      "version": "0.5.1",
+      "description": [
+        "This extension can be useful to:",
+        "* Stick accessories to moving objects",
+        "* Animate a skeleton",
+        "* Delete an object with another one",
+        "",
+        "An example allows to check it out ([open the project online](https://editor.gdevelop.io/?project=example://stick-objects))."
+      ],
+      "origin": {
+        "identifier": "Sticker",
+        "name": "gdevelop-extension-store"
+      },
+      "tags": [
+        "sticker",
+        "stick",
+        "follow",
+        "skeleton",
+        "joint",
+        "pin",
+        "bind",
+        "glue",
+        "tie",
+        "attach",
+        "hold",
+        "paste",
+        "wear"
+      ],
+      "authorIds": [
+        "IWykYNRvhCZBN3vEgKEbBPOR3Oc2"
+      ],
+      "dependencies": [],
+      "globalVariables": [],
+      "sceneVariables": [],
+      "eventsFunctions": [
+        {
+          "description": "Define helper classes JavaScript code.",
+          "fullName": "Define helper classes",
+          "functionType": "Action",
+          "name": "DefineHelperClasses",
+          "private": true,
+          "sentence": "Define helper classes JavaScript code",
+          "events": [
+            {
+              "type": "BuiltinCommonInstructions::JsCode",
+              "inlineCode": [
+                "if (gdjs._stickerExtension) {",
+                "    return;",
+                "}",
+                "",
+                "// Unstick from deleted objects.",
+                "gdjs.registerObjectDeletedFromSceneCallback(function (runtimeScene, deletedObject) {",
+                "    const extension = runtimeScene._stickerExtension;",
+                "    if (!extension) {",
+                "        return;",
+                "    }",
+                "    const allStickers = runtimeScene._stickerExtension.allStickers;",
+                "    for (const behavior of allStickers) {",
+                "        const sticker = behavior._sticker;",
+                "        if (sticker.isStuckTo(deletedObject)) {",
+                "            if (behavior._getIsDestroyedWithParent()) {",
+                "                behavior.owner.deleteFromScene(runtimeScene);",
+                "            }",
+                "            sticker.unstick();",
+                "        }",
+                "    }",
+                "});",
+                "",
+                "class Sticker {",
+                "    /** @type {gdjs.RuntimeBehavior} */",
+                "    behavior;",
+                "    /** @type {gdjs.RuntimeObject | null} */",
+                "    basisObject;",
+                "    followingDoneThisFrame = false;",
+                "    relativeX = 0;",
+                "    relativeY = 0;",
+                "    relativeAngle = 0;",
+                "    relativeRotatedX = 0;",
+                "    relativeRotatedY = 0;",
+                "    basisOldX = 0;",
+                "    basisOldY = 0;",
+                "    basisOldAngle = 0;",
+                "    basisOldWidth = 0;",
+                "    basisOldHeight = 0;",
+                "    basisOldCenterXInScene = 0;",
+                "    basisOldCenterYInScene = 0;",
+                "",
+                "    /**",
+                "     * @param {gdjs.RuntimeBehavior} behavior",
+                "     */",
+                "    constructor(behavior) {",
+                "        this.behavior = behavior;",
+                "    }",
+                "",
+                "    /**",
+                "     * @param {gdjs.RuntimeObject} basisObject",
+                "     */",
+                "    isStuckTo(basisObject) {",
+                "        return this.basisObject === basisObject;",
+                "    }",
+                "",
+                "    /**",
+                "     * @param {gdjs.RuntimeObject} basisObject",
+                "     */",
+                "    stickTo(basisObject) {",
+                "        this.basisObject = basisObject;",
+                "        this.updateOldCoordinates();",
+                "        this.updateRelativeCoordinates();",
+                "    }",
+                "",
+                "    unstick() {",
+                "        this.basisObject = null;",
+                "    }",
+                "",
+                "    onStepPreEvents() {",
+                "        this.followingDoneThisFrame = false;",
+                "    }",
+                "",
+                "    /**",
+                "     * Update the coordinates in the basisObject basis.",
+                "     * ",
+                "     * It uses the basisObject coordinates from the previous frame.",
+                "     * This way, the sticker can move relatively to it and still",
+                "     * follow basisObject.",
+                "     * ",
+                "     * @param {gdjs.RuntimeObject} basisObject",
+                "     */",
+                "    updateRelativeCoordinates() {",
+                "        const object = this.behavior.owner;",
+                "",
+                "        // Update relative coordinates",
+                "        this.relativeX = object.getX() - this.basisOldX;",
+                "        this.relativeY = object.getY() - this.basisOldY;",
+                "        if (!this.behavior._getOnlyFollowPosition()) {",
+                "            this.relativeAngle = object.getAngle() - this.basisOldAngle;",
+                "            this.relativeWidth = object.getWidth() / this.basisOldWidth;",
+                "            this.relativeHeight = object.getHeight() / this.basisOldHeight;",
+                "            const deltaX = object.getCenterXInScene() - this.basisOldCenterXInScene;",
+                "            const deltaY = object.getCenterYInScene() - this.basisOldCenterYInScene;",
+                "            const angle = this.basisOldAngle * Math.PI / 180;",
+                "            this.relativeRotatedX = (deltaX * Math.cos(angle) + deltaY * Math.sin(angle)) / this.basisOldWidth;",
+                "            this.relativeRotatedY = (-deltaX * Math.sin(angle) + deltaY * Math.cos(angle)) / this.basisOldHeight;",
+                "",
+                "            // Save initial values to avoid calculus and rounding errors",
+                "            this.basisOriginalWidth = this.basisObject.getWidth();",
+                "            this.basisOriginalHeight = this.basisObject.getHeight();",
+                "            this.basisOriginalAngle = this.basisObject.getAngle();",
+                "        }",
+                "    }",
+                "",
+                "    /**",
+                "     * Copy the coordinates to use it the next frame.",
+                "     */",
+                "    updateOldCoordinates() {",
+                "        const object = this.behavior.owner;",
+                "",
+                "        this.ownerOldX = object.getX();",
+                "        this.ownerOldY = object.getY();",
+                "",
+                "        this.basisOldX = this.basisObject.getX();",
+                "        this.basisOldY = this.basisObject.getY();",
+                "",
+                "        if (!this.behavior._getOnlyFollowPosition()) {",
+                "            this.ownerOldAngle = object.getAngle();",
+                "            this.ownerOldWidth = object.getWidth();",
+                "            this.ownerOldHeight = object.getHeight();",
+                "",
+                "            this.basisOldAngle = this.basisObject.getAngle();",
+                "            this.basisOldWidth = this.basisObject.getWidth();",
+                "            this.basisOldHeight = this.basisObject.getHeight();",
+                "            this.basisOldCenterXInScene = this.basisObject.getCenterXInScene();",
+                "            this.basisOldCenterYInScene = this.basisObject.getCenterYInScene();",
+                "        }",
+                "    }",
+                "",
+                "    /**",
+                "     * Follow the basisObject (called in doStepPostEvents).",
+                "     * ",
+                "     * This method is also called by children to ensure",
+                "     * parents are updated first.",
+                "     */",
+                "    followBasisObject() {",
+                "        if (this.followingDoneThisFrame) {",
+                "            return;",
+                "        }",
+                "        this.followingDoneThisFrame = true;",
+                "        const basisObject = this.basisObject;",
+                "        if (basisObject) {",
+                "            // If the behavior on the basis object has a different name,",
+                "            // the objects will still follow their basis objects",
+                "            // but frame delays could happen.",
+                "            const behaviorName = this.behavior.getName();",
+                "            if (basisObject.hasBehavior(behaviorName)) {",
+                "                const basisBehavior = basisObject.getBehavior(behaviorName);",
+                "                if (basisBehavior.type === this.behavior.type) {",
+                "                    // Follow parents 1st to avoid frame delays",
+                "                    basisBehavior._sticker.followBasisObject();",
+                "                }",
+                "            }",
+                "",
+                "            const object = this.behavior.owner;",
+                "",
+                "            if (this.behavior._getOnlyFollowPosition()) {",
+                "                if (object.getX() !== this.ownerOldX",
+                "                    || object.getY() !== this.ownerOldY) {",
+                "                    this.updateRelativeCoordinates();",
+                "                }",
+                "",
+                "                if (this.basisOldX !== basisObject.getX() ||",
+                "                    this.basisOldY !== basisObject.getY()) {",
+                "                    object.setPosition(",
+                "                        basisObject.getX() + this.relativeX,",
+                "                        basisObject.getY() + this.relativeY);",
+                "                }",
+                "            } else {",
+                "                if (object.getX() !== this.ownerOldX",
+                "                    || object.getY() !== this.ownerOldY",
+                "                    || object.getAngle() !== this.ownerOldAngle",
+                "                    || object.getWidth() !== this.ownerOldWidth",
+                "                    || object.getHeight() !== this.ownerOldHeight) {",
+                "                    this.updateRelativeCoordinates();",
+                "                }",
+                "",
+                "                // Follow basisObject",
+                "                if (basisObject.getAngle() === this.basisOriginalAngle && this.basisOriginalAngle === 0) {",
+                "                    if (basisObject.getWidth() === this.basisOriginalWidth ||",
+                "                        basisObject.getHeight() === this.basisOriginalHeight) {",
+                "                        if (this.basisOldX !== basisObject.getX() ||",
+                "                            this.basisOldY !== basisObject.getY()) {",
+                "                            object.setPosition(",
+                "                                basisObject.getX() + this.relativeX,",
+                "                                basisObject.getY() + this.relativeY);",
+                "                        }",
+                "                    } else {",
+                "                        object.setCenterPositionInScene(",
+                "                            basisObject.getCenterXInScene() + this.relativeRotatedX * basisObject.getWidth(),",
+                "                            basisObject.getCenterYInScene() + this.relativeRotatedY * basisObject.getHeight());",
+                "                    }",
+                "                } else {",
+                "                    object.setAngle(basisObject.getAngle() + this.relativeAngle);",
+                "",
+                "                    const deltaX = this.relativeRotatedX * basisObject.getWidth();",
+                "                    const deltaY = this.relativeRotatedY * basisObject.getHeight();",
+                "                    const angle = -basisObject.getAngle() * Math.PI / 180;",
+                "                    object.setX(basisObject.getCenterXInScene() + object.getX() - object.getCenterXInScene() + deltaX * Math.cos(angle) + deltaY * Math.sin(angle));",
+                "                    object.setY(basisObject.getCenterYInScene() + object.getY() - object.getCenterYInScene() - deltaX * Math.sin(angle) + deltaY * Math.cos(angle));",
+                "                }",
+                "                // Unproportional dimensions changes won't work as expected",
+                "                // if the object angle is not null but nothing more can be done",
+                "                // because there is no full affine transformation on objects.",
+                "                if (basisObject.getWidth() !== this.basisOriginalWidth) {",
+                "                    object.setWidth(this.relativeWidth * basisObject.getWidth());",
+                "                }",
+                "                if (basisObject.getHeight() !== this.basisOriginalHeight) {",
+                "                    object.setHeight(this.relativeHeight * basisObject.getHeight());",
+                "                }",
+                "            }",
+                "",
+                "            this.updateOldCoordinates();",
+                "        }",
+                "    }",
+                "}",
+                "",
+                "gdjs._stickerExtension = {",
+                "    Sticker",
+                "}"
+              ],
+              "parameterObjects": "",
+              "useStrict": true,
+              "eventsSheetExpanded": true
+            }
+          ],
+          "parameters": [],
+          "objectGroups": []
+        },
+        {
+          "description": "Check if the object is stuck to another object.",
+          "fullName": "Is stuck to another object",
+          "functionType": "Condition",
+          "name": "IsStuck",
+          "sentence": "_PARAM1_ is stuck to _PARAM3_",
+          "events": [
+            {
+              "type": "BuiltinCommonInstructions::JsCode",
+              "inlineCode": [
+                "const stickerBehaviorName = eventsFunctionContext.getBehaviorName(\"Behavior\");",
+                "/** @type {Hashtable<gdjs.RuntimeObject[]>} */",
+                "const stickerObjectsLists = eventsFunctionContext.getObjectsLists(\"Object\");",
+                "/** @type {Hashtable<gdjs.RuntimeObject[]>} */",
+                "const basisObjectsLists = eventsFunctionContext.getObjectsLists(\"BasisObject\");",
+                "",
+                "eventsFunctionContext.returnValue = gdjs.evtTools.object.twoListsTest(",
+                "  (stickerObject, basisObject) => {",
+                "    const sticker = stickerObject.getBehavior(stickerBehaviorName)._sticker;",
+                "    return sticker.isStuckTo(basisObject);",
+                "  },",
+                "  stickerObjectsLists,",
+                "  basisObjectsLists,",
+                "  false",
+                ");"
+              ],
+              "parameterObjects": "",
+              "useStrict": true,
+              "eventsSheetExpanded": true
+            }
+          ],
+          "parameters": [
+            {
+              "description": "Sticker",
+              "name": "Object",
+              "type": "objectList"
+            },
+            {
+              "description": "Sticker behavior",
+              "name": "Behavior",
+              "supplementaryInformation": "Sticker::Sticker",
+              "type": "behavior"
+            },
+            {
+              "description": "Basis",
+              "name": "BasisObject",
+              "type": "objectList"
+            }
+          ],
+          "objectGroups": []
+        }
+      ],
+      "eventsBasedBehaviors": [
+        {
+          "description": "Stick the object to another. Use the action to stick the object, or unstick it later.",
+          "fullName": "Sticker",
+          "name": "Sticker",
+          "objectType": "",
+          "eventsFunctions": [
+            {
+              "fullName": "",
+              "functionType": "Action",
+              "name": "onCreated",
+              "sentence": "",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "Sticker::DefineHelperClasses"
+                      },
+                      "parameters": [
+                        "",
+                        ""
+                      ]
+                    }
+                  ]
+                },
+                {
+                  "type": "BuiltinCommonInstructions::JsCode",
+                  "inlineCode": [
+                    "const Sticker = gdjs._stickerExtension.Sticker;",
+                    "",
+                    "const behaviorName = eventsFunctionContext.getBehaviorName(\"Behavior\");",
+                    "const object = objects[0];",
+                    "const behavior = object.getBehavior(behaviorName);",
+                    "",
+                    "behavior._sticker = new Sticker(behavior);",
+                    "",
+                    "// Set up the scene sticker objects list - if not done already.",
+                    "runtimeScene._stickerExtension = runtimeScene._stickerExtension || {",
+                    "    allStickers: new Set(),",
+                    "};",
+                    "// Register this object as a sticker.",
+                    "runtimeScene._stickerExtension.allStickers.add(behavior);",
+                    ""
+                  ],
+                  "parameterObjects": "Object",
+                  "useStrict": true,
+                  "eventsSheetExpanded": true
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "Sticker::Sticker",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "fullName": "",
+              "functionType": "Action",
+              "name": "doStepPreEvents",
+              "sentence": "",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::JsCode",
+                  "inlineCode": [
+                    "const behaviorName = eventsFunctionContext.getBehaviorName(\"Behavior\");",
+                    "const object = objects[0];",
+                    "const behavior = object.getBehavior(behaviorName);",
+                    "",
+                    "behavior._sticker.onStepPreEvents();"
+                  ],
+                  "parameterObjects": "Object",
+                  "useStrict": true,
+                  "eventsSheetExpanded": false
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "Sticker::Sticker",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "fullName": "",
+              "functionType": "Action",
+              "name": "doStepPostEvents",
+              "sentence": "",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::JsCode",
+                  "inlineCode": [
+                    "const behaviorName = eventsFunctionContext.getBehaviorName(\"Behavior\");",
+                    "const object = objects[0];",
+                    "const behavior = object.getBehavior(behaviorName);",
+                    "",
+                    "behavior._sticker.followBasisObject();"
+                  ],
+                  "parameterObjects": "Object",
+                  "useStrict": true,
+                  "eventsSheetExpanded": true
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "Sticker::Sticker",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "Stick on another object.",
+              "fullName": "Stick",
+              "functionType": "Action",
+              "name": "Stick",
+              "sentence": "Stick _PARAM0_ to _PARAM2_",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::JsCode",
+                  "inlineCode": [
+                    "const object = objects[0];",
+                    "const behaviorName = eventsFunctionContext.getBehaviorName(\"Behavior\");",
+                    "const basisObjects = eventsFunctionContext.getObjects(\"BasisObject\");",
+                    "",
+                    "if (basisObjects.length === 0) return;",
+                    "// An object can stick to only one object.",
+                    "const basisObject = basisObjects[0];",
+                    "object.getBehavior(behaviorName)._sticker.stickTo(basisObject);",
+                    ""
+                  ],
+                  "parameterObjects": "Object",
+                  "useStrict": true,
+                  "eventsSheetExpanded": true
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "Sticker::Sticker",
+                  "type": "behavior"
+                },
+                {
+                  "description": "Object to stick to",
+                  "name": "BasisObject",
+                  "type": "objectList"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "Unstick from the object it was stuck to.",
+              "fullName": "Unstick",
+              "functionType": "Action",
+              "name": "Unstick",
+              "sentence": "Unstick _PARAM0_",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::JsCode",
+                  "inlineCode": [
+                    "const object = objects[0];",
+                    "const behaviorName = eventsFunctionContext.getBehaviorName(\"Behavior\");",
+                    "const behavior = object.getBehavior(behaviorName);",
+                    "",
+                    "object.getBehavior(behaviorName)._sticker.unstick();",
+                    ""
+                  ],
+                  "parameterObjects": "Object",
+                  "useStrict": true,
+                  "eventsSheetExpanded": true
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "Sticker::Sticker",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "fullName": "",
+              "functionType": "Action",
+              "name": "onDestroy",
+              "sentence": "",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::JsCode",
+                  "inlineCode": [
+                    "const behaviorName = eventsFunctionContext.getBehaviorName(\"Behavior\");",
+                    "const object = objects[0];",
+                    "const behavior = object.getBehavior(behaviorName);",
+                    "",
+                    "runtimeScene._stickerExtension.allStickers.delete(behavior._sticker);",
+                    ""
+                  ],
+                  "parameterObjects": "Object",
+                  "useStrict": true,
+                  "eventsSheetExpanded": true
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "Sticker::Sticker",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            }
+          ],
+          "propertyDescriptors": [
+            {
+              "value": "",
+              "type": "Boolean",
+              "label": "Only follow the position",
+              "description": "",
+              "group": "",
+              "extraInformation": [],
+              "name": "OnlyFollowPosition"
+            },
+            {
+              "value": "",
+              "type": "Boolean",
+              "label": "Destroy when the object it's stuck on is destroyed",
+              "description": "",
+              "group": "",
+              "extraInformation": [],
+              "name": "IsDestroyedWithParent"
+            }
+          ],
+          "sharedPropertyDescriptors": []
+        }
+      ],
+      "eventsBasedObjects": []
+    },
+    {
+      "author": "Tristan Rhodes (tristan@victrisgames.com), Entropy",
+      "category": "Movement",
+      "extensionNamespace": "",
+      "fullName": "Screen wrap",
+      "helpPath": "",
+      "iconUrl": "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz48IURPQ1RZUEUgc3ZnIFBVQkxJQyAiLS8vVzNDLy9EVEQgU1ZHIDEuMS8vRU4iICJodHRwOi8vd3d3LnczLm9yZy9HcmFwaGljcy9TVkcvMS4xL0RURC9zdmcxMS5kdGQiPjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgdmVyc2lvbj0iMS4xIiBpZD0ibWRpLW1vbml0b3Itc2NyZWVuc2hvdCIgd2lkdGg9IjI0IiBoZWlnaHQ9IjI0IiB2aWV3Qm94PSIwIDAgMjQgMjQiPjxwYXRoIGQ9Ik05LDZINVYxMEg3VjhIOU0xOSwxMEgxN1YxMkgxNVYxNEgxOU0yMSwxNkgzVjRIMjFNMjEsMkgzQzEuODksMiAxLDIuODkgMSw0VjE2QTIsMiAwIDAsMCAzLDE4SDEwVjIwSDhWMjJIMTZWMjBIMTRWMThIMjFBMiwyIDAgMCwwIDIzLDE2VjRDMjMsMi44OSAyMi4xLDIgMjEsMiIgLz48L3N2Zz4=",
+      "name": "ScreenWrap",
+      "previewIconUrl": "https://resources.gdevelop-app.com/assets/Icons/monitor-screenshot.svg",
+      "shortDescription": "Teleport object when it moves off the screen and immediately appear on the opposite side while maintaining speed and trajectory.",
+      "version": "0.2.5",
+      "description": [
+        "The teleport happens when the center point of the object crosses a border (this can be adjusted with an offset).",
+        "By default, the borders of the wrapping area match the screen size, but they can alo be changed.",
+        "",
+        "The Asteroid-like example uses this extension ([open the project online](https://editor.gdevelop.io/?project=example://space-asteroid))."
+      ],
+      "origin": {
+        "identifier": "ScreenWrap",
+        "name": "gdevelop-extension-store"
+      },
+      "tags": [
+        "screen",
+        "wrap",
+        "teleport",
+        "asteroids"
+      ],
+      "authorIds": [
+        "q8ubdigLvIRXLxsJDDTaokO41mc2",
+        "gqDaZjCfevOOxBYkK6zlhtZnXCg1",
+        "1OgYzWp5UeVPbiWGJwI6vqfgZLC3"
+      ],
+      "dependencies": [],
+      "globalVariables": [],
+      "sceneVariables": [],
+      "eventsFunctions": [],
+      "eventsBasedBehaviors": [
+        {
+          "description": "Teleport the object when leaving one side of the screen so that it immediately reappears on the opposite side, maintaining speed and trajectory.",
+          "fullName": "Screen Wrap",
+          "name": "ScreenWrap",
+          "objectType": "",
+          "eventsFunctions": [
+            {
+              "fullName": "",
+              "functionType": "Action",
+              "name": "onCreated",
+              "sentence": "",
+              "events": [
+                {
+                  "colorB": 228,
+                  "colorG": 176,
+                  "colorR": 74,
+                  "creationTime": 0,
+                  "name": "Initialize variables (if needed)",
+                  "source": "",
+                  "type": "BuiltinCommonInstructions::Group",
+                  "events": [
+                    {
+                      "type": "BuiltinCommonInstructions::Standard",
+                      "conditions": [
+                        {
+                          "type": {
+                            "value": "ScreenWrap::ScreenWrap::PropertyBorderBottom"
+                          },
+                          "parameters": [
+                            "Object",
+                            "Behavior",
+                            "=",
+                            "0"
+                          ]
+                        }
+                      ],
+                      "actions": [
+                        {
+                          "type": {
+                            "value": "ScreenWrap::ScreenWrap::SetBottomBorder"
+                          },
+                          "parameters": [
+                            "Object",
+                            "Behavior",
+                            "SceneWindowHeight()",
+                            ""
+                          ]
+                        }
+                      ]
+                    },
+                    {
+                      "type": "BuiltinCommonInstructions::Standard",
+                      "conditions": [
+                        {
+                          "type": {
+                            "value": "ScreenWrap::ScreenWrap::PropertyBorderRight"
+                          },
+                          "parameters": [
+                            "Object",
+                            "Behavior",
+                            "=",
+                            "0"
+                          ]
+                        }
+                      ],
+                      "actions": [
+                        {
+                          "type": {
+                            "value": "ScreenWrap::ScreenWrap::SetRightBorder"
+                          },
+                          "parameters": [
+                            "Object",
+                            "Behavior",
+                            "SceneWindowWidth()",
+                            ""
+                          ]
+                        }
+                      ]
+                    }
+                  ],
+                  "parameters": []
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "ScreenWrap::ScreenWrap",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "fullName": "",
+              "functionType": "Action",
+              "name": "doStepPostEvents",
+              "sentence": "",
+              "events": [
+                {
+                  "colorB": 5,
+                  "colorG": 117,
+                  "colorR": 65,
+                  "creationTime": 0,
+                  "name": "ScreenWrap",
+                  "source": "",
+                  "type": "BuiltinCommonInstructions::Group",
+                  "events": [
+                    {
+                      "colorB": 228,
+                      "colorG": 176,
+                      "colorR": 74,
+                      "creationTime": 0,
+                      "name": "Move object to opposite side (if needed)",
+                      "source": "",
+                      "type": "BuiltinCommonInstructions::Group",
+                      "events": [],
+                      "parameters": []
+                    },
+                    {
+                      "type": "BuiltinCommonInstructions::Standard",
+                      "conditions": [
+                        {
+                          "type": {
+                            "value": "ScreenWrap::ScreenWrap::PropertyHorizontalWrapping"
+                          },
+                          "parameters": [
+                            "Object",
+                            "Behavior"
+                          ]
+                        }
+                      ],
+                      "actions": [],
+                      "events": [
+                        {
+                          "type": "BuiltinCommonInstructions::Standard",
+                          "conditions": [
+                            {
+                              "type": {
+                                "value": "PosX"
+                              },
+                              "parameters": [
+                                "Object",
+                                "<",
+                                "BorderLeft - (Object.Width()/2) - TriggerOffset"
+                              ]
+                            }
+                          ],
+                          "actions": [
+                            {
+                              "type": {
+                                "value": "MettreX"
+                              },
+                              "parameters": [
+                                "Object",
+                                "=",
+                                "BorderRight - (Object.Width()/2) +  TriggerOffset"
+                              ]
+                            }
+                          ]
+                        },
+                        {
+                          "type": "BuiltinCommonInstructions::Standard",
+                          "conditions": [
+                            {
+                              "type": {
+                                "value": "PosX"
+                              },
+                              "parameters": [
+                                "Object",
+                                ">",
+                                "BorderRight - (Object.Width()/2) + TriggerOffset"
+                              ]
+                            }
+                          ],
+                          "actions": [
+                            {
+                              "type": {
+                                "value": "MettreX"
+                              },
+                              "parameters": [
+                                "Object",
+                                "=",
+                                "BorderLeft - (Object.Width()/2) -  TriggerOffset"
+                              ]
+                            }
+                          ]
+                        }
+                      ]
+                    },
+                    {
+                      "type": "BuiltinCommonInstructions::Standard",
+                      "conditions": [
+                        {
+                          "type": {
+                            "value": "ScreenWrap::ScreenWrap::PropertyVerticalWrapping"
+                          },
+                          "parameters": [
+                            "Object",
+                            "Behavior"
+                          ]
+                        }
+                      ],
+                      "actions": [],
+                      "events": [
+                        {
+                          "type": "BuiltinCommonInstructions::Standard",
+                          "conditions": [
+                            {
+                              "type": {
+                                "value": "PosY"
+                              },
+                              "parameters": [
+                                "Object",
+                                "<",
+                                "BorderTop - (Object.Height()/2) - TriggerOffset"
+                              ]
+                            }
+                          ],
+                          "actions": [
+                            {
+                              "type": {
+                                "value": "MettreY"
+                              },
+                              "parameters": [
+                                "Object",
+                                "=",
+                                "BorderBottom - (Object.Height()/2) + TriggerOffset"
+                              ]
+                            }
+                          ]
+                        },
+                        {
+                          "type": "BuiltinCommonInstructions::Standard",
+                          "conditions": [
+                            {
+                              "type": {
+                                "value": "PosY"
+                              },
+                              "parameters": [
+                                "Object",
+                                ">",
+                                "BorderBottom - (Object.Height()/2) + TriggerOffset"
+                              ]
+                            }
+                          ],
+                          "actions": [
+                            {
+                              "type": {
+                                "value": "MettreY"
+                              },
+                              "parameters": [
+                                "Object",
+                                "=",
+                                "BorderTop - (Object.Height()/2) -  TriggerOffset"
+                              ]
+                            }
+                          ]
+                        }
+                      ]
+                    }
+                  ],
+                  "parameters": []
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "ScreenWrap::ScreenWrap",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "Check if the object is wrapping on the left and right borders.",
+              "fullName": "Is horizontal wrapping",
+              "functionType": "Condition",
+              "name": "IsHorizontalWrapping",
+              "sentence": "_PARAM0_ is horizontal wrapping",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [
+                    {
+                      "type": {
+                        "inverted": true,
+                        "value": "ScreenWrap::ScreenWrap::PropertyHorizontalWrapping"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior"
+                      ]
+                    }
+                  ],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SetReturnBoolean"
+                      },
+                      "parameters": [
+                        ""
+                      ]
+                    }
+                  ]
+                },
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [
+                    {
+                      "type": {
+                        "value": "ScreenWrap::ScreenWrap::PropertyHorizontalWrapping"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior"
+                      ]
+                    }
+                  ],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SetReturnBoolean"
+                      },
+                      "parameters": [
+                        "True"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "ScreenWrap::ScreenWrap",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "Check if the object is wrapping on the top and bottom borders.",
+              "fullName": "Is vertical wrapping",
+              "functionType": "Condition",
+              "name": "IsVerticalWrapping",
+              "sentence": "_PARAM0_ is vertical wrapping",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [
+                    {
+                      "type": {
+                        "inverted": true,
+                        "value": "ScreenWrap::ScreenWrap::PropertyVerticalWrapping"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior"
+                      ]
+                    }
+                  ],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SetReturnBoolean"
+                      },
+                      "parameters": [
+                        ""
+                      ]
+                    }
+                  ]
+                },
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [
+                    {
+                      "type": {
+                        "value": "ScreenWrap::ScreenWrap::PropertyVerticalWrapping"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior"
+                      ]
+                    }
+                  ],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SetReturnBoolean"
+                      },
+                      "parameters": [
+                        "True"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "ScreenWrap::ScreenWrap",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "Enable wrapping on the left and right borders.",
+              "fullName": "Enable horizontal wrapping",
+              "functionType": "Action",
+              "name": "EnableHorizontalWrapping",
+              "sentence": "Enable _PARAM0_ horizontal wrapping: _PARAM2_",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [
+                    {
+                      "type": {
+                        "inverted": true,
+                        "value": "GetArgumentAsBoolean"
+                      },
+                      "parameters": [
+                        "\"EnableHorizontalWrapping\""
+                      ]
+                    }
+                  ],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "ScreenWrap::ScreenWrap::SetPropertyHorizontalWrapping"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "no"
+                      ]
+                    }
+                  ]
+                },
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [
+                    {
+                      "type": {
+                        "value": "GetArgumentAsBoolean"
+                      },
+                      "parameters": [
+                        "\"EnableHorizontalWrapping\""
+                      ]
+                    }
+                  ],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "ScreenWrap::ScreenWrap::SetPropertyHorizontalWrapping"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "yes"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "ScreenWrap::ScreenWrap",
+                  "type": "behavior"
+                },
+                {
+                  "description": "Value",
+                  "name": "EnableHorizontalWrapping",
+                  "type": "yesorno"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "Enable wrapping on the top and bottom borders.",
+              "fullName": "Enable vertical wrapping",
+              "functionType": "Action",
+              "name": "EnableVerticalWrapping",
+              "sentence": "Enable _PARAM0_ vertical wrapping: _PARAM2_",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [
+                    {
+                      "type": {
+                        "inverted": true,
+                        "value": "GetArgumentAsBoolean"
+                      },
+                      "parameters": [
+                        "\"EnableVerticalWrapping\""
+                      ]
+                    }
+                  ],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "ScreenWrap::ScreenWrap::SetPropertyVerticalWrapping"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "no"
+                      ]
+                    }
+                  ]
+                },
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [
+                    {
+                      "type": {
+                        "value": "GetArgumentAsBoolean"
+                      },
+                      "parameters": [
+                        "\"EnableVerticalWrapping\""
+                      ]
+                    }
+                  ],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "ScreenWrap::ScreenWrap::SetPropertyVerticalWrapping"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "yes"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "ScreenWrap::ScreenWrap",
+                  "type": "behavior"
+                },
+                {
+                  "description": "Value",
+                  "name": "EnableVerticalWrapping",
+                  "type": "yesorno"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "Top border (Y position).",
+              "fullName": "Top border",
+              "functionType": "Expression",
+              "name": "BorderTop",
+              "sentence": "",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SetReturnNumber"
+                      },
+                      "parameters": [
+                        "BorderTop"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "expressionType": {
+                "type": "expression"
+              },
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "ScreenWrap::ScreenWrap",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "Left border (X position).",
+              "fullName": "Left border",
+              "functionType": "Expression",
+              "name": "BorderLeft",
+              "sentence": "",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SetReturnNumber"
+                      },
+                      "parameters": [
+                        "BorderLeft"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "expressionType": {
+                "type": "expression"
+              },
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "ScreenWrap::ScreenWrap",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "Right border (X position).",
+              "fullName": "Right border",
+              "functionType": "Expression",
+              "name": "BorderRight",
+              "sentence": "",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SetReturnNumber"
+                      },
+                      "parameters": [
+                        "BorderRight"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "expressionType": {
+                "type": "expression"
+              },
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "ScreenWrap::ScreenWrap",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "Bottom border (Y position).",
+              "fullName": "Bottom border",
+              "functionType": "Expression",
+              "name": "BorderBottom",
+              "sentence": "",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SetReturnNumber"
+                      },
+                      "parameters": [
+                        "BorderBottom"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "expressionType": {
+                "type": "expression"
+              },
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "ScreenWrap::ScreenWrap",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "Number of pixels past the center where the object teleports and appears.",
+              "fullName": "Trigger offset",
+              "functionType": "Expression",
+              "name": "TriggerOffset",
+              "sentence": "",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SetReturnNumber"
+                      },
+                      "parameters": [
+                        "TriggerOffset"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "expressionType": {
+                "type": "expression"
+              },
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "ScreenWrap::ScreenWrap",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "Set top border (Y position).",
+              "fullName": "Set top border",
+              "functionType": "Action",
+              "name": "SetTopBorder",
+              "sentence": "Set _PARAM0_ top border to _PARAM2_",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "ScreenWrap::ScreenWrap::SetPropertyBorderTop"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "=",
+                        "Value"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "ScreenWrap::ScreenWrap",
+                  "type": "behavior"
+                },
+                {
+                  "description": "Top border value",
+                  "name": "Value",
+                  "type": "expression"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "Set left border (X position).",
+              "fullName": "Set left border",
+              "functionType": "Action",
+              "name": "SetLeftBorder",
+              "sentence": "Set _PARAM0_ left border to _PARAM2_",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "ScreenWrap::ScreenWrap::SetPropertyBorderLeft"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "=",
+                        "Value"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "ScreenWrap::ScreenWrap",
+                  "type": "behavior"
+                },
+                {
+                  "description": "Left border value",
+                  "name": "Value",
+                  "type": "expression"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "Set bottom border (Y position).",
+              "fullName": "Set bottom border",
+              "functionType": "Action",
+              "name": "SetBottomBorder",
+              "sentence": "Set _PARAM0_ bottom border to _PARAM2_",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "ScreenWrap::ScreenWrap::SetPropertyBorderBottom"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "=",
+                        "Value"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "ScreenWrap::ScreenWrap",
+                  "type": "behavior"
+                },
+                {
+                  "description": "Bottom border value",
+                  "name": "Value",
+                  "type": "expression"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "Set right border (X position).",
+              "fullName": "Set right border",
+              "functionType": "Action",
+              "name": "SetRightBorder",
+              "sentence": "Set _PARAM0_ right border to _PARAM2_",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "ScreenWrap::ScreenWrap::SetPropertyBorderRight"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "=",
+                        "Value"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "ScreenWrap::ScreenWrap",
+                  "type": "behavior"
+                },
+                {
+                  "description": "Right border value",
+                  "name": "Value",
+                  "type": "expression"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "Set trigger offset (pixels).",
+              "fullName": "Set trigger offset",
+              "functionType": "Action",
+              "name": "SetTriggerOffset",
+              "sentence": "Set _PARAM0_ trigger offset to _PARAM2_ pixels",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "ScreenWrap::ScreenWrap::SetPropertyTriggerOffset"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "=",
+                        "Value"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "ScreenWrap::ScreenWrap",
+                  "type": "behavior"
+                },
+                {
+                  "description": "SetScreen Offset Leaving Value",
+                  "name": "Value",
+                  "type": "expression"
+                }
+              ],
+              "objectGroups": []
+            }
+          ],
+          "propertyDescriptors": [
+            {
+              "value": "true",
+              "type": "Boolean",
+              "label": "Horizontal wrapping",
+              "description": "",
+              "group": "",
+              "extraInformation": [],
+              "name": "HorizontalWrapping"
+            },
+            {
+              "value": "true",
+              "type": "Boolean",
+              "label": "Vertical wrapping",
+              "description": "",
+              "group": "",
+              "extraInformation": [],
+              "name": "VerticalWrapping"
+            },
+            {
+              "value": "0",
+              "type": "Number",
+              "unit": "Pixel",
+              "label": "Top border of wrapped area (Y)",
+              "description": "",
+              "group": "",
+              "extraInformation": [],
+              "name": "BorderTop"
+            },
+            {
+              "value": "0",
+              "type": "Number",
+              "unit": "Pixel",
+              "label": "Left border of wrapped area (X)",
+              "description": "",
+              "group": "",
+              "extraInformation": [],
+              "name": "BorderLeft"
+            },
+            {
+              "value": "0",
+              "type": "Number",
+              "unit": "Pixel",
+              "label": "Right border of wrapped area (X)",
+              "description": "If blank, the value will be the scene width.",
+              "group": "",
+              "extraInformation": [],
+              "name": "BorderRight"
+            },
+            {
+              "value": "0",
+              "type": "Number",
+              "unit": "Pixel",
+              "label": "Bottom border of wrapped area (Y)",
+              "description": "If blank, the value will be scene height.",
+              "group": "",
+              "extraInformation": [],
+              "name": "BorderBottom"
+            },
+            {
+              "value": "0",
+              "type": "Number",
+              "unit": "Pixel",
+              "label": "Number of pixels past the center where the object teleports and appears",
+              "description": "",
+              "group": "",
+              "extraInformation": [],
+              "name": "TriggerOffset"
+            }
+          ],
+          "sharedPropertyDescriptors": []
+        },
+        {
+          "description": "Teleport the object when leaving one side of the screen so that it immediately reappears on the opposite side, maintaining speed and trajectory.",
+          "fullName": "Screen Wrap (physics objects)",
+          "name": "ScreenWrapPhysics",
+          "objectType": "",
+          "eventsFunctions": [
+            {
+              "fullName": "",
+              "functionType": "Action",
+              "name": "onCreated",
+              "sentence": "",
+              "events": [
+                {
+                  "colorB": 228,
+                  "colorG": 176,
+                  "colorR": 74,
+                  "creationTime": 0,
+                  "name": "Initialize variables (if needed)",
+                  "source": "",
+                  "type": "BuiltinCommonInstructions::Group",
+                  "events": [
+                    {
+                      "type": "BuiltinCommonInstructions::Standard",
+                      "conditions": [
+                        {
+                          "type": {
+                            "value": "ScreenWrap::ScreenWrapPhysics::PropertyBorderBottom"
+                          },
+                          "parameters": [
+                            "Object",
+                            "Behavior",
+                            "=",
+                            "0"
+                          ]
+                        }
+                      ],
+                      "actions": [
+                        {
+                          "type": {
+                            "value": "ScreenWrap::ScreenWrapPhysics::SetBottomBorder"
+                          },
+                          "parameters": [
+                            "Object",
+                            "Behavior",
+                            "SceneWindowHeight()",
+                            ""
+                          ]
+                        }
+                      ]
+                    },
+                    {
+                      "type": "BuiltinCommonInstructions::Standard",
+                      "conditions": [
+                        {
+                          "type": {
+                            "value": "ScreenWrap::ScreenWrapPhysics::PropertyBorderRight"
+                          },
+                          "parameters": [
+                            "Object",
+                            "Behavior",
+                            "=",
+                            "0"
+                          ]
+                        }
+                      ],
+                      "actions": [
+                        {
+                          "type": {
+                            "value": "ScreenWrap::ScreenWrapPhysics::SetRightBorder"
+                          },
+                          "parameters": [
+                            "Object",
+                            "Behavior",
+                            "SceneWindowWidth()",
+                            ""
+                          ]
+                        }
+                      ]
+                    }
+                  ],
+                  "parameters": []
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "ScreenWrap::ScreenWrapPhysics",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "fullName": "",
+              "functionType": "Action",
+              "name": "doStepPreEvents",
+              "sentence": "",
+              "events": [
+                {
+                  "colorB": 5,
+                  "colorG": 117,
+                  "colorR": 65,
+                  "creationTime": 0,
+                  "name": "ScreenWrap",
+                  "source": "",
+                  "type": "BuiltinCommonInstructions::Group",
+                  "events": [
+                    {
+                      "colorB": 228,
+                      "colorG": 176,
+                      "colorR": 74,
+                      "creationTime": 0,
+                      "name": "Move object to opposite side (if needed)",
+                      "source": "",
+                      "type": "BuiltinCommonInstructions::Group",
+                      "events": [],
+                      "parameters": []
+                    },
+                    {
+                      "type": "BuiltinCommonInstructions::Standard",
+                      "conditions": [
+                        {
+                          "type": {
+                            "value": "ScreenWrap::ScreenWrapPhysics::IsHorizontalWrapping"
+                          },
+                          "parameters": [
+                            "Object",
+                            "Behavior",
+                            ""
+                          ]
+                        }
+                      ],
+                      "actions": [],
+                      "events": [
+                        {
+                          "type": "BuiltinCommonInstructions::Comment",
+                          "color": {
+                            "b": 109,
+                            "g": 230,
+                            "r": 255,
+                            "textB": 0,
+                            "textG": 0,
+                            "textR": 0
+                          },
+                          "comment": "Velocity is saved because Physics2 resets objects velocities when they are moved from the outside of the extension."
+                        },
+                        {
+                          "type": "BuiltinCommonInstructions::Standard",
+                          "conditions": [
+                            {
+                              "type": {
+                                "value": "PosX"
+                              },
+                              "parameters": [
+                                "Object",
+                                "<",
+                                "BorderLeft - (Object.Width()/2) - TriggerOffset"
+                              ]
+                            }
+                          ],
+                          "actions": [
+                            {
+                              "type": {
+                                "value": "ScreenWrap::ScreenWrapPhysics::SaveCurrentVelocities"
+                              },
+                              "parameters": [
+                                "Object",
+                                "Behavior",
+                                ""
+                              ]
+                            },
+                            {
+                              "type": {
+                                "value": "MettreX"
+                              },
+                              "parameters": [
+                                "Object",
+                                "=",
+                                "BorderRight - (Object.Width()/2) +  TriggerOffset"
+                              ]
+                            },
+                            {
+                              "type": {
+                                "value": "ScreenWrap::ScreenWrapPhysics::ApplySavedVelocities"
+                              },
+                              "parameters": [
+                                "Object",
+                                "Behavior",
+                                ""
+                              ]
+                            }
+                          ]
+                        },
+                        {
+                          "type": "BuiltinCommonInstructions::Standard",
+                          "conditions": [
+                            {
+                              "type": {
+                                "value": "PosX"
+                              },
+                              "parameters": [
+                                "Object",
+                                ">",
+                                "BorderRight - (Object.Width()/2) + TriggerOffset"
+                              ]
+                            }
+                          ],
+                          "actions": [
+                            {
+                              "type": {
+                                "value": "ScreenWrap::ScreenWrapPhysics::SaveCurrentVelocities"
+                              },
+                              "parameters": [
+                                "Object",
+                                "Behavior",
+                                ""
+                              ]
+                            },
+                            {
+                              "type": {
+                                "value": "MettreX"
+                              },
+                              "parameters": [
+                                "Object",
+                                "=",
+                                "BorderLeft - (Object.Width()/2) -  TriggerOffset"
+                              ]
+                            },
+                            {
+                              "type": {
+                                "value": "ScreenWrap::ScreenWrapPhysics::ApplySavedVelocities"
+                              },
+                              "parameters": [
+                                "Object",
+                                "Behavior",
+                                ""
+                              ]
+                            }
+                          ]
+                        }
+                      ]
+                    },
+                    {
+                      "type": "BuiltinCommonInstructions::Standard",
+                      "conditions": [
+                        {
+                          "type": {
+                            "value": "ScreenWrap::ScreenWrapPhysics::IsVerticalWrapping"
+                          },
+                          "parameters": [
+                            "Object",
+                            "Behavior",
+                            ""
+                          ]
+                        }
+                      ],
+                      "actions": [],
+                      "events": [
+                        {
+                          "type": "BuiltinCommonInstructions::Standard",
+                          "conditions": [
+                            {
+                              "type": {
+                                "value": "PosY"
+                              },
+                              "parameters": [
+                                "Object",
+                                "<",
+                                "BorderTop - (Object.Height()/2) - TriggerOffset"
+                              ]
+                            }
+                          ],
+                          "actions": [
+                            {
+                              "type": {
+                                "value": "ScreenWrap::ScreenWrapPhysics::SaveCurrentVelocities"
+                              },
+                              "parameters": [
+                                "Object",
+                                "Behavior",
+                                ""
+                              ]
+                            },
+                            {
+                              "type": {
+                                "value": "MettreY"
+                              },
+                              "parameters": [
+                                "Object",
+                                "=",
+                                "BorderBottom - (Object.Height()/2) + TriggerOffset"
+                              ]
+                            },
+                            {
+                              "type": {
+                                "value": "ScreenWrap::ScreenWrapPhysics::ApplySavedVelocities"
+                              },
+                              "parameters": [
+                                "Object",
+                                "Behavior",
+                                ""
+                              ]
+                            }
+                          ]
+                        },
+                        {
+                          "type": "BuiltinCommonInstructions::Standard",
+                          "conditions": [
+                            {
+                              "type": {
+                                "value": "PosY"
+                              },
+                              "parameters": [
+                                "Object",
+                                ">",
+                                "BorderBottom - (Object.Height()/2) + TriggerOffset"
+                              ]
+                            }
+                          ],
+                          "actions": [
+                            {
+                              "type": {
+                                "value": "ScreenWrap::ScreenWrapPhysics::SaveCurrentVelocities"
+                              },
+                              "parameters": [
+                                "Object",
+                                "Behavior",
+                                ""
+                              ]
+                            },
+                            {
+                              "type": {
+                                "value": "MettreY"
+                              },
+                              "parameters": [
+                                "Object",
+                                "=",
+                                "BorderTop - (Object.Height()/2) -  TriggerOffset"
+                              ]
+                            },
+                            {
+                              "type": {
+                                "value": "ScreenWrap::ScreenWrapPhysics::ApplySavedVelocities"
+                              },
+                              "parameters": [
+                                "Object",
+                                "Behavior",
+                                ""
+                              ]
+                            }
+                          ]
+                        }
+                      ]
+                    }
+                  ],
+                  "parameters": []
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "ScreenWrap::ScreenWrapPhysics",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "Check if the object is wrapping on the left and right borders.",
+              "fullName": "Is horizontal wrapping",
+              "functionType": "Condition",
+              "name": "IsHorizontalWrapping",
+              "sentence": "_PARAM0_ is horizontal wrapping",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [
+                    {
+                      "type": {
+                        "inverted": true,
+                        "value": "ScreenWrap::ScreenWrapPhysics::PropertyHorizontalWrapping"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior"
+                      ]
+                    }
+                  ],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SetReturnBoolean"
+                      },
+                      "parameters": [
+                        ""
+                      ]
+                    }
+                  ]
+                },
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [
+                    {
+                      "type": {
+                        "value": "ScreenWrap::ScreenWrapPhysics::PropertyHorizontalWrapping"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior"
+                      ]
+                    }
+                  ],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SetReturnBoolean"
+                      },
+                      "parameters": [
+                        "True"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "ScreenWrap::ScreenWrapPhysics",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "Check if the object is wrapping on the top and bottom borders.",
+              "fullName": "Is vertical wrapping",
+              "functionType": "Condition",
+              "name": "IsVerticalWrapping",
+              "sentence": "_PARAM0_ is vertical wrapping",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [
+                    {
+                      "type": {
+                        "inverted": true,
+                        "value": "ScreenWrap::ScreenWrapPhysics::PropertyVerticalWrapping"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior"
+                      ]
+                    }
+                  ],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SetReturnBoolean"
+                      },
+                      "parameters": [
+                        ""
+                      ]
+                    }
+                  ]
+                },
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [
+                    {
+                      "type": {
+                        "value": "ScreenWrap::ScreenWrapPhysics::PropertyVerticalWrapping"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior"
+                      ]
+                    }
+                  ],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SetReturnBoolean"
+                      },
+                      "parameters": [
+                        "True"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "ScreenWrap::ScreenWrapPhysics",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "Enable wrapping on the left and right borders.",
+              "fullName": "Enable horizontal wrapping",
+              "functionType": "Action",
+              "name": "EnableHorizontalWrapping",
+              "sentence": "Enable _PARAM0_ horizontal wrapping: _PARAM2_",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [
+                    {
+                      "type": {
+                        "inverted": true,
+                        "value": "GetArgumentAsBoolean"
+                      },
+                      "parameters": [
+                        "\"EnableHorizontalWrapping\""
+                      ]
+                    }
+                  ],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "ScreenWrap::ScreenWrapPhysics::SetPropertyHorizontalWrapping"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "no"
+                      ]
+                    }
+                  ]
+                },
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [
+                    {
+                      "type": {
+                        "value": "GetArgumentAsBoolean"
+                      },
+                      "parameters": [
+                        "\"EnableHorizontalWrapping\""
+                      ]
+                    }
+                  ],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "ScreenWrap::ScreenWrapPhysics::SetPropertyHorizontalWrapping"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "yes"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "ScreenWrap::ScreenWrapPhysics",
+                  "type": "behavior"
+                },
+                {
+                  "description": "Value",
+                  "name": "EnableHorizontalWrapping",
+                  "type": "yesorno"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "Enable wrapping on the top and bottom borders.",
+              "fullName": "Enable vertical wrapping",
+              "functionType": "Action",
+              "name": "EnableVerticalWrapping",
+              "sentence": "Enable _PARAM0_ vertical wrapping: _PARAM2_",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [
+                    {
+                      "type": {
+                        "inverted": true,
+                        "value": "GetArgumentAsBoolean"
+                      },
+                      "parameters": [
+                        "\"EnableVerticalWrapping\""
+                      ]
+                    }
+                  ],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "ScreenWrap::ScreenWrapPhysics::SetPropertyVerticalWrapping"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "no"
+                      ]
+                    }
+                  ]
+                },
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [
+                    {
+                      "type": {
+                        "value": "GetArgumentAsBoolean"
+                      },
+                      "parameters": [
+                        "\"EnableVerticalWrapping\""
+                      ]
+                    }
+                  ],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "ScreenWrap::ScreenWrapPhysics::SetPropertyVerticalWrapping"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "yes"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "ScreenWrap::ScreenWrapPhysics",
+                  "type": "behavior"
+                },
+                {
+                  "description": "Value",
+                  "name": "EnableVerticalWrapping",
+                  "type": "yesorno"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "Top border (Y position).",
+              "fullName": "Top border",
+              "functionType": "Expression",
+              "name": "BorderTop",
+              "sentence": "",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SetReturnNumber"
+                      },
+                      "parameters": [
+                        "BorderTop"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "expressionType": {
+                "type": "expression"
+              },
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "ScreenWrap::ScreenWrapPhysics",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "Left border (X position).",
+              "fullName": "Left border",
+              "functionType": "Expression",
+              "name": "BorderLeft",
+              "sentence": "",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SetReturnNumber"
+                      },
+                      "parameters": [
+                        "BorderLeft"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "expressionType": {
+                "type": "expression"
+              },
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "ScreenWrap::ScreenWrapPhysics",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "Right border (X position).",
+              "fullName": "Right border",
+              "functionType": "Expression",
+              "name": "BorderRight",
+              "sentence": "",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SetReturnNumber"
+                      },
+                      "parameters": [
+                        "BorderRight"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "expressionType": {
+                "type": "expression"
+              },
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "ScreenWrap::ScreenWrapPhysics",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "Bottom border (Y position).",
+              "fullName": "Bottom border",
+              "functionType": "Expression",
+              "name": "BorderBottom",
+              "sentence": "",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SetReturnNumber"
+                      },
+                      "parameters": [
+                        "BorderBottom"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "expressionType": {
+                "type": "expression"
+              },
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "ScreenWrap::ScreenWrapPhysics",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "Number of pixels past the center where the object teleports and appears.",
+              "fullName": "Trigger offset",
+              "functionType": "Expression",
+              "name": "TriggerOffset",
+              "sentence": "",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SetReturnNumber"
+                      },
+                      "parameters": [
+                        "TriggerOffset"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "expressionType": {
+                "type": "expression"
+              },
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "ScreenWrap::ScreenWrapPhysics",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "Set top border (Y position).",
+              "fullName": "Set top border",
+              "functionType": "Action",
+              "name": "SetTopBorder",
+              "sentence": "Set _PARAM0_ top border to _PARAM2_",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "ScreenWrap::ScreenWrapPhysics::SetPropertyBorderTop"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "=",
+                        "Value"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "ScreenWrap::ScreenWrapPhysics",
+                  "type": "behavior"
+                },
+                {
+                  "description": "Top border value",
+                  "name": "Value",
+                  "type": "expression"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "Set left border (X position).",
+              "fullName": "Set left border",
+              "functionType": "Action",
+              "name": "SetLeftBorder",
+              "sentence": "Set _PARAM0_ left border to _PARAM2_",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "ScreenWrap::ScreenWrapPhysics::SetPropertyBorderLeft"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "=",
+                        "Value"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "ScreenWrap::ScreenWrapPhysics",
+                  "type": "behavior"
+                },
+                {
+                  "description": "Left border value",
+                  "name": "Value",
+                  "type": "expression"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "Set bottom border (Y position).",
+              "fullName": "Set bottom border",
+              "functionType": "Action",
+              "name": "SetBottomBorder",
+              "sentence": "Set _PARAM0_ bottom border to _PARAM2_",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "ScreenWrap::ScreenWrapPhysics::SetPropertyBorderBottom"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "=",
+                        "Value"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "ScreenWrap::ScreenWrapPhysics",
+                  "type": "behavior"
+                },
+                {
+                  "description": "Bottom border value",
+                  "name": "Value",
+                  "type": "expression"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "Set right border (X position).",
+              "fullName": "Set right border",
+              "functionType": "Action",
+              "name": "SetRightBorder",
+              "sentence": "Set _PARAM0_ right border to _PARAM2_",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "ScreenWrap::ScreenWrapPhysics::SetPropertyBorderRight"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "=",
+                        "Value"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "ScreenWrap::ScreenWrapPhysics",
+                  "type": "behavior"
+                },
+                {
+                  "description": "Right border value",
+                  "name": "Value",
+                  "type": "expression"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "Set trigger offset (pixels).",
+              "fullName": "Set trigger offset",
+              "functionType": "Action",
+              "name": "SetTriggerOffset",
+              "sentence": "Set _PARAM0_ trigger offset to _PARAM2_ pixels",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "ScreenWrap::ScreenWrapPhysics::SetPropertyTriggerOffset"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "=",
+                        "Value"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "ScreenWrap::ScreenWrapPhysics",
+                  "type": "behavior"
+                },
+                {
+                  "description": "SetScreen Offset Leaving Value",
+                  "name": "Value",
+                  "type": "expression"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "Save current velocity values.",
+              "fullName": "Save current velocity values",
+              "functionType": "Action",
+              "name": "SaveCurrentVelocities",
+              "sentence": "Save current velocity values of _PARAM0_",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "ScreenWrap::ScreenWrapPhysics::SetPropertyAngularVelocity"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "=",
+                        "Object.RequiredPhysicsBehavior::AngularVelocity()"
+                      ]
+                    },
+                    {
+                      "type": {
+                        "value": "ScreenWrap::ScreenWrapPhysics::SetPropertyLinearVelocityX"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "=",
+                        "Object.RequiredPhysicsBehavior::LinearVelocityX()"
+                      ]
+                    },
+                    {
+                      "type": {
+                        "value": "ScreenWrap::ScreenWrapPhysics::SetPropertyLinearVelocityY"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "=",
+                        "Object.RequiredPhysicsBehavior::LinearVelocityY()"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "ScreenWrap::ScreenWrapPhysics",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "Apply saved velocity values.",
+              "fullName": "Apply saved velocity values",
+              "functionType": "Action",
+              "name": "ApplySavedVelocities",
+              "sentence": "Apply saved velocity values of _PARAM0_",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "Physics2::AngularVelocity"
+                      },
+                      "parameters": [
+                        "Object",
+                        "RequiredPhysicsBehavior",
+                        "=",
+                        "AngularVelocity"
+                      ]
+                    },
+                    {
+                      "type": {
+                        "value": "Physics2::LinearVelocityX"
+                      },
+                      "parameters": [
+                        "Object",
+                        "RequiredPhysicsBehavior",
+                        "=",
+                        "LinearVelocityX"
+                      ]
+                    },
+                    {
+                      "type": {
+                        "value": "Physics2::LinearVelocityY"
+                      },
+                      "parameters": [
+                        "Object",
+                        "RequiredPhysicsBehavior",
+                        "=",
+                        "LinearVelocityY"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "ScreenWrap::ScreenWrapPhysics",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            }
+          ],
+          "propertyDescriptors": [
+            {
+              "value": "",
+              "type": "Behavior",
+              "label": "Physics Behavior",
+              "description": "",
+              "group": "",
+              "extraInformation": [
+                "Physics2::Physics2Behavior"
+              ],
+              "name": "RequiredPhysicsBehavior"
+            },
+            {
+              "value": "true",
+              "type": "Boolean",
+              "label": "Horizontal wrapping",
+              "description": "",
+              "group": "",
+              "extraInformation": [],
+              "name": "HorizontalWrapping"
+            },
+            {
+              "value": "true",
+              "type": "Boolean",
+              "label": "Vertical wrapping",
+              "description": "",
+              "group": "",
+              "extraInformation": [],
+              "name": "VerticalWrapping"
+            },
+            {
+              "value": "0",
+              "type": "Number",
+              "unit": "Pixel",
+              "label": "Top border of wrapped area (Y)",
+              "description": "",
+              "group": "",
+              "extraInformation": [],
+              "name": "BorderTop"
+            },
+            {
+              "value": "0",
+              "type": "Number",
+              "unit": "Pixel",
+              "label": "Left border of wrapped area (X)",
+              "description": "",
+              "group": "",
+              "extraInformation": [],
+              "name": "BorderLeft"
+            },
+            {
+              "value": "0",
+              "type": "Number",
+              "unit": "Pixel",
+              "label": "Right border of wrapped area (X)",
+              "description": "If blank, the value will be the scene width.",
+              "group": "",
+              "extraInformation": [],
+              "name": "BorderRight"
+            },
+            {
+              "value": "0",
+              "type": "Number",
+              "unit": "Pixel",
+              "label": "Bottom border of wrapped area (Y)",
+              "description": "If blank, the value will be scene height.",
+              "group": "",
+              "extraInformation": [],
+              "name": "BorderBottom"
+            },
+            {
+              "value": "0",
+              "type": "Number",
+              "unit": "Pixel",
+              "label": "Number of pixels past the center where the object teleports and appears",
+              "description": "",
+              "group": "",
+              "extraInformation": [],
+              "name": "TriggerOffset"
+            },
+            {
+              "value": "0",
+              "type": "Number",
+              "label": "Angular Velocity",
+              "description": "",
+              "group": "",
+              "extraInformation": [],
+              "hidden": true,
+              "name": "AngularVelocity"
+            },
+            {
+              "value": "0",
+              "type": "Number",
+              "label": "Linear Velocity X",
+              "description": "",
+              "group": "",
+              "extraInformation": [],
+              "hidden": true,
+              "name": "LinearVelocityX"
+            },
+            {
+              "value": "0",
+              "type": "Number",
+              "label": "Linear Velocity Y",
+              "description": "",
+              "group": "",
+              "extraInformation": [],
+              "hidden": true,
+              "name": "LinearVelocityY"
+            }
+          ],
+          "sharedPropertyDescriptors": []
+        }
+      ],
+      "eventsBasedObjects": []
+    },
+    {
+      "author": "Silver-Streak, @Bouh, Tristan Rhodes",
+      "category": "Game mechanic",
+      "extensionNamespace": "",
+      "fullName": "Object \"Is On Screen\" Detection",
+      "helpPath": "",
+      "iconUrl": "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz48IURPQ1RZUEUgc3ZnIFBVQkxJQyAiLS8vVzNDLy9EVEQgU1ZHIDEuMS8vRU4iICJodHRwOi8vd3d3LnczLm9yZy9HcmFwaGljcy9TVkcvMS4xL0RURC9zdmcxMS5kdGQiPjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgdmVyc2lvbj0iMS4xIiBpZD0ibWRpLW1vbml0b3Itc2NyZWVuc2hvdCIgd2lkdGg9IjI0IiBoZWlnaHQ9IjI0IiB2aWV3Qm94PSIwIDAgMjQgMjQiPjxwYXRoIGQ9Ik05LDZINVYxMEg3VjhIOU0xOSwxMEgxN1YxMkgxNVYxNEgxOU0yMSwxNkgzVjRIMjFNMjEsMkgzQzEuODksMiAxLDIuODkgMSw0VjE2QTIsMiAwIDAsMCAzLDE4SDEwVjIwSDhWMjJIMTZWMjBIMTRWMThIMjFBMiwyIDAgMCwwIDIzLDE2VjRDMjMsMi44OSAyMi4xLDIgMjEsMiIgLz48L3N2Zz4=",
+      "name": "IsOnScreen",
+      "previewIconUrl": "https://resources.gdevelop-app.com/assets/Icons/monitor-screenshot.svg",
+      "shortDescription": "This adds a condition to detect if an object is on screen based off its current layer.",
+      "version": "1.2.1",
+      "description": [
+        "This extension adds conditions to check if an object is located within the visible portion of its layer's camera. The condition also allows for specifying padding to the virtual screen border.",
+        "",
+        "Note that this does not take into account any object visibility, such as being hidden or 0 opacity, but can be combined with those existing conditions."
+      ],
+      "origin": {
+        "identifier": "IsOnScreen",
+        "name": "gdevelop-extension-store"
+      },
+      "tags": [
+        "is on screen",
+        "condition",
+        "visible",
+        "hide",
+        "screen"
+      ],
+      "authorIds": [
+        "2OwwM8ToR9dx9RJ2sAKTcrLmCB92",
+        "8Ih1aa8f5gWUp4UB2BdhQ2iXWxJ3",
+        "gqDaZjCfevOOxBYkK6zlhtZnXCg1"
+      ],
+      "dependencies": [],
+      "globalVariables": [],
+      "sceneVariables": [],
+      "eventsFunctions": [],
+      "eventsBasedBehaviors": [
+        {
+          "description": "This behavior provides a condition to check if the object is located within the visible portion of its layer's camera. The condition also allows for specifying padding to the virtual screen border.\nNote that object visibility, such as being hidden or 0 opacity, is not considered (but you can use those existing conditions in addition to this behavior).",
+          "fullName": "Is on screen",
+          "name": "InOnScreen",
+          "objectType": "",
+          "eventsFunctions": [
+            {
+              "description": "Checks if an object position is within the viewport of its layer.",
+              "fullName": "Is on screen",
+              "functionType": "Condition",
+              "name": "IsOnScreen",
+              "sentence": "_PARAM0_ is on screen (padded by _PARAM2_ pixels)",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::JsCode",
+                  "inlineCode": [
+                    "/*",
+                    "Get the object layer, convert the position from this layer to the screen coordinates.",
+                    "Get the point on each side on the object on screen, and compare with the screen area.",
+                    "",
+                    "This way even if the camera has a rotation or custom scale the object is always compared to the screen area.",
+                    "*/",
+                    "",
+                    "",
+                    "// Get the layer of the object.",
+                    "const object = objects[0];",
+                    "const layer = runtimeScene.getLayer(object.getLayer());",
+                    "",
+                    "// Get the aabb of the object on his layer.",
+                    "const aabb = object.getVisibilityAABB();",
+                    "",
+                    "// Get the layer to convert the coordinates of the AABB to the screen coordinates",
+                    "const topLeft = layer.convertInverseCoords(aabb.min[0], aabb.min[1]);",
+                    "const topRight = layer.convertInverseCoords(aabb.max[0], aabb.min[1]);",
+                    "const bottomRight = layer.convertInverseCoords(aabb.max[0], aabb.max[1]);",
+                    "const bottomLeft = layer.convertInverseCoords(aabb.min[0], aabb.max[1]);",
+                    "",
+                    "// Get the points on each side of the object on screen.",
+                    "const posLeftObjectOnScreen = Math.min(topLeft[0], topRight[0], bottomLeft[0], bottomRight[0]);",
+                    "const posRightObjectOnScreen = Math.max(topLeft[0], topRight[0], bottomLeft[0], bottomRight[0]);",
+                    "const posUpObjectOnScreen = Math.min(topLeft[1], topRight[1], bottomLeft[1], bottomRight[1]);",
+                    "const posDownObjectOnScreen = Math.max(topLeft[1], topRight[1], bottomLeft[1], bottomRight[1]);",
+                    "",
+                    "const padding = eventsFunctionContext.getArgument(\"Padding\");",
+                    "",
+                    "if (",
+                    "    !(posLeftObjectOnScreen - padding > runtimeScene.getGame().getGameResolutionWidth() ||",
+                    "        posUpObjectOnScreen - padding > runtimeScene.getGame().getGameResolutionHeight() ||",
+                    "        posRightObjectOnScreen + padding < 0 ||",
+                    "        posDownObjectOnScreen + padding < 0",
+                    "        )",
+                    ") {",
+                    "    eventsFunctionContext.returnValue = true;",
+                    "}",
+                    ""
+                  ],
+                  "parameterObjects": "Object",
+                  "useStrict": true,
+                  "eventsSheetExpanded": true
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "IsOnScreen::InOnScreen",
+                  "type": "behavior"
+                },
+                {
+                  "description": "Padding (in pixels)",
+                  "longDescription": "Number of pixels to pad the screen border. Zero by default. A negative value goes inside the screen, a positive value go outside.",
+                  "name": "Padding",
+                  "type": "expression"
+                }
+              ],
+              "objectGroups": [
+                {
+                  "name": "Group",
+                  "objects": []
+                }
+              ]
+            }
+          ],
+          "propertyDescriptors": [],
+          "sharedPropertyDescriptors": []
+        }
+      ],
+      "eventsBasedObjects": []
+    },
+    {
+      "author": "",
+      "category": "Input",
+      "extensionNamespace": "",
+      "fullName": "Multitouch joystick and buttons (sprite)",
+      "helpPath": "/objects/multitouch-joystick",
+      "iconUrl": "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4NCjwhLS0gR2VuZXJhdG9yOiBBZG9iZSBJbGx1c3RyYXRvciAyMy4wLjMsIFNWRyBFeHBvcnQgUGx1Zy1JbiAuIFNWRyBWZXJzaW9uOiA2LjAwIEJ1aWxkIDApICAtLT4NCjxzdmcgdmVyc2lvbj0iMS4xIiBpZD0iSWNvbnMiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4Ig0KCSB2aWV3Qm94PSIwIDAgMzIgMzIiIHN0eWxlPSJlbmFibGUtYmFja2dyb3VuZDpuZXcgMCAwIDMyIDMyOyIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+DQo8c3R5bGUgdHlwZT0idGV4dC9jc3MiPg0KCS5zdDB7ZmlsbDpub25lO3N0cm9rZTojMDAwMDAwO3N0cm9rZS13aWR0aDoyO3N0cm9rZS1saW5lY2FwOnJvdW5kO3N0cm9rZS1saW5lam9pbjpyb3VuZDtzdHJva2UtbWl0ZXJsaW1pdDoxMDt9DQo8L3N0eWxlPg0KPGNpcmNsZSBjbGFzcz0ic3QwIiBjeD0iMTYiIGN5PSIxNiIgcj0iMTMiLz4NCjxwb2x5bGluZSBjbGFzcz0ic3QwIiBwb2ludHM9IjI4LjQsMTIgMjAsMTIgMjAsMy42ICIvPg0KPHBvbHlsaW5lIGNsYXNzPSJzdDAiIHBvaW50cz0iMjAsMjguNCAyMCwyMCAyOC40LDIwICIvPg0KPHBvbHlsaW5lIGNsYXNzPSJzdDAiIHBvaW50cz0iMy42LDIwIDEyLDIwIDEyLDI4LjQgIi8+DQo8cG9seWxpbmUgY2xhc3M9InN0MCIgcG9pbnRzPSIxMiwzLjYgMTIsMTIgMy42LDEyICIvPg0KPHBvbHlnb24gY2xhc3M9InN0MCIgcG9pbnRzPSIxNiw2IDE2LjcsNyAxNS4zLDcgIi8+DQo8cG9seWdvbiBjbGFzcz0ic3QwIiBwb2ludHM9IjE2LDI2IDE1LjMsMjUgMTYuNywyNSAiLz4NCjxwb2x5Z29uIGNsYXNzPSJzdDAiIHBvaW50cz0iNiwxNiA3LDE1LjMgNywxNi43ICIvPg0KPHBvbHlnb24gY2xhc3M9InN0MCIgcG9pbnRzPSIyNiwxNiAyNSwxNi43IDI1LDE1LjMgIi8+DQo8L3N2Zz4NCg==",
+      "name": "SpriteMultitouchJoystick",
+      "previewIconUrl": "https://resources.gdevelop-app.com/assets/Icons/Line Hero Pack/Master/SVG/Videogames/Videogames_controller_joystick_arrows_direction.svg",
+      "shortDescription": "Joysticks or buttons for touchscreens.",
+      "version": "1.6.1",
+      "description": [
+        "Multitouch joysticks can be used the same way as physical gamepads:",
+        "- 4 or 8 directions",
+        "- Analogus pads",
+        "- Player selection",
+        "- Controls mapping for top-down movement and platformer characters",
+        "",
+        "There are ready-to-use joysticks in the asset-store [multitouch joysticks pack](https://editor.gdevelop.io/?initial-dialog=asset-store&asset-pack=multitouch-joysticks-multitouch-joysticks)."
+      ],
+      "origin": {
+        "identifier": "SpriteMultitouchJoystick",
+        "name": "gdevelop-extension-store"
+      },
+      "tags": [
+        "multitouch",
+        "joystick",
+        "thumbstick",
+        "controller",
+        "touchscreen",
+        "twin stick",
+        "shooter",
+        "virtual",
+        "platformer",
+        "platform",
+        "top-down"
+      ],
+      "authorIds": [
+        "gqDaZjCfevOOxBYkK6zlhtZnXCg1",
+        "1OgYzWp5UeVPbiWGJwI6vqfgZLC3",
+        "v0YRpdAnIucZFgiRCCecqVnGKno2",
+        "IWykYNRvhCZBN3vEgKEbBPOR3Oc2"
+      ],
+      "dependencies": [],
+      "globalVariables": [],
+      "sceneVariables": [
+        {
+          "name": "Controllers",
+          "type": "array",
+          "children": [
+            {
+              "type": "structure",
+              "children": [
+                {
+                  "name": "Buttons",
+                  "type": "array",
+                  "children": [
+                    {
+                      "type": "structure",
+                      "children": [
+                        {
+                          "name": "State",
+                          "type": "string",
+                          "value": "Idle"
+                        }
+                      ]
+                    }
+                  ]
+                },
+                {
+                  "name": "Joystick",
+                  "type": "structure",
+                  "children": []
+                }
+              ]
+            }
+          ]
+        }
+      ],
+      "eventsFunctions": [
+        {
+          "fullName": "Accelerated speed",
+          "functionType": "Expression",
+          "name": "AcceleratedSpeed",
+          "private": true,
+          "sentence": "",
+          "events": [
+            {
+              "type": "BuiltinCommonInstructions::Standard",
+              "conditions": [],
+              "actions": [
+                {
+                  "type": {
+                    "value": "SetNumberVariable"
+                  },
+                  "parameters": [
+                    "AcceleratedSpeed",
+                    "=",
+                    "CurrentSpeed"
+                  ]
+                }
+              ],
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [
+                    {
+                      "type": {
+                        "value": "CompareArgumentAsNumber"
+                      },
+                      "parameters": [
+                        "\"TargetedSpeed\"",
+                        "<",
+                        "0"
+                      ]
+                    }
+                  ],
+                  "actions": [],
+                  "events": [
+                    {
+                      "type": "BuiltinCommonInstructions::Comment",
+                      "color": {
+                        "b": 109,
+                        "g": 230,
+                        "r": 255,
+                        "textB": 0,
+                        "textG": 0,
+                        "textR": 0
+                      },
+                      "comment": "Reduce the speed to match the stick force."
+                    },
+                    {
+                      "type": "BuiltinCommonInstructions::Standard",
+                      "conditions": [
+                        {
+                          "type": {
+                            "value": "CompareArgumentAsNumber"
+                          },
+                          "parameters": [
+                            "\"CurrentSpeed\"",
+                            "<",
+                            "TargetedSpeed"
+                          ]
+                        }
+                      ],
+                      "actions": [
+                        {
+                          "type": {
+                            "value": "SetNumberVariable"
+                          },
+                          "parameters": [
+                            "AcceleratedSpeed",
+                            "=",
+                            "min(TargetedSpeed, CurrentSpeed + Acceleration * TimeDelta())"
+                          ]
+                        }
+                      ]
+                    },
+                    {
+                      "type": "BuiltinCommonInstructions::Standard",
+                      "conditions": [
+                        {
+                          "type": {
+                            "value": "CompareArgumentAsNumber"
+                          },
+                          "parameters": [
+                            "\"CurrentSpeed\"",
+                            ">",
+                            "TargetedSpeed"
+                          ]
+                        },
+                        {
+                          "type": {
+                            "value": "CompareArgumentAsNumber"
+                          },
+                          "parameters": [
+                            "\"CurrentSpeed\"",
+                            "<",
+                            "0"
+                          ]
+                        }
+                      ],
+                      "actions": [
+                        {
+                          "type": {
+                            "value": "SetNumberVariable"
+                          },
+                          "parameters": [
+                            "AcceleratedSpeed",
+                            "-",
+                            "Acceleration * TimeDelta()"
+                          ]
+                        }
+                      ]
+                    },
+                    {
+                      "type": "BuiltinCommonInstructions::Comment",
+                      "color": {
+                        "b": 109,
+                        "g": 230,
+                        "r": 255,
+                        "textB": 0,
+                        "textG": 0,
+                        "textR": 0
+                      },
+                      "comment": "Turn back at least as fast as it would stop."
+                    },
+                    {
+                      "type": "BuiltinCommonInstructions::Standard",
+                      "conditions": [
+                        {
+                          "type": {
+                            "value": "CompareArgumentAsNumber"
+                          },
+                          "parameters": [
+                            "\"CurrentSpeed\"",
+                            ">=",
+                            "0"
+                          ]
+                        }
+                      ],
+                      "actions": [
+                        {
+                          "type": {
+                            "value": "SetNumberVariable"
+                          },
+                          "parameters": [
+                            "AcceleratedSpeed",
+                            "=",
+                            "max(TargetedSpeed, CurrentSpeed - max(Acceleration , Deceleration) * TimeDelta())"
+                          ]
+                        }
+                      ]
+                    }
+                  ]
+                },
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [
+                    {
+                      "type": {
+                        "value": "CompareArgumentAsNumber"
+                      },
+                      "parameters": [
+                        "\"TargetedSpeed\"",
+                        ">",
+                        "0"
+                      ]
+                    }
+                  ],
+                  "actions": [],
+                  "events": [
+                    {
+                      "type": "BuiltinCommonInstructions::Comment",
+                      "color": {
+                        "b": 109,
+                        "g": 230,
+                        "r": 255,
+                        "textB": 0,
+                        "textG": 0,
+                        "textR": 0
+                      },
+                      "comment": "Reduce the speed to match the stick force."
+                    },
+                    {
+                      "type": "BuiltinCommonInstructions::Standard",
+                      "conditions": [
+                        {
+                          "type": {
+                            "value": "CompareArgumentAsNumber"
+                          },
+                          "parameters": [
+                            "\"CurrentSpeed\"",
+                            ">",
+                            "TargetedSpeed"
+                          ]
+                        }
+                      ],
+                      "actions": [
+                        {
+                          "type": {
+                            "value": "SetNumberVariable"
+                          },
+                          "parameters": [
+                            "AcceleratedSpeed",
+                            "=",
+                            "max(TargetedSpeed, CurrentSpeed - Acceleration * TimeDelta())"
+                          ]
+                        }
+                      ]
+                    },
+                    {
+                      "type": "BuiltinCommonInstructions::Standard",
+                      "conditions": [
+                        {
+                          "type": {
+                            "value": "CompareArgumentAsNumber"
+                          },
+                          "parameters": [
+                            "\"CurrentSpeed\"",
+                            "<",
+                            "TargetedSpeed"
+                          ]
+                        },
+                        {
+                          "type": {
+                            "value": "CompareArgumentAsNumber"
+                          },
+                          "parameters": [
+                            "\"CurrentSpeed\"",
+                            ">",
+                            "0"
+                          ]
+                        }
+                      ],
+                      "actions": [
+                        {
+                          "type": {
+                            "value": "SetNumberVariable"
+                          },
+                          "parameters": [
+                            "AcceleratedSpeed",
+                            "+",
+                            "Acceleration * TimeDelta()"
+                          ]
+                        }
+                      ]
+                    },
+                    {
+                      "type": "BuiltinCommonInstructions::Comment",
+                      "color": {
+                        "b": 109,
+                        "g": 230,
+                        "r": 255,
+                        "textB": 0,
+                        "textG": 0,
+                        "textR": 0
+                      },
+                      "comment": "Turn back at least as fast as it would stop."
+                    },
+                    {
+                      "type": "BuiltinCommonInstructions::Standard",
+                      "conditions": [
+                        {
+                          "type": {
+                            "value": "CompareArgumentAsNumber"
+                          },
+                          "parameters": [
+                            "\"CurrentSpeed\"",
+                            "<=",
+                            "0"
+                          ]
+                        }
+                      ],
+                      "actions": [
+                        {
+                          "type": {
+                            "value": "SetNumberVariable"
+                          },
+                          "parameters": [
+                            "AcceleratedSpeed",
+                            "=",
+                            "min(TargetedSpeed, CurrentSpeed + max(Acceleration , Deceleration) * TimeDelta())"
+                          ]
+                        }
+                      ]
+                    }
+                  ]
+                },
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [
+                    {
+                      "type": {
+                        "value": "CompareArgumentAsNumber"
+                      },
+                      "parameters": [
+                        "\"TargetedSpeed\"",
+                        "=",
+                        "0"
+                      ]
+                    }
+                  ],
+                  "actions": [],
+                  "events": [
+                    {
+                      "type": "BuiltinCommonInstructions::Standard",
+                      "conditions": [
+                        {
+                          "type": {
+                            "value": "CompareArgumentAsNumber"
+                          },
+                          "parameters": [
+                            "\"CurrentSpeed\"",
+                            "<",
+                            "0"
+                          ]
+                        }
+                      ],
+                      "actions": [
+                        {
+                          "type": {
+                            "value": "SetNumberVariable"
+                          },
+                          "parameters": [
+                            "AcceleratedSpeed",
+                            "=",
+                            "min(0, CurrentSpeed + Acceleration * TimeDelta())"
+                          ]
+                        }
+                      ]
+                    },
+                    {
+                      "type": "BuiltinCommonInstructions::Standard",
+                      "conditions": [
+                        {
+                          "type": {
+                            "value": "CompareArgumentAsNumber"
+                          },
+                          "parameters": [
+                            "\"CurrentSpeed\"",
+                            ">",
+                            "0"
+                          ]
+                        }
+                      ],
+                      "actions": [
+                        {
+                          "type": {
+                            "value": "SetNumberVariable"
+                          },
+                          "parameters": [
+                            "AcceleratedSpeed",
+                            "=",
+                            "max(0, CurrentSpeed - Acceleration * TimeDelta())"
+                          ]
+                        }
+                      ]
+                    }
+                  ]
+                },
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SetReturnNumber"
+                      },
+                      "parameters": [
+                        "clamp(AcceleratedSpeed, -SpeedMax, SpeedMax)"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "variables": [
+                {
+                  "name": "AcceleratedSpeed",
+                  "type": "number",
+                  "value": 0
+                }
+              ]
+            }
+          ],
+          "expressionType": {
+            "type": "expression"
+          },
+          "parameters": [
+            {
+              "description": "Current speed",
+              "name": "CurrentSpeed",
+              "type": "expression"
+            },
+            {
+              "description": "Targeted speed",
+              "name": "TargetedSpeed",
+              "type": "expression"
+            },
+            {
+              "description": "Max speed",
+              "name": "SpeedMax",
+              "type": "expression"
+            },
+            {
+              "description": "Acceleration",
+              "name": "Acceleration",
+              "type": "expression"
+            },
+            {
+              "description": "Deceleration",
+              "name": "Deceleration",
+              "type": "expression"
+            }
+          ],
+          "objectGroups": []
+        },
+        {
+          "description": "Check if a button is pressed on a gamepad.",
+          "fullName": "Multitouch controller button pressed",
+          "functionType": "Condition",
+          "name": "IsButtonPressed",
+          "sentence": "Button _PARAM2_ of multitouch controller _PARAM1_ is pressed",
+          "events": [
+            {
+              "type": "BuiltinCommonInstructions::Standard",
+              "conditions": [
+                {
+                  "type": {
+                    "value": "StringVariable"
+                  },
+                  "parameters": [
+                    "Controllers[ControllerIdentifier].Buttons[Button].State",
+                    "=",
+                    "\"Pressed\""
+                  ]
+                }
+              ],
+              "actions": [
+                {
+                  "type": {
+                    "value": "SetReturnBoolean"
+                  },
+                  "parameters": [
+                    "True"
+                  ]
+                }
+              ]
+            }
+          ],
+          "parameters": [
+            {
+              "description": "Multitouch controller identifier (1, 2, 3, 4...)",
+              "name": "ControllerIdentifier",
+              "type": "expression"
+            },
+            {
+              "description": "Button name",
+              "name": "Button",
+              "supplementaryInformation": "[\"A\",\"CROSS\",\"B\",\"CIRCLE\",\"X\",\"SQUARE\",\"Y\",\"TRIANGLE\",\"LB\",\"L1\",\"RB\",\"R1\",\"LT\",\"L2\",\"RT\",\"R2\",\"UP\",\"DOWN\",\"LEFT\",\"RIGHT\",\"BACK\",\"SHARE\",\"START\",\"OPTIONS\",\"CLICK_STICK_LEFT\",\"CLICK_STICK_RIGHT\",\"PS_BUTTON\",\"CLICK_TOUCHPAD\"]",
+              "type": "string"
+            }
+          ],
+          "objectGroups": []
+        },
+        {
+          "description": "Check if a button is released on a gamepad.",
+          "fullName": "Multitouch controller button released",
+          "functionType": "Condition",
+          "name": "IsButtonReleased",
+          "sentence": "Button _PARAM2_ of multitouch controller _PARAM1_ is released",
+          "events": [
+            {
+              "type": "BuiltinCommonInstructions::Standard",
+              "conditions": [
+                {
+                  "type": {
+                    "value": "StringVariable"
+                  },
+                  "parameters": [
+                    "Controllers[ControllerIdentifier].Buttons[Button].State",
+                    "=",
+                    "\"Released\""
+                  ]
+                }
+              ],
+              "actions": [
+                {
+                  "type": {
+                    "value": "SetReturnBoolean"
+                  },
+                  "parameters": [
+                    "True"
+                  ]
+                }
+              ]
+            }
+          ],
+          "parameters": [
+            {
+              "description": "Multitouch controller identifier (1, 2, 3, 4...)",
+              "name": "ControllerIdentifier",
+              "type": "expression"
+            },
+            {
+              "description": "Button name",
+              "name": "Button",
+              "supplementaryInformation": "[\"A\",\"CROSS\",\"B\",\"CIRCLE\",\"X\",\"SQUARE\",\"Y\",\"TRIANGLE\",\"LB\",\"L1\",\"RB\",\"R1\",\"LT\",\"L2\",\"RT\",\"R2\",\"UP\",\"DOWN\",\"LEFT\",\"RIGHT\",\"BACK\",\"SHARE\",\"START\",\"OPTIONS\",\"CLICK_STICK_LEFT\",\"CLICK_STICK_RIGHT\",\"PS_BUTTON\",\"CLICK_TOUCHPAD\"]",
+              "type": "string"
+            }
+          ],
+          "objectGroups": []
+        },
+        {
+          "description": "Change a button state for a multitouch controller.",
+          "fullName": "Button state",
+          "functionType": "Action",
+          "name": "SetButtonState",
+          "private": true,
+          "sentence": "Mark _PARAM2_ button as _PARAM3_ for multitouch controller _PARAM1_",
+          "events": [
+            {
+              "type": "BuiltinCommonInstructions::Standard",
+              "conditions": [],
+              "actions": [
+                {
+                  "type": {
+                    "value": "SetStringVariable"
+                  },
+                  "parameters": [
+                    "Controllers[ControllerIdentifier].Buttons[Button].State",
+                    "=",
+                    "ButtonState"
+                  ]
+                }
+              ]
+            }
+          ],
+          "parameters": [
+            {
+              "description": "Multitouch controller identifier (1, 2, 3, 4...)",
+              "name": "ControllerIdentifier",
+              "type": "expression"
+            },
+            {
+              "description": "Button name",
+              "name": "Button",
+              "type": "string"
+            },
+            {
+              "description": "Button state",
+              "name": "ButtonState",
+              "supplementaryInformation": "[\"Idle\",\"Pressed\",\"Released\"]",
+              "type": "stringWithSelector"
+            }
+          ],
+          "objectGroups": []
+        },
+        {
+          "description": "Change the dead zone radius of a joystick. The deadzone is an area for which movement on sticks won't be taken into account (instead, the stick will be considered as not moved).",
+          "fullName": "Dead zone radius",
+          "functionType": "Action",
+          "name": "SetDeadZone",
+          "private": true,
+          "sentence": "Change the dead zone of multitouch joystick _PARAM2_ of multitouch controller _PARAM1_ to _PARAM3_",
+          "events": [
+            {
+              "type": "BuiltinCommonInstructions::Standard",
+              "conditions": [],
+              "actions": [
+                {
+                  "type": {
+                    "value": "SetNumberVariable"
+                  },
+                  "parameters": [
+                    "Controllers[ControllerIdentifier].Joystick[JoystickIdentifier].DeadZone",
+                    "=",
+                    "DeadZoneRadius"
+                  ]
+                }
+              ]
+            }
+          ],
+          "parameters": [
+            {
+              "description": "Multitouch controller identifier (1, 2, 3, 4...)",
+              "name": "ControllerIdentifier",
+              "type": "expression"
+            },
+            {
+              "description": "Joystick name",
+              "name": "JoystickIdentifier",
+              "type": "string"
+            },
+            {
+              "description": "Dead zone radius",
+              "name": "DeadZoneRadius",
+              "supplementaryInformation": "[]",
+              "type": "expression"
+            }
+          ],
+          "objectGroups": []
+        },
+        {
+          "description": "Return the dead zone radius of a joystick. The deadzone is an area for which movement on sticks won't be taken into account (instead, the stick will be considered as not moved).",
+          "fullName": "Dead zone radius",
+          "functionType": "Expression",
+          "name": "DeadZone",
+          "private": true,
+          "sentence": "Change multitouch joystick _PARAM2_ of multitouch controller _PARAM1_ dead zone to _PARAM3_",
+          "events": [
+            {
+              "type": "BuiltinCommonInstructions::Standard",
+              "conditions": [],
+              "actions": [
+                {
+                  "type": {
+                    "value": "SetReturnNumber"
+                  },
+                  "parameters": [
+                    "Controllers[ControllerIdentifier].Joystick[JoystickIdentifier].DeadZone"
+                  ]
+                }
+              ]
+            }
+          ],
+          "expressionType": {
+            "type": "expression"
+          },
+          "parameters": [
+            {
+              "description": "Multitouch controller identifier (1, 2, 3, 4...)",
+              "name": "ControllerIdentifier",
+              "type": "expression"
+            },
+            {
+              "description": "Joystick name",
+              "name": "JoystickIdentifier",
+              "type": "string"
+            }
+          ],
+          "objectGroups": []
+        },
+        {
+          "description": "the direction index (left = 1, bottom = 1, right = 2, top = 3) for an angle (in degrees).",
+          "fullName": "Angle to 4-way index",
+          "functionType": "ExpressionAndCondition",
+          "name": "AngleTo4Way",
+          "private": true,
+          "sentence": "The angle _PARAM1_ 4-way index",
+          "events": [
+            {
+              "type": "BuiltinCommonInstructions::Standard",
+              "conditions": [],
+              "actions": [
+                {
+                  "type": {
+                    "value": "SetReturnNumber"
+                  },
+                  "parameters": [
+                    "mod(round(Angle * 4 / 360), 4)"
+                  ]
+                }
+              ]
+            }
+          ],
+          "expressionType": {
+            "type": "expression"
+          },
+          "parameters": [
+            {
+              "description": "Angle",
+              "name": "Angle",
+              "type": "expression"
+            }
+          ],
+          "objectGroups": []
+        },
+        {
+          "description": "the direction index (left = 1, bottom-left = 1... top-left = 7) for an angle (in degrees).",
+          "fullName": "Angle to 8-way index",
+          "functionType": "ExpressionAndCondition",
+          "name": "AngleTo8Way",
+          "private": true,
+          "sentence": "The angle _PARAM1_ 8-way index",
+          "events": [
+            {
+              "type": "BuiltinCommonInstructions::Standard",
+              "conditions": [],
+              "actions": [
+                {
+                  "type": {
+                    "value": "SetReturnNumber"
+                  },
+                  "parameters": [
+                    "mod(round(Angle * 8 / 360), 8)"
+                  ]
+                }
+              ]
+            }
+          ],
+          "expressionType": {
+            "type": "expression"
+          },
+          "parameters": [
+            {
+              "description": "Angle",
+              "name": "Angle",
+              "type": "expression"
+            }
+          ],
+          "objectGroups": []
+        },
+        {
+          "description": "Check if angle is in a given direction.",
+          "fullName": "Angle 4-way direction",
+          "functionType": "Condition",
+          "name": "IsAngleIn4WayDirection",
+          "private": true,
+          "sentence": "The angle _PARAM1_ is the 4-way direction _PARAM2_",
+          "events": [
+            {
+              "type": "BuiltinCommonInstructions::Standard",
+              "conditions": [
+                {
+                  "type": {
+                    "value": "BuiltinCommonInstructions::CompareStrings"
+                  },
+                  "parameters": [
+                    "Direction",
+                    "=",
+                    "\"Right\""
+                  ]
+                },
+                {
+                  "type": {
+                    "value": "SpriteMultitouchJoystick::AngleTo4Way"
+                  },
+                  "parameters": [
+                    "",
+                    "=",
+                    "0",
+                    "Angle",
+                    ""
+                  ]
+                }
+              ],
+              "actions": [
+                {
+                  "type": {
+                    "value": "SetReturnBoolean"
+                  },
+                  "parameters": [
+                    "True"
+                  ]
+                }
+              ]
+            },
+            {
+              "type": "BuiltinCommonInstructions::Standard",
+              "conditions": [
+                {
+                  "type": {
+                    "value": "BuiltinCommonInstructions::CompareStrings"
+                  },
+                  "parameters": [
+                    "Direction",
+                    "=",
+                    "\"Down\""
+                  ]
+                },
+                {
+                  "type": {
+                    "value": "SpriteMultitouchJoystick::AngleTo4Way"
+                  },
+                  "parameters": [
+                    "",
+                    "=",
+                    "1",
+                    "Angle",
+                    ""
+                  ]
+                }
+              ],
+              "actions": [
+                {
+                  "type": {
+                    "value": "SetReturnBoolean"
+                  },
+                  "parameters": [
+                    "True"
+                  ]
+                }
+              ]
+            },
+            {
+              "type": "BuiltinCommonInstructions::Standard",
+              "conditions": [
+                {
+                  "type": {
+                    "value": "BuiltinCommonInstructions::CompareStrings"
+                  },
+                  "parameters": [
+                    "Direction",
+                    "=",
+                    "\"Left\""
+                  ]
+                },
+                {
+                  "type": {
+                    "value": "SpriteMultitouchJoystick::AngleTo4Way"
+                  },
+                  "parameters": [
+                    "",
+                    "=",
+                    "2",
+                    "Angle",
+                    ""
+                  ]
+                }
+              ],
+              "actions": [
+                {
+                  "type": {
+                    "value": "SetReturnBoolean"
+                  },
+                  "parameters": [
+                    "True"
+                  ]
+                }
+              ]
+            },
+            {
+              "type": "BuiltinCommonInstructions::Standard",
+              "conditions": [
+                {
+                  "type": {
+                    "value": "BuiltinCommonInstructions::CompareStrings"
+                  },
+                  "parameters": [
+                    "Direction",
+                    "=",
+                    "\"Up\""
+                  ]
+                },
+                {
+                  "type": {
+                    "value": "SpriteMultitouchJoystick::AngleTo4Way"
+                  },
+                  "parameters": [
+                    "",
+                    "=",
+                    "3",
+                    "Angle",
+                    ""
+                  ]
+                }
+              ],
+              "actions": [
+                {
+                  "type": {
+                    "value": "SetReturnBoolean"
+                  },
+                  "parameters": [
+                    "True"
+                  ]
+                }
+              ]
+            }
+          ],
+          "parameters": [
+            {
+              "description": "Angle",
+              "name": "Angle",
+              "type": "expression"
+            },
+            {
+              "description": "Direction",
+              "name": "Direction",
+              "supplementaryInformation": "[\"Up\",\"Down\",\"Left\",\"Right\"]",
+              "type": "stringWithSelector"
+            }
+          ],
+          "objectGroups": []
+        },
+        {
+          "description": "Check if angle is in a given direction.",
+          "fullName": "Angle 8-way direction",
+          "functionType": "Condition",
+          "name": "IsAngleIn8WayDirection",
+          "private": true,
+          "sentence": "The angle _PARAM1_ is the 8-way direction _PARAM2_",
+          "events": [
+            {
+              "type": "BuiltinCommonInstructions::Standard",
+              "conditions": [
+                {
+                  "type": {
+                    "value": "BuiltinCommonInstructions::CompareStrings"
+                  },
+                  "parameters": [
+                    "Direction",
+                    "=",
+                    "\"Right\""
+                  ]
+                },
+                {
+                  "type": {
+                    "value": "SpriteMultitouchJoystick::AngleTo8Way"
+                  },
+                  "parameters": [
+                    "",
+                    "=",
+                    "0",
+                    "Angle",
+                    ""
+                  ]
+                }
+              ],
+              "actions": [
+                {
+                  "type": {
+                    "value": "SetReturnBoolean"
+                  },
+                  "parameters": [
+                    "True"
+                  ]
+                }
+              ]
+            },
+            {
+              "type": "BuiltinCommonInstructions::Standard",
+              "conditions": [
+                {
+                  "type": {
+                    "value": "BuiltinCommonInstructions::CompareStrings"
+                  },
+                  "parameters": [
+                    "Direction",
+                    "=",
+                    "\"DownRight\""
+                  ]
+                },
+                {
+                  "type": {
+                    "value": "SpriteMultitouchJoystick::AngleTo8Way"
+                  },
+                  "parameters": [
+                    "",
+                    "=",
+                    "1",
+                    "Angle",
+                    ""
+                  ]
+                }
+              ],
+              "actions": [
+                {
+                  "type": {
+                    "value": "SetReturnBoolean"
+                  },
+                  "parameters": [
+                    "True"
+                  ]
+                }
+              ]
+            },
+            {
+              "type": "BuiltinCommonInstructions::Standard",
+              "conditions": [
+                {
+                  "type": {
+                    "value": "BuiltinCommonInstructions::CompareStrings"
+                  },
+                  "parameters": [
+                    "Direction",
+                    "=",
+                    "\"Down\""
+                  ]
+                },
+                {
+                  "type": {
+                    "value": "SpriteMultitouchJoystick::AngleTo8Way"
+                  },
+                  "parameters": [
+                    "",
+                    "=",
+                    "2",
+                    "Angle",
+                    ""
+                  ]
+                }
+              ],
+              "actions": [
+                {
+                  "type": {
+                    "value": "SetReturnBoolean"
+                  },
+                  "parameters": [
+                    "True"
+                  ]
+                }
+              ]
+            },
+            {
+              "type": "BuiltinCommonInstructions::Standard",
+              "conditions": [
+                {
+                  "type": {
+                    "value": "BuiltinCommonInstructions::CompareStrings"
+                  },
+                  "parameters": [
+                    "Direction",
+                    "=",
+                    "\"DownLeft\""
+                  ]
+                },
+                {
+                  "type": {
+                    "value": "SpriteMultitouchJoystick::AngleTo8Way"
+                  },
+                  "parameters": [
+                    "",
+                    "=",
+                    "3",
+                    "Angle",
+                    ""
+                  ]
+                }
+              ],
+              "actions": [
+                {
+                  "type": {
+                    "value": "SetReturnBoolean"
+                  },
+                  "parameters": [
+                    "True"
+                  ]
+                }
+              ]
+            },
+            {
+              "type": "BuiltinCommonInstructions::Standard",
+              "conditions": [
+                {
+                  "type": {
+                    "value": "BuiltinCommonInstructions::CompareStrings"
+                  },
+                  "parameters": [
+                    "Direction",
+                    "=",
+                    "\"Left\""
+                  ]
+                },
+                {
+                  "type": {
+                    "value": "SpriteMultitouchJoystick::AngleTo8Way"
+                  },
+                  "parameters": [
+                    "",
+                    "=",
+                    "4",
+                    "Angle",
+                    ""
+                  ]
+                }
+              ],
+              "actions": [
+                {
+                  "type": {
+                    "value": "SetReturnBoolean"
+                  },
+                  "parameters": [
+                    "True"
+                  ]
+                }
+              ]
+            },
+            {
+              "type": "BuiltinCommonInstructions::Standard",
+              "conditions": [
+                {
+                  "type": {
+                    "value": "BuiltinCommonInstructions::CompareStrings"
+                  },
+                  "parameters": [
+                    "Direction",
+                    "=",
+                    "\"UpLeft\""
+                  ]
+                },
+                {
+                  "type": {
+                    "value": "SpriteMultitouchJoystick::AngleTo8Way"
+                  },
+                  "parameters": [
+                    "",
+                    "=",
+                    "5",
+                    "Angle",
+                    ""
+                  ]
+                }
+              ],
+              "actions": [
+                {
+                  "type": {
+                    "value": "SetReturnBoolean"
+                  },
+                  "parameters": [
+                    "True"
+                  ]
+                }
+              ]
+            },
+            {
+              "type": "BuiltinCommonInstructions::Standard",
+              "conditions": [
+                {
+                  "type": {
+                    "value": "BuiltinCommonInstructions::CompareStrings"
+                  },
+                  "parameters": [
+                    "Direction",
+                    "=",
+                    "\"Up\""
+                  ]
+                },
+                {
+                  "type": {
+                    "value": "SpriteMultitouchJoystick::AngleTo8Way"
+                  },
+                  "parameters": [
+                    "",
+                    "=",
+                    "6",
+                    "Angle",
+                    ""
+                  ]
+                }
+              ],
+              "actions": [
+                {
+                  "type": {
+                    "value": "SetReturnBoolean"
+                  },
+                  "parameters": [
+                    "True"
+                  ]
+                }
+              ]
+            },
+            {
+              "type": "BuiltinCommonInstructions::Standard",
+              "conditions": [
+                {
+                  "type": {
+                    "value": "BuiltinCommonInstructions::CompareStrings"
+                  },
+                  "parameters": [
+                    "Direction",
+                    "=",
+                    "\"UpRight\""
+                  ]
+                },
+                {
+                  "type": {
+                    "value": "SpriteMultitouchJoystick::AngleTo8Way"
+                  },
+                  "parameters": [
+                    "",
+                    "=",
+                    "7",
+                    "Angle",
+                    ""
+                  ]
+                }
+              ],
+              "actions": [
+                {
+                  "type": {
+                    "value": "SetReturnBoolean"
+                  },
+                  "parameters": [
+                    "True"
+                  ]
+                }
+              ]
+            }
+          ],
+          "parameters": [
+            {
+              "description": "Angle",
+              "name": "Angle",
+              "type": "expression"
+            },
+            {
+              "description": "Direction",
+              "name": "Direction",
+              "supplementaryInformation": "[\"Up\",\"Down\",\"Left\",\"Right\",\"UpLeft\",\"UpRight\",\"DownLeft\",\"DownRight\"]",
+              "type": "stringWithSelector"
+            }
+          ],
+          "objectGroups": []
+        },
+        {
+          "description": "Check if joystick is pushed in a given direction.",
+          "fullName": "Joystick pushed in a direction (4-way)",
+          "functionType": "Condition",
+          "name": "IsDirectionPushed4Way",
+          "sentence": "Joystick _PARAM2_ of multitouch controller _PARAM1_ is pushed in direction _PARAM3_",
+          "events": [
+            {
+              "type": "BuiltinCommonInstructions::Comment",
+              "color": {
+                "b": 109,
+                "g": 230,
+                "r": 255,
+                "textB": 0,
+                "textG": 0,
+                "textR": 0
+              },
+              "comment": "Make sure the joystick has moved from center"
+            },
+            {
+              "type": "BuiltinCommonInstructions::Standard",
+              "conditions": [
+                {
+                  "type": {
+                    "value": "SpriteMultitouchJoystick::JoystickForce"
+                  },
+                  "parameters": [
+                    "",
+                    ">",
+                    "0",
+                    "ControllerIdentifier",
+                    "JoystickIdentifier",
+                    ""
+                  ]
+                },
+                {
+                  "type": {
+                    "value": "SpriteMultitouchJoystick::IsAngleIn4WayDirection"
+                  },
+                  "parameters": [
+                    "",
+                    "SpriteMultitouchJoystick::JoystickAngle(ControllerIdentifier, JoystickIdentifier)",
+                    "Direction",
+                    ""
+                  ]
+                }
+              ],
+              "actions": [
+                {
+                  "type": {
+                    "value": "SetReturnBoolean"
+                  },
+                  "parameters": [
+                    "True"
+                  ]
+                }
+              ]
+            }
+          ],
+          "parameters": [
+            {
+              "description": "Multitouch controller identifier (1, 2, 3, 4...)",
+              "name": "ControllerIdentifier",
+              "type": "expression"
+            },
+            {
+              "description": "Joystick name",
+              "name": "JoystickIdentifier",
+              "supplementaryInformation": "[\"Primary\",\"Secondary\"]",
+              "type": "stringWithSelector"
+            },
+            {
+              "description": "Direction",
+              "name": "Direction",
+              "supplementaryInformation": "[\"Up\",\"Down\",\"Left\",\"Right\"]",
+              "type": "stringWithSelector"
+            }
+          ],
+          "objectGroups": []
+        },
+        {
+          "description": "Check if joystick is pushed in a given direction.",
+          "fullName": "Joystick pushed in a direction (8-way)",
+          "functionType": "Condition",
+          "name": "IsDirectionPushed8Way",
+          "sentence": "Joystick _PARAM2_ of multitouch controller _PARAM1_ is pushed in direction _PARAM3_",
+          "events": [
+            {
+              "type": "BuiltinCommonInstructions::Comment",
+              "color": {
+                "b": 109,
+                "g": 230,
+                "r": 255,
+                "textB": 0,
+                "textG": 0,
+                "textR": 0
+              },
+              "comment": "Make sure the joystick has moved from center"
+            },
+            {
+              "type": "BuiltinCommonInstructions::Standard",
+              "conditions": [
+                {
+                  "type": {
+                    "value": "SpriteMultitouchJoystick::JoystickForce"
+                  },
+                  "parameters": [
+                    "",
+                    ">",
+                    "0",
+                    "ControllerIdentifier",
+                    "JoystickIdentifier",
+                    ""
+                  ]
+                },
+                {
+                  "type": {
+                    "value": "SpriteMultitouchJoystick::IsAngleIn8WayDirection"
+                  },
+                  "parameters": [
+                    "",
+                    "SpriteMultitouchJoystick::JoystickAngle(ControllerIdentifier, JoystickIdentifier)",
+                    "Direction",
+                    ""
+                  ]
+                }
+              ],
+              "actions": [
+                {
+                  "type": {
+                    "value": "SetReturnBoolean"
+                  },
+                  "parameters": [
+                    "True"
+                  ]
+                }
+              ]
+            }
+          ],
+          "parameters": [
+            {
+              "description": "Multitouch controller identifier (1, 2, 3, 4...)",
+              "name": "ControllerIdentifier",
+              "type": "expression"
+            },
+            {
+              "description": "Joystick name",
+              "name": "JoystickIdentifier",
+              "supplementaryInformation": "[\"Primary\",\"Secondary\"]",
+              "type": "stringWithSelector"
+            },
+            {
+              "description": "Direction",
+              "name": "Direction",
+              "supplementaryInformation": "[\"Up\",\"Down\",\"Left\",\"Right\",\"UpLeft\",\"UpRight\",\"DownLeft\",\"DownRight\"]",
+              "type": "stringWithSelector"
+            }
+          ],
+          "objectGroups": []
+        },
+        {
+          "description": "the percentage the thumb has been pulled away from the joystick center (Range: 0 to 1).",
+          "fullName": "Joystick force (deprecated)",
+          "functionType": "ExpressionAndCondition",
+          "name": "JoystickForce",
+          "private": true,
+          "sentence": "Joystick _PARAM2_ of multitouch controller _PARAM1_ force",
+          "events": [
+            {
+              "type": "BuiltinCommonInstructions::Standard",
+              "conditions": [],
+              "actions": [
+                {
+                  "type": {
+                    "value": "SetReturnNumber"
+                  },
+                  "parameters": [
+                    "SpriteMultitouchJoystick::StickForce(ControllerIdentifier, JoystickIdentifier)"
+                  ]
+                }
+              ]
+            }
+          ],
+          "expressionType": {
+            "type": "expression"
+          },
+          "parameters": [
+            {
+              "description": "Multitouch controller identifier (1, 2, 3, 4...)",
+              "name": "ControllerIdentifier",
+              "type": "expression"
+            },
+            {
+              "description": "Joystick name",
+              "name": "JoystickIdentifier",
+              "supplementaryInformation": "[\"Primary\",\"Secondary\"]",
+              "type": "stringWithSelector"
+            },
+            {
+              "description": "",
+              "name": "Coucou",
+              "type": "expression"
+            }
+          ],
+          "objectGroups": []
+        },
+        {
+          "description": "the force of multitouch contoller stick (from 0 to 1).",
+          "fullName": "Stick force",
+          "functionType": "ExpressionAndCondition",
+          "name": "StickForce",
+          "sentence": "multitouch controller _PARAM1_ _PARAM2_ stick force",
+          "events": [
+            {
+              "type": "BuiltinCommonInstructions::Standard",
+              "conditions": [],
+              "actions": [
+                {
+                  "type": {
+                    "value": "SetReturnNumber"
+                  },
+                  "parameters": [
+                    "max(0, Controllers[ControllerIdentifier].Joystick[JoystickIdentifier].Force - SpriteMultitouchJoystick::DeadZone(ControllerIdentifier, JoystickIdentifier)) / (1 - SpriteMultitouchJoystick::DeadZone(ControllerIdentifier, JoystickIdentifier))"
+                  ]
+                }
+              ]
+            }
+          ],
+          "expressionType": {
+            "type": "expression"
+          },
+          "parameters": [
+            {
+              "description": "Multitouch controller identifier (1, 2, 3, 4...)",
+              "name": "ControllerIdentifier",
+              "type": "expression"
+            },
+            {
+              "description": "Stick name",
+              "name": "JoystickIdentifier",
+              "supplementaryInformation": "[\"Primary\",\"Secondary\"]",
+              "type": "stringWithSelector"
+            }
+          ],
+          "objectGroups": []
+        },
+        {
+          "description": "Change the percentage the thumb has been pulled away from the joystick center (Range: 0 to 1).",
+          "fullName": "Joystick force",
+          "functionType": "Action",
+          "name": "SetJoystickForce",
+          "private": true,
+          "sentence": "Change the force of the joystick _PARAM2_ of multitouch controller _PARAM1_ to _PARAM3_",
+          "events": [
+            {
+              "type": "BuiltinCommonInstructions::Standard",
+              "conditions": [],
+              "actions": [
+                {
+                  "type": {
+                    "value": "SetNumberVariable"
+                  },
+                  "parameters": [
+                    "Controllers[ControllerIdentifier].Joystick[JoystickIdentifier].Force",
+                    "=",
+                    "Value"
+                  ]
+                }
+              ]
+            }
+          ],
+          "parameters": [
+            {
+              "description": "Multitouch controller identifier (1, 2, 3, 4...)",
+              "name": "ControllerIdentifier",
+              "type": "expression"
+            },
+            {
+              "description": "Joystick name",
+              "name": "JoystickIdentifier",
+              "type": "string"
+            },
+            {
+              "description": "Value",
+              "name": "Value",
+              "type": "expression"
+            }
+          ],
+          "objectGroups": []
+        },
+        {
+          "description": "Return the angle the joystick is pointing towards (Range: -180 to 180).",
+          "fullName": "Joystick angle (deprecated)",
+          "functionType": "Expression",
+          "name": "JoystickAngle",
+          "private": true,
+          "sentence": "",
+          "events": [
+            {
+              "type": "BuiltinCommonInstructions::Standard",
+              "conditions": [],
+              "actions": [
+                {
+                  "type": {
+                    "value": "SetReturnNumber"
+                  },
+                  "parameters": [
+                    "SpriteMultitouchJoystick::StickAngle(ControllerIdentifier, JoystickIdentifier)"
+                  ]
+                }
+              ]
+            }
+          ],
+          "expressionType": {
+            "type": "expression"
+          },
+          "parameters": [
+            {
+              "description": "Multitouch controller identifier (1, 2, 3, 4...)",
+              "name": "ControllerIdentifier",
+              "type": "expression"
+            },
+            {
+              "description": "Joystick name",
+              "name": "JoystickIdentifier",
+              "supplementaryInformation": "[\"Primary\",\"Secondary\"]",
+              "type": "stringWithSelector"
+            }
+          ],
+          "objectGroups": []
+        },
+        {
+          "description": "Return the angle the multitouch controller stick is pointing towards (Range: -180 to 180).",
+          "fullName": "Stick angle",
+          "functionType": "Expression",
+          "name": "StickAngle",
+          "sentence": "",
+          "events": [
+            {
+              "type": "BuiltinCommonInstructions::Standard",
+              "conditions": [],
+              "actions": [
+                {
+                  "type": {
+                    "value": "SetReturnNumber"
+                  },
+                  "parameters": [
+                    "Controllers[ControllerIdentifier].Joystick[JoystickIdentifier].Angle"
+                  ]
+                }
+              ]
+            }
+          ],
+          "expressionType": {
+            "type": "expression"
+          },
+          "parameters": [
+            {
+              "description": "Multitouch controller identifier (1, 2, 3, 4...)",
+              "name": "ControllerIdentifier",
+              "type": "expression"
+            },
+            {
+              "description": "Joystick name",
+              "name": "JoystickIdentifier",
+              "supplementaryInformation": "[\"Primary\",\"Secondary\"]",
+              "type": "stringWithSelector"
+            }
+          ],
+          "objectGroups": []
+        },
+        {
+          "description": "Change the angle the joystick is pointing towards (Range: -180 to 180).",
+          "fullName": "Joystick angle",
+          "functionType": "Action",
+          "name": "SetJoystickAngle",
+          "private": true,
+          "sentence": "Change the angle of the joystick _PARAM2_ of multitouch controller _PARAM1_ to _PARAM3_",
+          "events": [
+            {
+              "type": "BuiltinCommonInstructions::Standard",
+              "conditions": [],
+              "actions": [
+                {
+                  "type": {
+                    "value": "SetNumberVariable"
+                  },
+                  "parameters": [
+                    "Controllers[ControllerIdentifier].Joystick[JoystickIdentifier].Angle",
+                    "=",
+                    "Value"
+                  ]
+                }
+              ]
+            }
+          ],
+          "parameters": [
+            {
+              "description": "Multitouch controller identifier (1, 2, 3, 4...)",
+              "name": "ControllerIdentifier",
+              "type": "expression"
+            },
+            {
+              "description": "Joystick name",
+              "name": "JoystickIdentifier",
+              "type": "string"
+            },
+            {
+              "description": "Value",
+              "name": "Value",
+              "type": "expression"
+            }
+          ],
+          "objectGroups": []
+        },
+        {
+          "description": "Return the multitouch contoller stick force on X axis (from -1 at the left to 1 at the right).",
+          "fullName": "Stick X force",
+          "functionType": "Expression",
+          "name": "StickForceX",
+          "sentence": "",
+          "events": [
+            {
+              "type": "BuiltinCommonInstructions::Standard",
+              "conditions": [],
+              "actions": [
+                {
+                  "type": {
+                    "value": "SetReturnNumber"
+                  },
+                  "parameters": [
+                    "XFromAngleAndDistance(SpriteMultitouchJoystick::StickAngle(ControllerIdentifier, JoystickIdentifier), SpriteMultitouchJoystick::StickForce(ControllerIdentifier, JoystickIdentifier))"
+                  ]
+                }
+              ]
+            }
+          ],
+          "expressionType": {
+            "type": "expression"
+          },
+          "parameters": [
+            {
+              "description": "Multitouch controller identifier (1, 2, 3, 4...)",
+              "name": "ControllerIdentifier",
+              "type": "expression"
+            },
+            {
+              "description": "Joystick name",
+              "name": "JoystickIdentifier",
+              "supplementaryInformation": "[\"Primary\",\"Secondary\"]",
+              "type": "stringWithSelector"
+            }
+          ],
+          "objectGroups": []
+        },
+        {
+          "description": "Return the multitouch contoller stick force on Y axis (from -1 at the top to 1 at the bottom).",
+          "fullName": "Stick Y force",
+          "functionType": "Expression",
+          "name": "StickForceY",
+          "sentence": "",
+          "events": [
+            {
+              "type": "BuiltinCommonInstructions::Standard",
+              "conditions": [],
+              "actions": [
+                {
+                  "type": {
+                    "value": "SetReturnNumber"
+                  },
+                  "parameters": [
+                    "YFromAngleAndDistance(SpriteMultitouchJoystick::StickAngle(ControllerIdentifier, JoystickIdentifier), SpriteMultitouchJoystick::StickForce(ControllerIdentifier, JoystickIdentifier))"
+                  ]
+                }
+              ]
+            }
+          ],
+          "expressionType": {
+            "type": "expression"
+          },
+          "parameters": [
+            {
+              "description": "Multitouch controller identifier (1, 2, 3, 4...)",
+              "name": "ControllerIdentifier",
+              "type": "expression"
+            },
+            {
+              "description": "Joystick name",
+              "name": "JoystickIdentifier",
+              "supplementaryInformation": "[\"Primary\",\"Secondary\"]",
+              "type": "stringWithSelector"
+            }
+          ],
+          "objectGroups": []
+        },
+        {
+          "description": "Check if a new touch has started on the right or left side of the screen.",
+          "fullName": "New touch on a screen side",
+          "functionType": "Condition",
+          "group": "Multitouch Joystick",
+          "name": "HasTouchStartedOnScreenSide",
+          "sentence": "A new touch has started on the _PARAM2_ side of the screen on _PARAM1_'s layer",
+          "events": [
+            {
+              "type": "BuiltinCommonInstructions::Standard",
+              "conditions": [
+                {
+                  "type": {
+                    "value": "HasAnyTouchOrMouseStarted"
+                  },
+                  "parameters": [
+                    ""
+                  ]
+                }
+              ],
+              "actions": [],
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [
+                    {
+                      "type": {
+                        "value": "CompareArgumentAsString"
+                      },
+                      "parameters": [
+                        "\"Side\"",
+                        "=",
+                        "\"Left\""
+                      ]
+                    },
+                    {
+                      "type": {
+                        "value": "TouchX"
+                      },
+                      "parameters": [
+                        "",
+                        "StartedTouchOrMouseId(0)",
+                        "<",
+                        "CameraCenterX(Object.Layer())",
+                        "Object.Layer()",
+                        ""
+                      ]
+                    }
+                  ],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SetReturnBoolean"
+                      },
+                      "parameters": [
+                        "True"
+                      ]
+                    }
+                  ]
+                },
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [
+                    {
+                      "type": {
+                        "value": "CompareArgumentAsString"
+                      },
+                      "parameters": [
+                        "\"Side\"",
+                        "=",
+                        "\"Right\""
+                      ]
+                    },
+                    {
+                      "type": {
+                        "value": "TouchX"
+                      },
+                      "parameters": [
+                        "",
+                        "StartedTouchOrMouseId(0)",
+                        ">=",
+                        "CameraCenterX(Object.Layer())",
+                        "Object.Layer()",
+                        ""
+                      ]
+                    }
+                  ],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SetReturnBoolean"
+                      },
+                      "parameters": [
+                        "True"
+                      ]
+                    }
+                  ]
+                }
+              ]
+            }
+          ],
+          "parameters": [
+            {
+              "description": "Multitouch joystick",
+              "name": "Object",
+              "supplementaryInformation": "SpriteMultitouchJoystick::SpriteMultitouchJoystick",
+              "type": "objectList"
+            },
+            {
+              "description": "Screen side",
+              "name": "Side",
+              "supplementaryInformation": "[\"Left\",\"Right\"]",
+              "type": "stringWithSelector"
+            }
+          ],
+          "objectGroups": []
+        }
+      ],
+      "eventsBasedBehaviors": [
+        {
+          "description": "Joystick that can be controlled by interacting with a touchscreen.",
+          "fullName": "Multitouch Joystick",
+          "name": "MultitouchJoystick",
+          "objectType": "",
+          "private": true,
+          "eventsFunctions": [
+            {
+              "fullName": "",
+              "functionType": "Action",
+              "name": "onCreated",
+              "sentence": "",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SpriteMultitouchJoystick::SetDeadZone"
+                      },
+                      "parameters": [
+                        "",
+                        "ControllerIdentifier",
+                        "JoystickIdentifier",
+                        "DeadZoneRadius",
+                        ""
+                      ]
+                    },
+                    {
+                      "type": {
+                        "value": "SpriteMultitouchJoystick::MultitouchJoystick::Reset"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        ""
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "SpriteMultitouchJoystick::MultitouchJoystick",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "fullName": "",
+              "functionType": "Action",
+              "name": "onDeActivate",
+              "sentence": "",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SpriteMultitouchJoystick::MultitouchJoystick::Reset"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        ""
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "SpriteMultitouchJoystick::MultitouchJoystick",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "fullName": "",
+              "functionType": "Action",
+              "name": "doStepPreEvents",
+              "sentence": "",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [
+                    {
+                      "type": {
+                        "value": "HasGameJustResumed"
+                      },
+                      "parameters": [
+                        ""
+                      ]
+                    }
+                  ],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SpriteMultitouchJoystick::MultitouchJoystick::Reset"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        ""
+                      ]
+                    }
+                  ]
+                },
+                {
+                  "colorB": 228,
+                  "colorG": 176,
+                  "colorR": 74,
+                  "creationTime": 0,
+                  "name": "Manage touches",
+                  "source": "",
+                  "type": "BuiltinCommonInstructions::Group",
+                  "events": [
+                    {
+                      "type": "BuiltinCommonInstructions::Standard",
+                      "conditions": [
+                        {
+                          "type": {
+                            "value": "HasAnyTouchOrMouseStarted"
+                          },
+                          "parameters": [
+                            ""
+                          ]
+                        }
+                      ],
+                      "actions": [
+                        {
+                          "type": {
+                            "value": "SpriteMultitouchJoystick::MultitouchJoystick::SetPropertyTouchIndex"
+                          },
+                          "parameters": [
+                            "Object",
+                            "Behavior",
+                            "=",
+                            "0"
+                          ]
+                        }
+                      ],
+                      "events": [
+                        {
+                          "type": "BuiltinCommonInstructions::Repeat",
+                          "repeatExpression": "StartedTouchOrMouseCount()",
+                          "conditions": [],
+                          "actions": [],
+                          "events": [
+                            {
+                              "type": "BuiltinCommonInstructions::Standard",
+                              "conditions": [
+                                {
+                                  "type": {
+                                    "value": "SpriteMultitouchJoystick::MultitouchJoystick::PropertyTouchId"
+                                  },
+                                  "parameters": [
+                                    "Object",
+                                    "Behavior",
+                                    "=",
+                                    "0"
+                                  ]
+                                },
+                                {
+                                  "type": {
+                                    "value": "CollisionPoint"
+                                  },
+                                  "parameters": [
+                                    "Object",
+                                    "TouchX(StartedTouchOrMouseId(TouchIndex), Object.Layer(), 0)",
+                                    "TouchY(StartedTouchOrMouseId(TouchIndex), Object.Layer(), 0)"
+                                  ]
+                                }
+                              ],
+                              "actions": [
+                                {
+                                  "type": {
+                                    "value": "SpriteMultitouchJoystick::MultitouchJoystick::SetPropertyTouchId"
+                                  },
+                                  "parameters": [
+                                    "Object",
+                                    "Behavior",
+                                    "=",
+                                    "StartedTouchOrMouseId(TouchIndex)"
+                                  ]
+                                }
+                              ]
+                            },
+                            {
+                              "type": "BuiltinCommonInstructions::Standard",
+                              "conditions": [],
+                              "actions": [
+                                {
+                                  "type": {
+                                    "value": "SpriteMultitouchJoystick::MultitouchJoystick::SetPropertyTouchIndex"
+                                  },
+                                  "parameters": [
+                                    "Object",
+                                    "Behavior",
+                                    "+",
+                                    "1"
+                                  ]
+                                }
+                              ]
+                            }
+                          ]
+                        }
+                      ]
+                    },
+                    {
+                      "type": "BuiltinCommonInstructions::Comment",
+                      "color": {
+                        "b": 109,
+                        "g": 230,
+                        "r": 255,
+                        "textB": 0,
+                        "textG": 0,
+                        "textR": 0
+                      },
+                      "comment": "Move thumb back to center when not being pressed (acts like a spring on a real controller)"
+                    },
+                    {
+                      "type": "BuiltinCommonInstructions::Standard",
+                      "conditions": [
+                        {
+                          "type": {
+                            "value": "HasTouchEnded"
+                          },
+                          "parameters": [
+                            "",
+                            "TouchId"
+                          ]
+                        }
+                      ],
+                      "actions": [
+                        {
+                          "type": {
+                            "value": "SpriteMultitouchJoystick::MultitouchJoystick::Reset"
+                          },
+                          "parameters": [
+                            "Object",
+                            "Behavior",
+                            ""
+                          ]
+                        }
+                      ]
+                    }
+                  ],
+                  "parameters": []
+                },
+                {
+                  "colorB": 228,
+                  "colorG": 176,
+                  "colorR": 74,
+                  "creationTime": 0,
+                  "name": "Update joystick position",
+                  "source": "",
+                  "type": "BuiltinCommonInstructions::Group",
+                  "events": [
+                    {
+                      "type": "BuiltinCommonInstructions::Standard",
+                      "conditions": [
+                        {
+                          "type": {
+                            "value": "SpriteMultitouchJoystick::MultitouchJoystick::PropertyTouchId"
+                          },
+                          "parameters": [
+                            "Object",
+                            "Behavior",
+                            "!=",
+                            "0"
+                          ]
+                        }
+                      ],
+                      "actions": [
+                        {
+                          "type": {
+                            "value": "SpriteMultitouchJoystick::MultitouchJoystick::SetJoystickAngle"
+                          },
+                          "parameters": [
+                            "Object",
+                            "Behavior",
+                            "AngleBetweenPositions(Object.CenterX(), Object.CenterY(), TouchX(TouchId, Object.Layer(), 0), TouchY(TouchId, Object.Layer(), 0))",
+                            "AngleBetweenPositions(Object.CenterX(), Object.CenterY(), TouchX(TouchId, Object.Layer(), 0), TouchY(TouchId, Object.Layer(), 0))"
+                          ]
+                        },
+                        {
+                          "type": {
+                            "value": "SpriteMultitouchJoystick::MultitouchJoystick::SetJoystickForce"
+                          },
+                          "parameters": [
+                            "Object",
+                            "Behavior",
+                            "=",
+                            "clamp(2 * DistanceBetweenPositions(Object.CenterX(), Object.CenterY(), TouchX(TouchId, Object.Layer(), 0), TouchY(TouchId, Object.Layer(), 0)) / Object.Width(), 0, 1)",
+                            ""
+                          ]
+                        }
+                      ]
+                    }
+                  ],
+                  "parameters": []
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "SpriteMultitouchJoystick::MultitouchJoystick",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "the joystick force (from 0 to 1).",
+              "fullName": "Joystick force",
+              "functionType": "ExpressionAndCondition",
+              "name": "JoystickForce",
+              "sentence": "the joystick force",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SetReturnNumber"
+                      },
+                      "parameters": [
+                        "max(0, JoystickForce - DeadZoneRadius) / (1 - DeadZoneRadius)"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "expressionType": {
+                "type": "expression"
+              },
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "SpriteMultitouchJoystick::MultitouchJoystick",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "fullName": "",
+              "functionType": "ActionWithOperator",
+              "getterName": "JoystickForce",
+              "name": "SetJoystickForce",
+              "private": true,
+              "sentence": "",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SpriteMultitouchJoystick::MultitouchJoystick::SetPropertyJoystickForce"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "=",
+                        "Value"
+                      ]
+                    },
+                    {
+                      "type": {
+                        "value": "SpriteMultitouchJoystick::SetJoystickForce"
+                      },
+                      "parameters": [
+                        "",
+                        "ControllerIdentifier",
+                        "JoystickIdentifier",
+                        "JoystickForce",
+                        ""
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "SpriteMultitouchJoystick::MultitouchJoystick",
+                  "type": "behavior"
+                },
+                {
+                  "description": "",
+                  "name": "Parameter",
+                  "type": "objectList"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "Return the angle the joystick is pointing towards (Range: -180 to 180).",
+              "fullName": "Joystick angle",
+              "functionType": "Expression",
+              "name": "JoystickAngle",
+              "sentence": "",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SetReturnNumber"
+                      },
+                      "parameters": [
+                        "JoystickAngle"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "expressionType": {
+                "type": "expression"
+              },
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "SpriteMultitouchJoystick::MultitouchJoystick",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "Change the angle the joystick is pointing towards (Range: -180 to 180).",
+              "fullName": "Joystick angle",
+              "functionType": "Action",
+              "name": "SetJoystickAngle",
+              "private": true,
+              "sentence": "Change the joystick angle of _PARAM0_ to _PARAM2_",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SpriteMultitouchJoystick::MultitouchJoystick::SetPropertyJoystickAngle"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "=",
+                        "Value"
+                      ]
+                    },
+                    {
+                      "type": {
+                        "value": "SpriteMultitouchJoystick::SetJoystickAngle"
+                      },
+                      "parameters": [
+                        "",
+                        "ControllerIdentifier",
+                        "JoystickIdentifier",
+                        "JoystickAngle",
+                        ""
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "SpriteMultitouchJoystick::MultitouchJoystick",
+                  "type": "behavior"
+                },
+                {
+                  "description": "Angle",
+                  "name": "Value",
+                  "type": "expression"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "Return the stick force on X axis (from -1 at the left to 1 at the right).",
+              "fullName": "Stick X force",
+              "functionType": "Expression",
+              "name": "StickForceX",
+              "sentence": "",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SetReturnNumber"
+                      },
+                      "parameters": [
+                        "Object.Behavior::JoystickForce() * cos(ToRad(Object.Behavior::JoystickAngle()))"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "expressionType": {
+                "type": "expression"
+              },
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "SpriteMultitouchJoystick::MultitouchJoystick",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "Return the stick force on Y axis (from -1 at the top to 1 at the bottom).",
+              "fullName": "Stick Y force",
+              "functionType": "Expression",
+              "name": "StickForceY",
+              "sentence": "",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SetReturnNumber"
+                      },
+                      "parameters": [
+                        "Object.Behavior::JoystickForce() * sin(ToRad(Object.Behavior::JoystickAngle()))"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "expressionType": {
+                "type": "expression"
+              },
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "SpriteMultitouchJoystick::MultitouchJoystick",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "Check if joystick is pushed in a given direction.",
+              "fullName": "Joystick pushed in a direction (4-way movement)",
+              "functionType": "Condition",
+              "name": "IsDirectionPushed4Way",
+              "sentence": "_PARAM0_ is pushed in direction _PARAM2_",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [
+                    {
+                      "type": {
+                        "value": "SpriteMultitouchJoystick::MultitouchJoystick::JoystickForce"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        ">",
+                        "0",
+                        ""
+                      ]
+                    },
+                    {
+                      "type": {
+                        "value": "SpriteMultitouchJoystick::IsAngleIn4WayDirection"
+                      },
+                      "parameters": [
+                        "",
+                        "JoystickAngle",
+                        "Direction",
+                        ""
+                      ]
+                    }
+                  ],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SetReturnBoolean"
+                      },
+                      "parameters": [
+                        "True"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "SpriteMultitouchJoystick::MultitouchJoystick",
+                  "type": "behavior"
+                },
+                {
+                  "description": "Direction",
+                  "name": "Direction",
+                  "supplementaryInformation": "[\"Up\",\"Down\",\"Left\",\"Right\"]",
+                  "type": "stringWithSelector"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "Check if joystick is pushed in a given direction.",
+              "fullName": "Joystick pushed in a direction (8-way movement)",
+              "functionType": "Condition",
+              "name": "IsDirectionPushed8Way",
+              "sentence": "_PARAM0_ is pushed in direction _PARAM2_",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [
+                    {
+                      "type": {
+                        "value": "SpriteMultitouchJoystick::MultitouchJoystick::JoystickForce"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        ">",
+                        "0",
+                        ""
+                      ]
+                    },
+                    {
+                      "type": {
+                        "value": "SpriteMultitouchJoystick::IsAngleIn8WayDirection"
+                      },
+                      "parameters": [
+                        "",
+                        "JoystickAngle",
+                        "Direction",
+                        ""
+                      ]
+                    }
+                  ],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SetReturnBoolean"
+                      },
+                      "parameters": [
+                        "True"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "SpriteMultitouchJoystick::MultitouchJoystick",
+                  "type": "behavior"
+                },
+                {
+                  "description": "Direction",
+                  "name": "Direction",
+                  "supplementaryInformation": "[\"Up\",\"Down\",\"Left\",\"Right\",\"UpLeft\",\"UpRight\",\"DownLeft\",\"DownRight\"]",
+                  "type": "stringWithSelector"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "Check if a joystick is pressed.",
+              "fullName": "Joystick pressed",
+              "functionType": "Condition",
+              "name": "IsPressed",
+              "sentence": "Joystick _PARAM0_ is pressed",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [
+                    {
+                      "type": {
+                        "value": "SpriteMultitouchJoystick::MultitouchJoystick::PropertyTouchId"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "!=",
+                        "0"
+                      ]
+                    }
+                  ],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SetReturnBoolean"
+                      },
+                      "parameters": [
+                        "True"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "SpriteMultitouchJoystick::MultitouchJoystick",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "Reset the joystick values (except for angle, which stays the same)",
+              "fullName": "Reset",
+              "functionType": "Action",
+              "name": "Reset",
+              "private": true,
+              "sentence": "Reset the joystick of _PARAM0_",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SpriteMultitouchJoystick::MultitouchJoystick::SetJoystickForce"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "=",
+                        "0",
+                        ""
+                      ]
+                    },
+                    {
+                      "type": {
+                        "value": "SpriteMultitouchJoystick::MultitouchJoystick::SetPropertyTouchId"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "=",
+                        "0"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "SpriteMultitouchJoystick::MultitouchJoystick",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "the multitouch controller identifier.",
+              "fullName": "Multitouch controller identifier",
+              "functionType": "ExpressionAndCondition",
+              "group": "Multitouch Joystick configuration",
+              "name": "ControllerIdentifier",
+              "sentence": "the multitouch controller identifier",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SetReturnNumber"
+                      },
+                      "parameters": [
+                        "ControllerIdentifier"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "expressionType": {
+                "type": "expression"
+              },
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "SpriteMultitouchJoystick::MultitouchJoystick",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "fullName": "",
+              "functionType": "ActionWithOperator",
+              "getterName": "ControllerIdentifier",
+              "name": "SetControllerIdentifier",
+              "sentence": "",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SpriteMultitouchJoystick::MultitouchJoystick::SetPropertyControllerIdentifier"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "=",
+                        "Value"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "SpriteMultitouchJoystick::MultitouchJoystick",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "the joystick name.",
+              "fullName": "Joystick name",
+              "functionType": "ExpressionAndCondition",
+              "group": "Multitouch Joystick configuration",
+              "name": "JoystickIdentifier",
+              "sentence": "the joystick name",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SetReturnString"
+                      },
+                      "parameters": [
+                        "JoystickIdentifier"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "expressionType": {
+                "type": "string"
+              },
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "SpriteMultitouchJoystick::MultitouchJoystick",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "fullName": "",
+              "functionType": "ActionWithOperator",
+              "getterName": "JoystickIdentifier",
+              "name": "SetJoystickIdentifier",
+              "sentence": "",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SpriteMultitouchJoystick::MultitouchJoystick::SetPropertyJoystickIdentifier"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "=",
+                        "Value"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "SpriteMultitouchJoystick::MultitouchJoystick",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "the dead zone radius (range: 0 to 1) of the joystick. The deadzone is an area for which movement on sticks won't be taken into account (instead, the stick will be considered as not moved).",
+              "fullName": "Dead zone radius",
+              "functionType": "ExpressionAndCondition",
+              "group": "Multitouch Joystick configuration",
+              "name": "DeadZoneRadius",
+              "sentence": "the dead zone radius",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SetReturnNumber"
+                      },
+                      "parameters": [
+                        "DeadZoneRadius"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "expressionType": {
+                "type": "expression"
+              },
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "SpriteMultitouchJoystick::MultitouchJoystick",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "fullName": "",
+              "functionType": "ActionWithOperator",
+              "getterName": "DeadZoneRadius",
+              "name": "SetDeadZoneRadius",
+              "sentence": "",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SpriteMultitouchJoystick::MultitouchJoystick::SetPropertyDeadZoneRadius"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "=",
+                        "Value"
+                      ]
+                    },
+                    {
+                      "type": {
+                        "value": "SpriteMultitouchJoystick::SetDeadZone"
+                      },
+                      "parameters": [
+                        "",
+                        "ControllerIdentifier",
+                        "JoystickIdentifier",
+                        "Value",
+                        ""
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "SpriteMultitouchJoystick::MultitouchJoystick",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "Force the joystick into the pressing state.",
+              "fullName": "Force start pressing",
+              "functionType": "Action",
+              "name": "ForceStartPressing",
+              "sentence": "Force start pressing _PARAM0_ with touch identifier: _PARAM2_",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SpriteMultitouchJoystick::MultitouchJoystick::SetPropertyTouchId"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "=",
+                        "Value"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "SpriteMultitouchJoystick::MultitouchJoystick",
+                  "type": "behavior"
+                },
+                {
+                  "description": "Touch identifier",
+                  "name": "Value",
+                  "type": "expression"
+                }
+              ],
+              "objectGroups": []
+            }
+          ],
+          "propertyDescriptors": [
+            {
+              "value": "1",
+              "type": "Number",
+              "label": "Multitouch controller identifier (1, 2, 3, 4...)",
+              "description": "",
+              "group": "",
+              "extraInformation": [],
+              "name": "ControllerIdentifier"
+            },
+            {
+              "value": "Primary",
+              "type": "String",
+              "label": "Joystick name",
+              "description": "",
+              "group": "",
+              "extraInformation": [],
+              "name": "JoystickIdentifier"
+            },
+            {
+              "value": "0.4",
+              "type": "Number",
+              "label": "Dead zone radius (range: 0 to 1)",
+              "description": "The deadzone is an area for which movement on sticks won't be taken into account (instead, the stick will be considered as not moved)",
+              "group": "",
+              "extraInformation": [],
+              "name": "DeadZoneRadius"
+            },
+            {
+              "value": "0",
+              "type": "Number",
+              "label": "Joystick angle (range: -180 to 180)",
+              "description": "",
+              "group": "",
+              "extraInformation": [],
+              "hidden": true,
+              "name": "JoystickAngle"
+            },
+            {
+              "value": "0",
+              "type": "Number",
+              "label": "Joystick force (range: 0 to 1)",
+              "description": "",
+              "group": "",
+              "extraInformation": [],
+              "hidden": true,
+              "name": "JoystickForce"
+            },
+            {
+              "value": "0",
+              "type": "Number",
+              "label": "",
+              "description": "",
+              "group": "",
+              "extraInformation": [],
+              "hidden": true,
+              "name": "TouchId"
+            },
+            {
+              "value": "",
+              "type": "Number",
+              "label": "",
+              "description": "",
+              "group": "",
+              "extraInformation": [],
+              "hidden": true,
+              "name": "TouchIndex"
+            }
+          ],
+          "sharedPropertyDescriptors": []
+        },
+        {
+          "description": "Detect button presses made on a touchscreen.",
+          "fullName": "Multitouch button",
+          "name": "MultitouchButton",
+          "objectType": "",
+          "eventsFunctions": [
+            {
+              "fullName": "",
+              "functionType": "Action",
+              "name": "doStepPreEvents",
+              "sentence": "",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [
+                    {
+                      "type": {
+                        "value": "SpriteMultitouchJoystick::MultitouchButton::IsReleased"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        ""
+                      ]
+                    }
+                  ],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SpriteMultitouchJoystick::MultitouchButton::SetPropertyIsReleased"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "no"
+                      ]
+                    },
+                    {
+                      "type": {
+                        "value": "SpriteMultitouchJoystick::MultitouchButton::SetButtonState"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "\"Idle\"",
+                        ""
+                      ]
+                    }
+                  ]
+                },
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [
+                    {
+                      "type": {
+                        "inverted": true,
+                        "value": "SpriteMultitouchJoystick::MultitouchButton::IsPressed"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        ""
+                      ]
+                    },
+                    {
+                      "type": {
+                        "value": "HasAnyTouchOrMouseStarted"
+                      },
+                      "parameters": [
+                        ""
+                      ]
+                    }
+                  ],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SpriteMultitouchJoystick::MultitouchButton::SetPropertyTouchIndex"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "=",
+                        "0"
+                      ]
+                    }
+                  ],
+                  "events": [
+                    {
+                      "type": "BuiltinCommonInstructions::Repeat",
+                      "repeatExpression": "StartedTouchOrMouseCount()",
+                      "conditions": [],
+                      "actions": [],
+                      "events": [
+                        {
+                          "type": "BuiltinCommonInstructions::Standard",
+                          "conditions": [
+                            {
+                              "type": {
+                                "value": "BuiltinCommonInstructions::Or"
+                              },
+                              "parameters": [],
+                              "subInstructions": [
+                                {
+                                  "type": {
+                                    "value": "CollisionPoint"
+                                  },
+                                  "parameters": [
+                                    "Object",
+                                    "TouchX(StartedTouchOrMouseId(TouchIndex), Object.Layer())",
+                                    "TouchY(StartedTouchOrMouseId(TouchIndex), Object.Layer())"
+                                  ]
+                                },
+                                {
+                                  "type": {
+                                    "value": "SpriteMultitouchJoystick::MultitouchButton::PropertyRadius"
+                                  },
+                                  "parameters": [
+                                    "Object",
+                                    "Behavior",
+                                    ">",
+                                    "DistanceBetweenPositions(Object.CenterX(), Object.CenterY(), TouchX(StartedTouchOrMouseId(TouchIndex), Object.Layer()), TouchY(StartedTouchOrMouseId(TouchIndex), Object.Layer()))"
+                                  ]
+                                }
+                              ]
+                            }
+                          ],
+                          "actions": [
+                            {
+                              "type": {
+                                "value": "SpriteMultitouchJoystick::MultitouchButton::SetPropertyTouchId"
+                              },
+                              "parameters": [
+                                "Object",
+                                "Behavior",
+                                "=",
+                                "StartedTouchOrMouseId(TouchIndex)"
+                              ]
+                            },
+                            {
+                              "type": {
+                                "value": "SpriteMultitouchJoystick::MultitouchButton::SetButtonState"
+                              },
+                              "parameters": [
+                                "Object",
+                                "Behavior",
+                                "\"Pressed\"",
+                                ""
+                              ]
+                            }
+                          ]
+                        },
+                        {
+                          "type": "BuiltinCommonInstructions::Standard",
+                          "conditions": [],
+                          "actions": [
+                            {
+                              "type": {
+                                "value": "SpriteMultitouchJoystick::MultitouchButton::SetPropertyTouchIndex"
+                              },
+                              "parameters": [
+                                "Object",
+                                "Behavior",
+                                "+",
+                                "1"
+                              ]
+                            }
+                          ]
+                        }
+                      ]
+                    }
+                  ]
+                },
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [
+                    {
+                      "type": {
+                        "value": "SpriteMultitouchJoystick::MultitouchButton::IsPressed"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        ""
+                      ]
+                    },
+                    {
+                      "type": {
+                        "value": "HasTouchEnded"
+                      },
+                      "parameters": [
+                        "",
+                        "TouchId"
+                      ]
+                    }
+                  ],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SpriteMultitouchJoystick::MultitouchButton::SetButtonState"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "\"Released\"",
+                        ""
+                      ]
+                    },
+                    {
+                      "type": {
+                        "value": "SpriteMultitouchJoystick::MultitouchButton::SetPropertyIsReleased"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "yes"
+                      ]
+                    },
+                    {
+                      "type": {
+                        "value": "SpriteMultitouchJoystick::MultitouchButton::SetPropertyTouchId"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "=",
+                        "0"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "SpriteMultitouchJoystick::MultitouchButton",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "Check if button is released.",
+              "fullName": "Button released",
+              "functionType": "Condition",
+              "name": "IsReleased",
+              "sentence": "Button _PARAM0_ is released",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [
+                    {
+                      "type": {
+                        "value": "SpriteMultitouchJoystick::MultitouchButton::PropertyIsReleased"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior"
+                      ]
+                    }
+                  ],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SetReturnBoolean"
+                      },
+                      "parameters": [
+                        "True"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "SpriteMultitouchJoystick::MultitouchButton",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "Check if button is pressed.",
+              "fullName": "Button pressed",
+              "functionType": "Condition",
+              "name": "IsPressed",
+              "sentence": "Button _PARAM0_ is pressed",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [
+                    {
+                      "type": {
+                        "value": "SpriteMultitouchJoystick::MultitouchButton::PropertyTouchId"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "!=",
+                        "0"
+                      ]
+                    }
+                  ],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SetReturnBoolean"
+                      },
+                      "parameters": [
+                        "True"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "SpriteMultitouchJoystick::MultitouchButton",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "fullName": "Button state",
+              "functionType": "Action",
+              "name": "SetButtonState",
+              "private": true,
+              "sentence": "Mark the button _PARAM0_ as _PARAM2_",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SpriteMultitouchJoystick::SetButtonState"
+                      },
+                      "parameters": [
+                        "",
+                        "ControllerIdentifier",
+                        "ButtonIdentifier",
+                        "ButtonState",
+                        ""
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "SpriteMultitouchJoystick::MultitouchButton",
+                  "type": "behavior"
+                },
+                {
+                  "description": "Button state",
+                  "name": "ButtonState",
+                  "supplementaryInformation": "[\"Idle\",\"Pressed\",\"Released\"]",
+                  "type": "stringWithSelector"
+                }
+              ],
+              "objectGroups": []
+            }
+          ],
+          "propertyDescriptors": [
+            {
+              "value": "1",
+              "type": "Number",
+              "label": "Multitouch controller identifier (1, 2, 3, 4...)",
+              "description": "",
+              "group": "",
+              "extraInformation": [],
+              "name": "ControllerIdentifier"
+            },
+            {
+              "value": "A",
+              "type": "String",
+              "label": "Button identifier",
+              "description": "",
+              "group": "",
+              "extraInformation": [],
+              "name": "ButtonIdentifier"
+            },
+            {
+              "value": "0",
+              "type": "Number",
+              "label": "TouchID",
+              "description": "",
+              "group": "",
+              "extraInformation": [],
+              "hidden": true,
+              "name": "TouchId"
+            },
+            {
+              "value": "",
+              "type": "Number",
+              "label": "",
+              "description": "",
+              "group": "",
+              "extraInformation": [],
+              "hidden": true,
+              "name": "TouchIndex"
+            },
+            {
+              "value": "",
+              "type": "Boolean",
+              "label": "Button released",
+              "description": "",
+              "group": "",
+              "extraInformation": [],
+              "hidden": true,
+              "name": "IsReleased"
+            },
+            {
+              "value": "0",
+              "type": "Number",
+              "unit": "Pixel",
+              "label": "Triggering circle radius",
+              "description": "This circle adds up to the object collision mask.",
+              "group": "",
+              "extraInformation": [],
+              "name": "Radius"
+            }
+          ],
+          "sharedPropertyDescriptors": []
+        },
+        {
+          "description": "Control a platformer character with a multitouch controller.",
+          "fullName": "Platformer multitouch controller mapper",
+          "name": "PlatformerMultitouchMapper",
+          "objectType": "",
+          "eventsFunctions": [
+            {
+              "fullName": "",
+              "functionType": "Action",
+              "name": "doStepPreEvents",
+              "sentence": "",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [
+                    {
+                      "type": {
+                        "value": "SpriteMultitouchJoystick::IsDirectionPushed4Way"
+                      },
+                      "parameters": [
+                        "",
+                        "ControllerIdentifier",
+                        "JoystickIdentifier",
+                        "\"Left\"",
+                        ""
+                      ]
+                    }
+                  ],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "PlatformBehavior::SimulateLeftKey"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Property"
+                      ]
+                    }
+                  ]
+                },
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [
+                    {
+                      "type": {
+                        "value": "SpriteMultitouchJoystick::IsDirectionPushed4Way"
+                      },
+                      "parameters": [
+                        "",
+                        "ControllerIdentifier",
+                        "JoystickIdentifier",
+                        "\"Right\"",
+                        ""
+                      ]
+                    }
+                  ],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "PlatformBehavior::SimulateRightKey"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Property"
+                      ]
+                    }
+                  ]
+                },
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [
+                    {
+                      "type": {
+                        "value": "SpriteMultitouchJoystick::IsDirectionPushed4Way"
+                      },
+                      "parameters": [
+                        "",
+                        "ControllerIdentifier",
+                        "JoystickIdentifier",
+                        "\"Up\"",
+                        ""
+                      ]
+                    }
+                  ],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "PlatformBehavior::SimulateUpKey"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Property"
+                      ]
+                    },
+                    {
+                      "type": {
+                        "value": "PlatformBehavior::SimulateLadderKey"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Property"
+                      ]
+                    }
+                  ]
+                },
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [
+                    {
+                      "type": {
+                        "value": "SpriteMultitouchJoystick::IsDirectionPushed4Way"
+                      },
+                      "parameters": [
+                        "",
+                        "ControllerIdentifier",
+                        "JoystickIdentifier",
+                        "\"Down\"",
+                        ""
+                      ]
+                    }
+                  ],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "PlatformBehavior::SimulateDownKey"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Property"
+                      ]
+                    }
+                  ]
+                },
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [
+                    {
+                      "type": {
+                        "value": "SpriteMultitouchJoystick::IsButtonPressed"
+                      },
+                      "parameters": [
+                        "",
+                        "ControllerIdentifier",
+                        "JumpButton",
+                        "\"Down\""
+                      ]
+                    }
+                  ],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "PlatformBehavior::SimulateJumpKey"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Property"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "SpriteMultitouchJoystick::PlatformerMultitouchMapper",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            }
+          ],
+          "propertyDescriptors": [
+            {
+              "value": "",
+              "type": "Behavior",
+              "label": "Platform character behavior",
+              "description": "",
+              "group": "",
+              "extraInformation": [
+                "PlatformBehavior::PlatformerObjectBehavior"
+              ],
+              "name": "Property"
+            },
+            {
+              "value": "1",
+              "type": "Number",
+              "label": "Controller identifier (1, 2, 3, 4...)",
+              "description": "",
+              "group": "",
+              "extraInformation": [],
+              "name": "ControllerIdentifier"
+            },
+            {
+              "value": "Primary",
+              "type": "Choice",
+              "label": "Joystick name",
+              "description": "",
+              "group": "Controls",
+              "extraInformation": [
+                "Primary",
+                "Secondary"
+              ],
+              "name": "JoystickIdentifier"
+            },
+            {
+              "value": "A",
+              "type": "String",
+              "label": "Jump button name",
+              "description": "",
+              "group": "Controls",
+              "extraInformation": [],
+              "name": "JumpButton"
+            }
+          ],
+          "sharedPropertyDescriptors": []
+        },
+        {
+          "description": "Control a 3D physics character with a multitouch controller.",
+          "fullName": "3D platformer multitouch controller mapper",
+          "name": "Platformer3DMultitouchMapper",
+          "objectType": "",
+          "eventsFunctions": [
+            {
+              "fullName": "",
+              "functionType": "Action",
+              "name": "doStepPreEvents",
+              "sentence": "",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [
+                    {
+                      "type": {
+                        "value": "SpriteMultitouchJoystick::StickForce"
+                      },
+                      "parameters": [
+                        "",
+                        ">",
+                        "0",
+                        "ControllerIdentifier",
+                        "JoystickIdentifier",
+                        ""
+                      ]
+                    }
+                  ],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "Physics3D::PhysicsCharacter3D::SetForwardAngle"
+                      },
+                      "parameters": [
+                        "Object",
+                        "PhysicsCharacter3D",
+                        "=",
+                        "SpriteMultitouchJoystick::StickAngle(ControllerIdentifier, JoystickIdentifier) + CameraAngle(Object.Layer())"
+                      ]
+                    },
+                    {
+                      "type": {
+                        "value": "Physics3D::PhysicsCharacter3D::SimulateStick"
+                      },
+                      "parameters": [
+                        "Object",
+                        "PhysicsCharacter3D",
+                        "-90",
+                        "SpriteMultitouchJoystick::StickForce(ControllerIdentifier, JoystickIdentifier)"
+                      ]
+                    }
+                  ]
+                },
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [
+                    {
+                      "type": {
+                        "value": "SpriteMultitouchJoystick::IsButtonPressed"
+                      },
+                      "parameters": [
+                        "",
+                        "ControllerIdentifier",
+                        "JumpButton",
+                        "\"Down\""
+                      ]
+                    }
+                  ],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "Physics3D::PhysicsCharacter3D::SimulateJumpKey"
+                      },
+                      "parameters": [
+                        "Object",
+                        "PhysicsCharacter3D"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "SpriteMultitouchJoystick::Platformer3DMultitouchMapper",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            }
+          ],
+          "propertyDescriptors": [
+            {
+              "value": "",
+              "type": "Behavior",
+              "label": "3D physics character",
+              "description": "",
+              "group": "",
+              "extraInformation": [
+                "Physics3D::PhysicsCharacter3D"
+              ],
+              "name": "PhysicsCharacter3D"
+            },
+            {
+              "value": "1",
+              "type": "Number",
+              "label": "Controller identifier (1, 2, 3, 4...)",
+              "description": "",
+              "group": "",
+              "extraInformation": [],
+              "name": "ControllerIdentifier"
+            },
+            {
+              "value": "Primary",
+              "type": "Choice",
+              "label": "Walk joystick",
+              "description": "",
+              "group": "Controls",
+              "extraInformation": [
+                "Primary",
+                "Secondary"
+              ],
+              "name": "JoystickIdentifier"
+            },
+            {
+              "value": "A",
+              "type": "String",
+              "label": "Jump button name",
+              "description": "",
+              "group": "Controls",
+              "extraInformation": [],
+              "name": "JumpButton"
+            }
+          ],
+          "sharedPropertyDescriptors": []
+        },
+        {
+          "description": "Control a 3D physics character with a multitouch controller.",
+          "fullName": "3D shooter multitouch controller mapper",
+          "name": "Shooter3DMultitouchMapper",
+          "objectType": "",
+          "eventsFunctions": [
+            {
+              "fullName": "",
+              "functionType": "Action",
+              "name": "doStepPreEvents",
+              "sentence": "",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [
+                    {
+                      "type": {
+                        "value": "SpriteMultitouchJoystick::StickForce"
+                      },
+                      "parameters": [
+                        "",
+                        ">",
+                        "0",
+                        "ControllerIdentifier",
+                        "JoystickIdentifier",
+                        ""
+                      ]
+                    }
+                  ],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "Physics3D::PhysicsCharacter3D::SimulateStick"
+                      },
+                      "parameters": [
+                        "Object",
+                        "PhysicsCharacter3D",
+                        "SpriteMultitouchJoystick::StickAngle(ControllerIdentifier, JoystickIdentifier)",
+                        "SpriteMultitouchJoystick::StickForce(ControllerIdentifier, JoystickIdentifier)"
+                      ]
+                    }
+                  ]
+                },
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [
+                    {
+                      "type": {
+                        "value": "SpriteMultitouchJoystick::IsButtonPressed"
+                      },
+                      "parameters": [
+                        "",
+                        "ControllerIdentifier",
+                        "JumpButton",
+                        "\"Down\""
+                      ]
+                    }
+                  ],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "Physics3D::PhysicsCharacter3D::SimulateJumpKey"
+                      },
+                      "parameters": [
+                        "Object",
+                        "PhysicsCharacter3D"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "SpriteMultitouchJoystick::Shooter3DMultitouchMapper",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            }
+          ],
+          "propertyDescriptors": [
+            {
+              "value": "",
+              "type": "Behavior",
+              "label": "3D physics character",
+              "description": "",
+              "group": "",
+              "extraInformation": [
+                "Physics3D::PhysicsCharacter3D"
+              ],
+              "name": "PhysicsCharacter3D"
+            },
+            {
+              "value": "1",
+              "type": "Number",
+              "label": "Controller identifier (1, 2, 3, 4...)",
+              "description": "",
+              "group": "",
+              "extraInformation": [],
+              "name": "ControllerIdentifier"
+            },
+            {
+              "value": "Primary",
+              "type": "Choice",
+              "label": "Walk joystick",
+              "description": "",
+              "group": "Controls",
+              "extraInformation": [
+                "Primary",
+                "Secondary"
+              ],
+              "name": "JoystickIdentifier"
+            },
+            {
+              "value": "A",
+              "type": "String",
+              "label": "Jump button name",
+              "description": "",
+              "group": "Controls",
+              "extraInformation": [],
+              "name": "JumpButton"
+            }
+          ],
+          "sharedPropertyDescriptors": []
+        },
+        {
+          "description": "Control camera rotations with a multitouch controller.",
+          "fullName": "First person camera multitouch controller mapper",
+          "name": "FirstPersonMultitouchMapper",
+          "objectType": "",
+          "eventsFunctions": [
+            {
+              "fullName": "",
+              "functionType": "Action",
+              "name": "doStepPreEvents",
+              "sentence": "",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Comment",
+                  "color": {
+                    "b": 109,
+                    "g": 230,
+                    "r": 255,
+                    "textB": 0,
+                    "textG": 0,
+                    "textR": 0
+                  },
+                  "comment": "TODO It's probably a bad idea to rotate the object around Y."
+                },
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SpriteMultitouchJoystick::FirstPersonMultitouchMapper::SetPropertyCurrentRotationSpeedZ"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "=",
+                        "SpriteMultitouchJoystick::AcceleratedSpeed(CurrentRotationSpeedZ, SpriteMultitouchJoystick::StickForceX(ControllerIdentifier, CameraStick) * HorizontalRotationSpeedMax, HorizontalRotationSpeedMax, HorizontalRotationAcceleration, HorizontalRotationDeceleration)"
+                      ]
+                    },
+                    {
+                      "type": {
+                        "value": "SetAngle"
+                      },
+                      "parameters": [
+                        "Object",
+                        "+",
+                        "CurrentRotationSpeedZ * TimeDelta()"
+                      ]
+                    },
+                    {
+                      "type": {
+                        "value": "SpriteMultitouchJoystick::FirstPersonMultitouchMapper::SetPropertyCurrentRotationSpeedY"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "=",
+                        "SpriteMultitouchJoystick::AcceleratedSpeed(CurrentRotationSpeedY, SpriteMultitouchJoystick::StickForceY(ControllerIdentifier, CameraStick) * VerticalRotationSpeedMax, VerticalRotationSpeedMax, VerticalRotationAcceleration, VerticalRotationDeceleration)"
+                      ]
+                    },
+                    {
+                      "type": {
+                        "value": "Scene3D::Base3DBehavior::SetRotationY"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Object3D",
+                        "+",
+                        "CurrentRotationSpeedY * TimeDelta()"
+                      ]
+                    },
+                    {
+                      "type": {
+                        "value": "Scene3D::Base3DBehavior::SetRotationY"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Object3D",
+                        "=",
+                        "clamp(Object.Object3D::RotationY(), VerticalAngleMin, VerticalAngleMax)"
+                      ]
+                    },
+                    {
+                      "type": {
+                        "value": "SpriteMultitouchJoystick::FirstPersonMultitouchMapper::LookFromObjectEyes"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        ""
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "SpriteMultitouchJoystick::FirstPersonMultitouchMapper",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "Move the camera to look though _PARAM1_ eyes. The object must look to the right when all its angles are 0 and the top of its head be toward Z+.",
+              "fullName": "Look through object eyes",
+              "functionType": "Action",
+              "group": "Layers and cameras",
+              "name": "LookFromObjectEyes",
+              "private": true,
+              "sentence": "Move the camera to look though _PARAM0_ eyes",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "CentreCamera"
+                      },
+                      "parameters": [
+                        "",
+                        "Object",
+                        "",
+                        "Object.Layer()",
+                        ""
+                      ]
+                    },
+                    {
+                      "type": {
+                        "value": "Scene3D::SetCameraZ"
+                      },
+                      "parameters": [
+                        "",
+                        "=",
+                        "Object.Object3D::Z() + Object.Object3D::Depth() + OffsetZ",
+                        "",
+                        ""
+                      ]
+                    },
+                    {
+                      "type": {
+                        "value": "Scene3D::SetCameraRotationX"
+                      },
+                      "parameters": [
+                        "",
+                        "=",
+                        "- Object.Object3D::RotationY() + 90",
+                        "GetArgumentAsString(\"Layer\")",
+                        ""
+                      ]
+                    },
+                    {
+                      "type": {
+                        "value": "Scene3D::SetCameraRotationY"
+                      },
+                      "parameters": [
+                        "",
+                        "=",
+                        "Object.Object3D::RotationX()",
+                        "GetArgumentAsString(\"Layer\")",
+                        ""
+                      ]
+                    },
+                    {
+                      "type": {
+                        "value": "SetCameraAngle"
+                      },
+                      "parameters": [
+                        "",
+                        "=",
+                        "Object.Angle() + 90",
+                        "Object.Layer()",
+                        ""
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "SpriteMultitouchJoystick::FirstPersonMultitouchMapper",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "the maximum horizontal rotation speed of the object.",
+              "fullName": "Maximum horizontal rotation speed",
+              "functionType": "ExpressionAndCondition",
+              "group": "First person camera multitouch controller mapper horizontal rotation configuration",
+              "name": "HorizontalRotationSpeedMax",
+              "sentence": "the maximum horizontal rotation speed",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SetReturnNumber"
+                      },
+                      "parameters": [
+                        "HorizontalRotationSpeedMax"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "expressionType": {
+                "type": "expression"
+              },
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "SpriteMultitouchJoystick::FirstPersonMultitouchMapper",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "fullName": "",
+              "functionType": "ActionWithOperator",
+              "getterName": "HorizontalRotationSpeedMax",
+              "name": "SetHorizontalRotationSpeedMax",
+              "sentence": "",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SpriteMultitouchJoystick::FirstPersonMultitouchMapper::SetPropertyHorizontalRotationSpeedMax"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "=",
+                        "Value"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "SpriteMultitouchJoystick::FirstPersonMultitouchMapper",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "the horizontal rotation acceleration of the object.",
+              "fullName": "Horizontal rotation acceleration",
+              "functionType": "ExpressionAndCondition",
+              "group": "First person camera multitouch controller mapper horizontal rotation configuration",
+              "name": "HorizontalRotationAcceleration",
+              "sentence": "the horizontal rotation acceleration",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SetReturnNumber"
+                      },
+                      "parameters": [
+                        "HorizontalRotationAcceleration"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "expressionType": {
+                "type": "expression"
+              },
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "SpriteMultitouchJoystick::FirstPersonMultitouchMapper",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "fullName": "",
+              "functionType": "ActionWithOperator",
+              "getterName": "HorizontalRotationAcceleration",
+              "name": "SetHorizontalRotationAcceleration",
+              "sentence": "",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SpriteMultitouchJoystick::FirstPersonMultitouchMapper::SetPropertyHorizontalRotationAcceleration"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "=",
+                        "Value"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "SpriteMultitouchJoystick::FirstPersonMultitouchMapper",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "the horizontal rotation deceleration of the object.",
+              "fullName": "Horizontal rotation deceleration",
+              "functionType": "ExpressionAndCondition",
+              "group": "First person camera multitouch controller mapper horizontal rotation configuration",
+              "name": "HorizontalRotationDeceleration",
+              "sentence": "the horizontal rotation deceleration",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SetReturnNumber"
+                      },
+                      "parameters": [
+                        "HorizontalRotationDeceleration"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "expressionType": {
+                "type": "expression"
+              },
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "SpriteMultitouchJoystick::FirstPersonMultitouchMapper",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "fullName": "",
+              "functionType": "ActionWithOperator",
+              "getterName": "HorizontalRotationDeceleration",
+              "name": "SetHorizontalRotationDeceleration",
+              "sentence": "",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SpriteMultitouchJoystick::FirstPersonMultitouchMapper::SetPropertyHorizontalRotationDeceleration"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "=",
+                        "Value"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "SpriteMultitouchJoystick::FirstPersonMultitouchMapper",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "the maximum vertical rotation speed of the object.",
+              "fullName": "Maximum vertical rotation speed",
+              "functionType": "ExpressionAndCondition",
+              "group": "First person camera multitouch controller mapper vertical rotation configuration",
+              "name": "VerticalRotationSpeedMax",
+              "sentence": "the maximum vertical rotation speed",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SetReturnNumber"
+                      },
+                      "parameters": [
+                        "VerticalRotationSpeedMax"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "expressionType": {
+                "type": "expression"
+              },
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "SpriteMultitouchJoystick::FirstPersonMultitouchMapper",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "fullName": "",
+              "functionType": "ActionWithOperator",
+              "getterName": "VerticalRotationSpeedMax",
+              "name": "SetVerticalRotationSpeedMax",
+              "sentence": "",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SpriteMultitouchJoystick::FirstPersonMultitouchMapper::SetPropertyVerticalRotationSpeedMax"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "=",
+                        "Value"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "SpriteMultitouchJoystick::FirstPersonMultitouchMapper",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "the vertical rotation acceleration of the object.",
+              "fullName": "Vertical rotation acceleration",
+              "functionType": "ExpressionAndCondition",
+              "group": "First person camera multitouch controller mapper vertical rotation configuration",
+              "name": "VerticalRotationAcceleration",
+              "sentence": "the vertical rotation acceleration",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SetReturnNumber"
+                      },
+                      "parameters": [
+                        "VerticalRotationAcceleration"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "expressionType": {
+                "type": "expression"
+              },
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "SpriteMultitouchJoystick::FirstPersonMultitouchMapper",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "fullName": "",
+              "functionType": "ActionWithOperator",
+              "getterName": "VerticalRotationAcceleration",
+              "name": "SetVerticalRotationAcceleration",
+              "sentence": "",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SpriteMultitouchJoystick::FirstPersonMultitouchMapper::SetPropertyVerticalRotationAcceleration"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "=",
+                        "Value"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "SpriteMultitouchJoystick::FirstPersonMultitouchMapper",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "the vertical rotation deceleration of the object.",
+              "fullName": "Vertical rotation deceleration",
+              "functionType": "ExpressionAndCondition",
+              "group": "First person camera multitouch controller mapper vertical rotation configuration",
+              "name": "VerticalRotationDeceleration",
+              "sentence": "the vertical rotation deceleration",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SetReturnNumber"
+                      },
+                      "parameters": [
+                        "VerticalRotationDeceleration"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "expressionType": {
+                "type": "expression"
+              },
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "SpriteMultitouchJoystick::FirstPersonMultitouchMapper",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "fullName": "",
+              "functionType": "ActionWithOperator",
+              "getterName": "VerticalRotationDeceleration",
+              "name": "SetVerticalRotationDeceleration",
+              "sentence": "",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SpriteMultitouchJoystick::FirstPersonMultitouchMapper::SetPropertyVerticalRotationDeceleration"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "=",
+                        "Value"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "SpriteMultitouchJoystick::FirstPersonMultitouchMapper",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "the minimum vertical camera angle of the object.",
+              "fullName": "Minimum vertical camera angle",
+              "functionType": "ExpressionAndCondition",
+              "group": "First person camera multitouch controller mapper vertical rotation configuration",
+              "name": "VerticalAngleMin",
+              "sentence": "the minimum vertical camera angle",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SetReturnNumber"
+                      },
+                      "parameters": [
+                        "VerticalAngleMin"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "expressionType": {
+                "type": "expression"
+              },
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "SpriteMultitouchJoystick::FirstPersonMultitouchMapper",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "fullName": "",
+              "functionType": "ActionWithOperator",
+              "getterName": "VerticalAngleMin",
+              "name": "SetVerticalAngleMin",
+              "sentence": "",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SpriteMultitouchJoystick::FirstPersonMultitouchMapper::SetPropertyVerticalAngleMin"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "=",
+                        "Value"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "SpriteMultitouchJoystick::FirstPersonMultitouchMapper",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "the maximum vertical camera angle of the object.",
+              "fullName": "Maximum vertical camera angle",
+              "functionType": "ExpressionAndCondition",
+              "group": "First person camera multitouch controller mapper vertical rotation configuration",
+              "name": "VerticalAngleMax",
+              "sentence": "the maximum vertical camera angle",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SetReturnNumber"
+                      },
+                      "parameters": [
+                        "VerticalAngleMax"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "expressionType": {
+                "type": "expression"
+              },
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "SpriteMultitouchJoystick::FirstPersonMultitouchMapper",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "fullName": "",
+              "functionType": "ActionWithOperator",
+              "getterName": "VerticalAngleMax",
+              "name": "SetVerticalAngleMax",
+              "sentence": "",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SpriteMultitouchJoystick::FirstPersonMultitouchMapper::SetPropertyVerticalAngleMax"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "=",
+                        "Value"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "SpriteMultitouchJoystick::FirstPersonMultitouchMapper",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "the z position offset of the object.",
+              "fullName": "Z position offset",
+              "functionType": "ExpressionAndCondition",
+              "group": "First person camera multitouch controller mapper position configuration",
+              "name": "OffsetZ",
+              "sentence": "the z position offset",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SetReturnNumber"
+                      },
+                      "parameters": [
+                        "OffsetZ"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "expressionType": {
+                "type": "expression"
+              },
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "SpriteMultitouchJoystick::FirstPersonMultitouchMapper",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "fullName": "",
+              "functionType": "ActionWithOperator",
+              "getterName": "OffsetZ",
+              "name": "SetOffsetZ",
+              "sentence": "",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SpriteMultitouchJoystick::FirstPersonMultitouchMapper::SetPropertyOffsetZ"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "=",
+                        "Value"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "SpriteMultitouchJoystick::FirstPersonMultitouchMapper",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            }
+          ],
+          "propertyDescriptors": [
+            {
+              "value": "",
+              "type": "Behavior",
+              "label": "3D capability",
+              "description": "",
+              "group": "",
+              "extraInformation": [
+                "Scene3D::Base3DBehavior"
+              ],
+              "name": "Object3D"
+            },
+            {
+              "value": "1",
+              "type": "Number",
+              "label": "Controller identifier (1, 2, 3, 4...)",
+              "description": "",
+              "group": "",
+              "extraInformation": [],
+              "name": "ControllerIdentifier"
+            },
+            {
+              "value": "Secondary",
+              "type": "Choice",
+              "label": "Camera joystick",
+              "description": "",
+              "group": "Controls",
+              "extraInformation": [
+                "Primary",
+                "Secondary"
+              ],
+              "name": "CameraStick"
+            },
+            {
+              "value": "180",
+              "type": "Number",
+              "unit": "AngularSpeed",
+              "label": "Maximum rotation speed",
+              "description": "",
+              "group": "Horizontal rotation",
+              "extraInformation": [],
+              "name": "HorizontalRotationSpeedMax"
+            },
+            {
+              "value": "360",
+              "type": "Number",
+              "label": "Rotation acceleration",
+              "description": "",
+              "group": "Horizontal rotation",
+              "extraInformation": [],
+              "name": "HorizontalRotationAcceleration"
+            },
+            {
+              "value": "720",
+              "type": "Number",
+              "label": "Rotation deceleration",
+              "description": "",
+              "group": "Horizontal rotation",
+              "extraInformation": [],
+              "name": "HorizontalRotationDeceleration"
+            },
+            {
+              "value": "120",
+              "type": "Number",
+              "unit": "AngularSpeed",
+              "label": "Maximum rotation speed",
+              "description": "",
+              "group": "Vertical rotation",
+              "extraInformation": [],
+              "name": "VerticalRotationSpeedMax"
+            },
+            {
+              "value": "240",
+              "type": "Number",
+              "label": "Rotation acceleration",
+              "description": "",
+              "group": "Vertical rotation",
+              "extraInformation": [],
+              "name": "VerticalRotationAcceleration"
+            },
+            {
+              "value": "480",
+              "type": "Number",
+              "label": "Rotation deceleration",
+              "description": "",
+              "group": "Vertical rotation",
+              "extraInformation": [],
+              "name": "VerticalRotationDeceleration"
+            },
+            {
+              "value": "-90",
+              "type": "Number",
+              "unit": "DegreeAngle",
+              "label": "Minimum angle",
+              "description": "",
+              "group": "Vertical rotation",
+              "extraInformation": [],
+              "name": "VerticalAngleMin"
+            },
+            {
+              "value": "90",
+              "type": "Number",
+              "unit": "DegreeAngle",
+              "label": "Maximum angle",
+              "description": "",
+              "group": "Vertical rotation",
+              "extraInformation": [],
+              "name": "VerticalAngleMax"
+            },
+            {
+              "value": "0",
+              "type": "Number",
+              "unit": "Pixel",
+              "label": "Z position offset",
+              "description": "",
+              "group": "Position",
+              "extraInformation": [],
+              "name": "OffsetZ"
+            },
+            {
+              "value": "0",
+              "type": "Number",
+              "unit": "AngularSpeed",
+              "label": "Current rotation speed Z",
+              "description": "",
+              "group": "",
+              "extraInformation": [],
+              "hidden": true,
+              "name": "CurrentRotationSpeedZ"
+            },
+            {
+              "value": "0",
+              "type": "Number",
+              "unit": "AngularSpeed",
+              "label": "Current rotation speed Y",
+              "description": "",
+              "group": "",
+              "extraInformation": [],
+              "hidden": true,
+              "name": "CurrentRotationSpeedY"
+            }
+          ],
+          "sharedPropertyDescriptors": []
+        },
+        {
+          "description": "Control a top-down character with a multitouch controller.",
+          "fullName": "Top-down multitouch controller mapper",
+          "name": "TopDownMultitouchMapper",
+          "objectType": "",
+          "eventsFunctions": [
+            {
+              "fullName": "",
+              "functionType": "Action",
+              "name": "doStepPreEvents",
+              "sentence": "",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [
+                    {
+                      "type": {
+                        "value": "SpriteMultitouchJoystick::TopDownMultitouchMapper::PropertyStickMode"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "=",
+                        "\"Analog\""
+                      ]
+                    }
+                  ],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "TopDownMovementBehavior::SimulateStick"
+                      },
+                      "parameters": [
+                        "Object",
+                        "TopDownMovement",
+                        "SpriteMultitouchJoystick::StickAngle(ControllerIdentifier, JoystickIdentifier)",
+                        "SpriteMultitouchJoystick::StickForce(ControllerIdentifier, JoystickIdentifier)"
+                      ]
+                    }
+                  ]
+                },
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [
+                    {
+                      "type": {
+                        "value": "SpriteMultitouchJoystick::TopDownMultitouchMapper::PropertyStickMode"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "=",
+                        "\"360°\""
+                      ]
+                    }
+                  ],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "TopDownMovementBehavior::SimulateStick"
+                      },
+                      "parameters": [
+                        "Object",
+                        "TopDownMovement",
+                        "SpriteMultitouchJoystick::StickAngle(ControllerIdentifier, JoystickIdentifier)",
+                        "sign(SpriteMultitouchJoystick::StickForce(ControllerIdentifier, JoystickIdentifier))"
+                      ]
+                    }
+                  ]
+                },
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [
+                    {
+                      "type": {
+                        "value": "SpriteMultitouchJoystick::TopDownMultitouchMapper::PropertyStickMode"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "=",
+                        "\"8 Directions\""
+                      ]
+                    }
+                  ],
+                  "actions": [],
+                  "events": [
+                    {
+                      "type": "BuiltinCommonInstructions::Standard",
+                      "conditions": [
+                        {
+                          "type": {
+                            "inverted": true,
+                            "value": "TopDownMovementBehavior::DiagonalsAllowed"
+                          },
+                          "parameters": [
+                            "Object",
+                            "TopDownMovement"
+                          ]
+                        }
+                      ],
+                      "actions": [],
+                      "events": [
+                        {
+                          "type": "BuiltinCommonInstructions::Standard",
+                          "conditions": [
+                            {
+                              "type": {
+                                "value": "SpriteMultitouchJoystick::IsDirectionPushed4Way"
+                              },
+                              "parameters": [
+                                "",
+                                "ControllerIdentifier",
+                                "JoystickIdentifier",
+                                "\"Left\"",
+                                ""
+                              ]
+                            }
+                          ],
+                          "actions": [
+                            {
+                              "type": {
+                                "value": "TopDownMovementBehavior::SimulateLeftKey"
+                              },
+                              "parameters": [
+                                "Object",
+                                "TopDownMovement"
+                              ]
+                            }
+                          ]
+                        },
+                        {
+                          "type": "BuiltinCommonInstructions::Standard",
+                          "conditions": [
+                            {
+                              "type": {
+                                "value": "SpriteMultitouchJoystick::IsDirectionPushed4Way"
+                              },
+                              "parameters": [
+                                "",
+                                "ControllerIdentifier",
+                                "JoystickIdentifier",
+                                "\"Right\"",
+                                ""
+                              ]
+                            }
+                          ],
+                          "actions": [
+                            {
+                              "type": {
+                                "value": "TopDownMovementBehavior::SimulateRightKey"
+                              },
+                              "parameters": [
+                                "Object",
+                                "TopDownMovement"
+                              ]
+                            }
+                          ]
+                        },
+                        {
+                          "type": "BuiltinCommonInstructions::Standard",
+                          "conditions": [
+                            {
+                              "type": {
+                                "value": "SpriteMultitouchJoystick::IsDirectionPushed4Way"
+                              },
+                              "parameters": [
+                                "",
+                                "ControllerIdentifier",
+                                "JoystickIdentifier",
+                                "\"Up\"",
+                                ""
+                              ]
+                            }
+                          ],
+                          "actions": [
+                            {
+                              "type": {
+                                "value": "TopDownMovementBehavior::SimulateUpKey"
+                              },
+                              "parameters": [
+                                "Object",
+                                "TopDownMovement"
+                              ]
+                            }
+                          ]
+                        },
+                        {
+                          "type": "BuiltinCommonInstructions::Standard",
+                          "conditions": [
+                            {
+                              "type": {
+                                "value": "SpriteMultitouchJoystick::IsDirectionPushed4Way"
+                              },
+                              "parameters": [
+                                "",
+                                "ControllerIdentifier",
+                                "JoystickIdentifier",
+                                "\"Down\"",
+                                ""
+                              ]
+                            }
+                          ],
+                          "actions": [
+                            {
+                              "type": {
+                                "value": "TopDownMovementBehavior::SimulateDownKey"
+                              },
+                              "parameters": [
+                                "Object",
+                                "TopDownMovement"
+                              ]
+                            }
+                          ]
+                        }
+                      ]
+                    },
+                    {
+                      "type": "BuiltinCommonInstructions::Standard",
+                      "conditions": [
+                        {
+                          "type": {
+                            "value": "TopDownMovementBehavior::DiagonalsAllowed"
+                          },
+                          "parameters": [
+                            "Object",
+                            "TopDownMovement"
+                          ]
+                        }
+                      ],
+                      "actions": [],
+                      "events": [
+                        {
+                          "type": "BuiltinCommonInstructions::Standard",
+                          "conditions": [
+                            {
+                              "type": {
+                                "value": "SpriteMultitouchJoystick::IsDirectionPushed8Way"
+                              },
+                              "parameters": [
+                                "",
+                                "ControllerIdentifier",
+                                "JoystickIdentifier",
+                                "\"Left\"",
+                                ""
+                              ]
+                            }
+                          ],
+                          "actions": [
+                            {
+                              "type": {
+                                "value": "TopDownMovementBehavior::SimulateLeftKey"
+                              },
+                              "parameters": [
+                                "Object",
+                                "TopDownMovement"
+                              ]
+                            }
+                          ]
+                        },
+                        {
+                          "type": "BuiltinCommonInstructions::Standard",
+                          "conditions": [
+                            {
+                              "type": {
+                                "value": "SpriteMultitouchJoystick::IsDirectionPushed8Way"
+                              },
+                              "parameters": [
+                                "",
+                                "ControllerIdentifier",
+                                "JoystickIdentifier",
+                                "\"Right\"",
+                                ""
+                              ]
+                            }
+                          ],
+                          "actions": [
+                            {
+                              "type": {
+                                "value": "TopDownMovementBehavior::SimulateRightKey"
+                              },
+                              "parameters": [
+                                "Object",
+                                "TopDownMovement"
+                              ]
+                            }
+                          ]
+                        },
+                        {
+                          "type": "BuiltinCommonInstructions::Standard",
+                          "conditions": [
+                            {
+                              "type": {
+                                "value": "SpriteMultitouchJoystick::IsDirectionPushed8Way"
+                              },
+                              "parameters": [
+                                "",
+                                "ControllerIdentifier",
+                                "JoystickIdentifier",
+                                "\"Up\"",
+                                ""
+                              ]
+                            }
+                          ],
+                          "actions": [
+                            {
+                              "type": {
+                                "value": "TopDownMovementBehavior::SimulateUpKey"
+                              },
+                              "parameters": [
+                                "Object",
+                                "TopDownMovement"
+                              ]
+                            }
+                          ]
+                        },
+                        {
+                          "type": "BuiltinCommonInstructions::Standard",
+                          "conditions": [
+                            {
+                              "type": {
+                                "value": "SpriteMultitouchJoystick::IsDirectionPushed8Way"
+                              },
+                              "parameters": [
+                                "",
+                                "ControllerIdentifier",
+                                "JoystickIdentifier",
+                                "\"Down\"",
+                                ""
+                              ]
+                            }
+                          ],
+                          "actions": [
+                            {
+                              "type": {
+                                "value": "TopDownMovementBehavior::SimulateDownKey"
+                              },
+                              "parameters": [
+                                "Object",
+                                "TopDownMovement"
+                              ]
+                            }
+                          ]
+                        },
+                        {
+                          "type": "BuiltinCommonInstructions::Standard",
+                          "conditions": [
+                            {
+                              "type": {
+                                "value": "SpriteMultitouchJoystick::IsDirectionPushed8Way"
+                              },
+                              "parameters": [
+                                "",
+                                "ControllerIdentifier",
+                                "JoystickIdentifier",
+                                "\"UpLeft\"",
+                                ""
+                              ]
+                            }
+                          ],
+                          "actions": [
+                            {
+                              "type": {
+                                "value": "TopDownMovementBehavior::SimulateUpKey"
+                              },
+                              "parameters": [
+                                "Object",
+                                "TopDownMovement"
+                              ]
+                            },
+                            {
+                              "type": {
+                                "value": "TopDownMovementBehavior::SimulateLeftKey"
+                              },
+                              "parameters": [
+                                "Object",
+                                "TopDownMovement"
+                              ]
+                            }
+                          ]
+                        },
+                        {
+                          "type": "BuiltinCommonInstructions::Standard",
+                          "conditions": [
+                            {
+                              "type": {
+                                "value": "SpriteMultitouchJoystick::IsDirectionPushed8Way"
+                              },
+                              "parameters": [
+                                "",
+                                "ControllerIdentifier",
+                                "JoystickIdentifier",
+                                "\"UpRight\"",
+                                ""
+                              ]
+                            }
+                          ],
+                          "actions": [
+                            {
+                              "type": {
+                                "value": "TopDownMovementBehavior::SimulateUpKey"
+                              },
+                              "parameters": [
+                                "Object",
+                                "TopDownMovement"
+                              ]
+                            },
+                            {
+                              "type": {
+                                "value": "TopDownMovementBehavior::SimulateRightKey"
+                              },
+                              "parameters": [
+                                "Object",
+                                "TopDownMovement"
+                              ]
+                            }
+                          ]
+                        },
+                        {
+                          "type": "BuiltinCommonInstructions::Standard",
+                          "conditions": [
+                            {
+                              "type": {
+                                "value": "SpriteMultitouchJoystick::IsDirectionPushed8Way"
+                              },
+                              "parameters": [
+                                "",
+                                "ControllerIdentifier",
+                                "JoystickIdentifier",
+                                "\"DownLeft\"",
+                                ""
+                              ]
+                            }
+                          ],
+                          "actions": [
+                            {
+                              "type": {
+                                "value": "TopDownMovementBehavior::SimulateDownKey"
+                              },
+                              "parameters": [
+                                "Object",
+                                "TopDownMovement"
+                              ]
+                            },
+                            {
+                              "type": {
+                                "value": "TopDownMovementBehavior::SimulateLeftKey"
+                              },
+                              "parameters": [
+                                "Object",
+                                "TopDownMovement"
+                              ]
+                            }
+                          ]
+                        },
+                        {
+                          "type": "BuiltinCommonInstructions::Standard",
+                          "conditions": [
+                            {
+                              "type": {
+                                "value": "SpriteMultitouchJoystick::IsDirectionPushed8Way"
+                              },
+                              "parameters": [
+                                "",
+                                "ControllerIdentifier",
+                                "JoystickIdentifier",
+                                "\"DownRight\"",
+                                ""
+                              ]
+                            }
+                          ],
+                          "actions": [
+                            {
+                              "type": {
+                                "value": "TopDownMovementBehavior::SimulateDownKey"
+                              },
+                              "parameters": [
+                                "Object",
+                                "TopDownMovement"
+                              ]
+                            },
+                            {
+                              "type": {
+                                "value": "TopDownMovementBehavior::SimulateRightKey"
+                              },
+                              "parameters": [
+                                "Object",
+                                "TopDownMovement"
+                              ]
+                            }
+                          ]
+                        }
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "SpriteMultitouchJoystick::TopDownMultitouchMapper",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            }
+          ],
+          "propertyDescriptors": [
+            {
+              "value": "",
+              "type": "Behavior",
+              "label": "Top-down movement behavior",
+              "description": "",
+              "group": "",
+              "extraInformation": [
+                "TopDownMovementBehavior::TopDownMovementBehavior"
+              ],
+              "name": "TopDownMovement"
+            },
+            {
+              "value": "1",
+              "type": "Number",
+              "label": "Controller identifier (1, 2, 3, 4...)",
+              "description": "",
+              "group": "",
+              "extraInformation": [],
+              "name": "ControllerIdentifier"
+            },
+            {
+              "value": "Primary",
+              "type": "Choice",
+              "label": "Joystick name",
+              "description": "",
+              "group": "",
+              "extraInformation": [
+                "Primary",
+                "Secondary"
+              ],
+              "name": "JoystickIdentifier"
+            },
+            {
+              "value": "Analog",
+              "type": "Choice",
+              "label": "Stick mode",
+              "description": "",
+              "group": "Controls",
+              "extraInformation": [
+                "Analog",
+                "360°",
+                "8 Directions"
+              ],
+              "name": "StickMode"
+            }
+          ],
+          "sharedPropertyDescriptors": []
+        }
+      ],
+      "eventsBasedObjects": [
+        {
+          "areaMaxX": 64,
+          "areaMaxY": 64,
+          "areaMaxZ": 64,
+          "areaMinX": 0,
+          "areaMinY": 0,
+          "areaMinZ": 0,
+          "defaultName": "Joystick",
+          "description": "Joystick for touchscreens.",
+          "fullName": "Multitouch Joystick",
+          "isUsingLegacyInstancesRenderer": true,
+          "name": "SpriteMultitouchJoystick",
+          "eventsFunctions": [
+            {
+              "fullName": "",
+              "functionType": "Action",
+              "name": "onCreated",
+              "sentence": "",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "Create"
+                      },
+                      "parameters": [
+                        "",
+                        "Border",
+                        "0",
+                        "0",
+                        ""
+                      ]
+                    },
+                    {
+                      "type": {
+                        "value": "Create"
+                      },
+                      "parameters": [
+                        "",
+                        "Thumb",
+                        "0",
+                        "0",
+                        ""
+                      ]
+                    },
+                    {
+                      "type": {
+                        "value": "ChangePlan"
+                      },
+                      "parameters": [
+                        "Border",
+                        "=",
+                        "1"
+                      ]
+                    },
+                    {
+                      "type": {
+                        "value": "ChangePlan"
+                      },
+                      "parameters": [
+                        "Thumb",
+                        "=",
+                        "2"
+                      ]
+                    },
+                    {
+                      "type": {
+                        "value": "SetCenter"
+                      },
+                      "parameters": [
+                        "Border",
+                        "=",
+                        "0",
+                        "=",
+                        "0"
+                      ]
+                    },
+                    {
+                      "type": {
+                        "value": "SetCenter"
+                      },
+                      "parameters": [
+                        "Thumb",
+                        "=",
+                        "0",
+                        "=",
+                        "0"
+                      ]
+                    },
+                    {
+                      "type": {
+                        "value": "SpriteMultitouchJoystick::SpriteMultitouchJoystick::UpdateConfiguration"
+                      },
+                      "parameters": [
+                        "Object",
+                        ""
+                      ]
+                    },
+                    {
+                      "type": {
+                        "value": "SetIncludedInParentCollisionMask"
+                      },
+                      "parameters": [
+                        "Thumb",
+                        ""
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "supplementaryInformation": "SpriteMultitouchJoystick::SpriteMultitouchJoystick",
+                  "type": "object"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "fullName": "",
+              "functionType": "Action",
+              "name": "doStepPostEvents",
+              "sentence": "",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "MettreAutour"
+                      },
+                      "parameters": [
+                        "Thumb",
+                        "Border",
+                        "Border.MultitouchJoystick::JoystickForce() * Border.Width() / 2",
+                        "Border.MultitouchJoystick::JoystickAngle()"
+                      ]
+                    }
+                  ]
+                },
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [
+                    {
+                      "type": {
+                        "value": "SpriteMultitouchJoystick::SpriteMultitouchJoystick::PropertyShouldBeHiddenWhenReleased"
+                      },
+                      "parameters": [
+                        "Object"
+                      ]
+                    },
+                    {
+                      "type": {
+                        "inverted": true,
+                        "value": "SpriteMultitouchJoystick::SpriteMultitouchJoystick::IsPressed"
+                      },
+                      "parameters": [
+                        "Object",
+                        ""
+                      ]
+                    }
+                  ],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "Cache"
+                      },
+                      "parameters": [
+                        "Object"
+                      ]
+                    },
+                    {
+                      "type": {
+                        "value": "SpriteMultitouchJoystick::SpriteMultitouchJoystick::ActivateControl"
+                      },
+                      "parameters": [
+                        "Object",
+                        "no",
+                        ""
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "supplementaryInformation": "SpriteMultitouchJoystick::SpriteMultitouchJoystick",
+                  "type": "object"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "fullName": "",
+              "functionType": "Action",
+              "name": "onHotReloading",
+              "sentence": "",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SpriteMultitouchJoystick::SpriteMultitouchJoystick::UpdateConfiguration"
+                      },
+                      "parameters": [
+                        "Object",
+                        ""
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "supplementaryInformation": "SpriteMultitouchJoystick::SpriteMultitouchJoystick",
+                  "type": "object"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "Pass the object property values to the behavior.",
+              "fullName": "Update configuration",
+              "functionType": "Action",
+              "name": "UpdateConfiguration",
+              "private": true,
+              "sentence": "Update the configuration of _PARAM0_",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SpriteMultitouchJoystick::MultitouchJoystick::SetControllerIdentifier"
+                      },
+                      "parameters": [
+                        "Border",
+                        "MultitouchJoystick",
+                        "=",
+                        "ControllerIdentifier",
+                        ""
+                      ]
+                    },
+                    {
+                      "type": {
+                        "value": "SpriteMultitouchJoystick::MultitouchJoystick::SetJoystickIdentifier"
+                      },
+                      "parameters": [
+                        "Border",
+                        "MultitouchJoystick",
+                        "=",
+                        "JoystickIdentifier",
+                        ""
+                      ]
+                    },
+                    {
+                      "type": {
+                        "value": "SpriteMultitouchJoystick::MultitouchJoystick::SetDeadZoneRadius"
+                      },
+                      "parameters": [
+                        "Border",
+                        "MultitouchJoystick",
+                        "=",
+                        "DeadZoneRadius",
+                        ""
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "supplementaryInformation": "SpriteMultitouchJoystick::SpriteMultitouchJoystick",
+                  "type": "object"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "Show the joystick until it is released.",
+              "fullName": "Show and start pressing",
+              "functionType": "Action",
+              "name": "TeleportAndPress",
+              "sentence": "Show _PARAM0_ at the cursor position and start pressing",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [
+                    {
+                      "type": {
+                        "value": "HasAnyTouchOrMouseStarted"
+                      },
+                      "parameters": [
+                        ""
+                      ]
+                    }
+                  ],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "MettreXY"
+                      },
+                      "parameters": [
+                        "Object",
+                        "=",
+                        "Object.ParentTouchX(StartedTouchOrMouseId(0))",
+                        "=",
+                        "Object.ParentTouchY(StartedTouchOrMouseId(0))"
+                      ]
+                    },
+                    {
+                      "type": {
+                        "value": "SpriteMultitouchJoystick::SpriteMultitouchJoystick::ActivateControl"
+                      },
+                      "parameters": [
+                        "Object",
+                        "yes",
+                        ""
+                      ]
+                    },
+                    {
+                      "type": {
+                        "value": "Montre"
+                      },
+                      "parameters": [
+                        "Object",
+                        ""
+                      ]
+                    },
+                    {
+                      "type": {
+                        "value": "SpriteMultitouchJoystick::SpriteMultitouchJoystick::SetPropertyShouldBeHiddenWhenReleased"
+                      },
+                      "parameters": [
+                        "Object",
+                        "yes"
+                      ]
+                    },
+                    {
+                      "type": {
+                        "value": "SpriteMultitouchJoystick::MultitouchJoystick::ForceStartPressing"
+                      },
+                      "parameters": [
+                        "Border",
+                        "MultitouchJoystick",
+                        "StartedTouchOrMouseId(0)",
+                        ""
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "supplementaryInformation": "SpriteMultitouchJoystick::SpriteMultitouchJoystick",
+                  "type": "object"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "Return the X position of a specified touch",
+              "fullName": "Touch X position (on parent)",
+              "functionType": "Expression",
+              "name": "ParentTouchX",
+              "private": true,
+              "sentence": "",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": []
+                },
+                {
+                  "type": "BuiltinCommonInstructions::JsCode",
+                  "inlineCode": [
+                    "const object = objects[0];",
+                    "const touchId = eventsFunctionContext.getArgument(\"TouchId\");",
+                    "eventsFunctionContext.returnValue = gdjs.evtTools.input.getTouchX(object.getInstanceContainer(), touchId, object.getLayer());"
+                  ],
+                  "parameterObjects": "Object",
+                  "useStrict": true,
+                  "eventsSheetExpanded": false
+                }
+              ],
+              "expressionType": {
+                "type": "expression"
+              },
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "supplementaryInformation": "SpriteMultitouchJoystick::SpriteMultitouchJoystick",
+                  "type": "object"
+                },
+                {
+                  "description": "Touch identifier",
+                  "name": "TouchId",
+                  "type": "expression"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "Return the X position of a specified touch",
+              "fullName": "Touch X position (on parent)",
+              "functionType": "Expression",
+              "name": "ParentTouchY",
+              "private": true,
+              "sentence": "",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": []
+                },
+                {
+                  "type": "BuiltinCommonInstructions::JsCode",
+                  "inlineCode": [
+                    "const object = objects[0];",
+                    "const touchId = eventsFunctionContext.getArgument(\"TouchId\");",
+                    "eventsFunctionContext.returnValue = gdjs.evtTools.input.getTouchY(object.getInstanceContainer(), touchId, object.getLayer());"
+                  ],
+                  "parameterObjects": "Object",
+                  "useStrict": true,
+                  "eventsSheetExpanded": false
+                }
+              ],
+              "expressionType": {
+                "type": "expression"
+              },
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "supplementaryInformation": "SpriteMultitouchJoystick::SpriteMultitouchJoystick",
+                  "type": "object"
+                },
+                {
+                  "description": "Touch identifier",
+                  "name": "TouchId",
+                  "type": "expression"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "De/activate control of the joystick.",
+              "fullName": "De/activate control",
+              "functionType": "Action",
+              "name": "ActivateControl",
+              "sentence": "Activate control of _PARAM0_: _PARAM1_",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [
+                    {
+                      "type": {
+                        "inverted": true,
+                        "value": "GetArgumentAsBoolean"
+                      },
+                      "parameters": [
+                        "\"ShouldActivate\""
+                      ]
+                    }
+                  ],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "ActivateBehavior"
+                      },
+                      "parameters": [
+                        "Border",
+                        "MultitouchJoystick",
+                        "no"
+                      ]
+                    }
+                  ]
+                },
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [
+                    {
+                      "type": {
+                        "value": "GetArgumentAsBoolean"
+                      },
+                      "parameters": [
+                        "\"ShouldActivate\""
+                      ]
+                    }
+                  ],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "ActivateBehavior"
+                      },
+                      "parameters": [
+                        "Border",
+                        "MultitouchJoystick",
+                        "yes"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "supplementaryInformation": "SpriteMultitouchJoystick::SpriteMultitouchJoystick",
+                  "type": "object"
+                },
+                {
+                  "description": "Activate",
+                  "name": "ShouldActivate",
+                  "type": "yesorno"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "Check if a stick is pressed.",
+              "fullName": "Stick pressed",
+              "functionType": "Condition",
+              "name": "IsPressed",
+              "sentence": "Stick _PARAM0_ is pressed",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [
+                    {
+                      "type": {
+                        "value": "SpriteMultitouchJoystick::MultitouchJoystick::IsPressed"
+                      },
+                      "parameters": [
+                        "Border",
+                        "MultitouchJoystick",
+                        "!="
+                      ]
+                    }
+                  ],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SetReturnBoolean"
+                      },
+                      "parameters": [
+                        "True"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "supplementaryInformation": "SpriteMultitouchJoystick::SpriteMultitouchJoystick",
+                  "type": "object"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "the joystick force (from 0 to 1).",
+              "fullName": "Joystick force (deprecated)",
+              "functionType": "ExpressionAndCondition",
+              "name": "JoystickForce",
+              "private": true,
+              "sentence": "the joystick force",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SetReturnNumber"
+                      },
+                      "parameters": [
+                        "Border.MultitouchJoystick::JoystickForce()"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "expressionType": {
+                "type": "expression"
+              },
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "supplementaryInformation": "SpriteMultitouchJoystick::SpriteMultitouchJoystick",
+                  "type": "object"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "the strick force (from 0 to 1).",
+              "fullName": "Stick force",
+              "functionType": "ExpressionAndCondition",
+              "name": "StickForce",
+              "sentence": "the stick force",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SetReturnNumber"
+                      },
+                      "parameters": [
+                        "Border.MultitouchJoystick::JoystickForce()"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "expressionType": {
+                "type": "expression"
+              },
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "supplementaryInformation": "SpriteMultitouchJoystick::SpriteMultitouchJoystick",
+                  "type": "object"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "the stick force on X axis (from -1 at the left to 1 at the right).",
+              "fullName": "Stick X force",
+              "functionType": "ExpressionAndCondition",
+              "name": "StickForceX",
+              "sentence": "the stick X force",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SetReturnNumber"
+                      },
+                      "parameters": [
+                        "Border.MultitouchJoystick::StickForceX()"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "expressionType": {
+                "type": "expression"
+              },
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "supplementaryInformation": "SpriteMultitouchJoystick::SpriteMultitouchJoystick",
+                  "type": "object"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "the stick force on Y axis (from -1 at the top to 1 at the bottom).",
+              "fullName": "Stick Y force",
+              "functionType": "ExpressionAndCondition",
+              "name": "StickForceY",
+              "sentence": "the stick Y force",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SetReturnNumber"
+                      },
+                      "parameters": [
+                        "Border.MultitouchJoystick::StickForceY()"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "expressionType": {
+                "type": "expression"
+              },
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "supplementaryInformation": "SpriteMultitouchJoystick::SpriteMultitouchJoystick",
+                  "type": "object"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "Return the angle the joystick is pointing towards (from -180 to 180).",
+              "fullName": "Joystick angle (deprecated)",
+              "functionType": "Expression",
+              "name": "JoystickAngle",
+              "private": true,
+              "sentence": "",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SetReturnNumber"
+                      },
+                      "parameters": [
+                        "Border.MultitouchJoystick::JoystickAngle()"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "expressionType": {
+                "type": "expression"
+              },
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "supplementaryInformation": "SpriteMultitouchJoystick::SpriteMultitouchJoystick",
+                  "type": "object"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "Return the angle the stick is pointing towards (from -180 to 180).",
+              "fullName": "Stick angle",
+              "functionType": "Expression",
+              "name": "StickAngle",
+              "sentence": "",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SetReturnNumber"
+                      },
+                      "parameters": [
+                        "Border.MultitouchJoystick::JoystickAngle()"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "expressionType": {
+                "type": "expression"
+              },
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "supplementaryInformation": "SpriteMultitouchJoystick::SpriteMultitouchJoystick",
+                  "type": "object"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "Check if joystick is pushed in a given direction.",
+              "fullName": "Joystick pushed in a direction (4-way movement)",
+              "functionType": "Condition",
+              "name": "IsDirectionPushed4Way",
+              "sentence": "_PARAM0_ is pushed in direction _PARAM1_",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [
+                    {
+                      "type": {
+                        "value": "SpriteMultitouchJoystick::MultitouchJoystick::IsDirectionPushed4Way"
+                      },
+                      "parameters": [
+                        "Border",
+                        "MultitouchJoystick",
+                        "Direction",
+                        ""
+                      ]
+                    }
+                  ],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SetReturnBoolean"
+                      },
+                      "parameters": [
+                        "True"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "supplementaryInformation": "SpriteMultitouchJoystick::SpriteMultitouchJoystick",
+                  "type": "object"
+                },
+                {
+                  "description": "Direction",
+                  "name": "Direction",
+                  "supplementaryInformation": "[\"Up\",\"Down\",\"Left\",\"Right\"]",
+                  "type": "stringWithSelector"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "Check if joystick is pushed in a given direction.",
+              "fullName": "Joystick pushed in a direction (8-way movement)",
+              "functionType": "Condition",
+              "name": "IsDirectionPushed8Way",
+              "sentence": "_PARAM0_ is pushed in direction _PARAM1_",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [
+                    {
+                      "type": {
+                        "value": "SpriteMultitouchJoystick::MultitouchJoystick::IsDirectionPushed8Way"
+                      },
+                      "parameters": [
+                        "Border",
+                        "MultitouchJoystick",
+                        "Direction",
+                        ""
+                      ]
+                    }
+                  ],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SetReturnBoolean"
+                      },
+                      "parameters": [
+                        "True"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "supplementaryInformation": "SpriteMultitouchJoystick::SpriteMultitouchJoystick",
+                  "type": "object"
+                },
+                {
+                  "description": "Direction",
+                  "name": "Direction",
+                  "supplementaryInformation": "[\"Up\",\"Down\",\"Left\",\"Right\",\"UpLeft\",\"UpRight\",\"DownLeft\",\"DownRight\"]",
+                  "type": "stringWithSelector"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "the multitouch controller identifier (1, 2, 3, 4...).",
+              "fullName": "Multitouch controller identifier",
+              "functionType": "ExpressionAndCondition",
+              "group": "Multitouch Joystick configuration",
+              "name": "ControllerIdentifier",
+              "sentence": "the multitouch controller identifier",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SetReturnNumber"
+                      },
+                      "parameters": [
+                        "Border.MultitouchJoystick::PropertyControllerIdentifier()"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "expressionType": {
+                "type": "expression"
+              },
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "supplementaryInformation": "SpriteMultitouchJoystick::SpriteMultitouchJoystick",
+                  "type": "object"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "fullName": "",
+              "functionType": "ActionWithOperator",
+              "getterName": "ControllerIdentifier",
+              "name": "SetControllerIdentifier",
+              "sentence": "",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SpriteMultitouchJoystick::MultitouchJoystick::SetControllerIdentifier"
+                      },
+                      "parameters": [
+                        "Border",
+                        "MultitouchJoystick",
+                        "=",
+                        "Value",
+                        ""
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "supplementaryInformation": "SpriteMultitouchJoystick::SpriteMultitouchJoystick",
+                  "type": "object"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "the joystick name of the object.",
+              "fullName": "Joystick name",
+              "functionType": "ExpressionAndCondition",
+              "group": "Multitouch Joystick configuration",
+              "name": "JoystickIdentifier",
+              "sentence": "the joystick name",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SetReturnString"
+                      },
+                      "parameters": [
+                        "Border.MultitouchJoystick::PropertyJoystickIdentifier()"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "expressionType": {
+                "type": "string"
+              },
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "supplementaryInformation": "SpriteMultitouchJoystick::SpriteMultitouchJoystick",
+                  "type": "object"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "fullName": "",
+              "functionType": "ActionWithOperator",
+              "getterName": "JoystickIdentifier",
+              "name": "SetJoystickIdentifier",
+              "sentence": "",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SpriteMultitouchJoystick::MultitouchJoystick::SetJoystickIdentifier"
+                      },
+                      "parameters": [
+                        "Border",
+                        "MultitouchJoystick",
+                        "=",
+                        "Value",
+                        ""
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "supplementaryInformation": "SpriteMultitouchJoystick::SpriteMultitouchJoystick",
+                  "type": "object"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "the dead zone radius of the joystick (range: 0 to 1). The deadzone is an area for which movement on sticks won't be taken into account (instead, the stick will be considered as not moved).",
+              "fullName": "Dead zone radius",
+              "functionType": "ExpressionAndCondition",
+              "group": "Multitouch Joystick configuration",
+              "name": "DeadZoneRadius",
+              "sentence": "the dead zone radius",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SetReturnNumber"
+                      },
+                      "parameters": [
+                        "Border.MultitouchJoystick::PropertyDeadZoneRadius()"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "expressionType": {
+                "type": "expression"
+              },
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "supplementaryInformation": "SpriteMultitouchJoystick::SpriteMultitouchJoystick",
+                  "type": "object"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "fullName": "",
+              "functionType": "ActionWithOperator",
+              "getterName": "DeadZoneRadius",
+              "name": "SetDeadZoneRadius",
+              "sentence": "",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SpriteMultitouchJoystick::MultitouchJoystick::SetDeadZoneRadius"
+                      },
+                      "parameters": [
+                        "Border",
+                        "MultitouchJoystick",
+                        "=",
+                        "Value",
+                        ""
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "supplementaryInformation": "SpriteMultitouchJoystick::SpriteMultitouchJoystick",
+                  "type": "object"
+                }
+              ],
+              "objectGroups": []
+            }
+          ],
+          "propertyDescriptors": [
+            {
+              "value": "1",
+              "type": "Number",
+              "label": "Multitouch controller identifier (1, 2, 3, 4...)",
+              "description": "",
+              "group": "",
+              "extraInformation": [],
+              "name": "ControllerIdentifier"
+            },
+            {
+              "value": "Primary",
+              "type": "Choice",
+              "label": "Joystick name",
+              "description": "",
+              "group": "",
+              "extraInformation": [
+                "Primary",
+                "Secondary"
+              ],
+              "name": "JoystickIdentifier"
+            },
+            {
+              "value": "0.4",
+              "type": "Number",
+              "label": "Dead zone radius (range: 0 to 1)",
+              "description": "The deadzone is an area for which movement on sticks won't be taken into account (instead, the stick will be considered as not moved)",
+              "group": "",
+              "extraInformation": [],
+              "name": "DeadZoneRadius"
+            },
+            {
+              "value": "Center-center",
+              "type": "String",
+              "label": "",
+              "description": "Only used by the scene editor.",
+              "group": "",
+              "extraInformation": [
+                "Thumb"
+              ],
+              "hidden": true,
+              "name": "ThumbAnchorOrigin"
+            },
+            {
+              "value": "Center-center",
+              "type": "Number",
+              "label": "",
+              "description": "Only used by the scene editor.",
+              "group": "",
+              "extraInformation": [],
+              "hidden": true,
+              "name": "ThumbAnchorTarget"
+            },
+            {
+              "value": "true",
+              "type": "Boolean",
+              "label": "",
+              "description": "Only used by the scene editor.",
+              "group": "",
+              "extraInformation": [
+                "Thumb"
+              ],
+              "hidden": true,
+              "name": "ThumbIsScaledProportionally"
+            },
+            {
+              "value": "Center-center",
+              "type": "String",
+              "label": "",
+              "description": "Only used by the scene editor.",
+              "group": "",
+              "extraInformation": [],
+              "hidden": true,
+              "name": "ParentOrigin"
+            },
+            {
+              "value": "",
+              "type": "Boolean",
+              "label": "",
+              "description": "",
+              "group": "",
+              "extraInformation": [],
+              "hidden": true,
+              "name": "ShouldBeHiddenWhenReleased"
+            }
+          ],
+          "objects": [
+            {
+              "adaptCollisionMaskAutomatically": false,
+              "assetStoreId": "",
+              "name": "Thumb",
+              "type": "Sprite",
+              "updateIfNotVisible": false,
+              "variables": [],
+              "effects": [],
+              "behaviors": [],
+              "animations": [
+                {
+                  "name": "Idle",
+                  "useMultipleDirections": false,
+                  "directions": [
+                    {
+                      "looping": false,
+                      "timeBetweenFrames": 0.08,
+                      "sprites": []
+                    }
+                  ]
+                }
+              ]
+            },
+            {
+              "adaptCollisionMaskAutomatically": false,
+              "assetStoreId": "",
+              "name": "Border",
+              "type": "Sprite",
+              "updateIfNotVisible": false,
+              "variables": [],
+              "effects": [],
+              "behaviors": [
+                {
+                  "name": "MultitouchJoystick",
+                  "type": "SpriteMultitouchJoystick::MultitouchJoystick",
+                  "ControllerIdentifier": 1,
+                  "JoystickIdentifier": "Primary",
+                  "FloatingEnabled": false,
+                  "DeadZoneRadius": 0.4,
+                  "JoystickAngle": 0,
+                  "JoystickForce": 0,
+                  "TouchId": 0,
+                  "TouchIndex": 0
+                }
+              ],
+              "animations": [
+                {
+                  "name": "Idle",
+                  "useMultipleDirections": false,
+                  "directions": [
+                    {
+                      "looping": false,
+                      "timeBetweenFrames": 0.08,
+                      "sprites": []
+                    }
+                  ]
+                }
+              ]
+            }
+          ],
+          "objectsFolderStructure": {
+            "folderName": "__ROOT",
+            "children": [
+              {
+                "objectName": "Thumb"
+              },
+              {
+                "objectName": "Border"
+              }
+            ]
+          },
+          "objectsGroups": [],
+          "layers": [
+            {
+              "ambientLightColorB": 200,
+              "ambientLightColorG": 200,
+              "ambientLightColorR": 200,
+              "camera3DFarPlaneDistance": 10000,
+              "camera3DFieldOfView": 45,
+              "camera3DNearPlaneDistance": 3,
+              "cameraType": "",
+              "followBaseLayerCamera": false,
+              "isLightingLayer": false,
+              "isLocked": false,
+              "name": "",
+              "renderingType": "",
+              "visibility": true,
+              "cameras": [
+                {
+                  "defaultSize": true,
+                  "defaultViewport": true,
+                  "height": 0,
+                  "viewportBottom": 1,
+                  "viewportLeft": 0,
+                  "viewportRight": 1,
+                  "viewportTop": 0,
+                  "width": 0
+                }
+              ],
+              "effects": []
+            }
+          ],
+          "instances": []
+        }
+      ]
+    },
+    {
+      "author": "",
+      "category": "Movement",
+      "extensionNamespace": "",
+      "fullName": "Physics car",
+      "helpPath": "",
+      "iconUrl": "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz48IURPQ1RZUEUgc3ZnIFBVQkxJQyAiLS8vVzNDLy9EVEQgU1ZHIDEuMS8vRU4iICJodHRwOi8vd3d3LnczLm9yZy9HcmFwaGljcy9TVkcvMS4xL0RURC9zdmcxMS5kdGQiPjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgdmVyc2lvbj0iMS4xIiBpZD0ibWRpLWNhci1iYWNrIiB3aWR0aD0iMjQiIGhlaWdodD0iMjQiIHZpZXdCb3g9IjAgMCAyNCAyNCI+PHBhdGggZD0iTTYsMTFMNyw3SDE3TDE4LDExTTE4LjkyLDZDMTguNzEsNS40IDE4LjE0LDUgMTcuNSw1SDYuNUM1Ljg2LDUgNS4yOSw1LjQgNS4wOCw2TDMsMTJWMjBBMSwxIDAgMCwwIDQsMjFINUExLDEgMCAwLDAgNiwyMFYxOEgxOFYyMEExLDEgMCAwLDAgMTksMjFIMjBBMSwxIDAgMCwwIDIxLDIwVjEyTDE4LjkyLDZNNywxNkg1VjE0SDdWMTZNMTksMTZIMTdWMTRIMTlWMTZNMTQsMTZIMTBWMTRIMTRWMTZaIiAvPjwvc3ZnPg==",
+      "name": "PhysicsCar",
+      "previewIconUrl": "https://asset-resources.gdevelop.io/public-resources/Icons/b4a3bb68575adbecb4418e0397ef8c05913e1f0757dfd111de7e6a4ff31eb149_car-back.svg",
+      "shortDescription": "Simulate car motion with drifting.",
+      "version": "0.1.0",
+      "description": [
+        "Simulate car motion with the Physics 2 behavior.",
+        "",
+        "The 3D car coin hunt game example uses this extension ([open the project online](https://editor.gdevelop.io/?project=example://3d-car-coin-hunt))."
+      ],
+      "tags": [
+        "race"
+      ],
+      "authorIds": [
+        "IWykYNRvhCZBN3vEgKEbBPOR3Oc2"
+      ],
+      "dependencies": [],
+      "globalVariables": [],
+      "sceneVariables": [],
+      "eventsFunctions": [],
+      "eventsBasedBehaviors": [
+        {
+          "description": "Simulate car motion.",
+          "fullName": "Physics car",
+          "name": "PhysicsCar",
+          "objectType": "",
+          "eventsFunctions": [
+            {
+              "fullName": "",
+              "functionType": "Action",
+              "name": "onCreated",
+              "sentence": "",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "Physics2::AngularDamping"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Physics2",
+                        "=",
+                        "0"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "PhysicsCar::PhysicsCar",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "fullName": "",
+              "functionType": "Action",
+              "name": "doStepPreEvents",
+              "sentence": "",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [
+                    {
+                      "type": {
+                        "value": "PhysicsCar::PhysicsCar::PropertyIsDebug"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior"
+                      ]
+                    }
+                  ],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "ObjectVariableClearChildren"
+                      },
+                      "parameters": [
+                        "Object",
+                        "_PhysicsCarExtension.Forces"
+                      ]
+                    }
+                  ]
+                },
+                {
+                  "colorB": 228,
+                  "colorG": 176,
+                  "colorR": 74,
+                  "creationTime": 0,
+                  "name": "Steering inputs",
+                  "source": "",
+                  "type": "BuiltinCommonInstructions::Group",
+                  "events": [
+                    {
+                      "type": "BuiltinCommonInstructions::Standard",
+                      "conditions": [
+                        {
+                          "type": {
+                            "value": "PhysicsCar::PhysicsCar::PropertyIsLeftPressed"
+                          },
+                          "parameters": [
+                            "Object",
+                            "Behavior"
+                          ]
+                        }
+                      ],
+                      "actions": [
+                        {
+                          "type": {
+                            "value": "PhysicsCar::PhysicsCar::SetPropertySteeringStickValue"
+                          },
+                          "parameters": [
+                            "Object",
+                            "Behavior",
+                            "=",
+                            "-1"
+                          ]
+                        }
+                      ]
+                    },
+                    {
+                      "type": "BuiltinCommonInstructions::Standard",
+                      "conditions": [
+                        {
+                          "type": {
+                            "value": "PhysicsCar::PhysicsCar::PropertyIsRightPressed"
+                          },
+                          "parameters": [
+                            "Object",
+                            "Behavior"
+                          ]
+                        }
+                      ],
+                      "actions": [
+                        {
+                          "type": {
+                            "value": "PhysicsCar::PhysicsCar::SetPropertySteeringStickValue"
+                          },
+                          "parameters": [
+                            "Object",
+                            "Behavior",
+                            "=",
+                            "1"
+                          ]
+                        }
+                      ]
+                    },
+                    {
+                      "type": "BuiltinCommonInstructions::Standard",
+                      "conditions": [
+                        {
+                          "type": {
+                            "value": "PhysicsCar::PhysicsCar::PropertySteeringStickValue"
+                          },
+                          "parameters": [
+                            "Object",
+                            "Behavior",
+                            "=",
+                            "0"
+                          ]
+                        }
+                      ],
+                      "actions": [],
+                      "events": [
+                        {
+                          "type": "BuiltinCommonInstructions::Standard",
+                          "conditions": [
+                            {
+                              "type": {
+                                "value": "PhysicsCar::PhysicsCar::PropertySteeringAngle"
+                              },
+                              "parameters": [
+                                "Object",
+                                "Behavior",
+                                "<",
+                                "0"
+                              ]
+                            }
+                          ],
+                          "actions": [
+                            {
+                              "type": {
+                                "value": "PhysicsCar::PhysicsCar::SetPropertySteeringAngle"
+                              },
+                              "parameters": [
+                                "Object",
+                                "Behavior",
+                                "=",
+                                "min(0, SteeringAngle + SteeringBackSpeed * TimeDelta())"
+                              ]
+                            }
+                          ]
+                        },
+                        {
+                          "type": "BuiltinCommonInstructions::Standard",
+                          "conditions": [
+                            {
+                              "type": {
+                                "value": "PhysicsCar::PhysicsCar::PropertySteeringAngle"
+                              },
+                              "parameters": [
+                                "Object",
+                                "Behavior",
+                                ">",
+                                "0"
+                              ]
+                            }
+                          ],
+                          "actions": [
+                            {
+                              "type": {
+                                "value": "PhysicsCar::PhysicsCar::SetPropertySteeringAngle"
+                              },
+                              "parameters": [
+                                "Object",
+                                "Behavior",
+                                "=",
+                                "max(0, SteeringAngle - SteeringBackSpeed * TimeDelta())"
+                              ]
+                            }
+                          ]
+                        }
+                      ]
+                    },
+                    {
+                      "type": "BuiltinCommonInstructions::Standard",
+                      "conditions": [
+                        {
+                          "type": {
+                            "value": "PhysicsCar::PhysicsCar::PropertySteeringStickValue"
+                          },
+                          "parameters": [
+                            "Object",
+                            "Behavior",
+                            ">",
+                            "0"
+                          ]
+                        }
+                      ],
+                      "actions": [],
+                      "events": [
+                        {
+                          "type": "BuiltinCommonInstructions::Standard",
+                          "conditions": [
+                            {
+                              "type": {
+                                "value": "PhysicsCar::PhysicsCar::PropertySteeringAngle"
+                              },
+                              "parameters": [
+                                "Object",
+                                "Behavior",
+                                "<",
+                                "0"
+                              ]
+                            }
+                          ],
+                          "actions": [
+                            {
+                              "type": {
+                                "value": "PhysicsCar::PhysicsCar::SetPropertySteeringAngle"
+                              },
+                              "parameters": [
+                                "Object",
+                                "Behavior",
+                                "=",
+                                "min(SteeringAngleMax, SteeringAngle + SteeringBackSpeed * TimeDelta())"
+                              ]
+                            }
+                          ]
+                        },
+                        {
+                          "type": "BuiltinCommonInstructions::Standard",
+                          "conditions": [
+                            {
+                              "type": {
+                                "value": "PhysicsCar::PhysicsCar::PropertySteeringAngle"
+                              },
+                              "parameters": [
+                                "Object",
+                                "Behavior",
+                                ">=",
+                                "0"
+                              ]
+                            }
+                          ],
+                          "actions": [
+                            {
+                              "type": {
+                                "value": "PhysicsCar::PhysicsCar::SetPropertySteeringAngle"
+                              },
+                              "parameters": [
+                                "Object",
+                                "Behavior",
+                                "=",
+                                "min(SteeringAngleMax, SteeringAngle + SteeringSpeed * TimeDelta() * SteeringStickValue)"
+                              ]
+                            }
+                          ]
+                        }
+                      ]
+                    },
+                    {
+                      "type": "BuiltinCommonInstructions::Standard",
+                      "conditions": [
+                        {
+                          "type": {
+                            "value": "PhysicsCar::PhysicsCar::PropertySteeringStickValue"
+                          },
+                          "parameters": [
+                            "Object",
+                            "Behavior",
+                            "<",
+                            "0"
+                          ]
+                        }
+                      ],
+                      "actions": [],
+                      "events": [
+                        {
+                          "type": "BuiltinCommonInstructions::Standard",
+                          "conditions": [
+                            {
+                              "type": {
+                                "value": "PhysicsCar::PhysicsCar::PropertySteeringAngle"
+                              },
+                              "parameters": [
+                                "Object",
+                                "Behavior",
+                                "<=",
+                                "0"
+                              ]
+                            }
+                          ],
+                          "actions": [
+                            {
+                              "type": {
+                                "value": "PhysicsCar::PhysicsCar::SetPropertySteeringAngle"
+                              },
+                              "parameters": [
+                                "Object",
+                                "Behavior",
+                                "=",
+                                "max(-SteeringAngleMax, SteeringAngle + SteeringSpeed * TimeDelta() * SteeringStickValue)"
+                              ]
+                            }
+                          ]
+                        },
+                        {
+                          "type": "BuiltinCommonInstructions::Standard",
+                          "conditions": [
+                            {
+                              "type": {
+                                "value": "PhysicsCar::PhysicsCar::PropertySteeringAngle"
+                              },
+                              "parameters": [
+                                "Object",
+                                "Behavior",
+                                ">",
+                                "0"
+                              ]
+                            }
+                          ],
+                          "actions": [
+                            {
+                              "type": {
+                                "value": "PhysicsCar::PhysicsCar::SetPropertySteeringAngle"
+                              },
+                              "parameters": [
+                                "Object",
+                                "Behavior",
+                                "=",
+                                "max(-SteeringAngleMax, SteeringAngle - SteeringBackSpeed * TimeDelta())"
+                              ]
+                            }
+                          ]
+                        }
+                      ]
+                    }
+                  ],
+                  "parameters": []
+                },
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SetNumberVariable"
+                      },
+                      "parameters": [
+                        "FrontWheelX",
+                        "=",
+                        "Object.CenterX() + XFromAngleAndDistance(Object.Angle(), FrontWheelsPosition * Object.Width() / 2)"
+                      ]
+                    },
+                    {
+                      "type": {
+                        "value": "SetNumberVariable"
+                      },
+                      "parameters": [
+                        "FrontWheelY",
+                        "=",
+                        "Object.CenterY() + YFromAngleAndDistance(Object.Angle(), FrontWheelsPosition * Object.Width() / 2)"
+                      ]
+                    },
+                    {
+                      "type": {
+                        "value": "SetNumberVariable"
+                      },
+                      "parameters": [
+                        "FrontWheelAngle",
+                        "=",
+                        "Object.Angle() + SteeringAngle"
+                      ]
+                    },
+                    {
+                      "type": {
+                        "value": "SetNumberVariable"
+                      },
+                      "parameters": [
+                        "RearWheelX",
+                        "=",
+                        "Object.CenterX() - XFromAngleAndDistance(Object.Angle(), RearWheelsPosition * Object.Width() / 2)"
+                      ]
+                    },
+                    {
+                      "type": {
+                        "value": "SetNumberVariable"
+                      },
+                      "parameters": [
+                        "RearWheelY",
+                        "=",
+                        "Object.CenterY() - YFromAngleAndDistance(Object.Angle(), RearWheelsPosition * Object.Width() / 2)"
+                      ]
+                    }
+                  ],
+                  "events": [
+                    {
+                      "type": "BuiltinCommonInstructions::Comment",
+                      "color": {
+                        "b": 109,
+                        "g": 230,
+                        "r": 255,
+                        "textB": 0,
+                        "textG": 0,
+                        "textR": 0
+                      },
+                      "comment": "Engine"
+                    },
+                    {
+                      "type": "BuiltinCommonInstructions::Standard",
+                      "conditions": [
+                        {
+                          "type": {
+                            "value": "PhysicsCar::PhysicsCar::PropertyIsUpPressed"
+                          },
+                          "parameters": [
+                            "Object",
+                            "Behavior"
+                          ]
+                        }
+                      ],
+                      "actions": [
+                        {
+                          "type": {
+                            "value": "PhysicsCar::PhysicsCar::SetPropertyAccelerationStickValue"
+                          },
+                          "parameters": [
+                            "Object",
+                            "Behavior",
+                            "=",
+                            "1"
+                          ]
+                        }
+                      ]
+                    },
+                    {
+                      "type": "BuiltinCommonInstructions::Standard",
+                      "conditions": [
+                        {
+                          "type": {
+                            "value": "PhysicsCar::PhysicsCar::PropertyIsDownPressed"
+                          },
+                          "parameters": [
+                            "Object",
+                            "Behavior"
+                          ]
+                        }
+                      ],
+                      "actions": [
+                        {
+                          "type": {
+                            "value": "PhysicsCar::PhysicsCar::SetPropertyAccelerationStickValue"
+                          },
+                          "parameters": [
+                            "Object",
+                            "Behavior",
+                            "=",
+                            "-1"
+                          ]
+                        }
+                      ]
+                    },
+                    {
+                      "type": "BuiltinCommonInstructions::Standard",
+                      "conditions": [],
+                      "actions": [
+                        {
+                          "type": {
+                            "value": "PhysicsCar::PhysicsCar::ApplyPolarForce"
+                          },
+                          "parameters": [
+                            "Object",
+                            "Behavior",
+                            "FrontWheelAngle",
+                            "AccelerationStickValue * Acceleration * Object.Physics2::Mass()",
+                            "FrontWheelX",
+                            "FrontWheelY",
+                            "FrontWheelY"
+                          ]
+                        }
+                      ]
+                    },
+                    {
+                      "type": "BuiltinCommonInstructions::Comment",
+                      "color": {
+                        "b": 109,
+                        "g": 230,
+                        "r": 255,
+                        "textB": 0,
+                        "textG": 0,
+                        "textR": 0
+                      },
+                      "comment": "Wheels grip\nOnly 2 wheels are simulated instead of 4 to simplify calculus."
+                    },
+                    {
+                      "type": "BuiltinCommonInstructions::Standard",
+                      "conditions": [],
+                      "actions": [
+                        {
+                          "type": {
+                            "value": "PhysicsCar::PhysicsCar::ApplyWheelForces"
+                          },
+                          "parameters": [
+                            "Object",
+                            "Behavior",
+                            "FrontWheelX",
+                            "FrontWheelY",
+                            "FrontWheelAngle",
+                            "Debug",
+                            ""
+                          ]
+                        },
+                        {
+                          "type": {
+                            "value": "PhysicsCar::PhysicsCar::ApplyWheelForces"
+                          },
+                          "parameters": [
+                            "Object",
+                            "Behavior",
+                            "RearWheelX",
+                            "RearWheelY",
+                            "Object.Angle()",
+                            "Debug",
+                            ""
+                          ]
+                        }
+                      ]
+                    }
+                  ],
+                  "variables": [
+                    {
+                      "folded": true,
+                      "name": "FrontWheelX",
+                      "type": "number",
+                      "value": 0
+                    },
+                    {
+                      "folded": true,
+                      "name": "FrontWheelY",
+                      "type": "number",
+                      "value": 0
+                    },
+                    {
+                      "folded": true,
+                      "name": "FrontWheelAngle",
+                      "type": "number",
+                      "value": 0
+                    },
+                    {
+                      "folded": true,
+                      "name": "RearWheelX",
+                      "type": "number",
+                      "value": 0
+                    },
+                    {
+                      "folded": true,
+                      "name": "RearWheelY",
+                      "type": "number",
+                      "value": 0
+                    }
+                  ]
+                },
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [
+                    {
+                      "type": {
+                        "value": "Physics2::LinearVelocityLength"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Physics2",
+                        ">",
+                        "SpeedMax"
+                      ]
+                    }
+                  ],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "Physics2::LinearVelocityAngle"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Physics2",
+                        "Object.Physics2::LinearVelocityAngle()",
+                        "SpeedMax"
+                      ]
+                    }
+                  ]
+                },
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "PhysicsCar::PhysicsCar::SetPropertyIsLeftPressed"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "no"
+                      ]
+                    },
+                    {
+                      "type": {
+                        "value": "PhysicsCar::PhysicsCar::SetPropertyIsRightPressed"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "no"
+                      ]
+                    },
+                    {
+                      "type": {
+                        "value": "PhysicsCar::PhysicsCar::SetPropertyIsUpPressed"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "no"
+                      ]
+                    },
+                    {
+                      "type": {
+                        "value": "PhysicsCar::PhysicsCar::SetPropertyIsDownPressed"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "no"
+                      ]
+                    },
+                    {
+                      "type": {
+                        "value": "PhysicsCar::PhysicsCar::SetPropertyAccelerationStickValue"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "=",
+                        "0"
+                      ]
+                    },
+                    {
+                      "type": {
+                        "value": "PhysicsCar::PhysicsCar::SetPropertySteeringStickValue"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "=",
+                        "0"
+                      ]
+                    },
+                    {
+                      "type": {
+                        "value": "PhysicsCar::PhysicsCar::SetPropertyIsDebug"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        ""
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "PhysicsCar::PhysicsCar",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "Simulate a press of the right key.",
+              "fullName": "Simulate right key press",
+              "functionType": "Action",
+              "name": "SimulateRightKey",
+              "sentence": "Simulate pressing right for _PARAM0_",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "PhysicsCar::PhysicsCar::SetPropertyIsRightPressed"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "yes"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "PhysicsCar::PhysicsCar",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "Simulate a press of the left key.",
+              "fullName": "Simulate left key press",
+              "functionType": "Action",
+              "name": "SimulateLeftKey",
+              "sentence": "Simulate pressing left for _PARAM0_",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "PhysicsCar::PhysicsCar::SetPropertyIsLeftPressed"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "yes"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "PhysicsCar::PhysicsCar",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "Simulate a press of the up key.",
+              "fullName": "Simulate up key press",
+              "functionType": "Action",
+              "name": "SimulateUpKey",
+              "sentence": "Simulate pressing up for _PARAM0_",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "PhysicsCar::PhysicsCar::SetPropertyIsUpPressed"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "yes"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "PhysicsCar::PhysicsCar",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "Simulate a press of the down key.",
+              "fullName": "Simulate down key press",
+              "functionType": "Action",
+              "name": "SimulateDownKey",
+              "sentence": "Simulate pressing down for _PARAM0_",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "PhysicsCar::PhysicsCar::SetPropertyIsDownPressed"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "yes"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "PhysicsCar::PhysicsCar",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "Simulate a steering stick for a given axis force.",
+              "fullName": "Simulate steering stick",
+              "functionType": "Action",
+              "name": "SimulateSteeringStick",
+              "sentence": "Simulate a steering stick for _PARAM0_ with a force of _PARAM2_",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "PhysicsCar::PhysicsCar::SetPropertySteeringStickValue"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "=",
+                        "Value"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "PhysicsCar::PhysicsCar",
+                  "type": "behavior"
+                },
+                {
+                  "description": "Stick force",
+                  "name": "Value",
+                  "type": "expression"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "Simulate an acceleration stick for a given axis force.",
+              "fullName": "Simulate acceleration stick",
+              "functionType": "Action",
+              "name": "SimulateAccelerationStick",
+              "sentence": "Simulate an acceleration stick for _PARAM0_ with a force of _PARAM2_",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "PhysicsCar::PhysicsCar::SetPropertyAccelerationStickValue"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "=",
+                        "Value"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "PhysicsCar::PhysicsCar",
+                  "type": "behavior"
+                },
+                {
+                  "description": "Stick force",
+                  "name": "Value",
+                  "type": "expression"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "fullName": "Apply wheel forces",
+              "functionType": "Action",
+              "name": "ApplyWheelForces",
+              "private": true,
+              "sentence": "Apply forces on the wheel at _PARAM2_ ; _PARAM3_ and _PARAM4_° of _PARAM0_",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Comment",
+                  "color": {
+                    "b": 109,
+                    "g": 230,
+                    "r": 255,
+                    "textB": 0,
+                    "textG": 0,
+                    "textR": 0
+                  },
+                  "comment": "When cars don't slip (WheelGripRatio = 1), there is no motion orthogonally to wheels direction.\n\nSo, we project the velocity orthogonally to the wheel to find the velocity to fight:\nVelocity * sin(VelocityAngle - WheelAngle)\n\nNow, we have the velocity that we want to fight, but we need to apply a force to the object.\nSo, we need find how much force must be applied to have the velocity change we want.\n\nConsidering that:\n- there are only 1 wheel at the front and 1 wheel at the back\n- the wheels are considered to be in the same direction as the car (even if the front one car actually turn)\n\nWe have:\nAngularAcceleration = Force * Distance / Inertia\nAcceleration = AngularAcceleration * Distance\nVelocityDelta = Acceleration * TimeDelta\n\nWhich results to:\nVelocityDelta = Force / Inertia * Distance * TimeDelta\n\nWe find the force to apply to fight the velocity:\nForce = VelocityDelta * Inertia / (Distance * TimeDelta)\n\nFinally, there is a division by 2 because the car has 2 wheels and they probably fight the same velocity.\nTo check the equilibrium, you can set the WheelGripRatio around 2. The velocity will go back and forth and either converge (<2) or diverge (>2)."
+                },
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SetNumberVariable"
+                      },
+                      "parameters": [
+                        "DistanceSquared",
+                        "=",
+                        "(WheelX - Object.Physics2::MassCenterX()) * (WheelX - Object.Physics2::MassCenterX()) + (WheelY - Object.Physics2::MassCenterY()) * (WheelY - Object.CenterY())"
+                      ]
+                    },
+                    {
+                      "type": {
+                        "value": "PhysicsCar::PhysicsCar::ApplyPolarForce"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "WheelAngle + 90",
+                        "-WheelGripRatio * Object.Behavior::LinearVelocityAt(WheelX, WheelY) * sin(ToRad(Object.Behavior::LinearVelocityAngleAt(WheelX, WheelY) - WheelAngle)) * Object.Behavior::Inertia() / (DistanceSquared * TimeDelta()) / 2",
+                        "WheelX",
+                        "WheelY",
+                        "WheelY"
+                      ]
+                    }
+                  ],
+                  "variables": [
+                    {
+                      "folded": true,
+                      "name": "DistanceSquared",
+                      "type": "number",
+                      "value": 0
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "PhysicsCar::PhysicsCar",
+                  "type": "behavior"
+                },
+                {
+                  "description": "Wheel X",
+                  "name": "WheelX",
+                  "type": "expression"
+                },
+                {
+                  "description": "Wheel Y",
+                  "name": "WheelY",
+                  "type": "expression"
+                },
+                {
+                  "description": "Wheel angle",
+                  "name": "WheelAngle",
+                  "type": "expression"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "fullName": "",
+              "functionType": "Expression",
+              "name": "LinearVelocityXAt",
+              "private": true,
+              "sentence": "",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SetReturnNumber"
+                      },
+                      "parameters": [
+                        "Object.Physics2::LinearVelocityX() - ToRad(Object.Physics2::AngularVelocity()) * (PositionY - Object.CenterY())"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "expressionType": {
+                "type": "expression"
+              },
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "PhysicsCar::PhysicsCar",
+                  "type": "behavior"
+                },
+                {
+                  "description": "",
+                  "name": "PositionX",
+                  "type": "expression"
+                },
+                {
+                  "description": "",
+                  "name": "PositionY",
+                  "type": "expression"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "fullName": "",
+              "functionType": "Expression",
+              "name": "LinearVelocityYAt",
+              "private": true,
+              "sentence": "",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SetReturnNumber"
+                      },
+                      "parameters": [
+                        "Object.Physics2::LinearVelocityY() + ToRad(Object.Physics2::AngularVelocity()) * (PositionX - Object.CenterX())"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "expressionType": {
+                "type": "expression"
+              },
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "PhysicsCar::PhysicsCar",
+                  "type": "behavior"
+                },
+                {
+                  "description": "",
+                  "name": "PositionX",
+                  "type": "expression"
+                },
+                {
+                  "description": "",
+                  "name": "PositionY",
+                  "type": "expression"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "fullName": "",
+              "functionType": "Expression",
+              "name": "LinearVelocityAt",
+              "private": true,
+              "sentence": "",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SetReturnNumber"
+                      },
+                      "parameters": [
+                        "DistanceBetweenPositions(0, 0, Object.Behavior::LinearVelocityXAt(PositionX, PositionY), Object.Behavior::LinearVelocityYAt(PositionX, PositionY))"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "expressionType": {
+                "type": "expression"
+              },
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "PhysicsCar::PhysicsCar",
+                  "type": "behavior"
+                },
+                {
+                  "description": "",
+                  "name": "PositionX",
+                  "type": "expression"
+                },
+                {
+                  "description": "",
+                  "name": "PositionY",
+                  "type": "expression"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "fullName": "",
+              "functionType": "Expression",
+              "name": "LinearVelocityAngleAt",
+              "private": true,
+              "sentence": "",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SetReturnNumber"
+                      },
+                      "parameters": [
+                        "AngleBetweenPositions(0, 0, Object.Behavior::LinearVelocityXAt(PositionX, PositionY), Object.Behavior::LinearVelocityYAt(PositionX, PositionY))"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "expressionType": {
+                "type": "expression"
+              },
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "PhysicsCar::PhysicsCar",
+                  "type": "behavior"
+                },
+                {
+                  "description": "",
+                  "name": "PositionX",
+                  "type": "expression"
+                },
+                {
+                  "description": "",
+                  "name": "PositionY",
+                  "type": "expression"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "Return the momentum of inertia (in kg ⋅ pixel²)",
+              "fullName": "Momentum of inertia",
+              "functionType": "Expression",
+              "name": "Inertia",
+              "private": true,
+              "sentence": "",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::JsCode",
+                  "inlineCode": [
+                    "// TODO Remove this function when the engine uses pixels instead of meters.",
+                    "",
+                    "const object = objects[0];",
+                    "const behavior = object.getBehavior(eventsFunctionContext.getBehaviorName(\"Behavior\"));",
+                    "const physics2 = object.getBehavior(behavior._getPhysics2());",
+                    "",
+                    "// If there is no body, set a new one",
+                    "if (physics2._body === null) {",
+                    "    if (!physics2.createBody()) return 0;",
+                    "}",
+                    "// Wake up the object",
+                    "physics2._body.SetAwake(true);",
+                    "// scaleX or scaleY doesn't matter as they can't be different or it breaks any physics law.",
+                    "eventsFunctionContext.returnValue = physics2._body.GetInertia() * physics2._sharedData.scaleX * physics2._sharedData.scaleX;"
+                  ],
+                  "parameterObjects": "Object",
+                  "useStrict": true,
+                  "eventsSheetExpanded": true
+                }
+              ],
+              "expressionType": {
+                "type": "expression"
+              },
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "PhysicsCar::PhysicsCar",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "fullName": "Apply a polar force",
+              "functionType": "Action",
+              "name": "ApplyPolarForce",
+              "private": true,
+              "sentence": "",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [
+                    {
+                      "type": {
+                        "value": "PhysicsCar::PhysicsCar::PropertyIsDebug"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior"
+                      ]
+                    }
+                  ],
+                  "actions": [],
+                  "events": [
+                    {
+                      "type": "BuiltinCommonInstructions::Standard",
+                      "conditions": [],
+                      "actions": [
+                        {
+                          "type": {
+                            "value": "SetNumberVariable"
+                          },
+                          "parameters": [
+                            "Index",
+                            "=",
+                            "Object.VariableChildCount(_PhysicsCarExtension.Forces)"
+                          ]
+                        },
+                        {
+                          "type": {
+                            "value": "ModVarObjet"
+                          },
+                          "parameters": [
+                            "Object",
+                            "_PhysicsCarExtension.Forces[Index].Angle",
+                            "=",
+                            "Angle"
+                          ]
+                        },
+                        {
+                          "type": {
+                            "value": "ModVarObjet"
+                          },
+                          "parameters": [
+                            "Object",
+                            "_PhysicsCarExtension.Forces[Index].Length",
+                            "=",
+                            "Length"
+                          ]
+                        },
+                        {
+                          "type": {
+                            "value": "ModVarObjet"
+                          },
+                          "parameters": [
+                            "Object",
+                            "_PhysicsCarExtension.Forces[Index].OriginX",
+                            "=",
+                            "OriginX"
+                          ]
+                        },
+                        {
+                          "type": {
+                            "value": "ModVarObjet"
+                          },
+                          "parameters": [
+                            "Object",
+                            "_PhysicsCarExtension.Forces[Index].OriginY",
+                            "=",
+                            "OriginY"
+                          ]
+                        }
+                      ],
+                      "variables": [
+                        {
+                          "folded": true,
+                          "name": "Index",
+                          "type": "number",
+                          "value": 0
+                        }
+                      ]
+                    }
+                  ]
+                },
+                {
+                  "type": "BuiltinCommonInstructions::JsCode",
+                  "inlineCode": [
+                    "// TODO Remove this function when the engine uses pixels instead of meters.",
+                    "",
+                    "const object = objects[0];",
+                    "const behavior = object.getBehavior(eventsFunctionContext.getBehaviorName(\"Behavior\"));",
+                    "const physics2 = object.getBehavior(behavior._getPhysics2());",
+                    "const angle = gdjs.toRad(eventsFunctionContext.getArgument(\"Angle\"));",
+                    "// scaleX or scaleY doesn't matter as they can't be different or it breaks any physics law.",
+                    "const length = eventsFunctionContext.getArgument(\"Length\") * physics2._sharedData.invScaleX;",
+                    "const originX = eventsFunctionContext.getArgument(\"OriginX\") * physics2._sharedData.invScaleX;",
+                    "const originY = eventsFunctionContext.getArgument(\"OriginY\") * physics2._sharedData.invScaleX;",
+                    "",
+                    "// If there is no body, set a new one",
+                    "if (physics2._body === null) {",
+                    "  if (!physics2.createBody()) return;",
+                    "}",
+                    "",
+                    "// Wake up the object",
+                    "physics2._body.SetAwake(true);",
+                    "",
+                    "// Apply the force",
+                    "physics2._body.ApplyForce(",
+                    "  physics2.b2Vec2(length * Math.cos(angle), length * Math.sin(angle)),",
+                    "  physics2.b2Vec2Sec(originX, originY),",
+                    "  false",
+                    ");"
+                  ],
+                  "parameterObjects": "Object",
+                  "useStrict": true,
+                  "eventsSheetExpanded": true
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "PhysicsCar::PhysicsCar",
+                  "type": "behavior"
+                },
+                {
+                  "description": "",
+                  "name": "Angle",
+                  "type": "expression"
+                },
+                {
+                  "description": "Length (in kg ⋅ pixels ⋅ s^−2)",
+                  "name": "Length",
+                  "type": "expression"
+                },
+                {
+                  "description": "",
+                  "name": "OriginX",
+                  "type": "expression"
+                },
+                {
+                  "description": "",
+                  "name": "OriginY",
+                  "type": "expression"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "Draw forces applying on the car for debug purpose.",
+              "fullName": "Draw forces for debug",
+              "functionType": "Action",
+              "name": "DrawDebug",
+              "sentence": "Draw forces of car _PARAM0_ on _PARAM2_",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "PhysicsCar::PhysicsCar::SetPropertyIsDebug"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "yes"
+                      ]
+                    },
+                    {
+                      "type": {
+                        "value": "PrimitiveDrawing::Rectangle"
+                      },
+                      "parameters": [
+                        "ShapePainter",
+                        "-Object.Width() / 2",
+                        "-Object.Height() / 2",
+                        "Object.Width() / 2",
+                        "Object.Height() / 2"
+                      ]
+                    }
+                  ]
+                },
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [],
+                  "events": [
+                    {
+                      "type": "BuiltinCommonInstructions::Repeat",
+                      "repeatExpression": "Object.VariableChildCount(_PhysicsCarExtension.Forces)",
+                      "conditions": [],
+                      "actions": [],
+                      "events": [
+                        {
+                          "type": "BuiltinCommonInstructions::Standard",
+                          "conditions": [],
+                          "actions": [
+                            {
+                              "type": {
+                                "value": "SetNumberVariable"
+                              },
+                              "parameters": [
+                                "DeltaAngle",
+                                "=",
+                                "AngleBetweenPositions(Object.CenterX(), Object.CenterY(), Object.Variable(_PhysicsCarExtension.Forces[Index].OriginX), Object.Variable(_PhysicsCarExtension.Forces[Index].OriginY)) - Object.Angle()"
+                              ]
+                            },
+                            {
+                              "type": {
+                                "value": "SetNumberVariable"
+                              },
+                              "parameters": [
+                                "DeltaLength",
+                                "=",
+                                "DistanceBetweenPositions(Object.CenterX(), Object.CenterY(), Object.Variable(_PhysicsCarExtension.Forces[Index].OriginX), Object.Variable(_PhysicsCarExtension.Forces[Index].OriginY))"
+                              ]
+                            },
+                            {
+                              "type": {
+                                "value": "SetNumberVariable"
+                              },
+                              "parameters": [
+                                "LineX",
+                                "=",
+                                "XFromAngleAndDistance(DeltaAngle, DeltaLength)"
+                              ]
+                            },
+                            {
+                              "type": {
+                                "value": "SetNumberVariable"
+                              },
+                              "parameters": [
+                                "LineY",
+                                "=",
+                                "YFromAngleAndDistance(DeltaAngle, DeltaLength)"
+                              ]
+                            },
+                            {
+                              "type": {
+                                "value": "SetNumberVariable"
+                              },
+                              "parameters": [
+                                "LineAngle",
+                                "=",
+                                "Object.Variable(_PhysicsCarExtension.Forces[Index].Angle) - Object.Angle()"
+                              ]
+                            },
+                            {
+                              "type": {
+                                "value": "SetNumberVariable"
+                              },
+                              "parameters": [
+                                "LineLength",
+                                "=",
+                                "Object.Variable(_PhysicsCarExtension.Forces[Index].Length)"
+                              ]
+                            },
+                            {
+                              "type": {
+                                "value": "PrimitiveDrawing::LineV2"
+                              },
+                              "parameters": [
+                                "ShapePainter",
+                                "LineX",
+                                "LineY",
+                                "LineX + XFromAngleAndDistance(LineAngle, LineLength)",
+                                "LineY + YFromAngleAndDistance(LineAngle, LineLength)",
+                                "2"
+                              ]
+                            }
+                          ],
+                          "variables": [
+                            {
+                              "folded": true,
+                              "name": "LineX",
+                              "type": "number",
+                              "value": 0
+                            },
+                            {
+                              "folded": true,
+                              "name": "LineY",
+                              "type": "number",
+                              "value": 0
+                            },
+                            {
+                              "folded": true,
+                              "name": "LineAngle",
+                              "type": "number",
+                              "value": 0
+                            },
+                            {
+                              "folded": true,
+                              "name": "LineLength",
+                              "type": "number",
+                              "value": 0
+                            },
+                            {
+                              "folded": true,
+                              "name": "DeltaAngle",
+                              "type": "number",
+                              "value": 0
+                            },
+                            {
+                              "folded": true,
+                              "name": "DeltaLength",
+                              "type": "number",
+                              "value": 0
+                            }
+                          ]
+                        },
+                        {
+                          "type": "BuiltinCommonInstructions::Standard",
+                          "conditions": [],
+                          "actions": [
+                            {
+                              "type": {
+                                "value": "SetNumberVariable"
+                              },
+                              "parameters": [
+                                "Index",
+                                "+",
+                                "1"
+                              ]
+                            }
+                          ]
+                        }
+                      ]
+                    }
+                  ],
+                  "variables": [
+                    {
+                      "name": "Index",
+                      "type": "number",
+                      "value": 0
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "PhysicsCar::PhysicsCar",
+                  "type": "behavior"
+                },
+                {
+                  "description": "Shape painter",
+                  "name": "ShapePainter",
+                  "supplementaryInformation": "PrimitiveDrawing::Drawer",
+                  "type": "objectList"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "the steering angle of the object.",
+              "fullName": "Steering angle",
+              "functionType": "ExpressionAndCondition",
+              "name": "SteeringAngle",
+              "sentence": "the steering angle",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SetReturnNumber"
+                      },
+                      "parameters": [
+                        "SteeringAngle"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "expressionType": {
+                "type": "expression"
+              },
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "PhysicsCar::PhysicsCar",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "fullName": "",
+              "functionType": "ActionWithOperator",
+              "getterName": "SteeringAngle",
+              "name": "SetSteeringAngle",
+              "sentence": "",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "PhysicsCar::PhysicsCar::SetPropertySteeringAngle"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "=",
+                        "Value"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "PhysicsCar::PhysicsCar",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "the acceleration of the object.",
+              "fullName": "Acceleration",
+              "functionType": "ExpressionAndCondition",
+              "group": "Physics car speed configuration",
+              "name": "Acceleration",
+              "sentence": "the acceleration",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SetReturnNumber"
+                      },
+                      "parameters": [
+                        "Acceleration"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "expressionType": {
+                "type": "expression"
+              },
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "PhysicsCar::PhysicsCar",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "fullName": "",
+              "functionType": "ActionWithOperator",
+              "getterName": "Acceleration",
+              "name": "SetAcceleration",
+              "sentence": "",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "PhysicsCar::PhysicsCar::SetPropertyAcceleration"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "=",
+                        "Value"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "PhysicsCar::PhysicsCar",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "the maximum speed of the object.",
+              "fullName": "Maximum speed",
+              "functionType": "ExpressionAndCondition",
+              "group": "Physics car speed configuration",
+              "name": "SpeedMax",
+              "sentence": "the maximum speed",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SetReturnNumber"
+                      },
+                      "parameters": [
+                        "SpeedMax"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "expressionType": {
+                "type": "expression"
+              },
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "PhysicsCar::PhysicsCar",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "fullName": "",
+              "functionType": "ActionWithOperator",
+              "getterName": "SpeedMax",
+              "name": "SetSpeedMax",
+              "sentence": "",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "PhysicsCar::PhysicsCar::SetPropertySpeedMax"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "=",
+                        "Value"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "PhysicsCar::PhysicsCar",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "the wheel grip ratio of the object (from 0 to 1). A ratio of 0 is like driving on ice.",
+              "fullName": "Wheel grip ratio",
+              "functionType": "ExpressionAndCondition",
+              "group": "Physics car steering configuration",
+              "name": "WheelGripRatio",
+              "sentence": "the wheel grip ratio",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SetReturnNumber"
+                      },
+                      "parameters": [
+                        "WheelGripRatio"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "expressionType": {
+                "type": "expression"
+              },
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "PhysicsCar::PhysicsCar",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "fullName": "",
+              "functionType": "ActionWithOperator",
+              "getterName": "WheelGripRatio",
+              "name": "SetWheelGripRatio",
+              "sentence": "",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "PhysicsCar::PhysicsCar::SetPropertyWheelGripRatio"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "=",
+                        "Value"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "PhysicsCar::PhysicsCar",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "the maximum steering angle of the object.",
+              "fullName": "Maximum steering angle",
+              "functionType": "ExpressionAndCondition",
+              "group": "Physics car steering configuration",
+              "name": "SteeringAngleMax",
+              "sentence": "the maximum steering angle",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SetReturnNumber"
+                      },
+                      "parameters": [
+                        "SteeringAngleMax"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "expressionType": {
+                "type": "expression"
+              },
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "PhysicsCar::PhysicsCar",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "fullName": "",
+              "functionType": "ActionWithOperator",
+              "getterName": "SteeringAngleMax",
+              "name": "SetSteeringAngleMax",
+              "sentence": "",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "PhysicsCar::PhysicsCar::SetPropertySteeringAngleMax"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "=",
+                        "Value"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "PhysicsCar::PhysicsCar",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "the steering speed of the object.",
+              "fullName": "Steering speed",
+              "functionType": "ExpressionAndCondition",
+              "group": "Physics car steering configuration",
+              "name": "SteeringSpeed",
+              "sentence": "the steering speed",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SetReturnNumber"
+                      },
+                      "parameters": [
+                        "SteeringSpeed"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "expressionType": {
+                "type": "expression"
+              },
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "PhysicsCar::PhysicsCar",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "fullName": "",
+              "functionType": "ActionWithOperator",
+              "getterName": "SteeringSpeed",
+              "name": "SetSteeringSpeed",
+              "sentence": "",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "PhysicsCar::PhysicsCar::SetPropertySteeringSpeed"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "=",
+                        "Value"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "PhysicsCar::PhysicsCar",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "the sterring speed when turning back of the object.",
+              "fullName": "Sterring back speed",
+              "functionType": "ExpressionAndCondition",
+              "group": "Physics car steering configuration",
+              "name": "SteeringBackSpeed",
+              "sentence": "the sterring speed when turning back",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SetReturnNumber"
+                      },
+                      "parameters": [
+                        "SteeringBackSpeed"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "expressionType": {
+                "type": "expression"
+              },
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "PhysicsCar::PhysicsCar",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "fullName": "",
+              "functionType": "ActionWithOperator",
+              "getterName": "SteeringBackSpeed",
+              "name": "SetSteeringBackSpeed",
+              "sentence": "",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "PhysicsCar::PhysicsCar::SetPropertySteeringBackSpeed"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "=",
+                        "Value"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "PhysicsCar::PhysicsCar",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            }
+          ],
+          "propertyDescriptors": [
+            {
+              "value": "",
+              "type": "Behavior",
+              "label": "Physics Engine 2.0",
+              "description": "",
+              "group": "",
+              "extraInformation": [
+                "Physics2::Physics2Behavior"
+              ],
+              "name": "Physics2"
+            },
+            {
+              "value": "500",
+              "type": "Number",
+              "unit": "PixelAcceleration",
+              "label": "Acceleration",
+              "description": "",
+              "group": "Speed",
+              "extraInformation": [],
+              "name": "Acceleration"
+            },
+            {
+              "value": "800",
+              "type": "Number",
+              "unit": "PixelSpeed",
+              "label": "Maximum speed",
+              "description": "",
+              "group": "Speed",
+              "extraInformation": [],
+              "name": "SpeedMax"
+            },
+            {
+              "value": "0.5",
+              "type": "Number",
+              "unit": "Dimensionless",
+              "label": "Wheel grip ratio (from 0 to 1)",
+              "description": "A ratio of 0 is like driving on ice.",
+              "group": "Steering",
+              "extraInformation": [],
+              "name": "WheelGripRatio"
+            },
+            {
+              "value": "50",
+              "type": "Number",
+              "unit": "AngularSpeed",
+              "label": "Steering speed",
+              "description": "",
+              "group": "Steering",
+              "extraInformation": [],
+              "name": "SteeringSpeed"
+            },
+            {
+              "value": "240",
+              "type": "Number",
+              "unit": "AngularSpeed",
+              "label": "Sterring speed when turning back",
+              "description": "",
+              "group": "Steering",
+              "extraInformation": [],
+              "name": "SteeringBackSpeed"
+            },
+            {
+              "value": "30",
+              "type": "Number",
+              "unit": "DegreeAngle",
+              "label": "Maximum steering angle",
+              "description": "",
+              "group": "Steering",
+              "extraInformation": [],
+              "name": "SteeringAngleMax"
+            },
+            {
+              "value": "0",
+              "type": "Number",
+              "unit": "DegreeAngle",
+              "label": "Steering angle",
+              "description": "",
+              "group": "Steering",
+              "extraInformation": [],
+              "hidden": true,
+              "name": "SteeringAngle"
+            },
+            {
+              "value": "",
+              "type": "Boolean",
+              "label": "",
+              "description": "",
+              "group": "",
+              "extraInformation": [],
+              "hidden": true,
+              "name": "IsRightPressed"
+            },
+            {
+              "value": "",
+              "type": "Boolean",
+              "label": "",
+              "description": "",
+              "group": "",
+              "extraInformation": [],
+              "hidden": true,
+              "name": "IsLeftPressed"
+            },
+            {
+              "value": "",
+              "type": "Boolean",
+              "label": "",
+              "description": "",
+              "group": "",
+              "extraInformation": [],
+              "hidden": true,
+              "name": "IsUpPressed"
+            },
+            {
+              "value": "",
+              "type": "Boolean",
+              "label": "",
+              "description": "",
+              "group": "",
+              "extraInformation": [],
+              "hidden": true,
+              "name": "IsDownPressed"
+            },
+            {
+              "value": "0.8",
+              "type": "Number",
+              "label": "Front wheels position",
+              "description": "0 means at the center, 1 means at the front",
+              "group": "Wheels",
+              "extraInformation": [],
+              "advanced": true,
+              "name": "FrontWheelsPosition"
+            },
+            {
+              "value": "0.8",
+              "type": "Number",
+              "unit": "Dimensionless",
+              "label": "Rear wheels position",
+              "description": "0 means at the center, 1 means at the back",
+              "group": "Wheels",
+              "extraInformation": [],
+              "advanced": true,
+              "name": "RearWheelsPosition"
+            },
+            {
+              "value": "0",
+              "type": "Number",
+              "unit": "Dimensionless",
+              "label": "",
+              "description": "",
+              "group": "",
+              "extraInformation": [],
+              "hidden": true,
+              "name": "AccelerationStickValue"
+            },
+            {
+              "value": "0",
+              "type": "Number",
+              "unit": "Dimensionless",
+              "label": "",
+              "description": "",
+              "group": "",
+              "extraInformation": [],
+              "hidden": true,
+              "name": "SteeringStickValue"
+            },
+            {
+              "value": "",
+              "type": "Boolean",
+              "label": "",
+              "description": "",
+              "group": "",
+              "extraInformation": [],
+              "hidden": true,
+              "name": "IsDebug"
+            }
+          ],
+          "sharedPropertyDescriptors": []
+        }
+      ],
+      "eventsBasedObjects": []
+    },
+    {
+      "author": "",
+      "category": "",
+      "extensionNamespace": "",
+      "fullName": "",
+      "helpPath": "",
+      "iconUrl": "",
+      "name": "AdvancedWheelGrip",
+      "previewIconUrl": "",
+      "shortDescription": "",
+      "version": "",
+      "description": "",
+      "tags": [],
+      "authorIds": [],
+      "dependencies": [],
+      "globalVariables": [],
+      "sceneVariables": [],
+      "eventsFunctions": [],
+      "eventsBasedBehaviors": [
+        {
+          "description": "Simulate tire heat and surface changes.",
+          "fullName": "Advanced wheel grip",
+          "name": "AdvancedWheelGrip",
+          "objectType": "",
+          "quickCustomizationVisibility": "hidden",
+          "eventsFunctions": [
+            {
+              "fullName": "",
+              "functionType": "Action",
+              "name": "doStepPreEvents",
+              "sentence": "",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Comment",
+                  "color": {
+                    "b": 109,
+                    "g": 230,
+                    "r": 255,
+                    "textB": 0,
+                    "textG": 0,
+                    "textR": 0
+                  },
+                  "comment": "Save the initial propertie values to be able to set back right values whensitching of surface.\nThis is not done in onCreated because every required behaviors may not be initialized before this one."
+                },
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [
+                    {
+                      "type": {
+                        "value": "AdvancedWheelGrip::AdvancedWheelGrip::PropertySpeedMax"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "=",
+                        "0"
+                      ]
+                    }
+                  ],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "AdvancedWheelGrip::AdvancedWheelGrip::SetPropertySpeedMax"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "=",
+                        "Object.PhysicsCar::SpeedMax()"
+                      ]
+                    },
+                    {
+                      "type": {
+                        "value": "AdvancedWheelGrip::AdvancedWheelGrip::SetPropertyLinearDampling"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "=",
+                        "Object.Physics2::LinearDamping()"
+                      ]
+                    }
+                  ]
+                },
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "PhysicsCar::PhysicsCar::SetWheelGripRatio"
+                      },
+                      "parameters": [
+                        "Object",
+                        "PhysicsCar",
+                        "=",
+                        "GripFactor * Tween::Ease(\"easeInOutCubic\", GripRatioMax, GripRatioMin, abs(sin(ToRad(AngleDifference(Object.Physics2::LinearVelocityAngle(), Object.Angle() + Object.PhysicsCar::SteeringAngle())))))",
+                        ""
+                      ]
+                    },
+                    {
+                      "type": {
+                        "value": "ShakeObject3D::ShakeModel3D::SetFrequency"
+                      },
+                      "parameters": [
+                        "Object",
+                        "ShakeModel3D",
+                        "=",
+                        "0.01 * Object.Physics2::LinearVelocity()",
+                        ""
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "AdvancedWheelGrip::AdvancedWheelGrip",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "fullName": "Adapt grip to surface",
+              "functionType": "Action",
+              "name": "SetSurface",
+              "sentence": "Adapt _PARAM0_ grip to _PARAM2_",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [
+                    {
+                      "type": {
+                        "value": "CompareArgumentAsString"
+                      },
+                      "parameters": [
+                        "\"Value\"",
+                        "!=",
+                        "Surface"
+                      ]
+                    }
+                  ],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "AdvancedWheelGrip::AdvancedWheelGrip::SetPropertySurface"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "=",
+                        "Value"
+                      ]
+                    }
+                  ],
+                  "events": [
+                    {
+                      "type": "BuiltinCommonInstructions::Standard",
+                      "conditions": [
+                        {
+                          "type": {
+                            "value": "CompareArgumentAsString"
+                          },
+                          "parameters": [
+                            "\"Value\"",
+                            "=",
+                            "\"Asphalt\""
+                          ]
+                        }
+                      ],
+                      "actions": [
+                        {
+                          "type": {
+                            "value": "AdvancedWheelGrip::AdvancedWheelGrip::SetPropertyGripFactor"
+                          },
+                          "parameters": [
+                            "Object",
+                            "Behavior",
+                            "=",
+                            "1"
+                          ]
+                        },
+                        {
+                          "type": {
+                            "value": "Physics2::LinearDamping"
+                          },
+                          "parameters": [
+                            "Object",
+                            "Physics2",
+                            "=",
+                            "LinearDampling"
+                          ]
+                        },
+                        {
+                          "type": {
+                            "value": "PhysicsCar::PhysicsCar::SetSpeedMax"
+                          },
+                          "parameters": [
+                            "Object",
+                            "PhysicsCar",
+                            "=",
+                            "SpeedMax",
+                            ""
+                          ]
+                        },
+                        {
+                          "type": {
+                            "value": "ShakeObject3D::ShakeModel3D::StopShaking"
+                          },
+                          "parameters": [
+                            "Object",
+                            "ShakeModel3D",
+                            "0.5",
+                            ""
+                          ]
+                        }
+                      ]
+                    },
+                    {
+                      "type": "BuiltinCommonInstructions::Standard",
+                      "conditions": [
+                        {
+                          "type": {
+                            "value": "CompareArgumentAsString"
+                          },
+                          "parameters": [
+                            "\"Value\"",
+                            "=",
+                            "\"Grass\""
+                          ]
+                        }
+                      ],
+                      "actions": [
+                        {
+                          "type": {
+                            "value": "AdvancedWheelGrip::AdvancedWheelGrip::SetPropertyGripFactor"
+                          },
+                          "parameters": [
+                            "Object",
+                            "Behavior",
+                            "=",
+                            "GrassGripRatioFactor"
+                          ]
+                        },
+                        {
+                          "type": {
+                            "value": "Physics2::LinearDamping"
+                          },
+                          "parameters": [
+                            "Object",
+                            "Physics2",
+                            "=",
+                            "LinearDampling * GrassLinearDamplingFactor"
+                          ]
+                        },
+                        {
+                          "type": {
+                            "value": "PhysicsCar::PhysicsCar::SetSpeedMax"
+                          },
+                          "parameters": [
+                            "Object",
+                            "PhysicsCar",
+                            "=",
+                            "SpeedMax * GrassSpeedMaxFactor",
+                            ""
+                          ]
+                        },
+                        {
+                          "type": {
+                            "value": "ShakeObject3D::ShakeModel3D::StartShaking"
+                          },
+                          "parameters": [
+                            "Object",
+                            "ShakeModel3D",
+                            "0",
+                            ""
+                          ]
+                        }
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "AdvancedWheelGrip::AdvancedWheelGrip",
+                  "type": "behavior"
+                },
+                {
+                  "description": "Surface",
+                  "name": "Value",
+                  "supplementaryInformation": "[\"Asphalt\",\"Grass\"]",
+                  "type": "stringWithSelector"
+                }
+              ],
+              "objectGroups": []
+            }
+          ],
+          "propertyDescriptors": [
+            {
+              "value": "",
+              "type": "Behavior",
+              "label": "Physics car",
+              "description": "",
+              "group": "",
+              "extraInformation": [
+                "PhysicsCar::PhysicsCar"
+              ],
+              "name": "PhysicsCar"
+            },
+            {
+              "value": "",
+              "type": "Behavior",
+              "label": "Physics Engine 2.0",
+              "description": "",
+              "group": "",
+              "extraInformation": [
+                "Physics2::Physics2Behavior"
+              ],
+              "name": "Physics2"
+            },
+            {
+              "value": "",
+              "type": "Behavior",
+              "label": "3D shake",
+              "description": "",
+              "group": "",
+              "extraInformation": [
+                "ShakeObject3D::ShakeModel3D"
+              ],
+              "name": "ShakeModel3D"
+            },
+            {
+              "value": "0.075",
+              "type": "Number",
+              "unit": "Dimensionless",
+              "label": "Grip ratio at maximum speed",
+              "description": "",
+              "group": "Asphalt",
+              "extraInformation": [],
+              "name": "GripRatioMin"
+            },
+            {
+              "value": "0.5",
+              "type": "Number",
+              "unit": "Dimensionless",
+              "label": "Grip ratio at low speed",
+              "description": "",
+              "group": "Asphalt",
+              "extraInformation": [],
+              "name": "GripRatioMax"
+            },
+            {
+              "value": "0.5",
+              "type": "Number",
+              "unit": "Dimensionless",
+              "label": "Maximum speed factor on grass",
+              "description": "",
+              "group": "Grass",
+              "extraInformation": [],
+              "name": "GrassSpeedMaxFactor"
+            },
+            {
+              "value": "0.25",
+              "type": "Number",
+              "unit": "Dimensionless",
+              "label": "Grip ratio factor on grass",
+              "description": "",
+              "group": "Grass",
+              "extraInformation": [],
+              "name": "GrassGripRatioFactor"
+            },
+            {
+              "value": "5",
+              "type": "Number",
+              "unit": "Dimensionless",
+              "label": "Grip linear dampling factor on grass",
+              "description": "",
+              "group": "Grass",
+              "extraInformation": [],
+              "name": "GrassLinearDamplingFactor"
+            },
+            {
+              "value": "Asphalt",
+              "type": "Choice",
+              "label": "",
+              "description": "",
+              "group": "",
+              "extraInformation": [
+                "Asphalt",
+                "Grass"
+              ],
+              "hidden": true,
+              "name": "Surface"
+            },
+            {
+              "value": "1",
+              "type": "Number",
+              "unit": "Dimensionless",
+              "label": "",
+              "description": "",
+              "group": "",
+              "extraInformation": [],
+              "hidden": true,
+              "name": "GripFactor"
+            },
+            {
+              "value": "0",
+              "type": "Number",
+              "unit": "PixelSpeed",
+              "label": "",
+              "description": "",
+              "group": "",
+              "extraInformation": [],
+              "hidden": true,
+              "name": "SpeedMax"
+            },
+            {
+              "value": "0",
+              "type": "Number",
+              "label": "",
+              "description": "",
+              "group": "",
+              "extraInformation": [],
+              "hidden": true,
+              "name": "LinearDampling"
+            }
+          ],
+          "sharedPropertyDescriptors": []
+        }
+      ],
+      "eventsBasedObjects": []
+    },
+    {
+      "author": "",
+      "category": "",
+      "extensionNamespace": "",
+      "fullName": "",
+      "helpPath": "",
+      "iconUrl": "",
+      "name": "Collectible",
+      "previewIconUrl": "",
+      "shortDescription": "",
+      "version": "",
+      "description": "",
+      "tags": [],
+      "authorIds": [],
+      "dependencies": [],
+      "globalVariables": [],
+      "sceneVariables": [
+        {
+          "folded": true,
+          "name": "RisingPitch",
+          "type": "number",
+          "value": 0.8
+        }
+      ],
+      "eventsFunctions": [
+        {
+          "fullName": "",
+          "functionType": "Action",
+          "name": "onScenePreEvents",
+          "sentence": "",
+          "events": [
+            {
+              "type": "BuiltinCommonInstructions::Comment",
+              "color": {
+                "b": 109,
+                "g": 230,
+                "r": 255,
+                "textB": 0,
+                "textG": 0,
+                "textR": 0
+              },
+              "comment": "Reset rising pitch"
+            },
+            {
+              "type": "BuiltinCommonInstructions::Standard",
+              "conditions": [
+                {
+                  "type": {
+                    "value": "CompareTimer"
+                  },
+                  "parameters": [
+                    "",
+                    "\"RisingPitch\"",
+                    ">",
+                    "1"
+                  ]
+                }
+              ],
+              "actions": [
+                {
+                  "type": {
+                    "value": "SetNumberVariable"
+                  },
+                  "parameters": [
+                    "RisingPitch",
+                    "=",
+                    "RandomFloatInRange(0.9,1.1)"
+                  ]
+                },
+                {
+                  "type": {
+                    "value": "RemoveTimer"
+                  },
+                  "parameters": [
+                    "",
+                    "\"RisingPitch\""
+                  ]
+                }
+              ]
+            }
+          ],
+          "parameters": [],
+          "objectGroups": []
+        }
+      ],
+      "eventsBasedBehaviors": [
+        {
+          "description": "Rotate continuously and make a sound when deleted.",
+          "fullName": "Collectible",
+          "name": "Collectible",
+          "objectType": "Scene3D::Model3DObject",
+          "eventsFunctions": [
+            {
+              "fullName": "",
+              "functionType": "Action",
+              "name": "onDestroy",
+              "sentence": "",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Comment",
+                  "color": {
+                    "b": 109,
+                    "g": 230,
+                    "r": 255,
+                    "textB": 0,
+                    "textG": 0,
+                    "textR": 0
+                  },
+                  "comment": "Reseting the rising pitch variable after being adjusted in the coin collision event."
+                },
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "PlaySound"
+                      },
+                      "parameters": [
+                        "",
+                        "CoinPickUp",
+                        "",
+                        "25",
+                        "RisingPitch"
+                      ]
+                    },
+                    {
+                      "type": {
+                        "value": "SetNumberVariable"
+                      },
+                      "parameters": [
+                        "RisingPitch",
+                        "*",
+                        "1.05"
+                      ]
+                    },
+                    {
+                      "type": {
+                        "value": "ResetTimer"
+                      },
+                      "parameters": [
+                        "",
+                        "\"RisingPitch\""
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "supplementaryInformation": "Scene3D::Model3DObject",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "Collectible::Collectible",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "fullName": "",
+              "functionType": "Action",
+              "name": "doStepPreEvents",
+              "sentence": "",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Comment",
+                  "color": {
+                    "b": 109,
+                    "g": 230,
+                    "r": 255,
+                    "textB": 0,
+                    "textG": 0,
+                    "textR": 0
+                  },
+                  "comment": "Rotating the coins in game."
+                },
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "Rotate"
+                      },
+                      "parameters": [
+                        "Object",
+                        "RotationSpeed",
+                        ""
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "supplementaryInformation": "Scene3D::Model3DObject",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "Collectible::Collectible",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            }
+          ],
+          "propertyDescriptors": [
+            {
+              "value": "50",
+              "type": "Number",
+              "unit": "AngularSpeed",
+              "label": "Rotation speed",
+              "description": "",
+              "group": "",
+              "extraInformation": [],
+              "name": "RotationSpeed"
+            }
+          ],
+          "sharedPropertyDescriptors": []
+        }
+      ],
+      "eventsBasedObjects": []
+    },
+    {
+      "author": "",
+      "category": "User interface",
+      "extensionNamespace": "",
+      "fullName": "",
+      "helpPath": "",
+      "iconUrl": "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz48IURPQ1RZUEUgc3ZnIFBVQkxJQyAiLS8vVzNDLy9EVEQgU1ZHIDEuMS8vRU4iICJodHRwOi8vd3d3LnczLm9yZy9HcmFwaGljcy9TVkcvMS4xL0RURC9zdmcxMS5kdGQiPjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgdmVyc2lvbj0iMS4xIiBpZD0ibWRpLXRyb3BoeSIgd2lkdGg9IjI0IiBoZWlnaHQ9IjI0IiB2aWV3Qm94PSIwIDAgMjQgMjQiPjxwYXRoIGQ9Ik0xOCAyQzE3LjEgMiAxNiAzIDE2IDRIOEM4IDMgNi45IDIgNiAySDJWMTFDMiAxMiAzIDEzIDQgMTNINi4yQzYuNiAxNSA3LjkgMTYuNyAxMSAxN1YxOS4wOEM4IDE5LjU0IDggMjIgOCAyMkgxNkMxNiAyMiAxNiAxOS41NCAxMyAxOS4wOFYxN0MxNi4xIDE2LjcgMTcuNCAxNSAxNy44IDEzSDIwQzIxIDEzIDIyIDEyIDIyIDExVjJIMThNNiAxMUg0VjRINlYxMU0yMCAxMUgxOFY0SDIwVjExWiIgLz48L3N2Zz4=",
+      "name": "LeaderboardDialog",
+      "previewIconUrl": "https://asset-resources.gdevelop.io/public-resources/Icons/4b89b420c0ed9c540a7f00c5735a31af0db2160679d3fab2170df3681c3ac38c_trophy.svg",
+      "shortDescription": "",
+      "version": "",
+      "description": "",
+      "tags": [],
+      "authorIds": [],
+      "dependencies": [],
+      "globalVariables": [],
+      "sceneVariables": [],
+      "eventsFunctions": [],
+      "eventsBasedBehaviors": [],
+      "eventsBasedObjects": [
+        {
+          "areaMaxX": 688,
+          "areaMaxY": 480,
+          "areaMaxZ": 48,
+          "areaMinX": 0,
+          "areaMinY": 0,
+          "areaMinZ": 0,
+          "defaultName": "LeaderboardDialog",
+          "description": "Displays the player score and allows to submit it to a leaderboard.",
+          "fullName": "Leaderboard dialog",
+          "isInnerAreaFollowingParentSize": true,
+          "isUsingLegacyInstancesRenderer": false,
+          "name": "LeaderboardDialog",
+          "eventsFunctions": [
+            {
+              "fullName": "",
+              "functionType": "Action",
+              "name": "onCreated",
+              "sentence": "",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "LeaderboardDialog::LeaderboardDialog::SetScore"
+                      },
+                      "parameters": [
+                        "Object",
+                        "=",
+                        "0",
+                        ""
+                      ]
+                    },
+                    {
+                      "type": {
+                        "value": "LeaderboardDialog::LeaderboardDialog::SetDefaultPlayerName"
+                      },
+                      "parameters": [
+                        "Object",
+                        "=",
+                        "DefaultPlayerName",
+                        ""
+                      ]
+                    },
+                    {
+                      "type": {
+                        "value": "PlayerAuthentication::DisplayAuthenticationBanner"
+                      },
+                      "parameters": [
+                        ""
+                      ]
+                    }
+                  ]
+                },
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [
+                    {
+                      "type": {
+                        "value": "PlayerAuthentication::IsPlayerAuthenticated"
+                      },
+                      "parameters": []
+                    }
+                  ],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "TextContainerCapability::TextContainerBehavior::SetValue"
+                      },
+                      "parameters": [
+                        "PlayerNameInput",
+                        "Text",
+                        "=",
+                        "PlayerAuthentication::Username()"
+                      ]
+                    },
+                    {
+                      "type": {
+                        "value": "TextInput::TextInputObject::SetDisabled"
+                      },
+                      "parameters": [
+                        "PlayerNameInput",
+                        "yes"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "supplementaryInformation": "LeaderboardDialog::LeaderboardDialog",
+                  "type": "object"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "fullName": "",
+              "functionType": "Action",
+              "name": "doStepPostEvents",
+              "sentence": "",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [
+                    {
+                      "type": {
+                        "value": "PlayerAuthentication::HasPlayerLoggedIn"
+                      },
+                      "parameters": []
+                    }
+                  ],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "TextContainerCapability::TextContainerBehavior::SetValue"
+                      },
+                      "parameters": [
+                        "PlayerNameInput",
+                        "Text",
+                        "=",
+                        "PlayerAuthentication::Username()"
+                      ]
+                    },
+                    {
+                      "type": {
+                        "value": "TextInput::TextInputObject::SetDisabled"
+                      },
+                      "parameters": [
+                        "PlayerNameInput",
+                        "yes"
+                      ]
+                    },
+                    {
+                      "type": {
+                        "value": "PlayerAuthentication::DisplayAuthenticationBanner"
+                      },
+                      "parameters": [
+                        ""
+                      ]
+                    }
+                  ]
+                },
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [
+                    {
+                      "type": {
+                        "value": "Leaderboards::IsLeaderboardViewErrored"
+                      },
+                      "parameters": []
+                    }
+                  ],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "PlayerAuthentication::DisplayAuthenticationBanner"
+                      },
+                      "parameters": [
+                        ""
+                      ]
+                    }
+                  ]
+                },
+                {
+                  "type": "BuiltinCommonInstructions::Comment",
+                  "color": {
+                    "b": 109,
+                    "g": 230,
+                    "r": 255,
+                    "textB": 0,
+                    "textG": 0,
+                    "textR": 0
+                  },
+                  "comment": "Handle leaderboards.\nTo create a leaderboard, make sure your game is registered in Home > Profile > Games Dashboard and then, click on \"Manage game\" > Leaderboards. When a leaderboard is created, it should be available in the actions."
+                },
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [
+                    {
+                      "type": {
+                        "value": "PanelSpriteButton::PanelSpriteButton::IsClicked"
+                      },
+                      "parameters": [
+                        "SubmitButton",
+                        ""
+                      ]
+                    }
+                  ],
+                  "actions": [],
+                  "events": [
+                    {
+                      "type": "BuiltinCommonInstructions::Standard",
+                      "conditions": [
+                        {
+                          "type": {
+                            "value": "PlayerAuthentication::IsPlayerAuthenticated"
+                          },
+                          "parameters": []
+                        }
+                      ],
+                      "actions": [
+                        {
+                          "type": {
+                            "value": "Leaderboards::SaveConnectedPlayerScore"
+                          },
+                          "parameters": [
+                            "",
+                            "LeaderboardId",
+                            "Score"
+                          ]
+                        }
+                      ]
+                    },
+                    {
+                      "type": "BuiltinCommonInstructions::Standard",
+                      "conditions": [
+                        {
+                          "type": {
+                            "inverted": true,
+                            "value": "PlayerAuthentication::IsPlayerAuthenticated"
+                          },
+                          "parameters": []
+                        }
+                      ],
+                      "actions": [
+                        {
+                          "type": {
+                            "value": "Leaderboards::SavePlayerScore"
+                          },
+                          "parameters": [
+                            "",
+                            "LeaderboardId",
+                            "Score",
+                            "PlayerNameInput.Text()"
+                          ]
+                        }
+                      ]
+                    },
+                    {
+                      "type": "BuiltinCommonInstructions::Standard",
+                      "conditions": [],
+                      "actions": [
+                        {
+                          "type": {
+                            "value": "PlayerAuthentication::HideAuthenticationBanner"
+                          },
+                          "parameters": [
+                            ""
+                          ]
+                        },
+                        {
+                          "type": {
+                            "value": "Leaderboards::DisplayLeaderboard"
+                          },
+                          "parameters": [
+                            "",
+                            "LeaderboardId",
+                            "yes"
+                          ]
+                        }
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "supplementaryInformation": "LeaderboardDialog::LeaderboardDialog",
+                  "type": "object"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "the score.",
+              "fullName": "Score",
+              "functionType": "ExpressionAndCondition",
+              "name": "Score",
+              "sentence": "the score",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SetReturnNumber"
+                      },
+                      "parameters": [
+                        "Score"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "expressionType": {
+                "type": "expression"
+              },
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "supplementaryInformation": "LeaderboardDialog::LeaderboardDialog",
+                  "type": "object"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "fullName": "",
+              "functionType": "ActionWithOperator",
+              "getterName": "Score",
+              "name": "SetScore",
+              "sentence": "",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "LeaderboardDialog::LeaderboardDialog::SetPropertyScore"
+                      },
+                      "parameters": [
+                        "Object",
+                        "=",
+                        "Value"
+                      ]
+                    },
+                    {
+                      "type": {
+                        "value": "TextContainerCapability::TextContainerBehavior::SetValue"
+                      },
+                      "parameters": [
+                        "ScoreLabel",
+                        "Text",
+                        "=",
+                        "\"Time: \" + TimeFormatter::SecondsToHHMMSS000(Score)"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "supplementaryInformation": "LeaderboardDialog::LeaderboardDialog",
+                  "type": "object"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "the default player name.",
+              "fullName": "Default player name",
+              "functionType": "ExpressionAndCondition",
+              "name": "DefaultPlayerName",
+              "sentence": "the default player name",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SetReturnString"
+                      },
+                      "parameters": [
+                        "DefaultPlayerName"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "expressionType": {
+                "type": "string"
+              },
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "supplementaryInformation": "LeaderboardDialog::LeaderboardDialog",
+                  "type": "object"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "fullName": "",
+              "functionType": "ActionWithOperator",
+              "getterName": "DefaultPlayerName",
+              "name": "SetDefaultPlayerName",
+              "sentence": "",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "LeaderboardDialog::LeaderboardDialog::SetPropertyDefaultPlayerName"
+                      },
+                      "parameters": [
+                        "Object",
+                        "=",
+                        "Value"
+                      ]
+                    }
+                  ]
+                },
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [
+                    {
+                      "type": {
+                        "inverted": true,
+                        "value": "PlayerAuthentication::IsPlayerAuthenticated"
+                      },
+                      "parameters": []
+                    },
+                    {
+                      "type": {
+                        "value": "LeaderboardDialog::LeaderboardDialog::PropertyDefaultPlayerName"
+                      },
+                      "parameters": [
+                        "Object",
+                        "!=",
+                        "\"0\""
+                      ]
+                    }
+                  ],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "TextContainerCapability::TextContainerBehavior::SetValue"
+                      },
+                      "parameters": [
+                        "PlayerNameInput",
+                        "Text",
+                        "=",
+                        "DefaultPlayerName"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "supplementaryInformation": "LeaderboardDialog::LeaderboardDialog",
+                  "type": "object"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "the player name.",
+              "fullName": "Player name",
+              "functionType": "ExpressionAndCondition",
+              "name": "PlayerName",
+              "sentence": "the player name",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SetReturnString"
+                      },
+                      "parameters": [
+                        "PlayerNameInput.Text::Value()"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "expressionType": {
+                "type": "string"
+              },
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "supplementaryInformation": "LeaderboardDialog::LeaderboardDialog",
+                  "type": "object"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "Check if the restart button of the dialog is clicked.",
+              "fullName": "Restart button clicked",
+              "functionType": "Condition",
+              "name": "IsRestartClicked",
+              "sentence": "Restart button of _PARAM0_ is clicked",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [
+                    {
+                      "type": {
+                        "value": "PanelSpriteButton::PanelSpriteButton::IsClicked"
+                      },
+                      "parameters": [
+                        "RestartButton",
+                        ""
+                      ]
+                    }
+                  ],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "PlayerAuthentication::HideAuthenticationBanner"
+                      },
+                      "parameters": [
+                        ""
+                      ]
+                    },
+                    {
+                      "type": {
+                        "value": "SetReturnBoolean"
+                      },
+                      "parameters": [
+                        "True"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "supplementaryInformation": "LeaderboardDialog::LeaderboardDialog",
+                  "type": "object"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "Check if the score has been sucessfully submitted by the dialog.",
+              "fullName": "Score is submitted",
+              "functionType": "Condition",
+              "name": "IsScoreSubmitted",
+              "sentence": "_PARAM0_ submitted a score",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Comment",
+                  "color": {
+                    "b": 109,
+                    "g": 230,
+                    "r": 255,
+                    "textB": 0,
+                    "textG": 0,
+                    "textR": 0
+                  },
+                  "comment": "Allow to try and submit again in case of error."
+                },
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [
+                    {
+                      "type": {
+                        "value": "Leaderboards::HasPlayerJustClosedLeaderboardView"
+                      },
+                      "parameters": []
+                    },
+                    {
+                      "type": {
+                        "value": "Leaderboards::HasLastSaveSucceeded"
+                      },
+                      "parameters": [
+                        "LeaderboardId"
+                      ]
+                    }
+                  ],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SetReturnBoolean"
+                      },
+                      "parameters": [
+                        "True"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "supplementaryInformation": "LeaderboardDialog::LeaderboardDialog",
+                  "type": "object"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "the leaderboard of the object.",
+              "fullName": "Leaderboard",
+              "functionType": "ExpressionAndCondition",
+              "name": "LeaderboardId",
+              "sentence": "the leaderboard",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SetReturnString"
+                      },
+                      "parameters": [
+                        "LeaderboardId"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "expressionType": {
+                "type": "leaderboardId"
+              },
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "supplementaryInformation": "LeaderboardDialog::LeaderboardDialog",
+                  "type": "object"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "fullName": "",
+              "functionType": "ActionWithOperator",
+              "getterName": "LeaderboardId",
+              "name": "SetLeaderboardId",
+              "sentence": "",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "LeaderboardDialog::LeaderboardDialog::SetPropertyLeaderboardId"
+                      },
+                      "parameters": [
+                        "Object",
+                        "=",
+                        "Value"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "supplementaryInformation": "LeaderboardDialog::LeaderboardDialog",
+                  "type": "object"
+                }
+              ],
+              "objectGroups": []
+            }
+          ],
+          "propertyDescriptors": [
+            {
+              "value": "",
+              "type": "String",
+              "label": "Default player name",
+              "description": "",
+              "group": "",
+              "extraInformation": [],
+              "name": "DefaultPlayerName"
+            },
+            {
+              "value": "",
+              "type": "LeaderboardId",
+              "label": "Leaderboard",
+              "description": "",
+              "group": "",
+              "extraInformation": [],
+              "name": "LeaderboardId"
+            },
+            {
+              "value": "0",
+              "type": "Number",
+              "unit": "Dimensionless",
+              "label": "",
+              "description": "",
+              "group": "",
+              "extraInformation": [],
+              "hidden": true,
+              "name": "Score"
+            }
+          ],
+          "objects": [
+            {
+              "assetStoreId": "",
+              "bold": false,
+              "italic": false,
+              "name": "ScoreLabel",
+              "smoothed": true,
+              "type": "TextObject::Text",
+              "underlined": false,
+              "variables": [],
+              "effects": [],
+              "behaviors": [],
+              "string": "Time:",
+              "font": "27d4da0f7767cf3fbf14eb2c8da758dbcfc7b5038c9214d5e6ed62db6476a6e5_Chango-Regular.ttf",
+              "textAlignment": "",
+              "characterSize": 40,
+              "color": {
+                "b": 79,
+                "g": 72,
+                "r": 72
+              },
+              "content": {
+                "bold": false,
+                "isOutlineEnabled": false,
+                "isShadowEnabled": false,
+                "italic": false,
+                "outlineColor": "255;255;255",
+                "outlineThickness": 2,
+                "shadowAngle": 90,
+                "shadowBlurRadius": 2,
+                "shadowColor": "0;0;0",
+                "shadowDistance": 4,
+                "shadowOpacity": 127,
+                "smoothed": true,
+                "underlined": false,
+                "text": "Time:",
+                "font": "27d4da0f7767cf3fbf14eb2c8da758dbcfc7b5038c9214d5e6ed62db6476a6e5_Chango-Regular.ttf",
+                "textAlignment": "",
+                "verticalTextAlignment": "top",
+                "characterSize": 40,
+                "color": "72;72;79"
+              }
+            },
+            {
+              "assetStoreId": "",
+              "name": "PlayerNameInput",
+              "type": "TextInput::TextInputObject",
+              "variables": [],
+              "effects": [],
+              "behaviors": [
+                {
+                  "name": "Anchor",
+                  "type": "AnchorBehavior::AnchorBehavior",
+                  "bottomEdgeAnchor": 0,
+                  "leftEdgeAnchor": 1,
+                  "relativeToOriginalWindowSize": true,
+                  "rightEdgeAnchor": 2,
+                  "topEdgeAnchor": 0,
+                  "useLegacyBottomAndRightAnchors": false
+                }
+              ],
+              "content": {
+                "initialValue": "",
+                "placeholder": "PlayerName",
+                "fontResourceName": "27d4da0f7767cf3fbf14eb2c8da758dbcfc7b5038c9214d5e6ed62db6476a6e5_Chango-Regular.ttf",
+                "fontSize": 40,
+                "inputType": "text",
+                "textColor": "72;72;79",
+                "fillColor": "231;232;243",
+                "fillOpacity": 255,
+                "borderColor": "255;255;255",
+                "borderOpacity": 255,
+                "borderWidth": 6,
+                "readOnly": false,
+                "disabled": false
+              }
+            },
+            {
+              "assetStoreId": "bfab3a269992cf43081e2cb8053c28aaef11f2f9e0d0cbdc00207ea271f69c2c",
+              "name": "SubmitButton",
+              "type": "PanelSpriteButton::PanelSpriteButton",
+              "variables": [],
+              "effects": [],
+              "behaviors": [
+                {
+                  "name": "Anchor",
+                  "type": "AnchorBehavior::AnchorBehavior",
+                  "bottomEdgeAnchor": 0,
+                  "leftEdgeAnchor": 0,
+                  "relativeToOriginalWindowSize": true,
+                  "rightEdgeAnchor": 2,
+                  "topEdgeAnchor": 0,
+                  "useLegacyBottomAndRightAnchors": false
+                }
+              ],
+              "content": {
+                "LeftPadding": 16,
+                "RightPadding": 16,
+                "PressedLabelOffsetY": 10,
+                "BottomPadding": 32,
+                "TopPadding": 16,
+                "HoveredFadeOutDuration": 0.25
+              },
+              "childrenContent": {
+                "Hovered": {
+                  "bottomMargin": 32,
+                  "height": 106,
+                  "leftMargin": 16,
+                  "rightMargin": 16,
+                  "texture": "Yellow Button_Hovered.png",
+                  "tiled": true,
+                  "topMargin": 16,
+                  "width": 256
+                },
+                "Idle": {
+                  "bottomMargin": 32,
+                  "height": 106,
+                  "leftMargin": 16,
+                  "rightMargin": 16,
+                  "texture": "Yellow Button_Idle.png",
+                  "tiled": true,
+                  "topMargin": 16,
+                  "width": 256
+                },
+                "Label": {
+                  "bold": false,
+                  "italic": false,
+                  "smoothed": true,
+                  "underlined": false,
+                  "string": "Send",
+                  "font": "27d4da0f7767cf3fbf14eb2c8da758dbcfc7b5038c9214d5e6ed62db6476a6e5_Chango-Regular.ttf",
+                  "textAlignment": "center",
+                  "characterSize": 40,
+                  "color": {
+                    "b": 42,
+                    "g": 87,
+                    "r": 139
+                  },
+                  "content": {
+                    "bold": false,
+                    "isOutlineEnabled": false,
+                    "isShadowEnabled": false,
+                    "italic": false,
+                    "outlineColor": "255;255;255",
+                    "outlineThickness": 2,
+                    "shadowAngle": 90,
+                    "shadowBlurRadius": 2,
+                    "shadowColor": "0;0;0",
+                    "shadowDistance": 4,
+                    "shadowOpacity": 127,
+                    "smoothed": true,
+                    "underlined": false,
+                    "text": "Send",
+                    "font": "27d4da0f7767cf3fbf14eb2c8da758dbcfc7b5038c9214d5e6ed62db6476a6e5_Chango-Regular.ttf",
+                    "textAlignment": "center",
+                    "verticalTextAlignment": "top",
+                    "characterSize": 40,
+                    "color": "139;87;42"
+                  }
+                },
+                "Pressed": {
+                  "bottomMargin": 16,
+                  "height": 106,
+                  "leftMargin": 16,
+                  "rightMargin": 16,
+                  "texture": "Yellow Button_Pressed.png",
+                  "tiled": true,
+                  "topMargin": 32,
+                  "width": 256
+                }
+              }
+            },
+            {
+              "assetStoreId": "bfab3a269992cf43081e2cb8053c28aaef11f2f9e0d0cbdc00207ea271f69c2c",
+              "name": "RestartButton",
+              "type": "PanelSpriteButton::PanelSpriteButton",
+              "variables": [],
+              "effects": [],
+              "behaviors": [
+                {
+                  "name": "Anchor",
+                  "type": "AnchorBehavior::AnchorBehavior",
+                  "bottomEdgeAnchor": 2,
+                  "leftEdgeAnchor": 4,
+                  "relativeToOriginalWindowSize": true,
+                  "rightEdgeAnchor": 4,
+                  "topEdgeAnchor": 0,
+                  "useLegacyBottomAndRightAnchors": false
+                }
+              ],
+              "content": {
+                "LeftPadding": 16,
+                "RightPadding": 16,
+                "PressedLabelOffsetY": 10,
+                "BottomPadding": 32,
+                "TopPadding": 16,
+                "HoveredFadeOutDuration": 0.25
+              },
+              "childrenContent": {
+                "Hovered": {
+                  "bottomMargin": 32,
+                  "height": 106,
+                  "leftMargin": 16,
+                  "rightMargin": 16,
+                  "texture": "Yellow Button_Hovered.png",
+                  "tiled": true,
+                  "topMargin": 16,
+                  "width": 256
+                },
+                "Idle": {
+                  "bottomMargin": 32,
+                  "height": 106,
+                  "leftMargin": 16,
+                  "rightMargin": 16,
+                  "texture": "Yellow Button_Idle.png",
+                  "tiled": true,
+                  "topMargin": 16,
+                  "width": 256
+                },
+                "Label": {
+                  "bold": false,
+                  "italic": false,
+                  "smoothed": true,
+                  "underlined": false,
+                  "string": "Restart",
+                  "font": "27d4da0f7767cf3fbf14eb2c8da758dbcfc7b5038c9214d5e6ed62db6476a6e5_Chango-Regular.ttf",
+                  "textAlignment": "center",
+                  "characterSize": 40,
+                  "color": {
+                    "b": 42,
+                    "g": 87,
+                    "r": 139
+                  },
+                  "content": {
+                    "bold": false,
+                    "isOutlineEnabled": false,
+                    "isShadowEnabled": false,
+                    "italic": false,
+                    "outlineColor": "255;255;255",
+                    "outlineThickness": 2,
+                    "shadowAngle": 90,
+                    "shadowBlurRadius": 2,
+                    "shadowColor": "0;0;0",
+                    "shadowDistance": 4,
+                    "shadowOpacity": 127,
+                    "smoothed": true,
+                    "underlined": false,
+                    "text": "Restart",
+                    "font": "27d4da0f7767cf3fbf14eb2c8da758dbcfc7b5038c9214d5e6ed62db6476a6e5_Chango-Regular.ttf",
+                    "textAlignment": "center",
+                    "verticalTextAlignment": "top",
+                    "characterSize": 40,
+                    "color": "139;87;42"
+                  }
+                },
+                "Pressed": {
+                  "bottomMargin": 16,
+                  "height": 106,
+                  "leftMargin": 16,
+                  "rightMargin": 16,
+                  "texture": "Yellow Button_Pressed.png",
+                  "tiled": true,
+                  "topMargin": 32,
+                  "width": 256
+                }
+              }
+            },
+            {
+              "assetStoreId": "",
+              "bottomMargin": 32,
+              "height": 128,
+              "leftMargin": 16,
+              "name": "Panel",
+              "rightMargin": 16,
+              "texture": "assets\\Grey Button_Idle.png",
+              "tiled": false,
+              "topMargin": 16,
+              "type": "PanelSpriteObject::PanelSprite",
+              "width": 128,
+              "variables": [],
+              "effects": [],
+              "behaviors": [
+                {
+                  "name": "Anchor",
+                  "type": "AnchorBehavior::AnchorBehavior",
+                  "bottomEdgeAnchor": 2,
+                  "leftEdgeAnchor": 1,
+                  "relativeToOriginalWindowSize": true,
+                  "rightEdgeAnchor": 2,
+                  "topEdgeAnchor": 1,
+                  "useLegacyBottomAndRightAnchors": false
+                }
+              ]
+            }
+          ],
+          "objectsFolderStructure": {
+            "folderName": "__ROOT",
+            "children": [
+              {
+                "objectName": "ScoreLabel"
+              },
+              {
+                "objectName": "PlayerNameInput"
+              },
+              {
+                "objectName": "SubmitButton"
+              },
+              {
+                "objectName": "RestartButton"
+              },
+              {
+                "objectName": "Panel"
+              }
+            ]
+          },
+          "objectsGroups": [],
+          "layers": [
+            {
+              "ambientLightColorB": 200,
+              "ambientLightColorG": 200,
+              "ambientLightColorR": 200,
+              "camera3DFarPlaneDistance": 10000,
+              "camera3DFieldOfView": 45,
+              "camera3DNearPlaneDistance": 3,
+              "cameraType": "",
+              "followBaseLayerCamera": false,
+              "isLightingLayer": false,
+              "isLocked": false,
+              "name": "",
+              "renderingType": "",
+              "visibility": true,
+              "cameras": [
+                {
+                  "defaultSize": true,
+                  "defaultViewport": true,
+                  "height": 0,
+                  "viewportBottom": 1,
+                  "viewportLeft": 0,
+                  "viewportRight": 1,
+                  "viewportTop": 0,
+                  "width": 0
+                }
+              ],
+              "effects": []
+            }
+          ],
+          "instances": [
+            {
+              "angle": 0,
+              "customSize": false,
+              "height": 71,
+              "layer": "",
+              "name": "ScoreLabel",
+              "persistentUuid": "e0571581-5b6f-4e0b-928a-92dd21b3851d",
+              "width": 606,
+              "x": 36,
+              "y": 41,
+              "zOrder": 18,
+              "numberProperties": [],
+              "stringProperties": [],
+              "initialVariables": []
+            },
+            {
+              "angle": 0,
+              "customSize": true,
+              "depth": 1,
+              "height": 96,
+              "layer": "",
+              "name": "PlayerNameInput",
+              "persistentUuid": "1b66c419-4bc7-406f-91e7-7deeb515ff67",
+              "width": 360,
+              "x": 40,
+              "y": 160,
+              "zOrder": 19,
+              "numberProperties": [],
+              "stringProperties": [],
+              "initialVariables": []
+            },
+            {
+              "angle": 0,
+              "customSize": false,
+              "height": 0,
+              "layer": "",
+              "name": "RestartButton",
+              "persistentUuid": "d9bfca46-e22c-42c9-a581-b88dbe1f5967",
+              "width": 0,
+              "x": 224,
+              "y": 320,
+              "zOrder": 35,
+              "numberProperties": [],
+              "stringProperties": [],
+              "initialVariables": []
+            },
+            {
+              "angle": 0,
+              "customSize": true,
+              "depth": 48,
+              "height": 106,
+              "layer": "",
+              "name": "SubmitButton",
+              "persistentUuid": "3ad1036e-666f-4a8c-9b85-6d882e192ee8",
+              "width": 216,
+              "x": 432,
+              "y": 150,
+              "zOrder": 37,
+              "numberProperties": [],
+              "stringProperties": [],
+              "initialVariables": []
+            },
+            {
+              "angle": 0,
+              "customSize": true,
+              "depth": 0,
+              "height": 480,
+              "layer": "",
+              "name": "Panel",
+              "persistentUuid": "273f403b-d896-4fa8-81bd-ef1d5d6bd006",
+              "width": 688,
+              "x": 0,
+              "y": 0,
+              "zOrder": 1,
+              "numberProperties": [],
+              "stringProperties": [],
+              "initialVariables": []
+            }
+          ]
+        }
+      ]
+    }
+  ],
+  "externalLayouts": [
+    {
+      "associatedLayout": "Game",
+      "name": "Level1",
+      "instances": [
+        {
+          "angle": 90,
+          "customSize": true,
+          "depth": 201.7647033558585,
+          "height": 128,
+          "layer": "",
+          "name": "LargeBuildingA",
+          "persistentUuid": "a9a4887f-ca10-45a0-9c7b-0c7f37c70a8f",
+          "width": 256,
+          "x": -1088,
+          "y": 0,
+          "zOrder": 25,
+          "numberProperties": [],
+          "stringProperties": [],
+          "initialVariables": []
+        },
+        {
+          "angle": 0,
+          "customSize": true,
+          "depth": 201.7647033558585,
+          "height": 128,
+          "layer": "",
+          "name": "LargeBuildingA",
+          "persistentUuid": "bcfdd521-fe50-4e3f-b91a-9d73b72fe96b",
+          "width": 256,
+          "x": -128,
+          "y": 64,
+          "zOrder": 25,
+          "numberProperties": [],
+          "stringProperties": [],
+          "initialVariables": []
+        },
+        {
+          "angle": 0,
+          "customSize": true,
+          "depth": 201.7647033558585,
+          "height": 128,
+          "layer": "",
+          "name": "LargeBuildingA",
+          "persistentUuid": "97210e1e-0fe2-4470-b649-52fd75667fb5",
+          "width": 256,
+          "x": -768,
+          "y": -704,
+          "zOrder": 25,
+          "numberProperties": [],
+          "stringProperties": [],
+          "initialVariables": []
+        },
+        {
+          "angle": 90,
+          "customSize": true,
+          "depth": 201.7647033558585,
+          "height": 128,
+          "layer": "",
+          "name": "LargeBuildingA",
+          "persistentUuid": "3e217ee2-789d-4101-bfe1-7d1f93497ca9",
+          "width": 256,
+          "x": -64,
+          "y": -640,
+          "zOrder": 25,
+          "numberProperties": [],
+          "stringProperties": [],
+          "initialVariables": []
+        },
+        {
+          "angle": 0,
+          "customSize": true,
+          "depth": 201.7647033558585,
+          "height": 128,
+          "layer": "",
+          "name": "LargeBuildingA",
+          "persistentUuid": "56cbc3bf-8596-4964-9bb1-d435ef26deef",
+          "width": 256,
+          "x": -1024,
+          "y": -704,
+          "zOrder": 25,
+          "numberProperties": [],
+          "stringProperties": [],
+          "initialVariables": []
+        },
+        {
+          "angle": 0,
+          "customSize": true,
+          "depth": 201.7647033558585,
+          "height": 128,
+          "layer": "",
+          "name": "LargeBuildingA",
+          "persistentUuid": "31650634-da10-4bef-a53d-2fbc7f00c4a6",
+          "width": 256,
+          "x": -896,
+          "y": 64,
+          "zOrder": 25,
+          "numberProperties": [],
+          "stringProperties": [],
+          "initialVariables": []
+        },
+        {
+          "angle": 0,
+          "customSize": true,
+          "depth": 201.7647033558585,
+          "height": 128,
+          "layer": "",
+          "name": "LargeBuildingA",
+          "persistentUuid": "4674c98e-f9ad-4c4a-8268-d5501967fa2e",
+          "width": 256,
+          "x": -640,
+          "y": 64,
+          "zOrder": 25,
+          "numberProperties": [],
+          "stringProperties": [],
+          "initialVariables": []
+        },
+        {
+          "angle": 0,
+          "customSize": true,
+          "depth": 201.7647033558585,
+          "height": 128,
+          "layer": "",
+          "name": "LargeBuildingA",
+          "persistentUuid": "0c347560-65a4-4440-baff-086fd1567d09",
+          "width": 256,
+          "x": -384,
+          "y": 64,
+          "zOrder": 25,
+          "numberProperties": [],
+          "stringProperties": [],
+          "initialVariables": []
+        },
+        {
+          "angle": 90,
+          "customSize": true,
+          "depth": 201.7647033558585,
+          "height": 128,
+          "layer": "",
+          "name": "LargeBuildingA",
+          "persistentUuid": "f33beccc-0f5b-4b92-a193-ca3289b4a14d",
+          "width": 256,
+          "x": -1088,
+          "y": -512,
+          "zOrder": 25,
+          "numberProperties": [],
+          "stringProperties": [],
+          "initialVariables": []
+        },
+        {
+          "angle": 90,
+          "customSize": true,
+          "depth": 201.7647033558585,
+          "height": 128,
+          "layer": "",
+          "name": "LargeBuildingA",
+          "persistentUuid": "11a0284f-62eb-4ab5-8952-50ec50b45ed0",
+          "width": 256,
+          "x": -64,
+          "y": -384,
+          "zOrder": 25,
+          "numberProperties": [],
+          "stringProperties": [],
+          "initialVariables": []
+        },
+        {
+          "angle": 0,
+          "customSize": true,
+          "depth": 201.7647033558585,
+          "height": 128,
+          "layer": "",
+          "name": "LargeBuildingA",
+          "persistentUuid": "521d809c-97fb-42e1-9c23-2c6c9222c207",
+          "width": 256,
+          "x": -512,
+          "y": -704,
+          "zOrder": 25,
+          "numberProperties": [],
+          "stringProperties": [],
+          "initialVariables": []
+        },
+        {
+          "angle": 90,
+          "customSize": true,
+          "depth": 201.7647033558585,
+          "height": 128,
+          "layer": "",
+          "name": "LargeBuildingA",
+          "persistentUuid": "65fffd98-3fe2-461b-9f14-c4cac8ba87f9",
+          "width": 256,
+          "x": 1216,
+          "y": 1280,
+          "zOrder": 25,
+          "numberProperties": [],
+          "stringProperties": [],
+          "initialVariables": []
+        },
+        {
+          "angle": 0,
+          "customSize": true,
+          "depth": 201.7647033558585,
+          "height": 128,
+          "layer": "",
+          "name": "LargeBuildingA",
+          "persistentUuid": "20da10ce-0b97-4329-a824-7fdf6bd740c2",
+          "width": 256,
+          "x": 2176,
+          "y": 1344,
+          "zOrder": 25,
+          "numberProperties": [],
+          "stringProperties": [],
+          "initialVariables": []
+        },
+        {
+          "angle": 0,
+          "customSize": true,
+          "depth": 201.7647033558585,
+          "height": 128,
+          "layer": "",
+          "name": "LargeBuildingA",
+          "persistentUuid": "3bfc6886-f8b4-4139-a58d-53d6edd675f0",
+          "width": 256,
+          "x": 2048,
+          "y": 576,
+          "zOrder": 25,
+          "numberProperties": [],
+          "stringProperties": [],
+          "initialVariables": []
+        },
+        {
+          "angle": 0,
+          "customSize": true,
+          "depth": 201.7647033558585,
+          "height": 128,
+          "layer": "",
+          "name": "LargeBuildingA",
+          "persistentUuid": "11df5d8d-69e9-4c57-ab85-2915eb88aaa9",
+          "width": 256,
+          "x": 512,
+          "y": 64,
+          "zOrder": 25,
+          "numberProperties": [],
+          "stringProperties": [],
+          "initialVariables": []
+        },
+        {
+          "angle": 0,
+          "customSize": true,
+          "depth": 201.7647033558585,
+          "height": 128,
+          "layer": "",
+          "name": "LargeBuildingA",
+          "persistentUuid": "1a6ec265-0739-4bc3-9944-0bb45851a49f",
+          "width": 256,
+          "x": 1408,
+          "y": 1344,
+          "zOrder": 25,
+          "numberProperties": [],
+          "stringProperties": [],
+          "initialVariables": []
+        },
+        {
+          "angle": 0,
+          "customSize": true,
+          "depth": 201.7647033558585,
+          "height": 128,
+          "layer": "",
+          "name": "LargeBuildingA",
+          "persistentUuid": "92bba79a-e89e-4535-b63e-3fe3ce6e911f",
+          "width": 256,
+          "x": 1664,
+          "y": 1344,
+          "zOrder": 25,
+          "numberProperties": [],
+          "stringProperties": [],
+          "initialVariables": []
+        },
+        {
+          "angle": 0,
+          "customSize": true,
+          "depth": 201.7647033558585,
+          "height": 128,
+          "layer": "",
+          "name": "LargeBuildingA",
+          "persistentUuid": "6cd00539-0275-4d78-a788-e9b28b9bd55c",
+          "width": 256,
+          "x": 1920,
+          "y": 1344,
+          "zOrder": 25,
+          "numberProperties": [],
+          "stringProperties": [],
+          "initialVariables": []
+        },
+        {
+          "angle": 90,
+          "customSize": true,
+          "depth": 201.7647033558585,
+          "height": 128,
+          "layer": "",
+          "name": "LargeBuildingA",
+          "persistentUuid": "0c8e2b36-716f-425d-8805-1f1203ee27f2",
+          "width": 256,
+          "x": 448,
+          "y": -128,
+          "zOrder": 25,
+          "numberProperties": [],
+          "stringProperties": [],
+          "initialVariables": []
+        },
+        {
+          "angle": 0,
+          "customSize": true,
+          "depth": 201.7647033558585,
+          "height": 128,
+          "layer": "",
+          "name": "LargeBuildingA",
+          "persistentUuid": "2d09b44a-ed70-4e35-94a2-05f8dcccfc0b",
+          "width": 256,
+          "x": 1792,
+          "y": 576,
+          "zOrder": 25,
+          "numberProperties": [],
+          "stringProperties": [],
+          "initialVariables": []
+        },
+        {
+          "angle": 90,
+          "customSize": true,
+          "depth": 201.7647033558585,
+          "height": 128,
+          "layer": "",
+          "name": "LargeBuildingA",
+          "persistentUuid": "6838257f-94d5-458a-ab10-21c72b85a3f8",
+          "width": 256,
+          "x": 704,
+          "y": 0,
+          "zOrder": 25,
+          "numberProperties": [],
+          "stringProperties": [],
+          "initialVariables": []
+        },
+        {
+          "angle": 0,
+          "customSize": true,
+          "depth": 201.7647033558585,
+          "height": 128,
+          "layer": "",
+          "name": "LargeBuildingA",
+          "persistentUuid": "05746046-8c04-48e2-a104-fda74c0fcba0",
+          "width": 256,
+          "x": 1280,
+          "y": 576,
+          "zOrder": 25,
+          "numberProperties": [],
+          "stringProperties": [],
+          "initialVariables": []
+        },
+        {
+          "angle": 90,
+          "customSize": true,
+          "depth": 201.7647033558585,
+          "height": 128,
+          "layer": "",
+          "name": "LargeBuildingA",
+          "persistentUuid": "d2139f22-6fc5-43bc-93f5-88b408a0da00",
+          "width": 256,
+          "x": 1216,
+          "y": 768,
+          "zOrder": 25,
+          "numberProperties": [],
+          "stringProperties": [],
+          "initialVariables": []
+        },
+        {
+          "angle": 90,
+          "customSize": true,
+          "depth": 201.7647033558585,
+          "height": 128,
+          "layer": "",
+          "name": "LargeBuildingA",
+          "persistentUuid": "a13ec1aa-8a54-4826-8e82-c7b0f5e1f576",
+          "width": 256,
+          "x": 448,
+          "y": -640,
+          "zOrder": 25,
+          "numberProperties": [],
+          "stringProperties": [],
+          "initialVariables": []
+        },
+        {
+          "angle": 90,
+          "customSize": true,
+          "depth": 201.7647033558585,
+          "height": 128,
+          "layer": "",
+          "name": "LargeBuildingA",
+          "persistentUuid": "45b8dfbf-ccf4-4465-96c5-342d6cdaff5e",
+          "width": 256,
+          "x": 448,
+          "y": -384,
+          "zOrder": 25,
+          "numberProperties": [],
+          "stringProperties": [],
+          "initialVariables": []
+        },
+        {
+          "angle": 0,
+          "customSize": true,
+          "depth": 201.7647033558585,
+          "height": 128,
+          "layer": "",
+          "name": "LargeBuildingA",
+          "persistentUuid": "92d6018a-a0d6-40fc-8347-7e08a20f9f54",
+          "width": 256,
+          "x": 640,
+          "y": -704,
+          "zOrder": 25,
+          "numberProperties": [],
+          "stringProperties": [],
+          "initialVariables": []
+        },
+        {
+          "angle": 0,
+          "customSize": true,
+          "depth": 201.7647033558585,
+          "height": 128,
+          "layer": "",
+          "name": "LargeBuildingA",
+          "persistentUuid": "ded079a6-f2ff-4d59-a779-86ad03033732",
+          "width": 256,
+          "x": 512,
+          "y": 1344,
+          "zOrder": 25,
+          "numberProperties": [],
+          "stringProperties": [],
+          "initialVariables": []
+        },
+        {
+          "angle": 90,
+          "customSize": true,
+          "depth": 201.7647033558585,
+          "height": 128,
+          "layer": "",
+          "name": "LargeBuildingA",
+          "persistentUuid": "903808f8-da6d-4bff-ac04-70db3de11660",
+          "width": 256,
+          "x": 448,
+          "y": 1152,
+          "zOrder": 25,
+          "numberProperties": [],
+          "stringProperties": [],
+          "initialVariables": []
+        },
+        {
+          "angle": 90,
+          "customSize": true,
+          "depth": 201.7647033558585,
+          "height": 128,
+          "layer": "",
+          "name": "LargeBuildingA",
+          "persistentUuid": "fd42a88c-08da-4261-875f-ce4654e55420",
+          "width": 256,
+          "x": 704,
+          "y": 1280,
+          "zOrder": 25,
+          "numberProperties": [],
+          "stringProperties": [],
+          "initialVariables": []
+        },
+        {
+          "angle": 90,
+          "customSize": true,
+          "depth": 201.7647033558585,
+          "height": 128,
+          "layer": "",
+          "name": "LargeBuildingA",
+          "persistentUuid": "ea2b887a-cdbb-4a08-85be-f387a5da95df",
+          "width": 256,
+          "x": 704,
+          "y": 768,
+          "zOrder": 25,
+          "numberProperties": [],
+          "stringProperties": [],
+          "initialVariables": []
+        },
+        {
+          "angle": 90,
+          "customSize": true,
+          "depth": 201.7647033558585,
+          "height": 128,
+          "layer": "",
+          "name": "LargeBuildingA",
+          "persistentUuid": "0cdb6e4b-e015-4c82-9126-12cc1c0a9168",
+          "width": 256,
+          "x": 704,
+          "y": 1024,
+          "zOrder": 25,
+          "numberProperties": [],
+          "stringProperties": [],
+          "initialVariables": []
+        },
+        {
+          "angle": 90,
+          "customSize": true,
+          "depth": 201.7647033558585,
+          "height": 128,
+          "layer": "",
+          "name": "LargeBuildingA",
+          "persistentUuid": "90cabc2e-ff2d-4902-848a-3b2ca39c81c4",
+          "width": 256,
+          "x": 448,
+          "y": 640,
+          "zOrder": 25,
+          "numberProperties": [],
+          "stringProperties": [],
+          "initialVariables": []
+        },
+        {
+          "angle": 0,
+          "customSize": false,
+          "height": 159.1836734693878,
+          "keepRatio": true,
+          "layer": "",
+          "name": "Player",
+          "persistentUuid": "894bdbd1-6dc0-474b-ae8a-1ced5a9cc957",
+          "width": 300,
+          "x": -992,
+          "y": 288,
+          "zOrder": 26,
+          "numberProperties": [],
+          "stringProperties": [],
+          "initialVariables": []
+        },
+        {
+          "angle": 0,
+          "customSize": false,
+          "height": 0,
+          "layer": "",
+          "name": "Coin",
+          "persistentUuid": "0da5d162-ce33-441f-a8d3-ff017d2fd2d5",
+          "rotationY": 90,
+          "width": 0,
+          "x": 1664,
+          "y": 371,
+          "z": 21,
+          "zOrder": 27,
+          "numberProperties": [],
+          "stringProperties": [],
+          "initialVariables": []
+        },
+        {
+          "angle": 0,
+          "customSize": false,
+          "height": 0,
+          "layer": "",
+          "name": "Coin",
+          "persistentUuid": "86323d49-bfee-409d-910f-fdeea6999501",
+          "rotationY": 90,
+          "width": 0,
+          "x": 1568,
+          "y": 371,
+          "z": 21,
+          "zOrder": 27,
+          "numberProperties": [],
+          "stringProperties": [],
+          "initialVariables": []
+        },
+        {
+          "angle": 0,
+          "customSize": false,
+          "height": 0,
+          "layer": "",
+          "name": "Coin",
+          "persistentUuid": "d490a1e4-922e-4837-9160-56f76758c8b6",
+          "rotationY": 90,
+          "width": 0,
+          "x": 1472,
+          "y": 371,
+          "z": 21,
+          "zOrder": 27,
+          "numberProperties": [],
+          "stringProperties": [],
+          "initialVariables": []
+        },
+        {
+          "angle": 0,
+          "customSize": false,
+          "height": 0,
+          "layer": "",
+          "name": "Coin",
+          "persistentUuid": "36890c73-8149-4297-981e-d83555bf4dcb",
+          "rotationY": 90,
+          "width": 0,
+          "x": 1376,
+          "y": 371,
+          "z": 21,
+          "zOrder": 27,
+          "numberProperties": [],
+          "stringProperties": [],
+          "initialVariables": []
+        },
+        {
+          "angle": 0,
+          "customSize": false,
+          "height": 0,
+          "layer": "",
+          "name": "Coin",
+          "persistentUuid": "d392011b-0dab-4705-93c6-1565cede70d6",
+          "rotationY": 90,
+          "width": 0,
+          "x": -1280,
+          "y": 1120,
+          "z": 21,
+          "zOrder": 27,
+          "numberProperties": [],
+          "stringProperties": [],
+          "initialVariables": []
+        },
+        {
+          "angle": 0,
+          "customSize": false,
+          "height": 0,
+          "layer": "",
+          "name": "Coin",
+          "persistentUuid": "4e80ac91-a9c6-421d-84fb-da675ade03ca",
+          "rotationY": 90,
+          "width": 0,
+          "x": -1280,
+          "y": 1312,
+          "z": 21,
+          "zOrder": 27,
+          "numberProperties": [],
+          "stringProperties": [],
+          "initialVariables": []
+        },
+        {
+          "angle": 0,
+          "customSize": false,
+          "height": 0,
+          "layer": "",
+          "name": "Coin",
+          "persistentUuid": "3f567b7d-564a-4d42-95b9-421816917e0f",
+          "rotationY": 90,
+          "width": 0,
+          "x": -864,
+          "y": 1536,
+          "z": 21,
+          "zOrder": 27,
+          "numberProperties": [],
+          "stringProperties": [],
+          "initialVariables": []
+        },
+        {
+          "angle": 0,
+          "customSize": false,
+          "height": 0,
+          "layer": "",
+          "name": "Coin",
+          "persistentUuid": "84e79653-7e26-4fef-875f-5e858a03fbf9",
+          "rotationY": 90,
+          "width": 0,
+          "x": -1056,
+          "y": 1536,
+          "z": 21,
+          "zOrder": 27,
+          "numberProperties": [],
+          "stringProperties": [],
+          "initialVariables": []
+        },
+        {
+          "angle": 0,
+          "customSize": false,
+          "height": 0,
+          "layer": "",
+          "name": "Coin",
+          "persistentUuid": "eeda5d0e-06b7-4c85-bf36-c3092ccc8d9f",
+          "rotationY": 90,
+          "width": 0,
+          "x": 544,
+          "y": 260,
+          "z": 21,
+          "zOrder": 27,
+          "numberProperties": [],
+          "stringProperties": [],
+          "initialVariables": []
+        },
+        {
+          "angle": 0,
+          "customSize": false,
+          "height": 0,
+          "layer": "",
+          "name": "Coin",
+          "persistentUuid": "274b66e4-c95e-4374-add9-c1db8090bf6d",
+          "rotationY": 90,
+          "width": 0,
+          "x": 448,
+          "y": 260,
+          "z": 21,
+          "zOrder": 27,
+          "numberProperties": [],
+          "stringProperties": [],
+          "initialVariables": []
+        },
+        {
+          "angle": 0,
+          "customSize": false,
+          "height": 0,
+          "layer": "",
+          "name": "Coin",
+          "persistentUuid": "eb1a46fb-2f6b-4f05-8932-eb9d7072b3ca",
+          "rotationY": 90,
+          "width": 0,
+          "x": 640,
+          "y": 260,
+          "z": 21,
+          "zOrder": 27,
+          "numberProperties": [],
+          "stringProperties": [],
+          "initialVariables": []
+        },
+        {
+          "angle": 0,
+          "customSize": false,
+          "height": 0,
+          "layer": "",
+          "name": "Coin",
+          "persistentUuid": "b619af0b-588c-42ab-b98e-add403b1c6f7",
+          "rotationY": 90,
+          "width": 0,
+          "x": 736,
+          "y": 260,
+          "z": 21,
+          "zOrder": 27,
+          "numberProperties": [],
+          "stringProperties": [],
+          "initialVariables": []
+        },
+        {
+          "angle": 0,
+          "customSize": false,
+          "height": 0,
+          "layer": "",
+          "name": "Coin",
+          "persistentUuid": "464e62ae-e49a-46ec-b81c-3ffff428122b",
+          "rotationY": 90,
+          "width": 0,
+          "x": -192,
+          "y": 384,
+          "z": 21,
+          "zOrder": 27,
+          "numberProperties": [],
+          "stringProperties": [],
+          "initialVariables": []
+        },
+        {
+          "angle": 0,
+          "customSize": false,
+          "height": 0,
+          "layer": "",
+          "name": "Coin",
+          "persistentUuid": "ffe78002-8cb0-45e8-953f-65648fe0bb6b",
+          "rotationY": 90,
+          "width": 0,
+          "x": -288,
+          "y": 384,
+          "z": 21,
+          "zOrder": 27,
+          "numberProperties": [],
+          "stringProperties": [],
+          "initialVariables": []
+        },
+        {
+          "angle": 0,
+          "customSize": false,
+          "height": 0,
+          "layer": "",
+          "name": "Coin",
+          "persistentUuid": "d5856c94-5ca4-4e9c-ad8a-27f64107011a",
+          "rotationY": 90,
+          "width": 0,
+          "x": -384,
+          "y": 384,
+          "z": 21,
+          "zOrder": 27,
+          "numberProperties": [],
+          "stringProperties": [],
+          "initialVariables": []
+        },
+        {
+          "angle": 0,
+          "customSize": false,
+          "height": 0,
+          "layer": "",
+          "name": "Coin",
+          "persistentUuid": "c6cffc5d-5845-469a-a163-11f925f31309",
+          "rotationY": 90,
+          "width": 0,
+          "x": -480,
+          "y": 384,
+          "z": 21,
+          "zOrder": 27,
+          "numberProperties": [],
+          "stringProperties": [],
+          "initialVariables": []
+        },
+        {
+          "angle": 0,
+          "customSize": false,
+          "height": 0,
+          "layer": "",
+          "name": "Coin",
+          "persistentUuid": "37983f2a-555b-4705-a7de-88a8f5145ed4",
+          "rotationY": 90,
+          "width": 0,
+          "x": -1280,
+          "y": 1216,
+          "z": 21,
+          "zOrder": 27,
+          "numberProperties": [],
+          "stringProperties": [],
+          "initialVariables": []
+        },
+        {
+          "angle": 0,
+          "customSize": false,
+          "height": 0,
+          "layer": "",
+          "name": "Coin",
+          "persistentUuid": "cc3ff3ba-91eb-4e5d-b982-61d5bbedae1e",
+          "rotationY": 90,
+          "width": 0,
+          "x": -960,
+          "y": 1536,
+          "z": 21,
+          "zOrder": 27,
+          "numberProperties": [],
+          "stringProperties": [],
+          "initialVariables": []
+        },
+        {
+          "angle": 0,
+          "customSize": false,
+          "height": 0,
+          "layer": "",
+          "name": "Coin",
+          "persistentUuid": "84e5efdd-e8b2-4eec-a5c8-53026c16cd7c",
+          "rotationY": 90,
+          "width": 0,
+          "x": 2240,
+          "y": 1536,
+          "z": 21,
+          "zOrder": 27,
+          "numberProperties": [],
+          "stringProperties": [],
+          "initialVariables": []
+        },
+        {
+          "angle": 0,
+          "customSize": false,
+          "height": 0,
+          "layer": "",
+          "name": "Coin",
+          "persistentUuid": "2c168740-81d7-4fc4-9470-16f9c927628d",
+          "rotationY": 90,
+          "width": 0,
+          "x": 2048,
+          "y": 1536,
+          "z": 21,
+          "zOrder": 27,
+          "numberProperties": [],
+          "stringProperties": [],
+          "initialVariables": []
+        },
+        {
+          "angle": 0,
+          "customSize": false,
+          "height": 0,
+          "layer": "",
+          "name": "Coin",
+          "persistentUuid": "f7fc9e25-e24e-4df9-8093-e808223b4b99",
+          "rotationY": 90,
+          "width": 0,
+          "x": 2144,
+          "y": 1536,
+          "z": 21,
+          "zOrder": 27,
+          "numberProperties": [],
+          "stringProperties": [],
+          "initialVariables": []
+        },
+        {
+          "angle": 0,
+          "customSize": false,
+          "height": 0,
+          "layer": "",
+          "name": "Coin",
+          "persistentUuid": "902cf497-b394-425a-a9db-5eddefc4e98c",
+          "rotationY": 90,
+          "width": 0,
+          "x": 2464,
+          "y": 1120,
+          "z": 21,
+          "zOrder": 27,
+          "numberProperties": [],
+          "stringProperties": [],
+          "initialVariables": []
+        },
+        {
+          "angle": 0,
+          "customSize": false,
+          "height": 0,
+          "layer": "",
+          "name": "Coin",
+          "persistentUuid": "527f0396-148d-4b9a-891b-3b030ed4c7c9",
+          "rotationY": 90,
+          "width": 0,
+          "x": 2464,
+          "y": 1312,
+          "z": 21,
+          "zOrder": 27,
+          "numberProperties": [],
+          "stringProperties": [],
+          "initialVariables": []
+        },
+        {
+          "angle": 0,
+          "customSize": false,
+          "height": 0,
+          "layer": "",
+          "name": "Coin",
+          "persistentUuid": "c43c4f54-000b-49ee-b8e7-fd9f39a65ad6",
+          "rotationY": 90,
+          "width": 0,
+          "x": 2464,
+          "y": 1216,
+          "z": 21,
+          "zOrder": 27,
+          "numberProperties": [],
+          "stringProperties": [],
+          "initialVariables": []
+        },
+        {
+          "angle": 0,
+          "customSize": false,
+          "height": 0,
+          "layer": "",
+          "name": "Coin",
+          "persistentUuid": "4aa5ac47-d13c-47f3-b278-987402f165b8",
+          "rotationY": 90,
+          "width": 0,
+          "x": 2464,
+          "y": -704,
+          "z": 21,
+          "zOrder": 27,
+          "numberProperties": [],
+          "stringProperties": [],
+          "initialVariables": []
+        },
+        {
+          "angle": 0,
+          "customSize": false,
+          "height": 0,
+          "layer": "",
+          "name": "Coin",
+          "persistentUuid": "36309f76-fad5-4086-a216-142c7000932a",
+          "rotationY": 90,
+          "width": 0,
+          "x": 2464,
+          "y": -512,
+          "z": 21,
+          "zOrder": 27,
+          "numberProperties": [],
+          "stringProperties": [],
+          "initialVariables": []
+        },
+        {
+          "angle": 0,
+          "customSize": false,
+          "height": 0,
+          "layer": "",
+          "name": "Coin",
+          "persistentUuid": "13215893-1843-4998-beab-8c9420ab191e",
+          "rotationY": 90,
+          "width": 0,
+          "x": 2464,
+          "y": -608,
+          "z": 21,
+          "zOrder": 27,
+          "numberProperties": [],
+          "stringProperties": [],
+          "initialVariables": []
+        },
+        {
+          "angle": 0,
+          "customSize": false,
+          "height": 0,
+          "layer": "",
+          "name": "Coin",
+          "persistentUuid": "01564816-db2d-4222-9271-691a782814ab",
+          "rotationY": 90,
+          "width": 0,
+          "x": -1280,
+          "y": -704,
+          "z": 21,
+          "zOrder": 27,
+          "numberProperties": [],
+          "stringProperties": [],
+          "initialVariables": []
+        },
+        {
+          "angle": 0,
+          "customSize": false,
+          "height": 0,
+          "layer": "",
+          "name": "Coin",
+          "persistentUuid": "4112fc62-34be-4269-b843-94d7ef614ad2",
+          "rotationY": 90,
+          "width": 0,
+          "x": -1280,
+          "y": -512,
+          "z": 21,
+          "zOrder": 27,
+          "numberProperties": [],
+          "stringProperties": [],
+          "initialVariables": []
+        },
+        {
+          "angle": 0,
+          "customSize": false,
+          "height": 0,
+          "layer": "",
+          "name": "Coin",
+          "persistentUuid": "ae78128d-84ce-4d60-8baa-f0b7edac934c",
+          "rotationY": 90,
+          "width": 0,
+          "x": -1280,
+          "y": -608,
+          "z": 21,
+          "zOrder": 27,
+          "numberProperties": [],
+          "stringProperties": [],
+          "initialVariables": []
+        },
+        {
+          "angle": 0,
+          "customSize": false,
+          "height": 0,
+          "layer": "",
+          "name": "Coin",
+          "persistentUuid": "1a6de6d8-8f3a-465c-8872-69f145ddef23",
+          "rotationY": 90,
+          "width": 0,
+          "x": -864,
+          "y": -928,
+          "z": 21,
+          "zOrder": 27,
+          "numberProperties": [],
+          "stringProperties": [],
+          "initialVariables": []
+        },
+        {
+          "angle": 0,
+          "customSize": false,
+          "height": 0,
+          "layer": "",
+          "name": "Coin",
+          "persistentUuid": "a38b1d1e-0a5b-405c-ad85-4a4610c1b2c6",
+          "rotationY": 90,
+          "width": 0,
+          "x": -1056,
+          "y": -928,
+          "z": 21,
+          "zOrder": 27,
+          "numberProperties": [],
+          "stringProperties": [],
+          "initialVariables": []
+        },
+        {
+          "angle": 0,
+          "customSize": false,
+          "height": 0,
+          "layer": "",
+          "name": "Coin",
+          "persistentUuid": "a1fea144-0d41-47fe-9522-0191b2a188a8",
+          "rotationY": 90,
+          "width": 0,
+          "x": -960,
+          "y": -928,
+          "z": 21,
+          "zOrder": 27,
+          "numberProperties": [],
+          "stringProperties": [],
+          "initialVariables": []
+        },
+        {
+          "angle": 0,
+          "customSize": false,
+          "height": 0,
+          "layer": "",
+          "name": "Coin",
+          "persistentUuid": "81751047-2c44-4915-aa4c-a8beb7ffb039",
+          "rotationY": 90,
+          "width": 0,
+          "x": 736,
+          "y": -1036,
+          "z": 21,
+          "zOrder": 27,
+          "numberProperties": [],
+          "stringProperties": [],
+          "initialVariables": []
+        },
+        {
+          "angle": 0,
+          "customSize": false,
+          "height": 0,
+          "layer": "",
+          "name": "Coin",
+          "persistentUuid": "f2f482fe-5abe-4bed-a2a3-3d24fe635a26",
+          "rotationY": 90,
+          "width": 0,
+          "x": 640,
+          "y": -1036,
+          "z": 21,
+          "zOrder": 27,
+          "numberProperties": [],
+          "stringProperties": [],
+          "initialVariables": []
+        },
+        {
+          "angle": 0,
+          "customSize": false,
+          "height": 0,
+          "layer": "",
+          "name": "Coin",
+          "persistentUuid": "8c7022de-259d-408e-a83b-e2dba8f1ac18",
+          "rotationY": 90,
+          "width": 0,
+          "x": 544,
+          "y": -1036,
+          "z": 21,
+          "zOrder": 27,
+          "numberProperties": [],
+          "stringProperties": [],
+          "initialVariables": []
+        },
+        {
+          "angle": 0,
+          "customSize": false,
+          "height": 0,
+          "layer": "",
+          "name": "Coin",
+          "persistentUuid": "d52ddf39-738d-4e60-bf80-4b43f89099e2",
+          "rotationY": 90,
+          "width": 0,
+          "x": 448,
+          "y": -1036,
+          "z": 21,
+          "zOrder": 27,
+          "numberProperties": [],
+          "stringProperties": [],
+          "initialVariables": []
+        },
+        {
+          "angle": 0,
+          "customSize": false,
+          "height": 0,
+          "layer": "",
+          "name": "Coin",
+          "persistentUuid": "d50d718d-7834-44dd-9d09-a68d411882f3",
+          "rotationY": 90,
+          "width": 0,
+          "x": 736,
+          "y": 1664,
+          "z": 21,
+          "zOrder": 27,
+          "numberProperties": [],
+          "stringProperties": [],
+          "initialVariables": []
+        },
+        {
+          "angle": 0,
+          "customSize": false,
+          "height": 0,
+          "layer": "",
+          "name": "Coin",
+          "persistentUuid": "b954ca9b-4c6e-4478-9606-4e6bbad0aa1e",
+          "rotationY": 90,
+          "width": 0,
+          "x": 640,
+          "y": 1664,
+          "z": 21,
+          "zOrder": 27,
+          "numberProperties": [],
+          "stringProperties": [],
+          "initialVariables": []
+        },
+        {
+          "angle": 0,
+          "customSize": false,
+          "height": 0,
+          "layer": "",
+          "name": "Coin",
+          "persistentUuid": "0a095649-4dbc-4c54-a193-66cae16a1879",
+          "rotationY": 90,
+          "width": 0,
+          "x": 544,
+          "y": 1664,
+          "z": 21,
+          "zOrder": 27,
+          "numberProperties": [],
+          "stringProperties": [],
+          "initialVariables": []
+        },
+        {
+          "angle": 0,
+          "customSize": false,
+          "height": 0,
+          "layer": "",
+          "name": "Coin",
+          "persistentUuid": "2e4839e5-ef94-40e6-b2a6-3671ed98ac00",
+          "rotationY": 90,
+          "width": 0,
+          "x": 448,
+          "y": 1664,
+          "z": 21,
+          "zOrder": 27,
+          "numberProperties": [],
+          "stringProperties": [],
+          "initialVariables": []
+        },
+        {
+          "angle": 0,
+          "customSize": false,
+          "height": 0,
+          "layer": "",
+          "name": "Coin",
+          "persistentUuid": "3c41c0ed-3526-4fe0-adea-135b38a8555f",
+          "rotationY": 90,
+          "width": 0,
+          "x": 2240,
+          "y": -928,
+          "z": 21,
+          "zOrder": 27,
+          "numberProperties": [],
+          "stringProperties": [],
+          "initialVariables": []
+        },
+        {
+          "angle": 0,
+          "customSize": false,
+          "height": 0,
+          "layer": "",
+          "name": "Coin",
+          "persistentUuid": "7a5ff952-5e59-45d4-b1be-45e53fcdf281",
+          "rotationY": 90,
+          "width": 0,
+          "x": 2048,
+          "y": -928,
+          "z": 21,
+          "zOrder": 27,
+          "numberProperties": [],
+          "stringProperties": [],
+          "initialVariables": []
+        },
+        {
+          "angle": 0,
+          "customSize": false,
+          "height": 0,
+          "layer": "",
+          "name": "Coin",
+          "persistentUuid": "502f8c2f-079b-4ee3-8fc2-69e112c4ee13",
+          "rotationY": 90,
+          "width": 0,
+          "x": 2144,
+          "y": -928,
+          "z": 21,
+          "zOrder": 27,
+          "numberProperties": [],
+          "stringProperties": [],
+          "initialVariables": []
+        },
+        {
+          "angle": 0,
+          "customSize": false,
+          "height": 0,
+          "layer": "",
+          "name": "SuvLuxury",
+          "persistentUuid": "9589bc77-0357-4388-b5ec-e1092d0557fe",
+          "width": 0,
+          "x": 426,
+          "y": 449,
+          "z": 4,
+          "zOrder": 28,
+          "numberProperties": [],
+          "stringProperties": [],
+          "initialVariables": []
+        },
+        {
+          "angle": 180,
+          "customSize": true,
+          "depth": 180.14706219218877,
+          "height": 128,
+          "layer": "",
+          "name": "LargeBuildingF",
+          "persistentUuid": "0923c025-2301-44f6-9c32-91043e3cf9c5",
+          "width": 256,
+          "x": 1536,
+          "y": 576,
+          "zOrder": 30,
+          "numberProperties": [],
+          "stringProperties": [],
+          "initialVariables": []
+        },
+        {
+          "angle": 180,
+          "customSize": true,
+          "depth": 180.14706219218877,
+          "height": 128,
+          "layer": "",
+          "name": "LargeBuildingF",
+          "persistentUuid": "a94497ae-b7eb-4df8-840b-0ac8382f6327",
+          "width": 256,
+          "x": 640,
+          "y": 576,
+          "zOrder": 30,
+          "numberProperties": [],
+          "stringProperties": [],
+          "initialVariables": []
+        },
+        {
+          "angle": 180,
+          "customSize": true,
+          "depth": 180.14706219218877,
+          "height": 128,
+          "layer": "",
+          "name": "LargeBuildingF",
+          "persistentUuid": "a567d019-c172-4b8f-bfed-654c28709043",
+          "width": 256,
+          "x": -256,
+          "y": -704,
+          "zOrder": 30,
+          "numberProperties": [],
+          "stringProperties": [],
+          "initialVariables": []
+        },
+        {
+          "angle": 90,
+          "customSize": true,
+          "depth": 180.14706219218877,
+          "height": 128,
+          "layer": "",
+          "name": "LargeBuildingF",
+          "persistentUuid": "c1cd9bb9-62e8-4e06-8503-92aac61c7262",
+          "width": 256,
+          "x": 1216,
+          "y": 1024,
+          "zOrder": 30,
+          "numberProperties": [],
+          "stringProperties": [],
+          "initialVariables": []
+        },
+        {
+          "angle": 90,
+          "customSize": true,
+          "depth": 180.14706219218877,
+          "height": 128,
+          "layer": "",
+          "name": "LargeBuildingF",
+          "persistentUuid": "c3a83986-488c-4812-ae8c-5415ad20f08e",
+          "width": 256,
+          "x": 448,
+          "y": 896,
+          "zOrder": 30,
+          "numberProperties": [],
+          "stringProperties": [],
+          "initialVariables": []
+        },
+        {
+          "angle": 90,
+          "customSize": true,
+          "depth": 180.14706219218877,
+          "height": 128,
+          "layer": "",
+          "name": "LargeBuildingF",
+          "persistentUuid": "594ae657-a1bb-436f-a4df-319949c2c14a",
+          "width": 256,
+          "x": -1088,
+          "y": -256,
+          "zOrder": 30,
+          "numberProperties": [],
+          "stringProperties": [],
+          "initialVariables": []
+        },
+        {
+          "angle": 270,
+          "customSize": true,
+          "depth": 180.14706219218877,
+          "height": 128,
+          "layer": "",
+          "name": "LargeBuildingF",
+          "persistentUuid": "f4ccec6d-6fd1-4756-b079-8950ab54891e",
+          "width": 256,
+          "x": 704,
+          "y": -256,
+          "zOrder": 31,
+          "numberProperties": [],
+          "stringProperties": [],
+          "initialVariables": []
+        },
+        {
+          "angle": 270,
+          "customSize": true,
+          "depth": 180.14706219218877,
+          "height": 128,
+          "layer": "",
+          "name": "LargeBuildingF",
+          "persistentUuid": "76dfc897-cfb4-491a-8327-11380541a5ce",
+          "width": 256,
+          "x": 704,
+          "y": -512,
+          "zOrder": 32,
+          "numberProperties": [],
+          "stringProperties": [],
+          "initialVariables": []
+        },
+        {
+          "angle": 270,
+          "customSize": true,
+          "depth": 180.14706219218877,
+          "height": 128,
+          "layer": "",
+          "name": "LargeBuildingF",
+          "persistentUuid": "139dda3d-2ac7-42d4-91e0-ab44ff464a5f",
+          "width": 256,
+          "x": -64,
+          "y": -128,
+          "zOrder": 33,
+          "numberProperties": [],
+          "stringProperties": [],
+          "initialVariables": []
+        },
+        {
+          "angle": 270,
+          "customSize": true,
+          "depth": 180.14706219218877,
+          "height": 128,
+          "layer": "",
+          "name": "LargeBuildingF",
+          "persistentUuid": "8c1e8064-f239-4db0-97b0-682bea722b0d",
+          "width": 256,
+          "x": 2240,
+          "y": 640,
+          "zOrder": 34,
+          "numberProperties": [],
+          "stringProperties": [],
+          "initialVariables": []
+        },
+        {
+          "angle": 270,
+          "customSize": true,
+          "depth": 180.14706219218877,
+          "height": 128,
+          "layer": "",
+          "name": "LargeBuildingF",
+          "persistentUuid": "395f9f8d-19bc-4927-a103-9550563fd1c6",
+          "width": 256,
+          "x": 2240,
+          "y": 896,
+          "zOrder": 35,
+          "numberProperties": [],
+          "stringProperties": [],
+          "initialVariables": []
+        },
+        {
+          "angle": 270,
+          "customSize": true,
+          "depth": 180.14706219218877,
+          "height": 128,
+          "layer": "",
+          "name": "LargeBuildingF",
+          "persistentUuid": "3ca01a52-6ec5-43ea-9285-b3aa1ef17c15",
+          "width": 256,
+          "x": 2240,
+          "y": 1152,
+          "zOrder": 36,
+          "numberProperties": [],
+          "stringProperties": [],
+          "initialVariables": []
+        },
+        {
+          "angle": 180,
+          "customSize": false,
+          "height": 0,
+          "layer": "",
+          "name": "Van",
+          "persistentUuid": "fa97679d-81b5-4edf-91ce-a00d4bc798c2",
+          "width": 0,
+          "x": 472,
+          "y": 192,
+          "z": 4,
+          "zOrder": 37,
+          "numberProperties": [],
+          "stringProperties": [],
+          "initialVariables": []
+        },
+        {
+          "angle": 0,
+          "customSize": false,
+          "height": 0,
+          "layer": "",
+          "name": "SuvLuxury",
+          "persistentUuid": "2dd3cdfa-a9aa-4fca-969d-a9b53a744eb2",
+          "width": 0,
+          "x": -391,
+          "y": -835,
+          "z": 4,
+          "zOrder": 28,
+          "numberProperties": [],
+          "stringProperties": [],
+          "initialVariables": []
+        },
+        {
+          "angle": 180,
+          "customSize": false,
+          "height": 0,
+          "layer": "",
+          "name": "Van",
+          "persistentUuid": "c1c8cd46-9a2b-4927-858b-76316c5a95b6",
+          "width": 0,
+          "x": -216,
+          "y": -1087,
+          "z": 4,
+          "zOrder": 37,
+          "numberProperties": [],
+          "stringProperties": [],
+          "initialVariables": []
+        },
+        {
+          "angle": 0,
+          "customSize": false,
+          "height": 0,
+          "layer": "",
+          "name": "SuvLuxury",
+          "persistentUuid": "32547478-ebd7-4242-8afc-5c8f969bd186",
+          "width": 0,
+          "x": 1770,
+          "y": 442,
+          "z": 4,
+          "zOrder": 28,
+          "numberProperties": [],
+          "stringProperties": [],
+          "initialVariables": []
+        },
+        {
+          "angle": 0,
+          "customSize": false,
+          "height": 0,
+          "layer": "",
+          "name": "SuvLuxury",
+          "persistentUuid": "5f2e97f0-7bb3-457f-925e-d2d38ba0e7ab",
+          "width": 0,
+          "x": 1930,
+          "y": 442,
+          "z": 4,
+          "zOrder": 28,
+          "numberProperties": [],
+          "stringProperties": [],
+          "initialVariables": []
+        },
+        {
+          "angle": 0,
+          "customSize": false,
+          "height": 0,
+          "layer": "",
+          "name": "Van",
+          "persistentUuid": "384c861f-fddd-4481-b559-448e68de9746",
+          "width": 0,
+          "x": 2184,
+          "y": 442,
+          "z": 4,
+          "zOrder": 38,
+          "numberProperties": [],
+          "stringProperties": [],
+          "initialVariables": []
+        },
+        {
+          "angle": 0,
+          "customSize": false,
+          "height": 0,
+          "layer": "",
+          "name": "SuvLuxury",
+          "persistentUuid": "478f6d08-f51b-46ce-b5ae-a9ad98378e33",
+          "width": 0,
+          "x": -1078,
+          "y": 450,
+          "z": 4,
+          "zOrder": 28,
+          "numberProperties": [],
+          "stringProperties": [],
+          "initialVariables": []
+        },
+        {
+          "angle": 0,
+          "customSize": false,
+          "height": 0,
+          "layer": "",
+          "name": "SuvLuxury",
+          "persistentUuid": "3fce5402-89bd-4976-a7b4-b4238e485fb3",
+          "width": 0,
+          "x": -918,
+          "y": 450,
+          "z": 4,
+          "zOrder": 28,
+          "numberProperties": [],
+          "stringProperties": [],
+          "initialVariables": []
+        },
+        {
+          "angle": 0,
+          "customSize": false,
+          "height": 0,
+          "layer": "",
+          "name": "Van",
+          "persistentUuid": "acb3437b-84fc-4e7f-8260-5e39448bef0f",
+          "width": 0,
+          "x": -662,
+          "y": 450,
+          "z": 4,
+          "zOrder": 38,
+          "numberProperties": [],
+          "stringProperties": [],
+          "initialVariables": []
+        },
+        {
+          "angle": 0,
+          "customSize": false,
+          "height": 0,
+          "layer": "",
+          "name": "SuvLuxury",
+          "persistentUuid": "9d4b0ad2-7092-4d04-83ee-25f42217d50b",
+          "width": 0,
+          "x": -1014,
+          "y": -834,
+          "z": 4,
+          "zOrder": 28,
+          "numberProperties": [],
+          "stringProperties": [],
+          "initialVariables": []
+        },
+        {
+          "angle": 0,
+          "customSize": false,
+          "height": 0,
+          "layer": "",
+          "name": "SuvLuxury",
+          "persistentUuid": "778bca32-1711-492f-a3b0-a7818a0be53a",
+          "width": 0,
+          "x": -854,
+          "y": -834,
+          "z": 4,
+          "zOrder": 28,
+          "numberProperties": [],
+          "stringProperties": [],
+          "initialVariables": []
+        },
+        {
+          "angle": 0,
+          "customSize": false,
+          "height": 0,
+          "layer": "",
+          "name": "Van",
+          "persistentUuid": "50e40d07-e62f-4bbc-95f4-31e3a00b13a7",
+          "width": 0,
+          "x": -598,
+          "y": -834,
+          "z": 4,
+          "zOrder": 38,
+          "numberProperties": [],
+          "stringProperties": [],
+          "initialVariables": []
+        },
+        {
+          "angle": 180,
+          "customSize": false,
+          "height": 0,
+          "layer": "",
+          "name": "Van",
+          "persistentUuid": "db6379dd-828b-4b97-8c34-1e3aefd16053",
+          "width": 0,
+          "x": 616,
+          "y": 192,
+          "z": 4,
+          "zOrder": 37,
+          "numberProperties": [],
+          "stringProperties": [],
+          "initialVariables": []
+        },
+        {
+          "angle": 180,
+          "customSize": false,
+          "height": 0,
+          "layer": "",
+          "name": "Van",
+          "persistentUuid": "24c0eb97-df17-4ec3-b525-6775f3e4b171",
+          "width": 0,
+          "x": -168,
+          "y": 1474,
+          "z": 4,
+          "zOrder": 37,
+          "numberProperties": [],
+          "stringProperties": [],
+          "initialVariables": []
+        },
+        {
+          "angle": 180,
+          "customSize": false,
+          "height": 0,
+          "layer": "",
+          "name": "Van",
+          "persistentUuid": "3867bff0-bc6b-4cde-bd55-aa0e1acb951f",
+          "width": 0,
+          "x": -392,
+          "y": 1474,
+          "z": 4,
+          "zOrder": 37,
+          "numberProperties": [],
+          "stringProperties": [],
+          "initialVariables": []
+        },
+        {
+          "angle": 180,
+          "customSize": false,
+          "height": 0,
+          "layer": "",
+          "name": "Van",
+          "persistentUuid": "573153a2-d34f-4f7d-a078-d591f21417dd",
+          "width": 0,
+          "x": 935,
+          "y": -1083,
+          "z": 4,
+          "zOrder": 37,
+          "numberProperties": [],
+          "stringProperties": [],
+          "initialVariables": []
+        },
+        {
+          "angle": 180,
+          "customSize": false,
+          "height": 0,
+          "layer": "",
+          "name": "Van",
+          "persistentUuid": "6c3d140a-a1d9-4713-be38-d77dadcc147a",
+          "width": 0,
+          "x": 1095,
+          "y": -1083,
+          "z": 4,
+          "zOrder": 37,
+          "numberProperties": [],
+          "stringProperties": [],
+          "initialVariables": []
+        },
+        {
+          "angle": 180,
+          "customSize": false,
+          "height": 0,
+          "layer": "",
+          "name": "Van",
+          "persistentUuid": "2cc2aad3-8095-45bf-b31c-3ea4c53a54fe",
+          "width": 0,
+          "x": 2247,
+          "y": -1086,
+          "z": 4,
+          "zOrder": 37,
+          "numberProperties": [],
+          "stringProperties": [],
+          "initialVariables": []
+        },
+        {
+          "angle": 180,
+          "customSize": false,
+          "height": 0,
+          "layer": "",
+          "name": "SuvLuxury",
+          "persistentUuid": "e5a60d8e-77ff-4627-af10-ad2af0055da7",
+          "width": 0,
+          "x": 2102,
+          "y": -1085,
+          "z": 4,
+          "zOrder": 39,
+          "numberProperties": [],
+          "stringProperties": [],
+          "initialVariables": []
+        },
+        {
+          "angle": 180,
+          "customSize": false,
+          "height": 0,
+          "layer": "",
+          "name": "Van",
+          "persistentUuid": "bc21ef32-eb2c-4588-8535-9bbaf10b0fe8",
+          "width": 0,
+          "x": 1642,
+          "y": 195,
+          "z": 4,
+          "zOrder": 37,
+          "numberProperties": [],
+          "stringProperties": [],
+          "initialVariables": []
+        },
+        {
+          "angle": 180,
+          "customSize": false,
+          "height": 0,
+          "layer": "",
+          "name": "SuvLuxury",
+          "persistentUuid": "634c6020-2d28-47be-b2e4-2f0acdff4326",
+          "width": 0,
+          "x": 1514,
+          "y": 195,
+          "z": 4,
+          "zOrder": 39,
+          "numberProperties": [],
+          "stringProperties": [],
+          "initialVariables": []
+        },
+        {
+          "angle": 180,
+          "customSize": false,
+          "height": 0,
+          "layer": "",
+          "name": "Van",
+          "persistentUuid": "c2d673cd-0b27-4ab6-b26b-641469b9514b",
+          "width": 0,
+          "x": 1962,
+          "y": 1474,
+          "z": 4,
+          "zOrder": 37,
+          "numberProperties": [],
+          "stringProperties": [],
+          "initialVariables": []
+        },
+        {
+          "angle": 180,
+          "customSize": false,
+          "height": 0,
+          "layer": "",
+          "name": "SuvLuxury",
+          "persistentUuid": "f1ea9686-2334-44fb-8dc3-981db82aea0d",
+          "width": 0,
+          "x": 1834,
+          "y": 1474,
+          "z": 4,
+          "zOrder": 39,
+          "numberProperties": [],
+          "stringProperties": [],
+          "initialVariables": []
+        },
+        {
+          "angle": 0,
+          "customSize": true,
+          "depth": 8,
+          "height": 384,
+          "layer": "",
+          "name": "RoadStraight",
+          "persistentUuid": "8f939289-ce00-49c8-8646-515c85580db9",
+          "width": 1152,
+          "x": -1152,
+          "y": 1408,
+          "zOrder": 42,
+          "numberProperties": [],
+          "stringProperties": [],
+          "initialVariables": []
+        },
+        {
+          "angle": 0,
+          "customSize": false,
+          "height": 0,
+          "layer": "",
+          "name": "RoadStraight",
+          "persistentUuid": "cb18e3ac-db07-44c5-8746-0baeeadebe74",
+          "width": 0,
+          "x": 384,
+          "y": 1408,
+          "zOrder": 42,
+          "numberProperties": [],
+          "stringProperties": [],
+          "initialVariables": []
+        },
+        {
+          "angle": 0,
+          "customSize": true,
+          "depth": 8,
+          "height": 384,
+          "layer": "",
+          "name": "RoadStraight",
+          "persistentUuid": "dff9b457-42ce-4d46-a93e-a8bd0f44030f",
+          "width": 1152,
+          "x": 1152,
+          "y": 1408,
+          "zOrder": 42,
+          "numberProperties": [],
+          "stringProperties": [],
+          "initialVariables": []
+        },
+        {
+          "angle": 0,
+          "customSize": true,
+          "depth": 8,
+          "height": 384,
+          "layer": "",
+          "name": "RoadStraight",
+          "persistentUuid": "27286779-2ffd-4ad3-affe-39dbdbc8ef75",
+          "width": 1152,
+          "x": 1152,
+          "y": 128,
+          "zOrder": 42,
+          "numberProperties": [],
+          "stringProperties": [],
+          "initialVariables": []
+        },
+        {
+          "angle": 0,
+          "customSize": true,
+          "depth": 8,
+          "height": 384,
+          "layer": "",
+          "name": "RoadStraight",
+          "persistentUuid": "17fb7425-4817-4a27-a352-e2ecb15eae44",
+          "width": 1152,
+          "x": 1152,
+          "y": -1152,
+          "zOrder": 42,
+          "numberProperties": [],
+          "stringProperties": [],
+          "initialVariables": []
+        },
+        {
+          "angle": 0,
+          "customSize": true,
+          "depth": 8,
+          "height": 384,
+          "layer": "",
+          "name": "RoadStraight",
+          "persistentUuid": "9ed137cb-69e5-4d3c-8acd-f81b10baedc1",
+          "width": 1152,
+          "x": -1152,
+          "y": -1152,
+          "zOrder": 42,
+          "numberProperties": [],
+          "stringProperties": [],
+          "initialVariables": []
+        },
+        {
+          "angle": 0,
+          "customSize": false,
+          "height": 0,
+          "layer": "",
+          "name": "RoadStraight",
+          "persistentUuid": "75d41047-aac4-492d-a434-7c292fdc5932",
+          "width": 0,
+          "x": 384,
+          "y": -1152,
+          "zOrder": 42,
+          "numberProperties": [],
+          "stringProperties": [],
+          "initialVariables": []
+        },
+        {
+          "angle": 0,
+          "customSize": false,
+          "height": 0,
+          "layer": "",
+          "name": "RoadStraight",
+          "persistentUuid": "6bb6f7ac-9bdb-400d-bf70-164a7fe5bfec",
+          "width": 0,
+          "x": 384,
+          "y": 128,
+          "zOrder": 42,
+          "numberProperties": [],
+          "stringProperties": [],
+          "initialVariables": []
+        },
+        {
+          "angle": 90,
+          "customSize": true,
+          "depth": 8,
+          "height": 384,
+          "layer": "",
+          "name": "RoadStraight",
+          "persistentUuid": "3fcc1379-d254-481e-bad2-bc5d4c12de0d",
+          "width": 896,
+          "x": -256,
+          "y": -512,
+          "zOrder": 42,
+          "numberProperties": [],
+          "stringProperties": [],
+          "initialVariables": []
+        },
+        {
+          "angle": 90,
+          "customSize": true,
+          "depth": 8,
+          "height": 384,
+          "layer": "",
+          "name": "RoadStraight",
+          "persistentUuid": "b3fcfd40-daf0-4ac0-8ff4-c1ddcf9dcb2f",
+          "width": 896,
+          "x": 512,
+          "y": -512,
+          "zOrder": 42,
+          "numberProperties": [],
+          "stringProperties": [],
+          "initialVariables": []
+        },
+        {
+          "angle": 90,
+          "customSize": true,
+          "depth": 8,
+          "height": 384,
+          "layer": "",
+          "name": "RoadStraight",
+          "persistentUuid": "7b70847b-a556-49f3-bb48-c73bc31eeea8",
+          "width": 896,
+          "x": 2048,
+          "y": -512,
+          "zOrder": 42,
+          "numberProperties": [],
+          "stringProperties": [],
+          "initialVariables": []
+        },
+        {
+          "angle": 90,
+          "customSize": true,
+          "depth": 8,
+          "height": 384,
+          "layer": "",
+          "name": "RoadStraight",
+          "persistentUuid": "e9045c79-8c9f-433c-87f1-489cd7c5d8b5",
+          "width": 896,
+          "x": 2048,
+          "y": 768,
+          "zOrder": 42,
+          "numberProperties": [],
+          "stringProperties": [],
+          "initialVariables": []
+        },
+        {
+          "angle": 90,
+          "customSize": true,
+          "depth": 8,
+          "height": 384,
+          "layer": "",
+          "name": "RoadStraight",
+          "persistentUuid": "f7b9a20a-970f-46a1-a0a9-2345c3082812",
+          "width": 896,
+          "x": 512,
+          "y": 768,
+          "zOrder": 42,
+          "numberProperties": [],
+          "stringProperties": [],
+          "initialVariables": []
+        },
+        {
+          "angle": 90,
+          "customSize": true,
+          "depth": 8,
+          "height": 384,
+          "layer": "",
+          "name": "RoadStraight",
+          "persistentUuid": "c382140e-8041-4b53-b182-4d3afef43ceb",
+          "width": 896,
+          "x": -256,
+          "y": 768,
+          "zOrder": 42,
+          "numberProperties": [],
+          "stringProperties": [],
+          "initialVariables": []
+        },
+        {
+          "angle": 90,
+          "customSize": true,
+          "depth": 8,
+          "height": 384,
+          "layer": "",
+          "name": "RoadStraight",
+          "persistentUuid": "f97d31b2-91e4-4d98-8ec8-894830e52c4f",
+          "width": 896,
+          "x": -1792,
+          "y": 768,
+          "zOrder": 42,
+          "numberProperties": [],
+          "stringProperties": [],
+          "initialVariables": []
+        },
+        {
+          "angle": 90,
+          "customSize": true,
+          "depth": 8,
+          "height": 384,
+          "layer": "",
+          "name": "RoadStraight",
+          "persistentUuid": "92323d5f-fd91-4f0c-b2ba-7bed3b8a28a9",
+          "width": 896,
+          "x": -1792,
+          "y": -512,
+          "zOrder": 42,
+          "numberProperties": [],
+          "stringProperties": [],
+          "initialVariables": []
+        },
+        {
+          "angle": 0,
+          "customSize": true,
+          "depth": 8,
+          "height": 384,
+          "layer": "",
+          "name": "RoadStraight",
+          "persistentUuid": "9ce5b297-a47a-4ca1-978f-48440f38ed59",
+          "width": 1152,
+          "x": -1152,
+          "y": 128,
+          "zOrder": 42,
+          "numberProperties": [],
+          "stringProperties": [],
+          "initialVariables": []
+        },
+        {
+          "angle": 0,
+          "customSize": false,
+          "height": 0,
+          "layer": "",
+          "name": "RoadCrossroadPath",
+          "persistentUuid": "a65bdf09-c0a8-40f2-bef2-dcde130625b7",
+          "width": 0,
+          "x": 768,
+          "y": 128,
+          "zOrder": 43,
+          "numberProperties": [],
+          "stringProperties": [],
+          "initialVariables": []
+        },
+        {
+          "angle": 0,
+          "customSize": false,
+          "height": 0,
+          "layer": "",
+          "name": "RoadCrossroadPath",
+          "persistentUuid": "efa57fb9-dd0b-431b-ba97-e56bd554f4cb",
+          "width": 0,
+          "x": 0,
+          "y": 128,
+          "zOrder": 43,
+          "numberProperties": [],
+          "stringProperties": [],
+          "initialVariables": []
+        },
+        {
+          "angle": 0,
+          "customSize": false,
+          "height": 0,
+          "layer": "",
+          "name": "RoadIntersectionPath",
+          "persistentUuid": "eb323bf0-2c9f-470d-8f30-7b79ca5b1c15",
+          "width": 0,
+          "x": 768,
+          "y": 1408,
+          "zOrder": 44,
+          "numberProperties": [],
+          "stringProperties": [],
+          "initialVariables": []
+        },
+        {
+          "angle": 0,
+          "customSize": false,
+          "height": 0,
+          "layer": "",
+          "name": "RoadIntersectionPath",
+          "persistentUuid": "8a20020b-9ddf-4dd6-8eed-7203403731d5",
+          "width": 0,
+          "x": 0,
+          "y": 1408,
+          "zOrder": 44,
+          "numberProperties": [],
+          "stringProperties": [],
+          "initialVariables": []
+        },
+        {
+          "angle": 180,
+          "customSize": false,
+          "height": 0,
+          "layer": "",
+          "name": "RoadIntersectionPath",
+          "persistentUuid": "92e15216-85fa-4eeb-bb9a-04d8f369f724",
+          "width": 0,
+          "x": 0,
+          "y": -1152,
+          "zOrder": 44,
+          "numberProperties": [],
+          "stringProperties": [],
+          "initialVariables": []
+        },
+        {
+          "angle": 180,
+          "customSize": false,
+          "height": 0,
+          "layer": "",
+          "name": "RoadIntersectionPath",
+          "persistentUuid": "c4452ded-2d3e-4846-aae6-f81cf1539dd1",
+          "width": 0,
+          "x": 768,
+          "y": -1152,
+          "zOrder": 44,
+          "numberProperties": [],
+          "stringProperties": [],
+          "initialVariables": []
+        },
+        {
+          "angle": 90,
+          "customSize": false,
+          "height": 0,
+          "layer": "",
+          "name": "RoadIntersectionPath",
+          "persistentUuid": "a5d5abcc-c6db-41b5-9eed-36f231b1422f",
+          "width": 0,
+          "x": -1536,
+          "y": 128,
+          "zOrder": 44,
+          "numberProperties": [],
+          "stringProperties": [],
+          "initialVariables": []
+        },
+        {
+          "angle": -90,
+          "customSize": false,
+          "height": 0,
+          "layer": "",
+          "name": "RoadIntersectionPath",
+          "persistentUuid": "e8b0b84a-62f4-453c-beca-103132724997",
+          "width": 0,
+          "x": 2304,
+          "y": 128,
+          "zOrder": 44,
+          "numberProperties": [],
+          "stringProperties": [],
+          "initialVariables": []
+        },
+        {
+          "angle": 0,
+          "customSize": false,
+          "height": 0,
+          "layer": "",
+          "name": "RoadBend",
+          "persistentUuid": "e22cf8f0-a157-4ec5-9e42-663c7f0af27b",
+          "width": 0,
+          "x": -1536,
+          "y": 1408,
+          "zOrder": 45,
+          "numberProperties": [],
+          "stringProperties": [],
+          "initialVariables": []
+        },
+        {
+          "angle": 90,
+          "customSize": false,
+          "height": 0,
+          "layer": "",
+          "name": "RoadBend",
+          "persistentUuid": "97f7e8a7-7047-4992-bcc9-dbf5214b5414",
+          "width": 0,
+          "x": -1536,
+          "y": -1152,
+          "zOrder": 45,
+          "numberProperties": [],
+          "stringProperties": [],
+          "initialVariables": []
+        },
+        {
+          "angle": 180,
+          "customSize": false,
+          "height": 0,
+          "layer": "",
+          "name": "RoadBend",
+          "persistentUuid": "7b9c3f8f-bc84-4467-8d28-9453b49ca4c8",
+          "width": 0,
+          "x": 2304,
+          "y": -1152,
+          "zOrder": 45,
+          "numberProperties": [],
+          "stringProperties": [],
+          "initialVariables": []
+        },
+        {
+          "angle": -90,
+          "customSize": false,
+          "height": 0,
+          "layer": "",
+          "name": "RoadBend",
+          "persistentUuid": "4776e313-1e93-4503-93d4-44d6489b243f",
+          "width": 0,
+          "x": 2304,
+          "y": 1408,
+          "zOrder": 45,
+          "numberProperties": [],
+          "stringProperties": [],
+          "initialVariables": []
+        },
+        {
+          "angle": 315,
+          "customSize": false,
+          "height": 0,
+          "layer": "",
+          "name": "CommonTree",
+          "persistentUuid": "52d896d4-1113-4367-94bf-9398965ca670",
+          "width": 0,
+          "x": -820,
+          "y": 674,
+          "zOrder": 46,
+          "numberProperties": [],
+          "stringProperties": [],
+          "initialVariables": []
+        },
+        {
+          "angle": 61,
+          "customSize": false,
+          "height": 0,
+          "layer": "",
+          "name": "CommonTree",
+          "persistentUuid": "10990ae3-86ae-4dc3-af87-9f72cfbed80e",
+          "width": 0,
+          "x": -230,
+          "y": 958,
+          "zOrder": 46,
+          "numberProperties": [],
+          "stringProperties": [],
+          "initialVariables": []
+        },
+        {
+          "angle": 274,
+          "customSize": false,
+          "height": 0,
+          "layer": "",
+          "name": "CommonTree",
+          "persistentUuid": "79f0c61e-6546-421b-bdb9-fef257734726",
+          "width": 0,
+          "x": -767,
+          "y": 1240,
+          "zOrder": 46,
+          "numberProperties": [],
+          "stringProperties": [],
+          "initialVariables": []
+        },
+        {
+          "angle": 57,
+          "customSize": false,
+          "height": 0,
+          "layer": "",
+          "name": "CommonTree",
+          "persistentUuid": "85b17f12-f2b5-45c8-b365-ddfbbef7f2a0",
+          "width": 0,
+          "x": 2086,
+          "y": -274,
+          "zOrder": 46,
+          "numberProperties": [],
+          "stringProperties": [],
+          "initialVariables": []
+        },
+        {
+          "angle": 299,
+          "customSize": false,
+          "height": 0,
+          "layer": "",
+          "name": "CommonTree",
+          "persistentUuid": "e45f54e4-ed85-40d5-98eb-a3d7c77285d3",
+          "width": 0,
+          "x": 1422,
+          "y": -136,
+          "zOrder": 46,
+          "numberProperties": [],
+          "stringProperties": [],
+          "initialVariables": []
+        },
+        {
+          "angle": 348,
+          "customSize": false,
+          "height": 0,
+          "layer": "",
+          "name": "CommonTree",
+          "persistentUuid": "da1362be-1156-4cd4-af4b-891bb73a43fa",
+          "width": 0,
+          "x": 1660,
+          "y": -579,
+          "zOrder": 46,
+          "numberProperties": [],
+          "stringProperties": [],
+          "initialVariables": []
+        },
+        {
+          "angle": 0,
+          "customSize": true,
+          "depth": 1,
+          "height": 5888,
+          "layer": "",
+          "locked": true,
+          "name": "Grass",
+          "persistentUuid": "05276cd6-3502-4c9d-a19e-58308fbbb417",
+          "sealed": true,
+          "width": 7936,
+          "x": -3328,
+          "y": -2560,
+          "z": -30,
+          "zOrder": 47,
+          "numberProperties": [],
+          "stringProperties": [],
+          "initialVariables": []
+        }
+      ],
+      "editionSettings": {
+        "grid": true,
+        "gridType": "rectangular",
+        "gridWidth": 128,
+        "gridHeight": 128,
+        "gridOffsetX": 0,
+        "gridOffsetY": 0,
+        "gridColor": 10401023,
+        "gridAlpha": 0.8,
+        "snap": true,
+        "zoomFactor": 0.20220707303602042,
+        "windowMask": false
+      }
+    },
+    {
+      "associatedLayout": "Game",
+      "name": "Touch controls",
+      "instances": [
+        {
+          "angle": 0,
+          "customSize": true,
+          "depth": 32,
+          "height": 160,
+          "layer": "MobileControls",
+          "name": "SteeringJoystick",
+          "persistentUuid": "d42256d9-4b75-48e5-8211-672b4a24b2ff",
+          "width": 160,
+          "x": 112,
+          "y": 592,
+          "zOrder": 25,
+          "numberProperties": [],
+          "stringProperties": [],
+          "initialVariables": []
+        },
+        {
+          "angle": 0,
+          "customSize": true,
+          "depth": 32,
+          "height": 160,
+          "layer": "MobileControls",
+          "name": "PedalJoystick",
+          "persistentUuid": "37b8a038-ac16-4e8b-8ed5-38629193ec1f",
+          "width": 160,
+          "x": 1168,
+          "y": 592,
+          "zOrder": 26,
+          "numberProperties": [],
+          "stringProperties": [],
+          "initialVariables": []
+        },
+        {
+          "angle": 0,
+          "customSize": true,
+          "depth": 1.0013908205841446,
+          "height": 720,
+          "layer": "MobileControls",
+          "name": "ScreenOrientationChecker",
+          "persistentUuid": "eee10984-d670-48cb-b98e-b9cbe189e3f6",
+          "width": 1280,
+          "x": 0,
+          "y": 0,
+          "zOrder": 27,
+          "numberProperties": [],
+          "stringProperties": [],
+          "initialVariables": []
+        }
+      ],
+      "editionSettings": {
+        "grid": false,
+        "gridType": "rectangular",
+        "gridWidth": 32,
+        "gridHeight": 32,
+        "gridOffsetX": 0,
+        "gridOffsetY": 0,
+        "gridColor": 10401023,
+        "gridAlpha": 0.8,
+        "snap": false,
+        "zoomFactor": 0.546875,
+        "windowMask": false
+      }
+    }
+  ]
+}
\ No newline at end of file
diff --git a/GDJS/tests/games/savestate/3dCarCoinHuntSaveLoad/assets/37 - Reason of the Itch.aac b/GDJS/tests/games/savestate/3dCarCoinHuntSaveLoad/assets/37 - Reason of the Itch.aac
new file mode 100644
index 000000000000..c5d2d969e2a8
Binary files /dev/null and b/GDJS/tests/games/savestate/3dCarCoinHuntSaveLoad/assets/37 - Reason of the Itch.aac differ
diff --git a/GDJS/tests/games/savestate/3dCarCoinHuntSaveLoad/assets/Arrow.png b/GDJS/tests/games/savestate/3dCarCoinHuntSaveLoad/assets/Arrow.png
new file mode 100644
index 000000000000..2ac8f28145e8
Binary files /dev/null and b/GDJS/tests/games/savestate/3dCarCoinHuntSaveLoad/assets/Arrow.png differ
diff --git a/GDJS/tests/games/savestate/3dCarCoinHuntSaveLoad/assets/CarEngine.mp3 b/GDJS/tests/games/savestate/3dCarCoinHuntSaveLoad/assets/CarEngine.mp3
new file mode 100644
index 000000000000..ecc9b93061e0
Binary files /dev/null and b/GDJS/tests/games/savestate/3dCarCoinHuntSaveLoad/assets/CarEngine.mp3 differ
diff --git a/GDJS/tests/games/savestate/3dCarCoinHuntSaveLoad/assets/Chango-Regular.ttf b/GDJS/tests/games/savestate/3dCarCoinHuntSaveLoad/assets/Chango-Regular.ttf
new file mode 100644
index 000000000000..62af5e8df90c
Binary files /dev/null and b/GDJS/tests/games/savestate/3dCarCoinHuntSaveLoad/assets/Chango-Regular.ttf differ
diff --git a/GDJS/tests/games/savestate/3dCarCoinHuntSaveLoad/assets/Coin.glb b/GDJS/tests/games/savestate/3dCarCoinHuntSaveLoad/assets/Coin.glb
new file mode 100644
index 000000000000..44b6f9e0adef
Binary files /dev/null and b/GDJS/tests/games/savestate/3dCarCoinHuntSaveLoad/assets/Coin.glb differ
diff --git a/GDJS/tests/games/savestate/3dCarCoinHuntSaveLoad/assets/CoinPickUP.wav b/GDJS/tests/games/savestate/3dCarCoinHuntSaveLoad/assets/CoinPickUP.wav
new file mode 100644
index 000000000000..332a351bb4f5
Binary files /dev/null and b/GDJS/tests/games/savestate/3dCarCoinHuntSaveLoad/assets/CoinPickUP.wav differ
diff --git a/GDJS/tests/games/savestate/3dCarCoinHuntSaveLoad/assets/Collision.wav b/GDJS/tests/games/savestate/3dCarCoinHuntSaveLoad/assets/Collision.wav
new file mode 100644
index 000000000000..5b89dda9865d
Binary files /dev/null and b/GDJS/tests/games/savestate/3dCarCoinHuntSaveLoad/assets/Collision.wav differ
diff --git a/GDJS/tests/games/savestate/3dCarCoinHuntSaveLoad/assets/Common Tree 1.glb b/GDJS/tests/games/savestate/3dCarCoinHuntSaveLoad/assets/Common Tree 1.glb
new file mode 100644
index 000000000000..558f4aae5afe
Binary files /dev/null and b/GDJS/tests/games/savestate/3dCarCoinHuntSaveLoad/assets/Common Tree 1.glb differ
diff --git a/GDJS/tests/games/savestate/3dCarCoinHuntSaveLoad/assets/Grass.png b/GDJS/tests/games/savestate/3dCarCoinHuntSaveLoad/assets/Grass.png
new file mode 100644
index 000000000000..7c209d127b66
Binary files /dev/null and b/GDJS/tests/games/savestate/3dCarCoinHuntSaveLoad/assets/Grass.png differ
diff --git a/GDJS/tests/games/savestate/3dCarCoinHuntSaveLoad/assets/Grey Button_Idle.png b/GDJS/tests/games/savestate/3dCarCoinHuntSaveLoad/assets/Grey Button_Idle.png
new file mode 100644
index 000000000000..efbb5d1314f9
Binary files /dev/null and b/GDJS/tests/games/savestate/3dCarCoinHuntSaveLoad/assets/Grey Button_Idle.png differ
diff --git a/GDJS/tests/games/savestate/3dCarCoinHuntSaveLoad/assets/Large Building A.glb b/GDJS/tests/games/savestate/3dCarCoinHuntSaveLoad/assets/Large Building A.glb
new file mode 100644
index 000000000000..40d719210f35
Binary files /dev/null and b/GDJS/tests/games/savestate/3dCarCoinHuntSaveLoad/assets/Large Building A.glb differ
diff --git a/GDJS/tests/games/savestate/3dCarCoinHuntSaveLoad/assets/Large Building F2.glb b/GDJS/tests/games/savestate/3dCarCoinHuntSaveLoad/assets/Large Building F2.glb
new file mode 100644
index 000000000000..495669b54d9d
Binary files /dev/null and b/GDJS/tests/games/savestate/3dCarCoinHuntSaveLoad/assets/Large Building F2.glb differ
diff --git a/GDJS/tests/games/savestate/3dCarCoinHuntSaveLoad/assets/Line light joystick border LeftRightt.png b/GDJS/tests/games/savestate/3dCarCoinHuntSaveLoad/assets/Line light joystick border LeftRightt.png
new file mode 100644
index 000000000000..6f48b720e4bf
Binary files /dev/null and b/GDJS/tests/games/savestate/3dCarCoinHuntSaveLoad/assets/Line light joystick border LeftRightt.png differ
diff --git a/GDJS/tests/games/savestate/3dCarCoinHuntSaveLoad/assets/Line light joystick border UpDown.png b/GDJS/tests/games/savestate/3dCarCoinHuntSaveLoad/assets/Line light joystick border UpDown.png
new file mode 100644
index 000000000000..ac71a868d9d6
Binary files /dev/null and b/GDJS/tests/games/savestate/3dCarCoinHuntSaveLoad/assets/Line light joystick border UpDown.png differ
diff --git a/GDJS/tests/games/savestate/3dCarCoinHuntSaveLoad/assets/Line light joystick thumb.png b/GDJS/tests/games/savestate/3dCarCoinHuntSaveLoad/assets/Line light joystick thumb.png
new file mode 100644
index 000000000000..ece43907528f
Binary files /dev/null and b/GDJS/tests/games/savestate/3dCarCoinHuntSaveLoad/assets/Line light joystick thumb.png differ
diff --git a/GDJS/tests/games/savestate/3dCarCoinHuntSaveLoad/assets/Orange Bubble Button_Hovered.png b/GDJS/tests/games/savestate/3dCarCoinHuntSaveLoad/assets/Orange Bubble Button_Hovered.png
new file mode 100644
index 000000000000..a95b2b6e3db5
Binary files /dev/null and b/GDJS/tests/games/savestate/3dCarCoinHuntSaveLoad/assets/Orange Bubble Button_Hovered.png differ
diff --git a/GDJS/tests/games/savestate/3dCarCoinHuntSaveLoad/assets/Orange Bubble Button_Idle.png b/GDJS/tests/games/savestate/3dCarCoinHuntSaveLoad/assets/Orange Bubble Button_Idle.png
new file mode 100644
index 000000000000..04edc5ba7e01
Binary files /dev/null and b/GDJS/tests/games/savestate/3dCarCoinHuntSaveLoad/assets/Orange Bubble Button_Idle.png differ
diff --git a/GDJS/tests/games/savestate/3dCarCoinHuntSaveLoad/assets/Orange Bubble Button_Pressed.png b/GDJS/tests/games/savestate/3dCarCoinHuntSaveLoad/assets/Orange Bubble Button_Pressed.png
new file mode 100644
index 000000000000..c6878f45bf4d
Binary files /dev/null and b/GDJS/tests/games/savestate/3dCarCoinHuntSaveLoad/assets/Orange Bubble Button_Pressed.png differ
diff --git a/GDJS/tests/games/savestate/3dCarCoinHuntSaveLoad/assets/Particle.png b/GDJS/tests/games/savestate/3dCarCoinHuntSaveLoad/assets/Particle.png
new file mode 100644
index 000000000000..80fb0a9e995a
Binary files /dev/null and b/GDJS/tests/games/savestate/3dCarCoinHuntSaveLoad/assets/Particle.png differ
diff --git a/GDJS/tests/games/savestate/3dCarCoinHuntSaveLoad/assets/Poppins-Medium.ttf b/GDJS/tests/games/savestate/3dCarCoinHuntSaveLoad/assets/Poppins-Medium.ttf
new file mode 100644
index 000000000000..6bcdcc27f22e
Binary files /dev/null and b/GDJS/tests/games/savestate/3dCarCoinHuntSaveLoad/assets/Poppins-Medium.ttf differ
diff --git a/GDJS/tests/games/savestate/3dCarCoinHuntSaveLoad/assets/Road Bend.glb b/GDJS/tests/games/savestate/3dCarCoinHuntSaveLoad/assets/Road Bend.glb
new file mode 100644
index 000000000000..60634303aff6
Binary files /dev/null and b/GDJS/tests/games/savestate/3dCarCoinHuntSaveLoad/assets/Road Bend.glb differ
diff --git a/GDJS/tests/games/savestate/3dCarCoinHuntSaveLoad/assets/Road Crossroad Path.glb b/GDJS/tests/games/savestate/3dCarCoinHuntSaveLoad/assets/Road Crossroad Path.glb
new file mode 100644
index 000000000000..366ff2780940
Binary files /dev/null and b/GDJS/tests/games/savestate/3dCarCoinHuntSaveLoad/assets/Road Crossroad Path.glb differ
diff --git a/GDJS/tests/games/savestate/3dCarCoinHuntSaveLoad/assets/Road Intersection Path.glb b/GDJS/tests/games/savestate/3dCarCoinHuntSaveLoad/assets/Road Intersection Path.glb
new file mode 100644
index 000000000000..7f4f7ce9db9f
Binary files /dev/null and b/GDJS/tests/games/savestate/3dCarCoinHuntSaveLoad/assets/Road Intersection Path.glb differ
diff --git a/GDJS/tests/games/savestate/3dCarCoinHuntSaveLoad/assets/Road Straight.glb b/GDJS/tests/games/savestate/3dCarCoinHuntSaveLoad/assets/Road Straight.glb
new file mode 100644
index 000000000000..5b1b9682f121
Binary files /dev/null and b/GDJS/tests/games/savestate/3dCarCoinHuntSaveLoad/assets/Road Straight.glb differ
diff --git a/GDJS/tests/games/savestate/3dCarCoinHuntSaveLoad/assets/Sedan Sports.glb b/GDJS/tests/games/savestate/3dCarCoinHuntSaveLoad/assets/Sedan Sports.glb
new file mode 100644
index 000000000000..72e82ed46896
Binary files /dev/null and b/GDJS/tests/games/savestate/3dCarCoinHuntSaveLoad/assets/Sedan Sports.glb differ
diff --git a/GDJS/tests/games/savestate/3dCarCoinHuntSaveLoad/assets/Suv Luxury5.glb b/GDJS/tests/games/savestate/3dCarCoinHuntSaveLoad/assets/Suv Luxury5.glb
new file mode 100644
index 000000000000..b340a4c79ae5
Binary files /dev/null and b/GDJS/tests/games/savestate/3dCarCoinHuntSaveLoad/assets/Suv Luxury5.glb differ
diff --git a/GDJS/tests/games/savestate/3dCarCoinHuntSaveLoad/assets/Van2.glb b/GDJS/tests/games/savestate/3dCarCoinHuntSaveLoad/assets/Van2.glb
new file mode 100644
index 000000000000..5f6a47cd9b98
Binary files /dev/null and b/GDJS/tests/games/savestate/3dCarCoinHuntSaveLoad/assets/Van2.glb differ
diff --git a/GDJS/tests/games/savestate/3dCarCoinHuntSaveLoad/assets/Yellow Button_Hovered.png b/GDJS/tests/games/savestate/3dCarCoinHuntSaveLoad/assets/Yellow Button_Hovered.png
new file mode 100644
index 000000000000..9bb3ab612e8f
Binary files /dev/null and b/GDJS/tests/games/savestate/3dCarCoinHuntSaveLoad/assets/Yellow Button_Hovered.png differ
diff --git a/GDJS/tests/games/savestate/3dCarCoinHuntSaveLoad/assets/Yellow Button_Idle.png b/GDJS/tests/games/savestate/3dCarCoinHuntSaveLoad/assets/Yellow Button_Idle.png
new file mode 100644
index 000000000000..86bf6cf37909
Binary files /dev/null and b/GDJS/tests/games/savestate/3dCarCoinHuntSaveLoad/assets/Yellow Button_Idle.png differ
diff --git a/GDJS/tests/games/savestate/3dCarCoinHuntSaveLoad/assets/Yellow Button_Pressed.png b/GDJS/tests/games/savestate/3dCarCoinHuntSaveLoad/assets/Yellow Button_Pressed.png
new file mode 100644
index 000000000000..ff0195f73dea
Binary files /dev/null and b/GDJS/tests/games/savestate/3dCarCoinHuntSaveLoad/assets/Yellow Button_Pressed.png differ
diff --git a/GDJS/tests/games/savestate/3dCarCoinHuntSaveLoad/assets/android-icon-144.png b/GDJS/tests/games/savestate/3dCarCoinHuntSaveLoad/assets/android-icon-144.png
new file mode 100644
index 000000000000..0219796a6d15
Binary files /dev/null and b/GDJS/tests/games/savestate/3dCarCoinHuntSaveLoad/assets/android-icon-144.png differ
diff --git a/GDJS/tests/games/savestate/3dCarCoinHuntSaveLoad/assets/android-icon-192.png b/GDJS/tests/games/savestate/3dCarCoinHuntSaveLoad/assets/android-icon-192.png
new file mode 100644
index 000000000000..5f8889dc6b71
Binary files /dev/null and b/GDJS/tests/games/savestate/3dCarCoinHuntSaveLoad/assets/android-icon-192.png differ
diff --git a/GDJS/tests/games/savestate/3dCarCoinHuntSaveLoad/assets/android-icon-36.png b/GDJS/tests/games/savestate/3dCarCoinHuntSaveLoad/assets/android-icon-36.png
new file mode 100644
index 000000000000..420f670be60b
Binary files /dev/null and b/GDJS/tests/games/savestate/3dCarCoinHuntSaveLoad/assets/android-icon-36.png differ
diff --git a/GDJS/tests/games/savestate/3dCarCoinHuntSaveLoad/assets/android-icon-48.png b/GDJS/tests/games/savestate/3dCarCoinHuntSaveLoad/assets/android-icon-48.png
new file mode 100644
index 000000000000..7302359cd1ff
Binary files /dev/null and b/GDJS/tests/games/savestate/3dCarCoinHuntSaveLoad/assets/android-icon-48.png differ
diff --git a/GDJS/tests/games/savestate/3dCarCoinHuntSaveLoad/assets/android-icon-72.png b/GDJS/tests/games/savestate/3dCarCoinHuntSaveLoad/assets/android-icon-72.png
new file mode 100644
index 000000000000..9872b161319c
Binary files /dev/null and b/GDJS/tests/games/savestate/3dCarCoinHuntSaveLoad/assets/android-icon-72.png differ
diff --git a/GDJS/tests/games/savestate/3dCarCoinHuntSaveLoad/assets/android-icon-96.png b/GDJS/tests/games/savestate/3dCarCoinHuntSaveLoad/assets/android-icon-96.png
new file mode 100644
index 000000000000..301980204793
Binary files /dev/null and b/GDJS/tests/games/savestate/3dCarCoinHuntSaveLoad/assets/android-icon-96.png differ
diff --git a/GDJS/tests/games/savestate/3dCarCoinHuntSaveLoad/assets/android-windowSplashScreenAnimatedIcon.png b/GDJS/tests/games/savestate/3dCarCoinHuntSaveLoad/assets/android-windowSplashScreenAnimatedIcon.png
new file mode 100644
index 000000000000..f16454555b3b
Binary files /dev/null and b/GDJS/tests/games/savestate/3dCarCoinHuntSaveLoad/assets/android-windowSplashScreenAnimatedIcon.png differ
diff --git a/GDJS/tests/games/savestate/3dCarCoinHuntSaveLoad/assets/desktop-icon-512.png b/GDJS/tests/games/savestate/3dCarCoinHuntSaveLoad/assets/desktop-icon-512.png
new file mode 100644
index 000000000000..7185eec3abdc
Binary files /dev/null and b/GDJS/tests/games/savestate/3dCarCoinHuntSaveLoad/assets/desktop-icon-512.png differ
diff --git a/GDJS/tests/games/savestate/3dCarCoinHuntSaveLoad/assets/ios-icon-100.png b/GDJS/tests/games/savestate/3dCarCoinHuntSaveLoad/assets/ios-icon-100.png
new file mode 100644
index 000000000000..3e8e936f7041
Binary files /dev/null and b/GDJS/tests/games/savestate/3dCarCoinHuntSaveLoad/assets/ios-icon-100.png differ
diff --git a/GDJS/tests/games/savestate/3dCarCoinHuntSaveLoad/assets/ios-icon-1024.png b/GDJS/tests/games/savestate/3dCarCoinHuntSaveLoad/assets/ios-icon-1024.png
new file mode 100644
index 000000000000..b33fe2f9275c
Binary files /dev/null and b/GDJS/tests/games/savestate/3dCarCoinHuntSaveLoad/assets/ios-icon-1024.png differ
diff --git a/GDJS/tests/games/savestate/3dCarCoinHuntSaveLoad/assets/ios-icon-114.png b/GDJS/tests/games/savestate/3dCarCoinHuntSaveLoad/assets/ios-icon-114.png
new file mode 100644
index 000000000000..b878ac4eaf1d
Binary files /dev/null and b/GDJS/tests/games/savestate/3dCarCoinHuntSaveLoad/assets/ios-icon-114.png differ
diff --git a/GDJS/tests/games/savestate/3dCarCoinHuntSaveLoad/assets/ios-icon-120.png b/GDJS/tests/games/savestate/3dCarCoinHuntSaveLoad/assets/ios-icon-120.png
new file mode 100644
index 000000000000..dec12b2bbda9
Binary files /dev/null and b/GDJS/tests/games/savestate/3dCarCoinHuntSaveLoad/assets/ios-icon-120.png differ
diff --git a/GDJS/tests/games/savestate/3dCarCoinHuntSaveLoad/assets/ios-icon-144.png b/GDJS/tests/games/savestate/3dCarCoinHuntSaveLoad/assets/ios-icon-144.png
new file mode 100644
index 000000000000..0219796a6d15
Binary files /dev/null and b/GDJS/tests/games/savestate/3dCarCoinHuntSaveLoad/assets/ios-icon-144.png differ
diff --git a/GDJS/tests/games/savestate/3dCarCoinHuntSaveLoad/assets/ios-icon-152.png b/GDJS/tests/games/savestate/3dCarCoinHuntSaveLoad/assets/ios-icon-152.png
new file mode 100644
index 000000000000..f5df95de031a
Binary files /dev/null and b/GDJS/tests/games/savestate/3dCarCoinHuntSaveLoad/assets/ios-icon-152.png differ
diff --git a/GDJS/tests/games/savestate/3dCarCoinHuntSaveLoad/assets/ios-icon-167.png b/GDJS/tests/games/savestate/3dCarCoinHuntSaveLoad/assets/ios-icon-167.png
new file mode 100644
index 000000000000..aeff63072ef7
Binary files /dev/null and b/GDJS/tests/games/savestate/3dCarCoinHuntSaveLoad/assets/ios-icon-167.png differ
diff --git a/GDJS/tests/games/savestate/3dCarCoinHuntSaveLoad/assets/ios-icon-180.png b/GDJS/tests/games/savestate/3dCarCoinHuntSaveLoad/assets/ios-icon-180.png
new file mode 100644
index 000000000000..0f6f5582087f
Binary files /dev/null and b/GDJS/tests/games/savestate/3dCarCoinHuntSaveLoad/assets/ios-icon-180.png differ
diff --git a/GDJS/tests/games/savestate/3dCarCoinHuntSaveLoad/assets/ios-icon-20.png b/GDJS/tests/games/savestate/3dCarCoinHuntSaveLoad/assets/ios-icon-20.png
new file mode 100644
index 000000000000..75effb78ee96
Binary files /dev/null and b/GDJS/tests/games/savestate/3dCarCoinHuntSaveLoad/assets/ios-icon-20.png differ
diff --git a/GDJS/tests/games/savestate/3dCarCoinHuntSaveLoad/assets/ios-icon-29.png b/GDJS/tests/games/savestate/3dCarCoinHuntSaveLoad/assets/ios-icon-29.png
new file mode 100644
index 000000000000..54bbaf2bf6e2
Binary files /dev/null and b/GDJS/tests/games/savestate/3dCarCoinHuntSaveLoad/assets/ios-icon-29.png differ
diff --git a/GDJS/tests/games/savestate/3dCarCoinHuntSaveLoad/assets/ios-icon-40.png b/GDJS/tests/games/savestate/3dCarCoinHuntSaveLoad/assets/ios-icon-40.png
new file mode 100644
index 000000000000..658552f15700
Binary files /dev/null and b/GDJS/tests/games/savestate/3dCarCoinHuntSaveLoad/assets/ios-icon-40.png differ
diff --git a/GDJS/tests/games/savestate/3dCarCoinHuntSaveLoad/assets/ios-icon-50.png b/GDJS/tests/games/savestate/3dCarCoinHuntSaveLoad/assets/ios-icon-50.png
new file mode 100644
index 000000000000..75c831d75ec1
Binary files /dev/null and b/GDJS/tests/games/savestate/3dCarCoinHuntSaveLoad/assets/ios-icon-50.png differ
diff --git a/GDJS/tests/games/savestate/3dCarCoinHuntSaveLoad/assets/ios-icon-57.png b/GDJS/tests/games/savestate/3dCarCoinHuntSaveLoad/assets/ios-icon-57.png
new file mode 100644
index 000000000000..fb3461969bb7
Binary files /dev/null and b/GDJS/tests/games/savestate/3dCarCoinHuntSaveLoad/assets/ios-icon-57.png differ
diff --git a/GDJS/tests/games/savestate/3dCarCoinHuntSaveLoad/assets/ios-icon-58.png b/GDJS/tests/games/savestate/3dCarCoinHuntSaveLoad/assets/ios-icon-58.png
new file mode 100644
index 000000000000..c70f82618849
Binary files /dev/null and b/GDJS/tests/games/savestate/3dCarCoinHuntSaveLoad/assets/ios-icon-58.png differ
diff --git a/GDJS/tests/games/savestate/3dCarCoinHuntSaveLoad/assets/ios-icon-60.png b/GDJS/tests/games/savestate/3dCarCoinHuntSaveLoad/assets/ios-icon-60.png
new file mode 100644
index 000000000000..1548dab40312
Binary files /dev/null and b/GDJS/tests/games/savestate/3dCarCoinHuntSaveLoad/assets/ios-icon-60.png differ
diff --git a/GDJS/tests/games/savestate/3dCarCoinHuntSaveLoad/assets/ios-icon-72.png b/GDJS/tests/games/savestate/3dCarCoinHuntSaveLoad/assets/ios-icon-72.png
new file mode 100644
index 000000000000..9872b161319c
Binary files /dev/null and b/GDJS/tests/games/savestate/3dCarCoinHuntSaveLoad/assets/ios-icon-72.png differ
diff --git a/GDJS/tests/games/savestate/3dCarCoinHuntSaveLoad/assets/ios-icon-76.png b/GDJS/tests/games/savestate/3dCarCoinHuntSaveLoad/assets/ios-icon-76.png
new file mode 100644
index 000000000000..c3446c0a159e
Binary files /dev/null and b/GDJS/tests/games/savestate/3dCarCoinHuntSaveLoad/assets/ios-icon-76.png differ
diff --git a/GDJS/tests/games/savestate/3dCarCoinHuntSaveLoad/assets/ios-icon-80.png b/GDJS/tests/games/savestate/3dCarCoinHuntSaveLoad/assets/ios-icon-80.png
new file mode 100644
index 000000000000..5c136cf9c9f0
Binary files /dev/null and b/GDJS/tests/games/savestate/3dCarCoinHuntSaveLoad/assets/ios-icon-80.png differ
diff --git a/GDJS/tests/games/savestate/3dCarCoinHuntSaveLoad/assets/ios-icon-87.png b/GDJS/tests/games/savestate/3dCarCoinHuntSaveLoad/assets/ios-icon-87.png
new file mode 100644
index 000000000000..618c196d4a3b
Binary files /dev/null and b/GDJS/tests/games/savestate/3dCarCoinHuntSaveLoad/assets/ios-icon-87.png differ
diff --git a/GDJS/tests/games/savestate/3dCarCoinHuntSaveLoad/assets/rotate-screen-icon.png b/GDJS/tests/games/savestate/3dCarCoinHuntSaveLoad/assets/rotate-screen-icon.png
new file mode 100644
index 000000000000..a7726dee34eb
Binary files /dev/null and b/GDJS/tests/games/savestate/3dCarCoinHuntSaveLoad/assets/rotate-screen-icon.png differ
diff --git a/GDJS/tests/games/savestate/3dCarCoinHuntSaveLoad/assets/rotate-screen-icon2.png b/GDJS/tests/games/savestate/3dCarCoinHuntSaveLoad/assets/rotate-screen-icon2.png
new file mode 100644
index 000000000000..a7726dee34eb
Binary files /dev/null and b/GDJS/tests/games/savestate/3dCarCoinHuntSaveLoad/assets/rotate-screen-icon2.png differ
diff --git a/GDJS/tests/games/savestate/3dCarCoinHuntSaveLoad/assets/thumbnail-game.png b/GDJS/tests/games/savestate/3dCarCoinHuntSaveLoad/assets/thumbnail-game.png
new file mode 100644
index 000000000000..63c352872626
Binary files /dev/null and b/GDJS/tests/games/savestate/3dCarCoinHuntSaveLoad/assets/thumbnail-game.png differ
diff --git a/GDJS/tests/games/savestate/3dCarCoinHuntSaveLoad/assets/tiled_Background Blue Grass.png b/GDJS/tests/games/savestate/3dCarCoinHuntSaveLoad/assets/tiled_Background Blue Grass.png
new file mode 100644
index 000000000000..6d6674a80f81
Binary files /dev/null and b/GDJS/tests/games/savestate/3dCarCoinHuntSaveLoad/assets/tiled_Background Blue Grass.png differ
diff --git a/GDJS/tests/games/savestate/SaveLoadExemple/Save & load exemple.json b/GDJS/tests/games/savestate/SaveLoadExemple/Save & load exemple.json
new file mode 100644
index 000000000000..46826cc073ba
--- /dev/null
+++ b/GDJS/tests/games/savestate/SaveLoadExemple/Save & load exemple.json	
@@ -0,0 +1,17887 @@
+{
+  "firstLayout": "",
+  "gdVersion": {
+    "build": 224,
+    "major": 5,
+    "minor": 5,
+    "revision": 0
+  },
+  "properties": {
+    "adaptGameResolutionAtRuntime": true,
+    "antialiasingMode": "MSAA",
+    "antialisingEnabledOnMobile": false,
+    "folderProject": false,
+    "orientation": "landscape",
+    "packageName": "com.example.gamename",
+    "pixelsRounding": false,
+    "projectUuid": "51b32edd-2db2-459c-ae66-bca135668818",
+    "scaleMode": "linear",
+    "sizeOnStartupMode": "",
+    "templateSlug": "starting-platformer",
+    "version": "1.0.0",
+    "name": "Save & load exemple",
+    "description": "A simple exemple of the single action save & load system, here it creates a basic checkpoint system with flags.\nIf you touch a flag, the game is saved, and if you drown into lava, your last save is reloaded. \n\nThe goal of this exemple is to showcase the most straightforward usage of the save & load actions.",
+    "author": "",
+    "windowWidth": 1280,
+    "windowHeight": 720,
+    "latestCompilationDirectory": "",
+    "maxFPS": 60,
+    "minFPS": 20,
+    "verticalSync": false,
+    "platformSpecificAssets": {
+      "android-icon-144": "",
+      "android-icon-192": "",
+      "android-icon-36": "",
+      "android-icon-48": "",
+      "android-icon-72": "",
+      "android-icon-96": "",
+      "android-windowSplashScreenAnimatedIcon": "",
+      "desktop-icon-512": "",
+      "ios-icon-100": "",
+      "ios-icon-1024": "",
+      "ios-icon-114": "",
+      "ios-icon-120": "",
+      "ios-icon-144": "",
+      "ios-icon-152": "",
+      "ios-icon-167": "",
+      "ios-icon-180": "",
+      "ios-icon-20": "",
+      "ios-icon-29": "",
+      "ios-icon-40": "",
+      "ios-icon-50": "",
+      "ios-icon-57": "",
+      "ios-icon-58": "",
+      "ios-icon-60": "",
+      "ios-icon-72": "",
+      "ios-icon-76": "",
+      "ios-icon-80": "",
+      "ios-icon-87": ""
+    },
+    "loadingScreen": {
+      "backgroundColor": 0,
+      "backgroundFadeInDuration": 0.2,
+      "backgroundImageResourceName": "",
+      "gdevelopLogoStyle": "light",
+      "logoAndProgressFadeInDuration": 0.2,
+      "logoAndProgressLogoFadeInDelay": 0,
+      "minDuration": 1.5,
+      "progressBarColor": 16777215,
+      "progressBarHeight": 20,
+      "progressBarMaxWidth": 200,
+      "progressBarMinWidth": 40,
+      "progressBarWidthPercent": 30,
+      "showGDevelopSplash": true,
+      "showProgressBar": true
+    },
+    "watermark": {
+      "placement": "bottom-left",
+      "showWatermark": true
+    },
+    "authorIds": [],
+    "authorUsernames": [],
+    "categories": [],
+    "playableDevices": [],
+    "extensionProperties": [],
+    "platforms": [
+      {
+        "name": "GDevelop JS platform"
+      }
+    ],
+    "currentPlatform": "GDevelop JS platform"
+  },
+  "resources": {
+    "resources": [
+      {
+        "file": "assets/StartingPlayer.png",
+        "kind": "image",
+        "metadata": "",
+        "name": "assets\\StartingPlayer.png",
+        "smoothed": true,
+        "userAdded": false
+      },
+      {
+        "file": "assets/StartingGround.png",
+        "kind": "image",
+        "metadata": "",
+        "name": "assets\\StartingGround.png",
+        "smoothed": true,
+        "userAdded": false
+      },
+      {
+        "file": "assets/Background.png",
+        "kind": "image",
+        "metadata": "",
+        "name": "assets\\Background.png",
+        "smoothed": true,
+        "userAdded": false
+      },
+      {
+        "file": "assets/StartingCoin.png",
+        "kind": "image",
+        "metadata": "",
+        "name": "assets\\StartingCoin.png",
+        "smoothed": true,
+        "userAdded": false
+      },
+      {
+        "file": "assets/Top arrow button.png",
+        "kind": "image",
+        "metadata": "",
+        "name": "Top arrow button.png",
+        "smoothed": true,
+        "userAdded": false,
+        "origin": {
+          "identifier": "https://asset-resources.gdevelop.io/public-resources/On-Screen Controls/Sprites/Flat Dark/e3943e1b23ceb90f00fc5e7c0481c5147b983fdc5397fb3690e102e53ce72d4f_Top arrow button.png",
+          "name": "Top arrow button.png"
+        }
+      },
+      {
+        "file": "assets/Flat dark joystick border.png",
+        "kind": "image",
+        "metadata": "",
+        "name": "Flat dark joystick border.png",
+        "smoothed": true,
+        "userAdded": false,
+        "origin": {
+          "identifier": "https://asset-resources.gdevelop.io/public-resources/Multitouch joysticks/1db606cabd7372d1494ba5934bc25bcdd72f5a213c4a27509be57c3f4d5aecca_Flat dark joystick border.png",
+          "name": "Flat dark joystick border.png"
+        }
+      },
+      {
+        "file": "assets/Flat dark joystick thumb.png",
+        "kind": "image",
+        "metadata": "",
+        "name": "Flat dark joystick thumb.png",
+        "smoothed": true,
+        "userAdded": false,
+        "origin": {
+          "identifier": "https://asset-resources.gdevelop.io/public-resources/Multitouch joysticks/10167ade22c4a6b48324e6c1d1bd6dc74179d7bed0775890903f418b4a05c8a1_Flat dark joystick thumb.png",
+          "name": "Flat dark joystick thumb.png"
+        }
+      },
+      {
+        "file": "assets/PickupCoin.wav",
+        "kind": "audio",
+        "metadata": "{\"extension\":\".wav\",\"jfxr\":{\"data\":\"{\\\"_version\\\":1,\\\"_name\\\":\\\"Jump 1\\\",\\\"_locked\\\":[],\\\"sampleRate\\\":44100,\\\"attack\\\":0,\\\"sustain\\\":0.05,\\\"sustainPunch\\\":20,\\\"decay\\\":0.3,\\\"tremoloDepth\\\":0,\\\"tremoloFrequency\\\":10,\\\"frequency\\\":400,\\\"frequencySweep\\\":0,\\\"frequencyDeltaSweep\\\":0,\\\"repeatFrequency\\\":0,\\\"frequencyJump1Onset\\\":15,\\\"frequencyJump1Amount\\\":25,\\\"frequencyJump2Onset\\\":66,\\\"frequencyJump2Amount\\\":0,\\\"harmonics\\\":0,\\\"harmonicsFalloff\\\":0.5,\\\"waveform\\\":\\\"whistle\\\",\\\"interpolateNoise\\\":true,\\\"vibratoDepth\\\":0,\\\"vibratoFrequency\\\":10,\\\"squareDuty\\\":85,\\\"squareDutySweep\\\":35,\\\"flangerOffset\\\":0,\\\"flangerOffsetSweep\\\":0,\\\"bitCrush\\\":16,\\\"bitCrushSweep\\\":0,\\\"lowPassCutoff\\\":22050,\\\"lowPassCutoffSweep\\\":0,\\\"highPassCutoff\\\":0,\\\"highPassCutoffSweep\\\":0,\\\"compression\\\":1,\\\"normalization\\\":true,\\\"amplification\\\":100}\",\"name\":\"PickupCoin\"},\"localFilePath\":\"assets/PickupCoin.wav\"}",
+        "name": "PickupCoin",
+        "preloadAsMusic": false,
+        "preloadAsSound": true,
+        "preloadInCache": false,
+        "userAdded": false
+      },
+      {
+        "file": "assets/Flag Blue.png",
+        "kind": "image",
+        "metadata": "",
+        "name": "Flag Blue.png",
+        "smoothed": true,
+        "userAdded": false,
+        "origin": {
+          "identifier": "https://asset-resources.gdevelop.io/public-resources/Breakable Physics/Flag/0aa9551296f8ec80d9b1267e686f7b7a49cfd578e87b24e3bab71664098f582c_Flag Blue.png",
+          "name": "Flag Blue.png"
+        }
+      },
+      {
+        "file": "assets/LightGlow.png",
+        "kind": "image",
+        "metadata": "",
+        "name": "LightGlow.png",
+        "smoothed": true,
+        "userAdded": false,
+        "origin": {
+          "identifier": "https://asset-resources.gdevelop.io/public-resources/Particles/GDevelop/c17e2d500015e598624b763f4942b26e3aac96efca094cd20257a61e15b27fcb_LightGlow.png",
+          "name": "LightGlow.png"
+        }
+      },
+      {
+        "file": "assets/Coins 8.aac",
+        "kind": "audio",
+        "metadata": "",
+        "name": "c52f1dacc263a2a6dc94e712a2a148f909b73372fa8e0622cb237fdc6a72fd6c_Coins 8.aac",
+        "preloadAsMusic": false,
+        "preloadAsSound": false,
+        "preloadInCache": false,
+        "userAdded": false,
+        "origin": {
+          "identifier": "https://asset-resources.gdevelop.io/public-resources/Arcade/Sound effects/Coins/c52f1dacc263a2a6dc94e712a2a148f909b73372fa8e0622cb237fdc6a72fd6c_Coins 8.aac",
+          "name": "gdevelop-asset-store"
+        }
+      },
+      {
+        "file": "assets/NewTiledSprite.png",
+        "kind": "image",
+        "metadata": "{\"extension\":\".png\",\"pskl\":{}}",
+        "name": "NewTiledSprite",
+        "smoothed": true,
+        "userAdded": false
+      },
+      {
+        "file": "assets/Large sign.png",
+        "kind": "image",
+        "metadata": "",
+        "name": "Large sign.png",
+        "smoothed": true,
+        "userAdded": false,
+        "origin": {
+          "identifier": "https://asset-resources.gdevelop.io/public-resources/Abstract Platformer Pack (370 assets)/PNG/Other/184caf8844c91aff060202e8af13f21e6a7e2a70f4222518836b3015ce3cfa5b_Large sign.png",
+          "name": "Large sign.png"
+        }
+      },
+      {
+        "file": "assets/04 - Castle Nosferatu (Sega-style FM Synth Remix).aac",
+        "kind": "audio",
+        "metadata": "",
+        "name": "dfcdb6ef0ded864b301b5cc57940b190e803b1ea53d057338043f3b8bcd2a138_04 - Castle Nosferatu (Sega-style FM Synth Remix).aac",
+        "preloadAsMusic": false,
+        "preloadAsSound": false,
+        "preloadInCache": false,
+        "userAdded": false,
+        "origin": {
+          "identifier": "https://asset-resources.gdevelop.io/public-resources/Ragnar Random/Fakebit Chiptune Music/dfcdb6ef0ded864b301b5cc57940b190e803b1ea53d057338043f3b8bcd2a138_04 - Castle Nosferatu (Sega-style FM Synth Remix).aac",
+          "name": "gdevelop-asset-store"
+        }
+      }
+    ],
+    "resourceFolders": []
+  },
+  "objects": [],
+  "objectsFolderStructure": {
+    "folderName": "__ROOT"
+  },
+  "objectsGroups": [],
+  "variables": [],
+  "layouts": [
+    {
+      "b": 255,
+      "disableInputWhenNotFocused": true,
+      "mangledName": "Game_32Scene",
+      "name": "Game Scene",
+      "r": 255,
+      "standardSortMethod": true,
+      "stopSoundsOnStartup": true,
+      "title": "",
+      "v": 255,
+      "uiSettings": {
+        "grid": false,
+        "gridType": "rectangular",
+        "gridWidth": 32,
+        "gridHeight": 32,
+        "gridOffsetX": 0,
+        "gridOffsetY": 0,
+        "gridColor": 10401023,
+        "gridAlpha": 0.8,
+        "snap": false,
+        "zoomFactor": 0.4633111824514028,
+        "windowMask": false
+      },
+      "objectsGroups": [],
+      "variables": [],
+      "instances": [
+        {
+          "angle": 0,
+          "customSize": false,
+          "height": 78.99999999999999,
+          "keepRatio": true,
+          "layer": "",
+          "name": "Platformer_Character",
+          "persistentUuid": "4ded1d81-e820-41d3-868a-3f314e5850bd",
+          "width": 79,
+          "x": 500,
+          "y": 466,
+          "zOrder": 31,
+          "numberProperties": [],
+          "stringProperties": [],
+          "initialVariables": []
+        },
+        {
+          "angle": 0,
+          "customSize": true,
+          "depth": 1,
+          "height": 720,
+          "layer": "Background",
+          "locked": true,
+          "name": "Background",
+          "persistentUuid": "9d4a2623-239c-44d1-95a4-076307aa3170",
+          "width": 1280,
+          "x": 0,
+          "y": 0,
+          "zOrder": -10,
+          "numberProperties": [],
+          "stringProperties": [],
+          "initialVariables": []
+        },
+        {
+          "angle": 0,
+          "customSize": false,
+          "height": 0,
+          "keepRatio": true,
+          "layer": "MobileControls",
+          "name": "Joystick",
+          "persistentUuid": "855d809a-f954-4072-86d5-1d6f0b24a066",
+          "width": 0,
+          "x": 176,
+          "y": 576,
+          "zOrder": 2,
+          "numberProperties": [],
+          "stringProperties": [],
+          "initialVariables": []
+        },
+        {
+          "angle": 0,
+          "customSize": true,
+          "depth": 0,
+          "height": 120,
+          "keepRatio": true,
+          "layer": "MobileControls",
+          "name": "JumpButton",
+          "persistentUuid": "b1d437ce-f3b4-4799-875e-bb51f731cef0",
+          "width": 120,
+          "x": 1104,
+          "y": 576,
+          "zOrder": 3,
+          "numberProperties": [],
+          "stringProperties": [],
+          "initialVariables": []
+        },
+        {
+          "angle": 0,
+          "customSize": true,
+          "depth": 1,
+          "height": 64,
+          "keepRatio": true,
+          "layer": "",
+          "name": "Platform_Ground",
+          "persistentUuid": "1c4f7ab9-04d4-4cee-8572-96199ce1245c",
+          "width": 864,
+          "x": 64,
+          "y": 544,
+          "zOrder": 5,
+          "numberProperties": [],
+          "stringProperties": [],
+          "initialVariables": []
+        },
+        {
+          "angle": 0,
+          "customSize": true,
+          "depth": 1,
+          "height": 64,
+          "keepRatio": true,
+          "layer": "",
+          "name": "Platform_Ground",
+          "persistentUuid": "672ee5c6-d2bb-4ae4-bc77-78d15064dd68",
+          "width": 288,
+          "x": 1588,
+          "y": 529,
+          "zOrder": 5,
+          "numberProperties": [],
+          "stringProperties": [],
+          "initialVariables": []
+        },
+        {
+          "angle": 0,
+          "customSize": true,
+          "depth": 1,
+          "height": 64,
+          "keepRatio": true,
+          "layer": "",
+          "name": "Platform_Ground",
+          "persistentUuid": "e595746c-dfaa-4c91-a246-d36934b5a906",
+          "width": 288,
+          "x": 1080,
+          "y": 534,
+          "zOrder": 5,
+          "numberProperties": [],
+          "stringProperties": [],
+          "initialVariables": []
+        },
+        {
+          "angle": 0,
+          "customSize": true,
+          "depth": 1,
+          "height": 720,
+          "layer": "Background",
+          "locked": true,
+          "name": "Background",
+          "persistentUuid": "18b633b4-68e0-4420-85e8-766c67f4ad90",
+          "width": 1280,
+          "x": 10,
+          "y": 10,
+          "zOrder": -10,
+          "numberProperties": [],
+          "stringProperties": [],
+          "initialVariables": []
+        },
+        {
+          "angle": 0,
+          "customSize": true,
+          "depth": 1,
+          "height": 64,
+          "keepRatio": true,
+          "layer": "",
+          "name": "Platform_Ground",
+          "persistentUuid": "78286b2c-58ac-46ee-8324-67e5949d9cc3",
+          "width": 288,
+          "x": 2737,
+          "y": 517,
+          "zOrder": 5,
+          "numberProperties": [],
+          "stringProperties": [],
+          "initialVariables": []
+        },
+        {
+          "angle": 0,
+          "customSize": false,
+          "height": 0,
+          "keepRatio": true,
+          "layer": "",
+          "name": "FlagBlue",
+          "persistentUuid": "6c44e219-ac42-4abd-89fa-519914861a99",
+          "width": 0,
+          "x": 1196,
+          "y": 462,
+          "zOrder": 7,
+          "numberProperties": [],
+          "stringProperties": [],
+          "initialVariables": []
+        },
+        {
+          "angle": 0,
+          "customSize": true,
+          "depth": 1,
+          "height": 64,
+          "keepRatio": true,
+          "layer": "",
+          "name": "Platform_Ground",
+          "persistentUuid": "4267c4c2-2115-4233-9322-dff352e0ef53",
+          "width": 74,
+          "x": 1441,
+          "y": 535,
+          "zOrder": 5,
+          "numberProperties": [],
+          "stringProperties": [],
+          "initialVariables": []
+        },
+        {
+          "angle": 0,
+          "customSize": true,
+          "depth": 1,
+          "height": 870,
+          "keepRatio": true,
+          "layer": "",
+          "name": "NewTiledSprite",
+          "persistentUuid": "12bea79e-0a5c-41fd-94cb-ef541f916d8b",
+          "width": 4767,
+          "x": -819,
+          "y": 637,
+          "zOrder": 17,
+          "numberProperties": [],
+          "stringProperties": [],
+          "initialVariables": []
+        },
+        {
+          "angle": 0,
+          "customSize": false,
+          "height": 0,
+          "keepRatio": true,
+          "layer": "",
+          "name": "FlagBlue",
+          "persistentUuid": "14ba131e-f137-4724-b82d-3c4e9d8ae0ca",
+          "width": 0,
+          "x": 1717,
+          "y": 457,
+          "zOrder": 19,
+          "numberProperties": [],
+          "stringProperties": [],
+          "initialVariables": []
+        },
+        {
+          "angle": 0,
+          "customSize": true,
+          "depth": 1,
+          "height": 64,
+          "keepRatio": true,
+          "layer": "",
+          "name": "Platform_Ground",
+          "persistentUuid": "d04ed369-cb9d-4836-a26a-80ca84dd2147",
+          "width": 74,
+          "x": 2074,
+          "y": 525,
+          "zOrder": 5,
+          "numberProperties": [],
+          "stringProperties": [],
+          "initialVariables": []
+        },
+        {
+          "angle": 0,
+          "customSize": true,
+          "depth": 1,
+          "height": 64,
+          "keepRatio": true,
+          "layer": "",
+          "name": "Platform_Ground",
+          "persistentUuid": "50fed9ae-f21b-402d-a28f-10ded8bddc0f",
+          "width": 74,
+          "x": 2383,
+          "y": 528,
+          "zOrder": 5,
+          "numberProperties": [],
+          "stringProperties": [],
+          "initialVariables": []
+        },
+        {
+          "angle": 0,
+          "customSize": true,
+          "depth": 1,
+          "height": 115,
+          "keepRatio": true,
+          "layer": "",
+          "name": "LargeSign",
+          "persistentUuid": "2b1658c7-c224-4bf1-b8a1-7b9f98b195e2",
+          "width": 153,
+          "x": 285,
+          "y": 429,
+          "zOrder": 20,
+          "numberProperties": [],
+          "stringProperties": [],
+          "initialVariables": []
+        },
+        {
+          "angle": 0,
+          "customSize": true,
+          "depth": 1,
+          "height": 23,
+          "keepRatio": true,
+          "layer": "",
+          "name": "NewText",
+          "persistentUuid": "2032a6b6-85c4-4a1b-915b-ed28743d3efd",
+          "width": 191,
+          "x": 261,
+          "y": 457,
+          "zOrder": 21,
+          "numberProperties": [],
+          "stringProperties": [],
+          "initialVariables": []
+        },
+        {
+          "angle": 0,
+          "customSize": true,
+          "depth": 1,
+          "height": 186,
+          "keepRatio": true,
+          "layer": "",
+          "name": "LargeSign",
+          "persistentUuid": "9ccd79c9-45ba-4c2b-82bb-bd12bfeebfe7",
+          "width": 282,
+          "x": 2739,
+          "y": 334,
+          "zOrder": 0,
+          "numberProperties": [],
+          "stringProperties": [],
+          "initialVariables": []
+        },
+        {
+          "angle": 0,
+          "customSize": true,
+          "depth": 1,
+          "height": 25,
+          "keepRatio": true,
+          "layer": "",
+          "name": "NewText2",
+          "persistentUuid": "d8cc938a-22ad-48b0-b30e-9ddc2758936c",
+          "width": 132,
+          "x": 2819,
+          "y": 362,
+          "zOrder": 1,
+          "numberProperties": [],
+          "stringProperties": [],
+          "initialVariables": []
+        }
+      ],
+      "objects": [
+        {
+          "adaptCollisionMaskAutomatically": false,
+          "assetStoreId": "",
+          "name": "Platformer_Character",
+          "type": "Sprite",
+          "updateIfNotVisible": false,
+          "variables": [],
+          "effects": [],
+          "behaviors": [
+            {
+              "name": "PlatformerMultitouchMapper",
+              "type": "SpriteMultitouchJoystick::PlatformerMultitouchMapper",
+              "Property": "PlatformerObject",
+              "ControllerIdentifier": 1,
+              "JoystickIdentifier": "Primary",
+              "JumpButton": "A"
+            },
+            {
+              "name": "PlatformerObject",
+              "type": "PlatformBehavior::PlatformerObjectBehavior",
+              "acceleration": 1000,
+              "canGoDownFromJumpthru": true,
+              "canGrabPlatforms": false,
+              "canGrabWithoutMoving": false,
+              "deceleration": 1500,
+              "gravity": 1050,
+              "ignoreDefaultControls": false,
+              "jumpSpeed": 717,
+              "jumpSustainTime": 0.3,
+              "ladderClimbingSpeed": 200,
+              "maxFallingSpeed": 800,
+              "maxSpeed": 400,
+              "slopeMaxAngle": 60,
+              "useLegacyTrajectory": false,
+              "xGrabTolerance": 10,
+              "yGrabOffset": 0
+            },
+            {
+              "name": "SmoothCamera",
+              "type": "SmoothCamera::SmoothCamera",
+              "LeftwardSpeed": 0.9,
+              "RightwardSpeed": 0.9,
+              "UpwardSpeed": 0.95,
+              "DownwardSpeed": 0.95,
+              "FollowOnX": true,
+              "FollowOnY": true,
+              "FollowFreeAreaLeft": 0,
+              "FollowFreeAreaRight": 0,
+              "FollowFreeAreaTop": 0,
+              "FollowFreeAreaBottom": 0,
+              "CameraOffsetX": 0,
+              "CameraOffsetY": 0,
+              "CameraDelay": 0,
+              "ForecastTime": 0,
+              "ForecastHistoryDuration": 0,
+              "LogLeftwardSpeed": 2.0247e-320,
+              "LogRightwardSpeed": 2.0247e-320,
+              "LogDownwardSpeed": 2.0247e-320,
+              "LogUpwardSpeed": 2.0247e-320,
+              "DelayedCenterX": 2.0247e-320,
+              "DelayedCenterY": 2.0247e-320,
+              "ForecastHistoryMeanX": 2.0247e-320,
+              "ForecastHistoryMeanY": 2.0247e-320,
+              "ForecastHistoryVarianceX": 2.0247e-320,
+              "ForecastHistoryCovariance": 2.0247e-320,
+              "ForecastHistoryLinearA": 2.0247e-320,
+              "ForecastHistoryLinearB": 2.0247e-320,
+              "ForecastedX": 2.0247e-320,
+              "ForecastedY": 2.0247e-320,
+              "ProjectedNewestX": 2.0247e-320,
+              "ProjectedNewestY": 2.0247e-320,
+              "ProjectedOldestX": 2.0247e-320,
+              "ProjectedOldestY": 2.0247e-320,
+              "ForecastHistoryVarianceY": 2.0247e-320,
+              "Index": 2.0247e-320,
+              "CameraDelayCatchUpSpeed": 0,
+              "CameraExtraDelay": 2.0247e-320,
+              "WaitingSpeedXMax": 2.0247e-320,
+              "WaitingSpeedYMax": 2.0247e-320,
+              "WaitingEnd": 2.0247e-320,
+              "CameraDelayCatchUpDuration": 2.0247e-320,
+              "LeftwardSpeedMax": 9000,
+              "RightwardSpeedMax": 9000,
+              "UpwardSpeedMax": 9000,
+              "DownwardSpeedMax": 9000,
+              "OldX": 9000.000000007454,
+              "OldY": 9000.000000007454,
+              "IsCalledManually": false
+            }
+          ],
+          "animations": [
+            {
+              "name": "",
+              "useMultipleDirections": false,
+              "directions": [
+                {
+                  "looping": false,
+                  "timeBetweenFrames": 0.08,
+                  "sprites": [
+                    {
+                      "hasCustomCollisionMask": true,
+                      "image": "assets\\StartingPlayer.png",
+                      "points": [],
+                      "originPoint": {
+                        "name": "origine",
+                        "x": 0,
+                        "y": 0
+                      },
+                      "centerPoint": {
+                        "automatic": true,
+                        "name": "centre",
+                        "x": 0,
+                        "y": 0
+                      },
+                      "customCollisionMask": [
+                        [
+                          {
+                            "x": 0,
+                            "y": 2
+                          },
+                          {
+                            "x": 64,
+                            "y": 2
+                          },
+                          {
+                            "x": 64,
+                            "y": 64
+                          },
+                          {
+                            "x": 0,
+                            "y": 64
+                          }
+                        ]
+                      ]
+                    }
+                  ]
+                }
+              ]
+            }
+          ]
+        },
+        {
+          "assetStoreId": "",
+          "height": 64,
+          "name": "Background",
+          "texture": "assets\\Background.png",
+          "type": "TiledSpriteObject::TiledSprite",
+          "width": 64,
+          "variables": [],
+          "effects": [],
+          "behaviors": []
+        },
+        {
+          "adaptCollisionMaskAutomatically": true,
+          "assetStoreId": "",
+          "name": "Coins",
+          "type": "Sprite",
+          "updateIfNotVisible": false,
+          "variables": [],
+          "effects": [],
+          "behaviors": [],
+          "animations": [
+            {
+              "name": "",
+              "useMultipleDirections": false,
+              "directions": [
+                {
+                  "looping": false,
+                  "timeBetweenFrames": 0.08,
+                  "sprites": [
+                    {
+                      "hasCustomCollisionMask": true,
+                      "image": "assets\\StartingCoin.png",
+                      "points": [],
+                      "originPoint": {
+                        "name": "origine",
+                        "x": 0,
+                        "y": 0
+                      },
+                      "centerPoint": {
+                        "automatic": true,
+                        "name": "centre",
+                        "x": 0,
+                        "y": 0
+                      },
+                      "customCollisionMask": [
+                        [
+                          {
+                            "x": 0,
+                            "y": 0
+                          },
+                          {
+                            "x": 19,
+                            "y": 0
+                          },
+                          {
+                            "x": 19,
+                            "y": 18
+                          },
+                          {
+                            "x": 0,
+                            "y": 18
+                          }
+                        ]
+                      ]
+                    }
+                  ]
+                }
+              ]
+            }
+          ]
+        },
+        {
+          "adaptCollisionMaskAutomatically": true,
+          "assetStoreId": "9c727020616afdd6ba786b8af206a90481f07db0ca175ed6a4cc5b7e01c66d06",
+          "name": "JumpButton",
+          "type": "Sprite",
+          "updateIfNotVisible": false,
+          "variables": [],
+          "effects": [],
+          "behaviors": [
+            {
+              "name": "ButtonFSM",
+              "type": "ButtonStates::ButtonFSM",
+              "ShouldCheckHovering": true,
+              "State": "Idle",
+              "TouchId": 0,
+              "TouchIsInside": false,
+              "MouseIsInside": false,
+              "Index": 2.0247e-320
+            },
+            {
+              "name": "ButtonScaleTween",
+              "type": "ButtonStates::ButtonScaleTween",
+              "Scale": "Scale",
+              "ButtonFSM": "ButtonFSM",
+              "Tween": "Tween",
+              "IdleScale": 1.5,
+              "FocusedScale": 1.45,
+              "FadeInDuration": 0.05,
+              "FadeOutDuration": 0.1,
+              "PressedScale": 1.45,
+              "FadeInEasing": "easeInOutQuad",
+              "FadeOutEasing": "easeInOutQuad",
+              "PreviousState": "Idle"
+            },
+            {
+              "name": "MultitouchButton",
+              "type": "SpriteMultitouchJoystick::MultitouchButton",
+              "ControllerIdentifier": 1,
+              "ButtonIdentifier": "A",
+              "TouchId": 0,
+              "TouchIndex": 2.0247e-320,
+              "IsReleased": false
+            },
+            {
+              "name": "Tween",
+              "type": "Tween::TweenBehavior"
+            }
+          ],
+          "animations": [
+            {
+              "name": "",
+              "useMultipleDirections": false,
+              "directions": [
+                {
+                  "looping": true,
+                  "timeBetweenFrames": 0.025,
+                  "sprites": [
+                    {
+                      "hasCustomCollisionMask": true,
+                      "image": "Top arrow button.png",
+                      "points": [],
+                      "originPoint": {
+                        "name": "origine",
+                        "x": 40,
+                        "y": 40
+                      },
+                      "centerPoint": {
+                        "automatic": true,
+                        "name": "centre",
+                        "x": 0,
+                        "y": 0
+                      },
+                      "customCollisionMask": [
+                        [
+                          {
+                            "x": 0,
+                            "y": 0
+                          },
+                          {
+                            "x": 80,
+                            "y": 0
+                          },
+                          {
+                            "x": 80,
+                            "y": 80
+                          },
+                          {
+                            "x": 0,
+                            "y": 80
+                          }
+                        ]
+                      ]
+                    }
+                  ]
+                }
+              ]
+            }
+          ]
+        },
+        {
+          "assetStoreId": "e71bd69f896d6c7531b48c65ceb5da25071d4fbdeb518aeceecba8d21f34ed8d",
+          "name": "Joystick",
+          "type": "SpriteMultitouchJoystick::SpriteMultitouchJoystick",
+          "variables": [],
+          "effects": [],
+          "behaviors": [],
+          "content": {},
+          "childrenContent": {
+            "Border": {
+              "adaptCollisionMaskAutomatically": false,
+              "updateIfNotVisible": false,
+              "animations": [
+                {
+                  "name": "Idle",
+                  "useMultipleDirections": false,
+                  "directions": [
+                    {
+                      "looping": false,
+                      "timeBetweenFrames": 0.08,
+                      "sprites": [
+                        {
+                          "hasCustomCollisionMask": false,
+                          "image": "Flat dark joystick border.png",
+                          "points": [],
+                          "originPoint": {
+                            "name": "origine",
+                            "x": 0,
+                            "y": 0
+                          },
+                          "centerPoint": {
+                            "automatic": true,
+                            "name": "centre",
+                            "x": 0,
+                            "y": 0
+                          },
+                          "customCollisionMask": []
+                        }
+                      ]
+                    }
+                  ]
+                }
+              ]
+            },
+            "Thumb": {
+              "adaptCollisionMaskAutomatically": false,
+              "updateIfNotVisible": false,
+              "animations": [
+                {
+                  "name": "Idle",
+                  "useMultipleDirections": false,
+                  "directions": [
+                    {
+                      "looping": false,
+                      "timeBetweenFrames": 0.08,
+                      "sprites": [
+                        {
+                          "hasCustomCollisionMask": false,
+                          "image": "Flat dark joystick thumb.png",
+                          "points": [],
+                          "originPoint": {
+                            "name": "origine",
+                            "x": 0,
+                            "y": 0
+                          },
+                          "centerPoint": {
+                            "automatic": true,
+                            "name": "centre",
+                            "x": 0,
+                            "y": 0
+                          },
+                          "customCollisionMask": []
+                        }
+                      ]
+                    }
+                  ]
+                }
+              ]
+            }
+          }
+        },
+        {
+          "assetStoreId": "",
+          "height": 64,
+          "name": "Platform_Ground",
+          "texture": "assets\\StartingGround.png",
+          "type": "TiledSpriteObject::TiledSprite",
+          "width": 64,
+          "variables": [],
+          "effects": [],
+          "behaviors": [
+            {
+              "name": "Platform",
+              "type": "PlatformBehavior::PlatformBehavior",
+              "canBeGrabbed": false,
+              "platformType": "NormalPlatform",
+              "yGrabOffset": 0
+            }
+          ]
+        },
+        {
+          "adaptCollisionMaskAutomatically": true,
+          "assetStoreId": "8be3cdbd79d1bc3af6940fb98cdf48510c9e8bc62ba404020dd92f67b61fc79d",
+          "name": "FlagBlue",
+          "type": "Sprite",
+          "updateIfNotVisible": false,
+          "variables": [],
+          "effects": [],
+          "behaviors": [],
+          "animations": [
+            {
+              "name": "",
+              "useMultipleDirections": false,
+              "directions": [
+                {
+                  "looping": true,
+                  "timeBetweenFrames": 0.025,
+                  "sprites": [
+                    {
+                      "hasCustomCollisionMask": true,
+                      "image": "Flag Blue.png",
+                      "points": [],
+                      "originPoint": {
+                        "name": "origine",
+                        "x": 0,
+                        "y": 0
+                      },
+                      "centerPoint": {
+                        "automatic": true,
+                        "name": "centre",
+                        "x": 0,
+                        "y": 0
+                      },
+                      "customCollisionMask": [
+                        [
+                          {
+                            "x": 0,
+                            "y": 0
+                          },
+                          {
+                            "x": 70,
+                            "y": 0
+                          },
+                          {
+                            "x": 70,
+                            "y": 70
+                          },
+                          {
+                            "x": 0,
+                            "y": 70
+                          }
+                        ]
+                      ]
+                    }
+                  ]
+                }
+              ]
+            }
+          ]
+        },
+        {
+          "assetStoreId": "",
+          "height": 32,
+          "name": "NewTiledSprite",
+          "texture": "NewTiledSprite",
+          "type": "TiledSpriteObject::TiledSprite",
+          "width": 32,
+          "variables": [],
+          "effects": [],
+          "behaviors": []
+        },
+        {
+          "adaptCollisionMaskAutomatically": true,
+          "assetStoreId": "39d02c8d833898dd906c948e8403596aba1fdcfa223f374b260aa5b0ae70b776",
+          "name": "LargeSign",
+          "type": "Sprite",
+          "updateIfNotVisible": false,
+          "variables": [],
+          "effects": [],
+          "behaviors": [],
+          "animations": [
+            {
+              "name": "",
+              "useMultipleDirections": false,
+              "directions": [
+                {
+                  "looping": true,
+                  "timeBetweenFrames": 0.025,
+                  "sprites": [
+                    {
+                      "hasCustomCollisionMask": true,
+                      "image": "Large sign.png",
+                      "points": [],
+                      "originPoint": {
+                        "name": "origine",
+                        "x": 0,
+                        "y": 0
+                      },
+                      "centerPoint": {
+                        "automatic": true,
+                        "name": "centre",
+                        "x": 0,
+                        "y": 0
+                      },
+                      "customCollisionMask": [
+                        [
+                          {
+                            "x": 0,
+                            "y": 0
+                          },
+                          {
+                            "x": 64,
+                            "y": 0
+                          },
+                          {
+                            "x": 64,
+                            "y": 55
+                          },
+                          {
+                            "x": 0,
+                            "y": 55
+                          }
+                        ]
+                      ]
+                    }
+                  ]
+                }
+              ]
+            }
+          ]
+        },
+        {
+          "assetStoreId": "",
+          "bold": true,
+          "italic": false,
+          "name": "NewText",
+          "smoothed": true,
+          "type": "TextObject::Text",
+          "underlined": false,
+          "variables": [],
+          "effects": [],
+          "behaviors": [],
+          "string": "Flags are checkpoints !",
+          "font": "",
+          "textAlignment": "center",
+          "characterSize": 20,
+          "color": {
+            "b": 255,
+            "g": 255,
+            "r": 255
+          },
+          "content": {
+            "bold": true,
+            "isOutlineEnabled": false,
+            "isShadowEnabled": false,
+            "italic": false,
+            "outlineColor": "255;255;255",
+            "outlineThickness": 2,
+            "shadowAngle": 90,
+            "shadowBlurRadius": 2,
+            "shadowColor": "0;0;0",
+            "shadowDistance": 4,
+            "shadowOpacity": 127,
+            "smoothed": true,
+            "underlined": false,
+            "text": "Flags are checkpoints !",
+            "font": "",
+            "textAlignment": "center",
+            "verticalTextAlignment": "top",
+            "characterSize": 20,
+            "color": "255;255;255"
+          }
+        },
+        {
+          "assetStoreId": "",
+          "bold": true,
+          "italic": false,
+          "name": "NewText2",
+          "smoothed": true,
+          "type": "TextObject::Text",
+          "underlined": false,
+          "variables": [],
+          "effects": [],
+          "behaviors": [],
+          "string": "You won !",
+          "font": "",
+          "textAlignment": "center",
+          "characterSize": 50,
+          "color": {
+            "b": 255,
+            "g": 255,
+            "r": 255
+          },
+          "content": {
+            "bold": true,
+            "isOutlineEnabled": false,
+            "isShadowEnabled": false,
+            "italic": false,
+            "outlineColor": "255;255;255",
+            "outlineThickness": 2,
+            "shadowAngle": 90,
+            "shadowBlurRadius": 2,
+            "shadowColor": "0;0;0",
+            "shadowDistance": 4,
+            "shadowOpacity": 127,
+            "smoothed": true,
+            "underlined": false,
+            "text": "You won !",
+            "font": "",
+            "textAlignment": "center",
+            "verticalTextAlignment": "top",
+            "characterSize": 50,
+            "color": "255;255;255"
+          }
+        }
+      ],
+      "objectsFolderStructure": {
+        "folderName": "__ROOT",
+        "children": [
+          {
+            "objectName": "Platformer_Character"
+          },
+          {
+            "objectName": "Platform_Ground"
+          },
+          {
+            "objectName": "LargeSign"
+          },
+          {
+            "objectName": "NewText"
+          },
+          {
+            "objectName": "NewText2"
+          },
+          {
+            "objectName": "FlagBlue"
+          },
+          {
+            "objectName": "Background"
+          },
+          {
+            "objectName": "Coins"
+          },
+          {
+            "folderName": "Mobile Controls",
+            "children": [
+              {
+                "objectName": "Joystick"
+              },
+              {
+                "objectName": "JumpButton"
+              }
+            ]
+          },
+          {
+            "objectName": "NewTiledSprite"
+          }
+        ]
+      },
+      "events": [
+        {
+          "type": "BuiltinCommonInstructions::Standard",
+          "conditions": [
+            {
+              "type": {
+                "value": "CollisionNP"
+              },
+              "parameters": [
+                "Platformer_Character",
+                "Coins",
+                "",
+                "",
+                ""
+              ]
+            }
+          ],
+          "actions": [
+            {
+              "type": {
+                "value": "Delete"
+              },
+              "parameters": [
+                "Coins",
+                ""
+              ]
+            },
+            {
+              "type": {
+                "value": "PlaySound"
+              },
+              "parameters": [
+                "",
+                "PickupCoin",
+                "",
+                "80",
+                "RandomFloatInRange(1,1.1)"
+              ]
+            }
+          ]
+        },
+        {
+          "type": "BuiltinCommonInstructions::Standard",
+          "conditions": [
+            {
+              "type": {
+                "value": "Distance"
+              },
+              "parameters": [
+                "Platformer_Character",
+                "FlagBlue",
+                "10",
+                ""
+              ]
+            },
+            {
+              "type": {
+                "value": "BuiltinCommonInstructions::Once"
+              },
+              "parameters": []
+            }
+          ],
+          "actions": [
+            {
+              "type": {
+                "value": "PlaySound"
+              },
+              "parameters": [
+                "",
+                "c52f1dacc263a2a6dc94e712a2a148f909b73372fa8e0622cb237fdc6a72fd6c_Coins 8.aac",
+                "",
+                "",
+                ""
+              ]
+            },
+            {
+              "type": {
+                "value": "SaveState::SaveGame"
+              },
+              "parameters": [
+                "",
+                "",
+                ""
+              ]
+            }
+          ]
+        },
+        {
+          "type": "BuiltinCommonInstructions::Standard",
+          "conditions": [
+            {
+              "type": {
+                "value": "CollisionNP"
+              },
+              "parameters": [
+                "Platformer_Character",
+                "NewTiledSprite",
+                "",
+                "",
+                ""
+              ]
+            },
+            {
+              "type": {
+                "value": "BuiltinCommonInstructions::Once"
+              },
+              "parameters": []
+            }
+          ],
+          "actions": [
+            {
+              "type": {
+                "value": "SaveState::LoadGame"
+              },
+              "parameters": [
+                "",
+                "",
+                ""
+              ]
+            }
+          ]
+        },
+        {
+          "type": "BuiltinCommonInstructions::Standard",
+          "conditions": [
+            {
+              "type": {
+                "value": "DepartScene"
+              },
+              "parameters": [
+                ""
+              ]
+            }
+          ],
+          "actions": [
+            {
+              "type": {
+                "value": "PlayMusic"
+              },
+              "parameters": [
+                "",
+                "dfcdb6ef0ded864b301b5cc57940b190e803b1ea53d057338043f3b8bcd2a138_04 - Castle Nosferatu (Sega-style FM Synth Remix).aac",
+                "yes",
+                "",
+                ""
+              ]
+            }
+          ]
+        }
+      ],
+      "layers": [
+        {
+          "ambientLightColorB": 200,
+          "ambientLightColorG": 200,
+          "ambientLightColorR": 200,
+          "camera3DFarPlaneDistance": 10000,
+          "camera3DFieldOfView": 45,
+          "camera3DNearPlaneDistance": 3,
+          "cameraType": "",
+          "followBaseLayerCamera": false,
+          "isLightingLayer": false,
+          "isLocked": false,
+          "name": "Background",
+          "renderingType": "",
+          "visibility": true,
+          "cameras": [],
+          "effects": []
+        },
+        {
+          "ambientLightColorB": 200,
+          "ambientLightColorG": 200,
+          "ambientLightColorR": 200,
+          "camera3DFarPlaneDistance": 10000,
+          "camera3DFieldOfView": 45,
+          "camera3DNearPlaneDistance": 3,
+          "cameraType": "",
+          "followBaseLayerCamera": false,
+          "isLightingLayer": false,
+          "isLocked": false,
+          "name": "",
+          "renderingType": "",
+          "visibility": true,
+          "cameras": [
+            {
+              "defaultSize": true,
+              "defaultViewport": true,
+              "height": 0,
+              "viewportBottom": 1,
+              "viewportLeft": 0,
+              "viewportRight": 1,
+              "viewportTop": 0,
+              "width": 0
+            }
+          ],
+          "effects": []
+        },
+        {
+          "ambientLightColorB": 200,
+          "ambientLightColorG": 200,
+          "ambientLightColorR": 200,
+          "camera3DFarPlaneDistance": 10000,
+          "camera3DFieldOfView": 45,
+          "camera3DNearPlaneDistance": 3,
+          "cameraType": "",
+          "followBaseLayerCamera": false,
+          "isLightingLayer": false,
+          "isLocked": false,
+          "name": "MobileControls",
+          "renderingType": "",
+          "visibility": true,
+          "cameras": [],
+          "effects": []
+        }
+      ],
+      "behaviorsSharedData": [
+        {
+          "name": "Animation",
+          "type": "AnimatableCapability::AnimatableBehavior"
+        },
+        {
+          "name": "ButtonFSM",
+          "type": "ButtonStates::ButtonFSM"
+        },
+        {
+          "name": "ButtonScaleTween",
+          "type": "ButtonStates::ButtonScaleTween"
+        },
+        {
+          "name": "Effect",
+          "type": "EffectCapability::EffectBehavior"
+        },
+        {
+          "name": "Flippable",
+          "type": "FlippableCapability::FlippableBehavior"
+        },
+        {
+          "name": "MultitouchButton",
+          "type": "SpriteMultitouchJoystick::MultitouchButton"
+        },
+        {
+          "name": "Opacity",
+          "type": "OpacityCapability::OpacityBehavior"
+        },
+        {
+          "name": "Platform",
+          "type": "PlatformBehavior::PlatformBehavior"
+        },
+        {
+          "name": "PlatformerMultitouchMapper",
+          "type": "SpriteMultitouchJoystick::PlatformerMultitouchMapper"
+        },
+        {
+          "name": "PlatformerObject",
+          "type": "PlatformBehavior::PlatformerObjectBehavior"
+        },
+        {
+          "name": "Resizable",
+          "type": "ResizableCapability::ResizableBehavior"
+        },
+        {
+          "name": "Scale",
+          "type": "ScalableCapability::ScalableBehavior"
+        },
+        {
+          "name": "SmoothCamera",
+          "type": "SmoothCamera::SmoothCamera"
+        },
+        {
+          "name": "Text",
+          "type": "TextContainerCapability::TextContainerBehavior"
+        },
+        {
+          "name": "Tween",
+          "type": "Tween::TweenBehavior"
+        }
+      ]
+    }
+  ],
+  "externalEvents": [],
+  "eventsFunctionsExtensions": [
+    {
+      "author": "",
+      "category": "User interface",
+      "extensionNamespace": "",
+      "fullName": "Button states and effects",
+      "helpPath": "/objects/button",
+      "iconUrl": "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz48IURPQ1RZUEUgc3ZnIFBVQkxJQyAiLS8vVzNDLy9EVEQgU1ZHIDEuMS8vRU4iICJodHRwOi8vd3d3LnczLm9yZy9HcmFwaGljcy9TVkcvMS4xL0RURC9zdmcxMS5kdGQiPjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgdmVyc2lvbj0iMS4xIiBpZD0ibWRpLWdlc3R1cmUtdGFwLWJ1dHRvbiIgd2lkdGg9IjI0IiBoZWlnaHQ9IjI0IiB2aWV3Qm94PSIwIDAgMjQgMjQiPjxwYXRoIGQ9Ik0xMyA1QzE1LjIxIDUgMTcgNi43OSAxNyA5QzE3IDEwLjUgMTYuMiAxMS43NyAxNSAxMi40NlYxMS4yNEMxNS42MSAxMC42OSAxNiA5Ljg5IDE2IDlDMTYgNy4zNCAxNC42NiA2IDEzIDZTMTAgNy4zNCAxMCA5QzEwIDkuODkgMTAuMzkgMTAuNjkgMTEgMTEuMjRWMTIuNDZDOS44IDExLjc3IDkgMTAuNSA5IDlDOSA2Ljc5IDEwLjc5IDUgMTMgNU0yMCAyMC41QzE5Ljk3IDIxLjMyIDE5LjMyIDIxLjk3IDE4LjUgMjJIMTNDMTIuNjIgMjIgMTIuMjYgMjEuODUgMTIgMjEuNTdMOCAxNy4zN0w4Ljc0IDE2LjZDOC45MyAxNi4zOSA5LjIgMTYuMjggOS41IDE2LjI4SDkuN0wxMiAxOFY5QzEyIDguNDUgMTIuNDUgOCAxMyA4UzE0IDguNDUgMTQgOVYxMy40N0wxNS4yMSAxMy42TDE5LjE1IDE1Ljc5QzE5LjY4IDE2LjAzIDIwIDE2LjU2IDIwIDE3LjE0VjIwLjVNMjAgMkg0QzIuOSAyIDIgMi45IDIgNFYxMkMyIDEzLjExIDIuOSAxNCA0IDE0SDhWMTJMNCAxMkw0IDRIMjBMMjAgMTJIMThWMTRIMjBWMTMuOTZMMjAuMDQgMTRDMjEuMTMgMTQgMjIgMTMuMDkgMjIgMTJWNEMyMiAyLjkgMjEuMTEgMiAyMCAyWiIgLz48L3N2Zz4=",
+      "name": "ButtonStates",
+      "previewIconUrl": "https://asset-resources.gdevelop.io/public-resources/Icons/753a9a794bd885058159b7509f06f5a8f67f72decfccb9a1b0efee26f41c3c4c_gesture-tap-button.svg",
+      "shortDescription": "Use any object as a button and change appearance according to user interactions.",
+      "version": "1.1.1",
+      "description": [
+        "Use the \"Button states\" behavior to track user interactions with an object, including:",
+        "",
+        "- Hovered",
+        "- Pressed",
+        "- Clicked",
+        "- Idle",
+        "",
+        "Add additional behaviors to make juicy buttons with animated responses to user input:",
+        "",
+        "- Size",
+        "- Color",
+        "- Animation",
+        "- Object effects"
+      ],
+      "origin": {
+        "identifier": "ButtonStates",
+        "name": "gdevelop-extension-store"
+      },
+      "tags": [
+        "ui",
+        "button"
+      ],
+      "authorIds": [
+        "IWykYNRvhCZBN3vEgKEbBPOR3Oc2",
+        "gqDaZjCfevOOxBYkK6zlhtZnXCg1"
+      ],
+      "dependencies": [],
+      "globalVariables": [],
+      "sceneVariables": [],
+      "eventsFunctions": [],
+      "eventsBasedBehaviors": [
+        {
+          "description": "Use objects as buttons.",
+          "fullName": "Button states",
+          "name": "ButtonFSM",
+          "objectType": "",
+          "eventsFunctions": [
+            {
+              "fullName": "",
+              "functionType": "Action",
+              "name": "doStepPostEvents",
+              "sentence": "",
+              "events": [
+                {
+                  "colorB": 228,
+                  "colorG": 176,
+                  "colorR": 74,
+                  "creationTime": 0,
+                  "name": "Finite state machine",
+                  "source": "",
+                  "type": "BuiltinCommonInstructions::Group",
+                  "events": [
+                    {
+                      "type": "BuiltinCommonInstructions::Comment",
+                      "color": {
+                        "b": 109,
+                        "g": 230,
+                        "r": 255,
+                        "textB": 0,
+                        "textG": 0,
+                        "textR": 0
+                      },
+                      "comment": "The \"Validated\" state only last one frame."
+                    },
+                    {
+                      "type": "BuiltinCommonInstructions::Standard",
+                      "conditions": [
+                        {
+                          "type": {
+                            "value": "ButtonStates::ButtonFSM::PropertyState"
+                          },
+                          "parameters": [
+                            "Object",
+                            "Behavior",
+                            "=",
+                            "\"Validated\""
+                          ]
+                        }
+                      ],
+                      "actions": [
+                        {
+                          "type": {
+                            "value": "ButtonStates::ButtonFSM::SetPropertyState"
+                          },
+                          "parameters": [
+                            "Object",
+                            "Behavior",
+                            "=",
+                            "\"Idle\""
+                          ]
+                        }
+                      ]
+                    },
+                    {
+                      "colorB": 228,
+                      "colorG": 176,
+                      "colorR": 74,
+                      "creationTime": 0,
+                      "name": "Check position",
+                      "source": "",
+                      "type": "BuiltinCommonInstructions::Group",
+                      "events": [
+                        {
+                          "type": "BuiltinCommonInstructions::Comment",
+                          "color": {
+                            "b": 109,
+                            "g": 230,
+                            "r": 255,
+                            "textB": 0,
+                            "textG": 0,
+                            "textR": 0
+                          },
+                          "comment": "Make sure the cursor position is only checked once per frame."
+                        },
+                        {
+                          "type": "BuiltinCommonInstructions::Standard",
+                          "conditions": [],
+                          "actions": [
+                            {
+                              "type": {
+                                "value": "ButtonStates::ButtonFSM::SetPropertyMouseIsInside"
+                              },
+                              "parameters": [
+                                "Object",
+                                "Behavior",
+                                "no"
+                              ]
+                            }
+                          ]
+                        },
+                        {
+                          "type": "BuiltinCommonInstructions::Standard",
+                          "conditions": [
+                            {
+                              "type": {
+                                "value": "ButtonStates::ButtonFSM::PropertyShouldCheckHovering"
+                              },
+                              "parameters": [
+                                "Object",
+                                "Behavior"
+                              ]
+                            },
+                            {
+                              "type": {
+                                "value": "CollisionPoint"
+                              },
+                              "parameters": [
+                                "Object",
+                                "MouseOnlyCursorX(Object.Layer(), 0)",
+                                "MouseOnlyCursorY(Object.Layer(), 0)"
+                              ]
+                            }
+                          ],
+                          "actions": [
+                            {
+                              "type": {
+                                "value": "ButtonStates::ButtonFSM::SetPropertyMouseIsInside"
+                              },
+                              "parameters": [
+                                "Object",
+                                "Behavior",
+                                "yes"
+                              ]
+                            }
+                          ]
+                        },
+                        {
+                          "type": "BuiltinCommonInstructions::Comment",
+                          "color": {
+                            "b": 109,
+                            "g": 230,
+                            "r": 255,
+                            "textB": 0,
+                            "textG": 0,
+                            "textR": 0
+                          },
+                          "comment": "Touches are always pressed, so ShouldCheckHovering doesn't matter."
+                        },
+                        {
+                          "type": "BuiltinCommonInstructions::Standard",
+                          "conditions": [],
+                          "actions": [
+                            {
+                              "type": {
+                                "value": "ButtonStates::ButtonFSM::SetPropertyTouchIsInside"
+                              },
+                              "parameters": [
+                                "Object",
+                                "Behavior",
+                                "no"
+                              ]
+                            }
+                          ]
+                        },
+                        {
+                          "type": "BuiltinCommonInstructions::Standard",
+                          "conditions": [
+                            {
+                              "type": {
+                                "value": "ButtonStates::ButtonFSM::PropertyTouchId"
+                              },
+                              "parameters": [
+                                "Object",
+                                "Behavior",
+                                "!=",
+                                "0"
+                              ]
+                            },
+                            {
+                              "type": {
+                                "value": "CollisionPoint"
+                              },
+                              "parameters": [
+                                "Object",
+                                "TouchX(TouchId, Object.Layer(), 0)",
+                                "TouchY(TouchId, Object.Layer(), 0)"
+                              ]
+                            }
+                          ],
+                          "actions": [
+                            {
+                              "type": {
+                                "value": "ButtonStates::ButtonFSM::SetPropertyTouchIsInside"
+                              },
+                              "parameters": [
+                                "Object",
+                                "Behavior",
+                                "yes"
+                              ]
+                            }
+                          ]
+                        }
+                      ],
+                      "parameters": []
+                    },
+                    {
+                      "colorB": 228,
+                      "colorG": 176,
+                      "colorR": 74,
+                      "creationTime": 0,
+                      "name": "Handle touch start",
+                      "source": "",
+                      "type": "BuiltinCommonInstructions::Group",
+                      "events": [
+                        {
+                          "type": "BuiltinCommonInstructions::Standard",
+                          "conditions": [
+                            {
+                              "type": {
+                                "value": "HasAnyTouchOrMouseStarted"
+                              },
+                              "parameters": [
+                                ""
+                              ]
+                            }
+                          ],
+                          "actions": [
+                            {
+                              "type": {
+                                "value": "ButtonStates::ButtonFSM::SetPropertyIndex"
+                              },
+                              "parameters": [
+                                "Object",
+                                "Behavior",
+                                "=",
+                                "0"
+                              ]
+                            }
+                          ],
+                          "events": [
+                            {
+                              "type": "BuiltinCommonInstructions::Repeat",
+                              "repeatExpression": "StartedTouchOrMouseCount()",
+                              "conditions": [],
+                              "actions": [],
+                              "events": [
+                                {
+                                  "type": "BuiltinCommonInstructions::Standard",
+                                  "conditions": [
+                                    {
+                                      "type": {
+                                        "value": "CollisionPoint"
+                                      },
+                                      "parameters": [
+                                        "Object",
+                                        "TouchX(StartedTouchOrMouseId(Index), Object.Layer(), 0)",
+                                        "TouchY(StartedTouchOrMouseId(Index), Object.Layer(), 0)"
+                                      ]
+                                    }
+                                  ],
+                                  "actions": [
+                                    {
+                                      "type": {
+                                        "value": "ButtonStates::ButtonFSM::SetPropertyTouchId"
+                                      },
+                                      "parameters": [
+                                        "Object",
+                                        "Behavior",
+                                        "=",
+                                        "StartedTouchOrMouseId(Index)"
+                                      ]
+                                    },
+                                    {
+                                      "type": {
+                                        "value": "ButtonStates::ButtonFSM::SetPropertyTouchIsInside"
+                                      },
+                                      "parameters": [
+                                        "Object",
+                                        "Behavior",
+                                        "yes"
+                                      ]
+                                    }
+                                  ],
+                                  "events": [
+                                    {
+                                      "type": "BuiltinCommonInstructions::Standard",
+                                      "conditions": [
+                                        {
+                                          "type": {
+                                            "value": "BuiltinCommonInstructions::Or"
+                                          },
+                                          "parameters": [],
+                                          "subInstructions": [
+                                            {
+                                              "type": {
+                                                "value": "ButtonStates::ButtonFSM::PropertyState"
+                                              },
+                                              "parameters": [
+                                                "Object",
+                                                "Behavior",
+                                                "=",
+                                                "\"Hovered\""
+                                              ]
+                                            },
+                                            {
+                                              "type": {
+                                                "value": "ButtonStates::ButtonFSM::PropertyState"
+                                              },
+                                              "parameters": [
+                                                "Object",
+                                                "Behavior",
+                                                "=",
+                                                "\"Idle\""
+                                              ]
+                                            }
+                                          ]
+                                        }
+                                      ],
+                                      "actions": [
+                                        {
+                                          "type": {
+                                            "value": "ButtonStates::ButtonFSM::SetPropertyState"
+                                          },
+                                          "parameters": [
+                                            "Object",
+                                            "Behavior",
+                                            "=",
+                                            "\"PressedInside\""
+                                          ]
+                                        }
+                                      ]
+                                    }
+                                  ]
+                                },
+                                {
+                                  "type": "BuiltinCommonInstructions::Standard",
+                                  "conditions": [],
+                                  "actions": [
+                                    {
+                                      "type": {
+                                        "value": "ButtonStates::ButtonFSM::SetPropertyIndex"
+                                      },
+                                      "parameters": [
+                                        "Object",
+                                        "Behavior",
+                                        "+",
+                                        "1"
+                                      ]
+                                    }
+                                  ]
+                                }
+                              ]
+                            }
+                          ]
+                        }
+                      ],
+                      "parameters": []
+                    },
+                    {
+                      "colorB": 228,
+                      "colorG": 176,
+                      "colorR": 74,
+                      "creationTime": 0,
+                      "name": "Apply position changes",
+                      "source": "",
+                      "type": "BuiltinCommonInstructions::Group",
+                      "events": [
+                        {
+                          "type": "BuiltinCommonInstructions::Standard",
+                          "conditions": [
+                            {
+                              "type": {
+                                "inverted": true,
+                                "value": "ButtonStates::ButtonFSM::PropertyMouseIsInside"
+                              },
+                              "parameters": [
+                                "Object",
+                                "Behavior"
+                              ]
+                            },
+                            {
+                              "type": {
+                                "value": "ButtonStates::ButtonFSM::PropertyState"
+                              },
+                              "parameters": [
+                                "Object",
+                                "Behavior",
+                                "=",
+                                "\"Hovered\""
+                              ]
+                            }
+                          ],
+                          "actions": [
+                            {
+                              "type": {
+                                "value": "ButtonStates::ButtonFSM::SetPropertyState"
+                              },
+                              "parameters": [
+                                "Object",
+                                "Behavior",
+                                "=",
+                                "\"Idle\""
+                              ]
+                            }
+                          ]
+                        },
+                        {
+                          "type": "BuiltinCommonInstructions::Standard",
+                          "conditions": [
+                            {
+                              "type": {
+                                "value": "ButtonStates::ButtonFSM::PropertyMouseIsInside"
+                              },
+                              "parameters": [
+                                "Object",
+                                "Behavior"
+                              ]
+                            },
+                            {
+                              "type": {
+                                "value": "ButtonStates::ButtonFSM::PropertyState"
+                              },
+                              "parameters": [
+                                "Object",
+                                "Behavior",
+                                "=",
+                                "\"Idle\""
+                              ]
+                            }
+                          ],
+                          "actions": [
+                            {
+                              "type": {
+                                "value": "ButtonStates::ButtonFSM::SetPropertyState"
+                              },
+                              "parameters": [
+                                "Object",
+                                "Behavior",
+                                "=",
+                                "\"Hovered\""
+                              ]
+                            }
+                          ]
+                        },
+                        {
+                          "type": "BuiltinCommonInstructions::Standard",
+                          "conditions": [
+                            {
+                              "type": {
+                                "inverted": true,
+                                "value": "ButtonStates::ButtonFSM::PropertyTouchIsInside"
+                              },
+                              "parameters": [
+                                "Object",
+                                "Behavior"
+                              ]
+                            },
+                            {
+                              "type": {
+                                "value": "ButtonStates::ButtonFSM::PropertyState"
+                              },
+                              "parameters": [
+                                "Object",
+                                "Behavior",
+                                "=",
+                                "\"PressedInside\""
+                              ]
+                            }
+                          ],
+                          "actions": [
+                            {
+                              "type": {
+                                "value": "ButtonStates::ButtonFSM::SetPropertyState"
+                              },
+                              "parameters": [
+                                "Object",
+                                "Behavior",
+                                "=",
+                                "\"PressedOutside\""
+                              ]
+                            }
+                          ]
+                        },
+                        {
+                          "type": "BuiltinCommonInstructions::Standard",
+                          "conditions": [
+                            {
+                              "type": {
+                                "value": "ButtonStates::ButtonFSM::PropertyTouchIsInside"
+                              },
+                              "parameters": [
+                                "Object",
+                                "Behavior"
+                              ]
+                            },
+                            {
+                              "type": {
+                                "value": "ButtonStates::ButtonFSM::PropertyState"
+                              },
+                              "parameters": [
+                                "Object",
+                                "Behavior",
+                                "=",
+                                "\"PressedOutside\""
+                              ]
+                            }
+                          ],
+                          "actions": [
+                            {
+                              "type": {
+                                "value": "ButtonStates::ButtonFSM::SetPropertyState"
+                              },
+                              "parameters": [
+                                "Object",
+                                "Behavior",
+                                "=",
+                                "\"PressedInside\""
+                              ]
+                            }
+                          ]
+                        }
+                      ],
+                      "parameters": []
+                    },
+                    {
+                      "colorB": 228,
+                      "colorG": 176,
+                      "colorR": 74,
+                      "creationTime": 0,
+                      "name": "Handle touch end",
+                      "source": "",
+                      "type": "BuiltinCommonInstructions::Group",
+                      "events": [
+                        {
+                          "type": "BuiltinCommonInstructions::Standard",
+                          "conditions": [
+                            {
+                              "type": {
+                                "value": "HasTouchEnded"
+                              },
+                              "parameters": [
+                                "",
+                                "TouchId"
+                              ]
+                            }
+                          ],
+                          "actions": [
+                            {
+                              "type": {
+                                "value": "ButtonStates::ButtonFSM::SetPropertyTouchId"
+                              },
+                              "parameters": [
+                                "Object",
+                                "Behavior",
+                                "=",
+                                "0"
+                              ]
+                            }
+                          ],
+                          "events": [
+                            {
+                              "type": "BuiltinCommonInstructions::Standard",
+                              "conditions": [
+                                {
+                                  "type": {
+                                    "value": "ButtonStates::ButtonFSM::PropertyState"
+                                  },
+                                  "parameters": [
+                                    "Object",
+                                    "Behavior",
+                                    "=",
+                                    "\"PressedInside\""
+                                  ]
+                                }
+                              ],
+                              "actions": [
+                                {
+                                  "type": {
+                                    "value": "ButtonStates::ButtonFSM::SetPropertyState"
+                                  },
+                                  "parameters": [
+                                    "Object",
+                                    "Behavior",
+                                    "=",
+                                    "\"Validated\""
+                                  ]
+                                }
+                              ]
+                            },
+                            {
+                              "type": "BuiltinCommonInstructions::Standard",
+                              "conditions": [
+                                {
+                                  "type": {
+                                    "inverted": true,
+                                    "value": "ButtonStates::ButtonFSM::PropertyState"
+                                  },
+                                  "parameters": [
+                                    "Object",
+                                    "Behavior",
+                                    "=",
+                                    "\"PressedInside\""
+                                  ]
+                                },
+                                {
+                                  "type": {
+                                    "inverted": true,
+                                    "value": "ButtonStates::ButtonFSM::PropertyState"
+                                  },
+                                  "parameters": [
+                                    "Object",
+                                    "Behavior",
+                                    "=",
+                                    "\"Validated\""
+                                  ]
+                                }
+                              ],
+                              "actions": [
+                                {
+                                  "type": {
+                                    "value": "ButtonStates::ButtonFSM::SetPropertyState"
+                                  },
+                                  "parameters": [
+                                    "Object",
+                                    "Behavior",
+                                    "=",
+                                    "\"Idle\""
+                                  ]
+                                }
+                              ]
+                            }
+                          ]
+                        }
+                      ],
+                      "parameters": []
+                    }
+                  ],
+                  "parameters": []
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "ButtonStates::ButtonFSM",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "fullName": "",
+              "functionType": "Action",
+              "name": "onDeActivate",
+              "sentence": "",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "ButtonStates::ButtonFSM::ResetState"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        ""
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "ButtonStates::ButtonFSM",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "Reset the state of the button.",
+              "fullName": "Reset state",
+              "functionType": "Action",
+              "name": "ResetState",
+              "private": true,
+              "sentence": "Reset the button state of _PARAM0_",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "ButtonStates::ButtonFSM::SetPropertyState"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "=",
+                        "\"Idle\""
+                      ]
+                    },
+                    {
+                      "type": {
+                        "value": "ButtonStates::ButtonFSM::SetPropertyTouchId"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "=",
+                        "0"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "ButtonStates::ButtonFSM",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "Check if the button is not used.",
+              "fullName": "Is idle",
+              "functionType": "Condition",
+              "name": "IsIdle",
+              "sentence": "_PARAM0_ is idle",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [
+                    {
+                      "type": {
+                        "value": "ButtonStates::ButtonFSM::PropertyState"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "=",
+                        "\"Idle\""
+                      ]
+                    }
+                  ],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SetReturnBoolean"
+                      },
+                      "parameters": [
+                        "True"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "ButtonStates::ButtonFSM",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "Check if the button was just clicked.",
+              "fullName": "Is clicked",
+              "functionType": "Condition",
+              "name": "IsClicked",
+              "sentence": "_PARAM0_ is clicked",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [
+                    {
+                      "type": {
+                        "value": "ButtonStates::ButtonFSM::PropertyState"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "=",
+                        "\"Validated\""
+                      ]
+                    }
+                  ],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SetReturnBoolean"
+                      },
+                      "parameters": [
+                        "True"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "ButtonStates::ButtonFSM",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "Check if the cursor is hovered over the button.",
+              "fullName": "Is hovered",
+              "functionType": "Condition",
+              "name": "IsHovered",
+              "sentence": "_PARAM0_ is hovered",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [
+                    {
+                      "type": {
+                        "value": "ButtonStates::ButtonFSM::PropertyState"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "=",
+                        "\"Hovered\""
+                      ]
+                    }
+                  ],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SetReturnBoolean"
+                      },
+                      "parameters": [
+                        "True"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "ButtonStates::ButtonFSM",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "Check if the button is either hovered or pressed but not hovered.",
+              "fullName": "Is focused",
+              "functionType": "Condition",
+              "name": "IsFocused",
+              "sentence": "_PARAM0_ is focused",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [
+                    {
+                      "type": {
+                        "value": "ButtonStates::ButtonFSM::PropertyState"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "=",
+                        "\"Hovered\""
+                      ]
+                    }
+                  ],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SetReturnBoolean"
+                      },
+                      "parameters": [
+                        "True"
+                      ]
+                    }
+                  ]
+                },
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [
+                    {
+                      "type": {
+                        "value": "ButtonStates::ButtonFSM::PropertyState"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "=",
+                        "\"PressedOutside\""
+                      ]
+                    }
+                  ],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SetReturnBoolean"
+                      },
+                      "parameters": [
+                        "True"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "ButtonStates::ButtonFSM",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "Check if the button is currently being pressed with mouse or touch.",
+              "fullName": "Is pressed",
+              "functionType": "Condition",
+              "name": "IsPressed",
+              "sentence": "_PARAM0_ is pressed",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [
+                    {
+                      "type": {
+                        "value": "ButtonStates::ButtonFSM::PropertyState"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "=",
+                        "\"PressedInside\""
+                      ]
+                    }
+                  ],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SetReturnBoolean"
+                      },
+                      "parameters": [
+                        "True"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "ButtonStates::ButtonFSM",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "Check if the button is currently being pressed outside with mouse or touch.",
+              "fullName": "Is held outside",
+              "functionType": "Condition",
+              "name": "IsPressedOutside",
+              "sentence": "_PARAM0_ is held outside",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [
+                    {
+                      "type": {
+                        "value": "ButtonStates::ButtonFSM::PropertyState"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "=",
+                        "\"PressedOutside\""
+                      ]
+                    }
+                  ],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SetReturnBoolean"
+                      },
+                      "parameters": [
+                        "True"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "ButtonStates::ButtonFSM",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "the touch id that is using the button or 0 if none.",
+              "fullName": "Touch id",
+              "functionType": "ExpressionAndCondition",
+              "name": "TouchId",
+              "sentence": "the touch id",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SetReturnNumber"
+                      },
+                      "parameters": [
+                        "TouchId"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "expressionType": {
+                "type": "expression"
+              },
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "ButtonStates::ButtonFSM",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            }
+          ],
+          "propertyDescriptors": [
+            {
+              "value": "true",
+              "type": "Boolean",
+              "label": "",
+              "description": "Should check hovering",
+              "group": "",
+              "extraInformation": [],
+              "hidden": true,
+              "name": "ShouldCheckHovering"
+            },
+            {
+              "value": "Idle",
+              "type": "Choice",
+              "label": "State",
+              "description": "",
+              "group": "",
+              "extraInformation": [
+                "Idle",
+                "Hovered",
+                "PressedInside",
+                "PressedOutside",
+                "Validated"
+              ],
+              "hidden": true,
+              "name": "State"
+            },
+            {
+              "value": "0",
+              "type": "Number",
+              "label": "Touch id",
+              "description": "",
+              "group": "",
+              "extraInformation": [],
+              "hidden": true,
+              "name": "TouchId"
+            },
+            {
+              "value": "",
+              "type": "Boolean",
+              "label": "Touch is inside",
+              "description": "",
+              "group": "",
+              "extraInformation": [],
+              "hidden": true,
+              "name": "TouchIsInside"
+            },
+            {
+              "value": "",
+              "type": "Boolean",
+              "label": "Mouse is inside",
+              "description": "",
+              "group": "",
+              "extraInformation": [],
+              "hidden": true,
+              "name": "MouseIsInside"
+            },
+            {
+              "value": "",
+              "type": "Number",
+              "label": "",
+              "description": "",
+              "group": "",
+              "extraInformation": [],
+              "hidden": true,
+              "name": "Index"
+            }
+          ],
+          "sharedPropertyDescriptors": []
+        },
+        {
+          "description": "Enable effects on buttons based on their state.",
+          "fullName": "Button object effects",
+          "name": "ButtonObjectEffects",
+          "objectType": "",
+          "eventsFunctions": [
+            {
+              "fullName": "",
+              "functionType": "Action",
+              "name": "onCreated",
+              "sentence": "",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "EffectCapability::EffectBehavior::EnableEffect"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Effect",
+                        "IdleEffect",
+                        "yes"
+                      ]
+                    },
+                    {
+                      "type": {
+                        "value": "EffectCapability::EffectBehavior::EnableEffect"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Effect",
+                        "FocusedEffect",
+                        "no"
+                      ]
+                    },
+                    {
+                      "type": {
+                        "value": "EffectCapability::EffectBehavior::EnableEffect"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Effect",
+                        "PressedEffect",
+                        "no"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "ButtonStates::ButtonObjectEffects",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "fullName": "",
+              "functionType": "Action",
+              "name": "doStepPreEvents",
+              "sentence": "",
+              "events": [
+                {
+                  "colorB": 228,
+                  "colorG": 176,
+                  "colorR": 74,
+                  "creationTime": 0,
+                  "name": "States",
+                  "source": "",
+                  "type": "BuiltinCommonInstructions::Group",
+                  "events": [
+                    {
+                      "type": "BuiltinCommonInstructions::Standard",
+                      "conditions": [],
+                      "actions": [
+                        {
+                          "type": {
+                            "value": "EffectCapability::EffectBehavior::EnableEffect"
+                          },
+                          "parameters": [
+                            "Object",
+                            "Effect",
+                            "IdleEffect",
+                            "no"
+                          ]
+                        },
+                        {
+                          "type": {
+                            "value": "EffectCapability::EffectBehavior::EnableEffect"
+                          },
+                          "parameters": [
+                            "Object",
+                            "Effect",
+                            "FocusedEffect",
+                            "no"
+                          ]
+                        },
+                        {
+                          "type": {
+                            "value": "EffectCapability::EffectBehavior::EnableEffect"
+                          },
+                          "parameters": [
+                            "Object",
+                            "Effect",
+                            "PressedEffect",
+                            "no"
+                          ]
+                        }
+                      ]
+                    },
+                    {
+                      "type": "BuiltinCommonInstructions::Standard",
+                      "conditions": [
+                        {
+                          "type": {
+                            "value": "ButtonStates::ButtonObjectEffects::PropertyIdleEffect"
+                          },
+                          "parameters": [
+                            "Object",
+                            "Behavior",
+                            "!=",
+                            "\"\""
+                          ]
+                        },
+                        {
+                          "type": {
+                            "value": "ButtonStates::ButtonFSM::IsIdle"
+                          },
+                          "parameters": [
+                            "Object",
+                            "ButtonFSM",
+                            ""
+                          ]
+                        }
+                      ],
+                      "actions": [
+                        {
+                          "type": {
+                            "value": "EffectCapability::EffectBehavior::EnableEffect"
+                          },
+                          "parameters": [
+                            "Object",
+                            "Effect",
+                            "IdleEffect",
+                            "yes"
+                          ]
+                        }
+                      ]
+                    },
+                    {
+                      "type": "BuiltinCommonInstructions::Standard",
+                      "conditions": [
+                        {
+                          "type": {
+                            "value": "ButtonStates::ButtonObjectEffects::PropertyFocusedEffect"
+                          },
+                          "parameters": [
+                            "Object",
+                            "Behavior",
+                            "!=",
+                            "\"\""
+                          ]
+                        },
+                        {
+                          "type": {
+                            "value": "ButtonStates::ButtonFSM::IsFocused"
+                          },
+                          "parameters": [
+                            "Object",
+                            "ButtonFSM",
+                            ""
+                          ]
+                        }
+                      ],
+                      "actions": [
+                        {
+                          "type": {
+                            "value": "EffectCapability::EffectBehavior::EnableEffect"
+                          },
+                          "parameters": [
+                            "Object",
+                            "Effect",
+                            "FocusedEffect",
+                            "yes"
+                          ]
+                        }
+                      ]
+                    },
+                    {
+                      "type": "BuiltinCommonInstructions::Standard",
+                      "conditions": [
+                        {
+                          "type": {
+                            "value": "ButtonStates::ButtonObjectEffects::PropertyPressedEffect"
+                          },
+                          "parameters": [
+                            "Object",
+                            "Behavior",
+                            "!=",
+                            "\"\""
+                          ]
+                        },
+                        {
+                          "type": {
+                            "value": "ButtonStates::ButtonFSM::IsPressed"
+                          },
+                          "parameters": [
+                            "Object",
+                            "ButtonFSM",
+                            ""
+                          ]
+                        }
+                      ],
+                      "actions": [
+                        {
+                          "type": {
+                            "value": "EffectCapability::EffectBehavior::EnableEffect"
+                          },
+                          "parameters": [
+                            "Object",
+                            "Effect",
+                            "PressedEffect",
+                            "yes"
+                          ]
+                        }
+                      ]
+                    }
+                  ],
+                  "parameters": []
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "ButtonStates::ButtonObjectEffects",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "the idle state effect of the object.",
+              "fullName": "Idle state effect",
+              "functionType": "ExpressionAndCondition",
+              "group": "Button object effects configuration",
+              "name": "IdleEffect",
+              "sentence": "the idle state effect",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SetReturnString"
+                      },
+                      "parameters": [
+                        "IdleEffect"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "expressionType": {
+                "type": "string"
+              },
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "ButtonStates::ButtonObjectEffects",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "fullName": "",
+              "functionType": "ActionWithOperator",
+              "getterName": "IdleEffect",
+              "name": "SetIdleEffect",
+              "sentence": "",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "ButtonStates::ButtonObjectEffects::SetPropertyIdleEffect"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "=",
+                        "Value"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "ButtonStates::ButtonObjectEffects",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "the focused state effect of the object. The state is Focused when the button is hovered or held outside.",
+              "fullName": "Focused state effect",
+              "functionType": "ExpressionAndCondition",
+              "group": "Button object effects configuration",
+              "name": "FocusedEffect",
+              "sentence": "the focused state effect",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SetReturnString"
+                      },
+                      "parameters": [
+                        "FocusedEffect"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "expressionType": {
+                "type": "string"
+              },
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "ButtonStates::ButtonObjectEffects",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "fullName": "",
+              "functionType": "ActionWithOperator",
+              "getterName": "FocusedEffect",
+              "name": "SetFocusedEffect",
+              "sentence": "",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "ButtonStates::ButtonObjectEffects::SetPropertyFocusedEffect"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "=",
+                        "Value"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "ButtonStates::ButtonObjectEffects",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "the pressed state effect of the object.",
+              "fullName": "Pressed state effect",
+              "functionType": "ExpressionAndCondition",
+              "group": "Button object effects configuration",
+              "name": "PressedEffect",
+              "sentence": "the pressed state effect",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SetReturnString"
+                      },
+                      "parameters": [
+                        "PressedEffect"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "expressionType": {
+                "type": "string"
+              },
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "ButtonStates::ButtonObjectEffects",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "fullName": "",
+              "functionType": "ActionWithOperator",
+              "getterName": "PressedEffect",
+              "name": "SetPressedEffect",
+              "sentence": "",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "ButtonStates::ButtonObjectEffects::SetPropertyPressedEffect"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "=",
+                        "Value"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "ButtonStates::ButtonObjectEffects",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            }
+          ],
+          "propertyDescriptors": [
+            {
+              "value": "",
+              "type": "Behavior",
+              "label": "Button states",
+              "description": "",
+              "group": "",
+              "extraInformation": [
+                "ButtonStates::ButtonFSM"
+              ],
+              "name": "ButtonFSM"
+            },
+            {
+              "value": "",
+              "type": "Behavior",
+              "label": "Effect capability",
+              "description": "",
+              "group": "",
+              "extraInformation": [
+                "EffectCapability::EffectBehavior"
+              ],
+              "name": "Effect"
+            },
+            {
+              "value": "",
+              "type": "String",
+              "label": "Idle state effect",
+              "description": "",
+              "group": "Effects",
+              "extraInformation": [],
+              "name": "IdleEffect"
+            },
+            {
+              "value": "",
+              "type": "String",
+              "label": "Focused state effect",
+              "description": "The state is Focused when the button is hovered or held outside.",
+              "group": "Effects",
+              "extraInformation": [],
+              "name": "FocusedEffect"
+            },
+            {
+              "value": "",
+              "type": "String",
+              "label": "Pressed state effect",
+              "description": "",
+              "group": "Effects",
+              "extraInformation": [],
+              "name": "PressedEffect"
+            }
+          ],
+          "sharedPropertyDescriptors": []
+        },
+        {
+          "description": "Change the animation of buttons according to their state.",
+          "fullName": "Button animation",
+          "name": "ButtonAnimationName",
+          "objectType": "",
+          "eventsFunctions": [
+            {
+              "fullName": "",
+              "functionType": "Action",
+              "name": "onCreated",
+              "sentence": "",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "AnimatableCapability::AnimatableBehavior::SetName"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Animation",
+                        "=",
+                        "IdleAnimationName"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "ButtonStates::ButtonAnimationName",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "fullName": "",
+              "functionType": "Action",
+              "name": "doStepPreEvents",
+              "sentence": "",
+              "events": [
+                {
+                  "colorB": 228,
+                  "colorG": 176,
+                  "colorR": 74,
+                  "creationTime": 0,
+                  "name": "Change based on state",
+                  "source": "",
+                  "type": "BuiltinCommonInstructions::Group",
+                  "events": [
+                    {
+                      "type": "BuiltinCommonInstructions::Standard",
+                      "conditions": [
+                        {
+                          "type": {
+                            "value": "ButtonStates::ButtonFSM::IsIdle"
+                          },
+                          "parameters": [
+                            "Object",
+                            "ButtonFSM",
+                            ""
+                          ]
+                        }
+                      ],
+                      "actions": [
+                        {
+                          "type": {
+                            "value": "AnimatableCapability::AnimatableBehavior::SetName"
+                          },
+                          "parameters": [
+                            "Object",
+                            "Animation",
+                            "=",
+                            "IdleAnimationName"
+                          ]
+                        }
+                      ]
+                    },
+                    {
+                      "type": "BuiltinCommonInstructions::Standard",
+                      "conditions": [
+                        {
+                          "type": {
+                            "value": "ButtonStates::ButtonFSM::IsFocused"
+                          },
+                          "parameters": [
+                            "Object",
+                            "ButtonFSM",
+                            ""
+                          ]
+                        }
+                      ],
+                      "actions": [
+                        {
+                          "type": {
+                            "value": "AnimatableCapability::AnimatableBehavior::SetName"
+                          },
+                          "parameters": [
+                            "Object",
+                            "Animation",
+                            "=",
+                            "FocusedAnimationName"
+                          ]
+                        }
+                      ]
+                    },
+                    {
+                      "type": "BuiltinCommonInstructions::Standard",
+                      "conditions": [
+                        {
+                          "type": {
+                            "value": "ButtonStates::ButtonFSM::IsPressed"
+                          },
+                          "parameters": [
+                            "Object",
+                            "ButtonFSM",
+                            ""
+                          ]
+                        }
+                      ],
+                      "actions": [
+                        {
+                          "type": {
+                            "value": "AnimatableCapability::AnimatableBehavior::SetName"
+                          },
+                          "parameters": [
+                            "Object",
+                            "Animation",
+                            "=",
+                            "PressedAnimationName"
+                          ]
+                        }
+                      ]
+                    }
+                  ],
+                  "parameters": []
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "ButtonStates::ButtonAnimationName",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "the idle state animation name of the object.",
+              "fullName": "Idle state animation name",
+              "functionType": "ExpressionAndCondition",
+              "group": "Button animation configuration",
+              "name": "IdleAnimationName",
+              "sentence": "the idle state animation name",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SetReturnString"
+                      },
+                      "parameters": [
+                        "IdleAnimationName"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "expressionType": {
+                "type": "string"
+              },
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "ButtonStates::ButtonAnimationName",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "fullName": "",
+              "functionType": "ActionWithOperator",
+              "getterName": "IdleAnimationName",
+              "name": "SetIdleAnimationName",
+              "sentence": "",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "ButtonStates::ButtonAnimationName::SetPropertyIdleAnimationName"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "=",
+                        "Value"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "ButtonStates::ButtonAnimationName",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "the focused state animation name of the object. The state is Focused when the button is hovered or held outside.",
+              "fullName": "Focused state animation name",
+              "functionType": "ExpressionAndCondition",
+              "group": "Button animation configuration",
+              "name": "FocusedAnimationName",
+              "sentence": "the focused state animation name",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SetReturnString"
+                      },
+                      "parameters": [
+                        "FocusedAnimationName"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "expressionType": {
+                "type": "string"
+              },
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "ButtonStates::ButtonAnimationName",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "fullName": "",
+              "functionType": "ActionWithOperator",
+              "getterName": "FocusedAnimationName",
+              "name": "SetFocusedAnimationName",
+              "sentence": "",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "ButtonStates::ButtonAnimationName::SetPropertyFocusedAnimationName"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "=",
+                        "Value"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "ButtonStates::ButtonAnimationName",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "the pressed state animation name of the object.",
+              "fullName": "Pressed state animation name",
+              "functionType": "ExpressionAndCondition",
+              "group": "Button animation configuration",
+              "name": "PressedAnimationName",
+              "sentence": "the pressed state animation name",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SetReturnString"
+                      },
+                      "parameters": [
+                        "PressedAnimationName"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "expressionType": {
+                "type": "string"
+              },
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "ButtonStates::ButtonAnimationName",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "fullName": "",
+              "functionType": "ActionWithOperator",
+              "getterName": "PressedAnimationName",
+              "name": "SetPressedAnimationName",
+              "sentence": "",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "ButtonStates::ButtonAnimationName::SetPropertyPressedAnimationName"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "=",
+                        "Value"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "ButtonStates::ButtonAnimationName",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            }
+          ],
+          "propertyDescriptors": [
+            {
+              "value": "",
+              "type": "Behavior",
+              "label": "Animatable capability",
+              "description": "",
+              "group": "",
+              "extraInformation": [
+                "AnimatableCapability::AnimatableBehavior"
+              ],
+              "name": "Animation"
+            },
+            {
+              "value": "",
+              "type": "Behavior",
+              "label": "Button states",
+              "description": "",
+              "group": "",
+              "extraInformation": [
+                "ButtonStates::ButtonFSM"
+              ],
+              "name": "ButtonFSM"
+            },
+            {
+              "value": "Idle",
+              "type": "String",
+              "label": "Idle state animation name",
+              "description": "",
+              "group": "Animation",
+              "extraInformation": [],
+              "name": "IdleAnimationName"
+            },
+            {
+              "value": "Focused",
+              "type": "String",
+              "label": "Focused state animation name",
+              "description": "The state is Focused when the button is hovered or held outside.",
+              "group": "Animation",
+              "extraInformation": [],
+              "name": "FocusedAnimationName"
+            },
+            {
+              "value": "Pressed",
+              "type": "String",
+              "label": "Pressed state animation name",
+              "description": "",
+              "group": "Animation",
+              "extraInformation": [],
+              "name": "PressedAnimationName"
+            }
+          ],
+          "sharedPropertyDescriptors": []
+        },
+        {
+          "description": "Smoothly change an effect on buttons according to their state.",
+          "fullName": "Button object effect tween",
+          "name": "ButtonObjectEffectTween",
+          "objectType": "",
+          "eventsFunctions": [
+            {
+              "fullName": "",
+              "functionType": "Action",
+              "name": "onCreated",
+              "sentence": "",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "ButtonStates::ButtonObjectEffectTween::SetPropertyEffectValue"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "=",
+                        "IdleValue"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "ButtonStates::ButtonObjectEffectTween",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "fullName": "",
+              "functionType": "Action",
+              "name": "doStepPreEvents",
+              "sentence": "",
+              "events": [
+                {
+                  "colorB": 228,
+                  "colorG": 176,
+                  "colorR": 74,
+                  "creationTime": 0,
+                  "name": "States",
+                  "source": "",
+                  "type": "BuiltinCommonInstructions::Group",
+                  "events": [
+                    {
+                      "type": "BuiltinCommonInstructions::Standard",
+                      "conditions": [
+                        {
+                          "type": {
+                            "value": "ButtonStates::ButtonFSM::IsIdle"
+                          },
+                          "parameters": [
+                            "Object",
+                            "ButtonFSM",
+                            ""
+                          ]
+                        },
+                        {
+                          "type": {
+                            "value": "ButtonStates::ButtonObjectEffectTween::PropertyPreviousState"
+                          },
+                          "parameters": [
+                            "Object",
+                            "Behavior",
+                            "!=",
+                            "\"Idle\""
+                          ]
+                        }
+                      ],
+                      "actions": [
+                        {
+                          "type": {
+                            "value": "ButtonStates::ButtonObjectEffectTween::SetPropertyPreviousState"
+                          },
+                          "parameters": [
+                            "Object",
+                            "Behavior",
+                            "=",
+                            "\"Idle\""
+                          ]
+                        },
+                        {
+                          "type": {
+                            "value": "ButtonStates::ButtonObjectEffectTween::FadeOut"
+                          },
+                          "parameters": [
+                            "Object",
+                            "Behavior",
+                            "IdleValue",
+                            ""
+                          ]
+                        }
+                      ]
+                    },
+                    {
+                      "type": "BuiltinCommonInstructions::Standard",
+                      "conditions": [
+                        {
+                          "type": {
+                            "value": "ButtonStates::ButtonFSM::IsFocused"
+                          },
+                          "parameters": [
+                            "Object",
+                            "ButtonFSM",
+                            ""
+                          ]
+                        }
+                      ],
+                      "actions": [],
+                      "events": [
+                        {
+                          "type": "BuiltinCommonInstructions::Standard",
+                          "conditions": [
+                            {
+                              "type": {
+                                "value": "ButtonStates::ButtonObjectEffectTween::PropertyPreviousState"
+                              },
+                              "parameters": [
+                                "Object",
+                                "Behavior",
+                                "=",
+                                "\"Idle\""
+                              ]
+                            }
+                          ],
+                          "actions": [
+                            {
+                              "type": {
+                                "value": "ButtonStates::ButtonObjectEffectTween::SetPropertyPreviousState"
+                              },
+                              "parameters": [
+                                "Object",
+                                "Behavior",
+                                "=",
+                                "\"Focused\""
+                              ]
+                            },
+                            {
+                              "type": {
+                                "value": "ButtonStates::ButtonObjectEffectTween::FadeIn"
+                              },
+                              "parameters": [
+                                "Object",
+                                "Behavior",
+                                "FocusedValue",
+                                ""
+                              ]
+                            }
+                          ]
+                        },
+                        {
+                          "type": "BuiltinCommonInstructions::Standard",
+                          "conditions": [
+                            {
+                              "type": {
+                                "value": "ButtonStates::ButtonObjectEffectTween::PropertyPreviousState"
+                              },
+                              "parameters": [
+                                "Object",
+                                "Behavior",
+                                "=",
+                                "\"Pressed\""
+                              ]
+                            }
+                          ],
+                          "actions": [
+                            {
+                              "type": {
+                                "value": "ButtonStates::ButtonObjectEffectTween::SetPropertyPreviousState"
+                              },
+                              "parameters": [
+                                "Object",
+                                "Behavior",
+                                "=",
+                                "\"Focused\""
+                              ]
+                            },
+                            {
+                              "type": {
+                                "value": "ButtonStates::ButtonObjectEffectTween::FadeOut"
+                              },
+                              "parameters": [
+                                "Object",
+                                "Behavior",
+                                "FocusedValue",
+                                ""
+                              ]
+                            }
+                          ]
+                        }
+                      ]
+                    },
+                    {
+                      "type": "BuiltinCommonInstructions::Standard",
+                      "conditions": [
+                        {
+                          "type": {
+                            "value": "ButtonStates::ButtonFSM::IsPressed"
+                          },
+                          "parameters": [
+                            "Object",
+                            "ButtonFSM",
+                            ""
+                          ]
+                        },
+                        {
+                          "type": {
+                            "value": "ButtonStates::ButtonObjectEffectTween::PropertyPreviousState"
+                          },
+                          "parameters": [
+                            "Object",
+                            "Behavior",
+                            "!=",
+                            "\"Pressed\""
+                          ]
+                        }
+                      ],
+                      "actions": [
+                        {
+                          "type": {
+                            "value": "ButtonStates::ButtonObjectEffectTween::SetPropertyPreviousState"
+                          },
+                          "parameters": [
+                            "Object",
+                            "Behavior",
+                            "=",
+                            "\"Pressed\""
+                          ]
+                        },
+                        {
+                          "type": {
+                            "value": "ButtonStates::ButtonObjectEffectTween::FadeIn"
+                          },
+                          "parameters": [
+                            "Object",
+                            "Behavior",
+                            "PressedValue",
+                            ""
+                          ]
+                        }
+                      ]
+                    }
+                  ],
+                  "parameters": []
+                },
+                {
+                  "colorB": 228,
+                  "colorG": 176,
+                  "colorR": 74,
+                  "creationTime": 0,
+                  "name": "Tween",
+                  "source": "",
+                  "type": "BuiltinCommonInstructions::Group",
+                  "events": [
+                    {
+                      "type": "BuiltinCommonInstructions::Standard",
+                      "conditions": [
+                        {
+                          "type": {
+                            "value": "ButtonStates::ButtonObjectEffectTween::PropertyTweenState"
+                          },
+                          "parameters": [
+                            "Object",
+                            "Behavior",
+                            "=",
+                            "\"FadeIn\""
+                          ]
+                        }
+                      ],
+                      "actions": [
+                        {
+                          "type": {
+                            "value": "ButtonStates::ButtonObjectEffectTween::PlayTween"
+                          },
+                          "parameters": [
+                            "Object",
+                            "Behavior",
+                            "FadeInDuration",
+                            "FadeInEasing",
+                            ""
+                          ]
+                        }
+                      ]
+                    },
+                    {
+                      "type": "BuiltinCommonInstructions::Standard",
+                      "conditions": [
+                        {
+                          "type": {
+                            "value": "ButtonStates::ButtonObjectEffectTween::PropertyTweenState"
+                          },
+                          "parameters": [
+                            "Object",
+                            "Behavior",
+                            "=",
+                            "\"FadeOut\""
+                          ]
+                        }
+                      ],
+                      "actions": [
+                        {
+                          "type": {
+                            "value": "ButtonStates::ButtonObjectEffectTween::PlayTween"
+                          },
+                          "parameters": [
+                            "Object",
+                            "Behavior",
+                            "FadeOutDuration",
+                            "FadeOutEasing",
+                            ""
+                          ]
+                        }
+                      ]
+                    }
+                  ],
+                  "parameters": []
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "ButtonStates::ButtonObjectEffectTween",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "fullName": "Time delta",
+              "functionType": "Expression",
+              "name": "TimeDelta",
+              "private": true,
+              "sentence": "",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SetReturnNumber"
+                      },
+                      "parameters": [
+                        "TimeDelta() * LayerTimeScale(Object.Layer())"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "expressionType": {
+                "type": "expression"
+              },
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "ButtonStates::ButtonObjectEffectTween",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "fullName": "Fade in",
+              "functionType": "Action",
+              "name": "FadeIn",
+              "private": true,
+              "sentence": "_PARAM0_ fade in to _PARAM2_",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "ButtonStates::ButtonObjectEffectTween::SetPropertyTweenTime"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "=",
+                        "0"
+                      ]
+                    },
+                    {
+                      "type": {
+                        "value": "ButtonStates::ButtonObjectEffectTween::SetPropertyTweenState"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "=",
+                        "\"FadeIn\""
+                      ]
+                    },
+                    {
+                      "type": {
+                        "value": "ButtonStates::ButtonObjectEffectTween::SetPropertyTweenInitialValue"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "=",
+                        "EffectValue"
+                      ]
+                    },
+                    {
+                      "type": {
+                        "value": "ButtonStates::ButtonObjectEffectTween::SetPropertyTweenTargetedValue"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "=",
+                        "Value"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "ButtonStates::ButtonObjectEffectTween",
+                  "type": "behavior"
+                },
+                {
+                  "description": "Value",
+                  "name": "Value",
+                  "type": "expression"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "fullName": "Fade out",
+              "functionType": "Action",
+              "name": "FadeOut",
+              "private": true,
+              "sentence": "_PARAM0_ fade out to _PARAM2_",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "ButtonStates::ButtonObjectEffectTween::SetPropertyTweenTime"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "=",
+                        "0"
+                      ]
+                    },
+                    {
+                      "type": {
+                        "value": "ButtonStates::ButtonObjectEffectTween::SetPropertyTweenState"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "=",
+                        "\"FadeOut\""
+                      ]
+                    },
+                    {
+                      "type": {
+                        "value": "ButtonStates::ButtonObjectEffectTween::SetPropertyTweenInitialValue"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "=",
+                        "EffectValue"
+                      ]
+                    },
+                    {
+                      "type": {
+                        "value": "ButtonStates::ButtonObjectEffectTween::SetPropertyTweenTargetedValue"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "=",
+                        "Value"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "ButtonStates::ButtonObjectEffectTween",
+                  "type": "behavior"
+                },
+                {
+                  "description": "Value",
+                  "name": "Value",
+                  "type": "expression"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "fullName": "Play tween",
+              "functionType": "Action",
+              "name": "PlayTween",
+              "private": true,
+              "sentence": "Tween the effect property of object _PARAM0_ over _PARAM2_ seconds with _PARAM3_ easing",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [
+                    {
+                      "type": {
+                        "value": "ButtonStates::ButtonObjectEffectTween::PropertyTweenTime"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "<",
+                        "Duration"
+                      ]
+                    }
+                  ],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "ButtonStates::ButtonObjectEffectTween::SetPropertyTweenTime"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "+",
+                        "Object.Behavior::TimeDelta()"
+                      ]
+                    },
+                    {
+                      "type": {
+                        "value": "ButtonStates::ButtonObjectEffectTween::SetPropertyEffectValue"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "=",
+                        "Tween::Ease(Easing, TweenInitialValue, TweenTargetedValue, TweenTime / Duration)"
+                      ]
+                    }
+                  ]
+                },
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [
+                    {
+                      "type": {
+                        "value": "ButtonStates::ButtonObjectEffectTween::PropertyTweenTime"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        ">=",
+                        "Duration"
+                      ]
+                    }
+                  ],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "ButtonStates::ButtonObjectEffectTween::SetPropertyTweenState"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "=",
+                        "\"NoTween\""
+                      ]
+                    },
+                    {
+                      "type": {
+                        "value": "ButtonStates::ButtonObjectEffectTween::SetPropertyEffectValue"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "=",
+                        "TweenTargetedValue"
+                      ]
+                    }
+                  ]
+                },
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "EffectCapability::EffectBehavior::SetEffectDoubleParameter"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Effect",
+                        "EffectName",
+                        "EffectProperty",
+                        "EffectValue"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "ButtonStates::ButtonObjectEffectTween",
+                  "type": "behavior"
+                },
+                {
+                  "description": "Duration (in seconds)",
+                  "name": "Duration",
+                  "type": "expression"
+                },
+                {
+                  "description": "Easing",
+                  "name": "Easing",
+                  "supplementaryInformation": "[]",
+                  "type": "string"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "the effect name of the object.",
+              "fullName": "Effect name",
+              "functionType": "ExpressionAndCondition",
+              "group": "Button effect tween configuration",
+              "name": "EffectName",
+              "sentence": "the effect name",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SetReturnString"
+                      },
+                      "parameters": [
+                        "EffectName"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "expressionType": {
+                "type": "string"
+              },
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "ButtonStates::ButtonObjectEffectTween",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "the effect parameter of the object. The effect parameter names can be found in the effects tab with the \"Show parameter names\" action of the drop down menu.",
+              "fullName": "Effect parameter",
+              "functionType": "ExpressionAndCondition",
+              "group": "Button effect tween configuration",
+              "name": "EffectProperty",
+              "sentence": "the effect parameter",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SetReturnString"
+                      },
+                      "parameters": [
+                        "EffectProperty"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "expressionType": {
+                "type": "string"
+              },
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "ButtonStates::ButtonObjectEffectTween",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "Change the effect parameter of the object. The effect parameter names can be found in the effects tab with the \"Show parameter names\" action of the drop down menu.",
+              "fullName": "Effect parameter",
+              "functionType": "Action",
+              "getterName": "EffectName",
+              "group": "Button effect tween configuration",
+              "name": "SetEffectProperty",
+              "sentence": "Change the tweened effect of _PARAM0_ to _PARAM2_ with parameter _PARAM3_",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "ButtonStates::ButtonObjectEffectTween::SetPropertyEffectName"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "=",
+                        "Value"
+                      ]
+                    },
+                    {
+                      "type": {
+                        "value": "ButtonStates::ButtonObjectEffectTween::SetPropertyEffectProperty"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "=",
+                        "Value"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "ButtonStates::ButtonObjectEffectTween",
+                  "type": "behavior"
+                },
+                {
+                  "description": "Effect name",
+                  "name": "EffectName",
+                  "type": "string"
+                },
+                {
+                  "description": "Parameter name",
+                  "name": "PropertyName",
+                  "type": "string"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "the idle effect parameter value of the object.",
+              "fullName": "Idle effect parameter value",
+              "functionType": "ExpressionAndCondition",
+              "group": "Button effect tween configuration",
+              "name": "IdleValue",
+              "sentence": "the idle effect parameter value",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SetReturnNumber"
+                      },
+                      "parameters": [
+                        "IdleValue"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "expressionType": {
+                "type": "expression"
+              },
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "ButtonStates::ButtonObjectEffectTween",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "fullName": "",
+              "functionType": "ActionWithOperator",
+              "getterName": "IdleValue",
+              "name": "SetIdleValue",
+              "sentence": "",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "ButtonStates::ButtonObjectEffectTween::SetPropertyIdleValue"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "=",
+                        "Value"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "ButtonStates::ButtonObjectEffectTween",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "the focused effect parameter value of the object. The state is Focused when the button is hovered or held outside.",
+              "fullName": "Focused effect parameter value",
+              "functionType": "ExpressionAndCondition",
+              "group": "Button effect tween configuration",
+              "name": "FocusedValue",
+              "sentence": "the focused effect parameter value",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SetReturnNumber"
+                      },
+                      "parameters": [
+                        "FocusedValue"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "expressionType": {
+                "type": "expression"
+              },
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "ButtonStates::ButtonObjectEffectTween",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "fullName": "",
+              "functionType": "ActionWithOperator",
+              "getterName": "FocusedValue",
+              "name": "SetFocusedValue",
+              "sentence": "",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "ButtonStates::ButtonObjectEffectTween::SetPropertyFocusedValue"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "=",
+                        "Value"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "ButtonStates::ButtonObjectEffectTween",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "the pressed effect parameter value of the object.",
+              "fullName": "Pressed effect parameter value",
+              "functionType": "ExpressionAndCondition",
+              "group": "Button effect tween configuration",
+              "name": "PressedValue",
+              "sentence": "the pressed effect parameter value",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SetReturnNumber"
+                      },
+                      "parameters": [
+                        "PressedValue"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "expressionType": {
+                "type": "expression"
+              },
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "ButtonStates::ButtonObjectEffectTween",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "fullName": "",
+              "functionType": "ActionWithOperator",
+              "getterName": "PressedValue",
+              "name": "SetPressedValue",
+              "sentence": "",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "ButtonStates::ButtonObjectEffectTween::SetPropertyPressedValue"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "=",
+                        "Value"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "ButtonStates::ButtonObjectEffectTween",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "the fade-in easing of the object.",
+              "fullName": "Fade-in easing",
+              "functionType": "ExpressionAndCondition",
+              "group": "Button effect tween configuration",
+              "name": "FadeInEasing",
+              "sentence": "the fade-in easing",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SetReturnString"
+                      },
+                      "parameters": [
+                        "FadeInEasing"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "expressionType": {
+                "supplementaryInformation": "[\"linear\",\"easeInQuad\",\"easeOutQuad\",\"easeInOutQuad\",\"easeInCubic\",\"easeOutCubic\",\"easeInOutCubic\",\"easeInQuart\",\"easeOutQuart\",\"easeInOutQuart\",\"easeInQuint\",\"easeOutQuint\",\"easeInOutQuint\",\"easeInOutSine\",\"easeInExpo\",\"easeOutExpo\",\"easeInOutExpo\",\"easeInCirc\",\"easeOutCirc\",\"easeInOutCirc\",\"easeOutBounce\",\"easeInBack\",\"easeOutBack\",\"easeInOutBack\",\"elastic\",\"swingFromTo\",\"swingFrom\",\"swingTo\",\"bounce\",\"bouncePast\",\"easeFromTo\",\"easeFrom\",\"easeTo\"]",
+                "type": "stringWithSelector"
+              },
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "ButtonStates::ButtonObjectEffectTween",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "fullName": "",
+              "functionType": "ActionWithOperator",
+              "getterName": "FadeInEasing",
+              "name": "SetFadeInEasing",
+              "sentence": "",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "ButtonStates::ButtonObjectEffectTween::SetPropertyFadeInEasing"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "=",
+                        "Value"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "ButtonStates::ButtonObjectEffectTween",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "the fade-out easing of the object.",
+              "fullName": "Fade-out easing",
+              "functionType": "ExpressionAndCondition",
+              "group": "Button effect tween configuration",
+              "name": "FadeOutEasing",
+              "sentence": "the fade-out easing",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SetReturnString"
+                      },
+                      "parameters": [
+                        "FadeOutEasing"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "expressionType": {
+                "supplementaryInformation": "[\"linear\",\"easeInQuad\",\"easeOutQuad\",\"easeInOutQuad\",\"easeInCubic\",\"easeOutCubic\",\"easeInOutCubic\",\"easeInQuart\",\"easeOutQuart\",\"easeInOutQuart\",\"easeInQuint\",\"easeOutQuint\",\"easeInOutQuint\",\"easeInOutSine\",\"easeInExpo\",\"easeOutExpo\",\"easeInOutExpo\",\"easeInCirc\",\"easeOutCirc\",\"easeInOutCirc\",\"easeOutBounce\",\"easeInBack\",\"easeOutBack\",\"easeInOutBack\",\"elastic\",\"swingFromTo\",\"swingFrom\",\"swingTo\",\"bounce\",\"bouncePast\",\"easeFromTo\",\"easeFrom\",\"easeTo\"]",
+                "type": "stringWithSelector"
+              },
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "ButtonStates::ButtonObjectEffectTween",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "fullName": "",
+              "functionType": "ActionWithOperator",
+              "getterName": "FadeOutEasing",
+              "name": "SetFadeOutEasing",
+              "sentence": "",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "ButtonStates::ButtonObjectEffectTween::SetPropertyFadeOutEasing"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "=",
+                        "Value"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "ButtonStates::ButtonObjectEffectTween",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "the fade-in duration of the object.",
+              "fullName": "Fade-in duration",
+              "functionType": "ExpressionAndCondition",
+              "group": "Button effect tween configuration",
+              "name": "FadeInDuration",
+              "sentence": "the fade-in duration",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SetReturnNumber"
+                      },
+                      "parameters": [
+                        "FadeInDuration"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "expressionType": {
+                "type": "expression"
+              },
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "ButtonStates::ButtonObjectEffectTween",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "fullName": "",
+              "functionType": "ActionWithOperator",
+              "getterName": "FadeInDuration",
+              "name": "SetFadeInDuration",
+              "sentence": "",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "ButtonStates::ButtonObjectEffectTween::SetPropertyFadeInDuration"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "=",
+                        "Value"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "ButtonStates::ButtonObjectEffectTween",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "the fade-out duration of the object.",
+              "fullName": "Fade-out duration",
+              "functionType": "ExpressionAndCondition",
+              "group": "Button effect tween configuration",
+              "name": "FadeOutDuration",
+              "sentence": "the fade-out duration",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SetReturnNumber"
+                      },
+                      "parameters": [
+                        "FadeOutDuration"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "expressionType": {
+                "type": "expression"
+              },
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "ButtonStates::ButtonObjectEffectTween",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "fullName": "",
+              "functionType": "ActionWithOperator",
+              "getterName": "FadeOutDuration",
+              "name": "SetFadeOutDuration",
+              "sentence": "",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "ButtonStates::ButtonObjectEffectTween::SetPropertyFadeOutDuration"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "=",
+                        "Value"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "ButtonStates::ButtonObjectEffectTween",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            }
+          ],
+          "propertyDescriptors": [
+            {
+              "value": "",
+              "type": "Behavior",
+              "label": "Button states",
+              "description": "",
+              "group": "",
+              "extraInformation": [
+                "ButtonStates::ButtonFSM"
+              ],
+              "name": "ButtonFSM"
+            },
+            {
+              "value": "",
+              "type": "Behavior",
+              "label": "Effect capability",
+              "description": "",
+              "group": "",
+              "extraInformation": [
+                "EffectCapability::EffectBehavior"
+              ],
+              "name": "Effect"
+            },
+            {
+              "value": "Effect",
+              "type": "String",
+              "label": "Effect name",
+              "description": "",
+              "group": "Effect",
+              "extraInformation": [],
+              "name": "EffectName"
+            },
+            {
+              "value": "",
+              "type": "String",
+              "label": "Effect parameter",
+              "description": "The effect parameter names can be found in the effects tab with the \"Show parameter names\" action of the drop down menu.",
+              "group": "Effect",
+              "extraInformation": [],
+              "name": "EffectProperty"
+            },
+            {
+              "value": "0",
+              "type": "Number",
+              "label": "Idle effect parameter value",
+              "description": "",
+              "group": "Value",
+              "extraInformation": [],
+              "name": "IdleValue"
+            },
+            {
+              "value": "0",
+              "type": "Number",
+              "label": "Focused effect parameter value",
+              "description": "The state is Focused when the button is hovered or held outside.",
+              "group": "Value",
+              "extraInformation": [],
+              "name": "FocusedValue"
+            },
+            {
+              "value": "0",
+              "type": "Number",
+              "label": "Pressed effect parameter value",
+              "description": "",
+              "group": "Value",
+              "extraInformation": [],
+              "name": "PressedValue"
+            },
+            {
+              "value": "easeInOutQuad",
+              "type": "Choice",
+              "label": "Fade-in easing",
+              "description": "",
+              "group": "Speed",
+              "extraInformation": [
+                "linear",
+                "easeInQuad",
+                "easeOutQuad",
+                "easeInOutQuad",
+                "easeInCubic",
+                "easeOutCubic",
+                "easeInOutCubic",
+                "easeInQuart",
+                "easeOutQuart",
+                "easeInOutQuart",
+                "easeInQuint",
+                "easeOutQuint",
+                "easeInOutQuint",
+                "easeInOutSine",
+                "easeInExpo",
+                "easeOutExpo",
+                "easeInOutExpo",
+                "easeInCirc",
+                "easeOutCirc",
+                "easeInOutCirc",
+                "easeOutBounce",
+                "easeInBack",
+                "easeOutBack",
+                "easeInOutBack",
+                "elastic",
+                "swingFromTo",
+                "swingFrom",
+                "swingTo",
+                "bounce",
+                "bouncePast",
+                "easeFromTo",
+                "easeFrom",
+                "easeTo"
+              ],
+              "name": "FadeInEasing"
+            },
+            {
+              "value": "easeInOutQuad",
+              "type": "Choice",
+              "label": "Fade-out easing",
+              "description": "",
+              "group": "Speed",
+              "extraInformation": [
+                "linear",
+                "easeInQuad",
+                "easeOutQuad",
+                "easeInOutQuad",
+                "easeInCubic",
+                "easeOutCubic",
+                "easeInOutCubic",
+                "easeInQuart",
+                "easeOutQuart",
+                "easeInOutQuart",
+                "easeInQuint",
+                "easeOutQuint",
+                "easeInOutQuint",
+                "easeInOutSine",
+                "easeInExpo",
+                "easeOutExpo",
+                "easeInOutExpo",
+                "easeInCirc",
+                "easeOutCirc",
+                "easeInOutCirc",
+                "easeOutBounce",
+                "easeInBack",
+                "easeOutBack",
+                "easeInOutBack",
+                "elastic",
+                "swingFromTo",
+                "swingFrom",
+                "swingTo",
+                "bounce",
+                "bouncePast",
+                "easeFromTo",
+                "easeFrom",
+                "easeTo"
+              ],
+              "name": "FadeOutEasing"
+            },
+            {
+              "value": "0.125",
+              "type": "Number",
+              "unit": "Second",
+              "label": "Fade-in duration",
+              "description": "",
+              "group": "Speed",
+              "extraInformation": [],
+              "name": "FadeInDuration"
+            },
+            {
+              "value": "0.5",
+              "type": "Number",
+              "unit": "Second",
+              "label": "Fade-out duration",
+              "description": "",
+              "group": "Speed",
+              "extraInformation": [],
+              "name": "FadeOutDuration"
+            },
+            {
+              "value": "Idle",
+              "type": "Choice",
+              "label": "",
+              "description": "",
+              "group": "",
+              "extraInformation": [
+                "Idle",
+                "Focused",
+                "Pressed"
+              ],
+              "hidden": true,
+              "name": "PreviousState"
+            },
+            {
+              "value": "0",
+              "type": "Number",
+              "label": "",
+              "description": "",
+              "group": "",
+              "extraInformation": [],
+              "hidden": true,
+              "name": "TweenInitialValue"
+            },
+            {
+              "value": "0",
+              "type": "Number",
+              "label": "",
+              "description": "",
+              "group": "",
+              "extraInformation": [],
+              "hidden": true,
+              "name": "TweenTargetedValue"
+            },
+            {
+              "value": "0",
+              "type": "Number",
+              "label": "",
+              "description": "",
+              "group": "",
+              "extraInformation": [],
+              "hidden": true,
+              "name": "TweenTime"
+            },
+            {
+              "value": "NoTween",
+              "type": "Choice",
+              "label": "",
+              "description": "",
+              "group": "",
+              "extraInformation": [
+                "NoTween",
+                "FadeIn",
+                "FadeOut"
+              ],
+              "hidden": true,
+              "name": "TweenState"
+            },
+            {
+              "value": "0",
+              "type": "Number",
+              "label": "",
+              "description": "",
+              "group": "",
+              "extraInformation": [],
+              "hidden": true,
+              "name": "EffectValue"
+            }
+          ],
+          "sharedPropertyDescriptors": []
+        },
+        {
+          "description": "Smoothly resize buttons according to their state.",
+          "fullName": "Button scale tween",
+          "name": "ButtonScaleTween",
+          "objectType": "",
+          "eventsFunctions": [
+            {
+              "fullName": "",
+              "functionType": "Action",
+              "name": "onCreated",
+              "sentence": "",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "ScalableCapability::ScalableBehavior::SetValue"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Scale",
+                        "=",
+                        "IdleScale"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "ButtonStates::ButtonScaleTween",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "fullName": "",
+              "functionType": "Action",
+              "name": "doStepPreEvents",
+              "sentence": "",
+              "events": [
+                {
+                  "colorB": 228,
+                  "colorG": 176,
+                  "colorR": 74,
+                  "creationTime": 0,
+                  "name": "Change based on state",
+                  "source": "",
+                  "type": "BuiltinCommonInstructions::Group",
+                  "events": [
+                    {
+                      "type": "BuiltinCommonInstructions::Standard",
+                      "conditions": [
+                        {
+                          "type": {
+                            "value": "ButtonStates::ButtonFSM::IsIdle"
+                          },
+                          "parameters": [
+                            "Object",
+                            "ButtonFSM",
+                            ""
+                          ]
+                        },
+                        {
+                          "type": {
+                            "value": "ButtonStates::ButtonScaleTween::PropertyPreviousState"
+                          },
+                          "parameters": [
+                            "Object",
+                            "Behavior",
+                            "!=",
+                            "\"Idle\""
+                          ]
+                        }
+                      ],
+                      "actions": [
+                        {
+                          "type": {
+                            "value": "ButtonStates::ButtonScaleTween::SetPropertyPreviousState"
+                          },
+                          "parameters": [
+                            "Object",
+                            "Behavior",
+                            "=",
+                            "\"Idle\""
+                          ]
+                        },
+                        {
+                          "type": {
+                            "value": "ButtonStates::ButtonScaleTween::FadeOut"
+                          },
+                          "parameters": [
+                            "Object",
+                            "Behavior",
+                            "IdleScale",
+                            ""
+                          ]
+                        }
+                      ]
+                    },
+                    {
+                      "type": "BuiltinCommonInstructions::Standard",
+                      "conditions": [
+                        {
+                          "type": {
+                            "value": "ButtonStates::ButtonFSM::IsFocused"
+                          },
+                          "parameters": [
+                            "Object",
+                            "ButtonFSM",
+                            ""
+                          ]
+                        }
+                      ],
+                      "actions": [],
+                      "events": [
+                        {
+                          "type": "BuiltinCommonInstructions::Standard",
+                          "conditions": [
+                            {
+                              "type": {
+                                "value": "ButtonStates::ButtonScaleTween::PropertyPreviousState"
+                              },
+                              "parameters": [
+                                "Object",
+                                "Behavior",
+                                "=",
+                                "\"Idle\""
+                              ]
+                            }
+                          ],
+                          "actions": [
+                            {
+                              "type": {
+                                "value": "ButtonStates::ButtonScaleTween::SetPropertyPreviousState"
+                              },
+                              "parameters": [
+                                "Object",
+                                "Behavior",
+                                "=",
+                                "\"Focused\""
+                              ]
+                            },
+                            {
+                              "type": {
+                                "value": "ButtonStates::ButtonScaleTween::FadeIn"
+                              },
+                              "parameters": [
+                                "Object",
+                                "Behavior",
+                                "FocusedScale",
+                                ""
+                              ]
+                            }
+                          ]
+                        },
+                        {
+                          "type": "BuiltinCommonInstructions::Standard",
+                          "conditions": [
+                            {
+                              "type": {
+                                "value": "ButtonStates::ButtonScaleTween::PropertyPreviousState"
+                              },
+                              "parameters": [
+                                "Object",
+                                "Behavior",
+                                "=",
+                                "\"Pressed\""
+                              ]
+                            }
+                          ],
+                          "actions": [
+                            {
+                              "type": {
+                                "value": "ButtonStates::ButtonScaleTween::SetPropertyPreviousState"
+                              },
+                              "parameters": [
+                                "Object",
+                                "Behavior",
+                                "=",
+                                "\"Focused\""
+                              ]
+                            },
+                            {
+                              "type": {
+                                "value": "ButtonStates::ButtonScaleTween::FadeOut"
+                              },
+                              "parameters": [
+                                "Object",
+                                "Behavior",
+                                "FocusedScale",
+                                ""
+                              ]
+                            }
+                          ]
+                        }
+                      ]
+                    },
+                    {
+                      "type": "BuiltinCommonInstructions::Standard",
+                      "conditions": [
+                        {
+                          "type": {
+                            "value": "ButtonStates::ButtonFSM::IsPressed"
+                          },
+                          "parameters": [
+                            "Object",
+                            "ButtonFSM",
+                            ""
+                          ]
+                        },
+                        {
+                          "type": {
+                            "value": "ButtonStates::ButtonScaleTween::PropertyPreviousState"
+                          },
+                          "parameters": [
+                            "Object",
+                            "Behavior",
+                            "!=",
+                            "\"Pressed\""
+                          ]
+                        }
+                      ],
+                      "actions": [
+                        {
+                          "type": {
+                            "value": "ButtonStates::ButtonScaleTween::SetPropertyPreviousState"
+                          },
+                          "parameters": [
+                            "Object",
+                            "Behavior",
+                            "=",
+                            "\"Pressed\""
+                          ]
+                        },
+                        {
+                          "type": {
+                            "value": "ButtonStates::ButtonScaleTween::FadeIn"
+                          },
+                          "parameters": [
+                            "Object",
+                            "Behavior",
+                            "PressedScale",
+                            ""
+                          ]
+                        }
+                      ]
+                    }
+                  ],
+                  "parameters": []
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "ButtonStates::ButtonScaleTween",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "fullName": "Fade in",
+              "functionType": "Action",
+              "name": "FadeIn",
+              "private": true,
+              "sentence": "_PARAM0_ fade in to _PARAM2_",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "Tween::AddObjectScaleTween"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Tween",
+                        "\"__ButtonScaleTween.Fade\"",
+                        "Value",
+                        "Value",
+                        "FadeInEasing",
+                        "1000 * FadeInDuration",
+                        "no",
+                        "yes"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "ButtonStates::ButtonScaleTween",
+                  "type": "behavior"
+                },
+                {
+                  "description": "Value",
+                  "name": "Value",
+                  "type": "expression"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "fullName": "Fade out",
+              "functionType": "Action",
+              "name": "FadeOut",
+              "private": true,
+              "sentence": "_PARAM0_ fade out to _PARAM2_",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "Tween::AddObjectScaleTween"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Tween",
+                        "\"__ButtonScaleTween.Fade\"",
+                        "Value",
+                        "Value",
+                        "FadeOutEasing",
+                        "1000 * FadeOutDuration",
+                        "no",
+                        "yes"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "ButtonStates::ButtonScaleTween",
+                  "type": "behavior"
+                },
+                {
+                  "description": "Value",
+                  "name": "Value",
+                  "type": "expression"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "the idle state size scale of the object.",
+              "fullName": "Idle state size scale",
+              "functionType": "ExpressionAndCondition",
+              "group": "Button scale tween configuration",
+              "name": "IdleScale",
+              "sentence": "the idle state size scale",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SetReturnNumber"
+                      },
+                      "parameters": [
+                        "IdleScale"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "expressionType": {
+                "type": "expression"
+              },
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "ButtonStates::ButtonScaleTween",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "fullName": "",
+              "functionType": "ActionWithOperator",
+              "getterName": "IdleScale",
+              "name": "SetIdleScale",
+              "sentence": "",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "ButtonStates::ButtonScaleTween::SetPropertyIdleScale"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "=",
+                        "Value"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "ButtonStates::ButtonScaleTween",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "the focused state size scale of the object. The state is Focused when the button is hovered or held outside.",
+              "fullName": "Focused state size scale",
+              "functionType": "ExpressionAndCondition",
+              "group": "Button scale tween configuration",
+              "name": "FocusedScale",
+              "sentence": "the focused state size scale",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SetReturnNumber"
+                      },
+                      "parameters": [
+                        "FocusedScale"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "expressionType": {
+                "type": "expression"
+              },
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "ButtonStates::ButtonScaleTween",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "fullName": "",
+              "functionType": "ActionWithOperator",
+              "getterName": "FocusedScale",
+              "name": "SetFocusedScale",
+              "sentence": "",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "ButtonStates::ButtonScaleTween::SetPropertyFocusedScale"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "=",
+                        "Value"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "ButtonStates::ButtonScaleTween",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "the pressed state size scale of the object.",
+              "fullName": "Pressed state size scale",
+              "functionType": "ExpressionAndCondition",
+              "group": "Button scale tween configuration",
+              "name": "PressedScale",
+              "sentence": "the pressed state size scale",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SetReturnNumber"
+                      },
+                      "parameters": [
+                        "PressedScale"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "expressionType": {
+                "type": "expression"
+              },
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "ButtonStates::ButtonScaleTween",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "fullName": "",
+              "functionType": "ActionWithOperator",
+              "getterName": "PressedScale",
+              "name": "SetPressedScale",
+              "sentence": "",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "ButtonStates::ButtonScaleTween::SetPropertyPressedScale"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "=",
+                        "Value"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "ButtonStates::ButtonScaleTween",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "the fade-in duration of the object.",
+              "fullName": "Fade-in duration",
+              "functionType": "ExpressionAndCondition",
+              "group": "Button scale tween configuration",
+              "name": "FadeInDuration",
+              "sentence": "the fade-in duration",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SetReturnNumber"
+                      },
+                      "parameters": [
+                        "FadeInDuration"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "expressionType": {
+                "type": "expression"
+              },
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "ButtonStates::ButtonScaleTween",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "fullName": "",
+              "functionType": "ActionWithOperator",
+              "getterName": "FadeInDuration",
+              "name": "SetFadeInDuration",
+              "sentence": "",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "ButtonStates::ButtonScaleTween::SetPropertyFadeInDuration"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "=",
+                        "Value"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "ButtonStates::ButtonScaleTween",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "the fade-out duration of the object.",
+              "fullName": "Fade-out duration",
+              "functionType": "ExpressionAndCondition",
+              "group": "Button scale tween configuration",
+              "name": "FadeOutDuration",
+              "sentence": "the fade-out duration",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SetReturnNumber"
+                      },
+                      "parameters": [
+                        "FadeOutDuration"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "expressionType": {
+                "type": "expression"
+              },
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "ButtonStates::ButtonScaleTween",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "fullName": "",
+              "functionType": "ActionWithOperator",
+              "getterName": "FadeOutDuration",
+              "name": "SetFadeOutDuration",
+              "sentence": "",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "ButtonStates::ButtonScaleTween::SetPropertyFadeOutDuration"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "=",
+                        "Value"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "ButtonStates::ButtonScaleTween",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "the fade-in easing of the object.",
+              "fullName": "Fade-in easing",
+              "functionType": "ExpressionAndCondition",
+              "group": "Button scale tween configuration",
+              "name": "FadeInEasing",
+              "sentence": "the fade-in easing",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SetReturnString"
+                      },
+                      "parameters": [
+                        "FadeInEasing"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "expressionType": {
+                "supplementaryInformation": "[\"linear\",\"easeInQuad\",\"easeOutQuad\",\"easeInOutQuad\",\"easeInCubic\",\"easeOutCubic\",\"easeInOutCubic\",\"easeInQuart\",\"easeOutQuart\",\"easeInOutQuart\",\"easeInQuint\",\"easeOutQuint\",\"easeInOutQuint\",\"easeInOutSine\",\"easeInExpo\",\"easeOutExpo\",\"easeInOutExpo\",\"easeInCirc\",\"easeOutCirc\",\"easeInOutCirc\",\"easeOutBounce\",\"easeInBack\",\"easeOutBack\",\"easeInOutBack\",\"elastic\",\"swingFromTo\",\"swingFrom\",\"swingTo\",\"bounce\",\"bouncePast\",\"easeFromTo\",\"easeFrom\",\"easeTo\"]",
+                "type": "stringWithSelector"
+              },
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "ButtonStates::ButtonScaleTween",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "fullName": "",
+              "functionType": "ActionWithOperator",
+              "getterName": "FadeInEasing",
+              "name": "SetFadeInEasing",
+              "sentence": "",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "ButtonStates::ButtonScaleTween::SetPropertyFadeInEasing"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "=",
+                        "Value"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "ButtonStates::ButtonScaleTween",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "the fade-out easing of the object.",
+              "fullName": "Fade-out easing",
+              "functionType": "ExpressionAndCondition",
+              "group": "Button scale tween configuration",
+              "name": "FadeOutEasing",
+              "sentence": "the fade-out easing",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SetReturnString"
+                      },
+                      "parameters": [
+                        "FadeOutEasing"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "expressionType": {
+                "supplementaryInformation": "[\"linear\",\"easeInQuad\",\"easeOutQuad\",\"easeInOutQuad\",\"easeInCubic\",\"easeOutCubic\",\"easeInOutCubic\",\"easeInQuart\",\"easeOutQuart\",\"easeInOutQuart\",\"easeInQuint\",\"easeOutQuint\",\"easeInOutQuint\",\"easeInOutSine\",\"easeInExpo\",\"easeOutExpo\",\"easeInOutExpo\",\"easeInCirc\",\"easeOutCirc\",\"easeInOutCirc\",\"easeOutBounce\",\"easeInBack\",\"easeOutBack\",\"easeInOutBack\",\"elastic\",\"swingFromTo\",\"swingFrom\",\"swingTo\",\"bounce\",\"bouncePast\",\"easeFromTo\",\"easeFrom\",\"easeTo\"]",
+                "type": "stringWithSelector"
+              },
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "ButtonStates::ButtonScaleTween",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "fullName": "",
+              "functionType": "ActionWithOperator",
+              "getterName": "FadeOutEasing",
+              "name": "SetFadeOutEasing",
+              "sentence": "",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "ButtonStates::ButtonScaleTween::SetPropertyFadeOutEasing"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "=",
+                        "Value"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "ButtonStates::ButtonScaleTween",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            }
+          ],
+          "propertyDescriptors": [
+            {
+              "value": "",
+              "type": "Behavior",
+              "label": "Scalable capability",
+              "description": "",
+              "group": "",
+              "extraInformation": [
+                "ScalableCapability::ScalableBehavior"
+              ],
+              "name": "Scale"
+            },
+            {
+              "value": "",
+              "type": "Behavior",
+              "label": "Button states behavior (required)",
+              "description": "",
+              "group": "",
+              "extraInformation": [
+                "ButtonStates::ButtonFSM"
+              ],
+              "name": "ButtonFSM"
+            },
+            {
+              "value": "",
+              "type": "Behavior",
+              "label": "Tween behavior (required)",
+              "description": "",
+              "group": "",
+              "extraInformation": [
+                "Tween::TweenBehavior"
+              ],
+              "name": "Tween"
+            },
+            {
+              "value": "1",
+              "type": "Number",
+              "unit": "Dimensionless",
+              "label": "Idle state size scale",
+              "description": "",
+              "group": "Size",
+              "extraInformation": [],
+              "name": "IdleScale"
+            },
+            {
+              "value": "1",
+              "type": "Number",
+              "unit": "Dimensionless",
+              "label": "Focused state size scale",
+              "description": "The state is Focused when the button is hovered or held outside.",
+              "group": "Size",
+              "extraInformation": [],
+              "name": "FocusedScale"
+            },
+            {
+              "value": "0.1",
+              "type": "Number",
+              "unit": "Second",
+              "label": "Fade-in duration",
+              "description": "",
+              "group": "Speed",
+              "extraInformation": [],
+              "name": "FadeInDuration"
+            },
+            {
+              "value": "0.2",
+              "type": "Number",
+              "unit": "Second",
+              "label": "Fade-out duration",
+              "description": "",
+              "group": "Speed",
+              "extraInformation": [],
+              "name": "FadeOutDuration"
+            },
+            {
+              "value": "0.95",
+              "type": "Number",
+              "unit": "Dimensionless",
+              "label": "Pressed state size scale",
+              "description": "",
+              "group": "Size",
+              "extraInformation": [],
+              "name": "PressedScale"
+            },
+            {
+              "value": "easeInOutQuad",
+              "type": "Choice",
+              "label": "Fade-in easing",
+              "description": "",
+              "group": "Speed",
+              "extraInformation": [
+                "linear",
+                "easeInQuad",
+                "easeOutQuad",
+                "easeInOutQuad",
+                "easeInCubic",
+                "easeOutCubic",
+                "easeInOutCubic",
+                "easeInQuart",
+                "easeOutQuart",
+                "easeInOutQuart",
+                "easeInQuint",
+                "easeOutQuint",
+                "easeInOutQuint",
+                "easeInOutSine",
+                "easeInExpo",
+                "easeOutExpo",
+                "easeInOutExpo",
+                "easeInCirc",
+                "easeOutCirc",
+                "easeInOutCirc",
+                "easeOutBounce",
+                "easeInBack",
+                "easeOutBack",
+                "easeInOutBack",
+                "elastic",
+                "swingFromTo",
+                "swingFrom",
+                "swingTo",
+                "bounce",
+                "bouncePast",
+                "easeFromTo",
+                "easeFrom",
+                "easeTo"
+              ],
+              "name": "FadeInEasing"
+            },
+            {
+              "value": "easeInOutQuad",
+              "type": "Choice",
+              "label": "Fade-out easing",
+              "description": "",
+              "group": "Speed",
+              "extraInformation": [
+                "linear",
+                "easeInQuad",
+                "easeOutQuad",
+                "easeInOutQuad",
+                "easeInCubic",
+                "easeOutCubic",
+                "easeInOutCubic",
+                "easeInQuart",
+                "easeOutQuart",
+                "easeInOutQuart",
+                "easeInQuint",
+                "easeOutQuint",
+                "easeInOutQuint",
+                "easeInOutSine",
+                "easeInExpo",
+                "easeOutExpo",
+                "easeInOutExpo",
+                "easeInCirc",
+                "easeOutCirc",
+                "easeInOutCirc",
+                "easeOutBounce",
+                "easeInBack",
+                "easeOutBack",
+                "easeInOutBack",
+                "elastic",
+                "swingFromTo",
+                "swingFrom",
+                "swingTo",
+                "bounce",
+                "bouncePast",
+                "easeFromTo",
+                "easeFrom",
+                "easeTo"
+              ],
+              "name": "FadeOutEasing"
+            },
+            {
+              "value": "Idle",
+              "type": "Choice",
+              "label": "",
+              "description": "",
+              "group": "",
+              "extraInformation": [
+                "Idle",
+                "Focused",
+                "Pressed"
+              ],
+              "hidden": true,
+              "name": "PreviousState"
+            }
+          ],
+          "sharedPropertyDescriptors": []
+        },
+        {
+          "description": "Smoothly change the color tint of buttons according to their state.",
+          "fullName": "Button color tint tween",
+          "name": "ButtonColorTintTween",
+          "objectType": "Sprite",
+          "eventsFunctions": [
+            {
+              "fullName": "",
+              "functionType": "Action",
+              "name": "onCreated",
+              "sentence": "",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "ChangeColor"
+                      },
+                      "parameters": [
+                        "Object",
+                        "IdleColorTint"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "supplementaryInformation": "Sprite",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "ButtonStates::ButtonColorTintTween",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "fullName": "",
+              "functionType": "Action",
+              "name": "doStepPreEvents",
+              "sentence": "",
+              "events": [
+                {
+                  "colorB": 228,
+                  "colorG": 176,
+                  "colorR": 74,
+                  "creationTime": 0,
+                  "name": "States",
+                  "source": "",
+                  "type": "BuiltinCommonInstructions::Group",
+                  "events": [
+                    {
+                      "type": "BuiltinCommonInstructions::Standard",
+                      "conditions": [
+                        {
+                          "type": {
+                            "value": "ButtonStates::ButtonFSM::IsIdle"
+                          },
+                          "parameters": [
+                            "Object",
+                            "ButtonFSM",
+                            ""
+                          ]
+                        },
+                        {
+                          "type": {
+                            "value": "ButtonStates::ButtonColorTintTween::PropertyPreviousState"
+                          },
+                          "parameters": [
+                            "Object",
+                            "Behavior",
+                            "!=",
+                            "\"Idle\""
+                          ]
+                        }
+                      ],
+                      "actions": [
+                        {
+                          "type": {
+                            "value": "ButtonStates::ButtonColorTintTween::SetPropertyPreviousState"
+                          },
+                          "parameters": [
+                            "Object",
+                            "Behavior",
+                            "=",
+                            "\"Idle\""
+                          ]
+                        },
+                        {
+                          "type": {
+                            "value": "ButtonStates::ButtonColorTintTween::FadeOut"
+                          },
+                          "parameters": [
+                            "Object",
+                            "Behavior",
+                            "IdleColorTint",
+                            ""
+                          ]
+                        }
+                      ]
+                    },
+                    {
+                      "type": "BuiltinCommonInstructions::Standard",
+                      "conditions": [
+                        {
+                          "type": {
+                            "value": "ButtonStates::ButtonFSM::IsFocused"
+                          },
+                          "parameters": [
+                            "Object",
+                            "ButtonFSM",
+                            ""
+                          ]
+                        }
+                      ],
+                      "actions": [],
+                      "events": [
+                        {
+                          "type": "BuiltinCommonInstructions::Standard",
+                          "conditions": [
+                            {
+                              "type": {
+                                "value": "ButtonStates::ButtonColorTintTween::PropertyPreviousState"
+                              },
+                              "parameters": [
+                                "Object",
+                                "Behavior",
+                                "=",
+                                "\"Idle\""
+                              ]
+                            }
+                          ],
+                          "actions": [
+                            {
+                              "type": {
+                                "value": "ButtonStates::ButtonColorTintTween::SetPropertyPreviousState"
+                              },
+                              "parameters": [
+                                "Object",
+                                "Behavior",
+                                "=",
+                                "\"Focused\""
+                              ]
+                            },
+                            {
+                              "type": {
+                                "value": "ButtonStates::ButtonColorTintTween::FadeIn"
+                              },
+                              "parameters": [
+                                "Object",
+                                "Behavior",
+                                "FocusedColorTint",
+                                ""
+                              ]
+                            }
+                          ]
+                        },
+                        {
+                          "type": "BuiltinCommonInstructions::Standard",
+                          "conditions": [
+                            {
+                              "type": {
+                                "value": "ButtonStates::ButtonColorTintTween::PropertyPreviousState"
+                              },
+                              "parameters": [
+                                "Object",
+                                "Behavior",
+                                "=",
+                                "\"Pressed\""
+                              ]
+                            }
+                          ],
+                          "actions": [
+                            {
+                              "type": {
+                                "value": "ButtonStates::ButtonColorTintTween::SetPropertyPreviousState"
+                              },
+                              "parameters": [
+                                "Object",
+                                "Behavior",
+                                "=",
+                                "\"Focused\""
+                              ]
+                            },
+                            {
+                              "type": {
+                                "value": "ButtonStates::ButtonColorTintTween::FadeOut"
+                              },
+                              "parameters": [
+                                "Object",
+                                "Behavior",
+                                "FocusedColorTint",
+                                ""
+                              ]
+                            }
+                          ]
+                        }
+                      ]
+                    },
+                    {
+                      "type": "BuiltinCommonInstructions::Standard",
+                      "conditions": [
+                        {
+                          "type": {
+                            "value": "ButtonStates::ButtonFSM::IsPressed"
+                          },
+                          "parameters": [
+                            "Object",
+                            "ButtonFSM",
+                            ""
+                          ]
+                        },
+                        {
+                          "type": {
+                            "value": "ButtonStates::ButtonColorTintTween::PropertyPreviousState"
+                          },
+                          "parameters": [
+                            "Object",
+                            "Behavior",
+                            "!=",
+                            "\"Pressed\""
+                          ]
+                        }
+                      ],
+                      "actions": [
+                        {
+                          "type": {
+                            "value": "ButtonStates::ButtonColorTintTween::SetPropertyPreviousState"
+                          },
+                          "parameters": [
+                            "Object",
+                            "Behavior",
+                            "=",
+                            "\"Pressed\""
+                          ]
+                        },
+                        {
+                          "type": {
+                            "value": "ButtonStates::ButtonColorTintTween::FadeIn"
+                          },
+                          "parameters": [
+                            "Object",
+                            "Behavior",
+                            "PressedColorTint",
+                            ""
+                          ]
+                        }
+                      ]
+                    }
+                  ],
+                  "parameters": []
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "supplementaryInformation": "Sprite",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "ButtonStates::ButtonColorTintTween",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "fullName": "Fade in",
+              "functionType": "Action",
+              "name": "FadeIn",
+              "private": true,
+              "sentence": "_PARAM0_ fade in to _PARAM2_",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "Tween::AddObjectColorTween"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Tween",
+                        "\"__ButtonColorTintTween.Fade\"",
+                        "Value",
+                        "FadeInEasing",
+                        "1000 * FadeInDuration",
+                        "no",
+                        "yes"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "supplementaryInformation": "Sprite",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "ButtonStates::ButtonColorTintTween",
+                  "type": "behavior"
+                },
+                {
+                  "description": "Value",
+                  "name": "Value",
+                  "type": "color"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "fullName": "Fade out",
+              "functionType": "Action",
+              "name": "FadeOut",
+              "private": true,
+              "sentence": "_PARAM0_ fade out to _PARAM2_",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "Tween::AddObjectColorTween"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Tween",
+                        "\"__ButtonColorTintTween.Fade\"",
+                        "Value",
+                        "FadeOutEasing",
+                        "1000 * FadeOutDuration",
+                        "no",
+                        "yes"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "supplementaryInformation": "Sprite",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "ButtonStates::ButtonColorTintTween",
+                  "type": "behavior"
+                },
+                {
+                  "description": "Value",
+                  "name": "Value",
+                  "type": "color"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "the idle state color tint of the object.",
+              "fullName": "Idle state color tint",
+              "functionType": "ExpressionAndCondition",
+              "group": "Button color tint tween",
+              "name": "IdleColorTint",
+              "sentence": "the idle state color tint",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SetReturnString"
+                      },
+                      "parameters": [
+                        "IdleColorTint"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "expressionType": {
+                "type": "color"
+              },
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "supplementaryInformation": "Sprite",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "ButtonStates::ButtonColorTintTween",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "fullName": "",
+              "functionType": "ActionWithOperator",
+              "getterName": "IdleColorTint",
+              "name": "SetIdleColorTint",
+              "sentence": "",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "ButtonStates::ButtonColorTintTween::SetPropertyIdleColorTint"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "=",
+                        "Value"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "supplementaryInformation": "Sprite",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "ButtonStates::ButtonColorTintTween",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "the focused state color tint of the object. The state is Focused when the button is hovered or held outside.",
+              "fullName": "Focused state color tint",
+              "functionType": "ExpressionAndCondition",
+              "group": "Button color tint tween",
+              "name": "FocusedColorTint",
+              "sentence": "the focused state color tint",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SetReturnString"
+                      },
+                      "parameters": [
+                        "FocusedColorTint"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "expressionType": {
+                "type": "color"
+              },
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "supplementaryInformation": "Sprite",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "ButtonStates::ButtonColorTintTween",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "fullName": "",
+              "functionType": "ActionWithOperator",
+              "getterName": "FocusedColorTint",
+              "name": "SetFocusedColorTint",
+              "sentence": "",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "ButtonStates::ButtonColorTintTween::SetPropertyFocusedColorTint"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "=",
+                        "Value"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "supplementaryInformation": "Sprite",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "ButtonStates::ButtonColorTintTween",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "the pressed state color tint of the object.",
+              "fullName": "Pressed state color tint",
+              "functionType": "ExpressionAndCondition",
+              "group": "Button color tint tween",
+              "name": "PressedColorTint",
+              "sentence": "the pressed state color tint",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SetReturnString"
+                      },
+                      "parameters": [
+                        "PressedColorTint"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "expressionType": {
+                "type": "color"
+              },
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "supplementaryInformation": "Sprite",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "ButtonStates::ButtonColorTintTween",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "fullName": "",
+              "functionType": "ActionWithOperator",
+              "getterName": "PressedColorTint",
+              "name": "SetPressedColorTint",
+              "sentence": "",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "ButtonStates::ButtonColorTintTween::SetPropertyPressedColorTint"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "=",
+                        "Value"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "supplementaryInformation": "Sprite",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "ButtonStates::ButtonColorTintTween",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "the fade-in duration of the object.",
+              "fullName": "Fade-in duration",
+              "functionType": "ExpressionAndCondition",
+              "group": "Button color tint tween",
+              "name": "FadeInDuration",
+              "sentence": "the fade-in duration",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SetReturnNumber"
+                      },
+                      "parameters": [
+                        "FadeInDuration"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "expressionType": {
+                "type": "expression"
+              },
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "supplementaryInformation": "Sprite",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "ButtonStates::ButtonColorTintTween",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "fullName": "",
+              "functionType": "ActionWithOperator",
+              "getterName": "FadeInDuration",
+              "name": "SetFadeInDuration",
+              "sentence": "",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "ButtonStates::ButtonColorTintTween::SetPropertyFadeInDuration"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "=",
+                        "Value"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "supplementaryInformation": "Sprite",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "ButtonStates::ButtonColorTintTween",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "the fade-out duration of the object.",
+              "fullName": "Fade-out duration",
+              "functionType": "ExpressionAndCondition",
+              "group": "Button color tint tween",
+              "name": "FadeOutDuration",
+              "sentence": "the fade-out duration",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SetReturnNumber"
+                      },
+                      "parameters": [
+                        "FadeOutDuration"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "expressionType": {
+                "type": "expression"
+              },
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "supplementaryInformation": "Sprite",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "ButtonStates::ButtonColorTintTween",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "fullName": "",
+              "functionType": "ActionWithOperator",
+              "getterName": "FadeOutDuration",
+              "name": "SetFadeOutDuration",
+              "sentence": "",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "ButtonStates::ButtonColorTintTween::SetPropertyFadeOutDuration"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "=",
+                        "Value"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "supplementaryInformation": "Sprite",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "ButtonStates::ButtonColorTintTween",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "the fade-in easing of the object.",
+              "fullName": "Fade-in easing",
+              "functionType": "ExpressionAndCondition",
+              "group": "Button color tint tween",
+              "name": "FadeInEasing",
+              "sentence": "the fade-in easing",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SetReturnString"
+                      },
+                      "parameters": [
+                        "FadeInEasing"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "expressionType": {
+                "supplementaryInformation": "[\"linear\",\"easeInQuad\",\"easeOutQuad\",\"easeInOutQuad\",\"easeInCubic\",\"easeOutCubic\",\"easeInOutCubic\",\"easeInQuart\",\"easeOutQuart\",\"easeInOutQuart\",\"easeInQuint\",\"easeOutQuint\",\"easeInOutQuint\",\"easeInOutSine\",\"easeInExpo\",\"easeOutExpo\",\"easeInOutExpo\",\"easeInCirc\",\"easeOutCirc\",\"easeInOutCirc\",\"easeOutBounce\",\"easeInBack\",\"easeOutBack\",\"easeInOutBack\",\"elastic\",\"swingFromTo\",\"swingFrom\",\"swingTo\",\"bounce\",\"bouncePast\",\"easeFromTo\",\"easeFrom\",\"easeTo\"]",
+                "type": "stringWithSelector"
+              },
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "supplementaryInformation": "Sprite",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "ButtonStates::ButtonColorTintTween",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "fullName": "",
+              "functionType": "ActionWithOperator",
+              "getterName": "FadeInEasing",
+              "name": "SetFadeInEasing",
+              "sentence": "",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "ButtonStates::ButtonColorTintTween::SetPropertyFadeInEasing"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "=",
+                        "Value"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "supplementaryInformation": "Sprite",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "ButtonStates::ButtonColorTintTween",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "the fade-out easing of the object.",
+              "fullName": "Fade-out easing",
+              "functionType": "ExpressionAndCondition",
+              "group": "Button color tint tween",
+              "name": "FadeOutEasing",
+              "sentence": "the fade-out easing",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SetReturnString"
+                      },
+                      "parameters": [
+                        "FadeOutEasing"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "expressionType": {
+                "supplementaryInformation": "[\"linear\",\"easeInQuad\",\"easeOutQuad\",\"easeInOutQuad\",\"easeInCubic\",\"easeOutCubic\",\"easeInOutCubic\",\"easeInQuart\",\"easeOutQuart\",\"easeInOutQuart\",\"easeInQuint\",\"easeOutQuint\",\"easeInOutQuint\",\"easeInOutSine\",\"easeInExpo\",\"easeOutExpo\",\"easeInOutExpo\",\"easeInCirc\",\"easeOutCirc\",\"easeInOutCirc\",\"easeOutBounce\",\"easeInBack\",\"easeOutBack\",\"easeInOutBack\",\"elastic\",\"swingFromTo\",\"swingFrom\",\"swingTo\",\"bounce\",\"bouncePast\",\"easeFromTo\",\"easeFrom\",\"easeTo\"]",
+                "type": "stringWithSelector"
+              },
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "supplementaryInformation": "Sprite",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "ButtonStates::ButtonColorTintTween",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "fullName": "",
+              "functionType": "ActionWithOperator",
+              "getterName": "FadeOutEasing",
+              "name": "SetFadeOutEasing",
+              "sentence": "",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "ButtonStates::ButtonColorTintTween::SetPropertyFadeOutEasing"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "=",
+                        "Value"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "supplementaryInformation": "Sprite",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "ButtonStates::ButtonColorTintTween",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            }
+          ],
+          "propertyDescriptors": [
+            {
+              "value": "",
+              "type": "Behavior",
+              "label": "Button states",
+              "description": "",
+              "group": "",
+              "extraInformation": [
+                "ButtonStates::ButtonFSM"
+              ],
+              "name": "ButtonFSM"
+            },
+            {
+              "value": "",
+              "type": "Behavior",
+              "label": "Tween",
+              "description": "",
+              "group": "",
+              "extraInformation": [
+                "Tween::TweenBehavior"
+              ],
+              "name": "Tween"
+            },
+            {
+              "value": "255;255;255",
+              "type": "Color",
+              "label": "Idle state color tint",
+              "description": "",
+              "group": "Color",
+              "extraInformation": [],
+              "name": "IdleColorTint"
+            },
+            {
+              "value": "192;192;192",
+              "type": "Color",
+              "label": "Focused state color tint",
+              "description": "The state is Focused when the button is hovered or held outside.",
+              "group": "Color",
+              "extraInformation": [],
+              "name": "FocusedColorTint"
+            },
+            {
+              "value": "64;64;64",
+              "type": "Color",
+              "label": "Pressed state color tint",
+              "description": "",
+              "group": "Color",
+              "extraInformation": [],
+              "name": "PressedColorTint"
+            },
+            {
+              "value": "0.1",
+              "type": "Number",
+              "unit": "Second",
+              "label": "Fade-in duration",
+              "description": "",
+              "group": "Speed",
+              "extraInformation": [],
+              "name": "FadeInDuration"
+            },
+            {
+              "value": "0.2",
+              "type": "Number",
+              "unit": "Second",
+              "label": "Fade-out duration",
+              "description": "",
+              "group": "Speed",
+              "extraInformation": [],
+              "name": "FadeOutDuration"
+            },
+            {
+              "value": "easeInOutQuad",
+              "type": "Choice",
+              "label": "Fade-in easing",
+              "description": "",
+              "group": "Speed",
+              "extraInformation": [
+                "linear",
+                "easeInQuad",
+                "easeOutQuad",
+                "easeInOutQuad",
+                "easeInCubic",
+                "easeOutCubic",
+                "easeInOutCubic",
+                "easeInQuart",
+                "easeOutQuart",
+                "easeInOutQuart",
+                "easeInQuint",
+                "easeOutQuint",
+                "easeInOutQuint",
+                "easeInOutSine",
+                "easeInExpo",
+                "easeOutExpo",
+                "easeInOutExpo",
+                "easeInCirc",
+                "easeOutCirc",
+                "easeInOutCirc",
+                "easeOutBounce",
+                "easeInBack",
+                "easeOutBack",
+                "easeInOutBack",
+                "elastic",
+                "swingFromTo",
+                "swingFrom",
+                "swingTo",
+                "bounce",
+                "bouncePast",
+                "easeFromTo",
+                "easeFrom",
+                "easeTo"
+              ],
+              "name": "FadeInEasing"
+            },
+            {
+              "value": "easeInOutQuad",
+              "type": "Choice",
+              "label": "Fade-out easing",
+              "description": "",
+              "group": "Speed",
+              "extraInformation": [
+                "linear",
+                "easeInQuad",
+                "easeOutQuad",
+                "easeInOutQuad",
+                "easeInCubic",
+                "easeOutCubic",
+                "easeInOutCubic",
+                "easeInQuart",
+                "easeOutQuart",
+                "easeInOutQuart",
+                "easeInQuint",
+                "easeOutQuint",
+                "easeInOutQuint",
+                "easeInOutSine",
+                "easeInExpo",
+                "easeOutExpo",
+                "easeInOutExpo",
+                "easeInCirc",
+                "easeOutCirc",
+                "easeInOutCirc",
+                "easeOutBounce",
+                "easeInBack",
+                "easeOutBack",
+                "easeInOutBack",
+                "elastic",
+                "swingFromTo",
+                "swingFrom",
+                "swingTo",
+                "bounce",
+                "bouncePast",
+                "easeFromTo",
+                "easeFrom",
+                "easeTo"
+              ],
+              "name": "FadeOutEasing"
+            },
+            {
+              "value": "Idle",
+              "type": "Choice",
+              "label": "",
+              "description": "",
+              "group": "",
+              "extraInformation": [
+                "Idle",
+                "Focused",
+                "Pressed"
+              ],
+              "hidden": true,
+              "name": "PreviousState"
+            }
+          ],
+          "sharedPropertyDescriptors": []
+        }
+      ],
+      "eventsBasedObjects": []
+    },
+    {
+      "author": "",
+      "category": "Input",
+      "extensionNamespace": "",
+      "fullName": "Multitouch joystick and buttons (sprite)",
+      "helpPath": "/objects/multitouch-joystick",
+      "iconUrl": "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4NCjwhLS0gR2VuZXJhdG9yOiBBZG9iZSBJbGx1c3RyYXRvciAyMy4wLjMsIFNWRyBFeHBvcnQgUGx1Zy1JbiAuIFNWRyBWZXJzaW9uOiA2LjAwIEJ1aWxkIDApICAtLT4NCjxzdmcgdmVyc2lvbj0iMS4xIiBpZD0iSWNvbnMiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4Ig0KCSB2aWV3Qm94PSIwIDAgMzIgMzIiIHN0eWxlPSJlbmFibGUtYmFja2dyb3VuZDpuZXcgMCAwIDMyIDMyOyIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+DQo8c3R5bGUgdHlwZT0idGV4dC9jc3MiPg0KCS5zdDB7ZmlsbDpub25lO3N0cm9rZTojMDAwMDAwO3N0cm9rZS13aWR0aDoyO3N0cm9rZS1saW5lY2FwOnJvdW5kO3N0cm9rZS1saW5lam9pbjpyb3VuZDtzdHJva2UtbWl0ZXJsaW1pdDoxMDt9DQo8L3N0eWxlPg0KPGNpcmNsZSBjbGFzcz0ic3QwIiBjeD0iMTYiIGN5PSIxNiIgcj0iMTMiLz4NCjxwb2x5bGluZSBjbGFzcz0ic3QwIiBwb2ludHM9IjI4LjQsMTIgMjAsMTIgMjAsMy42ICIvPg0KPHBvbHlsaW5lIGNsYXNzPSJzdDAiIHBvaW50cz0iMjAsMjguNCAyMCwyMCAyOC40LDIwICIvPg0KPHBvbHlsaW5lIGNsYXNzPSJzdDAiIHBvaW50cz0iMy42LDIwIDEyLDIwIDEyLDI4LjQgIi8+DQo8cG9seWxpbmUgY2xhc3M9InN0MCIgcG9pbnRzPSIxMiwzLjYgMTIsMTIgMy42LDEyICIvPg0KPHBvbHlnb24gY2xhc3M9InN0MCIgcG9pbnRzPSIxNiw2IDE2LjcsNyAxNS4zLDcgIi8+DQo8cG9seWdvbiBjbGFzcz0ic3QwIiBwb2ludHM9IjE2LDI2IDE1LjMsMjUgMTYuNywyNSAiLz4NCjxwb2x5Z29uIGNsYXNzPSJzdDAiIHBvaW50cz0iNiwxNiA3LDE1LjMgNywxNi43ICIvPg0KPHBvbHlnb24gY2xhc3M9InN0MCIgcG9pbnRzPSIyNiwxNiAyNSwxNi43IDI1LDE1LjMgIi8+DQo8L3N2Zz4NCg==",
+      "name": "SpriteMultitouchJoystick",
+      "previewIconUrl": "https://resources.gdevelop-app.com/assets/Icons/Line Hero Pack/Master/SVG/Videogames/Videogames_controller_joystick_arrows_direction.svg",
+      "shortDescription": "Joysticks or buttons for touchscreens.",
+      "version": "1.4.0",
+      "description": [
+        "Multitouch joysticks can be used the same way as physical gamepads:",
+        "- 4 or 8 directions",
+        "- Analogus pads",
+        "- Player selection",
+        "- Controls mapping for top-down movement and platformer characters",
+        "",
+        "There are ready-to-use joysticks in the asset-store [multitouch joysticks pack](https://editor.gdevelop.io/?initial-dialog=asset-store&asset-pack=multitouch-joysticks-multitouch-joysticks)."
+      ],
+      "origin": {
+        "identifier": "SpriteMultitouchJoystick",
+        "name": "gdevelop-extension-store"
+      },
+      "tags": [
+        "multitouch",
+        "joystick",
+        "thumbstick",
+        "controller",
+        "touchscreen",
+        "twin stick",
+        "shooter",
+        "virtual",
+        "platformer",
+        "platform",
+        "top-down"
+      ],
+      "authorIds": [
+        "gqDaZjCfevOOxBYkK6zlhtZnXCg1",
+        "1OgYzWp5UeVPbiWGJwI6vqfgZLC3",
+        "v0YRpdAnIucZFgiRCCecqVnGKno2",
+        "IWykYNRvhCZBN3vEgKEbBPOR3Oc2"
+      ],
+      "dependencies": [],
+      "globalVariables": [],
+      "sceneVariables": [
+        {
+          "name": "Controllers",
+          "type": "array",
+          "children": [
+            {
+              "type": "structure",
+              "children": [
+                {
+                  "name": "Buttons",
+                  "type": "array",
+                  "children": [
+                    {
+                      "type": "structure",
+                      "children": [
+                        {
+                          "name": "State",
+                          "type": "string",
+                          "value": "Idle"
+                        }
+                      ]
+                    }
+                  ]
+                },
+                {
+                  "name": "Joystick",
+                  "type": "structure",
+                  "children": []
+                }
+              ]
+            }
+          ]
+        }
+      ],
+      "eventsFunctions": [
+        {
+          "description": "Check if a button is pressed on a gamepad.",
+          "fullName": "Multitouch controller button pressed",
+          "functionType": "Condition",
+          "name": "IsButtonPressed",
+          "sentence": "Button _PARAM2_ of multitouch controller _PARAM1_ is pressed",
+          "events": [
+            {
+              "type": "BuiltinCommonInstructions::Standard",
+              "conditions": [
+                {
+                  "type": {
+                    "value": "StringVariable"
+                  },
+                  "parameters": [
+                    "Controllers[ControllerIdentifier].Buttons[Button].State",
+                    "=",
+                    "\"Pressed\""
+                  ]
+                }
+              ],
+              "actions": [
+                {
+                  "type": {
+                    "value": "SetReturnBoolean"
+                  },
+                  "parameters": [
+                    "True"
+                  ]
+                }
+              ]
+            }
+          ],
+          "parameters": [
+            {
+              "description": "Multitouch controller identifier (1, 2, 3, 4...)",
+              "name": "ControllerIdentifier",
+              "type": "expression"
+            },
+            {
+              "description": "Button name",
+              "name": "Button",
+              "supplementaryInformation": "[\"A\",\"CROSS\",\"B\",\"CIRCLE\",\"X\",\"SQUARE\",\"Y\",\"TRIANGLE\",\"LB\",\"L1\",\"RB\",\"R1\",\"LT\",\"L2\",\"RT\",\"R2\",\"UP\",\"DOWN\",\"LEFT\",\"RIGHT\",\"BACK\",\"SHARE\",\"START\",\"OPTIONS\",\"CLICK_STICK_LEFT\",\"CLICK_STICK_RIGHT\",\"PS_BUTTON\",\"CLICK_TOUCHPAD\"]",
+              "type": "string"
+            }
+          ],
+          "objectGroups": []
+        },
+        {
+          "description": "Check if a button is released on a gamepad.",
+          "fullName": "Multitouch controller button released",
+          "functionType": "Condition",
+          "name": "IsButtonReleased",
+          "sentence": "Button _PARAM2_ of multitouch controller _PARAM1_ is released",
+          "events": [
+            {
+              "type": "BuiltinCommonInstructions::Standard",
+              "conditions": [
+                {
+                  "type": {
+                    "value": "StringVariable"
+                  },
+                  "parameters": [
+                    "Controllers[ControllerIdentifier].Buttons[Button].State",
+                    "=",
+                    "\"Released\""
+                  ]
+                }
+              ],
+              "actions": [
+                {
+                  "type": {
+                    "value": "SetReturnBoolean"
+                  },
+                  "parameters": [
+                    "True"
+                  ]
+                }
+              ]
+            }
+          ],
+          "parameters": [
+            {
+              "description": "Multitouch controller identifier (1, 2, 3, 4...)",
+              "name": "ControllerIdentifier",
+              "type": "expression"
+            },
+            {
+              "description": "Button name",
+              "name": "Button",
+              "supplementaryInformation": "[\"A\",\"CROSS\",\"B\",\"CIRCLE\",\"X\",\"SQUARE\",\"Y\",\"TRIANGLE\",\"LB\",\"L1\",\"RB\",\"R1\",\"LT\",\"L2\",\"RT\",\"R2\",\"UP\",\"DOWN\",\"LEFT\",\"RIGHT\",\"BACK\",\"SHARE\",\"START\",\"OPTIONS\",\"CLICK_STICK_LEFT\",\"CLICK_STICK_RIGHT\",\"PS_BUTTON\",\"CLICK_TOUCHPAD\"]",
+              "type": "string"
+            }
+          ],
+          "objectGroups": []
+        },
+        {
+          "description": "Change a button state for a multitouch controller.",
+          "fullName": "Button state",
+          "functionType": "Action",
+          "name": "SetButtonState",
+          "private": true,
+          "sentence": "Mark _PARAM2_ button as _PARAM3_ for multitouch controller _PARAM1_",
+          "events": [
+            {
+              "type": "BuiltinCommonInstructions::Standard",
+              "conditions": [],
+              "actions": [
+                {
+                  "type": {
+                    "value": "SetStringVariable"
+                  },
+                  "parameters": [
+                    "Controllers[ControllerIdentifier].Buttons[Button].State",
+                    "=",
+                    "ButtonState"
+                  ]
+                }
+              ]
+            }
+          ],
+          "parameters": [
+            {
+              "description": "Multitouch controller identifier (1, 2, 3, 4...)",
+              "name": "ControllerIdentifier",
+              "type": "expression"
+            },
+            {
+              "description": "Button name",
+              "name": "Button",
+              "type": "string"
+            },
+            {
+              "description": "Button state",
+              "name": "ButtonState",
+              "supplementaryInformation": "[\"Idle\",\"Pressed\",\"Released\"]",
+              "type": "stringWithSelector"
+            }
+          ],
+          "objectGroups": []
+        },
+        {
+          "description": "Change the dead zone radius of a joystick. The deadzone is an area for which movement on sticks won't be taken into account (instead, the stick will be considered as not moved).",
+          "fullName": "Dead zone radius",
+          "functionType": "Action",
+          "name": "SetDeadZone",
+          "private": true,
+          "sentence": "Change the dead zone of multitouch joystick _PARAM2_ of multitouch controller _PARAM1_ to _PARAM3_",
+          "events": [
+            {
+              "type": "BuiltinCommonInstructions::Standard",
+              "conditions": [],
+              "actions": [
+                {
+                  "type": {
+                    "value": "SetNumberVariable"
+                  },
+                  "parameters": [
+                    "Controllers[ControllerIdentifier].Joystick[JoystickIdentifier].DeadZone",
+                    "=",
+                    "DeadZoneRadius"
+                  ]
+                }
+              ]
+            }
+          ],
+          "parameters": [
+            {
+              "description": "Multitouch controller identifier (1, 2, 3, 4...)",
+              "name": "ControllerIdentifier",
+              "type": "expression"
+            },
+            {
+              "description": "Joystick name",
+              "name": "JoystickIdentifier",
+              "type": "string"
+            },
+            {
+              "description": "Dead zone radius",
+              "name": "DeadZoneRadius",
+              "supplementaryInformation": "[]",
+              "type": "expression"
+            }
+          ],
+          "objectGroups": []
+        },
+        {
+          "description": "Return the dead zone radius of a joystick. The deadzone is an area for which movement on sticks won't be taken into account (instead, the stick will be considered as not moved).",
+          "fullName": "Dead zone radius",
+          "functionType": "Expression",
+          "name": "DeadZone",
+          "private": true,
+          "sentence": "Change multitouch joystick _PARAM2_ of multitouch controller _PARAM1_ dead zone to _PARAM3_",
+          "events": [
+            {
+              "type": "BuiltinCommonInstructions::Standard",
+              "conditions": [],
+              "actions": [
+                {
+                  "type": {
+                    "value": "SetReturnNumber"
+                  },
+                  "parameters": [
+                    "Controllers[ControllerIdentifier].Joystick[JoystickIdentifier].DeadZone"
+                  ]
+                }
+              ]
+            }
+          ],
+          "expressionType": {
+            "type": "expression"
+          },
+          "parameters": [
+            {
+              "description": "Multitouch controller identifier (1, 2, 3, 4...)",
+              "name": "ControllerIdentifier",
+              "type": "expression"
+            },
+            {
+              "description": "Joystick name",
+              "name": "JoystickIdentifier",
+              "type": "string"
+            }
+          ],
+          "objectGroups": []
+        },
+        {
+          "description": "the direction index (left = 1, bottom = 1, right = 2, top = 3) for an angle (in degrees).",
+          "fullName": "Angle to 4-way index",
+          "functionType": "ExpressionAndCondition",
+          "name": "AngleTo4Way",
+          "private": true,
+          "sentence": "The angle _PARAM1_ 4-way index",
+          "events": [
+            {
+              "type": "BuiltinCommonInstructions::Standard",
+              "conditions": [],
+              "actions": [
+                {
+                  "type": {
+                    "value": "SetReturnNumber"
+                  },
+                  "parameters": [
+                    "mod(round(Angle * 4 / 360), 4)"
+                  ]
+                }
+              ]
+            }
+          ],
+          "expressionType": {
+            "type": "expression"
+          },
+          "parameters": [
+            {
+              "description": "Angle",
+              "name": "Angle",
+              "type": "expression"
+            }
+          ],
+          "objectGroups": []
+        },
+        {
+          "description": "the direction index (left = 1, bottom-left = 1... top-left = 7) for an angle (in degrees).",
+          "fullName": "Angle to 8-way index",
+          "functionType": "ExpressionAndCondition",
+          "name": "AngleTo8Way",
+          "private": true,
+          "sentence": "The angle _PARAM1_ 8-way index",
+          "events": [
+            {
+              "type": "BuiltinCommonInstructions::Standard",
+              "conditions": [],
+              "actions": [
+                {
+                  "type": {
+                    "value": "SetReturnNumber"
+                  },
+                  "parameters": [
+                    "mod(round(Angle * 8 / 360), 8)"
+                  ]
+                }
+              ]
+            }
+          ],
+          "expressionType": {
+            "type": "expression"
+          },
+          "parameters": [
+            {
+              "description": "Angle",
+              "name": "Angle",
+              "type": "expression"
+            }
+          ],
+          "objectGroups": []
+        },
+        {
+          "description": "Check if angle is in a given direction.",
+          "fullName": "Angle 4-way direction",
+          "functionType": "Condition",
+          "name": "IsAngleIn4WayDirection",
+          "private": true,
+          "sentence": "The angle _PARAM1_ is the 4-way direction _PARAM2_",
+          "events": [
+            {
+              "type": "BuiltinCommonInstructions::Standard",
+              "conditions": [
+                {
+                  "type": {
+                    "value": "BuiltinCommonInstructions::CompareStrings"
+                  },
+                  "parameters": [
+                    "Direction",
+                    "=",
+                    "\"Right\""
+                  ]
+                },
+                {
+                  "type": {
+                    "value": "SpriteMultitouchJoystick::AngleTo4Way"
+                  },
+                  "parameters": [
+                    "",
+                    "=",
+                    "0",
+                    "Angle",
+                    ""
+                  ]
+                }
+              ],
+              "actions": [
+                {
+                  "type": {
+                    "value": "SetReturnBoolean"
+                  },
+                  "parameters": [
+                    "True"
+                  ]
+                }
+              ]
+            },
+            {
+              "type": "BuiltinCommonInstructions::Standard",
+              "conditions": [
+                {
+                  "type": {
+                    "value": "BuiltinCommonInstructions::CompareStrings"
+                  },
+                  "parameters": [
+                    "Direction",
+                    "=",
+                    "\"Down\""
+                  ]
+                },
+                {
+                  "type": {
+                    "value": "SpriteMultitouchJoystick::AngleTo4Way"
+                  },
+                  "parameters": [
+                    "",
+                    "=",
+                    "1",
+                    "Angle",
+                    ""
+                  ]
+                }
+              ],
+              "actions": [
+                {
+                  "type": {
+                    "value": "SetReturnBoolean"
+                  },
+                  "parameters": [
+                    "True"
+                  ]
+                }
+              ]
+            },
+            {
+              "type": "BuiltinCommonInstructions::Standard",
+              "conditions": [
+                {
+                  "type": {
+                    "value": "BuiltinCommonInstructions::CompareStrings"
+                  },
+                  "parameters": [
+                    "Direction",
+                    "=",
+                    "\"Left\""
+                  ]
+                },
+                {
+                  "type": {
+                    "value": "SpriteMultitouchJoystick::AngleTo4Way"
+                  },
+                  "parameters": [
+                    "",
+                    "=",
+                    "2",
+                    "Angle",
+                    ""
+                  ]
+                }
+              ],
+              "actions": [
+                {
+                  "type": {
+                    "value": "SetReturnBoolean"
+                  },
+                  "parameters": [
+                    "True"
+                  ]
+                }
+              ]
+            },
+            {
+              "type": "BuiltinCommonInstructions::Standard",
+              "conditions": [
+                {
+                  "type": {
+                    "value": "BuiltinCommonInstructions::CompareStrings"
+                  },
+                  "parameters": [
+                    "Direction",
+                    "=",
+                    "\"Up\""
+                  ]
+                },
+                {
+                  "type": {
+                    "value": "SpriteMultitouchJoystick::AngleTo4Way"
+                  },
+                  "parameters": [
+                    "",
+                    "=",
+                    "3",
+                    "Angle",
+                    ""
+                  ]
+                }
+              ],
+              "actions": [
+                {
+                  "type": {
+                    "value": "SetReturnBoolean"
+                  },
+                  "parameters": [
+                    "True"
+                  ]
+                }
+              ]
+            }
+          ],
+          "parameters": [
+            {
+              "description": "Angle",
+              "name": "Angle",
+              "type": "expression"
+            },
+            {
+              "description": "Direction",
+              "name": "Direction",
+              "supplementaryInformation": "[\"Up\",\"Down\",\"Left\",\"Right\"]",
+              "type": "stringWithSelector"
+            }
+          ],
+          "objectGroups": []
+        },
+        {
+          "description": "Check if angle is in a given direction.",
+          "fullName": "Angle 8-way direction",
+          "functionType": "Condition",
+          "name": "IsAngleIn8WayDirection",
+          "private": true,
+          "sentence": "The angle _PARAM1_ is the 8-way direction _PARAM2_",
+          "events": [
+            {
+              "type": "BuiltinCommonInstructions::Standard",
+              "conditions": [
+                {
+                  "type": {
+                    "value": "BuiltinCommonInstructions::CompareStrings"
+                  },
+                  "parameters": [
+                    "Direction",
+                    "=",
+                    "\"Right\""
+                  ]
+                },
+                {
+                  "type": {
+                    "value": "SpriteMultitouchJoystick::AngleTo8Way"
+                  },
+                  "parameters": [
+                    "",
+                    "=",
+                    "0",
+                    "Angle",
+                    ""
+                  ]
+                }
+              ],
+              "actions": [
+                {
+                  "type": {
+                    "value": "SetReturnBoolean"
+                  },
+                  "parameters": [
+                    "True"
+                  ]
+                }
+              ]
+            },
+            {
+              "type": "BuiltinCommonInstructions::Standard",
+              "conditions": [
+                {
+                  "type": {
+                    "value": "BuiltinCommonInstructions::CompareStrings"
+                  },
+                  "parameters": [
+                    "Direction",
+                    "=",
+                    "\"DownRight\""
+                  ]
+                },
+                {
+                  "type": {
+                    "value": "SpriteMultitouchJoystick::AngleTo8Way"
+                  },
+                  "parameters": [
+                    "",
+                    "=",
+                    "1",
+                    "Angle",
+                    ""
+                  ]
+                }
+              ],
+              "actions": [
+                {
+                  "type": {
+                    "value": "SetReturnBoolean"
+                  },
+                  "parameters": [
+                    "True"
+                  ]
+                }
+              ]
+            },
+            {
+              "type": "BuiltinCommonInstructions::Standard",
+              "conditions": [
+                {
+                  "type": {
+                    "value": "BuiltinCommonInstructions::CompareStrings"
+                  },
+                  "parameters": [
+                    "Direction",
+                    "=",
+                    "\"Down\""
+                  ]
+                },
+                {
+                  "type": {
+                    "value": "SpriteMultitouchJoystick::AngleTo8Way"
+                  },
+                  "parameters": [
+                    "",
+                    "=",
+                    "2",
+                    "Angle",
+                    ""
+                  ]
+                }
+              ],
+              "actions": [
+                {
+                  "type": {
+                    "value": "SetReturnBoolean"
+                  },
+                  "parameters": [
+                    "True"
+                  ]
+                }
+              ]
+            },
+            {
+              "type": "BuiltinCommonInstructions::Standard",
+              "conditions": [
+                {
+                  "type": {
+                    "value": "BuiltinCommonInstructions::CompareStrings"
+                  },
+                  "parameters": [
+                    "Direction",
+                    "=",
+                    "\"DownLeft\""
+                  ]
+                },
+                {
+                  "type": {
+                    "value": "SpriteMultitouchJoystick::AngleTo8Way"
+                  },
+                  "parameters": [
+                    "",
+                    "=",
+                    "3",
+                    "Angle",
+                    ""
+                  ]
+                }
+              ],
+              "actions": [
+                {
+                  "type": {
+                    "value": "SetReturnBoolean"
+                  },
+                  "parameters": [
+                    "True"
+                  ]
+                }
+              ]
+            },
+            {
+              "type": "BuiltinCommonInstructions::Standard",
+              "conditions": [
+                {
+                  "type": {
+                    "value": "BuiltinCommonInstructions::CompareStrings"
+                  },
+                  "parameters": [
+                    "Direction",
+                    "=",
+                    "\"Left\""
+                  ]
+                },
+                {
+                  "type": {
+                    "value": "SpriteMultitouchJoystick::AngleTo8Way"
+                  },
+                  "parameters": [
+                    "",
+                    "=",
+                    "4",
+                    "Angle",
+                    ""
+                  ]
+                }
+              ],
+              "actions": [
+                {
+                  "type": {
+                    "value": "SetReturnBoolean"
+                  },
+                  "parameters": [
+                    "True"
+                  ]
+                }
+              ]
+            },
+            {
+              "type": "BuiltinCommonInstructions::Standard",
+              "conditions": [
+                {
+                  "type": {
+                    "value": "BuiltinCommonInstructions::CompareStrings"
+                  },
+                  "parameters": [
+                    "Direction",
+                    "=",
+                    "\"UpLeft\""
+                  ]
+                },
+                {
+                  "type": {
+                    "value": "SpriteMultitouchJoystick::AngleTo8Way"
+                  },
+                  "parameters": [
+                    "",
+                    "=",
+                    "5",
+                    "Angle",
+                    ""
+                  ]
+                }
+              ],
+              "actions": [
+                {
+                  "type": {
+                    "value": "SetReturnBoolean"
+                  },
+                  "parameters": [
+                    "True"
+                  ]
+                }
+              ]
+            },
+            {
+              "type": "BuiltinCommonInstructions::Standard",
+              "conditions": [
+                {
+                  "type": {
+                    "value": "BuiltinCommonInstructions::CompareStrings"
+                  },
+                  "parameters": [
+                    "Direction",
+                    "=",
+                    "\"Up\""
+                  ]
+                },
+                {
+                  "type": {
+                    "value": "SpriteMultitouchJoystick::AngleTo8Way"
+                  },
+                  "parameters": [
+                    "",
+                    "=",
+                    "6",
+                    "Angle",
+                    ""
+                  ]
+                }
+              ],
+              "actions": [
+                {
+                  "type": {
+                    "value": "SetReturnBoolean"
+                  },
+                  "parameters": [
+                    "True"
+                  ]
+                }
+              ]
+            },
+            {
+              "type": "BuiltinCommonInstructions::Standard",
+              "conditions": [
+                {
+                  "type": {
+                    "value": "BuiltinCommonInstructions::CompareStrings"
+                  },
+                  "parameters": [
+                    "Direction",
+                    "=",
+                    "\"UpRight\""
+                  ]
+                },
+                {
+                  "type": {
+                    "value": "SpriteMultitouchJoystick::AngleTo8Way"
+                  },
+                  "parameters": [
+                    "",
+                    "=",
+                    "7",
+                    "Angle",
+                    ""
+                  ]
+                }
+              ],
+              "actions": [
+                {
+                  "type": {
+                    "value": "SetReturnBoolean"
+                  },
+                  "parameters": [
+                    "True"
+                  ]
+                }
+              ]
+            }
+          ],
+          "parameters": [
+            {
+              "description": "Angle",
+              "name": "Angle",
+              "type": "expression"
+            },
+            {
+              "description": "Direction",
+              "name": "Direction",
+              "supplementaryInformation": "[\"Up\",\"Down\",\"Left\",\"Right\",\"UpLeft\",\"UpRight\",\"DownLeft\",\"DownRight\"]",
+              "type": "stringWithSelector"
+            }
+          ],
+          "objectGroups": []
+        },
+        {
+          "description": "Check if joystick is pushed in a given direction.",
+          "fullName": "Joystick pushed in a direction (4-way)",
+          "functionType": "Condition",
+          "name": "IsDirectionPushed4Way",
+          "sentence": "Joystick _PARAM2_ of multitouch controller _PARAM1_ is pushed in direction _PARAM3_",
+          "events": [
+            {
+              "type": "BuiltinCommonInstructions::Comment",
+              "color": {
+                "b": 109,
+                "g": 230,
+                "r": 255,
+                "textB": 0,
+                "textG": 0,
+                "textR": 0
+              },
+              "comment": "Make sure the joystick has moved from center"
+            },
+            {
+              "type": "BuiltinCommonInstructions::Standard",
+              "conditions": [
+                {
+                  "type": {
+                    "value": "SpriteMultitouchJoystick::JoystickForce"
+                  },
+                  "parameters": [
+                    "",
+                    ">",
+                    "0",
+                    "ControllerIdentifier",
+                    "JoystickIdentifier",
+                    ""
+                  ]
+                },
+                {
+                  "type": {
+                    "value": "SpriteMultitouchJoystick::IsAngleIn4WayDirection"
+                  },
+                  "parameters": [
+                    "",
+                    "SpriteMultitouchJoystick::JoystickAngle(ControllerIdentifier, JoystickIdentifier)",
+                    "Direction",
+                    ""
+                  ]
+                }
+              ],
+              "actions": [
+                {
+                  "type": {
+                    "value": "SetReturnBoolean"
+                  },
+                  "parameters": [
+                    "True"
+                  ]
+                }
+              ]
+            }
+          ],
+          "parameters": [
+            {
+              "description": "Multitouch controller identifier (1, 2, 3, 4...)",
+              "name": "ControllerIdentifier",
+              "type": "expression"
+            },
+            {
+              "description": "Joystick name",
+              "name": "JoystickIdentifier",
+              "supplementaryInformation": "[\"Primary\",\"Secondary\"]",
+              "type": "stringWithSelector"
+            },
+            {
+              "description": "Direction",
+              "name": "Direction",
+              "supplementaryInformation": "[\"Up\",\"Down\",\"Left\",\"Right\"]",
+              "type": "stringWithSelector"
+            }
+          ],
+          "objectGroups": []
+        },
+        {
+          "description": "Check if joystick is pushed in a given direction.",
+          "fullName": "Joystick pushed in a direction (8-way)",
+          "functionType": "Condition",
+          "name": "IsDirectionPushed8Way",
+          "sentence": "Joystick _PARAM2_ of multitouch controller _PARAM1_ is pushed in direction _PARAM3_",
+          "events": [
+            {
+              "type": "BuiltinCommonInstructions::Comment",
+              "color": {
+                "b": 109,
+                "g": 230,
+                "r": 255,
+                "textB": 0,
+                "textG": 0,
+                "textR": 0
+              },
+              "comment": "Make sure the joystick has moved from center"
+            },
+            {
+              "type": "BuiltinCommonInstructions::Standard",
+              "conditions": [
+                {
+                  "type": {
+                    "value": "SpriteMultitouchJoystick::JoystickForce"
+                  },
+                  "parameters": [
+                    "",
+                    ">",
+                    "0",
+                    "ControllerIdentifier",
+                    "JoystickIdentifier",
+                    ""
+                  ]
+                },
+                {
+                  "type": {
+                    "value": "SpriteMultitouchJoystick::IsAngleIn8WayDirection"
+                  },
+                  "parameters": [
+                    "",
+                    "SpriteMultitouchJoystick::JoystickAngle(ControllerIdentifier, JoystickIdentifier)",
+                    "Direction",
+                    ""
+                  ]
+                }
+              ],
+              "actions": [
+                {
+                  "type": {
+                    "value": "SetReturnBoolean"
+                  },
+                  "parameters": [
+                    "True"
+                  ]
+                }
+              ]
+            }
+          ],
+          "parameters": [
+            {
+              "description": "Multitouch controller identifier (1, 2, 3, 4...)",
+              "name": "ControllerIdentifier",
+              "type": "expression"
+            },
+            {
+              "description": "Joystick name",
+              "name": "JoystickIdentifier",
+              "supplementaryInformation": "[\"Primary\",\"Secondary\"]",
+              "type": "stringWithSelector"
+            },
+            {
+              "description": "Direction",
+              "name": "Direction",
+              "supplementaryInformation": "[\"Up\",\"Down\",\"Left\",\"Right\",\"UpLeft\",\"UpRight\",\"DownLeft\",\"DownRight\"]",
+              "type": "stringWithSelector"
+            }
+          ],
+          "objectGroups": []
+        },
+        {
+          "description": "the percentage the thumb has been pulled away from the joystick center (Range: 0 to 1).",
+          "fullName": "Joystick force (deprecated)",
+          "functionType": "ExpressionAndCondition",
+          "name": "JoystickForce",
+          "private": true,
+          "sentence": "Joystick _PARAM2_ of multitouch controller _PARAM1_ force",
+          "events": [
+            {
+              "type": "BuiltinCommonInstructions::Standard",
+              "conditions": [],
+              "actions": [
+                {
+                  "type": {
+                    "value": "SetReturnNumber"
+                  },
+                  "parameters": [
+                    "SpriteMultitouchJoystick::StickForce(ControllerIdentifier, JoystickIdentifier)"
+                  ]
+                }
+              ]
+            }
+          ],
+          "expressionType": {
+            "type": "expression"
+          },
+          "parameters": [
+            {
+              "description": "Multitouch controller identifier (1, 2, 3, 4...)",
+              "name": "ControllerIdentifier",
+              "type": "expression"
+            },
+            {
+              "description": "Joystick name",
+              "name": "JoystickIdentifier",
+              "supplementaryInformation": "[\"Primary\",\"Secondary\"]",
+              "type": "stringWithSelector"
+            }
+          ],
+          "objectGroups": []
+        },
+        {
+          "description": "the force of multitouch contoller stick (from 0 to 1).",
+          "fullName": "Stick force",
+          "functionType": "ExpressionAndCondition",
+          "name": "StickForce",
+          "sentence": "multitouch controller _PARAM1_ _PARAM2_ stick force",
+          "events": [
+            {
+              "type": "BuiltinCommonInstructions::Standard",
+              "conditions": [],
+              "actions": [
+                {
+                  "type": {
+                    "value": "SetReturnNumber"
+                  },
+                  "parameters": [
+                    "max(0, Controllers[ControllerIdentifier].Joystick[JoystickIdentifier].Force - SpriteMultitouchJoystick::DeadZone(ControllerIdentifier, JoystickIdentifier)) / (1 - SpriteMultitouchJoystick::DeadZone(ControllerIdentifier, JoystickIdentifier))"
+                  ]
+                }
+              ]
+            }
+          ],
+          "expressionType": {
+            "type": "expression"
+          },
+          "parameters": [
+            {
+              "description": "Multitouch controller identifier (1, 2, 3, 4...)",
+              "name": "ControllerIdentifier",
+              "type": "expression"
+            },
+            {
+              "description": "Stick name",
+              "name": "JoystickIdentifier",
+              "supplementaryInformation": "[\"Primary\",\"Secondary\"]",
+              "type": "stringWithSelector"
+            }
+          ],
+          "objectGroups": []
+        },
+        {
+          "description": "Change the percentage the thumb has been pulled away from the joystick center (Range: 0 to 1).",
+          "fullName": "Joystick force",
+          "functionType": "Action",
+          "name": "SetJoystickForce",
+          "private": true,
+          "sentence": "Change the force of the joystick _PARAM2_ of multitouch controller _PARAM1_ to _PARAM3_",
+          "events": [
+            {
+              "type": "BuiltinCommonInstructions::Standard",
+              "conditions": [],
+              "actions": [
+                {
+                  "type": {
+                    "value": "SetNumberVariable"
+                  },
+                  "parameters": [
+                    "Controllers[ControllerIdentifier].Joystick[JoystickIdentifier].Force",
+                    "=",
+                    "Value"
+                  ]
+                }
+              ]
+            }
+          ],
+          "parameters": [
+            {
+              "description": "Multitouch controller identifier (1, 2, 3, 4...)",
+              "name": "ControllerIdentifier",
+              "type": "expression"
+            },
+            {
+              "description": "Joystick name",
+              "name": "JoystickIdentifier",
+              "type": "string"
+            },
+            {
+              "description": "Value",
+              "name": "Value",
+              "type": "expression"
+            }
+          ],
+          "objectGroups": []
+        },
+        {
+          "description": "Return the angle the joystick is pointing towards (Range: -180 to 180).",
+          "fullName": "Joystick angle (deprecated)",
+          "functionType": "Expression",
+          "name": "JoystickAngle",
+          "private": true,
+          "sentence": "",
+          "events": [
+            {
+              "type": "BuiltinCommonInstructions::Standard",
+              "conditions": [],
+              "actions": [
+                {
+                  "type": {
+                    "value": "SetReturnNumber"
+                  },
+                  "parameters": [
+                    "SpriteMultitouchJoystick::StickAngle(ControllerIdentifier, JoystickIdentifier)"
+                  ]
+                }
+              ]
+            }
+          ],
+          "expressionType": {
+            "type": "expression"
+          },
+          "parameters": [
+            {
+              "description": "Multitouch controller identifier (1, 2, 3, 4...)",
+              "name": "ControllerIdentifier",
+              "type": "expression"
+            },
+            {
+              "description": "Joystick name",
+              "name": "JoystickIdentifier",
+              "supplementaryInformation": "[\"Primary\",\"Secondary\"]",
+              "type": "stringWithSelector"
+            }
+          ],
+          "objectGroups": []
+        },
+        {
+          "description": "Return the angle the multitouch controller stick is pointing towards (Range: -180 to 180).",
+          "fullName": "Stick angle",
+          "functionType": "Expression",
+          "name": "StickAngle",
+          "sentence": "",
+          "events": [
+            {
+              "type": "BuiltinCommonInstructions::Standard",
+              "conditions": [],
+              "actions": [
+                {
+                  "type": {
+                    "value": "SetReturnNumber"
+                  },
+                  "parameters": [
+                    "Controllers[ControllerIdentifier].Joystick[JoystickIdentifier].Angle"
+                  ]
+                }
+              ]
+            }
+          ],
+          "expressionType": {
+            "type": "expression"
+          },
+          "parameters": [
+            {
+              "description": "Multitouch controller identifier (1, 2, 3, 4...)",
+              "name": "ControllerIdentifier",
+              "type": "expression"
+            },
+            {
+              "description": "Joystick name",
+              "name": "JoystickIdentifier",
+              "supplementaryInformation": "[\"Primary\",\"Secondary\"]",
+              "type": "stringWithSelector"
+            }
+          ],
+          "objectGroups": []
+        },
+        {
+          "description": "Change the angle the joystick is pointing towards (Range: -180 to 180).",
+          "fullName": "Joystick angle",
+          "functionType": "Action",
+          "name": "SetJoystickAngle",
+          "private": true,
+          "sentence": "Change the angle of the joystick _PARAM2_ of multitouch controller _PARAM1_ to _PARAM3_",
+          "events": [
+            {
+              "type": "BuiltinCommonInstructions::Standard",
+              "conditions": [],
+              "actions": [
+                {
+                  "type": {
+                    "value": "SetNumberVariable"
+                  },
+                  "parameters": [
+                    "Controllers[ControllerIdentifier].Joystick[JoystickIdentifier].Angle",
+                    "=",
+                    "Value"
+                  ]
+                }
+              ]
+            }
+          ],
+          "parameters": [
+            {
+              "description": "Multitouch controller identifier (1, 2, 3, 4...)",
+              "name": "ControllerIdentifier",
+              "type": "expression"
+            },
+            {
+              "description": "Joystick name",
+              "name": "JoystickIdentifier",
+              "type": "string"
+            },
+            {
+              "description": "Value",
+              "name": "Value",
+              "type": "expression"
+            }
+          ],
+          "objectGroups": []
+        },
+        {
+          "description": "Return the multitouch contoller stick force on X axis (from -1 at the left to 1 at the right).",
+          "fullName": "Stick X force",
+          "functionType": "Expression",
+          "name": "StickForceX",
+          "sentence": "",
+          "events": [
+            {
+              "type": "BuiltinCommonInstructions::Standard",
+              "conditions": [],
+              "actions": [
+                {
+                  "type": {
+                    "value": "SetReturnNumber"
+                  },
+                  "parameters": [
+                    "SpriteMultitouchJoystick::JoystickForce(ControllerIdentifier, JoystickIdentifier) * cos(ToRad(SpriteMultitouchJoystick::JoystickAngle(ControllerIdentifier, JoystickIdentifier)))"
+                  ]
+                }
+              ]
+            }
+          ],
+          "expressionType": {
+            "type": "expression"
+          },
+          "parameters": [
+            {
+              "description": "Multitouch controller identifier (1, 2, 3, 4...)",
+              "name": "ControllerIdentifier",
+              "type": "expression"
+            },
+            {
+              "description": "Joystick name",
+              "name": "JoystickIdentifier",
+              "supplementaryInformation": "[\"Primary\",\"Secondary\"]",
+              "type": "stringWithSelector"
+            }
+          ],
+          "objectGroups": []
+        },
+        {
+          "description": "Return the multitouch contoller stick force on Y axis (from -1 at the top to 1 at the bottom).",
+          "fullName": "Stick Y force",
+          "functionType": "Expression",
+          "name": "StickForceY",
+          "sentence": "",
+          "events": [
+            {
+              "type": "BuiltinCommonInstructions::Standard",
+              "conditions": [],
+              "actions": [
+                {
+                  "type": {
+                    "value": "SetReturnNumber"
+                  },
+                  "parameters": [
+                    "SpriteMultitouchJoystick::JoystickForce(ControllerIdentifier, JoystickIdentifier) * sin(ToRad(SpriteMultitouchJoystick::JoystickAngle(ControllerIdentifier, JoystickIdentifier)))"
+                  ]
+                }
+              ]
+            }
+          ],
+          "expressionType": {
+            "type": "expression"
+          },
+          "parameters": [
+            {
+              "description": "Multitouch controller identifier (1, 2, 3, 4...)",
+              "name": "ControllerIdentifier",
+              "type": "expression"
+            },
+            {
+              "description": "Joystick name",
+              "name": "JoystickIdentifier",
+              "supplementaryInformation": "[\"Primary\",\"Secondary\"]",
+              "type": "stringWithSelector"
+            }
+          ],
+          "objectGroups": []
+        }
+      ],
+      "eventsBasedBehaviors": [
+        {
+          "description": "Joystick that can be controlled by interacting with a touchscreen.",
+          "fullName": "Multitouch Joystick",
+          "name": "MultitouchJoystick",
+          "objectType": "",
+          "private": true,
+          "eventsFunctions": [
+            {
+              "fullName": "",
+              "functionType": "Action",
+              "name": "onCreated",
+              "sentence": "",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SpriteMultitouchJoystick::SetDeadZone"
+                      },
+                      "parameters": [
+                        "",
+                        "ControllerIdentifier",
+                        "JoystickIdentifier",
+                        "DeadZoneRadius",
+                        ""
+                      ]
+                    },
+                    {
+                      "type": {
+                        "value": "SpriteMultitouchJoystick::MultitouchJoystick::Reset"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        ""
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "SpriteMultitouchJoystick::MultitouchJoystick",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "fullName": "",
+              "functionType": "Action",
+              "name": "onDeActivate",
+              "sentence": "",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SpriteMultitouchJoystick::MultitouchJoystick::Reset"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        ""
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "SpriteMultitouchJoystick::MultitouchJoystick",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "fullName": "",
+              "functionType": "Action",
+              "name": "doStepPreEvents",
+              "sentence": "",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [
+                    {
+                      "type": {
+                        "value": "HasGameJustResumed"
+                      },
+                      "parameters": [
+                        ""
+                      ]
+                    }
+                  ],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SpriteMultitouchJoystick::MultitouchJoystick::Reset"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        ""
+                      ]
+                    }
+                  ]
+                },
+                {
+                  "colorB": 228,
+                  "colorG": 176,
+                  "colorR": 74,
+                  "creationTime": 0,
+                  "name": "Manage touches",
+                  "source": "",
+                  "type": "BuiltinCommonInstructions::Group",
+                  "events": [
+                    {
+                      "type": "BuiltinCommonInstructions::Standard",
+                      "conditions": [
+                        {
+                          "type": {
+                            "value": "HasAnyTouchOrMouseStarted"
+                          },
+                          "parameters": [
+                            ""
+                          ]
+                        }
+                      ],
+                      "actions": [
+                        {
+                          "type": {
+                            "value": "SpriteMultitouchJoystick::MultitouchJoystick::SetPropertyTouchIndex"
+                          },
+                          "parameters": [
+                            "Object",
+                            "Behavior",
+                            "=",
+                            "0"
+                          ]
+                        }
+                      ],
+                      "events": [
+                        {
+                          "type": "BuiltinCommonInstructions::Repeat",
+                          "repeatExpression": "StartedTouchOrMouseCount()",
+                          "conditions": [],
+                          "actions": [],
+                          "events": [
+                            {
+                              "type": "BuiltinCommonInstructions::Standard",
+                              "conditions": [
+                                {
+                                  "type": {
+                                    "value": "SpriteMultitouchJoystick::MultitouchJoystick::PropertyTouchId"
+                                  },
+                                  "parameters": [
+                                    "Object",
+                                    "Behavior",
+                                    "=",
+                                    "0"
+                                  ]
+                                },
+                                {
+                                  "type": {
+                                    "value": "CollisionPoint"
+                                  },
+                                  "parameters": [
+                                    "Object",
+                                    "TouchX(StartedTouchOrMouseId(TouchIndex), Object.Layer(), 0)",
+                                    "TouchY(StartedTouchOrMouseId(TouchIndex), Object.Layer(), 0)"
+                                  ]
+                                }
+                              ],
+                              "actions": [
+                                {
+                                  "type": {
+                                    "value": "SpriteMultitouchJoystick::MultitouchJoystick::SetPropertyTouchId"
+                                  },
+                                  "parameters": [
+                                    "Object",
+                                    "Behavior",
+                                    "=",
+                                    "StartedTouchOrMouseId(TouchIndex)"
+                                  ]
+                                }
+                              ]
+                            },
+                            {
+                              "type": "BuiltinCommonInstructions::Standard",
+                              "conditions": [],
+                              "actions": [
+                                {
+                                  "type": {
+                                    "value": "SpriteMultitouchJoystick::MultitouchJoystick::SetPropertyTouchIndex"
+                                  },
+                                  "parameters": [
+                                    "Object",
+                                    "Behavior",
+                                    "+",
+                                    "1"
+                                  ]
+                                }
+                              ]
+                            }
+                          ]
+                        }
+                      ]
+                    },
+                    {
+                      "type": "BuiltinCommonInstructions::Comment",
+                      "color": {
+                        "b": 109,
+                        "g": 230,
+                        "r": 255,
+                        "textB": 0,
+                        "textG": 0,
+                        "textR": 0
+                      },
+                      "comment": "Move thumb back to center when not being pressed (acts like a spring on a real controller)"
+                    },
+                    {
+                      "type": "BuiltinCommonInstructions::Standard",
+                      "conditions": [
+                        {
+                          "type": {
+                            "value": "HasTouchEnded"
+                          },
+                          "parameters": [
+                            "",
+                            "TouchId"
+                          ]
+                        }
+                      ],
+                      "actions": [
+                        {
+                          "type": {
+                            "value": "SpriteMultitouchJoystick::MultitouchJoystick::Reset"
+                          },
+                          "parameters": [
+                            "Object",
+                            "Behavior",
+                            ""
+                          ]
+                        }
+                      ]
+                    }
+                  ],
+                  "parameters": []
+                },
+                {
+                  "colorB": 228,
+                  "colorG": 176,
+                  "colorR": 74,
+                  "creationTime": 0,
+                  "name": "Update joystick position",
+                  "source": "",
+                  "type": "BuiltinCommonInstructions::Group",
+                  "events": [
+                    {
+                      "type": "BuiltinCommonInstructions::Standard",
+                      "conditions": [
+                        {
+                          "type": {
+                            "value": "SpriteMultitouchJoystick::MultitouchJoystick::PropertyTouchId"
+                          },
+                          "parameters": [
+                            "Object",
+                            "Behavior",
+                            "!=",
+                            "0"
+                          ]
+                        }
+                      ],
+                      "actions": [
+                        {
+                          "type": {
+                            "value": "SpriteMultitouchJoystick::MultitouchJoystick::SetJoystickAngle"
+                          },
+                          "parameters": [
+                            "Object",
+                            "Behavior",
+                            "AngleBetweenPositions(Object.CenterX(), Object.CenterY(), TouchX(TouchId, Object.Layer(), 0), TouchY(TouchId, Object.Layer(), 0))",
+                            "AngleBetweenPositions(Object.CenterX(), Object.CenterY(), TouchX(TouchId, Object.Layer(), 0), TouchY(TouchId, Object.Layer(), 0))"
+                          ]
+                        },
+                        {
+                          "type": {
+                            "value": "SpriteMultitouchJoystick::MultitouchJoystick::SetJoystickForce"
+                          },
+                          "parameters": [
+                            "Object",
+                            "Behavior",
+                            "=",
+                            "clamp(2 * DistanceBetweenPositions(Object.CenterX(), Object.CenterY(), TouchX(TouchId, Object.Layer(), 0), TouchY(TouchId, Object.Layer(), 0)) / Object.Width(), 0, 1)",
+                            ""
+                          ]
+                        }
+                      ]
+                    }
+                  ],
+                  "parameters": []
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "SpriteMultitouchJoystick::MultitouchJoystick",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "the joystick force (from 0 to 1).",
+              "fullName": "Joystick force",
+              "functionType": "ExpressionAndCondition",
+              "name": "JoystickForce",
+              "sentence": "the joystick force",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SetReturnNumber"
+                      },
+                      "parameters": [
+                        "max(0, JoystickForce - DeadZoneRadius) / (1 - DeadZoneRadius)"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "expressionType": {
+                "type": "expression"
+              },
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "SpriteMultitouchJoystick::MultitouchJoystick",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "fullName": "",
+              "functionType": "ActionWithOperator",
+              "getterName": "JoystickForce",
+              "name": "SetJoystickForce",
+              "private": true,
+              "sentence": "",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SpriteMultitouchJoystick::MultitouchJoystick::SetPropertyJoystickForce"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "=",
+                        "Value"
+                      ]
+                    },
+                    {
+                      "type": {
+                        "value": "SpriteMultitouchJoystick::SetJoystickForce"
+                      },
+                      "parameters": [
+                        "",
+                        "ControllerIdentifier",
+                        "JoystickIdentifier",
+                        "JoystickForce",
+                        ""
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "SpriteMultitouchJoystick::MultitouchJoystick",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "Return the angle the joystick is pointing towards (Range: -180 to 180).",
+              "fullName": "Joystick angle",
+              "functionType": "Expression",
+              "name": "JoystickAngle",
+              "sentence": "",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SetReturnNumber"
+                      },
+                      "parameters": [
+                        "JoystickAngle"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "expressionType": {
+                "type": "expression"
+              },
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "SpriteMultitouchJoystick::MultitouchJoystick",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "Change the angle the joystick is pointing towards (Range: -180 to 180).",
+              "fullName": "Joystick angle",
+              "functionType": "Action",
+              "name": "SetJoystickAngle",
+              "private": true,
+              "sentence": "Change the joystick angle of _PARAM0_ to _PARAM2_",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SpriteMultitouchJoystick::MultitouchJoystick::SetPropertyJoystickAngle"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "=",
+                        "Value"
+                      ]
+                    },
+                    {
+                      "type": {
+                        "value": "SpriteMultitouchJoystick::SetJoystickAngle"
+                      },
+                      "parameters": [
+                        "",
+                        "ControllerIdentifier",
+                        "JoystickIdentifier",
+                        "JoystickAngle",
+                        ""
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "SpriteMultitouchJoystick::MultitouchJoystick",
+                  "type": "behavior"
+                },
+                {
+                  "description": "Angle",
+                  "name": "Value",
+                  "type": "expression"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "Return the stick force on X axis (from -1 at the left to 1 at the right).",
+              "fullName": "Stick X force",
+              "functionType": "Expression",
+              "name": "StickForceX",
+              "sentence": "",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SetReturnNumber"
+                      },
+                      "parameters": [
+                        "Object.Behavior::JoystickForce() * cos(ToRad(Object.Behavior::JoystickAngle()))"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "expressionType": {
+                "type": "expression"
+              },
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "SpriteMultitouchJoystick::MultitouchJoystick",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "Return the stick force on Y axis (from -1 at the top to 1 at the bottom).",
+              "fullName": "Stick Y force",
+              "functionType": "Expression",
+              "name": "StickForceY",
+              "sentence": "",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SetReturnNumber"
+                      },
+                      "parameters": [
+                        "Object.Behavior::JoystickForce() * sin(ToRad(Object.Behavior::JoystickAngle()))"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "expressionType": {
+                "type": "expression"
+              },
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "SpriteMultitouchJoystick::MultitouchJoystick",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "Check if joystick is pushed in a given direction.",
+              "fullName": "Joystick pushed in a direction (4-way movement)",
+              "functionType": "Condition",
+              "name": "IsDirectionPushed4Way",
+              "sentence": "_PARAM0_ is pushed in direction _PARAM2_",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [
+                    {
+                      "type": {
+                        "value": "SpriteMultitouchJoystick::MultitouchJoystick::JoystickForce"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        ">",
+                        "0",
+                        ""
+                      ]
+                    },
+                    {
+                      "type": {
+                        "value": "SpriteMultitouchJoystick::IsAngleIn4WayDirection"
+                      },
+                      "parameters": [
+                        "",
+                        "JoystickAngle",
+                        "Direction",
+                        ""
+                      ]
+                    }
+                  ],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SetReturnBoolean"
+                      },
+                      "parameters": [
+                        "True"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "SpriteMultitouchJoystick::MultitouchJoystick",
+                  "type": "behavior"
+                },
+                {
+                  "description": "Direction",
+                  "name": "Direction",
+                  "supplementaryInformation": "[\"Up\",\"Down\",\"Left\",\"Right\"]",
+                  "type": "stringWithSelector"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "Check if joystick is pushed in a given direction.",
+              "fullName": "Joystick pushed in a direction (8-way movement)",
+              "functionType": "Condition",
+              "name": "IsDirectionPushed8Way",
+              "sentence": "_PARAM0_ is pushed in direction _PARAM2_",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [
+                    {
+                      "type": {
+                        "value": "SpriteMultitouchJoystick::MultitouchJoystick::JoystickForce"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        ">",
+                        "0",
+                        ""
+                      ]
+                    },
+                    {
+                      "type": {
+                        "value": "SpriteMultitouchJoystick::IsAngleIn8WayDirection"
+                      },
+                      "parameters": [
+                        "",
+                        "JoystickAngle",
+                        "Direction",
+                        ""
+                      ]
+                    }
+                  ],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SetReturnBoolean"
+                      },
+                      "parameters": [
+                        "True"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "SpriteMultitouchJoystick::MultitouchJoystick",
+                  "type": "behavior"
+                },
+                {
+                  "description": "Direction",
+                  "name": "Direction",
+                  "supplementaryInformation": "[\"Up\",\"Down\",\"Left\",\"Right\",\"UpLeft\",\"UpRight\",\"DownLeft\",\"DownRight\"]",
+                  "type": "stringWithSelector"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "Check if a joystick is pressed.",
+              "fullName": "Joystick pressed",
+              "functionType": "Condition",
+              "name": "IsPressed",
+              "sentence": "Joystick _PARAM0_ is pressed",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [
+                    {
+                      "type": {
+                        "value": "SpriteMultitouchJoystick::MultitouchJoystick::PropertyTouchId"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "!=",
+                        "0"
+                      ]
+                    }
+                  ],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SetReturnBoolean"
+                      },
+                      "parameters": [
+                        "True"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "SpriteMultitouchJoystick::MultitouchJoystick",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "Reset the joystick values (except for angle, which stays the same)",
+              "fullName": "Reset",
+              "functionType": "Action",
+              "name": "Reset",
+              "private": true,
+              "sentence": "Reset the joystick of _PARAM0_",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SpriteMultitouchJoystick::MultitouchJoystick::SetJoystickForce"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "=",
+                        "0",
+                        ""
+                      ]
+                    },
+                    {
+                      "type": {
+                        "value": "SpriteMultitouchJoystick::MultitouchJoystick::SetPropertyTouchId"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "=",
+                        "0"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "SpriteMultitouchJoystick::MultitouchJoystick",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "the multitouch controller identifier.",
+              "fullName": "Multitouch controller identifier",
+              "functionType": "ExpressionAndCondition",
+              "group": "Multitouch Joystick configuration",
+              "name": "ControllerIdentifier",
+              "sentence": "the multitouch controller identifier",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SetReturnNumber"
+                      },
+                      "parameters": [
+                        "ControllerIdentifier"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "expressionType": {
+                "type": "expression"
+              },
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "SpriteMultitouchJoystick::MultitouchJoystick",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "fullName": "",
+              "functionType": "ActionWithOperator",
+              "getterName": "ControllerIdentifier",
+              "name": "SetControllerIdentifier",
+              "sentence": "",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SpriteMultitouchJoystick::MultitouchJoystick::SetPropertyControllerIdentifier"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "=",
+                        "Value"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "SpriteMultitouchJoystick::MultitouchJoystick",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "the joystick name.",
+              "fullName": "Joystick name",
+              "functionType": "ExpressionAndCondition",
+              "group": "Multitouch Joystick configuration",
+              "name": "JoystickIdentifier",
+              "sentence": "the joystick name",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SetReturnString"
+                      },
+                      "parameters": [
+                        "JoystickIdentifier"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "expressionType": {
+                "type": "string"
+              },
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "SpriteMultitouchJoystick::MultitouchJoystick",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "fullName": "",
+              "functionType": "ActionWithOperator",
+              "getterName": "JoystickIdentifier",
+              "name": "SetJoystickIdentifier",
+              "sentence": "",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SpriteMultitouchJoystick::MultitouchJoystick::SetPropertyJoystickIdentifier"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "=",
+                        "Value"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "SpriteMultitouchJoystick::MultitouchJoystick",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "the dead zone radius (range: 0 to 1) of the joystick. The deadzone is an area for which movement on sticks won't be taken into account (instead, the stick will be considered as not moved).",
+              "fullName": "Dead zone radius",
+              "functionType": "ExpressionAndCondition",
+              "group": "Multitouch Joystick configuration",
+              "name": "DeadZoneRadius",
+              "sentence": "the dead zone radius",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SetReturnNumber"
+                      },
+                      "parameters": [
+                        "DeadZoneRadius"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "expressionType": {
+                "type": "expression"
+              },
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "SpriteMultitouchJoystick::MultitouchJoystick",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "fullName": "",
+              "functionType": "ActionWithOperator",
+              "getterName": "DeadZoneRadius",
+              "name": "SetDeadZoneRadius",
+              "sentence": "",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SpriteMultitouchJoystick::MultitouchJoystick::SetPropertyDeadZoneRadius"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "=",
+                        "Value"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "SpriteMultitouchJoystick::MultitouchJoystick",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            }
+          ],
+          "propertyDescriptors": [
+            {
+              "value": "1",
+              "type": "Number",
+              "label": "Multitouch controller identifier (1, 2, 3, 4...)",
+              "description": "",
+              "group": "",
+              "extraInformation": [],
+              "name": "ControllerIdentifier"
+            },
+            {
+              "value": "Primary",
+              "type": "String",
+              "label": "Joystick name",
+              "description": "",
+              "group": "",
+              "extraInformation": [],
+              "name": "JoystickIdentifier"
+            },
+            {
+              "value": "0.4",
+              "type": "Number",
+              "label": "Dead zone radius (range: 0 to 1)",
+              "description": "The deadzone is an area for which movement on sticks won't be taken into account (instead, the stick will be considered as not moved)",
+              "group": "",
+              "extraInformation": [],
+              "name": "DeadZoneRadius"
+            },
+            {
+              "value": "0",
+              "type": "Number",
+              "label": "Joystick angle (range: -180 to 180)",
+              "description": "",
+              "group": "",
+              "extraInformation": [],
+              "hidden": true,
+              "name": "JoystickAngle"
+            },
+            {
+              "value": "0",
+              "type": "Number",
+              "label": "Joystick force (range: 0 to 1)",
+              "description": "",
+              "group": "",
+              "extraInformation": [],
+              "hidden": true,
+              "name": "JoystickForce"
+            },
+            {
+              "value": "0",
+              "type": "Number",
+              "label": "",
+              "description": "",
+              "group": "",
+              "extraInformation": [],
+              "hidden": true,
+              "name": "TouchId"
+            },
+            {
+              "value": "",
+              "type": "Number",
+              "label": "",
+              "description": "",
+              "group": "",
+              "extraInformation": [],
+              "hidden": true,
+              "name": "TouchIndex"
+            }
+          ],
+          "sharedPropertyDescriptors": []
+        },
+        {
+          "description": "Detect button presses made on a touchscreen.",
+          "fullName": "Multitouch button",
+          "name": "MultitouchButton",
+          "objectType": "",
+          "eventsFunctions": [
+            {
+              "fullName": "",
+              "functionType": "Action",
+              "name": "doStepPreEvents",
+              "sentence": "",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [
+                    {
+                      "type": {
+                        "value": "SpriteMultitouchJoystick::MultitouchButton::IsReleased"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        ""
+                      ]
+                    }
+                  ],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SpriteMultitouchJoystick::MultitouchButton::SetPropertyIsReleased"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "no"
+                      ]
+                    },
+                    {
+                      "type": {
+                        "value": "SpriteMultitouchJoystick::MultitouchButton::SetButtonState"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "\"Idle\"",
+                        ""
+                      ]
+                    }
+                  ]
+                },
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [
+                    {
+                      "type": {
+                        "inverted": true,
+                        "value": "SpriteMultitouchJoystick::MultitouchButton::IsPressed"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        ""
+                      ]
+                    },
+                    {
+                      "type": {
+                        "value": "HasAnyTouchOrMouseStarted"
+                      },
+                      "parameters": [
+                        ""
+                      ]
+                    }
+                  ],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SpriteMultitouchJoystick::MultitouchButton::SetPropertyTouchIndex"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "=",
+                        "0"
+                      ]
+                    }
+                  ],
+                  "events": [
+                    {
+                      "type": "BuiltinCommonInstructions::Repeat",
+                      "repeatExpression": "StartedTouchOrMouseCount()",
+                      "conditions": [],
+                      "actions": [],
+                      "events": [
+                        {
+                          "type": "BuiltinCommonInstructions::Standard",
+                          "conditions": [
+                            {
+                              "type": {
+                                "value": "CollisionPoint"
+                              },
+                              "parameters": [
+                                "Object",
+                                "TouchX(StartedTouchOrMouseId(TouchIndex), Object.Layer(), 0)",
+                                "TouchY(StartedTouchOrMouseId(TouchIndex), Object.Layer(), 0)"
+                              ]
+                            }
+                          ],
+                          "actions": [
+                            {
+                              "type": {
+                                "value": "SpriteMultitouchJoystick::MultitouchButton::SetPropertyTouchId"
+                              },
+                              "parameters": [
+                                "Object",
+                                "Behavior",
+                                "=",
+                                "StartedTouchOrMouseId(TouchIndex)"
+                              ]
+                            },
+                            {
+                              "type": {
+                                "value": "SpriteMultitouchJoystick::MultitouchButton::SetButtonState"
+                              },
+                              "parameters": [
+                                "Object",
+                                "Behavior",
+                                "\"Pressed\"",
+                                ""
+                              ]
+                            }
+                          ]
+                        },
+                        {
+                          "type": "BuiltinCommonInstructions::Standard",
+                          "conditions": [],
+                          "actions": [
+                            {
+                              "type": {
+                                "value": "SpriteMultitouchJoystick::MultitouchButton::SetPropertyTouchIndex"
+                              },
+                              "parameters": [
+                                "Object",
+                                "Behavior",
+                                "+",
+                                "1"
+                              ]
+                            }
+                          ]
+                        }
+                      ]
+                    }
+                  ]
+                },
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [
+                    {
+                      "type": {
+                        "value": "SpriteMultitouchJoystick::MultitouchButton::IsPressed"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        ""
+                      ]
+                    },
+                    {
+                      "type": {
+                        "value": "HasTouchEnded"
+                      },
+                      "parameters": [
+                        "",
+                        "TouchId"
+                      ]
+                    }
+                  ],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SpriteMultitouchJoystick::MultitouchButton::SetButtonState"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "\"Released\"",
+                        ""
+                      ]
+                    },
+                    {
+                      "type": {
+                        "value": "SpriteMultitouchJoystick::MultitouchButton::SetPropertyIsReleased"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "yes"
+                      ]
+                    },
+                    {
+                      "type": {
+                        "value": "SpriteMultitouchJoystick::MultitouchButton::SetPropertyTouchId"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "=",
+                        "0"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "SpriteMultitouchJoystick::MultitouchButton",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "Check if button is released.",
+              "fullName": "Button released",
+              "functionType": "Condition",
+              "name": "IsReleased",
+              "sentence": "Button _PARAM0_ is released",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [
+                    {
+                      "type": {
+                        "value": "SpriteMultitouchJoystick::MultitouchButton::PropertyIsReleased"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior"
+                      ]
+                    }
+                  ],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SetReturnBoolean"
+                      },
+                      "parameters": [
+                        "True"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "SpriteMultitouchJoystick::MultitouchButton",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "Check if button is pressed.",
+              "fullName": "Button pressed",
+              "functionType": "Condition",
+              "name": "IsPressed",
+              "sentence": "Button _PARAM0_ is pressed",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [
+                    {
+                      "type": {
+                        "value": "SpriteMultitouchJoystick::MultitouchButton::PropertyTouchId"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "!=",
+                        "0"
+                      ]
+                    }
+                  ],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SetReturnBoolean"
+                      },
+                      "parameters": [
+                        "True"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "SpriteMultitouchJoystick::MultitouchButton",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "fullName": "Button state",
+              "functionType": "Action",
+              "name": "SetButtonState",
+              "private": true,
+              "sentence": "Mark the button _PARAM0_ as _PARAM2_",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SpriteMultitouchJoystick::SetButtonState"
+                      },
+                      "parameters": [
+                        "",
+                        "ControllerIdentifier",
+                        "ButtonIdentifier",
+                        "ButtonState",
+                        ""
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "SpriteMultitouchJoystick::MultitouchButton",
+                  "type": "behavior"
+                },
+                {
+                  "description": "Button state",
+                  "name": "ButtonState",
+                  "supplementaryInformation": "[\"Idle\",\"Pressed\",\"Released\"]",
+                  "type": "stringWithSelector"
+                }
+              ],
+              "objectGroups": []
+            }
+          ],
+          "propertyDescriptors": [
+            {
+              "value": "1",
+              "type": "Number",
+              "label": "Multitouch controller identifier (1, 2, 3, 4...)",
+              "description": "",
+              "group": "",
+              "extraInformation": [],
+              "name": "ControllerIdentifier"
+            },
+            {
+              "value": "A",
+              "type": "String",
+              "label": "Button identifier",
+              "description": "",
+              "group": "",
+              "extraInformation": [],
+              "name": "ButtonIdentifier"
+            },
+            {
+              "value": "0",
+              "type": "Number",
+              "label": "TouchID",
+              "description": "",
+              "group": "",
+              "extraInformation": [],
+              "hidden": true,
+              "name": "TouchId"
+            },
+            {
+              "value": "",
+              "type": "Number",
+              "label": "",
+              "description": "",
+              "group": "",
+              "extraInformation": [],
+              "hidden": true,
+              "name": "TouchIndex"
+            },
+            {
+              "value": "",
+              "type": "Boolean",
+              "label": "Button released",
+              "description": "",
+              "group": "",
+              "extraInformation": [],
+              "hidden": true,
+              "name": "IsReleased"
+            }
+          ],
+          "sharedPropertyDescriptors": []
+        },
+        {
+          "description": "Control a platformer character with a multitouch controller.",
+          "fullName": "Platformer multitouch controller mapper",
+          "name": "PlatformerMultitouchMapper",
+          "objectType": "",
+          "eventsFunctions": [
+            {
+              "fullName": "",
+              "functionType": "Action",
+              "name": "doStepPreEvents",
+              "sentence": "",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [
+                    {
+                      "type": {
+                        "value": "SpriteMultitouchJoystick::IsDirectionPushed4Way"
+                      },
+                      "parameters": [
+                        "",
+                        "ControllerIdentifier",
+                        "JoystickIdentifier",
+                        "\"Left\"",
+                        ""
+                      ]
+                    }
+                  ],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "PlatformBehavior::SimulateLeftKey"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Property"
+                      ]
+                    }
+                  ]
+                },
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [
+                    {
+                      "type": {
+                        "value": "SpriteMultitouchJoystick::IsDirectionPushed4Way"
+                      },
+                      "parameters": [
+                        "",
+                        "ControllerIdentifier",
+                        "JoystickIdentifier",
+                        "\"Right\"",
+                        ""
+                      ]
+                    }
+                  ],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "PlatformBehavior::SimulateRightKey"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Property"
+                      ]
+                    }
+                  ]
+                },
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [
+                    {
+                      "type": {
+                        "value": "SpriteMultitouchJoystick::IsDirectionPushed4Way"
+                      },
+                      "parameters": [
+                        "",
+                        "ControllerIdentifier",
+                        "JoystickIdentifier",
+                        "\"Up\"",
+                        ""
+                      ]
+                    }
+                  ],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "PlatformBehavior::SimulateUpKey"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Property"
+                      ]
+                    },
+                    {
+                      "type": {
+                        "value": "PlatformBehavior::SimulateLadderKey"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Property"
+                      ]
+                    }
+                  ]
+                },
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [
+                    {
+                      "type": {
+                        "value": "SpriteMultitouchJoystick::IsDirectionPushed4Way"
+                      },
+                      "parameters": [
+                        "",
+                        "ControllerIdentifier",
+                        "JoystickIdentifier",
+                        "\"Down\"",
+                        ""
+                      ]
+                    }
+                  ],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "PlatformBehavior::SimulateDownKey"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Property"
+                      ]
+                    }
+                  ]
+                },
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [
+                    {
+                      "type": {
+                        "value": "SpriteMultitouchJoystick::IsButtonPressed"
+                      },
+                      "parameters": [
+                        "",
+                        "ControllerIdentifier",
+                        "JumpButton",
+                        "\"Down\""
+                      ]
+                    }
+                  ],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "PlatformBehavior::SimulateJumpKey"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Property"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "SpriteMultitouchJoystick::PlatformerMultitouchMapper",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            }
+          ],
+          "propertyDescriptors": [
+            {
+              "value": "",
+              "type": "Behavior",
+              "label": "Platform character behavior",
+              "description": "",
+              "group": "",
+              "extraInformation": [
+                "PlatformBehavior::PlatformerObjectBehavior"
+              ],
+              "name": "Property"
+            },
+            {
+              "value": "1",
+              "type": "Number",
+              "label": "Controller identifier (1, 2, 3, 4...)",
+              "description": "",
+              "group": "",
+              "extraInformation": [],
+              "name": "ControllerIdentifier"
+            },
+            {
+              "value": "Primary",
+              "type": "Choice",
+              "label": "Joystick name",
+              "description": "",
+              "group": "Controls",
+              "extraInformation": [
+                "Primary",
+                "Secondary"
+              ],
+              "name": "JoystickIdentifier"
+            },
+            {
+              "value": "A",
+              "type": "String",
+              "label": "Jump button name",
+              "description": "",
+              "group": "Controls",
+              "extraInformation": [],
+              "name": "JumpButton"
+            }
+          ],
+          "sharedPropertyDescriptors": []
+        },
+        {
+          "description": "Control a top-down character with a multitouch controller.",
+          "fullName": "Top-down multitouch controller mapper",
+          "name": "TopDownMultitouchMapper",
+          "objectType": "",
+          "eventsFunctions": [
+            {
+              "fullName": "",
+              "functionType": "Action",
+              "name": "doStepPreEvents",
+              "sentence": "",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [
+                    {
+                      "type": {
+                        "value": "SpriteMultitouchJoystick::TopDownMultitouchMapper::PropertyStickMode"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "=",
+                        "\"Analog\""
+                      ]
+                    }
+                  ],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "TopDownMovementBehavior::SimulateStick"
+                      },
+                      "parameters": [
+                        "Object",
+                        "TopDownMovement",
+                        "SpriteMultitouchJoystick::StickAngle(ControllerIdentifier, JoystickIdentifier)",
+                        "SpriteMultitouchJoystick::StickForce(ControllerIdentifier, JoystickIdentifier)"
+                      ]
+                    }
+                  ]
+                },
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [
+                    {
+                      "type": {
+                        "value": "SpriteMultitouchJoystick::TopDownMultitouchMapper::PropertyStickMode"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "=",
+                        "\"360°\""
+                      ]
+                    }
+                  ],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "TopDownMovementBehavior::SimulateStick"
+                      },
+                      "parameters": [
+                        "Object",
+                        "TopDownMovement",
+                        "SpriteMultitouchJoystick::StickAngle(ControllerIdentifier, JoystickIdentifier)",
+                        "sign(SpriteMultitouchJoystick::StickForce(ControllerIdentifier, JoystickIdentifier))"
+                      ]
+                    }
+                  ]
+                },
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [
+                    {
+                      "type": {
+                        "value": "SpriteMultitouchJoystick::TopDownMultitouchMapper::PropertyStickMode"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "=",
+                        "\"8 Directions\""
+                      ]
+                    }
+                  ],
+                  "actions": [],
+                  "events": [
+                    {
+                      "type": "BuiltinCommonInstructions::Standard",
+                      "conditions": [
+                        {
+                          "type": {
+                            "inverted": true,
+                            "value": "TopDownMovementBehavior::DiagonalsAllowed"
+                          },
+                          "parameters": [
+                            "Object",
+                            "TopDownMovement"
+                          ]
+                        }
+                      ],
+                      "actions": [],
+                      "events": [
+                        {
+                          "type": "BuiltinCommonInstructions::Standard",
+                          "conditions": [
+                            {
+                              "type": {
+                                "value": "SpriteMultitouchJoystick::IsDirectionPushed4Way"
+                              },
+                              "parameters": [
+                                "",
+                                "ControllerIdentifier",
+                                "JoystickIdentifier",
+                                "\"Left\"",
+                                ""
+                              ]
+                            }
+                          ],
+                          "actions": [
+                            {
+                              "type": {
+                                "value": "TopDownMovementBehavior::SimulateLeftKey"
+                              },
+                              "parameters": [
+                                "Object",
+                                "TopDownMovement"
+                              ]
+                            }
+                          ]
+                        },
+                        {
+                          "type": "BuiltinCommonInstructions::Standard",
+                          "conditions": [
+                            {
+                              "type": {
+                                "value": "SpriteMultitouchJoystick::IsDirectionPushed4Way"
+                              },
+                              "parameters": [
+                                "",
+                                "ControllerIdentifier",
+                                "JoystickIdentifier",
+                                "\"Right\"",
+                                ""
+                              ]
+                            }
+                          ],
+                          "actions": [
+                            {
+                              "type": {
+                                "value": "TopDownMovementBehavior::SimulateRightKey"
+                              },
+                              "parameters": [
+                                "Object",
+                                "TopDownMovement"
+                              ]
+                            }
+                          ]
+                        },
+                        {
+                          "type": "BuiltinCommonInstructions::Standard",
+                          "conditions": [
+                            {
+                              "type": {
+                                "value": "SpriteMultitouchJoystick::IsDirectionPushed4Way"
+                              },
+                              "parameters": [
+                                "",
+                                "ControllerIdentifier",
+                                "JoystickIdentifier",
+                                "\"Up\"",
+                                ""
+                              ]
+                            }
+                          ],
+                          "actions": [
+                            {
+                              "type": {
+                                "value": "TopDownMovementBehavior::SimulateUpKey"
+                              },
+                              "parameters": [
+                                "Object",
+                                "TopDownMovement"
+                              ]
+                            }
+                          ]
+                        },
+                        {
+                          "type": "BuiltinCommonInstructions::Standard",
+                          "conditions": [
+                            {
+                              "type": {
+                                "value": "SpriteMultitouchJoystick::IsDirectionPushed4Way"
+                              },
+                              "parameters": [
+                                "",
+                                "ControllerIdentifier",
+                                "JoystickIdentifier",
+                                "\"Down\"",
+                                ""
+                              ]
+                            }
+                          ],
+                          "actions": [
+                            {
+                              "type": {
+                                "value": "TopDownMovementBehavior::SimulateDownKey"
+                              },
+                              "parameters": [
+                                "Object",
+                                "TopDownMovement"
+                              ]
+                            }
+                          ]
+                        }
+                      ]
+                    },
+                    {
+                      "type": "BuiltinCommonInstructions::Standard",
+                      "conditions": [
+                        {
+                          "type": {
+                            "value": "TopDownMovementBehavior::DiagonalsAllowed"
+                          },
+                          "parameters": [
+                            "Object",
+                            "TopDownMovement"
+                          ]
+                        }
+                      ],
+                      "actions": [],
+                      "events": [
+                        {
+                          "type": "BuiltinCommonInstructions::Standard",
+                          "conditions": [
+                            {
+                              "type": {
+                                "value": "SpriteMultitouchJoystick::IsDirectionPushed8Way"
+                              },
+                              "parameters": [
+                                "",
+                                "ControllerIdentifier",
+                                "JoystickIdentifier",
+                                "\"Left\"",
+                                ""
+                              ]
+                            }
+                          ],
+                          "actions": [
+                            {
+                              "type": {
+                                "value": "TopDownMovementBehavior::SimulateLeftKey"
+                              },
+                              "parameters": [
+                                "Object",
+                                "TopDownMovement"
+                              ]
+                            }
+                          ]
+                        },
+                        {
+                          "type": "BuiltinCommonInstructions::Standard",
+                          "conditions": [
+                            {
+                              "type": {
+                                "value": "SpriteMultitouchJoystick::IsDirectionPushed8Way"
+                              },
+                              "parameters": [
+                                "",
+                                "ControllerIdentifier",
+                                "JoystickIdentifier",
+                                "\"Right\"",
+                                ""
+                              ]
+                            }
+                          ],
+                          "actions": [
+                            {
+                              "type": {
+                                "value": "TopDownMovementBehavior::SimulateRightKey"
+                              },
+                              "parameters": [
+                                "Object",
+                                "TopDownMovement"
+                              ]
+                            }
+                          ]
+                        },
+                        {
+                          "type": "BuiltinCommonInstructions::Standard",
+                          "conditions": [
+                            {
+                              "type": {
+                                "value": "SpriteMultitouchJoystick::IsDirectionPushed8Way"
+                              },
+                              "parameters": [
+                                "",
+                                "ControllerIdentifier",
+                                "JoystickIdentifier",
+                                "\"Up\"",
+                                ""
+                              ]
+                            }
+                          ],
+                          "actions": [
+                            {
+                              "type": {
+                                "value": "TopDownMovementBehavior::SimulateUpKey"
+                              },
+                              "parameters": [
+                                "Object",
+                                "TopDownMovement"
+                              ]
+                            }
+                          ]
+                        },
+                        {
+                          "type": "BuiltinCommonInstructions::Standard",
+                          "conditions": [
+                            {
+                              "type": {
+                                "value": "SpriteMultitouchJoystick::IsDirectionPushed8Way"
+                              },
+                              "parameters": [
+                                "",
+                                "ControllerIdentifier",
+                                "JoystickIdentifier",
+                                "\"Down\"",
+                                ""
+                              ]
+                            }
+                          ],
+                          "actions": [
+                            {
+                              "type": {
+                                "value": "TopDownMovementBehavior::SimulateDownKey"
+                              },
+                              "parameters": [
+                                "Object",
+                                "TopDownMovement"
+                              ]
+                            }
+                          ]
+                        },
+                        {
+                          "type": "BuiltinCommonInstructions::Standard",
+                          "conditions": [
+                            {
+                              "type": {
+                                "value": "SpriteMultitouchJoystick::IsDirectionPushed8Way"
+                              },
+                              "parameters": [
+                                "",
+                                "ControllerIdentifier",
+                                "JoystickIdentifier",
+                                "\"UpLeft\"",
+                                ""
+                              ]
+                            }
+                          ],
+                          "actions": [
+                            {
+                              "type": {
+                                "value": "TopDownMovementBehavior::SimulateUpKey"
+                              },
+                              "parameters": [
+                                "Object",
+                                "TopDownMovement"
+                              ]
+                            },
+                            {
+                              "type": {
+                                "value": "TopDownMovementBehavior::SimulateLeftKey"
+                              },
+                              "parameters": [
+                                "Object",
+                                "TopDownMovement"
+                              ]
+                            }
+                          ]
+                        },
+                        {
+                          "type": "BuiltinCommonInstructions::Standard",
+                          "conditions": [
+                            {
+                              "type": {
+                                "value": "SpriteMultitouchJoystick::IsDirectionPushed8Way"
+                              },
+                              "parameters": [
+                                "",
+                                "ControllerIdentifier",
+                                "JoystickIdentifier",
+                                "\"UpRight\"",
+                                ""
+                              ]
+                            }
+                          ],
+                          "actions": [
+                            {
+                              "type": {
+                                "value": "TopDownMovementBehavior::SimulateUpKey"
+                              },
+                              "parameters": [
+                                "Object",
+                                "TopDownMovement"
+                              ]
+                            },
+                            {
+                              "type": {
+                                "value": "TopDownMovementBehavior::SimulateRightKey"
+                              },
+                              "parameters": [
+                                "Object",
+                                "TopDownMovement"
+                              ]
+                            }
+                          ]
+                        },
+                        {
+                          "type": "BuiltinCommonInstructions::Standard",
+                          "conditions": [
+                            {
+                              "type": {
+                                "value": "SpriteMultitouchJoystick::IsDirectionPushed8Way"
+                              },
+                              "parameters": [
+                                "",
+                                "ControllerIdentifier",
+                                "JoystickIdentifier",
+                                "\"DownLeft\"",
+                                ""
+                              ]
+                            }
+                          ],
+                          "actions": [
+                            {
+                              "type": {
+                                "value": "TopDownMovementBehavior::SimulateDownKey"
+                              },
+                              "parameters": [
+                                "Object",
+                                "TopDownMovement"
+                              ]
+                            },
+                            {
+                              "type": {
+                                "value": "TopDownMovementBehavior::SimulateLeftKey"
+                              },
+                              "parameters": [
+                                "Object",
+                                "TopDownMovement"
+                              ]
+                            }
+                          ]
+                        },
+                        {
+                          "type": "BuiltinCommonInstructions::Standard",
+                          "conditions": [
+                            {
+                              "type": {
+                                "value": "SpriteMultitouchJoystick::IsDirectionPushed8Way"
+                              },
+                              "parameters": [
+                                "",
+                                "ControllerIdentifier",
+                                "JoystickIdentifier",
+                                "\"DownRight\"",
+                                ""
+                              ]
+                            }
+                          ],
+                          "actions": [
+                            {
+                              "type": {
+                                "value": "TopDownMovementBehavior::SimulateDownKey"
+                              },
+                              "parameters": [
+                                "Object",
+                                "TopDownMovement"
+                              ]
+                            },
+                            {
+                              "type": {
+                                "value": "TopDownMovementBehavior::SimulateRightKey"
+                              },
+                              "parameters": [
+                                "Object",
+                                "TopDownMovement"
+                              ]
+                            }
+                          ]
+                        }
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "SpriteMultitouchJoystick::TopDownMultitouchMapper",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            }
+          ],
+          "propertyDescriptors": [
+            {
+              "value": "",
+              "type": "Behavior",
+              "label": "Top-down movement behavior",
+              "description": "",
+              "group": "",
+              "extraInformation": [
+                "TopDownMovementBehavior::TopDownMovementBehavior"
+              ],
+              "name": "TopDownMovement"
+            },
+            {
+              "value": "1",
+              "type": "Number",
+              "label": "Controller identifier (1, 2, 3, 4...)",
+              "description": "",
+              "group": "",
+              "extraInformation": [],
+              "name": "ControllerIdentifier"
+            },
+            {
+              "value": "Primary",
+              "type": "Choice",
+              "label": "Joystick name",
+              "description": "",
+              "group": "",
+              "extraInformation": [
+                "Primary",
+                "Secondary"
+              ],
+              "name": "JoystickIdentifier"
+            },
+            {
+              "value": "Analog",
+              "type": "Choice",
+              "label": "Stick mode",
+              "description": "",
+              "group": "Controls",
+              "extraInformation": [
+                "Analog",
+                "360°",
+                "8 Directions"
+              ],
+              "name": "StickMode"
+            }
+          ],
+          "sharedPropertyDescriptors": []
+        }
+      ],
+      "eventsBasedObjects": [
+        {
+          "areaMaxX": 64,
+          "areaMaxY": 64,
+          "areaMaxZ": 64,
+          "areaMinX": 0,
+          "areaMinY": 0,
+          "areaMinZ": 0,
+          "defaultName": "Joystick",
+          "description": "Joystick for touchscreens.",
+          "fullName": "Multitouch Joystick",
+          "isUsingLegacyInstancesRenderer": true,
+          "name": "SpriteMultitouchJoystick",
+          "eventsFunctions": [
+            {
+              "fullName": "",
+              "functionType": "Action",
+              "name": "onCreated",
+              "sentence": "",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "Create"
+                      },
+                      "parameters": [
+                        "",
+                        "Border",
+                        "0",
+                        "0",
+                        ""
+                      ]
+                    },
+                    {
+                      "type": {
+                        "value": "Create"
+                      },
+                      "parameters": [
+                        "",
+                        "Thumb",
+                        "0",
+                        "0",
+                        ""
+                      ]
+                    },
+                    {
+                      "type": {
+                        "value": "ChangePlan"
+                      },
+                      "parameters": [
+                        "Border",
+                        "=",
+                        "1"
+                      ]
+                    },
+                    {
+                      "type": {
+                        "value": "ChangePlan"
+                      },
+                      "parameters": [
+                        "Thumb",
+                        "=",
+                        "2"
+                      ]
+                    },
+                    {
+                      "type": {
+                        "value": "SetCenter"
+                      },
+                      "parameters": [
+                        "Border",
+                        "=",
+                        "0",
+                        "=",
+                        "0"
+                      ]
+                    },
+                    {
+                      "type": {
+                        "value": "SetCenter"
+                      },
+                      "parameters": [
+                        "Thumb",
+                        "=",
+                        "0",
+                        "=",
+                        "0"
+                      ]
+                    },
+                    {
+                      "type": {
+                        "value": "SpriteMultitouchJoystick::SpriteMultitouchJoystick::UpdateConfiguration"
+                      },
+                      "parameters": [
+                        "Object",
+                        ""
+                      ]
+                    },
+                    {
+                      "type": {
+                        "value": "SetIncludedInParentCollisionMask"
+                      },
+                      "parameters": [
+                        "Thumb",
+                        ""
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "supplementaryInformation": "SpriteMultitouchJoystick::SpriteMultitouchJoystick",
+                  "type": "object"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "fullName": "",
+              "functionType": "Action",
+              "name": "doStepPostEvents",
+              "sentence": "",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "MettreAutour"
+                      },
+                      "parameters": [
+                        "Thumb",
+                        "Border",
+                        "Border.MultitouchJoystick::JoystickForce() * Border.Width() / 2",
+                        "Border.MultitouchJoystick::JoystickAngle()"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "supplementaryInformation": "SpriteMultitouchJoystick::SpriteMultitouchJoystick",
+                  "type": "object"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "fullName": "",
+              "functionType": "Action",
+              "name": "onHotReloading",
+              "sentence": "",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SpriteMultitouchJoystick::SpriteMultitouchJoystick::UpdateConfiguration"
+                      },
+                      "parameters": [
+                        "Object",
+                        ""
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "supplementaryInformation": "SpriteMultitouchJoystick::SpriteMultitouchJoystick",
+                  "type": "object"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "Pass the object property values to the behavior.",
+              "fullName": "Update configuration",
+              "functionType": "Action",
+              "name": "UpdateConfiguration",
+              "private": true,
+              "sentence": "Update the configuration of _PARAM0_",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SpriteMultitouchJoystick::MultitouchJoystick::SetControllerIdentifier"
+                      },
+                      "parameters": [
+                        "Border",
+                        "MultitouchJoystick",
+                        "=",
+                        "Object.PropertyControllerIdentifier()",
+                        ""
+                      ]
+                    },
+                    {
+                      "type": {
+                        "value": "SpriteMultitouchJoystick::MultitouchJoystick::SetJoystickIdentifier"
+                      },
+                      "parameters": [
+                        "Border",
+                        "MultitouchJoystick",
+                        "=",
+                        "Object.PropertyJoystickIdentifier()",
+                        ""
+                      ]
+                    },
+                    {
+                      "type": {
+                        "value": "SpriteMultitouchJoystick::MultitouchJoystick::SetDeadZoneRadius"
+                      },
+                      "parameters": [
+                        "Border",
+                        "MultitouchJoystick",
+                        "=",
+                        "Object.PropertyDeadZoneRadius()",
+                        ""
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "supplementaryInformation": "SpriteMultitouchJoystick::SpriteMultitouchJoystick",
+                  "type": "object"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "De/activate control of the joystick.",
+              "fullName": "De/activate control",
+              "functionType": "Action",
+              "name": "ActivateControl",
+              "sentence": "Activate control of _PARAM0_: _PARAM1_",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [
+                    {
+                      "type": {
+                        "inverted": true,
+                        "value": "GetArgumentAsBoolean"
+                      },
+                      "parameters": [
+                        "\"ShouldActivate\""
+                      ]
+                    }
+                  ],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "ActivateBehavior"
+                      },
+                      "parameters": [
+                        "Border",
+                        "MultitouchJoystick",
+                        "no"
+                      ]
+                    }
+                  ]
+                },
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [
+                    {
+                      "type": {
+                        "value": "GetArgumentAsBoolean"
+                      },
+                      "parameters": [
+                        "\"ShouldActivate\""
+                      ]
+                    }
+                  ],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "ActivateBehavior"
+                      },
+                      "parameters": [
+                        "Border",
+                        "MultitouchJoystick",
+                        "yes"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "supplementaryInformation": "SpriteMultitouchJoystick::SpriteMultitouchJoystick",
+                  "type": "object"
+                },
+                {
+                  "description": "Activate",
+                  "name": "ShouldActivate",
+                  "type": "yesorno"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "Check if a stick is pressed.",
+              "fullName": "Stick pressed",
+              "functionType": "Condition",
+              "name": "IsPressed",
+              "sentence": "Stick _PARAM0_ is pressed",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [
+                    {
+                      "type": {
+                        "value": "SpriteMultitouchJoystick::MultitouchJoystick::IsPressed"
+                      },
+                      "parameters": [
+                        "Border",
+                        "MultitouchJoystick",
+                        "!="
+                      ]
+                    }
+                  ],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SetReturnBoolean"
+                      },
+                      "parameters": [
+                        "True"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "supplementaryInformation": "SpriteMultitouchJoystick::SpriteMultitouchJoystick",
+                  "type": "object"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "the joystick force (from 0 to 1).",
+              "fullName": "Joystick force (deprecated)",
+              "functionType": "ExpressionAndCondition",
+              "name": "JoystickForce",
+              "private": true,
+              "sentence": "the joystick force",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SetReturnNumber"
+                      },
+                      "parameters": [
+                        "Border.MultitouchJoystick::JoystickForce()"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "expressionType": {
+                "type": "expression"
+              },
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "supplementaryInformation": "SpriteMultitouchJoystick::SpriteMultitouchJoystick",
+                  "type": "object"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "the strick force (from 0 to 1).",
+              "fullName": "Stick force",
+              "functionType": "ExpressionAndCondition",
+              "name": "StickForce",
+              "sentence": "the stick force",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SetReturnNumber"
+                      },
+                      "parameters": [
+                        "Border.MultitouchJoystick::JoystickForce()"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "expressionType": {
+                "type": "expression"
+              },
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "supplementaryInformation": "SpriteMultitouchJoystick::SpriteMultitouchJoystick",
+                  "type": "object"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "the stick force on X axis (from -1 at the left to 1 at the right).",
+              "fullName": "Stick X force",
+              "functionType": "ExpressionAndCondition",
+              "name": "StickForceX",
+              "sentence": "the stick X force",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SetReturnNumber"
+                      },
+                      "parameters": [
+                        "Border.MultitouchJoystick::StickForceX()"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "expressionType": {
+                "type": "expression"
+              },
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "supplementaryInformation": "SpriteMultitouchJoystick::SpriteMultitouchJoystick",
+                  "type": "object"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "the stick force on Y axis (from -1 at the top to 1 at the bottom).",
+              "fullName": "Stick Y force",
+              "functionType": "ExpressionAndCondition",
+              "name": "StickForceY",
+              "sentence": "the stick Y force",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SetReturnNumber"
+                      },
+                      "parameters": [
+                        "Border.MultitouchJoystick::StickForceY()"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "expressionType": {
+                "type": "expression"
+              },
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "supplementaryInformation": "SpriteMultitouchJoystick::SpriteMultitouchJoystick",
+                  "type": "object"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "Return the angle the joystick is pointing towards (from -180 to 180).",
+              "fullName": "Joystick angle (deprecated)",
+              "functionType": "Expression",
+              "name": "JoystickAngle",
+              "private": true,
+              "sentence": "",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SetReturnNumber"
+                      },
+                      "parameters": [
+                        "Border.MultitouchJoystick::JoystickAngle()"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "expressionType": {
+                "type": "expression"
+              },
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "supplementaryInformation": "SpriteMultitouchJoystick::SpriteMultitouchJoystick",
+                  "type": "object"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "Return the angle the stick is pointing towards (from -180 to 180).",
+              "fullName": "Stick angle",
+              "functionType": "Expression",
+              "name": "StickAngle",
+              "sentence": "",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SetReturnNumber"
+                      },
+                      "parameters": [
+                        "Border.MultitouchJoystick::JoystickAngle()"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "expressionType": {
+                "type": "expression"
+              },
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "supplementaryInformation": "SpriteMultitouchJoystick::SpriteMultitouchJoystick",
+                  "type": "object"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "Check if joystick is pushed in a given direction.",
+              "fullName": "Joystick pushed in a direction (4-way movement)",
+              "functionType": "Condition",
+              "name": "IsDirectionPushed4Way",
+              "sentence": "_PARAM0_ is pushed in direction _PARAM1_",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [
+                    {
+                      "type": {
+                        "value": "SpriteMultitouchJoystick::MultitouchJoystick::IsDirectionPushed4Way"
+                      },
+                      "parameters": [
+                        "Border",
+                        "MultitouchJoystick",
+                        "Direction",
+                        ""
+                      ]
+                    }
+                  ],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SetReturnBoolean"
+                      },
+                      "parameters": [
+                        "True"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "supplementaryInformation": "SpriteMultitouchJoystick::SpriteMultitouchJoystick",
+                  "type": "object"
+                },
+                {
+                  "description": "Direction",
+                  "name": "Direction",
+                  "supplementaryInformation": "[\"Up\",\"Down\",\"Left\",\"Right\"]",
+                  "type": "stringWithSelector"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "Check if joystick is pushed in a given direction.",
+              "fullName": "Joystick pushed in a direction (8-way movement)",
+              "functionType": "Condition",
+              "name": "IsDirectionPushed8Way",
+              "sentence": "_PARAM0_ is pushed in direction _PARAM1_",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [
+                    {
+                      "type": {
+                        "value": "SpriteMultitouchJoystick::MultitouchJoystick::IsDirectionPushed8Way"
+                      },
+                      "parameters": [
+                        "Border",
+                        "MultitouchJoystick",
+                        "Direction",
+                        ""
+                      ]
+                    }
+                  ],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SetReturnBoolean"
+                      },
+                      "parameters": [
+                        "True"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "supplementaryInformation": "SpriteMultitouchJoystick::SpriteMultitouchJoystick",
+                  "type": "object"
+                },
+                {
+                  "description": "Direction",
+                  "name": "Direction",
+                  "supplementaryInformation": "[\"Up\",\"Down\",\"Left\",\"Right\",\"UpLeft\",\"UpRight\",\"DownLeft\",\"DownRight\"]",
+                  "type": "stringWithSelector"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "the multitouch controller identifier (1, 2, 3, 4...).",
+              "fullName": "Multitouch controller identifier",
+              "functionType": "ExpressionAndCondition",
+              "group": "Multitouch Joystick configuration",
+              "name": "ControllerIdentifier",
+              "sentence": "the multitouch controller identifier",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SetReturnNumber"
+                      },
+                      "parameters": [
+                        "Border.MultitouchJoystick::PropertyControllerIdentifier()"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "expressionType": {
+                "type": "expression"
+              },
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "supplementaryInformation": "SpriteMultitouchJoystick::SpriteMultitouchJoystick",
+                  "type": "object"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "fullName": "",
+              "functionType": "ActionWithOperator",
+              "getterName": "ControllerIdentifier",
+              "name": "SetControllerIdentifier",
+              "sentence": "",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SpriteMultitouchJoystick::MultitouchJoystick::SetControllerIdentifier"
+                      },
+                      "parameters": [
+                        "Border",
+                        "MultitouchJoystick",
+                        "=",
+                        "Value",
+                        ""
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "supplementaryInformation": "SpriteMultitouchJoystick::SpriteMultitouchJoystick",
+                  "type": "object"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "the joystick name of the object.",
+              "fullName": "Joystick name",
+              "functionType": "ExpressionAndCondition",
+              "group": "Multitouch Joystick configuration",
+              "name": "JoystickIdentifier",
+              "sentence": "the joystick name",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SetReturnString"
+                      },
+                      "parameters": [
+                        "Border.MultitouchJoystick::PropertyJoystickIdentifier()"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "expressionType": {
+                "type": "string"
+              },
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "supplementaryInformation": "SpriteMultitouchJoystick::SpriteMultitouchJoystick",
+                  "type": "object"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "fullName": "",
+              "functionType": "ActionWithOperator",
+              "getterName": "JoystickIdentifier",
+              "name": "SetJoystickIdentifier",
+              "sentence": "",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SpriteMultitouchJoystick::MultitouchJoystick::SetJoystickIdentifier"
+                      },
+                      "parameters": [
+                        "Border",
+                        "MultitouchJoystick",
+                        "=",
+                        "Value",
+                        ""
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "supplementaryInformation": "SpriteMultitouchJoystick::SpriteMultitouchJoystick",
+                  "type": "object"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "the dead zone radius of the joystick (range: 0 to 1). The deadzone is an area for which movement on sticks won't be taken into account (instead, the stick will be considered as not moved).",
+              "fullName": "Dead zone radius",
+              "functionType": "ExpressionAndCondition",
+              "group": "Multitouch Joystick configuration",
+              "name": "DeadZoneRadius",
+              "sentence": "the dead zone radius",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SetReturnNumber"
+                      },
+                      "parameters": [
+                        "Border.MultitouchJoystick::PropertyDeadZoneRadius()"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "expressionType": {
+                "type": "expression"
+              },
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "supplementaryInformation": "SpriteMultitouchJoystick::SpriteMultitouchJoystick",
+                  "type": "object"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "fullName": "",
+              "functionType": "ActionWithOperator",
+              "getterName": "DeadZoneRadius",
+              "name": "SetDeadZoneRadius",
+              "sentence": "",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SpriteMultitouchJoystick::MultitouchJoystick::SetDeadZoneRadius"
+                      },
+                      "parameters": [
+                        "Border",
+                        "MultitouchJoystick",
+                        "=",
+                        "Value",
+                        ""
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "supplementaryInformation": "SpriteMultitouchJoystick::SpriteMultitouchJoystick",
+                  "type": "object"
+                }
+              ],
+              "objectGroups": []
+            }
+          ],
+          "propertyDescriptors": [
+            {
+              "value": "1",
+              "type": "Number",
+              "label": "Multitouch controller identifier (1, 2, 3, 4...)",
+              "description": "",
+              "group": "",
+              "extraInformation": [],
+              "name": "ControllerIdentifier"
+            },
+            {
+              "value": "Primary",
+              "type": "Choice",
+              "label": "Joystick name",
+              "description": "",
+              "group": "",
+              "extraInformation": [
+                "Primary",
+                "Secondary"
+              ],
+              "name": "JoystickIdentifier"
+            },
+            {
+              "value": "0.4",
+              "type": "Number",
+              "label": "Dead zone radius (range: 0 to 1)",
+              "description": "The deadzone is an area for which movement on sticks won't be taken into account (instead, the stick will be considered as not moved)",
+              "group": "",
+              "extraInformation": [],
+              "name": "DeadZoneRadius"
+            },
+            {
+              "value": "Center-center",
+              "type": "String",
+              "label": "",
+              "description": "Only used by the scene editor.",
+              "group": "",
+              "extraInformation": [
+                "Thumb"
+              ],
+              "hidden": true,
+              "name": "ThumbAnchorOrigin"
+            },
+            {
+              "value": "Center-center",
+              "type": "Number",
+              "label": "",
+              "description": "Only used by the scene editor.",
+              "group": "",
+              "extraInformation": [],
+              "hidden": true,
+              "name": "ThumbAnchorTarget"
+            },
+            {
+              "value": "true",
+              "type": "Boolean",
+              "label": "",
+              "description": "Only used by the scene editor.",
+              "group": "",
+              "extraInformation": [
+                "Thumb"
+              ],
+              "hidden": true,
+              "name": "ThumbIsScaledProportionally"
+            },
+            {
+              "value": "Center-center",
+              "type": "String",
+              "label": "",
+              "description": "Only used by the scene editor.",
+              "group": "",
+              "extraInformation": [],
+              "hidden": true,
+              "name": "ParentOrigin"
+            }
+          ],
+          "objects": [
+            {
+              "adaptCollisionMaskAutomatically": false,
+              "assetStoreId": "",
+              "name": "Thumb",
+              "type": "Sprite",
+              "updateIfNotVisible": false,
+              "variables": [],
+              "effects": [],
+              "behaviors": [],
+              "animations": [
+                {
+                  "name": "Idle",
+                  "useMultipleDirections": false,
+                  "directions": [
+                    {
+                      "looping": false,
+                      "timeBetweenFrames": 0.08,
+                      "sprites": []
+                    }
+                  ]
+                }
+              ]
+            },
+            {
+              "adaptCollisionMaskAutomatically": false,
+              "assetStoreId": "",
+              "name": "Border",
+              "type": "Sprite",
+              "updateIfNotVisible": false,
+              "variables": [],
+              "effects": [],
+              "behaviors": [
+                {
+                  "name": "MultitouchJoystick",
+                  "type": "SpriteMultitouchJoystick::MultitouchJoystick",
+                  "ControllerIdentifier": 1,
+                  "JoystickIdentifier": "Primary",
+                  "FloatingEnabled": false,
+                  "DeadZoneRadius": 0.4,
+                  "JoystickAngle": 0,
+                  "JoystickForce": 0,
+                  "TouchId": 0,
+                  "TouchIndex": 0
+                }
+              ],
+              "animations": [
+                {
+                  "name": "Idle",
+                  "useMultipleDirections": false,
+                  "directions": [
+                    {
+                      "looping": false,
+                      "timeBetweenFrames": 0.08,
+                      "sprites": []
+                    }
+                  ]
+                }
+              ]
+            }
+          ],
+          "objectsFolderStructure": {
+            "folderName": "__ROOT",
+            "children": [
+              {
+                "objectName": "Thumb"
+              },
+              {
+                "objectName": "Border"
+              }
+            ]
+          },
+          "objectsGroups": [],
+          "layers": [
+            {
+              "ambientLightColorB": 200,
+              "ambientLightColorG": 200,
+              "ambientLightColorR": 200,
+              "camera3DFarPlaneDistance": 10000,
+              "camera3DFieldOfView": 45,
+              "camera3DNearPlaneDistance": 3,
+              "cameraType": "",
+              "followBaseLayerCamera": false,
+              "isLightingLayer": false,
+              "isLocked": false,
+              "name": "",
+              "renderingType": "",
+              "visibility": true,
+              "cameras": [
+                {
+                  "defaultSize": true,
+                  "defaultViewport": true,
+                  "height": 0,
+                  "viewportBottom": 1,
+                  "viewportLeft": 0,
+                  "viewportRight": 1,
+                  "viewportTop": 0,
+                  "width": 0
+                }
+              ],
+              "effects": []
+            }
+          ],
+          "instances": []
+        }
+      ]
+    },
+    {
+      "author": "",
+      "category": "Camera",
+      "extensionNamespace": "",
+      "fullName": "Smooth Camera",
+      "helpPath": "/tutorials/follow-player-with-camera/",
+      "iconUrl": "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4NCjwhLS0gR2VuZXJhdG9yOiBBZG9iZSBJbGx1c3RyYXRvciAyMy4wLjMsIFNWRyBFeHBvcnQgUGx1Zy1JbiAuIFNWRyBWZXJzaW9uOiA2LjAwIEJ1aWxkIDApICAtLT4NCjxzdmcgdmVyc2lvbj0iMS4xIiBpZD0iSWNvbnMiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIHg9IjBweCIgeT0iMHB4Ig0KCSB2aWV3Qm94PSIwIDAgMzIgMzIiIHN0eWxlPSJlbmFibGUtYmFja2dyb3VuZDpuZXcgMCAwIDMyIDMyOyIgeG1sOnNwYWNlPSJwcmVzZXJ2ZSI+DQo8c3R5bGUgdHlwZT0idGV4dC9jc3MiPg0KCS5zdDB7ZmlsbDpub25lO3N0cm9rZTojMDAwMDAwO3N0cm9rZS13aWR0aDoyO3N0cm9rZS1saW5lY2FwOnJvdW5kO3N0cm9rZS1saW5lam9pbjpyb3VuZDtzdHJva2UtbWl0ZXJsaW1pdDoxMDt9DQoJLnN0MXtmaWxsOm5vbmU7c3Ryb2tlOiMwMDAwMDA7c3Ryb2tlLXdpZHRoOjI7c3Ryb2tlLWxpbmVqb2luOnJvdW5kO3N0cm9rZS1taXRlcmxpbWl0OjEwO30NCjwvc3R5bGU+DQo8cGF0aCBjbGFzcz0ic3QwIiBkPSJNMjQsMTNoLTZjLTEuMSwwLTItMC45LTItMlY1YzAtMS4xLDAuOS0yLDItMmg2YzEuMSwwLDIsMC45LDIsMnY2QzI2LDEyLjEsMjUuMSwxMywyNCwxM3oiLz4NCjxwYXRoIGNsYXNzPSJzdDAiIGQ9Ik0yNiw4djEwYzAsMS4xLTAuOSwyLTIsMkg4Yy0xLjEsMC0yLTAuOS0yLTJWOGMwLTEuMSwwLjktMiwyLTJoOCIvPg0KPGNpcmNsZSBjbGFzcz0ic3QwIiBjeD0iMjEiIGN5PSI4IiByPSIyIi8+DQo8Y2lyY2xlIGNsYXNzPSJzdDAiIGN4PSIxMSIgY3k9IjE2IiByPSIxIi8+DQo8cmVjdCB4PSI5IiB5PSI5IiBjbGFzcz0ic3QwIiB3aWR0aD0iNCIgaGVpZ2h0PSIzIi8+DQo8cG9seWxpbmUgY2xhc3M9InN0MCIgcG9pbnRzPSIyMSwyOSAyMSwyOSAxMSwyOSAxMSwyOSAiLz4NCjxwb2x5bGluZSBjbGFzcz0ic3QwIiBwb2ludHM9IjE4LDIwIDE4LDI5IDE0LDI5IDE0LDIwICIvPg0KPHJlY3QgeD0iNyIgeT0iMyIgY2xhc3M9InN0MCIgd2lkdGg9IjQiIGhlaWdodD0iMyIvPg0KPC9zdmc+DQo=",
+      "name": "SmoothCamera",
+      "previewIconUrl": "https://resources.gdevelop-app.com/assets/Icons/Line Hero Pack/Master/SVG/Computers and Hardware/Computers and Hardware_camcoder_gopro_go_pro_camera.svg",
+      "shortDescription": "Smoothly scroll to follow an object.",
+      "version": "0.3.2",
+      "description": [
+        "The camera follows an object according to:",
+        "- a frame rate independent catch-up speed to make the scrolling from smooth to strong",
+        "- a maximum speed to do linear following ([open the project online](https://editor.gdevelop.io/?project=example://platformer-with-tilemap)) or slow down the camera when teleporting the object",
+        "- a follow-free zone to avoid scrolling on small movements",
+        "- an offset to see further in one direction",
+        "- an extra delay and catch-up speed to give an impression of speed (useful for dash)",
+        "- position forecasting and delay to simulate a cameraman response time",
+        "",
+        "A platformer dedicated behavior allows to switch of settings when the character is in air or on the floor. This can be used to stabilize the camera when jumping."
+      ],
+      "origin": {
+        "identifier": "SmoothCamera",
+        "name": "gdevelop-extension-store"
+      },
+      "tags": [
+        "camera",
+        "scrolling",
+        "follow",
+        "smooth",
+        "platformer",
+        "platform"
+      ],
+      "authorIds": [
+        "IWykYNRvhCZBN3vEgKEbBPOR3Oc2"
+      ],
+      "dependencies": [],
+      "globalVariables": [],
+      "sceneVariables": [],
+      "eventsFunctions": [],
+      "eventsBasedBehaviors": [
+        {
+          "description": "Smoothly scroll to follow an object.",
+          "fullName": "Smooth Camera",
+          "name": "SmoothCamera",
+          "objectType": "",
+          "eventsFunctions": [
+            {
+              "fullName": "",
+              "functionType": "Action",
+              "name": "onCreated",
+              "sentence": "",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Comment",
+                  "color": {
+                    "b": 109,
+                    "g": 230,
+                    "r": 255,
+                    "textB": 0,
+                    "textG": 0,
+                    "textR": 0
+                  },
+                  "comment": "Update private properties through setters to check their values and initialize state."
+                },
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SmoothCamera::SmoothCamera::SetLeftwardSpeed"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "LeftwardSpeed",
+                        "log(1 - )"
+                      ]
+                    },
+                    {
+                      "type": {
+                        "value": "SmoothCamera::SmoothCamera::SetRightwardSpeed"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "RightwardSpeed",
+                        "log(1 - )"
+                      ]
+                    },
+                    {
+                      "type": {
+                        "value": "SmoothCamera::SmoothCamera::SetUpwardSpeed"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "UpwardSpeed",
+                        "log(1 - )"
+                      ]
+                    },
+                    {
+                      "type": {
+                        "value": "SmoothCamera::SmoothCamera::SetDownwardSpeed"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "DownwardSpeed",
+                        "log(1 - )"
+                      ]
+                    },
+                    {
+                      "type": {
+                        "value": "SmoothCamera::SmoothCamera::SetLeftwardSpeedMax"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "LeftwardSpeedMax",
+                        "log(1 - )"
+                      ]
+                    },
+                    {
+                      "type": {
+                        "value": "SmoothCamera::SmoothCamera::SetRightwardSpeedMax"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "RightwardSpeedMax",
+                        "log(1 - )"
+                      ]
+                    },
+                    {
+                      "type": {
+                        "value": "SmoothCamera::SmoothCamera::SetUpwardSpeedMax"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "UpwardSpeedMax",
+                        "log(1 - )"
+                      ]
+                    },
+                    {
+                      "type": {
+                        "value": "SmoothCamera::SmoothCamera::SetDownwardSpeedMax"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "DownwardSpeedMax",
+                        "log(1 - )"
+                      ]
+                    },
+                    {
+                      "type": {
+                        "value": "SmoothCamera::SmoothCamera::SetFollowFreeAreaLeft"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "FollowFreeAreaLeft",
+                        "log(1 - )"
+                      ]
+                    },
+                    {
+                      "type": {
+                        "value": "SmoothCamera::SmoothCamera::SetFollowFreeAreaRight"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "FollowFreeAreaRight",
+                        "log(1 - )"
+                      ]
+                    },
+                    {
+                      "type": {
+                        "value": "SmoothCamera::SmoothCamera::SetFollowFreeAreaTop"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "FollowFreeAreaTop",
+                        "log(1 - )"
+                      ]
+                    },
+                    {
+                      "type": {
+                        "value": "SmoothCamera::SmoothCamera::SetFollowFreeAreaBottom"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "FollowFreeAreaBottom",
+                        "log(1 - )"
+                      ]
+                    },
+                    {
+                      "type": {
+                        "value": "SmoothCamera::SmoothCamera::SetPropertyCameraDelay"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "=",
+                        "CameraDelay"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "SmoothCamera::SmoothCamera",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "fullName": "",
+              "functionType": "Action",
+              "name": "doStepPreEvents",
+              "sentence": "",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [
+                    {
+                      "type": {
+                        "inverted": true,
+                        "value": "SmoothCamera::SmoothCamera::PropertyIsCalledManually"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior"
+                      ]
+                    }
+                  ],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SmoothCamera::SmoothCamera::DoMoveCameraCloser"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        ""
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "SmoothCamera::SmoothCamera",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "Move the camera closer to the object. This action must be called after the object has moved for the frame.",
+              "fullName": "Move the camera closer",
+              "functionType": "Action",
+              "name": "MoveCameraCloser",
+              "sentence": "Move the camera closer to _PARAM0_",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Comment",
+                  "color": {
+                    "b": 109,
+                    "g": 230,
+                    "r": 255,
+                    "textB": 0,
+                    "textG": 0,
+                    "textR": 0
+                  },
+                  "comment": "The camera following is called with an action, the call from doStepPreEvents must be disabled to avoid to do it twice."
+                },
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SmoothCamera::SmoothCamera::SetPropertyIsCalledManually"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "yes"
+                      ]
+                    },
+                    {
+                      "type": {
+                        "value": "SmoothCamera::SmoothCamera::DoMoveCameraCloser"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        ""
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "SmoothCamera::SmoothCamera",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "Move the camera closer to the object.",
+              "fullName": "Do move the camera closer",
+              "functionType": "Action",
+              "name": "DoMoveCameraCloser",
+              "private": true,
+              "sentence": "Do move the camera closer _PARAM0_",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Comment",
+                  "color": {
+                    "b": 109,
+                    "g": 230,
+                    "r": 255,
+                    "textB": 0,
+                    "textG": 0,
+                    "textR": 0
+                  },
+                  "comment": "Delaying and forecasting can be used at the same time.\nForecasting only use the positions that are older than the one used for delaying.\nThe behavior uses a position history that is split in 2 arrays:\n- one for delaying the position (from TimeFromStart to TimeFromStart - CamearDelay)\n- one for forecasting the position (from TimeFromStart - CamearDelay to TimeFromStart - CamearDelay - ForecastHistoryDuration"
+                },
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SmoothCamera::SmoothCamera::UpdateDelayedPosition"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        ""
+                      ]
+                    },
+                    {
+                      "type": {
+                        "value": "SmoothCamera::SmoothCamera::UpdateForecastedPosition"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        ""
+                      ]
+                    }
+                  ]
+                },
+                {
+                  "type": "BuiltinCommonInstructions::Comment",
+                  "color": {
+                    "b": 109,
+                    "g": 230,
+                    "r": 255,
+                    "textB": 0,
+                    "textG": 0,
+                    "textR": 0
+                  },
+                  "comment": "At each frame, the camera must catchup the target by a given ratio (speed)\ncameraX(t) - targetX = (cameraX(t - 1) - targetX) * speed\n\nThe frame rate must not impact on the catch-up speed, we don't want a speed in ratio per frame but a speed ratio per second, like this:\ncameraX(t) - targetX = (cameraX(t - 1s) - targetX) * speed\n\nOk, but we still need to process each frame, we can use a exponent for this:\ncameraX(t) - targetX = (cameraX(t - timeDelta) - targetX) * speed^timeDelta\ncameraX(t) = targetX + (cameraX(t - timeDelta) - targetX) * exp(timeDelta * ln(speed))\n\npow is probably more efficient than precalculated log if the speed is changed continuously but this might be rare enough."
+                },
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [
+                    {
+                      "type": {
+                        "value": "SmoothCamera::SmoothCamera::PropertyFollowOnX"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior"
+                      ]
+                    }
+                  ],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SmoothCamera::SmoothCamera::SetPropertyOldX"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "=",
+                        "CameraX(Object.Layer(), 0)"
+                      ]
+                    }
+                  ],
+                  "events": [
+                    {
+                      "type": "BuiltinCommonInstructions::Standard",
+                      "conditions": [
+                        {
+                          "type": {
+                            "value": "CameraX"
+                          },
+                          "parameters": [
+                            "",
+                            ">",
+                            "Object.Behavior::FreeAreaRight()",
+                            "Object.Layer()",
+                            "0"
+                          ]
+                        }
+                      ],
+                      "actions": [
+                        {
+                          "type": {
+                            "value": "SetCameraX"
+                          },
+                          "parameters": [
+                            "",
+                            "=",
+                            "Object.Behavior::FreeAreaRight()\n+ (CameraX(Object.Layer(), 0) - Object.Behavior::FreeAreaRight())\n* exp(TimeDelta() * LogLeftwardSpeed)",
+                            "Object.Layer()",
+                            "0"
+                          ]
+                        }
+                      ],
+                      "events": [
+                        {
+                          "type": "BuiltinCommonInstructions::Standard",
+                          "conditions": [
+                            {
+                              "type": {
+                                "value": "CameraX"
+                              },
+                              "parameters": [
+                                "",
+                                "<",
+                                "OldX - LeftwardSpeedMax * TimeDelta()",
+                                "Object.Layer()",
+                                "0"
+                              ]
+                            }
+                          ],
+                          "actions": [
+                            {
+                              "type": {
+                                "value": "SetCameraX"
+                              },
+                              "parameters": [
+                                "",
+                                "=",
+                                "OldX - LeftwardSpeedMax * TimeDelta()",
+                                "Object.Layer()",
+                                "0"
+                              ]
+                            }
+                          ]
+                        }
+                      ]
+                    },
+                    {
+                      "type": "BuiltinCommonInstructions::Standard",
+                      "conditions": [
+                        {
+                          "type": {
+                            "value": "CameraX"
+                          },
+                          "parameters": [
+                            "",
+                            "<",
+                            "Object.Behavior::FreeAreaLeft()",
+                            "Object.Layer()",
+                            "0"
+                          ]
+                        }
+                      ],
+                      "actions": [
+                        {
+                          "type": {
+                            "value": "SetCameraX"
+                          },
+                          "parameters": [
+                            "",
+                            "=",
+                            "Object.Behavior::FreeAreaLeft()\n+ (CameraX(Object.Layer(), 0) - Object.Behavior::FreeAreaLeft())\n* exp(TimeDelta() * LogRightwardSpeed)",
+                            "Object.Layer()",
+                            "0"
+                          ]
+                        }
+                      ],
+                      "events": [
+                        {
+                          "type": "BuiltinCommonInstructions::Standard",
+                          "conditions": [
+                            {
+                              "type": {
+                                "value": "CameraX"
+                              },
+                              "parameters": [
+                                "",
+                                ">",
+                                "OldX + RightwardSpeedMax * TimeDelta()",
+                                "Object.Layer()",
+                                "0"
+                              ]
+                            }
+                          ],
+                          "actions": [
+                            {
+                              "type": {
+                                "value": "SetCameraX"
+                              },
+                              "parameters": [
+                                "",
+                                "=",
+                                "OldX + RightwardSpeedMax * TimeDelta()",
+                                "Object.Layer()",
+                                "0"
+                              ]
+                            }
+                          ]
+                        }
+                      ]
+                    }
+                  ]
+                },
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [
+                    {
+                      "type": {
+                        "value": "SmoothCamera::SmoothCamera::PropertyFollowOnY"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior"
+                      ]
+                    }
+                  ],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SmoothCamera::SmoothCamera::SetPropertyOldY"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "=",
+                        "CameraY(Object.Layer(), 0)"
+                      ]
+                    }
+                  ],
+                  "events": [
+                    {
+                      "type": "BuiltinCommonInstructions::Standard",
+                      "conditions": [
+                        {
+                          "type": {
+                            "value": "CameraY"
+                          },
+                          "parameters": [
+                            "",
+                            ">",
+                            "Object.Behavior::FreeAreaBottom()",
+                            "Object.Layer()",
+                            "0"
+                          ]
+                        }
+                      ],
+                      "actions": [
+                        {
+                          "type": {
+                            "value": "SetCameraY"
+                          },
+                          "parameters": [
+                            "",
+                            "=",
+                            "Object.Behavior::FreeAreaBottom()\n+ (CameraY(Object.Layer(), 0) - Object.Behavior::FreeAreaBottom())\n* exp(TimeDelta() * LogUpwardSpeed)",
+                            "Object.Layer()",
+                            "0"
+                          ]
+                        }
+                      ],
+                      "events": [
+                        {
+                          "type": "BuiltinCommonInstructions::Standard",
+                          "conditions": [
+                            {
+                              "type": {
+                                "value": "CameraY"
+                              },
+                              "parameters": [
+                                "",
+                                "<",
+                                "OldY - UpwardSpeedMax * TimeDelta()",
+                                "Object.Layer()",
+                                "0"
+                              ]
+                            }
+                          ],
+                          "actions": [
+                            {
+                              "type": {
+                                "value": "SetCameraY"
+                              },
+                              "parameters": [
+                                "",
+                                "=",
+                                "OldY - UpwardSpeedMax * TimeDelta()",
+                                "Object.Layer()",
+                                "0"
+                              ]
+                            }
+                          ]
+                        }
+                      ]
+                    },
+                    {
+                      "type": "BuiltinCommonInstructions::Standard",
+                      "conditions": [
+                        {
+                          "type": {
+                            "value": "CameraY"
+                          },
+                          "parameters": [
+                            "",
+                            "<",
+                            "Object.Behavior::FreeAreaTop()",
+                            "Object.Layer()",
+                            "0"
+                          ]
+                        }
+                      ],
+                      "actions": [
+                        {
+                          "type": {
+                            "value": "SetCameraY"
+                          },
+                          "parameters": [
+                            "",
+                            "=",
+                            "Object.Behavior::FreeAreaTop()\n+ (CameraY(Object.Layer(), 0) - Object.Behavior::FreeAreaTop())\n* exp(TimeDelta() * LogDownwardSpeed)",
+                            "Object.Layer()",
+                            "0"
+                          ]
+                        }
+                      ],
+                      "events": [
+                        {
+                          "type": "BuiltinCommonInstructions::Standard",
+                          "conditions": [
+                            {
+                              "type": {
+                                "value": "CameraY"
+                              },
+                              "parameters": [
+                                "",
+                                ">",
+                                "OldY + DownwardSpeedMax * TimeDelta()",
+                                "Object.Layer()",
+                                "0"
+                              ]
+                            }
+                          ],
+                          "actions": [
+                            {
+                              "type": {
+                                "value": "SetCameraY"
+                              },
+                              "parameters": [
+                                "",
+                                "=",
+                                "OldY + DownwardSpeedMax * TimeDelta()",
+                                "Object.Layer()",
+                                "0"
+                              ]
+                            }
+                          ]
+                        }
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "SmoothCamera::SmoothCamera",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "Delay the camera according to a maximum speed and catch up the delay.",
+              "fullName": "Wait and catch up",
+              "functionType": "Action",
+              "name": "WaitAndCatchUp",
+              "sentence": "Delay the camera of _PARAM0_ during: _PARAM2_ seconds according to the maximum speed _PARAM3_;_PARAM4_ seconds and catch up in _PARAM5_ seconds",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Comment",
+                  "color": {
+                    "b": 109,
+                    "g": 230,
+                    "r": 255,
+                    "textB": 0,
+                    "textG": 0,
+                    "textR": 0
+                  },
+                  "comment": "Maybe the catch-up show be done in constant pixel speed instead of constant time speed."
+                },
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SmoothCamera::SmoothCamera::SetPropertyWaitingEnd"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "=",
+                        "TimeFromStart() + WaitingDuration"
+                      ]
+                    },
+                    {
+                      "type": {
+                        "value": "SmoothCamera::SmoothCamera::SetPropertyWaitingSpeedXMax"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "=",
+                        "WaitingSpeedXMax"
+                      ]
+                    },
+                    {
+                      "type": {
+                        "value": "SmoothCamera::SmoothCamera::SetPropertyWaitingSpeedYMax"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "=",
+                        "WaitingSpeedYMax"
+                      ]
+                    },
+                    {
+                      "type": {
+                        "value": "SmoothCamera::SmoothCamera::SetPropertyCameraDelayCatchUpDuration"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "=",
+                        "CatchUpDuration"
+                      ]
+                    }
+                  ]
+                },
+                {
+                  "disabled": true,
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "DebuggerTools::ConsoleLog"
+                      },
+                      "parameters": [
+                        "\"Wait and catch up\"",
+                        "\"info\"",
+                        "\"SmoothCamera\""
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "SmoothCamera::SmoothCamera",
+                  "type": "behavior"
+                },
+                {
+                  "description": "Waiting duration (in seconds)",
+                  "name": "WaitingDuration",
+                  "type": "expression"
+                },
+                {
+                  "description": "Waiting maximum camera target speed X",
+                  "name": "WaitingSpeedXMax",
+                  "type": "expression"
+                },
+                {
+                  "description": "Waiting maximum camera target speed Y",
+                  "name": "WaitingSpeedYMax",
+                  "type": "expression"
+                },
+                {
+                  "description": "Catch up duration (in seconds)",
+                  "name": "CatchUpDuration",
+                  "type": "expression"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "Draw the targeted and actual camera position.",
+              "fullName": "Draw debug",
+              "functionType": "Action",
+              "name": "DrawDebug",
+              "sentence": "Draw targeted and actual camera position for _PARAM0_ on _PARAM2_",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "PrimitiveDrawing::FillOpacity"
+                      },
+                      "parameters": [
+                        "ShapePainter",
+                        "=",
+                        "0"
+                      ]
+                    }
+                  ]
+                },
+                {
+                  "colorB": 228,
+                  "colorG": 176,
+                  "colorR": 74,
+                  "creationTime": 0,
+                  "name": "Path used by the forecasting",
+                  "source": "",
+                  "type": "BuiltinCommonInstructions::Group",
+                  "events": [
+                    {
+                      "type": "BuiltinCommonInstructions::Standard",
+                      "conditions": [
+                        {
+                          "type": {
+                            "value": "Egal"
+                          },
+                          "parameters": [
+                            "Object.VariableChildCount(__SmoothCamera.ForecastHistoryTime)",
+                            ">",
+                            "0"
+                          ]
+                        }
+                      ],
+                      "actions": [
+                        {
+                          "type": {
+                            "value": "SmoothCamera::SmoothCamera::SetPropertyIndex"
+                          },
+                          "parameters": [
+                            "Object",
+                            "Behavior",
+                            "=",
+                            "0"
+                          ]
+                        },
+                        {
+                          "type": {
+                            "value": "PrimitiveDrawing::OutlineColor"
+                          },
+                          "parameters": [
+                            "ShapePainter",
+                            "\"245;166;35\""
+                          ]
+                        },
+                        {
+                          "type": {
+                            "value": "PrimitiveDrawing::BeginFillPath"
+                          },
+                          "parameters": [
+                            "ShapePainter",
+                            "Object.Variable(__SmoothCamera.ForecastHistoryX[0])",
+                            "Object.Variable(__SmoothCamera.ForecastHistoryY[0])"
+                          ]
+                        }
+                      ],
+                      "events": [
+                        {
+                          "type": "BuiltinCommonInstructions::Repeat",
+                          "repeatExpression": "Object.VariableChildCount(__SmoothCamera.ForecastHistoryX)",
+                          "conditions": [],
+                          "actions": [
+                            {
+                              "type": {
+                                "value": "PrimitiveDrawing::PathLineTo"
+                              },
+                              "parameters": [
+                                "ShapePainter",
+                                "Object.Variable(__SmoothCamera.ForecastHistoryX[Index])",
+                                "Object.Variable(__SmoothCamera.ForecastHistoryY[Index])"
+                              ]
+                            },
+                            {
+                              "type": {
+                                "value": "SmoothCamera::SmoothCamera::SetPropertyIndex"
+                              },
+                              "parameters": [
+                                "Object",
+                                "Behavior",
+                                "+",
+                                "1"
+                              ]
+                            }
+                          ]
+                        },
+                        {
+                          "type": "BuiltinCommonInstructions::Standard",
+                          "conditions": [],
+                          "actions": [
+                            {
+                              "type": {
+                                "value": "PrimitiveDrawing::EndFillPath"
+                              },
+                              "parameters": [
+                                "ShapePainter"
+                              ]
+                            }
+                          ]
+                        }
+                      ]
+                    }
+                  ],
+                  "parameters": []
+                },
+                {
+                  "colorB": 228,
+                  "colorG": 176,
+                  "colorR": 74,
+                  "creationTime": 0,
+                  "name": "Follow-free area.",
+                  "source": "",
+                  "type": "BuiltinCommonInstructions::Group",
+                  "events": [
+                    {
+                      "type": "BuiltinCommonInstructions::Standard",
+                      "conditions": [
+                        {
+                          "type": {
+                            "value": "BuiltinCommonInstructions::Or"
+                          },
+                          "parameters": [],
+                          "subInstructions": [
+                            {
+                              "type": {
+                                "value": "SmoothCamera::SmoothCamera::PropertyFollowFreeAreaLeft"
+                              },
+                              "parameters": [
+                                "Object",
+                                "Behavior",
+                                "!=",
+                                "0"
+                              ]
+                            },
+                            {
+                              "type": {
+                                "value": "SmoothCamera::SmoothCamera::PropertyFollowFreeAreaRight"
+                              },
+                              "parameters": [
+                                "Object",
+                                "Behavior",
+                                "!=",
+                                "0"
+                              ]
+                            },
+                            {
+                              "type": {
+                                "value": "SmoothCamera::SmoothCamera::PropertyFollowFreeAreaTop"
+                              },
+                              "parameters": [
+                                "Object",
+                                "Behavior",
+                                "!=",
+                                "0"
+                              ]
+                            },
+                            {
+                              "type": {
+                                "value": "SmoothCamera::SmoothCamera::PropertyFollowFreeAreaBottom"
+                              },
+                              "parameters": [
+                                "Object",
+                                "Behavior",
+                                "!=",
+                                "0"
+                              ]
+                            }
+                          ]
+                        }
+                      ],
+                      "actions": [
+                        {
+                          "type": {
+                            "value": "PrimitiveDrawing::OutlineColor"
+                          },
+                          "parameters": [
+                            "ShapePainter",
+                            "\"126;211;33\""
+                          ]
+                        },
+                        {
+                          "type": {
+                            "value": "PrimitiveDrawing::Rectangle"
+                          },
+                          "parameters": [
+                            "ShapePainter",
+                            "Object.Behavior::FreeAreaLeft() - 1",
+                            "Object.Behavior::FreeAreaTop() - 1",
+                            "Object.Behavior::FreeAreaRight() + 1",
+                            "Object.Behavior::FreeAreaBottom() + 1"
+                          ]
+                        }
+                      ]
+                    }
+                  ],
+                  "parameters": []
+                },
+                {
+                  "colorB": 228,
+                  "colorG": 176,
+                  "colorR": 74,
+                  "creationTime": 0,
+                  "name": "Linear regression vector used by the forecasting.",
+                  "source": "",
+                  "type": "BuiltinCommonInstructions::Group",
+                  "events": [
+                    {
+                      "type": "BuiltinCommonInstructions::Standard",
+                      "conditions": [],
+                      "actions": [
+                        {
+                          "type": {
+                            "value": "PrimitiveDrawing::OutlineColor"
+                          },
+                          "parameters": [
+                            "ShapePainter",
+                            "\"208;2;27\""
+                          ]
+                        },
+                        {
+                          "type": {
+                            "value": "PrimitiveDrawing::LineV2"
+                          },
+                          "parameters": [
+                            "ShapePainter",
+                            "ProjectedOldestX",
+                            "ProjectedOldestY",
+                            "ProjectedNewestX",
+                            "ProjectedNewestY",
+                            "1"
+                          ]
+                        }
+                      ]
+                    }
+                  ],
+                  "parameters": []
+                },
+                {
+                  "colorB": 228,
+                  "colorG": 176,
+                  "colorR": 74,
+                  "creationTime": 0,
+                  "name": "Targeted and actual camera position",
+                  "source": "",
+                  "type": "BuiltinCommonInstructions::Group",
+                  "events": [
+                    {
+                      "type": "BuiltinCommonInstructions::Standard",
+                      "conditions": [],
+                      "actions": [
+                        {
+                          "type": {
+                            "value": "PrimitiveDrawing::Circle"
+                          },
+                          "parameters": [
+                            "ShapePainter",
+                            "ForecastedX",
+                            "ForecastedY",
+                            "3"
+                          ]
+                        },
+                        {
+                          "type": {
+                            "value": "PrimitiveDrawing::LineV2"
+                          },
+                          "parameters": [
+                            "ShapePainter",
+                            "CameraX(Object.Layer(), 0)",
+                            "CameraY(Object.Layer(), 0) - 4",
+                            "CameraX(Object.Layer(), 0)",
+                            "CameraY(Object.Layer(), 0) + 4",
+                            "1"
+                          ]
+                        },
+                        {
+                          "type": {
+                            "value": "PrimitiveDrawing::LineV2"
+                          },
+                          "parameters": [
+                            "ShapePainter",
+                            "CameraX(Object.Layer(), 0) - 4",
+                            "CameraY(Object.Layer(), 0)",
+                            "CameraX(Object.Layer(), 0) + 4",
+                            "CameraY(Object.Layer(), 0)",
+                            "1"
+                          ]
+                        }
+                      ]
+                    }
+                  ],
+                  "parameters": []
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "SmoothCamera::SmoothCamera",
+                  "type": "behavior"
+                },
+                {
+                  "description": "Shape painter",
+                  "name": "ShapePainter",
+                  "supplementaryInformation": "PrimitiveDrawing::Drawer",
+                  "type": "objectList"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "Enable or disable the following on X axis.",
+              "fullName": "Follow on X",
+              "functionType": "Action",
+              "group": "Camera configuration",
+              "name": "SetFollowOnX",
+              "sentence": "The camera follows _PARAM0_ on X axis: _PARAM2_",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SmoothCamera::SmoothCamera::SetPropertyFollowOnX"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        ""
+                      ]
+                    }
+                  ]
+                },
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [
+                    {
+                      "type": {
+                        "value": "GetArgumentAsBoolean"
+                      },
+                      "parameters": [
+                        "\"FollowOnX\""
+                      ]
+                    }
+                  ],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SmoothCamera::SmoothCamera::SetPropertyFollowOnX"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "yes"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "SmoothCamera::SmoothCamera",
+                  "type": "behavior"
+                },
+                {
+                  "description": "Follow on X axis",
+                  "name": "FollowOnX",
+                  "type": "yesorno"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "Enable or disable the following on Y axis.",
+              "fullName": "Follow on Y",
+              "functionType": "Action",
+              "group": "Camera configuration",
+              "name": "SetFollowOnY",
+              "sentence": "The camera follows _PARAM0_ on Y axis: _PARAM2_",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SmoothCamera::SmoothCamera::SetPropertyFollowOnY"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        ""
+                      ]
+                    }
+                  ]
+                },
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [
+                    {
+                      "type": {
+                        "value": "GetArgumentAsBoolean"
+                      },
+                      "parameters": [
+                        "\"FollowOnY\""
+                      ]
+                    }
+                  ],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SmoothCamera::SmoothCamera::SetPropertyFollowOnY"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "yes"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "SmoothCamera::SmoothCamera",
+                  "type": "behavior"
+                },
+                {
+                  "description": "Follow on Y axis",
+                  "name": "FollowOnY",
+                  "type": "yesorno"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "Change the camera follow free area right border.",
+              "fullName": "Follow free area right border",
+              "functionType": "Action",
+              "group": "Camera configuration",
+              "name": "SetFollowFreeAreaRight",
+              "sentence": "Change the camera follow free area right border of _PARAM0_: _PARAM2_",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SmoothCamera::SmoothCamera::SetPropertyFollowFreeAreaTop"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "=",
+                        "max(0, SetFollowFreeAreaRight)"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "SmoothCamera::SmoothCamera",
+                  "type": "behavior"
+                },
+                {
+                  "description": "Follow free area right border",
+                  "name": "SetFollowFreeAreaRight",
+                  "type": "expression"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "Change the camera follow free area left border.",
+              "fullName": "Follow free area left border",
+              "functionType": "Action",
+              "group": "Camera configuration",
+              "name": "SetFollowFreeAreaLeft",
+              "sentence": "Change the camera follow free area left border of _PARAM0_: _PARAM2_",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SmoothCamera::SmoothCamera::SetPropertyFollowFreeAreaTop"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "=",
+                        "max(0, SetFollowFreeAreaLeft)"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "SmoothCamera::SmoothCamera",
+                  "type": "behavior"
+                },
+                {
+                  "description": "Follow free area left border",
+                  "name": "SetFollowFreeAreaLeft",
+                  "type": "expression"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "Change the camera follow free area top border.",
+              "fullName": "Follow free area top border",
+              "functionType": "Action",
+              "group": "Camera configuration",
+              "name": "SetFollowFreeAreaTop",
+              "sentence": "Change the camera follow free area top border of _PARAM0_: _PARAM2_",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SmoothCamera::SmoothCamera::SetPropertyFollowFreeAreaTop"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "=",
+                        "max(0, FollowFreeAreaTop)"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "SmoothCamera::SmoothCamera",
+                  "type": "behavior"
+                },
+                {
+                  "description": "Follow free area top border",
+                  "name": "FollowFreeAreaTop",
+                  "type": "expression"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "Change the camera follow free area bottom border.",
+              "fullName": "Follow free area bottom border",
+              "functionType": "Action",
+              "group": "Camera configuration",
+              "name": "SetFollowFreeAreaBottom",
+              "sentence": "Change the camera follow free area bottom border of _PARAM0_: _PARAM2_",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SmoothCamera::SmoothCamera::SetPropertyFollowFreeAreaBottom"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "=",
+                        "max(0, SetFollowFreeAreaBottom)"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "SmoothCamera::SmoothCamera",
+                  "type": "behavior"
+                },
+                {
+                  "description": "Follow free area bottom border",
+                  "name": "SetFollowFreeAreaBottom",
+                  "type": "expression"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "Change the camera leftward maximum speed (in pixels per second).",
+              "fullName": "Leftward maximum speed",
+              "functionType": "Action",
+              "group": "Camera configuration",
+              "name": "SetLeftwardSpeedMax",
+              "sentence": "Change the camera leftward maximum speed of _PARAM0_: _PARAM2_",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SmoothCamera::SmoothCamera::SetPropertyLeftwardSpeedMax"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "=",
+                        "max(0, Speed)"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "SmoothCamera::SmoothCamera",
+                  "type": "behavior"
+                },
+                {
+                  "description": "Leftward maximum speed (in ratio per second)",
+                  "name": "Speed",
+                  "type": "expression"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "Change the camera rightward maximum speed (in pixels per second).",
+              "fullName": "Rightward maximum speed",
+              "functionType": "Action",
+              "group": "Camera configuration",
+              "name": "SetRightwardSpeedMax",
+              "sentence": "Change the camera rightward maximum speed of _PARAM0_: _PARAM2_",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SmoothCamera::SmoothCamera::SetPropertyLeftwardSpeedMax"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "=",
+                        "max(0, Speed)"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "SmoothCamera::SmoothCamera",
+                  "type": "behavior"
+                },
+                {
+                  "description": "Rightward maximum speed (in pixels per second)",
+                  "name": "Speed",
+                  "type": "expression"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "Change the camera upward maximum speed (in pixels per second).",
+              "fullName": "Upward maximum speed",
+              "functionType": "Action",
+              "group": "Camera configuration",
+              "name": "SetUpwardSpeedMax",
+              "sentence": "Change the camera upward maximum speed of _PARAM0_: _PARAM2_",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SmoothCamera::SmoothCamera::SetPropertyUpwardSpeedMax"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "=",
+                        "max(0, Speed)"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "SmoothCamera::SmoothCamera",
+                  "type": "behavior"
+                },
+                {
+                  "description": "Upward maximum speed (in pixels per second)",
+                  "name": "Speed",
+                  "type": "expression"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "Change the camera downward maximum speed (in pixels per second).",
+              "fullName": "Downward maximum speed",
+              "functionType": "Action",
+              "group": "Camera configuration",
+              "name": "SetDownwardSpeedMax",
+              "sentence": "Change the camera downward maximum speed of _PARAM0_: _PARAM2_",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SmoothCamera::SmoothCamera::SetPropertyDownwardSpeedMax"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "=",
+                        "max(0, Speed)"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "SmoothCamera::SmoothCamera",
+                  "type": "behavior"
+                },
+                {
+                  "description": "Downward maximum speed (in pixels per second)",
+                  "name": "Speed",
+                  "type": "expression"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "Change the camera leftward catch-up speed (in ratio per second).",
+              "fullName": "Leftward catch-up speed",
+              "functionType": "Action",
+              "group": "Camera configuration",
+              "name": "SetLeftwardSpeed",
+              "sentence": "Change the camera leftward catch-up speed of _PARAM0_: _PARAM2_",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SmoothCamera::SmoothCamera::SetPropertyLeftwardSpeed"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "=",
+                        "clamp(0, 1, LeftwardSpeed)"
+                      ]
+                    },
+                    {
+                      "type": {
+                        "value": "SmoothCamera::SmoothCamera::SetPropertyLogLeftwardSpeed"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "=",
+                        "log(1 - LeftwardSpeed)"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "SmoothCamera::SmoothCamera",
+                  "type": "behavior"
+                },
+                {
+                  "description": "Leftward catch-up speed (in ratio per second)",
+                  "name": "LeftwardSpeed",
+                  "type": "expression"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "Change the camera rightward catch-up speed (in ratio per second).",
+              "fullName": "Rightward catch-up speed",
+              "functionType": "Action",
+              "group": "Camera configuration",
+              "name": "SetRightwardSpeed",
+              "sentence": "Change the camera rightward catch-up speed of _PARAM0_: _PARAM2_",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SmoothCamera::SmoothCamera::SetPropertyRightwardSpeed"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "=",
+                        "clamp(0, 1, RightwardSpeed)"
+                      ]
+                    },
+                    {
+                      "type": {
+                        "value": "SmoothCamera::SmoothCamera::SetPropertyLogRightwardSpeed"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "=",
+                        "log(1 - RightwardSpeed)"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "SmoothCamera::SmoothCamera",
+                  "type": "behavior"
+                },
+                {
+                  "description": "Rightward catch-up speed (in ratio per second)",
+                  "name": "RightwardSpeed",
+                  "type": "expression"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "Change the camera downward catch-up speed (in ratio per second).",
+              "fullName": "Downward catch-up speed",
+              "functionType": "Action",
+              "group": "Camera configuration",
+              "name": "SetDownwardSpeed",
+              "sentence": "Change the camera downward catch-up speed of _PARAM0_: _PARAM2_",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SmoothCamera::SmoothCamera::SetPropertyDownwardSpeed"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "=",
+                        "clamp(0, 1, DownwardSpeed)"
+                      ]
+                    },
+                    {
+                      "type": {
+                        "value": "SmoothCamera::SmoothCamera::SetPropertyLogDownwardSpeed"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "=",
+                        "log(1 - DownwardSpeed)"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "SmoothCamera::SmoothCamera",
+                  "type": "behavior"
+                },
+                {
+                  "description": "Downward catch-up speed (in ratio per second)",
+                  "name": "DownwardSpeed",
+                  "type": "expression"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "Change the camera upward catch-up speed (in ratio per second).",
+              "fullName": "Upward catch-up speed",
+              "functionType": "Action",
+              "group": "Camera configuration",
+              "name": "SetUpwardSpeed",
+              "sentence": "Change the camera upward catch-up speed of _PARAM0_: _PARAM2_",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SmoothCamera::SmoothCamera::SetPropertyUpwardSpeed"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "=",
+                        "clamp(0, 1, UpwardSpeed)"
+                      ]
+                    },
+                    {
+                      "type": {
+                        "value": "SmoothCamera::SmoothCamera::SetPropertyLogUpwardSpeed"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "=",
+                        "log(1 - UpwardSpeed)"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "SmoothCamera::SmoothCamera",
+                  "type": "behavior"
+                },
+                {
+                  "description": "Upward catch-up speed (in ratio per second)",
+                  "name": "UpwardSpeed",
+                  "type": "expression"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "the camera offset on X axis of the object. This is not the current difference between the object and the camera position.",
+              "fullName": "Camera offset X",
+              "functionType": "ExpressionAndCondition",
+              "group": "Camera configuration",
+              "name": "OffsetX",
+              "sentence": "the camera offset on X axis",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SetReturnNumber"
+                      },
+                      "parameters": [
+                        "CameraOffsetX"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "expressionType": {
+                "type": "expression"
+              },
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "SmoothCamera::SmoothCamera",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "fullName": "",
+              "functionType": "ActionWithOperator",
+              "getterName": "OffsetX",
+              "name": "SetOffsetXOp",
+              "sentence": "",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SmoothCamera::SmoothCamera::SetPropertyCameraOffsetX"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "=",
+                        "Value"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "SmoothCamera::SmoothCamera",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "Change the camera offset on X axis of an object.",
+              "fullName": "Camera Offset X",
+              "functionType": "Action",
+              "group": "Camera configuration",
+              "name": "SetOffsetX",
+              "private": true,
+              "sentence": "Change the camera offset on X axis of _PARAM0_: _PARAM2_",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Comment",
+                  "color": {
+                    "b": 109,
+                    "g": 230,
+                    "r": 255,
+                    "textB": 0,
+                    "textG": 0,
+                    "textR": 0
+                  },
+                  "comment": "Deprecated use SetOffsetXOp instead."
+                },
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SmoothCamera::SmoothCamera::SetOffsetXOp"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "=",
+                        "CameraOffsetX",
+                        ""
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "SmoothCamera::SmoothCamera",
+                  "type": "behavior"
+                },
+                {
+                  "description": "Camera offset X",
+                  "name": "CameraOffsetX",
+                  "type": "expression"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "the camera offset on Y axis of the object. This is not the current difference between the object and the camera position.",
+              "fullName": "Camera offset Y",
+              "functionType": "ExpressionAndCondition",
+              "group": "Camera configuration",
+              "name": "OffsetY",
+              "sentence": "the camera offset on Y axis",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SetReturnNumber"
+                      },
+                      "parameters": [
+                        "CameraOffsetY"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "expressionType": {
+                "type": "expression"
+              },
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "SmoothCamera::SmoothCamera",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "fullName": "",
+              "functionType": "ActionWithOperator",
+              "getterName": "OffsetY",
+              "name": "SetOffsetYOp",
+              "sentence": "",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Comment",
+                  "color": {
+                    "b": 109,
+                    "g": 230,
+                    "r": 255,
+                    "textB": 0,
+                    "textG": 0,
+                    "textR": 0
+                  },
+                  "comment": "Deprecated use SetOffsetYOp instead."
+                },
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SmoothCamera::SmoothCamera::SetPropertyCameraOffsetY"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "=",
+                        "Value"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "SmoothCamera::SmoothCamera",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "Change the camera offset on Y axis of an object.",
+              "fullName": "Camera Offset Y",
+              "functionType": "Action",
+              "group": "Camera configuration",
+              "name": "SetOffsetY",
+              "private": true,
+              "sentence": "Change the camera offset on Y axis of _PARAM0_: _PARAM2_",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SmoothCamera::SmoothCamera::SetOffsetYOp"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "=",
+                        "CameraOffsetY",
+                        ""
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "SmoothCamera::SmoothCamera",
+                  "type": "behavior"
+                },
+                {
+                  "description": "Camera offset Y",
+                  "name": "CameraOffsetY",
+                  "type": "expression"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "Change the camera forecast time (in seconds).",
+              "fullName": "Forecast time",
+              "functionType": "Action",
+              "group": "Camera configuration",
+              "name": "SetForecastTime",
+              "sentence": "Change the camera forecast time of _PARAM0_: _PARAM2_",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SmoothCamera::SmoothCamera::SetPropertyForecastTime"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "=",
+                        "min(0, ForecastTime)"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "SmoothCamera::SmoothCamera",
+                  "type": "behavior"
+                },
+                {
+                  "description": "Forecast time",
+                  "name": "ForecastTime",
+                  "type": "expression"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "Change the camera delay (in seconds).",
+              "fullName": "Camera delay",
+              "functionType": "Action",
+              "group": "Camera configuration",
+              "name": "SetCameraDelay",
+              "sentence": "Change the camera delay of _PARAM0_: _PARAM2_",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SmoothCamera::SmoothCamera::SetPropertyCameraDelay"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "=",
+                        "min(0, CameraDelay)"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "SmoothCamera::SmoothCamera",
+                  "type": "behavior"
+                },
+                {
+                  "description": "Camera delay",
+                  "name": "CameraDelay",
+                  "type": "expression"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "Return follow free area left border X.",
+              "fullName": "Free area left",
+              "functionType": "Expression",
+              "group": "Private",
+              "name": "FreeAreaLeft",
+              "private": true,
+              "sentence": "",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SetReturnNumber"
+                      },
+                      "parameters": [
+                        "ForecastedX + CameraOffsetX - FollowFreeAreaLeft"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "expressionType": {
+                "type": "expression"
+              },
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "SmoothCamera::SmoothCamera",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "Return follow free area right border X.",
+              "fullName": "Free area right",
+              "functionType": "Expression",
+              "group": "Private",
+              "name": "FreeAreaRight",
+              "private": true,
+              "sentence": "",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SetReturnNumber"
+                      },
+                      "parameters": [
+                        "ForecastedX + CameraOffsetX + FollowFreeAreaRight"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "expressionType": {
+                "type": "expression"
+              },
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "SmoothCamera::SmoothCamera",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "Return follow free area bottom border Y.",
+              "fullName": "Free area bottom",
+              "functionType": "Expression",
+              "group": "Private",
+              "name": "FreeAreaBottom",
+              "private": true,
+              "sentence": "",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SetReturnNumber"
+                      },
+                      "parameters": [
+                        "ForecastedY + CameraOffsetY + FollowFreeAreaBottom"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "expressionType": {
+                "type": "expression"
+              },
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "SmoothCamera::SmoothCamera",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "Return follow free area top border Y.",
+              "fullName": "Free area top",
+              "functionType": "Expression",
+              "group": "Private",
+              "name": "FreeAreaTop",
+              "private": true,
+              "sentence": "",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SetReturnNumber"
+                      },
+                      "parameters": [
+                        "ForecastedY + CameraOffsetY - FollowFreeAreaTop"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "expressionType": {
+                "type": "expression"
+              },
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "SmoothCamera::SmoothCamera",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "Update delayed position and delayed history. This is called in doStepPreEvents.",
+              "fullName": "Update delayed position",
+              "functionType": "Action",
+              "group": "Private",
+              "name": "UpdateDelayedPosition",
+              "private": true,
+              "sentence": "Update delayed position and delayed history of _PARAM0_",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Comment",
+                  "color": {
+                    "b": 109,
+                    "g": 230,
+                    "r": 255,
+                    "textB": 0,
+                    "textG": 0,
+                    "textR": 0
+                  },
+                  "comment": "Add the previous position to have enough (2) positions to evaluate the extra delay for waiting mode."
+                },
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [
+                    {
+                      "type": {
+                        "value": "SmoothCamera::SmoothCamera::IsWaiting"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        ""
+                      ]
+                    },
+                    {
+                      "type": {
+                        "value": "Egal"
+                      },
+                      "parameters": [
+                        "Object.VariableChildCount(__SmoothCamera.ObjectTime)",
+                        "=",
+                        "0"
+                      ]
+                    }
+                  ],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "ObjectVariablePushNumber"
+                      },
+                      "parameters": [
+                        "Object",
+                        "__SmoothCamera.ObjectTime",
+                        "TimeFromStart()"
+                      ]
+                    },
+                    {
+                      "type": {
+                        "value": "ObjectVariablePushNumber"
+                      },
+                      "parameters": [
+                        "Object",
+                        "__SmoothCamera.ObjectX",
+                        "DelayedCenterX"
+                      ]
+                    },
+                    {
+                      "type": {
+                        "value": "ObjectVariablePushNumber"
+                      },
+                      "parameters": [
+                        "Object",
+                        "__SmoothCamera.ObjectY",
+                        "DelayedCenterY"
+                      ]
+                    }
+                  ]
+                },
+                {
+                  "type": "BuiltinCommonInstructions::Comment",
+                  "color": {
+                    "b": 109,
+                    "g": 230,
+                    "r": 255,
+                    "textB": 0,
+                    "textG": 0,
+                    "textR": 0
+                  },
+                  "comment": "Use the object center when no delay is asked."
+                },
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SmoothCamera::SmoothCamera::SetPropertyDelayedCenterX"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "=",
+                        "Object.CenterX()"
+                      ]
+                    },
+                    {
+                      "type": {
+                        "value": "SmoothCamera::SmoothCamera::SetPropertyDelayedCenterY"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "=",
+                        "Object.CenterY()"
+                      ]
+                    }
+                  ]
+                },
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [
+                    {
+                      "type": {
+                        "inverted": true,
+                        "value": "SmoothCamera::SmoothCamera::IsDelayed"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        ""
+                      ]
+                    }
+                  ],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SmoothCamera::SmoothCamera::AddForecastHistoryPosition"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "TimeFromStart()",
+                        "Object.CenterX()",
+                        "Object.CenterY()",
+                        ""
+                      ]
+                    }
+                  ]
+                },
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [
+                    {
+                      "type": {
+                        "value": "BuiltinCommonInstructions::Or"
+                      },
+                      "parameters": [],
+                      "subInstructions": [
+                        {
+                          "type": {
+                            "value": "SmoothCamera::SmoothCamera::IsDelayed"
+                          },
+                          "parameters": [
+                            "Object",
+                            "Behavior",
+                            ""
+                          ]
+                        },
+                        {
+                          "type": {
+                            "value": "SmoothCamera::SmoothCamera::IsWaiting"
+                          },
+                          "parameters": [
+                            "Object",
+                            "Behavior",
+                            ""
+                          ]
+                        }
+                      ]
+                    }
+                  ],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "ObjectVariablePushNumber"
+                      },
+                      "parameters": [
+                        "Object",
+                        "__SmoothCamera.ObjectTime",
+                        "TimeFromStart()"
+                      ]
+                    },
+                    {
+                      "type": {
+                        "value": "ObjectVariablePushNumber"
+                      },
+                      "parameters": [
+                        "Object",
+                        "__SmoothCamera.ObjectX",
+                        "Object.CenterX()"
+                      ]
+                    },
+                    {
+                      "type": {
+                        "value": "ObjectVariablePushNumber"
+                      },
+                      "parameters": [
+                        "Object",
+                        "__SmoothCamera.ObjectY",
+                        "Object.CenterY()"
+                      ]
+                    }
+                  ],
+                  "events": [
+                    {
+                      "type": "BuiltinCommonInstructions::Comment",
+                      "color": {
+                        "b": 109,
+                        "g": 230,
+                        "r": 255,
+                        "textB": 0,
+                        "textG": 0,
+                        "textR": 0
+                      },
+                      "comment": "Remove history entries that are too old to be useful for delaying and pass it to the history for forecasting."
+                    },
+                    {
+                      "infiniteLoopWarning": true,
+                      "type": "BuiltinCommonInstructions::While",
+                      "whileConditions": [
+                        {
+                          "type": {
+                            "value": "Egal"
+                          },
+                          "parameters": [
+                            "Object.VariableChildCount(__SmoothCamera.ObjectTime)",
+                            ">=",
+                            "2"
+                          ]
+                        },
+                        {
+                          "type": {
+                            "value": "VarObjet"
+                          },
+                          "parameters": [
+                            "Object",
+                            "__SmoothCamera.ObjectTime[1]",
+                            "<",
+                            "TimeFromStart() - Object.Behavior::CurrentDelay()"
+                          ]
+                        }
+                      ],
+                      "conditions": [],
+                      "actions": [
+                        {
+                          "type": {
+                            "value": "SmoothCamera::SmoothCamera::AddForecastHistoryPosition"
+                          },
+                          "parameters": [
+                            "Object",
+                            "Behavior",
+                            "Object.Variable(__SmoothCamera.ObjectTime[0])",
+                            "Object.Variable(__SmoothCamera.ObjectX[0])",
+                            "Object.Variable(__SmoothCamera.ObjectY[0])",
+                            ""
+                          ]
+                        },
+                        {
+                          "type": {
+                            "value": "ObjectVariableRemoveAt"
+                          },
+                          "parameters": [
+                            "Object",
+                            "__SmoothCamera.ObjectTime",
+                            "0"
+                          ]
+                        },
+                        {
+                          "type": {
+                            "value": "ObjectVariableRemoveAt"
+                          },
+                          "parameters": [
+                            "Object",
+                            "__SmoothCamera.ObjectX",
+                            "0"
+                          ]
+                        },
+                        {
+                          "type": {
+                            "value": "ObjectVariableRemoveAt"
+                          },
+                          "parameters": [
+                            "Object",
+                            "__SmoothCamera.ObjectY",
+                            "0"
+                          ]
+                        }
+                      ]
+                    },
+                    {
+                      "type": "BuiltinCommonInstructions::Comment",
+                      "color": {
+                        "b": 109,
+                        "g": 230,
+                        "r": 255,
+                        "textB": 0,
+                        "textG": 0,
+                        "textR": 0
+                      },
+                      "comment": "Don't move the camera if there is not enough history."
+                    },
+                    {
+                      "type": "BuiltinCommonInstructions::Standard",
+                      "conditions": [],
+                      "actions": [
+                        {
+                          "type": {
+                            "value": "SmoothCamera::SmoothCamera::SetPropertyDelayedCenterX"
+                          },
+                          "parameters": [
+                            "Object",
+                            "Behavior",
+                            "=",
+                            "Object.Variable(__SmoothCamera.ObjectX[0])"
+                          ]
+                        },
+                        {
+                          "type": {
+                            "value": "SmoothCamera::SmoothCamera::SetPropertyDelayedCenterY"
+                          },
+                          "parameters": [
+                            "Object",
+                            "Behavior",
+                            "=",
+                            "Object.Variable(__SmoothCamera.ObjectY[0])"
+                          ]
+                        }
+                      ]
+                    },
+                    {
+                      "type": "BuiltinCommonInstructions::Standard",
+                      "conditions": [
+                        {
+                          "type": {
+                            "value": "Egal"
+                          },
+                          "parameters": [
+                            "Object.VariableChildCount(__SmoothCamera.ObjectTime)",
+                            ">=",
+                            "2"
+                          ]
+                        },
+                        {
+                          "type": {
+                            "value": "VarObjet"
+                          },
+                          "parameters": [
+                            "Object",
+                            "__SmoothCamera.ObjectTime[0]",
+                            "<",
+                            "TimeFromStart() - Object.Behavior::CurrentDelay()"
+                          ]
+                        }
+                      ],
+                      "actions": [],
+                      "events": [
+                        {
+                          "type": "BuiltinCommonInstructions::Comment",
+                          "color": {
+                            "b": 109,
+                            "g": 230,
+                            "r": 255,
+                            "textB": 0,
+                            "textG": 0,
+                            "textR": 0
+                          },
+                          "comment": "Add the extra delay that could be needed to respect the speed limit in waiting mode.\n\nspeedRatio = min(speedMaxX / historySpeedX, speedMaxY / historySpeedY)\ndelay += min(0, timeDelta * (1 - speedRatio))"
+                        },
+                        {
+                          "type": "BuiltinCommonInstructions::Standard",
+                          "conditions": [
+                            {
+                              "type": {
+                                "value": "SmoothCamera::SmoothCamera::IsWaiting"
+                              },
+                              "parameters": [
+                                "Object",
+                                "Behavior",
+                                ""
+                              ]
+                            }
+                          ],
+                          "actions": [
+                            {
+                              "type": {
+                                "value": "SmoothCamera::SmoothCamera::SetPropertyCameraExtraDelay"
+                              },
+                              "parameters": [
+                                "Object",
+                                "Behavior",
+                                "+",
+                                "max(0, TimeDelta() * (1 - min(WaitingSpeedXMax * abs(Object.Variable(__SmoothCamera.ObjectX[1]) - Object.Variable(__SmoothCamera.ObjectX[0])), WaitingSpeedYMax * abs(Object.Variable(__SmoothCamera.ObjectY[1]) - Object.Variable(__SmoothCamera.ObjectY[0]))) / (Object.Variable(__SmoothCamera.ObjectTime[1]) - Object.Variable(__SmoothCamera.ObjectTime[0]))))"
+                              ]
+                            }
+                          ],
+                          "events": [
+                            {
+                              "disabled": true,
+                              "type": "BuiltinCommonInstructions::Standard",
+                              "conditions": [],
+                              "actions": [
+                                {
+                                  "type": {
+                                    "value": "DebuggerTools::ConsoleLog"
+                                  },
+                                  "parameters": [
+                                    "\"Extra delay: \" + ToString(CameraExtraDelay)",
+                                    "\"info\"",
+                                    "\"SmoothCamera\""
+                                  ]
+                                }
+                              ]
+                            }
+                          ]
+                        },
+                        {
+                          "type": "BuiltinCommonInstructions::Comment",
+                          "color": {
+                            "b": 109,
+                            "g": 230,
+                            "r": 255,
+                            "textB": 0,
+                            "textG": 0,
+                            "textR": 0
+                          },
+                          "comment": "The time with delay is now between the first 2 indexes"
+                        },
+                        {
+                          "type": "BuiltinCommonInstructions::Standard",
+                          "conditions": [],
+                          "actions": [
+                            {
+                              "type": {
+                                "value": "SmoothCamera::SmoothCamera::SetPropertyDelayedCenterX"
+                              },
+                              "parameters": [
+                                "Object",
+                                "Behavior",
+                                "=",
+                                "lerp(Object.Variable(__SmoothCamera.ObjectX[1]), Object.Variable(__SmoothCamera.ObjectX[0]), ((TimeFromStart() - Object.Behavior::CurrentDelay()) - Object.Variable(__SmoothCamera.ObjectTime[1])) / (Object.Variable(__SmoothCamera.ObjectTime[0]) - Object.Variable(__SmoothCamera.ObjectTime[1])))"
+                              ]
+                            },
+                            {
+                              "type": {
+                                "value": "SmoothCamera::SmoothCamera::SetPropertyDelayedCenterY"
+                              },
+                              "parameters": [
+                                "Object",
+                                "Behavior",
+                                "=",
+                                "lerp(Object.Variable(__SmoothCamera.ObjectY[1]), Object.Variable(__SmoothCamera.ObjectY[0]), ((TimeFromStart() - Object.Behavior::CurrentDelay()) - Object.Variable(__SmoothCamera.ObjectTime[1])) / (Object.Variable(__SmoothCamera.ObjectTime[0]) - Object.Variable(__SmoothCamera.ObjectTime[1])))"
+                              ]
+                            }
+                          ]
+                        }
+                      ]
+                    }
+                  ]
+                },
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [
+                    {
+                      "type": {
+                        "inverted": true,
+                        "value": "SmoothCamera::SmoothCamera::IsDelayed"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        ""
+                      ]
+                    },
+                    {
+                      "type": {
+                        "inverted": true,
+                        "value": "SmoothCamera::SmoothCamera::IsWaiting"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        ""
+                      ]
+                    }
+                  ],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "ObjectVariableClearChildren"
+                      },
+                      "parameters": [
+                        "Object",
+                        "__SmoothCamera.ObjectTime"
+                      ]
+                    },
+                    {
+                      "type": {
+                        "value": "ObjectVariableClearChildren"
+                      },
+                      "parameters": [
+                        "Object",
+                        "__SmoothCamera.ObjectX"
+                      ]
+                    },
+                    {
+                      "type": {
+                        "value": "ObjectVariableClearChildren"
+                      },
+                      "parameters": [
+                        "Object",
+                        "__SmoothCamera.ObjectY"
+                      ]
+                    }
+                  ]
+                },
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [
+                    {
+                      "type": {
+                        "inverted": true,
+                        "value": "SmoothCamera::SmoothCamera::IsWaiting"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        ""
+                      ]
+                    },
+                    {
+                      "type": {
+                        "value": "BuiltinCommonInstructions::Once"
+                      },
+                      "parameters": []
+                    }
+                  ],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SmoothCamera::SmoothCamera::SetPropertyCameraDelayCatchUpSpeed"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "=",
+                        "CameraExtraDelay / CameraDelayCatchUpDuration"
+                      ]
+                    }
+                  ],
+                  "events": [
+                    {
+                      "disabled": true,
+                      "type": "BuiltinCommonInstructions::Standard",
+                      "conditions": [],
+                      "actions": [
+                        {
+                          "type": {
+                            "value": "DebuggerTools::ConsoleLog"
+                          },
+                          "parameters": [
+                            "\"Start to catch up\"",
+                            "\"info\"",
+                            "\"SmoothCamera\""
+                          ]
+                        }
+                      ]
+                    }
+                  ]
+                },
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [
+                    {
+                      "type": {
+                        "inverted": true,
+                        "value": "SmoothCamera::SmoothCamera::IsWaiting"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        ""
+                      ]
+                    },
+                    {
+                      "type": {
+                        "value": "SmoothCamera::SmoothCamera::PropertyCameraExtraDelay"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        ">",
+                        "0"
+                      ]
+                    }
+                  ],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SmoothCamera::SmoothCamera::SetPropertyCameraExtraDelay"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "=",
+                        "max(0, CameraExtraDelay -CameraDelayCatchUpSpeed * TimeDelta())"
+                      ]
+                    }
+                  ],
+                  "events": [
+                    {
+                      "disabled": true,
+                      "type": "BuiltinCommonInstructions::Standard",
+                      "conditions": [],
+                      "actions": [
+                        {
+                          "type": {
+                            "value": "DebuggerTools::ConsoleLog"
+                          },
+                          "parameters": [
+                            "\"Catching up delay: \" + ToString(CameraExtraDelay)",
+                            "\"info\"",
+                            "\"SmoothCamera\""
+                          ]
+                        }
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "SmoothCamera::SmoothCamera",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "Check if the camera following target is delayed from the object.",
+              "fullName": "Camera is delayed",
+              "functionType": "Condition",
+              "name": "IsDelayed",
+              "private": true,
+              "sentence": "The camera of _PARAM0_ is delayed",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [
+                    {
+                      "type": {
+                        "value": "Egal"
+                      },
+                      "parameters": [
+                        "Object.Behavior::CurrentDelay()",
+                        ">",
+                        "0"
+                      ]
+                    }
+                  ],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SetReturnBoolean"
+                      },
+                      "parameters": [
+                        "True"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "SmoothCamera::SmoothCamera",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "Return the current camera delay.",
+              "fullName": "Current delay",
+              "functionType": "Expression",
+              "name": "CurrentDelay",
+              "private": true,
+              "sentence": "",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SetReturnNumber"
+                      },
+                      "parameters": [
+                        "CameraDelay + CameraExtraDelay"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "expressionType": {
+                "type": "expression"
+              },
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "SmoothCamera::SmoothCamera",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "Check if the camera following is waiting at a reduced speed.",
+              "fullName": "Camera is waiting",
+              "functionType": "Condition",
+              "name": "IsWaiting",
+              "private": true,
+              "sentence": "The camera of _PARAM0_ is waiting",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [
+                    {
+                      "type": {
+                        "value": "SmoothCamera::SmoothCamera::PropertyWaitingEnd"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        ">",
+                        "TimeFromStart()"
+                      ]
+                    }
+                  ],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SetReturnBoolean"
+                      },
+                      "parameters": [
+                        "True"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "SmoothCamera::SmoothCamera",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "Add a position to the history for forecasting. This is called 2 times in UpadteDelayedPosition.",
+              "fullName": "Add forecast history position",
+              "functionType": "Action",
+              "group": "Private",
+              "name": "AddForecastHistoryPosition",
+              "private": true,
+              "sentence": "Add the time:_PARAM2_ and position: _PARAM3_; _PARAM4_ to the forecast history of _PARAM0_",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [
+                    {
+                      "type": {
+                        "value": "BuiltinCommonInstructions::Or"
+                      },
+                      "parameters": [],
+                      "subInstructions": [
+                        {
+                          "type": {
+                            "value": "SmoothCamera::SmoothCamera::PropertyForecastHistoryDuration"
+                          },
+                          "parameters": [
+                            "Object",
+                            "Behavior",
+                            ">",
+                            "0"
+                          ]
+                        },
+                        {
+                          "type": {
+                            "value": "SmoothCamera::SmoothCamera::PropertyForecastTime"
+                          },
+                          "parameters": [
+                            "Object",
+                            "Behavior",
+                            ">",
+                            "0"
+                          ]
+                        }
+                      ]
+                    }
+                  ],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "ObjectVariablePushNumber"
+                      },
+                      "parameters": [
+                        "Object",
+                        "__SmoothCamera.ForecastHistoryTime",
+                        "Time"
+                      ]
+                    },
+                    {
+                      "type": {
+                        "value": "ObjectVariablePushNumber"
+                      },
+                      "parameters": [
+                        "Object",
+                        "__SmoothCamera.ForecastHistoryX",
+                        "ObjectX"
+                      ]
+                    },
+                    {
+                      "type": {
+                        "value": "ObjectVariablePushNumber"
+                      },
+                      "parameters": [
+                        "Object",
+                        "__SmoothCamera.ForecastHistoryY",
+                        "ObjectY"
+                      ]
+                    }
+                  ],
+                  "events": [
+                    {
+                      "type": "BuiltinCommonInstructions::Comment",
+                      "color": {
+                        "b": 109,
+                        "g": 230,
+                        "r": 255,
+                        "textB": 0,
+                        "textG": 0,
+                        "textR": 0
+                      },
+                      "comment": "Remove history entries that are too old to be useful.\nKeep at least 2 positions because no forecast can be done with less positions."
+                    },
+                    {
+                      "infiniteLoopWarning": true,
+                      "type": "BuiltinCommonInstructions::While",
+                      "whileConditions": [
+                        {
+                          "type": {
+                            "value": "Egal"
+                          },
+                          "parameters": [
+                            "Object.VariableChildCount(__SmoothCamera.ForecastHistoryTime)",
+                            ">=",
+                            "3"
+                          ]
+                        },
+                        {
+                          "type": {
+                            "value": "VarObjet"
+                          },
+                          "parameters": [
+                            "Object",
+                            "__SmoothCamera.ForecastHistoryTime[0]",
+                            "<",
+                            "TimeFromStart() - CameraDelay - CameraExtraDelay - ForecastHistoryDuration"
+                          ]
+                        }
+                      ],
+                      "conditions": [],
+                      "actions": [
+                        {
+                          "type": {
+                            "value": "ObjectVariableRemoveAt"
+                          },
+                          "parameters": [
+                            "Object",
+                            "__SmoothCamera.ForecastHistoryTime",
+                            "0"
+                          ]
+                        },
+                        {
+                          "type": {
+                            "value": "ObjectVariableRemoveAt"
+                          },
+                          "parameters": [
+                            "Object",
+                            "__SmoothCamera.ForecastHistoryX",
+                            "0"
+                          ]
+                        },
+                        {
+                          "type": {
+                            "value": "ObjectVariableRemoveAt"
+                          },
+                          "parameters": [
+                            "Object",
+                            "__SmoothCamera.ForecastHistoryY",
+                            "0"
+                          ]
+                        }
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "SmoothCamera::SmoothCamera",
+                  "type": "behavior"
+                },
+                {
+                  "description": "Time",
+                  "name": "Time",
+                  "type": "expression"
+                },
+                {
+                  "description": "Object X",
+                  "name": "ObjectX",
+                  "type": "expression"
+                },
+                {
+                  "description": "Object Y",
+                  "name": "ObjectY",
+                  "type": "expression"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "Update forecasted position. This is called in doStepPreEvents.",
+              "fullName": "Update forecasted position",
+              "functionType": "Action",
+              "group": "Private",
+              "name": "UpdateForecastedPosition",
+              "private": true,
+              "sentence": "Update forecasted position of _PARAM0_",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SmoothCamera::SmoothCamera::SetPropertyForecastedX"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "=",
+                        "DelayedCenterX"
+                      ]
+                    },
+                    {
+                      "type": {
+                        "value": "SmoothCamera::SmoothCamera::SetPropertyForecastedY"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "=",
+                        "DelayedCenterY"
+                      ]
+                    }
+                  ]
+                },
+                {
+                  "type": "BuiltinCommonInstructions::Comment",
+                  "color": {
+                    "b": 109,
+                    "g": 230,
+                    "r": 255,
+                    "textB": 0,
+                    "textG": 0,
+                    "textR": 0
+                  },
+                  "comment": "Simple linear regression\ny = A * x + B\n\nA = Covariance / VarianceX\nB = MeanY - A * MeanX\n\nNote than we could use only one position every N positions to reduce the process time,\nbut if we really need efficient process JavaScript and circular queues are a must."
+                },
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [
+                    {
+                      "type": {
+                        "value": "Egal"
+                      },
+                      "parameters": [
+                        "Object.VariableChildCount(__SmoothCamera.ForecastHistoryTime)",
+                        ">=",
+                        "2"
+                      ]
+                    },
+                    {
+                      "type": {
+                        "value": "BuiltinCommonInstructions::Or"
+                      },
+                      "parameters": [],
+                      "subInstructions": [
+                        {
+                          "type": {
+                            "value": "SmoothCamera::SmoothCamera::PropertyForecastHistoryDuration"
+                          },
+                          "parameters": [
+                            "Object",
+                            "Behavior",
+                            ">",
+                            "0"
+                          ]
+                        },
+                        {
+                          "type": {
+                            "value": "SmoothCamera::SmoothCamera::PropertyForecastTime"
+                          },
+                          "parameters": [
+                            "Object",
+                            "Behavior",
+                            ">",
+                            "0"
+                          ]
+                        }
+                      ]
+                    }
+                  ],
+                  "actions": [],
+                  "events": [
+                    {
+                      "colorB": 228,
+                      "colorG": 176,
+                      "colorR": 74,
+                      "creationTime": 0,
+                      "name": "Mean X",
+                      "source": "",
+                      "type": "BuiltinCommonInstructions::Group",
+                      "events": [
+                        {
+                          "type": "BuiltinCommonInstructions::Standard",
+                          "conditions": [],
+                          "actions": [
+                            {
+                              "type": {
+                                "value": "SmoothCamera::SmoothCamera::SetPropertyForecastHistoryMeanX"
+                              },
+                              "parameters": [
+                                "Object",
+                                "Behavior",
+                                "=",
+                                "0"
+                              ]
+                            },
+                            {
+                              "type": {
+                                "value": "SmoothCamera::SmoothCamera::SetPropertyIndex"
+                              },
+                              "parameters": [
+                                "Object",
+                                "Behavior",
+                                "=",
+                                "0"
+                              ]
+                            }
+                          ]
+                        },
+                        {
+                          "type": "BuiltinCommonInstructions::Repeat",
+                          "repeatExpression": "Object.VariableChildCount(__SmoothCamera.ForecastHistoryX)",
+                          "conditions": [],
+                          "actions": [
+                            {
+                              "type": {
+                                "value": "SmoothCamera::SmoothCamera::SetPropertyForecastHistoryMeanX"
+                              },
+                              "parameters": [
+                                "Object",
+                                "Behavior",
+                                "+",
+                                "Object.Variable(__SmoothCamera.ForecastHistoryX[Index])"
+                              ]
+                            },
+                            {
+                              "type": {
+                                "value": "SmoothCamera::SmoothCamera::SetPropertyIndex"
+                              },
+                              "parameters": [
+                                "Object",
+                                "Behavior",
+                                "+",
+                                "1"
+                              ]
+                            }
+                          ]
+                        },
+                        {
+                          "type": "BuiltinCommonInstructions::Standard",
+                          "conditions": [],
+                          "actions": [
+                            {
+                              "type": {
+                                "value": "SmoothCamera::SmoothCamera::SetPropertyForecastHistoryMeanX"
+                              },
+                              "parameters": [
+                                "Object",
+                                "Behavior",
+                                "/",
+                                "Object.VariableChildCount(__SmoothCamera.ForecastHistoryX)"
+                              ]
+                            }
+                          ]
+                        }
+                      ],
+                      "parameters": []
+                    },
+                    {
+                      "colorB": 228,
+                      "colorG": 176,
+                      "colorR": 74,
+                      "creationTime": 0,
+                      "name": "Mean Y",
+                      "source": "",
+                      "type": "BuiltinCommonInstructions::Group",
+                      "events": [
+                        {
+                          "type": "BuiltinCommonInstructions::Standard",
+                          "conditions": [],
+                          "actions": [
+                            {
+                              "type": {
+                                "value": "SmoothCamera::SmoothCamera::SetPropertyForecastHistoryMeanY"
+                              },
+                              "parameters": [
+                                "Object",
+                                "Behavior",
+                                "=",
+                                "0"
+                              ]
+                            },
+                            {
+                              "type": {
+                                "value": "SmoothCamera::SmoothCamera::SetPropertyIndex"
+                              },
+                              "parameters": [
+                                "Object",
+                                "Behavior",
+                                "=",
+                                "0"
+                              ]
+                            }
+                          ]
+                        },
+                        {
+                          "type": "BuiltinCommonInstructions::Repeat",
+                          "repeatExpression": "Object.VariableChildCount(__SmoothCamera.ForecastHistoryY)",
+                          "conditions": [],
+                          "actions": [
+                            {
+                              "type": {
+                                "value": "SmoothCamera::SmoothCamera::SetPropertyForecastHistoryMeanY"
+                              },
+                              "parameters": [
+                                "Object",
+                                "Behavior",
+                                "+",
+                                "Object.Variable(__SmoothCamera.ForecastHistoryY[Index])"
+                              ]
+                            },
+                            {
+                              "type": {
+                                "value": "SmoothCamera::SmoothCamera::SetPropertyIndex"
+                              },
+                              "parameters": [
+                                "Object",
+                                "Behavior",
+                                "+",
+                                "1"
+                              ]
+                            }
+                          ]
+                        },
+                        {
+                          "type": "BuiltinCommonInstructions::Standard",
+                          "conditions": [],
+                          "actions": [
+                            {
+                              "type": {
+                                "value": "SmoothCamera::SmoothCamera::SetPropertyForecastHistoryMeanY"
+                              },
+                              "parameters": [
+                                "Object",
+                                "Behavior",
+                                "/",
+                                "Object.VariableChildCount(__SmoothCamera.ForecastHistoryY)"
+                              ]
+                            }
+                          ]
+                        },
+                        {
+                          "disabled": true,
+                          "type": "BuiltinCommonInstructions::Standard",
+                          "conditions": [],
+                          "actions": [
+                            {
+                              "type": {
+                                "value": "DebuggerTools::ConsoleLog"
+                              },
+                              "parameters": [
+                                "\"Mean: \" + ToString(ForecastHistoryMeanX) + \" \" + ToString(ForecastHistoryMeanY)",
+                                "",
+                                ""
+                              ]
+                            }
+                          ]
+                        }
+                      ],
+                      "parameters": []
+                    },
+                    {
+                      "colorB": 228,
+                      "colorG": 176,
+                      "colorR": 74,
+                      "creationTime": 0,
+                      "name": "Variance and Covariance",
+                      "source": "",
+                      "type": "BuiltinCommonInstructions::Group",
+                      "events": [
+                        {
+                          "type": "BuiltinCommonInstructions::Comment",
+                          "color": {
+                            "b": 109,
+                            "g": 230,
+                            "r": 255,
+                            "textB": 0,
+                            "textG": 0,
+                            "textR": 0
+                          },
+                          "comment": "VarianceX = sum((X[i] - MeanX)²)\nVarianceY = sum((Y[i] - MeanY)²)\nCovariance = sum((X[i] - MeanX) * (Y[i] - MeanY))"
+                        },
+                        {
+                          "type": "BuiltinCommonInstructions::Standard",
+                          "conditions": [],
+                          "actions": [
+                            {
+                              "type": {
+                                "value": "SmoothCamera::SmoothCamera::SetPropertyForecastHistoryVarianceX"
+                              },
+                              "parameters": [
+                                "Object",
+                                "Behavior",
+                                "=",
+                                "0"
+                              ]
+                            },
+                            {
+                              "type": {
+                                "value": "SmoothCamera::SmoothCamera::SetPropertyForecastHistoryVarianceY"
+                              },
+                              "parameters": [
+                                "Object",
+                                "Behavior",
+                                "=",
+                                "0"
+                              ]
+                            },
+                            {
+                              "type": {
+                                "value": "SmoothCamera::SmoothCamera::SetPropertyForecastHistoryCovariance"
+                              },
+                              "parameters": [
+                                "Object",
+                                "Behavior",
+                                "=",
+                                "0"
+                              ]
+                            },
+                            {
+                              "type": {
+                                "value": "SmoothCamera::SmoothCamera::SetPropertyIndex"
+                              },
+                              "parameters": [
+                                "Object",
+                                "Behavior",
+                                "=",
+                                "0"
+                              ]
+                            }
+                          ]
+                        },
+                        {
+                          "type": "BuiltinCommonInstructions::Repeat",
+                          "repeatExpression": "Object.VariableChildCount(__SmoothCamera.ForecastHistoryX)",
+                          "conditions": [],
+                          "actions": [
+                            {
+                              "type": {
+                                "value": "SmoothCamera::SmoothCamera::SetPropertyForecastHistoryVarianceX"
+                              },
+                              "parameters": [
+                                "Object",
+                                "Behavior",
+                                "+",
+                                "pow(Object.Variable(__SmoothCamera.ForecastHistoryX[Index]) - ForecastHistoryMeanX, 2)"
+                              ]
+                            },
+                            {
+                              "type": {
+                                "value": "SmoothCamera::SmoothCamera::SetPropertyForecastHistoryVarianceY"
+                              },
+                              "parameters": [
+                                "Object",
+                                "Behavior",
+                                "+",
+                                "pow(Object.Variable(__SmoothCamera.ForecastHistoryY[Index]) - ForecastHistoryMeanY, 2)"
+                              ]
+                            },
+                            {
+                              "type": {
+                                "value": "SmoothCamera::SmoothCamera::SetPropertyForecastHistoryCovariance"
+                              },
+                              "parameters": [
+                                "Object",
+                                "Behavior",
+                                "+",
+                                "(Object.Variable(__SmoothCamera.ForecastHistoryX[Index]) - ForecastHistoryMeanX)\n*\n(Object.Variable(__SmoothCamera.ForecastHistoryY[Index]) - ForecastHistoryMeanY)"
+                              ]
+                            },
+                            {
+                              "type": {
+                                "value": "SmoothCamera::SmoothCamera::SetPropertyIndex"
+                              },
+                              "parameters": [
+                                "Object",
+                                "Behavior",
+                                "+",
+                                "1"
+                              ]
+                            }
+                          ]
+                        },
+                        {
+                          "disabled": true,
+                          "type": "BuiltinCommonInstructions::Standard",
+                          "conditions": [],
+                          "actions": [
+                            {
+                              "type": {
+                                "value": "DebuggerTools::ConsoleLog"
+                              },
+                              "parameters": [
+                                "\"Variances: \" + ToString(ForecastHistoryVarianceX) + \" \" + ToString(ForecastHistoryVarianceY) + \" \" + ToString(ForecastHistoryCovariance)",
+                                "\"info\"",
+                                "\"SmoothCamera\""
+                              ]
+                            }
+                          ]
+                        },
+                        {
+                          "type": "BuiltinCommonInstructions::Standard",
+                          "conditions": [
+                            {
+                              "type": {
+                                "value": "Egal"
+                              },
+                              "parameters": [
+                                "abs(ForecastHistoryVarianceX)",
+                                "<",
+                                "1"
+                              ]
+                            },
+                            {
+                              "type": {
+                                "value": "Egal"
+                              },
+                              "parameters": [
+                                "abs(ForecastHistoryVarianceY)",
+                                "<",
+                                "1"
+                              ]
+                            }
+                          ],
+                          "actions": [
+                            {
+                              "type": {
+                                "value": "SmoothCamera::SmoothCamera::SetPropertyForecastedX"
+                              },
+                              "parameters": [
+                                "Object",
+                                "Behavior",
+                                "=",
+                                "DelayedCenterX"
+                              ]
+                            },
+                            {
+                              "type": {
+                                "value": "SmoothCamera::SmoothCamera::SetPropertyForecastedY"
+                              },
+                              "parameters": [
+                                "Object",
+                                "Behavior",
+                                "=",
+                                "DelayedCenterY"
+                              ]
+                            }
+                          ]
+                        },
+                        {
+                          "type": "BuiltinCommonInstructions::Standard",
+                          "conditions": [
+                            {
+                              "type": {
+                                "value": "BuiltinCommonInstructions::Or"
+                              },
+                              "parameters": [],
+                              "subInstructions": [
+                                {
+                                  "type": {
+                                    "value": "Egal"
+                                  },
+                                  "parameters": [
+                                    "abs(ForecastHistoryVarianceX)",
+                                    ">=",
+                                    "1"
+                                  ]
+                                },
+                                {
+                                  "type": {
+                                    "value": "Egal"
+                                  },
+                                  "parameters": [
+                                    "abs(ForecastHistoryVarianceY)",
+                                    ">=",
+                                    "1"
+                                  ]
+                                }
+                              ]
+                            }
+                          ],
+                          "actions": [],
+                          "events": [
+                            {
+                              "colorB": 228,
+                              "colorG": 176,
+                              "colorR": 74,
+                              "creationTime": 0,
+                              "name": "Linear function parameters",
+                              "source": "",
+                              "type": "BuiltinCommonInstructions::Group",
+                              "events": [
+                                {
+                                  "type": "BuiltinCommonInstructions::Comment",
+                                  "color": {
+                                    "b": 109,
+                                    "g": 230,
+                                    "r": 255,
+                                    "textB": 0,
+                                    "textG": 0,
+                                    "textR": 0
+                                  },
+                                  "comment": "y = A * x + B\n\nA = Covariance / VarianceX\nB = MeanY - A * MeanX"
+                                },
+                                {
+                                  "type": "BuiltinCommonInstructions::Standard",
+                                  "conditions": [
+                                    {
+                                      "type": {
+                                        "value": "Egal"
+                                      },
+                                      "parameters": [
+                                        "abs(ForecastHistoryVarianceX)",
+                                        ">=",
+                                        "abs(ForecastHistoryVarianceY)"
+                                      ]
+                                    }
+                                  ],
+                                  "actions": [
+                                    {
+                                      "type": {
+                                        "value": "SmoothCamera::SmoothCamera::SetPropertyForecastHistoryLinearA"
+                                      },
+                                      "parameters": [
+                                        "Object",
+                                        "Behavior",
+                                        "=",
+                                        "ForecastHistoryCovariance / ForecastHistoryVarianceX"
+                                      ]
+                                    },
+                                    {
+                                      "type": {
+                                        "value": "SmoothCamera::SmoothCamera::SetPropertyForecastHistoryLinearB"
+                                      },
+                                      "parameters": [
+                                        "Object",
+                                        "Behavior",
+                                        "=",
+                                        "ForecastHistoryMeanY - ForecastHistoryLinearA * ForecastHistoryMeanX"
+                                      ]
+                                    }
+                                  ],
+                                  "events": [
+                                    {
+                                      "disabled": true,
+                                      "type": "BuiltinCommonInstructions::Standard",
+                                      "conditions": [],
+                                      "actions": [
+                                        {
+                                          "type": {
+                                            "value": "DebuggerTools::ConsoleLog"
+                                          },
+                                          "parameters": [
+                                            "\"Linear: \" + ToString(ForecastHistoryLinearA) + \" \" + ToString(ForecastHistoryLinearB)",
+                                            "\"info\"",
+                                            "\"SmoothCamera\""
+                                          ]
+                                        }
+                                      ]
+                                    },
+                                    {
+                                      "colorB": 228,
+                                      "colorG": 176,
+                                      "colorR": 74,
+                                      "creationTime": 0,
+                                      "name": "Projection",
+                                      "source": "",
+                                      "type": "BuiltinCommonInstructions::Group",
+                                      "events": [
+                                        {
+                                          "type": "BuiltinCommonInstructions::Standard",
+                                          "conditions": [],
+                                          "actions": [
+                                            {
+                                              "type": {
+                                                "value": "SmoothCamera::SmoothCamera::ProjectHistoryEnds"
+                                              },
+                                              "parameters": [
+                                                "Object",
+                                                "Behavior",
+                                                "Object.Variable(__SmoothCamera.ForecastHistoryX[0])",
+                                                "Object.Variable(__SmoothCamera.ForecastHistoryY[0])",
+                                                "Object.Variable(__SmoothCamera.ForecastHistoryX[Object.VariableChildCount(__SmoothCamera.ForecastHistoryX) - 1])",
+                                                "Object.Variable(__SmoothCamera.ForecastHistoryY[Object.VariableChildCount(__SmoothCamera.ForecastHistoryY) - 1])",
+                                                ""
+                                              ]
+                                            }
+                                          ]
+                                        }
+                                      ],
+                                      "parameters": []
+                                    }
+                                  ]
+                                },
+                                {
+                                  "type": "BuiltinCommonInstructions::Comment",
+                                  "color": {
+                                    "b": 109,
+                                    "g": 230,
+                                    "r": 255,
+                                    "textB": 0,
+                                    "textG": 0,
+                                    "textR": 0
+                                  },
+                                  "comment": "Axis permutation to avoid a ratio between 2 numbers near 0."
+                                },
+                                {
+                                  "type": "BuiltinCommonInstructions::Standard",
+                                  "conditions": [
+                                    {
+                                      "type": {
+                                        "value": "Egal"
+                                      },
+                                      "parameters": [
+                                        "abs(ForecastHistoryVarianceX)",
+                                        "<",
+                                        "abs(ForecastHistoryVarianceY)"
+                                      ]
+                                    }
+                                  ],
+                                  "actions": [
+                                    {
+                                      "type": {
+                                        "value": "SmoothCamera::SmoothCamera::SetPropertyForecastHistoryLinearA"
+                                      },
+                                      "parameters": [
+                                        "Object",
+                                        "Behavior",
+                                        "=",
+                                        "ForecastHistoryCovariance / ForecastHistoryVarianceY"
+                                      ]
+                                    },
+                                    {
+                                      "type": {
+                                        "value": "SmoothCamera::SmoothCamera::SetPropertyForecastHistoryLinearB"
+                                      },
+                                      "parameters": [
+                                        "Object",
+                                        "Behavior",
+                                        "=",
+                                        "ForecastHistoryMeanX - ForecastHistoryLinearA * ForecastHistoryMeanY"
+                                      ]
+                                    }
+                                  ],
+                                  "events": [
+                                    {
+                                      "disabled": true,
+                                      "type": "BuiltinCommonInstructions::Standard",
+                                      "conditions": [],
+                                      "actions": [
+                                        {
+                                          "type": {
+                                            "value": "DebuggerTools::ConsoleLog"
+                                          },
+                                          "parameters": [
+                                            "\"Linear: \" + ToString(ForecastHistoryLinearA) + \" \" + ToString(ForecastHistoryLinearB)",
+                                            "\"info\"",
+                                            "\"SmoothCamera\""
+                                          ]
+                                        }
+                                      ]
+                                    },
+                                    {
+                                      "colorB": 228,
+                                      "colorG": 176,
+                                      "colorR": 74,
+                                      "creationTime": 0,
+                                      "name": "Projection",
+                                      "source": "",
+                                      "type": "BuiltinCommonInstructions::Group",
+                                      "events": [
+                                        {
+                                          "type": "BuiltinCommonInstructions::Standard",
+                                          "conditions": [],
+                                          "actions": [
+                                            {
+                                              "type": {
+                                                "value": "SmoothCamera::SmoothCamera::ProjectHistoryEnds"
+                                              },
+                                              "parameters": [
+                                                "Object",
+                                                "Behavior",
+                                                "Object.Variable(__SmoothCamera.ForecastHistoryY[0])",
+                                                "Object.Variable(__SmoothCamera.ForecastHistoryX[0])",
+                                                "Object.Variable(__SmoothCamera.ForecastHistoryY[Object.VariableChildCount(__SmoothCamera.ForecastHistoryY) - 1])",
+                                                "Object.Variable(__SmoothCamera.ForecastHistoryX[Object.VariableChildCount(__SmoothCamera.ForecastHistoryX) - 1])",
+                                                ""
+                                              ]
+                                            }
+                                          ]
+                                        },
+                                        {
+                                          "type": "BuiltinCommonInstructions::Comment",
+                                          "color": {
+                                            "b": 109,
+                                            "g": 230,
+                                            "r": 255,
+                                            "textB": 0,
+                                            "textG": 0,
+                                            "textR": 0
+                                          },
+                                          "comment": "Permute back axis"
+                                        },
+                                        {
+                                          "type": "BuiltinCommonInstructions::Standard",
+                                          "conditions": [],
+                                          "actions": [
+                                            {
+                                              "type": {
+                                                "value": "SmoothCamera::SmoothCamera::SetPropertyIndex"
+                                              },
+                                              "parameters": [
+                                                "Object",
+                                                "Behavior",
+                                                "=",
+                                                "ProjectedOldestX"
+                                              ]
+                                            },
+                                            {
+                                              "type": {
+                                                "value": "SmoothCamera::SmoothCamera::SetPropertyProjectedOldestX"
+                                              },
+                                              "parameters": [
+                                                "Object",
+                                                "Behavior",
+                                                "=",
+                                                "ProjectedOldestY"
+                                              ]
+                                            },
+                                            {
+                                              "type": {
+                                                "value": "SmoothCamera::SmoothCamera::SetPropertyProjectedOldestY"
+                                              },
+                                              "parameters": [
+                                                "Object",
+                                                "Behavior",
+                                                "=",
+                                                "Index"
+                                              ]
+                                            }
+                                          ]
+                                        },
+                                        {
+                                          "type": "BuiltinCommonInstructions::Standard",
+                                          "conditions": [],
+                                          "actions": [
+                                            {
+                                              "type": {
+                                                "value": "SmoothCamera::SmoothCamera::SetPropertyIndex"
+                                              },
+                                              "parameters": [
+                                                "Object",
+                                                "Behavior",
+                                                "=",
+                                                "ProjectedNewestX"
+                                              ]
+                                            },
+                                            {
+                                              "type": {
+                                                "value": "SmoothCamera::SmoothCamera::SetPropertyProjectedNewestX"
+                                              },
+                                              "parameters": [
+                                                "Object",
+                                                "Behavior",
+                                                "=",
+                                                "ProjectedNewestY"
+                                              ]
+                                            },
+                                            {
+                                              "type": {
+                                                "value": "SmoothCamera::SmoothCamera::SetPropertyProjectedNewestY"
+                                              },
+                                              "parameters": [
+                                                "Object",
+                                                "Behavior",
+                                                "=",
+                                                "Index"
+                                              ]
+                                            }
+                                          ]
+                                        }
+                                      ],
+                                      "parameters": []
+                                    },
+                                    {
+                                      "disabled": true,
+                                      "type": "BuiltinCommonInstructions::Standard",
+                                      "conditions": [],
+                                      "actions": [
+                                        {
+                                          "type": {
+                                            "value": "DebuggerTools::ConsoleLog"
+                                          },
+                                          "parameters": [
+                                            "\"Oldest: \" + ToString(ProjectedOldestX) + \" \" + ToString(ProjectedOldestY)",
+                                            "\"info\"",
+                                            "\"SmoothCamera\""
+                                          ]
+                                        }
+                                      ]
+                                    },
+                                    {
+                                      "disabled": true,
+                                      "type": "BuiltinCommonInstructions::Standard",
+                                      "conditions": [],
+                                      "actions": [
+                                        {
+                                          "type": {
+                                            "value": "DebuggerTools::ConsoleLog"
+                                          },
+                                          "parameters": [
+                                            "\"Newest: \" + ToString(ProjectedNewestX) + \" \" + ToString(ProjectedNewestY)",
+                                            "\"info\"",
+                                            "\"SmoothCamera\""
+                                          ]
+                                        }
+                                      ]
+                                    }
+                                  ]
+                                },
+                                {
+                                  "colorB": 228,
+                                  "colorG": 176,
+                                  "colorR": 74,
+                                  "creationTime": 0,
+                                  "name": "Forecasted position",
+                                  "source": "",
+                                  "type": "BuiltinCommonInstructions::Group",
+                                  "events": [
+                                    {
+                                      "type": "BuiltinCommonInstructions::Standard",
+                                      "conditions": [],
+                                      "actions": [
+                                        {
+                                          "type": {
+                                            "value": "SmoothCamera::SmoothCamera::SetPropertyForecastedX"
+                                          },
+                                          "parameters": [
+                                            "Object",
+                                            "Behavior",
+                                            "=",
+                                            "ProjectedNewestX + ( ProjectedNewestX - ProjectedOldestX) * Object.Behavior::ForecastTimeRatio()"
+                                          ]
+                                        },
+                                        {
+                                          "type": {
+                                            "value": "SmoothCamera::SmoothCamera::SetPropertyForecastedY"
+                                          },
+                                          "parameters": [
+                                            "Object",
+                                            "Behavior",
+                                            "=",
+                                            "ProjectedNewestY + ( ProjectedNewestY - ProjectedOldestY) * Object.Behavior::ForecastTimeRatio()"
+                                          ]
+                                        }
+                                      ]
+                                    },
+                                    {
+                                      "disabled": true,
+                                      "type": "BuiltinCommonInstructions::Standard",
+                                      "conditions": [],
+                                      "actions": [
+                                        {
+                                          "type": {
+                                            "value": "DebuggerTools::ConsoleLog"
+                                          },
+                                          "parameters": [
+                                            "\"Forecasted: \" + ToString(ForecastedX) + \" \" + ToString(ForecastedY)",
+                                            "\"info\"",
+                                            "\"SmoothCamera\""
+                                          ]
+                                        }
+                                      ]
+                                    }
+                                  ],
+                                  "parameters": []
+                                }
+                              ],
+                              "parameters": []
+                            }
+                          ]
+                        }
+                      ],
+                      "parameters": []
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "SmoothCamera::SmoothCamera",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "Project history ends position to have the vector on the line from linear regression. This function is only called by UpdateForecastedPosition.",
+              "fullName": "Project history ends",
+              "functionType": "Action",
+              "group": "Private",
+              "name": "ProjectHistoryEnds",
+              "private": true,
+              "sentence": "Project history oldest: _PARAM2_;_PARAM3_ and newest position: _PARAM4_;_PARAM5_ of _PARAM0_",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Comment",
+                  "color": {
+                    "b": 109,
+                    "g": 230,
+                    "r": 255,
+                    "textB": 0,
+                    "textG": 0,
+                    "textR": 0
+                  },
+                  "comment": "Perpendicular line:\npA = -1/a; \npB = -pA * x + y\n\nIntersection:\n/ ProjectedY = a * ProjectedX + b\n\\ ProjectedY = pA * ProjectedX + b\n\nSolution that is cleaned out from indeterminism (like 0 / 0 or infinity / infinity):\nProjectedX= (x + (y - b) * a) / (a² + 1)\nProjectedY = y + (x * a - y + b) / (a² + 1)"
+                },
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SmoothCamera::SmoothCamera::SetPropertyProjectedNewestX"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "=",
+                        "(NewestX + (NewestY - ForecastHistoryLinearB) * ForecastHistoryLinearA) / (1 + pow(ForecastHistoryLinearA, 2))"
+                      ]
+                    },
+                    {
+                      "type": {
+                        "value": "SmoothCamera::SmoothCamera::SetPropertyProjectedNewestY"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "=",
+                        "NewestY + (NewestX * ForecastHistoryLinearA - NewestY \n+ ForecastHistoryLinearB) / (1 + pow(ForecastHistoryLinearA, 2))"
+                      ]
+                    }
+                  ]
+                },
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SmoothCamera::SmoothCamera::SetPropertyProjectedOldestX"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "=",
+                        "(OldestX + (OldestY - ForecastHistoryLinearB) * ForecastHistoryLinearA) / (1 + pow(ForecastHistoryLinearA, 2))"
+                      ]
+                    },
+                    {
+                      "type": {
+                        "value": "SmoothCamera::SmoothCamera::SetPropertyProjectedOldestY"
+                      },
+                      "parameters": [
+                        "Object",
+                        "Behavior",
+                        "=",
+                        "OldestY + (OldestX * ForecastHistoryLinearA - OldestY \n+ ForecastHistoryLinearB) / (1 + pow(ForecastHistoryLinearA, 2))"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "SmoothCamera::SmoothCamera",
+                  "type": "behavior"
+                },
+                {
+                  "description": "OldestX",
+                  "name": "OldestX",
+                  "type": "expression"
+                },
+                {
+                  "description": "OldestY",
+                  "name": "OldestY",
+                  "type": "expression"
+                },
+                {
+                  "description": "Newest X",
+                  "name": "NewestX",
+                  "type": "expression"
+                },
+                {
+                  "description": "Newest Y",
+                  "name": "NewestY",
+                  "type": "expression"
+                }
+              ],
+              "objectGroups": []
+            },
+            {
+              "description": "Return the ratio between forecast time and the duration of the history. This function is only called by UpdateForecastedPosition.",
+              "fullName": "Forecast time ratio",
+              "functionType": "Expression",
+              "group": "Private",
+              "name": "ForecastTimeRatio",
+              "private": true,
+              "sentence": "",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SetReturnNumber"
+                      },
+                      "parameters": [
+                        "- ForecastTime / (Object.Variable(__SmoothCamera.ForecastHistoryTime[0]) - Object.Variable(__SmoothCamera.ForecastHistoryTime[Object.VariableChildCount(__SmoothCamera.ForecastHistoryTime) - 1]))"
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "expressionType": {
+                "type": "expression"
+              },
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "SmoothCamera::SmoothCamera",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            }
+          ],
+          "propertyDescriptors": [
+            {
+              "value": "0.9",
+              "type": "Number",
+              "label": "Leftward catch-up speed (in ratio per second)",
+              "description": "",
+              "group": "Catch-up speed",
+              "extraInformation": [],
+              "name": "LeftwardSpeed"
+            },
+            {
+              "value": "0.9",
+              "type": "Number",
+              "label": "Rightward catch-up speed (in ratio per second)",
+              "description": "",
+              "group": "Catch-up speed",
+              "extraInformation": [],
+              "name": "RightwardSpeed"
+            },
+            {
+              "value": "0.9",
+              "type": "Number",
+              "label": "Upward catch-up speed (in ratio per second)",
+              "description": "",
+              "group": "Catch-up speed",
+              "extraInformation": [],
+              "name": "UpwardSpeed"
+            },
+            {
+              "value": "0.9",
+              "type": "Number",
+              "label": "Downward catch-up speed (in ratio per second)",
+              "description": "",
+              "group": "Catch-up speed",
+              "extraInformation": [],
+              "name": "DownwardSpeed"
+            },
+            {
+              "value": "true",
+              "type": "Boolean",
+              "label": "Follow on X axis",
+              "description": "",
+              "group": "",
+              "extraInformation": [],
+              "name": "FollowOnX"
+            },
+            {
+              "value": "true",
+              "type": "Boolean",
+              "label": "Follow on Y axis",
+              "description": "",
+              "group": "",
+              "extraInformation": [],
+              "name": "FollowOnY"
+            },
+            {
+              "value": "0",
+              "type": "Number",
+              "unit": "Pixel",
+              "label": "Follow free area left border",
+              "description": "",
+              "group": "Position",
+              "extraInformation": [],
+              "advanced": true,
+              "name": "FollowFreeAreaLeft"
+            },
+            {
+              "value": "0",
+              "type": "Number",
+              "unit": "Pixel",
+              "label": "Follow free area right border",
+              "description": "",
+              "group": "Position",
+              "extraInformation": [],
+              "advanced": true,
+              "name": "FollowFreeAreaRight"
+            },
+            {
+              "value": "0",
+              "type": "Number",
+              "unit": "Pixel",
+              "label": "Follow free area top border",
+              "description": "",
+              "group": "Position",
+              "extraInformation": [],
+              "advanced": true,
+              "name": "FollowFreeAreaTop"
+            },
+            {
+              "value": "0",
+              "type": "Number",
+              "unit": "Pixel",
+              "label": "Follow free area bottom border",
+              "description": "",
+              "group": "Position",
+              "extraInformation": [],
+              "advanced": true,
+              "name": "FollowFreeAreaBottom"
+            },
+            {
+              "value": "0",
+              "type": "Number",
+              "unit": "Pixel",
+              "label": "Camera offset X",
+              "description": "",
+              "group": "Position",
+              "extraInformation": [],
+              "advanced": true,
+              "name": "CameraOffsetX"
+            },
+            {
+              "value": "0",
+              "type": "Number",
+              "unit": "Pixel",
+              "label": "Camera offset Y",
+              "description": "",
+              "group": "Position",
+              "extraInformation": [],
+              "advanced": true,
+              "name": "CameraOffsetY"
+            },
+            {
+              "value": "0",
+              "type": "Number",
+              "unit": "Second",
+              "label": "Camera delay",
+              "description": "",
+              "group": "Timing",
+              "extraInformation": [],
+              "deprecated": true,
+              "name": "CameraDelay"
+            },
+            {
+              "value": "0",
+              "type": "Number",
+              "unit": "Second",
+              "label": "Forecast time",
+              "description": "",
+              "group": "Timing",
+              "extraInformation": [],
+              "deprecated": true,
+              "name": "ForecastTime"
+            },
+            {
+              "value": "0",
+              "type": "Number",
+              "unit": "Second",
+              "label": "Forecast history duration",
+              "description": "",
+              "group": "Timing",
+              "extraInformation": [],
+              "deprecated": true,
+              "name": "ForecastHistoryDuration"
+            },
+            {
+              "value": "",
+              "type": "Number",
+              "label": "",
+              "description": "",
+              "group": "",
+              "extraInformation": [],
+              "hidden": true,
+              "name": "LogLeftwardSpeed"
+            },
+            {
+              "value": "",
+              "type": "Number",
+              "label": "",
+              "description": "",
+              "group": "",
+              "extraInformation": [],
+              "hidden": true,
+              "name": "LogRightwardSpeed"
+            },
+            {
+              "value": "",
+              "type": "Number",
+              "label": "",
+              "description": "",
+              "group": "",
+              "extraInformation": [],
+              "hidden": true,
+              "name": "LogDownwardSpeed"
+            },
+            {
+              "value": "",
+              "type": "Number",
+              "label": "",
+              "description": "",
+              "group": "",
+              "extraInformation": [],
+              "hidden": true,
+              "name": "LogUpwardSpeed"
+            },
+            {
+              "value": "",
+              "type": "Number",
+              "label": "",
+              "description": "",
+              "group": "",
+              "extraInformation": [],
+              "hidden": true,
+              "name": "DelayedCenterX"
+            },
+            {
+              "value": "",
+              "type": "Number",
+              "label": "",
+              "description": "",
+              "group": "",
+              "extraInformation": [],
+              "hidden": true,
+              "name": "DelayedCenterY"
+            },
+            {
+              "value": "",
+              "type": "Number",
+              "label": "",
+              "description": "",
+              "group": "",
+              "extraInformation": [],
+              "hidden": true,
+              "name": "ForecastHistoryMeanX"
+            },
+            {
+              "value": "",
+              "type": "Number",
+              "label": "",
+              "description": "",
+              "group": "",
+              "extraInformation": [],
+              "hidden": true,
+              "name": "ForecastHistoryMeanY"
+            },
+            {
+              "value": "",
+              "type": "Number",
+              "label": "",
+              "description": "",
+              "group": "",
+              "extraInformation": [],
+              "hidden": true,
+              "name": "ForecastHistoryVarianceX"
+            },
+            {
+              "value": "",
+              "type": "Number",
+              "label": "",
+              "description": "",
+              "group": "",
+              "extraInformation": [],
+              "hidden": true,
+              "name": "ForecastHistoryCovariance"
+            },
+            {
+              "value": "",
+              "type": "Number",
+              "label": "",
+              "description": "",
+              "group": "",
+              "extraInformation": [],
+              "hidden": true,
+              "name": "ForecastHistoryLinearA"
+            },
+            {
+              "value": "",
+              "type": "Number",
+              "label": "",
+              "description": "",
+              "group": "",
+              "extraInformation": [],
+              "hidden": true,
+              "name": "ForecastHistoryLinearB"
+            },
+            {
+              "value": "",
+              "type": "Number",
+              "label": "",
+              "description": "",
+              "group": "",
+              "extraInformation": [],
+              "hidden": true,
+              "name": "ForecastedX"
+            },
+            {
+              "value": "",
+              "type": "Number",
+              "label": "",
+              "description": "",
+              "group": "",
+              "extraInformation": [],
+              "hidden": true,
+              "name": "ForecastedY"
+            },
+            {
+              "value": "",
+              "type": "Number",
+              "label": "",
+              "description": "",
+              "group": "",
+              "extraInformation": [],
+              "hidden": true,
+              "name": "ProjectedNewestX"
+            },
+            {
+              "value": "",
+              "type": "Number",
+              "label": "",
+              "description": "",
+              "group": "",
+              "extraInformation": [],
+              "hidden": true,
+              "name": "ProjectedNewestY"
+            },
+            {
+              "value": "",
+              "type": "Number",
+              "label": "",
+              "description": "",
+              "group": "",
+              "extraInformation": [],
+              "hidden": true,
+              "name": "ProjectedOldestX"
+            },
+            {
+              "value": "",
+              "type": "Number",
+              "label": "",
+              "description": "",
+              "group": "",
+              "extraInformation": [],
+              "hidden": true,
+              "name": "ProjectedOldestY"
+            },
+            {
+              "value": "",
+              "type": "Number",
+              "label": "",
+              "description": "",
+              "group": "",
+              "extraInformation": [],
+              "hidden": true,
+              "name": "ForecastHistoryVarianceY"
+            },
+            {
+              "value": "",
+              "type": "Number",
+              "label": "Index (local variable)",
+              "description": "",
+              "group": "",
+              "extraInformation": [],
+              "hidden": true,
+              "name": "Index"
+            },
+            {
+              "value": "0",
+              "type": "Number",
+              "label": "",
+              "description": "",
+              "group": "",
+              "extraInformation": [],
+              "hidden": true,
+              "name": "CameraDelayCatchUpSpeed"
+            },
+            {
+              "value": "",
+              "type": "Number",
+              "label": "",
+              "description": "",
+              "group": "",
+              "extraInformation": [],
+              "hidden": true,
+              "name": "CameraExtraDelay"
+            },
+            {
+              "value": "",
+              "type": "Number",
+              "label": "",
+              "description": "",
+              "group": "",
+              "extraInformation": [],
+              "hidden": true,
+              "name": "WaitingSpeedXMax"
+            },
+            {
+              "value": "",
+              "type": "Number",
+              "label": "",
+              "description": "",
+              "group": "",
+              "extraInformation": [],
+              "hidden": true,
+              "name": "WaitingSpeedYMax"
+            },
+            {
+              "value": "",
+              "type": "Number",
+              "label": "",
+              "description": "",
+              "group": "",
+              "extraInformation": [],
+              "hidden": true,
+              "name": "WaitingEnd"
+            },
+            {
+              "value": "",
+              "type": "Number",
+              "label": "",
+              "description": "",
+              "group": "",
+              "extraInformation": [],
+              "hidden": true,
+              "name": "CameraDelayCatchUpDuration"
+            },
+            {
+              "value": "9000",
+              "type": "Number",
+              "unit": "PixelSpeed",
+              "label": "Leftward maximum speed",
+              "description": "",
+              "group": "Maximum speed",
+              "extraInformation": [],
+              "advanced": true,
+              "name": "LeftwardSpeedMax"
+            },
+            {
+              "value": "9000",
+              "type": "Number",
+              "unit": "PixelSpeed",
+              "label": "Rightward maximum speed",
+              "description": "",
+              "group": "Maximum speed",
+              "extraInformation": [],
+              "advanced": true,
+              "name": "RightwardSpeedMax"
+            },
+            {
+              "value": "9000",
+              "type": "Number",
+              "unit": "PixelSpeed",
+              "label": "Upward maximum speed",
+              "description": "",
+              "group": "Maximum speed",
+              "extraInformation": [],
+              "advanced": true,
+              "name": "UpwardSpeedMax"
+            },
+            {
+              "value": "9000",
+              "type": "Number",
+              "unit": "PixelSpeed",
+              "label": "Downward maximum speed",
+              "description": "",
+              "group": "Maximum speed",
+              "extraInformation": [],
+              "advanced": true,
+              "name": "DownwardSpeedMax"
+            },
+            {
+              "value": "",
+              "type": "Number",
+              "label": "OldX (local variable)",
+              "description": "",
+              "group": "",
+              "extraInformation": [],
+              "hidden": true,
+              "name": "OldX"
+            },
+            {
+              "value": "",
+              "type": "Number",
+              "label": "OldY (local variable)",
+              "description": "",
+              "group": "",
+              "extraInformation": [],
+              "hidden": true,
+              "name": "OldY"
+            },
+            {
+              "value": "",
+              "type": "Boolean",
+              "label": "",
+              "description": "",
+              "group": "",
+              "extraInformation": [],
+              "hidden": true,
+              "name": "IsCalledManually"
+            }
+          ],
+          "sharedPropertyDescriptors": []
+        },
+        {
+          "description": "Smoothly scroll to follow a character and stabilize the camera when jumping.",
+          "fullName": "Smooth platformer camera",
+          "name": "SmoothPlatformerCamera",
+          "objectType": "",
+          "eventsFunctions": [
+            {
+              "fullName": "",
+              "functionType": "Action",
+              "name": "doStepPreEvents",
+              "sentence": "",
+              "events": [
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [
+                    {
+                      "type": {
+                        "inverted": true,
+                        "value": "PlatformBehavior::IsJumping"
+                      },
+                      "parameters": [
+                        "Object",
+                        "PlatformerCharacter"
+                      ]
+                    },
+                    {
+                      "type": {
+                        "inverted": true,
+                        "value": "PlatformBehavior::IsFalling"
+                      },
+                      "parameters": [
+                        "Object",
+                        "PlatformerCharacter"
+                      ]
+                    }
+                  ],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SmoothCamera::SmoothCamera::SetFollowFreeAreaBottom"
+                      },
+                      "parameters": [
+                        "Object",
+                        "SmoothCamera",
+                        "FloorFollowFreeAreaTop",
+                        ""
+                      ]
+                    },
+                    {
+                      "type": {
+                        "value": "SmoothCamera::SmoothCamera::SetFollowFreeAreaTop"
+                      },
+                      "parameters": [
+                        "Object",
+                        "SmoothCamera",
+                        "FloorFollowFreeAreaBottom",
+                        ""
+                      ]
+                    },
+                    {
+                      "type": {
+                        "value": "SmoothCamera::SmoothCamera::SetUpwardSpeed"
+                      },
+                      "parameters": [
+                        "Object",
+                        "SmoothCamera",
+                        "FloorUpwardSpeed",
+                        ""
+                      ]
+                    },
+                    {
+                      "type": {
+                        "value": "SmoothCamera::SmoothCamera::SetDownwardSpeed"
+                      },
+                      "parameters": [
+                        "Object",
+                        "SmoothCamera",
+                        "FloorDownwardSpeed",
+                        ""
+                      ]
+                    },
+                    {
+                      "type": {
+                        "value": "SmoothCamera::SmoothCamera::SetUpwardSpeedMax"
+                      },
+                      "parameters": [
+                        "Object",
+                        "SmoothCamera",
+                        "FloorUpwardSpeedMax",
+                        ""
+                      ]
+                    },
+                    {
+                      "type": {
+                        "value": "SmoothCamera::SmoothCamera::SetDownwardSpeedMax"
+                      },
+                      "parameters": [
+                        "Object",
+                        "SmoothCamera",
+                        "FloorDownwardSpeedMax",
+                        ""
+                      ]
+                    }
+                  ]
+                },
+                {
+                  "type": "BuiltinCommonInstructions::Standard",
+                  "conditions": [
+                    {
+                      "type": {
+                        "value": "BuiltinCommonInstructions::Or"
+                      },
+                      "parameters": [],
+                      "subInstructions": [
+                        {
+                          "type": {
+                            "value": "PlatformBehavior::IsJumping"
+                          },
+                          "parameters": [
+                            "Object",
+                            "PlatformerCharacter"
+                          ]
+                        },
+                        {
+                          "type": {
+                            "value": "PlatformBehavior::IsFalling"
+                          },
+                          "parameters": [
+                            "Object",
+                            "PlatformerCharacter"
+                          ]
+                        }
+                      ]
+                    }
+                  ],
+                  "actions": [
+                    {
+                      "type": {
+                        "value": "SmoothCamera::SmoothCamera::SetFollowFreeAreaBottom"
+                      },
+                      "parameters": [
+                        "Object",
+                        "SmoothCamera",
+                        "AirFollowFreeAreaTop",
+                        ""
+                      ]
+                    },
+                    {
+                      "type": {
+                        "value": "SmoothCamera::SmoothCamera::SetFollowFreeAreaTop"
+                      },
+                      "parameters": [
+                        "Object",
+                        "SmoothCamera",
+                        "AirFollowFreeAreaBottom",
+                        ""
+                      ]
+                    },
+                    {
+                      "type": {
+                        "value": "SmoothCamera::SmoothCamera::SetUpwardSpeed"
+                      },
+                      "parameters": [
+                        "Object",
+                        "SmoothCamera",
+                        "AirUpwardSpeed",
+                        ""
+                      ]
+                    },
+                    {
+                      "type": {
+                        "value": "SmoothCamera::SmoothCamera::SetDownwardSpeed"
+                      },
+                      "parameters": [
+                        "Object",
+                        "SmoothCamera",
+                        "AirDownwardSpeed",
+                        ""
+                      ]
+                    },
+                    {
+                      "type": {
+                        "value": "SmoothCamera::SmoothCamera::SetUpwardSpeedMax"
+                      },
+                      "parameters": [
+                        "Object",
+                        "SmoothCamera",
+                        "AirUpwardSpeedMax",
+                        ""
+                      ]
+                    },
+                    {
+                      "type": {
+                        "value": "SmoothCamera::SmoothCamera::SetDownwardSpeedMax"
+                      },
+                      "parameters": [
+                        "Object",
+                        "SmoothCamera",
+                        "AirDownwardSpeedMax",
+                        ""
+                      ]
+                    }
+                  ]
+                }
+              ],
+              "parameters": [
+                {
+                  "description": "Object",
+                  "name": "Object",
+                  "type": "object"
+                },
+                {
+                  "description": "Behavior",
+                  "name": "Behavior",
+                  "supplementaryInformation": "SmoothCamera::SmoothPlatformerCamera",
+                  "type": "behavior"
+                }
+              ],
+              "objectGroups": []
+            }
+          ],
+          "propertyDescriptors": [
+            {
+              "value": "",
+              "type": "Behavior",
+              "label": "Platformer character behavior",
+              "description": "",
+              "group": "",
+              "extraInformation": [
+                "PlatformBehavior::PlatformerObjectBehavior"
+              ],
+              "name": "PlatformerCharacter"
+            },
+            {
+              "value": "",
+              "type": "Behavior",
+              "label": "Smooth camera behavior",
+              "description": "",
+              "group": "",
+              "extraInformation": [
+                "SmoothCamera::SmoothCamera"
+              ],
+              "name": "SmoothCamera"
+            },
+            {
+              "value": "",
+              "type": "Number",
+              "label": "",
+              "description": "",
+              "group": "",
+              "extraInformation": [],
+              "hidden": true,
+              "name": "JumpOriginY"
+            },
+            {
+              "value": "0",
+              "type": "Number",
+              "unit": "Pixel",
+              "label": "Follow free area top in the air",
+              "description": "",
+              "group": "Position",
+              "extraInformation": [],
+              "name": "AirFollowFreeAreaTop"
+            },
+            {
+              "value": "0",
+              "type": "Number",
+              "unit": "Pixel",
+              "label": "Follow free area bottom in the air",
+              "description": "",
+              "group": "Position",
+              "extraInformation": [],
+              "name": "AirFollowFreeAreaBottom"
+            },
+            {
+              "value": "0",
+              "type": "Number",
+              "unit": "Pixel",
+              "label": "Follow free area top on the floor",
+              "description": "",
+              "group": "Position",
+              "extraInformation": [],
+              "name": "FloorFollowFreeAreaTop"
+            },
+            {
+              "value": "0",
+              "type": "Number",
+              "unit": "Pixel",
+              "label": "Follow free area bottom on the floor",
+              "description": "",
+              "group": "Position",
+              "extraInformation": [],
+              "name": "FloorFollowFreeAreaBottom"
+            },
+            {
+              "value": "0.95",
+              "type": "Number",
+              "label": "Upward speed in the air (in ratio per second)",
+              "description": "",
+              "group": "Catch-up speed",
+              "extraInformation": [],
+              "name": "AirUpwardSpeed"
+            },
+            {
+              "value": "0.95",
+              "type": "Number",
+              "label": "Downward speed in the air (in ratio per second)",
+              "description": "",
+              "group": "Catch-up speed",
+              "extraInformation": [],
+              "name": "AirDownwardSpeed"
+            },
+            {
+              "value": "0.9",
+              "type": "Number",
+              "label": "Upward speed on the floor (in ratio per second)",
+              "description": "",
+              "group": "Catch-up speed",
+              "extraInformation": [],
+              "name": "FloorUpwardSpeed"
+            },
+            {
+              "value": "0.9",
+              "type": "Number",
+              "label": "Downward speed on the floor (in ratio per second)",
+              "description": "",
+              "group": "Catch-up speed",
+              "extraInformation": [],
+              "name": "FloorDownwardSpeed"
+            },
+            {
+              "value": "9000",
+              "type": "Number",
+              "unit": "PixelSpeed",
+              "label": "Upward maximum speed in the air",
+              "description": "",
+              "group": "Maximum speed",
+              "extraInformation": [],
+              "name": "AirUpwardSpeedMax"
+            },
+            {
+              "value": "9000",
+              "type": "Number",
+              "unit": "PixelSpeed",
+              "label": "Downward maximum speed in the air",
+              "description": "",
+              "group": "Maximum speed",
+              "extraInformation": [],
+              "name": "AirDownwardSpeedMax"
+            },
+            {
+              "value": "9000",
+              "type": "Number",
+              "unit": "PixelSpeed",
+              "label": "Upward maximum speed on the floor",
+              "description": "",
+              "group": "Maximum speed",
+              "extraInformation": [],
+              "name": "FloorUpwardSpeedMax"
+            },
+            {
+              "value": "9000",
+              "type": "Number",
+              "unit": "PixelSpeed",
+              "label": "Downward maximum speed on the floor",
+              "description": "",
+              "group": "Maximum speed",
+              "extraInformation": [],
+              "name": "FloorDownwardSpeedMax"
+            }
+          ],
+          "sharedPropertyDescriptors": []
+        }
+      ],
+      "eventsBasedObjects": []
+    }
+  ],
+  "externalLayouts": []
+}
\ No newline at end of file
diff --git a/GDJS/tests/games/savestate/SaveLoadExemple/assets/04 - Castle Nosferatu (Sega-style FM Synth Remix).aac b/GDJS/tests/games/savestate/SaveLoadExemple/assets/04 - Castle Nosferatu (Sega-style FM Synth Remix).aac
new file mode 100644
index 000000000000..8725f0750529
Binary files /dev/null and b/GDJS/tests/games/savestate/SaveLoadExemple/assets/04 - Castle Nosferatu (Sega-style FM Synth Remix).aac differ
diff --git a/GDJS/tests/games/savestate/SaveLoadExemple/assets/Background.png b/GDJS/tests/games/savestate/SaveLoadExemple/assets/Background.png
new file mode 100644
index 000000000000..10a4d95ac58d
Binary files /dev/null and b/GDJS/tests/games/savestate/SaveLoadExemple/assets/Background.png differ
diff --git a/GDJS/tests/games/savestate/SaveLoadExemple/assets/Coins 8.aac b/GDJS/tests/games/savestate/SaveLoadExemple/assets/Coins 8.aac
new file mode 100644
index 000000000000..392e9a6c95d7
Binary files /dev/null and b/GDJS/tests/games/savestate/SaveLoadExemple/assets/Coins 8.aac differ
diff --git a/GDJS/tests/games/savestate/SaveLoadExemple/assets/Flag Blue.png b/GDJS/tests/games/savestate/SaveLoadExemple/assets/Flag Blue.png
new file mode 100644
index 000000000000..593e553d11f2
Binary files /dev/null and b/GDJS/tests/games/savestate/SaveLoadExemple/assets/Flag Blue.png differ
diff --git a/GDJS/tests/games/savestate/SaveLoadExemple/assets/Flat dark joystick border.png b/GDJS/tests/games/savestate/SaveLoadExemple/assets/Flat dark joystick border.png
new file mode 100644
index 000000000000..5ddd717e42b5
Binary files /dev/null and b/GDJS/tests/games/savestate/SaveLoadExemple/assets/Flat dark joystick border.png differ
diff --git a/GDJS/tests/games/savestate/SaveLoadExemple/assets/Flat dark joystick thumb.png b/GDJS/tests/games/savestate/SaveLoadExemple/assets/Flat dark joystick thumb.png
new file mode 100644
index 000000000000..36f5262a9055
Binary files /dev/null and b/GDJS/tests/games/savestate/SaveLoadExemple/assets/Flat dark joystick thumb.png differ
diff --git a/GDJS/tests/games/savestate/SaveLoadExemple/assets/France.png b/GDJS/tests/games/savestate/SaveLoadExemple/assets/France.png
new file mode 100644
index 000000000000..534820f12310
Binary files /dev/null and b/GDJS/tests/games/savestate/SaveLoadExemple/assets/France.png differ
diff --git a/GDJS/tests/games/savestate/SaveLoadExemple/assets/Large sign.png b/GDJS/tests/games/savestate/SaveLoadExemple/assets/Large sign.png
new file mode 100644
index 000000000000..233dba537998
Binary files /dev/null and b/GDJS/tests/games/savestate/SaveLoadExemple/assets/Large sign.png differ
diff --git a/GDJS/tests/games/savestate/SaveLoadExemple/assets/LightGlow.png b/GDJS/tests/games/savestate/SaveLoadExemple/assets/LightGlow.png
new file mode 100644
index 000000000000..871c194ccc64
Binary files /dev/null and b/GDJS/tests/games/savestate/SaveLoadExemple/assets/LightGlow.png differ
diff --git a/GDJS/tests/games/savestate/SaveLoadExemple/assets/NewTiledSprite.png b/GDJS/tests/games/savestate/SaveLoadExemple/assets/NewTiledSprite.png
new file mode 100644
index 000000000000..77fbc7d08c6f
Binary files /dev/null and b/GDJS/tests/games/savestate/SaveLoadExemple/assets/NewTiledSprite.png differ
diff --git a/GDJS/tests/games/savestate/SaveLoadExemple/assets/PickupCoin.wav b/GDJS/tests/games/savestate/SaveLoadExemple/assets/PickupCoin.wav
new file mode 100644
index 000000000000..baf5f0edbc5b
Binary files /dev/null and b/GDJS/tests/games/savestate/SaveLoadExemple/assets/PickupCoin.wav differ
diff --git a/GDJS/tests/games/savestate/SaveLoadExemple/assets/StartingCoin.png b/GDJS/tests/games/savestate/SaveLoadExemple/assets/StartingCoin.png
new file mode 100644
index 000000000000..0b5afba9b80c
Binary files /dev/null and b/GDJS/tests/games/savestate/SaveLoadExemple/assets/StartingCoin.png differ
diff --git a/GDJS/tests/games/savestate/SaveLoadExemple/assets/StartingGround.png b/GDJS/tests/games/savestate/SaveLoadExemple/assets/StartingGround.png
new file mode 100644
index 000000000000..f92aeed9ca81
Binary files /dev/null and b/GDJS/tests/games/savestate/SaveLoadExemple/assets/StartingGround.png differ
diff --git a/GDJS/tests/games/savestate/SaveLoadExemple/assets/StartingPlayer.png b/GDJS/tests/games/savestate/SaveLoadExemple/assets/StartingPlayer.png
new file mode 100644
index 000000000000..9975a5828987
Binary files /dev/null and b/GDJS/tests/games/savestate/SaveLoadExemple/assets/StartingPlayer.png differ
diff --git a/GDJS/tests/games/savestate/SaveLoadExemple/assets/Top arrow button.png b/GDJS/tests/games/savestate/SaveLoadExemple/assets/Top arrow button.png
new file mode 100644
index 000000000000..564ccf32c0e6
Binary files /dev/null and b/GDJS/tests/games/savestate/SaveLoadExemple/assets/Top arrow button.png differ
diff --git a/GDJS/tests/games/savestate/SaveLoadExemple/assets/stick.png b/GDJS/tests/games/savestate/SaveLoadExemple/assets/stick.png
new file mode 100644
index 000000000000..ed9394c37262
Binary files /dev/null and b/GDJS/tests/games/savestate/SaveLoadExemple/assets/stick.png differ
diff --git a/newIDE/app/public/res/actions/Save-single-action-down.svg b/newIDE/app/public/res/actions/Save-single-action-down.svg
new file mode 100644
index 000000000000..1715ec52a91e
--- /dev/null
+++ b/newIDE/app/public/res/actions/Save-single-action-down.svg
@@ -0,0 +1,17 @@
+<svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
+<g clip-path="url(#clip0_492_72)">
+<path d="M0 14H14V3L11 0H4.30769H2.625H0V14Z" fill="#3DB3E4"/>
+<path d="M12 8H2V13H12V8Z" fill="#404D9B"/>
+<path d="M11 6V1H6.49998V2.5H8.5L5.5 6H11Z" fill="white"/>
+<path d="M6 -2L6 3L7 3L4.5 6L2 3L3 3L3 -2L6 -2Z" fill="#404D9B"/>
+<g opacity="0.1">
+<path d="M14 14L0 0V14H14Z" fill="black"/>
+<path d="M14 3L11 0H0L14 14V3Z" fill="white"/>
+</g>
+</g>
+<defs>
+<clipPath id="clip0_492_72">
+<rect width="14" height="14" fill="white"/>
+</clipPath>
+</defs>
+</svg>
diff --git a/newIDE/app/public/res/actions/Save-single-action-up.svg b/newIDE/app/public/res/actions/Save-single-action-up.svg
new file mode 100644
index 000000000000..439266da20e8
--- /dev/null
+++ b/newIDE/app/public/res/actions/Save-single-action-up.svg
@@ -0,0 +1,17 @@
+<svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
+<g clip-path="url(#clip0_492_78)">
+<path d="M0 14H14V3L11 0H4.30769H2.625H0V14Z" fill="#3DB3E4"/>
+<path d="M2 9L2 4L1 4L3.5 1L6 4L5 4L5 9L2 9Z" fill="#404D9B"/>
+<path d="M12 8H2V13H12V8Z" fill="#404D9B"/>
+<path d="M11 6V1H4.5L7.5 4.5H6V6H11Z" fill="white"/>
+<g opacity="0.1">
+<path d="M14 14L0 0V14H14Z" fill="black"/>
+<path d="M14 3L11 0H0L14 14V3Z" fill="white"/>
+</g>
+</g>
+<defs>
+<clipPath id="clip0_492_78">
+<rect width="14" height="14" fill="white"/>
+</clipPath>
+</defs>
+</svg>
diff --git a/newIDE/app/src/JsExtensionsLoader/BrowserJsExtensionsLoader.js b/newIDE/app/src/JsExtensionsLoader/BrowserJsExtensionsLoader.js
index f9567e8b644d..0c24eac24450 100644
--- a/newIDE/app/src/JsExtensionsLoader/BrowserJsExtensionsLoader.js
+++ b/newIDE/app/src/JsExtensionsLoader/BrowserJsExtensionsLoader.js
@@ -41,6 +41,12 @@ const jsExtensions = [
     extensionModule: require('GDJS-for-web-app-only/Runtime/Extensions/DeviceVibration/JsExtension.js'),
     objectsRenderingServiceModules: {},
   },
+  {
+    name: 'SaveState',
+    // $FlowExpectedError - this path is ignored for Flow.
+    extensionModule: require('GDJS-for-web-app-only/Runtime/Extensions/SaveState/JsExtension.js'),
+    objectsRenderingServiceModules: {},
+  },
   {
     name: 'DebuggerTools',
     // $FlowExpectedError - this path is ignored for Flow.
diff --git a/newIDE/electron-app/UsersData.json b/newIDE/electron-app/UsersData.json
new file mode 100644
index 000000000000..6729aaa19a91
--- /dev/null
+++ b/newIDE/electron-app/UsersData.json
@@ -0,0 +1 @@
+{"gameNetworkSyncData":{"var":[],"ss":[{"name":"Game Scene","networkId":"aa913988"}],"extVar":{}},"layoutNetworkSyncDatas":[{"sceneData":{"var":[{"name":"ControlKind","value":"Keyboard","type":"string","owner":0},{"name":"IsCameraLocked","value":false,"type":"boolean","owner":0}],"extVar":{},"id":"aa913988"},"objectDatas":{"NewText":{"x":401,"y":64,"zo":12,"a":0,"hid":false,"lay":"","if":[],"pfx":0,"pfy":0,"beh":{},"var":[],"eff":{},"tim":{},"str":"85","o":255,"cs":200,"fn":"","b":false,"i":false,"u":false,"c":[255,0,213],"scale":1,"ta":"left","wrap":false,"wrapw":100,"oena":false,"ot":2,"oc":[255,255,255],"sh":false,"shc":[0,0,0],"sho":127,"shd":4,"sha":90,"shb":2,"pad":5},"Save":{"x":128,"y":512,"zo":13,"a":0,"hid":false,"lay":"","if":[],"pfx":0,"pfy":0,"beh":{},"var":[],"eff":{},"tim":{}},"load":{"x":768,"y":512,"zo":14,"a":0,"hid":false,"lay":"","if":[],"pfx":0,"pfy":0,"beh":{},"var":[],"eff":{},"tim":{}},"New3DModel":{"x":576,"y":5739.612360850002,"zo":15,"a":0,"hid":false,"lay":"","if":[],"pfx":0,"pfy":0,"beh":{"Object3D":{"act":true,"props":{}},"PlatformerObject":{"act":true,"props":{"cs":0,"rdx":0,"rdy":14.595000000000255,"ldy":14.595000000000255,"cfs":700,"cj":false,"ldl":false,"lek":false,"rik":false,"lak":false,"upk":false,"dok":false,"juk":false,"rpk":false,"rlk":false,"sn":"Falling","ssd":{}}}},"var":[{"name":"Variable","value":85,"type":"number","owner":0}],"eff":{},"tim":{},"z":0,"w":86.6025465,"h":100,"d":25,"rx":0,"ry":0,"flipX":false,"flipY":false,"flipZ":false,"mt":1,"op":null,"cp":null,"anis":[],"ai":0,"ass":1,"ap":false,"cfd":0.10000000149011612},"NewSprite":{"x":832,"y":67,"zo":16,"a":75.72697999999993,"hid":false,"lay":"","if":[],"pfx":0,"pfy":0,"beh":{},"var":[],"eff":{},"tim":{},"anim":{"an":0,"di":0,"fr":0,"et":0.08,"ss":1,"pa":false},"ifx":false,"ify":false,"sx":0.125,"sy":0.125,"op":255,"color":"255;255;255"},"BasicExplosionEnergy":{"x":128,"y":256,"zo":17,"a":0,"hid":false,"lay":"","if":[],"pfx":0,"pfy":0,"beh":{},"var":[],"eff":{},"tim":{},"prms":0,"prmx":0,"mpc":300,"addr":true,"angb":360,"formin":50,"formax":90,"zr":3,"ltmin":0.5,"ltmax":10,"gravx":0,"gravy":0,"color1":2681087,"color2":6357186,"size1":100,"size2":0,"alp1":204,"alp2":0,"flow":100,"tank":30,"text":"ExplosionFog-Texture-2.png","paused":false},"New3DBox":{"x":256,"y":320,"zo":18,"a":227.1809400000001,"hid":false,"lay":"","if":[],"pfx":0,"pfy":0,"beh":{"Déplaçable":{"act":true,"props":{}},"Object3D":{"act":true,"props":{}}},"var":[],"eff":{},"tim":{},"z":0,"w":100,"h":100,"d":100,"rx":0,"ry":0,"flipX":false,"flipY":false,"flipZ":false,"mt":0,"fo":"Y","bfu":"X","vfb":61,"trfb":0,"frn":["","","","","",""],"tint":"255;255;255"},"RedFlame":{"x":672,"y":415,"zo":19,"a":0,"hid":false,"lay":"","if":[],"pfx":0,"pfy":0,"beh":{"Object3D":{"act":true,"props":{}}},"var":[],"eff":{},"tim":{}}}}]}
\ No newline at end of file