diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml
new file mode 100644
index 0000000..461b1c7
--- /dev/null
+++ b/.github/FUNDING.yml
@@ -0,0 +1 @@
+github: deepnight
\ No newline at end of file
diff --git a/.github/workflows/test.js.yml b/.github/workflows/test.js.yml
new file mode 100644
index 0000000..6b4a910
--- /dev/null
+++ b/.github/workflows/test.js.yml
@@ -0,0 +1,35 @@
+name: Test JS build
+
+on:
+ push:
+ branches:
+ - '**'
+ pull_request:
+ branches:
+ - '**'
+
+jobs:
+ build:
+
+ strategy:
+ matrix:
+ os: [windows-latest]
+ haxe: [4.3.3]
+ fail-fast: true
+ runs-on: windows-latest
+
+ steps:
+ # Checkout & install haxe
+ - uses: actions/checkout@v2
+ - uses: krdlab/setup-haxe@v1
+ with:
+ haxe-version: ${{ matrix.haxe }}
+ - run: haxe -version
+
+ # Install libs
+ - run: haxe setup.hxml
+ - run: haxelib list
+
+ # Try to build
+ - run: haxe build.js.hxml
+
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..498a43f
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,8 @@
+redist/
+bin/
+dump/
+res/.tmp/
+res/**/backups/
+
+# VScode
+.vscode/settings.json
diff --git a/.vscode/commandbar.json b/.vscode/commandbar.json
new file mode 100644
index 0000000..2c47129
--- /dev/null
+++ b/.vscode/commandbar.json
@@ -0,0 +1,66 @@
+{
+ "skipTerminateQuickPick": true,
+ "skipSwitchToOutput": false,
+ "skipErrorMessage": true,
+ "commands": [
+ {
+ "text": "🍊 DX",
+ "color": "orange",
+ "commandType":"exec",
+ "command": "haxe build.directx.hxml",
+ "alignment": "right",
+ "skipTerminateQuickPick": false,
+ "priority": -9
+ },
+ {
+ "text": "🍊 OpenGL",
+ "color": "orange",
+ "commandType":"exec",
+ "command": "haxe build.opengl.hxml",
+ "alignment": "right",
+ "skipTerminateQuickPick": false,
+ "priority": -10
+ },
+ {
+ "text": "Run HL",
+ "color": "orange",
+ "command": "hl bin/client.hl",
+ "alignment": "right",
+ "skipTerminateQuickPick": false,
+ "priority": -11
+ },
+ {
+ "text": "☕ JS",
+ "color": "yellow",
+ "commandType":"exec",
+ "command": "haxe build.js.hxml",
+ "alignment": "right",
+ "skipTerminateQuickPick": false,
+ "priority": -20
+ },
+ {
+ "text": "Run JS",
+ "color": "yellow",
+ "command": "start run_js.html",
+ "alignment": "right",
+ "skipTerminateQuickPick": false,
+ "priority": -21
+ },
+ {
+ "text": "🅰️ Lang",
+ "color": "white",
+ "command": "haxe tools.langParser.hxml",
+ "alignment": "right",
+ "skipTerminateQuickPick": false,
+ "priority": -40
+ },
+ {
+ "text": "📦 Redist",
+ "color": "lightgreen",
+ "command": "haxelib run redistHelper build.directx.hxml build.opengl.hxml build.js.hxml -o redist -p GameBase -zip",
+ "alignment": "right",
+ "skipTerminateQuickPick": false,
+ "priority": -50
+ }
+ ]
+}
\ No newline at end of file
diff --git a/.vscode/launch.json b/.vscode/launch.json
new file mode 100644
index 0000000..fb709b4
--- /dev/null
+++ b/.vscode/launch.json
@@ -0,0 +1,22 @@
+{
+ // 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": "HL debug",
+ "request": "launch",
+ "type": "hl",
+ "cwd": "${workspaceRoot}",
+ "preLaunchTask": "HaxeActiveConf"
+ },
+ {
+ "name": "Chrome JS debug",
+ "request": "launch",
+ "type": "chrome",
+ "file": "${workspaceFolder}/run_js.html",
+ "preLaunchTask": "HaxeActiveConf"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/.vscode/tasks.json b/.vscode/tasks.json
new file mode 100644
index 0000000..d50c549
--- /dev/null
+++ b/.vscode/tasks.json
@@ -0,0 +1,25 @@
+{
+ // See https://go.microsoft.com/fwlink/?LinkId=733558
+ // for the documentation about the tasks.json format
+ "version": "2.0.0",
+ "tasks": [
+ {
+ "label": "HaxeActiveConf",
+ "type": "haxe",
+ "args": "active configuration",
+ "problemMatcher": [
+ "$haxe-absolute",
+ "$haxe",
+ "$haxe-error",
+ "$haxe-trace"
+ ],
+ "presentation": {
+ "reveal": "never",
+ },
+ "group": {
+ "kind": "build",
+ "isDefault": true
+ },
+ },
+ ]
+}
diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
index 0000000..ed93786
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,44 @@
+# GameBase changelog
+
+## 2.0
+
+- General:
+ - **The official gameBase version is now the former "advanced" branch version. The previous, more minimalistic version, is still available in the `legacy` branch.**
+ - **Debug Drone**: press `CTRL-SHIFT-D` to spawn a debug Drone. Use arrows to fly around and quickly explore your current level. You can also type `/drone` in console.
+ - Full **Controller** rework, to provide much better Gamepad support, bindings, combos, etc.
+ - Full **LDtk** integration (https://ldtk.io), with hot-reloading support.
+ - Added many comments everywhere.
+ - Moved all source code to various `src/` subfolders
+ - Moved all assets related classes to the `assets.*` package
+ - Added various debug commands to console. Open it up by typing `/`. Enter `/help` to list all available commands.
+ - Added `/fps` command to console to display FPS chart over time.
+ - Added `/ctrl` command to visualize and debug controller (keyboard or gamepad).
+ - Fixed various FPS values issues
+ - Better "active" level management, through `startLevel()` method
+ - Cleaned up Main class
+ - Renamed Main class to App
+ - Renamed Data class to CastleDb
+ - Replaced pixel perfect filters with a more optimized one (now using `Nothing` filter)
+ - Added many comments and docs everywhere
+ - Added XML doc generation for future proper doc
+ - Removed SWF target (see you space cowboy)
+ - Added this CHANGELOG ;)
+
+- Entity:
+ - All entities now have a proper width/height and a pivotX/Y factor
+ - Separated bump X/Y frictions
+ - Fixed X/Y squash frictions (forgot to use tmod)
+ - Added a `sightCheck` methods using Bresenham algorithm
+ - Added `isAlive()` (ie. quick check to both `destroyed` flag and `life>0` check)
+ - Added `.exists()` to Game and Main
+
+- Camera:
+ - Cleanup & rework
+ - Added zoom support
+ - Camera slows down when reaching levels bounds
+ - Camera no longer clamps to level bounds by default
+ - Added isOnScreen(x,y)
+
+- UI:
+ - Added basic notifications to HUD
+ - Added debug text field to HUD
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..292f4a5
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2019 Sébastien Bénard
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..0f132a5
--- /dev/null
+++ b/README.md
@@ -0,0 +1,79 @@
+# About
+
+**A lightweight and simple base structure for games, using _[Heaps](https://heaps.io)_ framework and _[Haxe](https://haxe.org)_ language.**
+
+Latest release notes: [View changelog](CHANGELOG.md).
+
+[![GitHub Workflow Status](https://img.shields.io/github/actions/workflow/status/deepnight/gameBase/test.js.yml)](https://github.com/deepnight/gameBase/actions/workflows/test.js.yml)
+[![GitHub Repo stars](https://img.shields.io/github/stars/deepnight/gameBase?label=%E2%98%85)](https://github.com/deepnight/gameBase)
+
+# Install
+
+## Legacy or master?
+
+Two separate branches exist for GameBase:
+
+- `master`: latest GameBase version, actively maintained.
+- `legacy`: the previous Gamebase version. This one is much more minimalistic but it could be useful if you were looking for a very basic framework for Heaps+Haxe.
+
+The following document will only refer to the `master` branch.
+
+## Getting master
+
+1. Install **Haxe** and **Hashlink**: [Step-by-step tutorial](https://deepnight.net/tutorial/a-quick-guide-to-installing-haxe/)
+2. Install required libs by running the following command **in the root of the repo**: `haxe setup.hxml`
+
+# Compile
+
+From the command line, run either:
+
+- For **DirectX**: `haxe build.directx.hxml`
+- For **OpenGL**: `haxe build.opengl.hxml`
+- For **Javascript/WebGL**: `haxe build.js.hxml`
+
+The `build.dev.hxml` is just a shortcut to one of the previous ones, with added `-debug` flag.
+
+Run the result with either:
+
+- For **DirectX/OpenGL**: `hl bin\client.hl`
+- For **Javascript**: `start run_js.html`
+
+# Full guide
+
+An in-depth tutorial is available here: [Using gamebase to create a game](https://deepnight.net/tutorial/using-my-gamebase-to-create-a-heaps-game/). Please note that this tutorial still refers to the `legacy` branch, even though the general idea is the same in `master` branch.
+
+## Sample examples
+
+The samples are the recommended places to start for the latest `GameBase` version (`main`).
+
+They should give a pretty hands-on understanding of how entities work and how to integrate `ldtk` to development.
+
+`SamplePlayer.hx`[SamplePlayer.hx]
+
+SamplePlayer is an Entity with some extra functionalities:
+
+- user controlled (using gamepad or keyboard)
+- falls with gravity
+- has basic level collisions
+- some squash animations, because it's cheap and they do the job
+
+`SampleWorld.hx`
+
+A small class that just creates a SamplePlayer instance in the sample level.
+
+## Localization
+
+For **localization support** (ie. translating your game texts), you may also check the [following guide](https://deepnight.net/tutorial/part-4-localize-texts-using-po-files/).
+
+## Questions
+
+Any question? Join the [Official Deepnight Games discord](https://deepnight.net/go/discord).
+
+# Cleanup for your own usage
+
+You can safely remove the following files/folders from repo root:
+
+- `.github/`
+- `LICENSE`
+- `README.md`
+- `CHANGELOG.md`
diff --git a/_base.hxml b/_base.hxml
new file mode 100644
index 0000000..bad6f1e
--- /dev/null
+++ b/_base.hxml
@@ -0,0 +1,10 @@
+-cp src/game
+
+-lib castle
+-lib heaps
+-lib hscript
+-lib deepnightLibs
+-lib ldtk-haxe-api
+-lib heaps-aseprite
+
+-main Boot
diff --git a/build.dev.hxml b/build.dev.hxml
new file mode 100644
index 0000000..a2334db
--- /dev/null
+++ b/build.dev.hxml
@@ -0,0 +1,2 @@
+build.directx.hxml
+-debug
\ No newline at end of file
diff --git a/build.directx.hxml b/build.directx.hxml
new file mode 100644
index 0000000..d08302a
--- /dev/null
+++ b/build.directx.hxml
@@ -0,0 +1,4 @@
+_base.hxml
+-D windowSize=1280x720
+-hl bin/client.hl
+-lib hldx
\ No newline at end of file
diff --git a/build.js.hxml b/build.js.hxml
new file mode 100644
index 0000000..8e692e1
--- /dev/null
+++ b/build.js.hxml
@@ -0,0 +1,3 @@
+_base.hxml
+-dce std
+-js bin/client.js
diff --git a/build.opengl.hxml b/build.opengl.hxml
new file mode 100644
index 0000000..3fed5f4
--- /dev/null
+++ b/build.opengl.hxml
@@ -0,0 +1,4 @@
+_base.hxml
+-D windowSize=1280x720
+-hl bin/client.hl
+-lib hlsdl
\ No newline at end of file
diff --git a/res/atlas/tiles.aseprite b/res/atlas/tiles.aseprite
new file mode 100644
index 0000000..3a43216
Binary files /dev/null and b/res/atlas/tiles.aseprite differ
diff --git a/res/const.json b/res/const.json
new file mode 100644
index 0000000..eabffd0
--- /dev/null
+++ b/res/const.json
@@ -0,0 +1,3 @@
+{
+ "myJsonConstant": 2
+}
\ No newline at end of file
diff --git a/res/data.cdb b/res/data.cdb
new file mode 100644
index 0000000..ca1baca
--- /dev/null
+++ b/res/data.cdb
@@ -0,0 +1,121 @@
+{
+ "sheets": [
+ {
+ "name": "ConstDb",
+ "columns": [
+ {
+ "typeStr": "0",
+ "name": "constId"
+ },
+ {
+ "typeStr": "8",
+ "name": "values"
+ }
+ ],
+ "lines": [
+ {
+ "constId": "player",
+ "values": [
+ {
+ "valueName": "valueA",
+ "value": 1,
+ "doc": "This documentation will show up when using your editor auto-completion for this value",
+ "isInteger": true
+ },
+ {
+ "valueName": "valueB",
+ "value": 2.5,
+ "doc": "Doc for this other value",
+ "isInteger": false
+ },
+ {
+ "valueName": "withSubValues",
+ "value": 0,
+ "isInteger": false,
+ "subValues": {
+ "x": 1.1,
+ "y": 1.2,
+ "n": 3
+ },
+ "doc": "You may also define some \"sub values\". Accepted sub value types are: Int, Float, Bool, Text and Color."
+ }
+ ]
+ }
+ ],
+ "props": {
+ "separatorTitles": [
+ "This sheet allows to access CastleDB \"constants\" in your code. Just call for example \"Const.db.Player.valueA\" to access float values. These values will be updated on runtime if hot-reloading is enabled. DO NOT MODIFY THE SHEET NAME OR COLUMNS!"
+ ]
+ },
+ "separatorIds": [
+ "player"
+ ]
+ },
+ {
+ "name": "ConstDb@values",
+ "props": {
+ "hide": true
+ },
+ "separators": [],
+ "lines": [],
+ "columns": [
+ {
+ "typeStr": "1",
+ "name": "valueName",
+ "display": null
+ },
+ {
+ "typeStr": "1",
+ "name": "doc",
+ "opt": true,
+ "display": null
+ },
+ {
+ "typeStr": "4",
+ "name": "value",
+ "display": null
+ },
+ {
+ "typeStr": "2",
+ "name": "isInteger"
+ },
+ {
+ "typeStr": "17",
+ "name": "subValues",
+ "opt": true,
+ "display": null
+ }
+ ]
+ },
+ {
+ "name": "ConstDb@values@subValues",
+ "props": {
+ "hide": true,
+ "isProps": true
+ },
+ "separators": [],
+ "lines": [],
+ "columns": [
+ {
+ "typeStr": "4",
+ "name": "x",
+ "opt": true
+ },
+ {
+ "typeStr": "4",
+ "name": "y",
+ "opt": true,
+ "display": null
+ },
+ {
+ "typeStr": "3",
+ "name": "n",
+ "opt": true,
+ "display": null
+ }
+ ]
+ }
+ ],
+ "customTypes": [],
+ "compress": false
+}
\ No newline at end of file
diff --git a/res/fonts/pixel_unicode_regular_12.png b/res/fonts/pixel_unicode_regular_12.png
new file mode 100644
index 0000000..82a1651
Binary files /dev/null and b/res/fonts/pixel_unicode_regular_12.png differ
diff --git a/res/fonts/pixel_unicode_regular_12.xml b/res/fonts/pixel_unicode_regular_12.xml
new file mode 100644
index 0000000..46431b2
--- /dev/null
+++ b/res/fonts/pixel_unicode_regular_12.xml
@@ -0,0 +1,251 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/res/fonts/pixica_mono_regular_16.png b/res/fonts/pixica_mono_regular_16.png
new file mode 100644
index 0000000..96f472d
Binary files /dev/null and b/res/fonts/pixica_mono_regular_16.png differ
diff --git a/res/fonts/pixica_mono_regular_16.xml b/res/fonts/pixica_mono_regular_16.xml
new file mode 100644
index 0000000..d95aa9c
--- /dev/null
+++ b/res/fonts/pixica_mono_regular_16.xml
@@ -0,0 +1,339 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/res/lang/en.po b/res/lang/en.po
new file mode 100644
index 0000000..56a4fe7
--- /dev/null
+++ b/res/lang/en.po
@@ -0,0 +1,18 @@
+msgid ""
+msgstr ""
+"Project-Id-Version: \n"
+"POT-Creation-Date: \n"
+"PO-Revision-Date: 2021-10-01 21:26+0200\n"
+"Last-Translator: \n"
+"Language-Team: \n"
+"Language: en\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"X-Generator: Poedit 3.0\n"
+"X-Poedit-Basepath: .\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+
+#: "src/game/gm/Game.hx"
+msgid "Press ESCAPE again to exit."
+msgstr ""
diff --git a/res/lang/sourceTexts.pot b/res/lang/sourceTexts.pot
new file mode 100644
index 0000000..fb0369c
--- /dev/null
+++ b/res/lang/sourceTexts.pot
@@ -0,0 +1,10 @@
+msgid ""
+msgstr ""
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"MIME-Version: 1.0\n"
+
+
+#: "src/game/Game.hx"
+msgid "Press ESCAPE again to exit."
+msgstr ""
diff --git a/res/levels/sampleWorld.ldtk b/res/levels/sampleWorld.ldtk
new file mode 100644
index 0000000..235ce11
--- /dev/null
+++ b/res/levels/sampleWorld.ldtk
@@ -0,0 +1,2640 @@
+{
+ "__header__": {
+ "fileType": "LDtk Project JSON",
+ "app": "LDtk",
+ "doc": "https://ldtk.io/json",
+ "schema": "https://ldtk.io/files/JSON_SCHEMA.json",
+ "appAuthor": "Sebastien 'deepnight' Benard",
+ "appVersion": "1.5.0",
+ "url": "https://ldtk.io"
+ },
+ "iid": "de07ff90-7820-11ed-ac6b-bbc68d89d940",
+ "jsonVersion": "1.5.0",
+ "appBuildId": 473560,
+ "nextUid": 25,
+ "identifierStyle": "Capitalize",
+ "toc": [],
+ "worldLayout": null,
+ "worldGridWidth": null,
+ "worldGridHeight": null,
+ "defaultLevelWidth": null,
+ "defaultLevelHeight": null,
+ "defaultPivotX": 0.5,
+ "defaultPivotY": 1,
+ "defaultGridSize": 16,
+ "defaultEntityWidth": 16,
+ "defaultEntityHeight": 16,
+ "bgColor": "#4E586C",
+ "defaultLevelBgColor": "#1A212E",
+ "minifyJson": false,
+ "externalLevels": false,
+ "exportTiled": false,
+ "simplifiedExport": false,
+ "imageExportMode": "None",
+ "exportLevelBg": true,
+ "pngFilePattern": null,
+ "backupOnSave": false,
+ "backupLimit": 10,
+ "backupRelPath": null,
+ "levelNamePattern": "Level_%idx",
+ "tutorialDesc": null,
+ "customCommands": [],
+ "flags": [ "ExportOldTableOfContentData", "MultiWorlds" ],
+ "defs": { "layers": [
+ {
+ "__type": "Entities",
+ "identifier": "Entities",
+ "type": "Entities",
+ "uid": 22,
+ "doc": null,
+ "uiColor": "#0099DB",
+ "gridSize": 16,
+ "guideGridWid": 0,
+ "guideGridHei": 0,
+ "displayOpacity": 1,
+ "inactiveOpacity": 0.6,
+ "hideInList": false,
+ "hideFieldsWhenInactive": true,
+ "canSelectWhenInactive": true,
+ "renderInWorldView": true,
+ "pxOffsetX": 0,
+ "pxOffsetY": 0,
+ "parallaxFactorX": 0,
+ "parallaxFactorY": 0,
+ "parallaxScaling": true,
+ "requiredTags": [],
+ "excludedTags": [],
+ "autoTilesKilledByOtherLayerUid": null,
+ "uiFilterTags": [],
+ "intGridValues": [],
+ "intGridValuesGroups": [],
+ "autoRuleGroups": [],
+ "autoSourceLayerDefUid": null,
+ "tilesetDefUid": null,
+ "tilePivotX": 0,
+ "tilePivotY": 0,
+ "biomeFieldUid": null
+ },
+ {
+ "__type": "IntGrid",
+ "identifier": "Collisions",
+ "type": "IntGrid",
+ "uid": 1,
+ "doc": null,
+ "uiColor": "#E4A672",
+ "gridSize": 16,
+ "guideGridWid": 0,
+ "guideGridHei": 0,
+ "displayOpacity": 1,
+ "inactiveOpacity": 1,
+ "hideInList": false,
+ "hideFieldsWhenInactive": true,
+ "canSelectWhenInactive": true,
+ "renderInWorldView": true,
+ "pxOffsetX": 0,
+ "pxOffsetY": 0,
+ "parallaxFactorX": 0,
+ "parallaxFactorY": 0,
+ "parallaxScaling": true,
+ "requiredTags": [],
+ "excludedTags": [],
+ "autoTilesKilledByOtherLayerUid": null,
+ "uiFilterTags": [],
+ "intGridValues": [{ "value": 1, "identifier": "walls", "color": "#000000", "tile": { "tilesetUid": 4, "x": 16, "y": 0, "w": 16, "h": 16 }, "groupUid": 0 }],
+ "intGridValuesGroups": [],
+ "autoRuleGroups": [
+ {
+ "uid": 24,
+ "name": "Details",
+ "color": "#3E8948",
+ "icon": { "tilesetUid": 4, "x": 48, "y": 0, "w": 16, "h": 16 },
+ "active": true,
+ "isOptional": false,
+ "rules": [
+ {
+ "uid": 11,
+ "active": true,
+ "size": 3,
+ "tileRectsIds": [[3]],
+ "alpha": 1,
+ "chance": 1,
+ "breakOnMatch": false,
+ "pattern": [0,0,0,0,-1,0,0,1,0],
+ "flipX": false,
+ "flipY": false,
+ "xModulo": 1,
+ "yModulo": 1,
+ "xOffset": 0,
+ "yOffset": 0,
+ "tileXOffset": 0,
+ "tileYOffset": 0,
+ "tileRandomXMin": 0,
+ "tileRandomXMax": 0,
+ "tileRandomYMin": 0,
+ "tileRandomYMax": 0,
+ "checker": "None",
+ "tileMode": "Single",
+ "pivotX": 0,
+ "pivotY": 0,
+ "outOfBoundsValue": null,
+ "perlinActive": false,
+ "perlinSeed": 9994295,
+ "perlinScale": 0.2,
+ "perlinOctaves": 2
+ },
+ {
+ "uid": 15,
+ "active": true,
+ "size": 3,
+ "tileRectsIds": [[34]],
+ "alpha": 1,
+ "chance": 1,
+ "breakOnMatch": false,
+ "pattern": [0,1,0,0,-1,0,0,0,0],
+ "flipX": false,
+ "flipY": false,
+ "xModulo": 1,
+ "yModulo": 1,
+ "xOffset": 0,
+ "yOffset": 0,
+ "tileXOffset": 0,
+ "tileYOffset": 0,
+ "tileRandomXMin": 0,
+ "tileRandomXMax": 0,
+ "tileRandomYMin": 0,
+ "tileRandomYMax": 0,
+ "checker": "None",
+ "tileMode": "Single",
+ "pivotX": 0,
+ "pivotY": 0,
+ "outOfBoundsValue": null,
+ "perlinActive": false,
+ "perlinSeed": 6884511,
+ "perlinScale": 0.2,
+ "perlinOctaves": 2
+ }
+ ],
+ "usesWizard": false,
+ "requiredBiomeValues": [],
+ "biomeRequirementMode": 0
+ },
+ {
+ "uid": 5,
+ "name": "Walls",
+ "color": "#8B9BB4",
+ "icon": { "tilesetUid": 4, "x": 0, "y": 0, "w": 16, "h": 16 },
+ "active": true,
+ "isOptional": false,
+ "rules": [
+ {
+ "uid": 13,
+ "active": true,
+ "size": 3,
+ "tileRectsIds": [[32,64,33,65]],
+ "alpha": 1,
+ "chance": 0.5,
+ "breakOnMatch": false,
+ "pattern": [0,0,0,0,1,1,0,1,1],
+ "flipX": false,
+ "flipY": false,
+ "xModulo": 2,
+ "yModulo": 2,
+ "xOffset": 0,
+ "yOffset": 0,
+ "tileXOffset": 0,
+ "tileYOffset": 0,
+ "tileRandomXMin": 0,
+ "tileRandomXMax": 0,
+ "tileRandomYMin": 0,
+ "tileRandomYMax": 0,
+ "checker": "Vertical",
+ "tileMode": "Stamp",
+ "pivotX": 0,
+ "pivotY": 0,
+ "outOfBoundsValue": null,
+ "perlinActive": false,
+ "perlinSeed": 7268775,
+ "perlinScale": 0.2,
+ "perlinOctaves": 2
+ },
+ {
+ "uid": 12,
+ "active": true,
+ "size": 1,
+ "tileRectsIds": [[2]],
+ "alpha": 1,
+ "chance": 1,
+ "breakOnMatch": false,
+ "pattern": [1],
+ "flipX": false,
+ "flipY": false,
+ "xModulo": 2,
+ "yModulo": 1,
+ "xOffset": 0,
+ "yOffset": 0,
+ "tileXOffset": 0,
+ "tileYOffset": 0,
+ "tileRandomXMin": 0,
+ "tileRandomXMax": 0,
+ "tileRandomYMin": 0,
+ "tileRandomYMax": 0,
+ "checker": "Horizontal",
+ "tileMode": "Single",
+ "pivotX": 0,
+ "pivotY": 0,
+ "outOfBoundsValue": null,
+ "perlinActive": false,
+ "perlinSeed": 2505145,
+ "perlinScale": 0.2,
+ "perlinOctaves": 2
+ },
+ {
+ "uid": 6,
+ "active": true,
+ "size": 1,
+ "tileRectsIds": [ [0], [1] ],
+ "alpha": 1,
+ "chance": 1,
+ "breakOnMatch": false,
+ "pattern": [1],
+ "flipX": false,
+ "flipY": false,
+ "xModulo": 1,
+ "yModulo": 1,
+ "xOffset": 0,
+ "yOffset": 0,
+ "tileXOffset": 0,
+ "tileYOffset": 0,
+ "tileRandomXMin": 0,
+ "tileRandomXMax": 0,
+ "tileRandomYMin": 0,
+ "tileRandomYMax": 0,
+ "checker": "None",
+ "tileMode": "Single",
+ "pivotX": 0,
+ "pivotY": 0,
+ "outOfBoundsValue": null,
+ "perlinActive": false,
+ "perlinSeed": 3630262,
+ "perlinScale": 0.2,
+ "perlinOctaves": 2
+ }
+ ],
+ "usesWizard": false,
+ "requiredBiomeValues": [],
+ "biomeRequirementMode": 0
+ },
+ {
+ "uid": 16,
+ "name": "Background",
+ "color": "#68386C",
+ "icon": { "tilesetUid": 4, "x": 80, "y": 0, "w": 16, "h": 16 },
+ "active": true,
+ "isOptional": false,
+ "rules": [
+ {
+ "uid": 19,
+ "active": true,
+ "size": 3,
+ "tileRectsIds": [[5]],
+ "alpha": 1,
+ "chance": 1,
+ "breakOnMatch": false,
+ "pattern": [0,1,0,0,-1,0,0,0,0],
+ "flipX": false,
+ "flipY": false,
+ "xModulo": 1,
+ "yModulo": 1,
+ "xOffset": 0,
+ "yOffset": 0,
+ "tileXOffset": 0,
+ "tileYOffset": 0,
+ "tileRandomXMin": 0,
+ "tileRandomXMax": 0,
+ "tileRandomYMin": 0,
+ "tileRandomYMax": 0,
+ "checker": "None",
+ "tileMode": "Single",
+ "pivotX": 0,
+ "pivotY": 0,
+ "outOfBoundsValue": null,
+ "perlinActive": false,
+ "perlinSeed": 5066947,
+ "perlinScale": 0.2,
+ "perlinOctaves": 2
+ },
+ {
+ "uid": 18,
+ "active": true,
+ "size": 1,
+ "tileRectsIds": [[5]],
+ "alpha": 1,
+ "chance": 1,
+ "breakOnMatch": false,
+ "pattern": [-1],
+ "flipX": false,
+ "flipY": false,
+ "xModulo": 2,
+ "yModulo": 1,
+ "xOffset": 0,
+ "yOffset": 0,
+ "tileXOffset": 0,
+ "tileYOffset": 0,
+ "tileRandomXMin": 0,
+ "tileRandomXMax": 0,
+ "tileRandomYMin": 0,
+ "tileRandomYMax": 0,
+ "checker": "None",
+ "tileMode": "Single",
+ "pivotX": 0,
+ "pivotY": 0,
+ "outOfBoundsValue": null,
+ "perlinActive": false,
+ "perlinSeed": 4746799,
+ "perlinScale": 0.2,
+ "perlinOctaves": 2
+ },
+ {
+ "uid": 17,
+ "active": true,
+ "size": 1,
+ "tileRectsIds": [[4]],
+ "alpha": 1,
+ "chance": 1,
+ "breakOnMatch": false,
+ "pattern": [-1],
+ "flipX": false,
+ "flipY": false,
+ "xModulo": 1,
+ "yModulo": 1,
+ "xOffset": 0,
+ "yOffset": 0,
+ "tileXOffset": 0,
+ "tileYOffset": 0,
+ "tileRandomXMin": 0,
+ "tileRandomXMax": 0,
+ "tileRandomYMin": 0,
+ "tileRandomYMax": 0,
+ "checker": "None",
+ "tileMode": "Single",
+ "pivotX": 0,
+ "pivotY": 0,
+ "outOfBoundsValue": null,
+ "perlinActive": false,
+ "perlinSeed": 602091,
+ "perlinScale": 0.2,
+ "perlinOctaves": 2
+ }
+ ],
+ "usesWizard": false,
+ "requiredBiomeValues": [],
+ "biomeRequirementMode": 0
+ }
+ ],
+ "autoSourceLayerDefUid": null,
+ "tilesetDefUid": 4,
+ "tilePivotX": 0,
+ "tilePivotY": 0,
+ "biomeFieldUid": null
+ }
+ ], "entities": [
+ {
+ "identifier": "PlayerStart",
+ "uid": 21,
+ "tags": [],
+ "exportToToc": false,
+ "allowOutOfBounds": false,
+ "doc": null,
+ "width": 16,
+ "height": 16,
+ "resizableX": false,
+ "resizableY": false,
+ "minWidth": null,
+ "maxWidth": null,
+ "minHeight": null,
+ "maxHeight": null,
+ "keepAspectRatio": false,
+ "tileOpacity": 1,
+ "fillOpacity": 0,
+ "lineOpacity": 0,
+ "hollow": false,
+ "color": "#75FF00",
+ "renderMode": "Tile",
+ "showName": true,
+ "tilesetId": 23,
+ "tileRenderMode": "Stretch",
+ "tileRect": { "tilesetUid": 23, "x": 208, "y": 80, "w": 16, "h": 16 },
+ "uiTileRect": null,
+ "nineSliceBorders": [],
+ "maxCount": 1,
+ "limitScope": "PerWorld",
+ "limitBehavior": "MoveLastOne",
+ "pivotX": 0.5,
+ "pivotY": 1,
+ "fieldDefs": []
+ }
+ ], "tilesets": [
+ {
+ "__cWid": 32,
+ "__cHei": 32,
+ "identifier": "SampleTiles",
+ "uid": 4,
+ "relPath": "sampleWorldTiles.aseprite",
+ "embedAtlas": null,
+ "pxWid": 512,
+ "pxHei": 512,
+ "tileGridSize": 16,
+ "spacing": 0,
+ "padding": 0,
+ "tags": [],
+ "tagsSourceEnumUid": null,
+ "enumTags": [],
+ "customData": [],
+ "savedSelections": [],
+ "cachedPixelData": {
+ "opaqueTiles": "1110110000000000000000000000000011000000000000000000000000000000110000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
+ "averageColors": "f668f668f5572697f434f31300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f769f769154600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f769f7690000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
+ }
+ },
+ {
+ "__cWid": 32,
+ "__cHei": 64,
+ "identifier": "Internal_Icons",
+ "uid": 23,
+ "relPath": null,
+ "embedAtlas": "LdtkIcons",
+ "pxWid": 512,
+ "pxHei": 1024,
+ "tileGridSize": 16,
+ "spacing": 0,
+ "padding": 0,
+ "tags": [],
+ "tagsSourceEnumUid": null,
+ "enumTags": [],
+ "customData": [],
+ "savedSelections": [],
+ "cachedPixelData": {
+ "opaqueTiles": "00000000000000000000000000000000000000000000000000000000000000000000000000000000111111111000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
+ "averageColors": "00004b344233459b423349a959a9379c688769758ca4bc9489aab9aa58cc58bc42d74d2244ce428f4c7e4fb34abb45564ffe7dda78880000000000000000000069a969a97a99999999989a85998699767a7579667ccc7ccc7bcb7caa7ccc7ccc22d72d2224ce228f2c7e2fb32abb25562ffe000000000000000000000000000059764b97599868ac679a69ab4a84477756787688475347532a932a934a837a83f2b6fb22f3acf15afa6cfc93f899f334fccc000000000000000000000000000059aa49aa59996999699969aa489949995999799a499949992999299948997889a385a823a379a248a749a864a667a223a8880000000000000000000000000000189919991999199939994778166727772889289948993aaa389949a959a959a932b63b2233ad315a395c3c83389933343ccc00000000000000000000000000008aaa8aaa8aaa8aaa8aaa7bbb8aaa7bbb8bcb7aaa8bcb7bcb69aa8aaa8aaa69aa6abb6abb6abb6abb6a226a226a226a2261a661a661a661a600000000000000006c526c426c926c91659b649c66a566a46a7b6a7b667766776aba6abb676367636b746b746b746b74616c616c616c616c8abb8abb8abb8abb00000000000000006ba5579a6689598658875cb66abb9aa989aa98ac7abc6678968a88877c87cba952755823536952475648586354455223599900000000000000000000000000003ec63da76db79dc7554885498969b4377fa29e8289cdb9ce5ade5ade49ce49ce82a68a22839b8259885b8b73855683238aab00000000000000000000000000005d745d867da87e75448c458b86ad76ae68ac679c779b78ce3c9378867ca6adb784858933847a844788498963854584348989000000000000000000000000000057a668b899b8449396534493858364836853697769436667755667776c73498800000000000000000000000000000000000000000000000000000000000000006bba79b87d9679ad776a7b988abc8abc4aceaace4bba4bba6b8c4c9c4cac5b7c000000000000000000000000000000000000000000000000000000000000000059aaada7a9bdcdbd59aaada7a9bdcdbd8cb8a9b98ac889b8aabaacc79ea498bd000000000000000000000000000000000000000000000000000000000000000057ac596b55946abb5abb8ca65d8677ac437b5a3368886934547a595897a57b230000000000000000000000000000000000000000000000000000000000000000799a5c817b9b3a886abb8464676a7a967a857a857977898889882a954a956b950000000000000000000000000000000000000000000000000000000000000000499977997868799579875a6465995a8957a66a735ba53a935969479a576a467700000000000000000000000000000000000000000000000000000000000000005744985596659b747a659a76768a7a5676754777388735665976987794459465000000000000000000000000000000000000000000000000000000000000000088668a66868a9b8577666a4467846987778a7789797a87888b8676667a767ca50000000000000000000000000000000000000000000000000000000000000000449374934c957c9574847a438475a3958695768565956853b9447a777493a493000000000000000000000000000000000000000000000000000000000000000079547a838394689a49547a6357636975786383848997b384655873748974588400000000000000000000000000000000000000000000000000000000000000007da48ca769768b554b976cba3a824a82696259526a758c986963694268478b850000000000000000000000000000000000000000000000000000000000000000696559555579557458598674573353635677575579667a8758538b848a44838b0000000000000000000000000000000000000000000000000000000000000000385437883b95534549555a855877997598772b953b9529a939a95aa84b949a840000000000000000000000000000000000000000000000000000000000000000897687898776878578998485878b789a847b8b6579998a55886998788a879b9700000000000000000000000000000000000000000000000000000000000000006ba97988897469646b987a876a997a987b987955766777765c958a858777867700000000000000000000000000000000000000000000000000000000000000005a747b947b967866a855788928884566578879a98864a579233433343334633400000000000000000000000000000000000000000000000000000000000000006a747b846a844997598669987bb8b8aabaa96ba67cba9854687669864a864b86000000000000000000000000000000000000000000000000000000000000000038ab389b48ab47ac49ab48ac579b48ac49ab38ab58bc4b8659aa5c8457ac586a0000000000000000000000000000000000000000000000000000000000000000299b2999389a379b38893955589a79bc8c9588bc7a8c599a689a5b8558ac597a00000000000000000000000000000000000000000000000000000000000000002888378936773975579b389a579b488938884b74469a465747785b75568b586a000000000000000000000000000000000000000000000000000000000000000038553865285428444755566455763a64356746743779397445674c63469b585a0000000000000000000000000000000000000000000000000000000000000000284437643a7629641555297938874879385438664665355536775a85569a785a00000000000000000000000000000000000000000000000000000000000000005789789b779b6a75668a897b64558555876576798855845694749b74a68a986a000000000000000000000000000000000000000000000000000000000000000047776766678867667799798698768866976685673755387638763b74358b387a00000000000000000000000000000000000000000000000000000000000000005777686569874944498846774677685568646987677778775a456a65ab66ca550000000000000000000000000000000000000000000000000000000000000000355656666656455546455345634558655854aa749854775577737b64777a7a7900000000000000000000000000000000000000000000000000000000000000005955895598546c758c75ba76b88797749b75a98967888789978857888788a78800000000000000000000000000000000000000000000000000000000000000006977897799776a748a749a747987ba97aa998ba8a78bab75a87ab89cbb74b97b000000000000000000000000000000000000000000000000000000000000000059645788598858546a7569996a767a766887649c767476797a54766977667976000000000000000000000000000000000000000000000000000000000000000078887a75796577777a869976987799865777667787668a53857a885a98659546000000000000000000000000000000000000000000000000000000000000000087559877a96586779788b9769866888899877576777879647759a8659888a7440000000000000000000000000000000000000000000000000000000000000000785477887a55747b7585795b7999a9667456878889aa58997888797b56776855000000000000000000000000000000000000000000000000000000000000000048545854617b644557448744537b85565899899a39994a7a58998999a5558988000000000000000000000000000000000000000000000000000000000000000089659744a6559555a55698889486a57aab43a96b9556a665a854a579a744a5550000000000000000000000000000000000000000000000000000000000000000596587556677777777778578876687778974867787668876988897779876a744000000000000000000000000000000000000000000000000000000000000000067536556875448225922415851595456654587459456947b48997a86764585560000000000000000000000000000000000000000000000000000000000000000a854a89989998556a7559766a7779976a975997596749a64968a9779a55595450000000000000000000000000000000000000000000000000000000000000000674487549854885594558445a777a7778373579b5a32675584456975958b9944000000000000000000000000000000000000000000000000000000000000000077449754b674b469b964b658a766a864a777a975a566a754a677a875b777b9650000000000000000000000000000000000000000000000000000000000000000775577547445755676558744697377637766785334556566577859755877887600000000000000000000000000000000000000000000000000000000000000002789287328772a7436793a9457795a84368a3334323364555a757b856aaa9a5500000000000000000000000000000000000000000000000000000000000000005888516b5a3349a95964797778987a5375696a536668796577887a847a74797500000000000000000000000000000000000000000000000000000000000000007b537a53767b6769748775767a9a7988759c768a7b957a847775776478647854000000000000000000000000000000000000000000000000000000000000000098999788988998889b879a869a869a86696565676965667767446854677877880000000000000000000000000000000000000000000000000000000000000000678a77997ba647887a7589999ca59ba889aa9999655667bd6ba979a967bc6c7300000000000000000000000000000000000000000000000000000000000000006aaa6556518566775965485438985888576546854ca547775999699989997a9900000000000000000000000000000000000000000000000000000000000000006678526466335644769c5a7888547a785c4454a658885c946285627b6c54674a000000000000000000000000000000000000000000000000000000000000000033843b33359c337c395c3b853899355653745a33558b536b585b5a7557885445000000000000000000000000000000000000000000000000000000000000000026551566274525664a85486546564656377756664655465545454656516a656700000000000000000000000000000000000000000000000000000000000000004964696468553a86485437443645896588548856895477446a7569547a757954000000000000000000000000000000000000000000000000000000000000000036678566399988993b968b955ba658995566588859645a986ca7796477887ca6000000000000000000000000000000000000000000000000000000000000000019562a554c665c55156a256a468c557b1a8429744a845a83196b285a496b595b00000000000000000000000000000000000000000000000000000000000000001486248645a7549615782578469a5689187629764a875a861a692a694b7a5b79000000000000000000000000000000000000000000000000000000000000000017772777489858881555255546665556199528854884588411122112411251120000000000000000000000000000000000000000000000000000000000000000"
+ }
+ }
+ ], "enums": [], "externalEnums": [], "levelFields": [] },
+ "levels": [],
+ "worlds": [{ "iid": "8e3ea100-7820-11ed-9319-9dc1bfbb47b3", "identifier": "SampleWorld", "defaultLevelWidth": 256, "defaultLevelHeight": 256, "worldGridWidth": 256, "worldGridHeight": 256, "worldLayout": "LinearHorizontal", "levels": [
+ {
+ "identifier": "FirstLevel",
+ "iid": "de089bd1-7820-11ed-ac6b-1d2a77155ab2",
+ "uid": 0,
+ "worldX": -1,
+ "worldY": -1,
+ "worldDepth": 0,
+ "pxWid": 720,
+ "pxHei": 544,
+ "__bgColor": "#1A212E",
+ "bgColor": null,
+ "useAutoIdentifier": false,
+ "bgRelPath": null,
+ "bgPos": null,
+ "bgPivotX": 0.5,
+ "bgPivotY": 0.5,
+ "__smartColor": "#81858C",
+ "__bgPos": null,
+ "externalRelPath": null,
+ "fieldInstances": [],
+ "layerInstances": [
+ {
+ "__identifier": "Entities",
+ "__type": "Entities",
+ "__cWid": 45,
+ "__cHei": 34,
+ "__gridSize": 16,
+ "__opacity": 1,
+ "__pxTotalOffsetX": 0,
+ "__pxTotalOffsetY": 0,
+ "__tilesetDefUid": null,
+ "__tilesetRelPath": null,
+ "iid": "de08c2e2-7820-11ed-ac6b-fd96ee091199",
+ "levelId": 0,
+ "layerDefUid": 22,
+ "pxOffsetX": 0,
+ "pxOffsetY": 0,
+ "visible": true,
+ "optionalRules": [],
+ "intGridCsv": [],
+ "autoLayerTiles": [],
+ "seed": 7859480,
+ "overrideTilesetUid": null,
+ "gridTiles": [],
+ "entityInstances": [
+ {
+ "__identifier": "PlayerStart",
+ "__grid": [19,15],
+ "__pivot": [0.5,1],
+ "__tags": [],
+ "__tile": { "tilesetUid": 23, "x": 208, "y": 80, "w": 16, "h": 16 },
+ "__smartColor": "#75FF00",
+ "iid": "de08e9f0-7820-11ed-ac6b-9f5f3ed3c95d",
+ "width": 16,
+ "height": 16,
+ "defUid": 21,
+ "px": [312,256],
+ "fieldInstances": []
+ }
+ ]
+ },
+ {
+ "__identifier": "Collisions",
+ "__type": "IntGrid",
+ "__cWid": 45,
+ "__cHei": 34,
+ "__gridSize": 16,
+ "__opacity": 1,
+ "__pxTotalOffsetX": 0,
+ "__pxTotalOffsetY": 0,
+ "__tilesetDefUid": 4,
+ "__tilesetRelPath": "sampleWorldTiles.aseprite",
+ "iid": "de08e9f1-7820-11ed-ac6b-b5de4151f8f0",
+ "levelId": 0,
+ "layerDefUid": 1,
+ "pxOffsetX": 0,
+ "pxOffsetY": 0,
+ "visible": true,
+ "optionalRules": [],
+ "intGridCsv": [
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+ 1,1,1,1,1,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,1,1,1,1,
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,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,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,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,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+ 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,1,1,1,1,1,1,1,1,1,
+ 1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,
+ 0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,
+ 1,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+ 1,1,1,1,1,1,1,1,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,1,1,1,1,1,1,
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+ 0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,1,1,1,1,1,
+ 0,1,0,1,1,1,1,1,1,1,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+ 0,0,0,0,0,1,1,1,1,1,0,1,0,1,1,1,1,1,1,1,1,1,0,0,0,0,1,1,1,1,1,1,1,1,1,
+ 1,1,1,1,1,1,1,1,1,1,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,
+ 0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,1,1,0,1,1,1,0,1,
+ 0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,
+ 0,0,1,1,1,0,0,0,1,1,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+ 1,1,1,1,1,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1
+ ],
+ "autoLayerTiles": [
+ { "px": [176,160], "src": [64,0], "f": 0, "t": 4, "d": [17,461], "a": 1 },
+ { "px": [208,160], "src": [64,0], "f": 0, "t": 4, "d": [17,463], "a": 1 },
+ { "px": [240,160], "src": [64,0], "f": 0, "t": 4, "d": [17,465], "a": 1 },
+ { "px": [272,160], "src": [64,0], "f": 0, "t": 4, "d": [17,467], "a": 1 },
+ { "px": [304,160], "src": [64,0], "f": 0, "t": 4, "d": [17,469], "a": 1 },
+ { "px": [336,160], "src": [64,0], "f": 0, "t": 4, "d": [17,471], "a": 1 },
+ { "px": [368,160], "src": [64,0], "f": 0, "t": 4, "d": [17,473], "a": 1 },
+ { "px": [400,160], "src": [64,0], "f": 0, "t": 4, "d": [17,475], "a": 1 },
+ { "px": [432,160], "src": [64,0], "f": 0, "t": 4, "d": [17,477], "a": 1 },
+ { "px": [464,160], "src": [64,0], "f": 0, "t": 4, "d": [17,479], "a": 1 },
+ { "px": [496,160], "src": [64,0], "f": 0, "t": 4, "d": [17,481], "a": 1 },
+ { "px": [528,160], "src": [64,0], "f": 0, "t": 4, "d": [17,483], "a": 1 },
+ { "px": [176,176], "src": [64,0], "f": 0, "t": 4, "d": [17,506], "a": 1 },
+ { "px": [208,176], "src": [64,0], "f": 0, "t": 4, "d": [17,508], "a": 1 },
+ { "px": [240,176], "src": [64,0], "f": 0, "t": 4, "d": [17,510], "a": 1 },
+ { "px": [272,176], "src": [64,0], "f": 0, "t": 4, "d": [17,512], "a": 1 },
+ { "px": [304,176], "src": [64,0], "f": 0, "t": 4, "d": [17,514], "a": 1 },
+ { "px": [336,176], "src": [64,0], "f": 0, "t": 4, "d": [17,516], "a": 1 },
+ { "px": [368,176], "src": [64,0], "f": 0, "t": 4, "d": [17,518], "a": 1 },
+ { "px": [400,176], "src": [64,0], "f": 0, "t": 4, "d": [17,520], "a": 1 },
+ { "px": [432,176], "src": [64,0], "f": 0, "t": 4, "d": [17,522], "a": 1 },
+ { "px": [464,176], "src": [64,0], "f": 0, "t": 4, "d": [17,524], "a": 1 },
+ { "px": [496,176], "src": [64,0], "f": 0, "t": 4, "d": [17,526], "a": 1 },
+ { "px": [528,176], "src": [64,0], "f": 0, "t": 4, "d": [17,528], "a": 1 },
+ { "px": [560,176], "src": [64,0], "f": 0, "t": 4, "d": [17,530], "a": 1 },
+ { "px": [176,192], "src": [64,0], "f": 0, "t": 4, "d": [17,551], "a": 1 },
+ { "px": [208,192], "src": [64,0], "f": 0, "t": 4, "d": [17,553], "a": 1 },
+ { "px": [240,192], "src": [64,0], "f": 0, "t": 4, "d": [17,555], "a": 1 },
+ { "px": [272,192], "src": [64,0], "f": 0, "t": 4, "d": [17,557], "a": 1 },
+ { "px": [304,192], "src": [64,0], "f": 0, "t": 4, "d": [17,559], "a": 1 },
+ { "px": [336,192], "src": [64,0], "f": 0, "t": 4, "d": [17,561], "a": 1 },
+ { "px": [368,192], "src": [64,0], "f": 0, "t": 4, "d": [17,563], "a": 1 },
+ { "px": [400,192], "src": [64,0], "f": 0, "t": 4, "d": [17,565], "a": 1 },
+ { "px": [432,192], "src": [64,0], "f": 0, "t": 4, "d": [17,567], "a": 1 },
+ { "px": [464,192], "src": [64,0], "f": 0, "t": 4, "d": [17,569], "a": 1 },
+ { "px": [496,192], "src": [64,0], "f": 0, "t": 4, "d": [17,571], "a": 1 },
+ { "px": [528,192], "src": [64,0], "f": 0, "t": 4, "d": [17,573], "a": 1 },
+ { "px": [560,192], "src": [64,0], "f": 0, "t": 4, "d": [17,575], "a": 1 },
+ { "px": [176,208], "src": [64,0], "f": 0, "t": 4, "d": [17,596], "a": 1 },
+ { "px": [208,208], "src": [64,0], "f": 0, "t": 4, "d": [17,598], "a": 1 },
+ { "px": [240,208], "src": [64,0], "f": 0, "t": 4, "d": [17,600], "a": 1 },
+ { "px": [272,208], "src": [64,0], "f": 0, "t": 4, "d": [17,602], "a": 1 },
+ { "px": [304,208], "src": [64,0], "f": 0, "t": 4, "d": [17,604], "a": 1 },
+ { "px": [336,208], "src": [64,0], "f": 0, "t": 4, "d": [17,606], "a": 1 },
+ { "px": [368,208], "src": [64,0], "f": 0, "t": 4, "d": [17,608], "a": 1 },
+ { "px": [400,208], "src": [64,0], "f": 0, "t": 4, "d": [17,610], "a": 1 },
+ { "px": [432,208], "src": [64,0], "f": 0, "t": 4, "d": [17,612], "a": 1 },
+ { "px": [464,208], "src": [64,0], "f": 0, "t": 4, "d": [17,614], "a": 1 },
+ { "px": [496,208], "src": [64,0], "f": 0, "t": 4, "d": [17,616], "a": 1 },
+ { "px": [528,208], "src": [64,0], "f": 0, "t": 4, "d": [17,618], "a": 1 },
+ { "px": [560,208], "src": [64,0], "f": 0, "t": 4, "d": [17,620], "a": 1 },
+ { "px": [176,224], "src": [64,0], "f": 0, "t": 4, "d": [17,641], "a": 1 },
+ { "px": [208,224], "src": [64,0], "f": 0, "t": 4, "d": [17,643], "a": 1 },
+ { "px": [240,224], "src": [64,0], "f": 0, "t": 4, "d": [17,645], "a": 1 },
+ { "px": [272,224], "src": [64,0], "f": 0, "t": 4, "d": [17,647], "a": 1 },
+ { "px": [304,224], "src": [64,0], "f": 0, "t": 4, "d": [17,649], "a": 1 },
+ { "px": [336,224], "src": [64,0], "f": 0, "t": 4, "d": [17,651], "a": 1 },
+ { "px": [432,224], "src": [64,0], "f": 0, "t": 4, "d": [17,657], "a": 1 },
+ { "px": [464,224], "src": [64,0], "f": 0, "t": 4, "d": [17,659], "a": 1 },
+ { "px": [496,224], "src": [64,0], "f": 0, "t": 4, "d": [17,661], "a": 1 },
+ { "px": [528,224], "src": [64,0], "f": 0, "t": 4, "d": [17,663], "a": 1 },
+ { "px": [560,224], "src": [64,0], "f": 0, "t": 4, "d": [17,665], "a": 1 },
+ { "px": [176,240], "src": [64,0], "f": 0, "t": 4, "d": [17,686], "a": 1 },
+ { "px": [208,240], "src": [64,0], "f": 0, "t": 4, "d": [17,688], "a": 1 },
+ { "px": [240,240], "src": [64,0], "f": 0, "t": 4, "d": [17,690], "a": 1 },
+ { "px": [272,240], "src": [64,0], "f": 0, "t": 4, "d": [17,692], "a": 1 },
+ { "px": [304,240], "src": [64,0], "f": 0, "t": 4, "d": [17,694], "a": 1 },
+ { "px": [336,240], "src": [64,0], "f": 0, "t": 4, "d": [17,696], "a": 1 },
+ { "px": [432,240], "src": [64,0], "f": 0, "t": 4, "d": [17,702], "a": 1 },
+ { "px": [464,240], "src": [64,0], "f": 0, "t": 4, "d": [17,704], "a": 1 },
+ { "px": [496,240], "src": [64,0], "f": 0, "t": 4, "d": [17,706], "a": 1 },
+ { "px": [528,240], "src": [64,0], "f": 0, "t": 4, "d": [17,708], "a": 1 },
+ { "px": [560,240], "src": [64,0], "f": 0, "t": 4, "d": [17,710], "a": 1 },
+ { "px": [176,256], "src": [64,0], "f": 0, "t": 4, "d": [17,731], "a": 1 },
+ { "px": [208,256], "src": [64,0], "f": 0, "t": 4, "d": [17,733], "a": 1 },
+ { "px": [496,256], "src": [64,0], "f": 0, "t": 4, "d": [17,751], "a": 1 },
+ { "px": [528,256], "src": [64,0], "f": 0, "t": 4, "d": [17,753], "a": 1 },
+ { "px": [208,272], "src": [64,0], "f": 0, "t": 4, "d": [17,778], "a": 1 },
+ { "px": [496,272], "src": [64,0], "f": 0, "t": 4, "d": [17,796], "a": 1 },
+ { "px": [528,272], "src": [64,0], "f": 0, "t": 4, "d": [17,798], "a": 1 },
+ { "px": [208,288], "src": [64,0], "f": 0, "t": 4, "d": [17,823], "a": 1 },
+ { "px": [496,288], "src": [64,0], "f": 0, "t": 4, "d": [17,841], "a": 1 },
+ { "px": [528,288], "src": [64,0], "f": 0, "t": 4, "d": [17,843], "a": 1 },
+ { "px": [176,304], "src": [64,0], "f": 0, "t": 4, "d": [17,866], "a": 1 },
+ { "px": [208,304], "src": [64,0], "f": 0, "t": 4, "d": [17,868], "a": 1 },
+ { "px": [496,304], "src": [64,0], "f": 0, "t": 4, "d": [17,886], "a": 1 },
+ { "px": [528,304], "src": [64,0], "f": 0, "t": 4, "d": [17,888], "a": 1 },
+ { "px": [560,304], "src": [64,0], "f": 0, "t": 4, "d": [17,890], "a": 1 },
+ { "px": [176,320], "src": [64,0], "f": 0, "t": 4, "d": [17,911], "a": 1 },
+ { "px": [208,320], "src": [64,0], "f": 0, "t": 4, "d": [17,913], "a": 1 },
+ { "px": [528,320], "src": [64,0], "f": 0, "t": 4, "d": [17,933], "a": 1 },
+ { "px": [560,320], "src": [64,0], "f": 0, "t": 4, "d": [17,935], "a": 1 },
+ { "px": [176,336], "src": [64,0], "f": 0, "t": 4, "d": [17,956], "a": 1 },
+ { "px": [528,336], "src": [64,0], "f": 0, "t": 4, "d": [17,978], "a": 1 },
+ { "px": [560,336], "src": [64,0], "f": 0, "t": 4, "d": [17,980], "a": 1 },
+ { "px": [176,352], "src": [64,0], "f": 0, "t": 4, "d": [17,1001], "a": 1 },
+ { "px": [528,352], "src": [64,0], "f": 0, "t": 4, "d": [17,1023], "a": 1 },
+ { "px": [560,352], "src": [64,0], "f": 0, "t": 4, "d": [17,1025], "a": 1 },
+ { "px": [176,368], "src": [64,0], "f": 0, "t": 4, "d": [17,1046], "a": 1 },
+ { "px": [208,368], "src": [64,0], "f": 0, "t": 4, "d": [17,1048], "a": 1 },
+ { "px": [240,368], "src": [64,0], "f": 0, "t": 4, "d": [17,1050], "a": 1 },
+ { "px": [400,368], "src": [64,0], "f": 0, "t": 4, "d": [17,1060], "a": 1 },
+ { "px": [432,368], "src": [64,0], "f": 0, "t": 4, "d": [17,1062], "a": 1 },
+ { "px": [464,368], "src": [64,0], "f": 0, "t": 4, "d": [17,1064], "a": 1 },
+ { "px": [496,368], "src": [64,0], "f": 0, "t": 4, "d": [17,1066], "a": 1 },
+ { "px": [528,368], "src": [64,0], "f": 0, "t": 4, "d": [17,1068], "a": 1 },
+ { "px": [176,384], "src": [64,0], "f": 0, "t": 4, "d": [17,1091], "a": 1 },
+ { "px": [208,384], "src": [64,0], "f": 0, "t": 4, "d": [17,1093], "a": 1 },
+ { "px": [240,384], "src": [64,0], "f": 0, "t": 4, "d": [17,1095], "a": 1 },
+ { "px": [400,384], "src": [64,0], "f": 0, "t": 4, "d": [17,1105], "a": 1 },
+ { "px": [432,384], "src": [64,0], "f": 0, "t": 4, "d": [17,1107], "a": 1 },
+ { "px": [464,384], "src": [64,0], "f": 0, "t": 4, "d": [17,1109], "a": 1 },
+ { "px": [496,384], "src": [64,0], "f": 0, "t": 4, "d": [17,1111], "a": 1 },
+ { "px": [528,384], "src": [64,0], "f": 0, "t": 4, "d": [17,1113], "a": 1 },
+ { "px": [240,400], "src": [64,0], "f": 0, "t": 4, "d": [17,1140], "a": 1 },
+ { "px": [400,400], "src": [64,0], "f": 0, "t": 4, "d": [17,1150], "a": 1 },
+ { "px": [432,400], "src": [64,0], "f": 0, "t": 4, "d": [17,1152], "a": 1 },
+ { "px": [464,400], "src": [64,0], "f": 0, "t": 4, "d": [17,1154], "a": 1 },
+ { "px": [496,400], "src": [64,0], "f": 0, "t": 4, "d": [17,1156], "a": 1 },
+ { "px": [528,400], "src": [64,0], "f": 0, "t": 4, "d": [17,1158], "a": 1 },
+ { "px": [240,416], "src": [64,0], "f": 0, "t": 4, "d": [17,1185], "a": 1 },
+ { "px": [272,416], "src": [64,0], "f": 0, "t": 4, "d": [17,1187], "a": 1 },
+ { "px": [304,416], "src": [64,0], "f": 0, "t": 4, "d": [17,1189], "a": 1 },
+ { "px": [336,416], "src": [64,0], "f": 0, "t": 4, "d": [17,1191], "a": 1 },
+ { "px": [368,416], "src": [64,0], "f": 0, "t": 4, "d": [17,1193], "a": 1 },
+ { "px": [400,416], "src": [64,0], "f": 0, "t": 4, "d": [17,1195], "a": 1 },
+ { "px": [432,416], "src": [64,0], "f": 0, "t": 4, "d": [17,1197], "a": 1 },
+ { "px": [464,416], "src": [64,0], "f": 0, "t": 4, "d": [17,1199], "a": 1 },
+ { "px": [496,416], "src": [64,0], "f": 0, "t": 4, "d": [17,1201], "a": 1 },
+ { "px": [528,416], "src": [64,0], "f": 0, "t": 4, "d": [17,1203], "a": 1 },
+ { "px": [192,160], "src": [80,0], "f": 0, "t": 5, "d": [18,462], "a": 1 },
+ { "px": [224,160], "src": [80,0], "f": 0, "t": 5, "d": [18,464], "a": 1 },
+ { "px": [256,160], "src": [80,0], "f": 0, "t": 5, "d": [18,466], "a": 1 },
+ { "px": [288,160], "src": [80,0], "f": 0, "t": 5, "d": [18,468], "a": 1 },
+ { "px": [320,160], "src": [80,0], "f": 0, "t": 5, "d": [18,470], "a": 1 },
+ { "px": [352,160], "src": [80,0], "f": 0, "t": 5, "d": [18,472], "a": 1 },
+ { "px": [384,160], "src": [80,0], "f": 0, "t": 5, "d": [18,474], "a": 1 },
+ { "px": [416,160], "src": [80,0], "f": 0, "t": 5, "d": [18,476], "a": 1 },
+ { "px": [448,160], "src": [80,0], "f": 0, "t": 5, "d": [18,478], "a": 1 },
+ { "px": [480,160], "src": [80,0], "f": 0, "t": 5, "d": [18,480], "a": 1 },
+ { "px": [512,160], "src": [80,0], "f": 0, "t": 5, "d": [18,482], "a": 1 },
+ { "px": [544,160], "src": [80,0], "f": 0, "t": 5, "d": [18,484], "a": 1 },
+ { "px": [160,176], "src": [80,0], "f": 0, "t": 5, "d": [18,505], "a": 1 },
+ { "px": [192,176], "src": [80,0], "f": 0, "t": 5, "d": [18,507], "a": 1 },
+ { "px": [224,176], "src": [80,0], "f": 0, "t": 5, "d": [18,509], "a": 1 },
+ { "px": [256,176], "src": [80,0], "f": 0, "t": 5, "d": [18,511], "a": 1 },
+ { "px": [288,176], "src": [80,0], "f": 0, "t": 5, "d": [18,513], "a": 1 },
+ { "px": [320,176], "src": [80,0], "f": 0, "t": 5, "d": [18,515], "a": 1 },
+ { "px": [352,176], "src": [80,0], "f": 0, "t": 5, "d": [18,517], "a": 1 },
+ { "px": [384,176], "src": [80,0], "f": 0, "t": 5, "d": [18,519], "a": 1 },
+ { "px": [416,176], "src": [80,0], "f": 0, "t": 5, "d": [18,521], "a": 1 },
+ { "px": [448,176], "src": [80,0], "f": 0, "t": 5, "d": [18,523], "a": 1 },
+ { "px": [480,176], "src": [80,0], "f": 0, "t": 5, "d": [18,525], "a": 1 },
+ { "px": [512,176], "src": [80,0], "f": 0, "t": 5, "d": [18,527], "a": 1 },
+ { "px": [544,176], "src": [80,0], "f": 0, "t": 5, "d": [18,529], "a": 1 },
+ { "px": [160,192], "src": [80,0], "f": 0, "t": 5, "d": [18,550], "a": 1 },
+ { "px": [192,192], "src": [80,0], "f": 0, "t": 5, "d": [18,552], "a": 1 },
+ { "px": [224,192], "src": [80,0], "f": 0, "t": 5, "d": [18,554], "a": 1 },
+ { "px": [256,192], "src": [80,0], "f": 0, "t": 5, "d": [18,556], "a": 1 },
+ { "px": [288,192], "src": [80,0], "f": 0, "t": 5, "d": [18,558], "a": 1 },
+ { "px": [320,192], "src": [80,0], "f": 0, "t": 5, "d": [18,560], "a": 1 },
+ { "px": [352,192], "src": [80,0], "f": 0, "t": 5, "d": [18,562], "a": 1 },
+ { "px": [384,192], "src": [80,0], "f": 0, "t": 5, "d": [18,564], "a": 1 },
+ { "px": [416,192], "src": [80,0], "f": 0, "t": 5, "d": [18,566], "a": 1 },
+ { "px": [448,192], "src": [80,0], "f": 0, "t": 5, "d": [18,568], "a": 1 },
+ { "px": [480,192], "src": [80,0], "f": 0, "t": 5, "d": [18,570], "a": 1 },
+ { "px": [512,192], "src": [80,0], "f": 0, "t": 5, "d": [18,572], "a": 1 },
+ { "px": [544,192], "src": [80,0], "f": 0, "t": 5, "d": [18,574], "a": 1 },
+ { "px": [160,208], "src": [80,0], "f": 0, "t": 5, "d": [18,595], "a": 1 },
+ { "px": [192,208], "src": [80,0], "f": 0, "t": 5, "d": [18,597], "a": 1 },
+ { "px": [224,208], "src": [80,0], "f": 0, "t": 5, "d": [18,599], "a": 1 },
+ { "px": [256,208], "src": [80,0], "f": 0, "t": 5, "d": [18,601], "a": 1 },
+ { "px": [288,208], "src": [80,0], "f": 0, "t": 5, "d": [18,603], "a": 1 },
+ { "px": [320,208], "src": [80,0], "f": 0, "t": 5, "d": [18,605], "a": 1 },
+ { "px": [352,208], "src": [80,0], "f": 0, "t": 5, "d": [18,607], "a": 1 },
+ { "px": [384,208], "src": [80,0], "f": 0, "t": 5, "d": [18,609], "a": 1 },
+ { "px": [416,208], "src": [80,0], "f": 0, "t": 5, "d": [18,611], "a": 1 },
+ { "px": [448,208], "src": [80,0], "f": 0, "t": 5, "d": [18,613], "a": 1 },
+ { "px": [480,208], "src": [80,0], "f": 0, "t": 5, "d": [18,615], "a": 1 },
+ { "px": [512,208], "src": [80,0], "f": 0, "t": 5, "d": [18,617], "a": 1 },
+ { "px": [544,208], "src": [80,0], "f": 0, "t": 5, "d": [18,619], "a": 1 },
+ { "px": [160,224], "src": [80,0], "f": 0, "t": 5, "d": [18,640], "a": 1 },
+ { "px": [192,224], "src": [80,0], "f": 0, "t": 5, "d": [18,642], "a": 1 },
+ { "px": [224,224], "src": [80,0], "f": 0, "t": 5, "d": [18,644], "a": 1 },
+ { "px": [256,224], "src": [80,0], "f": 0, "t": 5, "d": [18,646], "a": 1 },
+ { "px": [288,224], "src": [80,0], "f": 0, "t": 5, "d": [18,648], "a": 1 },
+ { "px": [320,224], "src": [80,0], "f": 0, "t": 5, "d": [18,650], "a": 1 },
+ { "px": [352,224], "src": [80,0], "f": 0, "t": 5, "d": [18,652], "a": 1 },
+ { "px": [416,224], "src": [80,0], "f": 0, "t": 5, "d": [18,656], "a": 1 },
+ { "px": [448,224], "src": [80,0], "f": 0, "t": 5, "d": [18,658], "a": 1 },
+ { "px": [480,224], "src": [80,0], "f": 0, "t": 5, "d": [18,660], "a": 1 },
+ { "px": [512,224], "src": [80,0], "f": 0, "t": 5, "d": [18,662], "a": 1 },
+ { "px": [544,224], "src": [80,0], "f": 0, "t": 5, "d": [18,664], "a": 1 },
+ { "px": [160,240], "src": [80,0], "f": 0, "t": 5, "d": [18,685], "a": 1 },
+ { "px": [192,240], "src": [80,0], "f": 0, "t": 5, "d": [18,687], "a": 1 },
+ { "px": [224,240], "src": [80,0], "f": 0, "t": 5, "d": [18,689], "a": 1 },
+ { "px": [256,240], "src": [80,0], "f": 0, "t": 5, "d": [18,691], "a": 1 },
+ { "px": [288,240], "src": [80,0], "f": 0, "t": 5, "d": [18,693], "a": 1 },
+ { "px": [320,240], "src": [80,0], "f": 0, "t": 5, "d": [18,695], "a": 1 },
+ { "px": [352,240], "src": [80,0], "f": 0, "t": 5, "d": [18,697], "a": 1 },
+ { "px": [416,240], "src": [80,0], "f": 0, "t": 5, "d": [18,701], "a": 1 },
+ { "px": [448,240], "src": [80,0], "f": 0, "t": 5, "d": [18,703], "a": 1 },
+ { "px": [480,240], "src": [80,0], "f": 0, "t": 5, "d": [18,705], "a": 1 },
+ { "px": [512,240], "src": [80,0], "f": 0, "t": 5, "d": [18,707], "a": 1 },
+ { "px": [544,240], "src": [80,0], "f": 0, "t": 5, "d": [18,709], "a": 1 },
+ { "px": [160,256], "src": [80,0], "f": 0, "t": 5, "d": [18,730], "a": 1 },
+ { "px": [192,256], "src": [80,0], "f": 0, "t": 5, "d": [18,732], "a": 1 },
+ { "px": [224,256], "src": [80,0], "f": 0, "t": 5, "d": [18,734], "a": 1 },
+ { "px": [480,256], "src": [80,0], "f": 0, "t": 5, "d": [18,750], "a": 1 },
+ { "px": [512,256], "src": [80,0], "f": 0, "t": 5, "d": [18,752], "a": 1 },
+ { "px": [224,272], "src": [80,0], "f": 0, "t": 5, "d": [18,779], "a": 1 },
+ { "px": [480,272], "src": [80,0], "f": 0, "t": 5, "d": [18,795], "a": 1 },
+ { "px": [512,272], "src": [80,0], "f": 0, "t": 5, "d": [18,797], "a": 1 },
+ { "px": [224,288], "src": [80,0], "f": 0, "t": 5, "d": [18,824], "a": 1 },
+ { "px": [480,288], "src": [80,0], "f": 0, "t": 5, "d": [18,840], "a": 1 },
+ { "px": [512,288], "src": [80,0], "f": 0, "t": 5, "d": [18,842], "a": 1 },
+ { "px": [160,304], "src": [80,0], "f": 0, "t": 5, "d": [18,865], "a": 1 },
+ { "px": [192,304], "src": [80,0], "f": 0, "t": 5, "d": [18,867], "a": 1 },
+ { "px": [224,304], "src": [80,0], "f": 0, "t": 5, "d": [18,869], "a": 1 },
+ { "px": [480,304], "src": [80,0], "f": 0, "t": 5, "d": [18,885], "a": 1 },
+ { "px": [512,304], "src": [80,0], "f": 0, "t": 5, "d": [18,887], "a": 1 },
+ { "px": [544,304], "src": [80,0], "f": 0, "t": 5, "d": [18,889], "a": 1 },
+ { "px": [160,320], "src": [80,0], "f": 0, "t": 5, "d": [18,910], "a": 1 },
+ { "px": [192,320], "src": [80,0], "f": 0, "t": 5, "d": [18,912], "a": 1 },
+ { "px": [224,320], "src": [80,0], "f": 0, "t": 5, "d": [18,914], "a": 1 },
+ { "px": [320,320], "src": [80,0], "f": 0, "t": 5, "d": [18,920], "a": 1 },
+ { "px": [352,320], "src": [80,0], "f": 0, "t": 5, "d": [18,922], "a": 1 },
+ { "px": [512,320], "src": [80,0], "f": 0, "t": 5, "d": [18,932], "a": 1 },
+ { "px": [544,320], "src": [80,0], "f": 0, "t": 5, "d": [18,934], "a": 1 },
+ { "px": [160,336], "src": [80,0], "f": 0, "t": 5, "d": [18,955], "a": 1 },
+ { "px": [192,336], "src": [80,0], "f": 0, "t": 5, "d": [18,957], "a": 1 },
+ { "px": [512,336], "src": [80,0], "f": 0, "t": 5, "d": [18,977], "a": 1 },
+ { "px": [544,336], "src": [80,0], "f": 0, "t": 5, "d": [18,979], "a": 1 },
+ { "px": [160,352], "src": [80,0], "f": 0, "t": 5, "d": [18,1000], "a": 1 },
+ { "px": [192,352], "src": [80,0], "f": 0, "t": 5, "d": [18,1002], "a": 1 },
+ { "px": [512,352], "src": [80,0], "f": 0, "t": 5, "d": [18,1022], "a": 1 },
+ { "px": [544,352], "src": [80,0], "f": 0, "t": 5, "d": [18,1024], "a": 1 },
+ { "px": [160,368], "src": [80,0], "f": 0, "t": 5, "d": [18,1045], "a": 1 },
+ { "px": [192,368], "src": [80,0], "f": 0, "t": 5, "d": [18,1047], "a": 1 },
+ { "px": [224,368], "src": [80,0], "f": 0, "t": 5, "d": [18,1049], "a": 1 },
+ { "px": [256,368], "src": [80,0], "f": 0, "t": 5, "d": [18,1051], "a": 1 },
+ { "px": [416,368], "src": [80,0], "f": 0, "t": 5, "d": [18,1061], "a": 1 },
+ { "px": [448,368], "src": [80,0], "f": 0, "t": 5, "d": [18,1063], "a": 1 },
+ { "px": [480,368], "src": [80,0], "f": 0, "t": 5, "d": [18,1065], "a": 1 },
+ { "px": [512,368], "src": [80,0], "f": 0, "t": 5, "d": [18,1067], "a": 1 },
+ { "px": [160,384], "src": [80,0], "f": 0, "t": 5, "d": [18,1090], "a": 1 },
+ { "px": [192,384], "src": [80,0], "f": 0, "t": 5, "d": [18,1092], "a": 1 },
+ { "px": [224,384], "src": [80,0], "f": 0, "t": 5, "d": [18,1094], "a": 1 },
+ { "px": [256,384], "src": [80,0], "f": 0, "t": 5, "d": [18,1096], "a": 1 },
+ { "px": [416,384], "src": [80,0], "f": 0, "t": 5, "d": [18,1106], "a": 1 },
+ { "px": [448,384], "src": [80,0], "f": 0, "t": 5, "d": [18,1108], "a": 1 },
+ { "px": [480,384], "src": [80,0], "f": 0, "t": 5, "d": [18,1110], "a": 1 },
+ { "px": [512,384], "src": [80,0], "f": 0, "t": 5, "d": [18,1112], "a": 1 },
+ { "px": [256,400], "src": [80,0], "f": 0, "t": 5, "d": [18,1141], "a": 1 },
+ { "px": [416,400], "src": [80,0], "f": 0, "t": 5, "d": [18,1151], "a": 1 },
+ { "px": [448,400], "src": [80,0], "f": 0, "t": 5, "d": [18,1153], "a": 1 },
+ { "px": [480,400], "src": [80,0], "f": 0, "t": 5, "d": [18,1155], "a": 1 },
+ { "px": [512,400], "src": [80,0], "f": 0, "t": 5, "d": [18,1157], "a": 1 },
+ { "px": [256,416], "src": [80,0], "f": 0, "t": 5, "d": [18,1186], "a": 1 },
+ { "px": [288,416], "src": [80,0], "f": 0, "t": 5, "d": [18,1188], "a": 1 },
+ { "px": [320,416], "src": [80,0], "f": 0, "t": 5, "d": [18,1190], "a": 1 },
+ { "px": [352,416], "src": [80,0], "f": 0, "t": 5, "d": [18,1192], "a": 1 },
+ { "px": [384,416], "src": [80,0], "f": 0, "t": 5, "d": [18,1194], "a": 1 },
+ { "px": [416,416], "src": [80,0], "f": 0, "t": 5, "d": [18,1196], "a": 1 },
+ { "px": [448,416], "src": [80,0], "f": 0, "t": 5, "d": [18,1198], "a": 1 },
+ { "px": [480,416], "src": [80,0], "f": 0, "t": 5, "d": [18,1200], "a": 1 },
+ { "px": [512,416], "src": [80,0], "f": 0, "t": 5, "d": [18,1202], "a": 1 },
+ { "px": [176,144], "src": [80,0], "f": 0, "t": 5, "d": [19,416], "a": 1 },
+ { "px": [192,144], "src": [80,0], "f": 0, "t": 5, "d": [19,417], "a": 1 },
+ { "px": [208,144], "src": [80,0], "f": 0, "t": 5, "d": [19,418], "a": 1 },
+ { "px": [224,144], "src": [80,0], "f": 0, "t": 5, "d": [19,419], "a": 1 },
+ { "px": [240,144], "src": [80,0], "f": 0, "t": 5, "d": [19,420], "a": 1 },
+ { "px": [256,144], "src": [80,0], "f": 0, "t": 5, "d": [19,421], "a": 1 },
+ { "px": [272,144], "src": [80,0], "f": 0, "t": 5, "d": [19,422], "a": 1 },
+ { "px": [288,144], "src": [80,0], "f": 0, "t": 5, "d": [19,423], "a": 1 },
+ { "px": [304,144], "src": [80,0], "f": 0, "t": 5, "d": [19,424], "a": 1 },
+ { "px": [320,144], "src": [80,0], "f": 0, "t": 5, "d": [19,425], "a": 1 },
+ { "px": [336,144], "src": [80,0], "f": 0, "t": 5, "d": [19,426], "a": 1 },
+ { "px": [352,144], "src": [80,0], "f": 0, "t": 5, "d": [19,427], "a": 1 },
+ { "px": [368,144], "src": [80,0], "f": 0, "t": 5, "d": [19,428], "a": 1 },
+ { "px": [384,144], "src": [80,0], "f": 0, "t": 5, "d": [19,429], "a": 1 },
+ { "px": [400,144], "src": [80,0], "f": 0, "t": 5, "d": [19,430], "a": 1 },
+ { "px": [416,144], "src": [80,0], "f": 0, "t": 5, "d": [19,431], "a": 1 },
+ { "px": [432,144], "src": [80,0], "f": 0, "t": 5, "d": [19,432], "a": 1 },
+ { "px": [448,144], "src": [80,0], "f": 0, "t": 5, "d": [19,433], "a": 1 },
+ { "px": [464,144], "src": [80,0], "f": 0, "t": 5, "d": [19,434], "a": 1 },
+ { "px": [480,144], "src": [80,0], "f": 0, "t": 5, "d": [19,435], "a": 1 },
+ { "px": [496,144], "src": [80,0], "f": 0, "t": 5, "d": [19,436], "a": 1 },
+ { "px": [512,144], "src": [80,0], "f": 0, "t": 5, "d": [19,437], "a": 1 },
+ { "px": [528,144], "src": [80,0], "f": 0, "t": 5, "d": [19,438], "a": 1 },
+ { "px": [544,144], "src": [80,0], "f": 0, "t": 5, "d": [19,439], "a": 1 },
+ { "px": [160,160], "src": [80,0], "f": 0, "t": 5, "d": [19,460], "a": 1 },
+ { "px": [560,160], "src": [80,0], "f": 0, "t": 5, "d": [19,485], "a": 1 },
+ { "px": [160,288], "src": [80,0], "f": 0, "t": 5, "d": [19,820], "a": 1 },
+ { "px": [176,288], "src": [80,0], "f": 0, "t": 5, "d": [19,821], "a": 1 },
+ { "px": [192,288], "src": [80,0], "f": 0, "t": 5, "d": [19,822], "a": 1 },
+ { "px": [544,288], "src": [80,0], "f": 0, "t": 5, "d": [19,844], "a": 1 },
+ { "px": [560,288], "src": [80,0], "f": 0, "t": 5, "d": [19,845], "a": 1 },
+ { "px": [320,304], "src": [80,0], "f": 0, "t": 5, "d": [19,875], "a": 1 },
+ { "px": [352,304], "src": [80,0], "f": 0, "t": 5, "d": [19,877], "a": 1 },
+ { "px": [208,352], "src": [80,0], "f": 0, "t": 5, "d": [19,1003], "a": 1 },
+ { "px": [224,352], "src": [80,0], "f": 0, "t": 5, "d": [19,1004], "a": 1 },
+ { "px": [240,352], "src": [80,0], "f": 0, "t": 5, "d": [19,1005], "a": 1 },
+ { "px": [256,352], "src": [80,0], "f": 0, "t": 5, "d": [19,1006], "a": 1 },
+ { "px": [304,352], "src": [80,0], "f": 0, "t": 5, "d": [19,1009], "a": 1 },
+ { "px": [368,352], "src": [80,0], "f": 0, "t": 5, "d": [19,1013], "a": 1 },
+ { "px": [400,352], "src": [80,0], "f": 0, "t": 5, "d": [19,1015], "a": 1 },
+ { "px": [416,352], "src": [80,0], "f": 0, "t": 5, "d": [19,1016], "a": 1 },
+ { "px": [432,352], "src": [80,0], "f": 0, "t": 5, "d": [19,1017], "a": 1 },
+ { "px": [448,352], "src": [80,0], "f": 0, "t": 5, "d": [19,1018], "a": 1 },
+ { "px": [464,352], "src": [80,0], "f": 0, "t": 5, "d": [19,1019], "a": 1 },
+ { "px": [480,352], "src": [80,0], "f": 0, "t": 5, "d": [19,1020], "a": 1 },
+ { "px": [496,352], "src": [80,0], "f": 0, "t": 5, "d": [19,1021], "a": 1 },
+ { "px": [320,368], "src": [80,0], "f": 0, "t": 5, "d": [19,1055], "a": 1 },
+ { "px": [336,368], "src": [80,0], "f": 0, "t": 5, "d": [19,1056], "a": 1 },
+ { "px": [352,368], "src": [80,0], "f": 0, "t": 5, "d": [19,1057], "a": 1 },
+ { "px": [272,400], "src": [80,0], "f": 0, "t": 5, "d": [19,1142], "a": 1 },
+ { "px": [288,400], "src": [80,0], "f": 0, "t": 5, "d": [19,1143], "a": 1 },
+ { "px": [304,400], "src": [80,0], "f": 0, "t": 5, "d": [19,1144], "a": 1 },
+ { "px": [320,400], "src": [80,0], "f": 0, "t": 5, "d": [19,1145], "a": 1 },
+ { "px": [336,400], "src": [80,0], "f": 0, "t": 5, "d": [19,1146], "a": 1 },
+ { "px": [352,400], "src": [80,0], "f": 0, "t": 5, "d": [19,1147], "a": 1 },
+ { "px": [368,400], "src": [80,0], "f": 0, "t": 5, "d": [19,1148], "a": 1 },
+ { "px": [384,400], "src": [80,0], "f": 0, "t": 5, "d": [19,1149], "a": 1 },
+ { "px": [16,0], "src": [0,0], "f": 0, "t": 0, "d": [6,1], "a": 1 },
+ { "px": [48,0], "src": [16,0], "f": 0, "t": 1, "d": [6,3], "a": 1 },
+ { "px": [80,0], "src": [0,0], "f": 0, "t": 0, "d": [6,5], "a": 1 },
+ { "px": [112,0], "src": [16,0], "f": 0, "t": 1, "d": [6,7], "a": 1 },
+ { "px": [144,0], "src": [16,0], "f": 0, "t": 1, "d": [6,9], "a": 1 },
+ { "px": [176,0], "src": [0,0], "f": 0, "t": 0, "d": [6,11], "a": 1 },
+ { "px": [208,0], "src": [16,0], "f": 0, "t": 1, "d": [6,13], "a": 1 },
+ { "px": [240,0], "src": [0,0], "f": 0, "t": 0, "d": [6,15], "a": 1 },
+ { "px": [272,0], "src": [16,0], "f": 0, "t": 1, "d": [6,17], "a": 1 },
+ { "px": [304,0], "src": [16,0], "f": 0, "t": 1, "d": [6,19], "a": 1 },
+ { "px": [336,0], "src": [16,0], "f": 0, "t": 1, "d": [6,21], "a": 1 },
+ { "px": [368,0], "src": [16,0], "f": 0, "t": 1, "d": [6,23], "a": 1 },
+ { "px": [400,0], "src": [0,0], "f": 0, "t": 0, "d": [6,25], "a": 1 },
+ { "px": [432,0], "src": [0,0], "f": 0, "t": 0, "d": [6,27], "a": 1 },
+ { "px": [464,0], "src": [0,0], "f": 0, "t": 0, "d": [6,29], "a": 1 },
+ { "px": [496,0], "src": [0,0], "f": 0, "t": 0, "d": [6,31], "a": 1 },
+ { "px": [528,0], "src": [16,0], "f": 0, "t": 1, "d": [6,33], "a": 1 },
+ { "px": [560,0], "src": [0,0], "f": 0, "t": 0, "d": [6,35], "a": 1 },
+ { "px": [592,0], "src": [16,0], "f": 0, "t": 1, "d": [6,37], "a": 1 },
+ { "px": [624,0], "src": [0,0], "f": 0, "t": 0, "d": [6,39], "a": 1 },
+ { "px": [656,0], "src": [0,0], "f": 0, "t": 0, "d": [6,41], "a": 1 },
+ { "px": [688,0], "src": [16,0], "f": 0, "t": 1, "d": [6,43], "a": 1 },
+ { "px": [0,16], "src": [16,0], "f": 0, "t": 1, "d": [6,45], "a": 1 },
+ { "px": [32,16], "src": [16,0], "f": 0, "t": 1, "d": [6,47], "a": 1 },
+ { "px": [64,16], "src": [0,0], "f": 0, "t": 0, "d": [6,49], "a": 1 },
+ { "px": [128,16], "src": [16,0], "f": 0, "t": 1, "d": [6,53], "a": 1 },
+ { "px": [160,16], "src": [0,0], "f": 0, "t": 0, "d": [6,55], "a": 1 },
+ { "px": [192,16], "src": [16,0], "f": 0, "t": 1, "d": [6,57], "a": 1 },
+ { "px": [224,16], "src": [0,0], "f": 0, "t": 0, "d": [6,59], "a": 1 },
+ { "px": [256,16], "src": [0,0], "f": 0, "t": 0, "d": [6,61], "a": 1 },
+ { "px": [320,16], "src": [0,0], "f": 0, "t": 0, "d": [6,65], "a": 1 },
+ { "px": [352,16], "src": [16,0], "f": 0, "t": 1, "d": [6,67], "a": 1 },
+ { "px": [384,16], "src": [0,0], "f": 0, "t": 0, "d": [6,69], "a": 1 },
+ { "px": [416,16], "src": [0,0], "f": 0, "t": 0, "d": [6,71], "a": 1 },
+ { "px": [448,16], "src": [0,0], "f": 0, "t": 0, "d": [6,73], "a": 1 },
+ { "px": [512,16], "src": [16,0], "f": 0, "t": 1, "d": [6,77], "a": 1 },
+ { "px": [544,16], "src": [16,0], "f": 0, "t": 1, "d": [6,79], "a": 1 },
+ { "px": [576,16], "src": [0,0], "f": 0, "t": 0, "d": [6,81], "a": 1 },
+ { "px": [608,16], "src": [16,0], "f": 0, "t": 1, "d": [6,83], "a": 1 },
+ { "px": [640,16], "src": [16,0], "f": 0, "t": 1, "d": [6,85], "a": 1 },
+ { "px": [704,16], "src": [16,0], "f": 0, "t": 1, "d": [6,89], "a": 1 },
+ { "px": [16,32], "src": [0,0], "f": 0, "t": 0, "d": [6,91], "a": 1 },
+ { "px": [48,32], "src": [16,0], "f": 0, "t": 1, "d": [6,93], "a": 1 },
+ { "px": [80,32], "src": [16,0], "f": 0, "t": 1, "d": [6,95], "a": 1 },
+ { "px": [112,32], "src": [16,0], "f": 0, "t": 1, "d": [6,97], "a": 1 },
+ { "px": [144,32], "src": [16,0], "f": 0, "t": 1, "d": [6,99], "a": 1 },
+ { "px": [176,32], "src": [0,0], "f": 0, "t": 0, "d": [6,101], "a": 1 },
+ { "px": [208,32], "src": [16,0], "f": 0, "t": 1, "d": [6,103], "a": 1 },
+ { "px": [240,32], "src": [0,0], "f": 0, "t": 0, "d": [6,105], "a": 1 },
+ { "px": [272,32], "src": [0,0], "f": 0, "t": 0, "d": [6,107], "a": 1 },
+ { "px": [304,32], "src": [16,0], "f": 0, "t": 1, "d": [6,109], "a": 1 },
+ { "px": [336,32], "src": [16,0], "f": 0, "t": 1, "d": [6,111], "a": 1 },
+ { "px": [368,32], "src": [0,0], "f": 0, "t": 0, "d": [6,113], "a": 1 },
+ { "px": [400,32], "src": [16,0], "f": 0, "t": 1, "d": [6,115], "a": 1 },
+ { "px": [432,32], "src": [0,0], "f": 0, "t": 0, "d": [6,117], "a": 1 },
+ { "px": [464,32], "src": [16,0], "f": 0, "t": 1, "d": [6,119], "a": 1 },
+ { "px": [496,32], "src": [0,0], "f": 0, "t": 0, "d": [6,121], "a": 1 },
+ { "px": [528,32], "src": [0,0], "f": 0, "t": 0, "d": [6,123], "a": 1 },
+ { "px": [560,32], "src": [0,0], "f": 0, "t": 0, "d": [6,125], "a": 1 },
+ { "px": [592,32], "src": [16,0], "f": 0, "t": 1, "d": [6,127], "a": 1 },
+ { "px": [624,32], "src": [16,0], "f": 0, "t": 1, "d": [6,129], "a": 1 },
+ { "px": [656,32], "src": [0,0], "f": 0, "t": 0, "d": [6,131], "a": 1 },
+ { "px": [688,32], "src": [16,0], "f": 0, "t": 1, "d": [6,133], "a": 1 },
+ { "px": [0,48], "src": [16,0], "f": 0, "t": 1, "d": [6,135], "a": 1 },
+ { "px": [32,48], "src": [16,0], "f": 0, "t": 1, "d": [6,137], "a": 1 },
+ { "px": [64,48], "src": [16,0], "f": 0, "t": 1, "d": [6,139], "a": 1 },
+ { "px": [96,48], "src": [0,0], "f": 0, "t": 0, "d": [6,141], "a": 1 },
+ { "px": [128,48], "src": [16,0], "f": 0, "t": 1, "d": [6,143], "a": 1 },
+ { "px": [192,48], "src": [0,0], "f": 0, "t": 0, "d": [6,147], "a": 1 },
+ { "px": [256,48], "src": [0,0], "f": 0, "t": 0, "d": [6,151], "a": 1 },
+ { "px": [320,48], "src": [0,0], "f": 0, "t": 0, "d": [6,155], "a": 1 },
+ { "px": [352,48], "src": [0,0], "f": 0, "t": 0, "d": [6,157], "a": 1 },
+ { "px": [384,48], "src": [16,0], "f": 0, "t": 1, "d": [6,159], "a": 1 },
+ { "px": [416,48], "src": [16,0], "f": 0, "t": 1, "d": [6,161], "a": 1 },
+ { "px": [448,48], "src": [0,0], "f": 0, "t": 0, "d": [6,163], "a": 1 },
+ { "px": [480,48], "src": [0,0], "f": 0, "t": 0, "d": [6,165], "a": 1 },
+ { "px": [512,48], "src": [0,0], "f": 0, "t": 0, "d": [6,167], "a": 1 },
+ { "px": [576,48], "src": [16,0], "f": 0, "t": 1, "d": [6,171], "a": 1 },
+ { "px": [640,48], "src": [0,0], "f": 0, "t": 0, "d": [6,175], "a": 1 },
+ { "px": [672,48], "src": [0,0], "f": 0, "t": 0, "d": [6,177], "a": 1 },
+ { "px": [704,48], "src": [0,0], "f": 0, "t": 0, "d": [6,179], "a": 1 },
+ { "px": [16,64], "src": [16,0], "f": 0, "t": 1, "d": [6,181], "a": 1 },
+ { "px": [48,64], "src": [16,0], "f": 0, "t": 1, "d": [6,183], "a": 1 },
+ { "px": [80,64], "src": [16,0], "f": 0, "t": 1, "d": [6,185], "a": 1 },
+ { "px": [112,64], "src": [0,0], "f": 0, "t": 0, "d": [6,187], "a": 1 },
+ { "px": [144,64], "src": [0,0], "f": 0, "t": 0, "d": [6,189], "a": 1 },
+ { "px": [176,64], "src": [0,0], "f": 0, "t": 0, "d": [6,191], "a": 1 },
+ { "px": [208,64], "src": [16,0], "f": 0, "t": 1, "d": [6,193], "a": 1 },
+ { "px": [240,64], "src": [16,0], "f": 0, "t": 1, "d": [6,195], "a": 1 },
+ { "px": [272,64], "src": [16,0], "f": 0, "t": 1, "d": [6,197], "a": 1 },
+ { "px": [304,64], "src": [0,0], "f": 0, "t": 0, "d": [6,199], "a": 1 },
+ { "px": [336,64], "src": [0,0], "f": 0, "t": 0, "d": [6,201], "a": 1 },
+ { "px": [368,64], "src": [16,0], "f": 0, "t": 1, "d": [6,203], "a": 1 },
+ { "px": [400,64], "src": [0,0], "f": 0, "t": 0, "d": [6,205], "a": 1 },
+ { "px": [432,64], "src": [16,0], "f": 0, "t": 1, "d": [6,207], "a": 1 },
+ { "px": [464,64], "src": [16,0], "f": 0, "t": 1, "d": [6,209], "a": 1 },
+ { "px": [496,64], "src": [0,0], "f": 0, "t": 0, "d": [6,211], "a": 1 },
+ { "px": [528,64], "src": [16,0], "f": 0, "t": 1, "d": [6,213], "a": 1 },
+ { "px": [560,64], "src": [16,0], "f": 0, "t": 1, "d": [6,215], "a": 1 },
+ { "px": [592,64], "src": [0,0], "f": 0, "t": 0, "d": [6,217], "a": 1 },
+ { "px": [624,64], "src": [0,0], "f": 0, "t": 0, "d": [6,219], "a": 1 },
+ { "px": [656,64], "src": [16,0], "f": 0, "t": 1, "d": [6,221], "a": 1 },
+ { "px": [688,64], "src": [16,0], "f": 0, "t": 1, "d": [6,223], "a": 1 },
+ { "px": [0,80], "src": [0,0], "f": 0, "t": 0, "d": [6,225], "a": 1 },
+ { "px": [64,80], "src": [0,0], "f": 0, "t": 0, "d": [6,229], "a": 1 },
+ { "px": [128,80], "src": [0,0], "f": 0, "t": 0, "d": [6,233], "a": 1 },
+ { "px": [160,80], "src": [16,0], "f": 0, "t": 1, "d": [6,235], "a": 1 },
+ { "px": [192,80], "src": [0,0], "f": 0, "t": 0, "d": [6,237], "a": 1 },
+ { "px": [224,80], "src": [16,0], "f": 0, "t": 1, "d": [6,239], "a": 1 },
+ { "px": [256,80], "src": [0,0], "f": 0, "t": 0, "d": [6,241], "a": 1 },
+ { "px": [320,80], "src": [0,0], "f": 0, "t": 0, "d": [6,245], "a": 1 },
+ { "px": [384,80], "src": [16,0], "f": 0, "t": 1, "d": [6,249], "a": 1 },
+ { "px": [448,80], "src": [0,0], "f": 0, "t": 0, "d": [6,253], "a": 1 },
+ { "px": [512,80], "src": [0,0], "f": 0, "t": 0, "d": [6,257], "a": 1 },
+ { "px": [544,80], "src": [0,0], "f": 0, "t": 0, "d": [6,259], "a": 1 },
+ { "px": [576,80], "src": [0,0], "f": 0, "t": 0, "d": [6,261], "a": 1 },
+ { "px": [640,80], "src": [0,0], "f": 0, "t": 0, "d": [6,265], "a": 1 },
+ { "px": [672,80], "src": [0,0], "f": 0, "t": 0, "d": [6,267], "a": 1 },
+ { "px": [704,80], "src": [0,0], "f": 0, "t": 0, "d": [6,269], "a": 1 },
+ { "px": [16,96], "src": [16,0], "f": 0, "t": 1, "d": [6,271], "a": 1 },
+ { "px": [48,96], "src": [16,0], "f": 0, "t": 1, "d": [6,273], "a": 1 },
+ { "px": [80,96], "src": [16,0], "f": 0, "t": 1, "d": [6,275], "a": 1 },
+ { "px": [112,96], "src": [0,0], "f": 0, "t": 0, "d": [6,277], "a": 1 },
+ { "px": [144,96], "src": [16,0], "f": 0, "t": 1, "d": [6,279], "a": 1 },
+ { "px": [176,96], "src": [0,0], "f": 0, "t": 0, "d": [6,281], "a": 1 },
+ { "px": [208,96], "src": [16,0], "f": 0, "t": 1, "d": [6,283], "a": 1 },
+ { "px": [240,96], "src": [16,0], "f": 0, "t": 1, "d": [6,285], "a": 1 },
+ { "px": [272,96], "src": [0,0], "f": 0, "t": 0, "d": [6,287], "a": 1 },
+ { "px": [304,96], "src": [0,0], "f": 0, "t": 0, "d": [6,289], "a": 1 },
+ { "px": [336,96], "src": [0,0], "f": 0, "t": 0, "d": [6,291], "a": 1 },
+ { "px": [368,96], "src": [16,0], "f": 0, "t": 1, "d": [6,293], "a": 1 },
+ { "px": [400,96], "src": [0,0], "f": 0, "t": 0, "d": [6,295], "a": 1 },
+ { "px": [432,96], "src": [0,0], "f": 0, "t": 0, "d": [6,297], "a": 1 },
+ { "px": [464,96], "src": [16,0], "f": 0, "t": 1, "d": [6,299], "a": 1 },
+ { "px": [496,96], "src": [0,0], "f": 0, "t": 0, "d": [6,301], "a": 1 },
+ { "px": [528,96], "src": [16,0], "f": 0, "t": 1, "d": [6,303], "a": 1 },
+ { "px": [560,96], "src": [16,0], "f": 0, "t": 1, "d": [6,305], "a": 1 },
+ { "px": [592,96], "src": [16,0], "f": 0, "t": 1, "d": [6,307], "a": 1 },
+ { "px": [624,96], "src": [0,0], "f": 0, "t": 0, "d": [6,309], "a": 1 },
+ { "px": [656,96], "src": [0,0], "f": 0, "t": 0, "d": [6,311], "a": 1 },
+ { "px": [688,96], "src": [16,0], "f": 0, "t": 1, "d": [6,313], "a": 1 },
+ { "px": [0,112], "src": [16,0], "f": 0, "t": 1, "d": [6,315], "a": 1 },
+ { "px": [64,112], "src": [0,0], "f": 0, "t": 0, "d": [6,319], "a": 1 },
+ { "px": [96,112], "src": [0,0], "f": 0, "t": 0, "d": [6,321], "a": 1 },
+ { "px": [128,112], "src": [0,0], "f": 0, "t": 0, "d": [6,323], "a": 1 },
+ { "px": [192,112], "src": [0,0], "f": 0, "t": 0, "d": [6,327], "a": 1 },
+ { "px": [224,112], "src": [0,0], "f": 0, "t": 0, "d": [6,329], "a": 1 },
+ { "px": [256,112], "src": [16,0], "f": 0, "t": 1, "d": [6,331], "a": 1 },
+ { "px": [288,112], "src": [16,0], "f": 0, "t": 1, "d": [6,333], "a": 1 },
+ { "px": [320,112], "src": [16,0], "f": 0, "t": 1, "d": [6,335], "a": 1 },
+ { "px": [384,112], "src": [0,0], "f": 0, "t": 0, "d": [6,339], "a": 1 },
+ { "px": [416,112], "src": [0,0], "f": 0, "t": 0, "d": [6,341], "a": 1 },
+ { "px": [448,112], "src": [0,0], "f": 0, "t": 0, "d": [6,343], "a": 1 },
+ { "px": [512,112], "src": [16,0], "f": 0, "t": 1, "d": [6,347], "a": 1 },
+ { "px": [544,112], "src": [16,0], "f": 0, "t": 1, "d": [6,349], "a": 1 },
+ { "px": [576,112], "src": [0,0], "f": 0, "t": 0, "d": [6,351], "a": 1 },
+ { "px": [640,112], "src": [16,0], "f": 0, "t": 1, "d": [6,355], "a": 1 },
+ { "px": [672,112], "src": [16,0], "f": 0, "t": 1, "d": [6,357], "a": 1 },
+ { "px": [704,112], "src": [0,0], "f": 0, "t": 0, "d": [6,359], "a": 1 },
+ { "px": [16,128], "src": [16,0], "f": 0, "t": 1, "d": [6,361], "a": 1 },
+ { "px": [48,128], "src": [0,0], "f": 0, "t": 0, "d": [6,363], "a": 1 },
+ { "px": [80,128], "src": [16,0], "f": 0, "t": 1, "d": [6,365], "a": 1 },
+ { "px": [112,128], "src": [0,0], "f": 0, "t": 0, "d": [6,367], "a": 1 },
+ { "px": [144,128], "src": [16,0], "f": 0, "t": 1, "d": [6,369], "a": 1 },
+ { "px": [176,128], "src": [0,0], "f": 0, "t": 0, "d": [6,371], "a": 1 },
+ { "px": [208,128], "src": [0,0], "f": 0, "t": 0, "d": [6,373], "a": 1 },
+ { "px": [240,128], "src": [16,0], "f": 0, "t": 1, "d": [6,375], "a": 1 },
+ { "px": [272,128], "src": [0,0], "f": 0, "t": 0, "d": [6,377], "a": 1 },
+ { "px": [304,128], "src": [0,0], "f": 0, "t": 0, "d": [6,379], "a": 1 },
+ { "px": [336,128], "src": [0,0], "f": 0, "t": 0, "d": [6,381], "a": 1 },
+ { "px": [368,128], "src": [0,0], "f": 0, "t": 0, "d": [6,383], "a": 1 },
+ { "px": [400,128], "src": [0,0], "f": 0, "t": 0, "d": [6,385], "a": 1 },
+ { "px": [432,128], "src": [0,0], "f": 0, "t": 0, "d": [6,387], "a": 1 },
+ { "px": [464,128], "src": [0,0], "f": 0, "t": 0, "d": [6,389], "a": 1 },
+ { "px": [496,128], "src": [16,0], "f": 0, "t": 1, "d": [6,391], "a": 1 },
+ { "px": [528,128], "src": [16,0], "f": 0, "t": 1, "d": [6,393], "a": 1 },
+ { "px": [560,128], "src": [16,0], "f": 0, "t": 1, "d": [6,395], "a": 1 },
+ { "px": [592,128], "src": [0,0], "f": 0, "t": 0, "d": [6,397], "a": 1 },
+ { "px": [624,128], "src": [16,0], "f": 0, "t": 1, "d": [6,399], "a": 1 },
+ { "px": [656,128], "src": [0,0], "f": 0, "t": 0, "d": [6,401], "a": 1 },
+ { "px": [688,128], "src": [16,0], "f": 0, "t": 1, "d": [6,403], "a": 1 },
+ { "px": [0,144], "src": [0,0], "f": 0, "t": 0, "d": [6,405], "a": 1 },
+ { "px": [32,144], "src": [0,0], "f": 0, "t": 0, "d": [6,407], "a": 1 },
+ { "px": [64,144], "src": [0,0], "f": 0, "t": 0, "d": [6,409], "a": 1 },
+ { "px": [96,144], "src": [0,0], "f": 0, "t": 0, "d": [6,411], "a": 1 },
+ { "px": [128,144], "src": [16,0], "f": 0, "t": 1, "d": [6,413], "a": 1 },
+ { "px": [160,144], "src": [16,0], "f": 0, "t": 1, "d": [6,415], "a": 1 },
+ { "px": [576,144], "src": [0,0], "f": 0, "t": 0, "d": [6,441], "a": 1 },
+ { "px": [608,144], "src": [0,0], "f": 0, "t": 0, "d": [6,443], "a": 1 },
+ { "px": [640,144], "src": [16,0], "f": 0, "t": 1, "d": [6,445], "a": 1 },
+ { "px": [704,144], "src": [0,0], "f": 0, "t": 0, "d": [6,449], "a": 1 },
+ { "px": [16,160], "src": [0,0], "f": 0, "t": 0, "d": [6,451], "a": 1 },
+ { "px": [48,160], "src": [16,0], "f": 0, "t": 1, "d": [6,453], "a": 1 },
+ { "px": [80,160], "src": [0,0], "f": 0, "t": 0, "d": [6,455], "a": 1 },
+ { "px": [112,160], "src": [16,0], "f": 0, "t": 1, "d": [6,457], "a": 1 },
+ { "px": [144,160], "src": [0,0], "f": 0, "t": 0, "d": [6,459], "a": 1 },
+ { "px": [592,160], "src": [16,0], "f": 0, "t": 1, "d": [6,487], "a": 1 },
+ { "px": [624,160], "src": [16,0], "f": 0, "t": 1, "d": [6,489], "a": 1 },
+ { "px": [656,160], "src": [16,0], "f": 0, "t": 1, "d": [6,491], "a": 1 },
+ { "px": [688,160], "src": [0,0], "f": 0, "t": 0, "d": [6,493], "a": 1 },
+ { "px": [0,176], "src": [16,0], "f": 0, "t": 1, "d": [6,495], "a": 1 },
+ { "px": [64,176], "src": [0,0], "f": 0, "t": 0, "d": [6,499], "a": 1 },
+ { "px": [96,176], "src": [0,0], "f": 0, "t": 0, "d": [6,501], "a": 1 },
+ { "px": [128,176], "src": [0,0], "f": 0, "t": 0, "d": [6,503], "a": 1 },
+ { "px": [576,176], "src": [16,0], "f": 0, "t": 1, "d": [6,531], "a": 1 },
+ { "px": [608,176], "src": [0,0], "f": 0, "t": 0, "d": [6,533], "a": 1 },
+ { "px": [640,176], "src": [16,0], "f": 0, "t": 1, "d": [6,535], "a": 1 },
+ { "px": [672,176], "src": [0,0], "f": 0, "t": 0, "d": [6,537], "a": 1 },
+ { "px": [704,176], "src": [0,0], "f": 0, "t": 0, "d": [6,539], "a": 1 },
+ { "px": [16,192], "src": [16,0], "f": 0, "t": 1, "d": [6,541], "a": 1 },
+ { "px": [48,192], "src": [0,0], "f": 0, "t": 0, "d": [6,543], "a": 1 },
+ { "px": [80,192], "src": [0,0], "f": 0, "t": 0, "d": [6,545], "a": 1 },
+ { "px": [112,192], "src": [0,0], "f": 0, "t": 0, "d": [6,547], "a": 1 },
+ { "px": [144,192], "src": [0,0], "f": 0, "t": 0, "d": [6,549], "a": 1 },
+ { "px": [592,192], "src": [16,0], "f": 0, "t": 1, "d": [6,577], "a": 1 },
+ { "px": [624,192], "src": [0,0], "f": 0, "t": 0, "d": [6,579], "a": 1 },
+ { "px": [656,192], "src": [16,0], "f": 0, "t": 1, "d": [6,581], "a": 1 },
+ { "px": [688,192], "src": [16,0], "f": 0, "t": 1, "d": [6,583], "a": 1 },
+ { "px": [0,208], "src": [16,0], "f": 0, "t": 1, "d": [6,585], "a": 1 },
+ { "px": [32,208], "src": [0,0], "f": 0, "t": 0, "d": [6,587], "a": 1 },
+ { "px": [64,208], "src": [16,0], "f": 0, "t": 1, "d": [6,589], "a": 1 },
+ { "px": [128,208], "src": [16,0], "f": 0, "t": 1, "d": [6,593], "a": 1 },
+ { "px": [576,208], "src": [16,0], "f": 0, "t": 1, "d": [6,621], "a": 1 },
+ { "px": [640,208], "src": [0,0], "f": 0, "t": 0, "d": [6,625], "a": 1 },
+ { "px": [704,208], "src": [0,0], "f": 0, "t": 0, "d": [6,629], "a": 1 },
+ { "px": [16,224], "src": [16,0], "f": 0, "t": 1, "d": [6,631], "a": 1 },
+ { "px": [48,224], "src": [16,0], "f": 0, "t": 1, "d": [6,633], "a": 1 },
+ { "px": [80,224], "src": [0,0], "f": 0, "t": 0, "d": [6,635], "a": 1 },
+ { "px": [112,224], "src": [16,0], "f": 0, "t": 1, "d": [6,637], "a": 1 },
+ { "px": [144,224], "src": [0,0], "f": 0, "t": 0, "d": [6,639], "a": 1 },
+ { "px": [368,224], "src": [16,0], "f": 0, "t": 1, "d": [6,653], "a": 1 },
+ { "px": [400,224], "src": [16,0], "f": 0, "t": 1, "d": [6,655], "a": 1 },
+ { "px": [592,224], "src": [0,0], "f": 0, "t": 0, "d": [6,667], "a": 1 },
+ { "px": [624,224], "src": [16,0], "f": 0, "t": 1, "d": [6,669], "a": 1 },
+ { "px": [656,224], "src": [16,0], "f": 0, "t": 1, "d": [6,671], "a": 1 },
+ { "px": [688,224], "src": [0,0], "f": 0, "t": 0, "d": [6,673], "a": 1 },
+ { "px": [0,240], "src": [0,0], "f": 0, "t": 0, "d": [6,675], "a": 1 },
+ { "px": [64,240], "src": [0,0], "f": 0, "t": 0, "d": [6,679], "a": 1 },
+ { "px": [96,240], "src": [16,0], "f": 0, "t": 1, "d": [6,681], "a": 1 },
+ { "px": [128,240], "src": [0,0], "f": 0, "t": 0, "d": [6,683], "a": 1 },
+ { "px": [384,240], "src": [16,0], "f": 0, "t": 1, "d": [6,699], "a": 1 },
+ { "px": [576,240], "src": [0,0], "f": 0, "t": 0, "d": [6,711], "a": 1 },
+ { "px": [608,240], "src": [16,0], "f": 0, "t": 1, "d": [6,713], "a": 1 },
+ { "px": [640,240], "src": [0,0], "f": 0, "t": 0, "d": [6,715], "a": 1 },
+ { "px": [672,240], "src": [0,0], "f": 0, "t": 0, "d": [6,717], "a": 1 },
+ { "px": [704,240], "src": [0,0], "f": 0, "t": 0, "d": [6,719], "a": 1 },
+ { "px": [16,256], "src": [16,0], "f": 0, "t": 1, "d": [6,721], "a": 1 },
+ { "px": [48,256], "src": [0,0], "f": 0, "t": 0, "d": [6,723], "a": 1 },
+ { "px": [80,256], "src": [16,0], "f": 0, "t": 1, "d": [6,725], "a": 1 },
+ { "px": [112,256], "src": [16,0], "f": 0, "t": 1, "d": [6,727], "a": 1 },
+ { "px": [144,256], "src": [0,0], "f": 0, "t": 0, "d": [6,729], "a": 1 },
+ { "px": [240,256], "src": [0,0], "f": 0, "t": 0, "d": [6,735], "a": 1 },
+ { "px": [272,256], "src": [16,0], "f": 0, "t": 1, "d": [6,737], "a": 1 },
+ { "px": [304,256], "src": [0,0], "f": 0, "t": 0, "d": [6,739], "a": 1 },
+ { "px": [336,256], "src": [16,0], "f": 0, "t": 1, "d": [6,741], "a": 1 },
+ { "px": [368,256], "src": [0,0], "f": 0, "t": 0, "d": [6,743], "a": 1 },
+ { "px": [400,256], "src": [16,0], "f": 0, "t": 1, "d": [6,745], "a": 1 },
+ { "px": [432,256], "src": [16,0], "f": 0, "t": 1, "d": [6,747], "a": 1 },
+ { "px": [464,256], "src": [0,0], "f": 0, "t": 0, "d": [6,749], "a": 1 },
+ { "px": [560,256], "src": [0,0], "f": 0, "t": 0, "d": [6,755], "a": 1 },
+ { "px": [592,256], "src": [0,0], "f": 0, "t": 0, "d": [6,757], "a": 1 },
+ { "px": [624,256], "src": [16,0], "f": 0, "t": 1, "d": [6,759], "a": 1 },
+ { "px": [656,256], "src": [16,0], "f": 0, "t": 1, "d": [6,761], "a": 1 },
+ { "px": [688,256], "src": [16,0], "f": 0, "t": 1, "d": [6,763], "a": 1 },
+ { "px": [0,272], "src": [0,0], "f": 0, "t": 0, "d": [6,765], "a": 1 },
+ { "px": [64,272], "src": [0,0], "f": 0, "t": 0, "d": [6,769], "a": 1 },
+ { "px": [128,272], "src": [16,0], "f": 0, "t": 1, "d": [6,773], "a": 1 },
+ { "px": [160,272], "src": [16,0], "f": 0, "t": 1, "d": [6,775], "a": 1 },
+ { "px": [192,272], "src": [0,0], "f": 0, "t": 0, "d": [6,777], "a": 1 },
+ { "px": [256,272], "src": [16,0], "f": 0, "t": 1, "d": [6,781], "a": 1 },
+ { "px": [288,272], "src": [0,0], "f": 0, "t": 0, "d": [6,783], "a": 1 },
+ { "px": [320,272], "src": [16,0], "f": 0, "t": 1, "d": [6,785], "a": 1 },
+ { "px": [352,272], "src": [0,0], "f": 0, "t": 0, "d": [6,787], "a": 1 },
+ { "px": [384,272], "src": [16,0], "f": 0, "t": 1, "d": [6,789], "a": 1 },
+ { "px": [448,272], "src": [16,0], "f": 0, "t": 1, "d": [6,793], "a": 1 },
+ { "px": [544,272], "src": [16,0], "f": 0, "t": 1, "d": [6,799], "a": 1 },
+ { "px": [576,272], "src": [16,0], "f": 0, "t": 1, "d": [6,801], "a": 1 },
+ { "px": [640,272], "src": [0,0], "f": 0, "t": 0, "d": [6,805], "a": 1 },
+ { "px": [672,272], "src": [16,0], "f": 0, "t": 1, "d": [6,807], "a": 1 },
+ { "px": [704,272], "src": [16,0], "f": 0, "t": 1, "d": [6,809], "a": 1 },
+ { "px": [16,288], "src": [16,0], "f": 0, "t": 1, "d": [6,811], "a": 1 },
+ { "px": [48,288], "src": [16,0], "f": 0, "t": 1, "d": [6,813], "a": 1 },
+ { "px": [80,288], "src": [0,0], "f": 0, "t": 0, "d": [6,815], "a": 1 },
+ { "px": [112,288], "src": [16,0], "f": 0, "t": 1, "d": [6,817], "a": 1 },
+ { "px": [144,288], "src": [16,0], "f": 0, "t": 1, "d": [6,819], "a": 1 },
+ { "px": [240,288], "src": [16,0], "f": 0, "t": 1, "d": [6,825], "a": 1 },
+ { "px": [272,288], "src": [0,0], "f": 0, "t": 0, "d": [6,827], "a": 1 },
+ { "px": [304,288], "src": [16,0], "f": 0, "t": 1, "d": [6,829], "a": 1 },
+ { "px": [336,288], "src": [0,0], "f": 0, "t": 0, "d": [6,831], "a": 1 },
+ { "px": [368,288], "src": [0,0], "f": 0, "t": 0, "d": [6,833], "a": 1 },
+ { "px": [400,288], "src": [0,0], "f": 0, "t": 0, "d": [6,835], "a": 1 },
+ { "px": [432,288], "src": [0,0], "f": 0, "t": 0, "d": [6,837], "a": 1 },
+ { "px": [464,288], "src": [0,0], "f": 0, "t": 0, "d": [6,839], "a": 1 },
+ { "px": [592,288], "src": [16,0], "f": 0, "t": 1, "d": [6,847], "a": 1 },
+ { "px": [624,288], "src": [0,0], "f": 0, "t": 0, "d": [6,849], "a": 1 },
+ { "px": [656,288], "src": [16,0], "f": 0, "t": 1, "d": [6,851], "a": 1 },
+ { "px": [688,288], "src": [0,0], "f": 0, "t": 0, "d": [6,853], "a": 1 },
+ { "px": [0,304], "src": [0,0], "f": 0, "t": 0, "d": [6,855], "a": 1 },
+ { "px": [64,304], "src": [16,0], "f": 0, "t": 1, "d": [6,859], "a": 1 },
+ { "px": [96,304], "src": [0,0], "f": 0, "t": 0, "d": [6,861], "a": 1 },
+ { "px": [128,304], "src": [16,0], "f": 0, "t": 1, "d": [6,863], "a": 1 },
+ { "px": [256,304], "src": [16,0], "f": 0, "t": 1, "d": [6,871], "a": 1 },
+ { "px": [384,304], "src": [16,0], "f": 0, "t": 1, "d": [6,879], "a": 1 },
+ { "px": [448,304], "src": [16,0], "f": 0, "t": 1, "d": [6,883], "a": 1 },
+ { "px": [576,304], "src": [0,0], "f": 0, "t": 0, "d": [6,891], "a": 1 },
+ { "px": [608,304], "src": [0,0], "f": 0, "t": 0, "d": [6,893], "a": 1 },
+ { "px": [640,304], "src": [0,0], "f": 0, "t": 0, "d": [6,895], "a": 1 },
+ { "px": [672,304], "src": [16,0], "f": 0, "t": 1, "d": [6,897], "a": 1 },
+ { "px": [704,304], "src": [0,0], "f": 0, "t": 0, "d": [6,899], "a": 1 },
+ { "px": [16,320], "src": [16,0], "f": 0, "t": 1, "d": [6,901], "a": 1 },
+ { "px": [48,320], "src": [16,0], "f": 0, "t": 1, "d": [6,903], "a": 1 },
+ { "px": [80,320], "src": [0,0], "f": 0, "t": 0, "d": [6,905], "a": 1 },
+ { "px": [112,320], "src": [16,0], "f": 0, "t": 1, "d": [6,907], "a": 1 },
+ { "px": [144,320], "src": [16,0], "f": 0, "t": 1, "d": [6,909], "a": 1 },
+ { "px": [240,320], "src": [0,0], "f": 0, "t": 0, "d": [6,915], "a": 1 },
+ { "px": [272,320], "src": [16,0], "f": 0, "t": 1, "d": [6,917], "a": 1 },
+ { "px": [304,320], "src": [16,0], "f": 0, "t": 1, "d": [6,919], "a": 1 },
+ { "px": [336,320], "src": [0,0], "f": 0, "t": 0, "d": [6,921], "a": 1 },
+ { "px": [368,320], "src": [16,0], "f": 0, "t": 1, "d": [6,923], "a": 1 },
+ { "px": [400,320], "src": [16,0], "f": 0, "t": 1, "d": [6,925], "a": 1 },
+ { "px": [432,320], "src": [0,0], "f": 0, "t": 0, "d": [6,927], "a": 1 },
+ { "px": [464,320], "src": [16,0], "f": 0, "t": 1, "d": [6,929], "a": 1 },
+ { "px": [496,320], "src": [16,0], "f": 0, "t": 1, "d": [6,931], "a": 1 },
+ { "px": [592,320], "src": [0,0], "f": 0, "t": 0, "d": [6,937], "a": 1 },
+ { "px": [624,320], "src": [16,0], "f": 0, "t": 1, "d": [6,939], "a": 1 },
+ { "px": [656,320], "src": [0,0], "f": 0, "t": 0, "d": [6,941], "a": 1 },
+ { "px": [688,320], "src": [0,0], "f": 0, "t": 0, "d": [6,943], "a": 1 },
+ { "px": [0,336], "src": [0,0], "f": 0, "t": 0, "d": [6,945], "a": 1 },
+ { "px": [64,336], "src": [16,0], "f": 0, "t": 1, "d": [6,949], "a": 1 },
+ { "px": [128,336], "src": [0,0], "f": 0, "t": 0, "d": [6,953], "a": 1 },
+ { "px": [224,336], "src": [16,0], "f": 0, "t": 1, "d": [6,959], "a": 1 },
+ { "px": [256,336], "src": [16,0], "f": 0, "t": 1, "d": [6,961], "a": 1 },
+ { "px": [288,336], "src": [16,0], "f": 0, "t": 1, "d": [6,963], "a": 1 },
+ { "px": [320,336], "src": [0,0], "f": 0, "t": 0, "d": [6,965], "a": 1 },
+ { "px": [352,336], "src": [0,0], "f": 0, "t": 0, "d": [6,967], "a": 1 },
+ { "px": [384,336], "src": [0,0], "f": 0, "t": 0, "d": [6,969], "a": 1 },
+ { "px": [416,336], "src": [16,0], "f": 0, "t": 1, "d": [6,971], "a": 1 },
+ { "px": [448,336], "src": [16,0], "f": 0, "t": 1, "d": [6,973], "a": 1 },
+ { "px": [480,336], "src": [0,0], "f": 0, "t": 0, "d": [6,975], "a": 1 },
+ { "px": [576,336], "src": [0,0], "f": 0, "t": 0, "d": [6,981], "a": 1 },
+ { "px": [640,336], "src": [16,0], "f": 0, "t": 1, "d": [6,985], "a": 1 },
+ { "px": [704,336], "src": [0,0], "f": 0, "t": 0, "d": [6,989], "a": 1 },
+ { "px": [16,352], "src": [0,0], "f": 0, "t": 0, "d": [6,991], "a": 1 },
+ { "px": [48,352], "src": [0,0], "f": 0, "t": 0, "d": [6,993], "a": 1 },
+ { "px": [80,352], "src": [0,0], "f": 0, "t": 0, "d": [6,995], "a": 1 },
+ { "px": [112,352], "src": [0,0], "f": 0, "t": 0, "d": [6,997], "a": 1 },
+ { "px": [144,352], "src": [0,0], "f": 0, "t": 0, "d": [6,999], "a": 1 },
+ { "px": [272,352], "src": [0,0], "f": 0, "t": 0, "d": [6,1007], "a": 1 },
+ { "px": [336,352], "src": [16,0], "f": 0, "t": 1, "d": [6,1011], "a": 1 },
+ { "px": [592,352], "src": [16,0], "f": 0, "t": 1, "d": [6,1027], "a": 1 },
+ { "px": [624,352], "src": [16,0], "f": 0, "t": 1, "d": [6,1029], "a": 1 },
+ { "px": [656,352], "src": [0,0], "f": 0, "t": 0, "d": [6,1031], "a": 1 },
+ { "px": [688,352], "src": [0,0], "f": 0, "t": 0, "d": [6,1033], "a": 1 },
+ { "px": [0,368], "src": [16,0], "f": 0, "t": 1, "d": [6,1035], "a": 1 },
+ { "px": [32,368], "src": [16,0], "f": 0, "t": 1, "d": [6,1037], "a": 1 },
+ { "px": [64,368], "src": [16,0], "f": 0, "t": 1, "d": [6,1039], "a": 1 },
+ { "px": [128,368], "src": [16,0], "f": 0, "t": 1, "d": [6,1043], "a": 1 },
+ { "px": [288,368], "src": [0,0], "f": 0, "t": 0, "d": [6,1053], "a": 1 },
+ { "px": [384,368], "src": [16,0], "f": 0, "t": 1, "d": [6,1059], "a": 1 },
+ { "px": [544,368], "src": [16,0], "f": 0, "t": 1, "d": [6,1069], "a": 1 },
+ { "px": [576,368], "src": [0,0], "f": 0, "t": 0, "d": [6,1071], "a": 1 },
+ { "px": [640,368], "src": [0,0], "f": 0, "t": 0, "d": [6,1075], "a": 1 },
+ { "px": [672,368], "src": [16,0], "f": 0, "t": 1, "d": [6,1077], "a": 1 },
+ { "px": [704,368], "src": [0,0], "f": 0, "t": 0, "d": [6,1079], "a": 1 },
+ { "px": [16,384], "src": [16,0], "f": 0, "t": 1, "d": [6,1081], "a": 1 },
+ { "px": [48,384], "src": [16,0], "f": 0, "t": 1, "d": [6,1083], "a": 1 },
+ { "px": [80,384], "src": [0,0], "f": 0, "t": 0, "d": [6,1085], "a": 1 },
+ { "px": [112,384], "src": [0,0], "f": 0, "t": 0, "d": [6,1087], "a": 1 },
+ { "px": [144,384], "src": [0,0], "f": 0, "t": 0, "d": [6,1089], "a": 1 },
+ { "px": [272,384], "src": [0,0], "f": 0, "t": 0, "d": [6,1097], "a": 1 },
+ { "px": [304,384], "src": [16,0], "f": 0, "t": 1, "d": [6,1099], "a": 1 },
+ { "px": [336,384], "src": [0,0], "f": 0, "t": 0, "d": [6,1101], "a": 1 },
+ { "px": [368,384], "src": [16,0], "f": 0, "t": 1, "d": [6,1103], "a": 1 },
+ { "px": [560,384], "src": [0,0], "f": 0, "t": 0, "d": [6,1115], "a": 1 },
+ { "px": [592,384], "src": [0,0], "f": 0, "t": 0, "d": [6,1117], "a": 1 },
+ { "px": [624,384], "src": [0,0], "f": 0, "t": 0, "d": [6,1119], "a": 1 },
+ { "px": [656,384], "src": [16,0], "f": 0, "t": 1, "d": [6,1121], "a": 1 },
+ { "px": [688,384], "src": [0,0], "f": 0, "t": 0, "d": [6,1123], "a": 1 },
+ { "px": [0,400], "src": [0,0], "f": 0, "t": 0, "d": [6,1125], "a": 1 },
+ { "px": [64,400], "src": [16,0], "f": 0, "t": 1, "d": [6,1129], "a": 1 },
+ { "px": [128,400], "src": [0,0], "f": 0, "t": 0, "d": [6,1133], "a": 1 },
+ { "px": [160,400], "src": [0,0], "f": 0, "t": 0, "d": [6,1135], "a": 1 },
+ { "px": [192,400], "src": [0,0], "f": 0, "t": 0, "d": [6,1137], "a": 1 },
+ { "px": [224,400], "src": [16,0], "f": 0, "t": 1, "d": [6,1139], "a": 1 },
+ { "px": [544,400], "src": [16,0], "f": 0, "t": 1, "d": [6,1159], "a": 1 },
+ { "px": [576,400], "src": [16,0], "f": 0, "t": 1, "d": [6,1161], "a": 1 },
+ { "px": [640,400], "src": [16,0], "f": 0, "t": 1, "d": [6,1165], "a": 1 },
+ { "px": [704,400], "src": [16,0], "f": 0, "t": 1, "d": [6,1169], "a": 1 },
+ { "px": [16,416], "src": [0,0], "f": 0, "t": 0, "d": [6,1171], "a": 1 },
+ { "px": [48,416], "src": [0,0], "f": 0, "t": 0, "d": [6,1173], "a": 1 },
+ { "px": [80,416], "src": [0,0], "f": 0, "t": 0, "d": [6,1175], "a": 1 },
+ { "px": [112,416], "src": [0,0], "f": 0, "t": 0, "d": [6,1177], "a": 1 },
+ { "px": [144,416], "src": [0,0], "f": 0, "t": 0, "d": [6,1179], "a": 1 },
+ { "px": [176,416], "src": [0,0], "f": 0, "t": 0, "d": [6,1181], "a": 1 },
+ { "px": [208,416], "src": [16,0], "f": 0, "t": 1, "d": [6,1183], "a": 1 },
+ { "px": [560,416], "src": [0,0], "f": 0, "t": 0, "d": [6,1205], "a": 1 },
+ { "px": [592,416], "src": [0,0], "f": 0, "t": 0, "d": [6,1207], "a": 1 },
+ { "px": [624,416], "src": [0,0], "f": 0, "t": 0, "d": [6,1209], "a": 1 },
+ { "px": [656,416], "src": [0,0], "f": 0, "t": 0, "d": [6,1211], "a": 1 },
+ { "px": [688,416], "src": [16,0], "f": 0, "t": 1, "d": [6,1213], "a": 1 },
+ { "px": [0,432], "src": [16,0], "f": 0, "t": 1, "d": [6,1215], "a": 1 },
+ { "px": [32,432], "src": [0,0], "f": 0, "t": 0, "d": [6,1217], "a": 1 },
+ { "px": [64,432], "src": [0,0], "f": 0, "t": 0, "d": [6,1219], "a": 1 },
+ { "px": [96,432], "src": [16,0], "f": 0, "t": 1, "d": [6,1221], "a": 1 },
+ { "px": [128,432], "src": [16,0], "f": 0, "t": 1, "d": [6,1223], "a": 1 },
+ { "px": [192,432], "src": [16,0], "f": 0, "t": 1, "d": [6,1227], "a": 1 },
+ { "px": [256,432], "src": [0,0], "f": 0, "t": 0, "d": [6,1231], "a": 1 },
+ { "px": [320,432], "src": [16,0], "f": 0, "t": 1, "d": [6,1235], "a": 1 },
+ { "px": [352,432], "src": [16,0], "f": 0, "t": 1, "d": [6,1237], "a": 1 },
+ { "px": [384,432], "src": [16,0], "f": 0, "t": 1, "d": [6,1239], "a": 1 },
+ { "px": [448,432], "src": [0,0], "f": 0, "t": 0, "d": [6,1243], "a": 1 },
+ { "px": [512,432], "src": [16,0], "f": 0, "t": 1, "d": [6,1247], "a": 1 },
+ { "px": [544,432], "src": [0,0], "f": 0, "t": 0, "d": [6,1249], "a": 1 },
+ { "px": [576,432], "src": [0,0], "f": 0, "t": 0, "d": [6,1251], "a": 1 },
+ { "px": [640,432], "src": [16,0], "f": 0, "t": 1, "d": [6,1255], "a": 1 },
+ { "px": [672,432], "src": [0,0], "f": 0, "t": 0, "d": [6,1257], "a": 1 },
+ { "px": [704,432], "src": [0,0], "f": 0, "t": 0, "d": [6,1259], "a": 1 },
+ { "px": [16,448], "src": [16,0], "f": 0, "t": 1, "d": [6,1261], "a": 1 },
+ { "px": [48,448], "src": [16,0], "f": 0, "t": 1, "d": [6,1263], "a": 1 },
+ { "px": [80,448], "src": [0,0], "f": 0, "t": 0, "d": [6,1265], "a": 1 },
+ { "px": [112,448], "src": [16,0], "f": 0, "t": 1, "d": [6,1267], "a": 1 },
+ { "px": [144,448], "src": [16,0], "f": 0, "t": 1, "d": [6,1269], "a": 1 },
+ { "px": [176,448], "src": [0,0], "f": 0, "t": 0, "d": [6,1271], "a": 1 },
+ { "px": [208,448], "src": [0,0], "f": 0, "t": 0, "d": [6,1273], "a": 1 },
+ { "px": [240,448], "src": [0,0], "f": 0, "t": 0, "d": [6,1275], "a": 1 },
+ { "px": [272,448], "src": [0,0], "f": 0, "t": 0, "d": [6,1277], "a": 1 },
+ { "px": [304,448], "src": [0,0], "f": 0, "t": 0, "d": [6,1279], "a": 1 },
+ { "px": [336,448], "src": [16,0], "f": 0, "t": 1, "d": [6,1281], "a": 1 },
+ { "px": [368,448], "src": [0,0], "f": 0, "t": 0, "d": [6,1283], "a": 1 },
+ { "px": [400,448], "src": [16,0], "f": 0, "t": 1, "d": [6,1285], "a": 1 },
+ { "px": [432,448], "src": [0,0], "f": 0, "t": 0, "d": [6,1287], "a": 1 },
+ { "px": [464,448], "src": [16,0], "f": 0, "t": 1, "d": [6,1289], "a": 1 },
+ { "px": [496,448], "src": [16,0], "f": 0, "t": 1, "d": [6,1291], "a": 1 },
+ { "px": [528,448], "src": [0,0], "f": 0, "t": 0, "d": [6,1293], "a": 1 },
+ { "px": [560,448], "src": [0,0], "f": 0, "t": 0, "d": [6,1295], "a": 1 },
+ { "px": [592,448], "src": [0,0], "f": 0, "t": 0, "d": [6,1297], "a": 1 },
+ { "px": [624,448], "src": [0,0], "f": 0, "t": 0, "d": [6,1299], "a": 1 },
+ { "px": [656,448], "src": [16,0], "f": 0, "t": 1, "d": [6,1301], "a": 1 },
+ { "px": [688,448], "src": [16,0], "f": 0, "t": 1, "d": [6,1303], "a": 1 },
+ { "px": [0,464], "src": [16,0], "f": 0, "t": 1, "d": [6,1305], "a": 1 },
+ { "px": [32,464], "src": [16,0], "f": 0, "t": 1, "d": [6,1307], "a": 1 },
+ { "px": [64,464], "src": [0,0], "f": 0, "t": 0, "d": [6,1309], "a": 1 },
+ { "px": [128,464], "src": [16,0], "f": 0, "t": 1, "d": [6,1313], "a": 1 },
+ { "px": [160,464], "src": [16,0], "f": 0, "t": 1, "d": [6,1315], "a": 1 },
+ { "px": [192,464], "src": [16,0], "f": 0, "t": 1, "d": [6,1317], "a": 1 },
+ { "px": [256,464], "src": [0,0], "f": 0, "t": 0, "d": [6,1321], "a": 1 },
+ { "px": [288,464], "src": [16,0], "f": 0, "t": 1, "d": [6,1323], "a": 1 },
+ { "px": [320,464], "src": [0,0], "f": 0, "t": 0, "d": [6,1325], "a": 1 },
+ { "px": [352,464], "src": [0,0], "f": 0, "t": 0, "d": [6,1327], "a": 1 },
+ { "px": [384,464], "src": [16,0], "f": 0, "t": 1, "d": [6,1329], "a": 1 },
+ { "px": [416,464], "src": [16,0], "f": 0, "t": 1, "d": [6,1331], "a": 1 },
+ { "px": [448,464], "src": [16,0], "f": 0, "t": 1, "d": [6,1333], "a": 1 },
+ { "px": [480,464], "src": [16,0], "f": 0, "t": 1, "d": [6,1335], "a": 1 },
+ { "px": [512,464], "src": [0,0], "f": 0, "t": 0, "d": [6,1337], "a": 1 },
+ { "px": [576,464], "src": [0,0], "f": 0, "t": 0, "d": [6,1341], "a": 1 },
+ { "px": [640,464], "src": [16,0], "f": 0, "t": 1, "d": [6,1345], "a": 1 },
+ { "px": [672,464], "src": [0,0], "f": 0, "t": 0, "d": [6,1347], "a": 1 },
+ { "px": [704,464], "src": [0,0], "f": 0, "t": 0, "d": [6,1349], "a": 1 },
+ { "px": [16,480], "src": [0,0], "f": 0, "t": 0, "d": [6,1351], "a": 1 },
+ { "px": [48,480], "src": [16,0], "f": 0, "t": 1, "d": [6,1353], "a": 1 },
+ { "px": [80,480], "src": [0,0], "f": 0, "t": 0, "d": [6,1355], "a": 1 },
+ { "px": [112,480], "src": [0,0], "f": 0, "t": 0, "d": [6,1357], "a": 1 },
+ { "px": [144,480], "src": [16,0], "f": 0, "t": 1, "d": [6,1359], "a": 1 },
+ { "px": [176,480], "src": [0,0], "f": 0, "t": 0, "d": [6,1361], "a": 1 },
+ { "px": [208,480], "src": [16,0], "f": 0, "t": 1, "d": [6,1363], "a": 1 },
+ { "px": [240,480], "src": [16,0], "f": 0, "t": 1, "d": [6,1365], "a": 1 },
+ { "px": [272,480], "src": [16,0], "f": 0, "t": 1, "d": [6,1367], "a": 1 },
+ { "px": [304,480], "src": [16,0], "f": 0, "t": 1, "d": [6,1369], "a": 1 },
+ { "px": [336,480], "src": [16,0], "f": 0, "t": 1, "d": [6,1371], "a": 1 },
+ { "px": [368,480], "src": [0,0], "f": 0, "t": 0, "d": [6,1373], "a": 1 },
+ { "px": [400,480], "src": [16,0], "f": 0, "t": 1, "d": [6,1375], "a": 1 },
+ { "px": [432,480], "src": [0,0], "f": 0, "t": 0, "d": [6,1377], "a": 1 },
+ { "px": [464,480], "src": [16,0], "f": 0, "t": 1, "d": [6,1379], "a": 1 },
+ { "px": [496,480], "src": [0,0], "f": 0, "t": 0, "d": [6,1381], "a": 1 },
+ { "px": [528,480], "src": [16,0], "f": 0, "t": 1, "d": [6,1383], "a": 1 },
+ { "px": [560,480], "src": [16,0], "f": 0, "t": 1, "d": [6,1385], "a": 1 },
+ { "px": [592,480], "src": [16,0], "f": 0, "t": 1, "d": [6,1387], "a": 1 },
+ { "px": [624,480], "src": [16,0], "f": 0, "t": 1, "d": [6,1389], "a": 1 },
+ { "px": [656,480], "src": [16,0], "f": 0, "t": 1, "d": [6,1391], "a": 1 },
+ { "px": [688,480], "src": [0,0], "f": 0, "t": 0, "d": [6,1393], "a": 1 },
+ { "px": [0,496], "src": [0,0], "f": 0, "t": 0, "d": [6,1395], "a": 1 },
+ { "px": [64,496], "src": [0,0], "f": 0, "t": 0, "d": [6,1399], "a": 1 },
+ { "px": [128,496], "src": [16,0], "f": 0, "t": 1, "d": [6,1403], "a": 1 },
+ { "px": [192,496], "src": [0,0], "f": 0, "t": 0, "d": [6,1407], "a": 1 },
+ { "px": [224,496], "src": [0,0], "f": 0, "t": 0, "d": [6,1409], "a": 1 },
+ { "px": [256,496], "src": [16,0], "f": 0, "t": 1, "d": [6,1411], "a": 1 },
+ { "px": [320,496], "src": [0,0], "f": 0, "t": 0, "d": [6,1415], "a": 1 },
+ { "px": [352,496], "src": [16,0], "f": 0, "t": 1, "d": [6,1417], "a": 1 },
+ { "px": [384,496], "src": [16,0], "f": 0, "t": 1, "d": [6,1419], "a": 1 },
+ { "px": [448,496], "src": [0,0], "f": 0, "t": 0, "d": [6,1423], "a": 1 },
+ { "px": [480,496], "src": [0,0], "f": 0, "t": 0, "d": [6,1425], "a": 1 },
+ { "px": [512,496], "src": [0,0], "f": 0, "t": 0, "d": [6,1427], "a": 1 },
+ { "px": [544,496], "src": [16,0], "f": 0, "t": 1, "d": [6,1429], "a": 1 },
+ { "px": [576,496], "src": [0,0], "f": 0, "t": 0, "d": [6,1431], "a": 1 },
+ { "px": [608,496], "src": [0,0], "f": 0, "t": 0, "d": [6,1433], "a": 1 },
+ { "px": [640,496], "src": [0,0], "f": 0, "t": 0, "d": [6,1435], "a": 1 },
+ { "px": [704,496], "src": [16,0], "f": 0, "t": 1, "d": [6,1439], "a": 1 },
+ { "px": [16,512], "src": [0,0], "f": 0, "t": 0, "d": [6,1441], "a": 1 },
+ { "px": [48,512], "src": [16,0], "f": 0, "t": 1, "d": [6,1443], "a": 1 },
+ { "px": [80,512], "src": [16,0], "f": 0, "t": 1, "d": [6,1445], "a": 1 },
+ { "px": [112,512], "src": [0,0], "f": 0, "t": 0, "d": [6,1447], "a": 1 },
+ { "px": [144,512], "src": [16,0], "f": 0, "t": 1, "d": [6,1449], "a": 1 },
+ { "px": [176,512], "src": [16,0], "f": 0, "t": 1, "d": [6,1451], "a": 1 },
+ { "px": [208,512], "src": [16,0], "f": 0, "t": 1, "d": [6,1453], "a": 1 },
+ { "px": [240,512], "src": [16,0], "f": 0, "t": 1, "d": [6,1455], "a": 1 },
+ { "px": [272,512], "src": [16,0], "f": 0, "t": 1, "d": [6,1457], "a": 1 },
+ { "px": [304,512], "src": [0,0], "f": 0, "t": 0, "d": [6,1459], "a": 1 },
+ { "px": [336,512], "src": [16,0], "f": 0, "t": 1, "d": [6,1461], "a": 1 },
+ { "px": [368,512], "src": [16,0], "f": 0, "t": 1, "d": [6,1463], "a": 1 },
+ { "px": [400,512], "src": [0,0], "f": 0, "t": 0, "d": [6,1465], "a": 1 },
+ { "px": [432,512], "src": [0,0], "f": 0, "t": 0, "d": [6,1467], "a": 1 },
+ { "px": [464,512], "src": [16,0], "f": 0, "t": 1, "d": [6,1469], "a": 1 },
+ { "px": [496,512], "src": [0,0], "f": 0, "t": 0, "d": [6,1471], "a": 1 },
+ { "px": [528,512], "src": [16,0], "f": 0, "t": 1, "d": [6,1473], "a": 1 },
+ { "px": [560,512], "src": [16,0], "f": 0, "t": 1, "d": [6,1475], "a": 1 },
+ { "px": [592,512], "src": [0,0], "f": 0, "t": 0, "d": [6,1477], "a": 1 },
+ { "px": [624,512], "src": [0,0], "f": 0, "t": 0, "d": [6,1479], "a": 1 },
+ { "px": [656,512], "src": [16,0], "f": 0, "t": 1, "d": [6,1481], "a": 1 },
+ { "px": [688,512], "src": [0,0], "f": 0, "t": 0, "d": [6,1483], "a": 1 },
+ { "px": [0,528], "src": [16,0], "f": 0, "t": 1, "d": [6,1485], "a": 1 },
+ { "px": [32,528], "src": [16,0], "f": 0, "t": 1, "d": [6,1487], "a": 1 },
+ { "px": [64,528], "src": [16,0], "f": 0, "t": 1, "d": [6,1489], "a": 1 },
+ { "px": [96,528], "src": [16,0], "f": 0, "t": 1, "d": [6,1491], "a": 1 },
+ { "px": [128,528], "src": [16,0], "f": 0, "t": 1, "d": [6,1493], "a": 1 },
+ { "px": [160,528], "src": [0,0], "f": 0, "t": 0, "d": [6,1495], "a": 1 },
+ { "px": [192,528], "src": [16,0], "f": 0, "t": 1, "d": [6,1497], "a": 1 },
+ { "px": [224,528], "src": [16,0], "f": 0, "t": 1, "d": [6,1499], "a": 1 },
+ { "px": [256,528], "src": [16,0], "f": 0, "t": 1, "d": [6,1501], "a": 1 },
+ { "px": [288,528], "src": [0,0], "f": 0, "t": 0, "d": [6,1503], "a": 1 },
+ { "px": [320,528], "src": [0,0], "f": 0, "t": 0, "d": [6,1505], "a": 1 },
+ { "px": [352,528], "src": [0,0], "f": 0, "t": 0, "d": [6,1507], "a": 1 },
+ { "px": [384,528], "src": [0,0], "f": 0, "t": 0, "d": [6,1509], "a": 1 },
+ { "px": [416,528], "src": [16,0], "f": 0, "t": 1, "d": [6,1511], "a": 1 },
+ { "px": [448,528], "src": [16,0], "f": 0, "t": 1, "d": [6,1513], "a": 1 },
+ { "px": [480,528], "src": [16,0], "f": 0, "t": 1, "d": [6,1515], "a": 1 },
+ { "px": [512,528], "src": [0,0], "f": 0, "t": 0, "d": [6,1517], "a": 1 },
+ { "px": [544,528], "src": [16,0], "f": 0, "t": 1, "d": [6,1519], "a": 1 },
+ { "px": [576,528], "src": [0,0], "f": 0, "t": 0, "d": [6,1521], "a": 1 },
+ { "px": [608,528], "src": [0,0], "f": 0, "t": 0, "d": [6,1523], "a": 1 },
+ { "px": [640,528], "src": [0,0], "f": 0, "t": 0, "d": [6,1525], "a": 1 },
+ { "px": [672,528], "src": [0,0], "f": 0, "t": 0, "d": [6,1527], "a": 1 },
+ { "px": [704,528], "src": [0,0], "f": 0, "t": 0, "d": [6,1529], "a": 1 },
+ { "px": [0,0], "src": [32,0], "f": 0, "t": 2, "d": [12,0], "a": 1 },
+ { "px": [32,0], "src": [32,0], "f": 0, "t": 2, "d": [12,2], "a": 1 },
+ { "px": [96,0], "src": [32,0], "f": 0, "t": 2, "d": [12,6], "a": 1 },
+ { "px": [160,0], "src": [32,0], "f": 0, "t": 2, "d": [12,10], "a": 1 },
+ { "px": [192,0], "src": [32,0], "f": 0, "t": 2, "d": [12,12], "a": 1 },
+ { "px": [224,0], "src": [32,0], "f": 0, "t": 2, "d": [12,14], "a": 1 },
+ { "px": [288,0], "src": [32,0], "f": 0, "t": 2, "d": [12,18], "a": 1 },
+ { "px": [352,0], "src": [32,0], "f": 0, "t": 2, "d": [12,22], "a": 1 },
+ { "px": [416,0], "src": [32,0], "f": 0, "t": 2, "d": [12,26], "a": 1 },
+ { "px": [480,0], "src": [32,0], "f": 0, "t": 2, "d": [12,30], "a": 1 },
+ { "px": [544,0], "src": [32,0], "f": 0, "t": 2, "d": [12,34], "a": 1 },
+ { "px": [576,0], "src": [32,0], "f": 0, "t": 2, "d": [12,36], "a": 1 },
+ { "px": [608,0], "src": [32,0], "f": 0, "t": 2, "d": [12,38], "a": 1 },
+ { "px": [672,0], "src": [32,0], "f": 0, "t": 2, "d": [12,42], "a": 1 },
+ { "px": [704,0], "src": [32,0], "f": 0, "t": 2, "d": [12,44], "a": 1 },
+ { "px": [16,16], "src": [32,0], "f": 0, "t": 2, "d": [12,46], "a": 1 },
+ { "px": [48,16], "src": [32,0], "f": 0, "t": 2, "d": [12,48], "a": 1 },
+ { "px": [80,16], "src": [32,0], "f": 0, "t": 2, "d": [12,50], "a": 1 },
+ { "px": [112,16], "src": [32,0], "f": 0, "t": 2, "d": [12,52], "a": 1 },
+ { "px": [144,16], "src": [32,0], "f": 0, "t": 2, "d": [12,54], "a": 1 },
+ { "px": [176,16], "src": [32,0], "f": 0, "t": 2, "d": [12,56], "a": 1 },
+ { "px": [208,16], "src": [32,0], "f": 0, "t": 2, "d": [12,58], "a": 1 },
+ { "px": [240,16], "src": [32,0], "f": 0, "t": 2, "d": [12,60], "a": 1 },
+ { "px": [272,16], "src": [32,0], "f": 0, "t": 2, "d": [12,62], "a": 1 },
+ { "px": [304,16], "src": [32,0], "f": 0, "t": 2, "d": [12,64], "a": 1 },
+ { "px": [336,16], "src": [32,0], "f": 0, "t": 2, "d": [12,66], "a": 1 },
+ { "px": [368,16], "src": [32,0], "f": 0, "t": 2, "d": [12,68], "a": 1 },
+ { "px": [400,16], "src": [32,0], "f": 0, "t": 2, "d": [12,70], "a": 1 },
+ { "px": [432,16], "src": [32,0], "f": 0, "t": 2, "d": [12,72], "a": 1 },
+ { "px": [464,16], "src": [32,0], "f": 0, "t": 2, "d": [12,74], "a": 1 },
+ { "px": [496,16], "src": [32,0], "f": 0, "t": 2, "d": [12,76], "a": 1 },
+ { "px": [528,16], "src": [32,0], "f": 0, "t": 2, "d": [12,78], "a": 1 },
+ { "px": [560,16], "src": [32,0], "f": 0, "t": 2, "d": [12,80], "a": 1 },
+ { "px": [592,16], "src": [32,0], "f": 0, "t": 2, "d": [12,82], "a": 1 },
+ { "px": [624,16], "src": [32,0], "f": 0, "t": 2, "d": [12,84], "a": 1 },
+ { "px": [656,16], "src": [32,0], "f": 0, "t": 2, "d": [12,86], "a": 1 },
+ { "px": [688,16], "src": [32,0], "f": 0, "t": 2, "d": [12,88], "a": 1 },
+ { "px": [0,32], "src": [32,0], "f": 0, "t": 2, "d": [12,90], "a": 1 },
+ { "px": [32,32], "src": [32,0], "f": 0, "t": 2, "d": [12,92], "a": 1 },
+ { "px": [96,32], "src": [32,0], "f": 0, "t": 2, "d": [12,96], "a": 1 },
+ { "px": [160,32], "src": [32,0], "f": 0, "t": 2, "d": [12,100], "a": 1 },
+ { "px": [224,32], "src": [32,0], "f": 0, "t": 2, "d": [12,104], "a": 1 },
+ { "px": [256,32], "src": [32,0], "f": 0, "t": 2, "d": [12,106], "a": 1 },
+ { "px": [288,32], "src": [32,0], "f": 0, "t": 2, "d": [12,108], "a": 1 },
+ { "px": [352,32], "src": [32,0], "f": 0, "t": 2, "d": [12,112], "a": 1 },
+ { "px": [416,32], "src": [32,0], "f": 0, "t": 2, "d": [12,116], "a": 1 },
+ { "px": [448,32], "src": [32,0], "f": 0, "t": 2, "d": [12,118], "a": 1 },
+ { "px": [480,32], "src": [32,0], "f": 0, "t": 2, "d": [12,120], "a": 1 },
+ { "px": [512,32], "src": [32,0], "f": 0, "t": 2, "d": [12,122], "a": 1 },
+ { "px": [544,32], "src": [32,0], "f": 0, "t": 2, "d": [12,124], "a": 1 },
+ { "px": [576,32], "src": [32,0], "f": 0, "t": 2, "d": [12,126], "a": 1 },
+ { "px": [608,32], "src": [32,0], "f": 0, "t": 2, "d": [12,128], "a": 1 },
+ { "px": [672,32], "src": [32,0], "f": 0, "t": 2, "d": [12,132], "a": 1 },
+ { "px": [704,32], "src": [32,0], "f": 0, "t": 2, "d": [12,134], "a": 1 },
+ { "px": [16,48], "src": [32,0], "f": 0, "t": 2, "d": [12,136], "a": 1 },
+ { "px": [48,48], "src": [32,0], "f": 0, "t": 2, "d": [12,138], "a": 1 },
+ { "px": [80,48], "src": [32,0], "f": 0, "t": 2, "d": [12,140], "a": 1 },
+ { "px": [112,48], "src": [32,0], "f": 0, "t": 2, "d": [12,142], "a": 1 },
+ { "px": [144,48], "src": [32,0], "f": 0, "t": 2, "d": [12,144], "a": 1 },
+ { "px": [176,48], "src": [32,0], "f": 0, "t": 2, "d": [12,146], "a": 1 },
+ { "px": [208,48], "src": [32,0], "f": 0, "t": 2, "d": [12,148], "a": 1 },
+ { "px": [240,48], "src": [32,0], "f": 0, "t": 2, "d": [12,150], "a": 1 },
+ { "px": [272,48], "src": [32,0], "f": 0, "t": 2, "d": [12,152], "a": 1 },
+ { "px": [304,48], "src": [32,0], "f": 0, "t": 2, "d": [12,154], "a": 1 },
+ { "px": [336,48], "src": [32,0], "f": 0, "t": 2, "d": [12,156], "a": 1 },
+ { "px": [368,48], "src": [32,0], "f": 0, "t": 2, "d": [12,158], "a": 1 },
+ { "px": [400,48], "src": [32,0], "f": 0, "t": 2, "d": [12,160], "a": 1 },
+ { "px": [432,48], "src": [32,0], "f": 0, "t": 2, "d": [12,162], "a": 1 },
+ { "px": [464,48], "src": [32,0], "f": 0, "t": 2, "d": [12,164], "a": 1 },
+ { "px": [496,48], "src": [32,0], "f": 0, "t": 2, "d": [12,166], "a": 1 },
+ { "px": [528,48], "src": [32,0], "f": 0, "t": 2, "d": [12,168], "a": 1 },
+ { "px": [560,48], "src": [32,0], "f": 0, "t": 2, "d": [12,170], "a": 1 },
+ { "px": [592,48], "src": [32,0], "f": 0, "t": 2, "d": [12,172], "a": 1 },
+ { "px": [624,48], "src": [32,0], "f": 0, "t": 2, "d": [12,174], "a": 1 },
+ { "px": [656,48], "src": [32,0], "f": 0, "t": 2, "d": [12,176], "a": 1 },
+ { "px": [688,48], "src": [32,0], "f": 0, "t": 2, "d": [12,178], "a": 1 },
+ { "px": [0,64], "src": [32,0], "f": 0, "t": 2, "d": [12,180], "a": 1 },
+ { "px": [32,64], "src": [32,0], "f": 0, "t": 2, "d": [12,182], "a": 1 },
+ { "px": [96,64], "src": [32,0], "f": 0, "t": 2, "d": [12,186], "a": 1 },
+ { "px": [128,64], "src": [32,0], "f": 0, "t": 2, "d": [12,188], "a": 1 },
+ { "px": [160,64], "src": [32,0], "f": 0, "t": 2, "d": [12,190], "a": 1 },
+ { "px": [224,64], "src": [32,0], "f": 0, "t": 2, "d": [12,194], "a": 1 },
+ { "px": [256,64], "src": [32,0], "f": 0, "t": 2, "d": [12,196], "a": 1 },
+ { "px": [288,64], "src": [32,0], "f": 0, "t": 2, "d": [12,198], "a": 1 },
+ { "px": [320,64], "src": [32,0], "f": 0, "t": 2, "d": [12,200], "a": 1 },
+ { "px": [352,64], "src": [32,0], "f": 0, "t": 2, "d": [12,202], "a": 1 },
+ { "px": [384,64], "src": [32,0], "f": 0, "t": 2, "d": [12,204], "a": 1 },
+ { "px": [416,64], "src": [32,0], "f": 0, "t": 2, "d": [12,206], "a": 1 },
+ { "px": [448,64], "src": [32,0], "f": 0, "t": 2, "d": [12,208], "a": 1 },
+ { "px": [480,64], "src": [32,0], "f": 0, "t": 2, "d": [12,210], "a": 1 },
+ { "px": [512,64], "src": [32,0], "f": 0, "t": 2, "d": [12,212], "a": 1 },
+ { "px": [544,64], "src": [32,0], "f": 0, "t": 2, "d": [12,214], "a": 1 },
+ { "px": [576,64], "src": [32,0], "f": 0, "t": 2, "d": [12,216], "a": 1 },
+ { "px": [608,64], "src": [32,0], "f": 0, "t": 2, "d": [12,218], "a": 1 },
+ { "px": [672,64], "src": [32,0], "f": 0, "t": 2, "d": [12,222], "a": 1 },
+ { "px": [704,64], "src": [32,0], "f": 0, "t": 2, "d": [12,224], "a": 1 },
+ { "px": [16,80], "src": [32,0], "f": 0, "t": 2, "d": [12,226], "a": 1 },
+ { "px": [48,80], "src": [32,0], "f": 0, "t": 2, "d": [12,228], "a": 1 },
+ { "px": [80,80], "src": [32,0], "f": 0, "t": 2, "d": [12,230], "a": 1 },
+ { "px": [112,80], "src": [32,0], "f": 0, "t": 2, "d": [12,232], "a": 1 },
+ { "px": [144,80], "src": [32,0], "f": 0, "t": 2, "d": [12,234], "a": 1 },
+ { "px": [176,80], "src": [32,0], "f": 0, "t": 2, "d": [12,236], "a": 1 },
+ { "px": [208,80], "src": [32,0], "f": 0, "t": 2, "d": [12,238], "a": 1 },
+ { "px": [240,80], "src": [32,0], "f": 0, "t": 2, "d": [12,240], "a": 1 },
+ { "px": [272,80], "src": [32,0], "f": 0, "t": 2, "d": [12,242], "a": 1 },
+ { "px": [304,80], "src": [32,0], "f": 0, "t": 2, "d": [12,244], "a": 1 },
+ { "px": [336,80], "src": [32,0], "f": 0, "t": 2, "d": [12,246], "a": 1 },
+ { "px": [368,80], "src": [32,0], "f": 0, "t": 2, "d": [12,248], "a": 1 },
+ { "px": [400,80], "src": [32,0], "f": 0, "t": 2, "d": [12,250], "a": 1 },
+ { "px": [432,80], "src": [32,0], "f": 0, "t": 2, "d": [12,252], "a": 1 },
+ { "px": [464,80], "src": [32,0], "f": 0, "t": 2, "d": [12,254], "a": 1 },
+ { "px": [496,80], "src": [32,0], "f": 0, "t": 2, "d": [12,256], "a": 1 },
+ { "px": [528,80], "src": [32,0], "f": 0, "t": 2, "d": [12,258], "a": 1 },
+ { "px": [560,80], "src": [32,0], "f": 0, "t": 2, "d": [12,260], "a": 1 },
+ { "px": [592,80], "src": [32,0], "f": 0, "t": 2, "d": [12,262], "a": 1 },
+ { "px": [624,80], "src": [32,0], "f": 0, "t": 2, "d": [12,264], "a": 1 },
+ { "px": [656,80], "src": [32,0], "f": 0, "t": 2, "d": [12,266], "a": 1 },
+ { "px": [688,80], "src": [32,0], "f": 0, "t": 2, "d": [12,268], "a": 1 },
+ { "px": [32,96], "src": [32,0], "f": 0, "t": 2, "d": [12,272], "a": 1 },
+ { "px": [64,96], "src": [32,0], "f": 0, "t": 2, "d": [12,274], "a": 1 },
+ { "px": [96,96], "src": [32,0], "f": 0, "t": 2, "d": [12,276], "a": 1 },
+ { "px": [160,96], "src": [32,0], "f": 0, "t": 2, "d": [12,280], "a": 1 },
+ { "px": [192,96], "src": [32,0], "f": 0, "t": 2, "d": [12,282], "a": 1 },
+ { "px": [224,96], "src": [32,0], "f": 0, "t": 2, "d": [12,284], "a": 1 },
+ { "px": [256,96], "src": [32,0], "f": 0, "t": 2, "d": [12,286], "a": 1 },
+ { "px": [288,96], "src": [32,0], "f": 0, "t": 2, "d": [12,288], "a": 1 },
+ { "px": [320,96], "src": [32,0], "f": 0, "t": 2, "d": [12,290], "a": 1 },
+ { "px": [352,96], "src": [32,0], "f": 0, "t": 2, "d": [12,292], "a": 1 },
+ { "px": [384,96], "src": [32,0], "f": 0, "t": 2, "d": [12,294], "a": 1 },
+ { "px": [416,96], "src": [32,0], "f": 0, "t": 2, "d": [12,296], "a": 1 },
+ { "px": [448,96], "src": [32,0], "f": 0, "t": 2, "d": [12,298], "a": 1 },
+ { "px": [480,96], "src": [32,0], "f": 0, "t": 2, "d": [12,300], "a": 1 },
+ { "px": [512,96], "src": [32,0], "f": 0, "t": 2, "d": [12,302], "a": 1 },
+ { "px": [544,96], "src": [32,0], "f": 0, "t": 2, "d": [12,304], "a": 1 },
+ { "px": [608,96], "src": [32,0], "f": 0, "t": 2, "d": [12,308], "a": 1 },
+ { "px": [672,96], "src": [32,0], "f": 0, "t": 2, "d": [12,312], "a": 1 },
+ { "px": [704,96], "src": [32,0], "f": 0, "t": 2, "d": [12,314], "a": 1 },
+ { "px": [16,112], "src": [32,0], "f": 0, "t": 2, "d": [12,316], "a": 1 },
+ { "px": [48,112], "src": [32,0], "f": 0, "t": 2, "d": [12,318], "a": 1 },
+ { "px": [80,112], "src": [32,0], "f": 0, "t": 2, "d": [12,320], "a": 1 },
+ { "px": [112,112], "src": [32,0], "f": 0, "t": 2, "d": [12,322], "a": 1 },
+ { "px": [144,112], "src": [32,0], "f": 0, "t": 2, "d": [12,324], "a": 1 },
+ { "px": [176,112], "src": [32,0], "f": 0, "t": 2, "d": [12,326], "a": 1 },
+ { "px": [208,112], "src": [32,0], "f": 0, "t": 2, "d": [12,328], "a": 1 },
+ { "px": [240,112], "src": [32,0], "f": 0, "t": 2, "d": [12,330], "a": 1 },
+ { "px": [272,112], "src": [32,0], "f": 0, "t": 2, "d": [12,332], "a": 1 },
+ { "px": [304,112], "src": [32,0], "f": 0, "t": 2, "d": [12,334], "a": 1 },
+ { "px": [336,112], "src": [32,0], "f": 0, "t": 2, "d": [12,336], "a": 1 },
+ { "px": [368,112], "src": [32,0], "f": 0, "t": 2, "d": [12,338], "a": 1 },
+ { "px": [400,112], "src": [32,0], "f": 0, "t": 2, "d": [12,340], "a": 1 },
+ { "px": [432,112], "src": [32,0], "f": 0, "t": 2, "d": [12,342], "a": 1 },
+ { "px": [464,112], "src": [32,0], "f": 0, "t": 2, "d": [12,344], "a": 1 },
+ { "px": [496,112], "src": [32,0], "f": 0, "t": 2, "d": [12,346], "a": 1 },
+ { "px": [528,112], "src": [32,0], "f": 0, "t": 2, "d": [12,348], "a": 1 },
+ { "px": [560,112], "src": [32,0], "f": 0, "t": 2, "d": [12,350], "a": 1 },
+ { "px": [592,112], "src": [32,0], "f": 0, "t": 2, "d": [12,352], "a": 1 },
+ { "px": [624,112], "src": [32,0], "f": 0, "t": 2, "d": [12,354], "a": 1 },
+ { "px": [656,112], "src": [32,0], "f": 0, "t": 2, "d": [12,356], "a": 1 },
+ { "px": [688,112], "src": [32,0], "f": 0, "t": 2, "d": [12,358], "a": 1 },
+ { "px": [0,128], "src": [32,0], "f": 0, "t": 2, "d": [12,360], "a": 1 },
+ { "px": [32,128], "src": [32,0], "f": 0, "t": 2, "d": [12,362], "a": 1 },
+ { "px": [96,128], "src": [32,0], "f": 0, "t": 2, "d": [12,366], "a": 1 },
+ { "px": [160,128], "src": [32,0], "f": 0, "t": 2, "d": [12,370], "a": 1 },
+ { "px": [192,128], "src": [32,0], "f": 0, "t": 2, "d": [12,372], "a": 1 },
+ { "px": [224,128], "src": [32,0], "f": 0, "t": 2, "d": [12,374], "a": 1 },
+ { "px": [256,128], "src": [32,0], "f": 0, "t": 2, "d": [12,376], "a": 1 },
+ { "px": [288,128], "src": [32,0], "f": 0, "t": 2, "d": [12,378], "a": 1 },
+ { "px": [320,128], "src": [32,0], "f": 0, "t": 2, "d": [12,380], "a": 1 },
+ { "px": [352,128], "src": [32,0], "f": 0, "t": 2, "d": [12,382], "a": 1 },
+ { "px": [384,128], "src": [32,0], "f": 0, "t": 2, "d": [12,384], "a": 1 },
+ { "px": [416,128], "src": [32,0], "f": 0, "t": 2, "d": [12,386], "a": 1 },
+ { "px": [448,128], "src": [32,0], "f": 0, "t": 2, "d": [12,388], "a": 1 },
+ { "px": [480,128], "src": [32,0], "f": 0, "t": 2, "d": [12,390], "a": 1 },
+ { "px": [512,128], "src": [32,0], "f": 0, "t": 2, "d": [12,392], "a": 1 },
+ { "px": [544,128], "src": [32,0], "f": 0, "t": 2, "d": [12,394], "a": 1 },
+ { "px": [608,128], "src": [32,0], "f": 0, "t": 2, "d": [12,398], "a": 1 },
+ { "px": [640,128], "src": [32,0], "f": 0, "t": 2, "d": [12,400], "a": 1 },
+ { "px": [672,128], "src": [32,0], "f": 0, "t": 2, "d": [12,402], "a": 1 },
+ { "px": [704,128], "src": [32,0], "f": 0, "t": 2, "d": [12,404], "a": 1 },
+ { "px": [16,144], "src": [32,0], "f": 0, "t": 2, "d": [12,406], "a": 1 },
+ { "px": [48,144], "src": [32,0], "f": 0, "t": 2, "d": [12,408], "a": 1 },
+ { "px": [80,144], "src": [32,0], "f": 0, "t": 2, "d": [12,410], "a": 1 },
+ { "px": [112,144], "src": [32,0], "f": 0, "t": 2, "d": [12,412], "a": 1 },
+ { "px": [144,144], "src": [32,0], "f": 0, "t": 2, "d": [12,414], "a": 1 },
+ { "px": [560,144], "src": [32,0], "f": 0, "t": 2, "d": [12,440], "a": 1 },
+ { "px": [592,144], "src": [32,0], "f": 0, "t": 2, "d": [12,442], "a": 1 },
+ { "px": [624,144], "src": [32,0], "f": 0, "t": 2, "d": [12,444], "a": 1 },
+ { "px": [656,144], "src": [32,0], "f": 0, "t": 2, "d": [12,446], "a": 1 },
+ { "px": [688,144], "src": [32,0], "f": 0, "t": 2, "d": [12,448], "a": 1 },
+ { "px": [0,160], "src": [32,0], "f": 0, "t": 2, "d": [12,450], "a": 1 },
+ { "px": [32,160], "src": [32,0], "f": 0, "t": 2, "d": [12,452], "a": 1 },
+ { "px": [96,160], "src": [32,0], "f": 0, "t": 2, "d": [12,456], "a": 1 },
+ { "px": [128,160], "src": [32,0], "f": 0, "t": 2, "d": [12,458], "a": 1 },
+ { "px": [608,160], "src": [32,0], "f": 0, "t": 2, "d": [12,488], "a": 1 },
+ { "px": [672,160], "src": [32,0], "f": 0, "t": 2, "d": [12,492], "a": 1 },
+ { "px": [704,160], "src": [32,0], "f": 0, "t": 2, "d": [12,494], "a": 1 },
+ { "px": [16,176], "src": [32,0], "f": 0, "t": 2, "d": [12,496], "a": 1 },
+ { "px": [48,176], "src": [32,0], "f": 0, "t": 2, "d": [12,498], "a": 1 },
+ { "px": [80,176], "src": [32,0], "f": 0, "t": 2, "d": [12,500], "a": 1 },
+ { "px": [112,176], "src": [32,0], "f": 0, "t": 2, "d": [12,502], "a": 1 },
+ { "px": [144,176], "src": [32,0], "f": 0, "t": 2, "d": [12,504], "a": 1 },
+ { "px": [592,176], "src": [32,0], "f": 0, "t": 2, "d": [12,532], "a": 1 },
+ { "px": [624,176], "src": [32,0], "f": 0, "t": 2, "d": [12,534], "a": 1 },
+ { "px": [656,176], "src": [32,0], "f": 0, "t": 2, "d": [12,536], "a": 1 },
+ { "px": [688,176], "src": [32,0], "f": 0, "t": 2, "d": [12,538], "a": 1 },
+ { "px": [32,192], "src": [32,0], "f": 0, "t": 2, "d": [12,542], "a": 1 },
+ { "px": [96,192], "src": [32,0], "f": 0, "t": 2, "d": [12,546], "a": 1 },
+ { "px": [576,192], "src": [32,0], "f": 0, "t": 2, "d": [12,576], "a": 1 },
+ { "px": [608,192], "src": [32,0], "f": 0, "t": 2, "d": [12,578], "a": 1 },
+ { "px": [672,192], "src": [32,0], "f": 0, "t": 2, "d": [12,582], "a": 1 },
+ { "px": [704,192], "src": [32,0], "f": 0, "t": 2, "d": [12,584], "a": 1 },
+ { "px": [16,208], "src": [32,0], "f": 0, "t": 2, "d": [12,586], "a": 1 },
+ { "px": [48,208], "src": [32,0], "f": 0, "t": 2, "d": [12,588], "a": 1 },
+ { "px": [80,208], "src": [32,0], "f": 0, "t": 2, "d": [12,590], "a": 1 },
+ { "px": [112,208], "src": [32,0], "f": 0, "t": 2, "d": [12,592], "a": 1 },
+ { "px": [144,208], "src": [32,0], "f": 0, "t": 2, "d": [12,594], "a": 1 },
+ { "px": [592,208], "src": [32,0], "f": 0, "t": 2, "d": [12,622], "a": 1 },
+ { "px": [624,208], "src": [32,0], "f": 0, "t": 2, "d": [12,624], "a": 1 },
+ { "px": [656,208], "src": [32,0], "f": 0, "t": 2, "d": [12,626], "a": 1 },
+ { "px": [688,208], "src": [32,0], "f": 0, "t": 2, "d": [12,628], "a": 1 },
+ { "px": [0,224], "src": [32,0], "f": 0, "t": 2, "d": [12,630], "a": 1 },
+ { "px": [32,224], "src": [32,0], "f": 0, "t": 2, "d": [12,632], "a": 1 },
+ { "px": [64,224], "src": [32,0], "f": 0, "t": 2, "d": [12,634], "a": 1 },
+ { "px": [96,224], "src": [32,0], "f": 0, "t": 2, "d": [12,636], "a": 1 },
+ { "px": [128,224], "src": [32,0], "f": 0, "t": 2, "d": [12,638], "a": 1 },
+ { "px": [384,224], "src": [32,0], "f": 0, "t": 2, "d": [12,654], "a": 1 },
+ { "px": [608,224], "src": [32,0], "f": 0, "t": 2, "d": [12,668], "a": 1 },
+ { "px": [640,224], "src": [32,0], "f": 0, "t": 2, "d": [12,670], "a": 1 },
+ { "px": [672,224], "src": [32,0], "f": 0, "t": 2, "d": [12,672], "a": 1 },
+ { "px": [704,224], "src": [32,0], "f": 0, "t": 2, "d": [12,674], "a": 1 },
+ { "px": [16,240], "src": [32,0], "f": 0, "t": 2, "d": [12,676], "a": 1 },
+ { "px": [48,240], "src": [32,0], "f": 0, "t": 2, "d": [12,678], "a": 1 },
+ { "px": [80,240], "src": [32,0], "f": 0, "t": 2, "d": [12,680], "a": 1 },
+ { "px": [112,240], "src": [32,0], "f": 0, "t": 2, "d": [12,682], "a": 1 },
+ { "px": [144,240], "src": [32,0], "f": 0, "t": 2, "d": [12,684], "a": 1 },
+ { "px": [368,240], "src": [32,0], "f": 0, "t": 2, "d": [12,698], "a": 1 },
+ { "px": [400,240], "src": [32,0], "f": 0, "t": 2, "d": [12,700], "a": 1 },
+ { "px": [592,240], "src": [32,0], "f": 0, "t": 2, "d": [12,712], "a": 1 },
+ { "px": [624,240], "src": [32,0], "f": 0, "t": 2, "d": [12,714], "a": 1 },
+ { "px": [656,240], "src": [32,0], "f": 0, "t": 2, "d": [12,716], "a": 1 },
+ { "px": [688,240], "src": [32,0], "f": 0, "t": 2, "d": [12,718], "a": 1 },
+ { "px": [0,256], "src": [32,0], "f": 0, "t": 2, "d": [12,720], "a": 1 },
+ { "px": [32,256], "src": [32,0], "f": 0, "t": 2, "d": [12,722], "a": 1 },
+ { "px": [64,256], "src": [32,0], "f": 0, "t": 2, "d": [12,724], "a": 1 },
+ { "px": [96,256], "src": [32,0], "f": 0, "t": 2, "d": [12,726], "a": 1 },
+ { "px": [128,256], "src": [32,0], "f": 0, "t": 2, "d": [12,728], "a": 1 },
+ { "px": [288,256], "src": [32,0], "f": 0, "t": 2, "d": [12,738], "a": 1 },
+ { "px": [352,256], "src": [32,0], "f": 0, "t": 2, "d": [12,742], "a": 1 },
+ { "px": [416,256], "src": [32,0], "f": 0, "t": 2, "d": [12,746], "a": 1 },
+ { "px": [544,256], "src": [32,0], "f": 0, "t": 2, "d": [12,754], "a": 1 },
+ { "px": [608,256], "src": [32,0], "f": 0, "t": 2, "d": [12,758], "a": 1 },
+ { "px": [672,256], "src": [32,0], "f": 0, "t": 2, "d": [12,762], "a": 1 },
+ { "px": [704,256], "src": [32,0], "f": 0, "t": 2, "d": [12,764], "a": 1 },
+ { "px": [16,272], "src": [32,0], "f": 0, "t": 2, "d": [12,766], "a": 1 },
+ { "px": [48,272], "src": [32,0], "f": 0, "t": 2, "d": [12,768], "a": 1 },
+ { "px": [80,272], "src": [32,0], "f": 0, "t": 2, "d": [12,770], "a": 1 },
+ { "px": [112,272], "src": [32,0], "f": 0, "t": 2, "d": [12,772], "a": 1 },
+ { "px": [144,272], "src": [32,0], "f": 0, "t": 2, "d": [12,774], "a": 1 },
+ { "px": [176,272], "src": [32,0], "f": 0, "t": 2, "d": [12,776], "a": 1 },
+ { "px": [240,272], "src": [32,0], "f": 0, "t": 2, "d": [12,780], "a": 1 },
+ { "px": [272,272], "src": [32,0], "f": 0, "t": 2, "d": [12,782], "a": 1 },
+ { "px": [304,272], "src": [32,0], "f": 0, "t": 2, "d": [12,784], "a": 1 },
+ { "px": [336,272], "src": [32,0], "f": 0, "t": 2, "d": [12,786], "a": 1 },
+ { "px": [368,272], "src": [32,0], "f": 0, "t": 2, "d": [12,788], "a": 1 },
+ { "px": [400,272], "src": [32,0], "f": 0, "t": 2, "d": [12,790], "a": 1 },
+ { "px": [432,272], "src": [32,0], "f": 0, "t": 2, "d": [12,792], "a": 1 },
+ { "px": [464,272], "src": [32,0], "f": 0, "t": 2, "d": [12,794], "a": 1 },
+ { "px": [560,272], "src": [32,0], "f": 0, "t": 2, "d": [12,800], "a": 1 },
+ { "px": [592,272], "src": [32,0], "f": 0, "t": 2, "d": [12,802], "a": 1 },
+ { "px": [624,272], "src": [32,0], "f": 0, "t": 2, "d": [12,804], "a": 1 },
+ { "px": [656,272], "src": [32,0], "f": 0, "t": 2, "d": [12,806], "a": 1 },
+ { "px": [688,272], "src": [32,0], "f": 0, "t": 2, "d": [12,808], "a": 1 },
+ { "px": [0,288], "src": [32,0], "f": 0, "t": 2, "d": [12,810], "a": 1 },
+ { "px": [32,288], "src": [32,0], "f": 0, "t": 2, "d": [12,812], "a": 1 },
+ { "px": [96,288], "src": [32,0], "f": 0, "t": 2, "d": [12,816], "a": 1 },
+ { "px": [256,288], "src": [32,0], "f": 0, "t": 2, "d": [12,826], "a": 1 },
+ { "px": [288,288], "src": [32,0], "f": 0, "t": 2, "d": [12,828], "a": 1 },
+ { "px": [320,288], "src": [32,0], "f": 0, "t": 2, "d": [12,830], "a": 1 },
+ { "px": [352,288], "src": [32,0], "f": 0, "t": 2, "d": [12,832], "a": 1 },
+ { "px": [384,288], "src": [32,0], "f": 0, "t": 2, "d": [12,834], "a": 1 },
+ { "px": [416,288], "src": [32,0], "f": 0, "t": 2, "d": [12,836], "a": 1 },
+ { "px": [448,288], "src": [32,0], "f": 0, "t": 2, "d": [12,838], "a": 1 },
+ { "px": [576,288], "src": [32,0], "f": 0, "t": 2, "d": [12,846], "a": 1 },
+ { "px": [608,288], "src": [32,0], "f": 0, "t": 2, "d": [12,848], "a": 1 },
+ { "px": [672,288], "src": [32,0], "f": 0, "t": 2, "d": [12,852], "a": 1 },
+ { "px": [704,288], "src": [32,0], "f": 0, "t": 2, "d": [12,854], "a": 1 },
+ { "px": [16,304], "src": [32,0], "f": 0, "t": 2, "d": [12,856], "a": 1 },
+ { "px": [48,304], "src": [32,0], "f": 0, "t": 2, "d": [12,858], "a": 1 },
+ { "px": [80,304], "src": [32,0], "f": 0, "t": 2, "d": [12,860], "a": 1 },
+ { "px": [112,304], "src": [32,0], "f": 0, "t": 2, "d": [12,862], "a": 1 },
+ { "px": [144,304], "src": [32,0], "f": 0, "t": 2, "d": [12,864], "a": 1 },
+ { "px": [240,304], "src": [32,0], "f": 0, "t": 2, "d": [12,870], "a": 1 },
+ { "px": [272,304], "src": [32,0], "f": 0, "t": 2, "d": [12,872], "a": 1 },
+ { "px": [304,304], "src": [32,0], "f": 0, "t": 2, "d": [12,874], "a": 1 },
+ { "px": [336,304], "src": [32,0], "f": 0, "t": 2, "d": [12,876], "a": 1 },
+ { "px": [368,304], "src": [32,0], "f": 0, "t": 2, "d": [12,878], "a": 1 },
+ { "px": [400,304], "src": [32,0], "f": 0, "t": 2, "d": [12,880], "a": 1 },
+ { "px": [432,304], "src": [32,0], "f": 0, "t": 2, "d": [12,882], "a": 1 },
+ { "px": [464,304], "src": [32,0], "f": 0, "t": 2, "d": [12,884], "a": 1 },
+ { "px": [592,304], "src": [32,0], "f": 0, "t": 2, "d": [12,892], "a": 1 },
+ { "px": [624,304], "src": [32,0], "f": 0, "t": 2, "d": [12,894], "a": 1 },
+ { "px": [656,304], "src": [32,0], "f": 0, "t": 2, "d": [12,896], "a": 1 },
+ { "px": [688,304], "src": [32,0], "f": 0, "t": 2, "d": [12,898], "a": 1 },
+ { "px": [0,320], "src": [32,0], "f": 0, "t": 2, "d": [12,900], "a": 1 },
+ { "px": [32,320], "src": [32,0], "f": 0, "t": 2, "d": [12,902], "a": 1 },
+ { "px": [64,320], "src": [32,0], "f": 0, "t": 2, "d": [12,904], "a": 1 },
+ { "px": [96,320], "src": [32,0], "f": 0, "t": 2, "d": [12,906], "a": 1 },
+ { "px": [128,320], "src": [32,0], "f": 0, "t": 2, "d": [12,908], "a": 1 },
+ { "px": [288,320], "src": [32,0], "f": 0, "t": 2, "d": [12,918], "a": 1 },
+ { "px": [416,320], "src": [32,0], "f": 0, "t": 2, "d": [12,926], "a": 1 },
+ { "px": [448,320], "src": [32,0], "f": 0, "t": 2, "d": [12,928], "a": 1 },
+ { "px": [480,320], "src": [32,0], "f": 0, "t": 2, "d": [12,930], "a": 1 },
+ { "px": [608,320], "src": [32,0], "f": 0, "t": 2, "d": [12,938], "a": 1 },
+ { "px": [640,320], "src": [32,0], "f": 0, "t": 2, "d": [12,940], "a": 1 },
+ { "px": [672,320], "src": [32,0], "f": 0, "t": 2, "d": [12,942], "a": 1 },
+ { "px": [704,320], "src": [32,0], "f": 0, "t": 2, "d": [12,944], "a": 1 },
+ { "px": [16,336], "src": [32,0], "f": 0, "t": 2, "d": [12,946], "a": 1 },
+ { "px": [48,336], "src": [32,0], "f": 0, "t": 2, "d": [12,948], "a": 1 },
+ { "px": [80,336], "src": [32,0], "f": 0, "t": 2, "d": [12,950], "a": 1 },
+ { "px": [112,336], "src": [32,0], "f": 0, "t": 2, "d": [12,952], "a": 1 },
+ { "px": [144,336], "src": [32,0], "f": 0, "t": 2, "d": [12,954], "a": 1 },
+ { "px": [208,336], "src": [32,0], "f": 0, "t": 2, "d": [12,958], "a": 1 },
+ { "px": [240,336], "src": [32,0], "f": 0, "t": 2, "d": [12,960], "a": 1 },
+ { "px": [272,336], "src": [32,0], "f": 0, "t": 2, "d": [12,962], "a": 1 },
+ { "px": [304,336], "src": [32,0], "f": 0, "t": 2, "d": [12,964], "a": 1 },
+ { "px": [336,336], "src": [32,0], "f": 0, "t": 2, "d": [12,966], "a": 1 },
+ { "px": [368,336], "src": [32,0], "f": 0, "t": 2, "d": [12,968], "a": 1 },
+ { "px": [400,336], "src": [32,0], "f": 0, "t": 2, "d": [12,970], "a": 1 },
+ { "px": [432,336], "src": [32,0], "f": 0, "t": 2, "d": [12,972], "a": 1 },
+ { "px": [464,336], "src": [32,0], "f": 0, "t": 2, "d": [12,974], "a": 1 },
+ { "px": [496,336], "src": [32,0], "f": 0, "t": 2, "d": [12,976], "a": 1 },
+ { "px": [592,336], "src": [32,0], "f": 0, "t": 2, "d": [12,982], "a": 1 },
+ { "px": [624,336], "src": [32,0], "f": 0, "t": 2, "d": [12,984], "a": 1 },
+ { "px": [656,336], "src": [32,0], "f": 0, "t": 2, "d": [12,986], "a": 1 },
+ { "px": [688,336], "src": [32,0], "f": 0, "t": 2, "d": [12,988], "a": 1 },
+ { "px": [32,352], "src": [32,0], "f": 0, "t": 2, "d": [12,992], "a": 1 },
+ { "px": [64,352], "src": [32,0], "f": 0, "t": 2, "d": [12,994], "a": 1 },
+ { "px": [96,352], "src": [32,0], "f": 0, "t": 2, "d": [12,996], "a": 1 },
+ { "px": [288,352], "src": [32,0], "f": 0, "t": 2, "d": [12,1008], "a": 1 },
+ { "px": [320,352], "src": [32,0], "f": 0, "t": 2, "d": [12,1010], "a": 1 },
+ { "px": [352,352], "src": [32,0], "f": 0, "t": 2, "d": [12,1012], "a": 1 },
+ { "px": [384,352], "src": [32,0], "f": 0, "t": 2, "d": [12,1014], "a": 1 },
+ { "px": [608,352], "src": [32,0], "f": 0, "t": 2, "d": [12,1028], "a": 1 },
+ { "px": [640,352], "src": [32,0], "f": 0, "t": 2, "d": [12,1030], "a": 1 },
+ { "px": [672,352], "src": [32,0], "f": 0, "t": 2, "d": [12,1032], "a": 1 },
+ { "px": [704,352], "src": [32,0], "f": 0, "t": 2, "d": [12,1034], "a": 1 },
+ { "px": [16,368], "src": [32,0], "f": 0, "t": 2, "d": [12,1036], "a": 1 },
+ { "px": [48,368], "src": [32,0], "f": 0, "t": 2, "d": [12,1038], "a": 1 },
+ { "px": [80,368], "src": [32,0], "f": 0, "t": 2, "d": [12,1040], "a": 1 },
+ { "px": [112,368], "src": [32,0], "f": 0, "t": 2, "d": [12,1042], "a": 1 },
+ { "px": [144,368], "src": [32,0], "f": 0, "t": 2, "d": [12,1044], "a": 1 },
+ { "px": [272,368], "src": [32,0], "f": 0, "t": 2, "d": [12,1052], "a": 1 },
+ { "px": [304,368], "src": [32,0], "f": 0, "t": 2, "d": [12,1054], "a": 1 },
+ { "px": [368,368], "src": [32,0], "f": 0, "t": 2, "d": [12,1058], "a": 1 },
+ { "px": [560,368], "src": [32,0], "f": 0, "t": 2, "d": [12,1070], "a": 1 },
+ { "px": [592,368], "src": [32,0], "f": 0, "t": 2, "d": [12,1072], "a": 1 },
+ { "px": [624,368], "src": [32,0], "f": 0, "t": 2, "d": [12,1074], "a": 1 },
+ { "px": [656,368], "src": [32,0], "f": 0, "t": 2, "d": [12,1076], "a": 1 },
+ { "px": [688,368], "src": [32,0], "f": 0, "t": 2, "d": [12,1078], "a": 1 },
+ { "px": [32,384], "src": [32,0], "f": 0, "t": 2, "d": [12,1082], "a": 1 },
+ { "px": [96,384], "src": [32,0], "f": 0, "t": 2, "d": [12,1086], "a": 1 },
+ { "px": [128,384], "src": [32,0], "f": 0, "t": 2, "d": [12,1088], "a": 1 },
+ { "px": [288,384], "src": [32,0], "f": 0, "t": 2, "d": [12,1098], "a": 1 },
+ { "px": [320,384], "src": [32,0], "f": 0, "t": 2, "d": [12,1100], "a": 1 },
+ { "px": [352,384], "src": [32,0], "f": 0, "t": 2, "d": [12,1102], "a": 1 },
+ { "px": [384,384], "src": [32,0], "f": 0, "t": 2, "d": [12,1104], "a": 1 },
+ { "px": [544,384], "src": [32,0], "f": 0, "t": 2, "d": [12,1114], "a": 1 },
+ { "px": [576,384], "src": [32,0], "f": 0, "t": 2, "d": [12,1116], "a": 1 },
+ { "px": [608,384], "src": [32,0], "f": 0, "t": 2, "d": [12,1118], "a": 1 },
+ { "px": [672,384], "src": [32,0], "f": 0, "t": 2, "d": [12,1122], "a": 1 },
+ { "px": [704,384], "src": [32,0], "f": 0, "t": 2, "d": [12,1124], "a": 1 },
+ { "px": [16,400], "src": [32,0], "f": 0, "t": 2, "d": [12,1126], "a": 1 },
+ { "px": [48,400], "src": [32,0], "f": 0, "t": 2, "d": [12,1128], "a": 1 },
+ { "px": [80,400], "src": [32,0], "f": 0, "t": 2, "d": [12,1130], "a": 1 },
+ { "px": [112,400], "src": [32,0], "f": 0, "t": 2, "d": [12,1132], "a": 1 },
+ { "px": [144,400], "src": [32,0], "f": 0, "t": 2, "d": [12,1134], "a": 1 },
+ { "px": [176,400], "src": [32,0], "f": 0, "t": 2, "d": [12,1136], "a": 1 },
+ { "px": [208,400], "src": [32,0], "f": 0, "t": 2, "d": [12,1138], "a": 1 },
+ { "px": [560,400], "src": [32,0], "f": 0, "t": 2, "d": [12,1160], "a": 1 },
+ { "px": [592,400], "src": [32,0], "f": 0, "t": 2, "d": [12,1162], "a": 1 },
+ { "px": [624,400], "src": [32,0], "f": 0, "t": 2, "d": [12,1164], "a": 1 },
+ { "px": [656,400], "src": [32,0], "f": 0, "t": 2, "d": [12,1166], "a": 1 },
+ { "px": [688,400], "src": [32,0], "f": 0, "t": 2, "d": [12,1168], "a": 1 },
+ { "px": [32,416], "src": [32,0], "f": 0, "t": 2, "d": [12,1172], "a": 1 },
+ { "px": [64,416], "src": [32,0], "f": 0, "t": 2, "d": [12,1174], "a": 1 },
+ { "px": [96,416], "src": [32,0], "f": 0, "t": 2, "d": [12,1176], "a": 1 },
+ { "px": [160,416], "src": [32,0], "f": 0, "t": 2, "d": [12,1180], "a": 1 },
+ { "px": [224,416], "src": [32,0], "f": 0, "t": 2, "d": [12,1184], "a": 1 },
+ { "px": [544,416], "src": [32,0], "f": 0, "t": 2, "d": [12,1204], "a": 1 },
+ { "px": [576,416], "src": [32,0], "f": 0, "t": 2, "d": [12,1206], "a": 1 },
+ { "px": [608,416], "src": [32,0], "f": 0, "t": 2, "d": [12,1208], "a": 1 },
+ { "px": [640,416], "src": [32,0], "f": 0, "t": 2, "d": [12,1210], "a": 1 },
+ { "px": [672,416], "src": [32,0], "f": 0, "t": 2, "d": [12,1212], "a": 1 },
+ { "px": [704,416], "src": [32,0], "f": 0, "t": 2, "d": [12,1214], "a": 1 },
+ { "px": [16,432], "src": [32,0], "f": 0, "t": 2, "d": [12,1216], "a": 1 },
+ { "px": [48,432], "src": [32,0], "f": 0, "t": 2, "d": [12,1218], "a": 1 },
+ { "px": [80,432], "src": [32,0], "f": 0, "t": 2, "d": [12,1220], "a": 1 },
+ { "px": [112,432], "src": [32,0], "f": 0, "t": 2, "d": [12,1222], "a": 1 },
+ { "px": [144,432], "src": [32,0], "f": 0, "t": 2, "d": [12,1224], "a": 1 },
+ { "px": [176,432], "src": [32,0], "f": 0, "t": 2, "d": [12,1226], "a": 1 },
+ { "px": [208,432], "src": [32,0], "f": 0, "t": 2, "d": [12,1228], "a": 1 },
+ { "px": [240,432], "src": [32,0], "f": 0, "t": 2, "d": [12,1230], "a": 1 },
+ { "px": [272,432], "src": [32,0], "f": 0, "t": 2, "d": [12,1232], "a": 1 },
+ { "px": [304,432], "src": [32,0], "f": 0, "t": 2, "d": [12,1234], "a": 1 },
+ { "px": [336,432], "src": [32,0], "f": 0, "t": 2, "d": [12,1236], "a": 1 },
+ { "px": [368,432], "src": [32,0], "f": 0, "t": 2, "d": [12,1238], "a": 1 },
+ { "px": [400,432], "src": [32,0], "f": 0, "t": 2, "d": [12,1240], "a": 1 },
+ { "px": [432,432], "src": [32,0], "f": 0, "t": 2, "d": [12,1242], "a": 1 },
+ { "px": [464,432], "src": [32,0], "f": 0, "t": 2, "d": [12,1244], "a": 1 },
+ { "px": [496,432], "src": [32,0], "f": 0, "t": 2, "d": [12,1246], "a": 1 },
+ { "px": [528,432], "src": [32,0], "f": 0, "t": 2, "d": [12,1248], "a": 1 },
+ { "px": [560,432], "src": [32,0], "f": 0, "t": 2, "d": [12,1250], "a": 1 },
+ { "px": [592,432], "src": [32,0], "f": 0, "t": 2, "d": [12,1252], "a": 1 },
+ { "px": [624,432], "src": [32,0], "f": 0, "t": 2, "d": [12,1254], "a": 1 },
+ { "px": [656,432], "src": [32,0], "f": 0, "t": 2, "d": [12,1256], "a": 1 },
+ { "px": [688,432], "src": [32,0], "f": 0, "t": 2, "d": [12,1258], "a": 1 },
+ { "px": [0,448], "src": [32,0], "f": 0, "t": 2, "d": [12,1260], "a": 1 },
+ { "px": [32,448], "src": [32,0], "f": 0, "t": 2, "d": [12,1262], "a": 1 },
+ { "px": [96,448], "src": [32,0], "f": 0, "t": 2, "d": [12,1266], "a": 1 },
+ { "px": [160,448], "src": [32,0], "f": 0, "t": 2, "d": [12,1270], "a": 1 },
+ { "px": [224,448], "src": [32,0], "f": 0, "t": 2, "d": [12,1274], "a": 1 },
+ { "px": [288,448], "src": [32,0], "f": 0, "t": 2, "d": [12,1278], "a": 1 },
+ { "px": [320,448], "src": [32,0], "f": 0, "t": 2, "d": [12,1280], "a": 1 },
+ { "px": [352,448], "src": [32,0], "f": 0, "t": 2, "d": [12,1282], "a": 1 },
+ { "px": [384,448], "src": [32,0], "f": 0, "t": 2, "d": [12,1284], "a": 1 },
+ { "px": [416,448], "src": [32,0], "f": 0, "t": 2, "d": [12,1286], "a": 1 },
+ { "px": [480,448], "src": [32,0], "f": 0, "t": 2, "d": [12,1290], "a": 1 },
+ { "px": [544,448], "src": [32,0], "f": 0, "t": 2, "d": [12,1294], "a": 1 },
+ { "px": [608,448], "src": [32,0], "f": 0, "t": 2, "d": [12,1298], "a": 1 },
+ { "px": [640,448], "src": [32,0], "f": 0, "t": 2, "d": [12,1300], "a": 1 },
+ { "px": [672,448], "src": [32,0], "f": 0, "t": 2, "d": [12,1302], "a": 1 },
+ { "px": [704,448], "src": [32,0], "f": 0, "t": 2, "d": [12,1304], "a": 1 },
+ { "px": [16,464], "src": [32,0], "f": 0, "t": 2, "d": [12,1306], "a": 1 },
+ { "px": [48,464], "src": [32,0], "f": 0, "t": 2, "d": [12,1308], "a": 1 },
+ { "px": [80,464], "src": [32,0], "f": 0, "t": 2, "d": [12,1310], "a": 1 },
+ { "px": [112,464], "src": [32,0], "f": 0, "t": 2, "d": [12,1312], "a": 1 },
+ { "px": [144,464], "src": [32,0], "f": 0, "t": 2, "d": [12,1314], "a": 1 },
+ { "px": [176,464], "src": [32,0], "f": 0, "t": 2, "d": [12,1316], "a": 1 },
+ { "px": [208,464], "src": [32,0], "f": 0, "t": 2, "d": [12,1318], "a": 1 },
+ { "px": [240,464], "src": [32,0], "f": 0, "t": 2, "d": [12,1320], "a": 1 },
+ { "px": [272,464], "src": [32,0], "f": 0, "t": 2, "d": [12,1322], "a": 1 },
+ { "px": [304,464], "src": [32,0], "f": 0, "t": 2, "d": [12,1324], "a": 1 },
+ { "px": [336,464], "src": [32,0], "f": 0, "t": 2, "d": [12,1326], "a": 1 },
+ { "px": [368,464], "src": [32,0], "f": 0, "t": 2, "d": [12,1328], "a": 1 },
+ { "px": [400,464], "src": [32,0], "f": 0, "t": 2, "d": [12,1330], "a": 1 },
+ { "px": [432,464], "src": [32,0], "f": 0, "t": 2, "d": [12,1332], "a": 1 },
+ { "px": [464,464], "src": [32,0], "f": 0, "t": 2, "d": [12,1334], "a": 1 },
+ { "px": [496,464], "src": [32,0], "f": 0, "t": 2, "d": [12,1336], "a": 1 },
+ { "px": [528,464], "src": [32,0], "f": 0, "t": 2, "d": [12,1338], "a": 1 },
+ { "px": [560,464], "src": [32,0], "f": 0, "t": 2, "d": [12,1340], "a": 1 },
+ { "px": [592,464], "src": [32,0], "f": 0, "t": 2, "d": [12,1342], "a": 1 },
+ { "px": [624,464], "src": [32,0], "f": 0, "t": 2, "d": [12,1344], "a": 1 },
+ { "px": [656,464], "src": [32,0], "f": 0, "t": 2, "d": [12,1346], "a": 1 },
+ { "px": [688,464], "src": [32,0], "f": 0, "t": 2, "d": [12,1348], "a": 1 },
+ { "px": [0,480], "src": [32,0], "f": 0, "t": 2, "d": [12,1350], "a": 1 },
+ { "px": [32,480], "src": [32,0], "f": 0, "t": 2, "d": [12,1352], "a": 1 },
+ { "px": [64,480], "src": [32,0], "f": 0, "t": 2, "d": [12,1354], "a": 1 },
+ { "px": [96,480], "src": [32,0], "f": 0, "t": 2, "d": [12,1356], "a": 1 },
+ { "px": [128,480], "src": [32,0], "f": 0, "t": 2, "d": [12,1358], "a": 1 },
+ { "px": [160,480], "src": [32,0], "f": 0, "t": 2, "d": [12,1360], "a": 1 },
+ { "px": [192,480], "src": [32,0], "f": 0, "t": 2, "d": [12,1362], "a": 1 },
+ { "px": [224,480], "src": [32,0], "f": 0, "t": 2, "d": [12,1364], "a": 1 },
+ { "px": [256,480], "src": [32,0], "f": 0, "t": 2, "d": [12,1366], "a": 1 },
+ { "px": [288,480], "src": [32,0], "f": 0, "t": 2, "d": [12,1368], "a": 1 },
+ { "px": [352,480], "src": [32,0], "f": 0, "t": 2, "d": [12,1372], "a": 1 },
+ { "px": [416,480], "src": [32,0], "f": 0, "t": 2, "d": [12,1376], "a": 1 },
+ { "px": [448,480], "src": [32,0], "f": 0, "t": 2, "d": [12,1378], "a": 1 },
+ { "px": [480,480], "src": [32,0], "f": 0, "t": 2, "d": [12,1380], "a": 1 },
+ { "px": [512,480], "src": [32,0], "f": 0, "t": 2, "d": [12,1382], "a": 1 },
+ { "px": [544,480], "src": [32,0], "f": 0, "t": 2, "d": [12,1384], "a": 1 },
+ { "px": [576,480], "src": [32,0], "f": 0, "t": 2, "d": [12,1386], "a": 1 },
+ { "px": [608,480], "src": [32,0], "f": 0, "t": 2, "d": [12,1388], "a": 1 },
+ { "px": [640,480], "src": [32,0], "f": 0, "t": 2, "d": [12,1390], "a": 1 },
+ { "px": [672,480], "src": [32,0], "f": 0, "t": 2, "d": [12,1392], "a": 1 },
+ { "px": [704,480], "src": [32,0], "f": 0, "t": 2, "d": [12,1394], "a": 1 },
+ { "px": [16,496], "src": [32,0], "f": 0, "t": 2, "d": [12,1396], "a": 1 },
+ { "px": [48,496], "src": [32,0], "f": 0, "t": 2, "d": [12,1398], "a": 1 },
+ { "px": [80,496], "src": [32,0], "f": 0, "t": 2, "d": [12,1400], "a": 1 },
+ { "px": [112,496], "src": [32,0], "f": 0, "t": 2, "d": [12,1402], "a": 1 },
+ { "px": [144,496], "src": [32,0], "f": 0, "t": 2, "d": [12,1404], "a": 1 },
+ { "px": [176,496], "src": [32,0], "f": 0, "t": 2, "d": [12,1406], "a": 1 },
+ { "px": [208,496], "src": [32,0], "f": 0, "t": 2, "d": [12,1408], "a": 1 },
+ { "px": [240,496], "src": [32,0], "f": 0, "t": 2, "d": [12,1410], "a": 1 },
+ { "px": [272,496], "src": [32,0], "f": 0, "t": 2, "d": [12,1412], "a": 1 },
+ { "px": [304,496], "src": [32,0], "f": 0, "t": 2, "d": [12,1414], "a": 1 },
+ { "px": [336,496], "src": [32,0], "f": 0, "t": 2, "d": [12,1416], "a": 1 },
+ { "px": [368,496], "src": [32,0], "f": 0, "t": 2, "d": [12,1418], "a": 1 },
+ { "px": [400,496], "src": [32,0], "f": 0, "t": 2, "d": [12,1420], "a": 1 },
+ { "px": [432,496], "src": [32,0], "f": 0, "t": 2, "d": [12,1422], "a": 1 },
+ { "px": [464,496], "src": [32,0], "f": 0, "t": 2, "d": [12,1424], "a": 1 },
+ { "px": [496,496], "src": [32,0], "f": 0, "t": 2, "d": [12,1426], "a": 1 },
+ { "px": [528,496], "src": [32,0], "f": 0, "t": 2, "d": [12,1428], "a": 1 },
+ { "px": [560,496], "src": [32,0], "f": 0, "t": 2, "d": [12,1430], "a": 1 },
+ { "px": [592,496], "src": [32,0], "f": 0, "t": 2, "d": [12,1432], "a": 1 },
+ { "px": [624,496], "src": [32,0], "f": 0, "t": 2, "d": [12,1434], "a": 1 },
+ { "px": [656,496], "src": [32,0], "f": 0, "t": 2, "d": [12,1436], "a": 1 },
+ { "px": [688,496], "src": [32,0], "f": 0, "t": 2, "d": [12,1438], "a": 1 },
+ { "px": [0,512], "src": [32,0], "f": 0, "t": 2, "d": [12,1440], "a": 1 },
+ { "px": [32,512], "src": [32,0], "f": 0, "t": 2, "d": [12,1442], "a": 1 },
+ { "px": [96,512], "src": [32,0], "f": 0, "t": 2, "d": [12,1446], "a": 1 },
+ { "px": [160,512], "src": [32,0], "f": 0, "t": 2, "d": [12,1450], "a": 1 },
+ { "px": [192,512], "src": [32,0], "f": 0, "t": 2, "d": [12,1452], "a": 1 },
+ { "px": [224,512], "src": [32,0], "f": 0, "t": 2, "d": [12,1454], "a": 1 },
+ { "px": [256,512], "src": [32,0], "f": 0, "t": 2, "d": [12,1456], "a": 1 },
+ { "px": [288,512], "src": [32,0], "f": 0, "t": 2, "d": [12,1458], "a": 1 },
+ { "px": [320,512], "src": [32,0], "f": 0, "t": 2, "d": [12,1460], "a": 1 },
+ { "px": [352,512], "src": [32,0], "f": 0, "t": 2, "d": [12,1462], "a": 1 },
+ { "px": [384,512], "src": [32,0], "f": 0, "t": 2, "d": [12,1464], "a": 1 },
+ { "px": [416,512], "src": [32,0], "f": 0, "t": 2, "d": [12,1466], "a": 1 },
+ { "px": [448,512], "src": [32,0], "f": 0, "t": 2, "d": [12,1468], "a": 1 },
+ { "px": [480,512], "src": [32,0], "f": 0, "t": 2, "d": [12,1470], "a": 1 },
+ { "px": [544,512], "src": [32,0], "f": 0, "t": 2, "d": [12,1474], "a": 1 },
+ { "px": [576,512], "src": [32,0], "f": 0, "t": 2, "d": [12,1476], "a": 1 },
+ { "px": [608,512], "src": [32,0], "f": 0, "t": 2, "d": [12,1478], "a": 1 },
+ { "px": [640,512], "src": [32,0], "f": 0, "t": 2, "d": [12,1480], "a": 1 },
+ { "px": [672,512], "src": [32,0], "f": 0, "t": 2, "d": [12,1482], "a": 1 },
+ { "px": [704,512], "src": [32,0], "f": 0, "t": 2, "d": [12,1484], "a": 1 },
+ { "px": [16,528], "src": [32,0], "f": 0, "t": 2, "d": [12,1486], "a": 1 },
+ { "px": [48,528], "src": [32,0], "f": 0, "t": 2, "d": [12,1488], "a": 1 },
+ { "px": [80,528], "src": [32,0], "f": 0, "t": 2, "d": [12,1490], "a": 1 },
+ { "px": [112,528], "src": [32,0], "f": 0, "t": 2, "d": [12,1492], "a": 1 },
+ { "px": [144,528], "src": [32,0], "f": 0, "t": 2, "d": [12,1494], "a": 1 },
+ { "px": [176,528], "src": [32,0], "f": 0, "t": 2, "d": [12,1496], "a": 1 },
+ { "px": [208,528], "src": [32,0], "f": 0, "t": 2, "d": [12,1498], "a": 1 },
+ { "px": [240,528], "src": [32,0], "f": 0, "t": 2, "d": [12,1500], "a": 1 },
+ { "px": [272,528], "src": [32,0], "f": 0, "t": 2, "d": [12,1502], "a": 1 },
+ { "px": [304,528], "src": [32,0], "f": 0, "t": 2, "d": [12,1504], "a": 1 },
+ { "px": [336,528], "src": [32,0], "f": 0, "t": 2, "d": [12,1506], "a": 1 },
+ { "px": [368,528], "src": [32,0], "f": 0, "t": 2, "d": [12,1508], "a": 1 },
+ { "px": [400,528], "src": [32,0], "f": 0, "t": 2, "d": [12,1510], "a": 1 },
+ { "px": [432,528], "src": [32,0], "f": 0, "t": 2, "d": [12,1512], "a": 1 },
+ { "px": [464,528], "src": [32,0], "f": 0, "t": 2, "d": [12,1514], "a": 1 },
+ { "px": [496,528], "src": [32,0], "f": 0, "t": 2, "d": [12,1516], "a": 1 },
+ { "px": [528,528], "src": [32,0], "f": 0, "t": 2, "d": [12,1518], "a": 1 },
+ { "px": [560,528], "src": [32,0], "f": 0, "t": 2, "d": [12,1520], "a": 1 },
+ { "px": [592,528], "src": [32,0], "f": 0, "t": 2, "d": [12,1522], "a": 1 },
+ { "px": [624,528], "src": [32,0], "f": 0, "t": 2, "d": [12,1524], "a": 1 },
+ { "px": [656,528], "src": [32,0], "f": 0, "t": 2, "d": [12,1526], "a": 1 },
+ { "px": [688,528], "src": [32,0], "f": 0, "t": 2, "d": [12,1528], "a": 1 },
+ { "px": [64,0], "src": [0,16], "f": 0, "t": 32, "d": [13,4], "a": 1 },
+ { "px": [64,16], "src": [0,32], "f": 0, "t": 64, "d": [13,4], "a": 1 },
+ { "px": [80,0], "src": [16,16], "f": 0, "t": 33, "d": [13,4], "a": 1 },
+ { "px": [80,16], "src": [16,32], "f": 0, "t": 65, "d": [13,4], "a": 1 },
+ { "px": [128,0], "src": [0,16], "f": 0, "t": 32, "d": [13,8], "a": 1 },
+ { "px": [128,16], "src": [0,32], "f": 0, "t": 64, "d": [13,8], "a": 1 },
+ { "px": [144,0], "src": [16,16], "f": 0, "t": 33, "d": [13,8], "a": 1 },
+ { "px": [144,16], "src": [16,32], "f": 0, "t": 65, "d": [13,8], "a": 1 },
+ { "px": [256,0], "src": [0,16], "f": 0, "t": 32, "d": [13,16], "a": 1 },
+ { "px": [256,16], "src": [0,32], "f": 0, "t": 64, "d": [13,16], "a": 1 },
+ { "px": [272,0], "src": [16,16], "f": 0, "t": 33, "d": [13,16], "a": 1 },
+ { "px": [272,16], "src": [16,32], "f": 0, "t": 65, "d": [13,16], "a": 1 },
+ { "px": [320,0], "src": [0,16], "f": 0, "t": 32, "d": [13,20], "a": 1 },
+ { "px": [320,16], "src": [0,32], "f": 0, "t": 64, "d": [13,20], "a": 1 },
+ { "px": [336,0], "src": [16,16], "f": 0, "t": 33, "d": [13,20], "a": 1 },
+ { "px": [336,16], "src": [16,32], "f": 0, "t": 65, "d": [13,20], "a": 1 },
+ { "px": [384,0], "src": [0,16], "f": 0, "t": 32, "d": [13,24], "a": 1 },
+ { "px": [384,16], "src": [0,32], "f": 0, "t": 64, "d": [13,24], "a": 1 },
+ { "px": [400,0], "src": [16,16], "f": 0, "t": 33, "d": [13,24], "a": 1 },
+ { "px": [400,16], "src": [16,32], "f": 0, "t": 65, "d": [13,24], "a": 1 },
+ { "px": [448,0], "src": [0,16], "f": 0, "t": 32, "d": [13,28], "a": 1 },
+ { "px": [448,16], "src": [0,32], "f": 0, "t": 64, "d": [13,28], "a": 1 },
+ { "px": [464,0], "src": [16,16], "f": 0, "t": 33, "d": [13,28], "a": 1 },
+ { "px": [464,16], "src": [16,32], "f": 0, "t": 65, "d": [13,28], "a": 1 },
+ { "px": [512,0], "src": [0,16], "f": 0, "t": 32, "d": [13,32], "a": 1 },
+ { "px": [512,16], "src": [0,32], "f": 0, "t": 64, "d": [13,32], "a": 1 },
+ { "px": [528,0], "src": [16,16], "f": 0, "t": 33, "d": [13,32], "a": 1 },
+ { "px": [528,16], "src": [16,32], "f": 0, "t": 65, "d": [13,32], "a": 1 },
+ { "px": [640,0], "src": [0,16], "f": 0, "t": 32, "d": [13,40], "a": 1 },
+ { "px": [640,16], "src": [0,32], "f": 0, "t": 64, "d": [13,40], "a": 1 },
+ { "px": [656,0], "src": [16,16], "f": 0, "t": 33, "d": [13,40], "a": 1 },
+ { "px": [656,16], "src": [16,32], "f": 0, "t": 65, "d": [13,40], "a": 1 },
+ { "px": [96,16], "src": [0,16], "f": 0, "t": 32, "d": [13,51], "a": 1 },
+ { "px": [96,32], "src": [0,32], "f": 0, "t": 64, "d": [13,51], "a": 1 },
+ { "px": [112,16], "src": [16,16], "f": 0, "t": 33, "d": [13,51], "a": 1 },
+ { "px": [112,32], "src": [16,32], "f": 0, "t": 65, "d": [13,51], "a": 1 },
+ { "px": [288,16], "src": [0,16], "f": 0, "t": 32, "d": [13,63], "a": 1 },
+ { "px": [288,32], "src": [0,32], "f": 0, "t": 64, "d": [13,63], "a": 1 },
+ { "px": [304,16], "src": [16,16], "f": 0, "t": 33, "d": [13,63], "a": 1 },
+ { "px": [304,32], "src": [16,32], "f": 0, "t": 65, "d": [13,63], "a": 1 },
+ { "px": [480,16], "src": [0,16], "f": 0, "t": 32, "d": [13,75], "a": 1 },
+ { "px": [480,32], "src": [0,32], "f": 0, "t": 64, "d": [13,75], "a": 1 },
+ { "px": [496,16], "src": [16,16], "f": 0, "t": 33, "d": [13,75], "a": 1 },
+ { "px": [496,32], "src": [16,32], "f": 0, "t": 65, "d": [13,75], "a": 1 },
+ { "px": [672,16], "src": [0,16], "f": 0, "t": 32, "d": [13,87], "a": 1 },
+ { "px": [672,32], "src": [0,32], "f": 0, "t": 64, "d": [13,87], "a": 1 },
+ { "px": [688,16], "src": [16,16], "f": 0, "t": 33, "d": [13,87], "a": 1 },
+ { "px": [688,32], "src": [16,32], "f": 0, "t": 65, "d": [13,87], "a": 1 },
+ { "px": [64,32], "src": [0,16], "f": 0, "t": 32, "d": [13,94], "a": 1 },
+ { "px": [64,48], "src": [0,32], "f": 0, "t": 64, "d": [13,94], "a": 1 },
+ { "px": [80,32], "src": [16,16], "f": 0, "t": 33, "d": [13,94], "a": 1 },
+ { "px": [80,48], "src": [16,32], "f": 0, "t": 65, "d": [13,94], "a": 1 },
+ { "px": [128,32], "src": [0,16], "f": 0, "t": 32, "d": [13,98], "a": 1 },
+ { "px": [128,48], "src": [0,32], "f": 0, "t": 64, "d": [13,98], "a": 1 },
+ { "px": [144,32], "src": [16,16], "f": 0, "t": 33, "d": [13,98], "a": 1 },
+ { "px": [144,48], "src": [16,32], "f": 0, "t": 65, "d": [13,98], "a": 1 },
+ { "px": [192,32], "src": [0,16], "f": 0, "t": 32, "d": [13,102], "a": 1 },
+ { "px": [192,48], "src": [0,32], "f": 0, "t": 64, "d": [13,102], "a": 1 },
+ { "px": [208,32], "src": [16,16], "f": 0, "t": 33, "d": [13,102], "a": 1 },
+ { "px": [208,48], "src": [16,32], "f": 0, "t": 65, "d": [13,102], "a": 1 },
+ { "px": [320,32], "src": [0,16], "f": 0, "t": 32, "d": [13,110], "a": 1 },
+ { "px": [320,48], "src": [0,32], "f": 0, "t": 64, "d": [13,110], "a": 1 },
+ { "px": [336,32], "src": [16,16], "f": 0, "t": 33, "d": [13,110], "a": 1 },
+ { "px": [336,48], "src": [16,32], "f": 0, "t": 65, "d": [13,110], "a": 1 },
+ { "px": [384,32], "src": [0,16], "f": 0, "t": 32, "d": [13,114], "a": 1 },
+ { "px": [384,48], "src": [0,32], "f": 0, "t": 64, "d": [13,114], "a": 1 },
+ { "px": [400,32], "src": [16,16], "f": 0, "t": 33, "d": [13,114], "a": 1 },
+ { "px": [400,48], "src": [16,32], "f": 0, "t": 65, "d": [13,114], "a": 1 },
+ { "px": [640,32], "src": [0,16], "f": 0, "t": 32, "d": [13,130], "a": 1 },
+ { "px": [640,48], "src": [0,32], "f": 0, "t": 64, "d": [13,130], "a": 1 },
+ { "px": [656,32], "src": [16,16], "f": 0, "t": 33, "d": [13,130], "a": 1 },
+ { "px": [656,48], "src": [16,32], "f": 0, "t": 65, "d": [13,130], "a": 1 },
+ { "px": [160,48], "src": [0,16], "f": 0, "t": 32, "d": [13,145], "a": 1 },
+ { "px": [160,64], "src": [0,32], "f": 0, "t": 64, "d": [13,145], "a": 1 },
+ { "px": [176,48], "src": [16,16], "f": 0, "t": 33, "d": [13,145], "a": 1 },
+ { "px": [176,64], "src": [16,32], "f": 0, "t": 65, "d": [13,145], "a": 1 },
+ { "px": [224,48], "src": [0,16], "f": 0, "t": 32, "d": [13,149], "a": 1 },
+ { "px": [224,64], "src": [0,32], "f": 0, "t": 64, "d": [13,149], "a": 1 },
+ { "px": [240,48], "src": [16,16], "f": 0, "t": 33, "d": [13,149], "a": 1 },
+ { "px": [240,64], "src": [16,32], "f": 0, "t": 65, "d": [13,149], "a": 1 },
+ { "px": [288,48], "src": [0,16], "f": 0, "t": 32, "d": [13,153], "a": 1 },
+ { "px": [288,64], "src": [0,32], "f": 0, "t": 64, "d": [13,153], "a": 1 },
+ { "px": [304,48], "src": [16,16], "f": 0, "t": 33, "d": [13,153], "a": 1 },
+ { "px": [304,64], "src": [16,32], "f": 0, "t": 65, "d": [13,153], "a": 1 },
+ { "px": [544,48], "src": [0,16], "f": 0, "t": 32, "d": [13,169], "a": 1 },
+ { "px": [544,64], "src": [0,32], "f": 0, "t": 64, "d": [13,169], "a": 1 },
+ { "px": [560,48], "src": [16,16], "f": 0, "t": 33, "d": [13,169], "a": 1 },
+ { "px": [560,64], "src": [16,32], "f": 0, "t": 65, "d": [13,169], "a": 1 },
+ { "px": [608,48], "src": [0,16], "f": 0, "t": 32, "d": [13,173], "a": 1 },
+ { "px": [608,64], "src": [0,32], "f": 0, "t": 64, "d": [13,173], "a": 1 },
+ { "px": [624,48], "src": [16,16], "f": 0, "t": 33, "d": [13,173], "a": 1 },
+ { "px": [624,64], "src": [16,32], "f": 0, "t": 65, "d": [13,173], "a": 1 },
+ { "px": [64,64], "src": [0,16], "f": 0, "t": 32, "d": [13,184], "a": 1 },
+ { "px": [64,80], "src": [0,32], "f": 0, "t": 64, "d": [13,184], "a": 1 },
+ { "px": [80,64], "src": [16,16], "f": 0, "t": 33, "d": [13,184], "a": 1 },
+ { "px": [80,80], "src": [16,32], "f": 0, "t": 65, "d": [13,184], "a": 1 },
+ { "px": [192,64], "src": [0,16], "f": 0, "t": 32, "d": [13,192], "a": 1 },
+ { "px": [192,80], "src": [0,32], "f": 0, "t": 64, "d": [13,192], "a": 1 },
+ { "px": [208,64], "src": [16,16], "f": 0, "t": 33, "d": [13,192], "a": 1 },
+ { "px": [208,80], "src": [16,32], "f": 0, "t": 65, "d": [13,192], "a": 1 },
+ { "px": [640,64], "src": [0,16], "f": 0, "t": 32, "d": [13,220], "a": 1 },
+ { "px": [640,80], "src": [0,32], "f": 0, "t": 64, "d": [13,220], "a": 1 },
+ { "px": [656,64], "src": [16,16], "f": 0, "t": 33, "d": [13,220], "a": 1 },
+ { "px": [656,80], "src": [16,32], "f": 0, "t": 65, "d": [13,220], "a": 1 },
+ { "px": [32,80], "src": [0,16], "f": 0, "t": 32, "d": [13,227], "a": 1 },
+ { "px": [32,96], "src": [0,32], "f": 0, "t": 64, "d": [13,227], "a": 1 },
+ { "px": [48,80], "src": [16,16], "f": 0, "t": 33, "d": [13,227], "a": 1 },
+ { "px": [48,96], "src": [16,32], "f": 0, "t": 65, "d": [13,227], "a": 1 },
+ { "px": [96,80], "src": [0,16], "f": 0, "t": 32, "d": [13,231], "a": 1 },
+ { "px": [96,96], "src": [0,32], "f": 0, "t": 64, "d": [13,231], "a": 1 },
+ { "px": [112,80], "src": [16,16], "f": 0, "t": 33, "d": [13,231], "a": 1 },
+ { "px": [112,96], "src": [16,32], "f": 0, "t": 65, "d": [13,231], "a": 1 },
+ { "px": [288,80], "src": [0,16], "f": 0, "t": 32, "d": [13,243], "a": 1 },
+ { "px": [288,96], "src": [0,32], "f": 0, "t": 64, "d": [13,243], "a": 1 },
+ { "px": [304,80], "src": [16,16], "f": 0, "t": 33, "d": [13,243], "a": 1 },
+ { "px": [304,96], "src": [16,32], "f": 0, "t": 65, "d": [13,243], "a": 1 },
+ { "px": [352,80], "src": [0,16], "f": 0, "t": 32, "d": [13,247], "a": 1 },
+ { "px": [352,96], "src": [0,32], "f": 0, "t": 64, "d": [13,247], "a": 1 },
+ { "px": [368,80], "src": [16,16], "f": 0, "t": 33, "d": [13,247], "a": 1 },
+ { "px": [368,96], "src": [16,32], "f": 0, "t": 65, "d": [13,247], "a": 1 },
+ { "px": [416,80], "src": [0,16], "f": 0, "t": 32, "d": [13,251], "a": 1 },
+ { "px": [416,96], "src": [0,32], "f": 0, "t": 64, "d": [13,251], "a": 1 },
+ { "px": [432,80], "src": [16,16], "f": 0, "t": 33, "d": [13,251], "a": 1 },
+ { "px": [432,96], "src": [16,32], "f": 0, "t": 65, "d": [13,251], "a": 1 },
+ { "px": [480,80], "src": [0,16], "f": 0, "t": 32, "d": [13,255], "a": 1 },
+ { "px": [480,96], "src": [0,32], "f": 0, "t": 64, "d": [13,255], "a": 1 },
+ { "px": [496,80], "src": [16,16], "f": 0, "t": 33, "d": [13,255], "a": 1 },
+ { "px": [496,96], "src": [16,32], "f": 0, "t": 65, "d": [13,255], "a": 1 },
+ { "px": [608,80], "src": [0,16], "f": 0, "t": 32, "d": [13,263], "a": 1 },
+ { "px": [608,96], "src": [0,32], "f": 0, "t": 64, "d": [13,263], "a": 1 },
+ { "px": [624,80], "src": [16,16], "f": 0, "t": 33, "d": [13,263], "a": 1 },
+ { "px": [624,96], "src": [16,32], "f": 0, "t": 65, "d": [13,263], "a": 1 },
+ { "px": [0,96], "src": [0,16], "f": 0, "t": 32, "d": [13,270], "a": 1 },
+ { "px": [0,112], "src": [0,32], "f": 0, "t": 64, "d": [13,270], "a": 1 },
+ { "px": [16,96], "src": [16,16], "f": 0, "t": 33, "d": [13,270], "a": 1 },
+ { "px": [16,112], "src": [16,32], "f": 0, "t": 65, "d": [13,270], "a": 1 },
+ { "px": [128,96], "src": [0,16], "f": 0, "t": 32, "d": [13,278], "a": 1 },
+ { "px": [128,112], "src": [0,32], "f": 0, "t": 64, "d": [13,278], "a": 1 },
+ { "px": [144,96], "src": [16,16], "f": 0, "t": 33, "d": [13,278], "a": 1 },
+ { "px": [144,112], "src": [16,32], "f": 0, "t": 65, "d": [13,278], "a": 1 },
+ { "px": [576,96], "src": [0,16], "f": 0, "t": 32, "d": [13,306], "a": 1 },
+ { "px": [576,112], "src": [0,32], "f": 0, "t": 64, "d": [13,306], "a": 1 },
+ { "px": [592,96], "src": [16,16], "f": 0, "t": 33, "d": [13,306], "a": 1 },
+ { "px": [592,112], "src": [16,32], "f": 0, "t": 65, "d": [13,306], "a": 1 },
+ { "px": [640,96], "src": [0,16], "f": 0, "t": 32, "d": [13,310], "a": 1 },
+ { "px": [640,112], "src": [0,32], "f": 0, "t": 64, "d": [13,310], "a": 1 },
+ { "px": [656,96], "src": [16,16], "f": 0, "t": 33, "d": [13,310], "a": 1 },
+ { "px": [656,112], "src": [16,32], "f": 0, "t": 65, "d": [13,310], "a": 1 },
+ { "px": [32,112], "src": [0,16], "f": 0, "t": 32, "d": [13,317], "a": 1 },
+ { "px": [32,128], "src": [0,32], "f": 0, "t": 64, "d": [13,317], "a": 1 },
+ { "px": [48,112], "src": [16,16], "f": 0, "t": 33, "d": [13,317], "a": 1 },
+ { "px": [48,128], "src": [16,32], "f": 0, "t": 65, "d": [13,317], "a": 1 },
+ { "px": [160,112], "src": [0,16], "f": 0, "t": 32, "d": [13,325], "a": 1 },
+ { "px": [160,128], "src": [0,32], "f": 0, "t": 64, "d": [13,325], "a": 1 },
+ { "px": [176,112], "src": [16,16], "f": 0, "t": 33, "d": [13,325], "a": 1 },
+ { "px": [176,128], "src": [16,32], "f": 0, "t": 65, "d": [13,325], "a": 1 },
+ { "px": [352,112], "src": [0,16], "f": 0, "t": 32, "d": [13,337], "a": 1 },
+ { "px": [352,128], "src": [0,32], "f": 0, "t": 64, "d": [13,337], "a": 1 },
+ { "px": [368,112], "src": [16,16], "f": 0, "t": 33, "d": [13,337], "a": 1 },
+ { "px": [368,128], "src": [16,32], "f": 0, "t": 65, "d": [13,337], "a": 1 },
+ { "px": [480,112], "src": [0,16], "f": 0, "t": 32, "d": [13,345], "a": 1 },
+ { "px": [480,128], "src": [0,32], "f": 0, "t": 64, "d": [13,345], "a": 1 },
+ { "px": [496,112], "src": [16,16], "f": 0, "t": 33, "d": [13,345], "a": 1 },
+ { "px": [496,128], "src": [16,32], "f": 0, "t": 65, "d": [13,345], "a": 1 },
+ { "px": [608,112], "src": [0,16], "f": 0, "t": 32, "d": [13,353], "a": 1 },
+ { "px": [608,128], "src": [0,32], "f": 0, "t": 64, "d": [13,353], "a": 1 },
+ { "px": [624,112], "src": [16,16], "f": 0, "t": 33, "d": [13,353], "a": 1 },
+ { "px": [624,128], "src": [16,32], "f": 0, "t": 65, "d": [13,353], "a": 1 },
+ { "px": [64,128], "src": [0,16], "f": 0, "t": 32, "d": [13,364], "a": 1 },
+ { "px": [64,144], "src": [0,32], "f": 0, "t": 64, "d": [13,364], "a": 1 },
+ { "px": [80,128], "src": [16,16], "f": 0, "t": 33, "d": [13,364], "a": 1 },
+ { "px": [80,144], "src": [16,32], "f": 0, "t": 65, "d": [13,364], "a": 1 },
+ { "px": [128,128], "src": [0,16], "f": 0, "t": 32, "d": [13,368], "a": 1 },
+ { "px": [128,144], "src": [0,32], "f": 0, "t": 64, "d": [13,368], "a": 1 },
+ { "px": [144,128], "src": [16,16], "f": 0, "t": 33, "d": [13,368], "a": 1 },
+ { "px": [144,144], "src": [16,32], "f": 0, "t": 65, "d": [13,368], "a": 1 },
+ { "px": [576,128], "src": [0,16], "f": 0, "t": 32, "d": [13,396], "a": 1 },
+ { "px": [576,144], "src": [0,32], "f": 0, "t": 64, "d": [13,396], "a": 1 },
+ { "px": [592,128], "src": [16,16], "f": 0, "t": 33, "d": [13,396], "a": 1 },
+ { "px": [592,144], "src": [16,32], "f": 0, "t": 65, "d": [13,396], "a": 1 },
+ { "px": [672,144], "src": [0,16], "f": 0, "t": 32, "d": [13,447], "a": 1 },
+ { "px": [672,160], "src": [0,32], "f": 0, "t": 64, "d": [13,447], "a": 1 },
+ { "px": [688,144], "src": [16,16], "f": 0, "t": 33, "d": [13,447], "a": 1 },
+ { "px": [688,160], "src": [16,32], "f": 0, "t": 65, "d": [13,447], "a": 1 },
+ { "px": [64,160], "src": [0,16], "f": 0, "t": 32, "d": [13,454], "a": 1 },
+ { "px": [64,176], "src": [0,32], "f": 0, "t": 64, "d": [13,454], "a": 1 },
+ { "px": [80,160], "src": [16,16], "f": 0, "t": 33, "d": [13,454], "a": 1 },
+ { "px": [80,176], "src": [16,32], "f": 0, "t": 65, "d": [13,454], "a": 1 },
+ { "px": [576,160], "src": [0,16], "f": 0, "t": 32, "d": [13,486], "a": 1 },
+ { "px": [576,176], "src": [0,32], "f": 0, "t": 64, "d": [13,486], "a": 1 },
+ { "px": [592,160], "src": [16,16], "f": 0, "t": 33, "d": [13,486], "a": 1 },
+ { "px": [592,176], "src": [16,32], "f": 0, "t": 65, "d": [13,486], "a": 1 },
+ { "px": [640,160], "src": [0,16], "f": 0, "t": 32, "d": [13,490], "a": 1 },
+ { "px": [640,176], "src": [0,32], "f": 0, "t": 64, "d": [13,490], "a": 1 },
+ { "px": [656,160], "src": [16,16], "f": 0, "t": 33, "d": [13,490], "a": 1 },
+ { "px": [656,176], "src": [16,32], "f": 0, "t": 65, "d": [13,490], "a": 1 },
+ { "px": [32,176], "src": [0,16], "f": 0, "t": 32, "d": [13,497], "a": 1 },
+ { "px": [32,192], "src": [0,32], "f": 0, "t": 64, "d": [13,497], "a": 1 },
+ { "px": [48,176], "src": [16,16], "f": 0, "t": 33, "d": [13,497], "a": 1 },
+ { "px": [48,192], "src": [16,32], "f": 0, "t": 65, "d": [13,497], "a": 1 },
+ { "px": [0,192], "src": [0,16], "f": 0, "t": 32, "d": [13,540], "a": 1 },
+ { "px": [0,208], "src": [0,32], "f": 0, "t": 64, "d": [13,540], "a": 1 },
+ { "px": [16,192], "src": [16,16], "f": 0, "t": 33, "d": [13,540], "a": 1 },
+ { "px": [16,208], "src": [16,32], "f": 0, "t": 65, "d": [13,540], "a": 1 },
+ { "px": [64,192], "src": [0,16], "f": 0, "t": 32, "d": [13,544], "a": 1 },
+ { "px": [64,208], "src": [0,32], "f": 0, "t": 64, "d": [13,544], "a": 1 },
+ { "px": [80,192], "src": [16,16], "f": 0, "t": 33, "d": [13,544], "a": 1 },
+ { "px": [80,208], "src": [16,32], "f": 0, "t": 65, "d": [13,544], "a": 1 },
+ { "px": [128,192], "src": [0,16], "f": 0, "t": 32, "d": [13,548], "a": 1 },
+ { "px": [128,208], "src": [0,32], "f": 0, "t": 64, "d": [13,548], "a": 1 },
+ { "px": [144,192], "src": [16,16], "f": 0, "t": 33, "d": [13,548], "a": 1 },
+ { "px": [144,208], "src": [16,32], "f": 0, "t": 65, "d": [13,548], "a": 1 },
+ { "px": [640,192], "src": [0,16], "f": 0, "t": 32, "d": [13,580], "a": 1 },
+ { "px": [640,208], "src": [0,32], "f": 0, "t": 64, "d": [13,580], "a": 1 },
+ { "px": [656,192], "src": [16,16], "f": 0, "t": 33, "d": [13,580], "a": 1 },
+ { "px": [656,208], "src": [16,32], "f": 0, "t": 65, "d": [13,580], "a": 1 },
+ { "px": [96,208], "src": [0,16], "f": 0, "t": 32, "d": [13,591], "a": 1 },
+ { "px": [96,224], "src": [0,32], "f": 0, "t": 64, "d": [13,591], "a": 1 },
+ { "px": [112,208], "src": [16,16], "f": 0, "t": 33, "d": [13,591], "a": 1 },
+ { "px": [112,224], "src": [16,32], "f": 0, "t": 65, "d": [13,591], "a": 1 },
+ { "px": [608,208], "src": [0,16], "f": 0, "t": 32, "d": [13,623], "a": 1 },
+ { "px": [608,224], "src": [0,32], "f": 0, "t": 64, "d": [13,623], "a": 1 },
+ { "px": [624,208], "src": [16,16], "f": 0, "t": 33, "d": [13,623], "a": 1 },
+ { "px": [624,224], "src": [16,32], "f": 0, "t": 65, "d": [13,623], "a": 1 },
+ { "px": [672,208], "src": [0,16], "f": 0, "t": 32, "d": [13,627], "a": 1 },
+ { "px": [672,224], "src": [0,32], "f": 0, "t": 64, "d": [13,627], "a": 1 },
+ { "px": [688,208], "src": [16,16], "f": 0, "t": 33, "d": [13,627], "a": 1 },
+ { "px": [688,224], "src": [16,32], "f": 0, "t": 65, "d": [13,627], "a": 1 },
+ { "px": [576,224], "src": [0,16], "f": 0, "t": 32, "d": [13,666], "a": 1 },
+ { "px": [576,240], "src": [0,32], "f": 0, "t": 64, "d": [13,666], "a": 1 },
+ { "px": [592,224], "src": [16,16], "f": 0, "t": 33, "d": [13,666], "a": 1 },
+ { "px": [592,240], "src": [16,32], "f": 0, "t": 65, "d": [13,666], "a": 1 },
+ { "px": [32,240], "src": [0,16], "f": 0, "t": 32, "d": [13,677], "a": 1 },
+ { "px": [32,256], "src": [0,32], "f": 0, "t": 64, "d": [13,677], "a": 1 },
+ { "px": [48,240], "src": [16,16], "f": 0, "t": 33, "d": [13,677], "a": 1 },
+ { "px": [48,256], "src": [16,32], "f": 0, "t": 65, "d": [13,677], "a": 1 },
+ { "px": [256,256], "src": [0,16], "f": 0, "t": 32, "d": [13,736], "a": 1 },
+ { "px": [256,272], "src": [0,32], "f": 0, "t": 64, "d": [13,736], "a": 1 },
+ { "px": [272,256], "src": [16,16], "f": 0, "t": 33, "d": [13,736], "a": 1 },
+ { "px": [272,272], "src": [16,32], "f": 0, "t": 65, "d": [13,736], "a": 1 },
+ { "px": [320,256], "src": [0,16], "f": 0, "t": 32, "d": [13,740], "a": 1 },
+ { "px": [320,272], "src": [0,32], "f": 0, "t": 64, "d": [13,740], "a": 1 },
+ { "px": [336,256], "src": [16,16], "f": 0, "t": 33, "d": [13,740], "a": 1 },
+ { "px": [336,272], "src": [16,32], "f": 0, "t": 65, "d": [13,740], "a": 1 },
+ { "px": [384,256], "src": [0,16], "f": 0, "t": 32, "d": [13,744], "a": 1 },
+ { "px": [384,272], "src": [0,32], "f": 0, "t": 64, "d": [13,744], "a": 1 },
+ { "px": [400,256], "src": [16,16], "f": 0, "t": 33, "d": [13,744], "a": 1 },
+ { "px": [400,272], "src": [16,32], "f": 0, "t": 65, "d": [13,744], "a": 1 },
+ { "px": [448,256], "src": [0,16], "f": 0, "t": 32, "d": [13,748], "a": 1 },
+ { "px": [448,272], "src": [0,32], "f": 0, "t": 64, "d": [13,748], "a": 1 },
+ { "px": [464,256], "src": [16,16], "f": 0, "t": 33, "d": [13,748], "a": 1 },
+ { "px": [464,272], "src": [16,32], "f": 0, "t": 65, "d": [13,748], "a": 1 },
+ { "px": [576,256], "src": [0,16], "f": 0, "t": 32, "d": [13,756], "a": 1 },
+ { "px": [576,272], "src": [0,32], "f": 0, "t": 64, "d": [13,756], "a": 1 },
+ { "px": [592,256], "src": [16,16], "f": 0, "t": 33, "d": [13,756], "a": 1 },
+ { "px": [592,272], "src": [16,32], "f": 0, "t": 65, "d": [13,756], "a": 1 },
+ { "px": [640,256], "src": [0,16], "f": 0, "t": 32, "d": [13,760], "a": 1 },
+ { "px": [640,272], "src": [0,32], "f": 0, "t": 64, "d": [13,760], "a": 1 },
+ { "px": [656,256], "src": [16,16], "f": 0, "t": 33, "d": [13,760], "a": 1 },
+ { "px": [656,272], "src": [16,32], "f": 0, "t": 65, "d": [13,760], "a": 1 },
+ { "px": [32,272], "src": [0,16], "f": 0, "t": 32, "d": [13,767], "a": 1 },
+ { "px": [32,288], "src": [0,32], "f": 0, "t": 64, "d": [13,767], "a": 1 },
+ { "px": [48,272], "src": [16,16], "f": 0, "t": 33, "d": [13,767], "a": 1 },
+ { "px": [48,288], "src": [16,32], "f": 0, "t": 65, "d": [13,767], "a": 1 },
+ { "px": [96,272], "src": [0,16], "f": 0, "t": 32, "d": [13,771], "a": 1 },
+ { "px": [96,288], "src": [0,32], "f": 0, "t": 64, "d": [13,771], "a": 1 },
+ { "px": [112,272], "src": [16,16], "f": 0, "t": 33, "d": [13,771], "a": 1 },
+ { "px": [112,288], "src": [16,32], "f": 0, "t": 65, "d": [13,771], "a": 1 },
+ { "px": [416,272], "src": [0,16], "f": 0, "t": 32, "d": [13,791], "a": 1 },
+ { "px": [416,288], "src": [0,32], "f": 0, "t": 64, "d": [13,791], "a": 1 },
+ { "px": [432,272], "src": [16,16], "f": 0, "t": 33, "d": [13,791], "a": 1 },
+ { "px": [432,288], "src": [16,32], "f": 0, "t": 65, "d": [13,791], "a": 1 },
+ { "px": [608,272], "src": [0,16], "f": 0, "t": 32, "d": [13,803], "a": 1 },
+ { "px": [608,288], "src": [0,32], "f": 0, "t": 64, "d": [13,803], "a": 1 },
+ { "px": [624,272], "src": [16,16], "f": 0, "t": 33, "d": [13,803], "a": 1 },
+ { "px": [624,288], "src": [16,32], "f": 0, "t": 65, "d": [13,803], "a": 1 },
+ { "px": [64,288], "src": [0,16], "f": 0, "t": 32, "d": [13,814], "a": 1 },
+ { "px": [64,304], "src": [0,32], "f": 0, "t": 64, "d": [13,814], "a": 1 },
+ { "px": [80,288], "src": [16,16], "f": 0, "t": 33, "d": [13,814], "a": 1 },
+ { "px": [80,304], "src": [16,32], "f": 0, "t": 65, "d": [13,814], "a": 1 },
+ { "px": [128,288], "src": [0,16], "f": 0, "t": 32, "d": [13,818], "a": 1 },
+ { "px": [128,304], "src": [0,32], "f": 0, "t": 64, "d": [13,818], "a": 1 },
+ { "px": [144,288], "src": [16,16], "f": 0, "t": 33, "d": [13,818], "a": 1 },
+ { "px": [144,304], "src": [16,32], "f": 0, "t": 65, "d": [13,818], "a": 1 },
+ { "px": [640,288], "src": [0,16], "f": 0, "t": 32, "d": [13,850], "a": 1 },
+ { "px": [640,304], "src": [0,32], "f": 0, "t": 64, "d": [13,850], "a": 1 },
+ { "px": [656,288], "src": [16,16], "f": 0, "t": 33, "d": [13,850], "a": 1 },
+ { "px": [656,304], "src": [16,32], "f": 0, "t": 65, "d": [13,850], "a": 1 },
+ { "px": [32,304], "src": [0,16], "f": 0, "t": 32, "d": [13,857], "a": 1 },
+ { "px": [32,320], "src": [0,32], "f": 0, "t": 64, "d": [13,857], "a": 1 },
+ { "px": [48,304], "src": [16,16], "f": 0, "t": 33, "d": [13,857], "a": 1 },
+ { "px": [48,320], "src": [16,32], "f": 0, "t": 65, "d": [13,857], "a": 1 },
+ { "px": [288,304], "src": [0,16], "f": 0, "t": 32, "d": [13,873], "a": 1 },
+ { "px": [288,320], "src": [0,32], "f": 0, "t": 64, "d": [13,873], "a": 1 },
+ { "px": [304,304], "src": [16,16], "f": 0, "t": 33, "d": [13,873], "a": 1 },
+ { "px": [304,320], "src": [16,32], "f": 0, "t": 65, "d": [13,873], "a": 1 },
+ { "px": [416,304], "src": [0,16], "f": 0, "t": 32, "d": [13,881], "a": 1 },
+ { "px": [416,320], "src": [0,32], "f": 0, "t": 64, "d": [13,881], "a": 1 },
+ { "px": [432,304], "src": [16,16], "f": 0, "t": 33, "d": [13,881], "a": 1 },
+ { "px": [432,320], "src": [16,32], "f": 0, "t": 65, "d": [13,881], "a": 1 },
+ { "px": [256,320], "src": [0,16], "f": 0, "t": 32, "d": [13,916], "a": 1 },
+ { "px": [256,336], "src": [0,32], "f": 0, "t": 64, "d": [13,916], "a": 1 },
+ { "px": [272,320], "src": [16,16], "f": 0, "t": 33, "d": [13,916], "a": 1 },
+ { "px": [272,336], "src": [16,32], "f": 0, "t": 65, "d": [13,916], "a": 1 },
+ { "px": [384,320], "src": [0,16], "f": 0, "t": 32, "d": [13,924], "a": 1 },
+ { "px": [384,336], "src": [0,32], "f": 0, "t": 64, "d": [13,924], "a": 1 },
+ { "px": [400,320], "src": [16,16], "f": 0, "t": 33, "d": [13,924], "a": 1 },
+ { "px": [400,336], "src": [16,32], "f": 0, "t": 65, "d": [13,924], "a": 1 },
+ { "px": [576,320], "src": [0,16], "f": 0, "t": 32, "d": [13,936], "a": 1 },
+ { "px": [576,336], "src": [0,32], "f": 0, "t": 64, "d": [13,936], "a": 1 },
+ { "px": [592,320], "src": [16,16], "f": 0, "t": 33, "d": [13,936], "a": 1 },
+ { "px": [592,336], "src": [16,32], "f": 0, "t": 65, "d": [13,936], "a": 1 },
+ { "px": [32,336], "src": [0,16], "f": 0, "t": 32, "d": [13,947], "a": 1 },
+ { "px": [32,352], "src": [0,32], "f": 0, "t": 64, "d": [13,947], "a": 1 },
+ { "px": [48,336], "src": [16,16], "f": 0, "t": 33, "d": [13,947], "a": 1 },
+ { "px": [48,352], "src": [16,32], "f": 0, "t": 65, "d": [13,947], "a": 1 },
+ { "px": [96,336], "src": [0,16], "f": 0, "t": 32, "d": [13,951], "a": 1 },
+ { "px": [96,352], "src": [0,32], "f": 0, "t": 64, "d": [13,951], "a": 1 },
+ { "px": [112,336], "src": [16,16], "f": 0, "t": 33, "d": [13,951], "a": 1 },
+ { "px": [112,352], "src": [16,32], "f": 0, "t": 65, "d": [13,951], "a": 1 },
+ { "px": [608,336], "src": [0,16], "f": 0, "t": 32, "d": [13,983], "a": 1 },
+ { "px": [608,352], "src": [0,32], "f": 0, "t": 64, "d": [13,983], "a": 1 },
+ { "px": [624,336], "src": [16,16], "f": 0, "t": 33, "d": [13,983], "a": 1 },
+ { "px": [624,352], "src": [16,32], "f": 0, "t": 65, "d": [13,983], "a": 1 },
+ { "px": [672,336], "src": [0,16], "f": 0, "t": 32, "d": [13,987], "a": 1 },
+ { "px": [672,352], "src": [0,32], "f": 0, "t": 64, "d": [13,987], "a": 1 },
+ { "px": [688,336], "src": [16,16], "f": 0, "t": 33, "d": [13,987], "a": 1 },
+ { "px": [688,352], "src": [16,32], "f": 0, "t": 65, "d": [13,987], "a": 1 },
+ { "px": [0,352], "src": [0,16], "f": 0, "t": 32, "d": [13,990], "a": 1 },
+ { "px": [0,368], "src": [0,32], "f": 0, "t": 64, "d": [13,990], "a": 1 },
+ { "px": [16,352], "src": [16,16], "f": 0, "t": 33, "d": [13,990], "a": 1 },
+ { "px": [16,368], "src": [16,32], "f": 0, "t": 65, "d": [13,990], "a": 1 },
+ { "px": [128,352], "src": [0,16], "f": 0, "t": 32, "d": [13,998], "a": 1 },
+ { "px": [128,368], "src": [0,32], "f": 0, "t": 64, "d": [13,998], "a": 1 },
+ { "px": [144,352], "src": [16,16], "f": 0, "t": 33, "d": [13,998], "a": 1 },
+ { "px": [144,368], "src": [16,32], "f": 0, "t": 65, "d": [13,998], "a": 1 },
+ { "px": [576,352], "src": [0,16], "f": 0, "t": 32, "d": [13,1026], "a": 1 },
+ { "px": [576,368], "src": [0,32], "f": 0, "t": 64, "d": [13,1026], "a": 1 },
+ { "px": [592,352], "src": [16,16], "f": 0, "t": 33, "d": [13,1026], "a": 1 },
+ { "px": [592,368], "src": [16,32], "f": 0, "t": 65, "d": [13,1026], "a": 1 },
+ { "px": [96,368], "src": [0,16], "f": 0, "t": 32, "d": [13,1041], "a": 1 },
+ { "px": [96,384], "src": [0,32], "f": 0, "t": 64, "d": [13,1041], "a": 1 },
+ { "px": [112,368], "src": [16,16], "f": 0, "t": 33, "d": [13,1041], "a": 1 },
+ { "px": [112,384], "src": [16,32], "f": 0, "t": 65, "d": [13,1041], "a": 1 },
+ { "px": [608,368], "src": [0,16], "f": 0, "t": 32, "d": [13,1073], "a": 1 },
+ { "px": [608,384], "src": [0,32], "f": 0, "t": 64, "d": [13,1073], "a": 1 },
+ { "px": [624,368], "src": [16,16], "f": 0, "t": 33, "d": [13,1073], "a": 1 },
+ { "px": [624,384], "src": [16,32], "f": 0, "t": 65, "d": [13,1073], "a": 1 },
+ { "px": [0,384], "src": [0,16], "f": 0, "t": 32, "d": [13,1080], "a": 1 },
+ { "px": [0,400], "src": [0,32], "f": 0, "t": 64, "d": [13,1080], "a": 1 },
+ { "px": [16,384], "src": [16,16], "f": 0, "t": 33, "d": [13,1080], "a": 1 },
+ { "px": [16,400], "src": [16,32], "f": 0, "t": 65, "d": [13,1080], "a": 1 },
+ { "px": [64,384], "src": [0,16], "f": 0, "t": 32, "d": [13,1084], "a": 1 },
+ { "px": [64,400], "src": [0,32], "f": 0, "t": 64, "d": [13,1084], "a": 1 },
+ { "px": [80,384], "src": [16,16], "f": 0, "t": 33, "d": [13,1084], "a": 1 },
+ { "px": [80,400], "src": [16,32], "f": 0, "t": 65, "d": [13,1084], "a": 1 },
+ { "px": [640,384], "src": [0,16], "f": 0, "t": 32, "d": [13,1120], "a": 1 },
+ { "px": [640,400], "src": [0,32], "f": 0, "t": 64, "d": [13,1120], "a": 1 },
+ { "px": [656,384], "src": [16,16], "f": 0, "t": 33, "d": [13,1120], "a": 1 },
+ { "px": [656,400], "src": [16,32], "f": 0, "t": 65, "d": [13,1120], "a": 1 },
+ { "px": [32,400], "src": [0,16], "f": 0, "t": 32, "d": [13,1127], "a": 1 },
+ { "px": [32,416], "src": [0,32], "f": 0, "t": 64, "d": [13,1127], "a": 1 },
+ { "px": [48,400], "src": [16,16], "f": 0, "t": 33, "d": [13,1127], "a": 1 },
+ { "px": [48,416], "src": [16,32], "f": 0, "t": 65, "d": [13,1127], "a": 1 },
+ { "px": [96,400], "src": [0,16], "f": 0, "t": 32, "d": [13,1131], "a": 1 },
+ { "px": [96,416], "src": [0,32], "f": 0, "t": 64, "d": [13,1131], "a": 1 },
+ { "px": [112,400], "src": [16,16], "f": 0, "t": 33, "d": [13,1131], "a": 1 },
+ { "px": [112,416], "src": [16,32], "f": 0, "t": 65, "d": [13,1131], "a": 1 },
+ { "px": [608,400], "src": [0,16], "f": 0, "t": 32, "d": [13,1163], "a": 1 },
+ { "px": [608,416], "src": [0,32], "f": 0, "t": 64, "d": [13,1163], "a": 1 },
+ { "px": [624,400], "src": [16,16], "f": 0, "t": 33, "d": [13,1163], "a": 1 },
+ { "px": [624,416], "src": [16,32], "f": 0, "t": 65, "d": [13,1163], "a": 1 },
+ { "px": [672,400], "src": [0,16], "f": 0, "t": 32, "d": [13,1167], "a": 1 },
+ { "px": [672,416], "src": [0,32], "f": 0, "t": 64, "d": [13,1167], "a": 1 },
+ { "px": [688,400], "src": [16,16], "f": 0, "t": 33, "d": [13,1167], "a": 1 },
+ { "px": [688,416], "src": [16,32], "f": 0, "t": 65, "d": [13,1167], "a": 1 },
+ { "px": [0,416], "src": [0,16], "f": 0, "t": 32, "d": [13,1170], "a": 1 },
+ { "px": [0,432], "src": [0,32], "f": 0, "t": 64, "d": [13,1170], "a": 1 },
+ { "px": [16,416], "src": [16,16], "f": 0, "t": 33, "d": [13,1170], "a": 1 },
+ { "px": [16,432], "src": [16,32], "f": 0, "t": 65, "d": [13,1170], "a": 1 },
+ { "px": [128,416], "src": [0,16], "f": 0, "t": 32, "d": [13,1178], "a": 1 },
+ { "px": [128,432], "src": [0,32], "f": 0, "t": 64, "d": [13,1178], "a": 1 },
+ { "px": [144,416], "src": [16,16], "f": 0, "t": 33, "d": [13,1178], "a": 1 },
+ { "px": [144,432], "src": [16,32], "f": 0, "t": 65, "d": [13,1178], "a": 1 },
+ { "px": [192,416], "src": [0,16], "f": 0, "t": 32, "d": [13,1182], "a": 1 },
+ { "px": [192,432], "src": [0,32], "f": 0, "t": 64, "d": [13,1182], "a": 1 },
+ { "px": [208,416], "src": [16,16], "f": 0, "t": 33, "d": [13,1182], "a": 1 },
+ { "px": [208,432], "src": [16,32], "f": 0, "t": 65, "d": [13,1182], "a": 1 },
+ { "px": [160,432], "src": [0,16], "f": 0, "t": 32, "d": [13,1225], "a": 1 },
+ { "px": [160,448], "src": [0,32], "f": 0, "t": 64, "d": [13,1225], "a": 1 },
+ { "px": [176,432], "src": [16,16], "f": 0, "t": 33, "d": [13,1225], "a": 1 },
+ { "px": [176,448], "src": [16,32], "f": 0, "t": 65, "d": [13,1225], "a": 1 },
+ { "px": [224,432], "src": [0,16], "f": 0, "t": 32, "d": [13,1229], "a": 1 },
+ { "px": [224,448], "src": [0,32], "f": 0, "t": 64, "d": [13,1229], "a": 1 },
+ { "px": [240,432], "src": [16,16], "f": 0, "t": 33, "d": [13,1229], "a": 1 },
+ { "px": [240,448], "src": [16,32], "f": 0, "t": 65, "d": [13,1229], "a": 1 },
+ { "px": [288,432], "src": [0,16], "f": 0, "t": 32, "d": [13,1233], "a": 1 },
+ { "px": [288,448], "src": [0,32], "f": 0, "t": 64, "d": [13,1233], "a": 1 },
+ { "px": [304,432], "src": [16,16], "f": 0, "t": 33, "d": [13,1233], "a": 1 },
+ { "px": [304,448], "src": [16,32], "f": 0, "t": 65, "d": [13,1233], "a": 1 },
+ { "px": [416,432], "src": [0,16], "f": 0, "t": 32, "d": [13,1241], "a": 1 },
+ { "px": [416,448], "src": [0,32], "f": 0, "t": 64, "d": [13,1241], "a": 1 },
+ { "px": [432,432], "src": [16,16], "f": 0, "t": 33, "d": [13,1241], "a": 1 },
+ { "px": [432,448], "src": [16,32], "f": 0, "t": 65, "d": [13,1241], "a": 1 },
+ { "px": [480,432], "src": [0,16], "f": 0, "t": 32, "d": [13,1245], "a": 1 },
+ { "px": [480,448], "src": [0,32], "f": 0, "t": 64, "d": [13,1245], "a": 1 },
+ { "px": [496,432], "src": [16,16], "f": 0, "t": 33, "d": [13,1245], "a": 1 },
+ { "px": [496,448], "src": [16,32], "f": 0, "t": 65, "d": [13,1245], "a": 1 },
+ { "px": [608,432], "src": [0,16], "f": 0, "t": 32, "d": [13,1253], "a": 1 },
+ { "px": [608,448], "src": [0,32], "f": 0, "t": 64, "d": [13,1253], "a": 1 },
+ { "px": [624,432], "src": [16,16], "f": 0, "t": 33, "d": [13,1253], "a": 1 },
+ { "px": [624,448], "src": [16,32], "f": 0, "t": 65, "d": [13,1253], "a": 1 },
+ { "px": [64,448], "src": [0,16], "f": 0, "t": 32, "d": [13,1264], "a": 1 },
+ { "px": [64,464], "src": [0,32], "f": 0, "t": 64, "d": [13,1264], "a": 1 },
+ { "px": [80,448], "src": [16,16], "f": 0, "t": 33, "d": [13,1264], "a": 1 },
+ { "px": [80,464], "src": [16,32], "f": 0, "t": 65, "d": [13,1264], "a": 1 },
+ { "px": [128,448], "src": [0,16], "f": 0, "t": 32, "d": [13,1268], "a": 1 },
+ { "px": [128,464], "src": [0,32], "f": 0, "t": 64, "d": [13,1268], "a": 1 },
+ { "px": [144,448], "src": [16,16], "f": 0, "t": 33, "d": [13,1268], "a": 1 },
+ { "px": [144,464], "src": [16,32], "f": 0, "t": 65, "d": [13,1268], "a": 1 },
+ { "px": [192,448], "src": [0,16], "f": 0, "t": 32, "d": [13,1272], "a": 1 },
+ { "px": [192,464], "src": [0,32], "f": 0, "t": 64, "d": [13,1272], "a": 1 },
+ { "px": [208,448], "src": [16,16], "f": 0, "t": 33, "d": [13,1272], "a": 1 },
+ { "px": [208,464], "src": [16,32], "f": 0, "t": 65, "d": [13,1272], "a": 1 },
+ { "px": [256,448], "src": [0,16], "f": 0, "t": 32, "d": [13,1276], "a": 1 },
+ { "px": [256,464], "src": [0,32], "f": 0, "t": 64, "d": [13,1276], "a": 1 },
+ { "px": [272,448], "src": [16,16], "f": 0, "t": 33, "d": [13,1276], "a": 1 },
+ { "px": [272,464], "src": [16,32], "f": 0, "t": 65, "d": [13,1276], "a": 1 },
+ { "px": [448,448], "src": [0,16], "f": 0, "t": 32, "d": [13,1288], "a": 1 },
+ { "px": [448,464], "src": [0,32], "f": 0, "t": 64, "d": [13,1288], "a": 1 },
+ { "px": [464,448], "src": [16,16], "f": 0, "t": 33, "d": [13,1288], "a": 1 },
+ { "px": [464,464], "src": [16,32], "f": 0, "t": 65, "d": [13,1288], "a": 1 },
+ { "px": [512,448], "src": [0,16], "f": 0, "t": 32, "d": [13,1292], "a": 1 },
+ { "px": [512,464], "src": [0,32], "f": 0, "t": 64, "d": [13,1292], "a": 1 },
+ { "px": [528,448], "src": [16,16], "f": 0, "t": 33, "d": [13,1292], "a": 1 },
+ { "px": [528,464], "src": [16,32], "f": 0, "t": 65, "d": [13,1292], "a": 1 },
+ { "px": [576,448], "src": [0,16], "f": 0, "t": 32, "d": [13,1296], "a": 1 },
+ { "px": [576,464], "src": [0,32], "f": 0, "t": 64, "d": [13,1296], "a": 1 },
+ { "px": [592,448], "src": [16,16], "f": 0, "t": 33, "d": [13,1296], "a": 1 },
+ { "px": [592,464], "src": [16,32], "f": 0, "t": 65, "d": [13,1296], "a": 1 },
+ { "px": [96,464], "src": [0,16], "f": 0, "t": 32, "d": [13,1311], "a": 1 },
+ { "px": [96,480], "src": [0,32], "f": 0, "t": 64, "d": [13,1311], "a": 1 },
+ { "px": [112,464], "src": [16,16], "f": 0, "t": 33, "d": [13,1311], "a": 1 },
+ { "px": [112,480], "src": [16,32], "f": 0, "t": 65, "d": [13,1311], "a": 1 },
+ { "px": [224,464], "src": [0,16], "f": 0, "t": 32, "d": [13,1319], "a": 1 },
+ { "px": [224,480], "src": [0,32], "f": 0, "t": 64, "d": [13,1319], "a": 1 },
+ { "px": [240,464], "src": [16,16], "f": 0, "t": 33, "d": [13,1319], "a": 1 },
+ { "px": [240,480], "src": [16,32], "f": 0, "t": 65, "d": [13,1319], "a": 1 },
+ { "px": [544,464], "src": [0,16], "f": 0, "t": 32, "d": [13,1339], "a": 1 },
+ { "px": [544,480], "src": [0,32], "f": 0, "t": 64, "d": [13,1339], "a": 1 },
+ { "px": [560,464], "src": [16,16], "f": 0, "t": 33, "d": [13,1339], "a": 1 },
+ { "px": [560,480], "src": [16,32], "f": 0, "t": 65, "d": [13,1339], "a": 1 },
+ { "px": [608,464], "src": [0,16], "f": 0, "t": 32, "d": [13,1343], "a": 1 },
+ { "px": [608,480], "src": [0,32], "f": 0, "t": 64, "d": [13,1343], "a": 1 },
+ { "px": [624,464], "src": [16,16], "f": 0, "t": 33, "d": [13,1343], "a": 1 },
+ { "px": [624,480], "src": [16,32], "f": 0, "t": 65, "d": [13,1343], "a": 1 },
+ { "px": [320,480], "src": [0,16], "f": 0, "t": 32, "d": [13,1370], "a": 1 },
+ { "px": [320,496], "src": [0,32], "f": 0, "t": 64, "d": [13,1370], "a": 1 },
+ { "px": [336,480], "src": [16,16], "f": 0, "t": 33, "d": [13,1370], "a": 1 },
+ { "px": [336,496], "src": [16,32], "f": 0, "t": 65, "d": [13,1370], "a": 1 },
+ { "px": [384,480], "src": [0,16], "f": 0, "t": 32, "d": [13,1374], "a": 1 },
+ { "px": [384,496], "src": [0,32], "f": 0, "t": 64, "d": [13,1374], "a": 1 },
+ { "px": [400,480], "src": [16,16], "f": 0, "t": 33, "d": [13,1374], "a": 1 },
+ { "px": [400,496], "src": [16,32], "f": 0, "t": 65, "d": [13,1374], "a": 1 },
+ { "px": [32,496], "src": [0,16], "f": 0, "t": 32, "d": [13,1397], "a": 1 },
+ { "px": [32,512], "src": [0,32], "f": 0, "t": 64, "d": [13,1397], "a": 1 },
+ { "px": [48,496], "src": [16,16], "f": 0, "t": 33, "d": [13,1397], "a": 1 },
+ { "px": [48,512], "src": [16,32], "f": 0, "t": 65, "d": [13,1397], "a": 1 },
+ { "px": [96,496], "src": [0,16], "f": 0, "t": 32, "d": [13,1401], "a": 1 },
+ { "px": [96,512], "src": [0,32], "f": 0, "t": 64, "d": [13,1401], "a": 1 },
+ { "px": [112,496], "src": [16,16], "f": 0, "t": 33, "d": [13,1401], "a": 1 },
+ { "px": [112,512], "src": [16,32], "f": 0, "t": 65, "d": [13,1401], "a": 1 },
+ { "px": [160,496], "src": [0,16], "f": 0, "t": 32, "d": [13,1405], "a": 1 },
+ { "px": [160,512], "src": [0,32], "f": 0, "t": 64, "d": [13,1405], "a": 1 },
+ { "px": [176,496], "src": [16,16], "f": 0, "t": 33, "d": [13,1405], "a": 1 },
+ { "px": [176,512], "src": [16,32], "f": 0, "t": 65, "d": [13,1405], "a": 1 },
+ { "px": [288,496], "src": [0,16], "f": 0, "t": 32, "d": [13,1413], "a": 1 },
+ { "px": [288,512], "src": [0,32], "f": 0, "t": 64, "d": [13,1413], "a": 1 },
+ { "px": [304,496], "src": [16,16], "f": 0, "t": 33, "d": [13,1413], "a": 1 },
+ { "px": [304,512], "src": [16,32], "f": 0, "t": 65, "d": [13,1413], "a": 1 },
+ { "px": [416,496], "src": [0,16], "f": 0, "t": 32, "d": [13,1421], "a": 1 },
+ { "px": [416,512], "src": [0,32], "f": 0, "t": 64, "d": [13,1421], "a": 1 },
+ { "px": [432,496], "src": [16,16], "f": 0, "t": 33, "d": [13,1421], "a": 1 },
+ { "px": [432,512], "src": [16,32], "f": 0, "t": 65, "d": [13,1421], "a": 1 },
+ { "px": [672,496], "src": [0,16], "f": 0, "t": 32, "d": [13,1437], "a": 1 },
+ { "px": [672,512], "src": [0,32], "f": 0, "t": 64, "d": [13,1437], "a": 1 },
+ { "px": [688,496], "src": [16,16], "f": 0, "t": 33, "d": [13,1437], "a": 1 },
+ { "px": [688,512], "src": [16,32], "f": 0, "t": 65, "d": [13,1437], "a": 1 },
+ { "px": [64,512], "src": [0,16], "f": 0, "t": 32, "d": [13,1444], "a": 1 },
+ { "px": [64,528], "src": [0,32], "f": 0, "t": 64, "d": [13,1444], "a": 1 },
+ { "px": [80,512], "src": [16,16], "f": 0, "t": 33, "d": [13,1444], "a": 1 },
+ { "px": [80,528], "src": [16,32], "f": 0, "t": 65, "d": [13,1444], "a": 1 },
+ { "px": [128,512], "src": [0,16], "f": 0, "t": 32, "d": [13,1448], "a": 1 },
+ { "px": [128,528], "src": [0,32], "f": 0, "t": 64, "d": [13,1448], "a": 1 },
+ { "px": [144,512], "src": [16,16], "f": 0, "t": 33, "d": [13,1448], "a": 1 },
+ { "px": [144,528], "src": [16,32], "f": 0, "t": 65, "d": [13,1448], "a": 1 },
+ { "px": [512,512], "src": [0,16], "f": 0, "t": 32, "d": [13,1472], "a": 1 },
+ { "px": [512,528], "src": [0,32], "f": 0, "t": 64, "d": [13,1472], "a": 1 },
+ { "px": [528,512], "src": [16,16], "f": 0, "t": 33, "d": [13,1472], "a": 1 },
+ { "px": [528,528], "src": [16,32], "f": 0, "t": 65, "d": [13,1472], "a": 1 },
+ { "px": [176,144], "src": [32,16], "f": 0, "t": 34, "d": [15,416], "a": 1 },
+ { "px": [192,144], "src": [32,16], "f": 0, "t": 34, "d": [15,417], "a": 1 },
+ { "px": [208,144], "src": [32,16], "f": 0, "t": 34, "d": [15,418], "a": 1 },
+ { "px": [224,144], "src": [32,16], "f": 0, "t": 34, "d": [15,419], "a": 1 },
+ { "px": [240,144], "src": [32,16], "f": 0, "t": 34, "d": [15,420], "a": 1 },
+ { "px": [256,144], "src": [32,16], "f": 0, "t": 34, "d": [15,421], "a": 1 },
+ { "px": [272,144], "src": [32,16], "f": 0, "t": 34, "d": [15,422], "a": 1 },
+ { "px": [288,144], "src": [32,16], "f": 0, "t": 34, "d": [15,423], "a": 1 },
+ { "px": [304,144], "src": [32,16], "f": 0, "t": 34, "d": [15,424], "a": 1 },
+ { "px": [320,144], "src": [32,16], "f": 0, "t": 34, "d": [15,425], "a": 1 },
+ { "px": [336,144], "src": [32,16], "f": 0, "t": 34, "d": [15,426], "a": 1 },
+ { "px": [352,144], "src": [32,16], "f": 0, "t": 34, "d": [15,427], "a": 1 },
+ { "px": [368,144], "src": [32,16], "f": 0, "t": 34, "d": [15,428], "a": 1 },
+ { "px": [384,144], "src": [32,16], "f": 0, "t": 34, "d": [15,429], "a": 1 },
+ { "px": [400,144], "src": [32,16], "f": 0, "t": 34, "d": [15,430], "a": 1 },
+ { "px": [416,144], "src": [32,16], "f": 0, "t": 34, "d": [15,431], "a": 1 },
+ { "px": [432,144], "src": [32,16], "f": 0, "t": 34, "d": [15,432], "a": 1 },
+ { "px": [448,144], "src": [32,16], "f": 0, "t": 34, "d": [15,433], "a": 1 },
+ { "px": [464,144], "src": [32,16], "f": 0, "t": 34, "d": [15,434], "a": 1 },
+ { "px": [480,144], "src": [32,16], "f": 0, "t": 34, "d": [15,435], "a": 1 },
+ { "px": [496,144], "src": [32,16], "f": 0, "t": 34, "d": [15,436], "a": 1 },
+ { "px": [512,144], "src": [32,16], "f": 0, "t": 34, "d": [15,437], "a": 1 },
+ { "px": [528,144], "src": [32,16], "f": 0, "t": 34, "d": [15,438], "a": 1 },
+ { "px": [544,144], "src": [32,16], "f": 0, "t": 34, "d": [15,439], "a": 1 },
+ { "px": [160,160], "src": [32,16], "f": 0, "t": 34, "d": [15,460], "a": 1 },
+ { "px": [560,160], "src": [32,16], "f": 0, "t": 34, "d": [15,485], "a": 1 },
+ { "px": [160,288], "src": [32,16], "f": 0, "t": 34, "d": [15,820], "a": 1 },
+ { "px": [176,288], "src": [32,16], "f": 0, "t": 34, "d": [15,821], "a": 1 },
+ { "px": [192,288], "src": [32,16], "f": 0, "t": 34, "d": [15,822], "a": 1 },
+ { "px": [544,288], "src": [32,16], "f": 0, "t": 34, "d": [15,844], "a": 1 },
+ { "px": [560,288], "src": [32,16], "f": 0, "t": 34, "d": [15,845], "a": 1 },
+ { "px": [320,304], "src": [32,16], "f": 0, "t": 34, "d": [15,875], "a": 1 },
+ { "px": [352,304], "src": [32,16], "f": 0, "t": 34, "d": [15,877], "a": 1 },
+ { "px": [208,352], "src": [32,16], "f": 0, "t": 34, "d": [15,1003], "a": 1 },
+ { "px": [224,352], "src": [32,16], "f": 0, "t": 34, "d": [15,1004], "a": 1 },
+ { "px": [240,352], "src": [32,16], "f": 0, "t": 34, "d": [15,1005], "a": 1 },
+ { "px": [256,352], "src": [32,16], "f": 0, "t": 34, "d": [15,1006], "a": 1 },
+ { "px": [304,352], "src": [32,16], "f": 0, "t": 34, "d": [15,1009], "a": 1 },
+ { "px": [368,352], "src": [32,16], "f": 0, "t": 34, "d": [15,1013], "a": 1 },
+ { "px": [400,352], "src": [32,16], "f": 0, "t": 34, "d": [15,1015], "a": 1 },
+ { "px": [416,352], "src": [32,16], "f": 0, "t": 34, "d": [15,1016], "a": 1 },
+ { "px": [432,352], "src": [32,16], "f": 0, "t": 34, "d": [15,1017], "a": 1 },
+ { "px": [448,352], "src": [32,16], "f": 0, "t": 34, "d": [15,1018], "a": 1 },
+ { "px": [464,352], "src": [32,16], "f": 0, "t": 34, "d": [15,1019], "a": 1 },
+ { "px": [480,352], "src": [32,16], "f": 0, "t": 34, "d": [15,1020], "a": 1 },
+ { "px": [496,352], "src": [32,16], "f": 0, "t": 34, "d": [15,1021], "a": 1 },
+ { "px": [320,368], "src": [32,16], "f": 0, "t": 34, "d": [15,1055], "a": 1 },
+ { "px": [336,368], "src": [32,16], "f": 0, "t": 34, "d": [15,1056], "a": 1 },
+ { "px": [352,368], "src": [32,16], "f": 0, "t": 34, "d": [15,1057], "a": 1 },
+ { "px": [272,400], "src": [32,16], "f": 0, "t": 34, "d": [15,1142], "a": 1 },
+ { "px": [288,400], "src": [32,16], "f": 0, "t": 34, "d": [15,1143], "a": 1 },
+ { "px": [304,400], "src": [32,16], "f": 0, "t": 34, "d": [15,1144], "a": 1 },
+ { "px": [320,400], "src": [32,16], "f": 0, "t": 34, "d": [15,1145], "a": 1 },
+ { "px": [336,400], "src": [32,16], "f": 0, "t": 34, "d": [15,1146], "a": 1 },
+ { "px": [352,400], "src": [32,16], "f": 0, "t": 34, "d": [15,1147], "a": 1 },
+ { "px": [368,400], "src": [32,16], "f": 0, "t": 34, "d": [15,1148], "a": 1 },
+ { "px": [384,400], "src": [32,16], "f": 0, "t": 34, "d": [15,1149], "a": 1 },
+ { "px": [368,208], "src": [48,0], "f": 0, "t": 3, "d": [11,608], "a": 1 },
+ { "px": [384,208], "src": [48,0], "f": 0, "t": 3, "d": [11,609], "a": 1 },
+ { "px": [400,208], "src": [48,0], "f": 0, "t": 3, "d": [11,610], "a": 1 },
+ { "px": [240,240], "src": [48,0], "f": 0, "t": 3, "d": [11,690], "a": 1 },
+ { "px": [256,240], "src": [48,0], "f": 0, "t": 3, "d": [11,691], "a": 1 },
+ { "px": [272,240], "src": [48,0], "f": 0, "t": 3, "d": [11,692], "a": 1 },
+ { "px": [288,240], "src": [48,0], "f": 0, "t": 3, "d": [11,693], "a": 1 },
+ { "px": [304,240], "src": [48,0], "f": 0, "t": 3, "d": [11,694], "a": 1 },
+ { "px": [320,240], "src": [48,0], "f": 0, "t": 3, "d": [11,695], "a": 1 },
+ { "px": [336,240], "src": [48,0], "f": 0, "t": 3, "d": [11,696], "a": 1 },
+ { "px": [352,240], "src": [48,0], "f": 0, "t": 3, "d": [11,697], "a": 1 },
+ { "px": [416,240], "src": [48,0], "f": 0, "t": 3, "d": [11,701], "a": 1 },
+ { "px": [432,240], "src": [48,0], "f": 0, "t": 3, "d": [11,702], "a": 1 },
+ { "px": [448,240], "src": [48,0], "f": 0, "t": 3, "d": [11,703], "a": 1 },
+ { "px": [464,240], "src": [48,0], "f": 0, "t": 3, "d": [11,704], "a": 1 },
+ { "px": [544,240], "src": [48,0], "f": 0, "t": 3, "d": [11,709], "a": 1 },
+ { "px": [560,240], "src": [48,0], "f": 0, "t": 3, "d": [11,710], "a": 1 },
+ { "px": [160,256], "src": [48,0], "f": 0, "t": 3, "d": [11,730], "a": 1 },
+ { "px": [176,256], "src": [48,0], "f": 0, "t": 3, "d": [11,731], "a": 1 },
+ { "px": [192,256], "src": [48,0], "f": 0, "t": 3, "d": [11,732], "a": 1 },
+ { "px": [480,304], "src": [48,0], "f": 0, "t": 3, "d": [11,885], "a": 1 },
+ { "px": [496,304], "src": [48,0], "f": 0, "t": 3, "d": [11,886], "a": 1 },
+ { "px": [208,320], "src": [48,0], "f": 0, "t": 3, "d": [11,913], "a": 1 },
+ { "px": [224,320], "src": [48,0], "f": 0, "t": 3, "d": [11,914], "a": 1 },
+ { "px": [320,320], "src": [48,0], "f": 0, "t": 3, "d": [11,920], "a": 1 },
+ { "px": [352,320], "src": [48,0], "f": 0, "t": 3, "d": [11,922], "a": 1 },
+ { "px": [304,352], "src": [48,0], "f": 0, "t": 3, "d": [11,1009], "a": 1 },
+ { "px": [368,352], "src": [48,0], "f": 0, "t": 3, "d": [11,1013], "a": 1 },
+ { "px": [544,352], "src": [48,0], "f": 0, "t": 3, "d": [11,1024], "a": 1 },
+ { "px": [560,352], "src": [48,0], "f": 0, "t": 3, "d": [11,1025], "a": 1 },
+ { "px": [320,368], "src": [48,0], "f": 0, "t": 3, "d": [11,1055], "a": 1 },
+ { "px": [336,368], "src": [48,0], "f": 0, "t": 3, "d": [11,1056], "a": 1 },
+ { "px": [352,368], "src": [48,0], "f": 0, "t": 3, "d": [11,1057], "a": 1 },
+ { "px": [160,384], "src": [48,0], "f": 0, "t": 3, "d": [11,1090], "a": 1 },
+ { "px": [176,384], "src": [48,0], "f": 0, "t": 3, "d": [11,1091], "a": 1 },
+ { "px": [192,384], "src": [48,0], "f": 0, "t": 3, "d": [11,1092], "a": 1 },
+ { "px": [208,384], "src": [48,0], "f": 0, "t": 3, "d": [11,1093], "a": 1 },
+ { "px": [224,384], "src": [48,0], "f": 0, "t": 3, "d": [11,1094], "a": 1 },
+ { "px": [240,416], "src": [48,0], "f": 0, "t": 3, "d": [11,1185], "a": 1 },
+ { "px": [256,416], "src": [48,0], "f": 0, "t": 3, "d": [11,1186], "a": 1 },
+ { "px": [272,416], "src": [48,0], "f": 0, "t": 3, "d": [11,1187], "a": 1 },
+ { "px": [288,416], "src": [48,0], "f": 0, "t": 3, "d": [11,1188], "a": 1 },
+ { "px": [304,416], "src": [48,0], "f": 0, "t": 3, "d": [11,1189], "a": 1 },
+ { "px": [320,416], "src": [48,0], "f": 0, "t": 3, "d": [11,1190], "a": 1 },
+ { "px": [336,416], "src": [48,0], "f": 0, "t": 3, "d": [11,1191], "a": 1 },
+ { "px": [352,416], "src": [48,0], "f": 0, "t": 3, "d": [11,1192], "a": 1 },
+ { "px": [368,416], "src": [48,0], "f": 0, "t": 3, "d": [11,1193], "a": 1 },
+ { "px": [384,416], "src": [48,0], "f": 0, "t": 3, "d": [11,1194], "a": 1 },
+ { "px": [400,416], "src": [48,0], "f": 0, "t": 3, "d": [11,1195], "a": 1 },
+ { "px": [416,416], "src": [48,0], "f": 0, "t": 3, "d": [11,1196], "a": 1 },
+ { "px": [432,416], "src": [48,0], "f": 0, "t": 3, "d": [11,1197], "a": 1 },
+ { "px": [448,416], "src": [48,0], "f": 0, "t": 3, "d": [11,1198], "a": 1 },
+ { "px": [464,416], "src": [48,0], "f": 0, "t": 3, "d": [11,1199], "a": 1 },
+ { "px": [480,416], "src": [48,0], "f": 0, "t": 3, "d": [11,1200], "a": 1 },
+ { "px": [496,416], "src": [48,0], "f": 0, "t": 3, "d": [11,1201], "a": 1 },
+ { "px": [512,416], "src": [48,0], "f": 0, "t": 3, "d": [11,1202], "a": 1 },
+ { "px": [528,416], "src": [48,0], "f": 0, "t": 3, "d": [11,1203], "a": 1 }
+ ],
+ "seed": 9319627,
+ "overrideTilesetUid": null,
+ "gridTiles": [],
+ "entityInstances": []
+ }
+ ],
+ "__neighbours": []
+ }
+ ] }],
+ "dummyWorldIid": "8e3ea100-7820-11ed-9319-9dc1bfbb47b3"
+}
\ No newline at end of file
diff --git a/res/levels/sampleWorldTiles.aseprite b/res/levels/sampleWorldTiles.aseprite
new file mode 100644
index 0000000..aa931cc
Binary files /dev/null and b/res/levels/sampleWorldTiles.aseprite differ
diff --git a/run_js.html b/run_js.html
new file mode 100644
index 0000000..1b16dde
--- /dev/null
+++ b/run_js.html
@@ -0,0 +1,9 @@
+
+
base2D
+
+
+
+
+
+
+
diff --git a/setup.hxml b/setup.hxml
new file mode 100644
index 0000000..c7327a4
--- /dev/null
+++ b/setup.hxml
@@ -0,0 +1,20 @@
+# Misc
+--cmd haxelib --always git castle https://github.com/deepnight/castle.git
+--cmd haxelib --always git hscript https://github.com/HaxeFoundation/hscript.git
+
+# Aseprite
+--cmd haxelib --always install ase
+--cmd haxelib --always git heaps-aseprite https://github.com/deepnight/heaps-aseprite.git
+
+# Heaps
+--cmd haxelib --always git hlsdl https://github.com/HaxeFoundation/hashlink.git master libs/sdl
+--cmd haxelib --always git hldx https://github.com/HaxeFoundation/hashlink.git master libs/directx
+--cmd haxelib --always git heaps https://github.com/deepnight/heaps.git
+
+# Deepnight
+--cmd haxelib --always install redistHelper
+--cmd haxelib --always git ldtk-haxe-api https://github.com/deepnight/ldtk-haxe-api.git
+--cmd haxelib --always git deepnightLibs https://github.com/deepnight/deepnightLibs.git
+
+# Done
+--cmd haxelib list
diff --git a/src/game/App.hx b/src/game/App.hx
new file mode 100644
index 0000000..75854f9
--- /dev/null
+++ b/src/game/App.hx
@@ -0,0 +1,338 @@
+/**
+ "App" class takes care of all the top-level stuff in the whole application. Any other Process, including Game instance, should be a child of App.
+**/
+
+class App extends dn.Process {
+ public static var ME : App;
+
+ /** 2D scene **/
+ public var scene(default,null) : h2d.Scene;
+
+ /** Used to create "ControllerAccess" instances that will grant controller usage (keyboard or gamepad) **/
+ public var controller : Controller;
+
+ /** Controller Access created for Main & Boot **/
+ public var ca : ControllerAccess;
+
+ /** If TRUE, game is paused, and a Contrast filter is applied **/
+ public var screenshotMode(default,null) = false;
+
+ public function new(s:h2d.Scene) {
+ super();
+ ME = this;
+ scene = s;
+ createRoot(scene);
+
+ hxd.Window.getInstance().addEventTarget(onWindowEvent);
+
+ initEngine();
+ initAssets();
+ initController();
+
+ // Create console (open with [²] key)
+ new ui.Console(Assets.fontPixelMono, scene); // init debug console
+
+ // Optional screen that shows a "Click to start/continue" message when the game client looses focus
+ if( dn.heaps.GameFocusHelper.isUseful() )
+ new dn.heaps.GameFocusHelper(scene, Assets.fontPixel);
+
+ #if debug
+ Console.ME.enableStats();
+ #end
+
+ startGame();
+ }
+
+
+ function onWindowEvent(ev:hxd.Event) {
+ switch ev.kind {
+ case EPush:
+ case ERelease:
+ case EMove:
+ case EOver: onMouseEnter(ev);
+ case EOut: onMouseLeave(ev);
+ case EWheel:
+ case EFocus: onWindowFocus(ev);
+ case EFocusLost: onWindowBlur(ev);
+ case EKeyDown:
+ case EKeyUp:
+ case EReleaseOutside:
+ case ETextInput:
+ case ECheck:
+ }
+ }
+
+ function onMouseEnter(e:hxd.Event) {}
+ function onMouseLeave(e:hxd.Event) {}
+ function onWindowFocus(e:hxd.Event) {}
+ function onWindowBlur(e:hxd.Event) {}
+
+
+ #if hl
+ public static function onCrash(err:Dynamic) {
+ var title = L.untranslated("Fatal error");
+ var msg = L.untranslated('I\'m really sorry but the game crashed! Error: ${Std.string(err)}');
+ var flags : haxe.EnumFlags = new haxe.EnumFlags();
+ flags.set(IsError);
+
+ var log = [ Std.string(err) ];
+ try {
+ log.push("BUILD: "+Const.BUILD_INFO);
+ log.push("EXCEPTION:");
+ log.push( haxe.CallStack.toString( haxe.CallStack.exceptionStack() ) );
+
+ log.push("CALL:");
+ log.push( haxe.CallStack.toString( haxe.CallStack.callStack() ) );
+
+ sys.io.File.saveContent("crash.log", log.join("\n"));
+ hl.UI.dialog(title, msg, flags);
+ }
+ catch(_) {
+ sys.io.File.saveContent("crash2.log", log.join("\n"));
+ hl.UI.dialog(title, msg, flags);
+ }
+
+ hxd.System.exit();
+ }
+ #end
+
+
+ /** Start game process **/
+ public function startGame() {
+ if( Game.exists() ) {
+ // Kill previous game instance first
+ Game.ME.destroy();
+ dn.Process.updateAll(1); // ensure all garbage collection is done
+ _createGameInstance();
+ hxd.Timer.skip();
+ }
+ else {
+ // Fresh start
+ delayer.addF( ()->{
+ _createGameInstance();
+ hxd.Timer.skip();
+ }, 1 );
+ }
+ }
+
+ final function _createGameInstance() {
+ // new Game(); // <---- Uncomment this to start an empty Game instance
+ new sample.SampleGame(); // <---- Uncomment this to start the Sample Game instance
+ }
+
+
+ public function anyInputHasFocus() {
+ return Console.ME.isActive() || cd.has("consoleRecentlyActive") || cd.has("modalClosedRecently");
+ }
+
+
+ /**
+ Set "screenshot" mode.
+ If enabled, the game will be adapted to be more suitable for screenshots: more color contrast, no UI etc.
+ **/
+ public function setScreenshotMode(v:Bool) {
+ screenshotMode = v;
+
+ Console.ME.runCommand("cls");
+ if( screenshotMode ) {
+ var f = new h2d.filter.ColorMatrix();
+ f.matrix.colorContrast(0.2);
+ root.filter = f;
+ if( Game.exists() ) {
+ Game.ME.hud.root.visible = false;
+ Game.ME.pause();
+ }
+ }
+ else {
+ if( Game.exists() ) {
+ Game.ME.hud.root.visible = true;
+ Game.ME.resume();
+ }
+ root.filter = null;
+ }
+ }
+
+ /** Toggle current game pause state **/
+ public inline function toggleGamePause() setGamePause( !isGamePaused() );
+
+ /** Return TRUE if current game is paused **/
+ public inline function isGamePaused() return Game.exists() && Game.ME.isPaused();
+
+ /** Set current game pause state **/
+ public function setGamePause(pauseState:Bool) {
+ if( Game.exists() )
+ if( pauseState )
+ Game.ME.pause();
+ else
+ Game.ME.resume();
+ }
+
+
+ /**
+ Initialize low-level engine stuff, before anything else
+ **/
+ function initEngine() {
+ // Engine settings
+ engine.backgroundColor = 0xff<<24 | 0x111133;
+ #if( hl && !debug )
+ engine.fullScreen = true;
+ #end
+
+ #if( hl && !debug)
+ hl.UI.closeConsole();
+ hl.Api.setErrorHandler( onCrash );
+ #end
+
+ // Heaps resource management
+ #if( hl && debug )
+ hxd.Res.initLocal();
+ hxd.res.Resource.LIVE_UPDATE = true;
+ #else
+ hxd.Res.initEmbed();
+ #end
+
+ // Sound manager (force manager init on startup to avoid a freeze on first sound playback)
+ hxd.snd.Manager.get();
+ hxd.Timer.skip(); // needed to ignore heavy Sound manager init frame
+
+ // Framerate
+ hxd.Timer.smoothFactor = 0.4;
+ hxd.Timer.wantedFPS = Const.FPS;
+ dn.Process.FIXED_UPDATE_FPS = Const.FIXED_UPDATE_FPS;
+ }
+
+
+ /**
+ Init app assets
+ **/
+ function initAssets() {
+ // Init game assets
+ Assets.init();
+
+ // Init lang data
+ Lang.init("en");
+
+ // Bind DB hot-reloading callback
+ Const.db.onReload = onDbReload;
+ }
+
+
+ /** Init game controller and default key bindings **/
+ function initController() {
+ controller = dn.heaps.input.Controller.createFromAbstractEnum(GameAction);
+ ca = controller.createAccess();
+ ca.lockCondition = ()->return destroyed || anyInputHasFocus();
+
+ initControllerBindings();
+ }
+
+ public function initControllerBindings() {
+ controller.removeBindings();
+
+ // Gamepad bindings
+ controller.bindPadLStick4(MoveLeft, MoveRight, MoveUp, MoveDown);
+ controller.bindPad(Jump, A);
+ controller.bindPad(Restart, SELECT);
+ controller.bindPad(Pause, START);
+ controller.bindPad(MoveLeft, DPAD_LEFT);
+ controller.bindPad(MoveRight, DPAD_RIGHT);
+ controller.bindPad(MoveUp, DPAD_UP);
+ controller.bindPad(MoveDown, DPAD_DOWN);
+
+ controller.bindPad(MenuUp, [DPAD_UP, LSTICK_UP]);
+ controller.bindPad(MenuDown, [DPAD_DOWN, LSTICK_DOWN]);
+ controller.bindPad(MenuLeft, [DPAD_LEFT, LSTICK_LEFT]);
+ controller.bindPad(MenuRight, [DPAD_RIGHT, LSTICK_RIGHT]);
+ controller.bindPad(MenuOk, [A, X]);
+ controller.bindPad(MenuCancel, B);
+
+ // Keyboard bindings
+ controller.bindKeyboard(MoveLeft, [K.LEFT, K.Q, K.A]);
+ controller.bindKeyboard(MoveRight, [K.RIGHT, K.D]);
+ controller.bindKeyboard(MoveUp, [K.UP, K.Z, K.W]);
+ controller.bindKeyboard(MoveDown, [K.DOWN, K.S]);
+ controller.bindKeyboard(Jump, [K.SPACE,K.UP]);
+ controller.bindKeyboard(Restart, K.R);
+ controller.bindKeyboard(ScreenshotMode, K.F9);
+ controller.bindKeyboard(Pause, K.P);
+ controller.bindKeyboard(Pause, K.PAUSE_BREAK);
+
+ controller.bindKeyboard(MenuUp, [K.UP, K.Z, K.W]);
+ controller.bindKeyboard(MenuDown, [K.DOWN, K.S]);
+ controller.bindKeyboard(MenuLeft, [K.LEFT, K.Q, K.A]);
+ controller.bindKeyboard(MenuRight, [K.RIGHT, K.D]);
+ controller.bindKeyboard(MenuOk, [K.SPACE, K.ENTER, K.F]);
+ controller.bindKeyboard(MenuCancel, K.ESCAPE);
+
+ // Debug controls
+ #if debug
+ controller.bindPad(DebugTurbo, LT);
+ controller.bindPad(DebugSlowMo, LB);
+ controller.bindPad(DebugDroneZoomIn, RSTICK_UP);
+ controller.bindPad(DebugDroneZoomOut, RSTICK_DOWN);
+
+ controller.bindKeyboard(DebugDroneZoomIn, K.PGUP);
+ controller.bindKeyboard(DebugDroneZoomOut, K.PGDOWN);
+ controller.bindKeyboard(DebugTurbo, [K.END, K.NUMPAD_ADD]);
+ controller.bindKeyboard(DebugSlowMo, [K.HOME, K.NUMPAD_SUB]);
+ controller.bindPadCombo(ToggleDebugDrone, [LSTICK_PUSH, RSTICK_PUSH]);
+ controller.bindKeyboardCombo(ToggleDebugDrone, [K.CTRL,K.SHIFT, K.D]);
+ controller.bindKeyboardCombo(OpenConsoleFlags, [[K.QWERTY_TILDE], [K.QWERTY_QUOTE], ["²".code], [K.CTRL,K.SHIFT, K.F]]);
+ #end
+ }
+
+
+ /** Return TRUE if an App instance exists **/
+ public static inline function exists() return ME!=null && !ME.destroyed;
+
+ /** Close & exit the app **/
+ public function exit() {
+ destroy();
+ }
+
+ override function onDispose() {
+ super.onDispose();
+
+ hxd.Window.getInstance().removeEventTarget( onWindowEvent );
+
+ #if hl
+ hxd.System.exit();
+ #end
+ }
+
+ /** Called when Const.db values are hot-reloaded **/
+ public function onDbReload() {
+ if( Game.exists() )
+ Game.ME.onDbReload();
+ }
+
+ override function update() {
+ Assets.update(tmod);
+
+ super.update();
+
+ if( !Window.hasAnyModal() ) {
+ if( ca.isPressed(ScreenshotMode) )
+ setScreenshotMode( !screenshotMode );
+
+ if( ca.isPressed(Pause) )
+ toggleGamePause();
+
+ if( ca.isPressed(OpenConsoleFlags) )
+ Console.ME.runCommand("/flags");
+ }
+
+ if( ui.Console.ME.isActive() )
+ cd.setF("consoleRecentlyActive",2);
+
+
+ // Mem track reporting
+ #if debug
+ if( ca.isKeyboardDown(K.SHIFT) && ca.isKeyboardPressed(K.ENTER) ) {
+ Console.ME.runCommand("/cls");
+ dn.debug.MemTrack.report( (v)->Console.ME.log(v,Yellow) );
+ }
+ #end
+
+ }
+}
\ No newline at end of file
diff --git a/src/game/Boot.hx b/src/game/Boot.hx
new file mode 100644
index 0000000..709d1fc
--- /dev/null
+++ b/src/game/Boot.hx
@@ -0,0 +1,74 @@
+/**
+ Boot class is the entry point for the app.
+ It doesn't do much, except creating Main class and taking care of loops. Thus, you shouldn't be doing too much in this class.
+**/
+
+class Boot extends hxd.App {
+ #if debug
+ // Debug controls over game speed
+ var tmodSpeedMul = 1.0;
+
+ // Shortcut to controller
+ var ca(get,never) : ControllerAccess;
+ inline function get_ca() return App.ME.ca;
+ #end
+
+
+ /**
+ App entry point: everything starts here
+ **/
+ static function main() {
+ new Boot();
+ }
+
+ /**
+ Called when engine is ready, actual app can start
+ **/
+ override function init() {
+ new App(s2d);
+ onResize();
+ }
+
+ // Window resized
+ override function onResize() {
+ super.onResize();
+ dn.Process.resizeAll();
+ }
+
+
+ /** Main app loop **/
+ override function update(deltaTime:Float) {
+ super.update(deltaTime);
+
+ // Debug controls over app speed
+ var adjustedTmod = hxd.Timer.tmod;
+ #if debug
+ if( App.exists() ) {
+ // Slow down (toggle)
+ if( ca.isPressed(DebugSlowMo) )
+ tmodSpeedMul = tmodSpeedMul>=1 ? 0.2 : 1;
+ adjustedTmod *= tmodSpeedMul;
+
+ // Turbo (by holding a key)
+ adjustedTmod *= ca.isDown(DebugTurbo) ? 5 : 1;
+ }
+ #end
+
+ #if( hl && !debug )
+ try {
+ #end
+
+ // Run all dn.Process instances loops
+ dn.Process.updateAll(adjustedTmod);
+
+ // Update current sprite atlas "tmod" value (for animations)
+ Assets.update(adjustedTmod);
+
+ #if( hl && !debug )
+ } catch(err) {
+ App.onCrash(err);
+ }
+ #end
+ }
+}
+
diff --git a/src/game/Camera.hx b/src/game/Camera.hx
new file mode 100644
index 0000000..d1006a6
--- /dev/null
+++ b/src/game/Camera.hx
@@ -0,0 +1,398 @@
+class Camera extends GameChildProcess {
+ public static var MIN_ZOOM : Float = 1.0;
+ public static var MAX_ZOOM : Float = 10;
+
+
+ /** Camera focus coord in level pixels. This is the raw camera location: the actual camera location might be clamped to level bounds. **/
+ public var rawFocus : LPoint;
+
+ /** This is equal to rawFocus if `clampToLevelBounds` is disabled **/
+ var clampedFocus : LPoint;
+
+ var target : Null;
+ public var targetOffX = 0.;
+ public var targetOffY = 0.;
+
+ /** Width of viewport in level pixels **/
+ public var pxWid(get,never) : Int;
+
+ /** Height of viewport in level pixels **/
+ public var pxHei(get,never) : Int;
+
+ public var cWid(get,never) : Int; inline function get_cWid() return M.ceil(pxWid/Const.GRID);
+ public var cHei(get,never) : Int; inline function get_cHei() return M.ceil(pxHei/Const.GRID);
+
+ /** Horizontal camera dead-zone in percentage of viewport width **/
+ public var deadZonePctX = 0.04;
+
+ /** Verticakl camera dead-zone in percentage of viewport height **/
+ public var deadZonePctY = 0.10;
+
+ var baseFrict = 0.89;
+ var dx = 0.;
+ var dy = 0.;
+ var dz = 0.;
+ var bumpOffX = 0.;
+ var bumpOffY = 0.;
+ var bumpFrict = 0.85;
+ var bumpZoomFactor = 0.;
+
+ /** Actual zoom value without modifiers **/
+ var baseZoom = 1.0;
+ var zoomSpeed = 0.0014;
+ var zoomFrict = 0.9;
+
+ /** Current zoom factor, including all modifiers **/
+ public var zoom(get,never) : Float;
+
+ /** Target base zoom value **/
+ public var targetZoom(default,set) = 1.0;
+
+ /** Speed multiplier when camera is tracking a target **/
+ var trackingSpeed = 1.0;
+
+ /** If TRUE (default), the camera will try to stay inside level bounds. It cannot be done if level is smaller than actual viewport. In such case, the camera will be centered. **/
+ public var clampToLevelBounds = false;
+ var brakeDistNearBounds = 0.1;
+
+
+ /** Camera bound coords in level pixels **/
+ public var pxLeft(get,never) : Int; inline function get_pxLeft() return Std.int( clampedFocus.levelX - pxWid*0.5 );
+ public var pxRight(get,never) : Int; inline function get_pxRight() return Std.int( pxLeft + (pxWid - 1) );
+ public var pxTop(get,never) : Int; inline function get_pxTop() return Std.int( clampedFocus.levelY-pxHei*0.5 );
+ public var pxBottom(get,never) : Int; inline function get_pxBottom() return pxTop + pxHei - 1;
+
+ /** Center X in pixels **/
+ public var centerX(get,never) : Int; inline function get_centerX() return Std.int( (pxLeft+pxRight) * 0.5 );
+
+ /** Center Y in pixels **/
+ public var centerY(get,never) : Int; inline function get_centerY() return Std.int( (pxTop+pxBottom) * 0.5 );
+
+ /** Camera bound coords in grid cells **/
+ public var cLeft(get,never) : Int; inline function get_cLeft() return Std.int( pxLeft/Const.GRID );
+ public var cRight(get,never) : Int; inline function get_cRight() return M.ceil( pxRight/Const.GRID );
+ public var cTop(get,never) : Int; inline function get_cTop() return Std.int( pxTop/Const.GRID );
+ public var cBottom(get,never) : Int; inline function get_cBottom() return M.ceil( pxBottom/Const.GRID );
+
+ // Debugging
+ var invalidateDebugBounds = false;
+ var debugBounds : Null;
+
+
+ public function new() {
+ super();
+ rawFocus = LPoint.fromCase(0,0);
+ clampedFocus = LPoint.fromCase(0,0);
+ dx = dy = 0;
+ }
+
+ @:keep
+ override function toString() {
+ return 'Camera@${Std.int(rawFocus.levelX)},${Std.int(rawFocus.levelY)}';
+ }
+
+ inline function get_zoom() {
+ return baseZoom + bumpZoomFactor;
+ }
+
+
+ inline function set_targetZoom(v) {
+ return targetZoom = M.fclamp(v, MIN_ZOOM, MAX_ZOOM);
+ }
+
+ /** Smoothly change zoom within MIN/MAX bounds **/
+ public inline function zoomTo(v:Float) {
+ targetZoom = v;
+ }
+
+ /** Force zoom immediately to given value **/
+ public function forceZoom(v) {
+ baseZoom = targetZoom = M.fclamp(v, MIN_ZOOM, MAX_ZOOM);
+ dz = 0;
+ }
+
+ public inline function bumpZoom(z:Float) {
+ bumpZoomFactor = z;
+ }
+
+ function get_pxWid() {
+ return M.ceil( Game.ME.w() / Const.SCALE / zoom );
+ }
+
+ function get_pxHei() {
+ return M.ceil( Game.ME.h() / Const.SCALE / zoom );
+ }
+
+
+ /**
+ Return TRUE if given coords are in current camera bounds. Padding is *added* to the screen bounds (it can be negative to *shrink* these bounds).
+ **/
+ public inline function isOnScreen(levelX:Float, levelY: Float, padding=0.) {
+ return levelX>=pxLeft-padding && levelX<=pxRight+padding && levelY>=pxTop-padding && levelY<=pxBottom+padding;
+ }
+
+ /**
+ Return TRUE if given rectangle is partially inside current camera bounds. Padding is *added* to the screen bounds (it can be negative to *shrink* these bounds).
+ **/
+ public inline function isOnScreenRect(x:Float, y:Float, wid:Float, hei:Float, padding=0.) {
+ return Lib.rectangleOverlaps(
+ pxLeft-padding, pxTop-padding, pxWid+padding*2, pxHei+padding*2,
+ x, y, wid, hei
+ );
+ }
+
+ /**
+ Return TRUE if given grid coords are in current camera bounds. Padding is *added* to the screen bounds (it can be negative to *shrink* these bounds).
+ **/
+ public inline function isOnScreenCase(cx:Int, cy:Int, padding=32) {
+ return cx*Const.GRID>=pxLeft-padding && (cx+1)*Const.GRID<=pxRight+padding
+ && cy*Const.GRID>=pxTop-padding && (cy+1)*Const.GRID<=pxBottom+padding;
+ }
+
+
+ /**
+ Enable auto tracking on given Entity. If `immediate` is true, the camera is immediately positioned over the Entity, otherwise it just moves to it.
+ **/
+ public function trackEntity(e:Entity, immediate:Bool, speed=1.0) {
+ target = e;
+ setTrackingSpeed(speed);
+ if( immediate || rawFocus.levelX==0 && rawFocus.levelY==0 )
+ centerOnTarget();
+ }
+
+ public inline function setTrackingSpeed(spd:Float) {
+ trackingSpeed = M.fclamp(spd, 0.01, 10);
+ }
+
+ public inline function stopTracking() {
+ target = null;
+ }
+
+ public function centerOnTarget() {
+ if( target!=null ) {
+ rawFocus.levelX = target.centerX + targetOffX;
+ rawFocus.levelY = target.centerY + targetOffY;
+ }
+ }
+
+ public inline function levelToGlobalX(v:Float) return v*Const.SCALE + Game.ME.scroller.x;
+ public inline function levelToGlobalY(v:Float) return v*Const.SCALE + Game.ME.scroller.y;
+
+ var shakePower = 1.0;
+ public function shakeS(t:Float, pow=1.0) {
+ cd.setS("shaking", t, false);
+ shakePower = pow;
+ }
+
+ public inline function bumpAng(a, dist) {
+ bumpOffX+=Math.cos(a)*dist;
+ bumpOffY+=Math.sin(a)*dist;
+ }
+
+ public inline function bump(x,y) {
+ bumpOffX+=x;
+ bumpOffY+=y;
+ }
+
+
+ /** Apply camera values to Game scroller **/
+ function apply() {
+ if( ui.Console.ME.hasFlag(F_CameraScrolling) )
+ return;
+
+ var level = Game.ME.level;
+ var scroller = Game.ME.scroller;
+
+ // Update scroller
+ scroller.x = -clampedFocus.levelX + pxWid*0.5;
+ scroller.y = -clampedFocus.levelY + pxHei*0.5;
+
+ // Bumps friction
+ bumpOffX *= Math.pow(bumpFrict, tmod);
+ bumpOffY *= Math.pow(bumpFrict, tmod);
+
+ // Bump
+ scroller.x -= bumpOffX;
+ scroller.y -= bumpOffY;
+
+ // Shakes
+ if( cd.has("shaking") ) {
+ scroller.x += Math.cos(ftime*1.1)*2.5*shakePower * cd.getRatio("shaking");
+ scroller.y += Math.sin(0.3+ftime*1.7)*2.5*shakePower * cd.getRatio("shaking");
+ }
+
+ // Scaling
+ scroller.x*=Const.SCALE*zoom;
+ scroller.y*=Const.SCALE*zoom;
+
+ // Rounding
+ scroller.x = M.round(scroller.x);
+ scroller.y = M.round(scroller.y);
+
+ // Zoom
+ scroller.setScale(Const.SCALE * zoom);
+ }
+
+
+ /** Hide camera debug bounds **/
+ public function disableDebugBounds() {
+ if( debugBounds!=null ) {
+ debugBounds.remove();
+ debugBounds = null;
+ }
+ }
+
+ /** Show camera debug bounds **/
+ public function enableDebugBounds() {
+ disableDebugBounds();
+ debugBounds = new h2d.Graphics();
+ Game.ME.scroller.add(debugBounds, Const.DP_TOP);
+ invalidateDebugBounds = true;
+ }
+
+ function renderDebugBounds() {
+ debugBounds.clear();
+
+ debugBounds.lineStyle(2,0xff00ff);
+ debugBounds.drawRect(0,0,pxWid,pxHei);
+
+ debugBounds.moveTo(pxWid*0.5, 0);
+ debugBounds.lineTo(pxWid*0.5, pxHei);
+
+ debugBounds.moveTo(0, pxHei*0.5);
+ debugBounds.lineTo(pxWid, pxHei*0.5);
+ }
+
+
+ override function onResize() {
+ super.onResize();
+ invalidateDebugBounds = true;
+ }
+
+
+ override function postUpdate() {
+ super.postUpdate();
+
+ apply();
+
+ // Debug bounds
+ if( ui.Console.ME.hasFlag(F_Camera) && debugBounds==null )
+ enableDebugBounds();
+ else if( !ui.Console.ME.hasFlag(F_Camera) && debugBounds!=null )
+ disableDebugBounds();
+
+ if( debugBounds!=null ) {
+ if( invalidateDebugBounds ) {
+ renderDebugBounds();
+ invalidateDebugBounds = false;
+ }
+ debugBounds.setPosition(pxLeft,pxTop);
+ }
+ }
+
+
+ override function update() {
+ super.update();
+
+ final level = Game.ME.level;
+
+
+ // Zoom movement
+ var tz = targetZoom;
+
+ if( tz!=baseZoom ) {
+ if( tz>baseZoom)
+ dz+=zoomSpeed;
+ else
+ dz-=zoomSpeed;
+ }
+ else
+ dz = 0;
+
+ var prevZoom = baseZoom;
+ baseZoom+=dz*tmod;
+
+ bumpZoomFactor *= Math.pow(0.9, tmod);
+ dz*=Math.pow(zoomFrict, tmod);
+ if( M.fabs(tz-baseZoom)<=0.05*tmod )
+ dz*=Math.pow(0.8,tmod);
+
+ // Reached target zoom
+ if( prevZoom=tz || prevZoom>tz && baseZoom<=tz ) {
+ baseZoom = tz;
+ dz = 0;
+ }
+
+
+ // Follow target entity
+ if( target!=null ) {
+ var spdX = 0.015*trackingSpeed*zoom;
+ var spdY = 0.023*trackingSpeed*zoom;
+ var tx = target.centerX + targetOffX;
+ var ty = target.centerY + targetOffY;
+
+ var a = rawFocus.angTo(tx,ty);
+ var distX = M.fabs( tx - rawFocus.levelX );
+ if( distX>=deadZonePctX*pxWid )
+ dx += Math.cos(a) * (0.8*distX-deadZonePctX*pxWid) * spdX * tmod;
+
+ var distY = M.fabs( ty - rawFocus.levelY );
+ if( distY>=deadZonePctY*pxHei)
+ dy += Math.sin(a) * (0.8*distY-deadZonePctY*pxHei) * spdY * tmod;
+ }
+
+ // Compute frictions
+ var frictX = baseFrict - trackingSpeed*zoom*0.027*baseFrict;
+ var frictY = frictX;
+ if( clampToLevelBounds ) {
+ // "Brake" when approaching bounds
+ final brakeDist = brakeDistNearBounds * pxWid;
+ if( dx<=0 ) {
+ final brakeRatio = 1-M.fclamp( ( rawFocus.levelX - pxWid*0.5 ) / brakeDist, 0, 1 );
+ frictX *= 1 - 1*brakeRatio;
+ }
+ else if( dx>0 ) {
+ final brakeRatio = 1-M.fclamp( ( (level.pxWid-pxWid*0.5) - rawFocus.levelX ) / brakeDist, 0, 1 );
+ frictX *= 1 - 0.9*brakeRatio;
+ }
+
+ final brakeDist = brakeDistNearBounds * pxHei;
+ if( dy<0 ) {
+ final brakeRatio = 1-M.fclamp( ( rawFocus.levelY - pxHei*0.5 ) / brakeDist, 0, 1 );
+ frictY *= 1 - 0.9*brakeRatio;
+ }
+ else if( dy>0 ) {
+ final brakeRatio = 1-M.fclamp( ( (level.pxHei-pxHei*0.5) - rawFocus.levelY ) / brakeDist, 0, 1 );
+ frictY *= 1 - 0.9*brakeRatio;
+ }
+ }
+
+ // Apply velocities
+ rawFocus.levelX += dx*tmod;
+ dx *= Math.pow(frictX,tmod);
+ rawFocus.levelY += dy*tmod;
+ dy *= Math.pow(frictY,tmod);
+
+
+ // Bounds clamping
+ if( clampToLevelBounds ) {
+ // X
+ if( level.pxWid < pxWid)
+ clampedFocus.levelX = level.pxWid*0.5; // centered small level
+ else
+ clampedFocus.levelX = M.fclamp( rawFocus.levelX, pxWid*0.5, level.pxWid-pxWid*0.5 );
+
+ // Y
+ if( level.pxHei < pxHei)
+ clampedFocus.levelY = level.pxHei*0.5; // centered small level
+ else
+ clampedFocus.levelY = M.fclamp( rawFocus.levelY, pxHei*0.5, level.pxHei-pxHei*0.5 );
+ }
+ else {
+ // No clamping
+ clampedFocus.levelX = rawFocus.levelX;
+ clampedFocus.levelY = rawFocus.levelY;
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/src/game/Const.hx b/src/game/Const.hx
new file mode 100644
index 0000000..dbfbb7b
--- /dev/null
+++ b/src/game/Const.hx
@@ -0,0 +1,80 @@
+/**
+ The Const class is a place for you to store various values that should be available everywhere in your code. Example: `Const.FPS`
+**/
+class Const {
+#if !macro
+
+ /** Default engine framerate (60) **/
+ public static var FPS(get,never) : Int;
+ static inline function get_FPS() return Std.int( hxd.System.getDefaultFrameRate() );
+
+ /**
+ "Fixed" updates framerate. 30fps is a good value here, as it's almost guaranteed to work on any decent setup, and it's more than enough to run any gameplay related physics.
+ **/
+ public static final FIXED_UPDATE_FPS = 30;
+
+ /** Grid size in pixels **/
+ public static final GRID = 16;
+
+ /** "Infinite", sort-of. More like a "big number" **/
+ public static final INFINITE : Int = 0xfffFfff;
+
+ static var _nextUniqueId = 0;
+ /** Unique value generator **/
+ public static inline function makeUniqueId() {
+ return _nextUniqueId++;
+ }
+
+ /** Viewport scaling **/
+ public static var SCALE(get,never) : Int;
+ static inline function get_SCALE() {
+ // can be replaced with another way to determine the game scaling
+ return dn.heaps.Scaler.bestFit_i(200,200);
+ }
+
+ /** Specific scaling for top UI elements **/
+ public static var UI_SCALE(get,never) : Float;
+ static inline function get_UI_SCALE() {
+ // can be replaced with another way to determine the UI scaling
+ return dn.heaps.Scaler.bestFit_i(400,400);
+ }
+
+
+ /** Current build information, including date, time, language & various other things **/
+ public static var BUILD_INFO(get,never) : String;
+ static function get_BUILD_INFO() return dn.MacroTools.getBuildInfo();
+
+
+ /** Game layers indexes **/
+ static var _inc = 0;
+ public static var DP_BG = _inc++;
+ public static var DP_FX_BG = _inc++;
+ public static var DP_MAIN = _inc++;
+ public static var DP_FRONT = _inc++;
+ public static var DP_FX_FRONT = _inc++;
+ public static var DP_TOP = _inc++;
+ public static var DP_UI = _inc++;
+
+
+ /**
+ Simplified "constants database" using CastleDB and JSON files
+ It will be filled with all values found in both following sources:
+
+ - `res/const.json`, a basic JSON file,
+ - `res/data.cdb`, the CastleDB file, from the sheet named "ConstDb".
+
+ This allows super easy access to your game constants and settings. Example:
+
+ Having `res/const.json`:
+ { "myValue":5, "someText":"hello" }
+
+ You may use:
+ Const.db.myValue; // equals to 5
+ Const.db.someText; // equals to "hello"
+
+ If the JSON changes on runtime, the `myValue` field is kept up-to-date, allowing testing without recompiling. IMPORTANT: this hot-reloading only works if the project was built using the `-debug` flag. In release builds, all values become constants and are fully embedded.
+ **/
+ public static var db = ConstDbBuilder.buildVar(["data.cdb", "const.json"]);
+
+#end
+}
diff --git a/src/game/Entity.hx b/src/game/Entity.hx
new file mode 100644
index 0000000..2381fd9
--- /dev/null
+++ b/src/game/Entity.hx
@@ -0,0 +1,831 @@
+class Entity {
+ public static var ALL : FixedArray = new FixedArray(1024);
+ public static var GC : FixedArray = new FixedArray(ALL.maxSize);
+
+ // Various getters to access all important stuff easily
+ public var app(get,never) : App; inline function get_app() return App.ME;
+ public var game(get,never) : Game; inline function get_game() return Game.ME;
+ public var fx(get,never) : Fx; inline function get_fx() return Game.ME.fx;
+ public var level(get,never) : Level; inline function get_level() return Game.ME.level;
+ public var destroyed(default,null) = false;
+ public var ftime(get,never) : Float; inline function get_ftime() return game.ftime;
+ public var camera(get,never) : Camera; inline function get_camera() return game.camera;
+
+ var tmod(get,never) : Float; inline function get_tmod() return Game.ME.tmod;
+ var utmod(get,never) : Float; inline function get_utmod() return Game.ME.utmod;
+ public var hud(get,never) : ui.Hud; inline function get_hud() return Game.ME.hud;
+
+ /** Cooldowns **/
+ public var cd : dn.Cooldown;
+
+ /** Cooldowns, unaffected by slowmo (ie. always in realtime) **/
+ public var ucd : dn.Cooldown;
+
+ /** Temporary gameplay affects **/
+ var affects : Map = new Map();
+
+ /** State machine. Value should only be changed using `startState(v)` **/
+ public var state(default,null) : State;
+
+ /** Unique identifier **/
+ public var uid(default,null) : Int;
+
+ /** Grid X coordinate **/
+ public var cx = 0;
+ /** Grid Y coordinate **/
+ public var cy = 0;
+ /** Sub-grid X coordinate (from 0.0 to 1.0) **/
+ public var xr = 0.5;
+ /** Sub-grid Y coordinate (from 0.0 to 1.0) **/
+ public var yr = 1.0;
+
+ var allVelocities : VelocityArray;
+
+ /** Base X/Y velocity of the Entity **/
+ public var vBase : Velocity;
+ /** "External bump" velocity. It is used to push the Entity in some direction, independently of the "user-controlled" base velocity. **/
+ public var vBump : Velocity;
+
+ /** Last known X position of the attach point (in pixels), at the beginning of the latest fixedUpdate **/
+ var lastFixedUpdateX = 0.;
+ /** Last known Y position of the attach point (in pixels), at the beginning of the latest fixedUpdate **/
+ var lastFixedUpdateY = 0.;
+
+ /** If TRUE, the sprite display coordinates will be an interpolation between the last known position and the current one. This is useful if the gameplay happens in the `fixedUpdate()` (so at 30 FPS), but you still want the sprite position to move smoothly at 60 FPS or more. **/
+ var interpolateSprPos = true;
+
+ /** Total of all X velocities **/
+ public var dxTotal(get,never) : Float; inline function get_dxTotal() return allVelocities.getSumX();
+ /** Total of all Y velocities **/
+ public var dyTotal(get,never) : Float; inline function get_dyTotal() return allVelocities.getSumY();
+
+ /** Pixel width of entity **/
+ public var wid(default,set) : Float = Const.GRID;
+ inline function set_wid(v) { invalidateDebugBounds=true; return wid=v; }
+ public var iwid(get,set) : Int;
+ inline function get_iwid() return M.round(wid);
+ inline function set_iwid(v:Int) { invalidateDebugBounds=true; wid=v; return iwid; }
+
+ /** Pixel height of entity **/
+ public var hei(default,set) : Float = Const.GRID;
+ inline function set_hei(v) { invalidateDebugBounds=true; return hei=v; }
+ public var ihei(get,set) : Int;
+ inline function get_ihei() return M.round(hei);
+ inline function set_ihei(v:Int) { invalidateDebugBounds=true; hei=v; return ihei; }
+
+ /** Inner radius in pixels (ie. smallest value between width/height, then divided by 2) **/
+ public var innerRadius(get,never) : Float;
+ inline function get_innerRadius() return M.fmin(wid,hei)*0.5;
+
+ /** "Large" radius in pixels (ie. biggest value between width/height, then divided by 2) **/
+ public var largeRadius(get,never) : Float;
+ inline function get_largeRadius() return M.fmax(wid,hei)*0.5;
+
+ /** Horizontal direction, can only be -1 or 1 **/
+ public var dir(default,set) = 1;
+
+ /** Current sprite X **/
+ public var sprX(get,never) : Float;
+ inline function get_sprX() {
+ return interpolateSprPos
+ ? M.lerp( lastFixedUpdateX, (cx+xr)*Const.GRID, game.getFixedUpdateAccuRatio() )
+ : (cx+xr)*Const.GRID;
+ }
+
+ /** Current sprite Y **/
+ public var sprY(get,never) : Float;
+ inline function get_sprY() {
+ return interpolateSprPos
+ ? M.lerp( lastFixedUpdateY, (cy+yr)*Const.GRID, game.getFixedUpdateAccuRatio() )
+ : (cy+yr)*Const.GRID;
+ }
+
+ /** Sprite X scaling **/
+ public var sprScaleX = 1.0;
+ /** Sprite Y scaling **/
+ public var sprScaleY = 1.0;
+
+ /** Sprite X squash & stretch scaling, which automatically comes back to 1 after a few frames **/
+ var sprSquashX = 1.0;
+ /** Sprite Y squash & stretch scaling, which automatically comes back to 1 after a few frames **/
+ var sprSquashY = 1.0;
+
+ /** Entity visibility **/
+ public var entityVisible = true;
+
+ /** Current hit points **/
+ public var life(default,null) : dn.struct.Stat;
+ /** Last source of damage if it was an Entity **/
+ public var lastDmgSource(default,null) : Null;
+
+ /** Horizontal direction (left=-1 or right=1): from "last source of damage" to "this" **/
+ public var lastHitDirFromSource(get,never) : Int;
+ inline function get_lastHitDirFromSource() return lastDmgSource==null ? -dir : -dirTo(lastDmgSource);
+
+ /** Horizontal direction (left=-1 or right=1): from "this" to "last source of damage" **/
+ public var lastHitDirToSource(get,never) : Int;
+ inline function get_lastHitDirToSource() return lastDmgSource==null ? dir : dirTo(lastDmgSource);
+
+ /** Main entity HSprite instance **/
+ public var spr : HSprite;
+
+ /** Color vector transformation applied to sprite **/
+ public var baseColor : h3d.Vector;
+
+ /** Color matrix transformation applied to sprite **/
+ public var colorMatrix : h3d.Matrix;
+
+ // Animated blink color on damage hit
+ var blinkColor : h3d.Vector;
+
+ /** Sprite X shake power **/
+ var shakePowX = 0.;
+ /** Sprite Y shake power **/
+ var shakePowY = 0.;
+
+ // Debug stuff
+ var debugLabel : Null;
+ var debugBounds : Null;
+ var invalidateDebugBounds = false;
+
+ /** Defines X alignment of entity at its attach point (0 to 1.0) **/
+ public var pivotX(default,set) : Float = 0.5;
+ /** Defines Y alignment of entity at its attach point (0 to 1.0) **/
+ public var pivotY(default,set) : Float = 1;
+
+ /** Entity attach X pixel coordinate **/
+ public var attachX(get,never) : Float; inline function get_attachX() return (cx+xr)*Const.GRID;
+ /** Entity attach Y pixel coordinate **/
+ public var attachY(get,never) : Float; inline function get_attachY() return (cy+yr)*Const.GRID;
+
+ // Various coordinates getters, for easier gameplay coding
+
+ /** Left pixel coordinate of the bounding box **/
+ public var left(get,never) : Float; inline function get_left() return attachX + (0-pivotX) * wid;
+ /** Right pixel coordinate of the bounding box **/
+ public var right(get,never) : Float; inline function get_right() return attachX + (1-pivotX) * wid;
+ /** Top pixel coordinate of the bounding box **/
+ public var top(get,never) : Float; inline function get_top() return attachY + (0-pivotY) * hei;
+ /** Bottom pixel coordinate of the bounding box **/
+ public var bottom(get,never) : Float; inline function get_bottom() return attachY + (1-pivotY) * hei;
+
+ /** Center X pixel coordinate of the bounding box **/
+ public var centerX(get,never) : Float; inline function get_centerX() return attachX + (0.5-pivotX) * wid;
+ /** Center Y pixel coordinate of the bounding box **/
+ public var centerY(get,never) : Float; inline function get_centerY() return attachY + (0.5-pivotY) * hei;
+
+ /** Current X position on screen (ie. absolute)**/
+ public var screenAttachX(get,never) : Float;
+ inline function get_screenAttachX() return game!=null && !game.destroyed ? sprX*Const.SCALE + game.scroller.x : sprX*Const.SCALE;
+
+ /** Current Y position on screen (ie. absolute)**/
+ public var screenAttachY(get,never) : Float;
+ inline function get_screenAttachY() return game!=null && !game.destroyed ? sprY*Const.SCALE + game.scroller.y : sprY*Const.SCALE;
+
+ /** attachX value during last frame **/
+ public var prevFrameAttachX(default,null) : Float = -Const.INFINITE;
+ /** attachY value during last frame **/
+ public var prevFrameAttachY(default,null) : Float = -Const.INFINITE;
+
+ var actions : RecyclablePool;
+
+
+ /**
+ Constructor
+ **/
+ public function new(x:Int, y:Int) {
+ uid = Const.makeUniqueId();
+ ALL.push(this);
+
+ cd = new dn.Cooldown(Const.FPS);
+ ucd = new dn.Cooldown(Const.FPS);
+ life = new Stat();
+ setPosCase(x,y);
+ initLife(1);
+ state = Normal;
+ actions = new RecyclablePool(15, ()->new tools.ChargedAction());
+
+ allVelocities = new VelocityArray(15);
+ vBase = registerNewVelocity(0.82);
+ vBump = registerNewVelocity(0.93);
+
+ spr = new HSprite(Assets.tiles);
+ Game.ME.scroller.add(spr, Const.DP_MAIN);
+ spr.colorAdd = new h3d.Vector();
+ baseColor = new h3d.Vector();
+ blinkColor = new h3d.Vector();
+ spr.colorMatrix = colorMatrix = h3d.Matrix.I();
+ spr.setCenterRatio(pivotX, pivotY);
+
+ if( ui.Console.ME.hasFlag(F_Bounds) )
+ enableDebugBounds();
+ }
+
+
+ public function registerNewVelocity(frict:Float) : Velocity {
+ var v = Velocity.createFrict(frict);
+ allVelocities.push(v);
+ return v;
+ }
+
+
+ /** Remove sprite from display context. Only do that if you're 100% sure your entity won't need the `spr` instance itself. **/
+ function noSprite() {
+ spr.setEmptyTexture();
+ spr.remove();
+ }
+
+
+ function set_pivotX(v) {
+ pivotX = M.fclamp(v,0,1);
+ if( spr!=null )
+ spr.setCenterRatio(pivotX, pivotY);
+ return pivotX;
+ }
+
+ function set_pivotY(v) {
+ pivotY = M.fclamp(v,0,1);
+ if( spr!=null )
+ spr.setCenterRatio(pivotX, pivotY);
+ return pivotY;
+ }
+
+ /** Initialize current and max hit points **/
+ public function initLife(v) {
+ life.initMaxOnMax(v);
+ }
+
+ /** Inflict damage **/
+ public function hit(dmg:Int, from:Null) {
+ if( !isAlive() || dmg<=0 )
+ return;
+
+ life.v -= dmg;
+ lastDmgSource = from;
+ onDamage(dmg, from);
+ if( life.v<=0 )
+ onDie();
+ }
+
+ /** Kill instantly **/
+ public function kill(by:Null) {
+ if( isAlive() )
+ hit(life.v, by);
+ }
+
+ function onDamage(dmg:Int, from:Entity) {}
+
+ function onDie() {
+ destroy();
+ }
+
+ inline function set_dir(v) {
+ return dir = v>0 ? 1 : v<0 ? -1 : dir;
+ }
+
+ /** Return TRUE if current entity wasn't destroyed or killed **/
+ public inline function isAlive() {
+ return !destroyed && life.v>0;
+ }
+
+ /** Move entity to grid coordinates **/
+ public function setPosCase(x:Int, y:Int) {
+ cx = x;
+ cy = y;
+ xr = 0.5;
+ yr = 1;
+ onPosManuallyChangedBoth();
+ }
+
+ /** Move entity to pixel coordinates **/
+ public function setPosPixel(x:Float, y:Float) {
+ cx = Std.int(x/Const.GRID);
+ cy = Std.int(y/Const.GRID);
+ xr = (x-cx*Const.GRID)/Const.GRID;
+ yr = (y-cy*Const.GRID)/Const.GRID;
+ onPosManuallyChangedBoth();
+ }
+
+ /** Should be called when you manually (ie. ignoring physics) modify both X & Y entity coordinates **/
+ function onPosManuallyChangedBoth() {
+ if( M.dist(attachX,attachY,prevFrameAttachX,prevFrameAttachY) > Const.GRID*2 ) {
+ prevFrameAttachX = attachX;
+ prevFrameAttachY = attachY;
+ }
+ updateLastFixedUpdatePos();
+ }
+
+ /** Should be called when you manually (ie. ignoring physics) modify entity X coordinate **/
+ function onPosManuallyChangedX() {
+ if( M.fabs(attachX-prevFrameAttachX) > Const.GRID*2 )
+ prevFrameAttachX = attachX;
+ lastFixedUpdateX = attachX;
+ }
+
+ /** Should be called when you manually (ie. ignoring physics) modify entity Y coordinate **/
+ function onPosManuallyChangedY() {
+ if( M.fabs(attachY-prevFrameAttachY) > Const.GRID*2 )
+ prevFrameAttachY = attachY;
+ lastFixedUpdateY = attachY;
+ }
+
+
+ /** Quickly set X/Y pivots. If Y is omitted, it will be equal to X. **/
+ public function setPivots(x:Float, ?y:Float) {
+ pivotX = x;
+ pivotY = y!=null ? y : x;
+ }
+
+ /** Return TRUE if the Entity *center point* is in screen bounds (default padding is +32px) **/
+ public inline function isOnScreenCenter(padding=32) {
+ return camera.isOnScreen( centerX, centerY, padding + M.fmax(wid*0.5, hei*0.5) );
+ }
+
+ /** Return TRUE if the Entity rectangle is in screen bounds (default padding is +32px) **/
+ public inline function isOnScreenBounds(padding=32) {
+ return camera.isOnScreenRect( left,top, wid, hei, padding );
+ }
+
+
+ /**
+ Changed the current entity state.
+ Return TRUE if the state is `s` after the call.
+ **/
+ public function startState(s:State) : Bool {
+ if( s==state )
+ return true;
+
+ if( !canChangeStateTo(state, s) )
+ return false;
+
+ var old = state;
+ state = s;
+ onStateChange(old,state);
+ return true;
+ }
+
+
+ /** Return TRUE to allow a change of the state value **/
+ function canChangeStateTo(from:State, to:State) {
+ return true;
+ }
+
+ /** Called when state is changed to a new value **/
+ function onStateChange(old:State, newState:State) {}
+
+
+ /** Apply a bump/kick force to entity **/
+ public function bump(x:Float,y:Float) {
+ vBump.addXY(x,y);
+ }
+
+ /** Reset velocities to zero **/
+ public function cancelVelocities() {
+ allVelocities.clearAll();
+ }
+
+ public function is(c:Class) return Std.isOfType(this, c);
+ public function as(c:Class) : T return Std.downcast(this, c);
+
+ /** Return a random Float value in range [min,max]. If `sign` is TRUE, returned value might be multiplied by -1 randomly. **/
+ public inline function rnd(min,max,?sign) return Lib.rnd(min,max,sign);
+ /** Return a random Integer value in range [min,max]. If `sign` is TRUE, returned value might be multiplied by -1 randomly. **/
+ public inline function irnd(min,max,?sign) return Lib.irnd(min,max,sign);
+
+ /** Truncate a float value using given `precision` **/
+ public inline function pretty(value:Float,?precision=1) return M.pretty(value,precision);
+
+ public inline function dirTo(e:Entity) return e.centerXVoid, ?onProgress:ChargedAction->Void) {
+ if( !isAlive() )
+ return;
+
+ if( isChargingAction(id) )
+ cancelAction(id);
+
+ var a = actions.alloc();
+ a.id = id;
+ a.onComplete = onComplete;
+ a.durationS = sec;
+ if( onProgress!=null )
+ a.onProgress = onProgress;
+ }
+
+ /** If id is null, return TRUE if any action is charging. If id is provided, return TRUE if this specific action is charging nokw. **/
+ public function isChargingAction(?id:ChargedActionId) {
+ if( !isAlive() )
+ return false;
+
+ if( id==null )
+ return actions.allocated>0;
+
+ for(a in actions)
+ if( a.id==id )
+ return true;
+
+ return false;
+ }
+
+ public function cancelAction(?onlyId:ChargedActionId) {
+ if( !isAlive() )
+ return;
+
+ if( onlyId==null )
+ actions.freeAll();
+ else {
+ var i = 0;
+ while( i0;
+ }
+
+ public inline function getAffectDurationS(k:Affect) {
+ return hasAffect(k) ? affects.get(k) : 0.;
+ }
+
+ /** Add an Affect. If `allowLower` is TRUE, it is possible to override an existing Affect with a shorter duration. **/
+ public function setAffectS(k:Affect, t:Float, allowLower=false) {
+ if( !isAlive() || affects.exists(k) && affects.get(k)>t && !allowLower )
+ return;
+
+ if( t<=0 )
+ clearAffect(k);
+ else {
+ var isNew = !hasAffect(k);
+ affects.set(k,t);
+ if( isNew )
+ onAffectStart(k);
+ }
+ }
+
+ /** Multiply an Affect duration by a factor `f` **/
+ public function mulAffectS(k:Affect, f:Float) {
+ if( hasAffect(k) )
+ setAffectS(k, getAffectDurationS(k)*f, true);
+ }
+
+ public function clearAffect(k:Affect) {
+ if( hasAffect(k) ) {
+ affects.remove(k);
+ onAffectEnd(k);
+ }
+ }
+
+ /** Affects update loop **/
+ function updateAffects() {
+ if( !isAlive() )
+ return;
+
+ for(k in affects.keys()) {
+ var t = affects.get(k);
+ t-=1/Const.FPS * tmod;
+ if( t<=0 )
+ clearAffect(k);
+ else
+ affects.set(k,t);
+ }
+ }
+
+ function onAffectStart(k:Affect) {}
+ function onAffectEnd(k:Affect) {}
+
+ /** Return TRUE if the entity is active and has no status affect that prevents actions. **/
+ public function isConscious() {
+ return !hasAffect(Stun) && isAlive();
+ }
+
+ /** Blink `spr` briefly (eg. when damaged by something) **/
+ public function blink(c:Col) {
+ blinkColor.setColor(c);
+ cd.setS("keepBlink",0.06);
+ }
+
+ public function shakeS(xPow:Float, yPow:Float, t:Float) {
+ cd.setS("shaking", t, true);
+ shakePowX = xPow;
+ shakePowY = yPow;
+ }
+
+ /** Briefly squash sprite on X (Y changes accordingly). "1.0" means no distorsion. **/
+ public function setSquashX(scaleX:Float) {
+ sprSquashX = scaleX;
+ sprSquashY = 2-scaleX;
+ }
+
+ /** Briefly squash sprite on Y (X changes accordingly). "1.0" means no distorsion. **/
+ public function setSquashY(scaleY:Float) {
+ sprSquashX = 2-scaleY;
+ sprSquashY = scaleY;
+ }
+
+
+ /**
+ "Beginning of the frame" loop, called before any other Entity update loop
+ **/
+ public function preUpdate() {
+ ucd.update(utmod);
+ cd.update(tmod);
+ updateAffects();
+ updateActions();
+
+
+ #if debug
+ // Display the list of active "affects" (with `/set affect` in console)
+ if( ui.Console.ME.hasFlag(F_Affects) ) {
+ var all = [];
+ for(k in affects.keys())
+ all.push( k+"=>"+M.pretty( getAffectDurationS(k) , 1) );
+ debug(all);
+ }
+
+ // Show bounds (with `/bounds` in console)
+ if( ui.Console.ME.hasFlag(F_Bounds) && debugBounds==null )
+ enableDebugBounds();
+
+ // Hide bounds
+ if( !ui.Console.ME.hasFlag(F_Bounds) && debugBounds!=null )
+ disableDebugBounds();
+ #end
+
+ }
+
+ /**
+ Post-update loop, which is guaranteed to happen AFTER any preUpdate/update. This is usually where render and display is updated
+ **/
+ public function postUpdate() {
+ spr.x = sprX;
+ spr.y = sprY;
+ spr.scaleX = dir*sprScaleX * sprSquashX;
+ spr.scaleY = sprScaleY * sprSquashY;
+ spr.visible = entityVisible;
+
+ sprSquashX += (1-sprSquashX) * M.fmin(1, 0.2*tmod);
+ sprSquashY += (1-sprSquashY) * M.fmin(1, 0.2*tmod);
+
+ if( cd.has("shaking") ) {
+ spr.x += Math.cos(ftime*1.1)*shakePowX * cd.getRatio("shaking");
+ spr.y += Math.sin(0.3+ftime*1.7)*shakePowY * cd.getRatio("shaking");
+ }
+
+ // Blink
+ if( !cd.has("keepBlink") ) {
+ blinkColor.r*=Math.pow(0.60, tmod);
+ blinkColor.g*=Math.pow(0.55, tmod);
+ blinkColor.b*=Math.pow(0.50, tmod);
+ }
+
+ // Color adds
+ spr.colorAdd.load(baseColor);
+ spr.colorAdd.r += blinkColor.r;
+ spr.colorAdd.g += blinkColor.g;
+ spr.colorAdd.b += blinkColor.b;
+
+ // Debug label
+ if( debugLabel!=null ) {
+ debugLabel.x = Std.int(attachX - debugLabel.textWidth*0.5);
+ debugLabel.y = Std.int(attachY+1);
+ }
+
+ // Debug bounds
+ if( debugBounds!=null ) {
+ if( invalidateDebugBounds ) {
+ invalidateDebugBounds = false;
+ renderDebugBounds();
+ }
+ debugBounds.x = Std.int(attachX);
+ debugBounds.y = Std.int(attachY);
+ }
+ }
+
+ /**
+ Loop that runs at the absolute end of the frame
+ **/
+ public function finalUpdate() {
+ prevFrameAttachX = attachX;
+ prevFrameAttachY = attachY;
+ }
+
+
+ final function updateLastFixedUpdatePos() {
+ lastFixedUpdateX = attachX;
+ lastFixedUpdateY = attachY;
+ }
+
+
+
+ /** Called at the beginning of each X movement step **/
+ function onPreStepX() {
+ }
+
+ /** Called at the beginning of each Y movement step **/
+ function onPreStepY() {
+ }
+
+
+ /**
+ Main loop, but it only runs at a "guaranteed" 30 fps (so it might not be called during some frames, if the app runs at 60fps). This is usually where most gameplay elements affecting physics should occur, to ensure these will not depend on FPS at all.
+ **/
+ public function fixedUpdate() {
+ updateLastFixedUpdatePos();
+
+ /*
+ Stepping: any movement greater than 33% of grid size (ie. 0.33) will increase the number of `steps` here. These steps will break down the full movement into smaller iterations to avoid jumping over grid collisions.
+ */
+ var steps = M.ceil( ( M.fabs(dxTotal) + M.fabs(dyTotal) ) / 0.33 );
+ if( steps>0 ) {
+ var n = 0;
+ while ( n1 ) { xr--; cx++; }
+ while( xr<0 ) { xr++; cx--; }
+
+
+ // Y movement
+ yr += dyTotal / steps;
+
+ if( dyTotal!=0 )
+ onPreStepY(); // <---- Add Y collisions checks and physics in here
+
+ while( yr>1 ) { yr--; cy++; }
+ while( yr<0 ) { yr++; cy--; }
+
+ n++;
+ }
+ }
+
+ // Update velocities
+ for(v in allVelocities)
+ v.fixedUpdate();
+ }
+
+
+ /**
+ Main loop running at full FPS (ie. always happen once on every frames, after preUpdate and before postUpdate)
+ **/
+ public function frameUpdate() {
+ }
+}
\ No newline at end of file
diff --git a/src/game/Fx.hx b/src/game/Fx.hx
new file mode 100644
index 0000000..d23873e
--- /dev/null
+++ b/src/game/Fx.hx
@@ -0,0 +1,177 @@
+import h2d.Sprite;
+import dn.heaps.HParticle;
+
+
+class Fx extends GameChildProcess {
+ var pool : ParticlePool;
+
+ public var bg_add : h2d.SpriteBatch;
+ public var bg_normal : h2d.SpriteBatch;
+ public var main_add : h2d.SpriteBatch;
+ public var main_normal : h2d.SpriteBatch;
+
+ public function new() {
+ super();
+
+ pool = new ParticlePool(Assets.tiles.tile, 2048, Const.FPS);
+
+ bg_add = new h2d.SpriteBatch(Assets.tiles.tile);
+ game.scroller.add(bg_add, Const.DP_FX_BG);
+ bg_add.blendMode = Add;
+ bg_add.hasRotationScale = true;
+
+ bg_normal = new h2d.SpriteBatch(Assets.tiles.tile);
+ game.scroller.add(bg_normal, Const.DP_FX_BG);
+ bg_normal.hasRotationScale = true;
+
+ main_normal = new h2d.SpriteBatch(Assets.tiles.tile);
+ game.scroller.add(main_normal, Const.DP_FX_FRONT);
+ main_normal.hasRotationScale = true;
+
+ main_add = new h2d.SpriteBatch(Assets.tiles.tile);
+ game.scroller.add(main_add, Const.DP_FX_FRONT);
+ main_add.blendMode = Add;
+ main_add.hasRotationScale = true;
+ }
+
+ override public function onDispose() {
+ super.onDispose();
+
+ pool.dispose();
+ bg_add.remove();
+ bg_normal.remove();
+ main_add.remove();
+ main_normal.remove();
+ }
+
+ /** Clear all particles **/
+ public function clear() {
+ pool.clear();
+ }
+
+ /** Create a HParticle instance in the BG layer, using ADDITIVE blendmode **/
+ public inline function allocBg_add(id,x,y) return pool.alloc(bg_add, Assets.tiles.getTileRandom(id), x, y);
+
+ /** Create a HParticle instance in the BG layer, using NORMAL blendmode **/
+ public inline function allocBg_normal(id,x,y) return pool.alloc(bg_normal, Assets.tiles.getTileRandom(id), x, y);
+
+ /** Create a HParticle instance in the MAIN layer, using ADDITIVE blendmode **/
+ public inline function allocMain_add(id,x,y) return pool.alloc( main_add, Assets.tiles.getTileRandom(id), x, y );
+
+ /** Create a HParticle instance in the MAIN layer, using NORMAL blendmode **/
+ public inline function allocMain_normal(id,x,y) return pool.alloc(main_normal, Assets.tiles.getTileRandom(id), x, y);
+
+
+ public inline function markerEntity(e:Entity, c:Col=Pink, sec=3.0) {
+ #if debug
+ if( e!=null && e.isAlive() ) {
+ var p = allocMain_add(D.tiles.fxCircle15, e.attachX, e.attachY);
+ p.setCenterRatio(e.pivotX, e.pivotY);
+ p.scaleTo(e.wid, e.hei);
+ p.setFadeS(1, 0, 0.06);
+ p.colorize(c);
+ p.lifeS = sec;
+
+ var p = allocMain_add(D.tiles.pixel, e.attachX, e.attachY);
+ p.setFadeS(1, 0, 0.06);
+ p.colorize(c);
+ p.setScale(2);
+ p.lifeS = sec;
+ }
+ #end
+ }
+
+ public inline function markerCase(cx:Int, cy:Int, sec=3.0, c:Col=Pink) {
+ #if debug
+ var p = allocMain_add(D.tiles.fxCircle15, (cx+0.5)*Const.GRID, (cy+0.5)*Const.GRID);
+ p.setFadeS(1, 0, 0.06);
+ p.colorize(c);
+ p.lifeS = sec;
+
+ var p = allocMain_add(D.tiles.pixel, (cx+0.5)*Const.GRID, (cy+0.5)*Const.GRID);
+ p.setFadeS(1, 0, 0.06);
+ p.colorize(c);
+ p.setScale(2);
+ p.lifeS = sec;
+ #end
+ }
+
+ public inline function markerFree(x:Float, y:Float, sec=3.0, c:Col=Pink) {
+ #if debug
+ var p = allocMain_add(D.tiles.fxDot, x,y);
+ p.setCenterRatio(0.5,0.5);
+ p.setFadeS(1, 0, 0.06);
+ p.colorize(c);
+ p.setScale(3);
+ p.lifeS = sec;
+ #end
+ }
+
+ public inline function markerText(cx:Int, cy:Int, txt:String, t=1.0) {
+ #if debug
+ var tf = new h2d.Text(Assets.fontPixel, main_normal);
+ tf.text = txt;
+
+ var p = allocMain_add(D.tiles.fxCircle15, (cx+0.5)*Const.GRID, (cy+0.5)*Const.GRID);
+ p.colorize(0x0080FF);
+ p.alpha = 0.6;
+ p.lifeS = 0.3;
+ p.fadeOutSpeed = 0.4;
+ p.onKill = tf.remove;
+
+ tf.setPosition(p.x-tf.textWidth*0.5, p.y-tf.textHeight*0.5);
+ #end
+ }
+
+
+ public inline function markerLine(fx:Float, fy:Float, tx:Float, ty:Float, c:Col, sec=3.) {
+ #if debug
+ var p = allocMain_add(D.tiles.fxLine, fx,fy);
+ p.setFadeS(1, 0, 0);
+ p.colorize(c);
+ p.setCenterRatio(0,0.5);
+ p.scaleX = M.dist(fx,fy,tx,ty) / p.t.width;
+ p.rotation = Math.atan2(ty-fy, tx-fx);
+ p.lifeS = sec;
+ #end
+ }
+
+ inline function collides(p:HParticle, offX=0., offY=0.) {
+ return level.hasCollision( Std.int((p.x+offX)/Const.GRID), Std.int((p.y+offY)/Const.GRID) );
+ }
+
+ public inline function flashBangS(c:Col, a:Float, t=0.1) {
+ var e = new h2d.Bitmap(h2d.Tile.fromColor(c,1,1,a));
+ game.root.add(e, Const.DP_FX_FRONT);
+ e.scaleX = game.w();
+ e.scaleY = game.h();
+ e.blendMode = Add;
+ game.tw.createS(e.alpha, 0, t).end( function() {
+ e.remove();
+ });
+ }
+
+
+ /**
+ A small sample to demonstrate how basic particles work. This example produces a small explosion of yellow dots that will fall and slowly fade to purple.
+
+ USAGE: fx.dotsExplosionExample(50,50, 0xffcc00)
+ **/
+ public inline function dotsExplosionExample(x:Float, y:Float, color:Col) {
+ for(i in 0...80) {
+ var p = allocMain_add( D.tiles.fxDot, x+rnd(0,3,true), y+rnd(0,3,true) );
+ p.alpha = rnd(0.4,1);
+ p.colorAnimS(color, 0x762087, rnd(0.6, 3)); // fade particle color from given color to some purple
+ p.moveAwayFrom(x,y, rnd(1,3)); // move away from source
+ p.frict = rnd(0.8, 0.9); // friction applied to velocities
+ p.gy = rnd(0, 0.02); // gravity Y (added on each frame)
+ p.lifeS = rnd(2,3); // life time in seconds
+ }
+ }
+
+
+ override function update() {
+ super.update();
+ pool.update(game.tmod);
+ }
+}
\ No newline at end of file
diff --git a/src/game/Game.hx b/src/game/Game.hx
new file mode 100644
index 0000000..98e0cfc
--- /dev/null
+++ b/src/game/Game.hx
@@ -0,0 +1,237 @@
+class Game extends AppChildProcess {
+ public static var ME : Game;
+
+ /** Game controller (pad or keyboard) **/
+ public var ca : ControllerAccess;
+
+ /** Particles **/
+ public var fx : Fx;
+
+ /** Basic viewport control **/
+ public var camera : Camera;
+
+ /** Container of all visual game objects. Ths wrapper is moved around by Camera. **/
+ public var scroller : h2d.Layers;
+
+ /** Level data **/
+ public var level : Level;
+
+ /** UI **/
+ public var hud : ui.Hud;
+
+ /** Slow mo internal values**/
+ var curGameSpeed = 1.0;
+ var slowMos : Map = new Map();
+
+
+ public function new() {
+ super();
+
+ ME = this;
+ ca = App.ME.controller.createAccess();
+ ca.lockCondition = isGameControllerLocked;
+ createRootInLayers(App.ME.root, Const.DP_BG);
+ dn.Gc.runNow();
+
+ scroller = new h2d.Layers();
+ root.add(scroller, Const.DP_BG);
+ scroller.filter = new h2d.filter.Nothing(); // force rendering for pixel perfect
+
+ fx = new Fx();
+ hud = new ui.Hud();
+ camera = new Camera();
+
+ startLevel(Assets.worldData.all_worlds.SampleWorld.all_levels.FirstLevel);
+ }
+
+
+ public static function isGameControllerLocked() {
+ return !exists() || ME.isPaused() || App.ME.anyInputHasFocus();
+ }
+
+
+ public static inline function exists() {
+ return ME!=null && !ME.destroyed;
+ }
+
+
+ /** Load a level **/
+ function startLevel(l:World.World_Level) {
+ if( level!=null )
+ level.destroy();
+ fx.clear();
+ for(e in Entity.ALL) // <---- Replace this with more adapted entity destruction (eg. keep the player alive)
+ e.destroy();
+ garbageCollectEntities();
+
+ level = new Level(l);
+ // <---- Here: instanciate your level entities
+
+ camera.centerOnTarget();
+ hud.onLevelStart();
+ dn.Process.resizeAll();
+ dn.Gc.runNow();
+ }
+
+
+
+ /** Called when either CastleDB or `const.json` changes on disk **/
+ @:allow(App)
+ function onDbReload() {
+ hud.notify("DB reloaded");
+ }
+
+
+ /** Called when LDtk file changes on disk **/
+ @:allow(assets.Assets)
+ function onLdtkReload() {
+ hud.notify("LDtk reloaded");
+ if( level!=null )
+ startLevel( Assets.worldData.all_worlds.SampleWorld.getLevel(level.data.uid) );
+ }
+
+ /** Window/app resize event **/
+ override function onResize() {
+ super.onResize();
+ }
+
+
+ /** Garbage collect any Entity marked for destruction. This is normally done at the end of the frame, but you can call it manually if you want to make sure marked entities are disposed right away, and removed from lists. **/
+ public function garbageCollectEntities() {
+ if( Entity.GC==null || Entity.GC.allocated==0 )
+ return;
+
+ for(e in Entity.GC)
+ e.dispose();
+ Entity.GC.empty();
+ }
+
+ /** Called if game is destroyed, but only at the end of the frame **/
+ override function onDispose() {
+ super.onDispose();
+
+ fx.destroy();
+ for(e in Entity.ALL)
+ e.destroy();
+ garbageCollectEntities();
+
+ if( ME==this )
+ ME = null;
+ }
+
+
+ /**
+ Start a cumulative slow-motion effect that will affect `tmod` value in this Process
+ and all its children.
+
+ @param sec Realtime second duration of this slowmo
+ @param speedFactor Cumulative multiplier to the Process `tmod`
+ **/
+ public function addSlowMo(id:SlowMoId, sec:Float, speedFactor=0.3) {
+ if( slowMos.exists(id) ) {
+ var s = slowMos.get(id);
+ s.f = speedFactor;
+ s.t = M.fmax(s.t, sec);
+ }
+ else
+ slowMos.set(id, { id:id, t:sec, f:speedFactor });
+ }
+
+
+ /** The loop that updates slow-mos **/
+ final function updateSlowMos() {
+ // Timeout active slow-mos
+ for(s in slowMos) {
+ s.t -= utmod * 1/Const.FPS;
+ if( s.t<=0 )
+ slowMos.remove(s.id);
+ }
+
+ // Update game speed
+ var targetGameSpeed = 1.0;
+ for(s in slowMos)
+ targetGameSpeed*=s.f;
+ curGameSpeed += (targetGameSpeed-curGameSpeed) * (targetGameSpeed>curGameSpeed ? 0.2 : 0.6);
+
+ if( M.fabs(curGameSpeed-targetGameSpeed)<=0.001 )
+ curGameSpeed = targetGameSpeed;
+ }
+
+
+ /**
+ Pause briefly the game for 1 frame: very useful for impactful moments,
+ like when hitting an opponent in Street Fighter ;)
+ **/
+ public inline function stopFrame() {
+ ucd.setS("stopFrame", 4/Const.FPS);
+ }
+
+
+ /** Loop that happens at the beginning of the frame **/
+ override function preUpdate() {
+ super.preUpdate();
+
+ for(e in Entity.ALL) if( !e.destroyed ) e.preUpdate();
+ }
+
+ /** Loop that happens at the end of the frame **/
+ override function postUpdate() {
+ super.postUpdate();
+
+ // Update slow-motions
+ updateSlowMos();
+ baseTimeMul = ( 0.2 + 0.8*curGameSpeed ) * ( ucd.has("stopFrame") ? 0.1 : 1 );
+ Assets.tiles.tmod = tmod;
+
+ // Entities post-updates
+ for(e in Entity.ALL) if( !e.destroyed ) e.postUpdate();
+
+ // Entities final updates
+ for(e in Entity.ALL) if( !e.destroyed ) e.finalUpdate();
+
+ // Dispose entities marked as "destroyed"
+ garbageCollectEntities();
+ }
+
+
+ /** Main loop but limited to 30 fps (so it might not be called during some frames) **/
+ override function fixedUpdate() {
+ super.fixedUpdate();
+
+ // Entities "30 fps" loop
+ for(e in Entity.ALL) if( !e.destroyed ) e.fixedUpdate();
+ }
+
+ /** Main loop **/
+ override function update() {
+ super.update();
+
+ // Entities main loop
+ for(e in Entity.ALL) if( !e.destroyed ) e.frameUpdate();
+
+
+ // Global key shortcuts
+ if( !App.ME.anyInputHasFocus() && !ui.Window.hasAnyModal() && !Console.ME.isActive() ) {
+ // Exit by pressing ESC twice
+ #if hl
+ if( ca.isKeyboardPressed(K.ESCAPE) )
+ if( !cd.hasSetS("exitWarn",3) )
+ hud.notify(Lang.t._("Press ESCAPE again to exit."));
+ else
+ App.ME.exit();
+ #end
+
+ // Attach debug drone (CTRL-SHIFT-D)
+ #if debug
+ if( ca.isPressed(ToggleDebugDrone) )
+ new DebugDrone(); // <-- HERE: provide an Entity as argument to attach Drone near it
+ #end
+
+ // Restart whole game
+ if( ca.isPressed(Restart) )
+ App.ME.startGame();
+
+ }
+ }
+}
+
diff --git a/src/game/Level.hx b/src/game/Level.hx
new file mode 100644
index 0000000..415e641
--- /dev/null
+++ b/src/game/Level.hx
@@ -0,0 +1,78 @@
+class Level extends GameChildProcess {
+ /** Level grid-based width**/
+ public var cWid(default,null): Int;
+ /** Level grid-based height **/
+ public var cHei(default,null): Int;
+
+ /** Level pixel width**/
+ public var pxWid(default,null) : Int;
+ /** Level pixel height**/
+ public var pxHei(default,null) : Int;
+
+ public var data : World_Level;
+ var tilesetSource : h2d.Tile;
+
+ public var marks : dn.MarkerMap;
+ var invalidated = true;
+
+ public function new(ldtkLevel:World.World_Level) {
+ super();
+
+ createRootInLayers(Game.ME.scroller, Const.DP_BG);
+ data = ldtkLevel;
+ cWid = data.l_Collisions.cWid;
+ cHei = data.l_Collisions.cHei;
+ pxWid = cWid * Const.GRID;
+ pxHei = cHei * Const.GRID;
+ tilesetSource = hxd.Res.levels.sampleWorldTiles.toAseprite().toTile();
+
+ marks = new dn.MarkerMap(cWid, cHei);
+ for(cy in 0...cHei)
+ for(cx in 0...cWid) {
+ if( data.l_Collisions.getInt(cx,cy)==1 )
+ marks.set(M_Coll_Wall, cx,cy);
+ }
+ }
+
+ override function onDispose() {
+ super.onDispose();
+ data = null;
+ tilesetSource = null;
+ marks.dispose();
+ marks = null;
+ }
+
+ /** TRUE if given coords are in level bounds **/
+ public inline function isValid(cx,cy) return cx>=0 && cx=0 && cy{
+ // Only reload actual updated file from disk after a short delay, to avoid reading a file being written
+ App.ME.delayer.cancelById("ldtk");
+ App.ME.delayer.addS("ldtk", function() {
+ worldData.parseJson( res.entry.getText() );
+ if( Game.exists() )
+ Game.ME.onLdtkReload();
+ }, 0.2);
+ });
+ #end
+ }
+
+
+ /**
+ Pass `tmod` value from the game to atlases, to allow them to play animations at the same speed as the Game.
+ For example, if the game has some slow-mo running, all atlas anims should also play in slow-mo
+ **/
+ public static function update(tmod:Float) {
+ if( Game.exists() && Game.ME.isPaused() )
+ tmod = 0;
+
+ tiles.tmod = tmod;
+ // <-- add other atlas TMOD updates here
+ }
+
+}
\ No newline at end of file
diff --git a/src/game/assets/AssetsDictionaries.hx b/src/game/assets/AssetsDictionaries.hx
new file mode 100644
index 0000000..602194b
--- /dev/null
+++ b/src/game/assets/AssetsDictionaries.hx
@@ -0,0 +1,15 @@
+package assets;
+
+/**
+ Access to slice names present in Aseprite files (eg. `trace( tiles.fxStar )` ).
+ This class only provides access to *names* (ie. String). To get actual h2d.Tile, use Assets class.
+
+ Examples:
+ ```haxe
+ Assets.tiles.getTile( AssetsDictionaries.tiles.mySlice );
+ Assets.tiles.getTile( D.tiles.mySlice ); // uses "D" alias defined in "import.hx" file
+ ```
+**/
+class AssetsDictionaries {
+ public static var tiles = dn.heaps.assets.Aseprite.getDict( hxd.Res.atlas.tiles );
+}
\ No newline at end of file
diff --git a/src/game/assets/CastleDb.hx b/src/game/assets/CastleDb.hx
new file mode 100644
index 0000000..639c0ce
--- /dev/null
+++ b/src/game/assets/CastleDb.hx
@@ -0,0 +1,3 @@
+package assets;
+
+private typedef Init = haxe.macro.MacroType<[cdb.Module.build("data.cdb")]>;
diff --git a/src/game/assets/ConstDbBuilder.hx b/src/game/assets/ConstDbBuilder.hx
new file mode 100644
index 0000000..e8b7d11
--- /dev/null
+++ b/src/game/assets/ConstDbBuilder.hx
@@ -0,0 +1,457 @@
+package assets;
+
+#if( macro || display )
+import haxe.macro.Context;
+import haxe.macro.Expr;
+using haxe.macro.Tools;
+#end
+
+// Rough CastleDB JSON typedef
+typedef CastleDbJson = {
+ sheets : Array<{
+ name : String,
+ columns : Array<{
+ typeStr: String,
+ name: String,
+ }>,
+ lines:Array<{
+ constId: String,
+ values: Array<{
+ value : Dynamic,
+ valueName : String,
+ subValues: Dynamic,
+ isInteger : Bool,
+ doc : String,
+ }>,
+ }>,
+ }>,
+}
+
+class ConstDbBuilder {
+
+ /**
+ Generate a class based on fields extracted from provided source files (JSON or CastleDB). Then return an instance of this class to be stored in some static var. Typically:
+ ```haxe
+ public static var db = ConstDbBuilder.buildVar(["data.cdb", "const.json"]);
+ ```
+ **/
+ public static macro function buildVar(dbFileNames:Array) {
+ var pos = Context.currentPos();
+ var rawMod = Context.getLocalModule();
+ var modPack = rawMod.split(".");
+ var modName = modPack.pop();
+
+ // Create class type
+ var classTypeDef : TypeDefinition = {
+ pos : pos,
+ name : cleanupIdentifier('Db_${dbFileNames.join("_")}'),
+ pack : modPack,
+ meta: [{ name:":keep", pos:pos }],
+ doc: "Project specific Level class",
+ kind : TDClass(),
+ fields : (macro class {
+ public function new() {}
+
+ /** This callback will trigger when one of the files is reloaded. **/
+ public dynamic function onReload() {}
+ }).fields,
+ }
+
+ // Parse given files and create class fields
+ for(f in dbFileNames) {
+ var fileFields = switch dn.FilePath.extractExtension(f) {
+ case "cdb": readCdb(f);
+ case "json": readJson(f);
+ case _: Context.fatalError("Unsupported database file "+f, pos);
+ }
+ classTypeDef.fields = fileFields.concat(classTypeDef.fields);
+ }
+
+ // Register stuff
+ Context.defineModule(rawMod, [classTypeDef]);
+ for(f in dbFileNames)
+ Context.registerModuleDependency(rawMod, resolveFilePath(f));
+
+ // Return class constructor
+ var classTypePath : TypePath = { pack:classTypeDef.pack, name:classTypeDef.name }
+ return macro new $classTypePath();
+ }
+
+
+ #if( macro || display )
+
+ /**
+ Parse a JSON and create class fields using its root values
+ **/
+ static function readJson(fileName:String) : Array {
+ var uid = cleanupIdentifier(fileName);
+ var pos = Context.currentPos();
+
+ // Read file
+ var path = resolveFilePath(fileName);
+ if( path==null ) {
+ Context.fatalError("File not found: "+fileName, pos);
+ return [];
+ }
+
+ var fileName = dn.FilePath.extractFileWithExt(path);
+ Context.registerModuleDependency(Context.getLocalModule(), path);
+
+
+ // Parse JSON
+ var raw = sys.io.File.getContent(path);
+ var jsonPos = Context.makePosition({ file:path, min:1, max:1 });
+ var json = try haxe.Json.parse(raw) catch(_) null;
+ if( json==null ) {
+ Context.fatalError("Couldn't parse JSON: "+path, jsonPos);
+ return [];
+ }
+
+ // List all supported fields in JSON
+ var fields : Array = [];
+ var initializers : Array = [];
+ for(k in Reflect.fields(json)) {
+ var val = Reflect.field(json, k);
+ var kind : FieldType = null;
+
+ // Build field type
+ switch Type.typeof(val) {
+ case TNull:
+ kind = FVar(null);
+
+ case TInt:
+ kind = FVar(macro:Int, macro $v{val});
+
+ case TFloat:
+ kind = FVar(macro:Float, macro $v{val});
+
+ case TBool:
+ kind = FVar(macro:Bool, macro $v{val});
+
+ case TClass(String):
+ kind = FVar(macro:String, macro $v{val});
+
+ case _:
+ Context.warning('Unsupported JSON type "${Type.typeof(val)}" for $k', jsonPos);
+ }
+
+ // Add field and default value
+ if( kind!=null ) {
+ fields.push({
+ name: k,
+ pos: pos,
+ kind: kind,
+ doc: '$k\n\n*From $fileName* ',
+ access: [APublic],
+ });
+ initializers.push( macro trace("hello "+$v{k}) );
+ }
+ }
+
+ // Update class fields using given JSON string (used for hot-reloading support)
+ fields.push({
+ name: "reload_"+uid,
+ doc: "Update class values using given JSON (useful if you want to support hot-reloading of the JSON db file)",
+ pos: pos,
+ access: [APublic],
+ kind: FFun({
+ args: [{ name:"updatedJsonStr", type:macro:String }],
+ expr: macro {
+ var json = try haxe.Json.parse(updatedJsonStr) catch(_) null;
+ if( json==null )
+ return;
+
+ for(k in Reflect.fields(json))
+ if( Reflect.hasField(this, k) ) {
+ try Reflect.setField( this, k, Reflect.field(json,k) )
+ catch(_) trace("ERROR: couldn't update JSON const "+k);
+ }
+
+ onReload();
+ },
+ }),
+ });
+
+ return fields;
+ }
+
+
+
+ /**
+ Parse CastleDB and create class fields using its "ConstDb" sheet
+ **/
+ static function readCdb(fileName:String) : Array {
+ var uid = cleanupIdentifier(fileName);
+ var pos = Context.currentPos();
+
+ // Read file
+ var path = resolveFilePath(fileName);
+ if( path==null ) {
+ Context.fatalError("File not found: "+fileName, pos);
+ return [];
+ }
+ Context.registerModuleDependency(Context.getLocalModule(), path);
+
+ // Parse JSON
+ var raw = sys.io.File.getContent(path);
+ var json : CastleDbJson = try haxe.Json.parse(raw) catch(_) null;
+ if( json==null ) {
+ Context.fatalError("CastleDB JSON parsing failed!", pos);
+ return [];
+ }
+
+ // List sub-values types
+ var subValueTypes : Map = new Map();
+ for(sheet in json.sheets) {
+ if( sheet.name.indexOf("ConstDb")<0 || sheet.name.indexOf("@subValues")<0 )
+ continue;
+ inline function _unsupported(typeName:String, valueName:String) {
+ Context.fatalError("Unsupported CastleDB type "+typeName+" for sub-value "+valueName, pos);
+ return null;
+ }
+ for(col in sheet.columns) {
+ var ct : ComplexType = switch col.typeStr {
+ case "1": macro:String;
+ case "2": macro:Bool;
+ case "3": macro:Int;
+ case "4": macro:Float;
+ case "11": macro:Int;
+ case _: _unsupported(col.typeStr, col.name);
+ }
+ if( ct!=null )
+ subValueTypes.set(col.name, { ct:ct, typeStr:col.typeStr });
+ }
+ }
+
+ // List constants
+ var fields : Array = [];
+ var valid = false;
+ for(sheet in json.sheets)
+ if( sheet.name=="ConstDb" ) {
+ if( sheet.columns.filter(c->c.name=="constId").length==0 )
+ continue;
+
+ if( sheet.columns.filter(c->c.name=="values").length==0 )
+ continue;
+
+ valid = true;
+ for(l in sheet.lines) {
+ var id = Reflect.field(l, "constId");
+ var doc = Reflect.field(l,"doc");
+
+ // List sub values
+ var valuesFields : Array = [];
+ var valuesIniters : Array = [];
+ for( v in l.values ) {
+ var doc = (v.doc==null ? v.valueName : v.doc ) + '\n\n*From $fileName* ';
+ var vid = cleanupIdentifier(v.valueName);
+
+ if( v.subValues!=null && Reflect.fields(v.subValues).length>0 ) {
+ // Value is an object with sub fields
+ var fields : Array = [];
+ var initers : Array = [];
+
+ // Read sub values
+ for(k in Reflect.fields(v.subValues)) {
+ if( k=="_value" )
+ Context.fatalError('[$fileName] "${l.constId}.${v.valueName}" value name "_value" is not allowed.', pos);
+
+ var ct = subValueTypes.exists(k) ? subValueTypes.get(k).ct : (macro:Float);
+ fields.push({
+ name: k,
+ kind: FVar(ct),
+ pos: pos,
+ doc: doc,
+ });
+
+ var rawVal = Reflect.field(v.subValues, k);
+ var const : Constant = !subValueTypes.exists(k)
+ ? CFloat( Std.string(rawVal) )
+ : switch subValueTypes.get(k).typeStr {
+ case "1": CString(rawVal);
+ case "2": CIdent( Std.string(rawVal) );
+ case "3": CInt( Std.string(rawVal) );
+ case "4": CFloat( Std.string(rawVal) );
+ case "11": CInt( Std.string(rawVal) );
+ case _:
+ Context.fatalError("Unexpected CastleDB typeStr "+subValueTypes.get(k).typeStr+" for sub-value init expr", pos);
+ }
+ initers.push({
+ field: k,
+ expr: { expr:EConst(const), pos:pos },
+ });
+ }
+
+ // Also include column value if it's not zero
+ if( v.value!=0 ) {
+ fields.push({
+ name: "_value",
+ pos: pos,
+ doc: doc,
+ kind: FVar( v.isInteger ? macro:Int : macro:Float ),
+ });
+ if( v.isInteger && v.value != Std.int(v.value) )
+ Context.warning('[$fileName] "${l.constId}.${v.valueName}" is a Float instead of an Int', pos);
+ var cleanVal = Std.string( v.isInteger ? Std.int(v.value) : v.value );
+ initers.push({
+ field: "_value",
+ expr: {
+ pos: pos,
+ expr: EConst( v.isInteger ? CInt(cleanVal) : CFloat(cleanVal) ),
+ },
+ });
+ }
+
+ // Value definition
+ valuesFields.push({
+ name: vid,
+ pos: pos,
+ doc: (v.doc==null ? v.valueName : v.doc ) + '\n\n*From $fileName* ',
+ kind: FVar( TAnonymous(fields) ),
+ });
+ // Value init
+ valuesIniters.push({
+ field: vid,
+ expr: {
+ pos: pos,
+ expr: EObjectDecl(initers),
+ },
+ });
+ }
+ else {
+ // Simple value (int/float)
+ valuesFields.push({
+ name: vid,
+ pos: pos,
+ doc: doc,
+ kind: FVar( v.isInteger ? macro:Int : macro:Float ),
+ });
+
+ // Initial value setter
+ if( v.isInteger && v.value!=Std.int(v.value) )
+ Context.warning('[$fileName] "${l.constId}.${v.valueName}" is a Float instead of an Int', pos);
+ var cleanVal = Std.string( v.isInteger ? Std.int(v.value) : v.value );
+ valuesIniters.push({
+ field: vid,
+ expr: {
+ pos: pos,
+ expr: EConst( v.isInteger ? CInt(cleanVal) : CFloat(cleanVal) ),
+ },
+ });
+ }
+ }
+
+ fields.push({
+ name: id,
+ pos: pos,
+ access: [APublic],
+ doc: ( doc==null ? id : doc ) + '\n\n*From $fileName* ',
+ kind: FVar( TAnonymous(valuesFields), {
+ pos:pos,
+ expr: EObjectDecl(valuesIniters),
+ } ),
+ });
+ }
+ }
+
+ // Check CDB sheets
+ if( !valid ) {
+ Context.fatalError('$fileName CastleDB file should contain a valid "ConstDb" sheet.', pos);
+ return [];
+ }
+
+ // CDB hot reloader
+ var cdbJsonType = Context.getType("ConstDbBuilder.CastleDbJson").toComplexType();
+ fields.push({
+ pos:pos,
+ name: "reload_"+uid,
+ doc: "Update class values using the content of the CastleDB file (useful if you want to support hot-reloading of the CastleDB file)",
+ access: [ APublic ],
+ kind: FFun({
+ args: [{ name:"updatedCdbJson", type:macro:String}],
+ expr: macro {
+ var json : $cdbJsonType = try haxe.Json.parse(updatedCdbJson) catch(_) null;
+ if( json==null )
+ return;
+
+ for(s in json.sheets) {
+ if( s.name!="ConstDb" )
+ continue;
+
+ for(l in s.lines) {
+ var obj = Reflect.field(this, l.constId);
+ if( obj==null ) {
+ obj = {}
+ Reflect.setField(this, l.constId, obj);
+ }
+ for(v in l.values) {
+ var subValues = v.subValues==null ? [] : Reflect.fields(v.subValues);
+ if( subValues.length>0 ) {
+ // Reload sub values object
+ var subObj = Reflect.field(obj, v.valueName);
+ if( subObj==null ) {
+ subObj = {};
+ Reflect.setField(obj, v.valueName, subObj);
+ }
+ for(k in subValues)
+ Reflect.setField(subObj, k, Reflect.field(v.subValues, k));
+
+ // Also include (or remove) _value
+ if( v.value!=0 )
+ Reflect.setField(subObj, "_value", v.isInteger ? Std.int(v.value) : v.value );
+ else
+ Reflect.deleteField(subObj, "_value");
+ }
+ else {
+ // Reload int/float value
+ Reflect.setField(obj, v.valueName, v.isInteger ? Std.int(v.value) : v.value );
+ }
+ }
+ }
+ }
+ onReload();
+ },
+ }),
+ });
+
+ return fields;
+ }
+
+
+
+ /** Lookup a file in all known project paths **/
+ static function resolveFilePath(basePath:String) : Null {
+ // Look in class paths
+ var path = try Context.resolvePath(basePath) catch( e : Dynamic ) null;
+
+ // Look in resourcesPath define
+ if( path == null ) {
+ var r = Context.definedValue("resourcesPath");
+ if( r != null ) {
+ r = r.split("\\").join("/");
+ if( !StringTools.endsWith(r, "/") ) r += "/";
+ try path = Context.resolvePath(r + basePath) catch( e : Dynamic ) null;
+ }
+ }
+
+ // Look in default Heaps resource dir
+ if( path == null )
+ try path = Context.resolvePath("res/" + basePath) catch( e : Dynamic ) null;
+
+ return path;
+ }
+
+
+ /** Remove invalid characters from a given string **/
+ static inline function cleanupIdentifier(str:String) {
+ if( str==null )
+ return "";
+ else
+ return ~/[^a-z0-9_]/gi.replace(str, "_");
+ }
+
+
+
+ #end
+
+}
\ No newline at end of file
diff --git a/src/game/assets/Lang.hx b/src/game/assets/Lang.hx
new file mode 100644
index 0000000..782de68
--- /dev/null
+++ b/src/game/assets/Lang.hx
@@ -0,0 +1,47 @@
+package assets;
+
+import dn.data.GetText;
+
+class Lang {
+ static var _initDone = false;
+ public static var CUR = "??";
+ public static var t : GetText;
+
+ public static function init(?lid:String) {
+ if( _initDone )
+ return;
+
+ _initDone = true;
+ CUR = lid==null ? getSystemLang() : lid;
+ var res =
+ try hxd.Res.load("lang/"+CUR+".po")
+ catch(_) {
+ CUR = "en";
+ hxd.Res.load("lang/"+CUR+".po");
+ }
+
+ t = new GetText();
+ t.readPo( res.entry.getBytes() );
+ }
+
+ public static function untranslated(str:Dynamic) : LocaleString {
+ init();
+ return t.untranslated(str);
+ }
+
+
+ /**
+ Return a simple language code, depending on current System setting (eg. "en", "fr", "de" etc.). If something goes wrong, this returns "en".
+ See: https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes
+ **/
+ public static function getSystemLang() : String {
+ try {
+ var code = hxd.System.getLocale();
+ if( code.indexOf("-")>=0 )
+ code = code.substr(0,code.indexOf("-") );
+ return code.toLowerCase();
+ }
+ catch(_)
+ return "en";
+ }
+}
\ No newline at end of file
diff --git a/src/game/assets/World.hx b/src/game/assets/World.hx
new file mode 100644
index 0000000..4437dbb
--- /dev/null
+++ b/src/game/assets/World.hx
@@ -0,0 +1,4 @@
+package assets;
+
+private typedef _Tmp =
+ haxe.macro.MacroType<[ ldtk.Project.build("res/levels/sampleWorld.ldtk") ]>;
\ No newline at end of file
diff --git a/src/game/en/DebugDrone.hx b/src/game/en/DebugDrone.hx
new file mode 100644
index 0000000..8839639
--- /dev/null
+++ b/src/game/en/DebugDrone.hx
@@ -0,0 +1,148 @@
+package en;
+
+/**
+ This Entity is intended for quick debugging / level exploration.
+ Create one by pressing CTRL-SHIFT-D in game, fly around using ARROWS.
+**/
+@:access(Camera)
+class DebugDrone extends Entity {
+ public static var ME : DebugDrone;
+ static var DEFAULT_COLOR : Col = 0x00ff00;
+
+ var ca : ControllerAccess;
+ var prevCamTarget : Null;
+ var prevCamZoom : Float;
+
+ var g : h2d.Graphics;
+ var help : h2d.Text;
+
+ var droneDx = 0.;
+ var droneDy = 0.;
+ var droneFrict = 0.86;
+
+ public function new() {
+ if( ME!=null ) {
+ ME.destroy();
+ Game.ME.garbageCollectEntities();
+ }
+
+ super(0,0);
+
+ ME = this;
+ setPosPixel(camera.rawFocus.levelX, camera.rawFocus.levelY);
+
+ // Controller
+ ca = App.ME.controller.createAccess();
+ ca.takeExclusivity();
+
+ // Take control of camera
+ if( camera.target!=null && camera.target.isAlive() )
+ prevCamTarget = camera.target;
+ prevCamZoom = camera.zoom;
+ camera.trackEntity(this,false);
+
+ // Placeholder render
+ g = new h2d.Graphics(spr);
+ g.beginFill(0xffffff);
+ g.drawCircle(0,0,6, 16);
+ setPivots(0.5);
+ setColor(DEFAULT_COLOR);
+
+ help = new h2d.Text(Assets.fontPixel);
+ game.root.add(help, Const.DP_TOP);
+ help.filter = new dn.heaps.filter.PixelOutline();
+ help.textColor = DEFAULT_COLOR;
+ help.text = [
+ "CANCEL -- Escape",
+ "MOVE -- ARROWS/pad",
+ "ZOOM IN -- "+ca.input.getAllBindindTextsFor(DebugDroneZoomIn).join(", "),
+ "ZOOM OUT -- "+ca.input.getAllBindindTextsFor(DebugDroneZoomOut).join(", "),
+ ].join("\n");
+ help.setScale(Const.UI_SCALE);
+ help.x = 4*Const.UI_SCALE;
+
+ // <----- HERE: add your own specific inits, like setting drone gravity to zero, updating collision behaviors etc.
+ }
+
+ inline function setColor(c:Col) {
+ g.color.setColor( c.withAlpha(1) );
+ }
+
+ override function dispose() {
+ // Try to restore camera state
+ if( prevCamTarget!=null )
+ camera.trackEntity(prevCamTarget, false);
+ else
+ camera.target = null;
+ prevCamTarget = null;
+ camera.forceZoom( prevCamZoom );
+
+ super.dispose();
+
+ // Clean up
+ help.remove();
+ ca.dispose();
+ if( ME==this )
+ ME = null;
+ }
+
+
+ override function frameUpdate() {
+ super.frameUpdate();
+
+ // Ignore game standard velocities
+ cancelVelocities();
+
+ // Movement controls
+ var spd = 0.02 * ( ca.isPadDown(X) ? 3 : 1 ); // turbo by holding pad-X
+
+ if( !App.ME.anyInputHasFocus() ) {
+ // Fly around
+ var dist = ca.getAnalogDist4(MoveLeft,MoveRight, MoveUp,MoveDown);
+ if( dist > 0 ) {
+ var a = ca.getAnalogAngle4(MoveLeft,MoveRight, MoveUp,MoveDown);
+ droneDx+=Math.cos(a) * dist*spd * tmod;
+ droneDy+=Math.sin(a) * dist*spd * tmod;
+ }
+
+ // Zoom controls
+ if( ca.isDown(DebugDroneZoomOut) )
+ camera.forceZoom( camera.baseZoom-0.04*camera.baseZoom );
+
+ if( ca.isDown(DebugDroneZoomIn) )
+ camera.forceZoom( camera.baseZoom+0.02*camera.baseZoom );
+
+ // Destroy
+ if( ca.isKeyboardPressed(K.ESCAPE) || ca.isPressed(ToggleDebugDrone) ) {
+ destroy();
+ return;
+ }
+ }
+
+
+ // X physics
+ xr += droneDx*tmod;
+ while( xr>1 ) { xr--; cx++; }
+ while( xr<0 ) { xr++; cx--; }
+ droneDx*=Math.pow(droneFrict, tmod);
+
+ // Y physics
+ yr += droneDy*tmod;
+ while( yr>1 ) { yr--; cy++; }
+ while( yr<0 ) { yr++; cy--; }
+ droneDy*=Math.pow(droneFrict, tmod);
+
+ // Update previous cam target if it changes
+ if( camera.target!=null && camera.target!=this && camera.target.isAlive() )
+ prevCamTarget = camera.target;
+
+ // Display FPS
+ debug( M.round(hxd.Timer.fps()) + " FPS" );
+
+ // Collisions
+ if( level.hasCollision(cx,cy) )
+ setColor(0xff0000);
+ else
+ setColor(DEFAULT_COLOR);
+ }
+}
\ No newline at end of file
diff --git a/src/game/import.hx b/src/game/import.hx
new file mode 100644
index 0000000..3d206e3
--- /dev/null
+++ b/src/game/import.hx
@@ -0,0 +1,40 @@
+#if !macro
+
+// Libs
+import dn.M;
+import dn.Lib;
+import dn.Col;
+import dn.Tweenie;
+import dn.data.GetText;
+import dn.struct.*;
+import dn.heaps.input.*;
+import dn.heaps.slib.*;
+import dn.phys.Velocity;
+
+// Project classes
+import Types;
+import ui.Console;
+import ui.Bar;
+import ui.Window;
+import tools.*;
+import assets.*;
+import en.*;
+
+// Castle DB
+import assets.CastleDb;
+
+// LDtk
+import assets.World;
+
+// Aliases
+import dn.RandomTools as R;
+import assets.Assets as A;
+import assets.AssetsDictionaries as D;
+import hxd.Key as K;
+import tools.LPoint as P;
+import assets.Lang.t as L;
+import Const.db as DB;
+
+import dn.debug.MemTrack.measure as MM;
+
+#end
\ No newline at end of file
diff --git a/src/game/sample/SampleGame.hx b/src/game/sample/SampleGame.hx
new file mode 100644
index 0000000..5820d67
--- /dev/null
+++ b/src/game/sample/SampleGame.hx
@@ -0,0 +1,16 @@
+package sample;
+
+/**
+ This small class just creates a SamplePlayer instance in current level
+**/
+class SampleGame extends Game {
+ public function new() {
+ super();
+ }
+
+ override function startLevel(l:World_Level) {
+ super.startLevel(l);
+ new SamplePlayer();
+ }
+}
+
diff --git a/src/game/sample/SamplePlayer.hx b/src/game/sample/SamplePlayer.hx
new file mode 100644
index 0000000..98f8717
--- /dev/null
+++ b/src/game/sample/SamplePlayer.hx
@@ -0,0 +1,125 @@
+package sample;
+
+/**
+ SamplePlayer is an Entity with some extra functionalities:
+ - user controlled (using gamepad or keyboard)
+ - falls with gravity
+ - has basic level collisions
+ - some squash animations, because it's cheap and they do the job
+**/
+
+class SamplePlayer extends Entity {
+ var ca : ControllerAccess;
+ var walkSpeed = 0.;
+
+ // This is TRUE if the player is not falling
+ var onGround(get,never) : Bool;
+ inline function get_onGround() return !destroyed && vBase.dy==0 && yr==1 && level.hasCollision(cx,cy+1);
+
+
+ public function new() {
+ super(5,5);
+
+ // Start point using level entity "PlayerStart"
+ var start = level.data.l_Entities.all_PlayerStart[0];
+ if( start!=null )
+ setPosCase(start.cx, start.cy);
+
+ // Misc inits
+ vBase.setFricts(0.84, 0.94);
+
+ // Camera tracks this
+ camera.trackEntity(this, true);
+ camera.clampToLevelBounds = true;
+
+ // Init controller
+ ca = App.ME.controller.createAccess();
+ ca.lockCondition = Game.isGameControllerLocked;
+
+ // Placeholder display
+ var b = new h2d.Bitmap( h2d.Tile.fromColor(Green, iwid, ihei), spr );
+ b.tile.setCenterRatio(0.5,1);
+ }
+
+
+ override function dispose() {
+ super.dispose();
+ ca.dispose(); // don't forget to dispose controller accesses
+ }
+
+
+ /** X collisions **/
+ override function onPreStepX() {
+ super.onPreStepX();
+
+ // Right collision
+ if( xr>0.8 && level.hasCollision(cx+1,cy) )
+ xr = 0.8;
+
+ // Left collision
+ if( xr<0.2 && level.hasCollision(cx-1,cy) )
+ xr = 0.2;
+ }
+
+
+ /** Y collisions **/
+ override function onPreStepY() {
+ super.onPreStepY();
+
+ // Land on ground
+ if( yr>1 && level.hasCollision(cx,cy+1) ) {
+ setSquashY(0.5);
+ vBase.dy = 0;
+ vBump.dy = 0;
+ yr = 1;
+ ca.rumble(0.2, 0.06);
+ onPosManuallyChangedY();
+ }
+
+ // Ceiling collision
+ if( yr<0.2 && level.hasCollision(cx,cy-1) )
+ yr = 0.2;
+ }
+
+
+ /**
+ Control inputs are checked at the beginning of the frame.
+ VERY IMPORTANT NOTE: because game physics only occur during the `fixedUpdate` (at a constant 30 FPS), no physics increment should ever happen here! What this means is that you can SET a physics value (eg. see the Jump below), but not make any calculation that happens over multiple frames (eg. increment X speed when walking).
+ **/
+ override function preUpdate() {
+ super.preUpdate();
+
+ walkSpeed = 0;
+ if( onGround )
+ cd.setS("recentlyOnGround",0.1); // allows "just-in-time" jumps
+
+
+ // Jump
+ if( cd.has("recentlyOnGround") && ca.isPressed(Jump) ) {
+ vBase.dy = -0.85;
+ setSquashX(0.6);
+ cd.unset("recentlyOnGround");
+ fx.dotsExplosionExample(centerX, centerY, 0xffcc00);
+ ca.rumble(0.05, 0.06);
+ }
+
+ // Walk
+ if( !isChargingAction() && ca.getAnalogDist2(MoveLeft,MoveRight)>0 ) {
+ // As mentioned above, we don't touch physics values (eg. `dx`) here. We just store some "requested walk speed", which will be applied to actual physics in fixedUpdate.
+ walkSpeed = ca.getAnalogValue2(MoveLeft,MoveRight); // -1 to 1
+ }
+ }
+
+
+ override function fixedUpdate() {
+ super.fixedUpdate();
+
+ // Gravity
+ if( !onGround )
+ vBase.dy+=0.05;
+
+ // Apply requested walk movement
+ if( walkSpeed!=0 )
+ vBase.dx += walkSpeed * 0.045; // some arbitrary speed
+ }
+}
\ No newline at end of file
diff --git a/src/game/tools/AppChildProcess.hx b/src/game/tools/AppChildProcess.hx
new file mode 100644
index 0000000..058fd55
--- /dev/null
+++ b/src/game/tools/AppChildProcess.hx
@@ -0,0 +1,17 @@
+package tools;
+
+class AppChildProcess extends dn.Process {
+ public static var ALL : FixedArray = new FixedArray(256);
+
+ public var app(get,never) : App; inline function get_app() return App.ME;
+
+ public function new() {
+ super(App.ME);
+ ALL.push(this);
+ }
+
+ override function onDispose() {
+ super.onDispose();
+ ALL.remove(this);
+ }
+}
\ No newline at end of file
diff --git a/src/game/tools/ChargedAction.hx b/src/game/tools/ChargedAction.hx
new file mode 100644
index 0000000..581d98a
--- /dev/null
+++ b/src/game/tools/ChargedAction.hx
@@ -0,0 +1,64 @@
+package tools;
+
+/** An utility class to manage Entity charged actions, with a very low memory footprint (garbage collector friendly) **/
+class ChargedAction implements dn.struct.RecyclablePool.Recyclable {
+ public var id : ChargedActionId;
+ public var durationS = 0.;
+ public var elapsedS(default,null) = 0.;
+ public var remainS(get,never) : Float; inline function get_remainS() return M.fclamp(durationS-elapsedS, 0, durationS);
+
+ public var onComplete : ChargedAction->Void;
+ public var onProgress : ChargedAction->Void;
+
+ /** From 0 (start) to 1 (end) **/
+ public var elapsedRatio(get,never) : Float;
+ inline function get_elapsedRatio() return durationS<=0 ? 1 : M.fclamp(elapsedS/durationS, 0, 1);
+
+ /** From 1 (start) to 0 (end) **/
+ public var remainingRatio(get,never) : Float;
+ inline function get_remainingRatio() return durationS<=0 ? 0 : M.fclamp(1-elapsedS/durationS, 0, 1);
+
+
+ public inline function new() {
+ recycle();
+ }
+
+ public inline function recycle() {
+ id = CA_Unknown;
+ durationS = 0;
+ elapsedS = 0;
+ onComplete = _doNothing;
+ onProgress = _doNothing;
+ }
+
+ public inline function resetProgress() {
+ elapsedS = 0;
+ onProgress(this);
+ }
+
+ public inline function reduceProgressS(lossS:Float) {
+ elapsedS = M.fmax(0, elapsedS-lossS);
+ onProgress(this);
+ }
+
+ public inline function isComplete() {
+ return elapsedS>=durationS;
+ }
+
+ function _doNothing(a:ChargedAction) {}
+
+
+ /** Update progress and return TRUE if completed **/
+ public inline function update(tmod:Float) {
+ elapsedS = M.fmin( elapsedS + tmod/Const.FPS, durationS );
+ onProgress(this);
+ if( isComplete() ) {
+ onComplete(this);
+ if( isComplete() )
+ recycle(); // breaks possibles mem refs, for GC
+ return true;
+ }
+ else
+ return false;
+ }
+}
diff --git a/src/game/tools/GameChildProcess.hx b/src/game/tools/GameChildProcess.hx
new file mode 100644
index 0000000..451b0a2
--- /dev/null
+++ b/src/game/tools/GameChildProcess.hx
@@ -0,0 +1,12 @@
+package tools;
+
+class GameChildProcess extends dn.Process {
+ public var app(get,never) : App; inline function get_app() return App.ME;
+ public var game(get,never) : Game; inline function get_game() return Game.ME;
+ public var fx(get,never) : Fx; inline function get_fx() return Game.exists() ? Game.ME.fx : null;
+ public var level(get,never) : Level; inline function get_level() return Game.exists() ? Game.ME.level : null;
+
+ public function new() {
+ super(Game.ME);
+ }
+}
\ No newline at end of file
diff --git a/src/game/tools/LPoint.hx b/src/game/tools/LPoint.hx
new file mode 100644
index 0000000..3d124a5
--- /dev/null
+++ b/src/game/tools/LPoint.hx
@@ -0,0 +1,171 @@
+package tools;
+
+class LPoint {
+ /** Grid based X **/
+ public var cx : Int;
+
+ /** Grid based Y **/
+ public var cy : Int;
+
+ /** X-ratio (0-1) in current grid cell **/
+ public var xr : Float;
+
+ /** Y-ratio (0-1) in current grid cell **/
+ public var yr : Float;
+
+
+
+ /** Grid based X, including sub grid cell ratio **/
+ public var cxf(get,never) : Float;
+ inline function get_cxf() return cx+xr;
+
+ /** Grid based Y, including sub grid cell ratio **/
+ public var cyf(get,never) : Float;
+ inline function get_cyf() return cy+yr;
+
+
+
+ /** Level X pixel coord **/
+ public var levelX(get,set) : Float;
+ inline function get_levelX() return (cx+xr)*Const.GRID;
+ inline function set_levelX(v:Float) {
+ setLevelPixelX(v);
+ return levelX;
+ }
+
+ /** Level Y pixel coord **/
+ public var levelY(get,set) : Float;
+ inline function get_levelY() return (cy+yr)*Const.GRID;
+ inline function set_levelY(v:Float) {
+ setLevelPixelY(v);
+ return levelY;
+ }
+
+
+
+ /** Level X pixel coord (as Integer) **/
+ public var levelXi(get,never) : Int;
+ inline function get_levelXi() return Std.int(levelX);
+
+ /** Level Y pixel coord **/
+ public var levelYi(get,never) : Int;
+ inline function get_levelYi() return Std.int(levelY);
+
+
+
+ /** Screen X pixel coord **/
+ public var screenX(get,never) : Float;
+ inline function get_screenX() {
+ return !Game.exists() ? -1. : levelX*Const.SCALE + Game.ME.scroller.x;
+ }
+
+ /** Screen Y pixel coord **/
+ public var screenY(get,never) : Float;
+ inline function get_screenY() {
+ return !Game.exists() ? -1. : levelY*Const.SCALE + Game.ME.scroller.y;
+ }
+
+
+
+ private inline function new() {
+ cx = cy = 0;
+ xr = yr = 0;
+ }
+
+ @:keep
+ public function toString() : String {
+ return 'LPoint<${M.pretty(cxf)},${M.pretty(cyf)} / $levelXi,$levelYi>';
+ }
+
+ public static inline function fromCase(cx:Float, cy:Float) {
+ return new LPoint().setLevelCase( Std.int(cx), Std.int(cy), cx%1, cy%1 );
+ }
+
+ public static inline function fromCaseCenter(cx:Int, cy:Int) {
+ return new LPoint().setLevelCase(cx, cy, 0.5, 0.5);
+ }
+
+ public static inline function fromPixels(x:Float, y:Float) {
+ return new LPoint().setLevelPixel(x,y);
+ }
+
+ public static inline function fromScreen(sx:Float, sy:Float) {
+ return new LPoint().setScreen(sx,sy);
+ }
+
+ /** Init using level grid coords **/
+ public inline function setLevelCase(x,y,?xr=0.5,?yr=0.5) {
+ this.cx = x;
+ this.cy = y;
+ this.xr = xr;
+ this.yr = yr;
+ return this;
+ }
+
+
+ /** Set this point using another LPoint **/
+ public inline function usePoint(other:LPoint) {
+ cx = other.cx;
+ cy = other.cy;
+ xr = other.xr;
+ yr = other.yr;
+ }
+
+ /** Init from screen coord **/
+ public inline function setScreen(sx:Float, sy:Float) {
+ setLevelPixel(
+ ( sx - Game.ME.scroller.x ) / Const.SCALE,
+ ( sy - Game.ME.scroller.y ) / Const.SCALE
+ );
+ return this;
+ }
+
+ /** Init using level pixels coords **/
+ public inline function setLevelPixel(x:Float,y:Float) {
+ setLevelPixelX(x);
+ setLevelPixelY(y);
+ return this;
+ }
+
+ inline function setLevelPixelX(x:Float) {
+ cx = Std.int(x/Const.GRID);
+ this.xr = ( x % Const.GRID ) / Const.GRID;
+ return this;
+ }
+
+ inline function setLevelPixelY(y:Float) {
+ cy = Std.int(y/Const.GRID);
+ this.yr = ( y % Const.GRID ) / Const.GRID;
+ return this;
+ }
+
+ /** Return distance to something else, in grid unit **/
+ public inline function distCase(?e:Entity, ?pt:LPoint, tcx=0, tcy=0, txr=0.5, tyr=0.5) {
+ if( e!=null )
+ return M.dist(this.cx+this.xr, this.cy+this.yr, e.cx+e.xr, e.cy+e.yr);
+ else if( pt!=null )
+ return M.dist(this.cx+this.xr, this.cy+this.yr, pt.cx+pt.xr, pt.cy+pt.yr);
+ else
+ return M.dist(this.cx+this.xr, this.cy+this.yr, tcx+txr, tcy+tyr);
+ }
+
+ /** Distance to something else, in level pixels **/
+ public inline function distPx(?e:Entity, ?pt:LPoint, lvlX=0., lvlY=0.) {
+ if( e!=null )
+ return M.dist(levelX, levelY, e.attachX, e.attachY);
+ else if( pt!=null )
+ return M.dist(levelX, levelY, pt.levelX, pt.levelY);
+ else
+ return M.dist(levelX, levelY, lvlX, lvlY);
+ }
+
+ /** Angle in radians to something else, in level pixels **/
+ public inline function angTo(?e:Entity, ?pt:LPoint, lvlX=0., lvlY=0.) {
+ if( e!=null )
+ return Math.atan2((e.cy+e.yr)-cyf, (e.cx+e.xr)-cxf );
+ else if( pt!=null )
+ return Math.atan2(pt.cyf-cyf, pt.cxf-cxf);
+ else
+ return Math.atan2(lvlY-levelY, lvlX-levelX);
+ }
+}
diff --git a/src/game/tools/LRect.hx b/src/game/tools/LRect.hx
new file mode 100644
index 0000000..fffd168
--- /dev/null
+++ b/src/game/tools/LRect.hx
@@ -0,0 +1,119 @@
+package tools;
+
+class LRect {
+ var topLeft : LPoint;
+ var bottomRight : LPoint;
+
+ /** Pixel based left coordinate **/
+ public var pxLeft(get,set) : Int;
+ /** Pixel based top coordinate **/
+ public var pxTop(get,set) : Int;
+ /** Pixel based right coordinate **/
+ public var pxRight(get,set) : Int;
+ /** Pixel based bottom coordinate **/
+ public var pxBottom(get,set) : Int;
+ /** Pixel based width **/
+ public var pxWid(get,set) : Int;
+ /** Pixel based height **/
+ public var pxHei(get,set) : Int;
+
+ /** Grid based left coordinate **/
+ public var cLeft(get,set) : Int;
+ /** Grid based top coordinate **/
+ public var cTop(get,set) : Int;
+ /** Grid based right coordinate **/
+ public var cRight(get,set) : Int;
+ /** Grid based bottom coordinate **/
+ public var cBottom(get,set) : Int;
+ /** Grid based width **/
+ public var cWid(get,set) : Int;
+ /** Grid based height **/
+ public var cHei(get,set) : Int;
+
+ private inline function new() {
+ topLeft = LPoint.fromPixels(0,0);
+ bottomRight = LPoint.fromPixels(0,0);
+ }
+
+ @:keep
+ public function toString() : String {
+ return 'LRect';
+ }
+
+ /**
+ Create a LRect using pixel coordinates and dimensions.
+ **/
+ public static inline function fromPixels(x:Int, y:Int, w:Int, h:Int) {
+ var r = new LRect();
+ r.topLeft.setLevelPixel(x,y);
+ r.bottomRight.setLevelPixel(x+M.iabs(w)-1, y+M.iabs(h)-1);
+ return r;
+ }
+
+
+ /**
+ Create a LRect using grid-based coordinates and dimensions.
+ **/
+ public static inline function fromCase(cx:Int, cy:Int, w:Int, h:Int) {
+ var r = new LRect();
+ r.topLeft.setLevelCase(cx,cy, 0,0);
+ r.bottomRight.setLevelCase(cx+M.iabs(w)-1, cy+M.iabs(h)-1, 0.999, 0.999);
+ return r;
+ }
+
+
+ /** Swap coordinates if needed **/
+ inline function normalize() {
+ if( topLeft.levelX > bottomRight.levelX ) {
+ var swp = topLeft.levelX;
+ topLeft.levelX = bottomRight.levelX;
+ bottomRight.levelX = swp;
+ }
+
+ if( topLeft.levelY > bottomRight.levelY ) {
+ var swp = topLeft.levelY;
+ topLeft.levelY = bottomRight.levelY;
+ bottomRight.levelY = swp;
+ }
+ }
+
+
+
+ inline function get_pxLeft() return topLeft.levelXi;
+ inline function set_pxLeft(v:Int) { topLeft.levelX = v; normalize(); return v; }
+
+ inline function get_pxTop() return topLeft.levelYi;
+ inline function set_pxTop(v:Int) { topLeft.levelY = v; normalize(); return v; }
+
+ inline function get_pxBottom() return bottomRight.levelYi;
+ inline function set_pxBottom(v:Int) { bottomRight.levelY = v; normalize(); return v; }
+
+ inline function get_pxRight() return bottomRight.levelXi;
+ inline function set_pxRight(v:Int) { bottomRight.levelX = v; normalize(); return v; }
+
+ inline function get_pxWid() return bottomRight.levelXi - topLeft.levelXi + 1;
+ inline function set_pxWid(v) { bottomRight.levelX = topLeft.levelXi + v; normalize(); return v; }
+
+ inline function get_pxHei() return bottomRight.levelYi - topLeft.levelYi + 1;
+ inline function set_pxHei(v) { bottomRight.levelY = topLeft.levelYi + v; normalize(); return v; }
+
+
+
+ inline function get_cLeft() return topLeft.cx;
+ inline function set_cLeft(v:Int) { topLeft.cx = v; topLeft.xr = 0; normalize(); return v; }
+
+ inline function get_cTop() return topLeft.cy;
+ inline function set_cTop(v:Int) { topLeft.cy = v; topLeft.yr = 0; normalize(); return v; }
+
+ inline function get_cRight() return bottomRight.cx;
+ inline function set_cRight(v:Int) { bottomRight.cx = v; bottomRight.xr = 0.999; normalize(); return v; }
+
+ inline function get_cBottom() return bottomRight.cy;
+ inline function set_cBottom(v:Int) { bottomRight.cy = v; bottomRight.yr = 0.999; normalize(); return v; }
+
+ inline function get_cWid() return bottomRight.cx - topLeft.cx + 1;
+ inline function set_cWid(v:Int) { bottomRight.cx = topLeft.cx + v-1; bottomRight.xr = 0.999; normalize(); return v; }
+
+ inline function get_cHei() return bottomRight.cy - topLeft.cy + 1;
+ inline function set_cHei(v:Int) { bottomRight.cy = topLeft.cy + v-1; bottomRight.yr = 0.999; normalize(); return v; }
+}
diff --git a/src/game/tools/script/Api.hx b/src/game/tools/script/Api.hx
new file mode 100644
index 0000000..c3fecf0
--- /dev/null
+++ b/src/game/tools/script/Api.hx
@@ -0,0 +1,12 @@
+package tools.script;
+
+/**
+ Everything in this class will be available in HScript execution context.
+**/
+@:keep
+class Api {
+ public var levelWid(get,never) : Int; inline function get_levelWid() return Game.ME.level.pxWid;
+ public var levelHei(get,never) : Int; inline function get_levelHei() return Game.ME.level.pxHei;
+
+ public function new() {}
+}
\ No newline at end of file
diff --git a/src/game/tools/script/Script.hx b/src/game/tools/script/Script.hx
new file mode 100644
index 0000000..03151ea
--- /dev/null
+++ b/src/game/tools/script/Script.hx
@@ -0,0 +1,70 @@
+package tools.script;
+
+class Script {
+ public static var log : dn.Log;
+ public static var parser : hscript.Parser;
+
+ /**
+ Execute provided hscript.
+ USAGE:
+ Script.run('var a=1 ; a++ ; log(a) ; return a');
+ **/
+ public static function run(script:String) {
+ // Init script
+ init();
+ log.clear();
+ log.add("exec", "Script started.");
+
+ // API
+ var interp = new hscript.Interp();
+ interp.variables.set("api", new tools.script.Api());
+ interp.variables.set("log", (v:Dynamic)->log.add("run", Std.string(v)));
+
+ // Execute
+ var program = parser.parseString(script);
+ var out : Dynamic = try interp.execute(program)
+ catch( e:hscript.Expr.Error ) {
+ log.error( Std.string(e) );
+ null;
+ }
+
+ // Returned value
+ if( out!=null )
+ log.add("exec", "Returned: "+out);
+
+ if( log.containsAnyCriticalEntry() ) {
+ // Error
+ printLastLog();
+ return false;
+ }
+ else {
+ // Done!
+ log.add("exec", "Script completed.");
+ return true;
+ }
+ }
+
+
+ /**
+ Print last script log to default output
+ **/
+ public static function printLastLog() {
+ log.printAll();
+ }
+
+
+ static var initDone = false;
+ static function init() {
+ if( initDone )
+ return;
+ initDone = true;
+
+ parser = new hscript.Parser();
+
+ log = new dn.Log();
+ log.outputConsole = Console.ME;
+ log.tagColors.set("error", "#ff6c6c");
+ log.tagColors.set("exec", "#a1b2db");
+ log.tagColors.set("run", "#3affe5");
+ }
+}
\ No newline at end of file
diff --git a/src/game/ui/Bar.hx b/src/game/ui/Bar.hx
new file mode 100644
index 0000000..3d27dd7
--- /dev/null
+++ b/src/game/ui/Bar.hx
@@ -0,0 +1,154 @@
+package ui;
+
+class Bar extends h2d.Object {
+ var cd : dn.Cooldown;
+ var bg : h2d.ScaleGrid;
+ var bar : h2d.ScaleGrid;
+ var oldBar : Null;
+
+ public var innerBarMaxWidth(get,never) : Float;
+ public var innerBarHeight(get,never) : Float;
+ public var outerWidth(get,never) : Float;
+ public var outerHeight(get,never) : Float;
+ public var color(default,set) : Col;
+ public var defaultColor(default,null) : Col;
+ var padding : Int;
+ var oldBarSpeed : Float = 1.;
+
+ var blinkColor : h3d.Vector;
+ var gradTg : Null;
+
+ var curValue : Float;
+ var curMax : Float;
+
+ public function new(wid:Int, hei:Int, c:Col, ?p:h2d.Object) {
+ super(p);
+
+ curValue = 0;
+ curMax = 1;
+ cd = new dn.Cooldown(Const.FPS);
+
+ bg = new h2d.ScaleGrid( Assets.tiles.getTile(D.tiles.uiBarBg), 2, 2, this );
+ bg.colorAdd = blinkColor = new h3d.Vector();
+
+ bar = new h2d.ScaleGrid( Assets.tiles.getTile(D.tiles.uiBar), 1,1, this );
+
+ setSize(wid,hei,1);
+ defaultColor = color = c;
+ }
+
+ public function enableOldValue(oldBarColor:Col, speed=1.0) {
+ if( oldBar!=null )
+ oldBar.remove();
+ oldBar = new h2d.ScaleGrid( h2d.Tile.fromColor(oldBarColor,3,3), 1, 1 );
+ this.addChildAt( oldBar, this.getChildIndex(bar) );
+ oldBar.height = bar.height;
+ oldBar.width = 0;
+ oldBar.setPosition(padding,padding);
+
+ oldBarSpeed = speed;
+ }
+
+ public function setGraduationPx(step:Int, alpha=0.5) {
+ if( step<=1 )
+ throw "Invalid bar graduation "+step;
+
+ if( gradTg!=null )
+ gradTg.remove();
+
+ gradTg = new h2d.TileGroup(Assets.tiles.tile, this);
+ gradTg.colorAdd = blinkColor;
+ gradTg.setDefaultColor(0x0, alpha);
+
+ var x = step-1;
+ var t = Assets.tiles.getTile(D.tiles.pixel);
+ while( xbar.width) {
+ cd.setS("oldMaintain",0.06);
+ oldBar.width = oldWidth;
+ }
+ }
+
+ function renderBar() {
+ bar.visible = curValue>0;
+ bar.width = innerBarMaxWidth * (curValue/curMax);
+ }
+
+ public function skipOldValueBar() {
+ if( oldBar!=null )
+ oldBar.width = 0;
+ }
+
+ public function blink(?c:Col, a=1.0) {
+ blinkColor.setColor( (c==null ? color : c).withAlpha(a) );
+ cd.setS("blinkMaintain", 0.15 * 1/oldBarSpeed);
+ }
+
+ override function sync(ctx:h2d.RenderContext) {
+ var tmod = Game.ME.tmod;
+ cd.update(tmod);
+
+ // Decrease oldValue bar
+ if( oldBar!=null ) {
+ if( !cd.has("oldMaintain") )
+ oldBar.width = M.fmax(0, oldBar.width - oldBarSpeed*2*tmod);
+ oldBar.visible = oldBar.width>0;
+ }
+
+ // Blink fade
+ if( !cd.has("blinkMaintain") ) {
+ blinkColor.r*=Math.pow(0.60, tmod);
+ blinkColor.g*=Math.pow(0.55, tmod);
+ blinkColor.b*=Math.pow(0.50, tmod);
+ }
+
+ super.sync(ctx);
+ }
+}
\ No newline at end of file
diff --git a/src/game/ui/Console.hx b/src/game/ui/Console.hx
new file mode 100644
index 0000000..61ad23f
--- /dev/null
+++ b/src/game/ui/Console.hx
@@ -0,0 +1,256 @@
+package ui;
+
+enum abstract ConsoleFlag(Int) to Int from Int {
+ var F_Camera;
+ var F_CameraScrolling;
+ var F_Bounds;
+ var F_Affects;
+}
+
+class Console extends h2d.Console {
+ public static var ME : Console;
+ #if debug
+ var flags : Map;
+ var allFlags : Array<{ name:String, value:Int }> = [];
+ #end
+
+ var stats : Null;
+
+ public function new(f:h2d.Font, p:h2d.Object) {
+ super(f, p);
+
+ logTxt.filter = new dn.heaps.filter.PixelOutline();
+ scale(2); // TODO smarter scaling for 4k screens
+ logTxt.condenseWhite = false;
+ errorColor = 0xff6666;
+
+ // Settings
+ ME = this;
+ h2d.Console.HIDE_LOG_TIMEOUT = #if debug 60 #else 5 #end;
+ Lib.redirectTracesToH2dConsole(this);
+
+ #if debug
+ // Debug console flags
+ flags = new Map();
+ allFlags = dn.MacroTools.getAbstractEnumValues(ConsoleFlag);
+ allFlags.sort( (a,b)->Reflect.compare(a.name, b.name) );
+ this.addCommand("flags", "Open the console flags window", [], function() {
+ this.hide();
+ var w = new ui.win.SimpleMenu();
+ w.verticalAlign = End;
+ w.addButton("Disable all", false, ()->{
+ for(f in allFlags)
+ if( hasFlag(f.value) )
+ setFlag(f.value, false);
+ });
+ for(f in allFlags)
+ w.addCheckBox(f.name.substr(2), ()->hasFlag(f.value), v->setFlag(f.value,v));
+ });
+ this.addAlias("f","flags");
+ this.addAlias("flag","flags");
+
+ // List all console flags
+ this.addCommand("list", [], function() {
+ for(f in allFlags)
+ log( (hasFlag(f.value) ? "+" : "-")+f.name, hasFlag(f.value)?0x80ff00:0xff8888 );
+ });
+
+ // Controller debugger
+ this.addCommand("ctrl", [], ()->{
+ App.ME.ca.toggleDebugger(App.ME, dbg->{
+ dbg.root.filter = new dn.heaps.filter.PixelOutline();
+ });
+ });
+
+ // Garbage collector
+ this.addCommand("gc", [{ name:"state", t:AInt, opt:true }], (?state:Int)->{
+ if( !dn.Gc.isSupported() )
+ log("GC is not supported on this platform", Red);
+ else {
+ if( state!=null )
+ dn.Gc.setState(state!=0);
+ dn.Gc.runNow();
+ log("GC forced (current state: "+(dn.Gc.isActive() ? "active" : "inactive" )+")", dn.Gc.isActive()?Green:Yellow);
+ }
+ });
+
+ // Level marks
+ var allLevelMarks : Array<{ name:String, value:Int }>;
+ allLevelMarks = dn.MacroTools.getAbstractEnumValues(Types.LevelMark);
+ this.addCommand(
+ "mark",
+ [
+ { name:"levelMark", t:AEnum( allLevelMarks.map(m->m.name) ), opt:true },
+ { name:"bit", t:AInt, opt:true },
+ ],
+ (k:String, bit:Null)->{
+ if( !Game.exists() ) {
+ error('Game is not running');
+ return;
+ }
+ if( k==null ) {
+ // Game.ME.level.clearDebug();
+ return;
+ }
+
+ var bit : Null = cast bit;
+ var mark = -1;
+ for(m in allLevelMarks)
+ if( m.name==k ) {
+ mark = m.value;
+ break;
+ }
+ if( mark<0 ) {
+ error('Unknown level mark $k');
+ return;
+ }
+
+ var col = 0xffcc00;
+ log('Displaying $mark (bit=$bit)...', col);
+ // Game.ME.level.renderDebugMark(cast mark, bit);
+ }
+ );
+ this.addAlias("m","mark");
+ #end
+
+ // List all active dn.Process
+ this.addCommand("process", [], ()->{
+ for( l in App.ME.rprintChildren().split("\n") )
+ log(l);
+ });
+ this.addAlias("p", "process");
+
+ // Show build info
+ this.addCommand("build", [], ()->log( Const.BUILD_INFO ) );
+
+ // Create a debug drone
+ #if debug
+ this.addCommand("drone", [], ()->{
+ new en.DebugDrone();
+ });
+ #end
+
+ // Create a stats box
+ this.addCommand("fps", [], ()->toggleStats());
+ this.addAlias("stats","fps");
+
+ // All flag aliases
+ #if debug
+ for(f in allFlags)
+ addCommand(f.name.substr(2), [], ()->{
+ setFlag(f.value, !hasFlag(f.value));
+ });
+ #end
+ }
+
+ public function disableStats() {
+ if( stats!=null ) {
+ stats.destroy();
+ stats = null;
+ }
+ }
+
+ public function enableStats() {
+ disableStats();
+ stats = new dn.heaps.StatsBox(App.ME);
+ stats.addFpsChart();
+ stats.addDrawCallsChart();
+ #if hl
+ stats.addMemoryChart();
+ #end
+ }
+
+ public function toggleStats() {
+ if( stats!=null )
+ disableStats();
+ else
+ enableStats();
+ }
+
+ override function getCommandSuggestion(cmd:String):String {
+ var sugg = super.getCommandSuggestion(cmd);
+ if( sugg.length>0 )
+ return sugg;
+
+ if( cmd.length==0 )
+ return "";
+
+ // Simplistic argument auto-complete
+ for(c in commands.keys()) {
+ var reg = new EReg("([ \t\\/]*"+c+"[ \t]+)(.*)", "gi");
+ if( reg.match(cmd) ) {
+ var lowArg = reg.matched(2).toLowerCase();
+ for(a in commands.get(c).args)
+ switch a.t {
+ case AInt:
+ case AArray(_):
+ case AFloat:
+ case AString:
+ case ABool:
+ case AEnum(values):
+ for(v in values)
+ if( v.toLowerCase().indexOf(lowArg)==0 )
+ return reg.matched(1) + v;
+ }
+ }
+ }
+
+ return "";
+ }
+
+ /** Creates a shortcut command "/flag" to toggle specified flag state **/
+ // inline function addFlagCommandAlias(flag:ConsoleFlag) {
+ // #if debug
+ // var str = Std.string(flag);
+ // for(f in allFlags)
+ // if( f.value==flag ) {
+ // str = f.name;
+ // break;
+ // }
+ // addCommand(str, [], ()->{
+ // setFlag(flag, !hasFlag(flag));
+ // });
+ // #end
+ // }
+
+ override function handleCommand(command:String) {
+ var flagReg = ~/[\/ \t]*\+[ \t]*([\w]+)/g; // cleanup missing spaces
+ super.handleCommand( flagReg.replace(command, "/+ $1") );
+ }
+
+ public function error(msg:Dynamic) {
+ log("[ERROR] "+Std.string(msg), errorColor);
+ h2d.Console.HIDE_LOG_TIMEOUT = Const.INFINITE;
+ }
+
+ #if debug
+ public function setFlag(f:ConsoleFlag, v:Bool) {
+ var hadBefore = hasFlag(f);
+
+ if( v )
+ flags.set(f,v);
+ else
+ flags.remove(f);
+
+ if( v && !hadBefore || !v && hadBefore )
+ onFlagChange(f,v);
+ return v;
+ }
+ public function hasFlag(f:ConsoleFlag) return flags.get(f)==true;
+ #else
+ public inline function hasFlag(f:ConsoleFlag) return false;
+ #end
+
+ public function onFlagChange(f:ConsoleFlag, v:Bool) {}
+
+
+ override function log(text:String, ?color:Int) {
+ if( !App.ME.screenshotMode )
+ super.log(text, color);
+ }
+
+ public inline function clearAndLog(str:Dynamic) {
+ runCommand("cls");
+ log( Std.string(str) );
+ }
+}
\ No newline at end of file
diff --git a/src/game/ui/Hud.hx b/src/game/ui/Hud.hx
new file mode 100644
index 0000000..de7a34d
--- /dev/null
+++ b/src/game/ui/Hud.hx
@@ -0,0 +1,111 @@
+package ui;
+
+class Hud extends GameChildProcess {
+ var flow : h2d.Flow;
+ var invalidated = true;
+ var notifications : Array = [];
+ var notifTw : dn.Tweenie;
+
+ var debugText : h2d.Text;
+
+ public function new() {
+ super();
+
+ notifTw = new Tweenie(Const.FPS);
+
+ createRootInLayers(game.root, Const.DP_UI);
+ root.filter = new h2d.filter.Nothing(); // force pixel perfect rendering
+
+ flow = new h2d.Flow(root);
+ notifications = [];
+
+ debugText = new h2d.Text(Assets.fontPixel, root);
+ debugText.filter = new dn.heaps.filter.PixelOutline();
+ clearDebug();
+ }
+
+ override function onResize() {
+ super.onResize();
+ root.setScale(Const.UI_SCALE);
+ }
+
+ /** Clear debug printing **/
+ public inline function clearDebug() {
+ debugText.text = "";
+ debugText.visible = false;
+ }
+
+ /** Display a debug string **/
+ public inline function debug(v:Dynamic, clear=true) {
+ if( clear )
+ debugText.text = Std.string(v);
+ else
+ debugText.text += "\n"+v;
+ debugText.visible = true;
+ debugText.x = Std.int( w()/Const.UI_SCALE - 4 - debugText.textWidth );
+ }
+
+
+ /** Pop a quick s in the corner **/
+ public function notify(str:String, color:Col=0x0) {
+ // Bg
+ var t = Assets.tiles.getTile( D.tiles.uiNotification );
+ var f = new dn.heaps.FlowBg(t, 5, root);
+ f.colorizeBg(color);
+ f.paddingHorizontal = 6;
+ f.paddingBottom = 4;
+ f.paddingTop = 0;
+ f.paddingLeft = 9;
+ f.y = 4;
+
+ // Text
+ var tf = new h2d.Text(Assets.fontPixel, f);
+ tf.text = str;
+ tf.maxWidth = 0.6 * w()/Const.UI_SCALE;
+ tf.textColor = 0xffffff;
+ tf.filter = new dn.heaps.filter.PixelOutline( color.toBlack(0.2) );
+
+ // Notification lifetime
+ var durationS = 2 + str.length*0.04;
+ var p = createChildProcess();
+ notifications.insert(0,f);
+ p.tw.createS(f.x, -f.outerWidth>-2, TEaseOut, 0.1);
+ p.onUpdateCb = ()->{
+ if( p.stime>=durationS && !p.cd.hasSetS("done",Const.INFINITE) )
+ p.tw.createS(f.x, -f.outerWidth, 0.2).end( p.destroy );
+ }
+ p.onDisposeCb = ()->{
+ notifications.remove(f);
+ f.remove();
+ }
+
+ // Move existing notifications
+ var y = 4;
+ for(f in notifications) {
+ notifTw.terminateWithoutCallbacks(f.y);
+ notifTw.createS(f.y, y, TEaseOut, 0.2);
+ y+=f.outerHeight+1;
+ }
+
+ }
+
+ public inline function invalidate() invalidated = true;
+
+ function render() {}
+
+ public function onLevelStart() {}
+
+ override function preUpdate() {
+ super.preUpdate();
+ notifTw.update(tmod);
+ }
+
+ override function postUpdate() {
+ super.postUpdate();
+
+ if( invalidated ) {
+ invalidated = false;
+ render();
+ }
+ }
+}
diff --git a/src/game/ui/IconBar.hx b/src/game/ui/IconBar.hx
new file mode 100644
index 0000000..0966a17
--- /dev/null
+++ b/src/game/ui/IconBar.hx
@@ -0,0 +1,27 @@
+package ui;
+
+class IconBar extends h2d.TileGroup {
+ var curX = 0;
+ public var width(default,null) = 0;
+ public var height(default,null) = 0;
+ public var overlapPx = 2;
+
+ public function new(?p) {
+ super(Assets.tiles.tile, p);
+ }
+
+ public inline function empty() {
+ clear();
+ curX = 0;
+ }
+
+ public function addIcons(iconId:String, n=1) {
+ for(i in 0...n) {
+ var t = Assets.tiles.getTile(iconId);
+ add(curX, 0, t);
+ width = curX + t.iwidth;
+ height = M.imax(height, t.iheight);
+ curX += t.iwidth-overlapPx;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/game/ui/UiComponent.hx b/src/game/ui/UiComponent.hx
new file mode 100644
index 0000000..19ff179
--- /dev/null
+++ b/src/game/ui/UiComponent.hx
@@ -0,0 +1,114 @@
+package ui;
+
+class UiComponent extends h2d.Flow {
+ var _tmpPt : h2d.col.Point;
+
+ public var uid(default,null) : Int;
+
+ public var globalLeft(get,never) : Float;
+ public var globalRight(get,never) : Float;
+ public var globalTop(get,never) : Float;
+ public var globalBottom(get,never) : Float;
+
+ public var globalWidth(get,never) : Int;
+ public var globalHeight(get,never) : Int;
+
+ public var globalCenterX(get,never) : Float;
+ public var globalCenterY(get,never) : Float;
+
+
+ public function new(?p:h2d.Object) {
+ super(p);
+ uid = Const.makeUniqueId();
+ _tmpPt = new h2d.col.Point();
+ }
+
+ @:keep override function toString() {
+ return super.toString()+".UiComponent";
+ }
+
+ public final function use() {
+ onUse();
+ onUseCb();
+ }
+ function onUse() {}
+ public dynamic function onUseCb() {}
+
+ @:allow(ui.UiGroupController)
+ function onFocus() {
+ filter = new dn.heaps.filter.Invert();
+ }
+
+ @:allow(ui.UiGroupController)
+ function onBlur() {
+ filter = null;
+ }
+
+
+
+ function get_globalLeft() {
+ _tmpPt.set();
+ localToGlobal(_tmpPt);
+ return _tmpPt.x;
+ }
+
+ function get_globalRight() {
+ _tmpPt.set(outerWidth, outerHeight);
+ localToGlobal(_tmpPt);
+ return _tmpPt.x;
+ }
+
+ function get_globalTop() {
+ _tmpPt.set();
+ localToGlobal(_tmpPt);
+ return _tmpPt.y;
+ }
+
+ function get_globalBottom() {
+ _tmpPt.set(outerWidth, outerHeight);
+ localToGlobal(_tmpPt);
+ return _tmpPt.y;
+ }
+
+ inline function get_globalWidth() return Std.int( globalRight - globalLeft );
+ inline function get_globalHeight() return Std.int( globalBottom - globalTop );
+ inline function get_globalCenterX() return ( globalLeft + globalRight ) * 0.5;
+ inline function get_globalCenterY() return ( globalTop + globalBottom ) * 0.5;
+
+
+ public function getRelativeX(relativeTo:h2d.Object) {
+ _tmpPt.set();
+ localToGlobal(_tmpPt);
+ relativeTo.globalToLocal(_tmpPt);
+ return _tmpPt.x;
+ }
+
+ public function getRelativeY(relativeTo:h2d.Object) {
+ _tmpPt.set();
+ localToGlobal(_tmpPt);
+ relativeTo.globalToLocal(_tmpPt);
+ return _tmpPt.y;
+ }
+
+ public inline function globalAngTo(to:UiComponent) {
+ return Math.atan2(to.globalCenterY-globalCenterY, to.globalCenterX-globalCenterX);
+ }
+
+ public inline function globalDistTo(to:UiComponent) {
+ return M.dist(globalCenterX, globalCenterY, to.globalCenterX, to.globalCenterY);
+ }
+
+ public function overlapsRect(x:Float, y:Float, w:Int, h:Int) {
+ return dn.Geom.rectOverlapsRect(
+ globalLeft, globalTop, globalWidth, globalHeight,
+ x, y, w, h
+ );
+ }
+
+ override function sync(ctx:h2d.RenderContext) {
+ super.sync(ctx);
+ update();
+ }
+
+ function update() {}
+}
\ No newline at end of file
diff --git a/src/game/ui/UiGroupController.hx b/src/game/ui/UiGroupController.hx
new file mode 100644
index 0000000..e932fec
--- /dev/null
+++ b/src/game/ui/UiGroupController.hx
@@ -0,0 +1,450 @@
+package ui;
+
+enum abstract GroupDir(Int) {
+ var North;
+ var East;
+ var South;
+ var West;
+}
+
+/**
+ This process takes care of interactions with a group of UiComponents.
+ This includes:
+ - user interaction with a component,
+ - focus/blur of a component,
+ - supports gamepad, keyboard and mouse.
+
+ USAGE:
+ - Add some UiComponents to your scene,
+ - Create a UiGroupController instance,
+ - Register all these UiComponents in the UiGroupController.
+**/
+class UiGroupController extends dn.Process {
+ var uid : Int;
+ var ca : ControllerAccess;
+ public var currentComp(default,null) : Null;
+
+ var components : Array = [];
+
+ var connectionsNeedRebuild = false;
+ var uiGroupsConnections : Map = new Map();
+ var componentsConnections : Map> = new Map();
+
+ var groupFocused = true;
+ var useMouse : Bool;
+
+
+ public function new(parentProcess:dn.Process, useMouse=true) {
+ super(parentProcess);
+
+ this.useMouse = useMouse;
+
+ uid = Const.makeUniqueId();
+ ca = App.ME.controller.createAccess();
+ ca.lockCondition = ()->!groupFocused || customControllerLock();
+ ca.lock(0.1);
+ }
+
+
+ public dynamic function customControllerLock() return false;
+
+
+ public function registerComponent(comp:ui.UiComponent) {
+ components.push(comp);
+ comp.onAfterReflow = invalidateConnections; // TODO not a reliable solution
+
+ if( useMouse ) {
+ comp.enableInteractive = true;
+ comp.interactive.cursor = Button;
+
+ comp.interactive.onOver = _->{
+ focusComponent(comp);
+ focusGroup();
+ }
+
+ comp.interactive.onOut = _->{
+ blurComponent(comp);
+ }
+
+ comp.interactive.onClick = ev->{
+ if( ev.button==0 )
+ comp.use();
+ }
+
+ comp.interactive.enableRightButton = true;
+ }
+ }
+
+
+ public function focusGroup() {
+ var wasFocused = groupFocused;
+ groupFocused = true;
+ ca.lock(0.2);
+ blurAllConnectedGroups();
+ if( !wasFocused )
+ onGroupFocusCb();
+ }
+
+ public function blurGroup() {
+ var wasFocused = groupFocused;
+ groupFocused = false;
+ if( currentComp!=null ) {
+ currentComp.onBlur();
+ currentComp = null;
+ }
+ if( wasFocused )
+ onGroupBlurCb();
+ }
+
+ public dynamic function onGroupFocusCb() {}
+ public dynamic function onGroupBlurCb() {}
+
+ function blurAllConnectedGroups(?ignoredGroup:UiGroupController) {
+ var pending = [this];
+ var dones = new Map();
+ dones.set(uid,true);
+
+ while( pending.length>0 ) {
+ var cur = pending.pop();
+ dones.set(cur.uid, true);
+ for(g in cur.uiGroupsConnections) {
+ if( dones.exists(g.uid) )
+ continue;
+ g.blurGroup();
+ pending.push(g);
+ }
+ }
+ }
+
+ public function connectComponents(from:UiComponent, to:UiComponent, dir:GroupDir) {
+ if( !componentsConnections.exists(from.uid) )
+ componentsConnections.set(from.uid, new Map());
+ componentsConnections.get(from.uid).set(dir, to);
+ }
+
+ public function countComponentConnections(c:UiComponent) {
+ if( !componentsConnections.exists(c.uid) )
+ return 0;
+
+ var n = 0;
+ for( next in componentsConnections.get(c.uid) )
+ n++;
+ return n;
+ }
+
+ public inline function hasComponentConnectionDir(c:UiComponent, dir:GroupDir) {
+ return componentsConnections.exists(c.uid) && componentsConnections.get(c.uid).exists(dir);
+ }
+
+ public function hasComponentConnection(from:UiComponent, to:UiComponent) {
+ if( !componentsConnections.exists(from.uid) )
+ return false;
+
+ for(next in componentsConnections.get(from.uid))
+ if( next==to )
+ return true;
+ return false;
+ }
+
+ public inline function getComponentConnectionDir(from:UiComponent, dir:GroupDir) {
+ return componentsConnections.exists(from.uid)
+ ? componentsConnections.get(from.uid).get(dir)
+ : null;
+ }
+
+
+
+ public inline function invalidateConnections() {
+ connectionsNeedRebuild = true;
+ }
+
+ function buildConnections() {
+ // Clear
+ componentsConnections = new Map();
+
+ // Build connections with closest aligned components
+ for(from in components)
+ for(dir in [North,East,South,West]) {
+ var other = findComponentRaycast(from,dir);
+ if( other!=null ) {
+ connectComponents(from, other, dir);
+ connectComponents(other, from, getOppositeDir(dir));
+ }
+ }
+
+ // Fix missing connections
+ for(from in components)
+ for(dir in [North,East,South,West]) {
+ if( hasComponentConnectionDir(from,dir) )
+ continue;
+ var next = findComponentFromAng(from, dirToAng(dir), M.PI*0.8, true);
+ if( next!=null )
+ connectComponents(from,next,dir);
+ }
+ }
+
+
+ // Returns closest UiComponent using an angle range
+ function findComponentFromAng(from:UiComponent, ang:Float, angRange:Float, ignoreConnecteds:Bool) : Null {
+ var best = null;
+ for( other in components ) {
+ if( other==from || hasComponentConnection(from,other) )
+ continue;
+
+ if( M.radDistance(ang, from.globalAngTo(other)) < angRange*0.5 ) {
+ if( best==null )
+ best = other;
+ else {
+ if( from.globalDistTo(other) < from.globalDistTo(best) )
+ best = other;
+ }
+ }
+ }
+ return best;
+
+
+ }
+
+ // Returns closest UiComponent using a collider-raycast
+ function findComponentRaycast(from:UiComponent, dir:GroupDir) : Null {
+ var ang = dirToAng(dir);
+ var step = switch dir {
+ case North, South: from.globalHeight;
+ case East,West: from.globalWidth;
+ }
+ var x = from.globalLeft + Math.cos(ang)*step;
+ var y = from.globalTop + Math.sin(ang)*step;
+ var elapsedDist = step;
+
+ var possibleNexts = [];
+ while( elapsedDist0 )
+ return dn.Lib.findBestInArray(possibleNexts, (t)->-t.globalDistTo(from) );
+
+ x += Math.cos(ang)*step;
+ y += Math.sin(ang)*step;
+ elapsedDist+=step;
+ }
+
+
+ return null;
+ }
+
+
+ function findClosest(from:UiComponent) : Null {
+ var best = null;
+ for(other in components)
+ if( other!=from && ( best==null || from.globalDistTo(other) < from.globalDistTo(best) ) )
+ best = other;
+ return best;
+ }
+
+
+ public function createDebugger() {
+ var g = new h2d.Graphics(App.ME.root);
+ var debugProc = createChildProcess();
+ debugProc.onUpdateCb = ()->{
+ if( !debugProc.cd.hasSetS("tick",0.1) )
+ renderDebugToGraphics(g);
+ }
+ debugProc.onDisposeCb = ()->{
+ g.remove();
+ }
+ }
+
+ /**
+ Draw a debug render of the group structure into an existing Graphics object.
+ NOTE: the render uses global coordinates, so the Graphics object should be attached to the scene root.
+ **/
+ public function renderDebugToGraphics(g:h2d.Graphics) {
+ g.clear();
+ g.removeChildren();
+ buildConnections();
+ var font = hxd.res.DefaultFont.get();
+ for(from in components) {
+ // Bounds
+ g.lineStyle(2, Pink);
+ g.beginFill(Pink, 0.5);
+ g.drawRect(from.globalLeft, from.globalTop, from.globalWidth, from.globalHeight);
+ g.endFill();
+ // Connections
+ for(dir in [North,East,South,West]) {
+ if( !hasComponentConnectionDir(from,dir) )
+ continue;
+
+ var next = getComponentConnectionDir(from,dir);
+ var ang = from.globalAngTo(next);
+ g.lineStyle(2, Yellow);
+ g.moveTo(from.globalCenterX, from.globalCenterY);
+ g.lineTo(next.globalCenterX, next.globalCenterY);
+
+ // Arrow head
+ var arrowDist = 16;
+ var arrowAng = M.PI*0.95;
+ g.moveTo(next.globalCenterX, next.globalCenterY);
+ g.lineTo(next.globalCenterX+Math.cos(ang+arrowAng)*arrowDist, next.globalCenterY+Math.sin(ang+arrowAng)*arrowDist);
+
+ g.moveTo(next.globalCenterX, next.globalCenterY);
+ g.lineTo(next.globalCenterX+Math.cos(ang-arrowAng)*arrowDist, next.globalCenterY+Math.sin(ang-arrowAng)*arrowDist);
+
+ var tf = new h2d.Text(font,g);
+ tf.text = switch dir {
+ case North: 'N';
+ case East: 'E';
+ case South: 'S';
+ case West: 'W';
+ }
+ tf.x = Std.int( ( from.globalCenterX*0.3 + next.globalCenterX*0.7 ) - tf.textWidth*0.5 );
+ tf.y = Std.int( ( from.globalCenterY*0.3 + next.globalCenterY*0.7 ) - tf.textHeight*0.5 );
+ tf.filter = new dn.heaps.filter.PixelOutline();
+ }
+ }
+ }
+
+
+ override function onDispose() {
+ super.onDispose();
+
+ ca.dispose();
+ ca = null;
+
+ components = null;
+ currentComp = null;
+ }
+
+ public function clearAllRegisteredComponents() {
+ currentComp = null;
+ components = [];
+ invalidateConnections();
+ }
+
+ function focusClosestComponentFromGlobalCoord(x:Float, y:Float) {
+ var best = Lib.findBestInArray(components, e->{
+ return -M.dist(x, y, e.globalCenterX, e.globalCenterY);
+ });
+ if( best!=null )
+ focusComponent(best);
+ }
+
+ function blurComponent(ge:UiComponent) {
+ if( currentComp==ge ) {
+ currentComp.onBlur();
+ currentComp = null;
+ }
+ }
+
+ function focusComponent(ge:UiComponent) {
+ if( currentComp==ge )
+ return;
+
+ if( currentComp!=null )
+ currentComp.onBlur();
+ currentComp = ge;
+ currentComp.onFocus();
+ }
+
+ inline function getOppositeDir(dir:GroupDir) {
+ return switch dir {
+ case North: South;
+ case East: West;
+ case South: North;
+ case West: East;
+ }
+ }
+
+ inline function dirToAng(dir:GroupDir) : Float {
+ return switch dir {
+ case North: -M.PIHALF;
+ case East: 0;
+ case South: M.PIHALF;
+ case West: M.PI;
+ }
+ }
+
+ function angToDir(ang:Float) : GroupDir {
+ return M.radDistance(ang,0)<=M.PIHALF*0.5 ? East
+ : M.radDistance(ang,M.PIHALF)<=M.PIHALF*0.5 ? South
+ : M.radDistance(ang,M.PI)<=M.PIHALF*0.5 ? West
+ : North;
+ }
+
+
+ function gotoNextDir(dir:GroupDir) {
+ if( currentComp==null )
+ return;
+
+ if( hasComponentConnectionDir(currentComp,dir) )
+ focusComponent( getComponentConnectionDir(currentComp,dir) );
+ else
+ gotoConnectedGroup(dir);
+ }
+
+
+ function gotoConnectedGroup(dir:GroupDir) : Bool {
+ if( !uiGroupsConnections.exists(dir) )
+ return false;
+
+ if( uiGroupsConnections.get(dir).components.length==0 )
+ return false;
+
+ var g = uiGroupsConnections.get(dir);
+ var from = currentComp;
+ // var pt = new h2d.col.Point(from.width*0.5, from.height*0.5);
+ // from.f.localToGlobal(pt);
+ blurGroup();
+ g.focusGroup();
+ g.focusClosestComponentFromGlobalCoord(from.globalCenterX, from.globalCenterY);
+ return true;
+ }
+
+
+ public function connectGroup(dir:GroupDir, targetGroup:UiGroupController, symetric=true) {
+ uiGroupsConnections.set(dir,targetGroup);
+ if( symetric )
+ targetGroup.connectGroup(getOppositeDir(dir), this, false);
+
+ if( groupFocused )
+ blurAllConnectedGroups();
+ }
+
+
+ override function preUpdate() {
+ super.preUpdate();
+
+ if( !groupFocused )
+ return;
+
+ // Build components connections
+ if( connectionsNeedRebuild ) {
+ connectionsNeedRebuild = false;
+ buildConnections();
+ }
+
+ // Init default currentComp
+ if( currentComp==null && components.length>0 )
+ if( !cd.hasSetS("firstInitDone",Const.INFINITE) || ca.isDown(MenuLeft) || ca.isDown(MenuRight) || ca.isDown(MenuUp) || ca.isDown(MenuDown) )
+ focusComponent(components[0]);
+
+ if( currentComp!=null ) {
+ // Use current
+ if( ca.isPressed(MenuOk) )
+ currentComp.use();
+
+ // Move current
+ if( ca.isPressedAutoFire(MenuLeft) )
+ gotoNextDir(West);
+ else if( ca.isPressedAutoFire(MenuRight) )
+ gotoNextDir(East);
+
+ if( ca.isPressedAutoFire(MenuUp) )
+ gotoNextDir(North);
+ else if( ca.isPressedAutoFire(MenuDown) )
+ gotoNextDir(South);
+ }
+ }
+}
+
diff --git a/src/game/ui/Window.hx b/src/game/ui/Window.hx
new file mode 100644
index 0000000..fe6b567
--- /dev/null
+++ b/src/game/ui/Window.hx
@@ -0,0 +1,236 @@
+package ui;
+
+enum WindowAlign {
+ Start;
+ End;
+ Center;
+ Fill;
+}
+
+class Window extends dn.Process {
+ public static var ALL : Array = [];
+
+ var uiWid(get,never) : Int; inline function get_uiWid() return M.ceil( w()/Const.UI_SCALE );
+ var uiHei(get,never) : Int; inline function get_uiHei() return M.ceil( h()/Const.UI_SCALE );
+
+ public var content: h2d.Flow;
+
+ var ca : ControllerAccess;
+ var mask : Null;
+
+ public var isModal(default, null) = false;
+ public var canBeClosedManually = true;
+ public var horizontalAlign(default,set) : WindowAlign = WindowAlign.Center;
+ public var verticalAlign(default,set) : WindowAlign = WindowAlign.Center;
+
+
+ public function new(modal:Bool, ?p:dn.Process) {
+ var parentProc = p==null ? App.ME : p;
+ super(parentProc);
+
+ ALL.push(this);
+ createRootInLayers(parentProc.root, Const.DP_UI);
+ root.filter = new h2d.filter.Nothing(); // force pixel perfect rendering
+
+ content = new h2d.Flow(root);
+ content.backgroundTile = h2d.Tile.fromColor(0xffffff, 32,32);
+ content.borderWidth = 7;
+ content.borderHeight = 7;
+ content.layout = Vertical;
+ content.verticalSpacing = 2;
+ content.onAfterReflow = onResize;
+ content.enableInteractive = true;
+
+ ca = App.ME.controller.createAccess();
+ ca.lockCondition = ()->App.ME.anyInputHasFocus() || !isActive();
+ ca.lock(0.1);
+
+ emitResizeAtEndOfFrame();
+
+ if( modal )
+ makeModal();
+ }
+
+ function getModalIndex() {
+ if( !isModal )
+ return -1;
+
+ var i = 0;
+ for( w in ALL )
+ if( w.isModal ) {
+ if( w==this )
+ return i;
+ i++;
+ }
+ Console.ME.error('$this has no valid modalIndex');
+ return -1;
+ }
+
+ function set_horizontalAlign(v:WindowAlign) {
+ if( v!=horizontalAlign ) {
+ switch horizontalAlign {
+ case Fill: content.minWidth = content.maxWidth = null; // clear previous constraint from onResize()
+ case _:
+ }
+ horizontalAlign = v;
+ emitResizeAtEndOfFrame();
+ }
+ return v;
+ }
+
+ function set_verticalAlign(v:WindowAlign) {
+ if( v!=verticalAlign ) {
+ switch verticalAlign {
+ case Fill: content.minHeight = content.maxHeight = null; // clear previous constraint from onResize()
+ case _:
+ }
+ verticalAlign = v;
+ emitResizeAtEndOfFrame();
+ }
+ return v;
+ }
+
+ public function setAlign(h:WindowAlign, ?v:WindowAlign) {
+ horizontalAlign = h;
+ verticalAlign = v!=null ? v : h;
+ }
+
+ public function isActive() {
+ return !destroyed && ( !isModal || isLatestModal() );
+ }
+
+ public function makeTransparent() {
+ content.backgroundTile = null;
+ }
+
+ override function onDispose() {
+ super.onDispose();
+
+ ALL.remove(this);
+
+ ca.dispose();
+ ca = null;
+
+ if( !hasAnyModal() )
+ Game.ME.resume();
+
+ emitResizeAtEndOfFrame();
+ }
+
+ @:keep override function toString():String {
+ return isModal ? 'ModalWin${isActive()?"*":""}(${getModalIndex()})' : 'Win';
+ }
+
+ function makeModal() {
+ if( isModal )
+ return;
+
+ isModal = true;
+
+ if( getModalIndex()==0 )
+ Game.ME.pause();
+
+ mask = new h2d.Flow(root);
+ mask.backgroundTile = h2d.Tile.fromColor(0x0, 1, 1, 0.8);
+ mask.enableInteractive = true;
+ mask.interactive.onClick = _->{
+ if( canBeClosedManually )
+ close();
+ }
+ mask.interactive.enableRightButton = true;
+ root.under(mask);
+ }
+
+ function isLatestModal() {
+ var idx = ALL.length-1;
+ while( idx>=0 ) {
+ var w = ALL[idx];
+ if( !w.destroyed ) {
+ if( w!=this && w.isModal )
+ return false;
+ if( w==this )
+ return true;
+ }
+ idx--;
+ }
+ return false;
+ }
+
+ public static function hasAnyModal() {
+ for(e in ALL)
+ if( !e.destroyed && e.isModal )
+ return true;
+ return false;
+ }
+
+ public function clearContent() {
+ content.removeChildren();
+ }
+
+
+ override function onResize() {
+ super.onResize();
+
+ root.setScale(Const.UI_SCALE);
+
+ // Horizontal
+ if( horizontalAlign==Fill )
+ content.minWidth = content.maxWidth = uiWid;
+
+ switch horizontalAlign {
+ case Start: content.x = 0;
+ case End: content.x = uiWid-content.outerWidth;
+ case Center: content.x = Std.int( uiWid*0.5 - content.outerWidth*0.5 + getModalIndex()*8 );
+ case Fill: content.x = 0; content.minWidth = content.maxWidth = uiWid;
+ }
+
+ // Vertical
+ if( verticalAlign==Fill )
+ content.minHeight = content.maxHeight = uiHei;
+
+ switch verticalAlign {
+ case Start: content.y = 0;
+ case End: content.y = uiHei-content.outerHeight;
+ case Center: content.y = Std.int( uiHei*0.5 - content.outerHeight*0.5 + getModalIndex()*4 );
+ case Fill: content.y = 0; content.minHeight = content.maxHeight = uiHei;
+ }
+
+ // Mask
+ if( mask!=null ) {
+ mask.minWidth = uiWid;
+ mask.minHeight = uiHei;
+ }
+ }
+
+ public dynamic function onClose() {}
+
+ public function close() {
+ if( !destroyed ) {
+ destroy();
+ onClose();
+ }
+ }
+
+
+ public function addSpacer(pixels=4) {
+ var f = new h2d.Flow(content);
+ f.minWidth = f.minHeight = pixels;
+ }
+
+ public function addTitle(str:String) {
+ new ui.component.Text( str.toUpperCase(), Col.coldGray(0.5), content );
+ addSpacer();
+ }
+
+ public function addText(str:String, col:Col=Black) {
+ new ui.component.Text( str, col, content );
+ }
+
+
+
+ override function update() {
+ super.update();
+ if( canBeClosedManually && isModal && ca.isPressed(MenuCancel) )
+ close();
+ }
+}
diff --git a/src/game/ui/component/Button.hx b/src/game/ui/component/Button.hx
new file mode 100644
index 0000000..60e52a0
--- /dev/null
+++ b/src/game/ui/component/Button.hx
@@ -0,0 +1,26 @@
+package ui.component;
+
+class Button extends ui.UiComponent {
+ var tf : h2d.Text;
+
+ public function new(?label:String, ?iconTile:h2d.Tile, col:dn.Col=Black, ?p:h2d.Object) {
+ super(p);
+
+ verticalAlign = Middle;
+ padding = 2;
+ paddingBottom = 4;
+ backgroundTile = h2d.Tile.fromColor(White);
+
+ if( iconTile!=null )
+ new h2d.Bitmap(iconTile, this);
+
+ tf = new h2d.Text(Assets.fontPixelMono, this);
+ if( label!=null )
+ setLabel(label, col);
+ }
+
+ public function setLabel(str:String, col:dn.Col=Black) {
+ tf.text = str;
+ tf.textColor = col;
+ }
+}
diff --git a/src/game/ui/component/CheckBox.hx b/src/game/ui/component/CheckBox.hx
new file mode 100644
index 0000000..5036f51
--- /dev/null
+++ b/src/game/ui/component/CheckBox.hx
@@ -0,0 +1,33 @@
+package ui.component;
+
+class CheckBox extends ui.component.Button {
+ var label : String;
+ var lastDisplayedValue : Bool;
+ var getter : Void->Bool;
+ var setter : Bool->Void;
+
+ public function new(label:String, getter:Void->Bool, setter:Bool->Void, ?p:h2d.Object) {
+ this.getter = getter;
+ this.setter = setter;
+ super(label, p);
+ }
+
+ override function onUse() {
+ super.onUse();
+
+ setter(!getter());
+ setLabel(label);
+ }
+
+ override function setLabel(str:String, col:Col = Black) {
+ label = str;
+ lastDisplayedValue = getter();
+ super.setLabel( (getter()?"[ON]":"[ ]")+" "+label, col );
+ }
+
+ override function sync(ctx:h2d.RenderContext) {
+ super.sync(ctx);
+ if( lastDisplayedValue!=getter() )
+ setLabel(label);
+ }
+}
diff --git a/src/game/ui/component/ControlsHelp.hx b/src/game/ui/component/ControlsHelp.hx
new file mode 100644
index 0000000..18b40b2
--- /dev/null
+++ b/src/game/ui/component/ControlsHelp.hx
@@ -0,0 +1,26 @@
+package ui.component;
+
+class ControlsHelp extends ui.UiComponent {
+ public function new(?p) {
+ super(p);
+
+ layout = Horizontal;
+ horizontalSpacing = 16;
+ }
+
+
+ public function addControl(a:GameAction, label:String, col:Col=White) {
+ var f = new h2d.Flow(this);
+ f.layout = Horizontal;
+ f.verticalAlign = Middle;
+
+ var icon = App.ME.controller.getFirstBindindIconFor(a, "agnostic", f);
+ f.addSpacing(4);
+
+ var tf = new h2d.Text(Assets.fontPixel, f);
+ f.getProperties(tf).offsetY = -2;
+ tf.textColor = col;
+ tf.text = txt;
+ }
+
+}
diff --git a/src/game/ui/component/Text.hx b/src/game/ui/component/Text.hx
new file mode 100644
index 0000000..fa787aa
--- /dev/null
+++ b/src/game/ui/component/Text.hx
@@ -0,0 +1,13 @@
+package ui.component;
+
+class Text extends ui.UiComponent {
+ public function new(label:String, col:dn.Col=Black, ?p) {
+ super(p);
+
+ paddingTop = 4;
+ paddingBottom = 4;
+ var tf = new h2d.Text(Assets.fontPixelMono, this);
+ tf.textColor = col;
+ tf.text = label;
+ }
+}
diff --git a/src/game/ui/win/DebugWindow.hx b/src/game/ui/win/DebugWindow.hx
new file mode 100644
index 0000000..182c6c4
--- /dev/null
+++ b/src/game/ui/win/DebugWindow.hx
@@ -0,0 +1,36 @@
+package ui.win;
+
+class DebugWindow extends ui.Window {
+ public var updateCooldownS = 0.0;
+
+ public function new(?renderCb:DebugWindow->Void) {
+ super(false);
+
+ if( renderCb!=null )
+ this.renderCb = renderCb;
+
+ content.backgroundTile = Col.white().toTile(1,1, 0.5);
+ content.padding = 4;
+ content.horizontalSpacing = 4;
+ content.verticalSpacing = 0;
+ content.layout = Vertical;
+ setAlign(End,Start);
+ }
+
+ public dynamic function renderCb(thisWin:DebugWindow) {}
+
+ override function onResize() {
+ super.onResize();
+ switch verticalAlign {
+ case Start,End: content.maxHeight = Std.int( 0.4 * h()/Const.UI_SCALE );
+ case Center: content.maxHeight = Std.int( 0.8 * h()/Const.UI_SCALE );
+ case Fill: content.maxHeight = Std.int( h()/Const.UI_SCALE );
+ }
+ }
+
+ override function update() {
+ super.update();
+ if( updateCooldownS<=0 || !cd.hasSetS("updateLock",updateCooldownS) )
+ renderCb(this);
+ }
+}
\ No newline at end of file
diff --git a/src/game/ui/win/SimpleMenu.hx b/src/game/ui/win/SimpleMenu.hx
new file mode 100644
index 0000000..00cc9b3
--- /dev/null
+++ b/src/game/ui/win/SimpleMenu.hx
@@ -0,0 +1,55 @@
+package ui.win;
+
+class SimpleMenu extends ui.Window {
+ public var uiCtrl : UiGroupController;
+
+ public function new() {
+ super(true);
+
+ content.padding = 1;
+ content.horizontalSpacing = 4;
+ content.verticalSpacing = 0;
+ content.layout = Vertical;
+ content.multiline = true;
+ content.colWidth = 150;
+
+ uiCtrl = new UiGroupController(this);
+ uiCtrl.customControllerLock = ()->!isActive();
+ }
+
+
+ public function setColumnWidth(w:Int) {
+ content.colWidth = w;
+ }
+
+ override function onResize() {
+ super.onResize();
+ switch verticalAlign {
+ case Start,End: content.maxHeight = Std.int( 0.4 * h()/Const.UI_SCALE );
+ case Center: content.maxHeight = Std.int( 0.8 * h()/Const.UI_SCALE );
+ case Fill: content.maxHeight = Std.int( h()/Const.UI_SCALE );
+ }
+ }
+
+ public function addButton(label:String, ?tile:h2d.Tile, autoClose=true, cb:Void->Void) {
+ var bt = new ui.component.Button(label, tile, content);
+ bt.minWidth = content.colWidth;
+ bt.onUseCb = ()->{
+ cb();
+ if( autoClose )
+ close();
+ }
+ uiCtrl.registerComponent(bt);
+ }
+
+ public function addCheckBox(label:String, getter:Void->Bool, setter:Bool->Void, autoClose=false) {
+ var bt = new ui.component.CheckBox(label,getter,setter,content);
+ bt.minWidth = content.colWidth;
+ bt.onUseCb = ()->{
+ if( autoClose )
+ close();
+ }
+
+ uiCtrl.registerComponent(bt);
+ }
+}
\ No newline at end of file
diff --git a/src/langParser/LangParser.hx b/src/langParser/LangParser.hx
new file mode 100644
index 0000000..66e82f2
--- /dev/null
+++ b/src/langParser/LangParser.hx
@@ -0,0 +1,24 @@
+import dn.data.GetText;
+
+class LangParser {
+ public static function main() {
+ var allEntries : Array = [];
+
+ // Extract from source code
+ GetText.parseSourceCode(allEntries, "src");
+
+ // Extract from LDtk
+ GetText.parseLdtk(allEntries, "res/levels/sampleWorld.ldtk", {
+ entityFields: [], // fill this with Entity fields that should be extracted for localization
+ levelFieldIds: [], // fill this with Level fields that should be extracted for localization
+ });
+
+ // Extract from CastleDB
+ GetText.parseCastleDB(allEntries, "res/data.cdb");
+
+ // Write POT
+ GetText.writePOT("res/lang/sourceTexts.pot", allEntries);
+
+ Sys.println("Done.");
+ }
+}
\ No newline at end of file
diff --git a/tools.langParser.hxml b/tools.langParser.hxml
new file mode 100644
index 0000000..5dffbb9
--- /dev/null
+++ b/tools.langParser.hxml
@@ -0,0 +1,9 @@
+-cp src/langParser
+-main LangParser
+-lib castle
+-lib deepnightLibs
+-D potools
+-hl bin/langParser.hl
+
+--next
+-cmd hl bin/langParser.hl
\ No newline at end of file