Explorar el Código

[haxe] Initial commit

Mario Zechner hace 2 años
padre
commit
ec0ff67084
Se han modificado 100 ficheros con 20042 adiciones y 0 borrados
  1. 1 0
      .gitignore
  2. 53 0
      examples/export/runtimes.sh
  3. 20 0
      spine-haxe/.vscode/launch.json
  4. 54 0
      spine-haxe/README.md
  5. 259 0
      spine-haxe/example/assets/coin-pro.json
  6. BIN
      spine-haxe/example/assets/coin-pro.skel
  7. 18 0
      spine-haxe/example/assets/coin.atlas
  8. BIN
      spine-haxe/example/assets/coin.png
  9. 1079 0
      spine-haxe/example/assets/dragon-ess.json
  10. BIN
      spine-haxe/example/assets/dragon-ess.skel
  11. 123 0
      spine-haxe/example/assets/dragon.atlas
  12. BIN
      spine-haxe/example/assets/dragon.png
  13. BIN
      spine-haxe/example/assets/dragon_2.png
  14. BIN
      spine-haxe/example/assets/dragon_3.png
  15. BIN
      spine-haxe/example/assets/dragon_4.png
  16. BIN
      spine-haxe/example/assets/dragon_5.png
  17. BIN
      spine-haxe/example/assets/dragon_6.png
  18. 1232 0
      spine-haxe/example/assets/goblins-pro.json
  19. BIN
      spine-haxe/example/assets/goblins-pro.skel
  20. 96 0
      spine-haxe/example/assets/goblins.atlas
  21. BIN
      spine-haxe/example/assets/goblins.png
  22. 1285 0
      spine-haxe/example/assets/mix-and-match-pro.json
  23. BIN
      spine-haxe/example/assets/mix-and-match-pro.skel
  24. 362 0
      spine-haxe/example/assets/mix-and-match.atlas
  25. BIN
      spine-haxe/example/assets/mix-and-match.png
  26. 191 0
      spine-haxe/example/assets/owl-pro.json
  27. BIN
      spine-haxe/example/assets/owl-pro.skel
  28. 73 0
      spine-haxe/example/assets/owl.atlas
  29. BIN
      spine-haxe/example/assets/owl.png
  30. 692 0
      spine-haxe/example/assets/raptor-pro.json
  31. BIN
      spine-haxe/example/assets/raptor-pro.skel
  32. 100 0
      spine-haxe/example/assets/raptor.atlas
  33. BIN
      spine-haxe/example/assets/raptor.png
  34. 541 0
      spine-haxe/example/assets/spineboy-pro.json
  35. BIN
      spine-haxe/example/assets/spineboy-pro.skel
  36. 101 0
      spine-haxe/example/assets/spineboy.atlas
  37. BIN
      spine-haxe/example/assets/spineboy.png
  38. 512 0
      spine-haxe/example/assets/stretchyman-pro.json
  39. BIN
      spine-haxe/example/assets/stretchyman-pro.skel
  40. 18 0
      spine-haxe/example/assets/stretchyman.atlas
  41. BIN
      spine-haxe/example/assets/stretchyman.png
  42. 5020 0
      spine-haxe/example/assets/tank-pro.json
  43. BIN
      spine-haxe/example/assets/tank-pro.skel
  44. 56 0
      spine-haxe/example/assets/tank.atlas
  45. BIN
      spine-haxe/example/assets/tank.png
  46. 156 0
      spine-haxe/example/assets/vine-pro.json
  47. BIN
      spine-haxe/example/assets/vine-pro.skel
  48. 5 0
      spine-haxe/example/assets/vine.atlas
  49. BIN
      spine-haxe/example/assets/vine.png
  50. 77 0
      spine-haxe/example/src/Main.hx
  51. 27 0
      spine-haxe/haxelib.json
  52. 15 0
      spine-haxe/project.xml
  53. 93 0
      spine-haxe/spine-haxe/spine/BinaryInput.hx
  54. 28 0
      spine-haxe/spine-haxe/spine/BlendMode.hx
  55. 326 0
      spine-haxe/spine-haxe/spine/Bone.hx
  56. 55 0
      spine-haxe/spine-haxe/spine/BoneData.hx
  57. 101 0
      spine-haxe/spine-haxe/spine/Color.hx
  58. 17 0
      spine-haxe/spine-haxe/spine/ConstraintData.hx
  59. 31 0
      spine-haxe/spine-haxe/spine/Event.hx
  60. 30 0
      spine-haxe/spine-haxe/spine/EventData.hx
  61. 307 0
      spine-haxe/spine-haxe/spine/IkConstraint.hx
  62. 18 0
      spine-haxe/spine-haxe/spine/IkConstraintData.hx
  63. 11 0
      spine-haxe/spine-haxe/spine/Interpolation.hx
  64. 40 0
      spine-haxe/spine-haxe/spine/MathUtils.hx
  65. 571 0
      spine-haxe/spine-haxe/spine/PathConstraint.hx
  66. 28 0
      spine-haxe/spine-haxe/spine/PathConstraintData.hx
  67. 60 0
      spine-haxe/spine-haxe/spine/Polygon.hx
  68. 34 0
      spine-haxe/spine-haxe/spine/Pool.hx
  69. 5 0
      spine-haxe/spine-haxe/spine/Poolable.hx
  70. 24 0
      spine-haxe/spine-haxe/spine/PositionMode.hx
  71. 25 0
      spine-haxe/spine-haxe/spine/RotateMode.hx
  72. 612 0
      spine-haxe/spine-haxe/spine/Skeleton.hx
  73. 1205 0
      spine-haxe/spine-haxe/spine/SkeletonBinary.hx
  74. 319 0
      spine-haxe/spine-haxe/spine/SkeletonClipping.hx
  75. 175 0
      spine-haxe/spine-haxe/spine/SkeletonData.hx
  76. 1182 0
      spine-haxe/spine-haxe/spine/SkeletonJson.hx
  77. 209 0
      spine-haxe/spine-haxe/spine/Skin.hx
  78. 15 0
      spine-haxe/spine-haxe/spine/SkinEntry.hx
  79. 101 0
      spine-haxe/spine-haxe/spine/Slot.hx
  80. 48 0
      spine-haxe/spine-haxe/spine/SlotData.hx
  81. 26 0
      spine-haxe/spine-haxe/spine/SpacingMode.hx
  82. 259 0
      spine-haxe/spine-haxe/spine/TransformConstraint.hx
  83. 33 0
      spine-haxe/spine-haxe/spine/TransformConstraintData.hx
  84. 27 0
      spine-haxe/spine-haxe/spine/TransformMode.hx
  85. 283 0
      spine-haxe/spine-haxe/spine/Triangulator.hx
  86. 7 0
      spine-haxe/spine-haxe/spine/Updatable.hx
  87. 15 0
      spine-haxe/spine-haxe/spine/Vertex.hx
  88. 9 0
      spine-haxe/spine-haxe/spine/VertexEffect.hx
  89. 57 0
      spine-haxe/spine-haxe/spine/animation/AlphaTimeline.hx
  90. 72 0
      spine-haxe/spine-haxe/spine/animation/Animation.hx
  91. 686 0
      spine-haxe/spine-haxe/spine/animation/AnimationState.hx
  92. 47 0
      spine-haxe/spine-haxe/spine/animation/AnimationStateData.hx
  93. 60 0
      spine-haxe/spine-haxe/spine/animation/AttachmentTimeline.hx
  94. 5 0
      spine-haxe/spine-haxe/spine/animation/BoneTimeline.hx
  95. 104 0
      spine-haxe/spine-haxe/spine/animation/CurveTimeline.hx
  96. 51 0
      spine-haxe/spine-haxe/spine/animation/CurveTimeline1.hx
  97. 30 0
      spine-haxe/spine-haxe/spine/animation/CurveTimeline2.hx
  98. 279 0
      spine-haxe/spine-haxe/spine/animation/DeformTimeline.hx
  99. 63 0
      spine-haxe/spine-haxe/spine/animation/DrawOrderTimeline.hx
  100. 93 0
      spine-haxe/spine-haxe/spine/animation/EventQueue.hx

+ 1 - 0
.gitignore

@@ -193,3 +193,4 @@ spine-flutter/src/spine-cpp
 spine-godot/godot-nuget
 spine-godot/godot-spine-csharp
 spine-ue4/Plugins/Developer
+spine-haxe/export

+ 53 - 0
examples/export/runtimes.sh

@@ -418,6 +418,59 @@ cp -f ../mix-and-match/export/mix-and-match-pro.json "$ROOT/spine-monogame/spine
 cp -f ../mix-and-match/export/mix-and-match.atlas "$ROOT/spine-monogame/spine-monogame-example/data/"
 cp -f ../mix-and-match/export/mix-and-match.png "$ROOT/spine-monogame/spine-monogame-example/data/"
 
+echo "spine-haxe"
+rm "$ROOT/spine-haxe/example/assets/"*
+cp -f ../coin/export/coin-pro.json "$ROOT/spine-haxe/example/assets/"
+cp -f ../coin/export/coin-pro.skel "$ROOT/spine-haxe/example/assets/"
+cp -f ../coin/export/coin.atlas "$ROOT/spine-haxe/example/assets/"
+cp -f ../coin/export/coin.png "$ROOT/spine-haxe/example/assets/"
+
+cp -f ../goblins/export/goblins-pro.json "$ROOT/spine-haxe/example/assets/"
+cp -f ../goblins/export/goblins-pro.skel "$ROOT/spine-haxe/example/assets/"
+cp -f ../goblins/export/goblins.atlas "$ROOT/spine-haxe/example/assets/"
+cp -f ../goblins/export/goblins.png "$ROOT/spine-haxe/example/assets/"
+
+cp -f ../dragon/export/dragon-ess.json "$ROOT/spine-haxe/example/assets/"
+cp -f ../dragon/export/dragon-ess.skel "$ROOT/spine-haxe/example/assets/"
+cp -f ../dragon/export/dragon.atlas "$ROOT/spine-haxe/example/assets/"
+cp -f ../dragon/export/dragon*.png "$ROOT/spine-haxe/example/assets/"
+
+cp -f ../raptor/export/raptor-pro.json "$ROOT/spine-haxe/example/assets/"
+cp -f ../raptor/export/raptor-pro.skel "$ROOT/spine-haxe/example/assets/"
+cp -f ../raptor/export/raptor.atlas "$ROOT/spine-haxe/example/assets/"
+cp -f ../raptor/export/raptor.png "$ROOT/spine-haxe/example/assets/"
+
+cp -f ../spineboy/export/spineboy-pro.json "$ROOT/spine-haxe/example/assets/"
+cp -f ../spineboy/export/spineboy-pro.skel "$ROOT/spine-haxe/example/assets/"
+cp -f ../spineboy/export/spineboy.atlas "$ROOT/spine-haxe/example/assets/"
+cp -f ../spineboy/export/spineboy.png "$ROOT/spine-haxe/example/assets/"
+cp -f ../spineboy/export/spineboy.png "$ROOT/spine-haxe/example/assets/"
+
+cp -f ../tank/export/tank-pro.json "$ROOT/spine-haxe/example/assets/"
+cp -f ../tank/export/tank-pro.skel "$ROOT/spine-haxe/example/assets/"
+cp -f ../tank/export/tank.atlas "$ROOT/spine-haxe/example/assets/"
+cp -f ../tank/export/tank.png "$ROOT/spine-haxe/example/assets/"
+
+cp -f ../vine/export/vine-pro.json "$ROOT/spine-haxe/example/assets/"
+cp -f ../vine/export/vine-pro.skel "$ROOT/spine-haxe/example/assets/"
+cp -f ../vine/export/vine.atlas "$ROOT/spine-haxe/example/assets/"
+cp -f ../vine/export/vine.png "$ROOT/spine-haxe/example/assets/"
+
+cp -f ../owl/export/owl-pro.json "$ROOT/spine-haxe/example/assets/"
+cp -f ../owl/export/owl-pro.skel "$ROOT/spine-haxe/example/assets/"
+cp -f ../owl/export/owl.atlas "$ROOT/spine-haxe/example/assets/"
+cp -f ../owl/export/owl.png "$ROOT/spine-haxe/example/assets/"
+
+cp -f ../stretchyman/export/stretchyman-pro.json "$ROOT/spine-haxe/example/assets/"
+cp -f ../stretchyman/export/stretchyman-pro.skel "$ROOT/spine-haxe/example/assets/"
+cp -f ../stretchyman/export/stretchyman.atlas "$ROOT/spine-haxe/example/assets/"
+cp -f ../stretchyman/export/stretchyman.png "$ROOT/spine-haxe/example/assets/"
+
+cp -f ../mix-and-match/export/mix-and-match-pro.json "$ROOT/spine-haxe/example/assets/"
+cp -f ../mix-and-match/export/mix-and-match-pro.skel "$ROOT/spine-haxe/example/assets/"
+cp -f ../mix-and-match/export/mix-and-match.atlas "$ROOT/spine-haxe/example/assets/"
+cp -f ../mix-and-match/export/mix-and-match.png "$ROOT/spine-haxe/example/assets/"
+
 echo "spine-ue4"
 rm "$ROOT/spine-ue4/Content/GettingStarted/Assets/Raptor/raptor.json"
 rm "$ROOT/spine-ue4/Content/GettingStarted/Assets/Raptor/raptor.atlas"

+ 20 - 0
spine-haxe/.vscode/launch.json

@@ -0,0 +1,20 @@
+{
+  // Use IntelliSense to learn about possible attributes.
+  // Hover to view descriptions of existing attributes.
+  // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
+  "version": "0.2.0",
+  "configurations": [
+    {
+      "name": "web",
+      "request": "launch",
+      "type": "chrome",
+      "url": "http://localhost:3000",
+      "webRoot": "${workspaceFolder}"
+    },
+    {
+      "name": "lime",
+      "type": "lime",
+      "request": "launch"
+    }
+  ]
+}

+ 54 - 0
spine-haxe/README.md

@@ -0,0 +1,54 @@
+# spine-haxe
+
+The spine-haxe runtime provides functionality to load, manipulate and render [Spine](http://esotericsoftware.com) skeletal animation data using [Haxe](https://haxe.org/) in combination with [OpenFL](https://www.openfl.org/) and [Lime](https://lime.openfl.org/).
+
+## Licensing
+
+You are welcome to evaluate the Spine Runtimes and the examples we provide in this repository free of charge.
+
+You can integrate the Spine Runtimes into your software free of charge, but users of your software must have their own [Spine license](https://esotericsoftware.com/spine-purchase). Please make your users aware of this requirement! This option is often chosen by those making development tools, such as an SDK, game toolkit, or software library.
+
+In order to distribute your software containing the Spine Runtimes to others that don't have a Spine license, you need a [Spine license](https://esotericsoftware.com/spine-purchase) at the time of integration. Then you can distribute your software containing the Spine Runtimes however you like, provided others don't modify it or use it to create new software. If others want to do that, they'll need their own Spine license.
+
+For the official legal terms governing the Spine Runtimes, please read the [Spine Runtimes License Agreement](http://esotericsoftware.com/spine-runtimes-license) and Section 2 of the [Spine Editor License Agreement](http://esotericsoftware.com/spine-editor-license#s2).
+
+## Spine version
+
+spine-haxe works with data exported from Spine 4.1.xx.
+
+## Setup
+
+TBD
+
+## Example
+
+TBD
+
+## Development
+
+To setup the development environment install the following:
+
+1. [Haxe](https://haxe.org/download/), ensure it's available on the command line through your `PATH` if you use the binaries instead of the installer.
+2. On the command line, run:
+   ```
+   haxelib setup
+   haxelib install openfl
+   haxelib run openfl setup
+   haxelib install starling
+   ```
+3. Clone the `spine-runtimes` repository, and use `haxelib` to setup a dev library:
+   ```
+   git clone https://github.com/esotericsoftware/spine-runtimes
+   cd spine-runtimes
+   haxelib dev spine-haxe .
+   ```
+
+As an IDE, we recommend [Visual Studio Code](https://code.visualstudio.com/) with the following extension:
+
+1. [Haxe extension](https://marketplace.visualstudio.com/items?itemName=nadako.vshaxe)
+2. [HXCPP debugger extension](https://marketplace.visualstudio.com/items?itemName=vshaxe.hxcpp-debugger)
+3. [Lime extension](https://marketplace.visualstudio.com/items?itemName=openfl.lime-vscode-extension)
+
+The extensions provide IDE features like auto-completion, debugging, and build support.
+
+To debug the HTML5 build, set the Lime target in the status bar at the bottom of VS Code to `HTML5 / Debug`. Next, press `CTRL+SHIFT+B` (`CMD+SHIFT+B` on macOS) to build the project. Run the `lime` run configuration by pressing `F5`. This will start a webserver at `http://localhost:3000`. Finally, start the `web` run configuration. If you modify code, rebuild and restart the `web` configuration.

+ 259 - 0
spine-haxe/example/assets/coin-pro.json

@@ -0,0 +1,259 @@
+{
+"skeleton": {
+	"hash": "oo0dBSCqNHM",
+	"spine": "4.1.17",
+	"x": -152.5,
+	"y": -151,
+	"width": 305,
+	"height": 302,
+	"images": "./images/",
+	"audio": ""
+},
+"bones": [
+	{ "name": "root" },
+	{ "name": "coin-front", "parent": "root" },
+	{ "name": "clipping", "parent": "coin-front" },
+	{ "name": "coin-sides", "parent": "root" },
+	{ "name": "coin-side-round", "parent": "coin-sides" },
+	{ "name": "coin-side-straight", "parent": "coin-sides" },
+	{ "name": "shine", "parent": "root", "x": 243.14 }
+],
+"slots": [
+	{ "name": "coin-side", "bone": "coin-side-straight", "color": "ffdb2fff", "attachment": "coin-side-straight" },
+	{ "name": "coin-side-round", "bone": "coin-side-round", "color": "ffdb2fff", "attachment": "coin-side-round" },
+	{ "name": "coin-front-texture", "bone": "coin-front", "color": "868686ff", "attachment": "coin-front-logo" },
+	{ "name": "coin-front-shine", "bone": "coin-front", "color": "888888ff", "dark": "000000", "attachment": "coin-front-shine-logo", "blend": "additive" },
+	{ "name": "clipping", "bone": "clipping", "attachment": "clipping" },
+	{ "name": "shine", "bone": "shine", "color": "ffffff60", "attachment": "shine", "blend": "additive" }
+],
+"skins": [
+	{
+		"name": "default",
+		"attachments": {
+			"clipping": {
+				"clipping": {
+					"type": "clipping",
+					"end": "clipping",
+					"vertexCount": 39,
+					"vertices": [ 0.1, 140.26, -26.4, 138.14, -50.51, 131.25, -75.42, 119.06, -98.21, 101.04, -115.44, 82.22, -127.63, 62.08, -136.11, 39.03, -140.08, 19.68, -141.41, -0.19, -140.08, -22.98, -134.78, -45.5, -125.24, -66.44, -113.32, -84.19, -98.21, -101.95, -80.46, -116.52, -61.38, -127.39, -38.92, -134.81, -18.22, -139.27, -0.14, -140.58, 24.23, -138.48, 45.45, -132.46, 67.98, -122.5, 86.58, -110.19, 102.56, -95.25, 115.4, -78.75, 125.36, -61.72, 134, -42.33, 138.46, -22.15, 139.24, -0.15, 138.46, 20.29, 133.48, 39.94, 127.19, 58.54, 117.5, 76.1, 104.4, 92.86, 88.42, 108.32, 69.03, 121.42, 50.43, 130.85, 26.32, 137.4 ],
+					"color": "ce3a3aff"
+				}
+			},
+			"coin-front-shine": {
+				"coin-front-shine-logo": { "width": 282, "height": 282 },
+				"coin-front-shine-spineboy": { "width": 282, "height": 282 }
+			},
+			"coin-front-texture": {
+				"coin-front-logo": { "width": 305, "height": 302 },
+				"coin-front-spineboy": { "width": 305, "height": 302 }
+			},
+			"coin-side": {
+				"coin-side-straight": { "x": 0.5, "width": 17, "height": 282 }
+			},
+			"coin-side-round": {
+				"coin-side-round": { "x": -69.43, "width": 144, "height": 282 }
+			},
+			"shine": {
+				"shine": { "y": 0.5, "scaleX": 1.6004, "scaleY": 1.6004, "width": 72, "height": 245 }
+			}
+		}
+	}
+],
+"animations": {
+	"animation": {
+		"slots": {
+			"coin-front-shine": {
+				"rgba2": [
+					{ "light": "7d7d7dff", "dark": "000000" },
+					{ "time": 0.2667, "light": "000000ff", "dark": "7e7e7e" },
+					{ "time": 0.664, "light": "000000ff", "dark": "000000" },
+					{ "time": 1.0333, "light": "7f7f7fff", "dark": "000000" },
+					{ "time": 1.3333, "light": "404040ff", "dark": "000000" },
+					{ "time": 1.6, "light": "000000ff", "dark": "7e7e7e" },
+					{ "time": 2.0022, "light": "000000ff", "dark": "000000" },
+					{ "time": 2.4, "light": "7f7f7fff", "dark": "000000" },
+					{ "time": 2.6667, "light": "7d7d7dff", "dark": "000000" }
+				],
+				"attachment": [
+					{ "time": 0.6667, "name": "coin-front-shine-spineboy" },
+					{ "time": 2, "name": "coin-front-shine-logo" }
+				]
+			},
+			"coin-front-texture": {
+				"rgba": [
+					{ "color": "858585ff" },
+					{ "time": 0.4, "color": "ffffffff" },
+					{
+						"time": 0.6696,
+						"color": "858585ff",
+						"curve": [ 0.725, 0.59, 0.892, 1, 0.725, 0.59, 0.892, 1, 0.725, 0.59, 0.892, 1, 0.725, 1, 0.892, 1 ]
+					},
+					{ "time": 0.9667, "color": "ffffffff" },
+					{ "time": 1.3318, "color": "858585ff", "curve": "stepped" },
+					{ "time": 1.3333, "color": "858585ff" },
+					{ "time": 1.7333, "color": "ffffffff" },
+					{ "time": 1.9982, "color": "858585ff", "curve": "stepped" },
+					{ "time": 2.0022, "color": "858585ff" },
+					{ "time": 2.3, "color": "ffffffff" },
+					{ "time": 2.6667, "color": "858585ff" }
+				],
+				"attachment": [
+					{ "time": 0.6667, "name": "coin-front-spineboy" },
+					{ "time": 2, "name": "coin-front-logo" }
+				]
+			}
+		},
+		"bones": {
+			"coin-front": {
+				"translate": [
+					{},
+					{ "time": 0.664, "x": 8.3, "curve": "stepped" },
+					{
+						"time": 0.6696,
+						"x": -8.3,
+						"curve": [ 0.794, -7.08, 1.167, 0, 0.794, 0, 1.167, 0 ]
+					},
+					{ "time": 1.3333 },
+					{ "time": 1.9982, "x": 8.3, "curve": "stepped" },
+					{ "time": 2.0022, "x": -8.3 },
+					{ "time": 2.6667 }
+				],
+				"scale": [
+					{
+						"curve": [ 0.164, 1, 0.484, 0.091, 0.164, 1, 0.484, 1 ]
+					},
+					{ "time": 0.664, "x": 0, "curve": "stepped" },
+					{
+						"time": 0.6696,
+						"x": 0.003,
+						"curve": [ 0.786, 0.153, 1.167, 1, 0.786, 1, 1.167, 1 ]
+					},
+					{
+						"time": 1.3333,
+						"curve": [ 1.442, 0.992, 1.858, 0.098, 1.442, 1, 1.858, 1 ]
+					},
+					{ "time": 1.9982, "x": 0.003, "curve": "stepped" },
+					{
+						"time": 2.0022,
+						"x": 0.003,
+						"curve": [ 2.123, 0.151, 2.501, 1, 2.123, 1, 2.501, 1 ]
+					},
+					{ "time": 2.6667 }
+				]
+			},
+			"coin-side-round": {
+				"translate": [
+					{},
+					{ "time": 0.664, "x": -6.75, "curve": "stepped" },
+					{
+						"time": 0.6696,
+						"x": 7.03,
+						"curve": [ 0.794, 5.99, 1.167, 0, 0.794, 0, 1.167, 0 ]
+					},
+					{ "time": 1.3333 },
+					{ "time": 1.9982, "x": -6.75, "curve": "stepped" },
+					{ "time": 2.0022, "x": 7.03 },
+					{ "time": 2.6667 }
+				],
+				"scale": [
+					{
+						"curve": [ 0.085, 1, 0.207, 0.789, 0.085, 1, 0.207, 1 ]
+					},
+					{
+						"time": 0.3333,
+						"x": 0.555,
+						"curve": [ 0.449, 0.347, 0.567, 0.122, 0.449, 1, 0.567, 1 ]
+					},
+					{ "time": 0.664, "x": 0.014, "curve": "stepped" },
+					{
+						"time": 0.6696,
+						"x": -0.028,
+						"curve": [ 0.723, -0.126, 0.865, -0.367, 0.723, 1, 0.865, 1 ]
+					},
+					{
+						"time": 1,
+						"x": -0.609,
+						"curve": [ 1.053, -0.778, 1.29, -0.997, 1.053, 1, 1.29, 1 ]
+					},
+					{ "time": 1.3318, "x": -1, "curve": "stepped" },
+					{
+						"time": 1.3333,
+						"curve": [ 1.384, 0.997, 1.439, 0.94, 1.384, 1, 1.439, 1 ]
+					},
+					{
+						"time": 1.5,
+						"x": 0.852,
+						"curve": [ 1.564, 0.748, 1.703, 0.509, 1.564, 1, 1.703, 1 ]
+					},
+					{
+						"time": 1.8,
+						"x": 0.315,
+						"curve": [ 1.873, 0.13, 1.987, 0.015, 1.873, 1, 1.987, 1 ]
+					},
+					{ "time": 1.9982, "x": 0.014, "curve": "stepped" },
+					{
+						"time": 2.0022,
+						"x": -0.028,
+						"curve": [ 2.039, -0.072, 2.123, -0.239, 2.039, 1, 2.123, 1 ]
+					},
+					{
+						"time": 2.2018,
+						"x": -0.365,
+						"curve": [ 2.269, -0.513, 2.337, -0.635, 2.269, 1, 2.337, 1 ]
+					},
+					{
+						"time": 2.4,
+						"x": -0.731,
+						"curve": [ 2.503, -0.871, 2.596, -0.961, 2.503, 1, 2.596, 1 ]
+					},
+					{
+						"time": 2.6592,
+						"x": -1,
+						"curve": [ 2.661, -1, 2.665, 1, 2.661, 1, 2.665, 1 ]
+					},
+					{ "time": 2.6667 }
+				]
+			},
+			"shine": {
+				"translate": [
+					{
+						"curve": [ 0.167, 0, 0.5, -473.39, 0.167, 0, 0.5, 0 ]
+					},
+					{
+						"time": 0.6667,
+						"x": -473.39,
+						"curve": [ 0.833, -473.39, 1.167, -33.16, 0.833, 0, 1.167, 0 ]
+					},
+					{
+						"time": 1.3333,
+						"x": -33.16,
+						"curve": [ 1.5, -33.16, 1.833, -473.39, 1.5, 0, 1.833, 0 ]
+					},
+					{
+						"time": 2,
+						"x": -473.39,
+						"curve": [ 2.167, -473.39, 2.5, 0, 2.167, 0, 2.5, 0 ]
+					},
+					{ "time": 2.6667 }
+				]
+			}
+		},
+		"drawOrder": [
+			{
+				"time": 0.6667,
+				"offsets": [
+					{ "slot": "coin-side", "offset": 2 }
+				]
+			},
+			{ "time": 0.6696 },
+			{
+				"time": 1.9982,
+				"offsets": [
+					{ "slot": "coin-side", "offset": 2 }
+				]
+			},
+			{ "time": 2.0022 }
+		]
+	}
+}
+}

BIN
spine-haxe/example/assets/coin-pro.skel


+ 18 - 0
spine-haxe/example/assets/coin.atlas

@@ -0,0 +1,18 @@
+coin.png
+	size: 1024, 1024
+	filter: Linear, Linear
+coin-front-logo
+	bounds: 2, 570, 305, 302
+coin-front-shine-logo
+	bounds: 2, 286, 282, 282
+coin-front-shine-spineboy
+	bounds: 305, 283, 282, 282
+coin-front-spineboy
+	bounds: 309, 567, 305, 302
+	rotate: 90
+coin-side-round
+	bounds: 2, 2, 144, 282
+coin-side-straight
+	bounds: 286, 286, 17, 282
+shine
+	bounds: 148, 39, 72, 245

BIN
spine-haxe/example/assets/coin.png


+ 1079 - 0
spine-haxe/example/assets/dragon-ess.json

@@ -0,0 +1,1079 @@
+{
+"skeleton": {
+	"hash": "aPoA1GjXkVI",
+	"spine": "4.1.17",
+	"x": -366.31,
+	"y": -327.81,
+	"width": 660.39,
+	"height": 643,
+	"images": "./images/",
+	"audio": "././"
+},
+"bones": [
+	{ "name": "root", "y": -176.12 },
+	{ "name": "center", "parent": "root", "y": 176.12, "color": "ffe300ff" },
+	{
+		"name": "back",
+		"parent": "center",
+		"length": 115.38,
+		"rotation": 151.83,
+		"x": 16.04,
+		"y": 27.94,
+		"color": "ffe400ff"
+	},
+	{
+		"name": "chest",
+		"parent": "center",
+		"length": 31.24,
+		"rotation": 161.7,
+		"x": 52.53,
+		"y": 15.35,
+		"color": "ffe400ff"
+	},
+	{
+		"name": "neck",
+		"parent": "center",
+		"length": 41.37,
+		"rotation": 39.06,
+		"x": 64.76,
+		"y": 11.98,
+		"color": "ffe400ff"
+	},
+	{
+		"name": "chin",
+		"parent": "neck",
+		"length": 153.16,
+		"rotation": -69.07,
+		"x": 64.63,
+		"y": -6.99,
+		"color": "ffe400ff"
+	},
+	{
+		"name": "head",
+		"parent": "neck",
+		"length": 188.84,
+		"rotation": 8.07,
+		"x": 69.96,
+		"y": 2.5,
+		"color": "ffe400ff"
+	},
+	{
+		"name": "left-front-thigh",
+		"parent": "chest",
+		"length": 67.42,
+		"rotation": 138.94,
+		"x": -45.59,
+		"y": 7.93,
+		"color": "ff0000ff"
+	},
+	{
+		"name": "left-front-leg",
+		"parent": "left-front-thigh",
+		"length": 51.58,
+		"rotation": 43.36,
+		"x": 67.42,
+		"y": 0.03,
+		"color": "ff0000ff"
+	},
+	{
+		"name": "left-front-toe1",
+		"parent": "left-front-leg",
+		"length": 51.45,
+		"rotation": -98.01,
+		"x": 45.54,
+		"y": 2.43,
+		"color": "ff0000ff"
+	},
+	{
+		"name": "left-front-toe2",
+		"parent": "left-front-leg",
+		"length": 61.98,
+		"rotation": -55.26,
+		"x": 51.58,
+		"y": -0.13,
+		"color": "ff0000ff"
+	},
+	{
+		"name": "left-front-toe3",
+		"parent": "left-front-leg",
+		"length": 45.65,
+		"rotation": -11.14,
+		"x": 54.19,
+		"y": 0.6,
+		"scaleX": 1.135,
+		"color": "ff0000ff"
+	},
+	{
+		"name": "left-front-toe4",
+		"parent": "left-front-leg",
+		"length": 53.47,
+		"rotation": 19.43,
+		"x": 50.61,
+		"y": 7.09,
+		"scaleX": 1.135,
+		"color": "ff0000ff"
+	},
+	{
+		"name": "right-rear-thigh",
+		"parent": "back",
+		"length": 123.47,
+		"rotation": 104.88,
+		"x": 65.31,
+		"y": 59.89,
+		"color": "29ff00ff"
+	},
+	{
+		"name": "left-rear-thigh",
+		"parent": "right-rear-thigh",
+		"length": 88.06,
+		"rotation": 28.35,
+		"x": -8.59,
+		"y": 30.19,
+		"color": "ff0000ff"
+	},
+	{
+		"name": "left-rear-leg",
+		"parent": "left-rear-thigh",
+		"length": 103.74,
+		"rotation": -122.41,
+		"x": 96.04,
+		"y": -0.97,
+		"color": "ff0000ff"
+	},
+	{
+		"name": "left-wing",
+		"parent": "chest",
+		"length": 301.12,
+		"rotation": -75.51,
+		"x": -7.25,
+		"y": -24.66,
+		"color": "ff0000ff"
+	},
+	{
+		"name": "right-front-thigh",
+		"parent": "chest",
+		"length": 81.64,
+		"rotation": 67.97,
+		"x": -10.89,
+		"y": 28.25,
+		"color": "29ff00ff"
+	},
+	{
+		"name": "right-front-leg",
+		"parent": "right-front-thigh",
+		"length": 66.53,
+		"rotation": 92.7,
+		"x": 83.05,
+		"y": -0.31,
+		"color": "29ff00ff"
+	},
+	{
+		"name": "right-front-toe1",
+		"parent": "right-front-leg",
+		"length": 46.66,
+		"rotation": 8.59,
+		"x": 70.03,
+		"y": 5.31,
+		"color": "29ff00ff"
+	},
+	{
+		"name": "right-front-toe2",
+		"parent": "right-front-leg",
+		"length": 53.67,
+		"rotation": -35.02,
+		"x": 66.53,
+		"y": 0.34,
+		"color": "29ff00ff"
+	},
+	{
+		"name": "right-front-toe3",
+		"parent": "right-front-leg",
+		"length": 58.39,
+		"rotation": -74.67,
+		"x": 62.1,
+		"y": -0.79,
+		"color": "29ff00ff"
+	},
+	{
+		"name": "right-rear-leg",
+		"parent": "right-rear-thigh",
+		"length": 91.06,
+		"rotation": -129.04,
+		"x": 123.47,
+		"y": -0.27,
+		"color": "29ff00ff"
+	},
+	{
+		"name": "right-rear-toe1",
+		"parent": "right-rear-leg",
+		"length": 95,
+		"rotation": 141.98,
+		"x": 90.07,
+		"y": 2.12,
+		"color": "29ff00ff"
+	},
+	{
+		"name": "right-rear-toe2",
+		"parent": "right-rear-leg",
+		"length": 99.29,
+		"rotation": 125.32,
+		"x": 89.6,
+		"y": 1.52,
+		"color": "29ff00ff"
+	},
+	{
+		"name": "right-rear-toe3",
+		"parent": "right-rear-leg",
+		"length": 103.46,
+		"rotation": 112.27,
+		"x": 91.06,
+		"y": -0.35,
+		"color": "29ff00ff"
+	},
+	{
+		"name": "right-wing",
+		"parent": "head",
+		"length": 359.5,
+		"rotation": 83.21,
+		"x": -74.68,
+		"y": 20.91,
+		"color": "29ff00ff"
+	},
+	{
+		"name": "tail1",
+		"parent": "back",
+		"length": 65.65,
+		"rotation": 44.32,
+		"x": 115.38,
+		"y": -0.2,
+		"color": "ffe400ff"
+	},
+	{
+		"name": "tail2",
+		"parent": "tail1",
+		"length": 54.5,
+		"rotation": 12,
+		"x": 65.65,
+		"y": 0.23,
+		"color": "ffe400ff"
+	},
+	{
+		"name": "tail3",
+		"parent": "tail2",
+		"length": 41.78,
+		"rotation": 1.8,
+		"x": 54.5,
+		"y": 0.37,
+		"color": "ffe400ff"
+	},
+	{
+		"name": "tail4",
+		"parent": "tail3",
+		"length": 34.19,
+		"rotation": -1.8,
+		"x": 41.78,
+		"y": 0.16,
+		"color": "ffe400ff"
+	},
+	{
+		"name": "tail5",
+		"parent": "tail4",
+		"length": 32.33,
+		"rotation": -3.15,
+		"x": 34.19,
+		"y": -0.19,
+		"color": "ffe400ff"
+	},
+	{
+		"name": "tail6",
+		"parent": "tail5",
+		"length": 80.08,
+		"rotation": -29.55,
+		"x": 32.33,
+		"y": -0.23,
+		"color": "ffe400ff"
+	}
+],
+"slots": [
+	{ "name": "left-rear-leg", "bone": "left-rear-leg", "attachment": "left-rear-leg" },
+	{ "name": "left-rear-thigh", "bone": "left-rear-thigh", "attachment": "left-rear-thigh" },
+	{ "name": "left-wing", "bone": "left-wing", "attachment": "left-wing" },
+	{ "name": "tail6", "bone": "tail6", "attachment": "tail06" },
+	{ "name": "tail5", "bone": "tail5", "attachment": "tail05" },
+	{ "name": "tail4", "bone": "tail4", "attachment": "tail04" },
+	{ "name": "tail3", "bone": "tail3", "attachment": "tail03" },
+	{ "name": "tail2", "bone": "tail2", "attachment": "tail02" },
+	{ "name": "tail1", "bone": "tail1", "attachment": "tail01" },
+	{ "name": "back", "bone": "back", "attachment": "back" },
+	{ "name": "left-front-thigh", "bone": "left-front-thigh", "attachment": "left-front-thigh" },
+	{ "name": "left-front-leg", "bone": "left-front-leg", "attachment": "left-front-leg" },
+	{ "name": "left-front-toe1", "bone": "left-front-toe1", "attachment": "front-toe-a" },
+	{ "name": "left-front-toe4", "bone": "left-front-toe4", "attachment": "front-toe-b" },
+	{ "name": "left-front-toe3", "bone": "left-front-toe3", "attachment": "front-toe-b" },
+	{ "name": "left-front-toe2", "bone": "left-front-toe2", "attachment": "front-toe-b" },
+	{ "name": "chest", "bone": "chest", "attachment": "chest" },
+	{ "name": "right-rear-toe1", "bone": "right-rear-toe1", "attachment": "right-rear-toe" },
+	{ "name": "right-rear-toe2", "bone": "right-rear-toe2", "attachment": "right-rear-toe" },
+	{ "name": "right-rear-toe3", "bone": "right-rear-toe3", "attachment": "right-rear-toe" },
+	{ "name": "right-rear-leg", "bone": "right-rear-leg", "attachment": "right-rear-leg" },
+	{ "name": "right-rear-thigh", "bone": "right-rear-thigh", "attachment": "right-rear-thigh" },
+	{ "name": "right-front-toe1", "bone": "right-front-toe1", "attachment": "front-toe-b" },
+	{ "name": "right-front-thigh", "bone": "right-front-thigh", "attachment": "right-front-thigh" },
+	{ "name": "right-front-leg", "bone": "right-front-leg", "attachment": "right-front-leg" },
+	{ "name": "right-front-toe2", "bone": "right-front-toe2", "attachment": "front-toe-b" },
+	{ "name": "right-front-toe3", "bone": "right-front-toe3", "attachment": "front-toe-b" },
+	{ "name": "chin", "bone": "chin", "attachment": "chin" },
+	{ "name": "right-wing", "bone": "right-wing", "attachment": "right-wing" },
+	{ "name": "head", "bone": "head", "attachment": "head" },
+	{ "name": "thiagobrayner", "bone": "root", "attachment": "thiagobrayner" }
+],
+"skins": [
+	{
+		"name": "default",
+		"attachments": {
+			"back": {
+				"back": { "x": 35.85, "y": 19.99, "rotation": -151.83, "width": 190, "height": 185 }
+			},
+			"chest": {
+				"chest": { "x": -14.6, "y": 24.79, "rotation": -161.7, "width": 136, "height": 122 }
+			},
+			"chin": {
+				"chin": { "x": 66.55, "y": 7.32, "rotation": 30.01, "width": 214, "height": 146 }
+			},
+			"head": {
+				"head": { "x": 76.69, "y": 32.21, "rotation": -47.13, "width": 296, "height": 260 }
+			},
+			"left-front-leg": {
+				"left-front-leg": { "x": 14.69, "y": 0.49, "rotation": 16, "width": 84, "height": 57 }
+			},
+			"left-front-thigh": {
+				"left-front-thigh": { "x": 27.66, "y": -11.59, "rotation": 58.66, "width": 84, "height": 72 }
+			},
+			"left-front-toe1": {
+				"front-toe-a": { "x": 31.93, "y": 0.61, "rotation": 109.56, "width": 29, "height": 50 }
+			},
+			"left-front-toe2": {
+				"front-toe-b": { "x": 26.84, "y": -4.95, "rotation": 109.51, "width": 56, "height": 57 }
+			},
+			"left-front-toe3": {
+				"front-toe-b": {
+					"x": 18.22,
+					"y": -7.22,
+					"scaleX": 0.8811,
+					"scaleY": 0.9409,
+					"rotation": 99.71,
+					"width": 56,
+					"height": 57
+				}
+			},
+			"left-front-toe4": {
+				"front-toe-b": { "x": 23.21, "y": -11.69, "scaleX": 0.8811, "rotation": 79.89, "width": 56, "height": 57 }
+			},
+			"left-rear-leg": {
+				"left-rear-leg": { "x": 67.29, "y": 12.63, "rotation": -162.65, "width": 206, "height": 177 }
+			},
+			"left-rear-thigh": {
+				"left-rear-thigh": { "x": 56.03, "y": 27.39, "rotation": 74.94, "width": 91, "height": 149 }
+			},
+			"left-wing": {
+				"left-wing": {
+					"x": -36.32,
+					"y": -44.53,
+					"rotation": -83.7,
+					"width": 264,
+					"height": 589,
+					"sequence": { "count": 9, "digits": 2 }
+				}
+			},
+			"right-front-leg": {
+				"right-front-leg": { "x": 17.8, "y": 4.23, "rotation": 37.63, "width": 101, "height": 89 }
+			},
+			"right-front-thigh": {
+				"right-front-thigh": { "x": 35.29, "y": 2.11, "rotation": 130.33, "width": 108, "height": 108 }
+			},
+			"right-front-toe1": {
+				"front-toe-b": { "x": 24.5, "y": -2.61, "rotation": 104.18, "width": 56, "height": 57 }
+			},
+			"right-front-toe2": {
+				"front-toe-b": { "x": 26.39, "y": 1.17, "rotation": 104.58, "width": 56, "height": 57 }
+			},
+			"right-front-toe3": {
+				"front-toe-b": { "x": 30.67, "y": -0.07, "rotation": 112.3, "width": 56, "height": 57 }
+			},
+			"right-rear-leg": {
+				"right-rear-leg": { "x": 60.88, "y": -5.73, "rotation": -127.67, "width": 116, "height": 100 }
+			},
+			"right-rear-thigh": {
+				"right-rear-thigh": { "x": 53.25, "y": 12.58, "rotation": 103.29, "width": 91, "height": 149 }
+			},
+			"right-rear-toe1": {
+				"right-rear-toe": { "x": 54.76, "y": -5.72, "rotation": 134.79, "width": 109, "height": 77 }
+			},
+			"right-rear-toe2": {
+				"right-rear-toe": { "x": 57.03, "y": -7.23, "rotation": 134.43, "width": 109, "height": 77 }
+			},
+			"right-rear-toe3": {
+				"right-rear-toe": { "x": 47.46, "y": -7.64, "rotation": 134.34, "width": 109, "height": 77 }
+			},
+			"right-wing": {
+				"right-wing": {
+					"x": 35.09,
+					"y": 78.11,
+					"rotation": -130.34,
+					"width": 365,
+					"height": 643,
+					"sequence": { "count": 9, "digits": 2 }
+				}
+			},
+			"tail1": {
+				"tail01": { "x": 22.6, "y": -4.5, "rotation": 163.85, "width": 120, "height": 153 }
+			},
+			"tail2": {
+				"tail02": { "x": 18.12, "y": -1.75, "rotation": 151.85, "width": 95, "height": 120 }
+			},
+			"tail3": {
+				"tail03": { "x": 16.94, "y": -2.01, "rotation": 150.04, "width": 73, "height": 92 }
+			},
+			"tail4": {
+				"tail04": { "x": 15.35, "y": -2.18, "rotation": 151.85, "width": 56, "height": 71 }
+			},
+			"tail5": {
+				"tail05": { "x": 15.06, "y": -3.57, "rotation": 155, "width": 52, "height": 59 }
+			},
+			"tail6": {
+				"tail06": { "x": 28.02, "y": -16.83, "rotation": -175.45, "width": 95, "height": 68 }
+			},
+			"thiagobrayner": {
+				"thiagobrayner": { "y": -95, "width": 350, "height": 31 }
+			}
+		}
+	}
+],
+"animations": {
+	"flying": {
+		"bones": {
+			"back": {
+				"rotate": [
+					{
+						"curve": [ 0.042, 2.86, 0.143, 10.41 ]
+					},
+					{
+						"time": 0.1667,
+						"value": 11.59,
+						"curve": [ 0.245, 15.58, 0.383, 22.41 ]
+					},
+					{
+						"time": 0.5,
+						"value": 22.4,
+						"curve": [ 0.593, 22.39, 0.75, -3.18 ]
+					},
+					{
+						"time": 0.8333,
+						"value": -3.18,
+						"curve": [ 0.875, -3.18, 0.942, -2.19 ]
+					},
+					{ "time": 1 }
+				]
+			},
+			"neck": {
+				"rotate": [
+					{
+						"curve": [ 0.092, -0.1, 0.304, -21.22 ]
+					},
+					{
+						"time": 0.3333,
+						"value": -23.16,
+						"curve": [ 0.386, -26.7, 0.442, -31.89 ]
+					},
+					{
+						"time": 0.5333,
+						"value": -31.93,
+						"curve": [ 0.644, -31.97, 0.888, -0.1 ]
+					},
+					{ "time": 1 }
+				]
+			},
+			"right-rear-leg": {
+				"rotate": [
+					{
+						"curve": [ 0.054, -7.16, 0.227, -10.75 ]
+					},
+					{
+						"time": 0.3333,
+						"value": -10.75,
+						"curve": [ 0.417, -10.75, 0.583, 23.32 ]
+					},
+					{
+						"time": 0.6667,
+						"value": 23.32,
+						"curve": [ 0.792, 23.32, 0.881, 13.94 ]
+					},
+					{ "time": 1 }
+				]
+			},
+			"right-rear-toe3": {
+				"rotate": [
+					{
+						"value": -7.31,
+						"curve": [ 0.051, -3.08, 0.097, 0 ]
+					},
+					{
+						"time": 0.1333,
+						"curve": [ 0.258, 0, 0.508, -36.06 ]
+					},
+					{
+						"time": 0.6333,
+						"value": -36.06,
+						"curve": [ 0.723, -36.06, 0.875, -17.83 ]
+					},
+					{ "time": 1, "value": -7.31 }
+				]
+			},
+			"right-rear-toe2": {
+				"rotate": [
+					{
+						"value": -1.41,
+						"curve": [ 0.025, -0.6, 0.047, 0 ]
+					},
+					{
+						"time": 0.0667,
+						"curve": [ 0.192, 0, 0.442, -20.32 ]
+					},
+					{
+						"time": 0.5667,
+						"value": -20.32,
+						"curve": [ 0.673, -20.32, 0.868, -5.88 ]
+					},
+					{ "time": 1, "value": -1.41 }
+				]
+			},
+			"right-rear-toe1": {
+				"rotate": [
+					{
+						"curve": [ 0.125, 0, 0.375, -18.71 ]
+					},
+					{
+						"time": 0.5,
+						"value": -18.71,
+						"curve": [ 0.625, -18.71, 0.875, 0 ]
+					},
+					{ "time": 1 }
+				]
+			},
+			"head": {
+				"rotate": [
+					{
+						"curve": [ 0.125, 0, 0.375, 1.04 ]
+					},
+					{
+						"time": 0.5,
+						"value": 1.04,
+						"curve": [ 0.625, 1.04, 0.875, 0 ]
+					},
+					{ "time": 1 }
+				]
+			},
+			"chin": {
+				"rotate": [
+					{
+						"curve": [ 0.136, -10.78, 0.217, -12.01 ]
+					},
+					{
+						"time": 0.3333,
+						"value": -11.71,
+						"curve": [ 0.443, -11.42, 0.508, 21.91 ]
+					},
+					{
+						"time": 0.6667,
+						"value": 22.01,
+						"curve": [ 0.847, 22.13, 0.95, 5.91 ]
+					},
+					{ "time": 1 }
+				]
+			},
+			"left-front-thigh": {
+				"rotate": [
+					{
+						"value": -0.02,
+						"curve": [ 0.068, -0.02, 0.348, -32.09 ]
+					},
+					{
+						"time": 0.5,
+						"value": -32.02,
+						"curve": [ 0.653, -31.95, 0.906, 0 ]
+					},
+					{ "time": 1 }
+				]
+			},
+			"right-front-thigh": {
+				"rotate": [
+					{
+						"curve": [ 0.053, -5.52, 0.125, -12.96 ]
+					},
+					{
+						"time": 0.1667,
+						"value": -12.96,
+						"curve": [ 0.25, -12.96, 0.417, 16.2 ]
+					},
+					{
+						"time": 0.5,
+						"value": 16.2,
+						"curve": [ 0.625, 16.2, 0.866, 9.48 ]
+					},
+					{ "time": 1 }
+				]
+			},
+			"left-front-leg": {
+				"rotate": [
+					{
+						"curve": [ 0.09, -12.24, 0.131, -21.37 ]
+					},
+					{
+						"time": 0.2,
+						"value": -21.49,
+						"curve": [ 0.319, -21.94, 0.5, 77.25 ]
+					},
+					{
+						"time": 0.6,
+						"value": 77.25,
+						"curve": [ 0.7, 77.25, 0.908, 12.37 ]
+					},
+					{ "time": 1 }
+				]
+			},
+			"left-front-toe1": {
+				"rotate": [
+					{
+						"curve": [ 0.071, -4.48, 0.2, -12.68 ]
+					},
+					{
+						"time": 0.2667,
+						"value": -12.68,
+						"curve": [ 0.375, -12.68, 0.592, 6.99 ]
+					},
+					{
+						"time": 0.7,
+						"value": 6.99,
+						"curve": [ 0.775, 6.99, 0.904, 3.55 ]
+					},
+					{ "time": 1 }
+				]
+			},
+			"left-front-toe2": {
+				"scale": [
+					{
+						"curve": [ 0.125, 1, 0.375, 1.331, 0.125, 1, 0.375, 1.029 ]
+					},
+					{
+						"time": 0.5,
+						"x": 1.331,
+						"y": 1.029,
+						"curve": [ 0.625, 1.331, 0.875, 1, 0.625, 1.029, 0.875, 1 ]
+					},
+					{ "time": 1 }
+				]
+			},
+			"left-front-toe4": {
+				"rotate": [
+					{
+						"curve": [ 0.125, 0, 0.375, 26.52 ]
+					},
+					{
+						"time": 0.5,
+						"value": 26.52,
+						"curve": [ 0.625, 26.52, 0.875, 0 ]
+					},
+					{ "time": 1 }
+				],
+				"scale": [
+					{
+						"curve": [ 0.125, 1, 0.375, 1.211, 0.125, 1, 0.375, 0.993 ]
+					},
+					{
+						"time": 0.5,
+						"x": 1.211,
+						"y": 0.993,
+						"curve": [ 0.625, 1.211, 0.875, 1, 0.625, 0.993, 0.875, 1 ]
+					},
+					{ "time": 1 }
+				]
+			},
+			"left-front-toe3": {
+				"rotate": [
+					{
+						"curve": [ 0.125, 0, 0.375, 16.99 ]
+					},
+					{
+						"time": 0.5,
+						"value": 16.99,
+						"curve": [ 0.625, 16.99, 0.875, 0 ]
+					},
+					{ "time": 1 }
+				],
+				"scale": [
+					{
+						"curve": [ 0.125, 1, 0.375, 1.355, 0.125, 1, 0.375, 1.008 ]
+					},
+					{
+						"time": 0.5,
+						"x": 1.355,
+						"y": 1.008,
+						"curve": [ 0.625, 1.355, 0.875, 1, 0.625, 1.008, 0.875, 1 ]
+					},
+					{ "time": 1 }
+				]
+			},
+			"right-front-leg": {
+				"rotate": [
+					{
+						"curve": [ 0.11, -8.79, 0.179, -18.3 ]
+					},
+					{
+						"time": 0.2667,
+						"value": -18.31,
+						"curve": [ 0.352, -18.33, 0.55, 15.48 ]
+					},
+					{
+						"time": 0.6333,
+						"value": 15.48,
+						"curve": [ 0.758, 15.48, 0.875, 7.9 ]
+					},
+					{ "time": 1 }
+				]
+			},
+			"right-front-toe1": {
+				"rotate": [
+					{
+						"curve": [ 0.042, 0, 0.147, 8.28 ]
+					},
+					{
+						"time": 0.1667,
+						"value": 10.52,
+						"curve": [ 0.238, 18.41, 0.406, 35.06 ]
+					},
+					{
+						"time": 0.5,
+						"value": 34.84,
+						"curve": [ 0.577, 34.84, 0.669, 29.65 ]
+					},
+					{
+						"time": 0.7667,
+						"value": 20.24,
+						"curve": [ 0.852, 11.97, 0.941, 0 ]
+					},
+					{ "time": 1 }
+				],
+				"scale": [
+					{
+						"curve": [ 0.125, 1, 0.375, 1.412, 0.125, 1, 0.375, 1 ]
+					},
+					{
+						"time": 0.5,
+						"x": 1.412,
+						"curve": [ 0.625, 1.412, 0.875, 1, 0.625, 1, 0.875, 1 ]
+					},
+					{ "time": 1 }
+				]
+			},
+			"right-front-toe2": {
+				"rotate": [
+					{
+						"curve": [ 0.042, 0, 0.125, 24.7 ]
+					},
+					{
+						"time": 0.1667,
+						"value": 24.7,
+						"curve": [ 0.25, 24.7, 0.417, 7.35 ]
+					},
+					{
+						"time": 0.5,
+						"value": 7.35,
+						"curve": [ 0.544, 7.35, 0.671, 33.55 ]
+					},
+					{
+						"time": 0.7333,
+						"value": 32.36,
+						"curve": [ 0.853, 30.19, 0.919, 0 ]
+					},
+					{ "time": 1 }
+				],
+				"scale": [
+					{
+						"curve": [ 0.125, 1, 0.375, 1.407, 0.125, 1, 0.375, 1.058 ]
+					},
+					{
+						"time": 0.5,
+						"x": 1.407,
+						"y": 1.058,
+						"curve": [ 0.625, 1.407, 0.875, 1, 0.625, 1.058, 0.875, 1 ]
+					},
+					{ "time": 1 }
+				]
+			},
+			"right-front-toe3": {
+				"rotate": [
+					{
+						"curve": [ 0.033, 0, 0.1, 11.01 ]
+					},
+					{
+						"time": 0.1333,
+						"value": 11.01,
+						"curve": [ 0.208, 11.01, 0.358, -5.41 ]
+					},
+					{
+						"time": 0.4333,
+						"value": -5.41,
+						"curve": [ 0.508, -5.41, 0.658, 20.3 ]
+					},
+					{
+						"time": 0.7333,
+						"value": 20.3,
+						"curve": [ 0.8, 20.3, 0.933, 0 ]
+					},
+					{ "time": 1 }
+				],
+				"scale": [
+					{
+						"curve": [ 0.125, 1, 0.375, 1.33, 0.125, 1, 0.375, 1.182 ]
+					},
+					{
+						"time": 0.5,
+						"x": 1.33,
+						"y": 1.182,
+						"curve": [ 0.625, 1.33, 0.875, 1, 0.625, 1.182, 0.875, 1 ]
+					},
+					{ "time": 1 }
+				]
+			},
+			"right-wing": {
+				"rotate": [
+					{
+						"value": -9.59,
+						"curve": [ 0.131, -9.59, 0.191, 22.41 ]
+					},
+					{
+						"time": 0.3333,
+						"value": 22.81,
+						"curve": [ 0.417, 23.05, 0.533, 7.5 ]
+					},
+					{
+						"time": 0.5667,
+						"value": 2.07,
+						"curve": [ 0.588, -1.48, 0.639, -8.35 ]
+					},
+					{
+						"time": 0.7,
+						"value": -8.29,
+						"curve": [ 0.743, -8.24, 0.792, 5.28 ]
+					},
+					{
+						"time": 0.8333,
+						"value": 5.02,
+						"curve": [ 0.904, 4.59, 0.957, -9.72 ]
+					},
+					{ "time": 1, "value": -9.59 }
+				]
+			},
+			"left-wing": {
+				"rotate": [
+					{
+						"curve": [ 0.039, 0, 0.084, -21.1 ]
+					},
+					{
+						"time": 0.1333,
+						"value": -21.1,
+						"curve": [ 0.292, -21.1, 0.505, 4.34 ]
+					},
+					{
+						"time": 0.6333,
+						"value": 4.48,
+						"curve": [ 0.679, 4.48, 0.699, -6.03 ]
+					},
+					{
+						"time": 0.7667,
+						"value": -5.95,
+						"curve": [ 0.857, -5.84, 0.936, 0 ]
+					},
+					{ "time": 1 }
+				]
+			},
+			"left-rear-leg": {
+				"rotate": [
+					{
+						"curve": [ 0.08, 5.97, 0.25, 15.82 ]
+					},
+					{
+						"time": 0.3333,
+						"value": 15.82,
+						"curve": [ 0.425, 15.82, 0.608, -17.33 ]
+					},
+					{
+						"time": 0.7,
+						"value": -17.33,
+						"curve": [ 0.775, -17.33, 0.871, -8.98 ]
+					},
+					{ "time": 1 }
+				]
+			},
+			"center": {
+				"rotate": [
+					{
+						"curve": [ 0.072, 6.07, 0.213, 17.69 ]
+					},
+					{
+						"time": 0.3333,
+						"value": 17.69,
+						"curve": [ 0.454, 17.68, 0.583, -15 ]
+					},
+					{
+						"time": 0.6667,
+						"value": -15,
+						"curve": [ 0.804, -15, 0.966, -3.62 ]
+					},
+					{ "time": 1 }
+				],
+				"translate": [
+					{
+						"curve": [ 0.125, 0, 0.375, -0.01, 0.162, 0, 0.288, 144.81 ]
+					},
+					{
+						"time": 0.5,
+						"x": -0.01,
+						"y": 144.61,
+						"curve": [ 0.625, -0.01, 0.875, 0, 0.718, 145.5, 0.832, 0 ]
+					},
+					{ "time": 1 }
+				]
+			},
+			"tail1": {
+				"rotate": [
+					{
+						"value": -1.88,
+						"curve": [ 0.125, -1.88, 0.375, -67.4 ]
+					},
+					{
+						"time": 0.5,
+						"value": -67.4,
+						"curve": [ 0.625, -67.4, 0.875, -1.88 ]
+					},
+					{ "time": 1, "value": -1.88 }
+				]
+			},
+			"tail2": {
+				"rotate": [
+					{
+						"value": -27.45,
+						"curve": [ 0.125, -27.45, 0.375, 2.68 ]
+					},
+					{
+						"time": 0.5,
+						"value": 2.68,
+						"curve": [ 0.625, 2.68, 0.875, -27.45 ]
+					},
+					{ "time": 1, "value": -27.45 }
+				]
+			},
+			"tail3": {
+				"rotate": [
+					{
+						"value": -7.63,
+						"curve": [ 0.125, -7.63, 0.375, 24.54 ]
+					},
+					{
+						"time": 0.5,
+						"value": 24.54,
+						"curve": [ 0.625, 24.54, 0.875, -7.63 ]
+					},
+					{ "time": 1, "value": -7.63 }
+				]
+			},
+			"tail4": {
+				"rotate": [
+					{
+						"value": -10.04,
+						"curve": [ 0.125, -10.04, 0.375, 1.03 ]
+					},
+					{
+						"time": 0.5,
+						"value": 1.03,
+						"curve": [ 0.625, 1.03, 0.875, -10.04 ]
+					},
+					{ "time": 1, "value": -10.04 }
+				]
+			},
+			"tail5": {
+				"rotate": [
+					{
+						"value": -11.26,
+						"curve": [ 0.152, -11.21, 0.334, 9.91 ]
+					},
+					{
+						"time": 0.5,
+						"value": 9.93,
+						"curve": [ 0.662, 9.95, 0.844, -11.17 ]
+					},
+					{ "time": 1, "value": -11.26 }
+				]
+			},
+			"tail6": {
+				"rotate": [
+					{
+						"value": 26.68,
+						"curve": [ 0.194, 26.72, 0.328, 35.84 ]
+					},
+					{
+						"time": 0.5,
+						"value": 35.84,
+						"curve": [ 0.678, 35.84, 0.818, 26.62 ]
+					},
+					{ "time": 1, "value": 26.68 }
+				]
+			},
+			"right-rear-thigh": {
+				"rotate": [
+					{
+						"curve": [ 0.055, -6.4, 0.166, -22.66 ]
+					},
+					{
+						"time": 0.2667,
+						"value": -22.66,
+						"curve": [ 0.384, -22.66, 0.517, 19.08 ]
+					},
+					{
+						"time": 0.6333,
+						"value": 19.32,
+						"curve": [ 0.746, 19.56, 0.91, 9.17 ]
+					},
+					{ "time": 1 }
+				]
+			}
+		},
+		"attachments": {
+			"default": {
+				"left-wing": {
+					"left-wing": {
+						"sequence": [
+							{ "mode": "loop", "delay": 0.0667 },
+							{ "time": 0.6 },
+							{ "time": 0.7333, "mode": "loop", "index": 1 },
+							{ "time": 0.8, "mode": "loop", "index": 2, "delay": 0.0333 },
+							{ "time": 0.9667, "index": 7 }
+						]
+					}
+				},
+				"right-wing": {
+					"right-wing": {
+						"sequence": [
+							{ "mode": "loop", "delay": 0.0667 },
+							{ "time": 0.6 },
+							{ "time": 0.7333, "mode": "loop", "index": 1 },
+							{ "time": 0.8, "mode": "loop", "index": 2, "delay": 0.0333 },
+							{ "time": 0.9667, "index": 7 }
+						]
+					}
+				}
+			}
+		}
+	}
+}
+}

BIN
spine-haxe/example/assets/dragon-ess.skel


+ 123 - 0
spine-haxe/example/assets/dragon.atlas

@@ -0,0 +1,123 @@
+dragon.png
+	size: 1024, 1024
+	filter: Linear, Linear
+back
+	bounds: 564, 534, 190, 185
+chest
+	bounds: 2, 645, 136, 122
+chin
+	bounds: 140, 619, 214, 146
+front-toe-a
+	bounds: 2, 862, 29, 50
+	rotate: 90
+front-toe-b
+	bounds: 467, 835, 56, 57
+	rotate: 90
+head
+	bounds: 756, 398, 296, 260
+	rotate: 90
+left-front-leg
+	bounds: 599, 834, 84, 57
+left-front-thigh
+	bounds: 782, 819, 84, 72
+left-rear-leg
+	bounds: 356, 558, 206, 177
+left-rear-thigh
+	bounds: 216, 767, 91, 149
+	rotate: 90
+left-wing01
+	bounds: 2, 268, 264, 589
+	rotate: 90
+left-wing02
+	bounds: 2, 2, 264, 589
+	rotate: 90
+right-front-leg
+	bounds: 113, 769, 101, 89
+right-front-thigh
+	bounds: 758, 709, 108, 108
+right-rear-leg
+	bounds: 640, 721, 116, 100
+right-rear-thigh
+	bounds: 367, 742, 91, 149
+	rotate: 90
+right-rear-toe
+	bounds: 2, 781, 109, 77
+tail01
+	bounds: 868, 696, 120, 153
+	rotate: 90
+tail02
+	bounds: 518, 737, 95, 120
+	rotate: 90
+tail03
+	bounds: 868, 818, 73, 92
+	rotate: 90
+tail04
+	bounds: 526, 835, 56, 71
+	rotate: 90
+tail05
+	bounds: 406, 839, 52, 59
+	rotate: 90
+tail06
+	bounds: 685, 823, 95, 68
+thiagobrayner
+	bounds: 54, 860, 350, 31
+
+dragon_2.png
+	size: 1024, 1024
+	filter: Linear, Linear
+left-wing03
+	bounds: 2, 534, 264, 589
+	rotate: 90
+left-wing04
+	bounds: 2, 268, 264, 589
+	rotate: 90
+left-wing05
+	bounds: 593, 209, 264, 589
+left-wing06
+	bounds: 2, 2, 264, 589
+	rotate: 90
+
+dragon_3.png
+	size: 1024, 1024
+	filter: Linear, Linear
+left-wing07
+	bounds: 2, 694, 264, 589
+	rotate: 90
+left-wing08
+	bounds: 2, 428, 264, 589
+	rotate: 90
+left-wing09
+	bounds: 593, 369, 264, 589
+right-wing01
+	bounds: 2, 2, 365, 643
+	rotate: 90
+
+dragon_4.png
+	size: 1024, 1024
+	filter: Linear, Linear
+right-wing02
+	bounds: 2, 369, 365, 643
+right-wing03
+	bounds: 369, 369, 365, 643
+right-wing04
+	bounds: 2, 2, 365, 643
+	rotate: 90
+
+dragon_5.png
+	size: 1024, 1024
+	filter: Linear, Linear
+right-wing05
+	bounds: 2, 369, 365, 643
+right-wing06
+	bounds: 369, 369, 365, 643
+right-wing07
+	bounds: 2, 2, 365, 643
+	rotate: 90
+
+dragon_6.png
+	size: 1024, 1024
+	filter: Linear, Linear
+right-wing08
+	bounds: 2, 2, 365, 643
+right-wing09
+	bounds: 369, 2, 365, 643

BIN
spine-haxe/example/assets/dragon.png


BIN
spine-haxe/example/assets/dragon_2.png


BIN
spine-haxe/example/assets/dragon_3.png


BIN
spine-haxe/example/assets/dragon_4.png


BIN
spine-haxe/example/assets/dragon_5.png


BIN
spine-haxe/example/assets/dragon_6.png


+ 1232 - 0
spine-haxe/example/assets/goblins-pro.json

@@ -0,0 +1,1232 @@
+{
+"skeleton": {
+	"hash": "CdaHvXeCMjE",
+	"spine": "4.1.17",
+	"x": -134.12,
+	"y": -3.28,
+	"width": 266.94,
+	"height": 349.61,
+	"images": "./images/",
+	"audio": ""
+},
+"bones": [
+	{ "name": "root" },
+	{ "name": "hip", "parent": "root", "x": 0.65, "y": 114.41, "color": "ffcf00ff" },
+	{
+		"name": "torso",
+		"parent": "hip",
+		"length": 85.83,
+		"rotation": 93.93,
+		"x": -6.42,
+		"y": 1.98,
+		"color": "ffcf00ff"
+	},
+	{
+		"name": "neck",
+		"parent": "torso",
+		"length": 18.38,
+		"rotation": -1.52,
+		"x": 81.68,
+		"y": -6.35,
+		"color": "ffcf00ff"
+	},
+	{
+		"name": "head",
+		"parent": "neck",
+		"length": 68.29,
+		"rotation": -13.92,
+		"x": 20.94,
+		"y": 11.59,
+		"color": "ffcf00ff"
+	},
+	{
+		"name": "left-shoulder",
+		"parent": "torso",
+		"length": 35.43,
+		"rotation": -156.96,
+		"x": 74.05,
+		"y": -20.39,
+		"color": "ff0000ff"
+	},
+	{
+		"name": "left-arm",
+		"parent": "left-shoulder",
+		"length": 35.62,
+		"rotation": 28.17,
+		"x": 37.86,
+		"y": -2.35,
+		"color": "ff0000ff"
+	},
+	{
+		"name": "left-upper-leg",
+		"parent": "hip",
+		"length": 50.4,
+		"rotation": -89.1,
+		"x": 14.45,
+		"y": 2.81,
+		"color": "ff0000ff"
+	},
+	{
+		"name": "left-lower-leg",
+		"parent": "left-upper-leg",
+		"length": 49.9,
+		"rotation": -16.66,
+		"x": 56.34,
+		"y": 0.99,
+		"color": "ff0000ff"
+	},
+	{
+		"name": "left-foot",
+		"parent": "left-lower-leg",
+		"length": 46.5,
+		"rotation": 102.43,
+		"x": 58.94,
+		"y": -7.61,
+		"color": "ff0000ff"
+	},
+	{
+		"name": "left-hand",
+		"parent": "left-arm",
+		"length": 11.52,
+		"rotation": 2.7,
+		"x": 35.62,
+		"y": 0.08,
+		"color": "ff0000ff"
+	},
+	{ "name": "pelvis", "parent": "hip", "x": 1.41, "y": -6.58, "color": "ffcf00ff" },
+	{
+		"name": "right-shoulder",
+		"parent": "torso",
+		"length": 37.25,
+		"rotation": 133.89,
+		"x": 76.02,
+		"y": 18.15,
+		"color": "62ff00ff"
+	},
+	{
+		"name": "right-arm",
+		"parent": "right-shoulder",
+		"length": 36.75,
+		"rotation": 36.33,
+		"x": 37.61,
+		"y": 0.31,
+		"color": "62ff00ff"
+	},
+	{
+		"name": "right-upper-leg",
+		"parent": "hip",
+		"length": 42.46,
+		"rotation": -97.5,
+		"x": -20.08,
+		"y": -6.84,
+		"color": "62ff00ff"
+	},
+	{
+		"name": "right-lower-leg",
+		"parent": "right-upper-leg",
+		"length": 58.53,
+		"rotation": -14.34,
+		"x": 43,
+		"y": -0.62,
+		"color": "62ff00ff"
+	},
+	{
+		"name": "right-foot",
+		"parent": "right-lower-leg",
+		"length": 45.46,
+		"rotation": 110.31,
+		"x": 64.89,
+		"y": 0.04,
+		"color": "62ff00ff"
+	},
+	{
+		"name": "right-hand",
+		"parent": "right-arm",
+		"length": 15.32,
+		"rotation": 2.36,
+		"x": 36.9,
+		"y": 0.35,
+		"color": "62ff00ff"
+	},
+	{
+		"name": "spear1",
+		"parent": "left-hand",
+		"length": 65.07,
+		"rotation": 102.43,
+		"x": 0.48,
+		"y": 17.03,
+		"color": "ffcf00ff"
+	},
+	{
+		"name": "spear2",
+		"parent": "spear1",
+		"length": 61.42,
+		"rotation": 0.9,
+		"x": 65.06,
+		"y": 0.04,
+		"color": "ffcf00ff"
+	},
+	{
+		"name": "spear3",
+		"parent": "spear2",
+		"length": 76.8,
+		"rotation": -0.9,
+		"x": 61.89,
+		"y": 0.57,
+		"color": "ffcf00ff"
+	}
+],
+"slots": [
+	{ "name": "left-shoulder", "bone": "left-shoulder", "attachment": "left-shoulder" },
+	{ "name": "left-arm", "bone": "left-arm", "attachment": "left-arm" },
+	{ "name": "left-hand-item", "bone": "left-hand", "attachment": "spear" },
+	{ "name": "left-hand", "bone": "left-hand", "attachment": "left-hand" },
+	{ "name": "left-foot", "bone": "left-foot", "attachment": "left-foot" },
+	{ "name": "left-lower-leg", "bone": "left-lower-leg", "attachment": "left-lower-leg" },
+	{ "name": "left-upper-leg", "bone": "left-upper-leg", "attachment": "left-upper-leg" },
+	{ "name": "neck", "bone": "neck", "attachment": "neck" },
+	{ "name": "torso", "bone": "torso", "attachment": "torso" },
+	{ "name": "pelvis", "bone": "pelvis", "attachment": "pelvis" },
+	{ "name": "right-foot", "bone": "right-foot", "attachment": "right-foot" },
+	{ "name": "right-lower-leg", "bone": "right-lower-leg", "attachment": "right-lower-leg" },
+	{ "name": "undie-straps", "bone": "pelvis", "attachment": "undie-straps" },
+	{ "name": "undies", "bone": "pelvis", "attachment": "undies" },
+	{ "name": "right-upper-leg", "bone": "right-upper-leg", "attachment": "right-upper-leg" },
+	{ "name": "head", "bone": "head", "attachment": "head" },
+	{ "name": "eyes", "bone": "head" },
+	{ "name": "right-shoulder", "bone": "right-shoulder", "attachment": "right-shoulder" },
+	{ "name": "right-arm", "bone": "right-arm", "attachment": "right-arm" },
+	{ "name": "right-hand-thumb", "bone": "right-hand", "attachment": "right-hand-thumb" },
+	{ "name": "right-hand-item", "bone": "right-hand", "attachment": "dagger" },
+	{ "name": "right-hand", "bone": "right-hand", "attachment": "right-hand" },
+	{ "name": "right-hand-item2", "bone": "right-hand", "attachment": "shield" }
+],
+"skins": [
+	{
+		"name": "default",
+		"attachments": {
+			"left-hand-item": {
+				"dagger": { "x": 7.88, "y": -23.46, "rotation": 10.48, "width": 26, "height": 108 },
+				"spear": {
+					"type": "mesh",
+					"uvs": [ 1, 0.11236, 0.77096, 0.13278, 0.76608, 0.21781, 0.75642, 0.386, 0.74723, 0.54607, 0.72117, 1, 0.28838, 1, 0.24208, 0.54328, 0.2259, 0.38362, 0.20891, 0.21605, 0.20043, 0.13243, 0, 0.1152, 0.4527, 0, 0.58399, 0 ],
+					"triangles": [ 4, 7, 3, 6, 7, 4, 5, 6, 4, 10, 11, 12, 1, 13, 0, 12, 13, 1, 10, 12, 1, 9, 10, 1, 2, 9, 1, 8, 9, 2, 3, 8, 2, 7, 8, 3 ],
+					"vertices": [ 1, 20, 38.54, -10.89, 1, 1, 20, 30.97, -5.93, 1, 2, 19, 61.48, -5.59, 0.5116, 20, -0.31, -6.16, 0.4884, 2, 18, 64.73, -5.03, 0.50272, 19, -0.4, -5.07, 0.49728, 1, 10, 4.57, 23.91, 1, 1, 10, 41.7, -138.95, 1, 1, 10, 32.43, -141.1, 1, 1, 10, -6.49, 22.41, 1, 2, 18, 65.48, 6.65, 0.50272, 19, 0.53, 6.6, 0.49728, 2, 19, 62.19, 6.67, 0.5116, 20, 0.2, 6.1, 0.4884, 1, 20, 30.97, 6.62, 1, 1, 20, 37.26, 11.09, 1, 1, 20, 79.75, 1.6, 1, 1, 20, 79.78, -1.29, 1 ],
+					"hull": 14,
+					"edges": [ 24, 22, 22, 20, 10, 12, 2, 0, 24, 26, 0, 26, 8, 10, 12, 14, 6, 8, 14, 16, 2, 4, 4, 6, 16, 18, 18, 20, 20, 2 ],
+					"width": 22,
+					"height": 368
+				}
+			},
+			"right-hand-item": {
+				"dagger": {
+					"type": "mesh",
+					"uvs": [ 0.78092, 0.38453, 1, 0.38406, 1, 0.44882, 0.73954, 0.4687, 0.74642, 0.81344, 0.34023, 1, 0.15434, 1, 0.11304, 0.78858, 0.23007, 0.47368, 0, 0.45047, 0, 0.38622, 0.22368, 0.38573, 0.24384, 0, 1, 0 ],
+					"triangles": [ 0, 12, 13, 11, 12, 0, 0, 1, 2, 9, 10, 11, 3, 11, 0, 3, 0, 2, 8, 11, 3, 9, 11, 8, 5, 6, 7, 4, 5, 8, 4, 8, 3, 5, 7, 8 ],
+					"vertices": [ 15.49, -12.83, 21.14, -13.57, 20.16, -20.5, 13.16, -21.68, 8.13, -58.57, -5.14, -77.04, -9.92, -76.37, -7.8, -53.61, -0.03, -20.36, -5.61, -17.04, -4.64, -10.17, 1.13, -10.93, 7.47, 30.24, 26.93, 27.5 ],
+					"hull": 14,
+					"edges": [ 22, 20, 24, 26, 22, 24, 2, 0, 0, 22, 0, 26, 12, 14, 14, 16, 18, 20, 16, 18, 2, 4, 4, 6, 6, 8, 10, 12, 8, 10 ],
+					"width": 26,
+					"height": 108
+				}
+			},
+			"right-hand-item2": {
+				"shield": { "rotation": 93.5, "width": 70, "height": 72 }
+			}
+		}
+	},
+	{
+		"name": "goblin",
+		"attachments": {
+			"eyes": {
+				"eyes-closed": { "name": "goblin/eyes-closed", "x": 29.19, "y": -24.9, "rotation": -88.93, "width": 34, "height": 12 }
+			},
+			"head": {
+				"head": {
+					"name": "goblin/head",
+					"type": "mesh",
+					"uvs": [ 0, 0.60495, 0.14172, 0.51451, 0.24218, 0.55229, 0.32668, 0.67807, 0.37969, 0.79352, 0.53505, 0.93015, 0.86057, 1, 0.94071, 0.94169, 0.92099, 0.69924, 0.9888, 0.65498, 0.99003, 0.51643, 0.89633, 0.43562, 0.94487, 0.41917, 1, 0.39713, 1, 0.2836, 0.94017, 0.27027, 0.87906, 0.25666, 0.80755, 0.16045, 0.66699, 0.01998, 0.4734, 0.01806, 0.29215, 0.19893, 0.25393, 0.31824, 0.09117, 0.32401, 0, 0.44331, 0.43271, 0.69154, 0.46601, 0.47795, 0.35997, 0.31246, 0.73474, 0.68594, 0.72216, 0.57426, 0.8818, 0.5583, 0.80268, 0.51016 ],
+					"triangles": [ 5, 27, 6, 7, 27, 8, 7, 6, 27, 4, 24, 5, 5, 24, 27, 4, 3, 24, 27, 29, 8, 8, 29, 9, 24, 28, 27, 24, 25, 28, 24, 3, 25, 29, 28, 30, 29, 27, 28, 25, 2, 26, 25, 3, 2, 9, 29, 10, 0, 23, 1, 28, 25, 30, 29, 11, 10, 29, 30, 11, 2, 21, 26, 2, 1, 21, 23, 22, 1, 1, 22, 21, 30, 16, 11, 30, 17, 16, 30, 25, 17, 17, 26, 18, 18, 26, 19, 26, 17, 25, 11, 15, 12, 11, 16, 15, 12, 15, 13, 15, 14, 13, 21, 20, 26, 26, 20, 19 ],
+					"vertices": [ 14.56, 50.42, 23.12, 35.48, 17.47, 26.36, 11.58, 16.87, 3.75, 11.71, -5.9, -3.92, -11.83, -37.24, -8.32, -45.64, 7.75, -44.24, 10.4, -51.34, 19.53, -51.83, 25.21, -43.16, 26.13, -47.43, 27.36, -53.16, 34.84, -53.46, 35.97, -47.34, 37.11, -41.09, 43.75, -33.98, 53.59, -19.88, 54.51, 0.04, 43.32, 19.17, 35.61, 23.41, 35.89, 40.18, 28.4, 49.87, 10.26, 5.99, 24.21, 2, 35.55, 12.48, 9.39, -25.11, 16.8, -24.31, 17.21, -40.66, 20.69, -33.02 ],
+					"hull": 24,
+					"edges": [ 0, 2, 6, 8, 8, 10, 10, 12, 12, 14, 14, 16, 16, 18, 18, 20, 20, 22, 26, 28, 32, 34, 34, 36, 36, 38, 38, 40, 40, 42, 42, 44, 44, 46, 0, 46, 6, 48, 48, 50, 50, 52, 52, 42, 2, 4, 4, 6, 4, 52, 2, 44, 22, 32, 22, 24, 24, 26, 28, 30, 30, 32, 24, 30, 16, 54, 54, 56, 20, 58, 58, 54, 16, 58, 22, 60, 60, 56, 58, 60 ],
+					"width": 103,
+					"height": 66
+				}
+			},
+			"left-arm": {
+				"left-arm": {
+					"name": "goblin/left-arm",
+					"type": "mesh",
+					"uvs": [ 0.68993, 0.29284, 1, 0.46364, 1, 0.74644, 0.8409, 1, 0.66344, 1, 0.33766, 0.64284, 0, 0.44124, 0, 0, 0.34296, 0 ],
+					"triangles": [ 3, 4, 2, 4, 5, 2, 5, 0, 2, 0, 1, 2, 0, 5, 8, 5, 6, 8, 6, 7, 8 ],
+					"vertices": [ 18.6, 8.81, 32.2, 10.32, 38.02, 1.63, 38.08, -9.63, 32.32, -13.5, 14.37, -9.62, -0.76, -10.78, -9.85, 2.78, 1.29, 10.25 ],
+					"hull": 9,
+					"edges": [ 14, 16, 16, 0, 0, 2, 2, 4, 6, 4, 6, 8, 8, 10, 12, 14, 10, 12 ],
+					"width": 37,
+					"height": 35
+				}
+			},
+			"left-foot": {
+				"left-foot": {
+					"name": "goblin/left-foot",
+					"type": "mesh",
+					"uvs": [ 0.15734, 0.31874, 0.08195, 0.78503, 0.15884, 0.99367, 0.41633, 0.96805, 0.68823, 0.97637, 1, 0.96388, 0.99386, 0.73501, 0.85295, 0.51863, 0.61479, 0.31056, 0.46992, 0, 0.48033, 0.75604, 0.75995, 0.77706 ],
+					"triangles": [ 0, 9, 8, 10, 0, 8, 10, 8, 7, 11, 10, 7, 11, 7, 6, 1, 0, 10, 11, 6, 5, 3, 1, 10, 4, 10, 11, 4, 11, 5, 3, 10, 4, 2, 1, 3 ],
+					"vertices": [ 2.28, 13.07, -1.77, -1.64, 3.6, -7.81, 20.26, -6.04, 37.92, -5.28, 58.13, -3.71, 57.32, 3.35, 47.78, 9.51, 31.95, 15.05, 21.99, 24.12, 24.03, 0.76, 42.21, 1.16 ],
+					"hull": 10,
+					"edges": [ 0, 2, 2, 4, 4, 6, 6, 8, 8, 10, 10, 12, 12, 14, 14, 16, 16, 18, 0, 18, 6, 20, 20, 16, 2, 20, 8, 22, 22, 14, 20, 22, 22, 10 ],
+					"width": 65,
+					"height": 31
+				}
+			},
+			"left-hand": {
+				"left-hand": {
+					"name": "goblin/left-hand",
+					"type": "mesh",
+					"uvs": [ 0.51801, 0.12578, 1, 0.16286, 0.99789, 0.50578, 0.69745, 1, 0.37445, 1, 0, 0.80051, 0, 0.42793, 0.17601, 0, 0.43568, 0 ],
+					"triangles": [ 2, 0, 1, 0, 5, 6, 6, 7, 0, 0, 7, 8, 3, 4, 0, 4, 5, 0, 2, 3, 0 ],
+					"vertices": [ -3.11, 15.43, 10.84, 22.27, 15.5, 14.56, 18.36, -8.96, 9.48, -14.33, -4.59, -14.3, -11.64, -2.64, -14.89, 13.68, -7.76, 18 ],
+					"hull": 9,
+					"edges": [ 16, 0, 0, 2, 2, 4, 4, 6, 6, 8, 8, 10, 10, 12, 14, 16, 12, 14 ],
+					"width": 36,
+					"height": 41
+				}
+			},
+			"left-lower-leg": {
+				"left-lower-leg": {
+					"name": "goblin/left-lower-leg",
+					"type": "mesh",
+					"uvs": [ 0.95509, 0.2075, 0.81927, 0.65214, 0.94754, 0.77308, 0.67842, 0.97347, 0.46464, 1, 0.26845, 1, 0.04964, 0.90707, 0.21061, 0.60115, 0.07479, 0.40195, 0.18545, 0, 0.28858, 0 ],
+					"triangles": [ 10, 8, 9, 1, 7, 10, 7, 8, 10, 0, 1, 10, 1, 4, 7, 3, 1, 2, 5, 6, 7, 7, 4, 5, 1, 3, 4 ],
+					"vertices": [ -0.2, 6.82, 30.97, 10.96, 37.97, 17.34, 53.88, 12.6, 57.59, 6.32, 59.35, 0.09, 55.05, -8.63, 33, -9.34, 20.8, -17.44, -7.28, -21.57, -8.2, -18.29 ],
+					"hull": 11,
+					"edges": [ 20, 0, 0, 2, 2, 4, 4, 6, 6, 8, 8, 10, 10, 12, 12, 14, 14, 16, 18, 20, 16, 18 ],
+					"width": 33,
+					"height": 70
+				}
+			},
+			"left-shoulder": {
+				"left-shoulder": {
+					"name": "goblin/left-shoulder",
+					"type": "mesh",
+					"uvs": [ 0.7377, 0.40692, 1, 0.75238, 1, 1, 0.62046, 1, 0.26184, 0.56602, 0, 0.29783, 0, 0, 0.44115, 0 ],
+					"triangles": [ 3, 1, 2, 3, 0, 1, 3, 4, 0, 4, 7, 0, 4, 5, 7, 5, 6, 7 ],
+					"vertices": [ 15.18, 5.74, 32.17, 5.33, 41.79, 0.22, 36.63, -9.5, 14.89, -9.73, 0.9, -10.9, -10.67, -4.75, -4.67, 6.55 ],
+					"hull": 8,
+					"edges": [ 12, 14, 14, 0, 4, 2, 0, 2, 4, 6, 6, 8, 10, 12, 8, 10 ],
+					"width": 29,
+					"height": 44
+				}
+			},
+			"left-upper-leg": {
+				"left-upper-leg": {
+					"name": "goblin/left-upper-leg",
+					"type": "mesh",
+					"uvs": [ 1, 0.12168, 1, 0.54873, 0.91067, 0.78907, 0.76568, 1, 0.30871, 0.95791, 0, 0.68777, 0, 0.21901, 0.51962, 0, 0.87552, 0 ],
+					"triangles": [ 7, 8, 0, 5, 6, 7, 0, 1, 7, 4, 5, 7, 1, 4, 7, 2, 4, 1, 3, 4, 2 ],
+					"vertices": [ 2.34, 13.07, 33.51, 12.57, 51, 9.34, 66.32, 4.31, 63.01, -10.71, 43.13, -20.59, 8.91, -20.04, -6.8, -2.64, -6.61, 9.1 ],
+					"hull": 9,
+					"edges": [ 10, 8, 8, 6, 6, 4, 4, 2, 10, 12, 12, 14, 14, 16, 2, 0, 16, 0 ],
+					"width": 33,
+					"height": 73
+				}
+			},
+			"neck": {
+				"neck": {
+					"name": "goblin/neck",
+					"type": "mesh",
+					"uvs": [ 0.81968, 0.27365, 0.92101, 0.82048, 0.47135, 1, 0.15679, 0.93541, 0, 0.7556, 0.19268, 0.51834, 0.15468, 0.35707, 0, 0.2199, 0.13568, 0, 0.68879, 0, 0.70146, 0.53873 ],
+					"triangles": [ 3, 5, 2, 2, 10, 1, 2, 5, 10, 3, 4, 5, 10, 0, 1, 0, 10, 6, 10, 5, 6, 7, 8, 6, 6, 9, 0, 6, 8, 9 ],
+					"vertices": [ 18.63, -11.66, -3.98, -13.86, -10.29, 2.77, -6.92, 13.9, 0.8, 19.06, 10.06, 11.51, 16.75, 12.45, 22.72, 17.65, 31.4, 12.2, 30.12, -7.68, 8.05, -6.71 ],
+					"hull": 10,
+					"edges": [ 14, 12, 12, 10, 10, 8, 8, 6, 6, 4, 4, 2, 2, 20, 20, 0, 0, 18, 16, 18, 14, 16, 0, 2 ],
+					"width": 36,
+					"height": 41
+				}
+			},
+			"pelvis": {
+				"pelvis": {
+					"name": "goblin/pelvis",
+					"type": "mesh",
+					"uvs": [ 1, 1, 0, 1, 0, 0, 1, 0 ],
+					"triangles": [ 1, 2, 3, 1, 3, 0 ],
+					"vertices": [ 25.38, -20.73, -36.62, -20.73, -36.62, 22.27, 25.38, 22.27 ],
+					"hull": 4,
+					"edges": [ 0, 2, 2, 4, 4, 6, 0, 6 ],
+					"width": 62,
+					"height": 43
+				}
+			},
+			"right-arm": {
+				"right-arm": {
+					"name": "goblin/right-arm",
+					"type": "mesh",
+					"uvs": [ 1, 0.09223, 1, 0.8501, 0.72058, 1, 0.24385, 1, 0, 0.86559, 0.20823, 0.1092, 0.50903, 0, 0.85342, 0 ],
+					"triangles": [ 1, 2, 6, 6, 2, 5, 1, 6, 0, 4, 5, 3, 2, 3, 5, 6, 7, 0 ],
+					"vertices": [ -4.75, 8.89, 33.03, 11.74, 40.99, 5.9, 41.82, -5.03, 35.54, -11.13, -2.54, -9.2, -8.5, -2.72, -9.1, 5.18 ],
+					"hull": 8,
+					"edges": [ 8, 6, 4, 6, 4, 2, 12, 14, 2, 0, 14, 0, 10, 12, 8, 10 ],
+					"width": 23,
+					"height": 50
+				}
+			},
+			"right-foot": {
+				"right-foot": {
+					"name": "goblin/right-foot",
+					"type": "mesh",
+					"uvs": [ 0.40851, 0.00471, 0.59088, 0.33404, 0.75959, 0.48311, 0.88907, 0.59751, 0.97533, 0.89392, 0.90386, 1, 0.6722, 1, 0.38633, 1, 0.08074, 1, 0, 0.88921, 0, 0.65985, 0, 0.46578, 0.0906, 0.0988, 0.305, 0, 0.47461, 0.71258, 0.71501, 0.74681 ],
+					"triangles": [ 1, 10, 11, 1, 13, 0, 14, 1, 2, 1, 12, 13, 12, 1, 11, 14, 10, 1, 15, 14, 2, 15, 2, 3, 9, 10, 14, 15, 3, 4, 7, 8, 9, 14, 7, 9, 6, 14, 15, 5, 6, 15, 7, 14, 6, 4, 5, 15 ],
+					"vertices": [ 17.36, 26, 29.14, 15.44, 39.89, 10.81, 48.15, 7.25, 53.84, -2.38, 49.43, -6, 34.85, -6.39, 16.84, -6.87, -2.4, -7.38, -7.59, -3.87, -7.79, 3.7, -7.96, 10.1, -2.57, 22.36, 10.84, 25.98, 22.15, 2.76, 37.32, 2.03 ],
+					"hull": 14,
+					"edges": [ 0, 2, 6, 8, 8, 10, 16, 18, 22, 24, 24, 26, 0, 26, 10, 12, 2, 4, 4, 6, 12, 14, 14, 16, 18, 20, 20, 22, 2, 28, 28, 14, 20, 28, 4, 30, 30, 12, 28, 30, 30, 8 ],
+					"width": 63,
+					"height": 33
+				}
+			},
+			"right-hand": {
+				"right-hand": {
+					"name": "goblin/right-hand",
+					"type": "mesh",
+					"uvs": [ 0.17957, 0, 0, 0.44772, 0, 0.79734, 0.20057, 0.94264, 0.55057, 1, 0.8539, 1, 0.89824, 0.82005, 0.8259, 0.74286, 0.84224, 0.49994, 0.96357, 0.34102, 0.66024, 0 ],
+					"triangles": [ 8, 10, 9, 0, 10, 1, 8, 2, 1, 8, 1, 10, 7, 3, 8, 3, 2, 8, 4, 3, 7, 5, 7, 6, 4, 7, 5 ],
+					"vertices": [ -10.83, -9.45, 5.95, -15.35, 18.88, -14.9, 24.01, -7.5, 25.69, 5.16, 25.32, 16.08, 18.61, 17.44, 15.85, 14.74, 6.84, 15.02, 0.82, 19.19, -11.42, 7.84 ],
+					"hull": 11,
+					"edges": [ 0, 2, 2, 4, 4, 6, 6, 8, 8, 10, 10, 12, 12, 14, 14, 16, 16, 18, 18, 20, 0, 20 ],
+					"width": 36,
+					"height": 37
+				}
+			},
+			"right-hand-thumb": {
+				"right-hand-thumb": {
+					"name": "goblin/right-hand",
+					"type": "mesh",
+					"uvs": [ 0.88538, 0.22263, 0.76168, 0.3594, 0.75089, 0.78308, 0.95326, 0.84981, 1, 0.60303 ],
+					"triangles": [ 1, 0, 4, 2, 1, 4, 3, 2, 4 ],
+					"vertices": [ -2.82, 15.98, 2.4, 11.72, 18.08, 11.91, 20.28, 19.28, 11.09, 20.62 ],
+					"hull": 5,
+					"edges": [ 2, 4, 4, 6, 6, 8, 2, 0, 0, 8 ],
+					"width": 36,
+					"height": 37
+				}
+			},
+			"right-lower-leg": {
+				"right-lower-leg": {
+					"name": "goblin/right-lower-leg",
+					"type": "mesh",
+					"uvs": [ 1, 0.27261, 0.81312, 0.52593, 0.79587, 0.71796, 0.95544, 0.80989, 0.85194, 0.95493, 0.47242, 1, 0.14034, 1, 0, 0.8773, 0.14896, 0.67914, 0.1619, 0.30326, 0.60611, 0 ],
+					"triangles": [ 1, 10, 0, 9, 10, 1, 8, 9, 1, 2, 8, 1, 4, 2, 3, 6, 7, 8, 5, 6, 8, 2, 5, 8, 4, 5, 2 ],
+					"vertices": [ 6.27, 8.46, 23.32, 8.05, 37.1, 12.9, 41.45, 20.83, 53.07, 21.47, 61.33, 10.06, 65.78, -1.04, 59, -9.19, 43.02, -9.82, 16.33, -20.01, -12.8, -9.26 ],
+					"hull": 11,
+					"edges": [ 0, 2, 2, 4, 4, 6, 6, 8, 8, 10, 10, 12, 12, 14, 14, 16, 16, 18, 0, 20, 18, 20 ],
+					"width": 36,
+					"height": 76
+				}
+			},
+			"right-shoulder": {
+				"right-shoulder": {
+					"name": "goblin/right-shoulder",
+					"type": "mesh",
+					"uvs": [ 0.62008, 0.03709, 0.92131, 0.09049, 1, 0.3832, 0.72049, 0.69371, 0.31657, 1, 0, 1, 0, 0.75106, 0.28234, 0.49989 ],
+					"triangles": [ 4, 6, 7, 4, 7, 3, 4, 5, 6, 7, 0, 3, 2, 0, 1, 2, 3, 0 ],
+					"vertices": [ -3.17, -11.05, -9, -0.58, -1.01, 10.34, 16.69, 11.17, 37.41, 8.2, 45.46, -1.16, 36.96, -8.46, 21.21, -7.48 ],
+					"hull": 8,
+					"edges": [ 10, 12, 12, 14, 14, 0, 0, 2, 2, 4, 4, 6, 8, 10, 6, 8 ],
+					"width": 39,
+					"height": 45
+				}
+			},
+			"right-upper-leg": {
+				"right-upper-leg": {
+					"name": "goblin/right-upper-leg",
+					"type": "mesh",
+					"uvs": [ 0.27019, 0, 0.11619, 0.18177, 0, 0.70688, 0, 0.89577, 0.26669, 1, 0.48719, 1, 0.67619, 0.83533, 1, 0.5161, 1, 0.25544, 0.74619, 0.0571 ],
+					"triangles": [ 9, 8, 7, 9, 1, 0, 6, 9, 7, 6, 1, 9, 2, 1, 6, 4, 3, 2, 6, 4, 2, 5, 4, 6 ],
+					"vertices": [ -9.86, -10.37, 2.18, -14.07, 35.49, -13.67, 47.29, -12.11, 52.62, -2.27, 51.64, 5.17, 40.51, 10.18, 19.14, 18.47, 2.85, 16.33, -8.41, 6.14 ],
+					"hull": 10,
+					"edges": [ 0, 2, 2, 4, 4, 6, 6, 8, 8, 10, 10, 12, 12, 14, 14, 16, 16, 18, 0, 18 ],
+					"width": 34,
+					"height": 63
+				}
+			},
+			"torso": {
+				"torso": {
+					"name": "goblin/torso",
+					"type": "mesh",
+					"uvs": [ 0, 0.33288, 0.15946, 0.46489, 0.15762, 0.60314, 0.15502, 0.79806, 0.32808, 0.93479, 0.68751, 1, 0.80732, 1, 1, 0.77763, 1, 0.66148, 1, 0.56704, 0.93208, 0.4771, 0.86944, 0.39417, 0.83838, 0.22601, 0.68085, 0, 0.14836, 0, 0, 0.07199, 0.78735, 0.8625, 0.43679, 0.79649, 0.76738, 0.61733, 0.44345, 0.58747, 0.54329, 0.38317, 0.77692, 0.73447, 0.66479, 0.51012 ],
+					"triangles": [ 5, 16, 6, 6, 16, 7, 4, 17, 5, 5, 17, 16, 4, 3, 17, 17, 21, 16, 16, 21, 7, 3, 2, 17, 21, 19, 18, 21, 17, 19, 17, 2, 19, 21, 8, 7, 21, 18, 8, 18, 9, 8, 19, 22, 18, 18, 10, 9, 18, 22, 10, 2, 1, 19, 19, 20, 22, 19, 1, 20, 22, 11, 10, 22, 20, 11, 20, 1, 14, 20, 12, 11, 1, 0, 14, 20, 13, 12, 20, 14, 13, 0, 15, 14 ],
+					"vertices": [ 56.93, 27.95, 43.37, 18.24, 30.16, 19.5, 11.53, 21.29, -2.55, 10.69, -10.89, -13.12, -11.59, -21.24, 8.55, -36.13, 19.66, -37.09, 28.69, -37.86, 37.69, -34.01, 45.99, -30.45, 56.4, -29.07, 84.79, -20.92, 87.9, 15.15, 81.89, 25.8, 1.67, -21.02, 10.04, 2.19, 25.23, -18.25, 29.99, 0.01, 48.54, -8.4, 13.98, -21.37, 35.91, -15.6 ],
+					"hull": 16,
+					"edges": [ 0, 2, 6, 8, 8, 10, 10, 12, 12, 14, 22, 24, 24, 26, 26, 28, 28, 30, 0, 30, 14, 32, 32, 34, 34, 6, 18, 36, 36, 38, 2, 4, 4, 6, 38, 4, 2, 40, 40, 22, 40, 38, 38, 34, 32, 10, 34, 8, 40, 28, 14, 16, 16, 18, 32, 42, 42, 36, 16, 42, 42, 34, 18, 20, 20, 22, 36, 44, 44, 40, 20, 44 ],
+					"width": 68,
+					"height": 96
+				}
+			},
+			"undie-straps": {
+				"undie-straps": {
+					"name": "goblin/undie-straps",
+					"type": "mesh",
+					"uvs": [ 0.36098, 0.4496, 0.66298, 0.60591, 1, 0.19486, 1, 0.57118, 0.75898, 1, 0.38698, 1, 0, 0.26433, 0, 0, 0.12498, 0 ],
+					"triangles": [ 6, 7, 8, 6, 8, 0, 3, 1, 2, 5, 0, 1, 6, 0, 5, 4, 1, 3, 5, 1, 4 ],
+					"vertices": [ -10.56, 12.88, 6.54, 9.91, 25.62, 17.72, 25.62, 10.57, 11.97, 2.42, -9.09, 2.42, -31, 16.4, -31, 21.42, -23.93, 21.42 ],
+					"hull": 9,
+					"edges": [ 14, 16, 16, 0, 0, 2, 2, 4, 4, 6, 6, 8, 8, 10, 12, 14, 10, 12, 0, 10, 2, 8 ],
+					"width": 55,
+					"height": 19
+				}
+			},
+			"undies": {
+				"undies": {
+					"name": "goblin/undies",
+					"type": "mesh",
+					"uvs": [ 0, 0.3203, 0.14893, 0.59457, 0.22438, 1, 0.3591, 1, 0.50999, 1, 0.7956, 0.58454, 0.98421, 0.28016, 1, 0.00588, 0.46957, 0.17647, 0, 0.03933, 0.48843, 0.59123, 0.48115, 0.431 ],
+					"triangles": [ 6, 8, 7, 0, 9, 8, 11, 8, 6, 0, 8, 11, 5, 11, 6, 10, 11, 5, 1, 0, 11, 1, 11, 10, 3, 2, 1, 10, 3, 1, 4, 10, 5, 3, 10, 4 ],
+					"vertices": [ -13.22, 5.57, -8, -2.48, -5.49, -14.28, -0.64, -14.36, 4.79, -14.46, 15.28, -2.59, 22.22, 6.12, 22.93, 14.06, 3.75, 9.45, -13.08, 13.72, 4.22, -2.59, 4.04, 2.06 ],
+					"hull": 10,
+					"edges": [ 0, 2, 2, 4, 8, 10, 10, 12, 12, 14, 14, 16, 16, 18, 0, 18, 4, 6, 6, 8, 6, 20, 16, 22, 22, 20, 0, 22, 22, 12, 2, 20, 20, 10 ],
+					"width": 36,
+					"height": 29
+				}
+			}
+		}
+	},
+	{
+		"name": "goblingirl",
+		"attachments": {
+			"eyes": {
+				"eyes-closed": { "name": "goblingirl/eyes-closed", "x": 28, "y": -25.55, "rotation": -87.05, "width": 37, "height": 21 }
+			},
+			"head": {
+				"head": { "name": "goblingirl/head", "x": 27.72, "y": -4.32, "rotation": -85.58, "width": 103, "height": 81 }
+			},
+			"left-arm": {
+				"left-arm": { "name": "goblingirl/left-arm", "x": 19.64, "y": -2.43, "rotation": 33.05, "width": 37, "height": 35 }
+			},
+			"left-foot": {
+				"left-foot": { "name": "goblingirl/left-foot", "type": "linkedmesh", "skin": "goblin", "parent": "left-foot", "width": 65, "height": 31 }
+			},
+			"left-hand": {
+				"left-hand": {
+					"name": "goblingirl/left-hand",
+					"x": 4.34,
+					"y": 2.39,
+					"scaleX": 0.8965,
+					"scaleY": 0.8965,
+					"rotation": 30.35,
+					"width": 35,
+					"height": 40
+				}
+			},
+			"left-lower-leg": {
+				"left-lower-leg": { "name": "goblingirl/left-lower-leg", "x": 25.02, "y": -0.61, "rotation": 105.76, "width": 33, "height": 70 }
+			},
+			"left-shoulder": {
+				"left-shoulder": { "name": "goblingirl/left-shoulder", "x": 19.81, "y": -0.43, "rotation": 61.22, "width": 28, "height": 46 }
+			},
+			"left-upper-leg": {
+				"left-upper-leg": { "name": "goblingirl/left-upper-leg", "x": 30.22, "y": -2.95, "rotation": 89.1, "width": 33, "height": 70 }
+			},
+			"neck": {
+				"neck": { "name": "goblingirl/neck", "x": 6.16, "y": -3.15, "rotation": -98.86, "width": 35, "height": 41 }
+			},
+			"pelvis": {
+				"pelvis": { "name": "goblingirl/pelvis", "x": -3.88, "y": 3.19, "width": 62, "height": 43 }
+			},
+			"right-arm": {
+				"right-arm": { "name": "goblingirl/right-arm", "x": 16.85, "y": -0.66, "rotation": 93.53, "width": 28, "height": 50 }
+			},
+			"right-foot": {
+				"right-foot": { "name": "goblingirl/right-foot", "type": "linkedmesh", "skin": "goblin", "parent": "right-foot", "width": 63, "height": 33 }
+			},
+			"right-hand": {
+				"right-hand": { "name": "goblingirl/right-hand", "x": 7.22, "y": 3.44, "rotation": 91.17, "width": 36, "height": 37 }
+			},
+			"right-hand-thumb": {
+				"right-hand-thumb": { "name": "goblingirl/right-hand", "x": 7.22, "y": 3.44, "rotation": 91.17, "width": 36, "height": 37 }
+			},
+			"right-lower-leg": {
+				"right-lower-leg": { "name": "goblingirl/right-lower-leg", "x": 26.15, "y": -3.28, "rotation": 111.84, "width": 36, "height": 76 }
+			},
+			"right-shoulder": {
+				"right-shoulder": { "name": "goblingirl/right-shoulder", "x": 14.46, "y": 0.46, "rotation": 129.85, "width": 39, "height": 45 }
+			},
+			"right-upper-leg": {
+				"right-upper-leg": { "name": "goblingirl/right-upper-leg", "x": 19.7, "y": 2.13, "rotation": 97.5, "width": 34, "height": 63 }
+			},
+			"torso": {
+				"torso": { "name": "goblingirl/torso", "x": 36.28, "y": -5.14, "rotation": -95.75, "width": 68, "height": 96 }
+			},
+			"undie-straps": {
+				"undie-straps": { "name": "goblingirl/undie-straps", "x": -1.52, "y": 14.19, "width": 55, "height": 19 }
+			},
+			"undies": {
+				"undies": { "name": "goblingirl/undies", "x": 5.4, "y": 1.71, "width": 36, "height": 29 }
+			}
+		}
+	}
+],
+"animations": {
+	"walk": {
+		"slots": {
+			"eyes": {
+				"attachment": [
+					{ "time": 0.7, "name": "eyes-closed" },
+					{ "time": 0.8 }
+				]
+			}
+		},
+		"bones": {
+			"left-upper-leg": {
+				"rotate": [
+					{ "value": -26.56 },
+					{ "time": 0.1333, "value": -8.79 },
+					{ "time": 0.2333, "value": 9.51 },
+					{ "time": 0.3667, "value": 30.74 },
+					{ "time": 0.5, "value": 25.34 },
+					{ "time": 0.6333, "value": 26.12 },
+					{ "time": 0.7333, "value": 7.45 },
+					{ "time": 0.8667, "value": -21.19 },
+					{ "time": 1, "value": -26.56 }
+				],
+				"translate": [
+					{ "x": -1.32, "y": 1.71 },
+					{ "time": 0.3667, "x": -0.06, "y": 2.43 },
+					{ "time": 1, "x": -1.32, "y": 1.71 }
+				]
+			},
+			"right-upper-leg": {
+				"rotate": [
+					{ "value": 42.45 },
+					{
+						"time": 0.1333,
+						"value": 49.86,
+						"curve": [ 0.175, 49.86, 0.204, 22.69 ]
+					},
+					{ "time": 0.2333, "value": 22.51 },
+					{ "time": 0.5, "value": -16.94 },
+					{ "time": 0.6333, "value": 1.9 },
+					{
+						"time": 0.7333,
+						"value": 34.87,
+						"curve": [ 0.795, 37.71, 0.867, 58.69 ]
+					},
+					{
+						"time": 0.8667,
+						"value": 58.69,
+						"curve": [ 0.933, 58.35, 1, 42.45 ]
+					},
+					{ "time": 1, "value": 42.45 }
+				],
+				"translate": [
+					{ "x": 6.24 },
+					{ "time": 0.2333, "x": 2.14, "y": 2.4 },
+					{ "time": 0.5, "x": 2.44, "y": 4.8 },
+					{ "time": 1, "x": 6.24 }
+				]
+			},
+			"left-lower-leg": {
+				"rotate": [
+					{ "value": -18.05 },
+					{ "time": 0.1333, "value": -63.51 },
+					{ "time": 0.2333, "value": -83.02 },
+					{ "time": 0.5, "value": 5.12 },
+					{ "time": 0.6333, "value": -28.3 },
+					{ "time": 0.7333, "value": -27.52 },
+					{ "time": 0.8667, "value": 3.53 },
+					{ "time": 1, "value": -18.05 }
+				],
+				"translate": [
+					{},
+					{ "time": 0.2333, "x": 2.56, "y": -0.47 },
+					{ "time": 0.5 }
+				]
+			},
+			"left-foot": {
+				"rotate": [
+					{ "value": -14.57 },
+					{ "time": 0.1333, "value": -10.42 },
+					{ "time": 0.2333, "value": -5.01 },
+					{ "time": 0.3, "value": 6.67 },
+					{ "time": 0.3667, "value": 3.87 },
+					{ "time": 0.5, "value": -3.88 },
+					{ "time": 0.6333, "value": 2.78 },
+					{ "time": 0.7333, "value": -12 },
+					{ "time": 0.8667, "value": -12.45 },
+					{ "time": 1, "value": -14.57 }
+				]
+			},
+			"right-shoulder": {
+				"rotate": [
+					{
+						"value": 5.29,
+						"curve": [ 0.167, 5.29, 0.475, 6.65 ]
+					},
+					{ "time": 0.6333, "value": 6.65 },
+					{ "time": 1, "value": 5.29 }
+				]
+			},
+			"right-arm": {
+				"rotate": [
+					{
+						"value": -4.03,
+						"curve": [ 0.169, -3.91, 0.51, 19.66 ]
+					},
+					{
+						"time": 0.6333,
+						"value": 19.79,
+						"curve": [ 0.746, 19.75, 0.922, -3.91 ]
+					},
+					{ "time": 1, "value": -4.03 }
+				]
+			},
+			"right-hand": {
+				"rotate": [
+					{ "value": 8.99 },
+					{ "time": 0.6333, "value": 0.51 },
+					{ "time": 1, "value": 8.99 }
+				]
+			},
+			"left-shoulder": {
+				"rotate": [
+					{
+						"value": 6.26,
+						"curve": [ 0.17, 6.26, 0.342, -11.79 ]
+					},
+					{
+						"time": 0.5,
+						"value": -11.79,
+						"curve": [ 0.641, -11.79, 0.843, 6.16 ]
+					},
+					{ "time": 1, "value": 6.26 }
+				],
+				"translate": [
+					{ "x": 1.15, "y": 0.24 }
+				]
+			},
+			"left-hand": {
+				"rotate": [
+					{
+						"value": -21.24,
+						"curve": [ 0.148, -21.24, 0.378, -27.21 ]
+					},
+					{
+						"time": 0.5,
+						"value": -27.28,
+						"curve": [ 0.621, -27.28, 0.875, -21.4 ]
+					},
+					{ "time": 1, "value": -21.24 }
+				]
+			},
+			"left-arm": {
+				"rotate": [
+					{
+						"value": 28.38,
+						"curve": [ 0.17, 28.38, 0.342, 60.09 ]
+					},
+					{
+						"time": 0.5,
+						"value": 60.09,
+						"curve": [ 0.641, 60.09, 0.843, 28.54 ]
+					},
+					{ "time": 1, "value": 28.38 }
+				]
+			},
+			"torso": {
+				"rotate": [
+					{ "value": -10.28 },
+					{
+						"time": 0.1333,
+						"value": -15.39,
+						"curve": [ 0.261, -15.36, 0.324, -9.78 ]
+					},
+					{
+						"time": 0.3667,
+						"value": -9.78,
+						"curve": [ 0.521, -10.8, 0.545, -15.72 ]
+					},
+					{
+						"time": 0.6333,
+						"value": -15.75,
+						"curve": [ 0.688, -15.66, 0.819, -7.07 ]
+					},
+					{
+						"time": 0.8667,
+						"value": -7.07,
+						"curve": [ 0.895, -7.07, 0.975, -10.25 ]
+					},
+					{ "time": 1, "value": -10.28 }
+				]
+			},
+			"right-foot": {
+				"rotate": [
+					{ "value": -5.25 },
+					{ "time": 0.2333, "value": -17.77 },
+					{ "time": 0.3667, "value": -20.1 },
+					{ "time": 0.5, "value": -19.74 },
+					{ "time": 0.7333, "value": -11.69 },
+					{ "time": 0.8, "value": 4.47 },
+					{ "time": 0.8667, "value": 0.46 },
+					{ "time": 1, "value": -5.25 }
+				]
+			},
+			"right-lower-leg": {
+				"rotate": [
+					{
+						"value": -3.39,
+						"curve": [ 0.042, -4.01, 0.099, -42.81 ]
+					},
+					{
+						"time": 0.1333,
+						"value": -43.22,
+						"curve": [ 0.175, -43.22, 0.204, -26.09 ]
+					},
+					{ "time": 0.2333, "value": -25.98 },
+					{ "time": 0.5, "value": -19.53 },
+					{ "time": 0.6333, "value": -64.8 },
+					{
+						"time": 0.7333,
+						"value": -89.54,
+						"curve": [ 0.882, -74, 1, -3.39 ]
+					},
+					{ "time": 1, "value": -3.39 }
+				],
+				"translate": [
+					{ "time": 0.5 },
+					{ "time": 0.6333, "x": 2.19, "y": 0.21 },
+					{ "time": 1 }
+				]
+			},
+			"hip": {
+				"translate": [
+					{ "y": -8.4 },
+					{
+						"time": 0.1333,
+						"y": -9.35,
+						"curve": [ 0.166, 0, 0.201, 0, 0.166, -8.91, 0.201, -1.14 ]
+					},
+					{
+						"time": 0.2333,
+						"y": -0.59,
+						"curve": [ 0.277, 0, 0.319, 0, 0.277, -1.92, 0.319, -2.96 ]
+					},
+					{ "time": 0.3667, "y": -3.97 },
+					{ "time": 0.5, "y": -8.4 },
+					{
+						"time": 0.6333,
+						"y": -10.01,
+						"curve": [ 0.669, 0, 0.698, 0, 0.669, -7.79, 0.698, -6.49 ]
+					},
+					{
+						"time": 0.7333,
+						"y": -5.3,
+						"curve": [ 0.756, 0, 0.778, 0, 0.756, -4.27, 0.778, -3.35 ]
+					},
+					{
+						"time": 0.8,
+						"y": -2.5,
+						"curve": [ 0.821, 0, 0.843, 0, 0.821, -3.02, 0.843, -3.5 ]
+					},
+					{ "time": 0.8667, "y": -3.97 },
+					{ "time": 1, "y": -8.4 }
+				]
+			},
+			"neck": {
+				"rotate": [
+					{ "value": 3.6 },
+					{ "time": 0.1333, "value": 17.5 },
+					{ "time": 0.2333, "value": 6.11 },
+					{ "time": 0.3667, "value": 3.46 },
+					{ "time": 0.5, "value": 5.18 },
+					{ "time": 0.6333, "value": 18.36 },
+					{ "time": 0.7333, "value": 6.09 },
+					{ "time": 0.8667, "value": 2.29 },
+					{ "time": 1, "value": 3.6 }
+				]
+			},
+			"head": {
+				"rotate": [
+					{
+						"value": 3.6,
+						"curve": [ 0, 3.6, 0.094, -0.89 ]
+					},
+					{ "time": 0.1333, "value": -0.21 },
+					{ "time": 0.2333, "value": 6.11 },
+					{ "time": 0.3667, "value": 3.46 },
+					{
+						"time": 0.5,
+						"value": 5.18,
+						"curve": [ 0.5, 5.18, 0.617, -1.4 ]
+					},
+					{ "time": 0.6667, "value": 1.11 },
+					{ "time": 0.7333, "value": 6.09 },
+					{ "time": 0.8667, "value": 2.29 },
+					{ "time": 1, "value": 3.6 }
+				]
+			},
+			"pelvis": {
+				"rotate": [
+					{ "value": -1.34 }
+				],
+				"translate": [
+					{ "x": 0.39, "y": -0.78 }
+				]
+			},
+			"spear1": {
+				"rotate": [
+					{ "value": 1.85 },
+					{ "time": 0.2, "value": -5.39 },
+					{ "time": 0.5, "value": 2.95 },
+					{ "time": 0.7333, "value": -3.67 },
+					{ "time": 1, "value": 1.85 }
+				]
+			},
+			"spear2": {
+				"rotate": [
+					{ "value": 1.85 },
+					{ "time": 0.2, "value": -5.39 },
+					{ "time": 0.5, "value": 2.95 },
+					{ "time": 0.7333, "value": -3.67 },
+					{ "time": 1, "value": 1.85 }
+				]
+			},
+			"spear3": {
+				"rotate": [
+					{ "value": 3.64 },
+					{ "time": 0.2, "value": -3.6 },
+					{ "time": 0.5, "value": 4.74 },
+					{ "time": 0.7333, "value": -1.88 },
+					{ "time": 1, "value": 3.64 }
+				]
+			}
+		},
+		"attachments": {
+			"default": {
+				"right-hand-item": {
+					"dagger": {
+						"deform": [
+							{
+								"offset": 26,
+								"vertices": [ 2.34755, 0.1447 ],
+								"curve": [ 0.125, 0, 0.375, 1 ]
+							},
+							{
+								"time": 0.5,
+								"offset": 8,
+								"vertices": [ -1.19415, 4.31532, 0.07279, 6.41351, 1.66048, 6.18883, 1.75233, 3.59555 ],
+								"curve": [ 0.625, 0, 0.875, 1 ]
+							},
+							{
+								"time": 1,
+								"offset": 26,
+								"vertices": [ 2.34755, 0.1447 ]
+							}
+						]
+					}
+				}
+			},
+			"goblin": {
+				"head": {
+					"head": {
+						"deform": [
+							{
+								"curve": [ 0.127, 0, 0.15, 1 ]
+							},
+							{
+								"time": 0.2,
+								"vertices": [ -10.97827, -6.68962, -4.68015, -2.46175, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1.08534, 0.08392, -1.08534, 0.08392, -1.08534, 0.08392, 0, 0, -2.22325, 2.66465, -4.83295, 2.70085, -5.70553, -0.51941, -3.15962, -1.61502, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -6.64742, 0.81612, -11.82286, -1.34955, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1.08534, 0.08392 ],
+								"curve": [ 0.242, 0, 0.325, 1 ]
+							},
+							{
+								"time": 0.3667,
+								"vertices": [ 10.69276, 4.05949, 3.66373, 1.85427, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1.47305, 0.09018, 1.47305, 0.09018, 1.47305, 0.09018, 0, 0, 2.69653, -0.22738, 3.77135, 0.11418, 3.6893, 1.55352, 2.49595, 1.65501, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4.45881, -3.9113, 9.19594, -1.66854, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1.47305, 0.09018 ],
+								"curve": [ 0.574, 0, 0.617, 1 ]
+							},
+							{
+								"time": 0.7,
+								"vertices": [ -10.97827, -6.68962, -4.68015, -2.46175, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1.17551, -0.17183, -1.17551, -0.17183, -1.17551, -0.17183, 0, 0, -2.22325, 2.66465, -4.83295, 2.70085, -5.70553, -0.51941, -3.15962, -1.61502, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -6.64742, 0.81612, -11.82286, -1.34955, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1.17551, -0.17183 ],
+								"curve": [ 0.742, 0, 0.825, 1 ]
+							},
+							{
+								"time": 0.8667,
+								"vertices": [ 10.69276, 4.05949, 3.66373, 1.85427, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.38687, 0.08446, 0.38687, 0.08446, 0.38687, 0.08446, 0, 0, 2.69653, -0.22738, 3.77135, 0.11418, 3.6893, 1.55352, 2.49595, 1.65501, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4.45881, -3.9113, 9.19594, -1.66854, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.38687, 0.08446 ],
+								"curve": [ 0.9, 0, 0.967, 1 ]
+							},
+							{ "time": 1 }
+						]
+					}
+				},
+				"left-foot": {
+					"left-foot": {
+						"deform": [
+							{
+								"offset": 8,
+								"vertices": [ 3.69298, 2.37573, -7.16969, 18.79733, -12.78162, 14.7778, -12.75776, 6.50514, -3.13476, 1.98906, -0.44402, 0.36629, 0, 0, -3.80085, 2.98474 ]
+							},
+							{ "time": 0.1333 },
+							{
+								"time": 0.2333,
+								"offset": 8,
+								"vertices": [ -3.96073, -2.34594, -5.80446, -12.47629, -2.2313, -12.99038, 2.02942, -9.1036, 0, 0, 0, 0, 0, 0, -1.35254, -5.2883 ]
+							},
+							{
+								"time": 0.3667,
+								"offset": 8,
+								"vertices": [ 0.66505, 0.33548, 0.33902, 2.69014, -0.48171, 2.54524, -1.13593, 1.38562, 0, 0, 0, 0, 0, 0, -0.11908, 0.79273 ]
+							},
+							{ "time": 0.5, "curve": "stepped" },
+							{ "time": 0.6333 },
+							{
+								"time": 0.7333,
+								"offset": 8,
+								"vertices": [ -2.97738, 9.40254, -6.91661, 19.92794, -10.55287, 18.41085, -12.37161, 12.38473, -4.72607, 6.30799, 0, 0, -1.48902, 4.88944, -7.06773, 10.70102 ]
+							},
+							{
+								"time": 0.8333,
+								"offset": 6,
+								"vertices": [ 1.05319, 1.56362, -2.52723, 7.9974, -5.52031, 17.14137, -8.93317, 15.79635, -10.73748, 10.22056, -4.23801, 5.36992, 0, 0, 0, 0, -5.83148, 8.55532 ]
+							},
+							{
+								"time": 1,
+								"offset": 8,
+								"vertices": [ 3.69298, 2.37573, -7.16969, 18.79733, -12.78162, 14.7778, -12.75776, 6.50514, -3.13476, 1.98906, -0.44402, 0.36629, 0, 0, -3.80085, 2.98474 ]
+							}
+						]
+					}
+				},
+				"pelvis": {
+					"pelvis": {
+						"deform": [
+							{},
+							{
+								"time": 0.1333,
+								"offset": 6,
+								"vertices": [ -0.6899, -4.13284 ]
+							},
+							{
+								"time": 0.3333,
+								"offset": 6,
+								"vertices": [ -1.04945, -3.10477 ]
+							},
+							{
+								"time": 0.7,
+								"offset": 6,
+								"vertices": [ -1.4245, -6.30617 ]
+							},
+							{
+								"time": 0.8667,
+								"offset": 6,
+								"vertices": [ -1.13542, -1.79036 ]
+							},
+							{ "time": 1 }
+						]
+					}
+				},
+				"right-foot": {
+					"right-foot": {
+						"deform": [
+							{},
+							{
+								"time": 0.1333,
+								"offset": 2,
+								"vertices": [ -2.81259, 2.63115, -2.35238, 3.89441, -1.99921, 4.8639, -0.93273, 5.57982, -0.48886, 5.09855, -0.34813, 3.42912, -0.17446, 1.36899, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1.31305, 1.91372, -1.32986, 3.65703 ]
+							},
+							{
+								"time": 0.2333,
+								"offset": 2,
+								"vertices": [ -6.39088, 6.41246, -7.74575, 8.27192, -7.02471, 11.35894, -4.03471, 13.93454, -2.50399, 12.62963, -1.46125, 7.58915, -0.17446, 1.36899, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -3.84766, 2.61216, -4.53956, 7.92358 ]
+							},
+							{
+								"time": 0.3,
+								"offset": 2,
+								"vertices": [ -8.27185, 6.68822, -9.29764, 10.13797, -8.62231, 14.7134, -4.5863, 18.81939, -2.20304, 17.10709, -0.07795, 9.9046, 2.54452, 1.01642, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -2.94625, 2.38008, -4.59399, 10.01888 ]
+							},
+							{
+								"time": 0.3667,
+								"offset": 2,
+								"vertices": [ -10.47684, 9.44176, -13.36883, 12.40983, -14.32569, 16.94392, -9.24463, 23.55674, -5.51712, 21.51378, -1.19582, 11.53193, 2.54452, 1.01642, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -4.14848, 2.29389, -6.63419, 11.37127 ]
+							},
+							{
+								"time": 0.5,
+								"offset": 2,
+								"vertices": [ -5.42474, 4.36854, -10.59004, 7.04468, -11.64251, 11.55845, -6.19665, 20.12806, -1.45498, 18.05411, 4.8662, 6.41679, 2.81463, 0.27601, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -2.96412, 4.9483 ]
+							},
+							{ "time": 0.6333 },
+							{
+								"time": 0.7333,
+								"offset": 4,
+								"vertices": [ 1.31462, -6.84099, -0.87905, -12.54479, -5.9851, -14.08368, -7.15892, -11.63194, -5.6792, -4.83545, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -2.06164, -6.93844 ]
+							},
+							{
+								"time": 0.8,
+								"offset": 4,
+								"vertices": [ 0.65731, -3.4205, -0.43953, -6.2724, -2.99255, -7.04184, -3.57946, -5.81597, -2.8396, -2.41772, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2.79688, -1.28021, 0, 0, 0, 0, -1.03082, -3.46922 ]
+							},
+							{ "time": 0.8667 }
+						]
+					}
+				},
+				"right-hand": {
+					"right-hand": {
+						"deform": [
+							{
+								"offset": 4,
+								"vertices": [ -1.48417, 0.34736, 0, 0, 1.31152, 0.08085, 1.60296, 0.09882, 0.13673, 0.15471, 0, 0, 0, 0, -0.72862, -0.0449 ]
+							},
+							{ "time": 0.5 },
+							{
+								"time": 1,
+								"offset": 4,
+								"vertices": [ -1.48417, 0.34736, 0, 0, 1.31152, 0.08085, 1.60296, 0.09882, 0.13673, 0.15471, 0, 0, 0, 0, -0.72862, -0.0449 ]
+							}
+						]
+					}
+				},
+				"right-lower-leg": {
+					"right-lower-leg": {
+						"deform": [
+							{},
+							{
+								"time": 0.6,
+								"offset": 6,
+								"vertices": [ 1.80396, -1.56553 ]
+							},
+							{ "time": 1 }
+						]
+					}
+				},
+				"right-upper-leg": {
+					"right-upper-leg": {
+						"deform": [
+							{
+								"vertices": [ -6.03857, -1.46325, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -0.34685, -1.93102, -1.86047, -5.05266, -2.5014, -3.09985 ]
+							},
+							{ "time": 0.3333 },
+							{
+								"time": 0.8667,
+								"offset": 14,
+								"vertices": [ 0.13425, -2.35378, -1.33318, -5.99573, -1.35862, -4.43324 ]
+							},
+							{
+								"time": 1,
+								"vertices": [ -6.03857, -1.46325, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -0.34685, -1.93102, -1.86047, -5.05266, -2.5014, -3.09985 ]
+							}
+						]
+					}
+				},
+				"torso": {
+					"torso": {
+						"deform": [
+							{
+								"offset": 2,
+								"vertices": [ 0.24821, 2.86673, 0.24821, 2.86673, 0.24821, 2.86673, 0.24821, 2.86673, 0.24821, 2.86673, 0.24821, 2.86673, -1.24131, 2.62652, -2.47492, 0.71183, -0.26363, -0.5308, 0.24821, 2.86673, 0.24821, 2.86673, 0, 0, 0, 0, 0, 0, 0, 0, 1.34461, 0.25215, 0.24821, 2.86673, 0.82507, 1.61798, 0.24821, 2.86673, 0, 0, -1.86431, -0.4326, 0.24821, 2.86673 ]
+							},
+							{
+								"time": 0.1333,
+								"offset": 2,
+								"vertices": [ 0.35589, 4.10914, 0.35589, 4.10914, 0.35589, 4.10914, 0.35589, 4.10914, 0.35589, 4.10914, 0.35589, 4.10914, 1.66908, 3.51187, -0.62355, 2.47979, 1.1045, 3.49684, -1.09009, 6.08429, 0.35589, 4.10914, 0, 0, 0, 0, 0, 0, 0, 0, 3.01291, 0.15693, 0.35589, 4.10914, -1.11398, 3.7954, 0.35589, 4.10914, 0, 0, -2.96167, 0.55563, -2.20741, 4.40587 ]
+							},
+							{
+								"time": 0.3,
+								"offset": 2,
+								"vertices": [ 0.2884, 3.32948, 0.2884, 3.32948, 0.2884, 3.32948, 0.2884, 3.32948, 0.2884, 3.32948, 0.2884, 3.32948, 6.32601, 0.19387, 7.84315, 1.94837, 7.08587, 3.64119, 4.52343, 4.46961, 0.2884, 3.32948, 0, 0, 0, 0, 0, 0, 0, 0, 4.36416, -1.83876, 0.2884, 3.32948, 4.2925, 3.60194, 0.2884, 3.32948, 0, 0, 3.72601, -0.19338, 0.2884, 3.32948 ]
+							},
+							{
+								"time": 0.5,
+								"offset": 2,
+								"vertices": [ 0.3133, 3.61659, 0.3133, 3.61659, 0.3133, 3.61659, 0.3133, 3.61659, 0.3133, 3.61659, 0.3133, 3.61659, 2.57273, 2.74457, 2.88831, 3.04797, 3.48442, 3.04655, 1.80035, 4.609, 0.3133, 3.61659, 0, 0, 0, 0, 0, 0, 0, 0, 3.53782, -0.82203, 0.3133, 3.61659, 1.80022, 3.63246, 0.3133, 3.61659, 0, 0, 0.62718, 0.33564, -1.22467, 3.79463 ]
+							},
+							{
+								"time": 0.6333,
+								"offset": 2,
+								"vertices": [ 0.44398, 5.125, 0.44398, 5.125, 0.44398, 5.125, 0.44398, 5.125, 0.44398, 5.125, 0.44398, 5.125, 1.19401, 3.60798, -0.53546, 3.49565, 1.1926, 4.5127, -1.002, 7.10015, 0.44398, 5.125, 0, 0, 0, 0, 0, 0, 0, 0, 3.101, 1.17278, 0.44398, 5.125, -1.02589, 4.81126, 0.44398, 5.125, 0, 0, -2.87358, 1.57149, -2.11931, 5.42173 ]
+							},
+							{
+								"time": 0.8667,
+								"offset": 2,
+								"vertices": [ 0.30385, 3.50647, 0.30385, 3.50647, 0.30385, 3.50647, 0.30385, 3.50647, 0.30385, 3.50647, 0.30385, 3.50647, 0.92587, 2.24385, 0.68874, 1.29945, 3.55433, 3.00604, 2.71494, 5.89962, 0.30385, 3.50647, 0, 0, 0, 0, 0, 0, 0, 0, 1.96775, 0.40548, 0.30385, 3.50647, 2.6104, 2.3545, 0.30385, 3.50647, 0, 0, 0.22709, -0.12851, -0.62826, 3.61437 ]
+							},
+							{
+								"time": 1,
+								"offset": 2,
+								"vertices": [ 0.32802, 3.78826, 0.32802, 3.78826, 0.32802, 3.78826, 0.32802, 3.78826, 0.32802, 3.78826, 0.32802, 3.78826, -1.1615, 3.54805, -2.39511, 1.63336, -0.18382, 0.39073, 0.32802, 3.78826, 0.32802, 3.78826, 0, 0, 0, 0, 0, 0, 0, 0, 1.42442, 1.17368, 0.32802, 3.78826, 0.90488, 2.53951, 0.32802, 3.78826, 0, 0, -1.7845, 0.48894, 0.32802, 3.78826 ]
+							}
+						]
+					}
+				},
+				"undie-straps": {
+					"undie-straps": {
+						"deform": [
+							{
+								"offset": 2,
+								"vertices": [ -1.77697, 0.5476, -0.96145, -1.03793, -0.39148, -0.24072, -1.77697, 0.5476 ]
+							},
+							{
+								"time": 0.1333,
+								"offset": 2,
+								"vertices": [ -2.25684, -1.03177, -1.49719, -4.23862, -0.7447, -2.84907, -1.90072, 0.54478 ]
+							},
+							{
+								"time": 0.3333,
+								"offset": 2,
+								"vertices": [ -2.37974, -0.05432, -0.49433, 0.19437, -0.90861, 1.16519, -1.60956, 2.70799, 0.96186, 0.80615 ]
+							},
+							{
+								"time": 0.7,
+								"offset": 2,
+								"vertices": [ -0.91715, -2.76567, -0.62215, -3.63489, -0.84941, -2.26772, -2.56077, 0.52971 ]
+							},
+							{
+								"time": 0.8667,
+								"offset": 2,
+								"vertices": [ -2.56077, 0.52971, -1.58065, 0.32031, -1.3847, 0.32476, -2.56077, 0.52971 ]
+							},
+							{
+								"time": 1,
+								"offset": 2,
+								"vertices": [ -1.77697, 0.5476, -0.80128, 0.53413, -0.80128, 0.53413, -1.77697, 0.5476 ]
+							}
+						]
+					}
+				},
+				"undies": {
+					"undies": {
+						"deform": [
+							{
+								"vertices": [ 0.43099, 0.722, 10.60295, -0.117, 2.29599, 0, 2.29599, 0, 2.29599, 0, 0.58799, 0.244, -2.40018, -0.65335, -2.2782, -0.77534, 2.29599, 0, 0.58799, -0.488, 4.98698, -0.117, 6.50797, -0.23399 ]
+							},
+							{
+								"time": 0.1333,
+								"vertices": [ 0.72659, 0.4332, 7.20417, -0.1638, 1.37759, 0, 1.37759, 0, 1.37759, 0, 1.25279, 0.0464, -0.99862, -2.95085, -1.37543, -3.07404, 1.37759, 0, 0.35279, -0.2928, 2.99219, -0.0702, 3.90478, -0.1404 ]
+							},
+							{
+								"time": 0.3333,
+								"vertices": [ 1.16999, 0, 2.10599, -0.23401, 0, 0, 0, 0, 0, 0, 2.24999, -0.24999, -0.4344, 0.60551, -1.55939, 0.48051 ]
+							},
+							{
+								"time": 0.5333,
+								"vertices": [ 1.16999, 0, -0.234, -0.936, -2.92499, 0.351, 0, 0, 0, 0, 0.5, -0.24999, -0.64079, -2.07915, -0.64079, -2.07915 ]
+							},
+							{
+								"time": 0.7,
+								"vertices": [ 1.86271, -0.11514, 4.66327, -0.091, -1.76428, 0.21171, 0, 0, -0.56833, 0.32833, -1.13833, -1.15111, -2.19996, -3.47068, -1.29719, -3.47068, 0, 0, 0, 0, 1.58785, -0.04643, 2.65942, 0.16715 ]
+							},
+							{
+								"time": 0.8333,
+								"vertices": [ 2.41688, -0.20726, 8.58108, 0.585, -0.83571, 0.10029, 0, 0, -1.02299, 0.59099, -2.449, -1.872, -1.625, 0, 0, 0, 0, 0, 0, 0, 2.85813, -0.08357, 4.78695, 0.30086 ]
+							},
+							{
+								"time": 0.8667,
+								"vertices": [ 2.0197, -0.02141, 8.98546, 0.4446, -0.20937, 0.08023, 0.4592, 0, -0.3592, 0.47279, -1.8416, -1.4488, -0.79153, 1.26421, 0.53286, 1.23981, 0.4592, 0, 0.1176, -0.0976, 3.2839, -0.09025, 5.13116, 0.19389 ]
+							},
+							{
+								"time": 1,
+								"vertices": [ 0.43099, 0.722, 10.60295, -0.117, 2.29599, 0, 2.29599, 0, 2.29599, 0, 0.58799, 0.244, -2.40018, -0.65335, -2.2782, -0.77534, 2.29599, 0, 0.58799, -0.488, 4.98698, -0.117, 6.50797, -0.23399 ]
+							}
+						]
+					}
+				}
+			}
+		}
+	}
+}
+}

BIN
spine-haxe/example/assets/goblins-pro.skel


+ 96 - 0
spine-haxe/example/assets/goblins.atlas

@@ -0,0 +1,96 @@
+goblins.png
+	size: 1024, 128
+	filter: Linear, Linear
+dagger
+	bounds: 2, 18, 26, 108
+goblin/eyes-closed
+	bounds: 2, 4, 34, 12
+goblin/head
+	bounds: 113, 23, 103, 66
+	rotate: 90
+goblin/left-arm
+	bounds: 937, 89, 37, 35
+	rotate: 90
+goblin/left-foot
+	bounds: 609, 61, 65, 31
+	rotate: 90
+goblin/left-hand
+	bounds: 840, 21, 36, 41
+goblin/left-lower-leg
+	bounds: 504, 56, 33, 70
+goblin/left-shoulder
+	bounds: 745, 17, 29, 44
+goblin/left-upper-leg
+	bounds: 397, 53, 33, 73
+goblin/neck
+	bounds: 862, 85, 36, 41
+goblin/pelvis
+	bounds: 776, 18, 62, 43
+goblin/right-arm
+	bounds: 181, 5, 23, 50
+	rotate: 90
+goblin/right-foot
+	bounds: 747, 63, 63, 33
+	rotate: 90
+goblin/right-hand
+	bounds: 878, 3, 36, 37
+goblin/right-lower-leg
+	bounds: 321, 50, 36, 76
+goblin/right-shoulder
+	bounds: 663, 14, 39, 45
+goblin/right-upper-leg
+	bounds: 675, 63, 34, 63
+goblin/torso
+	bounds: 181, 30, 68, 96
+goblin/undie-straps
+	bounds: 38, 2, 55, 19
+goblin/undies
+	bounds: 974, 97, 36, 29
+goblingirl/eyes-closed
+	bounds: 397, 30, 37, 21
+goblingirl/head
+	bounds: 30, 23, 103, 81
+	rotate: 90
+goblingirl/left-arm
+	bounds: 916, 8, 37, 35
+	rotate: 90
+goblingirl/left-foot
+	bounds: 642, 61, 65, 31
+	rotate: 90
+goblingirl/left-hand
+	bounds: 900, 86, 35, 40
+goblingirl/left-lower-leg
+	bounds: 539, 56, 33, 70
+goblingirl/left-shoulder
+	bounds: 633, 13, 28, 46
+goblingirl/left-upper-leg
+	bounds: 574, 56, 33, 70
+goblingirl/neck
+	bounds: 878, 42, 35, 41
+goblingirl/pelvis
+	bounds: 817, 64, 62, 43
+	rotate: 90
+goblingirl/right-arm
+	bounds: 603, 4, 28, 50
+goblingirl/right-foot
+	bounds: 782, 63, 63, 33
+	rotate: 90
+goblingirl/right-hand
+	bounds: 915, 47, 36, 37
+goblingirl/right-lower-leg
+	bounds: 359, 50, 36, 76
+goblingirl/right-shoulder
+	bounds: 704, 16, 39, 45
+goblingirl/right-upper-leg
+	bounds: 711, 63, 34, 63
+goblingirl/torso
+	bounds: 251, 30, 68, 96
+goblingirl/undie-straps
+	bounds: 95, 2, 55, 19
+goblingirl/undies
+	bounds: 974, 66, 36, 29
+shield
+	bounds: 432, 54, 70, 72
+spear
+	bounds: 233, 6, 22, 368
+	rotate: 90

BIN
spine-haxe/example/assets/goblins.png


La diferencia del archivo ha sido suprimido porque es demasiado grande
+ 1285 - 0
spine-haxe/example/assets/mix-and-match-pro.json


BIN
spine-haxe/example/assets/mix-and-match-pro.skel


+ 362 - 0
spine-haxe/example/assets/mix-and-match.atlas

@@ -0,0 +1,362 @@
+mix-and-match.png
+	size: 1024, 512
+	filter: Linear, Linear
+	scale: 0.5
+base-head
+	bounds: 587, 2, 95, 73
+boy/arm-front
+	bounds: 558, 271, 36, 115
+boy/backpack
+	bounds: 235, 109, 119, 153
+boy/backpack-pocket
+	bounds: 328, 73, 34, 62
+	rotate: 90
+boy/backpack-strap-front
+	bounds: 665, 79, 38, 88
+boy/backpack-up
+	bounds: 395, 364, 21, 70
+	rotate: 90
+boy/body
+	bounds: 251, 264, 97, 132
+	rotate: 90
+boy/boot-ribbon-front
+	bounds: 648, 131, 9, 11
+boy/collar
+	bounds: 744, 4, 73, 29
+	rotate: 90
+boy/ear
+	bounds: 383, 109, 19, 23
+	rotate: 90
+boy/eye-back-low-eyelid
+	bounds: 739, 284, 17, 6
+	rotate: 90
+boy/eye-back-pupil
+	bounds: 832, 443, 8, 9
+	rotate: 90
+boy/eye-back-up-eyelid
+	bounds: 558, 264, 23, 5
+boy/eye-back-up-eyelid-back
+	bounds: 802, 491, 19, 10
+	rotate: 90
+boy/eye-front-low-eyelid
+	bounds: 386, 363, 22, 7
+	rotate: 90
+boy/eye-front-pupil
+	bounds: 816, 389, 9, 9
+boy/eye-front-up-eyelid
+	bounds: 160, 71, 31, 6
+	rotate: 90
+boy/eye-front-up-eyelid-back
+	bounds: 801, 434, 26, 9
+	rotate: 90
+boy/eye-iris-back
+	bounds: 618, 264, 17, 17
+boy/eye-iris-front
+	bounds: 727, 264, 18, 18
+boy/eye-white-back
+	bounds: 580, 131, 20, 12
+boy/eye-white-front
+	bounds: 510, 130, 27, 13
+boy/eyebrow-back
+	bounds: 751, 88, 20, 11
+	rotate: 90
+boy/eyebrow-front
+	bounds: 483, 130, 25, 11
+boy/hair-back
+	bounds: 494, 388, 122, 81
+	rotate: 90
+boy/hair-bangs
+	bounds: 667, 284, 70, 37
+boy/hair-side
+	bounds: 789, 374, 25, 43
+boy/hand-backfingers
+	bounds: 467, 364, 19, 21
+boy/hand-front-fingers
+	bounds: 488, 364, 19, 21
+boy/hat
+	bounds: 615, 417, 93, 56
+	rotate: 90
+boy/leg-front
+	bounds: 138, 104, 31, 158
+boy/mouth-close
+	bounds: 551, 365, 21, 5
+	rotate: 90
+girl-blue-cape/mouth-close
+	bounds: 551, 365, 21, 5
+	rotate: 90
+girl-spring-dress/mouth-close
+	bounds: 551, 365, 21, 5
+	rotate: 90
+girl/mouth-close
+	bounds: 551, 365, 21, 5
+	rotate: 90
+boy/mouth-smile
+	bounds: 705, 79, 29, 7
+boy/nose
+	bounds: 836, 473, 17, 10
+	rotate: 90
+boy/pompom
+	bounds: 747, 273, 48, 43
+	rotate: 90
+boy/zip
+	bounds: 648, 144, 14, 23
+girl-blue-cape/back-eyebrow
+	bounds: 602, 131, 18, 12
+girl-blue-cape/body-dress
+	bounds: 2, 264, 109, 246
+girl-blue-cape/body-ribbon
+	bounds: 615, 283, 50, 38
+girl-blue-cape/cape-back
+	bounds: 2, 69, 134, 193
+girl-blue-cape/cape-back-up
+	bounds: 386, 387, 123, 106
+	rotate: 90
+girl-blue-cape/cape-ribbon
+	bounds: 675, 264, 50, 18
+girl-blue-cape/cape-shoulder-back
+	bounds: 751, 110, 49, 59
+girl-blue-cape/cape-shoulder-front
+	bounds: 113, 264, 62, 76
+	rotate: 90
+girl-blue-cape/cape-up-front
+	bounds: 399, 264, 98, 117
+	rotate: 90
+girl-blue-cape/ear
+	bounds: 775, 2, 19, 23
+girl-spring-dress/ear
+	bounds: 775, 2, 19, 23
+girl/ear
+	bounds: 775, 2, 19, 23
+girl-blue-cape/eye-back-low-eyelid
+	bounds: 802, 463, 17, 6
+girl-spring-dress/eye-back-low-eyelid
+	bounds: 802, 463, 17, 6
+girl/eye-back-low-eyelid
+	bounds: 802, 463, 17, 6
+girl-blue-cape/eye-back-pupil
+	bounds: 816, 367, 8, 9
+girl-spring-dress/eye-back-pupil
+	bounds: 816, 367, 8, 9
+girl/eye-back-pupil
+	bounds: 816, 367, 8, 9
+girl-blue-cape/eye-back-up-eyelid
+	bounds: 554, 131, 24, 12
+girl-spring-dress/eye-back-up-eyelid
+	bounds: 554, 131, 24, 12
+girl/eye-back-up-eyelid
+	bounds: 554, 131, 24, 12
+girl-blue-cape/eye-back-up-eyelid-back
+	bounds: 832, 453, 17, 11
+	rotate: 90
+girl-spring-dress/eye-back-up-eyelid-back
+	bounds: 832, 453, 17, 11
+	rotate: 90
+girl/eye-back-up-eyelid-back
+	bounds: 832, 453, 17, 11
+	rotate: 90
+girl-blue-cape/eye-front-low-eyelid
+	bounds: 739, 303, 18, 6
+	rotate: 90
+girl-spring-dress/eye-front-low-eyelid
+	bounds: 739, 303, 18, 6
+	rotate: 90
+girl/eye-front-low-eyelid
+	bounds: 739, 303, 18, 6
+	rotate: 90
+girl-blue-cape/eye-front-pupil
+	bounds: 816, 378, 9, 9
+girl-spring-dress/eye-front-pupil
+	bounds: 816, 378, 9, 9
+girl/eye-front-pupil
+	bounds: 816, 378, 9, 9
+girl-blue-cape/eye-front-up-eyelid
+	bounds: 392, 77, 30, 14
+	rotate: 90
+girl-spring-dress/eye-front-up-eyelid
+	bounds: 392, 77, 30, 14
+	rotate: 90
+girl/eye-front-up-eyelid
+	bounds: 392, 77, 30, 14
+	rotate: 90
+girl-blue-cape/eye-front-up-eyelid-back
+	bounds: 455, 130, 26, 11
+girl-spring-dress/eye-front-up-eyelid-back
+	bounds: 455, 130, 26, 11
+girl/eye-front-up-eyelid-back
+	bounds: 455, 130, 26, 11
+girl-blue-cape/eye-iris-back
+	bounds: 637, 264, 17, 17
+girl-blue-cape/eye-iris-front
+	bounds: 802, 471, 18, 18
+girl-blue-cape/eye-white-back
+	bounds: 596, 264, 20, 16
+girl-spring-dress/eye-white-back
+	bounds: 596, 264, 20, 16
+girl-blue-cape/eye-white-front
+	bounds: 796, 5, 20, 16
+	rotate: 90
+girl-spring-dress/eye-white-front
+	bounds: 796, 5, 20, 16
+	rotate: 90
+girl/eye-white-front
+	bounds: 796, 5, 20, 16
+	rotate: 90
+girl-blue-cape/front-eyebrow
+	bounds: 608, 149, 18, 12
+	rotate: 90
+girl-blue-cape/hair-back
+	bounds: 508, 145, 117, 98
+	rotate: 90
+girl-blue-cape/hair-bangs
+	bounds: 673, 419, 91, 40
+	rotate: 90
+girl-blue-cape/hair-head-side-back
+	bounds: 196, 331, 30, 52
+	rotate: 90
+girl-blue-cape/hair-head-side-front
+	bounds: 738, 323, 41, 42
+girl-blue-cape/hair-side
+	bounds: 473, 3, 36, 71
+girl-blue-cape/hand-front-fingers
+	bounds: 509, 365, 19, 21
+girl-spring-dress/hand-front-fingers
+	bounds: 509, 365, 19, 21
+girl-blue-cape/leg-front
+	bounds: 168, 72, 30, 158
+	rotate: 90
+girl-blue-cape/mouth-smile
+	bounds: 736, 79, 29, 7
+girl-spring-dress/mouth-smile
+	bounds: 736, 79, 29, 7
+girl/mouth-smile
+	bounds: 736, 79, 29, 7
+girl-blue-cape/nose
+	bounds: 747, 264, 11, 7
+girl-spring-dress/nose
+	bounds: 747, 264, 11, 7
+girl/nose
+	bounds: 747, 264, 11, 7
+girl-blue-cape/sleeve-back
+	bounds: 767, 79, 42, 29
+girl-blue-cape/sleeve-front
+	bounds: 408, 76, 52, 119
+	rotate: 90
+girl-spring-dress/arm-front
+	bounds: 596, 282, 17, 111
+girl-spring-dress/back-eyebrow
+	bounds: 801, 420, 18, 12
+girl-spring-dress/body-up
+	bounds: 179, 4, 64, 66
+girl-spring-dress/cloak-down
+	bounds: 775, 27, 50, 50
+girl-spring-dress/cloak-up
+	bounds: 360, 7, 64, 58
+	rotate: 90
+girl-spring-dress/eye-iris-back
+	bounds: 656, 264, 17, 17
+girl-spring-dress/eye-iris-front
+	bounds: 814, 492, 18, 18
+girl-spring-dress/front-eyebrow
+	bounds: 822, 472, 18, 12
+	rotate: 90
+girl-spring-dress/hair-back
+	bounds: 196, 363, 147, 93
+	rotate: 90
+girl-spring-dress/hair-bangs
+	bounds: 696, 326, 91, 40
+	rotate: 90
+girl-spring-dress/hair-head-side-back
+	bounds: 529, 76, 30, 52
+girl-spring-dress/hair-head-side-front
+	bounds: 781, 323, 41, 42
+girl-spring-dress/hair-side
+	bounds: 511, 3, 36, 71
+girl-spring-dress/leg-front
+	bounds: 171, 104, 30, 158
+girl-spring-dress/neck
+	bounds: 138, 70, 20, 32
+girl-spring-dress/shoulder-ribbon
+	bounds: 622, 131, 36, 24
+	rotate: 90
+girl-spring-dress/skirt
+	bounds: 113, 328, 182, 81
+	rotate: 90
+girl-spring-dress/underskirt
+	bounds: 2, 2, 175, 65
+girl/arm-front
+	bounds: 577, 395, 36, 115
+girl/back-eyebrow
+	bounds: 834, 492, 18, 12
+	rotate: 90
+girl/bag-base
+	bounds: 191, 264, 62, 58
+	rotate: 90
+girl/bag-strap-front
+	bounds: 385, 265, 12, 96
+girl/bag-top
+	bounds: 738, 367, 49, 50
+girl/body
+	bounds: 356, 130, 97, 132
+girl/boot-ribbon-front
+	bounds: 539, 130, 13, 13
+girl/eye-iris-back
+	bounds: 821, 424, 17, 17
+girl/eye-iris-front
+	bounds: 812, 443, 18, 18
+girl/eye-white-back
+	bounds: 814, 5, 20, 16
+	rotate: 90
+girl/front-eyebrow
+	bounds: 816, 400, 18, 12
+	rotate: 90
+girl/hair-back
+	bounds: 291, 363, 147, 93
+	rotate: 90
+girl/hair-bangs
+	bounds: 715, 419, 91, 40
+	rotate: 90
+girl/hair-flap-down-front
+	bounds: 288, 5, 70, 65
+girl/hair-head-side-back
+	bounds: 561, 77, 30, 52
+girl/hair-head-side-front
+	bounds: 757, 419, 41, 42
+	rotate: 90
+girl/hair-patch
+	bounds: 245, 4, 66, 41
+	rotate: 90
+girl/hair-side
+	bounds: 549, 3, 36, 71
+girl/hair-strand-back-1
+	bounds: 684, 3, 58, 74
+girl/hair-strand-back-2
+	bounds: 692, 171, 91, 58
+	rotate: 90
+girl/hair-strand-back-3
+	bounds: 615, 323, 92, 79
+	rotate: 90
+girl/hair-strand-front-1
+	bounds: 518, 269, 38, 94
+girl/hair-strand-front-2
+	bounds: 593, 79, 70, 50
+girl/hair-strand-front-3
+	bounds: 705, 88, 44, 81
+girl/hand-front-fingers
+	bounds: 530, 365, 19, 21
+girl/hat
+	bounds: 608, 169, 93, 82
+	rotate: 90
+girl/leg-front
+	bounds: 203, 104, 30, 158
+girl/pompom
+	bounds: 757, 462, 48, 43
+	rotate: 90
+girl/scarf
+	bounds: 455, 143, 119, 51
+	rotate: 90
+girl/scarf-back
+	bounds: 420, 2, 72, 51
+	rotate: 90
+girl/zip
+	bounds: 356, 109, 19, 25
+	rotate: 90

BIN
spine-haxe/example/assets/mix-and-match.png


La diferencia del archivo ha sido suprimido porque es demasiado grande
+ 191 - 0
spine-haxe/example/assets/owl-pro.json


BIN
spine-haxe/example/assets/owl-pro.skel


+ 73 - 0
spine-haxe/example/assets/owl.atlas

@@ -0,0 +1,73 @@
+owl.png
+	size: 1024, 512
+	filter: Linear, Linear
+	scale: 0.5
+L_eye-closed
+	bounds: 512, 5, 90, 86
+	rotate: 90
+L_eye-iris
+	bounds: 600, 10, 90, 86
+	rotate: 90
+L_eye-light
+	bounds: 688, 4, 21, 20
+	rotate: 90
+L_eye-pupil
+	bounds: 856, 13, 63, 60
+	rotate: 90
+L_foot
+	bounds: 834, 78, 64, 48
+	rotate: 90
+L_wing
+	bounds: 349, 4, 81, 110
+	rotate: 90
+R_eye-closed
+	bounds: 637, 119, 90, 86
+	rotate: 90
+R_eye-iris
+	bounds: 688, 27, 90, 86
+	rotate: 90
+R_eye-light
+	bounds: 710, 4, 21, 20
+	rotate: 90
+R_eye-pupil
+	bounds: 884, 80, 63, 60
+	rotate: 90
+R_foot
+	bounds: 845, 145, 64, 48
+	rotate: 90
+R_wing
+	bounds: 421, 93, 81, 110
+beak
+	bounds: 776, 2, 39, 41
+beak-down
+	bounds: 817, 3, 37, 40
+body
+	bounds: 2, 7, 248, 196
+feather-1
+	bounds: 918, 18, 59, 60
+feather-2
+	bounds: 781, 144, 62, 65
+feather-3
+	bounds: 776, 45, 56, 76
+head-base
+	bounds: 621, 211, 299, 237
+	rotate: 90
+leaf-1
+	bounds: 559, 102, 76, 101
+leaf-2
+	bounds: 252, 4, 65, 95
+	rotate: 90
+leaf-3
+	bounds: 252, 71, 132, 77
+	rotate: 90
+leaf-4
+	bounds: 461, 2, 89, 49
+	rotate: 90
+leaf-5
+	bounds: 504, 97, 53, 106
+leaf-6
+	bounds: 331, 87, 88, 116
+leaf-7
+	bounds: 725, 123, 54, 86
+wood
+	bounds: 2, 205, 617, 305

BIN
spine-haxe/example/assets/owl.png


La diferencia del archivo ha sido suprimido porque es demasiado grande
+ 692 - 0
spine-haxe/example/assets/raptor-pro.json


BIN
spine-haxe/example/assets/raptor-pro.skel


+ 100 - 0
spine-haxe/example/assets/raptor.atlas

@@ -0,0 +1,100 @@
+raptor.png
+	size: 1024, 512
+	filter: Linear, Linear
+	scale: 0.5
+back-arm
+	bounds: 829, 88, 46, 25
+	rotate: 90
+back-bracer
+	bounds: 195, 238, 39, 28
+	rotate: 90
+back-hand
+	bounds: 724, 140, 36, 34
+	rotate: 90
+back-knee
+	bounds: 760, 131, 49, 67
+	rotate: 90
+back-thigh
+	bounds: 225, 238, 39, 24
+	rotate: 90
+eyes-open
+	bounds: 975, 204, 47, 45
+front-arm
+	bounds: 969, 112, 48, 26
+front-bracer
+	bounds: 724, 97, 41, 29
+	rotate: 90
+front-hand
+	bounds: 251, 239, 41, 38
+front-open-hand
+	bounds: 856, 76, 43, 44
+	rotate: 90
+front-thigh
+	bounds: 729, 178, 57, 29
+	rotate: 90
+gun
+	bounds: 894, 251, 107, 103
+gun-nohand
+	bounds: 764, 241, 105, 102
+head
+	bounds: 756, 345, 136, 149
+lower-leg
+	bounds: 475, 237, 73, 98
+	rotate: 90
+mouth-grind
+	bounds: 975, 172, 47, 30
+mouth-smile
+	bounds: 975, 140, 47, 30
+neck
+	bounds: 366, 282, 18, 21
+raptor-back-arm
+	bounds: 636, 97, 82, 86
+	rotate: 90
+raptor-body
+	bounds: 2, 2, 632, 233
+raptor-front-arm
+	bounds: 871, 168, 81, 102
+	rotate: 90
+raptor-front-leg
+	bounds: 2, 237, 191, 257
+raptor-hindleg-back
+	bounds: 195, 279, 169, 215
+raptor-horn
+	bounds: 431, 312, 182, 80
+	rotate: 90
+raptor-horn-back
+	bounds: 513, 318, 176, 77
+	rotate: 90
+raptor-jaw
+	bounds: 894, 356, 126, 138
+raptor-jaw-tooth
+	bounds: 294, 240, 37, 48
+	rotate: 90
+raptor-mouth-inside
+	bounds: 344, 241, 36, 41
+	rotate: 90
+raptor-saddle-strap-back
+	bounds: 575, 242, 54, 74
+raptor-saddle-strap-front
+	bounds: 764, 182, 57, 95
+	rotate: 90
+raptor-saddle-w-shadow
+	bounds: 592, 323, 162, 171
+raptor-tail-shadow
+	bounds: 366, 305, 189, 63
+	rotate: 90
+raptor-tongue
+	bounds: 387, 239, 86, 64
+stirrup-back
+	bounds: 829, 136, 44, 35
+	rotate: 90
+stirrup-front
+	bounds: 866, 121, 45, 50
+	rotate: 90
+stirrup-strap
+	bounds: 918, 120, 49, 46
+torso
+	bounds: 636, 181, 54, 91
+	rotate: 90
+visor
+	bounds: 631, 237, 131, 84

BIN
spine-haxe/example/assets/raptor.png


La diferencia del archivo ha sido suprimido porque es demasiado grande
+ 541 - 0
spine-haxe/example/assets/spineboy-pro.json


BIN
spine-haxe/example/assets/spineboy-pro.skel


+ 101 - 0
spine-haxe/example/assets/spineboy.atlas

@@ -0,0 +1,101 @@
+spineboy.png
+	size: 1024, 256
+	filter: Linear, Linear
+	scale: 0.5
+crosshair
+	bounds: 813, 160, 45, 45
+eye-indifferent
+	bounds: 569, 2, 47, 45
+eye-surprised
+	bounds: 643, 7, 47, 45
+	rotate: 90
+front-bracer
+	bounds: 811, 51, 29, 40
+front-fist-closed
+	bounds: 807, 93, 38, 41
+front-fist-open
+	bounds: 815, 210, 43, 44
+front-foot
+	bounds: 706, 64, 63, 35
+	rotate: 90
+front-shin
+	bounds: 80, 11, 41, 92
+front-thigh
+	bounds: 754, 12, 23, 56
+front-upper-arm
+	bounds: 618, 5, 23, 49
+goggles
+	bounds: 214, 20, 131, 83
+gun
+	bounds: 347, 14, 105, 102
+	rotate: 90
+head
+	bounds: 80, 105, 136, 149
+hoverboard-board
+	bounds: 2, 8, 246, 76
+	rotate: 90
+hoverboard-thruster
+	bounds: 478, 2, 30, 32
+hoverglow-small
+	bounds: 218, 117, 137, 38
+	rotate: 90
+mouth-grind
+	bounds: 775, 80, 47, 30
+	rotate: 90
+mouth-oooo
+	bounds: 779, 31, 47, 30
+	rotate: 90
+mouth-smile
+	bounds: 783, 207, 47, 30
+	rotate: 90
+muzzle-glow
+	bounds: 779, 4, 25, 25
+muzzle-ring
+	bounds: 451, 14, 25, 105
+muzzle01
+	bounds: 664, 60, 67, 40
+	rotate: 90
+muzzle02
+	bounds: 580, 56, 68, 42
+	rotate: 90
+muzzle03
+	bounds: 478, 36, 83, 53
+	rotate: 90
+muzzle04
+	bounds: 533, 49, 75, 45
+	rotate: 90
+muzzle05
+	bounds: 624, 56, 68, 38
+	rotate: 90
+neck
+	bounds: 806, 8, 18, 21
+portal-bg
+	bounds: 258, 121, 133, 133
+portal-flare1
+	bounds: 690, 2, 56, 30
+	rotate: 90
+portal-flare2
+	bounds: 510, 3, 57, 31
+portal-flare3
+	bounds: 722, 4, 58, 30
+	rotate: 90
+portal-shade
+	bounds: 393, 121, 133, 133
+portal-streaks1
+	bounds: 528, 126, 126, 128
+portal-streaks2
+	bounds: 656, 129, 125, 125
+rear-bracer
+	bounds: 826, 13, 28, 36
+rear-foot
+	bounds: 743, 70, 57, 30
+	rotate: 90
+rear-shin
+	bounds: 174, 14, 38, 89
+rear-thigh
+	bounds: 783, 158, 28, 47
+rear-upper-arm
+	bounds: 783, 136, 20, 44
+	rotate: 90
+torso
+	bounds: 123, 13, 49, 90

BIN
spine-haxe/example/assets/spineboy.png


La diferencia del archivo ha sido suprimido porque es demasiado grande
+ 512 - 0
spine-haxe/example/assets/stretchyman-pro.json


BIN
spine-haxe/example/assets/stretchyman-pro.skel


+ 18 - 0
spine-haxe/example/assets/stretchyman.atlas

@@ -0,0 +1,18 @@
+stretchyman.png
+	size: 1024, 256
+	filter: Linear, Linear
+back-arm
+	bounds: 679, 173, 72, 202
+	rotate: 90
+back-leg
+	bounds: 2, 2, 100, 318
+	rotate: 90
+body
+	bounds: 2, 104, 141, 452
+	rotate: 90
+front-arm
+	bounds: 456, 100, 145, 221
+	rotate: 90
+head
+	bounds: 322, 15, 87, 102
+	rotate: 90

BIN
spine-haxe/example/assets/stretchyman.png


+ 5020 - 0
spine-haxe/example/assets/tank-pro.json

@@ -0,0 +1,5020 @@
+{
+"skeleton": {
+	"hash": "ulJOWXMG9PE",
+	"spine": "4.1.17",
+	"x": -5852.65,
+	"y": -348.5,
+	"width": 7202.61,
+	"height": 1298.88,
+	"images": "./images/",
+	"audio": ""
+},
+"bones": [
+	{ "name": "root" },
+	{ "name": "tank-root", "parent": "root", "y": 146.79 },
+	{ "name": "tank-treads", "parent": "tank-root", "y": 48.35 },
+	{ "name": "tank-body", "parent": "tank-treads", "y": 10 },
+	{ "name": "guntower", "parent": "tank-body", "x": -21.72, "y": 245.48 },
+	{ "name": "antenna-root", "parent": "guntower", "x": 164.61, "y": 202.53 },
+	{ "name": "antenna1", "parent": "antenna-root", "length": 40, "rotation": 90, "y": 40, "color": "ffee00ff" },
+	{ "name": "antenna2", "parent": "antenna1", "length": 42, "x": 42, "color": "ffee00ff" },
+	{ "name": "antenna3", "parent": "antenna2", "length": 42, "x": 42, "color": "ffee00ff" },
+	{ "name": "antenna4", "parent": "antenna3", "length": 42, "x": 42, "color": "ffee00ff" },
+	{ "name": "antenna5", "parent": "antenna4", "length": 42, "x": 42, "color": "ffee00ff" },
+	{ "name": "antenna6", "parent": "antenna5", "length": 42, "x": 42, "color": "ffee00ff" },
+	{ "name": "cannon-connector", "parent": "guntower", "x": -235.05, "y": 96.07 },
+	{ "name": "cannon-target", "parent": "tank-root", "x": -2276.67, "y": 400.17, "color": "0096ffff" },
+	{ "name": "cannon", "parent": "cannon-connector", "length": 946.68, "rotation": 180, "color": "ff4000ff" },
+	{
+		"name": "machinegun-mount",
+		"parent": "guntower",
+		"length": 90.98,
+		"rotation": 90,
+		"x": -123.73,
+		"y": 218.33,
+		"color": "15ff00ff"
+	},
+	{ "name": "machinegun-target", "parent": "tank-root", "x": -2272.76, "y": 607.77, "color": "0096ffff" },
+	{
+		"name": "machinegun",
+		"parent": "machinegun-mount",
+		"length": 208.95,
+		"rotation": 90,
+		"x": 91.52,
+		"y": -1.03,
+		"color": "15ff00ff"
+	},
+	{ "name": "machinegun-tip", "parent": "machinegun", "x": 210.43, "y": -12.21 },
+	{ "name": "rock", "parent": "root", "x": -1925.2, "y": 33.17 },
+	{
+		"name": "smoke-root",
+		"parent": "tank-root",
+		"x": -1200.38,
+		"y": 405.76,
+		"scaleX": -6.5,
+		"scaleY": 6.5,
+		"color": "ff4000ff"
+	},
+	{ "name": "smoke-glow", "parent": "smoke-root", "x": 62.92, "y": -0.71, "color": "ff4000ff" },
+	{
+		"name": "smoke1",
+		"parent": "smoke-root",
+		"rotation": -179.99,
+		"x": 14.75,
+		"y": -1.55,
+		"scaleX": 1.6484,
+		"scaleY": 1.6484,
+		"color": "ff4000ff"
+	},
+	{
+		"name": "smoke10",
+		"parent": "smoke-root",
+		"rotation": -103.52,
+		"x": 25.45,
+		"y": 2.48,
+		"scaleX": 3.9011,
+		"scaleY": 2.8523,
+		"color": "ff4000ff"
+	},
+	{
+		"name": "smoke11",
+		"parent": "smoke-root",
+		"rotation": -103.52,
+		"x": 25.45,
+		"y": 2.48,
+		"scaleX": 3.9011,
+		"scaleY": 2.8523,
+		"color": "ff4000ff"
+	},
+	{
+		"name": "smoke12",
+		"parent": "smoke-root",
+		"rotation": -103.52,
+		"x": 25.45,
+		"y": 2.48,
+		"scaleX": 3.9011,
+		"scaleY": 2.8523,
+		"color": "ff4000ff"
+	},
+	{
+		"name": "smoke13",
+		"parent": "smoke-root",
+		"rotation": -179.99,
+		"x": 14.75,
+		"y": -1.55,
+		"scaleX": 1.6484,
+		"scaleY": 1.6484,
+		"color": "ff4000ff"
+	},
+	{
+		"name": "smoke14",
+		"parent": "smoke-root",
+		"rotation": -179.99,
+		"x": 14.75,
+		"y": -1.55,
+		"scaleX": 1.6484,
+		"scaleY": 1.6484,
+		"color": "ff4000ff"
+	},
+	{
+		"name": "smoke15",
+		"parent": "smoke-root",
+		"rotation": -179.99,
+		"x": 14.75,
+		"y": -1.55,
+		"scaleX": 1.6484,
+		"scaleY": 1.6484,
+		"color": "ff4000ff"
+	},
+	{
+		"name": "smoke16",
+		"parent": "smoke-root",
+		"rotation": -179.99,
+		"x": 14.75,
+		"y": -1.55,
+		"scaleX": 1.6484,
+		"scaleY": 1.6484,
+		"color": "ff4000ff"
+	},
+	{
+		"name": "smoke17",
+		"parent": "smoke-root",
+		"rotation": -179.99,
+		"x": 14.75,
+		"y": -1.55,
+		"scaleX": 1.6484,
+		"scaleY": 1.6484,
+		"color": "ff4000ff"
+	},
+	{
+		"name": "smoke18",
+		"parent": "smoke-root",
+		"rotation": -179.99,
+		"x": 14.75,
+		"y": -1.55,
+		"scaleX": 1.6484,
+		"scaleY": 1.6484,
+		"color": "ff4000ff"
+	},
+	{
+		"name": "smoke2",
+		"parent": "smoke-root",
+		"rotation": -84.14,
+		"x": 45.06,
+		"y": 29.7,
+		"scaleX": 3.3345,
+		"scaleY": 3.3345,
+		"color": "ff4000ff"
+	},
+	{
+		"name": "smoke20",
+		"parent": "smoke-root",
+		"rotation": -179.99,
+		"x": 14.75,
+		"y": -1.55,
+		"scaleX": 1.6484,
+		"scaleY": 1.6484,
+		"color": "ff4000ff"
+	},
+	{
+		"name": "smoke21",
+		"parent": "smoke-root",
+		"rotation": -179.99,
+		"x": 14.75,
+		"y": -1.55,
+		"scaleX": 1.6484,
+		"scaleY": 1.6484,
+		"color": "ff4000ff"
+	},
+	{
+		"name": "smoke22",
+		"parent": "smoke-root",
+		"rotation": -179.99,
+		"x": 14.75,
+		"y": -1.55,
+		"scaleX": 1.6484,
+		"scaleY": 1.6484,
+		"color": "ff4000ff"
+	},
+	{
+		"name": "smoke23",
+		"parent": "smoke-root",
+		"rotation": -179.99,
+		"x": 14.75,
+		"y": -1.55,
+		"scaleX": 1.6484,
+		"scaleY": 1.6484,
+		"color": "ff4000ff"
+	},
+	{
+		"name": "smoke24",
+		"parent": "smoke-root",
+		"rotation": -179.99,
+		"x": 14.75,
+		"y": -1.55,
+		"scaleX": 1.6484,
+		"scaleY": 1.6484,
+		"color": "ff4000ff"
+	},
+	{
+		"name": "smoke25",
+		"parent": "smoke-root",
+		"rotation": -179.99,
+		"x": 14.75,
+		"y": -1.55,
+		"scaleX": 1.6484,
+		"scaleY": 1.6484,
+		"color": "ff4000ff"
+	},
+	{
+		"name": "smoke26",
+		"parent": "smoke-root",
+		"rotation": -179.99,
+		"x": 14.75,
+		"y": -1.55,
+		"scaleX": 1.6484,
+		"scaleY": 1.6484,
+		"color": "ff4000ff"
+	},
+	{
+		"name": "smoke27",
+		"parent": "smoke-root",
+		"rotation": -179.99,
+		"x": 14.75,
+		"y": -1.55,
+		"scaleX": 1.6484,
+		"scaleY": 1.6484,
+		"color": "ff4000ff"
+	},
+	{
+		"name": "smoke3",
+		"parent": "smoke-root",
+		"rotation": -87.91,
+		"x": 55.15,
+		"y": -17.5,
+		"scaleX": 3.0415,
+		"scaleY": 4.157,
+		"color": "ff4000ff"
+	},
+	{
+		"name": "smoke4",
+		"parent": "smoke-root",
+		"rotation": -87.91,
+		"x": 69.25,
+		"y": 8.01,
+		"scaleX": 2.1808,
+		"scaleY": 2.9807,
+		"color": "ff4000ff"
+	},
+	{
+		"name": "smoke5",
+		"parent": "smoke-root",
+		"rotation": -87.91,
+		"x": 80.63,
+		"y": 59.88,
+		"scaleX": 4.5119,
+		"scaleY": 2.9725,
+		"color": "ff4000ff"
+	},
+	{
+		"name": "smoke6",
+		"parent": "smoke-root",
+		"rotation": -87.91,
+		"x": 96.19,
+		"y": 25.65,
+		"scaleX": 3.7912,
+		"scaleY": 3.0552,
+		"color": "ff4000ff"
+	},
+	{
+		"name": "smoke7",
+		"parent": "smoke-root",
+		"rotation": 153.68,
+		"x": 85.65,
+		"y": -50.47,
+		"scaleX": 4.8523,
+		"scaleY": 3.6528,
+		"color": "ff4000ff"
+	},
+	{
+		"name": "smoke8",
+		"parent": "smoke-root",
+		"rotation": 67.58,
+		"x": 47.85,
+		"y": -42.55,
+		"scaleX": 4.0006,
+		"scaleY": 3.4796,
+		"color": "ff4000ff"
+	},
+	{
+		"name": "smoke9",
+		"parent": "smoke-root",
+		"rotation": 150.05,
+		"x": 104.02,
+		"y": -8.73,
+		"scaleX": 4.2074,
+		"scaleY": 3.0762,
+		"color": "ff4000ff"
+	},
+	{ "name": "tank-glow", "parent": "tank-root", "x": -247.72, "y": 404.37, "scaleX": 1.0582, "scaleY": 0.6785 },
+	{
+		"name": "tread",
+		"parent": "tank-root",
+		"length": 82,
+		"rotation": 180,
+		"x": -22.9,
+		"y": 213.86,
+		"scaleX": 0.9933,
+		"color": "e64344ff"
+	},
+	{ "name": "wheel-mid-center", "parent": "tank-root", "y": -66.21 },
+	{ "name": "tread-collider1", "parent": "wheel-mid-center", "x": -329.58, "y": -85.44, "color": "ff00fbff" },
+	{ "name": "tread-collider2", "parent": "wheel-mid-center", "x": -165.95, "y": -85.44, "color": "ff00fbff" },
+	{ "name": "tread-collider3", "parent": "wheel-mid-center", "y": -85.44, "color": "ff00fbff" },
+	{ "name": "tread-collider4", "parent": "wheel-mid-center", "x": 163.56, "y": -85.44, "color": "ff00fbff" },
+	{ "name": "tread-collider5", "parent": "wheel-mid-center", "x": 329.12, "y": -85.44, "color": "ff00fbff" },
+	{ "name": "tread-gravity1", "parent": "tank-root", "rotation": 180, "x": -175.35, "y": 149.31, "color": "ff00fbff" },
+	{ "name": "tread-gravity2", "parent": "tank-root", "rotation": 180, "x": 177.89, "y": 144.78, "color": "ff00fbff" },
+	{
+		"name": "tread10",
+		"parent": "tread",
+		"length": 82,
+		"rotation": 48.85,
+		"x": 662.9,
+		"y": -120.35,
+		"color": "e64344ff"
+	},
+	{
+		"name": "tread11",
+		"parent": "tread",
+		"length": 82,
+		"rotation": 97.99,
+		"x": 651.5,
+		"y": -39.69,
+		"color": "e64344ff"
+	},
+	{
+		"name": "tread12",
+		"parent": "tread",
+		"length": 82,
+		"rotation": 113.79,
+		"x": 618.43,
+		"y": 34.83,
+		"color": "e64344ff"
+	},
+	{
+		"name": "tread13",
+		"parent": "tread",
+		"length": 82,
+		"rotation": 122.96,
+		"x": 573.82,
+		"y": 103.18,
+		"color": "e64344ff"
+	},
+	{
+		"name": "tread14",
+		"parent": "tread",
+		"length": 82,
+		"rotation": 142.01,
+		"x": 509.19,
+		"y": 153.3,
+		"color": "e64344ff"
+	},
+	{
+		"name": "tread15",
+		"parent": "tread",
+		"length": 82,
+		"rotation": 157.84,
+		"x": 433.25,
+		"y": 184.02,
+		"color": "e64344ff"
+	},
+	{
+		"name": "tread16",
+		"parent": "tread",
+		"length": 82,
+		"rotation": 157.37,
+		"x": 357.56,
+		"y": 215.37,
+		"color": "e64344ff"
+	},
+	{
+		"name": "tread17",
+		"parent": "tread",
+		"length": 82,
+		"rotation": 157.29,
+		"x": 281.92,
+		"y": 246.8,
+		"color": "e64344ff"
+	},
+	{
+		"name": "tread18",
+		"parent": "tread",
+		"length": 82,
+		"rotation": 157.19,
+		"x": 206.33,
+		"y": 278.38,
+		"color": "e64344ff"
+	},
+	{
+		"name": "tread19",
+		"parent": "tread",
+		"length": 82,
+		"rotation": 157.14,
+		"x": 130.77,
+		"y": 310.02,
+		"color": "e64344ff"
+	},
+	{ "name": "tread2", "parent": "tread", "length": 82, "x": 82, "color": "e64344ff" },
+	{
+		"name": "tread20",
+		"parent": "tread",
+		"length": 82,
+		"rotation": 157.34,
+		"x": 55.1,
+		"y": 341.41,
+		"color": "e64344ff"
+	},
+	{
+		"name": "tread21",
+		"parent": "tread",
+		"length": 82,
+		"rotation": 158.11,
+		"x": -20.99,
+		"y": 371.77,
+		"color": "e64344ff"
+	},
+	{
+		"name": "tread22",
+		"parent": "tread",
+		"length": 82,
+		"rotation": 157.99,
+		"x": -97.02,
+		"y": 402.28,
+		"color": "e64344ff"
+	},
+	{
+		"name": "tread23",
+		"parent": "tread",
+		"length": 82,
+		"rotation": 157.59,
+		"x": -172.83,
+		"y": 433.33,
+		"color": "e64344ff"
+	},
+	{
+		"name": "tread24",
+		"parent": "tread",
+		"length": 82,
+		"rotation": 156.86,
+		"x": -248.23,
+		"y": 465.34,
+		"color": "e64344ff"
+	},
+	{
+		"name": "tread25",
+		"parent": "tread",
+		"length": 82,
+		"rotation": 177.94,
+		"x": -330.17,
+		"y": 468.27,
+		"color": "e64344ff"
+	},
+	{
+		"name": "tread26",
+		"parent": "tread",
+		"length": 82,
+		"rotation": -169.55,
+		"x": -410.81,
+		"y": 453.5,
+		"color": "e64344ff"
+	},
+	{
+		"name": "tread27",
+		"parent": "tread",
+		"length": 82,
+		"rotation": -163.86,
+		"x": -489.58,
+		"y": 430.86,
+		"color": "e64344ff"
+	},
+	{
+		"name": "tread28",
+		"parent": "tread",
+		"length": 82,
+		"rotation": -139.13,
+		"x": -551.59,
+		"y": 377.57,
+		"color": "e64344ff"
+	},
+	{
+		"name": "tread29",
+		"parent": "tread",
+		"length": 82,
+		"rotation": -89.04,
+		"x": -550.21,
+		"y": 296.14,
+		"color": "e64344ff"
+	},
+	{
+		"name": "tread3",
+		"parent": "tread",
+		"length": 82,
+		"rotation": -8.91,
+		"x": 163.01,
+		"y": -12.61,
+		"color": "e64344ff"
+	},
+	{
+		"name": "tread30",
+		"parent": "tread",
+		"length": 82,
+		"rotation": -38.99,
+		"x": -486.48,
+		"y": 244.89,
+		"color": "e64344ff"
+	},
+	{
+		"name": "tread31",
+		"parent": "tread",
+		"length": 82,
+		"rotation": -20.04,
+		"x": -409.45,
+		"y": 216.98,
+		"color": "e64344ff"
+	},
+	{
+		"name": "tread32",
+		"parent": "tread",
+		"length": 82,
+		"rotation": -46.24,
+		"x": -352.74,
+		"y": 158.15,
+		"color": "e64344ff"
+	},
+	{
+		"name": "tread33",
+		"parent": "tread",
+		"length": 82,
+		"rotation": -27.95,
+		"x": -280.3,
+		"y": 119.98,
+		"color": "e64344ff"
+	},
+	{
+		"name": "tread34",
+		"parent": "tread",
+		"length": 82,
+		"rotation": 10.46,
+		"x": -199.66,
+		"y": 134.77,
+		"color": "e64344ff"
+	},
+	{
+		"name": "tread35",
+		"parent": "tread",
+		"length": 82,
+		"rotation": -17.9,
+		"x": -121.63,
+		"y": 109.73,
+		"color": "e64344ff"
+	},
+	{
+		"name": "tread36",
+		"parent": "tread",
+		"length": 82,
+		"rotation": -36.82,
+		"x": -55.99,
+		"y": 60.92,
+		"color": "fbff00ff"
+	},
+	{
+		"name": "tread4",
+		"parent": "tread",
+		"length": 82,
+		"rotation": -29.27,
+		"x": 234.55,
+		"y": -52.43,
+		"color": "e64344ff"
+	},
+	{
+		"name": "tread5",
+		"parent": "tread",
+		"length": 82,
+		"rotation": -45.26,
+		"x": 292.26,
+		"y": -110.28,
+		"color": "e64344ff"
+	},
+	{
+		"name": "tread6",
+		"parent": "tread",
+		"length": 82,
+		"rotation": -15.29,
+		"x": 371.36,
+		"y": -131.76,
+		"color": "e64344ff"
+	},
+	{
+		"name": "tread7",
+		"parent": "tread",
+		"length": 82,
+		"rotation": -5.49,
+		"x": 452.98,
+		"y": -139.55,
+		"color": "e64344ff"
+	},
+	{
+		"name": "tread8",
+		"parent": "tread",
+		"length": 82,
+		"rotation": -24.99,
+		"x": 527.31,
+		"y": -173.95,
+		"color": "e64344ff"
+	},
+	{
+		"name": "tread9",
+		"parent": "tread",
+		"length": 82,
+		"rotation": -5.44,
+		"x": 608.94,
+		"y": -181.68,
+		"color": "e64344ff"
+	},
+	{ "name": "wheel-big-root1", "parent": "tank-treads", "x": -549.6, "y": 14.4, "color": "abe323ff" },
+	{ "name": "wheel-big-root2", "parent": "tank-treads", "x": 547.34, "y": 14.4 },
+	{ "name": "wheel-big1", "parent": "wheel-big-root1", "x": -0.02, "color": "abe323ff" },
+	{ "name": "wheel-big2", "parent": "wheel-big-root2" },
+	{ "name": "wheel-mid-root1", "parent": "wheel-mid-center", "x": -410.57, "color": "abe323ff" },
+	{ "name": "wheel-mid-root2", "parent": "wheel-mid-center", "x": -246.95 },
+	{ "name": "wheel-mid-root3", "parent": "wheel-mid-center", "x": -82.73 },
+	{ "name": "wheel-mid-root4", "parent": "wheel-mid-center", "x": 80.89 },
+	{ "name": "wheel-mid-root5", "parent": "wheel-mid-center", "x": 244.51 },
+	{ "name": "wheel-mid-root6", "parent": "wheel-mid-center", "x": 408.74 },
+	{ "name": "wheel-mid1", "parent": "wheel-mid-root1", "color": "abe323ff" },
+	{ "name": "wheel-mid2", "parent": "wheel-mid-root2" },
+	{ "name": "wheel-mid3", "parent": "wheel-mid-root3" },
+	{ "name": "wheel-mid4", "parent": "wheel-mid-root4" },
+	{ "name": "wheel-mid5", "parent": "wheel-mid-root5" },
+	{ "name": "wheel-mid6", "parent": "wheel-mid-root6" },
+	{ "name": "wheel-small-root1", "parent": "tank-treads", "x": -337.39, "y": 109.43 },
+	{ "name": "wheel-small-root2", "parent": "tank-treads", "x": 0.09, "y": 109.43 },
+	{ "name": "wheel-small-root3", "parent": "tank-treads", "x": 334.69, "y": 109.43 },
+	{ "name": "wheel-small1", "parent": "wheel-small-root1", "color": "abe323ff" },
+	{ "name": "wheel-small2", "parent": "wheel-small-root2" },
+	{ "name": "wheel-small3", "parent": "wheel-small-root3" }
+],
+"slots": [
+	{ "name": "rock", "bone": "rock", "attachment": "rock" },
+	{ "name": "ground", "bone": "root", "attachment": "ground" },
+	{ "name": "ground2", "bone": "root", "attachment": "ground" },
+	{ "name": "ground3", "bone": "root", "attachment": "ground" },
+	{ "name": "ground4", "bone": "root", "attachment": "ground" },
+	{ "name": "ground5", "bone": "root", "attachment": "ground" },
+	{ "name": "ground6", "bone": "root", "attachment": "ground" },
+	{ "name": "ground7", "bone": "root", "attachment": "ground" },
+	{ "name": "tank-body-shadow", "bone": "tank-body", "color": "ffffffb9", "attachment": "tank-bottom-shadow" },
+	{ "name": "bottom", "bone": "tank-body", "attachment": "tank-bottom" },
+	{ "name": "tread-inside1", "bone": "tread", "attachment": "tread-inside" },
+	{ "name": "tread-inside53", "bone": "tread27", "attachment": "tread-inside" },
+	{ "name": "tread-inside27", "bone": "tread14", "attachment": "tread-inside" },
+	{ "name": "tread-inside3", "bone": "tread2", "attachment": "tread-inside" },
+	{ "name": "tread-inside55", "bone": "tread28", "attachment": "tread-inside" },
+	{ "name": "tread-inside29", "bone": "tread15", "attachment": "tread-inside" },
+	{ "name": "tread-inside5", "bone": "tread3", "attachment": "tread-inside" },
+	{ "name": "tread-inside57", "bone": "tread29", "attachment": "tread-inside" },
+	{ "name": "tread-inside31", "bone": "tread16", "attachment": "tread-inside" },
+	{ "name": "tread-inside7", "bone": "tread4", "attachment": "tread-inside" },
+	{ "name": "tread-inside59", "bone": "tread30", "attachment": "tread-inside" },
+	{ "name": "tread-inside33", "bone": "tread17", "attachment": "tread-inside" },
+	{ "name": "tread-inside9", "bone": "tread5", "attachment": "tread-inside" },
+	{ "name": "tread-inside61", "bone": "tread31", "attachment": "tread-inside" },
+	{ "name": "tread-inside35", "bone": "tread18", "attachment": "tread-inside" },
+	{ "name": "tread-inside11", "bone": "tread6", "attachment": "tread-inside" },
+	{ "name": "tread-inside63", "bone": "tread32", "attachment": "tread-inside" },
+	{ "name": "tread-inside37", "bone": "tread19", "attachment": "tread-inside" },
+	{ "name": "tread-inside13", "bone": "tread7", "attachment": "tread-inside" },
+	{ "name": "tread-inside65", "bone": "tread33", "attachment": "tread-inside" },
+	{ "name": "tread-inside39", "bone": "tread20", "attachment": "tread-inside" },
+	{ "name": "tread-inside15", "bone": "tread8", "attachment": "tread-inside" },
+	{ "name": "tread-inside67", "bone": "tread34", "attachment": "tread-inside" },
+	{ "name": "tread-inside69", "bone": "tread35", "attachment": "tread-inside" },
+	{ "name": "tread-inside71", "bone": "tread36", "attachment": "tread-inside" },
+	{ "name": "tread-inside41", "bone": "tread21", "attachment": "tread-inside" },
+	{ "name": "tread-inside17", "bone": "tread9", "attachment": "tread-inside" },
+	{ "name": "tread-inside43", "bone": "tread22", "attachment": "tread-inside" },
+	{ "name": "tread-inside19", "bone": "tread10", "attachment": "tread-inside" },
+	{ "name": "tread-inside45", "bone": "tread23", "attachment": "tread-inside" },
+	{ "name": "tread-inside21", "bone": "tread11", "attachment": "tread-inside" },
+	{ "name": "tread-inside47", "bone": "tread24", "attachment": "tread-inside" },
+	{ "name": "tread-inside23", "bone": "tread12", "attachment": "tread-inside" },
+	{ "name": "tread-inside49", "bone": "tread25", "attachment": "tread-inside" },
+	{ "name": "tread-inside25", "bone": "tread13", "attachment": "tread-inside" },
+	{ "name": "tread-inside51", "bone": "tread26", "attachment": "tread-inside" },
+	{ "name": "tread-inside2", "bone": "tread", "attachment": "tread-inside" },
+	{ "name": "tread-inside54", "bone": "tread27", "attachment": "tread-inside" },
+	{ "name": "tread-inside28", "bone": "tread14", "attachment": "tread-inside" },
+	{ "name": "tread-inside4", "bone": "tread2", "attachment": "tread-inside" },
+	{ "name": "tread-inside56", "bone": "tread28", "attachment": "tread-inside" },
+	{ "name": "tread-inside30", "bone": "tread15", "attachment": "tread-inside" },
+	{ "name": "tread-inside6", "bone": "tread3", "attachment": "tread-inside" },
+	{ "name": "tread-inside58", "bone": "tread29", "attachment": "tread-inside" },
+	{ "name": "tread-inside32", "bone": "tread16", "attachment": "tread-inside" },
+	{ "name": "tread-inside8", "bone": "tread4", "attachment": "tread-inside" },
+	{ "name": "tread-inside60", "bone": "tread30", "attachment": "tread-inside" },
+	{ "name": "tread-inside34", "bone": "tread17", "attachment": "tread-inside" },
+	{ "name": "tread-inside10", "bone": "tread5", "attachment": "tread-inside" },
+	{ "name": "tread-inside62", "bone": "tread31", "attachment": "tread-inside" },
+	{ "name": "tread-inside36", "bone": "tread18", "attachment": "tread-inside" },
+	{ "name": "tread-inside12", "bone": "tread6", "attachment": "tread-inside" },
+	{ "name": "tread-inside64", "bone": "tread32", "attachment": "tread-inside" },
+	{ "name": "tread-inside38", "bone": "tread19", "attachment": "tread-inside" },
+	{ "name": "tread-inside14", "bone": "tread7", "attachment": "tread-inside" },
+	{ "name": "tread-inside66", "bone": "tread33", "attachment": "tread-inside" },
+	{ "name": "tread-inside40", "bone": "tread20", "attachment": "tread-inside" },
+	{ "name": "tread-inside16", "bone": "tread8", "attachment": "tread-inside" },
+	{ "name": "tread-inside68", "bone": "tread34", "attachment": "tread-inside" },
+	{ "name": "tread-inside70", "bone": "tread35", "attachment": "tread-inside" },
+	{ "name": "tread-inside72", "bone": "tread36", "attachment": "tread-inside" },
+	{ "name": "tread-inside42", "bone": "tread21", "attachment": "tread-inside" },
+	{ "name": "tread-inside18", "bone": "tread9", "attachment": "tread-inside" },
+	{ "name": "tread-inside44", "bone": "tread22", "attachment": "tread-inside" },
+	{ "name": "tread-inside20", "bone": "tread10", "attachment": "tread-inside" },
+	{ "name": "tread-inside46", "bone": "tread23", "attachment": "tread-inside" },
+	{ "name": "tread-inside22", "bone": "tread11", "attachment": "tread-inside" },
+	{ "name": "tread-inside48", "bone": "tread24", "attachment": "tread-inside" },
+	{ "name": "tread-inside24", "bone": "tread12", "attachment": "tread-inside" },
+	{ "name": "tread-inside50", "bone": "tread25", "attachment": "tread-inside" },
+	{ "name": "tread-inside26", "bone": "tread13", "attachment": "tread-inside" },
+	{ "name": "tread-inside52", "bone": "tread26", "attachment": "tread-inside" },
+	{ "name": "wheel-big", "bone": "wheel-big1", "color": "dbdbdbff", "attachment": "wheel-big" },
+	{ "name": "wheel-big2", "bone": "wheel-big2", "color": "dbdbdbff", "attachment": "wheel-big" },
+	{ "name": "wheel-mid", "bone": "wheel-mid1", "attachment": "wheel-mid" },
+	{ "name": "wheel-mid2", "bone": "wheel-mid2", "attachment": "wheel-mid" },
+	{ "name": "wheel-mid3", "bone": "wheel-mid3", "attachment": "wheel-mid" },
+	{ "name": "wheel-mid4", "bone": "wheel-mid4", "attachment": "wheel-mid" },
+	{ "name": "wheel-mid5", "bone": "wheel-mid5", "attachment": "wheel-mid" },
+	{ "name": "wheel-mid6", "bone": "wheel-mid6", "attachment": "wheel-mid" },
+	{ "name": "wheel-small", "bone": "wheel-small1", "attachment": "wheel-small" },
+	{ "name": "wheel-small2", "bone": "wheel-small2", "attachment": "wheel-small" },
+	{ "name": "wheel-small3", "bone": "wheel-small3", "attachment": "wheel-small" },
+	{ "name": "wheel-mid-overlay", "bone": "wheel-mid-root1", "color": "ffffffec", "attachment": "wheel-mid-overlay", "blend": "multiply" },
+	{ "name": "wheel-mid-overlay2", "bone": "wheel-mid-root2", "color": "ffffffec", "attachment": "wheel-mid-overlay", "blend": "multiply" },
+	{ "name": "wheel-mid-overlay3", "bone": "wheel-mid-root3", "color": "ffffffec", "attachment": "wheel-mid-overlay", "blend": "multiply" },
+	{ "name": "wheel-mid-overlay4", "bone": "wheel-mid-root4", "color": "ffffffec", "attachment": "wheel-mid-overlay", "blend": "multiply" },
+	{ "name": "wheel-mid-overlay5", "bone": "wheel-mid-root5", "color": "ffffffec", "attachment": "wheel-mid-overlay", "blend": "multiply" },
+	{ "name": "wheel-mid-overlay6", "bone": "wheel-mid-root6", "color": "ffffffec", "attachment": "wheel-mid-overlay", "blend": "multiply" },
+	{ "name": "wheel-big-overlay1", "bone": "wheel-big-root1", "color": "ffffffe9", "attachment": "wheel-big-overlay", "blend": "multiply" },
+	{ "name": "wheel-big-overlay2", "bone": "wheel-big-root2", "color": "ffffffe9", "attachment": "wheel-big-overlay", "blend": "multiply" },
+	{ "name": "treads-path", "bone": "tank-root", "attachment": "treads-path" },
+	{ "name": "tread", "bone": "tread", "attachment": "tread" },
+	{ "name": "tread27", "bone": "tread27", "color": "adc9b8ff", "attachment": "tread" },
+	{ "name": "tread14", "bone": "tread14", "attachment": "tread" },
+	{ "name": "tread2", "bone": "tread2", "attachment": "tread" },
+	{ "name": "tread28", "bone": "tread28", "attachment": "tread" },
+	{ "name": "tread15", "bone": "tread15", "color": "adc9b8ff", "attachment": "tread" },
+	{ "name": "tread3", "bone": "tread3", "color": "adc9b8ff", "attachment": "tread" },
+	{ "name": "tread29", "bone": "tread29", "color": "adc9b8ff", "attachment": "tread" },
+	{ "name": "tread16", "bone": "tread16", "attachment": "tread" },
+	{ "name": "tread4", "bone": "tread4", "attachment": "tread" },
+	{ "name": "tread30", "bone": "tread30", "attachment": "tread" },
+	{ "name": "tread17", "bone": "tread17", "color": "adc9b8ff", "attachment": "tread" },
+	{ "name": "tread5", "bone": "tread5", "color": "adc9b8ff", "attachment": "tread" },
+	{ "name": "tread31", "bone": "tread31", "color": "adc9b8ff", "attachment": "tread" },
+	{ "name": "tread18", "bone": "tread18", "attachment": "tread" },
+	{ "name": "tread6", "bone": "tread6", "attachment": "tread" },
+	{ "name": "tread32", "bone": "tread32", "attachment": "tread" },
+	{ "name": "tread19", "bone": "tread19", "color": "adc9b8ff", "attachment": "tread" },
+	{ "name": "tread7", "bone": "tread7", "color": "adc9b8ff", "attachment": "tread" },
+	{ "name": "tread33", "bone": "tread33", "color": "adc9b8ff", "attachment": "tread" },
+	{ "name": "tread20", "bone": "tread20", "attachment": "tread" },
+	{ "name": "tread8", "bone": "tread8", "attachment": "tread" },
+	{ "name": "tread34", "bone": "tread34", "attachment": "tread" },
+	{ "name": "tread35", "bone": "tread35", "color": "adc9b8ff", "attachment": "tread" },
+	{ "name": "tread36", "bone": "tread36", "color": "adc9b8ff", "attachment": "tread" },
+	{ "name": "tread21", "bone": "tread21", "color": "adc9b8ff", "attachment": "tread" },
+	{ "name": "tread9", "bone": "tread9", "color": "adc9b8ff", "attachment": "tread" },
+	{ "name": "tread22", "bone": "tread22", "attachment": "tread" },
+	{ "name": "tread10", "bone": "tread10", "attachment": "tread" },
+	{ "name": "tread23", "bone": "tread23", "color": "adc9b8ff", "attachment": "tread" },
+	{ "name": "tread11", "bone": "tread11", "color": "adc9b8ff", "attachment": "tread" },
+	{ "name": "tread24", "bone": "tread24", "attachment": "tread" },
+	{ "name": "tread12", "bone": "tread12", "attachment": "tread" },
+	{ "name": "tread25", "bone": "tread25", "color": "adc9b8ff", "attachment": "tread" },
+	{ "name": "tread13", "bone": "tread13", "color": "adc9b8ff", "attachment": "tread" },
+	{ "name": "tread26", "bone": "tread26", "attachment": "tread" },
+	{ "name": "machinegun", "bone": "machinegun", "attachment": "machinegun" },
+	{ "name": "machinegun-mount", "bone": "machinegun-mount", "attachment": "machinegun-mount" },
+	{ "name": "tank-top", "bone": "tank-body", "attachment": "tank-top" },
+	{ "name": "guntower", "bone": "guntower", "attachment": "guntower" },
+	{ "name": "cannon", "bone": "cannon", "attachment": "cannon" },
+	{ "name": "cannon-connector", "bone": "cannon-connector", "attachment": "cannon-connector" },
+	{ "name": "antenna", "bone": "antenna-root", "attachment": "antenna" },
+	{ "name": "smoke-puff1-bg", "bone": "smoke1", "color": "ecececff", "dark": "000000" },
+	{ "name": "smoke-puff1-bg13", "bone": "smoke13", "color": "ecececff", "dark": "000000" },
+	{ "name": "smoke-puff1-bg14", "bone": "smoke14", "color": "ecececff", "dark": "000000" },
+	{ "name": "smoke-puff1-bg15", "bone": "smoke15", "color": "ecececff", "dark": "000000" },
+	{ "name": "smoke-puff1-bg16", "bone": "smoke16", "color": "ecececff", "dark": "000000" },
+	{ "name": "smoke-puff1-bg17", "bone": "smoke17", "color": "ecececff", "dark": "000000" },
+	{ "name": "smoke-puff1-bg18", "bone": "smoke18", "color": "ecececff", "dark": "000000" },
+	{ "name": "smoke-puff1-bg20", "bone": "smoke20", "color": "ecececff", "dark": "000000" },
+	{ "name": "smoke-puff1-bg21", "bone": "smoke21", "color": "ecececff", "dark": "000000" },
+	{ "name": "smoke-puff1-bg22", "bone": "smoke22", "color": "ecececff", "dark": "000000" },
+	{ "name": "smoke-puff1-bg23", "bone": "smoke23", "color": "ecececff", "dark": "000000" },
+	{ "name": "smoke-puff1-bg24", "bone": "smoke24", "color": "ecececff", "dark": "000000" },
+	{ "name": "smoke-puff1-bg25", "bone": "smoke25", "color": "ecececff", "dark": "000000" },
+	{ "name": "smoke-puff1-bg26", "bone": "smoke26", "color": "ecececff", "dark": "000000" },
+	{ "name": "smoke-puff1-bg27", "bone": "smoke27", "color": "ecececff", "dark": "000000" },
+	{ "name": "smoke-puff1-bg2", "bone": "smoke2", "color": "ecececff", "dark": "000000" },
+	{ "name": "smoke-puff1-bg5", "bone": "smoke5", "color": "ecececff", "dark": "000000" },
+	{ "name": "smoke-puff1-bg6", "bone": "smoke6", "color": "ecececff", "dark": "000000" },
+	{ "name": "smoke-puff1-bg7", "bone": "smoke7", "color": "ecececff", "dark": "000000" },
+	{ "name": "smoke-puff1-bg10", "bone": "smoke10", "color": "ecececff", "dark": "000000" },
+	{ "name": "smoke-puff1-bg11", "bone": "smoke11", "color": "ecececff", "dark": "000000" },
+	{ "name": "smoke-puff1-bg12", "bone": "smoke12", "color": "ecececff", "dark": "000000" },
+	{ "name": "smoke-puff1-bg8", "bone": "smoke8", "color": "ecececff", "dark": "000000" },
+	{ "name": "smoke-puff1-bg9", "bone": "smoke9", "color": "ecececff", "dark": "000000" },
+	{ "name": "smoke-puff1-bg4", "bone": "smoke4", "color": "ecececff", "dark": "000000" },
+	{ "name": "smoke-puff1-bg3", "bone": "smoke3", "color": "ecececff", "dark": "000000" },
+	{ "name": "smoke-puff1-fg", "bone": "smoke1", "color": "ecececff", "dark": "000000" },
+	{ "name": "smoke-puff1-fg13", "bone": "smoke13", "color": "ecececff", "dark": "000000" },
+	{ "name": "smoke-puff1-fg14", "bone": "smoke14", "color": "ecececff", "dark": "000000" },
+	{ "name": "smoke-puff1-fg15", "bone": "smoke15", "color": "ecececff", "dark": "000000" },
+	{ "name": "smoke-puff1-fg16", "bone": "smoke16", "color": "ecececff", "dark": "000000" },
+	{ "name": "smoke-puff1-fg17", "bone": "smoke17", "color": "ecececff", "dark": "000000" },
+	{ "name": "smoke-puff1-fg18", "bone": "smoke18", "color": "ecececff", "dark": "000000" },
+	{ "name": "smoke-puff1-fg20", "bone": "smoke20", "color": "ecececff", "dark": "000000" },
+	{ "name": "smoke-puff1-fg21", "bone": "smoke21", "color": "ecececff", "dark": "000000" },
+	{ "name": "smoke-puff1-fg22", "bone": "smoke22", "color": "ecececff", "dark": "000000" },
+	{ "name": "smoke-puff1-fg23", "bone": "smoke23", "color": "ecececff", "dark": "000000" },
+	{ "name": "smoke-puff1-fg24", "bone": "smoke24", "color": "ecececff", "dark": "000000" },
+	{ "name": "smoke-puff1-fg25", "bone": "smoke25", "color": "ecececff", "dark": "000000" },
+	{ "name": "smoke-puff1-fg26", "bone": "smoke26", "color": "ecececff", "dark": "000000" },
+	{ "name": "smoke-puff1-fg27", "bone": "smoke27", "color": "ecececff", "dark": "000000" },
+	{ "name": "smoke-puff1-fg2", "bone": "smoke2", "color": "ecececff", "dark": "000000" },
+	{ "name": "smoke-puff1-fg5", "bone": "smoke5", "color": "ecececff", "dark": "000000" },
+	{ "name": "smoke-puff1-fg6", "bone": "smoke6", "color": "ecececff", "dark": "000000" },
+	{ "name": "smoke-puff1-fg7", "bone": "smoke7", "color": "ecececff", "dark": "000000" },
+	{ "name": "smoke-puff1-fg10", "bone": "smoke10", "color": "ecececff", "dark": "000000" },
+	{ "name": "smoke-puff1-fg11", "bone": "smoke11", "color": "ecececff", "dark": "000000" },
+	{ "name": "smoke-puff1-fg12", "bone": "smoke12", "color": "ecececff", "dark": "000000" },
+	{ "name": "smoke-puff1-fg8", "bone": "smoke8", "color": "ecececff", "dark": "000000" },
+	{ "name": "smoke-puff1-fg9", "bone": "smoke9", "color": "ecececff", "dark": "000000" },
+	{ "name": "smoke-puff1-fg4", "bone": "smoke4", "color": "ecececff", "dark": "000000" },
+	{ "name": "smoke-puff1-fg3", "bone": "smoke3", "color": "ecececff", "dark": "000000" },
+	{ "name": "smoke-glow", "bone": "smoke-glow", "blend": "additive" },
+	{ "name": "clipping", "bone": "tank-body", "attachment": "clipping" },
+	{ "name": "tank-glow", "bone": "tank-glow", "color": "fcdc6da7", "blend": "additive" }
+],
+"ik": [
+	{
+		"name": "cannon-ik",
+		"bones": [ "cannon" ],
+		"target": "cannon-target"
+	},
+	{
+		"name": "machinegun-ik",
+		"order": 1,
+		"bones": [ "machinegun" ],
+		"target": "machinegun-target",
+		"mix": 0
+	}
+],
+"transform": [
+	{
+		"name": "wheel-big-transform",
+		"order": 8,
+		"bones": [ "wheel-big2" ],
+		"target": "wheel-big1",
+		"rotation": 65.6,
+		"mixX": 0,
+		"mixScaleX": 0,
+		"mixShearY": 0
+	},
+	{
+		"name": "wheel-mid1-transform",
+		"order": 3,
+		"bones": [ "wheel-mid2", "wheel-mid4" ],
+		"target": "wheel-mid1",
+		"rotation": 93,
+		"mixX": 0,
+		"mixScaleX": 0,
+		"mixShearY": 0
+	},
+	{
+		"name": "wheel-mid2-transform",
+		"order": 4,
+		"bones": [ "wheel-mid3", "wheel-mid5" ],
+		"target": "wheel-mid1",
+		"rotation": -89,
+		"mixX": 0,
+		"mixScaleX": 0,
+		"mixShearY": 0
+	},
+	{
+		"name": "wheel-mid3-transform",
+		"order": 5,
+		"bones": [ "wheel-mid6" ],
+		"target": "wheel-mid1",
+		"rotation": -152.6,
+		"mixX": 0,
+		"mixScaleX": 0,
+		"mixShearY": 0
+	},
+	{
+		"name": "wheel-small1-transform",
+		"order": 6,
+		"bones": [ "wheel-small2" ],
+		"target": "wheel-small1",
+		"rotation": 87,
+		"mixX": 0,
+		"mixScaleX": 0,
+		"mixShearY": 0
+	},
+	{
+		"name": "wheel-small2-transform",
+		"order": 7,
+		"bones": [ "wheel-small3" ],
+		"target": "wheel-small1",
+		"rotation": 54.9,
+		"mixX": 0,
+		"mixScaleX": 0,
+		"mixShearY": 0
+	}
+],
+"path": [
+	{
+		"name": "treads-path",
+		"order": 2,
+		"bones": [ "tread", "tread2", "tread3", "tread4", "tread5", "tread6", "tread7", "tread8", "tread9", "tread10", "tread11", "tread12", "tread13", "tread14", "tread15", "tread16", "tread17", "tread18", "tread19", "tread20", "tread21", "tread22", "tread23", "tread24", "tread25", "tread26", "tread27", "tread28", "tread29", "tread30", "tread31", "tread32", "tread33", "tread34", "tread35", "tread36" ],
+		"target": "treads-path",
+		"rotateMode": "chain"
+	}
+],
+"skins": [
+	{
+		"name": "default",
+		"attachments": {
+			"antenna": {
+				"antenna": {
+					"type": "mesh",
+					"uvs": [ 0.64286, 0.07876, 0.65354, 0.1535, 0.66325, 0.22138, 0.67367, 0.29433, 0.68383, 0.36543, 0.6936, 0.43374, 0.70311, 0.5003, 0.71311, 0.57031, 0.72327, 0.64139, 0.73406, 0.71689, 0.74441, 0.7893, 0.75614, 0.87141, 0.76905, 0.94311, 1, 0.94311, 1, 1, 0, 1, 0, 0.94311, 0.20106, 0.94311, 0.20106, 0.87094, 0.21461, 0.78847, 0.22651, 0.71607, 0.23886, 0.64099, 0.25036, 0.57105, 0.26206, 0.49983, 0.27306, 0.43291, 0.2843, 0.36454, 0.29593, 0.29382, 0.308, 0.22038, 0.319, 0.15345, 0.33142, 0.07796, 0.34423, 0, 0.63161, 0 ],
+					"triangles": [ 30, 31, 0, 29, 30, 0, 29, 0, 1, 28, 29, 1, 28, 1, 2, 27, 28, 2, 27, 2, 3, 26, 3, 4, 25, 26, 4, 25, 4, 5, 26, 27, 3, 24, 5, 6, 23, 24, 6, 7, 23, 6, 24, 25, 5, 22, 7, 8, 21, 22, 8, 21, 8, 9, 7, 22, 23, 20, 9, 10, 19, 20, 10, 20, 21, 9, 19, 10, 11, 18, 19, 11, 17, 18, 11, 17, 11, 12, 15, 16, 17, 12, 13, 14, 15, 17, 12, 14, 15, 12 ],
+					"vertices": [ 2, 10, 65.38, -3.14, 0.3125, 11, 23.38, -3.14, 0.6875, 2, 10, 42.73, -3.38, 0.66667, 11, 0.73, -3.38, 0.33333, 2, 9, 64.17, -3.59, 0.33333, 10, 22.17, -3.59, 0.66667, 2, 9, 42.06, -3.82, 0.66667, 10, 0.06, -3.82, 0.33333, 2, 8, 62.52, -4.04, 0.33333, 9, 20.52, -4.04, 0.66667, 2, 8, 41.82, -4.26, 0.66667, 9, -0.18, -4.26, 0.33333, 2, 7, 63.65, -4.47, 0.33333, 8, 21.65, -4.47, 0.66667, 2, 7, 42.44, -4.69, 0.66667, 8, 0.44, -4.69, 0.33333, 2, 6, 62.9, -4.91, 0.33333, 7, 20.9, -4.91, 0.66667, 2, 6, 40.03, -5.15, 0.66667, 7, -1.97, -5.15, 0.33333, 2, 5, 5.38, 58.09, 0.4, 6, 18.09, -5.38, 0.6, 1, 5, 5.64, 33.21, 1, 1, 5, 5.92, 11.48, 1, 1, 5, 11, 11.48, 1, 1, 5, 11, -5.76, 1, 1, 5, -11, -5.76, 1, 1, 5, -11, 11.48, 1, 1, 5, -6.58, 11.48, 1, 1, 5, -6.58, 33.35, 1, 2, 5, -6.28, 58.34, 0.4, 6, 18.34, 6.28, 0.6, 2, 6, 40.27, 6.02, 0.66667, 7, -1.73, 6.02, 0.33333, 2, 6, 63.03, 5.75, 0.33333, 7, 21.03, 5.75, 0.66667, 2, 7, 42.22, 5.49, 0.66667, 8, 0.22, 5.49, 0.33333, 2, 7, 63.8, 5.23, 0.33333, 8, 21.8, 5.23, 0.66667, 2, 8, 42.07, 4.99, 0.66667, 9, 0.07, 4.99, 0.33333, 2, 8, 62.79, 4.75, 0.33333, 9, 20.79, 4.75, 0.66667, 2, 9, 42.22, 4.49, 0.66667, 10, 0.22, 4.49, 0.33333, 2, 9, 64.47, 4.22, 0.33333, 10, 22.47, 4.22, 0.66667, 2, 10, 42.75, 3.98, 0.66667, 11, 0.75, 3.98, 0.33333, 2, 10, 65.62, 3.71, 0.3125, 11, 23.62, 3.71, 0.6875, 1, 11, 47.24, 3.43, 1, 1, 11, 47.24, -2.9, 1 ],
+					"hull": 32,
+					"edges": [ 28, 30, 28, 26, 30, 32, 26, 24, 24, 22, 32, 34, 34, 24, 34, 36, 36, 22, 60, 62, 38, 36, 20, 22, 38, 20, 40, 38, 18, 20, 40, 18, 42, 40, 16, 18, 42, 16, 44, 42, 14, 16, 44, 14, 46, 44, 12, 14, 46, 12, 48, 46, 10, 12, 48, 10, 50, 48, 8, 10, 50, 8, 52, 50, 6, 8, 52, 6, 54, 52, 4, 6, 54, 4, 56, 54, 2, 4, 56, 2, 60, 58, 58, 56, 62, 0, 0, 2, 58, 0 ],
+					"width": 22,
+					"height": 303
+				}
+			},
+			"bottom": {
+				"tank-bottom": { "x": -16.67, "y": 9.89, "width": 1285, "height": 276 }
+			},
+			"cannon": {
+				"cannon": { "x": 481.95, "y": -0.03, "rotation": 180, "width": 931, "height": 58 }
+			},
+			"cannon-connector": {
+				"cannon-connector": {
+					"type": "mesh",
+					"uvs": [ 1, 0.03237, 1, 0.10603, 0.90988, 0.32859, 0.81975, 0.55116, 0.72963, 0.77373, 0.6395, 0.9963, 0.42157, 0.9963, 0.20364, 0.9963, 0, 0.85434, 0, 0.69902, 0.02268, 0.52884, 0, 0.31444, 0.21602, 0.12998, 0.43368, 0, 0.63547, 0.0037, 0.48408, 0.77059, 0.31496, 0.52497, 0.64133, 0.19648, 0.21516, 0.76766, 0.58346, 0.56471, 0.68444, 0.40146, 0.46758, 0.36649, 0.28935, 0.34604 ],
+					"triangles": [ 7, 18, 6, 6, 18, 15, 7, 8, 18, 8, 9, 18, 18, 16, 15, 15, 16, 19, 9, 10, 18, 18, 10, 16, 16, 21, 19, 19, 21, 20, 10, 22, 16, 10, 11, 22, 16, 22, 21, 21, 17, 20, 21, 12, 13, 17, 13, 14, 17, 21, 13, 11, 12, 22, 21, 22, 12, 6, 15, 5, 5, 15, 4, 15, 19, 4, 4, 19, 3, 19, 20, 3, 3, 20, 2, 20, 17, 2, 2, 17, 1, 17, 14, 1, 14, 0, 1 ],
+					"vertices": [ 1, 12, 35.91, 69.08, 1, 1, 12, 35.91, 59.14, 1, 1, 12, 25.82, 29.09, 1, 1, 12, 15.72, -0.95, 1, 1, 12, 5.63, -31, 1, 1, 12, -4.46, -61.05, 1, 2, 12, -28.87, -61.05, 0.33333, 14, 28.87, 61.03, 0.66667, 1, 14, 53.28, 61.02, 1, 1, 14, 76.09, 41.84, 1, 1, 14, 71.17, 21.63, 1, 1, 14, 72.83, -1.62, 1, 1, 14, 70.38, -29.12, 1, 1, 14, 50.67, -56.14, 1, 2, 12, -28.43, 74.38, 0.41, 14, 28.43, -74.4, 0.59, 2, 12, -4.92, 72.95, 0.52, 14, 4.92, -72.95, 0.48, 2, 12, -21.87, -30.58, 0.49, 14, 21.87, 30.57, 0.51, 1, 14, 40.81, -2.6, 1, 2, 12, -4.26, 46.93, 0.49, 14, 4.26, -46.93, 0.51, 1, 14, 51.99, 30.15, 1, 2, 12, -10.74, -2.78, 0.49, 14, 10.74, 2.78, 0.51, 2, 12, 0.57, 19.25, 0.49, 14, -0.57, -19.25, 0.51, 1, 14, 23.72, -23.99, 1, 1, 14, 43.68, -26.76, 1 ],
+					"hull": 15,
+					"edges": [ 0, 2, 2, 4, 4, 6, 6, 8, 8, 10, 10, 12, 12, 14, 14, 16, 16, 18, 18, 20, 20, 22, 22, 24, 24, 26, 26, 28, 28, 0 ],
+					"width": 112,
+					"height": 135
+				}
+			},
+			"clipping": {
+				"clipping": {
+					"type": "clipping",
+					"end": "tank-glow",
+					"vertexCount": 32,
+					"vertices": [ 1, 3, 165.84, 455.67, 1, 1, 3, 114.21, 493.01, 1, 1, 3, -38.53, 492.23, 1, 1, 3, -193.4, 464.18, 1, 2, 3, -280.85, 415.48, 0.752, 14, 24.09, -73.93, 0.248, 1, 14, 70.34, -27.32, 1, 1, 14, 412.56, -22.02, 1, 1, 14, 412.82, -29.21, 1, 1, 14, 539.26, -29.34, 1, 1, 14, 539.52, -17.09, 1, 1, 14, 894.02, -16.8, 1, 1, 14, 902.99, -28.89, 1, 1, 14, 942.06, -28.58, 1, 1, 14, 948.14, -16.64, 1, 1, 14, 947.9, 14.29, 1, 1, 14, 539.3, 14.55, 1, 1, 14, 539, 29.22, 1, 1, 14, 412.51, 29.88, 1, 1, 14, 412.51, 21.73, 1, 1, 14, 74.24, 27.28, 1, 1, 3, -296.64, 281.2, 1, 1, 3, -316.06, 225.71, 1, 1, 3, -521.69, 190.74, 1, 1, 3, -610.03, 141.02, 1, 1, 3, -671.84, 87.13, 1, 1, 3, -652.23, -11.24, 1, 1, 3, -618.53, -71.36, 1, 1, 3, -478.77, -114.21, 1, 1, 3, -274.11, -116.26, 1, 1, 3, 1.38, -45.75, 1, 1, 3, 189.67, 148.78, 1, 1, 3, 215.75, 276.59, 1 ],
+					"color": "ce3a3aff"
+				}
+			},
+			"ground": {
+				"ground": { "x": 837.96, "y": -172, "width": 1024, "height": 353 }
+			},
+			"ground2": {
+				"ground": { "x": -179.89, "y": -172, "width": 1024, "height": 353 }
+			},
+			"ground3": {
+				"ground": { "x": -1213.48, "y": -172, "scaleX": 1.035, "width": 1024, "height": 353 }
+			},
+			"ground4": {
+				"ground": { "x": -2268.51, "y": -172, "scaleX": 1.04, "width": 1024, "height": 353 }
+			},
+			"ground5": {
+				"ground": { "x": -3306.54, "y": -172, "width": 1024, "height": 353 }
+			},
+			"ground6": {
+				"ground": { "x": -4322.71, "y": -172, "width": 1024, "height": 353 }
+			},
+			"ground7": {
+				"ground": { "x": -5340.65, "y": -172, "width": 1024, "height": 353 }
+			},
+			"guntower": {
+				"guntower": { "x": 77.22, "y": 122.59, "width": 730, "height": 289 }
+			},
+			"machinegun": {
+				"machinegun": { "x": 44.85, "y": -5.72, "rotation": -180, "width": 331, "height": 57 }
+			},
+			"machinegun-mount": {
+				"machinegun-mount": { "x": 47.42, "y": -1.54, "rotation": -90, "width": 72, "height": 96 }
+			},
+			"rock": {
+				"rock": { "x": 25.24, "y": 27.35, "width": 580, "height": 127 }
+			},
+			"smoke-glow": {
+				"smoke-glow": {
+					"type": "mesh",
+					"uvs": [ 1, 0.24906, 1, 0.51991, 1, 0.73165, 0.70776, 1, 0.49012, 1, 0.24373, 1, 0, 0.71158, 0, 0.50308, 0, 0.26235, 0.28107, 0, 0.47435, 0, 0.73345, 0, 0.48858, 0.51759 ],
+					"triangles": [ 12, 7, 8, 12, 10, 11, 12, 11, 0, 9, 10, 12, 12, 8, 9, 12, 0, 1, 6, 7, 12, 12, 1, 2, 5, 6, 12, 3, 4, 12, 5, 12, 4, 2, 3, 12 ],
+					"vertices": [ 49.99, 25.1, 50, -1.98, 50.01, -23.15, 20.79, -50, -0.98, -50, -25.62, -50.01, -50, -21.17, -50, -0.32, -50.01, 23.75, -21.9, 50, -2.58, 50, 23.33, 50.01, -1.14, -1.76 ],
+					"hull": 12,
+					"edges": [ 2, 24, 24, 14, 20, 24, 24, 8, 2, 0, 20, 22, 0, 22, 18, 20, 14, 16, 18, 16, 12, 14, 8, 10, 12, 10, 6, 8, 2, 4, 6, 4 ],
+					"width": 100,
+					"height": 100
+				}
+			},
+			"smoke-puff1-bg": {
+				"smoke-puff01-bg": {
+					"x": -0.5,
+					"y": -0.12,
+					"scaleX": 0.1106,
+					"scaleY": 0.1106,
+					"rotation": 88.58,
+					"width": 184,
+					"height": 123
+				}
+			},
+			"smoke-puff1-bg2": {
+				"smoke-puff01-bg": {
+					"x": -0.5,
+					"y": -0.12,
+					"scaleX": 0.1106,
+					"scaleY": 0.1106,
+					"rotation": 88.58,
+					"width": 184,
+					"height": 123
+				}
+			},
+			"smoke-puff1-bg3": {
+				"smoke-puff01-bg": {
+					"x": -0.5,
+					"y": -0.12,
+					"scaleX": 0.1106,
+					"scaleY": 0.1106,
+					"rotation": 88.58,
+					"width": 184,
+					"height": 123
+				}
+			},
+			"smoke-puff1-bg4": {
+				"smoke-puff01-bg": {
+					"x": -0.5,
+					"y": -0.12,
+					"scaleX": 0.1106,
+					"scaleY": 0.1106,
+					"rotation": 88.58,
+					"width": 184,
+					"height": 123
+				}
+			},
+			"smoke-puff1-bg5": {
+				"smoke-puff01-bg": {
+					"x": -0.5,
+					"y": -0.12,
+					"scaleX": 0.1106,
+					"scaleY": 0.1106,
+					"rotation": 88.58,
+					"width": 184,
+					"height": 123
+				}
+			},
+			"smoke-puff1-bg6": {
+				"smoke-puff01-bg": {
+					"x": -0.5,
+					"y": -0.12,
+					"scaleX": 0.1106,
+					"scaleY": 0.1106,
+					"rotation": 88.58,
+					"width": 184,
+					"height": 123
+				}
+			},
+			"smoke-puff1-bg7": {
+				"smoke-puff01-bg": {
+					"x": -0.5,
+					"y": -0.12,
+					"scaleX": 0.1106,
+					"scaleY": 0.1106,
+					"rotation": 88.58,
+					"width": 184,
+					"height": 123
+				}
+			},
+			"smoke-puff1-bg8": {
+				"smoke-puff01-bg": {
+					"x": -0.5,
+					"y": -0.12,
+					"scaleX": 0.1106,
+					"scaleY": 0.1106,
+					"rotation": 88.58,
+					"width": 184,
+					"height": 123
+				}
+			},
+			"smoke-puff1-bg9": {
+				"smoke-puff01-bg": {
+					"x": -0.5,
+					"y": -0.12,
+					"scaleX": 0.1106,
+					"scaleY": 0.1106,
+					"rotation": 88.58,
+					"width": 184,
+					"height": 123
+				}
+			},
+			"smoke-puff1-bg10": {
+				"smoke-puff01-bg": {
+					"x": -0.5,
+					"y": -0.12,
+					"scaleX": 0.1106,
+					"scaleY": 0.1106,
+					"rotation": 88.58,
+					"width": 184,
+					"height": 123
+				}
+			},
+			"smoke-puff1-bg11": {
+				"smoke-puff01-bg": {
+					"x": -0.5,
+					"y": -0.12,
+					"scaleX": 0.1106,
+					"scaleY": 0.1106,
+					"rotation": 88.58,
+					"width": 184,
+					"height": 123
+				}
+			},
+			"smoke-puff1-bg12": {
+				"smoke-puff01-bg": {
+					"x": -0.5,
+					"y": -0.12,
+					"scaleX": 0.1106,
+					"scaleY": 0.1106,
+					"rotation": 88.58,
+					"width": 184,
+					"height": 123
+				}
+			},
+			"smoke-puff1-bg13": {
+				"smoke-puff01-bg": {
+					"x": -0.5,
+					"y": -0.12,
+					"scaleX": 0.1106,
+					"scaleY": 0.1106,
+					"rotation": 88.58,
+					"width": 184,
+					"height": 123
+				}
+			},
+			"smoke-puff1-bg14": {
+				"smoke-puff01-bg": {
+					"x": -0.5,
+					"y": -0.12,
+					"scaleX": 0.1106,
+					"scaleY": 0.1106,
+					"rotation": 88.58,
+					"width": 184,
+					"height": 123
+				}
+			},
+			"smoke-puff1-bg15": {
+				"smoke-puff01-bg": {
+					"x": -0.5,
+					"y": -0.12,
+					"scaleX": 0.1106,
+					"scaleY": 0.1106,
+					"rotation": 88.58,
+					"width": 184,
+					"height": 123
+				}
+			},
+			"smoke-puff1-bg16": {
+				"smoke-puff01-bg": {
+					"x": -0.5,
+					"y": -0.12,
+					"scaleX": 0.1106,
+					"scaleY": 0.1106,
+					"rotation": 88.58,
+					"width": 184,
+					"height": 123
+				}
+			},
+			"smoke-puff1-bg17": {
+				"smoke-puff01-bg": {
+					"x": -0.5,
+					"y": -0.12,
+					"scaleX": 0.1106,
+					"scaleY": 0.1106,
+					"rotation": 88.58,
+					"width": 184,
+					"height": 123
+				}
+			},
+			"smoke-puff1-bg18": {
+				"smoke-puff01-bg": {
+					"x": -0.5,
+					"y": -0.12,
+					"scaleX": 0.1106,
+					"scaleY": 0.1106,
+					"rotation": 88.58,
+					"width": 184,
+					"height": 123
+				}
+			},
+			"smoke-puff1-bg20": {
+				"smoke-puff01-bg": {
+					"x": -0.5,
+					"y": -0.12,
+					"scaleX": 0.1106,
+					"scaleY": 0.1106,
+					"rotation": 88.58,
+					"width": 184,
+					"height": 123
+				}
+			},
+			"smoke-puff1-bg21": {
+				"smoke-puff01-bg": {
+					"x": -0.5,
+					"y": -0.12,
+					"scaleX": 0.1106,
+					"scaleY": 0.1106,
+					"rotation": 88.58,
+					"width": 184,
+					"height": 123
+				}
+			},
+			"smoke-puff1-bg22": {
+				"smoke-puff01-bg": {
+					"x": -0.5,
+					"y": -0.12,
+					"scaleX": 0.1106,
+					"scaleY": 0.1106,
+					"rotation": 88.58,
+					"width": 184,
+					"height": 123
+				}
+			},
+			"smoke-puff1-bg23": {
+				"smoke-puff01-bg": {
+					"x": -0.5,
+					"y": -0.12,
+					"scaleX": 0.1106,
+					"scaleY": 0.1106,
+					"rotation": 88.58,
+					"width": 184,
+					"height": 123
+				}
+			},
+			"smoke-puff1-bg24": {
+				"smoke-puff01-bg": {
+					"x": -0.5,
+					"y": -0.12,
+					"scaleX": 0.1106,
+					"scaleY": 0.1106,
+					"rotation": 88.58,
+					"width": 184,
+					"height": 123
+				}
+			},
+			"smoke-puff1-bg25": {
+				"smoke-puff01-bg": {
+					"x": -0.5,
+					"y": -0.12,
+					"scaleX": 0.1106,
+					"scaleY": 0.1106,
+					"rotation": 88.58,
+					"width": 184,
+					"height": 123
+				}
+			},
+			"smoke-puff1-bg26": {
+				"smoke-puff01-bg": {
+					"x": -0.5,
+					"y": -0.12,
+					"scaleX": 0.1106,
+					"scaleY": 0.1106,
+					"rotation": 88.58,
+					"width": 184,
+					"height": 123
+				}
+			},
+			"smoke-puff1-bg27": {
+				"smoke-puff01-bg": {
+					"x": -0.5,
+					"y": -0.12,
+					"scaleX": 0.1106,
+					"scaleY": 0.1106,
+					"rotation": 88.58,
+					"width": 184,
+					"height": 123
+				}
+			},
+			"smoke-puff1-fg": {
+				"smoke-puff01-fg": {
+					"x": -0.5,
+					"y": -0.12,
+					"scaleX": 0.1106,
+					"scaleY": 0.1106,
+					"rotation": 88.58,
+					"width": 175,
+					"height": 118
+				},
+				"smoke-puff02-fg": {
+					"x": -1.38,
+					"y": -0.12,
+					"scaleX": 0.1106,
+					"scaleY": 0.1106,
+					"rotation": 88.58,
+					"width": 184,
+					"height": 123
+				},
+				"smoke-puff03-fg": {
+					"x": -1.38,
+					"y": -0.12,
+					"scaleX": 0.1106,
+					"scaleY": 0.1106,
+					"rotation": 88.58,
+					"width": 184,
+					"height": 123
+				},
+				"smoke-puff04-fg": {
+					"x": -1.38,
+					"y": -0.12,
+					"scaleX": 0.1106,
+					"scaleY": 0.1106,
+					"rotation": 88.58,
+					"width": 155,
+					"height": 96
+				}
+			},
+			"smoke-puff1-fg2": {
+				"smoke-puff01-fg": {
+					"x": -1.01,
+					"y": -0.07,
+					"scaleX": 0.1106,
+					"scaleY": 0.1106,
+					"rotation": 88.58,
+					"width": 175,
+					"height": 118
+				},
+				"smoke-puff02-fg": {
+					"x": -1.82,
+					"y": -0.39,
+					"scaleX": 0.1106,
+					"scaleY": 0.1106,
+					"rotation": 88.58,
+					"width": 184,
+					"height": 123
+				},
+				"smoke-puff03-fg": {
+					"x": -1.82,
+					"y": -0.39,
+					"scaleX": 0.1106,
+					"scaleY": 0.1106,
+					"rotation": 88.58,
+					"width": 184,
+					"height": 123
+				},
+				"smoke-puff04-fg": {
+					"x": -1.82,
+					"y": -0.39,
+					"scaleX": 0.1106,
+					"scaleY": 0.1106,
+					"rotation": 88.58,
+					"width": 155,
+					"height": 96
+				}
+			},
+			"smoke-puff1-fg3": {
+				"smoke-puff01-fg": {
+					"x": -0.5,
+					"y": -0.12,
+					"scaleX": 0.1106,
+					"scaleY": 0.1106,
+					"rotation": 88.58,
+					"width": 175,
+					"height": 118
+				},
+				"smoke-puff02-fg": {
+					"x": -1.02,
+					"y": -0.25,
+					"scaleX": 0.1106,
+					"scaleY": 0.1106,
+					"rotation": 88.58,
+					"width": 184,
+					"height": 123
+				},
+				"smoke-puff03-fg": {
+					"x": -0.5,
+					"y": -0.12,
+					"scaleX": 0.1106,
+					"scaleY": 0.1145,
+					"rotation": 88.58,
+					"width": 184,
+					"height": 123
+				},
+				"smoke-puff04-fg": {
+					"x": -1.03,
+					"y": -0.43,
+					"scaleX": 0.1106,
+					"scaleY": 0.1106,
+					"rotation": 88.58,
+					"width": 155,
+					"height": 96
+				}
+			},
+			"smoke-puff1-fg4": {
+				"smoke-puff01-fg": {
+					"x": -0.5,
+					"y": -0.12,
+					"scaleX": 0.1106,
+					"scaleY": 0.1106,
+					"rotation": 88.58,
+					"width": 175,
+					"height": 118
+				},
+				"smoke-puff02-fg": {
+					"x": -1.63,
+					"y": -0.09,
+					"scaleX": 0.1106,
+					"scaleY": 0.1106,
+					"rotation": 88.58,
+					"width": 184,
+					"height": 123
+				},
+				"smoke-puff03-fg": {
+					"x": -1.63,
+					"y": -0.09,
+					"scaleX": 0.1106,
+					"scaleY": 0.1106,
+					"rotation": 88.58,
+					"width": 184,
+					"height": 123
+				},
+				"smoke-puff04-fg": {
+					"x": -1.63,
+					"y": -0.09,
+					"scaleX": 0.1106,
+					"scaleY": 0.1106,
+					"rotation": 88.58,
+					"width": 155,
+					"height": 96
+				}
+			},
+			"smoke-puff1-fg5": {
+				"smoke-puff01-fg": {
+					"x": -1.21,
+					"y": -0.08,
+					"scaleX": 0.1106,
+					"scaleY": 0.1106,
+					"rotation": 88.58,
+					"width": 175,
+					"height": 118
+				},
+				"smoke-puff02-fg": {
+					"x": -1.89,
+					"y": -0.04,
+					"scaleX": 0.1106,
+					"scaleY": 0.1106,
+					"rotation": 88.58,
+					"width": 184,
+					"height": 123
+				},
+				"smoke-puff03-fg": {
+					"x": -1.89,
+					"y": -0.04,
+					"scaleX": 0.1106,
+					"scaleY": 0.1106,
+					"rotation": 88.58,
+					"width": 184,
+					"height": 123
+				},
+				"smoke-puff04-fg": {
+					"x": -1.89,
+					"y": -0.04,
+					"scaleX": 0.1106,
+					"scaleY": 0.1106,
+					"rotation": 88.58,
+					"width": 155,
+					"height": 96
+				}
+			},
+			"smoke-puff1-fg6": {
+				"smoke-puff01-fg": {
+					"x": -0.5,
+					"y": -0.12,
+					"scaleX": 0.1106,
+					"scaleY": 0.1106,
+					"rotation": 88.58,
+					"width": 175,
+					"height": 118
+				},
+				"smoke-puff02-fg": {
+					"x": -1.48,
+					"y": -0.07,
+					"scaleX": 0.1106,
+					"scaleY": 0.1106,
+					"rotation": 88.58,
+					"width": 184,
+					"height": 123
+				},
+				"smoke-puff03-fg": {
+					"x": -1.48,
+					"y": -0.07,
+					"scaleX": 0.1106,
+					"scaleY": 0.1106,
+					"rotation": 88.58,
+					"width": 184,
+					"height": 123
+				},
+				"smoke-puff04-fg": {
+					"x": -1.48,
+					"y": -0.07,
+					"scaleX": 0.1106,
+					"scaleY": 0.1106,
+					"rotation": 88.58,
+					"width": 155,
+					"height": 96
+				}
+			},
+			"smoke-puff1-fg7": {
+				"smoke-puff01-fg": {
+					"x": -0.5,
+					"y": -0.12,
+					"scaleX": 0.1106,
+					"scaleY": 0.1106,
+					"rotation": 88.58,
+					"width": 175,
+					"height": 118
+				},
+				"smoke-puff02-fg": {
+					"x": -0.7,
+					"y": -0.36,
+					"scaleX": 0.1216,
+					"scaleY": 0.1214,
+					"rotation": 88.58,
+					"width": 184,
+					"height": 123
+				},
+				"smoke-puff03-fg": {
+					"x": -0.56,
+					"y": -0.15,
+					"scaleX": 0.1224,
+					"scaleY": 0.1224,
+					"rotation": 88.58,
+					"width": 184,
+					"height": 123
+				},
+				"smoke-puff04-fg": {
+					"x": -0.56,
+					"y": -0.15,
+					"scaleX": 0.1224,
+					"scaleY": 0.1224,
+					"rotation": 88.58,
+					"width": 155,
+					"height": 96
+				}
+			},
+			"smoke-puff1-fg8": {
+				"smoke-puff01-fg": {
+					"x": -0.5,
+					"y": -0.12,
+					"scaleX": 0.1106,
+					"scaleY": 0.1106,
+					"rotation": 88.58,
+					"width": 175,
+					"height": 118
+				},
+				"smoke-puff02-fg": {
+					"x": -0.65,
+					"y": 0.01,
+					"scaleX": 0.1226,
+					"scaleY": 0.1226,
+					"rotation": 88.58,
+					"width": 184,
+					"height": 123
+				},
+				"smoke-puff03-fg": {
+					"x": -0.75,
+					"y": -0.15,
+					"scaleX": 0.1211,
+					"scaleY": 0.1211,
+					"rotation": 88.58,
+					"width": 184,
+					"height": 123
+				},
+				"smoke-puff04-fg": {
+					"x": -0.75,
+					"y": -0.15,
+					"scaleX": 0.1211,
+					"scaleY": 0.1211,
+					"rotation": 88.58,
+					"width": 155,
+					"height": 96
+				}
+			},
+			"smoke-puff1-fg9": {
+				"smoke-puff01-fg": {
+					"x": -0.5,
+					"y": -0.12,
+					"scaleX": 0.1106,
+					"scaleY": 0.1106,
+					"rotation": 88.58,
+					"width": 175,
+					"height": 118
+				},
+				"smoke-puff02-fg": {
+					"x": -1.99,
+					"y": -0.09,
+					"scaleX": 0.1106,
+					"scaleY": 0.1106,
+					"rotation": 88.58,
+					"width": 184,
+					"height": 123
+				},
+				"smoke-puff03-fg": {
+					"x": -1.99,
+					"y": -0.09,
+					"scaleX": 0.1106,
+					"scaleY": 0.1106,
+					"rotation": 88.58,
+					"width": 184,
+					"height": 123
+				},
+				"smoke-puff04-fg": {
+					"x": -0.95,
+					"y": -0.48,
+					"scaleX": 0.1106,
+					"scaleY": 0.1106,
+					"rotation": 88.58,
+					"width": 155,
+					"height": 96
+				}
+			},
+			"smoke-puff1-fg10": {
+				"smoke-puff01-fg": {
+					"x": -0.5,
+					"y": -0.12,
+					"scaleX": 0.1106,
+					"scaleY": 0.1106,
+					"rotation": 88.58,
+					"width": 175,
+					"height": 118
+				},
+				"smoke-puff02-fg": {
+					"x": -1.27,
+					"y": -0.37,
+					"scaleX": 0.1106,
+					"scaleY": 0.1106,
+					"rotation": 88.58,
+					"width": 184,
+					"height": 123
+				},
+				"smoke-puff03-fg": {
+					"x": -1.27,
+					"y": -0.37,
+					"scaleX": 0.1106,
+					"scaleY": 0.1106,
+					"rotation": 88.58,
+					"width": 184,
+					"height": 123
+				},
+				"smoke-puff04-fg": {
+					"x": -1.27,
+					"y": -0.37,
+					"scaleX": 0.1106,
+					"scaleY": 0.1106,
+					"rotation": 88.58,
+					"width": 155,
+					"height": 96
+				}
+			},
+			"smoke-puff1-fg11": {
+				"smoke-puff04-fg": {
+					"x": -1.27,
+					"y": -0.37,
+					"scaleX": 0.1106,
+					"scaleY": 0.1106,
+					"rotation": 88.58,
+					"width": 155,
+					"height": 96
+				}
+			},
+			"smoke-puff1-fg12": {
+				"smoke-puff04-fg": {
+					"x": -1.27,
+					"y": -0.37,
+					"scaleX": 0.1106,
+					"scaleY": 0.1106,
+					"rotation": 88.58,
+					"width": 155,
+					"height": 96
+				}
+			},
+			"smoke-puff1-fg13": {
+				"smoke-puff01-fg": {
+					"x": -0.5,
+					"y": -0.12,
+					"scaleX": 0.1106,
+					"scaleY": 0.1106,
+					"rotation": 88.58,
+					"width": 175,
+					"height": 118
+				},
+				"smoke-puff02-fg": {
+					"x": -1.38,
+					"y": -0.12,
+					"scaleX": 0.1106,
+					"scaleY": 0.1106,
+					"rotation": 88.58,
+					"width": 184,
+					"height": 123
+				},
+				"smoke-puff03-fg": {
+					"x": -1.38,
+					"y": -0.12,
+					"scaleX": 0.1106,
+					"scaleY": 0.1106,
+					"rotation": 88.58,
+					"width": 184,
+					"height": 123
+				},
+				"smoke-puff04-fg": {
+					"x": -1.38,
+					"y": -0.12,
+					"scaleX": 0.1106,
+					"scaleY": 0.1106,
+					"rotation": 88.58,
+					"width": 155,
+					"height": 96
+				}
+			},
+			"smoke-puff1-fg14": {
+				"smoke-puff01-fg": {
+					"x": -0.5,
+					"y": -0.12,
+					"scaleX": 0.1106,
+					"scaleY": 0.1106,
+					"rotation": 88.58,
+					"width": 175,
+					"height": 118
+				},
+				"smoke-puff02-fg": {
+					"x": -1.38,
+					"y": -0.12,
+					"scaleX": 0.1106,
+					"scaleY": 0.1106,
+					"rotation": 88.58,
+					"width": 184,
+					"height": 123
+				},
+				"smoke-puff03-fg": {
+					"x": -1.38,
+					"y": -0.12,
+					"scaleX": 0.1106,
+					"scaleY": 0.1106,
+					"rotation": 88.58,
+					"width": 184,
+					"height": 123
+				},
+				"smoke-puff04-fg": {
+					"x": -1.38,
+					"y": -0.12,
+					"scaleX": 0.1106,
+					"scaleY": 0.1106,
+					"rotation": 88.58,
+					"width": 155,
+					"height": 96
+				}
+			},
+			"smoke-puff1-fg15": {
+				"smoke-puff01-fg": {
+					"x": -0.5,
+					"y": -0.12,
+					"scaleX": 0.1106,
+					"scaleY": 0.1106,
+					"rotation": 88.58,
+					"width": 175,
+					"height": 118
+				},
+				"smoke-puff02-fg": {
+					"x": -1.38,
+					"y": -0.12,
+					"scaleX": 0.1106,
+					"scaleY": 0.1106,
+					"rotation": 88.58,
+					"width": 184,
+					"height": 123
+				},
+				"smoke-puff03-fg": {
+					"x": -1.38,
+					"y": -0.12,
+					"scaleX": 0.1106,
+					"scaleY": 0.1106,
+					"rotation": 88.58,
+					"width": 184,
+					"height": 123
+				},
+				"smoke-puff04-fg": {
+					"x": -1.38,
+					"y": -0.12,
+					"scaleX": 0.1106,
+					"scaleY": 0.1106,
+					"rotation": 88.58,
+					"width": 155,
+					"height": 96
+				}
+			},
+			"smoke-puff1-fg16": {
+				"smoke-puff01-fg": {
+					"x": -0.5,
+					"y": -0.12,
+					"scaleX": 0.1106,
+					"scaleY": 0.1106,
+					"rotation": 88.58,
+					"width": 175,
+					"height": 118
+				},
+				"smoke-puff02-fg": {
+					"x": -1.38,
+					"y": -0.12,
+					"scaleX": 0.1106,
+					"scaleY": 0.1106,
+					"rotation": 88.58,
+					"width": 184,
+					"height": 123
+				},
+				"smoke-puff03-fg": {
+					"x": -1.38,
+					"y": -0.12,
+					"scaleX": 0.1106,
+					"scaleY": 0.1106,
+					"rotation": 88.58,
+					"width": 184,
+					"height": 123
+				},
+				"smoke-puff04-fg": {
+					"x": -1.38,
+					"y": -0.12,
+					"scaleX": 0.1106,
+					"scaleY": 0.1106,
+					"rotation": 88.58,
+					"width": 155,
+					"height": 96
+				}
+			},
+			"smoke-puff1-fg17": {
+				"smoke-puff01-fg": {
+					"x": -0.5,
+					"y": -0.12,
+					"scaleX": 0.1106,
+					"scaleY": 0.1106,
+					"rotation": 88.58,
+					"width": 175,
+					"height": 118
+				},
+				"smoke-puff02-fg": {
+					"x": -1.38,
+					"y": -0.12,
+					"scaleX": 0.1106,
+					"scaleY": 0.1106,
+					"rotation": 88.58,
+					"width": 184,
+					"height": 123
+				},
+				"smoke-puff03-fg": {
+					"x": -1.38,
+					"y": -0.12,
+					"scaleX": 0.1106,
+					"scaleY": 0.1106,
+					"rotation": 88.58,
+					"width": 184,
+					"height": 123
+				},
+				"smoke-puff04-fg": {
+					"x": -1.38,
+					"y": -0.12,
+					"scaleX": 0.1106,
+					"scaleY": 0.1106,
+					"rotation": 88.58,
+					"width": 155,
+					"height": 96
+				}
+			},
+			"smoke-puff1-fg18": {
+				"smoke-puff01-fg": {
+					"x": -0.5,
+					"y": -0.12,
+					"scaleX": 0.1106,
+					"scaleY": 0.1106,
+					"rotation": 88.58,
+					"width": 175,
+					"height": 118
+				},
+				"smoke-puff02-fg": {
+					"x": -1.38,
+					"y": -0.12,
+					"scaleX": 0.1106,
+					"scaleY": 0.1106,
+					"rotation": 88.58,
+					"width": 184,
+					"height": 123
+				},
+				"smoke-puff03-fg": {
+					"x": -1.38,
+					"y": -0.12,
+					"scaleX": 0.1106,
+					"scaleY": 0.1106,
+					"rotation": 88.58,
+					"width": 184,
+					"height": 123
+				},
+				"smoke-puff04-fg": {
+					"x": -1.38,
+					"y": -0.12,
+					"scaleX": 0.1106,
+					"scaleY": 0.1106,
+					"rotation": 88.58,
+					"width": 155,
+					"height": 96
+				}
+			},
+			"smoke-puff1-fg20": {
+				"smoke-puff01-fg": {
+					"x": -0.5,
+					"y": -0.12,
+					"scaleX": 0.1106,
+					"scaleY": 0.1106,
+					"rotation": 88.58,
+					"width": 175,
+					"height": 118
+				},
+				"smoke-puff02-fg": {
+					"x": -1.38,
+					"y": -0.12,
+					"scaleX": 0.1106,
+					"scaleY": 0.1106,
+					"rotation": 88.58,
+					"width": 184,
+					"height": 123
+				},
+				"smoke-puff03-fg": {
+					"x": -1.38,
+					"y": -0.12,
+					"scaleX": 0.1106,
+					"scaleY": 0.1106,
+					"rotation": 88.58,
+					"width": 184,
+					"height": 123
+				},
+				"smoke-puff04-fg": {
+					"x": -1.38,
+					"y": -0.12,
+					"scaleX": 0.1106,
+					"scaleY": 0.1106,
+					"rotation": 88.58,
+					"width": 155,
+					"height": 96
+				}
+			},
+			"smoke-puff1-fg21": {
+				"smoke-puff01-fg": {
+					"x": -0.5,
+					"y": -0.12,
+					"scaleX": 0.1106,
+					"scaleY": 0.1106,
+					"rotation": 88.58,
+					"width": 175,
+					"height": 118
+				},
+				"smoke-puff02-fg": {
+					"x": -1.38,
+					"y": -0.12,
+					"scaleX": 0.1106,
+					"scaleY": 0.1106,
+					"rotation": 88.58,
+					"width": 184,
+					"height": 123
+				},
+				"smoke-puff03-fg": {
+					"x": -1.38,
+					"y": -0.12,
+					"scaleX": 0.1106,
+					"scaleY": 0.1106,
+					"rotation": 88.58,
+					"width": 184,
+					"height": 123
+				},
+				"smoke-puff04-fg": {
+					"x": -1.38,
+					"y": -0.12,
+					"scaleX": 0.1106,
+					"scaleY": 0.1106,
+					"rotation": 88.58,
+					"width": 155,
+					"height": 96
+				}
+			},
+			"smoke-puff1-fg22": {
+				"smoke-puff01-fg": {
+					"x": -0.5,
+					"y": -0.12,
+					"scaleX": 0.1106,
+					"scaleY": 0.1106,
+					"rotation": 88.58,
+					"width": 175,
+					"height": 118
+				},
+				"smoke-puff02-fg": {
+					"x": -1.38,
+					"y": -0.12,
+					"scaleX": 0.1106,
+					"scaleY": 0.1106,
+					"rotation": 88.58,
+					"width": 184,
+					"height": 123
+				},
+				"smoke-puff03-fg": {
+					"x": -1.38,
+					"y": -0.12,
+					"scaleX": 0.1106,
+					"scaleY": 0.1106,
+					"rotation": 88.58,
+					"width": 184,
+					"height": 123
+				},
+				"smoke-puff04-fg": {
+					"x": -1.38,
+					"y": -0.12,
+					"scaleX": 0.1106,
+					"scaleY": 0.1106,
+					"rotation": 88.58,
+					"width": 155,
+					"height": 96
+				}
+			},
+			"smoke-puff1-fg23": {
+				"smoke-puff01-fg": {
+					"x": -0.5,
+					"y": -0.12,
+					"scaleX": 0.1106,
+					"scaleY": 0.1106,
+					"rotation": 88.58,
+					"width": 175,
+					"height": 118
+				},
+				"smoke-puff02-fg": {
+					"x": -1.38,
+					"y": -0.12,
+					"scaleX": 0.1106,
+					"scaleY": 0.1106,
+					"rotation": 88.58,
+					"width": 184,
+					"height": 123
+				},
+				"smoke-puff03-fg": {
+					"x": -1.38,
+					"y": -0.12,
+					"scaleX": 0.1106,
+					"scaleY": 0.1106,
+					"rotation": 88.58,
+					"width": 184,
+					"height": 123
+				},
+				"smoke-puff04-fg": {
+					"x": -1.38,
+					"y": -0.12,
+					"scaleX": 0.1106,
+					"scaleY": 0.1106,
+					"rotation": 88.58,
+					"width": 155,
+					"height": 96
+				}
+			},
+			"smoke-puff1-fg24": {
+				"smoke-puff01-fg": {
+					"x": -0.5,
+					"y": -0.12,
+					"scaleX": 0.1106,
+					"scaleY": 0.1106,
+					"rotation": 88.58,
+					"width": 175,
+					"height": 118
+				},
+				"smoke-puff02-fg": {
+					"x": -1.38,
+					"y": -0.12,
+					"scaleX": 0.1106,
+					"scaleY": 0.1106,
+					"rotation": 88.58,
+					"width": 184,
+					"height": 123
+				},
+				"smoke-puff03-fg": {
+					"x": -1.38,
+					"y": -0.12,
+					"scaleX": 0.1106,
+					"scaleY": 0.1106,
+					"rotation": 88.58,
+					"width": 184,
+					"height": 123
+				},
+				"smoke-puff04-fg": {
+					"x": -1.38,
+					"y": -0.12,
+					"scaleX": 0.1106,
+					"scaleY": 0.1106,
+					"rotation": 88.58,
+					"width": 155,
+					"height": 96
+				}
+			},
+			"smoke-puff1-fg25": {
+				"smoke-puff01-fg": {
+					"x": -0.5,
+					"y": -0.12,
+					"scaleX": 0.1106,
+					"scaleY": 0.1106,
+					"rotation": 88.58,
+					"width": 175,
+					"height": 118
+				},
+				"smoke-puff02-fg": {
+					"x": -1.38,
+					"y": -0.12,
+					"scaleX": 0.1106,
+					"scaleY": 0.1106,
+					"rotation": 88.58,
+					"width": 184,
+					"height": 123
+				},
+				"smoke-puff03-fg": {
+					"x": -1.38,
+					"y": -0.12,
+					"scaleX": 0.1106,
+					"scaleY": 0.1106,
+					"rotation": 88.58,
+					"width": 184,
+					"height": 123
+				},
+				"smoke-puff04-fg": {
+					"x": -1.38,
+					"y": -0.12,
+					"scaleX": 0.1106,
+					"scaleY": 0.1106,
+					"rotation": 88.58,
+					"width": 155,
+					"height": 96
+				}
+			},
+			"smoke-puff1-fg26": {
+				"smoke-puff01-fg": {
+					"x": -0.5,
+					"y": -0.12,
+					"scaleX": 0.1106,
+					"scaleY": 0.1106,
+					"rotation": 88.58,
+					"width": 175,
+					"height": 118
+				},
+				"smoke-puff02-fg": {
+					"x": -1.38,
+					"y": -0.12,
+					"scaleX": 0.1106,
+					"scaleY": 0.1106,
+					"rotation": 88.58,
+					"width": 184,
+					"height": 123
+				},
+				"smoke-puff03-fg": {
+					"x": -1.38,
+					"y": -0.12,
+					"scaleX": 0.1106,
+					"scaleY": 0.1106,
+					"rotation": 88.58,
+					"width": 184,
+					"height": 123
+				},
+				"smoke-puff04-fg": {
+					"x": -1.38,
+					"y": -0.12,
+					"scaleX": 0.1106,
+					"scaleY": 0.1106,
+					"rotation": 88.58,
+					"width": 155,
+					"height": 96
+				}
+			},
+			"smoke-puff1-fg27": {
+				"smoke-puff01-fg": {
+					"x": -0.5,
+					"y": -0.12,
+					"scaleX": 0.1106,
+					"scaleY": 0.1106,
+					"rotation": 88.58,
+					"width": 175,
+					"height": 118
+				},
+				"smoke-puff02-fg": {
+					"x": -1.38,
+					"y": -0.12,
+					"scaleX": 0.1106,
+					"scaleY": 0.1106,
+					"rotation": 88.58,
+					"width": 184,
+					"height": 123
+				},
+				"smoke-puff03-fg": {
+					"x": -1.38,
+					"y": -0.12,
+					"scaleX": 0.1106,
+					"scaleY": 0.1106,
+					"rotation": 88.58,
+					"width": 184,
+					"height": 123
+				},
+				"smoke-puff04-fg": {
+					"x": -1.38,
+					"y": -0.12,
+					"scaleX": 0.1106,
+					"scaleY": 0.1106,
+					"rotation": 88.58,
+					"width": 155,
+					"height": 96
+				}
+			},
+			"tank-body-shadow": {
+				"tank-bottom-shadow": { "x": -11.44, "y": -42.89, "width": 1291, "height": 341 }
+			},
+			"tank-glow": {
+				"smoke-glow": {
+					"type": "mesh",
+					"uvs": [ 1, 1, 0, 1, 1, 0 ],
+					"triangles": [ 1, 2, 0 ],
+					"vertices": [ 469.64, -738.08, -1660.32, -738.08, 469.64, 1391.88 ],
+					"hull": 3,
+					"edges": [ 0, 2, 0, 4, 2, 4 ],
+					"width": 100,
+					"height": 100
+				}
+			},
+			"tank-top": {
+				"tank-top": { "x": 6.8, "y": 168.71, "width": 1407, "height": 222 }
+			},
+			"tread": {
+				"tread": { "x": 45.47, "y": -8.28, "rotation": -180, "width": 96, "height": 30 }
+			},
+			"tread-inside1": {
+				"tread-inside": { "x": 60.1, "y": 12.56, "rotation": -180, "width": 25, "height": 28 }
+			},
+			"tread-inside2": {
+				"tread-inside": { "x": 20.1, "y": 12.56, "rotation": -180, "width": 25, "height": 28 }
+			},
+			"tread-inside3": {
+				"tread-inside": { "x": 60.1, "y": 12.56, "rotation": -180, "width": 25, "height": 28 }
+			},
+			"tread-inside4": {
+				"tread-inside": { "x": 20.1, "y": 12.56, "rotation": -180, "width": 25, "height": 28 }
+			},
+			"tread-inside5": {
+				"tread-inside": { "x": 60.1, "y": 12.56, "rotation": -180, "width": 25, "height": 28 }
+			},
+			"tread-inside6": {
+				"tread-inside": { "x": 20.1, "y": 12.56, "rotation": -180, "width": 25, "height": 28 }
+			},
+			"tread-inside7": {
+				"tread-inside": { "x": 60.1, "y": 12.56, "rotation": -180, "width": 25, "height": 28 }
+			},
+			"tread-inside8": {
+				"tread-inside": { "x": 20.1, "y": 12.56, "rotation": -180, "width": 25, "height": 28 }
+			},
+			"tread-inside9": {
+				"tread-inside": { "x": 60.1, "y": 12.56, "rotation": -180, "width": 25, "height": 28 }
+			},
+			"tread-inside10": {
+				"tread-inside": { "x": 20.1, "y": 12.56, "rotation": -180, "width": 25, "height": 28 }
+			},
+			"tread-inside11": {
+				"tread-inside": { "x": 60.1, "y": 12.56, "rotation": -180, "width": 25, "height": 28 }
+			},
+			"tread-inside12": {
+				"tread-inside": { "x": 20.1, "y": 12.56, "rotation": -180, "width": 25, "height": 28 }
+			},
+			"tread-inside13": {
+				"tread-inside": { "x": 60.1, "y": 12.56, "rotation": -180, "width": 25, "height": 28 }
+			},
+			"tread-inside14": {
+				"tread-inside": { "x": 20.1, "y": 12.56, "rotation": -180, "width": 25, "height": 28 }
+			},
+			"tread-inside15": {
+				"tread-inside": { "x": 60.1, "y": 12.56, "rotation": -180, "width": 25, "height": 28 }
+			},
+			"tread-inside16": {
+				"tread-inside": { "x": 20.1, "y": 12.56, "rotation": -180, "width": 25, "height": 28 }
+			},
+			"tread-inside17": {
+				"tread-inside": { "x": 60.1, "y": 12.56, "rotation": -180, "width": 25, "height": 28 }
+			},
+			"tread-inside18": {
+				"tread-inside": { "x": 20.1, "y": 12.56, "rotation": -180, "width": 25, "height": 28 }
+			},
+			"tread-inside19": {
+				"tread-inside": { "x": 60.1, "y": 12.56, "rotation": -180, "width": 25, "height": 28 }
+			},
+			"tread-inside20": {
+				"tread-inside": { "x": 20.1, "y": 12.56, "rotation": -180, "width": 25, "height": 28 }
+			},
+			"tread-inside21": {
+				"tread-inside": { "x": 60.1, "y": 12.56, "rotation": -180, "width": 25, "height": 28 }
+			},
+			"tread-inside22": {
+				"tread-inside": { "x": 20.1, "y": 12.56, "rotation": -180, "width": 25, "height": 28 }
+			},
+			"tread-inside23": {
+				"tread-inside": { "x": 60.1, "y": 12.56, "rotation": -180, "width": 25, "height": 28 }
+			},
+			"tread-inside24": {
+				"tread-inside": { "x": 20.1, "y": 12.56, "rotation": -180, "width": 25, "height": 28 }
+			},
+			"tread-inside25": {
+				"tread-inside": { "x": 60.1, "y": 12.56, "rotation": -180, "width": 25, "height": 28 }
+			},
+			"tread-inside26": {
+				"tread-inside": { "x": 20.1, "y": 12.56, "rotation": -180, "width": 25, "height": 28 }
+			},
+			"tread-inside27": {
+				"tread-inside": { "x": 60.1, "y": 12.56, "rotation": -180, "width": 25, "height": 28 }
+			},
+			"tread-inside28": {
+				"tread-inside": { "x": 20.1, "y": 12.56, "rotation": -180, "width": 25, "height": 28 }
+			},
+			"tread-inside29": {
+				"tread-inside": { "x": 60.1, "y": 12.56, "rotation": -180, "width": 25, "height": 28 }
+			},
+			"tread-inside30": {
+				"tread-inside": { "x": 20.1, "y": 12.56, "rotation": -180, "width": 25, "height": 28 }
+			},
+			"tread-inside31": {
+				"tread-inside": { "x": 60.1, "y": 12.56, "rotation": -180, "width": 25, "height": 28 }
+			},
+			"tread-inside32": {
+				"tread-inside": { "x": 20.1, "y": 12.56, "rotation": -180, "width": 25, "height": 28 }
+			},
+			"tread-inside33": {
+				"tread-inside": { "x": 60.1, "y": 12.56, "rotation": -180, "width": 25, "height": 28 }
+			},
+			"tread-inside34": {
+				"tread-inside": { "x": 20.1, "y": 12.56, "rotation": -180, "width": 25, "height": 28 }
+			},
+			"tread-inside35": {
+				"tread-inside": { "x": 60.1, "y": 12.56, "rotation": -180, "width": 25, "height": 28 }
+			},
+			"tread-inside36": {
+				"tread-inside": { "x": 20.1, "y": 12.56, "rotation": -180, "width": 25, "height": 28 }
+			},
+			"tread-inside37": {
+				"tread-inside": { "x": 60.1, "y": 12.56, "rotation": -180, "width": 25, "height": 28 }
+			},
+			"tread-inside38": {
+				"tread-inside": { "x": 20.1, "y": 12.56, "rotation": -180, "width": 25, "height": 28 }
+			},
+			"tread-inside39": {
+				"tread-inside": { "x": 60.1, "y": 12.56, "rotation": -180, "width": 25, "height": 28 }
+			},
+			"tread-inside40": {
+				"tread-inside": { "x": 20.1, "y": 12.56, "rotation": -180, "width": 25, "height": 28 }
+			},
+			"tread-inside41": {
+				"tread-inside": { "x": 60.1, "y": 12.56, "rotation": -180, "width": 25, "height": 28 }
+			},
+			"tread-inside42": {
+				"tread-inside": { "x": 20.1, "y": 12.56, "rotation": -180, "width": 25, "height": 28 }
+			},
+			"tread-inside43": {
+				"tread-inside": { "x": 60.1, "y": 12.56, "rotation": -180, "width": 25, "height": 28 }
+			},
+			"tread-inside44": {
+				"tread-inside": { "x": 20.1, "y": 12.56, "rotation": -180, "width": 25, "height": 28 }
+			},
+			"tread-inside45": {
+				"tread-inside": { "x": 60.1, "y": 12.56, "rotation": -180, "width": 25, "height": 28 }
+			},
+			"tread-inside46": {
+				"tread-inside": { "x": 20.1, "y": 12.56, "rotation": -180, "width": 25, "height": 28 }
+			},
+			"tread-inside47": {
+				"tread-inside": { "x": 60.1, "y": 12.56, "rotation": -180, "width": 25, "height": 28 }
+			},
+			"tread-inside48": {
+				"tread-inside": { "x": 20.1, "y": 12.56, "rotation": -180, "width": 25, "height": 28 }
+			},
+			"tread-inside49": {
+				"tread-inside": { "x": 60.1, "y": 12.56, "rotation": -180, "width": 25, "height": 28 }
+			},
+			"tread-inside50": {
+				"tread-inside": { "x": 20.1, "y": 12.56, "rotation": -180, "width": 25, "height": 28 }
+			},
+			"tread-inside51": {
+				"tread-inside": { "x": 60.1, "y": 12.56, "rotation": -180, "width": 25, "height": 28 }
+			},
+			"tread-inside52": {
+				"tread-inside": { "x": 20.1, "y": 12.56, "rotation": -180, "width": 25, "height": 28 }
+			},
+			"tread-inside53": {
+				"tread-inside": { "x": 60.1, "y": 12.56, "rotation": -180, "width": 25, "height": 28 }
+			},
+			"tread-inside54": {
+				"tread-inside": { "x": 20.1, "y": 12.56, "rotation": -180, "width": 25, "height": 28 }
+			},
+			"tread-inside55": {
+				"tread-inside": { "x": 60.1, "y": 12.56, "rotation": -180, "width": 25, "height": 28 }
+			},
+			"tread-inside56": {
+				"tread-inside": { "x": 20.1, "y": 12.56, "rotation": -180, "width": 25, "height": 28 }
+			},
+			"tread-inside57": {
+				"tread-inside": { "x": 60.1, "y": 12.56, "rotation": -180, "width": 25, "height": 28 }
+			},
+			"tread-inside58": {
+				"tread-inside": { "x": 20.1, "y": 12.56, "rotation": -180, "width": 25, "height": 28 }
+			},
+			"tread-inside59": {
+				"tread-inside": { "x": 60.1, "y": 12.56, "rotation": -180, "width": 25, "height": 28 }
+			},
+			"tread-inside60": {
+				"tread-inside": { "x": 20.1, "y": 12.56, "rotation": -180, "width": 25, "height": 28 }
+			},
+			"tread-inside61": {
+				"tread-inside": { "x": 60.1, "y": 12.56, "rotation": -180, "width": 25, "height": 28 }
+			},
+			"tread-inside62": {
+				"tread-inside": { "x": 20.1, "y": 12.56, "rotation": -180, "width": 25, "height": 28 }
+			},
+			"tread-inside63": {
+				"tread-inside": { "x": 60.1, "y": 12.56, "rotation": -180, "width": 25, "height": 28 }
+			},
+			"tread-inside64": {
+				"tread-inside": { "x": 20.1, "y": 12.56, "rotation": -180, "width": 25, "height": 28 }
+			},
+			"tread-inside65": {
+				"tread-inside": { "x": 60.1, "y": 12.56, "rotation": -180, "width": 25, "height": 28 }
+			},
+			"tread-inside66": {
+				"tread-inside": { "x": 20.1, "y": 12.56, "rotation": -180, "width": 25, "height": 28 }
+			},
+			"tread-inside67": {
+				"tread-inside": { "x": 60.1, "y": 12.56, "rotation": -180, "width": 25, "height": 28 }
+			},
+			"tread-inside68": {
+				"tread-inside": { "x": 20.1, "y": 12.56, "rotation": -180, "width": 25, "height": 28 }
+			},
+			"tread-inside69": {
+				"tread-inside": { "x": 60.1, "y": 12.56, "rotation": -180, "width": 25, "height": 28 }
+			},
+			"tread-inside70": {
+				"tread-inside": { "x": 20.1, "y": 12.56, "rotation": -180, "width": 25, "height": 28 }
+			},
+			"tread-inside71": {
+				"tread-inside": { "x": 60.1, "y": 12.56, "rotation": -180, "width": 25, "height": 28 }
+			},
+			"tread-inside72": {
+				"tread-inside": { "x": 20.1, "y": 12.56, "rotation": -180, "width": 25, "height": 28 }
+			},
+			"tread2": {
+				"tread": { "x": 45.47, "y": -8.28, "rotation": -180, "width": 96, "height": 30 }
+			},
+			"tread3": {
+				"tread": { "x": 45.47, "y": -8.28, "rotation": -180, "width": 96, "height": 30 }
+			},
+			"tread4": {
+				"tread": { "x": 45.47, "y": -8.28, "rotation": -180, "width": 96, "height": 30 }
+			},
+			"tread5": {
+				"tread": { "x": 45.47, "y": -8.28, "rotation": -180, "width": 96, "height": 30 }
+			},
+			"tread6": {
+				"tread": { "x": 45.47, "y": -8.28, "rotation": -180, "width": 96, "height": 30 }
+			},
+			"tread7": {
+				"tread": { "x": 45.47, "y": -8.28, "rotation": -180, "width": 96, "height": 30 }
+			},
+			"tread8": {
+				"tread": { "x": 45.47, "y": -8.28, "rotation": -180, "width": 96, "height": 30 }
+			},
+			"tread9": {
+				"tread": { "x": 45.47, "y": -8.28, "rotation": -180, "width": 96, "height": 30 }
+			},
+			"tread10": {
+				"tread": { "x": 45.47, "y": -8.28, "rotation": -180, "width": 96, "height": 30 }
+			},
+			"tread11": {
+				"tread": { "x": 45.47, "y": -8.28, "rotation": -180, "width": 96, "height": 30 }
+			},
+			"tread12": {
+				"tread": { "x": 45.47, "y": -8.28, "rotation": -180, "width": 96, "height": 30 }
+			},
+			"tread13": {
+				"tread": { "x": 45.47, "y": -8.28, "rotation": -180, "width": 96, "height": 30 }
+			},
+			"tread14": {
+				"tread": { "x": 45.47, "y": -8.28, "rotation": -180, "width": 96, "height": 30 }
+			},
+			"tread15": {
+				"tread": { "x": 45.47, "y": -8.28, "rotation": -180, "width": 96, "height": 30 }
+			},
+			"tread16": {
+				"tread": { "x": 45.47, "y": -8.28, "rotation": -180, "width": 96, "height": 30 }
+			},
+			"tread17": {
+				"tread": { "x": 45.47, "y": -8.28, "rotation": -180, "width": 96, "height": 30 }
+			},
+			"tread18": {
+				"tread": { "x": 45.47, "y": -8.28, "rotation": -180, "width": 96, "height": 30 }
+			},
+			"tread19": {
+				"tread": { "x": 45.47, "y": -8.28, "rotation": -180, "width": 96, "height": 30 }
+			},
+			"tread20": {
+				"tread": { "x": 45.47, "y": -8.28, "rotation": -180, "width": 96, "height": 30 }
+			},
+			"tread21": {
+				"tread": { "x": 45.47, "y": -8.28, "rotation": -180, "width": 96, "height": 30 }
+			},
+			"tread22": {
+				"tread": { "x": 45.47, "y": -8.28, "rotation": -180, "width": 96, "height": 30 }
+			},
+			"tread23": {
+				"tread": { "x": 45.47, "y": -8.28, "rotation": -180, "width": 96, "height": 30 }
+			},
+			"tread24": {
+				"tread": { "x": 45.47, "y": -8.28, "rotation": -180, "width": 96, "height": 30 }
+			},
+			"tread25": {
+				"tread": { "x": 45.47, "y": -8.28, "rotation": -180, "width": 96, "height": 30 }
+			},
+			"tread26": {
+				"tread": { "x": 45.47, "y": -8.28, "rotation": -180, "width": 96, "height": 30 }
+			},
+			"tread27": {
+				"tread": { "x": 45.47, "y": -8.28, "rotation": -180, "width": 96, "height": 30 }
+			},
+			"tread28": {
+				"tread": { "x": 45.47, "y": -8.28, "rotation": -180, "width": 96, "height": 30 }
+			},
+			"tread29": {
+				"tread": { "x": 45.47, "y": -8.28, "rotation": -180, "width": 96, "height": 30 }
+			},
+			"tread30": {
+				"tread": { "x": 45.47, "y": -8.28, "rotation": -180, "width": 96, "height": 30 }
+			},
+			"tread31": {
+				"tread": { "x": 45.47, "y": -8.28, "rotation": -180, "width": 96, "height": 30 }
+			},
+			"tread32": {
+				"tread": { "x": 45.47, "y": -8.28, "rotation": -180, "width": 96, "height": 30 }
+			},
+			"tread33": {
+				"tread": { "x": 45.47, "y": -8.28, "rotation": -180, "width": 96, "height": 30 }
+			},
+			"tread34": {
+				"tread": { "x": 45.47, "y": -8.28, "rotation": -180, "width": 96, "height": 30 }
+			},
+			"tread35": {
+				"tread": { "x": 45.47, "y": -8.28, "rotation": -180, "width": 96, "height": 30 }
+			},
+			"tread36": {
+				"tread": { "x": 45.47, "y": -8.28, "rotation": -180, "width": 96, "height": 30 }
+			},
+			"treads-path": {
+				"treads-path": {
+					"type": "path",
+					"closed": true,
+					"lengths": [ 185.21, 354.53, 478.3, 608.52, 786, 1058.49, 1138.97, 1223.96, 1303.87, 1388.23, 1471.11, 1551.64, 1633.55, 1713.27, 1799.89, 1882.28, 2164.2, 2326.85, 2444.07, 2584.91, 2754.15, 2940.62 ],
+					"vertexCount": 66,
+					"vertices": [ 1, 110, 11.23, 41.87, 1, 1, 110, 0.79, 41.95, 1, 1, 110, -34.72, 42.24, 1, 1, 56, -104.22, 0.41, 1, 1, 56, 0.07, 0.55, 1, 1, 56, 68.8, 0.65, 1, 1, 109, 20.5, 43.47, 1, 1, 109, 1.14, 40.82, 1, 1, 109, -27.38, 36.85, 1, 1, 93, 147.07, 105.01, 1, 1, 93, 96.21, 96.63, 1, 1, 93, 43.87, 87.72, 1, 1, 93, 16.18, 103.35, 1, 1, 93, -33.67, 94.21, 1, 1, 93, -99.36, 81.25, 1, 1, 93, -122.05, -1.7, 1, 1, 93, -83.58, -47.93, 1, 1, 93, -33.53, -109.37, 1, 1, 97, -83.57, -66.1, 1, 1, 97, -2.17, -67.9, 1, 2, 97, 56.68, -41.49, 0.68, 51, -24.31, -41.49, 0.32, 1, 51, -26.59, 16.7, 1, 1, 51, -2.69, 16.7, 1, 1, 51, 13.52, 16.7, 1, 2, 98, -52.42, -46.51, 0.744, 51, 30.21, -46.52, 0.256, 1, 98, -0.32, -68.92, 1, 2, 98, 52.09, -44.73, 0.712, 52, -28.91, -44.73, 0.288, 1, 52, -22.81, 16.24, 1, 1, 52, -1.42, 16.24, 1, 1, 52, 20.48, 16.24, 1, 2, 99, -47.21, -47.46, 0.744, 52, 36.01, -47.46, 0.256, 1, 99, -0.29, -69.66, 1, 2, 99, 45.24, -47.26, 0.736, 53, -37.49, -47.26, 0.264, 1, 53, -23.76, 15.28, 1, 1, 53, -0.14, 15.28, 1, 1, 53, 24.45, 15.28, 1, 2, 100, -47.37, -48.7, 0.744, 53, 33.53, -48.7, 0.256, 1, 100, -0.5, -70.4, 1, 2, 100, 49.09, -48.34, 0.744, 54, -33.58, -48.34, 0.256, 1, 54, -20.89, 15.84, 1, 1, 54, -1.26, 15.84, 1, 1, 54, 15.78, 15.84, 1, 2, 101, -52.5, -48.21, 0.76, 54, 28.45, -48.22, 0.24, 1, 101, -2.5, -68.92, 1, 2, 101, 55.72, -47.82, 0.752, 55, -28.88, -47.83, 0.248, 1, 55, -21.64, 16.7, 1, 1, 55, -0.48, 16.7, 1, 1, 55, 20.74, 16.7, 1, 2, 102, -53.65, -48.9, 0.76, 55, 25.97, -48.9, 0.24, 1, 102, 2.28, -69.66, 1, 1, 102, 44.95, -69.74, 1, 1, 94, 76.03, -85.61, 1, 1, 94, 93.58, -42.24, 1, 1, 94, 118.67, 19.75, 1, 1, 94, 78.59, 76.62, 1, 1, 94, 37.27, 95.07, 1, 1, 94, 31.45, 97.67, 1, 1, 94, -15.16, 87.48, 1, 1, 94, -79.8, 92.52, 1, 1, 94, -119.06, 95.58, 1, 1, 111, 47.07, 42.29, 1, 1, 111, 0.25, 42.75, 1, 1, 111, -29.64, 43.29, 1, 1, 57, -86.65, 1.35, 1, 1, 57, 0.49, 0.26, 1, 1, 57, 92.42, -0.9, 1 ],
+					"color": "ff8819ff"
+				}
+			},
+			"wheel-big": {
+				"wheel-big": { "width": 191, "height": 191 }
+			},
+			"wheel-big-overlay1": {
+				"wheel-big-overlay": { "width": 186, "height": 186 }
+			},
+			"wheel-big-overlay2": {
+				"wheel-big-overlay": { "width": 186, "height": 186 }
+			},
+			"wheel-big2": {
+				"wheel-big": { "width": 191, "height": 191 }
+			},
+			"wheel-mid": {
+				"wheel-mid": { "width": 136, "height": 136 }
+			},
+			"wheel-mid-overlay": {
+				"wheel-mid-overlay": { "width": 136, "height": 136 }
+			},
+			"wheel-mid-overlay2": {
+				"wheel-mid-overlay": { "width": 136, "height": 136 }
+			},
+			"wheel-mid-overlay3": {
+				"wheel-mid-overlay": { "width": 136, "height": 136 }
+			},
+			"wheel-mid-overlay4": {
+				"wheel-mid-overlay": { "width": 136, "height": 136 }
+			},
+			"wheel-mid-overlay5": {
+				"wheel-mid-overlay": { "width": 136, "height": 136 }
+			},
+			"wheel-mid-overlay6": {
+				"wheel-mid-overlay": { "width": 136, "height": 136 }
+			},
+			"wheel-mid2": {
+				"wheel-mid": { "width": 136, "height": 136 }
+			},
+			"wheel-mid3": {
+				"wheel-mid": { "width": 136, "height": 136 }
+			},
+			"wheel-mid4": {
+				"wheel-mid": { "width": 136, "height": 136 }
+			},
+			"wheel-mid5": {
+				"wheel-mid": { "width": 136, "height": 136 }
+			},
+			"wheel-mid6": {
+				"wheel-mid": { "width": 136, "height": 136 }
+			},
+			"wheel-small": {
+				"wheel-small": { "width": 71, "height": 71 }
+			},
+			"wheel-small2": {
+				"wheel-small": { "width": 71, "height": 71 }
+			},
+			"wheel-small3": {
+				"wheel-small": { "width": 71, "height": 71 }
+			}
+		}
+	}
+],
+"animations": {
+	"drive": {
+		"bones": {
+			"tank-root": {
+				"rotate": [
+					{ "time": 2 },
+					{ "time": 2.0667, "value": 1.99 },
+					{ "time": 2.5, "value": -15.63 },
+					{
+						"time": 2.6667,
+						"value": -10.37,
+						"curve": [ 2.718, -10.37, 2.78, -8.34 ]
+					},
+					{
+						"time": 2.8333,
+						"value": -6.13,
+						"curve": [ 2.909, -2.8, 2.974, 0.8 ]
+					},
+					{ "time": 3, "value": 1.84 },
+					{ "time": 3.0667, "value": 5.32 },
+					{ "time": 3.1667, "value": 10.99 },
+					{ "time": 3.2333, "value": 9.73 },
+					{
+						"time": 3.4333,
+						"value": -4.52,
+						"curve": [ 3.474, -3.99, 3.608, 0.01 ]
+					},
+					{ "time": 3.6667, "value": 0.01 }
+				],
+				"translate": [
+					{
+						"curve": [ 1.019, 0, 1.608, -582.83, 1.019, 0, 1.608, 0 ]
+					},
+					{ "time": 2, "x": -1209.75 },
+					{ "time": 2.3333, "x": -1652.84, "y": 26.05 },
+					{ "time": 2.5, "x": -1877.69, "y": 71.5 },
+					{ "time": 2.6667, "x": -2053.37, "y": 100.44 },
+					{ "time": 2.8333, "x": -2183.86, "y": 97.42 },
+					{ "time": 3, "x": -2312.32, "y": 74.12 },
+					{ "time": 3.0667, "x": -2340.68, "y": 45.94 },
+					{ "time": 3.1333, "x": -2403.04, "y": 17.04 },
+					{ "time": 3.1667, "x": -2439.84, "y": 5.45 },
+					{ "time": 3.2333, "x": -2523.34, "y": -3.31 },
+					{ "time": 3.4333, "x": -2728.27, "y": -12.73 },
+					{
+						"time": 3.5,
+						"x": -2795.65,
+						"y": -6.14,
+						"curve": [ 3.538, -2829.89, 3.583, -2878.59, 3.538, -4.93, 3.583, -3.21 ]
+					},
+					{
+						"time": 3.6333,
+						"x": -2938.53,
+						"y": -1.09,
+						"curve": [ 3.89, -3218.84, 4.404, -3972.02, 3.89, -0.79, 4.404, 0 ]
+					},
+					{ "time": 4.8333, "x": -3972.02 },
+					{ "time": 5, "x": -3991.31 },
+					{ "time": 5.3667, "x": -3973.94 }
+				]
+			},
+			"tread-collider1": {
+				"translate": [
+					{ "time": 2 },
+					{ "time": 2.0667, "y": 9.99 },
+					{ "time": 2.1667, "y": 37.69 },
+					{ "time": 2.3333, "y": 53.45 },
+					{ "time": 2.5, "y": 30.97 },
+					{ "time": 2.6667, "y": -2.89 },
+					{ "time": 2.8333, "y": -0.71 },
+					{ "time": 3.0667, "y": -13.64 },
+					{ "time": 3.1667, "y": 59.3 },
+					{ "time": 3.2333, "y": 48.2 },
+					{ "time": 3.4333, "y": -11.27 },
+					{ "time": 3.6333, "y": 4.15 }
+				]
+			},
+			"tread-collider2": {
+				"translate": [
+					{ "time": 2 },
+					{ "time": 2.0667, "y": -2.83 },
+					{ "time": 2.1667, "y": -17.44 },
+					{ "time": 2.3333, "y": 46.07 },
+					{ "time": 2.5, "y": 19.45 },
+					{ "time": 2.6667, "y": 13.46 },
+					{ "time": 2.8333, "y": -1.92, "curve": "stepped" },
+					{ "time": 2.9667, "y": -1.92 },
+					{ "time": 3, "y": -13.17 },
+					{ "time": 3.0667, "y": -23.25 },
+					{ "time": 3.1667, "y": 28.13 },
+					{ "time": 3.2333, "y": 25.63 },
+					{ "time": 3.4333, "y": -1.52 },
+					{ "time": 3.6333, "y": 1.15 }
+				]
+			},
+			"tread-collider3": {
+				"translate": [
+					{ "time": 2 },
+					{ "time": 2.0667, "y": -7.76 },
+					{ "time": 2.1667, "y": -16.61 },
+					{ "time": 2.5, "y": 29.05 },
+					{ "time": 2.6667, "y": 30.12 },
+					{ "time": 2.8333, "y": 5.3 },
+					{ "time": 3, "y": -0.38 },
+					{ "time": 3.1667, "y": 2.6 },
+					{ "time": 3.4333, "y": 15.41 },
+					{ "time": 3.6333, "y": 1.44 }
+				]
+			},
+			"tread-collider4": {
+				"translate": [
+					{ "time": 2 },
+					{ "time": 2.1667, "y": -6.72 },
+					{ "time": 2.3333, "y": -0.92 },
+					{ "time": 2.5, "y": 18.37 },
+					{ "time": 2.6667, "y": 38.77 },
+					{ "time": 2.8333, "y": 30.6 },
+					{ "time": 3.1667, "y": 12.61 },
+					{ "time": 3.2333, "y": -16 },
+					{ "time": 3.4333, "y": 25.62 },
+					{ "time": 3.6333, "y": -0.68 }
+				]
+			},
+			"tread-collider5": {
+				"translate": [
+					{ "time": 2 },
+					{ "time": 2.1667, "y": 3.35 },
+					{ "time": 2.3333, "y": 22.17 },
+					{ "time": 2.6667, "y": 13.35 },
+					{ "time": 2.8333, "y": 39 },
+					{ "time": 3, "y": 39.88 },
+					{ "time": 3.1667, "y": 26.57 },
+					{ "time": 3.2333, "y": -10.15 },
+					{ "time": 3.4333, "y": 35.98 },
+					{ "time": 3.6333, "y": -1.36 }
+				]
+			},
+			"wheel-mid-root6": {
+				"translate": [
+					{ "time": 2 },
+					{ "time": 2.1667, "y": 5.61 },
+					{ "time": 2.3333, "y": 27.21 },
+					{ "time": 2.5, "y": 30.28 },
+					{ "time": 2.6667, "y": -2.81 },
+					{ "time": 2.8333, "y": 19.59 },
+					{ "time": 3, "y": 29.11 },
+					{ "time": 3.1667, "y": 32.55 },
+					{ "time": 3.2333, "y": 3.55 },
+					{ "time": 3.4333, "y": 40.54 },
+					{ "time": 3.6333 }
+				]
+			},
+			"wheel-mid-root5": {
+				"translate": [
+					{ "time": 2 },
+					{ "time": 2.1667, "y": -7.46 },
+					{ "time": 2.3333, "y": 9.53 },
+					{ "time": 2.6667, "y": 36.78 },
+					{ "time": 2.8333, "y": 46.11 },
+					{ "time": 3.1667, "y": 7.55 },
+					{ "time": 3.2333, "y": -16.28 },
+					{ "time": 3.4333, "y": 26.21 },
+					{ "time": 3.6333 }
+				]
+			},
+			"wheel-mid-root4": {
+				"translate": [
+					{ "time": 2 },
+					{ "time": 2.1667, "y": -13.98 },
+					{ "time": 2.3333, "y": -8.26 },
+					{ "time": 2.5, "y": 24.27 },
+					{ "time": 2.6667, "y": 34.42 },
+					{ "time": 2.8333, "y": 8.88 },
+					{ "time": 3.1667, "y": 10.32 },
+					{ "time": 3.2333, "y": -7.63 },
+					{ "time": 3.4333, "y": 19.69 },
+					{ "time": 3.6333 }
+				]
+			},
+			"wheel-mid-root3": {
+				"translate": [
+					{ "time": 2 },
+					{ "time": 2.1667, "y": -21.14 },
+					{ "time": 2.3333, "y": 22.83 },
+					{ "time": 2.5, "y": 23.34 },
+					{ "time": 2.6667, "y": 18.07 },
+					{ "time": 2.8333, "y": 1.2 },
+					{ "time": 3.0667, "y": -13.36 },
+					{ "time": 3.1667, "y": 15.48 },
+					{ "time": 3.2333, "y": 13.34 },
+					{ "time": 3.4333, "y": 6.4 },
+					{ "time": 3.6333 }
+				]
+			},
+			"wheel-mid-root2": {
+				"translate": [
+					{ "time": 2 },
+					{ "time": 2.0667, "y": -4.39 },
+					{ "time": 2.1667, "y": 3.13 },
+					{ "time": 2.3333, "y": 53.56 },
+					{ "time": 2.5, "y": 16.65 },
+					{ "time": 2.6667, "y": 8.39 },
+					{ "time": 3.0667, "y": -19.16 },
+					{ "time": 3.1667, "y": 43.25 },
+					{ "time": 3.2333, "y": 39.04 },
+					{ "time": 3.4333, "y": -8.61 },
+					{ "time": 3.6333 }
+				]
+			},
+			"wheel-mid-root1": {
+				"translate": [
+					{ "time": 2 },
+					{ "time": 2.0333, "y": 22.64 },
+					{ "time": 2.0667, "y": 53.65 },
+					{ "time": 2.1667, "y": 71.18 },
+					{ "time": 2.5, "y": 46.83 },
+					{ "time": 2.6667, "y": 8.38 },
+					{ "time": 3.0667, "y": -10.03 },
+					{ "time": 3.1667, "y": 72.71 },
+					{ "time": 3.2333, "y": 64.74 },
+					{ "time": 3.4333, "y": -17.65 },
+					{ "time": 3.6333 }
+				]
+			},
+			"tank-body": {
+				"rotate": [
+					{
+						"curve": [ 0.208, 0, 0.625, -4.39 ]
+					},
+					{ "time": 0.8333, "value": -4.39 },
+					{ "time": 2 },
+					{ "time": 2.1667, "value": -1.34 },
+					{ "time": 2.3333, "value": -6.23 },
+					{ "time": 2.5, "value": -5.45 },
+					{ "time": 2.9667, "value": -5.07 },
+					{ "time": 3.0667, "value": -2.39 },
+					{ "time": 3.1667, "value": -0.98 },
+					{ "time": 3.2333, "value": -1.1 },
+					{
+						"time": 3.4,
+						"value": 0.43,
+						"curve": [ 3.433, 0.43, 3.483, -1.56 ]
+					},
+					{
+						"time": 3.5333,
+						"value": -3.55,
+						"curve": [ 3.675, -3.47, 3.754, 1.49 ]
+					},
+					{ "time": 3.8333, "value": 1.93 },
+					{ "time": 4, "value": 0.48 },
+					{
+						"time": 4.3333,
+						"curve": [ 4.476, 0.59, 4.833, 3.8 ]
+					},
+					{
+						"time": 5,
+						"value": 3.8,
+						"curve": [ 5.286, 3.8, 5.35, -2.17 ]
+					},
+					{ "time": 5.4667, "value": -2.17 },
+					{ "time": 5.6, "value": -0.61 }
+				]
+			},
+			"wheel-big-root1": {
+				"translate": [
+					{ "time": 2 },
+					{ "time": 2.0667, "y": 20.07 },
+					{ "time": 2.3333, "y": 67.24 },
+					{ "time": 2.6667, "y": 21.04 },
+					{ "time": 3, "y": 10.28 },
+					{ "time": 3.1, "y": 11.28 },
+					{ "time": 3.1667, "y": 29.43 },
+					{ "time": 3.2333, "y": 35.31 },
+					{ "time": 3.4333, "y": 18.38 },
+					{ "time": 3.5 }
+				]
+			},
+			"tank-treads": {
+				"rotate": [
+					{},
+					{ "time": 0.8333, "value": -2.4 },
+					{ "time": 2 },
+					{ "time": 2.0667, "value": 1.72 },
+					{ "time": 2.4333, "value": -0.37 },
+					{ "time": 2.8 },
+					{ "time": 3, "value": -1.41 },
+					{ "time": 3.1667, "value": 0.54 },
+					{
+						"time": 3.2667,
+						"value": 2.22,
+						"curve": [ 3.348, 2.22, 3.392, -1.31 ]
+					},
+					{ "time": 3.4333, "value": -1.31 },
+					{ "time": 3.7333, "value": -1.14 },
+					{
+						"time": 4.3333,
+						"curve": [ 4.476, 0.35, 4.833, 2.24 ]
+					},
+					{
+						"time": 5,
+						"value": 2.24,
+						"curve": [ 5.286, 2.24, 5.35, 0 ]
+					},
+					{ "time": 5.4667 }
+				]
+			},
+			"cannon-target": {
+				"translate": [
+					{},
+					{ "time": 0.8333, "y": 121.95 },
+					{ "time": 2, "y": 45.73 }
+				]
+			},
+			"wheel-big-root2": {
+				"translate": [
+					{ "time": 3.4333, "y": 13.01 }
+				]
+			},
+			"wheel-big1": {
+				"rotate": [
+					{
+						"curve": [ 0.51, 0, 0.804, 57.81 ]
+					},
+					{ "time": 1, "value": 120 },
+					{ "time": 1.2667, "value": 240 },
+					{ "time": 1.5333, "value": 360 },
+					{ "time": 1.7667, "value": 480 },
+					{ "time": 2.0333, "value": 600 },
+					{ "time": 2.2, "value": 720 },
+					{ "time": 2.4, "value": 840 },
+					{ "time": 2.5667, "value": 960 },
+					{ "time": 2.7333, "value": 1080 },
+					{ "time": 2.9333, "value": 1200 },
+					{ "time": 3.1333, "value": 1320 },
+					{ "time": 3.3333, "value": 1440 },
+					{ "time": 3.5, "value": 1560 },
+					{ "time": 3.6667, "value": 1680 },
+					{ "time": 3.8667, "value": 1800 },
+					{ "time": 4.0667, "value": 1920 },
+					{ "time": 4.2667, "value": 2040 },
+					{
+						"time": 4.5,
+						"value": 2160,
+						"curve": [ 4.563, 2194.34, 4.695, 2225.3 ]
+					},
+					{ "time": 4.8333, "value": 2247.67 }
+				]
+			},
+			"wheel-mid1": {
+				"rotate": [
+					{
+						"curve": [ 0.459, 0, 0.724, 57.81 ]
+					},
+					{ "time": 0.9, "value": 120 },
+					{ "time": 1.1667, "value": 240 },
+					{ "time": 1.4333, "value": 360 },
+					{ "time": 1.6333, "value": 480 },
+					{ "time": 1.8333, "value": 600 },
+					{ "time": 2, "value": 720 },
+					{ "time": 2.1333, "value": 840 },
+					{ "time": 2.2667, "value": 960 },
+					{ "time": 2.4, "value": 1080 },
+					{ "time": 2.5333, "value": 1200 },
+					{ "time": 2.6667, "value": 1320 },
+					{ "time": 2.8333, "value": 1440 },
+					{ "time": 2.9667, "value": 1560 },
+					{ "time": 3.1, "value": 1680 },
+					{ "time": 3.2333, "value": 1800 },
+					{ "time": 3.3667, "value": 1920 },
+					{ "time": 3.5, "value": 2040 },
+					{ "time": 3.6333, "value": 2160 },
+					{ "time": 3.7667, "value": 2280 },
+					{ "time": 3.9, "value": 2400 },
+					{ "time": 4.0333, "value": 2520 },
+					{ "time": 4.1667, "value": 2640 },
+					{ "time": 4.3, "value": 2760 },
+					{
+						"time": 4.4667,
+						"value": 2880,
+						"curve": [ 4.538, 2949.2, 4.742, 3000 ]
+					},
+					{ "time": 4.8333, "value": 3000 }
+				]
+			},
+			"wheel-small1": {
+				"rotate": [
+					{
+						"curve": [ 0.34, 0, 0.536, 57.81 ]
+					},
+					{ "time": 0.6667, "value": 120 },
+					{ "time": 0.8667, "value": 240 },
+					{ "time": 1.0333, "value": 360 },
+					{ "time": 1.1667, "value": 480 },
+					{ "time": 1.3, "value": 600 },
+					{ "time": 1.4333, "value": 720 },
+					{ "time": 1.5333, "value": 840 },
+					{ "time": 1.6333, "value": 960 },
+					{ "time": 1.7333, "value": 1080 },
+					{ "time": 1.8333, "value": 1200 },
+					{ "time": 1.9333, "value": 1320 },
+					{ "time": 2.0333, "value": 1440 },
+					{ "time": 2.1333, "value": 1560 },
+					{ "time": 2.2333, "value": 1680 },
+					{ "time": 2.3333, "value": 1800 },
+					{ "time": 2.4333, "value": 1920 },
+					{ "time": 2.5333, "value": 2040 },
+					{ "time": 2.6333, "value": 2160 },
+					{ "time": 2.7333, "value": 2280 },
+					{ "time": 2.8333, "value": 2400 },
+					{ "time": 2.9333, "value": 2520 },
+					{ "time": 3.0333, "value": 2640 },
+					{ "time": 3.1333, "value": 2760 },
+					{ "time": 3.2333, "value": 2880 },
+					{ "time": 3.3333, "value": 3000 },
+					{ "time": 3.4333, "value": 3120 },
+					{ "time": 3.5333, "value": 3240 },
+					{ "time": 3.6333, "value": 3360 },
+					{ "time": 3.7333, "value": 3480 },
+					{ "time": 3.8333, "value": 3600 },
+					{ "time": 3.9333, "value": 3720 },
+					{ "time": 4.0333, "value": 3840 },
+					{ "time": 4.1333, "value": 3960 },
+					{ "time": 4.2333, "value": 4080 },
+					{ "time": 4.3333, "value": 4200 },
+					{ "time": 4.4333, "value": 4320 },
+					{ "time": 4.6667, "value": 4440 },
+					{ "time": 4.9, "value": 4490 }
+				]
+			},
+			"wheel-small-root1": {
+				"translate": [
+					{ "time": 2 },
+					{ "time": 2.1333, "y": 12.37 },
+					{ "time": 2.4667, "y": 32.37 },
+					{ "time": 2.7333, "y": -5.27 },
+					{ "time": 2.9667, "y": 14.31 },
+					{ "time": 3.1667, "y": 19.54 },
+					{ "time": 3.4667, "y": 7.5 },
+					{ "time": 4.3667, "y": -2.4 }
+				]
+			},
+			"wheel-small-root2": {
+				"translate": [
+					{ "time": 2 },
+					{ "time": 2.9, "y": 5.26 },
+					{ "time": 3.1667, "y": 10.67 },
+					{ "time": 3.4667, "y": -4.71 }
+				]
+			},
+			"wheel-small-root3": {
+				"translate": [
+					{ "time": 2 },
+					{ "time": 2.4667, "y": -10.56 },
+					{ "time": 2.9, "y": -16.08 },
+					{ "time": 3.1667, "y": 10.12 },
+					{ "time": 3.4667, "y": 4.1 },
+					{ "time": 4.3667, "y": -0.03 }
+				]
+			},
+			"antenna1": {
+				"rotate": [
+					{},
+					{
+						"time": 0.3333,
+						"value": 1.05,
+						"curve": [ 0.731, 1.05, 1.008, -4.55 ]
+					},
+					{
+						"time": 1.2333,
+						"value": -4.55,
+						"curve": [ 1.35, -4.55, 1.583, 0.53 ]
+					},
+					{ "time": 1.7, "value": 0.53 },
+					{ "time": 2, "value": -2.13 },
+					{ "time": 2.2, "value": 4.71 },
+					{ "time": 2.3667, "value": -7.26 },
+					{ "time": 2.5667, "value": 2.26 },
+					{ "time": 2.7333, "value": -3.39 },
+					{ "time": 3.0667, "value": -5.53 },
+					{ "time": 3.2333, "value": 7.78 },
+					{ "time": 3.4667, "value": -5.99 },
+					{ "time": 3.7, "value": 3.11 },
+					{ "time": 3.9, "value": -3.05 },
+					{ "time": 4.1, "value": 0.31 },
+					{ "time": 4.3, "value": -3.06 },
+					{ "time": 4.5333, "value": 0.36 },
+					{
+						"time": 4.8667,
+						"value": 4.94,
+						"curve": [ 4.925, 4.94, 5.042, -2.38 ]
+					},
+					{ "time": 5.1, "value": -2.38 },
+					{ "time": 5.2667, "value": 3.65 },
+					{ "time": 5.4, "value": -3.04 },
+					{ "time": 5.5, "value": 1.49 },
+					{ "time": 5.6, "value": -1.86 },
+					{ "time": 5.7, "value": 0.42 }
+				]
+			},
+			"antenna2": {
+				"rotate": [
+					{},
+					{
+						"time": 0.3333,
+						"value": 1.05,
+						"curve": [ 0.731, 1.05, 1.008, -4.55 ]
+					},
+					{
+						"time": 1.2333,
+						"value": -4.55,
+						"curve": [ 1.35, -4.55, 1.583, 0.53 ]
+					},
+					{ "time": 1.7, "value": 0.53 },
+					{ "time": 2, "value": -2.13 },
+					{ "time": 2.2, "value": 4.71 },
+					{ "time": 2.3667, "value": -7.26 },
+					{ "time": 2.5667, "value": 2.26 },
+					{ "time": 2.7333, "value": -3.39 },
+					{ "time": 3.0667, "value": -5.53 },
+					{ "time": 3.2333, "value": 7.78 },
+					{ "time": 3.4667, "value": -5.99 },
+					{ "time": 3.7, "value": 3.11 },
+					{ "time": 3.9, "value": -3.05 },
+					{ "time": 4.1, "value": 0.31 },
+					{ "time": 4.3, "value": -3.06 },
+					{ "time": 4.5333, "value": 0.36 },
+					{
+						"time": 4.8667,
+						"value": 4.94,
+						"curve": [ 4.925, 4.94, 5.042, -2.38 ]
+					},
+					{ "time": 5.1, "value": -2.38 },
+					{ "time": 5.2667, "value": 3.65 },
+					{ "time": 5.4, "value": -3.04 },
+					{ "time": 5.5, "value": 1.49 },
+					{ "time": 5.6, "value": -1.86 },
+					{ "time": 5.7, "value": 0.42 }
+				]
+			},
+			"antenna3": {
+				"rotate": [
+					{},
+					{
+						"time": 0.3333,
+						"value": 1.05,
+						"curve": [ 0.731, 1.05, 1.008, -4.55 ]
+					},
+					{
+						"time": 1.2333,
+						"value": -4.55,
+						"curve": [ 1.35, -4.55, 1.583, 0.53 ]
+					},
+					{ "time": 1.7, "value": 0.53 },
+					{ "time": 2, "value": -2.13 },
+					{ "time": 2.2, "value": 4.71 },
+					{ "time": 2.3667, "value": -7.26 },
+					{ "time": 2.5667, "value": 2.26 },
+					{ "time": 2.7333, "value": -3.39 },
+					{ "time": 3.0667, "value": -5.53 },
+					{ "time": 3.2333, "value": 7.78 },
+					{ "time": 3.4667, "value": -5.99 },
+					{ "time": 3.7, "value": 3.11 },
+					{ "time": 3.9, "value": -3.05 },
+					{ "time": 4.1, "value": 0.31 },
+					{ "time": 4.3, "value": -3.06 },
+					{ "time": 4.5333, "value": 0.36 },
+					{
+						"time": 4.8667,
+						"value": 4.94,
+						"curve": [ 4.925, 4.94, 5.042, -2.38 ]
+					},
+					{ "time": 5.1, "value": -2.38 },
+					{ "time": 5.2667, "value": 3.65 },
+					{ "time": 5.4, "value": -3.04 },
+					{ "time": 5.5, "value": 1.49 },
+					{ "time": 5.6, "value": -1.86 },
+					{ "time": 5.7, "value": 0.42 }
+				]
+			},
+			"antenna4": {
+				"rotate": [
+					{},
+					{
+						"time": 0.3333,
+						"value": 1.05,
+						"curve": [ 0.731, 1.05, 1.008, -4.55 ]
+					},
+					{
+						"time": 1.2333,
+						"value": -4.55,
+						"curve": [ 1.35, -4.55, 1.583, 0.53 ]
+					},
+					{ "time": 1.7, "value": 0.53 },
+					{ "time": 2, "value": -2.13 },
+					{ "time": 2.2, "value": 4.71 },
+					{ "time": 2.3667, "value": -7.26 },
+					{ "time": 2.5667, "value": 2.26 },
+					{ "time": 2.7333, "value": -3.39 },
+					{ "time": 3.0667, "value": -5.53 },
+					{ "time": 3.2333, "value": 7.78 },
+					{ "time": 3.4667, "value": -5.99 },
+					{ "time": 3.7, "value": 3.11 },
+					{ "time": 3.9, "value": -3.05 },
+					{ "time": 4.1, "value": 0.31 },
+					{ "time": 4.3, "value": -3.06 },
+					{ "time": 4.5333, "value": 0.36 },
+					{
+						"time": 4.8667,
+						"value": 4.94,
+						"curve": [ 4.925, 4.94, 5.042, -2.38 ]
+					},
+					{ "time": 5.1, "value": -2.38 },
+					{ "time": 5.2667, "value": 3.65 },
+					{ "time": 5.4, "value": -3.04 },
+					{ "time": 5.5, "value": 1.49 },
+					{ "time": 5.6, "value": -1.86 },
+					{ "time": 5.7, "value": 0.42 }
+				]
+			},
+			"antenna5": {
+				"rotate": [
+					{},
+					{
+						"time": 0.3333,
+						"value": 1.05,
+						"curve": [ 0.731, 1.05, 1.008, -4.55 ]
+					},
+					{
+						"time": 1.2333,
+						"value": -4.55,
+						"curve": [ 1.35, -4.55, 1.583, 0.53 ]
+					},
+					{ "time": 1.7, "value": 0.53 },
+					{ "time": 2, "value": -2.13 },
+					{ "time": 2.2, "value": 4.71 },
+					{ "time": 2.3667, "value": -7.26 },
+					{ "time": 2.5667, "value": 2.26 },
+					{ "time": 2.7333, "value": -3.39 },
+					{ "time": 3.0667, "value": -5.53 },
+					{ "time": 3.2333, "value": 7.78 },
+					{ "time": 3.4667, "value": -5.99 },
+					{ "time": 3.7, "value": 3.11 },
+					{ "time": 3.9, "value": -3.05 },
+					{ "time": 4.1, "value": 0.31 },
+					{ "time": 4.3, "value": -3.06 },
+					{ "time": 4.5333, "value": 0.36 },
+					{
+						"time": 4.8667,
+						"value": 4.94,
+						"curve": [ 4.925, 4.94, 5.042, -2.38 ]
+					},
+					{ "time": 5.1, "value": -2.38 },
+					{ "time": 5.2667, "value": 3.65 },
+					{ "time": 5.4, "value": -3.04 },
+					{ "time": 5.5, "value": 1.49 },
+					{ "time": 5.6, "value": -1.86 },
+					{ "time": 5.7, "value": 0.42 }
+				]
+			},
+			"antenna6": {
+				"rotate": [
+					{},
+					{
+						"time": 0.3333,
+						"value": 1.05,
+						"curve": [ 0.731, 1.05, 1.008, -4.55 ]
+					},
+					{
+						"time": 1.2333,
+						"value": -4.55,
+						"curve": [ 1.35, -4.55, 1.583, 0.53 ]
+					},
+					{ "time": 1.7, "value": 0.53 },
+					{ "time": 2, "value": -2.13 },
+					{ "time": 2.2, "value": 4.71 },
+					{ "time": 2.3667, "value": -7.26 },
+					{ "time": 2.5667, "value": 2.26 },
+					{ "time": 2.7333, "value": -3.39 },
+					{ "time": 3.0667, "value": -5.53 },
+					{ "time": 3.2333, "value": 7.78 },
+					{ "time": 3.4667, "value": -5.99 },
+					{ "time": 3.7, "value": 3.11 },
+					{ "time": 3.9, "value": -3.05 },
+					{ "time": 4.1, "value": 0.31 },
+					{ "time": 4.3, "value": -3.06 },
+					{ "time": 4.5333, "value": 0.36 },
+					{
+						"time": 4.8667,
+						"value": 4.94,
+						"curve": [ 4.925, 4.94, 5.042, -2.38 ]
+					},
+					{ "time": 5.1, "value": -2.38 },
+					{ "time": 5.2667, "value": 3.65 },
+					{ "time": 5.4, "value": -3.04 },
+					{ "time": 5.5, "value": 1.49 },
+					{ "time": 5.6, "value": -1.86 },
+					{ "time": 5.7, "value": 0.42 }
+				]
+			},
+			"machinegun": {
+				"rotate": [
+					{ "value": 8.07, "curve": "stepped" },
+					{ "time": 2.0667, "value": 8.07 },
+					{ "time": 2.1667, "value": 3.11 },
+					{ "time": 2.5667, "value": -10.99, "curve": "stepped" },
+					{ "time": 3.1333, "value": -10.99 },
+					{ "time": 3.2667, "value": 18.18 },
+					{ "time": 3.4333, "value": 2.75, "curve": "stepped" },
+					{ "time": 4.7, "value": 2.75 },
+					{ "time": 4.9, "value": 8.07 }
+				]
+			}
+		},
+		"path": {
+			"treads-path": {
+				"position": [
+					{
+						"curve": [ 0.984, 0, 1.588, 0.1788 ]
+					},
+					{
+						"time": 2,
+						"value": 0.385,
+						"curve": [ 2.023, 0.3916, 2.045, 0.3983 ]
+					},
+					{ "time": 2.0667, "value": 0.405 },
+					{ "time": 2.3333, "value": 0.555 },
+					{ "time": 2.5, "value": 0.605 },
+					{ "time": 2.6667, "value": 0.685 },
+					{ "time": 2.8333, "value": 0.745 },
+					{ "time": 3, "value": 0.785 },
+					{ "time": 3.0667, "value": 0.8 },
+					{ "time": 3.1333, "value": 0.825 },
+					{ "time": 3.1667, "value": 0.835 },
+					{ "time": 3.2333, "value": 0.87 },
+					{
+						"time": 3.5,
+						"value": 0.98,
+						"curve": [ 3.726, 1.0474, 4.335, 1.4 ]
+					},
+					{ "time": 4.8333, "value": 1.4 }
+				]
+			}
+		}
+	},
+	"shoot": {
+		"slots": {
+			"rock": {
+				"attachment": [
+					{}
+				]
+			},
+			"smoke-glow": {
+				"rgba": [
+					{ "time": 0.1333, "color": "ffffffff" },
+					{ "time": 0.1667, "color": "ffbc8af4" },
+					{ "time": 0.2, "color": "fc8e8e90" },
+					{ "time": 0.2667, "color": "fa3e3e1e" }
+				],
+				"attachment": [
+					{ "time": 0.0667, "name": "smoke-glow" },
+					{ "time": 0.3 }
+				]
+			},
+			"smoke-puff1-bg": {
+				"rgba2": [
+					{ "time": 0.0667, "light": "ffd50cff", "dark": "3b2c23" },
+					{ "time": 0.3, "light": "ffd50cff", "dark": "604b3f", "curve": "stepped" },
+					{ "time": 0.6, "light": "ffd50cff", "dark": "604b3f" },
+					{ "time": 1.0333, "light": "ffd50c00", "dark": "604b3f" }
+				],
+				"attachment": [
+					{ "time": 0.0667, "name": "smoke-puff01-bg" }
+				]
+			},
+			"smoke-puff1-bg2": {
+				"rgba2": [
+					{ "time": 0.0667, "light": "ffd50cff", "dark": "3b2c23" },
+					{ "time": 0.3, "light": "ffd50cff", "dark": "604b3f", "curve": "stepped" },
+					{ "time": 0.4667, "light": "ffd50cff", "dark": "604b3f" },
+					{ "time": 0.8333, "light": "ffd50c00", "dark": "604b3f" }
+				],
+				"attachment": [
+					{ "time": 0.1333, "name": "smoke-puff01-bg" }
+				]
+			},
+			"smoke-puff1-bg3": {
+				"rgba2": [
+					{ "time": 0.0667, "light": "ffd50cff", "dark": "3b2c23" },
+					{ "time": 0.3, "light": "ffd50cff", "dark": "604b3f", "curve": "stepped" },
+					{ "time": 0.4667, "light": "ffd50cff", "dark": "604b3f" },
+					{ "time": 0.8333, "light": "ffd50c00", "dark": "604b3f" }
+				],
+				"attachment": [
+					{ "time": 0.1333, "name": "smoke-puff01-bg" }
+				]
+			},
+			"smoke-puff1-bg4": {
+				"rgba2": [
+					{ "time": 0.0667, "light": "ffd50cff", "dark": "3b2c23" },
+					{ "time": 0.3, "light": "ffd50cff", "dark": "604b3f", "curve": "stepped" },
+					{ "time": 0.6, "light": "ffd50cff", "dark": "604b3f" },
+					{ "time": 0.9, "light": "ffd50c00", "dark": "604b3f" }
+				],
+				"attachment": [
+					{ "time": 0.1333, "name": "smoke-puff01-bg" }
+				]
+			},
+			"smoke-puff1-bg5": {
+				"rgba2": [
+					{ "time": 0.0667, "light": "ffd50cff", "dark": "3b2c23" },
+					{ "time": 0.3, "light": "ffd50cff", "dark": "604b3f", "curve": "stepped" },
+					{ "time": 0.6, "light": "ffd50cff", "dark": "604b3f" },
+					{ "time": 0.9, "light": "ffd50c00", "dark": "604a3f" }
+				],
+				"attachment": [
+					{ "time": 0.1333, "name": "smoke-puff01-bg" }
+				]
+			},
+			"smoke-puff1-bg6": {
+				"rgba2": [
+					{ "time": 0.0667, "light": "ffd50cff", "dark": "3b2c23" },
+					{ "time": 0.3, "light": "ffd50cff", "dark": "604b3f", "curve": "stepped" },
+					{ "time": 0.4667, "light": "ffd50cff", "dark": "604b3f" },
+					{ "time": 0.7, "light": "ffd50c00", "dark": "604b3f" }
+				],
+				"attachment": [
+					{ "time": 0.1333, "name": "smoke-puff01-bg" }
+				]
+			},
+			"smoke-puff1-bg7": {
+				"rgba2": [
+					{ "time": 0.0667, "light": "ffd50cff", "dark": "3b2c23" },
+					{ "time": 0.3333, "light": "ffd50cff", "dark": "604b3f", "curve": "stepped" },
+					{ "time": 0.4667, "light": "ffd50cff", "dark": "604b3f" },
+					{ "time": 0.8333, "light": "ffd50c00", "dark": "604b3f" }
+				],
+				"attachment": [
+					{ "time": 0.1333, "name": "smoke-puff01-bg" }
+				]
+			},
+			"smoke-puff1-bg8": {
+				"rgba2": [
+					{ "time": 0.0667, "light": "ffd50cff", "dark": "3b2c23" },
+					{ "time": 0.3, "light": "ffd50cff", "dark": "604b3f", "curve": "stepped" },
+					{ "time": 0.4333, "light": "ffd50cff", "dark": "604b3f" },
+					{ "time": 0.9333, "light": "ffd50c00", "dark": "604b3f" }
+				],
+				"attachment": [
+					{ "time": 0.1333, "name": "smoke-puff01-bg" }
+				]
+			},
+			"smoke-puff1-bg9": {
+				"rgba2": [
+					{ "time": 0.1333, "light": "ffd50cff", "dark": "3b2c23" },
+					{ "time": 0.3, "light": "ffd50cff", "dark": "604b3f", "curve": "stepped" },
+					{ "time": 0.5333, "light": "ffd50cff", "dark": "604b3f" },
+					{ "time": 0.8333, "light": "ffd50c00", "dark": "604b3f" }
+				],
+				"attachment": [
+					{ "time": 0.1333, "name": "smoke-puff01-bg" }
+				]
+			},
+			"smoke-puff1-bg10": {
+				"rgba2": [
+					{ "time": 0.1333, "light": "ffd50cff", "dark": "3b2c23" },
+					{ "time": 0.3, "light": "ffd50cff", "dark": "604b3f", "curve": "stepped" },
+					{ "time": 0.5333, "light": "ffd50cff", "dark": "604b3f" },
+					{ "time": 0.8333, "light": "ffd50c00", "dark": "604a3f" }
+				],
+				"attachment": [
+					{ "time": 0.1333, "name": "smoke-puff01-bg" }
+				]
+			},
+			"smoke-puff1-bg11": {
+				"rgba2": [
+					{ "time": 0.1333, "light": "ffd50cff", "dark": "604b3f", "curve": "stepped" },
+					{ "time": 0.4667, "light": "ffd50cff", "dark": "604b3f" },
+					{ "time": 0.7667, "light": "ffd50c00", "dark": "604b3f" }
+				],
+				"attachment": [
+					{ "time": 0.3333, "name": "smoke-puff01-bg" }
+				]
+			},
+			"smoke-puff1-bg12": {
+				"rgba2": [
+					{ "time": 0.3333, "light": "ffd50cff", "dark": "604b3f", "curve": "stepped" },
+					{ "time": 0.6, "light": "ffd50cff", "dark": "604b3f" },
+					{ "time": 0.8667, "light": "ffd50c00", "dark": "604a3f" }
+				],
+				"attachment": [
+					{ "time": 0.3667, "name": "smoke-puff01-bg" }
+				]
+			},
+			"smoke-puff1-bg13": {
+				"rgba2": [
+					{ "time": 0.3667, "light": "ffd50cff", "dark": "604b3f", "curve": "stepped" },
+					{ "time": 0.4667, "light": "ffd50cff", "dark": "604b3f" },
+					{ "time": 1, "light": "ffd50c00", "dark": "604b3f" }
+				],
+				"attachment": [
+					{ "time": 0.3667, "name": "smoke-puff01-bg" }
+				]
+			},
+			"smoke-puff1-bg14": {
+				"rgba2": [
+					{ "time": 0.4333, "light": "ffd50cff", "dark": "604b3f", "curve": "stepped" },
+					{ "time": 0.6, "light": "ffd50cff", "dark": "604b3f" },
+					{ "time": 1.0667, "light": "ffd50c00", "dark": "604b3f" }
+				],
+				"attachment": [
+					{ "time": 0.4333, "name": "smoke-puff01-bg" }
+				]
+			},
+			"smoke-puff1-bg15": {
+				"rgba2": [
+					{ "time": 0.4, "light": "ffd50cff", "dark": "604b3f", "curve": "stepped" },
+					{ "time": 0.4667, "light": "ffd50cff", "dark": "604b3f" },
+					{ "time": 0.8333, "light": "ffd50c00", "dark": "604b3f" }
+				],
+				"attachment": [
+					{ "time": 0.4, "name": "smoke-puff01-bg" }
+				]
+			},
+			"smoke-puff1-bg16": {
+				"rgba2": [
+					{ "time": 0.4, "light": "ffd50cff", "dark": "604b3f", "curve": "stepped" },
+					{ "time": 0.4667, "light": "ffd50cff", "dark": "604b3f" },
+					{ "time": 0.7, "light": "ffd50c00", "dark": "604b3f" }
+				],
+				"attachment": [
+					{ "time": 0.4, "name": "smoke-puff01-bg" }
+				]
+			},
+			"smoke-puff1-bg17": {
+				"rgba2": [
+					{ "time": 0.2333, "light": "ffd50cff", "dark": "534035" },
+					{ "time": 0.3, "light": "ffd50cff", "dark": "604b3f", "curve": "stepped" },
+					{ "time": 0.4, "light": "ffd50cff", "dark": "604b3f" },
+					{ "time": 0.6667, "light": "ffd50c00", "dark": "604b3f" }
+				],
+				"attachment": [
+					{ "time": 0.2333, "name": "smoke-puff01-bg" }
+				]
+			},
+			"smoke-puff1-bg18": {
+				"rgba2": [
+					{ "time": 0.0667, "light": "ffd50cff", "dark": "3b2c23" },
+					{ "time": 0.3, "light": "ffd50cff", "dark": "604b3f", "curve": "stepped" },
+					{ "time": 0.5, "light": "ffd50cff", "dark": "604b3f" },
+					{ "time": 0.8, "light": "ffd50c00", "dark": "604b3f" }
+				],
+				"attachment": [
+					{ "time": 0.2333, "name": "smoke-puff01-bg" }
+				]
+			},
+			"smoke-puff1-bg20": {
+				"rgba2": [
+					{ "time": 0.0667, "light": "ffd50cff", "dark": "3b2c23" },
+					{ "time": 0.3, "light": "ffd50cff", "dark": "604b3f", "curve": "stepped" },
+					{ "time": 0.4667, "light": "ffd50cff", "dark": "604b3f" },
+					{ "time": 0.8, "light": "ffd50c00", "dark": "604b3f" }
+				],
+				"attachment": [
+					{ "time": 0.3333, "name": "smoke-puff01-bg" }
+				]
+			},
+			"smoke-puff1-bg21": {
+				"rgba2": [
+					{ "time": 0.0667, "light": "ffd50cff", "dark": "3b2c23" },
+					{ "time": 0.3, "light": "ffd50cff", "dark": "604b3f", "curve": "stepped" },
+					{ "time": 0.4667, "light": "ffd50cff", "dark": "604b3f" },
+					{ "time": 0.7, "light": "ffd50c00", "dark": "604b3f" }
+				]
+			},
+			"smoke-puff1-bg22": {
+				"rgba2": [
+					{ "time": 0.0667, "light": "ffd50cff", "dark": "3b2c23" },
+					{ "time": 0.3, "light": "ffd50cff", "dark": "604b3f", "curve": "stepped" },
+					{ "time": 0.4667, "light": "ffd50cff", "dark": "604b3f" },
+					{ "time": 0.7, "light": "ffd50c00", "dark": "604b3f" }
+				]
+			},
+			"smoke-puff1-bg23": {
+				"rgba2": [
+					{ "time": 0.0667, "light": "ffd50cff", "dark": "3b2c23" },
+					{ "time": 0.3, "light": "ffd50cff", "dark": "604b3f", "curve": "stepped" },
+					{ "time": 0.5, "light": "ffd50cff", "dark": "604b3f" },
+					{ "time": 0.7667, "light": "ffd50c00", "dark": "604b3f" }
+				],
+				"attachment": [
+					{ "time": 0.3, "name": "smoke-puff01-bg" }
+				]
+			},
+			"smoke-puff1-bg24": {
+				"rgba2": [
+					{ "time": 0.3, "light": "ffd50cff", "dark": "604b3f", "curve": "stepped" },
+					{ "time": 0.4667, "light": "ffd50cff", "dark": "604b3f" },
+					{ "time": 0.7, "light": "ffd50c00", "dark": "604b3f" }
+				],
+				"attachment": [
+					{ "time": 0.3, "name": "smoke-puff01-bg" }
+				]
+			},
+			"smoke-puff1-bg25": {
+				"rgba2": [
+					{ "time": 0.3, "light": "ffd50cff", "dark": "604b3f", "curve": "stepped" },
+					{ "time": 0.4667, "light": "ffd50cff", "dark": "604b3f" },
+					{ "time": 1, "light": "ffd50c00", "dark": "604b3f" }
+				],
+				"attachment": [
+					{ "time": 0.3667, "name": "smoke-puff01-bg" }
+				]
+			},
+			"smoke-puff1-bg26": {
+				"rgba2": [
+					{ "time": 0.3, "light": "ffd50cff", "dark": "604b3f", "curve": "stepped" },
+					{ "time": 0.6, "light": "ffd50cff", "dark": "604b3f" },
+					{ "time": 0.9333, "light": "ffd50c00", "dark": "604b3f" }
+				],
+				"attachment": [
+					{ "time": 0.3667, "name": "smoke-puff01-bg" }
+				]
+			},
+			"smoke-puff1-bg27": {
+				"rgba2": [
+					{ "time": 0.3, "light": "ffd50cff", "dark": "604b3f", "curve": "stepped" },
+					{ "time": 0.4667, "light": "ffd50cff", "dark": "604b3f" },
+					{ "time": 0.7333, "light": "ffd50c00", "dark": "604b3f" }
+				],
+				"attachment": [
+					{ "time": 0.3667, "name": "smoke-puff01-bg" }
+				]
+			},
+			"smoke-puff1-fg": {
+				"rgba2": [
+					{ "time": 0.0667, "light": "ffdf31ff", "dark": "ff0000" },
+					{ "time": 0.1333, "light": "fde252ff", "dark": "ff0000" },
+					{ "time": 0.1667, "light": "ffe568ff", "dark": "e26425" },
+					{ "time": 0.2, "light": "ffe568ff", "dark": "ab774c" },
+					{ "time": 0.3, "light": "ab764cff", "dark": "ac8d75", "curve": "stepped" },
+					{ "time": 0.6, "light": "ab764cff", "dark": "ac8d75" },
+					{ "time": 1.0333, "light": "ab764c00", "dark": "ac8d75" }
+				],
+				"attachment": [
+					{ "time": 0.0667, "name": "smoke-puff01-fg" },
+					{ "time": 0.1667, "name": "smoke-puff02-fg" },
+					{ "time": 0.2, "name": "smoke-puff03-fg" },
+					{ "time": 0.2333, "name": "smoke-puff04-fg" }
+				]
+			},
+			"smoke-puff1-fg2": {
+				"rgba2": [
+					{ "time": 0.1333, "light": "ffdf31ff", "dark": "ff0000" },
+					{ "time": 0.1667, "light": "ffe568ff", "dark": "e26425" },
+					{ "time": 0.2, "light": "ffe568ff", "dark": "ab774c" },
+					{ "time": 0.3, "light": "ab764cff", "dark": "ac8d75", "curve": "stepped" },
+					{ "time": 0.4667, "light": "ab764cff", "dark": "ac8d75" },
+					{ "time": 0.8333, "light": "ab764c00", "dark": "ac8d75" }
+				],
+				"attachment": [
+					{ "time": 0.1333, "name": "smoke-puff01-fg" },
+					{ "time": 0.1667, "name": "smoke-puff02-fg" },
+					{ "time": 0.2, "name": "smoke-puff03-fg" },
+					{ "time": 0.2333, "name": "smoke-puff04-fg" }
+				]
+			},
+			"smoke-puff1-fg3": {
+				"rgba2": [
+					{ "time": 0.1333, "light": "ffe457ff", "dark": "ff0000" },
+					{ "time": 0.1667, "light": "ffe568ff", "dark": "e26425" },
+					{ "time": 0.2, "light": "ffe568ff", "dark": "ab774c" },
+					{ "time": 0.3, "light": "ab764cff", "dark": "ac8d75", "curve": "stepped" },
+					{ "time": 0.4667, "light": "ab764cff", "dark": "ac8d75" },
+					{ "time": 0.8333, "light": "ab764c00", "dark": "ac8d75" }
+				],
+				"attachment": [
+					{ "time": 0.1333, "name": "smoke-puff01-fg" },
+					{ "time": 0.1667, "name": "smoke-puff02-fg" },
+					{ "time": 0.2, "name": "smoke-puff03-fg" },
+					{ "time": 0.2667, "name": "smoke-puff04-fg" }
+				]
+			},
+			"smoke-puff1-fg4": {
+				"rgba2": [
+					{ "time": 0.1333, "light": "fae781ff", "dark": "ff0000" },
+					{ "time": 0.1667, "light": "ffe568ff", "dark": "e26425" },
+					{ "time": 0.2, "light": "ffe568ff", "dark": "ab774c" },
+					{ "time": 0.3, "light": "ab764cff", "dark": "ac8d75", "curve": "stepped" },
+					{ "time": 0.6, "light": "ab764cff", "dark": "ac8d75" },
+					{ "time": 0.9, "light": "ac8c7500", "dark": "604a3f" }
+				],
+				"attachment": [
+					{ "time": 0.1333, "name": "smoke-puff01-fg" },
+					{ "time": 0.1667, "name": "smoke-puff02-fg" },
+					{ "time": 0.2, "name": "smoke-puff03-fg" },
+					{ "time": 0.2667, "name": "smoke-puff04-fg" }
+				]
+			},
+			"smoke-puff1-fg5": {
+				"rgba2": [
+					{ "time": 0.1333, "light": "ffdf31ff", "dark": "ff0000" },
+					{ "time": 0.1667, "light": "ffe568ff", "dark": "e26425" },
+					{ "time": 0.2, "light": "ffe568ff", "dark": "ab774c" },
+					{ "time": 0.3, "light": "ab764cff", "dark": "ac8d75", "curve": "stepped" },
+					{ "time": 0.6, "light": "ab764cff", "dark": "ac8d75" },
+					{ "time": 0.9, "light": "ab764c00", "dark": "ac8d75" }
+				],
+				"attachment": [
+					{ "time": 0.1333, "name": "smoke-puff01-fg" },
+					{ "time": 0.1667, "name": "smoke-puff02-fg" },
+					{ "time": 0.2, "name": "smoke-puff03-fg" },
+					{ "time": 0.2667, "name": "smoke-puff04-fg" }
+				]
+			},
+			"smoke-puff1-fg6": {
+				"rgba2": [
+					{ "time": 0.1333, "light": "ffdf31ff", "dark": "ff0000" },
+					{ "time": 0.1667, "light": "ffe568ff", "dark": "e26425" },
+					{ "time": 0.2, "light": "ffe568ff", "dark": "ab774c" },
+					{ "time": 0.3, "light": "ab764cff", "dark": "ac8d75", "curve": "stepped" },
+					{ "time": 0.4667, "light": "ab764cff", "dark": "ac8d75" },
+					{ "time": 0.7, "light": "ab764c00", "dark": "ac8d75" }
+				],
+				"attachment": [
+					{ "time": 0.1333, "name": "smoke-puff01-fg" },
+					{ "time": 0.1667, "name": "smoke-puff02-fg" },
+					{ "time": 0.2, "name": "smoke-puff03-fg" },
+					{ "time": 0.2667, "name": "smoke-puff04-fg" }
+				]
+			},
+			"smoke-puff1-fg7": {
+				"rgba2": [
+					{ "time": 0.1333, "light": "ffdf31ff", "dark": "ff0000" },
+					{ "time": 0.1667, "light": "ffe568ff", "dark": "e26425" },
+					{ "time": 0.2, "light": "ffe568ff", "dark": "ab774c" },
+					{ "time": 0.3333, "light": "ab764cff", "dark": "ac8d75", "curve": "stepped" },
+					{ "time": 0.4667, "light": "ab764cff", "dark": "ac8d75" },
+					{ "time": 0.8333, "light": "ac8c7500", "dark": "604a3f" }
+				],
+				"attachment": [
+					{ "time": 0.1333, "name": "smoke-puff01-fg" },
+					{ "time": 0.1667, "name": "smoke-puff02-fg" },
+					{ "time": 0.2, "name": "smoke-puff03-fg" },
+					{ "time": 0.2333, "name": "smoke-puff04-fg" }
+				]
+			},
+			"smoke-puff1-fg8": {
+				"rgba2": [
+					{ "time": 0.1333, "light": "ffdf31ff", "dark": "ff0000" },
+					{ "time": 0.1667, "light": "ffe568ff", "dark": "e26425" },
+					{ "time": 0.2, "light": "ffe568ff", "dark": "ab774c" },
+					{ "time": 0.3, "light": "ab764cff", "dark": "ac8d75", "curve": "stepped" },
+					{ "time": 0.4333, "light": "ab764cff", "dark": "ac8d75" },
+					{ "time": 0.9333, "light": "ab764c00", "dark": "ac8d75" }
+				],
+				"attachment": [
+					{ "time": 0.1333, "name": "smoke-puff01-fg" },
+					{ "time": 0.1667, "name": "smoke-puff02-fg" },
+					{ "time": 0.2, "name": "smoke-puff03-fg" },
+					{ "time": 0.2333, "name": "smoke-puff04-fg" }
+				]
+			},
+			"smoke-puff1-fg9": {
+				"rgba2": [
+					{ "time": 0.1333, "light": "ffdf31ff", "dark": "ff0000" },
+					{ "time": 0.1667, "light": "ffe568ff", "dark": "e26425" },
+					{ "time": 0.2, "light": "ffe568ff", "dark": "ab774c" },
+					{ "time": 0.3, "light": "ab764cff", "dark": "ac8d75", "curve": "stepped" },
+					{ "time": 0.5333, "light": "ab764cff", "dark": "ac8d75" },
+					{ "time": 0.8333, "light": "ab764c00", "dark": "ac8d75" }
+				],
+				"attachment": [
+					{ "time": 0.1333, "name": "smoke-puff01-fg" },
+					{ "time": 0.1667, "name": "smoke-puff02-fg" },
+					{ "time": 0.2, "name": "smoke-puff03-fg" },
+					{ "time": 0.2333, "name": "smoke-puff04-fg" }
+				]
+			},
+			"smoke-puff1-fg10": {
+				"rgba2": [
+					{ "time": 0.1333, "light": "fce35dff", "dark": "ff0000" },
+					{ "time": 0.1667, "light": "ffe568ff", "dark": "e26425" },
+					{ "time": 0.2, "light": "ffe568ff", "dark": "ab774c" },
+					{ "time": 0.3, "light": "ab764cff", "dark": "ac8d75", "curve": "stepped" },
+					{ "time": 0.5333, "light": "ab764cff", "dark": "ac8d75" },
+					{ "time": 0.8333, "light": "ac8c7500", "dark": "604a3f" }
+				],
+				"attachment": [
+					{ "time": 0.1333, "name": "smoke-puff01-fg" },
+					{ "time": 0.1667, "name": "smoke-puff02-fg" },
+					{ "time": 0.2, "name": "smoke-puff03-fg" },
+					{ "time": 0.2667, "name": "smoke-puff04-fg" }
+				]
+			},
+			"smoke-puff1-fg11": {
+				"rgba2": [
+					{ "time": 0.3333, "light": "ab764cff", "dark": "ac8d75", "curve": "stepped" },
+					{ "time": 0.4667, "light": "ab764cff", "dark": "ac8d75" },
+					{ "time": 0.7667, "light": "ab764c00", "dark": "ac8d75" }
+				],
+				"attachment": [
+					{ "time": 0.3333, "name": "smoke-puff04-fg" }
+				]
+			},
+			"smoke-puff1-fg12": {
+				"rgba2": [
+					{ "time": 0.3667, "light": "ab764cff", "dark": "ac8d75", "curve": "stepped" },
+					{ "time": 0.6, "light": "ab764cff", "dark": "ac8d75" },
+					{ "time": 0.8667, "light": "ac8c7500", "dark": "604a3f" }
+				],
+				"attachment": [
+					{ "time": 0.3667, "name": "smoke-puff04-fg" }
+				]
+			},
+			"smoke-puff1-fg13": {
+				"rgba2": [
+					{ "time": 0.3667, "light": "ab764cff", "dark": "ac8d75", "curve": "stepped" },
+					{ "time": 0.4667, "light": "ab764cff", "dark": "ac8d75" },
+					{ "time": 1, "light": "ab764c00", "dark": "ac8d75" }
+				],
+				"attachment": [
+					{ "time": 0.3667, "name": "smoke-puff04-fg" }
+				]
+			},
+			"smoke-puff1-fg14": {
+				"rgba2": [
+					{ "time": 0.4333, "light": "ab764cff", "dark": "ac8d75", "curve": "stepped" },
+					{ "time": 0.6, "light": "ab764cff", "dark": "ac8d75" },
+					{ "time": 1.0667, "light": "ab764c00", "dark": "ac8d75" }
+				],
+				"attachment": [
+					{ "time": 0.4333, "name": "smoke-puff04-fg" }
+				]
+			},
+			"smoke-puff1-fg15": {
+				"rgba2": [
+					{ "time": 0.4, "light": "ab764cff", "dark": "ac8d75", "curve": "stepped" },
+					{ "time": 0.4667, "light": "ab764cff", "dark": "ac8d75" },
+					{ "time": 0.8333, "light": "ab764c00", "dark": "ac8d75" }
+				],
+				"attachment": [
+					{ "time": 0.4, "name": "smoke-puff04-fg" }
+				]
+			},
+			"smoke-puff1-fg16": {
+				"rgba2": [
+					{ "time": 0.4, "light": "ab764cff", "dark": "ac8d75", "curve": "stepped" },
+					{ "time": 0.4667, "light": "ab764cff", "dark": "ac8d75" },
+					{ "time": 0.7, "light": "ab764c00", "dark": "ac8d75" }
+				],
+				"attachment": [
+					{ "time": 0.4, "name": "smoke-puff04-fg" }
+				]
+			},
+			"smoke-puff1-fg17": {
+				"rgba2": [
+					{ "time": 0.2333, "light": "e3c05eff", "dark": "ab7e59" },
+					{ "time": 0.3, "light": "ab764cff", "dark": "ac8d75", "curve": "stepped" },
+					{ "time": 0.4, "light": "ab764cff", "dark": "ac8d75" },
+					{ "time": 0.6667, "light": "ab764c00", "dark": "ac8d75" }
+				],
+				"attachment": [
+					{ "time": 0.2333, "name": "smoke-puff04-fg" }
+				]
+			},
+			"smoke-puff1-fg18": {
+				"rgba2": [
+					{ "time": 0.1333, "light": "ffdf31ff", "dark": "ff0000" },
+					{ "time": 0.1667, "light": "ffe568ff", "dark": "e26425" },
+					{ "time": 0.2, "light": "ffe568ff", "dark": "ab774c" },
+					{ "time": 0.3, "light": "ab764cff", "dark": "ac8d75", "curve": "stepped" },
+					{ "time": 0.5, "light": "ab764cff", "dark": "ac8d75" },
+					{ "time": 0.7667, "light": "ab764c00", "dark": "ac8d75" }
+				],
+				"attachment": [
+					{ "time": 0.2333, "name": "smoke-puff03-fg" },
+					{ "time": 0.2667, "name": "smoke-puff04-fg" }
+				]
+			},
+			"smoke-puff1-fg20": {
+				"rgba2": [
+					{ "time": 0.1333, "light": "ffdf31ff", "dark": "ff0000" },
+					{ "time": 0.1667, "light": "ffe568ff", "dark": "e26425" },
+					{ "time": 0.2, "light": "ffe568ff", "dark": "ab774c" },
+					{ "time": 0.3, "light": "ab764cff", "dark": "ac8d75", "curve": "stepped" },
+					{ "time": 0.4667, "light": "ab764cff", "dark": "ac8d75" },
+					{ "time": 0.8, "light": "ab764c00", "dark": "ac8d75" }
+				],
+				"attachment": [
+					{ "time": 0.3333, "name": "smoke-puff04-fg" }
+				]
+			},
+			"smoke-puff1-fg21": {
+				"rgba2": [
+					{ "time": 0.1333, "light": "ffdf31ff", "dark": "ff0000" },
+					{ "time": 0.1667, "light": "ffe568ff", "dark": "e26425" },
+					{ "time": 0.2, "light": "ffe568ff", "dark": "ab774c" },
+					{ "time": 0.3, "light": "ab764cff", "dark": "ac8d75", "curve": "stepped" },
+					{ "time": 0.4667, "light": "ab764cff", "dark": "ac8d75" },
+					{ "time": 0.7, "light": "ab764c00", "dark": "ac8d75" }
+				]
+			},
+			"smoke-puff1-fg22": {
+				"rgba2": [
+					{ "time": 0.1333, "light": "ffdf31ff", "dark": "ff0000" },
+					{ "time": 0.1667, "light": "ffe568ff", "dark": "e26425" },
+					{ "time": 0.2, "light": "ffe568ff", "dark": "ab774c" },
+					{ "time": 0.3, "light": "ab764cff", "dark": "ac8d75", "curve": "stepped" },
+					{ "time": 0.4667, "light": "ab764cff", "dark": "ac8d75" },
+					{ "time": 0.7, "light": "ab764c00", "dark": "ac8d75" }
+				]
+			},
+			"smoke-puff1-fg23": {
+				"rgba2": [
+					{ "time": 0.1333, "light": "ffdf31ff", "dark": "ff0000" },
+					{ "time": 0.1667, "light": "ffe568ff", "dark": "e26425" },
+					{ "time": 0.2, "light": "ffe568ff", "dark": "ab774c" },
+					{ "time": 0.3, "light": "ab764cff", "dark": "ac8d75", "curve": "stepped" },
+					{ "time": 0.5, "light": "ab764cff", "dark": "ac8d75" },
+					{ "time": 0.7667, "light": "ab764c00", "dark": "ac8d75" }
+				],
+				"attachment": [
+					{ "time": 0.3, "name": "smoke-puff04-fg" }
+				]
+			},
+			"smoke-puff1-fg24": {
+				"rgba2": [
+					{ "time": 0.3, "light": "ab764cff", "dark": "ac8d75", "curve": "stepped" },
+					{ "time": 0.4667, "light": "ab764cff", "dark": "ac8d75" },
+					{ "time": 0.7, "light": "ab764c00", "dark": "ac8d75" }
+				],
+				"attachment": [
+					{ "time": 0.3, "name": "smoke-puff04-fg" }
+				]
+			},
+			"smoke-puff1-fg25": {
+				"rgba2": [
+					{ "time": 0.3, "light": "ab764cff", "dark": "ac8d75", "curve": "stepped" },
+					{ "time": 0.4667, "light": "ab764cff", "dark": "ac8d75" },
+					{ "time": 1, "light": "ab764c00", "dark": "ac8d75" }
+				],
+				"attachment": [
+					{ "time": 0.3667, "name": "smoke-puff04-fg" }
+				]
+			},
+			"smoke-puff1-fg26": {
+				"rgba2": [
+					{ "time": 0.3, "light": "ab764cff", "dark": "ac8d75", "curve": "stepped" },
+					{ "time": 0.6, "light": "ab764cff", "dark": "ac8d75" },
+					{ "time": 0.9333, "light": "ab764c00", "dark": "ac8d75" }
+				],
+				"attachment": [
+					{ "time": 0.3667, "name": "smoke-puff04-fg" }
+				]
+			},
+			"smoke-puff1-fg27": {
+				"rgba2": [
+					{ "time": 0.3, "light": "ab764cff", "dark": "ac8d75", "curve": "stepped" },
+					{ "time": 0.4667, "light": "ab764cff", "dark": "ac8d75" },
+					{ "time": 0.7333, "light": "ab764c00", "dark": "ac8d75" }
+				],
+				"attachment": [
+					{ "time": 0.3667, "name": "smoke-puff04-fg" }
+				]
+			},
+			"tank-glow": {
+				"rgba": [
+					{ "time": 0.0667, "color": "fc994d84" },
+					{
+						"time": 0.1333,
+						"color": "f5b16bc8",
+						"curve": [ 0.221, 0.96, 0.252, 0.98, 0.221, 0.69, 0.252, 0.62, 0.221, 0.42, 0.252, 0.33, 0.221, 0.78, 0.252, 0.32 ]
+					},
+					{ "time": 0.2667, "color": "fc994c30" }
+				],
+				"attachment": [
+					{ "time": 0.0667, "name": "smoke-glow" },
+					{ "time": 0.2667 }
+				]
+			}
+		},
+		"bones": {
+			"cannon": {
+				"translate": [
+					{ "time": 0.0667 },
+					{ "time": 0.1667, "x": 34.77, "y": 0.9 },
+					{ "time": 0.2667, "x": 1.3 }
+				]
+			},
+			"tank-body": {
+				"rotate": [
+					{ "time": 0.0667 },
+					{
+						"time": 0.1667,
+						"value": -4.29,
+						"curve": [ 0.2, -4.29, 0.267, 2.37 ]
+					},
+					{
+						"time": 0.3,
+						"value": 2.37,
+						"curve": [ 0.333, 2.37, 0.4, 0 ]
+					},
+					{ "time": 0.4333 }
+				],
+				"translate": [
+					{ "time": 0.0667 },
+					{
+						"time": 0.1667,
+						"x": 31.04,
+						"y": 1.67,
+						"curve": [ 0.2, 31.04, 0.267, -12.05, 0.2, 1.67, 0.267, -0.23 ]
+					},
+					{ "time": 0.3, "x": -12.05, "y": -0.23 },
+					{ "time": 0.3667 }
+				]
+			},
+			"tank-treads": {
+				"rotate": [
+					{ "time": 0.0667 },
+					{ "time": 0.1667, "value": -3.08 },
+					{ "time": 0.3, "value": -0.42 }
+				]
+			},
+			"smoke1": {
+				"rotate": [
+					{ "time": 0.0667 },
+					{ "time": 0.1333, "value": 2.88 },
+					{ "time": 0.1667, "value": 2.34 },
+					{ "time": 0.2, "value": 124.36 },
+					{ "time": 0.2667, "value": 142.26 },
+					{ "time": 0.3333, "value": 86.78 },
+					{ "time": 0.4667, "value": 128.79 },
+					{ "time": 0.6333, "value": 146.22 },
+					{ "time": 1.0333, "value": 210.7 }
+				],
+				"translate": [
+					{ "time": 0.0667, "x": -9.69, "y": 1.05 },
+					{ "time": 0.1333, "x": 7.53, "y": 1.21 },
+					{ "time": 0.1667, "x": 3.26, "y": 4.07 },
+					{ "time": 0.2, "x": 29.64, "y": -17.46 },
+					{ "time": 0.2667, "x": 86.97, "y": 17.83 },
+					{ "time": 0.3333, "x": 193.74, "y": -38.98 },
+					{ "time": 0.4, "x": 341.67, "y": -39.52 },
+					{ "time": 0.6333, "x": 393.24, "y": -4.01 },
+					{ "time": 1.0333, "x": 410.76, "y": 6.35 }
+				],
+				"scale": [
+					{ "time": 0.0667 },
+					{ "time": 0.1333, "x": 3.171, "y": 0.756 },
+					{ "time": 0.1667, "x": 3.488, "y": 1.279 },
+					{ "time": 0.2, "x": 5.151, "y": 2.369 },
+					{ "time": 0.2667, "x": 4.735, "y": 3.622 },
+					{ "time": 0.3, "x": 4.735, "y": 4.019 },
+					{ "time": 0.3333, "x": 4.613, "y": 3.339 },
+					{ "time": 0.3667, "x": 4.918, "y": 3.561 },
+					{ "time": 0.4, "x": 4.6, "y": 4.263 },
+					{ "time": 0.6333, "x": 4.449, "y": 2.62 },
+					{ "time": 1.0333, "x": 3.09, "y": 1.447 }
+				]
+			},
+			"smoke2": {
+				"rotate": [
+					{ "time": 0.1667, "value": 31.55 },
+					{ "time": 0.3, "value": -22.63 },
+					{ "time": 0.4667, "value": 142.89 },
+					{ "time": 0.6, "value": 253.78 },
+					{ "time": 0.8333, "value": 299.28 }
+				],
+				"translate": [
+					{ "time": 0.1667, "x": 17.26, "y": 4.86 },
+					{ "time": 0.2333, "x": 141.22, "y": 27.27 },
+					{ "time": 0.3, "x": 178.86, "y": 56.63 },
+					{ "time": 0.3667, "x": 200.46, "y": 71.05 },
+					{ "time": 0.4333, "x": 213.12, "y": 78.39 },
+					{ "time": 0.6333, "x": 221.44, "y": 73.1 },
+					{ "time": 0.8333, "x": 223.32, "y": 73.74 }
+				],
+				"scale": [
+					{ "time": 0.1667, "x": 1.34, "y": 1.34 },
+					{ "time": 0.2333, "x": 2.81, "y": 1.317 },
+					{ "time": 0.3, "x": 2.932, "y": 1.374 },
+					{ "time": 0.4667, "x": 1.247, "y": 0.639 },
+					{ "time": 0.8333, "x": 0.778, "y": 0.515 }
+				]
+			},
+			"smoke3": {
+				"rotate": [
+					{ "time": 0.1667, "value": -5.54 },
+					{ "time": 0.2333, "value": 0.2 },
+					{ "time": 0.3333, "value": 20.27 },
+					{ "time": 0.4, "value": 31.36 },
+					{ "time": 0.4667, "value": 68.52 },
+					{ "time": 0.5333, "value": 99.74 },
+					{ "time": 0.6333, "value": 145.8 },
+					{ "time": 0.8333, "value": 193.28 }
+				],
+				"translate": [
+					{ "time": 0.1333, "x": 1.17, "y": 8.53 },
+					{ "time": 0.1667, "x": 37.53, "y": 4.84 },
+					{ "time": 0.2, "x": 67.99, "y": 9.85 },
+					{ "time": 0.2333, "x": 134.14, "y": -13.5 },
+					{ "time": 0.2667, "x": 181.31, "y": -19.93 },
+					{ "time": 0.3, "x": 238.28, "y": -8.82 },
+					{ "time": 0.3333, "x": 268.51, "y": -25.75 },
+					{ "time": 0.3667, "x": 359.06, "y": -28.49 },
+					{ "time": 0.4, "x": 432.96, "y": -24.11 },
+					{ "time": 0.4667, "x": 452.16, "y": -16.73 },
+					{ "time": 0.6333, "x": 456.28, "y": -0.41 },
+					{ "time": 0.8333, "x": 454.14, "y": 16.41 }
+				],
+				"scale": [
+					{ "time": 0.1333, "x": 2.258, "y": 1.366 },
+					{ "time": 0.1667, "x": 2.656, "y": 1.47 },
+					{ "time": 0.2, "x": 3.202, "y": 1.772 },
+					{ "time": 0.2333, "x": 3.202, "y": 1.93 },
+					{ "time": 0.2667, "x": 3.124, "y": 1.896 },
+					{ "time": 0.3, "x": 3.593, "y": 1.896 },
+					{ "time": 0.3333, "x": 2.363, "y": 1.247 },
+					{ "time": 0.3667, "x": 1.845, "y": 0.973 },
+					{ "time": 0.4, "x": 1.754, "y": 0.926 },
+					{ "time": 0.4333, "x": 1.448, "y": 0.695 },
+					{ "time": 0.4667, "x": 1.441, "y": 0.688 },
+					{ "time": 0.5333, "x": 0.865, "y": 0.456 },
+					{ "time": 0.7, "x": 0.86, "y": 0.454 },
+					{ "time": 0.8333, "x": 0.211, "y": 0.111 }
+				]
+			},
+			"smoke4": {
+				"rotate": [
+					{ "time": 0.1667, "value": -20.35 },
+					{ "time": 0.2333, "value": 18.5 },
+					{ "time": 0.3, "value": 57.77 },
+					{ "time": 0.4, "value": 105.85 },
+					{ "time": 0.6, "value": 161.28 },
+					{ "time": 0.9, "value": 208.43 }
+				],
+				"translate": [
+					{ "time": 0.1667, "x": 35.95, "y": 25.54 },
+					{ "time": 0.2333, "x": 34.17, "y": 1.87 },
+					{ "time": 0.3, "x": 136.7, "y": 21.5 },
+					{ "time": 0.4, "x": 138.61, "y": 34.8 },
+					{ "time": 0.6, "x": 160.38, "y": 37.13 },
+					{ "time": 0.9, "x": 196.41, "y": 30.36 }
+				],
+				"scale": [
+					{ "time": 0.1667, "x": 2.751, "y": 1.754 },
+					{ "time": 0.2333, "x": 3.486, "y": 2.224 },
+					{ "time": 0.2667, "x": 3.486, "y": 2.586 },
+					{ "time": 0.3, "x": 3.847, "y": 2.109 },
+					{ "time": 0.4, "x": 1.96, "y": 1.074 },
+					{ "time": 0.9, "x": 0.825, "y": 0.452 }
+				]
+			},
+			"smoke5": {
+				"rotate": [
+					{ "time": 0.2, "value": 23.09 },
+					{ "time": 0.2667, "value": 12.24 },
+					{ "time": 0.3333, "value": 36.92 },
+					{ "time": 0.4333, "value": -37.33 },
+					{ "time": 0.5333, "value": -0.66 },
+					{ "time": 0.9, "value": 64.02 }
+				],
+				"translate": [
+					{ "time": 0.1333 },
+					{ "time": 0.2333, "x": 123.76, "y": 19.44 },
+					{ "time": 0.3, "x": 239.08, "y": -49.72 },
+					{ "time": 0.3667, "x": 280.23, "y": -51.46 },
+					{ "time": 0.7, "x": 340.62, "y": -20.09 },
+					{ "time": 0.9, "x": 349.18, "y": -5.25 }
+				],
+				"scale": [
+					{ "time": 0.1333 },
+					{ "time": 0.1667, "x": 1.718, "y": 1.718 },
+					{ "time": 0.2, "x": 2.109, "y": 2.109 },
+					{ "time": 0.2333, "x": 1.781, "y": 2.183 },
+					{ "time": 0.2667, "x": 2.148, "y": 2.633 },
+					{ "time": 0.3333, "x": 2.234, "y": 2.738 },
+					{ "time": 0.3667, "x": 1.366, "y": 2.148 },
+					{ "time": 0.4, "x": 0.97, "y": 1.524 },
+					{ "time": 0.4333, "x": 1.078, "y": 1.157 },
+					{ "time": 0.4667, "x": 1.126, "y": 1.005 },
+					{ "time": 0.7, "x": 1.241, "y": 1.301 },
+					{ "time": 0.9, "x": 0.709, "y": 0.893 }
+				]
+			},
+			"smoke6": {
+				"rotate": [
+					{ "time": 0.1667, "value": -37.43 },
+					{ "time": 0.2333, "value": -18.36 },
+					{ "time": 0.3333, "value": 28.58 },
+					{ "time": 0.4, "value": 150.54 },
+					{ "time": 0.7, "value": 301.59 }
+				],
+				"translate": [
+					{ "time": 0.1333 },
+					{ "time": 0.2, "x": 68.04, "y": 16.15 },
+					{ "time": 0.2667, "x": 214.52, "y": 13.25 },
+					{ "time": 0.3333, "x": 285.4, "y": 17.95 },
+					{ "time": 0.4, "x": 202.91, "y": 101.43 },
+					{ "time": 0.4667, "x": 189.25, "y": 116.39 },
+					{ "time": 0.7, "x": 182.77, "y": 137.4 }
+				],
+				"scale": [
+					{ "time": 0.1333 },
+					{ "time": 0.1667, "x": 1.152, "y": 1.288 },
+					{ "time": 0.2, "x": 1.939, "y": 2.168 },
+					{ "time": 0.2333, "x": 2.278, "y": 2.223 },
+					{ "time": 0.2667, "x": 2.023, "y": 1.974 },
+					{ "time": 0.3, "x": 2.644, "y": 1.974 },
+					{ "time": 0.4, "x": 1.539, "y": 1.425 },
+					{ "time": 0.4667, "x": 1.14, "y": 0.939 },
+					{ "time": 0.7, "x": 0.215, "y": 0.161 }
+				]
+			},
+			"smoke7": {
+				"rotate": [
+					{ "time": 0.1667, "value": -243.11 },
+					{ "time": 0.4, "value": -182.02 },
+					{ "time": 0.8333, "value": -83.02 }
+				],
+				"translate": [
+					{ "time": 0.1333, "x": 3.19, "y": -6.53 },
+					{ "time": 0.1667, "x": 44.54, "y": 1.12 },
+					{ "time": 0.2, "x": 65.84, "y": 6.02 },
+					{ "time": 0.2333, "x": 173.84, "y": 97.51 },
+					{ "time": 0.4, "x": 167.39, "y": 74.58 },
+					{ "time": 0.8333, "x": 227.77, "y": 84.64 }
+				],
+				"scale": [
+					{ "time": 0.1333, "x": 0.878, "y": 0.878 },
+					{ "time": 0.1667, "x": 1.235, "y": 1.235 },
+					{ "time": 0.2, "x": 1.461, "y": 1.461 },
+					{ "time": 0.2333, "x": 1.114, "y": 1.114 },
+					{ "time": 0.3333, "x": 1.067, "y": 1.067 },
+					{ "time": 0.4667, "x": 0.81, "y": 0.753 },
+					{ "time": 0.8333, "x": 0.52, "y": 0.484 }
+				]
+			},
+			"smoke8": {
+				"rotate": [
+					{ "time": 0.1667, "value": -156.52 },
+					{ "time": 0.2667, "value": -154.05 },
+					{ "time": 0.3333, "value": -108.35 },
+					{ "time": 0.6, "value": -93.14 },
+					{ "time": 0.9333, "value": -70.89 }
+				],
+				"translate": [
+					{ "time": 0.1667, "x": 20.72, "y": 0.25 },
+					{ "time": 0.2333, "x": 46.1, "y": -10.06 },
+					{ "time": 0.3, "x": 149.77, "y": 0.92 },
+					{ "time": 0.3667, "x": 241.21, "y": 49.01 },
+					{ "time": 0.5333, "x": 276, "y": 58.76 },
+					{ "time": 0.7, "x": 292.02, "y": 65.91 },
+					{ "time": 0.9333, "x": 308.7, "y": 69.51 }
+				],
+				"scale": [
+					{ "time": 0.1333, "y": 1.174 },
+					{ "time": 0.1667, "x": 1.813, "y": 1.438 },
+					{ "time": 0.2, "x": 1.813, "y": 1.878 },
+					{ "time": 0.2333, "x": 1.211, "y": 1.878 },
+					{ "time": 0.2667, "x": 1.584, "y": 1.596 },
+					{ "time": 0.3, "x": 1.958, "y": 1.878 },
+					{ "time": 0.4667, "x": 1.139, "y": 0.958 },
+					{ "time": 0.9333, "x": 0.839, "y": 0.591 }
+				]
+			},
+			"smoke9": {
+				"rotate": [
+					{ "time": 0.1333, "value": -44.34 },
+					{ "time": 0.1667, "value": 14.73 },
+					{ "time": 0.2333, "value": 116.07 },
+					{ "time": 0.2667, "value": 118.29 },
+					{ "time": 0.3333, "value": 148.13 },
+					{ "time": 0.3667, "value": 172.74 },
+					{ "time": 0.4, "value": 235.69 },
+					{ "time": 0.4333, "value": 283.36 },
+					{ "time": 0.7667, "value": 358.76 }
+				],
+				"translate": [
+					{ "time": 0.1333, "x": -3.49, "y": 0.04 },
+					{ "time": 0.2, "x": 87.4, "y": -7.97 },
+					{ "time": 0.2667, "x": 233.69, "y": -33.86 },
+					{ "time": 0.3333, "x": 296.44, "y": -30.87 },
+					{ "time": 0.4, "x": 390.8, "y": 4 },
+					{ "time": 0.4667, "x": 391.42, "y": 13.17 },
+					{ "time": 0.6333, "x": 413.3, "y": 36.13 },
+					{ "time": 0.7667, "x": 408.59, "y": 40.75 }
+				],
+				"scale": [
+					{ "time": 0.1333, "x": 1.289, "y": 1.501 },
+					{ "time": 0.2, "x": 1.751, "y": 2.039 },
+					{ "time": 0.2667, "x": 2.064, "y": 2.347 },
+					{ "time": 0.3333, "x": 1.822, "y": 2.072 },
+					{ "time": 0.4, "x": 1.296, "y": 1.045 },
+					{ "time": 0.4667, "x": 1.872, "y": 1.526 },
+					{ "time": 0.6333, "x": 1.181, "y": 1.037 },
+					{ "time": 0.7667, "x": 0.716, "y": 0.615 }
+				]
+			},
+			"smoke10": {
+				"rotate": [
+					{ "time": 0.1333, "value": 12.16 },
+					{ "time": 0.2, "value": 49.19 },
+					{ "time": 0.2667, "value": 33.17 },
+					{ "time": 0.3333, "value": 42.23 },
+					{ "time": 0.4, "value": 11.69 },
+					{ "time": 0.4667, "value": 41.83 },
+					{ "time": 0.5333, "value": 54.86 },
+					{ "time": 0.6333, "value": 75.25 },
+					{ "time": 0.8333, "value": 126.4 }
+				],
+				"translate": [
+					{ "time": 0.1333, "x": 7.74, "y": 10.25 },
+					{ "time": 0.2, "x": 42.9, "y": 72.89 },
+					{ "time": 0.2667, "x": 221.58, "y": 82.27 },
+					{ "time": 0.3333, "x": 297.31, "y": 85.39 },
+					{ "time": 0.4, "x": 322.91, "y": 81.04 },
+					{ "time": 0.4667, "x": 346.62, "y": 76.68 },
+					{ "time": 0.6667, "x": 377.46, "y": 81.85 },
+					{ "time": 0.8333, "x": 402.18, "y": 101.03 }
+				],
+				"scale": [
+					{ "time": 0.1333, "x": 0.537, "y": 1.062 },
+					{ "time": 0.1667, "x": 1.042, "y": 0.841 },
+					{ "time": 0.2, "x": 1.937, "y": 1.563 },
+					{ "time": 0.2333, "x": 1.937, "y": 2.176 },
+					{ "time": 0.2667, "x": 2.254, "y": 2.532 },
+					{ "time": 0.3, "x": 2.24, "y": 2.516 },
+					{ "time": 0.5333, "x": 1.731, "y": 1.882 },
+					{ "time": 0.8333, "x": 0.855, "y": 0.867 }
+				]
+			},
+			"smoke-glow": {
+				"translate": [
+					{ "time": 0.0667, "x": -57.08, "y": 0.01 },
+					{ "time": 0.1, "x": -49.68, "y": -1.46 },
+					{ "time": 0.1333, "x": 6.3, "y": -2.92 },
+					{ "time": 0.1667, "x": 31.57, "y": 0.44 },
+					{ "time": 0.2, "x": 34.04, "y": 0.27 },
+					{ "time": 0.2333, "x": 109.29, "y": 1.02 },
+					{ "time": 0.4, "x": 119.89, "y": 1.01 },
+					{ "time": 0.4333, "x": 135.2, "y": 1.03 },
+					{ "time": 0.4667, "x": 152.86, "y": 1.06 },
+					{ "time": 0.5333, "x": 164.64, "y": 1.07 },
+					{ "time": 0.6, "x": 179.94, "y": 1.09 },
+					{ "time": 0.6333, "x": 190.54, "y": 1.1 }
+				],
+				"scale": [
+					{ "time": 0.0667, "x": 0.233, "y": 0.233 },
+					{ "time": 0.1, "x": 0.42, "y": 0.288 },
+					{ "time": 0.1333, "x": 1.669, "y": 1.072 },
+					{ "time": 0.1667, "x": 1.669, "y": 1.785, "curve": "stepped" },
+					{ "time": 0.2, "x": 1.669, "y": 1.785 },
+					{ "time": 0.2333, "x": 2.544, "y": 1.785 },
+					{ "time": 0.4333, "x": 3.48, "y": 2.22 },
+					{ "time": 0.4667, "x": 4.337, "y": 2.655 }
+				]
+			},
+			"smoke11": {
+				"rotate": [
+					{ "time": 0.4, "value": 47.07 },
+					{ "time": 0.4333, "value": 109.71 },
+					{ "time": 0.4667, "value": 164.62 },
+					{ "time": 0.8333, "value": 276.93 }
+				],
+				"translate": [
+					{ "time": 0.3333, "x": 280.31, "y": 126.85 },
+					{ "time": 0.4, "x": 296.27, "y": 125.62 },
+					{ "time": 0.4667, "x": 312.45, "y": 131.57 },
+					{ "time": 0.6667, "x": 310.5, "y": 149.67 },
+					{ "time": 0.8333, "x": 307.08, "y": 153.94 }
+				],
+				"scale": [
+					{ "time": 0.3333, "x": 1.491, "y": 1.491 },
+					{ "time": 0.4667, "x": 1.144, "y": 0.948 },
+					{ "time": 0.5667, "x": 0.491, "y": 0.491 },
+					{ "time": 0.8333, "x": 0.985, "y": 0.91 }
+				]
+			},
+			"smoke12": {
+				"rotate": [
+					{ "time": 0.3667, "value": -37.96 },
+					{ "time": 0.4333, "value": 28.55 },
+					{ "time": 0.5333, "value": 108.53 },
+					{ "time": 0.8667, "value": 191.85 }
+				],
+				"translate": [
+					{ "time": 0.3667, "x": 390.22, "y": -1.06 },
+					{ "time": 0.4333, "x": 411.78, "y": 26.39 },
+					{ "time": 0.5333, "x": 428.12, "y": 56.28 },
+					{ "time": 0.8667, "x": 444.34, "y": 68.06 }
+				],
+				"scale": [
+					{ "time": 0.3667, "x": 2.006, "y": 1.821 },
+					{ "time": 0.5333, "x": 1.719, "y": 1.293 },
+					{ "time": 0.7333, "x": 1.562, "y": 1.304 },
+					{ "time": 0.8667, "x": 0.727, "y": 0.637 }
+				]
+			},
+			"smoke13": {
+				"rotate": [
+					{ "time": 0.3667, "value": 305.8 },
+					{ "time": 0.4, "value": 478.49 },
+					{ "time": 0.4333, "value": 537.45 },
+					{ "time": 0.4667, "value": 573.84 },
+					{ "time": 0.5333, "value": 596.4 },
+					{ "time": 0.7, "value": 622.3 },
+					{ "time": 1, "value": 657.95 }
+				],
+				"translate": [
+					{ "time": 0.3667, "x": 331.84, "y": -25.82 },
+					{ "time": 0.4, "x": 417.88, "y": -42.62 },
+					{ "time": 0.4667, "x": 451.61, "y": -42.21 },
+					{ "time": 0.5333, "x": 453.81, "y": -37.03 },
+					{ "time": 0.6, "x": 451.86, "y": -31.89 },
+					{ "time": 0.7, "x": 453.37, "y": -27.28 },
+					{ "time": 1, "x": 454.04, "y": -17.89 }
+				],
+				"scale": [
+					{ "time": 0.3667, "x": 4.509, "y": 3.114 },
+					{ "time": 0.4, "x": 3.673, "y": 2.537 },
+					{ "time": 0.4333, "x": 4.201, "y": 2.638 },
+					{ "time": 0.4667, "x": 4.27, "y": 2.399 },
+					{ "time": 0.6, "x": 2.798, "y": 1.932 },
+					{ "time": 0.8333, "x": 2.316, "y": 1.599 },
+					{ "time": 1, "x": 1.081, "y": 0.746 }
+				]
+			},
+			"smoke14": {
+				"rotate": [
+					{ "time": 0.4333, "value": 271.03 },
+					{ "time": 0.7, "value": 299.97 },
+					{ "time": 1.0667, "value": 331.16 }
+				],
+				"translate": [
+					{ "time": 0.4333, "x": 371.68, "y": -29.8 },
+					{ "time": 0.7667, "x": 400.59, "y": -44.36 },
+					{ "time": 1.0667, "x": 432.26, "y": -44.79 }
+				],
+				"scale": [
+					{ "time": 0.4333, "x": 4.011, "y": 3.366 },
+					{ "time": 0.7667, "x": 2.071, "y": 1.624 },
+					{ "time": 1.0667, "x": 1.798, "y": 1.111 }
+				]
+			},
+			"smoke15": {
+				"rotate": [
+					{ "time": 0.4, "value": 111.75 },
+					{ "time": 0.4667, "value": 171.93 },
+					{ "time": 0.6, "value": 256.95 },
+					{ "time": 0.8333, "value": 299.15 }
+				],
+				"translate": [
+					{ "time": 0.4, "x": 266.71, "y": -53.04 },
+					{ "time": 0.4333, "x": 290.84, "y": -51.43 },
+					{ "time": 0.5333, "x": 305.65, "y": -44.32 },
+					{ "time": 0.6667, "x": 318.96, "y": -38.95 },
+					{ "time": 0.8333, "x": 342.65, "y": -27.33 }
+				],
+				"scale": [
+					{ "time": 0.4, "x": 2.749, "y": 2.095 },
+					{ "time": 0.4333, "x": 3.302, "y": 2.289 },
+					{ "time": 0.4667, "x": 2.591, "y": 1.895 },
+					{ "time": 0.5333, "x": 1.777, "y": 1.354 },
+					{ "time": 0.7, "x": 1.932, "y": 1.267 },
+					{ "time": 0.8333, "x": 1.002, "y": 1.546 }
+				]
+			},
+			"smoke16": {
+				"rotate": [
+					{ "time": 0.4, "value": 89.78 },
+					{ "time": 0.4667, "value": 137.83 },
+					{ "time": 0.5333, "value": 193.49 },
+					{ "time": 0.6, "value": 235.26 },
+					{ "time": 0.6333, "value": 286.8 }
+				],
+				"translate": [
+					{ "time": 0.4, "x": 217.23, "y": -21.45 },
+					{ "time": 0.4667, "x": 249.95, "y": -13.73 },
+					{ "time": 0.5333, "x": 264.96, "y": -9.87 },
+					{ "time": 0.6, "x": 278.95, "y": 6.37 },
+					{ "time": 0.6333, "x": 245.65, "y": 11.77 }
+				],
+				"scale": [
+					{ "time": 0.4, "x": 2.265, "y": 1.859 },
+					{ "time": 0.4333, "x": 2.621, "y": 1.955 },
+					{ "time": 0.4667, "x": 1.953, "y": 1.538 },
+					{ "time": 0.6, "x": 1.005, "y": 0.825 },
+					{ "time": 0.6333, "x": 0.387, "y": 0.318 }
+				]
+			},
+			"smoke17": {
+				"rotate": [
+					{ "time": 0.2333, "value": 99.02 },
+					{ "time": 0.3, "value": 58.06 },
+					{ "time": 0.3333, "value": 34.05 },
+					{ "time": 0.3667, "value": -17.34 },
+					{ "time": 0.6667, "value": -62.36 }
+				],
+				"translate": [
+					{ "time": 0.2333, "x": 18.91, "y": -62.91 },
+					{ "time": 0.3, "x": 2.43, "y": -61.54 },
+					{ "time": 0.3333, "x": 1.89, "y": -36.55 },
+					{ "time": 0.3667, "x": 6.97, "y": -29.52 },
+					{ "time": 0.4333, "x": 10.78, "y": -20.55 },
+					{ "time": 0.6667, "x": 18.65, "y": -13.19 }
+				],
+				"scale": [
+					{ "time": 0.2333, "x": 1.915, "y": 1.915 },
+					{ "time": 0.3, "x": 1.509, "y": 1.509 },
+					{ "time": 0.3333, "x": 1.01, "y": 1.01 },
+					{ "time": 0.3667, "x": 0.715, "y": 0.715 },
+					{ "time": 0.4333, "x": 0.949, "y": 0.721 },
+					{ "time": 0.5667, "x": 0.785, "y": 0.74 }
+				]
+			},
+			"smoke18": {
+				"rotate": [
+					{ "time": 0.2333, "value": 141.75 },
+					{ "time": 0.2667, "value": 134.51 },
+					{ "time": 0.3333, "value": 249.12 },
+					{ "time": 0.5, "value": 363.82 },
+					{ "time": 0.7333, "value": 450.54 }
+				],
+				"translate": [
+					{ "time": 0.2333, "x": 60.81, "y": 56.17 },
+					{ "time": 0.2667, "x": 68.74, "y": 69.4 },
+					{ "time": 0.3333, "x": 76.85, "y": 69.07 },
+					{ "time": 0.5, "x": 101.49, "y": 89.87 },
+					{ "time": 0.7333, "x": 118.58, "y": 101.16 }
+				],
+				"scale": [
+					{ "time": 0.2333, "x": 2.288, "y": 2.288 },
+					{ "time": 0.2667, "x": 2.288, "y": 1.628 },
+					{ "time": 0.3, "x": 1.524, "y": 1.308 },
+					{ "time": 0.5, "x": 1.757, "y": 1.385 },
+					{ "time": 0.5333, "x": 2.08, "y": 1.51 },
+					{ "time": 0.7333, "x": 1.405, "y": 0.896 }
+				]
+			},
+			"smoke20": {
+				"rotate": [
+					{ "time": 0.3333, "value": 95.16 },
+					{ "time": 0.3667, "value": 130.42 },
+					{ "time": 0.4, "value": 170.7 },
+					{ "time": 0.4333, "value": 266.75 },
+					{ "time": 0.4667, "value": 299.82 },
+					{ "time": 0.5333, "value": 326.88 },
+					{ "time": 0.6, "value": 350.8 },
+					{ "time": 0.9, "value": 403.14 }
+				],
+				"translate": [
+					{ "time": 0.3333, "x": 124.61, "y": -46.55 },
+					{ "time": 0.5333, "x": 173.8, "y": -36.62 },
+					{ "time": 0.7, "x": 186.5, "y": -35.41 },
+					{ "time": 0.9, "x": 188.56, "y": -37.75 }
+				],
+				"scale": [
+					{ "time": 0.3333, "x": 3.346, "y": 2.654 },
+					{ "time": 0.3667, "x": 2.661, "y": 2.111 },
+					{ "time": 0.4333, "x": 2.751, "y": 1.984 },
+					{ "time": 0.4667, "x": 3.059, "y": 2.21 },
+					{ "time": 0.5333, "x": 2.159, "y": 1.712 },
+					{ "time": 0.7, "x": 1.601, "y": 1.27 },
+					{ "time": 0.9, "x": 1.679, "y": 0.856 }
+				]
+			},
+			"smoke23": {
+				"rotate": [
+					{ "time": 0.3, "value": 115.12 },
+					{ "time": 0.3667, "value": 79.01 },
+					{ "time": 0.7667, "value": 6.96 }
+				],
+				"translate": [
+					{ "time": 0.3, "x": 75.15, "y": -50.92 },
+					{ "time": 0.3667, "x": 59.33, "y": -53.52 },
+					{ "time": 0.7667, "x": 39.68, "y": -48.64 }
+				],
+				"scale": [
+					{ "time": 0.3, "x": 3.331, "y": 2.096 },
+					{ "time": 0.4333, "x": 2.4, "y": 2.006 },
+					{ "time": 0.5, "x": 2.555, "y": 2.094 },
+					{ "time": 0.7667, "x": 1.35, "y": 1.241 }
+				]
+			},
+			"antenna1": {
+				"rotate": [
+					{ "time": 0.0667 },
+					{ "time": 0.2, "value": 11.78 },
+					{ "time": 0.3, "value": -9.52 },
+					{ "time": 0.4, "value": 8.07 },
+					{ "time": 0.5, "value": -4.45 },
+					{ "time": 0.6, "value": 1.54 },
+					{ "time": 0.7, "value": -0.34 }
+				]
+			},
+			"antenna2": {
+				"rotate": [
+					{ "time": 0.0667 },
+					{ "time": 0.2, "value": 11.78 },
+					{ "time": 0.3, "value": -9.52 },
+					{ "time": 0.4, "value": 8.07 },
+					{ "time": 0.5, "value": -4.45 },
+					{ "time": 0.6, "value": 1.54 },
+					{ "time": 0.7, "value": -0.34 }
+				]
+			},
+			"antenna3": {
+				"rotate": [
+					{ "time": 0.0667 },
+					{ "time": 0.2, "value": 11.78 },
+					{ "time": 0.3, "value": -9.52 },
+					{ "time": 0.4, "value": 8.07 },
+					{ "time": 0.5, "value": -4.45 },
+					{ "time": 0.6, "value": 1.54 },
+					{ "time": 0.7, "value": -0.34 }
+				]
+			},
+			"antenna4": {
+				"rotate": [
+					{ "time": 0.0667 },
+					{ "time": 0.2, "value": 11.78 },
+					{ "time": 0.3, "value": -9.52 },
+					{ "time": 0.4, "value": 8.07 },
+					{ "time": 0.5, "value": -4.45 },
+					{ "time": 0.6, "value": 1.54 },
+					{ "time": 0.7, "value": -0.34 }
+				]
+			},
+			"antenna5": {
+				"rotate": [
+					{ "time": 0.0667 },
+					{ "time": 0.2, "value": 11.78 },
+					{ "time": 0.3, "value": -9.52 },
+					{ "time": 0.4, "value": 8.07 },
+					{ "time": 0.5, "value": -4.45 },
+					{ "time": 0.6, "value": 1.54 },
+					{ "time": 0.7, "value": -0.34 }
+				]
+			},
+			"antenna6": {
+				"rotate": [
+					{ "time": 0.0667 },
+					{ "time": 0.2, "value": 11.78 },
+					{ "time": 0.3, "value": -9.52 },
+					{ "time": 0.4, "value": 8.07 },
+					{ "time": 0.5, "value": -4.45 },
+					{ "time": 0.6, "value": 1.54 },
+					{ "time": 0.7, "value": -0.34 }
+				]
+			},
+			"smoke24": {
+				"rotate": [
+					{ "time": 0.3, "value": 71.32 },
+					{ "time": 0.3667, "value": 112.39 },
+					{ "time": 0.4667, "value": 159.56 },
+					{ "time": 0.7, "value": 224.21 }
+				],
+				"translate": [
+					{ "time": 0.3, "x": 90.72, "y": -18.79 },
+					{ "time": 0.3667, "x": 149.69, "y": -7.78 },
+					{ "time": 0.4667, "x": 176.26, "y": 12.31 },
+					{ "time": 0.7, "x": 184.07, "y": 31.75 }
+				],
+				"scale": [
+					{ "time": 0.3, "x": 2.906, "y": 2.311 },
+					{ "time": 0.4333, "x": 3.567, "y": 2.58 },
+					{ "time": 0.4667, "x": 3.157, "y": 2.41 },
+					{ "time": 0.7, "x": 1.705, "y": 1.356 }
+				]
+			},
+			"smoke25": {
+				"rotate": [
+					{ "time": 0.3667, "value": 91.25 },
+					{ "time": 0.4333, "value": 117.56 },
+					{ "time": 0.6333, "value": 150.9 },
+					{ "time": 1, "value": 189.47 }
+				],
+				"translate": [
+					{ "time": 0.3667, "x": 187.21, "y": -51.18 },
+					{ "time": 0.5333, "x": 245.48, "y": -46.28 },
+					{ "time": 0.6667, "x": 277.36, "y": -43.12 },
+					{ "time": 1, "x": 313.27, "y": -38.14 }
+				],
+				"scale": [
+					{ "time": 0.3667, "x": 3.606, "y": 2.657 },
+					{ "time": 0.4333, "x": 4.166, "y": 2.792 },
+					{ "time": 0.5333, "x": 3.09, "y": 2.091 },
+					{ "time": 1, "x": 3.062, "y": 1.801 }
+				]
+			},
+			"smoke26": {
+				"rotate": [
+					{ "time": 0.3667, "value": 10.64 },
+					{ "time": 0.4, "value": 60.85 },
+					{ "time": 0.4667, "value": 89.45 },
+					{ "time": 0.7, "value": 125.01 },
+					{ "time": 0.9333, "value": 155.24 }
+				],
+				"translate": [
+					{ "time": 0.3667, "x": 442.07, "y": -13.19 },
+					{ "time": 0.4, "x": 453.7, "y": 0.81 },
+					{ "time": 0.4667, "x": 443.57, "y": -6.95 },
+					{ "time": 0.7, "x": 460.97, "y": 15.79 },
+					{ "time": 0.9333, "x": 465.22, "y": 20.92 }
+				],
+				"scale": [
+					{ "time": 0.3667, "x": 2.726, "y": 2.726 },
+					{ "time": 0.4333, "x": 3.729, "y": 2.822 },
+					{ "time": 0.4667, "x": 3.398, "y": 2.441 },
+					{ "time": 0.7, "x": 4.324, "y": 3.159 },
+					{ "time": 0.9, "x": 1.977, "y": 1.48 }
+				]
+			},
+			"smoke27": {
+				"rotate": [
+					{ "time": 0.3667, "value": 24.75 },
+					{ "time": 0.4333, "value": -5.43 },
+					{ "time": 0.5333, "value": -39.76 },
+					{ "time": 0.8333, "value": -56.25 }
+				],
+				"translate": [
+					{ "time": 0.3667, "x": 92.98, "y": -49.06 },
+					{ "time": 0.5333, "x": 129.81, "y": -33.09 },
+					{ "time": 0.8333, "x": 143.68, "y": -25.27 }
+				],
+				"scale": [
+					{ "time": 0.3667, "x": 3.633, "y": 2.223 },
+					{ "time": 0.4333, "x": 2.745, "y": 2.283 },
+					{ "time": 0.4667, "x": 2.962, "y": 2.122 },
+					{ "time": 0.5333, "x": 2.007, "y": 1.266 }
+				]
+			},
+			"cannon-target": {
+				"translate": [
+					{ "time": 0.1333 },
+					{
+						"time": 0.2,
+						"y": 128.38,
+						"curve": [ 0.4, 0, 0.8, 0, 0.4, 128.38, 0.8, 0 ]
+					},
+					{ "time": 1 }
+				],
+				"scale": [
+					{ "time": 0.4333, "x": 0.632, "y": 1.244 },
+					{ "time": 0.4667, "x": 0.477, "y": 1.487 }
+				]
+			},
+			"machinegun-target": {
+				"scale": [
+					{ "time": 0.4333, "x": 0.632, "y": 1.244 },
+					{ "time": 0.4667, "x": 0.477, "y": 1.487 }
+				]
+			},
+			"machinegun": {
+				"rotate": [
+					{ "value": 8.07, "curve": "stepped" },
+					{ "time": 0.0667, "value": 8.07 },
+					{
+						"time": 0.2333,
+						"value": -18.67,
+						"curve": [ 0.894, -18.44, 0.832, 7.5 ]
+					},
+					{ "time": 0.9, "value": 8.07 }
+				]
+			},
+			"tank-root": {
+				"translate": [
+					{ "time": 0.0667 },
+					{
+						"time": 0.1667,
+						"x": 46.59,
+						"curve": [ 0.192, 46.59, 0.242, 0, 0.192, 0, 0.242, 0 ]
+					},
+					{ "time": 0.2667 }
+				]
+			},
+			"tank-glow": {
+				"translate": [
+					{
+						"time": 0.1333,
+						"x": 198.14,
+						"curve": [ 0.199, 190.76, 0.222, -255.89, 0.199, 0, 0.222, 0 ]
+					},
+					{ "time": 0.2333, "x": -390 }
+				],
+				"scale": [
+					{ "time": 0.0667 },
+					{
+						"time": 0.1333,
+						"x": 1.185,
+						"y": 0.945,
+						"curve": [ 0.199, 1.182, 0.222, 1.048, 0.199, 0.939, 0.222, 0.579 ]
+					},
+					{ "time": 0.2333, "x": 1.008, "y": 0.471 }
+				]
+			}
+		},
+		"attachments": {
+			"default": {
+				"clipping": {
+					"clipping": {
+						"deform": [
+							{
+								"time": 0.0667,
+								"offset": 54,
+								"vertices": [ 4.59198, -4.59192 ]
+							},
+							{
+								"time": 0.1333,
+								"offset": 8,
+								"vertices": [ -8.97369, -1.88211, 9.11177, 1.02258, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -14.73321, -45.16878, -30.31448, -84.4631, -32.24969, -108.78421, 70.26825, -36.90201 ]
+							},
+							{
+								"time": 0.1667,
+								"offset": 8,
+								"vertices": [ -11.32373, -1.65065, 11.42179, 0.53259, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -15.36503, -69.18713, -4.45626, -121.90839, 5.46554, -115.23274, 71.78526, -33.85687 ]
+							},
+							{
+								"time": 0.2,
+								"offset": 8,
+								"vertices": [ -8.70522, 1.02196, 8.65102, -1.4101, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4.59198, -4.59192 ]
+							},
+							{
+								"time": 0.2333,
+								"offset": 8,
+								"vertices": [ -5.23146, 0.85796, 5.23882, -0.81519 ]
+							},
+							{
+								"time": 0.2667,
+								"offset": 54,
+								"vertices": [ 4.59198, -4.59192 ]
+							}
+						]
+					}
+				},
+				"smoke-glow": {
+					"smoke-glow": {
+						"deform": [
+							{
+								"time": 0.1333,
+								"vertices": [ -14.17073, 19.14352, 0, 0, -10.97961, -15.09065, -5.79558, -24.82121, 0.68117, -17.78759, -1.1179, -5.4463, 0, 0, 0, 0, 17.52957, 6.89397, -0.33841, -2.21582, 5.51004, 18.88118, -6.80153, 20.91101 ]
+							},
+							{
+								"time": 0.1667,
+								"vertices": [ -4.34264, 39.78125, 5.6649, -2.42686, -8.39346, -22.52338, -2.66431, 5.08595, -19.28093, 3.98568, -11.21397, 10.2879, 4.56749, 4.1329, -19.50706, -2.28786, 11.35747, 4.55941, 9.04341, -11.72194, 2.15381, 5.14344, -12.82158, 16.08209, -23.19814, 1.81836 ]
+							},
+							{
+								"time": 0.2,
+								"vertices": [ -3.95581, 36.12203, 37.20779, -0.87419, 21.29579, -15.76854, -2.02438, 6.16526, -5.92201, 4.19709, -1.39027, 9.92793, 7.70584, -0.7169, -6.69733, -2.62048, 17.91826, 7.77333, -12.2858, 3.25454, -12.75876, 3.71516, 9.67891, 15.48546 ]
+							},
+							{
+								"time": 0.2333,
+								"vertices": [ -11.9371, 26.01078, 2.91821, -0.27533, 7.69899, -17.45375, -2.02438, 6.16526, -5.92201, 4.19709, -1.39027, 9.92793, 7.70584, -0.7169, -6.69733, -2.62048, 17.91826, 7.77333, -4.30551, -6.01406, -12.75876, 3.71516, -5.10017, 17.59191 ]
+							},
+							{
+								"time": 0.2667,
+								"vertices": [ 0.5959, 23.58176, 20.74303, 0.93943, 7.69899, -17.45375, -2.02438, 6.16526, -5.92201, 4.19709, -1.39027, 9.92793, 20.51733, 2.52203, 13.35544, 2.64274, 24.32408, -1.94308, 8.50604, -20.99353, 13.14276, 5.73959, 6.31876, 19.2114, 16.98909, 0.80981 ]
+							}
+						]
+					}
+				}
+			}
+		},
+		"drawOrder": [
+			{
+				"time": 0.3,
+				"offsets": [
+					{ "slot": "smoke-puff1-bg2", "offset": 24 },
+					{ "slot": "smoke-puff1-bg8", "offset": 19 },
+					{ "slot": "smoke-puff1-bg9", "offset": 22 },
+					{ "slot": "smoke-puff1-bg3", "offset": 17 },
+					{ "slot": "smoke-puff1-fg17", "offset": 13 },
+					{ "slot": "smoke-puff1-fg2", "offset": 2 },
+					{ "slot": "smoke-puff1-fg5", "offset": 8 },
+					{ "slot": "smoke-puff1-fg6", "offset": 4 },
+					{ "slot": "smoke-puff1-fg7", "offset": -4 },
+					{ "slot": "smoke-puff1-fg4", "offset": -4 }
+				]
+			},
+			{
+				"time": 0.3333,
+				"offsets": [
+					{ "slot": "smoke-puff1-bg2", "offset": 8 },
+					{ "slot": "smoke-puff1-bg8", "offset": 5 },
+					{ "slot": "smoke-puff1-bg9", "offset": 3 },
+					{ "slot": "smoke-puff1-fg", "offset": 24 },
+					{ "slot": "smoke-puff1-fg5", "offset": -14 },
+					{ "slot": "smoke-puff1-fg6", "offset": -21 },
+					{ "slot": "smoke-puff1-fg7", "offset": -19 },
+					{ "slot": "smoke-puff1-fg10", "offset": -21 }
+				]
+			},
+			{
+				"time": 0.3667,
+				"offsets": [
+					{ "slot": "smoke-puff1-bg2", "offset": 7 },
+					{ "slot": "smoke-puff1-bg9", "offset": 4 },
+					{ "slot": "smoke-puff1-fg", "offset": 24 },
+					{ "slot": "smoke-puff1-fg2", "offset": 5 },
+					{ "slot": "smoke-puff1-fg6", "offset": -22 },
+					{ "slot": "smoke-puff1-fg7", "offset": -18 },
+					{ "slot": "smoke-puff1-fg10", "offset": -20 }
+				]
+			},
+			{
+				"time": 0.4,
+				"offsets": [
+					{ "slot": "smoke-puff1-bg2", "offset": 5 },
+					{ "slot": "smoke-puff1-bg4", "offset": 0 },
+					{ "slot": "smoke-puff1-fg", "offset": 24 },
+					{ "slot": "smoke-puff1-fg2", "offset": 5 },
+					{ "slot": "smoke-puff1-fg6", "offset": -21 },
+					{ "slot": "smoke-puff1-fg7", "offset": -18 },
+					{ "slot": "smoke-puff1-fg10", "offset": -22 }
+				]
+			},
+			{
+				"time": 0.4333,
+				"offsets": [
+					{ "slot": "smoke-puff1-bg2", "offset": 4 },
+					{ "slot": "smoke-puff1-bg9", "offset": 4 },
+					{ "slot": "smoke-puff1-fg", "offset": 24 },
+					{ "slot": "smoke-puff1-fg2", "offset": 5 },
+					{ "slot": "smoke-puff1-fg6", "offset": -17 },
+					{ "slot": "smoke-puff1-fg7", "offset": -19 },
+					{ "slot": "smoke-puff1-fg10", "offset": -23 }
+				]
+			},
+			{
+				"time": 0.5333,
+				"offsets": [
+					{ "slot": "smoke-puff1-bg2", "offset": 9 },
+					{ "slot": "smoke-puff1-bg12", "offset": 0 },
+					{ "slot": "smoke-puff1-fg", "offset": 24 },
+					{ "slot": "smoke-puff1-fg2", "offset": 6 },
+					{ "slot": "smoke-puff1-fg6", "offset": -20 },
+					{ "slot": "smoke-puff1-fg7", "offset": -19 },
+					{ "slot": "smoke-puff1-fg10", "offset": -23 },
+					{ "slot": "smoke-puff1-fg4", "offset": -5 }
+				]
+			}
+		]
+	}
+}
+}

BIN
spine-haxe/example/assets/tank-pro.skel


+ 56 - 0
spine-haxe/example/assets/tank.atlas

@@ -0,0 +1,56 @@
+tank.png
+	size: 1024, 1024
+	filter: Linear, Linear
+	scale: 0.5
+antenna
+	bounds: 2, 712, 11, 152
+	rotate: 90
+cannon
+	bounds: 222, 694, 466, 29
+cannon-connector
+	bounds: 82, 654, 56, 68
+	rotate: 90
+ground
+	bounds: 2, 175, 512, 177
+guntower
+	bounds: 647, 347, 365, 145
+machinegun
+	bounds: 690, 694, 166, 29
+machinegun-mount
+	bounds: 858, 687, 36, 48
+	rotate: 90
+rock
+	bounds: 664, 607, 290, 64
+smoke-glow
+	bounds: 946, 673, 50, 50
+smoke-puff01-bg
+	bounds: 312, 630, 92, 62
+smoke-puff01-fg
+	bounds: 222, 633, 88, 59
+smoke-puff02-fg
+	bounds: 406, 630, 92, 62
+smoke-puff03-fg
+	bounds: 500, 630, 92, 62
+smoke-puff04-fg
+	bounds: 2, 662, 78, 48
+tank-bottom
+	bounds: 2, 354, 643, 138
+tank-bottom-shadow
+	bounds: 2, 2, 646, 171
+tank-top
+	bounds: 195, 494, 704, 111
+tread
+	bounds: 172, 708, 48, 15
+tread-inside
+	bounds: 156, 710, 13, 14
+	rotate: 90
+wheel-big
+	bounds: 97, 540, 96, 96
+wheel-big-overlay
+	bounds: 2, 559, 93, 93
+wheel-mid
+	bounds: 594, 624, 68, 68
+wheel-mid-overlay
+	bounds: 152, 638, 68, 68
+wheel-small
+	bounds: 908, 687, 36, 36

BIN
spine-haxe/example/assets/tank.png


La diferencia del archivo ha sido suprimido porque es demasiado grande
+ 156 - 0
spine-haxe/example/assets/vine-pro.json


BIN
spine-haxe/example/assets/vine-pro.skel


+ 5 - 0
spine-haxe/example/assets/vine.atlas

@@ -0,0 +1,5 @@
+vine.png
+	size: 128, 1024
+	filter: Linear, Linear
+vine
+	bounds: 2, 2, 68, 962

BIN
spine-haxe/example/assets/vine.png


+ 77 - 0
spine-haxe/example/src/Main.hx

@@ -0,0 +1,77 @@
+package;
+
+import starling.display.Image;
+import haxe.io.Bytes;
+import openfl.display.Bitmap;
+import openfl.display.BitmapData;
+import openfl.display.Sprite;
+import openfl.Assets;
+import openfl.geom.Rectangle;
+import openfl.utils.ByteArray;
+import openfl.utils.Endian;
+import spine.animation.AnimationStateData;
+import spine.atlas.Atlas;
+import spine.attachments.AtlasAttachmentLoader;
+import spine.SkeletonBinary;
+import spine.SkeletonData;
+import spine.SkeletonJson;
+import spine.starling.SkeletonAnimation;
+import spine.starling.StarlingTextureLoader;
+import starling.core.Starling;
+import starling.events.Event;
+import starling.textures.Texture;
+
+class Main extends Sprite {
+	private static inline var loadBinary:Bool = true;
+
+	private var starlingSingleton:Starling;
+
+	public function new() {
+		super();
+
+		starlingSingleton = new Starling(starling.display.Sprite, stage, new Rectangle(0, 0, 800, 600));
+		starlingSingleton.supportHighResolutions = true;
+		starlingSingleton.addEventListener(Event.ROOT_CREATED, onStarlingRootCreated);
+	}
+
+	private function onStarlingRootCreated(event:Event):Void {
+		starlingSingleton.removeEventListener(Event.ROOT_CREATED, onStarlingRootCreated);
+		starlingSingleton.start();
+		Starling.current.stage.color = 0x000000;
+
+		loadSpineAnimation();
+	}
+
+	private function loadSpineAnimation():Void {
+		var textureAtlasBitmapData:BitmapData = Assets.getBitmapData("assets/coin.png");
+		var stAtlas:String = Assets.getText("assets/coin.atlas");
+		var binaryData:Bytes = Assets.getBytes("assets/coin-pro.skel");
+		var jsonData:String = Assets.getText("assets/coin-pro.json");
+
+		var textureAtlas:Texture = Texture.fromBitmapData(textureAtlasBitmapData);
+		var textureloader:StarlingTextureLoader = new StarlingTextureLoader(textureAtlas);
+		var atlas:Atlas = new Atlas(stAtlas, textureloader);
+
+		var skeletondata:SkeletonData;
+		if (loadBinary) {
+			var skeletonBinary:SkeletonBinary = new SkeletonBinary(new AtlasAttachmentLoader(atlas));
+			var bytearray:ByteArray = ByteArray.fromBytes(binaryData);
+			bytearray.endian = Endian.BIG_ENDIAN;
+			skeletondata = skeletonBinary.readSkeletonData(bytearray);
+		} else {
+			var skeletonJson:SkeletonJson = new SkeletonJson(new AtlasAttachmentLoader(atlas));
+			skeletondata = skeletonJson.readSkeletonData(jsonData);
+		}
+
+		var stateData:AnimationStateData = new AnimationStateData(skeletondata);
+		stateData.defaultMix = 0.25;
+
+		var skeletonanimation:SkeletonAnimation = new SkeletonAnimation(skeletondata, stateData);
+		skeletonanimation.x = Starling.current.stage.stageWidth / 2;
+		skeletonanimation.y = Starling.current.stage.stageHeight * 0.5;
+
+		Starling.current.stage.addChild(skeletonanimation);
+		Starling.current.juggler.add(skeletonanimation);
+		skeletonanimation.state.setAnimationByName(0, "animation", true);
+	}
+}

+ 27 - 0
spine-haxe/haxelib.json

@@ -0,0 +1,27 @@
+{
+   "name": "spine-haxe",
+   "url": "https://github.com/esotericsoftware/spine-runtimes",
+   "license": "Spine Runtime License",
+   "tags": [
+      "spine",
+      "2d-animation",
+      "skeletal-animation",
+      "starling",
+      "2d",
+      "gpu",
+      "openfl",
+      "flash",
+      "air",
+      "js",
+      "web",
+      "cpp"
+   ],
+   "description": "The official Spine Runtime for Haxe",
+   "version": "4.1.0",
+   "releasenote": "Initial release",
+   "contributors": [
+      "esotericsoftware"
+   ],
+   "dependencies": {},
+   "classPath": "spine-haxe"
+}

+ 15 - 0
spine-haxe/project.xml

@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="utf-8"?>
+<project>
+
+	<meta title="spine-haxe-example" package="spine" version="4.1.0" company="Esoteric Software" />
+	<app main="Main" path="export" file="SpineHaxeExample" />
+	<window allow-high-dpi="true" />
+
+	<haxelib name="openfl" />
+	<haxelib name="starling" />
+	<haxelib name="spine-haxe" />
+
+	<source path="example/src" />
+	<assets path="example/assets" rename="assets" />
+
+</project>

+ 93 - 0
spine-haxe/spine-haxe/spine/BinaryInput.hx

@@ -0,0 +1,93 @@
+package spine;
+
+import openfl.utils.ByteArray;
+import openfl.Vector;
+
+class BinaryInput {
+	private var bytes:ByteArray;
+
+	public var strings:Vector<String> = new Vector<String>();
+
+	public function new(bytes:ByteArray) {
+		this.bytes = bytes;
+	}
+
+	public function readByte():Int {
+		return bytes.readByte();
+	}
+
+	public function readUnsignedByte():Int {
+		return bytes.readUnsignedByte();
+	}
+
+	public function readShort():Int {
+		return bytes.readShort();
+	}
+
+	public function readInt32():Int {
+		return bytes.readInt();
+	}
+
+	public function readInt(optimizePositive:Bool):Int {
+		var b:Int = readByte();
+		var result:Int = b & 0x7F;
+		if ((b & 0x80) != 0) {
+			b = readByte();
+			result |= (b & 0x7F) << 7;
+			if ((b & 0x80) != 0) {
+				b = readByte();
+				result |= (b & 0x7F) << 14;
+				if ((b & 0x80) != 0) {
+					b = readByte();
+					result |= (b & 0x7F) << 21;
+					if ((b & 0x80) != 0) {
+						b = readByte();
+						result |= (b & 0x7F) << 28;
+					}
+				}
+			}
+		}
+		return optimizePositive ? result : ((result >>> 1) ^ -(result & 1));
+	}
+
+	public function readStringRef():String {
+		var index:Int = readInt(true);
+		return index == 0 ? null : strings[index - 1];
+	}
+
+	public function readString():String {
+		var byteCount:Int = readInt(true);
+		switch (byteCount) {
+			case 0:
+				return null;
+			case 1:
+				return "";
+		}
+		byteCount--;
+		var chars:String = "";
+		var i:Int = 0;
+		while (i < byteCount) {
+			var b:Int = readByte();
+			switch (b >> 4) {
+				case 12, 13:
+					chars += String.fromCharCode(((b & 0x1F) << 6 | readByte() & 0x3F));
+					i += 2;
+				case 14:
+					chars += String.fromCharCode(((b & 0x0F) << 12 | (readByte() & 0x3F) << 6 | readByte() & 0x3F));
+					i += 3;
+				default:
+					chars += String.fromCharCode(b);
+					i++;
+			}
+		}
+		return chars;
+	}
+
+	public function readFloat():Float {
+		return bytes.readFloat();
+	}
+
+	public function readBoolean():Bool {
+		return this.readByte() != 0;
+	}
+}

+ 28 - 0
spine-haxe/spine-haxe/spine/BlendMode.hx

@@ -0,0 +1,28 @@
+package spine;
+
+import openfl.Vector;
+
+class BlendMode {
+	public static var normal(default, never):BlendMode = new BlendMode(0, "normal");
+	public static var additive(default, never):BlendMode = new BlendMode(1, "additive");
+	public static var multiply(default, never):BlendMode = new BlendMode(2, "multiply");
+	public static var screen(default, never):BlendMode = new BlendMode(3, "screen");
+
+	public static var values(default, never):Vector<BlendMode> = Vector.ofArray([normal, additive, multiply, screen]);
+
+	public var ordinal(default, null):Int;
+	public var name(default, null):String;
+
+	public function new(ordinal:Int, name:String) {
+		this.ordinal = ordinal;
+		this.name = name;
+	}
+
+	public static function fromName(name:String):BlendMode {
+		for (value in values) {
+			if (value.name == name)
+				return value;
+		}
+		return null;
+	}
+}

+ 326 - 0
spine-haxe/spine-haxe/spine/Bone.hx

@@ -0,0 +1,326 @@
+package spine;
+
+import openfl.errors.ArgumentError;
+import openfl.Vector;
+
+class Bone implements Updatable {
+	static public var yDown:Bool = false;
+
+	private var _data:BoneData;
+	private var _skeleton:Skeleton;
+	private var _parent:Bone;
+	private var _children:Vector<Bone> = new Vector<Bone>();
+
+	public var x:Float = 0;
+	public var y:Float = 0;
+	public var rotation:Float = 0;
+	public var scaleX:Float = 0;
+	public var scaleY:Float = 0;
+	public var shearX:Float = 0;
+	public var shearY:Float = 0;
+	public var ax:Float = 0;
+	public var ay:Float = 0;
+	public var arotation:Float = 0;
+	public var ascaleX:Float = 0;
+	public var ascaleY:Float = 0;
+	public var ashearX:Float = 0;
+	public var ashearY:Float = 0;
+	public var a:Float = 0;
+	public var b:Float = 0;
+	public var c:Float = 0;
+	public var d:Float = 0;
+	public var worldX:Float = 0;
+	public var worldY:Float = 0;
+	public var sorted:Bool = false;
+	public var active:Bool = false;
+
+	/** @param parent May be null. */
+	public function new(data:BoneData, skeleton:Skeleton, parent:Bone) {
+		if (data == null)
+			throw new ArgumentError("data cannot be null.");
+		if (skeleton == null)
+			throw new ArgumentError("skeleton cannot be null.");
+		_data = data;
+		_skeleton = skeleton;
+		_parent = parent;
+		setToSetupPose();
+	}
+
+	public function isActive():Bool {
+		return active;
+	}
+
+	/** Same as updateWorldTransform(). This method exists for Bone to implement Updatable. */
+	public function update():Void {
+		updateWorldTransformWith(ax, ay, arotation, ascaleX, ascaleY, ashearX, ashearY);
+	}
+
+	/** Computes the world SRT using the parent bone and this bone's local SRT. */
+	public function updateWorldTransform():Void {
+		updateWorldTransformWith(x, y, rotation, scaleX, scaleY, shearX, shearY);
+	}
+
+	/** Computes the world SRT using the parent bone and the specified local SRT. */
+	public function updateWorldTransformWith(x:Float, y:Float, rotation:Float, scaleX:Float, scaleY:Float, shearX:Float, shearY:Float):Void {
+		ax = x;
+		ay = y;
+		arotation = rotation;
+		ascaleX = scaleX;
+		ascaleY = scaleY;
+		ashearX = shearX;
+		ashearY = shearY;
+
+		var rotationY:Float = 0;
+		var la:Float = 0;
+		var lb:Float = 0;
+		var lc:Float = 0;
+		var ld:Float = 0;
+		var sin:Float = 0;
+		var cos:Float = 0;
+		var s:Float = 0;
+		var sx:Float = skeleton.scaleX;
+		var sy:Float = skeleton.scaleY * (yDown ? -1 : 1);
+
+		var parent:Bone = _parent;
+		if (parent == null) {
+			// Root bone.
+			rotationY = rotation + 90 + shearY;
+			a = MathUtils.cosDeg(rotation + shearX) * scaleX * sx;
+			b = MathUtils.cosDeg(rotationY) * scaleY * sx;
+			c = MathUtils.sinDeg(rotation + shearX) * scaleX * sy;
+			d = MathUtils.sinDeg(rotationY) * scaleY * sy;
+			worldX = x * sx + skeleton.x;
+			worldY = y * sy + skeleton.y;
+			return;
+		}
+
+		var pa:Float = parent.a,
+			pb:Float = parent.b,
+			pc:Float = parent.c,
+			pd:Float = parent.d;
+		worldX = pa * x + pb * y + parent.worldX;
+		worldY = pc * x + pd * y + parent.worldY;
+
+		switch (data.transformMode) {
+			case TransformMode.normal:
+				rotationY = rotation + 90 + shearY;
+				la = MathUtils.cosDeg(rotation + shearX) * scaleX;
+				lb = MathUtils.cosDeg(rotationY) * scaleY;
+				lc = MathUtils.sinDeg(rotation + shearX) * scaleX;
+				ld = MathUtils.sinDeg(rotationY) * scaleY;
+				a = pa * la + pb * lc;
+				b = pa * lb + pb * ld;
+				c = pc * la + pd * lc;
+				d = pc * lb + pd * ld;
+				return;
+			case TransformMode.onlyTranslation:
+				rotationY = rotation + 90 + shearY;
+				a = MathUtils.cosDeg(rotation + shearX) * scaleX;
+				b = MathUtils.cosDeg(rotationY) * scaleY;
+				c = MathUtils.sinDeg(rotation + shearX) * scaleX;
+				d = MathUtils.sinDeg(rotationY) * scaleY;
+			case TransformMode.noRotationOrReflection:
+				s = pa * pa + pc * pc;
+				var prx:Float = 0;
+				if (s > 0.0001) {
+					s = Math.abs(pa * pd - pb * pc) / s;
+					pb = pc * s;
+					pd = pa * s;
+					prx = Math.atan2(pc, pa) * MathUtils.radDeg;
+				} else {
+					pa = 0;
+					pc = 0;
+					prx = 90 - Math.atan2(pd, pb) * MathUtils.radDeg;
+				}
+				var rx:Float = rotation + shearX - prx;
+				var ry:Float = rotation + shearY - prx + 90;
+				la = MathUtils.cosDeg(rx) * scaleX;
+				lb = MathUtils.cosDeg(ry) * scaleY;
+				lc = MathUtils.sinDeg(rx) * scaleX;
+				ld = MathUtils.sinDeg(ry) * scaleY;
+				a = pa * la - pb * lc;
+				b = pa * lb - pb * ld;
+				c = pc * la + pd * lc;
+				d = pc * lb + pd * ld;
+			case TransformMode.noScale, TransformMode.noScaleOrReflection:
+				cos = MathUtils.cosDeg(rotation);
+				sin = MathUtils.sinDeg(rotation);
+				var za:Float = (pa * cos + pb * sin) / sx;
+				var zc:Float = (pc * cos + pd * sin) / sy;
+				s = Math.sqrt(za * za + zc * zc);
+				if (s > 0.00001)
+					s = 1 / s;
+				za *= s;
+				zc *= s;
+				s = Math.sqrt(za * za + zc * zc);
+				if (data.transformMode == TransformMode.noScale && ((pa * pd - pb * pc < 0) != ((sx < 0) != (sy < 0)))) {
+					s = -s;
+				}
+				var r:Float = Math.PI / 2 + Math.atan2(zc, za);
+				var zb:Float = Math.cos(r) * s;
+				var zd:Float = Math.sin(r) * s;
+				la = MathUtils.cosDeg(shearX) * scaleX;
+				lb = MathUtils.cosDeg(90 + shearY) * scaleY;
+				lc = MathUtils.sinDeg(shearX) * scaleX;
+				ld = MathUtils.sinDeg(90 + shearY) * scaleY;
+				a = za * la + zb * lc;
+				b = za * lb + zb * ld;
+				c = zc * la + zd * lc;
+				d = zc * lb + zd * ld;
+		}
+		a *= sx;
+		b *= sx;
+		c *= sy;
+		d *= sy;
+	}
+
+	public function setToSetupPose():Void {
+		x = data.x;
+		y = data.y;
+		rotation = data.rotation;
+		scaleX = data.scaleX;
+		scaleY = data.scaleY;
+		shearX = data.shearX;
+		shearY = data.shearY;
+	}
+
+	public var data(get, never):BoneData;
+
+	private function get_data():BoneData {
+		return _data;
+	}
+
+	public var skeleton(get, never):Skeleton;
+
+	private function get_skeleton():Skeleton {
+		return _skeleton;
+	}
+
+	public var parent(get, never):Bone;
+
+	private function get_parent():Bone {
+		return _parent;
+	}
+
+	public var children(get, never):Vector<Bone>;
+
+	private function get_children():Vector<Bone> {
+		return _children;
+	}
+
+	public var worldRotationX(get, never):Float;
+
+	private function get_worldRotationX():Float {
+		return Math.atan2(c, a) * MathUtils.radDeg;
+	}
+
+	public var worldRotationY(get, never):Float;
+
+	private function get_worldRotationY():Float {
+		return Math.atan2(d, b) * MathUtils.radDeg;
+	}
+
+	public var worldScaleX(get, never):Float;
+
+	private function get_worldScaleX():Float {
+		return Math.sqrt(a * a + c * c);
+	}
+
+	public var worldScaleY(get, never):Float;
+
+	private function get_worldScaleY():Float {
+		return Math.sqrt(b * b + d * d);
+	}
+
+	/** Computes the individual applied transform values from the world transform. This can be useful to perform processing using
+	 * the applied transform after the world transform has been modified directly (eg, by a constraint).
+	 * <p>
+	 * Some information is ambiguous in the world transform, such as -1,-1 scale versus 180 rotation. */
+	public function updateAppliedTransform():Void {
+		var parent:Bone = parent;
+		if (parent == null) {
+			ax = worldX - skeleton.x;
+			ay = worldY - skeleton.y;
+			arotation = Math.atan2(c, a) * MathUtils.radDeg;
+			ascaleX = Math.sqrt(a * a + c * c);
+			ascaleY = Math.sqrt(b * b + d * d);
+			ashearX = 0;
+			ashearY = Math.atan2(a * b + c * d, a * d - b * c) * MathUtils.radDeg;
+			return;
+		}
+		var pa:Float = parent.a,
+			pb:Float = parent.b,
+			pc:Float = parent.c,
+			pd:Float = parent.d;
+		var pid:Float = 1 / (pa * pd - pb * pc);
+		var dx:Float = worldX - parent.worldX,
+			dy:Float = worldY - parent.worldY;
+		ax = (dx * pd * pid - dy * pb * pid);
+		ay = (dy * pa * pid - dx * pc * pid);
+		var ia:Float = pid * pd;
+		var id:Float = pid * pa;
+		var ib:Float = pid * pb;
+		var ic:Float = pid * pc;
+		var ra:Float = ia * a - ib * c;
+		var rb:Float = ia * b - ib * d;
+		var rc:Float = id * c - ic * a;
+		var rd:Float = id * d - ic * b;
+		ashearX = 0;
+		ascaleX = Math.sqrt(ra * ra + rc * rc);
+		if (scaleX > 0.0001) {
+			var det:Float = ra * rd - rb * rc;
+			ascaleY = det / ascaleX;
+			ashearY = Math.atan2(ra * rb + rc * rd, det) * MathUtils.radDeg;
+			arotation = Math.atan2(rc, ra) * MathUtils.radDeg;
+		} else {
+			ascaleX = 0;
+			ascaleY = Math.sqrt(rb * rb + rd * rd);
+			ashearY = 0;
+			arotation = 90 - Math.atan2(rd, rb) * MathUtils.radDeg;
+		}
+	}
+
+	public function worldToLocal(world:Vector<Float>):Void {
+		var a:Float = a, b:Float = b, c:Float = c, d:Float = d;
+		var invDet:Float = 1 / (a * d - b * c);
+		var x:Float = world[0] - worldX, y:Float = world[1] - worldY;
+		world[0] = (x * d * invDet - y * b * invDet);
+		world[1] = (y * a * invDet - x * c * invDet);
+	}
+
+	public function localToWorld(local:Vector<Float>):Void {
+		var localX:Float = local[0], localY:Float = local[1];
+		local[0] = localX * a + localY * b + worldX;
+		local[1] = localX * c + localY * d + worldY;
+	}
+
+	public function worldToLocalRotation(worldRotation:Float):Float {
+		var sin:Float = MathUtils.sinDeg(worldRotation),
+			cos:Float = MathUtils.cosDeg(worldRotation);
+		return Math.atan2(a * sin - c * cos, d * cos - b * sin) * MathUtils.radDeg + rotation - shearX;
+	}
+
+	public function localToWorldRotation(localRotation:Float):Float {
+		localRotation -= rotation - shearX;
+		var sin:Float = MathUtils.sinDeg(localRotation),
+			cos:Float = MathUtils.cosDeg(localRotation);
+		return Math.atan2(cos * c + sin * d, cos * a + sin * b) * MathUtils.radDeg;
+	}
+
+	public function rotateWorld(degrees:Float):Void {
+		var a:Float = this.a,
+			b:Float = this.b,
+			c:Float = this.c,
+			d:Float = this.d;
+		var cos:Float = MathUtils.cosDeg(degrees),
+			sin:Float = MathUtils.sinDeg(degrees);
+		this.a = cos * a - sin * c;
+		this.b = cos * b - sin * d;
+		this.c = sin * a + cos * c;
+		this.d = sin * b + cos * d;
+	}
+
+	public function toString():String {
+		return data.name;
+	}
+}

+ 55 - 0
spine-haxe/spine-haxe/spine/BoneData.hx

@@ -0,0 +1,55 @@
+package spine;
+
+import openfl.errors.ArgumentError;
+
+class BoneData {
+	private var _index:Int;
+	private var _name:String;
+	private var _parent:BoneData;
+
+	public var length:Float = 0;
+	public var x:Float = 0;
+	public var y:Float = 0;
+	public var rotation:Float = 0;
+	public var scaleX:Float = 1;
+	public var scaleY:Float = 1;
+	public var shearX:Float = 0;
+	public var shearY:Float = 0;
+	public var transformMode:TransformMode = TransformMode.normal;
+	public var skinRequired:Bool = false;
+	public var color:Color = new Color(0, 0, 0, 0);
+
+	/** @param parent May be null. */
+	public function new(index:Int, name:String, parent:BoneData) {
+		if (index < 0)
+			throw new ArgumentError("index must be >= 0");
+		if (name == null)
+			throw new ArgumentError("name cannot be null.");
+		_index = index;
+		_name = name;
+		_parent = parent;
+	}
+
+	public var index(get, never):Int;
+
+	private function get_index():Int {
+		return _index;
+	}
+
+	public var name(get, never):String;
+
+	private function get_name():String {
+		return _name;
+	}
+
+	/** @return May be null. */
+	public var parent(get, never):BoneData;
+
+	private function get_parent():BoneData {
+		return _parent;
+	}
+
+	public function toString():String {
+		return _name;
+	}
+}

+ 101 - 0
spine-haxe/spine-haxe/spine/Color.hx

@@ -0,0 +1,101 @@
+package spine;
+
+class Color {
+	public static var WHITE:Color = new Color(1, 1, 1, 1);
+	public static var RED:Color = new Color(1, 0, 0, 1);
+	public static var GREEN:Color = new Color(0, 1, 0, 1);
+	public static var BLUE:Color = new Color(0, 0, 1, 1);
+	public static var MAGENTA:Color = new Color(1, 0, 1, 1);
+
+	public var r:Float = 0;
+	public var g:Float = 0;
+	public var b:Float = 0;
+	public var a:Float = 0;
+
+	public function new(r:Float, g:Float, b:Float, a:Float = 0) {
+		this.r = r;
+		this.g = g;
+		this.b = b;
+		this.a = a;
+		clamp();
+	}
+
+	public function setFromColor(c:Color):Color {
+		r = c.r;
+		g = c.g;
+		b = c.b;
+		a = c.a;
+		clamp();
+		return this;
+	}
+
+	public function setFromString(hex:String):Color {
+		hex = hex.charAt(0) == '#' ? hex.substr(1) : hex;
+		r = Std.parseInt("0x" + hex.substr(0, 2)) / 255.0;
+		g = Std.parseInt("0x" + hex.substr(2, 2)) / 255.0;
+		b = Std.parseInt("0x" + hex.substr(4, 2)) / 255.0;
+		a = (hex.length != 8 ? 255 : Std.parseInt("0x" + hex.substr(6, 2))) / 255.0;
+		clamp();
+		return this;
+	}
+
+	public function set(r:Float, g:Float, b:Float, a:Float):Color {
+		this.r = r;
+		this.g = g;
+		this.b = b;
+		this.a = a;
+		clamp();
+		return this;
+	}
+
+	public function add(r:Float, g:Float, b:Float, a:Float):Color {
+		this.r += r;
+		this.g += g;
+		this.b += b;
+		this.a += a;
+		clamp();
+		return this;
+	}
+
+	public function setFromRgba8888(value:Int):Void {
+		r = ((value & 0xff000000) >>> 24) / 255;
+		g = ((value & 0x00ff0000) >>> 16) / 255;
+		b = ((value & 0x0000ff00) >>> 8) / 255;
+		a = ((value & 0x000000ff)) / 255;
+		clamp();
+	}
+
+	public function setFromRgb888(value:Int):Void {
+		r = ((value & 0x00ff0000) >>> 16) / 255;
+		g = ((value & 0x0000ff00) >>> 8) / 255;
+		b = ((value & 0x000000ff)) / 255;
+		clamp();
+	}
+
+	private function clamp():Color {
+		if (r < 0)
+			r = 0;
+		else if (r > 1)
+			r = 1;
+
+		if (g < 0)
+			g = 0;
+		else if (g > 1)
+			g = 1;
+
+		if (b < 0)
+			b = 0;
+		else if (b > 1)
+			b = 1;
+
+		if (a < 0)
+			a = 0;
+		else if (a > 1)
+			a = 1;
+		return this;
+	}
+
+	static public function fromString(hex:String):Color {
+		return new Color(0, 0, 0, 0).setFromString(hex);
+	}
+}

+ 17 - 0
spine-haxe/spine-haxe/spine/ConstraintData.hx

@@ -0,0 +1,17 @@
+package spine;
+
+class ConstraintData {
+	public var name:String;
+	public var order:Int = 0;
+	public var skinRequired:Bool = false;
+
+	function new(name:String, order:Int, skinRequired:Bool) {
+		this.name = name;
+		this.order = order;
+		this.skinRequired = skinRequired;
+	}
+
+	public function toString():String {
+		return name;
+	}
+}

+ 31 - 0
spine-haxe/spine-haxe/spine/Event.hx

@@ -0,0 +1,31 @@
+package spine;
+
+import openfl.errors.ArgumentError;
+
+class Event {
+	private var _data:EventData;
+
+	public var time:Float = 0;
+	public var intValue:Int = 0;
+	public var floatValue:Float = 0;
+	public var stringValue:String;
+	public var volume:Float = 1;
+	public var balance:Float = 0;
+
+	public function new(time:Float, data:EventData) {
+		if (data == null)
+			throw new ArgumentError("data cannot be null.");
+		this.time = time;
+		_data = data;
+	}
+
+	public var data(get, never):EventData;
+
+	private function get_data():EventData {
+		return _data;
+	}
+
+	public function toString():String {
+		return _data.name != null ? _data.name : "Event?";
+	}
+}

+ 30 - 0
spine-haxe/spine-haxe/spine/EventData.hx

@@ -0,0 +1,30 @@
+package spine;
+
+import openfl.errors.ArgumentError;
+
+class EventData {
+	private var _name:String;
+
+	public var intValue:Int = 0;
+	public var floatValue:Float = 0;
+	public var stringValue:String;
+	public var audioPath:String;
+	public var volume:Float = 1;
+	public var balance:Float = 0;
+
+	public function new(name:String) {
+		if (name == null)
+			throw new ArgumentError("name cannot be null.");
+		_name = name;
+	}
+
+	public var name(get, never):String;
+
+	private function get_name():String {
+		return _name;
+	}
+
+	public function toString():String {
+		return _name;
+	}
+}

+ 307 - 0
spine-haxe/spine-haxe/spine/IkConstraint.hx

@@ -0,0 +1,307 @@
+package spine;
+
+import openfl.errors.ArgumentError;
+import openfl.Vector;
+
+class IkConstraint implements Updatable {
+	private var _data:IkConstraintData;
+
+	public var bones:Vector<Bone>;
+	public var target:Bone;
+	public var bendDirection:Int = 0;
+	public var compress:Bool = false;
+	public var stretch:Bool = false;
+	public var mix:Float = 0;
+	public var softness:Float = 0;
+	public var active:Bool = false;
+
+	public function new(data:IkConstraintData, skeleton:Skeleton) {
+		if (data == null)
+			throw new ArgumentError("data cannot be null.");
+		if (skeleton == null)
+			throw new ArgumentError("skeleton cannot be null.");
+		_data = data;
+		mix = data.mix;
+		softness = data.softness;
+		bendDirection = data.bendDirection;
+		compress = data.compress;
+		stretch = data.stretch;
+
+		bones = new Vector<Bone>();
+		for (boneData in data.bones) {
+			bones.push(skeleton.findBone(boneData.name));
+		}
+		target = skeleton.findBone(data.target.name);
+	}
+
+	public function isActive():Bool {
+		return active;
+	}
+
+	public function update():Void {
+		if (mix == 0)
+			return;
+		switch (bones.length) {
+			case 1:
+				apply1(bones[0], target.worldX, target.worldY, compress, stretch, _data.uniform, mix);
+			case 2:
+				apply2(bones[0], bones[1], target.worldX, target.worldY, bendDirection, stretch, _data.uniform, softness, mix);
+		}
+	}
+
+	public var data(get, never):IkConstraintData;
+
+	private function get_data():IkConstraintData {
+		return _data;
+	}
+
+	public function toString():String {
+		return _data.name != null ? _data.name : "IkContstraint?";
+	}
+
+	/** Adjusts the bone rotation so the tip is as close to the target position as possible. The target is specified in the world
+	 * coordinate system. */
+	static public function apply1(bone:Bone, targetX:Float, targetY:Float, compress:Bool, stretch:Bool, uniform:Bool, alpha:Float):Void {
+		var p:Bone = bone.parent;
+		var pa:Float = p.a, pb:Float = p.b, pc:Float = p.c, pd:Float = p.d;
+		var rotationIK:Float = -bone.ashearX - bone.arotation,
+			tx:Float = 0,
+			ty:Float = 0;
+		switch (bone.data.transformMode) {
+			case TransformMode.onlyTranslation:
+				tx = targetX - bone.worldX;
+				ty = targetY - bone.worldY;
+			case TransformMode.noRotationOrReflection:
+				var s:Float = Math.abs(pa * pd - pb * pc) / (pa * pa + pc * pc);
+				var sa:Float = pa / bone.skeleton.scaleX;
+				var sc:Float = pc / bone.skeleton.scaleY;
+				pb = -sc * s * bone.skeleton.scaleX;
+				pd = sa * s * bone.skeleton.scaleY;
+				rotationIK += Math.atan2(sc, sa) * MathUtils.radDeg;
+				var x:Float = targetX - p.worldX, y:Float = targetY - p.worldY;
+				var d:Float = pa * pd - pb * pc;
+				tx = (x * pd - y * pb) / d - bone.ax;
+				ty = (y * pa - x * pc) / d - bone.ay;
+			default:
+				var x:Float = targetX - p.worldX, y:Float = targetY - p.worldY;
+				var d:Float = pa * pd - pb * pc;
+				tx = (x * pd - y * pb) / d - bone.ax;
+				ty = (y * pa - x * pc) / d - bone.ay;
+		}
+
+		rotationIK += Math.atan2(ty, tx) * MathUtils.radDeg;
+		if (bone.ascaleX < 0)
+			rotationIK += 180;
+		if (rotationIK > 180)
+			rotationIK -= 360;
+		else if (rotationIK < -180)
+			rotationIK += 360;
+		var sx:Float = bone.ascaleX;
+		var sy:Float = bone.ascaleY;
+		if (compress || stretch) {
+			switch (bone.data.transformMode) {
+				case TransformMode.noScale, TransformMode.noScaleOrReflection:
+					tx = targetX - bone.worldX;
+					ty = targetY - bone.worldY;
+			}
+			var b:Float = bone.data.length * sx,
+				dd:Float = Math.sqrt(tx * tx + ty * ty);
+			if ((compress && dd < b) || (stretch && dd > b) && b > 0.0001) {
+				var ss:Float = (dd / b - 1) * alpha + 1;
+				sx *= ss;
+				if (uniform)
+					sy *= ss;
+			}
+		}
+		bone.updateWorldTransformWith(bone.ax, bone.ay, bone.arotation + rotationIK * alpha, sx, sy, bone.ashearX, bone.ashearY);
+	}
+
+	/** Adjusts the parent and child bone rotations so the tip of the child is as close to the target position as possible. The
+	 * target is specified in the world coordinate system.
+	 * @param child Any descendant bone of the parent. */
+	static public function apply2(parent:Bone, child:Bone, targetX:Float, targetY:Float, bendDir:Int, stretch:Bool, uniform:Bool, softness:Float,
+			alpha:Float):Void {
+		var px:Float = parent.ax;
+		var py:Float = parent.ay;
+		var psx:Float = parent.ascaleX;
+		var sx:Float = psx;
+		var psy:Float = parent.ascaleY;
+		var sy:Float = psy;
+		var csx:Float = child.ascaleX;
+		var os1:Int;
+		var os2:Int;
+		var s2:Int;
+		if (psx < 0) {
+			psx = -psx;
+			os1 = 180;
+			s2 = -1;
+		} else {
+			os1 = 0;
+			s2 = 1;
+		}
+		if (psy < 0) {
+			psy = -psy;
+			s2 = -s2;
+		}
+		if (csx < 0) {
+			csx = -csx;
+			os2 = 180;
+		} else {
+			os2 = 0;
+		}
+		var cx:Float = child.ax;
+		var cy:Float;
+		var cwx:Float;
+		var cwy:Float;
+		var a:Float = parent.a;
+		var b:Float = parent.b;
+		var c:Float = parent.c;
+		var d:Float = parent.d;
+		var u:Bool = Math.abs(psx - psy) <= 0.0001;
+		if (!u || stretch) {
+			cy = 0;
+			cwx = a * cx + parent.worldX;
+			cwy = c * cx + parent.worldY;
+		} else {
+			cy = child.ay;
+			cwx = a * cx + b * cy + parent.worldX;
+			cwy = c * cx + d * cy + parent.worldY;
+		}
+		var pp:Bone = parent.parent;
+		a = pp.a;
+		b = pp.b;
+		c = pp.c;
+		d = pp.d;
+		var id:Float = 1 / (a * d - b * c),
+			x:Float = cwx - pp.worldX,
+			y:Float = cwy - pp.worldY;
+		var dx:Float = (x * d - y * b) * id - px,
+			dy:Float = (y * a - x * c) * id - py;
+		var l1:Float = Math.sqrt(dx * dx + dy * dy);
+		var l2:Float = child.data.length * csx;
+		var a1:Float = 0;
+		var a2:Float = 0;
+		if (l1 < 0.0001) {
+			apply1(parent, targetX, targetY, false, stretch, false, alpha);
+			child.updateWorldTransformWith(cx, cy, 0, child.ascaleX, child.ascaleY, child.ashearX, child.ashearY);
+			return;
+		}
+		x = targetX - pp.worldX;
+		y = targetY - pp.worldY;
+		var tx:Float = (x * d - y * b) * id - px;
+		var ty:Float = (y * a - x * c) * id - py;
+		var dd:Float = tx * tx + ty * ty;
+		if (softness != 0) {
+			softness *= psx * (csx + 1) / 2;
+			var td:Float = Math.sqrt(dd);
+			var sd:Float = td - l1 - l2 * psx + softness;
+			if (sd > 0) {
+				var p:Float = Math.min(1, sd / (softness * 2)) - 1;
+				p = (sd - softness * (1 - p * p)) / td;
+				tx -= p * tx;
+				ty -= p * ty;
+				dd = tx * tx + ty * ty;
+			}
+		}
+
+		var breakOuter:Bool = false;
+		if (u) {
+			l2 *= psx;
+			var cos:Float = (dd - l1 * l1 - l2 * l2) / (2 * l1 * l2);
+			if (cos < -1) {
+				cos = -1;
+			} else if (cos > 1) {
+				cos = 1;
+				if (stretch) {
+					a = (Math.sqrt(dd) / (l1 + l2) - 1) * alpha + 1;
+					sx *= a;
+					if (uniform)
+						sy *= a;
+				}
+			}
+			a2 = Math.acos(cos) * bendDir;
+			a = l1 + l2 * cos;
+			b = l2 * Math.sin(a2);
+			a1 = Math.atan2(ty * a - tx * b, tx * a + ty * b);
+		} else {
+			a = psx * l2;
+			b = psy * l2;
+			var aa:Float = a * a;
+			var bb:Float = b * b;
+			var ta:Float = Math.atan2(ty, tx);
+			c = bb * l1 * l1 + aa * dd - aa * bb;
+			var c1:Float = -2 * bb * l1;
+			var c2:Float = bb - aa;
+			d = c1 * c1 - 4 * c2 * c;
+			if (d >= 0) {
+				var q:Float = Math.sqrt(d);
+				if (c1 < 0)
+					q = -q;
+				q = -(c1 + q) / 2;
+				var r0:Float = q / c2, r1:Float = c / q;
+				var r:Float = Math.abs(r0) < Math.abs(r1) ? r0 : r1;
+				if (r * r <= dd) {
+					y = Math.sqrt(dd - r * r) * bendDir;
+					a1 = ta - Math.atan2(y, r);
+					a2 = Math.atan2(y / psy, (r - l1) / psx);
+					breakOuter = true;
+				}
+			}
+
+			if (!breakOuter) {
+				var minAngle:Float = Math.PI;
+				var minX:Float = l1 - a;
+				var minDist:Float = minX * minX;
+				var minY:Float = 0;
+				var maxAngle:Float = 0;
+				var maxX:Float = l1 + a;
+				var maxDist:Float = maxX * maxX;
+				var maxY:Float = 0;
+				c = -a * l1 / (aa - bb);
+				if (c >= -1 && c <= 1) {
+					c = Math.acos(c);
+					x = a * Math.cos(c) + l1;
+					y = b * Math.sin(c);
+					d = x * x + y * y;
+					if (d < minDist) {
+						minAngle = c;
+						minDist = d;
+						minX = x;
+						minY = y;
+					}
+					if (d > maxDist) {
+						maxAngle = c;
+						maxDist = d;
+						maxX = x;
+						maxY = y;
+					}
+				}
+				if (dd <= (minDist + maxDist) / 2) {
+					a1 = ta - Math.atan2(minY * bendDir, minX);
+					a2 = minAngle * bendDir;
+				} else {
+					a1 = ta - Math.atan2(maxY * bendDir, maxX);
+					a2 = maxAngle * bendDir;
+				}
+			}
+		}
+
+		var os:Float = Math.atan2(cy, cx) * s2;
+		var rotation:Float = parent.arotation;
+		a1 = (a1 - os) * MathUtils.radDeg + os1 - rotation;
+		if (a1 > 180) {
+			a1 -= 360;
+		} else if (a1 < -180) {
+			a1 += 360;
+		}
+		parent.updateWorldTransformWith(px, py, rotation + a1 * alpha, sx, sy, 0, 0);
+		rotation = child.arotation;
+		a2 = ((a2 + os) * MathUtils.radDeg - child.ashearX) * s2 + os2 - rotation;
+		if (a2 > 180) {
+			a2 -= 360;
+		} else if (a2 < -180) {
+			a2 += 360;
+		}
+		child.updateWorldTransformWith(cx, cy, rotation + a2 * alpha, child.ascaleX, child.ascaleY, child.ashearX, child.ashearY);
+	}
+}

+ 18 - 0
spine-haxe/spine-haxe/spine/IkConstraintData.hx

@@ -0,0 +1,18 @@
+package spine;
+
+import openfl.Vector;
+
+class IkConstraintData extends ConstraintData {
+	public var bones:Vector<BoneData> = new Vector<BoneData>();
+	public var target:BoneData;
+	public var mix:Float = 1;
+	public var bendDirection:Int = 1;
+	public var compress:Bool = false;
+	public var stretch:Bool = false;
+	public var uniform:Bool = false;
+	public var softness:Float = 0;
+
+	public function new(name:String) {
+		super(name, 0, false);
+	}
+}

+ 11 - 0
spine-haxe/spine-haxe/spine/Interpolation.hx

@@ -0,0 +1,11 @@
+package spine;
+
+class Interpolation {
+	private function applyInternal(a:Float):Float {
+		return a;
+	}
+
+	public function apply(start:Float, end:Float, a:Float):Float {
+		return start + (end - start) * applyInternal(a);
+	}
+}

+ 40 - 0
spine-haxe/spine-haxe/spine/MathUtils.hx

@@ -0,0 +1,40 @@
+package spine;
+
+class MathUtils {
+	static public var PI:Float = Math.PI;
+	static public var PI2:Float = Math.PI * 2;
+	static public var radDeg:Float = 180 / Math.PI;
+	static public var degRad:Float = Math.PI / 180;
+
+	static public function cosDeg(degrees:Float):Float {
+		return Math.cos(degrees * degRad);
+	}
+
+	static public function sinDeg(degrees:Float):Float {
+		return Math.sin(degrees * degRad);
+	}
+
+	static public function clamp(value:Float, min:Float, max:Float):Float {
+		if (value < min)
+			return min;
+		if (value > max)
+			return max;
+		return value;
+	}
+
+	static public function signum(value:Float):Float {
+		return value > 0 ? 1 : value < 0 ? -1 : 0;
+	}
+
+	static public function randomTriangular(min:Float, max:Float):Float {
+		return randomTriangularWith(min, max, (min + max) * 0.5);
+	}
+
+	static public function randomTriangularWith(min:Float, max:Float, mode:Float):Float {
+		var u:Float = Math.random();
+		var d:Float = max - min;
+		if (u <= (mode - min) / d)
+			return min + Math.sqrt(u * d * (mode - min));
+		return max - Math.sqrt((1 - u) * d * (max - mode));
+	}
+}

+ 571 - 0
spine-haxe/spine-haxe/spine/PathConstraint.hx

@@ -0,0 +1,571 @@
+package spine;
+
+import openfl.errors.ArgumentError;
+import openfl.Vector;
+import spine.attachments.PathAttachment;
+
+class PathConstraint implements Updatable {
+	private static inline var NONE:Int = -1;
+	private static inline var BEFORE:Int = -2;
+	private static inline var AFTER:Int = -3;
+	private static inline var epsilon:Float = 0.00001;
+
+	private var _data:PathConstraintData;
+	private var _bones:Vector<Bone>;
+
+	public var target:Slot;
+	public var position:Float = 0;
+	public var spacing:Float = 0;
+	public var mixRotate:Float = 0;
+	public var mixX:Float = 0;
+	public var mixY:Float = 0;
+
+	private var _spaces(default, never):Vector<Float> = new Vector<Float>();
+	private var _positions(default, never):Vector<Float> = new Vector<Float>();
+	private var _world(default, never):Vector<Float> = new Vector<Float>();
+	private var _curves(default, never):Vector<Float> = new Vector<Float>();
+	private var _lengths(default, never):Vector<Float> = new Vector<Float>();
+	private var _segments(default, never):Vector<Float> = new Vector<Float>(10, true);
+
+	public var active:Bool = false;
+
+	public function new(data:PathConstraintData, skeleton:Skeleton) {
+		if (data == null)
+			throw new ArgumentError("data cannot be null.");
+		if (skeleton == null)
+			throw new ArgumentError("skeleton cannot be null.");
+		_data = data;
+		_bones = new Vector<Bone>();
+		for (boneData in data.bones) {
+			_bones.push(skeleton.findBone(boneData.name));
+		}
+		target = skeleton.findSlot(data.target.name);
+		position = data.position;
+		spacing = data.spacing;
+		mixRotate = data.mixRotate;
+		mixX = data.mixX;
+		mixY = data.mixY;
+	}
+
+	public function isActive():Bool {
+		return active;
+	}
+
+	public function update():Void {
+		var attachment:PathAttachment = cast(target.attachment, PathAttachment);
+		if (attachment == null)
+			return;
+		if (mixRotate == 0 && mixX == 0 && mixY == 0)
+			return;
+
+		var data:PathConstraintData = _data;
+		var percentSpacing:Bool = data.spacingMode == SpacingMode.percent;
+		var rotateMode:RotateMode = data.rotateMode;
+		var fTangents:Bool = rotateMode == RotateMode.tangent,
+			fScale:Bool = rotateMode == RotateMode.chainScale;
+
+		var boneCount:Int = _bones.length;
+		var spacesCount:Int = fTangents ? boneCount : boneCount + 1;
+		var bones:Vector<Bone> = _bones;
+		_spaces.length = spacesCount;
+
+		if (fScale)
+			_lengths.length = boneCount;
+
+		var i:Int,
+			n:Int,
+			bone:Bone,
+			setupLength:Float,
+			x:Float,
+			y:Float,
+			length:Float;
+		switch (data.spacingMode) {
+			case SpacingMode.percent:
+				if (fScale) {
+					n = spacesCount - 1;
+					for (i in 0...n) {
+						bone = bones[i];
+						setupLength = bone.data.length;
+						if (setupLength < PathConstraint.epsilon) {
+							_lengths[i] = 0;
+						} else {
+							x = setupLength * bone.a;
+							y = setupLength * bone.c;
+							_lengths[i] = Math.sqrt(x * x + y * y);
+						}
+					}
+				}
+				for (i in 1...spacesCount) {
+					_spaces[i] = spacing;
+				}
+			case SpacingMode.proportional:
+				var sum:Float = 0;
+				i = 0;
+				n = spacesCount - 1;
+				while (i < n) {
+					bone = bones[i];
+					setupLength = bone.data.length;
+					if (setupLength < PathConstraint.epsilon) {
+						if (fScale)
+							_lengths[i] = 0;
+						_spaces[++i] = spacing;
+					} else {
+						x = setupLength * bone.a;
+						y = setupLength * bone.c;
+						length = Math.sqrt(x * x + y * y);
+						if (fScale)
+							_lengths[i] = length;
+						_spaces[++i] = length;
+						sum += length;
+					}
+				}
+				if (sum > 0) {
+					sum = spacesCount / sum * spacing;
+					for (i in 1...spacesCount) {
+						_spaces[i] *= sum;
+					}
+				}
+			default:
+				var lengthSpacing:Bool = data.spacingMode == SpacingMode.length;
+				i = 0;
+				n = spacesCount - 1;
+				while (i < n) {
+					bone = bones[i];
+					setupLength = bone.data.length;
+					if (setupLength < PathConstraint.epsilon) {
+						if (fScale)
+							_lengths[i] = 0;
+						_spaces[++i] = spacing;
+					} else {
+						x = setupLength * bone.a;
+						y = setupLength * bone.c;
+						length = Math.sqrt(x * x + y * y);
+						if (fScale)
+							_lengths[i] = length;
+						_spaces[++i] = (lengthSpacing ? setupLength + spacing : spacing) * length / setupLength;
+					}
+				}
+		}
+
+		var positions:Vector<Float> = computeWorldPositions(attachment, spacesCount, fTangents);
+		var boneX:Float = positions[0];
+		var boneY:Float = positions[1];
+		var offsetRotation:Float = data.offsetRotation;
+		var tip:Bool = false;
+		if (offsetRotation == 0) {
+			tip = data.rotateMode == RotateMode.chain;
+		} else {
+			tip = false;
+			var pa:Bone = target.bone;
+			offsetRotation *= pa.a * pa.d - pa.b * pa.c > 0 ? MathUtils.degRad : -MathUtils.degRad;
+		}
+
+		i = 0;
+		var p:Int = 3;
+		while (i < boneCount) {
+			var bone:Bone = bones[i];
+			bone.worldX += (boneX - bone.worldX) * mixX;
+			bone.worldY += (boneY - bone.worldY) * mixY;
+			var x:Float = positions[p];
+			var y:Float = positions[p + 1];
+			var dx:Float = x - boneX;
+			var dy:Float = y - boneY;
+			if (fScale) {
+				var length = _lengths[i];
+				if (length != 0) {
+					var s:Float = (Math.sqrt(dx * dx + dy * dy) / length - 1) * mixRotate + 1;
+					bone.a *= s;
+					bone.c *= s;
+				}
+			}
+			boneX = x;
+			boneY = y;
+			if (mixRotate > 0) {
+				var a:Float = bone.a,
+					b:Float = bone.b,
+					c:Float = bone.c,
+					d:Float = bone.d,
+					r:Float,
+					cos:Float,
+					sin:Float;
+				if (fTangents) {
+					r = positions[p - 1];
+				} else if (_spaces[i + 1] == 0) {
+					r = positions[p + 2];
+				} else {
+					r = Math.atan2(dy, dx);
+				}
+				r -= Math.atan2(c, a);
+				if (tip) {
+					cos = Math.cos(r);
+					sin = Math.sin(r);
+					var length:Float = bone.data.length;
+					boneX += (length * (cos * a - sin * c) - dx) * mixRotate;
+					boneY += (length * (sin * a + cos * c) - dy) * mixRotate;
+				} else {
+					r += offsetRotation;
+				}
+				if (r > Math.PI) {
+					r -= (Math.PI * 2);
+				} else if (r < -Math.PI) {
+					r += (Math.PI * 2);
+				}
+				r *= mixRotate;
+				cos = Math.cos(r);
+				sin = Math.sin(r);
+				bone.a = cos * a - sin * c;
+				bone.b = cos * b - sin * d;
+				bone.c = sin * a + cos * c;
+				bone.d = sin * b + cos * d;
+			}
+			bone.updateAppliedTransform();
+
+			i++;
+			p += 3;
+		}
+	}
+
+	private function computeWorldPositions(path:PathAttachment, spacesCount:Int, tangents:Bool):Vector<Float> {
+		var position:Float = this.position;
+		_positions.length = spacesCount * 3 + 2;
+		var out:Vector<Float> = _positions, world:Vector<Float>;
+		var closed:Bool = path.closed;
+		var verticesLength:Int = path.worldVerticesLength;
+		var curveCount:Int = Std.int(verticesLength / 6);
+		var prevCurve:Int = NONE;
+		var multiplier:Float, i:Int;
+
+		if (!path.constantSpeed) {
+			var lengths:Vector<Float> = path.lengths;
+			curveCount -= closed ? 1 : 2;
+			var pathLength:Float = lengths[curveCount];
+			if (data.positionMode == PositionMode.percent)
+				position *= pathLength;
+			switch (data.spacingMode) {
+				case SpacingMode.percent:
+					multiplier = pathLength;
+				case SpacingMode.proportional:
+					multiplier = pathLength / spacesCount;
+				default:
+					multiplier = 1;
+			}
+
+			_world.length = 8;
+			world = _world;
+			var i:Int = 0;
+			var o:Int = 0;
+			var curve:Int = 0;
+			while (i < spacesCount) {
+				var space:Float = _spaces[i] * multiplier;
+				position += space;
+				var p:Float = position;
+
+				if (closed) {
+					p %= pathLength;
+					if (p < 0)
+						p += pathLength;
+					curve = 0;
+				} else if (p < 0) {
+					if (prevCurve != BEFORE) {
+						prevCurve = BEFORE;
+						path.computeWorldVertices(target, 2, 4, world, 0, 2);
+					}
+					addBeforePosition(p, world, 0, out, o);
+					continue;
+				} else if (p > pathLength) {
+					if (prevCurve != AFTER) {
+						prevCurve = AFTER;
+						path.computeWorldVertices(target, verticesLength - 6, 4, world, 0, 2);
+					}
+					addAfterPosition(p - pathLength, world, 0, out, o);
+					continue;
+				}
+
+				// Determine curve containing position.
+				while (true) {
+					var length:Float = lengths[curve];
+					if (p > length) {
+						curve++;
+						continue;
+					}
+					if (curve == 0) {
+						p /= length;
+					} else {
+						var prev:Float = lengths[curve - 1];
+						p = (p - prev) / (length - prev);
+					}
+					break;
+				}
+				if (curve != prevCurve) {
+					prevCurve = curve;
+					if (closed && curve == curveCount) {
+						path.computeWorldVertices(target, verticesLength - 4, 4, world, 0, 2);
+						path.computeWorldVertices(target, 0, 4, world, 4, 2);
+					} else {
+						path.computeWorldVertices(target, curve * 6 + 2, 8, world, 0, 2);
+					}
+				}
+				addCurvePosition(p, world[0], world[1], world[2], world[3], world[4], world[5], world[6], world[7], out, o, tangents || (i > 0 && space == 0));
+
+				i++;
+				o += 3;
+			}
+			return out;
+		}
+
+		// World vertices.
+		if (closed) {
+			verticesLength += 2;
+			_world.length = verticesLength;
+			world = _world;
+			path.computeWorldVertices(target, 2, verticesLength - 4, world, 0, 2);
+			path.computeWorldVertices(target, 0, 2, world, verticesLength - 4, 2);
+			world[verticesLength - 2] = world[0];
+			world[verticesLength - 1] = world[1];
+		} else {
+			curveCount--;
+			verticesLength -= 4;
+			_world.length = verticesLength;
+			world = _world;
+			path.computeWorldVertices(target, 2, verticesLength, world, 0, 2);
+		}
+
+		// Curve lengths.
+		_curves.length = curveCount;
+		var curves:Vector<Float> = _curves;
+		var pathLength:Float = 0;
+		var x1:Float = world[0],
+			y1:Float = world[1],
+			cx1:Float = 0,
+			cy1:Float = 0,
+			cx2:Float = 0,
+			cy2:Float = 0,
+			x2:Float = 0,
+			y2:Float = 0;
+		var tmpx:Float, tmpy:Float, dddfx:Float, dddfy:Float, ddfx:Float, ddfy:Float, dfx:Float, dfy:Float;
+		var i:Int = 0;
+		var w:Int = 2;
+		while (i < curveCount) {
+			cx1 = world[w];
+			cy1 = world[w + 1];
+			cx2 = world[w + 2];
+			cy2 = world[w + 3];
+			x2 = world[w + 4];
+			y2 = world[w + 5];
+			tmpx = (x1 - cx1 * 2 + cx2) * 0.1875;
+			tmpy = (y1 - cy1 * 2 + cy2) * 0.1875;
+			dddfx = ((cx1 - cx2) * 3 - x1 + x2) * 0.09375;
+			dddfy = ((cy1 - cy2) * 3 - y1 + y2) * 0.09375;
+			ddfx = tmpx * 2 + dddfx;
+			ddfy = tmpy * 2 + dddfy;
+			dfx = (cx1 - x1) * 0.75 + tmpx + dddfx * 0.16666667;
+			dfy = (cy1 - y1) * 0.75 + tmpy + dddfy * 0.16666667;
+			pathLength += Math.sqrt(dfx * dfx + dfy * dfy);
+			dfx += ddfx;
+			dfy += ddfy;
+			ddfx += dddfx;
+			ddfy += dddfy;
+			pathLength += Math.sqrt(dfx * dfx + dfy * dfy);
+			dfx += ddfx;
+			dfy += ddfy;
+			pathLength += Math.sqrt(dfx * dfx + dfy * dfy);
+			dfx += ddfx + dddfx;
+			dfy += ddfy + dddfy;
+			pathLength += Math.sqrt(dfx * dfx + dfy * dfy);
+			curves[i] = pathLength;
+			x1 = x2;
+			y1 = y2;
+
+			i++;
+			w += 6;
+		}
+
+		if (data.positionMode == PositionMode.percent)
+			position *= pathLength;
+
+		switch (data.spacingMode) {
+			case SpacingMode.percent:
+				multiplier = pathLength;
+			case SpacingMode.proportional:
+				multiplier = pathLength / spacesCount;
+			default:
+				multiplier = 1;
+		}
+
+		var segments:Vector<Float> = _segments;
+		var curveLength:Float = 0;
+		var segment:Int;
+		i = 0;
+		var o:Int = 0;
+		var segment:Int = 0;
+		while (i < spacesCount) {
+			var space = _spaces[i] * multiplier;
+			position += space;
+			var p = position;
+
+			if (closed) {
+				p %= pathLength;
+				if (p < 0)
+					p += pathLength;
+			} else if (p < 0) {
+				addBeforePosition(p, world, 0, out, o);
+				i++;
+				o += 3;
+				continue;
+			} else if (p > pathLength) {
+				addAfterPosition(p - pathLength, world, verticesLength - 4, out, o);
+				i++;
+				o += 3;
+				continue;
+			}
+
+			// Determine curve containing position.
+			var curve = 0;
+			while (true) {
+				var length = curves[curve];
+				if (p > length) {
+					curve++;
+					continue;
+				}
+				if (curve == 0) {
+					p /= length;
+				} else {
+					var prev = curves[curve - 1];
+					p = (p - prev) / (length - prev);
+				}
+				break;
+			}
+
+			// Curve segment lengths.
+			if (curve != prevCurve) {
+				prevCurve = curve;
+				var ii:Int = curve * 6;
+				x1 = world[ii];
+				y1 = world[ii + 1];
+				cx1 = world[ii + 2];
+				cy1 = world[ii + 3];
+				cx2 = world[ii + 4];
+				cy2 = world[ii + 5];
+				x2 = world[ii + 6];
+				y2 = world[ii + 7];
+				tmpx = (x1 - cx1 * 2 + cx2) * 0.03;
+				tmpy = (y1 - cy1 * 2 + cy2) * 0.03;
+				dddfx = ((cx1 - cx2) * 3 - x1 + x2) * 0.006;
+				dddfy = ((cy1 - cy2) * 3 - y1 + y2) * 0.006;
+				ddfx = tmpx * 2 + dddfx;
+				ddfy = tmpy * 2 + dddfy;
+				dfx = (cx1 - x1) * 0.3 + tmpx + dddfx * 0.16666667;
+				dfy = (cy1 - y1) * 0.3 + tmpy + dddfy * 0.16666667;
+				curveLength = Math.sqrt(dfx * dfx + dfy * dfy);
+				segments[0] = curveLength;
+				for (ii in 1...8) {
+					dfx += ddfx;
+					dfy += ddfy;
+					ddfx += dddfx;
+					ddfy += dddfy;
+					curveLength += Math.sqrt(dfx * dfx + dfy * dfy);
+					segments[ii] = curveLength;
+				}
+				dfx += ddfx;
+				dfy += ddfy;
+				curveLength += Math.sqrt(dfx * dfx + dfy * dfy);
+				segments[8] = curveLength;
+				dfx += ddfx + dddfx;
+				dfy += ddfy + dddfy;
+				curveLength += Math.sqrt(dfx * dfx + dfy * dfy);
+				segments[9] = curveLength;
+				segment = 0;
+			}
+
+			// Weight by segment length.
+			p *= curveLength;
+			while (true) {
+				var length = segments[segment];
+				if (p > length) {
+					segment++;
+					continue;
+				}
+				if (segment == 0) {
+					p /= length;
+				} else {
+					var prev = segments[segment - 1];
+					p = segment + (p - prev) / (length - prev);
+				}
+				break;
+			}
+			addCurvePosition(p * 0.1, x1, y1, cx1, cy1, cx2, cy2, x2, y2, out, o, tangents || (i > 0 && space == 0));
+
+			i++;
+			o += 3;
+		}
+		return out;
+	}
+
+	private function addBeforePosition(p:Float, temp:Vector<Float>, i:Int, out:Vector<Float>, o:Int):Void {
+		var x1:Float = temp[i];
+		var y1:Float = temp[i + 1];
+		var dx:Float = temp[i + 2] - x1;
+		var dy:Float = temp[i + 3] - y1;
+		var r:Float = Math.atan2(dy, dx);
+		out[o] = x1 + p * Math.cos(r);
+		out[o + 1] = y1 + p * Math.sin(r);
+		out[o + 2] = r;
+	}
+
+	private function addAfterPosition(p:Float, temp:Vector<Float>, i:Int, out:Vector<Float>, o:Int):Void {
+		var x1:Float = temp[i + 2];
+		var y1:Float = temp[i + 3];
+		var dx:Float = x1 - temp[i];
+		var dy:Float = y1 - temp[i + 1];
+		var r:Float = Math.atan2(dy, dx);
+		out[o] = x1 + p * Math.cos(r);
+		out[o + 1] = y1 + p * Math.sin(r);
+		out[o + 2] = r;
+	}
+
+	private function addCurvePosition(p:Float, x1:Float, y1:Float, cx1:Float, cy1:Float, cx2:Float, cy2:Float, x2:Float, y2:Float, out:Vector<Float>, o:Int,
+			tangents:Bool):Void {
+		if (p == 0 || Math.isNaN(p)) {
+			out[o] = x1;
+			out[o + 1] = y1;
+			out[o + 2] = Math.atan2(cy1 - y1, cx1 - x1);
+			return;
+		}
+		var tt:Float = p * p;
+		var ttt:Float = tt * p;
+		var u:Float = 1 - p;
+		var uu:Float = u * u;
+		var uuu:Float = uu * u;
+		var ut:Float = u * p;
+		var ut3:Float = ut * 3;
+		var uut3:Float = u * ut3;
+		var utt3:Float = ut3 * p;
+		var x:Float = x1 * uuu + cx1 * uut3 + cx2 * utt3 + x2 * ttt,
+			y:Float = y1 * uuu + cy1 * uut3 + cy2 * utt3 + y2 * ttt;
+		out[o] = x;
+		out[o + 1] = y;
+		if (tangents) {
+			if (p < 0.001) {
+				out[o + 2] = Math.atan2(cy1 - y1, cx1 - x1);
+			} else {
+				out[o + 2] = Math.atan2(y - (y1 * uu + cy1 * ut * 2 + cy2 * tt), x - (x1 * uu + cx1 * ut * 2 + cx2 * tt));
+			}
+		}
+	}
+
+	public var bones(get, never):Vector<Bone>;
+
+	private function get_bones():Vector<Bone> {
+		return _bones;
+	}
+
+	public var data(get, never):PathConstraintData;
+
+	private function get_data():PathConstraintData {
+		return _data;
+	}
+
+	public function toString():String {
+		return _data.name != null ? _data.name : "PathConstraint?";
+	}
+}

+ 28 - 0
spine-haxe/spine-haxe/spine/PathConstraintData.hx

@@ -0,0 +1,28 @@
+package spine;
+
+import openfl.Vector;
+
+class PathConstraintData extends ConstraintData {
+	private var _bones:Vector<BoneData> = new Vector<BoneData>();
+
+	public var target:SlotData;
+	public var positionMode:PositionMode;
+	public var spacingMode:SpacingMode;
+	public var rotateMode:RotateMode;
+	public var offsetRotation:Float = 0;
+	public var position:Float = 0;
+	public var spacing:Float = 0;
+	public var mixRotate:Float = 0;
+	public var mixX:Float = 0;
+	public var mixY:Float = 0;
+
+	public function new(name:String) {
+		super(name, 0, false);
+	}
+
+	public var bones(get, never):Vector<BoneData>;
+
+	private function get_bones():Vector<BoneData> {
+		return _bones;
+	}
+}

+ 60 - 0
spine-haxe/spine-haxe/spine/Polygon.hx

@@ -0,0 +1,60 @@
+package spine;
+
+import openfl.Vector;
+
+class Polygon {
+	public var vertices:Vector<Float> = new Vector<Float>();
+
+	public function new() {}
+
+	/** Returns true if the polygon contains the point. */
+	public function containsPoint(x:Float, y:Float):Bool {
+		var nn:Int = vertices.length;
+
+		var prevIndex:Int = nn - 2;
+		var inside:Bool = false;
+		var ii:Int = 0;
+		while (ii < nn) {
+			var vertexY:Float = vertices[ii + 1];
+			var prevY:Float = vertices[prevIndex + 1];
+			if ((vertexY < y && prevY >= y) || (prevY < y && vertexY >= y)) {
+				var vertexX:Float = vertices[ii];
+				if (vertexX + (y - vertexY) / (prevY - vertexY) * (vertices[prevIndex] - vertexX) < x)
+					inside = !inside;
+			}
+			prevIndex = ii;
+
+			ii += 2;
+		}
+
+		return inside;
+	}
+
+	/** Returns true if the polygon contains the line segment. */
+	public function intersectsSegment(x1:Float, y1:Float, x2:Float, y2:Float):Bool {
+		var nn:Int = vertices.length;
+
+		var width12:Float = x1 - x2, height12:Float = y1 - y2;
+		var det1:Float = x1 * y2 - y1 * x2;
+		var x3:Float = vertices[nn - 2];
+		var y3:Float = vertices[nn - 1];
+		var ii:Int = 0;
+		while (ii < nn) {
+			var x4:Float = vertices[ii], y4:Float = vertices[ii + 1];
+			var det2:Float = x3 * y4 - y3 * x4;
+			var width34:Float = x3 - x4, height34:Float = y3 - y4;
+			var det3:Float = width12 * height34 - height12 * width34;
+			var x:Float = (det1 * width34 - width12 * det2) / det3;
+			if (((x >= x3 && x <= x4) || (x >= x4 && x <= x3)) && ((x >= x1 && x <= x2) || (x >= x2 && x <= x1))) {
+				var y:Float = (det1 * height34 - height12 * det2) / det3;
+				if (((y >= y3 && y <= y4) || (y >= y4 && y <= y3)) && ((y >= y1 && y <= y2) || (y >= y2 && y <= y1)))
+					return true;
+			}
+			x3 = x4;
+			y3 = y4;
+
+			ii += 2;
+		}
+		return false;
+	}
+}

+ 34 - 0
spine-haxe/spine-haxe/spine/Pool.hx

@@ -0,0 +1,34 @@
+package spine;
+
+import openfl.utils.Function;
+import openfl.Vector;
+
+@:generic class Pool<T> {
+	private var items:Vector<T>;
+	private var instantiator:Function;
+
+	public function new(instantiator:Void->T) {
+		this.items = new Vector<T>();
+		this.instantiator = instantiator;
+	}
+
+	public function obtain():T {
+		return this.items.length > 0 ? this.items.pop() : this.instantiator();
+	}
+
+	public function free(item:T):Void {
+		if (Std.isOfType(item, Poolable))
+			cast(item, Poolable).reset();
+		items.push(item);
+	}
+
+	public function freeAll(items:Vector<T>):Void {
+		for (item in items) {
+			free(item);
+		}
+	}
+
+	public function clear():Void {
+		items.length = 0;
+	}
+}

+ 5 - 0
spine-haxe/spine-haxe/spine/Poolable.hx

@@ -0,0 +1,5 @@
+package spine;
+
+interface Poolable {
+	function reset():Void;
+}

+ 24 - 0
spine-haxe/spine-haxe/spine/PositionMode.hx

@@ -0,0 +1,24 @@
+package spine;
+
+import openfl.Vector;
+
+class PositionMode {
+	public static var fixed(default, never):PositionMode = new PositionMode("fixed");
+	public static var percent(default, never):PositionMode = new PositionMode("percent");
+
+	public static var values(default, never):Vector<PositionMode> = Vector.ofArray([fixed, percent]);
+
+	public var name(default, null):String;
+
+	public function new(name:String) {
+		this.name = name;
+	}
+
+	public static function fromName(name:String):PositionMode {
+		for (value in values) {
+			if (value.name == name)
+				return value;
+		}
+		return null;
+	}
+}

+ 25 - 0
spine-haxe/spine-haxe/spine/RotateMode.hx

@@ -0,0 +1,25 @@
+package spine;
+
+import openfl.Vector;
+
+class RotateMode {
+	public static var tangent(default, never):RotateMode = new RotateMode("tangent");
+	public static var chain(default, never):RotateMode = new RotateMode("chain");
+	public static var chainScale(default, never):RotateMode = new RotateMode("chainScale");
+
+	public static var values(default, never):Vector<RotateMode> = Vector.ofArray([tangent, chain, chainScale]);
+
+	public var name(default, null):String;
+
+	public function new(name:String) {
+		this.name = name;
+	}
+
+	public static function fromName(name:String):RotateMode {
+		for (value in values) {
+			if (value.name == name)
+				return value;
+		}
+		return null;
+	}
+}

+ 612 - 0
spine-haxe/spine-haxe/spine/Skeleton.hx

@@ -0,0 +1,612 @@
+package spine;
+
+import openfl.errors.ArgumentError;
+import openfl.utils.Dictionary;
+import openfl.Vector;
+import spine.attachments.Attachment;
+import spine.attachments.MeshAttachment;
+import spine.attachments.PathAttachment;
+import spine.attachments.RegionAttachment;
+
+class Skeleton {
+	private var _data:SkeletonData;
+
+	public var bones:Vector<Bone>;
+	public var slots:Vector<Slot>;
+	public var drawOrder:Vector<Slot>;
+	public var ikConstraints:Vector<IkConstraint>;
+	public var transformConstraints:Vector<TransformConstraint>;
+	public var pathConstraints:Vector<PathConstraint>;
+
+	private var _updateCache:Vector<Updatable> = new Vector<Updatable>();
+	private var _skin:Skin;
+
+	public var color:Color = new Color(1, 1, 1, 1);
+	public var time:Float = 0;
+	public var scaleX:Float = 1;
+	public var scaleY:Float = 1;
+	public var x:Float = 0;
+	public var y:Float = 0;
+
+	public function new(data:SkeletonData) {
+		if (data == null) {
+			throw new ArgumentError("data cannot be null.");
+		}
+		_data = data;
+
+		bones = new Vector<Bone>();
+		for (boneData in data.bones) {
+			var bone:Bone;
+			if (boneData.parent == null) {
+				bone = new Bone(boneData, this, null);
+			} else {
+				var parent:Bone = bones[boneData.parent.index];
+				bone = new Bone(boneData, this, parent);
+				parent.children.push(bone);
+			}
+			bones.push(bone);
+		}
+
+		slots = new Vector<Slot>();
+		drawOrder = new Vector<Slot>();
+		for (slotData in data.slots) {
+			var bone = bones[slotData.boneData.index];
+			var slot:Slot = new Slot(slotData, bone);
+			slots.push(slot);
+			drawOrder.push(slot);
+		}
+
+		ikConstraints = new Vector<IkConstraint>();
+		for (ikConstraintData in data.ikConstraints) {
+			ikConstraints.push(new IkConstraint(ikConstraintData, this));
+		}
+
+		transformConstraints = new Vector<TransformConstraint>();
+		for (transformConstraintData in data.transformConstraints) {
+			transformConstraints.push(new TransformConstraint(transformConstraintData, this));
+		}
+
+		pathConstraints = new Vector<PathConstraint>();
+		for (pathConstraintData in data.pathConstraints) {
+			pathConstraints.push(new PathConstraint(pathConstraintData, this));
+		}
+
+		updateCache();
+	}
+
+	/** Caches information about bones and constraints. Must be called if bones, constraints, or weighted path attachments are
+	 * added or removed. */
+	public function updateCache():Void {
+		_updateCache.length = 0;
+
+		for (bone in bones) {
+			bone.sorted = bone.data.skinRequired;
+			bone.active = !bone.sorted;
+		}
+
+		if (skin != null) {
+			var skinBones:Vector<BoneData> = skin.bones;
+			for (i in 0...skin.bones.length) {
+				var bone:Bone = bones[skinBones[i].index];
+				do {
+					bone.sorted = false;
+					bone.active = true;
+					bone = bone.parent;
+				} while (bone != null);
+			}
+		}
+
+		// IK first, lowest hierarchy depth first.
+		var ikCount:Int = ikConstraints.length;
+		var transformCount:Int = transformConstraints.length;
+		var pathCount:Int = pathConstraints.length;
+		var constraintCount:Int = ikCount + transformCount + pathCount;
+
+		var continueOuter:Bool;
+		for (i in 0...constraintCount) {
+			continueOuter = false;
+			for (ikConstraint in ikConstraints) {
+				if (ikConstraint.data.order == i) {
+					sortIkConstraint(ikConstraint);
+					continueOuter = true;
+					break;
+				}
+			}
+			if (continueOuter)
+				continue;
+			for (transformConstraint in transformConstraints) {
+				if (transformConstraint.data.order == i) {
+					sortTransformConstraint(transformConstraint);
+					continueOuter = true;
+					break;
+				}
+			}
+			if (continueOuter)
+				continue;
+			for (pathConstraint in pathConstraints) {
+				if (pathConstraint.data.order == i) {
+					sortPathConstraint(pathConstraint);
+					break;
+				}
+			}
+		}
+
+		for (bone in bones) {
+			sortBone(bone);
+		}
+	}
+
+	private static function contains(list:Vector<ConstraintData>, element:ConstraintData):Bool {
+		return list.indexOf(element) != -1;
+	}
+
+	private function sortIkConstraint(constraint:IkConstraint):Void {
+		constraint.active = constraint.target.isActive()
+			&& (!constraint.data.skinRequired || (this.skin != null && contains(this.skin.constraints, constraint.data)));
+		if (!constraint.active)
+			return;
+
+		var target:Bone = constraint.target;
+		sortBone(target);
+
+		var constrained:Vector<Bone> = constraint.bones;
+		var parent:Bone = constrained[0];
+		sortBone(parent);
+
+		if (constrained.length == 1) {
+			_updateCache.push(constraint);
+			sortReset(parent.children);
+		} else {
+			var child:Bone = constrained[constrained.length - 1];
+			sortBone(child);
+
+			_updateCache.push(constraint);
+
+			sortReset(parent.children);
+			child.sorted = true;
+		}
+
+		_updateCache.push(constraint);
+
+		sortReset(parent.children);
+		constrained[constrained.length - 1].sorted = true;
+	}
+
+	private function sortPathConstraint(constraint:PathConstraint):Void {
+		constraint.active = constraint.target.bone.isActive()
+			&& (!constraint.data.skinRequired || (this.skin != null && contains(this.skin.constraints, constraint.data)));
+		if (!constraint.active)
+			return;
+
+		var slot:Slot = constraint.target;
+		var slotIndex:Int = slot.data.index;
+		var slotBone:Bone = slot.bone;
+		if (skin != null)
+			sortPathConstraintAttachment(skin, slotIndex, slotBone);
+		if (data.defaultSkin != null && data.defaultSkin != skin) {
+			sortPathConstraintAttachment(data.defaultSkin, slotIndex, slotBone);
+		}
+		for (i in 0...data.skins.length) {
+			sortPathConstraintAttachment(data.skins[i], slotIndex, slotBone);
+		}
+
+		var attachment:Attachment = slot.attachment;
+		if (Std.isOfType(attachment, PathAttachment))
+			sortPathConstraintAttachment2(attachment, slotBone);
+
+		var constrainedBones:Vector<Bone> = constraint.bones;
+		for (bone in constrainedBones) {
+			sortBone(bone);
+		}
+
+		_updateCache.push(constraint);
+
+		for (bone in constrainedBones) {
+			sortReset(bone.children);
+		}
+		for (bone in constrainedBones) {
+			bone.sorted = true;
+		}
+	}
+
+	private function sortTransformConstraint(constraint:TransformConstraint):Void {
+		constraint.active = constraint.target.isActive()
+			&& (!constraint.data.skinRequired || (this.skin != null && contains(this.skin.constraints, constraint.data)));
+		if (!constraint.active)
+			return;
+
+		sortBone(constraint.target);
+
+		var constrainedBones:Vector<Bone> = constraint.bones;
+		if (constraint.data.local) {
+			for (bone in constrainedBones) {
+				sortBone(bone.parent);
+				sortBone(bone);
+			}
+		} else {
+			for (bone in constrainedBones) {
+				sortBone(bone);
+			}
+		}
+
+		_updateCache.push(constraint);
+		for (bone in constrainedBones) {
+			sortReset(bone.children);
+		}
+		for (bone in constrainedBones) {
+			bone.sorted = true;
+		}
+	}
+
+	private function sortPathConstraintAttachment(skin:Skin, slotIndex:Int, slotBone:Bone):Void {
+		var dict:Dictionary<String, Attachment> = skin.attachments[slotIndex];
+		if (dict != null) {
+			for (attachment in dict.each()) {
+				sortPathConstraintAttachment2(attachment, slotBone);
+			}
+		}
+	}
+
+	private function sortPathConstraintAttachment2(attachment:Attachment, slotBone:Bone):Void {
+		var pathAttachment:PathAttachment = cast(attachment, PathAttachment);
+		if (pathAttachment == null)
+			return;
+		var pathBones:Vector<Int> = pathAttachment.bones;
+		if (pathBones == null) {
+			sortBone(slotBone);
+		} else {
+			var i:Int = 0;
+			var n:Int = pathBones.length;
+			while (i < n) {
+				var nn:Int = pathBones[i++];
+				nn += i;
+				while (i < nn) {
+					sortBone(bones[pathBones[i++]]);
+				}
+			}
+		}
+	}
+
+	private function sortBone(bone:Bone):Void {
+		if (bone.sorted)
+			return;
+		var parent:Bone = bone.parent;
+		if (parent != null)
+			sortBone(parent);
+		bone.sorted = true;
+		_updateCache.push(bone);
+	}
+
+	private function sortReset(bones:Vector<Bone>):Void {
+		for (bone in bones) {
+			if (!bone.active)
+				continue;
+			if (bone.sorted)
+				sortReset(bone.children);
+			bone.sorted = false;
+		}
+	}
+
+	/** Updates the world transform for each bone and applies constraints. */
+	public function updateWorldTransform():Void {
+		for (bone in bones) {
+			bone.ax = bone.x;
+			bone.ay = bone.y;
+			bone.arotation = bone.rotation;
+			bone.ascaleX = bone.scaleX;
+			bone.ascaleY = bone.scaleY;
+			bone.ashearX = bone.shearX;
+			bone.ashearY = bone.shearY;
+		}
+
+		for (updatable in _updateCache) {
+			updatable.update();
+		}
+	}
+
+	public function updateWorldTransformWith(parent:Bone):Void {
+		// Apply the parent bone transform to the root bone. The root bone always inherits scale, rotation and reflection.
+		var rootBone:Bone = rootBone;
+		var pa:Float = parent.a,
+			pb:Float = parent.b,
+			pc:Float = parent.c,
+			pd:Float = parent.d;
+		rootBone.worldX = pa * x + pb * y + parent.worldX;
+		rootBone.worldY = pc * x + pd * y + parent.worldY;
+
+		var rotationY:Float = rootBone.rotation + 90 + rootBone.shearY;
+		var la:Float = MathUtils.cosDeg(rootBone.rotation + rootBone.shearX) * rootBone.scaleX;
+		var lb:Float = MathUtils.cosDeg(rotationY) * rootBone.scaleY;
+		var lc:Float = MathUtils.sinDeg(rootBone.rotation + rootBone.shearX) * rootBone.scaleX;
+		var ld:Float = MathUtils.sinDeg(rotationY) * rootBone.scaleY;
+		rootBone.a = (pa * la + pb * lc) * scaleX;
+		rootBone.b = (pa * lb + pb * ld) * scaleX;
+		rootBone.c = (pc * la + pd * lc) * scaleY;
+		rootBone.d = (pc * lb + pd * ld) * scaleY;
+
+		// Update everything except root bone.
+		for (updatable in _updateCache) {
+			if (updatable != rootBone)
+				updatable.update();
+		}
+	}
+
+	/** Sets the bones, constraints, and slots to their setup pose values. */
+	public function setToSetupPose():Void {
+		setBonesToSetupPose();
+		setSlotsToSetupPose();
+	}
+
+	/** Sets the bones and constraints to their setup pose values. */
+	public function setBonesToSetupPose():Void {
+		for (bone in bones) {
+			bone.setToSetupPose();
+		}
+
+		for (ikConstraint in ikConstraints) {
+			ikConstraint.mix = ikConstraint.data.mix;
+			ikConstraint.softness = ikConstraint.data.softness;
+			ikConstraint.bendDirection = ikConstraint.data.bendDirection;
+			ikConstraint.compress = ikConstraint.data.compress;
+			ikConstraint.stretch = ikConstraint.data.stretch;
+		}
+
+		for (transformConstraint in transformConstraints) {
+			transformConstraint.mixRotate = transformConstraint.data.mixRotate;
+			transformConstraint.mixX = transformConstraint.data.mixX;
+			transformConstraint.mixY = transformConstraint.data.mixY;
+			transformConstraint.mixScaleX = transformConstraint.data.mixScaleX;
+			transformConstraint.mixScaleY = transformConstraint.data.mixScaleY;
+			transformConstraint.mixShearY = transformConstraint.data.mixShearY;
+		}
+
+		for (pathConstraint in pathConstraints) {
+			pathConstraint.position = pathConstraint.data.position;
+			pathConstraint.spacing = pathConstraint.data.spacing;
+			pathConstraint.mixRotate = pathConstraint.data.mixRotate;
+			pathConstraint.mixX = pathConstraint.data.mixX;
+			pathConstraint.mixY = pathConstraint.data.mixY;
+		}
+	}
+
+	public function setSlotsToSetupPose():Void {
+		var i:Int = 0;
+		for (slot in slots) {
+			drawOrder[i++] = slot;
+			slot.setToSetupPose();
+		}
+	}
+
+	public var data(get, never):SkeletonData;
+
+	private function get_data():SkeletonData {
+		return _data;
+	}
+
+	public var getUpdateCache(get, never):Vector<Updatable>;
+
+	private function get_getUpdateCache():Vector<Updatable> {
+		return _updateCache;
+	}
+
+	public var rootBone(get, never):Bone;
+
+	private function get_rootBone():Bone {
+		if (bones.length == 0)
+			return null;
+		return bones[0];
+	}
+
+	/** @return May be null. */
+	public function findBone(boneName:String):Bone {
+		if (boneName == null) {
+			throw new ArgumentError("boneName cannot be null.");
+		}
+		for (bone in bones) {
+			if (bone.data.name == boneName)
+				return bone;
+		}
+		return null;
+	}
+
+	/** @return -1 if the bone was not found. */
+	public function findBoneIndex(boneName:String):Int {
+		if (boneName == null) {
+			throw new ArgumentError("boneName cannot be null.");
+		}
+		var i:Int = 0;
+		for (bone in bones) {
+			if (bone.data.name == boneName)
+				return i;
+			i++;
+		}
+		return -1;
+	}
+
+	/** @return May be null. */
+	public function findSlot(slotName:String):Slot {
+		if (slotName == null) {
+			throw new ArgumentError("slotName cannot be null.");
+		}
+		for (slot in slots) {
+			if (slot.data.name == slotName)
+				return slot;
+		}
+		return null;
+	}
+
+	public var skinName(get, set):String;
+
+	private function set_skinName(skinName:String):String {
+		var skin:Skin = data.findSkin(skinName);
+		if (skin == null)
+			throw new ArgumentError("Skin not found: " + skinName);
+		this.skin = skin;
+		return skinName;
+	}
+
+	/** @return May be null. */
+	private function get_skinName():String {
+		return _skin == null ? null : _skin.name;
+	}
+
+	public var skin(get, set):Skin;
+
+	private function get_skin():Skin {
+		return _skin;
+	}
+
+	/** Sets the skin used to look up attachments before looking in the {@link SkeletonData#getDefaultSkin() default skin}.
+	 * Attachments from the new skin are attached if the corresponding attachment from the old skin was attached. If there was
+	 * no old skin, each slot's setup mode attachment is attached from the new skin.
+	 * @param newSkin May be null. */
+	private function set_skin(newSkin:Skin):Skin {
+		if (newSkin == _skin)
+			return null;
+		if (newSkin != null) {
+			if (skin != null) {
+				newSkin.attachAll(this, skin);
+			} else {
+				var i:Int = 0;
+				for (slot in slots) {
+					var name:String = slot.data.attachmentName;
+					if (name != null) {
+						var attachment:Attachment = newSkin.getAttachment(i, name);
+						if (attachment != null)
+							slot.attachment = attachment;
+					}
+					i++;
+				}
+			}
+		}
+		_skin = newSkin;
+		updateCache();
+		return _skin;
+	}
+
+	/** @return May be null. */
+	public function getAttachmentForSlotName(slotName:String, attachmentName:String):Attachment {
+		return getAttachmentForSlotIndex(data.findSlot(slotName).index, attachmentName);
+	}
+
+	/** @return May be null. */
+	public function getAttachmentForSlotIndex(slotIndex:Int, attachmentName:String):Attachment {
+		if (attachmentName == null)
+			throw new ArgumentError("attachmentName cannot be null.");
+		if (skin != null) {
+			var attachment:Attachment = skin.getAttachment(slotIndex, attachmentName);
+			if (attachment != null)
+				return attachment;
+		}
+		if (data.defaultSkin != null)
+			return data.defaultSkin.getAttachment(slotIndex, attachmentName);
+		return null;
+	}
+
+	/** @param attachmentName May be null. */
+	public function setAttachment(slotName:String, attachmentName:String):Void {
+		if (slotName == null)
+			throw new ArgumentError("slotName cannot be null.");
+		var i:Int = 0;
+		for (slot in slots) {
+			if (slot.data.name == slotName) {
+				var attachment:Attachment = null;
+				if (attachmentName != null) {
+					attachment = getAttachmentForSlotIndex(i, attachmentName);
+					if (attachment == null) {
+						throw new ArgumentError("Attachment not found: " + attachmentName + ", for slot: " + slotName);
+					}
+				}
+				slot.attachment = attachment;
+				return;
+			}
+			i++;
+		}
+		throw new ArgumentError("Slot not found: " + slotName);
+	}
+
+	/** @return May be null. */
+	public function findIkConstraint(constraintName:String):IkConstraint {
+		if (constraintName == null)
+			throw new ArgumentError("constraintName cannot be null.");
+		for (ikConstraint in ikConstraints) {
+			if (ikConstraint.data.name == constraintName)
+				return ikConstraint;
+		}
+		return null;
+	}
+
+	/** @return May be null. */
+	public function findTransformConstraint(constraintName:String):TransformConstraint {
+		if (constraintName == null)
+			throw new ArgumentError("constraintName cannot be null.");
+		for (transformConstraint in transformConstraints) {
+			if (transformConstraint.data.name == constraintName)
+				return transformConstraint;
+		}
+		return null;
+	}
+
+	/** @return May be null. */
+	public function findPathConstraint(constraintName:String):PathConstraint {
+		if (constraintName == null)
+			throw new ArgumentError("constraintName cannot be null.");
+		for (pathConstraint in pathConstraints) {
+			if (pathConstraint.data.name == constraintName)
+				return pathConstraint;
+		}
+		return null;
+	}
+
+	public function update(delta:Float):Void {
+		time += delta;
+	}
+
+	public function toString():String {
+		return _data.name != null ? _data.name : "Skeleton?";
+	}
+
+	public function getBounds(offset:Vector<Float>, size:Vector<Float>, temp:Vector<Float>):Void {
+		if (offset == null)
+			throw new ArgumentError("offset cannot be null.");
+		if (size == null)
+			throw new ArgumentError("size cannot be null.");
+		var minX:Float = Math.POSITIVE_INFINITY;
+		var minY:Float = Math.POSITIVE_INFINITY;
+		var maxX:Float = Math.NEGATIVE_INFINITY;
+		var maxY:Float = Math.NEGATIVE_INFINITY;
+		for (slot in drawOrder) {
+			var verticesLength:Int = 0;
+			var vertices:Vector<Float> = null;
+			var attachment:Attachment = slot.attachment;
+			if (Std.isOfType(attachment, RegionAttachment)) {
+				verticesLength = 8;
+				temp.length = verticesLength;
+				vertices = temp;
+				cast(attachment, RegionAttachment).computeWorldVertices(slot.bone, vertices, 0, 2);
+			} else if (Std.isOfType(attachment, MeshAttachment)) {
+				var mesh:MeshAttachment = cast(attachment, MeshAttachment);
+				verticesLength = mesh.worldVerticesLength;
+				temp.length = verticesLength;
+				vertices = temp;
+				mesh.computeWorldVertices(slot, 0, verticesLength, vertices, 0, 2);
+			}
+			if (vertices != null) {
+				var ii:Int = 0;
+				var nn:Int = vertices.length;
+				while (ii < nn) {
+					var x:Float = vertices[ii], y:Float = vertices[ii + 1];
+					minX = Math.min(minX, x);
+					minY = Math.min(minY, y);
+					maxX = Math.max(maxX, x);
+					maxY = Math.max(maxY, y);
+					ii += 2;
+				}
+			}
+		}
+		offset[0] = minX;
+		offset[1] = minY;
+		size[0] = maxX - minX;
+		size[1] = maxY - minY;
+	}
+}

+ 1205 - 0
spine-haxe/spine-haxe/spine/SkeletonBinary.hx

@@ -0,0 +1,1205 @@
+package spine;
+
+import openfl.errors.ArgumentError;
+import openfl.errors.Error;
+import openfl.utils.ByteArray;
+import openfl.Vector;
+import spine.animation.AlphaTimeline;
+import spine.animation.Animation;
+import spine.animation.AttachmentTimeline;
+import spine.animation.CurveTimeline1;
+import spine.animation.CurveTimeline2;
+import spine.animation.CurveTimeline;
+import spine.animation.DeformTimeline;
+import spine.animation.DrawOrderTimeline;
+import spine.animation.EventTimeline;
+import spine.animation.IkConstraintTimeline;
+import spine.animation.PathConstraintMixTimeline;
+import spine.animation.PathConstraintPositionTimeline;
+import spine.animation.PathConstraintSpacingTimeline;
+import spine.animation.RGB2Timeline;
+import spine.animation.RGBA2Timeline;
+import spine.animation.RGBATimeline;
+import spine.animation.RGBTimeline;
+import spine.animation.RotateTimeline;
+import spine.animation.ScaleTimeline;
+import spine.animation.ScaleXTimeline;
+import spine.animation.ScaleYTimeline;
+import spine.animation.ShearTimeline;
+import spine.animation.ShearXTimeline;
+import spine.animation.ShearYTimeline;
+import spine.animation.Timeline;
+import spine.animation.TransformConstraintTimeline;
+import spine.animation.TranslateTimeline;
+import spine.animation.TranslateXTimeline;
+import spine.animation.TranslateYTimeline;
+import spine.attachments.Attachment;
+import spine.attachments.AttachmentLoader;
+import spine.attachments.AttachmentType;
+import spine.attachments.BoundingBoxAttachment;
+import spine.attachments.ClippingAttachment;
+import spine.attachments.MeshAttachment;
+import spine.attachments.PathAttachment;
+import spine.attachments.PointAttachment;
+import spine.attachments.RegionAttachment;
+import spine.attachments.VertexAttachment;
+import StringTools;
+
+class SkeletonBinary {
+	public var attachmentLoader:AttachmentLoader;
+	public var scale:Float = 1;
+
+	private var linkedMeshes:Vector<LinkedMeshBinary> = new Vector<LinkedMeshBinary>();
+
+	private static inline var BONE_ROTATE:Int = 0;
+	private static inline var BONE_TRANSLATE:Int = 1;
+	private static inline var BONE_TRANSLATEX:Int = 2;
+	private static inline var BONE_TRANSLATEY:Int = 3;
+	private static inline var BONE_SCALE:Int = 4;
+	private static inline var BONE_SCALEX:Int = 5;
+	private static inline var BONE_SCALEY:Int = 6;
+	private static inline var BONE_SHEAR:Int = 7;
+	private static inline var BONE_SHEARX:Int = 8;
+	private static inline var BONE_SHEARY:Int = 9;
+
+	private static inline var SLOT_ATTACHMENT:Int = 0;
+	private static inline var SLOT_RGBA:Int = 1;
+	private static inline var SLOT_RGB:Int = 2;
+	private static inline var SLOT_RGBA2:Int = 3;
+	private static inline var SLOT_RGB2:Int = 4;
+	private static inline var SLOT_ALPHA:Int = 5;
+
+	private static inline var PATH_POSITION:Int = 0;
+	private static inline var PATH_SPACING:Int = 1;
+	private static inline var PATH_MIX:Int = 2;
+
+	private static inline var CURVE_LINEAR:Int = 0;
+	private static inline var CURVE_STEPPED:Int = 1;
+	private static inline var CURVE_BEZIER:Int = 2;
+
+	public function new(attachmentLoader:AttachmentLoader = null) {
+		this.attachmentLoader = attachmentLoader;
+	}
+
+	public function readSkeletonData(object:ByteArray):SkeletonData {
+		if (object == null)
+			throw new ArgumentError("Object cannot be null");
+		if (!Std.isOfType(object, ByteArrayData))
+			throw new ArgumentError("Object must be ByteArrayData");
+
+		var skeletonData:SkeletonData = new SkeletonData();
+		skeletonData.name = null;
+
+		var input:BinaryInput = new BinaryInput(object);
+
+		var lowHash:Int = input.readInt32();
+		var highHash:Int = input.readInt32();
+		skeletonData.hash = highHash == 0 && lowHash == 0 ? null : StringTools.hex(highHash) + StringTools.hex(lowHash);
+		skeletonData.version = input.readString();
+		skeletonData.x = input.readFloat();
+		skeletonData.y = input.readFloat();
+		skeletonData.width = input.readFloat();
+		skeletonData.height = input.readFloat();
+
+		var nonessential:Bool = input.readBoolean();
+		if (nonessential) {
+			skeletonData.fps = input.readFloat();
+			skeletonData.imagesPath = input.readString();
+			skeletonData.audioPath = input.readString();
+		}
+
+		var n:Int = 0;
+		var nn:Int = 0;
+
+		// Strings.
+		n = input.readInt(true);
+		for (i in 0...n) {
+			input.strings.push(input.readString());
+		}
+
+		// Bones.
+		n = input.readInt(true);
+		for (i in 0...n) {
+			var boneName:String = input.readString();
+			var boneParent:BoneData = i == 0 ? null : skeletonData.bones[input.readInt(true)];
+			var boneData:BoneData = new BoneData(i, boneName, boneParent);
+			boneData.rotation = input.readFloat();
+			boneData.x = input.readFloat() * scale;
+			boneData.y = input.readFloat() * scale;
+			boneData.scaleX = input.readFloat();
+			boneData.scaleY = input.readFloat();
+			boneData.shearX = input.readFloat();
+			boneData.shearY = input.readFloat();
+			boneData.length = input.readFloat() * scale;
+			boneData.transformMode = TransformMode.values[input.readInt(true)];
+			boneData.skinRequired = input.readBoolean();
+			if (nonessential)
+				boneData.color.setFromRgba8888(input.readInt32());
+			skeletonData.bones.push(boneData);
+		}
+
+		// Slots.
+		n = input.readInt(true);
+		for (i in 0...n) {
+			var slotName:String = input.readString();
+			var slotBoneData:BoneData = skeletonData.bones[input.readInt(true)];
+			var slotData:SlotData = new SlotData(i, slotName, slotBoneData);
+			slotData.color.setFromRgba8888(input.readInt32());
+
+			var darkColor:Int = input.readInt32();
+			if (darkColor != -1) {
+				slotData.darkColor = new Color(0, 0, 0);
+				slotData.darkColor.setFromRgb888(darkColor);
+			}
+
+			slotData.attachmentName = input.readStringRef();
+			slotData.blendMode = BlendMode.values[input.readInt(true)];
+			skeletonData.slots.push(slotData);
+		}
+
+		// IK constraints.
+		n = input.readInt(true);
+		for (i in 0...n) {
+			var ikData:IkConstraintData = new IkConstraintData(input.readString());
+			ikData.order = input.readInt(true);
+			ikData.skinRequired = input.readBoolean();
+			nn = input.readInt(true);
+			for (ii in 0...nn) {
+				ikData.bones.push(skeletonData.bones[input.readInt(true)]);
+			}
+			ikData.target = skeletonData.bones[input.readInt(true)];
+			ikData.mix = input.readFloat();
+			ikData.softness = input.readFloat() * scale;
+			ikData.bendDirection = input.readByte();
+			ikData.compress = input.readBoolean();
+			ikData.stretch = input.readBoolean();
+			ikData.uniform = input.readBoolean();
+			skeletonData.ikConstraints.push(ikData);
+		}
+
+		// Transform constraints.
+		n = input.readInt(true);
+		for (i in 0...n) {
+			var transformData:TransformConstraintData = new TransformConstraintData(input.readString());
+			transformData.order = input.readInt(true);
+			transformData.skinRequired = input.readBoolean();
+			nn = input.readInt(true);
+			for (ii in 0...nn) {
+				transformData.bones.push(skeletonData.bones[input.readInt(true)]);
+			}
+			transformData.target = skeletonData.bones[input.readInt(true)];
+			transformData.local = input.readBoolean();
+			transformData.relative = input.readBoolean();
+			transformData.offsetRotation = input.readFloat();
+			transformData.offsetX = input.readFloat() * scale;
+			transformData.offsetY = input.readFloat() * scale;
+			transformData.offsetScaleX = input.readFloat();
+			transformData.offsetScaleY = input.readFloat();
+			transformData.offsetShearY = input.readFloat();
+			transformData.mixRotate = input.readFloat();
+			transformData.mixX = input.readFloat();
+			transformData.mixY = input.readFloat();
+			transformData.mixScaleX = input.readFloat();
+			transformData.mixScaleY = input.readFloat();
+			transformData.mixShearY = input.readFloat();
+			skeletonData.transformConstraints.push(transformData);
+		}
+
+		// Path constraints.
+		n = input.readInt(true);
+		for (i in 0...n) {
+			var pathData:PathConstraintData = new PathConstraintData(input.readString());
+			pathData.order = input.readInt(true);
+			pathData.skinRequired = input.readBoolean();
+			nn = input.readInt(true);
+			for (ii in 0...nn) {
+				pathData.bones.push(skeletonData.bones[input.readInt(true)]);
+			}
+			pathData.target = skeletonData.slots[input.readInt(true)];
+			pathData.positionMode = PositionMode.values[input.readInt(true)];
+			pathData.spacingMode = SpacingMode.values[input.readInt(true)];
+			pathData.rotateMode = RotateMode.values[input.readInt(true)];
+			pathData.offsetRotation = input.readFloat();
+			pathData.position = input.readFloat();
+			if (pathData.positionMode == PositionMode.fixed)
+				pathData.position *= scale;
+			pathData.spacing = input.readFloat();
+			if (pathData.spacingMode == SpacingMode.length || pathData.spacingMode == SpacingMode.fixed)
+				pathData.spacing *= scale;
+			pathData.mixRotate = input.readFloat();
+			pathData.mixX = input.readFloat();
+			pathData.mixY = input.readFloat();
+			skeletonData.pathConstraints.push(pathData);
+		}
+
+		// Default skin.
+		var defaultSkin:Skin = readSkin(input, skeletonData, true, nonessential);
+		if (defaultSkin != null) {
+			skeletonData.defaultSkin = defaultSkin;
+			skeletonData.skins.push(defaultSkin);
+		}
+
+		// Skins.
+		{
+			var i:Int = skeletonData.skins.length;
+			n = i + input.readInt(true);
+			while (i < n) {
+				skeletonData.skins.push(readSkin(input, skeletonData, false, nonessential));
+				i++;
+			}
+		}
+
+		// Linked meshes.
+		for (linkedMesh in linkedMeshes) {
+			var skin:Skin = linkedMesh.skin == null ? skeletonData.defaultSkin : skeletonData.findSkin(linkedMesh.skin);
+			if (skin == null)
+				throw new Error("Skin not found: " + linkedMesh.skin);
+			var parent:Attachment = skin.getAttachment(linkedMesh.slotIndex, linkedMesh.parent);
+			if (parent == null)
+				throw new Error("Parent mesh not found: " + linkedMesh.parent);
+			linkedMesh.mesh.deformAttachment = linkedMesh.inheritDeform ? cast(parent, VertexAttachment) : linkedMesh.mesh;
+			linkedMesh.mesh.parentMesh = cast(parent, MeshAttachment);
+			linkedMesh.mesh.updateUVs();
+		}
+		linkedMeshes.length = 0;
+
+		// Events.
+		n = input.readInt(true);
+		for (i in 0...n) {
+			var data:EventData = new EventData(input.readStringRef());
+			data.intValue = input.readInt(false);
+			data.floatValue = input.readFloat();
+			data.stringValue = input.readString();
+			data.audioPath = input.readString();
+			if (data.audioPath != null) {
+				data.volume = input.readFloat();
+				data.balance = input.readFloat();
+			}
+			skeletonData.events.push(data);
+		}
+
+		// Animations.
+		n = input.readInt(true);
+		for (i in 0...n) {
+			skeletonData.animations.push(readAnimation(input, input.readString(), skeletonData));
+		}
+		return skeletonData;
+	}
+
+	private function readSkin(input:BinaryInput, skeletonData:SkeletonData, defaultSkin:Bool, nonessential:Bool):Skin {
+		var skin:Skin = null;
+		var slotCount:Int = 0;
+
+		if (defaultSkin) {
+			slotCount = input.readInt(true);
+			if (slotCount == 0)
+				return null;
+			skin = new Skin("default");
+		} else {
+			skin = new Skin(input.readStringRef());
+			skin.bones.length = input.readInt(true);
+			for (i in 0...skin.bones.length) {
+				skin.bones[i] = skeletonData.bones[input.readInt(true)];
+			}
+
+			for (i in 0...input.readInt(true)) {
+				skin.constraints.push(skeletonData.ikConstraints[input.readInt(true)]);
+			}
+			for (i in 0...input.readInt(true)) {
+				skin.constraints.push(skeletonData.transformConstraints[input.readInt(true)]);
+			}
+			for (i in 0...input.readInt(true)) {
+				skin.constraints.push(skeletonData.pathConstraints[input.readInt(true)]);
+			}
+
+			slotCount = input.readInt(true);
+		}
+
+		for (i in 0...slotCount) {
+			var slotIndex:Int = input.readInt(true);
+			for (ii in 0...input.readInt(true)) {
+				var name:String = input.readStringRef();
+				var attachment:Attachment = readAttachment(input, skeletonData, skin, slotIndex, name, nonessential);
+				if (attachment != null)
+					skin.setAttachment(slotIndex, name, attachment);
+			}
+		}
+		return skin;
+	}
+
+	private function readAttachment(input:BinaryInput, skeletonData:SkeletonData, skin:Skin, slotIndex:Int, attachmentName:String,
+			nonessential:Bool):Attachment {
+		var vertexCount:Int;
+		var vertices:Vertices;
+		var path:String;
+		var rotation:Float;
+		var x:Float;
+		var y:Float;
+		var scaleX:Float;
+		var scaleY:Float;
+		var width:Float = 0;
+		var height:Float = 0;
+		var color:Int;
+		var mesh:MeshAttachment;
+
+		var name:String = input.readStringRef();
+		if (name == null)
+			name = attachmentName;
+
+		switch (AttachmentType.values[input.readByte()]) {
+			case AttachmentType.region:
+				path = input.readStringRef();
+				rotation = input.readFloat();
+				x = input.readFloat();
+				y = input.readFloat();
+				scaleX = input.readFloat();
+				scaleY = input.readFloat();
+				width = input.readFloat();
+				height = input.readFloat();
+				color = input.readInt32();
+
+				if (path == null)
+					path = name;
+				var region:RegionAttachment = attachmentLoader.newRegionAttachment(skin, name, path);
+				if (region == null)
+					return null;
+				region.path = path;
+				region.x = x * scale;
+				region.y = y * scale;
+				region.scaleX = scaleX;
+				region.scaleY = scaleY;
+				region.rotation = rotation;
+				region.width = width * scale;
+				region.height = height * scale;
+				region.color.setFromRgba8888(color);
+				region.updateOffset();
+				return region;
+			case AttachmentType.boundingbox:
+				vertexCount = input.readInt(true);
+				vertices = readVertices(input, vertexCount);
+				color = nonessential ? input.readInt32() : 0;
+
+				var box:BoundingBoxAttachment = attachmentLoader.newBoundingBoxAttachment(skin, name);
+				if (box == null)
+					return null;
+				box.worldVerticesLength = vertexCount << 1;
+				box.vertices = vertices.vertices;
+				if (vertices.bones.length > 0)
+					box.bones = vertices.bones;
+				if (nonessential)
+					box.color.setFromRgba8888(color);
+				return box;
+			case AttachmentType.mesh:
+				path = input.readStringRef();
+				color = input.readInt32();
+				vertexCount = input.readInt(true);
+				var uvs:Vector<Float> = readFloatArray(input, vertexCount << 1, 1);
+				var triangles:Vector<Int> = readShortArray(input);
+				vertices = readVertices(input, vertexCount);
+				var hullLength:Int = input.readInt(true);
+				var edges:Vector<Int> = null;
+				if (nonessential) {
+					edges = readShortArray(input);
+					width = input.readFloat();
+					height = input.readFloat();
+				}
+
+				if (path == null)
+					path = name;
+				mesh = attachmentLoader.newMeshAttachment(skin, name, path);
+				if (mesh == null)
+					return null;
+				mesh.path = path;
+				mesh.color.setFromRgba8888(color);
+				if (vertices.bones.length > 0)
+					mesh.bones = vertices.bones;
+				mesh.vertices = vertices.vertices;
+				mesh.worldVerticesLength = vertexCount << 1;
+				mesh.triangles = triangles;
+				mesh.regionUVs = uvs;
+				mesh.updateUVs();
+				mesh.hullLength = hullLength << 1;
+				if (nonessential) {
+					mesh.edges = edges;
+					mesh.width = width * scale;
+					mesh.height = height * scale;
+				}
+				return mesh;
+			case AttachmentType.linkedmesh:
+				path = input.readStringRef();
+				color = input.readInt32();
+				var skinName:String = input.readStringRef();
+				var parent:String = input.readStringRef();
+				var inheritDeform:Bool = input.readBoolean();
+				if (nonessential) {
+					width = input.readFloat();
+					height = input.readFloat();
+				}
+
+				if (path == null)
+					path = name;
+				mesh = attachmentLoader.newMeshAttachment(skin, name, path);
+				if (mesh == null)
+					return null;
+				mesh.path = path;
+				mesh.color.setFromRgba8888(color);
+				if (nonessential) {
+					mesh.width = width * scale;
+					mesh.height = height * scale;
+				}
+				this.linkedMeshes.push(new LinkedMeshBinary(mesh, skinName, slotIndex, parent, inheritDeform));
+				return mesh;
+			case AttachmentType.path:
+				var closed:Bool = input.readBoolean();
+				var constantSpeed:Bool = input.readBoolean();
+				vertexCount = input.readInt(true);
+				vertices = readVertices(input, vertexCount);
+				var lengths:Vector<Float> = new Vector<Float>();
+				lengths.length = Std.int(vertexCount / 3);
+				for (i in 0...lengths.length) {
+					lengths[i] = input.readFloat() * scale;
+				}
+				color = nonessential ? input.readInt32() : 0;
+
+				var pathAttachment:PathAttachment = attachmentLoader.newPathAttachment(skin, name);
+				if (pathAttachment == null)
+					return null;
+				pathAttachment.closed = closed;
+				pathAttachment.constantSpeed = constantSpeed;
+				pathAttachment.worldVerticesLength = vertexCount << 1;
+				pathAttachment.vertices = vertices.vertices;
+				if (vertices.bones.length > 0)
+					pathAttachment.bones = vertices.bones;
+				pathAttachment.lengths = lengths;
+				if (nonessential)
+					pathAttachment.color.setFromRgba8888(color);
+				return pathAttachment;
+			case AttachmentType.point:
+				rotation = input.readFloat();
+				x = input.readFloat();
+				y = input.readFloat();
+				color = nonessential ? input.readInt32() : 0;
+
+				var point:PointAttachment = attachmentLoader.newPointAttachment(skin, name);
+				if (point == null)
+					return null;
+				point.x = x * scale;
+				point.y = y * scale;
+				point.rotation = rotation;
+				if (nonessential)
+					point.color.setFromRgba8888(color);
+				return point;
+			case AttachmentType.clipping:
+				var endSlotIndex:Int = input.readInt(true);
+				vertexCount = input.readInt(true);
+				vertices = readVertices(input, vertexCount);
+				color = nonessential ? input.readInt32() : 0;
+
+				var clip:ClippingAttachment = attachmentLoader.newClippingAttachment(skin, name);
+				if (clip == null)
+					return null;
+				clip.endSlot = skeletonData.slots[endSlotIndex];
+				clip.worldVerticesLength = vertexCount << 1;
+				clip.vertices = vertices.vertices;
+				if (vertices.bones.length > 0)
+					clip.bones = vertices.bones;
+				if (nonessential)
+					clip.color.setFromRgba8888(color);
+				return clip;
+		}
+		return null;
+	}
+
+	private function readVertices(input:BinaryInput, vertexCount:Int):Vertices {
+		var verticesLength:Int = vertexCount << 1;
+		var vertices:Vertices = new Vertices();
+
+		var isWeighted:Bool = input.readBoolean();
+		if (!isWeighted) {
+			vertices.vertices = readFloatArray(input, verticesLength, scale);
+			return vertices;
+		}
+		var weights:Vector<Float> = new Vector<Float>();
+		var bonesArray:Vector<Int> = new Vector<Int>();
+		for (i in 0...vertexCount) {
+			var boneCount:Int = input.readInt(true);
+			bonesArray.push(boneCount);
+			for (ii in 0...boneCount) {
+				bonesArray.push(input.readInt(true));
+				weights.push(input.readFloat() * scale);
+				weights.push(input.readFloat() * scale);
+				weights.push(input.readFloat());
+			}
+		}
+		vertices.vertices = weights;
+		vertices.bones = bonesArray;
+		return vertices;
+	}
+
+	private function readFloatArray(input:BinaryInput, n:Int, scale:Float):Vector<Float> {
+		var array:Vector<Float> = new Vector<Float>();
+		if (scale == 1) {
+			for (i in 0...n) {
+				array.push(input.readFloat());
+			}
+		} else {
+			for (i in 0...n) {
+				array.push(input.readFloat() * scale);
+			}
+		}
+		return array;
+	}
+
+	private function readShortArray(input:BinaryInput):Vector<Int> {
+		var n:Int = input.readInt(true);
+		var array:Vector<Int> = new Vector<Int>();
+		for (i in 0...n) {
+			array.push(input.readShort());
+		}
+		return array;
+	}
+
+	private function readAnimation(input:BinaryInput, name:String, skeletonData:SkeletonData):Animation {
+		input.readInt(true); // Count of timelines.
+		var timelines:Vector<Timeline> = new Vector<Timeline>();
+		var i:Int = 0, n:Int = 0, ii:Int = 0, nn:Int = 0;
+
+		var index:Int, slotIndex:Int, timelineType:Int, timelineScale:Float;
+		var frameCount:Int,
+			frameLast:Int,
+			frame:Int,
+			bezierCount:Int,
+			bezier:Int;
+		var time:Float, time2:Float;
+
+		// Slot timelines.
+		var r:Float, g:Float, b:Float, a:Float;
+		var r2:Float, g2:Float, b2:Float, a2:Float;
+		var nr:Float, ng:Float, nb:Float, na:Float;
+		var nr2:Float, ng2:Float, nb2:Float, na2:Float;
+		for (i in 0...input.readInt(true)) {
+			slotIndex = input.readInt(true);
+			for (ii in 0...input.readInt(true)) {
+				timelineType = input.readByte();
+				frameCount = input.readInt(true);
+				frameLast = frameCount - 1;
+				switch (timelineType) {
+					case SkeletonBinary.SLOT_ATTACHMENT:
+						var attachmentTimeline:AttachmentTimeline = new AttachmentTimeline(frameCount, slotIndex);
+						attachmentTimeline.slotIndex = slotIndex;
+						for (frame in 0...frameCount) {
+							attachmentTimeline.setFrame(frame, input.readFloat(), input.readStringRef());
+						}
+						timelines.push(attachmentTimeline);
+					case SLOT_RGBA:
+						bezierCount = input.readInt(true);
+						var rgbaTimeline:RGBATimeline = new RGBATimeline(frameCount, bezierCount, slotIndex);
+
+						time = input.readFloat();
+						r = input.readUnsignedByte() / 255.0;
+						g = input.readUnsignedByte() / 255.0;
+						b = input.readUnsignedByte() / 255.0;
+						a = input.readUnsignedByte() / 255.0;
+
+						frame = 0;
+						bezier = 0;
+						while (true) {
+							rgbaTimeline.setFrame(frame, time, r, g, b, a);
+							if (frame == frameLast)
+								break;
+
+							time2 = input.readFloat();
+							r2 = input.readUnsignedByte() / 255.0;
+							g2 = input.readUnsignedByte() / 255.0;
+							b2 = input.readUnsignedByte() / 255.0;
+							a2 = input.readUnsignedByte() / 255.0;
+
+							switch (input.readByte()) {
+								case CURVE_STEPPED:
+									rgbaTimeline.setStepped(frame);
+								case CURVE_BEZIER:
+									setBezier(input, rgbaTimeline, bezier++, frame, 0, time, time2, r, r2, 1);
+									setBezier(input, rgbaTimeline, bezier++, frame, 1, time, time2, g, g2, 1);
+									setBezier(input, rgbaTimeline, bezier++, frame, 2, time, time2, b, b2, 1);
+									setBezier(input, rgbaTimeline, bezier++, frame, 3, time, time2, a, a2, 1);
+							}
+							time = time2;
+							r = r2;
+							g = g2;
+							b = b2;
+							a = a2;
+
+							frame++;
+						}
+						timelines.push(rgbaTimeline);
+					case SLOT_RGB:
+						bezierCount = input.readInt(true);
+						var rgbTimeline:RGBTimeline = new RGBTimeline(frameCount, bezierCount, slotIndex);
+
+						time = input.readFloat();
+						r = input.readUnsignedByte() / 255.0;
+						g = input.readUnsignedByte() / 255.0;
+						b = input.readUnsignedByte() / 255.0;
+
+						frame = 0;
+						bezier = 0;
+						while (true) {
+							rgbTimeline.setFrame(frame, time, r, g, b);
+							if (frame == frameLast)
+								break;
+
+							time2 = input.readFloat();
+							r2 = input.readUnsignedByte() / 255.0;
+							g2 = input.readUnsignedByte() / 255.0;
+							b2 = input.readUnsignedByte() / 255.0;
+
+							switch (input.readByte()) {
+								case CURVE_STEPPED:
+									rgbTimeline.setStepped(frame);
+								case CURVE_BEZIER:
+									setBezier(input, rgbTimeline, bezier++, frame, 0, time, time2, r, r2, 1);
+									setBezier(input, rgbTimeline, bezier++, frame, 1, time, time2, g, g2, 1);
+									setBezier(input, rgbTimeline, bezier++, frame, 2, time, time2, b, b2, 1);
+							}
+							time = time2;
+							r = r2;
+							g = g2;
+							b = b2;
+
+							frame++;
+						}
+						timelines.push(rgbTimeline);
+					case SLOT_RGBA2:
+						bezierCount = input.readInt(true);
+						var rgba2Timeline:RGBA2Timeline = new RGBA2Timeline(frameCount, bezierCount, slotIndex);
+
+						time = input.readFloat();
+						r = input.readUnsignedByte() / 255.0;
+						g = input.readUnsignedByte() / 255.0;
+						b = input.readUnsignedByte() / 255.0;
+						a = input.readUnsignedByte() / 255.0;
+						r2 = input.readUnsignedByte() / 255.0;
+						g2 = input.readUnsignedByte() / 255.0;
+						b2 = input.readUnsignedByte() / 255.0;
+
+						frame = 0;
+						bezier = 0;
+						while (true) {
+							rgba2Timeline.setFrame(frame, time, r, g, b, a, r2, g2, b2);
+							if (frame == frameLast)
+								break;
+
+							time2 = input.readFloat();
+							nr = input.readUnsignedByte() / 255.0;
+							ng = input.readUnsignedByte() / 255.0;
+							nb = input.readUnsignedByte() / 255.0;
+							na = input.readUnsignedByte() / 255.0;
+							nr2 = input.readUnsignedByte() / 255.0;
+							ng2 = input.readUnsignedByte() / 255.0;
+							nb2 = input.readUnsignedByte() / 255.0;
+
+							switch (input.readByte()) {
+								case CURVE_STEPPED:
+									rgba2Timeline.setStepped(frame);
+								case CURVE_BEZIER:
+									setBezier(input, rgba2Timeline, bezier++, frame, 0, time, time2, r, nr, 1);
+									setBezier(input, rgba2Timeline, bezier++, frame, 1, time, time2, g, ng, 1);
+									setBezier(input, rgba2Timeline, bezier++, frame, 2, time, time2, b, nb, 1);
+									setBezier(input, rgba2Timeline, bezier++, frame, 3, time, time2, a, na, 1);
+									setBezier(input, rgba2Timeline, bezier++, frame, 4, time, time2, r2, nr2, 1);
+									setBezier(input, rgba2Timeline, bezier++, frame, 5, time, time2, g2, ng2, 1);
+									setBezier(input, rgba2Timeline, bezier++, frame, 6, time, time2, b2, nb2, 1);
+							}
+							time = time2;
+							r = nr;
+							g = ng;
+							b = nb;
+							a = na;
+							r2 = nr2;
+							g2 = ng2;
+							b2 = nb2;
+
+							frame++;
+						}
+						timelines.push(rgba2Timeline);
+					case SLOT_RGB2:
+						bezierCount = input.readInt(true);
+						var rgb2Timeline:RGB2Timeline = new RGB2Timeline(frameCount, bezierCount, slotIndex);
+
+						time = input.readFloat();
+						r = input.readUnsignedByte() / 255.0;
+						g = input.readUnsignedByte() / 255.0;
+						b = input.readUnsignedByte() / 255.0;
+						r2 = input.readUnsignedByte() / 255.0;
+						g2 = input.readUnsignedByte() / 255.0;
+						b2 = input.readUnsignedByte() / 255.0;
+
+						frame = 0;
+						bezier = 0;
+						while (true) {
+							rgb2Timeline.setFrame(frame, time, r, g, b, r2, g2, b2);
+							if (frame == frameLast)
+								break;
+
+							time2 = input.readFloat();
+							nr = input.readUnsignedByte() / 255.0;
+							ng = input.readUnsignedByte() / 255.0;
+							nb = input.readUnsignedByte() / 255.0;
+							nr2 = input.readUnsignedByte() / 255.0;
+							ng2 = input.readUnsignedByte() / 255.0;
+							nb2 = input.readUnsignedByte() / 255.0;
+
+							switch (input.readByte()) {
+								case CURVE_STEPPED:
+									rgb2Timeline.setStepped(frame);
+								case CURVE_BEZIER:
+									setBezier(input, rgb2Timeline, bezier++, frame, 0, time, time2, r, nr, 1);
+									setBezier(input, rgb2Timeline, bezier++, frame, 1, time, time2, g, ng, 1);
+									setBezier(input, rgb2Timeline, bezier++, frame, 2, time, time2, b, nb, 1);
+									setBezier(input, rgb2Timeline, bezier++, frame, 3, time, time2, r2, nr2, 1);
+									setBezier(input, rgb2Timeline, bezier++, frame, 4, time, time2, g2, ng2, 1);
+									setBezier(input, rgb2Timeline, bezier++, frame, 5, time, time2, b2, nb2, 1);
+							}
+							time = time2;
+							r = nr;
+							g = ng;
+							b = nb;
+							r2 = nr2;
+							g2 = ng2;
+							b2 = nb2;
+
+							frame++;
+						}
+						timelines.push(rgb2Timeline);
+					case SLOT_ALPHA:
+						var alphaTimeline:AlphaTimeline = new AlphaTimeline(frameCount, input.readInt(true), slotIndex);
+						time = input.readFloat();
+						a = input.readUnsignedByte() / 255;
+
+						frame = 0;
+						bezier = 0;
+						while (true) {
+							alphaTimeline.setFrame(frame, time, a);
+							if (frame == frameLast)
+								break;
+
+							time2 = input.readFloat();
+							a2 = input.readUnsignedByte() / 255;
+							switch (input.readByte()) {
+								case CURVE_STEPPED:
+									alphaTimeline.setStepped(frame);
+								case CURVE_BEZIER:
+									setBezier(input, alphaTimeline, bezier++, frame, 0, time, time2, a, a2, 1);
+							}
+							time = time2;
+							a = a2;
+
+							frame++;
+						}
+						timelines.push(alphaTimeline);
+				}
+			}
+		}
+
+		// Bone timelines.
+		for (i in 0...input.readInt(true)) {
+			var boneIndex:Int = input.readInt(true);
+			for (ii in 0...input.readInt(true)) {
+				timelineType = input.readByte();
+				frameCount = input.readInt(true);
+				bezierCount = input.readInt(true);
+				switch (timelineType) {
+					case BONE_ROTATE:
+						timelines.push(readTimeline(input, new RotateTimeline(frameCount, bezierCount, boneIndex), 1));
+					case BONE_TRANSLATE:
+						timelines.push(readTimeline2(input, new TranslateTimeline(frameCount, bezierCount, boneIndex), scale));
+					case BONE_TRANSLATEX:
+						timelines.push(readTimeline(input, new TranslateXTimeline(frameCount, bezierCount, boneIndex), scale));
+					case BONE_TRANSLATEY:
+						timelines.push(readTimeline(input, new TranslateYTimeline(frameCount, bezierCount, boneIndex), scale));
+					case BONE_SCALE:
+						timelines.push(readTimeline2(input, new ScaleTimeline(frameCount, bezierCount, boneIndex), 1));
+					case BONE_SCALEX:
+						timelines.push(readTimeline(input, new ScaleXTimeline(frameCount, bezierCount, boneIndex), 1));
+					case BONE_SCALEY:
+						timelines.push(readTimeline(input, new ScaleYTimeline(frameCount, bezierCount, boneIndex), 1));
+					case BONE_SHEAR:
+						timelines.push(readTimeline2(input, new ShearTimeline(frameCount, bezierCount, boneIndex), 1));
+					case BONE_SHEARX:
+						timelines.push(readTimeline(input, new ShearXTimeline(frameCount, bezierCount, boneIndex), 1));
+					case BONE_SHEARY:
+						timelines.push(readTimeline(input, new ShearYTimeline(frameCount, bezierCount, boneIndex), 1));
+				}
+			}
+		}
+
+		// IK constraint timelines.
+		for (i in 0...input.readInt(true)) {
+			index = input.readInt(true);
+			frameCount = input.readInt(true);
+			frameLast = frameCount - 1;
+			var ikTimeline:IkConstraintTimeline = new IkConstraintTimeline(frameCount, input.readInt(true), index);
+			time = input.readFloat();
+			var mix:Float = input.readFloat(),
+				softness:Float = input.readFloat() * scale;
+
+			frame = 0;
+			bezier = 0;
+			while (true) {
+				ikTimeline.setFrame(frame, time, mix, softness, input.readByte(), input.readBoolean(), input.readBoolean());
+				if (frame == frameLast)
+					break;
+
+				time2 = input.readFloat();
+				var mix2:Float = input.readFloat(),
+					softness2:Float = input.readFloat() * scale;
+				switch (input.readByte()) {
+					case CURVE_STEPPED:
+						ikTimeline.setStepped(frame);
+					case CURVE_BEZIER:
+						setBezier(input, ikTimeline, bezier++, frame, 0, time, time2, mix, mix2, 1);
+						setBezier(input, ikTimeline, bezier++, frame, 1, time, time2, softness, softness2, scale);
+				}
+				time = time2;
+				mix = mix2;
+				softness = softness2;
+
+				frame++;
+			}
+			timelines.push(ikTimeline);
+		}
+
+		// Transform constraint timelines.
+		var mixRotate:Float, mixRotate2:Float;
+		var mixX:Float, mixX2:Float;
+		var mixY:Float, mixY2:Float;
+		for (i in 0...input.readInt(true)) {
+			index = input.readInt(true);
+			frameCount = input.readInt(true);
+
+			frameLast = frameCount - 1;
+			var transformTimeline:TransformConstraintTimeline = new TransformConstraintTimeline(frameCount, input.readInt(true), index);
+			time = input.readFloat();
+			mixRotate = input.readFloat();
+			mixX = input.readFloat();
+			mixY = input.readFloat();
+			var mixScaleX:Float = input.readFloat(),
+				mixScaleY:Float = input.readFloat(),
+				mixShearY:Float = input.readFloat();
+			frame = 0;
+			bezier = 0;
+			while (true) {
+				transformTimeline.setFrame(frame, time, mixRotate, mixX, mixY, mixScaleX, mixScaleY, mixShearY);
+				if (frame == frameLast)
+					break;
+
+				time2 = input.readFloat();
+				mixRotate2 = input.readFloat();
+				mixX2 = input.readFloat();
+				mixY2 = input.readFloat();
+				var mixScaleX2:Float = input.readFloat(),
+					mixScaleY2:Float = input.readFloat(),
+					mixShearY2:Float = input.readFloat();
+				switch (input.readByte()) {
+					case CURVE_STEPPED:
+						transformTimeline.setStepped(frame);
+					case CURVE_BEZIER:
+						setBezier(input, transformTimeline, bezier++, frame, 0, time, time2, mixRotate, mixRotate2, 1);
+						setBezier(input, transformTimeline, bezier++, frame, 1, time, time2, mixX, mixX2, 1);
+						setBezier(input, transformTimeline, bezier++, frame, 2, time, time2, mixY, mixY2, 1);
+						setBezier(input, transformTimeline, bezier++, frame, 3, time, time2, mixScaleX, mixScaleX2, 1);
+						setBezier(input, transformTimeline, bezier++, frame, 4, time, time2, mixScaleY, mixScaleY2, 1);
+						setBezier(input, transformTimeline, bezier++, frame, 5, time, time2, mixShearY, mixShearY2, 1);
+				}
+				time = time2;
+				mixRotate = mixRotate2;
+				mixX = mixX2;
+				mixY = mixY2;
+				mixScaleX = mixScaleX2;
+				mixScaleY = mixScaleY2;
+				mixShearY = mixShearY2;
+
+				frame++;
+			}
+
+			timelines.push(transformTimeline);
+		}
+
+		// Path constraint timelines.
+		for (i in 0...input.readInt(true)) {
+			index = input.readInt(true);
+			var data:PathConstraintData = skeletonData.pathConstraints[index];
+			for (ii in 0...input.readInt(true)) {
+				switch (input.readByte()) {
+					case PATH_POSITION:
+						timelines.push(readTimeline(input, new PathConstraintPositionTimeline(input.readInt(true), input.readInt(true), index),
+							data.positionMode == PositionMode.fixed ? scale : 1));
+					case PATH_SPACING:
+						timelines.push(readTimeline(input, new PathConstraintSpacingTimeline(input.readInt(true), input.readInt(true), index),
+							data.spacingMode == SpacingMode.length
+							|| data.spacingMode == SpacingMode.fixed ? scale : 1));
+					case PATH_MIX:
+						var mixTimeline:PathConstraintMixTimeline = new PathConstraintMixTimeline(input.readInt(true), input.readInt(true), index);
+						time = input.readFloat();
+						mixRotate = input.readFloat();
+						mixX = input.readFloat();
+						mixY = input.readFloat();
+
+						frame = 0;
+						bezier = 0;
+						frameLast = mixTimeline.getFrameCount() - 1;
+						while (true) {
+							mixTimeline.setFrame(frame, time, mixRotate, mixX, mixY);
+							if (frame == frameLast)
+								break;
+							time2 = input.readFloat();
+							mixRotate2 = input.readFloat();
+							mixX2 = input.readFloat();
+							mixY2 = input.readFloat();
+							switch (input.readByte()) {
+								case CURVE_STEPPED:
+									mixTimeline.setStepped(frame);
+								case CURVE_BEZIER:
+									setBezier(input, mixTimeline, bezier++, frame, 0, time, time2, mixRotate, mixRotate2, 1);
+									setBezier(input, mixTimeline, bezier++, frame, 1, time, time2, mixX, mixX2, 1);
+									setBezier(input, mixTimeline, bezier++, frame, 2, time, time2, mixY, mixY2, 1);
+							}
+							time = time2;
+							mixRotate = mixRotate2;
+							mixX = mixX2;
+							mixY = mixY2;
+
+							frame++;
+						}
+						timelines.push(mixTimeline);
+				}
+			}
+		}
+
+		// Deform timelines.
+		for (i in 0...input.readInt(true)) {
+			var skin:Skin = skeletonData.skins[input.readInt(true)];
+			for (ii in 0...input.readInt(true)) {
+				slotIndex = input.readInt(true);
+				for (iii in 0...input.readInt(true)) {
+					var attachmentName:String = input.readStringRef();
+					var attachment:VertexAttachment = cast(skin.getAttachment(slotIndex, attachmentName), VertexAttachment);
+					if (attachment == null)
+						throw new Error("Vertex attachment not found: " + attachmentName);
+					var weighted:Bool = attachment.bones != null;
+					var vertices:Vector<Float> = attachment.vertices;
+					var deformLength:Int = weighted ? Std.int(vertices.length / 3 * 2) : vertices.length;
+
+					frameCount = input.readInt(true);
+					frameLast = frameCount - 1;
+					bezierCount = input.readInt(true);
+					var deformTimeline:DeformTimeline = new DeformTimeline(frameCount, bezierCount, slotIndex, attachment);
+
+					time = input.readFloat();
+					frame = 0;
+					bezier = 0;
+					while (true) {
+						var deform:Vector<Float>;
+						var end:Int = input.readInt(true);
+						if (end == 0) {
+							if (weighted) {
+								deform = new Vector<Float>(deformLength, true);
+							} else {
+								deform = vertices;
+							}
+						} else {
+							var v:Int, vn:Int;
+							deform = new Vector<Float>(deformLength, true);
+							var start:Int = input.readInt(true);
+							end += start;
+							if (scale == 1) {
+								for (v in start...end) {
+									deform[v] = input.readFloat();
+								}
+							} else {
+								for (v in start...end) {
+									deform[v] = input.readFloat() * scale;
+								}
+							}
+							if (!weighted) {
+								for (v in 0...deform.length) {
+									deform[v] += vertices[v];
+								}
+							}
+						}
+
+						deformTimeline.setFrame(frame, time, deform);
+						if (frame == frameLast)
+							break;
+						time2 = input.readFloat();
+						switch (input.readByte()) {
+							case CURVE_STEPPED:
+								deformTimeline.setStepped(frame);
+							case CURVE_BEZIER:
+								SkeletonBinary.setBezier(input, deformTimeline, bezier++, frame, 0, time, time2, 0, 1, 1);
+						}
+						time = time2;
+
+						frame++;
+					}
+					timelines.push(deformTimeline);
+				}
+			}
+		}
+
+		// Draw order timelines.
+		var drawOrderCount:Int = input.readInt(true);
+		if (drawOrderCount > 0) {
+			var drawOrderTimeline:DrawOrderTimeline = new DrawOrderTimeline(drawOrderCount);
+			var slotCount:Int = skeletonData.slots.length;
+			for (i in 0...drawOrderCount) {
+				time = input.readFloat();
+				var offsetCount:Int = input.readInt(true);
+				var drawOrder:Vector<Int> = new Vector<Int>(slotCount, true);
+				var ii:Int = slotCount - 1;
+				while (ii >= 0) {
+					drawOrder[ii--] = -1;
+				}
+				var unchanged:Vector<Int> = new Vector<Int>(slotCount - offsetCount, true);
+				var originalIndex:Int = 0, unchangedIndex:Int = 0;
+				for (ii in 0...offsetCount) {
+					slotIndex = input.readInt(true);
+					// Collect unchanged items.
+					while (originalIndex != slotIndex) {
+						unchanged[unchangedIndex++] = originalIndex++;
+					}
+					// Set changed items.
+					drawOrder[originalIndex + input.readInt(true)] = originalIndex++;
+				}
+				// Collect remaining unchanged items.
+				while (originalIndex < slotCount) {
+					unchanged[unchangedIndex++] = originalIndex++;
+				}
+				// Fill in unchanged items.
+				ii = slotCount - 1;
+				while (ii >= 0) {
+					if (drawOrder[ii] == -1)
+						drawOrder[ii] = unchanged[--unchangedIndex];
+					ii--;
+				}
+				drawOrderTimeline.setFrame(i, time, drawOrder);
+			}
+			timelines.push(drawOrderTimeline);
+		}
+
+		// Event timelines.
+		var eventCount:Int = input.readInt(true);
+		if (eventCount > 0) {
+			var eventTimeline:EventTimeline = new EventTimeline(eventCount);
+			for (i in 0...eventCount) {
+				time = input.readFloat();
+				var eventData:EventData = skeletonData.events[input.readInt(true)];
+				var event:Event = new Event(time, eventData);
+				event.intValue = input.readInt(false);
+				event.floatValue = input.readFloat();
+				event.stringValue = input.readBoolean() ? input.readString() : eventData.stringValue;
+				if (event.data.audioPath != null) {
+					event.volume = input.readFloat();
+					event.balance = input.readFloat();
+				}
+				eventTimeline.setFrame(i, event);
+			}
+			timelines.push(eventTimeline);
+		}
+
+		var duration:Float = 0;
+		for (i in 0...timelines.length) {
+			duration = Math.max(duration, timelines[i].getDuration());
+		}
+		return new Animation(name, timelines, duration);
+	}
+
+	static private function readTimeline(input:BinaryInput, timeline:CurveTimeline1, scale:Float):CurveTimeline1 {
+		var time:Float = input.readFloat(),
+			value:Float = input.readFloat() * scale;
+
+		var frame:Int = 0,
+			bezier:Int = 0,
+			frameLast:Int = timeline.getFrameCount() - 1;
+		while (true) {
+			timeline.setFrame(frame, time, value);
+			if (frame == frameLast)
+				break;
+
+			var time2:Float = input.readFloat(),
+				value2:Float = input.readFloat() * scale;
+			switch (input.readByte()) {
+				case CURVE_STEPPED:
+					timeline.setStepped(frame);
+				case CURVE_BEZIER:
+					setBezier(input, timeline, bezier++, frame, 0, time, time2, value, value2, scale);
+			}
+			time = time2;
+			value = value2;
+
+			frame++;
+		}
+		return timeline;
+	}
+
+	static private function readTimeline2(input:BinaryInput, timeline:CurveTimeline2, scale:Float):CurveTimeline2 {
+		var time:Float = input.readFloat(),
+			value1:Float = input.readFloat() * scale,
+			value2:Float = input.readFloat() * scale;
+
+		var frame:Int = 0,
+			bezier:Int = 0,
+			frameLast:Int = timeline.getFrameCount() - 1;
+		while (true) {
+			timeline.setFrame(frame, time, value1, value2);
+			if (frame == frameLast)
+				break;
+
+			var time2:Float = input.readFloat(),
+				nvalue1:Float = input.readFloat() * scale,
+				nvalue2:Float = input.readFloat() * scale;
+			switch (input.readByte()) {
+				case CURVE_STEPPED:
+					timeline.setStepped(frame);
+				case CURVE_BEZIER:
+					setBezier(input, timeline, bezier++, frame, 0, time, time2, value1, nvalue1, scale);
+					setBezier(input, timeline, bezier++, frame, 1, time, time2, value2, nvalue2, scale);
+			}
+			time = time2;
+			value1 = nvalue1;
+			value2 = nvalue2;
+
+			frame++;
+		}
+		return timeline;
+	}
+
+	static private function setBezier(input:BinaryInput, timeline:CurveTimeline, bezier:Int, frame:Int, value:Float, time1:Float, time2:Float, value1:Float,
+			value2:Float, scale:Float):Void {
+		timeline.setBezier(bezier, frame, value, time1, value1, input.readFloat(), input.readFloat() * scale, input.readFloat(), input.readFloat() * scale,
+			time2, value2);
+	}
+}
+
+class Vertices {
+	public var vertices:Vector<Float> = new Vector<Float>();
+	public var bones:Vector<Int> = new Vector<Int>();
+
+	public function new() {}
+}
+
+class LinkedMeshBinary {
+	public var parent(default, null):String;
+	public var skin(default, null):String;
+	public var slotIndex(default, null):Int;
+	public var mesh(default, null):MeshAttachment;
+	public var inheritDeform(default, null):Bool;
+
+	public function new(mesh:MeshAttachment, skin:String, slotIndex:Int, parent:String, inheritDeform:Bool) {
+		this.mesh = mesh;
+		this.skin = skin;
+		this.slotIndex = slotIndex;
+		this.parent = parent;
+		this.inheritDeform = inheritDeform;
+	}
+}

+ 319 - 0
spine-haxe/spine-haxe/spine/SkeletonClipping.hx

@@ -0,0 +1,319 @@
+package spine;
+
+import openfl.Vector;
+import spine.attachments.ClippingAttachment;
+
+class SkeletonClipping {
+	private var triangulator:Triangulator = new Triangulator();
+	private var clippingPolygon:Vector<Float> = new Vector<Float>();
+	private var clipOutput:Vector<Float> = new Vector<Float>();
+
+	public var clippedVertices:Vector<Float> = new Vector<Float>();
+	public var clippedUvs:Vector<Float> = new Vector<Float>();
+	public var clippedTriangles:Vector<Int> = new Vector<Int>();
+
+	private var scratch:Vector<Float> = new Vector<Float>();
+
+	private var clipAttachment:ClippingAttachment;
+	private var clippingPolygons:Vector<Vector<Float>>;
+
+	public function new() {}
+
+	public function clipStart(slot:Slot, clip:ClippingAttachment):Int {
+		if (clipAttachment != null)
+			return 0;
+		clipAttachment = clip;
+		clippingPolygon.length = clip.worldVerticesLength;
+		clip.computeWorldVertices(slot, 0, clippingPolygon.length, clippingPolygon, 0, 2);
+		SkeletonClipping.makeClockwise(clippingPolygon);
+		clippingPolygons = triangulator.decompose(clippingPolygon, triangulator.triangulate(clippingPolygon));
+		for (polygon in clippingPolygons) {
+			SkeletonClipping.makeClockwise(polygon);
+			polygon.push(polygon[0]);
+			polygon.push(polygon[1]);
+		}
+		return clippingPolygons.length;
+	}
+
+	public function clipEndWithSlot(slot:Slot):Void {
+		if (clipAttachment != null && clipAttachment.endSlot == slot.data)
+			clipEnd();
+	}
+
+	public function clipEnd():Void {
+		if (clipAttachment == null)
+			return;
+		clipAttachment = null;
+		clippingPolygons = null;
+		clippedVertices.length = 0;
+		clippedUvs.length = 0;
+		clippedTriangles.length = 0;
+		clippingPolygon.length = 0;
+		clipOutput.length = 0;
+	}
+
+	public function isClipping():Bool {
+		return clipAttachment != null;
+	}
+
+	public function clipTriangles(vertices:Vector<Float>, triangles:Vector<Int>, trianglesLength:Float, uvs:Vector<Float>):Void {
+		var polygonsCount:Int = clippingPolygons.length;
+		var index:Int = 0;
+		clippedVertices.length = 0;
+		clippedUvs.length = 0;
+		clippedTriangles.length = 0;
+		var i:Int = 0;
+		while (i < trianglesLength) {
+			var vertexOffset:Int = triangles[i] << 1;
+			var x1:Float = vertices[vertexOffset],
+				y1:Float = vertices[vertexOffset + 1];
+			var u1:Float = uvs[vertexOffset], v1:Float = uvs[vertexOffset + 1];
+
+			vertexOffset = triangles[i + 1] << 1;
+			var x2:Float = vertices[vertexOffset],
+				y2:Float = vertices[vertexOffset + 1];
+			var u2:Float = uvs[vertexOffset], v2:Float = uvs[vertexOffset + 1];
+
+			vertexOffset = triangles[i + 2] << 1;
+			var x3:Float = vertices[vertexOffset],
+				y3:Float = vertices[vertexOffset + 1];
+			var u3:Float = uvs[vertexOffset], v3:Float = uvs[vertexOffset + 1];
+
+			for (p in 0...polygonsCount) {
+				var s:Int = clippedVertices.length;
+				var clippedVerticesItems:Vector<Float>;
+				var clippedUvsItems:Vector<Float>;
+				var clippedTrianglesItems:Vector<Int>;
+				if (this.clip(x1, y1, x2, y2, x3, y3, clippingPolygons[p], clipOutput)) {
+					var clipOutputLength:Int = clipOutput.length;
+					if (clipOutputLength == 0)
+						continue;
+					var d0:Float = y2 - y3,
+						d1:Float = x3 - x2,
+						d2:Float = x1 - x3,
+						d4:Float = y3 - y1;
+					var d:Float = 1 / (d0 * d2 + d1 * (y1 - y3));
+
+					var clipOutputCount:Int = clipOutputLength >> 1;
+					var clipOutputItems:Vector<Float> = clipOutput;
+					clippedVerticesItems = clippedVertices;
+					clippedVerticesItems.length = s + clipOutputLength;
+					clippedUvsItems = clippedUvs;
+					clippedUvsItems.length = s + clipOutputLength;
+
+					var ii:Int = 0;
+					while (ii < clipOutputLength) {
+						var x:Float = clipOutputItems[ii],
+							y:Float = clipOutputItems[ii + 1];
+						clippedVerticesItems[s] = x;
+						clippedVerticesItems[s + 1] = y;
+						var c0:Float = x - x3, c1:Float = y - y3;
+						var a:Float = (d0 * c0 + d1 * c1) * d;
+						var b:Float = (d4 * c0 + d2 * c1) * d;
+						var c:Float = 1 - a - b;
+						clippedUvsItems[s] = u1 * a + u2 * b + u3 * c;
+						clippedUvsItems[s + 1] = v1 * a + v2 * b + v3 * c;
+						s += 2;
+
+						ii += 2;
+					}
+
+					s = clippedTriangles.length;
+					clippedTrianglesItems = clippedTriangles;
+					clippedTrianglesItems.length = s + 3 * (clipOutputCount - 2);
+					clipOutputCount--;
+					for (ii in 1...clipOutputCount) {
+						clippedTrianglesItems[s] = index;
+						clippedTrianglesItems[s + 1] = (index + ii);
+						clippedTrianglesItems[s + 2] = (index + ii + 1);
+						s += 3;
+					}
+					index += clipOutputCount + 1;
+				} else {
+					clippedVerticesItems = clippedVertices;
+					clippedVerticesItems.length = s + 3 * 2;
+					clippedVerticesItems[s] = x1;
+					clippedVerticesItems[s + 1] = y1;
+					clippedVerticesItems[s + 2] = x2;
+					clippedVerticesItems[s + 3] = y2;
+					clippedVerticesItems[s + 4] = x3;
+					clippedVerticesItems[s + 5] = y3;
+
+					clippedUvsItems = clippedUvs;
+					clippedUvsItems.length = s + 3 * 2;
+					clippedUvsItems[s] = u1;
+					clippedUvsItems[s + 1] = v1;
+					clippedUvsItems[s + 2] = u2;
+					clippedUvsItems[s + 3] = v2;
+					clippedUvsItems[s + 4] = u3;
+					clippedUvsItems[s + 5] = v3;
+
+					s = clippedTriangles.length;
+					clippedTrianglesItems = clippedTriangles;
+					clippedTrianglesItems.length = s + 3;
+					clippedTrianglesItems[s] = index;
+					clippedTrianglesItems[s + 1] = (index + 1);
+					clippedTrianglesItems[s + 2] = (index + 2);
+					index += 3;
+					break;
+				}
+			}
+
+			i += 3;
+		}
+	}
+
+	/** Clips the input triangle against the convex, clockwise clipping area. If the triangle lies entirely within the clipping
+	 * area, false is returned. The clipping area must duplicate the first vertex at the end of the vertices list. */
+	public function clip(x1:Float, y1:Float, x2:Float, y2:Float, x3:Float, y3:Float, clippingArea:Vector<Float>, output:Vector<Float>):Bool {
+		var originalOutput:Vector<Float> = output;
+		var clipped:Bool = false;
+
+		// Avoid copy at the end.
+		var input:Vector<Float> = null;
+		if (clippingArea.length % 4 >= 2) {
+			input = output;
+			output = scratch;
+		} else {
+			input = scratch;
+		}
+
+		input.length = 0;
+		input.push(x1);
+		input.push(y1);
+		input.push(x2);
+		input.push(y2);
+		input.push(x3);
+		input.push(y3);
+		input.push(x1);
+		input.push(y1);
+		output.length = 0;
+
+		var clippingVertices:Vector<Float> = clippingArea;
+		var clippingVerticesLast:Int = clippingArea.length - 4;
+		var c0:Float, c2:Float, s:Float, ua:Float;
+		var i:Int = 0;
+		var n:Int = 0;
+		while (true) {
+			var edgeX:Float = clippingVertices[i],
+				edgeY:Float = clippingVertices[i + 1];
+			var edgeX2:Float = clippingVertices[i + 2],
+				edgeY2:Float = clippingVertices[i + 3];
+			var deltaX:Float = edgeX - edgeX2, deltaY:Float = edgeY - edgeY2;
+
+			var inputVertices:Vector<Float> = input;
+			var inputVerticesLength:Int = input.length - 2,
+				outputStart:Int = output.length;
+			var ii:Int = 0;
+			while (ii < inputVerticesLength) {
+				var inputX:Float = inputVertices[ii],
+					inputY:Float = inputVertices[ii + 1];
+				var inputX2:Float = inputVertices[ii + 2],
+					inputY2:Float = inputVertices[ii + 3];
+				var side2:Bool = deltaX * (inputY2 - edgeY2) - deltaY * (inputX2 - edgeX2) > 0;
+				if (deltaX * (inputY - edgeY2) - deltaY * (inputX - edgeX2) > 0) {
+					if (side2) {
+						// v1 inside, v2 inside
+						output.push(inputX2);
+						output.push(inputY2);
+						ii += 2;
+						continue;
+					}
+					// v1 inside, v2 outside
+					c0 = inputY2 - inputY;
+					c2 = inputX2 - inputX;
+					s = c0 * (edgeX2 - edgeX) - c2 * (edgeY2 - edgeY);
+					if (Math.abs(s) > 0.000001) {
+						ua = (c2 * (edgeY - inputY) - c0 * (edgeX - inputX)) / s;
+						output.push(edgeX + (edgeX2 - edgeX) * ua);
+						output.push(edgeY + (edgeY2 - edgeY) * ua);
+					} else {
+						output.push(edgeX);
+						output.push(edgeY);
+					}
+				} else if (side2) {
+					// v1 outside, v2 inside
+					c0 = inputY2 - inputY;
+					c2 = inputX2 - inputX;
+					s = c0 * (edgeX2 - edgeX) - c2 * (edgeY2 - edgeY);
+					if (Math.abs(s) > 0.000001) {
+						ua = (c2 * (edgeY - inputY) - c0 * (edgeX - inputX)) / s;
+						output.push(edgeX + (edgeX2 - edgeX) * ua);
+						output.push(edgeY + (edgeY2 - edgeY) * ua);
+					} else {
+						output.push(edgeX);
+						output.push(edgeY);
+					}
+					output.push(inputX2);
+					output.push(inputY2);
+				}
+				clipped = true;
+
+				ii += 2;
+			}
+
+			if (outputStart == output.length) {
+				// All edges outside.
+				originalOutput.length = 0;
+				return true;
+			}
+
+			output.push(output[0]);
+			output.push(output[1]);
+
+			if (i == clippingVerticesLast)
+				break;
+			var temp:Vector<Float> = output;
+			output = input;
+			output.length = 0;
+			input = temp;
+
+			i += 2;
+		}
+
+		if (originalOutput != output) {
+			originalOutput.length = 0;
+			n = output.length - 2;
+			for (i in 0...n) {
+				originalOutput[i] = output[i];
+			}
+		} else {
+			originalOutput.length = originalOutput.length - 2;
+		}
+
+		return clipped;
+	}
+
+	public static function makeClockwise(polygon:Vector<Float>):Void {
+		var vertices:Vector<Float> = polygon;
+		var verticeslength:Int = polygon.length;
+
+		var area:Float = vertices[verticeslength - 2] * vertices[1] - vertices[0] * vertices[verticeslength - 1];
+		var p1x:Float = 0, p1y:Float = 0, p2x:Float = 0, p2y:Float = 0;
+		var i:Int = 0;
+		var n:Int = verticeslength - 3;
+		while (i < n) {
+			p1x = vertices[i];
+			p1y = vertices[i + 1];
+			p2x = vertices[i + 2];
+			p2y = vertices[i + 3];
+			area += p1x * p2y - p2x * p1y;
+			i += 2;
+		}
+		if (area < 0)
+			return;
+
+		i = 0;
+		n = verticeslength >> 1;
+		var lastX:Int = verticeslength - 2;
+		while (i < n) {
+			var x:Float = vertices[i], y:Float = vertices[i + 1];
+			var other:Int = lastX - i;
+			vertices[i] = vertices[other];
+			vertices[i + 1] = vertices[other + 1];
+			vertices[other] = x;
+			vertices[other + 1] = y;
+			i += 2;
+		}
+	}
+}

+ 175 - 0
spine-haxe/spine-haxe/spine/SkeletonData.hx

@@ -0,0 +1,175 @@
+package spine;
+
+import openfl.errors.ArgumentError;
+import openfl.Vector;
+import spine.animation.Animation;
+
+class SkeletonData {
+	/** May be null. */
+	public var name:String;
+
+	public var bones:Vector<BoneData> = new Vector<BoneData>(); // Ordered parents first.
+	public var slots:Vector<SlotData> = new Vector<SlotData>(); // Setup pose draw order.
+	public var skins:Vector<Skin> = new Vector<Skin>();
+	public var defaultSkin:Skin;
+	public var events:Vector<EventData> = new Vector<EventData>();
+	public var animations:Vector<Animation> = new Vector<Animation>();
+	public var ikConstraints:Vector<IkConstraintData> = new Vector<IkConstraintData>();
+	public var transformConstraints:Vector<TransformConstraintData> = new Vector<TransformConstraintData>();
+	public var pathConstraints:Vector<PathConstraintData> = new Vector<PathConstraintData>();
+	public var x:Float = 0;
+	public var y:Float = 0;
+	public var width:Float = 0;
+	public var height:Float = 0;
+	public var version:String;
+	public var hash:String;
+	public var fps:Float = 0;
+	public var imagesPath:String;
+	public var audioPath:String;
+
+	public function new() {}
+
+	// --- Bones.
+
+	/** @return May be null. */
+	public function findBone(boneName:String):BoneData {
+		if (boneName == null)
+			throw new ArgumentError("boneName cannot be null.");
+		for (i in 0...bones.length) {
+			var bone:BoneData = bones[i];
+			if (bone.name == boneName)
+				return bone;
+		}
+		return null;
+	}
+
+	/** @return -1 if the bone was not found. */
+	public function findBoneIndex(boneName:String):Int {
+		if (boneName == null)
+			throw new ArgumentError("boneName cannot be null.");
+		for (i in 0...bones.length) {
+			if (bones[i].name == boneName)
+				return i;
+		}
+		return -1;
+	}
+
+	// --- Slots.
+
+	/** @return May be null. */
+	public function findSlot(slotName:String):SlotData {
+		if (slotName == null)
+			throw new ArgumentError("slotName cannot be null.");
+		for (i in 0...slots.length) {
+			var slot:SlotData = slots[i];
+			if (slot.name == slotName)
+				return slot;
+		}
+		return null;
+	}
+
+	// --- Skins.
+
+	/** @return May be null. */
+	public function findSkin(skinName:String):Skin {
+		if (skinName == null)
+			throw new ArgumentError("skinName cannot be null.");
+		for (skin in skins) {
+			if (skin.name == skinName)
+				return skin;
+		}
+		return null;
+	}
+
+	// --- Events.
+
+	/** @return May be null. */
+	public function findEvent(eventName:String):EventData {
+		if (eventName == null)
+			throw new ArgumentError("eventName cannot be null.");
+		for (eventData in events) {
+			if (eventData.name == eventName)
+				return eventData;
+		}
+		return null;
+	}
+
+	// --- Animations.
+
+	/** @return May be null. */
+	public function findAnimation(animationName:String):Animation {
+		if (animationName == null)
+			throw new ArgumentError("animationName cannot be null.");
+		for (animation in animations) {
+			if (animation.name == animationName)
+				return animation;
+		}
+		return null;
+	}
+
+	// --- IK constraints.
+
+	/** @return May be null. */
+	public function findIkConstraint(constraintName:String):IkConstraintData {
+		if (constraintName == null)
+			throw new ArgumentError("constraintName cannot be null.");
+		for (ikConstraintData in ikConstraints) {
+			if (ikConstraintData.name == constraintName)
+				return ikConstraintData;
+		}
+		return null;
+	}
+
+	// --- Transform constraints.
+
+	/** @return May be null. */
+	public function findTransformConstraint(constraintName:String):TransformConstraintData {
+		if (constraintName == null)
+			throw new ArgumentError("constraintName cannot be null.");
+		for (transformConstraintData in transformConstraints) {
+			if (transformConstraintData.name == constraintName)
+				return transformConstraintData;
+		}
+		return null;
+	}
+
+	/** @return -1 if the transform constraint was not found. */
+	public function findTransformConstraintIndex(transformConstraintName:String):Int {
+		if (transformConstraintName == null)
+			throw new ArgumentError("transformConstraintName cannot be null.");
+		for (i in 0...transformConstraints.length) {
+			if (transformConstraints[i].name == transformConstraintName)
+				return i;
+		}
+		return -1;
+	}
+
+	// --- Path constraints.
+
+	/** @return May be null. */
+	public function findPathConstraint(constraintName:String):PathConstraintData {
+		if (constraintName == null)
+			throw new ArgumentError("constraintName cannot be null.");
+		for (i in 0...pathConstraints.length) {
+			var constraint:PathConstraintData = pathConstraints[i];
+			if (constraint.name == constraintName)
+				return constraint;
+		}
+		return null;
+	}
+
+	/** @return -1 if the path constraint was not found. */
+	public function findPathConstraintIndex(pathConstraintName:String):Int {
+		if (pathConstraintName == null)
+			throw new ArgumentError("pathConstraintName cannot be null.");
+		for (i in 0...pathConstraints.length) {
+			if (pathConstraints[i].name == pathConstraintName)
+				return i;
+		}
+		return -1;
+	}
+
+	public function toString():String {
+		return name;
+	}
+}

+ 1182 - 0
spine-haxe/spine-haxe/spine/SkeletonJson.hx

@@ -0,0 +1,1182 @@
+package spine;
+
+import haxe.Json;
+import openfl.errors.ArgumentError;
+import openfl.errors.Error;
+import openfl.utils.ByteArray;
+import openfl.utils.Object;
+import openfl.Vector;
+import Reflect;
+import spine.animation.AlphaTimeline;
+import spine.animation.Animation;
+import spine.animation.AttachmentTimeline;
+import spine.animation.CurveTimeline1;
+import spine.animation.CurveTimeline2;
+import spine.animation.CurveTimeline;
+import spine.animation.DeformTimeline;
+import spine.animation.DrawOrderTimeline;
+import spine.animation.EventTimeline;
+import spine.animation.IkConstraintTimeline;
+import spine.animation.PathConstraintMixTimeline;
+import spine.animation.PathConstraintPositionTimeline;
+import spine.animation.PathConstraintSpacingTimeline;
+import spine.animation.RGB2Timeline;
+import spine.animation.RGBA2Timeline;
+import spine.animation.RGBATimeline;
+import spine.animation.RGBTimeline;
+import spine.animation.RotateTimeline;
+import spine.animation.ScaleTimeline;
+import spine.animation.ScaleXTimeline;
+import spine.animation.ScaleYTimeline;
+import spine.animation.ShearTimeline;
+import spine.animation.ShearXTimeline;
+import spine.animation.ShearYTimeline;
+import spine.animation.Timeline;
+import spine.animation.TransformConstraintTimeline;
+import spine.animation.TranslateTimeline;
+import spine.animation.TranslateXTimeline;
+import spine.animation.TranslateYTimeline;
+import spine.attachments.Attachment;
+import spine.attachments.AttachmentLoader;
+import spine.attachments.AttachmentType;
+import spine.attachments.BoundingBoxAttachment;
+import spine.attachments.ClippingAttachment;
+import spine.attachments.MeshAttachment;
+import spine.attachments.PathAttachment;
+import spine.attachments.PointAttachment;
+import spine.attachments.RegionAttachment;
+import spine.attachments.VertexAttachment;
+
+class SkeletonJson {
+	public var attachmentLoader:AttachmentLoader;
+	public var scale:Float = 1;
+
+	private var linkedMeshes:Vector<LinkedMesh> = new Vector<LinkedMesh>();
+
+	public function new(attachmentLoader:AttachmentLoader = null) {
+		this.attachmentLoader = attachmentLoader;
+	}
+
+	/** @param object A String or ByteArray. */
+	public function readSkeletonData(object:Object, name:String = null):SkeletonData {
+		if (object == null)
+			throw new ArgumentError("object cannot be null.");
+
+		var root:Object;
+		if (Std.isOfType(object, String)) {
+			root = Json.parse(cast(object, String));
+		} else if (Std.isOfType(object, ByteArrayData)) {
+			root = Json.parse(cast(object, ByteArray).readUTFBytes(cast(object, ByteArray).length));
+		} else if (Std.isOfType(object, Dynamic)) {
+			root = object;
+		} else {
+			throw new ArgumentError("object must be a String, ByteArray or Object.");
+		}
+
+		var skeletonData:SkeletonData = new SkeletonData();
+		skeletonData.name = name;
+
+		// Skeleton.
+		var skeletonMap:Object = Reflect.getProperty(root, "skeleton");
+		if (skeletonMap != null) {
+			skeletonData.hash = Reflect.getProperty(skeletonMap, "hash");
+			skeletonData.version = Reflect.getProperty(skeletonMap, "spine");
+			skeletonData.x = getFloat(Reflect.getProperty(skeletonMap, "x"));
+			skeletonData.y = getFloat(Reflect.getProperty(skeletonMap, "y"));
+			skeletonData.width = getFloat(Reflect.getProperty(skeletonMap, "width"));
+			skeletonData.height = getFloat(Reflect.getProperty(skeletonMap, "height"));
+			skeletonData.fps = getFloat(Reflect.getProperty(skeletonMap, "fps"));
+			skeletonData.imagesPath = Reflect.getProperty(skeletonMap, "images");
+		}
+
+		// Bones.
+		var boneData:BoneData;
+		for (boneMap in cast(Reflect.getProperty(root, "bones"), Array<Dynamic>)) {
+			var parent:BoneData = null;
+			var parentName:String = Reflect.getProperty(boneMap, "parent");
+			if (parentName != null) {
+				parent = skeletonData.findBone(parentName);
+				if (parent == null)
+					throw new Error("Parent bone not found: " + parentName);
+			}
+			boneData = new BoneData(skeletonData.bones.length, Reflect.getProperty(boneMap, "name"), parent);
+			boneData.length = getFloat(Reflect.getProperty(boneMap, "length")) * scale;
+			boneData.x = getFloat(Reflect.getProperty(boneMap, "x")) * scale;
+			boneData.y = getFloat(Reflect.getProperty(boneMap, "y")) * scale;
+			boneData.rotation = getFloat(Reflect.getProperty(boneMap, "rotation"));
+			boneData.scaleX = getFloat(Reflect.getProperty(boneMap, "scaleX"), 1);
+			boneData.scaleY = getFloat(Reflect.getProperty(boneMap, "scaleY"), 1);
+			boneData.shearX = getFloat(Reflect.getProperty(boneMap, "shearX"));
+			boneData.shearY = getFloat(Reflect.getProperty(boneMap, "shearY"));
+			boneData.transformMode = Reflect.hasField(boneMap,
+				"transform") ? TransformMode.fromName(Reflect.getProperty(boneMap, "transform")) : TransformMode.normal;
+			boneData.skinRequired = Reflect.hasField(boneMap, "skin") ? cast(Reflect.getProperty(boneMap, "skin"), Bool) : false;
+
+			var color:String = Reflect.getProperty(boneMap, "color");
+			if (color != null) {
+				boneData.color.setFromString(color);
+			}
+
+			skeletonData.bones.push(boneData);
+		}
+
+		// Slots.
+		for (slotMap in cast(Reflect.getProperty(root, "slots"), Array<Dynamic>)) {
+			var slotName:String = Reflect.getProperty(slotMap, "name");
+			var boneName:String = Reflect.getProperty(slotMap, "bone");
+			boneData = skeletonData.findBone(boneName);
+			if (boneData == null)
+				throw new Error("Slot bone not found: " + boneName);
+			var slotData:SlotData = new SlotData(skeletonData.slots.length, slotName, boneData);
+
+			var color:String = Reflect.getProperty(slotMap, "color");
+			if (color != null) {
+				slotData.color.setFromString(color);
+			}
+
+			var dark:String = Reflect.getProperty(slotMap, "dark");
+			if (dark != null) {
+				slotData.darkColor = new Color(0, 0, 0);
+				slotData.darkColor.setFromString(dark);
+			}
+
+			slotData.attachmentName = Reflect.getProperty(slotMap, "attachment");
+			slotData.blendMode = Reflect.hasField(slotMap, "blend") ? BlendMode.fromName(Reflect.getProperty(slotMap, "blend")) : BlendMode.normal;
+			skeletonData.slots.push(slotData);
+		}
+
+		// IK constraints.
+		if (Reflect.hasField(root, "ik")) {
+			for (constraintMap in cast(Reflect.getProperty(root, "ik"), Array<Dynamic>)) {
+				var ikData:IkConstraintData = new IkConstraintData(Reflect.getProperty(constraintMap, "name"));
+				ikData.order = getInt(Reflect.getProperty(constraintMap, "order"));
+				ikData.skinRequired = Reflect.hasField(constraintMap, "skin") ? cast(Reflect.getProperty(constraintMap, "skin"), Bool) : false;
+
+				for (boneName in cast(Reflect.getProperty(constraintMap, "bones"), Array<Dynamic>)) {
+					var bone:BoneData = skeletonData.findBone(boneName);
+					if (bone == null)
+						throw new Error("IK constraint bone not found: " + boneName);
+					ikData.bones.push(bone);
+				}
+
+				ikData.target = skeletonData.findBone(Reflect.getProperty(constraintMap, "target"));
+				if (ikData.target == null)
+					throw new Error("Target bone not found: " + Reflect.getProperty(constraintMap, "target"));
+
+				ikData.bendDirection = (!Reflect.hasField(constraintMap, "bendPositive")
+					|| cast(Reflect.getProperty(constraintMap, "bendPositive"), Bool)) ? 1 : -1;
+				ikData.compress = (Reflect.hasField(constraintMap, "compress")
+					&& cast(Reflect.getProperty(constraintMap, "compress"), Bool));
+				ikData.stretch = (Reflect.hasField(constraintMap, "stretch") && cast(Reflect.getProperty(constraintMap, "stretch"), Bool));
+				ikData.uniform = (Reflect.hasField(constraintMap, "uniform") && cast(Reflect.getProperty(constraintMap, "uniform"), Bool));
+				ikData.softness = getFloat(Reflect.getProperty(constraintMap, "softness")) * scale;
+				ikData.mix = getFloat(Reflect.getProperty(constraintMap, "mix"), 1);
+
+				skeletonData.ikConstraints.push(ikData);
+			}
+		}
+
+		// Transform constraints.
+		if (Reflect.hasField(root, "transform")) {
+			for (constraintMap in cast(Reflect.getProperty(root, "transform"), Array<Dynamic>)) {
+				var transformData:TransformConstraintData = new TransformConstraintData(Reflect.getProperty(constraintMap, "name"));
+				transformData.order = getInt(Reflect.getProperty(constraintMap, "order"));
+				transformData.skinRequired = Reflect.hasField(constraintMap, "skin") ? cast(Reflect.getProperty(constraintMap, "skin"), Bool) : false;
+
+				for (boneName in cast(Reflect.getProperty(constraintMap, "bones"), Array<Dynamic>)) {
+					var bone = skeletonData.findBone(boneName);
+					if (bone == null)
+						throw new Error("Transform constraint bone not found: " + boneName);
+					transformData.bones.push(bone);
+				}
+
+				transformData.target = skeletonData.findBone(Reflect.getProperty(constraintMap, "target"));
+				if (transformData.target == null)
+					throw new Error("Target bone not found: " + Reflect.getProperty(constraintMap, "target"));
+
+				transformData.local = Reflect.hasField(constraintMap, "local") ? cast(Reflect.getProperty(constraintMap, "local"), Bool) : false;
+				transformData.relative = Reflect.hasField(constraintMap, "relative") ? cast(Reflect.getProperty(constraintMap, "relative"), Bool) : false;
+
+				transformData.offsetRotation = getFloat(Reflect.getProperty(constraintMap, "rotation"));
+				transformData.offsetX = getFloat(Reflect.getProperty(constraintMap, "x")) * scale;
+				transformData.offsetY = getFloat(Reflect.getProperty(constraintMap, "y")) * scale;
+				transformData.offsetScaleX = getFloat(Reflect.getProperty(constraintMap, "scaleX"));
+				transformData.offsetScaleY = getFloat(Reflect.getProperty(constraintMap, "scaleY"));
+				transformData.offsetShearY = getFloat(Reflect.getProperty(constraintMap, "shearY"));
+
+				transformData.mixRotate = getFloat(Reflect.getProperty(constraintMap, "mixRotate"), 1);
+				transformData.mixX = getFloat(Reflect.getProperty(constraintMap, "mixX"), 1);
+				transformData.mixY = getFloat(Reflect.getProperty(constraintMap, "mixY"), transformData.mixX);
+				transformData.mixScaleX = getFloat(Reflect.getProperty(constraintMap, "mixScaleX"), 1);
+				transformData.mixScaleY = getFloat(Reflect.getProperty(constraintMap, "mixScaleY"), transformData.mixScaleX);
+				transformData.mixShearY = getFloat(Reflect.getProperty(constraintMap, "mixShearY"), 1);
+
+				skeletonData.transformConstraints.push(transformData);
+			}
+		}
+
+		// Path constraints.
+		if (Reflect.hasField(root, "path")) {
+			for (constraintMap in cast(Reflect.getProperty(root, "path"), Array<Dynamic>)) {
+				var pathData:PathConstraintData = new PathConstraintData(Reflect.getProperty(constraintMap, "name"));
+				pathData.order = getInt(Reflect.getProperty(constraintMap, "order"));
+				pathData.skinRequired = Reflect.hasField(constraintMap, "skin") ? cast(Reflect.getProperty(constraintMap, "skin"), Bool) : false;
+
+				for (boneName in cast(Reflect.getProperty(constraintMap, "bones"), Array<Dynamic>)) {
+					var bone = skeletonData.findBone(boneName);
+					if (bone == null)
+						throw new Error("Path constraint bone not found: " + boneName);
+					pathData.bones.push(bone);
+				}
+
+				pathData.target = skeletonData.findSlot(Reflect.getProperty(constraintMap, "target"));
+				if (pathData.target == null)
+					throw new Error("Path target slot not found: " + Reflect.getProperty(constraintMap, "target"));
+
+				pathData.positionMode = Reflect.hasField(constraintMap,
+					"positionMode") ? PositionMode.fromName(Reflect.getProperty(constraintMap, "positionMode")) : PositionMode.percent;
+				pathData.spacingMode = Reflect.hasField(constraintMap,
+					"spacingMode") ? SpacingMode.fromName(Reflect.getProperty(constraintMap, "spacingMode")) : SpacingMode.length;
+				pathData.rotateMode = Reflect.hasField(constraintMap,
+					"rotateMode") ? RotateMode.fromName(Reflect.getProperty(constraintMap, "rotateMode")) : RotateMode.tangent;
+				pathData.offsetRotation = getFloat(Reflect.getProperty(constraintMap, "rotation"));
+				pathData.position = getFloat(Reflect.getProperty(constraintMap, "position"));
+				if (pathData.positionMode == PositionMode.fixed)
+					pathData.position *= scale;
+				pathData.spacing = getFloat(Reflect.getProperty(constraintMap, "spacing"));
+				if (pathData.spacingMode == SpacingMode.length || pathData.spacingMode == SpacingMode.fixed)
+					pathData.spacing *= scale;
+				pathData.mixRotate = getFloat(Reflect.getProperty(constraintMap, "mixRotate"), 1);
+				pathData.mixX = getFloat(Reflect.getProperty(constraintMap, "mixX"), 1);
+				pathData.mixY = getFloat(Reflect.getProperty(constraintMap, "mixY"), 1);
+
+				skeletonData.pathConstraints.push(pathData);
+			}
+		}
+
+		// Skins.
+		if (Reflect.hasField(root, "skins")) {
+			for (skinMap in cast(Reflect.getProperty(root, "skins"), Array<Dynamic>)) {
+				var skin:Skin = new Skin(Reflect.getProperty(skinMap, "name"));
+
+				if (Reflect.hasField(skinMap, "bones")) {
+					var bones:Array<Dynamic> = cast(Reflect.getProperty(skinMap, "bones"), Array<Dynamic>);
+					for (ii in 0...bones.length) {
+						var boneData:BoneData = skeletonData.findBone(bones[ii]);
+						if (boneData == null)
+							throw new Error("Skin bone not found: " + bones[ii]);
+						skin.bones.push(boneData);
+					}
+				}
+
+				if (Reflect.hasField(skinMap, "ik")) {
+					var ik:Array<Dynamic> = cast(Reflect.getProperty(skinMap, "ik"), Array<Dynamic>);
+					for (ii in 0...ik.length) {
+						var constraint:ConstraintData = skeletonData.findIkConstraint(ik[ii]);
+						if (constraint == null)
+							throw new Error("Skin IK constraint not found: " + ik[ii]);
+						skin.constraints.push(constraint);
+					}
+				}
+
+				if (Reflect.hasField(skinMap, "transform")) {
+					var transform:Array<Dynamic> = cast(Reflect.getProperty(skinMap, "transform"), Array<Dynamic>);
+					for (ii in 0...transform.length) {
+						var constraint:ConstraintData = skeletonData.findTransformConstraint(transform[ii]);
+						if (constraint == null)
+							throw new Error("Skin transform constraint not found: " + transform[ii]);
+						skin.constraints.push(constraint);
+					}
+				}
+
+				if (Reflect.hasField(skinMap, "path")) {
+					var path:Array<Dynamic> = cast(Reflect.getProperty(skinMap, "path"), Array<Dynamic>);
+					for (ii in 0...path.length) {
+						var constraint:ConstraintData = skeletonData.findPathConstraint(path[ii]);
+						if (constraint == null)
+							throw new Error("Skin path constraint not found: " + path[ii]);
+						skin.constraints.push(constraint);
+					}
+				}
+
+				if (Reflect.hasField(skinMap, "attachments")) {
+					var attachments:Object = Reflect.getProperty(skinMap, "attachments");
+					for (slotName in attachments) {
+						var slot:SlotData = skeletonData.findSlot(slotName);
+						var slotEntry:Object = Reflect.getProperty(attachments, slotName);
+						for (attachmentName in slotEntry) {
+							var attachment:Attachment = readAttachment(Reflect.getProperty(slotEntry, attachmentName), skin, slot.index, attachmentName,
+								skeletonData);
+							if (attachment != null) {
+								skin.setAttachment(slot.index, attachmentName, attachment);
+							}
+						}
+					}
+				}
+
+				skeletonData.skins.push(skin);
+				if (skin.name == "default") {
+					skeletonData.defaultSkin = skin;
+				}
+			}
+		}
+
+		// Linked meshes.
+		for (linkedMesh in linkedMeshes) {
+			var parentSkin:Skin = linkedMesh.skin == null ? skeletonData.defaultSkin : skeletonData.findSkin(linkedMesh.skin);
+			if (parentSkin == null)
+				throw new Error("Skin not found: " + linkedMesh.skin);
+			var parentMesh:Attachment = parentSkin.getAttachment(linkedMesh.slotIndex, linkedMesh.parent);
+			if (parentMesh == null)
+				throw new Error("Parent mesh not found: " + linkedMesh.parent);
+			linkedMesh.mesh.deformAttachment = linkedMesh.inheritDeform ? cast(parentMesh, VertexAttachment) : linkedMesh.mesh;
+			linkedMesh.mesh.parentMesh = cast(parentMesh, MeshAttachment);
+			linkedMesh.mesh.updateUVs();
+		}
+		linkedMeshes.length = 0;
+
+		// Events.
+		var events:Object = Reflect.getProperty(root, "events");
+		for (eventName in events) {
+			var eventMap:Map<String, Dynamic> = events[eventName];
+			var eventData:EventData = new EventData(eventName);
+			eventData.intValue = getInt(eventMap["int"]);
+			eventData.floatValue = getFloat(eventMap["float"]);
+			eventData.stringValue = eventMap["string"] != null ? eventMap["string"] : "";
+			eventData.audioPath = eventMap["audio"];
+			if (eventData.audioPath != null) {
+				eventData.volume = getFloat(eventMap["volume"], 1);
+				eventData.balance = getFloat(eventMap["balance"]);
+			}
+			skeletonData.events.push(eventData);
+		}
+
+		// Animations.
+		var animations:Object = Reflect.getProperty(root, "animations");
+		for (animationName in animations) {
+			readAnimation(animations[animationName], animationName, skeletonData);
+		}
+		return skeletonData;
+	}
+
+	private function readAttachment(map:Object, skin:Skin, slotIndex:Int, name:String, skeletonData:SkeletonData):Attachment {
+		if (map["name"] != null)
+			name = map["name"];
+
+		var color:String;
+		switch (AttachmentType.fromName(Reflect.hasField(map, "type") ? Reflect.getProperty(map, "type") : "region")) {
+			case AttachmentType.region:
+				var region:RegionAttachment = attachmentLoader.newRegionAttachment(skin, name, map["path"] != null ? map["path"] : name);
+				if (region == null)
+					return null;
+				region.path = map["path"] != null ? map["path"] : name;
+				region.x = getFloat(map["x"]) * scale;
+				region.y = getFloat(map["y"]) * scale;
+				region.scaleX = getFloat(map["scaleX"], 1);
+				region.scaleY = getFloat(map["scaleY"], 1);
+				region.rotation = getFloat(map["rotation"]);
+				region.width = getFloat(map["width"]) * scale;
+				region.height = getFloat(map["height"]) * scale;
+				color = Reflect.getProperty(map, "color");
+				if (color != null) {
+					region.color.setFromString(color);
+				}
+				region.updateOffset();
+				return region;
+			case AttachmentType.mesh, AttachmentType.linkedmesh:
+				var mesh:MeshAttachment = attachmentLoader.newMeshAttachment(skin, name, map["path"] != null ? map["path"] : name);
+				if (mesh == null)
+					return null;
+				mesh.path = map["path"] != null ? map["path"] : name;
+				color = Reflect.getProperty(map, "color");
+				if (color != null) {
+					mesh.color.setFromString(color);
+				}
+				mesh.width = getFloat(map["width"]) * scale;
+				mesh.height = getFloat(map["height"]) * scale;
+				if (map["parent"] != null) {
+					var inheritDeform:Bool = map.hasOwnProperty("deform") ? cast(map["deform"], Bool) : true;
+					linkedMeshes.push(new LinkedMesh(mesh, map["skin"], slotIndex, map["parent"], inheritDeform));
+					return mesh;
+				}
+				var uvs:Vector<Float> = getFloatArray(map, "uvs");
+				readVertices(map, mesh, uvs.length);
+				mesh.triangles = getIntArray(map, "triangles");
+				mesh.regionUVs = uvs;
+				mesh.updateUVs();
+				mesh.hullLength = (getInt(map["hull"])) * 2;
+				if (map["edges"] != null)
+					mesh.edges = getIntArray(map, "edges");
+				return mesh;
+			case AttachmentType.boundingbox:
+				var box:BoundingBoxAttachment = attachmentLoader.newBoundingBoxAttachment(skin, name);
+				if (box == null)
+					return null;
+				readVertices(map, box, Std.parseInt(map["vertexCount"]) << 1);
+				return box;
+			case AttachmentType.path:
+				var path:PathAttachment = attachmentLoader.newPathAttachment(skin, name);
+				if (path == null)
+					return null;
+				path.closed = map.hasOwnProperty("closed") ? cast(map["closed"], Bool) : false;
+				path.constantSpeed = map.hasOwnProperty("constantSpeed") ? cast(map["constantSpeed"], Bool) : true;
+				var vertexCount:Int = Std.parseInt(map["vertexCount"]);
+				readVertices(map, path, vertexCount << 1);
+				var lengths:Vector<Float> = new Vector<Float>();
+				for (curves in cast(map["lengths"], Array<Dynamic>)) {
+					lengths.push(Std.parseFloat(curves) * scale);
+				}
+				path.lengths = lengths;
+				return path;
+			case AttachmentType.point:
+				var point:PointAttachment = attachmentLoader.newPointAttachment(skin, name);
+				if (point == null)
+					return null;
+				point.x = map.hasOwnProperty("x") ? Std.parseFloat(map["x"]) * scale : 0;
+				point.y = map.hasOwnProperty("y") ? Std.parseFloat(map["y"]) * scale : 0;
+				point.rotation = map.hasOwnProperty("rotation") ? Std.parseFloat(map["rotation"]) : 0;
+				color = Reflect.getProperty(map, "color");
+				if (color != null) {
+					point.color.setFromString(color);
+				}
+				return point;
+			case AttachmentType.clipping:
+				var clip:ClippingAttachment = attachmentLoader.newClippingAttachment(skin, name);
+				if (clip == null)
+					return null;
+				var end:String = map["end"];
+				if (end != null) {
+					var slot:SlotData = skeletonData.findSlot(end);
+					if (slot == null)
+						throw new Error("Clipping end slot not found: " + end);
+					clip.endSlot = slot;
+				}
+				var vertexCount:Int = Std.parseInt(map["vertexCount"]);
+				readVertices(map, clip, vertexCount << 1);
+				color = Reflect.getProperty(map, "color");
+				if (color != null) {
+					clip.color.setFromString(color);
+				}
+				return clip;
+		}
+		return null;
+	}
+
+	private function readVertices(map:Object, attachment:VertexAttachment, verticesLength:Int):Void {
+		attachment.worldVerticesLength = verticesLength;
+		var vertices:Vector<Float> = getFloatArray(map, "vertices");
+		if (verticesLength == vertices.length) {
+			if (scale != 1) {
+				for (i in 0...vertices.length) {
+					vertices[i] *= scale;
+				}
+			}
+			attachment.vertices = vertices;
+			return;
+		}
+
+		var weights:Vector<Float> = new Vector<Float>();
+		var bones:Vector<Int> = new Vector<Int>();
+		var i:Int = 0;
+		var n:Int = vertices.length;
+		while (i < n) {
+			var boneCount:Int = Std.int(vertices[i++]);
+			bones.push(boneCount);
+			var nn:Int = i + boneCount * 4;
+			while (i < nn) {
+				bones.push(Std.int(vertices[i]));
+				weights.push(vertices[i + 1] * scale);
+				weights.push(vertices[i + 2] * scale);
+				weights.push(vertices[i + 3]);
+
+				i += 4;
+			}
+		}
+		attachment.bones = bones;
+		attachment.vertices = weights;
+	}
+
+	private function readAnimation(map:Object, name:String, skeletonData:SkeletonData):Void {
+		var timelines:Vector<Timeline> = new Vector<Timeline>();
+
+		var slotMap:Object;
+		var slotIndex:Int;
+		var slotName:String;
+
+		var timelineMap:Array<Object>;
+		var keyMap:Object;
+		var nextMap:Object;
+		var frame:Int, bezier:Int;
+		var time:Float, time2:Float;
+		var curve:Object;
+		var timelineName:String;
+
+		// Slot timelines.
+		var slots:Object = Reflect.getProperty(map, "slots");
+		for (slotName in slots) {
+			slotMap = slots[slotName];
+			slotIndex = skeletonData.findSlot(slotName).index;
+			for (timelineName in slotMap) {
+				timelineMap = slotMap[timelineName];
+				if (timelineMap == null)
+					continue;
+				if (timelineName == "attachment") {
+					var attachmentTimeline:AttachmentTimeline = new AttachmentTimeline(timelineMap.length, slotIndex);
+					for (frame in 0...timelineMap.length) {
+						keyMap = timelineMap[frame];
+						attachmentTimeline.setFrame(frame, getFloat(Reflect.getProperty(keyMap, "time")), keyMap.name);
+					}
+					timelines.push(attachmentTimeline);
+				} else if (timelineName == "rgba") {
+					var rgbaTimeline:RGBATimeline = new RGBATimeline(timelineMap.length, timelineMap.length << 2, slotIndex);
+					keyMap = timelineMap[0];
+					time = getFloat(Reflect.getProperty(keyMap, "time"));
+					var rgba:Color = Color.fromString(keyMap.color);
+
+					frame = 0;
+					bezier = 0;
+					while (true) {
+						rgbaTimeline.setFrame(frame, time, rgba.r, rgba.g, rgba.b, rgba.a);
+						if (timelineMap.length == frame + 1)
+							break;
+
+						nextMap = timelineMap[frame + 1];
+						time2 = getFloat(Reflect.getProperty(nextMap, "time"));
+						var newRgba:Color = Color.fromString(nextMap.color);
+						curve = keyMap.curve;
+						if (curve != null) {
+							bezier = readCurve(curve, rgbaTimeline, bezier, frame, 0, time, time2, rgba.r, newRgba.r, 1);
+							bezier = readCurve(curve, rgbaTimeline, bezier, frame, 1, time, time2, rgba.g, newRgba.g, 1);
+							bezier = readCurve(curve, rgbaTimeline, bezier, frame, 2, time, time2, rgba.b, newRgba.b, 1);
+							bezier = readCurve(curve, rgbaTimeline, bezier, frame, 3, time, time2, rgba.a, newRgba.a, 1);
+						}
+						time = time2;
+						rgba = newRgba;
+						keyMap = nextMap;
+
+						frame++;
+					}
+
+					timelines.push(rgbaTimeline);
+				} else if (timelineName == "rgb") {
+					var rgbTimeline:RGBTimeline = new RGBTimeline(timelineMap.length, timelineMap.length * 3, slotIndex);
+					keyMap = timelineMap[0];
+					time = getFloat(Reflect.getProperty(keyMap, "time"));
+					var rgb:Color = Color.fromString(keyMap.color);
+
+					frame = 0;
+					bezier = 0;
+					while (true) {
+						rgbTimeline.setFrame(frame, time, rgb.r, rgb.g, rgb.b);
+						nextMap = timelineMap[frame + 1];
+						if (nextMap == null) {
+							rgbTimeline.shrink(bezier);
+							break;
+						}
+
+						time2 = getFloat(Reflect.getProperty(nextMap, "time"));
+						var newRgb:Color = Color.fromString(nextMap.color);
+						curve = keyMap.curve;
+						if (curve != null) {
+							bezier = readCurve(curve, rgbTimeline, bezier, frame, 0, time, time2, rgb.r, newRgb.r, 1);
+							bezier = readCurve(curve, rgbTimeline, bezier, frame, 1, time, time2, rgb.g, newRgb.g, 1);
+							bezier = readCurve(curve, rgbTimeline, bezier, frame, 2, time, time2, rgb.b, newRgb.b, 1);
+						}
+						time = time2;
+						rgb = newRgb;
+						keyMap = nextMap;
+
+						frame++;
+					}
+
+					timelines.push(rgbTimeline);
+				} else if (timelineName == "alpha") {
+					timelines.push(readTimeline(timelineMap, new AlphaTimeline(timelineMap.length, timelineMap.length, slotIndex), 0, 1));
+				} else if (timelineName == "rgba2") {
+					var rgba2Timeline:RGBA2Timeline = new RGBA2Timeline(timelineMap.length, timelineMap.length * 7, slotIndex);
+
+					keyMap = timelineMap[0];
+					time = getFloat(Reflect.getProperty(keyMap, "time"));
+					var lighta:Color = Color.fromString(keyMap.light);
+					var darka:Color = Color.fromString(keyMap.dark);
+
+					frame = 0;
+					bezier = 0;
+					while (true) {
+						rgba2Timeline.setFrame(frame, time, lighta.r, lighta.g, lighta.b, lighta.a, darka.r, darka.g, darka.b);
+						nextMap = timelineMap[frame + 1];
+						if (nextMap == null) {
+							rgba2Timeline.shrink(bezier);
+							break;
+						}
+
+						time2 = getFloat(Reflect.getProperty(nextMap, "time"));
+						var newLighta:Color = Color.fromString(nextMap.light);
+						var newDarka:Color = Color.fromString(nextMap.dark);
+						curve = keyMap.curve;
+						if (curve != null) {
+							bezier = readCurve(curve, rgba2Timeline, bezier, frame, 0, time, time2, lighta.r, newLighta.r, 1);
+							bezier = readCurve(curve, rgba2Timeline, bezier, frame, 1, time, time2, lighta.g, newLighta.g, 1);
+							bezier = readCurve(curve, rgba2Timeline, bezier, frame, 2, time, time2, lighta.b, newLighta.b, 1);
+							bezier = readCurve(curve, rgba2Timeline, bezier, frame, 3, time, time2, lighta.a, newLighta.a, 1);
+							bezier = readCurve(curve, rgba2Timeline, bezier, frame, 4, time, time2, darka.r, newDarka.r, 1);
+							bezier = readCurve(curve, rgba2Timeline, bezier, frame, 5, time, time2, darka.g, newDarka.g, 1);
+							bezier = readCurve(curve, rgba2Timeline, bezier, frame, 6, time, time2, darka.b, newDarka.b, 1);
+						}
+						time = time2;
+						lighta = newLighta;
+						darka = newDarka;
+						keyMap = nextMap;
+
+						frame++;
+					}
+
+					timelines.push(rgba2Timeline);
+				} else if (timelineName == "rgb2") {
+					var rgb2Timeline:RGB2Timeline = new RGB2Timeline(timelineMap.length, timelineMap.length * 6, slotIndex);
+
+					keyMap = timelineMap[0];
+					time = getFloat(Reflect.getProperty(keyMap, "time"));
+					var light:Color = Color.fromString(keyMap.light);
+					var dark:Color = Color.fromString(keyMap.dark);
+
+					frame = 0;
+					bezier = 0;
+					while (true) {
+						rgb2Timeline.setFrame(frame, time, light.r, light.g, light.b, dark.r, dark.g, dark.b);
+						nextMap = timelineMap[frame + 1];
+						if (nextMap == null) {
+							rgb2Timeline.shrink(bezier);
+							break;
+						}
+
+						time2 = getFloat(Reflect.getProperty(nextMap, "time"));
+						var newLight:Color = Color.fromString(nextMap.light);
+						var newDark:Color = Color.fromString(nextMap.dark);
+						curve = keyMap.curve;
+						if (curve != null) {
+							bezier = readCurve(curve, rgb2Timeline, bezier, frame, 0, time, time2, light.r, newLight.r, 1);
+							bezier = readCurve(curve, rgb2Timeline, bezier, frame, 1, time, time2, light.g, newLight.g, 1);
+							bezier = readCurve(curve, rgb2Timeline, bezier, frame, 2, time, time2, light.b, newLight.b, 1);
+							bezier = readCurve(curve, rgb2Timeline, bezier, frame, 3, time, time2, dark.r, newDark.r, 1);
+							bezier = readCurve(curve, rgb2Timeline, bezier, frame, 4, time, time2, dark.g, newDark.g, 1);
+							bezier = readCurve(curve, rgb2Timeline, bezier, frame, 5, time, time2, dark.b, newDark.b, 1);
+						}
+						time = time2;
+						light = newLight;
+						dark = newDark;
+						keyMap = nextMap;
+
+						frame++;
+					}
+
+					timelines.push(rgb2Timeline);
+				} else {
+					throw new Error("Invalid timeline type for a slot: " + timelineName + " (" + slotName + ")");
+				}
+			}
+		}
+
+		// Bone timelines.
+		var bones:Object = Reflect.getProperty(map, "bones");
+		for (boneName in bones) {
+			var boneIndex:Int = skeletonData.findBoneIndex(boneName);
+			if (boneIndex == -1)
+				throw new Error("Bone not found: " + boneName);
+			var boneMap:Object = bones[boneName];
+			for (timelineName in boneMap) {
+				timelineMap = boneMap[timelineName];
+				if (timelineMap.length == 0)
+					continue;
+
+				if (timelineName == "rotate") {
+					timelines.push(readTimeline(timelineMap, new RotateTimeline(timelineMap.length, timelineMap.length, boneIndex), 0, 1));
+				} else if (timelineName == "translate") {
+					var translateTimeline:TranslateTimeline = new TranslateTimeline(timelineMap.length, timelineMap.length << 1, boneIndex);
+					timelines.push(readTimeline2(timelineMap, translateTimeline, "x", "y", 0, scale));
+				} else if (timelineName == "translatex") {
+					var translateXTimeline:TranslateXTimeline = new TranslateXTimeline(timelineMap.length, timelineMap.length, boneIndex);
+					timelines.push(readTimeline(timelineMap, translateXTimeline, 0, scale));
+				} else if (timelineName == "translatey") {
+					var translateYTimeline:TranslateYTimeline = new TranslateYTimeline(timelineMap.length, timelineMap.length, boneIndex);
+					timelines.push(readTimeline(timelineMap, translateYTimeline, 0, scale));
+				} else if (timelineName == "scale") {
+					var scaleTimeline:ScaleTimeline = new ScaleTimeline(timelineMap.length, timelineMap.length << 1, boneIndex);
+					timelines.push(readTimeline2(timelineMap, scaleTimeline, "x", "y", 1, 1));
+				} else if (timelineName == "scalex") {
+					var scaleXTimeline:ScaleXTimeline = new ScaleXTimeline(timelineMap.length, timelineMap.length, boneIndex);
+					timelines.push(readTimeline(timelineMap, scaleXTimeline, 1, 1));
+				} else if (timelineName == "scaley") {
+					var scaleYTimeline:ScaleYTimeline = new ScaleYTimeline(timelineMap.length, timelineMap.length, boneIndex);
+					timelines.push(readTimeline(timelineMap, scaleYTimeline, 1, 1));
+				} else if (timelineName == "shear") {
+					var shearTimeline:ShearTimeline = new ShearTimeline(timelineMap.length, timelineMap.length << 1, boneIndex);
+					timelines.push(readTimeline2(timelineMap, shearTimeline, "x", "y", 0, 1));
+				} else if (timelineName == "shearx") {
+					var shearXTimeline:ShearXTimeline = new ShearXTimeline(timelineMap.length, timelineMap.length, boneIndex);
+					timelines.push(readTimeline(timelineMap, shearXTimeline, 0, 1));
+				} else if (timelineName == "sheary") {
+					var shearYTimeline:ShearYTimeline = new ShearYTimeline(timelineMap.length, timelineMap.length, boneIndex);
+					timelines.push(readTimeline(timelineMap, shearYTimeline, 0, 1));
+				} else {
+					throw new Error("Invalid timeline type for a bone: " + timelineName + " (" + boneName + ")");
+				}
+			}
+		}
+
+		// IK constraint timelines.
+		var iks:Object = Reflect.getProperty(map, "ik");
+		for (ikConstraintName in iks) {
+			timelineMap = iks[ikConstraintName];
+			keyMap = timelineMap[0];
+			if (keyMap == null)
+				continue;
+
+			var ikIndex:Int = skeletonData.ikConstraints.indexOf(skeletonData.findIkConstraint(ikConstraintName));
+			var ikTimeline:IkConstraintTimeline = new IkConstraintTimeline(timelineMap.length, timelineMap.length << 1, ikIndex);
+
+			time = getFloat(Reflect.getProperty(keyMap, "time"));
+			var mix:Float = getFloat(Reflect.getProperty(keyMap, "mix"), 1);
+			var softness:Float = getFloat(Reflect.getProperty(keyMap, "softness")) * scale;
+
+			frame = 0;
+			bezier = 0;
+			while (true) {
+				ikTimeline.setFrame(frame, time, mix, softness,
+					Reflect.hasField(keyMap, "bendPositive") ? (cast(Reflect.getProperty(keyMap, "bendPositive"), Bool) ? 1 : -1) : 1,
+					Reflect.hasField(keyMap, "compress") ? cast(Reflect.getProperty(keyMap, "compress"), Bool) : false,
+					Reflect.hasField(keyMap, "stretch") ? cast(Reflect.getProperty(keyMap, "stretch"), Bool) : false);
+
+				nextMap = timelineMap[frame + 1];
+				if (nextMap == null) {
+					ikTimeline.shrink(bezier);
+					break;
+				}
+
+				time2 = getFloat(Reflect.getProperty(nextMap, "time"));
+				var mix2:Float = getFloat(Reflect.getProperty(nextMap, "mix"), 1);
+				var softness2:Float = getFloat(Reflect.getProperty(nextMap, "softness")) * scale;
+				curve = keyMap.curve;
+				if (curve != null) {
+					bezier = readCurve(curve, ikTimeline, bezier, frame, 0, time, time2, mix, mix2, 1);
+					bezier = readCurve(curve, ikTimeline, bezier, frame, 1, time, time2, softness, softness2, scale);
+				}
+				time = time2;
+				mix = mix2;
+				softness = softness2;
+				keyMap = nextMap;
+
+				frame++;
+			}
+			timelines.push(ikTimeline);
+		}
+
+		// Transform constraint timelines.
+		var mixRotate:Float, mixRotate2:Float;
+		var mixX:Float, mixX2:Float;
+		var mixY:Float, mixY2:Float;
+		var transforms:Object = Reflect.getProperty(map, "transform");
+		for (transformName in transforms) {
+			timelineMap = transforms[transformName];
+			keyMap = timelineMap[0];
+			if (keyMap == null)
+				continue;
+
+			var transformIndex:Int = skeletonData.transformConstraints.indexOf(skeletonData.findTransformConstraint(transformName));
+			var transformTimeline:TransformConstraintTimeline = new TransformConstraintTimeline(timelineMap.length, timelineMap.length << 2, transformIndex);
+
+			time = getFloat(Reflect.getProperty(keyMap, "time"));
+			mixRotate = getFloat(Reflect.getProperty(keyMap, "mixRotate"), 1);
+			var mixShearY:Float = getFloat(Reflect.getProperty(keyMap, "mixShearY"), 1);
+			mixX = getFloat(Reflect.getProperty(keyMap, "mixX"), 1);
+			mixY = getFloat(Reflect.getProperty(keyMap, "mixY"), mixX);
+			var mixScaleX:Float = getFloat(Reflect.getProperty(keyMap, "mixScaleX"), 1);
+			var mixScaleY:Float = getFloat(Reflect.getProperty(keyMap, "mixScaleY"), mixScaleX);
+
+			frame = 0;
+			bezier = 0;
+			while (true) {
+				transformTimeline.setFrame(frame, time, mixRotate, mixX, mixY, mixScaleX, mixScaleY, mixShearY);
+				nextMap = timelineMap[frame + 1];
+				if (nextMap == null) {
+					transformTimeline.shrink(bezier);
+					break;
+				}
+
+				time2 = getFloat(Reflect.getProperty(nextMap, "time"));
+				mixRotate2 = getFloat(Reflect.getProperty(nextMap, "mixRotate"), 1);
+				var mixShearY2:Float = getFloat(Reflect.getProperty(nextMap, "mixShearY"), 1);
+				mixX2 = getFloat(Reflect.getProperty(nextMap, "mixX"), 1);
+				mixY2 = getFloat(Reflect.getProperty(nextMap, "mixY"), mixX2);
+				var mixScaleX2:Float = getFloat(Reflect.getProperty(nextMap, "mixScaleX"), 1);
+				var mixScaleY2:Float = getFloat(Reflect.getProperty(nextMap, "mixScaleY"), mixScaleX2);
+				curve = keyMap.curve;
+				if (curve != null) {
+					bezier = readCurve(curve, transformTimeline, bezier, frame, 0, time, time2, mixRotate, mixRotate2, 1);
+					bezier = readCurve(curve, transformTimeline, bezier, frame, 1, time, time2, mixX, mixX2, 1);
+					bezier = readCurve(curve, transformTimeline, bezier, frame, 2, time, time2, mixY, mixY2, 1);
+					bezier = readCurve(curve, transformTimeline, bezier, frame, 3, time, time2, mixScaleX, mixScaleX2, 1);
+					bezier = readCurve(curve, transformTimeline, bezier, frame, 4, time, time2, mixScaleY, mixScaleY2, 1);
+					bezier = readCurve(curve, transformTimeline, bezier, frame, 5, time, time2, mixShearY, mixShearY2, 1);
+				}
+				time = time2;
+				mixRotate = mixRotate2;
+				mixX = mixX2;
+				mixY = mixY2;
+				mixScaleX = mixScaleX2;
+				mixScaleY = mixScaleY2;
+				mixScaleX = mixScaleX2;
+				keyMap = nextMap;
+
+				frame++;
+			}
+
+			timelines.push(transformTimeline);
+		}
+
+		// Path constraint timelines.
+		var paths:Object = Reflect.getProperty(map, "path");
+		for (pathName in paths) {
+			var index:Int = skeletonData.findPathConstraintIndex(pathName);
+			if (index == -1)
+				throw new Error("Path constraint not found: " + pathName);
+			var pathData:PathConstraintData = skeletonData.pathConstraints[index];
+
+			var pathMap:Object = paths[pathName];
+			for (timelineName in pathMap) {
+				timelineMap = pathMap[timelineName];
+				keyMap = timelineMap[0];
+				if (keyMap == null)
+					continue;
+
+				if (timelineName == "position") {
+					var positionTimeline:PathConstraintPositionTimeline = new PathConstraintPositionTimeline(timelineMap.length, timelineMap.length, index);
+					timelines.push(readTimeline(timelineMap, positionTimeline, 0, pathData.positionMode == PositionMode.fixed ? scale : 1));
+				} else if (timelineName == "spacing") {
+					var spacingTimeline:PathConstraintSpacingTimeline = new PathConstraintSpacingTimeline(timelineMap.length, timelineMap.length, index);
+					timelines.push(readTimeline(timelineMap, spacingTimeline,
+						0, pathData.spacingMode == SpacingMode.length || pathData.spacingMode == SpacingMode.fixed ? scale : 1));
+				} else if (timelineName == "mix") {
+					var mixTimeline:PathConstraintMixTimeline = new PathConstraintMixTimeline(timelineMap.length, timelineMap.length * 3, index);
+					time = getFloat(Reflect.getProperty(keyMap, "time"));
+					mixRotate = getFloat(Reflect.getProperty(keyMap, "mixRotate"), 1);
+					mixX = getFloat(Reflect.getProperty(keyMap, "mixX"), 1);
+					mixY = getFloat(Reflect.getProperty(keyMap, "mixY"), mixX);
+
+					frame = 0;
+					bezier = 0;
+					while (true) {
+						mixTimeline.setFrame(frame, time, mixRotate, mixX, mixY);
+						nextMap = timelineMap[frame + 1];
+						if (nextMap == null) {
+							mixTimeline.shrink(bezier);
+							break;
+						}
+						time2 = getFloat(Reflect.getProperty(nextMap, "time"));
+						mixRotate2 = getFloat(Reflect.getProperty(nextMap, "mixRotate"), 1);
+						mixX2 = getFloat(Reflect.getProperty(nextMap, "mixX"), 1);
+						mixY2 = getFloat(Reflect.getProperty(nextMap, "mixY"), mixX2);
+						curve = keyMap.curve;
+						if (curve != null) {
+							bezier = readCurve(curve, mixTimeline, bezier, frame, 0, time, time2, mixRotate, mixRotate2, 1);
+							bezier = readCurve(curve, mixTimeline, bezier, frame, 1, time, time2, mixX, mixX2, 1);
+							bezier = readCurve(curve, mixTimeline, bezier, frame, 2, time, time2, mixY, mixY2, 1);
+						}
+						time = time2;
+						mixRotate = mixRotate2;
+						mixX = mixX2;
+						mixY = mixY2;
+						keyMap = nextMap;
+
+						frame++;
+					}
+
+					timelines.push(mixTimeline);
+				}
+			}
+		}
+
+		// Deform timelines.
+		var deforms:Object = Reflect.getProperty(map, "deform");
+		for (deformName in deforms) {
+			var deformMap:Object = deforms[deformName];
+			var skin:Skin = skeletonData.findSkin(deformName);
+			if (skin == null)
+				throw new Error("Skin not found: " + deformName);
+
+			for (slotName in deformMap) {
+				slotMap = deformMap[slotName];
+				slotIndex = skeletonData.findSlot(slotName).index;
+				if (slotIndex == -1)
+					throw new Error("Slot not found: " + slotName);
+				for (timelineName in slotMap) {
+					timelineMap = slotMap[timelineName];
+					keyMap = timelineMap[0];
+					if (keyMap == null)
+						continue;
+
+					var attachment:VertexAttachment = cast(skin.getAttachment(slotIndex, timelineName), VertexAttachment);
+					if (attachment == null)
+						throw new Error("Deform attachment not found: " + timelineName);
+					var weighted:Bool = attachment.bones != null;
+					var vertices:Vector<Float> = attachment.vertices;
+					var deformLength:Int = weighted ? Std.int(vertices.length / 3 * 2) : vertices.length;
+
+					var deformTimeline:DeformTimeline = new DeformTimeline(timelineMap.length, timelineMap.length, slotIndex, attachment);
+					time = getFloat(Reflect.getProperty(keyMap, "time"));
+					frame = 0;
+					bezier = 0;
+					while (true) {
+						var deform:Vector<Float>;
+						var verticesValue:Vector<Float> = Reflect.getProperty(keyMap, "vertices");
+						if (verticesValue == null) {
+							deform = weighted ? new Vector<Float>(deformLength, true) : vertices;
+						} else {
+							deform = new Vector<Float>(deformLength, true);
+							var start:Int = getInt(Reflect.getProperty(keyMap, "offset"));
+							var temp:Vector<Float> = getFloatArray(keyMap, "vertices");
+							for (i in 0...temp.length) {
+								deform[start + i] = temp[i];
+							}
+							if (scale != 1) {
+								for (i in start...start + temp.length) {
+									deform[i] *= scale;
+								}
+							}
+							if (!weighted) {
+								for (i in 0...deformLength) {
+									deform[i] += vertices[i];
+								}
+							}
+						}
+
+						deformTimeline.setFrame(frame, time, deform);
+						nextMap = timelineMap[frame + 1];
+						if (nextMap == null) {
+							deformTimeline.shrink(bezier);
+							break;
+						}
+						time2 = getFloat(Reflect.getProperty(nextMap, "time"));
+						curve = keyMap.curve;
+						if (curve != null) {
+							bezier = readCurve(curve, deformTimeline, bezier, frame, 0, time, time2, 0, 1, 1);
+						}
+						time = time2;
+						keyMap = nextMap;
+
+						frame++;
+					}
+
+					timelines.push(deformTimeline);
+				}
+			}
+		}
+
+		// Draw order timelines.
+		if (Reflect.hasField(map, "drawOrder")) {
+			var drawOrders:Array<Dynamic> = cast(map["drawOrder"], Array<Dynamic>);
+			if (drawOrders != null) {
+				var drawOrderTimeline:DrawOrderTimeline = new DrawOrderTimeline(drawOrders.length);
+				var slotCount:Int = skeletonData.slots.length;
+				frame = 0;
+				for (drawOrderMap in drawOrders) {
+					var drawOrder:Vector<Int> = null;
+					var offsets:Array<Dynamic> = Reflect.getProperty(drawOrderMap, "offsets");
+					if (offsets != null) {
+						drawOrder = new Vector<Int>(slotCount, true);
+						var i = slotCount - 1;
+						while (i >= 0) {
+							drawOrder[i--] = -1;
+						}
+						var unchanged:Vector<Int> = new Vector<Int>(slotCount - offsets.length, true);
+						var originalIndex:Int = 0, unchangedIndex:Int = 0;
+						for (offsetMap in offsets) {
+							slotIndex = skeletonData.findSlot(Reflect.getProperty(offsetMap, "slot")).index;
+							if (slotIndex == -1)
+								throw new Error("Slot not found: " + Reflect.getProperty(offsetMap, "slot"));
+							// Collect unchanged items.
+							while (originalIndex != slotIndex) {
+								unchanged[unchangedIndex++] = originalIndex++;
+							}
+							// Set changed items.
+							drawOrder[originalIndex + Reflect.getProperty(offsetMap, "offset")] = originalIndex++;
+						}
+						// Collect remaining unchanged items.
+						while (originalIndex < slotCount) {
+							unchanged[unchangedIndex++] = originalIndex++;
+						}
+						// Fill in unchanged items.
+						i = slotCount - 1;
+						while (i >= 0) {
+							if (drawOrder[i] == -1)
+								drawOrder[i] = unchanged[--unchangedIndex];
+							i--;
+						}
+					}
+					drawOrderTimeline.setFrame(frame++, getFloat(Reflect.getProperty(drawOrderMap, "time")), drawOrder);
+				}
+				timelines.push(drawOrderTimeline);
+			}
+		}
+
+		// Event timelines.
+		if (Reflect.hasField(map, "events")) {
+			var eventsMap:Array<Dynamic> = cast(map["events"], Array<Dynamic>);
+			if (eventsMap != null) {
+				var eventTimeline:EventTimeline = new EventTimeline(eventsMap.length);
+				frame = 0;
+				for (eventMap in eventsMap) {
+					var eventData:EventData = skeletonData.findEvent(Reflect.getProperty(eventMap, "name"));
+					if (eventData == null)
+						throw new Error("Event not found: " + Reflect.getProperty(eventMap, "name"));
+					var event:Event = new Event(getFloat(Reflect.getProperty(eventMap, "time")), eventData);
+					event.intValue = Reflect.hasField(eventMap, "int") ? getInt(Reflect.getProperty(eventMap, "int")) : eventData.intValue;
+					event.floatValue = Reflect.hasField(eventMap, "float") ? getFloat(Reflect.getProperty(eventMap, "float")) : eventData.floatValue;
+					event.stringValue = Reflect.hasField(eventMap, "string") ? Reflect.getProperty(eventMap, "string") : eventData.stringValue;
+					if (eventData.audioPath != null) {
+						event.volume = getFloat(Reflect.getProperty(eventMap, "volume"), 1);
+						event.balance = getFloat(Reflect.getProperty(eventMap, "balance"));
+					}
+					eventTimeline.setFrame(frame++, event);
+				}
+				timelines.push(eventTimeline);
+			}
+		}
+
+		var duration:Float = 0;
+		for (i in 0...timelines.length) {
+			duration = Math.max(duration, timelines[i].getDuration());
+		}
+
+		skeletonData.animations.push(new Animation(name, timelines, duration));
+	}
+
+	static private function readTimeline(keys:Array<Dynamic>, timeline:CurveTimeline1, defaultValue:Float, scale:Float):CurveTimeline1 {
+		var keyMap:Object = keys[0];
+		var time:Float = getFloat(Reflect.getProperty(keyMap, "time"));
+		var value:Float = getFloat(Reflect.getProperty(keyMap, "value"), defaultValue) * scale;
+		var bezier:Int = 0;
+		var frame:Int = 0;
+		while (true) {
+			timeline.setFrame(frame, time, value);
+			var nextMap:Object = keys[frame + 1];
+			if (nextMap == null) {
+				timeline.shrink(bezier);
+				break;
+			}
+			var time2:Float = getFloat(Reflect.getProperty(nextMap, "time"));
+			var value2:Float = getFloat(Reflect.getProperty(nextMap, "value"), defaultValue) * scale;
+			var curve:Object = keyMap.curve;
+			if (curve != null) {
+				bezier = readCurve(curve, timeline, bezier, frame, 0, time, time2, value, value2, scale);
+			}
+			time = time2;
+			value = value2;
+			keyMap = nextMap;
+
+			frame++;
+		}
+		return timeline;
+	}
+
+	static private function readTimeline2(keys:Array<Dynamic>, timeline:CurveTimeline2, name1:String, name2:String, defaultValue:Float,
+			scale:Float):CurveTimeline2 {
+		var keyMap:Object = keys[0];
+		var time:Float = getFloat(Reflect.getProperty(keyMap, "time"));
+		var value1:Float = getFloat(Reflect.getProperty(keyMap, name1), defaultValue) * scale;
+		var value2:Float = getFloat(Reflect.getProperty(keyMap, name2), defaultValue) * scale;
+		var bezier:Int = 0;
+		var frame:Int = 0;
+		while (true) {
+			timeline.setFrame(frame, time, value1, value2);
+			var nextMap:Object = keys[frame + 1];
+			if (nextMap == null) {
+				timeline.shrink(bezier);
+				break;
+			}
+			var time2:Float = getFloat(Reflect.getProperty(nextMap, "time"));
+			var nvalue1:Float = getFloat(Reflect.getProperty(nextMap, name1), defaultValue) * scale;
+			var nvalue2:Float = getFloat(Reflect.getProperty(nextMap, name2), defaultValue) * scale;
+			var curve:Object = keyMap.curve;
+			if (curve != null) {
+				bezier = readCurve(curve, timeline, bezier, frame, 0, time, time2, value1, nvalue1, scale);
+				bezier = readCurve(curve, timeline, bezier, frame, 1, time, time2, value2, nvalue2, scale);
+			}
+			time = time2;
+			value1 = nvalue1;
+			value2 = nvalue2;
+			keyMap = nextMap;
+
+			frame++;
+		}
+		return timeline;
+	}
+
+	static private function readCurve(curve:Object, timeline:CurveTimeline, bezier:Int, frame:Int, value:Int, time1:Float, time2:Float, value1:Float,
+			value2:Float, scale:Float):Int {
+		if (curve == "stepped") {
+			timeline.setStepped(frame);
+			return bezier;
+		}
+
+		var i:Int = value << 2;
+		var cx1:Float = curve[Std.string(i)];
+		var cy1:Float = curve[Std.string(i + 1)] * scale;
+		var cx2:Float = curve[Std.string(i + 2)];
+		var cy2:Float = curve[Std.string(i + 3)] * scale;
+		timeline.setBezier(bezier, frame, value, time1, value1, cx1, cy1, cx2, cy2, time2, value2);
+		return bezier + 1;
+	}
+
+	static private function getFloat(value:Object, defaultValue:Float = 0):Float {
+		if (Std.isOfType(value, Float))
+			return cast(value, Float);
+		var floatValue:Float = Std.parseFloat(value);
+		if (Math.isNaN(floatValue))
+			floatValue = defaultValue;
+		return floatValue;
+	}
+
+	static private function getFloatArray(map:Object, name:String):Vector<Float> {
+		var list:Array<Dynamic> = cast(map[name], Array<Dynamic>);
+		var values:Vector<Float> = new Vector<Float>(list.length, true);
+		for (i in 0...list.length) {
+			values[i] = getFloat(list[i]);
+		}
+		return values;
+	}
+
+	static private function getInt(value:Object):Int {
+		if (Std.isOfType(value, Int))
+			return cast(value, Int);
+		var intValue:Null<Int> = Std.parseInt(value);
+		if (intValue == null)
+			intValue = 0;
+		return intValue;
+	}
+
+	static private function getIntArray(map:Object, name:String):Vector<Int> {
+		var list:Array<Dynamic> = cast(map[name], Array<Dynamic>);
+		var values:Vector<Int> = new Vector<Int>(list.length, true);
+		for (i in 0...list.length) {
+			values[i] = getInt(list[i]);
+		}
+		return values;
+	}
+}
+
+class LinkedMesh {
+	public var parent(default, null):String;
+	public var skin(default, null):String;
+	public var slotIndex(default, null):Int;
+	public var mesh(default, null):MeshAttachment;
+	public var inheritDeform(default, null):Bool;
+
+	public function new(mesh:MeshAttachment, skin:String, slotIndex:Int, parent:String, inheritDeform:Bool) {
+		this.mesh = mesh;
+		this.skin = skin;
+		this.slotIndex = slotIndex;
+		this.parent = parent;
+		this.inheritDeform = inheritDeform;
+	}
+}

+ 209 - 0
spine-haxe/spine-haxe/spine/Skin.hx

@@ -0,0 +1,209 @@
+package spine;
+
+import openfl.errors.ArgumentError;
+import openfl.utils.Dictionary;
+import openfl.Vector;
+import spine.attachments.Attachment;
+import spine.attachments.MeshAttachment;
+
+/** Stores attachments by slot index and attachment name. */
+class Skin {
+	private var _name:String;
+	private var _attachments:Vector<Dictionary<String, Attachment>> = new Vector<Dictionary<String, Attachment>>();
+	private var _bones:Vector<BoneData> = new Vector<BoneData>();
+	private var _constraints:Vector<ConstraintData> = new Vector<ConstraintData>();
+
+	public function new(name:String) {
+		if (name == null)
+			throw new ArgumentError("name cannot be null.");
+		_name = name;
+	}
+
+	public function setAttachment(slotIndex:Int, name:String, attachment:Attachment):Void {
+		if (attachment == null)
+			throw new ArgumentError("attachment cannot be null.");
+		if (slotIndex >= _attachments.length)
+			_attachments.length = slotIndex + 1;
+		if (_attachments[slotIndex] == null)
+			_attachments[slotIndex] = new Dictionary<String, Attachment>();
+		_attachments[slotIndex][name] = attachment;
+	}
+
+	public function addSkin(skin:Skin):Void {
+		var contained:Bool = false;
+		for (i in 0...skin.bones.length) {
+			var bone:BoneData = skin.bones[i];
+			contained = false;
+			for (j in 0...bones.length) {
+				if (_bones[j] == bone) {
+					contained = true;
+					break;
+				}
+			}
+			if (!contained)
+				_bones.push(bone);
+		}
+
+		for (i in 0...skin.constraints.length) {
+			var constraint:ConstraintData = skin.constraints[i];
+			contained = false;
+			for (j in 0..._constraints.length) {
+				if (_constraints[j] == constraint) {
+					contained = true;
+					break;
+				}
+			}
+			if (!contained)
+				_constraints.push(constraint);
+		}
+
+		var attachments:Vector<SkinEntry> = skin.getAttachments();
+		for (i in 0...attachments.length) {
+			var attachment:SkinEntry = attachments[i];
+			setAttachment(attachment.slotIndex, attachment.name, attachment.attachment);
+		}
+	}
+
+	public function copySkin(skin:Skin):Void {
+		var contained:Bool = false;
+		var attachment:SkinEntry;
+
+		for (i in 0...skin.bones.length) {
+			var bone:BoneData = skin.bones[i];
+			contained = false;
+			for (j in 0..._bones.length) {
+				if (_bones[j] == bone) {
+					contained = true;
+					break;
+				}
+			}
+			if (!contained)
+				_bones.push(bone);
+		}
+
+		for (i in 0...skin.constraints.length) {
+			var constraint:ConstraintData = skin.constraints[i];
+			contained = false;
+			for (j in 0..._constraints.length) {
+				if (_constraints[j] == constraint) {
+					contained = true;
+					break;
+				}
+			}
+			if (!contained)
+				_constraints.push(constraint);
+		}
+
+		var attachments:Vector<SkinEntry> = skin.getAttachments();
+		for (i in 0...attachments.length) {
+			attachment = attachments[i];
+			if (attachment.attachment == null)
+				continue;
+			if (Std.isOfType(attachment.attachment, MeshAttachment)) {
+				attachment.attachment = new MeshAttachment(attachment.attachment.name).newLinkedMesh();
+				setAttachment(attachment.slotIndex, attachment.name, attachment.attachment);
+			} else {
+				attachment.attachment = attachment.attachment.copy();
+				setAttachment(attachment.slotIndex, attachment.name, attachment.attachment);
+			}
+		}
+	}
+
+	public function getAttachment(slotIndex:Int, name:String):Attachment {
+		if (slotIndex >= _attachments.length)
+			return null;
+		var dictionary:Dictionary<String, Attachment> = _attachments[slotIndex];
+		return dictionary != null ? dictionary[name] : null;
+	}
+
+	public function removeAttachment(slotIndex:Int, name:String):Void {
+		var dictionary:Dictionary<String, Attachment> = _attachments[slotIndex];
+		if (dictionary != null)
+			dictionary[name] = null;
+	}
+
+	public function getAttachments():Vector<SkinEntry> {
+		var entries:Vector<SkinEntry> = new Vector<SkinEntry>();
+		for (slotIndex in 0..._attachments.length) {
+			var attachments:Dictionary<String, Attachment> = _attachments[slotIndex];
+			if (attachments != null) {
+				for (name in attachments.iterator()) {
+					var attachment:Attachment = attachments[name];
+					if (attachment != null)
+						entries.push(new SkinEntry(slotIndex, name, attachment));
+				}
+			}
+		}
+		return entries;
+	}
+
+	public function getAttachmentsForSlot(slotIndex:Int):Vector<SkinEntry> {
+		var entries:Vector<SkinEntry> = new Vector<SkinEntry>();
+		var attachments:Dictionary<String, Attachment> = _attachments[slotIndex];
+		if (attachments != null) {
+			for (name in attachments.iterator()) {
+				var attachment:Attachment = attachments[name];
+				if (attachment != null)
+					entries.push(new SkinEntry(slotIndex, name, attachment));
+			}
+		}
+		return entries;
+	}
+
+	public function clear():Void {
+		_attachments.length = 0;
+		_bones.length = 0;
+		_constraints.length = 0;
+	}
+
+	public var attachments(get, never):Vector<Dictionary<String, Attachment>>;
+
+	private function get_attachments():Vector<Dictionary<String, Attachment>> {
+		return _attachments;
+	}
+
+	public var bones(get, never):Vector<BoneData>;
+
+	private function get_bones():Vector<BoneData> {
+		return _bones;
+	}
+
+	public var constraints(get, never):Vector<ConstraintData>;
+
+	private function get_constraints():Vector<ConstraintData> {
+		return _constraints;
+	}
+
+	public var name(get, never):String;
+
+	private function get_name():String {
+		return _name;
+	}
+
+	/*
+		public function toString():String
+		{
+			return _name;
+		}
+	 */
+	/** Attach each attachment in this skin if the corresponding attachment in the old skin is currently attached. */
+	public function attachAll(skeleton:Skeleton, oldSkin:Skin):Void {
+		var slotIndex:Int = 0;
+		for (slot in skeleton.slots) {
+			var slotAttachment:Attachment = slot.attachment;
+			if (slotAttachment != null && slotIndex < oldSkin.attachments.length) {
+				var dictionary:Dictionary<String, Attachment> = oldSkin.attachments[slotIndex];
+				for (name in dictionary) {
+					var skinAttachment:Attachment = dictionary[name];
+					if (slotAttachment == skinAttachment) {
+						var attachment:Attachment = getAttachment(slotIndex, name);
+						if (attachment != null)
+							slot.attachment = attachment;
+						break;
+					}
+				}
+			}
+			slotIndex++;
+		}
+	}
+}

+ 15 - 0
spine-haxe/spine-haxe/spine/SkinEntry.hx

@@ -0,0 +1,15 @@
+package spine;
+
+import spine.attachments.Attachment;
+
+class SkinEntry {
+	public var slotIndex:Int = 0;
+	public var name:String;
+	public var attachment:Attachment;
+
+	public function new(slotIndex:Int, name:String, attachment:Attachment) {
+		this.slotIndex = slotIndex;
+		this.name = name;
+		this.attachment = attachment;
+	}
+}

+ 101 - 0
spine-haxe/spine-haxe/spine/Slot.hx

@@ -0,0 +1,101 @@
+package spine;
+
+import openfl.errors.ArgumentError;
+import openfl.Vector;
+import spine.attachments.Attachment;
+import spine.attachments.VertexAttachment;
+
+class Slot {
+	private var _data:SlotData;
+	private var _bone:Bone;
+
+	public var color:Color;
+	public var darkColor:Color;
+
+	private var _attachment:Attachment;
+	private var _attachmentTime:Float = 0;
+
+	public var attachmentState:Int = 0;
+	public var deform:Vector<Float> = new Vector<Float>();
+
+	public function new(data:SlotData, bone:Bone) {
+		if (data == null)
+			throw new ArgumentError("data cannot be null.");
+		if (bone == null)
+			throw new ArgumentError("bone cannot be null.");
+		_data = data;
+		_bone = bone;
+		this.color = new Color(1, 1, 1, 1);
+		this.darkColor = data.darkColor == null ? null : new Color(1, 1, 1, 1);
+		setToSetupPose();
+	}
+
+	public var data(get, never):SlotData;
+
+	private function get_data():SlotData {
+		return _data;
+	}
+
+	public var bone(get, never):Bone;
+
+	private function get_bone():Bone {
+		return _bone;
+	}
+
+	public var skeleton(get, never):Skeleton;
+
+	private function get_skeleton():Skeleton {
+		return _bone.skeleton;
+	}
+
+	/** @return May be null. */
+	public var attachment(get, set):Attachment;
+
+	private function get_attachment():Attachment {
+		return _attachment;
+	}
+
+	/** Sets the slot's attachment and, if the attachment changed, resets {@link #attachmentTime} and clears the {@link #deform}.
+	 * The deform is not cleared if the old attachment has the same {@link VertexAttachment#getDeformAttachment()} as the specified attachment.
+	 * @param attachment May be null. */
+	public function set_attachment(attachmentNew:Attachment):Attachment {
+		if (attachment == attachmentNew)
+			return attachmentNew;
+		if (!Std.isOfType(attachmentNew, VertexAttachment)
+			|| !Std.isOfType(attachment, VertexAttachment)
+			|| cast(attachmentNew, VertexAttachment).deformAttachment != cast(attachment, VertexAttachment).deformAttachment) {
+			deform = new Vector<Float>();
+		}
+		_attachment = attachmentNew;
+		_attachmentTime = skeleton.time;
+		return attachmentNew;
+	}
+
+	public var attachmentTime(get, set):Float;
+
+	private function set_attachmentTime(time:Float):Float {
+		_attachmentTime = skeleton.time - time;
+		return _attachmentTime;
+	}
+
+	/** Returns the time since the attachment was set. */
+	private function get_attachmentTime():Float {
+		return skeleton.time - _attachmentTime;
+	}
+
+	public function setToSetupPose():Void {
+		color.setFromColor(data.color);
+		if (darkColor != null)
+			darkColor.setFromColor(data.darkColor);
+		if (_data.attachmentName == null) {
+			attachment = null;
+		} else {
+			_attachment = null;
+			attachment = skeleton.getAttachmentForSlotIndex(data.index, data.attachmentName);
+		}
+	}
+
+	public function toString():String {
+		return _data.name != null ? _data.name : "Slot?";
+	}
+}

+ 48 - 0
spine-haxe/spine-haxe/spine/SlotData.hx

@@ -0,0 +1,48 @@
+package spine;
+
+import openfl.errors.ArgumentError;
+
+class SlotData {
+	private var _index:Int;
+	private var _name:String;
+	private var _boneData:BoneData;
+
+	public var color:Color = new Color(1, 1, 1, 1);
+	public var darkColor:Color = null;
+	public var attachmentName:String;
+	public var blendMode:BlendMode;
+
+	public function new(index:Int, name:String, boneData:BoneData) {
+		if (index < 0)
+			throw new ArgumentError("index must be >= 0.");
+		if (name == null)
+			throw new ArgumentError("name cannot be null.");
+		if (boneData == null)
+			throw new ArgumentError("boneData cannot be null.");
+		_index = index;
+		_name = name;
+		_boneData = boneData;
+	}
+
+	public var index(get, never):Int;
+
+	private function get_index():Int {
+		return _index;
+	}
+
+	public var name(get, never):String;
+
+	private function get_name():String {
+		return _name;
+	}
+
+	public var boneData(get, never):BoneData;
+
+	private function get_boneData():BoneData {
+		return _boneData;
+	}
+
+	public function toString():String {
+		return _name;
+	}
+}

+ 26 - 0
spine-haxe/spine-haxe/spine/SpacingMode.hx

@@ -0,0 +1,26 @@
+package spine;
+
+import openfl.Vector;
+
+class SpacingMode {
+	public static var length(default, never):SpacingMode = new SpacingMode("length");
+	public static var fixed(default, never):SpacingMode = new SpacingMode("fixed");
+	public static var percent(default, never):SpacingMode = new SpacingMode("percent");
+	public static var proportional(default, never):SpacingMode = new SpacingMode("proportional");
+
+	public static var values(default, never):Vector<SpacingMode> = Vector.ofArray([length, fixed, percent, proportional]);
+
+	public var name(default, null):String;
+
+	public function new(name:String) {
+		this.name = name;
+	}
+
+	public static function fromName(name:String):SpacingMode {
+		for (value in values) {
+			if (value.name == name)
+				return value;
+		}
+		return null;
+	}
+}

+ 259 - 0
spine-haxe/spine-haxe/spine/TransformConstraint.hx

@@ -0,0 +1,259 @@
+package spine;
+
+import openfl.errors.ArgumentError;
+import openfl.Vector;
+
+class TransformConstraint implements Updatable {
+	private var _data:TransformConstraintData;
+	private var _bones:Vector<Bone>;
+
+	public var target:Bone;
+	public var mixRotate:Float = 0;
+	public var mixX:Float = 0;
+	public var mixY:Float = 0;
+	public var mixScaleX:Float = 0;
+	public var mixScaleY:Float = 0;
+	public var mixShearY:Float = 0;
+
+	private var _temp:Vector<Float> = new Vector<Float>(2, true);
+
+	public var active:Bool = false;
+
+	public function new(data:TransformConstraintData, skeleton:Skeleton) {
+		if (data == null)
+			throw new ArgumentError("data cannot be null.");
+		if (skeleton == null)
+			throw new ArgumentError("skeleton cannot be null.");
+		_data = data;
+		mixRotate = data.mixRotate;
+		mixX = data.mixX;
+		mixY = data.mixY;
+		mixScaleX = data.mixScaleX;
+		mixScaleY = data.mixScaleY;
+		mixShearY = data.mixShearY;
+		_bones = new Vector<Bone>();
+		for (boneData in data.bones) {
+			_bones.push(skeleton.findBone(boneData.name));
+		}
+		target = skeleton.findBone(data.target.name);
+	}
+
+	public function isActive():Bool {
+		return active;
+	}
+
+	public function update():Void {
+		if (mixRotate == 0 && mixX == 0 && mixY == 0 && mixScaleX == 0 && mixScaleX == 0 && mixShearY == 0)
+			return;
+
+		if (data.local) {
+			if (data.relative) {
+				applyRelativeLocal();
+			} else {
+				applyAbsoluteLocal();
+			}
+		} else {
+			if (data.relative) {
+				applyRelativeWorld();
+			} else {
+				applyAbsoluteWorld();
+			}
+		}
+	}
+
+	private function applyAbsoluteWorld():Void {
+		var translate:Bool = mixX != 0 || mixY != 0;
+		var ta:Float = target.a,
+			tb:Float = target.b,
+			tc:Float = target.c,
+			td:Float = target.d;
+		var degRadReflect:Float = ta * td - tb * tc > 0 ? MathUtils.degRad : -MathUtils.degRad;
+		var offsetRotation:Float = data.offsetRotation * degRadReflect;
+		var offsetShearY:Float = data.offsetShearY * degRadReflect;
+		for (bone in bones) {
+			if (mixRotate != 0) {
+				var a:Float = bone.a,
+					b:Float = bone.b,
+					c:Float = bone.c,
+					d:Float = bone.d;
+				var r:Float = Math.atan2(tc, ta) - Math.atan2(c, a) + offsetRotation;
+				if (r > Math.PI)
+					r -= Math.PI * 2;
+				else if (r < -Math.PI)
+					r += Math.PI * 2;
+				r *= mixRotate;
+				var cos:Float = Math.cos(r), sin:Float = Math.sin(r);
+				bone.a = cos * a - sin * c;
+				bone.b = cos * b - sin * d;
+				bone.c = sin * a + cos * c;
+				bone.d = sin * b + cos * d;
+			}
+
+			if (translate) {
+				_temp[0] = data.offsetX;
+				_temp[1] = data.offsetY;
+				target.localToWorld(_temp);
+				bone.worldX += (_temp[0] - bone.worldX) * mixX;
+				bone.worldY += (_temp[1] - bone.worldY) * mixY;
+			}
+
+			if (mixScaleX != 0) {
+				var s:Float = Math.sqrt(bone.a * bone.a + bone.c * bone.c);
+				if (s != 0)
+					s = (s + (Math.sqrt(ta * ta + tc * tc) - s + _data.offsetScaleX) * mixScaleX) / s;
+				bone.a *= s;
+				bone.c *= s;
+			}
+
+			if (mixScaleY != 0) {
+				var s:Float = Math.sqrt(bone.b * bone.b + bone.d * bone.d);
+				if (s != 0)
+					s = (s + (Math.sqrt(tb * tb + td * td) - s + _data.offsetScaleY) * mixScaleY) / s;
+				bone.b *= s;
+				bone.d *= s;
+			}
+
+			if (mixShearY > 0) {
+				var by:Float = Math.atan2(bone.d, bone.b);
+				var r:Float = Math.atan2(td, tb) - Math.atan2(tc, ta) - (by - Math.atan2(bone.c, bone.a));
+				if (r > Math.PI)
+					r -= Math.PI * 2;
+				else if (r < -Math.PI)
+					r += Math.PI * 2;
+				r = by + (r + offsetShearY) * mixShearY;
+				var s:Float = Math.sqrt(bone.b * bone.b + bone.d * bone.d);
+				bone.b = Math.cos(r) * s;
+				bone.d = Math.sin(r) * s;
+			}
+
+			bone.updateAppliedTransform();
+		}
+	}
+
+	public function applyRelativeWorld():Void {
+		var translate:Bool = mixX != 0 || mixY != 0;
+		var ta:Float = target.a,
+			tb:Float = target.b,
+			tc:Float = target.c,
+			td:Float = target.d;
+		var degRadReflect:Float = ta * td - tb * tc > 0 ? MathUtils.degRad : -MathUtils.degRad;
+		var offsetRotation:Float = _data.offsetRotation * degRadReflect,
+			offsetShearY:Float = _data.offsetShearY * degRadReflect;
+		for (bone in bones) {
+			if (mixRotate != 0) {
+				var a:Float = bone.a,
+					b:Float = bone.b,
+					c:Float = bone.c,
+					d:Float = bone.d;
+				var r:Float = Math.atan2(tc, ta) + offsetRotation;
+				if (r > MathUtils.PI)
+					r -= MathUtils.PI2;
+				else if (r < -MathUtils.PI)
+					r += MathUtils.PI2;
+				r *= mixRotate;
+				var cos:Float = Math.cos(r), sin:Float = Math.sin(r);
+				bone.a = cos * a - sin * c;
+				bone.b = cos * b - sin * d;
+				bone.c = sin * a + cos * c;
+				bone.d = sin * b + cos * d;
+			}
+
+			if (translate) {
+				var temp:Vector<Float> = _temp;
+				temp[0] = _data.offsetX;
+				temp[1] = _data.offsetY;
+				target.localToWorld(temp);
+				bone.worldX += temp[0] * mixX;
+				bone.worldY += temp[1] * mixY;
+			}
+
+			if (mixScaleX != 0) {
+				var s:Float = (Math.sqrt(ta * ta + tc * tc) - 1 + _data.offsetScaleX) * mixScaleX + 1;
+				bone.a *= s;
+				bone.c *= s;
+			}
+
+			if (mixScaleY != 0) {
+				var s:Float = (Math.sqrt(tb * tb + td * td) - 1 + _data.offsetScaleY) * mixScaleY + 1;
+				bone.b *= s;
+				bone.d *= s;
+			}
+
+			if (mixShearY > 0) {
+				var r = Math.atan2(td, tb) - Math.atan2(tc, ta);
+				if (r > MathUtils.PI)
+					r -= MathUtils.PI2;
+				else if (r < -MathUtils.PI)
+					r += MathUtils.PI2;
+				var b = bone.b;
+				var d = bone.d;
+				r = Math.atan2(d, b) + (r - MathUtils.PI / 2 + offsetShearY) * mixShearY;
+				var s = Math.sqrt(b * b + d * d);
+				bone.b = Math.cos(r) * s;
+				bone.d = Math.sin(r) * s;
+			}
+
+			bone.updateAppliedTransform();
+		}
+	}
+
+	public function applyAbsoluteLocal():Void {
+		for (bone in bones) {
+			var rotation:Float = bone.arotation;
+			if (mixRotate != 0) {
+				var r:Float = target.arotation - rotation + _data.offsetRotation;
+				r -= (16384 - (Std.int(16384.499999999996 - r / 360) | 0)) * 360;
+				rotation += r * mixRotate;
+			}
+
+			var x:Float = bone.ax, y:Float = bone.ay;
+			x += (target.ax - x + _data.offsetX) * mixX;
+			y += (target.ay - y + _data.offsetY) * mixY;
+
+			var scaleX:Float = bone.ascaleX, scaleY:Float = bone.ascaleY;
+			if (mixScaleX != 0 && scaleX != 0) {
+				scaleX = (scaleX + (target.ascaleX - scaleX + _data.offsetScaleX) * mixScaleX) / scaleX;
+			}
+			if (mixScaleY != 0 && scaleY != 0) {
+				scaleY = (scaleY + (target.ascaleY - scaleY + _data.offsetScaleY) * mixScaleX) / scaleY;
+			}
+
+			var shearY:Float = bone.ashearY;
+			if (mixShearY != 0) {
+				var r:Float = target.ashearY - shearY + _data.offsetShearY;
+				r -= (16384 - (Std.int(16384.499999999996 - r / 360) | 0)) * 360;
+				bone.shearY += r * mixShearY;
+			}
+
+			bone.updateWorldTransformWith(x, y, rotation, scaleX, scaleY, bone.ashearX, shearY);
+		}
+	}
+
+	public function applyRelativeLocal():Void {
+		for (bone in bones) {
+			var rotation:Float = bone.arotation + (target.arotation + _data.offsetRotation) * mixRotate;
+			var x:Float = bone.ax + (target.ax + _data.offsetX) * mixX;
+			var y:Float = bone.ay + (target.ay + _data.offsetY) * mixY;
+			var scaleX:Float = bone.ascaleX * (((target.ascaleX - 1 + _data.offsetScaleX) * mixScaleX) + 1);
+			var scaleY:Float = bone.ascaleY * (((target.ascaleY - 1 + _data.offsetScaleY) * mixScaleY) + 1);
+			var shearY:Float = bone.ashearY + (target.ashearY + _data.offsetShearY) * mixShearY;
+			bone.updateWorldTransformWith(x, y, rotation, scaleX, scaleY, bone.ashearX, shearY);
+		}
+	}
+
+	public var data(get, never):TransformConstraintData;
+
+	private function get_data():TransformConstraintData {
+		return _data;
+	}
+
+	public var bones(get, never):Vector<Bone>;
+
+	private function get_bones():Vector<Bone> {
+		return _bones;
+	}
+
+	public function toString():String {
+		return _data.name != null ? _data.name : "TransformConstraint?";
+	}
+}

+ 33 - 0
spine-haxe/spine-haxe/spine/TransformConstraintData.hx

@@ -0,0 +1,33 @@
+package spine;
+
+import openfl.Vector;
+
+class TransformConstraintData extends ConstraintData {
+	private var _bones:Vector<BoneData> = new Vector<BoneData>();
+
+	public var target:BoneData;
+	public var mixRotate:Float = 0;
+	public var mixX:Float = 0;
+	public var mixY:Float = 0;
+	public var mixScaleX:Float = 0;
+	public var mixScaleY:Float = 0;
+	public var mixShearY:Float = 0;
+	public var offsetRotation:Float = 0;
+	public var offsetX:Float = 0;
+	public var offsetY:Float = 0;
+	public var offsetScaleX:Float = 0;
+	public var offsetScaleY:Float = 0;
+	public var offsetShearY:Float = 0;
+	public var relative:Bool = false;
+	public var local:Bool = false;
+
+	public function new(name:String) {
+		super(name, 0, false);
+	}
+
+	public var bones(get, never):Vector<BoneData>;
+
+	private function get_bones():Vector<BoneData> {
+		return _bones;
+	}
+}

+ 27 - 0
spine-haxe/spine-haxe/spine/TransformMode.hx

@@ -0,0 +1,27 @@
+package spine;
+
+import openfl.Vector;
+
+class TransformMode {
+	public static var normal(default, never):TransformMode = new TransformMode("normal");
+	public static var onlyTranslation(default, never):TransformMode = new TransformMode("onlyTranslation");
+	public static var noRotationOrReflection(default, never):TransformMode = new TransformMode("noRotationOrReflection");
+	public static var noScale(default, never):TransformMode = new TransformMode("noScale");
+	public static var noScaleOrReflection(default, never):TransformMode = new TransformMode("noScaleOrReflection");
+
+	public static var values:Vector<TransformMode> = Vector.ofArray([normal, onlyTranslation, noRotationOrReflection, noScale, noScaleOrReflection]);
+
+	public var name(default, null):String;
+
+	public function new(name:String) {
+		this.name = name;
+	}
+
+	public static function fromName(name:String):TransformMode {
+		for (value in values) {
+			if (value.name == name)
+				return value;
+		}
+		return null;
+	}
+}

+ 283 - 0
spine-haxe/spine-haxe/spine/Triangulator.hx

@@ -0,0 +1,283 @@
+package spine;
+
+import openfl.Vector;
+
+class Triangulator {
+	private var convexPolygons:Vector<Vector<Float>> = new Vector<Vector<Float>>();
+	private var convexPolygonsIndices:Vector<Vector<Int>> = new Vector<Vector<Int>>();
+	private var indicesArray:Vector<Int> = new Vector<Int>();
+	private var isConcaveArray:Vector<Bool> = new Vector<Bool>();
+	private var triangles:Vector<Int> = new Vector<Int>();
+	private var polygonPool:Pool<Vector<Float>> = new Pool(function():Dynamic {
+		return new Vector<Float>();
+	});
+	private var polygonIndicesPool:Pool<Vector<Int>> = new Pool(function():Dynamic {
+		return new Vector<Int>();
+	});
+
+	public function new() {}
+
+	public function triangulate(vertices:Vector<Float>):Vector<Int> {
+		var vertexCount:Int = vertices.length >> 1;
+
+		indicesArray.length = 0;
+		for (i in 0...vertexCount) {
+			indicesArray.push(i);
+		}
+
+		isConcaveArray.length = 0;
+		for (i in 0...vertexCount) {
+			isConcaveArray.push(isConcave(i, vertexCount, vertices, indicesArray));
+		}
+
+		triangles.length = 0;
+
+		while (vertexCount > 3) {
+			// Find ear tip.
+			var previous:Int = vertexCount - 1, next:Int = 1;
+			var i:Int = 0;
+			while (true) {
+				if (!isConcaveArray[i]) {
+					var p1:Int = indicesArray[previous] << 1,
+						p2:Int = indicesArray[i] << 1,
+						p3:Int = indicesArray[next] << 1;
+					var p1x:Float = vertices[p1], p1y:Float = vertices[p1 + 1];
+					var p2x:Float = vertices[p2], p2y:Float = vertices[p2 + 1];
+					var p3x:Float = vertices[p3], p3y:Float = vertices[p3 + 1];
+					var ii:Int = (next + 1) % vertexCount;
+					while (ii != previous) {
+						if (!isConcaveArray[ii]) {
+							ii = (ii + 1) % vertexCount;
+							continue;
+						}
+						var v:Int = indicesArray[ii] << 1;
+						var vx:Float = vertices[v];
+						var vy:Float = vertices[v + 1];
+						if (positiveArea(p3x, p3y, p1x, p1y, vx, vy)) {
+							if (positiveArea(p1x, p1y, p2x, p2y, vx, vy)) {
+								if (positiveArea(p2x, p2y, p3x, p3y, vx, vy)) {
+									break;
+								}
+							}
+						}
+						ii = (ii + 1) % vertexCount;
+					}
+					break;
+				}
+
+				if (next == 0) {
+					do {
+						if (!isConcaveArray[i])
+							break;
+						i--;
+					} while (i > 0);
+					break;
+				}
+
+				previous = i;
+				i = next;
+				next = (next + 1) % vertexCount;
+			}
+
+			// Cut ear tip.
+			triangles.push(indicesArray[(vertexCount + i - 1) % vertexCount]);
+			triangles.push(indicesArray[i]);
+			triangles.push(indicesArray[(i + 1) % vertexCount]);
+			indicesArray.splice(i, 1);
+			isConcaveArray.splice(i, 1);
+			vertexCount--;
+
+			var previousIndex:Int = (vertexCount + i - 1) % vertexCount;
+			var nextIndex:Int = i == vertexCount ? 0 : i;
+			isConcaveArray[previousIndex] = isConcave(previousIndex, vertexCount, vertices, indicesArray);
+			isConcaveArray[nextIndex] = isConcave(nextIndex, vertexCount, vertices, indicesArray);
+		}
+
+		if (vertexCount == 3) {
+			triangles.push(indicesArray[2]);
+			triangles.push(indicesArray[0]);
+			triangles.push(indicesArray[1]);
+		}
+
+		return triangles;
+	}
+
+	public function decompose(vertices:Vector<Float>, triangles:Vector<Int>):Vector<Vector<Float>> {
+		for (i in 0...convexPolygons.length) {
+			this.polygonPool.free(convexPolygons[i]);
+		}
+		convexPolygons.length = 0;
+
+		for (i in 0...convexPolygonsIndices.length) {
+			this.polygonIndicesPool.free(convexPolygonsIndices[i]);
+		}
+		convexPolygonsIndices.length = 0;
+
+		var polygonIndices:Vector<Int> = polygonIndicesPool.obtain();
+		polygonIndices.length = 0;
+
+		var polygon:Vector<Float> = polygonPool.obtain();
+		polygon.length = 0;
+
+		// Merge subsequent triangles if they form a triangle fan.
+		var fanBaseIndex:Int = -1, lastWinding:Int = 0;
+		var x1:Float, y1:Float, x2:Float, y2:Float, x3:Float, y3:Float;
+		var winding1:Int, winding2:Int, o:Int;
+		var i:Int = 0;
+		while (i < triangles.length) {
+			var t1:Int = triangles[i] << 1,
+				t2:Int = triangles[i + 1] << 1,
+				t3:Int = triangles[i + 2] << 1;
+			x1 = vertices[t1];
+			y1 = vertices[t1 + 1];
+			x2 = vertices[t2];
+			y2 = vertices[t2 + 1];
+			x3 = vertices[t3];
+			y3 = vertices[t3 + 1];
+
+			// If the base of the last triangle is the same as this triangle, check if they form a convex polygon (triangle fan).
+			var merged:Bool = false;
+			if (fanBaseIndex == t1) {
+				o = polygon.length - 4;
+				winding1 = Triangulator.winding(polygon[o], polygon[o + 1], polygon[o + 2], polygon[o + 3], x3, y3);
+				winding2 = Triangulator.winding(x3, y3, polygon[0], polygon[1], polygon[2], polygon[3]);
+				if (winding1 == lastWinding && winding2 == lastWinding) {
+					polygon.push(x3);
+					polygon.push(y3);
+					polygonIndices.push(t3);
+					merged = true;
+				}
+			}
+
+			// Otherwise make this triangle the new base.
+			if (!merged) {
+				if (polygon.length > 0) {
+					convexPolygons.push(polygon);
+					convexPolygonsIndices.push(polygonIndices);
+				} else {
+					polygonPool.free(polygon);
+					polygonIndicesPool.free(polygonIndices);
+				}
+				polygon = polygonPool.obtain();
+				polygon.length = 0;
+				polygon.push(x1);
+				polygon.push(y1);
+				polygon.push(x2);
+				polygon.push(y2);
+				polygon.push(x3);
+				polygon.push(y3);
+				polygonIndices = polygonIndicesPool.obtain();
+				polygonIndices.length = 0;
+				polygonIndices.push(t1);
+				polygonIndices.push(t2);
+				polygonIndices.push(t3);
+				lastWinding = Triangulator.winding(x1, y1, x2, y2, x3, y3);
+				fanBaseIndex = t1;
+			}
+
+			i += 3;
+		}
+
+		if (polygon.length > 0) {
+			convexPolygons.push(polygon);
+			convexPolygonsIndices.push(polygonIndices);
+		}
+
+		// Go through the list of polygons and try to merge the remaining triangles with the found triangle fans.
+		i = 0;
+		var n:Int = convexPolygons.length;
+		while (i < n) {
+			polygonIndices = convexPolygonsIndices[i];
+			if (polygonIndices.length == 0) {
+				i++;
+				continue;
+			}
+			var firstIndex:Int = polygonIndices[0];
+			var lastIndex:Int = polygonIndices[polygonIndices.length - 1];
+
+			polygon = convexPolygons[i];
+			o = polygon.length - 4;
+			var prevPrevX:Float = polygon[o], prevPrevY:Float = polygon[o + 1];
+			var prevX:Float = polygon[o + 2], prevY:Float = polygon[o + 3];
+			var firstX:Float = polygon[0], firstY:Float = polygon[1];
+			var secondX:Float = polygon[2], secondY:Float = polygon[3];
+			var currWinding:Int = Triangulator.winding(prevPrevX, prevPrevY, prevX, prevY, firstX, firstY);
+
+			var ii:Int = 0;
+			while (ii < n) {
+				if (ii == i) {
+					ii++;
+					continue;
+				}
+				var otherIndices:Vector<Int> = convexPolygonsIndices[ii];
+				if (otherIndices.length != 3) {
+					ii++;
+					continue;
+				}
+				var otherFirstIndex:Int = otherIndices[0];
+				var otherSecondIndex:Int = otherIndices[1];
+				var otherLastIndex:Int = otherIndices[2];
+
+				var otherPoly:Vector<Float> = convexPolygons[ii];
+				x3 = otherPoly[otherPoly.length - 2];
+				y3 = otherPoly[otherPoly.length - 1];
+
+				if (otherFirstIndex != firstIndex || otherSecondIndex != lastIndex) {
+					ii++;
+					continue;
+				}
+				winding1 = Triangulator.winding(prevPrevX, prevPrevY, prevX, prevY, x3, y3);
+				winding2 = Triangulator.winding(x3, y3, firstX, firstY, secondX, secondY);
+				if (winding1 == currWinding && winding2 == currWinding) {
+					otherPoly.length = 0;
+					otherIndices.length = 0;
+					polygon.push(x3);
+					polygon.push(y3);
+					polygonIndices.push(otherLastIndex);
+					prevPrevX = prevX;
+					prevPrevY = prevY;
+					prevX = x3;
+					prevY = y3;
+					ii = 0;
+				}
+
+				ii++;
+			}
+
+			i++;
+		}
+
+		// Remove empty polygons that resulted from the merge step above.
+		i = convexPolygons.length - 1;
+		while (i >= 0) {
+			polygon = convexPolygons[i];
+			if (polygon.length == 0) {
+				convexPolygons.splice(i, 1);
+				this.polygonPool.free(polygon);
+				polygonIndices = convexPolygonsIndices[i];
+				convexPolygonsIndices.splice(i, 1);
+				this.polygonIndicesPool.free(polygonIndices);
+			}
+
+			i--;
+		}
+
+		return convexPolygons;
+	}
+
+	private static function isConcave(index:Int, vertexCount:Int, vertices:Vector<Float>, indices:Vector<Int>):Bool {
+		var previous:Int = indices[(vertexCount + index - 1) % vertexCount] << 1;
+		var current:Int = indices[index] << 1;
+		var next:Int = indices[(index + 1) % vertexCount] << 1;
+		return !positiveArea(vertices[previous], vertices[previous + 1], vertices[current], vertices[current + 1], vertices[next], vertices[next + 1]);
+	}
+
+	private static function positiveArea(p1x:Float, p1y:Float, p2x:Float, p2y:Float, p3x:Float, p3y:Float):Bool {
+		return p1x * (p3y - p2y) + p2x * (p1y - p3y) + p3x * (p2y - p1y) >= 0;
+	}
+
+	private static function winding(p1x:Float, p1y:Float, p2x:Float, p2y:Float, p3x:Float, p3y:Float):Int {
+		var px:Float = p2x - p1x, py:Float = p2y - p1y;
+		return p3x * py - p3y * px + px * p1y - p1x * py >= 0 ? 1 : -1;
+	}
+}

+ 7 - 0
spine-haxe/spine-haxe/spine/Updatable.hx

@@ -0,0 +1,7 @@
+package spine;
+
+interface Updatable {
+	function update():Void;
+
+	function isActive():Bool;
+}

+ 15 - 0
spine-haxe/spine-haxe/spine/Vertex.hx

@@ -0,0 +1,15 @@
+package spine;
+
+/**
+ * @author badlogic
+ */
+class Vertex {
+	public var x:Float = 0;
+	public var y:Float = 0;
+	public var u:Float = 0;
+	public var v:Float = 0;
+	public var light:Color = new spine.Color(0, 0, 0);
+	public var dark:Color = new spine.Color(0, 0, 0);
+
+	public function new() {}
+}

+ 9 - 0
spine-haxe/spine-haxe/spine/VertexEffect.hx

@@ -0,0 +1,9 @@
+package spine;
+
+interface VertexEffect {
+	function begin(skeleton:Skeleton):Void;
+
+	function transform(vertex:Vertex):Void;
+
+	function end():Void;
+}

+ 57 - 0
spine-haxe/spine-haxe/spine/animation/AlphaTimeline.hx

@@ -0,0 +1,57 @@
+package spine.animation;
+
+import openfl.Vector;
+import spine.Event;
+import spine.Skeleton;
+import spine.Slot;
+
+class AlphaTimeline extends CurveTimeline1 implements SlotTimeline {
+	private static inline var ENTRIES:Int = 4;
+	private static inline var R:Float = 1;
+	private static inline var G:Float = 2;
+	private static inline var B:Float = 3;
+
+	private var slotIndex:Int = 0;
+
+	public function new(frameCount:Int, bezierCount:Int, slotIndex:Int) {
+		super(frameCount, bezierCount, Vector.ofArray([Property.alpha + "|" + slotIndex]));
+		this.slotIndex = slotIndex;
+	}
+
+	public override function getFrameEntries():Int {
+		return ENTRIES;
+	}
+
+	public function getSlotIndex():Int {
+		return slotIndex;
+	}
+
+	public override function apply(skeleton:Skeleton, lastTime:Float, time:Float, events:Vector<Event>, alpha:Float, blend:MixBlend,
+			direction:MixDirection):Void {
+		var slot:Slot = skeleton.slots[slotIndex];
+		if (!slot.bone.active)
+			return;
+
+		var color:Color = slot.color;
+		if (time < frames[0]) // Time is before first frame.
+		{
+			var setup:Color = slot.data.color;
+			switch (blend) {
+				case MixBlend.setup:
+					color.a = setup.a;
+				case MixBlend.first:
+					color.a += (setup.a - color.a) * alpha;
+			}
+			return;
+		}
+
+		var a:Float = getCurveValue(time);
+		if (alpha == 1) {
+			color.a = a;
+		} else {
+			if (blend == MixBlend.setup)
+				color.a = slot.data.color.a;
+			color.a += (a - color.a) * alpha;
+		}
+	}
+}

+ 72 - 0
spine-haxe/spine-haxe/spine/animation/Animation.hx

@@ -0,0 +1,72 @@
+package spine.animation;
+
+import openfl.errors.ArgumentError;
+import openfl.utils.Dictionary;
+import openfl.Vector;
+import spine.Event;
+import spine.Skeleton;
+
+class Animation {
+	private var _name:String;
+	private var _timelines:Vector<Timeline>;
+	private var _timelineIds:Dictionary<String, Bool> = new Dictionary<String, Bool>();
+
+	public var duration:Float = 0;
+
+	public function new(name:String, timelines:Vector<Timeline>, duration:Float) {
+		if (name == null)
+			throw new ArgumentError("name cannot be null.");
+		if (timelines == null)
+			throw new ArgumentError("timelines cannot be null.");
+		_name = name;
+		_timelines = timelines;
+		for (timeline in timelines) {
+			var ids:Vector<String> = timeline.propertyIds;
+			for (id in ids) {
+				_timelineIds[id] = true;
+			}
+		}
+		this.duration = duration;
+	}
+
+	public function hasTimeline(ids:Vector<String>):Bool {
+		for (id in ids) {
+			if (_timelineIds[id])
+				return true;
+		}
+		return false;
+	}
+
+	/** Poses the skeleton at the specified time for this animation. */
+	public function apply(skeleton:Skeleton, lastTime:Float, time:Float, loop:Bool, events:Vector<Event>, alpha:Float, blend:MixBlend,
+			direction:MixDirection):Void {
+		if (skeleton == null)
+			throw new ArgumentError("skeleton cannot be null.");
+
+		if (loop && duration != 0) {
+			time %= duration;
+			if (lastTime > 0)
+				lastTime %= duration;
+		}
+
+		for (timeline in timelines) {
+			timeline.apply(skeleton, lastTime, time, events, alpha, blend, direction);
+		}
+	}
+
+	public var name(get, never):String;
+
+	private function get_name():String {
+		return _name;
+	}
+
+	public function toString():String {
+		return _name;
+	}
+
+	public var timelines(get, never):Vector<Timeline>;
+
+	private function get_timelines():Vector<Timeline> {
+		return _timelines;
+	}
+}

+ 686 - 0
spine-haxe/spine-haxe/spine/animation/AnimationState.hx

@@ -0,0 +1,686 @@
+package spine.animation;
+
+import starling.utils.Max;
+import openfl.errors.ArgumentError;
+import openfl.utils.Dictionary;
+import openfl.Vector;
+import spine.animation.Listeners.EventListeners;
+import spine.Event;
+import spine.Pool;
+import spine.Skeleton;
+
+class AnimationState {
+	public static inline var SUBSEQUENT:Int = 0;
+	public static inline var FIRST:Int = 1;
+	public static inline var HOLD_SUBSEQUENT:Int = 2;
+	public static inline var HOLD_FIRST:Int = 3;
+	public static inline var HOLD_MIX:Int = 4;
+	public static inline var SETUP:Int = 1;
+	public static inline var CURRENT:Int = 2;
+
+	private static var emptyAnimation:Animation = new Animation("<empty>", new Vector<Timeline>(), 0);
+
+	public var data:AnimationStateData;
+	public var tracks:Vector<TrackEntry> = new Vector<TrackEntry>();
+
+	private var events:Vector<Event> = new Vector<Event>();
+
+	public var onStart:Listeners = new Listeners();
+	public var onInterrupt:Listeners = new Listeners();
+	public var onEnd:Listeners = new Listeners();
+	public var onDispose:Listeners = new Listeners();
+	public var onComplete:Listeners = new Listeners();
+	public var onEvent:EventListeners = new EventListeners();
+
+	private var queue:EventQueue;
+	private var propertyIDs:StringSet = new StringSet();
+
+	public var animationsChanged:Bool = false;
+	public var timeScale:Float = 1;
+	public var trackEntryPool:Pool<TrackEntry>;
+
+	private var unkeyedState:Int = 0;
+
+	public function new(data:AnimationStateData) {
+		if (data == null)
+			throw new ArgumentError("data can not be null");
+		this.data = data;
+		this.queue = new EventQueue(this);
+		this.trackEntryPool = new Pool(function():Dynamic {
+			return new TrackEntry();
+		});
+	}
+
+	public function update(delta:Float):Void {
+		delta *= timeScale;
+		for (i in 0...tracks.length) {
+			var current:TrackEntry = tracks[i];
+			if (current == null)
+				continue;
+
+			current.animationLast = current.nextAnimationLast;
+			current.trackLast = current.nextTrackLast;
+
+			var currentDelta:Float = delta * current.timeScale;
+
+			if (current.delay > 0) {
+				current.delay -= currentDelta;
+				if (current.delay > 0)
+					continue;
+				currentDelta = -current.delay;
+				current.delay = 0;
+			}
+
+			var next:TrackEntry = current.next;
+			if (next != null) {
+				// When the next entry's delay is passed, change to the next entry, preserving leftover time.
+				var nextTime:Float = current.trackLast - next.delay;
+				if (nextTime >= 0) {
+					next.delay = 0;
+					next.trackTime = current.timeScale == 0 ? 0 : (nextTime / current.timeScale + delta) * next.timeScale;
+					current.trackTime += currentDelta;
+					setCurrent(i, next, true);
+					while (next.mixingFrom != null) {
+						next.mixTime += currentDelta;
+						next = next.mixingFrom;
+					}
+					continue;
+				}
+			} else if (current.trackLast >= current.trackEnd && current.mixingFrom == null) {
+				// Clear the track when there is no next entry, the track end time is reached, and there is no mixingFrom.
+				tracks[i] = null;
+				queue.end(current);
+				clearNext(current);
+				continue;
+			}
+
+			if (current.mixingFrom != null && updateMixingFrom(current, delta)) {
+				// End mixing from entries once all have completed.
+				var from:TrackEntry = current.mixingFrom;
+				current.mixingFrom = null;
+				if (from != null)
+					from.mixingTo = null;
+				while (from != null) {
+					queue.end(from);
+					from = from.mixingFrom;
+				}
+			}
+
+			current.trackTime += currentDelta;
+		}
+
+		queue.drain();
+	}
+
+	private function updateMixingFrom(to:TrackEntry, delta:Float):Bool {
+		var from:TrackEntry = to.mixingFrom;
+		if (from == null)
+			return true;
+
+		var finished:Bool = updateMixingFrom(from, delta);
+
+		from.animationLast = from.nextAnimationLast;
+		from.trackLast = from.nextTrackLast;
+
+		// Require mixTime > 0 to ensure the mixing from entry was applied at least once.
+		if (to.mixTime > 0 && to.mixTime >= to.mixDuration) {
+			// Require totalAlpha == 0 to ensure mixing is complete, unless mixDuration == 0 (the transition is a single frame).
+			if (from.totalAlpha == 0 || to.mixDuration == 0) {
+				to.mixingFrom = from.mixingFrom;
+				if (from.mixingFrom != null)
+					from.mixingFrom.mixingTo = to;
+				to.interruptAlpha = from.interruptAlpha;
+				queue.end(from);
+			}
+			return finished;
+		}
+
+		from.trackTime += delta * from.timeScale;
+		to.mixTime += delta;
+		return false;
+	}
+
+	public function apply(skeleton:Skeleton):Bool {
+		if (skeleton == null)
+			throw new ArgumentError("skeleton cannot be null.");
+		if (animationsChanged)
+			_animationsChanged();
+		var applied:Bool = false;
+
+		for (i in 0...tracks.length) {
+			var current:TrackEntry = tracks[i];
+			if (current == null || current.delay > 0)
+				continue;
+			applied = true;
+			var blend:MixBlend = i == 0 ? MixBlend.first : current.mixBlend;
+
+			// Apply mixing from entries first.
+			var mix:Float = current.alpha;
+			if (current.mixingFrom != null) {
+				mix *= applyMixingFrom(current, skeleton, blend);
+			} else if (current.trackTime >= current.trackEnd && current.next == null) {
+				mix = 0;
+			}
+
+			// Apply current entry.
+			var animationLast:Float = current.animationLast,
+				animationTime:Float = current.getAnimationTime(),
+				applyTime:Float = animationTime;
+			var applyEvents:Vector<Event> = events;
+			if (current.reverse) {
+				applyTime = current.animation.duration - applyTime;
+				applyEvents = null;
+			}
+			var timelines:Vector<Timeline> = current.animation.timelines;
+			var timelineCount:Int = timelines.length;
+			var timeline:Timeline;
+			if ((i == 0 && mix == 1) || blend == MixBlend.add) {
+				for (timeline in timelines) {
+					timeline.apply(skeleton, animationLast, applyTime, applyEvents, mix, blend, MixDirection.mixIn);
+				}
+			} else {
+				var timelineMode:Vector<Int> = current.timelineMode;
+
+				var firstFrame:Bool = current.timelinesRotation.length == 0;
+				if (firstFrame)
+					current.timelinesRotation.length = timelineCount << 1;
+
+				for (ii in 0...timelineCount) {
+					var timeline:Timeline = timelines[ii];
+					var timelineBlend:MixBlend = timelineMode[ii] == SUBSEQUENT ? blend : MixBlend.setup;
+					timeline.apply(skeleton, animationLast, applyTime, applyEvents, mix, timelineBlend, MixDirection.mixIn);
+				}
+			}
+			queueEvents(current, animationTime);
+			events.length = 0;
+			current.nextAnimationLast = animationTime;
+			current.nextTrackLast = current.trackTime;
+		}
+
+		// Set slots attachments to the setup pose, if needed. This occurs if an animation that is mixing out sets attachments so
+		// subsequent timelines see any deform, but the subsequent timelines don't set an attachment (eg they are also mixing out or
+		// the time is before the first key).
+		var setupState:Int = unkeyedState + SETUP;
+		for (slot in skeleton.slots) {
+			if (slot.attachmentState == setupState) {
+				var attachmentName:String = slot.data.attachmentName;
+				slot.attachment = attachmentName == null ? null : skeleton.getAttachmentForSlotIndex(slot.data.index, attachmentName);
+			}
+		}
+		unkeyedState += 2; // Increasing after each use avoids the need to reset attachmentState for every slot.
+
+		queue.drain();
+		return applied;
+	}
+
+	private function applyMixingFrom(to:TrackEntry, skeleton:Skeleton, blend:MixBlend):Float {
+		var from:TrackEntry = to.mixingFrom;
+		if (from.mixingFrom != null)
+			applyMixingFrom(from, skeleton, blend);
+
+		var mix:Float = 0;
+		if (to.mixDuration == 0) // Single frame mix to undo mixingFrom changes.
+		{
+			mix = 1;
+			if (blend == MixBlend.first)
+				blend = MixBlend.setup;
+		} else {
+			mix = to.mixTime / to.mixDuration;
+			if (mix > 1)
+				mix = 1;
+			if (blend != MixBlend.first)
+				blend = from.mixBlend;
+		}
+
+		var attachments:Bool = mix < from.attachmentThreshold,
+			drawOrder:Bool = mix < from.drawOrderThreshold;
+		var timelineCount:Int = from.animation.timelines.length;
+		var timelines:Vector<Timeline> = from.animation.timelines;
+		var alphaHold:Float = from.alpha * to.interruptAlpha,
+			alphaMix:Float = alphaHold * (1 - mix);
+		var animationLast:Float = from.animationLast,
+			animationTime:Float = from.getAnimationTime(),
+			applyTime:Float = animationTime;
+		var applyEvents:Vector<Event> = null;
+		if (from.reverse) {
+			applyTime = from.animation.duration - applyTime;
+		} else if (mix < from.eventThreshold) {
+			applyEvents = events;
+		}
+
+		if (blend == MixBlend.add) {
+			for (timeline in timelines) {
+				timeline.apply(skeleton, animationLast, applyTime, applyEvents, alphaMix, blend, MixDirection.mixOut);
+			}
+		} else {
+			var timelineMode:Vector<Int> = from.timelineMode;
+			var timelineHoldMix:Vector<TrackEntry> = from.timelineHoldMix;
+
+			var firstFrame:Bool = from.timelinesRotation.length != timelineCount << 1;
+			if (firstFrame)
+				from.timelinesRotation.length = timelineCount << 1;
+			var timelinesRotation:Vector<Float> = from.timelinesRotation;
+
+			from.totalAlpha = 0;
+			for (i in 0...timelineCount) {
+				var timeline:Timeline = timelines[i];
+				var direction:MixDirection = MixDirection.mixOut;
+				var timelineBlend:MixBlend;
+				var alpha:Float = 0;
+				switch (timelineMode[i]) {
+					case SUBSEQUENT:
+						if (!drawOrder && Std.isOfType(timeline, DrawOrderTimeline))
+							continue;
+						timelineBlend = blend;
+						alpha = alphaMix;
+					case FIRST:
+						timelineBlend = MixBlend.setup;
+						alpha = alphaMix;
+					case HOLD_SUBSEQUENT:
+						timelineBlend = blend;
+						alpha = alphaHold;
+					case HOLD_FIRST:
+						timelineBlend = MixBlend.setup;
+						alpha = alphaHold;
+					default:
+						timelineBlend = MixBlend.setup;
+						var holdMix:TrackEntry = timelineHoldMix[i];
+						alpha = alphaHold * Math.max(0, 1 - holdMix.mixTime / holdMix.mixDuration);
+				}
+
+				from.totalAlpha += alpha;
+
+				if (drawOrder && Std.isOfType(timeline, DrawOrderTimeline) && timelineBlend == MixBlend.setup)
+					direction = MixDirection.mixIn;
+				timeline.apply(skeleton, animationLast, applyTime, applyEvents, alpha, timelineBlend, direction);
+			}
+		}
+
+		if (to.mixDuration > 0)
+			queueEvents(from, animationTime);
+		events.length = 0;
+		from.nextAnimationLast = animationTime;
+		from.nextTrackLast = from.trackTime;
+
+		return mix;
+	}
+
+	private function setAttachment(skeleton:Skeleton, slot:Slot, attachmentName:String, attachments:Bool):Void {
+		slot.attachment = attachmentName == null ? null : skeleton.getAttachmentForSlotIndex(slot.data.index, attachmentName);
+		if (attachments)
+			slot.attachmentState = unkeyedState + CURRENT;
+	}
+
+	private function queueEvents(entry:TrackEntry, animationTime:Float):Void {
+		var animationStart:Float = entry.animationStart,
+			animationEnd:Float = entry.animationEnd;
+		var duration:Float = animationEnd - animationStart;
+		var trackLastWrapped:Float = entry.trackLast % duration;
+
+		// Queue events before complete.
+		var event:Event;
+		var i:Int = 0;
+		var n:Int = events.length;
+		while (i < n) {
+			event = events[i++];
+			if (event == null)
+				continue;
+			if (event.time < trackLastWrapped)
+				break;
+			if (event.time > animationEnd)
+				continue; // Discard events outside animation start/end.
+			queue.event(entry, event);
+		}
+
+		// Queue complete if completed a loop iteration or the animation.
+		var complete:Bool;
+		if (entry.loop) {
+			complete = duration == 0 || trackLastWrapped > entry.trackTime % duration;
+		} else {
+			complete = animationTime >= animationEnd && entry.animationLast < animationEnd;
+		}
+		if (complete)
+			queue.complete(entry);
+
+		// Queue events after complete.
+		while (i < n) {
+			event = events[i++];
+			if (event == null)
+				continue;
+			if (event.time < animationStart)
+				continue; // Discard events outside animation start/end.
+			queue.event(entry, event);
+		}
+	}
+
+	public function clearTracks():Void {
+		var oldTrainDisabled:Bool = queue.drainDisabled;
+		queue.drainDisabled = true;
+		for (i in 0...tracks.length) {
+			clearTrack(i);
+		}
+		tracks.length = 0;
+		queue.drainDisabled = oldTrainDisabled;
+		queue.drain();
+	}
+
+	public function clearTrack(trackIndex:Int):Void {
+		if (trackIndex >= tracks.length)
+			return;
+		var current:TrackEntry = tracks[trackIndex];
+		if (current == null)
+			return;
+
+		queue.end(current);
+		clearNext(current);
+
+		var entry:TrackEntry = current;
+		while (true) {
+			var from:TrackEntry = entry.mixingFrom;
+			if (from == null)
+				break;
+			queue.end(from);
+			entry.mixingFrom = null;
+			entry.mixingTo = null;
+			entry = from;
+		}
+
+		tracks[current.trackIndex] = null;
+
+		queue.drain();
+	}
+
+	private function setCurrent(index:Int, current:TrackEntry, interrupt:Bool):Void {
+		var from:TrackEntry = expandToIndex(index);
+		tracks[index] = current;
+
+		if (from != null) {
+			if (interrupt)
+				queue.interrupt(from);
+			current.mixingFrom = from;
+			from.mixingTo = current;
+			current.mixTime = 0;
+
+			// Store the interrupted mix percentage.
+			if (from.mixingFrom != null && from.mixDuration > 0) {
+				current.interruptAlpha *= Math.min(1, from.mixTime / from.mixDuration);
+			}
+
+			from.timelinesRotation.length = 0; // Reset rotation for mixing out, in case entry was mixed in.
+		}
+
+		queue.start(current);
+	}
+
+	public function setAnimationByName(trackIndex:Int, animationName:String, loop:Bool):TrackEntry {
+		var animation:Animation = data.skeletonData.findAnimation(animationName);
+		if (animation == null)
+			throw new ArgumentError("Animation not found: " + animationName);
+		return setAnimation(trackIndex, animation, loop);
+	}
+
+	public function setAnimation(trackIndex:Int, animation:Animation, loop:Bool):TrackEntry {
+		if (animation == null)
+			throw new ArgumentError("animation cannot be null.");
+		var interrupt:Bool = true;
+		var current:TrackEntry = expandToIndex(trackIndex);
+		if (current != null) {
+			if (current.nextTrackLast == -1) {
+				// Don't mix from an entry that was never applied.
+				tracks[trackIndex] = current.mixingFrom;
+				queue.interrupt(current);
+				queue.end(current);
+				clearNext(current);
+				current = current.mixingFrom;
+				interrupt = false;
+			} else {
+				clearNext(current);
+			}
+		}
+		var entry:TrackEntry = trackEntry(trackIndex, animation, loop, current);
+		setCurrent(trackIndex, entry, interrupt);
+		queue.drain();
+		return entry;
+	}
+
+	public function addAnimationByName(trackIndex:Int, animationName:String, loop:Bool, delay:Float):TrackEntry {
+		var animation:Animation = data.skeletonData.findAnimation(animationName);
+		if (animation == null)
+			throw new ArgumentError("Animation not found: " + animationName);
+		return addAnimation(trackIndex, animation, loop, delay);
+	}
+
+	public function addAnimation(trackIndex:Int, animation:Animation, loop:Bool, delay:Float):TrackEntry {
+		if (animation == null)
+			throw new ArgumentError("animation cannot be null.");
+
+		var last:TrackEntry = expandToIndex(trackIndex);
+		if (last != null) {
+			while (last.next != null) {
+				last = last.next;
+			}
+		}
+
+		var entry:TrackEntry = trackEntry(trackIndex, animation, loop, last);
+
+		if (last == null) {
+			setCurrent(trackIndex, entry, true);
+			queue.drain();
+		} else {
+			last.next = entry;
+			entry.previous = last;
+			if (delay <= 0)
+				delay += last.getTrackComplete() - entry.mixDuration;
+		}
+
+		entry.delay = delay;
+		return entry;
+	}
+
+	public function setEmptyAnimation(trackIndex:Int, mixDuration:Float):TrackEntry {
+		var entry:TrackEntry = setAnimation(trackIndex, emptyAnimation, false);
+		entry.mixDuration = mixDuration;
+		entry.trackEnd = mixDuration;
+		return entry;
+	}
+
+	public function addEmptyAnimation(trackIndex:Int, mixDuration:Float, delay:Float):TrackEntry {
+		var entry:TrackEntry = addAnimation(trackIndex, emptyAnimation, false, delay);
+		if (delay <= 0)
+			entry.delay += entry.mixDuration - mixDuration;
+		entry.mixDuration = mixDuration;
+		entry.trackEnd = mixDuration;
+		return entry;
+	}
+
+	public function setEmptyAnimations(mixDuration:Float):Void {
+		var oldDrainDisabled:Bool = queue.drainDisabled;
+		queue.drainDisabled = true;
+		for (i in 0...tracks.length) {
+			var current:TrackEntry = tracks[i];
+			if (current != null)
+				setEmptyAnimation(current.trackIndex, mixDuration);
+		}
+		queue.drainDisabled = oldDrainDisabled;
+		queue.drain();
+	}
+
+	private function expandToIndex(index:Int):TrackEntry {
+		if (index < tracks.length)
+			return tracks[index];
+		tracks.length = index + 1;
+		return null;
+	}
+
+	private function trackEntry(trackIndex:Int, animation:Animation, loop:Bool, last:TrackEntry):TrackEntry {
+		var entry:TrackEntry = cast(trackEntryPool.obtain(), TrackEntry);
+		entry.trackIndex = trackIndex;
+		entry.animation = animation;
+		entry.loop = loop;
+		entry.holdPrevious = false;
+
+		entry.eventThreshold = 0;
+		entry.attachmentThreshold = 0;
+		entry.drawOrderThreshold = 0;
+
+		entry.animationStart = 0;
+		entry.animationEnd = animation.duration;
+		entry.animationLast = -1;
+		entry.nextAnimationLast = -1;
+
+		entry.delay = 0;
+		entry.trackTime = 0;
+		entry.trackLast = -1;
+		entry.nextTrackLast = -1;
+		entry.trackEnd = Max.INT_MAX_VALUE;
+		entry.timeScale = 1;
+
+		entry.alpha = 1;
+		entry.interruptAlpha = 1;
+		entry.mixTime = 0;
+		entry.mixDuration = last == null ? 0 : data.getMix(last.animation, animation);
+		entry.mixBlend = MixBlend.replace;
+		return entry;
+	}
+
+	/** Removes the {@link TrackEntry#getNext() next entry} and all entries after it for the specified entry. */
+	public function clearNext(entry:TrackEntry):Void {
+		var next:TrackEntry = entry.next;
+		while (next != null) {
+			queue.dispose(next);
+			next = next.next;
+		}
+		entry.next = null;
+	}
+
+	private function _animationsChanged():Void {
+		animationsChanged = false;
+
+		propertyIDs.clear();
+		var entry:TrackEntry = null;
+		for (i in 0...tracks.length) {
+			entry = tracks[i];
+			if (entry == null)
+				continue;
+			while (entry.mixingFrom != null) {
+				entry = entry.mixingFrom;
+			}
+			do {
+				if (entry.mixingTo == null || entry.mixBlend != MixBlend.add)
+					computeHold(entry);
+				entry = entry.mixingTo;
+			} while (entry != null);
+		}
+	}
+
+	private function computeHold(entry:TrackEntry):Void {
+		var to:TrackEntry = entry.mixingTo;
+		var timelines:Vector<Timeline> = entry.animation.timelines;
+		var timelinesCount:Int = entry.animation.timelines.length;
+		var timelineMode:Vector<Int> = entry.timelineMode;
+		timelineMode.length = timelinesCount;
+		entry.timelineHoldMix.length = 0;
+		var timelineHoldMix:Vector<TrackEntry> = entry.timelineHoldMix;
+		timelineHoldMix.length = timelinesCount;
+
+		if (to != null && to.holdPrevious) {
+			for (i in 0...timelinesCount) {
+				timelineMode[i] = propertyIDs.addAll(timelines[i].propertyIds) ? HOLD_FIRST : HOLD_SUBSEQUENT;
+			}
+			return;
+		}
+
+		var continueOuter:Bool;
+		for (i in 0...timelinesCount) {
+			continueOuter = false;
+			var timeline:Timeline = timelines[i];
+			var ids:Vector<String> = timeline.propertyIds;
+			if (!propertyIDs.addAll(ids)) {
+				timelineMode[i] = SUBSEQUENT;
+			} else if (to == null
+				|| Std.isOfType(timeline, AttachmentTimeline)
+				|| Std.isOfType(timeline, DrawOrderTimeline)
+				|| Std.isOfType(timeline, EventTimeline)
+				|| !to.animation.hasTimeline(ids)) {
+				timelineMode[i] = FIRST;
+			} else {
+				var next:TrackEntry = to.mixingTo;
+				while (next != null) {
+					if (next.animation.hasTimeline(ids)) {
+						next = next.mixingTo;
+						continue;
+					}
+					if (entry.mixDuration > 0) {
+						timelineMode[i] = HOLD_MIX;
+						timelineHoldMix[i] = next;
+						continueOuter = true;
+						break;
+					}
+					break;
+				}
+				if (continueOuter)
+					continue;
+				timelineMode[i] = HOLD_FIRST;
+			}
+		}
+	}
+
+	public function getCurrent(trackIndex:Int):TrackEntry {
+		if (trackIndex >= tracks.length)
+			return null;
+		return tracks[trackIndex];
+	}
+
+	public var fHasEndListener(get, never):Bool;
+
+	private function get_fHasEndListener():Bool {
+		return onComplete.listeners.length > 0 || onEnd.listeners.length > 0;
+	}
+
+	public function clearListeners():Void {
+		onStart.listeners.length = 0;
+		onInterrupt.listeners.length = 0;
+		onEnd.listeners.length = 0;
+		onDispose.listeners.length = 0;
+		onComplete.listeners.length = 0;
+		onEvent.listeners.length = 0;
+	}
+
+	public function clearListenerNotifications():Void {
+		queue.clear();
+	}
+}
+
+class StringSet {
+	private var entries:Dictionary<String, Bool> = new Dictionary<String, Bool>();
+	private var size:Int = 0;
+
+	public function new() {}
+
+	public function add(value:String):Bool {
+		var contains:Bool = entries[value];
+		entries[value] = true;
+		if (!contains) {
+			size++;
+			return true;
+		}
+		return false;
+	}
+
+	public function addAll(values:Vector<String>):Bool {
+		var oldSize:Int = size;
+		for (i in 0...values.length) {
+			add(values[i]);
+		}
+		return oldSize != size;
+	}
+
+	public function contains(value:String):Bool {
+		return entries[value];
+	}
+
+	public function clear():Void {
+		entries = new Dictionary<String, Bool>();
+		size = 0;
+	}
+}

+ 47 - 0
spine-haxe/spine-haxe/spine/animation/AnimationStateData.hx

@@ -0,0 +1,47 @@
+package spine.animation;
+
+import openfl.errors.ArgumentError;
+import openfl.utils.Object;
+import spine.SkeletonData;
+
+class AnimationStateData {
+	private var _skeletonData:SkeletonData;
+	private var animationToMixTime:Object = new Object();
+
+	public var defaultMix:Float = 0;
+
+	public function new(skeletonData:SkeletonData) {
+		_skeletonData = skeletonData;
+	}
+
+	public var skeletonData(get, never):SkeletonData;
+
+	private function get_skeletonData():SkeletonData {
+		return _skeletonData;
+	}
+
+	public function setMixByName(fromName:String, toName:String, duration:Float):Void {
+		var from:Animation = _skeletonData.findAnimation(fromName);
+		if (from == null)
+			throw new ArgumentError("Animation not found: " + fromName);
+		var to:Animation = _skeletonData.findAnimation(toName);
+		if (to == null)
+			throw new ArgumentError("Animation not found: " + toName);
+		setMix(from, to, duration);
+	}
+
+	public function setMix(from:Animation, to:Animation, duration:Float):Void {
+		if (from == null)
+			throw new ArgumentError("from cannot be null.");
+		if (to == null)
+			throw new ArgumentError("to cannot be null.");
+		animationToMixTime[from.name + ":" + to.name] = duration;
+	}
+
+	public function getMix(from:Animation, to:Animation):Float {
+		var time:Object = animationToMixTime[from.name + ":" + to.name];
+		if (time == null)
+			return defaultMix;
+		return cast(time, Float);
+	}
+}

+ 60 - 0
spine-haxe/spine-haxe/spine/animation/AttachmentTimeline.hx

@@ -0,0 +1,60 @@
+package spine.animation;
+
+import openfl.Vector;
+import spine.Event;
+import spine.Skeleton;
+import spine.Slot;
+
+class AttachmentTimeline extends Timeline implements SlotTimeline {
+	public var slotIndex:Int = 0;
+
+	/** The attachment name for each key frame. May contain null values to clear the attachment. */
+	public var attachmentNames:Vector<String>;
+
+	public function new(frameCount:Int, slotIndex:Int) {
+		super(frameCount, Vector.ofArray([Property.attachment + "|" + slotIndex]));
+		this.slotIndex = slotIndex;
+		attachmentNames = new Vector<String>(frameCount, true);
+	}
+
+	public override function getFrameCount():Int {
+		return frames.length;
+	}
+
+	public function getSlotIndex():Int {
+		return slotIndex;
+	}
+
+	/** Sets the time in seconds and the attachment name for the specified key frame. */
+	public function setFrame(frame:Int, time:Float, attachmentName:String):Void {
+		frames[frame] = time;
+		attachmentNames[frame] = attachmentName;
+	}
+
+	public override function apply(skeleton:Skeleton, lastTime:Float, time:Float, events:Vector<Event>, alpha:Float, blend:MixBlend,
+			direction:MixDirection):Void {
+		var slot:Slot = skeleton.slots[slotIndex];
+		if (!slot.bone.active)
+			return;
+
+		if (direction == MixDirection.mixOut) {
+			if (blend == MixBlend.setup) {
+				setAttachment(skeleton, slot, slot.data.attachmentName);
+			}
+			return;
+		}
+
+		if (time < frames[0]) {
+			if (blend == MixBlend.setup || blend == MixBlend.first) {
+				setAttachment(skeleton, slot, slot.data.attachmentName);
+			}
+			return;
+		}
+
+		setAttachment(skeleton, slot, attachmentNames[Timeline.search1(frames, time)]);
+	}
+
+	private function setAttachment(skeleton:Skeleton, slot:Slot, attachmentName:String):Void {
+		slot.attachment = attachmentName == null ? null : skeleton.getAttachmentForSlotIndex(slotIndex, attachmentName);
+	}
+}

+ 5 - 0
spine-haxe/spine-haxe/spine/animation/BoneTimeline.hx

@@ -0,0 +1,5 @@
+package spine.animation;
+
+interface BoneTimeline {
+	function getBoneIndex():Int;
+}

+ 104 - 0
spine-haxe/spine-haxe/spine/animation/CurveTimeline.hx

@@ -0,0 +1,104 @@
+package spine.animation;
+
+import openfl.Vector;
+
+/** Base class for frames that use an interpolation bezier curve. */
+class CurveTimeline extends Timeline {
+	private static inline var LINEAR:Int = 0;
+	private static inline var STEPPED:Int = 1;
+	private static inline var BEZIER:Int = 2;
+	private static inline var BEZIER_SIZE:Int = 18;
+
+	private var curves:Vector<Float>; // type, x, y, ...
+
+	public function new(frameCount:Int, bezierCount:Int, propertyIds:Vector<String>) {
+		super(frameCount, propertyIds);
+		curves = new Vector<Float>(frameCount + bezierCount * BEZIER_SIZE, true);
+		curves[frameCount - 1] = STEPPED;
+	}
+
+	public function setLinear(frame:Int):Void {
+		curves[frame] = LINEAR;
+	}
+
+	public function setStepped(frame:Int):Void {
+		curves[frame] = STEPPED;
+	}
+
+	/** Shrinks the storage for Bezier curves, for use when <code>bezierCount</code> (specified in the constructor) was larger
+	 * than the actual number of Bezier curves. */
+	public function shrink(bezierCount:Int):Void {
+		var size:Int = getFrameCount() + bezierCount * BEZIER_SIZE;
+		curves.length = size;
+	}
+
+	/** Stores the segments for the specified Bezier curve. For timelines that modify multiple values, there may be more than
+	 * one curve per frame.
+	 * @param bezier The ordinal of this Bezier curve for this timeline, between 0 and <code>bezierCount - 1</code> (specified
+	 *           in the constructor), inclusive.
+	 * @param frame Between 0 and <code>frameCount - 1</code>, inclusive.
+	 * @param value The index of the value for this frame that this curve is used for.
+	 * @param time1 The time for the first key.
+	 * @param value1 The value for the first key.
+	 * @param cx1 The time for the first Bezier handle.
+	 * @param cy1 The value for the first Bezier handle.
+	 * @param cx2 The time of the second Bezier handle.
+	 * @param cy2 The value for the second Bezier handle.
+	 * @param time2 The time for the second key.
+	 * @param value2 The value for the second key. */
+	public function setBezier(bezier:Int, frame:Int, value:Float, time1:Float, value1:Float, cx1:Float, cy1:Float, cx2:Float, cy2:Float, time2:Float,
+			value2:Float):Void {
+		var i:Int = getFrameCount() + bezier * BEZIER_SIZE;
+		if (value == 0)
+			curves[frame] = BEZIER + i;
+		var tmpx:Float = (time1 - cx1 * 2 + cx2) * 0.03,
+			tmpy:Float = (value1 - cy1 * 2 + cy2) * 0.03;
+		var dddx:Float = ((cx1 - cx2) * 3 - time1 + time2) * 0.006,
+			dddy:Float = ((cy1 - cy2) * 3 - value1 + value2) * 0.006;
+		var ddx:Float = tmpx * 2 + dddx, ddy:Float = tmpy * 2 + dddy;
+		var dx:Float = (cx1 - time1) * 0.3 + tmpx + dddx * 0.16666667,
+			dy:Float = (cy1 - value1) * 0.3 + tmpy + dddy * 0.16666667;
+		var x:Float = time1 + dx, y:Float = value1 + dy;
+		var n:Int = i + BEZIER_SIZE;
+		while (i < n) {
+			curves[i] = x;
+			curves[i + 1] = y;
+			dx += ddx;
+			dy += ddy;
+			ddx += dddx;
+			ddy += dddy;
+			x += dx;
+			y += dy;
+
+			i += 2;
+		}
+	}
+
+	/** Returns the Bezier interpolated value for the specified time.
+	 * @param frameIndex The index into {@link #getFrames()} for the values of the frame before <code>time</code>.
+	 * @param valueOffset The offset from <code>frameIndex</code> to the value this curve is used for.
+	 * @param i The index of the Bezier segments. See {@link #getCurveType(int)}. */
+	public function getBezierValue(time:Float, frameIndex:Int, valueOffset:Int, i:Int):Float {
+		var x:Float, y:Float;
+		if (curves[i] > time) {
+			x = frames[frameIndex];
+			y = frames[frameIndex + valueOffset];
+			return y + (time - x) / (curves[i] - x) * (curves[i + 1] - y);
+		}
+		var n:Int = i + BEZIER_SIZE;
+		i += 2;
+		while (i < n) {
+			if (curves[i] >= time) {
+				x = curves[i - 2];
+				y = curves[i - 1];
+				return y + (time - x) / (curves[i] - x) * (curves[i + 1] - y);
+			}
+
+			i += 2;
+		}
+		frameIndex += getFrameEntries();
+		x = curves[n - 2];
+		y = curves[n - 1];
+		return y + (time - x) / (frames[frameIndex] - x) * (frames[frameIndex + valueOffset] - y);
+	}
+}

+ 51 - 0
spine-haxe/spine-haxe/spine/animation/CurveTimeline1.hx

@@ -0,0 +1,51 @@
+package spine.animation;
+
+/** The base class for a {@link CurveTimeline} that sets one property. */
+import openfl.Vector;
+
+class CurveTimeline1 extends CurveTimeline {
+	private static inline var ENTRIES:Int = 2;
+	private static inline var VALUE:Int = 1;
+
+	/** @param bezierCount The maximum number of Bezier curves. See {@link #shrink(Int)}.
+	 * @param propertyIds Unique identifiers for the properties the timeline modifies. */
+	public function new(frameCount:Int, bezierCount:Int, propertyIds:Vector<String>) {
+		super(frameCount, bezierCount, propertyIds);
+	}
+
+	public override function getFrameEntries():Int {
+		return ENTRIES;
+	}
+
+	/** Sets the time and values for the specified frame.
+	 * @param frame Between 0 and <code>frameCount</code>, inclusive.
+	 * @param time The frame time in seconds. */
+	public function setFrame(frame:Int, time:Float, value1:Float):Void {
+		frame <<= 1;
+		frames[frame] = time;
+		frames[frame + VALUE] = value1;
+	}
+
+	/** Returns the interpolated value for the specified time. */
+	public function getCurveValue(time:Float):Float {
+		var i:Int = frames.length - 2;
+		var ii:Int = 2;
+		while (ii <= i) {
+			if (frames[ii] > time) {
+				i = ii - 2;
+				break;
+			}
+			ii += 2;
+		}
+
+		var curveType:Int = Std.int(curves[i >> 1]);
+		switch (curveType) {
+			case CurveTimeline.LINEAR:
+				var before:Float = frames[i], value:Float = frames[i + VALUE];
+				return value + (time - before) / (frames[i + ENTRIES] - before) * (frames[i + ENTRIES + VALUE] - value);
+			case CurveTimeline.STEPPED:
+				return frames[i + VALUE];
+		}
+		return getBezierValue(time, i, VALUE, curveType - CurveTimeline.BEZIER);
+	}
+}

+ 30 - 0
spine-haxe/spine-haxe/spine/animation/CurveTimeline2.hx

@@ -0,0 +1,30 @@
+package spine.animation;
+
+/** The base class for a {@link CurveTimeline} which sets two properties. */
+import openfl.Vector;
+
+class CurveTimeline2 extends CurveTimeline {
+	private static inline var ENTRIES:Int = 3;
+	private static inline var VALUE1:Int = 1;
+	private static inline var VALUE2:Int = 2;
+
+	/** @param bezierCount The maximum number of Bezier curves. See {@link #shrink(Int)}.
+	 * @param propertyIds Unique identifiers for the properties the timeline modifies. */
+	public function new(frameCount:Int, bezierCount:Int, propertyIds:Vector<String>) {
+		super(frameCount, bezierCount, propertyIds);
+	}
+
+	public override function getFrameEntries():Int {
+		return ENTRIES;
+	}
+
+	/** Sets the time and values for the specified frame.
+	 * @param frame Between 0 and <code>frameCount</code>, inclusive.
+	 * @param time The frame time in seconds. */
+	public function setFrame(frame:Int, time:Float, value1:Float, value2:Float):Void {
+		frame *= ENTRIES;
+		frames[frame] = time;
+		frames[frame + VALUE1] = value1;
+		frames[frame + VALUE2] = value2;
+	}
+}

+ 279 - 0
spine-haxe/spine-haxe/spine/animation/DeformTimeline.hx

@@ -0,0 +1,279 @@
+package spine.animation;
+
+import openfl.Vector;
+import spine.animation.Timeline;
+import spine.attachments.Attachment;
+import spine.attachments.VertexAttachment;
+import spine.Event;
+import spine.Skeleton;
+import spine.Slot;
+
+class DeformTimeline extends CurveTimeline implements SlotTimeline {
+	public var slotIndex:Int = 0;
+
+	/** The attachment that will be deformed. */
+	public var attachment:VertexAttachment;
+
+	/** The vertices for each key frame. */
+	public var vertices:Vector<Vector<Float>>;
+
+	public function new(frameCount:Int, bezierCount:Int, slotIndex:Int, attachment:VertexAttachment) {
+		super(frameCount, bezierCount, Vector.ofArray([Property.deform + "|" + slotIndex + "|" + attachment.id]));
+		this.slotIndex = slotIndex;
+		this.attachment = attachment;
+		vertices = new Vector<Vector<Float>>(frameCount, true);
+	}
+
+	public override function getFrameCount():Int {
+		return frames.length;
+	}
+
+	public function getSlotIndex():Int {
+		return slotIndex;
+	}
+
+	/** Sets the time in seconds and the vertices for the specified key frame.
+	 * @param vertices Vertex positions for an unweighted VertexAttachment, or deform offsets if it has weights. */
+	public function setFrame(frame:Int, time:Float, verticesOrDeform:Vector<Float>):Void {
+		frames[frame] = time;
+		vertices[frame] = verticesOrDeform;
+	}
+
+	/** @param value1 Ignored (0 is used for a deform timeline).
+	 * @param value2 Ignored (1 is used for a deform timeline). */
+	public override function setBezier(bezier:Int, frame:Int, value:Float, time1:Float, value1:Float, cx1:Float, cy1:Float, cx2:Float, cy2:Float, time2:Float,
+			value2:Float):Void {
+		var i:Int = getFrameCount() + bezier * CurveTimeline.BEZIER_SIZE;
+		if (value == 0)
+			curves[frame] = CurveTimeline.BEZIER + i;
+		var tmpx:Float = (time1 - cx1 * 2 + cx2) * 0.03,
+			tmpy:Float = cy2 * 0.03 - cy1 * 0.06;
+		var dddx:Float = ((cx1 - cx2) * 3 - time1 + time2) * 0.006,
+			dddy:Float = (cy1 - cy2 + 0.33333333) * 0.018;
+		var ddx:Float = tmpx * 2 + dddx, ddy:Float = tmpy * 2 + dddy;
+		var dx:Float = (cx1 - time1) * 0.3 + tmpx + dddx * 0.16666667,
+			dy:Float = cy1 * 0.3 + tmpy + dddy * 0.16666667;
+		var x:Float = time1 + dx, y:Float = dy;
+		var n:Int = i + CurveTimeline.BEZIER_SIZE;
+		while (i < n) {
+			curves[i] = x;
+			curves[i + 1] = y;
+			dx += ddx;
+			dy += ddy;
+			ddx += dddx;
+			ddy += dddy;
+			x += dx;
+			y += dy;
+
+			i += 2;
+		}
+	}
+
+	private function getCurvePercent(time:Float, frame:Int):Float {
+		var i:Int = Std.int(curves[frame]);
+		var x:Float;
+		switch (i) {
+			case CurveTimeline.LINEAR:
+				x = frames[frame];
+				return (time - x) / (frames[frame + getFrameEntries()] - x);
+			case CurveTimeline.STEPPED:
+				return 0;
+		}
+		i -= CurveTimeline.BEZIER;
+		if (curves[i] > time) {
+			x = frames[frame];
+			return curves[i + 1] * (time - x) / (curves[i] - x);
+		}
+		var n:Int = i + CurveTimeline.BEZIER_SIZE, y:Float;
+		i += 2;
+		while (i < n) {
+			if (curves[i] >= time) {
+				x = curves[i - 2];
+				y = curves[i - 1];
+				return y + (time - x) / (curves[i] - x) * (curves[i + 1] - y);
+			}
+
+			i += 2;
+		}
+		x = curves[n - 2];
+		y = curves[n - 1];
+		return y + (1 - y) * (time - x) / (frames[frame + getFrameEntries()] - x);
+	}
+
+	public override function apply(skeleton:Skeleton, lastTime:Float, time:Float, events:Vector<Event>, alpha:Float, blend:MixBlend,
+			direction:MixDirection):Void {
+		var slot:Slot = skeleton.slots[slotIndex];
+		if (!slot.bone.active)
+			return;
+		var slotAttachment:Attachment = slot.attachment;
+
+		if (!Std.isOfType(slotAttachment, VertexAttachment) || cast(slotAttachment, VertexAttachment).deformAttachment != attachment)
+			return;
+		var vertexAttachment:VertexAttachment = cast(slotAttachment, VertexAttachment);
+
+		var deform:Vector<Float> = slot.deform;
+		if (deform.length == 0)
+			blend = MixBlend.setup;
+
+		var vertexCount:Int = vertices[0].length;
+		var i:Int, setupVertices:Vector<Float>;
+
+		if (time < frames[0]) {
+			switch (blend) {
+				case MixBlend.setup:
+					deform.length = 0;
+				case MixBlend.first:
+					if (alpha == 1) {
+						deform.length = 0;
+						return;
+					}
+					deform.length = vertexCount;
+					if (vertexAttachment.bones == null) {
+						// Unweighted vertex positions.
+						setupVertices = vertexAttachment.vertices;
+						for (i in 0...vertexCount) {
+							deform[i] += (setupVertices[i] - deform[i]) * alpha;
+						}
+					} else {
+						// Weighted deform offsets.
+						alpha = 1 - alpha;
+						for (i in 0...vertexCount) {
+							deform[i] *= alpha;
+						}
+					}
+			}
+			return;
+		}
+
+		deform.length = vertexCount;
+		var setup:Float;
+		if (time >= frames[frames.length - 1]) // Time is after last frame.
+		{
+			var lastVertices:Vector<Float> = vertices[frames.length - 1];
+			if (alpha == 1) {
+				if (blend == MixBlend.add) {
+					if (vertexAttachment.bones == null) {
+						// Unweighted vertex positions, with alpha.
+						setupVertices = vertexAttachment.vertices;
+						for (i in 0...vertexCount) {
+							deform[i] += lastVertices[i] - setupVertices[i];
+						}
+					} else {
+						// Weighted deform offsets, with alpha.
+						for (i in 0...vertexCount) {
+							deform[i] += lastVertices[i];
+						}
+					}
+				} else {
+					for (i in 0...vertexCount) {
+						deform[i] = lastVertices[i];
+					}
+				}
+			} else {
+				switch (blend) {
+					case MixBlend.setup:
+						if (vertexAttachment.bones == null) {
+							// Unweighted vertex positions, with alpha.
+							setupVertices = vertexAttachment.vertices;
+							for (i in 0...vertexCount) {
+								setup = setupVertices[i];
+								deform[i] = setup + (lastVertices[i] - setup) * alpha;
+							}
+						} else {
+							// Weighted deform offsets, with alpha.
+							for (i in 0...vertexCount) {
+								deform[i] = lastVertices[i] * alpha;
+							}
+						}
+					case MixBlend.first, MixBlend.replace:
+						for (i in 0...vertexCount) {
+							deform[i] += (lastVertices[i] - deform[i]) * alpha;
+						}
+					case MixBlend.add:
+						if (vertexAttachment.bones == null) {
+							// Unweighted vertex positions, with alpha.
+							setupVertices = vertexAttachment.vertices;
+							for (i in 0...vertexCount) {
+								deform[i] += (lastVertices[i] - setupVertices[i]) * alpha;
+							}
+						} else {
+							// Weighted deform offsets, with alpha.
+							for (i in 0...vertexCount) {
+								deform[i] += lastVertices[i] * alpha;
+							}
+						}
+				}
+			}
+			return;
+		}
+
+		// Interpolate between the previous frame and the current frame.
+		var frame:Int = Timeline.search1(frames, time);
+		var percent:Float = getCurvePercent(time, frame);
+		var prevVertices:Vector<Float> = vertices[frame], prev:Float;
+		var nextVertices:Vector<Float> = vertices[frame + 1];
+
+		if (alpha == 1) {
+			if (blend == MixBlend.add) {
+				if (vertexAttachment.bones == null) {
+					// Unweighted vertex positions, with alpha.
+					setupVertices = vertexAttachment.vertices;
+					for (i in 0...vertexCount) {
+						prev = prevVertices[i];
+						deform[i] += prev + (nextVertices[i] - prev) * percent - setupVertices[i];
+					}
+				} else {
+					// Weighted deform offsets, with alpha.
+					for (i in 0...vertexCount) {
+						prev = prevVertices[i];
+						deform[i] += prev + (nextVertices[i] - prev) * percent;
+					}
+				}
+			} else {
+				for (i in 0...vertexCount) {
+					prev = prevVertices[i];
+					deform[i] = prev + (nextVertices[i] - prev) * percent;
+				}
+			}
+		} else {
+			switch (blend) {
+				case MixBlend.setup:
+					if (vertexAttachment.bones == null) {
+						// Unweighted vertex positions, with alpha.
+						setupVertices = vertexAttachment.vertices;
+						for (i in 0...vertexCount) {
+							prev = prevVertices[i];
+							setup = setupVertices[i];
+							deform[i] = setup + (prev + (nextVertices[i] - prev) * percent - setup) * alpha;
+						}
+					} else {
+						// Weighted deform offsets, with alpha.
+						for (i in 0...vertexCount) {
+							prev = prevVertices[i];
+							deform[i] = (prev + (nextVertices[i] - prev) * percent) * alpha;
+						}
+					}
+				case MixBlend.first, MixBlend.replace:
+					for (i in 0...vertexCount) {
+						prev = prevVertices[i];
+						deform[i] += (prev + (nextVertices[i] - prev) * percent - deform[i]) * alpha;
+					}
+				case MixBlend.add:
+					if (vertexAttachment.bones == null) {
+						// Unweighted vertex positions, with alpha.
+						setupVertices = vertexAttachment.vertices;
+						for (i in 0...vertexCount) {
+							prev = prevVertices[i];
+							deform[i] += (prev + (nextVertices[i] - prev) * percent - setupVertices[i]) * alpha;
+						}
+					} else {
+						// Weighted deform offsets, with alpha.
+						for (i in 0...vertexCount) {
+							prev = prevVertices[i];
+							deform[i] += (prev + (nextVertices[i] - prev) * percent) * alpha;
+						}
+					}
+			}
+		}
+	}
+}

+ 63 - 0
spine-haxe/spine-haxe/spine/animation/DrawOrderTimeline.hx

@@ -0,0 +1,63 @@
+package spine.animation;
+
+import openfl.Vector;
+import spine.Event;
+import spine.Skeleton;
+import spine.Slot;
+
+class DrawOrderTimeline extends Timeline {
+	public var drawOrders:Vector<Vector<Int>>;
+
+	public function new(frameCount:Int) {
+		super(frameCount, Vector.ofArray([Std.string(Property.drawOrder)]));
+		drawOrders = new Vector<Vector<Int>>(frameCount, true);
+	}
+
+	public var frameCount(get, never):Int;
+
+	private function get_frameCount():Int {
+		return frames.length;
+	}
+
+	/** Sets the time and value of the specified keyframe. */
+	public function setFrame(frame:Int, time:Float, drawOrder:Vector<Int>):Void {
+		frames[frame] = time;
+		drawOrders[frame] = drawOrder;
+	}
+
+	override public function apply(skeleton:Skeleton, lastTime:Float, time:Float, events:Vector<Event>, alpha:Float, blend:MixBlend,
+			direction:MixDirection):Void {
+		var drawOrder:Vector<Slot> = skeleton.drawOrder;
+		var slots:Vector<Slot> = skeleton.slots;
+		var i:Int = 0, n:Int = slots.length;
+
+		if (direction == MixDirection.mixOut) {
+			if (blend == MixBlend.setup) {
+				for (i in 0...n) {
+					drawOrder[i] = slots[i];
+				}
+			}
+			return;
+		}
+
+		if (time < frames[0]) {
+			if (blend == MixBlend.setup || blend == MixBlend.first) {
+				for (i in 0...n) {
+					drawOrder[i] = slots[i];
+				}
+			}
+			return;
+		}
+
+		var drawOrderToSetupIndex:Vector<Int> = drawOrders[Timeline.search1(frames, time)];
+		if (drawOrderToSetupIndex == null) {
+			for (i in 0...n) {
+				drawOrder[i] = slots[i];
+			}
+		} else {
+			for (i in 0...n) {
+				drawOrder[i] = slots[drawOrderToSetupIndex[i]];
+			}
+		}
+	}
+}

+ 93 - 0
spine-haxe/spine-haxe/spine/animation/EventQueue.hx

@@ -0,0 +1,93 @@
+package spine.animation;
+
+import spine.Event;
+
+class EventQueue {
+	private var objects:Array<Dynamic>;
+	private var animationState:AnimationState;
+
+	public var drainDisabled:Bool = false;
+
+	public function new(animationState:AnimationState) {
+		this.animationState = animationState;
+		objects = new Array<Dynamic>();
+	}
+
+	public function start(entry:TrackEntry):Void {
+		objects.push(EventType.start);
+		objects.push(entry);
+		animationState.animationsChanged = true;
+	}
+
+	public function interrupt(entry:TrackEntry):Void {
+		objects.push(EventType.interrupt);
+		objects.push(entry);
+	}
+
+	public function end(entry:TrackEntry):Void {
+		objects.push(EventType.end);
+		objects.push(entry);
+		animationState.animationsChanged = true;
+	}
+
+	public function dispose(entry:TrackEntry):Void {
+		objects.push(EventType.dispose);
+		objects.push(entry);
+	}
+
+	public function complete(entry:TrackEntry):Void {
+		objects.push(EventType.complete);
+		objects.push(entry);
+	}
+
+	public function event(entry:TrackEntry, event:Event):Void {
+		objects.push(EventType.event);
+		objects.push(entry);
+		objects.push(event);
+	}
+
+	public function drain():Void {
+		if (drainDisabled)
+			return; // Not reentrant.
+		drainDisabled = true;
+
+		var i:Int = 0;
+		while (i < objects.length) {
+			var type:EventType = cast(objects[i], EventType);
+			var entry:TrackEntry = cast(objects[i + 1], TrackEntry);
+			switch (type) {
+				case EventType.start:
+					entry.onStart.invoke(entry);
+					animationState.onStart.invoke(entry);
+				case EventType.interrupt:
+					entry.onInterrupt.invoke(entry);
+					animationState.onInterrupt.invoke(entry);
+				case EventType.end:
+					entry.onEnd.invoke(entry);
+					animationState.onEnd.invoke(entry);
+					entry.onDispose.invoke(entry);
+					animationState.onDispose.invoke(entry);
+					animationState.trackEntryPool.free(entry);
+				case EventType.dispose:
+					entry.onDispose.invoke(entry);
+					animationState.onDispose.invoke(entry);
+					animationState.trackEntryPool.free(entry);
+				case EventType.complete:
+					entry.onComplete.invoke(entry);
+					animationState.onComplete.invoke(entry);
+				case EventType.event:
+					var event:Event = cast(objects[i++ + 2], Event);
+					entry.onEvent.invoke(entry, event);
+					animationState.onEvent.invoke(entry, event);
+			}
+			i += 2;
+		}
+		clear();
+
+		drainDisabled = false;
+	}
+
+	public function clear():Void {
+		objects.resize(0);
+	}
+}

Algunos archivos no se mostraron porque demasiados archivos cambiaron en este cambio