diff --git a/COPYING b/COPYING index a232a6f4..f7cad76e 100644 --- a/COPYING +++ b/COPYING @@ -40,6 +40,11 @@ lua-discordRPC License: MIT/Expat Copyright (c) 2018 Joel Schumacher +Flux + Website: https://github.com/rxi/flux + License: MIT/Expat + Copyright (c) 2016 rxi + License text ============ diff --git a/client/assets/themes/Panel Attack Modern/scenes/Circle@2x.png b/client/assets/themes/Panel Attack Modern/scenes/Circle@2x.png new file mode 100644 index 00000000..0fdb5f97 Binary files /dev/null and b/client/assets/themes/Panel Attack Modern/scenes/Circle@2x.png differ diff --git a/client/assets/themes/Panel Attack Modern/scenes/Crystal@2x.png b/client/assets/themes/Panel Attack Modern/scenes/Crystal@2x.png new file mode 100644 index 00000000..5d64c51d Binary files /dev/null and b/client/assets/themes/Panel Attack Modern/scenes/Crystal@2x.png differ diff --git a/client/assets/themes/Panel Attack Modern/scenes/DoubleWindowBackground.png b/client/assets/themes/Panel Attack Modern/scenes/DoubleWindowBackground.png new file mode 100644 index 00000000..9bc0bc7e Binary files /dev/null and b/client/assets/themes/Panel Attack Modern/scenes/DoubleWindowBackground.png differ diff --git a/client/assets/themes/Panel Attack Modern/scenes/DoubleWindowFrame.png b/client/assets/themes/Panel Attack Modern/scenes/DoubleWindowFrame.png new file mode 100644 index 00000000..fa2562b8 Binary files /dev/null and b/client/assets/themes/Panel Attack Modern/scenes/DoubleWindowFrame.png differ diff --git a/client/assets/themes/Panel Attack Modern/scenes/DoubleWindowGlass.png b/client/assets/themes/Panel Attack Modern/scenes/DoubleWindowGlass.png new file mode 100644 index 00000000..cf61fe17 Binary files /dev/null and b/client/assets/themes/Panel Attack Modern/scenes/DoubleWindowGlass.png differ diff --git a/client/assets/themes/Panel Attack Modern/scenes/DoubleWindowGlow.png b/client/assets/themes/Panel Attack Modern/scenes/DoubleWindowGlow.png new file mode 100644 index 00000000..eeb3cece Binary files /dev/null and b/client/assets/themes/Panel Attack Modern/scenes/DoubleWindowGlow.png differ diff --git a/client/assets/themes/Panel Attack Modern/scenes/DoubleWindowLight.png b/client/assets/themes/Panel Attack Modern/scenes/DoubleWindowLight.png new file mode 100644 index 00000000..3a1da34e Binary files /dev/null and b/client/assets/themes/Panel Attack Modern/scenes/DoubleWindowLight.png differ diff --git a/client/assets/themes/Panel Attack Modern/scenes/Gear0@2x.png b/client/assets/themes/Panel Attack Modern/scenes/Gear0@2x.png new file mode 100644 index 00000000..865be8c2 Binary files /dev/null and b/client/assets/themes/Panel Attack Modern/scenes/Gear0@2x.png differ diff --git a/client/assets/themes/Panel Attack Modern/scenes/Gear1@2x.png b/client/assets/themes/Panel Attack Modern/scenes/Gear1@2x.png new file mode 100644 index 00000000..a23c0feb Binary files /dev/null and b/client/assets/themes/Panel Attack Modern/scenes/Gear1@2x.png differ diff --git a/client/assets/themes/Panel Attack Modern/scenes/Gear2@2x.png b/client/assets/themes/Panel Attack Modern/scenes/Gear2@2x.png new file mode 100644 index 00000000..28bd9a20 Binary files /dev/null and b/client/assets/themes/Panel Attack Modern/scenes/Gear2@2x.png differ diff --git a/client/assets/themes/Panel Attack Modern/scenes/Gear3-1@2x.png b/client/assets/themes/Panel Attack Modern/scenes/Gear3-1@2x.png new file mode 100644 index 00000000..de04952e Binary files /dev/null and b/client/assets/themes/Panel Attack Modern/scenes/Gear3-1@2x.png differ diff --git a/client/assets/themes/Panel Attack Modern/scenes/Gear3-2@2x.png b/client/assets/themes/Panel Attack Modern/scenes/Gear3-2@2x.png new file mode 100644 index 00000000..2e427b75 Binary files /dev/null and b/client/assets/themes/Panel Attack Modern/scenes/Gear3-2@2x.png differ diff --git a/client/assets/themes/Panel Attack Modern/scenes/Gear3@2x.png b/client/assets/themes/Panel Attack Modern/scenes/Gear3@2x.png new file mode 100644 index 00000000..cac63ab5 Binary files /dev/null and b/client/assets/themes/Panel Attack Modern/scenes/Gear3@2x.png differ diff --git a/client/assets/themes/Panel Attack Modern/scenes/Gear4@2x.png b/client/assets/themes/Panel Attack Modern/scenes/Gear4@2x.png new file mode 100644 index 00000000..385e6ca4 Binary files /dev/null and b/client/assets/themes/Panel Attack Modern/scenes/Gear4@2x.png differ diff --git a/client/assets/themes/Panel Attack Modern/scenes/Gear5-1@2x.png b/client/assets/themes/Panel Attack Modern/scenes/Gear5-1@2x.png new file mode 100644 index 00000000..07bde902 Binary files /dev/null and b/client/assets/themes/Panel Attack Modern/scenes/Gear5-1@2x.png differ diff --git a/client/assets/themes/Panel Attack Modern/scenes/Glyph@2x.png b/client/assets/themes/Panel Attack Modern/scenes/Glyph@2x.png new file mode 100644 index 00000000..483bafcd Binary files /dev/null and b/client/assets/themes/Panel Attack Modern/scenes/Glyph@2x.png differ diff --git a/client/assets/themes/Panel Attack Modern/scenes/GlyphCircle@2x.png b/client/assets/themes/Panel Attack Modern/scenes/GlyphCircle@2x.png new file mode 100644 index 00000000..ed80baa4 Binary files /dev/null and b/client/assets/themes/Panel Attack Modern/scenes/GlyphCircle@2x.png differ diff --git a/client/assets/themes/Panel Attack Modern/scenes/HourGlass@2x.png b/client/assets/themes/Panel Attack Modern/scenes/HourGlass@2x.png new file mode 100644 index 00000000..ce9fed17 Binary files /dev/null and b/client/assets/themes/Panel Attack Modern/scenes/HourGlass@2x.png differ diff --git a/client/assets/themes/Panel Attack Modern/scenes/MainMenu.json b/client/assets/themes/Panel Attack Modern/scenes/MainMenu.json new file mode 100644 index 00000000..143ba9f6 --- /dev/null +++ b/client/assets/themes/Panel Attack Modern/scenes/MainMenu.json @@ -0,0 +1,266 @@ +{ + "drawables": [ + { + "filePath": "MainMenuCityBackground" + }, + { + "ref": "Stars.json" + }, + { + "filePath": "MainMenuCityFog1", + "position": { + "x": 0, + "y": 10 + }, + "wrap": "repeat", + "animationTracks": [ + { + "loopTrack": true, + "steps": [ + { + "animateProperties": { + "scrollX": 1280 + }, + "durationSeconds": 50, + "easeType": "linear" + } + ] + } + ] + }, + { + "filePath": "MainMenuCityBackBuildings" + }, + { + "ref": "WindowsBack.json" + }, + { + "filePath": "MainMenuCityFog2", + "position": { + "x": 0, + "y": 100 + }, + "wrap": "repeat", + "animationTracks": [ + { + "loopTrack": true, + "steps": [ + { + "animateProperties": { + "scrollX": 1280 + }, + "durationSeconds": 40, + "easeType": "linear" + } + ] + } + ] + }, + { + "filePath": "MainMenuCityMiddleBuildings" + }, + { + "ref": "WindowsMid.json" + }, + { + "id": "Crystal", + "templateOnly": true, + "filePath": "Crystal", + "tint": [ + 0.8, + 0.8, + 1 + ], + "anchor": "center", + "xScale": 0.2, + "yScale": 0.2, + "position": { + "x": 549, + "y": 450 + }, + "animationTracks": [ + { + "yoyo": true, + "steps": [ + { + "animateProperties": { + "y": 456 + }, + "durationSeconds": 4, + "easeType": "quadinout" + } + ] + } + ] + }, + { + "id": "Glyph", + "filePath": "Glyph", + "tint": [ + 0.2, + 0.999, + 0.2 + ], + "anchor": "center", + "xScale": 1, + "yScale": 1, + "position": { + "x": 1106, + "y": 240 + }, + "animationTracks": [ + { + "yoyo": true, + "steps": [ + { + "animateProperties": { + "alpha": 0.8 + }, + "durationSeconds": 4, + "easeType": "quadinout" + } + ] + } + ], + "children": [ + { + "filePath": "GlyphCircle", + "anchor": "center", + "position": { + "x": 21, + "y": 54 + }, + "tint": [ + 0.2, + 0.999, + 0.2 + ], + "animationTracks": [ + { + "loopTrack": true, + "steps": [ + { + "animateProperties": { + "xScale": 1.1, + "yScale": 1.1, + "rotation": 6.28318, + "alpha": 0.8 + }, + "durationSeconds": 2, + "easeType": "linear" + } + ] + } + ] + } + ] + }, + { + "ref": "Crystal", + "overrides": { + "position": { + "x": 201, + "y": 244 + }, + "tint": [ + 0.6, + 0.999, + 0.6 + ], + "animationTracks": [ + { + "steps": [ + { + "animateProperties": { + "y": 240 + } + } + ] + } + ] + } + }, + { + "ref": "Glyph", + "overrides": { + "position": { + "x": 332, + "y": 224 + }, + "tint": [ + 0.6, + 0.6, + 0.999 + ], + "children": [ + { + "tint": [ + 0.6, + 0.6, + 0.999 + ] + } + ], + "animationTracks": [ + { + "steps": [ + { + "animateProperties": {} + } + ] + } + ] + } + }, + { + "filePath": "MainMenuCityFog3", + "position": { + "x": 0, + "y": 200 + }, + "wrap": "repeat", + "animationTracks": [ + { + "loopTrack": true, + "steps": [ + { + "animateProperties": { + "scrollX": 1280 + }, + "durationSeconds": 30, + "easeType": "linear" + } + ] + } + ] + }, + { + "filePath": "MainMenuCityFrontBuildings" + }, + { + "ref": "WindowsFront.json" + }, + { + "filePath": "MainMenuCityFog4", + "position": { + "x": 0, + "y": 300 + }, + "wrap": "repeat", + "animationTracks": [ + { + "loopTrack": true, + "steps": [ + { + "animateProperties": { + "scrollX": 1280 + }, + "durationSeconds": 20, + "easeType": "linear" + } + ] + } + ] + } + ] +} \ No newline at end of file diff --git a/client/assets/themes/Panel Attack Modern/scenes/MainMenuCityBack@2x.png b/client/assets/themes/Panel Attack Modern/scenes/MainMenuCityBack@2x.png new file mode 100644 index 00000000..ebe28092 Binary files /dev/null and b/client/assets/themes/Panel Attack Modern/scenes/MainMenuCityBack@2x.png differ diff --git a/client/assets/themes/Panel Attack Modern/scenes/MainMenuCityBackBuildings@2x.png b/client/assets/themes/Panel Attack Modern/scenes/MainMenuCityBackBuildings@2x.png new file mode 100644 index 00000000..366421b2 Binary files /dev/null and b/client/assets/themes/Panel Attack Modern/scenes/MainMenuCityBackBuildings@2x.png differ diff --git a/client/assets/themes/Panel Attack Modern/scenes/MainMenuCityBackground@2x.png b/client/assets/themes/Panel Attack Modern/scenes/MainMenuCityBackground@2x.png new file mode 100644 index 00000000..d2c90763 Binary files /dev/null and b/client/assets/themes/Panel Attack Modern/scenes/MainMenuCityBackground@2x.png differ diff --git a/client/assets/themes/Panel Attack Modern/scenes/MainMenuCityFog1@2x.png b/client/assets/themes/Panel Attack Modern/scenes/MainMenuCityFog1@2x.png new file mode 100644 index 00000000..b37ee436 Binary files /dev/null and b/client/assets/themes/Panel Attack Modern/scenes/MainMenuCityFog1@2x.png differ diff --git a/client/assets/themes/Panel Attack Modern/scenes/MainMenuCityFog2@2x.png b/client/assets/themes/Panel Attack Modern/scenes/MainMenuCityFog2@2x.png new file mode 100644 index 00000000..b7e1d3a8 Binary files /dev/null and b/client/assets/themes/Panel Attack Modern/scenes/MainMenuCityFog2@2x.png differ diff --git a/client/assets/themes/Panel Attack Modern/scenes/MainMenuCityFog3@2x.png b/client/assets/themes/Panel Attack Modern/scenes/MainMenuCityFog3@2x.png new file mode 100644 index 00000000..78ce0c31 Binary files /dev/null and b/client/assets/themes/Panel Attack Modern/scenes/MainMenuCityFog3@2x.png differ diff --git a/client/assets/themes/Panel Attack Modern/scenes/MainMenuCityFog4@2x.png b/client/assets/themes/Panel Attack Modern/scenes/MainMenuCityFog4@2x.png new file mode 100644 index 00000000..6cadc551 Binary files /dev/null and b/client/assets/themes/Panel Attack Modern/scenes/MainMenuCityFog4@2x.png differ diff --git a/client/assets/themes/Panel Attack Modern/scenes/MainMenuCityFog@2x.png b/client/assets/themes/Panel Attack Modern/scenes/MainMenuCityFog@2x.png new file mode 100644 index 00000000..61521a8d Binary files /dev/null and b/client/assets/themes/Panel Attack Modern/scenes/MainMenuCityFog@2x.png differ diff --git a/client/assets/themes/Panel Attack Modern/scenes/MainMenuCityFront@2x.png b/client/assets/themes/Panel Attack Modern/scenes/MainMenuCityFront@2x.png new file mode 100644 index 00000000..e74bfefc Binary files /dev/null and b/client/assets/themes/Panel Attack Modern/scenes/MainMenuCityFront@2x.png differ diff --git a/client/assets/themes/Panel Attack Modern/scenes/MainMenuCityFrontBuildings@2x.png b/client/assets/themes/Panel Attack Modern/scenes/MainMenuCityFrontBuildings@2x.png new file mode 100644 index 00000000..acb361fd Binary files /dev/null and b/client/assets/themes/Panel Attack Modern/scenes/MainMenuCityFrontBuildings@2x.png differ diff --git a/client/assets/themes/Panel Attack Modern/scenes/MainMenuCityMiddle@2x.png b/client/assets/themes/Panel Attack Modern/scenes/MainMenuCityMiddle@2x.png new file mode 100644 index 00000000..f82561a5 Binary files /dev/null and b/client/assets/themes/Panel Attack Modern/scenes/MainMenuCityMiddle@2x.png differ diff --git a/client/assets/themes/Panel Attack Modern/scenes/MainMenuCityMiddleBuildings@2x.png b/client/assets/themes/Panel Attack Modern/scenes/MainMenuCityMiddleBuildings@2x.png new file mode 100644 index 00000000..4824a561 Binary files /dev/null and b/client/assets/themes/Panel Attack Modern/scenes/MainMenuCityMiddleBuildings@2x.png differ diff --git a/client/assets/themes/Panel Attack Modern/scenes/PanelAttackLogoLayer0@2x.png b/client/assets/themes/Panel Attack Modern/scenes/PanelAttackLogoLayer0@2x.png new file mode 100644 index 00000000..a44e5766 Binary files /dev/null and b/client/assets/themes/Panel Attack Modern/scenes/PanelAttackLogoLayer0@2x.png differ diff --git a/client/assets/themes/Panel Attack Modern/scenes/PanelAttackLogoLayer2@2x.png b/client/assets/themes/Panel Attack Modern/scenes/PanelAttackLogoLayer2@2x.png new file mode 100644 index 00000000..8adb69a0 Binary files /dev/null and b/client/assets/themes/Panel Attack Modern/scenes/PanelAttackLogoLayer2@2x.png differ diff --git a/client/assets/themes/Panel Attack Modern/scenes/PanelAttackLogoLayer4@2x.png b/client/assets/themes/Panel Attack Modern/scenes/PanelAttackLogoLayer4@2x.png new file mode 100644 index 00000000..57f40377 Binary files /dev/null and b/client/assets/themes/Panel Attack Modern/scenes/PanelAttackLogoLayer4@2x.png differ diff --git a/client/assets/themes/Panel Attack Modern/scenes/SingleWindowBackground.png b/client/assets/themes/Panel Attack Modern/scenes/SingleWindowBackground.png new file mode 100644 index 00000000..6c0c2a3c Binary files /dev/null and b/client/assets/themes/Panel Attack Modern/scenes/SingleWindowBackground.png differ diff --git a/client/assets/themes/Panel Attack Modern/scenes/SingleWindowFrame.png b/client/assets/themes/Panel Attack Modern/scenes/SingleWindowFrame.png new file mode 100644 index 00000000..f04508eb Binary files /dev/null and b/client/assets/themes/Panel Attack Modern/scenes/SingleWindowFrame.png differ diff --git a/client/assets/themes/Panel Attack Modern/scenes/SingleWindowGlass.png b/client/assets/themes/Panel Attack Modern/scenes/SingleWindowGlass.png new file mode 100644 index 00000000..3c9cc1cf Binary files /dev/null and b/client/assets/themes/Panel Attack Modern/scenes/SingleWindowGlass.png differ diff --git a/client/assets/themes/Panel Attack Modern/scenes/SingleWindowGlow.png b/client/assets/themes/Panel Attack Modern/scenes/SingleWindowGlow.png new file mode 100644 index 00000000..c9491da7 Binary files /dev/null and b/client/assets/themes/Panel Attack Modern/scenes/SingleWindowGlow.png differ diff --git a/client/assets/themes/Panel Attack Modern/scenes/SingleWindowLight.png b/client/assets/themes/Panel Attack Modern/scenes/SingleWindowLight.png new file mode 100644 index 00000000..0ba93191 Binary files /dev/null and b/client/assets/themes/Panel Attack Modern/scenes/SingleWindowLight.png differ diff --git a/client/assets/themes/Panel Attack Modern/scenes/Star@2x.png b/client/assets/themes/Panel Attack Modern/scenes/Star@2x.png new file mode 100644 index 00000000..df3db675 Binary files /dev/null and b/client/assets/themes/Panel Attack Modern/scenes/Star@2x.png differ diff --git a/client/assets/themes/Panel Attack Modern/scenes/Stars.json b/client/assets/themes/Panel Attack Modern/scenes/Stars.json new file mode 100644 index 00000000..100f2fff --- /dev/null +++ b/client/assets/themes/Panel Attack Modern/scenes/Stars.json @@ -0,0 +1,6647 @@ +{ + "drawables": [ + { + "id": "Star", + "filePath": "Star", + "position": { + "x": 100, + "y": 100 + }, + "xScale": 1.0, + "yScale": 1.0, + "alpha": 0.4, + "animationTracks": [ + { + "loopTrack": true, + "steps": [ + { + "animateProperties": { + "alpha": 1, + "xScale": 1.05, + "yScale": 1.05 + }, + "durationSeconds": 0.18, + "easeType": "quadinout" + }, + { + "animateProperties": { + "alpha": 0.4, + "xScale": 1.0, + "yScale": 1.0 + }, + "durationSeconds": 0.18, + "easeType": "quadinout" + }, + { + "animateProperties": { + "alpha": 0.4 + }, + "durationSeconds": 8.0, + "easeType": "linear" + } + ] + } + ] + }, + { + "ref": "Star", + "overrides": { + "position": { + "x": 228, + "y": 12 + }, + "xScale": 0.76, + "yScale": 0.76, + "alpha": 0.4, + "rotation": 0.005, + "tint": [ + 0.8, + 0.9, + 1.0 + ], + "animationTracks": [ + { + "steps": [ + { + "animateProperties": { + "xScale": 0.79, + "yScale": 0.79, + "alpha": 0.46 + } + }, + { + "animateProperties": { + "xScale": 0.76, + "yScale": 0.76, + "alpha": 0.4 + } + }, + { + "animateProperties": { + "alpha": 0.4 + }, + "durationSeconds": 20.59 + } + ] + } + ] + } + }, + { + "ref": "Star", + "overrides": { + "position": { + "x": 1209, + "y": 216 + }, + "xScale": 0.27, + "yScale": 0.27, + "alpha": 0.29, + "rotation": 0.022, + "tint": [ + 0.8, + 0.9, + 1.0 + ], + "animationTracks": [ + { + "steps": [ + { + "animateProperties": { + "xScale": 0.28, + "yScale": 0.28, + "alpha": 0.72 + } + }, + { + "animateProperties": { + "xScale": 0.27, + "yScale": 0.27, + "alpha": 0.29 + } + }, + { + "animateProperties": { + "alpha": 0.29 + }, + "durationSeconds": 17.54 + } + ] + } + ] + } + }, + { + "ref": "Star", + "overrides": { + "position": { + "x": 1116, + "y": 214 + }, + "xScale": 0.37, + "yScale": 0.37, + "alpha": 0.37, + "rotation": 0.021, + "tint": [ + 0.8, + 0.9, + 1.0 + ], + "animationTracks": [ + { + "steps": [ + { + "animateProperties": { + "xScale": 0.39, + "yScale": 0.39, + "alpha": 0.37 + } + }, + { + "animateProperties": { + "xScale": 0.37, + "yScale": 0.37, + "alpha": 0.37 + } + }, + { + "animateProperties": { + "alpha": 0.37 + }, + "durationSeconds": 21.7 + } + ] + } + ] + } + }, + { + "ref": "Star", + "overrides": { + "position": { + "x": 696, + "y": 142 + }, + "xScale": 0.34, + "yScale": 0.34, + "alpha": 0.32, + "rotation": 0.036, + "tint": [ + 0.8, + 0.9, + 1.0 + ], + "animationTracks": [ + { + "steps": [ + { + "animateProperties": { + "xScale": 0.37, + "yScale": 0.37, + "alpha": 0.38 + } + }, + { + "animateProperties": { + "xScale": 0.34, + "yScale": 0.34, + "alpha": 0.32 + } + }, + { + "animateProperties": { + "alpha": 0.32 + }, + "durationSeconds": 9.64 + } + ] + } + ] + } + }, + { + "ref": "Star", + "overrides": { + "position": { + "x": 1236, + "y": 135 + }, + "xScale": 0.81, + "yScale": 0.81, + "alpha": 0.46, + "rotation": 0.005, + "tint": [ + 0.8, + 0.9, + 1.0 + ], + "animationTracks": [ + { + "steps": [ + { + "animateProperties": { + "xScale": 0.86, + "yScale": 0.86, + "alpha": 0.99 + } + }, + { + "animateProperties": { + "xScale": 0.81, + "yScale": 0.81, + "alpha": 0.46 + } + }, + { + "animateProperties": { + "alpha": 0.46 + }, + "durationSeconds": 14.44 + } + ] + } + ] + } + }, + { + "ref": "Star", + "overrides": { + "position": { + "x": 1266, + "y": 185 + }, + "xScale": 0.62, + "yScale": 0.62, + "alpha": 0.36, + "rotation": -0.044, + "tint": [ + 0.8, + 0.9, + 1.0 + ], + "animationTracks": [ + { + "steps": [ + { + "animateProperties": { + "xScale": 0.66, + "yScale": 0.66, + "alpha": 0.51 + } + }, + { + "animateProperties": { + "xScale": 0.62, + "yScale": 0.62, + "alpha": 0.36 + } + }, + { + "animateProperties": { + "alpha": 0.36 + }, + "durationSeconds": 12.92 + } + ] + } + ] + } + }, + { + "ref": "Star", + "overrides": { + "position": { + "x": 476, + "y": 51 + }, + "xScale": 0.48, + "yScale": 0.48, + "alpha": 0.4, + "rotation": 0.018, + "tint": [ + 0.8, + 0.9, + 1.0 + ], + "animationTracks": [ + { + "steps": [ + { + "animateProperties": { + "xScale": 0.51, + "yScale": 0.51, + "alpha": 0.5 + } + }, + { + "animateProperties": { + "xScale": 0.48, + "yScale": 0.48, + "alpha": 0.4 + } + }, + { + "animateProperties": { + "alpha": 0.4 + }, + "durationSeconds": 14.04 + } + ] + } + ] + } + }, + { + "ref": "Star", + "overrides": { + "position": { + "x": 146, + "y": 311 + }, + "xScale": 0.67, + "yScale": 0.67, + "alpha": 0.39, + "rotation": 0.044, + "tint": [ + 0.8, + 0.9, + 1.0 + ], + "animationTracks": [ + { + "steps": [ + { + "animateProperties": { + "xScale": 0.71, + "yScale": 0.71, + "alpha": 0.67 + } + }, + { + "animateProperties": { + "xScale": 0.67, + "yScale": 0.67, + "alpha": 0.39 + } + }, + { + "animateProperties": { + "alpha": 0.39 + }, + "durationSeconds": 12.59 + } + ] + } + ] + } + }, + { + "ref": "Star", + "overrides": { + "position": { + "x": 1140, + "y": 112 + }, + "xScale": 0.71, + "yScale": 0.71, + "alpha": 0.46, + "rotation": -0.019, + "tint": [ + 0.8, + 0.9, + 1.0 + ], + "animationTracks": [ + { + "steps": [ + { + "animateProperties": { + "xScale": 0.76, + "yScale": 0.76, + "alpha": 0.58 + } + }, + { + "animateProperties": { + "xScale": 0.71, + "yScale": 0.71, + "alpha": 0.46 + } + }, + { + "animateProperties": { + "alpha": 0.46 + }, + "durationSeconds": 8.55 + } + ] + } + ] + } + }, + { + "ref": "Star", + "overrides": { + "position": { + "x": 548, + "y": 33 + }, + "xScale": 0.37, + "yScale": 0.37, + "alpha": 0.38, + "rotation": -0.011, + "tint": [ + 0.8, + 0.9, + 1.0 + ], + "animationTracks": [ + { + "steps": [ + { + "animateProperties": { + "xScale": 0.4, + "yScale": 0.4, + "alpha": 0.58 + } + }, + { + "animateProperties": { + "xScale": 0.37, + "yScale": 0.37, + "alpha": 0.38 + } + }, + { + "animateProperties": { + "alpha": 0.38 + }, + "durationSeconds": 19.14 + } + ] + } + ] + } + }, + { + "ref": "Star", + "overrides": { + "position": { + "x": 939, + "y": 73 + }, + "xScale": 0.4, + "yScale": 0.4, + "alpha": 0.36, + "rotation": 0.041, + "tint": [ + 0.8, + 0.9, + 1.0 + ], + "animationTracks": [ + { + "steps": [ + { + "animateProperties": { + "xScale": 0.42, + "yScale": 0.42, + "alpha": 0.53 + } + }, + { + "animateProperties": { + "xScale": 0.4, + "yScale": 0.4, + "alpha": 0.36 + } + }, + { + "animateProperties": { + "alpha": 0.36 + }, + "durationSeconds": 17.94 + } + ] + } + ] + } + }, + { + "ref": "Star", + "overrides": { + "position": { + "x": 817, + "y": 185 + }, + "xScale": 0.37, + "yScale": 0.37, + "alpha": 0.34, + "rotation": -0.041, + "tint": [ + 0.8, + 0.9, + 1.0 + ], + "animationTracks": [ + { + "steps": [ + { + "animateProperties": { + "xScale": 0.4, + "yScale": 0.4, + "alpha": 0.4 + } + }, + { + "animateProperties": { + "xScale": 0.37, + "yScale": 0.37, + "alpha": 0.34 + } + }, + { + "animateProperties": { + "alpha": 0.34 + }, + "durationSeconds": 8.8 + } + ] + } + ] + } + }, + { + "ref": "Star", + "overrides": { + "position": { + "x": 327, + "y": 216 + }, + "xScale": 0.64, + "yScale": 0.64, + "alpha": 0.42, + "rotation": 0.006, + "tint": [ + 0.8, + 0.9, + 1.0 + ], + "animationTracks": [ + { + "steps": [ + { + "animateProperties": { + "xScale": 0.67, + "yScale": 0.67, + "alpha": 0.69 + } + }, + { + "animateProperties": { + "xScale": 0.64, + "yScale": 0.64, + "alpha": 0.42 + } + }, + { + "animateProperties": { + "alpha": 0.42 + }, + "durationSeconds": 12.27 + } + ] + } + ] + } + }, + { + "ref": "Star", + "overrides": { + "position": { + "x": 23, + "y": 58 + }, + "xScale": 0.71, + "yScale": 0.71, + "alpha": 0.4, + "rotation": -0.007, + "tint": [ + 0.8, + 0.9, + 1.0 + ], + "animationTracks": [ + { + "steps": [ + { + "animateProperties": { + "xScale": 0.75, + "yScale": 0.75, + "alpha": 0.78 + } + }, + { + "animateProperties": { + "xScale": 0.71, + "yScale": 0.71, + "alpha": 0.4 + } + }, + { + "animateProperties": { + "alpha": 0.4 + }, + "durationSeconds": 9.9 + } + ] + } + ] + } + }, + { + "ref": "Star", + "overrides": { + "position": { + "x": 929, + "y": 1 + }, + "xScale": 0.96, + "yScale": 0.96, + "alpha": 0.47, + "rotation": 0.043, + "tint": [ + 0.8, + 0.9, + 1.0 + ], + "animationTracks": [ + { + "steps": [ + { + "animateProperties": { + "xScale": 1.03, + "yScale": 1.03, + "alpha": 0.74 + } + }, + { + "animateProperties": { + "xScale": 0.96, + "yScale": 0.96, + "alpha": 0.47 + } + }, + { + "animateProperties": { + "alpha": 0.47 + }, + "durationSeconds": 11.04 + } + ] + } + ] + } + }, + { + "ref": "Star", + "overrides": { + "position": { + "x": 1280, + "y": 152 + }, + "xScale": 0.85, + "yScale": 0.85, + "alpha": 0.43, + "rotation": 0.047, + "tint": [ + 0.8, + 0.9, + 1.0 + ], + "animationTracks": [ + { + "steps": [ + { + "animateProperties": { + "xScale": 0.9, + "yScale": 0.9, + "alpha": 0.64 + } + }, + { + "animateProperties": { + "xScale": 0.85, + "yScale": 0.85, + "alpha": 0.43 + } + }, + { + "animateProperties": { + "alpha": 0.43 + }, + "durationSeconds": 10.75 + } + ] + } + ] + } + }, + { + "ref": "Star", + "overrides": { + "position": { + "x": 1086, + "y": 0 + }, + "xScale": 0.64, + "yScale": 0.64, + "alpha": 0.37, + "rotation": 0.032, + "tint": [ + 0.8, + 0.9, + 1.0 + ], + "animationTracks": [ + { + "steps": [ + { + "animateProperties": { + "xScale": 0.67, + "yScale": 0.67, + "alpha": 0.6 + } + }, + { + "animateProperties": { + "xScale": 0.64, + "yScale": 0.64, + "alpha": 0.37 + } + }, + { + "animateProperties": { + "alpha": 0.37 + }, + "durationSeconds": 24.75 + } + ] + } + ] + } + }, + { + "ref": "Star", + "overrides": { + "position": { + "x": 490, + "y": 29 + }, + "xScale": 0.38, + "yScale": 0.38, + "alpha": 0.3, + "rotation": 0.05, + "tint": [ + 0.8, + 0.9, + 1.0 + ], + "animationTracks": [ + { + "steps": [ + { + "animateProperties": { + "xScale": 0.4, + "yScale": 0.4, + "alpha": 0.81 + } + }, + { + "animateProperties": { + "xScale": 0.38, + "yScale": 0.38, + "alpha": 0.3 + } + }, + { + "animateProperties": { + "alpha": 0.3 + }, + "durationSeconds": 21.87 + } + ] + } + ] + } + }, + { + "ref": "Star", + "overrides": { + "position": { + "x": 1090, + "y": 64 + }, + "xScale": 0.31, + "yScale": 0.31, + "alpha": 0.33, + "rotation": -0.008, + "tint": [ + 0.8, + 0.9, + 1.0 + ], + "animationTracks": [ + { + "steps": [ + { + "animateProperties": { + "xScale": 0.33, + "yScale": 0.33, + "alpha": 0.51 + } + }, + { + "animateProperties": { + "xScale": 0.31, + "yScale": 0.31, + "alpha": 0.33 + } + }, + { + "animateProperties": { + "alpha": 0.33 + }, + "durationSeconds": 22.83 + } + ] + } + ] + } + }, + { + "ref": "Star", + "overrides": { + "position": { + "x": 433, + "y": 276 + }, + "xScale": 0.77, + "yScale": 0.77, + "alpha": 0.46, + "rotation": -0.013, + "tint": [ + 0.8, + 0.9, + 1.0 + ], + "animationTracks": [ + { + "steps": [ + { + "animateProperties": { + "xScale": 0.82, + "yScale": 0.82, + "alpha": 0.68 + } + }, + { + "animateProperties": { + "xScale": 0.77, + "yScale": 0.77, + "alpha": 0.46 + } + }, + { + "animateProperties": { + "alpha": 0.46 + }, + "durationSeconds": 19.42 + } + ] + } + ] + } + }, + { + "ref": "Star", + "overrides": { + "position": { + "x": 1059, + "y": 231 + }, + "xScale": 0.31, + "yScale": 0.31, + "alpha": 0.31, + "rotation": -0.029, + "tint": [ + 0.8, + 0.9, + 1.0 + ], + "animationTracks": [ + { + "steps": [ + { + "animateProperties": { + "xScale": 0.32, + "yScale": 0.32, + "alpha": 0.72 + } + }, + { + "animateProperties": { + "xScale": 0.31, + "yScale": 0.31, + "alpha": 0.31 + } + }, + { + "animateProperties": { + "alpha": 0.31 + }, + "durationSeconds": 11.91 + } + ] + } + ] + } + }, + { + "ref": "Star", + "overrides": { + "position": { + "x": 145, + "y": 30 + }, + "xScale": 0.37, + "yScale": 0.37, + "alpha": 0.38, + "rotation": 0.018, + "tint": [ + 0.8, + 0.9, + 1.0 + ], + "animationTracks": [ + { + "steps": [ + { + "animateProperties": { + "xScale": 0.4, + "yScale": 0.4, + "alpha": 0.42 + } + }, + { + "animateProperties": { + "xScale": 0.37, + "yScale": 0.37, + "alpha": 0.38 + } + }, + { + "animateProperties": { + "alpha": 0.38 + }, + "durationSeconds": 12.05 + } + ] + } + ] + } + }, + { + "ref": "Star", + "overrides": { + "position": { + "x": 438, + "y": 276 + }, + "xScale": 0.32, + "yScale": 0.32, + "alpha": 0.34, + "rotation": 0.032, + "tint": [ + 0.8, + 0.9, + 1.0 + ], + "animationTracks": [ + { + "steps": [ + { + "animateProperties": { + "xScale": 0.34, + "yScale": 0.34, + "alpha": 0.65 + } + }, + { + "animateProperties": { + "xScale": 0.32, + "yScale": 0.32, + "alpha": 0.34 + } + }, + { + "animateProperties": { + "alpha": 0.34 + }, + "durationSeconds": 21.34 + } + ] + } + ] + } + }, + { + "ref": "Star", + "overrides": { + "position": { + "x": 389, + "y": 48 + }, + "xScale": 0.3, + "yScale": 0.3, + "alpha": 0.32, + "rotation": 0.018, + "tint": [ + 0.8, + 0.9, + 1.0 + ], + "animationTracks": [ + { + "steps": [ + { + "animateProperties": { + "xScale": 0.32, + "yScale": 0.32, + "alpha": 0.64 + } + }, + { + "animateProperties": { + "xScale": 0.3, + "yScale": 0.3, + "alpha": 0.32 + } + }, + { + "animateProperties": { + "alpha": 0.32 + }, + "durationSeconds": 20.39 + } + ] + } + ] + } + }, + { + "ref": "Star", + "overrides": { + "position": { + "x": 201, + "y": 31 + }, + "xScale": 0.49, + "yScale": 0.49, + "alpha": 0.41, + "rotation": -0.005, + "tint": [ + 0.8, + 0.9, + 1.0 + ], + "animationTracks": [ + { + "steps": [ + { + "animateProperties": { + "xScale": 0.51, + "yScale": 0.51, + "alpha": 0.56 + } + }, + { + "animateProperties": { + "xScale": 0.49, + "yScale": 0.49, + "alpha": 0.41 + } + }, + { + "animateProperties": { + "alpha": 0.41 + }, + "durationSeconds": 11.23 + } + ] + } + ] + } + }, + { + "ref": "Star", + "overrides": { + "position": { + "x": 864, + "y": 93 + }, + "xScale": 0.41, + "yScale": 0.41, + "alpha": 0.39, + "rotation": 0.005, + "tint": [ + 0.8, + 0.9, + 1.0 + ], + "animationTracks": [ + { + "steps": [ + { + "animateProperties": { + "xScale": 0.43, + "yScale": 0.43, + "alpha": 0.66 + } + }, + { + "animateProperties": { + "xScale": 0.41, + "yScale": 0.41, + "alpha": 0.39 + } + }, + { + "animateProperties": { + "alpha": 0.39 + }, + "durationSeconds": 22.64 + } + ] + } + ] + } + }, + { + "ref": "Star", + "overrides": { + "position": { + "x": 103, + "y": 276 + }, + "xScale": 0.85, + "yScale": 0.85, + "alpha": 0.51, + "rotation": -0.001, + "tint": [ + 0.8, + 0.9, + 1.0 + ], + "animationTracks": [ + { + "steps": [ + { + "animateProperties": { + "xScale": 0.92, + "yScale": 0.92, + "alpha": 0.93 + } + }, + { + "animateProperties": { + "xScale": 0.85, + "yScale": 0.85, + "alpha": 0.51 + } + }, + { + "animateProperties": { + "alpha": 0.51 + }, + "durationSeconds": 10.83 + } + ] + } + ] + } + }, + { + "ref": "Star", + "overrides": { + "position": { + "x": 437, + "y": 205 + }, + "xScale": 0.9, + "yScale": 0.9, + "alpha": 0.43, + "rotation": 0.03, + "tint": [ + 0.8, + 0.9, + 1.0 + ], + "animationTracks": [ + { + "steps": [ + { + "animateProperties": { + "xScale": 0.93, + "yScale": 0.93, + "alpha": 0.65 + } + }, + { + "animateProperties": { + "xScale": 0.9, + "yScale": 0.9, + "alpha": 0.43 + } + }, + { + "animateProperties": { + "alpha": 0.43 + }, + "durationSeconds": 23.75 + } + ] + } + ] + } + }, + { + "ref": "Star", + "overrides": { + "position": { + "x": 584, + "y": 216 + }, + "xScale": 0.72, + "yScale": 0.72, + "alpha": 0.46, + "rotation": -0.032, + "tint": [ + 0.8, + 0.9, + 1.0 + ], + "animationTracks": [ + { + "steps": [ + { + "animateProperties": { + "xScale": 0.77, + "yScale": 0.77, + "alpha": 0.82 + } + }, + { + "animateProperties": { + "xScale": 0.72, + "yScale": 0.72, + "alpha": 0.46 + } + }, + { + "animateProperties": { + "alpha": 0.46 + }, + "durationSeconds": 16.27 + } + ] + } + ] + } + }, + { + "ref": "Star", + "overrides": { + "position": { + "x": 445, + "y": 29 + }, + "xScale": 0.62, + "yScale": 0.62, + "alpha": 0.43, + "rotation": 0.0, + "tint": [ + 0.8, + 0.9, + 1.0 + ], + "animationTracks": [ + { + "steps": [ + { + "animateProperties": { + "xScale": 0.66, + "yScale": 0.66, + "alpha": 0.46 + } + }, + { + "animateProperties": { + "xScale": 0.62, + "yScale": 0.62, + "alpha": 0.43 + } + }, + { + "animateProperties": { + "alpha": 0.43 + }, + "durationSeconds": 17.93 + } + ] + } + ] + } + }, + { + "ref": "Star", + "overrides": { + "position": { + "x": 1087, + "y": 80 + }, + "xScale": 0.28, + "yScale": 0.28, + "alpha": 0.36, + "rotation": 0.038, + "tint": [ + 0.8, + 0.9, + 1.0 + ], + "animationTracks": [ + { + "steps": [ + { + "animateProperties": { + "xScale": 0.3, + "yScale": 0.3, + "alpha": 0.4 + } + }, + { + "animateProperties": { + "xScale": 0.28, + "yScale": 0.28, + "alpha": 0.36 + } + }, + { + "animateProperties": { + "alpha": 0.36 + }, + "durationSeconds": 9.16 + } + ] + } + ] + } + }, + { + "ref": "Star", + "overrides": { + "position": { + "x": 826, + "y": 61 + }, + "xScale": 0.94, + "yScale": 0.94, + "alpha": 0.49, + "rotation": 0.016, + "tint": [ + 0.8, + 0.9, + 1.0 + ], + "animationTracks": [ + { + "steps": [ + { + "animateProperties": { + "xScale": 0.99, + "yScale": 0.99, + "alpha": 0.51 + } + }, + { + "animateProperties": { + "xScale": 0.94, + "yScale": 0.94, + "alpha": 0.49 + } + }, + { + "animateProperties": { + "alpha": 0.49 + }, + "durationSeconds": 9.39 + } + ] + } + ] + } + }, + { + "ref": "Star", + "overrides": { + "position": { + "x": 1157, + "y": 267 + }, + "xScale": 0.44, + "yScale": 0.44, + "alpha": 0.38, + "rotation": -0.038, + "tint": [ + 0.8, + 0.9, + 1.0 + ], + "animationTracks": [ + { + "steps": [ + { + "animateProperties": { + "xScale": 0.46, + "yScale": 0.46, + "alpha": 0.57 + } + }, + { + "animateProperties": { + "xScale": 0.44, + "yScale": 0.44, + "alpha": 0.38 + } + }, + { + "animateProperties": { + "alpha": 0.38 + }, + "durationSeconds": 12.52 + } + ] + } + ] + } + }, + { + "ref": "Star", + "overrides": { + "position": { + "x": 614, + "y": 234 + }, + "xScale": 0.44, + "yScale": 0.44, + "alpha": 0.32, + "rotation": 0.052, + "tint": [ + 0.8, + 0.9, + 1.0 + ], + "animationTracks": [ + { + "steps": [ + { + "animateProperties": { + "xScale": 0.47, + "yScale": 0.47, + "alpha": 0.63 + } + }, + { + "animateProperties": { + "xScale": 0.44, + "yScale": 0.44, + "alpha": 0.32 + } + }, + { + "animateProperties": { + "alpha": 0.32 + }, + "durationSeconds": 24.97 + } + ] + } + ] + } + }, + { + "ref": "Star", + "overrides": { + "position": { + "x": 150, + "y": 275 + }, + "xScale": 0.37, + "yScale": 0.37, + "alpha": 0.39, + "rotation": -0.014, + "tint": [ + 0.8, + 0.9, + 1.0 + ], + "animationTracks": [ + { + "steps": [ + { + "animateProperties": { + "xScale": 0.39, + "yScale": 0.39, + "alpha": 0.93 + } + }, + { + "animateProperties": { + "xScale": 0.37, + "yScale": 0.37, + "alpha": 0.39 + } + }, + { + "animateProperties": { + "alpha": 0.39 + }, + "durationSeconds": 22.95 + } + ] + } + ] + } + }, + { + "ref": "Star", + "overrides": { + "position": { + "x": 323, + "y": 224 + }, + "xScale": 0.85, + "yScale": 0.85, + "alpha": 0.47, + "rotation": -0.051, + "tint": [ + 0.8, + 0.9, + 1.0 + ], + "animationTracks": [ + { + "steps": [ + { + "animateProperties": { + "xScale": 0.91, + "yScale": 0.91, + "alpha": 0.99 + } + }, + { + "animateProperties": { + "xScale": 0.85, + "yScale": 0.85, + "alpha": 0.47 + } + }, + { + "animateProperties": { + "alpha": 0.47 + }, + "durationSeconds": 19.12 + } + ] + } + ] + } + }, + { + "ref": "Star", + "overrides": { + "position": { + "x": 1135, + "y": 153 + }, + "xScale": 0.94, + "yScale": 0.94, + "alpha": 0.52, + "rotation": 0.025, + "tint": [ + 0.8, + 0.9, + 1.0 + ], + "animationTracks": [ + { + "steps": [ + { + "animateProperties": { + "xScale": 0.97, + "yScale": 0.97, + "alpha": 0.65 + } + }, + { + "animateProperties": { + "xScale": 0.94, + "yScale": 0.94, + "alpha": 0.52 + } + }, + { + "animateProperties": { + "alpha": 0.52 + }, + "durationSeconds": 23.13 + } + ] + } + ] + } + }, + { + "ref": "Star", + "overrides": { + "position": { + "x": 318, + "y": 139 + }, + "xScale": 0.41, + "yScale": 0.41, + "alpha": 0.34, + "rotation": 0.001, + "tint": [ + 0.8, + 0.9, + 1.0 + ], + "animationTracks": [ + { + "steps": [ + { + "animateProperties": { + "xScale": 0.43, + "yScale": 0.43, + "alpha": 0.79 + } + }, + { + "animateProperties": { + "xScale": 0.41, + "yScale": 0.41, + "alpha": 0.34 + } + }, + { + "animateProperties": { + "alpha": 0.34 + }, + "durationSeconds": 22.5 + } + ] + } + ] + } + }, + { + "ref": "Star", + "overrides": { + "position": { + "x": 514, + "y": 26 + }, + "xScale": 0.3, + "yScale": 0.3, + "alpha": 0.3, + "rotation": 0.014, + "tint": [ + 0.8, + 0.9, + 1.0 + ], + "animationTracks": [ + { + "steps": [ + { + "animateProperties": { + "xScale": 0.32, + "yScale": 0.32, + "alpha": 0.3 + } + }, + { + "animateProperties": { + "xScale": 0.3, + "yScale": 0.3, + "alpha": 0.3 + } + }, + { + "animateProperties": { + "alpha": 0.3 + }, + "durationSeconds": 21.11 + } + ] + } + ] + } + }, + { + "ref": "Star", + "overrides": { + "position": { + "x": 536, + "y": 82 + }, + "xScale": 0.76, + "yScale": 0.76, + "alpha": 0.43, + "rotation": 0.04, + "tint": [ + 0.8, + 0.9, + 1.0 + ], + "animationTracks": [ + { + "steps": [ + { + "animateProperties": { + "xScale": 0.8, + "yScale": 0.8, + "alpha": 0.44 + } + }, + { + "animateProperties": { + "xScale": 0.76, + "yScale": 0.76, + "alpha": 0.43 + } + }, + { + "animateProperties": { + "alpha": 0.43 + }, + "durationSeconds": 9.28 + } + ] + } + ] + } + }, + { + "ref": "Star", + "overrides": { + "position": { + "x": 305, + "y": 279 + }, + "xScale": 0.27, + "yScale": 0.27, + "alpha": 0.32, + "rotation": -0.014, + "tint": [ + 0.8, + 0.9, + 1.0 + ], + "animationTracks": [ + { + "steps": [ + { + "animateProperties": { + "xScale": 0.28, + "yScale": 0.28, + "alpha": 0.61 + } + }, + { + "animateProperties": { + "xScale": 0.27, + "yScale": 0.27, + "alpha": 0.32 + } + }, + { + "animateProperties": { + "alpha": 0.32 + }, + "durationSeconds": 8.71 + } + ] + } + ] + } + }, + { + "ref": "Star", + "overrides": { + "position": { + "x": 81, + "y": 183 + }, + "xScale": 0.37, + "yScale": 0.37, + "alpha": 0.3, + "rotation": -0.01, + "tint": [ + 0.8, + 0.9, + 1.0 + ], + "animationTracks": [ + { + "steps": [ + { + "animateProperties": { + "xScale": 0.39, + "yScale": 0.39, + "alpha": 0.85 + } + }, + { + "animateProperties": { + "xScale": 0.37, + "yScale": 0.37, + "alpha": 0.3 + } + }, + { + "animateProperties": { + "alpha": 0.3 + }, + "durationSeconds": 23.03 + } + ] + } + ] + } + }, + { + "ref": "Star", + "overrides": { + "position": { + "x": 1271, + "y": 79 + }, + "xScale": 0.92, + "yScale": 0.92, + "alpha": 0.45, + "rotation": -0.009, + "tint": [ + 0.8, + 0.9, + 1.0 + ], + "animationTracks": [ + { + "steps": [ + { + "animateProperties": { + "xScale": 0.96, + "yScale": 0.96, + "alpha": 0.89 + } + }, + { + "animateProperties": { + "xScale": 0.92, + "yScale": 0.92, + "alpha": 0.45 + } + }, + { + "animateProperties": { + "alpha": 0.45 + }, + "durationSeconds": 11.01 + } + ] + } + ] + } + }, + { + "ref": "Star", + "overrides": { + "position": { + "x": 367, + "y": 170 + }, + "xScale": 0.79, + "yScale": 0.79, + "alpha": 0.46, + "rotation": -0.035, + "tint": [ + 0.8, + 0.9, + 1.0 + ], + "animationTracks": [ + { + "steps": [ + { + "animateProperties": { + "xScale": 0.83, + "yScale": 0.83, + "alpha": 0.86 + } + }, + { + "animateProperties": { + "xScale": 0.79, + "yScale": 0.79, + "alpha": 0.46 + } + }, + { + "animateProperties": { + "alpha": 0.46 + }, + "durationSeconds": 12.22 + } + ] + } + ] + } + }, + { + "ref": "Star", + "overrides": { + "position": { + "x": 221, + "y": 195 + }, + "xScale": 0.88, + "yScale": 0.88, + "alpha": 0.44, + "rotation": -0.02, + "tint": [ + 0.8, + 0.9, + 1.0 + ], + "animationTracks": [ + { + "steps": [ + { + "animateProperties": { + "xScale": 0.94, + "yScale": 0.94, + "alpha": 0.9 + } + }, + { + "animateProperties": { + "xScale": 0.88, + "yScale": 0.88, + "alpha": 0.44 + } + }, + { + "animateProperties": { + "alpha": 0.44 + }, + "durationSeconds": 15.83 + } + ] + } + ] + } + }, + { + "ref": "Star", + "overrides": { + "position": { + "x": 466, + "y": 114 + }, + "xScale": 0.26, + "yScale": 0.26, + "alpha": 0.3, + "rotation": -0.023, + "tint": [ + 0.8, + 0.9, + 1.0 + ], + "animationTracks": [ + { + "steps": [ + { + "animateProperties": { + "xScale": 0.27, + "yScale": 0.27, + "alpha": 0.91 + } + }, + { + "animateProperties": { + "xScale": 0.26, + "yScale": 0.26, + "alpha": 0.3 + } + }, + { + "animateProperties": { + "alpha": 0.3 + }, + "durationSeconds": 24.44 + } + ] + } + ] + } + }, + { + "ref": "Star", + "overrides": { + "position": { + "x": 1043, + "y": 204 + }, + "xScale": 0.71, + "yScale": 0.71, + "alpha": 0.41, + "rotation": -0.025, + "tint": [ + 0.8, + 0.9, + 1.0 + ], + "animationTracks": [ + { + "steps": [ + { + "animateProperties": { + "xScale": 0.76, + "yScale": 0.76, + "alpha": 0.43 + } + }, + { + "animateProperties": { + "xScale": 0.71, + "yScale": 0.71, + "alpha": 0.41 + } + }, + { + "animateProperties": { + "alpha": 0.41 + }, + "durationSeconds": 22.91 + } + ] + } + ] + } + }, + { + "ref": "Star", + "overrides": { + "position": { + "x": 1189, + "y": 135 + }, + "xScale": 0.27, + "yScale": 0.27, + "alpha": 0.3, + "rotation": 0.05, + "tint": [ + 0.8, + 0.9, + 1.0 + ], + "animationTracks": [ + { + "steps": [ + { + "animateProperties": { + "xScale": 0.29, + "yScale": 0.29, + "alpha": 0.85 + } + }, + { + "animateProperties": { + "xScale": 0.27, + "yScale": 0.27, + "alpha": 0.3 + } + }, + { + "animateProperties": { + "alpha": 0.3 + }, + "durationSeconds": 15.42 + } + ] + } + ] + } + }, + { + "ref": "Star", + "overrides": { + "position": { + "x": 236, + "y": 197 + }, + "xScale": 0.9, + "yScale": 0.9, + "alpha": 0.43, + "rotation": 0.032, + "tint": [ + 0.8, + 0.9, + 1.0 + ], + "animationTracks": [ + { + "steps": [ + { + "animateProperties": { + "xScale": 0.94, + "yScale": 0.94, + "alpha": 0.68 + } + }, + { + "animateProperties": { + "xScale": 0.9, + "yScale": 0.9, + "alpha": 0.43 + } + }, + { + "animateProperties": { + "alpha": 0.43 + }, + "durationSeconds": 16.84 + } + ] + } + ] + } + }, + { + "ref": "Star", + "overrides": { + "position": { + "x": 403, + "y": 186 + }, + "xScale": 0.52, + "yScale": 0.52, + "alpha": 0.42, + "rotation": -0.039, + "tint": [ + 0.8, + 0.9, + 1.0 + ], + "animationTracks": [ + { + "steps": [ + { + "animateProperties": { + "xScale": 0.56, + "yScale": 0.56, + "alpha": 0.78 + } + }, + { + "animateProperties": { + "xScale": 0.52, + "yScale": 0.52, + "alpha": 0.42 + } + }, + { + "animateProperties": { + "alpha": 0.42 + }, + "durationSeconds": 19.28 + } + ] + } + ] + } + }, + { + "ref": "Star", + "overrides": { + "position": { + "x": 615, + "y": 259 + }, + "xScale": 0.42, + "yScale": 0.42, + "alpha": 0.35, + "rotation": -0.008, + "tint": [ + 0.8, + 0.9, + 1.0 + ], + "animationTracks": [ + { + "steps": [ + { + "animateProperties": { + "xScale": 0.44, + "yScale": 0.44, + "alpha": 0.54 + } + }, + { + "animateProperties": { + "xScale": 0.42, + "yScale": 0.42, + "alpha": 0.35 + } + }, + { + "animateProperties": { + "alpha": 0.35 + }, + "durationSeconds": 10.16 + } + ] + } + ] + } + }, + { + "ref": "Star", + "overrides": { + "position": { + "x": 776, + "y": 89 + }, + "xScale": 0.66, + "yScale": 0.66, + "alpha": 0.42, + "rotation": -0.007, + "tint": [ + 0.8, + 0.9, + 1.0 + ], + "animationTracks": [ + { + "steps": [ + { + "animateProperties": { + "xScale": 0.69, + "yScale": 0.69, + "alpha": 0.42 + } + }, + { + "animateProperties": { + "xScale": 0.66, + "yScale": 0.66, + "alpha": 0.42 + } + }, + { + "animateProperties": { + "alpha": 0.42 + }, + "durationSeconds": 12.88 + } + ] + } + ] + } + }, + { + "ref": "Star", + "overrides": { + "position": { + "x": 1187, + "y": 310 + }, + "xScale": 0.69, + "yScale": 0.69, + "alpha": 0.42, + "rotation": 0.042, + "tint": [ + 0.8, + 0.9, + 1.0 + ], + "animationTracks": [ + { + "steps": [ + { + "animateProperties": { + "xScale": 0.73, + "yScale": 0.73, + "alpha": 0.54 + } + }, + { + "animateProperties": { + "xScale": 0.69, + "yScale": 0.69, + "alpha": 0.42 + } + }, + { + "animateProperties": { + "alpha": 0.42 + }, + "durationSeconds": 16.04 + } + ] + } + ] + } + }, + { + "ref": "Star", + "overrides": { + "position": { + "x": 347, + "y": 43 + }, + "xScale": 0.41, + "yScale": 0.41, + "alpha": 0.36, + "rotation": -0.028, + "tint": [ + 0.8, + 0.9, + 1.0 + ], + "animationTracks": [ + { + "steps": [ + { + "animateProperties": { + "xScale": 0.44, + "yScale": 0.44, + "alpha": 0.42 + } + }, + { + "animateProperties": { + "xScale": 0.41, + "yScale": 0.41, + "alpha": 0.36 + } + }, + { + "animateProperties": { + "alpha": 0.36 + }, + "durationSeconds": 24.18 + } + ] + } + ] + } + }, + { + "ref": "Star", + "overrides": { + "position": { + "x": 635, + "y": 115 + }, + "xScale": 0.81, + "yScale": 0.81, + "alpha": 0.41, + "rotation": 0.028, + "tint": [ + 0.8, + 0.9, + 1.0 + ], + "animationTracks": [ + { + "steps": [ + { + "animateProperties": { + "xScale": 0.84, + "yScale": 0.84, + "alpha": 0.99 + } + }, + { + "animateProperties": { + "xScale": 0.81, + "yScale": 0.81, + "alpha": 0.41 + } + }, + { + "animateProperties": { + "alpha": 0.41 + }, + "durationSeconds": 18.39 + } + ] + } + ] + } + }, + { + "ref": "Star", + "overrides": { + "position": { + "x": 932, + "y": 212 + }, + "xScale": 0.88, + "yScale": 0.88, + "alpha": 0.49, + "rotation": -0.037, + "tint": [ + 0.8, + 0.9, + 1.0 + ], + "animationTracks": [ + { + "steps": [ + { + "animateProperties": { + "xScale": 0.93, + "yScale": 0.93, + "alpha": 0.69 + } + }, + { + "animateProperties": { + "xScale": 0.88, + "yScale": 0.88, + "alpha": 0.49 + } + }, + { + "animateProperties": { + "alpha": 0.49 + }, + "durationSeconds": 14.79 + } + ] + } + ] + } + }, + { + "ref": "Star", + "overrides": { + "position": { + "x": 11, + "y": 54 + }, + "xScale": 0.79, + "yScale": 0.79, + "alpha": 0.48, + "rotation": 0.006, + "tint": [ + 0.8, + 0.9, + 1.0 + ], + "animationTracks": [ + { + "steps": [ + { + "animateProperties": { + "xScale": 0.82, + "yScale": 0.82, + "alpha": 0.84 + } + }, + { + "animateProperties": { + "xScale": 0.79, + "yScale": 0.79, + "alpha": 0.48 + } + }, + { + "animateProperties": { + "alpha": 0.48 + }, + "durationSeconds": 15.9 + } + ] + } + ] + } + }, + { + "ref": "Star", + "overrides": { + "position": { + "x": 248, + "y": 233 + }, + "xScale": 0.32, + "yScale": 0.32, + "alpha": 0.33, + "rotation": 0.027, + "tint": [ + 0.8, + 0.9, + 1.0 + ], + "animationTracks": [ + { + "steps": [ + { + "animateProperties": { + "xScale": 0.34, + "yScale": 0.34, + "alpha": 0.7 + } + }, + { + "animateProperties": { + "xScale": 0.32, + "yScale": 0.32, + "alpha": 0.33 + } + }, + { + "animateProperties": { + "alpha": 0.33 + }, + "durationSeconds": 13.39 + } + ] + } + ] + } + }, + { + "ref": "Star", + "overrides": { + "position": { + "x": 906, + "y": 313 + }, + "xScale": 0.83, + "yScale": 0.83, + "alpha": 0.45, + "rotation": -0.035, + "tint": [ + 0.8, + 0.9, + 1.0 + ], + "animationTracks": [ + { + "steps": [ + { + "animateProperties": { + "xScale": 0.89, + "yScale": 0.89, + "alpha": 0.95 + } + }, + { + "animateProperties": { + "xScale": 0.83, + "yScale": 0.83, + "alpha": 0.45 + } + }, + { + "animateProperties": { + "alpha": 0.45 + }, + "durationSeconds": 15.58 + } + ] + } + ] + } + }, + { + "ref": "Star", + "overrides": { + "position": { + "x": 972, + "y": 230 + }, + "xScale": 0.4, + "yScale": 0.4, + "alpha": 0.36, + "rotation": 0.013, + "tint": [ + 0.8, + 0.9, + 1.0 + ], + "animationTracks": [ + { + "steps": [ + { + "animateProperties": { + "xScale": 0.42, + "yScale": 0.42, + "alpha": 0.85 + } + }, + { + "animateProperties": { + "xScale": 0.4, + "yScale": 0.4, + "alpha": 0.36 + } + }, + { + "animateProperties": { + "alpha": 0.36 + }, + "durationSeconds": 16.86 + } + ] + } + ] + } + }, + { + "ref": "Star", + "overrides": { + "position": { + "x": 562, + "y": 225 + }, + "xScale": 0.29, + "yScale": 0.29, + "alpha": 0.3, + "rotation": -0.038, + "tint": [ + 0.8, + 0.9, + 1.0 + ], + "animationTracks": [ + { + "steps": [ + { + "animateProperties": { + "xScale": 0.3, + "yScale": 0.3, + "alpha": 0.52 + } + }, + { + "animateProperties": { + "xScale": 0.29, + "yScale": 0.29, + "alpha": 0.3 + } + }, + { + "animateProperties": { + "alpha": 0.3 + }, + "durationSeconds": 17.18 + } + ] + } + ] + } + }, + { + "ref": "Star", + "overrides": { + "position": { + "x": 473, + "y": 196 + }, + "xScale": 0.72, + "yScale": 0.72, + "alpha": 0.39, + "rotation": -0.009, + "tint": [ + 0.8, + 0.9, + 1.0 + ], + "animationTracks": [ + { + "steps": [ + { + "animateProperties": { + "xScale": 0.77, + "yScale": 0.77, + "alpha": 0.64 + } + }, + { + "animateProperties": { + "xScale": 0.72, + "yScale": 0.72, + "alpha": 0.39 + } + }, + { + "animateProperties": { + "alpha": 0.39 + }, + "durationSeconds": 17.22 + } + ] + } + ] + } + }, + { + "ref": "Star", + "overrides": { + "position": { + "x": 423, + "y": 215 + }, + "xScale": 0.48, + "yScale": 0.48, + "alpha": 0.41, + "rotation": 0.008, + "tint": [ + 0.8, + 0.9, + 1.0 + ], + "animationTracks": [ + { + "steps": [ + { + "animateProperties": { + "xScale": 0.51, + "yScale": 0.51, + "alpha": 0.42 + } + }, + { + "animateProperties": { + "xScale": 0.48, + "yScale": 0.48, + "alpha": 0.41 + } + }, + { + "animateProperties": { + "alpha": 0.41 + }, + "durationSeconds": 22.97 + } + ] + } + ] + } + }, + { + "ref": "Star", + "overrides": { + "position": { + "x": 976, + "y": 3 + }, + "xScale": 0.94, + "yScale": 0.94, + "alpha": 0.47, + "rotation": 0.004, + "tint": [ + 0.8, + 0.9, + 1.0 + ], + "animationTracks": [ + { + "steps": [ + { + "animateProperties": { + "xScale": 0.98, + "yScale": 0.98, + "alpha": 0.94 + } + }, + { + "animateProperties": { + "xScale": 0.94, + "yScale": 0.94, + "alpha": 0.47 + } + }, + { + "animateProperties": { + "alpha": 0.47 + }, + "durationSeconds": 22.21 + } + ] + } + ] + } + }, + { + "ref": "Star", + "overrides": { + "position": { + "x": 1118, + "y": 308 + }, + "xScale": 0.9, + "yScale": 0.9, + "alpha": 0.45, + "rotation": 0.018, + "tint": [ + 0.8, + 0.9, + 1.0 + ], + "animationTracks": [ + { + "steps": [ + { + "animateProperties": { + "xScale": 0.95, + "yScale": 0.95, + "alpha": 0.72 + } + }, + { + "animateProperties": { + "xScale": 0.9, + "yScale": 0.9, + "alpha": 0.45 + } + }, + { + "animateProperties": { + "alpha": 0.45 + }, + "durationSeconds": 14.61 + } + ] + } + ] + } + }, + { + "ref": "Star", + "overrides": { + "position": { + "x": 828, + "y": 84 + }, + "xScale": 0.85, + "yScale": 0.85, + "alpha": 0.51, + "rotation": 0.01, + "tint": [ + 0.8, + 0.9, + 1.0 + ], + "animationTracks": [ + { + "steps": [ + { + "animateProperties": { + "xScale": 0.91, + "yScale": 0.91, + "alpha": 0.77 + } + }, + { + "animateProperties": { + "xScale": 0.85, + "yScale": 0.85, + "alpha": 0.51 + } + }, + { + "animateProperties": { + "alpha": 0.51 + }, + "durationSeconds": 23.42 + } + ] + } + ] + } + }, + { + "ref": "Star", + "overrides": { + "position": { + "x": 55, + "y": 42 + }, + "xScale": 0.67, + "yScale": 0.67, + "alpha": 0.41, + "rotation": -0.03, + "tint": [ + 0.8, + 0.9, + 1.0 + ], + "animationTracks": [ + { + "steps": [ + { + "animateProperties": { + "xScale": 0.69, + "yScale": 0.69, + "alpha": 0.44 + } + }, + { + "animateProperties": { + "xScale": 0.67, + "yScale": 0.67, + "alpha": 0.41 + } + }, + { + "animateProperties": { + "alpha": 0.41 + }, + "durationSeconds": 14.44 + } + ] + } + ] + } + }, + { + "ref": "Star", + "overrides": { + "position": { + "x": 669, + "y": 172 + }, + "xScale": 0.77, + "yScale": 0.77, + "alpha": 0.47, + "rotation": -0.043, + "tint": [ + 0.8, + 0.9, + 1.0 + ], + "animationTracks": [ + { + "steps": [ + { + "animateProperties": { + "xScale": 0.81, + "yScale": 0.81, + "alpha": 0.91 + } + }, + { + "animateProperties": { + "xScale": 0.77, + "yScale": 0.77, + "alpha": 0.47 + } + }, + { + "animateProperties": { + "alpha": 0.47 + }, + "durationSeconds": 12.29 + } + ] + } + ] + } + }, + { + "ref": "Star", + "overrides": { + "position": { + "x": 39, + "y": 276 + }, + "xScale": 0.28, + "yScale": 0.28, + "alpha": 0.29, + "rotation": -0.048, + "tint": [ + 0.8, + 0.9, + 1.0 + ], + "animationTracks": [ + { + "steps": [ + { + "animateProperties": { + "xScale": 0.3, + "yScale": 0.3, + "alpha": 0.34 + } + }, + { + "animateProperties": { + "xScale": 0.28, + "yScale": 0.28, + "alpha": 0.29 + } + }, + { + "animateProperties": { + "alpha": 0.29 + }, + "durationSeconds": 24.27 + } + ] + } + ] + } + }, + { + "ref": "Star", + "overrides": { + "position": { + "x": 63, + "y": 126 + }, + "xScale": 0.36, + "yScale": 0.36, + "alpha": 0.31, + "rotation": 0.007, + "tint": [ + 0.8, + 0.9, + 1.0 + ], + "animationTracks": [ + { + "steps": [ + { + "animateProperties": { + "xScale": 0.37, + "yScale": 0.37, + "alpha": 0.4 + } + }, + { + "animateProperties": { + "xScale": 0.36, + "yScale": 0.36, + "alpha": 0.31 + } + }, + { + "animateProperties": { + "alpha": 0.31 + }, + "durationSeconds": 19.38 + } + ] + } + ] + } + }, + { + "ref": "Star", + "overrides": { + "position": { + "x": 446, + "y": 238 + }, + "xScale": 0.72, + "yScale": 0.72, + "alpha": 0.4, + "rotation": -0.04, + "tint": [ + 0.8, + 0.9, + 1.0 + ], + "animationTracks": [ + { + "steps": [ + { + "animateProperties": { + "xScale": 0.77, + "yScale": 0.77, + "alpha": 0.76 + } + }, + { + "animateProperties": { + "xScale": 0.72, + "yScale": 0.72, + "alpha": 0.4 + } + }, + { + "animateProperties": { + "alpha": 0.4 + }, + "durationSeconds": 20.71 + } + ] + } + ] + } + }, + { + "ref": "Star", + "overrides": { + "position": { + "x": 335, + "y": 159 + }, + "xScale": 0.3, + "yScale": 0.3, + "alpha": 0.31, + "rotation": -0.011, + "tint": [ + 0.8, + 0.9, + 1.0 + ], + "animationTracks": [ + { + "steps": [ + { + "animateProperties": { + "xScale": 0.31, + "yScale": 0.31, + "alpha": 0.78 + } + }, + { + "animateProperties": { + "xScale": 0.3, + "yScale": 0.3, + "alpha": 0.31 + } + }, + { + "animateProperties": { + "alpha": 0.31 + }, + "durationSeconds": 24.29 + } + ] + } + ] + } + }, + { + "ref": "Star", + "overrides": { + "position": { + "x": 406, + "y": 38 + }, + "xScale": 0.64, + "yScale": 0.64, + "alpha": 0.38, + "rotation": 0.019, + "tint": [ + 0.8, + 0.9, + 1.0 + ], + "animationTracks": [ + { + "steps": [ + { + "animateProperties": { + "xScale": 0.69, + "yScale": 0.69, + "alpha": 0.81 + } + }, + { + "animateProperties": { + "xScale": 0.64, + "yScale": 0.64, + "alpha": 0.38 + } + }, + { + "animateProperties": { + "alpha": 0.38 + }, + "durationSeconds": 13.13 + } + ] + } + ] + } + }, + { + "ref": "Star", + "overrides": { + "position": { + "x": 247, + "y": 289 + }, + "xScale": 0.79, + "yScale": 0.79, + "alpha": 0.44, + "rotation": -0.017, + "tint": [ + 0.8, + 0.9, + 1.0 + ], + "animationTracks": [ + { + "steps": [ + { + "animateProperties": { + "xScale": 0.83, + "yScale": 0.83, + "alpha": 0.65 + } + }, + { + "animateProperties": { + "xScale": 0.79, + "yScale": 0.79, + "alpha": 0.44 + } + }, + { + "animateProperties": { + "alpha": 0.44 + }, + "durationSeconds": 16.6 + } + ] + } + ] + } + }, + { + "ref": "Star", + "overrides": { + "position": { + "x": 860, + "y": 250 + }, + "xScale": 0.3, + "yScale": 0.3, + "alpha": 0.34, + "rotation": -0.007, + "tint": [ + 0.8, + 0.9, + 1.0 + ], + "animationTracks": [ + { + "steps": [ + { + "animateProperties": { + "xScale": 0.32, + "yScale": 0.32, + "alpha": 0.89 + } + }, + { + "animateProperties": { + "xScale": 0.3, + "yScale": 0.3, + "alpha": 0.34 + } + }, + { + "animateProperties": { + "alpha": 0.34 + }, + "durationSeconds": 20.02 + } + ] + } + ] + } + }, + { + "ref": "Star", + "overrides": { + "position": { + "x": 1068, + "y": 138 + }, + "xScale": 0.66, + "yScale": 0.66, + "alpha": 0.44, + "rotation": 0.01, + "tint": [ + 0.8, + 0.9, + 1.0 + ], + "animationTracks": [ + { + "steps": [ + { + "animateProperties": { + "xScale": 0.71, + "yScale": 0.71, + "alpha": 0.7 + } + }, + { + "animateProperties": { + "xScale": 0.66, + "yScale": 0.66, + "alpha": 0.44 + } + }, + { + "animateProperties": { + "alpha": 0.44 + }, + "durationSeconds": 22.04 + } + ] + } + ] + } + }, + { + "ref": "Star", + "overrides": { + "position": { + "x": 660, + "y": 125 + }, + "xScale": 0.85, + "yScale": 0.85, + "alpha": 0.5, + "rotation": 0.011, + "tint": [ + 0.8, + 0.9, + 1.0 + ], + "animationTracks": [ + { + "steps": [ + { + "animateProperties": { + "xScale": 0.88, + "yScale": 0.88, + "alpha": 0.62 + } + }, + { + "animateProperties": { + "xScale": 0.85, + "yScale": 0.85, + "alpha": 0.5 + } + }, + { + "animateProperties": { + "alpha": 0.5 + }, + "durationSeconds": 15.9 + } + ] + } + ] + } + }, + { + "ref": "Star", + "overrides": { + "position": { + "x": 776, + "y": 172 + }, + "xScale": 0.26, + "yScale": 0.26, + "alpha": 0.28, + "rotation": -0.017, + "tint": [ + 0.8, + 0.9, + 1.0 + ], + "animationTracks": [ + { + "steps": [ + { + "animateProperties": { + "xScale": 0.28, + "yScale": 0.28, + "alpha": 0.43 + } + }, + { + "animateProperties": { + "xScale": 0.26, + "yScale": 0.26, + "alpha": 0.28 + } + }, + { + "animateProperties": { + "alpha": 0.28 + }, + "durationSeconds": 21.56 + } + ] + } + ] + } + }, + { + "ref": "Star", + "overrides": { + "position": { + "x": 1220, + "y": 141 + }, + "xScale": 0.61, + "yScale": 0.61, + "alpha": 0.37, + "rotation": 0.006, + "tint": [ + 0.8, + 0.9, + 1.0 + ], + "animationTracks": [ + { + "steps": [ + { + "animateProperties": { + "xScale": 0.64, + "yScale": 0.64, + "alpha": 0.52 + } + }, + { + "animateProperties": { + "xScale": 0.61, + "yScale": 0.61, + "alpha": 0.37 + } + }, + { + "animateProperties": { + "alpha": 0.37 + }, + "durationSeconds": 14.91 + } + ] + } + ] + } + }, + { + "ref": "Star", + "overrides": { + "position": { + "x": 492, + "y": 243 + }, + "xScale": 0.67, + "yScale": 0.67, + "alpha": 0.45, + "rotation": 0.02, + "tint": [ + 0.8, + 0.9, + 1.0 + ], + "animationTracks": [ + { + "steps": [ + { + "animateProperties": { + "xScale": 0.71, + "yScale": 0.71, + "alpha": 0.5 + } + }, + { + "animateProperties": { + "xScale": 0.67, + "yScale": 0.67, + "alpha": 0.45 + } + }, + { + "animateProperties": { + "alpha": 0.45 + }, + "durationSeconds": 11.77 + } + ] + } + ] + } + }, + { + "ref": "Star", + "overrides": { + "position": { + "x": 627, + "y": 297 + }, + "xScale": 0.46, + "yScale": 0.46, + "alpha": 0.35, + "rotation": -0.015, + "tint": [ + 0.8, + 0.9, + 1.0 + ], + "animationTracks": [ + { + "steps": [ + { + "animateProperties": { + "xScale": 0.49, + "yScale": 0.49, + "alpha": 1.0 + } + }, + { + "animateProperties": { + "xScale": 0.46, + "yScale": 0.46, + "alpha": 0.35 + } + }, + { + "animateProperties": { + "alpha": 0.35 + }, + "durationSeconds": 17.36 + } + ] + } + ] + } + }, + { + "ref": "Star", + "overrides": { + "position": { + "x": 929, + "y": 138 + }, + "xScale": 0.42, + "yScale": 0.42, + "alpha": 0.38, + "rotation": 0.047, + "tint": [ + 0.8, + 0.9, + 1.0 + ], + "animationTracks": [ + { + "steps": [ + { + "animateProperties": { + "xScale": 0.44, + "yScale": 0.44, + "alpha": 0.58 + } + }, + { + "animateProperties": { + "xScale": 0.42, + "yScale": 0.42, + "alpha": 0.38 + } + }, + { + "animateProperties": { + "alpha": 0.38 + }, + "durationSeconds": 20.63 + } + ] + } + ] + } + }, + { + "ref": "Star", + "overrides": { + "position": { + "x": 379, + "y": 98 + }, + "xScale": 0.37, + "yScale": 0.37, + "alpha": 0.36, + "rotation": -0.023, + "tint": [ + 0.8, + 0.9, + 1.0 + ], + "animationTracks": [ + { + "steps": [ + { + "animateProperties": { + "xScale": 0.39, + "yScale": 0.39, + "alpha": 0.99 + } + }, + { + "animateProperties": { + "xScale": 0.37, + "yScale": 0.37, + "alpha": 0.36 + } + }, + { + "animateProperties": { + "alpha": 0.36 + }, + "durationSeconds": 16.92 + } + ] + } + ] + } + }, + { + "ref": "Star", + "overrides": { + "position": { + "x": 205, + "y": 99 + }, + "xScale": 0.42, + "yScale": 0.42, + "alpha": 0.34, + "rotation": -0.047, + "tint": [ + 0.8, + 0.9, + 1.0 + ], + "animationTracks": [ + { + "steps": [ + { + "animateProperties": { + "xScale": 0.44, + "yScale": 0.44, + "alpha": 0.81 + } + }, + { + "animateProperties": { + "xScale": 0.42, + "yScale": 0.42, + "alpha": 0.34 + } + }, + { + "animateProperties": { + "alpha": 0.34 + }, + "durationSeconds": 10.15 + } + ] + } + ] + } + }, + { + "ref": "Star", + "overrides": { + "position": { + "x": 111, + "y": 283 + }, + "xScale": 0.42, + "yScale": 0.42, + "alpha": 0.37, + "rotation": -0.051, + "tint": [ + 0.8, + 0.9, + 1.0 + ], + "animationTracks": [ + { + "steps": [ + { + "animateProperties": { + "xScale": 0.45, + "yScale": 0.45, + "alpha": 0.84 + } + }, + { + "animateProperties": { + "xScale": 0.42, + "yScale": 0.42, + "alpha": 0.37 + } + }, + { + "animateProperties": { + "alpha": 0.37 + }, + "durationSeconds": 9.74 + } + ] + } + ] + } + }, + { + "ref": "Star", + "overrides": { + "position": { + "x": 582, + "y": 240 + }, + "xScale": 0.55, + "yScale": 0.55, + "alpha": 0.43, + "rotation": -0.04, + "tint": [ + 0.8, + 0.9, + 1.0 + ], + "animationTracks": [ + { + "steps": [ + { + "animateProperties": { + "xScale": 0.58, + "yScale": 0.58, + "alpha": 0.57 + } + }, + { + "animateProperties": { + "xScale": 0.55, + "yScale": 0.55, + "alpha": 0.43 + } + }, + { + "animateProperties": { + "alpha": 0.43 + }, + "durationSeconds": 22.65 + } + ] + } + ] + } + }, + { + "ref": "Star", + "overrides": { + "position": { + "x": 133, + "y": 205 + }, + "xScale": 0.56, + "yScale": 0.56, + "alpha": 0.41, + "rotation": 0.047, + "tint": [ + 0.8, + 0.9, + 1.0 + ], + "animationTracks": [ + { + "steps": [ + { + "animateProperties": { + "xScale": 0.59, + "yScale": 0.59, + "alpha": 0.5 + } + }, + { + "animateProperties": { + "xScale": 0.56, + "yScale": 0.56, + "alpha": 0.41 + } + }, + { + "animateProperties": { + "alpha": 0.41 + }, + "durationSeconds": 21.79 + } + ] + } + ] + } + }, + { + "ref": "Star", + "overrides": { + "position": { + "x": 174, + "y": 127 + }, + "xScale": 0.31, + "yScale": 0.31, + "alpha": 0.34, + "rotation": 0.002, + "tint": [ + 0.8, + 0.9, + 1.0 + ], + "animationTracks": [ + { + "steps": [ + { + "animateProperties": { + "xScale": 0.33, + "yScale": 0.33, + "alpha": 0.86 + } + }, + { + "animateProperties": { + "xScale": 0.31, + "yScale": 0.31, + "alpha": 0.34 + } + }, + { + "animateProperties": { + "alpha": 0.34 + }, + "durationSeconds": 11.84 + } + ] + } + ] + } + }, + { + "ref": "Star", + "overrides": { + "position": { + "x": 922, + "y": 226 + }, + "xScale": 0.42, + "yScale": 0.42, + "alpha": 0.35, + "rotation": 0.048, + "tint": [ + 0.8, + 0.9, + 1.0 + ], + "animationTracks": [ + { + "steps": [ + { + "animateProperties": { + "xScale": 0.44, + "yScale": 0.44, + "alpha": 0.72 + } + }, + { + "animateProperties": { + "xScale": 0.42, + "yScale": 0.42, + "alpha": 0.35 + } + }, + { + "animateProperties": { + "alpha": 0.35 + }, + "durationSeconds": 9.02 + } + ] + } + ] + } + }, + { + "ref": "Star", + "overrides": { + "position": { + "x": 203, + "y": 106 + }, + "xScale": 0.66, + "yScale": 0.66, + "alpha": 0.37, + "rotation": -0.036, + "tint": [ + 0.8, + 0.9, + 1.0 + ], + "animationTracks": [ + { + "steps": [ + { + "animateProperties": { + "xScale": 0.69, + "yScale": 0.69, + "alpha": 0.52 + } + }, + { + "animateProperties": { + "xScale": 0.66, + "yScale": 0.66, + "alpha": 0.37 + } + }, + { + "animateProperties": { + "alpha": 0.37 + }, + "durationSeconds": 17.38 + } + ] + } + ] + } + }, + { + "ref": "Star", + "overrides": { + "position": { + "x": 836, + "y": 230 + }, + "xScale": 0.71, + "yScale": 0.71, + "alpha": 0.38, + "rotation": 0.037, + "tint": [ + 0.8, + 0.9, + 1.0 + ], + "animationTracks": [ + { + "steps": [ + { + "animateProperties": { + "xScale": 0.75, + "yScale": 0.75, + "alpha": 0.56 + } + }, + { + "animateProperties": { + "xScale": 0.71, + "yScale": 0.71, + "alpha": 0.38 + } + }, + { + "animateProperties": { + "alpha": 0.38 + }, + "durationSeconds": 12.81 + } + ] + } + ] + } + }, + { + "ref": "Star", + "overrides": { + "position": { + "x": 145, + "y": 119 + }, + "xScale": 0.92, + "yScale": 0.92, + "alpha": 0.49, + "rotation": -0.008, + "tint": [ + 0.8, + 0.9, + 1.0 + ], + "animationTracks": [ + { + "steps": [ + { + "animateProperties": { + "xScale": 0.98, + "yScale": 0.98, + "alpha": 0.83 + } + }, + { + "animateProperties": { + "xScale": 0.92, + "yScale": 0.92, + "alpha": 0.49 + } + }, + { + "animateProperties": { + "alpha": 0.49 + }, + "durationSeconds": 23.87 + } + ] + } + ] + } + }, + { + "ref": "Star", + "overrides": { + "position": { + "x": 1115, + "y": 115 + }, + "xScale": 0.67, + "yScale": 0.67, + "alpha": 0.45, + "rotation": -0.02, + "tint": [ + 0.8, + 0.9, + 1.0 + ], + "animationTracks": [ + { + "steps": [ + { + "animateProperties": { + "xScale": 0.72, + "yScale": 0.72, + "alpha": 0.49 + } + }, + { + "animateProperties": { + "xScale": 0.67, + "yScale": 0.67, + "alpha": 0.45 + } + }, + { + "animateProperties": { + "alpha": 0.45 + }, + "durationSeconds": 10.82 + } + ] + } + ] + } + }, + { + "ref": "Star", + "overrides": { + "position": { + "x": 1165, + "y": 147 + }, + "xScale": 0.52, + "yScale": 0.52, + "alpha": 0.36, + "rotation": 0.004, + "tint": [ + 0.8, + 0.9, + 1.0 + ], + "animationTracks": [ + { + "steps": [ + { + "animateProperties": { + "xScale": 0.55, + "yScale": 0.55, + "alpha": 0.62 + } + }, + { + "animateProperties": { + "xScale": 0.52, + "yScale": 0.52, + "alpha": 0.36 + } + }, + { + "animateProperties": { + "alpha": 0.36 + }, + "durationSeconds": 12.63 + } + ] + } + ] + } + }, + { + "ref": "Star", + "overrides": { + "position": { + "x": 896, + "y": 41 + }, + "xScale": 0.64, + "yScale": 0.64, + "alpha": 0.43, + "rotation": -0.028, + "tint": [ + 0.8, + 0.9, + 1.0 + ], + "animationTracks": [ + { + "steps": [ + { + "animateProperties": { + "xScale": 0.69, + "yScale": 0.69, + "alpha": 0.77 + } + }, + { + "animateProperties": { + "xScale": 0.64, + "yScale": 0.64, + "alpha": 0.43 + } + }, + { + "animateProperties": { + "alpha": 0.43 + }, + "durationSeconds": 8.44 + } + ] + } + ] + } + }, + { + "ref": "Star", + "overrides": { + "position": { + "x": 1178, + "y": 300 + }, + "xScale": 0.96, + "yScale": 0.96, + "alpha": 0.51, + "rotation": 0.027, + "tint": [ + 0.8, + 0.9, + 1.0 + ], + "animationTracks": [ + { + "steps": [ + { + "animateProperties": { + "xScale": 1.04, + "yScale": 1.04, + "alpha": 0.64 + } + }, + { + "animateProperties": { + "xScale": 0.96, + "yScale": 0.96, + "alpha": 0.51 + } + }, + { + "animateProperties": { + "alpha": 0.51 + }, + "durationSeconds": 8.68 + } + ] + } + ] + } + }, + { + "ref": "Star", + "overrides": { + "position": { + "x": 963, + "y": 265 + }, + "xScale": 0.69, + "yScale": 0.69, + "alpha": 0.39, + "rotation": -0.001, + "tint": [ + 0.8, + 0.9, + 1.0 + ], + "animationTracks": [ + { + "steps": [ + { + "animateProperties": { + "xScale": 0.74, + "yScale": 0.74, + "alpha": 0.75 + } + }, + { + "animateProperties": { + "xScale": 0.69, + "yScale": 0.69, + "alpha": 0.39 + } + }, + { + "animateProperties": { + "alpha": 0.39 + }, + "durationSeconds": 18.79 + } + ] + } + ] + } + }, + { + "ref": "Star", + "overrides": { + "position": { + "x": 186, + "y": 240 + }, + "xScale": 0.45, + "yScale": 0.45, + "alpha": 0.38, + "rotation": 0.02, + "tint": [ + 0.8, + 0.9, + 1.0 + ], + "animationTracks": [ + { + "steps": [ + { + "animateProperties": { + "xScale": 0.47, + "yScale": 0.47, + "alpha": 0.91 + } + }, + { + "animateProperties": { + "xScale": 0.45, + "yScale": 0.45, + "alpha": 0.38 + } + }, + { + "animateProperties": { + "alpha": 0.38 + }, + "durationSeconds": 13.61 + } + ] + } + ] + } + }, + { + "ref": "Star", + "overrides": { + "position": { + "x": 590, + "y": 205 + }, + "xScale": 0.83, + "yScale": 0.83, + "alpha": 0.45, + "rotation": 0.049, + "tint": [ + 0.8, + 0.9, + 1.0 + ], + "animationTracks": [ + { + "steps": [ + { + "animateProperties": { + "xScale": 0.88, + "yScale": 0.88, + "alpha": 0.62 + } + }, + { + "animateProperties": { + "xScale": 0.83, + "yScale": 0.83, + "alpha": 0.45 + } + }, + { + "animateProperties": { + "alpha": 0.45 + }, + "durationSeconds": 13.5 + } + ] + } + ] + } + }, + { + "ref": "Star", + "overrides": { + "position": { + "x": 827, + "y": 263 + }, + "xScale": 0.83, + "yScale": 0.83, + "alpha": 0.49, + "rotation": 0.002, + "tint": [ + 0.8, + 0.9, + 1.0 + ], + "animationTracks": [ + { + "steps": [ + { + "animateProperties": { + "xScale": 0.85, + "yScale": 0.85, + "alpha": 0.73 + } + }, + { + "animateProperties": { + "xScale": 0.83, + "yScale": 0.83, + "alpha": 0.49 + } + }, + { + "animateProperties": { + "alpha": 0.49 + }, + "durationSeconds": 8.92 + } + ] + } + ] + } + }, + { + "ref": "Star", + "overrides": { + "position": { + "x": 1275, + "y": 255 + }, + "xScale": 0.66, + "yScale": 0.66, + "alpha": 0.39, + "rotation": -0.006, + "tint": [ + 0.8, + 0.9, + 1.0 + ], + "animationTracks": [ + { + "steps": [ + { + "animateProperties": { + "xScale": 0.7, + "yScale": 0.7, + "alpha": 0.73 + } + }, + { + "animateProperties": { + "xScale": 0.66, + "yScale": 0.66, + "alpha": 0.39 + } + }, + { + "animateProperties": { + "alpha": 0.39 + }, + "durationSeconds": 23.77 + } + ] + } + ] + } + }, + { + "ref": "Star", + "overrides": { + "position": { + "x": 992, + "y": 62 + }, + "xScale": 0.26, + "yScale": 0.26, + "alpha": 0.34, + "rotation": -0.051, + "tint": [ + 0.8, + 0.9, + 1.0 + ], + "animationTracks": [ + { + "steps": [ + { + "animateProperties": { + "xScale": 0.28, + "yScale": 0.28, + "alpha": 0.81 + } + }, + { + "animateProperties": { + "xScale": 0.26, + "yScale": 0.26, + "alpha": 0.34 + } + }, + { + "animateProperties": { + "alpha": 0.34 + }, + "durationSeconds": 13.28 + } + ] + } + ] + } + }, + { + "ref": "Star", + "overrides": { + "position": { + "x": 835, + "y": 47 + }, + "xScale": 0.37, + "yScale": 0.37, + "alpha": 0.3, + "rotation": -0.036, + "tint": [ + 0.8, + 0.9, + 1.0 + ], + "animationTracks": [ + { + "steps": [ + { + "animateProperties": { + "xScale": 0.4, + "yScale": 0.4, + "alpha": 0.96 + } + }, + { + "animateProperties": { + "xScale": 0.37, + "yScale": 0.37, + "alpha": 0.3 + } + }, + { + "animateProperties": { + "alpha": 0.3 + }, + "durationSeconds": 19.01 + } + ] + } + ] + } + }, + { + "ref": "Star", + "overrides": { + "position": { + "x": 597, + "y": 260 + }, + "xScale": 0.72, + "yScale": 0.72, + "alpha": 0.43, + "rotation": -0.037, + "tint": [ + 0.8, + 0.9, + 1.0 + ], + "animationTracks": [ + { + "steps": [ + { + "animateProperties": { + "xScale": 0.76, + "yScale": 0.76, + "alpha": 0.7 + } + }, + { + "animateProperties": { + "xScale": 0.72, + "yScale": 0.72, + "alpha": 0.43 + } + }, + { + "animateProperties": { + "alpha": 0.43 + }, + "durationSeconds": 15.77 + } + ] + } + ] + } + }, + { + "ref": "Star", + "overrides": { + "position": { + "x": 390, + "y": 306 + }, + "xScale": 0.56, + "yScale": 0.56, + "alpha": 0.43, + "rotation": -0.009, + "tint": [ + 0.8, + 0.9, + 1.0 + ], + "animationTracks": [ + { + "steps": [ + { + "animateProperties": { + "xScale": 0.6, + "yScale": 0.6, + "alpha": 0.59 + } + }, + { + "animateProperties": { + "xScale": 0.56, + "yScale": 0.56, + "alpha": 0.43 + } + }, + { + "animateProperties": { + "alpha": 0.43 + }, + "durationSeconds": 21.43 + } + ] + } + ] + } + }, + { + "ref": "Star", + "overrides": { + "position": { + "x": 1039, + "y": 136 + }, + "xScale": 0.83, + "yScale": 0.83, + "alpha": 0.44, + "rotation": -0.001, + "tint": [ + 0.8, + 0.9, + 1.0 + ], + "animationTracks": [ + { + "steps": [ + { + "animateProperties": { + "xScale": 0.87, + "yScale": 0.87, + "alpha": 0.77 + } + }, + { + "animateProperties": { + "xScale": 0.83, + "yScale": 0.83, + "alpha": 0.44 + } + }, + { + "animateProperties": { + "alpha": 0.44 + }, + "durationSeconds": 24.98 + } + ] + } + ] + } + }, + { + "ref": "Star", + "overrides": { + "position": { + "x": 304, + "y": 228 + }, + "xScale": 0.59, + "yScale": 0.59, + "alpha": 0.4, + "rotation": -0.019, + "tint": [ + 0.8, + 0.9, + 1.0 + ], + "animationTracks": [ + { + "steps": [ + { + "animateProperties": { + "xScale": 0.62, + "yScale": 0.62, + "alpha": 0.73 + } + }, + { + "animateProperties": { + "xScale": 0.59, + "yScale": 0.59, + "alpha": 0.4 + } + }, + { + "animateProperties": { + "alpha": 0.4 + }, + "durationSeconds": 15.74 + } + ] + } + ] + } + }, + { + "ref": "Star", + "overrides": { + "position": { + "x": 386, + "y": 122 + }, + "xScale": 0.62, + "yScale": 0.62, + "alpha": 0.43, + "rotation": 0.021, + "tint": [ + 0.8, + 0.9, + 1.0 + ], + "animationTracks": [ + { + "steps": [ + { + "animateProperties": { + "xScale": 0.65, + "yScale": 0.65, + "alpha": 0.45 + } + }, + { + "animateProperties": { + "xScale": 0.62, + "yScale": 0.62, + "alpha": 0.43 + } + }, + { + "animateProperties": { + "alpha": 0.43 + }, + "durationSeconds": 20.66 + } + ] + } + ] + } + }, + { + "ref": "Star", + "overrides": { + "position": { + "x": 780, + "y": 197 + }, + "xScale": 0.98, + "yScale": 0.98, + "alpha": 0.51, + "rotation": -0.039, + "tint": [ + 0.8, + 0.9, + 1.0 + ], + "animationTracks": [ + { + "steps": [ + { + "animateProperties": { + "xScale": 1.05, + "yScale": 1.05, + "alpha": 0.58 + } + }, + { + "animateProperties": { + "xScale": 0.98, + "yScale": 0.98, + "alpha": 0.51 + } + }, + { + "animateProperties": { + "alpha": 0.51 + }, + "durationSeconds": 24.4 + } + ] + } + ] + } + }, + { + "ref": "Star", + "overrides": { + "position": { + "x": 1208, + "y": 169 + }, + "xScale": 0.86, + "yScale": 0.86, + "alpha": 0.46, + "rotation": 0.023, + "tint": [ + 0.8, + 0.9, + 1.0 + ], + "animationTracks": [ + { + "steps": [ + { + "animateProperties": { + "xScale": 0.92, + "yScale": 0.92, + "alpha": 0.74 + } + }, + { + "animateProperties": { + "xScale": 0.86, + "yScale": 0.86, + "alpha": 0.46 + } + }, + { + "animateProperties": { + "alpha": 0.46 + }, + "durationSeconds": 15.77 + } + ] + } + ] + } + }, + { + "ref": "Star", + "overrides": { + "position": { + "x": 839, + "y": 79 + }, + "xScale": 0.29, + "yScale": 0.29, + "alpha": 0.3, + "rotation": -0.044, + "tint": [ + 0.8, + 0.9, + 1.0 + ], + "animationTracks": [ + { + "steps": [ + { + "animateProperties": { + "xScale": 0.31, + "yScale": 0.31, + "alpha": 0.74 + } + }, + { + "animateProperties": { + "xScale": 0.29, + "yScale": 0.29, + "alpha": 0.3 + } + }, + { + "animateProperties": { + "alpha": 0.3 + }, + "durationSeconds": 14.76 + } + ] + } + ] + } + }, + { + "ref": "Star", + "overrides": { + "position": { + "x": 672, + "y": 273 + }, + "xScale": 0.48, + "yScale": 0.48, + "alpha": 0.39, + "rotation": -0.048, + "tint": [ + 0.8, + 0.9, + 1.0 + ], + "animationTracks": [ + { + "steps": [ + { + "animateProperties": { + "xScale": 0.5, + "yScale": 0.5, + "alpha": 0.85 + } + }, + { + "animateProperties": { + "xScale": 0.48, + "yScale": 0.48, + "alpha": 0.39 + } + }, + { + "animateProperties": { + "alpha": 0.39 + }, + "durationSeconds": 22.83 + } + ] + } + ] + } + }, + { + "ref": "Star", + "overrides": { + "position": { + "x": 140, + "y": 120 + }, + "xScale": 0.67, + "yScale": 0.67, + "alpha": 0.47, + "rotation": -0.042, + "tint": [ + 0.8, + 0.9, + 1.0 + ], + "animationTracks": [ + { + "steps": [ + { + "animateProperties": { + "xScale": 0.72, + "yScale": 0.72, + "alpha": 0.87 + } + }, + { + "animateProperties": { + "xScale": 0.67, + "yScale": 0.67, + "alpha": 0.47 + } + }, + { + "animateProperties": { + "alpha": 0.47 + }, + "durationSeconds": 15.38 + } + ] + } + ] + } + }, + { + "ref": "Star", + "overrides": { + "position": { + "x": 205, + "y": 227 + }, + "xScale": 0.34, + "yScale": 0.34, + "alpha": 0.29, + "rotation": -0.015, + "tint": [ + 0.8, + 0.9, + 1.0 + ], + "animationTracks": [ + { + "steps": [ + { + "animateProperties": { + "xScale": 0.36, + "yScale": 0.36, + "alpha": 0.52 + } + }, + { + "animateProperties": { + "xScale": 0.34, + "yScale": 0.34, + "alpha": 0.29 + } + }, + { + "animateProperties": { + "alpha": 0.29 + }, + "durationSeconds": 8.95 + } + ] + } + ] + } + }, + { + "ref": "Star", + "overrides": { + "position": { + "x": 882, + "y": 74 + }, + "xScale": 0.38, + "yScale": 0.38, + "alpha": 0.36, + "rotation": 0.011, + "tint": [ + 0.8, + 0.9, + 1.0 + ], + "animationTracks": [ + { + "steps": [ + { + "animateProperties": { + "xScale": 0.4, + "yScale": 0.4, + "alpha": 0.48 + } + }, + { + "animateProperties": { + "xScale": 0.38, + "yScale": 0.38, + "alpha": 0.36 + } + }, + { + "animateProperties": { + "alpha": 0.36 + }, + "durationSeconds": 10.98 + } + ] + } + ] + } + }, + { + "ref": "Star", + "overrides": { + "position": { + "x": 783, + "y": 317 + }, + "xScale": 0.71, + "yScale": 0.71, + "alpha": 0.44, + "rotation": -0.004, + "tint": [ + 0.8, + 0.9, + 1.0 + ], + "animationTracks": [ + { + "steps": [ + { + "animateProperties": { + "xScale": 0.75, + "yScale": 0.75, + "alpha": 0.57 + } + }, + { + "animateProperties": { + "xScale": 0.71, + "yScale": 0.71, + "alpha": 0.44 + } + }, + { + "animateProperties": { + "alpha": 0.44 + }, + "durationSeconds": 18.84 + } + ] + } + ] + } + }, + { + "ref": "Star", + "overrides": { + "position": { + "x": 19, + "y": 238 + }, + "xScale": 0.9, + "yScale": 0.9, + "alpha": 0.44, + "rotation": 0.009, + "tint": [ + 0.8, + 0.9, + 1.0 + ], + "animationTracks": [ + { + "steps": [ + { + "animateProperties": { + "xScale": 0.96, + "yScale": 0.96, + "alpha": 0.69 + } + }, + { + "animateProperties": { + "xScale": 0.9, + "yScale": 0.9, + "alpha": 0.44 + } + }, + { + "animateProperties": { + "alpha": 0.44 + }, + "durationSeconds": 13.87 + } + ] + } + ] + } + }, + { + "ref": "Star", + "overrides": { + "position": { + "x": 868, + "y": 128 + }, + "xScale": 0.53, + "yScale": 0.53, + "alpha": 0.43, + "rotation": -0.012, + "tint": [ + 0.8, + 0.9, + 1.0 + ], + "animationTracks": [ + { + "steps": [ + { + "animateProperties": { + "xScale": 0.55, + "yScale": 0.55, + "alpha": 0.92 + } + }, + { + "animateProperties": { + "xScale": 0.53, + "yScale": 0.53, + "alpha": 0.43 + } + }, + { + "animateProperties": { + "alpha": 0.43 + }, + "durationSeconds": 9.81 + } + ] + } + ] + } + }, + { + "ref": "Star", + "overrides": { + "position": { + "x": 735, + "y": 294 + }, + "xScale": 0.42, + "yScale": 0.42, + "alpha": 0.31, + "rotation": -0.051, + "tint": [ + 0.8, + 0.9, + 1.0 + ], + "animationTracks": [ + { + "steps": [ + { + "animateProperties": { + "xScale": 0.45, + "yScale": 0.45, + "alpha": 0.88 + } + }, + { + "animateProperties": { + "xScale": 0.42, + "yScale": 0.42, + "alpha": 0.31 + } + }, + { + "animateProperties": { + "alpha": 0.31 + }, + "durationSeconds": 14.73 + } + ] + } + ] + } + }, + { + "ref": "Star", + "overrides": { + "position": { + "x": 100, + "y": 310 + }, + "xScale": 0.76, + "yScale": 0.76, + "alpha": 0.48, + "rotation": 0.031, + "tint": [ + 0.8, + 0.9, + 1.0 + ], + "animationTracks": [ + { + "steps": [ + { + "animateProperties": { + "xScale": 0.81, + "yScale": 0.81, + "alpha": 0.88 + } + }, + { + "animateProperties": { + "xScale": 0.76, + "yScale": 0.76, + "alpha": 0.48 + } + }, + { + "animateProperties": { + "alpha": 0.48 + }, + "durationSeconds": 11.91 + } + ] + } + ] + } + }, + { + "ref": "Star", + "overrides": { + "position": { + "x": 448, + "y": 97 + }, + "xScale": 0.66, + "yScale": 0.66, + "alpha": 0.44, + "rotation": 0.013, + "tint": [ + 0.8, + 0.9, + 1.0 + ], + "animationTracks": [ + { + "steps": [ + { + "animateProperties": { + "xScale": 0.7, + "yScale": 0.7, + "alpha": 0.81 + } + }, + { + "animateProperties": { + "xScale": 0.66, + "yScale": 0.66, + "alpha": 0.44 + } + }, + { + "animateProperties": { + "alpha": 0.44 + }, + "durationSeconds": 22.25 + } + ] + } + ] + } + }, + { + "ref": "Star", + "overrides": { + "position": { + "x": 80, + "y": 158 + }, + "xScale": 0.79, + "yScale": 0.79, + "alpha": 0.43, + "rotation": -0.018, + "tint": [ + 0.8, + 0.9, + 1.0 + ], + "animationTracks": [ + { + "steps": [ + { + "animateProperties": { + "xScale": 0.82, + "yScale": 0.82, + "alpha": 0.5 + } + }, + { + "animateProperties": { + "xScale": 0.79, + "yScale": 0.79, + "alpha": 0.43 + } + }, + { + "animateProperties": { + "alpha": 0.43 + }, + "durationSeconds": 23.46 + } + ] + } + ] + } + }, + { + "ref": "Star", + "overrides": { + "position": { + "x": 850, + "y": 89 + }, + "xScale": 0.36, + "yScale": 0.36, + "alpha": 0.38, + "rotation": -0.024, + "tint": [ + 0.8, + 0.9, + 1.0 + ], + "animationTracks": [ + { + "steps": [ + { + "animateProperties": { + "xScale": 0.38, + "yScale": 0.38, + "alpha": 0.61 + } + }, + { + "animateProperties": { + "xScale": 0.36, + "yScale": 0.36, + "alpha": 0.38 + } + }, + { + "animateProperties": { + "alpha": 0.38 + }, + "durationSeconds": 16.53 + } + ] + } + ] + } + }, + { + "ref": "Star", + "overrides": { + "position": { + "x": 336, + "y": 131 + }, + "xScale": 0.92, + "yScale": 0.92, + "alpha": 0.53, + "rotation": 0.032, + "tint": [ + 0.8, + 0.9, + 1.0 + ], + "animationTracks": [ + { + "steps": [ + { + "animateProperties": { + "xScale": 0.99, + "yScale": 0.99, + "alpha": 0.67 + } + }, + { + "animateProperties": { + "xScale": 0.92, + "yScale": 0.92, + "alpha": 0.53 + } + }, + { + "animateProperties": { + "alpha": 0.53 + }, + "durationSeconds": 22.8 + } + ] + } + ] + } + }, + { + "ref": "Star", + "overrides": { + "position": { + "x": 959, + "y": 38 + }, + "xScale": 0.32, + "yScale": 0.32, + "alpha": 0.37, + "rotation": 0.049, + "tint": [ + 0.8, + 0.9, + 1.0 + ], + "animationTracks": [ + { + "steps": [ + { + "animateProperties": { + "xScale": 0.35, + "yScale": 0.35, + "alpha": 0.83 + } + }, + { + "animateProperties": { + "xScale": 0.32, + "yScale": 0.32, + "alpha": 0.37 + } + }, + { + "animateProperties": { + "alpha": 0.37 + }, + "durationSeconds": 24.66 + } + ] + } + ] + } + }, + { + "ref": "Star", + "overrides": { + "position": { + "x": 1141, + "y": 187 + }, + "xScale": 0.3, + "yScale": 0.3, + "alpha": 0.3, + "rotation": 0.026, + "tint": [ + 0.8, + 0.9, + 1.0 + ], + "animationTracks": [ + { + "steps": [ + { + "animateProperties": { + "xScale": 0.31, + "yScale": 0.31, + "alpha": 0.39 + } + }, + { + "animateProperties": { + "xScale": 0.3, + "yScale": 0.3, + "alpha": 0.3 + } + }, + { + "animateProperties": { + "alpha": 0.3 + }, + "durationSeconds": 14.27 + } + ] + } + ] + } + }, + { + "ref": "Star", + "overrides": { + "position": { + "x": 536, + "y": 299 + }, + "xScale": 0.48, + "yScale": 0.48, + "alpha": 0.36, + "rotation": 0.012, + "tint": [ + 0.8, + 0.9, + 1.0 + ], + "animationTracks": [ + { + "steps": [ + { + "animateProperties": { + "xScale": 0.51, + "yScale": 0.51, + "alpha": 0.79 + } + }, + { + "animateProperties": { + "xScale": 0.48, + "yScale": 0.48, + "alpha": 0.36 + } + }, + { + "animateProperties": { + "alpha": 0.36 + }, + "durationSeconds": 16.02 + } + ] + } + ] + } + }, + { + "ref": "Star", + "overrides": { + "position": { + "x": 1149, + "y": 167 + }, + "xScale": 0.92, + "yScale": 0.92, + "alpha": 0.44, + "rotation": -0.021, + "tint": [ + 0.8, + 0.9, + 1.0 + ], + "animationTracks": [ + { + "steps": [ + { + "animateProperties": { + "xScale": 0.96, + "yScale": 0.96, + "alpha": 0.9 + } + }, + { + "animateProperties": { + "xScale": 0.92, + "yScale": 0.92, + "alpha": 0.44 + } + }, + { + "animateProperties": { + "alpha": 0.44 + }, + "durationSeconds": 23.46 + } + ] + } + ] + } + }, + { + "ref": "Star", + "overrides": { + "position": { + "x": 836, + "y": 59 + }, + "xScale": 0.32, + "yScale": 0.32, + "alpha": 0.31, + "rotation": 0.04, + "tint": [ + 0.8, + 0.9, + 1.0 + ], + "animationTracks": [ + { + "steps": [ + { + "animateProperties": { + "xScale": 0.34, + "yScale": 0.34, + "alpha": 0.65 + } + }, + { + "animateProperties": { + "xScale": 0.32, + "yScale": 0.32, + "alpha": 0.31 + } + }, + { + "animateProperties": { + "alpha": 0.31 + }, + "durationSeconds": 9.65 + } + ] + } + ] + } + }, + { + "ref": "Star", + "overrides": { + "position": { + "x": 277, + "y": 198 + }, + "xScale": 0.53, + "yScale": 0.53, + "alpha": 0.41, + "rotation": 0.025, + "tint": [ + 0.8, + 0.9, + 1.0 + ], + "animationTracks": [ + { + "steps": [ + { + "animateProperties": { + "xScale": 0.56, + "yScale": 0.56, + "alpha": 0.97 + } + }, + { + "animateProperties": { + "xScale": 0.53, + "yScale": 0.53, + "alpha": 0.41 + } + }, + { + "animateProperties": { + "alpha": 0.41 + }, + "durationSeconds": 15.13 + } + ] + } + ] + } + }, + { + "ref": "Star", + "overrides": { + "position": { + "x": 316, + "y": 212 + }, + "xScale": 0.69, + "yScale": 0.69, + "alpha": 0.43, + "rotation": 0.02, + "tint": [ + 0.8, + 0.9, + 1.0 + ], + "animationTracks": [ + { + "steps": [ + { + "animateProperties": { + "xScale": 0.74, + "yScale": 0.74, + "alpha": 0.97 + } + }, + { + "animateProperties": { + "xScale": 0.69, + "yScale": 0.69, + "alpha": 0.43 + } + }, + { + "animateProperties": { + "alpha": 0.43 + }, + "durationSeconds": 12.76 + } + ] + } + ] + } + }, + { + "ref": "Star", + "overrides": { + "position": { + "x": 444, + "y": 227 + }, + "xScale": 0.52, + "yScale": 0.52, + "alpha": 0.37, + "rotation": 0.042, + "tint": [ + 0.8, + 0.9, + 1.0 + ], + "animationTracks": [ + { + "steps": [ + { + "animateProperties": { + "xScale": 0.54, + "yScale": 0.54, + "alpha": 0.99 + } + }, + { + "animateProperties": { + "xScale": 0.52, + "yScale": 0.52, + "alpha": 0.37 + } + }, + { + "animateProperties": { + "alpha": 0.37 + }, + "durationSeconds": 14.24 + } + ] + } + ] + } + }, + { + "ref": "Star", + "overrides": { + "position": { + "x": 734, + "y": 31 + }, + "xScale": 0.49, + "yScale": 0.49, + "alpha": 0.33, + "rotation": 0.017, + "tint": [ + 0.8, + 0.9, + 1.0 + ], + "animationTracks": [ + { + "steps": [ + { + "animateProperties": { + "xScale": 0.51, + "yScale": 0.51, + "alpha": 0.9 + } + }, + { + "animateProperties": { + "xScale": 0.49, + "yScale": 0.49, + "alpha": 0.33 + } + }, + { + "animateProperties": { + "alpha": 0.33 + }, + "durationSeconds": 15.73 + } + ] + } + ] + } + }, + { + "ref": "Star", + "overrides": { + "position": { + "x": 1222, + "y": 10 + }, + "xScale": 0.28, + "yScale": 0.28, + "alpha": 0.37, + "rotation": 0.034, + "tint": [ + 0.8, + 0.9, + 1.0 + ], + "animationTracks": [ + { + "steps": [ + { + "animateProperties": { + "xScale": 0.29, + "yScale": 0.29, + "alpha": 0.87 + } + }, + { + "animateProperties": { + "xScale": 0.28, + "yScale": 0.28, + "alpha": 0.37 + } + }, + { + "animateProperties": { + "alpha": 0.37 + }, + "durationSeconds": 11.49 + } + ] + } + ] + } + }, + { + "ref": "Star", + "overrides": { + "position": { + "x": 1135, + "y": 106 + }, + "xScale": 0.62, + "yScale": 0.62, + "alpha": 0.38, + "rotation": 0.01, + "tint": [ + 0.8, + 0.9, + 1.0 + ], + "animationTracks": [ + { + "steps": [ + { + "animateProperties": { + "xScale": 0.66, + "yScale": 0.66, + "alpha": 0.86 + } + }, + { + "animateProperties": { + "xScale": 0.62, + "yScale": 0.62, + "alpha": 0.38 + } + }, + { + "animateProperties": { + "alpha": 0.38 + }, + "durationSeconds": 21.41 + } + ] + } + ] + } + }, + { + "ref": "Star", + "overrides": { + "position": { + "x": 567, + "y": 74 + }, + "xScale": 0.98, + "yScale": 0.98, + "alpha": 0.52, + "rotation": -0.038, + "tint": [ + 0.8, + 0.9, + 1.0 + ], + "animationTracks": [ + { + "steps": [ + { + "animateProperties": { + "xScale": 1.04, + "yScale": 1.04, + "alpha": 0.57 + } + }, + { + "animateProperties": { + "xScale": 0.98, + "yScale": 0.98, + "alpha": 0.52 + } + }, + { + "animateProperties": { + "alpha": 0.52 + }, + "durationSeconds": 22.73 + } + ] + } + ] + } + }, + { + "ref": "Star", + "overrides": { + "position": { + "x": 733, + "y": 121 + }, + "xScale": 0.62, + "yScale": 0.62, + "alpha": 0.38, + "rotation": -0.04, + "tint": [ + 0.8, + 0.9, + 1.0 + ], + "animationTracks": [ + { + "steps": [ + { + "animateProperties": { + "xScale": 0.64, + "yScale": 0.64, + "alpha": 0.46 + } + }, + { + "animateProperties": { + "xScale": 0.62, + "yScale": 0.62, + "alpha": 0.38 + } + }, + { + "animateProperties": { + "alpha": 0.38 + }, + "durationSeconds": 15.16 + } + ] + } + ] + } + }, + { + "ref": "Star", + "overrides": { + "position": { + "x": 130, + "y": 243 + }, + "xScale": 0.52, + "yScale": 0.52, + "alpha": 0.39, + "rotation": 0.012, + "tint": [ + 0.8, + 0.9, + 1.0 + ], + "animationTracks": [ + { + "steps": [ + { + "animateProperties": { + "xScale": 0.55, + "yScale": 0.55, + "alpha": 0.67 + } + }, + { + "animateProperties": { + "xScale": 0.52, + "yScale": 0.52, + "alpha": 0.39 + } + }, + { + "animateProperties": { + "alpha": 0.39 + }, + "durationSeconds": 11.77 + } + ] + } + ] + } + }, + { + "ref": "Star", + "overrides": { + "position": { + "x": 1067, + "y": 154 + }, + "xScale": 0.53, + "yScale": 0.53, + "alpha": 0.34, + "rotation": 0.019, + "tint": [ + 0.8, + 0.9, + 1.0 + ], + "animationTracks": [ + { + "steps": [ + { + "animateProperties": { + "xScale": 0.57, + "yScale": 0.57, + "alpha": 0.66 + } + }, + { + "animateProperties": { + "xScale": 0.53, + "yScale": 0.53, + "alpha": 0.34 + } + }, + { + "animateProperties": { + "alpha": 0.34 + }, + "durationSeconds": 14.83 + } + ] + } + ] + } + }, + { + "ref": "Star", + "overrides": { + "position": { + "x": 1004, + "y": 227 + }, + "xScale": 0.29, + "yScale": 0.29, + "alpha": 0.33, + "rotation": 0.014, + "tint": [ + 0.8, + 0.9, + 1.0 + ], + "animationTracks": [ + { + "steps": [ + { + "animateProperties": { + "xScale": 0.3, + "yScale": 0.3, + "alpha": 0.37 + } + }, + { + "animateProperties": { + "xScale": 0.29, + "yScale": 0.29, + "alpha": 0.33 + } + }, + { + "animateProperties": { + "alpha": 0.33 + }, + "durationSeconds": 12.68 + } + ] + } + ] + } + }, + { + "ref": "Star", + "overrides": { + "position": { + "x": 1123, + "y": 166 + }, + "xScale": 0.48, + "yScale": 0.48, + "alpha": 0.35, + "rotation": 0.03, + "tint": [ + 0.8, + 0.9, + 1.0 + ], + "animationTracks": [ + { + "steps": [ + { + "animateProperties": { + "xScale": 0.51, + "yScale": 0.51, + "alpha": 0.68 + } + }, + { + "animateProperties": { + "xScale": 0.48, + "yScale": 0.48, + "alpha": 0.35 + } + }, + { + "animateProperties": { + "alpha": 0.35 + }, + "durationSeconds": 15.31 + } + ] + } + ] + } + }, + { + "ref": "Star", + "overrides": { + "position": { + "x": 234, + "y": 282 + }, + "xScale": 0.74, + "yScale": 0.74, + "alpha": 0.43, + "rotation": -0.005, + "tint": [ + 0.8, + 0.9, + 1.0 + ], + "animationTracks": [ + { + "steps": [ + { + "animateProperties": { + "xScale": 0.77, + "yScale": 0.77, + "alpha": 0.56 + } + }, + { + "animateProperties": { + "xScale": 0.74, + "yScale": 0.74, + "alpha": 0.43 + } + }, + { + "animateProperties": { + "alpha": 0.43 + }, + "durationSeconds": 13.76 + } + ] + } + ] + } + }, + { + "ref": "Star", + "overrides": { + "position": { + "x": 851, + "y": 48 + }, + "xScale": 0.44, + "yScale": 0.44, + "alpha": 0.34, + "rotation": -0.003, + "tint": [ + 0.8, + 0.9, + 1.0 + ], + "animationTracks": [ + { + "steps": [ + { + "animateProperties": { + "xScale": 0.46, + "yScale": 0.46, + "alpha": 0.97 + } + }, + { + "animateProperties": { + "xScale": 0.44, + "yScale": 0.44, + "alpha": 0.34 + } + }, + { + "animateProperties": { + "alpha": 0.34 + }, + "durationSeconds": 19.68 + } + ] + } + ] + } + }, + { + "ref": "Star", + "overrides": { + "position": { + "x": 186, + "y": 43 + }, + "xScale": 0.3, + "yScale": 0.3, + "alpha": 0.35, + "rotation": 0.009, + "tint": [ + 0.8, + 0.9, + 1.0 + ], + "animationTracks": [ + { + "steps": [ + { + "animateProperties": { + "xScale": 0.31, + "yScale": 0.31, + "alpha": 0.88 + } + }, + { + "animateProperties": { + "xScale": 0.3, + "yScale": 0.3, + "alpha": 0.35 + } + }, + { + "animateProperties": { + "alpha": 0.35 + }, + "durationSeconds": 17.46 + } + ] + } + ] + } + }, + { + "ref": "Star", + "overrides": { + "position": { + "x": 1150, + "y": 287 + }, + "xScale": 0.44, + "yScale": 0.44, + "alpha": 0.35, + "rotation": 0.038, + "tint": [ + 0.8, + 0.9, + 1.0 + ], + "animationTracks": [ + { + "steps": [ + { + "animateProperties": { + "xScale": 0.46, + "yScale": 0.46, + "alpha": 0.78 + } + }, + { + "animateProperties": { + "xScale": 0.44, + "yScale": 0.44, + "alpha": 0.35 + } + }, + { + "animateProperties": { + "alpha": 0.35 + }, + "durationSeconds": 20.75 + } + ] + } + ] + } + }, + { + "ref": "Star", + "overrides": { + "position": { + "x": 105, + "y": 147 + }, + "xScale": 0.64, + "yScale": 0.64, + "alpha": 0.42, + "rotation": -0.029, + "tint": [ + 0.8, + 0.9, + 1.0 + ], + "animationTracks": [ + { + "steps": [ + { + "animateProperties": { + "xScale": 0.67, + "yScale": 0.67, + "alpha": 0.54 + } + }, + { + "animateProperties": { + "xScale": 0.64, + "yScale": 0.64, + "alpha": 0.42 + } + }, + { + "animateProperties": { + "alpha": 0.42 + }, + "durationSeconds": 19.16 + } + ] + } + ] + } + }, + { + "ref": "Star", + "overrides": { + "position": { + "x": 221, + "y": 179 + }, + "xScale": 0.85, + "yScale": 0.85, + "alpha": 0.49, + "rotation": 0.036, + "tint": [ + 0.8, + 0.9, + 1.0 + ], + "animationTracks": [ + { + "steps": [ + { + "animateProperties": { + "xScale": 0.89, + "yScale": 0.89, + "alpha": 0.78 + } + }, + { + "animateProperties": { + "xScale": 0.85, + "yScale": 0.85, + "alpha": 0.49 + } + }, + { + "animateProperties": { + "alpha": 0.49 + }, + "durationSeconds": 21.72 + } + ] + } + ] + } + }, + { + "ref": "Star", + "overrides": { + "position": { + "x": 1273, + "y": 314 + }, + "xScale": 0.71, + "yScale": 0.71, + "alpha": 0.44, + "rotation": -0.049, + "tint": [ + 0.8, + 0.9, + 1.0 + ], + "animationTracks": [ + { + "steps": [ + { + "animateProperties": { + "xScale": 0.75, + "yScale": 0.75, + "alpha": 0.81 + } + }, + { + "animateProperties": { + "xScale": 0.71, + "yScale": 0.71, + "alpha": 0.44 + } + }, + { + "animateProperties": { + "alpha": 0.44 + }, + "durationSeconds": 19.79 + } + ] + } + ] + } + }, + { + "ref": "Star", + "overrides": { + "position": { + "x": 559, + "y": 158 + }, + "xScale": 0.92, + "yScale": 0.92, + "alpha": 0.45, + "rotation": -0.045, + "tint": [ + 0.8, + 0.9, + 1.0 + ], + "animationTracks": [ + { + "steps": [ + { + "animateProperties": { + "xScale": 0.96, + "yScale": 0.96, + "alpha": 0.53 + } + }, + { + "animateProperties": { + "xScale": 0.92, + "yScale": 0.92, + "alpha": 0.45 + } + }, + { + "animateProperties": { + "alpha": 0.45 + }, + "durationSeconds": 19.17 + } + ] + } + ] + } + }, + { + "ref": "Star", + "overrides": { + "position": { + "x": 62, + "y": 46 + }, + "xScale": 0.76, + "yScale": 0.76, + "alpha": 0.43, + "rotation": 0.023, + "tint": [ + 0.8, + 0.9, + 1.0 + ], + "animationTracks": [ + { + "steps": [ + { + "animateProperties": { + "xScale": 0.79, + "yScale": 0.79, + "alpha": 0.62 + } + }, + { + "animateProperties": { + "xScale": 0.76, + "yScale": 0.76, + "alpha": 0.43 + } + }, + { + "animateProperties": { + "alpha": 0.43 + }, + "durationSeconds": 14.29 + } + ] + } + ] + } + } + ] +} \ No newline at end of file diff --git a/client/assets/themes/Panel Attack Modern/scenes/TitleScreen.json b/client/assets/themes/Panel Attack Modern/scenes/TitleScreen.json new file mode 100644 index 00000000..b22aad7e --- /dev/null +++ b/client/assets/themes/Panel Attack Modern/scenes/TitleScreen.json @@ -0,0 +1,666 @@ +{ + "drawables": [ + { + "id": "logoAssembly", + "anchor": "center", + "position": { + "x": 640, + "y": 290 + }, + "size": { + "width": 584, + "height": 416 + }, + "children": [ + { + "id": "backgroundBottomLeftGear", + "anchor": "topLeft", + "position": { + "x": 98, + "y": 240 + }, + "children": [ + { + "filePath": "Gear0", + "anchor": "topLeft", + "position": { + "x": 0, + "y": 0 + }, + "animationTracks": [ + { + "loopTrack": true, + "steps": [ + { + "animateProperties": { + "rotation": 6.28318 + }, + "durationSeconds": 20, + "easeType": "linear" + } + ] + } + ] + }, + { + "filePath": "Gear1", + "anchor": "topLeft", + "position": { + "x": 0, + "y": 0 + }, + "animationTracks": [ + { + "loopTrack": true, + "steps": [ + { + "animateProperties": { + "rotation": 6.28318 + }, + "durationSeconds": 20, + "easeType": "linear" + } + ] + } + ] + }, + { + "filePath": "Gear2", + "anchor": "topLeft", + "position": { + "x": 0, + "y": 0 + }, + "tint": [ + 1, + 0, + 0 + ], + "animationTracks": [ + { + "loopTrack": true, + "steps": [ + { + "animateProperties": { + "rotation": 6.28318 + }, + "durationSeconds": 20, + "easeType": "linear" + } + ] + } + ] + }, + { + "filePath": "Gear3", + "anchor": "topLeft", + "position": { + "x": 0, + "y": 0 + }, + "yoyo": true, + "animationTracks": [ + { + "yoyo": true, + "steps": [ + { + "animateProperties": { + "alpha": 0.8, + "xScale": 1.2, + "yScale": 1.2 + }, + "durationSeconds": 1, + "easeType": "quadinout" + } + ] + } + ] + }, + { + "filePath": "Gear4", + "anchor": "topLeft", + "stencil": true, + "alpha": 0.3, + "blendMode": "add", + "position": { + "x": 0, + "y": 0 + } + } + ] + }, + { + "id": "backgroundBottomRightGear", + "anchor": "topLeft", + "position": { + "x": 394, + "y": 240 + }, + "children": [ + { + "filePath": "Gear0", + "anchor": "topLeft", + "position": { + "x": 0, + "y": 0 + }, + "animationTracks": [ + { + "loopTrack": true, + "steps": [ + { + "animateProperties": { + "rotation": -6.28318 + }, + "durationSeconds": 20, + "easeType": "linear" + } + ] + } + ] + }, + { + "filePath": "Gear1", + "anchor": "topLeft", + "position": { + "x": 0, + "y": 0 + }, + "animationTracks": [ + { + "loopTrack": true, + "steps": [ + { + "animateProperties": { + "rotation": -6.28318 + }, + "durationSeconds": 20, + "easeType": "linear" + } + ] + } + ] + }, + { + "filePath": "Gear2", + "anchor": "topLeft", + "position": { + "x": 0, + "y": 0 + }, + "tint": [ + 1, + 0, + 0 + ], + "animationTracks": [ + { + "loopTrack": true, + "steps": [ + { + "animateProperties": { + "rotation": -6.28318 + }, + "durationSeconds": 20, + "easeType": "linear" + } + ] + } + ] + }, + { + "filePath": "Gear3", + "anchor": "topLeft", + "position": { + "x": 0, + "y": 0 + }, + "yoyo": true, + "animationTracks": [ + { + "yoyo": true, + "steps": [ + { + "animateProperties": { + "alpha": 0.8, + "xScale": 1.2, + "yScale": 1.2 + }, + "durationSeconds": 1, + "easeType": "quadinout" + } + ] + } + ] + }, + { + "filePath": "Gear4", + "anchor": "topLeft", + "stencil": true, + "alpha": 0.3, + "blendMode": "add", + "position": { + "x": 0, + "y": 0 + } + } + ] + }, + { + "id": "mainLogo", + "filePath": "PanelAttackLogoLayer0", + "anchor": "topLeft", + "position": { + "x": 0, + "y": 0 + } + }, + { + "id": "logoMid", + "filePath": "PanelAttackLogoLayer2", + "anchor": "topLeft", + "position": { + "x": 0, + "y": 0 + } + }, + { + "id": "ForegroundLeftGear", + "anchor": "topLeft", + "position": { + "x": 208, + "y": 312 + }, + "xScale": 0.6, + "yScale": 0.6, + "children": [ + { + "filePath": "Gear0", + "anchor": "topLeft", + "position": { + "x": 0, + "y": 0 + }, + "animationTracks": [ + { + "loopTrack": true, + "steps": [ + { + "animateProperties": { + "rotation": -6.28318 + }, + "durationSeconds": 20, + "easeType": "linear" + } + ] + } + ] + }, + { + "filePath": "Gear1", + "anchor": "topLeft", + "position": { + "x": 0, + "y": 0 + }, + "animationTracks": [ + { + "loopTrack": true, + "steps": [ + { + "animateProperties": { + "rotation": -6.28318 + }, + "durationSeconds": 20, + "easeType": "linear" + } + ] + } + ] + }, + { + "filePath": "Gear2", + "anchor": "topLeft", + "position": { + "x": 0, + "y": 0 + }, + "tint": [ + 0, + 1, + 0 + ], + "animationTracks": [ + { + "loopTrack": true, + "steps": [ + { + "animateProperties": { + "rotation": -6.28318 + }, + "durationSeconds": 20, + "easeType": "linear" + } + ] + } + ] + }, + { + "filePath": "Circle", + "anchor": "topLeft", + "xScale": 1.4, + "yScale": 1.4, + "position": { + "x": 0, + "y": 0 + } + }, + { + "filePath": "Gear3", + "anchor": "topLeft", + "position": { + "x": 0, + "y": 0 + }, + "yoyo": true, + "animationTracks": [ + { + "yoyo": true, + "steps": [ + { + "animateProperties": { + "alpha": 0.8, + "xScale": 1.2, + "yScale": 1.2 + }, + "durationSeconds": 1, + "easeType": "quadinout" + } + ] + } + ] + }, + { + "filePath": "Gear4", + "anchor": "topLeft", + "stencil": true, + "alpha": 0.3, + "blendMode": "add", + "position": { + "x": 0, + "y": 0 + } + } + ] + }, + { + "id": "ForegroundRighGear", + "anchor": "topLeft", + "position": { + "x": 320, + "y": 312 + }, + "xScale": 0.6, + "yScale": 0.6, + "children": [ + { + "filePath": "Gear0", + "anchor": "topLeft", + "position": { + "x": 0, + "y": 0 + }, + "animationTracks": [ + { + "loopTrack": true, + "steps": [ + { + "animateProperties": { + "rotation": 6.28318 + }, + "durationSeconds": 20, + "easeType": "linear" + } + ] + } + ] + }, + { + "filePath": "Gear1", + "anchor": "topLeft", + "position": { + "x": 0, + "y": 0 + }, + "animationTracks": [ + { + "loopTrack": true, + "steps": [ + { + "animateProperties": { + "rotation": 6.28318 + }, + "durationSeconds": 20, + "easeType": "linear" + } + ] + } + ] + }, + { + "filePath": "Gear2", + "anchor": "topLeft", + "position": { + "x": 0, + "y": 0 + }, + "tint": [ + 1, + 1, + 0 + ], + "animationTracks": [ + { + "loopTrack": true, + "steps": [ + { + "animateProperties": { + "rotation": 6.28318 + }, + "durationSeconds": 20, + "easeType": "linear" + } + ] + } + ] + }, + { + "filePath": "X", + "anchor": "topLeft", + "xScale": 1.4, + "yScale": 1.4, + "position": { + "x": 0, + "y": 0 + } + }, + { + "filePath": "Gear3", + "anchor": "topLeft", + "position": { + "x": 0, + "y": 0 + }, + "yoyo": true, + "animationTracks": [ + { + "yoyo": true, + "steps": [ + { + "animateProperties": { + "alpha": 0.8, + "xScale": 1.2, + "yScale": 1.2 + }, + "durationSeconds": 1, + "easeType": "quadinout" + } + ] + } + ] + }, + { + "filePath": "Gear4", + "anchor": "topLeft", + "stencil": true, + "alpha": 0.3, + "blendMode": "add", + "position": { + "x": 0, + "y": 0 + } + } + ] + }, + { + "id": "logoTop", + "filePath": "PanelAttackLogoLayer4", + "anchor": "topLeft", + "position": { + "x": 0, + "y": 0 + } + }, + { + "id": "ForegroundTopGear", + "anchor": "topLeft", + "position": { + "x": 258, + "y": 24 + }, + "xScale": 0.7, + "yScale": 0.7, + "children": [ + { + "filePath": "Gear0", + "anchor": "topLeft", + "position": { + "x": 0, + "y": 0 + }, + "animationTracks": [ + { + "loopTrack": true, + "steps": [ + { + "animateProperties": { + "rotation": 6.28318 + }, + "durationSeconds": 20, + "easeType": "linear" + } + ] + } + ] + }, + { + "filePath": "Gear1", + "anchor": "topLeft", + "position": { + "x": 0, + "y": 0 + }, + "animationTracks": [ + { + "loopTrack": true, + "steps": [ + { + "animateProperties": { + "rotation": 6.28318 + }, + "durationSeconds": 20, + "easeType": "linear" + } + ] + } + ] + }, + { + "filePath": "Gear2", + "anchor": "topLeft", + "position": { + "x": 0, + "y": 0 + }, + "tint": [ + 1, + 0, + 1 + ], + "animationTracks": [ + { + "loopTrack": true, + "steps": [ + { + "animateProperties": { + "rotation": 6.28318 + }, + "durationSeconds": 20, + "easeType": "linear" + } + ] + } + ] + }, + { + "filePath": "HourGlass", + "anchor": "topLeft", + "xScale": 1.4, + "yScale": 1.4, + "position": { + "x": 0, + "y": 0 + } + }, + { + "filePath": "Gear3", + "anchor": "topLeft", + "position": { + "x": 0, + "y": 0 + }, + "yoyo": true, + "animationTracks": [ + { + "yoyo": true, + "steps": [ + { + "animateProperties": { + "alpha": 0.8, + "xScale": 1.2, + "yScale": 1.2 + }, + "durationSeconds": 1, + "easeType": "quadinout" + } + ] + } + ] + }, + { + "filePath": "Gear4", + "anchor": "topLeft", + "stencil": true, + "alpha": 0.3, + "blendMode": "add", + "position": { + "x": 0, + "y": 0 + } + } + ] + } + ] + } + ] +} \ No newline at end of file diff --git a/client/assets/themes/Panel Attack Modern/scenes/Triangle@2x.png b/client/assets/themes/Panel Attack Modern/scenes/Triangle@2x.png new file mode 100644 index 00000000..3e29a6fa Binary files /dev/null and b/client/assets/themes/Panel Attack Modern/scenes/Triangle@2x.png differ diff --git a/client/assets/themes/Panel Attack Modern/scenes/WindowBackground.png b/client/assets/themes/Panel Attack Modern/scenes/WindowBackground.png new file mode 100644 index 00000000..b778b581 Binary files /dev/null and b/client/assets/themes/Panel Attack Modern/scenes/WindowBackground.png differ diff --git a/client/assets/themes/Panel Attack Modern/scenes/WindowFrame.png b/client/assets/themes/Panel Attack Modern/scenes/WindowFrame.png new file mode 100644 index 00000000..b16c2d69 Binary files /dev/null and b/client/assets/themes/Panel Attack Modern/scenes/WindowFrame.png differ diff --git a/client/assets/themes/Panel Attack Modern/scenes/WindowGlass.png b/client/assets/themes/Panel Attack Modern/scenes/WindowGlass.png new file mode 100644 index 00000000..228ac376 Binary files /dev/null and b/client/assets/themes/Panel Attack Modern/scenes/WindowGlass.png differ diff --git a/client/assets/themes/Panel Attack Modern/scenes/WindowGlow.png b/client/assets/themes/Panel Attack Modern/scenes/WindowGlow.png new file mode 100644 index 00000000..49c73b59 Binary files /dev/null and b/client/assets/themes/Panel Attack Modern/scenes/WindowGlow.png differ diff --git a/client/assets/themes/Panel Attack Modern/scenes/WindowLight.png b/client/assets/themes/Panel Attack Modern/scenes/WindowLight.png new file mode 100644 index 00000000..d728c6fe Binary files /dev/null and b/client/assets/themes/Panel Attack Modern/scenes/WindowLight.png differ diff --git a/client/assets/themes/Panel Attack Modern/scenes/Windows.json b/client/assets/themes/Panel Attack Modern/scenes/Windows.json new file mode 100644 index 00000000..6f4c29e3 --- /dev/null +++ b/client/assets/themes/Panel Attack Modern/scenes/Windows.json @@ -0,0 +1,566 @@ +{ + "drawables": [ + { + "id": "SingleWindow", + "templateOnly": true, + "filePath": "SingleWindowBackground", + "anchor": "center", + "children": [ + { + "filePath": "SingleWindowGlass" + }, + { + "filePath": "SingleWindowLight", + "tint": [ + 0.8862745098, + 0.6196078431, + 0.09019607843 + ], + "alpha": 0.4, + "blendMode": "add", + "animationTracks": [ + { + "yoyo": true, + "steps": [ + { + "animateProperties": { + "alpha": 0.8 + }, + "durationSeconds": 2, + "easeType": "linear" + } + ] + } + ] + }, + { + "filePath": "SingleWindowFrame" + }, + { + "filePath": "SingleWindowGlow", + "tint": [ + 0.8862745098, + 0.6196078431, + 0.09019607843 + ], + "alpha": 0.7, + "blendMode": "add", + "animationTracks": [ + { + "yoyo": true, + "steps": [ + { + "animateProperties": { + "alpha": 0.9 + }, + "durationSeconds": 2, + "easeType": "linear" + } + ] + } + ] + } + ] + }, + { + "id": "SingleWindowFront", + "ref": "SingleWindow", + "templateOnly": true, + "overrides": { + "xScale": 0.5, + "yScale": 0.5 + } + }, + { + "id": "SingleWindowMid", + "ref": "SingleWindow", + "templateOnly": true, + "overrides": { + "xScale": 0.4, + "yScale": 0.4 + } + }, + { + "id": "SingleWindowBack", + "ref": "SingleWindow", + "templateOnly": true, + "overrides": { + "xScale": 0.3, + "yScale": 0.3, + "children": [ + {}, + { + "tint": [ + 0.50, + 0.37, + 0.34 + ] + }, + {}, + { + "tint": [ + 0.50, + 0.37, + 0.34 + ] + } + ] + } + }, + { + "id": "DoubleWindow", + "templateOnly": true, + "filePath": "DoubleWindowBackground", + "anchor": "center", + "children": [ + { + "filePath": "DoubleWindowGlass" + }, + { + "filePath": "DoubleWindowLight", + "tint": [ + 0.8862745098, + 0.6196078431, + 0.09019607843 + ], + "alpha": 0.4, + "blendMode": "add", + "animationTracks": [ + { + "yoyo": true, + "steps": [ + { + "animateProperties": { + "alpha": 0.8 + }, + "durationSeconds": 2, + "easeType": "linear" + } + ] + } + ] + }, + { + "filePath": "DoubleWindowFrame" + }, + { + "filePath": "DoubleWindowGlow", + "tint": [ + 0.8862745098, + 0.6196078431, + 0.09019607843 + ], + "alpha": 0.7, + "blendMode": "add", + "animationTracks": [ + { + "yoyo": true, + "steps": [ + { + "animateProperties": { + "alpha": 0.9 + }, + "durationSeconds": 2, + "easeType": "linear" + } + ] + } + ] + } + ] + }, + { + "id": "DoubleWindowFront", + "ref": "DoubleWindow", + "templateOnly": true, + "overrides": { + "xScale": 0.8, + "yScale": 0.8 + } + }, + { + "id": "DoubleWindowMid", + "ref": "DoubleWindow", + "templateOnly": true, + "overrides": { + "xScale": 0.7, + "yScale": 0.7 + } + }, + { + "id": "DoubleWindowBack", + "ref": "DoubleWindow", + "templateOnly": true, + "overrides": { + "xScale": 0.6, + "yScale": 0.6, + "children": [ + {}, + { + "tint": [ + 0.50, + 0.37, + 0.34 + ] + }, + {}, + { + "tint": [ + 0.50, + 0.37, + 0.34 + ] + } + ] + } + }, + { + "id": "Window", + "templateOnly": true, + "filePath": "WindowBackground", + "anchor": "center", + "position": { + "x": 190, + "y": 460 + }, + "children": [ + { + "filePath": "WindowGlass" + }, + { + "filePath": "WindowLight", + "tint": [ + 0.8235294118, + 0.5882352941, + 0.1764705882 + ], + "alpha": 0.4, + "blendMode": "add", + "animationTracks": [ + { + "yoyo": true, + "steps": [ + { + "animateProperties": { + "alpha": 0.8 + }, + "durationSeconds": 2, + "easeType": "linear" + } + ] + } + ] + }, + { + "filePath": "WindowFrame" + }, + { + "filePath": "WindowGlow", + "tint": [ + 0.99, + 0.8235294118, + 0.3921568627 + ], + "alpha": 0.7, + "blendMode": "add", + "animationTracks": [ + { + "yoyo": true, + "steps": [ + { + "animateProperties": { + "alpha": 0.9 + }, + "durationSeconds": 2, + "easeType": "linear" + } + ] + } + ] + } + ] + }, + { + "id": "WindowFront", + "ref": "Window", + "templateOnly": true, + "overrides": { + "xScale": 0.5, + "yScale": 0.5 + } + }, + { + "id": "WindowMid", + "ref": "Window", + "templateOnly": true, + "overrides": { + "xScale": 0.4, + "yScale": 0.4 + } + }, + { + "id": "WindowBack", + "ref": "Window", + "templateOnly": true, + "overrides": { + "xScale": 0.3, + "yScale": 0.3, + "children": [ + {}, + { + "tint": [ + 0.50, + 0.37, + 0.34 + ] + }, + {}, + { + "tint": [ + 0.50, + 0.37, + 0.34 + ] + } + ] + } + }, + { + "ref": "SingleWindowMid", + "overrides": { + "position": { + "x": 906, + "y": 350 + } + } + }, + { + "ref": "WindowFront", + "overrides": { + "position": { + "x": 1182, + "y": 518 + }, + "children": [ + {}, + { + "tint": [ + 0.8235294118, + 0.5882352941, + 0.99 + ] + }, + {}, + { + "tint": [ + 0.8235294118, + 0.5882352941, + 0.99 + ] + } + ] + } + }, + { + "ref": "DoubleWindowMid", + "overrides": { + "position": { + "x": 202, + "y": 350 + } + } + }, + { + "ref": "SingleWindowMid", + "overrides": { + "position": { + "x": 164, + "y": 360 + } + } + }, + { + "ref": "SingleWindowMid", + "overrides": { + "position": { + "x": 240, + "y": 360 + } + } + }, + { + "ref": "WindowMid", + "overrides": { + "position": { + "x": 910, + "y": 524 + } + } + }, + { + "ref": "WindowMid", + "overrides": { + "position": { + "x": 933, + "y": 524 + } + } + }, + { + "ref": "WindowMid", + "overrides": { + "position": { + "x": 958, + "y": 524 + } + } + }, + { + "ref": "WindowMid", + "overrides": { + "position": { + "x": 992, + "y": 518 + } + } + }, + { + "ref": "WindowMid", + "overrides": { + "position": { + "x": 888, + "y": 524 + } + } + }, + { + "ref": "WindowMid", + "overrides": { + "position": { + "x": 936, + "y": 446 + } + } + }, + { + "ref": "WindowMid", + "overrides": { + "position": { + "x": 980, + "y": 468 + } + } + }, + { + "ref": "WindowFront", + "overrides": { + "position": { + "x": 1092, + "y": 586 + } + } + }, + { + "ref": "WindowFront", + "overrides": { + "position": { + "x": 1052, + "y": 586 + } + } + }, + { + "ref": "WindowFront", + "overrides": { + "position": { + "x": 1012, + "y": 586 + } + } + }, + { + "ref": "SingleWindowFront", + "overrides": { + "position": { + "x": 1050, + "y": 530 + } + } + }, + + + { + "ref" : "SingleWindowFront", + "overrides" : { + "position" : { + "x" : 226, + "y" : 493 + } + } + }, + { + "ref" : "SingleWindowFront", + "overrides" : { + "position" : { + "x" : 202, + "y" : 493 + } + } + }, + { + "ref" : "DoubleWindowFront", + "overrides" : { + "position" : { + "x" : 326, + "y" : 563 + } + } + }, + { + "ref" : "SingleWindowFront", + "overrides" : { + "position" : { + "x" : 461, + "y" : 638 + } + } + }, + { + "ref" : "SingleWindowFront", + "overrides" : { + "position" : { + "x" : 76, + "y" : 533 + } + } + }, + { + "ref" : "SingleWindowFront", + "overrides" : { + "position" : { + "x" : 111, + "y" : 533 + } + } + }, + { + "ref" : "SingleWindowFront", + "overrides" : { + "position" : { + "x" : 146, + "y" : 533 + } + } + }, + { + "ref" : "DoubleWindowFront", + "overrides" : { + "position" : { + "x" : 1131, + "y" : 478 + } + } + } + + ] +} \ No newline at end of file diff --git a/client/assets/themes/Panel Attack Modern/scenes/WindowsBack.json b/client/assets/themes/Panel Attack Modern/scenes/WindowsBack.json new file mode 100644 index 00000000..2b68565e --- /dev/null +++ b/client/assets/themes/Panel Attack Modern/scenes/WindowsBack.json @@ -0,0 +1,203 @@ +{ + "drawables": [ + { + "ref" : "Windows.json#SingleWindowBack", + "overrides" : { + "position" : { + "x" : 878, + "y" : 458 + } + } + }, + { + "ref" : "Windows.json#SingleWindowBack", + "overrides" : { + "position" : { + "x" : 858, + "y" : 458 + } + } + }, + { + "ref" : "Windows.json#SingleWindowBack", + "overrides" : { + "position" : { + "x" : 838, + "y" : 458 + } + } + }, + { + "ref" : "Windows.json#SingleWindowBack", + "overrides" : { + "position" : { + "x" : 878, + "y" : 424 + } + } + }, + { + "ref" : "Windows.json#SingleWindowBack", + "overrides" : { + "position" : { + "x" : 858, + "y" : 424 + } + } + }, + { + "ref" : "Windows.json#SingleWindowBack", + "overrides" : { + "position" : { + "x" : 838, + "y" : 424 + } + } + }, + { + "ref" : "Windows.json#SingleWindowBack", + "overrides" : { + "position" : { + "x" : 878, + "y" : 388 + } + } + }, + { + "ref" : "Windows.json#SingleWindowBack", + "overrides" : { + "position" : { + "x" : 858, + "y" : 388 + } + } + }, + { + "ref" : "Windows.json#WindowBack", + "overrides" : { + "position" : { + "x" : 369, + "y" : 393 + } + } + }, + { + "ref" : "Windows.json#WindowBack", + "overrides" : { + "position" : { + "x" : 399, + "y" : 393 + } + } + }, + { + "ref" : "Windows.json#WindowBack", + "overrides" : { + "position" : { + "x" : 434, + "y" : 393 + } + } + }, + { + "ref" : "Windows.json#DoubleWindowBack", + "overrides" : { + "position" : { + "x" : 567, + "y" : 496 + } + } + }, + { + "ref" : "Windows.json#DoubleWindowBack", + "overrides" : { + "position" : { + "x" : 522, + "y" : 496 + } + } + }, + { + "ref" : "Windows.json#WindowBack", + "overrides" : { + "position" : { + "x" : 464, + "y" : 393 + } + } + }, + { + "ref" : "Windows.json#WindowBack", + "overrides" : { + "position" : { + "x" : 1254, + "y" : 323 + } + } + }, + { + "ref" : "Windows.json#WindowBack", + "overrides" : { + "position" : { + "x" : 1209, + "y" : 323 + } + } + }, + { + "ref" : "Windows.json#WindowBack", + "overrides" : { + "position" : { + "x" : 1169, + "y" : 323 + } + } + }, + { + "ref" : "Windows.json#WindowBack", + "overrides" : { + "position" : { + "x" : 1034, + "y" : 292 + } + } + }, + { + "ref" : "Windows.json#WindowBack", + "overrides" : { + "position" : { + "x" : 61, + "y" : 238 + } + } + }, + { + "ref" : "Windows.json#WindowBack", + "overrides" : { + "position" : { + "x" : 269, + "y" : 328 + } + } + }, + { + "ref" : "Windows.json#WindowBack", + "overrides" : { + "position" : { + "x" : 504, + "y" : 288 + } + } + }, + { + "ref" : "Windows.json#SingleWindowBack", + "overrides" : { + "position" : { + "x" : 838, + "y" : 388 + } + } + } + + ] +} \ No newline at end of file diff --git a/client/assets/themes/Panel Attack Modern/scenes/WindowsFront.json b/client/assets/themes/Panel Attack Modern/scenes/WindowsFront.json new file mode 100644 index 00000000..d357516b --- /dev/null +++ b/client/assets/themes/Panel Attack Modern/scenes/WindowsFront.json @@ -0,0 +1,115 @@ +{ + "drawables": [ + { + "ref": "Windows.json#WindowFront", + "overrides": { + "position": { + "x": 1092, + "y": 586 + } + } + }, + { + "ref": "Windows.json#WindowFront", + "overrides": { + "position": { + "x": 1052, + "y": 586 + } + } + }, + { + "ref": "Windows.json#WindowFront", + "overrides": { + "position": { + "x": 1012, + "y": 586 + } + } + }, + { + "ref": "Windows.json#SingleWindowFront", + "overrides": { + "position": { + "x": 1050, + "y": 530 + } + } + }, + + + { + "ref" : "Windows.json#SingleWindowFront", + "overrides" : { + "position" : { + "x" : 226, + "y" : 493 + } + } + }, + { + "ref" : "Windows.json#SingleWindowFront", + "overrides" : { + "position" : { + "x" : 202, + "y" : 493 + } + } + }, + { + "ref" : "Windows.json#DoubleWindowFront", + "overrides" : { + "position" : { + "x" : 326, + "y" : 563 + } + } + }, + { + "ref" : "Windows.json#SingleWindowFront", + "overrides" : { + "position" : { + "x" : 461, + "y" : 638 + } + } + }, + { + "ref" : "Windows.json#SingleWindowFront", + "overrides" : { + "position" : { + "x" : 76, + "y" : 533 + } + } + }, + { + "ref" : "Windows.json#SingleWindowFront", + "overrides" : { + "position" : { + "x" : 111, + "y" : 533 + } + } + }, + { + "ref" : "Windows.json#SingleWindowFront", + "overrides" : { + "position" : { + "x" : 146, + "y" : 533 + } + } + }, + { + "ref" : "Windows.json#DoubleWindowFront", + "overrides" : { + "position" : { + "x" : 1131, + "y" : 478 + } + } + } + + ] +} \ No newline at end of file diff --git a/client/assets/themes/Panel Attack Modern/scenes/WindowsMid.json b/client/assets/themes/Panel Attack Modern/scenes/WindowsMid.json new file mode 100644 index 00000000..199f4650 --- /dev/null +++ b/client/assets/themes/Panel Attack Modern/scenes/WindowsMid.json @@ -0,0 +1,104 @@ +{ + "drawables": [ + { + "ref": "Windows.json#SingleWindowMid", + "overrides": { + "position": { + "x": 906, + "y": 350 + } + } + }, + { + "ref": "Windows.json#DoubleWindowMid", + "overrides": { + "position": { + "x": 202, + "y": 350 + } + } + }, + { + "ref": "Windows.json#SingleWindowMid", + "overrides": { + "position": { + "x": 164, + "y": 360 + } + } + }, + { + "ref": "Windows.json#SingleWindowMid", + "overrides": { + "position": { + "x": 240, + "y": 360 + } + } + }, + { + "ref": "Windows.json#WindowMid", + "overrides": { + "position": { + "x": 910, + "y": 524 + } + } + }, + { + "ref": "Windows.json#WindowMid", + "overrides": { + "position": { + "x": 933, + "y": 524 + } + } + }, + { + "ref": "Windows.json#WindowMid", + "overrides": { + "position": { + "x": 958, + "y": 524 + } + } + }, + { + "ref": "Windows.json#WindowMid", + "overrides": { + "position": { + "x": 992, + "y": 518 + } + } + }, + { + "ref": "Windows.json#WindowMid", + "overrides": { + "position": { + "x": 888, + "y": 524 + } + } + }, + { + "ref": "Windows.json#WindowMid", + "overrides": { + "position": { + "x": 936, + "y": 446 + } + } + }, + { + "ref": "Windows.json#WindowMid", + "overrides": { + "position": { + "x": 980, + "y": 468 + } + } + } + + ] +} \ No newline at end of file diff --git a/client/assets/themes/Panel Attack Modern/scenes/X@2x.png b/client/assets/themes/Panel Attack Modern/scenes/X@2x.png new file mode 100644 index 00000000..d89ce29e Binary files /dev/null and b/client/assets/themes/Panel Attack Modern/scenes/X@2x.png differ diff --git a/client/lib/flux/LICENSE b/client/lib/flux/LICENSE new file mode 100644 index 00000000..5818e8db --- /dev/null +++ b/client/lib/flux/LICENSE @@ -0,0 +1,20 @@ +Copyright (c) 2016 rxi + + +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/client/lib/flux/README.md b/client/lib/flux/README.md new file mode 100644 index 00000000..b54d5312 --- /dev/null +++ b/client/lib/flux/README.md @@ -0,0 +1,131 @@ +# flux +A fast, lightweight tweening library for Lua. + +## Installation +The [flux.lua](flux.lua?raw=1) file should be dropped into an existing project +and required by it. +```lua +flux = require "flux" +``` +The `flux.update()` function should be called at the start of each frame. As +its only argument It should be given the time in seconds that has passed since +the last call. +```lua +flux.update(deltatime) +``` + +## Usage +Any number of numerical values in a table can be tweened simultaneously. Tweens +are started by using the `flux.to()` function. This function requires 3 +arguments: +* `obj` The object which contains the variables to tween +* `time` The amount of time the tween should take to complete +* `vars` A table where the keys correspond to the keys in `obj` which should be + tweened, and their values correspond to the destination +```lua +-- Moves the ball object to the position 200, 300 over 4 seconds +flux.to(ball, 4, { x = 200, y = 300 }) +``` +If you try to tween a variable which is already being tweened, the original +tween stops tweening the variable and the new tween begins from the current +value. + +### Additional options +Additional options when creating a tween can be set through the use of chained +functions provided by the tween object which `flux.to()` returns. +```lua +flux.to(t, 4, { x = 10 }):ease("linear"):delay(1) +``` + +#### :ease(type) +The easing type which should be used by the tween; `type` should be a string +containing the name of the easing to be used. The library provides the +following easing types: + + `linear` + `quadin` `quadout` `quadinout` + `cubicin` `cubicout` `cubicinout` + `quartin` `quartout` `quartinout` + `quintin` `quintout` `quintinout` + `expoin` `expoout` `expoinout` + `sinein` `sineout` `sineinout` + `circin` `circout` `circinout` + `backin` `backout` `backinout` + `elasticin` `elasticout` `elasticinout` + +The default easing type is `quadout`. Examples of the different easing types +can be [found here](http://easings.net/). + + +#### :delay(time) +The amount of time flux should wait before starting the tween; `time` should be +a number of seconds. The default delay time is `0`. + +#### :onstart(fn) +Sets the function `fn` to be called when the tween starts (once the delay has +finished). `:onstart()` can be called multiple times to add more than one +function. + +#### :onupdate(fn) +Sets the function `fn` to be called each frame the tween updates a value. +`onupdate()` can be called multiple times to add more than one function. + +#### :oncomplete(fn) +Sets the function `fn` to be called once the tween has finished and reached its +destination values. `oncomplete()` can be called multiple times to add more +than one function. + +#### :after([obj,] time, vars) +Creates a new tween and chains it to the end of the existing tween; the chained +tween will be called after the original one has finished. Any additional +chained function used after `:after()` will effect the chained tween. There is +no limit to how many times `:after()` can be used in a chain, allowing the +creation of long tween sequences. If `obj` is not specified the `obj` argument +from the original tween is used. +```lua +-- Tweens t.x to 10 over 2 seconds, then to 20 over 1 second +flux.to(t, 2, { x = 10 }):after(t, 1, { x = 20 }) +``` + +### Stopping a tween +If you want the ability to stop a tween before it has finished, the tween +should be assigned to a variable when it is created. +```lua +local tween = flux.to(x, 2, { y = 20 }):delay(1) +``` +The tween can then be stopped at any point by calling its `:stop()` method. +```lua +tween:stop() +``` +This will cause the tween to immediatly be removed from its parent group and +will leave its tweened variables at their current values. The tween's +`oncomplete()` callback is not called. + +### Groups +flux provides the ability to create tween groups; these are objects +which can have tweens added to them, and who are in charge of updating and +handling their contained tweens. A group is created by calling the +`flux.group()` function. +```lua +group = flux.group() +``` +Once a group is created it acts independently of the `flux` object, and must +be updated each frame using its own update method. +```lua +group:update(deltatime) +``` +To add a tween to a group, the group's `to()` method should be used. +```lua +group:to(t, 3, { x = 10, y = 20 }) +``` +A good example of where groups are useful is for games where you may have a set +of tweens which effect objects in the game world and which you want to pause +when the game is paused. A group's tweens can be paused by simply neglecting +to call its `update()` method; when a group is destroyed its tweens are also +destroyed. + + +## License +This library is free software; you can redistribute it and/or modify it under +the terms of the MIT license. See [LICENSE](LICENSE) for details. + diff --git a/client/lib/flux/flux.lua b/client/lib/flux/flux.lua new file mode 100644 index 00000000..d34da902 --- /dev/null +++ b/client/lib/flux/flux.lua @@ -0,0 +1,228 @@ +-- +-- flux +-- +-- Copyright (c) 2016 rxi +-- +-- This library is free software; you can redistribute it and/or modify it +-- under the terms of the MIT license. See LICENSE for details. +-- + +local flux = { _version = "0.1.5" } +flux.__index = flux + +flux.tweens = {} +flux.easing = { linear = function(p) return p end } + +local easing = { + quad = "p * p", + cubic = "p * p * p", + quart = "p * p * p * p", + quint = "p * p * p * p * p", + expo = "2 ^ (10 * (p - 1))", + sine = "-math.cos(p * (math.pi * .5)) + 1", + circ = "-(math.sqrt(1 - (p * p)) - 1)", + back = "p * p * (2.7 * p - 1.7)", + elastic = "-(2^(10 * (p - 1)) * math.sin((p - 1.075) * (math.pi * 2) / .3))" +} + +local makefunc = function(str, expr) + local load = loadstring or load + return load("return function(p) " .. str:gsub("%$e", expr) .. " end")() +end + +for k, v in pairs(easing) do + flux.easing[k .. "in"] = makefunc("return $e", v) + flux.easing[k .. "out"] = makefunc([[ + p = 1 - p + return 1 - ($e) + ]], v) + flux.easing[k .. "inout"] = makefunc([[ + p = p * 2 + if p < 1 then + return .5 * ($e) + else + p = 2 - p + return .5 * (1 - ($e)) + .5 + end + ]], v) +end + + + +local tween = {} +tween.__index = tween + +local function makefsetter(field) + return function(self, x) + local mt = getmetatable(x) + if type(x) ~= "function" and not (mt and mt.__call) then + error("expected function or callable", 2) + end + local old = self[field] + self[field] = old and function() old() x() end or x + return self + end +end + +local function makesetter(field, checkfn, errmsg) + return function(self, x) + if checkfn and not checkfn(x) then + error(errmsg:gsub("%$x", tostring(x)), 2) + end + self[field] = x + return self + end +end + +tween.ease = makesetter("_ease", + function(x) return flux.easing[x] end, + "bad easing type '$x'") +tween.delay = makesetter("_delay", + function(x) return type(x) == "number" end, + "bad delay time; expected number") +tween.onstart = makefsetter("_onstart") +tween.onupdate = makefsetter("_onupdate") +tween.oncomplete = makefsetter("_oncomplete") + + +function tween.new(obj, time, vars) + local self = setmetatable({}, tween) + self.obj = obj + self.rate = time > 0 and 1 / time or 0 + self.progress = time > 0 and 0 or 1 + self._delay = 0 + self._ease = "quadout" + self.vars = {} + for k, v in pairs(vars) do + if type(v) ~= "number" then + error("bad value for key '" .. k .. "'; expected number") + end + self.vars[k] = v + end + return self +end + + +function tween:init() + for k, v in pairs(self.vars) do + local x = self.obj[k] + if type(x) ~= "number" then + error("bad value on object key '" .. k .. "'; expected number") + end + self.vars[k] = { start = x, diff = v - x } + end + self.inited = true +end + + +function tween:after(...) + local t + if select("#", ...) == 2 then + t = tween.new(self.obj, ...) + else + t = tween.new(...) + end + t.parent = self.parent + self:oncomplete(function() flux.add(self.parent, t) end) + return t +end + + +function tween:stop() + flux.remove(self.parent, self) +end + + + +function flux.group() + return setmetatable({}, flux) +end + + +function flux:to(obj, time, vars) + return flux.add(self, tween.new(obj, time, vars)) +end + + +function flux:update(deltatime) + for i = #self, 1, -1 do + local t = self[i] + if t._delay > 0 then + t._delay = t._delay - deltatime + else + if not t.inited then + flux.clear(self, t.obj, t.vars) + t:init() + end + if t._onstart then + t._onstart() + t._onstart = nil + end + t.progress = t.progress + t.rate * deltatime + local p = t.progress + local x = p >= 1 and 1 or flux.easing[t._ease](p) + for k, v in pairs(t.vars) do + t.obj[k] = v.start + x * v.diff + end + if t._onupdate then t._onupdate() end + if p >= 1 then + flux.remove(self, i) + if t._oncomplete then t._oncomplete() end + end + end + end +end + + +function flux:clear(obj, vars) + local objTweens = self[obj] + if not objTweens or type(objTweens) ~= "table" then + return + end + for t in pairs(objTweens) do + if t.inited then + for k in pairs(vars) do t.vars[k] = nil end + end + end +end + + +function flux:add(tween) + -- Add to object table, create table if it does not exist + local obj = tween.obj + self[obj] = self[obj] or {} + self[obj][tween] = true + -- Add to array + table.insert(self, tween) + tween.parent = self + return tween +end + + +function flux:remove(x) + if type(x) == "number" then + -- Remove from object table, destroy table if it is empty + local obj = self[x].obj + self[obj][self[x]] = nil + if not next(self[obj]) then self[obj] = nil end + -- Remove from array + self[x] = self[#self] + return table.remove(self) + end + for i, v in ipairs(self) do + if v == x then + return flux.remove(self, i) + end + end +end + + + +local bound = { + to = function(...) return flux.to(flux.tweens, ...) end, + update = function(...) return flux.update(flux.tweens, ...) end, + remove = function(...) return flux.remove(flux.tweens, ...) end, +} +setmetatable(bound, flux) + +return bound diff --git a/client/src/Game.lua b/client/src/Game.lua index 5ea3a22d..1756d7f9 100644 --- a/client/src/Game.lua +++ b/client/src/Game.lua @@ -204,7 +204,9 @@ function Game:writeReleaseStreamDefinition() } -- this will only start to be active on next startup - love.filesystem.write("releaseStreams.json", json.encode(releaseStreamDefinition)) + local encodedReleaseStreams = json.encode(releaseStreamDefinition) + ---@cast encodedReleaseStreams string + love.filesystem.write("releaseStreams.json", encodedReleaseStreams) -- this is for the assumption that a release stream is being retired -- comment in / out as fit depending on release @@ -215,7 +217,9 @@ function Game:writeReleaseStreamDefinition() { activeReleaseStream = releaseStreamDefinition.default } - love.filesystem.write("updater/launch.json", json.encode(launchDefinition)) + local encodedLaunchDefinition = json.encode(launchDefinition) + ---@cast encodedLaunchDefinition string + love.filesystem.write("updater/launch.json", encodedLaunchDefinition) end end end diff --git a/client/src/NavigationStack.lua b/client/src/NavigationStack.lua index e9ef669c..12aeb8a2 100644 --- a/client/src/NavigationStack.lua +++ b/client/src/NavigationStack.lua @@ -147,6 +147,9 @@ function NavigationStack:update(dt) self.transition:update(dt) if self.transition.progress >= 1 then + if self.transition.oldScene then + self.transition.oldScene:sceneDidDissappear() + end self.transition = nil self.scenes[#self.scenes]:applyMusic() if self.callback then diff --git a/client/src/Shortcuts.lua b/client/src/Shortcuts.lua index 2d428a57..246a58d8 100644 --- a/client/src/Shortcuts.lua +++ b/client/src/Shortcuts.lua @@ -54,7 +54,9 @@ local function handleCopy() if tableUtils.length(stacks) > 0 then local encodeArguments = {indent = true, keyorder = {"P1", "P2", "Player", "Stop", "Shake", "Stack"}} - love.system.setClipboardText(json.encode(stacks, encodeArguments)) + local encodedStacks = json.encode(stacks, encodeArguments) + ---@cast encodedStacks string + love.system.setClipboardText(encodedStacks) return true end end diff --git a/client/src/analytics.lua b/client/src/analytics.lua index 2b9ad22d..fc4d1bb6 100644 --- a/client/src/analytics.lua +++ b/client/src/analytics.lua @@ -282,7 +282,9 @@ local function write_analytics_files() return end - love.filesystem.write("analytics.json", json.encode(analytics_data)) + local encodedAnalytics = json.encode(analytics_data) + ---@cast encodedAnalytics string + love.filesystem.write("analytics.json", encodedAnalytics) end ) output_pretty_analytics() diff --git a/client/src/config.lua b/client/src/config.lua index 32dda586..f9ddb842 100644 --- a/client/src/config.lua +++ b/client/src/config.lua @@ -55,6 +55,7 @@ require("client.src.globals") ---@field display integer ---@field windowX number? ---@field windowY number? +---@alias DebugConfig UserConfig config = { -- The last used engine version version = consts.ENGINE_VERSION, @@ -144,7 +145,9 @@ config = { function write_conf_file() pcall( function() - love.filesystem.write("conf.json", json.encode(config)) + local encodedConfig = json.encode(config) + ---@cast encodedConfig string + love.filesystem.write("conf.json", encodedConfig) end ) end diff --git a/client/src/graphics/AnimationLoader.lua b/client/src/graphics/AnimationLoader.lua new file mode 100644 index 00000000..d42ca452 --- /dev/null +++ b/client/src/graphics/AnimationLoader.lua @@ -0,0 +1,470 @@ +local class = require("common.lib.class") +local GraphicsUtil = require("client.src.graphics.graphics_util") +local FileUtils = require("client.src.FileUtils") +local Flux = require("client.lib.flux.flux") + +-- A class that loads a heirarchy of images and animations from an animation file and returns the drawable objects. Animations are setup to update with the flux engine. +---@class AnimationLoader +local AnimationLoader = + class( + function(self) + end +) + +-- Returns the offset to apply for the drawable and it's given anchor. +function AnimationLoader.anchorOffset(drawable, anchor) + if anchor == "topLeft" then + return 0, 0 + end + + local w, h = drawable.width, drawable.height + + if anchor == "topCenter" then + return w/2, 0 + elseif anchor == "topRight" then + return w, 0 + elseif anchor == "centerLeft" then + return 0, h/2 + elseif anchor == "center" then + return w/2, h/2 + elseif anchor == "centerRight" then + return w, h/2 + elseif anchor == "bottomLeft" then + return 0, h + elseif anchor == "bottomCenter" then + return w/2, h + elseif anchor == "bottomRight" then + return w, h + else + return 0, 0 -- fallback to topLeft + end +end + +-- Builds the flux animation for the given track step on the target +local function buildTrackStep(target, track, currentStep, stepAmount) + local step = track.steps[currentStep] + local targetProperties + if stepAmount > 0 then + targetProperties = step.animateProperties + else + targetProperties = step.reverseProperties + end + local tween = Flux.to(target, step.durationSeconds, + targetProperties) + target.fluxTweens[#target.fluxTweens+1] = tween + + -- Save off the previous properties on the track in case they are needed for looping or yoyo + if stepAmount > 0 then + tween:onstart(function () + step.reverseProperties = {} + for k in pairs(step.animateProperties) do + step.reverseProperties[k] = target[k] + end + end) + end + + tween:ease(step.easeType):delay(step.delaySeconds or 0) + + tween:oncomplete(function() + local nextStep = currentStep + stepAmount + if nextStep < 1 or nextStep > #track.steps then + if track.yoyo then + -- start going throught the steps backwards + stepAmount = stepAmount * -1 + nextStep = #track.steps + if stepAmount > 0 then + nextStep = 1 + end + elseif track.loopTrack then + nextStep = 1 + -- Reset to the first properties in reverse order in case some properties aren't covered by all steps + for stepIndex = #track.steps, 1, -1 do + for key, value in pairs(track.steps[stepIndex].reverseProperties) do + target[key] = value + end + end + end + end + + if nextStep >= 1 and nextStep <= #track.steps then + buildTrackStep(target, track, nextStep, stepAmount) + end + end) +end + +-- Loads one drawable from the given file directory root and the given animation data. +-- @return table the drawable or nil if it didn't need to be loaded +local function loadDrawables(rootPath, animationData) + if animationData.ref == nil and animationData.templateOnly then + -- Skip templateOnly items unless they are references + return nil + end + + assert((animationData.size == nil or animationData.size.width == nil) or animationData.xScale == nil, "Node has both xScale and width set, pick only one way to specify the width") + assert((animationData.size == nil or animationData.size.height == nil) or animationData.yScale == nil, "Node has both yScale and height set, pick only one way to specify the height") + local obj = { + id = animationData.id, + x = animationData.position and animationData.position.x or 0, + y = animationData.position and animationData.position.y or 0, + width = animationData.size and animationData.size.width, + height = animationData.size and animationData.size.height, + rotation = animationData.rotation or 0, + xScale = animationData.xScale, + yScale = animationData.yScale, + alpha = animationData.alpha or 1, + blendMode = animationData.blendMode or "alpha", + alphaMode = animationData.alphaMode or "alphamultiply", + stencil = animationData.stencil == true or false, + tint = animationData.tint or {1, 1, 1}, + anchor = animationData.anchor or "topLeft", + pivot = animationData.pivot, + animationTracks = animationData.animationTracks or {}, + originalRef = animationData.originalRef + } + obj.children = {} + if animationData.filePath then + local texture = GraphicsUtil.loadImageFromSupportedExtensions(rootPath .. animationData.filePath) + assert(texture, "couldn't load image for animation filepath " .. rootPath .. animationData.filePath) + if texture then + obj.texture = texture + local textureWidth = texture:getWidth() + local textureHeight = texture:getHeight() + if obj.width then + obj.xScale = obj.width / textureWidth + end + obj.width = textureWidth + if obj.height then + obj.yScale = obj.height / textureHeight + end + obj.height = textureHeight + + if animationData.wrap == "repeat" then + obj.scrollX = 0 + obj.scrollY = 0 + obj.texture:setWrap("repeat", "repeat") + obj.quad = love.graphics.newQuad(obj.scrollX, obj.scrollY, obj.width, obj.height, obj.width, obj.height) + end + end + end + + if obj.pivot == nil then + if obj.texture then + obj.pivot = "center" + else + obj.pivot = "topLeft" -- Allows containers to work if they don't set height and width + end + end + + if obj.xScale == nil then + obj.xScale = 1 + end + if obj.yScale == nil then + obj.yScale = 1 + end + + assert(obj.anchor == "topLeft" or obj.width > 0 and obj.height > 0, "Objects must have width and height if you have a non top left anchor") + assert(obj.pivot == "topLeft" or obj.width > 0 and obj.height > 0, "Objects must have width and height if you have a non top left pivot") + + obj.fluxTweens = {} + for _,track in ipairs(animationData.animationTracks or {}) do + buildTrackStep(obj, track, 1, 1) + end + for _, childData in ipairs(animationData.children or {}) do + local drawable = loadDrawables(rootPath, childData) + if drawable then + obj.children[#obj.children+1] = drawable + end + end + + return obj +end + +function AnimationLoader.applyOverridesRecursively(destinationTable, overridesTable) + for key, overrideValue in pairs(overridesTable) do + local destinationValue = destinationTable[key] + + if type(overrideValue) == "table" + and type(destinationValue) == "table" then + AnimationLoader.applyOverridesRecursively(destinationValue, overrideValue) + else + destinationTable[key] = overrideValue + end + end +end + +function AnimationLoader.cloneNodeWithOverrides(sourceNode, overridesTable) + assert(sourceNode and sourceNode.id) + local clonedNode = deepcpy(sourceNode) + + if overridesTable ~= nil then + AnimationLoader.applyOverridesRecursively(clonedNode, overridesTable) + end + + return clonedNode +end + +-- Loads all files referenced and records a map of ID's to their nodes +function AnimationLoader.indexNodesRecursively(nodeTable, filePath, idLookupTable, loadedFileTables) + if nodeTable.ref ~= nil and nodeTable.ref:match("(.*%.json)#?") then + local directoryPart = filePath:match("(.*/)") or "" + local referencedFile = nodeTable.ref:match("(.*%.json)#?") + local referencedFilePath = directoryPart .. referencedFile + AnimationLoader.readFileAndCollectIdsRecursively(referencedFilePath, loadedFileTables, idLookupTable) + end + if nodeTable.id ~= nil then + local qualifiedId = nodeTable.id + + if idLookupTable[qualifiedId] ~= nil then + error("Duplicate id detected: " .. qualifiedId) + end + + idLookupTable[qualifiedId] = nodeTable + end + + if nodeTable.children ~= nil then + for _, childNode in ipairs(nodeTable.children) do + AnimationLoader.indexNodesRecursively(childNode, filePath, idLookupTable, loadedFileTables) + end + end +end + +function AnimationLoader.readFileAndCollectIdsRecursively(filePath, loadedFileTables, idLookupTable) + if loadedFileTables[filePath] ~= nil then + return + end + + local sceneTable = FileUtils.readJsonFile(filePath) + + sceneTable.filePath = filePath + loadedFileTables[filePath] = sceneTable + + if sceneTable then + if sceneTable.drawables ~= nil then + for _, drawableNode in ipairs(sceneTable.drawables) do + AnimationLoader.indexNodesRecursively(drawableNode, filePath, idLookupTable, loadedFileTables) + end + end + end +end + +-- Changes the nodeTable so it has all the same properties as the reference with overrides applied. Recursive. +function AnimationLoader.resolveNodeReference(nodeTable, idLookupTable) + if nodeTable.ref == nil then + return -- nothing to resolve + end + + local originalRef = nodeTable.ref:match("#(.+)$") + + if originalRef == nil then + originalRef = nodeTable.ref + end + assert(originalRef ~= nil) + local sourceNode = idLookupTable[originalRef] + + if sourceNode == nil then + error("Unknown ref: " .. tostring(originalRef)) + end + + -- Validate the reference doesn't have invalid keys, the user likely intended overrides. + for key in pairs(nodeTable) do + assert(key == "id" or + key == "ref" or + key == "overrides" or + key == "templateOnly", "Reference has unexpected key " .. key .. " did you mean to override instead?") + end + + if sourceNode.ref then + AnimationLoader.resolveNodeReference(sourceNode, idLookupTable) + end + + local clonedNode = AnimationLoader.cloneNodeWithOverrides( + sourceNode, + nodeTable.overrides + ) + clonedNode.id = nodeTable.id + + for key, value in pairs(clonedNode) do + if key ~= "templateOnly" then + nodeTable[key] = value + end + end + nodeTable.overrides = nil + nodeTable.ref = nil + nodeTable.originalRef = originalRef + +end + +function AnimationLoader.resolveRefsRecursively(nodeTable, loadedFileTables, idLookupTable, visitedTables, filePath, rootPath) + if type(nodeTable) ~= "table" then + return + end + + if visitedTables[nodeTable] ~= nil then + error("Circular ref detected at " .. tostring(nodeTable.ref)) + end + + visitedTables[nodeTable] = true + + if nodeTable.ref ~= nil then + + -- Check if this is a file reference (ends with .json) + if nodeTable.ref:match("%.json$") then + local directoryPart = filePath:match("(.*/)") or "" + local referencedFilePath = directoryPart .. nodeTable.ref + local referencedFileTable = loadedFileTables[referencedFilePath] + + nodeTable.ref = nil + nodeTable.children = referencedFileTable.drawables + -- fall through to children now + else + AnimationLoader.resolveNodeReference(nodeTable, idLookupTable) + end + end + + if nodeTable.children ~= nil then + for _, childNode in ipairs(nodeTable.children) do + AnimationLoader.resolveRefsRecursively(childNode, loadedFileTables, idLookupTable, visitedTables, filePath, rootPath) + end + end +end + +-- Loads all drawables for a animation file. +-- @param rootPath String the full directory to the file to load +-- @param fillPath String just the file name part of the file to load +-- @return table an array of drawables representing all the objects with flux animations setup +function AnimationLoader.loadFromFile(rootPath, filePath) + local loadedFileTables = {} -- filePath → parsed table + local idLookupTable = {} -- "id" → node table + + -- First all files are loaded from JSON and indexed + AnimationLoader.readFileAndCollectIdsRecursively(filePath, loadedFileTables, idLookupTable) + + local rootData = loadedFileTables[filePath] + + -- Then all references are resolved with overrides applied + for _, drawable in ipairs(rootData.drawables) do + AnimationLoader.resolveRefsRecursively(drawable, loadedFileTables, idLookupTable, {}, filePath, rootPath) + end + + -- Finally once the full data structure is setup, it is converted into separate drawable objects. + local drawables = {} + if rootData then + for _, data in ipairs(rootData.drawables) do + local drawable = loadDrawables(rootPath, data) + if drawable then + drawables[#drawables+1] = drawable + end + end + end + return drawables +end + +function AnimationLoader.stopFluxTweensOnDrawable(drawable) + for _, tween in ipairs(drawable.fluxTweens) do + tween:stop() + end + for _, child in ipairs(drawable.children) do + AnimationLoader.stopFluxTweensOnDrawable(child) + end +end + +-- DRAWING CODE + +local loveMajor = love.getVersion() + +-- Shader used for clipping using a stencil pass +local alphaDiscardShader = love.graphics.newShader([[ + vec4 effect(vec4 tintColor, Image tex, vec2 texCoord, vec2 screenCoord) + { + vec4 pixelColor = Texel(tex, texCoord); + + if (pixelColor.a * tintColor.a <= 0.0) + { + discard; + } + + return vec4(0.0); + } +]]) + +function AnimationLoader.objectTransform(obj) + local ax, ay = AnimationLoader.anchorOffset(obj, obj.anchor) + return obj.x - ax, obj.y - ay, obj.rotation, obj.xScale, obj.yScale +end + + +local function drawSiblingsBeforeNode(parent, node) + love.graphics.setShader(alphaDiscardShader) + for _, sibling in ipairs(parent.children) do + if sibling == node then + break + end + AnimationLoader.drawNode(sibling, nil) + end + love.graphics.setShader() +end + +function AnimationLoader.drawNode(d, parent) + love.graphics.push("all") + + local px, py = AnimationLoader.anchorOffset(d, d.pivot) + local wx, wy, wrot, xScale, yScale = AnimationLoader.objectTransform(d) + + love.graphics.translate(wx, wy) + + love.graphics.translate(px, py) + love.graphics.rotate(wrot) + love.graphics.scale(xScale, yScale) + love.graphics.translate(-px, -py) + + if d.texture then + local usesStencil = d.stencil + local usesBlendAlphaMode = d.blendMode ~= "alpha" or d.alphaMode ~= "alphamultiply" + + if usesStencil then + assert(parent, "To use a stencil you need siblings") + if loveMajor >= 12 then + love.graphics.setStencilMode("draw", 1) + drawSiblingsBeforeNode(parent, d) + love.graphics.setStencilMode("test", 1) + else + love.graphics.stencil(function() + drawSiblingsBeforeNode(parent, d) + end, "replace", 1, false) + love.graphics.setStencilTest("equal", 1) + end + end + + if usesBlendAlphaMode then + love.graphics.setBlendMode(d.blendMode, d.alphaMode) + end + + love.graphics.setColor(d.tint[1], d.tint[2], d.tint[3], d.alpha) + if d.quad then + d.quad:setViewport(d.scrollX, d.scrollY, d.width, d.height) + love.graphics.draw(d.texture, d.quad, 0, 0) + else + love.graphics.draw(d.texture, 0, 0) + end + + if usesBlendAlphaMode then + love.graphics.setBlendMode("alpha", "alphamultiply") + end + + if usesStencil then + if loveMajor >= 12 then + love.graphics.setStencilMode() + else + love.graphics.setStencilTest() + end + end + end + + for _, child in ipairs(d.children) do + AnimationLoader.drawNode(child, d) + end + + love.graphics.pop() +end + +return AnimationLoader \ No newline at end of file diff --git a/client/src/scenes/MainMenu.lua b/client/src/scenes/MainMenu.lua index 8054187c..542da5e6 100644 --- a/client/src/scenes/MainMenu.lua +++ b/client/src/scenes/MainMenu.lua @@ -2,6 +2,7 @@ local Scene = require("client.src.scenes.Scene") local consts = require("common.engine.consts") local ui = require("client.src.ui") local GraphicsUtil = require("client.src.graphics.graphics_util") +local AnimationLoader = require("client.src.graphics.AnimationLoader") local class = require("common.lib.class") local GameModes = require("common.data.GameModes") local EndlessMenu = require("client.src.scenes.EndlessMenu") @@ -26,11 +27,15 @@ local VsSelfGame = require("client.src.scenes.VsSelfGame") local GameBase = require("client.src.scenes.GameBase") local PuzzleGame = require("client.src.scenes.PuzzleGame") + local SCENE_PATH = themes[config.theme].path .. "/scenes/" +local SCENE_FILENAME = "MainMenu.json" + -- Scene for the main menu local MainMenu = class(function(self, sceneParams) self.music = "main" self.menu = self:createMainMenu() self.uiRoot:addChild(self.menu) + self.drawables = AnimationLoader.loadFromFile(SCENE_PATH, SCENE_PATH .. SCENE_FILENAME) end, Scene) MainMenu.name = "MainMenu" @@ -148,14 +153,18 @@ function MainMenu:checkForUpdates() end function MainMenu:updateSelf(dt) - GAME.theme.images.bg_main:update(dt) self.menu:receiveInputs() self:checkForUpdates() end function MainMenu:drawSelf() - GAME.theme.images.bg_main:draw() + for _,d in ipairs(self.drawables) do + AnimationLoader.drawNode(d) + end + + self.uiRoot:draw() + local fontHeight = GraphicsUtil.getGlobalFont():getHeight() local infoYPosition = 705 - fontHeight / 2 @@ -199,4 +208,18 @@ function MainMenu:drawSelf() end end +function MainMenu:sceneDidDissappear() + for _,d in ipairs(self.drawables) do + AnimationLoader.stopFluxTweensOnDrawable(d) + end + self.drawables = {} +end + + +function MainMenu:refresh() + if #self.drawables == 0 then + self.drawables = AnimationLoader.loadFromFile(SCENE_PATH, SCENE_PATH .. SCENE_FILENAME) + end +end + return MainMenu diff --git a/client/src/scenes/Scene.lua b/client/src/scenes/Scene.lua index ea9f9adc..ddbff80e 100644 --- a/client/src/scenes/Scene.lua +++ b/client/src/scenes/Scene.lua @@ -59,6 +59,13 @@ function Scene:applyMusic() end end +function Scene:sceneDidDissappear() + -- Override if you need to do anything on complete +end + +-- abstract functions to be implemented per scene + +-- Ran every frame while the scene is active function Scene:update(dt) self:updateSelf(dt) self.uiRoot:update(dt) diff --git a/client/src/scenes/TitleScreen.lua b/client/src/scenes/TitleScreen.lua index 05fc9897..64c85927 100644 --- a/client/src/scenes/TitleScreen.lua +++ b/client/src/scenes/TitleScreen.lua @@ -5,24 +5,49 @@ local tableUtils = require("common.lib.tableUtils") local class = require("common.lib.class") local GraphicsUtil = require("client.src.graphics.graphics_util") local MainMenu = require("client.src.scenes.MainMenu") +local AnimationLoader = require("client.src.graphics.AnimationLoader") +local Flux = require("client.lib.flux.flux") + +local START_OPACITY = 0.5 +local SCENE_PATH = themes[config.theme].path .. "/scenes/" +local SCENE_FILENAME = "TitleScreen.json" -- The title screen scene local TitleScreen = class( function (self, sceneParams) - self.backgroundImg = themes[config.theme].images.bg_title + self.backgroundImg = themes[config.theme].images.bg_main self.music = "title_screen" + self.opacity = START_OPACITY + self.drawables = AnimationLoader.loadFromFile(SCENE_PATH, SCENE_PATH .. SCENE_FILENAME) + self.direction = 1 + self.animation = nil + self:startAnimation() end, Scene ) +function TitleScreen:startAnimation() + local target = START_OPACITY + if self.direction == 1 then + target = 1 + end + + self.animation = Flux.to(self, 0.6, { opacity = target }) + :ease("quadinout") + :oncomplete(function() + self.direction = -self.direction + self:startAnimation() + end) +end + TitleScreen.name = "TitleScreen" -local function titleDrawPressStart(percent) +function TitleScreen:titleDrawPressStart(opacity) + local opacityTarget = self.opacity local textMaxWidth = consts.CANVAS_WIDTH - 40 - local textHeight = 40 local x = (consts.CANVAS_WIDTH / 2) - (textMaxWidth / 2) local y = consts.CANVAS_HEIGHT * 0.75 - GraphicsUtil.printf(loc("continue_button"), x, y, textMaxWidth, "center", {1,1,1,percent}, nil, 16) + GraphicsUtil.printf(loc("continue_button"), x, y, textMaxWidth, "center", {1,1,1,opacityTarget}, nil, 16) end function TitleScreen:update(dt) @@ -30,13 +55,32 @@ function TitleScreen:update(dt) local keyPressed = tableUtils.trueForAny(input.allKeys.isDown, function(key) return key end) if love.mouse.isDown(1, 2, 3) or #love.touch.getTouches() > 0 or keyPressed then GAME.theme:playValidationSfx() + self.animation:stop() GAME.navigationStack:replace(MainMenu()) end end function TitleScreen:draw() self.backgroundImg:draw() - titleDrawPressStart(((math.sin(5 * love.timer.getTime()) / 2 + .5) ^ .5) / 2 + .5) + self:titleDrawPressStart(((math.sin(5 * love.timer.getTime()) / 2 + .5) ^ .5) / 2 + .5) + + for _,d in ipairs(self.drawables) do + AnimationLoader.drawNode(d) + end +end + +function TitleScreen:sceneDidDissappear() + for _,d in ipairs(self.drawables) do + AnimationLoader.stopFluxTweensOnDrawable(d) + end + self.drawables = {} +end + + +function TitleScreen:refresh() + if #self.drawables == 0 then + self.drawables = AnimationLoader.loadFromFile(SCENE_PATH, SCENE_PATH .. SCENE_FILENAME) + end end return TitleScreen \ No newline at end of file diff --git a/client/src/scores.lua b/client/src/scores.lua index 4fb9ca0b..cfca12cd 100644 --- a/client/src/scores.lua +++ b/client/src/scores.lua @@ -286,7 +286,9 @@ end function Scores.saveToFile(self) if self.version == currentVersion then - love.filesystem.write("scores.json", json.encode(self)) + local encodedScores = json.encode(self) + ---@cast encodedScores string + love.filesystem.write("scores.json", encodedScores) end end diff --git a/client/tests/AnimationLoaderTestData/anchorOverrideTest.json b/client/tests/AnimationLoaderTestData/anchorOverrideTest.json new file mode 100644 index 00000000..af6b416d --- /dev/null +++ b/client/tests/AnimationLoaderTestData/anchorOverrideTest.json @@ -0,0 +1,40 @@ +{ + "drawables": [ + { + "id": "CenterAnchorTemplate", + "templateOnly": true, + "filePath": "test_image", + "position": { + "x": 100, + "y": 100 + }, + "size": { + "width": 64, + "height": 64 + }, + "anchor": "center", + "alpha": 0.9 + }, + { + "ref": "CenterAnchorTemplate", + "overrides": { + "position": { + "x": 250 + } + } + }, + { + "ref": "CenterAnchorTemplate", + "overrides": { + "position": { + "x": 0, + "y": 216 + }, + "size": { + "width": 17, + "height": 25 + } + } + } + ] +} \ No newline at end of file diff --git a/client/tests/AnimationLoaderTestData/basic.json b/client/tests/AnimationLoaderTestData/basic.json new file mode 100644 index 00000000..b8613969 --- /dev/null +++ b/client/tests/AnimationLoaderTestData/basic.json @@ -0,0 +1,23 @@ +{ + "drawables": [ + { + "id": "BasicElement", + "filePath": "test_image", + "position": { + "x": 100, + "y": 200 + }, + "size": { + "width": 50, + "height": 75 + }, + "alpha": 0.8, + "tint": [1.0, 0.5, 0.2], + "anchor": "center", + "pivot": "center", + "rotation": 0.5, + "blendMode": "add", + "alphaMode": "premultiplied" + } + ] +} \ No newline at end of file diff --git a/client/tests/AnimationLoaderTestData/externalRef.json b/client/tests/AnimationLoaderTestData/externalRef.json new file mode 100644 index 00000000..3857d8b8 --- /dev/null +++ b/client/tests/AnimationLoaderTestData/externalRef.json @@ -0,0 +1,18 @@ +{ + "drawables": [ + { + "id": "ExternalElement", + "filePath": "test_image", + "position": { + "x": 150, + "y": 250 + }, + "size": { + "width": 48, + "height": 48 + }, + "alpha": 0.9, + "tint": [0.8, 0.9, 1.0] + } + ] +} \ No newline at end of file diff --git a/client/tests/AnimationLoaderTestData/externalRefToElement.json b/client/tests/AnimationLoaderTestData/externalRefToElement.json new file mode 100644 index 00000000..d25b93b5 --- /dev/null +++ b/client/tests/AnimationLoaderTestData/externalRefToElement.json @@ -0,0 +1,7 @@ +{ + "drawables": [ + { + "ref": "externalRef.json#ExternalElement" + } + ] +} \ No newline at end of file diff --git a/client/tests/AnimationLoaderTestData/nestedFileRef.json b/client/tests/AnimationLoaderTestData/nestedFileRef.json new file mode 100644 index 00000000..8832a570 --- /dev/null +++ b/client/tests/AnimationLoaderTestData/nestedFileRef.json @@ -0,0 +1,21 @@ +{ + "drawables": [ + { + "id": "NestedElement", + "filePath": "test_image", + "position": { + "x": 500, + "y": 600 + }, + "size": { + "width": 80, + "height": 80 + }, + "alpha": 1.0, + "tint": [0.9, 0.7, 0.8] + }, + { + "ref": "withFileRef.json" + } + ] +} \ No newline at end of file diff --git a/client/tests/AnimationLoaderTestData/referenceToReferenceTest.json b/client/tests/AnimationLoaderTestData/referenceToReferenceTest.json new file mode 100644 index 00000000..c4d95e02 --- /dev/null +++ b/client/tests/AnimationLoaderTestData/referenceToReferenceTest.json @@ -0,0 +1,29 @@ +{ + "drawables": [ + { + "id": "Element", + "filePath": "test_image", + "position": { + "x": 100, + "y": 100 + } + }, + { + "ref": "Element", + "id": "Element2", + "overrides": { + "position": { + "x": 200 + } + } + }, + { + "ref": "Element2", + "overrides": { + "position": { + "y": 200 + } + } + } + ] +} \ No newline at end of file diff --git a/client/tests/AnimationLoaderTestData/templateOnly.json b/client/tests/AnimationLoaderTestData/templateOnly.json new file mode 100644 index 00000000..9c77dacd --- /dev/null +++ b/client/tests/AnimationLoaderTestData/templateOnly.json @@ -0,0 +1,75 @@ +{ + "drawables": [ + { + "id": "Template", + "templateOnly": true, + "filePath": "test_image", + "position": { + "x": 100, + "y": 100 + }, + "size": { + "width": 32, + "height": 32 + }, + "alpha": 0.8 + }, + { + "id": "NormalElement", + "filePath": "test_image", + "position": { + "x": 200, + "y": 200 + }, + "size": { + "width": 64, + "height": 64 + }, + "children": [ + { + "id": "ChildTemplate", + "templateOnly": true, + "filePath": "test_image", + "position": { + "x": 10, + "y": 10 + }, + "size": { + "width": 16, + "height": 16 + } + }, + { + "id": "NormalChild", + "filePath": "test_image", + "position": { + "x": 20, + "y": 20 + }, + "size": { + "width": 24, + "height": 24 + } + }, + { + "ref": "ChildTemplate", + "overrides": { + "position": { + "x": 30, + "y": 30 + } + } + } + ] + }, + { + "ref": "Template", + "overrides": { + "position": { + "x": 300, + "y": 300 + } + } + } + ] +} \ No newline at end of file diff --git a/client/tests/AnimationLoaderTestData/test_image.png b/client/tests/AnimationLoaderTestData/test_image.png new file mode 100644 index 00000000..c5cd71bb Binary files /dev/null and b/client/tests/AnimationLoaderTestData/test_image.png differ diff --git a/client/tests/AnimationLoaderTestData/withFileRef.json b/client/tests/AnimationLoaderTestData/withFileRef.json new file mode 100644 index 00000000..ffda0423 --- /dev/null +++ b/client/tests/AnimationLoaderTestData/withFileRef.json @@ -0,0 +1,31 @@ +{ + "drawables": [ + { + "id": "LocalElement", + "filePath": "test_image", + "position": { + "x": 75, + "y": 125 + }, + "size": { + "width": 40, + "height": 40 + }, + "alpha": 0.7, + "tint": [1.0, 0.8, 0.6] + }, + { + "ref": "externalRef.json" + }, + { + "ref": "ExternalElement", + "overrides": { + "position": { + "x": 300, + "y": 400 + }, + "tint": [0.5, 1.0, 0.5] + } + } + ] +} \ No newline at end of file diff --git a/client/tests/AnimationLoaderTestData/withRef.json b/client/tests/AnimationLoaderTestData/withRef.json new file mode 100644 index 00000000..591bfb6a --- /dev/null +++ b/client/tests/AnimationLoaderTestData/withRef.json @@ -0,0 +1,21 @@ +{ + "drawables": [ + { + "id": "OriginalElement", + "filePath": "test_image", + "position": { + "x": 50, + "y": 100 + }, + "size": { + "width": 32, + "height": 32 + }, + "alpha": 1.0, + "tint": [1.0, 1.0, 1.0] + }, + { + "ref": "OriginalElement" + } + ] +} \ No newline at end of file diff --git a/client/tests/AnimationLoaderTestData/withRefOverrides.json b/client/tests/AnimationLoaderTestData/withRefOverrides.json new file mode 100644 index 00000000..30c6fdfb --- /dev/null +++ b/client/tests/AnimationLoaderTestData/withRefOverrides.json @@ -0,0 +1,59 @@ +{ + "drawables": [ + { + "id": "BaseElement", + "filePath": "test_image", + "position": { + "x": 10, + "y": 20 + }, + "size": { + "width": 64, + "height": 64 + }, + "alpha": 0.5, + "tint": [0.5, 0.5, 0.5], + "rotation": 0.0, + "animationTracks": [ + { + "loopTrack": true, + "steps": [ + { + "animateProperties": { + "alpha": 1.0 + }, + "durationSeconds": 1.0, + "easeType": "linear" + } + ] + } + ] + }, + { + "ref": "BaseElement", + "overrides": { + "position": { + "x": 200, + "y": 300 + }, + "tint": [1.0, 0.0, 0.0], + "rotation": 1.57, + "animationTracks": [ + { + "yoyo": true, + "steps": [ + { + "animateProperties": { + "alpha": 0.8, + "rotation": 3.14 + }, + "durationSeconds": 2.0, + "easeType": "quadinout" + } + ] + } + ] + } + } + ] +} \ No newline at end of file diff --git a/client/tests/AnimationLoaderTests.lua b/client/tests/AnimationLoaderTests.lua new file mode 100644 index 00000000..cb562256 --- /dev/null +++ b/client/tests/AnimationLoaderTests.lua @@ -0,0 +1,336 @@ +local AnimationLoader = require("client.src.graphics.AnimationLoader") + +-- Helper function to assert equality with detailed error messages +local function assertEqual(actual, expected, message) + if actual ~= expected then + error(string.format("%s: Expected %s, got %s", message or "Assertion failed", tostring(expected), tostring(actual))) + end +end + +-- Helper function to check if a value is approximately equal (for floating point comparisons) +local function assertApproxEqual(actual, expected, tolerance, message) + tolerance = tolerance or 0.001 + if math.abs(actual - expected) > tolerance then + error(string.format("%s: Expected %f (±%f), got %f", message or "Approximation failed", expected, tolerance, actual)) + end +end + +-- Test basic JSON loading +local function testBasicJSONLoading() + print("Testing basic JSON loading...") + + local testPath = "client/tests/AnimationLoaderTestData/" + local results = AnimationLoader.loadFromFile(testPath, testPath .. "basic.json") + + assertEqual(#results, 1, "Should load exactly one drawable") + + local drawable = results[1] + assertEqual(drawable.id, "BasicElement", "ID should match") + assertEqual(drawable.x, 100, "X position should match") + assertEqual(drawable.y, 200, "Y position should match") + assertApproxEqual(drawable.alpha, 0.8, nil, "Alpha should match") + assertEqual(drawable.anchor, "center", "Anchor should match") + assertEqual(drawable.pivot, "center", "Pivot should match") + assertApproxEqual(drawable.rotation, 0.5, nil, "Rotation should match") + assertApproxEqual(drawable.xScale, 0.781, nil, "X scale should match") + assertApproxEqual(drawable.yScale, 1.171875, nil, "Y scale should match") + assertEqual(drawable.blendMode, "add", "Blend mode should match") + assertEqual(drawable.alphaMode, "premultiplied", "Alpha mode should match") + + -- Check tint array + assertEqual(#drawable.tint, 3, "Tint should have 3 components") + assertApproxEqual(drawable.tint[1], 1.0, nil, "Tint red component should match") + assertApproxEqual(drawable.tint[2], 0.5, nil, "Tint green component should match") + assertApproxEqual(drawable.tint[3], 0.2, nil, "Tint blue component should match") + + print("✓ Basic JSON loading test passed") +end + +-- Test loading a reference +local function testLoadingRef() + print("Testing loading with reference...") + + local testPath = "client/tests/AnimationLoaderTestData/" + local results = AnimationLoader.loadFromFile(testPath, testPath .. "withRef.json") + + assertEqual(#results, 2, "Should load exactly two drawables") + + local original = results[1] + local referenced = results[2] + + assertEqual(original.id, "OriginalElement", "Original ID should match") + assertEqual(referenced.originalRef, "OriginalElement", "Referenced element should have ref property") + + -- Both should have the same properties (reference should be resolved) + assertEqual(original.x, referenced.x, "X positions should match") + assertEqual(original.y, referenced.y, "Y positions should match") + assertEqual(original.width, referenced.width, "Widths should match") + assertEqual(original.height, referenced.height, "Heights should match") + + print("✓ Reference loading test passed") +end + +-- Test loading a reference with overrides +local function testLoadingRefWithOverrides() + print("Testing loading reference with overrides...") + + local testPath = "client/tests/AnimationLoaderTestData/" + local results = AnimationLoader.loadFromFile(testPath, testPath .. "withRefOverrides.json") + + assertEqual(#results, 2, "Should load exactly two drawables") + + local base = results[1] + local overridden = results[2] + + assertEqual(base.id, "BaseElement", "Base ID should match") + assertEqual(overridden.originalRef, "BaseElement", "Overridden element should have ref property") + + -- Check that overrides were applied + assertEqual(overridden.x, 200, "Overridden X position should match") + assertEqual(overridden.y, 300, "Overridden Y position should match") + assertApproxEqual(overridden.rotation, 1.57, nil, "Overridden rotation should match") + + -- Check tint override + assertApproxEqual(overridden.tint[1], 1.0, nil, "Overridden tint red should match") + assertApproxEqual(overridden.tint[2], 0.0, nil, "Overridden tint green should match") + assertApproxEqual(overridden.tint[3], 0.0, nil, "Overridden tint blue should match") + + -- Check that non-overridden properties remain the same + assertEqual(overridden.width, base.width, "Non-overridden width should match base") + assertEqual(overridden.height, base.height, "Non-overridden height should match base") + + -- Check that animation tracks were overridden + assertEqual(#overridden.animationTracks, 1, "Should have one animation track") + assertEqual(overridden.animationTracks[1].yoyo, true, "Animation should be yoyo") + assertEqual(overridden.animationTracks[1].steps[1].durationSeconds, 2.0, "Animation duration should be overridden") + + print("✓ Reference with overrides test passed") +end + +-- Test loading a reference to one element in a different file +local function testLoadingRefToElementInDifferentFile() + print("Testing loading reference to element in different file...") + + local testPath = "client/tests/AnimationLoaderTestData/" + local results = AnimationLoader.loadFromFile(testPath, testPath .. "externalRefToElement.json") + + assertEqual(#results, 1) + + local localElement = results[1] + assertEqual(localElement.originalRef, "ExternalElement") + + print("✓ Reference to element in different file test passed") +end + +-- Test loading a reference that references a different file +local function testLoadingRefToDifferentFile() + print("Testing loading reference to different file...") + + local testPath = "client/tests/AnimationLoaderTestData/" + local results = AnimationLoader.loadFromFile(testPath, testPath .. "withFileRef.json") + + -- Should load local element + all elements from externalRef.json + overridden external element + assertEqual(#results, 3, "Should load three drawables") + + local localElement = results[1] + local externalFileRef = results[2] + local overriddenExternal = results[3] + + assertEqual(localElement.id, "LocalElement", "Local element ID should match") + + -- The file reference should import all drawables from the external file as children + assertEqual(#externalFileRef.children, 1, "File reference should have children from external file") + assertEqual(externalFileRef.children[1].id, "ExternalElement", "Child should be the external element") + + -- Check that the overridden external element has the correct properties + assertEqual(overriddenExternal.originalRef, "ExternalElement", "Should reference ExternalElement") + assertEqual(overriddenExternal.x, 300, "Overridden X should match") + assertEqual(overriddenExternal.y, 400, "Overridden Y should match") + assertApproxEqual(overriddenExternal.tint[1], 0.5, nil, "Overridden tint red should match") + assertApproxEqual(overriddenExternal.tint[2], 1.0, nil, "Overridden tint green should match") + assertApproxEqual(overriddenExternal.tint[3], 0.5, nil, "Overridden tint blue should match") + + print("✓ Reference to different file test passed") +end + +-- Test loading a reference to a file while loading from another file (nested file references) +local function testLoadingNestedFileReferences() + print("Testing loading nested file references...") + + local testPath = "client/tests/AnimationLoaderTestData/" + local results = AnimationLoader.loadFromFile(testPath, testPath .. "nestedFileRef.json") + + -- Should load nested element + all elements from withFileRef.json (which includes externalRef.json) + assertEqual(#results, 2, "Should load two top-level drawables") + + local nestedElement = results[1] + local fileRef = results[2] + + assertEqual(nestedElement.id, "NestedElement", "Nested element ID should match") + + -- The file reference should contain all the drawables from withFileRef.json + assertEqual(#fileRef.children, 3, "File reference should have all children from withFileRef.json") + + -- Check that nested file references are properly resolved + local localElement = fileRef.children[1] + local externalFileRef = fileRef.children[2] + local overriddenExternal = fileRef.children[3] + + assertEqual(localElement.id, "LocalElement", "Nested local element should be present") + assertEqual(#externalFileRef.children, 1, "Nested external file ref should have children") + assertEqual(externalFileRef.children[1].id, "ExternalElement", "Deeply nested element should be present") + assertEqual(overriddenExternal.originalRef, "ExternalElement", "Nested override should reference correct element") + + print("✓ Nested file references test passed") +end + +-- Test templateOnly property +local function testTemplateOnly() + print("Testing templateOnly property...") + + local testPath = "client/tests/AnimationLoaderTestData/" + local results = AnimationLoader.loadFromFile(testPath, testPath .. "templateOnly.json") + + -- Should only load 2 drawables: NormalElement and the referenced Template + -- The Template itself should not be loaded directly because templateOnly=true + assertEqual(#results, 2, "Should load exactly two drawables (normal element + referenced template)") + + local normalElement = results[1] + local referencedTemplate = results[2] + + -- First drawable should be the normal element (not template-only) + assertEqual(normalElement.id, "NormalElement", "First element should be NormalElement") + assertEqual(normalElement.x, 200, "Normal element X should match") + assertEqual(normalElement.y, 200, "Normal element Y should match") + assertEqual(normalElement.width, 64, "Normal element width should match") + assertEqual(normalElement.height, 64, "Normal element height should match") + + -- Check children of normal element - should only have 2 children (NormalChild + referenced ChildTemplate) + -- ChildTemplate itself should not be loaded because templateOnly=true + assertEqual(#normalElement.children, 2, "Normal element should have 2 children (normal child + referenced child template)") + + local normalChild = normalElement.children[1] + local referencedChildTemplate = normalElement.children[2] + + assertEqual(normalChild.id, "NormalChild", "First child should be NormalChild") + assertEqual(normalChild.x, 20, "Normal child X should match") + assertEqual(normalChild.y, 20, "Normal child Y should match") + + assertEqual(referencedChildTemplate.originalRef, "ChildTemplate", "Second child should reference ChildTemplate") + assertEqual(referencedChildTemplate.x, 30, "Referenced child template X should be overridden") + assertEqual(referencedChildTemplate.y, 30, "Referenced child template Y should be overridden") + + -- Second drawable should be the referenced template with overrides applied + assertEqual(referencedTemplate.originalRef, "Template", "Second element should reference Template") + assertEqual(referencedTemplate.x, 300, "Referenced template X should be overridden") + assertEqual(referencedTemplate.y, 300, "Referenced template Y should be overridden") + assertApproxEqual(referencedTemplate.alpha, 0.8, nil, "Referenced template alpha should match template") + + print("✓ TemplateOnly test passed") +end + +-- Test that anchor is preserved when overriding x position of a template +local function testAnchorPreservationWithXOverride() + print("Testing anchor preservation with x position override...") + + local testPath = "client/tests/AnimationLoaderTestData/" + local results = AnimationLoader.loadFromFile(testPath, testPath .. "anchorOverrideTest.json") + + -- Should load exactly two drawables (the referenced templates with overrides) + -- The template itself should not be loaded because templateOnly=true + assertEqual(#results, 2, "Should load exactly two drawables (referenced templates)") + + local firstOverride = results[1] + local secondOverride = results[2] + + -- Test first override: only x position changed + assertEqual(firstOverride.originalRef, "CenterAnchorTemplate", "First should reference CenterAnchorTemplate") + assertEqual(firstOverride.x, 250, "X position should be overridden to 250") + assertEqual(firstOverride.y, 100, "Y position should remain 100 (not overridden)") + assertEqual(firstOverride.width, 64, "Width should match template") + assertEqual(firstOverride.height, 64, "Height should match template") + assertEqual(firstOverride.anchor, "center", "Anchor should remain 'center' from template") + assertApproxEqual(firstOverride.alpha, 0.9, nil, "Alpha should match template") + + -- Verify that the anchor offset calculation works correctly with the new x position + local anchorOffsetX, anchorOffsetY = AnimationLoader.anchorOffset(firstOverride, firstOverride.anchor) + assertEqual(anchorOffsetX, 32, "Anchor offset X should be width/2 for center anchor") + assertEqual(anchorOffsetY, 32, "Anchor offset Y should be height/2 for center anchor") + + -- Verify that the object transform calculation uses the correct anchor with the new x position + local transformX, transformY = AnimationLoader.objectTransform(firstOverride) + assertEqual(transformX, 218, "Transform X should be x - anchorOffsetX (250 - 32)") + assertEqual(transformY, 68, "Transform Y should be y - anchorOffsetY (100 - 32)") + + -- Test second override: both position and size changed (like the Windows.json case) + assertEqual(secondOverride.originalRef, "CenterAnchorTemplate", "Second should reference CenterAnchorTemplate") + assertEqual(secondOverride.x, 0, "X position should be overridden to 0") + assertEqual(secondOverride.y, 216, "Y position should be overridden to 216") + assertEqual(secondOverride.anchor, "center", "Anchor should remain 'center' from template") + assertApproxEqual(secondOverride.alpha, 0.9, nil, "Alpha should match template") + + -- Verify that the anchor offset calculation works correctly with the new size + local anchorOffsetX2, anchorOffsetY2 = AnimationLoader.anchorOffset(secondOverride, secondOverride.anchor) + assertApproxEqual(anchorOffsetX2, 32, nil, "Anchor offset X should be width/2 for center anchor (64/2)") + assertApproxEqual(anchorOffsetY2, 32, nil, "Anchor offset Y should be height/2 for center anchor (64/2)") + + -- Verify that the object transform calculation uses the correct anchor with the new position and size + local transformX2, transformY2 = AnimationLoader.objectTransform(secondOverride) + assertApproxEqual(transformX2, -32, nil, "Transform X should be x - anchorOffsetX (0 - 32)") + assertApproxEqual(transformY2, 184, nil, "Transform Y should be y - anchorOffsetY (216 - 32)") + + print("✓ Anchor preservation with x position override test passed") +end + + +-- Test that anchor is preserved when overriding x position of a template +local function testLoadingReferenceToReference() + print("Testing reference to a reference...") + + local testPath = "client/tests/AnimationLoaderTestData/" + local results = AnimationLoader.loadFromFile(testPath, testPath .. "referenceToReferenceTest.json") + + -- Should load exactly two drawables (the referenced templates with overrides) + -- The template itself should not be loaded because templateOnly=true + assertEqual(#results, 3, "Should load exactly 3 drawables") + + local drawable = results[1] + local firstOverride = results[2] + local secondOverride = results[3] + + assertEqual(drawable.id, "Element") + assertEqual(drawable.originalRef, nil) + assertEqual(drawable.x, 100) + assertEqual(drawable.y, 100) + + assertEqual(firstOverride.id, "Element2") + assertEqual(firstOverride.originalRef, "Element") + assertEqual(firstOverride.x, 200) + assertEqual(firstOverride.y, 100) + + assertEqual(secondOverride.id, nil) + assertEqual(secondOverride.originalRef, "Element2") + assertEqual(secondOverride.x, 200) + assertEqual(secondOverride.y, 200) + + print("✓ Testing reference to a reference") +end + +-- Run all tests +local function runAllTests() + print("Running AnimationLoader tests...") + + testBasicJSONLoading() + testLoadingRef() + testLoadingRefWithOverrides() + testLoadingRefToElementInDifferentFile() + testLoadingRefToDifferentFile() + testLoadingNestedFileReferences() + testTemplateOnly() + testAnchorPreservationWithXOverride() + testLoadingReferenceToReference() + + print("✓ All AnimationLoader tests passed!") +end + +runAllTests() diff --git a/client/tests/PuzzleSetTests.lua b/client/tests/PuzzleSetTests.lua index 0ecd35e6..7e90eba8 100644 --- a/client/tests/PuzzleSetTests.lua +++ b/client/tests/PuzzleSetTests.lua @@ -99,6 +99,7 @@ function PuzzleSetTests.testJSONValidityRequirement1() local data = puzzleSet:generateSaveData() local baseEncoded = json.encode(data, {indent = true, pretty = true, keyorder = PuzzleSet.keyOrder}) + ---@cast baseEncoded string local encoded = FileUtils.prettifyJson(baseEncoded) -- Test that the JSON is valid by attempting to decode it @@ -126,6 +127,7 @@ function PuzzleSetTests.testUnchangedPuzzlePreservationRequirement3() -- Generate the original save data local originalData = originalPuzzleSet:generateSaveData() local originalEncoded = json.encode(originalData, {indent = true, pretty = true, keyorder = PuzzleSet.keyOrder}) + ---@cast originalEncoded string -- Get the original puzzle data for comparison local originalPuzzle1Data = originalData["Puzzle Sets"][1]["Puzzles"][1] @@ -139,6 +141,7 @@ function PuzzleSetTests.testUnchangedPuzzlePreservationRequirement3() -- Generate the new save data local newData = originalPuzzleSet:generateSaveData() local newEncoded = json.encode(newData, {indent = true, pretty = true, keyorder = PuzzleSet.keyOrder}) + ---@cast newEncoded string -- Get the new puzzle data for comparison local newPuzzle1Data = newData["Puzzle Sets"][1]["Puzzles"][1] @@ -173,6 +176,7 @@ function PuzzleSetTests.testExactJSONFormatting() local data = puzzleSet:generateSaveData() local encoded = json.encode(data, {indent = true, pretty = true, keyorder = PuzzleSet.keyOrder}) + ---@cast encoded string local prettified = FileUtils.prettifyJson(encoded) -- Define the exact expected format diff --git a/common/tests/engine/GarbageQueueTestingUtils.lua b/common/tests/engine/GarbageQueueTestingUtils.lua index c0bca785..afbab95f 100644 --- a/common/tests/engine/GarbageQueueTestingUtils.lua +++ b/common/tests/engine/GarbageQueueTestingUtils.lua @@ -34,7 +34,9 @@ function GarbageQueueTestingUtils.createMatch(stackHealth, attackFile) stack1:receiveConfirmedInput(string.rep("A", 10000)) if attackFile then - local stack2 = match:createSimulatedStackWithSettings(save.readAttackFile(attackFile)) + local attackData = save.readAttackFile(attackFile) + assert(type(attackData) == "table", "Expected attack data table from save.readAttackFile") + local stack2 = match:createSimulatedStackWithSettings(attackData) stack2:setMaxRunsPerFrame(1) match:addTarget(stack2, stack1) else diff --git a/common/tests/lib/JsonEncodingTests.lua b/common/tests/lib/JsonEncodingTests.lua index 507cba82..fdf7d619 100644 --- a/common/tests/lib/JsonEncodingTests.lua +++ b/common/tests/lib/JsonEncodingTests.lua @@ -2,7 +2,10 @@ local json = require("common.lib.dkjson") local function testEncodingDecoding(data) local encoded = json.encode(data) + ---@cast encoded string local reformedData = json.decode(encoded) + assert(reformedData ~= nil, "Failed to decode JSON") + assert(type(reformedData) == "table", "Decoded data is not a table") for key, value in pairs(reformedData) do assert(data[key] == value) end diff --git a/docs/animations.md b/docs/animations.md new file mode 100644 index 00000000..1873cdfa --- /dev/null +++ b/docs/animations.md @@ -0,0 +1,122 @@ +# Animation Loader JSON Documentation + +## Root Level Properties + +### `drawables` +An array containing drawable objects that will be rendered. This is the main container for all visual elements in the scene. + +## Drawable Properties + +### Basic Properties + +#### `id` +A unique identifier for the drawable. Used for referencing this drawable from other drawables. + +#### `filePath` +Path to an image file relative to the root path. When specified, loads a texture for this drawable that will be drawn. + +#### `ref` +Reference to another drawable by ID or to another JSON file. To reference an ID in this file, just use the ID. To reference an ID in another file use "file.json#id". If you want to import a whole file, you can just use "file.json". This allows reusing existing drawables or importing from external files. File references are resolved relative to the current file's directory + +#### `overrides` +Table of property overrides to apply when using a `ref`. Allows customizing referenced drawables without modifying the original. This will directly apply the properties recursively, so you can apply deep overrides if you use the same structure. Note you cannot apply overrides to a whole file import. + +### Position and Size + +#### `position` +Object containing `x` and `y` coordinates for the drawable's position. +- `x`: Horizontal position (default: 0) +- `y`: Vertical position (default: 0) + +#### `size` +Object containing dimensions for the drawable. Use this to automaticaly change the xScale or yScale to be appropriate to match the given width or height +- `width`: Width the drawable should be drawn +- `height`: Height the drawable should be drawn + +### Transform Properties + +#### `rotation` +Rotation angle in radians (default: 0). + +#### `scale` +- `xScale`: how much to scale the image and children in x direction, you can't specify this and width +- `yScale`: how much to scale the image and children in y direction, you can't specify this and height + +#### `anchor` +Determines which point of the drawable is positioned at the x,y coordinates. Options: +- `"topLeft"` (default) +- `"topCenter"` +- `"topRight"` +- `"centerLeft"` +- `"center"` +- `"centerRight"` +- `"bottomLeft"` +- `"bottomCenter"` +- `"bottomRight"` + +Drawables with non-`"topLeft"` anchor must have width and height greater than 0 set or a texture + +#### `pivot` +Determines the point around which rotation and scaling occur. Same options as `anchor` (default: `"center"`). + +Drawables with non-`"topLeft"` pivot must have width and height greater than 0 set or a texture + +### Visual Properties + +#### `alpha` +Transparency level from 0 (fully transparent) to 1 (fully opaque) (default: 1). + +#### `tint` +RGB color tint applied to the drawable as an array of three values [r, g, b] where each component ranges from 0 to 1 (default: [1, 1, 1] for white/no tint). + +#### `blendMode` +How this drawable blends with what's behind it (default: `"alpha"`). Uses Love2D blend modes. + +#### `alphaMode` +How alpha blending is calculated (default: `"alphamultiply"`). Uses Love2D alpha modes. + +#### `stencil` +Boolean indicating whether this drawable should only draw in the spots all its previous siblings draw. I.E. the siblings before this drawable will mask it. (default: false). + +### Texture Properties + +#### `wrap` +When set to `"repeat"`, enables texture wrapping and adds scrolling capabilities. Sets up the texture to repeat and enables `scrollX`/`scrollY` properties. + +#### `scrollX` +Horizontal scroll offset for repeating textures (only available when `wrap` is `"repeat"`). + +#### `scrollY` +Vertical scroll offset for repeating textures (only available when `wrap` is `"repeat"`). + +### Animation Properties + +#### `animationTracks` +Array of animation track objects that define how the drawable's properties change over time. Each track contains: + +##### Track Properties: +- `steps`: Array of animation steps +- `loopTrack`: Boolean indicating whether the animation should loop +- `yoyo`: Boolean indicating whether the animation should reverse direction when reaching the end and repeat. Note that you only can do yoyo or loop, not both. + +##### Step Properties (within each step in `steps`): +- `durationSeconds`: How long this step takes to complete +- `delaySeconds`: Delay before starting this step (optional) +- `easeType`: Easing function to use for the animation + - `linear` – Constant speed. + - `quadin` / `quadout` / `quadinout` – Ease using t² (slow > fast, fast > slow, or both). + - `cubicin` / `cubicout` / `cubicinout` – Steeper cubic curve (t³). + - `quartin` / `quartout` / `quartinout` – Even steeper quartic curve (t⁴). + - `quintin` / `quintout` / `quintinout` – Sharpest power curve (t⁵). + - `sinein` / `sineout` / `sineinout` – Smooth wave-like motion. + - `expoin` / `expoout` / `expoinout` – Exponential acceleration/deceleration. + - `circin` / `circout` / `circinout` – Circular motion effect. + - `backin` / `backout` / `backinout` – Overshoots slightly before settling. + - `bouncein` / `bounceout` / `bounceinout` – Bounce effect on entry/exit. + - `elasticin` / `elasticout` / `elasticinout` – Springy, elastic motion. +- `animateProperties`: Object containing the target values for properties to animate to + +### Hierarchy + +#### `children` +Array of child drawables that will be rendered relative to this drawable's transform. Child drawables inherit the parent's transformations. diff --git a/main.lua b/main.lua index 3bfb5928..f43240f6 100644 --- a/main.lua +++ b/main.lua @@ -8,6 +8,7 @@ local inputFieldManager = require("client.src.ui.inputFieldManager") local RunTimeGraph = require("client.src.RunTimeGraph") local CustomRun = require("client.src.CustomRun") local GraphicsUtil = require("client.src.graphics.graphics_util") +local Flux = require("client.lib.flux.flux") local prof = require("common.lib.zoneProfiler") local ReplayV3 = require("common.data.ReplayV3") require("common.lib.util") @@ -86,6 +87,7 @@ function love.update(dt) CustomRun.runTimeGraph = nil end + Flux.update(dt) inputManager:update(dt) inputFieldManager.update() touchHandler:update(dt) diff --git a/server/FileIO.lua b/server/FileIO.lua index e8ee438c..3d28c216 100644 --- a/server/FileIO.lua +++ b/server/FileIO.lua @@ -83,6 +83,7 @@ end function FileIO.write_error_report(error_report_json) local json_string = json.encode(error_report_json) + ---@cast json_string string if json_string:len() >= 5000 --[[5kB]] then return false end diff --git a/testLauncher.lua b/testLauncher.lua index 119c52ef..480b82cb 100644 --- a/testLauncher.lua +++ b/testLauncher.lua @@ -56,6 +56,7 @@ end local allTests = { "common.tests.lib.JsonPrecisionTests", + "client.tests.AnimationLoaderTests", "common.tests.engine.PanelGenTests", "common.tests.engine.HealthTests", "common.tests.engine.RollbackBufferTests",